feat(ui): direction D design and a light theme - #300
Conversation
Replace upstream's paper/ink tokens with the two sets of the selected design (docs/design/viewer-d.md): Nebula for dark and Daylight for light, under the same names, with the spec's effects, gradients, canvas and type roles, and upstream's names kept as aliases so every existing rule paints with them. Drop the global square-corner rule. The viewer follows prefers-color-scheme until the reader picks System, Dark or Light; the choice is stored under codegraph-ui.theme and applied as data-theme on the page. A contrast suite reads both sets from theme.css and fails on any declared pair under 4.5:1. Bundle Inter and JetBrains Mono instead of Archivo and IBM Plex Mono, turn ligatures off, draw Lucide icons as masks, colour kind tiles by family, and let the SVG export paint either set.
Replace the top tab bar with the design's icon nav rail, the command bar with its search palette and index status, and the trail ribbon, with the theme switch, the shortcut sheet and the viewer facts in the rail's foot. Below 1024px the rail narrows and search folds into a button; below 600px a bottom tab bar and a More sheet take the rail's place. Add the error card and the amber drift callout the views share.
Lay every view out as the design's islands: Start as a dashboard of what the index holds, Symbol with its line-anchored Calls rail, code card, caller filter and blast radius tiles, File, Flow with an Every hop table, Map with a tabbed inspector, Type hierarchy, Dead code, Entry points and saved trails, and the Screens and Steps empty states. Draw the loading, drift, error, long-name, empty-search, keyboard-focus and read-only states, the tablet drawer and the phone panes and sheet. Layout constants measured against the old fonts and geometry move with the design, each pinned by name in the suites.
Describe the shell, the breakpoints, the two themes and how one is chosen, and list every place the build differs from the boards. Record the restyle in the upstream ledger as a local layer a later ui/ sync re-applies.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af64bc75ff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| label: 'Start', | ||
| short: 'Start', | ||
| icon: 'layout-dashboard', | ||
| href: '#/', |
There was a problem hiding this comment.
Give Start a route that does not alias Screens
When the indexed graph contains any navigates edge, hasScreens becomes true and App.svelte renders ScreensView for the home route. Consequently this #/ href makes the new Start destination, brand link, and project link all reopen Screens instead of the dashboard, leaving HomeView unreachable for exactly those projects; use a distinct dashboard route or stop overloading home.
Useful? React with 👍 / 👎.
| {:else} | ||
| {@render skeleton(6)} |
There was a problem hiding this comment.
Stop treating failed dashboard requests as loading
If /api/entrypoints fails, the palette store sets entriesFailure and entriesSettled while leaving entries null, but this branch and the other entry-backed cards continue rendering skeletons indefinitely. The Entry view already distinguishes that settled failure, so the Start dashboard should surface it (and a retry) rather than presenting a completed request as perpetual loading.
Useful? React with 👍 / 👎.
| written. The icons are Lucide (ISC) geometry, copied into | ||
| `ui/src/lib/icons.ts` with its notice. | ||
|
|
||
| ### Direction D as built — 偏离(画板 vs 落地,供 owner 复核) |
There was a problem hiding this comment.
Keep the canonical heading in English
docs/ui.md is the canonical technical reference, but this heading includes Chinese text; replace it with an English-only heading so the page follows the repository's explicit canonical-documentation language rule.
AGENTS.md reference: docs/AGENTS.md:L5-L6
Useful? React with 👍 / 👎.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #300 +/- ##
=======================================
Coverage 95.05% 95.05%
=======================================
Files 197 197
Lines 108387 108387
=======================================
Hits 103023 103023
Misses 5364 5364 🚀 New features to boost your workflow:
|
The imported ui/README.md and the package comment still described upstream's fonts, rail geometry and always-light exports. Point the README at docs/ui.md and the design spec, correct those passages, and note that the end-cap width estimate was re-measured against Inter.
The spec now says the viewer is built to its numbers since #300 and points at the deviation log in docs/ui.md; §13 records that §3.6 settled the light variant and that the build turned ligatures off for the owner to review.
Summary
Phase 1, part B of the browser viewer: the upstream frontend that PR #298 serves, redrawn in the design the owner selected (
docs/design/viewer-d.md, Penpot direction D), with a dark and a light theme. The server, the wire contract and the models' behaviour are untouched. This is a frontend-only change plus the committed bundle and docs.Tokens.
ui/src/lib/theme.cssdefines:fontshorthands.Upstream's global
border-radius: 0 !importantis gone.Theme choice.
prefers-color-schemeby default. A System / Dark / Light control stores the choice inlocalStorageundercodegraph-ui.themeand applies it asdata-themeon<html>, using upstream's own mechanism. It sits in the nav-rail foot on desktop and tablet, and in the More sheet on the phone.ui/tests/theme-contrast.test.tsparses both sets fromtheme.cssand fails if any declared pair drops below 4.5:1. It also pins that the four copies of the sets agree and that the SVG export's palettes are the same colours.Type and icons. Inter and JetBrains Mono come from
@fontsource-variable(OFL), normal style only; Archivo and IBM Plex Mono are dropped and the bundle shrinks. Ligatures are off, so->reads as written. Lucide 0.544 icon geometry (ISC, notice kept) is inlined inui/src/lib/icons.tsand drawn as CSS masks, so icons add no SVG nodes.Shell (§2).
+Nfold for earlier hops and a read-only pill in place of Save;Views (§8).
/api/stats,/api/entrypointsand/api/trails.y = max(lineY − 22, previous + 52);States (§9).
Responsive (§10).
Measured constants. The Inter and JetBrains Mono advances were measured in Chrome. Results:
Layout constants that moved with the design are pinned by name in the suites: hierarchy rows and indent, outline pitch, and map node height and gaps.
Export.
export-svg.tscarries both palettes as literal hex. Copy image and Download SVG paint the theme on screen, and light remains the function default.Docs.
docs/ui.mdgains "Look and themes" and "Direction D as built", a 偏离 table of every place the build differs from the boards;ui/README.mdnow points atdocs/ui.md, and its font, geometry and export passages match the build;UPSTREAM.mdgains a dated entry, because the restyle is a local layer a later upstreamui/sync must re-apply.Tests moved, and why
ui-steps-model: a test computed the row pitch with the literal40, which was upstream's map node height. It now usesNODE_HEIGHT, so the assertion ("at most three lines") is unchanged.ui-export-svg: the font assertion now expects'JetBrains Mono'instead of'IBM Plex Mono'.ui-hierarchy-model,ui-map-modelandui-file-modelpin the moved constants by name and pass unchanged.New suites:
theme-contrast— the declared pairs on both sets, block agreement, and export palettes;ui-d-symbol-rails— the caller filter and the callee-row placement;ui-d-highlight—syn-typeandsyn-punctclassing.Verification (evidence under
/tmp/evidence-ui/)make pre-ciexited 0 on the headcfb9dd9, run by the pre-push hook, and onaf64bc7(pre-ci-b2.log):-D warningsall pass;make ui-checkpasses:npm ci, svelte-check with 0 errors, vitest 20 files / 569 tests, and a fresh build byte-identical to the committed bundle;PR-B changes no Rust source (
git diff 143752a..cfb9dd9 -- crates ':!crates/codegraph-ui/viewer'is empty), so golden output cannot move.Every commit was checked out alone in a throwaway worktree: svelte-check reports 0 errors at each, and vitest passes 560, 560, 569, 569, 569 and 569 tests.
The contrast suite fails when a pair is weakened. Daylight
fg-3set to#8a93a6failed 8 cases; the original value is restored and passes.Browser acceptance, bound to the head:
cfb9dd9(sha25627dc6775…30e3, byte-identical toaf64bc7's: the last commit is docs and comments only);git archive cfb9dd9indexed by it (690 files, 22,471 nodes), plus the NestJS routed fixture;meta.head, the tree and the binary sha256;accept-b-{repo,routed}-{dark,light}.json, screenshotsshots-b/).State captures (
states-b/, release binary at the head):--read-onlyserver, in both themes;🤖 Generated with Claude Code