feat(ui): add core primitives and state panels - #1031
Merged
Conversation
EhabY
force-pushed
the
feat/devex-618-ui-state-panels
branch
4 times, most recently
from
July 14, 2026 10:08
e1122d6 to
839acc7
Compare
EhabY
force-pushed
the
feat/devex-624-ui-scaffold
branch
from
July 20, 2026 11:21
b9d3bea to
5a6fe06
Compare
EhabY
force-pushed
the
feat/devex-618-ui-state-panels
branch
from
July 20, 2026 11:33
da82146 to
6cd7c9c
Compare
jakehwll
approved these changes
Jul 27, 2026
…joining - Move the theme sync machinery into .storybook/themes/ next to its generated output: the extension-test suite becomes a real suite.cjs file instead of an escaped template string, and the fixture manifest is checked in instead of written to a temp dir at runtime (VS Code loads a manifest-only fixture; the empty extension.js shim is gone). - Pin the captured VS Code to 1.128.0 so dumps are reproducible; bump VSCODE_VERSION in sync.mjs to resync against a newer release. - Capture the startup theme (dark) last so every dump follows a real theme-change event; capturing it immediately at startup can read partially-loaded theme data. Failed activation rejects loudly instead of silently dumping the wrong theme. - Format the generated stylesheet from sync.mjs so a resync never leaves an unformatted diff for format:check. - Add a tiny cx() helper used by all components instead of the repeated filter/join expression, and reach shared internals (#cx, #codicons, #storybook) through package.json subpath imports instead of ../../ paths; the aliases are scoped to packages/ui and ship with it. - JSDoc for top-level declarations, readonly types on const collections, and list sync:vscode-themes in the AGENTS.md command table.
Typography, text color, and body margin now come from the dumped VS Code default stylesheet; keep only the sidebar background, viewport sizing, and the deliberate body-padding override.
EhabY
force-pushed
the
feat/devex-618-ui-state-panels
branch
from
July 27, 2026 09:22
6cd7c9c to
9b2e4ca
Compare
Apply /simplify pass across the PR: - derive Storybook themes from the generated dump and apply them as one synchronous :root stylesheet swap instead of ~1750 inline-style mutations per story navigation - share one MutationObserver across useVscodeTheme instances - extract an internal StatePanel component so EmptyState/ErrorState stop duplicating the panel markup by class-name convention - read foundations token values from a live getComputedStyle view, dropping the rAF workaround and per-render stylesheet scans - drop dead code: unused iconLabel prop, inert CSS rules that codicon.css already wins, undefined-propagating ProgressBar aliases - exercise --ui-link-foreground with a Link row in the parity story - lint *.cjs files (suite.cjs was covered by no config block) - write generated theme dumps verbatim: ignore the directory in prettier, drop the sync-time format step, and commit the verbatim regeneration - use the qs() test helper in the new ui tests
EhabY
force-pushed
the
feat/devex-618-ui-state-panels
branch
from
July 27, 2026 10:54
7e4416e to
a8ea854
Compare
VS Code's badge is a plain counter with no icon or status variant, and its tone accents (testing-iconPassed, errorForeground, ...) are tuned against panel backgrounds, not the badge fill: on the badge they measured as low as 1.2:1. Tint the pill background with the tone accent itself (15% alpha via color-mix) so accents sit over the surface they were designed for, keep text at the theme foreground, and pull the icon color 30% toward the foreground so pale light-theme accents stay visible. Works across all four themes from a single formula, so the high-contrast override and the now-unused badge tokens are gone.
The pill is VS Code first: without a tone it renders as the native badge (badge-background/foreground), verified against VscodeBadge in the parity story alongside a plain 42 counter. Tones remain the GitHub-style tinted extra, and a Native story snapshots the plain pill on its own.
- type props with ComponentProps so React 19 forwards refs through the existing spreads; SearchInput merges the consumer ref with its internal clear-and-refocus ref via a callback ref, with a test - lint with eslint-plugin-react-hooks and its compiler diagnostics, combined with @eslint-react through the official disable-conflict-eslint-plugin-react-hooks preset so no rule reports twice; globals/immutability/refs enabled explicitly since the recommended preset leaves them off - run the React Compiler in Storybook, matching production webview builds; this surfaced a real stale-memo bug in the foundations story (the compiler drops deps-array-only triggers), fixed by remounting the token table per theme and reading values in state initializers - let story bodies scroll: real webviews scroll, so overflow: hidden was a wrong host stand-in that made tall stories unviewable
Removing the stand-in's own overflow: hidden wasn't enough: the preview auto-imports every package's CSS and tasks' app shell hides body overflow globally. Override it with a higher-specificity rule so load order doesn't matter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1035
PR #1030 established the
@repo/uiscaffold and semantic theme boundary. This stacked PR turns it into the primitives needed for upcoming Workspaces states:Icon,IconButton,Spinner,ProgressBar,SearchInput,StatusPill,EmptyState, andErrorState.WelcomePanelis omitted (nativeviewsWelcomealready covers it) andSkeletonis deferred until the Workspaces first-load layout provides concrete placeholder geometry.VS Code fidelity
Components are native HTML elements styled to match VS Code's own controls, using VS Code Elements as a design reference without importing it: 22px toolbar icon buttons, 26px search input, 28px progress ring and 2px progress bar, native primary-button retry styling, VS Code's 0.4 disabled opacity, and high-contrast, forced-colors, and reduced-motion handling throughout.
StatusPilldefaults to VS Code's native badge; tones and icons are an extra, where a tone tints the pill GitHub-style with its own accent at 15% alpha so it stays legible in all four themes.Library boundary (NPM-ready)
@repo/*and relative cross-package imports; React stays a peer dependency and codicons the only runtime dependency.CodiconNameis derived at type-check time from codicon metadata, and shared internals (#cx,#codicons,#storybook) usepackage.jsonsubpath imports that ship with the package on a standalone split.--ui-*tokens only;tokens.cssis the single VS Code mapping layer.Storybook and visual regression
pnpm sync:vscode-themes; deterministic capture, verbatim prettier-ignored output so a resync leaves no diff) and applied as a single synchronous:rootstylesheet swap.parameters.pixeladds the high-contrast themes to the light/dark base matrix).eslint-plugin-react-hookscombined with@eslint-reactvia its conflict preset) all exercise what ships.UI/VSCodeParityrenders each control beside its@vscode-elementscounterpart under identical variables (plus a link row against the captured default stylesheet), so drift from VS Code styling shows up as a visual diff.Coverage
test/webview/ui/cover accessibility and behavior;EmptyState/ErrorStateshare one internalStatePanelso the panel markup can't drift.Generated by Coder Agents on behalf of @EhabY.
Implementation plan
UI core primitives and state panels
Goal
Create a stacked PR on
feat/devex-624-ui-scaffoldthat adds concise, generic components to@repo/uiwith Storybook, visual-regression, accessibility, and focused behavioral coverage, while preserving a clean future standalone-library boundary.Scope
Implement
Icon,IconButton,Spinner,ProgressBar,SearchInput,StatusPill,EmptyState, andErrorState.Do not implement
WelcomePanel(nativeviewsWelcomeremains) orSkeleton(no concrete first-load geometry yet).Styling strategy
Match VS Code exactly with native elements rather than a custom design system or a runtime dependency on
@vscode-elements:tokens.css; component CSS uses semantic--ui-*tokens only.forced-colorsandprefers-reduced-motionmedia handling.Minimal tooling
CodiconNameas a TypeScript type from codicon metadata (resolveJsonModule); commit no generated output and expose no runtime icon array.@repo/*bans andimport-x/no-relative-packages) instead of a custom script.Storybook
One combined matrix story per component under
UI/<Component>, four-theme Chromatic modes on each matrix, interaction assertions via play functions, and a snapshot-disabled curated icon gallery. Detailed behavior stays in Vitest.Behavioral tests
Focused Testing Library/Vitest tests in
test/webview/ui/covering icon semantics, button accessibility, spinner status, progress ARIA/clamping, controlled search and clear-focus behavior, status pill tones, and state panel slots/retry.Validation
pnpm format:check,pnpm typecheck,pnpm lint,pnpm test:webview ./test/webview/ui,pnpm storybook:ci,pnpm build, andgit diff --check, plus manual Storybook review across all four themes.Git and PR
feat/devex-618-ui-state-panels, stacked onfeat/devex-624-ui-scaffold.