Add milestone-open/milestone-done icons to the #465 icons mechanism; apply state-mapped on milestones page, issues list, and the repo tab strip #484

Closed
opened 2026-09-13 19:26:17 +00:00 by crueber · 6 comments
Owner

Add milestone-open/milestone-done icons to the #465 icon mechanism and apply them state-mapped on milestones surfaces

Sequencing

Builds directly on the landed #465 shared icon mechanism (web/src/lib/icons.jsx, in tree as of 2b42843). #481 already extends the same mechanism with the provided comment-bubble icon on the issues list — this ticket adds the two milestone icons and their three call sites; coordinate so the icons.jsx edits do not collide (sequential is fine, the map is append-only).

What's requested

Add the two user-provided milestone SVGs — milestone-open.svg and milestone-done.svg (delivered at /tmp/svg-icons/, 1em × 1em, fill="currentColor", viewBox 0 0 24 24 and 0 0 16 16 respectively) — to the embedded ICONS map in web/src/lib/icons.jsx as "milestone-open" and "milestone-done", embedded verbatim (paths/viewBoxes unmodified, currentColor paint preserved, no runtime fetches — same rules as the existing 11 entries).

Then apply them state-mapped in three places:

Surface File Current rendering Change
Milestones page — open-card state chip web/src/pages/Milestones.jsx (~line 129, <span class="chip">{m.state}</span> next to the <h3> title) bare text chip reading open milestone-open icon inside the chip, left of the state text
Milestones page — closed rows web/src/pages/Milestones.jsx (the "Closed" section, collapsed single-line cards ~line 180) closed milestones render with no state marker at all (only the emerald title color signals state) milestone-done icon left of the title in each closed row
Issues list — milestone chip web/src/pages/Issues.jsx (~line 369, <A class="chip max-w-40 truncate …">{d().text}</A>) text-only milestone chip per issue row milestone-open icon left of the milestone title inside the chip (the chip links to the milestone filter; state here is the milestone's own open state, not the issue's — see below)
Repo tab strip — Milestones tab web/src/pages/Repo.jsx TABS array (~line 162) + the <For each={TABS}> render (~line 688) text-only "Milestones" tab; note the tab strip currently has NO icons on any tab, and milestones maps to the issues tab id in lib/tabs.js (the /milestones route highlights "Issues") an icon-bearing Milestones affordance in the strip. Decision point for the implementer (pick one, note it): (a) add the icon inside the existing Issues tab when the current path is under /milestones, or (b) promote Milestones to its own tab entry with the milestone-open icon. Do not break the #319 open-count badge (tabBadge) or the #274 scroll-into-view behavior in either case.

State mapping question the implementer must settle and note: the issues-list chip and the tab strip icon can be either static (milestone-open always) or state-aware when the milestone's state is available in the payload (the milestones:{o}/{r} cached set the chip title already resolves against carries per-milestone state). Preference: state-aware where the payload already has it, static where it doesn't — but do not add new requests to get state.

Architecture notes

  • The mechanism contract is already fixed by #465: mixed viewBoxes scale through width="1em" height="1em"; .icon utility from web/src/ui.css; no color literals; size/spacing belongs to the caller's classes. The two new entries follow it exactly — the 16-unit vs 24-unit viewBox difference is expected and already handled.
  • The issues-list chip is a truncate context (max-w-40 truncate): the icon must not be clipped — flex-shrink: 0 is already on .icon; keep the title text as the truncating element.
  • The Milestones page chip {m.state} reads the raw state string; if the icon makes the redundant word noisy, keeping the word is still the default (accessibility signal beyond the icon) — the icon is decorative with aria-hidden="true".
  • No server changes, no new wire data, no ETag concerns (icons ride existing payloads; milestones:{o}/{r} is already fetched by both pages).

