Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1288,6 +1288,38 @@ are recorded in the audit report but deferred to keep this change tight.)
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 `<Link>` (an `<a>`, 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.
### Board columns scroll-snap to a "home" on mobile

On a phone the board columns hit their `min-w-72` floor and the row scrolls
Expand Down
4 changes: 3 additions & 1 deletion docs/REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 30 additions & 10 deletions src/client/pages/Outline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -174,11 +175,30 @@ function IssueRow({
else if (!next) setDraft(issue.title); // never blank a saved issue
};

const issueKey = issueKeyOf(ws, issue);

return (
<div
className="group flex items-center gap-1.5 rounded py-0.5 hover:bg-line/30"
style={{ paddingLeft: depth * 22 }}
>
{/* 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. */}
<Link
href={`/issue/${issueKey}`}
title="Open full issue"
aria-label={`Open issue ${issueKey}`}
className="flex h-6 w-6 shrink-0 items-center justify-center rounded text-ink-faint hover:bg-line hover:text-ink-soft sm:opacity-40 sm:group-hover:opacity-100 sm:group-focus-within:opacity-100 sm:focus-visible:opacity-100"
>
<svg viewBox="0 0 16 16" className="h-4 w-4" fill="currentColor" aria-hidden>
<circle cx="3" cy="8" r="1.4" />
<circle cx="8" cy="8" r="1.4" />
<circle cx="13" cy="8" r="1.4" />
</svg>
</Link>
<LevelIcon kind={depth === 0 ? "issue" : "sub"} />
<input
value={draft}
Expand All @@ -203,16 +223,10 @@ function IssueRow({
done ? "text-ink-faint line-through" : "text-ink"
}`}
/>
{/* Affordances appear on hover/focus to keep the outline clean. */}
{/* Arc assignment stays a hover/focus affordance — desktop-only polish,
not a navigation control. */}
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 focus-within:opacity-100">
{depth === 0 && <ArcMenu issue={issue} arcs={arcs} />}
<Link
href={`/issue/${issueKeyOf(ws, issue)}`}
title="Open full issue"
className="rounded px-1 text-ink-faint hover:bg-line hover:text-ink-soft"
>
</Link>
</div>
</div>
);
Expand Down Expand Up @@ -267,6 +281,8 @@ function ProductCaptureRow({

return (
<div className="flex items-center gap-1.5 py-0.5">
{/* Match IssueRow's far-left open-link gutter so bullets align (PROG-80). */}
<span className="h-6 w-6 shrink-0" aria-hidden />
<LevelIcon kind="product" />
<input
ref={nameRef}
Expand Down Expand Up @@ -333,6 +349,9 @@ function CaptureRow({

return (
<div className="flex items-center gap-1.5 py-0.5" style={{ paddingLeft: depth * 22 }}>
{/* Empty gutter matching IssueRow's far-left open-link so the + bullet
lines up with the issue bullets above it (PROG-80). */}
<span className="h-6 w-6 shrink-0" aria-hidden />
<span className="text-ink-faint/50">+</span>
<input
ref={ref}
Expand Down Expand Up @@ -642,7 +661,8 @@ export default function Outline({ workspace }: { workspace: WorkspacePayload })
<h1 className="text-2xl font-semibold tracking-tight">Outline</h1>
<p className="mt-1 text-xs text-ink-faint">
Fast capture — type to add issues, <kbd>Enter</kbd> for the next,{" "}
<kbd>Tab</kbd>/<kbd>Shift+Tab</kbd> to nest. The <code>…</code> opens the full issue.
<kbd>Tab</kbd>/<kbd>Shift+Tab</kbd> to nest. Tap the <code>⋯</code> at the
start of a row to open the full issue.
</p>
</div>
<div className="flex items-center gap-4">
Expand Down