Split the Fork pill: count links to the fork-network page (/forks); drop the redundant forks link from the metadata line #464

Closed
opened 2026-09-13 15:03:55 +00:00 by crueber · 3 comments
Owner

What's requested

Split the repo-header Fork pill so its count is a link to the fork-network page, and drop the now-redundant forks link from the left metadata line. Today the header carries the fork count in TWO places with only one of them wired to the network page; consolidate to one, action-strip placement.

Current behavior (evidence)

web/src/pages/Repo.jsx, at the tree as of 06c9743:

  • Left metadata line (.repo-meta, ~lines 603-609): a Show when={(s().forks ?? 0) > 0} anchor — {s().forks} forks linking to /${full()}/forks (added by #424).
  • Header action strip (right cluster, ~lines 636-638): the {n} Fork pill from #447 — <A class="btn px-2 py-1 text-sm" href={/${full()}/fork}> — navigates to the fork-CREATE page (/fork), not the fork network.

So the user sees the count twice, and the prominent action-strip pill (the one that says "Fork") never reaches the network page; only the small muted metadata link does.

Requested shape

  • Fork pill becomes split-navigation: the label ("Fork") keeps its current create navigation (/${full()}/fork), while the count portion of the pill links to the fork-network page (/${full()}/forks). Implementation is the implementer's call within the header idiom from #447 (count sits left of the label, same btn px-2 py-1 text-sm metrics) — e.g. a nested anchor on the count, or count and label as sibling links styled as one pill — but the two destinations must be distinct and the visual must still read as ONE pill.
  • Remove the forks anchor from the left metadata line (repo-meta, lines 603-609). The metadata line keeps branches · tags only; the fork network is reachable from the pill count. Note the metadata anchor is currently conditional on forks > 0 — the pill already always renders (0 included, per #447), so removing the metadata link does not lose reachability even at zero forks.

Architecture notes

  • The fork-network page already exists: web/src/pages/Forks.jsx at route /:owner/:name/forks (web/src/index.jsx line 106, from #424), listing via the SDK's repoClient.forks.list. No new page, no new API surface — this is client-only wiring in Repo.jsx.
  • Counts still ride the shared summary (s().forks) — no new requests, no ETag concern (display-only).
  • #447's idiom constraints still apply: one container shape, count left of label, single aligned row, no reflow orphans at narrow widths (#274/#438 precedent).

Acceptance criteria

  • On a repo with forks, the header Fork pill shows {n} Fork where the count is a link landing on /{owner}/{name}/forks and the label lands on /{owner}/{name}/fork.
  • The left metadata line no longer renders a forks link/count (only the ref pill and branches · tags).
  • Pill still renders at 0 forks, matching Star/Watch (per #447); with no forks the count link still navigates to the (empty) forks page.
  • No new network requests; summary is still the single count source.
  • Narrow-viewport check: the split pill does not wrap or overflow the header action cluster.
## What's requested Split the repo-header Fork pill so its count is a link to the fork-network page, and drop the now-redundant forks link from the left metadata line. Today the header carries the fork count in TWO places with only one of them wired to the network page; consolidate to one, action-strip placement. ## Current behavior (evidence) `web/src/pages/Repo.jsx`, at the tree as of 06c9743: - Left metadata line (`.repo-meta`, ~lines 603-609): a `Show when={(s().forks ?? 0) > 0}` anchor — `{s().forks} forks` linking to `/${full()}/forks` (added by #424). - Header action strip (right cluster, ~lines 636-638): the `{n} Fork` pill from #447 — `<A class="btn px-2 py-1 text-sm" href={/${full()}/fork}>` — navigates to the fork-CREATE page (`/fork`), not the fork network. So the user sees the count twice, and the prominent action-strip pill (the one that says "Fork") never reaches the network page; only the small muted metadata link does. ## Requested shape - **Fork pill becomes split-navigation**: the label ("Fork") keeps its current create navigation (`/${full()}/fork`), while the count portion of the pill links to the fork-network page (`/${full()}/forks`). Implementation is the implementer's call within the header idiom from #447 (count sits left of the label, same `btn px-2 py-1 text-sm` metrics) — e.g. a nested anchor on the count, or count and label as sibling links styled as one pill — but the two destinations must be distinct and the visual must still read as ONE pill. - **Remove the forks anchor from the left metadata line** (`repo-meta`, lines 603-609). The metadata line keeps `branches · tags` only; the fork network is reachable from the pill count. Note the metadata anchor is currently conditional on `forks > 0` — the pill already always renders (0 included, per #447), so removing the metadata link does not lose reachability even at zero forks. ## Architecture notes - The fork-network page already exists: `web/src/pages/Forks.jsx` at route `/:owner/:name/forks` (`web/src/index.jsx` line 106, from #424), listing via the SDK's `repoClient.forks.list`. **No new page, no new API surface** — this is client-only wiring in `Repo.jsx`. - Counts still ride the shared summary (`s().forks`) — no new requests, no ETag concern (display-only). - #447's idiom constraints still apply: one container shape, count left of label, single aligned row, no reflow orphans at narrow widths (#274/#438 precedent). ## Acceptance criteria - [ ] On a repo with forks, the header Fork pill shows `{n} Fork` where the count is a link landing on `/{owner}/{name}/forks` and the label lands on `/{owner}/{name}/fork`. - [ ] The left metadata line no longer renders a forks link/count (only the ref pill and `branches · tags`). - [ ] Pill still renders at 0 forks, matching Star/Watch (per #447); with no forks the count link still navigates to the (empty) forks page. - [ ] No new network requests; summary is still the single count source. - [ ] Narrow-viewport check: the split pill does not wrap or overflow the header action cluster.
crueber added this to the v1 milestone 2026-09-13 15:04:16 +00:00
crueber added
issues
and removed
fork
labels 2026-09-13 15:04:48 +00:00
Author
Owner

Split-navigation implemented in PR #474 (#474): one pill shell on the #447 btn metrics with sibling links (count -> /forks, label -> /fork); repo-meta keeps branches - tags only; renders at 0; no new requests. Tests 988/986/2 (2 pre-existing live-server smoke, identical on pristine main). Ready for review — not merging per instructions.

Split-navigation implemented in PR #474 (https://git.packden.us/crueber/walhub/pulls/474): one pill shell on the #447 btn metrics with sibling links (count -> /forks, label -> /fork); repo-meta keeps branches - tags only; renders at 0; no new requests. Tests 988/986/2 (2 pre-existing live-server smoke, identical on pristine main). Ready for review — not merging per instructions.
Author
Owner

REVIEW PR #474 (fix/issue-464) — verified in scratch worktree /tmp/pr474 (removed afterward); main worktree untouched (still clean on main, pre-existing untracked .opencode/ only). No browser (per instructions: node tests + reasoning; browser proof remains open — shared-daemon loopback guard, as the PR notes).

AGENTS.md laws: L1 deps OK (no package.json/web dependency change — diff is Repo.jsx + 4 test files + 12_web_ui.md decision only); L7 N/A (display-only, no long work); L8 OK (change confined to web page layer, no core-package imports); L12 OK (decision appended to docs/go/12_web_ui.md in the same change).

Acceptance criteria (all hold):

  1. ONE pill, split destinations — PASS. web/src/pages/Repo.jsx:658-666: single shell (one container, #447 canonical metrics intact), count left of label , distinct destinations, {" "} separator, no glyph (text-only #438 pair kept).
  2. Metadata forks link gone — PASS. Repo.jsx:601-609: repo-meta keeps RefPicker + branches · tags only; the #424 Show(forks>0) rail is deleted (explanatory comment left in place).
  3. Renders at 0, unconditional — PASS. Count interpolation {s().forks ?? 0} sits INSIDE the /forks link with no >0 conditional, so zero forks still links to the (empty) network page — matches Star/Watch per #447.
  4. No new requests — PASS. Single source s().forks (summary); no forks.list, no fetch in the pill (pinned by test).
  5. Narrow viewport — PASS by reasoning (no browser per instructions): shell adds whitespace-nowrap (no mid-pill wrap); destinations add zero text so the #447 390px arithmetic (row ~366px < 390px) is unchanged; repo-header keeps flex-wrap and the cluster keeps ml-auto flex gap-2 (row-owned gaps, #463 spacing holds — shell carries no spacing utility, still one flex item).
  6. a11y — PASS. Sibling links inside a span, NOT a nested anchor (nested would be invalid HTML — correctly avoided). Two adjacent tab stops, each hover:underline with its own title tooltip; focus order = visual order (count then label). Outer span is not focusable itself, which is correct for a group shell.
  7. #447 idiom intact — PASS. Count-left-of-label, shared metrics on all four controls, zero-count rule kept (header-pills-447 pins updated, not weakened: dual-href + order asserts added).
  8. Pin updates faithful — PASS. header-pills-447.test.js, fork-page-438.test.js, header-gap-463.test.js retargeted → with #464-scoped asserts only; no unrelated pin loosened.
  9. No backend change, no new deps, docs accurate — PASS. No Go/SDK/API/config touched; 12_web_ui.md #464 entry matches the shipped shape (span shell, sibling links, whitespace-nowrap, unconditional zero-count link).

Review fix pushed to origin/fix/issue-464 (7b5697d): two stale #447 comment lines in Repo.jsx said 'Fork stays an link' / 'Fork navigation ... untouched' — now accurate ('split count/label destinations since #464'; 'Fork navigation is split per #464 below'). Comment-only; targeted pins 24/24 pass after the fix.

Tests: full node --test web/test/unit/*.test.js in scratch = 988 pass 986 / fail 2; pristine main baseline = 982 / 980 / 2 (same two smoke.test.js live-server subtests — need a live Go server, pre-existing, zero PR-caused; +6 net new tests). vite build green (2.08s; chunk-size warning is advisory/pre-existing). esbuild SDK bundle green (32.9kb).

MERGE RECOMMENDATION: ready to merge (modulo the already-noted open browser proof).

REVIEW PR #474 (fix/issue-464) — verified in scratch worktree /tmp/pr474 (removed afterward); main worktree untouched (still clean on main, pre-existing untracked .opencode/ only). No browser (per instructions: node tests + reasoning; browser proof remains open — shared-daemon loopback guard, as the PR notes). AGENTS.md laws: L1 deps OK (no package.json/web dependency change — diff is Repo.jsx + 4 test files + 12_web_ui.md decision only); L7 N/A (display-only, no long work); L8 OK (change confined to web page layer, no core-package imports); L12 OK (decision appended to docs/go/12_web_ui.md in the same change). Acceptance criteria (all hold): 1. ONE pill, split destinations — PASS. web/src/pages/Repo.jsx:658-666: single <span class="btn px-2 py-1 text-sm whitespace-nowrap"> shell (one container, #447 canonical metrics intact), count <A href={/${full()}/forks}> left of label <A href={/${full()}/fork}>, distinct destinations, {" "} separator, no glyph (text-only #438 pair kept). 2. Metadata forks link gone — PASS. Repo.jsx:601-609: repo-meta keeps RefPicker + branches · tags only; the #424 Show(forks>0) rail is deleted (explanatory comment left in place). 3. Renders at 0, unconditional — PASS. Count interpolation {s().forks ?? 0} sits INSIDE the /forks link with no >0 conditional, so zero forks still links to the (empty) network page — matches Star/Watch per #447. 4. No new requests — PASS. Single source s().forks (summary); no forks.list, no fetch in the pill (pinned by test). 5. Narrow viewport — PASS by reasoning (no browser per instructions): shell adds whitespace-nowrap (no mid-pill wrap); destinations add zero text so the #447 390px arithmetic (row ~366px < 390px) is unchanged; repo-header keeps flex-wrap and the cluster keeps ml-auto flex gap-2 (row-owned gaps, #463 spacing holds — shell carries no spacing utility, still one flex item). 6. a11y — PASS. Sibling links inside a span, NOT a nested anchor (nested <a> would be invalid HTML — correctly avoided). Two adjacent tab stops, each hover:underline with its own title tooltip; focus order = visual order (count then label). Outer span is not focusable itself, which is correct for a group shell. 7. #447 idiom intact — PASS. Count-left-of-label, shared metrics on all four controls, zero-count rule kept (header-pills-447 pins updated, not weakened: dual-href + order asserts added). 8. Pin updates faithful — PASS. header-pills-447.test.js, fork-page-438.test.js, header-gap-463.test.js retargeted </A>→</span> with #464-scoped asserts only; no unrelated pin loosened. 9. No backend change, no new deps, docs accurate — PASS. No Go/SDK/API/config touched; 12_web_ui.md #464 entry matches the shipped shape (span shell, sibling links, whitespace-nowrap, unconditional zero-count link). Review fix pushed to origin/fix/issue-464 (7b5697d): two stale #447 comment lines in Repo.jsx said 'Fork stays an <A> link' / 'Fork navigation ... untouched' — now accurate ('split count/label destinations since #464'; 'Fork navigation is split per #464 below'). Comment-only; targeted pins 24/24 pass after the fix. Tests: full node --test web/test/unit/*.test.js in scratch = 988 pass 986 / fail 2; pristine main baseline = 982 / 980 / 2 (same two smoke.test.js live-server subtests — need a live Go server, pre-existing, zero PR-caused; +6 net new tests). vite build green (2.08s; chunk-size warning is advisory/pre-existing). esbuild SDK bundle green (32.9kb). MERGE RECOMMENDATION: ready to merge (modulo the already-noted open browser proof).
Author
Owner

Fixed by PR #474 (review clean + stale comment fix by reviewer; all 5 criteria pass, sibling-link a11y, #447 intact), merged. Closing.

Fixed by PR #474 (review clean + stale comment fix by reviewer; all 5 criteria pass, sibling-link a11y, #447 intact), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#464
No description provided.