@pixerate/editor 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +512 -0
- package/dist/agent/index.cjs.map +1 -0
- package/dist/agent/index.d.cts +369 -0
- package/dist/agent/index.d.ts +369 -0
- package/dist/agent/index.js +471 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/canvas/index.cjs +797 -0
- package/dist/canvas/index.cjs.map +1 -0
- package/dist/canvas/index.d.cts +187 -0
- package/dist/canvas/index.d.ts +187 -0
- package/dist/canvas/index.js +731 -0
- package/dist/canvas/index.js.map +1 -0
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +5 -27
- package/dist/extensions/index.d.ts +5 -27
- package/dist/extensions/index.js.map +1 -1
- package/dist/index.cjs +1274 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -5
- package/dist/index.d.ts +41 -5
- package/dist/index.js +1232 -6
- package/dist/index.js.map +1 -1
- package/dist/mention-Dcevh3ff.d.cts +32 -0
- package/dist/mention-Dcevh3ff.d.ts +32 -0
- package/dist/serializers/index.cjs +194 -4
- package/dist/serializers/index.cjs.map +1 -1
- package/dist/serializers/index.d.cts +89 -2
- package/dist/serializers/index.d.ts +89 -2
- package/dist/serializers/index.js +186 -3
- package/dist/serializers/index.js.map +1 -1
- package/dist/types-lGtRobRV.d.cts +213 -0
- package/dist/types-lGtRobRV.d.ts +213 -0
- package/package.json +13 -1
package/dist/index.cjs
CHANGED
|
@@ -31,9 +31,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
ColorHighlighter: () => ColorHighlighter,
|
|
34
|
+
DEFAULT_NODE_HEIGHT: () => DEFAULT_NODE_HEIGHT,
|
|
35
|
+
DEFAULT_NODE_WIDTH: () => DEFAULT_NODE_WIDTH,
|
|
36
|
+
DEFAULT_PLACEMENT_GAP: () => DEFAULT_PLACEMENT_GAP,
|
|
34
37
|
DataSourceBinder: () => DataSourceBinder,
|
|
35
38
|
DependencyGraph: () => DependencyGraph,
|
|
36
39
|
DirtyTracker: () => DirtyTracker,
|
|
40
|
+
EDITOR_MCP_TOOLS: () => EDITOR_MCP_TOOLS,
|
|
37
41
|
EditorController: () => EditorController,
|
|
38
42
|
FontSize: () => FontSize,
|
|
39
43
|
FormulaEvaluator: () => FormulaEvaluator,
|
|
@@ -43,48 +47,86 @@ __export(src_exports, {
|
|
|
43
47
|
INSTRUCTION_REGEX: () => INSTRUCTION_REGEX,
|
|
44
48
|
Image: () => Image2,
|
|
45
49
|
LoadingNode: () => LoadingNode,
|
|
50
|
+
MARKDOWN_MENTION_SPLIT_REGEX: () => MARKDOWN_MENTION_SPLIT_REGEX,
|
|
51
|
+
MCP_TOOL_EDIT_CANVAS: () => MCP_TOOL_EDIT_CANVAS,
|
|
52
|
+
MCP_TOOL_EDIT_PROMPT: () => MCP_TOOL_EDIT_PROMPT,
|
|
53
|
+
MCP_TOOL_GET_EDITOR_STATE: () => MCP_TOOL_GET_EDITOR_STATE,
|
|
54
|
+
MENTION_REGEX: () => MENTION_REGEX,
|
|
46
55
|
Markdown: () => Markdown2,
|
|
47
56
|
Mention: () => Mention,
|
|
48
57
|
SlashCommands: () => SlashCommands,
|
|
49
58
|
SmilieReplacer: () => SmilieReplacer,
|
|
50
59
|
SpreadsheetController: () => SpreadsheetController,
|
|
60
|
+
StreamingLayoutManager: () => StreamingLayoutManager,
|
|
51
61
|
TEMPLATE_REGEX: () => TEMPLATE_REGEX,
|
|
52
62
|
TemplateSuggestions: () => TemplateSuggestions,
|
|
53
63
|
VARIABLE_REGEX: () => VARIABLE_REGEX,
|
|
64
|
+
boundsOf: () => boundsOf,
|
|
65
|
+
calculateDagreReflowDisplacement: () => calculateDagreReflowDisplacement,
|
|
66
|
+
calculateDirectionalDisplacement: () => calculateDirectionalDisplacement,
|
|
67
|
+
centerNodes: () => centerNodes,
|
|
68
|
+
centre: () => centre,
|
|
54
69
|
colNameToIndex: () => colNameToIndex,
|
|
70
|
+
computeNodesBoundingBox: () => computeNodesBoundingBox,
|
|
71
|
+
createBezierTrajectory: () => createBezierTrajectory,
|
|
55
72
|
createDirtyTracker: () => createDirtyTracker,
|
|
56
73
|
createEditor: () => createEditor,
|
|
74
|
+
createFanOutTrajectory: () => createFanOutTrajectory,
|
|
57
75
|
createHistoryManager: () => createHistoryManager,
|
|
58
76
|
createMentionExtension: () => createMentionExtension,
|
|
59
77
|
createRichTextPreset: () => createRichTextPreset,
|
|
78
|
+
defaultEasing: () => defaultEasing,
|
|
60
79
|
defaultIsEqual: () => defaultIsEqual,
|
|
80
|
+
ensureLayout: () => ensureLayout,
|
|
61
81
|
escapeHtml: () => escapeHtml,
|
|
82
|
+
estimateNodeSize: () => estimateNodeSize,
|
|
62
83
|
extractCellDependencies: () => extractCellDependencies,
|
|
63
84
|
extractInstructionNames: () => extractInstructionNames,
|
|
85
|
+
extractMentions: () => extractMentions,
|
|
64
86
|
extractMentionsFromDoc: () => extractMentionsFromDoc,
|
|
65
87
|
extractTemplateNames: () => extractTemplateNames,
|
|
66
88
|
extractVariableNames: () => extractVariableNames,
|
|
67
89
|
findHexColors: () => findHexColors,
|
|
90
|
+
flowSide: () => flowSide,
|
|
91
|
+
getCenteredNodePosition: () => getCenteredNodePosition,
|
|
68
92
|
getEditorMarkdown: () => getEditorMarkdown,
|
|
69
93
|
getEditorText: () => getEditorText,
|
|
70
94
|
getLatestVersion: () => getLatestVersion,
|
|
95
|
+
getLayoutedNodes: () => getLayoutedNodes,
|
|
71
96
|
handleImageInsertion: () => handleImageInsertion,
|
|
72
97
|
htmlToPlainText: () => htmlToPlainText,
|
|
73
98
|
imagePastePluginKey: () => imagePastePluginKey,
|
|
74
99
|
indexToColName: () => indexToColName,
|
|
75
100
|
insertImageNode: () => insertImageNode,
|
|
76
101
|
instructionRegex: () => instructionRegex,
|
|
102
|
+
isEventFromTextInput: () => isEventFromTextInput,
|
|
103
|
+
isValidCanvasNode: () => isValidCanvasNode,
|
|
104
|
+
isValidUrl: () => isValidUrl,
|
|
105
|
+
linearPositionTrajectory: () => linearPositionTrajectory,
|
|
106
|
+
linearTrajectory: () => linearTrajectory,
|
|
77
107
|
mapRawOffsetToResolved: () => mapRawOffsetToResolved,
|
|
78
108
|
mapResolvedOffsetToRaw: () => mapResolvedOffsetToRaw,
|
|
79
109
|
markdownToTipTapHtml: () => markdownToTipTapHtml,
|
|
110
|
+
overlaps: () => overlaps,
|
|
111
|
+
parseCanvasClipboardData: () => parseCanvasClipboardData,
|
|
80
112
|
parseCellReference: () => parseCellReference,
|
|
81
113
|
parseFormula: () => parseFormula,
|
|
114
|
+
parseMentionSegments: () => parseMentionSegments,
|
|
82
115
|
parseRange: () => parseRange,
|
|
116
|
+
placeBeside: () => placeBeside,
|
|
83
117
|
plainTextToTipTapHtml: () => plainTextToTipTapHtml,
|
|
118
|
+
planAgentCommand: () => planAgentCommand,
|
|
119
|
+
preprocessMarkdownFileLinks: () => preprocessMarkdownFileLinks,
|
|
120
|
+
preprocessMarkdownMentions: () => preprocessMarkdownMentions,
|
|
121
|
+
remapPastedNodes: () => remapPastedNodes,
|
|
84
122
|
removeImageNode: () => removeImageNode,
|
|
123
|
+
repositionNodesTo: () => repositionNodesTo,
|
|
85
124
|
resolveTemplates: () => resolveTemplates,
|
|
86
125
|
resolveTemplatesWithMapping: () => resolveTemplatesWithMapping,
|
|
87
126
|
resolveVariables: () => resolveVariables,
|
|
127
|
+
runMultiNodeTransition: () => runMultiNodeTransition,
|
|
128
|
+
serializeCanvasNodes: () => serializeCanvasNodes,
|
|
129
|
+
stripMarkdownCode: () => stripMarkdownCode,
|
|
88
130
|
templateRegex: () => templateRegex,
|
|
89
131
|
tokenizeFormula: () => tokenizeFormula,
|
|
90
132
|
tokenizePrompt: () => tokenizePrompt,
|
|
@@ -122,10 +164,10 @@ function tokenizePrompt(text) {
|
|
|
122
164
|
while ((match = varRegex.exec(text)) !== null) {
|
|
123
165
|
const start = match.index;
|
|
124
166
|
const end = start + match[0].length;
|
|
125
|
-
const
|
|
167
|
+
const overlaps2 = rawMatches.some(
|
|
126
168
|
(m) => !(end <= m.start || start >= m.end)
|
|
127
169
|
);
|
|
128
|
-
if (!
|
|
170
|
+
if (!overlaps2) {
|
|
129
171
|
rawMatches.push({
|
|
130
172
|
type: "variable",
|
|
131
173
|
raw: match[0],
|
|
@@ -142,10 +184,10 @@ function tokenizePrompt(text) {
|
|
|
142
184
|
while ((match = instRegex.exec(text)) !== null) {
|
|
143
185
|
const start = match.index;
|
|
144
186
|
const end = start + match[0].length;
|
|
145
|
-
const
|
|
187
|
+
const overlaps2 = rawMatches.some(
|
|
146
188
|
(m) => !(end <= m.start || start >= m.end)
|
|
147
189
|
);
|
|
148
|
-
if (!
|
|
190
|
+
if (!overlaps2) {
|
|
149
191
|
rawMatches.push({
|
|
150
192
|
type: "instruction",
|
|
151
193
|
raw: match[0],
|
|
@@ -2290,6 +2332,150 @@ function createRichTextPreset(options = {}) {
|
|
|
2290
2332
|
return extensions2;
|
|
2291
2333
|
}
|
|
2292
2334
|
|
|
2335
|
+
// src/serializers/mentions.ts
|
|
2336
|
+
var MENTION_REGEX = /(?:^|[\s>(*_"';\\/[\]{}~`:]|"|>|<|&)@(?:"([^"]+)"|"([^&]+)"|([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*))/g;
|
|
2337
|
+
var MARKDOWN_MENTION_SPLIT_REGEX = /(```[\s\S]*?```|~~~[\s\S]*?~~~|<pre[\s\S]*?<\/pre>|<code[\s\S]*?<\/code>|`[^`\n]+`|<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["']|data-mention=["'][^"']*["'])[^>]*>[\s\S]*?<\/span>|<[^>]+>|\]\([^)]+\)|(?:https?|file|vscode|cursor|mailto):\/\/[^\s<)]+)/gi;
|
|
2338
|
+
function stripMarkdownCode(content) {
|
|
2339
|
+
if (!content || typeof content !== "string") return "";
|
|
2340
|
+
return content.replace(/```[\s\S]*?```/g, "").replace(/~~~[\s\S]*?~~~/g, "").replace(/<pre[\s\S]*?<\/pre>/gi, "").replace(/<code[\s\S]*?<\/code>/gi, "").replace(/`[^`\n]*`/g, "");
|
|
2341
|
+
}
|
|
2342
|
+
function extractMentions(content) {
|
|
2343
|
+
if (!content || typeof content !== "string") return [];
|
|
2344
|
+
const normalizedMentionNodes = content.replace(
|
|
2345
|
+
/<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["'])[^>]*>([\s\S]*?)<\/span>/gi,
|
|
2346
|
+
(match2, inner) => {
|
|
2347
|
+
const labelMatch = match2.match(/\bdata-label=["']([^"']+)["']/i);
|
|
2348
|
+
if (labelMatch) return ` @${labelMatch[1]} `;
|
|
2349
|
+
const idMatch = match2.match(/\bdata-id=["']([^"']+)["']/i);
|
|
2350
|
+
if (idMatch) return ` @${idMatch[1]} `;
|
|
2351
|
+
return ` ${inner} `;
|
|
2352
|
+
}
|
|
2353
|
+
);
|
|
2354
|
+
const cleanContent = stripMarkdownCode(normalizedMentionNodes);
|
|
2355
|
+
const withoutUrls = cleanContent.replace(/(?:https?|ftp|file|vscode|cursor|mailto):\/\/[^\s<>()]+/gi, "").replace(/\]\([^)]*\)/g, "]");
|
|
2356
|
+
const textOnly = withoutUrls.replace(/<[^>]+>/g, " ");
|
|
2357
|
+
const mentions = [];
|
|
2358
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2359
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2360
|
+
let match;
|
|
2361
|
+
while ((match = regex.exec(textOnly)) !== null) {
|
|
2362
|
+
const handle = (match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
2363
|
+
if (handle && !seen.has(handle)) {
|
|
2364
|
+
seen.add(handle);
|
|
2365
|
+
mentions.push(handle);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
return mentions;
|
|
2369
|
+
}
|
|
2370
|
+
function parseMentionSegments(content) {
|
|
2371
|
+
if (!content) return [];
|
|
2372
|
+
const segments = [];
|
|
2373
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2374
|
+
let lastIndex = 0;
|
|
2375
|
+
let match;
|
|
2376
|
+
while ((match = regex.exec(content)) !== null) {
|
|
2377
|
+
const fullMatch = match[0];
|
|
2378
|
+
const atIndex = fullMatch.indexOf("@");
|
|
2379
|
+
const prefix = atIndex > 0 ? fullMatch.slice(0, atIndex) : "";
|
|
2380
|
+
const mentionToken = fullMatch.slice(atIndex);
|
|
2381
|
+
const handle = match[1] ?? match[2] ?? match[3];
|
|
2382
|
+
const matchStart = match.index + prefix.length;
|
|
2383
|
+
if (matchStart > lastIndex) {
|
|
2384
|
+
segments.push({
|
|
2385
|
+
type: "text",
|
|
2386
|
+
value: content.slice(lastIndex, matchStart)
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
segments.push({
|
|
2390
|
+
type: "mention",
|
|
2391
|
+
value: mentionToken,
|
|
2392
|
+
handle
|
|
2393
|
+
});
|
|
2394
|
+
lastIndex = matchStart + mentionToken.length;
|
|
2395
|
+
}
|
|
2396
|
+
if (lastIndex < content.length) {
|
|
2397
|
+
segments.push({
|
|
2398
|
+
type: "text",
|
|
2399
|
+
value: content.slice(lastIndex)
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
return segments;
|
|
2403
|
+
}
|
|
2404
|
+
function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
|
|
2405
|
+
if (!content || typeof content !== "string") return "";
|
|
2406
|
+
const options = Array.isArray(optionsOrMentionables) ? { mentionables: optionsOrMentionables } : optionsOrMentionables;
|
|
2407
|
+
const allMentionables = options.mentionables || [];
|
|
2408
|
+
const defaultType = options.defaultType || "user";
|
|
2409
|
+
const resolveMention = options.resolveMention;
|
|
2410
|
+
const parts = content.split(MARKDOWN_MENTION_SPLIT_REGEX);
|
|
2411
|
+
return parts.map((part) => {
|
|
2412
|
+
if (!part) return "";
|
|
2413
|
+
if (part.startsWith("```") || part.startsWith("~~~") || part.startsWith("`") || part.startsWith("](") || /^(?:https?|file|vscode|cursor|mailto):\/\//i.test(part) || /^<pre[\s>]/i.test(part) || /^<code[\s>]/i.test(part) || /^<[^>]+>$/i.test(part) || /^<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["']|data-mention=["'][^"']*["'])/i.test(
|
|
2414
|
+
part
|
|
2415
|
+
)) {
|
|
2416
|
+
return part;
|
|
2417
|
+
}
|
|
2418
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2419
|
+
return part.replace(regex, (match, quotedHandle, escapedQuotedHandle, standardHandle) => {
|
|
2420
|
+
const atIndex = match.indexOf("@");
|
|
2421
|
+
const prefix = atIndex > 0 ? match.slice(0, atIndex) : "";
|
|
2422
|
+
const rawHandle = (quotedHandle || escapedQuotedHandle || standardHandle || "").trim();
|
|
2423
|
+
if (!rawHandle) return match;
|
|
2424
|
+
const lowerHandle = rawHandle.toLowerCase();
|
|
2425
|
+
const entity = allMentionables.find((e) => {
|
|
2426
|
+
if (e.label?.toLowerCase() === lowerHandle) return true;
|
|
2427
|
+
if (e.handle?.toLowerCase() === lowerHandle) return true;
|
|
2428
|
+
if (e.name?.toLowerCase() === lowerHandle) return true;
|
|
2429
|
+
if (e.id?.toLowerCase() === lowerHandle) return true;
|
|
2430
|
+
if (e.type === "agent" && `agent${(e.handle || e.label || "").toLowerCase()}` === lowerHandle) return true;
|
|
2431
|
+
if (e.aliases?.some(
|
|
2432
|
+
(a) => a.toLowerCase() === lowerHandle || e.type === "agent" && `agent${a.toLowerCase()}` === lowerHandle
|
|
2433
|
+
)) {
|
|
2434
|
+
return true;
|
|
2435
|
+
}
|
|
2436
|
+
return false;
|
|
2437
|
+
});
|
|
2438
|
+
let id = entity?.id;
|
|
2439
|
+
let label = entity?.label || entity?.handle || rawHandle;
|
|
2440
|
+
let type = entity?.type;
|
|
2441
|
+
let avatarUrl = entity?.avatarUrl;
|
|
2442
|
+
let color = entity?.color || entity?.hexColor;
|
|
2443
|
+
if (!entity) {
|
|
2444
|
+
const customResolved = resolveMention?.(rawHandle);
|
|
2445
|
+
if (customResolved) {
|
|
2446
|
+
id = customResolved.id ?? id;
|
|
2447
|
+
label = customResolved.label ?? customResolved.handle ?? label;
|
|
2448
|
+
type = customResolved.type ?? type;
|
|
2449
|
+
avatarUrl = customResolved.avatarUrl ?? avatarUrl;
|
|
2450
|
+
color = customResolved.color ?? customResolved.hexColor ?? color;
|
|
2451
|
+
} else {
|
|
2452
|
+
const stripped = lowerHandle.replace(/^agent[\s\-_]*/i, "");
|
|
2453
|
+
if (["supervisor", "coordinator", "planner", "validator", "executor"].includes(stripped)) {
|
|
2454
|
+
type = "agent";
|
|
2455
|
+
id = stripped;
|
|
2456
|
+
label = stripped;
|
|
2457
|
+
} else if (lowerHandle === "agents") {
|
|
2458
|
+
type = "team";
|
|
2459
|
+
id = "agents";
|
|
2460
|
+
label = "agents";
|
|
2461
|
+
} else {
|
|
2462
|
+
type = defaultType;
|
|
2463
|
+
id = rawHandle;
|
|
2464
|
+
label = rawHandle;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
const avatarAttr = avatarUrl ? ` data-avatar-url="${escapeHtml(avatarUrl)}"` : "";
|
|
2469
|
+
const colorAttr = color ? ` data-color="${escapeHtml(color)}"` : "";
|
|
2470
|
+
return `${prefix}<span class="pixerate-mention-node" data-type="mention" data-id="${escapeHtml(
|
|
2471
|
+
id || label
|
|
2472
|
+
)}" data-label="${escapeHtml(label)}" data-mention-suggestion-char="@" data-type-name="${escapeHtml(
|
|
2473
|
+
type || defaultType
|
|
2474
|
+
)}"${avatarAttr}${colorAttr}>@${escapeHtml(label)}</span>`;
|
|
2475
|
+
});
|
|
2476
|
+
}).join("");
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2293
2479
|
// src/serializers/plain-text.ts
|
|
2294
2480
|
function escapeHtml(text) {
|
|
2295
2481
|
if (!text) return "";
|
|
@@ -2414,6 +2600,16 @@ function extractMentionsFromDoc(docOrEditor) {
|
|
|
2414
2600
|
});
|
|
2415
2601
|
}
|
|
2416
2602
|
}
|
|
2603
|
+
if (mentions.length === 0) {
|
|
2604
|
+
const rawHandles = extractMentions(docOrEditor);
|
|
2605
|
+
for (const handle of rawHandles) {
|
|
2606
|
+
mentions.push({
|
|
2607
|
+
id: handle,
|
|
2608
|
+
label: handle,
|
|
2609
|
+
type: "user"
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2417
2613
|
}
|
|
2418
2614
|
return mentions;
|
|
2419
2615
|
}
|
|
@@ -2433,9 +2629,31 @@ md2.validateLink = (url) => {
|
|
|
2433
2629
|
}
|
|
2434
2630
|
return defaultValidateLink.call(md2, url);
|
|
2435
2631
|
};
|
|
2436
|
-
function
|
|
2632
|
+
function preprocessMarkdownFileLinks(content) {
|
|
2633
|
+
if (!content) return "";
|
|
2634
|
+
return content.replace(
|
|
2635
|
+
/\[([^\]]+)\]\(((?:file|vscode|cursor):\/\/[^)]+)\)/g,
|
|
2636
|
+
(_match, rawLabel, href) => {
|
|
2637
|
+
const trimmed = rawLabel.trim();
|
|
2638
|
+
const codeMatch = trimmed.match(/^`([^`]+)`$/);
|
|
2639
|
+
const labelContent = codeMatch ? `<code>${codeMatch[1]}</code>` : trimmed;
|
|
2640
|
+
return `<a href="${href}" data-file-link="true" title="Click to copy file path">${labelContent}</a>`;
|
|
2641
|
+
}
|
|
2642
|
+
);
|
|
2643
|
+
}
|
|
2644
|
+
function markdownToTipTapHtml(markdown, options) {
|
|
2437
2645
|
if (!markdown) return "<p></p>";
|
|
2438
|
-
|
|
2646
|
+
let content = markdown;
|
|
2647
|
+
if (options?.preprocessFileLinks) {
|
|
2648
|
+
content = preprocessMarkdownFileLinks(content);
|
|
2649
|
+
}
|
|
2650
|
+
if (options?.preprocessMentions || options?.mentionables || options?.preprocessOptions) {
|
|
2651
|
+
const mentionOpts = options.preprocessOptions || {
|
|
2652
|
+
mentionables: options.mentionables
|
|
2653
|
+
};
|
|
2654
|
+
content = preprocessMarkdownMentions(content, mentionOpts);
|
|
2655
|
+
}
|
|
2656
|
+
return md2.render(content);
|
|
2439
2657
|
}
|
|
2440
2658
|
function getEditorMarkdown(editor) {
|
|
2441
2659
|
if (!editor) return "";
|
|
@@ -4160,12 +4378,1024 @@ var HistoryManager = class {
|
|
|
4160
4378
|
function createHistoryManager(options = {}) {
|
|
4161
4379
|
return new HistoryManager(options);
|
|
4162
4380
|
}
|
|
4381
|
+
|
|
4382
|
+
// src/canvas/layout.ts
|
|
4383
|
+
var import_dagre = __toESM(require("@dagrejs/dagre"), 1);
|
|
4384
|
+
var DEFAULT_NODE_WIDTH = 200;
|
|
4385
|
+
var DEFAULT_NODE_HEIGHT = 60;
|
|
4386
|
+
var DEFAULT_PLACEMENT_GAP = 48;
|
|
4387
|
+
function getCenteredNodePosition(width = 0, height = 0, screenToFlowPosition, screenDimensions) {
|
|
4388
|
+
const rect = screenDimensions ?? (typeof document !== "undefined" ? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } : { width: 1920, height: 1080 });
|
|
4389
|
+
const flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });
|
|
4390
|
+
return {
|
|
4391
|
+
x: flowPos.x - width / 2,
|
|
4392
|
+
y: flowPos.y - height / 2
|
|
4393
|
+
};
|
|
4394
|
+
}
|
|
4395
|
+
function centerNodes(nodes, screenToFlowPosition, getNodesBounds, screenDimensions) {
|
|
4396
|
+
if (nodes.length === 0) {
|
|
4397
|
+
return [];
|
|
4398
|
+
}
|
|
4399
|
+
const bounds = getNodesBounds(nodes);
|
|
4400
|
+
const boundingBoxOrigin = { x: bounds.x, y: bounds.y };
|
|
4401
|
+
const centeredPosition = getCenteredNodePosition(
|
|
4402
|
+
bounds.width,
|
|
4403
|
+
bounds.height,
|
|
4404
|
+
screenToFlowPosition,
|
|
4405
|
+
screenDimensions
|
|
4406
|
+
);
|
|
4407
|
+
const deltaX = centeredPosition.x - boundingBoxOrigin.x;
|
|
4408
|
+
const deltaY = centeredPosition.y - boundingBoxOrigin.y;
|
|
4409
|
+
return nodes.map((node) => ({
|
|
4410
|
+
...node,
|
|
4411
|
+
position: {
|
|
4412
|
+
x: node.position.x + deltaX,
|
|
4413
|
+
y: node.position.y + deltaY
|
|
4414
|
+
}
|
|
4415
|
+
}));
|
|
4416
|
+
}
|
|
4417
|
+
function getLayoutedNodes(nodes, edges, getNodesBounds, options = {}) {
|
|
4418
|
+
if (nodes.length === 0) return [];
|
|
4419
|
+
const {
|
|
4420
|
+
direction = "LR",
|
|
4421
|
+
ranksep = 100,
|
|
4422
|
+
nodesep = 25,
|
|
4423
|
+
defaultNodeWidth = DEFAULT_NODE_WIDTH,
|
|
4424
|
+
defaultNodeHeight = DEFAULT_NODE_HEIGHT
|
|
4425
|
+
} = options;
|
|
4426
|
+
const dagreGraph = new import_dagre.default.graphlib.Graph();
|
|
4427
|
+
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
4428
|
+
dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });
|
|
4429
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
4430
|
+
nodes.forEach((node) => {
|
|
4431
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4432
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4433
|
+
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
|
4434
|
+
});
|
|
4435
|
+
edges.forEach((edge) => {
|
|
4436
|
+
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
4437
|
+
dagreGraph.setEdge(edge.source, edge.target);
|
|
4438
|
+
}
|
|
4439
|
+
});
|
|
4440
|
+
import_dagre.default.layout(dagreGraph);
|
|
4441
|
+
return nodes.map((node) => {
|
|
4442
|
+
const nodeWithPosition = dagreGraph.node(node.id);
|
|
4443
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4444
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4445
|
+
return {
|
|
4446
|
+
...node,
|
|
4447
|
+
position: {
|
|
4448
|
+
x: nodeWithPosition.x - nodeWidth / 2,
|
|
4449
|
+
y: nodeWithPosition.y - nodeHeight / 2
|
|
4450
|
+
}
|
|
4451
|
+
};
|
|
4452
|
+
});
|
|
4453
|
+
}
|
|
4454
|
+
function estimateNodeSize(node, defaultWidth = DEFAULT_NODE_WIDTH, defaultHeight = DEFAULT_NODE_HEIGHT) {
|
|
4455
|
+
const label = String(node.data?.label ?? node.data?.title ?? node.id ?? "");
|
|
4456
|
+
const lines = label.split(/\n|<br\s*\/?>/);
|
|
4457
|
+
const longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, "").length), 1);
|
|
4458
|
+
const width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));
|
|
4459
|
+
const height = Math.max(defaultHeight, lines.length * 20 + 24);
|
|
4460
|
+
return { width, height };
|
|
4461
|
+
}
|
|
4462
|
+
function boundsOf(rects) {
|
|
4463
|
+
if (!rects.length) return null;
|
|
4464
|
+
const minX = Math.min(...rects.map((r) => r.x));
|
|
4465
|
+
const minY = Math.min(...rects.map((r) => r.y));
|
|
4466
|
+
const maxX = Math.max(...rects.map((r) => r.x + r.width));
|
|
4467
|
+
const maxY = Math.max(...rects.map((r) => r.y + r.height));
|
|
4468
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
4469
|
+
}
|
|
4470
|
+
function centre(r) {
|
|
4471
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
4472
|
+
}
|
|
4473
|
+
function overlaps(a, b, pad = 16) {
|
|
4474
|
+
return a.x < b.x + b.width + pad && a.x + a.width + pad > b.x && a.y < b.y + b.height + pad && a.y + a.height + pad > b.y;
|
|
4475
|
+
}
|
|
4476
|
+
function flowSide(direction = "LR") {
|
|
4477
|
+
switch (direction) {
|
|
4478
|
+
case "LR":
|
|
4479
|
+
return "right";
|
|
4480
|
+
case "RL":
|
|
4481
|
+
return "left";
|
|
4482
|
+
case "BT":
|
|
4483
|
+
return "above";
|
|
4484
|
+
case "TB":
|
|
4485
|
+
default:
|
|
4486
|
+
return "below";
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
function ensureLayout(nodes, edges, options = {}) {
|
|
4490
|
+
if (nodes.length === 0) return [];
|
|
4491
|
+
const stored = options.storedPositions ?? {};
|
|
4492
|
+
const missing = nodes.filter((n) => !stored[n.id]);
|
|
4493
|
+
if (missing.length === 0) {
|
|
4494
|
+
return nodes.map((n) => ({
|
|
4495
|
+
...n,
|
|
4496
|
+
position: { ...stored[n.id] }
|
|
4497
|
+
}));
|
|
4498
|
+
}
|
|
4499
|
+
const autoLayouted = getLayoutedNodes(nodes, edges, void 0, options);
|
|
4500
|
+
const autoMap = new Map(
|
|
4501
|
+
autoLayouted.map((n) => [n.id, n.position])
|
|
4502
|
+
);
|
|
4503
|
+
const both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));
|
|
4504
|
+
if (both.length === 0) {
|
|
4505
|
+
return autoLayouted;
|
|
4506
|
+
}
|
|
4507
|
+
let dx = 0;
|
|
4508
|
+
let dy = 0;
|
|
4509
|
+
for (const n of both) {
|
|
4510
|
+
const s = stored[n.id];
|
|
4511
|
+
const a = autoMap.get(n.id);
|
|
4512
|
+
dx += s.x - a.x;
|
|
4513
|
+
dy += s.y - a.y;
|
|
4514
|
+
}
|
|
4515
|
+
dx /= both.length;
|
|
4516
|
+
dy /= both.length;
|
|
4517
|
+
return nodes.map((node) => {
|
|
4518
|
+
if (stored[node.id]) {
|
|
4519
|
+
return {
|
|
4520
|
+
...node,
|
|
4521
|
+
position: { ...stored[node.id] }
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
const autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };
|
|
4525
|
+
return {
|
|
4526
|
+
...node,
|
|
4527
|
+
position: {
|
|
4528
|
+
x: Math.round(autoPos.x + dx),
|
|
4529
|
+
y: Math.round(autoPos.y + dy)
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
function placeBeside(rects, size, side, nearNodeId, options = {}) {
|
|
4535
|
+
const {
|
|
4536
|
+
gap = DEFAULT_PLACEMENT_GAP,
|
|
4537
|
+
ignore = [],
|
|
4538
|
+
maxAttempts = 32
|
|
4539
|
+
} = options;
|
|
4540
|
+
const skip = new Set([nearNodeId, ...ignore].filter(Boolean));
|
|
4541
|
+
const obstacles = Object.entries(rects).filter(([id]) => !skip.has(id)).map(([, r]) => r);
|
|
4542
|
+
const anchor = nearNodeId && rects[nearNodeId] || boundsOf(Object.values(rects)) || { x: 0, y: 0, width: 0, height: 0 };
|
|
4543
|
+
const anchorCenter = centre(anchor);
|
|
4544
|
+
let baseX;
|
|
4545
|
+
let baseY;
|
|
4546
|
+
switch (side) {
|
|
4547
|
+
case "right":
|
|
4548
|
+
baseX = anchor.x + anchor.width + gap;
|
|
4549
|
+
baseY = anchorCenter.y - size.height / 2;
|
|
4550
|
+
break;
|
|
4551
|
+
case "left":
|
|
4552
|
+
baseX = anchor.x - gap - size.width;
|
|
4553
|
+
baseY = anchorCenter.y - size.height / 2;
|
|
4554
|
+
break;
|
|
4555
|
+
case "above":
|
|
4556
|
+
baseX = anchorCenter.x - size.width / 2;
|
|
4557
|
+
baseY = anchor.y - gap - size.height;
|
|
4558
|
+
break;
|
|
4559
|
+
case "below":
|
|
4560
|
+
default:
|
|
4561
|
+
baseX = anchorCenter.x - size.width / 2;
|
|
4562
|
+
baseY = anchor.y + anchor.height + gap;
|
|
4563
|
+
break;
|
|
4564
|
+
}
|
|
4565
|
+
const isHorizontal = side === "right" || side === "left";
|
|
4566
|
+
const step = (isHorizontal ? size.height : size.width) + 20;
|
|
4567
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
4568
|
+
const offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);
|
|
4569
|
+
const candidateBox = {
|
|
4570
|
+
x: isHorizontal ? baseX : baseX + offset,
|
|
4571
|
+
y: isHorizontal ? baseY + offset : baseY,
|
|
4572
|
+
width: size.width,
|
|
4573
|
+
height: size.height
|
|
4574
|
+
};
|
|
4575
|
+
if (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {
|
|
4576
|
+
return {
|
|
4577
|
+
x: Math.round(candidateBox.x),
|
|
4578
|
+
y: Math.round(candidateBox.y)
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return {
|
|
4583
|
+
x: Math.round(baseX),
|
|
4584
|
+
y: Math.round(baseY)
|
|
4585
|
+
};
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
// src/canvas/trajectory.ts
|
|
4589
|
+
var defaultEasing = (t) => --t * t * t + 1;
|
|
4590
|
+
var linearTrajectory = (origin, target, progress) => {
|
|
4591
|
+
return {
|
|
4592
|
+
position: {
|
|
4593
|
+
x: origin.x + (target.x - origin.x) * progress,
|
|
4594
|
+
y: origin.y + (target.y - origin.y) * progress
|
|
4595
|
+
},
|
|
4596
|
+
scale: progress,
|
|
4597
|
+
opacity: Math.min(1, progress * 1.5)
|
|
4598
|
+
};
|
|
4599
|
+
};
|
|
4600
|
+
var linearPositionTrajectory = (origin, target, progress) => {
|
|
4601
|
+
return {
|
|
4602
|
+
position: {
|
|
4603
|
+
x: origin.x + (target.x - origin.x) * progress,
|
|
4604
|
+
y: origin.y + (target.y - origin.y) * progress
|
|
4605
|
+
}
|
|
4606
|
+
};
|
|
4607
|
+
};
|
|
4608
|
+
function createFanOutTrajectory(options = {}) {
|
|
4609
|
+
const {
|
|
4610
|
+
curvature = 0.4,
|
|
4611
|
+
startScale = 0,
|
|
4612
|
+
endScale = 1,
|
|
4613
|
+
startOpacity = 0,
|
|
4614
|
+
endOpacity = 1
|
|
4615
|
+
} = options;
|
|
4616
|
+
return (origin, target, progress, index, total) => {
|
|
4617
|
+
const dx = target.x - origin.x;
|
|
4618
|
+
const dy = target.y - origin.y;
|
|
4619
|
+
const normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;
|
|
4620
|
+
const midX = origin.x + dx * 0.4;
|
|
4621
|
+
const midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;
|
|
4622
|
+
const t = progress;
|
|
4623
|
+
const oneMinusT = 1 - t;
|
|
4624
|
+
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
|
|
4625
|
+
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
|
|
4626
|
+
const scale = startScale + (endScale - startScale) * progress;
|
|
4627
|
+
const opacity = startOpacity + (endOpacity - startOpacity) * progress;
|
|
4628
|
+
return {
|
|
4629
|
+
position: { x, y },
|
|
4630
|
+
scale,
|
|
4631
|
+
opacity
|
|
4632
|
+
};
|
|
4633
|
+
};
|
|
4634
|
+
}
|
|
4635
|
+
function createBezierTrajectory(getControlPoint) {
|
|
4636
|
+
return (origin, target, progress) => {
|
|
4637
|
+
const cp = getControlPoint(origin, target);
|
|
4638
|
+
const t = progress;
|
|
4639
|
+
const oneMinusT = 1 - t;
|
|
4640
|
+
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;
|
|
4641
|
+
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;
|
|
4642
|
+
return {
|
|
4643
|
+
position: { x, y },
|
|
4644
|
+
scale: progress,
|
|
4645
|
+
opacity: progress
|
|
4646
|
+
};
|
|
4647
|
+
};
|
|
4648
|
+
}
|
|
4649
|
+
function runMultiNodeTransition(transitions, options = {}) {
|
|
4650
|
+
if (transitions.length === 0) {
|
|
4651
|
+
options.onComplete?.();
|
|
4652
|
+
return () => {
|
|
4653
|
+
};
|
|
4654
|
+
}
|
|
4655
|
+
const { defaultDuration = 400, defaultEasing: easingFunc = defaultEasing, onUpdate, onComplete } = options;
|
|
4656
|
+
let isCancelled = false;
|
|
4657
|
+
const startTime = Date.now();
|
|
4658
|
+
const items = transitions.map((tr, i) => ({
|
|
4659
|
+
...tr,
|
|
4660
|
+
index: tr.index ?? i,
|
|
4661
|
+
total: tr.total ?? transitions.length,
|
|
4662
|
+
duration: tr.duration ?? defaultDuration,
|
|
4663
|
+
easing: tr.easing ?? easingFunc,
|
|
4664
|
+
delay: tr.delay ?? 0,
|
|
4665
|
+
trajectory: tr.trajectory ?? linearTrajectory
|
|
4666
|
+
}));
|
|
4667
|
+
function frame() {
|
|
4668
|
+
if (isCancelled) return;
|
|
4669
|
+
const now = Date.now();
|
|
4670
|
+
let allCompleted = true;
|
|
4671
|
+
for (let i = 0; i < items.length; i++) {
|
|
4672
|
+
const item = items[i];
|
|
4673
|
+
const elapsed = now - (startTime + item.delay);
|
|
4674
|
+
if (elapsed < 0) {
|
|
4675
|
+
allCompleted = false;
|
|
4676
|
+
continue;
|
|
4677
|
+
}
|
|
4678
|
+
const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
|
|
4679
|
+
const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
|
|
4680
|
+
const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
|
|
4681
|
+
item.node.position = { ...point.position };
|
|
4682
|
+
if (point.scale !== void 0 || point.opacity !== void 0) {
|
|
4683
|
+
if (!item.node.data) {
|
|
4684
|
+
item.node.data = {};
|
|
4685
|
+
}
|
|
4686
|
+
if (point.scale !== void 0) {
|
|
4687
|
+
item.node.data.scale = point.scale;
|
|
4688
|
+
}
|
|
4689
|
+
if (point.opacity !== void 0) {
|
|
4690
|
+
item.node.data.opacity = point.opacity;
|
|
4691
|
+
const currentStyle = (item.node.style || "").replace(/opacity:\s*[^;]+;?/g, "").trim();
|
|
4692
|
+
item.node.style = `${currentStyle ? currentStyle + "; " : ""}opacity: ${point.opacity};`;
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
if (rawProgress < 1) {
|
|
4696
|
+
allCompleted = false;
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
onUpdate?.();
|
|
4700
|
+
if (!allCompleted) {
|
|
4701
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
4702
|
+
requestAnimationFrame(frame);
|
|
4703
|
+
}
|
|
4704
|
+
} else {
|
|
4705
|
+
onComplete?.();
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
frame();
|
|
4709
|
+
return () => {
|
|
4710
|
+
isCancelled = true;
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4714
|
+
// src/canvas/displacement.ts
|
|
4715
|
+
var import_dagre2 = __toESM(require("@dagrejs/dagre"), 1);
|
|
4716
|
+
function computeNodesBoundingBox(nodes, defaultWidth = 200, defaultHeight = 60) {
|
|
4717
|
+
if (nodes.length === 0) {
|
|
4718
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
4719
|
+
}
|
|
4720
|
+
let minX = Infinity;
|
|
4721
|
+
let minY = Infinity;
|
|
4722
|
+
let maxX = -Infinity;
|
|
4723
|
+
let maxY = -Infinity;
|
|
4724
|
+
for (const node of nodes) {
|
|
4725
|
+
const width = node.measured?.width ?? node.width ?? defaultWidth;
|
|
4726
|
+
const height = node.measured?.height ?? node.height ?? defaultHeight;
|
|
4727
|
+
minX = Math.min(minX, node.position.x);
|
|
4728
|
+
minY = Math.min(minY, node.position.y);
|
|
4729
|
+
maxX = Math.max(maxX, node.position.x + width);
|
|
4730
|
+
maxY = Math.max(maxY, node.position.y + height);
|
|
4731
|
+
}
|
|
4732
|
+
return {
|
|
4733
|
+
x: minX,
|
|
4734
|
+
y: minY,
|
|
4735
|
+
width: maxX - minX,
|
|
4736
|
+
height: maxY - minY
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
function calculateDirectionalDisplacement(params) {
|
|
4740
|
+
const {
|
|
4741
|
+
existingNodes,
|
|
4742
|
+
originNode,
|
|
4743
|
+
childNodes: childNodes2,
|
|
4744
|
+
getNodesBounds,
|
|
4745
|
+
options = {}
|
|
4746
|
+
} = params;
|
|
4747
|
+
const {
|
|
4748
|
+
direction = "right",
|
|
4749
|
+
gap = 40,
|
|
4750
|
+
spreadSiblingLanes = false,
|
|
4751
|
+
siblingTolerance = 50,
|
|
4752
|
+
minY
|
|
4753
|
+
} = options;
|
|
4754
|
+
const displacedMap = /* @__PURE__ */ new Map();
|
|
4755
|
+
if (childNodes2.length === 0 || existingNodes.length === 0) {
|
|
4756
|
+
return displacedMap;
|
|
4757
|
+
}
|
|
4758
|
+
const childBounds = getNodesBounds ? getNodesBounds(childNodes2) : computeNodesBoundingBox(childNodes2);
|
|
4759
|
+
const originWidth = originNode.measured?.width ?? originNode.width ?? 200;
|
|
4760
|
+
const originHeight = originNode.measured?.height ?? originNode.height ?? 60;
|
|
4761
|
+
const deltaPrimary = direction === "right" || direction === "left" ? childBounds.width + gap : childBounds.height + gap;
|
|
4762
|
+
const deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);
|
|
4763
|
+
const deltaDown = Math.max(
|
|
4764
|
+
0,
|
|
4765
|
+
childBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap
|
|
4766
|
+
);
|
|
4767
|
+
const equalDelta = Math.max(deltaUp, deltaDown);
|
|
4768
|
+
for (const node of existingNodes) {
|
|
4769
|
+
if (node.id === originNode.id) continue;
|
|
4770
|
+
const isSiblingRow = Math.abs(node.position.y - originNode.position.y) < siblingTolerance;
|
|
4771
|
+
switch (direction) {
|
|
4772
|
+
case "right": {
|
|
4773
|
+
if (node.position.x > originNode.position.x) {
|
|
4774
|
+
displacedMap.set(node.id, {
|
|
4775
|
+
x: node.position.x + deltaPrimary,
|
|
4776
|
+
y: node.position.y
|
|
4777
|
+
});
|
|
4778
|
+
}
|
|
4779
|
+
break;
|
|
4780
|
+
}
|
|
4781
|
+
case "left": {
|
|
4782
|
+
if (node.position.x < originNode.position.x) {
|
|
4783
|
+
displacedMap.set(node.id, {
|
|
4784
|
+
x: node.position.x - deltaPrimary,
|
|
4785
|
+
y: node.position.y
|
|
4786
|
+
});
|
|
4787
|
+
}
|
|
4788
|
+
break;
|
|
4789
|
+
}
|
|
4790
|
+
case "down": {
|
|
4791
|
+
if (node.position.y > originNode.position.y) {
|
|
4792
|
+
displacedMap.set(node.id, {
|
|
4793
|
+
x: node.position.x,
|
|
4794
|
+
y: node.position.y + deltaPrimary
|
|
4795
|
+
});
|
|
4796
|
+
}
|
|
4797
|
+
break;
|
|
4798
|
+
}
|
|
4799
|
+
case "up": {
|
|
4800
|
+
if (node.position.y < originNode.position.y) {
|
|
4801
|
+
displacedMap.set(node.id, {
|
|
4802
|
+
x: node.position.x,
|
|
4803
|
+
y: node.position.y - deltaPrimary
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4806
|
+
break;
|
|
4807
|
+
}
|
|
4808
|
+
case "vertical": {
|
|
4809
|
+
if (isSiblingRow) break;
|
|
4810
|
+
if (node.position.y < originNode.position.y) {
|
|
4811
|
+
displacedMap.set(node.id, {
|
|
4812
|
+
x: node.position.x,
|
|
4813
|
+
y: node.position.y - equalDelta
|
|
4814
|
+
});
|
|
4815
|
+
} else if (node.position.y > originNode.position.y) {
|
|
4816
|
+
displacedMap.set(node.id, {
|
|
4817
|
+
x: node.position.x,
|
|
4818
|
+
y: node.position.y + equalDelta
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4821
|
+
break;
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
if (spreadSiblingLanes && (direction === "right" || direction === "left")) {
|
|
4826
|
+
for (const [nodeId, pos] of displacedMap.entries()) {
|
|
4827
|
+
const node = existingNodes.find((n) => n.id === nodeId);
|
|
4828
|
+
if (node && Math.abs(node.position.y - originNode.position.y) < siblingTolerance) {
|
|
4829
|
+
const isAbove = node.position.y < originNode.position.y;
|
|
4830
|
+
displacedMap.set(nodeId, {
|
|
4831
|
+
x: pos.x,
|
|
4832
|
+
y: pos.y + (isAbove ? -deltaUp : deltaDown)
|
|
4833
|
+
});
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
if (minY !== void 0) {
|
|
4838
|
+
for (const [nodeId, pos] of displacedMap.entries()) {
|
|
4839
|
+
if (pos.y < minY) {
|
|
4840
|
+
displacedMap.set(nodeId, { x: pos.x, y: minY });
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
return displacedMap;
|
|
4845
|
+
}
|
|
4846
|
+
function calculateDagreReflowDisplacement(params) {
|
|
4847
|
+
const {
|
|
4848
|
+
currentNodes,
|
|
4849
|
+
futureNodes,
|
|
4850
|
+
futureEdges,
|
|
4851
|
+
options
|
|
4852
|
+
} = params;
|
|
4853
|
+
const {
|
|
4854
|
+
anchorNodeId,
|
|
4855
|
+
direction = "LR",
|
|
4856
|
+
ranksep = 100,
|
|
4857
|
+
nodesep = 25,
|
|
4858
|
+
defaultNodeWidth = 200,
|
|
4859
|
+
defaultNodeHeight = 60
|
|
4860
|
+
} = options;
|
|
4861
|
+
const anchorNode = currentNodes.find((n) => n.id === anchorNodeId);
|
|
4862
|
+
if (!anchorNode) return /* @__PURE__ */ new Map();
|
|
4863
|
+
const g = new import_dagre2.default.graphlib.Graph();
|
|
4864
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
4865
|
+
g.setGraph({ rankdir: direction, ranksep, nodesep });
|
|
4866
|
+
const nodeIds = new Set(futureNodes.map((n) => n.id));
|
|
4867
|
+
futureNodes.forEach((node) => {
|
|
4868
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4869
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4870
|
+
g.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
|
4871
|
+
});
|
|
4872
|
+
futureEdges.forEach((edge) => {
|
|
4873
|
+
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
4874
|
+
g.setEdge(edge.source, edge.target);
|
|
4875
|
+
}
|
|
4876
|
+
});
|
|
4877
|
+
import_dagre2.default.layout(g);
|
|
4878
|
+
const anchorLayoutPos = g.node(anchorNodeId);
|
|
4879
|
+
if (!anchorLayoutPos) return /* @__PURE__ */ new Map();
|
|
4880
|
+
const anchorWidth = anchorNode.measured?.width ?? anchorNode.width ?? defaultNodeWidth;
|
|
4881
|
+
const anchorHeight = anchorNode.measured?.height ?? anchorNode.height ?? defaultNodeHeight;
|
|
4882
|
+
const anchorLayoutUpperLeft = {
|
|
4883
|
+
x: anchorLayoutPos.x - anchorWidth / 2,
|
|
4884
|
+
y: anchorLayoutPos.y - anchorHeight / 2
|
|
4885
|
+
};
|
|
4886
|
+
const deltaX = anchorNode.position.x - anchorLayoutUpperLeft.x;
|
|
4887
|
+
const deltaY = anchorNode.position.y - anchorLayoutUpperLeft.y;
|
|
4888
|
+
const displacementMap = /* @__PURE__ */ new Map();
|
|
4889
|
+
currentNodes.forEach((node) => {
|
|
4890
|
+
if (node.id === anchorNodeId) return;
|
|
4891
|
+
const layoutNode = g.node(node.id);
|
|
4892
|
+
if (!layoutNode) return;
|
|
4893
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4894
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4895
|
+
const upperLeftX = layoutNode.x - nodeWidth / 2 + deltaX;
|
|
4896
|
+
const upperLeftY = layoutNode.y - nodeHeight / 2 + deltaY;
|
|
4897
|
+
if (Math.abs(upperLeftX - node.position.x) > 1 || Math.abs(upperLeftY - node.position.y) > 1) {
|
|
4898
|
+
displacementMap.set(node.id, { x: upperLeftX, y: upperLeftY });
|
|
4899
|
+
}
|
|
4900
|
+
});
|
|
4901
|
+
return displacementMap;
|
|
4902
|
+
}
|
|
4903
|
+
|
|
4904
|
+
// src/canvas/clipboard.ts
|
|
4905
|
+
function isEventFromTextInput(event) {
|
|
4906
|
+
const target = event.target;
|
|
4907
|
+
if (!target) return false;
|
|
4908
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable || target.closest?.('[contenteditable="true"]') || target.closest?.(".ProseMirror")) {
|
|
4909
|
+
return true;
|
|
4910
|
+
}
|
|
4911
|
+
return false;
|
|
4912
|
+
}
|
|
4913
|
+
function isValidCanvasNode(obj) {
|
|
4914
|
+
return typeof obj === "object" && obj !== null && "id" in obj && typeof obj.id === "string" && "position" in obj && typeof obj.position === "object" && obj.position !== null && "x" in obj.position && "y" in obj.position;
|
|
4915
|
+
}
|
|
4916
|
+
function isValidUrl(url) {
|
|
4917
|
+
try {
|
|
4918
|
+
const parsed = new URL(url);
|
|
4919
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
4920
|
+
} catch {
|
|
4921
|
+
return false;
|
|
4922
|
+
}
|
|
4923
|
+
}
|
|
4924
|
+
function serializeCanvasNodes(nodes, edges) {
|
|
4925
|
+
const payload = {
|
|
4926
|
+
version: 1,
|
|
4927
|
+
nodes,
|
|
4928
|
+
edges
|
|
4929
|
+
};
|
|
4930
|
+
return JSON.stringify(payload);
|
|
4931
|
+
}
|
|
4932
|
+
function parseCanvasClipboardData(raw) {
|
|
4933
|
+
try {
|
|
4934
|
+
const parsed = JSON.parse(raw);
|
|
4935
|
+
if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.nodes) && parsed.nodes.length > 0 && parsed.nodes.every(isValidCanvasNode)) {
|
|
4936
|
+
return parsed;
|
|
4937
|
+
}
|
|
4938
|
+
} catch {
|
|
4939
|
+
}
|
|
4940
|
+
return null;
|
|
4941
|
+
}
|
|
4942
|
+
function remapPastedNodes(nodes, edges = [], offset = { x: 30, y: 30 }, idGenerator = () => `node-${Math.random().toString(36).substring(2, 9)}`) {
|
|
4943
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
4944
|
+
const newNodes = nodes.map((node) => {
|
|
4945
|
+
const newId = idGenerator();
|
|
4946
|
+
idMap.set(node.id, newId);
|
|
4947
|
+
return {
|
|
4948
|
+
...node,
|
|
4949
|
+
id: newId,
|
|
4950
|
+
position: {
|
|
4951
|
+
x: node.position.x + offset.x,
|
|
4952
|
+
y: node.position.y + offset.y
|
|
4953
|
+
},
|
|
4954
|
+
selected: true
|
|
4955
|
+
};
|
|
4956
|
+
});
|
|
4957
|
+
const newEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((edge) => ({
|
|
4958
|
+
...edge,
|
|
4959
|
+
id: `edge-${Math.random().toString(36).substring(2, 9)}`,
|
|
4960
|
+
source: idMap.get(edge.source),
|
|
4961
|
+
target: idMap.get(edge.target),
|
|
4962
|
+
selected: false
|
|
4963
|
+
}));
|
|
4964
|
+
return { nodes: newNodes, edges: newEdges };
|
|
4965
|
+
}
|
|
4966
|
+
function repositionNodesTo(nodes, targetOrigin) {
|
|
4967
|
+
if (nodes.length === 0) return [];
|
|
4968
|
+
const minX = Math.min(...nodes.map((n) => n.position.x));
|
|
4969
|
+
const minY = Math.min(...nodes.map((n) => n.position.y));
|
|
4970
|
+
const deltaX = targetOrigin.x - minX;
|
|
4971
|
+
const deltaY = targetOrigin.y - minY;
|
|
4972
|
+
return nodes.map((n) => ({
|
|
4973
|
+
...n,
|
|
4974
|
+
position: {
|
|
4975
|
+
x: n.position.x + deltaX,
|
|
4976
|
+
y: n.position.y + deltaY
|
|
4977
|
+
}
|
|
4978
|
+
}));
|
|
4979
|
+
}
|
|
4980
|
+
|
|
4981
|
+
// src/canvas/streaming.ts
|
|
4982
|
+
var StreamingLayoutManager = class {
|
|
4983
|
+
throttleMs;
|
|
4984
|
+
glideDurationMs;
|
|
4985
|
+
layoutOptions;
|
|
4986
|
+
onLayoutUpdated;
|
|
4987
|
+
pendingTimer = null;
|
|
4988
|
+
activeTransitionCancel = null;
|
|
4989
|
+
targetPositions = /* @__PURE__ */ new Map();
|
|
4990
|
+
constructor(options = {}) {
|
|
4991
|
+
this.throttleMs = options.throttleMs ?? 200;
|
|
4992
|
+
this.glideDurationMs = options.glideDurationMs ?? 300;
|
|
4993
|
+
this.layoutOptions = options.layoutOptions ?? { direction: "LR" };
|
|
4994
|
+
this.onLayoutUpdated = options.onLayoutUpdated;
|
|
4995
|
+
}
|
|
4996
|
+
/**
|
|
4997
|
+
* Feed incoming nodes and edges into the streaming manager.
|
|
4998
|
+
* Coalesces rapid streaming bursts and triggers smooth layout gliding.
|
|
4999
|
+
*/
|
|
5000
|
+
pushStreamUpdate(nodes, edges, currentPositions) {
|
|
5001
|
+
if (this.pendingTimer) {
|
|
5002
|
+
clearTimeout(this.pendingTimer);
|
|
5003
|
+
}
|
|
5004
|
+
this.pendingTimer = setTimeout(() => {
|
|
5005
|
+
this.pendingTimer = null;
|
|
5006
|
+
this.recalculateAndGlide(nodes, edges, currentPositions);
|
|
5007
|
+
}, this.throttleMs);
|
|
5008
|
+
}
|
|
5009
|
+
/**
|
|
5010
|
+
* Forces immediate calculation and cancels pending throttled timers.
|
|
5011
|
+
*/
|
|
5012
|
+
flush(nodes, edges, currentPositions) {
|
|
5013
|
+
if (this.pendingTimer) {
|
|
5014
|
+
clearTimeout(this.pendingTimer);
|
|
5015
|
+
this.pendingTimer = null;
|
|
5016
|
+
}
|
|
5017
|
+
this.recalculateAndGlide(nodes, edges, currentPositions);
|
|
5018
|
+
}
|
|
5019
|
+
recalculateAndGlide(nodes, edges, currentPositions) {
|
|
5020
|
+
if (nodes.length === 0) return;
|
|
5021
|
+
if (this.activeTransitionCancel) {
|
|
5022
|
+
this.activeTransitionCancel();
|
|
5023
|
+
this.activeTransitionCancel = null;
|
|
5024
|
+
}
|
|
5025
|
+
const stored = {};
|
|
5026
|
+
for (const [id, pos] of currentPositions.entries()) {
|
|
5027
|
+
stored[id] = pos;
|
|
5028
|
+
}
|
|
5029
|
+
const layouted = ensureLayout(nodes, edges, {
|
|
5030
|
+
...this.layoutOptions,
|
|
5031
|
+
storedPositions: stored
|
|
5032
|
+
});
|
|
5033
|
+
const nextPositions = /* @__PURE__ */ new Map();
|
|
5034
|
+
const transitions = [];
|
|
5035
|
+
for (const node of layouted) {
|
|
5036
|
+
const target = node.position;
|
|
5037
|
+
nextPositions.set(node.id, target);
|
|
5038
|
+
const current = currentPositions.get(node.id) ?? target;
|
|
5039
|
+
const distance = Math.hypot(target.x - current.x, target.y - current.y);
|
|
5040
|
+
if (distance > 2) {
|
|
5041
|
+
transitions.push({
|
|
5042
|
+
node,
|
|
5043
|
+
from: current,
|
|
5044
|
+
to: target,
|
|
5045
|
+
trajectory: linearPositionTrajectory,
|
|
5046
|
+
duration: this.glideDurationMs
|
|
5047
|
+
});
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
this.targetPositions = nextPositions;
|
|
5051
|
+
if (transitions.length > 0) {
|
|
5052
|
+
this.activeTransitionCancel = runMultiNodeTransition(transitions, {
|
|
5053
|
+
onUpdate: () => {
|
|
5054
|
+
const liveMap = new Map(currentPositions);
|
|
5055
|
+
for (const trans of transitions) {
|
|
5056
|
+
liveMap.set(trans.node.id, { ...trans.node.position });
|
|
5057
|
+
}
|
|
5058
|
+
this.onLayoutUpdated?.(liveMap);
|
|
5059
|
+
},
|
|
5060
|
+
onComplete: () => {
|
|
5061
|
+
this.activeTransitionCancel = null;
|
|
5062
|
+
this.onLayoutUpdated?.(nextPositions);
|
|
5063
|
+
}
|
|
5064
|
+
});
|
|
5065
|
+
} else {
|
|
5066
|
+
this.onLayoutUpdated?.(nextPositions);
|
|
5067
|
+
}
|
|
5068
|
+
}
|
|
5069
|
+
destroy() {
|
|
5070
|
+
if (this.pendingTimer) {
|
|
5071
|
+
clearTimeout(this.pendingTimer);
|
|
5072
|
+
this.pendingTimer = null;
|
|
5073
|
+
}
|
|
5074
|
+
if (this.activeTransitionCancel) {
|
|
5075
|
+
this.activeTransitionCancel();
|
|
5076
|
+
this.activeTransitionCancel = null;
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
};
|
|
5080
|
+
|
|
5081
|
+
// src/agent/planner.ts
|
|
5082
|
+
function planAgentCommand(command, snapshot, options = {}) {
|
|
5083
|
+
const defaultNodeSize = options.defaultNodeSize ?? { width: 200, height: 60 };
|
|
5084
|
+
const idGen = options.idGenerator ?? (() => `node-${Math.random().toString(36).substring(2, 9)}`);
|
|
5085
|
+
const edgeIdGen = () => `edge-${Math.random().toString(36).substring(2, 9)}`;
|
|
5086
|
+
const nodes = snapshot.canvas?.nodes ?? [];
|
|
5087
|
+
const edges = snapshot.canvas?.edges ?? [];
|
|
5088
|
+
switch (command.type) {
|
|
5089
|
+
case "canvas:add_node": {
|
|
5090
|
+
let pos = command.node.position;
|
|
5091
|
+
if (!pos) {
|
|
5092
|
+
if (command.nearNodeId) {
|
|
5093
|
+
const rects = {};
|
|
5094
|
+
for (const n of nodes) {
|
|
5095
|
+
rects[n.id] = {
|
|
5096
|
+
x: n.position.x,
|
|
5097
|
+
y: n.position.y,
|
|
5098
|
+
width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
|
|
5099
|
+
height: n.measured?.height ?? n.height ?? defaultNodeSize.height
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
pos = placeBeside(
|
|
5103
|
+
rects,
|
|
5104
|
+
defaultNodeSize,
|
|
5105
|
+
command.side ?? "right",
|
|
5106
|
+
command.nearNodeId
|
|
5107
|
+
);
|
|
5108
|
+
} else if (nodes.length > 0) {
|
|
5109
|
+
const lastNode = nodes[nodes.length - 1];
|
|
5110
|
+
const rects = {};
|
|
5111
|
+
for (const n of nodes) {
|
|
5112
|
+
rects[n.id] = {
|
|
5113
|
+
x: n.position.x,
|
|
5114
|
+
y: n.position.y,
|
|
5115
|
+
width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
|
|
5116
|
+
height: n.measured?.height ?? n.height ?? defaultNodeSize.height
|
|
5117
|
+
};
|
|
5118
|
+
}
|
|
5119
|
+
pos = placeBeside(rects, defaultNodeSize, "right", lastNode.id);
|
|
5120
|
+
} else {
|
|
5121
|
+
pos = { x: 100, y: 100 };
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
const fullNode = {
|
|
5125
|
+
...command.node,
|
|
5126
|
+
id: command.node.id || idGen(),
|
|
5127
|
+
position: pos
|
|
5128
|
+
};
|
|
5129
|
+
const steps = [
|
|
5130
|
+
{
|
|
5131
|
+
type: "step:canvas_add_node",
|
|
5132
|
+
node: fullNode
|
|
5133
|
+
}
|
|
5134
|
+
];
|
|
5135
|
+
if (command.nearNodeId) {
|
|
5136
|
+
steps.push({
|
|
5137
|
+
type: "step:canvas_add_edge",
|
|
5138
|
+
edge: {
|
|
5139
|
+
id: edgeIdGen(),
|
|
5140
|
+
source: command.nearNodeId,
|
|
5141
|
+
target: fullNode.id
|
|
5142
|
+
}
|
|
5143
|
+
});
|
|
5144
|
+
}
|
|
5145
|
+
return steps;
|
|
5146
|
+
}
|
|
5147
|
+
case "canvas:remove_nodes": {
|
|
5148
|
+
return [
|
|
5149
|
+
{
|
|
5150
|
+
type: "step:canvas_remove_nodes",
|
|
5151
|
+
nodeIds: command.nodeIds
|
|
5152
|
+
}
|
|
5153
|
+
];
|
|
5154
|
+
}
|
|
5155
|
+
case "canvas:connect": {
|
|
5156
|
+
return [
|
|
5157
|
+
{
|
|
5158
|
+
type: "step:canvas_add_edge",
|
|
5159
|
+
edge: {
|
|
5160
|
+
id: edgeIdGen(),
|
|
5161
|
+
source: command.source,
|
|
5162
|
+
target: command.target,
|
|
5163
|
+
label: command.label
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
];
|
|
5167
|
+
}
|
|
5168
|
+
case "canvas:update_node": {
|
|
5169
|
+
return [
|
|
5170
|
+
{
|
|
5171
|
+
type: "step:canvas_update_node",
|
|
5172
|
+
id: command.id,
|
|
5173
|
+
data: command.data
|
|
5174
|
+
}
|
|
5175
|
+
];
|
|
5176
|
+
}
|
|
5177
|
+
case "canvas:layout": {
|
|
5178
|
+
const dir = command.direction ?? "LR";
|
|
5179
|
+
let layouted;
|
|
5180
|
+
if (command.incremental) {
|
|
5181
|
+
const stored = {};
|
|
5182
|
+
for (const n of nodes) {
|
|
5183
|
+
if (n.position && (n.position.x !== 0 || n.position.y !== 0)) {
|
|
5184
|
+
stored[n.id] = { ...n.position };
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
5187
|
+
layouted = ensureLayout(nodes, edges, {
|
|
5188
|
+
direction: dir,
|
|
5189
|
+
storedPositions: stored
|
|
5190
|
+
});
|
|
5191
|
+
} else {
|
|
5192
|
+
layouted = getLayoutedNodes(nodes, edges, void 0, { direction: dir });
|
|
5193
|
+
}
|
|
5194
|
+
const positions = {};
|
|
5195
|
+
for (const node of layouted) {
|
|
5196
|
+
positions[node.id] = { ...node.position };
|
|
5197
|
+
}
|
|
5198
|
+
return [
|
|
5199
|
+
{
|
|
5200
|
+
type: "step:canvas_move_nodes",
|
|
5201
|
+
positions
|
|
5202
|
+
}
|
|
5203
|
+
];
|
|
5204
|
+
}
|
|
5205
|
+
case "canvas:select": {
|
|
5206
|
+
return [
|
|
5207
|
+
{
|
|
5208
|
+
type: "step:canvas_select",
|
|
5209
|
+
nodeIds: command.nodeIds
|
|
5210
|
+
}
|
|
5211
|
+
];
|
|
5212
|
+
}
|
|
5213
|
+
case "canvas:highlight": {
|
|
5214
|
+
return [
|
|
5215
|
+
{
|
|
5216
|
+
type: "step:canvas_highlight",
|
|
5217
|
+
nodeIds: command.nodeIds,
|
|
5218
|
+
color: command.color ?? "#3b82f6",
|
|
5219
|
+
durationMs: command.durationMs ?? 2e3
|
|
5220
|
+
}
|
|
5221
|
+
];
|
|
5222
|
+
}
|
|
5223
|
+
case "prompt:insert_text": {
|
|
5224
|
+
return [
|
|
5225
|
+
{
|
|
5226
|
+
type: "step:prompt_insert",
|
|
5227
|
+
text: command.text,
|
|
5228
|
+
at: command.at
|
|
5229
|
+
}
|
|
5230
|
+
];
|
|
5231
|
+
}
|
|
5232
|
+
case "prompt:replace_range": {
|
|
5233
|
+
return [
|
|
5234
|
+
{
|
|
5235
|
+
type: "step:prompt_replace",
|
|
5236
|
+
from: command.from,
|
|
5237
|
+
to: command.to,
|
|
5238
|
+
text: command.text
|
|
5239
|
+
}
|
|
5240
|
+
];
|
|
5241
|
+
}
|
|
5242
|
+
case "prompt:set_variable": {
|
|
5243
|
+
return [
|
|
5244
|
+
{
|
|
5245
|
+
type: "step:prompt_set_variable",
|
|
5246
|
+
name: command.name,
|
|
5247
|
+
value: command.value
|
|
5248
|
+
}
|
|
5249
|
+
];
|
|
5250
|
+
}
|
|
5251
|
+
case "prompt:select": {
|
|
5252
|
+
return [
|
|
5253
|
+
{
|
|
5254
|
+
type: "step:prompt_select",
|
|
5255
|
+
from: command.from,
|
|
5256
|
+
to: command.to
|
|
5257
|
+
}
|
|
5258
|
+
];
|
|
5259
|
+
}
|
|
5260
|
+
default: {
|
|
5261
|
+
return [];
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5265
|
+
|
|
5266
|
+
// src/agent/tools.ts
|
|
5267
|
+
var MCP_TOOL_GET_EDITOR_STATE = {
|
|
5268
|
+
name: "get_editor_state",
|
|
5269
|
+
description: "Retrieves the current state of the document, variables, selection, and canvas graph (nodes and edges).",
|
|
5270
|
+
inputSchema: {
|
|
5271
|
+
type: "object",
|
|
5272
|
+
properties: {
|
|
5273
|
+
includeDocument: {
|
|
5274
|
+
type: "boolean",
|
|
5275
|
+
description: "Whether to include the prompt document text and variables (default: true)."
|
|
5276
|
+
},
|
|
5277
|
+
includeCanvas: {
|
|
5278
|
+
type: "boolean",
|
|
5279
|
+
description: "Whether to include the canvas nodes and edges (default: true)."
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
};
|
|
5284
|
+
var MCP_TOOL_EDIT_CANVAS = {
|
|
5285
|
+
name: "edit_canvas",
|
|
5286
|
+
description: "Applies high-level structural operations to the visual canvas (adding nodes, connecting edges, organizing layout, or moving elements).",
|
|
5287
|
+
inputSchema: {
|
|
5288
|
+
type: "object",
|
|
5289
|
+
properties: {
|
|
5290
|
+
operation: {
|
|
5291
|
+
type: "string",
|
|
5292
|
+
enum: [
|
|
5293
|
+
"add_node",
|
|
5294
|
+
"remove_nodes",
|
|
5295
|
+
"connect",
|
|
5296
|
+
"update_node",
|
|
5297
|
+
"layout",
|
|
5298
|
+
"select",
|
|
5299
|
+
"highlight"
|
|
5300
|
+
],
|
|
5301
|
+
description: "The canvas operation to execute."
|
|
5302
|
+
},
|
|
5303
|
+
node: {
|
|
5304
|
+
type: "object",
|
|
5305
|
+
description: "Node definition when operation is add_node. Can omit position to auto-place."
|
|
5306
|
+
},
|
|
5307
|
+
nearNodeId: {
|
|
5308
|
+
type: "string",
|
|
5309
|
+
description: "Anchor node ID to position beside using collision-aware placement."
|
|
5310
|
+
},
|
|
5311
|
+
side: {
|
|
5312
|
+
type: "string",
|
|
5313
|
+
enum: ["top", "right", "bottom", "left"],
|
|
5314
|
+
description: "Side of the anchor node to place the new node beside."
|
|
5315
|
+
},
|
|
5316
|
+
nodeIds: {
|
|
5317
|
+
type: "array",
|
|
5318
|
+
items: { type: "string" },
|
|
5319
|
+
description: "Array of node IDs for remove_nodes, select, or highlight."
|
|
5320
|
+
},
|
|
5321
|
+
source: {
|
|
5322
|
+
type: "string",
|
|
5323
|
+
description: "Source node ID for connect operation."
|
|
5324
|
+
},
|
|
5325
|
+
target: {
|
|
5326
|
+
type: "string",
|
|
5327
|
+
description: "Target node ID for connect operation."
|
|
5328
|
+
},
|
|
5329
|
+
label: {
|
|
5330
|
+
type: "string",
|
|
5331
|
+
description: "Optional edge label for connect operation."
|
|
5332
|
+
},
|
|
5333
|
+
direction: {
|
|
5334
|
+
type: "string",
|
|
5335
|
+
enum: ["LR", "TB", "RL", "BT"],
|
|
5336
|
+
description: "Layout direction (default LR)."
|
|
5337
|
+
},
|
|
5338
|
+
incremental: {
|
|
5339
|
+
type: "boolean",
|
|
5340
|
+
description: "Preserve existing node coordinates and blend layout (default true)."
|
|
5341
|
+
}
|
|
5342
|
+
},
|
|
5343
|
+
required: ["operation"]
|
|
5344
|
+
}
|
|
5345
|
+
};
|
|
5346
|
+
var MCP_TOOL_EDIT_PROMPT = {
|
|
5347
|
+
name: "edit_prompt",
|
|
5348
|
+
description: "Applies edits to the prompt template document, including inserting text, replacing ranges, and setting variables.",
|
|
5349
|
+
inputSchema: {
|
|
5350
|
+
type: "object",
|
|
5351
|
+
properties: {
|
|
5352
|
+
operation: {
|
|
5353
|
+
type: "string",
|
|
5354
|
+
enum: ["insert_text", "replace_range", "set_variable", "select"],
|
|
5355
|
+
description: "The prompt operation to execute."
|
|
5356
|
+
},
|
|
5357
|
+
text: {
|
|
5358
|
+
type: "string",
|
|
5359
|
+
description: "Text to insert or replace."
|
|
5360
|
+
},
|
|
5361
|
+
at: {
|
|
5362
|
+
type: "number",
|
|
5363
|
+
description: "Position to insert text at."
|
|
5364
|
+
},
|
|
5365
|
+
from: {
|
|
5366
|
+
type: "number",
|
|
5367
|
+
description: "Start offset for replace_range or select."
|
|
5368
|
+
},
|
|
5369
|
+
to: {
|
|
5370
|
+
type: "number",
|
|
5371
|
+
description: "End offset for replace_range or select."
|
|
5372
|
+
},
|
|
5373
|
+
name: {
|
|
5374
|
+
type: "string",
|
|
5375
|
+
description: "Variable name for set_variable."
|
|
5376
|
+
},
|
|
5377
|
+
value: {
|
|
5378
|
+
description: "Variable value for set_variable."
|
|
5379
|
+
}
|
|
5380
|
+
},
|
|
5381
|
+
required: ["operation"]
|
|
5382
|
+
}
|
|
5383
|
+
};
|
|
5384
|
+
var EDITOR_MCP_TOOLS = [
|
|
5385
|
+
MCP_TOOL_GET_EDITOR_STATE,
|
|
5386
|
+
MCP_TOOL_EDIT_CANVAS,
|
|
5387
|
+
MCP_TOOL_EDIT_PROMPT
|
|
5388
|
+
];
|
|
4163
5389
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4164
5390
|
0 && (module.exports = {
|
|
4165
5391
|
ColorHighlighter,
|
|
5392
|
+
DEFAULT_NODE_HEIGHT,
|
|
5393
|
+
DEFAULT_NODE_WIDTH,
|
|
5394
|
+
DEFAULT_PLACEMENT_GAP,
|
|
4166
5395
|
DataSourceBinder,
|
|
4167
5396
|
DependencyGraph,
|
|
4168
5397
|
DirtyTracker,
|
|
5398
|
+
EDITOR_MCP_TOOLS,
|
|
4169
5399
|
EditorController,
|
|
4170
5400
|
FontSize,
|
|
4171
5401
|
FormulaEvaluator,
|
|
@@ -4175,48 +5405,86 @@ function createHistoryManager(options = {}) {
|
|
|
4175
5405
|
INSTRUCTION_REGEX,
|
|
4176
5406
|
Image,
|
|
4177
5407
|
LoadingNode,
|
|
5408
|
+
MARKDOWN_MENTION_SPLIT_REGEX,
|
|
5409
|
+
MCP_TOOL_EDIT_CANVAS,
|
|
5410
|
+
MCP_TOOL_EDIT_PROMPT,
|
|
5411
|
+
MCP_TOOL_GET_EDITOR_STATE,
|
|
5412
|
+
MENTION_REGEX,
|
|
4178
5413
|
Markdown,
|
|
4179
5414
|
Mention,
|
|
4180
5415
|
SlashCommands,
|
|
4181
5416
|
SmilieReplacer,
|
|
4182
5417
|
SpreadsheetController,
|
|
5418
|
+
StreamingLayoutManager,
|
|
4183
5419
|
TEMPLATE_REGEX,
|
|
4184
5420
|
TemplateSuggestions,
|
|
4185
5421
|
VARIABLE_REGEX,
|
|
5422
|
+
boundsOf,
|
|
5423
|
+
calculateDagreReflowDisplacement,
|
|
5424
|
+
calculateDirectionalDisplacement,
|
|
5425
|
+
centerNodes,
|
|
5426
|
+
centre,
|
|
4186
5427
|
colNameToIndex,
|
|
5428
|
+
computeNodesBoundingBox,
|
|
5429
|
+
createBezierTrajectory,
|
|
4187
5430
|
createDirtyTracker,
|
|
4188
5431
|
createEditor,
|
|
5432
|
+
createFanOutTrajectory,
|
|
4189
5433
|
createHistoryManager,
|
|
4190
5434
|
createMentionExtension,
|
|
4191
5435
|
createRichTextPreset,
|
|
5436
|
+
defaultEasing,
|
|
4192
5437
|
defaultIsEqual,
|
|
5438
|
+
ensureLayout,
|
|
4193
5439
|
escapeHtml,
|
|
5440
|
+
estimateNodeSize,
|
|
4194
5441
|
extractCellDependencies,
|
|
4195
5442
|
extractInstructionNames,
|
|
5443
|
+
extractMentions,
|
|
4196
5444
|
extractMentionsFromDoc,
|
|
4197
5445
|
extractTemplateNames,
|
|
4198
5446
|
extractVariableNames,
|
|
4199
5447
|
findHexColors,
|
|
5448
|
+
flowSide,
|
|
5449
|
+
getCenteredNodePosition,
|
|
4200
5450
|
getEditorMarkdown,
|
|
4201
5451
|
getEditorText,
|
|
4202
5452
|
getLatestVersion,
|
|
5453
|
+
getLayoutedNodes,
|
|
4203
5454
|
handleImageInsertion,
|
|
4204
5455
|
htmlToPlainText,
|
|
4205
5456
|
imagePastePluginKey,
|
|
4206
5457
|
indexToColName,
|
|
4207
5458
|
insertImageNode,
|
|
4208
5459
|
instructionRegex,
|
|
5460
|
+
isEventFromTextInput,
|
|
5461
|
+
isValidCanvasNode,
|
|
5462
|
+
isValidUrl,
|
|
5463
|
+
linearPositionTrajectory,
|
|
5464
|
+
linearTrajectory,
|
|
4209
5465
|
mapRawOffsetToResolved,
|
|
4210
5466
|
mapResolvedOffsetToRaw,
|
|
4211
5467
|
markdownToTipTapHtml,
|
|
5468
|
+
overlaps,
|
|
5469
|
+
parseCanvasClipboardData,
|
|
4212
5470
|
parseCellReference,
|
|
4213
5471
|
parseFormula,
|
|
5472
|
+
parseMentionSegments,
|
|
4214
5473
|
parseRange,
|
|
5474
|
+
placeBeside,
|
|
4215
5475
|
plainTextToTipTapHtml,
|
|
5476
|
+
planAgentCommand,
|
|
5477
|
+
preprocessMarkdownFileLinks,
|
|
5478
|
+
preprocessMarkdownMentions,
|
|
5479
|
+
remapPastedNodes,
|
|
4216
5480
|
removeImageNode,
|
|
5481
|
+
repositionNodesTo,
|
|
4217
5482
|
resolveTemplates,
|
|
4218
5483
|
resolveTemplatesWithMapping,
|
|
4219
5484
|
resolveVariables,
|
|
5485
|
+
runMultiNodeTransition,
|
|
5486
|
+
serializeCanvasNodes,
|
|
5487
|
+
stripMarkdownCode,
|
|
4220
5488
|
templateRegex,
|
|
4221
5489
|
tokenizeFormula,
|
|
4222
5490
|
tokenizePrompt,
|