Closes#5919
Ref #5920
## Outcome
Extend the existing Assets system resource with optional queries for
Markdown, JSON, and standard file metadata. Unconfigured Assets
resources retain the existing fetch-all behavior and activate none of
the content-query infrastructure.
Files are the authoring model. A standalone content engine converts an
immutable snapshot of only the files required by configured queries into
one bounded, deterministic runtime database. Builder preview, SSR, and
SSG use the same query semantics without requiring a database service
for a published site.
## Final architecture
```text
Markdown / JSON files
│
▼
ContentSource adapter
(FS or Webstudio Assets)
│ immutable snapshot
▼
ContentCompilationPlan
(union of configured query requirements)
│
▼
ContentCompiler
(parse, normalize, discover fields, enforce budget)
│
▼
ContentArtifact
(bounded, deterministic, immutable JSON)
│
▼
ContentDatabase
(filter, OR groups, sort, paginate, project, read content)
│
├── Builder preview
├── SSR runtime
└── SSG build only
```
The authoring/storage layer owns mutable files. The content engine
consumes immutable snapshots and produces immutable, disposable
artifacts.
Query authoring is a separate OpenAPI-driven path:
```text
Assets REST OpenAPI operation
+ standard request JSON Schema
+ dynamic field paths
│
▼
Generic OpenAPI adapter
│ resolved schemas, controls, paths, and transport mapping
▼
Generic query-builder core and React renderer
│ arbitrary provider-defined value
▼
Assets REST request adapter
│
▼
{ query: value }
```
The selected standard OpenAPI operation is the only source of query UI
semantics. Request parameters and JSON Schema describe the editable
data; the generic renderer derives controls and owns layout.
### Activation and compilation
- The compiler does not index every asset. It combines all reachable
configured Assets-resource queries into one `ContentCompilationPlan`.
- Static constraints such as folder, path, and file type select
candidate files. Dynamic values such as a route-bound slug cannot reduce
the build-time candidate set.
- The plan contains the union of fields used by filtering, sorting,
routing, projection, excerpts, and requested full content.
- Documents shared by multiple resources are stored once in one
deployment-wide runtime database.
- Markdown/frontmatter, JSON properties, excerpts, and full bodies are
prepared only when required.
- No configured content query means no parsing, artifact, or shipped
runtime dependency. Fresh generated apps need no content-runtime
cleanup; incremental generation removes stale integration if a
previously configured query is deleted.
### Runtime database limit
- `CONTENT_DATABASE_MAX_BYTES` limits the complete serialized runtime
database, not source files, images, or unrelated assets.
- The default is `500 * 1024` bytes and is presented as 500 KiB.
- Counted data includes indexed metadata, frontmatter/JSON properties,
excerpts, the field catalog, serialization overhead, and full Markdown
bodies only when requested.
- Prepublish validation builds and serializes the exact database that
will be attached to the immutable build, so it knows the unbounded size,
bounded size, included documents, omitted documents, and affected
resources.
- Overflow never stores partial documents. Candidates are ordered by
`createdAt` descending, canonical path ascending, and stable document ID
ascending. Missing dates follow dated documents. A document that does
not fit is skipped while smaller later documents are still considered.
- Exceeding the limit warns but does not block publishing.
- Builder preview can report exact bounded versus unbounded results when
dynamic expressions have concrete preview values. Prepublish warnings
remain conservative for future route values.
### Snapshot consistency
The source contract guarantees a consistent compilation, while adapters
provide the mechanism:
- Webstudio Assets captures asset metadata and immutable content
references.
- Filesystem sources capture file identity and verify files did not
change before accepting the artifact.
- A changed source retries once, then fails clearly instead of producing
a mixed-revision database.
- This requires no universal snapshot service, source-tree copy, or new
versioned-asset infrastructure.
### Package ownership
- `@webstudio-is/content-engine`: runtime artifact schema, query
execution, field catalog, compact runtime statistics.
- `@webstudio-is/content-engine/compiler`: source snapshots, compilation
plans, Markdown/JSON parsing, deterministic compilation, byte
accounting, and compiler diagnostics. This entry exists to keep
Remark/YAML/future MDX dependencies out of runtime bundles.
- `@webstudio-is/query-builder`: resolves standard OpenAPI operation
schemas into an internal provider-neutral editor model, validation,
mutations, and source serialization. It owns no Assets,
provider-authored UI contract, or fixed REST-query semantics.
- `@webstudio-is/query-builder/react`: container-neutral schema renderer
whose layout is renderer policy. It remains separate from the content
engine.
- SDK: Webstudio Assets/Resource domain models and mapping Webstudio
expressions into generic content requirements.
- Protocol: external Assets REST/OpenAPI transport contracts.
- Asset uploader/repository: asset and folder mutations, generic
metadata, immutable byte references, and storage access. Its hosted
adapter supplies Webstudio Assets snapshots and parsed-metadata cache
persistence to the content engine; parsing, artifact creation, and query
semantics remain engine-owned.
- Builder and generated-app integrations decide when to compile, cache,
embed, load, or discard a database.
### Runtime lifecycle
- Builder caches immutable `ContentDatabase` instances by source
revision plus compilation-plan hash and coalesces concurrent
compilation. Matching operations reuse the same instance; field
discovery uses a dedicated minimal all-properties plan that excludes
excerpts and bodies.
- SSR attaches the validated bounded database to the build and includes
the small runtime only when required.
- SSG uses the database only while generating pages and omits it and the
runtime from final output unless a future runtime-query feature requires
them.
- Local development compiles from the filesystem and uses identical
query semantics.
- Published pages query their local artifact; they do not call Builder
REST endpoints.
- Published artifacts need no external index store, invalidation,
garbage collection, SaaS content proxy, new bucket, or new environment
variable.
## Implemented foundation
These existing parts remain useful and should be moved behind the final
ownership boundaries rather than reimplemented.
- [x] Upgrade the existing Assets system resource in place and preserve
unconfigured fetch-all behavior.
- [x] Implement typed filtering, nested boolean groups, deterministic
sorting, pagination, projections, dynamic values, and content hydration.
- [x] Add Markdown frontmatter, excerpt, JSON-property, and
field-catalog extraction.
- [x] Add reusable query-control implementations and a separately
imported React UI as a foundation for the final OpenAPI-driven contract.
- [x] Add Assets query authoring and dynamic field discovery as existing
behavior to migrate into the Assets OpenAPI operation.
- [x] Add synchronized visual and editable expression authoring.
- [x] Unify Assets REST operations under `/rest/assets/*`.
- [x] Define authenticated collection, item, content, upload, folder,
query, field-catalog, and OpenAPI operations.
- [x] Support Builder cookie authentication and public API token
authentication.
- [x] Add Builder preview, MCP/CLI operations, SSR execution, SSG path
generation, and conditional activation.
- [x] Add revision-keyed parsed metadata persistence for the hosted
Builder.
- [x] Add focused query, API, authorization, Builder, CLI, SSR, SSG,
migration, and fixture coverage.
## Remaining implementation plan
### 1. Establish the content engine
- [x] Rename the generic `@webstudio-is/asset-resource` implementation
to `@webstudio-is/content-engine`.
- [x] Move existing behavior instead of creating a parallel
implementation.
- [x] Keep the primary entry runtime-only and add only the compiler
entry needed for bundle separation.
- [x] Remove the old package after all consumers migrate.
- [x] Ensure the engine has no Builder, SDK, asset-uploader, PostgreSQL,
R2, or deployment-target dependency.
### 2. Define source snapshots and compilation plans
- [x] Introduce `ContentSource.openSnapshot()` and a revision-bearing
`ContentSourceSnapshot`.
- [x] Include canonical path, stable document ID, content type,
immutable content reference, size, and optional `createdAt`.
- [x] Implement Webstudio Assets and filesystem snapshot guarantees
using their adapter-specific mechanisms.
- [x] Introduce `ContentCompilationPlan` for the union of reachable
configured resource requirements.
- [x] Separate statically selectable candidates from dynamic runtime
predicates.
- [x] Add mutation-during-compilation tests for replacement, deletion,
folder movement, and filesystem changes.
### 3. Separate storage from content processing
- [x] Keep upload, replacement, folders, byte storage, revisions, and
generic metadata in the Assets repository/uploader.
- [x] Move parsing, excerpts, field discovery, query execution, and
artifact creation into the content engine.
- [x] Remove content-index coordination from asset mutation business
logic.
- [x] Preserve original source `createdAt` across content replacement
and expose it as optional queryable metadata.
- [x] Keep hosted parsed-document persistence behind the content
compiler's optional cache interface.
### 4. Build deterministic compilation
- [x] Move the existing Markdown, frontmatter, JSON, canonicalization,
hashing, document-building, and tests into the compiler.
- [x] Keep frontmatter behind a dedicated module and continue using the
existing Remark/Micromark pipeline.
- [x] Parse only candidate files and only the tiers required by the
compilation plan.
- [x] Deduplicate documents and fields required by multiple resources.
- [x] Define a versioned, deterministic `ContentArtifact` with integrity
information.
- [x] Keep verbose parse and omission diagnostics outside the runtime
artifact.
- [x] Reject incompatible artifact versions cleanly.
### 5. Enforce the deployment-wide database budget
- [x] Replace the old asset-index limit with
`CONTENT_DATABASE_MAX_BYTES`, defaulting to 500 KiB.
- [x] Have applications read configuration and pass the byte limit into
the compiler.
- [x] Measure the exact serialized database, including selected bodies
and overhead.
- [x] Implement deterministic skip-and-continue packing of complete
documents.
- [x] Return compiler diagnostics with unbounded/bounded bytes,
included/omitted counts, omitted identities, largest contributors, and
affected resources.
- [x] Expose only compact artifact statistics at runtime through
`ContentDatabase.getStats()`.
- [x] Add exact boundary, deterministic ordering, deduplication, and
oversized-document tests.
### 6. Finalize the runtime database
- [x] Define `ContentDatabase` over an immutable artifact.
- [x] Preserve filtering, nested AND/OR groups, sorting, offset/limit,
projection, field references, and content retrieval.
- [x] Expose `getFieldCatalog()` and compact `getStats()`.
- [x] Keep Markdown/YAML/compiler dependencies out of the runtime entry.
- [x] Add execution guards and maximum-size runtime benchmarks.
### 7. Make query authoring OpenAPI-driven
The selected standard OpenAPI operation is the complete query-authoring
contract. The query builder resolves parameters and request-body JSON
Schema, derives its internal editor model, and applies renderer-owned
layout policy. It must not embed or fetch a second capabilities/UI
language through `x-webstudio-query` or another side channel.
- [x] Remove `x-webstudio-query` and all vendor query-definition
metadata from the Assets OpenAPI operation.
- [x] Resolve local OpenAPI references and derive the editable request
value, defaults, validation, labels, choices, arrays, objects, and
unions solely from standard operation parameters and request-body JSON
Schema.
- [x] Represent dynamic Assets field paths with standard JSON Schema
vocabulary in the `queryAssets` request schema.
- [x] Keep the normalized editor representation internal to
query-builder; do not expose another provider-authored capabilities
contract.
- [x] Remove provider-controlled layout metadata such as `group`; make
compact scalar packing and all other layout renderer policy.
- [x] Keep Webstudio expression binding as a consumer/editor concern
layered over schema values, not part of the OpenAPI contract.
- [x] Preserve the existing Assets filter, sort, output selection,
full-content/range selection, limit/offset, editable source, and
configured-field behavior.
- [x] Keep the Assets resource adapter responsible for serializing the
edited query into its request body.
- [x] Remove the obsolete Assets query-capabilities endpoint, URL, and
UI-side capability parsing.
- [x] Add a non-Assets OpenAPI proof with differently named parameters
and no vendor metadata.
- [x] Verify the generic packages contain no Assets imports, fixed
Assets property contract, or provider-authored layout model.
- [x] Update focused core, React, SDK, protocol/OpenAPI, Builder, and
source round-trip tests.
### 8. Manage ContentDatabase lifecycle in Builder
- [x] Cache databases by source revision plus normalized
compilation-plan hash.
- [x] Coalesce concurrent compilation requests.
- [x] Reuse cached database instances across field discovery,
autocomplete, query preview, and response preview when their normalized
compilation plans match.
- [x] Replace it when source revision or query requirements change.
- [x] Preserve the optional revision-keyed hosted parsed-document cache
without making it a source of truth.
- [x] Show exact preview warnings when concrete values reveal missing
bounded results.
### 9. Materialize and verify immutable builds
- [x] Compile and serialize the exact deployment-wide database during
prepublish validation.
- [x] Attach that validated bounded database to the immutable build
without rebuilding it from later asset state.
- [x] Show non-blocking prepublish warnings with database bytes,
omissions, and potentially affected resources.
- [x] Avoid claiming exact future route matches when dynamic values are
unknown.
- [x] Include the database/runtime only when a reachable configured
query requires them.
- [x] Ensure detail resources requesting Markdown content include all
statically eligible bodies needed by dynamic routes.
### 10. Integrate all targets
- [x] SSR: load the build's bounded database and conditionally include
the small runtime.
- [x] SSG: query the same database during generation, then omit
database/runtime output when no runtime query is needed.
- [x] Local/CLI: compile from the filesystem source with the same
semantics.
- [x] Preserve overview pagination, alternative route fields,
draft-as-user-data behavior, and full article rendering.
- [x] Verify ordinary Assets and fresh query-free sites perform no
parser, artifact, content-runtime dependency install, or cleanup work.
### 11. Correct API and package ownership
- [x] Move external Assets REST/OpenAPI contracts to
`@webstudio-is/protocol`.
- [x] Keep Webstudio domain models and Assets system-resource
configuration in the SDK.
- [x] Keep both cookie and token authentication behavior on the unified
Assets REST surface.
- [x] Remove pass-through route wrappers/reexports that add no transport
behavior.
- [x] Keep published runtime queries local to the artifact rather than
routing them through Builder REST.
### 12. Remove superseded deployment infrastructure
- [x] Remove external deployment-index upload, fetch, invalidation,
cleanup, and garbage-collection code.
- [x] Remove the SaaS asset-origin adapter requirement; close
webstudio-is/webstudio-saas#652 as superseded because it has no
independent purpose.
- [x] Remove index-bucket compatibility configuration and avoid adding
Infisical variables.
- [x] Remove obsolete external-index paths, package dependencies,
routes, scripts, and tests.
- [x] Consolidate unreleased intermediate migrations where safe and
retain only schema required by the final hosted cache/API design.
### 13. Verification and release readiness
- [x] Move and preserve existing tests while changing ownership; add new
tests only for changed contracts and behavior.
- [x] Run content-engine compiler/runtime tests, query-builder tests,
SDK/protocol/API tests, Builder tests, CLI tests, and affected
typechecks.
- [x] Run formatting, generated-file idempotence, fixtures, clean
Supabase-compatible migrations, E2E shards, and complete agent
evaluations.
- [x] Verify SSR and SSG conditional inclusion and query-free generated
package manifests.
- [x] Run the five-article MCP blog workflow: authoring, field
discovery, overview, dynamic detail routes, full Markdown rendering,
publishing, and published-site verification.
- [x] Run a maximum-budget database test and a
mutation-during-compilation test.
- [x] Perform a final architecture, security, API compatibility,
migration, bundle, correctness, and DRY review.
- [x] Reconcile every checkbox and replace the verification section with
results from the final architecture before release.
## Future MDX work
MDX is intentionally separate from this release.
- The compiler contract should allow a future build-only MDX parser.
- MDX compilation must not enter the published runtime.
- Embedded JSX must use an explicit compiled representation rather than
runtime source evaluation.
- Webstudio components, tokens, styles, and component allowlists remain
Webstudio integration concerns.
- The future MDX work should reuse `ContentArtifact` and
`ContentDatabase` where practical.
## Superseded design
The following earlier design choices are no longer the target
architecture:
- GROQ or GraphQL as the Assets query language.
- One external or long-lived index object per resource/query.
- A deployment index fetched by the Worker from SaaS/CDN infrastructure.
- Lazy production reads of source Markdown bodies.
- A separate index bucket, asset origin, invalidation service, or
garbage collector.
- A 4 MiB published index limit.
- Rebuilding an old build's database from the current mutable asset set.
- Bundling query infrastructure into projects without a configured
content query.
## Verification baseline
The embedded runtime-database architecture previously passed the checks
below. The OpenAPI-driven query-authoring change additionally passed:
- Query-builder core: 4 files / 19 tests.
- Query-builder React: 1 file / 7 tests.
- SDK Assets source/config: 1 file / 7 tests.
- Protocol Assets OpenAPI: 1 file / 9 tests, including independent
OpenAPI validation.
- Builder Assets OpenAPI description: 1 file / 5 tests.
- Query-builder, React, SDK, protocol, and Builder typechecks.
- Package-boundary validation and formatting of every affected file.
Local verification baseline:
- Content engine: 15 files / 104 tests.
- Asset uploader: 24 files / 175 tests.
- Builder: 204 files / 2,423 tests.
- CLI: 54 files / 866 tests.
- Complete workspace typecheck, formatting, lint, package boundaries,
generated fixtures, and fixture builds.
- The complete migration history applied successfully to a fresh
Supabase-compatible database, including every migration after the
consolidated Assets migration.
- Builder production build and full local E2E suite passed.
- Complete agent evaluations passed: authenticated page (170,859 ms),
design input (207,101 ms), and font assets (67,050 ms).
- Final review additionally verified correct REST edit/view/build
permits, cookie and public-token read paths, immutable snapshot retries,
exact database budgeting, query-free dependency omission, and SSR/SSG
conditional runtime inclusion.
Final staging verification passed against the embedded runtime-database
architecture: MCP uploaded and updated five Markdown articles,
discovered dynamic frontmatter fields, previewed a five-item sorted
overview and one full-body slug query, updated both Assets resources,
found zero binding issues, rendered `/blog` and `/blog/dynamic-routes`
at HTTP 200, published build version 266, and verified both published
routes at HTTP 200 with the expected article metadata and Markdown body.
The workflow also found and fixed offset-form asset timestamps failing
canonical datetime validation and made MCP `preview.start({ port: 0 })`
correctly select an available local port.
## Release constraints
- No new SaaS content service, bucket, or environment variable.
- No published-site database dependency.
- No parser/compiler dependency in query-free sites or runtime-only
bundles.
- The validated content database is immutable build output.
- Publishing warnings do not block deployment.
- The selected standard OpenAPI operation and its request JSON Schema
are the only source of query UI semantics.
## Summary
- Keep the shared content-model validator strict during normal Builder
authoring.
- Report HTML and component nesting violations as publish warnings
instead of blocking legacy projects.
- Allow paste operations to retain violations already contained inside
legacy fragments and surface them as warnings.
- Continue rejecting new violations introduced at the paste destination
boundary.
- Remove obsolete empty content models from CodeText and Video so their
runtime-supported, HTML-valid children pass validation.
## Root cause
The new pre-publish audit applied current authoring constraints to
historical project trees. Some legacy structures, including old
Accordion triggers containing an h3 inside a button, were created by
earlier Webstudio templates and cannot satisfy the current HTML model
without migration. Blocking publish or paste would make untouched legacy
content unusable.
CodeText and Video also had metadata that rejected children their
renderers intentionally support. Those verified metadata mismatches are
corrected independently of the compatibility policy.
## Behavior
- New invalid nesting remains prevented by normal authoring operations.
- Existing invalid nesting produces a Publish warning and does not abort
publishing.
- Webstudio instance, HTML, JSX, and Webflow paste retain
fragment-internal violations and show a paste warning.
- Paste still validates the destination and cannot introduce a new
invalid parent-child relationship.
- Resource integrity and missing audit inputs still prevent publishing.
## Verification
- [x] Legacy button containing h3 reports a publish warning
- [x] Legacy invalid subtree pastes successfully with a warning
- [x] New paste destination violations remain rejected or use the
closest valid target
- [x] Valid legacy CodeText children pass validation
- [x] Valid Video source and track children pass validation
- [x] Project-build matcher and insert-target tests
- [x] Builder instance, HTML, JSX, and Webflow paste tests
- [x] Project-build and Builder typechecks
- [x] Focused oxlint and formatting checks
## What changed
- mark published Head Slot descendants with an internal context
- portal JSON-LD scripts nested in Head Slot into `document.head`
- preserve page-flow rendering for JSON-LD used outside Head Slot
- add browser-DOM regression coverage for both placements
## Root cause
React hoists `title`, `meta`, and `link` elements rendered by Head Slot,
but it does not hoist inline `script type="application/ld+json"`
elements. As a result, JSON-LD nested in Head Slot remained under the
page body.
## Impact
JSON-LD placed in Head Slot now appears in the document head after the
published page mounts. JSON-LD intentionally placed outside Head Slot
remains in its original page position.
## Validation
- `pnpm --filter @webstudio-is/sdk-components-react test`
- `pnpm --filter @webstudio-is/sdk-components-react typecheck`
- targeted oxlint on changed files
- `pnpm --filter @webstudio-is/sdk-components-react build`
- `pnpm build`
## Summary
This update adds new MCP and CLI capabilities for project auditing,
structured data, project-specific agent instructions, focused project
search, safer bulk editing, image descriptions, font discovery, visual
verification, and external registry inspection.
## New Capabilities
### Audit projects
- Run a single `audit` command across accessibility, SEO, security,
assets, styles, and performance.
- Limit an audit to selected scopes, pages, or severity levels.
- Receive stable findings with locations and suggested fixes.
- Run rendered audits across desktop, mobile, and Builder breakpoint
widths.
- Detect horizontal overflow, broken images, oversized images, eager
below-fold images, render-blocking resources, and legacy font formats.
- Identify unused assets, tokens, CSS variables, local styles, and
breakpoints.
- Identify duplicate token declarations.
- Detect accessibility issues involving names, labels, headings,
landmarks, IDs, keyboard focus, ARIA, iframes, and autoplaying media.
- Detect SEO issues involving titles, descriptions, language, social
images, and structured data.
- Detect unsafe new-tab links and write resources exposed during page
rendering.
### Search an entire project
- Search project text, labels, props, resources, assets, and styles
through `search-project`.
- Find relevant records without downloading and reparsing the full
project snapshot.
- Scope searches to the relevant project-data areas.
### Add validated JSON-LD
- Add structured data with a dedicated JSON-LD component.
- Place JSON-LD inside the page Head Slot.
- Edit JSON in a purpose-built code editor.
- Prevent structurally invalid JSON-LD from being saved.
- Validate Schema.org types, properties, and values.
- Find invalid, missing, unknown, deprecated, or incompatible structured
data through the SEO audit.
- Let agents discover the correct JSON-LD workflow without using page
custom metadata or a generic HTML embed.
### Configure project-specific agent instructions
- Add AI-agent instructions from the new **Agents** section in Project
Settings.
- Synchronize those instructions into a managed root `AGENTS.md`.
- Update or remove the managed file automatically during project sync.
- Protect existing user-owned `AGENTS.md` files from being overwritten.
- Share the instructions with authenticated CLI and MCP agents.
- Keep agent instructions out of publicly readable production bundles.
### Generate and manage image descriptions
- Audit image assets for missing descriptions.
- Let a vision-capable agent inspect each image in its rendered context.
- Save generated descriptions to multiple image assets in one operation.
- Mark images as intentionally decorative.
- Reuse the saved asset description wherever the image is used.
- Re-run the audit to verify that missing-description findings are
resolved.
### Perform bounded bulk replacements
- Replace matching literal text across selected pages with
`replace-text`.
- Replace matching fixed prop text with `replace-prop-text`.
- Replace matching resource names or URLs with `replace-resource-text`.
- Restrict replacements by page, instance, prop, resource, field, match
mode, and maximum change count.
- Leave dynamic expressions unchanged.
### Inspect element ancestry
- Return an element’s direct parent in instance listings.
- Request the complete ancestor chain when inspecting an element.
- Understand tree placement without rebuilding the instance hierarchy
from raw project data.
### Discover available fonts
- List uploaded font families through `list-fonts`.
- Discover available system font stacks.
- Inspect font family, style, weight, format, and associated asset
information before applying typography.
### Inspect external component registries
- Inspect a local or remote shadcn-style registry item.
- Read its description, files, dependencies, targets, and documentation.
- Receive an explicit Webstudio compatibility report.
- Inspect registry metadata without installing or executing external
code.
### Capture generated project routes directly
- Run `webstudio screenshot --path /route` against the configured
project.
- Generate a temporary production-style preview automatically.
- Capture the requested route.
- Stop the temporary preview after capture.
- Avoid manually building and starting a preview for a single
screenshot.
### Assert visual changes
- Add expected text to screenshot comparisons.
- Set maximum pixel mismatch limits.
- Set minimum or maximum changed-region counts.
- Assert whether overall brightness or a color channel moved in the
expected direction.
- Use these assertions as pass/fail evidence for visual changes.
### Require compatible CLI versions
- Send the project-bundle contract version with CLI synchronization
requests.
- Stop incompatible CLI versions before they receive incomplete project
data.
- Tell users exactly how to update the CLI or run the latest version
once.
- Prevent older clients from silently dropping project settings.
## Upgrade Note
This branch introduces a new synchronized project-bundle contract. Older
CLI versions must update before synchronizing against the new API.
```sh
npm install -g webstudio@latest
```
Or run the latest version once:
```sh
npx webstudio@latest <command>
```
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## prettier -> oxfmt (0.58.0)
- .oxfmtrc.json generated with `oxfmt --migrate=prettier`: trailingComma
es5, printWidth 80 (oxfmt defaults to 100), sortPackageJson disabled,
.prettierignore patterns folded in (file deleted).
- ignorePatterns additionally exclude yml/yaml/toml/jsonc and
packages/cli/templates/** so oxfmt covers exactly the file set prettier
used to format (ts,tsx,js,json,css,md) and never touches user-facing
scaffolding artifacts.
- root `format` script, nano-staged pre-commit hook and the CI check
(`pnpm oxfmt --check`, replacing `prettier --cache --check` and its
actions/cache step — oxfmt checks the whole repo in ~0.7s) now run
oxfmt.
- every generator script that ran `prettier --write` on __generated__
output now runs `oxfmt` (css-data, icons, html-data, postgrest, sdk,
sdk-components-*, apps/builder tailwind preflight).
- the four scripts that used prettier's JS `format()` API write raw
source instead and their package.json scripts pipe the generated file
through the oxfmt CLI: project-build generate-docs +
generate-runtime-operation-contracts, cli generate-docs, apps/builder
generate-server-only-api-metadata. oxfmt rejects paths containing "..",
so cross-package generated files are formatted via a subshell cd into
the owning package.
- one-time reformat: oxfmt's canonical output differs from prettier on 8
repo source files (binary-operator continuation indent, import() call
wrapping, sub-pixel spacing of empty for-clauses); oxfmt honors existing
`// prettier-ignore` comments so intentional layouts are kept.
- prettier remains ONLY as a cli devDependency for the
prettier/standalone HTML normalizer inside html-to-jsx.test.ts, and
inside fixtures/ + packages/cli/templates/ (ships to scaffolded user
projects; out of scope here).
- editor config switched from esbenp.prettier-vscode to oxc.oxc-vscode
(devcontainer extension list, .vscode editor.defaultFormatter).
## oxlint 1.56 -> 1.73
- oxlint 1.73 rules-of-hooks special-cases hooks NAMED useEffectEvent:
their results may only be called from effects. Our
app/shared/hook-utils/effect-event.ts is a stable-callback helper
(useEvent pattern), intentionally called from event handlers and passed
as props. The rule is disabled via .oxlintrc.json overrides for the five
files doing this (product code untouched); note those files lose
hook-order linting entirely until the rule becomes configurable.
## TypeScript 5.8.2 -> 7.0.2 (native compiler)
- `tsc` is now the Go-based native binary; all `tsgo` script usages
(workspace packages and fixtures) renamed back to `tsc` and
@typescript/native-preview removed (the package is being phased out).
- fixes for TS7 changed defaults and stricter checks:
- `types` now defaults to [] -> base tsconfig sets "types": ["node"]
(restores the implicit-@types/node behavior of TS5).
- `rootDir` now defaults to ./ -> every tsconfig.dts.json sets
"rootDir": "./src" (TS5011 otherwise).
- import assertions removed -> cli/src/cli.ts uses `with { type: "json"
}` (TS2880).
- stricter isolatedDeclarations inference -> explicit annotations in
css-data bin/property-filter.ts (TS9010) and image image-dev.stories.tsx
(TS9013).
- declaration serialization limit -> project-build builder-runtime.ts
runtimeOperationContracts gets an explicit type (TS7056); the annotation
preserves the narrow RuntimeOperationId union.
- TS7 ships no stable programmatic API:
- react-sdk (component-generator tests drive the compiler API) aliases
its typescript devDependency to @typescript/typescript6@^6.0.2; its bin
is `tsc6`, so the package's typecheck still runs native TS7 tsc from the
workspace root.
- generate-arg-types keeps typescript 5.8.2 for react-docgen-typescript.
- fixtures/ and packages/cli/templates/ keep typescript 5.8.2
(user-facing); fixture scripts still work because plain `tsc` resolves
to their local dependency.
## vite -> rolldown-vite 7.3.1 (oxc bundler)
- global pnpm override `"vite": "npm:rolldown-vite@^7.3.1"`: the builder
app (remix v2 vite plugin works unmodified), every package vite build
and the storybook build now bundle on rolldown/oxc.
- vitest is scoped BACK to vanilla vite via `vitest>vite`,
`vite-node>vite` and `@vitest/mocker>vite` overrides because vitest
3.1's runner does not understand rolldown's SSR transform
(__vite_ssr_exportName__ ReferenceError in every suite).
- those overrides pin vite EXACTLY to 6.3.4: letting ^6.3.4 float to
6.4.3 breaks cli prebuild tests (dynamic import of a generated
app/routes/$.tsx fails to resolve under 6.4.3's loader).
## unrelated cleanup requested during review
- apps/builder commands.ts imports duplicate-tokens-group statically
instead of via dynamic import. The circular dependency the dynamic
import guarded against only exists through the command-panel barrel
(command-panel.tsx > groups/index.ts > commands-group.tsx >
commands.ts); importing the group module directly is cycle-free
(verified with madge), matching the neighboring wrap-group and
convert-group imports.
## verification
- pnpm lint, oxfmt --check: clean.
- pnpm -r typecheck (TS7 native) and pnpm dts: clean.
- pnpm -r test: all suites pass (builder 2093 tests, cli 630, react-sdk
45, project-build 11, ...).
- builder app build, full workspace build, cli build and storybook build
all succeed on rolldown-vite.
## Description
1. What is this PR about (link the issue and add a short description)
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR expands Webstudio MCP and CLI into a more complete agent
authoring interface. Agents can discover project capabilities, edit real
Webstudio content, use data/resources, verify visual output, and report
useful issues when workflows fail.
## MCP User Capabilities
- Create, update, duplicate, reorder, and delete pages.
- Create pages from templates and manage reusable page templates.
- Inspect project pages, instances, props, styles, assets, variables,
resources, and metadata.
- Insert Webstudio components and authored JSX fragments into pages.
- Edit text, props, styles, breakpoints, design tokens, and CSS
variables.
- Update page metadata, SEO fields, redirects, marketplace metadata, and
project settings.
- Create and use static data variables.
- Create and use dynamic resources fetched over HTTP.
- Use REST, GraphQL, and built-in system resources in authored pages.
- Bind data variables and resources to visible page content.
- Create resource-backed pages such as sitemap XML, structured SEO data,
and API-driven content sections.
- Preview current project state without requiring a publish step.
- Capture screenshots for visual verification.
- Verify responsive designs across desktop, tablet, and mobile
breakpoints.
- Use screenshot diff and OCR-style checks to inspect rendered output.
- Reset and refresh local MCP project state when needed.
- Discover available CLI/MCP commands from generated manuals.
- Use clearer examples for resources, data variables, JSX authoring,
previewing, and visual checks.
- Read marketplace product metadata without requiring a full project
snapshot.
- Get safer guidance when using low-level patch tools.
- Get a ready-to-copy Discord report template when MCP/CLI behavior is
confusing or broken.
## Summary
Adds timezone selection support to the Time component.
Users can now choose:
- `UTC` for deterministic server/client rendering
- `visitor` to render UTC during SSR, then update to the browser
timezone after hydration
- an IANA timezone such as `Europe/Berlin`
Also adds a timezone combobox control in the settings panel with common
timezone options, supported IANA timezone values, typed input, and
preview descriptions.
## Changes
- Added `timeZone` prop to the Time component, defaulting to `UTC`
- Updated standard and custom-format date rendering to use the selected
timezone
- Added visitor timezone mode with hydration-safe UTC fallback
- Added `timeZone` prop metadata and settings-panel control support
- Added timezone combobox control with previews
- Added tests for:
- explicit timezone rendering
- custom format timezone rendering
- date rollover in another timezone
- invalid timezone fallback
- trimmed timezone values
- visitor timezone hydration behavior
## Verification
- `pnpm --filter @webstudio-is/sdk-components-react test --
time.test.ts`
- `pnpm --filter @webstudio-is/sdk-components-react typecheck`
- `pnpm --filter @webstudio-is/builder typecheck`
- `pnpm exec prettier --check ...`
I’m seeing React hydration errors 418/425/423 in the console when the
Time component is used with an ISO date. This was breaking my Klaro
cookie banner: it would flash briefly and then disappear. Once I
switched to a date-only value (2026-05-06 instead of
2026-05-06T22:00:00.000Z), the issue disappeared, I no longer see
console errors, and Klaro works correctly.
## Description
This PR refactors content mode property restrictions to use a
declarative, component-based configuration approach. Instead of
maintaining hardcoded lists of editable properties in a central
location, each component now declares which of its properties should be
editable in content mode via the `contentMode: boolean` flag in
PropMeta.
### Content Mode Properties Added:
| Component | Properties |
|-----------|-----------|
| Image | src, width, height, alt |
| Video | src, width, height |
| YouTube | url |
| Vimeo | url |
| Vimeo Preview Image | src |
| Link | href |
| Markdown Embed | code |
## Steps for reproduction
1. Create a project and share it with editor-only access
2. Switch to content edit mode
3. Select a YouTube or Vimeo component on the canvas
4. In the Properties panel, the `url` property should now be visible and
editable
5. Modify the YouTube/Vimeo URL and verify it updates correctly
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [x] made a self-review
- [x] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [x] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
---------
Co-authored-by: shreyanshkotak <sk7739@nyu.edu>
- Add new story variants across ~100 files covering previously untested props
- Fix all story titles to use sentence case per UI label guidelines
- Consolidate duplicate card, thumbnail, and value-text stories into parent files
- Add interactive/controlled variants for checkbox, radio, switch, select, etc.
- Add style source, disabled, icon, placeholder, unit options variants to CssValueInput
- Add sub-menu, radio group, destructive, and disabled variants to dropdown/context menus
- Add dialog resize, maximize, actions, and non-draggable variants
- Add popover title, actions, and side placement variants
- Add tooltip side placements and content variant stories
Show a warning banner when a redirect path matches the current page
path, indicating the redirect will override access to the page.
Changes:
- Add PanelBanner component with warning variant to page settings
- Implement findMatchingRedirect utility to detect redirect conflicts
- Show contextual warning with link to Redirects settings section
- Add comprehensive tests for redirect conflict detection
Code quality:
- Replace 'return undefined' with 'return' per style guide
- Use 'return null' for React components returning nothing
- Changed safe mode detection from .has() to strict === 'true' check
- Made safemode a required boolean parameter in builderPath/builderUrl
- Added 'Open in safe mode' option to dashboard project menu
- Added safe mode indicator button with shield icon in topbar
- Safe mode now prevents all script execution in HTML embeds
- Added isSafeMode to React SDK context
- Added test coverage for safe mode in HTML embed component
## Description
1. What is this PR about (link the issue and add a short description)
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
Fixes
https://discord.com/channels/955905230107738152/955905231227609158/1467592164727849104
- Add DDDD/DDD tokens for full/short day names (e.g., Monday/Mon)
- Add MMMM/MMM tokens for full/short month names (e.g., January/Jan)
- Day and month names are localized based on selected language
- Simplify format field tooltip for better UX
- Add comprehensive tests for new tokens in multiple languages
- Maintain backward compatibility with existing format tokens
Co-authored-by: Oleg Isonen <oleg008@gmail.com>
## Description
- [x] new file types support
- [x] categories filter
- [x] sorting filter
- [x] asset resource for access from custom code
## Steps for reproduction
1. click button
3. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
---------
Co-authored-by: zehjotkah <111518041+zehjotkah@users.noreply.github.com>
Closes https://github.com/webstudio-is/webstudio/issues/5507
## Description
1. What is this PR about (link the issue and add a short description)
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
## Description
1. What is this PR about (link the issue and add a short description)
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
Closes https://github.com/webstudio-is/webstudio/issues/3510
## Summary
This PR adds a **Current Date** system resource, extends the `system`
object with a `pathname` property, and enhances the **Time component**
with custom date formatting capabilities to provide more flexibility and
prevent hydration errors.
### Key Features
1. **Current Date System Resource** - Provides normalized date/time
information that prevents React hydration errors
2. **System Pathname** - Adds `pathname` property directly to the
`system` object for easy access in expressions
3. **Time Component Custom Formatting** - Adds a `format` prop with
template tokens for flexible date/time display
## Motivation
These changes solve important use cases:
- **Current Date**: Prevents React hydration errors when displaying
dynamic dates (e.g., copyright years in footers). Previously, using `new
Date()` in expressions would cause server/client mismatch.
- **System Pathname**: Provides easy access to the current page path in
expressions without needing to parse the URL manually.
- **Custom Time Formatting**: Allows users to format dates with simple
templates (e.g., `YYYY-MM-DD`) without being limited to
`Intl.DateTimeFormat` options, and provides a simple way to exclude
specific date or time components to avoid hydration issues.
## Changes
### Current Date Resource
- Created
[`apps/builder/app/shared/$resources/current-date.server.ts`](apps/builder/app/shared/$resources/current-date.server.ts)
- Provides date fields: `year`, `month`, `day`, `iso`, `timestamp`
- All values normalized to midnight UTC (00:00:00.000Z) to prevent
hydration errors
- Registered in
[`rest.resources-loader.ts`](apps/builder/app/routes/rest.resources-loader.ts)
- Added to SystemResourceForm dropdown in UI
### System Pathname
- Added `pathname` property to
[`System`](packages/sdk/src/schema/pages.ts) type
- Updated [`system.ts`](apps/builder/app/shared/system.ts) to compute
and include pathname
- Normalized empty pathname to `'/'` for consistency
- Available in expressions as `system.pathname`
### Time Component Enhancements
- Added [`formatDate()`](packages/sdk-components-react/src/time.tsx:353)
function supporting template tokens:
- `YYYY`, `YY` - year (4-digit, 2-digit)
- `MM`, `M` - month (zero-padded, no padding)
- `DD`, `D` - day (zero-padded, no padding)
- `HH`, `H` - hours (zero-padded, no padding)
- `mm`, `m` - minutes (zero-padded, no padding)
- `ss`, `s` - seconds (zero-padded, no padding)
- Added `format` prop to
[`Time`](packages/sdk-components-react/src/time.tsx:386) component that
overrides `dateStyle`/`timeStyle` when provided
- Updated component logic to check for `format` prop first before
falling back to `Intl.DateTimeFormat`
- Added comprehensive tests for date formatting in
[`time.test.ts`](packages/sdk-components-react/src/time.test.ts)
- Updated component metadata in
[`time.ws.ts`](packages/sdk-components-react/src/time.ws.ts) to include
`format` in initialProps
- Example: `format="YYYY-MM-DD HH:mm:ss"` displays as "2025-11-03
18:47:25"
### Template & Fixture Updates
- Updated all CLI templates (defaults, react-router, SSG) to include
`pathname` in system objects
- Updated all fixture files (47 files total) to include `pathname` in
system objects
- All 694 tests passing
- All typecheck passing
## Usage
### Current Date Resource
Users can now add the Current Date resource from the Data Variables
panel:
1. Click + next to Data Variables
2. Select "System Resource"
3. Choose "Current Date" from the dropdown
4. Access in expressions:
- `date.year` - for copyright year
- `date.month` - current month (1-12)
- `date.day` - current day (1-31)
- `date.iso` - ISO 8601 string
- `date.timestamp` - Unix timestamp
### System Pathname
Access the current page path directly:
- `system.pathname` - current page path (e.g., `/about`, `/blog/post-1`)
### Time Component Custom Format
Use the `format` prop for custom date/time display:
```jsx
<Time datetime="2025-11-03T18:47:25Z" format="YYYY-MM-DD" />
// Displays: 2025-11-03
<Time datetime="2025-11-03T18:47:25Z" format="YYYY-MM-DD HH:mm:ss" />
// Displays: 2025-11-03 18:47:25
<Time datetime="2025-11-03T18:47:25Z" format="M/D/YYYY" />
// Displays: 11/3/2025
```
Technical Notes
- Current Date values are normalized to midnight UTC to ensure
consistency throughout the day and prevent hydration mismatches
- The pathname property is computed from the request URL and normalized
to '/' when empty
- Custom format templates in the Time component are processed before
falling back to Intl.DateTimeFormat
- All changes are backward compatible
Ref https://github.com/webstudio-is/webstudio/issues/3632
Here unflagged element component in components panel and html paste.
Additionally
- migrate link template to html element
- added :local-link state to links which is replaced with
[aria-current=page] when css is generated, sadly not yet supported in
any browser
- migrated many templates from legacy components to html elements
- put tailwind paste behind flag
Ref https://github.com/webstudio-is/webstudio/issues/3632
This is big one. So I switched to html elements whole forms section in
components. Though here I introduced some deviation from specification
**Textarea**
Initial value is represented as text content
```html
<textarea>content</textarea>
````
Though it is trickier to manage text content as state and all frameworks
rely on value property of textarea dom interface.
**Select**
Similar story. There is no value attribute and state is represented by
selected boolean attribute on options
```html
<select>
<option value="value1" selected></option>
<option value="value2"></option>
</select>
```
We replace selected attribute on option with value attribute on selected
when paste.
So basically all form elements are using "value" and "checked"
attributes which can be easily mapped to html and all frameworks.
Html elements can completely replace legacy typography components. Here
replace component templates with element templates as part of sdk.
- moved content block to general section
- moved separator into typography section
- moved code text into typography section and used regular `<code>`
element for it
- made "Text Content" setting expandable
- fixed dragging templates onto canvas.
<img width="475" alt="Screenshot 2025-05-21 at 11 27 27"
src="https://github.com/user-attachments/assets/ab53c5ec-e51f-498d-b01b-cc7f2eb20c2d"
/>
<img width="515" alt="Screenshot 2025-05-21 at 11 27 20"
src="https://github.com/user-attachments/assets/0f560c26-7e51-4025-a487-6f7e4e3f43e3"
/>
All html attributes all converted to react style though in case of Time
property was converted.
The easiest solution is to rename the actual property to accept
converted time.
## Description
fixes#5015
Added `backgroundColor` to `white` for `XMLNode` so the content is
visible even when the `GlobalRoot` is set to something else.
## Steps for reproduction
- Create a demo project.
- Create a XML with sitemap.xml
- Now, switch back to the home page and set the `backgroudnColor` of the
global root to some random colour.
- Then, switch back to the sitemap page. Now, the contents are not
visible as the background is inherited from the GlobalRoot. This fix
will help in setting to white whenever a xml node is used.
## Code Review
- [ ] hi @kof, I need you to do
- test it on preview
## Before requesting a review
- [x] made a self-review
## Before merging
- [x] tested locally and on preview environment (preview dev login:
0000)
Here dropped legacy descriptions generated with AI. All attributes are
using descriptions scraped from html spec. Though many component
properties redefine these attributes without descriptions. So to
workaround just always fall back to attributes descriptions. Covers most
cases.
Now we use html attribute for all components. Now components like Box
with already set "className" property will be migrated to "class"
attribute. This cuts ~350kB in builder scripts. Total ~1MB after
removing both html and aria attributes.
```diff
- 5036 build/client/assets/
+ 4676 build/client/assets/
```
Attributes and properties now have the following order
- component properties
- tag attributes
- global html attributes
- role
- aria attributes
Here replaced "transparent" keyword with more explicit "descendants"
list in component content model.
This will enable use case with child constraint while allowing other
instances to be children like here
```
contentModel: {
category: "instance",
children: [
"instance",
"AnimateText",
"StaggerAnimation",
"VideoAnimation",
],
},
```
and descendants are described like this
```
contentModel: {
category: "instance",
children: ["instance"],
descendants: ["VimeoSpinner", "VimeoPlayButton", "VimeoPreviewImage"],
},
```
## Description
Error toasts in components instead of failing
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
Here replaced "embed" and "control" components with content model. Many
components now detect no content based on html content model like with
img or input tags. In other cases component meta can explicitly specify
empty array to not accept anything.
```
meta = {
contentModel: {
category: 'instance',
children: []
}
}
```
## Description
1. What is this PR about (link the issue and add a short description)
## Steps for reproduction
1. click button
2. expect xyz
## Code Review
- [ ] hi @kof, I need you to do
- conceptual review (architecture, feature-correctness)
- detailed review (read every line)
- test it on preview
## Before requesting a review
- [ ] made a self-review
- [ ] added inline comments where things may be not obvious (the "why",
not "what")
## Before merging
- [ ] tested locally and on preview environment (preview dev login:
0000)
- [ ] updated [test
cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md)
document
- [ ] added tests
- [ ] if any new env variables are added, added them to `.env` file
Ref https://github.com/webstudio-is/webstudio/issues/3632
Here I finally got rid from "rich-text-child" necessity. With html
elements in the tree it will no longer work so we need a new way to work
with rich text.
Added a few new utilities to content model which detect rich text by
text inside or any textual tags. Span and links can still be detected as
containers rather than children when not surrounded by text and other
textual elements.