Skip to content

Refactor studios to use shared StudioFrame with per-panel controls - #412

Closed
jackgranatowski wants to merge 2 commits into
mainfrom
codex/rozszerzanie-i-refaktoryzacja-komponentow-studio
Closed

Refactor studios to use shared StudioFrame with per-panel controls#412
jackgranatowski wants to merge 2 commits into
mainfrom
codex/rozszerzanie-i-refaktoryzacja-komponentow-studio

Conversation

@jackgranatowski

@jackgranatowski jackgranatowski commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Motivation

  • Consolidate duplicated decorative workflow navigation into a single reusable studio shell that exposes a left section navigator, a central preview/canvas, and a right-side active-section control panel.
  • Make each studio declare scoped panels so controls show only tokens related to the active panel while keeping the full token catalogue in All variables.

Description

  • Extended StudioFrame.svelte to render three areas: left navigation (panels), main preview canvas (renders children with the current activePanel), and an active-section controls panel that calls back into the studio to render scoped controls.
  • Converted LayoutStudio.svelte, ShapeStudio.svelte, ShadowStudio.svelte, MotionStudio.svelte, and EffectsStudio.svelte to declare panels (arrays of { label, description, groups }) via tokenByName and removed their local duplicated workflow/navigation UI.
  • Replaced global StudioControls {groups} usage with a snippet that renders StudioControls for activePanel.groups so each panel only exposes its related knobs and live preview.
  • Updated component smoke tests in configurator/tests-components/studios.test.js to match the new panel labels and expectations.

Testing

  • Ran npm run check (Svelte diagnostics); no errors, only existing warnings about values captured at initialization.
  • Ran npm run test:components (Vitest); component smoke tests passed after updating expectations (all tests green).
  • Ran npm run build (Vite); production build completed successfully.
  • Attempted npx playwright install chromium and a Playwright screenshot for a visual sanity check, but the browser download failed with a CDN 403 Domain forbidden, so browser-based screenshot could not be produced.

Codex Task

Summary by CodeRabbit

  • New Features

    • Studio editors now use panel-based navigation, making it easier to switch between sections while editing.
    • The studio interface adapts better on smaller screens, with improved initialization for responsive layouts.
  • Bug Fixes

    • Fixed a startup issue where panel visibility could be calculated too early on some screen sizes.
    • Updated studio content to keep labels and control text consistent across editing experiences.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The configurator now uses panel-based studio schema data and panel-aware rendering across StudioFrame and the studio editor components. The related schema tests and smoke-test expectations were updated, and ControlSection now defers viewport-based open-state initialization until after mount.

Changes

Studio panel navigation refactor

