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

48 lines
1.4 KiB
TypeScript

import { gfm, gfmHtml } from "micromark-extension-gfm";
import { micromark } from "micromark";
import { denormalizeSrcProps } from "./asset-upload";
import { generateFragmentFromHtml } from "@webstudio-is/project-build/runtime";
import { pasteHandled, pasteIgnored, type Plugin } from "./copy-paste";
import {
hasFragmentData,
insertFragmentWithBreakpointWarning,
} from "./fragment-utils";
const parse = (clipboardData: string) => {
const html = micromark(clipboardData, "utf-8", {
extensions: [gfm()],
htmlExtensions: [gfmHtml()],
});
const { skippedSelectors: _skipped, ...fragment } =
generateFragmentFromHtml(html);
const instances = new Map(fragment.instances.map((item) => [item.id, item]));
for (const instance of fragment.instances) {
if (instance.tag === "img") {
delete instance.tag;
instance.component = "Image";
}
}
fragment.instances = Array.from(instances.values());
return fragment;
};
const handlePasteMarkdown = async (clipboardData: string) => {
let fragment = parse(clipboardData);
if (hasFragmentData(fragment) === false) {
return pasteIgnored;
}
fragment = await denormalizeSrcProps(fragment);
await insertFragmentWithBreakpointWarning(fragment);
return pasteHandled;
};
export const markdown: Plugin = {
name: "markdown",
mimeType: "text/plain",
onPaste: handlePasteMarkdown,
};
export const __testing__ = {
parse,
};