Introduce studio preview components and friendly controls; refactor DomainPanel and Header - #401
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds shared control and studio components, rewires domain panels and smart settings to use friendly token cards, refreshes the header layout, and enables keyboard focus on two editor tablists. ChangesConfigurator control and studio UI
Sequence Diagram(s)sequenceDiagram
participant DomainPanel
participant StudioControls
participant FriendlyControl
participant ControlPreview
participant overrides
DomainPanel->>StudioControls: render resolved studio groups
StudioControls->>FriendlyControl: render each token control
FriendlyControl->>overrides: read or update override
FriendlyControl->>ControlPreview: render token preview
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 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. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
CI Feedback 🧐A test triggered by this PR failed. Here is an AI-generated analysis of the failure:
|
PR Summary by QodoAdd studio previews and FriendlyControl UI; refactor DomainPanel and Header Description
Diagram
High-Level Assessment
Files changed (19)
|
Code Review by Qodo
Context used✅ Compliance rules (platform):
5 rules 1. Reduced-motion ignored in previews
|
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
configurator/src/components/Header.svelte (1)
13-20: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winClear pending share timer on unmount.
Line 19 schedules a timeout, but it is never cleaned up on teardown. If the header unmounts before it fires, the callback can run against disposed component state.
Suggested fix
<script> + import { onDestroy } from 'svelte'; import { sync, allTokens, frameworkVersion } from '../lib/model.js'; import { DOMAIN_BY_ID } from '../lib/domains.js'; import { ui, overrides, history, undo, redo, openOutputDrawer, currentShareUrl } from '../lib/store.svelte.js'; import { copyText, COPY_FEEDBACK_MS } from '../lib/clipboard.js'; @@ let shareCopied = $state(false); let _shareTimer; + + onDestroy(() => { + clearTimeout(_shareTimer); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@configurator/src/components/Header.svelte` around lines 13 - 20, The share timer in Header.svelte is not cleaned up when the component is destroyed, so add teardown logic to clear the pending timeout used by shareLink and prevent it from firing after unmount. Use the existing _shareTimer and shareLink symbols as the place to manage the timer, and register a component cleanup/unmount handler that clears any active timer and resets the reference.
🧹 Nitpick comments (1)
configurator/src/components/ControlPreview.svelte (1)
6-6: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoffEach preview rebuilds the full token declaration set — O(previews × allTokens) on every override change.
buildPreviewDeclarationsiteratesallTokensplus alloverridesand serializes them (perconfigurator/src/lib/preview.js:23-47). SinceDomainPanel/SmartSettingsmount oneControlPreviewperFriendlyControl, every override edit recomputes and re-serializes the entire token set once per visible card. For panels with many tokens this is a noticeable hot path. Consider liftingstageStyleto a single shared$derived(module-level memo keyed onoverrides+ui.previewTheme) and passing it down, rather than recomputing inside each preview.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@configurator/src/components/ControlPreview.svelte` at line 6, The preview styling work in ControlPreview.svelte is being recomputed independently for every FriendlyControl, causing repeated full token serialization on each override change. Move the stageStyle derivation out of the per-card ControlPreview path and into a shared memoized or module-level $derived keyed by overrides and ui.previewTheme, then pass the computed result down so buildPreviewDeclarations is only run once per state change rather than once per preview.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@configurator/src/components/editors/EffectsStudio.svelte`:
- Line 2: The StudioFrame description in EffectsStudio.svelte mixes English with
untranslated Polish text, so update the description copy to fully match the
English UI used by the rest of the studios. Locate the EffectsStudio component
and replace the current description string with a complete English sentence that
describes the blur, opacity, scrim, and compositing preview without any Polish
phrasing.
In `@configurator/src/components/editors/TypographyStudio.svelte`:
- Around line 11-20: The tab state in TypographyStudio.svelte is only used for
button styling and does not affect the specimen content, so the control is
misleading. Update the active tab logic in the tabs block and the specimen
rendering so selecting a tab actually switches what typography preview is shown,
using the existing active state and the tab-related symbols such as tabs,
active, and the specimen section.
In `@configurator/src/lib/controlSchema.js`:
- Around line 22-33: The preview kind mapping is incomplete: controlSchema now
emits gradient for --sf-scrim-direction and scale for numeric tokens, but
ControlPreview.svelte does not handle either and they fall back to spacing.
Update ControlPreview.svelte to add explicit branches for gradient and scale, or
remap those preview kinds to an existing renderer, ensuring the preview
selection matches the values produced by controlSchema.
---
Outside diff comments:
In `@configurator/src/components/Header.svelte`:
- Around line 13-20: The share timer in Header.svelte is not cleaned up when the
component is destroyed, so add teardown logic to clear the pending timeout used
by shareLink and prevent it from firing after unmount. Use the existing
_shareTimer and shareLink symbols as the place to manage the timer, and register
a component cleanup/unmount handler that clears any active timer and resets the
reference.
---
Nitpick comments:
In `@configurator/src/components/ControlPreview.svelte`:
- Line 6: The preview styling work in ControlPreview.svelte is being recomputed
independently for every FriendlyControl, causing repeated full token
serialization on each override change. Move the stageStyle derivation out of the
per-card ControlPreview path and into a shared memoized or module-level $derived
keyed by overrides and ui.previewTheme, then pass the computed result down so
buildPreviewDeclarations is only run once per state change rather than once per
preview.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 36ba43a4-a48e-4a61-ac59-394b962e9c13
📒 Files selected for processing (19)
configurator/src/components/CategoryHeader.svelteconfigurator/src/components/ControlPreview.svelteconfigurator/src/components/ControlSection.svelteconfigurator/src/components/DomainPanel.svelteconfigurator/src/components/FriendlyControl.svelteconfigurator/src/components/Header.svelteconfigurator/src/components/HeadingEditor.svelteconfigurator/src/components/RadiusEditor.svelteconfigurator/src/components/SmartSettings.svelteconfigurator/src/components/editors/ColorStudio.svelteconfigurator/src/components/editors/EffectsStudio.svelteconfigurator/src/components/editors/LayoutStudio.svelteconfigurator/src/components/editors/MotionStudio.svelteconfigurator/src/components/editors/ShadowStudio.svelteconfigurator/src/components/editors/ShapeStudio.svelteconfigurator/src/components/editors/SpacingStudio.svelteconfigurator/src/components/editors/StudioFrame.svelteconfigurator/src/components/editors/TypographyStudio.svelteconfigurator/src/lib/controlSchema.js
| @@ -0,0 +1,3 @@ | |||
| <script>import StudioFrame from './StudioFrame.svelte';</script> | |||
| <StudioFrame title="Effects Studio" description="Blur, opacity, scrim i compositing pokazane jako gotowa karta."><div class="fx"><div><h4>Readable media card</h4><p>Scrim, blur and muted states preview.</p></div></div></StudioFrame> | |||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
User-facing description contains untranslated Polish text.
The description reads "Blur, opacity, scrim i compositing pokazane jako gotowa karta." — the trailing clause is Polish and inconsistent with the English UI copy elsewhere in the studios.
✏️ Proposed copy fix
-<StudioFrame title="Effects Studio" description="Blur, opacity, scrim i compositing pokazane jako gotowa karta."><div class="fx"><div><h4>Readable media card</h4><p>Scrim, blur and muted states preview.</p></div></div></StudioFrame>
+<StudioFrame title="Effects Studio" description="Blur, opacity, scrim and compositing shown as a ready-made card."><div class="fx"><div><h4>Readable media card</h4><p>Scrim, blur and muted states preview.</p></div></div></StudioFrame>📝 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.
| <StudioFrame title="Effects Studio" description="Blur, opacity, scrim i compositing pokazane jako gotowa karta."><div class="fx"><div><h4>Readable media card</h4><p>Scrim, blur and muted states preview.</p></div></div></StudioFrame> | |
| <StudioFrame title="Effects Studio" description="Blur, opacity, scrim and compositing shown as a ready-made card."><div class="fx"><div><h4>Readable media card</h4><p>Scrim, blur and muted states preview.</p></div></div></StudioFrame> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/components/editors/EffectsStudio.svelte` at line 2, The
StudioFrame description in EffectsStudio.svelte mixes English with untranslated
Polish text, so update the description copy to fully match the English UI used
by the rest of the studios. Locate the EffectsStudio component and replace the
current description string with a complete English sentence that describes the
blur, opacity, scrim, and compositing preview without any Polish phrasing.
| <div class="tabs">{#each tabs as t}<button class:active={active===t} onclick={() => active=t}>{t}</button>{/each}</div> | ||
| <div class="specimen"> | ||
| <h1 style:font-family={headingFont}>The quick brown fox</h1> | ||
| <h2 style:font-family={headingFont}>Jumps over the lazy dog</h2> | ||
| <h3 style:font-family={headingFont}>Typography at every scale</h3> | ||
| <h4 style:font-family={headingFont}>Fluid, readable, precise</h4> | ||
| <strong style:font-family={headingFont}>Fine-tune each heading level</strong> | ||
| <code style:font-family={monoFont}>Pixel-perfect control</code> | ||
| <p style:font-family={bodyFont}>Body: The quick brown fox jumps over the lazy dog. A short paragraph shows body rhythm, line height and spacing at a glance.</p> | ||
| </div> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Tabs are stateful but functionally inert
active only changes button styling; the specimen never changes with tab selection. This makes the tab control misleading.
Suggested fix
- <div class="specimen">
- <h1 style:font-family={headingFont}>The quick brown fox</h1>
- <h2 style:font-family={headingFont}>Jumps over the lazy dog</h2>
- <h3 style:font-family={headingFont}>Typography at every scale</h3>
- <h4 style:font-family={headingFont}>Fluid, readable, precise</h4>
- <strong style:font-family={headingFont}>Fine-tune each heading level</strong>
- <code style:font-family={monoFont}>Pixel-perfect control</code>
- <p style:font-family={bodyFont}>Body: The quick brown fox jumps over the lazy dog. A short paragraph shows body rhythm, line height and spacing at a glance.</p>
- </div>
+ <div class="specimen">
+ {`#if` active === 'All' || active === 'H1'}<h1 style:font-family={headingFont}>The quick brown fox</h1>{/if}
+ {`#if` active === 'All' || active === 'H2'}<h2 style:font-family={headingFont}>Jumps over the lazy dog</h2>{/if}
+ {`#if` active === 'All' || active === 'H3'}<h3 style:font-family={headingFont}>Typography at every scale</h3>{/if}
+ {`#if` active === 'All' || active === 'H4'}<h4 style:font-family={headingFont}>Fluid, readable, precise</h4>{/if}
+ {`#if` active === 'All' || active === 'H5' || active === 'H6'}<strong style:font-family={headingFont}>Fine-tune each heading level</strong>{/if}
+ {`#if` active === 'All' || active === 'Mono'}<code style:font-family={monoFont}>Pixel-perfect control</code>{/if}
+ {`#if` active === 'All' || active === 'Body'}<p style:font-family={bodyFont}>Body: The quick brown fox jumps over the lazy dog. A short paragraph shows body rhythm, line height and spacing at a glance.</p>{/if}
+ </div>📝 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.
| <div class="tabs">{#each tabs as t}<button class:active={active===t} onclick={() => active=t}>{t}</button>{/each}</div> | |
| <div class="specimen"> | |
| <h1 style:font-family={headingFont}>The quick brown fox</h1> | |
| <h2 style:font-family={headingFont}>Jumps over the lazy dog</h2> | |
| <h3 style:font-family={headingFont}>Typography at every scale</h3> | |
| <h4 style:font-family={headingFont}>Fluid, readable, precise</h4> | |
| <strong style:font-family={headingFont}>Fine-tune each heading level</strong> | |
| <code style:font-family={monoFont}>Pixel-perfect control</code> | |
| <p style:font-family={bodyFont}>Body: The quick brown fox jumps over the lazy dog. A short paragraph shows body rhythm, line height and spacing at a glance.</p> | |
| </div> | |
| <div class="tabs">{`#each` tabs as t}<button class:active={active===t} onclick={() => active=t}>{t}</button>{/each}</div> | |
| <div class="specimen"> | |
| {`#if` active === 'All' || active === 'H1'}<h1 style:font-family={headingFont}>The quick brown fox</h1>{/if} | |
| {`#if` active === 'All' || active === 'H2'}<h2 style:font-family={headingFont}>Jumps over the lazy dog</h2>{/if} | |
| {`#if` active === 'All' || active === 'H3'}<h3 style:font-family={headingFont}>Typography at every scale</h3>{/if} | |
| {`#if` active === 'All' || active === 'H4'}<h4 style:font-family={headingFont}>Fluid, readable, precise</h4>{/if} | |
| {`#if` active === 'All' || active === 'H5' || active === 'H6'}<strong style:font-family={headingFont}>Fine-tune each heading level</strong>{/if} | |
| {`#if` active === 'All' || active === 'Mono'}<code style:font-family={monoFont}>Pixel-perfect control</code>{/if} | |
| {`#if` active === 'All' || active === 'Body'}<p style:font-family={bodyFont}>Body: The quick brown fox jumps over the lazy dog. A short paragraph shows body rhythm, line height and spacing at a glance.</p>{/if} | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/components/editors/TypographyStudio.svelte` around lines 11
- 20, The tab state in TypographyStudio.svelte is only used for button styling
and does not affect the specimen content, so the control is misleading. Update
the active tab logic in the tabs block and the specimen rendering so selecting a
tab actually switches what typography preview is shown, using the existing
active state and the tab-related symbols such as tabs, active, and the specimen
section.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@configurator/src/components/editors/ColorStudio.svelte`:
- Line 16: The StudioFrame copy in ColorStudio is mixing English and Polish,
which breaks UI consistency. Update the description used with the Color Studio
title so it matches the rest of the view’s English strings, and verify the
surrounding localized labels in ColorStudio.svelte remain consistent with the
same language and tone.
In `@configurator/src/lib/controlSchema.js`:
- Around line 31-32: The gradient inference branch in controlSchema.js can set
control to select for direction-related names without providing options, so
update the schema logic in the controlSchema inference path to always attach a
valid options list when choosing select for gradient direction tokens. Use the
existing controlSchema handling around the scrim/gradient and direction checks
to derive or supply the direction values, so FriendlyControl.svelte can render
the select instead of falling back to the raw editor.
- Around line 35-48: The schema classification in the controlSchema logic is
matching broad font-related names before the more specific weight case, so
weight tokens are getting the wrong control type. Update the conditional order
in the controlSchema classifier so the weight-specific check is evaluated before
the generic /font/ branch, and ensure the existing number control path for
weight still applies after the reordering.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: a0656255-a25e-4adc-8f1b-bf1c2bdec125
📒 Files selected for processing (16)
configurator/src/components/ControlPreview.svelteconfigurator/src/components/DomainPanel.svelteconfigurator/src/components/FriendlyControl.svelteconfigurator/src/components/Header.svelteconfigurator/src/components/editors/ColorStudio.svelteconfigurator/src/components/editors/EffectsStudio.svelteconfigurator/src/components/editors/LayoutStudio.svelteconfigurator/src/components/editors/MotionStudio.svelteconfigurator/src/components/editors/ShadowStudio.svelteconfigurator/src/components/editors/ShapeStudio.svelteconfigurator/src/components/editors/SpacingStudio.svelteconfigurator/src/components/editors/StudioControls.svelteconfigurator/src/components/editors/StudioFrame.svelteconfigurator/src/components/editors/TypographyStudio.svelteconfigurator/src/lib/controlSchema.jsconfigurator/src/lib/studioSchema.js
🚧 Files skipped from review as they are similar to previous changes (10)
- configurator/src/components/editors/EffectsStudio.svelte
- configurator/src/components/ControlPreview.svelte
- configurator/src/components/editors/ShadowStudio.svelte
- configurator/src/components/editors/MotionStudio.svelte
- configurator/src/components/editors/StudioFrame.svelte
- configurator/src/components/editors/TypographyStudio.svelte
- configurator/src/components/FriendlyControl.svelte
- configurator/src/components/editors/SpacingStudio.svelte
- configurator/src/components/Header.svelte
- configurator/src/components/DomainPanel.svelte
| .map((name) => tokenByName.get(name)).filter(Boolean); | ||
| </script> | ||
|
|
||
| <StudioFrame title="Color Studio" description="Najpierw ustaw źródła marki, potem sprawdź semantic roles, statusy, shade ramp, kontrast i realne użycie koloru." tone="color"> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Mixed-language description. The title is English ("Color Studio") while the description is Polish, inconsistent with the otherwise English UI strings in this view (button labels, legends, etc.). Likely leftover/placeholder copy.
[localization_and_formatting]
Suggested fix
-<StudioFrame title="Color Studio" description="Najpierw ustaw źródła marki, potem sprawdź semantic roles, statusy, shade ramp, kontrast i realne użycie koloru." tone="color">
+<StudioFrame title="Color Studio" description="Set the brand sources first, then check semantic roles, statuses, shade ramp, contrast and real color usage." tone="color">📝 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.
| <StudioFrame title="Color Studio" description="Najpierw ustaw źródła marki, potem sprawdź semantic roles, statusy, shade ramp, kontrast i realne użycie koloru." tone="color"> | |
| <StudioFrame title="Color Studio" description="Set the brand sources first, then check semantic roles, statuses, shade ramp, contrast and real color usage." tone="color"> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/components/editors/ColorStudio.svelte` at line 16, The
StudioFrame copy in ColorStudio is mixing English and Polish, which breaks UI
consistency. Update the description used with the Color Studio title so it
matches the rest of the view’s English strings, and verify the surrounding
localized labels in ColorStudio.svelte remain consistent with the same language
and tone.
| } else if (/scrim|gradient/.test(name)) { | ||
| schema = { ...schema, control: /direction/.test(name) ? 'select' : 'text', preview: 'gradient' }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Add options when inferring gradient direction selects.
This branch can emit control: 'select' without options; FriendlyControl.svelte only renders selects when options exist, so generic gradient direction tokens fall back to the raw editor.
Proposed fix
} else if (/scrim|gradient/.test(name)) {
- schema = { ...schema, control: /direction/.test(name) ? 'select' : 'text', preview: 'gradient' };
+ const isDirection = /direction/.test(name);
+ schema = {
+ ...schema,
+ control: isDirection ? 'select' : 'text',
+ preview: 'gradient',
+ ...(isDirection ? { options: ['to top', 'to bottom', 'to right', 'to left', '135deg'] } : {})
+ };📝 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.
| } else if (/scrim|gradient/.test(name)) { | |
| schema = { ...schema, control: /direction/.test(name) ? 'select' : 'text', preview: 'gradient' }; | |
| } else if (/scrim|gradient/.test(name)) { | |
| const isDirection = /direction/.test(name); | |
| schema = { | |
| ...schema, | |
| control: isDirection ? 'select' : 'text', | |
| preview: 'gradient', | |
| ...(isDirection ? { options: ['to top', 'to bottom', 'to right', 'to left', '135deg'] } : {}) | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/lib/controlSchema.js` around lines 31 - 32, The gradient
inference branch in controlSchema.js can set control to select for
direction-related names without providing options, so update the schema logic in
the controlSchema inference path to always attach a valid options list when
choosing select for gradient direction tokens. Use the existing controlSchema
handling around the scrim/gradient and direction checks to derive or supply the
direction values, so FriendlyControl.svelte can render the select instead of
falling back to the raw editor.
| } else if (/font/.test(name)) { | ||
| schema = { ...schema, control: 'font', preview: 'font' }; | ||
| } else if (/duration|motion-scale|animation|transition/.test(name)) { | ||
| schema = { ...schema, control: parseLength(value) ? 'length' : 'text', preview: 'motion' }; | ||
| } else if (/radius/.test(name)) { | ||
| schema = { ...schema, control: parseLength(value) ? 'length' : 'number', preview: 'radius' }; | ||
| } else if (/shadow/.test(name)) { | ||
| schema = { ...schema, control: 'text', preview: 'shadow' }; | ||
| } else if (/opacity/.test(name)) { | ||
| schema = { ...schema, control: 'number', preview: 'opacity' }; | ||
| } else if (/space|gap|gutter|pad|width|height|size|container|leading|tracking|border-width|divider|offset|inset/.test(name) || parseLength(value)) { | ||
| schema = { ...schema, control: 'length', preview: /leading/.test(name) ? 'line-height' : 'spacing' }; | ||
| } else if (/scale|threshold|bias|weight|z-/.test(name) || /^-?\d*\.?\d+$/.test(value)) { | ||
| schema = { ...schema, control: 'number', preview: /weight/.test(name) ? 'font' : 'scale' }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Move weight handling before the broad font match.
Line 35 catches any font-weight-style token before Line 47 can assign the intended numeric weight control.
Proposed fix
- } else if (/font/.test(name)) {
- schema = { ...schema, control: 'font', preview: 'font' };
+ } else if (/weight/.test(name)) {
+ schema = { ...schema, control: 'number', preview: 'font' };
+ } else if (/font/.test(name)) {
+ schema = { ...schema, control: 'font', preview: 'font' };
...
- } else if (/scale|threshold|bias|weight|z-/.test(name) || /^-?\d*\.?\d+$/.test(value)) {
+ } else if (/scale|threshold|bias|z-/.test(name) || /^-?\d*\.?\d+$/.test(value)) {📝 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.
| } else if (/font/.test(name)) { | |
| schema = { ...schema, control: 'font', preview: 'font' }; | |
| } else if (/duration|motion-scale|animation|transition/.test(name)) { | |
| schema = { ...schema, control: parseLength(value) ? 'length' : 'text', preview: 'motion' }; | |
| } else if (/radius/.test(name)) { | |
| schema = { ...schema, control: parseLength(value) ? 'length' : 'number', preview: 'radius' }; | |
| } else if (/shadow/.test(name)) { | |
| schema = { ...schema, control: 'text', preview: 'shadow' }; | |
| } else if (/opacity/.test(name)) { | |
| schema = { ...schema, control: 'number', preview: 'opacity' }; | |
| } else if (/space|gap|gutter|pad|width|height|size|container|leading|tracking|border-width|divider|offset|inset/.test(name) || parseLength(value)) { | |
| schema = { ...schema, control: 'length', preview: /leading/.test(name) ? 'line-height' : 'spacing' }; | |
| } else if (/scale|threshold|bias|weight|z-/.test(name) || /^-?\d*\.?\d+$/.test(value)) { | |
| schema = { ...schema, control: 'number', preview: /weight/.test(name) ? 'font' : 'scale' }; | |
| } else if (/weight/.test(name)) { | |
| schema = { ...schema, control: 'number', preview: 'font' }; | |
| } else if (/font/.test(name)) { | |
| schema = { ...schema, control: 'font', preview: 'font' }; | |
| } else if (/duration|motion-scale|animation|transition/.test(name)) { | |
| schema = { ...schema, control: parseLength(value) ? 'length' : 'text', preview: 'motion' }; | |
| } else if (/radius/.test(name)) { | |
| schema = { ...schema, control: parseLength(value) ? 'length' : 'number', preview: 'radius' }; | |
| } else if (/shadow/.test(name)) { | |
| schema = { ...schema, control: 'text', preview: 'shadow' }; | |
| } else if (/opacity/.test(name)) { | |
| schema = { ...schema, control: 'number', preview: 'opacity' }; | |
| } else if (/space|gap|gutter|pad|width|height|size|container|leading|tracking|border-width|divider|offset|inset/.test(name) || parseLength(value)) { | |
| schema = { ...schema, control: 'length', preview: /leading/.test(name) ? 'line-height' : 'spacing' }; | |
| } else if (/scale|threshold|bias|z-/.test(name) || /^-?\d*\.?\d+$/.test(value)) { | |
| schema = { ...schema, control: 'number', preview: 'scale' }; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/lib/controlSchema.js` around lines 35 - 48, The schema
classification in the controlSchema logic is matching broad font-related names
before the more specific weight case, so weight tokens are getting the wrong
control type. Update the conditional order in the controlSchema classifier so
the weight-specific check is evaluated before the generic /font/ branch, and
ensure the existing number control path for weight still applies after the
reordering.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@configurator/src/components/editors/SpacingStudio.svelte`:
- Line 11: The SpacingStudio description currently mixes Polish and English in
the user-facing text, which makes the UI inconsistent. Update the description on
the StudioFrame in SpacingStudio.svelte so it uses one locale consistently,
matching the rest of the English UI, and keep the wording aligned with the
“Spacing Studio” title and its layout/rhythm preview purpose.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7b1e0f3a-e123-4297-949a-3f51a20a6041
📒 Files selected for processing (3)
configurator/src/components/editors/ColorStudio.svelteconfigurator/src/components/editors/SpacingStudio.svelteconfigurator/tests-components/studios.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
- configurator/tests-components/studios.test.js
- configurator/src/components/editors/ColorStudio.svelte
| const groups = resolveStudioGroups(STUDIO_GROUPS.spacing); | ||
| </script> | ||
|
|
||
| <StudioFrame title="Spacing Studio" description="Ruler, rhythm i layout preview zamiast samych wartości liczbowych: od globalnej skali po realne odstępy w komponentach i sekcjach."> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use consistent locale in the studio description text.
Line 11 mixes Polish and English in a user-facing description, which creates an inconsistent UX in an otherwise English UI.
Suggested edit
-<StudioFrame title="Spacing Studio" description="Ruler, rhythm i layout preview zamiast samych wartości liczbowych: od globalnej skali po realne odstępy w komponentach i sekcjach.">
+<StudioFrame title="Spacing Studio" description="Ruler, rhythm, and layout previews instead of raw numeric values—from global scale to real spacing in components and sections.">📝 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.
| <StudioFrame title="Spacing Studio" description="Ruler, rhythm i layout preview zamiast samych wartości liczbowych: od globalnej skali po realne odstępy w komponentach i sekcjach."> | |
| <StudioFrame title="Spacing Studio" description="Ruler, rhythm, and layout previews instead of raw numeric values—from global scale to real spacing in components and sections."> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/components/editors/SpacingStudio.svelte` at line 11, The
SpacingStudio description currently mixes Polish and English in the user-facing
text, which makes the UI inconsistent. Update the description on the StudioFrame
in SpacingStudio.svelte so it uses one locale consistently, matching the rest of
the English UI, and keep the wording aligned with the “Spacing Studio” title and
its layout/rhythm preview purpose.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
configurator/src/components/editors/LayoutStudio.svelte (1)
13-15: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winUse ordered-list semantics for workflow steps.
Since this is a numbered process, rendering steps as
<ol><li>inside the nav will improve screen-reader semantics over plain<span>elements.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@configurator/src/components/editors/LayoutStudio.svelte` around lines 13 - 15, The workflow steps in LayoutStudio should use ordered-list semantics instead of plain spans. Update the markup inside the nav for the workflow rendering so the {`#each` workflow as step, index (step)} loop produces an ordered list with list items, preserving the existing numbering and text. Keep the aria-label on the nav and make sure the step rendering remains keyed by step while switching to semantic list elements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@configurator/src/components/editors/MotionStudio.svelte`:
- Around line 58-83: The animated previews in MotionStudio.svelte ignore users’
reduced-motion preference, so update the motion preview styles to respect
prefers-reduced-motion. In the MotionStudio component, specifically around the
motion-rails, easing-lab, preset-lab, reduced-lab, and keyframes like
move/enter/exit/shimmer, add a reduced-motion branch that disables or greatly
minimizes infinite animations and transitions when the media query matches,
while keeping the preview content readable and functional.
---
Nitpick comments:
In `@configurator/src/components/editors/LayoutStudio.svelte`:
- Around line 13-15: The workflow steps in LayoutStudio should use ordered-list
semantics instead of plain spans. Update the markup inside the nav for the
workflow rendering so the {`#each` workflow as step, index (step)} loop produces
an ordered list with list items, preserving the existing numbering and text.
Keep the aria-label on the nav and make sure the step rendering remains keyed by
step while switching to semantic list elements.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 91cce83d-544c-4617-9fa3-39200470eb1c
📒 Files selected for processing (7)
configurator/src/components/editors/EffectsStudio.svelteconfigurator/src/components/editors/LayoutStudio.svelteconfigurator/src/components/editors/MotionStudio.svelteconfigurator/src/components/editors/ShadowStudio.svelteconfigurator/src/components/editors/ShapeStudio.svelteconfigurator/src/components/editors/StudioWorkflow.svelteconfigurator/tests-components/studios.test.js
✅ Files skipped from review due to trivial changes (1)
- configurator/src/components/editors/StudioWorkflow.svelte
🚧 Files skipped from review as they are similar to previous changes (3)
- configurator/src/components/editors/ShadowStudio.svelte
- configurator/src/components/editors/EffectsStudio.svelte
- configurator/tests-components/studios.test.js
| .motion-rails span { width: 88px; text-align: center; padding: 8px 12px; border-radius: 999px; background: var(--cfg-accent-strong); color: white; animation: move var(--sf-duration-normal, 300ms) var(--sf-ease-in-out, ease-in-out) infinite alternate; } | ||
| .motion-rails .instant { animation-duration: var(--sf-duration-instant, 75ms); } | ||
| .motion-rails .fast { animation-duration: var(--sf-duration-fast, 150ms); } | ||
| .motion-rails .slow { animation-duration: var(--sf-duration-slow, 600ms); } | ||
| .motion-rails b { color: var(--cfg-text-muted); font-size: 11px; } | ||
| .easing-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | ||
| .easing-lab article { display: grid; gap: 8px; padding: 12px; } | ||
| .easing-lab b { text-transform: uppercase; font-size: 11px; letter-spacing: .06em; } | ||
| .easing-lab span { display: block; height: 58px; border-left: 1px solid var(--cfg-border-strong); border-bottom: 1px solid var(--cfg-border-strong); background: linear-gradient(135deg, transparent 48%, var(--cfg-accent-strong) 49% 52%, transparent 53%); border-radius: 8px; } | ||
| .easing-lab .in { background: radial-gradient(circle at 85% 85%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 58%, var(--cfg-accent-strong) 59% 62%, transparent 63%); } | ||
| .easing-lab .out { background: radial-gradient(circle at 20% 20%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 38%, var(--cfg-accent-strong) 39% 42%, transparent 43%); } | ||
| .easing-lab .spring { background: repeating-radial-gradient(ellipse at 50% 70%, transparent 0 10px, color-mix(in oklab, var(--cfg-accent-strong) 70%, transparent) 11px 13px); } | ||
| .preset-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | ||
| .preset-lab article { min-height: 96px; display: grid; place-items: center; text-transform: uppercase; font-weight: 900; letter-spacing: .06em; transition: transform var(--sf-duration-normal) var(--sf-ease-out), box-shadow var(--sf-duration-normal) var(--sf-ease-out), opacity var(--sf-duration-fast) var(--sf-ease-in); } | ||
| .enter { animation: enter var(--sf-duration-normal) var(--sf-ease-out) infinite alternate; } | ||
| .hover:hover { transform: translateY(-4px) scale(1.02); box-shadow: var(--sf-shadow-m); } | ||
| .exit { animation: exit var(--sf-duration-slow) var(--sf-ease-in) infinite alternate; } | ||
| .shimmer { background: linear-gradient(90deg, var(--cfg-bg-2), var(--cfg-accent-soft), var(--cfg-bg-2)); background-size: 220% 100%; animation: shimmer var(--sf-duration-slower, 900ms) var(--sf-ease-in-out) infinite; } | ||
| .reduced-lab { display: grid; gap: 8px; padding: 14px; } | ||
| .reduced-lab p { margin: 0; } | ||
| .reduced-lab div { display: flex; gap: 8px; } | ||
| .reduced-lab span { inline-size: 36px; block-size: 10px; border-radius: 999px; background: var(--cfg-accent-soft); opacity: var(--sf-state-pending-opacity, .65); } | ||
| @keyframes move { to { transform: translateX(min(360px, 42vw)); } } | ||
| @keyframes enter { from { transform: translateY(8px); opacity: .4; } to { transform: translateY(0); opacity: 1; } } | ||
| @keyframes exit { from { opacity: 1; } to { opacity: .45; transform: scale(.96); } } | ||
| @keyframes shimmer { to { background-position: -220% 0; } } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Respect prefers-reduced-motion for animated previews.
This view runs several infinite animations even for users who explicitly request reduced motion, which is an accessibility blocker.
Proposed fix
`@keyframes` shimmer { to { background-position: -220% 0; } }
+ `@media` (prefers-reduced-motion: reduce) {
+ .motion-rails span,
+ .enter,
+ .exit,
+ .shimmer {
+ animation: none !important;
+ transform: none !important;
+ }
+ .preset-lab article {
+ transition: none;
+ }
+ }
`@media` (max-width: 860px) { .duration-lab, .easing-lab, .preset-lab { grid-template-columns: 1fr 1fr; } }📝 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.
| .motion-rails span { width: 88px; text-align: center; padding: 8px 12px; border-radius: 999px; background: var(--cfg-accent-strong); color: white; animation: move var(--sf-duration-normal, 300ms) var(--sf-ease-in-out, ease-in-out) infinite alternate; } | |
| .motion-rails .instant { animation-duration: var(--sf-duration-instant, 75ms); } | |
| .motion-rails .fast { animation-duration: var(--sf-duration-fast, 150ms); } | |
| .motion-rails .slow { animation-duration: var(--sf-duration-slow, 600ms); } | |
| .motion-rails b { color: var(--cfg-text-muted); font-size: 11px; } | |
| .easing-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | |
| .easing-lab article { display: grid; gap: 8px; padding: 12px; } | |
| .easing-lab b { text-transform: uppercase; font-size: 11px; letter-spacing: .06em; } | |
| .easing-lab span { display: block; height: 58px; border-left: 1px solid var(--cfg-border-strong); border-bottom: 1px solid var(--cfg-border-strong); background: linear-gradient(135deg, transparent 48%, var(--cfg-accent-strong) 49% 52%, transparent 53%); border-radius: 8px; } | |
| .easing-lab .in { background: radial-gradient(circle at 85% 85%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 58%, var(--cfg-accent-strong) 59% 62%, transparent 63%); } | |
| .easing-lab .out { background: radial-gradient(circle at 20% 20%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 38%, var(--cfg-accent-strong) 39% 42%, transparent 43%); } | |
| .easing-lab .spring { background: repeating-radial-gradient(ellipse at 50% 70%, transparent 0 10px, color-mix(in oklab, var(--cfg-accent-strong) 70%, transparent) 11px 13px); } | |
| .preset-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | |
| .preset-lab article { min-height: 96px; display: grid; place-items: center; text-transform: uppercase; font-weight: 900; letter-spacing: .06em; transition: transform var(--sf-duration-normal) var(--sf-ease-out), box-shadow var(--sf-duration-normal) var(--sf-ease-out), opacity var(--sf-duration-fast) var(--sf-ease-in); } | |
| .enter { animation: enter var(--sf-duration-normal) var(--sf-ease-out) infinite alternate; } | |
| .hover:hover { transform: translateY(-4px) scale(1.02); box-shadow: var(--sf-shadow-m); } | |
| .exit { animation: exit var(--sf-duration-slow) var(--sf-ease-in) infinite alternate; } | |
| .shimmer { background: linear-gradient(90deg, var(--cfg-bg-2), var(--cfg-accent-soft), var(--cfg-bg-2)); background-size: 220% 100%; animation: shimmer var(--sf-duration-slower, 900ms) var(--sf-ease-in-out) infinite; } | |
| .reduced-lab { display: grid; gap: 8px; padding: 14px; } | |
| .reduced-lab p { margin: 0; } | |
| .reduced-lab div { display: flex; gap: 8px; } | |
| .reduced-lab span { inline-size: 36px; block-size: 10px; border-radius: 999px; background: var(--cfg-accent-soft); opacity: var(--sf-state-pending-opacity, .65); } | |
| @keyframes move { to { transform: translateX(min(360px, 42vw)); } } | |
| @keyframes enter { from { transform: translateY(8px); opacity: .4; } to { transform: translateY(0); opacity: 1; } } | |
| @keyframes exit { from { opacity: 1; } to { opacity: .45; transform: scale(.96); } } | |
| @keyframes shimmer { to { background-position: -220% 0; } } | |
| .motion-rails span { width: 88px; text-align: center; padding: 8px 12px; border-radius: 999px; background: var(--cfg-accent-strong); color: white; animation: move var(--sf-duration-normal, 300ms) var(--sf-ease-in-out, ease-in-out) infinite alternate; } | |
| .motion-rails .instant { animation-duration: var(--sf-duration-instant, 75ms); } | |
| .motion-rails .fast { animation-duration: var(--sf-duration-fast, 150ms); } | |
| .motion-rails .slow { animation-duration: var(--sf-duration-slow, 600ms); } | |
| .motion-rails b { color: var(--cfg-text-muted); font-size: 11px; } | |
| .easing-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | |
| .easing-lab article { display: grid; gap: 8px; padding: 12px; } | |
| .easing-lab b { text-transform: uppercase; font-size: 11px; letter-spacing: .06em; } | |
| .easing-lab span { display: block; height: 58px; border-left: 1px solid var(--cfg-border-strong); border-bottom: 1px solid var(--cfg-border-strong); background: linear-gradient(135deg, transparent 48%, var(--cfg-accent-strong) 49% 52%, transparent 53%); border-radius: 8px; } | |
| .easing-lab .in { background: radial-gradient(circle at 85% 85%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 58%, var(--cfg-accent-strong) 59% 62%, transparent 63%); } | |
| .easing-lab .out { background: radial-gradient(circle at 20% 20%, var(--cfg-accent-strong) 0 6px, transparent 7px), linear-gradient(145deg, transparent 38%, var(--cfg-accent-strong) 39% 42%, transparent 43%); } | |
| .easing-lab .spring { background: repeating-radial-gradient(ellipse at 50% 70%, transparent 0 10px, color-mix(in oklab, var(--cfg-accent-strong) 70%, transparent) 11px 13px); } | |
| .preset-lab { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } | |
| .preset-lab article { min-height: 96px; display: grid; place-items: center; text-transform: uppercase; font-weight: 900; letter-spacing: .06em; transition: transform var(--sf-duration-normal) var(--sf-ease-out), box-shadow var(--sf-duration-normal) var(--sf-ease-out), opacity var(--sf-duration-fast) var(--sf-ease-in); } | |
| .enter { animation: enter var(--sf-duration-normal) var(--sf-ease-out) infinite alternate; } | |
| .hover:hover { transform: translateY(-4px) scale(1.02); box-shadow: var(--sf-shadow-m); } | |
| .exit { animation: exit var(--sf-duration-slow) var(--sf-ease-in) infinite alternate; } | |
| .shimmer { background: linear-gradient(90deg, var(--cfg-bg-2), var(--cfg-accent-soft), var(--cfg-bg-2)); background-size: 220% 100%; animation: shimmer var(--sf-duration-slower, 900ms) var(--sf-ease-in-out) infinite; } | |
| .reduced-lab { display: grid; gap: 8px; padding: 14px; } | |
| .reduced-lab p { margin: 0; } | |
| .reduced-lab div { display: flex; gap: 8px; } | |
| .reduced-lab span { inline-size: 36px; block-size: 10px; border-radius: 999px; background: var(--cfg-accent-soft); opacity: var(--sf-state-pending-opacity, .65); } | |
| `@keyframes` move { to { transform: translateX(min(360px, 42vw)); } } | |
| `@keyframes` enter { from { transform: translateY(8px); opacity: .4; } to { transform: translateY(0); opacity: 1; } } | |
| `@keyframes` exit { from { opacity: 1; } to { opacity: .45; transform: scale(.96); } } | |
| `@keyframes` shimmer { to { background-position: -220% 0; } } | |
| `@media` (prefers-reduced-motion: reduce) { | |
| .motion-rails span, | |
| .enter, | |
| .exit, | |
| .shimmer { | |
| animation: none !important; | |
| transform: none !important; | |
| } | |
| .preset-lab article { | |
| transition: none; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@configurator/src/components/editors/MotionStudio.svelte` around lines 58 -
83, The animated previews in MotionStudio.svelte ignore users’ reduced-motion
preference, so update the motion preview styles to respect
prefers-reduced-motion. In the MotionStudio component, specifically around the
motion-rails, easing-lab, preset-lab, reduced-lab, and keyframes like
move/enter/exit/shimmer, add a reduced-motion branch that disables or greatly
minimizes infinite animations and transitions when the media query matches,
while keeping the preview content readable and functional.
Motivation
Description
CategoryHeader,ControlSection,ControlPreview,FriendlyControl, andStudioFrame, plus multiple studio views undercomponents/editors(TypographyStudio,ColorStudio,SpacingStudio,LayoutStudio,ShapeStudio,ShadowStudio,MotionStudio,EffectsStudio) to present domain-specific visual canvases.controlSchema.jswithcontrolForToken()to heuristically map tokens to control types and preview kinds (color, font, length, motion, radius, shadow, etc.).DomainPanel.svelteto useCategoryHeader,ControlSection, andFriendlyControl, and to embed the new studio views for matching domains; removed the previous manual header and expanded card markup in favor of the new components.Header.sveltemarkup and styles for a simplified brand row, compact search/command area, grouped action sets, improved responsive behavior, and share/export button semantics.SmartSettings.svelteto renderFriendlyControlinstead of rawTokenRowin many places, and applied small accessibility/focus tweaks toHeadingEditor.svelteandRadiusEditor.svelte(addedtabindexon tablists).Testing
npm run buildto validate bundling and Svelte compilation, which completed successfully.npm testand the tests passed without regressions.Codex Task
Summary by CodeRabbit