Files
Oleg Isonen d440e02990 feat: Add safer MCP authoring and draft-page verification (#5905)
- **Restore points for agent work**: Create named restore points before
major edits, review changes with a dry run, and safely revert project
content if needed.

- **Shared Slot authoring**: Agents can turn existing sections into
shared Slots and insert linked copies across pages, including valid
nested Slots.

- **DTCG and Figma token imports**: Import design tokens and CSS
variables through CLI, MCP, or direct paste, with aliases, modes,
composite values, and mappings supported.

- **Consistent token conflict handling**: Every paste workflow now
offers the same preserve, rename, or merge choices instead of silently
overwriting existing design tokens.

- **Draft-page previews**: Agents can generate and inspect previews of
draft pages without publishing them or exposing drafts in production
builds.

- **Safer MCP mutations**: Destructive operations require explicit
confirmation, invalid raw patches and invented record IDs are rejected,
and restore operations commit atomically.

- **More dependable agent contracts**: New tools have complete runtime
schemas, clearer validation errors, and consistent behavior across CLI,
MCP, Builder, and server APIs.
2026-07-17 23:16:39 +02:00

41 lines
1.3 KiB
TypeScript

import type { WebstudioFragment } from "@webstudio-is/sdk";
import { generateFragmentFromHtml } from "@webstudio-is/project-build/runtime";
import { generateFragmentFromTailwind } from "../tailwind/tailwind";
import { denormalizeSrcProps } from "./asset-upload";
import { pasteHandled, pasteIgnored, type Plugin } from "./copy-paste";
import { builderApi } from "../builder-api";
import {
hasFragmentData,
insertFragmentWithBreakpointWarning,
} from "./fragment-utils";
const inceptionMark = `<!-- @webstudio/inception/1 -->`;
const handlePasteHtml = async (html: string) => {
const parseResult = generateFragmentFromHtml(html);
const { skippedSelectors } = parseResult;
let fragment: WebstudioFragment = parseResult;
if (hasFragmentData(fragment) === false) {
return pasteIgnored;
}
fragment = await denormalizeSrcProps(fragment);
if (html.includes(inceptionMark)) {
fragment = await generateFragmentFromTailwind(fragment);
}
if ((await insertFragmentWithBreakpointWarning(fragment)) === false) {
return pasteHandled;
}
if (skippedSelectors.length > 0) {
builderApi.toast.info(
`Skipped nested selectors (no matching elements): ${skippedSelectors.join(", ")}`
);
}
return pasteHandled;
};
export const html: Plugin = {
name: "html",
mimeType: "text/plain",
onPaste: handlePasteHtml,
};