diff --git a/.agents/skills/frontend/nodejs-setup/SKILL.md b/.agents/skills/frontend/nodejs-setup/SKILL.md index 8c757377..f5eb1dd7 100644 --- a/.agents/skills/frontend/nodejs-setup/SKILL.md +++ b/.agents/skills/frontend/nodejs-setup/SKILL.md @@ -64,8 +64,8 @@ pnpm add -w -D # root tooling only - `@visionset/annotator` keeps `react` as an **optional peer dependency** — never promote it to a hard dependency; the core must stay usable without React. - `@visionset/ui-core` UI primitives are **Radix behaviour + shadcn Nova styling, iconed with - Tabler**. Do not add another component library, and do not add a second icon set: - `@tabler/icons-react` is the only one the frontend declares, and a gate refuses another. + lucide**. Do not add another component library, and do not add a second icon set: + `lucide-react` is the only one the frontend declares, and a gate refuses another. - `@visionset/app` is `private: true` — never published to npm; its bundle ships inside the Python wheel. diff --git a/AGENTS.md b/AGENTS.md index 1e391beb..19da2040 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,7 +64,7 @@ client of the same SDK, and the release artifact is a plain `pip` package. | Component | Location | Stack | | --- | --- | --- | | Python distribution | `src/visionset/` | Python 3.12+, pydantic v2, FastAPI, Typer, MCP, SQLAlchemy, uv | -| Frontend workspace | `frontend/` | Node 24, pnpm, TypeScript, React 19, Vite, vitest, Radix + Tabler | +| Frontend workspace | `frontend/` | Node 24, pnpm, TypeScript, React 19, Vite, vitest, Radix + lucide | | Dev infra | `docker/` | Docker Compose (dev only) | See `README.md` for the monorepo map and `CONTRIBUTING.md` for the full check list. diff --git a/DESIGN.md b/DESIGN.md index 5b7f8e32..b646a3f6 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -27,6 +27,13 @@ that CLI version is the reference for every value in this document. Nothing here invented — every token, every derived radius, every chart colour traces back to the CLI's own output. +**One property deliberately diverges: the icon set.** The preset decodes +`icons: tabler`, and that line above says so because it is what the code decodes to; +the product draws `lucide-react`, and `components.json` says `iconLibrary: lucide`. +The schema accepts the value, so this is a decision rather than drift — but it is the +one place where reading the preset and reading the configuration give different +answers, and it is written here so nobody has to discover that by diffing them. + `@visionset/ui-core` owns the implementation, in exactly three files: | File | Role | @@ -409,11 +416,13 @@ First-class, and part of every rule above rather than a section to satisfy after - **Ad-hoc geometry that fights Nova.** A control's height, padding, or radius is not a per-screen decision; reaching past the geometry table above for a bespoke size is a design decision to make in this document, not in a component diff. -- **Mixing icon sets in new code.** Tabler is the set, and now the only one: every icon - the primitives, the screens and the annotation workspace draw is - `@tabler/icons-react`, no package declares a second icon library, and - `tests/scripts/design_tokens.test.mjs` refuses one that reappears in a manifest or an - import. A second set is a decision to make in this document, not a dependency to add. +- **Mixing icon sets in new code.** `lucide-react` is the set, and the only one: every + icon the primitives, the screens and the annotation workspace draw comes from it, no + package declares a second icon library, and `tests/scripts/design_tokens.test.mjs` + refuses one that reappears in a manifest or an import. The rule is *one* set rather + than one particular set — what costs a reader is two of them on a screen, where the + same idea arrives at two weights and two grids. Changing which one is a decision to + make in this document, not a dependency to add. - **Brand in a functional control.** Robomous coral is identity — the wordmark and its styleguide swatch, nothing else. A functional control reaching for `brand` is a semantic-colour violation regardless of how many other sites already use it correctly. diff --git a/docs/content/architecture/frontend/ui-core.md b/docs/content/architecture/frontend/ui-core.md index f74fc618..2f2019d7 100644 --- a/docs/content/architecture/frontend/ui-core.md +++ b/docs/content/architecture/frontend/ui-core.md @@ -79,12 +79,15 @@ Geist heading fonts, radius `medium`, menu `inverted`/`subtle`) - the CLI's own generated output, transcribed verbatim, plus five VisionSet extension roles (`stage`, `brand`, `success`, `warning`, `origin-*`) added through shadcn's own extension convention. `components.json` (`style: "radix-nova"`, -`iconLibrary: "tabler"`, `menuColor: "inverted"`, `menuAccent: "subtle"`) holds +`iconLibrary: "lucide"`, `menuColor: "inverted"`, `menuAccent: "subtle"`) holds the preset properties shadcn's own tools read - the fields its config schema defines, and no others. The schema is strict, so the properties it has no field for - the radius, the fonts, the chart palette, every colour - are values carried by `styles.css` instead; see [`DESIGN.md`](../../../../DESIGN.md)'s -Source of Truth for the three layers. `tokens.ts` is the TypeScript mirror for a caller that +Source of Truth for the three layers. The icon set is the one place the +configuration and the preset disagree on purpose: the preset decodes `tabler`, the +product draws `lucide-react`, and `iconLibrary` records the product rather than the +preset. `tokens.ts` is the TypeScript mirror for a caller that cannot read CSS. Both themes - light and dark - are declared in full from the preset, so `bg-primary` in a component here and `bg-primary` in a screen mean the same colour by construction. There is no `tailwind.config.js` in this @@ -106,7 +109,7 @@ visual-design rule. The current choices: | Concern | Choice | | --- | --- | | UI primitives | Radix (+ shadcn-style composition with `cva` and `cn`) - the open-code shadcn maintenance model is the direction: a primitive is VisionSet-owned source in `frontend/ui-core/src/primitives/`, edited directly, not a package dependency upgraded blindly | -| Icons | `@tabler/icons-react`, and nothing else: the primitives, the screens and the annotation workspace all draw from it, and no package declares a second icon library | +| Icons | `lucide-react`, and nothing else: the primitives, the screens and the annotation workspace all draw from it, and no package declares a second icon library | | Styling | Tailwind v4, CSS-first `@theme`, on the shadcn preset `b3bXyyPdWj` - no `tailwind.config.js`, ever | | Toasts | sonner | | Component tests | vitest + jsdom + @testing-library/react | diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-dark-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-dark-visual-linux.png index bf1993e4..6ae82a8b 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-dark-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-dark-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-light-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-light-visual-linux.png index a3fc21ff..333daeec 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-light-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/annotator-desktop-light-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-dark-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-dark-visual-linux.png index 93eeed36..be5eac50 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-dark-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-dark-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-light-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-light-visual-linux.png index 5720bfec..7da1e4d7 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-light-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/menu-open-light-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-dark-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-dark-visual-linux.png index 96fc29e2..e80b8e32 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-dark-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-dark-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-light-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-light-visual-linux.png index 780b8fa3..10683c6f 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-light-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/models-desktop-light-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-dark-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-dark-visual-linux.png index c58e8660..aa9b1292 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-dark-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-dark-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-light-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-light-visual-linux.png index 4e610f2d..f3ccd00c 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-light-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/projects-desktop-light-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/schema-desktop-dark-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/schema-desktop-dark-visual-linux.png index 8b00f4d0..d7d4ca99 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/schema-desktop-dark-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/schema-desktop-dark-visual-linux.png differ diff --git a/frontend/app/e2e/visual.spec.ts-snapshots/schema-narrow-light-visual-linux.png b/frontend/app/e2e/visual.spec.ts-snapshots/schema-narrow-light-visual-linux.png index a442b967..18e8ff0f 100644 Binary files a/frontend/app/e2e/visual.spec.ts-snapshots/schema-narrow-light-visual-linux.png and b/frontend/app/e2e/visual.spec.ts-snapshots/schema-narrow-light-visual-linux.png differ diff --git a/frontend/app/package.json b/frontend/app/package.json index cdfa9a2b..273dd79f 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -17,9 +17,9 @@ "cycle": "playwright test -c playwright.cycle.config.ts" }, "dependencies": { - "@tabler/icons-react": "^3.46.0", "@visionset/annotator": "workspace:*", "@visionset/ui-core": "workspace:*", + "lucide-react": "^1.32.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router": "^8.3.0" diff --git a/frontend/app/src/shell/AppShell.tsx b/frontend/app/src/shell/AppShell.tsx index e0734198..17712eb5 100644 --- a/frontend/app/src/shell/AppShell.tsx +++ b/frontend/app/src/shell/AppShell.tsx @@ -82,14 +82,7 @@ * right level for it. */ -import { - IconCpu, - IconFolders, - IconHome, - IconLayoutSidebarLeftCollapse, - IconLayoutSidebarLeftExpand, - IconLogout, -} from "@tabler/icons-react"; +import { Cpu, Folders, House, LogOut, PanelLeftClose, PanelLeftOpen } from "lucide-react"; import { Button, buttonVariants, @@ -145,21 +138,21 @@ export function AppShell(): JSX.Element { onClick={toggle} > {collapsed ? ( -