@pixerate/editor 0.8.1 → 0.9.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/index.cjs CHANGED
@@ -43,6 +43,8 @@ __export(src_exports, {
43
43
  INSTRUCTION_REGEX: () => INSTRUCTION_REGEX,
44
44
  Image: () => Image2,
45
45
  LoadingNode: () => LoadingNode,
46
+ MARKDOWN_MENTION_SPLIT_REGEX: () => MARKDOWN_MENTION_SPLIT_REGEX,
47
+ MENTION_REGEX: () => MENTION_REGEX,
46
48
  Markdown: () => Markdown2,
47
49
  Mention: () => Mention,
48
50
  SlashCommands: () => SlashCommands,
@@ -61,6 +63,7 @@ __export(src_exports, {
61
63
  escapeHtml: () => escapeHtml,
62
64
  extractCellDependencies: () => extractCellDependencies,
63
65
  extractInstructionNames: () => extractInstructionNames,
66
+ extractMentions: () => extractMentions,
64
67
  extractMentionsFromDoc: () => extractMentionsFromDoc,
65
68
  extractTemplateNames: () => extractTemplateNames,
66
69
  extractVariableNames: () => extractVariableNames,
@@ -79,12 +82,16 @@ __export(src_exports, {
79
82
  markdownToTipTapHtml: () => markdownToTipTapHtml,
80
83
  parseCellReference: () => parseCellReference,
81
84
  parseFormula: () => parseFormula,
85
+ parseMentionSegments: () => parseMentionSegments,
82
86
  parseRange: () => parseRange,
83
87
  plainTextToTipTapHtml: () => plainTextToTipTapHtml,
88
+ preprocessMarkdownFileLinks: () => preprocessMarkdownFileLinks,
89
+ preprocessMarkdownMentions: () => preprocessMarkdownMentions,
84
90
  removeImageNode: () => removeImageNode,
85
91
  resolveTemplates: () => resolveTemplates,
86
92
  resolveTemplatesWithMapping: () => resolveTemplatesWithMapping,
87
93
  resolveVariables: () => resolveVariables,
94
+ stripMarkdownCode: () => stripMarkdownCode,
88
95
  templateRegex: () => templateRegex,
89
96
  tokenizeFormula: () => tokenizeFormula,
90
97
  tokenizePrompt: () => tokenizePrompt,
@@ -2290,6 +2297,150 @@ function createRichTextPreset(options = {}) {
2290
2297
  return extensions2;
2291
2298
  }
2292
2299
 
2300
+ // src/serializers/mentions.ts
2301
+ var MENTION_REGEX = /(?:^|[\s>(*_"';\\/[\]{}~`:]|"|>|<|&)@(?:"([^"]+)"|"([^&]+)"|([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*))/g;
2302
+ 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;
2303
+ function stripMarkdownCode(content) {
2304
+ if (!content || typeof content !== "string") return "";
2305
+ 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, "");
2306
+ }
2307
+ function extractMentions(content) {
2308
+ if (!content || typeof content !== "string") return [];
2309
+ const normalizedMentionNodes = content.replace(
2310
+ /<span\b[^>]*\b(?:data-type=["']mention["']|class=["'][^"']*pixerate-mention-node[^"']*["'])[^>]*>([\s\S]*?)<\/span>/gi,
2311
+ (match2, inner) => {
2312
+ const labelMatch = match2.match(/\bdata-label=["']([^"']+)["']/i);
2313
+ if (labelMatch) return ` @${labelMatch[1]} `;
2314
+ const idMatch = match2.match(/\bdata-id=["']([^"']+)["']/i);
2315
+ if (idMatch) return ` @${idMatch[1]} `;
2316
+ return ` ${inner} `;
2317
+ }
2318
+ );
2319
+ const cleanContent = stripMarkdownCode(normalizedMentionNodes);
2320
+ const withoutUrls = cleanContent.replace(/(?:https?|ftp|file|vscode|cursor|mailto):\/\/[^\s<>()]+/gi, "").replace(/\]\([^)]*\)/g, "]");
2321
+ const textOnly = withoutUrls.replace(/<[^>]+>/g, " ");
2322
+ const mentions = [];
2323
+ const seen = /* @__PURE__ */ new Set();
2324
+ const regex = new RegExp(MENTION_REGEX.source, "g");
2325
+ let match;
2326
+ while ((match = regex.exec(textOnly)) !== null) {
2327
+ const handle = (match[1] ?? match[2] ?? match[3] ?? "").trim();
2328
+ if (handle && !seen.has(handle)) {
2329
+ seen.add(handle);
2330
+ mentions.push(handle);
2331
+ }
2332
+ }
2333
+ return mentions;
2334
+ }
2335
+ function parseMentionSegments(content) {
2336
+ if (!content) return [];
2337
+ const segments = [];
2338
+ const regex = new RegExp(MENTION_REGEX.source, "g");
2339
+ let lastIndex = 0;
2340
+ let match;
2341
+ while ((match = regex.exec(content)) !== null) {
2342
+ const fullMatch = match[0];
2343
+ const atIndex = fullMatch.indexOf("@");
2344
+ const prefix = atIndex > 0 ? fullMatch.slice(0, atIndex) : "";
2345
+ const mentionToken = fullMatch.slice(atIndex);
2346
+ const handle = match[1] ?? match[2] ?? match[3];
2347
+ const matchStart = match.index + prefix.length;
2348
+ if (matchStart > lastIndex) {
2349
+ segments.push({
2350
+ type: "text",
2351
+ value: content.slice(lastIndex, matchStart)
2352
+ });
2353
+ }
2354
+ segments.push({
2355
+ type: "mention",
2356
+ value: mentionToken,
2357
+ handle
2358
+ });
2359
+ lastIndex = matchStart + mentionToken.length;
2360
+ }
2361
+ if (lastIndex < content.length) {
2362
+ segments.push({
2363
+ type: "text",
2364
+ value: content.slice(lastIndex)
2365
+ });
2366
+ }
2367
+ return segments;
2368
+ }
2369
+ function preprocessMarkdownMentions(content, optionsOrMentionables = []) {
2370
+ if (!content || typeof content !== "string") return "";
2371
+ const options = Array.isArray(optionsOrMentionables) ? { mentionables: optionsOrMentionables } : optionsOrMentionables;
2372
+ const allMentionables = options.mentionables || [];
2373
+ const defaultType = options.defaultType || "user";
2374
+ const resolveMention = options.resolveMention;
2375
+ const parts = content.split(MARKDOWN_MENTION_SPLIT_REGEX);
2376
+ return parts.map((part) => {
2377
+ if (!part) return "";
2378
+ 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(
2379
+ part
2380
+ )) {
2381
+ return part;
2382
+ }
2383
+ const regex = new RegExp(MENTION_REGEX.source, "g");
2384
+ return part.replace(regex, (match, quotedHandle, escapedQuotedHandle, standardHandle) => {
2385
+ const atIndex = match.indexOf("@");
2386
+ const prefix = atIndex > 0 ? match.slice(0, atIndex) : "";
2387
+ const rawHandle = (quotedHandle || escapedQuotedHandle || standardHandle || "").trim();
2388
+ if (!rawHandle) return match;
2389
+ const lowerHandle = rawHandle.toLowerCase();
2390
+ const entity = allMentionables.find((e) => {
2391
+ if (e.label?.toLowerCase() === lowerHandle) return true;
2392
+ if (e.handle?.toLowerCase() === lowerHandle) return true;
2393
+ if (e.name?.toLowerCase() === lowerHandle) return true;
2394
+ if (e.id?.toLowerCase() === lowerHandle) return true;
2395
+ if (e.type === "agent" && `agent${(e.handle || e.label || "").toLowerCase()}` === lowerHandle) return true;
2396
+ if (e.aliases?.some(
2397
+ (a) => a.toLowerCase() === lowerHandle || e.type === "agent" && `agent${a.toLowerCase()}` === lowerHandle
2398
+ )) {
2399
+ return true;
2400
+ }
2401
+ return false;
2402
+ });
2403
+ let id = entity?.id;
2404
+ let label = entity?.label || entity?.handle || rawHandle;
2405
+ let type = entity?.type;
2406
+ let avatarUrl = entity?.avatarUrl;
2407
+ let color = entity?.color || entity?.hexColor;
2408
+ if (!entity) {
2409
+ const customResolved = resolveMention?.(rawHandle);
2410
+ if (customResolved) {
2411
+ id = customResolved.id ?? id;
2412
+ label = customResolved.label ?? customResolved.handle ?? label;
2413
+ type = customResolved.type ?? type;
2414
+ avatarUrl = customResolved.avatarUrl ?? avatarUrl;
2415
+ color = customResolved.color ?? customResolved.hexColor ?? color;
2416
+ } else {
2417
+ const stripped = lowerHandle.replace(/^agent[\s\-_]*/i, "");
2418
+ if (["supervisor", "coordinator", "planner", "validator", "executor"].includes(stripped)) {
2419
+ type = "agent";
2420
+ id = stripped;
2421
+ label = stripped;
2422
+ } else if (lowerHandle === "agents") {
2423
+ type = "team";
2424
+ id = "agents";
2425
+ label = "agents";
2426
+ } else {
2427
+ type = defaultType;
2428
+ id = rawHandle;
2429
+ label = rawHandle;
2430
+ }
2431
+ }
2432
+ }
2433
+ const avatarAttr = avatarUrl ? ` data-avatar-url="${escapeHtml(avatarUrl)}"` : "";
2434
+ const colorAttr = color ? ` data-color="${escapeHtml(color)}"` : "";
2435
+ return `${prefix}<span class="pixerate-mention-node" data-type="mention" data-id="${escapeHtml(
2436
+ id || label
2437
+ )}" data-label="${escapeHtml(label)}" data-mention-suggestion-char="@" data-type-name="${escapeHtml(
2438
+ type || defaultType
2439
+ )}"${avatarAttr}${colorAttr}>@${escapeHtml(label)}</span>`;
2440
+ });
2441
+ }).join("");
2442
+ }
2443
+
2293
2444
  // src/serializers/plain-text.ts
2294
2445
  function escapeHtml(text) {
2295
2446
  if (!text) return "";
@@ -2414,6 +2565,16 @@ function extractMentionsFromDoc(docOrEditor) {
2414
2565
  });
2415
2566
  }
2416
2567
  }
2568
+ if (mentions.length === 0) {
2569
+ const rawHandles = extractMentions(docOrEditor);
2570
+ for (const handle of rawHandles) {
2571
+ mentions.push({
2572
+ id: handle,
2573
+ label: handle,
2574
+ type: "user"
2575
+ });
2576
+ }
2577
+ }
2417
2578
  }
2418
2579
  return mentions;
2419
2580
  }
@@ -2433,9 +2594,31 @@ md2.validateLink = (url) => {
2433
2594
  }
2434
2595
  return defaultValidateLink.call(md2, url);
2435
2596
  };
2436
- function markdownToTipTapHtml(markdown) {
2597
+ function preprocessMarkdownFileLinks(content) {
2598
+ if (!content) return "";
2599
+ return content.replace(
2600
+ /\[([^\]]+)\]\(((?:file|vscode|cursor):\/\/[^)]+)\)/g,
2601
+ (_match, rawLabel, href) => {
2602
+ const trimmed = rawLabel.trim();
2603
+ const codeMatch = trimmed.match(/^`([^`]+)`$/);
2604
+ const labelContent = codeMatch ? `<code>${codeMatch[1]}</code>` : trimmed;
2605
+ return `<a href="${href}" data-file-link="true" title="Click to copy file path">${labelContent}</a>`;
2606
+ }
2607
+ );
2608
+ }
2609
+ function markdownToTipTapHtml(markdown, options) {
2437
2610
  if (!markdown) return "<p></p>";
2438
- return md2.render(markdown);
2611
+ let content = markdown;
2612
+ if (options?.preprocessFileLinks) {
2613
+ content = preprocessMarkdownFileLinks(content);
2614
+ }
2615
+ if (options?.preprocessMentions || options?.mentionables || options?.preprocessOptions) {
2616
+ const mentionOpts = options.preprocessOptions || {
2617
+ mentionables: options.mentionables
2618
+ };
2619
+ content = preprocessMarkdownMentions(content, mentionOpts);
2620
+ }
2621
+ return md2.render(content);
2439
2622
  }
2440
2623
  function getEditorMarkdown(editor) {
2441
2624
  if (!editor) return "";
@@ -4175,6 +4358,8 @@ function createHistoryManager(options = {}) {
4175
4358
  INSTRUCTION_REGEX,
4176
4359
  Image,
4177
4360
  LoadingNode,
4361
+ MARKDOWN_MENTION_SPLIT_REGEX,
4362
+ MENTION_REGEX,
4178
4363
  Markdown,
4179
4364
  Mention,
4180
4365
  SlashCommands,
@@ -4193,6 +4378,7 @@ function createHistoryManager(options = {}) {
4193
4378
  escapeHtml,
4194
4379
  extractCellDependencies,
4195
4380
  extractInstructionNames,
4381
+ extractMentions,
4196
4382
  extractMentionsFromDoc,
4197
4383
  extractTemplateNames,
4198
4384
  extractVariableNames,
@@ -4211,12 +4397,16 @@ function createHistoryManager(options = {}) {
4211
4397
  markdownToTipTapHtml,
4212
4398
  parseCellReference,
4213
4399
  parseFormula,
4400
+ parseMentionSegments,
4214
4401
  parseRange,
4215
4402
  plainTextToTipTapHtml,
4403
+ preprocessMarkdownFileLinks,
4404
+ preprocessMarkdownMentions,
4216
4405
  removeImageNode,
4217
4406
  resolveTemplates,
4218
4407
  resolveTemplatesWithMapping,
4219
4408
  resolveVariables,
4409
+ stripMarkdownCode,
4220
4410
  templateRegex,
4221
4411
  tokenizeFormula,
4222
4412
  tokenizePrompt,