Layer / File(s) Summary
Schema and resolver
configurator/src/lib/studioSchema.js, configurator/tests/studio-schema.test.js
STUDIO_PANELS replaces STUDIO_GROUPS, resolveStudioPanels resolves panel tokens, and schema tests cover panel metadata and token filtering.
Frame navigation and controls
configurator/src/components/editors/StudioFrame.svelte
StudioFrame now accepts panels and controls, tracks the active panel, renders panel navigation, and passes the selected panel into children and controls.
Studio consumers adopt panels
configurator/src/components/editors/*Studio.svelte, configurator/src/components/DomainPanel.svelte, configurator/tests-components/studios.test.js
Studio editor components switch to panel resolution and active-panel controls, DomainPanel renders Studio directly, and smoke-test expectations update for the changed studio text.

Control section viewport init

Layer / File(s) Summary
Viewport-based open state
configurator/src/components/ControlSection.svelte
ControlSection initializes closed and sets open once from window.innerWidth and defaultOpen inside a guarded effect.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • codeslash-dev/SLASHED#402: Introduced the earlier STUDIO_GROUPS / resolveStudioGroups studio model that this PR replaces with STUDIO_PANELS / resolveStudioPanels.
  • codeslash-dev/SLASHED#403: Touches the same configurator/src/components/ControlSection.svelte viewport-based open initialization path.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactor: studios now share StudioFrame and render controls per active panel.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/rozszerzanie-i-refaktoryzacja-komponentow-studio

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Refactor token studios to use StudioFrame panels with scoped controls
✨ Enhancement 🧪 Tests 🕐 20-40 Minutes

Grey Divider

Description

• Extend StudioFrame to add section navigation, preview canvas, and active-panel controls.
• Refactor token studios to declare panel metadata and scoped token groups per section.
• Update component smoke tests to assert new panel labels and rendered content.
Diagram

graph TD
  A["Token Studio (e.g. Layout)"] --> B["StudioFrame"] --> C["Left nav (panels)"] --> D["Active panel"] --> E["Right controls"] --> F["StudioControls"]
  B --> G["Preview canvas"]
  A --> H["tokenByName"]
  B --> I["overrides/ui store"]
  J["studios.test.js"] --> A

  subgraph Legend
    direction LR
    _file["Svelte file"] ~~~ _store[("Store/data")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extract a shared panel-builder helper
  • ➕ Removes repeated panel() function and token mapping logic from each studio
  • ➕ Centralizes filtering/validation (e.g., missing token names)
  • ➖ Adds another abstraction/file to navigate
  • ➖ May be premature if panel shapes will diverge per studio soon
2. Keep STUDIO_GROUPS as source of truth and derive panels from schema
  • ➕ Single authoritative schema for groups/tokens
  • ➕ Reduces risk of drifting token lists across UI and schema
  • ➖ Schema may not express per-panel scoping cleanly without extra structure
  • ➖ Requires more involved schema changes/migration
3. Let StudioFrame accept groups + per-panel filters instead of full panels
  • ➕ Studios provide less metadata; filtering stays in the shell
  • ➕ Potentially simpler authoring for studios
  • ➖ Pushes domain knowledge (which tokens belong to which panel) into StudioFrame or an indirection layer
  • ➖ Harder to customize panel titles/descriptions and ordering

Recommendation: Current approach (studios own panels, StudioFrame owns layout and selection) is a good split: it keeps token/domain grouping close to each studio while de-duplicating navigation/layout. Consider a follow-up to extract the repeated panel() builder into a shared utility (and optionally add warnings for unknown token names) to reduce copy/paste and prevent silent omissions.

Files changed (7) +120 / -74

Enhancement (1) +52 / -23
StudioFrame.svelteAdd 3-column StudioFrame layout with panel navigation and controls slot +52/-23

Add 3-column StudioFrame layout with panel navigation and controls slot

• Extends StudioFrame props to accept 'panels' and a 'controls' render function, tracks the selected panel, and passes the active panel into both preview children and the controls renderer. Updates layout/styling to a left nav + main canvas + right sticky controls panel with responsive behavior.

configurator/src/components/editors/StudioFrame.svelte

Refactor (5) +65 / -48
EffectsStudio.svelteDefine effects panels and render scoped controls via StudioFrame +13/-8

Define effects panels and render scoped controls via StudioFrame

• Replaces schema-derived groups and the local workflow UI with a 'panels' array built from explicit token names via 'tokenByName'. Adds a 'controls(activePanel)' snippet so StudioControls only shows groups for the selected panel.

configurator/src/components/editors/EffectsStudio.svelte

LayoutStudio.svelteConvert layout studio to StudioFrame panels and remove workflow strip +13/-16

Convert layout studio to StudioFrame panels and remove workflow strip

• Defines four layout panels (Containers/Grid/Measure/Anchors) with token-scoped groups and passes them into StudioFrame. Removes the duplicated workflow navigation and switches controls rendering to the active panel snippet.

configurator/src/components/editors/LayoutStudio.svelte

MotionStudio.svelteScope motion controls by active panel (Durations/Easing/Presets/Reduced) +13/-8

Scope motion controls by active panel (Durations/Easing/Presets/Reduced)

• Moves from global studio groups + workflow steps to a 'panels' model using 'tokenByName' lookups. Renders StudioControls through the 'controls(activePanel)' snippet for per-panel knob scoping.

configurator/src/components/editors/MotionStudio.svelte

ShadowStudio.svelteIntroduce shadow panels and route controls through active panel +13/-8

Introduce shadow panels and route controls through active panel

• Drops StudioWorkflow and schema-based group resolution in favor of explicit panels (Elevation/Surfaces/Overlays/Text & media). Updates controls to render only the selected panel's token group list.

configurator/src/components/editors/ShadowStudio.svelte

ShapeStudio.svelteRefactor shape studio to panel-driven navigation and scoped controls +13/-8

Refactor shape studio to panel-driven navigation and scoped controls

• Defines shape panels (Radius/Borders/Dividers/Focus) using 'tokenByName' and removes the local workflow UI. Uses the 'controls(activePanel)' snippet to scope StudioControls to the currently selected panel.

configurator/src/components/editors/ShapeStudio.svelte

Tests (1) +3 / -3
studios.test.jsUpdate studio smoke tests for new panel labels/content +3/-3

Update studio smoke tests for new panel labels/content

• Adjusts expected text assertions for Shadow/Motion/Effects studios to match the new panel naming and scoped headings rendered after the refactor.

configurator/tests-components/studios.test.js

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 5 rules

Grey Divider


Remediation recommended

1. Panel nav highlight desync 🐞 Bug ≡ Correctness
Description
selectedPanelId can remain a stale activePanelId even when it no longer matches any panel, while
activePanel falls back to panels[0]; this can render panel 0 controls with no nav item marked
active/aria-current.
Code

configurator/src/components/editors/StudioFrame.svelte[R5-8]

+  let activePanelId = $state('');
+  const selectedPanelId = $derived(activePanelId || panels[0]?.id || panels[0]?.label || '');
  const stageStyle = $derived(buildPreviewDeclarations(overrides, ui.previewTheme));
+  const activePanel = $derived(panels.find((panel) => (panel.id ?? panel.label) === selectedPanelId) ?? panels[0]);
Relevance

⭐⭐⭐ High

Team accepted prior UI desync fixes via deterministic fallback when selection key invalid (Preview
domain→section mapping).

PR-#398
PR-#402

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code explicitly allows activePanel to fall back to panels[0] when selectedPanelId has no
match, but selectedPanelId itself does not validate against panels, and the nav uses
selectedPanelId for the active state/aria-current.

configurator/src/components/editors/StudioFrame.svelte[5-8]
configurator/src/components/editors/StudioFrame.svelte[18-28]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`StudioFrame` computes `selectedPanelId` and `activePanel` using different fallback rules. If `activePanelId` is set to a value not present in `panels` (e.g. panels prop changes, HMR, conditional panels), `activePanel` falls back to `panels[0]` but `selectedPanelId` remains stale, so no button matches `selectedPanelId`.

### Issue Context
Current code:
- `selectedPanelId = activePanelId || panels[0]?.id || panels[0]?.label || ''`
- `activePanel = panels.find(...) ?? panels[0]`
- Nav active state uses `(panel.id ?? panel.label) === selectedPanelId`

### Fix Focus Areas
- configurator/src/components/editors/StudioFrame.svelte[5-8]
- configurator/src/components/editors/StudioFrame.svelte[18-30]

### Suggested fix
Option A (derive a validated selection):
- Compute `const candidate = activePanelId || (panels[0]?.id ?? panels[0]?.label ?? '')`
- Compute `const selectedPanelId = $derived(panels.some(p => (p.id ?? p.label) === candidate) ? candidate : (panels[0]?.id ?? panels[0]?.label ?? ''))`
- Then derive `activePanel` from `selectedPanelId` without a separate `?? panels[0]` fallback.

Option B (auto-reset state):
- Add an `$effect` that resets `activePanelId` to '' (or the first panel id) if it doesn't exist in `panels`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unused third column reserved 🐞 Bug ≡ Correctness
Description
StudioFrame always uses a 3-column grid, but only conditionally renders the right controls
<aside>; callers without panels/controls render an empty third column that shrinks the preview
canvas on wide screens.
Code

configurator/src/components/editors/StudioFrame.svelte[R49-51]

+  .studio { display: grid; grid-template-columns: minmax(170px, .26fr) minmax(0, 1fr) minmax(250px, .34fr); gap: 18px; padding: 18px; border: 1px solid var(--cfg-border); border-radius: var(--cfg-radius-l); background: radial-gradient(circle at 0 0, color-mix(in oklab, var(--cfg-accent) 16%, transparent), transparent 34%), linear-gradient(135deg, color-mix(in oklab, var(--cfg-accent) 8%, transparent), transparent 48%), var(--cfg-surface); overflow: hidden; }
+  .studio__nav, .studio__panel { position: sticky; top: 10px; align-self: start; display: grid; gap: 14px; min-width: 0; }
+  .studio__copy { display: grid; gap: 10px; }
Relevance

⭐⭐ Medium

Layout/ergonomics fixes are common, but no explicit precedent on conditional grid columns vs
always-3-column StudioFrame.

PR-#403
PR-#312

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The grid always allocates a third column, while the right-side panel is rendered only when
controls exists. Multiple existing studios still invoke StudioFrame without passing
panels/controls, so they will display with an empty third column.

configurator/src/components/editors/StudioFrame.svelte[36-45]
configurator/src/components/editors/StudioFrame.svelte[49-64]
configurator/src/components/editors/ColorStudio.svelte[25-27]
configurator/src/components/editors/SpacingStudio.svelte[11-13]
configurator/src/components/editors/TypographyStudio.svelte[16-18]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`StudioFrame` reserves space for a right-side controls panel via a fixed 3-column CSS grid, but the controls panel is only rendered when `controls` is provided. Studios that still call `<StudioFrame>` without `panels`/`controls` (e.g. Typography/Color/Spacing) end up with an empty third column.

### Issue Context
- `StudioFrame` renders `.studio` with `grid-template-columns: ... ... ...` unconditionally.
- The controls `<aside class="studio__panel">` is rendered only when `activePanel && controls`.
- Some studios still use the old pattern (no `panels` / no `controls` snippet), so this is a compatibility regression.

### Fix Focus Areas
- configurator/src/components/editors/StudioFrame.svelte[4-8]
- configurator/src/components/editors/StudioFrame.svelte[36-45]
- configurator/src/components/editors/StudioFrame.svelte[49-65]

### Suggested fix
- Compute `const hasControls = $derived(Boolean(controls) && Boolean(activePanel));`
- Add a class like `class:studio--with-controls={hasControls}` (or `studio--no-controls`).
- Use 2-column grid when `!hasControls` (and optionally when `!panels.length`), and 3-column grid only when the right panel is actually rendered.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
configurator/src/components/editors/EffectsStudio.svelte (1)

35-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Optional: the controls snippet is identical across all five studios.

{#snippet controls(activePanel)}<StudioControls groups={activePanel.groups} />{/snippet} is duplicated in EffectsStudio, LayoutStudio, ShapeStudio, ShadowStudio, and MotionStudio. Since the body is the same in every consumer, consider rendering StudioControls from the active panel inside StudioFrame (with a slot/prop opt-out) so consumers only pass panels. This removes the repeated wiring and the per-file StudioControls import.

🤖 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` around lines 35 -
37, The `controls` snippet is duplicated across the studio components, so move
the `StudioControls` rendering into `StudioFrame` and let it render from the
active panel by default. Update `StudioFrame` to accept the needed panel data
(with a slot/prop opt-out for custom controls), then remove the repeated
`controls` snippet and `StudioControls` import from `EffectsStudio` and the
other studio consumers so they only pass `panels`.
🤖 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.

Nitpick comments:
In `@configurator/src/components/editors/EffectsStudio.svelte`:
- Around line 35-37: The `controls` snippet is duplicated across the studio
components, so move the `StudioControls` rendering into `StudioFrame` and let it
render from the active panel by default. Update `StudioFrame` to accept the
needed panel data (with a slot/prop opt-out for custom controls), then remove
the repeated `controls` snippet and `StudioControls` import from `EffectsStudio`
and the other studio consumers so they only pass `panels`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66a4e69c-4346-4cb7-8252-9c5bfb59eaa8

📥 Commits

Reviewing files that changed from the base of the PR and between f391ec2 and 971bd94.

📒 Files selected for processing (11)
  • configurator/src/components/ControlSection.svelte
  • configurator/src/components/DomainPanel.svelte
  • configurator/src/components/editors/EffectsStudio.svelte
  • configurator/src/components/editors/LayoutStudio.svelte
  • configurator/src/components/editors/MotionStudio.svelte
  • configurator/src/components/editors/ShadowStudio.svelte
  • configurator/src/components/editors/ShapeStudio.svelte
  • configurator/src/components/editors/StudioFrame.svelte
  • configurator/src/lib/studioSchema.js
  • configurator/tests-components/studios.test.js
  • configurator/tests/studio-schema.test.js

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant