40 Commits
Author SHA1 Message Date
Oleg Isonen 9ec4c820e1 fix: Use writable npm cache for previews (#6272)
## Summary

- Use a writable `.webstudio/preview/.npm-cache` for npm preview
installs when `npm_config_cache` is not explicitly configured.
- Preserve explicit npm cache settings and pnpm behavior.
- Document a safe recovery path that requires no cache deletion.

This removes preview startup failures caused by an unwritable default
npm cache. Initial dependency installation and its network/download cost
still remain when preview dependencies are not already installed.

## Technical choices

- Keep the cache beside the isolated generated preview because that
directory is already writable and reused across regenerations.
- Let an explicit `npm_config_cache` continue to take precedence.
- Do not retry arbitrary install failures or classify package-manager
prose.

## Todo

- [x] Reproduce npm dependency installation against an unwritable cache
before changing code.
- [x] Reproduce #6274 at the same dependency-install stage used by
session-backed screenshots.
- [x] Demonstrate the regression assertion failing before the fix and
passing afterward.
- [x] Use the preview-local cache only for npm without an explicit
cache.
- [x] Preserve explicit npm cache configuration and pnpm behavior.
- [x] Verify the real dependency installer against an unwritable project
npm cache.
- [x] Verify session-backed screenshot flow proceeds from successful
preview preparation to browser capture.
- [x] Review documentation impact and update the CLI manual plus
generated MCP documentation.
- [x] Review fixture impact; no project fixture inputs or outputs are
affected.
- [x] Review the final diff for correctness, duplication, and
unnecessary complexity.

## Verification

- Actual npm install reproduced #6269 and #6274 with `EPERM` against an
unwritable cache.
- Real preview dependency installation passed after the fix while
project npm configuration still pointed at that cache.
- Fail-before/pass-after regression demonstrated.
- `pnpm --filter webstudio test` — 65 files, 1,044 tests passed.
- `pnpm --filter webstudio typecheck`
- `pnpm lint`
- `pnpm check:generated-docs`
- Formatting and diff checks passed.
- GitHub CI after rebasing onto current `main` — 30 checks passed, 7
skipped, no failures.
- Agent evaluations were not run because they require separate explicit
approval.

Closes #6269

Closes #6274
2026-08-31 19:12:01 +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 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
Oleg Isonen f9681dc30f feat: MDX-backed Content Blocks (#6234)
## Summary

Supersede #6182 with a clean implementation of MDX-backed Content Blocks
on top of the unchanged `main` Content Block editing model. A connected
`.mdx` Asset changes where authored children are loaded and saved; it
must not change how Content mode inserts, edits, selects, moves, copies,
or deletes instances.

The previous implementation remains available in
`agent/mdx-content-block-6124` and
`agent/mdx-content-block-6124-review`. Reuse its independently tested
parsers, converters, schemas, Asset repository, template resolution,
publication, MCP, and UI work where those pieces fit the corrected
architecture. Do not port its per-operation storage routing, projected
editing stores, MDX undo/history, or atomic multi-root machinery.

Ref #6124
Ref #6182
Closes #6261

## Locked behavior

- `.md` keeps its existing Markdown behavior; `.mdx` enables MDX.
- Standard Markdown structures are stored as Markdown. JSX is used only
for structures Markdown cannot represent losslessly.
- Generic elements use `<ws.element ws:tag="…">`; template instances use
`<ws.element ws:name="…">`.
- Invalid, unsafe, or unsupported MDX never prevents editing the file or
publishing the project. Builder and publication use the same recovery
result: recoverable nodes are skipped with source-located warnings,
while an unrecoverable document renders no authored file content in
either environment.
- Connecting a source never serializes existing Content Block children
into the file. **Connect** removes ordinary persisted children,
preserves Templates, sets `src`, and uses file content.
- Disconnecting resets `src`, discards derived instances, leaves the
Content Block empty, and never changes the Asset.
- While connected, normal Content Block mutations update in-memory
instances synchronously. Asset persistence happens afterward and never
blocks rendering.
- Genuine external revision conflicts use the standard reload-required
flow. Rapid local edits are ordered/coalesced and must never conflict
with each other.
- Repeated scopes use Content Block ID plus Collection item identity.
- Multiple Content Blocks may intentionally share one MDX Asset. They
share one revisioned document session and edits propagate to every
connected occurrence.
- Missing templates show warnings/placeholders only in Builder and are
skipped on published sites.

## Reuse strategy

- [x] Audit all 62 original implementation commits and 5 later
review/fix commits
- [x] Reuse the shared Content Engine Markdown/MDX AST, parser,
serializer, diagnostics, reference discovery, and tests
- [x] Reuse SDK source schemas, instance-name utilities, unique template
naming, and source-integrity validation
- [x] Reuse template resolution/materialization and authored-tree
conversion after removing storage-operation coupling
- [x] Reuse revisioned Asset content repository, authorization, limits,
and browser/server transport
- [x] Reuse publication, document-graph, CLI, MCP, source-control UI,
frontmatter UI, and E2E work where contracts remain valid
- [x] Reuse tests as behavioral specifications; rewrite tests that
assert the rejected projection/routing architecture
- [x] Do not port per-operation MDX routing, alternate projected
mutation stores, semantic reconciliation after each command, MDX Builder
history/undo, atomic multi-file rollback, or canvas loading/saving UI


## Old-branch porting map — do not rewrite

Before implementing any checklist batch, inspect the named
implementation and tests on `agent/mdx-content-block-6124-review`. Move
the implementation and its tests first; change only the boundary that
conflicts with the rebuilt architecture. Do not create a second
implementation under a new name. When a test encodes the rejected
projected-store architecture, retain its behavioral assertion and
replace only its setup.

### Already ported

- `parseMarkdownAst`, `createMdxDocumentFromMarkdown`,
`parseMdxDocument`, `serializeMdxDocument`, `preferMarkdownSyntax`,
`discoverMdxBodyAssetReferences`, `discoverMdxAssetReferences`, and
`previewMarkdownToMdxConversion`
- Asset-reference, byte-stream, document-graph, Markdown, and MDX
helpers included in commit `76bc388ba9`
- Tests: the corresponding `packages/content-engine/src/**/*.test.ts`
files already on this branch

### Port with their existing tests

| Area | Functions to move or adapt | Tests to move with them |
| --- | --- | --- |
| Source contract and integrity | `parseContentBlockSourceProp`,
`getContentBlockSource`, `isEqualContentBlockSource`,
`formatContentBlockSourceIntegrityIssue`,
`getContentBlockSourceIntegrityIssues`,
`allocateUniqueContentBlockTemplateName` |
`packages/sdk/src/content-block.test.ts`,
`packages/sdk/src/schema/content-block.test.ts` |
| Existing instance/name utilities | `findChildReferenceIndex`,
`findParentInstanceReference`, `getInstanceName` |
`packages/sdk/src/instances-utils.test.tsx` |
| Template naming in mutations |
`assignUniqueBlockTemplateNamesMutable`,
`findBlockTemplateNameCollision`, `getSourceBackedBlockTemplateContext`,
`getBlockTemplateNameConfirmation`, `getBlockTemplateInsertionIndex` |
`packages/project-build/src/runtime/block.test.ts` |
| Content-mode property permissions | `getContentModePropEligibility`;
preserve its distinction between editable, design-only, incompatible,
and unknown props |
`packages/project-build/src/runtime/content-mode-permissions.test.ts` |
| Template resolution | `resolveMdxTemplates` |
`packages/project-build/src/runtime/mdx-template-resolution.test.ts` |
| Template materialization | `createMdxScopeIdGenerator`,
`materializeMdxTemplates` |
`packages/project-build/src/runtime/mdx-materialization.test.ts` |
| MDX → instances and instances → MDX |
`isMaterializedMdxAuthoredContentRoot`, `preserveMdxAuthoredContentIds`,
`materializeMdxAuthoredContent`, `reconcileMdxAuthoredContent`,
`omitTransientEmptyParagraphs`, `serializeMdxTemplateInsertion` |
`packages/project-build/src/runtime/mdx-authored-content.test.ts`;
retain the pure serialization cases from `mdx-storage-adapter.test.ts` |
| Revisioned Asset I/O | `createAssetContentRevision`,
`readAssetContentBytes`, the package-owned
`createHttpAssetContentRepository`, the thin Builder
`createBuilderHttpAssetContentRepository`, `createAssetContentBridge`,
`initAssetContentBridge`, `getAssetContentBridge`; keep one repository
implementation rather than copying the old Builder duplicate |
`packages/asset-uploader/src/content-repository.test.ts`,
`apps/builder/app/builder/shared/assets/mdx-content-repository.test.ts`,
`apps/builder/app/shared/asset-content-bridge.client.test.ts` |
| Source lifecycle planning | `createContentBlockBodyRemoval`,
`prepareContentBlockConnect`, `prepareContentBlockDisconnect`,
`prepareContentBlockSwitch`, `ContentBlockSourceRevisionConflictError` |
`packages/project-build/src/runtime/content-block-source-lifecycle.test.ts`
|
| Per-Asset revision/session behavior | `getContentBlockSessionSource`,
`isContentBlockSessionSourceCommitted`; reuse the loading,
authorization, revision, queue, conflict, retry, cancellation, and
source-replacement internals of `createMdxAssetEditingSession`, but key
the rebuilt session by Asset rather than Content Block/render root |
Port the applicable cases from
`packages/project-build/src/runtime/mdx-asset-session.test.ts`; replace
root-projection setup with in-memory snapshot setup |
| Template migrations | `validateMdxTemplateMigrationConfirmation`,
`planMdxTemplateMigration` |
`packages/project-build/src/runtime/mdx-template-migration.test.ts` |
| Publication | `createPublishedMdxMaterializationCache`,
`materializePublishedMdx`, `getPublishedMdxContentDatabaseMaxBytes`,
`getDynamicPublishedMdxSourceBlockIds`, `hasDynamicPublishedMdxSources`,
`createPublishedBuildContentCompilationPlan`,
`resolvePublishedMdxDependencyClosure`,
`resolvePublishedMdxAssetCandidates` |
`packages/project-build/src/runtime/published-mdx.test.ts`,
`packages/project-build/src/content-database.test.ts` |
| Build/import integrity | `assertContentBlockSourceIntegrity` and the
Content Block cases in
`getBuildIntegrityIssues`/`formatBuildIntegrityIssue` |
`packages/project-build/src/build-integrity.test.ts` |
| MDX paste/MCP insertion | `insertMdxText` and its input/result
schemas; retain parsing, confirmation, template-resolution, and
insertion-index logic, then invoke the normal mutation path |
`packages/project-build/src/runtime/mdx-paste.test.ts` |
| Builder source control | `createContentBlockSourceController`,
`createBuilderContentBlockSourceController` and the existing
source-control components | `content-block-source-controller.test.ts`,
`content-block-source.test.tsx` |
| Diagnostics | `formatContentBlockDiagnostic`,
`deduplicateContentBlockDiagnostics`, `takeNewContentBlockDiagnostics` |
Port only their focused cases from
`apps/builder/app/shared/content-block-content.test.ts` |

### Explicitly do not port

- `projectContentStorageChanges`, `createContentStorageProjection`,
`createContentStorageSelectorProjection`,
`executeContentStorageMutation`, `executeContentStorageTextReplacement`,
`executeContentStoragePropMutation`, and
`executeContentStorageStructuralMutation`
- `applyMdxContentStorageChanges` and `prepareMdxContentStorageWrites`;
replace them with one post-mutation snapshot serializer built from the
reusable pure `reconcileMdxAuthoredContent` converter
- `createContentBlockApplicationOperations` and the persistence-plan
wrappers that route individual editing commands
- Builder `$runtime*` projected stores, materialized mutation functions,
alternate selector/tree helpers, pending projected-change state, and
MDX-specific mutation sequences from `content-block-content.ts`
- Canvas loading/saving/open-file presentation state, MDX history/undo,
and atomic rollback machinery

## Implementation checklist

### 1. Preserve the `main` editing architecture

- [x] Keep normal synchronous insert, text, Enter, slash menu, property,
delete, move, wrap, copy/paste, selection, and focus mutations unchanged
- [x] Materialize connected content through the same runtime instance
interfaces used by ordinary Content Blocks
- [x] Keep connected authored children in memory and exclude them from
persisted build data
- [x] Route persistence only after a synchronous mutation has updated
the canvas
- [x] Never wait for Asset loading, conversion, or saving before
rendering an interaction
- [x] Add no MDX-specific implementation to individual editing commands

### 2. Shared Markdown and MDX conversion

- [x] Parse Markdown and the Markdown portion of MDX through one shared
AST and plugin configuration
- [x] Implement one shared MDX-to-instances converter and one shared
instances-to-MDX converter
- [x] Serialize headings, paragraphs, links, images, lists, tables, task
lists, blockquotes, thematic breaks, code, emphasis, strong,
strikethrough, and other supported GFM structures as Markdown
- [x] Serialize only non-lossless/custom structures as Webstudio JSX
- [x] Preserve nested Markdown in MDX elements, frontmatter, comments,
tight/loose list semantics, code whitespace, reference links, and Asset
references
- [x] Omit transient empty draft paragraphs instead of emitting
`<ws.element ws:tag="p" />`
- [x] Preserve unsupported source text and authored metadata whenever it
cannot be converted safely
- [x] Add deterministic round-trip and canonical-output coverage for
every supported Markdown structure

### 3. Invalid and unsafe MDX

- [x] Preserve file source exactly; never silently repair or rewrite
invalid source
- [x] Ignore imports, exports, expressions, executable JavaScript,
invalid properties, and other unsupported nodes when they can be
isolated
- [x] Continue rendering valid siblings and report every skipped subtree
once with filename, line, and column
- [x] Use the same recovery pipeline in Builder and publication so both
render the same authored result for the current file revision
- [x] When structural syntax prevents recovery, render no authored file
content in Builder or publication instead of retaining a stale
materialization
- [x] Skip an unrecoverable Content Block on published sites and
continue the build with a warning
- [x] Keep invalid and unsafe files editable and reconnectable
- [x] Enforce UTF-8, byte-size, depth, node-count, property-count, and
generated-output limits with source-located diagnostics where possible

### 4. File formats and Asset creation

- [x] Keep `.md` parsing and raw-HTML behavior unchanged
- [x] Support creating, opening, editing, deleting, and recreating
`.mdx` files through the normal text-file UI
- [x] Open `.mdx` in the same editor experience as `.md`
- [x] Always create an independent empty MDX Asset even when identical
empty files exist
- [x] Allow renaming `.md` to `.mdx` without converting or rewriting its
contents; after rename, handle invalid contents through the normal MDX
diagnostics and recovery pipeline
- [x] Update an already-open MDX editor immediately after canvas
persistence
- [x] Bound read/write buffering and verify the returned Asset identity
and revision

### 5. Source contracts and lifecycle

- [x] Support direct Asset bindings and dynamic expression bindings
- [x] Keep source mutation controls available only in Design mode; show
the connected filename and **Open** action read-only in Content mode
- [x] Reject duplicate source props, unsupported prop types, missing
Assets, non-MDX Assets, and unauthorized Assets
- [x] Analyze selected file diagnostics before confirmation without
modifying the block or file; MDX syntax diagnostics do not block
connection
- [x] For a non-empty block, show **Abort** and **Connect** and explain
that ordinary children will be replaced
- [x] On connect, remove every non-template child, preserve Templates,
set `src`, and materialize file content
- [x] Replace disconnect handoff with normal `src` reset; discard
derived content and leave the block empty
- [x] Remove disconnect confirmation and conversion; resetting a
repeated binding leaves every occurrence empty
- [x] Switching sources validates/loads the target before replacing the
usable binding and leaves both files unchanged
- [x] Revalidate Asset revision and project version when confirmation is
submitted
- [x] Make repeated connect/disconnect operations idempotent
- [x] Prevent disposed, stale, or out-of-order loads from replacing the
newest source
- [x] Keep loading, saving, conflict, empty-file, and open-file actions
off canvas

### 6. Asynchronous persistence and conflicts

- [x] Snapshot the current in-memory authored root after normal
mutations
- [x] Serialize/coalesce rapid edits per resolved Asset and always
prepare against the latest local snapshot
- [x] Allow independent Assets to save concurrently without scope
leakage
- [x] Use one revisioned document session and one ordered save queue per
Asset, even when multiple Content Blocks or render scopes reference it
- [x] After a shared Asset changes, rematerialize every connected
occurrence without persisting their generated instances to project data
- [x] Pin the resolved Asset and exact loaded revision for each write
- [x] Recheck authorization immediately before storage
- [x] Ensure local queued edits never produce stale-revision conflicts
with each other
- [x] On a genuine external revision conflict, preserve local content,
stop writes, and enter the standard reload-required state
- [x] Keep failed non-conflicting writes retryable without discarding
local content
- [x] Finish already accepted persistence writes after a Content Block
unmounts or switches source
- [x] Cancel obsolete loads, materializations, and UI updates after
unmount, reload, or source switch
- [x] Dispose project-scoped roots, queues, parse caches, failure state,
generations, and session subscriptions when the Asset session is
replaced
- [x] Invalidate Asset resources and synchronize open file editors after
successful writes
- [x] Keep multi-root persistence ordered, serial, and non-atomic;
report partial completion without rollback
- [x] Do not add Asset-history undo/redo; native text-editor undo
remains local to the open editor

### 7. Content mode parity

- [x] Empty connected blocks remain selectable and expose the normal
Content-mode `+` affordance
- [x] First insertion renders immediately
- [x] Enter creates and focuses the next paragraph immediately
- [x] Slash commands work in newly created empty paragraphs
- [x] Deleting one selected instance deletes exactly that instance
- [x] Mouse and keyboard moves use the same operation and permissions
- [x] Preserve sibling reorder, nested reparenting, and moves between
editable Content Blocks where `main` permits them
- [x] Preserve content-editable props such as Link `href`
- [x] Preserve copy, paste, cut, multi-cut, wrapping, unwrapping, grid
insertion, selection, Navigator order, and canvas order
- [x] Cover mutation routing centrally, retain the full unconnected
Builder suite, and exercise representative connected interactions in
real Builder E2E

### 8. Template names and permissions

- [x] Resolve only exact user-facing names from the direct flat
Templates list
- [x] Enforce unique names within each Templates list
- [x] Reject duplicate names during rename with inline validation
- [x] Allocate unique names during insert, paste, duplicate, move, wrap,
and unwrap
- [x] Apply valid template properties using the same permissions as
Content mode
- [x] Preserve and warn about ignored design-only, invalid, or stale
properties
- [x] Preserve template shell provenance without serializing expanded
template internals
- [x] Generate deterministic materialized IDs from source identity,
render scope, and authored usage path so focus/selection survives saves
- [x] Warn before renaming or deleting referenced templates; files
remain unchanged
- [x] Provide shared AST-based rename/remove migration operations for
MCP-assisted multi-file cleanup

### 9. Scope, frontmatter, and diagnostics

- [x] Scope materialized content by Content Block ID plus exact
Collection-item/render context
- [x] Prevent repeated instances and dynamic candidates from leaking
generated IDs, template resolution, or diagnostics across scopes while
sharing the underlying Asset source and revision where applicable
- [x] Remove frontmatter editing from Settings; edit the exact YAML
through the MDX Asset file editor
- [x] Preserve the exact frontmatter source byte-for-byte during canvas
body saves
- [x] Route canvas body changes and complete-file editor saves through
one revisioned per-Asset coordinator
- [x] Show a source warning indicator in Settings and precise
source-located diagnostics in the Asset editor
- [x] Deduplicate repeated diagnostics without losing distinct locations
- [x] Keep unresolved-template placeholders selectable and accessible
only in Builder

### 10. Asset references, transfer, and integrity

- [x] Discover and rewrite Asset references in frontmatter, Markdown
links/images, and MDX properties
- [x] Preserve Asset references while editing unrelated content
- [x] Include referenced Asset and template revisions in
dependency/cache identity
- [x] Route referenced Assets through published URL rewriting
- [x] Validate Content Block source integrity during project import,
fragment/page transfer, copy/paste, build integrity, and pre-publish
audit
- [x] Allow multiple Content Blocks and dynamic render scopes to
reference the same MDX Asset without treating the connection as an
integrity error
- [x] Preserve Templates when copying a connected Content Block
- [x] Preserve direct and dynamic Content Block source bindings when
copying or pasting within the same project
- [x] When copying only one rendered Collection item, resolve that
item’s current MDX Asset and replace the copied Content Block’s dynamic
source with a direct binding to that same Asset
- [x] When copying an entire Collection, preserve the Collection, its
instances, and its dynamic source bindings
- [x] Preserve the complete MDX source and frontmatter by reference;
copying a connected block must not rewrite the shared file
- [x] During cross-project transfer, copy every discoverable distinct
bound MDX Asset and rewrite transferred bindings to the destination
Assets
- [x] If some cross-project dynamic candidates cannot be discovered or
copied, continue with successful copies, preserve what can be
represented safely, and warn about skipped candidates
- [x] Revalidate source revisions and the destination project before
applying a cross-project transfer
- [x] If cross-project Asset creation or project insertion fails, leave
the source project unchanged and clean up any newly reserved destination
Asset
- [x] Use the same transfer operation through Builder and MCP

### 11. Dynamic sources and publication

- [x] Resolve dynamic bindings once per render/save scope
- [x] Require finite, discoverable candidate sets for publication and
load no unrelated MDX
- [x] Warn and skip unbounded, missing, unreadable, malformed, or unsafe
sources without failing the complete build
- [x] Materialize only rendered Content Blocks; ignore Templates
definitions and `src` on non-Content-Block instances
- [x] Skip unresolved template subtrees while keeping valid siblings
- [x] Include selected template styles, fonts, resources, unresolved
names, and referenced Asset revisions in generated dependency output
- [x] Keep draft-only MDX Assets out of publication retention

### 12. Builder, MCP, CLI, and shared operations

- [x] Use the same converters, source lifecycle, revision checks,
persistence service, diagnostics, and template migrations in Builder and
MCP
- [x] Keep frontmatter editing file-based; retain whole-frontmatter
runtime operations only where compatibility requires them
- [x] Avoid Builder-only or MCP-only mutation/conversion implementations
- [x] Regenerate runtime/API contracts from their source definitions
- [x] Update CLI Content Engine reference and generated MCP
documentation

### 13. Verification and delivery

- [x] Add regression tests that reproduce failures from the previous
implementation, including double deletion and blocked rendering
- [x] Cover two Content Blocks sharing one Asset: edits from either
block serialize once, update both canvases, preserve scope-specific
Templates, and cannot overwrite a newer local snapshot
- [x] Verify new tests fail against the broken implementation and pass
with the corrected behavior
- [x] Run SDK, Content Engine, Asset Uploader, Project Build, Builder,
React SDK, CLI, Protocol, and HTTP Client affected suites and typechecks
- [x] Run real Builder E2E for source lifecycle, live file refresh,
empty insertion, Enter/slash focus, Link props, delete, keyboard
reorder, switching, disconnect, conflicts, unresolved templates, dynamic
sources, and repeated scopes
- [x] Regenerate shared project fixture bundles and verify a second
generation leaves the tree clean
- [x] Run lint, package boundaries, `pnpm oxfmt --check`, and `git diff
--check`
- [x] Review and update authoritative `docs/` content and navigation
- [ ] Run the complete agent evaluation suite — not run because it
requires separate explicit approval

### 14. Content-mode parity and disconnect handoff

- [x] Allow keyboard reordering in Content mode through the normal move
command while keeping the editable Content Block boundary
- [x] Let already accepted Asset writes drain independently before
resetting the source; disconnect does not load, flush, or convert the
file
- [x] Remove MDX-to-instance copying from disconnect
- [x] Reset `src`, remove the derived tree, and leave the ordinary
Content Block empty
- [x] Preserve automatic text-editor focus after inserting editable
content from the template menu
- [x] Remove Builder frontmatter-save UI and report complete-file save
failures through the Asset editor
- [x] Add regression coverage for keyboard movement, disconnect
teardown, source switching, insertion focus, and paragraph deletion
focus restoration
- [x] Run the complete six-scenario MDX Content Block Builder E2E suite

### 15. Final architecture review fixes

- [x] Keep one canonical ordered pending document per Asset so shared
Content Block occurrences cannot conflict with one another
- [x] Merge ordered local edits from different occurrences while
preserving text changes, insertions, deletions, and sibling reordering;
keep external revisions on the strict reload-required path
- [x] Centralize typed render-scope parsing, repeated-occurrence
detection, scoped variable lookup, and dynamic Asset resolution across
Canvas, Settings, source lifecycle, and copy/paste
- [x] Capture the owning Asset session when a save is accepted so normal
unmount/source-switch teardown cannot redirect or lose it
- [x] Dispose project-owned roots, queues, caches, generations, failure
state, and subscriptions when replacing the project Asset session
- [x] Add regression coverage for same-Asset edits from two occurrences,
same-node last-write ordering, structural reorder, insertion
preservation, scoped source resolution, and project cleanup
- [x] Re-run affected full unit suites, typechecks, production build,
static checks, and all six MDX Content Block Builder E2E scenarios


### 16. Final persistence and transfer hardening

- [x] Drain every accepted per-Asset edit before project-session
teardown while retaining the owning authorization context
- [x] Drain canvas serialization queues and revisioned Asset writes
before pre-publish audit and publication
- [x] Block publication on a queued serialization failure or Asset
revision conflict
- [x] Copy actual Asset bytes during cross-project paste, preserve MDX
reference names, rewrite direct and Collection-backed Asset IDs, and
clean up newly created Assets when insertion fails
- [x] Include every resolved dynamic MDX source when copying an entire
Collection and warn while preserving successful copies when individual
Assets cannot be transferred
- [x] Keep MDX editor save failures visible with retry and route
conflicts through the standard reload-required flow
- [x] Give keyboard nested reparenting the same editable Content Block
boundary used by pointer movement
- [x] Add regression coverage and rerun the complete Content Engine and
Builder unit suites plus type, lint, formatting, package-boundary, and
diff checks

## Current rebuild status

- The connected Content Block now owns one complete MDX document:
frontmatter feeds designed-shell bindings and the explicit Body outlet
owns the authored MDX body.
- Builder and published output resolve the same frontmatter document
graph, including nested document references and Asset references.
- Direct frontmatter text/property edits use the existing ordered
per-Asset session; referenced-document values remain file-owned.
- Repeated Collection occurrences keep isolated runtime shell/Body
identities and occurrence-specific document values.
- The local blog article layout has been upgraded to the document/Body
model and its bindings validate cleanly.
- Full affected package suites and typechecks pass; every new regression
was demonstrated fail/pass.
- `pnpm lint`, package boundaries, `pnpm oxfmt --check`, generated
API/docs checks, fixture builds, and `git diff --check` pass.
- Full fixture synchronization is blocked by the development API
rejecting this local CLI version before fixture generation; existing
fixture bundles build successfully.
- Complete agent evaluations remain pending explicit approval.

### 17. Treat Content Block source as a property

- [x] Declare Content Block `src` in component metadata as a Design-mode
initial property labeled **Source**
- [x] Add the short property description: “Create an .mdx file in
Assets, then select or bind it here.”
- [x] Accept only a direct Asset or an expression resolving to an Asset
ID; reject URL/string sources, missing Assets, and non-MDX Assets
- [x] Extract and reuse the Image-style Asset picker and binding
presentation without duplicating source-control UI
- [x] Render Source through the normal properties section and remove the
separate source-section placement and special `src` filtering
- [x] Route Source changes through Content Block connect, switch, and
disconnect lifecycle operations instead of generic prop updates
- [x] Keep connect confirmation, Open, diagnostics, and persistence
state attached to Source; remove frontmatter and disconnect UI
- [x] Add regression coverage for direct Assets, dynamic Asset bindings,
invalid resolved values, lifecycle operations, and Design/Content mode
visibility
- [x] Re-review authoritative `docs/` content for the Source-property UI
change and update it if required

### 18. Source property UX

- [x] Show one full-width bindable Connect .mdx file button while
disconnected
- [x] Show equal-width filename and Open buttons while connected
- [x] Switch files from the filename button through the source lifecycle
- [x] Override PropertyLabel reset and Alt-click with the disconnect
lifecycle
- [x] Preserve confirmation, pending, failure, repeated-scope, and
dynamic-source behavior
- [x] Add regression coverage for connect, switch, binding, reset, and
disconnect
- [x] Update Content Block documentation
- [x] Run affected tests, typecheck, lint, formatting, package
boundaries, and diff checks


### 19. Reduced file-owned frontmatter follow-up

This checklist supersedes the earlier structured-frontmatter and
disconnect-copy behavior. Frontmatter remains file-owned and is edited
only in the MDX Asset editor.

#### A. Remove structured frontmatter UI

- [x] Remove the JSON frontmatter editor and unused Builder
controller/state
- [x] Keep source mutation Design-mode-only; show connected Source
read-only in Content mode and allow authorized editors to open the
complete file
- [x] Retain runtime frontmatter APIs only where compatibility requires
them

#### B. Preserve source and separate validation

- [x] Preserve the exact frontmatter envelope during every canvas body
save without parsing/stringifying YAML
- [x] Preserve comments, ordering, quoting, whitespace, BOM, newline
style, delimiters, and invalid but closed YAML
- [x] Keep the existing unsupported-node/property MDX recovery; do not
add an ad hoc partial parser
- [x] Parse body and frontmatter independently so either valid half
remains usable
- [x] Keep bounded Asset-query frontmatter extraction unchanged
- [x] Treat structurally malformed MDX as having no safe body tree

#### C. Share Asset state with the raw editor

- [x] Route canvas and complete-file changes through one ordered,
non-blocking per-Asset coordinator
- [x] Update canvas and clean raw editors immediately from shared
in-memory source
- [x] Reject stale dirty-editor saves, preserve unsaved text, and
require reload; never force-overwrite
- [x] Drain accepted writes after source replacement/disconnect and
report background failures globally
- [x] Dispose sessions only after they have no consumers and no queued
work

#### D. Connect, replace, and disconnect

- [x] Load and diagnose before connect; block only
unreadable/unauthorized/non-MDX Assets
- [x] Show **Abort / Connect** for non-empty blocks, including
diagnostics and the recoverable-content warning
- [x] Set `src` and delete persisted children through one runtime
mutation
- [x] Switch to a loaded source without waiting for the previous Asset's
network save
- [x] Remove disconnect confirmation and conversion
- [x] Reset `src` immediately, discard the derived tree, leave the block
empty, and preserve the Asset
- [x] Resetting a bound source leaves every repeated occurrence empty

#### E. Dynamic and shared sources

- [x] Keep one source/revision state per Asset and a separate derived
instances tree per occurrence
- [x] Resolve Templates independently per Content Block and preserve
Builder-only missing-template placeholders
- [x] Resolve bound `src` from the selected Collection occurrence
- [x] Make **Open** use the occurrence's resolved Asset; update the
filename and disable Open when unresolved
- [x] Add direct/bound tests including Collection item A opening `a.mdx`
and item B opening `b.mdx`

#### F. Diagnostics, MCP, and publication

- [x] Show an accessible warning icon beside Source with the first
diagnostic, line/column, and remaining count
- [x] Show precise YAML/MDX diagnostics in the Asset editor without
repetitive canvas/toast UI
- [x] Reuse parser/diagnostic/revision contracts in Builder, build,
publication, and MCP
- [x] Keep MCP frontmatter editing file-based and return structured
diagnostics for complete-source writes
- [x] Emit build warnings; published sites skip invalid/unsupported
content without diagnostic UI

#### G. Verification and documentation

- [x] Add fail/pass regressions for source preservation, independent
validation, stale raw saves, lifecycle, dynamic Open, shared Assets, and
cleanup
- [x] Run affected tests, typechecks, lint, formatting, package
boundaries, fixtures, and diff checks
- [x] Open Markdown and MDX editors at twice the default dialog size
with the right-side preview visible initially
- [x] Document canvas body editing, file-owned frontmatter, Content-mode
Asset access, and cloning an MDX Asset for a new post
- [x] Perform a final correctness, data-loss, DRY, simplification,
accessibility, and performance review
- [x] Keep this checklist synchronized with implementation status

### 20. Content-mode component properties

- [x] Show a connected Content Block Source read-only in Content mode
with an active **Open** action
- [x] Keep source switching, binding, reset, and disconnect available
only in Design mode
- [x] Expose HTML Embed code and preserve Code Text's code-editor
metadata on authored Text Content
- [x] Audit every registered component and expose authored payload while
keeping design, structure, and behavior controls in Design mode
- [x] Keep media dimensions and Code Text theme Design-only while
preserving adapter-owned intrinsic Image metadata during MDX round trips
- [x] Add exact component-permission and regression coverage
- [x] Update authoritative component documentation
- [x] Run affected full suites, typechecks, formatting, lint, package
boundaries, and generated contract/documentation checks


### 21. Designed document shell and Body outlet

- [x] Add one structural **Content Block Body** outlet and keep legacy
Content Blocks without it compatible
- [x] Materialize and persist only `document.body` children through the
Body outlet while preserving the surrounding designed shell and
Templates
- [x] Expose the connected document frontmatter through a Content Block
`document` parameter for shell text and property bindings
- [x] Write direct frontmatter-bound text and properties back through
the same ordered per-Asset session without blocking canvas mutations
- [x] Resolve document `$ref` dependencies and Asset references
consistently in Builder and published output
- [x] Keep referenced-document values read-only on canvas and direct
editors to the referenced file
- [x] Isolate repeated Collection occurrences while retaining
occurrence-specific source, frontmatter, shell, and Body content
- [x] Render the same designed shell, frontmatter values, and Body
substitution in generated/published output
- [x] Upgrade the local blog article layout to bind title, excerpt,
dates, author data, and images from frontmatter and render MDX through
Body
- [x] Add fail/pass regressions for nested Body discovery, lifecycle,
permissions, repeated values, document references, and generated shell
rendering
- [x] Update authoritative Content Block documentation
- [x] Run affected tests and typechecks, formatting, lint, package
boundaries, generated API/docs checks, and fixture builds


### 22. Final nested references and frontmatter editing

- [x] Keep direct frontmatter-bound text and property controls editable
in Content mode without exposing or replacing their bindings
- [x] Keep referenced-document values read-only and preserve legacy
instance-property name normalization
- [x] Render direct frontmatter text bindings through the plain-text
editing path
- [x] Resolve nested legacy and structured Asset references from
referenced Markdown/MDX frontmatter
- [x] Preserve missing Asset references, emit structured
`ASSET_NOT_FOUND` diagnostics, and keep them out of the document graph
- [x] Add fail/pass regressions for the real Settings control and
root/nested missing Asset-reference cases
- [x] Fix exact Content Block selection after the Body node appears in
Navigator
- [x] Run full affected suites/typechecks, all six MDX Content Block E2E
scenarios, deterministic fixture builds, generated checks, lint,
formatting, package boundaries, and diff checks

### Before merge

- [x] Resolve #6261: Nested Asset references in referenced Markdown
frontmatter work consistently in Assets queries, previews, publication,
and static export


### 23. Explicit writable expression bindings

- [x] Add an explicit `readwrite` mode to expression bindings; existing
expressions remain read-only by default
- [x] Accept `readwrite` only for direct static member paths and
centralize their parsing and validation
- [x] Require the expression root to provide a writable data source;
initially support the connected Content Block document frontmatter
- [x] Update Content Block permissions, canvas text editing, and
property controls to require a valid writable binding
- [x] Persist direct frontmatter edits through the existing ordered
per-Asset session
- [x] Create new direct frontmatter bindings with `readwrite` mode and
migrate existing compatible bindings
- [x] Preserve writable mode through schemas, serialization, loading,
copy/paste, transfer, and export
- [x] Add fail/pass regressions for valid paths, derived expressions,
unsupported sources, permissions, persistence, and compatibility
- [x] Review authoritative `docs/` content and update it if the
persisted binding contract affects user-facing documentation
- [x] Reject unsafe frontmatter paths and reject `readwrite` bindings
outside a connected Content Block document
- [x] Preserve expression binding mode through the public HTTP client
and document the MCP authoring contract
- [x] Limit frontmatter value write-back to Content mode so Design mode
retains normal binding replacement/reset behavior
- [x] Keep bindings inside Templates and across nested Content Block
ownership boundaries read-only
- [x] Upgrade compatible legacy bindings even when the Content Block is
disconnected during migration
- [x] Run affected tests, typechecks, formatting, lint, package
boundaries, fixture checks, and `git diff --check`

Verification: full SDK (567), Project Build (2,277), and Builder (2,695)
suites pass, along with affected Expression, Migration, Template, HTTP
Client, and MCP suites; typechecks, lint, formatting, package
boundaries, and diff checks pass. Generated contracts are refreshed.
Fixture synchronization remains blocked before generation because the
development API rejects this local CLI version as incompatible. The
checked-in bundles pass the current Protocol suite and every fixture
build with the current bundle version.
2026-08-29 20:15:20 +02:00
Oleg Isonen 7e7c14868a fix: Preserve read-only guides and publish targets (#6266)
## Summary

- Route explicit read-only `meta.guide` requests to focused discovery
tools and exclude mutations, side-effecting session tools, broad design
context, and visual verification.
- Require explicit task scope, specialized workflow, authored-fragment
routing, and design-system reuse instead of inferring behavior from
brief prose.
- Persist the requested staging or production target in new SaaS
deployment records.
- Return the same target from publish list and job status, with
backward-compatible inference for historical records.

## Todo

- [x] Reproduce the read-only custom-code audit misrouting from #6262.
- [x] Add failing regressions and implement strict read-only routing.
- [x] Remove behavior-affecting prose inference from CLI/MCP guide
routing.
- [x] Reproduce the single-domain production target mismatch from #6263.
- [x] Add create-to-list/status regressions and persist the explicit
target.
- [x] Cover historical deployment records that predate the stored
target.
- [x] Verify production create → list → get-job through the staging
deployment.
- [x] Regenerate and verify project fixtures against the changed bundle
contract.
- [x] Review documentation impact and update the generated MCP manual.
- [x] Review the complete diff for correctness, compatibility,
duplication, and unnecessary complexity.

## Verification

- Real staging publish for #6263 — passed; create, list, and get-job
consistently reported `production` for a one-domain publish. The
supplied project has no custom domains, so the custom-domain variant was
not available. The job remained `pending` during this check.
- Real local CLI/MCP verification for #6262 — passed; no mutation or
side-effecting tools leaked.
- `pnpm --filter @webstudio-is/protocol test -- src/fixtures.test.ts` —
17 passed
- `pnpm -r fixtures:build` — passed for all eight fixtures
- `pnpm --filter @webstudio-is/project-build test` — 2,134 passed
- `pnpm --filter webstudio test` — 1,037 passed; the one payload-size
regression was fixed and its targeted test passed
- `pnpm --filter @webstudio-is/domain test` — 24 passed
- `pnpm --filter @webstudio-is/sdk test` — 519 passed
- Builder API router test — 30 passed
- Relevant package typechecks — passed
- `pnpm lint`
- `pnpm check:package-boundaries`
- `pnpm check:generated-api`
- `pnpm check:generated-docs`
- Regression tests were demonstrated failing against the previous
behavior and passing with the fixes.
- Agent evaluations were not run because they require separate explicit
approval.

Closes #6262
Closes #6263
2026-08-29 18:54:05 +02:00
Oleg Isonen cbe4950cd0 fix: Use available package manager for previews (#6251)
## Summary

- Use the npm or pnpm launcher that started Webstudio for preview
dependency installation, builds, and server startup.
- Use pnpm-compatible isolated-install flags and report the selected
package-manager path on failure.
- Preserve the existing installed-dependency reuse path before
package-manager resolution.

## Technical choices

- Read the standard `npm_execpath` metadata already supplied by npm and
pnpm; users do not need to configure another environment variable.
- Run JavaScript launchers such as `pnpm.cjs` with the selected Node
runtime, supporting bundled Node distributions without adjacent npm.
- Keep the existing npx-to-npm mapping and fallback behavior unchanged.
- Distinguish numeric package-manager exit codes from process-launch
errors such as `ENOENT`.

## Todo

- [x] Reproduce the bundled Node + pnpm + missing adjacent npm failure.
- [x] Add fail-before/pass-after coverage for dependency installation,
production build, and preview startup.
- [x] Reuse the detected pnpm launcher.
- [x] Preserve installed-dependency reuse.
- [x] Include the selected launcher path in installation failures.
- [x] Report package-manager exits and process-launch errors accurately.
- [x] Review documentation impact and update the CLI manual plus
generated MCP documentation.
- [x] Review fixture impact; no fixture inputs or outputs are affected.

## Verification

- Focused install diagnostic regression suite — 28 tests passed.
- `pnpm --filter webstudio typecheck`
- `pnpm lint`
- `pnpm --filter webstudio test` — 1,029 of 1,030 tests passed; an
existing process-supervisor cleanup test hit a transient `ENOENT`, then
passed 4/4 when rerun in isolation.
- Generated documentation checks passed.
- Real pnpm JavaScript launcher invocation with isolated preview install
flags.
- Agent evaluations were not run because they require separate approval.

Closes #6250
2026-08-28 00:23:01 +02:00
Oleg Isonen 981f45fd0d docs: Optimize documentation image sizes (#6253)
## Summary

Replace ten oversized PNG screenshots in the product documentation with
high-quality WebP assets and update their references.

The affected assets shrink from 27,813,051 bytes to 2,822,010 bytes (90%
smaller) while retaining their original pixel dimensions.

## Technical choices

- Encoded screenshots as WebP at quality 88 with sharp YUV conversion.
- Kept original dimensions to preserve UI text legibility and zoom
detail.
- Left animated GIFs unchanged: GIF-native optimization, palette
reduction, and resizing tests increased their file sizes, so changing
them would add cost or reduce clarity.
- Kept documentation content, navigation, and public page paths
unchanged.

## Checklist

1. [x] Audit raster assets in `docs/.gitbook/assets`.
2. [x] Optimize referenced PNG assets above 1 MB where conversion
materially reduces size.
3. [x] Update every affected documentation reference.
4. [x] Verify replacement dimensions and image decoding.
5. [x] Verify visual quality (SSIM 0.961–0.997 plus manual spot checks).
6. [x] Verify new references resolve and removed filenames are no longer
referenced.
7. [x] Review documentation impact; this PR changes documentation assets
and their direct references only.
8. [x] Verify no fixture inputs or generated fixture outputs are
affected.

## Verification

- `git diff --check`
- Reference checks for all ten added and removed filenames
- Pixel-dimension comparison for all ten replacements
- FFmpeg SSIM comparison for all ten replacements
- Manual visual review of representative full-resolution screenshots

The repository formatter excludes these documentation files, so it
reported no matching targets. Agent evaluations were not run because
this is a documentation-asset-only change.

Closes #6252
2026-08-27 17:39:10 +02:00
Oleg Isonen 471f504087 feat: Select unused assets before deleting (#6245)
Closes #6243

## Summary

- Add checked-by-default controls for every asset in the **Delete unused
assets** dialog.
- Add a footer **Select all** control that reflects the complete
selection and toggles all assets.
- Delete only selected assets that remain unused and disable deletion
when the selection is empty.
- Keep the Assets documentation aligned with the selective deletion
workflow.

## Implementation

The dialog owns a set of selected asset IDs initialized from the
unused-assets result. Individual checkboxes update that set, while
**Select all** replaces it with either every currently unused ID or an
empty set.

Asset usage inputs remain subscribed while the dialog is open. The
deletion payload is intersected with the current unused-assets list, and
this cleanup path does not force deletion. If an asset becomes
referenced before the mutation executes, the runtime rejects its
deletion instead of removing a now-used asset.

## Todo

- [x] Add per-asset selection controls.
- [x] Add and position the **Select all** footer control.
- [x] Limit deletion to selected asset IDs that remain unused.
- [x] Protect against assets becoming referenced while the dialog is
open.
- [x] Add regression tests for individual selection, select-all
behavior, empty-selection protection, selective deletion, and live usage
changes.
- [x] Review documentation impact and update the Assets guides.
- [x] Verify formatting, lint, types, and Asset Manager/assets tests.

## Verification

- `pnpm --filter @webstudio-is/builder exec vitest run
app/builder/shared/asset-manager app/builder/shared/assets` — 26 files,
221 tests passed.
- `pnpm --filter @webstudio-is/builder typecheck` — passed.
- `pnpm exec oxlint --deny-warnings
apps/builder/app/builder/shared/assets/delete-assets.ts
apps/builder/app/builder/shared/asset-manager/delete-unused-assets.tsx
apps/builder/app/builder/shared/asset-manager/delete-unused-assets.test.tsx`
— passed.
- `pnpm exec oxfmt --check
apps/builder/app/builder/shared/assets/delete-assets.ts
apps/builder/app/builder/shared/asset-manager/delete-unused-assets.tsx
apps/builder/app/builder/shared/asset-manager/delete-unused-assets.test.tsx`
— passed.
- Both regression tests were confirmed failing before their respective
implementations and passing afterward.

## Compatibility and follow-ups

No data migrations, public API changes, fixture changes, or known
follow-ups.
2026-08-26 12:14:06 +02:00
Oleg Isonen db2175f333 fix: transfer pasted assets across deployments (#6228)
Closes #6227

## What changed

Cross-deployment copy/paste now transfers referenced assets into the
destination project before inserting instances, pages, templates, or
folders.

- Add optional source deployment metadata to the existing v0.1 clipboard
formats.
- Keep old v0.1 payloads readable and keep new payloads readable by
older builders, which ignore the additive field.
- Reuse the existing asset upload-by-URL flow for images, fonts, videos,
and files.
- Preserve source video dimensions through the existing upload route.
- Replace source asset IDs and font-family references with destination
values before runtime insertion.
- Include and remap page social images and marketplace thumbnails.
- Reject current clipboard payloads whose page metadata references
omitted assets.
- Leave the public `pageTransfer.insert` runtime input unchanged.
- Abort insertion with a paste error when an asset cannot be imported.

No clipboard version increase, custom network fetcher, address
blocklist, rollback framework, or global paste wrapper is introduced. If
a multi-asset import partially uploads before a later upload fails, the
completed uploads may remain unused.

## Checklist

- [x] Rebased onto `main`
- [x] Preserve the existing v0.1 clipboard format
- [x] Transfer and remap instance assets
- [x] Transfer and remap page, template, and folder assets
- [x] Preserve video metadata
- [x] Keep runtime page-transfer API compatible
- [x] Add regression coverage
- [x] Review documentation impact and update page-copy documentation
- [x] Run Builder tests and typecheck
- [x] Run project-build tests and typecheck
- [x] Run lint, package-boundary, and generated-API checks

## Verification

- Builder full suite: 219 files, 2,552 tests passed
- Project-build full suite: 78 files, 2,129 tests passed
- Final affected suite: 84 tests passed
- New regressions were confirmed failing before the fixes and passing
afterward
- Builder and project-build typechecks passed
- `pnpm lint` passed
- `pnpm check:package-boundaries` passed
- `pnpm check:generated-api` passed
- Fixture inputs and generated fixture bundles are unaffected
2026-08-25 16:44:38 +02:00
Oleg Isonen 9e504037fb fix: improve MCP reliability across project workflows (#6222)
## Summary

Fixes the reproducible MCP, preview, redirect, audit, mutation, and
asset-upload failures from the latest issue batch. Historical and
non-reproducible reports were investigated without speculative fixes.
Visual verification is now opt-in unless the user explicitly requests
it.

## Issue checklist

- [x] #6207 — report tested recovery when an explicitly selected browser
fails to start.
- [x] #6208 — accept generic and registered buttons as valid label
targets without requiring a separate form label.
- [x] #6209 — reject unknown style coordinates such as `breakpointId`
and document the supported `breakpoint` field.
- [x] #6210 — traced Node 18 access to CLI 0.293.0 predating the
launcher guard; closed as already fixed by #6201.
- [x] #6211 — verified the supported launcher blocks the reported Node
18 crypto path; closed as already fixed by #6201.
- [x] #6212 — give sequential responsive captures the sum of their
per-capture timeout budgets.
- [x] #6213 — keep preview install, build, and server subprocesses on
the parent CLI Node runtime.
- [x] #6214 — add safe confirmation and diagnostics for ambiguous
mutation responses; this does not claim to remove upstream latency.
- [x] #6215 — confirmed as the same historical Node 18 release path as
#6210; closed as already fixed by #6201.
- [x] #6216 — verified malformed input returns structured errors on
current main; closed as not reproducible and improved future failure
diagnostics.
- [x] #6217 — add transaction-id confirmation for large fragment
mutations after ambiguous 5xx responses; this does not make the
operation asynchronous.
- [x] #6218 — verified the existing DevTools reconnect fix from #6205;
closed as already fixed.
- [x] #6219 — preserve the incoming pathname and query when a redirect
target supplies only a hash.
- [x] #6220 — return ordered `uploaded`, `failed`, and `ambiguous`
results so partial asset successes are not lost or retried unsafely.
- [x] #6221 — verified parser recovery without a Node crash; closed as
not reproducible and improved future JSX failure diagnostics.
- [x] #6223 — verified browser-session reuse, reconnect, and cleanup;
closed as already fixed by #6205 after the reported CLI version.
- [x] #6224 — expose both text-asset sources and encode the exactly-one
requirement in the standard MCP schema.
- [x] #6225 — could not reproduce the project-specific upstream timeout;
closed with safe confirmation coverage and improved structured
diagnostics, without claiming a latency fix.
- [x] #6226 — could not reproduce the project-specific upstream timeout;
closed with safe confirmation coverage for insert-fragment, without
adding a speculative fragment-specific fix.
- [x] #6229 — reproduced the reported two-process sequence with the
current local CLI and a real browser; both captures passed, so the
0.293.0 report was closed without a speculative fix.
- [x] Require explicit user consent before preview, screenshots, diffs,
OCR, or rendered visual audits unless visual verification was requested.

## Technical notes

- Ambiguous mutation responses are confirmed only with the same
idempotent transaction id. If confirmation is also ambiguous, the
session reports `AMBIGUOUS_COMMIT`, refreshes state, and never replays a
fresh mutation.
- CLI error mapping retains the original remote error as a
non-enumerable cause, so JSON gateway status reaches the ambiguity
classifier without exposing raw remote context in serialized
diagnostics.
- Forced asset uploads are not automatically retried after an
unknown/network/5xx outcome. Batch results retain the original input
index, including duplicate filenames.
- Stable issue codes and constraints are retained for diagnostics.
Arbitrary validation paths, messages, argument values, project content,
and raw contextual data are excluded.
- The MCP handshake remains the source of tool input schemas;
goal-specific guides avoid duplicating those contracts and reduce
model-context cost.
- `update-asset-content` now exposes `path` and `content` with a
machine-readable `oneOf` constraint, matching the runtime rule that
exactly one source is required.
- Redirect targets with their own pathname or query still replace those
request components.
- Generated runtime contracts, CLI documentation, product documentation,
and deployment fixtures were regenerated from their sources.

## Verification

- [x] Three independent review/test agents exercised visual consent and
preview behavior; mutation, asset, and privacy behavior; and
accessibility, styles, and redirects.
- [x] `pnpm checks` passed after the final transport follow-up: all
workspace tests and typechecks, lint with warnings denied, package
boundaries, generated API contracts, generated docs and documentation
checks, and the complete fixture link/sync/build pipeline.
- [x] Changed-package suites passed, including Project Build (2,125
tests), HTTP client (42 tests), CLI, Protocol, and Builder.
- [x] Complete `pnpm evaluations` run on the preceding implementation
commit: all five real-world fixtures and every behavioral check passed.
Four baseline comparisons passed; one font-assets sample exceeded only
its model-output token allowance, and its isolated rerun passed.
Evaluations were not rerun after the final transport and schema
follow-ups because fresh approval is required.
- [x] Design evaluation MCP response bytes improved from the 39.8 KB
baseline to 37.6 KB after removing duplicated guide contracts.
- [x] Documentation impact reviewed; generated MCP docs and multi-file
upload guidance are updated.

## Related issues

Closes #6207

Closes #6208

Closes #6209

Ref #6210

Ref #6211

Closes #6212

Closes #6213

Ref #6214

Ref #6215

Ref #6216

Ref #6217

Ref #6218

Closes #6219

Closes #6220

Ref #6221

Ref #6223

Closes #6224

Ref #6225

Ref #6226

Ref #6229
2026-08-24 16:07:00 +02:00
Oleg Isonen 626b5e741b fix: Reduce discovery for authored fragments (#6203)
Ref #6177
Ref #6154

## Outcome

Bounded authored-fragment requests no longer receive broad
design/authentication context bundles, registry discovery, or
design-token operations through generic keyword matching.

The initial ProjectSession synchronization is unchanged. Focused target
reads, missing nested schemas, and expression guidance can still add
calls when the requested fragment requires them.

## Root cause

`meta.guide` treated the generic word `design` as visual intent and
allowed workflow-specific context bundle tools to enter ordinary
keyword-ranked recommendations. An authored component insertion could
therefore be classified as visual work and receive
`inspect-design-context` even without a supplied visual reference or a
request to reuse the project design system.

## Implementation

- Give specialized goal guides stable IDs and return structured routing
metadata with the matched workflow and broad context tools.
- Keep `inspect-auth-context` and `inspect-design-context` exclusive to
their specialized workflows.
- Treat Figma, screenshots, supplied designs, and explicit design-system
references as visual intent without treating every use of `design` as
visual work.
- Suppress component/template registry discovery for explicit
`insert-fragment` or authored-fragment requests.
- Suppress design-token operations for those requests unless the brief
explicitly asks for design-system or token reuse.
- Preserve the existing design-input workflow for Figma, screenshots,
and supplied designs.

## Todo

- [x] Add a regression test for the reported authored timer/component
workflow.
- [x] Demonstrate that the regression test fails before the routing fix
and passes afterward.
- [x] Preserve explicit design-input discovery and verification
behavior.
- [x] Add machine-readable workflow routing metadata.
- [x] Review documentation impact and update the authoritative MCP
manual plus generated GitBook output.
- [x] Review fixture impact; no fixture inputs or generated fixture
bundles are affected.
- [ ] Run the complete local agent evaluation suite. It was not run
because repository policy requires separate explicit approval and
approval was not provided.

## Verification

- Project-session MCP tests: 117 passed.
- Project-build and CLI typechecks passed.
- Targeted oxlint passed.
- Documentation generator tests and documentation typecheck passed.
- MCP documentation and CLI generated docs were regenerated twice with
stable hashes.
- `git diff --check` passed.

## Tradeoffs and limitations

- Brief classification remains deterministic intent matching; clients
still decide which recommended tools they actually call.
- The change prevents broad context tools from being recommended
generically but does not enforce a universal discovery-call limit.
- A fragment that genuinely needs existing tokens, unfamiliar
expressions, or unknown target structure can still require focused
discovery.
- No schema migration, project-data migration, or fixture regeneration
is required.
2026-08-20 13:13:45 +02:00
Oleg Isonen b2d62b2e49 fix: Restore scoped descendant selectors (#6195)
## Summary

Restore custom state selectors that target descendants of the selected
instance, including `:hover .button` and `:hover > .icon`.

The CSS engine already generated these selectors correctly. #5886
unintentionally rejected them while hardening MCP style inputs against
selectors that can escape instance scope.

## Technical choices

- Parse exactly one selector with `css-tree`.
- Require the selector to begin with a pseudo-class, pseudo-element, or
attribute state anchored to the selected instance.
- Allow only descendant and direct-child combinators after that anchor.
- Continue rejecting selector lists, unanchored type/class/ID selectors,
sibling combinators, unknown pseudo-selectors, and pseudo-elements used
as ancestors.
- Document CSS variables as the preferred parent-child interaction
pattern; use descendant selectors when selector-based targeting is
specifically required.

## Todo

- [x] Restore scoped descendant and direct-child selectors.
- [x] Preserve instance-scope protections.
- [x] Cover real authoring inputs and generated CSS.
- [x] Demonstrate new tests fail without the restored behavior and scope
guards.
- [x] Verify the reported workflow through the local CLI/MCP and
generated preview.
- [x] Review documentation impact and update the authoritative Builder
docs.
- [x] Verify generated API contracts remain current.

## Verification

- `pnpm --filter @webstudio-is/css-data test` — 4,623 passed, 1 skipped
- `pnpm --filter @webstudio-is/css-data typecheck`
- `pnpm --filter @webstudio-is/project-build exec vitest run
src/runtime/styles.test.ts src/runtime/audit.test.ts` — 198 passed
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm lint`
- `pnpm check:generated-api`
- Documentation targets and anchors verified
- Local MCP disposable-fixture workflow:
- Discovered `insert-fragment` and `update-styles` through MCP metadata.
  - Inserted a card with a descendant `<button class="button">`.
- Dry-run and committed `state: ":hover .button"`; `get-styles` returned
the persisted state.
- MCP preview generated `.chjrfuf:hover .button` while the card used the
`chjrfuf` instance class, proving the selector remained
instance-anchored.
  - Preview screenshot completed and returned HTTP 200.
- Agent evaluations were not run because the suite has no
custom-selector coverage

## Known limitation

Sibling combinators remain unsupported because they can select elements
outside the selected instance. Selector lists remain unsupported because
later list entries can escape the generated instance prefix.

Closes #6175
2026-08-20 00:20:03 +02:00
Oleg Isonen 2df130171f feat: Resolve asset references as structured metadata (#6184)
## Summary

- add exact `$ref` asset values that resolve to Asset Manager metadata
- keep plain local asset paths returning URL strings for backward
compatibility
- derive reference metadata from the canonical asset object so newly
added asset fields flow through automatically
- let Assets query output fields select only the metadata returned,
including nested arrays
- carry the same resolved shape through preview, generated preview,
published runtime, and static export
- document the syntax and accessible image binding in Content Engine and
MCP docs

## Design

The source document declares only the relationship:

```yaml
featureImage:
  $ref: ./assets/hero.png
```

The Assets query controls projection with fields such as
`properties.featureImage.src` and `properties.featureImage.description`.
Selecting `properties.featureImage` returns the canonical Asset Manager
asset metadata together with the resolved `src`. The Content Engine
runtime contract accepts the canonical asset object instead of
maintaining a second metadata field list; `url` becomes `src`, and
internal `contentRef` is not exposed.

Plain strings retain their existing URL-string result. Existing document
`$ref` behavior is preserved by resolving structured asset metadata only
when the target is a known non-document asset.

## Verification

- [x] Content Engine full test suite: 298 tests
- [x] SDK full test suite: 517 tests
- [x] Asset repository tests: 39 tests
- [x] CLI prebuild tests: 58 tests
- [x] Content Engine, SDK, Asset Uploader, CLI, and Project Build
typechecks
- [x] Repository lint
- [x] Content Engine and MCP documentation generation and tests
- [x] Generated API check
- [x] Local fixture build and generated fixture update
- [x] New behavior tests demonstrated fail before/pass after the
implementation
- [ ] Complete fixture sync (development API currently rejects this
branch's newer CLI protocol; local fixture build passes)
- [ ] Agent evaluations (not run; require explicit approval)

## Checklist

- [x] Local structured references return field-selected metadata
- [x] Newly added canonical asset fields flow through without a Content
Engine whitelist change
- [x] Plain-path queries remain backward compatible
- [x] Nested object and array projection
- [x] External URL strings remain usable
- [x] Missing or null descriptions allow `??` fallback to the title
- [x] Expanded metadata counts toward query result-size limits
- [x] Documentation impact reviewed and Content Engine and MCP docs
updated
- [ ] Missing asset targets return a non-failing unresolved value and
dedicated diagnostic
- [ ] Apply the resource and Image binding changes to the separate
Webstudio blog project

## Known limitation

A `$ref` whose asset target no longer exists is not yet converted into
an unresolved asset value with a dedicated diagnostic. This PR
intentionally does not claim that acceptance criterion yet.

Ref #6179
2026-08-19 01:11:41 +02:00
Oleg Isonen 5a57234c75 revert: Store Content Block content in MDX assets
Revert the accidental merge of #6125 so the implementation can continue in draft PR #6181.
2026-08-18 13:23:32 +01:00
Oleg Isonen 5ec3547d36 fix: Support JSON content files in Builder (#6168)
## Summary

- make the complete asset filename editable in Asset settings
- convert existing editable text assets when their extension changes
while preserving the asset ID
- accept every JSON root type and normalize JSON-compatible syntax into
formatted strict JSON
- expose the same optional extension change through MCP/API
`update-asset-content`
- initialize new JSON files and empty text-to-JSON conversions with a
valid empty object
- distinguish browser URL imports from JSON file uploads so JSON objects
with a root `url` field remain file content
- update the Assets and Content Engine documentation

## Root cause

Asset settings rendered the extension as a fixed suffix outside the name
input. The metadata rename mutation could only change the display
basename, while content revisions always inherited the current storage
extension. As a result, changing `.md` to `.json` was impossible in the
UI and unavailable through the shared MCP/API revision command.

Asset settings now edits the complete filename. Ordinary renames keep
using the metadata mutation. A text-extension change loads the current
content, validates it for the target format, and writes a new immutable
revision through the existing conflict-safe content endpoint. The
existing Acorn-backed `parseJsonExpression` utility accepts
JSON-compatible syntax without evaluating code and serializes it as
strict JSON. Objects, arrays, strings, numbers, booleans, and `null` are
all accepted.

## Checklist

- [x] Create JSON files by entering a `.json` filename
- [x] Edit the complete filename in Asset settings
- [x] Convert editable text-file extensions while preserving the asset
ID
- [x] Expose extension conversion through `update-asset-content`
- [x] Create JSON files and convert empty text files with a valid empty
object
- [x] Normalize safe JSON-compatible syntax to strict JSON
- [x] Accept objects, arrays, and scalar JSON roots
- [x] Reject executable or incomplete content
- [x] Preserve browser-uploaded JSON object shapes
- [x] Review and update authoritative Builder documentation
- [x] Add focused UI, revision, HTTP-client, CLI/MCP, protocol, and
upload regression tests
- [x] Run affected typechecks, lint, documentation checks, and the
Builder production build
- [ ] Manually verify create, edit, rename `.md` to `.json`, reload
persistence, and Content Engine query in a browser
- [ ] Run the complete local agent evaluation suite if approved

## Verification

- 217 focused tests passed across Builder, asset revisions, HTTP client,
CLI/MCP, and protocol
- Builder, asset-uploader, HTTP-client, protocol, CLI, and project-build
typechecks passed
- Builder production build passed
- MCP and Content Engine documentation checks passed
- `oxlint` on changed TypeScript files passed
- `git diff --check` passed
- Regression tests were demonstrated failing before the implementation
and passing afterward

A disposable E2E database and PostgREST stack bootstrapped successfully
in the earlier real-path test, but UI automation remained blocked
because no controllable browser was available. Fixture inputs and
generated fixture outputs are not affected. Agent evaluations were not
run because approval is required and was not requested.

Closes #6167
2026-08-18 11:39:01 +02:00
Oleg Isonen 81d5148046 fix: canonicalize trailing-slash page URLs (#6173)
## Outcome

Canonical page URLs now omit a trailing slash in Webstudio Cloud sites
and exported JavaScript applications. A request such as
`/about/?campaign=launch` receives a permanent `301` redirect to
`/about?campaign=launch`, preventing both URL variants from serving
identical page content.

The behavior is implemented once in the generated root redirect handler,
so it covers fixed and dynamic application routes without generating an
extra route per page. Exact project redirects remain authoritative.
Protocol-relative-looking paths such as `//example.com/` are left
untouched so canonicalization cannot become an external redirect.

Static exports cannot redirect requests themselves. Their URL behavior
remains host-specific and is documented for Cloudflare Pages, Netlify,
Vercel, and other static hosts.

## Implementation

- Add one shared pathname normalizer in the SDK.
- Apply canonicalization after exact authored redirect matching in both
generated application runtimes.
- Ignore repeated leading slashes before constructing a redirect
location.
- Extend redirect-loop detection to model the implicit canonical
redirect.
- Regenerate the affected fixture applications from their templates.
- Document application canonicalization and host-specific static export
behavior.

## Todo

1. [x] Add central trailing-slash canonicalization.
2. [x] Preserve query strings, encoded path segments, and the root path.
3. [x] Cover concrete dynamic URLs without per-route generation.
4. [x] Preserve explicit project redirect precedence.
5. [x] Detect redirect loops that pass through canonicalization.
6. [x] Prevent protocol-relative external redirects.
7. [x] Regenerate and review fixture output.
8. [x] Review documentation impact; document application and SSG
behavior.
9. [x] Run targeted and repository-level verification.

## Verification

- SDK tests: 548 passed
- SDK typecheck: passed
- Project Build tests: 2,379 passed
- Project Build typecheck: passed
- CLI tests: 997 passed; two tests timed out only while three package
suites ran concurrently, then both affected files passed in isolation
(6/6)
- Redirect regression suite after security follow-up: 302 passed
- CLI typecheck: passed
- `pnpm lint`: passed
- `pnpm check:package-boundaries`: passed
- `pnpm fixtures`: passed
- Documentation internal anchors and Markdown whitespace: checked
- New regression tests were confirmed failing before their
implementations and passing afterward
- Agent evaluations were not run because they require separate explicit
approval

Closes #6172
Closes https://github.com/webstudio-is/webstudio/issues/4327
2026-08-18 11:38:27 +02:00
Oleg Isonen 86b6310fff feat: Store Content Block content in MDX assets 2026-08-17 15:22:20 +01:00
Oleg Isonen af11ec211f feat: Search all local project data from MCP (#6165)
Ref #6154

## What

- Add standalone search across all 13 synchronized Builder namespaces.
- Search in the CLI ProjectSession and send only paged matches to the
model; initial synchronization is unchanged.
- Return stable match ids, exact paths, entities, references, and
affected routes.
- Preserve deprecated `scopes` output, redact recognized credential
fields, and document the command.

## Objectives

- [x] O1 — Universal Builder namespace search
- [x] O2 — Stable identity and actionable location
- [x] O3 — Filters, pagination, route impact, and references
- [x] O4 — Honest synchronization → local search → model-input boundary
- [x] O5 — Standalone command and workflow documentation
- [x] O6 — Safety, compatibility, typed contracts, and regression tests

Verified by Atlas (implementation), Scribe (docs/workflow), and Sentinel
(adversarial review).

## Remaining #6154 work

- [ ] Atomic semantic multi-record updates
- [ ] Targeted route rendering and assertions
- [ ] General slow-operation consent and time budgets
- [ ] Full agent evaluation suite after approval

## Verification

- Project search + MCP: 129 tests passed
- Protocol docs: 8 tests passed
- CLI schema/docs: 23 tests passed
- Compact MCP handshake budget test passed
- Project-build, protocol, and CLI typechecks passed
- Generated docs and API checks passed
- Agent evaluations not run; approval is required

## Limitation

Asset metadata is searchable. Asset binaries and document contents are
not Builder namespace data and are not searched.
2026-08-17 14:08:25 +02:00
Oleg Isonen dbfb0a9849 fix: Preserve variable font styles (#6153)
## Summary

Preserve font style metadata for variable fonts so upright and italic
files from the same family produce distinct `@font-face` rules. Variable
fonts with an `ital` or `slnt` axis are also registered for the matching
CSS styles.

## Technical choices

- Detect `normal`, `italic`, and `oblique` from the font binary,
preferring OS/2 `fsSelection`, then `post.italicAngle`, then the
existing subfamily parser.
- Add `style` to variable-font metadata while defaulting legacy stored
records to `normal`.
- Keep update metadata partial and strict, but accept and persist
`style` and `variationAxes` together so a `get-asset` result can be
written back.
- Group generated font faces by family, style, and browser-selectable
variable descriptors so separate upright and italic files cannot
overwrite each other.
- Regenerate the runtime operation contracts from the updated schema.

## Checklist

- [x] Add regressions using real upright and italic Inter variable-font
binaries.
- [x] Cover style detection, metadata compatibility, updates, and
generated font-face selection.
- [x] Verify the final stylesheet contains distinct upright and italic
`@font-face` rules.
- [x] Verify the public asset update contract accepts a complete
variable-font metadata object.
- [x] Review documentation impact and update the custom-font guide.
- [x] Regenerate and verify runtime API contracts.

## Verification

- `@webstudio-is/fonts`: 14 tests and typecheck passed.
- `@webstudio-is/asset-uploader`: 198 tests and typecheck passed.
- `@webstudio-is/sdk`: 514 tests and typecheck passed.
- `@webstudio-is/project-build`: 2,094 tests and typecheck passed.
- CLI: 980 tests and typecheck passed.
- Builder typecheck passed.
- `pnpm lint`, `pnpm check:package-boundaries`, `pnpm
check:generated-api`, and `pnpm check:generated-docs` passed.
- Regression tests were demonstrated failing against the previous
behavior and passing with the fix.
- Fixture builds regenerated successfully with no diff. Full remote
fixture sync was attempted, but the deployed API rejected the source CLI
contract as incompatible.
- Agent evaluations were not run because they require separate explicit
approval.

Closes #6150
2026-08-15 22:52:00 +02:00
Oleg Isonen a22b23936d docs: Explain Content Engine limits (#6123)
## Summary

- explain which kinds of blogs, catalogues, directories, and file-based
content fit the Content Engine
- identify projects better served by an external CMS, commerce backend,
or media service
- translate the current query, database, and content-loading limits into
practical guidance
- link readers to the complete Content Engine limits reference

## Why

The existing guide explains how to build a Markdown blog but does not
help readers decide whether the Content Engine is appropriate for their
content volume, media needs, or editorial workflow. This addition sets
expectations before readers commit to an architecture.

## Todo

- [x] Document suitable Content Engine use cases.
- [x] Document cases that should use an external system.
- [x] Confirm every stated limit against the current implementation and
generated reference.
- [x] Review documentation impact: the authoritative Content Engine
guide is updated; navigation is unchanged because this is an existing
page.
- [x] Verify the reference link and anchor.

## Verification

- `pnpm docs:content-engine:test` — passed
- `pnpm docs:content-engine:check` — generated files are clean
- verified `content-engine-reference.md#query-limits` exists
- `git diff --check` — passed

The repository pre-commit hook was bypassed after its `oxfmt` task
rejected the docs-only target because `docs/` is excluded from that
formatter. The hook restored the file unchanged; the
documentation-specific checks above passed before committing.
2026-08-13 11:23:12 +02:00
Oleg Isonen 87968f40ff fix: Improve MCP authoring and preview reliability (#6105)
## Summary

Fix MCP reliability failures reported in ten issues against CLI 0.288.0
and 0.289.0:

- keep every advertised array input schema client-valid, including
compact and tuple schemas
- report a text asset update as successful when its persisted revision
and bytes confirm that the server committed despite a failed response
- preserve standard package import resolution in generated React Router
previews
- upload variable WOFF2 fonts when callers provide the documented static
font metadata shape
- explain unmatched JSX as a stateless fragment syntax error
- distinguish the `animation` component namespace from value helpers and
reject unsupported CSS at-rules with an actionable validation error
- forward optional session tools such as `download-asset` through
top-level CLI shortcuts
- isolate published generated-preview dependencies from parent
workspaces
- return structured screenshot failures when React Router production
builds fail

Sensitive visual regression checks also run automatically for
organization-authored fork PRs. External fork PRs remain gated by
`safe-to-deploy`.

## Root causes and implementation

The MCP handshake compactor retained an array's type but discarded its
`items` schema. Unconstrained and tuple arrays could also be advertised
without `items`. Schema normalization now adds `items: {}` whenever an
array omits it, while focused discovery retains complete constraints.
Catalog coverage rejects every missing array item, including tuple
schemas, and the handshake remains below its 200 KB budget.

Asset content updates previously treated any failed response as a failed
mutation, even when the server had already swapped the revision.
Recovery now reads the current revision, compares the persisted bytes,
and confirms the revision remains stable across the read. It returns the
updated asset only when all evidence agrees. Otherwise it returns
`ASSET_UPDATE_COMMIT_UNCERTAIN` with guidance not to retry blindly.
Definitive 4xx responses retain their original errors.

The generated React Router Vite config replaced the client and SSR
resolver condition lists without explicitly retaining the standard
`import` condition. Both lists now preserve `import`. The regression
test generates a dynamic route, stages the built React Router SDK as a
published dependency without the local `webstudio` source condition, and
runs the generated build.

Variable fonts are detected with `variationAxes`. Merging the documented
static `style` and `weight` upload metadata into that detected variable
shape made validation fail after the binary was read. Font override
validation and shape-aware merging now live in `@webstudio-is/fonts`.
The SDK delegates to that domain function, which validates the complete
override before applying compatible fields, retains the detected
static-or-variable shape, and keeps the detected format.

Fragment parsing is stateless. Unmatched opening and closing tags now
explain that every opening element must use the same closing tag or be
self-closing, and that refreshing the session cannot repair malformed
JSX.

The `animation` binding is a component namespace, not a callable helper.
Tool schema text, evaluation errors, and the MCP manual now separate
component namespaces from value helpers. The MCP JSX `css` binding
parses the final interpolated CSS with CSS-tree and recursively rejects
selectors and unsupported at-rules such as `@keyframes`, while
preserving supported declarations and case-insensitive `@media` rules.
Keeping this validation at the external binding avoids changing internal
component templates.

CLI shortcut discovery previously omitted optional session tools even
though the runtime registered their handlers. Shortcut discovery now
includes the same optional tool categories as the CLI MCP host, while
real top-level CLI commands keep precedence.

Published generated previews could reuse a stale `node_modules` symlink
into a parent source workspace. Only development manifests containing
the local placeholder version may now reuse the CLI dependency tree.
Published manifests replace symlinks with a local install and disable
npm workspace discovery.

React Router could report a bundler failure while its build command
exited successfully outside CI, allowing screenshot startup to terminate
without a structured result. Managed production builds now set `CI=1`,
so the existing MCP error boundary receives the nonzero build failure
and prints structured JSON.

Visual regression authorization now distinguishes repository members
from external contributors. Organization members' fork PRs run on open,
synchronize, and reopen events; external fork code runs only from a
`safe-to-deploy` label event, and a later commit does not reuse that
approval.

## Implementation checklist

- [x] Preserve valid array item schemas in compact MCP discovery
- [x] Normalize tuple input schemas with an `items` fallback
- [x] Keep the MCP handshake below its 200 KB size budget
- [x] Cover the real `update-props` schema and oversized array
compaction
- [x] Reconcile failed asset-update responses against stable persisted
content
- [x] Reject recovery when a concurrent asset revision is detected
- [x] Preserve `import` resolution in generated React Router client and
SSR builds
- [x] Build a generated dynamic route against a staged published SDK
package
- [x] Regenerate and verify affected React Router fixtures
- [x] Preserve detected variable-font axes with documented upload
metadata
- [x] Add actionable unmatched-tag and unsupported-CSS validation
- [x] Correct animation namespace guidance in tool discovery and errors
- [x] Forward optional session tools through top-level CLI shortcuts
- [x] Replace parent-workspace dependency links for published previews
- [x] Disable npm workspace discovery for isolated preview installs
- [x] Make React Router build failures return nonzero in managed
previews
- [x] Run visual regression automatically for organization-authored PRs
- [x] Verify each regression test fails without its corresponding fix
- [x] Review documentation impact and update the authoritative MCP
manual plus generated outputs
- [x] Confirm no database migration or compatibility migration is
required
- [ ] Run `pnpm evaluations` after explicit approval; not run because
repository policy requires approval before agent evaluations

## Verification

- `packages/cli/src/prebuild.test.ts`: 57 tests passed
- `packages/cli/src/commands/schema.test.ts` and `mcp.test.ts`: 51 tests
passed
- `packages/project-build/src/runtime/components.test.ts` and
`mcp.test.ts`: 194 tests passed
- `packages/asset-uploader`: 196 tests passed
- focused font schema, SDK asset schema, CSS parser, and template CSS
tests passed
- `packages/http-client/src/index.test.ts`: 38 tests passed
- relevant CLI, project-build, asset-uploader, fonts, SDK, CSS data,
template, HTTP client, and React Router SDK typechecks passed
- MCP documentation tests, typecheck, generation, and Markdown source
tests passed
- React Router SDK package build passed
- full fixture link, sync, and generation pipeline passed
- affected Docker and Vercel fixture typechecks and locally bootstrapped
production builds passed
- complete CLI suite: 61 test files and 966 tests passed
- CLI typecheck passed
- preserved nested-workspace dynamic preview installed local `0.289.0`
dependencies and completed its production build
- visual-regression workflow YAML parsing and authorization truth table
passed
- repository lint passed with warnings denied
- formatting and `git diff --check` passed

## Tradeoffs and limitations

Deferred array schemas advertise unconstrained `items` in the compact
handshake to remain client-valid without duplicating detailed
constraints. Agents can retrieve the complete item schema through
focused `meta.get-more-tools` discovery.

Asset reconciliation performs three read requests only after an
uncertain 5xx, malformed response, or transport failure. This keeps the
normal successful path unchanged and avoids claiming success if the
revision changes during recovery.

For variable fonts, detected axes define the metadata shape. Static
`style` and `weight` hints are ignored because they cannot be
represented alongside `variationAxes`; compatible fields such as
`family` still override detection.

Native CSS keyframe authoring remains unsupported in fragment `css`
templates. The tool now rejects it before rendering and directs callers
to editable Webstudio Animation components instead of advertising a
nonexistent callable helper.

Published previews perform one isolated npm install when no valid local
dependency tree is cached. Local source development keeps the fast
workspace symlink path for placeholder-version manifests.

Closes #6104
Closes #6099
Closes #6098
Closes #6106
Closes #6107
Closes #6108
Closes #6114
Closes #6115
Closes #6116
Closes #6117
2026-08-12 21:23:35 +02:00
Oleg Isonen 5c6ffd3148 docs: document Content Engine workflows (#6097)
## Summary

- add a step-by-step Content Engine guide for building a Markdown
article overview and dynamic article page
- clarify that the example folder structure is optional and link to the
Markdown Blog marketplace template
- explain that an AI agent can complete the entire workflow through
Webstudio MCP and include a ready-to-use prompt
- document frontmatter, JSON files, relative and published asset paths,
document references, draft filtering, and single-result resources
- document Markdown-to-Markdown, Markdown-to-JSON, JSON-to-Markdown, and
JSON-to-JSON document references, including fragments and JSON Pointer
escaping
- add a schema-aware `webstudio man content-engine` reference for
fields, operators, saved and preview inputs, result/output/content
modes, diagnostics, limits, and reference syntax
- generate the GitBook Content Engine reference and the matching MCP
manual section from that shared CLI source
- fail documentation generation when result modes, output modes, content
modes, diagnostic fields, or Content Engine limits drift from the
implementation
- add four focused visual-editor screenshots
- keep the Assets page focused on asset management and link it to the
dedicated Content Engine guide
- update MCP Assets examples and the Markdown blog recipe to use
Markdown files, `result: "one"`, direct detail bindings, page metadata,
and visual verification

## Impact

Users get one workflow for treating files as the source of truth while
designing article listings and detail pages in Webstudio. The generated
reference documents the complete Assets query contract and
document-reference system. Agents receive the same reference through the
MCP manual, so the product and agent documentation no longer need
separate factual copies. This does not change product runtime behavior.

## Checklist

- [x] Documentation impact reviewed
- [x] Added the Content Engine guide and generated reference to
`docs/SUMMARY.md`
- [x] Updated related Assets, Data variables, and MCP documentation
- [x] Verified internal documentation and image targets
- [x] Verified the Markdown Blog marketplace URL returns HTTP 200
- [x] Confirmed the MCP recipe test failed before the recipe correction
and passed afterward
- [x] Confirmed the schema-coverage test fails when an implemented
result mode is omitted and passes after restoration
- [x] Confirmed both Content Engine generator tests fail without their
corresponding generator behavior and pass after restoration
- [x] Ran `pnpm --filter @webstudio-is/project-build test --
src/mcp.test.ts`
- [x] Ran `pnpm --filter webstudio test --
src/commands/api-command-docs.test.ts`
- [x] Ran the Content Engine and MCP generator tests
- [x] Ran project-build and CLI typechecks
- [x] Ran `pnpm check:package-boundaries`
- [x] Ran targeted lint and formatting checks
- [x] Ran `pnpm check:generated-docs`
- [x] Verified the final diff and working tree
- [ ] Agent evaluation suite: not run because it requires explicit
approval

Ref #5973
2026-08-11 21:24:27 +02:00
Oleg Isonen 43a9f824ad fix: Resolve published asset paths in structured content (#6096)
## Summary

Resolve Markdown frontmatter and JSON values written as published paths
such as `./assets/hero.png` or `/assets/hero.png`, even when the content
document and asset live in sibling Asset folders.

This staging PR replaces #6094 and supersedes #6016. #6016's raw-format
changes were unrelated to the reported asset-path problem.

## Technical approach

- Keep format-neutral path indexing and resolution in
`asset-path-resolution.ts`.
- Keep `markdown-assets.ts` limited to Markdown destination parsing.
- Normalize both Markdown frontmatter and JSON objects through the
shared structured-value reference pipeline.
- Keep canonical source-relative resolution as the first choice.
- Recognize only the published `assets/<file-name>` path shape as a
fallback.
- Match the final canonical asset file name only when it uniquely
identifies one project asset.
- Leave external URLs, missing assets, ambiguous names, and unrelated
root-relative paths unchanged.
- Resolve recorded asset references to runtime URLs when queries render
their results.

## Compatibility

This extends resolution for imported/published structured content
without changing ordinary source-relative paths or direct asset ID
references. Duplicate final file names intentionally remain unresolved
to avoid choosing the wrong asset.

## Checklist

- [x] Add regression coverage for relative and root-relative published
asset paths.
- [x] Cover discovery and runtime structured-value resolution.
- [x] Add explicit JSON document coverage with the asset in a sibling
folder.
- [x] Verify ambiguous asset names remain unresolved.
- [x] Separate format-neutral resolution from Markdown parsing.
- [x] Run the full content-engine test suite.
- [x] Run content-engine typecheck and targeted lint/format checks.
- [x] Rebuild content runtime and repository fixtures.
- [x] Review documentation impact and update the Assets guide.
- [ ] Manually verify the published site from `md-assets.staging`.
- [ ] Confirm CI checks pass.

## Verification

- Regression tests were run with the published-path fallback disabled
and failed for both generic structured values and the explicit JSON
integration case.
- `pnpm --filter @webstudio-is/content-engine test` — 294 tests passed.
- `pnpm --filter @webstudio-is/content-engine typecheck` — passed.
- `pnpm exec oxlint --deny-warnings ...` — passed.
- `pnpm exec oxfmt --check ...` — passed.
- `pnpm --filter webstudio build:content-runtime` — passed.
- `pnpm -r fixtures:build` — passed; no generated fixture diff.
- `git diff --check` — passed.
- Agent evaluations were not run because they require separate explicit
approval.

Closes #6093
2026-08-10 21:48:32 +02:00
Oleg Isonen eb46abf4e5 fix: Recover missing preview assets (#6091)
## Summary

Fix session-based generated preview startup when the local
`.webstudio/assets` directory has not been created yet.

- materialize the current session's asset files before compiling the
Assets resource index
- reuse the existing safe asset downloader so missing parent directories
are created and downloads retain retry and temporary-file cleanup
behavior
- return the stable `PREVIEW_ASSET_DOWNLOAD_FAILED` code with a concrete
recovery action when asset access cannot be restored automatically
- document the automatic recovery and regenerate the authoritative MCP
manual

## Root cause

`preview.start` materialized current ProjectSession data and compiled
reachable Assets resources before generated-app prebuild. That
compilation called `realpath` on the local assets directory, but the
session path had never downloaded assets the way `webstudio sync` does.
A newly linked project without `.webstudio/assets` therefore failed with
`ENOENT` before preview generation.

## Technical choices

- Download only after confirming that the project has a reachable Assets
resource compilation plan.
- Keep canonical path and symlink validation in the filesystem content
source after materialization.
- Wrap download failures at the preview boundary without exposing local
paths or raw remote diagnostics.
- Preserve the existing generated-doc source/generator workflow rather
than editing generated guidance alone.

## Todo

- [x] Reproduce startup from a missing local assets directory
- [x] Materialize required session assets before index compilation
- [x] Add actionable failure recovery
- [x] Add fail/pass regression coverage
- [x] Review documentation impact and update the MCP vision guidance
- [x] Regenerate the CLI and GitBook MCP manuals
- [x] Review fixture impact — no fixture inputs or generated fixture
outputs are affected
- [x] Run the focused low-reasoning Markdown fixture, including the cold
assets-directory scenario
- [ ] Deferred: run the remaining agent evaluation fixtures

## Verification

- Regression demonstrated failing before the fix with `ENOENT` for the
missing assets directory, then passing after the fix.
- Recovery regression demonstrated failing with raw `ENOENT`, then
passing with `PREVIEW_ASSET_DOWNLOAD_FAILED` and the retry action.
- `pnpm --filter webstudio test` — 60 files, 976 tests passed.
- `pnpm --filter webstudio exec vitest run src/project-session.test.ts
src/mcp-guidance.test.ts` — 25 tests passed after final fixture cleanup.
- `pnpm --filter webstudio typecheck` — passed.
- Targeted `oxlint --deny-warnings` — passed.
- Documentation generation, docs tests, and docs typecheck — passed. The
aggregate generated-doc clean-tree guard reports the intentionally
changed generated CLI/GitBook outputs included in this PR.
- `git diff --check` — passed.
- Focused real-world evaluation: `gpt-5.4-mini` with
`model_reasoning_effort="low"`, `markdown-blog-v1`, 12/12 MCP calls
succeeded, 0 failures, 0 retries, 2 rendered verifications, and all
fixture checks passed.
- Cold-directory variant: removed `.webstudio/assets` immediately before
the first rendered verification; preview restored the required assets,
and both verification calls plus content-database checks passed.
- Remaining high-impact fixtures were not run; this was the targeted
real-world scenario requested for #6090.

## Known limitations

Preview startup still requires network and project asset access when
required files are not already local. It now reports a stable error and
tells the caller to restore access and retry instead of leaking an
`ENOENT`.

Closes #6090
2026-08-10 21:13:45 +02:00
Oleg Isonen db3b11970f fix: Optimize images in Markdown Embed (#6089)
## Summary

Markdown Embed now renders image tags with the same optimized attributes
and canvas behavior as the SDK Image component.

The implementation shares the complete attribute-resolution path without
rendering React inside the Markdown pipeline:

- `@webstudio-is/image` exposes one framework-independent
`getImageAttributes` function for optimization, placeholder fallback,
loading, decoding, dimensions, alternative text, and responsive sources.
- React-only element props such as `className`, `style`, handlers,
context, and refs stay in React callers.
- The SDK Image component and Markdown renderer both use
`getSdkImageProps` for canvas-specific loading, decoding, asset
identity, and incomplete-upload handling.
- Markdown retains only the necessary HTML attribute-name conversion,
sanitization, and Safari-safe `sizes` / `srcset` / `src` ordering.
- The redundant low-level React Image primitive was removed. Its callers
now render native `img` elements, and the image package no longer has
React runtime or peer dependencies.

## Compatibility and tradeoffs

- The SDK Image component remains the public React component because it
owns React context and ref forwarding.
- The removed low-level `@webstudio-is/image` Image export is replaced
by the pure attribute function for internal callers.
- No data migration or schema change is required.
- Loader output is sanitized after transformation, while unsafe input
URLs are sanitized before reaching the loader.

## Todo

- [x] Share image attribute resolution between native, SDK, and Markdown
images.
- [x] Preserve SDK canvas behavior for Markdown images.
- [x] Preserve safe URL sanitization and Safari-safe responsive
attribute ordering.
- [x] Remove the redundant low-level React image primitive and migrate
its callers.
- [x] Remove the overlapping `getImageProps` abstraction.
- [x] Add regression coverage comparing Markdown, SDK Image, and native
attribute output.
- [x] Review documentation impact and update the Markdown Embed
documentation.
- [x] Run targeted tests, typechecks, builds, lint, formatting,
package-boundary checks, and fixture generation.

## Verification

- `pnpm --filter @webstudio-is/image test`
- `pnpm --filter @webstudio-is/image typecheck`
- `pnpm --filter @webstudio-is/image build`
- `pnpm --filter @webstudio-is/image dts`
- `pnpm --filter @webstudio-is/sdk-components-react test`
- `pnpm --filter @webstudio-is/sdk-components-react typecheck`
- `pnpm --filter @webstudio-is/sdk-components-react build`
- `pnpm --filter @webstudio-is/sdk-components-react dts`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm --filter @webstudio-is/design-system typecheck`
- Targeted Oxlint and Oxfmt checks
- `pnpm check:package-boundaries`
- `pnpm fixtures` (no fixture diff)

Ref #6014
2026-08-10 18:21:45 +02:00
Oleg Isonen e4983b0d1d fix: Report unsupported Node versions (#6058)
Closes #6057

## Outcome

Generated projects now reject Node.js versions older than 22.12 before
React Router, Remix, or Vite evaluates SDK components. Users see the
package-manager engine error with the current and required versions
instead of the indirect `sanitize-html` / `htmlparser2`
`ERR_REQUIRE_ESM` stack trace.

## Root cause

`sanitize-html@2.17.6` synchronously loads the ESM-only
`htmlparser2@12`. That dependency combination relies on synchronous
`require(ESM)` support, which Node.js enabled by default in 22.12.
Generated projects incorrectly declared the broader `>=22` range,
allowing an unsupported runtime to reach SSR evaluation.

## Implementation

- Set the CLI and all generated base templates to `engines.node:
>=22.12.0`.
- Add npm `devEngines.runtime` with `onFail: error`, which runs before
install, CI, and run commands.
- Enable `engine-strict` in generated npm configuration so pnpm also
rejects an unsupported runtime.
- Regenerate every tracked CLI fixture package manifest.
- Update CLI documentation and the release-smoke manifest contract.
- Remove the earlier sanitizer-bundling workaround from the branch;
sanitizer code and packaging are unchanged from `main`.

## Checklist

- [x] Declare the exact Node.js compatibility floor
- [x] Fail before generated development, build, and start commands
- [x] Cover the generated engine contract with a fail/pass regression
- [x] Regenerate and inspect all affected fixtures
- [x] Review documentation impact and update the CLI guide
- [x] Keep sanitizer behavior and SDK packaging unchanged

## Verification

- Regression demonstrated failing before the template metadata change
and passing afterward
- `pnpm --filter webstudio test` — 946 passed
- `pnpm --filter webstudio typecheck`
- `pnpm --filter webstudio build`
- `pnpm fixtures`
- `pnpm lint`
- `pnpm exec oxfmt --check` on changed source, template, fixture, and
documentation files
- `git diff --check`

Agent evaluations were not run because they require separate approval.
2026-08-09 01:53:31 +02:00
Oleg Isonen 1fe087ed26 fix: Improve MCP preview and mutation reliability (#6073)
## Summary

Fix the second MCP reliability batch across tool discovery, runtime
mutations, preview/screenshot setup, Windows process launching, and
stdio recovery.

## Implementation

- [x] #6071 Accept canonical dotted/kebab tool names and MCP underscore
names in `mcp run`; return near matches and `meta.index` guidance for
unknown tools.
- [x] #6067 Parse JSON-stringified `tools` arrays in
`meta.get-more-tools`.
- [x] #6069 Preserve resource-backed variables when renaming without a
replacement value.
- [x] #6068 Parse CSS declaration strings consistently in design-token
style updates.
- [x] #6065 Fail immediately for occupied explicit screenshot ports and
discover Chromium from the Playwright browser cache.
- [x] #6066 Recover the stdio MCP transport after an abandoned partial
newline-delimited frame.
- [x] #6072 Launch managed Windows previews through Node plus
`npm-cli.js`, avoiding `spawn EINVAL`.
- [x] #6061 Reuse `npm-cli.js` when launched through `npx`, fall back to
the Node-adjacent npm CLI on Windows, forward `npm_config_cache`, and
include sanitized package-manager diagnostics.
- [x] Address review findings: preserve rebinding on resource scope
changes and discover default Playwright caches on Linux, macOS, and
Windows.
- [x] Complete the DRY pass: remove the dead npm command selector,
centralize accepted MCP tool-name lookup, simplify stdio frame recovery,
and share Levenshtein distance across MCP suggestions and screenshot
text matching.
- [x] Complete the follow-up review: centralize preview URL port
resolution, use `get-port` instead of custom TCP probing, use named
resolver dependencies, and correctly reuse owned previews on default
HTTP/HTTPS ports.
- [x] Complete the final review: use non-reserving `detect-port` checks
for explicit ports and repair the generated-app guidance regression
coverage.
- [x] Audit new utility code against maintained libraries: use
`fastest-levenshtein` for edit distance; retain custom stdio recovery,
Playwright cache discovery, and npm launching where no supported library
API preserves the required behavior.
- [x] Audit Windows/macOS/Linux compatibility helpers: use `path-key`
for environment PATH casing and `which` for executable discovery; retain
explicit Playwright layouts, browser application paths, npm CLI
resolution, symlink types, and process-tree shutdown where behavior is
platform- or product-specific.
- [x] Add fail/pass regression coverage for every fix.
- [x] Review documentation impact and update the CLI MCP sources plus
generated GitBook output.
- [x] Review fixture impact: no fixture inputs or generated fixture
bundles are affected.
- [x] Run targeted tests, package type checks, lint, package-boundary
checks, generated API checks, and generated documentation checks.
- [ ] Agent evaluations — not run because repository policy requires
separate explicit approval.

## Verification

- Complete CLI suite: 60 files, 968 tests passed.
- Project-build affected tests: 4 files, 382 tests passed.
- `pnpm --filter webstudio typecheck`
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm lint`
- `pnpm check:package-boundaries`
- `pnpm check:generated-api`
- `pnpm check:generated-docs`

Each new regression test was run against the unfixed behavior to confirm
the expected failure, then rerun after its fix.

## Compatibility note

The stdio recovery layer discards only an unterminated frame that
remains idle for five seconds. Complete newline-delimited JSON-RPC
frames continue through the SDK transport unchanged.

Closes #6072
Closes #6071
Closes #6069
Closes #6065
Closes #6066
Closes #6067
Closes #6068
Closes #6061
2026-08-09 01:04:01 +02:00
Oleg Isonen d5b3f3ab80 fix: Improve MCP authoring reliability (#6056)
## Summary

This consolidates the reported MCP reliability fixes into one delivery.

- validate all inserted fragment expressions with the shared runtime
rules and reject text/expression children when component content models
require elements
- return an actionable error for unsupported existing-resource
references in JSX
- preserve siblings rendered before expression children
- accept unresolved CSS-variable shorthand expansion and eliminate
dynamic-alt/`htmlFor` audit false positives
- resolve exposed underscore MCP names during focused discovery
- omit unavailable stopped-preview fields and restart stale iterative
previews for external clients
- bound single and responsive screenshot capture, return
`SCREENSHOT_TIMEOUT`, and discard failed reusable browser sessions
- dispose previews when shared MCP runs fail or stdio clients
disconnect, including complete Windows process-tree cleanup
- resolve generated route templates from the CLI-managed preview
workspace during responsive verification
- retry a browser that exits during rendered-audit startup and report
sanitized exit diagnostics with a concrete browser-path recovery action
- materialize complete current session data before preview generation so
route order and project styles cannot collapse from missing lazy
namespaces
- always include CLI and Node.js version fields in generated issue
reports, with explicit legacy-client fallbacks
- document the updated preview and screenshot contracts and regenerate
the CLI manual

The current branch also retains the protections already present on
`main` for dry-run isolation, CSS rewrite truncation metadata,
stderr-only progress, shortcut argument forwarding, strict MCP schemas,
and refreshed conflict diagnostics.

## Technical choices

- MCP command orchestration stays in `mcp.ts`; preview port/retry
behavior lives with `mcp-preview`, and persisted checkpoint storage
lives in `mcp-checkpoint`. Each module owns its matching tests.
- Expression validation lives on the shared fragment input schema,
reuses `listFragmentExpressions`, and emits the same structured
validation issues as prop bindings, so JSX-derived and structured
fragments follow one contract across fragment, collection, and runtime
UI insertion.
- Element-child requirements come from component registry content models
during fragment insertion; no component names are hardcoded in the JSX
parser. `CollapsibleTrigger` metadata now matches its enforced `asChild`
implementation.
- Stopped preview status returns only `running: false`; unavailable
`url`, `pid`, and `mode` fields are optional and omitted from JSON.
- Explicit `preview.start` restarts a stale compatible iterative
preview; managed screenshots keep their optimized refresh path.
- Screenshot operations share one timeout utility. A single capture or
complete responsive batch is bounded after preview readiness;
lower-level CDP deadlines use the same stable error code.
- Failed managed captures close and forget their browser session. A
closed browser session cannot restart itself while cancellation is in
progress, which prevents orphaned Chromium processes.
- One ownership helper disposes one-shot and multi-project MCP hosts on
success or failure. Shared `mcp run` hosts dispose in one `finally`
boundary, long-running MCP hosts dispose on connection close, and
Windows uses `taskkill /T /F` instead of killing only the npm wrapper.
- Framework template readers receive the absolute template directory
captured from the build root, so later async work cannot redirect them
to the linked project's cwd. The same option drives cleanup for React
Router, Remix, and Vike.
- Browser startup gets one bounded retry before a rendered capture batch
fails. Persistent failures report only the exit code, signal, or spawn
error code plus the supported `WEBSTUDIO_BROWSER_PATH` recovery action;
local paths and raw browser output are not exposed.
- CLI and MCP preview share one session materializer that ensures the
canonical Builder namespace set before writing `.webstudio/data.json`.
Serialization rejects incomplete snapshots instead of converting missing
style/instance namespaces into valid-looking empty arrays, and preview
freshness records the post-materialization project version.
- The CLI injects its package version and `process.versions.node`; issue
formatting now keeps both fields visible even for legacy reports that
omit runtime metadata.

## Checklist

- [x] Add regression coverage for every changed behavior
- [x] Demonstrate new regression tests fail without the corresponding
fixes and pass with them
- [x] Run affected package test suites and typechecks
- [x] Run lint, package-boundary, and generated API checks
- [x] Review fixture impact — no fixture inputs or outputs are affected
- [x] Review documentation impact and update the authoritative MCP CLI
manual source/generated output
- [x] Run the complete local agent evaluation suite

## Verification

- `@webstudio-is/project-build`: 2,090 tests passed
- MCP asset query result modes are preserved across create, read, and
update serialization
- complete local agent evaluation suite: all 5 fixtures passed,
1,308,276 aggregate tokens
- `@webstudio-is/react-sdk`: 140 tests passed
- `@webstudio-is/template`: 43 tests passed
- `webstudio` CLI: 956 tests passed
- issue reporter: 5 targeted tests passed
- MCP host disposal regression was demonstrated failing without the
shared ownership boundary and passing with it
- relevant package typechecks passed
- `pnpm lint` passed
- `pnpm check:package-boundaries` passed
- `pnpm check:generated-api` passed
- generated-doc tests, typecheck, sync, and regeneration passed; the
aggregate clean-tree command reports the intentionally changed generated
CLI manual
- stalled single capture, stalled four-viewport capture, startup retry
across a four-viewport batch, sanitized browser-exit diagnostics,
complete preview materialization, post-refresh version tracking,
closed-session restart, Windows process-tree cleanup, and
preview-workspace regressions were demonstrated failing without their
fixes and passing with them

## Known limitations

- External `baseUrl` screenshots remain caller-owned and intentionally
do not regenerate, restart, or stop that external server.

Closes #6034
Closes #6035
Closes #6038
Closes #6042
Closes #6043
Closes #6044
Closes #6045
Closes #6046
Closes #6047
Closes #6048
Closes #6049
Closes #6050
Closes #6051
Closes #6052
Closes #6059
Closes #6060
2026-08-08 22:22:25 +02:00
Oleg Isonen 928867a08b fix: Improve MCP CSS variable safety and state feedback (#6018)
Closes #6017
Ref #6019

## Summary

Improve agent-facing CLI/MCP feedback for CSS variables, mutation
durability, batch operations, and preview freshness.

## Implementation

- Validate structured hex colors as normalized channels and recommend
CSS strings for ordinary 0–255 hex input.
- Return exact CSS variable values while retaining `valueLength` in
compact output for compatibility.
- Preserve optional boolean inputs generically in compact MCP SDK
schemas, including `withUsage`, `verbose`, `overwrite`, and `force`.
- Add multi-variable define/delete examples and document safe batching.
- Derive `meta.session.commitStatus` from the session envelope so reads,
plans, failed/no-op mutations, and durable mutations are unambiguous.
- Retain `committed` on read results for compatibility while reporting
`commitStatus: "not-applicable"`.
- Report preview `stale` state and `renderedProjectVersion`, and avoid
marking a preview fresh when the project changes during regeneration.
- Track WSL2/9P cleanup hardening separately in #6019.

## Todo

- [x] Reject out-of-range structured hex color channels and alpha.
- [x] Add fail-before/pass-after CSS color regression coverage.
- [x] Return exact compact CSS variable values without removing
`valueLength`.
- [x] Clarify mutation durability with `commitStatus`.
- [x] Distinguish failed, unchanged, planned, committed, and read-only
results.
- [x] Distinguish server-backed reads from committed mutations at the
session boundary.
- [x] Preserve optional boolean schemas without a per-field allowlist.
- [x] Add multi-variable define and delete examples.
- [x] Report preview freshness and rendered project version.
- [x] Investigate WSL cleanup behavior and create follow-up #6019.
- [x] Review and update authoritative CLI/MCP documentation.
- [x] Verify fixture impact; no fixture inputs or outputs changed.
- [x] Run affected tests, typechecks, lint, and generated-file checks.
- [ ] Run the complete local agent evaluation suite after explicit
approval.

## Verification

- [x] Regression tests demonstrated failing before their implementation
and passing afterward.
- [x] `pnpm --filter @webstudio-is/project-build test` — 82 files, 2,085
tests passed.
- [x] `pnpm --filter webstudio test` — 57 files, 939 tests passed.
- [x] Project-build and CLI typechecks passed.
- [x] `pnpm lint` passed.
- [x] `pnpm oxfmt --check` passed.
- [x] `pnpm check:generated-api` passed.
- [x] `pnpm check:generated-docs` passed.
- [ ] `pnpm evaluations` — awaiting explicit approval per repository
policy.

## Compatibility and limitations

- `committed` remains available on every session result. For reads it is
`false` and durability does not apply; use `commitStatus:
"not-applicable"`.
- Compact CSS-variable records include both exact `value` and
`valueLength`.
- Structured hex color channels remain normalized `0..1`; CSS color
strings such as `"#2d3748"` are the recommended 0–255 representation.
- WSL2/9P process cleanup is not changed in this PR and is scoped in
#6019.
2026-08-06 02:15:51 +02:00
Oleg Isonen 8e7a8a21aa feat: Report anonymous MCP reliability issues (#6021)
Closes #6020

## Summary

Adds the first end-to-end slice for anonymous, LLM-authored reliability
reports from the CLI and MCP.

The shared report contract captures the generalized user story,
attempted workflow, expectation, actual result, recovery attempts, user
impact, technical context, acceptance criteria, and agent runtime
(client, provider, model, version, and reasoning effort). Guidance
requires removing any user- or project-identifying information before
submission.

## Architecture

- Adds a project-authenticated `api.reports.issue` tRPC mutation as the
reusable product boundary.
- Maps that mutation to the public `report-issue` operation for CLI and
MCP callers.
- Keeps the endpoint directly callable from a future Builder UI through
the existing tRPC client.
- Publishes through a server-side GitHub App installation, so end users
do not need GitHub accounts or tokens.
- Discovers the App installation for the fixed Webstudio reporting
repository instead of requiring deployment-specific installation or
repository configuration.
- Searches for a hidden anonymous technical marker before creation to
avoid obvious duplicates.

## Todo

1. [x] Define a shared anonymous LLM-authored report contract.
2. [x] Include the user story, attempted workflow, expected and actual
behavior, recovery attempts, impact, technical context, and acceptance
criteria.
3. [x] Include the LLM client, provider, model, client version when
known, and reasoning effort.
4. [x] Attach sanitized CLI, Node.js, operating-system, architecture,
execution-mode, and API-contract metadata automatically.
5. [x] Add the Builder-reusable project tRPC mutation.
6. [x] Expose the mutation to CLI and MCP as `report-issue`.
7. [x] Add GitHub App authentication and issue publication.
8. [x] Reduce deployment configuration to the App ID and private key.
9. [x] Add marker-based duplicate detection.
10. [x] Add guidance for explicit user-requested reports and qualifying
repeated-friction reports.
11. [x] Review and simplify repeated GitHub request handling and public
protocol exports.
12. [ ] Add server-side throttling and harden concurrent duplicate
creation.
13. [x] Configure and install the reporting GitHub App in deployed
environments.
14. [x] Exercise the deployed endpoint with a controlled test report.
15. [ ] Run the complete agent evaluation suite after explicit approval.
16. [x] Review documentation impact and update the generated MCP
documentation.

## Verification

- Protocol issue-report contract tests: passed (3)
- Builder GitHub reporting tests: passed (4)
- Builder API router tests: passed (30)
- CLI schema, command-doc, MCP, and project-session tests: passed
- Protocol, HTTP client, project-build, CLI, and Builder typechecks:
passed
- Root lint: passed
- Generated API and documentation checks: passed
- New and changed regression tests were demonstrated failing against the
previous behavior, then passing after implementation
- No fixture inputs or outputs are affected
- Agent evaluations were not run because they require explicit approval

## Deployment notes

The Builder only needs `GITHUB_ISSUE_REPORT_APP_ID` and
`GITHUB_ISSUE_REPORT_PRIVATE_KEY`. The server discovers the App
installation for `webstudio-is/webstudio` and creates a short-lived
installation token. The App needs repository metadata read access and
Issues write access.

## Known limitations

Duplicate detection currently uses GitHub issue search and can race when
identical reports arrive concurrently. The endpoint is
project-authenticated, but dedicated server-side throttling remains a
follow-up before production rollout. No Builder UI is included yet; the
tRPC boundary is intentionally ready for one.
2026-08-06 00:32:51 +02:00
Oleg Isonen 6385e290c6 feat: Add syntax highlighting to Code Text (#6007)
Closes #4119

Supersedes #5999 after GitHub closed it during the head-branch rename.

## Approach

Code Text uses one optional public highlighting boundary:

- `@webstudio-is/sdk-components-react/code-text` → `src/code-text.tsx`

The ordinary `components` entry remains Shiki-free. Canvas loading,
metadata options, theme variables, and the plain semantic `<code>`
component remain private implementation details.

**Code** uses the code editor. **Language** and **Theme** use normal
bindable selects. Fixed selections generate direct imports for only the
per-page language/theme union; plaintext imports no grammar. Bound
selections generate Shiki's lazy language/theme registries.

SSR and client rendering share the same factory. Missing bound assets
suspend the first render while SSR resolves the selected modules. SSG
now waits for Suspense before serializing HTML. During hydration the
completed server markup stays in place while the browser fetches only
the selected language and theme. Later client-side binding changes load
only the new selections and keep the same `<code>` node and focus.

Highlighted output preserves the semantic root, ref, author
classes/styles, whitespace, escaped source, and legacy children/native
`lang` behavior. Shiki's implicit tab stop is disabled. A color-only
transformer strips theme typography so asynchronous highlighting cannot
change text geometry.

## Bundle impact

Measured from same-commit production React Router + Cloudflare builds
containing one JavaScript / GitHub Light Code Text instance. Sizes are
the sum of emitted JavaScript files; gzip is the sum of each file
compressed independently.

| Worker output | Files | Raw | Sum gzip |
| --- | ---: | ---: | ---: |
| Fixed selections | 3 | 1,341,851 B | 288,342 B |
| Bound selections | 310 | 10,372,112 B | 1,872,273 B |
| Increase | +307 | +9,030,261 B (8.61 MiB) | +1,583,931 B (1.51 MiB) |

The bound browser build also emits the catalog as lazy chunks:
+9,012,594 raw bytes / +1,578,779 sum-gzip bytes across the complete
deployed output. Those chunks are not all delivered on page load. The
production SSG regression verifies 100+ catalog chunks are emitted while
the HTML directly module-preloads only one shared runtime chunk; the
browser fetches the runtime-selected language/theme modules on demand.

## Tradeoffs and compatibility

- Instances with both highlighting properties unset keep the legacy
plain renderer.
- Static highlighting still requires both **Language** and **Theme**.
- Binding either selection includes that catalog in deploy output
because its runtime value is not known at publish time.
- The syntax-highlighting property is `language`; legacy native HTML
`lang` remains preserved.
- Invalid or unavailable runtime selections render as plain code.
- Fixed selections retain the smallest publish output; bound selections
trade deploy/worker size for runtime flexibility.

## Todo

- [x] Restore the specialized Code Text component and semantic plain
fallback
- [x] Use a code editor for Code and bindable selects for Language and
Theme
- [x] Persist JavaScript / GitHub Light template defaults
- [x] Collapse highlighting to one public `./code-text` entry
- [x] Keep canvas loaders and option/theme helpers private
- [x] Generate direct imports for fixed selections and omit plaintext
grammar
- [x] Generate lazy catalogs when Language or Theme is bound
- [x] Preserve highlighted SSR markup during hydration without
node/focus movement
- [x] Support Suspense-aware highlighted SSG output
- [x] Preserve escaping, accessibility, legacy children, and native
`lang`
- [x] Measure fixed versus bound Cloudflare worker output
- [x] Deep-review correctness, DRYness, package surface, and obsolete
binding infrastructure
- [x] Review documentation impact and update Code Text documentation
- [x] Run focused tests, typechecks, production builds, formatting, and
staged diff review
- [ ] Fixture pipeline — blocked during sync because the current API
rejects this local CLI version as incompatible; no fixture output
changed

## Verification

- [x] New/changed regressions were demonstrated failing without their
behavior and passing after restoration
- [x] SDK Code Text metadata, rendering, hydration, and bundle tests: 17
passed
- [x] Component registry tests: 3 passed
- [x] CLI asset collection and full prebuild suite: 59 passed
- [x] SDK components, component registry, SDK, and CLI typechecks
- [x] SDK components declaration and production builds
- [x] Component registry production build
- [x] Production Vite + Vike prerender emits highlighted bound SSG HTML
with one doctype
- [x] Dynamic catalog chunks remain outside the SSG page's direct
preload set
- [x] Documentation reviewed and updated
- [x] Formatting and staged diff checks
2026-08-04 18:25:36 +02:00
Oleg Isonen 74d88a3fb2 fix: Resolve structured Asset paths in queries (#5995)
## Summary

Resolve portable relative Asset paths stored in Markdown frontmatter and
structured JSON properties to canonical runtime Asset URLs inside the
content engine.

The authored source remains unchanged. Asset resource consumers now
receive canonical URLs without Image, CSS, JSON-LD, or other
binding-specific transformations.

## Root cause

Markdown body links already recorded relative Asset references for
runtime rewriting, but structured values bypassed that pipeline.
Browsers therefore resolved values such as `./assets/hero.png` against
the page URL instead of the source document folder.

## Implementation

- Recursively discover unique structured Asset references using the
existing canonical path resolver and the platform URL parser.
- Store optional `assetValueReferences` in content artifacts and runtime
projections.
- Include referenced Assets in publication/runtime dependency collection
and validation.
- Resolve values before filtering, sorting, and projection in normal and
document-graph queries.
- Rewrite materialized query items at runtime without mutating canonical
documents or compiled rows.
- Merge authored query parameters and fragments with canonical Asset
URLs.
- Keep queries whose filter/sort semantics depend on runtime Asset URLs
out of compile-time materialization.
- Add an explicit content-compilation cache generation so pre-feature
artifacts are recompiled.
- Refresh fixture bundle versions and the generated asset-query runtime.
- Document relative references in Markdown frontmatter and JSON content.

## Checklist

- [x] Add structured reference discovery for nested objects and arrays
- [x] Support Markdown frontmatter and JSON document properties
- [x] Preserve URL query strings and fragments
- [x] Thread references through build/runtime artifacts and publication
dependencies
- [x] Resolve normal, materialized, and document-graph query results
- [x] Apply resolved values to filtering, sorting, and projection
- [x] Preserve compatibility with artifacts that omit the optional field
- [x] Add fail/pass regression coverage
- [x] Run package tests, typechecks, lint, and generated API checks
- [x] Regenerate and build all fixtures
- [x] Review documentation impact and update the Assets guide

## Verification

- `pnpm --filter @webstudio-is/content-engine test` — 269 passed
- `pnpm --filter @webstudio-is/content-engine typecheck` — passed
- `pnpm --filter @webstudio-is/asset-uploader test` — 184 passed
- `pnpm --filter @webstudio-is/asset-uploader typecheck` — passed
- `pnpm --filter @webstudio-is/protocol test` — 88 passed
- `pnpm lint` — passed
- `pnpm check:generated-api` — passed; generated files clean
- `pnpm --filter webstudio build:content-runtime && pnpm -r
fixtures:build` — passed for all fixtures
- Assets documentation — internal links resolve and `git diff --check`
passes
- Regression proof: neutralizing structured discovery/replacement caused
8 expected failures across source compilation, standard queries,
document-graph queries, and materialized queries; restoring the
implementation passed all tests.
- `pnpm evaluations` — not run because explicit approval is required.

## Compatibility

The artifact field is optional, so older artifacts continue to parse and
execute. Existing absolute, protocol-relative, root-relative,
fragment-only, query-only, external-scheme, malformed, missing, and
ambiguous values remain unchanged.
2026-08-03 23:11:59 +02:00
Oleg Isonen 1f4095cf9a fix: Improve generated MCP documentation (#5991)
## Summary

- render the generated MCP version as visible body content immediately
below the page heading
- keep the GitBook navigation label stable as `MCP`
- stamp only the CLI version placeholder during release publication,
leaving Markdown content owned by the template
- remove empty `{}` blocks from the rendered MCP argument examples while
preserving meaningful examples and complete structured JSON data

## Root cause

GitBook uses the `SUMMARY.md` link label as the published page heading
and suppresses the Markdown H1 from the document body. The generated
version was therefore present in source but absent from the rendered
page.

The release workflow also coupled version stamping to the complete
rendered Markdown line. It now replaces and verifies only the standard
`0.0.0-webstudio-version` placeholder.

## Verification

- live page structure inspected: published H1 is `MCP`
- MCP manual regression test demonstrated failing before empty argument
blocks were filtered, then passing after the fix
- release placeholder stamping simulated against the generated page
- `pnpm --filter webstudio exec vitest run src/commands/man.test.ts`
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter webstudio typecheck`
- `pnpm check:generated-docs`
- `pnpm lint`
- `pnpm oxfmt --check packages/project-build/src/mcp.ts
packages/cli/src/commands/man.ts packages/cli/src/commands/man.test.ts`
- `git diff --check`

Fixture inputs and outputs are unaffected.

## Todo

- [x] Generate the release version as visible GitBook body content.
- [x] Decouple release stamping from the rendered Markdown content.
- [x] Remove empty MCP argument example blocks.
- [x] Validate generated output and regression coverage.
- [ ] Merge this pull request.
- [ ] Publish the latest released revision to the `docs` branch.
- [ ] Verify the version on the live GitBook page.

Ref #5986
2026-08-03 20:21:35 +02:00
Oleg Isonen e6a29b5d36 fix: Show released CLI version in MCP docs (#5990)
## Summary

- make the MCP page generator render the CLI package version directly in
the page title
- publish `Webstudio MCP v<release>` from the exact generated page in
each successful release
- merge each successful release tag into the publication-only `docs`
branch
- serialize publication and ignore reruns or older release tags already
contained in `docs`

## Root cause

The first publication design advanced `docs` to the release commit
without putting the CLI package version into the generated page. The
repository uses `0.0.0-webstudio-version` until release time, so the
release workflow now replaces that existing version placeholder with the
successful tag version before publishing.

## Todo

- [x] Generate the CLI version in the MCP page title.
- [x] Publish the generated page from the exact release revision.
- [x] Make publication idempotent and prevent version downgrades.
- [x] Validate generation, formatting, linting, and release-branch
behavior.
- [ ] Merge this pull request.
- [ ] Publish `0.285.0` as the initial version on the existing `docs`
branch.

## Verification

- `pnpm docs:mcp:test`
- `pnpm docs:mcp:typecheck`
- `pnpm check:generated-docs`
- `pnpm lint`
- `pnpm oxfmt --check`
- `git diff --check`
- isolated Git simulation for release merge, exact generated-page
selection, version replacement, and rerun detection
- regression tests demonstrated failing without version rendering and
validation, then passing after restoration

Ref #5986
2026-08-03 18:32:59 +02:00
Oleg Isonen 99371187dc docs: Move product documentation into Builder (#5987)
## Summary

- move the complete GitBook documentation tree and assets into Builder
under `/docs`
- generate the exhaustive MCP page from the local CLI source in the same
change
- publish GitBook from a release-gated `docs` branch containing the
exact successfully released revision
- run MCP documentation tests, typechecking, and stale-output checks
with the existing generated-docs check
- remove links to the Community Discussions repository

## Technical choices

- Import the current documentation as a linear snapshot so the branch
satisfies Builder's required-linear-history protection.
- Keep the Markdown wrapper separate from the TypeScript generator at
`scripts/docs/mcp-page.md`.
- Generate through `packages/cli/local.js` so CLI guidance and its
documentation are reviewed together.
- Let `check:generated-docs` reject CLI changes that leave the committed
MCP page stale.
- After both GitHub release creation and package publication succeed,
fast-forward the read-only `docs` branch to the immutable release tag
commit. A divergent update fails instead of publishing a different
revision.
- Configure GitBook to publish `/docs` from the `docs` branch, not
directly from `main`.
- Exclude `/docs` from the repository-wide `oxfmt` scan so imported
GitBook Markdown retains its established formatting; generator tests and
stale-output checks still validate the MCP page.
- Exclude `transcripts/` and GitHub Discussions from the migration.

## Todo

- [x] Import documentation, navigation, includes, and assets under
`/docs`.
- [x] Generate the MCP page from the local CLI source.
- [x] Move MCP generation and validation into Builder.
- [x] Require MCP documentation updates alongside CLI changes.
- [x] Publish released revisions through a read-only `docs` branch.
- [x] Remove Community repository links from Builder and the migrated
docs.
- [x] Preserve and verify a final Community Git bundle locally.
- [ ] Merge this pull request.
- [ ] Let the next successful release create or advance the `docs`
branch.
- [ ] Repoint GitBook Git Sync to `webstudio-is/webstudio`, branch
`docs`, root directory `/docs`.
- [ ] Verify `docs.webstudio.is`, navigation, assets, anchors, and the
MCP page in production.
- [ ] Delete `webstudio-is/webstudio-community`.

## Verification

- `pnpm check:generated-docs`
- `pnpm lint`
- `pnpm oxfmt --check`
- `git diff --check origin/main...HEAD`
- generator regression test demonstrated failing when the GitBook
wrapper behavior was removed, then passing after restoration
- no fixture inputs or outputs are affected

## Known limitation

All documentation merged after the previous release waits for the next
successful release before GitBook publishes it. This deliberately keeps
the public MCP reference aligned with an installable CLI revision.

Ref #5986
2026-08-03 17:55:50 +02:00
Oleg Isonen 310166343b first working version of designer in turborepo 2022-07-06 11:36:59 +02:00
Sara Vieira c19406ff56 better testing cases 2022-06-26 14:25:49 +02:00
Sara Vieira 5b6ee08a14 add test cases 2022-06-25 16:58:40 +02:00
Oleg Isonen 9510684475 breakpoints test cases 2022-05-13 14:57:33 +02:00
Oleg Isonen 0123c7598d Create test-cases.md 2022-03-13 10:21:52 +00:00