mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-30 00:27:43 +09:00
This PR expands Webstudio MCP and CLI into a more complete agent authoring interface. Agents can discover project capabilities, edit real Webstudio content, use data/resources, verify visual output, and report useful issues when workflows fail. ## MCP User Capabilities - Create, update, duplicate, reorder, and delete pages. - Create pages from templates and manage reusable page templates. - Inspect project pages, instances, props, styles, assets, variables, resources, and metadata. - Insert Webstudio components and authored JSX fragments into pages. - Edit text, props, styles, breakpoints, design tokens, and CSS variables. - Update page metadata, SEO fields, redirects, marketplace metadata, and project settings. - Create and use static data variables. - Create and use dynamic resources fetched over HTTP. - Use REST, GraphQL, and built-in system resources in authored pages. - Bind data variables and resources to visible page content. - Create resource-backed pages such as sitemap XML, structured SEO data, and API-driven content sections. - Preview current project state without requiring a publish step. - Capture screenshots for visual verification. - Verify responsive designs across desktop, tablet, and mobile breakpoints. - Use screenshot diff and OCR-style checks to inspect rendered output. - Reset and refresh local MCP project state when needed. - Discover available CLI/MCP commands from generated manuals. - Use clearer examples for resources, data variables, JSX authoring, previewing, and visual checks. - Read marketplace product metadata without requiring a full project snapshot. - Get safer guidance when using low-level patch tools. - Get a ready-to-copy Discord report template when MCP/CLI behavior is confusing or broken.
50 lines
1.3 KiB
TypeScript
50 lines
1.3 KiB
TypeScript
import type { Page } from "playwright";
|
|
import { waitForCanvasFrame } from "./builder";
|
|
import { waitForSyncStatus } from "./sync-status";
|
|
|
|
const getVisibleInsertBlockButton = async ({
|
|
page,
|
|
anchorText,
|
|
}: {
|
|
page: Page;
|
|
anchorText: string;
|
|
}) => {
|
|
await page.keyboard.press("Escape");
|
|
const canvas = await waitForCanvasFrame({ page });
|
|
const target = canvas.getByText(anchorText, { exact: true }).first();
|
|
const button = page.getByRole("button", { name: "Insert block" }).last();
|
|
const startedAt = Date.now();
|
|
let lastError: unknown;
|
|
|
|
while (Date.now() - startedAt < 10_000) {
|
|
try {
|
|
await target.scrollIntoViewIfNeeded();
|
|
await target.hover();
|
|
await button.waitFor({ state: "visible", timeout: 1_000 });
|
|
return button;
|
|
} catch (error) {
|
|
lastError = error;
|
|
}
|
|
}
|
|
|
|
throw new Error(`Expected insert block button for "${anchorText}"`, {
|
|
cause: lastError,
|
|
});
|
|
};
|
|
|
|
export const insertTemplateAfterCanvasText = async ({
|
|
page,
|
|
anchorText,
|
|
templateName,
|
|
}: {
|
|
page: Page;
|
|
anchorText: string;
|
|
templateName: string;
|
|
}) => {
|
|
const insertButton = await getVisibleInsertBlockButton({ page, anchorText });
|
|
await insertButton.click();
|
|
|
|
await page.getByRole("menuitemradio", { name: templateName }).click();
|
|
await waitForSyncStatus({ page, status: "idle" });
|
|
};
|