From 2535bb6741eba51bdad4d172cff6141a7727aba9 Mon Sep 17 00:00:00 2001 From: bkennedy Date: Sat, 4 Jul 2026 15:39:39 +0000 Subject: [PATCH] feat(outline): PROG-80 move issue-jump link to a persistent far-left gutter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Outline's only door to an issue's page was a trailing `…` link hidden in an `opacity-0 group-hover:opacity-100` cluster on the right edge. Touch has no hover, so on a phone there was no way to reach an issue from the Outline; even on desktop the control sat at a right edge that shifts with the row's text. Move that jump control to a fixed gutter at the far left of every row: a per-row `` rendered as a 24x24 three-dot SVG tap target. Full opacity on mobile (always reachable, no hover), `sm:opacity-40` firming to full on hover/focus-within/focus-visible on desktop — discoverable there too, a strict improvement over the old fully-hidden state. Arc assignment stays a desktop hover affordance (navigation-only control moved). Capture rows gain a matching empty gutter so their `+` bullets stay aligned with the issue bullets. Docs: record the rationale as DECISIONS `### PROG-80` and update the Outline entry in REFERENCE. --- docs/DECISIONS.md | 33 +++++++++++++++++++++++++++++ docs/REFERENCE.md | 4 +++- src/client/pages/Outline.tsx | 40 +++++++++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 1c3f903..01974ea 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -1287,3 +1287,36 @@ are recorded in the audit report but deferred to keep this change tight.) *Rejected:* duplicating `` behind `hidden`/`md:hidden` (double mount) and flex `order` hacks (can't express the desktop "right column spans two left-stacked rows" cleanly). + +### PROG-80 — Outline: jump-to-issue moved to a persistent far-left gutter + +On a phone there was **no way to reach an issue's page from the Outline** — the +only door was the trailing `…` link, which lived in an `opacity-0 +group-hover:opacity-100` cluster on the right edge. Touch has no hover, so it +never appeared; even on desktop it sat at a right edge that shifts with the +row's text width. Fixed by relocating that same jump control to a fixed gutter +at the **far left** of every row (the issue asked for exactly this), so it's in +one predictable spot at every depth and reachable by tap. + +- **Always visible on mobile, faint-until-hover on desktop.** The control is a + per-row `` (an ``, so it drag-selects/opens-in-new-tab like any link) + rendered as a 24×24 tap target holding a three-dot glyph. Base opacity is full + (mobile is primary); desktop dials it back with `sm:opacity-40` and firms to + `opacity-100` on `sm:group-hover`/`group-focus-within`/`focus-visible`. So the + outline stays calm on a wide screen but the affordance is discoverable — a + strict improvement over the old fully-hidden state, which is the "improve + desktop too" half of the ask. +- **Three dots rendered as an SVG, not the `…` text glyph.** A centered SVG + gives a reliable square hit-area and crisp centering the text ellipsis + (baseline-aligned) couldn't; the meaning is unchanged from the control it + replaces. +- **Arc assignment stays a desktop hover affordance.** Only the *navigation* + control moved to the always-on gutter; the `ArcMenu` (depth-0 only) remains in + the right-side hover cluster. It was already hover-only and thus mobile- + unreachable before this change — no regression, and arc assignment is reachable + from the full issue page. Out of scope here. +- **Capture rows get a matching empty gutter** (`h-6 w-6` spacer) so the `+` + new-bullet and the product-capture row keep their bullets aligned with the + issue bullets now pushed right by the open-link. *Not adopted:* making the + bullet icon itself the link (Workflowy-style) — two adjacent controls to the + same page reads as redundant and risks mis-taps while editing the title. diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 5de2936..c57ec56 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -378,7 +378,9 @@ canonical key — entirely client-side from the loaded workspace (D22). it under the last sibling as a sub-issue, Shift+Tab outdents). Existing rows rename on Enter/blur and reparent in place via Tab/Shift+Tab. Arcs are reached only by the explicit per-row "→ arc" control (pick existing or create new); - the `…` opens the full issue. Nothing here deletes or archives. All writes + a per-row three-dot link in a fixed **far-left** gutter opens the full issue — + always visible on mobile (tappable, no hover needed) and faint-until-hover on + desktop (PROG-80). Nothing here deletes or archives. All writes reuse the optimistic `createIssue`/`updateIssue`/`createContainer` paths. Completed issues (done/canceled) read as finished — dimmed + struck through — and a page-level **Hide done** toggle drops them (and their subtrees) from the diff --git a/src/client/pages/Outline.tsx b/src/client/pages/Outline.tsx index fd08442..cfb72b0 100644 --- a/src/client/pages/Outline.tsx +++ b/src/client/pages/Outline.tsx @@ -10,7 +10,8 @@ // and keeps focus for the next sibling; Tab on that bullet deepens it under the // last sibling (→ sub-issue), Shift+Tab pops back up. Existing rows rename on // Enter/blur and reparent in place via Tab/Shift+Tab. Nothing here deletes or -// archives — the `…` opens the full issue page. +// archives — the far-left `⋯` (a per-row link, tappable on mobile) opens the +// full issue page. import { useEffect, useMemo, useRef, useState } from "react"; import { Link, useLocation, useSearch } from "wouter"; @@ -174,11 +175,30 @@ function IssueRow({ else if (!next) setDraft(issue.title); // never blank a saved issue }; + const issueKey = issueKeyOf(ws, issue); + return (
+ {/* Jump-to-issue affordance, pinned to the far left so it sits in a + consistent gutter and — crucially — is reachable on touch, where there + is no hover to reveal it (PROG-80). Always shown on mobile; on desktop + it rests faint and firms up on row hover/focus to keep the outline + calm. Tapping the same three dots that used to hide on the right edge. */} + + + + + + + - {/* Affordances appear on hover/focus to keep the outline clean. */} + {/* Arc assignment stays a hover/focus affordance — desktop-only polish, + not a navigation control. */}
{depth === 0 && } - - … -
); @@ -267,6 +281,8 @@ function ProductCaptureRow({ return (
+ {/* Match IssueRow's far-left open-link gutter so bullets align (PROG-80). */} + + {/* Empty gutter matching IssueRow's far-left open-link so the + bullet + lines up with the issue bullets above it (PROG-80). */} + Outline

Fast capture — type to add issues, Enter for the next,{" "} - Tab/Shift+Tab to nest. The opens the full issue. + Tab/Shift+Tab to nest. Tap the at the + start of a row to open the full issue.