feat(pwa): rebuild /account against DS primitives + tokens (#141) #167

Merged
james merged 3 commits from 141-account-ds-rebuild into main 2026-06-20 03:17:10 +00:00
Owner

Closes #141.

Summary

  • /account rebuilt against the in-tree primitives (Card, Button) and the Carol DS token aliases. No inline styles in the page; new account.module.css carries the layout, icon-tile, and section rules ported from ../carol-design/design_files/index.html .cax-*.
  • Sign-in methods render as icon-tile rows inside a single Card. Local password row uses KeyRound; every OAuth row uses ShieldCheck. Lucide v1.21 dropped its brand marks, so brand-free + sub-text identifying the provider is the trade-off; a self-hoster's OIDC instance wouldn't render with a misleading vendor mark either.
  • "Connect another" is a stack of Button variant="secondary" fullWidth with a leading shield icon and label "Connect <provider>". Hidden entirely when no providers remain to connect (previously the section header rendered with an empty body).
  • PAT entry re-skinned as the same secondary fullWidth Button linking to /account/tokens.
  • Each Button that navigates lives inside a <form method="get"> — the OAuth start route already accepts GET, intra-app nav to /account/tokens does too, and this keeps the primitive used as designed (a real <button>) without inline styles on an <a> or a polymorphic as prop on Button.

Server flows untouched: getServerSession, the OAuth start handler, and unlinkOAuthIdentityAction see the same shape — only the rendering layer changed.

Test plan

  • npm run lint / npx tsc --noEmit / npm test all clean.
  • Visual parity in both themes, linked + local-only states — see screenshots below.
  • Connect GitHub form-GET → 302 to github.com/login/oauth/authorize?...&state=...&code_challenge=... (OAuth start preserved end-to-end).
  • Manage tokens form-GET → 200 from /account/tokens (intra-app nav preserved).
  • HTML inline-style grep on the rebuilt page returns only style="color:transparent" on the sidebar <img> (Next.js Image default; not from this page).

Light, linked GitHub

![light linked](/attachments/missing — uploaded out-of-band)

Dark, linked GitHub

![dark linked](/attachments/missing — uploaded out-of-band)

Light, local-only ("Connect another" visible)

![light local-only](/attachments/missing — uploaded out-of-band)

Dark, local-only

![dark local-only](/attachments/missing — uploaded out-of-band)

🤖 Generated with Claude Code

Closes #141. ## Summary - `/account` rebuilt against the in-tree primitives (`Card`, `Button`) and the Carol DS token aliases. No inline styles in the page; new `account.module.css` carries the layout, icon-tile, and section rules ported from `../carol-design/design_files/index.html` `.cax-*`. - Sign-in methods render as icon-tile rows inside a single `Card`. Local password row uses `KeyRound`; every OAuth row uses `ShieldCheck`. Lucide v1.21 dropped its brand marks, so brand-free + sub-text identifying the provider is the trade-off; a self-hoster's OIDC instance wouldn't render with a misleading vendor mark either. - "Connect another" is a stack of `Button variant="secondary" fullWidth` with a leading shield icon and label "Connect &lt;provider&gt;". Hidden entirely when no providers remain to connect (previously the section header rendered with an empty body). - PAT entry re-skinned as the same `secondary fullWidth` Button linking to `/account/tokens`. - Each Button that navigates lives inside a `<form method="get">` — the OAuth start route already accepts GET, intra-app nav to `/account/tokens` does too, and this keeps the primitive used as designed (a real `<button>`) without inline styles on an `<a>` or a polymorphic `as` prop on Button. Server flows untouched: `getServerSession`, the OAuth start handler, and `unlinkOAuthIdentityAction` see the same shape — only the rendering layer changed. ## Test plan - [x] `npm run lint` / `npx tsc --noEmit` / `npm test` all clean. - [x] Visual parity in both themes, linked + local-only states — see screenshots below. - [x] `Connect GitHub` form-GET → 302 to `github.com/login/oauth/authorize?...&state=...&code_challenge=...` (OAuth start preserved end-to-end). - [x] `Manage tokens` form-GET → 200 from `/account/tokens` (intra-app nav preserved). - [x] HTML inline-style grep on the rebuilt page returns only `style="color:transparent"` on the sidebar `<img>` (Next.js Image default; not from this page). ### Light, linked GitHub ![light linked](/attachments/missing — uploaded out-of-band) ### Dark, linked GitHub ![dark linked](/attachments/missing — uploaded out-of-band) ### Light, local-only ("Connect another" visible) ![light local-only](/attachments/missing — uploaded out-of-band) ### Dark, local-only ![dark local-only](/attachments/missing — uploaded out-of-band) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(pwa): rebuild /account against DS primitives + tokens (#141)
Some checks failed
Commits / Conventional Commits (pull_request) Successful in 10s
PR / Static analysis (pull_request) Successful in 30s
PR / OSV-Scanner (pull_request) Successful in 3m39s
PR / npm audit (pull_request) Successful in 3m58s
PR / Package age policy (soft) (pull_request) Successful in 3m31s
PR / Typecheck (pull_request) Successful in 5m9s
PR / Trivy (image) (pull_request) Failing after 5m14s
PR / Lint (pull_request) Successful in 5m18s
PR / Build (pull_request) Successful in 5m19s
PR / Test (postgres) (pull_request) Successful in 6m0s
Secrets / gitleaks (pull_request) Successful in 2m59s
PR / Coverage (soft) (pull_request) Successful in 6m45s
PR / Test (sqlite) (pull_request) Successful in 7m50s
ae69bb07e8
Replace inline-styled /account with the in-tree primitives (Card,
Button) and the Carol DS token aliases. Sign-in methods now render
as icon-tile rows (KeyRound for the local password row, ShieldCheck
for every OAuth row) inside a single Card; the Unlink action is a
ghost Button on the row. "Connect another" is a stack of secondary
fullWidth Buttons with a leading shield icon and a "Connect
<provider>" label, hidden entirely when no providers remain to
connect. The PAT entry is re-skinned as a secondary fullWidth
Button linking to /account/tokens.

Form-GET wraps each Button to keep the primitive used as designed
(a real <button>) without inline styles on an <a>; the OAuth start
route already accepts GET, and intra-app nav to /account/tokens
piggybacks on the same shape.

Lucide v1.21 dropped its brand marks, so every provider row gets
the same ShieldCheck glyph — the row's sub-text identifies which
one, and a self-hoster's OIDC instance wouldn't render with a
misleading vendor mark anyway.

Server flows are untouched: getServerSession, the OAuth start
handler, and unlinkOAuthIdentityAction render the same shape — only
the rendering layer changed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

📊 Test coverage

Patch coverage: no testable lines changed.

Overall (app/, lib/, db/, excluding UI per ADR-0019):

Metric Value Soft target
Lines 80.8% ≥ 50%
Branches 80.8% ≥ 75%
Functions 88.5% informational

Soft thresholds per ADR-0019. Coverage is informational and does not block merge.

<!-- coverage-comment --> ## 📊 Test coverage **Patch coverage:** no testable lines changed. **Overall** (`app/`, `lib/`, `db/`, excluding UI per ADR-0019): | Metric | Value | Soft target | |---|---|---| | Lines | 80.8% ✅ | ≥ 50% | | Branches | 80.8% ✅ | ≥ 75% | | Functions | 88.5% | informational | Soft thresholds per [ADR-0019](docs/adr/0019-coverage-soft-targets.md). Coverage is informational and does not block merge.
feat(pwa): rebuild /account/tokens against DS primitives + tokens (#141)
Some checks failed
Commits / Conventional Commits (pull_request) Successful in 6s
PR / Static analysis (pull_request) Successful in 26s
PR / Typecheck (pull_request) Successful in 58s
PR / Lint (pull_request) Successful in 1m0s
PR / OSV-Scanner (pull_request) Successful in 20s
PR / Build (pull_request) Successful in 1m47s
PR / Package age policy (soft) (pull_request) Successful in 22s
Secrets / gitleaks (pull_request) Successful in 27s
PR / npm audit (pull_request) Successful in 1m46s
PR / Test (sqlite) (pull_request) Successful in 2m0s
PR / Test (postgres) (pull_request) Failing after 2m2s
PR / Trivy (image) (pull_request) Failing after 1m29s
PR / Coverage (soft) (pull_request) Successful in 1m27s
f40f12b078
Follow-up to the /account pilot: the PAT panel now uses the same in-tree
primitives (Card, Button, Field, Input) and Carol DS token aliases.
Sections are Cards (lg padding); the reveal banner is a green-tinted
Card with the plaintext in a mono code block plus Copy (secondary) and
Dismiss (ghost) Buttons. Each active-token row is a small bordered row
with name + ghost Revoke + a meta line (created / last-used / expires).

TanStack Query and TanStack Form wiring is unchanged — the create
mutation, the revoke mutation, the ["account","tokens"] cache key,
the zod onChange validator, and the reveal-once plaintext are all
identical. Only the rendering layer flipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Trivy (container image)

Threshold: high  ·  Total findings: 121  ·  At/above threshold: 1

critical high medium low
0 1 50 70
severity id package installed / range fix
high CVE-2026-12151 undici 6.25.0 6.27.0, 7.28.0, 8.5.0
<!-- scanner-comment: trivy --> ### Trivy (container image) **Threshold:** `high` &nbsp;·&nbsp; **Total findings:** 121 &nbsp;·&nbsp; **At/above threshold:** 1 | critical | high | medium | low | |---:|---:|---:|---:| | 0 | 1 | 50 | 70 | | severity | id | package | installed / range | fix | |---|---|---|---|---| | high | [CVE-2026-12151](https://avd.aquasec.com/nvd/cve-2026-12151) | undici | 6.25.0 | `6.27.0, 7.28.0, 8.5.0` |
fix(pwa): give the page lede breathing room from the h1 (#141)
Some checks failed
Commits / Conventional Commits (pull_request) Successful in 23s
PR / OSV-Scanner (pull_request) Successful in 36s
PR / Static analysis (pull_request) Successful in 50s
PR / Package age policy (soft) (pull_request) Successful in 16s
Secrets / gitleaks (pull_request) Successful in 15s
PR / Trivy (image) (pull_request) Failing after 1m19s
PR / Lint (pull_request) Successful in 2m44s
PR / Typecheck (pull_request) Successful in 2m46s
PR / npm audit (pull_request) Successful in 2m57s
PR / Test (sqlite) (pull_request) Successful in 3m13s
PR / Coverage (soft) (pull_request) Successful in 2m54s
PR / Test (postgres) (pull_request) Successful in 3m18s
PR / Build (pull_request) Successful in 3m29s
edd63a4c5c
The DS prototype's `.cax-lede { margin-top: -10px; }` hugs the h1
too tight in the actual app — the title and lede read as one block.
Swap to a positive 8px top margin on /account and /account/tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
james merged commit b7dfc87399 into main 2026-06-20 03:17:10 +00:00
james deleted branch 141-account-ds-rebuild 2026-06-20 03:17:11 +00:00
Sign in to join this conversation.
No description provided.