fix(studio): code-review and live-test fixes for the variables stack#2052
Conversation
5751d71 to
e8b75b7
Compare
8254c95 to
5b89882
Compare
94a938e to
b8f24c8
Compare
68ddf49 to
d01f9cb
Compare
88044b9 to
eb91f17
Compare
d01f9cb to
523c70f
Compare
eb91f17 to
3976c7e
Compare
523c70f to
20e00af
Compare
miga-heygen
left a comment
There was a problem hiding this comment.
LGTM — all fixes address real bugs with correct root causes.
The varDecl before var ordering fix in applyOverrideSet is the most important: without it, snapshot-vs-value ordering was non-deterministic. The cssCompatChange routing in handleSetVariableValue is a clean SSOT consolidation (two paths that each had to know about CSS compat props become one call, and the stale-scalar-on-object-write bug is fixed as a consequence). The useStudioSdkSessions extraction correctly gives the master view a live SDK session while preserving edit-flow gating, and the preview-variable clearing on composition/project change prevents cross-composition value leaks.
Clean work.
— Miga
miguel-heygen
left a comment
There was a problem hiding this comment.
Stamped after Miga's stack review. GitHub state checked: mergeable, no red checks visible; #2046 base conflict still needs resolution separately.
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at 3976c7e. Stack context: studio-UI lane (#2050/#2051/#2052/#2055/#2071) — 5-of-12 in template-variables stack.
Layering on Miga's review — Miga covered the applyOverrideSet sort, cssCompatChange consolidation, and useStudioSdkSessions master-view fix. Two follow-ups on the fallback.
🟠 Hold
-
useStudioSdkSessions(projectId, activeCompPath ?? "index.html", ...)— behavior when the project has noindex.html? The body's justification is "single-file projects — the primary use case," which impliesindex.htmlis a naming convention. Butpackages/parsers/src/composition.tsand the compositions test fixtures across the repo don't hard-require the filename — templates can usecard.html,main.html,hero.html, etc. Two questions this raises:- What does
useSdkSessiondo when passed a path that doesn't exist? Silent-return-null (Variables panel just shows "Open a composition"), throw an error (studio crash), or open on an empty document (worse — user edits, save writes to a phantomindex.html)? - The
editFlowSdkSession = activeCompPath ? sdkHandle.session : nullgating keeps the pre-#2052 behavior for edit consumers — good — but the Variables panel and the "Handoff footer" in #2051 both bypass that gate. On acard.html-only project, the panel is now writing toindex.html, and #2051's copy-command usesactiveCompPath ?? "index.html"— the user copies a command referencing a file that doesn't exist.
Suggested resolution: fall back to "first
.htmlin the project root at the sort-order the composition browser uses" rather than the hardcoded"index.html", OR keep the panel's session-null branch ("Open a composition to manage its variables.") active wheneveractiveCompPathis null ANDindex.htmlisn't confirmed on disk. The current code makes the master view work for the 80% case but silently mis-targets the 20%. - What does
🟡 Nits
-
studioUrlState.ts'sVALID_TABS— this PR adds"variables"to the allowlist, but"slideshow"remains missing from the list even though it's a validRightPanelTab. Pre-existing gap, not a regression of this PR, but since the file is being touched anyway it'd be a two-character win to add it. (If deliberately excluded because slideshow deep links break something else, worth a code comment.) -
applyOverrideSet's new sort — the comparator only ordersvarDecl.*beforevar.*. Other keys (style.*,attr.*) go through with return-0, so their relative order is stable-insertion. The sort is stable in V8/JSC (ECMA-262 spec compliance since 2018), so this is safe today; if you ever run this in an older JS engine (jsdom pre-2018?), an unstable sort could permute non-var keys. Not blocking — noting for future-proofing. -
cssCompatChangeroute now handles the object-value-clears-stale-scalar case implicitly. Nice consolidation. The one edge case to double-check: if a variable's default was previously scalar-set (writes--{id}to the root style), then a subsequentsetVariableValue(id, {name: ...})(object) is issued — the compat channel clears viaString(null)... actually looking again, thecssCompatChangeis passedisObjectVariableValue(value) ? null : String(value), so null triggers the clear path. Good. Worth a one-line test that exercises the scalar-to-object transition and asserts the CSS prop is gone on the root after.
🟢 Good
useStudioSdkSessionscleanly splits "session everyone can read" from "session only edit consumers write via" — the cutover is a small, well-labeled change to the App.tsx call sites (sdkHandle.session→editFlowSdkSessionat 3 spots). Grep-friendly rename.- The preview-variables clear on
[projectId, activeCompPath]change is exactly the right leak-prevention primitive. Values are per-composition; clearing on either identity change kills the two failure modes (project switch, composition switch within a project) in one hook. _collectCssTextextraction is a nice cache-friendly refactor — the content-keyed cache in #2050's diff mentioned by the body would benefit from the extraction.- The
varDeclbeforevarordering fix is the highest-value change here — I'd expect Vance to write a comment on the order guarantee at the call site ofapplyOverrideSetif there isn't one already, so future patch authors don't accidentally revert this via a "clean up the sort" refactor.
vanceingalls
left a comment
There was a problem hiding this comment.
R1 — hyperframes #2052 at 3976c7ea
🟢 LGTM on the fix-set correctness, concurring with Miga; concur with Rames-D's 🟠 Hold on the hardcoded "index.html" fallback — real gap for non-conventional filenames.
Verified independently, seven discrete fixes:
- SDK
applyOverrideSetpatch ordering (apply-patches.ts) — sortvarDecl.{id}beforevar.{id}. Real bug (snapshot could clobber fresher value), correct fix. - SDK
cssCompatChangeunification (mutate.ts) — shared helper wired through declare/update/remove. CI validates the definition exists. - SDK
_collectCssTextextraction — CSS-var usage scan dedup. Refactor. - Studio
useStudioSdkSessionsfallback — opens session withactiveCompPath ?? "index.html"for schema panels.editFlowSdkSession = activeCompPath ? sdkHandle.session : nullpreserves legacy null gating on edit-flow. (Concurring hold below.) - Studio
usePreviewDocumentVersionextraction — refresh + pending-timer collapse pulled fromApp.tsx. Refactor. - Studio preview cleanup on project/comp change —
setValues(null)onprojectId/activeCompPathchange. Real bug (override leak between comps), closes the invariant I flagged pending on #2050. - Studio
?tab=variablesdeep-link allowlist — was being stripped by normalization. Real bug, one-liner.
CI green across SDK 435 / Studio 1392 / Studio-Server 259.
Concurring with Miga's LGTM on the fix-set correctness.
Concurring with Rames-D's 🟠 Hold:
- Hardcoded
"index.html"fallback breaks on projects with a different entry filename. Rames-D's read is right:packages/parsers/src/composition.tsand the compositions test fixtures don't require theindex.htmlfilename — templates can usecard.html,main.html,hero.html. Two failure modes:- If
useSdkSessionsilent-return-nulls on a nonexistent path: Variables panel shows "Open a composition" perpetually on a validcard.html-only project. - If it opens on an empty document: the panel writes declarations to a phantom
index.html, silently corrupting the project on save.
- If
- The
editFlowSdkSession = activeCompPath ? sdkHandle.session : nullgating keeps the pre-#2052 behavior for edit consumers, but the Variables panel + #2051's Handoff footer both bypass that gate. On acard.html-only project, #2051's copy-command references a file that doesn't exist.
Concur on Rames-D's suggested resolution: fall back to "first .html in the project root at the sort-order the composition browser uses" rather than a hardcoded literal, OR keep the panel's session-null branch active whenever activeCompPath is null AND index.html isn't confirmed on disk. Current code works for the conventional 80%; the 20% silently mis-targets.
No decorative-gate patterns in the remaining six fixes — read + populate paths trace cleanly.
R1 by Via
20e00af to
84b75ab
Compare
3976c7e to
a5b0960
Compare
84b75ab to
81b68d7
Compare
a5b0960 to
48ac8f8
Compare
|
Addressed R1 🟠 Hold (hardcoded |
81b68d7 to
db4a9e0
Compare
0d987ff to
9d2b297
Compare
db4a9e0 to
0208ce5
Compare
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
R2 at 9d2b297.
Prior R1 🟠 (useStudioSdkSessions(projectId, activeCompPath ?? "index.html", …) silently mistargets template projects that use card.html / main.html / etc.) resolved at useStudioSdkSessions.ts:17-31: hook now takes a masterCompPath parameter resolved via resolveMasterCompositionPath(fileTree) (first .html in the tree), and coalesces activeCompPath ?? masterCompPath — no more hardcoded "index.html". Docstring at line 23-26 explicitly calls out "the first .html in the tree, not a hardcoded 'index.html' that may not exist." Projects with no composition file yet correctly leave the session null (session and panels empty).
No residuals from my side. CI green.
vanceingalls
left a comment
There was a problem hiding this comment.
🟢 LGTM concurring with Rames-D — R2 at 9d2b297.
R1 🟠 hold (hardcoded "index.html" fallback in useStudioSdkSessions — card.html / main.html-only projects show empty Variables panel or write declarations to a phantom file) resolved via Rames-D's proposed option (a): first .html in project root at composition-browser sort order, with belt-and-braces session-null gating when the project has zero .html files.
Verified at head:
packages/studio/src/hooks/useStudioSdkSessions.ts:17-31— hook now acceptsmasterCompPath: string | nulland usesuseSdkSession(projectId, activeCompPath ?? masterCompPath, ...). Hardcoded literal is gone.- Docstring at lines 23-26: "the first
.htmlin the tree, not a hardcoded 'index.html' that may not exist.nullwhen the project has no composition yet, which correctly leaves the session (and the panels) empty." packages/studio/src/App.tsx:139-148— suppliesmasterCompPathviaresolveMasterCompositionPath(fileManager.fileTree), memoized on file-tree changes.packages/studio/src/utils/studioUrlState.ts— newresolveMasterCompositionPathhelper: prefersindex.htmlwhen present, falls back to first.htmlat composition-browser sort order.studioUrlState.test.tsnewdescribe("resolveMasterCompositionPath")block covers the sort-order preference.
No residuals from my side.
R2 by Via
0208ce5 to
924b306
Compare
9d2b297 to
3878270
Compare
924b306 to
30b38ab
Compare
3878270 to
255ca1f
Compare
vanceingalls
left a comment
There was a problem hiding this comment.
🟢 LGTM at R3 — 3878270.
R2 hold preserved through the rebase-onto-#2098. R2 verdict stands.
Verified at head:
packages/studio/src/hooks/useStudioSdkSessions.ts:20—masterCompPath: string | null,4th prop preserved.:30—useSdkSession(projectId, activeCompPath ?? masterCompPath, ...)fallback wire preserved.- JSDoc at
:4-14documenting master-view + preview-clearing semantics intact.
resolveMasterCompositionPath(fileTree) caller-side signature also intact.
R3 by Via
30b38ab to
ea6cd91
Compare
255ca1f to
a7bfb94
Compare
a7bfb94 to
bc0e0b3
Compare
ea6cd91 to
010d493
Compare

What
Top of the template-variables stack: fixes from an 8-angle code review + a full in-browser test pass over #2046–#2051. Hunks touching stack-authored lines were absorbed into their PRs (
gt absorb); this PR carries the fixes that touch pre-stack code.activeCompPathis null there), so the Variables/Slideshow panels were dead on single-file projects — the primary use case.useStudioSdkSessionsopens the session with anindex.htmlfallback while edit-flow consumers keep the legacy null gating (cutover behavior unchanged). Found by driving the panel in a real browser"variables"registered in the URL-state tab allowlist so?tab=variablesdeep links survive normalization; preview variable overrides are cleared when the composition or project changes so values can't leak into another composition's preview/renderusePreviewDocumentVersionextracted from App.tsx (file-size gate)handleSetVariableValue's CSS compat sync routed throughcssCompatChange— one implementation of the--{id}channel; object values now clear a stale scalar prop instead of leaving it behindgetVariableUsagescript scansHow it was tested
The whole stack was exercised end-to-end against a live studio session (local
agent-browserdriving a real project): every control type committing to the preview, override pill/reset semantics, Set-default → disk → undo round-trip (verbatim file restore incl. CSS compat prop), add/remove declarations → disk,?tab=variablesdeep links, HTTP injection/400s/ETag variance, and wire-level render forwarding. Review verdicts: 13 confirmed findings (9 fixed across the stack, others tracked), 4 candidates refuted by verification.Test plan
🤖 Generated with Claude Code