fix: Make article dates editable and clarify complete content setup

This commit is contained in:
Oleg Isonen committed 2026-09-09 10:42:02 +01:00
1 parent 6359f979a3
commit 7834d90a61
19 files changed
+429 -17

No files matched your search

@@ -1,4 +1,5 @@
import { TextControl } from "./text";
import { DateControl } from "./date";
import { CodeControl } from "./code";
import { NumberControl } from "./number";
import { CheckControl } from "./check";
@@ -59,6 +60,10 @@ export const renderControl = ({
return <TextControl key={key} meta={meta} prop={prop} {...rest} />;
}
if (meta.control === "date") {
return <DateControl key={key} meta={meta} prop={prop} {...rest} />;
}
if (meta.control === "resource") {
return <ResourceControl key={key} meta={meta} prop={prop} {...rest} />;
}
@@ -0,0 +1,87 @@
import { act } from "react-dom/test-utils";
import { useState } from "react";
import { createRoot } from "react-dom/client";
import { expect, test, vi } from "vitest";
import { userEvent } from "@vitest/browser/context";
import { TooltipProvider } from "@webstudio-is/design-system";
import { Time } from "@webstudio-is/sdk-components-react/metas";
import { renderControl } from "./combined";
(
globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }
).IS_REACT_ACT_ENVIRONMENT = true;
test.each([
["2025-07-07", "date", "2025-07-07", "2025-08-09", "2025-08-09"],
[
"2025-07-07T12:30:00+02:00",
"datetime-local",
"2025-07-07T10:30",
"2025-08-09T11:45",
"2025-08-09T11:45:00.000Z",
],
[
"2025-07-07T12:30",
"datetime-local",
"2025-07-07T12:30",
"2025-08-09T11:45",
"2025-08-09T11:45",
],
["", "date", "", "2025-08-09", "2025-08-09"],
["not a date", "text", "not a date", "2025-08-09", "2025-08-09"],
])(
"edits Date Time value %s without changing it on focus",
async (source, type, displayed, edited, saved) => {
const container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
const onChange = vi.fn();
const Harness = () => {
const [value, setValue] = useState(source);
return (
<TooltipProvider>
{renderControl({
instanceId: "date",
propName: "datetime",
meta: Time.props!.datetime,
prop: {
id: "datetime",
instanceId: "date",
name: "datetime",
type: "string",
value,
},
computedValue: value,
onChange: (next) => {
onChange(next);
if (next.type === "string") {
setValue(next.value);
}
},
})}
</TooltipProvider>
);
};
try {
await act(async () => root.render(<Harness />));
const input = container.querySelector<HTMLInputElement>("input")!;
expect(input?.type).toBe(type);
expect(input.value).toBe(displayed);
await act(async () => userEvent.click(input));
await act(async () => input.blur());
expect(onChange).not.toHaveBeenCalled();
await act(async () => userEvent.fill(input, edited));
await act(async () => input.blur());
expect(onChange).toHaveBeenLastCalledWith({
type: "string",
value: saved,
});
await act(async () => userEvent.fill(input, ""));
await act(async () => input.blur());
expect(onChange).toHaveBeenLastCalledWith({ type: "string", value: "" });
} finally {
await act(async () => root.unmount());
container.remove();
}
}
);
@@ -0,0 +1,117 @@
import { useId, useMemo } from "react";
import { InputField, Tooltip } from "@webstudio-is/design-system";
import { validatePrimitiveValue } from "@webstudio-is/project-build/runtime";
import { useDraftValue } from "~/builder/shared/use-draft-value";
import { BindableExpressionControl } from "~/builder/shared/bindable-expression";
import {
type ControlProps,
ResponsiveLayout,
humanizeAttribute,
} from "../shared";
import { PropertyLabel } from "../property-label";
import { useBindableControl } from "./use-bindable-control";
const getDateInput = (source: string) => {
// Let the platform recognize native date and local date-time values. Keep
// legacy/invalid values editable instead of silently clearing them.
const input = document.createElement("input");
for (const type of ["date", "datetime-local"] as const) {
input.type = type;
input.value = source;
if (input.value !== "" || source === "") {
return { type, value: input.value, utc: false };
}
}
const date = new Date(source);
if (Number.isNaN(date.getTime()) === false) {
input.type = "datetime-local";
input.value = date.toISOString().slice(0, -1);
if (input.value !== "") {
return { type: "datetime-local" as const, value: input.value, utc: true };
}
}
return { type: "text" as const, value: source, utc: false };
};
export const DateControl = ({
meta,
prop,
propName,
computedValue,
onChange,
}: ControlProps<"date">) => {
const id = useId();
const source = String(computedValue ?? "");
const input = useMemo(() => getDateInput(source), [source]);
const binding = useBindableControl({
boundExpression: prop?.type === "expression" ? prop : undefined,
fallbackExpression: JSON.stringify(computedValue),
});
const localValue = useDraftValue(input.value, (value) => {
if (binding.bindingState.overwritable === false) {
return;
}
onChange({
type: "string",
value:
value !== "" && input.utc ? new Date(`${value}Z`).toISOString() : value,
});
});
const label = humanizeAttribute(meta.label || propName);
return (
<ResponsiveLayout
label={
<PropertyLabel
name={propName}
readOnly={binding.bindingState.overwritable === false}
/>
}
>
<BindableExpressionControl
{...binding}
value={source}
validate={(value) => validatePrimitiveValue(value, label)}
onChangeValue={(value) => onChange({ type: "string", value })}
onChangeExpression={(value) => onChange({ type: "expression", value })}
onRemove={(value) => onChange({ type: "string", value: String(value) })}
renderControl={({ readOnly }) => (
<Tooltip
content={
binding.fieldError ??
(input.utc
? "Date and time in UTC. Display formatting and time zone are configured separately."
: "")
}
open={
binding.fieldError === undefined && input.utc === false
? false
: undefined
}
>
<InputField
id={id}
aria-label={input.utc ? `${label} (UTC)` : label}
type={input.type}
step="any"
value={localValue.value}
disabled={readOnly}
color={binding.fieldError === undefined ? undefined : "error"}
aria-invalid={binding.fieldError !== undefined || undefined}
onChange={(event) => {
if (event.currentTarget.validity.badInput === false) {
localValue.set(event.currentTarget.value);
}
}}
onBlur={localValue.save}
onKeyDown={(event) => {
if (event.key === "Enter") {
localValue.save();
}
}}
/>
</Tooltip>
)}
/>
</ResponsiveLayout>
);
};
+45
View File
@@ -0,0 +1,45 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
test.each([[], ["--reporter=line"]])(
"validates E2E selection before starting services with arguments %j",
(...args) => {
const directory = mkdtempSync(join(tmpdir(), "e2e-selection-"));
const capture = join(directory, "arguments.json");
try {
// Stop at the package-manager boundary: no Docker or database work runs.
writeFileSync(
join(directory, "pnpm"),
`#!${process.execPath}\nrequire("node:fs").writeFileSync(process.env.ARGUMENT_CAPTURE, JSON.stringify(process.argv.slice(2))); process.exit(73);\n`,
{ mode: 0o755 }
);
const result = spawnSync(
"/bin/bash",
[join(import.meta.dirname, "run.sh"), "run", ...args],
{
encoding: "utf8",
env: {
...process.env,
PATH: `${directory}:${process.env.PATH}`,
ARGUMENT_CAPTURE: capture,
E2E_SKIP_CLEANUP: "true",
E2E_RUN_TESTS: "true",
SCHEMA_SNAPSHOT: join(directory, "unused.sql"),
},
timeout: 10_000,
}
);
expect(result.status, result.stderr).toBe(73);
expect(JSON.parse(readFileSync(capture, "utf8"))).toEqual([
"e2e:ci",
"--list",
...args,
]);
} finally {
rmSync(directory, { recursive: true, force: true });
}
}
);
+5 -1
View File
@@ -164,8 +164,12 @@ run_builder_e2e_tests() {
validate_builder_e2e_selection() {
(
cd "$ROOT_DIR/apps/builder"
E2E_BUILDER_URL="${E2E_BUILDER_URL:-https://127.0.0.1:3000}" \
export E2E_BUILDER_URL="${E2E_BUILDER_URL:-https://127.0.0.1:3000}"
if [ "${#PLAYWRIGHT_ARGS[@]}" -eq 0 ]; then
pnpm e2e:ci --list
else
pnpm e2e:ci --list "${PLAYWRIGHT_ARGS[@]}"
fi
)
}
@@ -207,6 +207,26 @@ featureImage:
To use frontmatter in the designed part of a Content Block, [bind](../foundations/variables.md) a property or text value to the Content Block's **document** variable. For example, bind a heading to `document.frontmatter.title`, an Image source to `document.frontmatter.featureImage.src`, and its alternative text to `document.frontmatter.featureImage.description`. This uses the image's Asset description instead of duplicating alternative text in frontmatter. Fields authored directly in the connected MDX file can be edited on the canvas and through supported Settings controls in Content mode; edits save back to that file. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only.
**Check every article field**
An editable MDX body does not make the whole article editable. Check the title,
excerpt, author details, dates, reading time, hero and inline images,
alternative text, captions, links, and custom-component content in **Content
mode**. Include header fields outside **MDX content**. Change each value through
its UI control, check the saved file, reload, and restore the original value.
Do not treat editing the raw MDX file as a substitute for an editor-facing control.
Replacing an article's image changes its frontmatter image `$ref`, not the
shared Asset's resolved `.src`. With a writable direct Image source binding to
`document.frontmatter.featureImage.src`, use **Choose source** in Content mode.
The resolved URL itself remains read-only.
To edit shared alternative text, open **Choose source**, open the image's
actions menu, then choose **Settings** and edit **Description**. A binding to
`document.frontmatter.featureImage.description` uses that description.
Changing it affects every use of the Asset; replacing an article's image does
not change the original Asset's description.
**Keep formatted values editable**
Keep an editable value separate from its surrounding wording. For example, to
@@ -239,6 +259,27 @@ In Design mode, select the **Source** property label and choose **Reset value**.
## Content Block in Content mode
### Verify the editor experience before handoff
Use this checklist when creating, migrating, or changing any Content Block,
with or without an MDX source:
1. List every value the editor must change, its UI control, and where it saves.
Include headers, dates, reading time, images, alternative text, links, and
custom-component content—not just body text.
2. Use the appropriate control: a calendar for dates and an image picker for
image replacement. Keep fixed punctuation outside directly bound values.
Preserve existing field types and wording; a stored `4 min read` string
can stay one directly bound value.
3. Edit every listed value in **Content mode**. Check its saved project data,
MDX, or referenced file, reload, then restore and verify the original value.
4. Record each field as passed, failed, or not tested. A correct preview,
successful API edit, or one editable heading does not verify other fields.
Missing controls and read-only required fields mean the setup is unfinished.
Templates and designer-owned layout stay protected. This protection must not
be used to exclude content the editor was promised they could change.
In [Content mode](../foundations/modes.md#content), you can edit existing content inside Content Blocks. But what if you want to add _new_ content?
You can within Content Block — region(s) on the page the designer designates as a place you can add new content from building blocks to entire sections.
+14
View File
@@ -27,6 +27,20 @@ Use Time for:
## Properties
### Edit the date in Settings
The **Date Time** control uses a calendar input for date-only values and a
date/time input for timestamps. Timestamps with a time-zone offset are edited
in UTC; the tooltip identifies this separately from the display time zone.
Opening or focusing the control does not rewrite the stored value. Existing
values that cannot be represented by the date input remain available as text
so they can be repaired.
For an editable article, bind **Date Time** directly to the Content Block's
document frontmatter field. Content-mode changes then save back to that file.
Configure date formatting separately; formatting the binding expression makes
it read-only.
Some commonly used properties (see the Settings panel for all available options):
### Core Properties
+18 -1
View File
@@ -191,7 +191,24 @@ Use the dedicated connect, switch, inspect, edit, update-frontmatter, reload, an
A source edit replaces the complete MDX document. A frontmatter update replaces the complete frontmatter property map. Inspect the current source first and preserve everything the user did not ask to change. Store frontmatter images as exact `$ref` objects, bind Image sources to their resolved `.src`, and bind alt properties to their Asset `.description`. Use `update-content-block-frontmatter` for MCP frontmatter edits. MDX-rendered elements are not persistent instance targets for generic `bind-props` or `update-text` calls. Preserve existing `mode:"readwrite"` bindings when encountered; they are valid only for exact direct paths into the connected document's frontmatter. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Before handoff, verify the intended fields' document paths and write modes, and verify a representative Content-mode edit saves and survives reload; restore the test value and disclose if UI verification was not run.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Follow the complete article editability checklist below before handoff.
### Content Block completion checklist
For every Content Block creation, migration, or repair—with or without MDX—treat Content-mode editability as a delivery requirement for all content the user expects editors to change. Before implementation, inventory each value, its intended UI control, and its write destination. Choose controls by meaning: date/calendar controls for dates, image pickers for image replacement, and direct text or number controls for reading time. Keep fixed punctuation and units outside a directly bound value element; never mix literal and expression children there. Preserve the existing stored type and wording: if `readTime` already stores `4 min read`, bind that whole string directly instead of assuming it stores only a number. Distinguish protected layout/templates from editable content explicitly. Test each field through Content mode, check the saved project data or source file, reload, restore test values, and record passed/failed/not tested. Testing one heading, inspecting bindings, or successfully writing through MCP does not cover the remaining fields. Report missing controls and unsupported writes as unfinished requirements, not successful delivery.
### Complete article editability is required
When the user asks for the whole article to be editable in Content mode, this includes every article-owned value: title, excerpt, author details and links, dates, reading time, categories, hero and inline image sources, alternative text, captions, body text, links, and custom-component content and media. Exclude only designer-owned layout, styling, templates, and shared navigation/footer unless the user asks otherwise. A correct preview, a successful MCP write, or an editable body does not prove this requirement is met.
Before handoff:
1. Inventory every article field, its UI control, binding, and source file/field. Header fields outside **MDX content** are still part of the article and must be editable through the Content Block's document bindings.
2. In Content mode, edit every inventoried field through the UI, including choosing a different hero image and editing link destinations and custom-component props. Do not substitute a raw MDX/YAML edit or an MCP write for this check.
3. Read the saved source after each edit. Confirm the intended MDX body/frontmatter or referenced author file changed, unrelated content stayed intact, and the edit survives reload. Restore test values and verify restoration.
4. Mark each field passed, failed, or not tested. Do not claim the article is fully editable while any required field is failed or untested.
**Image replacement is not shared metadata editing.** For frontmatter such as `featureImage: { $ref: "./images/hero.png" }`, bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. In Content mode, **Choose source** replaces the article’s `featureImage.$ref`; it does not overwrite the shared Asset’s resolved `.src`. The resolved URL field stays read-only. Verify the picker, saved reference, and reload—do not infer support from the displayed image alone. Bind alternative text to `.description` to use the shared Asset description; edit it through **Choose source → asset actions → Settings → Description**, which affects every use of that Asset. Do not treat a missing write mode as a platform limitation, and do not defer image replacement over a separate shared-description question.
Keep intended editable values separate from display formatting. For reading time, use three inline sibling text elements: static `— `, one value element bound directly to `document.frontmatter.readingTime` with `expressionBindingMode:"readwrite"`, and static ` min read`. The value element must contain only that binding, not mixed literal and expression children. Preserve whitespace and the stored field type. Do not concatenate labels or units, use template literals, or add fallbacks/formatting calls to an editable binding. Prefer component formatting controls with a directly bound value. Explain unsupported cases and ask before making an intended editable field read-only; do not trade away editability just to match the display.
+4 -4
View File
@@ -2,15 +2,15 @@
// Edit markdown files in src/docs instead.
export const cliDocs = {
"api-use-cases":
'# CLI API Use Cases\n\n## Link/configure one project\n\nCommands:\n\n- webstudio init --link <api-share-link> --json\n\nNotes:\n\n- Writes local project id and global origin/token config.\n\n## Import synced project bundle into another project\n\nCommands:\n\n- webstudio sync\n- webstudio import --to <destination-share-link>\n- MCP tool: import {"to":"<destination-share-link>"}\n\nNotes:\n\n- Imports local `.webstudio/data.json` into the destination project.\n- Destination share link must allow build/import access.\n- Use `--skip-assets` only when asset rows and files should not be imported.\n\n## Identify current token\n\nCommands:\n\n- MCP tool: whoami {}\n\n## Check token permissions\n\nCommands:\n\n- webstudio permissions --json\n\n## Inspect project/build/version\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["pages","instances","styles"]}\n\n## Discover CLI/API capabilities\n\nCommands:\n\n- webstudio schema api\n- webstudio schema mcp\n- webstudio man --json\n- webstudio man llm --json\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"Create a pricing page"}\n- MCP tool: meta.get-more-tools {"brief":"update-styles"}\n- webstudio mcp list-resources\n- webstudio mcp read-resource webstudio://project/guide\n- webstudio mcp read-resource webstudio://project/expressions\n\nNotes:\n\n- Use `webstudio schema mcp` for a compact machine-readable MCP tool overview. Add `--verbose` or use focused `meta.get-more-tools` calls only when exact input schemas are needed.\n- Use focused MCP tools for discovery first: `meta.index`, `meta.guide`, `meta.get-more-tools`, `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get`. Protocol clients can use `resources/list` and `resources/read`; shell agents can use `webstudio mcp list-resources` and `webstudio mcp read-resource <uri>`. Read longer resources such as `webstudio://project/tools` and `webstudio://project/components` only when focused tools are insufficient.\n- `components.summary` returns counts by default; request `{"detail":"components","limit":20}` for paginated entries. Registry list tools return compact metadata, while `components.get` and `templates.get` return focused full details.\n- Read `webstudio://project/expressions` before authoring unfamiliar computed text, prop bindings, resource expressions, actions, or Collection item bindings.\n- From a shell, call one MCP tool with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio components.summary`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n\n## Inspect external shadcn registry items\n\nCommands:\n\n- webstudio registry inspect --source https://example.com/r/registry.json --item button --json\n- webstudio registry inspect --source ./registry.json --item dialog --json\n- webstudio registry inspect --source https://example.com/r/button.json --json\n\nNotes:\n\n- Reads a local or remote registry item without installing files or changing the configured Webstudio project.\n- Returns the item name, description, package and registry dependencies, file paths/targets, available docs, and a read-only compatibility report.\n- The report explicitly says whether installation or editable-component conversion is supported, lists declared requirements and manual steps, and says when arbitrary source code was not analyzed.\n- This is an inspection step only. It does not install files or change the configured project.\n\n## Understand what MCP can do\n\nCommands:\n\n- webstudio man mcp\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"What can Webstudio MCP do?"}\n\nMCP lets agents work on one configured Webstudio project. Agents can:\n\n- Inspect the linked project, token permissions, and latest editable build.\n- Read selected project data for audits, migrations, and repair.\n- Search values and ids across every Builder namespace without sending full project data to the model.\n- Audit accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, and unused or duplicate style data.\n- Create and edit pages, folders, redirects, breakpoints, and page templates.\n- Create pages from reusable templates.\n- Update page metadata, SEO fields, auth settings, and marketplace metadata.\n- Insert components and styled JSX sections.\n- Create data-driven lists, grids, cards, and similar repeated UI from array or object data in one Collection operation.\n- Move, copy, wrap, unwrap, convert, rename, retag, and delete elements.\n- Update text, rich text, props, bindings, and actions.\n- Create and update local styles, design tokens, style sources, and CSS variables.\n- Create static data variables and JSON variables.\n- Create HTTP, GraphQL, and system resources.\n- Use system resources for sitemap, current date, and assets.\n- Bind resources to rendered data or form/action props.\n- Manage nest Line truncated
'# CLI API Use Cases\n\n## Link/configure one project\n\nCommands:\n\n- webstudio init --link <api-share-link> --json\n\nNotes:\n\n- Writes local project id and global origin/token config.\n\n## Import synced project bundle into another project\n\nCommands:\n\n- webstudio sync\n- webstudio import --to <destination-share-link>\n- MCP tool: import {"to":"<destination-share-link>"}\n\nNotes:\n\n- Imports local `.webstudio/data.json` into the destination project.\n- Destination share link must allow build/import access.\n- Use `--skip-assets` only when asset rows and files should not be imported.\n\n## Identify current token\n\nCommands:\n\n- MCP tool: whoami {}\n\n## Check token permissions\n\nCommands:\n\n- webstudio permissions --json\n\n## Inspect project/build/version\n\nCommands:\n\n- MCP tool: inspect {}\n- MCP tool: snapshot {"include":["pages","instances","styles"]}\n\n## Discover CLI/API capabilities\n\nCommands:\n\n- webstudio schema api\n- webstudio schema mcp\n- webstudio man --json\n- webstudio man llm --json\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"Create a pricing page"}\n- MCP tool: meta.get-more-tools {"brief":"update-styles"}\n- webstudio mcp list-resources\n- webstudio mcp read-resource webstudio://project/guide\n- webstudio mcp read-resource webstudio://project/expressions\n\nNotes:\n\n- Use `webstudio schema mcp` for a compact machine-readable MCP tool overview. Add `--verbose` or use focused `meta.get-more-tools` calls only when exact input schemas are needed.\n- Use focused MCP tools for discovery first: `meta.index`, `meta.guide`, `meta.get-more-tools`, `components.list`, `components.summary`, `components.search`, `components.get`, `templates.list`, and `templates.get`. Protocol clients can use `resources/list` and `resources/read`; shell agents can use `webstudio mcp list-resources` and `webstudio mcp read-resource <uri>`. Read longer resources such as `webstudio://project/tools` and `webstudio://project/components` only when focused tools are insufficient.\n- `components.summary` returns counts by default; request `{"detail":"components","limit":20}` for paginated entries. Registry list tools return compact metadata, while `components.get` and `templates.get` return focused full details.\n- Read `webstudio://project/expressions` before authoring unfamiliar computed text, prop bindings, resource expressions, actions, or Collection item bindings.\n- From a shell, call one MCP tool with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio components.summary`. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `--input-file` for large payloads.\n\n## Inspect external shadcn registry items\n\nCommands:\n\n- webstudio registry inspect --source https://example.com/r/registry.json --item button --json\n- webstudio registry inspect --source ./registry.json --item dialog --json\n- webstudio registry inspect --source https://example.com/r/button.json --json\n\nNotes:\n\n- Reads a local or remote registry item without installing files or changing the configured Webstudio project.\n- Returns the item name, description, package and registry dependencies, file paths/targets, available docs, and a read-only compatibility report.\n- The report explicitly says whether installation or editable-component conversion is supported, lists declared requirements and manual steps, and says when arbitrary source code was not analyzed.\n- This is an inspection step only. It does not install files or change the configured project.\n\n## Understand what MCP can do\n\nCommands:\n\n- webstudio man mcp\n- MCP tool: meta.index {}\n- MCP tool: meta.guide {"brief":"What can Webstudio MCP do?"}\n\nMCP lets agents work on one configured Webstudio project. Agents can:\n\n- Inspect the linked project, token permissions, and latest editable build.\n- Read selected project data for audits, migrations, and repair.\n- Search values and ids across every Builder namespace without sending full project data to the model.\n- Audit accessibility, security, SEO, performance settings, unused assets, ineffective Collection styles, and unused or duplicate style data.\n- Create and edit pages, folders, redirects, breakpoints, and page templates.\n- Create pages from reusable templates.\n- Update page metadata, SEO fields, auth settings, and marketplace metadata.\n- Insert components and styled JSX sections.\n- Create data-driven lists, grids, cards, and similar repeated UI from array or object data in one Collection operation.\n- Move, copy, wrap, unwrap, convert, rename, retag, and delete elements.\n- Update text, rich text, props, bindings, and actions.\n- Create and update local styles, design tokens, style sources, and CSS variables.\n- Create static data variables and JSON variables.\n- Create HTTP, GraphQL, and system resources.\n- Use system resources for sitemap, current date, and assets.\n- Bind resources to rendered data or form/action props.\n- Manage nest Line truncated
"manual-api":
'# Webstudio API CLI Manual\n\nThe API commands operate on the single project configured by:\n\n- .webstudio/config.json: projectId\n- global Webstudio config: origin and token\n\n- Pass --json to API/discovery commands that support it. Do not add --json to top-level commands unless their help/schema documents it.\n- Never pass a project id. Commands use configured project only.\n- Read ids before writing. Do not invent ids for existing records.\n- stdout is one JSON object. stderr is diagnostics.\n- Prefer MCP semantic tools for detailed project edits. Use MCP apply-patch only when no semantic tool exists.\n\n## Start\n\n{{start}}\n\n## Read First\n\n{{readFirst}}\n\n## Project Session Cache\n\n- CLI commands use one local ProjectSession snapshot for the configured project.\n- Local-capable reads use cached namespaces when compatible and fetch only missing or stale namespaces.\n- Local-capable mutations build patches from the local snapshot, then commit with the cached build version.\n- Successful mutation commits update the local snapshot only after the remote commit succeeds.\n- Server-only commands run remotely and invalidate/refetch namespaces declared by the operation catalog.\n- Use --refresh on local-capable commands to refresh required namespaces before running.\n- Successful JSON responses include compact meta.session with operationId, buildId, version, source, committed, namespaceCounts, diagnosticCount, non-empty diagnostic summaries, and optional compatibilityVersion.\n\n## Search project data\n\nRun `webstudio search-project \'{"query":"known value"}\'` to search all Builder\nnamespaces in the local project session. Narrow it with\n`{"query":"Brand","namespaces":["styleSources","styles"]}` when only specific\nnamespaces are relevant. Results contain the current value, stable match id,\nnamespace path, owning entity, resolved references, and affected pages.\n\nValues in recognized credential fields are excluded and reported in\n`excludedSensitiveValueCount`.\n\nThe command synchronizes its required Builder namespaces, then searches locally\nand returns only matches. Namespace filters limit values matched; related\nnamespaces may still supply route and reference context, and synchronization is\nunchanged. This reduces data sent to the model, not project synchronization. It\nsearches asset metadata, not asset binaries or document contents.\n\n## CLI Capability Inventory\n\nFor a short end-consumer summary of what MCP can do, see\n`manual mcp` / `webstudio man mcp`. The MCP inventory describes the same\nproject, page, element, style, data, asset, publish, domain, and visual\nverification capabilities without internal command names.\n\n### Top-Level Commands\n\n{{topLevelCapabilityIndex}}\n\n### High-Level API Commands By Area\n\n{{apiCapabilityIndex}}\n\n### MCP Tool Operations\n\nThese are MCP tools. From a shell, call them with the shortcut form `webstudio <tool> \'<json>\'` or with the explicit form `webstudio mcp single-op-call <tool> \'<json>\'`:\n\n{{mcpOnlyCommandIndex}}\n\n## Task Recipes\n\n{{taskRecipeIndex}}\n\n## Use Case Index\n\n{{useCaseIndex}}\n\n## Known CLI Gaps\n\n{{knownCliGapIndex}}\n\n## Input File Shapes\n\n{{inputFileShapeIndex}}\n\n## Raw Patch Fallback\n\napply-patch accepts either BuildPatchTransaction[] or { "transactions": BuildPatchTransaction[] }.\n\nEach transaction has:\n\n{\n"id": "patch-transaction-label",\n"payload": [\n{\n"namespace": "projectSettings",\n"patches": [\n{ "op": "replace", "path": ["meta", "siteName"], "value": "New Site" }\n]\n}\n]\n}\n\nThe transaction id is a patch label used for optimistic synchronization. It is\nnot a Builder record id. Do not invent ids for pages, instances, props,\nbreakpoints, resources, variables, folders, assets, or other project records.\n\nPatch paths are JSON-patch-like paths into Builder store data. Map-like namespaces use ids as the first path item.\n\nSupported namespaces:\n\n- pages: redirects, page records, and folders\n- projectSettings: project-wide metadata and compiler settings\n- instances: element instances and children, including text/expression children\n- props: element props, bindings, page references, resource bindings\n- styles: CSS declarations keyed by style declaration key\n- styleSources: local style sources and reusable design tokens\n- styleSourceSelections: instance-to-style-source connections\n- dataSources: data variables, parameters, and resource data sources\n- resources: data resource definitions\n- assets: project asset records handled by the existing asset patch path\n- breakpoints: responsive breakpoints\n- marketplaceProduct: marketplace metadata\n\n## Data Sources\n\n`dataSources` is the internal Builder namespace for variables. Public API, CLI,\nand MCP tools expose it through two user-facing groups:\n\n- data variables: `list-variables`, `create-variable`, `update-variable`, and\n `delete-variable`\n- data resources: `list-resources`, `create-resource`, `update-resource`, and\Line truncated
"manual-llm":
'# Webstudio CLI Manual for LLMs\n\nUse this order. Stop only when a command returns ok:false.\n\nIf you are inside the Webstudio monorepo, the first command discovery should use\nthe local CLI exactly as `node packages/cli/local.js ...` from the repo root. Do\nnot use `packages/cli/bin.js` for local source-tree work; it is the packaged\nbuild entry and may use stale built output. Do not use `pnpm exec webstudio`,\n`pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can\nresolve an older binary.\n\nFor delegated design-system or “use every component” tasks, skip the generic warm-up sequence and start with exactly one MCP command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`. Report that returned checkpoint to the parent/user and stop until continued.\n\n## Use MCP locally or optionally connect a client\n\nDo not install, register, or connect an MCP server merely because the user asks\nyou to edit a Webstudio project. If Webstudio MCP tools are already available,\nuse them. If you have shell access, use the local CLI shortcuts such as\n`webstudio meta.index` and `webstudio list-pages`; they expose the same project\noperations without changing client configuration or restarting the app.\n\nOnly when the user explicitly asks for persistent native MCP integration, run\nthe command for their client:\n\n- Claude Code: `webstudio connect claude`\n- Codex: `webstudio connect codex`\n- Cursor: `webstudio connect cursor`\n- VS Code or GitHub Copilot: `webstudio connect vscode`\n\nRun project operations from the linked project root. If the folder is not\nlinked, ask for an editable Builder share link and run\n`webstudio init --link <share-link> --json`. You can then use local CLI\nshortcuts immediately. Do not run `webstudio sync`, `webstudio connect`, or\nrestart the app unless the user specifically wants native MCP registration:\nMCP reads and edits the latest editable Builder build directly, including for\nprojects that have never been published. Treat the share link as a credential\nand do not include it in committed files, logs, screenshots, or issue reports.\n\nThe optional `connect` command verifies project access before changing client configuration. For\nClaude Code, Cursor, and VS Code it safely merges the `webstudio` server into\nthe client\'s project configuration. For Codex it runs both `codex mcp add` and\n`codex mcp get webstudio`; do not repeat those commands separately. Follow the\nreload, restart, or approval instruction printed by `connect`, then verify the\nloaded MCP connection by asking the client to use Webstudio MCP and list the\nproject pages. Use `--print` only to inspect the generated setup without\nchanging configuration or requiring project access.\n\n## Always\n\n1. webstudio permissions --json\n2. For bounded shell workflows, call MCP tools directly through the CLI shortcut, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and useful when you need to make the MCP boundary obvious. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for multiple calls in one shared CLI session. Use a normal JSON file path for large batches. Use long-running `webstudio mcp` only when your environment is a real MCP client. Do not manually send raw JSON-RPC to `webstudio mcp` from a shell or PTY.\n3. Read MCP `meta.index`, for example `webstudio meta.index`.\n4. Use focused MCP calls with concrete JSON: `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.coverage-plan`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`.\n5. Read overview resources `webstudio://project/tools-overview` or `webstudio://project/components-overview` when useful. Read full resources `webstudio://project/tools` or `webstudio://project/components` only when focused tools are insufficient.\n6. Pick focused MCP read tool.\n7. Pick semantic MCP write tool.\n\nUse `webstudio schema mcp` for a compact MCP tool overview. Add `--verbose` only when exact input schemas for all tools are truly needed; otherwise prefer focused `meta.get-more-tools` and `components.*` calls.\n\nRun these commands from the linked project root. Use the MCP startup status line\'s absolute root for local files; write temporary scripts and artifacts under `<project root>/.temp`, not under a parent workspace.\n\nMonorepo quick path for a simple styled section:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call m Line truncated
'# Webstudio CLI Manual for LLMs\n\nUse this order. Stop only when a command returns ok:false.\n\nIf you are inside the Webstudio monorepo, the first command discovery should use\nthe local CLI exactly as `node packages/cli/local.js ...` from the repo root. Do\nnot use `packages/cli/bin.js` for local source-tree work; it is the packaged\nbuild entry and may use stale built output. Do not use `pnpm exec webstudio`,\n`pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can\nresolve an older binary.\n\nFor delegated design-system or “use every component” tasks, skip the generic warm-up sequence and start with exactly one MCP command: `webstudio workflow.next \'{"goal":"design-system-page"}\'`. Report that returned checkpoint to the parent/user and stop until continued.\n\n## Use MCP locally or optionally connect a client\n\nDo not install, register, or connect an MCP server merely because the user asks\nyou to edit a Webstudio project. If Webstudio MCP tools are already available,\nuse them. If you have shell access, use the local CLI shortcuts such as\n`webstudio meta.index` and `webstudio list-pages`; they expose the same project\noperations without changing client configuration or restarting the app.\n\nOnly when the user explicitly asks for persistent native MCP integration, run\nthe command for their client:\n\n- Claude Code: `webstudio connect claude`\n- Codex: `webstudio connect codex`\n- Cursor: `webstudio connect cursor`\n- VS Code or GitHub Copilot: `webstudio connect vscode`\n\nRun project operations from the linked project root. If the folder is not\nlinked, ask for an editable Builder share link and run\n`webstudio init --link <share-link> --json`. You can then use local CLI\nshortcuts immediately. Do not run `webstudio sync`, `webstudio connect`, or\nrestart the app unless the user specifically wants native MCP registration:\nMCP reads and edits the latest editable Builder build directly, including for\nprojects that have never been published. Treat the share link as a credential\nand do not include it in committed files, logs, screenshots, or issue reports.\n\nThe optional `connect` command verifies project access before changing client configuration. For\nClaude Code, Cursor, and VS Code it safely merges the `webstudio` server into\nthe client\'s project configuration. For Codex it runs both `codex mcp add` and\n`codex mcp get webstudio`; do not repeat those commands separately. Follow the\nreload, restart, or approval instruction printed by `connect`, then verify the\nloaded MCP connection by asking the client to use Webstudio MCP and list the\nproject pages. Use `--print` only to inspect the generated setup without\nchanging configuration or requiring project access.\n\n## Always\n\n1. webstudio permissions --json\n2. For bounded shell workflows, call MCP tools directly through the CLI shortcut, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and useful when you need to make the MCP boundary obvious. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for multiple calls in one shared CLI session. Use a normal JSON file path for large batches. Use long-running `webstudio mcp` only when your environment is a real MCP client. Do not manually send raw JSON-RPC to `webstudio mcp` from a shell or PTY.\n3. Read MCP `meta.index`, for example `webstudio meta.index`.\n4. Use focused MCP calls with concrete JSON: `webstudio meta.guide \'{"brief":"Create a design system page using every component"}\'`, `webstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'`, `webstudio components.list \'{"source":"all"}\'`, `webstudio components.coverage-plan`, `webstudio components.search \'{"brief":"radix select"}\'`, `webstudio components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`, `webstudio templates.list`, and `webstudio templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'`.\n5. Read overview resources `webstudio://project/tools-overview` or `webstudio://project/components-overview` when useful. Read full resources `webstudio://project/tools` or `webstudio://project/components` only when focused tools are insufficient.\n6. Pick focused MCP read tool.\n7. Pick semantic MCP write tool.\n\nUse `webstudio schema mcp` for a compact MCP tool overview. Add `--verbose` only when exact input schemas for all tools are truly needed; otherwise prefer focused `meta.get-more-tools` and `components.*` calls.\n\nRun these commands from the linked project root. Use the MCP startup status line\'s absolute root for local files; write temporary scripts and artifacts under `<project root>/.temp`, not under a parent workspace.\n\nMonorepo quick path for a simple styled section:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call m Line truncated
"manual-mcp":
'# Webstudio MCP Manual\n\n`webstudio mcp` starts a stdio MCP server for real MCP clients. Shell users can call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. `webstudio mcp single-op-call` is the explicit equivalent and prints the structured JSON result. `webstudio mcp run` runs multiple MCP tool calls from inline JSON or a normal JSON file in one shared CLI session. Do not manually type or pipe raw JSON-RPC frames into `webstudio mcp` from an interactive shell or PTY.\n\n## Startup\n\nIf you are already working with a shell-capable agent, it can use the local CLI\ndirectly. Native MCP client registration is optional. Give the editable Builder\nshare link only when the trusted agent asks for it. Treat the share link as a\ncredential: do not include it in committed files, screenshots, logs, or issue\nreports.\n\n1. Configure a project with `webstudio init --link <api-share-link> --json`.\n2. Check capabilities with `webstudio permissions --json`.\n3. Use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for individual MCP tool calls. Use the explicit equivalent `webstudio mcp single-op-call <tool> \'<json>\'` when you need to force the MCP path, or `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for bounded multi-call workflows. Use `webstudio mcp run .temp/mcp-calls.json` for large batches.\n4. Start discovery with `meta.index`, then call focused tools with concrete JSON, for example `webstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'`.\n\nDo not run `webstudio sync`, install an MCP server, change client configuration,\nor restart the app for this local CLI workflow.\n\nWhen the user explicitly wants persistent native MCP integration, run\n`webstudio connect claude`, `webstudio connect codex`, `webstudio connect\ncursor`, or `webstudio connect vscode`. This optional command changes client\nconfiguration, so follow its client-specific reload or restart instruction.\nUse `--print` to inspect the generated setup without changing configuration or\nrequiring project access. For Codex, `connect` registers and verifies the server\nthrough the Codex CLI. Before changing client configuration, `connect` verifies\nthat the saved project endpoint is reachable and its credential is accepted.\n\nStart MCP from the linked Webstudio project root. The lifecycle status line prints that absolute root; create local scripts, screenshots, and temporary artifacts under that root, for example `<project root>/.temp/script.mjs`. If the shell starts in a parent workspace, `cd` into the project root first or use absolute paths.\n\nWhen developing inside the Webstudio monorepo, start the local CLI exactly as `node packages/cli/local.js mcp` from the repo root. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can resolve an older binary.\n\nWhile the server is running, stdout is reserved for MCP JSON-RPC messages. Do not print human text from the server process. The server advertises MCP `logging` capability and emits sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`; stderr also mirrors these sparse lifecycle fallback lines prefixed with `[webstudio mcp]`.\n\n## One-Shot Tool Calls\n\nUse the shortcut `webstudio <tool> \'<json>\'` when you are operating from a shell and need one MCP tool result. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and avoids writing temporary Node.js stdio client scripts.\n\nExamples:\n\n```sh\nwebstudio mcp single-op-call meta.index\nwebstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio mcp single-op-call components.list \'{"source":"all"}\'\nwebstudio mcp single-op-call components.coverage-plan\nwebstudio mcp single-op-call components.search \'{"brief":"radix select"}\'\nwebstudio mcp single-op-call components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call templates.list\nwebstudio mcp single-op-call templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json\n```\n\nShortcut equivalents:\n\n```sh\nwebstudio meta.index\nwebstudio meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio components.list \'{"source":"all"}\'\nwebstudio components.coverage-plan\nwebstudio components.search \'{"brief":"radix select"}\'Line truncated
'# Webstudio MCP Manual\n\n`webstudio mcp` starts a stdio MCP server for real MCP clients. Shell users can call MCP tools with the shortcut form `webstudio <tool> \'<json>\'`, for example `webstudio meta.index` or `webstudio insert-fragment \'<json>\' --dry-run`. `webstudio mcp single-op-call` is the explicit equivalent and prints the structured JSON result. `webstudio mcp run` runs multiple MCP tool calls from inline JSON or a normal JSON file in one shared CLI session. Do not manually type or pipe raw JSON-RPC frames into `webstudio mcp` from an interactive shell or PTY.\n\n## Startup\n\nIf you are already working with a shell-capable agent, it can use the local CLI\ndirectly. Native MCP client registration is optional. Give the editable Builder\nshare link only when the trusted agent asks for it. Treat the share link as a\ncredential: do not include it in committed files, screenshots, logs, or issue\nreports.\n\n1. Configure a project with `webstudio init --link <api-share-link> --json`.\n2. Check capabilities with `webstudio permissions --json`.\n3. Use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for individual MCP tool calls. Use the explicit equivalent `webstudio mcp single-op-call <tool> \'<json>\'` when you need to force the MCP path, or `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for bounded multi-call workflows. Use `webstudio mcp run .temp/mcp-calls.json` for large batches.\n4. Start discovery with `meta.index`, then call focused tools with concrete JSON, for example `webstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'`.\n\nDo not run `webstudio sync`, install an MCP server, change client configuration,\nor restart the app for this local CLI workflow.\n\nWhen the user explicitly wants persistent native MCP integration, run\n`webstudio connect claude`, `webstudio connect codex`, `webstudio connect\ncursor`, or `webstudio connect vscode`. This optional command changes client\nconfiguration, so follow its client-specific reload or restart instruction.\nUse `--print` to inspect the generated setup without changing configuration or\nrequiring project access. For Codex, `connect` registers and verifies the server\nthrough the Codex CLI. Before changing client configuration, `connect` verifies\nthat the saved project endpoint is reachable and its credential is accepted.\n\nStart MCP from the linked Webstudio project root. The lifecycle status line prints that absolute root; create local scripts, screenshots, and temporary artifacts under that root, for example `<project root>/.temp/script.mjs`. If the shell starts in a parent workspace, `cd` into the project root first or use absolute paths.\n\nWhen developing inside the Webstudio monorepo, start the local CLI exactly as `node packages/cli/local.js mcp` from the repo root. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`: they can resolve an older binary.\n\nWhile the server is running, stdout is reserved for MCP JSON-RPC messages. Do not print human text from the server process. The server advertises MCP `logging` capability and emits sparse `notifications/message` logs for ready state and tool lifecycle checkpoints such as `tool preview.start started`, `tool preview.start still running after 10000ms`, and `tool preview.start succeeded in 1234ms`; stderr also mirrors these sparse lifecycle fallback lines prefixed with `[webstudio mcp]`.\n\n## One-Shot Tool Calls\n\nUse the shortcut `webstudio <tool> \'<json>\'` when you are operating from a shell and need one MCP tool result. The explicit form `webstudio mcp single-op-call <tool> \'<json>\'` is equivalent and avoids writing temporary Node.js stdio client scripts.\n\nExamples:\n\n```sh\nwebstudio mcp single-op-call meta.index\nwebstudio mcp single-op-call meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio mcp single-op-call components.list \'{"source":"all"}\'\nwebstudio mcp single-op-call components.coverage-plan\nwebstudio mcp single-op-call components.search \'{"brief":"radix select"}\'\nwebstudio mcp single-op-call components.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call templates.list\nwebstudio mcp single-op-call templates.get \'{"component":"@webstudio-is/sdk-components-react-radix:Select"}\'\nwebstudio mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json\n```\n\nShortcut equivalents:\n\n```sh\nwebstudio meta.index\nwebstudio meta.guide \'{"brief":"Create a design system page using every component"}\'\nwebstudio meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nwebstudio components.list \'{"source":"all"}\'\nwebstudio components.coverage-plan\nwebstudio components.search \'{"brief":"radix select"}\'Line truncated
"mcp-startup-epilogue":
'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output.\n\nPlain `webstudio mcp` starts the stdio MCP server for real MCP clients. Do not manually type or pipe raw JSON-RPC frames into it from an interactive shell or PTY. From a shell, use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for one bounded tool call. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for small multi-call batches in one shared CLI session. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nPut each local CLI call in its own shell command; do not chain helper commands with `&&`, `;`, command substitution, or shell wrappers around the CLI when reporting a CLI step. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`; those can resolve an older binary. For example:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nSave the input as `.temp/insert-fragment.json`; use single quotes for JSX attributes so the JSON needs no backslash escaping:\n\n```json\n{\n "parentInstanceId": "parent-id",\n "fragment": "<section ws:style={css`padding: 32px; display: grid; gap: 12px;`}><h2>Launch Kit</h2><p>A focused section created with Webstudio JSX.</p><button>Get started</button></section>"\n}\n```\n\nThe shorter local shortcut form is preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nFor simple authored/styled sections, run the three commands above. Do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only when you still need the target `parentInstanceId`.\n\nWhen building for a Content-mode editor, use a Content Block (`ws:block`) around every region that editor must change. Content-mode text and supported props are editable only in Content Block descendants; content outside is read-only. Keep reusable insertable source options in exactly one direct `ws:block-template` child. A missing or second template container makes the block invalid. Templates themselves are protected; an editor\'s inserted copy becomes editable Content Block body content. Verify this structure before handoff.\n\nFor an MDX-backed Content Block, use the dedicated content-source tools instead of generic prop mutations: connect, switch, inspect, edit, update frontmatter, reload, or disconnect the source. Confirm before replacing existing Body content. Prefer Markdown for standard document content; it automatically uses unique matching semantic templates. Use lowercase JSX such as `<section>` or `<svg>` for HTML or SVG that Markdown cannot express. Capitalized JSX resolves a unique stable template **Name** or a built-in MDX adapter. Add custom components to this Content Block\'s Templates before referencing them in MDX. The display label is independent. JSX attributes accept quoted values and bare booleans; expressions such as `{false}` are unsupported. Legacy `ws.element` and `ws:name` forms are compatibility input only. Component namespaces such as `$.*`, `radix.*`, and `animation.*` are unsupported; use direct component identifiers. Matching explicit children overlay designed descendants and keep template styles. A mismatched child structure replaces defaults, an explicit empty pair clears defaults, and a self-closing reference keeps them. Editing inherited default content writes it back as explicit JSX children. Template resolution is live when matching templates are added later. Preserve invalid source and unresolved names, inspect all diagnostics, and reload before retrying a revision conflict. A frontmatter update replaces the complete property map; preserve fields the user did not ask to remove. Store frontmatter images as exact `$ref` objects; bind Image sources to `.src` and alt properties to the Asset `.description`. Only exact direct frontmatter bindings may use `mode:"readwrite"`. After a template rename or deletion, use `migrate-content-block-template-references` to preview and confirm custom-template JSX and legacy changes across selected MDX files. A confirmed removal unwraps paired references and preservesLine truncated
'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output.\n\nPlain `webstudio mcp` starts the stdio MCP server for real MCP clients. Do not manually type or pipe raw JSON-RPC frames into it from an interactive shell or PTY. From a shell, use shortcut calls such as `webstudio meta.index` and `webstudio insert-fragment \'<json>\' --dry-run` for one bounded tool call. The explicit equivalent is `webstudio mcp single-op-call <tool> \'<json>\'`. Use `webstudio mcp run \'[{"tool":"components.find","input":{"brief":"button"}}]\'` for small multi-call batches in one shared CLI session. For large batches, pass a normal JSON file path such as `.temp/mcp-calls.json`. Do not use shell process substitution like `<(...)`; use inline JSON or a real file.\n\nPut each local CLI call in its own shell command; do not chain helper commands with `&&`, `;`, command substitution, or shell wrappers around the CLI when reporting a CLI step. Do not use `pnpm exec webstudio`, `pnpm --filter webstudio exec webstudio`, or a global `webstudio`; those can resolve an older binary. For example:\n\n```sh\nnode packages/cli/local.js mcp single-op-call meta.index\nnode packages/cli/local.js mcp single-op-call meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js mcp single-op-call insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nSave the input as `.temp/insert-fragment.json`; use single quotes for JSX attributes so the JSON needs no backslash escaping:\n\n```json\n{\n "parentInstanceId": "parent-id",\n "fragment": "<section ws:style={css`padding: 32px; display: grid; gap: 12px;`}><h2>Launch Kit</h2><p>A focused section created with Webstudio JSX.</p><button>Get started</button></section>"\n}\n```\n\nThe shorter local shortcut form is preferred for simple shell steps:\n\n```sh\nnode packages/cli/local.js meta.index\nnode packages/cli/local.js meta.get-more-tools \'{"tools":["insert-fragment"]}\'\nnode packages/cli/local.js insert-fragment --input-file .temp/insert-fragment.json --dry-run\n```\n\nFor simple authored/styled sections, run the three commands above. Do not grep source files, dump full MCP resources, or write parser scripts first. Use `list-pages`, `get-page-by-path`, or `list-instances` only when you still need the target `parentInstanceId`.\n\nWhen building for a Content-mode editor, use a Content Block (`ws:block`) around every region that editor must change. Content-mode text and supported props are editable only in Content Block descendants; content outside is read-only. Keep reusable insertable source options in exactly one direct `ws:block-template` child. A missing or second template container makes the block invalid. Templates themselves are protected; an editor\'s inserted copy becomes editable Content Block body content. Verify this structure before handoff.\n\nFor an MDX-backed Content Block, use the dedicated content-source tools instead of generic prop mutations: connect, switch, inspect, edit, update frontmatter, reload, or disconnect the source. Confirm before replacing existing Body content. Prefer Markdown for standard document content; it automatically uses unique matching semantic templates. Use lowercase JSX such as `<section>` or `<svg>` for HTML or SVG that Markdown cannot express. Capitalized JSX resolves a unique stable template **Name** or a built-in MDX adapter. Add custom components to this Content Block\'s Templates before referencing them in MDX. The display label is independent. JSX attributes accept quoted values and bare booleans; expressions such as `{false}` are unsupported. Legacy `ws.element` and `ws:name` forms are compatibility input only. Component namespaces such as `$.*`, `radix.*`, and `animation.*` are unsupported; use direct component identifiers. Matching explicit children overlay designed descendants and keep template styles. A mismatched child structure replaces defaults, an explicit empty pair clears defaults, and a self-closing reference keeps them. Editing inherited default content writes it back as explicit JSX children. Template resolution is live when matching templates are added later. Preserve invalid source and unresolved names, inspect all diagnostics, and reload before retrying a revision conflict. A frontmatter update replaces the complete property map; preserve fields the user did not ask to remove. Store frontmatter images as exact `$ref` objects; bind Image sources to `.src` and alt properties to the Asset `.description`. Only exact direct frontmatter bindings may use `mode:"readwrite"`. After a template rename or deletion, use `migrate-content-block-template-references` to preview and confirm custom-template JSX and legacy changes across selected MDX files. A confirmed removal unwraps paired references and preservesLine truncated
"mcp-vision":
'# MCP Vision Verification\n\n## Generated App Dependency Notes\n\n- `preview.start` and `webstudio preview` install generated app dependencies under `.webstudio/preview` and reuse them across regenerations.\n- Session previews download missing project assets into `.webstudio/assets`. If `PREVIEW_ASSET_DOWNLOAD_FAILED` occurs, restore network and project asset access, then retry `preview.start`.\n- When launcher metadata is available, dependency installation reuses a supported npm or pnpm launcher. Without launcher metadata, it defaults to npm.\n- When npm is selected, dependency installation honors `npm_config_cache`. If it is unset, preview uses the writable `.webstudio/preview/.npm-cache` directory instead of the user\'s default npm cache.\n- For npm cache permission errors, unset `npm_config_cache` to use the preview-local cache, then retry. No cache deletion is required.\n- Do not add generated-preview dependencies to the repository root `package.json` or `pnpm-lock.yaml`.\n- If dependency installation fails, the error includes the selected package-manager path and sanitized diagnostics. Check that path and the network configuration, then reinstall or update the Webstudio CLI if the problem persists.\n\n## Visual Verification Rule\n\nAsk the user whether they want visual verification before starting it, unless the user explicitly requested screenshots, visual verification, or a rendered audit. Never start `preview.start`, `screenshot`, `screenshot.diff`, `vision.install-ocr`, or a rendered audit automatically. If the user does not opt in, use focused non-visual assertions and report that vision was not run. After the user opts in, use `preview.start` and `screenshot({ path })` so vision can inspect the current MCP session and verify that generated project files are current. Iterative preview is the default: it keeps one generated-site server and browser alive, regenerates changed files, and performs an ordinary page reload without Vite HMR. Use `mode: "production"` only for release-like verification; rendered audit selects it automatically. `preview.start` is long-lived and cannot be used through one-shot `mcp single-op-call`; from a shell, use `webstudio mcp run` for preview.start/screenshot/preview.stop in one shared process, or use a long-running MCP server. When a baseline exists, use screenshot.diff to get pixel regions, OCR text changes, and diff PNG artifacts.\n\nAn authenticated project share URL is used with `webstudio init --link`; it is\nnot a generated-site preview URL. Project screenshots and rendered audits use\nthe generated local preview owned by the current CLI/MCP process. Do not pass a\nBuilder/share URL to `screenshot`, even without query parameters. Use `path`\nafter starting preview, or use `baseUrl` only for an intentional generated site\nthat is already running. Path captures verify the generated-site root marker\nand fail instead of returning a screenshot of Builder chrome.\n\n## Vision Verification Loop\n\n- Ask the user whether they want visual verification unless they explicitly requested it. Stop before calling preview, capture, comparison, OCR, or rendered-audit tools until the user opts in.\n- Make focused page/content/style changes with semantic MCP tools.\n- Call preview.start once to keep the iterative generated site running. In shell-driven workflows, run preview.start, screenshot, and preview.stop inside one `webstudio mcp run` call so they share the same preview owner.\n- Read `preview.status.stale` before relying on generated output. When present, `renderedProjectVersion` identifies the last project version materialized into the preview; a stale preview refreshes automatically on the next managed screenshot or `preview.start` call.\n- {{dependency-notes}}\n- After MCP mutations, path-based screenshots regenerate the current session in place, wait for its exact project version, and normally reload the route. The server and browser remain alive. From one-shot shell calls or another process, pass `baseUrl` with `path` to capture an already-running generated site without starting it. Use preview.stop only in the same long-running MCP server or `webstudio mcp run` process that started preview; a separate one-shot `single-op-call` process does not own another process\'s preview controller.\n- For multi-page work, capture each changed page by path through the same preview server, for example screenshot({ path: "/" }), screenshot({ path: "/pricing" }), and screenshot({ path: "/about" }). The screenshot tool navigates directly to the requested route; no browser click navigation is required.\n- For responsive work, call list-breakpoints first, then capture screenshots at viewport widths based on the Builder breakpoints plus a narrow mobile and desktop width.\n- Call screenshot with { path: "/" } or the changed page path and viewport such as { width: 375, height: 812 } and { width: 1440, height: 900 }. For an existing preview in another process, call screenshot with { baLine truncated
} as const;
+4 -2
View File
@@ -452,7 +452,8 @@ Notes:
- When a page will be handed to a Content-mode editor, wrap every region they should be able to edit in a Content Block (`ws:block`). Content-mode editors can edit text and supported props only in Content Block descendants. Content outside those blocks remains read-only, even when it looks like ordinary editable text.
- Put reusable insertable options inside exactly one direct `ws:block-template` child. A missing or second template container makes the Content Block invalid. A template is source material, not editor content: editors cannot edit or delete it directly. When an editor inserts a template, its copy becomes a direct child of the Content Block and is editable.
- Before handing off a page, verify with `inspect-instance` that the intended text, images, and links are inside a Content Block, and that templates include all required styling because Content-mode editors cannot use the Style panel.
- Before implementation, inventory every editor-owned field and its intended control and write destination, whether or not the block uses MDX. Check that templates contain the required styling, but do not treat containment or a successful text edit as proof that the whole block is editable. Test every field through Content mode, inspect its saved project data or source file, reload, and restore the original value. Report passed, failed, or not tested for each field; missing or read-only required controls are unfinished requirements.
- Use controls that match the content: a calendar for dates and an image picker for image replacement. Keep fixed punctuation and units out of directly bound value elements. Preserve stored types and wording: when reading time is already a complete string such as `4 min read`, bind that string directly rather than assuming it is a number.
- For MDX-backed blocks, containment alone is not enough: the MDX body is editable through its source mapping, but the designed shell needs writable document-frontmatter bindings. Follow the binding recipe below rather than binding editable article fields to query results.
## Store Content Block content in MDX
@@ -512,7 +513,8 @@ Notes:
- Keep every intended editable value in its own text element with a single direct read-write binding. For reading time, use three inline siblings: static `— `, the bound reading-time value, and static ` min read`. Preserve whitespace and the stored field type. Do not combine them into a template literal or concatenate strings, and do not put literal siblings inside the value element itself. Keep fixed wording protected in the designed shell.
- Prefer component formatting controls, such as Date Time formatting with a directly bound date prop, over transforming the expression. Do not silently sacrifice editability for formatting or a fallback; explain unsupported cases and ask before making an intended editable field read-only.
- A direct writable binding such as `document.frontmatter.author.name` can edit a shared author loaded through `../authors/oleg.md#frontmatter`. The edit saves to the author file and affects every article using it; preserve the article's `$ref` marker.
- Before handoff, inspect each intended editable field's document path and binding mode. Correct rendering or containment does not prove editability. Verify a representative Content-mode edit saves to the intended MDX field and survives reload, then restore the test value. Report when this UI verification was not run.
- Before handoff, inventory every article-owned field, including header text, author details, dates, reading time, categories, hero and inline image sources, alternative text, captions, links, and custom-component content. For each field, inspect its binding and source, edit it through the Content-mode UI, verify the saved MDX or referenced file, reload, and restore the test value. Record passed, failed, or not tested for each field. A correct preview, a successful MCP write, and one representative text edit do not prove the whole article is editable. Do not claim completion while required fields fail or remain untested.
- Bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. Content mode's **Choose source** replaces the article's frontmatter image `$ref`; the resolved URL stays read-only. Verify selection, the saved reference, and reload instead of treating a missing write mode as a platform limitation. For alternative text bound to `.description`, use **Choose source → asset actions → Settings → Description** to edit shared Asset metadata. This affects every use of the Asset and is separate from replacing an article's image.
## Move elements
+18 -1
View File
@@ -94,7 +94,24 @@ When the Content Block body must be stored in an `.mdx` Asset, use the dedicated
Frontmatter is part of the same MDX source. `update-content-block-frontmatter` receives the complete replacement property map, so inspect and preserve properties the user did not ask to remove. Store frontmatter images as exact `$ref` objects, bind Image sources to their resolved `.src`, and bind alt properties to their Asset `.description`. Use `update-content-block-frontmatter` for MCP frontmatter edits; MDX-rendered elements are not persistent targets for generic prop or text mutations. Preserve existing `mode:"readwrite"` bindings, which are valid only for exact direct frontmatter paths. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. For an expression-bound source inside a Collection, pass the occurrence's scoped values and a distinct stable `renderScope`; for example, resolve `post.assetId` with `variables:{"post":{"assetId":"<mdxAssetId>"}}`.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Before handoff, verify the intended fields' document paths and write modes, and verify a representative Content-mode edit saves and survives reload; restore the test value and disclose if UI verification was not run.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Follow the complete article editability checklist below before handoff.
### Content Block completion checklist
For every Content Block creation, migration, or repair—with or without MDX—treat Content-mode editability as a delivery requirement for all content the user expects editors to change. Before implementation, inventory each value, its intended UI control, and its write destination. Choose controls by meaning: date/calendar controls for dates, image pickers for image replacement, and direct text or number controls for reading time. Keep fixed punctuation and units outside a directly bound value element; never mix literal and expression children there. Preserve the existing stored type and wording: if `readTime` already stores `4 min read`, bind that whole string directly instead of assuming it stores only a number. Distinguish protected layout/templates from editable content explicitly. Test each field through Content mode, check the saved project data or source file, reload, restore test values, and record passed/failed/not tested. Testing one heading, inspecting bindings, or successfully writing through MCP does not cover the remaining fields. Report missing controls and unsupported writes as unfinished requirements, not successful delivery.
### Complete article editability is required
When the user asks for the whole article to be editable in Content mode, this includes every article-owned value: title, excerpt, author details and links, dates, reading time, categories, hero and inline image sources, alternative text, captions, body text, links, and custom-component content and media. Exclude only designer-owned layout, styling, templates, and shared navigation/footer unless the user asks otherwise. A correct preview, a successful MCP write, or an editable body does not prove this requirement is met.
Before handoff:
1. Inventory every article field, its UI control, binding, and source file/field. Header fields outside **MDX content** are still part of the article and must be editable through the Content Block's document bindings.
2. In Content mode, edit every inventoried field through the UI, including choosing a different hero image and editing link destinations and custom-component props. Do not substitute a raw MDX/YAML edit or an MCP write for this check.
3. Read the saved source after each edit. Confirm the intended MDX body/frontmatter or referenced author file changed, unrelated content stayed intact, and the edit survives reload. Restore test values and verify restoration.
4. Mark each field passed, failed, or not tested. Do not claim the article is fully editable while any required field is failed or untested.
**Image replacement is not shared metadata editing.** For frontmatter such as `featureImage: { $ref: "./images/hero.png" }`, bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. In Content mode, **Choose source** replaces the article’s `featureImage.$ref`; it does not overwrite the shared Asset’s resolved `.src`. The resolved URL field stays read-only. Verify the picker, saved reference, and reload—do not infer support from the displayed image alone. Bind alternative text to `.description` to use the shared Asset description; edit it through **Choose source → asset actions → Settings → Description**, which affects every use of that Asset. Do not treat a missing write mode as a platform limitation, and do not defer image replacement over a separate shared-description question.
Keep intended editable values separate from display formatting. For reading time, use three inline sibling text elements: static `— `, one value element bound directly to `document.frontmatter.readingTime` with `expressionBindingMode:"readwrite"`, and static ` min read`. The value element must contain only that binding, not mixed literal and expression children. Preserve whitespace and the stored field type. Do not concatenate labels or units, use template literals, or add fallbacks/formatting calls to an editable binding. Prefer component formatting controls with a directly bound value. Explain unsupported cases and ask before making an intended editable field read-only; do not trade away editability just to match the display.
+18 -1
View File
@@ -164,7 +164,24 @@ Use the dedicated connect, switch, inspect, edit, update-frontmatter, reload, an
A source edit replaces the complete MDX document. A frontmatter update replaces the complete frontmatter property map. Inspect the current source first and preserve everything the user did not ask to change. Store frontmatter images as exact `$ref` objects, bind Image sources to their resolved `.src`, and bind alt properties to their Asset `.description`. Use `update-content-block-frontmatter` for MCP frontmatter edits. MDX-rendered elements are not persistent instance targets for generic `bind-props` or `update-text` calls. Preserve existing `mode:"readwrite"` bindings when encountered; they are valid only for exact direct paths into the connected document's frontmatter. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Before handoff, verify the intended fields' document paths and write modes, and verify a representative Content-mode edit saves and survives reload; restore the test value and disclose if UI verification was not run.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Follow the complete article editability checklist below before handoff.
### Content Block completion checklist
For every Content Block creation, migration, or repair—with or without MDX—treat Content-mode editability as a delivery requirement for all content the user expects editors to change. Before implementation, inventory each value, its intended UI control, and its write destination. Choose controls by meaning: date/calendar controls for dates, image pickers for image replacement, and direct text or number controls for reading time. Keep fixed punctuation and units outside a directly bound value element; never mix literal and expression children there. Preserve the existing stored type and wording: if `readTime` already stores `4 min read`, bind that whole string directly instead of assuming it stores only a number. Distinguish protected layout/templates from editable content explicitly. Test each field through Content mode, check the saved project data or source file, reload, restore test values, and record passed/failed/not tested. Testing one heading, inspecting bindings, or successfully writing through MCP does not cover the remaining fields. Report missing controls and unsupported writes as unfinished requirements, not successful delivery.
### Complete article editability is required
When the user asks for the whole article to be editable in Content mode, this includes every article-owned value: title, excerpt, author details and links, dates, reading time, categories, hero and inline image sources, alternative text, captions, body text, links, and custom-component content and media. Exclude only designer-owned layout, styling, templates, and shared navigation/footer unless the user asks otherwise. A correct preview, a successful MCP write, or an editable body does not prove this requirement is met.
Before handoff:
1. Inventory every article field, its UI control, binding, and source file/field. Header fields outside **MDX content** are still part of the article and must be editable through the Content Block's document bindings.
2. In Content mode, edit every inventoried field through the UI, including choosing a different hero image and editing link destinations and custom-component props. Do not substitute a raw MDX/YAML edit or an MCP write for this check.
3. Read the saved source after each edit. Confirm the intended MDX body/frontmatter or referenced author file changed, unrelated content stayed intact, and the edit survives reload. Restore test values and verify restoration.
4. Mark each field passed, failed, or not tested. Do not claim the article is fully editable while any required field is failed or untested.
**Image replacement is not shared metadata editing.** For frontmatter such as `featureImage: { $ref: "./images/hero.png" }`, bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. In Content mode, **Choose source** replaces the article’s `featureImage.$ref`; it does not overwrite the shared Asset’s resolved `.src`. The resolved URL field stays read-only. Verify the picker, saved reference, and reload—do not infer support from the displayed image alone. Bind alternative text to `.description` to use the shared Asset description; edit it through **Choose source → asset actions → Settings → Description**, which affects every use of that Asset. Do not treat a missing write mode as a platform limitation, and do not defer image replacement over a separate shared-description question.
Keep intended editable values separate from display formatting. For reading time, use three inline sibling text elements: static `— `, one value element bound directly to `document.frontmatter.readingTime` with `expressionBindingMode:"readwrite"`, and static ` min read`. The value element must contain only that binding, not mixed literal and expression children. Preserve whitespace and the stored field type. Do not concatenate labels or units, use template literals, or add fallbacks/formatting calls to an editable binding. Prefer component formatting controls with a directly bound value. Explain unsupported cases and ask before making an intended editable field read-only; do not trade away editability just to match the display.
+18 -1
View File
@@ -33,7 +33,24 @@ When building for a Content-mode editor, use a Content Block (`ws:block`) around
For an MDX-backed Content Block, use the dedicated content-source tools instead of generic prop mutations: connect, switch, inspect, edit, update frontmatter, reload, or disconnect the source. Confirm before replacing existing Body content. Prefer Markdown for standard document content; it automatically uses unique matching semantic templates. Use lowercase JSX such as `<section>` or `<svg>` for HTML or SVG that Markdown cannot express. Capitalized JSX resolves a unique stable template **Name** or a built-in MDX adapter. Add custom components to this Content Block's Templates before referencing them in MDX. The display label is independent. JSX attributes accept quoted values and bare booleans; expressions such as `{false}` are unsupported. Legacy `ws.element` and `ws:name` forms are compatibility input only. Component namespaces such as `$.*`, `radix.*`, and `animation.*` are unsupported; use direct component identifiers. Matching explicit children overlay designed descendants and keep template styles. A mismatched child structure replaces defaults, an explicit empty pair clears defaults, and a self-closing reference keeps them. Editing inherited default content writes it back as explicit JSX children. Template resolution is live when matching templates are added later. Preserve invalid source and unresolved names, inspect all diagnostics, and reload before retrying a revision conflict. A frontmatter update replaces the complete property map; preserve fields the user did not ask to remove. Store frontmatter images as exact `$ref` objects; bind Image sources to `.src` and alt properties to the Asset `.description`. Only exact direct frontmatter bindings may use `mode:"readwrite"`. After a template rename or deletion, use `migrate-content-block-template-references` to preview and confirm custom-template JSX and legacy changes across selected MDX files. A confirmed removal unwraps paired references and preserves their authored children; a self-closing reference disappears because it has none.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Before handoff, verify the intended fields' document paths and write modes, and verify a representative Content-mode edit saves and survives reload; restore the test value and disclose if UI verification was not run.
For an editable MDX article, use the resource to select the Content Block's source Asset (`post.data.id`), then bind article fields inside the block to its document parameter (`document.frontmatter.title`), not query-result properties. Inspect the actual document variable name first. Create writable bindings explicitly: `update-text` uses `expressionBindingMode:"readwrite"`; `bind-props` uses `binding.mode:"readwrite"`. These generic tools target persistent designed instances, such as a header outside the MDX body, not MDX-generated instances. The MDX body is editable through its source mapping; merely placing static or query-bound content inside the block does not make it editable. Use exact direct paths; property access is already safe. Direct bindings through a loaded Markdown or MDX `$ref` ending in `#frontmatter` save to the referenced file, with its write permissions enforced. Shared-record edits affect every document using that record. Computed expressions, JSON/body references, and resolved image metadata remain read-only. Follow the complete article editability checklist below before handoff.
### Content Block completion checklist
For every Content Block creation, migration, or repair—with or without MDX—treat Content-mode editability as a delivery requirement for all content the user expects editors to change. Before implementation, inventory each value, its intended UI control, and its write destination. Choose controls by meaning: date/calendar controls for dates, image pickers for image replacement, and direct text or number controls for reading time. Keep fixed punctuation and units outside a directly bound value element; never mix literal and expression children there. Preserve the existing stored type and wording: if `readTime` already stores `4 min read`, bind that whole string directly instead of assuming it stores only a number. Distinguish protected layout/templates from editable content explicitly. Test each field through Content mode, check the saved project data or source file, reload, restore test values, and record passed/failed/not tested. Testing one heading, inspecting bindings, or successfully writing through MCP does not cover the remaining fields. Report missing controls and unsupported writes as unfinished requirements, not successful delivery.
### Complete article editability is required
When the user asks for the whole article to be editable in Content mode, this includes every article-owned value: title, excerpt, author details and links, dates, reading time, categories, hero and inline image sources, alternative text, captions, body text, links, and custom-component content and media. Exclude only designer-owned layout, styling, templates, and shared navigation/footer unless the user asks otherwise. A correct preview, a successful MCP write, or an editable body does not prove this requirement is met.
Before handoff:
1. Inventory every article field, its UI control, binding, and source file/field. Header fields outside **MDX content** are still part of the article and must be editable through the Content Block's document bindings.
2. In Content mode, edit every inventoried field through the UI, including choosing a different hero image and editing link destinations and custom-component props. Do not substitute a raw MDX/YAML edit or an MCP write for this check.
3. Read the saved source after each edit. Confirm the intended MDX body/frontmatter or referenced author file changed, unrelated content stayed intact, and the edit survives reload. Restore test values and verify restoration.
4. Mark each field passed, failed, or not tested. Do not claim the article is fully editable while any required field is failed or untested.
**Image replacement is not shared metadata editing.** For frontmatter such as `featureImage: { $ref: "./images/hero.png" }`, bind the Image source directly to `document.frontmatter.featureImage.src` with `binding.mode:"readwrite"` using `bind-props`. In Content mode, **Choose source** replaces the article’s `featureImage.$ref`; it does not overwrite the shared Asset’s resolved `.src`. The resolved URL field stays read-only. Verify the picker, saved reference, and reload—do not infer support from the displayed image alone. Bind alternative text to `.description` to use the shared Asset description; edit it through **Choose source → asset actions → Settings → Description**, which affects every use of that Asset. Do not treat a missing write mode as a platform limitation, and do not defer image replacement over a separate shared-description question.
Keep intended editable values separate from display formatting. For reading time, use three inline sibling text elements: static `— `, one value element bound directly to `document.frontmatter.readingTime` with `expressionBindingMode:"readwrite"`, and static ` min read`. The value element must contain only that binding, not mixed literal and expression children. Preserve whitespace and the stored field type. Do not concatenate labels or units, use template literals, or add fallbacks/formatting calls to an editable binding. Prefer component formatting controls with a directly bound value. Explain unsupported cases and ask before making an intended editable field read-only; do not trade away editability just to match the display.
@@ -28,6 +28,8 @@ export const inputFieldTypes = [
"url",
"number",
"search",
"date",
"datetime-local",
] as const;
export const inputFieldColors = ["placeholder", "set", "error"] as const;
+2 -2
View File
@@ -4,9 +4,9 @@ export const projectBuildDocs = {
"accessibility-review":
'<!-- Adapted for Webstudio MCP from Community-Access/accessibility-agents, web-accessibility-wizard/SKILL.md (MIT): https://github.com/Community-Access/accessibility-agents/blob/main/codex-skills/web-accessibility-wizard/SKILL.md -->\n\n# Webstudio Accessibility Review\n\nUse this workflow when creating or reviewing a page, component, form, menu,\ndialog, or responsive layout. It is an LLM-assisted review, not proof of WCAG\nconformance. Do not claim that a page is accessible solely because this review\nhas no findings.\n\n## Evidence First\n\n1. Read `meta.index` and use focused project tools. Do not inspect Webstudio\n source code or generated files for normal project reviews.\n2. Run `audit` with `{"scopes":["accessibility"]}` before changing\n anything. It detects deterministic metadata issues such as labels, landmark\n structure, positive tabindex values, invalid static ARIA states, and unmuted\n autoplay. Fix confirmed static findings first.\n3. Ask whether the user wants visual verification unless they explicitly\n requested it. Only after they opt in, use `preview.start` and capture each\n changed route with `screenshot` at desktop, tablet, and mobile widths. Use\n `waitForSelector` or `waitForTimeout` only when the page has an actual\n delayed ready state. If they do not opt in, continue with static evidence\n and mark visual findings as manual verification items.\n4. Use `get-page-by-path`, `list-instances`, `inspect-instance`, and focused\n prop/style reads to verify semantics and authored state that screenshots\n cannot prove.\n5. Make only supported, evidence-based fixes with semantic MCP tools. Recheck\n the affected route and the static accessibility search after every fix.\n\n## Review Areas\n\nReview these areas in priority order. Record the evidence for every finding.\n\n### 1. Names, Semantics, and Structure\n\n- Every image has purposeful alternative text or an explicitly empty alt when\n decorative. Image-submit inputs need a non-empty alt label describing their\n action.\n- For `missing-image-description` findings, inspect the rendered image in its\n page context and call `set-image-descriptions` with either a concise generated\n description or `decorative: true`. Re-run the audit after saving the decision.\n- Buttons, links, icon-only controls, and form controls have accessible names.\n- Prefer native HTML semantics over ARIA roles.\n- Non-native elements using `role="button"` or `role="link"` must be\n keyboard-focusable.\n- Use one sensible page h1, ordered heading levels, meaningful link text, and\n a main landmark.\n- Iframes have titles. Static HTML ids are unique, ARIA reference props point\n to real ids, and static boolean/select/numeric ARIA values are valid.\n- Focusable controls must not be hidden from assistive technology with\n `aria-hidden`.\n\n### 2. Keyboard and Focus\n\n- Interactive controls should be native or have a documented keyboard model.\n- Visible focus must not be removed without a visible replacement.\n- Hover-only information or controls need a keyboard-accessible equivalent.\n- Do not use positive tabindex values.\n- Mark keyboard behavior as **manual verification required** when the available\n evidence cannot prove tab order, Escape handling, or focus restoration.\n\n### 3. Dialogs, Menus, and Other Overlays\n\n- Dialogs need an accessible name and a close action.\n- Opening an overlay should move focus into it, trap focus where appropriate,\n and restore focus to its trigger on close.\n- Menus, popovers, disclosures, and tabs must expose their expanded/selected\n state and remain usable without a pointer.\n- If the current MCP/browser tools cannot exercise the interaction, report it\n as a manual verification item instead of guessing.\n\n### 4. Forms and Errors\n\n- Inputs have labels, required state, helpers, and errors associated with the\n right control.\n- Invalid controls expose `aria-invalid` when applicable.\n- Error, loading, and success feedback is not conveyed only by color or a\n transient visual toast.\n- Submit controls do not become unexplained dead ends when disabled.\n\n### 5. Visual, Responsive, Media, and Motion\n\n- At each target width, text remains readable, controls remain reachable, and\n no content is clipped, overlapped, or dependent on hover alone.\n- Do not rely only on color, position, shape, or iconography to convey meaning.\n- Keep non-text contrast, text contrast, focus indicators, and small text as\n visual review items unless measured evidence is available.\n- Videos with meaningful speech need captions; non-essential motion should\n respect reduced-motion preferences. Do not autoplay sound.\n\n### 6. Dynamic Content and Data\n\n- Loading, empty, error, and success states have understandable visible text.\n- Important dynamic updates and validation feedback need an appropriate\n announcement strategy.\n- Check repeated resource-driven content for unique labels, headings, idLine truncated
expressions:
'# Webstudio Expressions\n\nUse an expression only when a value must be computed at runtime from scoped\ndata. Use direct text and direct prop values for fixed content.\n\n## Source Format\n\nExpression-capable MCP fields receive JavaScript expression source as a JSON\nstring. Send one expression, without a `return` statement or surrounding\nfunction. For example:\n\n```json\n{ "binding": { "type": "expression", "value": "post.title ?? \\"Untitled\\"" } }\n```\n\nInside that string, use readable JavaScript syntax rather than serialized JSON.\nFor object expressions, leave identifier property names unquoted, for example\n`{ query: queryText, variables: { slug: system.params.slug } }`. Quote a property\nname only when JavaScript requires it, such as `{ "published-at": date }`. Do\nnot pass a JSON-stringified object as an expression.\n\nDo not send a fixed prop string as an expression. Use `update-props` with\n`type:"string"`. Page metadata and resource URLs accept plain fixed strings and\nnormalize them for storage. Expression-only resource headers, search parameters,\nand bodies accept `{ "type": "literal", "value": "fixed text" }` when the\nvalue is not dynamic.\n\n## Read and read-write bindings\n\nExpression bindings use `mode:"read"` by default. Use `mode:"readwrite"` only\nfor an exact direct path into the frontmatter of an MDX source connected to the\ncontaining Content Block, for example:\n\n```json\n{\n "binding": {\n "type": "expression",\n "value": "document.frontmatter.title",\n "mode": "readwrite"\n }\n}\n```\n\nThis lets Content mode save a direct edit back to that frontmatter field.\nComputed expressions, including fallbacks and concatenation, are always\nread-only. The mutation is rejected when the expression is not a direct path or\ndoes not belong to a connected Content Block document. Use the variable name\nreturned by `inspect-instance`; do not assume it is `document`.\n\nFor an MDX-backed article, use the query resource to select the Content Block\'s\nsource Asset, for example `post.data.id`. Inside the block, bind article fields\nto the block\'s document parameter, not `post.data.properties.title` or other\nequivalent query-result values. A query binding can display the correct value\nwithout supporting Content-mode edits.\n\nThe MDX body is editable through its source mapping. Designer-created elements\noutside that body, such as an article header, stay protected unless their text\nor supported props have writable frontmatter bindings. Containment in the\nContent Block alone is not enough. Use `update-text` with\n`expressionBindingMode:"readwrite"` for a designed heading\'s text, and\n`bind-props` with `binding.mode:"readwrite"` for a supported prop. These target\npersistent designed instances, not MDX-generated instances.\n\nUse direct paths for writable bindings, such as\n`document.frontmatter.title`; property access is already safe. Adding `??`\nfallbacks or formatting makes the expression read-only. Direct bindings through\nloaded Markdown or MDX `$ref` values ending in `#frontmatter` save to the\nreferenced file, with its write permissions enforced. For example, editing\n`document.frontmatter.author.name` updates the shared author file, affecting\nevery article using it while preserving the article\'s reference. JSON/body\nreferences and resolved image metadata remain read-only.\n\n### Keep editable values separate from formatting\n\nWhen a field must be editable in Content mode, do not concatenate its value\nwith labels, units, or punctuation, or wrap it in a template literal, fallback,\nor formatting call. Keep the value in its own text element with one direct\nread-write binding. Put the fixed prefix and suffix in separate sibling text\nelements. Do not mix literal and expression children in the value element.\n\nFor example, display reading time as three inline siblings: fixed `— `,\nan element bound to `document.frontmatter.readingTime`, and fixed ` min read`.\nSet `expressionBindingMode:"readwrite"` on the middle element with `update-text`.\nThe number remains editable while the surrounding wording stays protected.\nPreserve spacing and the field\'s stored type. For dates, prefer a Date Time\ncomponent with its date prop bound directly and formatting configured separately.\n\nDo not silently choose a read-only expression to achieve the requested display.\nIf a required transformation has no editable presentation, explain the limitation\nand ask before making that field read-only. This does not extend the supported\nwrite targets or override permissions.\n\n## Scope\n\n- Data variables are available on their scope instance and descendants.\n- An inner variable with the same name masks the outer variable.\n- A scoped resource result is a variable. Read its payload from its result\n wrapper, usually `resourceName.data`; APIs may nest the desired value deeper.\n- Collection creates internal `collectionItem` and `collectionItemKey`\n parameters. They are availaLine truncated
'# Webstudio Expressions\n\nUse an expression only when a value must be computed at runtime from scoped\ndata. Use direct text and direct prop values for fixed content.\n\n## Source Format\n\nExpression-capable MCP fields receive JavaScript expression source as a JSON\nstring. Send one expression, without a `return` statement or surrounding\nfunction. For example:\n\n```json\n{ "binding": { "type": "expression", "value": "post.title ?? \\"Untitled\\"" } }\n```\n\nInside that string, use readable JavaScript syntax rather than serialized JSON.\nFor object expressions, leave identifier property names unquoted, for example\n`{ query: queryText, variables: { slug: system.params.slug } }`. Quote a property\nname only when JavaScript requires it, such as `{ "published-at": date }`. Do\nnot pass a JSON-stringified object as an expression.\n\nDo not send a fixed prop string as an expression. Use `update-props` with\n`type:"string"`. Page metadata and resource URLs accept plain fixed strings and\nnormalize them for storage. Expression-only resource headers, search parameters,\nand bodies accept `{ "type": "literal", "value": "fixed text" }` when the\nvalue is not dynamic.\n\n## Read and read-write bindings\n\nExpression bindings use `mode:"read"` by default. Use `mode:"readwrite"` only\nfor an exact direct path into the frontmatter of an MDX source connected to the\ncontaining Content Block, for example:\n\n```json\n{\n "binding": {\n "type": "expression",\n "value": "document.frontmatter.title",\n "mode": "readwrite"\n }\n}\n```\n\nThis lets Content mode save a direct edit back to that frontmatter field.\nComputed expressions, including fallbacks and concatenation, are always\nread-only. The mutation is rejected when the expression is not a direct path or\ndoes not belong to a connected Content Block document. Use the variable name\nreturned by `inspect-instance`; do not assume it is `document`.\n\nFor an MDX-backed article, use the query resource to select the Content Block\'s\nsource Asset, for example `post.data.id`. Inside the block, bind article fields\nto the block\'s document parameter, not `post.data.properties.title` or other\nequivalent query-result values. A query binding can display the correct value\nwithout supporting Content-mode edits.\n\nThe MDX body is editable through its source mapping. Designer-created elements\noutside that body, such as an article header, stay protected unless their text\nor supported props have writable frontmatter bindings. Containment in the\nContent Block alone is not enough. Use `update-text` with\n`expressionBindingMode:"readwrite"` for a designed heading\'s text, and\n`bind-props` with `binding.mode:"readwrite"` for a supported prop. These target\npersistent designed instances, not MDX-generated instances.\n\nUse direct paths for writable bindings, such as\n`document.frontmatter.title`; property access is already safe. Adding `??`\nfallbacks or formatting makes the expression read-only. Direct bindings through\nloaded Markdown or MDX `$ref` values ending in `#frontmatter` save to the\nreferenced file, with its write permissions enforced. For example, editing\n`document.frontmatter.author.name` updates the shared author file, affecting\nevery article using it while preserving the article\'s reference. JSON/body\nreferences and resolved image metadata remain read-only.\n\n### Verify the whole article, not just its body\n\nIf the user asks for full Content-mode editing, inventory every article field,\nincluding the designed header, hero image source, alternative text, captions,\nauthor details, dates, reading time, links, and custom-component content.\nVerify each field through its Content-mode UI, inspect the saved MDX or\nreferenced file, reload, then restore and verify the original value. A valid\nbinding, correct preview, or successful MCP edit is not a UI-editability test.\nReport each field as passed, failed, or not tested; do not call the article\nfully editable with required fields missing or untested.\n\nFor Image sources, bind `document.frontmatter.featureImage.src` with\n`binding.mode:"readwrite"` using `bind-props`. Content mode\'s **Choose source**\nreplaces the article\'s frontmatter image `$ref`; the resolved URL field remains\nread-only. Test this picker and the saved reference instead of assuming that\ndisplaying an image proves replacement works.\n\nAn alternative-text binding to the image\'s `.description` uses shared Asset\nmetadata. Edit it through **Choose source → asset actions → Settings →\nDescription**; that edit affects every use of the Asset. Image replacement\nand shared-description editing are separate actions, not reasons to leave\nrequired image content uneditable.\n\n### Keep editable values separate from formatting\n\nWhen a field must be editable in Content mode, do not concatenate its value\nwith labels, units, or punctuation, or wrap it in a template literal, fallback,\nor formatting call. Keep the value in its ownLine truncated
"mcp-startup-guidance":
'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output. Before editing a Webstudio project, do one small discovery step, then act. For delegated design-system or “use every component” tasks, the first MCP command is `workflow.next {"goal":"design-system-page"}`; report that returned checkpoint to the parent/user and stop until continued. For a simple authored/styled section, the intended path is literal: call `meta.index`, call `meta.get-more-tools` with `{"tools":["insert-fragment"]}`, read the target parent id with `list-pages`/`get-page-by-path`/`list-instances` only if needed, then call `insert-fragment`. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. Do not grep source files, dump full MCP resources, or write parser scripts before trying focused MCP calls. Good first calls are `meta.index` for orientation, `meta.guide` for a goal workflow, `workflow.next` for one bounded delegated phase, `meta.get-more-tools` for exact tool schemas, and `components.coverage-plan`, `components.coverage-insert-next`, `components.find`, or `components.get` for component work. Do not call every discovery tool up front. For delegated or non-streaming agents whose parent cannot see live stderr/stdout, treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report its command/result, and wait for the parent before the next MCP command. Do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands; they hide progress from the parent. Do not take a broad task such as creating a full design-system page as one execution unit. Instead call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint to the parent, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For design-system or “use every component” tasks, start with `workflow.next`, acknowledge its checkpoint only after parent/user continuation, then run compact `components.coverage-plan`, checkpoint, create the page, checkpoint, then call `components.coverage-insert-next` once per coverage checkpoint, and finish with the `presentation-pass` workflow phase. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world design-system examples instead of raw unstyled component dumps. Request component coverage details with `components.coverage-plan {"detail":"roots"}` or `components.coverage-plan {"detail":"parts"}` only when needed. `list-pages` does not accept `detail`; use `list-pages {}` or `get-page-by-path` for page lookup. Read overview resources such as `webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview` only when focused tools are insufficient. Do not dump or parse full resources such as `webstudio://project/tools` or `webstudio://project/components` unless the focused tools are insufficient. Run one-shot shortcut or explicit `webstudio mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. For experiments, pass `--dry-run` to local-capable mutations. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project. If a one-shot call returns checkpoint.required, the CLI persists `CHECKPOINT_REQUIRED` across later one-shot calls until you report the checkpoint, wait until continued, and call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially. Complete one bounded phase and return a checkpoint before continuing. Follow the guide: read ids and Builder breakpoints first, use focused component discovery for component ids, props, and content model composition, prefer semantic tools over `apply-patch`, use direct value tools for fixed text/props, use bindings only for dynamic expressions/resources/actions, page metadata and fixed resource URLs accept plain strings, put page/resource update fields under `va Line truncated
'If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output. Before editing a Webstudio project, do one small discovery step, then act. For delegated design-system or “use every component” tasks, the first MCP command is `workflow.next {"goal":"design-system-page"}`; report that returned checkpoint to the parent/user and stop until continued. For a simple authored/styled section, the intended path is literal: call `meta.index`, call `meta.get-more-tools` with `{"tools":["insert-fragment"]}`, read the target parent id with `list-pages`/`get-page-by-path`/`list-instances` only if needed, then call `insert-fragment`. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. Do not grep source files, dump full MCP resources, or write parser scripts before trying focused MCP calls. Good first calls are `meta.index` for orientation, `meta.guide` for a goal workflow, `workflow.next` for one bounded delegated phase, `meta.get-more-tools` for exact tool schemas, and `components.coverage-plan`, `components.coverage-insert-next`, `components.find`, or `components.get` for component work. Do not call every discovery tool up front. For delegated or non-streaming agents whose parent cannot see live stderr/stdout, treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report its command/result, and wait for the parent before the next MCP command. Do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands; they hide progress from the parent. Do not take a broad task such as creating a full design-system page as one execution unit. Instead call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint to the parent, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For design-system or “use every component” tasks, start with `workflow.next`, acknowledge its checkpoint only after parent/user continuation, then run compact `components.coverage-plan`, checkpoint, create the page, checkpoint, then call `components.coverage-insert-next` once per coverage checkpoint, and finish with the `presentation-pass` workflow phase. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world design-system examples instead of raw unstyled component dumps. Request component coverage details with `components.coverage-plan {"detail":"roots"}` or `components.coverage-plan {"detail":"parts"}` only when needed. `list-pages` does not accept `detail`; use `list-pages {}` or `get-page-by-path` for page lookup. Read overview resources such as `webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview` only when focused tools are insufficient. Do not dump or parse full resources such as `webstudio://project/tools` or `webstudio://project/components` unless the focused tools are insufficient. Run one-shot shortcut or explicit `webstudio mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. For experiments, pass `--dry-run` to local-capable mutations. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project. If a one-shot call returns checkpoint.required, the CLI persists `CHECKPOINT_REQUIRED` across later one-shot calls until you report the checkpoint, wait until continued, and call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially. Complete one bounded phase and return a checkpoint before continuing. Follow the guide: read ids and Builder breakpoints first, use focused component discovery for component ids, props, and content model composition, prefer semantic tools over `apply-patch`, use direct value tools for fixed text/props, use bindings only for dynamic expressions/resources/actions, page metadata and fixed resource URLs accept plain strings, put page/resource update fields under `va Line truncated
} as const;
export type ProjectBuildDocName = keyof typeof projectBuildDocs;
@@ -70,6 +70,29 @@ referenced file, with its write permissions enforced. For example, editing
every article using it while preserving the article's reference. JSON/body
references and resolved image metadata remain read-only.
### Verify the whole article, not just its body
If the user asks for full Content-mode editing, inventory every article field,
including the designed header, hero image source, alternative text, captions,
author details, dates, reading time, links, and custom-component content.
Verify each field through its Content-mode UI, inspect the saved MDX or
referenced file, reload, then restore and verify the original value. A valid
binding, correct preview, or successful MCP edit is not a UI-editability test.
Report each field as passed, failed, or not tested; do not call the article
fully editable with required fields missing or untested.
For Image sources, bind `document.frontmatter.featureImage.src` with
`binding.mode:"readwrite"` using `bind-props`. Content mode's **Choose source**
replaces the article's frontmatter image `$ref`; the resolved URL field remains
read-only. Test this picker and the saved reference instead of assuming that
displaying an image proves replacement works.
An alternative-text binding to the image's `.description` uses shared Asset
metadata. Edit it through **Choose source → asset actions → Settings →
Description**; that edit affects every use of the Asset. Image replacement
and shared-description editing are separate actions, not reasons to leave
required image content uneditable.
### Keep editable values separate from formatting
When a field must be editable in Content mode, do not concatenate its value
@@ -1,3 +1,9 @@
If you are inside the Webstudio monorepo, use the local CLI exactly as `node packages/cli/local.js ...` as your first command path. Do not use `packages/cli/bin.js` for local source-tree work; it is the packaged build entry and may use stale built output. Before editing a Webstudio project, do one small discovery step, then act. For delegated design-system or “use every component” tasks, the first MCP command is `workflow.next {"goal":"design-system-page"}`; report that returned checkpoint to the parent/user and stop until continued. For a simple authored/styled section, the intended path is literal: call `meta.index`, call `meta.get-more-tools` with `{"tools":["insert-fragment"]}`, read the target parent id with `list-pages`/`get-page-by-path`/`list-instances` only if needed, then call `insert-fragment`. Prefer `insert-fragment` for authored/styled sections; use `insert-component` only when inserting one automatic component template. Do not grep source files, dump full MCP resources, or write parser scripts before trying focused MCP calls. Good first calls are `meta.index` for orientation, `meta.guide` for a goal workflow, `workflow.next` for one bounded delegated phase, `meta.get-more-tools` for exact tool schemas, and `components.coverage-plan`, `components.coverage-insert-next`, `components.find`, or `components.get` for component work. Do not call every discovery tool up front. For delegated or non-streaming agents whose parent cannot see live stderr/stdout, treat each parent-visible checkpoint as the unit of work. If the parent asks for status within 30 seconds, run exactly one shortcut command such as `webstudio meta.index` or one explicit `webstudio mcp single-op-call` command, report its command/result, and wait for the parent before the next MCP command. Do not run long shell loops of shortcut or `webstudio mcp single-op-call` commands; they hide progress from the parent. Do not take a broad task such as creating a full design-system page as one execution unit. Instead call `workflow.next {"goal":"design-system-page"}`, report the returned phase/checkpoint to the parent, wait until the parent continues, call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`, complete exactly that bounded phase, and return: discovery, page creation, one dry-run JSX section, one committed JSX section, one `components.coverage-insert-next` call, or one presentation pass. Phase commands do not include nextPhase in their own output. After the parent continues, acknowledge the previous checkpoint first, then call `workflow.next` with the next phase. For design-system or “use every component” tasks, start with `workflow.next`, acknowledge its checkpoint only after parent/user continuation, then run compact `components.coverage-plan`, checkpoint, create the page, checkpoint, then call `components.coverage-insert-next` once per coverage checkpoint, and finish with the `presentation-pass` workflow phase. Coverage 72/72 is necessary but not sufficient: the page must be organized into styled, real-world design-system examples instead of raw unstyled component dumps. Request component coverage details with `components.coverage-plan {"detail":"roots"}` or `components.coverage-plan {"detail":"parts"}` only when needed. `list-pages` does not accept `detail`; use `list-pages {}` or `get-page-by-path` for page lookup. Read overview resources such as `webstudio://project/guide`, `webstudio://project/tools-overview`, and `webstudio://project/components-overview` only when focused tools are insufficient. Do not dump or parse full resources such as `webstudio://project/tools` or `webstudio://project/components` unless the focused tools are insufficient. Run one-shot shortcut or explicit `webstudio mcp single-op-call` commands sequentially against the same linked `.webstudio` folder. For experiments, pass `--dry-run` to local-capable mutations. Copying a `.webstudio` folder is not an isolated project clone; `.webstudio/config.json` still points to the same remote project, so non-dry-run mutations can commit to that project. If a one-shot call returns checkpoint.required, the CLI persists `CHECKPOINT_REQUIRED` across later one-shot calls until you report the checkpoint, wait until continued, and call `checkpoint.ack {"reported":true,"continueAfterReport":true,"summary":"<what you reported>"}`. If you receive `PROJECT_SESSION_BUSY`, another CLI/MCP process is updating the local session; wait a moment and retry sequentially. Complete one bounded phase and return a checkpoint before continuing. Follow the guide: read ids and Builder breakpoints first, use focused component discovery for component ids, props, and content model composition, prefer semantic tools over `apply-patch`, use direct value tools for fixed text/props, use bindings only for dynamic expressions/resources/actions, page metadata and fixed resource URLs accept plain strings, put page/resource update fields under `values`Line truncated
When `report-issue` is available and the user explicitly asks to report a problem, call it once without requiring a GitHub login. Also call it automatically after a Webstudio-controlled tool crash, hang, malformed result, misleading success, repeated failure of the same documented operation, failed documented recovery, or required undocumented workaround. Do not automatically report expected confirmation/checkpoint flow, a clearly explained invalid input, missing permissions, `PROJECT_SESSION_BUSY`, a recovered version conflict, rate limiting, cancellation, or an offline environment. The LLM must write a complete anonymous technical report with the generalized user story, summary, attempted workflow, expected behavior, actual result, recovery attempts, user impact, technical context, acceptance criteria, exact model identifier, and reasoning effort. Never include names, usernames, emails, phone numbers, organizations, project or resource ids, domains, URLs, IP addresses, local paths, credentials, tokens, customer content, or exact unique values. Do not copy raw tool inputs, outputs, or contextual errors; preserve only stable tool names, schema fields, error codes, CLI versions, and generalized input shapes. Use `unknown` instead of guessing unavailable model metadata.
## Content Block completion
Content Block work is not complete after one editable heading works. For every creation, migration, or repair, with or without MDX, inventory all intended editor-owned fields and their write destinations before implementation. Use semantic controls (calendar for dates, image picker for image replacement), keep fixed wording separate from direct writable bindings, and preserve existing field types and wording. Verify each field through Content mode, saved data, and reload; restore test values. Report passed, failed, or not tested per field. Missing or read-only required controls are unfinished requirements. Never substitute a correct preview or MCP write for the UI test.
For MDX articles, include the designed header, reading time, dates, images, alternative text, links, author fields, and custom-component content—not just the body. Replacing an image changes the article's frontmatter image `$ref`, not shared resolved Asset metadata. Do not defer image replacement because shared alternative text needs a decision. Follow the expressions guide's whole-article verification checklist before handoff.
+1 -1
View File
@@ -26,7 +26,7 @@ export const meta: WsComponentMeta = {
...props,
datetime: {
type: "string",
control: "text",
control: "date",
required: false,
contentMode: true,
},
+1 -3
View File
@@ -179,13 +179,11 @@ const json = z.object({
defaultValue: z.unknown().optional(),
});
// we neither generate date nor support it in props panel, listed here for completeness
const date = z.object({
...common,
control: z.literal("date"),
// @todo not sure what type should be here
// (we don't support Date yet, added for completeness)
// Dates are stored as strings, not JavaScript Date objects.
type: z.literal("string"),
defaultValue: z.string().optional(),
});