Skip to content

Dashboard frontend modernization: Rspack build, real Tailwind v4, LCM TSX#50

Open
ScriptedAlchemy wants to merge 30 commits into
profile-storagefrom
dashboard/rspack-tailwind
Open

Dashboard frontend modernization: Rspack build, real Tailwind v4, LCM TSX#50
ScriptedAlchemy wants to merge 30 commits into
profile-storagefrom
dashboard/rspack-tailwind

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Modernizes the dashboard frontend build and UI onto a single Rust-based toolchain (Rspack/Rsbuild) and cleans up the inconsistencies called out in a frontend review. Prod-embed contract is unchanged: `src/dashboard/assets.rs` still `include_bytes!`/`include_str!` the `dist/` files at compile time, so the shipped binary serves the UI with no Node/Rspack dependency at launch — Rspack runs only at build time.

Build (Rspack, no esbuild in the pipeline)

  • `dashboard/build.mjs` now builds the shell + holographic/graph/savings/lcm plugins with `@rspack/core` (single-file IIFE per plugin; React externalized onto the host SDK via in-tree shims; exact `dist/` paths preserved; Hermes-wrapper concatenation preserved).
  • Holographic CSS compiled with real Tailwind v4 (`@tailwindcss/node` + `@tailwindcss/oxide`; `@layer theme`/`base` stripped so the plugin never clobbers host `:root` vars, wrapped in `@layer hermes-plugin`). Deletes the ~390-line hand-rolled utility subset (the fragile `[class*="xl:grid-cols-[minmax(0,1.25fr)_minmax(0,1fr)]"]` attribute selectors). A small CSS compactor replaces esbuild for minify (preserves `@supports color-mix`).
  • esbuild is gone from the build (the esbuild fallback builder + `build:esbuild` script are removed); esbuild remains a devDep only for the unit-test bundler helper.

Plugins

  • LCM: the 2020-line hand-written vanilla IIFE is ported to TSX (entry/App/components/markdown/helpers), built like the other plugins. Bundle 86 KB → 48 KB. All behavior + `hermes-lcm-*` classes preserved.
  • Canonical `cn` (`lib/cn.ts`): one class-name joiner for the shell + lib + holographic's self-contained copy (pinned by `test/shell-sdk.test.mjs`).
  • Shared UI primitives (`lib/primitives.tsx` + `.css`): `EmptyState`, `ErrorPanel`, `SkeletonLines`, `Stat`, `BarList` — adopted in graph as the reference pattern (delivered via a new `buildPlugin` `primitives` CSS-prepend option so the CSS rides into both standalone and Hermes).

Shell / a11y

  • Light-theme collapse: the ~120-line block of 20 per-component `[data-theme="light"]` overrides → 32 semantic tokens that flip with theme. Dark + light computed values are byte-identical. New components no longer need a manual light override.
  • a11y: `CoverageGauge` gets `role="img"` + `aria-label`; savings view-switch is a proper `role="tablist"`/`role="tab"`/`aria-selected`; `VIEW_TABS` hoisted to module scope.

Dev server

  • New `npm run dev` (`dashboard/dev/run.mjs`) — Rsbuild HMR dev server proxying `/api/*` to a running `tracedecay dashboard` (`TRACEDECAY_DEV_API` / `TRACEDECAY_DEV_PORT`). The dev entry builds the SDK on `window` then imports plugin entries directly. NOTE: in some sandboxes both Rsbuild Tailwind-v4 integrations (`@rsbuild/plugin-tailwindcss` and `@tailwindcss/postcss`) hit a native segfault in `createRsbuild()`, so the shipped dev server is `pluginReact()`-only — holographic renders unstyled in dev there; the prod build is the source of truth for its Tailwind styles. Verify `npm run dev` in your env.

Verification

  • `npm test`: 100/100 node + 12/12 vitest.
  • `npm run build`: all 14 artifacts; cargo embed + dashboard serve all routes 200 (served byte sizes match Rspack output).
  • Real-browser Playwright smoke (desktop + narrow): Holographic/Similarity/Curation/Code Graph/LCM all render incl. the new TSX LCM bundle.

