mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-28 23:57:17 +09:00
- **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.
48 lines
1.4 KiB
TypeScript
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,
|
|
};
|