feat: CodeMirror editor and theme preferences toggle (#905, #807)#909
feat: CodeMirror editor and theme preferences toggle (#905, #807)#909
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📜 Recent review details⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
🧰 Additional context used📓 Path-based instructions (3)**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/src/**/*.{ts,tsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
web/**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (CLAUDE.md)
Files:
🧠 Learnings (5)📓 Common learnings📚 Learning: 2026-03-20T08:28:32.845ZApplied to files:
📚 Learning: 2026-03-28T18:10:43.543ZApplied to files:
📚 Learning: 2026-03-28T18:10:43.543ZApplied to files:
📚 Learning: 2026-03-28T18:10:43.542ZApplied to files:
🔇 Additional comments (5)
WalkthroughAdds a Zustand theme store (color palette, density, typography, animation preset, sidebar mode) with localStorage persistence, reduced-motion detection, safe DOM class application, and a reset API. Introduces UI components and hooks: CodeMirrorEditor, SegmentedControl, ThemeToggle, and useAnimationPreset. Integrates ThemeToggle into StatusBar and registers theme commands in AppLayout. Sidebar rendering now respects sidebarMode (hidden, rail, compact, persistent, collapsible) with persisted localCollapsed. Replaces Settings page textarea with CodeMirror, updates design tokens/CSS for sidebar widths and typography, adds font and CodeMirror packages, Storybook stories, and extensive Vitest tests. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
Dependency ReviewThe following issues were found:
Snapshot WarningsEnsure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice. OpenSSF ScorecardScorecard details
Scanned Files
|
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive theme system, allowing users to customize color palettes, density, typography, motion, and sidebar behavior. Key additions include a new theme Zustand store with localStorage persistence, a ThemeToggle component integrated into the status bar, and an accessible SegmentedControl UI component. Additionally, the settings page has been upgraded to use a new CodeMirrorEditor component for JSON and YAML editing. Feedback focuses on simplifying the ThemeToggle by passing option constants directly to child components and removing a redundant storage write in the theme store's reset logic.
| // Memoize stable option arrays for SegmentedControl (avoid re-renders) | ||
| const densityOpts = useMemo(() => [...DENSITY_OPTIONS], []) | ||
| const animationOpts = useMemo(() => [...ANIMATION_OPTIONS], []) | ||
| const sidebarOpts = useMemo(() => [...SIDEBAR_OPTIONS], []) |
There was a problem hiding this comment.
There's an inconsistency in how options arrays are passed to child components. You are using useMemo to create stable arrays for SegmentedControl (to prevent re-renders), but for SelectField components (lines 122 and 141), new arrays are created on each render.
A simpler and more consistent approach is to pass the constant ..._OPTIONS arrays directly to all components. Since they are defined with as const, their references are already stable. This would make the useMemo calls here unnecessary and ensure consistent behavior.
For example:
<SelectField
label="Color"
options={COLOR_OPTIONS}
// ...
/>
<SegmentedControl
label="Density"
options={DENSITY_OPTIONS}
// ...
/>| reset: () => { | ||
| const defaults = getDefaultPreferences() | ||
| set({ ...defaults }) | ||
| savePreferences(defaults) | ||
| applyThemeClasses(defaults) | ||
| try { | ||
| localStorage.removeItem(STORAGE_KEY) | ||
| } catch { | ||
| // Ignore | ||
| } | ||
| }, |
There was a problem hiding this comment.
The savePreferences(defaults) call on line 230 is redundant because localStorage.removeItem(STORAGE_KEY) is called on line 233. This writes the default preferences to local storage only to have them immediately deleted. You can remove the savePreferences call to simplify the logic.
reset: () => {
const defaults = getDefaultPreferences()
set({ ...defaults })
applyThemeClasses(defaults)
try {
localStorage.removeItem(STORAGE_KEY)
} catch {
// Ignore
}
},There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/code-mirror-editor.test.tsx`:
- Around line 112-217: Add property-based tests using fast-check in the
CodeMirrorEditor test suite: import fc and write fc.assert + fc.property cases
that (1) generate arbitrary editor values (fc.string or fc.jsonText) and assert
rerendering CodeMirrorEditor with a new value calls mockDispatch with a change
containing the new string (reference MockEditorView, mockDispatch, and the
existing "syncs external value changes via dispatch" test), and (2) generate
arbitrary language (fc.constantFrom('json','yaml',...)) and/or readOnly booleans
(fc.boolean) and assert rerendering triggers a reconfiguration dispatch
(reference the "reconfigures language compartment..." and "reconfigures readOnly
compartment..." tests and expect.effects). Keep existing deterministic tests;
use beforeEach/afterEach setup (MockEditorView._instances,
mockDispatch.mockClear, mockDestroy) inside the property closures so each
shrink/run has isolated state and assertions match the current mock call
expectations.
In `@web/src/__tests__/components/ui/segmented-control.test.tsx`:
- Around line 12-102: Add property-based tests using fast-check to cover
SegmentedControl interaction invariants: import fc and write
fc.assert(fc.property(...)) blocks in
web/src/__tests__/components/ui/segmented-control.test.tsx that generate random
option arrays (including some disabled flags), a random current value, and
random user actions (click on an index or ArrowLeft/ArrowRight key). For each
property, render <SegmentedControl ... /> and assert that (1) clicking only
calls onChange for enabled options and not for disabled ones (use generated
index and enabled flag), and (2) ArrowLeft/ArrowRight navigation computes the
expected next value with wrap-around relative to the generated options and
current value (assert onChange called with that value). Reference the
SegmentedControl component and existing test helpers (userEvent.setup(),
onChange vi.fn()) so the new fc.assert(fc.property(...)) tests integrate with
the current test patterns.
In `@web/src/__tests__/components/ui/theme-toggle.test.tsx`:
- Around line 7-87: Add property-based tests using fast-check to assert store/UI
synchronization for ThemeToggle: write fc.assert(fc.property(...)) cases that
(1) pick random valid colorPalette keys and ensure selecting that option via the
Color select updates useThemeStore.getState().colorPalette accordingly
(reference: ThemeToggle and test block that gets color select and uses
useThemeStore.setColorPalette), (2) pick random density values and ensure
clicking the segmented control radio updates useThemeStore.getState().density
(reference: radio 'Dense' usage and useThemeStore), (3) pick random typography
keys and ensure the Font select updates useThemeStore.getState().typography, and
(4) assert reset behavior by applying random changes to the store, opening
ThemeToggle, clicking the "Reset to defaults" button, and checking store fields
return to their default values; use fc.constantFrom or fc.oneof limited to the
app's allowed axis values, call userEvent interactions inside the property, and
wrap each property with fc.assert to fail on counterexamples.
In `@web/src/__tests__/stores/theme.test.ts`:
- Around line 207-269: Add a property-based test that verifies loadPreferences
always returns valid enum values for arbitrary localStorage contents by using
fast-check: import fc and write an fc.assert(fc.property(...)) that sets
localStorage.setItem(STORAGE_KEY, JSON.stringify(arbitrary)), calls
loadPreferences(), and asserts the returned prefs' fields are members of the
allowed sets (COLOR_PALETTES, DENSITIES, TYPOGRAPHIES, ANIMATION_PRESETS,
SIDEBAR_MODES); use fc.assert + fc.property so the test covers many malformed
inputs automatically.
In `@web/src/components/layout/Sidebar.tsx`:
- Line 85: The component currently uses hardcoded pixel widths in the expression
that computes sidebar class names (sidebarMode and collapsed) — replace
'w-[180px]' and 'w-[220px]' with design-token-backed classes or CSS vars (e.g.,
token classes like w-token-xx or utility classes that reference CSS variables)
so layout spacing follows the theme/density system; update the conditional
expression in Sidebar.tsx where sidebarMode and collapsed are used to return the
tokenized width classes (or a var-based class such as
'w-[var(--sidebar-compact)]' / 'w-[var(--sidebar-default)]') and ensure any
corresponding CSS variables or design-token mappings exist or are added to the
theme tokens.
In `@web/src/components/ui/code-mirror-editor.stories.tsx`:
- Around line 36-71: Add an Error story (or document absence of an error state)
to the CodeMirror editor stories to satisfy the component-state coverage
guideline: create a new exported story named Error alongside Default, YAML,
ReadOnly, and Empty that supplies args demonstrating a syntax/validation error
(e.g., value containing invalid JSON/YAML, language set appropriately, onChange
stub, and an aria-label like "Error editor") so the UI shows error
styling/behavior; if the CodeMirrorEditor component does not support a distinct
error mode, add a brief comment in the stories file explaining that no error
state exists and why instead of adding a story.
In `@web/src/components/ui/code-mirror-editor.tsx`:
- Around line 30-83: The theme currently hardcodes pixel values for fontSize and
padding; update the EditorView.theme darkTheme to use design tokens instead of
'14px' and '16px 8px' — replace the fontSize set on the root selector ('&') with
the appropriate token (e.g. var(--so-font-size-...)) and replace the padding in
'.cm-content' with spacing tokens (e.g. var(--so-space-vertical-...)
var(--so-space-horizontal-...)); ensure you only change the values for darkTheme
in the EditorView.theme call and keep selectors '.cm-content' and '&' intact.
In `@web/src/components/ui/segmented-control.stories.tsx`:
- Around line 22-93: Add the required Storybook states for this shared component
by adding Story exports named hover, loading, error, and empty alongside the
existing Default/SmallSize/MediumSize/Disabled/WithDisabledOption/Interactive;
each should reference the SegmentedControl component (and densityOptions where
appropriate) and provide appropriate args: hover should render the control with
its value and simulate the hovered visual state (or include a play function to
set :hover), loading should pass a loading flag or prop to show a
skeleton/spinner state, error should pass an error prop/message to render the
error UI, and empty should provide an empty options array (or value=null) to
display the empty-state UI—use the same pattern of args and, when needed, a
render function like InteractiveControl to manage local state.
In `@web/src/components/ui/theme-toggle.stories.tsx`:
- Around line 1-21: Add explicit story variants for ThemeToggle to cover the
popover-open and reduced-motion scenarios: create a PopoverOpen Story (export
const PopoverOpen) whose play function imports within from '@storybook/test' and
userEvent from '@testing-library/user-event', finds the toggle button
(getByRole('button')) and clicks it to open the popover; and create a
WithReducedMotion Story (export const WithReducedMotion) that simulates
prefers-reduced-motion by stubbing window.matchMedia in the story's play or a
decorator before rendering so the component reads reduced-motion as true and
renders the reduced-motion UI; update the StoryObj exports (Default,
PopoverOpen, WithReducedMotion) in theme-toggle.stories.tsx and ensure imports
for within and userEvent are added.
In `@web/src/components/ui/theme-toggle.tsx`:
- Around line 21-56: COLOR_OPTIONS, DENSITY_OPTIONS, TYPOGRAPHY_OPTIONS,
ANIMATION_OPTIONS and SIDEBAR_OPTIONS are hard-coded here with repeated "as
const" casts causing drift from the store; instead import the canonical
axis/value enums or constant arrays from the theme/store and derive these option
lists by mapping those exports to { value, label } entries and typing them with
the store's exported value types (so you can remove the ad-hoc "as const"
casts). Specifically: import the axis constant (e.g., THEME_COLOR_AXIS,
DENSITY_AXIS, TYPOGRAPHY_AXIS, ANIMATION_AXIS, SIDEBAR_AXIS) and their value
types from the store, map each to the option shape used in the UI, and update
any handlers/props to use the store-exported types so options and state stay in
sync (also apply the same change to the other occurrences noted around the
component).
In `@web/src/stores/theme.ts`:
- Around line 227-237: The reset function currently calls
savePreferences(defaults) to persist defaults and then immediately removes the
same STORAGE_KEY from localStorage, which contradicts persistence; remove the
try/catch block that calls localStorage.removeItem(STORAGE_KEY) so reset uses
getDefaultPreferences(), calls set(...) and savePreferences(defaults), and then
applyThemeClasses(defaults) without clearing STORAGE_KEY (look for the reset
function and the localStorage.removeItem code around savePreferences and
applyThemeClasses to remove).
- Around line 167-183: The reduced-motion change handler incorrectly assumes
that equality with getDefaultPreferences().animation means the user hasn't
explicitly chosen an animation; update the logic to track an explicit flag
(e.g., animationExplicit or animationPreferenceSet) in the preferences/state and
only auto-change animation inside the mql.change handler when that flag is
false. Adjust the state type and any getters/setters (get(), set(), getPrefs()),
ensure savePreferences() persists the new explicit flag when the user manually
chooses an animation, and then in the mql.addEventListener callback check that
flag before computing newAnimation, calling savePreferences(prefs),
applyThemeClasses(prefs) and set({ animation: newAnimation }) so an explicit
user choice (including 'status-driven') is never overridden by OS reduced-motion
changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7a9b9f0b-ad20-4b15-adef-cf870ffcd57b
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (23)
CLAUDE.mddocs/design/brand-and-ux.mdweb/package.jsonweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/stores/theme.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/theme-toggle.tsxweb/src/hooks/useAnimationPreset.tsweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/stores/theme.tsweb/src/styles/design-tokens.cssweb/src/styles/global.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Deploy Preview
- GitHub Check: Dashboard Test
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always reuse existing components from
web/src/components/ui/before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside.map()blocks, or usergba()with hardcoded values instead of design token variables.
Files:
web/src/components/layout/StatusBar.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/stores/theme.test.tsweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use design token rules: colors via Tailwind semantic classes (
text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-accent)), typography viafont-sansorfont-mono, spacing via density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind, shadows/borders via token variables (var(--so-shadow-card-hover),border-border,border-bright). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Files:
web/src/components/layout/StatusBar.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/stores/theme.test.tsweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use property-based testing with fast-check (
fc.assert+fc.property). Run tests withnpm --prefix web run testfor Vitest with--detect-async-leaksflag.
Files:
web/src/components/layout/StatusBar.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/pages/settings/CodeEditorPanel.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/stores/theme.test.tsweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
**/*.{md,mdx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always read the relevant
docs/design/page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from spec, alert the user and explain why -- user decides whether to proceed or update the spec. Never silently diverge. When approved deviations occur, update the relevantdocs/design/page to reflect the new reality.
Files:
docs/design/brand-and-ux.mdCLAUDE.md
docs/design/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Design spec pages are mandatory reading before implementing features. Design specs pages are the starting point for architecture, data models, and behavior. When implementation deviates from spec, alert the user with reasoning. Update the relevant design page when approved deviations occur.
Files:
docs/design/brand-and-ux.md
web/src/components/ui/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
New shared components in
web/src/components/ui/must have a corresponding.stories.tsxfile with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and usecn()from@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/theme-toggle.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.tsx
🧠 Learnings (18)
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Applied to files:
web/src/styles/global.cssweb/src/components/ui/theme-toggle.tsxCLAUDE.mdweb/src/styles/design-tokens.cssweb/src/stores/theme.ts
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
web/src/styles/global.cssweb/package.jsonCLAUDE.md
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/components/layout/StatusBar.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxdocs/design/brand-and-ux.mdweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/stores/theme.test.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxCLAUDE.mdweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.tsxweb/src/styles/design-tokens.cssweb/src/stores/theme.ts
📚 Learning: 2026-03-28T18:10:43.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
Applied to files:
web/src/components/layout/StatusBar.tsxdocs/design/brand-and-ux.mdweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxCLAUDE.mdweb/src/components/ui/segmented-control.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/stores/theme.test.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxCLAUDE.md
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/**/*.{ts,tsx,js,jsx} : Use property-based testing with fast-check (`fc.assert` + `fc.property`). Run tests with `npm --prefix web run test` for Vitest with `--detect-async-leaks` flag.
Applied to files:
web/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
Applied to files:
web/package.jsonCLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Applied to files:
CLAUDE.md
🔇 Additional comments (27)
web/src/styles/global.css (1)
7-10: LGTM!The font imports are correctly added for the new typography axis options (JetBrains Mono + Inter, IBM Plex). Self-hosting via
@fontsourcepackages aligns with the privacy/performance requirements documented inbrand-and-ux.md.web/src/components/layout/StatusBar.tsx (1)
125-128: LGTM!The
ThemeToggleintegration follows the established StatusBar patterns—using aDividerbefore the new element and placing it at the far right. This satisfies the PR objective of an "always-visible, non-intrusive" theme toggle accessible from all pages.docs/design/brand-and-ux.md (1)
265-265: LGTM!The documentation accurately describes
useAnimationPreset()return shape (spring,tween,staggerDelay,enableLayout) and provides clear guidance that components should use this hook rather than directly referencinglib/motion.tsconstants. This aligns with the implementation and test assertions.web/package.json (2)
30-33: LGTM!The font packages are correctly added to support the new typography axis options (JetBrains Mono + Inter, IBM Plex). These align with the imports in
global.cssand the theme system design.
18-23: CodeMirror 6 packages are current and secure.All specified versions (6.10.3, 6.0.2, 6.1.3, 6.12.3, 6.6.0, 6.40.0) match the latest releases on npm. No known security vulnerabilities affect CodeMirror 6; reported CVEs target only the legacy CodeMirror 5.x branch. These dependencies are safe to use.
web/src/components/layout/AppLayout.tsx (1)
64-92: LGTM!The theme commands are well-structured, covering all 5 theme axes with consistent patterns. Using
useThemeStore.getState()in action callbacks is the correct Zustand pattern for accessing store methods from event handlers. The empty dependency array inuseMemois appropriate since no reactive component values are captured—the store access is deferred to action execution time.web/src/pages/settings/CodeEditorPanel.tsx (2)
240-246: LGTM!The
CodeMirrorEditorintegration is clean. Thelanguageprop receives the correctly-typedformatvalue ('json' | 'yaml'), andreadOnly={saving}appropriately prevents edits during save operations. Accessibility is maintained viaaria-label.
165-169: LGTM!The
handleChangesignature is correctly updated from the textarea event pattern to theCodeMirrorEditor's direct string callback pattern(value: string) => void.web/src/__tests__/hooks/useAnimationPreset.test.ts (1)
1-61: LGTM!The test suite is well-structured with proper store isolation via
beforeEachreset, comprehensive preset coverage usingit.each, and specific value assertions for key presets (minimal,instant,spring,aggressive). The tests verify both the shape contract and behavioral expectations of theuseAnimationPresethook.While property-based testing with fast-check is recommended by coding guidelines, the finite preset domain (5 fixed values) is thoroughly covered by the
it.eachapproach, making property-based testing of limited additional value here.web/src/hooks/useAnimationPreset.ts (1)
24-67: Preset mapping is exhaustive and clean.The
Record<AnimationPreset, AnimationPresetConfig>approach plus memoized lookup is solid and keeps this hook predictable.web/src/components/ui/code-mirror-editor.stories.tsx (1)
73-113: LGTM! Well-structured Interactive story.The Interactive story effectively demonstrates language switching and controlled value updates. Good use of React state to showcase the component's dynamic behavior.
web/src/__tests__/stores/theme.test.ts (1)
1-206: LGTM! Comprehensive test coverage.The test suite thoroughly covers store defaults, setters with CSS class application, localStorage persistence, class removal on theme changes, and reset behavior. The reduced-motion handling in Line 23 is correctly tested with an array assertion.
web/src/pages/settings/CodeEditorPanel.stories.tsx (2)
6-58: LGTM! Well-designed mock data helper.The
makeSettinghelper provides sensible defaults while allowing granular overrides. ThemockEntriesarray covers diverse setting types (int, float) across different namespaces and groups, which is good for demonstrating component behavior.
86-107: LGTM! Interactive story effectively demonstrates save flow.The Interactive story correctly manages local
savingstate and simulates an async save operation, allowing visual verification of the loading/saving UI state.web/src/styles/design-tokens.css (2)
117-187: LGTM! Consistent theme palette implementation.All four theme palettes override the complete set of brand/background/border/shadow/overlay tokens including the new selection overlay tokens. This ensures consistent styling across themes.
One note: The comment header mentions WCAG AA verification (Line 14-15). Ensure the new color palettes have been validated for accessibility contrast ratios.
189-213: LGTM! Clean typography and animation axis definitions.Typography classes correctly override both
--so-font-sansand--so-font-monowith appropriate fallback stacks. The animation axis comment block clearly documents the marker class convention consumed byuseAnimationPreset().web/src/components/ui/segmented-control.tsx (3)
4-24: LGTM! Well-typed generic props interface.The generic
SegmentedControlOption<T>andSegmentedControlProps<T>interfaces provide strong type safety while maintaining flexibility. Thereadonlymodifiers on options array and individual option properties enforce immutability appropriately.
37-67: LGTM! Robust keyboard navigation implementation.The keyboard handler correctly:
- Filters disabled options before navigation
- Implements wrap-around for both directions
- Prevents default on arrow keys to avoid scroll interference
- Moves focus to the newly selected option via data-value lookup
69-112: LGTM! Accessible radiogroup implementation.The component follows ARIA best practices with proper
role="radiogroup",role="radio",aria-checked, and roving tabindex (tabIndex={selected ? 0 : -1}). Uses design tokens consistently via semantic Tailwind classes.web/src/components/ui/code-mirror-editor.tsx (4)
89-98: LGTM! Syntax highlighting uses design tokens.The
HighlightStylecorrectly maps syntax tags to design token CSS variables (--so-accent,--so-success,--so-warning, etc.), ensuring consistent theming.
132-172: LGTM! Well-structured editor initialization.The mount effect correctly:
- Initializes compartments for dynamic reconfiguration
- Sets up the update listener with
isProgrammaticRefguard- Properly cleans up the EditorView on unmount
- The eslint-disable comment is justified as the effect should only run once with compartment-based updates handled separately
174-211: LGTM! Correct external value and configuration sync.The separate effects for value sync (Lines 175-187), language reconfiguration (Lines 190-199), and readOnly reconfiguration (Lines 202-211) properly handle each concern independently. The
isProgrammaticRefguard correctly prevents feedback loops during external value updates.
213-227: LGTM! Accessible container with proper ARIA attributes.The container correctly exposes
role="textbox",aria-label,aria-readonly, andaria-multilinefor screen reader accessibility. Usescn()for class merging as required.web/src/stores/theme.ts (4)
112-120: LGTM! Good security practice with CSS class sanitization.The
safeClass()function prevents potential CSS class injection by validating that class names contain only lowercase alphanumeric characters and hyphens. This is a thoughtful security measure.
1-111: LGTM! Well-structured type definitions and helpers.The type definitions, constant arrays, and helper functions are cleanly organized. The
loadPreferences()function provides robust fallback behavior for invalid or missing localStorage data.
145-165: LGTM! Clean store initialization.The store correctly loads preferences from localStorage, applies initial theme classes synchronously, and sets up reduced motion detection on initialization.
185-226: LGTM! Consistent setter implementation pattern.All setters follow a consistent pattern: update state, compute full preferences, persist to localStorage, and apply CSS classes. The
setPopoverOpencorrectly skips persistence as it's transient UI state.
| describe('ThemeToggle', () => { | ||
| beforeEach(() => { | ||
| useThemeStore.getState().reset() | ||
| useThemeStore.getState().setPopoverOpen(false) | ||
| }) | ||
|
|
||
| it('renders the trigger button', () => { | ||
| render(<ThemeToggle />) | ||
| expect(screen.getByRole('button', { name: 'Theme preferences' })).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('opens popover on click', async () => { | ||
| const user = userEvent.setup() | ||
| render(<ThemeToggle />) | ||
|
|
||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
| expect(screen.getByText('Theme Preferences')).toBeInTheDocument() | ||
| }) | ||
|
|
||
| it('displays all 5 axis controls when open', async () => { | ||
| const user = userEvent.setup() | ||
| render(<ThemeToggle />) | ||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
|
|
||
| // Color (select) + Font (select) labels | ||
| expect(screen.getByLabelText('Color')).toBeInTheDocument() | ||
| expect(screen.getByLabelText('Font')).toBeInTheDocument() | ||
|
|
||
| // Density, Motion, Sidebar segmented controls (visible labels -- multiple matches due to sr-only legend) | ||
| expect(screen.getAllByText('Density').length).toBeGreaterThanOrEqual(1) | ||
| expect(screen.getByText('Motion')).toBeInTheDocument() | ||
| expect(screen.getAllByText('Sidebar').length).toBeGreaterThanOrEqual(1) | ||
| }) | ||
|
|
||
| it('changes color palette via select', async () => { | ||
| const user = userEvent.setup() | ||
| render(<ThemeToggle />) | ||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
|
|
||
| const colorSelect = screen.getByLabelText('Color') | ||
| await user.selectOptions(colorSelect, 'ice-station') | ||
|
|
||
| expect(useThemeStore.getState().colorPalette).toBe('ice-station') | ||
| }) | ||
|
|
||
| it('changes density via segmented control', async () => { | ||
| const user = userEvent.setup() | ||
| render(<ThemeToggle />) | ||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
|
|
||
| await user.click(screen.getByRole('radio', { name: 'Dense' })) | ||
| expect(useThemeStore.getState().density).toBe('dense') | ||
| }) | ||
|
|
||
| it('resets to defaults', async () => { | ||
| const user = userEvent.setup() | ||
|
|
||
| // Change some settings first | ||
| useThemeStore.getState().setColorPalette('neon') | ||
| useThemeStore.getState().setDensity('sparse') | ||
|
|
||
| render(<ThemeToggle />) | ||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
| await user.click(screen.getByRole('button', { name: 'Reset to defaults' })) | ||
|
|
||
| const state = useThemeStore.getState() | ||
| expect(state.colorPalette).toBe('warm-ops') | ||
| expect(state.density).toBe('balanced') | ||
| }) | ||
|
|
||
| it('changes typography via select', async () => { | ||
| const user = userEvent.setup() | ||
| render(<ThemeToggle />) | ||
| await user.click(screen.getByRole('button', { name: 'Theme preferences' })) | ||
|
|
||
| const fontSelect = screen.getByLabelText('Font') | ||
| await user.selectOptions(fontSelect, 'ibm-plex') | ||
|
|
||
| expect(useThemeStore.getState().typography).toBe('ibm-plex') | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Add property-based tests for store/UI synchronization paths.
Please add fast-check properties for theme-axis transitions (select/radio/reset), not only fixed examples, to meet the required web test strategy.
Based on learnings: Dashboard testing ... Use fast-check for property-based testing (fc.assert + fc.property).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/theme-toggle.test.tsx` around lines 7 - 87,
Add property-based tests using fast-check to assert store/UI synchronization for
ThemeToggle: write fc.assert(fc.property(...)) cases that (1) pick random valid
colorPalette keys and ensure selecting that option via the Color select updates
useThemeStore.getState().colorPalette accordingly (reference: ThemeToggle and
test block that gets color select and uses useThemeStore.setColorPalette), (2)
pick random density values and ensure clicking the segmented control radio
updates useThemeStore.getState().density (reference: radio 'Dense' usage and
useThemeStore), (3) pick random typography keys and ensure the Font select
updates useThemeStore.getState().typography, and (4) assert reset behavior by
applying random changes to the store, opening ThemeToggle, clicking the "Reset
to defaults" button, and checking store fields return to their default values;
use fc.constantFrom or fc.oneof limited to the app's allowed axis values, call
userEvent interactions inside the property, and wrap each property with
fc.assert to fail on counterexamples.
| export const Default: Story = { | ||
| args: { | ||
| label: 'Density', | ||
| options: [...densityOptions], | ||
| value: 'balanced', | ||
| onChange: () => {}, | ||
| }, | ||
| } | ||
|
|
||
| export const SmallSize: Story = { | ||
| args: { | ||
| label: 'Density', | ||
| options: [...densityOptions], | ||
| value: 'dense', | ||
| onChange: () => {}, | ||
| size: 'sm', | ||
| }, | ||
| } | ||
|
|
||
| export const MediumSize: Story = { | ||
| args: { | ||
| label: 'Density', | ||
| options: [...densityOptions], | ||
| value: 'balanced', | ||
| onChange: () => {}, | ||
| size: 'md', | ||
| }, | ||
| } | ||
|
|
||
| export const Disabled: Story = { | ||
| args: { | ||
| label: 'Density', | ||
| options: [...densityOptions], | ||
| value: 'balanced', | ||
| onChange: () => {}, | ||
| disabled: true, | ||
| }, | ||
| } | ||
|
|
||
| export const WithDisabledOption: Story = { | ||
| args: { | ||
| label: 'Animation', | ||
| options: [ | ||
| { value: 'minimal', label: 'Minimal' }, | ||
| { value: 'spring', label: 'Spring', disabled: true }, | ||
| { value: 'instant', label: 'Instant' }, | ||
| { value: 'status-driven', label: 'Status' }, | ||
| ], | ||
| value: 'minimal', | ||
| onChange: () => {}, | ||
| }, | ||
| } | ||
|
|
||
| export const Interactive: Story = { | ||
| args: { | ||
| label: 'Mode', | ||
| options: [...densityOptions], | ||
| value: 'balanced', | ||
| onChange: () => {}, | ||
| }, | ||
| render: function InteractiveControl() { | ||
| const [value, setValue] = useState('balanced') | ||
| return ( | ||
| <SegmentedControl | ||
| label="Density" | ||
| options={[...densityOptions]} | ||
| value={value} | ||
| onChange={setValue} | ||
| /> | ||
| ) | ||
| }, | ||
| } |
There was a problem hiding this comment.
Add the mandatory shared-component Storybook states.
For a new shared UI component story file, hover, loading, error, and empty states are still missing.
As per coding guidelines: New shared components in web/src/components/ui/ must have a corresponding .stories.tsx file with all states (default, hover, loading, error, empty).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/segmented-control.stories.tsx` around lines 22 - 93,
Add the required Storybook states for this shared component by adding Story
exports named hover, loading, error, and empty alongside the existing
Default/SmallSize/MediumSize/Disabled/WithDisabledOption/Interactive; each
should reference the SegmentedControl component (and densityOptions where
appropriate) and provide appropriate args: hover should render the control with
its value and simulate the hovered visual state (or include a play function to
set :hover), loading should pass a loading flag or prop to show a
skeleton/spinner state, error should pass an error prop/message to render the
error UI, and empty should provide an empty options array (or value=null) to
display the empty-state UI—use the same pattern of args and, when needed, a
render function like InteractiveControl to manage local state.
| import type { Meta, StoryObj } from '@storybook/react' | ||
| import { ThemeToggle } from './theme-toggle' | ||
|
|
||
| const meta = { | ||
| title: 'UI/ThemeToggle', | ||
| component: ThemeToggle, | ||
| tags: ['autodocs'], | ||
| parameters: { layout: 'centered' }, | ||
| decorators: [ | ||
| (Story) => ( | ||
| <div className="flex h-[500px] w-[400px] items-start justify-end p-8"> | ||
| <Story /> | ||
| </div> | ||
| ), | ||
| ], | ||
| } satisfies Meta<typeof ThemeToggle> | ||
|
|
||
| export default meta | ||
| type Story = StoryObj<typeof meta> | ||
|
|
||
| export const Default: Story = {} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add additional story variants for different states.
Per coding guidelines, shared components in web/src/components/ui/ should have stories covering all applicable states. Consider adding stories for:
- PopoverOpen: Shows the theme popover expanded (useful for visual regression testing)
- WithReducedMotion: Simulates
prefers-reduced-motiondetection scenario
The hover, loading, error, and empty states may not be applicable for a toggle control, but the popover-open state is a key visual state for this component.
💡 Suggested additional stories
export const PopoverOpen: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
const trigger = canvas.getByRole('button')
await userEvent.click(trigger)
},
}Note: This requires importing within from @storybook/test and userEvent from @testing-library/user-event.
As per coding guidelines: "New shared components in web/src/components/ui/ must have a corresponding .stories.tsx file with all states (default, hover, loading, error, empty)".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/theme-toggle.stories.tsx` around lines 1 - 21, Add
explicit story variants for ThemeToggle to cover the popover-open and
reduced-motion scenarios: create a PopoverOpen Story (export const PopoverOpen)
whose play function imports within from '@storybook/test' and userEvent from
'@testing-library/user-event', finds the toggle button (getByRole('button')) and
clicks it to open the popover; and create a WithReducedMotion Story (export
const WithReducedMotion) that simulates prefers-reduced-motion by stubbing
window.matchMedia in the story's play or a decorator before rendering so the
component reads reduced-motion as true and renders the reduced-motion UI; update
the StoryObj exports (Default, PopoverOpen, WithReducedMotion) in
theme-toggle.stories.tsx and ensure imports for within and userEvent are added.
| // Listen for reduced-motion changes | ||
| if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') { | ||
| const mql = window.matchMedia('(prefers-reduced-motion: reduce)') | ||
| mql.addEventListener('change', (e) => { | ||
| set({ reducedMotionDetected: e.matches }) | ||
| // If user hasn't explicitly changed animation, follow OS preference | ||
| const state = get() | ||
| const defaults = getDefaultPreferences() | ||
| if (state.animation === defaults.animation || (e.matches && state.animation !== 'minimal')) { | ||
| const newAnimation: AnimationPreset = e.matches ? 'minimal' : 'status-driven' | ||
| const prefs = { ...getPrefs(state), animation: newAnimation } | ||
| savePreferences(prefs) | ||
| applyThemeClasses(prefs) | ||
| set({ animation: newAnimation }) | ||
| } | ||
| }) | ||
| } |
There was a problem hiding this comment.
Potential logic issue in reduced-motion change handler.
The condition on Line 175 may not correctly detect "user hasn't explicitly changed animation". If the user explicitly sets animation to status-driven (the default), this condition would still be true and override their choice when reduced-motion toggles.
Consider tracking whether the user has ever explicitly set the animation preference separately from comparing against defaults.
💡 Suggested approach
export interface ThemeState extends ThemePreferences {
popoverOpen: boolean
reducedMotionDetected: boolean
+ animationExplicitlySet: boolean
// ... setters
}Then in the change handler:
mql.addEventListener('change', (e) => {
set({ reducedMotionDetected: e.matches })
- // If user hasn't explicitly changed animation, follow OS preference
const state = get()
- const defaults = getDefaultPreferences()
- if (state.animation === defaults.animation || (e.matches && state.animation !== 'minimal')) {
+ // Only auto-switch if user hasn't explicitly set animation
+ if (!state.animationExplicitlySet) {
const newAnimation: AnimationPreset = e.matches ? 'minimal' : 'status-driven'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/theme.ts` around lines 167 - 183, The reduced-motion change
handler incorrectly assumes that equality with getDefaultPreferences().animation
means the user hasn't explicitly chosen an animation; update the logic to track
an explicit flag (e.g., animationExplicit or animationPreferenceSet) in the
preferences/state and only auto-change animation inside the mql.change handler
when that flag is false. Adjust the state type and any getters/setters (get(),
set(), getPrefs()), ensure savePreferences() persists the new explicit flag when
the user manually chooses an animation, and then in the mql.addEventListener
callback check that flag before computing newAnimation, calling
savePreferences(prefs), applyThemeClasses(prefs) and set({ animation:
newAnimation }) so an explicit user choice (including 'status-driven') is never
overridden by OS reduced-motion changes.
| reset: () => { | ||
| const defaults = getDefaultPreferences() | ||
| set({ ...defaults }) | ||
| savePreferences(defaults) | ||
| applyThemeClasses(defaults) | ||
| try { | ||
| localStorage.removeItem(STORAGE_KEY) | ||
| } catch { | ||
| // Ignore | ||
| } | ||
| }, |
There was a problem hiding this comment.
Redundant localStorage removal after savePreferences.
Line 230 calls savePreferences(defaults) which writes to localStorage, then Lines 232-236 immediately try to remove the same key. This is contradictory—either persist the defaults or clear storage, but not both.
🐛 Proposed fix
reset: () => {
const defaults = getDefaultPreferences()
set({ ...defaults })
- savePreferences(defaults)
applyThemeClasses(defaults)
try {
localStorage.removeItem(STORAGE_KEY)
} catch {
// Ignore
}
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| reset: () => { | |
| const defaults = getDefaultPreferences() | |
| set({ ...defaults }) | |
| savePreferences(defaults) | |
| applyThemeClasses(defaults) | |
| try { | |
| localStorage.removeItem(STORAGE_KEY) | |
| } catch { | |
| // Ignore | |
| } | |
| }, | |
| reset: () => { | |
| const defaults = getDefaultPreferences() | |
| set({ ...defaults }) | |
| applyThemeClasses(defaults) | |
| try { | |
| localStorage.removeItem(STORAGE_KEY) | |
| } catch { | |
| // Ignore | |
| } | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/stores/theme.ts` around lines 227 - 237, The reset function currently
calls savePreferences(defaults) to persist defaults and then immediately removes
the same STORAGE_KEY from localStorage, which contradicts persistence; remove
the try/catch block that calls localStorage.removeItem(STORAGE_KEY) so reset
uses getDefaultPreferences(), calls set(...) and savePreferences(defaults), and
then applyThemeClasses(defaults) without clearing STORAGE_KEY (look for the
reset function and the localStorage.removeItem code around savePreferences and
applyThemeClasses to remove).
Replace Settings code editor textarea with CodeMirror 6 for syntax highlighting, line numbers, and bracket matching. Add theme preferences toggle in StatusBar with 5-axis controls (color palette, density, typography, animation, sidebar mode), localStorage persistence, Cmd+K integration, and prefers-reduced-motion detection. Closes #905 Closes #807 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 5 agents, 13 findings addressed: - Add CSS class name safety guard in theme store (security) - Remove dead externalUpdate listener from CodeMirror editor - Add useAnimationPreset hook tests (was untested) - Rewrite theme store tests to exercise loadPreferences directly - Add sidebar CSS class and setter integration tests - Update CLAUDE.md component inventory, deps, stores listing - Update brand-and-ux.md animation hooks table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Gemini Code fixes: - Wrap onSave in try/catch in CodeEditorPanel (silent-failure-hunter) - Add try/finally around isProgrammaticRef to prevent stuck flag (silent-failure-hunter) - Remove dead savePreferences call in theme store reset (frontend/gemini/coderabbit) - Add console.warn to all silent catch blocks in theme store (silent-failure) - Replace format toggle buttons with SegmentedControl in CodeEditorPanel (frontend) - Replace hardcoded pixel sidebar widths with design tokens (coderabbit) - Remove unnecessary useMemo spreads, pass constants directly (frontend/gemini) - Wrap applyThemeClasses init in try/catch for resilience (security) - Truncate raw value in safeClass error message (security) - Fix MAX_EDITOR_BYTES to use TextEncoder byte count (security) - Derive CodeFormat from CodeMirrorEditorProps (type-design) - Export constant arrays with satisfies from theme store (type-design/coderabbit) - Use explicit SegmentedControl generics, remove type assertions (type-design) - Protect serializeEntries/YAML.dump calls in callbacks (silent-failure) Docs/comments: - Add CodeMirrorEditor, SegmentedControl, ThemeToggle to brand-and-ux.md inventory - Fix misleading "saving state changes" comment on readOnly effect - Fix misleading reduced-motion override comment scope - Fix spring field JSDoc, render-phase sync comment, CSS class comment - Add readonly to AnimationPresetConfig fields - Document CodeMirror pixel values and error state absence in stories - Add PopoverOpen story for ThemeToggle Tests (31 new tests): - Create CodeEditorPanel test suite (10 tests including onSave failure) - Add safeClass guard, reducedMotion, save-load round-trip tests to theme store - Add fast-check property tests for theme store, CodeMirror, SegmentedControl, ThemeToggle - Add sidebar mode tests (hidden/rail/compact/persistent/collapsible toggle) - Add StatusBar ThemeToggle presence test - Add animation/sidebar axis tests to ThemeToggle - Add ArrowDown/ArrowUp and skip-disabled keyboard nav tests to SegmentedControl - Add onChange non-invocation test for CodeMirror programmatic sync Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
789dd02 to
f6b044f
Compare
There was a problem hiding this comment.
Actionable comments posted: 11
♻️ Duplicate comments (4)
web/src/components/ui/segmented-control.stories.tsx (1)
22-93: 🛠️ Refactor suggestion | 🟠 MajorRequired shared-component Storybook states are still missing.
This file covers the happy-path variants, but the repo contract for shared UI stories is still incomplete without dedicated
hover,loading,error, andemptycoverage. If any of those states are intentionally unsupported, please document that explicitly in-file instead of omitting them silently.As per coding guidelines:
New shared components in web/src/components/ui/ must have a corresponding .stories.tsx file with all states (default, hover, loading, error, empty).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/segmented-control.stories.tsx` around lines 22 - 93, The Storybook file currently defines Default, SmallSize, MediumSize, Disabled, WithDisabledOption and Interactive (InteractiveControl using SegmentedControl) but is missing the required shared-component states: hover, loading, error, and empty; add Story exports (e.g., Hover, Loading, Error, Empty) that render the SegmentedControl in those states (simulate hover via story args/classes or play function, provide a loading visual/state prop or wrapper, show an error state and an empty-options state) or, if a given state is intentionally unsupported, add an explicit inline comment and a minimal Story export documenting that (e.g., UnsupportedLoading) so the contract for shared UI stories is satisfied and each state is discoverable.web/src/components/ui/code-mirror-editor.tsx (1)
26-31: 🛠️ Refactor suggestion | 🟠 MajorKeep the shared editor theme fully tokenized.
14pxand16px 8pxhardcode font/spacing values back into a shared UI component. Please move those onto existing design tokens instead of baking fixed sizes into the CodeMirror theme.As per coding guidelines:
New shared components in web/src/components/ui/ must use design tokens exclusively (no hardcoded colors, fonts, or spacing).Also applies to: 35-50
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/code-mirror-editor.tsx` around lines 26 - 31, The shared CodeMirror theme contains hardcoded sizes (e.g., "14px" for font-size and "16px 8px" for padding) inside the theme/CSS rules; replace those literals with the project design tokens (for example use the existing CSS custom properties for font sizes and spacing such as --so-font-size-*, --so-space-* or the token names used elsewhere) so the theme remains fully tokenized; update the CodeMirror theme object / CSS-in-JS style block where fontSize and padding are set (the CodeMirror theme definition in this file) to reference the appropriate design-token vars and ensure all other occurrences noted (lines ~35-50) are converted similarly.web/src/components/ui/code-mirror-editor.stories.tsx (1)
36-117: 🛠️ Refactor suggestion | 🟠 MajorStory coverage is still missing the required hover/loading states.
The note at Lines 73-75 is a good explanation for the missing editor-local error state, but this shared-component story set still doesn't export or document
hoverandloading. Please add those states, or explicitly note that they do not exist.As per coding guidelines:
New shared components in web/src/components/ui/ must have a corresponding .stories.tsx file with all states (default, hover, loading, error, empty).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/ui/code-mirror-editor.stories.tsx` around lines 36 - 117, Add explicit Hover and Loading stories for the CodeMirrorEditor story file: either implement stories named Hover and Loading that simulate the states (e.g., render CodeMirrorEditor inside a wrapper with the hover class or a skeleton/loading placeholder) or, if the component truly has no hover/loading API, add Hover and Loading stories that simply set parameters.docs.description.story to a short note stating those states are unsupported; update the story exports (Hover, Loading) alongside Default, YAML, ReadOnly, Empty, Interactive and reference the CodeMirrorEditor component and story names so consumers see the coverage or the explicit note.web/src/stores/theme.ts (1)
173-189:⚠️ Potential issue | 🟠 MajorReduced-motion toggles can overwrite and then strand the user's animation preference.
Line 183 compares
state.animationto the new OS default. If the store defaulted to'minimal'because reduced motion was initially on, turning the OS setting off makes the condition false and the store stays pinned to'minimal'. The same check also can't distinguish an implicit default from an explicit user choice, so toggling the OS setting can clobber a persisted'spring'or'status-driven'selection. Please track explicit animation selection separately (or preserve the last explicit value) before auto-updating here, and add a regression test that coverstrue -> falseplus an explicit non-default choice.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/stores/theme.ts` around lines 173 - 189, The reduced-motion listener can overwrite a user's explicit animation choice; change the theme store to track explicit selection (e.g., add a boolean like animationSelectedExplicitly or a lastExplicitAnimation value alongside reducedMotionDetected) and update the matchMedia 'change' handler to only auto-switch animation when there was no explicit user choice (use get(), getDefaultPreferences(), getPrefs(), savePreferences(), applyThemeClasses(), set() to read/update state) and to restore the last explicit value when the OS toggles off; add a regression test that simulates reduced-motion true -> false with an intervening explicit non-default selection (verify the persisted animation stays or is restored rather than remaining pinned to 'minimal').
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@CLAUDE.md`:
- Line 423: Update the Web dashboard Node baseline text in the CLAUDE.md bullet
that currently reads "**Web dashboard**: Node.js 22+..." to match the
repository's documented frontend baseline of "Web dashboard Node.js 20+" so
contributors get consistent setup guidance; locate the exact string "**Web
dashboard**" in CLAUDE.md and replace "Node.js 22+" with "Node.js 20+", and scan
any other occurrences of "Node.js 22+" in the docs to align them to Node.js 20+
as well.
In `@web/src/__tests__/components/ui/code-mirror-editor.test.tsx`:
- Around line 239-260: The property-based test for CodeMirrorEditor should
exclude the unchanged-value case so it doesn't fail when fast-check generates
"initial"; update the fc.property used in the test "dispatches changes for
arbitrary external values" to filter out or shrink away the string "initial" (or
assert the no-op branch explicitly) so that only values not equal to the
original "initial" are exercised; locate the test block referencing
CodeMirrorEditor, mockDispatch, rerender and change the fc.string generator or
add a precondition to ensure newValue !== "initial" (or add an explicit
expectation that mockDispatch was not called when newValue === "initial").
In `@web/src/__tests__/components/ui/theme-toggle.test.tsx`:
- Around line 107-117: The test currently exercises the store directly
(useThemeStore.getState().setColorPalette) instead of exercising the ThemeToggle
UI; change the property-based test to render the ThemeToggle component
(render(<ThemeToggle/>)), drive it via user interactions (e.g., fireEvent or
userEvent to open the palette menu and select a palette option) using the same
generated palettes
(constantFrom('warm-ops','ice-station','stealth','signal','neon')), and then
assert either that the visible UI reflects the chosen palette or that
useThemeStore.getState().colorPalette was updated as a result; target the
ThemeToggle component and useThemeStore/setColorPalette symbols to locate code
to modify.
- Around line 62-76: The test only asserts two theme axes; update the
ThemeToggle reset test to verify all five axes reset by calling the same UI flow
(render ThemeToggle, open preferences, click "Reset to defaults") then inspect
useThemeStore.getState() and assert that colorPalette, density, typography,
animation, and sidebarMode match their default values as set by the store's
reset() method (the reset() in the theme store is the authoritative source of
defaults); ensure the assertions reference those properties (colorPalette,
density, typography, animation, sidebarMode) so the test covers all axes.
In `@web/src/__tests__/hooks/useAnimationPreset.test.ts`:
- Around line 12-60: Replace the manual preset matrix with a property-based test
using fast-check: import fc and use
fc.assert(fc.property(fc.constantFrom('minimal','spring','instant','status-driven','aggressive'),
(preset) => { useThemeStore.getState().setAnimation(preset); const { result } =
renderHook(() => useAnimationPreset()); /* assertions */ })); inside the
property assert the same invariants (result.current has spring, tween,
staggerDelay, enableLayout; typeof staggerDelay is number; typeof enableLayout
is boolean). Keep separate explicit examples (fc.assert properties or plain
tests) for the well-known behaviors (enableLayout false for 'minimal'/'instant',
true for 'spring', staggerDelay === 0 for 'minimal'/'instant', staggerDelay > 0
for 'aggressive') if you want to preserve those targeted checks, and ensure you
reference useAnimationPreset, useThemeStore.getState().setAnimation, renderHook
and result.current in the property body.
In `@web/src/components/layout/AppLayout.tsx`:
- Around line 64-89: themeCommands is hardcoded instead of deriving options from
the canonical sets exported by the theme store; change AppLayout.tsx to build
themeCommands dynamically by importing the exported option lists from the theme
store (e.g., colorPalettes, densities, typographies, animations, sidebarModes)
and mapping each option to a CommandItem that calls useThemeStore.getState()
setters (setColorPalette, setDensity, setTypography, setAnimation,
setSidebarMode, setPopoverOpen), using a small label/keyword map to supply
human-friendly labels and keywords so future additions to the store are
automatically included.
In `@web/src/components/layout/StatusBar.tsx`:
- Around line 126-128: The ThemeToggle placed inside StatusBar is being clipped
by AppLayout's overflow-hidden when the status row overflows; update the
StatusBar component to either move ThemeToggle out of the clipped metrics row
(render ThemeToggle outside the status metrics container and after the Divider)
or make the status metrics container horizontally scrollable (wrap the metrics
content in a container with overflow-x: auto and white-space: nowrap) so
ThemeToggle remains reachable; adjust the JSX in StatusBar (reference StatusBar,
ThemeToggle, Divider) accordingly and ensure visual alignment/spacing is
preserved.
In `@web/src/components/ui/code-mirror-editor.tsx`:
- Around line 92-101: The editor currently defines syntax colors in
highlightStyle but doesn't surface parse errors; update the CodeMirror
extensions setup (where highlightStyle is used) to include a linter and gutter
for diagnostics: import and add the appropriate linter for the language (e.g.,
jsonParseLinter from `@codemirror/lang-json` or a YAML linter) via linter(...) and
include lintGutter() in the extensions array so parse errors show as diagnostics
in the editor; ensure these new extensions are wired into the same place that
constructs the editor's extensions (the CodeMirror setup that references
highlightStyle).
In `@web/src/components/ui/segmented-control.tsx`:
- Around line 82-109: The JSX for each option inside the options.map in
segmented-control.tsx is too large; extract it into a small helper/component
(e.g., SegmentOption or OptionButton) that accepts props {option, value,
disabled, size, onChange} and encapsulates the
selection/disabled/focus/onClick/aria/tabIndex and className logic currently
computed in the map (selected, optionDisabled, aria-checked, data-value,
tabIndex, onClick, and cn(...) usage). Replace the inline button in options.map
with this new component and pass through onChange and other needed props so the
map body becomes a single simple JSX tag and all logic lives inside the new
SegmentOption component.
In `@web/src/components/ui/theme-toggle.stories.tsx`:
- Around line 24-31: The decorator for the Story "PopoverOpen" mutates the theme
store during render by calling useThemeStore.getState().setPopoverOpen(true);
move this mutation out of the render path—either wrap the mutation in a
useEffect inside the decorator component or remove the decorator and implement a
Story play function that calls useThemeStore.getState().setPopoverOpen(true)
before assertions; update the PopoverOpen export to use the chosen approach
(useEffect inside the decorator component or a play function) so the store
update runs once outside render.
In `@web/src/styles/design-tokens.css`:
- Around line 127-192: Each theme class (.theme-ice-station, .theme-stealth,
.theme-signal, .theme-neon) needs to override the --so-overlay-active CSS
variable (currently inherited from :root) so active/pressed UI states match the
theme accent instead of Warm Ops cyan; update each theme block to set
--so-overlay-active to an RGBA that corresponds to that theme's accent color
(use the same hue as --so-accent with an appropriate alpha consistent with other
overlay vars like --so-overlay-selection-focused).
---
Duplicate comments:
In `@web/src/components/ui/code-mirror-editor.stories.tsx`:
- Around line 36-117: Add explicit Hover and Loading stories for the
CodeMirrorEditor story file: either implement stories named Hover and Loading
that simulate the states (e.g., render CodeMirrorEditor inside a wrapper with
the hover class or a skeleton/loading placeholder) or, if the component truly
has no hover/loading API, add Hover and Loading stories that simply set
parameters.docs.description.story to a short note stating those states are
unsupported; update the story exports (Hover, Loading) alongside Default, YAML,
ReadOnly, Empty, Interactive and reference the CodeMirrorEditor component and
story names so consumers see the coverage or the explicit note.
In `@web/src/components/ui/code-mirror-editor.tsx`:
- Around line 26-31: The shared CodeMirror theme contains hardcoded sizes (e.g.,
"14px" for font-size and "16px 8px" for padding) inside the theme/CSS rules;
replace those literals with the project design tokens (for example use the
existing CSS custom properties for font sizes and spacing such as
--so-font-size-*, --so-space-* or the token names used elsewhere) so the theme
remains fully tokenized; update the CodeMirror theme object / CSS-in-JS style
block where fontSize and padding are set (the CodeMirror theme definition in
this file) to reference the appropriate design-token vars and ensure all other
occurrences noted (lines ~35-50) are converted similarly.
In `@web/src/components/ui/segmented-control.stories.tsx`:
- Around line 22-93: The Storybook file currently defines Default, SmallSize,
MediumSize, Disabled, WithDisabledOption and Interactive (InteractiveControl
using SegmentedControl) but is missing the required shared-component states:
hover, loading, error, and empty; add Story exports (e.g., Hover, Loading,
Error, Empty) that render the SegmentedControl in those states (simulate hover
via story args/classes or play function, provide a loading visual/state prop or
wrapper, show an error state and an empty-options state) or, if a given state is
intentionally unsupported, add an explicit inline comment and a minimal Story
export documenting that (e.g., UnsupportedLoading) so the contract for shared UI
stories is satisfied and each state is discoverable.
In `@web/src/stores/theme.ts`:
- Around line 173-189: The reduced-motion listener can overwrite a user's
explicit animation choice; change the theme store to track explicit selection
(e.g., add a boolean like animationSelectedExplicitly or a lastExplicitAnimation
value alongside reducedMotionDetected) and update the matchMedia 'change'
handler to only auto-switch animation when there was no explicit user choice
(use get(), getDefaultPreferences(), getPrefs(), savePreferences(),
applyThemeClasses(), set() to read/update state) and to restore the last
explicit value when the OS toggles off; add a regression test that simulates
reduced-motion true -> false with an intervening explicit non-default selection
(verify the persisted animation stays or is restored rather than remaining
pinned to 'minimal').
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 072708da-05e1-487a-ac99-143f8dd742bd
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (26)
CLAUDE.mddocs/design/brand-and-ux.mdweb/package.jsonweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/__tests__/components/layout/StatusBar.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/__tests__/stores/theme.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/theme-toggle.tsxweb/src/hooks/useAnimationPreset.tsweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/stores/theme.tsweb/src/styles/design-tokens.cssweb/src/styles/global.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always reuse existing components from
web/src/components/ui/before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside.map()blocks, or usergba()with hardcoded values instead of design token variables.
Files:
web/src/__tests__/components/layout/StatusBar.test.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/ui/theme-toggle.tsxweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/__tests__/stores/theme.test.tsweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use design token rules: colors via Tailwind semantic classes (
text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-accent)), typography viafont-sansorfont-mono, spacing via density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind, shadows/borders via token variables (var(--so-shadow-card-hover),border-border,border-bright). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Files:
web/src/__tests__/components/layout/StatusBar.test.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/ui/theme-toggle.tsxweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/__tests__/stores/theme.test.tsweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use property-based testing with fast-check (
fc.assert+fc.property). Run tests withnpm --prefix web run testfor Vitest with--detect-async-leaksflag.
Files:
web/src/__tests__/components/layout/StatusBar.test.tsxweb/src/components/layout/StatusBar.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/ui/theme-toggle.tsxweb/src/hooks/useAnimationPreset.tsweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/pages/settings/CodeEditorPanel.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/__tests__/stores/theme.test.tsweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
web/src/components/ui/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
New shared components in
web/src/components/ui/must have a corresponding.stories.tsxfile with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and usecn()from@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/theme-toggle.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsx
**/*.{md,mdx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always read the relevant
docs/design/page before implementing any feature or planning any issue. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from spec, alert the user and explain why -- user decides whether to proceed or update the spec. Never silently diverge. When approved deviations occur, update the relevantdocs/design/page to reflect the new reality.
Files:
docs/design/brand-and-ux.mdCLAUDE.md
docs/design/*.md
📄 CodeRabbit inference engine (CLAUDE.md)
Design spec pages are mandatory reading before implementing features. Design specs pages are the starting point for architecture, data models, and behavior. When implementation deviates from spec, alert the user with reasoning. Update the relevant design page when approved deviations occur.
Files:
docs/design/brand-and-ux.md
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsxweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxdocs/design/brand-and-ux.mdweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxCLAUDE.mdweb/src/styles/design-tokens.cssweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/pages/settings/CodeEditorPanel.stories.tsxweb/src/__tests__/stores/theme.test.tsweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/stores/theme.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/components/layout/StatusBar.test.tsxweb/src/__tests__/components/layout/Sidebar.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/pages/settings/CodeEditorPanel.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxCLAUDE.mdweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/stores/theme.test.ts
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Applied to files:
web/src/styles/global.cssdocs/design/brand-and-ux.mdweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxCLAUDE.mdweb/src/styles/design-tokens.cssweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/stores/theme.ts
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)
Applied to files:
web/src/styles/global.cssweb/package.jsonCLAUDE.md
📚 Learning: 2026-03-28T18:10:43.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
Applied to files:
web/src/components/layout/StatusBar.tsxweb/src/components/ui/theme-toggle.stories.tsxdocs/design/brand-and-ux.mdweb/src/components/layout/Sidebar.tsxweb/src/components/ui/theme-toggle.tsxweb/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxCLAUDE.mdweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.stories.tsx
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).
Applied to files:
web/package.jsonCLAUDE.md
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/**/*.{ts,tsx,js,jsx} : Use property-based testing with fast-check (`fc.assert` + `fc.property`). Run tests with `npm --prefix web run test` for Vitest with `--detect-async-leaks` flag.
Applied to files:
web/src/__tests__/components/ui/segmented-control.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/stores/theme.test.ts
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.
Applied to files:
CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).
Applied to files:
CLAUDE.md
🔇 Additional comments (14)
docs/design/brand-and-ux.md (2)
230-232: Documentation accurately reflects new component implementations.The component inventory entries correctly document the props interfaces and purposes for
CodeMirrorEditor,SegmentedControl, andThemeToggle, matching their actual implementations.
268-268: Hook documentation is accurate.The
useAnimationPreset()entry correctly describes its role in deriving animation config from user theme preferences.web/src/__tests__/components/layout/Sidebar.test.tsx (1)
141-190: Comprehensive test coverage for all sidebar modes.The new
sidebarModetest block correctly verifies behavior for all five modes:
hidden: returns null (no navigation rendered)rail/compact: always collapsed, no togglepersistent: always expanded, no togglecollapsible: shows collapse toggleThe assertions match the expected behavior from
Sidebar.tsx.web/src/components/layout/Sidebar.tsx (2)
60-68: Clean mode-based collapsed state logic.The computed
collapsedvalue correctly handles all sidebar modes:
rail/compact: always true (collapsed)persistent: always false (expanded)collapsible: useslocalCollapsedstateThe
showCollapseToggleflag ensures the toggle button only appears in collapsible mode.
83-86: Design token usage for sidebar widths is correct.The sidebar width now uses CSS custom properties (
--so-sidebar-compact,--so-sidebar-collapsed,--so-sidebar-expanded) which aligns with the design token system and resolves the previously flagged hardcoded pixel issue.web/src/pages/settings/CodeEditorPanel.tsx (3)
96-99: Correct UTF-8 byte length validation.Using
TextEncoder().encode(text).lengthproperly measures byte length rather than character count, which is important for accurate size limiting when dealing with multi-byte characters.
210-216: Good error handling for async save failures.Wrapping
onSavein try/catch ensures unexpected exceptions are caught and displayed to the user viasetParseError, preventing silent failures.
237-251: Correct usage of shared UI components.The replacement of textarea with
CodeMirrorEditorand buttons withSegmentedControlproperly reuses existing shared components fromweb/src/components/ui/, following the component reuse guidelines.web/src/__tests__/pages/settings/CodeEditorPanel.test.tsx (2)
64-71: Clever workaround for textarea value updates.Using
Object.getOwnPropertyDescriptorto get the native setter andfireEvent.inputbypasses issues withuserEvent.typeand large text values in React's controlled inputs. This is a pragmatic solution for test scenarios.
73-199: Comprehensive test coverage for CodeEditorPanel.The test suite covers key scenarios:
- Format selector and button rendering
- Serialization/parsing behavior
- Dirty state management
- Error handling (invalid JSON, removed keys, save failures)
- Reset functionality
- Save invocation with correct change map
web/src/components/ui/theme-toggle.tsx (3)
1-19: Options are now derived from store constants - good improvement.The component correctly imports axis constants (
COLOR_PALETTES,DENSITIES, etc.) and types from the store, then derives option arrays from them. This addresses previous feedback about centralizing option sources to avoid drift.
129-129: Type casts are necessary due to SelectField's string onChange signature.The
as ColorPaletteandas Typographycasts are required becauseSelectField.onChangereturns a genericstring(perweb/src/components/ui/select-field.tsx:64-81). This is acceptable given the options are constrained to valid values.Also applies to: 148-148
76-195: Well-structured component using design tokens and shared UI components.The component correctly:
- Uses
cn()for class merging- Uses semantic Tailwind classes (
text-foreground,bg-surface,text-warning, etc.)- Reuses
SelectField,SegmentedControl, andButtonfrom shared components- Exports
ThemeTogglePropsinterfaceAs per coding guidelines: "New shared components in
web/src/components/ui/must have... design tokens exclusively (no hardcoded colors, fonts, or spacing), and usecn()from@/lib/utilsfor conditional class merging."web/src/__tests__/stores/theme.test.ts (1)
342-355: Good fast-check coverage forloadPreferences.This gives the validation path much broader input coverage without bloating the example-based cases. As per coding guidelines: "Use property-based testing with fast-check (
fc.assert+fc.property)."
| - **Required**: `mem0ai` (Mem0 memory backend -- the default and currently only backend), `cryptography` (Fernet encryption for sensitive settings at rest), `faker` (multi-locale agent name generation for templates and setup wizard) | ||
| - **Install**: `uv sync` installs everything (dev group is default) | ||
| - **Web dashboard**: Node.js 22+, TypeScript 6.0+, dependencies in `web/package.json` (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios, Lucide React, @fontsource-variable/geist, @fontsource-variable/geist-mono, Storybook 10, Vitest, @vitest/coverage-v8, @testing-library/react, fast-check, ESLint, @eslint-react/eslint-plugin, eslint-plugin-security) | ||
| - **Web dashboard**: Node.js 22+, TypeScript 6.0+, dependencies in `web/package.json` (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, @tanstack/react-query, @xyflow/react, @dagrejs/dagre, @dnd-kit, Recharts, Framer Motion, cmdk, js-yaml, Axios, Lucide React, @fontsource-variable/geist, @fontsource-variable/geist-mono, @fontsource-variable/jetbrains-mono, @fontsource-variable/inter, @fontsource/ibm-plex-mono, @fontsource/ibm-plex-sans, CodeMirror 6, Storybook 10, Vitest, @vitest/coverage-v8, @testing-library/react, fast-check, ESLint, @eslint-react/eslint-plugin, eslint-plugin-security) |
There was a problem hiding this comment.
Keep the documented Node baseline consistent.
This now says the web dashboard requires Node.js 22+, but the repo context still documents the frontend baseline as Node.js 20+. Please align these setup requirements so contributors do not get conflicting guidance.
Based on learnings, Web dashboard Node.js 20+ is the documented frontend baseline.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CLAUDE.md` at line 423, Update the Web dashboard Node baseline text in the
CLAUDE.md bullet that currently reads "**Web dashboard**: Node.js 22+..." to
match the repository's documented frontend baseline of "Web dashboard Node.js
20+" so contributors get consistent setup guidance; locate the exact string
"**Web dashboard**" in CLAUDE.md and replace "Node.js 22+" with "Node.js 20+",
and scan any other occurrences of "Node.js 22+" in the docs to align them to
Node.js 20+ as well.
| it('synchronizes random color palette changes', () => { | ||
| fc.assert( | ||
| fc.property( | ||
| fc.constantFrom('warm-ops', 'ice-station', 'stealth', 'signal', 'neon'), | ||
| (palette) => { | ||
| useThemeStore.getState().setColorPalette(palette) | ||
| expect(useThemeStore.getState().colorPalette).toBe(palette) | ||
| }, | ||
| ), | ||
| ) | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Property-based test only exercises store, not UI interaction.
The fast-check test calls setColorPalette directly on the store rather than testing through UI interaction. While this validates store behavior, a UI-focused property test would provide more confidence in the ThemeToggle component specifically.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/__tests__/components/ui/theme-toggle.test.tsx` around lines 107 -
117, The test currently exercises the store directly
(useThemeStore.getState().setColorPalette) instead of exercising the ThemeToggle
UI; change the property-based test to render the ThemeToggle component
(render(<ThemeToggle/>)), drive it via user interactions (e.g., fireEvent or
userEvent to open the palette menu and select a palette option) using the same
generated palettes
(constantFrom('warm-ops','ice-station','stealth','signal','neon')), and then
assert either that the visible UI reflects the chosen palette or that
useThemeStore.getState().colorPalette was updated as a result; target the
ThemeToggle component and useThemeStore/setColorPalette symbols to locate code
to modify.
| <Divider /> | ||
|
|
||
| <ThemeToggle /> |
There was a problem hiding this comment.
Keep the theme toggle reachable when the status bar overflows.
Because web/src/components/layout/AppLayout.tsx Line 95 wraps the shell in overflow-hidden, this extra trailing control is the first thing clipped once the status row no longer fits. On narrower windows or higher zoom levels, the only theme entry point can disappear entirely. Pin it outside the clipped metrics row or make the status content horizontally scrollable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/layout/StatusBar.tsx` around lines 126 - 128, The
ThemeToggle placed inside StatusBar is being clipped by AppLayout's
overflow-hidden when the status row overflows; update the StatusBar component to
either move ThemeToggle out of the clipped metrics row (render ThemeToggle
outside the status metrics container and after the Divider) or make the status
metrics container horizontally scrollable (wrap the metrics content in a
container with overflow-x: auto and white-space: nowrap) so ThemeToggle remains
reachable; adjust the JSX in StatusBar (reference StatusBar, ThemeToggle,
Divider) accordingly and ensure visual alignment/spacing is preserved.
| const highlightStyle = HighlightStyle.define([ | ||
| { tag: tags.propertyName, color: 'var(--so-accent)' }, | ||
| { tag: tags.keyword, color: 'var(--so-accent)' }, | ||
| { tag: tags.string, color: 'var(--so-success)' }, | ||
| { tag: tags.number, color: 'var(--so-warning)' }, | ||
| { tag: tags.bool, color: 'var(--so-accent-dim)' }, | ||
| { tag: tags.null, color: 'var(--so-text-muted)' }, | ||
| { tag: tags.punctuation, color: 'var(--so-text-secondary)' }, | ||
| { tag: tags.comment, color: 'var(--so-text-muted)', fontStyle: 'italic' }, | ||
| ]) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In CodeMirror 6, if an editor uses json()/yaml()with a customHighlightStyle, are syntax errors visibly highlighted by default, or must invalid syntax / diagnostics be configured explicitly (for example via tags.invalid styling or a lint extension)?
💡 Result:
In CodeMirror 6, when using json or yaml with a custom HighlightStyle, syntax errors are not visibly highlighted by default. Syntax highlighting via HighlightStyle only styles valid parse tree nodes with semantic tags. Parse errors result in unstyled or incomplete tree coverage, but do not automatically receive special styling like tags.invalid without explicit configuration. To highlight invalid syntax, diagnostics must be configured explicitly, typically via a lint extension using linter(jsonParseLinter) for JSON (which produces Diagnostic objects shown in lint gutter/tooltips) or equivalent for YAML, combined with lintGutter. Custom HighlightStyle can style tags.invalid if the parser produces it, but standard json and yaml parsers do not tag errors this way—errors are handled via separate linter functions.
Citations:
- 1: https://registry.npmjs.org/%40codemirror%2Flang-json
- 2: https://github.com/codemirror/lang-json
- 3: https://registry.npmjs.org/%40codemirror%2Flang-yaml
- 4: https://www.npmjs.com/package/codemirror-json-schema
- 5: https://codemirror.net/examples/lint
🏁 Script executed:
# Check the actual code file structure and imports
head -60 web/src/components/ui/code-mirror-editor.tsx | cat -nRepository: Aureliolo/synthorg
Length of output: 2767
🏁 Script executed:
# Look for linter-related imports and configuration
rg -i "linter|diagnostic|invalid" web/src/components/ui/code-mirror-editor.tsxRepository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
# Show lines 85-110 to see the highlightStyle and surrounding context
sed -n '85,110p' web/src/components/ui/code-mirror-editor.tsx | cat -nRepository: Aureliolo/synthorg
Length of output: 1248
🏁 Script executed:
# Show lines 140-165 to see the second referenced section
sed -n '140,165p' web/src/components/ui/code-mirror-editor.tsx | cat -nRepository: Aureliolo/synthorg
Length of output: 985
Add syntax error highlighting via linter extension.
The editor wires up syntax highlighting for valid tokens but doesn't mark invalid syntax (broken JSON/YAML). CodeMirror 6 requires an explicit linter extension—standard json() and yaml() parsers don't tag errors without a separate diagnostics setup. To highlight syntax errors, add a linter extension (e.g., linter(jsonParseLinter) from @codemirror/lang-json) and lintGutter() to the extensions array.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/components/ui/code-mirror-editor.tsx` around lines 92 - 101, The
editor currently defines syntax colors in highlightStyle but doesn't surface
parse errors; update the CodeMirror extensions setup (where highlightStyle is
used) to include a linter and gutter for diagnostics: import and add the
appropriate linter for the language (e.g., jsonParseLinter from
`@codemirror/lang-json` or a YAML linter) via linter(...) and include lintGutter()
in the extensions array so parse errors show as diagnostics in the editor;
ensure these new extensions are wired into the same place that constructs the
editor's extensions (the CodeMirror setup that references highlightStyle).
Code: - Derive themeCommands dynamically from exported store constants (AppLayout) - Extract SegmentOption component from .map() body (>8-line JSX rule) - Add --so-overlay-active per theme for accent-matched active states - Tokenize CodeMirror font-size/padding with --so-text-body-sm, --so-space-* - Fix PopoverOpen story render-time mutation (use useEffect decorator) Tests: - Filter "initial" from CodeMirror fc.property to avoid no-op false failure - Assert all 5 axes in ThemeToggle reset test - Add fast-check property test for useAnimationPreset - Fix ThemeToggle property test (store-level, UI paths covered by axis tests) Docs: - Document non-applicable Storybook states (hover/loading/error) for CodeMirrorEditor and SegmentedControl Skipped (not valid): - Node.js 22+ -> 20+: project baseline IS 22+, no 20+ reference exists - StatusBar clipping: Radix Popover uses Portal, not clipped by overflow - CodeMirror linter: out of scope per #905 (no IDE-like features) - Reduced-motion explicit tracking: intentional accessibility override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/code-mirror-editor.test.tsx`:
- Around line 245-261: The test mounts CodeMirrorEditor with render(...) then
calls unmount() at the end but can leak if an assertion fails; wrap the
render/rerender/assert/unmount sequence in a try/finally so unmount() is always
called. Locate the block using render(... CodeMirrorEditor ...),
rerender(<CodeMirrorEditor ...>), expect(mockDispatch)... and ensure the
unmount() call is moved into a finally that executes regardless of test
failures.
- Around line 11-33: The mocked editor shares a single global mockDocString and
uses a fixed doc.length snapshot, causing state leakage between instances;
update MockEditorView to keep per-instance document state (e.g., an instance
property instead of the module-level mockDocString) and make state.doc.toString
and state.doc.length read from that instance state so dispatch updates mutate
only that instance's document (refer to mockDocString, MockEditorView,
state.doc.toString, state.doc.length, and the dispatch mock implementation to
locate and change the behavior).
In `@web/src/__tests__/hooks/useAnimationPreset.test.ts`:
- Line 13: The tests duplicate the AnimationPreset literals; consolidate them by
keeping the single const presets: AnimationPreset[] = [...] declaration and
replace the second occurrence with fc.constantFrom(...presets) so both
property-based tests reuse the same source of truth; update any test that
currently re-declares the array (look for the second list around the other test)
to spread the existing presets variable into constantFrom.
In `@web/src/components/ui/code-mirror-editor.stories.tsx`:
- Around line 92-106: Replace the inline template literal className usages on
the JSON and YAML buttons with the project's cn() helper: import cn from
'@/lib/utils' and call cn to compose the shared classes ('rounded px-2 py-1
text-xs font-medium') plus the conditional classes based on lang (e.g., lang ===
'json' or lang === 'yaml' to add 'bg-accent/10 text-accent' else
'text-text-muted'); update the onClick handlers remain using setLang and
preserve the lang variable checks when converting both button className usages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a527babe-2bfd-42cf-b944-b9a6a3f3d3c0
📒 Files selected for processing (10)
web/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/styles/design-tokens.css
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Dashboard Test
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Build Sandbox
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always reuse existing components from
web/src/components/ui/before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside.map()blocks, or usergba()with hardcoded values instead of design token variables.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use design token rules: colors via Tailwind semantic classes (
text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-accent)), typography viafont-sansorfont-mono, spacing via density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind, shadows/borders via token variables (var(--so-shadow-card-hover),border-border,border-bright). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use property-based testing with fast-check (
fc.assert+fc.property). Run tests withnpm --prefix web run testfor Vitest with--detect-async-leaksflag.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
web/src/components/ui/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
New shared components in
web/src/components/ui/must have a corresponding.stories.tsxfile with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and usecn()from@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/**/*.{ts,tsx,js,jsx} : Use property-based testing with fast-check (`fc.assert` + `fc.property`). Run tests with `npm --prefix web run test` for Vitest with `--detect-async-leaks` flag.
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/components/layout/AppLayout.tsxweb/src/components/ui/theme-toggle.stories.tsxweb/src/components/ui/segmented-control.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/styles/design-tokens.cssweb/src/components/ui/code-mirror-editor.stories.tsxweb/src/components/ui/segmented-control.stories.tsxweb/src/components/ui/code-mirror-editor.tsxweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Applied to files:
web/src/components/layout/AppLayout.tsxweb/src/__tests__/components/ui/theme-toggle.test.tsxweb/src/styles/design-tokens.cssweb/src/components/ui/code-mirror-editor.tsx
🔇 Additional comments (17)
web/src/__tests__/hooks/useAnimationPreset.test.ts (1)
27-41: Good property-based coverage for preset invariants.Nice use of
fc.assert+fc.propertyto validate config shape/types across the preset domain.As per coding guidelines,
web/**/*.{ts,tsx,js,jsx}: Use property-based testing with fast-check (fc.assert + fc.property).web/src/components/ui/code-mirror-editor.tsx (4)
91-100: Syntax error highlighting scope clarification.A previous review flagged that CodeMirror 6 requires an explicit linter extension to highlight invalid JSON/YAML syntax. However, based on the PR objectives which state "Keep scope minimal (no folding/minimap/IDE extras)" and commit messages mentioning "Clarified scope (no CodeMirror linter / IDE features)", this appears to be an intentional scope decision.
If syntax error highlighting is desired in the future, you would need to add
linter(jsonParseLinter)from@codemirror/lang-jsonandlintGutter()to the extensions array.
32-85: Design tokens now correctly used throughout the theme.This addresses the previous review feedback. The theme now uses
var(--so-text-body-sm)for font size andvar(--so-space-4) var(--so-space-2)for padding instead of hardcoded pixel values.
114-174: Well-structured editor initialization with proper cleanup.The component correctly:
- Uses
Compartmentfor dynamic language and readOnly reconfiguration- Guards
onChangecalls withisProgrammaticRefto prevent feedback loops during external value sync- Cleans up by calling
view.destroy()on unmount- Uses ref-based callback storage to avoid extension recreation
The
eslint-disablecomment at line 173 is appropriate since the effect intentionally runs only on mount.
218-231: Proper accessibility attributes and class merging.The container correctly includes:
role="textbox"witharia-multilinearia-labelandaria-readonlyforwardingcn()for conditional class merging per guidelinesweb/src/components/ui/theme-toggle.stories.tsx (1)
1-40: LGTM!The stories file correctly addresses previous review feedback:
PopoverOpenDecoratorproperly wraps the store mutation inuseEffectto avoid render-time side effects- Both
DefaultandPopoverOpenstates are coveredThe hover, loading, error, and empty states are reasonably not applicable for this toggle control component (hover is CSS-handled, and the others don't apply to a simple trigger button).
web/src/components/layout/AppLayout.tsx (1)
76-119: LGTM! Commands now derived from exported store constants.This correctly addresses the previous review feedback. The
themeCommandsare now built dynamically by mapping over the canonicalCOLOR_PALETTES,DENSITIES,TYPOGRAPHIES,ANIMATION_PRESETS, andSIDEBAR_MODESarrays, ensuring future additions to the theme store are automatically reflected in the command palette.web/src/components/ui/segmented-control.tsx (2)
26-63: LGTM! SegmentOption correctly extracted.This addresses the previous review feedback about complex JSX in
.map()blocks. TheSegmentOptionhelper component cleanly encapsulates selection, disabled, focus, and styling logic while keeping the map body simple.
65-134: Well-implemented accessible segmented control.The component demonstrates solid accessibility patterns:
- Proper
radiogroup/radioARIA roles- Keyboard navigation with arrow keys (including wrap-around and disabled-option skipping)
fieldset/legendstructure with visually hidden label- Correct
tabIndexmanagement (only selected option is focusable)Design token compliance is correct with semantic Tailwind classes (
text-accent,border-border,bg-background).web/src/styles/design-tokens.css (2)
127-196: LGTM! Palette classes now include complete overlay token overrides.All four non-default palettes (Ice Station, Stealth, Signal, Neon) now correctly override
--so-overlay-activealongside the other overlay tokens, addressing the previous review feedback about inconsistent active/pressed state theming.
65-68: Sidebar layout tokens correctly integrated.These tokens are properly consumed by
Sidebar.tsx(per the provided context snippet at lines 51-87), which appliesw-[var(--so-sidebar-compact)],w-[var(--so-sidebar-collapsed)], orw-[var(--so-sidebar-expanded)]based on sidebar mode.web/src/__tests__/components/ui/theme-toggle.test.tsx (2)
62-82: Reset test now comprehensively covers all 5 theme axes.This addresses the previous review feedback. The test correctly:
- Mutates all 5 axes before reset
- Asserts each axis returns to defaults
- Uses
toContain(['minimal', 'status-driven'])for animation to handle OS reduced-motion detection
113-126: Property test approach is reasonable.The comment at lines 113-115 clearly explains the rationale: UI-level tests above already verify the wiring between controls and store setters for all 5 axes, so the property test focuses on confirming the store contract accepts all valid palette values. This is a pragmatic balance between coverage and test complexity.
web/src/components/ui/segmented-control.stories.tsx (2)
75-79: Good documentation of non-applicable states.The comment clearly explains why hover, loading, error, and empty states don't apply to this component:
- Hover is CSS-only
- Loading/error aren't part of the API (stateless input control)
- Empty options would create an invalid radiogroup
This satisfies the guideline requirement by explicitly documenting the reasoning rather than silently omitting states.
1-99: LGTM!The stories file provides comprehensive coverage for
SegmentedControlwith appropriate variants (Default, SmallSize, MediumSize, Disabled, WithDisabledOption, Interactive) and clear documentation of why certain standard states don't apply.web/src/components/ui/code-mirror-editor.stories.tsx (1)
73-77: Good documentation of non-applicable states.The comment clearly explains why hover, loading, and error states don't apply to this editor component, and that parse errors are displayed externally by the parent component. This satisfies the guideline requirement.
web/src/__tests__/components/ui/code-mirror-editor.test.tsx (1)
238-264: Nice addition of property-based coverage for value-sync behavior.This improves confidence beyond single deterministic cases and aligns with the test policy.
As per coding guidelines:
web/**/*.{ts,tsx,js,jsx}: Use property-based testing with fast-check (fc.assert + fc.property).
- Per-instance document state in MockEditorView (no shared mutable global) - try/finally around render/unmount in fc.property to prevent DOM leaks - DRY presets in useAnimationPreset test via fc.constantFrom(...presets) - Replace template literal className with cn() in CodeMirror Interactive story Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/__tests__/components/ui/code-mirror-editor.test.tsx`:
- Around line 172-177: The value-sync assertions in the CodeMirror editor test
currently only assert the change object's from and insert fields (via
mockDispatch in the test for CodeMirrorEditor), which allows regressions that
stop replacing the full document; update both assertions that assert
replacements (the ones using expect.objectContaining({ changes:
expect.objectContaining({...}) }) ) to also assert the to field equals the
document length being replaced (e.g., the full-document end index) so the test
ensures a full replacement rather than a partial insert.
- Around line 32-36: The test's mockDispatch implementation only updates
this._docString and never invokes any registered update listeners, so the
onChange/updateListener path isn't exercised and the assertion can be a false
positive; update the mockDispatch mockImplementation (the mockDispatch used to
replace dispatch in the test) to, after applying the insert to this._docString,
also call any stored updateListener callbacks (or simulate the
EditorView/EditorState update event) so that the component's onChange handler is
executed; look for references to mockDispatch, dispatch, updateListener,
onChange and this._docString in the test and ensure the mock triggers the same
callback(s) the real dispatch would invoke.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3056c829-20ad-4d11-ab67-a0dcf715adc0
📒 Files selected for processing (3)
web/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/components/ui/code-mirror-editor.stories.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Sandbox
- GitHub Check: Build Backend
- GitHub Check: Build Web
- GitHub Check: Dashboard Test
- GitHub Check: Dependency Review
- GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always reuse existing components from
web/src/components/ui/before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside.map()blocks, or usergba()with hardcoded values instead of design token variables.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsx
web/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use design token rules: colors via Tailwind semantic classes (
text-foreground,bg-card,text-accent,text-success,bg-danger) or CSS variables (var(--so-accent)), typography viafont-sansorfont-mono, spacing via density-aware tokens (p-card,gap-section-gap,gap-grid-gap) or standard Tailwind, shadows/borders via token variables (var(--so-shadow-card-hover),border-border,border-bright). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsx
web/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use property-based testing with fast-check (
fc.assert+fc.property). Run tests withnpm --prefix web run testfor Vitest with--detect-async-leaksflag.
Files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsx
web/src/components/ui/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
New shared components in
web/src/components/ui/must have a corresponding.stories.tsxfile with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and usecn()from@/lib/utilsfor conditional class merging.
Files:
web/src/components/ui/code-mirror-editor.stories.tsx
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/**/*.{ts,tsx,js,jsx} : Use property-based testing with fast-check (`fc.assert` + `fc.property`). Run tests with `npm --prefix web run test` for Vitest with `--detect-async-leaks` flag.
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsx
📚 Learning: 2026-03-28T18:10:43.542Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.542Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. Do not recreate status dots inline, build card-with-header layouts from scratch, create metric displays manually, render initials circles manually, create complex (>8 line) JSX inside `.map()` blocks, or use `rgba()` with hardcoded values instead of design token variables.
Applied to files:
web/src/__tests__/hooks/useAnimationPreset.test.tsweb/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/components/ui/*.{ts,tsx} : New shared components in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty), exported props as a TypeScript interface, design tokens exclusively (no hardcoded colors, fonts, or spacing), and use `cn()` from `@/lib/utils` for conditional class merging.
Applied to files:
web/src/__tests__/components/ui/code-mirror-editor.test.tsxweb/src/components/ui/code-mirror-editor.stories.tsx
📚 Learning: 2026-03-28T18:10:43.543Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T18:10:43.543Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design token rules: colors via Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-accent)`), typography via `font-sans` or `font-mono`, spacing via density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind, shadows/borders via token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`). Never hardcode hex values, set fontFamily directly, or hardcode pixel values for layout spacing.
Applied to files:
web/src/components/ui/code-mirror-editor.stories.tsx
🔇 Additional comments (4)
web/src/__tests__/hooks/useAnimationPreset.test.ts (3)
1-13: LGTM! Clean setup with proper test isolation.The single
presetsconstant at line 13 now serves as the source of truth for bothit.eachand thefast-checkproperty test, addressing the previous DRY concern. ThebeforeEachreset ensures proper test isolation between iterations.
27-41: Good use of property-based testing per coding guidelines.The
fc.constantFrom(...presets)at line 30 correctly spreads the sharedpresetsarray, maintaining DRY principles while satisfying the property-based testing requirement. As per coding guidelines,web/**/*.{ts,tsx,js,jsx}: Use property-based testing with fast-check (fc.assert + fc.property).
43-77: Assertions correctly match implementation values.The specific behavior tests align with the
PRESET_CONFIGSinuseAnimationPreset.ts:minimalandinstanthaveenableLayout: falseandstaggerDelay: 0,springhasenableLayout: true, andaggressivehasstaggerDelay: 0.05(satisfies> 0). Good coverage of the key behavioral distinctions between presets.web/src/components/ui/code-mirror-editor.stories.tsx (1)
3-4: Solid Storybook implementation and token-safe interactive controls.Good use of
cn()for conditional class composition and clean interactive coverage without introducing hardcoded design values.Based on learnings: "New shared components in
web/src/components/ui/must have a corresponding.stories.tsxfile ... and usecn()from@/lib/utilsfor conditional class merging."Also applies to: 80-126
…t `to` field - mockDispatch now invokes the captured updateListener callback after updating _docString, matching real CodeMirror dispatch behavior - "does not call onChange during programmatic value sync" test now properly exercises the isProgrammaticRef guard (was a false positive) - All value-sync assertions now include `to: docLength` to catch regressions that change full-replacement to partial-insert Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- #MAJOR CHANGES; We got a somewhat working webui :) ## [0.5.0](v0.4.9...v0.5.0) (2026-03-30) ### Features * add analytics trends and budget forecast API endpoints ([#798](#798)) ([16b61f5](16b61f5)) * add department policies to default templates ([#852](#852)) ([7a41548](7a41548)) * add remaining activity event types (task_started, tool_used, delegation, cost_incurred) ([#832](#832)) ([4252fac](4252fac)) * agent performance, activity, and history API endpoints ([#811](#811)) ([9b75c1d](9b75c1d)) * Agent Profiles and Detail pages (biography, career, performance) ([#874](#874)) ([62d7880](62d7880)) * app shell, Storybook, and CI/CD pipeline ([#819](#819)) ([d4dde90](d4dde90)) * Approvals page with risk grouping, urgency indicators, batch actions ([#889](#889)) ([4e9673d](4e9673d)) * Budget Panel page (P&L dashboard, breakdown charts, forecast) ([#890](#890)) ([b63b0f1](b63b0f1)) * build infrastructure layer (API client, auth, WebSocket) ([#815](#815)) ([9f01d3e](9f01d3e)) * CLI global options infrastructure, UI modes, exit codes, env vars ([#891](#891)) ([fef4fc5](fef4fc5)) * CodeMirror editor and theme preferences toggle ([#905](#905), [#807](#807)) ([#909](#909)) ([41fbedc](41fbedc)) * Company page (department/agent management) ([#888](#888)) ([cfb88b0](cfb88b0)) * comprehensive hint coverage across all CLI commands ([#900](#900)) ([937974e](937974e)) * config system extensions, per-command flags for init/start/stop/status/logs ([#895](#895)) ([32f83fe](32f83fe)) * configurable currency system replacing hardcoded USD ([#854](#854)) ([b372551](b372551)) * Dashboard page (metric cards, activity feed, budget burn) ([#861](#861)) ([7d519d5](7d519d5)) * department health, provider status, and activity feed endpoints ([#818](#818)) ([6d5f196](6d5f196)) * design tokens and core UI components ([#833](#833)) ([ed887f2](ed887f2)) * extend approval, meeting, and budget API responses ([#834](#834)) ([31472bf](31472bf)) * frontend polish -- real-time UX, accessibility, responsive, performance ([#790](#790), [#792](#792), [#791](#791), [#793](#793)) ([#917](#917)) ([f04a537](f04a537)) * implement human roles and access control levels ([#856](#856)) ([d6d8a06](d6d8a06)) * implement semantic conflict detection in workspace merge ([#860](#860)) ([d97283b](d97283b)) * interaction components and animation patterns ([#853](#853)) ([82d4b01](82d4b01)) * Login page + first-run bootstrap + Company page ([#789](#789), [#888](#888)) ([#896](#896)) ([8758e8d](8758e8d)) * Meetings page with timeline viz, token bars, contribution formatting ([#788](#788)) ([#904](#904)) ([b207f46](b207f46)) * Messages page with threading, channel badges, sender indicators ([#787](#787)) ([#903](#903)) ([28293ad](28293ad)) * Org Chart force-directed view and drag-drop reassignment ([#872](#872), [#873](#873)) ([#912](#912)) ([a68a938](a68a938)) * Org Chart page (living nodes, status, CRUD, department health) ([#870](#870)) ([0acbdae](0acbdae)) * per-command flags for remaining commands, auto-behavior wiring, help/discoverability ([#897](#897)) ([3f7afa2](3f7afa2)) * Providers page with backend rework -- health, CRUD, subscription auth ([#893](#893)) ([9f8dd98](9f8dd98)) * scaffold React + Vite + TypeScript + Tailwind project ([#799](#799)) ([bd151aa](bd151aa)) * Settings page with search, dependency indicators, grouped rendering ([#784](#784)) ([#902](#902)) ([a7b9870](a7b9870)) * Setup Wizard rebuild with template comparison, cost estimator, theme customization ([#879](#879)) ([ae8b50b](ae8b50b)) * setup wizard UX -- template filters, card metadata, provider form reuse ([#910](#910)) ([7f04676](7f04676)) * setup wizard UX overhaul -- mode choice, step reorder, provider fixes ([#907](#907)) ([ee964c4](ee964c4)) * structured ModelRequirement in template agent configs ([#795](#795)) ([7433548](7433548)) * Task Board page (rich Kanban, filtering, dependency viz) ([#871](#871)) ([04a19b0](04a19b0)) ### Bug Fixes * align frontend types with backend and debounce WS refetches ([#916](#916)) ([134c11b](134c11b)) * auto-cleanup targets newly pulled images instead of old ones ([#884](#884)) ([50e6591](50e6591)) * correct wipe backup-skip flow and harden error handling ([#808](#808)) ([c05860f](c05860f)) * improve provider setup in wizard, subscription auth, dashboard bugs ([#914](#914)) ([87bf8e6](87bf8e6)) * improve update channel detection and add config get command ([#814](#814)) ([6b137f0](6b137f0)) * resolve all ESLint warnings, add zero-warnings enforcement ([#899](#899)) ([079b46a](079b46a)) * subscription auth uses api_key, base URL optional for cloud providers ([#915](#915)) ([f0098dd](f0098dd)) ### Refactoring * semantic analyzer cleanup -- shared filtering, concurrency, extraction ([#908](#908)) ([81372bf](81372bf)) ### Documentation * brand identity and UX design system from [#765](#765) exploration ([#804](#804)) ([389a9f4](389a9f4)) * page structure and information architecture for v0.5.0 dashboard ([#809](#809)) ([f8d6d4a](f8d6d4a)) * write UX design guidelines with WCAG-verified color system ([#816](#816)) ([4a4594e](4a4594e)) ### Tests * add unit tests for agent hooks and page components ([#875](#875)) ([#901](#901)) ([1d81546](1d81546)) ### CI/CD * bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group ([#831](#831)) ([01c19de](01c19de)) * bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in /.github/actions/setup-python-uv in the all group ([#920](#920)) ([5f6ba54](5f6ba54)) * bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group ([#868](#868)) ([f22a181](f22a181)) * bump github/codeql-action from 4.34.1 to 4.35.0 in the all group ([#883](#883)) ([87a4890](87a4890)) * bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the minor-and-patch group ([#830](#830)) ([7a69050](7a69050)) * bump the all group with 3 updates ([#923](#923)) ([ff27c8e](ff27c8e)) * bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch group ([#822](#822)) ([07d43eb](07d43eb)) * bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group ([#882](#882)) ([f84118d](f84118d)) ### Maintenance * add design system enforcement hook and component inventory ([#846](#846)) ([15abc43](15abc43)) * add dev-only auth bypass for frontend testing ([#885](#885)) ([6cdcd8a](6cdcd8a)) * add pre-push rebase check hook ([#855](#855)) ([b637a04](b637a04)) * backend hardening -- eviction/size-caps and model validation ([#911](#911)) ([81253d9](81253d9)) * bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1 directory ([#922](#922)) ([b1b0232](b1b0232)) * bump brace-expansion from 5.0.4 to 5.0.5 in /web ([#862](#862)) ([ba4a565](ba4a565)) * bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web ([#801](#801)) ([7574bb5](7574bb5)) * bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group ([#803](#803)) ([14d322e](14d322e)) * bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to 0.15.8 ([#864](#864)) ([f52901e](f52901e)) * bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in /docker/web in the all group ([#919](#919)) ([df85e4f](df85e4f)) * bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in /docker/web ([#800](#800)) ([f4e9450](f4e9450)) * bump node from `44bcbf4` to `71be405` in /docker/sandbox ([#827](#827)) ([91bec67](91bec67)) * bump node from `5209bca` to `cf38e1f` in /docker/web ([#863](#863)) ([66d6043](66d6043)) * bump picomatch in /site ([#842](#842)) ([5f20bcc](5f20bcc)) * bump recharts 2->3 and @types/node 22->25 in /web ([#802](#802)) ([a908800](a908800)) * Bump requests from 2.32.5 to 2.33.0 ([#843](#843)) ([41daf69](41daf69)) * bump smol-toml from 1.6.0 to 1.6.1 in /site ([#826](#826)) ([3e5dbe4](3e5dbe4)) * bump the all group with 3 updates ([#921](#921)) ([7bace0b](7bace0b)) * bump the minor-and-patch group across 1 directory with 2 updates ([#829](#829)) ([93e611f](93e611f)) * bump the minor-and-patch group across 1 directory with 3 updates ([#841](#841)) ([7010c8e](7010c8e)) * bump the minor-and-patch group across 1 directory with 3 updates ([#869](#869)) ([548cee5](548cee5)) * bump the minor-and-patch group in /site with 2 updates ([#865](#865)) ([9558101](9558101)) * bump the minor-and-patch group with 2 updates ([#867](#867)) ([4830706](4830706)) * consolidate Dependabot groups to 1 PR per ecosystem ([06d2556](06d2556)) * consolidate Dependabot groups to 1 PR per ecosystem ([#881](#881)) ([06d2556](06d2556)) * improve worktree skill with full dep sync and status enhancements ([#906](#906)) ([772c625](772c625)) * remove Vue remnants and document framework decision ([#851](#851)) ([bf2adf6](bf2adf6)) * update web dependencies and fix brace-expansion CVE ([#880](#880)) ([a7a0ed6](a7a0ed6)) * upgrade to Storybook 10 and TypeScript 6 ([#845](#845)) ([52d95f2](52d95f2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
<textarea>in the Settings code editor with CodeMirror 6, adding JSON/YAML syntax highlighting, line numbers, bracket matching, and a dark theme mapped to--so-*design tokens. ReusableCodeMirrorEditorcomponent with controlled value sync andreadOnlysupport.SegmentedControlandSelectFieldcontrols, localStorage persistence, Cmd+K integration (23 commands),prefers-reduced-motiondetection, and CSS class name safety guard.CodeMirrorEditor,SegmentedControl,ThemeToggle(all with Storybook stories)useThemeStore(Zustand) withapplyThemeClasses()for live CSS variable switchinguseAnimationPreset()for progressive animation config adoptionsidebarModeaxis controls sidebar layout (rail/collapsible/hidden/persistent/compact)Test plan
npm --prefix web run lint-- 0 errors, 0 warningsnpm --prefix web run type-check-- cleannpm --prefix web run test-- 2007 tests passing (167 files)npm --prefix web run build-- production build succeedsReview coverage
Pre-reviewed by 5 agents (docs-consistency, frontend-reviewer, issue-resolution-verifier, security-reviewer, test-quality-reviewer). 13 findings triaged, all valid findings fixed.
Closes #905
Closes #807
Release-As: 0.5.0