Acceptance criteria

  • ICONS map gains milestone-open and milestone-done, embedded verbatim from the provided files (paths + viewBoxes byte-identical in intent, currentColor preserved)
  • Open milestone cards on /milestones show the open icon in the state chip; closed rows show the done icon
  • Issues-list milestone chips show the milestone icon without breaking truncation (max-w-40 still truncates the title, icon never clipped)
  • The repo tab strip gains the milestone affordance per the chosen option (a) or (b), with tabBadge (#319) and active-tab scroll (#274) behavior intact
  • All decorative icons carry aria-hidden="true"; no accessible name changes text
  • No new network requests; no color literals in the icon layer
# Add milestone-open/milestone-done icons to the #465 icon mechanism and apply them state-mapped on milestones surfaces ## Sequencing Builds directly on the landed #465 shared icon mechanism (`web/src/lib/icons.jsx`, in tree as of 2b42843). #481 already extends the same mechanism with the provided comment-bubble icon on the issues list — this ticket adds the two milestone icons and their three call sites; coordinate so the icons.jsx edits do not collide (sequential is fine, the map is append-only). ## What's requested Add the two user-provided milestone SVGs — `milestone-open.svg` and `milestone-done.svg` (delivered at `/tmp/svg-icons/`, 1em × 1em, `fill="currentColor"`, viewBox `0 0 24 24` and `0 0 16 16` respectively) — to the embedded `ICONS` map in `web/src/lib/icons.jsx` as `"milestone-open"` and `"milestone-done"`, embedded verbatim (paths/viewBoxes unmodified, currentColor paint preserved, no runtime fetches — same rules as the existing 11 entries). Then apply them state-mapped in three places: | Surface | File | Current rendering | Change | |---|---|---|---| | Milestones page — open-card state chip | `web/src/pages/Milestones.jsx` (~line 129, `<span class="chip">{m.state}</span>` next to the `<h3>` title) | bare text chip reading `open` | `milestone-open` icon inside the chip, left of the state text | | Milestones page — closed rows | `web/src/pages/Milestones.jsx` (the "Closed" section, collapsed single-line cards ~line 180) | closed milestones render with no state marker at all (only the emerald title color signals state) | `milestone-done` icon left of the title in each closed row | | Issues list — milestone chip | `web/src/pages/Issues.jsx` (~line 369, `<A class="chip max-w-40 truncate …">{d().text}</A>`) | text-only milestone chip per issue row | `milestone-open` icon left of the milestone title inside the chip (the chip links to the milestone filter; state here is the milestone's own open state, not the issue's — see below) | | Repo tab strip — Milestones tab | `web/src/pages/Repo.jsx` `TABS` array (~line 162) + the `<For each={TABS}>` render (~line 688) | text-only "Milestones" tab; note the tab strip currently has NO icons on any tab, and `milestones` maps to the `issues` tab id in `lib/tabs.js` (the /milestones route highlights "Issues") | an icon-bearing Milestones affordance in the strip. Decision point for the implementer (pick one, note it): (a) add the icon inside the existing Issues tab when the current path is under /milestones, or (b) promote Milestones to its own tab entry with the `milestone-open` icon. Do not break the #319 open-count badge (`tabBadge`) or the #274 scroll-into-view behavior in either case. | State mapping question the implementer must settle and note: the issues-list chip and the tab strip icon can be either static (`milestone-open` always) or state-aware when the milestone's `state` is available in the payload (the `milestones:{o}/{r}` cached set the chip title already resolves against carries per-milestone state). Preference: state-aware where the payload already has it, static where it doesn't — but do not add new requests to get state. ## Architecture notes - The mechanism contract is already fixed by #465: mixed viewBoxes scale through `width="1em" height="1em"`; `.icon` utility from `web/src/ui.css`; no color literals; size/spacing belongs to the caller's classes. The two new entries follow it exactly — the 16-unit vs 24-unit viewBox difference is expected and already handled. - The issues-list chip is a truncate context (`max-w-40 truncate`): the icon must not be clipped — `flex-shrink: 0` is already on `.icon`; keep the title text as the truncating element. - The Milestones page chip `{m.state}` reads the raw state string; if the icon makes the redundant word noisy, keeping the word is still the default (accessibility signal beyond the icon) — the icon is decorative with `aria-hidden="true"`. - No server changes, no new wire data, no ETag concerns (icons ride existing payloads; `milestones:{o}/{r}` is already fetched by both pages). ## Acceptance criteria - [ ] `ICONS` map gains `milestone-open` and `milestone-done`, embedded verbatim from the provided files (paths + viewBoxes byte-identical in intent, `currentColor` preserved) - [ ] Open milestone cards on /milestones show the open icon in the state chip; closed rows show the done icon - [ ] Issues-list milestone chips show the milestone icon without breaking truncation (`max-w-40` still truncates the title, icon never clipped) - [ ] The repo tab strip gains the milestone affordance per the chosen option (a) or (b), with `tabBadge` (#319) and active-tab scroll (#274) behavior intact - [ ] All decorative icons carry `aria-hidden="true"`; no accessible name changes text - [ ] No new network requests; no color literals in the icon layer
crueber added this to the v1 milestone 2026-09-13 19:26:24 +00:00
Author
Owner

Icon file milestone-open.svg (verbatim from the user; 1em, currentColor — missing from the filed body, posted here as the source of truth):

<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16">
	<path fill="currentColor" fill-rule="evenodd" d="M8.354 2.664a.5.5 0 0 0-.708 0L2.664 7.646a.5.5 0 0 0 0 .708l4.982 4.982a.5.5 0 0 0 .708 0l4.982-4.982a.5.5 0 0 0 0-.708zm-1.768-1.06a2 2 0 0 1 2.828 0l4.982 4.982a2 2 0 0 1 0 2.828l-4.982 4.982a2 2 0 0 1-2.828 0L1.604 9.414a2 2 0 0 1 0-2.828z" clip-rule="evenodd" />
</svg>
Icon file `milestone-open.svg` (verbatim from the user; `1em`, `currentColor` — missing from the filed body, posted here as the source of truth): ```svg <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16"> <path fill="currentColor" fill-rule="evenodd" d="M8.354 2.664a.5.5 0 0 0-.708 0L2.664 7.646a.5.5 0 0 0 0 .708l4.982 4.982a.5.5 0 0 0 .708 0l4.982-4.982a.5.5 0 0 0 0-.708zm-1.768-1.06a2 2 0 0 1 2.828 0l4.982 4.982a2 2 0 0 1 0 2.828l-4.982 4.982a2 2 0 0 1-2.828 0L1.604 9.414a2 2 0 0 1 0-2.828z" clip-rule="evenodd" /> </svg> ```
Author
Owner

Icon file milestone-done.svg (verbatim from the user; 1em, currentColor — missing from the filed body, posted here as the source of truth):

<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
	<path fill="currentColor" d="m22.115 10.055l-8.17-8.17a2.76 2.76 0 0 0-3.89 0l-8.17 8.17a2.76 2.76 0 0 0 0 3.89l8.17 8.17c.535.535 1.24.805 1.945.805s1.41-.27 1.945-.805l8.17-8.17a2.76 2.76 0 0 0 0-3.89m-10.73 5.12a1.25 1.25 0 0 1-.885.365c-.32 0-.64-.12-.885-.365l-2.27-2.27l1.06-1.06L10.5 13.94l5.47-5.47l1.06 1.06z" />
</svg>
Icon file `milestone-done.svg` (verbatim from the user; `1em`, `currentColor` — missing from the filed body, posted here as the source of truth): ```svg <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"> <path fill="currentColor" d="m22.115 10.055l-8.17-8.17a2.76 2.76 0 0 0-3.89 0l-8.17 8.17a2.76 2.76 0 0 0 0 3.89l8.17 8.17c.535.535 1.24.805 1.945.805s1.41-.27 1.945-.805l8.17-8.17a2.76 2.76 0 0 0 0-3.89m-10.73 5.12a1.25 1.25 0 0 1-.885.365c-.32 0-.64-.12-.885-.365l-2.27-2.27l1.06-1.06L10.5 13.94l5.47-5.47l1.06 1.06z" /> </svg> ```
Author
Owner

Note: the icon source files (milestone-open / milestone-done) are posted as comments below in ```svg blocks — copy verbatim into web/src/lib/icons.jsx. The body does not contain the SVG markup.

Note: the icon source files (milestone-open / milestone-done) are posted as comments below in ```svg blocks — copy verbatim into web/src/lib/icons.jsx. The body does not contain the SVG markup.
Author
Owner

Fix PR: #490 (branch fix/issue-484). Notes: icons embedded AS LABELED (open=16, done=24) — the issue body prose has them swapped; repo strip takes option (a) per the rationale in Repo.jsx + docs/go/12_web_ui.md.

Fix PR: https://git.packden.us/crueber/walhub/pulls/490 (branch fix/issue-484). Notes: icons embedded AS LABELED (open=16, done=24) — the issue body prose has them swapped; repo strip takes option (a) per the rationale in Repo.jsx + docs/go/12_web_ui.md.
Author
Owner

REVIEW PR #490 (fix/issue-484) — verified in scratch worktree, all checks pass with one small fix pushed (789e331).

  1. VERBATIM ARTWORK (programmatic, python diff of d + viewBox + paint): both paths byte-identical to /tmp/svg-icons/ — open d (243 chars, fill-rule/clip-rule evenodd) == milestone-open.svg, done d (288 chars, no evenodd) == milestone-done.svg, both fill=currentColor. PASS.
  2. AS-LABELED viewBoxes: icons.jsx:137-149 open='0 0 16 16', done='0 0 24 24' — files win over the issue prose (which lists them swapped), as the header comment notes. PASS.
  3. FOUR SURFACES: Milestones.jsx:143-145 open-chip icon left of kept state word; Milestones.jsx:195 closed rows static done icon left of title link; Issues.jsx:375-386 chip derives icon() off the cached set with title in inner span.truncate; Repo.jsx:188-192 isMilestonesPath + :789-791 Show — option (a), TABS stays 7 entries, tabs.js untouched, badge/scroll lines intact. Choice (a) justified: activeTab maps milestones→issues so (b) would split highlight from nav. PASS.
  4. STATE MAPPING: payload-mapped where available (m.state on page, cached-set lookup on issues list, unknown/deleted fail visible to open), static where closed-by-construction or path-derived. Single milestones.list() call site pinned by test; no new requests. PASS.
  5. TRUNCATION: chip cap kept, title span is the truncating element, .icon is shrink-0 (ui.css:84), .chip is inline-flex — icon never clips by compiled-class reasoning. No browser drive (shared-daemon loopback guard; node tests + bundle reasoning only — noted explicitly).
  6. NO NEW DEPS (package.json untouched, test-pinned) + docs law-12 entry present. PASS.

TESTS (head-to-head, full node --test): PR branch 1064 total / 1062 pass / 2 fail vs pristine main 1055 / 1053 / 2 — delta is exactly the 9 new tests, failures are the same 2 pre-existing live-server smoke subtests. PR description's '944/929/15 (+13 data/markdown failures)' is STALE — those 13 failures do not exist on current main. FIXED in-docs by 789e331 (12_web_ui.md now reads 1064/1062/2 vs 1055/1053/2); PR description body still shows the stale numbers — cosmetic, consider editing. vite build green, both path fragments confirmed in dist bundle (index-*.js).

RECOMMENDATION: ready to merge (after optional PR-body number touch-up). No browser proof — same standing caveat as prior web PRs.

REVIEW PR #490 (fix/issue-484) — verified in scratch worktree, all checks pass with one small fix pushed (789e331). 1) VERBATIM ARTWORK (programmatic, python diff of d + viewBox + paint): both paths byte-identical to /tmp/svg-icons/ — open d (243 chars, fill-rule/clip-rule evenodd) == milestone-open.svg, done d (288 chars, no evenodd) == milestone-done.svg, both fill=currentColor. PASS. 2) AS-LABELED viewBoxes: icons.jsx:137-149 open='0 0 16 16', done='0 0 24 24' — files win over the issue prose (which lists them swapped), as the header comment notes. PASS. 3) FOUR SURFACES: Milestones.jsx:143-145 open-chip icon left of kept state word; Milestones.jsx:195 closed rows static done icon left of title link; Issues.jsx:375-386 chip derives icon() off the cached set with title in inner span.truncate; Repo.jsx:188-192 isMilestonesPath + :789-791 Show — option (a), TABS stays 7 entries, tabs.js untouched, badge/scroll lines intact. Choice (a) justified: activeTab maps milestones→issues so (b) would split highlight from nav. PASS. 4) STATE MAPPING: payload-mapped where available (m.state on page, cached-set lookup on issues list, unknown/deleted fail visible to open), static where closed-by-construction or path-derived. Single milestones.list() call site pinned by test; no new requests. PASS. 5) TRUNCATION: chip cap kept, title span is the truncating element, .icon is shrink-0 (ui.css:84), .chip is inline-flex — icon never clips by compiled-class reasoning. No browser drive (shared-daemon loopback guard; node tests + bundle reasoning only — noted explicitly). 6) NO NEW DEPS (package.json untouched, test-pinned) + docs law-12 entry present. PASS. TESTS (head-to-head, full node --test): PR branch 1064 total / 1062 pass / 2 fail vs pristine main 1055 / 1053 / 2 — delta is exactly the 9 new tests, failures are the same 2 pre-existing live-server smoke subtests. PR description's '944/929/15 (+13 data/markdown failures)' is STALE — those 13 failures do not exist on current main. FIXED in-docs by 789e331 (12_web_ui.md now reads 1064/1062/2 vs 1055/1053/2); PR description body still shows the stale numbers — cosmetic, consider editing. vite build green, both path fragments confirmed in dist bundle (index-*.js). RECOMMENDATION: ready to merge (after optional PR-body number touch-up). No browser proof — same standing caveat as prior web PRs.
Author
Owner

Fixed by PR #490 (review clean + stale test-count fix by reviewer; byte-identical transcription, as-labeled viewBoxes, all 4 surfaces verified), merged. Closing.

Fixed by PR #490 (review clean + stale test-count fix by reviewer; byte-identical transcription, as-labeled viewBoxes, all 4 surfaces verified), 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#484
No description provided.