Not in scope (follow-ups)

  • Adopt shared primitives in savings/lcm (holographic stays self-contained).
  • Consolidate the prod `@rspack/core` script onto idiomatic Rsbuild multi-environment builds.
  • Migrate the unit-test bundler off esbuild (would remove the last esbuild devDep at the cost of slower tests).

Branch based on `profile-storage` so the diff is UI-only. Retarget to `master` if you prefer.

ScriptedAlchemy added 6 commits June 18, 2026 23:28
Replace the esbuild + hand-rolled-utility dashboard build with the Rspack
engine (the one Rsbuild wraps) and real Tailwind v4, while preserving the
prod-embed contract exactly: dist files are still embedded at compile time
by src/dashboard/assets.rs and served with no Node/Rspack dependency at
launch. Rspack runs only at build time.

Build (dashboard/build.mjs, replaces esbuild):
- shell + holographic/graph/savings plugins built with @rspack/core; React
  externalized onto the host SDK via resolve.alias to the in-tree shims, so
  every plugin still shares the host's single React instance and runs
  unmodified in both the standalone shell and Hermes.
- single-file IIFE per plugin at the exact dist paths assets.rs expects.
- hermes-wrapper concatenation + lcm copy preserved.
- esbuild builder kept as build-esbuild.mjs (`npm run build:esbuild`) fallback.
- holographic CSS compiled with real Tailwind v4 (@tailwindcss/node +
  @tailwindcss/oxide); @layer theme/base stripped so the plugin never
  clobbers host :root vars, wrapped in @layer hermes-plugin.

Holographic styles (dashboard/holographic/src/styles.css):
- @import "tailwindcss" + @theme color tokens replace the ~390-line
  hand-rolled utility subset (the fragile [class*="xl:grid-cols-[..."]
  attribute selectors). Arbitrary values now resolve natively.
- surviving :root provides only the tokens the host doesn't
  (text-primary/secondary/tertiary, midground, shadow-*); hv-*/ts-card
  component polish kept verbatim.

cn (dashboard/lib/cn.ts): one canonical class-name joiner (flatten nested
arrays, keep non-empty strings) consumed by the shell SDK, lib/sdk.ts, and
holographic's self-contained in-tree copy. Pinned by test/shashell-sdk.test.mjs.

Verified: 100/100 node + 12/12 vitest; jsdom smokes (shell renders + exposes
SDK; all 3 plugins register); cargo embed + dashboard serve (all routes 200,
byte sizes match); real-browser Playwright smoke (desktop + narrow) exercises
Holographic/Similarity/Curation/Code Graph/LCM.
…uild dev server

LCM plugin — port the 2020-line hand-written vanilla IIFE
(lcm/src/index.js, React.createElement via h()) to modern TSX built as a
standard plugin bundle, mirroring graph/savings. Split into navigable
modules: entry.tsx (registers "hermes-lcm"), App.tsx, components.tsx,
markdown.tsx, helpers.ts. All behavior + hermes-lcm-* class names preserved
(style.css unchanged, renamed to styles.css for build uniformity). Built as a
TSX plugin (React externalized via lib/ shims) instead of copied verbatim;
bundle shrinks 86 KB → 48 KB.

Build pipeline — esbuild is gone from the build. build.mjs:
- LCM now built with buildPlugin (was copyLcm).
- Tailwind CSS minify switched from esbuild.transform to a small CSS compactor
  (preserves @supports color-mix blocks lightningcss would strip).
- esbuild fallback builder (build-esbuild.mjs) and the build:esbuild script
  removed. esbuild remains a devDependency ONLY for the unit-test bundler
  helper (test/helpers/module-loader.mjs); not in the shipped build path.

