9e6756f678
Backfills transcripts that existed only locally in ohm-infra: - 0019.1/.2/.3 - UX-polish wave subagent transcripts (driver 0019.0 never finalized) - 0026.1-.9 - security-audit-0026 subagent transcripts (driver abandoned/closed-out by 0068; audit drove published 0030 remediation) - 0073.0 - PPE/progressive-delivery + engineering-handbook session, originally drafted as a duplicate 0046.0; reassigned next free number (0072 taken by a concurrent session) sessions.json: add 0019/0073 titles, update 0026 title. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
97 lines
4.9 KiB
Markdown
97 lines
4.9 KiB
Markdown
# SESSION-0019.2 — header rename + inbox icon + light inbox UX
|
||
|
||
Parent: SESSION-0019.0-TRANSCRIPT-… (UX-polish wave, rfc-app v0.21.0)
|
||
Subsession: 0019.2
|
||
Window: 2026-05-28T11-27 → 2026-05-28T11-31 (PST)
|
||
|
||
## Goal
|
||
|
||
Roadmap #24 (rename), #25 (inbox icon + light inbox UX), and the
|
||
header's share of #31 polish — confined to the App.jsx header region,
|
||
Inbox.jsx, and a new Inbox.css. No version/changelog/tag/merge/push.
|
||
|
||
## Pre-state
|
||
|
||
- Branch `feature/v0.21.0-header-inbox` at the foundation commit (token
|
||
module `frontend/src/styles/tokens.css` present).
|
||
- Header link text was "About" (already routing to /philosophy).
|
||
- Inbox trigger used a 📮 emoji with title "Notifications inbox (§15.2)".
|
||
- Inbox.jsx: working panel; row click marks-read + navigates; one
|
||
"Mark all read (under filter)" button; terse empty copy; no per-row
|
||
mark-read; unread indicated only by `.inbox-row.unread` bg tint.
|
||
- No Amplitude `track()` calls inside Inbox.jsx (only api.js calls:
|
||
listNotifications, markNotificationRead, markNotificationsReadByFilter).
|
||
So "preserve §15 tracking" = don't break the data path; nothing to add.
|
||
|
||
## Turn-by-turn arc (honest)
|
||
|
||
1. Read tokens.css, the App.jsx header block (~150–238), Inbox.jsx full,
|
||
and the §15 inbox block of App.css (1573–1641) to learn existing class
|
||
names + property values before touching anything.
|
||
2. #24: one-word change "About" → "Philosophy" (kept the title attr).
|
||
3. #25 icon: replaced `<span>📮</span>` with a hand-rolled inline SVG
|
||
envelope (18×18, viewBox 0 0 24 24, stroke=currentColor, ~1.75 stroke,
|
||
rounded). Set `aria-label="Inbox"`, retitled "Inbox (§15.2)". Badge
|
||
block untouched.
|
||
4. #25 UX: refactored Inbox.jsx. Extracted `markOneRead(item)` (used by
|
||
both row-click and the new per-row affordance) which also decrements
|
||
the local unreadCount. Added an `inbox-unread-dot` span + `read`/`unread`
|
||
row class, a per-row "Mark as read" button (inline check SVG) that sits
|
||
OUTSIDE the Link and `preventDefault/stopPropagation`s so it marks
|
||
without navigating, shown only for unread rows. Simplified the
|
||
mark-all label to "Mark all read" with an explanatory title. Replaced
|
||
empty copy with a "You're all caught up." state that branches on
|
||
whether filters are active.
|
||
5. Wrote Inbox.css (tokenized). Discovered via import order that Inbox.jsx
|
||
is imported at App.jsx line 6, BEFORE App.css at line 30 — so under
|
||
ESM depth-first eval Inbox.css injects FIRST and App.css wins ties.
|
||
Wrote override rules one notch more specific (`.inbox-list .inbox-row…`)
|
||
and `.inbox-empty .muted` to beat global `.muted`. New classes need no
|
||
guard. All values via var(--space/-text/-color/-radius/-motion/-ease).
|
||
6. Build: first `npm run build` failed — VITE_APP_NAME required (config
|
||
guard, unrelated to my change; no .env in worktree). Re-ran with
|
||
`VITE_APP_NAME="OHM RFC" npm run build` → green (expected ~971kB
|
||
chunk warning only).
|
||
7. Verified `git status` shows only App.jsx + Inbox.jsx modified and
|
||
Inbox.css new (node_modules symlink left untracked). Committed exactly
|
||
those three.
|
||
|
||
## Cut state
|
||
|
||
- Branch: `feature/v0.21.0-header-inbox`
|
||
- Final commit: 317738ed7998a7af5abe0b64769941cb11808ab2
|
||
- Files changed: frontend/src/App.jsx (header region only),
|
||
frontend/src/components/Inbox.jsx, frontend/src/components/Inbox.css (new)
|
||
- Build: green (chunk-size warning only).
|
||
|
||
## What the driver needs to know (inbox UX review pointers)
|
||
|
||
The inbox pass is LIGHT — same layout, panel, filters, bundle toggle,
|
||
deep-links, and API calls as before. Exactly what changed for review:
|
||
|
||
- Unread/read: unread rows now get an accent left-bar (inset box-shadow)
|
||
+ warm tint + a filled accent dot + medium-weight summary; read rows
|
||
drop the dot (space reserved, stays aligned) and mute the summary.
|
||
- Per-row affordance: a check-icon button appears on row hover/focus
|
||
(always visible on touch via `@media (hover:none)`) that marks the row
|
||
read without navigating. Uses the same markOneRead path.
|
||
- Mark-all: relabeled "Mark all read" (+ title clarifying it's
|
||
filter-scoped). Same `markNotificationsReadByFilter` call, same
|
||
disabled-when-all-read guard.
|
||
- Empty state: "You're all caught up." with filter-aware sub-copy.
|
||
- Behavior preserved: row-click still marks-read then navigates +
|
||
closes; unreadCount now also decremented locally on single marks (was
|
||
only reset to 0 on mark-all before — minor, improves badge accuracy,
|
||
SSE still authoritative per §15.3).
|
||
|
||
## §19.2 candidates
|
||
|
||
- Full inbox redesign deferred to a #25-followup: the operator's
|
||
reference screenshot did not transmit, so this stayed a polish pass
|
||
within the existing structure rather than a re-layout. Revisit with
|
||
the screenshot.
|
||
- The inbox-trigger / inbox-* base rules still live in App.css with
|
||
hardcoded hex (a sibling is tokenizing App.css this wave). If that
|
||
sweep lands, the few overrides in Inbox.css could shed their
|
||
extra-specificity guards once import order is normalized.
|