Repository navigation
Conversation
Studio's variant tabs preview variant N through the ?__draft= pointer instead of a request header. The pointer's query carries reserved __variant parameters, `<block>@<path>=<index>` URL-encoded: the saved block that holds the multivariate, its JSON path inside that block, and the variant to show. - parseDraftPointer lifts them out of `path` into `variants`; formatDraftPointer appends them. A malformed one rejects the pointer. - cms.forDraft applies them to the snapshot it reads, even over a source with no drafts (the content module): each addressed multivariate keeps only the forced variant with rule `true`, so no rule runs. The snapshot is copied along the addressed paths only; a stale address is ignored. - Loaders get the pointer without them, so every variant of one draft shares one load and one cache entry. forRelease never forces anything. - Migration skill gotchas: the two template pieces local editing needs (cms.ts taking a new content module via import.meta.hot, and a Vite plugin reloading pages when .deco/blocks.gen.ts changes). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Studio's variant tabs now preview variant N on a v8 site. They do it through the
?__draft=preview pointer. There is no new request API and no header: v8 never readx-deco-matchers-override.Design
<savedBlock>@<jsonPath>=<index>, like v7's[email protected]. It names the saved block that holds the multivariate, the JSON path to the multivariate inside it, and the variant index. It travels in the pointer's query as a reserved__variantparameter, URL-encoded, one per forced variant. Example of a localdeco servepointer:127.0.0.1:4547/rpc?__variant=pages-home%40sections.5%3D1@local.parseDraftPointer/formatDraftPointer(packages/blocks/src/v8/draft.ts). Parsing lifts the__variantparameters out ofpathinto a newDraftPointer.variants, and formatting appends them. A malformed__variantrejects the whole pointer.cms.forDraft(pointer)(cms.ts,content.ts,variants.ts) applies the forced variants to the snapshot it reads. This works even when the source has no drafts, such as the content module. Each addressed multivariate keeps only the forced variant, with ruletrue, so no rule runs. The snapshot is copied only along the addressed paths. An address that no longer exists is ignored.forReleasenever forces anything. Only preview requests can force a variant. Variants are not access control, so a variant forced from the URL is fine, and the docs say so.cms.tstaking a new content module throughimport.meta.hot, and a dev Vite plugin that reloads open pages when.deco/blocks.gen.tschanges. Both match what storefront-tanstack does. They are documented as recipes and added to the migration skill's gotchas (.agents/skills/deco-v7-to-v8-migration/reference/gotchas.md). No helper was added to a package.Evidence
bun run checkpasses (typecheck, biome, knip, secrets audit).bun run test: 1762 passed, 104 skipped. New tests are indraft.test.ts,variants.test.tsand__conformance__/sdk.test.ts.feat/blocks-v8-support, feat: support Blocks v8 (content protocol) behind a flag studio#7728) and storefront-tanstack (feat/next-major, unchanged) linked to this branch, driven with Playwright. A Banner in section 5 ofpages-homewas made multivariate in Studio:?__draft=off127.0.0.1:4547/rpc?__variant=pages-home%40sections.5%3D1@local%3D0=1pointerAfter the content was put back, the old pointer still returned a 200 with the saved content, so a stale variant address is ignored.
createCMSkeeps one CMS per process onglobalThis, so a linked dev server keeps old CMS code until its worker restarts. Touchwrangler.jsoncor restartvite dev. A site that upgrades the package is not affected.Open question
Going back from a variant tab to the section list keeps that variant forced. Studio does this on purpose ("Merely entering/auto-selecting a section must not re-navigate the iframe"), and v7 behaves the same way. Dropping the forced variant on leaving the section would be a small Studio change.
Docs: deco-sites/docs-tanstack#4 (releases-and-drafts "Preview a variant", api-reference
DraftPointer.variants, matchers-and-variants, studio-compatibility, content-protocol, and the TanStack Start and Next.js guides).Stack: v8 ← … ← 14 ← 15
🤖 Generated with Claude Code
https://claude.ai/code/session_01WNwbSEePYNcY5YCgqZURig
Summary by cubic
Studio's variant tabs now preview variants on a v8 site through the
?__draft=pointer instead of a request header. A forced variant is a__variantquery parameter —<block>@<jsonPath>=<index>, URL-encoded — naming the saved block, the JSON path inside it, and the variant index.parseDraftPointerlifts it out ofpathintoDraftPointer.variants; a malformed one rejects the whole pointer.cms.forDraftapplies the forced variants to the snapshot it reads, even when the source has no drafts; each addressed multivariate keeps only the forced variant with ruletrue, so no rule runs, and a stale address is ignored. Loaders get the pointer without the variants, so every variant of one draft shares one load and cache entry.forReleasenever forces anything.Local editing recipes
cms.tsmust accept a new content module throughimport.meta.hot, and a Vite plugin must reload pages when.deco/blocks.gen.tschanges; both are documented as recipes in the migration skill's gotchas.createCMSkeeps one CMS per process onglobalThis, so a linked dev server keeps old CMS code until a worker restart (touchwrangler.jsoncor restartvite dev).Written for commit f25d72f. Summary will update on new commits.