Dev server — new dashboard/dev/ Rsbuild dev server (`npm run dev`) with HMR,
proxying /api/* to a running `tracedecay dashboard` (TRACEDECAY_DEV_API,
default 127.0.0.1:7341; port TRACEDECAY_DEV_PORT, default 7342). The dev entry
builds the SDK on window before importing plugin entries, so SDK consumers
behave like prod. @rsbuild/plugin-tailwindcss compiles holographic's Tailwind
v4 in dev (closes the prior dev/prod styling divergence).

Verified: 100/100 node + 12/12 vitest; build emits all 14 artifacts; cargo
embed + dashboard serve (all routes 200); real-browser Playwright smoke
(desktop) exercises Holographic/Similarity/Curation/Code Graph/LCM incl. the
new TSX LCM bundle.
- HolographicMemoryPage: CoverageGauge now exposes role="img" + an
  aria-label ("N% HRR coverage, <status>") so the gauge is announced to
  assistive tech instead of being sight-only. VIEW_TABS hoisted to module
  scope (it was rebuilt on every render; it references only module-scope
  icons).
- SavingsExplorer: the Savings/Sessions/Models view switch is now a proper
  tablist (role="tablist" + role="tab" + aria-selected), matching the shell's
  tablist pattern for screen-reader parity.
dev server (dashboard/dev/run.mjs): both Rsbuild Tailwind-v4 integrations
(@rsbuild/plugin-tailwindcss and @tailwindcss/postcss via tools.postcss)
segfault natively in this execution environment (createRsbuild core dump;
@rspack/core itself is fine, so the Tailwind native path is the trigger).
Ship the dev server as pluginReact()-only so `npm run dev` actually works —
HMR + every non-holographic plugin styled. Holographic renders unstyled in
dev (documented divergence); the prod build remains the source of truth for
its Tailwind v4 styles. Verified: dev server starts (no segfault), serves
HTTP 200, /api proxy wired.

docs/dashboard.md: updated the build/frontend/dev sections to the new reality
— Rspack build (@rspack/core, per-plugin IIFE, React externalized via shims),
real Tailwind v4 for holographic, LCM as a TSX bundle, the `npm run dev`
Rsbuild HMR server (env vars TRACEDECAY_DEV_API / TRACEDECAY_DEV_PORT, /api
proxy), and the unchanged prod include_bytes! embed contract.
Add lib/primitives.tsx (+ lib/primitives.css) with shared EmptyState,
ErrorPanel, SkeletonLines, Stat, BarList — the small UI patterns every
plugin hand-rolled under a different class namespace. Components build on
the SDK primitives + a tdp-* namespace whose colors resolve through host
--color-* vars, so they theme correctly in both the standalone shell and
Hermes. CSS is delivered by build.mjs: a new buildPlugin `primitives`
option prepends lib/primitives.css to the consuming plugin's dist
stylesheet (rides into standalone serve + the Hermes-wrapper concat).

Adopted in graph (CodeGraphExplorer) — inline tsg-empty / tsg-error
replaced with EmptyState / ErrorPanel (visible text + behavior preserved;
graph's other tsg-* styling untouched) — as the reference pattern.
Holographic stays self-contained; savings/lcm adoption is follow-up.

Verified: 100/100 node + 12/12 vitest; build emits all artifacts; real-
browser Playwright smoke (desktop+narrow) passes with the primitives
prepended into graph's served CSS.
…ic tokens

Replace the ~120-line block of 20 per-component [data-theme="light"]
overrides with 32 semantic tokens (--ts-button-bg-2, --ts-card-bg,
--ts-input-bg, --ts-tab-active-bg, ...) defined once in :root (dark) and
flipped in :root[data-theme="light"]. Component rules now reference the
tokens and theme automatically, so new components no longer need a matching
manual light override.

Dark and light computed values are byte-identical to before (every token's
dark value = the old dark rule value; every light value = the old override
value). Three rules with no original light override were intentionally left
hardcoded (tokenizing them would have changed light-theme output). Residual
per-component [data-theme="light"] selectors: 0.

Verified: real-browser Playwright smoke (desktop+narrow) passes.
@changeset-bot

changeset-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: abd3997

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Route tracedecay info/todos reads and symbol-edit writes through ProjectPath resolution so out-of-root paths are rejected while valid files still populate touched context. Also update dashboard asset wording to describe UTF-8 JavaScript output independent of bundler.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 64a0025aa5

ℹ️ 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".

Comment thread dashboard/lcm/src/components.tsx
Comment thread dashboard/dev/main.tsx Outdated
ScriptedAlchemy added 18 commits June 19, 2026 02:15
Rsbuild is now the single build tool. build.mjs is a thin orchestrator over
build.shared.mjs, which builds the shell + every plugin via createRsbuild
(@rsbuild/core + pluginReact), emitting the same single-file IIFE per plugin
at the exact dist/ paths (splitChunks/runtimeChunk off, BannerPlugin, React
externalized via the in-tree shims). The dev server config is shared there
too (createDashboardDevConfig). Holographic Tailwind v4 still compiles via
the programmatic @tailwindcss/node + oxide path (strip @layer theme/base,
wrap @layer hermes-plugin), not the segfault-prone Rsbuild tailwind plugins.

esbuild is fully removed: the unit-test bundler (test/helpers/module-loader.mjs)
now bundles with @rspack/core to an ESM temp module (100/100 node tests still
pass, ~unchanged runtime), and esbuild is dropped from devDependencies (no
longer in node_modules at all). Redundant build-rsbuild.mjs/rsbuild.config.ts
alternatives removed.

Added dashboard/tsconfig.json + `npm run typecheck` (tsc --noEmit) with
typescript as a devDep. The typecheck is intentionally lenient (strict:false)
to surface real bugs over intentional host-SDK `any` noise — it already caught
two real undefined-ref crashes (fixed in the follow-up commit).

Verified: Rsbuild build emits all 14 artifacts; 100/100 node + 12/12 vitest;
real-browser Playwright smoke passes against the cargo-embedded Rsbuild binary.
Real bugs surfaced by `npm run typecheck` (undefined at runtime):
- lcm/src/components.tsx used ratioStr (compression ratio) without importing
  it — crash when the compression view rendered. Now imported from ./helpers.
- holographic/src/CurationPanel.tsx referenced loadStatus (Status-tab refresh)
  without destructuring it from useCurationData — dead refresh button. Now
  destructured.

Shared-primitives adoption (lib/primitives): savings (ErrorPanel, Stat) and
lcm (EmptyState, ErrorPanel) adopt the shared components; graph's OverviewPanel
adopts BarList/EmptyState (dropping its hand-rolled HBarChart). Holographic
stays self-contained. (graph/savings/lcm build with primitives:true so
lib/primitives.css is prepended to their dist stylesheet.)

a11y: holographic Stat now exposes its hint via aria-describedby + a
visually-hidden description element (native title kept for sighted users).

docs/dashboard.md: documents the shared primitives + buildPlugin primitives
opt-in, the canonical lib/cn.ts, and the dev-server Rsbuild-Tailwind segfault
limitation (dev is pluginReact()-only; prod is the source of truth for
holographic Tailwind styles).

Verified: 100/100 node + 12/12 vitest; Rsbuild build clean; real-browser
Playwright smoke passes.
Drop holographic jsx/react shims so Rsbuild typecheck covers all panels; rebuild embedded dist when dashboard sources drift and fix LCM fetchSession typing.
Type-checking is now integrated into the Rsbuild build/dev via
@rsbuild/plugin-type-check (ts-checker-rspack-plugin), so the separate
'tsc --noEmit' npm script is redundant. The build is the source of truth
for type errors.
@ScriptedAlchemy ScriptedAlchemy force-pushed the dashboard/rspack-tailwind branch 3 times, most recently from b6ce776 to 5138ec8 Compare June 19, 2026 06:07
@ScriptedAlchemy ScriptedAlchemy force-pushed the dashboard/rspack-tailwind branch from 5138ec8 to 99cea7f Compare June 19, 2026 06:29
@ScriptedAlchemy ScriptedAlchemy force-pushed the dashboard/rspack-tailwind branch from 99cea7f to 152012e Compare June 19, 2026 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant