@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.js
CHANGED
|
@@ -27,10 +27,10 @@ function tokenizePrompt(text) {
|
|
|
27
27
|
while ((match = varRegex.exec(text)) !== null) {
|
|
28
28
|
const start = match.index;
|
|
29
29
|
const end = start + match[0].length;
|
|
30
|
-
const
|
|
30
|
+
const overlaps2 = rawMatches.some(
|
|
31
31
|
(m) => !(end <= m.start || start >= m.end)
|
|
32
32
|
);
|
|
33
|
-
if (!
|
|
33
|
+
if (!overlaps2) {
|
|
34
34
|
rawMatches.push({
|
|
35
35
|
type: "variable",
|
|
36
36
|
raw: match[0],
|
|
@@ -47,10 +47,10 @@ function tokenizePrompt(text) {
|
|
|
47
47
|
while ((match = instRegex.exec(text)) !== null) {
|
|
48
48
|
const start = match.index;
|
|
49
49
|
const end = start + match[0].length;
|
|
50
|
-
const
|
|
50
|
+
const overlaps2 = rawMatches.some(
|
|
51
51
|
(m) => !(end <= m.start || start >= m.end)
|
|
52
52
|
);
|
|
53
|
-
if (!
|
|
53
|
+
if (!overlaps2) {
|
|
54
54
|
rawMatches.push({
|
|
55
55
|
type: "instruction",
|
|
56
56
|
raw: match[0],
|
|
@@ -2200,6 +2200,150 @@ function createRichTextPreset(options = {}) {
|
|
|
2200
2200
|
return extensions2;
|
|
2201
2201
|
}
|
|
2202
2202
|
|
|
2203
|
+
// src/serializers/mentions.ts
|
|
2204
|
+
var MENTION_REGEX = /(?:^|[\s>(*_"';\\/[\]{}~`:]|"|>|<|&)@(?:"([^"]+)"|"([^&]+)"|([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*))/g;
|
|
2205
|
+
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;
|
|
2206
|
+
function stripMarkdownCode(content) {
|
|
2207
|
+
if (!content || typeof content !== "string") return "";
|
|
2208
|
+
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, "");
|
|
2209
|
+
}
|
|
2210
|
+
function extractMentions(content) {
|
|
2211
|
+
if (!content || typeof content !== "string") return [];
|
|
2212
|
+
const normalizedMentionNodes = content.replace(
|
|
2213
|
+
/<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["'])[^>]*>([\s\S]*?)<\/span>/gi,
|
|
2214
|
+
(match2, inner) => {
|
|
2215
|
+
const labelMatch = match2.match(/\bdata-label=["']([^"']+)["']/i);
|
|
2216
|
+
if (labelMatch) return ` @${labelMatch[1]} `;
|
|
2217
|
+
const idMatch = match2.match(/\bdata-id=["']([^"']+)["']/i);
|
|
2218
|
+
if (idMatch) return ` @${idMatch[1]} `;
|
|
2219
|
+
return ` ${inner} `;
|
|
2220
|
+
}
|
|
2221
|
+
);
|
|
2222
|
+
const cleanContent = stripMarkdownCode(normalizedMentionNodes);
|
|
2223
|
+
const withoutUrls = cleanContent.replace(/(?:https?|ftp|file|vscode|cursor|mailto):\/\/[^\s<>()]+/gi, "").replace(/\]\([^)]*\)/g, "]");
|
|
2224
|
+
const textOnly = withoutUrls.replace(/<[^>]+>/g, " ");
|
|
2225
|
+
const mentions = [];
|
|
2226
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2227
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2228
|
+
let match;
|
|
2229
|
+
while ((match = regex.exec(textOnly)) !== null) {
|
|
2230
|
+
const handle = (match[1] ?? match[2] ?? match[3] ?? "").trim();
|
|
2231
|
+
if (handle && !seen.has(handle)) {
|
|
2232
|
+
seen.add(handle);
|
|
2233
|
+
mentions.push(handle);
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
return mentions;
|
|
2237
|
+
}
|
|
2238
|
+
function parseMentionSegments(content) {
|
|
2239
|
+
if (!content) return [];
|
|
2240
|
+
const segments = [];
|
|
2241
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2242
|
+
let lastIndex = 0;
|
|
2243
|
+
let match;
|
|
2244
|
+
while ((match = regex.exec(content)) !== null) {
|
|
2245
|
+
const fullMatch = match[0];
|
|
2246
|
+
const atIndex = fullMatch.indexOf("@");
|
|
2247
|
+
const prefix = atIndex > 0 ? fullMatch.slice(0, atIndex) : "";
|
|
2248
|
+
const mentionToken = fullMatch.slice(atIndex);
|
|
2249
|
+
const handle = match[1] ?? match[2] ?? match[3];
|
|
2250
|
+
const matchStart = match.index + prefix.length;
|
|
2251
|
+
if (matchStart > lastIndex) {
|
|
2252
|
+
segments.push({
|
|
2253
|
+
type: "text",
|
|
2254
|
+
value: content.slice(lastIndex, matchStart)
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
segments.push({
|
|
2258
|
+
type: "mention",
|
|
2259
|
+
value: mentionToken,
|
|
2260
|
+
handle
|
|
2261
|
+
});
|
|
2262
|
+
lastIndex = matchStart + mentionToken.length;
|
|
2263
|
+
}
|
|
2264
|
+
if (lastIndex < content.length) {
|
|
2265
|
+
segments.push({
|
|
2266
|
+
type: "text",
|
|
2267
|
+
value: content.slice(lastIndex)
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
return segments;
|
|
2271
|
+
}
|
|
2272
|
+
function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
|
|
2273
|
+
if (!content || typeof content !== "string") return "";
|
|
2274
|
+
const options = Array.isArray(optionsOrMentionables) ? { mentionables: optionsOrMentionables } : optionsOrMentionables;
|
|
2275
|
+
const allMentionables = options.mentionables || [];
|
|
2276
|
+
const defaultType = options.defaultType || "user";
|
|
2277
|
+
const resolveMention = options.resolveMention;
|
|
2278
|
+
const parts = content.split(MARKDOWN_MENTION_SPLIT_REGEX);
|
|
2279
|
+
return parts.map((part) => {
|
|
2280
|
+
if (!part) return "";
|
|
2281
|
+
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(
|
|
2282
|
+
part
|
|
2283
|
+
)) {
|
|
2284
|
+
return part;
|
|
2285
|
+
}
|
|
2286
|
+
const regex = new RegExp(MENTION_REGEX.source, "g");
|
|
2287
|
+
return part.replace(regex, (match, quotedHandle, escapedQuotedHandle, standardHandle) => {
|
|
2288
|
+
const atIndex = match.indexOf("@");
|
|
2289
|
+
const prefix = atIndex > 0 ? match.slice(0, atIndex) : "";
|
|
2290
|
+
const rawHandle = (quotedHandle || escapedQuotedHandle || standardHandle || "").trim();
|
|
2291
|
+
if (!rawHandle) return match;
|
|
2292
|
+
const lowerHandle = rawHandle.toLowerCase();
|
|
2293
|
+
const entity = allMentionables.find((e) => {
|
|
2294
|
+
if (e.label?.toLowerCase() === lowerHandle) return true;
|
|
2295
|
+
if (e.handle?.toLowerCase() === lowerHandle) return true;
|
|
2296
|
+
if (e.name?.toLowerCase() === lowerHandle) return true;
|
|
2297
|
+
if (e.id?.toLowerCase() === lowerHandle) return true;
|
|
2298
|
+
if (e.type === "agent" && `agent${(e.handle || e.label || "").toLowerCase()}` === lowerHandle) return true;
|
|
2299
|
+
if (e.aliases?.some(
|
|
2300
|
+
(a) => a.toLowerCase() === lowerHandle || e.type === "agent" && `agent${a.toLowerCase()}` === lowerHandle
|
|
2301
|
+
)) {
|
|
2302
|
+
return true;
|
|
2303
|
+
}
|
|
2304
|
+
return false;
|
|
2305
|
+
});
|
|
2306
|
+
let id = entity?.id;
|
|
2307
|
+
let label = entity?.label || entity?.handle || rawHandle;
|
|
2308
|
+
let type = entity?.type;
|
|
2309
|
+
let avatarUrl = entity?.avatarUrl;
|
|
2310
|
+
let color = entity?.color || entity?.hexColor;
|
|
2311
|
+
if (!entity) {
|
|
2312
|
+
const customResolved = resolveMention?.(rawHandle);
|
|
2313
|
+
if (customResolved) {
|
|
2314
|
+
id = customResolved.id ?? id;
|
|
2315
|
+
label = customResolved.label ?? customResolved.handle ?? label;
|
|
2316
|
+
type = customResolved.type ?? type;
|
|
2317
|
+
avatarUrl = customResolved.avatarUrl ?? avatarUrl;
|
|
2318
|
+
color = customResolved.color ?? customResolved.hexColor ?? color;
|
|
2319
|
+
} else {
|
|
2320
|
+
const stripped = lowerHandle.replace(/^agent[\s\-_]*/i, "");
|
|
2321
|
+
if (["supervisor", "coordinator", "planner", "validator", "executor"].includes(stripped)) {
|
|
2322
|
+
type = "agent";
|
|
2323
|
+
id = stripped;
|
|
2324
|
+
label = stripped;
|
|
2325
|
+
} else if (lowerHandle === "agents") {
|
|
2326
|
+
type = "team";
|
|
2327
|
+
id = "agents";
|
|
2328
|
+
label = "agents";
|
|
2329
|
+
} else {
|
|
2330
|
+
type = defaultType;
|
|
2331
|
+
id = rawHandle;
|
|
2332
|
+
label = rawHandle;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
const avatarAttr = avatarUrl ? ` data-avatar-url="${escapeHtml(avatarUrl)}"` : "";
|
|
2337
|
+
const colorAttr = color ? ` data-color="${escapeHtml(color)}"` : "";
|
|
2338
|
+
return `${prefix}<span class="pixerate-mention-node" data-type="mention" data-id="${escapeHtml(
|
|
2339
|
+
id || label
|
|
2340
|
+
)}" data-label="${escapeHtml(label)}" data-mention-suggestion-char="@" data-type-name="${escapeHtml(
|
|
2341
|
+
type || defaultType
|
|
2342
|
+
)}"${avatarAttr}${colorAttr}>@${escapeHtml(label)}</span>`;
|
|
2343
|
+
});
|
|
2344
|
+
}).join("");
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2203
2347
|
// src/serializers/plain-text.ts
|
|
2204
2348
|
function escapeHtml(text) {
|
|
2205
2349
|
if (!text) return "";
|
|
@@ -2324,6 +2468,16 @@ function extractMentionsFromDoc(docOrEditor) {
|
|
|
2324
2468
|
});
|
|
2325
2469
|
}
|
|
2326
2470
|
}
|
|
2471
|
+
if (mentions.length === 0) {
|
|
2472
|
+
const rawHandles = extractMentions(docOrEditor);
|
|
2473
|
+
for (const handle of rawHandles) {
|
|
2474
|
+
mentions.push({
|
|
2475
|
+
id: handle,
|
|
2476
|
+
label: handle,
|
|
2477
|
+
type: "user"
|
|
2478
|
+
});
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2327
2481
|
}
|
|
2328
2482
|
return mentions;
|
|
2329
2483
|
}
|
|
@@ -2343,9 +2497,31 @@ md2.validateLink = (url) => {
|
|
|
2343
2497
|
}
|
|
2344
2498
|
return defaultValidateLink.call(md2, url);
|
|
2345
2499
|
};
|
|
2346
|
-
function
|
|
2500
|
+
function preprocessMarkdownFileLinks(content) {
|
|
2501
|
+
if (!content) return "";
|
|
2502
|
+
return content.replace(
|
|
2503
|
+
/\[([^\]]+)\]\(((?:file|vscode|cursor):\/\/[^)]+)\)/g,
|
|
2504
|
+
(_match, rawLabel, href) => {
|
|
2505
|
+
const trimmed = rawLabel.trim();
|
|
2506
|
+
const codeMatch = trimmed.match(/^`([^`]+)`$/);
|
|
2507
|
+
const labelContent = codeMatch ? `<code>${codeMatch[1]}</code>` : trimmed;
|
|
2508
|
+
return `<a href="${href}" data-file-link="true" title="Click to copy file path">${labelContent}</a>`;
|
|
2509
|
+
}
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
function markdownToTipTapHtml(markdown, options) {
|
|
2347
2513
|
if (!markdown) return "<p></p>";
|
|
2348
|
-
|
|
2514
|
+
let content = markdown;
|
|
2515
|
+
if (options?.preprocessFileLinks) {
|
|
2516
|
+
content = preprocessMarkdownFileLinks(content);
|
|
2517
|
+
}
|
|
2518
|
+
if (options?.preprocessMentions || options?.mentionables || options?.preprocessOptions) {
|
|
2519
|
+
const mentionOpts = options.preprocessOptions || {
|
|
2520
|
+
mentionables: options.mentionables
|
|
2521
|
+
};
|
|
2522
|
+
content = preprocessMarkdownMentions(content, mentionOpts);
|
|
2523
|
+
}
|
|
2524
|
+
return md2.render(content);
|
|
2349
2525
|
}
|
|
2350
2526
|
function getEditorMarkdown(editor) {
|
|
2351
2527
|
if (!editor) return "";
|
|
@@ -4070,11 +4246,1023 @@ var HistoryManager = class {
|
|
|
4070
4246
|
function createHistoryManager(options = {}) {
|
|
4071
4247
|
return new HistoryManager(options);
|
|
4072
4248
|
}
|
|
4249
|
+
|
|
4250
|
+
// src/canvas/layout.ts
|
|
4251
|
+
import dagre from "@dagrejs/dagre";
|
|
4252
|
+
var DEFAULT_NODE_WIDTH = 200;
|
|
4253
|
+
var DEFAULT_NODE_HEIGHT = 60;
|
|
4254
|
+
var DEFAULT_PLACEMENT_GAP = 48;
|
|
4255
|
+
function getCenteredNodePosition(width = 0, height = 0, screenToFlowPosition, screenDimensions) {
|
|
4256
|
+
const rect = screenDimensions ?? (typeof document !== "undefined" ? { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight } : { width: 1920, height: 1080 });
|
|
4257
|
+
const flowPos = screenToFlowPosition({ x: rect.width / 2, y: rect.height / 2 });
|
|
4258
|
+
return {
|
|
4259
|
+
x: flowPos.x - width / 2,
|
|
4260
|
+
y: flowPos.y - height / 2
|
|
4261
|
+
};
|
|
4262
|
+
}
|
|
4263
|
+
function centerNodes(nodes, screenToFlowPosition, getNodesBounds, screenDimensions) {
|
|
4264
|
+
if (nodes.length === 0) {
|
|
4265
|
+
return [];
|
|
4266
|
+
}
|
|
4267
|
+
const bounds = getNodesBounds(nodes);
|
|
4268
|
+
const boundingBoxOrigin = { x: bounds.x, y: bounds.y };
|
|
4269
|
+
const centeredPosition = getCenteredNodePosition(
|
|
4270
|
+
bounds.width,
|
|
4271
|
+
bounds.height,
|
|
4272
|
+
screenToFlowPosition,
|
|
4273
|
+
screenDimensions
|
|
4274
|
+
);
|
|
4275
|
+
const deltaX = centeredPosition.x - boundingBoxOrigin.x;
|
|
4276
|
+
const deltaY = centeredPosition.y - boundingBoxOrigin.y;
|
|
4277
|
+
return nodes.map((node) => ({
|
|
4278
|
+
...node,
|
|
4279
|
+
position: {
|
|
4280
|
+
x: node.position.x + deltaX,
|
|
4281
|
+
y: node.position.y + deltaY
|
|
4282
|
+
}
|
|
4283
|
+
}));
|
|
4284
|
+
}
|
|
4285
|
+
function getLayoutedNodes(nodes, edges, getNodesBounds, options = {}) {
|
|
4286
|
+
if (nodes.length === 0) return [];
|
|
4287
|
+
const {
|
|
4288
|
+
direction = "LR",
|
|
4289
|
+
ranksep = 100,
|
|
4290
|
+
nodesep = 25,
|
|
4291
|
+
defaultNodeWidth = DEFAULT_NODE_WIDTH,
|
|
4292
|
+
defaultNodeHeight = DEFAULT_NODE_HEIGHT
|
|
4293
|
+
} = options;
|
|
4294
|
+
const dagreGraph = new dagre.graphlib.Graph();
|
|
4295
|
+
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
|
4296
|
+
dagreGraph.setGraph({ rankdir: direction, ranksep, nodesep });
|
|
4297
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
4298
|
+
nodes.forEach((node) => {
|
|
4299
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4300
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4301
|
+
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
|
4302
|
+
});
|
|
4303
|
+
edges.forEach((edge) => {
|
|
4304
|
+
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
4305
|
+
dagreGraph.setEdge(edge.source, edge.target);
|
|
4306
|
+
}
|
|
4307
|
+
});
|
|
4308
|
+
dagre.layout(dagreGraph);
|
|
4309
|
+
return nodes.map((node) => {
|
|
4310
|
+
const nodeWithPosition = dagreGraph.node(node.id);
|
|
4311
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4312
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4313
|
+
return {
|
|
4314
|
+
...node,
|
|
4315
|
+
position: {
|
|
4316
|
+
x: nodeWithPosition.x - nodeWidth / 2,
|
|
4317
|
+
y: nodeWithPosition.y - nodeHeight / 2
|
|
4318
|
+
}
|
|
4319
|
+
};
|
|
4320
|
+
});
|
|
4321
|
+
}
|
|
4322
|
+
function estimateNodeSize(node, defaultWidth = DEFAULT_NODE_WIDTH, defaultHeight = DEFAULT_NODE_HEIGHT) {
|
|
4323
|
+
const label = String(node.data?.label ?? node.data?.title ?? node.id ?? "");
|
|
4324
|
+
const lines = label.split(/\n|<br\s*\/?>/);
|
|
4325
|
+
const longest = Math.max(...lines.map((l) => l.replace(/<[^>]+>/g, "").length), 1);
|
|
4326
|
+
const width = Math.min(480, Math.max(defaultWidth, longest * 8 + 48));
|
|
4327
|
+
const height = Math.max(defaultHeight, lines.length * 20 + 24);
|
|
4328
|
+
return { width, height };
|
|
4329
|
+
}
|
|
4330
|
+
function boundsOf(rects) {
|
|
4331
|
+
if (!rects.length) return null;
|
|
4332
|
+
const minX = Math.min(...rects.map((r) => r.x));
|
|
4333
|
+
const minY = Math.min(...rects.map((r) => r.y));
|
|
4334
|
+
const maxX = Math.max(...rects.map((r) => r.x + r.width));
|
|
4335
|
+
const maxY = Math.max(...rects.map((r) => r.y + r.height));
|
|
4336
|
+
return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
|
|
4337
|
+
}
|
|
4338
|
+
function centre(r) {
|
|
4339
|
+
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
|
|
4340
|
+
}
|
|
4341
|
+
function overlaps(a, b, pad = 16) {
|
|
4342
|
+
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;
|
|
4343
|
+
}
|
|
4344
|
+
function flowSide(direction = "LR") {
|
|
4345
|
+
switch (direction) {
|
|
4346
|
+
case "LR":
|
|
4347
|
+
return "right";
|
|
4348
|
+
case "RL":
|
|
4349
|
+
return "left";
|
|
4350
|
+
case "BT":
|
|
4351
|
+
return "above";
|
|
4352
|
+
case "TB":
|
|
4353
|
+
default:
|
|
4354
|
+
return "below";
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
function ensureLayout(nodes, edges, options = {}) {
|
|
4358
|
+
if (nodes.length === 0) return [];
|
|
4359
|
+
const stored = options.storedPositions ?? {};
|
|
4360
|
+
const missing = nodes.filter((n) => !stored[n.id]);
|
|
4361
|
+
if (missing.length === 0) {
|
|
4362
|
+
return nodes.map((n) => ({
|
|
4363
|
+
...n,
|
|
4364
|
+
position: { ...stored[n.id] }
|
|
4365
|
+
}));
|
|
4366
|
+
}
|
|
4367
|
+
const autoLayouted = getLayoutedNodes(nodes, edges, void 0, options);
|
|
4368
|
+
const autoMap = new Map(
|
|
4369
|
+
autoLayouted.map((n) => [n.id, n.position])
|
|
4370
|
+
);
|
|
4371
|
+
const both = nodes.filter((n) => stored[n.id] && autoMap.has(n.id));
|
|
4372
|
+
if (both.length === 0) {
|
|
4373
|
+
return autoLayouted;
|
|
4374
|
+
}
|
|
4375
|
+
let dx = 0;
|
|
4376
|
+
let dy = 0;
|
|
4377
|
+
for (const n of both) {
|
|
4378
|
+
const s = stored[n.id];
|
|
4379
|
+
const a = autoMap.get(n.id);
|
|
4380
|
+
dx += s.x - a.x;
|
|
4381
|
+
dy += s.y - a.y;
|
|
4382
|
+
}
|
|
4383
|
+
dx /= both.length;
|
|
4384
|
+
dy /= both.length;
|
|
4385
|
+
return nodes.map((node) => {
|
|
4386
|
+
if (stored[node.id]) {
|
|
4387
|
+
return {
|
|
4388
|
+
...node,
|
|
4389
|
+
position: { ...stored[node.id] }
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
const autoPos = autoMap.get(node.id) ?? { x: 0, y: 0 };
|
|
4393
|
+
return {
|
|
4394
|
+
...node,
|
|
4395
|
+
position: {
|
|
4396
|
+
x: Math.round(autoPos.x + dx),
|
|
4397
|
+
y: Math.round(autoPos.y + dy)
|
|
4398
|
+
}
|
|
4399
|
+
};
|
|
4400
|
+
});
|
|
4401
|
+
}
|
|
4402
|
+
function placeBeside(rects, size, side, nearNodeId, options = {}) {
|
|
4403
|
+
const {
|
|
4404
|
+
gap = DEFAULT_PLACEMENT_GAP,
|
|
4405
|
+
ignore = [],
|
|
4406
|
+
maxAttempts = 32
|
|
4407
|
+
} = options;
|
|
4408
|
+
const skip = new Set([nearNodeId, ...ignore].filter(Boolean));
|
|
4409
|
+
const obstacles = Object.entries(rects).filter(([id]) => !skip.has(id)).map(([, r]) => r);
|
|
4410
|
+
const anchor = nearNodeId && rects[nearNodeId] || boundsOf(Object.values(rects)) || { x: 0, y: 0, width: 0, height: 0 };
|
|
4411
|
+
const anchorCenter = centre(anchor);
|
|
4412
|
+
let baseX;
|
|
4413
|
+
let baseY;
|
|
4414
|
+
switch (side) {
|
|
4415
|
+
case "right":
|
|
4416
|
+
baseX = anchor.x + anchor.width + gap;
|
|
4417
|
+
baseY = anchorCenter.y - size.height / 2;
|
|
4418
|
+
break;
|
|
4419
|
+
case "left":
|
|
4420
|
+
baseX = anchor.x - gap - size.width;
|
|
4421
|
+
baseY = anchorCenter.y - size.height / 2;
|
|
4422
|
+
break;
|
|
4423
|
+
case "above":
|
|
4424
|
+
baseX = anchorCenter.x - size.width / 2;
|
|
4425
|
+
baseY = anchor.y - gap - size.height;
|
|
4426
|
+
break;
|
|
4427
|
+
case "below":
|
|
4428
|
+
default:
|
|
4429
|
+
baseX = anchorCenter.x - size.width / 2;
|
|
4430
|
+
baseY = anchor.y + anchor.height + gap;
|
|
4431
|
+
break;
|
|
4432
|
+
}
|
|
4433
|
+
const isHorizontal = side === "right" || side === "left";
|
|
4434
|
+
const step = (isHorizontal ? size.height : size.width) + 20;
|
|
4435
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
4436
|
+
const offset = i === 0 ? 0 : Math.ceil(i / 2) * step * (i % 2 === 1 ? 1 : -1);
|
|
4437
|
+
const candidateBox = {
|
|
4438
|
+
x: isHorizontal ? baseX : baseX + offset,
|
|
4439
|
+
y: isHorizontal ? baseY + offset : baseY,
|
|
4440
|
+
width: size.width,
|
|
4441
|
+
height: size.height
|
|
4442
|
+
};
|
|
4443
|
+
if (!obstacles.some((obstacle) => overlaps(candidateBox, obstacle, 8))) {
|
|
4444
|
+
return {
|
|
4445
|
+
x: Math.round(candidateBox.x),
|
|
4446
|
+
y: Math.round(candidateBox.y)
|
|
4447
|
+
};
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
return {
|
|
4451
|
+
x: Math.round(baseX),
|
|
4452
|
+
y: Math.round(baseY)
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
// src/canvas/trajectory.ts
|
|
4457
|
+
var defaultEasing = (t) => --t * t * t + 1;
|
|
4458
|
+
var linearTrajectory = (origin, target, progress) => {
|
|
4459
|
+
return {
|
|
4460
|
+
position: {
|
|
4461
|
+
x: origin.x + (target.x - origin.x) * progress,
|
|
4462
|
+
y: origin.y + (target.y - origin.y) * progress
|
|
4463
|
+
},
|
|
4464
|
+
scale: progress,
|
|
4465
|
+
opacity: Math.min(1, progress * 1.5)
|
|
4466
|
+
};
|
|
4467
|
+
};
|
|
4468
|
+
var linearPositionTrajectory = (origin, target, progress) => {
|
|
4469
|
+
return {
|
|
4470
|
+
position: {
|
|
4471
|
+
x: origin.x + (target.x - origin.x) * progress,
|
|
4472
|
+
y: origin.y + (target.y - origin.y) * progress
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4475
|
+
};
|
|
4476
|
+
function createFanOutTrajectory(options = {}) {
|
|
4477
|
+
const {
|
|
4478
|
+
curvature = 0.4,
|
|
4479
|
+
startScale = 0,
|
|
4480
|
+
endScale = 1,
|
|
4481
|
+
startOpacity = 0,
|
|
4482
|
+
endOpacity = 1
|
|
4483
|
+
} = options;
|
|
4484
|
+
return (origin, target, progress, index, total) => {
|
|
4485
|
+
const dx = target.x - origin.x;
|
|
4486
|
+
const dy = target.y - origin.y;
|
|
4487
|
+
const normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;
|
|
4488
|
+
const midX = origin.x + dx * 0.4;
|
|
4489
|
+
const midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;
|
|
4490
|
+
const t = progress;
|
|
4491
|
+
const oneMinusT = 1 - t;
|
|
4492
|
+
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
|
|
4493
|
+
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
|
|
4494
|
+
const scale = startScale + (endScale - startScale) * progress;
|
|
4495
|
+
const opacity = startOpacity + (endOpacity - startOpacity) * progress;
|
|
4496
|
+
return {
|
|
4497
|
+
position: { x, y },
|
|
4498
|
+
scale,
|
|
4499
|
+
opacity
|
|
4500
|
+
};
|
|
4501
|
+
};
|
|
4502
|
+
}
|
|
4503
|
+
function createBezierTrajectory(getControlPoint) {
|
|
4504
|
+
return (origin, target, progress) => {
|
|
4505
|
+
const cp = getControlPoint(origin, target);
|
|
4506
|
+
const t = progress;
|
|
4507
|
+
const oneMinusT = 1 - t;
|
|
4508
|
+
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;
|
|
4509
|
+
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;
|
|
4510
|
+
return {
|
|
4511
|
+
position: { x, y },
|
|
4512
|
+
scale: progress,
|
|
4513
|
+
opacity: progress
|
|
4514
|
+
};
|
|
4515
|
+
};
|
|
4516
|
+
}
|
|
4517
|
+
function runMultiNodeTransition(transitions, options = {}) {
|
|
4518
|
+
if (transitions.length === 0) {
|
|
4519
|
+
options.onComplete?.();
|
|
4520
|
+
return () => {
|
|
4521
|
+
};
|
|
4522
|
+
}
|
|
4523
|
+
const { defaultDuration = 400, defaultEasing: easingFunc = defaultEasing, onUpdate, onComplete } = options;
|
|
4524
|
+
let isCancelled = false;
|
|
4525
|
+
const startTime = Date.now();
|
|
4526
|
+
const items = transitions.map((tr, i) => ({
|
|
4527
|
+
...tr,
|
|
4528
|
+
index: tr.index ?? i,
|
|
4529
|
+
total: tr.total ?? transitions.length,
|
|
4530
|
+
duration: tr.duration ?? defaultDuration,
|
|
4531
|
+
easing: tr.easing ?? easingFunc,
|
|
4532
|
+
delay: tr.delay ?? 0,
|
|
4533
|
+
trajectory: tr.trajectory ?? linearTrajectory
|
|
4534
|
+
}));
|
|
4535
|
+
function frame() {
|
|
4536
|
+
if (isCancelled) return;
|
|
4537
|
+
const now = Date.now();
|
|
4538
|
+
let allCompleted = true;
|
|
4539
|
+
for (let i = 0; i < items.length; i++) {
|
|
4540
|
+
const item = items[i];
|
|
4541
|
+
const elapsed = now - (startTime + item.delay);
|
|
4542
|
+
if (elapsed < 0) {
|
|
4543
|
+
allCompleted = false;
|
|
4544
|
+
continue;
|
|
4545
|
+
}
|
|
4546
|
+
const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
|
|
4547
|
+
const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
|
|
4548
|
+
const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
|
|
4549
|
+
item.node.position = { ...point.position };
|
|
4550
|
+
if (point.scale !== void 0 || point.opacity !== void 0) {
|
|
4551
|
+
if (!item.node.data) {
|
|
4552
|
+
item.node.data = {};
|
|
4553
|
+
}
|
|
4554
|
+
if (point.scale !== void 0) {
|
|
4555
|
+
item.node.data.scale = point.scale;
|
|
4556
|
+
}
|
|
4557
|
+
if (point.opacity !== void 0) {
|
|
4558
|
+
item.node.data.opacity = point.opacity;
|
|
4559
|
+
const currentStyle = (item.node.style || "").replace(/opacity:\s*[^;]+;?/g, "").trim();
|
|
4560
|
+
item.node.style = `${currentStyle ? currentStyle + "; " : ""}opacity: ${point.opacity};`;
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
if (rawProgress < 1) {
|
|
4564
|
+
allCompleted = false;
|
|
4565
|
+
}
|
|
4566
|
+
}
|
|
4567
|
+
onUpdate?.();
|
|
4568
|
+
if (!allCompleted) {
|
|
4569
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
4570
|
+
requestAnimationFrame(frame);
|
|
4571
|
+
}
|
|
4572
|
+
} else {
|
|
4573
|
+
onComplete?.();
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
frame();
|
|
4577
|
+
return () => {
|
|
4578
|
+
isCancelled = true;
|
|
4579
|
+
};
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
// src/canvas/displacement.ts
|
|
4583
|
+
import dagre2 from "@dagrejs/dagre";
|
|
4584
|
+
function computeNodesBoundingBox(nodes, defaultWidth = 200, defaultHeight = 60) {
|
|
4585
|
+
if (nodes.length === 0) {
|
|
4586
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
4587
|
+
}
|
|
4588
|
+
let minX = Infinity;
|
|
4589
|
+
let minY = Infinity;
|
|
4590
|
+
let maxX = -Infinity;
|
|
4591
|
+
let maxY = -Infinity;
|
|
4592
|
+
for (const node of nodes) {
|
|
4593
|
+
const width = node.measured?.width ?? node.width ?? defaultWidth;
|
|
4594
|
+
const height = node.measured?.height ?? node.height ?? defaultHeight;
|
|
4595
|
+
minX = Math.min(minX, node.position.x);
|
|
4596
|
+
minY = Math.min(minY, node.position.y);
|
|
4597
|
+
maxX = Math.max(maxX, node.position.x + width);
|
|
4598
|
+
maxY = Math.max(maxY, node.position.y + height);
|
|
4599
|
+
}
|
|
4600
|
+
return {
|
|
4601
|
+
x: minX,
|
|
4602
|
+
y: minY,
|
|
4603
|
+
width: maxX - minX,
|
|
4604
|
+
height: maxY - minY
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
function calculateDirectionalDisplacement(params) {
|
|
4608
|
+
const {
|
|
4609
|
+
existingNodes,
|
|
4610
|
+
originNode,
|
|
4611
|
+
childNodes: childNodes2,
|
|
4612
|
+
getNodesBounds,
|
|
4613
|
+
options = {}
|
|
4614
|
+
} = params;
|
|
4615
|
+
const {
|
|
4616
|
+
direction = "right",
|
|
4617
|
+
gap = 40,
|
|
4618
|
+
spreadSiblingLanes = false,
|
|
4619
|
+
siblingTolerance = 50,
|
|
4620
|
+
minY
|
|
4621
|
+
} = options;
|
|
4622
|
+
const displacedMap = /* @__PURE__ */ new Map();
|
|
4623
|
+
if (childNodes2.length === 0 || existingNodes.length === 0) {
|
|
4624
|
+
return displacedMap;
|
|
4625
|
+
}
|
|
4626
|
+
const childBounds = getNodesBounds ? getNodesBounds(childNodes2) : computeNodesBoundingBox(childNodes2);
|
|
4627
|
+
const originWidth = originNode.measured?.width ?? originNode.width ?? 200;
|
|
4628
|
+
const originHeight = originNode.measured?.height ?? originNode.height ?? 60;
|
|
4629
|
+
const deltaPrimary = direction === "right" || direction === "left" ? childBounds.width + gap : childBounds.height + gap;
|
|
4630
|
+
const deltaUp = Math.max(0, originNode.position.y - childBounds.y + gap);
|
|
4631
|
+
const deltaDown = Math.max(
|
|
4632
|
+
0,
|
|
4633
|
+
childBounds.y + childBounds.height - (originNode.position.y + originHeight) + gap
|
|
4634
|
+
);
|
|
4635
|
+
const equalDelta = Math.max(deltaUp, deltaDown);
|
|
4636
|
+
for (const node of existingNodes) {
|
|
4637
|
+
if (node.id === originNode.id) continue;
|
|
4638
|
+
const isSiblingRow = Math.abs(node.position.y - originNode.position.y) < siblingTolerance;
|
|
4639
|
+
switch (direction) {
|
|
4640
|
+
case "right": {
|
|
4641
|
+
if (node.position.x > originNode.position.x) {
|
|
4642
|
+
displacedMap.set(node.id, {
|
|
4643
|
+
x: node.position.x + deltaPrimary,
|
|
4644
|
+
y: node.position.y
|
|
4645
|
+
});
|
|
4646
|
+
}
|
|
4647
|
+
break;
|
|
4648
|
+
}
|
|
4649
|
+
case "left": {
|
|
4650
|
+
if (node.position.x < originNode.position.x) {
|
|
4651
|
+
displacedMap.set(node.id, {
|
|
4652
|
+
x: node.position.x - deltaPrimary,
|
|
4653
|
+
y: node.position.y
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4656
|
+
break;
|
|
4657
|
+
}
|
|
4658
|
+
case "down": {
|
|
4659
|
+
if (node.position.y > originNode.position.y) {
|
|
4660
|
+
displacedMap.set(node.id, {
|
|
4661
|
+
x: node.position.x,
|
|
4662
|
+
y: node.position.y + deltaPrimary
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
break;
|
|
4666
|
+
}
|
|
4667
|
+
case "up": {
|
|
4668
|
+
if (node.position.y < originNode.position.y) {
|
|
4669
|
+
displacedMap.set(node.id, {
|
|
4670
|
+
x: node.position.x,
|
|
4671
|
+
y: node.position.y - deltaPrimary
|
|
4672
|
+
});
|
|
4673
|
+
}
|
|
4674
|
+
break;
|
|
4675
|
+
}
|
|
4676
|
+
case "vertical": {
|
|
4677
|
+
if (isSiblingRow) break;
|
|
4678
|
+
if (node.position.y < originNode.position.y) {
|
|
4679
|
+
displacedMap.set(node.id, {
|
|
4680
|
+
x: node.position.x,
|
|
4681
|
+
y: node.position.y - equalDelta
|
|
4682
|
+
});
|
|
4683
|
+
} else if (node.position.y > originNode.position.y) {
|
|
4684
|
+
displacedMap.set(node.id, {
|
|
4685
|
+
x: node.position.x,
|
|
4686
|
+
y: node.position.y + equalDelta
|
|
4687
|
+
});
|
|
4688
|
+
}
|
|
4689
|
+
break;
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
if (spreadSiblingLanes && (direction === "right" || direction === "left")) {
|
|
4694
|
+
for (const [nodeId, pos] of displacedMap.entries()) {
|
|
4695
|
+
const node = existingNodes.find((n) => n.id === nodeId);
|
|
4696
|
+
if (node && Math.abs(node.position.y - originNode.position.y) < siblingTolerance) {
|
|
4697
|
+
const isAbove = node.position.y < originNode.position.y;
|
|
4698
|
+
displacedMap.set(nodeId, {
|
|
4699
|
+
x: pos.x,
|
|
4700
|
+
y: pos.y + (isAbove ? -deltaUp : deltaDown)
|
|
4701
|
+
});
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
if (minY !== void 0) {
|
|
4706
|
+
for (const [nodeId, pos] of displacedMap.entries()) {
|
|
4707
|
+
if (pos.y < minY) {
|
|
4708
|
+
displacedMap.set(nodeId, { x: pos.x, y: minY });
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
return displacedMap;
|
|
4713
|
+
}
|
|
4714
|
+
function calculateDagreReflowDisplacement(params) {
|
|
4715
|
+
const {
|
|
4716
|
+
currentNodes,
|
|
4717
|
+
futureNodes,
|
|
4718
|
+
futureEdges,
|
|
4719
|
+
options
|
|
4720
|
+
} = params;
|
|
4721
|
+
const {
|
|
4722
|
+
anchorNodeId,
|
|
4723
|
+
direction = "LR",
|
|
4724
|
+
ranksep = 100,
|
|
4725
|
+
nodesep = 25,
|
|
4726
|
+
defaultNodeWidth = 200,
|
|
4727
|
+
defaultNodeHeight = 60
|
|
4728
|
+
} = options;
|
|
4729
|
+
const anchorNode = currentNodes.find((n) => n.id === anchorNodeId);
|
|
4730
|
+
if (!anchorNode) return /* @__PURE__ */ new Map();
|
|
4731
|
+
const g = new dagre2.graphlib.Graph();
|
|
4732
|
+
g.setDefaultEdgeLabel(() => ({}));
|
|
4733
|
+
g.setGraph({ rankdir: direction, ranksep, nodesep });
|
|
4734
|
+
const nodeIds = new Set(futureNodes.map((n) => n.id));
|
|
4735
|
+
futureNodes.forEach((node) => {
|
|
4736
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4737
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4738
|
+
g.setNode(node.id, { width: nodeWidth, height: nodeHeight });
|
|
4739
|
+
});
|
|
4740
|
+
futureEdges.forEach((edge) => {
|
|
4741
|
+
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
4742
|
+
g.setEdge(edge.source, edge.target);
|
|
4743
|
+
}
|
|
4744
|
+
});
|
|
4745
|
+
dagre2.layout(g);
|
|
4746
|
+
const anchorLayoutPos = g.node(anchorNodeId);
|
|
4747
|
+
if (!anchorLayoutPos) return /* @__PURE__ */ new Map();
|
|
4748
|
+
const anchorWidth = anchorNode.measured?.width ?? anchorNode.width ?? defaultNodeWidth;
|
|
4749
|
+
const anchorHeight = anchorNode.measured?.height ?? anchorNode.height ?? defaultNodeHeight;
|
|
4750
|
+
const anchorLayoutUpperLeft = {
|
|
4751
|
+
x: anchorLayoutPos.x - anchorWidth / 2,
|
|
4752
|
+
y: anchorLayoutPos.y - anchorHeight / 2
|
|
4753
|
+
};
|
|
4754
|
+
const deltaX = anchorNode.position.x - anchorLayoutUpperLeft.x;
|
|
4755
|
+
const deltaY = anchorNode.position.y - anchorLayoutUpperLeft.y;
|
|
4756
|
+
const displacementMap = /* @__PURE__ */ new Map();
|
|
4757
|
+
currentNodes.forEach((node) => {
|
|
4758
|
+
if (node.id === anchorNodeId) return;
|
|
4759
|
+
const layoutNode = g.node(node.id);
|
|
4760
|
+
if (!layoutNode) return;
|
|
4761
|
+
const nodeWidth = node.measured?.width ?? node.width ?? defaultNodeWidth;
|
|
4762
|
+
const nodeHeight = node.measured?.height ?? node.height ?? defaultNodeHeight;
|
|
4763
|
+
const upperLeftX = layoutNode.x - nodeWidth / 2 + deltaX;
|
|
4764
|
+
const upperLeftY = layoutNode.y - nodeHeight / 2 + deltaY;
|
|
4765
|
+
if (Math.abs(upperLeftX - node.position.x) > 1 || Math.abs(upperLeftY - node.position.y) > 1) {
|
|
4766
|
+
displacementMap.set(node.id, { x: upperLeftX, y: upperLeftY });
|
|
4767
|
+
}
|
|
4768
|
+
});
|
|
4769
|
+
return displacementMap;
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
// src/canvas/clipboard.ts
|
|
4773
|
+
function isEventFromTextInput(event) {
|
|
4774
|
+
const target = event.target;
|
|
4775
|
+
if (!target) return false;
|
|
4776
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT" || target.isContentEditable || target.closest?.('[contenteditable="true"]') || target.closest?.(".ProseMirror")) {
|
|
4777
|
+
return true;
|
|
4778
|
+
}
|
|
4779
|
+
return false;
|
|
4780
|
+
}
|
|
4781
|
+
function isValidCanvasNode(obj) {
|
|
4782
|
+
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;
|
|
4783
|
+
}
|
|
4784
|
+
function isValidUrl(url) {
|
|
4785
|
+
try {
|
|
4786
|
+
const parsed = new URL(url);
|
|
4787
|
+
return parsed.protocol === "http:" || parsed.protocol === "https:";
|
|
4788
|
+
} catch {
|
|
4789
|
+
return false;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
function serializeCanvasNodes(nodes, edges) {
|
|
4793
|
+
const payload = {
|
|
4794
|
+
version: 1,
|
|
4795
|
+
nodes,
|
|
4796
|
+
edges
|
|
4797
|
+
};
|
|
4798
|
+
return JSON.stringify(payload);
|
|
4799
|
+
}
|
|
4800
|
+
function parseCanvasClipboardData(raw) {
|
|
4801
|
+
try {
|
|
4802
|
+
const parsed = JSON.parse(raw);
|
|
4803
|
+
if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.nodes) && parsed.nodes.length > 0 && parsed.nodes.every(isValidCanvasNode)) {
|
|
4804
|
+
return parsed;
|
|
4805
|
+
}
|
|
4806
|
+
} catch {
|
|
4807
|
+
}
|
|
4808
|
+
return null;
|
|
4809
|
+
}
|
|
4810
|
+
function remapPastedNodes(nodes, edges = [], offset = { x: 30, y: 30 }, idGenerator = () => `node-${Math.random().toString(36).substring(2, 9)}`) {
|
|
4811
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
4812
|
+
const newNodes = nodes.map((node) => {
|
|
4813
|
+
const newId = idGenerator();
|
|
4814
|
+
idMap.set(node.id, newId);
|
|
4815
|
+
return {
|
|
4816
|
+
...node,
|
|
4817
|
+
id: newId,
|
|
4818
|
+
position: {
|
|
4819
|
+
x: node.position.x + offset.x,
|
|
4820
|
+
y: node.position.y + offset.y
|
|
4821
|
+
},
|
|
4822
|
+
selected: true
|
|
4823
|
+
};
|
|
4824
|
+
});
|
|
4825
|
+
const newEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((edge) => ({
|
|
4826
|
+
...edge,
|
|
4827
|
+
id: `edge-${Math.random().toString(36).substring(2, 9)}`,
|
|
4828
|
+
source: idMap.get(edge.source),
|
|
4829
|
+
target: idMap.get(edge.target),
|
|
4830
|
+
selected: false
|
|
4831
|
+
}));
|
|
4832
|
+
return { nodes: newNodes, edges: newEdges };
|
|
4833
|
+
}
|
|
4834
|
+
function repositionNodesTo(nodes, targetOrigin) {
|
|
4835
|
+
if (nodes.length === 0) return [];
|
|
4836
|
+
const minX = Math.min(...nodes.map((n) => n.position.x));
|
|
4837
|
+
const minY = Math.min(...nodes.map((n) => n.position.y));
|
|
4838
|
+
const deltaX = targetOrigin.x - minX;
|
|
4839
|
+
const deltaY = targetOrigin.y - minY;
|
|
4840
|
+
return nodes.map((n) => ({
|
|
4841
|
+
...n,
|
|
4842
|
+
position: {
|
|
4843
|
+
x: n.position.x + deltaX,
|
|
4844
|
+
y: n.position.y + deltaY
|
|
4845
|
+
}
|
|
4846
|
+
}));
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
// src/canvas/streaming.ts
|
|
4850
|
+
var StreamingLayoutManager = class {
|
|
4851
|
+
throttleMs;
|
|
4852
|
+
glideDurationMs;
|
|
4853
|
+
layoutOptions;
|
|
4854
|
+
onLayoutUpdated;
|
|
4855
|
+
pendingTimer = null;
|
|
4856
|
+
activeTransitionCancel = null;
|
|
4857
|
+
targetPositions = /* @__PURE__ */ new Map();
|
|
4858
|
+
constructor(options = {}) {
|
|
4859
|
+
this.throttleMs = options.throttleMs ?? 200;
|
|
4860
|
+
this.glideDurationMs = options.glideDurationMs ?? 300;
|
|
4861
|
+
this.layoutOptions = options.layoutOptions ?? { direction: "LR" };
|
|
4862
|
+
this.onLayoutUpdated = options.onLayoutUpdated;
|
|
4863
|
+
}
|
|
4864
|
+
/**
|
|
4865
|
+
* Feed incoming nodes and edges into the streaming manager.
|
|
4866
|
+
* Coalesces rapid streaming bursts and triggers smooth layout gliding.
|
|
4867
|
+
*/
|
|
4868
|
+
pushStreamUpdate(nodes, edges, currentPositions) {
|
|
4869
|
+
if (this.pendingTimer) {
|
|
4870
|
+
clearTimeout(this.pendingTimer);
|
|
4871
|
+
}
|
|
4872
|
+
this.pendingTimer = setTimeout(() => {
|
|
4873
|
+
this.pendingTimer = null;
|
|
4874
|
+
this.recalculateAndGlide(nodes, edges, currentPositions);
|
|
4875
|
+
}, this.throttleMs);
|
|
4876
|
+
}
|
|
4877
|
+
/**
|
|
4878
|
+
* Forces immediate calculation and cancels pending throttled timers.
|
|
4879
|
+
*/
|
|
4880
|
+
flush(nodes, edges, currentPositions) {
|
|
4881
|
+
if (this.pendingTimer) {
|
|
4882
|
+
clearTimeout(this.pendingTimer);
|
|
4883
|
+
this.pendingTimer = null;
|
|
4884
|
+
}
|
|
4885
|
+
this.recalculateAndGlide(nodes, edges, currentPositions);
|
|
4886
|
+
}
|
|
4887
|
+
recalculateAndGlide(nodes, edges, currentPositions) {
|
|
4888
|
+
if (nodes.length === 0) return;
|
|
4889
|
+
if (this.activeTransitionCancel) {
|
|
4890
|
+
this.activeTransitionCancel();
|
|
4891
|
+
this.activeTransitionCancel = null;
|
|
4892
|
+
}
|
|
4893
|
+
const stored = {};
|
|
4894
|
+
for (const [id, pos] of currentPositions.entries()) {
|
|
4895
|
+
stored[id] = pos;
|
|
4896
|
+
}
|
|
4897
|
+
const layouted = ensureLayout(nodes, edges, {
|
|
4898
|
+
...this.layoutOptions,
|
|
4899
|
+
storedPositions: stored
|
|
4900
|
+
});
|
|
4901
|
+
const nextPositions = /* @__PURE__ */ new Map();
|
|
4902
|
+
const transitions = [];
|
|
4903
|
+
for (const node of layouted) {
|
|
4904
|
+
const target = node.position;
|
|
4905
|
+
nextPositions.set(node.id, target);
|
|
4906
|
+
const current = currentPositions.get(node.id) ?? target;
|
|
4907
|
+
const distance = Math.hypot(target.x - current.x, target.y - current.y);
|
|
4908
|
+
if (distance > 2) {
|
|
4909
|
+
transitions.push({
|
|
4910
|
+
node,
|
|
4911
|
+
from: current,
|
|
4912
|
+
to: target,
|
|
4913
|
+
trajectory: linearPositionTrajectory,
|
|
4914
|
+
duration: this.glideDurationMs
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
this.targetPositions = nextPositions;
|
|
4919
|
+
if (transitions.length > 0) {
|
|
4920
|
+
this.activeTransitionCancel = runMultiNodeTransition(transitions, {
|
|
4921
|
+
onUpdate: () => {
|
|
4922
|
+
const liveMap = new Map(currentPositions);
|
|
4923
|
+
for (const trans of transitions) {
|
|
4924
|
+
liveMap.set(trans.node.id, { ...trans.node.position });
|
|
4925
|
+
}
|
|
4926
|
+
this.onLayoutUpdated?.(liveMap);
|
|
4927
|
+
},
|
|
4928
|
+
onComplete: () => {
|
|
4929
|
+
this.activeTransitionCancel = null;
|
|
4930
|
+
this.onLayoutUpdated?.(nextPositions);
|
|
4931
|
+
}
|
|
4932
|
+
});
|
|
4933
|
+
} else {
|
|
4934
|
+
this.onLayoutUpdated?.(nextPositions);
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
destroy() {
|
|
4938
|
+
if (this.pendingTimer) {
|
|
4939
|
+
clearTimeout(this.pendingTimer);
|
|
4940
|
+
this.pendingTimer = null;
|
|
4941
|
+
}
|
|
4942
|
+
if (this.activeTransitionCancel) {
|
|
4943
|
+
this.activeTransitionCancel();
|
|
4944
|
+
this.activeTransitionCancel = null;
|
|
4945
|
+
}
|
|
4946
|
+
}
|
|
4947
|
+
};
|
|
4948
|
+
|
|
4949
|
+
// src/agent/planner.ts
|
|
4950
|
+
function planAgentCommand(command, snapshot, options = {}) {
|
|
4951
|
+
const defaultNodeSize = options.defaultNodeSize ?? { width: 200, height: 60 };
|
|
4952
|
+
const idGen = options.idGenerator ?? (() => `node-${Math.random().toString(36).substring(2, 9)}`);
|
|
4953
|
+
const edgeIdGen = () => `edge-${Math.random().toString(36).substring(2, 9)}`;
|
|
4954
|
+
const nodes = snapshot.canvas?.nodes ?? [];
|
|
4955
|
+
const edges = snapshot.canvas?.edges ?? [];
|
|
4956
|
+
switch (command.type) {
|
|
4957
|
+
case "canvas:add_node": {
|
|
4958
|
+
let pos = command.node.position;
|
|
4959
|
+
if (!pos) {
|
|
4960
|
+
if (command.nearNodeId) {
|
|
4961
|
+
const rects = {};
|
|
4962
|
+
for (const n of nodes) {
|
|
4963
|
+
rects[n.id] = {
|
|
4964
|
+
x: n.position.x,
|
|
4965
|
+
y: n.position.y,
|
|
4966
|
+
width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
|
|
4967
|
+
height: n.measured?.height ?? n.height ?? defaultNodeSize.height
|
|
4968
|
+
};
|
|
4969
|
+
}
|
|
4970
|
+
pos = placeBeside(
|
|
4971
|
+
rects,
|
|
4972
|
+
defaultNodeSize,
|
|
4973
|
+
command.side ?? "right",
|
|
4974
|
+
command.nearNodeId
|
|
4975
|
+
);
|
|
4976
|
+
} else if (nodes.length > 0) {
|
|
4977
|
+
const lastNode = nodes[nodes.length - 1];
|
|
4978
|
+
const rects = {};
|
|
4979
|
+
for (const n of nodes) {
|
|
4980
|
+
rects[n.id] = {
|
|
4981
|
+
x: n.position.x,
|
|
4982
|
+
y: n.position.y,
|
|
4983
|
+
width: n.measured?.width ?? n.width ?? defaultNodeSize.width,
|
|
4984
|
+
height: n.measured?.height ?? n.height ?? defaultNodeSize.height
|
|
4985
|
+
};
|
|
4986
|
+
}
|
|
4987
|
+
pos = placeBeside(rects, defaultNodeSize, "right", lastNode.id);
|
|
4988
|
+
} else {
|
|
4989
|
+
pos = { x: 100, y: 100 };
|
|
4990
|
+
}
|
|
4991
|
+
}
|
|
4992
|
+
const fullNode = {
|
|
4993
|
+
...command.node,
|
|
4994
|
+
id: command.node.id || idGen(),
|
|
4995
|
+
position: pos
|
|
4996
|
+
};
|
|
4997
|
+
const steps = [
|
|
4998
|
+
{
|
|
4999
|
+
type: "step:canvas_add_node",
|
|
5000
|
+
node: fullNode
|
|
5001
|
+
}
|
|
5002
|
+
];
|
|
5003
|
+
if (command.nearNodeId) {
|
|
5004
|
+
steps.push({
|
|
5005
|
+
type: "step:canvas_add_edge",
|
|
5006
|
+
edge: {
|
|
5007
|
+
id: edgeIdGen(),
|
|
5008
|
+
source: command.nearNodeId,
|
|
5009
|
+
target: fullNode.id
|
|
5010
|
+
}
|
|
5011
|
+
});
|
|
5012
|
+
}
|
|
5013
|
+
return steps;
|
|
5014
|
+
}
|
|
5015
|
+
case "canvas:remove_nodes": {
|
|
5016
|
+
return [
|
|
5017
|
+
{
|
|
5018
|
+
type: "step:canvas_remove_nodes",
|
|
5019
|
+
nodeIds: command.nodeIds
|
|
5020
|
+
}
|
|
5021
|
+
];
|
|
5022
|
+
}
|
|
5023
|
+
case "canvas:connect": {
|
|
5024
|
+
return [
|
|
5025
|
+
{
|
|
5026
|
+
type: "step:canvas_add_edge",
|
|
5027
|
+
edge: {
|
|
5028
|
+
id: edgeIdGen(),
|
|
5029
|
+
source: command.source,
|
|
5030
|
+
target: command.target,
|
|
5031
|
+
label: command.label
|
|
5032
|
+
}
|
|
5033
|
+
}
|
|
5034
|
+
];
|
|
5035
|
+
}
|
|
5036
|
+
case "canvas:update_node": {
|
|
5037
|
+
return [
|
|
5038
|
+
{
|
|
5039
|
+
type: "step:canvas_update_node",
|
|
5040
|
+
id: command.id,
|
|
5041
|
+
data: command.data
|
|
5042
|
+
}
|
|
5043
|
+
];
|
|
5044
|
+
}
|
|
5045
|
+
case "canvas:layout": {
|
|
5046
|
+
const dir = command.direction ?? "LR";
|
|
5047
|
+
let layouted;
|
|
5048
|
+
if (command.incremental) {
|
|
5049
|
+
const stored = {};
|
|
5050
|
+
for (const n of nodes) {
|
|
5051
|
+
if (n.position && (n.position.x !== 0 || n.position.y !== 0)) {
|
|
5052
|
+
stored[n.id] = { ...n.position };
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
layouted = ensureLayout(nodes, edges, {
|
|
5056
|
+
direction: dir,
|
|
5057
|
+
storedPositions: stored
|
|
5058
|
+
});
|
|
5059
|
+
} else {
|
|
5060
|
+
layouted = getLayoutedNodes(nodes, edges, void 0, { direction: dir });
|
|
5061
|
+
}
|
|
5062
|
+
const positions = {};
|
|
5063
|
+
for (const node of layouted) {
|
|
5064
|
+
positions[node.id] = { ...node.position };
|
|
5065
|
+
}
|
|
5066
|
+
return [
|
|
5067
|
+
{
|
|
5068
|
+
type: "step:canvas_move_nodes",
|
|
5069
|
+
positions
|
|
5070
|
+
}
|
|
5071
|
+
];
|
|
5072
|
+
}
|
|
5073
|
+
case "canvas:select": {
|
|
5074
|
+
return [
|
|
5075
|
+
{
|
|
5076
|
+
type: "step:canvas_select",
|
|
5077
|
+
nodeIds: command.nodeIds
|
|
5078
|
+
}
|
|
5079
|
+
];
|
|
5080
|
+
}
|
|
5081
|
+
case "canvas:highlight": {
|
|
5082
|
+
return [
|
|
5083
|
+
{
|
|
5084
|
+
type: "step:canvas_highlight",
|
|
5085
|
+
nodeIds: command.nodeIds,
|
|
5086
|
+
color: command.color ?? "#3b82f6",
|
|
5087
|
+
durationMs: command.durationMs ?? 2e3
|
|
5088
|
+
}
|
|
5089
|
+
];
|
|
5090
|
+
}
|
|
5091
|
+
case "prompt:insert_text": {
|
|
5092
|
+
return [
|
|
5093
|
+
{
|
|
5094
|
+
type: "step:prompt_insert",
|
|
5095
|
+
text: command.text,
|
|
5096
|
+
at: command.at
|
|
5097
|
+
}
|
|
5098
|
+
];
|
|
5099
|
+
}
|
|
5100
|
+
case "prompt:replace_range": {
|
|
5101
|
+
return [
|
|
5102
|
+
{
|
|
5103
|
+
type: "step:prompt_replace",
|
|
5104
|
+
from: command.from,
|
|
5105
|
+
to: command.to,
|
|
5106
|
+
text: command.text
|
|
5107
|
+
}
|
|
5108
|
+
];
|
|
5109
|
+
}
|
|
5110
|
+
case "prompt:set_variable": {
|
|
5111
|
+
return [
|
|
5112
|
+
{
|
|
5113
|
+
type: "step:prompt_set_variable",
|
|
5114
|
+
name: command.name,
|
|
5115
|
+
value: command.value
|
|
5116
|
+
}
|
|
5117
|
+
];
|
|
5118
|
+
}
|
|
5119
|
+
case "prompt:select": {
|
|
5120
|
+
return [
|
|
5121
|
+
{
|
|
5122
|
+
type: "step:prompt_select",
|
|
5123
|
+
from: command.from,
|
|
5124
|
+
to: command.to
|
|
5125
|
+
}
|
|
5126
|
+
];
|
|
5127
|
+
}
|
|
5128
|
+
default: {
|
|
5129
|
+
return [];
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
|
|
5134
|
+
// src/agent/tools.ts
|
|
5135
|
+
var MCP_TOOL_GET_EDITOR_STATE = {
|
|
5136
|
+
name: "get_editor_state",
|
|
5137
|
+
description: "Retrieves the current state of the document, variables, selection, and canvas graph (nodes and edges).",
|
|
5138
|
+
inputSchema: {
|
|
5139
|
+
type: "object",
|
|
5140
|
+
properties: {
|
|
5141
|
+
includeDocument: {
|
|
5142
|
+
type: "boolean",
|
|
5143
|
+
description: "Whether to include the prompt document text and variables (default: true)."
|
|
5144
|
+
},
|
|
5145
|
+
includeCanvas: {
|
|
5146
|
+
type: "boolean",
|
|
5147
|
+
description: "Whether to include the canvas nodes and edges (default: true)."
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
};
|
|
5152
|
+
var MCP_TOOL_EDIT_CANVAS = {
|
|
5153
|
+
name: "edit_canvas",
|
|
5154
|
+
description: "Applies high-level structural operations to the visual canvas (adding nodes, connecting edges, organizing layout, or moving elements).",
|
|
5155
|
+
inputSchema: {
|
|
5156
|
+
type: "object",
|
|
5157
|
+
properties: {
|
|
5158
|
+
operation: {
|
|
5159
|
+
type: "string",
|
|
5160
|
+
enum: [
|
|
5161
|
+
"add_node",
|
|
5162
|
+
"remove_nodes",
|
|
5163
|
+
"connect",
|
|
5164
|
+
"update_node",
|
|
5165
|
+
"layout",
|
|
5166
|
+
"select",
|
|
5167
|
+
"highlight"
|
|
5168
|
+
],
|
|
5169
|
+
description: "The canvas operation to execute."
|
|
5170
|
+
},
|
|
5171
|
+
node: {
|
|
5172
|
+
type: "object",
|
|
5173
|
+
description: "Node definition when operation is add_node. Can omit position to auto-place."
|
|
5174
|
+
},
|
|
5175
|
+
nearNodeId: {
|
|
5176
|
+
type: "string",
|
|
5177
|
+
description: "Anchor node ID to position beside using collision-aware placement."
|
|
5178
|
+
},
|
|
5179
|
+
side: {
|
|
5180
|
+
type: "string",
|
|
5181
|
+
enum: ["top", "right", "bottom", "left"],
|
|
5182
|
+
description: "Side of the anchor node to place the new node beside."
|
|
5183
|
+
},
|
|
5184
|
+
nodeIds: {
|
|
5185
|
+
type: "array",
|
|
5186
|
+
items: { type: "string" },
|
|
5187
|
+
description: "Array of node IDs for remove_nodes, select, or highlight."
|
|
5188
|
+
},
|
|
5189
|
+
source: {
|
|
5190
|
+
type: "string",
|
|
5191
|
+
description: "Source node ID for connect operation."
|
|
5192
|
+
},
|
|
5193
|
+
target: {
|
|
5194
|
+
type: "string",
|
|
5195
|
+
description: "Target node ID for connect operation."
|
|
5196
|
+
},
|
|
5197
|
+
label: {
|
|
5198
|
+
type: "string",
|
|
5199
|
+
description: "Optional edge label for connect operation."
|
|
5200
|
+
},
|
|
5201
|
+
direction: {
|
|
5202
|
+
type: "string",
|
|
5203
|
+
enum: ["LR", "TB", "RL", "BT"],
|
|
5204
|
+
description: "Layout direction (default LR)."
|
|
5205
|
+
},
|
|
5206
|
+
incremental: {
|
|
5207
|
+
type: "boolean",
|
|
5208
|
+
description: "Preserve existing node coordinates and blend layout (default true)."
|
|
5209
|
+
}
|
|
5210
|
+
},
|
|
5211
|
+
required: ["operation"]
|
|
5212
|
+
}
|
|
5213
|
+
};
|
|
5214
|
+
var MCP_TOOL_EDIT_PROMPT = {
|
|
5215
|
+
name: "edit_prompt",
|
|
5216
|
+
description: "Applies edits to the prompt template document, including inserting text, replacing ranges, and setting variables.",
|
|
5217
|
+
inputSchema: {
|
|
5218
|
+
type: "object",
|
|
5219
|
+
properties: {
|
|
5220
|
+
operation: {
|
|
5221
|
+
type: "string",
|
|
5222
|
+
enum: ["insert_text", "replace_range", "set_variable", "select"],
|
|
5223
|
+
description: "The prompt operation to execute."
|
|
5224
|
+
},
|
|
5225
|
+
text: {
|
|
5226
|
+
type: "string",
|
|
5227
|
+
description: "Text to insert or replace."
|
|
5228
|
+
},
|
|
5229
|
+
at: {
|
|
5230
|
+
type: "number",
|
|
5231
|
+
description: "Position to insert text at."
|
|
5232
|
+
},
|
|
5233
|
+
from: {
|
|
5234
|
+
type: "number",
|
|
5235
|
+
description: "Start offset for replace_range or select."
|
|
5236
|
+
},
|
|
5237
|
+
to: {
|
|
5238
|
+
type: "number",
|
|
5239
|
+
description: "End offset for replace_range or select."
|
|
5240
|
+
},
|
|
5241
|
+
name: {
|
|
5242
|
+
type: "string",
|
|
5243
|
+
description: "Variable name for set_variable."
|
|
5244
|
+
},
|
|
5245
|
+
value: {
|
|
5246
|
+
description: "Variable value for set_variable."
|
|
5247
|
+
}
|
|
5248
|
+
},
|
|
5249
|
+
required: ["operation"]
|
|
5250
|
+
}
|
|
5251
|
+
};
|
|
5252
|
+
var EDITOR_MCP_TOOLS = [
|
|
5253
|
+
MCP_TOOL_GET_EDITOR_STATE,
|
|
5254
|
+
MCP_TOOL_EDIT_CANVAS,
|
|
5255
|
+
MCP_TOOL_EDIT_PROMPT
|
|
5256
|
+
];
|
|
4073
5257
|
export {
|
|
4074
5258
|
ColorHighlighter,
|
|
5259
|
+
DEFAULT_NODE_HEIGHT,
|
|
5260
|
+
DEFAULT_NODE_WIDTH,
|
|
5261
|
+
DEFAULT_PLACEMENT_GAP,
|
|
4075
5262
|
DataSourceBinder,
|
|
4076
5263
|
DependencyGraph,
|
|
4077
5264
|
DirtyTracker,
|
|
5265
|
+
EDITOR_MCP_TOOLS,
|
|
4078
5266
|
EditorController,
|
|
4079
5267
|
FontSize,
|
|
4080
5268
|
FormulaEvaluator,
|
|
@@ -4084,48 +5272,86 @@ export {
|
|
|
4084
5272
|
INSTRUCTION_REGEX,
|
|
4085
5273
|
Image2 as Image,
|
|
4086
5274
|
LoadingNode,
|
|
5275
|
+
MARKDOWN_MENTION_SPLIT_REGEX,
|
|
5276
|
+
MCP_TOOL_EDIT_CANVAS,
|
|
5277
|
+
MCP_TOOL_EDIT_PROMPT,
|
|
5278
|
+
MCP_TOOL_GET_EDITOR_STATE,
|
|
5279
|
+
MENTION_REGEX,
|
|
4087
5280
|
Markdown2 as Markdown,
|
|
4088
5281
|
Mention,
|
|
4089
5282
|
SlashCommands,
|
|
4090
5283
|
SmilieReplacer,
|
|
4091
5284
|
SpreadsheetController,
|
|
5285
|
+
StreamingLayoutManager,
|
|
4092
5286
|
TEMPLATE_REGEX,
|
|
4093
5287
|
TemplateSuggestions,
|
|
4094
5288
|
VARIABLE_REGEX,
|
|
5289
|
+
boundsOf,
|
|
5290
|
+
calculateDagreReflowDisplacement,
|
|
5291
|
+
calculateDirectionalDisplacement,
|
|
5292
|
+
centerNodes,
|
|
5293
|
+
centre,
|
|
4095
5294
|
colNameToIndex,
|
|
5295
|
+
computeNodesBoundingBox,
|
|
5296
|
+
createBezierTrajectory,
|
|
4096
5297
|
createDirtyTracker,
|
|
4097
5298
|
createEditor,
|
|
5299
|
+
createFanOutTrajectory,
|
|
4098
5300
|
createHistoryManager,
|
|
4099
5301
|
createMentionExtension,
|
|
4100
5302
|
createRichTextPreset,
|
|
5303
|
+
defaultEasing,
|
|
4101
5304
|
defaultIsEqual,
|
|
5305
|
+
ensureLayout,
|
|
4102
5306
|
escapeHtml,
|
|
5307
|
+
estimateNodeSize,
|
|
4103
5308
|
extractCellDependencies,
|
|
4104
5309
|
extractInstructionNames,
|
|
5310
|
+
extractMentions,
|
|
4105
5311
|
extractMentionsFromDoc,
|
|
4106
5312
|
extractTemplateNames,
|
|
4107
5313
|
extractVariableNames,
|
|
4108
5314
|
findHexColors,
|
|
5315
|
+
flowSide,
|
|
5316
|
+
getCenteredNodePosition,
|
|
4109
5317
|
getEditorMarkdown,
|
|
4110
5318
|
getEditorText,
|
|
4111
5319
|
getLatestVersion,
|
|
5320
|
+
getLayoutedNodes,
|
|
4112
5321
|
handleImageInsertion,
|
|
4113
5322
|
htmlToPlainText,
|
|
4114
5323
|
imagePastePluginKey,
|
|
4115
5324
|
indexToColName,
|
|
4116
5325
|
insertImageNode,
|
|
4117
5326
|
instructionRegex,
|
|
5327
|
+
isEventFromTextInput,
|
|
5328
|
+
isValidCanvasNode,
|
|
5329
|
+
isValidUrl,
|
|
5330
|
+
linearPositionTrajectory,
|
|
5331
|
+
linearTrajectory,
|
|
4118
5332
|
mapRawOffsetToResolved,
|
|
4119
5333
|
mapResolvedOffsetToRaw,
|
|
4120
5334
|
markdownToTipTapHtml,
|
|
5335
|
+
overlaps,
|
|
5336
|
+
parseCanvasClipboardData,
|
|
4121
5337
|
parseCellReference,
|
|
4122
5338
|
parseFormula,
|
|
5339
|
+
parseMentionSegments,
|
|
4123
5340
|
parseRange,
|
|
5341
|
+
placeBeside,
|
|
4124
5342
|
plainTextToTipTapHtml,
|
|
5343
|
+
planAgentCommand,
|
|
5344
|
+
preprocessMarkdownFileLinks,
|
|
5345
|
+
preprocessMarkdownMentions,
|
|
5346
|
+
remapPastedNodes,
|
|
4125
5347
|
removeImageNode,
|
|
5348
|
+
repositionNodesTo,
|
|
4126
5349
|
resolveTemplates,
|
|
4127
5350
|
resolveTemplatesWithMapping,
|
|
4128
5351
|
resolveVariables,
|
|
5352
|
+
runMultiNodeTransition,
|
|
5353
|
+
serializeCanvasNodes,
|
|
5354
|
+
stripMarkdownCode,
|
|
4129
5355
|
templateRegex,
|
|
4130
5356
|
tokenizeFormula,
|
|
4131
5357
|
tokenizePrompt,
|