Commit Graph
4165 Commits
Author SHA1 Message Date
Oleg Isonen 3d0f19ffe0 fix: complete and speed up publish validation (#6478)
## Summary

Complete Validate for large projects with linked MDX content, using the
same validation API in the dialog and MCP. Publishing continues
independently of diagnostics.

- Load MDX sources omitted from the embedded artifact and report storage
failures with typed diagnostic reasons.
- Reuse parsed MDX and template scope indexes; cache source reads in the
shared asset loader.
- Exclude conservative non-MDX query candidates while retaining explicit
invalid references for diagnostics.
- Give validation requests a 60-second server execution budget.
- Support both server bundles in E2E tests and make OAuth logging
initialization repeatable.
- Raise the project bundle import limit from 20 to 32 MiB to reproduce
the customer project.

## TODO / acceptance checklist

- [x] Import and inspect the customer project locally and on staging.
- [x] Reproduce missing embedded MDX sources and slow template
materialization.
- [x] Load omitted MDX sources and discover nested template
dependencies.
- [x] Report source-read failures using typed reasons rather than
matching error messages.
- [x] Reuse template scope indexes and remove duplicate source caching.
- [x] Route dialog Validate through the same API as MCP, after flushing
editor content.
- [x] Preserve publishing in parallel with diagnostics, including failed
or unfinished checks.
- [x] Fix JSON collection files being treated as MDX query candidates.
- [x] Preserve explicit invalid file references so they still produce
diagnostics.
- [x] Fix E2E routing across both generated server bundles and repeated
OAuth logging initialization.
- [x] Demonstrate the new review regressions fail before the fixes and
pass after them.
- [x] Run targeted tests, relevant typechecks, lint, formatting, package
boundaries, and generated API checks.
- [x] Run the full fixture pipeline and production builder build; no
generated fixture changes.
- [x] Build the exact staging project locally after the final review
changes.
- [x] Validate the exact staging project through MCP/CLI after the final
deployment.
- [x] Complete an actual staging publish after the final deployment.
- [x] Address PR comments: simplify the stream mock, explain every
diagnostic reason, and retain the existing diagnostic code contract.
- [x] Review documentation impact; these fixes restore expected behavior
and require no user instruction changes.
- [x] Finish CI on the final commit, including all six Builder E2E
shards.

## Verification

Local checks passed: 179 project-build tests, 78 CLI prebuild tests, 8
asset publication tests, 42 server tests, and 9 publish UI/helper tests.
The imported staging project also builds successfully.

The new explicit-reference regression and repeated-initialization
regression both failed before their fixes and passed afterward. A
production build serves the login route and dispatches tRPC requests to
its separate server bundle. Validation on staging project
`b9d23e54-5dbf-45c4-b6d8-bc4cf842a468` completed in 25.3 seconds after
deployment and 23.5 seconds after publishing: 92 documents included, 0
omitted, and 0 MDX errors. The [original staging publish job
rerun](https://github.com/webstudio-is/webstudio-saas/actions/runs/36647770120/job/109819143695)
succeeded end to end with the final artifact, including CLI/Remix
builds, worker deployment, and database updates. [CI on commit
`01a9d772dc`](https://github.com/webstudio-is/webstudio/actions/runs/36696154831)
is green, including all six Builder E2E shards and Windows/Ubuntu
fixture checks.

The comment follow-ups simplify the publication test mock and document
all diagnostic reasons. Production behavior is unchanged. The 8
publication tests, 6 schema tests, package typecheck, formatter, and
lint pass. Disabling the source cache makes the simplified regression
fail as expected. Final staging validation passed in 27.1 seconds with
92 documents and no omissions or MDX errors. One additional publish
attempt hit a database statement timeout during project sync, before the
build. A [retry with no code changes succeeded end to
end](https://github.com/webstudio-is/webstudio-saas/actions/runs/36647770120/job/109826767589).
CI is green on `01a9d772dc`.

Ref #3550
2026-09-30 11:40:15 +02:00
Oleg Isonen e700a24210 fix: run publish checks in background (#6476)
## Summary
- Start dynamic and static publish diagnostics in the background,
without waiting for them before sending the publish request.
- Error findings, failed or hanging checks, and failures while reporting
them cannot block or delay publishing.
- Keep the standalone Validate action's reporting behavior unchanged.
- If diagnostics fail, the dialog explains that the publish request was
sent without waiting for them.
- The publish operation remains authoritative for authorization and
publish-job failures.

## Verification
- `pnpm --filter @webstudio-is/builder test --
app/builder/features/publish/publish-preflight.test.ts
app/builder/features/publish/publish-error.test.ts` (8 tests passed)
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm exec oxfmt` on changed files
- `git diff --check`
2026-09-29 20:03:43 +02:00
Oleg Isonen a6a9049399 fix: validate published MDX before publish (#6468)
## Related issue

Related to #3550.

## Summary
- Run the existing published-MDX materializer during pre-publish
validation and surface its error diagnostics before starting dynamic or
static publishing.
- Reuse the dependency walk that builds the publication artifact to
limit validation to reachable Content Blocks, including blocks whose
asset is missing.
- Add actionable fix hints for unavailable MDX assets and invalid HTML
nesting.
- Show a compact error/warning count in the publish dialog, with a
details dialog that has counted Errors and Warnings tabs and hides empty
tabs.
- Include relevant IDs and navigation links in reports when available,
with guidance when they are not; allow users to copy all reports and
select the text.
- Resolve missing-document IDs against loaded asset metadata and show
the filename and Content Assets folder path when available.

The page-tree pre-publish audit already checks HTML content models, but
it cannot see content generated from MDX assets. This extends preflight
to that content using the same materializer and diagnostics as the
publish build. It also catches unavailable MDX assets before the publish
job starts.

## Plan and status

### Implemented
- [x] Reuse existing publish-time MDX discovery and materialization so
preflight checks the content that will be published.
- [x] Collect every page-tree audit error and warning, not only the
first finding for each severity.
- [x] Collect every MDX publish error and provide a location and
remediation hint.
- [x] Collect missing or ambiguous template warnings and
content-database omission warnings.
- [x] Show a compact error/warning total in the publish dialog.
- [x] Add a details dialog with standard panel padding, spaced and
separated findings, and counted Errors and Warnings tabs; hide empty
tabs.
- [x] Copy all reports from the dialog header and allow report text
selection.
- [x] Include diagnostic codes and relevant instance, asset, document,
and resource IDs in reports.
- [x] Keep “See issues” visible in the narrow publish panel.
- [x] Keep element navigation available from page-tree audit findings
and link content findings when the target is available.
- [x] Use the same findings UI for dynamic and static publishing.
- [x] Block publishing on errors and allow warnings to proceed.
- [x] Keep unexpected preflight failures visible to the user.
- [x] Show a preflight failure either in the open publish dialog or in a
toast when the dialog is closed, avoiding duplicate reports.
- [x] Replace opaque “Document … could not be loaded” errors with
actionable guidance, including the asset filename and folder path when
available.

- [x] Load linked document sources during MDX validation and prebuild
when they are absent from the compact content artifact.

### Regression checks completed
- [x] No-findings validation preserves the normal publish path.
- [x] Single and multiple findings show accurate totals and details.
- [x] The “See issues” action is visible in the publish panel and opens
diagnostics.
- [x] “Show element” navigation remains wired to the affected element
and closes the publish dialog.
- [x] More than five MDX errors and more than ten missing-template
warnings remain available in diagnostics.
- [x] Mixed MDX errors, audit errors, template warnings, and database
warnings are combined and counted.
- [x] Errors stop dynamic and static publishing; warnings do not block
publishing.
- [x] Error-only, warning-only, and mixed states show the right tabs and
counts, with empty tabs hidden.
- [x] Copy-all includes every error and warning with context, and report
text is selectable.
- [x] An open dialog receives inline preflight failures without a toast;
a closed dialog receives a toast without a duplicate inline report.
- [x] Missing-document errors include filename/folder context when
available and useful fallback guidance when metadata is unavailable.

- [x] Reproduce the linked-author failure with the affected project’s
hosted build, then confirm it builds successfully with the fix.

## Verification
- `pnpm --filter @webstudio-is/project-build test --
src/runtime/published-mdx.test.ts` (26 tests passed)
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter webstudio typecheck`
- Affected project: `webstudio build --template react-router` reproduced
the document load error before the fix and completed after it.
- `pnpm --filter @webstudio-is/builder test --
app/builder/features/publish/publish-validation-results.test.tsx
app/builder/features/publish/content-database-publish-warning.test.tsx
app/builder/features/publish/publish-error.test.ts
app/services/build-router.server.test.ts` (26 tests passed)
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm exec oxfmt --check` on changed files
- `git diff --check`
- Reviewed dynamic/static preflight and element-navigation code paths.
2026-09-29 18:27:20 +02:00
Oleg Isonen adf1fd3aaf fix: show publish status only for selected domain (#6473)
## Summary
- Show the project-domain publish status only when the latest build
targets that domain.
- Keep the staging checkbox default tied to a staging publish.

A pending publish to a custom domain no longer makes the staging domain
appear to be publishing.

## Verification
- `pnpm -r typecheck` passed locally.
- GitHub Actions passed: checks, test matrix, builder end to end shards,
visual regression, lint, and submodule check.
2026-09-29 15:19:00 +02:00
Oleg Isonen e1e5bc152e feat: append response header rules for last-match precedence (#6460)
Response headers now use list order when more than one rule sets the
same name on a path: the last matching rule wins. New or edited rules
currently move to the top of the Builder list, which would make older
rules override them. Append those rules instead and explain the
precedence in the settings UI. Authentication list behavior is
unchanged.

Validation: SDK response-header tests and typecheck, CLI response-header
tests, SDK build and declaration build passed.
2026-09-28 14:10:59 +02:00
Oleg Isonen 9b1100f25b fix: Restore hosted Assets queries (#6454)
Hosted Assets queries started returning 502 in 0.301.0 because publish
sync omitted their query database. Restore that database for hosted
builds while keeping Markdown body-reference queries on demand. Static
exports still prepare their database in the build runner.

Reject builds with Assets queries but no database before changing
generated files, so this failure cannot silently produce another broken
deployment or erase the previous generated output. Uses the existing
shared query compiler; no MCP-specific logic or API schema changes.

Closes #6450
Ref #6427

Replaces #6453 with the same commits on `assets.staging`, which matches
the staging deployment branch policy.

## Checklist

- [x] Reproduce the reported 502 and verify the fix on project and
custom origins.
- [x] Preserve static export behavior and on-demand Markdown loading.
- [x] Add a regression test for rejecting missing databases; demonstrate
failure before the fix and success afterward.
- [x] Review documentation impact and update the Content Engine
publishing explanation.
- [x] Run relevant tests, Builder/CLI typechecks, lint, formatting, and
package-boundary checks.
- [x] Run the full fixture link, sync, and build pipeline against the
patched preview API. Regenerate all eight JSON snapshots with the
current formatter; parsed data is unchanged and generated application
files are unchanged.

Validation: 88 CLI tests, 22 Builder tests, and four no-body-read
publication tests pass. Fixture size checks were unavailable without
compiled app bundles. Agent evaluations were not run. Existing affected
sites need republishing after the fix is deployed.
2026-09-27 00:16:04 +02:00
Oleg Isonen d78e770d82 perf: Keep article indexing out of publish API requests (#6427)
Ref #6416

The publish workflow was doing too much work in one short-lived API
request. This moves content indexing into the build runner and keeps
article bodies out of hosted/SSR sync.

## What changed

- The build-bundle API can return the lightweight project bundle without
preparing a full content index.
- The CLI downloads required assets directly from storage and builds the
index locally for SSG. Asset downloads remain bounded to 10 concurrent
requests.
- Hosted/SSR sync skips Markdown and MDX article downloads and does not
compile an article index. It still downloads regular assets needed by
the build.
- Markdown body queries use stored frontmatter and fetch only the
requested article at runtime. MDX still needs source downloads and
compilation for SSG.
- Public MCP and content-engine docs explain the Markdown/MDX tradeoff.

This removes article parsing and article-body transfer from the tRPC
request. SSG still downloads article sources from storage and compiles
MDX in the build runner. Hosted runtime article reads remain on demand.

## Checklist

- [x] Keep the tRPC bundle request limited to project metadata and
necessary records.
- [x] Prepare SSG content indexes in the build runner; keep hosted
article bodies out of sync.
- [x] Cover no-body query preparation, direct asset reads, and
SSG-versus-hosted sync behavior with regression tests.
- [x] Update public CLI, MCP, and content-engine guidance.
- [x] Regenerate and verify fixtures.
- [x] Run workspace tests, typechecks, lint, formatting,
package-boundary, generated API/docs, and fixture-size checks.
- [ ] Verify the deployed publish flow has comfortable margin below the
hosted API timeout.

## Verification

- `pnpm -r --workspace-concurrency=1 test`: passed, including all 3,082
Builder tests and 1,150 CLI tests.
- `pnpm -r typecheck`, `pnpm lint`, `pnpm check:package-boundaries`,
`pnpm check:generated-api`, `pnpm check:generated-docs`, `pnpm
check:fixture-sizes`, and `pnpm exec oxfmt --check`: passed.
- The new hosted article-download regression test was confirmed to fail
with the old behavior and pass after the fix.
- A deployed end-to-end publish timing check is still needed, so this PR
remains draft.
2026-09-26 11:56:37 +02:00
Oleg Isonen df7ed327ab refactor: clarify pathname rule matchers (#6446)
Authentication and custom response headers use the same route-rule
syntax, but the shared matcher and validator had auth-specific names.
Give those functions generic names in `wsauth` and update the Builder
header UI and SDK response-header schema to call them directly. All
basic-auth logic remains in `wsauth`; this change adds no package or
re-export.

The project-build page matcher is different: it uses `URLPattern` and
returns decoded parameters. Rename it to `matchUrlPattern` and document
the behavior difference. For example, `/docs/*` matches `/docs` for
authentication and headers, but not for the page matcher. Tests cover
both behaviors.

This renames the two public `wsauth` route exports. Published workers
pinned to the older package retain the old API; the SaaS worker can
adopt the new names after this package is released.

Validation: `wsauth` tests, including its packaged consumer test; SDK
page and response-header tests; project-build route and redirect tests;
SDK, `wsauth`, project-build, and Builder typechecks.
2026-09-26 11:28:23 +02:00
Oleg Isonen be74d8d698 fix: Validate response headers and expose MCP rule edits (#6444)
Header rules previously accepted `null` values, which silently turned a
rule into a header-removal instruction even though the settings UI only
offers adding a string value or deleting a rule. Require a nonempty
string throughout the SDK, Builder, CLI generator, and API contract.

Expose configured rules to MCP through `list-response-headers`,
`set-response-header`, and `delete-response-header`. Setting a rule adds
or replaces the header for the same route and case-insensitive name; `/`
and `/*` remain distinct. The Builder UI and MCP operations now share
the same rule-key, edit, and Zod validation logic. The generated JSON
Schema advertises the header-name pattern and nonempty value, while
server-side Zod validation enforces route syntax, forbidden headers,
duplicates, and total size. The generic `update-project-settings`
operation still supports replacing the whole list.

Validation: SDK, project-build runtime and MCP, CLI command and prebuild
tests; SDK, Builder, project-build, CLI, and protocol typechecks; lint,
package boundaries, generated API and docs checks. Local fixture sync
against the development API is blocked by differing bundle versions; PR
CI tests fixtures against its deployed Builder host.
2026-09-25 23:37:07 +02:00
Oleg Isonen a4c3b37fc2 fix: Unblock reported MCP workflows and improve issue diagnostics (#6438)
## What this fixes

MCP authors can reuse an existing design token in JSX fragments, create
valid border shorthands with CSS variables, and bypass a font that never
settles during responsive screenshots. Common fixed page titles with
spaced separators are also accepted. Issue reports now include the
project ID and safe, bounded IDs from the failed operation, plus clearer
response and browser-startup diagnostics.

## Scope and tradeoffs

- `token("name")` attaches an existing design token; an unknown name
returns a not-found error. Empty token definitions from Builder/UI
remain definitions and follow the normal conflict rules.
- Border shorthands map a single unresolved `var()` to the remaining
width, style, or color field when the other fields are explicit.
- `waitForFonts: false` skips font readiness but still waits for stable
layout frames. The default remains unchanged.
- Fixed titles such as `Pricing - Plans` stay literal. Addition such as
`first + second` remains an expression. Wrap ambiguous subtraction in
parentheses, for example `(first - second)`. Other ambiguous title forms
may still need an exact report.
- Only recognized operation-level IDs are attached to a public GitHub
issue. Authored values, raw arguments, URLs, and credentials are
excluded. IDs from later successful diagnostic reads are not captured.
- This does not fix the underlying response-transform failure; it
improves future reports for that case.

Closes #6424
Closes #6425
Closes #6434
Closes #6439
Ref #6435
Ref #6430
Ref #6417

## Verification

- [x] Reproduced the privacy, page-expression, empty-token, and
content-mode token-reference regressions with failing tests, then
confirmed the fixes pass. A regression test also covers parenthesized
subtraction.
- [x] Full protocol, template, vision, project-build, and CLI test
suites pass; focused Builder issue-report and fragment tests pass. The
protocol, CLI, and project-build suites were rerun after the CI fix.
- [x] Relevant package typechecks, lint, package-boundary check, fixture
pipeline, generated API check, and generated docs check pass. Fixture
output has no diff.
- [x] Reviewed the authoritative MCP documentation and regenerated the
API metadata and MCP docs.
- [x] CSS parser regressions cover border, each side shorthand, and
unresolved variables in width, style, and color positions; css-data
tests, typecheck, and lint pass.
- [x] Fixed the local evaluation runner to pass the task and Webstudio
MCP tools to Luna/low. Each of the six fixtures passed at least once;
focused MCP/evaluator tests, CLI and project-build typechecks, lint, and
formatting pass.
- [x] The complete Luna/low suite passed all six fixtures in two
consecutive sequential runs. Baselines remain incompatible with this
model/effort override and were not updated.

## Follow-up

- If another fixed-title form still fails, use the new project-level
report context and the exact title shape to narrow the parser case.
2026-09-25 21:01:43 +02:00
Oleg Isonen 2c9fd16689 feat: configure custom response headers by route (#6429)
## Description

Project Settings starts with an empty response-header rule list. Users
can add valid HTTP response headers for all paths (`/*`) or an
Authentication-style route such as `/private/*`, and remove any
configured rule. The Headers and Authentication views share the
accessible rule-list form. The header form suggests standard
response-header names and common values for the selected header, while
accepting custom names and values. `Set-Cookie` is not suggested because
a rule can store only one value per header and would replace existing
cookies.

`X-Powered-By`, `X-Content-Type-Options`, and
`Strict-Transport-Security` are dispatcher-owned and cannot be
configured. Users may configure `Content-Security-Policy`,
`X-Frame-Options`, and `Referrer-Policy`; when absent, the dispatcher
supplies their defaults. The SDK validates header names, values, routes,
duplicates, and size limits. The CLI emits only configured rules.

Customization requires Pro for custom-domain publishing, while unchanged
defaults and staging remain available on Free plans. The server checks
the saved production snapshot against the project owner's plan.

Companion Cloud worker PR:
https://github.com/webstudio-is/webstudio-saas/pull/664. Deploy its
dispatcher support before exposing these settings in the Builder. Other
hosts need an adapter that applies the generated rules; Webstudio Cloud
dispatcher defaults do not apply automatically elsewhere. Static exports
do not apply headers automatically.

## Validation

- SDK, CLI, Builder browser, publish-warning, and production-build tests
pass.
- Builder, SDK, and CLI typechecks and lint pass.
- The companion Cloud worker tests, typecheck, and lint pass.

Replaces https://github.com/webstudio-is/webstudio/pull/6428 to use
`headers.staging`.
Closes https://github.com/webstudio-is/webstudio/issues/4748
2026-09-25 16:46:35 +02:00
Oleg Isonen 30b4f994dc fix: restore publish countdown (#6443)
## Summary
- Keep the publish countdown visible while the publish action is in
progress.
- Show the pending spinner after the 60-second countdown expires.

The refactor in `804cd39bc` applied the design system's pending button
state immediately. That state hides the button text, including the
countdown label. Progress still disables the action throughout
publishing, while the pending visual state now waits until the countdown
ends.

## Verification
- `git diff --check`
2026-09-25 14:51:27 +02:00
Oleg Isonen cd64a6627e fix: Load unbound resource previews in the variable dialog (#6432)
New resources can fail to show data in the **New variable** dialog
because a page-plan refresh cancels their preview request before it
finishes.

This change keeps explicitly requested previews alive until the dialog
releases them. Changing inputs, changing resource type, or closing the
dialog cancels obsolete work and prevents old data from showing. Unused
page resources remain lazy; a preview still makes an on-demand request
when **Load data** is clicked. The dialog stays usable while unrelated
page resources are loading.

Follow-up to #6030.

### Checklist
- [x] Reproduce the cancellation with a regression test that fails
before the fix.
- [x] Keep dialog previews independent of page resource discovery, using
the existing loader, cache, and request versions.
- [x] Clear old previews on input changes and release requests on dialog
close.
- [x] Cover new Current date, Sitemap, HTTP, and existing unbound
resources, plus repeated loads and stale responses.
- [x] Keep tests for obsolete page-request cancellation and async
resource dependencies passing.
- [x] Test the unsaved dialog with an unrelated page request pending and
a page-plan recalculation.
- [x] Clear the old preview when switching resource types or starting an
asynchronous body edit.
- [x] Review documentation impact: no update needed; this restores the
documented preview behavior.
- [ ] Confirm GitHub CI passes.

### Verification
- The dialog loading, type-switch, and body-edit regression tests failed
before their fixes and passed afterward.
- 82 focused tests and the settings-panel suite (588 tests) passed.
- Builder typecheck and repository lint passed.
- With preview retention temporarily disabled, all nine new loader
regression cases failed as expected.
2026-09-23 13:03:13 +02:00
Oleg Isonen 9845517029 fix: hydrate published Markdown assets (#6422)
Migrated from #6421 to the short .staging branch name.
2026-09-23 01:01:08 +02:00
Oleg Isonen 3d0a0657aa feat: Support async resource dependencies (#6030)
## Summary

Make `Resource<Document>` the lazy document-resolution abstraction
shared by Content Engine graphs, generated sites, and Builder previews.
Expressions use one async computation interface: referenced resource
documents resolve first, independent requests run concurrently, and
unused resources stay unloaded.

Ref #6029
Ref #6031

## Implementation checklist

- [x] Define one resource contract for synchronous or asynchronous
document resolution.
- [x] Resolve explicit roots lazily with memoization, bounded
concurrency, cancellation, and structured errors.
- [x] Preserve cycle, duplicate, and missing-resource diagnostics.
- [x] Extract resource dependencies with the existing expression AST.
- [x] Discover consumed resource roots from the current page tree and
page metadata.
- [x] Generate a lazy request graph for published page data, including
independent requests.
- [x] Resolve dependency documents before evaluating dependent request
expressions.
- [x] Integrate dependency-first loading with Builder preview caching
and invalidation.
- [x] Expose safe Resource bindings in the editor and reject
self/indirect cycles.
- [x] Adapt REST, GraphQL, Assets, date, and sitemap requests through
the same Resource contract.
- [x] Cover laziness, chains, shared dependencies, concurrency,
invalidation, cancellation, failures, and cycles with fail/pass tests.
- [x] Review documentation impact and document dependent Resources in
the Data variables guide.
- [x] Track collection-item and component-parameter-dependent resources
as follow-up #6031.
- [x] Regenerate and review affected fixtures.
- [x] Resolve action-resource request dependencies when an action is
invoked.
- [x] Reproduce and fix cached form submissions and multi-root
dependency-cycle hangs.
- [x] Document uncached form actions and regenerate affected fixtures.
- [x] Preserve single and multi-selected content until a cut
successfully writes to the clipboard.
- [x] Preserve boolean expression values in the search-exclusion
checkbox.
- [x] Cancel cut deletion if project data changes while preparing or
writing the clipboard; cover content/property edits and project switches
for single and multi-selection, and document the retry behavior.

- [x] Validate pending cuts after the final await, immediately before
deletion, and reproduce the queued-update race for single and
multi-selection.

## Technical direction

The persisted, revisioned `DocumentGraph` remains the Content Engine
artifact. `Resource<Document>` is the execution abstraction for
obtaining a document; its resolver evaluates only the dependency closure
reachable from requested roots. Static and remote documents therefore
differ by Resource implementation, not by expression value type.

Generated pages always emit a `ResourceRequestGraph`. Each node owns its
output name, dependencies, and request factory. Roots come from resource
variables actually referenced by the current page's metadata, props,
actions, or expression children. Unused and off-page resources remain
unresolved. The loader still accepts legacy request maps for
compatibility.

Builder preview uses the same page-root discovery as published
generation. Its existing cache remains the lifecycle boundary: cached
dependency documents unlock dependent requests, while invalidation
removes downstream requests until their inputs resolve again. The
separate canvas frame recomputes its local plan from the synchronized
cache so rendered expressions receive those documents.

Action requests and their dependencies resolve when a form is submitted.
Each submission sends a new action request, bypassing Webstudio's
resource cache. Dependency requests still use their configured cache
lifetime.

## Review simplifications

- Consolidated page resource-root discovery in the SDK and reused it in
Builder.
- Scoped root and action discovery to the current page tree.
- Removed the redundant immediate-resource helper; `Resource.resolve`
already accepts `Document | Promise<Document>`.
- Moved output names onto graph nodes and removed the duplicate
graph-level map.
- Removed unused planner output and corrected transitive cycle discovery
to include only Resource data sources.
- Regenerated fixtures so independent resources also use lazy graph
execution.
- Reused one abort-aware concurrency limiter for document sessions and
Resource resolution.
- Collapsed duplicate generator action scans and removed redundant
generated-resource bookkeeping.
- Unified legacy request maps and request graphs through one resolution
path.
- Shared the 20-request concurrency limit between SDK, Builder preview,
and the server batch boundary.
- Preserved dependency failure paths and original causes, including the
first cancellation reason.
- Removed invalid-request logging that could expose Resource headers or
request bodies.
- Made editor cycle detection alias-safe by comparing underlying
Resource IDs.
- Memoized page settings values to prevent repeated async validation
renders.

## Verification

Latest local verification for `bbca6395e3`:

- Both queued-update regression cases failed before the fix and passed
afterward. Validation now runs synchronously immediately before
deletion, after the final await, in both cut paths.
- Passed: 255 copy/paste and command tests, Builder typecheck, lint,
package boundaries, formatting, and diff checks.
- Reviewed documentation: the existing copy/paste safety description
remains accurate. Fixture inputs and outputs are unaffected; no fixture
regeneration was needed. Agent evaluations were not run because separate
approval is required.
- CI for the new commit is pending.

Previous local verification for `ffbc75b152`:

- Six regression cases failed before the fix and passed afterward:
content edits, property edits, and project switches during a pending
cut, with both single and multiple selections.
- Passed: 253 copy/paste and command tests, Builder typecheck, lint,
package boundaries, source formatting, and diff checks.
- An unchanged content-block test file initially failed to import in the
browser. Its isolated rerun and the full 253-test rerun passed without
code changes.
- The guard conservatively treats any project-data change as a reason to
keep the clipboard copy without deleting content. It shows a retry
message. Copy/paste documentation now describes this behavior.
- Fixture inputs and outputs are unaffected; fixtures were not
regenerated. Agent evaluations were not run because separate approval is
required. CI for the new commit is pending.

Previous local verification for `3eb580697c`:

- Reproduced both regressions with failing tests before fixing them:
premature deletion during cut and false search exclusion appearing
checked.
- Passed: 203 copy/paste and page-settings tests, Builder typecheck,
lint, package boundaries, formatting, and diff checks.
- Cut coverage includes keyboard and programmatic entry points, single
and multiple selections, delayed successful writes, rejected writes, and
disabled copying.
- Reviewed documentation impact: these fixes restore existing behavior;
no documentation update is needed. Only Builder UI and clipboard files
changed; fixture inputs and outputs are unaffected, so fixtures were not
regenerated for this commit.
- CI status for this older commit was recorded as pending at handoff.
Agent evaluations were not run; they require separate approval.

Previous local verification for `858b348ad`, rebased onto `cf606ca4c`:

- Regression tests failed before each fix and passed afterward: repeated
submissions in both generated route templates, plus three
multi-root/branching cycle cases.
- Passed: 590 SDK tests, 1,129 CLI tests, 72 focused Builder tests,
SDK/CLI/Builder typechecks, lint, package boundaries, and fixture
link/sync/build.
- Initial concurrent runs hit timeouts. The complete CLI suite passed
with one worker; the focused Builder suites passed without file
parallelism.
- Documentation and generated fixture changes reviewed. Agent
evaluations were not run; they require separate approval.
- CI for `858b348ad` passed: Main (including all six E2E shards), Visual
Regression, Lint PR, and Check submodules.

Earlier verification (before this update):

TDD fail/pass evidence includes resolver creation, AST dependency
discovery, published graph reachability, dependency-gated request
generation, Builder dependency-first planning, Resource editor cycle
filtering, page-tree root scoping, off-page action isolation, transitive
Resource-only cycle discovery, abort/timeout precedence, server batch
limits, and the rebased SSG fixture consuming its lazy Resource.

- `pnpm checks` passed after rebasing onto `ce84ddcf12`; it covered
workspace tests, typechecks, lint, package boundaries, generated
API/docs validation, and fixture link/sync/build.
- Earlier focused checks passed: Builder typecheck, 12
page-settings/pages unit tests, lint, formatting, and `git diff
--check`.
- Local generated-resource E2E passed for HTTP, GraphQL, and
current-date resources (3 tests).
- The page-actions and Marketplace page-settings persistence E2Es both
pass locally.
- CI for `6b8b08a70` passed: Main workflow (including all six Builder
E2E shards), Visual Regression, Lint PR, and Check submodules.
- Agent evaluations were not run because they require separate explicit
approval.

## Current behavior and limitations

- A failed dependency blocks its dependents and reports the resource
path and cause.
- One published resolution call is the memoization boundary; Builder
maps dependency readiness onto its versioned request-cache lifecycle.
- Scoped collection-item and component-parameter resources remain in
#6031.
- Builder skips cyclic dependency paths without blocking independent
resources.
2026-09-22 21:04:42 +02:00
Oleg Isonen 804cd39bc2 fix: Stop slow diagnostics from blocking publishing and clarify publish status (#6416)
Closes #6406

## What changes

Publishing no longer waits for advisory content-database and MDX
diagnostics. Those checks run alongside the publish request, so a slow
diagnostic does not prevent publishing from starting.

Permissions, pending content saves, and blocking project checks still
run before publishing. The server continues to enforce publish
authorization and build integrity.

### Publish dialog

- Add a green **Validate** button beside the blue **Publish** button,
with equal widths.
- Show waiting states and disable the actions while their work is
running.
- Let users validate without publishing. Successful validation shows a
persistent “Validation passed. Ready to publish.” toast.
- Clear the button's **Validated** state when the popover closes.
Reopening still reflects an active publish job.
- Move **Add new domain**, **Export**, and **Publish settings** into the
header's ellipsis menu.
- Show yellow progress icons for publishing domains, green checkmarks
for completed publishes, red icons for failures, and neutral icons for
unpublished domains. Built-in and custom domains share the status
mapping.

### CLI and MCP

Add `check-publish-readiness` to check the saved project without
starting a publish. It checks permissions, rejects empty or unusable
domain selections, reuses the existing build-integrity checks, and
returns content diagnostics. Broken resource references no longer
produce a false `valid: true` result.

### Content checks

Run independent collection reads with bounded concurrency instead of
processing every collection sequentially. This reduces serial waiting,
but does not eliminate the underlying storage reads or content
processing.

## Checklist

- [x] Keep authorization and blocking project checks in place.
- [x] Run advisory content diagnostics alongside publishing.
- [x] Add standalone validation, waiting states, and popover-local
validation results.
- [x] Consolidate secondary actions in the publish menu.
- [x] Distinguish pending, completed, failed, and unpublished domain
states.
- [x] Add CLI/MCP readiness checks using existing integrity and
verified-domain logic.
- [x] Bound concurrent collection reads.
- [x] Add regression tests, including invalid domains, missing
resources, status transitions, and popover state.
- [x] Demonstrate the latest regression tests failing without the fixes
and passing with them.
- [x] Review documentation impact: update CLI operation guidance and
metadata, and document domain status colors in the publishing guide.
- [x] Run focused Builder, CLI, and asset-uploader tests, Builder
typechecking, lint, formatting, and the generated-API check.

## Verification and limits

The latest UI check passed all 49 publish tests. The preceding readiness
fix passed 98 focused Builder tests and 130 CLI tests; collection
coverage passed 112 tests. Typechecking, lint, formatting, and
generated-API checks passed. The latest fixes do not affect fixture
inputs or generated site output.

Storybook screenshots and agent evaluations were not run because
approval was not given. A live production publish was not performed
during the latest verification.

Publish can finish before advisory diagnostics do. Returned warnings
appear afterward and cannot stop a publish that has already started.
Standalone validation still waits for diagnostics, and actual publishing
still requires content synchronization, server checks, and
build/deployment work. This does not promise that every publishing
timeout is eliminated.
2026-09-22 17:34:14 +02:00
Oleg Isonen cd1881bde6 fix: upload asset tickets serially (#6414)
## Summary

- Reserve and complete each asset upload before requesting the next
upload ticket.
- Prevent concurrent identical uploads from triggering the server's
in-progress deduplication error.
- Keep the upload result mapping keyed by the folder-scoped upload key.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/builder/shared/assets/upload-assets.test.ts
app/builder/shared/assets/directory-drop.test.ts
app/builder/shared/assets/asset-upload.test.tsx`
- 41 tests passed.
2026-09-18 14:38:13 +02:00
Oleg Isonen 0e30263787 fix: Fix multi-file asset drops and folder-scoped deduplication (#6413)
## Summary
- Preserve all files when dropping multiple local files into Assets.
- Scope client-side duplicate detection by folder.
- Allow the same underlying file to have asset entries in different
folders.

## Validation
- Focused asset upload and directory-drop tests passed locally.
- `git diff --check` passed.
2026-09-18 13:33:00 +02:00
Oleg Isonen e7181fffdc feat: Add GitHub-style Alert component (#6279)
## Summary

- Add one official `Alert` component with a content-editable `variant`
property for note, tip, important, warning, and caution.
- Register Alert in the component catalog with a generated title
descendant and Storybook story.
- Render GitHub-style alerts in Markdown Embed using the same DOM,
class, role, and variant contract.
- Materialize connected Content Block MDX alerts as actual Alert
instances and serialize content or variant edits back to `> [!TYPE]`.
- Document the component and both Markdown workflows.

## Implementation

Alert labels and variants live in Content Engine and are shared across
the component, Markdown renderer, and MDX adapters.

1. The Alert React component owns the wrapper, generated title, stable
classes, `role="note"`, and `data-variant`.
2. Markdown Embed transforms candidate micromark HTML before the
existing sanitizer and emits the same markup contract. It remains
sanitized HTML rather than a Builder instance tree.
3. Content Engine retains alert metadata on parsed Markdown blockquotes.
Project Build maps those nodes to Alert instances, reconciles their
authored children, and restores canonical GitHub syntax during MDX
serialization.
4. The component registry exports one Alert insertion template with a
styleable title descendant.

Unsupported, lowercase, misplaced, and malformed markers remain ordinary
blockquotes. There are no schema, migration, persisted-data, or
sanitizer-policy changes.

## Checklist

- [x] Add Alert as an official component with one Variant property.
- [x] Support all five GitHub alert types.
- [x] Share alert metadata and marker recognition.
- [x] Preserve nested Markdown and ordinary blockquote behavior.
- [x] Keep URL and HTML sanitization in the existing security boundary.
- [x] Add shared, title, and type-specific Markdown Embed descendants.
- [x] Materialize Content Block alerts as Alert instances.
- [x] Preserve alert content and variant changes through MDX
serialization.
- [x] Regenerate component props and Storybook artifacts from source.
- [x] Review documentation impact and update Alert, Markdown Embed,
Content Block, and navigation docs.
- [x] Verify fixture impact; no fixture inputs or generated fixture
outputs are changed.

## Verification

- [x] New tests demonstrated failure before implementation and pass
afterward.
- [x] `pnpm --filter @webstudio-is/content-engine test` — 417 passed.
- [x] `pnpm --filter @webstudio-is/project-build test` — 2,282 passed.
- [x] `pnpm --filter @webstudio-is/sdk-components-react test` — 144
passed, 1 skipped.
- [x] SDK component registry tests.
- [x] Relevant package typechecks, declaration builds, and production
builds.
- [x] Root lint, formatting, package-boundary, generated-doc, and diff
checks.
- [x] GitHub Actions main, E2E, lint, submodule, deployment, and
approved visual-regression checks.

## Known limitations

Alert identifiers must be uppercase and appear at the start of the
blockquote's first paragraph, matching the documented syntax. Markdown
Embed emits the component's markup contract but cannot create Builder
instances inside sanitized HTML.

Closes #6278
2026-09-18 00:42:50 +02:00
Oleg Isonen 18d171212f feat: Resolve asset paths in HTML embeds (#6409)
## Description

- Resolve root-relative script, stylesheet, image, and other asset
attributes in HTML Embed markup.
- Use the same asset-path map in Builder Canvas and generated published
sites.
- Keep SaaS asset proxy URLs deployment-relative instead of hard-coding
the `p-….apps.webstudio.is` project origin.
- Preserve canonical `/cgi/asset/` versus `/cgi/image/` routing and
`?format=raw` behavior in Canvas.

## Regression coverage

Added coverage for SaaS prebuild URLs, the real Builder
asset-map-to-HTML-Embed integration, every supported asset attribute,
HTML character references, and responsive-image `srcset` edge cases.

## Verification

- [x] CLI prebuild suite: 69 passed
- [x] HTML Embed suites: 63 passed
- [x] Full Builder browser suite: 2,646 passed
- [x] Asset URL map tests: 2 passed
- [x] CLI, Builder, React SDK, and project-build typechecks
- [x] Lint and package-boundary checks
- [x] Previous PR CI: 29 successful, 1 skipped

## Code Review

- [x] Self-reviewed
- [x] Added regression and integration tests
2026-09-17 20:38:40 +02:00
Oleg Isonen 60c549c46f fix: preserve local styles and improve issue diagnostics (#6408)
Fixes #6386

## Summary

- Keep local `ws:style` declarations after `ws:tokens` so instance
styles override token defaults.
- Capture privacy-safe HTTP timing/status, exceptional session namespace
states, preview freshness/version agreement, and validation paths in
issue reports.
- Derive reportable validation roots from the public operation catalog
instead of maintaining a duplicate allowlist.
- Keep captured failures current by clearing them after a successful
tool call.

## Verification

- Protocol issue-report tests: 3 passed
- Project build MCP: 128 tests passed
- CLI project session: 27 tests passed
- Builder issue reporter: 5 tests passed
- Relevant package typechecks passed
- Generated API metadata is clean
2026-09-17 20:13:27 +02:00
Oleg Isonen fc15ebf81a perf: avoid repeated MDX parsing in publish diagnostics (#6407)
## Description

The cold `build.contentDatabasePublishDiagnostics` path was parsing the
same reachable MDX documents while the publication plan converged, then
parsing them a third time to produce omission diagnostics. Content-heavy
projects can approach or exceed the 15-second serverless limit on that
first request.

This change:

- creates a request-scoped MDX dependency resolver that reuses parse
results by asset revision while the publication plan converges;
- carries the final template omissions out of the existing publication
pass, so the diagnostics endpoint does not walk the artifact again;
- keeps the existing sequential asset validation/preparation/stability
checks unchanged;
- does not use module-level caching or depend on a warm serverless
instance;
- leaves the timeout error handling from #6406 unchanged.

## Performance evidence

An isolated cold-path benchmark used the same MDX payload (2,000
sections) and compared the previous three independent
dependency-resolution walks with the new request-scoped convergence
path:

| path | time |
| --- | ---: |
| before | 1,178 ms |
| after | 404 ms |

That is a 66% reduction in the measured MDX resolution phase. The tests
also assert the underlying work reduction directly: an unchanged
revision is parsed once across convergence passes, while a changed
revision is parsed again. For a stable 214-document candidate set, this
removes up to 428 of the previous 642 parse operations.

## Verification

- `pnpm --filter @webstudio-is/project-build test` — 2,420 tests passed
- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-server` — 336 tests passed, including server-side e2e runner tests
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm lint`
- `pnpm check:package-boundaries`
- `pnpm check:generated-api`

## Code Review

- [x] made a self-review
- [x] added comments for the request-scoped cache and revision safety
- [x] added tests for parse reuse, revision invalidation, and the
optimized endpoint integration path

## Before merging

- [ ] tested on a deployment preview with a production-sized content
project

## Additional content-read optimization

Successive compilation plans now share already-fetched, revision-keyed
content bytes within one callback-scoped preparation session. The byte
store:

- is created inside one asset-stability attempt and discarded when that
attempt finishes;
- is not stored on the repository, in a module, or outside the request;
- remains bounded by the existing 2 MiB hydration limit;
- refetches changed revisions and starts clean on stability retries.

Tests verify that two overlapping plans read unchanged content once, a
revision change forces another read, a new session reads again, and a
stability retry creates a new session. A fully overlapping two-pass
workload therefore performs up to 50% fewer object-storage content
reads.

Additional verification:

- `pnpm --filter @webstudio-is/asset-uploader test` — 292 tests passed
- `pnpm --filter @webstudio-is/asset-uploader typecheck`

## Remaining optimization assessment

The percentages below estimate whole-request improvement and are
workload-dependent; they are not additive.

| optimization | estimated improvement | complexity | decision |
| --- | ---: | ---: | --- |
| Request-local content-byte reuse | 10–35% | 3/10 | Implemented |
| Stop when the initial dependency plan is already a fixed point | 5–15%
for static/known MDX; ~0% for dynamic discovery | 2/10 | Implemented |
| Reuse a canonical source snapshot/materialized entries across
different plans | 15–30% | 8/10 | Deferred: large semantic and
invalidation surface |
| Reuse the bundle's initial asset-data snapshot | 1–5% | 5/10 | Not
worthwhile |
| Add a diagnostics-specific bundle loader | 2–8% | 6/10 | Not
worthwhile for this PR |
| Parallelize or skip unrelated user/project metadata | 0–3% | 2/10 |
Not meaningful |
| Combine collection validation and reserved-asset inspection | 0–5%
normally | 4/10 | Only meaningful for projects with many collections |

The fixed-point fast path is covered by a test asserting one artifact
preparation and one dependency-resolution call when the original plan is
already stable.
2026-09-17 10:47:48 +02:00
Oleg Isonen 0d448313ab fix: harden MCP mutations and validation (#6402)
## Summary

Fixes the reproducible MCP regressions reported today:

- validate font upload metadata locally instead of surfacing an HTTP 500
- replace only the addressed child when `insertIndex` is supplied
- support cross-parent duplication
- bind data-variable assignments in actions
- reject string-valued `style` bindings
- include descriptions for every advertised MCP tool
- preserve resource data-source exposure on full updates
- render mixed expression/element children in the Builder canvas
- preserve explicit outer Collection references in nested scopes
- reject unsupported `update-page-settings` fields instead of dropping
them
- reject unavailable asset-query variables and log server error details

Closes #6390
Closes #6391
Closes #6392
Closes #6393
Closes #6394
Closes #6395
Closes #6398
Closes #6399
Closes #6401
Closes #6404
Closes #6405

## Verification

- 484 project-build tests
- 62 CLI tests
- 35 Builder browser tests
- 25 SDK tests
- project-build, Builder, CLI, and SDK type checks
- root lint and generated runtime contracts
2026-09-17 10:16:11 +02:00
Oleg Isonen aa1587c100 fix: Clarify publish timeout errors (#6406)
## Summary

- detect pre-publish HTTP timeouts from the response metadata already
attached to `TRPCClientError`
- show a clear publish-specific message instead of the JSON parser error
- cover both HTTP 504 and Vercel timeout responses

## Why

When `build.contentDatabasePublishDiagnostics` exceeds the serverless
function duration, the proxy returns HTML or plain text. tRPC's JSON
parser fails, but the resulting client error still contains the original
HTTP response. The publish flow now uses that response to report:

> Pre-publish checks timed out. Publishing was not started. Please try
again.

This PR intentionally does not change the shared tRPC transport or claim
to solve the underlying diagnostics performance problem.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-browser-builder-features
app/builder/features/publish/publish-error.test.ts`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm exec oxlint --deny-warnings <changed files>`
- `pnpm exec oxfmt --check <changed files>`
2026-09-17 09:30:08 +02:00
Oleg Isonen b7a11e4bc2 feat: Delay canvas scripts until resources render (#6380)
HTML Embed scripts in canvas and preview now wait until the complete
resource queue settles. Their markup remains rendered while loading;
only executable scripts are delayed, so scripts can read the committed
resource-backed DOM without requiring a custom event.

## Implementation

- Add an internal `$resourcesState` atom with `pending` and `settled`
states, driven by the complete resource queue.
- Synchronize the atom from the Builder to the canvas iframe.
- At the Builder component-render boundary, pass only HTML Embed the
private prop `$ws$executeScripts={isSafeMode !== true && resourcesState
=== "settled"}`.
- In the SDK HTML Embed component, default `$ws$executeScripts` to
`true`, keep markup inert while it is false, and execute scripts after
React commits the transition to true.
- Treat failed requests as settled so scripts cannot wait forever.
- Keep the private prop out of generated component arguments and
metadata through the existing `$`-prefixed prop convention.
- Keep published rendering unchanged because published sites do not pass
the private Builder prop.
- Keep the existing standard `DOMContentLoaded` / `load` compatibility
and sequential script queue.
- Remove the canvas wrapper and component-registry override.

## Todo

- [x] Track collective resource readiness in the Builder.
- [x] Synchronize readiness into the canvas.
- [x] Delay HTML Embed scripts in both canvas and preview.
- [x] Combine safe mode and resource readiness into one private
execution prop.
- [x] Preserve rendered HTML while scripts are delayed.
- [x] Preserve pages with no resources and failed-resource behavior.
- [x] Keep the private prop out of user-facing component metadata.
- [x] Add resource-state, sync, Builder-prop, and browser regression
tests.
- [x] Review documentation impact; no user-facing documentation changes
are needed.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.

## Verification

- SDK HTML Embed browser tests — 44 passed
- Builder private-prop tests — 15 passed
- Resource tests — 28 passed
- Sync tests — 14 passed
- React SDK, SDK components, and Builder typechecks — passed
- Focused Oxlint and formatting — passed

Closes #6379
2026-09-14 15:53:26 +02:00
Oleg Isonen 9963c95ef8 fix: Sync OAuth profile fields on login (#6376)
Closes #6375

## Summary

- Refresh username, avatar, and provider fields when an existing user
logs in through OAuth.
- Avoid unnecessary database writes when profile fields are unchanged.
- Preserve existing development-login behavior.
- Add regression coverage for changed GitHub profile data.

## Testing

- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-server app/shared/db/user.server.test.ts`
- `pnpm --filter @webstudio-is/builder typecheck`
2026-09-14 12:29:10 +02:00
Oleg Isonen 2aec79774e fix: focus spacing input when popover opens (#6374)
## Description

Restore automatic focus to the CSS value input when the spacing popover
opens.

The property label became focusable after its migration to
`ResettableLabel`, making it the first tabbable element in the popover
and causing Radix to focus the label. Marking the intended value input
with `autoFocus` makes React focus it as the popover content mounts;
Radix then detects that focus is already inside its focus scope and
preserves it.

## Steps for reproduction

1. Select an element.
2. Open the Style panel spacing section.
3. Click any margin or padding value.
4. Verify focus is placed in the CSS value input.

## Verification

- [x] Builder typecheck
- [x] Prettier check
- [x] Oxlint
- [x] `git diff --check`

## Code Review

- [ ] conceptual review
- [ ] detailed review
- [ ] test on preview
2026-09-14 12:26:50 +02:00
Oleg Isonen 649ee3669d fix: Batch asset queries during project import (#6368)
## Problem

Project import sent every asset filename in one database request. Large
projects produced an oversized URL and failed before assets could
upload.

Closes #6367

## Changes

- [x] Reuse the existing encoded filename chunking rule for imports
- [x] Batch imported file lookup and restore requests
- [x] Add regression coverage for long filename lists
- [x] Review documentation impact: no user-facing documentation changes
are needed

## Verification

- [x] Builder import tests: 21 passed
- [x] Asset uploader tests: 50 passed
- [x] Builder and asset-uploader typechecks
- [x] Lint, formatting, and diff checks
- [x] Real CLI import passed the original metadata lookup and began
uploading 380 assets
- [ ] Agent evaluations were not run; they require separate approval

The real import later reached the target project's separate 350-asset
limit. That limit is outside this fix.
2026-09-11 23:33:09 +02:00
Oleg Isonen 5d34b77fac feat: Improve collection entry authoring (#6361)
Collection authors and editors get clearer field help, direct entry
navigation, and actionable validation instead of warnings that point at
the wrong place.

Closes #6360

## Implementation

- [x] Keep resettable help open after a click while the label remains
hovered
- [x] Stop repeating the property label inside resettable help
- [x] Store JSON Schema field descriptions and show them in entry forms
- [x] Add an optional dynamic collection entry page for explicit canvas
navigation
- [x] Rename entry file actions to **Edit file**
- [x] Add **Open on canvas** to entry menus and Entry settings
- [x] Put the entry filename in the Entry settings title
- [x] Make collection validation banners explain the problem and open
the first affected entry
- [x] Remove false output-only `UNOBSERVED_FIELD` warnings while keeping
filter and sort diagnostics
- [x] Encourage useful field descriptions in general MCP collection
guidance
- [x] Update Content Engine documentation
- [x] Confirm no database migration is required
- [x] Regenerate the affected asset-query runtime fixture
- [ ] Visually review the affected Storybook states in light and dark
themes
- [ ] Run the complete local agent evaluation suite
- [x] Run broader package and Builder checks
- [x] Complete final diff review

## Technical notes

- `entryPageId` lives in `collection.json` under `x-webstudio`; no
database schema changes are needed.
- Entry creation still leaves the current canvas unchanged. Canvas
navigation only happens after the user selects **Open on canvas**.
- An entry page must contain exactly one URL parameter. The entry
filename supplies that parameter, so the page can identify the selected
entry.
- Output projection fields are schemaless and may be produced after
reference resolution, so catalog absence is no longer reported there.
Filter and sort fields still report catalog diagnostics.
- The structured-query runtime change affects the generated
`webstudio-features` fixture, which is regenerated in this PR.

## Verification

- 561 Content Engine tests
- 2,413 Project Build tests
- 253 Design System tests
- 1,794 Builder tests
- 67 focused Builder regression tests after the final navigation change
- 11 ResettableLabel browser tests
- 128 focused MCP tests
- Builder, Content Engine, Design System, and Project Build typechecks
- Repository lint and package-boundary checks
- Generated documentation checks
- Full workspace fixture build with `pnpm -r fixtures:build`
- Formatting and diff checks
- New regression tests were demonstrated failing before their fixes and
passing afterward

## Documentation impact

- [x] Updated the Content Engine guide for field descriptions, dynamic
entry pages, actionable validation, **Edit file**, and **Open on
canvas**
- [x] Updated MCP guidance so collection fields include editor-facing
descriptions
2026-09-11 19:25:04 +02:00
Oleg Isonen 39c290bfa7 fix: resolve MCP-reported runtime and component issues (#6362)
## Summary

- harden MCP runtime behavior around asset uploads and resources, HTTP
responses, component and Content Block application, data reads, and
Chromium screenshots
- reject shared Slots inside Content Block templates and avoid unsafe
unused-asset findings when Content Blocks can hide MDX dependencies
- expand named grid-area values according to CSS shorthand fallback
rules
- add a consent-safe YouTube preconnect control and only warm origins
that are actually used
- add focused and end-to-end regression coverage for the reported paths

## Verification

- project-build: 2,419 tests passed
- css-data: 4,623 tests passed, 1 skipped
- sdk-components-react: 134 tests passed, 1 skipped
- high-impact CLI fixture: 7 tests passed
- Builder asset REST service: 22 tests passed

Closes #6336
Closes #6339
Closes #6353
Closes #6354
Closes #6355
Closes #6356
Closes #6357
Closes #6358
Closes #6359
2026-09-10 21:32:14 +02:00
Oleg Isonen eb61139e5a feat: Scale publishing allowance with workspace seats (#6352)
Builder already counts production builds for the project owner,
including publishes created through MCP and CLI. This change scales the
allowance used by Builder with the owner's paid seat capacity, so five
seats at 100 publishes each give 500 publishes per day. Free stays at
10.

Closes #6351

## Implementation

The existing `userPublishCount` endpoint still reads the owner-wide
`user_publish_count` view. For a project, it now returns the project
owner's base allowance multiplied by the owner, included member seats,
and purchased extra seats. Builder uses that returned allowance in its
existing publish UI.

The count remains owner-wide. MCP and CLI publishes contribute to it
because they create production build records, but their publish
endpoints do not enforce this Builder-side limit. No database, server
publish-path, dependency, fixture, or generated-file changes are
included.

## Checklist

- [x] Calculate the paid allowance from the owner, included seats, and
purchased extra seats.
- [x] Keep Free at 10 publishes per day.
- [x] Reuse the existing owner-wide publish count.
- [x] Use the calculated allowance in Builder.
- [x] Review documentation impact; no Builder documentation changes are
needed for this small allowance update.
- [ ] Update the pricing page to explain the publishing allowance and
how seats increase it.
- [x] Review fixture impact; fixture inputs and outputs are unchanged.
- [x] Review the final diff for unnecessary changes.

## Verification

- The project router tests pass, including the five-seat 500-publish
allowance.
- The new assertion fails against the previous implementation and passes
with this change.
- Builder browser tests passed.
- Project and Builder typechecks passed.
- Formatting, lint, and package-boundary checks passed.
- All GitHub Actions workflows passed.
- Agent evaluations do not cover this flow and were not run.
2026-09-10 18:39:54 +02:00
Oleg Isonen 13952a7d34 fix: Preserve Global root tokens when importing templates (#6348)
Inserting the CMS Sitemap marketplace page could detach existing Global
root tokens and break styles across existing pages. Preserve the root's
existing style-source selections in their original order, then append
mapped incoming sources without duplicates. Incoming local declarations
continue to merge into the existing root local source.

The MDX save fix is now included in main through
`commitAssetContentUpdate`. This PR retains an E2E assertion that saved
MDX revisions are not persisted again through `build.patch`.

Closes #6347

Verification and delivery:

- [x] Add four regression cases covering empty, local-only, token-only,
and mixed incoming root selections, including repeated imports, token
remapping, and local declaration merging. All four failed before the fix
and pass after it.
- [x] Rebase onto main and rerun style-copy and page-copy tests: 55
passed. Formatting and diff checks passed.
- [x] Verify the cloned project locally: reproduce the broken Home
canvas before the fix and restore it with Undo. After the fix, insert
Sitemap and verify Home retains its logo sizing, typography, spacing,
rounded corners, and both Global root tokens after reload.
- [x] Verify the MDX E2E assertion fails with duplicate persistence and
passes with the fix. Before rebase, all 13 shard 4 tests passed locally
with four workers, along with relevant typecheck and lint checks.
- [x] Resolve the rebase conflict using main's shared MDX implementation
and remove the duplicate helper from this branch.
- [x] Review documentation impact against marketplace, CSS-variable, and
Content Block documentation: no documentation changes required.
- [x] Review fixture impact: no fixture inputs, serialization formats,
or generated contracts changed.

No migrations are required. This prevents future imports from dropping
root selections; it does not reconstruct selections already lost in
previously saved projects.
2026-09-09 21:43:06 +02:00
Oleg Isonen e69a3d2f8e feat: Create structured MDX entries from collection folders (#6317)
## Paragraph editing follow-up — 42a16db02c (September 9)

Nested formatted paragraphs now retain their text, links, styling
identity, and existing labels during editing and saving. No database
migration.

- [x] Review all four changed text-editor files.
- [x] Preserve text before and after nested links inside emphasis,
strong text, and spans.
- [x] Preserve existing instance labels on save and keep inherited
formatting from overwriting a link's styling identifier.
- [x] Demonstrate regression failures without each fix, then pass all 41
focused text-editor and MDX synchronization tests.
- [x] Pass Builder typecheck, lint, formatting, and diff checks.
- [x] Verify the reported paragraph in Design and Content modes: edits
reach MDX and survive reload; original text restored. Verify matching
link color and underline inside and outside editing.
- [x] Review documentation and fixture impact: this restores existing
documented behavior; no fixture inputs, generated output, or
documentation changes are needed.
- [x] Commit and push the reviewed fixes.
- [ ] Confirm CI on this commit before merging.

Full E2E and agent evaluations were not rerun for this focused
follow-up. Earlier CI and suite results below apply to their recorded
commits, not this new head.

---

## Final verification — 50093ed2b7 (September 9)

Article edits now retain inline formatting and save newly inserted
custom components reliably. Saving a file no longer sends a second
project-sync write that could restore an older revision. No database
migration.

- [x] Fix iframe-blur saves, inherited template defaults, unused
resolved assets, and stale template tag metadata.
- [x] Remove duplicate server writes for already saved file revisions;
share the result with the canvas using the existing external-content
sync channel.
- [x] Demonstrate new regression tests fail without their fixes and pass
with them. Keep E2E file-write, content-order, and reload assertions;
remove the obsolete duplicate metadata-write wait.
- [x] Pass all 2,955 Builder tests, all 2,409 Project Build tests, and
all 8 focused MDX E2E checks.
- [x] Pass Builder and Project Build typechecks, lint, formatting, and
diff checks.
- [x] Run the complete fixture pipeline with no generated changes. The
later Builder-only save-acknowledgment changes affect no fixture inputs
or outputs.
- [x] Verify live article fields, hero replacement/save/reload/restore,
Accordion text edits, and YouTube URL edits. Verify UI edits reach MDX
and MDX edits persist after reload. Restore original article content.
- [x] Review documentation impact: these fixes restore the documented
Content Block editing behavior; no further documentation change is
needed.
- [x] Apply `visual-change-approved` after the user's explicit approval
of the 24 reviewed intended visual changes.
- [x] Commit and push the reviewed changes; worktree is clean.
- [x] Confirm CI and mergeability on this final commit: all 19 Main
workflow jobs passed, including all six E2E shards; PR lint and
submodule checks passed. Visual approval run 34383497017 passed. Its
predecessor reported the 24 intended differences; all 24 current images
were byte-identical to the reviewed report, with zero capture errors.
GitHub reports no merge conflict.

Agent evaluations were explicitly skipped for these final runtime fixes,
as requested. Earlier evaluation results below apply to their recorded
commits, not this final commit. This summary supersedes historical
pending-check notes below. The PR has not been merged.

---

## Final merge check — 9bc31ca679 (September 9)

- [x] Merge main without rewriting branch history. Preserve main's
image-picker fix and description-list validation.
- [x] Reset inherited HTML parent context at template boundaries. All
four new regressions fail without the fix and pass with it.
- [x] Fix shared navigation style composition and alignment; verify the
browser regression fails before and passes after. Review focused
screenshots.
- [x] Pass 2,408 Project Build tests, 251 design-system tests, and 3
image-picker tests; Builder, Project Build, and design-system
typechecks; lint, formatting, and diff checks.
- [x] Run the complete fixture pipeline with no generated changes.
- [x] Rerun all six agent evaluation scenarios on the merged code with
GPT-5.5. All functional checks pass; incompatible/missing token-baseline
comparison remains explicitly disabled.
- [x] Review documentation impact: this merge preserves documented
behavior; the image-editing guidance was already updated in f90e39cfcc.
- [x] Commit and push; worktree is clean and GitHub reports no merge
conflict.
- [x] Confirm Main CI on 9bc31ca679: all 19 jobs passed, including all
six E2E shards.
- [x] Review final visual artifact 10110845203 from run 34367911826: 24
added/changed stories, zero capture errors. Reviewed collection dialogs
and icons, light/dark login gradients, dashboard and project-settings
navigation, panel spacing, button variants, inset lists, and resettable
labels. Fourteen captures are byte-identical to the previously inspected
CI images. Updated navigation and collection-settings captures were
inspected; the formerly blank story renders normally. Pixel measurements
confirm the columns story retains its intended column positions (second
column x=261, third around x=388).
- [x] Apply visual approval after renewed explicit user approval.
Final-head CI is tracked above.

The live hero replacement and article/custom-component checks below are
now complete. The PR has not been merged.

---

## Merge-readiness follow-up — September 9

- [x] Remove contradictory image guidance and add an executable
writable-source/read-only-alt binding recipe. Regenerate CLI/MCP docs.
- [x] Verify the recipe regression fails with the old guidance and
passes with the new guidance. All 10 focused documentation tests, 2
content-reference tests, CLI/docs typechecks, lint, formatting, and
generated-documentation checks passed.
- [x] Rerun all six agent evaluation scenarios with GPT-5.5: all
functional checks passed. The incompatible/missing token-baseline gate
was explicitly disabled; existing baselines are unchanged.
- [x] Commit the documentation follow-up as f90e39cfcc.
- [x] Review existing CI screenshots and identify unintended shared-list
alignment changes. Implement a local shared-style composition/alignment
fix with a regression test demonstrated failing before and passing
after; all 251 design-system tests and its typecheck passed.
- [x] Visually verify and push the shared list fix in 9bc31ca679. The
focused run compared 28 stories; navigation is left aligned, column
layouts remain intact, and the previously blank collection-settings
story rendered normally. No product fix was inferred from that one blank
capture.
- [x] Review and approve the intended visual differences. See the
current final-head CI status above.
- [x] Verify these changes affect no fixture inputs or generated fixture
output. No database migration.

The previously pending live hero-swap and article/custom-component
checks are now complete. See the current verification summary above.

---

## Article editing follow-up — d307c8ba3d (September 9)

The target blog's hero source was incorrectly marked read-only. It now
uses the existing writable image picker. Reading time and the author URL
also use direct writable values. Time properties now have native
date/date-time controls. No database migration.

- [x] Fix the target's hero source, reading-time structure, and author
URL bindings while preserving the current content and link destination.
- [x] Verify Content-mode category, title, excerpt, date, reading-time,
shared image Description, author name/URL, MDX heading, and list-item
edits reach their source files. Check reloads and restore test values.
- [x] Add date-only, local timestamp, UTC timestamp, empty-value, and
legacy-value regression coverage; demonstrate failure without the date
control and passing results with it.
- [x] Fix the macOS Bash runner's empty-argument failure. Demonstrate
default invocation and argument-forwarding regressions fail without
their behavior and pass afterward.
- [x] Update authoritative Content Block/Time docs and generated CLI/MCP
guidance: inventory every article field, use direct writable bindings
and appropriate controls, check saved source and reload, and never infer
full editability from one heading.
- [x] Pass 291 date/text/Time/design-system tests (one existing skip), 2
runner tests, and 9 documentation tests; relevant typechecks, lint,
package boundaries, formatting, and diff checks.
- [x] Run the complete fixture pipeline: no generated fixture changes.
The later runner/docs-only edits do not affect fixture inputs or
outputs.
- [x] Run the full Builder E2E suite: 82 passed. This used an explicit
reporter; the default invocation correction was then covered by the
runner tests.
- [x] Run all six agent evaluation scenarios on gpt-5.5 after the final
image guidance correction. All functional checks passed. The older-model
token-baseline gate was disabled explicitly; accepted baselines were not
changed.
- [x] Review, commit, and push code and generated documentation.
- [x] With explicit approval, replace the live hero image through
Content mode, verify the MDX reference and reload, then restore and
verify the original image.
- [x] Complete the article-field checks listed above. Insert Accordion
and YouTube from templates; verify UI edits reach MDX and MDX edits
persist after reload. Restore the original article content.
- [x] Inspect CI for 7834d90a61: formatting, lint, typechecks, generated
docs, package/Builder tests, build checks, and five E2E shards passed.
The long MDX lifecycle exhausted its total 120-second budget at its
final assertion; the trace showed completed edits and reloads, not a
stalled save.
- [x] Give only that lifecycle a 180-second total budget; leave
individual save waits and assertions unchanged. Both local repeats
passed (plus setup). Push d307c8ba3d; this test-timeout-only follow-up
affects no fixture inputs or generated output.
- [x] Verify Main CI for d307c8ba3d: all 19 jobs passed, including all
six E2E shards. Visual approval remains separate.
- [x] Finish visual review and approval after correcting navigation
alignment and verifying the collection-settings story renders. The user
approved the reviewed 24 intended differences.
- [x] Recheck target publication preparation: seven documents included,
zero omitted, zero missing-template omissions. No deployment.
- Deferred: manual Storybook/screenshots; not authorized for this pass.

These results supersede the earlier pending image-product decisions and
live-check notes. Evaluation status for the latest fixes is recorded
above.

---

## Blog authoring fixes — 6359f979a3 (September 9)

This review fixes failed UI-to-MDX saves, missing source Undo/Redo,
editor recovery and focus, and publication preparation for the migrated
blog. No database migration.

- [x] Stop treating template-record ordering as a style edit; still
reject actual style changes that cannot be saved in MDX.
- [x] Share save-preparation errors with property controls across the
canvas frame.
- [x] Keep malformed MDX as an unsaved draft, with repair and discard
paths; focus the source editor when opened from the Navigator.
- [x] Enable supported Content-mode Move actions using the existing
central permission rules.
- [x] Keep source Undo/Redo separate from transient rendering history.
Serialize rapid commands, wait for pending canvas edits, reject
stale-source conflicts, and retain the last 20 edits per project
session.
- [x] Fix publication candidate discovery for projected documents and
unrelated author references; keep rejecting unsupported references used
as the actual MDX source.
- [x] Demonstrate regression tests failing without their fixes and
passing afterward.
- [x] Run the complete Builder (2,938), Content Engine (559), and
Project Build (2,395) suites: 5,892 tests passed.
- [x] Pass relevant package typechecks, root lint, formatting, package
boundaries, and diff checks.
- [x] Run the full fixture pipeline; no generated fixture changes.
- [x] Review documentation impact and document draft recovery and source
Undo/Redo.
- [x] Verify live heading edits, author-file edits, malformed-draft
recovery, and immediate delete/Undo/Redo; restore test content and
confirm persisted source.
- [x] Verify target publication preparation: seven documents included,
zero omitted, no missing-template omissions. This did not deploy the
site.
- [x] Review, commit, and push.

### Remaining verification and decisions

- [x] Correct the target's read-only Image source binding. The existing
Content-mode picker replaces the article reference; shared Description
edits use Asset settings. No new image-editing API or data model is
needed. See the latest verification limits above.
- [x] Repeat the full E2E and agent evaluation suites after these
changes: 82 E2E tests and all six evaluation scenarios passed.
- [x] Repeat the live Accordion/YouTube insertion and editing flow after
the save fixes, including UI-to-MDX saves and MDX edits surviving
reload.
- Historical note: this round did not run CI or Storybook checks; later
results are recorded above.

Unknown JSX already has source-range lint diagnostics; it remains an
editable missing-template placeholder, not an unconditionally blocked
save. The target author's binding was also repaired through the UI so
Content mode can edit its referenced author file. Shared resolved URLs
remain read-only text; the image picker changes the article reference,
and Asset settings edits the shared Description.

This section supersedes earlier claims of complete verification for the
latest code; historical results below remain records of earlier runs.

---

## Sync and import verification — a2a567551c (September 9)

Ordinary `link` → `sync` → `import` now exports the current saved
Builder project with its current assets. It no longer mixes an older
published build with current files. Import uploads assets before
assigning their source folders, then restores those assignments when
importing the bundle.

- [x] Fix foldered asset imports without a database migration.
- [x] Preserve file formats, folder assignments, build metadata, and
stable repeat-sync output.
- [x] Keep explicit `--buildId` export behavior unchanged; default sync
does not require publishing.
- [x] Review implementation and demonstrate regression tests failing
before the fixes and passing afterward.
- [x] Update protocol test servers for the current Builder data
endpoint.
- [x] Review documentation impact and update CLI documentation and
README.
- [x] Run all 1,121 CLI tests and 43 HTTP-client tests, both package
typechecks, root lint, package boundaries, formatting, and diff checks.
- [x] Run the full fixture pipeline and include expected current-build
fixture changes.
- [x] Verify the real source through the local CLI: repeated sync gives
identical output; import into the supplied target succeeds. Blog listing
and article rendering were also checked during the import investigation.
- [x] Commit and push the fix.
- Deferred at user request: agent evaluations for this round. Later CI
results are recorded above.

Sync still downloads the project and missing asset files and builds the
content index locally. This is not a transfer-free operation. Default
sync exports editable-build metadata rather than published deployment
metadata; use the explicit build option when selecting a particular
build.

---

## Final review and verification — ab3a6b7c55 (September 9)

Existing entries now use the same field controls as New entry. The
author-name permission fix, field-level validation, publishing warnings,
and final cache fixes are included. No database migration.

### Completed

- [x] Review all 38 local files, including entry autosave, revision
conflicts, permissions, shared fields, collection discovery, and
publishing diagnostics.
- [x] Allow existing-folder setup without rewriting entries or rejecting
unconfigured frontmatter.
- [x] Add Entry settings with autosave, repairable invalid fields,
immutable filename slugs, and preserved MDX body/unknown properties.
- [x] Warn before publication when missing or ambiguous custom templates
omit content, including discovered dynamic sources.
- [x] Allow authorized frontmatter writes to referenced Markdown author
files and preserve error messages across the canvas-frame boundary.
- [x] Reject impossible integer limits, slugless generated-filename
patterns, and slug maximum length zero. Existing raw configurations
remain readable for repairs.
- [x] Share collection discovery across Assets, file editors, and
property controls. Builder first synchronizes project asset metadata
into the browser. The browser then groups that metadata once per update
and shares configuration/template reads, instead of repeating them for
each control. This does not remove initial synchronization or
entry-frontmatter reads. Preserve project/access isolation, refresh
retries, stale-response protection, and duplicate-filename invalidation.
- [x] Demonstrate new regressions fail before fixes and pass afterward.
- [x] Correct the folder-drag E2E's obsolete root-folder assumption and
verify remembered-folder persistence.
- [x] Review and update authoritative Content Engine and Content Block
documentation.
- [x] Merge current main and regenerate the conflicting CLI
documentation from its source.
- [x] Fix generated previews with shared development dependencies:
invoke React Router binaries through Node, isolate each preview's Vite
cache, and forward script options correctly through npm and pnpm.
- [x] Fix a false MDX conflict when a new edit arrives immediately after
the previous save is acknowledged. Advance the local revision before
asynchronous parsing and ignore stale refresh results.
- [x] Run the full fixture link/sync/build pipeline. Generated changes
contain only the expected React Router scripts and Vite cache location.
No database migration.
- [x] Review the final diff and scan added lines for credential
patterns.

### Verification results

- Builder: **2,934 tests passed**. CLI: **1,120 tests passed**.
- Content Engine: **559 passed**; Asset Uploader: **291 passed**;
Project Build: **2,394 passed**; Design system: **250 passed**.
- The clean full workspace run passed all 35 non-CLI packages. Its two
CLI failures exposed pnpm argument forwarding; the complete CLI rerun
passed after that fix. Earlier load-related test timeouts did not recur
in the clean run.
- All workspace typechecks passed. Builder and CLI typechecks and
repository lint were repeated after their final changes. Package
boundaries, generated API checks, and the complete
generated-documentation gate passed. Documentation was regenerated from
source; the post-commit clean-file check passed.
- Full Builder E2E: **82 passed** from a fresh build, including
generated previews, collection creation/reload, MDX editing, stale
revisions, and permission checks. The isolated disposable test database
was cleaned up afterward; development data was untouched.
- Full agent evaluations: **all six scenarios passed on gpt-5.5**,
including every functional check for Markdown blogs, reference
discovery, MDX article editing, authentication, fonts, and design input.
The account cannot use the old **gpt-5.4-mini** baseline model. Five
baseline comparisons are incompatible and the MDX scenario has no
accepted baseline; token-baseline gating was disabled explicitly.
Accepted baselines are unchanged.
- New preview and MDX regressions were demonstrated failing before their
fixes and passing afterward.
- Working-tree/diff checks passed. Main was merged without rewriting
branch history. CI was not checked, as requested.

### Final delivery checklist

- [x] Finish the full Builder E2E rerun.
- [x] Finish the full agent evaluation rerun with the authorized
accessible model.
- [x] Commit, verify the clean generated-documentation gate, and push.
GitHub reports the branch mergeable with current main.

No known code or functional-test blocker remains from this review. CI
was not checked at the user's request. Manual Storybook/screen-reader
checks remain deferred; the automated E2E and agent visual checks above
passed.

The historical review sections below retain their original run results.
This section supersedes earlier claims about strict entry validation,
entry editing, browsing reads, and evaluation authorization.

## Outcome

Content editors can create structured MDX entries from a collection
folder without writing frontmatter. Designers configure flat fields,
limits, optional slug behavior, and the Markdown template in the
Builder.

## High-level goals

- [x] Keep folders ordinary in the database and derive collection
behavior from a direct **collection.json**.
- [x] Add **Use as content collection** to folder creation.
- [x] Give designers a visual configurator for the JSON Schema and MDX
template.
- [x] Show a collection badge and a generic **New entry** action.
- [x] Generate independent MDX entries from the configured schema and
template.
- [x] Keep the current page unchanged after creating an entry; no
automatic preview navigation.
- [x] Select direct MDX entries with URLPattern filename rules; keep
ignored files as ordinary supporting assets.
- [x] Keep reserved collection files out of collection query results and
generated content indexes.
- [x] Preserve broken collections as visible, repairable collection
folders.
- [x] Restore normal folder behavior when **collection.json** is
removed.

## Implementation todos

- [x] Add the Content Engine collection contract, default
schema/template, slug normalization, validation, serialization, and
entry generation.
- [x] Discover collections from Assets files without a database folder
type or migration.
- [x] Create **template.mdx** and **collection.json** when the folder
checkbox is selected.
- [x] Add basic field controls for text, long text, slug, number,
integer, and Boolean values.
- [x] Support required fields, string length limits, numeric limits,
template defaults, and generated slugs.
- [x] Add designer collection settings and template editing.
- [x] Remove dynamic preview-page configuration and ignore its legacy
metadata.
- [x] Add the schema-generated **New entry** form and creation endpoint.
- [x] Preserve reserved-file portability in authenticated export/import
while excluding it from content query and generated-index results.
- [x] Document the feature in the authoritative Content Engine guide.

## Deep review checklist

A checked item means the area was inspected and evidence was recorded.
It does not mean the behavior passed. Unresolved defects and product
decisions remain listed in **Review findings**.

### 1. Product contract

- [x] Decide whether the collection checkbox creates defaults
immediately or opens the configurator first.
- [x] Decide whether existing folders can be converted through the UI.
- [x] Define collection activation when **collection.json** exists but
is invalid.
- [x] Define an entry: every valid direct MDX child or only files
created through **New entry**.
- [x] Confirm collection membership is direct-child only and nested
folders can define their own collections.
- [x] Define designer, content editor, viewer, service, CLI/MCP,
old-client, and direct-API authority.
- [x] Decide whether content mode is simplified UI or a server
authorization boundary.
- [x] Decide whether slugs are permanently immutable.
- [x] Decide whether schema changes may invalidate existing entries or
must migrate them.
- [x] Decide whether invalid unused collections block unrelated queries
or whole-project publication.
- [x] Decide what happens to the preserved template after collection
removal.
- [x] Define the promised JSON Schema dialect and supported subset.
- [x] Decide whether temporary invalid collection states are admitted
for repair.
- [x] Define duplicate-request semantics: no duplicates, exactly one
success, and retry behavior.
- [x] Decide whether entries require valid frontmatter only or valid
complete MDX.

### 2. State machine

- [x] Review ordinary folders and root-level **collection.json**.
- [x] Review empty-folder, template-only, config-uploading, and
config-loading initialization states.
- [x] Review ready collections.
- [x] Review invalid JSON, UTF-8, size, schema, and duplicate-config
states.
- [x] Review missing, duplicate, wrong-type, oversized,
invalid-frontmatter, and invalid-MDX template states.
- [x] Review forbidden direct files, invalid entries, and slug/filename
mismatches.
- [x] Distinguish transient read/network failures from persistent
invalid content.
- [x] Review removing, removed, moved, renamed, restored, duplicated,
and deleted collection states.
- [x] For every state, inspect visibility, allowed actions, server
admission, query/publish behavior, recovery, and concurrent-client
behavior; record recovery behavior.

### 3. Activation and deactivation paths

- [x] Review checkbox creation.
- [x] Review create/upload/rename/move/paste/duplicate of
**collection.json**.
- [x] Review restore, import, project duplication, and sync behavior.
- [x] Review delete/rename/move/replace of **collection.json**.
- [x] Verify all activation paths produce the same state and validation.
- [x] Verify all deactivation paths restore ordinary-folder behavior
without accidental data loss.

### 4. Folder membership and nested collections

- [x] Verify only direct children are collection entries or reserved
files.
- [x] Verify a nested folder with its own direct config becomes its own
collection.
- [x] Verify parent rules do not leak into nested folders.
- [x] Define filename and extension case sensitivity.
- [x] Define duplicate logical-name behavior over generated storage
names.
- [x] Define how uploading, deleted, and stale assets affect folder
state.
- [x] Review moving entries out and between collections with different
schemas.
- [x] Review ordinary subfolders with arbitrary files inside a
collection.
- [x] Review template behavior after collection removal.

### 5. Roles and authorization

- [x] Review config/template listing and download for every role.
- [x] Review config/template content edits for every role.
- [x] Review config/template rename, move, restore, and deletion for
every role.
- [x] Review entry create/edit/rename/move/delete/restore for every
role.
- [x] Review generic upload and subfolder operations for every role.
- [x] Review collection removal and repair for every role.
- [x] Verify server enforcement separately from hidden or disabled
controls.
- [x] Recheck permission changes while operations are in flight.

### 6. Mutation surfaces

- [x] Review REST upload-ticket creation and upload completion.
- [x] Review asset-content replacement and asset metadata changes.
- [x] Review runtime add/update/duplicate operations.
- [x] Review sync patches and direct persistence helpers.
- [x] Review clipboard, drag/drop, move, rename, and duplicate paths.
- [x] Review import/export, restore, and project/folder duplication
paths.
- [x] Review delete-unused-assets and recursive folder deletion.
- [x] Verify invariants at the final persistence boundary, not only in
UI preflight.

### 7. Concurrent operations

- [x] Review create entry against schema save, template save, raw config
edit, and collection removal.
- [x] Review edit/delete/restore entry against schema and template
changes.
- [x] Review two collection-settings editors and raw editor versus
configurator.
- [x] Review schema writer A interleaved with template writer B.
- [x] Review upload/move/rename against collection activation and
removal.
- [x] Review template/config deletion against creation and publication.
- [x] Review folder deletion/move and project switch against all async
operations.
- [x] Review final validity, conflict detection, cleanup, user outcome,
cache invalidation, and publish behavior across the implemented
intersections; record conflict and recovery behavior.
- [x] Review retry after the server commits but the HTTP response is
lost.

### 8. Collection initialization and recovery

- [x] Review project switch, permission revocation, dialog close, folder
rename/move/delete, and conflicting uploads during initialization.
- [x] Verify retry is idempotent and does not duplicate reserved files.
- [x] Verify partial setup has a visible finish, retry, or cleanup path.
- [x] Verify generic actions cannot corrupt the initialization window.
- [x] Verify project and folder identities are captured for the whole
operation.
- [x] Review asset-count, storage, filename, and folder limits with
cleanup.

### 9. JSON Schema conformance

- [x] Confirm the exact draft and validate configs against its
meta-schema.
- [x] Review unknown dialects, boolean schemas, malformed keyword types,
and duplicate JSON keys.
- [x] Review refs/defs, recursion, composition, conditionals,
dependencies, and nullable/multiple types.
- [x] Review enum, const, format, pattern, patternProperties, and
additionalProperties.
- [x] Review min/max properties, array constraints, contains, and
uniqueItems.
- [x] Review exclusive numeric bounds and all string/number/integer
limits.
- [x] Differential-test validation against an independent draft 2020-12
implementation.
- [x] Verify unsupported keywords are rejected or explicitly documented,
never silently ignored.

### 10. Schema resource limits and hostile input

- [x] Bound schema bytes, depth, field count, enum size, and reference
depth.
- [x] Bound validator compilation and validation time.
- [x] Review recursive references and excessive composition branches.
- [x] Review catastrophic patterns and regex denial of service.
- [x] Review huge labels, keys, defaults, and values.
- [x] Review prototype-sensitive and structurally awkward field names.
- [x] Review memory and CPU amplification across many collections.

### 11. Configurator ownership and round trips

- [x] Define owned, preserved, read-only, and rejected schema paths.
- [x] Verify parse/save/parse semantic equivalence and repeated-save
idempotence.
- [x] Verify a UI edit changes only explicitly owned schema paths.
- [x] Review property/form order and required-array order.
- [x] Review unknown root, property, and Webstudio extension metadata.
- [x] Review editable primitive fields that also contain enum, const,
format, composition, readOnly, or custom constraints.
- [x] Review type changes for incompatible preserved or removed
keywords.
- [x] Review rename/removal collisions with unsupported properties and
requirements.
- [x] Surface hidden preserved rules that can cause unexplained
validation errors.
- [x] Review field ordering/reordering.
- [x] Review choosing/changing slug field, slug source, and template
filename.
- [x] Review turning newly added fields into slug or slug-source fields.

### 12. Required/default/unset semantics

- [x] Review absent, empty string, false, zero, null, default, and
user-cleared values for every field type.
- [x] Define required-property versus non-empty-value behavior.
- [x] Verify required Boolean false remains valid and understandable.
- [x] Review optional Boolean absence versus explicit false.
- [x] Review explicitly clearing template-backed values.
- [x] Review JSON Schema default as annotation versus persisted entry
value.
- [x] Verify precedence among template values, schema defaults,
generated slug, and user values.
- [x] Review hidden/non-renderable required fields and template
requirements.
- [x] Verify a collection cannot be ready when every entry form must
fail.

### 13. Schema evolution and migration

- [x] Review add/remove optional and required fields.
- [x] Review field rename and label-only changes.
- [x] Review type changes and tightened/relaxed limits.
- [x] Review enum option, default, slug field/source, template name, and
additionalProperties changes.
- [x] Verify compatible save ordering and identify impossible two-write
migrations.
- [x] Decide whether entries are rewritten and whether migration is
atomic.
- [x] Review entry changes after validation but before schema commit.
- [x] Ensure designers see all incompatible entries or an actionable
summary.

### 14. Template, YAML, and MDX

- [x] Review absent/empty frontmatter and empty YAML.
- [x] Review BOM, CRLF, final-newline, and alternate YAML delimiter
behavior.
- [x] Review duplicate keys, anchors, aliases, tags, comments, quoting,
and multiline strings.
- [x] Review Unicode and byte limits.
- [x] Verify frontmatter and starter-body preservation.
- [x] Review invalid MDX bodies, imports, exports, expressions, and
unsafe nodes.
- [x] Review missing, renamed, duplicate, wrong-type, and oversized
templates.
- [x] Verify template changes affect future entries only.
- [x] Review reserved template exposure through direct asset URLs and
build copying.

### 15. New-entry form and creation

- [x] Verify every required field is renderable or supplied safely.
- [x] Verify deterministic field order and correct controls/constraints.
- [x] Compare client and authoritative server validation.
- [x] Prevent submitted values from overwriting protected hidden fields.
- [x] Preserve values and focus after failed creation.
- [x] Review per-field errors, error summary, and first-error focus.
- [x] Prevent duplicate submission and define lost-response recovery.
- [x] Verify local sync, reload persistence, and project-switch
handling.
- [x] Review no/stale preview page, query delay, and draft-filtered
entries.
- [x] Review large forms and narrow/mobile layouts.

### 16. Entry editing and lifecycle

- [x] Review body-only, frontmatter-only, and combined edits.
- [x] Review invalid intermediate frontmatter and invalid complete MDX.
- [x] Review slug change, filename rename, and reference consequences.
- [x] Review delete, restore, move out, move between collections,
duplicate, paste, and undo.
- [x] Review slug reuse after delete and restore collision.
- [x] Review edits concurrent with schema/template changes.
- [x] Review repair of legacy invalid entries and blocked intermediate
saves.
- [x] Decide whether existing entries receive a generated settings form
or raw MDX editing.

### 17. Slug and filename identity

- [x] Review slug source selection, same slug/source field, manual
override, and visible final normalization.
- [x] Enforce lowercase-dash identity independently of editable schema
metadata.
- [x] Review empty normalized slugs and template/schema slug defaults.
- [x] Review diacritics, non-Latin scripts, and emoji-only sources.
- [x] Use locale-independent case comparison and Unicode normalization.
- [x] Review character/byte limits and case-insensitive filesystems.
- [x] Review collisions with reserved config/template filenames.
- [x] Review concurrent creation, stale reservations, deletion, reuse,
restoration, and URL encoding.

### 18. Collection-root restrictions

- [x] Review upload, external drop, internal drag, generic text
creation, paste, duplicate, move, rename, and extension changes.
- [x] Review runtime add, sync patch, import, and restore bypasses.
- [x] Review upload tickets completed after activation.
- [x] Review moving or renaming **collection.json** through metadata
changes.
- [x] Review dedicated entry creation, initialization, repair, body
editing, and collection removal exceptions.
- [x] Verify subfolder creation and arbitrary files inside subfolders
remain allowed.

### 19. Storage, database, idempotency, and cleanup

- [x] Review object-without-File, File-without-Asset, stale UPLOADING,
and soft-deleted states.
- [x] Review storage success/database failure and database
success/storage failure.
- [x] Review postcheck failure, object cleanup, row cleanup, and cleanup
failure.
- [x] Verify cleanup preserves the original error and cannot delete
another request's asset.
- [x] Review timeout/lost response after commit and safe retries.
- [x] Review reservation lookup/cleanup failure and expiration.
- [x] Review failed creation cleanup and distinguish incomplete uploads
from existing entries with repairable field errors. Existing field
errors do not hide entries or block publication.
- [x] Verify orphan reclamation and project-scoped cleanup.

### 20. Query, build, and publication

- [x] Verify config/template exclusion only for an identified
collection.
- [x] Verify ordinary root-level **collection.json** remains ordinary
content.
- [x] Review template queryability after collection removal.
- [x] Validate actual entry frontmatter and decide complete-MDX
requirements.
- [x] Review invalid content versus transient read failure.
- [x] Decide invalid-collection blast radius across queries, field
catalog, build, and publish.
- [x] Review direct asset IDs, document references, build copying, and
public URLs for reserved-file leakage.
- [x] Verify preview and publish use the same collection rules.

### 21. Cache and snapshot consistency

- [x] Verify config, template, entries, filenames, folder movement,
activation, and removal affect source revision.
- [x] Verify immutable/revision-pinned reads under concurrent swaps.
- [x] Verify config and template come from one logical version.
- [x] Review revision consistency between entry reads and indexing.
Queries do not require entry-schema validation.
- [x] Review rejected-promise caching and retry after repair.
- [x] Verify no cross-project cache collision.

### 22. Performance and availability

- [x] Review sequential versus bounded parallel reads.
- [x] Review request cancellation and repeated preview-query work.
- [x] Review rate limits and resource-exhaustion protection.
- [x] Review progress/cancellation and actionable limit errors.

### 23. Invalid-state repair

- [x] Review repair for every config, template, forbidden-file, entry,
and slug mismatch state.
- [x] Show exact folder, file, field, and rule.
- [x] Distinguish retryable transport errors from content errors.
- [x] Provide missing-template creation despite normal root
restrictions.
- [x] Provide duplicate-file deletion and forbidden-file move recovery.
- [x] Preserve raw repair when the configurator cannot parse the schema.
- [x] Show collection health after repair.
- [x] Prevent custom-template leakage to content editors when config
cannot identify it.

### 24. Entry navigation

- [x] Remove dynamic preview configuration and automatic navigation
after entry creation.
- [x] Verify entry creation leaves the selected page unchanged, with or
without a slug field.

### 25. Accessibility and responsive behavior

- [x] Review native form submission and required semantics beyond an
asterisk.
- [x] Review linked errors, error summary, live announcements, and busy
state.
- [x] Review focus after errors, add/remove field, key edits,
confirmation transitions, and dialog close.
- [x] Review dirty-close warning, explicit cancel/retry, and durable
feedback beyond toasts.

### 26. Portability, compatibility, and rollback

- [x] Review export/import, folder/project duplication, and restore
ordering.
- [x] Verify logical filenames, not storage names, define the portable
contract.
- [x] Review case-insensitive filesystem behavior.
- [x] Review rollback after collection files already exist.
- [x] Verify removal or rollback leaves content recoverable.
- [x] Confirm database/migration assumptions.

### 27. Observability and supportability

- [x] Review state-transition and validation diagnostics.
- [x] Track cleanup failures and orphaned reservations.
- [x] Make publish errors actionable without logging private content.
- [x] Ensure client errors do not expose stack traces or sensitive
internals.

### 28. Verification strategy

- [x] Unit-test schema, slug, merge, serialization, and validators.
- [x] Demonstrate each new regression test fails without its fix.
- [x] Run affected unit suites, typechecks, lint, docs checks, E2E,
fixture-impact review, and final diff review.
- [x] Review authoritative documentation impact.

## Persistence and recovery

No database migration is needed. Folders remain ordinary database rows;
their direct **collection.json** activates collection behavior. This PR
adds no table, column, index, RPC, folder type, or backfill.

- Setup writes the template first and **collection.json** last.
Interrupted setup leaves an ordinary folder and can be retried.
- Existing immutable File revisions, conditional Asset updates, and
upload reservations provide conflict detection and retry-safe entry
creation.
- Settings saves validate the intended template and schema together,
then write only the files that changed. Existing entry field errors stay
repairable and do not block unrelated saves, queries, or publication.
New entry creation remains strict.
- Invalid or interrupted collections remain visible and repairable.
Configuration/template structure errors remain distinct from entry-field
errors; entry-field errors do not invalidate the entire collection.
- Public/service publication excludes configuration and template files.
Authenticated CLI exports retain them for lossless import.

## Review findings

The implementation review is complete. Confirmed defects were fixed and
the following tradeoffs remain explicit.

### Fixed

- Enforced collection structure and permissions across REST, runtime
patches, import, query, and publication, while keeping existing
entry-field errors non-blocking for queries and publication.
- Preserved Undo/Redo, including restoring shared File revisions in
cloned projects.
- Rejected forged shared-file metadata while preserving metadata edits
on legacy files with null uploader provenance.
- Added retry and recovery behavior for setup, settings saves, lost
responses, and transient reads.
- Moved collection setup into one retry-safe server operation and kept
the local Builder update as one Undoable transaction.
- Centralized collection inspection and limited schemas to the flat
fields the configurator can represent. Opening a collection still reads
its entries in the background to report field errors.
- Guarded stale dialogs and deferred saves against permission changes,
protected files, project switches, and replaced revisions.
- Kept Asset settings open while choosing nested folders; the existing
browser regression fails before the fix and passes afterward.
- Preserved required Boolean false values, field-level errors,
dirty-close prompts, and inline Markdown editing.
- Separated authenticated portable exports from public runtime assets.
- Updated the authoritative Content Engine guide.

### Known tradeoffs

- A failure after template content saves but before configuration saves
can temporarily leave an invalid collection. Retrying the settings save
completes it; otherwise the visible files allow repair. The
template/config rename endpoint remains retry-safe and rolls back a
rename when its config write fails.
- Cloned-project Undo can retain an unused prior File revision. Separate
read-then-delete cleanup could race with another restore, so this path
retains the revision.
- Build/asset persistence and import already use multiple calls. This PR
validates before writes and provides retry/conflict handling; it does
not provide a transaction spanning every resource or a global collection
lock.
- Opening a collection loads its configuration and template through
shared Builder discovery, then checks entry frontmatter in the
background for field errors. Unchanged configuration/template content is
reused across controls and entry edits. Frontmatter checks still require
entry reads; ordinary folder browsing does not discover every
collection’s file content.

## Latest unstaged-change review — 2026-09-07

- [x] Review collection settings, validation, autosave/error recovery,
entry creation, reserved-file actions, and conversion flows.
- [x] Review shared labels, list keyboard handling, dialog focus/Escape,
buttons, and panel spacing migrations.
- [x] Fix Enter/Space list activation lost when arrow navigation
replaced the existing press handler.
- [x] Recognize historical generated ASCII/Unicode slug patterns, use
the bundled Unicode slug schema, and save a locally resolved $ref
without changing existing filenames.
- [x] Prevent duplicate slugless entries after a lost response: New
entry sends a stable UUID per attempt, reused by the repository's
existing filename/content retry checks. No database migration. Callers
omitting the optional requestId retain the previous behavior.
- [x] Preserve Content Block template validation independently of page
HTML placement, without accepting invalid rendered nesting.
- [x] Demonstrate the new review regressions fail without their fixes
and pass afterward.
- [x] Review authoritative documentation and update legacy slug
behavior.
- [x] Run fixtures and inspect generated changes: none.
- Deferred manual verification: Storybook screenshots were not
authorized for this pass.
- Historical evaluation status: not run in this pass; superseded by the
final verification section above.

## Verification

Local checks for commit **2b8195692e**:

- Builder app: 1,739 tests; Builder core: 1,150 tests. After the final
fixes, entry/settings tests and shared asset REST checks also passed,
including the added stable-retry test.
- Content Engine: 535 tests.
- Asset Uploader: 279 tests.
- Design system: 249 tests.
- Project Build: 2,392 tests.
- All five affected package typechecks, changed-file lint/formatting,
package boundaries, and Content Engine documentation tests passed.
- Complete fixture link/sync/build pipeline passed with no tracked
fixture changes.
- Staged diff and credential-pattern checks passed. No database schema,
migration, or generated database type changes. The lockfile includes the
collection validator and filename matcher dependencies.
- CI for this commit is pending. Builder E2E, manual
screen-reader/mobile checks, Storybook screenshots, and agent
evaluations were not run in this pass.

## MDX editing review — commit c3702a7238

- [x] Review all 36 local changed files: MDX parsing/materialization,
template enforcement, mutation commits, persistence, Assets refresh,
Navigator, source binding, editor diagnostics, dialogs, and query menus.
- [x] Validate new custom components at the shared Builder mutation
commit before applying tree changes. Cover move, reparent, paste, nested
paste, duplicate, and batched operations; preserve unchanged authored
content.
- [x] Match template names and component structure, including renamed
templates, instead of accepting any component type found in Templates.
- [x] Remove the registered-component fallback from MDX materialization
and completion; report untemplated JSX in the editor.
- [x] Preserve unresolved JSX until its placeholder is deleted, then
persist its removal without clearing the Assets resource during refresh.
- [x] Keep custom designer content outside the MDX body; omit the extra
body for fresh unconnected Content Blocks.
- [x] Resolve Navigator's MDX-file action from the active content root
for dynamic sources.
- [x] Fix the first dialog drag jump while honoring subsequent explicit
size changes; contain long query menus and prevent editor Backspace from
deleting instances.
- [x] Demonstrate new review regression failures before the fixes and
passing results afterward.
- [x] Review and update authoritative Content Block documentation.
- [x] Run the complete fixture pipeline; no generated fixture changes.
- Historical evaluation status: deferred in this pass; superseded by the
final verification section above.
- Historical verification limits: no Storybook screenshots, Builder E2E,
or manual accessibility checks in this pass. See the final verification
section for the current E2E result; manual checks remain deferred.

### Latest verification

- Builder affected suites: **624 tests passed**.
- Project Build full suite: **2,394 tests passed**.
- Design system full suite: **250 tests passed**.
- Query Builder React full suite: **14 tests passed**.
- Component Registry full suite: **7 tests passed**.
- All five affected package typechecks, repository lint, changed-source
formatting, staged diff checks, and credential-pattern scan passed.
- Fixture link/sync/build passed without generated changes. No database
migration.
- CI for this commit has not yet been verified.

The new mutation-commit check covers Builder operations affecting loaded
external content roots. It is not a claim that every remote API mutation
is now guarded by this client-side check.

Closes #6316


## Local-change review — 2026-09-08

- [x] Review all 47 changed files and trace collection setup, filename
filters, public metadata/upload APIs, sync patches, Content-mode write
targets, local folder persistence, and MCP downloads.
- [x] Fix ignored MDX files being renamed into entries through the
metadata API. Selected filenames require New entry.
- [x] Fix supporting-asset moves/renames being blocked by unrelated
invalid entries. Configuration/template checks still run. The later
entry-repair work also removed entry-field validation as a blocker for
unrelated writes and publication.
- [x] Validate frontmatter directly with Ajv, resolve the bundled slug
$ref without network fetching, preserve the flat schema contract, and
avoid retaining old validator instances.
- [x] Verify Unicode generation, legacy-pattern upgrades, sibling
constraints, field error paths, optional binding paths, referenced-file
permissions, and independent queued edits.
- [x] Review authoritative documentation, correct contradictory
referenced-write guidance, and regenerate CLI/MCP documentation.
- [x] Demonstrate new review regression failures with the fixes
disabled, then restore and pass them.
- [x] Pass Content Engine (553), asset uploader (284), SDK (570),
project-build (2,394), MCP command (61), and collection/Content-mode
browser tests (279), plus expression tests and documentation checks.
- [x] Pass seven affected package typechecks, repository lint,
formatting, package boundaries, and fixture link/sync/build. No
generated fixture changes or database migration.
- Historical evaluation status: deferred in this pass; the user
subsequently authorized the full suite. See the final verification
section above.
- Historical verification limits: no Builder E2E or manual
visual/accessibility checks in this pass. See the final verification
section for the current E2E result; manual checks remain deferred.
2026-09-09 21:31:04 +02:00
Oleg Isonen e33ce76489 fix: Keep image picker width stable and accept quick clicks (#6344)
Choosing an image now works with a quick thumbnail click, and selecting
an asset keeps the Source picker at its intended width, including long
names and nested folder paths.

The dialog's content-based width was overriding the floating panel's
width. Focusing a thumbnail widened the panel, triggering resize
handling that briefly disabled pointer events before mouse release. Pass
the floating panel styles through the dialog's `css` override so the
intended width wins. Explicit dimensions and drag/resize handlers remain
as implemented.

Closes #6343

- [x] Reproduce both failures in Chromium using the actual Source
picker.
- [x] Fix floating panel style precedence.
- [x] Add three regressions for root and nested asset selection widths
and an 80 ms thumbnail click. All three fail without the fix and pass
with it.
- [x] Run related Builder tests (50 passed), the design-system suite
(228 passed), both package typechecks, repository lint, and formatting.
- [x] Review documentation impact: the Image Source instructions already
describe the restored behavior; no update needed.
- [x] Verify no fixture inputs or generated outputs are affected.

Validation used headless Chromium component tests. Full canvas E2E and
Storybook screenshot checks were not run.
2026-09-09 12:04:52 +02:00
Oleg Isonen 722b3af113 feat: Copy links to instances from the context menu (#6342)
Right-click an instance in the Navigator and choose **Copy link to
instance** to share its full Builder URL. Opening it restores the page
and the same instance occurrence, including descendants of shared Slots.
The shared menu also exposes the action on the canvas.

Copying and opening a link use the same existing
`getDeepLinkedInstanceSelection` function, moved unchanged from
`use-switch-page.ts` into `shared/instance-utils/link.ts`, alongside URL
creation and the existing instance URL parser. It validates the instance
path and page access. The context menu and pre-publish “Show element”
links both call `getInstanceLink(instanceSelector)`. It lives in
`shared/instance-utils/link.ts` and owns validation and the complete URL
setup: project, page, full selector, origin, share token, editor mode,
and safe mode, using the existing `builderUrl`. There is no separate
link-permission implementation.

The link keeps the current share access and editor mode, clears
unrelated scroll anchors, and shows clipboard success or failure. Body
is supported; Global Root and multiple selections are unavailable.

Instance-only navigation now preserves loaded Builder data: the route
comparison recognizes the `instance` parameter as well as the older
`instanceId`. A functional regression test confirms that changing the
target does not change the data URL comparison, while changing share
access does.

MCP guidance explains how to extract the target ID from the link for
`inspect-instance`, verify the project and shared Slot context, and
avoid guessing when an instance no longer exists. This does not add a
URL-taking MCP endpoint.

1. [x] Add the menu action and reuse complete instance-link construction
with pre-publish links.
2. [x] Share existing instance-link validation between copying and
opening links.
3. [x] Update the Navigator and MCP guidance; regenerate CLI
documentation.
4. [x] Extend the existing functional test to round-trip a shared Slot
URL, including share access and editor mode. Verify it fails without the
instance parameter and passes after restoration.
5. [x] Remove the React/clipboard test harness and duplicate
permission-test matrix. Confirm no fixture inputs or outputs are
affected.

Validation: 28 focused Builder tests, Builder typecheck, repository
lint, and `git diff --check` pass on this revision. The earlier CLI
documentation checks passed and their inputs are unchanged. An earlier
GPT-5.4 mini subagent at low reasoning passed a focused native MCP test
against commit 072a8dc534: given only a link and text-edit request, it
changed the correct heading among three identical-looking headings,
leaving all other fixture records unchanged. It used four MCP calls with
broad instance/text reads. That agent test used an isolated local
fixture; it was not a production or shared-Slot agent test. Screenshots
and the full agent evaluation suite were not run.

Closes #4610
Ref #4364
2026-09-08 15:23:29 +02:00
Oleg Isonen 05706b5425 feat: Make Content Block templates the MDX authoring interface (#6307)
Closes #6306

## Outcome

Content Block templates are now the public component model for MDX
authoring.

- Standard Markdown resolves through matching Content Block templates.
- PascalCase JSX resolves to a named template first, then to a
registered component.
- Adding or changing a template immediately affects matching MDX,
including content authored before that template existed.
- Every supported standard MDX element is present in new Content Blocks
by default.
- Template `name` is a stable PascalCase JSX identifier. `label` remains
optional display text only.
- Lowercase JSX represents native HTML and SVG without exposing
`ws.element` in authored syntax.
- Unknown, ambiguous, and unsafe JSX stays in source, produces a
source-ranged Builder diagnostic, and is omitted from published output.
- Frontmatter images preserve exact `$ref` values and use the Asset
description for `alt` text.
- Keyboard and mouse insertion from the slash menu remove the trigger
slash and preserve the selected template.

## Public MDX contract

- Markdown is canonical for standard prose.
- Lowercase JSX creates native elements:
`<section><p>Text</p></section>`.
- PascalCase JSX uses templates and registered components:
`<PromotionCard />`, `<Heading tag="h1">Title</Heading>`, and `<YouTube
src="https://youtu.be/KI5JHpzBK1s" />`.
- Component-name collisions use stable qualified identifiers such as
`Checkbox` and `RadixCheckbox`.
- A self-closing template keeps its designed default subtree. An
explicitly paired empty template clears it.
- Static quoted values and bare booleans are supported.
- Imports, exports, expressions, spreads, functions, executable
JavaScript, unsafe attributes, `<$.*>`, and authored `<ws.element>` are
rejected.
- `ws` remains only for compiler primitives with no React component.

## Implementation

- [x] Add stable, unique template names and keep them separate from
labels.
- [x] Parse and validate Markdown and JSX through their ASTs with
precise source ranges.
- [x] Share component identity between template JSX, Content MDX,
Builder materialization, reload, copy, and publication.
- [x] Resolve templates live after add, edit, rename, reorder, removal,
undo/redo, collaboration updates, and reload.
- [x] Preserve authored props, children, Asset references, template
defaults, and the exact slash-menu template choice.
- [x] Make one typed MDX semantic inventory drive default templates and
slash-menu eligibility.
- [x] Move component-dependent template assembly into the component
registry.
- [x] Replace production `$`, `animation`, and `radix` JSX namespaces
with direct component imports.
- [x] Keep only explicit compiler primitives in `ws` template JSX.
- [x] Migrate repository templates, fixtures, stories, examples, MCP
guidance, CLI guidance, and generated outputs.
- [x] Add Builder unit and Playwright coverage for authoring,
diagnostics, insertion, synchronization, reload, and publication.
- [x] Review and update the authoritative Content Block and generated
MCP documentation.

## Compatibility

- Persisted project data continues to use namespaced component IDs and
`ws:element` for generic HTML elements. No project-data migration is
required.
- Legacy `<ws.element ws:tag="…">`, `<ws.element ws:name="…">`, `<$.*>`,
`<animation.*>`, and `<radix.*>` input remains readable during the
compatibility window.
- Current serializers and repository-owned sources emit only canonical
Markdown, lowercase intrinsic JSX, or PascalCase identifiers.
- Untouched user MDX is not rewritten automatically. Legacy syntax is
canonicalized when its owning source is edited.
- Deferred follow-up: remove legacy namespace readers and proxy support
only after the canonical writer is deployed and existing projects are
backfilled. This is intentionally outside this PR.

## Verification

- [x] CI package tests, typechecks, lint, package boundaries, generated
assets, migrations, and size checks pass.
- [x] All six Builder E2E shards pass.
- [x] Fixture checks pass on Ubuntu and Windows.
- [x] Targeted local Content Engine, Template, SDK, registry, Project
Build, migration, Builder, component, CLI, and Playwright tests pass.
- [x] Runtime contracts, CLI docs, and MCP docs were regenerated from
their owning sources.
- [x] Local fixture generation completed without an unexpected fixture
diff.
- [x] Documentation impact reviewed and included in this PR.
- [x] Review and approve the intentional List visual snapshot change.
- [x] Run the complete agent evaluation suite after explicit approval.
- [x] Run the network fixture link/sync pipeline against the matching PR
deployment after explicit permission.

## Known visual change

The generated List story now targets its actual default `ul` element
instead of `ol`, so the default list styling appears in the visual
snapshot. The visual change was reviewed and the approved visual check
passes.
2026-09-06 15:19:54 +02:00
Oleg Isonen 55920c57c4 fix: Show all Markdown and Assets query diagnostics (#6308)
Ref #6305

## Outcome

Show actionable Markdown, MDX, and Assets query problems in the editor,
query Diagnostics, API, and MCP.

The current implementation substantially improves source diagnostics,
query error aggregation, query scoping, exact paths, MCP feedback, and
loading states. The exhaustive audit below also records what is not
complete yet.

## Completion rule

An error state is complete only when all applicable checks pass:

- [x] The editor uses the production validator and shows every issue for
the open file.
- [x] Query execution uses the same validation and returns every issue
for every file that matches the query, not only the returned page.
- [ ] MCP can reproduce the state through a real tool call and returns
the same code, severity, message, file, and location.
- [x] Diagnostics shows fatal errors and nonfatal warnings in the
correct place.
- [x] Exact file is always present; line and column are present whenever
the parser can determine them.
- [x] Multiple issues in the same file, multiple files, and multiple
validation phases are returned together.
- [ ] A regression test proves failure without the behavior and success
with it.

## Cross-surface contract

### Required validator parity

- [x] Editor, query execution, and MCP file operations invoke the same
exported production file-validation pipeline for byte decoding,
source/frontmatter, Markdown/MDX, Content Block semantics, and
references. Copied schemas, adapter-only checks, or reimplementations do
not count as parity.
- [x] Query builder, query execution, `validate-asset-query`, and
`preview-asset-query` invoke the same exported structural and
catalog-aware semantic query validator with the same project snapshot
and context.
- [x] Every surface preserves the same complete, deterministically
ordered diagnostic array: severity, code, message, phase/scope, asset
ID, file path, line/column/range, query path, and
fatal-versus-warning/result behavior. UI, API, and MCP serializers must
not drop or remap fields.
- [ ] A table-driven contract suite calls the real editor, query, API,
and MCP entry points—including MCP upload, update, download, validate,
and preview—and compares full normalized arrays for multiple errors,
multiple warnings, multiple files, and pre-pagination matches.

- [x] Markdown and MDX source syntax/frontmatter use
validateTextAssetSource in editor, query compilation, and MCP file
tools.
- [x] Query request execution and MCP local parsing use
assetQueryRequest.
- [x] Fatal query setup errors retain all detected semantic errors and
accompanying warnings.
- [x] Nonfatal query warnings retain a stable code and exact query path.
- [x] Source diagnostics retain severity, phase, code, asset ID, path,
and available source position.
- [x] Query Diagnostics excludes diagnostics produced only by unrelated
published queries.
- [x] Query, Preview, and Diagnostics tabs show loading states.
- [x] Authenticated diagnostics can show a sanitized internal message;
public/pre-auth errors remain redacted.
- [x] Share byte-level UTF-8 validation instead of decoding separately
in query and MCP.
- [x] Make the query editor and MCP call one literal shared
query-validation function before execution.
- [x] Share cross-file reference validation across editor, query, and
MCP file operations.
- [x] Share contextual Content Block/template validation across all
applicable surfaces.
- [x] Preserve an invalid stored query in the editor and explain it
instead of replacing it with defaults.
- [x] Define and enforce whether “matches the query” includes every
pre-pagination match; current source validation follows
returned/paginated IDs.
- [x] Continue through source, reference, and runtime phases where safe
so one phase does not hide later issues.
- [x] Map every user-triggerable graph/storage/runtime failure to a
structured public error instead of INTERNAL_ERROR.
- [ ] Label candidate-file warnings separately from returned-file
diagnostics.

## File transport and encoding

- [x] Empty Markdown and MDX are accepted.
- [x] UTF-8 BOM and CRLF are accepted.
- [x] Whole Markdown/MDX file over 1 MiB is detected.
- [x] Missing or unavailable selected content is reported.
- [x] Invalid UTF-8 frontmatter returns the same structured diagnostic
in query and MCP.
- [x] Invalid UTF-8 body returns the same structured diagnostic in query
and MCP.
- [x] MCP download must use fatal UTF-8 decoding rather than replacement
characters.
- [x] Validate bytes before committing an upload, or return the
committed Asset ID and explicit cleanup state when post-commit feedback
fails.
- [x] Reject filename/format mismatches instead of allowing either hint
to bypass Markdown/MDX validation.
- [x] Storage status, missing body, invalid metadata, revision mismatch,
format mismatch, short read, and excess read each return structured file
context.
- [x] Content changing during validation has a clear retryable conflict
diagnostic.

## Frontmatter

- [x] No frontmatter.
- [x] Empty frontmatter.
- [x] Opening delimiter without a closing delimiter.
- [x] Frontmatter over 64 KiB.
- [x] Invalid YAML grammar.
- [x] Multiple YAML parser errors.
- [x] Duplicate keys.
- [x] YAML aliases.
- [x] Unsupported YAML tags.
- [x] Scalar or array root instead of an object.
- [x] Non-JSON values, NaN, Infinity, and unsafe integers.
- [x] Nesting deeper than 8.
- [x] More than 256 fields/array entries.
- [x] One string over 16 KiB.
- [x] Normalized indexed properties over 64 KiB.
- [x] Frontmatter warnings remain nonfatal and visible.
- [x] Collect all post-parse normalization/limit errors; this phase
currently stops at the first.
- [ ] Prove every state above through real MCP upload, update, download,
query, and Diagnostics paths.

## Markdown body

- [x] Treat ordinary CommonMark/GFM as permissive; malformed-looking
Markdown and raw HTML are not automatically syntax errors.
- [x] Preserve and validate the body when frontmatter is invalid.
- [x] Detect the 1 MiB body/file limit.
- [x] Handle body extraction and bounded excerpts.
- [x] Return a shared structured invalid-UTF-8 body error from every
byte-reading surface.
- [ ] Distinguish author errors from internal
excerpt-limit/configuration errors.
- [ ] Prove body-size and decoding states through real MCP and
Diagnostics.

## MDX grammar and safety

- [x] Malformed MDX/JSX grammar.
- [x] Imports.
- [x] Exports.
- [x] Executable flow expressions.
- [x] Executable text expressions.
- [x] JSX expression attributes.
- [x] JSX spread attributes.
- [x] Arbitrary components.
- [x] HTML-looking JSX outside ws.element.
- [x] Unsupported/raw MDX node types.
- [x] Duplicate attributes and normalized alias collisions.
- [x] Event-handler attributes.
- [x] srcdoc, srcset, and dangerouslySetInnerHTML.
- [x] Unsafe URL protocols in link/source/action-like props.
- [x] Forbidden tags: base, link, meta, noscript, script, style,
template, title.
- [x] Conflicting, empty, or non-static ws:name and ws:tag.
- [x] More than 20,000 nodes.
- [x] Nesting deeper than 100.
- [x] More than 4,000 props.
- [x] Multiple recoverable unsafe-mdx warnings are collected.
- [ ] Collect multiple fatal grammar/structural errors when parser
recovery can do so; unrecoverable parsing currently stops at the first
fatal error.
- [x] Preserve sibling expression warnings when unsupported JSX is also
present; {first()}, <Card />, {second()} currently reports only the Card
warning.
- [ ] Prove every state above through real MCP and Diagnostics, not only
validator unit tests.

## Content Block semantics

- [x] Unresolved template warning exists.
- [x] Unknown template prop warning exists.
- [x] Incompatible template prop warning exists.
- [x] Design-only template prop warning exists.
- [x] Stale template prop warning exists.
- [x] Invalid materialized HTML content model error exists.
- [x] Run these checks for an MDX file even when no active editor
Content Block root has materialized it.
- [x] Run applicable semantic checks in query Diagnostics.
- [x] Run applicable semantic checks for MCP upload as well as
contextual update/download.
- [x] Return all Content Block frontmatter diagnostics; one path still
throws only diagnostics[0].
- [ ] Add real MCP and UI coverage for every semantic state.

## Cross-file references

- [x] Non-string $ref marker.
- [x] Invalid reference URL.
- [x] Invalid fragment encoding.
- [x] Invalid fragment.
- [x] Invalid JSON Pointer.
- [x] Missing referenced document.
- [x] Missing referenced JSON path.
- [x] Missing referenced local Asset warning.
- [x] Unsupported target representation.
- [x] Reference cycle.
- [x] Duplicate document ID or URL.
- [x] Missing graph source or target.
- [x] Root not found.
- [x] Duplicate/unused/unresolved reference.
- [x] Invalid resolved value.
- [x] Content identity conflict.
- [x] Target revision mismatch.
- [x] Representation selector required/invalid.
- [ ] Diagnose {$ref: value, extraKey: value} as an invalid marker or
explicitly document it as ordinary data.
- [x] Collect all bad references and targets instead of stopping at the
first graph failure.
- [x] Validate references from the editor and MCP file tools, not only
query compilation.
- [x] Map graph compilation/resolution, Markdown-document, and
JSON-document errors to exact structured Diagnostics rows.
- [x] Prevent an unrelated file's graph error or duplicate canonical URL
from blocking an exact-file query.
- [ ] Prove missing targets, cycles, bad representations, and bad JSON
paths through real MCP.

## Query request and structure

- [x] Invalid JSON, invalid request UTF-8, empty/non-object request, and
request over 512 KiB.
- [x] Missing query and unknown request/query keys.
- [x] Invalid result mode.
- [x] Malformed where/all/any/filter shape.
- [x] More than 32 filters or children.
- [x] Filter nesting deeper than 8.
- [x] Empty field path or segment.
- [x] Field path deeper than 9.
- [x] Bare properties path.
- [x] Unknown or nested standard field.
- [x] Missing or unknown operator.
- [x] Non-JSON operator value.
- [x] Invalid in value or more than 1,000 in values.
- [x] Non-boolean exists/isEmpty value.
- [x] More than 8 sort entries.
- [x] Invalid sort field or direction.
- [x] first/last without a sort.
- [x] Invalid output mode or includeMetadata.
- [x] Duplicate output fields or more than 256 fields.
- [x] No selected output.
- [x] Negative, fractional, or over-1,000 limit.
- [x] Negative, fractional, or over-1,000 offset.
- [x] Invalid content mode.
- [x] Invalid full/markdown-body-ref maxBytes outside 1..1 MiB.
- [x] Invalid range offset/length or range over 256 KiB.
- [x] Empty or over-255-character index revision.
- [x] Multiple simultaneous schema issues retain exact query paths.
- [x] Make content option objects strict; unknown keys are currently
stripped.
- [x] Show an invalid persisted resource body rather than silently
loading the default query.
- [x] Aggregate unknown-key errors with every other structural issue;
MCP currently stops at the first unsupported key.
- [x] Add field-value type validation, for example reject size gt
"large" and id contains {}.
- [x] Warn about unobserved dynamic fields selected only as output.
- [ ] Prove the full structural matrix through the MCP server/API end to
end; the current broad matrix tests the local adapter.

## Query semantic states

- [x] INCOMPATIBLE_OPERATOR is fatal for standard fields.
- [x] UNOBSERVED_FIELD is a nonfatal warning.
- [x] INCOMPATIBLE_OBSERVED_TYPES is a nonfatal warning.
- [x] STRUCTURED_SORT_ORDER is a nonfatal warning.
- [x] Every incompatible standard-field operator is collected.
- [x] Fatal semantic errors retain nonfatal warnings.
- [x] Successful warnings appear with exact query paths.
- [ ] Reproduce every catalog-dependent semantic state through real MCP.
- [x] Test repeated warnings at distinct query paths without accidental
deduplication.

## Query execution and hydration

- [x] Stale index revision.
- [x] result one matching multiple files, including matchedCount.
- [x] Candidate document limit of 1,000.
- [x] Result payload limit of 16 MiB.
- [x] Hydrated file count limit of 20.
- [x] Per-file hydration limit of 1 MiB.
- [x] Total hydration limit of 2 MiB.
- [x] Range limit of 256 KiB.
- [x] Binary content requested as text.
- [x] Invalid UTF-8 during hydration.
- [x] Missing, stale, or conflicting content identity.
- [x] Canonical metadata/byte-count mismatch.
- [x] Storage returning more bytes than requested.
- [x] Missing content reader.
- [x] Selected content not returned.
- [x] Runtime Asset URL unavailable.
- [x] Document graph document-count/per-file/total limits.
- [x] Classify candidate-document and result-byte limits as
CONTENT_LIMIT_EXCEEDED instead of INVALID_REQUEST.
- [x] Give unavailable URL, missing reader/content, storage, selection,
and assembly failures stable codes and exact asset/file details instead
of INTERNAL_ERROR.
- [x] Never silently turn an exact-file unavailable-content failure into
an empty successful result.
- [x] Add direct regression tests for duplicate identity, excess bytes,
and canonical byte mismatch.
- [ ] Reproduce every runtime/hydration state through real MCP.

## Scoping and aggregation

- [x] Errors and warnings from multiple loaded result files are returned
together.
- [x] Nonfatal errors do not suppress successful query results.
- [x] Unrelated published-query diagnostics do not replace current-query
diagnostics.
- [x] File path and available line/column are shown.
- [x] Exact nested query paths are shown.
- [x] Validate and report every pre-pagination matching file if that is
the product definition.
- [ ] Explain candidate metadata warnings when invalid metadata prevents
deciding whether a file matches.
- [ ] Stop including METADATA_PREPARATION_FAILED globally unless the UI
labels its candidate scope.
- [x] Aggregate reference errors instead of stopping at the first.
- [x] Aggregate across validation phases where safe.
- [x] Add a matrix for same-file multiple errors, multiple files,
outside-page errors, filter-only candidate files, and unrelated queries.

## Transport, authorization, cancellation, and internal failures

- [x] UNAUTHORIZED and FORBIDDEN.
- [x] REQUEST_CANCELLED in query HTTP paths.
- [x] Sanitized authenticated internal message.
- [x] Generic unauthenticated/public internal message.
- [x] Preserve structured causes for document-load HTTP
status/body/metadata failures.
- [x] Preserve structured causes for object-storage read failures.
- [x] Preserve structured causes for graph selection/assembly failures.
- [ ] Test MCP/API cancellation directly.
- [ ] Reproduce authorization, cancellation, storage, and
sanitized-internal states through real MCP where safe.

## MCP reproduction

- [x] Real MCP reproduction: multiple Markdown YAML errors.
- [x] Real MCP reproduction: multiple simultaneous structural query
errors.
- [x] MCP update returns Markdown and MDX source diagnostics.
- [x] MCP upload returns Markdown and MDX source diagnostics.
- [x] MCP download returns Markdown and MDX source diagnostics.
- [x] Temporary remote fixtures are removed after reproduction.
- [ ] Real MCP reproduction for every frontmatter code.
- [ ] Real MCP reproduction for every MDX fatal/warning class.
- [ ] Real MCP reproduction for every Content Block semantic diagnostic.
- [ ] Real MCP reproduction for every reference/graph error.
- [ ] Real MCP reproduction for every semantic query warning/error.
- [ ] Real MCP reproduction for every hydration/runtime/limit error.
- [ ] Real MCP reproduction for auth, cancellation, storage, and
internal errors where safe.
- [ ] A durable table-driven test proves code/severity/message/path
parity between editor, query, Diagnostics, and MCP.
- [x] Preserve stable top-level error identity and structured details
through MCP instead of flattening known errors to MCP_TOOL_FAILED.
- [x] Prove failed post-commit MCP feedback reports whether a mutation
committed and returns enough identity for cleanup.

## Real MCP recheck and follow-up fixes (2026-09-03)

Ran the branch CLI against the PR preview backend. The first run exposed
that the saved CLI origin pointed to a different backend, so those
results were discarded. Every MCP result below came from the PR preview.
All temporary remote and local fixtures were removed afterward. The
failures found in that run were then fixed and covered by regression
tests.

- [x] Exact broken Markdown query returns FRONTMATTER_INVALID with
severity, filename, message, line, and column.
- [x] Exact MDX query returns unsafe-mdx with severity, filename,
message, line, and column.
- [x] A no-extension query returns warnings from both matching Markdown
and MDX files.
- [x] A Markdown-only query excludes the unrelated MDX warning.
- [x] Two fatal MDX files inside the returned page are both reported;
matching warnings remain attached.
- [x] Structural query validation returns three independent errors with
exact paths in one call.
- [x] Semantic query validation returns three fatal operator errors and
two warnings with exact paths in one call.
- [x] MCP upload, update, download, and exact-file query return multiple
source diagnostics for the same MDX source.
- [x] Query preview reports a missing Asset reference as ASSET_NOT_FOUND
with the source file and reference.
- [x] All temporary MCP fixtures were deleted and the saved CLI
connection was restored.
- [x] Fixed after recheck: validate every matching file before
pagination, including broken files outside the returned page.
- [x] Fixed after recheck: unrelated graph failures no longer block an
exact-file query.
- [x] Fixed after recheck: update, download, editor, and query paths use
shared reference validation.
- [x] Fixed after recheck: graph failures map to structured diagnostics
with the exact file.
- [x] Fixed after recheck: shared fatal UTF-8 validation runs before
upload writes and preserves structured error identity.
- [x] Fixed after recheck: MCP rejects filename/format mismatches.
- [x] Fixed after recheck: MDX validation keeps sibling and nested
warnings around unsupported components.
- [x] Fixed after recheck: MCP preserves stable top-level error codes
and structured details.
- [x] Fixed after recheck: query and MCP serializers preserve full
normalized diagnostic fields.
- [x] Fixed after recheck: unknown keys are aggregated with all other
structural query errors.
- [x] Fixed after recheck: shared query validation covers literal types,
output-only fields, and strict content options.
- [x] Fixed after recheck: unavailable selected content returns an
exact-file diagnostic instead of a silent empty result.

## Tests and delivery

- [x] Full Builder suite: 2,707 tests.
- [x] Content Engine suite: 445 tests.
- [x] Asset uploader suite: 211 tests.
- [x] Project runtime/MCP targeted suite: 128 tests.
- [x] CLI/MCP suite: 1,051 tests.
- [x] Relevant package typechecks, lint, generated docs, and diff
checks.
- [x] Documentation impact reviewed and generated Content Engine/MCP
reference updated.
- [x] Add the missing cross-surface error matrix described above.
- [ ] Demonstrate red/green behavior for every new matrix case.
- [ ] Run the complete agent evaluation suite after explicit approval.
- [ ] Re-run all checks after the remaining checklist is implemented.

## Compatibility and risk

Normal query result and expression-binding shapes remain unchanged.
Diagnostics add optional fields. Existing public/pre-auth error
redaction remains intact. The remaining unchecked items are broader
exhaustive real-MCP coverage, parser-recovery limits, candidate-warning
labeling, cancellation coverage, and agent evaluations.
2026-09-04 16:00:28 +02:00
d292502037 fix: Paste in Firefox navigator (#6329)
## Summary

- Read every clipboard mime type before the first await in both paste
handlers.
- Call the paste handler registered by the active mode from `emitPaste`
instead of dispatching a synthetic clipboard event.
- Add regression coverage for both paths: a clipboard event that
disconnects after dispatch, and a Firefox-shaped `ClipboardEvent`.

## Root cause

Two independent Firefox behaviors, one per entry point.

Keyboard paste: the plugin loop re-read `event.clipboardData` on every
iteration. Firefox disconnects a paste event's `DataTransfer` as soon as
the handler yields, so once `pageText` — first in the list, and async —
awaited, every later plugin read an empty string and `instanceText`
never received the payload. Chrome keeps the data readable across
microtasks, which is why this only surfaced in Firefox. Introduced in
#5798, which put `pageText` at the head of the plugin list.

Context menu paste: `emitPaste` dispatched `new ClipboardEvent("paste",
{ clipboardData })`. Gecko's `ClipboardEventInit` has no `clipboardData`
member, so the event arrived with a non-null but empty `clipboardData`.
`validateClipboardEvent` only rejects a null one, so the paste silently
did nothing — no toast, no error.

Measured in Firefox 134 against Chrome and Edge 152: `getData` after one
microtask returns the payload in Chromium and an empty string in
Firefox; a synthetic event's `getData` likewise returns the payload in
Chromium and an empty string in Firefox.

No behavior change in Chromium. Every plugin returns `pasteIgnored`
before its first await, so reading all mime types up front yields the
same input the loop received before.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/shared/copy-paste app/builder/shared/asset-manager
app/shared/commands-emitter.test.ts` — 335 tests passed.
- `pnpm --filter @webstudio-is/builder typecheck` — passed.
- `pnpm exec oxlint --deny-warnings apps/builder/app/shared/copy-paste/`
— passed.
- `pnpm exec oxfmt --check
apps/builder/app/shared/copy-paste/copy-paste.ts
apps/builder/app/shared/copy-paste/copy-paste.test.ts` — passed.
- Reverted each fix in turn against the new tests: 13 failures for the
keyboard path, 2 for the context menu, all passing again once restored.
- e2e runs Chromium only, and the previous test `DataTransfer` never
disconnected, so neither path was covered before.

Closes #6288

---------

Co-authored-by: Anmol <anmol@fathershops.com>
Co-authored-by: Oleg Isonen <oleg008@gmail.com>
2026-09-04 15:36:20 +02:00
Oleg Isonen e33a269802 fix: show unsupported browser warning in a dialog (#6332)
The unsupported-browser warning now uses the Builder’s regular modal
dialog. Users can acknowledge it and continue, while the small-window
state remains a blocking alert.

Closes #6331

## Implementation

- Render the existing browser guidance and links inside the
design-system `Dialog`.
- Set the dialog to modal and keep close, Escape, outside-click, and
**Continue** dismissal behavior.
- Keep unsupported-browser detection, preview-mode suppression, and
Builder-ready timing unchanged.
- Keep the small-window alert blocking and remove its now-unused
dismissible-alert path.
- Add a focused Storybook state and browser regression test.
- Increase Main and Visual Regression workflow job timeouts from five to
seven minutes after otherwise healthy jobs were cancelled during slow
dependency setup.
- Run failed E2E diagnostics and service teardown through the backend
shell script so cleanup does not depend on pnpm remaining on `PATH`
after cancellation.
- Route `safe-to-deploy` label events through one reusable gate that
starts both Main and Vercel workflows.
- Remove revision-specific deployment and visual approval labels in one
synchronization-only cleanup job.
- Reduce expected skipped jobs from 6 to 1 on a normal branch update and
from 17 to 3 on a visual approval label event.

No migrations or compatibility changes are required.

## Todo

- [x] Replace the unsupported-browser alert with a modal dialog.
- [x] Preserve the small-window blocking alert.
- [x] Add regression coverage.
- [x] Give CI enough time for slower dependency setup.
- [x] Keep E2E cleanup available after runner cancellation.
- [x] Simplify label-triggered Main and Vercel workflows.
- [x] Consolidate revision-specific approval cleanup.
- [x] Review documentation impact: no product documentation changes are
needed for this presentation-only warning update.
- [x] Review fixture impact: no fixture inputs or generated fixture
outputs are affected.

## Verification

- [x] Focused browser regression test
- [x] Builder typecheck
- [x] Oxlint on changed files
- [x] Oxfmt and `git diff --check`
- [x] Workflow YAML parsing and backend shell syntax checks
- [x] Workflow trigger, nesting, permissions, and reusable-job contract
assertions
- [x] Prettier checks for changed workflows
- [ ] Storybook screenshot review was not run locally because explicit
visual-test approval was not available.

## Known limitations

- The visual state was not screenshot-tested locally.
- The reduced `pull_request_target` fan-out takes effect after these
workflow files reach the default branch, because GitHub loads those
workflow definitions from that branch.
2026-09-04 15:06:34 +02:00
Oleg Isonen b832a8e808 refactor: Centralize Builder ID generation (#6323)
## Outcome

Builder-owned code now routes standard ID generation through
`@webstudio-is/sdk/createId` while preserving existing NanoID and UUID
formats.

Closes #6322

## Technical choices

- Use `createId("nano")` for Builder records, assets, transaction IDs,
and other existing NanoID-backed values.
- Use `createId()` for UUID-backed database records and browser-side
client IDs.
- Keep `BuilderRuntimeContext.createId` as an injectable dependency.
Runtime operations, copy/paste, and deterministic tests rely on that
injection.
- Keep Node's `randomUUID` in CLI paths that explicitly support running
without the Web Crypto global.
- Keep specialized generators for custom-alphabet project domains and
actual hash generation.
- Remove direct `nanoid` dependencies from packages where no specialized
use remains.
- Do not migrate existing data or change database schemas.

## Todo

- [x] Migrate Builder app ID creation.
- [x] Migrate project runtime and template defaults.
- [x] Migrate asset, project, domain, and authorization-token IDs.
- [x] Preserve runtime ID injection.
- [x] Review remaining direct generators as explicit exceptions.
- [x] Add NanoID-format regression coverage for the default runtime
context.
- [x] Review documentation impact: no user-facing documentation changes
are needed.
- [x] Review fixture impact: no fixture input or generated output format
changes are needed.
- [x] Run targeted tests, typechecks, lint, and package-boundary checks.

## Verification

- Targeted Builder tests: 153 passed after preserving runtime-context
injection.
- Project-build runtime tests: 270 passed.
- Asset uploader tests: 61 passed.
- Authorization-token tests: 17 passed.
- Project tests: 52 passed.
- Domain tests: 18 passed.
- CLI tests: 32 passed.
- Typechecks passed for Builder, project-build, asset-uploader,
authorization-token, project, and domain.
- `pnpm lint` passed.
- `pnpm check:package-boundaries` passed.
- The runtime-context format test was demonstrated to fail with a UUID
default and pass with the NanoID default.
- Agent evaluations were not run because they require separate explicit
approval.
2026-09-03 15:56:23 +02:00
Oleg Isonen fc9fa39fbc fix: Reduce style source padding (#6315)
## Result

Style source controls now use spacing level 1 for their inner padding
instead of spacing level 2.

## Implementation

- [x] Change only the inner padding of `StyleSourceControl`.
- [x] Keep interactions and accessible behavior unchanged.
- [x] Review documentation impact: no documentation changes are needed
for this visual spacing correction.
- [x] Verify formatting, lint, and Builder types.

## Verification

- `pnpm exec oxfmt --check
apps/builder/app/builder/features/style-panel/style-source/style-source-control.tsx`
- `pnpm exec oxlint --deny-warnings
apps/builder/app/builder/features/style-panel/style-source/style-source-control.tsx`
- `pnpm --filter=@webstudio-is/builder typecheck`

No fixture inputs or generated outputs are affected. No known
limitations.

Closes #6314
2026-09-02 14:57:23 +02:00
Oleg Isonen d4b1994a6a fix: Improve editor selection and project tag contrast (#6302)
Closes #6301

## Summary

- Restore Dashboard project tags to the pre-color-refactor treatment:
fixed white text over a 30% black scrim in both application themes.
- Use one high-contrast text-selection treatment across native inputs,
textareas, contenteditable fields, and every shared CodeMirror editor.
- Preserve the existing subtle selection treatment for Navigator,
Project Settings, command menus, Dashboard navigation, and other
selected controls.
- Add direct Storybook coverage for the CSS fragment editor.
- Simplify chromatic color derivation from 571 to 329 lines without
changing computed colors.

## Technical choices

- Keep project tags independent from application-theme polarity because
they overlay arbitrary thumbnail artwork.
- Keep the existing `selectionBackground` control-state export unchanged
at its original 12% light / 20% dark accent blend.
- Define separate `--background-text-selection` and
`--foreground-on-text-selection` tokens exclusively for selected text.
- Use the bounded accent in light mode and a luminance-lifted accent in
dark mode so selected text remains distinct even with extreme custom
theme colors.
- Style CodeMirror through its existing selection layer and
`::selection` CSS hooks; no selection tracking or editor-state logic is
added.
- Leave canvas rich-text selection independent: authored canvas
documents do not load the Builder UI theme stylesheet.
- Replace five duplicated XYZ clamp formulas with the exact
upper-luminance cap they reduce to. The removed lower-bound and
white-mixing branches were unreachable because the minimum was fixed at
zero.

## Todo

- [x] Restore the original project-tag scrim and foreground in both
themes.
- [x] Inventory native, CodeMirror, and canvas rich-text selection
paths.
- [x] Unify native and shared CodeMirror text-selection colors.
- [x] Keep text selection independent from selected control and
navigation states.
- [x] Add the missing CSS fragment editor story.
- [x] Add light- and dark-mode browser regressions and confirm they fail
before the fix.
- [x] Enforce 4.5:1 selected-text contrast and 3:1 highlight-to-surface
contrast across supported theme inputs.
- [x] Remove duplicated and unreachable chromatic color derivation while
preserving outputs.
- [x] Review documentation impact; the design-system color-token
reference is updated, with no user-facing product documentation change
required.
- [x] Verify fixture impact; no fixture inputs or generated outputs are
affected.

## Verification

- `pnpm --filter @webstudio-is/design-system test` — 227 tests passed,
including the control/text separation regression, Navigator tree
interaction, default-palette comparisons, and the full supported-theme
contrast sweep
- `pnpm --filter @webstudio-is/design-system typecheck`
- `pnpm --filter @webstudio-is/builder exec vitest run
app/shared/code-editor-base.browser.test.tsx --project
app-browser-shared`
- `pnpm --filter @webstudio-is/builder exec vitest run
app/dashboard/projects/project-card.browser.test.tsx --project
app-browser-canvas-dashboard`
- `pnpm --filter @webstudio-is/builder typecheck`
- Targeted `oxlint`, formatting, and `git diff --check`
- Live dark-mode Dashboard verification of the restored subtle
navigation selection
- Storybook visual checks for the full color system and input/CodeMirror
selection in light and dark modes
2026-09-01 17:05:14 +02:00
Oleg Isonen 1de752f2ae fix: Keep Slot colors consistent (#6296)
## Summary

- Define the purple treatment as a semantic secondary-accent CSS color
family.
- Use its foreground in the Layers panel and its
background/on-background pair on the canvas.
- Add visual regression coverage that shows the actual Navigator and
canvas Slot treatments together.
- Keep the same purple in light mode and use a more chromatic, readable
foreground in dark mode.

## Implementation

The dark-theme migration replaced the former reusable-instance color
with two unrelated choices: the blue accent foreground in the navigator
and an accent-derived purple canvas fill.

This change adds three generated, theme-aware semantic colors using
Craft's existing naming and explicit foreground pairing conventions:

- `--background-accent-secondary` for Slot canvas labels and outlines.
- `--foreground-accent-secondary` for reusable instances in the
navigator.
- `--foreground-on-accent-secondary` for content on the strong
secondary-accent background.

The secondary accent derives from the theme accent. Its foreground keeps
the existing strong purple in light mode and raises dark-mode lightness
without washing out its chroma, so reusable instances remain visibly
distinct from ordinary Navigator text.

Portable light and dark visual stories render the real `NavigatorTree`
beside the real canvas `Outline` and `Label` Slot variant. This covers
the user-reported cross-surface consistency and the dark-mode foreground
directly.

## Todo

- [x] Trace the regression to the color-system migration.
- [x] Add the secondary-accent semantic CSS family and generated types.
- [x] Add foreground/background contrast contracts across supported
themes.
- [x] Update navigator and canvas consumers.
- [x] Add light and dark visual regression coverage for the actual
Navigator and canvas treatments.
- [x] Confirm the visual comparison fails before approval and inspect
the rendered screenshot.
- [x] Run Design System and focused Builder tests.
- [x] Run Design System and Builder typechecks, scoped lint, and
formatting checks.
- [x] Review fixture impact; no fixture inputs or generated fixture
outputs are affected.
- [x] Review documentation impact; update the internal color-system
reference. Authoritative user documentation is unaffected.

## Verification

- `pnpm --filter @webstudio-is/design-system test` — 223 tests passed.
- `pnpm --filter @webstudio-is/builder exec vitest run --project
core-browser-builder-features
app/builder/features/navigator/navigator-tree.test.ts` — 20 tests
passed.
- The dark secondary-accent regression test was confirmed to fail with
the washed-out foreground, then pass with the stronger chromatic
foreground.
- `pnpm --filter @webstudio-is/design-system typecheck` — passed,
including generated color-type freshness.
- `pnpm --filter @webstudio-is/builder typecheck` — passed.
- Scoped `oxlint --deny-warnings` — passed.
- Scoped `oxfmt --check` — passed.
- `pnpm visual-regression -- --base origin/main --grep
"Builder/Navigator/Navigator tree"` — expected failure with two added
visual stories; both light and dark screenshots were inspected.

The source-contract test was confirmed to fail before the semantic
variables were added, then pass with the implementation. Browser
contrast tests cover both new pairings across light/dark modes and the
complete supported theme parameter range.

Closes #6295
2026-09-01 02:51:13 +02:00
Oleg Isonen c49c0786be fix: unify dashboard background surface (#6287)
## Description

1. What is this PR about (link the issue and add a short description)

## Steps for reproduction

1. click button
2. expect xyz

## Code Review

- [ ] hi @kof, I need you to do
  - conceptual review (architecture, feature-correctness)
  - detailed review (read every line)
  - test it on preview

## Before requesting a review

- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")

## Before merging

- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
2026-08-31 21:55:59 +02:00
Oleg Isonen f953d1be37 fix: Restore the login background gradient (#6286)
## Summary

Restores the original Webstudio login gradient after the color-system
migration added a dark scrim over it. Fixed Webstudio identity artwork
now belongs to the Design System, while generic surfaces, content,
translucency, and overlays use the semantic color system instead of a
Builder-local brand color bag.

## Architecture

- The Design System exports only the fixed Webstudio background
gradient, progress gradient, and progress shadow as brand artwork.
- Generic black, white, translucent panel, and scrim entries were
removed rather than moved.
- Login and error surfaces use semantic light/dark pairings.
- Project-card tags derive their local 30% scrim from the semantic
overlay color.

## Todo

- [x] Restore the original login gradient without a scheme-dependent
scrim.
- [x] Move fixed identity artwork into the Design System package.
- [x] Remove the Builder-local brand color registry.
- [x] Replace generic fixed colors with semantic colors or local
derivations.
- [x] Add explicit dark-mode Storybook coverage for the login screen.
- [x] Run Design System and Builder typechecks.
- [x] Run Design System tests, scoped lint, formatting, and
package-boundary validation.
- [x] Review documentation impact and document ownership in the Design
System color README.

## Verification

- Design System tests: 222 passed.
- Design System and Builder typechecks: passed.
- Scoped Oxlint and formatting: passed.
- Package-boundary validation: passed.
- Focused visual regression: 23 affected stories compared; the newly
added dark login story is the only difference.
- Fixture impact: no fixture inputs or generated outputs are affected.

Ref #3818
2026-08-31 21:16:26 +02:00
Oleg Isonen 4273b7001f refactor: Organize Builder development and test tooling (#6285)
## Outcome

Builder development, backend, E2E, and CI tooling now share one
ownership model: Builder defines named commands and backend modes, while
CI only schedules those commands.

## Technical choices

- Group shared Compose files, PostgreSQL initialization, and backend
helpers under `apps/builder/backend`.
- Resolve `compose.<mode>.yaml` by convention instead of maintaining
Compose filename lists in scripts and workflows.
- Keep persistent development and disposable test Compose projects
separate by default, while allowing the standard `COMPOSE_PROJECT_NAME`
override.
- Make schema snapshots invalidate from discovered migration sources and
backend files.
- Move E2E behavior recipes into named Builder commands; retain concise
root proxies for developer entry points.
- Let Vitest allocate available browser ports instead of maintaining a
cross-package port registry.
- Keep test-suite scheduling explicit in `main.yml`, but move executable
test commands to root package scripts.
- Move fixture-size enforcement from inline GitHub JavaScript into a
locally runnable repository script.

## Todo

- [x] Consolidate Builder backend and Compose ownership.
- [x] Add named development, E2E, schema-cache, Supabase, and CI-shard
commands.
- [x] Remove Compose topology and behavior flags from CI.
- [x] Remove the static Vitest browser-port registry.
- [x] Extract fixture-size checking from the workflow.
- [x] Verify migration bootstrap, snapshot-backed E2E, tests, builds,
types, formatting, lint, generated contracts, and workflow syntax.
- [x] Review documentation impact: updated the E2E developer guide; no
user-facing `docs/` content is affected.

## Verification

- Full package, CLI, Builder core, and Builder app test suites
- Full workspace typecheck
- Builder production build
- Schema-cache generation through the complete migration history
- Supabase migration compatibility
- Snapshot-backed Playwright animation workflow
- Backend regression tests, including demonstrated fail/pass coverage
- All four Compose mode configurations
- `oxfmt`, `oxlint`, package boundaries, generated API, and generated
docs checks
- `actionlint` and Bash syntax checks

The fixture-size checker preserves the existing Linux `du` thresholds.
Its three inputs build locally; macOS allocation sizes are not directly
comparable to the Linux CI thresholds, so CI remains the authoritative
size result.
2026-08-31 21:09:54 +02:00
Oleg Isonen bc8630f413 fix: Gate Builder releases on the published CLI contract (#6282)
Closes #6280

## Summary

- Expose the deployed Builder's CLI-facing bundle contract through a
non-cached, read-only endpoint.
- Resolve and execute the exact `webstudio@latest` registry artifact,
observe the bundle version sent by its real `sync` request, and require
an exact match before assigning the release staging alias.
- Include expected and received bundle versions in runtime and
release-gate errors.

## Technical choices

- The existing generated `bundleVersion` remains the single
compatibility boundary. Builder-only changes outside that project-bundle
contract do not require a CLI release.
- The CLI sync probe runs against a local fixture API and temporary
directory. It reads the deployed runtime contract but never reads or
modifies a Cloud project.
- Verification runs after Vercel creates the exact production-built
deployment and before the release staging alias is assigned. A mismatch
therefore leaves production domains untouched.
- Registry resolution requires version and integrity metadata, then
executes the resolved immutable version rather than source from the
checkout.
- The gate requires the deployed Builder origin and cannot fall back to
the checkout's contract.

## Todo

1. [x] Add the deployed Builder contract endpoint.
2. [x] Probe the exact published CLI through its real sync request.
3. [x] Gate the release staging alias on an exact contract match.
4. [x] Report expected and received versions for compatibility failures.
5. [x] Add regression tests and verify the production Builder build.
6. [x] Review documentation and fixture impact.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/services/build-router.server.test.ts
app/services/cli-compatibility-route.node.test.ts` — 12 passed.
- `pnpm --filter webstudio exec vitest run
scripts/builder-contract-gate.test.ts
scripts/release-smoke-registry.test.ts` — 7 passed.
- Rebased onto `main` at `9ec4c820e1`.
- Builder and CLI typechecks passed.
- `pnpm lint` and changed-file formatting passed.
- `pnpm --filter @webstudio-is/builder build` passed and emitted
`/rest/cli-compatibility`.
- The changed GitHub Action parses as valid YAML.
- Real registry probe passed for `webstudio@0.293.0` against
`bundle-kzb45v`.
- Real registry probe correctly blocked current Builder contract
`bundle-1mym82p` and reported expected `bundle-1mym82p`, received
`bundle-kzb45v`.
- Missing staged Builder origin fails closed before registry execution.
- GitHub CI passed all four workflows, including all six Builder E2E
shards.
- The formerly failing empty-MDX insertion workflow passed 8 repeated
runs with 4 workers locally, then passed in Builder E2E shard 4 in CI.
- Full browser-based release smoke was not run, per the request not to
use Chrome. Agent evaluations were not run because no evaluation
approval was requested or received.
- Documentation: no user-facing documentation changes are required.
- Fixtures/generated outputs: no fixture inputs or generated outputs are
affected.

## Operational boundary

The check needs the exact deployed runtime, so Vercel creates an
unaliased `--skip-domain` deployment before verification. A failed check
cannot affect production domains or the normal release alias. Repository
automation cannot prevent a Vercel administrator from manually promoting
an arbitrary deployment outside this release path.
2026-08-31 20:43:52 +02:00
Oleg Isonen 1b48716ff4 docs: Document MDX-backed Content Blocks (#6273)
## Summary

- Document how people create, connect, edit, validate, and publish
MDX-backed Content Blocks.
- Document the complete MCP workflow, including scoped expression
sources, frontmatter updates, diagnostics, conflicts, and disconnect
behavior.
- Add two focused screenshots for the human workflow.
- Fix issues found by a low-reasoning MCP agent while executing every
documented workflow.

Ref #6182

## Implementation notes

- Connection confirmation uses the public `confirmReplacement` field
because `confirm` is reserved as a hidden transport field.
- MCP edits frontmatter with `update-content-block-frontmatter`;
materialized MDX elements are not persistent targets for generic
instance mutation tools.
- Reload now intentionally discards a conflicted local source and
accepts the Asset's latest storage revision before an edit is reapplied.
- Generated MCP documentation and runtime contracts are updated from
their source files.

## Checklist

- [x] Document the human MDX Content Block workflow.
- [x] Document MCP discovery, connection, editing, diagnostics,
frontmatter, dynamic sources, conflicts, and disconnection.
- [x] Add only screenshots that clarify otherwise hard-to-describe UI
steps.
- [x] Test every documented MDX capability through the local MCP CLI
with a low-reasoning agent.
- [x] Reproduce and fix the replacement-confirmation schema mismatch.
- [x] Reproduce and fix persistent-session conflict reload and reapply.
- [x] Add regression tests that failed before each product fix and pass
afterward.
- [x] Regenerate runtime contracts and generated MCP documentation.
- [x] Review authoritative `docs/` content and update affected pages.
- [x] Run affected tests, typechecks, formatting, and diff checks.

## Verification

- Project Build: 208 affected tests passed.
- Content Engine Asset session: 8 tests passed.
- CLI documentation tests: 14 tests passed.
- MCP docs generator: 3 tests passed.
- Content Engine, Project Build, CLI, and MCP docs typechecks passed.
- `pnpm oxfmt --check` passed.
- `git diff --check` passed.
- Low-reasoning MCP evaluation passed all documented workflows,
including a real two-client revision conflict, reload, reapply, and
final saved inspection.
2026-08-31 18:39:05 +02:00
Oleg Isonen 8718feb465 test: simplify browser testing infrastructure (#6271)
## Summary

Unifies frontend testing around three intentional boundaries:

- Vitest Node projects for pure and server tests
- Vitest Browser Mode with Playwright/Chromium for DOM and component
tests
- Playwright Test for Builder end-to-end workflows

The Builder no longer uses jsdom. Ordinary E2E workflows use an
automatically managed isolated `page` fixture; explicit contexts remain
only for simultaneous identities and suite-scoped setup.

## Technical choices

- Split Builder tests into one Node project and independent browser
projects so each Chromium/Vite process has a bounded workload.
- Added shared Browser Mode configuration for the affected SDK React
packages.
- Preserve the Builder's production top-window bridge ownership; Browser
Mode uses explicit test-only installation seams.
- Centralized Playwright context options and kept action/navigation
timeouts bounded.
- Validate Playwright filters before Docker, migrations, browser
installation, or Builder builds start.
- Preserve the first failed E2E attempt's report, traces, screenshots,
and videos when CI retries a shard.
- Split static checks, typechecks, CLI tests, core package tests,
browser package tests, and Builder projects into parallel CI jobs with
five-minute hard ceilings.
- Distribute Builder E2E tests across ten isolated shards with two
workers; parallel suites create unique per-worker setup data.
- Reuse color contrast DOM/canvas samples so the exhaustive theme sweep
takes about four seconds instead of timing out at 30 seconds.
- The removed cost is jsdom behavior drift, oversized serial check jobs,
and unnecessary E2E startup for empty selections. Real browser startup
and E2E environment setup remain.

## Todo

- [x] Split Builder pure/server tests from browser/component tests.
- [x] Migrate Builder DOM tests from jsdom to Vitest Browser Mode.
- [x] Migrate affected SDK React DOM tests to the shared Browser Mode
setup.
- [x] Remove jsdom annotations, dependencies, and compatibility setup.
- [x] Replace ordinary manual E2E contexts with the isolated `page`
fixture.
- [x] Keep explicit contexts only for concurrent identities and
`beforeAll` setup.
- [x] Preserve setup authentication isolation from workflow pages.
- [x] Add bounded Playwright action and navigation timeouts.
- [x] Preserve first-attempt E2E failure evidence across CI retry.
- [x] Reject empty E2E selections before expensive environment setup.
- [x] Parallelize repository checks into bounded CI shards.
- [x] Isolate parallel E2E setup data across workers.
- [x] Remove the color contrast sweep bottleneck.
- [x] Update E2E contributor documentation.
- [x] Review product documentation impact; no user-facing product docs
are affected.
- [x] Run the full repository tests and typechecks.
- [x] Run formatting, lint, package-boundary, generated-file, and
fixture checks.
- [x] Complete final correctness and simplification review.

## Verification

- `pnpm --filter @webstudio-is/builder test`
- `pnpm -r typecheck`
- `pnpm lint`
- `pnpm check:package-boundaries`
- `pnpm check:generated-api`
- `pnpm check:generated-docs`
- `pnpm fixtures`
- Design-system: 222 tests pass locally.
- Builder: all seven Vitest projects pass locally.
- The formerly slow E2E shard passes locally in 1m55s.
- Full database-backed Builder E2E execution passes in CI.
- All CI jobs pass; the slowest completed in 4m58s.
- No fixture inputs/outputs or generated files changed.

Ref #6270
2026-08-31 17:17:28 +02:00
Oleg Isonen f6c524572d feat: add Builder dark theme and rebuild colors
## Summary

Rebuilds Webstudio’s color system, migrates the entire Builder to it,
and introduces a complete dark theme.

A small set of theme colors and contrast controls now drives the
interface through relative CSS colors:

```text
Theme colors and contrast
    ↓
Light/dark derivation bounds
    ↓
Derived color families
    ↓
Reusable semantic colors
    ↓
Component composition
    ↓
Builder interface
```

CSS is the runtime source of truth. Light and dark modes share the same
theme inputs, so changing a theme color automatically updates its
surfaces, content, borders, states, and component presentation. The
refactor turns dark mode into a first-class Builder theme rather than a
separately maintained palette.

This is an atomic migration. Legacy color names and compatibility
aliases will not remain after all consumers have moved.

## Scope

- The color source, derivation model, semantic vocabulary, validation,
typing, and Storybook overview.
- Every design-system component.
- Builder, dashboard, authentication, shared UI, Query Builder, stories,
and tests that consume the legacy color scale.
- Removal of the legacy Stitches color scale after its final consumer is
migrated.
- A complete dark theme across application UI while keeping authored
canvas content independent.
- Complete light and dark visual verification.
- Unified browser-test infrastructure: Vitest Browser Mode for Design
System browser contracts and standard Playwright Test for all Builder
E2E workflows.

Literal colors remain only where color is the actual content or belongs
to a deliberately separate system, such as user-authored values,
external previews, brand artwork, data visualization, functional canvas
overlays, and source assets. Every remaining exception must have an
explicit owner.

## Architecture decisions

- Theme authors control six color families and three bounded contrast
controls.
- Relative CSS colors derive the rest of the system.
- Dark mode changes derivation bounds instead of maintaining a second
independent palette.
- Components consume a small reusable semantic vocabulary.
- Component states and exceptional presentation are composed inside
components.
- Builder-specific categorical meaning uses focused composite recipes,
not intent semantics or a larger global vocabulary.
- TypeScript validates public semantic CSS-variable names but does not
generate runtime colors.
- No token-manager dependency, compatibility aliases, or
component-specific public color tokens.
- A new semantic or theme variable is added only when existing semantics
and local composition cannot represent a reusable cross-feature
decision.

## Todo

### Color-system foundation

- [x] Establish direct CSS as the runtime source of truth.
- [x] Define bounded theme colors and contrast controls.
- [x] Derive light and dark modes from the same theme inputs.
- [x] Reduce the semantic inventory to reusable decisions and explicit
accessibility pairs.
- [x] Generate only TypeScript names for public semantic CSS variables.
- [x] Validate layers, references, cycles, consumption, naming,
generated types, and browser-computed contrast.
- [x] Add the Storybook color-system overview and theme controls.
- [x] Document the architecture and its relationship to the public Craft
specification.

### Design-system migration

- [x] Replace every active legacy color consumer in design-system
components and Storybook chrome.
- [x] Compose component states locally without component-specific public
tokens.
- [x] Preserve intentional preview and sample-content colors.
- [x] Add complete positive, negative, warning, informative, and accent
accessibility pairings.
- [x] Validate light and dark scheme-bound references independently.
- [x] Verify the design-system package with typecheck, lint, formatting,
Storybook, and 222 tests.

### Inventory and contract

- [x] Inventory all 319 legacy references across 107 Builder, Query
Builder, story, test, and design-system files.
- [x] Classify every reference as a direct semantic replacement, local
composite recipe, documented literal exception, or removable code.
- [x] Record the intentional authored-content, external-preview,
functional-preview, data-visualization, brand, and source-asset
exceptions.
- [x] Treat the public semantic vocabulary as closed by default and
justify any proposed addition by cross-feature reuse and accessibility
need.
- [x] Keep Builder code limited to typed public variables accepted by
`cssVar()`.

### Theme infrastructure

- [x] Add `system` (default), `light`, and `dark` application
preferences.
- [x] Persist the preference in existing client settings and resolve
`system` through `matchMedia`.
- [x] Apply the resolved scheme before paint and respond to
operating-system changes.
- [x] Expose the persisted theme control under **View → Color scheme**.
- [x] Keep authored canvas documents independent from the application
theme.
- [x] Ensure portals, overlays, embedded surfaces, and third-party
controls receive the correct interface scheme.

### Generic interface migration

- [x] Migrate shared dialogs, project settings, notifications, controls,
and Query Builder.
- [x] Migrate dashboard, authentication, publishing, and shared
application UI.
- [x] Migrate the Builder loading state, topbar, sidebars, navigator,
workspace, footer, settings, pages, alerts, and menus.
- [x] Replace fixed interface literals alongside their owning surfaces.
- [x] Remove the old scrollbar variable and hardcoded loading color.

### Style panel and style-source recipes

- [x] Migrate ordinary style-panel inputs, labels, layouts, spacing,
alignment, menus, property states, and value editors.
- [x] Model style-source provenance, kind, interaction, and error as
separate axes.
- [x] Compose categorical style-source colors from neutral and
accent-relative colors.
- [x] Use negative semantics only for actual errors, not overwritten
provenance.
- [x] Reuse one feature-owned recipe across style-source controls,
badges, menus, and values without adding global tokens.

### Canvas and editors

- [x] Migrate canvas selection, reusable outlines, grid guides, resize
handles, labels, badges, and overlays.
- [x] Keep collaborator colors as data visualization while guaranteeing
readable labels.
- [x] Prevent Builder theme values from leaking into authored content.
- [x] Migrate editor surfaces, selections, autocomplete, lint states,
and tooltips.
- [x] Derive syntax visualization from neutral and accent-relative
colors; reserve intent colors for diagnostics.

### Explicit color domains

- [x] Isolate external search and social previews from the application
scheme.
- [x] Preserve user-authored CSS values, functional color previews, and
checkerboards.
- [x] Review brand surfaces independently in both schemes.
- [x] Document every retained literal in the exception ledger.

### Stories, tests, and legacy removal

- [x] Migrate all affected Builder stories and tests.
- [x] Replace the design-system test that intentionally references the
legacy scale.
- [x] Add theme-preference and scheme-resolution behavior tests.
- [x] Remove the legacy `design-tokens.color` registry.
- [x] Remove the Stitches `colors` scale and `rawTheme.colors`.
- [x] Remove color-name conversion code that exists only for the legacy
scale.
- [x] Add a focused repository check that rejects legacy references.
- [x] Confirm no compatibility mappings or deprecated aliases remain.

### Browser-test infrastructure

- [x] Run pure Design System tests in Node and browser-dependent tests
in Vitest Browser Mode with Playwright Chromium.
- [x] Migrate all 79 Builder E2E workflows from the custom harness to
standard Playwright Test.
- [x] Replace filename-based shard discovery with standard Playwright
sharding.
- [x] Preserve clean-backend shard retries and upload Playwright failure
artifacts in CI.
- [x] Remove the custom runner, registry, and timeout infrastructure.

### Verification and documentation

- [x] Compare light-mode visual regressions directly with `main`; review
every difference rather than accepting snapshots wholesale.
- [x] Add representative dark coverage for dashboard, Builder shell,
style panel, dialogs, editors, canvas tools, and status states.
- [x] Test default and extreme custom-theme presets.
- [x] Verify semantic and categorical-recipe contrast.
- [x] Test first paint, persistence, system changes, portals, and canvas
isolation.
- [x] Run affected tests, typechecks, lint, formatting, and Storybook
checks.
- [x] Review fixture impact; no fixture inputs or generated outputs are
affected.
- [x] Review authoritative `docs/` content and update it where needed.
- [x] Review the final diff and make this checklist reflect delivered
scope.

## Current verification

- Design-system typecheck and generated-type freshness: passed.
- Design-system tests: 222 passed, including the full theme-parameter
contrast sweep and browser component coverage.
- Builder typecheck and production build: passed.
- Theme preference and scheme-resolution tests: 10 passed.
- Scoped Oxlint, formatting, and diff checks: passed.
- Design-system Storybook build and representative light/dark visual
review: passed.
- Builder E2E: 80/80 Playwright entries passed (setup plus 79
workflows).

Ref #3818
Closes #6267
2026-08-30 03:24:56 +01:00