@tensor_1/react 2.39.0 → 2.40.1
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/README.md +18 -0
- package/dist/chart-analytic-model.d.ts +10 -2
- package/dist/chart-analytic-model.js +24 -17
- package/dist/chart-analytic.cjs +25 -18
- package/dist/chart-analytic.d.cts +10 -2
- package/dist/chart-panels-model.js +1 -1
- package/dist/components/Comments.d.ts +4 -1
- package/dist/components/Comments.js +3 -2
- package/dist/components/Markdown.d.ts +9 -2
- package/dist/components/Markdown.js +16 -2
- package/dist/components/RichTextEditor.d.ts +17 -0
- package/dist/components/RichTextEditor.js +179 -11
- package/dist/components/ToggleRow.d.ts +8 -1
- package/dist/components/ToggleRow.js +17 -3
- package/dist/icons/Icon.d.ts +12 -12
- package/dist/index.cjs +490 -475
- package/dist/index.d.cts +12 -2
- package/dist/markdown-model-Cuwi6pIk.d.cts +110 -0
- package/dist/markdown-model.d.ts +38 -1
- package/dist/markdown-model.js +67 -0
- package/dist/markdown.cjs +90 -4
- package/dist/markdown.d.cts +11 -75
- package/dist/markdown.d.ts +1 -1
- package/dist/markdown.js +16 -2
- package/dist/rich-text/mention.d.ts +14 -0
- package/dist/rich-text/mention.js +57 -0
- package/dist/rich-text/pipeline.js +4 -1
- package/dist/rich-text.cjs +1795 -1483
- package/dist/rich-text.d.cts +27 -1
- package/dist/rich-text.d.ts +3 -0
- package/dist/rich-text.js +6 -0
- package/package.json +1 -1
package/dist/rich-text.cjs
CHANGED
|
@@ -31,16 +31,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var rich_text_exports = {};
|
|
32
32
|
__export(rich_text_exports, {
|
|
33
33
|
DEFAULT_RICH_TEXT_LABELS: () => DEFAULT_RICH_TEXT_LABELS,
|
|
34
|
+
MENTION_SCHEME: () => MENTION_SCHEME,
|
|
34
35
|
RichTextEditor: () => RichTextEditor,
|
|
35
36
|
RichTextEditorFallback: () => RichTextEditorFallback,
|
|
36
37
|
emojiImageFileName: () => emojiImageFileName,
|
|
38
|
+
findMentions: () => findMentions,
|
|
39
|
+
formatMention: () => formatMention,
|
|
40
|
+
isMentionId: () => isMentionId,
|
|
41
|
+
readMention: () => readMention,
|
|
37
42
|
roundTripMarkdown: () => roundTripMarkdown
|
|
38
43
|
});
|
|
39
44
|
module.exports = __toCommonJS(rich_text_exports);
|
|
40
45
|
|
|
41
46
|
// src/components/RichTextEditor.tsx
|
|
42
47
|
var import_react7 = require("react");
|
|
43
|
-
var
|
|
48
|
+
var import_core3 = require("@tiptap/core");
|
|
44
49
|
var import_react8 = require("@tiptap/react");
|
|
45
50
|
var import_markdown2 = require("@tiptap/markdown");
|
|
46
51
|
var import_extensions = require("@tiptap/extensions");
|
|
@@ -328,7 +333,7 @@ function useEmojiRenderer(emojiImageBaseUrl) {
|
|
|
328
333
|
}
|
|
329
334
|
|
|
330
335
|
// src/rich-text/pipeline.ts
|
|
331
|
-
var
|
|
336
|
+
var import_core2 = require("@tiptap/core");
|
|
332
337
|
var import_markdown = require("@tiptap/markdown");
|
|
333
338
|
var import_extension_list = require("@tiptap/extension-list");
|
|
334
339
|
var import_extension_table = require("@tiptap/extension-table");
|
|
@@ -346,1578 +351,1699 @@ function defaultIsSafeUrl(url) {
|
|
|
346
351
|
return ALLOWED_START.test(candidate);
|
|
347
352
|
}
|
|
348
353
|
|
|
349
|
-
// src/rich-text/
|
|
350
|
-
var
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if (first?.type !== "paragraph" || !(first.content ?? []).some((c) => c.type === "hardBreak")) {
|
|
371
|
-
return (0, import_core.renderNestedMarkdownContent)(node, h, prefix);
|
|
372
|
-
}
|
|
373
|
-
const lines = [[]];
|
|
374
|
-
for (const child of first.content ?? []) {
|
|
375
|
-
if (child.type === "hardBreak") lines.push([]);
|
|
376
|
-
else lines[lines.length - 1].push(child);
|
|
377
|
-
}
|
|
378
|
-
const paragraphs = lines.filter((line) => line.length > 0).map((content) => ({ type: "paragraph", content }));
|
|
379
|
-
return (0, import_core.renderNestedMarkdownContent)({ ...node, content: [...paragraphs, ...rest] }, h, prefix);
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
function createSchemaExtensions(options = {}) {
|
|
383
|
-
const isSafeUrl = options.isSafeUrl ?? defaultIsSafeUrl;
|
|
384
|
-
return [
|
|
385
|
-
import_starter_kit.StarterKit.configure({
|
|
386
|
-
underline: false,
|
|
387
|
-
listItem: false,
|
|
388
|
-
heading: { levels: [...options.headingLevels ?? [1, 2, 3]] },
|
|
389
|
-
link: {
|
|
390
|
-
openOnClick: false,
|
|
391
|
-
autolink: true,
|
|
392
|
-
linkOnPaste: true,
|
|
393
|
-
defaultProtocol: "https",
|
|
394
|
-
// Unsafe links never become marks: pasted or typed, they stay text.
|
|
395
|
-
isAllowedUri: (url) => isSafeUrl(url),
|
|
396
|
-
shouldAutoLink: (url) => isSafeUrl(url),
|
|
397
|
-
HTMLAttributes: { rel: "noopener noreferrer nofollow", target: null }
|
|
398
|
-
}
|
|
399
|
-
}),
|
|
400
|
-
UixListItem,
|
|
401
|
-
import_extension_list.TaskList,
|
|
402
|
-
UixTaskItem.configure({ nested: true }),
|
|
403
|
-
import_extension_table.TableKit.configure({ table: { resizable: false } }),
|
|
404
|
-
options.image ?? import_extension_image.Image.configure({ inline: true, allowBase64: false })
|
|
405
|
-
];
|
|
354
|
+
// src/rich-text/mention.ts
|
|
355
|
+
var import_core = require("@tiptap/core");
|
|
356
|
+
|
|
357
|
+
// src/markdown-model.ts
|
|
358
|
+
var MENTION_SCHEME = "mention:";
|
|
359
|
+
var MENTION_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
|
360
|
+
var MENTION_RE = /^@\[((?:\\[\\[\]]|[^\\[\]\n]){1,200})\]\(mention:([A-Za-z0-9][A-Za-z0-9_.-]{0,127})\)/;
|
|
361
|
+
var cleanMentionLabel = (label) => label.replace(/\s+/gu, " ").trim().slice(0, 200);
|
|
362
|
+
function isMentionId(id) {
|
|
363
|
+
return MENTION_ID.test(id);
|
|
364
|
+
}
|
|
365
|
+
function formatMention(mention) {
|
|
366
|
+
const label = cleanMentionLabel(mention.label);
|
|
367
|
+
if (!isMentionId(mention.id) || !label) return null;
|
|
368
|
+
return `@[${label.replace(/[\\[\]]/g, (ch) => `\\${ch}`)}](${MENTION_SCHEME}${mention.id})`;
|
|
369
|
+
}
|
|
370
|
+
function readMention(text) {
|
|
371
|
+
const match = MENTION_RE.exec(text);
|
|
372
|
+
if (!match) return null;
|
|
373
|
+
const label = cleanMentionLabel(match[1].replace(/\\([\\[\]])/g, "$1"));
|
|
374
|
+
return label ? { id: match[2], label, length: match[0].length } : null;
|
|
406
375
|
}
|
|
407
|
-
var ENTITY_AFTER_AMP = /^(?:#\d+;|#x[\da-f]+;|[a-z][a-z\d]*;)/i;
|
|
408
|
-
var TAG_START = /^[a-z/!?]$/i;
|
|
409
376
|
var ASCII_PUNCT = /[!-/:-@[-`{-~]/;
|
|
410
|
-
var
|
|
411
|
-
var
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
377
|
+
var WORD = /[\p{L}\p{N}]/u;
|
|
378
|
+
var NAMED_ENTITIES = {
|
|
379
|
+
amp: "&",
|
|
380
|
+
lt: "<",
|
|
381
|
+
gt: ">",
|
|
382
|
+
quot: '"',
|
|
383
|
+
apos: "'",
|
|
384
|
+
nbsp: "\xA0",
|
|
385
|
+
copy: "\xA9",
|
|
386
|
+
reg: "\xAE",
|
|
387
|
+
hellip: "\u2026",
|
|
388
|
+
mdash: "\u2014",
|
|
389
|
+
ndash: "\u2013"
|
|
390
|
+
};
|
|
391
|
+
var ENTITY_RE = /^&(?:#(\d{1,7})|#[xX]([\da-fA-F]{1,6})|([a-zA-Z][a-zA-Z\d]{1,31}));/;
|
|
392
|
+
var ENTITY_GLOBAL = /&(?:#\d{1,7}|#[xX][\da-fA-F]{1,6}|[a-zA-Z][a-zA-Z\d]{1,31});/g;
|
|
393
|
+
var BARE_URL_RE = /^https?:\/\/[^\s<>()]+/;
|
|
394
|
+
var BR_RE = /^<br\s*\/?>/i;
|
|
395
|
+
var AUTOLINK_RE = /^<((?:https?:|mailto:)[^\s<>]*)>/i;
|
|
396
|
+
var decodeEntity = (match) => {
|
|
397
|
+
const [, dec, hex, name] = match;
|
|
398
|
+
const code = dec ? Number(dec) : hex ? parseInt(hex, 16) : void 0;
|
|
399
|
+
if (code !== void 0) return code > 0 && code <= 1114111 ? String.fromCodePoint(code) : "\uFFFD";
|
|
400
|
+
return name && Object.prototype.hasOwnProperty.call(NAMED_ENTITIES, name) ? NAMED_ENTITIES[name] : null;
|
|
401
|
+
};
|
|
402
|
+
var MAX_SCAN = 2e3;
|
|
403
|
+
var MAX_DEPTH = 12;
|
|
404
|
+
var findBracketEnd = (text, open) => {
|
|
405
|
+
let depth = 0;
|
|
406
|
+
const stop = Math.min(text.length, open + MAX_SCAN);
|
|
407
|
+
for (let i = open; i < stop; i += 1) {
|
|
408
|
+
const ch = text[i];
|
|
409
|
+
if (ch === "\\") {
|
|
410
|
+
i += 1;
|
|
425
411
|
continue;
|
|
426
412
|
}
|
|
427
|
-
if (
|
|
428
|
-
|
|
413
|
+
if (ch === "`") {
|
|
414
|
+
const close = text.indexOf("`", i + 1);
|
|
415
|
+
if (close > 0) {
|
|
416
|
+
i = close;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
429
419
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const markerBefore = !atStart && LIST_MARKER.test(out.slice(out.lastIndexOf("\n") + 1));
|
|
435
|
-
if (
|
|
436
|
-
// `#` always: marked ends a list item at a line starting with `#`, space or not.
|
|
437
|
-
atStart && (ch === "#" || /[-+*]/.test(ch) && endOrSpace) || atStart && /[-=*_]/.test(ch) && new RegExp(`^(?:\\${ch}[ \\t]*)+$`).test(lineRest) || markerBefore && /[.)]/.test(ch) && endOrSpace
|
|
438
|
-
) {
|
|
439
|
-
out += BACKSLASH + ch;
|
|
440
|
-
continue;
|
|
420
|
+
if (ch === "[") depth += 1;
|
|
421
|
+
else if (ch === "]") {
|
|
422
|
+
depth -= 1;
|
|
423
|
+
if (depth === 0) return i;
|
|
441
424
|
}
|
|
442
|
-
|
|
443
|
-
|
|
425
|
+
}
|
|
426
|
+
return -1;
|
|
427
|
+
};
|
|
428
|
+
var readDestination = (text, open) => {
|
|
429
|
+
if (text[open] !== "(") return null;
|
|
430
|
+
let depth = 0;
|
|
431
|
+
let end = -1;
|
|
432
|
+
const stop = Math.min(text.length, open + MAX_SCAN);
|
|
433
|
+
for (let i = open; i < stop; i += 1) {
|
|
434
|
+
const ch = text[i];
|
|
435
|
+
if (ch === "\\") {
|
|
436
|
+
i += 1;
|
|
444
437
|
continue;
|
|
445
438
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
case "[":
|
|
452
|
-
case "]":
|
|
453
|
-
case "~":
|
|
454
|
-
out += BACKSLASH + ch;
|
|
455
|
-
break;
|
|
456
|
-
case "|":
|
|
457
|
-
out += options.inTable ? BACKSLASH + ch : ch;
|
|
458
|
-
break;
|
|
459
|
-
case "&":
|
|
460
|
-
out += ENTITY_AFTER_AMP.test(chars.slice(i + 1, i + 40).join("")) ? "&" : ch;
|
|
461
|
-
break;
|
|
462
|
-
case "<":
|
|
463
|
-
out += TAG_START.test(next) ? "<" : ch;
|
|
464
|
-
break;
|
|
465
|
-
case "!":
|
|
466
|
-
out += next === "" && options.beforeLink ? BACKSLASH + ch : ch;
|
|
467
|
-
break;
|
|
468
|
-
case "*":
|
|
469
|
-
case "_": {
|
|
470
|
-
const spaced = (prev === "" || /\s/u.test(prev)) && (next === "" || /\s/u.test(next));
|
|
471
|
-
const intraword = ch === "_" && /[\p{L}\p{N}]/u.test(prev) && /[\p{L}\p{N}]/u.test(next);
|
|
472
|
-
out += spaced || intraword ? ch : BACKSLASH + ch;
|
|
439
|
+
if (ch === "(") depth += 1;
|
|
440
|
+
else if (ch === ")") {
|
|
441
|
+
depth -= 1;
|
|
442
|
+
if (depth === 0) {
|
|
443
|
+
end = i;
|
|
473
444
|
break;
|
|
474
445
|
}
|
|
475
|
-
case ">":
|
|
476
|
-
out += atStart ? BACKSLASH + ch : ch;
|
|
477
|
-
break;
|
|
478
|
-
default:
|
|
479
|
-
out += ch;
|
|
480
446
|
}
|
|
481
447
|
}
|
|
482
|
-
return
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
448
|
+
if (end < 0) return null;
|
|
449
|
+
const inner = text.slice(open + 1, end).trim();
|
|
450
|
+
const titled = /^(\S+)\s+(?:"([^"]*)"|'([^']*)')$/.exec(inner);
|
|
451
|
+
const rawUrl = titled ? titled[1] : inner;
|
|
452
|
+
const url = rawUrl.replace(/^<(.*)>$/, "$1").replace(/\\([!-/:-@[-`{-~])/g, "$1").replace(ENTITY_GLOBAL, (match) => decodeEntity(ENTITY_RE.exec(match)) ?? match);
|
|
453
|
+
const title = titled ? titled[2] ?? titled[3] : void 0;
|
|
454
|
+
return title === void 0 ? { url, end } : { url, title, end };
|
|
455
|
+
};
|
|
456
|
+
var backtickRun = (text, at) => {
|
|
457
|
+
let end = at;
|
|
458
|
+
while (text[end] === "`") end += 1;
|
|
459
|
+
return text.slice(at, end);
|
|
460
|
+
};
|
|
461
|
+
var findCloser = (text, from, delim, misses) => {
|
|
462
|
+
if (from >= (misses.get(delim) ?? Infinity)) return -1;
|
|
463
|
+
for (let i = from; i < text.length; i += 1) {
|
|
464
|
+
const ch = text[i];
|
|
465
|
+
if (ch === "\\") {
|
|
466
|
+
i += 1;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (ch === "`") {
|
|
470
|
+
const run = backtickRun(text, i);
|
|
471
|
+
const close = text.indexOf(run, i + run.length);
|
|
472
|
+
if (close > 0) {
|
|
473
|
+
i = close + run.length - 1;
|
|
474
|
+
continue;
|
|
494
475
|
}
|
|
495
476
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
if (inCode) return text;
|
|
504
|
-
const siblings = parentNode?.content ?? [];
|
|
505
|
-
const index = siblings.indexOf(node);
|
|
506
|
-
const previous = index > 0 ? siblings[index - 1] : void 0;
|
|
507
|
-
const next = index >= 0 ? siblings[index + 1] : void 0;
|
|
508
|
-
return encodeText(text, {
|
|
509
|
-
inTable: !!internals.uixInTable,
|
|
510
|
-
beforeLink: hasLink(next) && !hasLink(node),
|
|
511
|
-
// Unknown position (node not found among its siblings): assume a line start, the safe side.
|
|
512
|
-
lineStart: index <= 0 || previous?.type === "hardBreak",
|
|
513
|
-
headingEnd: parentNode?.type === "heading" && (index < 0 || index === siblings.length - 1)
|
|
514
|
-
});
|
|
515
|
-
};
|
|
516
|
-
return manager;
|
|
517
|
-
}
|
|
518
|
-
function serializeNode(manager, node) {
|
|
519
|
-
const internals = manager;
|
|
520
|
-
internals.uixInTable = node.type === "table";
|
|
521
|
-
try {
|
|
522
|
-
return manager.serialize({ type: "doc", content: [node] }).replace(/^\n+|\n+$/g, "");
|
|
523
|
-
} finally {
|
|
524
|
-
internals.uixInTable = false;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
function createManager(extensions) {
|
|
528
|
-
return patchManager(new import_markdown.MarkdownManager({ marked: createMarked(), extensions }));
|
|
529
|
-
}
|
|
530
|
-
var SEPARATOR_TOKENS = /* @__PURE__ */ new Set(["space", "def"]);
|
|
531
|
-
var nodeKey = (node) => JSON.stringify(node);
|
|
532
|
-
var normalize = (schema, content) => {
|
|
533
|
-
if (content.length === 0) return [];
|
|
534
|
-
const blocks = [];
|
|
535
|
-
for (const node of content) {
|
|
536
|
-
if (node.type && schema.nodes[node.type]?.isInline) {
|
|
537
|
-
const last = blocks[blocks.length - 1];
|
|
538
|
-
if (last?.type === "paragraph" && last.wrapped) last.content.push(node);
|
|
539
|
-
else blocks.push({ type: "paragraph", content: [node], wrapped: true });
|
|
540
|
-
} else {
|
|
541
|
-
blocks.push(node);
|
|
477
|
+
if (text.startsWith(delim, i) && i > from && !/\s/u.test(text[i - 1])) {
|
|
478
|
+
if (delim[0] === "_" && WORD.test(text[i + delim.length] ?? "")) continue;
|
|
479
|
+
if (delim.length === 1 && text[i + 1] === delim) {
|
|
480
|
+
i += 1;
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
return i;
|
|
542
484
|
}
|
|
543
485
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
doc.check();
|
|
547
|
-
return doc.toJSON().content ?? [];
|
|
486
|
+
misses.set(delim, Math.min(from, misses.get(delim) ?? Infinity));
|
|
487
|
+
return -1;
|
|
548
488
|
};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
489
|
+
var pushText = (out, value) => {
|
|
490
|
+
if (!value) return;
|
|
491
|
+
const last = out[out.length - 1];
|
|
492
|
+
if (last?.type === "text") last.value += value;
|
|
493
|
+
else out.push({ type: "text", value });
|
|
494
|
+
};
|
|
495
|
+
function parseInline(text, depth = 0) {
|
|
496
|
+
const out = [];
|
|
497
|
+
if (depth > MAX_DEPTH) {
|
|
498
|
+
pushText(out, text);
|
|
499
|
+
return out;
|
|
556
500
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
let lead = "";
|
|
567
|
-
let defs = [];
|
|
568
|
-
const keepInLead = (raw) => {
|
|
569
|
-
lead += raw;
|
|
570
|
-
const text = raw.replace(/\s+$/u, "").replace(/^\s+/u, "");
|
|
571
|
-
if (text) defs.push(text);
|
|
572
|
-
};
|
|
573
|
-
const pushBlock = (raw) => {
|
|
574
|
-
let nodes;
|
|
575
|
-
try {
|
|
576
|
-
nodes = normalize(schema, manager.parse(raw).content ?? []);
|
|
577
|
-
} catch {
|
|
578
|
-
nodes = normalize(schema, [{ type: "paragraph", content: [{ type: "text", text: raw }] }]);
|
|
579
|
-
}
|
|
580
|
-
if (nodes.length === 0) {
|
|
581
|
-
keepInLead(raw);
|
|
582
|
-
return;
|
|
501
|
+
const misses = /* @__PURE__ */ new Map();
|
|
502
|
+
let i = 0;
|
|
503
|
+
while (i < text.length) {
|
|
504
|
+
const ch = text[i];
|
|
505
|
+
const rest = text.slice(i);
|
|
506
|
+
if (ch === "\\" && ASCII_PUNCT.test(text[i + 1] ?? "")) {
|
|
507
|
+
pushText(out, text[i + 1]);
|
|
508
|
+
i += 2;
|
|
509
|
+
continue;
|
|
583
510
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
};
|
|
588
|
-
if (joined !== source) {
|
|
589
|
-
const core = source.replace(/\s+$/u, "");
|
|
590
|
-
lead = source.slice(0, source.length - source.trimStart().length);
|
|
591
|
-
if (core.trim()) pushBlock(core.trimStart());
|
|
592
|
-
const tail = source.slice(core.length);
|
|
593
|
-
return { source, blocks, tail, tailDefs: [], eol, doc: { type: "doc", content: blocks.flatMap((b) => b.nodes) } };
|
|
594
|
-
}
|
|
595
|
-
for (const token of tokens) {
|
|
596
|
-
if (SEPARATOR_TOKENS.has(token.type) || token.raw.trim() === "") {
|
|
597
|
-
if (token.type === "def") keepInLead(token.raw);
|
|
598
|
-
else lead += token.raw;
|
|
511
|
+
if (ch === "\n") {
|
|
512
|
+
out.push({ type: "br" });
|
|
513
|
+
i += 1;
|
|
599
514
|
continue;
|
|
600
515
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
eol,
|
|
614
|
-
doc: { type: "doc", content: blocks.flatMap((b) => b.nodes) }
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
var isEmptyParagraph = (node) => node.type === "paragraph" && !(node.content ?? []).some((c) => c.type !== "text" || (c.text ?? "").trim() !== "");
|
|
618
|
-
var matchesAt = (children, at, block) => block.keys.every((key, k) => children[at + k] === key);
|
|
619
|
-
var LOOKAHEAD = 8;
|
|
620
|
-
function writeMarkdown(doc, origin, manager) {
|
|
621
|
-
const content = [...doc.content ?? []];
|
|
622
|
-
while (content.length > 0 && isEmptyParagraph(content[content.length - 1])) content.pop();
|
|
623
|
-
if (content.length === 0) {
|
|
624
|
-
return origin.blocks.length === 0 ? origin.source : "";
|
|
625
|
-
}
|
|
626
|
-
const keys = content.map(nodeKey);
|
|
627
|
-
const { blocks, eol } = origin;
|
|
628
|
-
const pieces = [];
|
|
629
|
-
let i = 0;
|
|
630
|
-
let j = 0;
|
|
631
|
-
while (i < content.length) {
|
|
632
|
-
if (j < blocks.length && matchesAt(keys, i, blocks[j])) {
|
|
633
|
-
pieces.push({ text: blocks[j].raw, slot: j, edited: false });
|
|
634
|
-
i += blocks[j].keys.length;
|
|
635
|
-
j += 1;
|
|
516
|
+
if (ch === "`") {
|
|
517
|
+
const run = backtickRun(text, i);
|
|
518
|
+
const close = text.indexOf(run, i + run.length);
|
|
519
|
+
if (close > 0 && text[close + run.length] !== "`") {
|
|
520
|
+
let value = text.slice(i + run.length, close).replace(/\n/g, " ");
|
|
521
|
+
if (/^ .* $/.test(value) && value.trim()) value = value.slice(1, -1);
|
|
522
|
+
out.push({ type: "code", value });
|
|
523
|
+
i = close + run.length;
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
pushText(out, run);
|
|
527
|
+
i += run.length;
|
|
636
528
|
continue;
|
|
637
529
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (
|
|
641
|
-
|
|
642
|
-
|
|
530
|
+
if (ch === "@" && text[i + 1] === "[") {
|
|
531
|
+
const mention = readMention(rest);
|
|
532
|
+
if (mention) {
|
|
533
|
+
out.push({ type: "mention", id: mention.id, label: mention.label });
|
|
534
|
+
i += mention.length;
|
|
535
|
+
continue;
|
|
643
536
|
}
|
|
644
537
|
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
538
|
+
if (ch === "!" && text[i + 1] === "[") {
|
|
539
|
+
const end = findBracketEnd(text, i + 1);
|
|
540
|
+
const dest = end > 0 ? readDestination(text, end + 1) : null;
|
|
541
|
+
if (dest) {
|
|
542
|
+
const alt = plainText(parseInline(text.slice(i + 2, end), depth + 1));
|
|
543
|
+
out.push(dest.title === void 0 ? { type: "image", src: dest.url, alt } : { type: "image", src: dest.url, alt, title: dest.title });
|
|
544
|
+
i = dest.end + 1;
|
|
545
|
+
continue;
|
|
652
546
|
}
|
|
653
547
|
}
|
|
654
|
-
if (
|
|
655
|
-
|
|
548
|
+
if (ch === "[") {
|
|
549
|
+
const end = findBracketEnd(text, i);
|
|
550
|
+
const dest = end > 0 ? readDestination(text, end + 1) : null;
|
|
551
|
+
if (dest) {
|
|
552
|
+
const children = parseInline(text.slice(i + 1, end), depth + 1);
|
|
553
|
+
out.push(dest.title === void 0 ? { type: "link", href: dest.url, children } : { type: "link", href: dest.url, title: dest.title, children });
|
|
554
|
+
i = dest.end + 1;
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (ch === "<") {
|
|
559
|
+
const br = BR_RE.exec(rest);
|
|
560
|
+
if (br) {
|
|
561
|
+
out.push({ type: "br" });
|
|
562
|
+
i += br[0].length;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
const auto = AUTOLINK_RE.exec(rest);
|
|
566
|
+
if (auto) {
|
|
567
|
+
out.push({ type: "link", href: auto[1], children: [{ type: "text", value: auto[1].replace(/^mailto:/i, "") }], bare: true });
|
|
568
|
+
i += auto[0].length;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (ch === "&") {
|
|
573
|
+
const entity = ENTITY_RE.exec(rest);
|
|
574
|
+
const decoded = entity ? decodeEntity(entity) : null;
|
|
575
|
+
if (entity && decoded !== null) {
|
|
576
|
+
pushText(out, decoded);
|
|
577
|
+
i += entity[0].length;
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (ch === "~" && text[i + 1] === "~" && text[i + 2] && !/\s/u.test(text[i + 2])) {
|
|
582
|
+
const close = findCloser(text, i + 2, "~~", misses);
|
|
583
|
+
if (close > 0) {
|
|
584
|
+
out.push({ type: "del", children: parseInline(text.slice(i + 2, close), depth + 1) });
|
|
585
|
+
i = close + 2;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if (ch === "*" || ch === "_") {
|
|
590
|
+
const leftOk = ch === "*" || !WORD.test(text[i - 1] ?? "");
|
|
591
|
+
const double = text[i + 1] === ch;
|
|
592
|
+
const delim = double ? ch + ch : ch;
|
|
593
|
+
const after = text[i + delim.length];
|
|
594
|
+
if (leftOk && after && !/\s/u.test(after)) {
|
|
595
|
+
const close = findCloser(text, i + delim.length, delim, misses);
|
|
596
|
+
if (close > 0) {
|
|
597
|
+
const children = parseInline(text.slice(i + delim.length, close), depth + 1);
|
|
598
|
+
out.push(double ? { type: "strong", children } : { type: "em", children });
|
|
599
|
+
i = close + delim.length;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
const run = new RegExp(`^\\${ch}+`).exec(rest)[0];
|
|
604
|
+
pushText(out, run);
|
|
605
|
+
i += run.length;
|
|
656
606
|
continue;
|
|
657
607
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
608
|
+
if (ch === "h" && !WORD.test(text[i - 1] ?? "")) {
|
|
609
|
+
const bare = BARE_URL_RE.exec(rest);
|
|
610
|
+
if (bare) {
|
|
611
|
+
const url = bare[0].replace(/[.,;:!?'"]+$/, "");
|
|
612
|
+
out.push({ type: "link", href: url, children: [{ type: "text", value: url }], bare: true });
|
|
613
|
+
i += url.length;
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
pushText(out, ch);
|
|
662
618
|
i += 1;
|
|
663
|
-
if (!insert && j < blocks.length) j += 1;
|
|
664
619
|
}
|
|
665
|
-
const blank = eol + eol;
|
|
666
|
-
const emitted = /* @__PURE__ */ new Set();
|
|
667
|
-
let out = "";
|
|
668
|
-
pieces.forEach((piece, n) => {
|
|
669
|
-
const prev = pieces[n - 1];
|
|
670
|
-
const slot = piece.slot;
|
|
671
|
-
const lead = slot !== void 0 ? blocks[slot].lead : "";
|
|
672
|
-
if (n === 0) {
|
|
673
|
-
if (slot === 0) {
|
|
674
|
-
out += lead;
|
|
675
|
-
emitted.add(0);
|
|
676
|
-
}
|
|
677
|
-
} else if (slot !== void 0 && prev?.slot === slot - 1 && // Next to a re-serialized block, only a separator with a blank line is safe.
|
|
678
|
-
(!(piece.edited || prev.edited) || /\n[ \t]*\r?\n/.test(lead))) {
|
|
679
|
-
out += lead;
|
|
680
|
-
emitted.add(slot);
|
|
681
|
-
} else {
|
|
682
|
-
out += blank;
|
|
683
|
-
}
|
|
684
|
-
out += piece.text;
|
|
685
|
-
});
|
|
686
|
-
const last = pieces[pieces.length - 1];
|
|
687
|
-
const tailKept = last.slot === blocks.length - 1;
|
|
688
|
-
const lost = [
|
|
689
|
-
...blocks.flatMap((block, n) => emitted.has(n) ? [] : block.defs),
|
|
690
|
-
...tailKept ? [] : origin.tailDefs
|
|
691
|
-
];
|
|
692
|
-
if (lost.length) out += blank + lost.join(eol);
|
|
693
|
-
if (tailKept) out += origin.tail;
|
|
694
|
-
else if (/\n$/.test(origin.tail)) out += eol;
|
|
695
620
|
return out;
|
|
696
621
|
}
|
|
697
|
-
function
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
function Composer({ children, className, label = "Add a comment", ...props }) {
|
|
714
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react2.Children.map(children, (child) => {
|
|
715
|
-
if (!(0, import_react2.isValidElement)(child) || child.type !== "textarea") return child;
|
|
716
|
-
const p = child.props;
|
|
717
|
-
return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react2.cloneElement)(child, { "aria-label": label });
|
|
718
|
-
}) });
|
|
719
|
-
}
|
|
720
|
-
function ComposerBar({ children, className, ...props }) {
|
|
721
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
622
|
+
function plainText(nodes) {
|
|
623
|
+
return nodes.map((n) => {
|
|
624
|
+
switch (n.type) {
|
|
625
|
+
case "text":
|
|
626
|
+
case "code":
|
|
627
|
+
return n.value;
|
|
628
|
+
case "br":
|
|
629
|
+
return " ";
|
|
630
|
+
case "image":
|
|
631
|
+
return n.alt;
|
|
632
|
+
case "mention":
|
|
633
|
+
return `@${n.label}`;
|
|
634
|
+
default:
|
|
635
|
+
return plainText(n.children);
|
|
636
|
+
}
|
|
637
|
+
}).join("");
|
|
722
638
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
heading2: "M4 12h8M4 18V6M12 18V6M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1",
|
|
729
|
-
heading3: "M4 12h8M4 18V6M12 18V6M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2",
|
|
730
|
-
bold: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8",
|
|
731
|
-
italic: "M19 4h-9M14 20H5M15 4 9 20",
|
|
732
|
-
strike: "M16 4H9a3 3 0 0 0-2.83 4M14 12a4 4 0 0 1 0 8H6M4 12h16",
|
|
733
|
-
code: "m16 18 6-6-6-6M8 6l-6 6 6 6",
|
|
734
|
-
bulletList: "M3 12h.01M3 18h.01M3 6h.01M8 12h13M8 18h13M8 6h13",
|
|
735
|
-
orderedList: "M10 12h11M10 18h11M10 6h11M4 10h2M4 6h1v4M6 18H4c0-1 2-2 2-3s-1-1.5-2-1",
|
|
736
|
-
taskList: "m3 17 2 2 4-4M3 7l2 2 4-4M13 6h8M13 12h8M13 18h8",
|
|
737
|
-
blockquote: "M17 6H3M21 12H8M21 18H8M3 12v6",
|
|
738
|
-
codeBlock: "m10 9-3 3 3 3M14 15l3-3-3-3M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z",
|
|
739
|
-
horizontalRule: "M5 12h14",
|
|
740
|
-
link: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
|
|
741
|
-
table: "M12 3v18M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2ZM3 9h18M3 15h18",
|
|
742
|
-
tableAddRow: "M4 3h16a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM12 15v6M9 18h6",
|
|
743
|
-
tableAddColumn: "M4 3h6a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM18 9v6M15 12h6",
|
|
744
|
-
tableDeleteRow: "M4 3h16a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM9 18h6",
|
|
745
|
-
tableDeleteColumn: "M4 3h6a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM15 12h6",
|
|
746
|
-
trash: "M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",
|
|
747
|
-
image: "M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2ZM9 7a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM21 15l-3.09-3.09a2 2 0 0 0-2.82 0L6 21",
|
|
748
|
-
emoji: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01",
|
|
749
|
-
addReaction: "M22 11v1a10 10 0 1 1-9-10M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01M16 5h6M19 2v6",
|
|
750
|
-
undo: "M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 0 11H11",
|
|
751
|
-
redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
|
|
752
|
-
sourceMode: "M10 12.5 8 15l2 2.5M14 12.5l2 2.5-2 2.5M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",
|
|
753
|
-
richMode: "M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM8 13h8M8 17h5",
|
|
754
|
-
clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2",
|
|
755
|
-
grip: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01",
|
|
756
|
-
more: "M5 12h.01M12 12h.01M19 12h.01"
|
|
639
|
+
var collectMentions = (nodes, out) => {
|
|
640
|
+
for (const n of nodes) {
|
|
641
|
+
if (n.type === "mention") out.push({ id: n.id, label: n.label });
|
|
642
|
+
else if ("children" in n) collectMentions(n.children, out);
|
|
643
|
+
}
|
|
757
644
|
};
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
{
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
stroke: "currentColor",
|
|
765
|
-
strokeWidth: 2,
|
|
766
|
-
strokeLinecap: "round",
|
|
767
|
-
strokeLinejoin: "round",
|
|
768
|
-
"aria-hidden": "true",
|
|
769
|
-
focusable: "false",
|
|
770
|
-
...props,
|
|
771
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: PATHS[name] })
|
|
645
|
+
function findMentions(markdown) {
|
|
646
|
+
const out = [];
|
|
647
|
+
const walkList = (list) => {
|
|
648
|
+
for (const item of list.items) {
|
|
649
|
+
collectMentions(item.children, out);
|
|
650
|
+
item.lists.forEach(walkList);
|
|
772
651
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const css = typeof CSS !== "undefined" ? CSS : void 0;
|
|
791
|
-
anchoringSupported = !!css && typeof css.supports === "function" && css.supports("anchor-name", "--uix-a") && css.supports("position-anchor", "--uix-a") && css.supports("top", "round(anchor(bottom) + 1px, 1px)");
|
|
792
|
-
} catch {
|
|
793
|
-
anchoringSupported = false;
|
|
652
|
+
};
|
|
653
|
+
for (const block of parseMarkdown(markdown)) {
|
|
654
|
+
switch (block.type) {
|
|
655
|
+
case "paragraph":
|
|
656
|
+
case "heading":
|
|
657
|
+
case "quote":
|
|
658
|
+
collectMentions(block.children, out);
|
|
659
|
+
break;
|
|
660
|
+
case "list":
|
|
661
|
+
walkList(block);
|
|
662
|
+
break;
|
|
663
|
+
case "table":
|
|
664
|
+
block.head.forEach((cell) => collectMentions(cell, out));
|
|
665
|
+
block.rows.forEach((row) => row.forEach((cell) => collectMentions(cell, out)));
|
|
666
|
+
break;
|
|
667
|
+
default:
|
|
668
|
+
break;
|
|
794
669
|
}
|
|
795
670
|
}
|
|
796
|
-
return
|
|
797
|
-
}
|
|
798
|
-
var ANCHOR_BAND = 64;
|
|
799
|
-
var anchorCount = 0;
|
|
800
|
-
var namesOf = (value) => value.split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
|
|
801
|
-
function addAnchorName(el, name) {
|
|
802
|
-
const names = namesOf(getComputedStyle(el).getPropertyValue("anchor-name"));
|
|
803
|
-
if (!names.includes(name)) names.push(name);
|
|
804
|
-
el.style.setProperty("anchor-name", names.join(", "));
|
|
805
|
-
}
|
|
806
|
-
function removeAnchorName(el, name) {
|
|
807
|
-
const names = namesOf(el.style.getPropertyValue("anchor-name")).filter((n) => n !== name);
|
|
808
|
-
if (names.length) el.style.setProperty("anchor-name", names.join(", "));
|
|
809
|
-
else el.style.removeProperty("anchor-name");
|
|
671
|
+
return out;
|
|
810
672
|
}
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
673
|
+
var FENCE_RE = /^(`{3,}|~{3,})\s*([^`\s]*)/;
|
|
674
|
+
var HEADING_RE = /^(#{1,6})\s+(.*?)(?:\s+#+)?$/;
|
|
675
|
+
var RULE_RE = /^(?:(?:\*\s*){3,}|(?:-\s*){3,}|(?:_\s*){3,})$/;
|
|
676
|
+
var BULLET_RE = /^(\s*)([-*+])\s+(.*)$/;
|
|
677
|
+
var ORDERED_RE = /^(\s*)(\d{1,9})[.)]\s+(.*)$/;
|
|
678
|
+
var TASK_RE = /^\[([ xX])\]\s+(.*)$/;
|
|
679
|
+
var DELIMITER_ROW_RE = /^\s*\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*)*\|?\s*$/;
|
|
680
|
+
var indentOf = (s) => s.replace(/\t/g, " ").length;
|
|
681
|
+
function splitTableRow(line) {
|
|
682
|
+
let row = line.trim();
|
|
683
|
+
if (row.startsWith("|")) row = row.slice(1);
|
|
684
|
+
if (row.endsWith("|") && !row.endsWith("\\|")) row = row.slice(0, -1);
|
|
685
|
+
const cells = [];
|
|
686
|
+
let cell = "";
|
|
687
|
+
let inCode = false;
|
|
688
|
+
for (let i = 0; i < row.length; i += 1) {
|
|
689
|
+
const ch = row[i];
|
|
690
|
+
if (ch === "\\" && row[i + 1] === "|") {
|
|
691
|
+
cell += "|";
|
|
692
|
+
i += 1;
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
if (ch === "`") inCode = !inCode;
|
|
696
|
+
if (ch === "|" && !inCode) {
|
|
697
|
+
cells.push(cell.trim());
|
|
698
|
+
cell = "";
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
cell += ch;
|
|
819
702
|
}
|
|
703
|
+
cells.push(cell.trim());
|
|
704
|
+
return cells;
|
|
820
705
|
}
|
|
821
|
-
var
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
706
|
+
var alignOf = (cell) => {
|
|
707
|
+
const c = cell.trim();
|
|
708
|
+
const left = c.startsWith(":");
|
|
709
|
+
const right = c.endsWith(":");
|
|
710
|
+
return left && right ? "center" : right ? "right" : left ? "left" : null;
|
|
711
|
+
};
|
|
712
|
+
var matchListLine = (line) => {
|
|
713
|
+
const bullet = BULLET_RE.exec(line);
|
|
714
|
+
if (bullet && !RULE_RE.test(line.trim())) return { indent: indentOf(bullet[1]), ordered: false, number: 1, text: bullet[3] };
|
|
715
|
+
const ordered = ORDERED_RE.exec(line);
|
|
716
|
+
if (ordered) return { indent: indentOf(ordered[1]), ordered: true, number: Number(ordered[2]), text: ordered[3] };
|
|
717
|
+
return null;
|
|
718
|
+
};
|
|
719
|
+
var toItem = (text) => {
|
|
720
|
+
const task = TASK_RE.exec(text);
|
|
721
|
+
return task ? { checked: task[1] !== " ", children: parseInline(task[2]), lists: [] } : { checked: null, children: parseInline(text), lists: [] };
|
|
722
|
+
};
|
|
723
|
+
var buildList = (lines) => {
|
|
724
|
+
const lists = [];
|
|
725
|
+
const stack = [];
|
|
726
|
+
for (const line of lines) {
|
|
727
|
+
while (stack.length && line.indent < stack[stack.length - 1].indent) stack.pop();
|
|
728
|
+
let top = stack[stack.length - 1];
|
|
729
|
+
if (top && line.indent > top.indent) {
|
|
730
|
+
const parentItem = top.list.items[top.list.items.length - 1];
|
|
731
|
+
const nested = { type: "list", ordered: line.ordered, start: line.number, items: [] };
|
|
732
|
+
parentItem.lists.push(nested);
|
|
733
|
+
stack.push({ list: nested, indent: line.indent });
|
|
734
|
+
top = stack[stack.length - 1];
|
|
735
|
+
} else if (!top || top.list.ordered !== line.ordered) {
|
|
736
|
+
const list = { type: "list", ordered: line.ordered, start: line.number, items: [] };
|
|
737
|
+
if (stack.length <= 1) {
|
|
738
|
+
lists.push(list);
|
|
739
|
+
stack.length = 0;
|
|
740
|
+
} else {
|
|
741
|
+
stack.pop();
|
|
742
|
+
const parent = stack[stack.length - 1].list;
|
|
743
|
+
parent.items[parent.items.length - 1].lists.push(list);
|
|
847
744
|
}
|
|
848
|
-
|
|
745
|
+
stack.push({ list, indent: line.indent });
|
|
746
|
+
top = stack[stack.length - 1];
|
|
849
747
|
}
|
|
850
|
-
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const floating = floatingRef.current;
|
|
864
|
-
if (!anchorEl || !floating) return;
|
|
865
|
-
const s = floating.style;
|
|
866
|
-
if (!prepared.has(floating)) {
|
|
867
|
-
s.position = "fixed";
|
|
868
|
-
s.inset = "auto";
|
|
869
|
-
s.margin = "0";
|
|
870
|
-
prepared.add(floating);
|
|
871
|
-
written.current = "";
|
|
748
|
+
top.list.items.push(toItem(line.text));
|
|
749
|
+
}
|
|
750
|
+
return lists;
|
|
751
|
+
};
|
|
752
|
+
function parseMarkdown(source) {
|
|
753
|
+
const lines = source.replace(/\r\n?/g, "\n").split("\n");
|
|
754
|
+
const blocks = [];
|
|
755
|
+
let paragraph = [];
|
|
756
|
+
let i = 0;
|
|
757
|
+
const flush = () => {
|
|
758
|
+
if (paragraph.length) {
|
|
759
|
+
blocks.push({ type: "paragraph", children: parseInline(paragraph.join("\n")) });
|
|
760
|
+
paragraph = [];
|
|
872
761
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
heightCap.current.written = max;
|
|
762
|
+
};
|
|
763
|
+
while (i < lines.length) {
|
|
764
|
+
const line = lines[i];
|
|
765
|
+
const trimmed = line.trim();
|
|
766
|
+
if (trimmed === "") {
|
|
767
|
+
flush();
|
|
768
|
+
i += 1;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
const fence = FENCE_RE.exec(trimmed);
|
|
772
|
+
if (fence) {
|
|
773
|
+
flush();
|
|
774
|
+
const marker = fence[1];
|
|
775
|
+
const body = [];
|
|
776
|
+
i += 1;
|
|
777
|
+
while (i < lines.length && !lines[i].trim().startsWith(marker)) {
|
|
778
|
+
body.push(lines[i]);
|
|
779
|
+
i += 1;
|
|
892
780
|
}
|
|
893
|
-
|
|
894
|
-
|
|
781
|
+
i += 1;
|
|
782
|
+
blocks.push({ type: "code", lang: fence[2] ?? "", value: body.join("\n") });
|
|
783
|
+
continue;
|
|
895
784
|
}
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
{
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
{ placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, stickySide: side.current ?? void 0 }
|
|
903
|
-
);
|
|
904
|
-
const measured = size.width > 0 || size.height > 0;
|
|
905
|
-
if (measured && session.current) side.current = pos.side;
|
|
906
|
-
if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
|
|
907
|
-
const writeFixed = (at) => {
|
|
908
|
-
const left2 = `${Math.round(at.x)}px`;
|
|
909
|
-
const top2 = `${Math.round(at.y)}px`;
|
|
910
|
-
const key2 = `fixed ${left2} ${top2}`;
|
|
911
|
-
if (key2 === written.current) return;
|
|
912
|
-
s.left = left2;
|
|
913
|
-
s.top = top2;
|
|
914
|
-
written.current = key2;
|
|
915
|
-
};
|
|
916
|
-
const limit = pos.mainAxisRange;
|
|
917
|
-
const nearLimit = limit !== null && (pos.mainAxisNatural < limit[0] + ANCHOR_BAND || pos.mainAxisNatural > limit[1] - ANCHOR_BAND);
|
|
918
|
-
if (anchoringFailed.current || nearLimit || !supportsAnchoring()) {
|
|
919
|
-
if (anchoring.current) release(false);
|
|
920
|
-
writeFixed(pos);
|
|
921
|
-
return;
|
|
785
|
+
const heading = HEADING_RE.exec(trimmed);
|
|
786
|
+
if (heading) {
|
|
787
|
+
flush();
|
|
788
|
+
blocks.push({ type: "heading", level: heading[1].length, children: parseInline(heading[2]) });
|
|
789
|
+
i += 1;
|
|
790
|
+
continue;
|
|
922
791
|
}
|
|
923
|
-
if (
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
anchoring.current = { anchor: anchorEl, name: anchorName.current, yEdge: "bottom", xEdge: "left", dy: 0, dx: 0, verified: false };
|
|
929
|
-
written.current = "";
|
|
792
|
+
if (RULE_RE.test(trimmed)) {
|
|
793
|
+
flush();
|
|
794
|
+
blocks.push({ type: "rule" });
|
|
795
|
+
i += 1;
|
|
796
|
+
continue;
|
|
930
797
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
s.top = top;
|
|
941
|
-
s.left = left;
|
|
942
|
-
written.current = key;
|
|
798
|
+
if (trimmed.startsWith(">")) {
|
|
799
|
+
flush();
|
|
800
|
+
const quote = [];
|
|
801
|
+
while (i < lines.length && lines[i].trim().startsWith(">")) {
|
|
802
|
+
quote.push(lines[i].trim().replace(/^>\s?/, ""));
|
|
803
|
+
i += 1;
|
|
804
|
+
}
|
|
805
|
+
blocks.push({ type: "quote", children: parseInline(quote.join(" ")) });
|
|
806
|
+
continue;
|
|
943
807
|
}
|
|
944
|
-
if (
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
808
|
+
if (trimmed.includes("|") && i + 1 < lines.length && DELIMITER_ROW_RE.test(lines[i + 1]) && lines[i + 1].includes("-")) {
|
|
809
|
+
const head = splitTableRow(line);
|
|
810
|
+
const delimiters = splitTableRow(lines[i + 1]);
|
|
811
|
+
if (delimiters.length === head.length) {
|
|
812
|
+
flush();
|
|
813
|
+
const rows = [];
|
|
814
|
+
i += 2;
|
|
815
|
+
while (i < lines.length && lines[i].trim() !== "" && lines[i].includes("|")) {
|
|
816
|
+
const cells = splitTableRow(lines[i]);
|
|
817
|
+
rows.push(head.map((_, c) => parseInline(cells[c] ?? "")));
|
|
818
|
+
i += 1;
|
|
819
|
+
}
|
|
820
|
+
blocks.push({ type: "table", align: delimiters.map(alignOf), head: head.map((c) => parseInline(c)), rows });
|
|
821
|
+
continue;
|
|
954
822
|
}
|
|
955
823
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (
|
|
967
|
-
|
|
824
|
+
const first = matchListLine(line);
|
|
825
|
+
if (first) {
|
|
826
|
+
flush();
|
|
827
|
+
const listLines = [];
|
|
828
|
+
while (i < lines.length) {
|
|
829
|
+
const current = lines[i];
|
|
830
|
+
const item = matchListLine(current);
|
|
831
|
+
if (item) {
|
|
832
|
+
if (listLines.length && item.indent <= listLines[0].indent && item.ordered !== listLines[0].ordered) break;
|
|
833
|
+
listLines.push(item);
|
|
834
|
+
} else if (current.trim() !== "" && indentOf(current) > listLines[0].indent && listLines.length) {
|
|
835
|
+
listLines[listLines.length - 1].text += `
|
|
836
|
+
${current.trim()}`;
|
|
837
|
+
} else {
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
i += 1;
|
|
968
841
|
}
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
window.removeEventListener("scroll", onScroll, true);
|
|
979
|
-
window.removeEventListener("resize", onResize);
|
|
980
|
-
observer?.disconnect();
|
|
981
|
-
session.current = false;
|
|
982
|
-
side.current = null;
|
|
983
|
-
anchoringFailed.current = false;
|
|
984
|
-
release(true);
|
|
985
|
-
uncap();
|
|
986
|
-
};
|
|
987
|
-
}, [open, reposition, anchor, floatingRef, release, uncap]);
|
|
988
|
-
const watchAnchor = open && !!onAnchorHidden;
|
|
989
|
-
(0, import_react3.useEffect)(() => {
|
|
990
|
-
const anchorEl = resolveAnchor(anchor);
|
|
991
|
-
if (!watchAnchor || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
992
|
-
const observer = new IntersectionObserver((entries) => {
|
|
993
|
-
const latest = entries[entries.length - 1];
|
|
994
|
-
if (latest && !latest.isIntersecting) onAnchorHiddenRef.current?.();
|
|
995
|
-
});
|
|
996
|
-
observer.observe(anchorEl);
|
|
997
|
-
return () => observer.disconnect();
|
|
998
|
-
}, [watchAnchor, anchor]);
|
|
999
|
-
(0, import_react3.useEffect)(() => () => release(false), [release]);
|
|
1000
|
-
return reposition;
|
|
842
|
+
const base = Math.min(...listLines.map((l) => l.indent));
|
|
843
|
+
blocks.push(...buildList(listLines.map((l) => ({ ...l, indent: l.indent - base }))));
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
paragraph.push(trimmed);
|
|
847
|
+
i += 1;
|
|
848
|
+
}
|
|
849
|
+
flush();
|
|
850
|
+
return blocks;
|
|
1001
851
|
}
|
|
1002
852
|
|
|
1003
|
-
// src/
|
|
1004
|
-
var
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
return
|
|
1012
|
-
|
|
1013
|
-
}
|
|
1014
|
-
var settle = (el) => {
|
|
1015
|
-
if (!el.hasAttribute(PLACING)) return;
|
|
1016
|
-
getComputedStyle(el).getPropertyValue("transform");
|
|
1017
|
-
el.removeAttribute(PLACING);
|
|
1018
|
-
};
|
|
1019
|
-
function Popover({
|
|
1020
|
-
popover: popoverProp,
|
|
1021
|
-
anchor,
|
|
1022
|
-
placement = "bottom-start",
|
|
1023
|
-
offset = 6,
|
|
1024
|
-
closeWhenAnchorHidden = false,
|
|
1025
|
-
onAnchorHidden,
|
|
1026
|
-
capHeight = false,
|
|
1027
|
-
openOnHover = false,
|
|
1028
|
-
className,
|
|
1029
|
-
children,
|
|
1030
|
-
...props
|
|
1031
|
-
}) {
|
|
1032
|
-
const hover = !!openOnHover && !!anchor;
|
|
1033
|
-
const popover = popoverProp ?? (hover ? "manual" : "auto");
|
|
1034
|
-
const openDelay = typeof openOnHover === "object" ? openOnHover.openDelay ?? 300 : 300;
|
|
1035
|
-
const closeDelay = typeof openOnHover === "object" ? openOnHover.closeDelay ?? 150 : 150;
|
|
1036
|
-
const ref = (0, import_react4.useRef)(null);
|
|
1037
|
-
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1038
|
-
const openRef = (0, import_react4.useRef)(false);
|
|
1039
|
-
const onAnchorHiddenRef = (0, import_react4.useRef)(onAnchorHidden);
|
|
1040
|
-
onAnchorHiddenRef.current = onAnchorHidden;
|
|
1041
|
-
const popoverAttr = { popover };
|
|
1042
|
-
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
1043
|
-
open: open && !!anchor,
|
|
1044
|
-
placement,
|
|
1045
|
-
offset,
|
|
1046
|
-
capHeight,
|
|
1047
|
-
onAnchorHidden: closeWhenAnchorHidden ? () => {
|
|
1048
|
-
const el = ref.current;
|
|
1049
|
-
if (!el || !isPopoverOpen(el)) return;
|
|
1050
|
-
onAnchorHiddenRef.current?.();
|
|
1051
|
-
const active = el.ownerDocument.activeElement;
|
|
1052
|
-
if (active && el.contains(active)) active.blur();
|
|
1053
|
-
el.hidePopover();
|
|
1054
|
-
} : void 0
|
|
1055
|
-
});
|
|
1056
|
-
(0, import_react4.useEffect)(() => {
|
|
1057
|
-
const el = ref.current;
|
|
1058
|
-
if (!el || !anchor) return;
|
|
1059
|
-
const hold = (event) => {
|
|
1060
|
-
if (event.newState !== "open") return;
|
|
1061
|
-
el.setAttribute(PLACING, "");
|
|
1062
|
-
window.setTimeout(() => settle(el), 150);
|
|
1063
|
-
};
|
|
1064
|
-
const sync = () => {
|
|
1065
|
-
if (!isPopoverOpen(el)) {
|
|
1066
|
-
el.removeAttribute(PLACING);
|
|
1067
|
-
setOpen(false);
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
reposition();
|
|
1071
|
-
window.setTimeout(() => {
|
|
1072
|
-
if (!isPopoverOpen(el)) return;
|
|
1073
|
-
if (!openRef.current) {
|
|
1074
|
-
setOpen(true);
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
reposition();
|
|
1078
|
-
settle(el);
|
|
1079
|
-
}, 0);
|
|
1080
|
-
};
|
|
1081
|
-
el.addEventListener("beforetoggle", hold);
|
|
1082
|
-
el.addEventListener("toggle", sync);
|
|
1083
|
-
return () => {
|
|
1084
|
-
el.removeEventListener("beforetoggle", hold);
|
|
1085
|
-
el.removeEventListener("toggle", sync);
|
|
1086
|
-
el.removeAttribute(PLACING);
|
|
1087
|
-
};
|
|
1088
|
-
}, [anchor, reposition]);
|
|
1089
|
-
(0, import_react4.useEffect)(() => {
|
|
1090
|
-
const el = ref.current;
|
|
1091
|
-
const anchorEl = resolveAnchor(anchor);
|
|
1092
|
-
if (!hover || !el || !anchorEl) return;
|
|
1093
|
-
let timer;
|
|
1094
|
-
const inside = (node) => !!node && (anchorEl.contains(node) || el.contains(node));
|
|
1095
|
-
const stillWanted = () => {
|
|
1096
|
-
try {
|
|
1097
|
-
return anchorEl.matches(":hover") || el.matches(":hover") || inside(el.ownerDocument.activeElement);
|
|
1098
|
-
} catch {
|
|
1099
|
-
return false;
|
|
1100
|
-
}
|
|
1101
|
-
};
|
|
1102
|
-
const show = (delay) => {
|
|
1103
|
-
window.clearTimeout(timer);
|
|
1104
|
-
timer = window.setTimeout(() => {
|
|
1105
|
-
if (!isPopoverOpen(el)) {
|
|
1106
|
-
try {
|
|
1107
|
-
el.showPopover?.();
|
|
1108
|
-
} catch {
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
}, delay);
|
|
1112
|
-
};
|
|
1113
|
-
const hide = (delay) => {
|
|
1114
|
-
window.clearTimeout(timer);
|
|
1115
|
-
timer = window.setTimeout(() => {
|
|
1116
|
-
if (isPopoverOpen(el) && !stillWanted()) {
|
|
1117
|
-
try {
|
|
1118
|
-
el.hidePopover?.();
|
|
1119
|
-
} catch {
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}, delay);
|
|
853
|
+
// src/rich-text/mention.ts
|
|
854
|
+
var Mention = import_core.Node.create({
|
|
855
|
+
name: "mention",
|
|
856
|
+
group: "inline",
|
|
857
|
+
inline: true,
|
|
858
|
+
atom: true,
|
|
859
|
+
selectable: false,
|
|
860
|
+
addAttributes() {
|
|
861
|
+
return {
|
|
862
|
+
id: { default: null },
|
|
863
|
+
label: { default: null }
|
|
1123
864
|
};
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
};
|
|
1134
|
-
const onKeyDown = (event) => {
|
|
1135
|
-
if (event.key !== "Escape" || !isPopoverOpen(el)) return;
|
|
1136
|
-
window.clearTimeout(timer);
|
|
1137
|
-
const hadFocus = el.contains(el.ownerDocument.activeElement);
|
|
1138
|
-
try {
|
|
1139
|
-
el.hidePopover?.();
|
|
1140
|
-
} catch {
|
|
865
|
+
},
|
|
866
|
+
parseHTML() {
|
|
867
|
+
return [{
|
|
868
|
+
tag: "span[data-mention-id]",
|
|
869
|
+
getAttrs: (el) => {
|
|
870
|
+
const node = el;
|
|
871
|
+
const id = node.getAttribute("data-mention-id") ?? "";
|
|
872
|
+
const label = node.getAttribute("data-mention-label") ?? node.textContent?.replace(/^@/, "") ?? "";
|
|
873
|
+
return formatMention({ id, label }) ? { id, label } : false;
|
|
1141
874
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
},
|
|
1170
|
-
|
|
1171
|
-
}
|
|
875
|
+
}];
|
|
876
|
+
},
|
|
877
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
878
|
+
const label = String(node.attrs.label ?? "");
|
|
879
|
+
return [
|
|
880
|
+
"span",
|
|
881
|
+
(0, import_core.mergeAttributes)(HTMLAttributes, {
|
|
882
|
+
class: "uix-mention",
|
|
883
|
+
"data-mention-id": String(node.attrs.id ?? ""),
|
|
884
|
+
"data-mention-label": label,
|
|
885
|
+
contenteditable: "false"
|
|
886
|
+
}),
|
|
887
|
+
`@${label}`
|
|
888
|
+
];
|
|
889
|
+
},
|
|
890
|
+
renderText({ node }) {
|
|
891
|
+
return `@${String(node.attrs.label ?? "")}`;
|
|
892
|
+
},
|
|
893
|
+
markdownTokenizer: {
|
|
894
|
+
name: "mention",
|
|
895
|
+
level: "inline",
|
|
896
|
+
start: (src) => src.indexOf("@["),
|
|
897
|
+
tokenize: (src) => {
|
|
898
|
+
const found = readMention(src);
|
|
899
|
+
if (!found) return void 0;
|
|
900
|
+
return { type: "mention", raw: src.slice(0, found.length), id: found.id, label: found.label };
|
|
901
|
+
}
|
|
902
|
+
},
|
|
903
|
+
parseMarkdown: (token, helpers) => helpers.createNode("mention", { id: String(token.id ?? ""), label: String(token.label ?? "") }),
|
|
904
|
+
renderMarkdown: (node) => formatMention({ id: String(node.attrs?.id ?? ""), label: String(node.attrs?.label ?? "") }) ?? `@${String(node.attrs?.label ?? "")}`
|
|
905
|
+
});
|
|
1172
906
|
|
|
1173
|
-
// src/
|
|
1174
|
-
var
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
};
|
|
1185
|
-
|
|
1186
|
-
var
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
}) {
|
|
1207
|
-
const
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
const [active, setActive] = (0, import_react5.useState)(0);
|
|
1223
|
-
const popoverEl = () => typeof document === "undefined" ? null : document.getElementById(popoverId);
|
|
1224
|
-
const isOpen = (el) => {
|
|
1225
|
-
try {
|
|
1226
|
-
return !!el?.matches(":popover-open");
|
|
1227
|
-
} catch {
|
|
1228
|
-
return false;
|
|
1229
|
-
}
|
|
1230
|
-
};
|
|
1231
|
-
(0, import_react5.useEffect)(() => {
|
|
1232
|
-
const el = popoverEl();
|
|
1233
|
-
if (!el) return;
|
|
1234
|
-
const onToggle = (event) => {
|
|
1235
|
-
const { newState } = event;
|
|
1236
|
-
const nowOpen = newState ? newState === "open" : isOpen(el);
|
|
1237
|
-
setOpen(nowOpen);
|
|
1238
|
-
onOpenChange?.(nowOpen);
|
|
1239
|
-
if (nowOpen) {
|
|
1240
|
-
closedByAnchor.current = false;
|
|
1241
|
-
pendingJump.current = null;
|
|
1242
|
-
setRecent(readRecentEmoji(recentStorageKey));
|
|
1243
|
-
setQuery("");
|
|
1244
|
-
setActive(0);
|
|
1245
|
-
requestAnimationFrame(() => searchRef.current?.focus({ preventScroll: true }));
|
|
1246
|
-
} else if (closedByAnchor.current) {
|
|
1247
|
-
closedByAnchor.current = false;
|
|
1248
|
-
} else if (el.contains(document.activeElement) || document.activeElement === document.body) {
|
|
1249
|
-
focusTrigger();
|
|
1250
|
-
}
|
|
1251
|
-
};
|
|
1252
|
-
el.addEventListener("toggle", onToggle);
|
|
1253
|
-
return () => el.removeEventListener("toggle", onToggle);
|
|
1254
|
-
}, [popoverId, recentStorageKey, onOpenChange]);
|
|
1255
|
-
(0, import_react5.useEffect)(() => {
|
|
1256
|
-
if (!open || data?.locale === locale) return;
|
|
1257
|
-
let live = true;
|
|
1258
|
-
setFailed(false);
|
|
1259
|
-
loadData(locale).then(
|
|
1260
|
-
(loaded) => {
|
|
1261
|
-
if (live) setData(loaded);
|
|
1262
|
-
},
|
|
1263
|
-
() => {
|
|
1264
|
-
if (live) setFailed(true);
|
|
1265
|
-
}
|
|
1266
|
-
);
|
|
1267
|
-
return () => {
|
|
1268
|
-
live = false;
|
|
1269
|
-
};
|
|
1270
|
-
}, [open, locale, loadData, data?.locale]);
|
|
1271
|
-
const focusTrigger = () => {
|
|
1272
|
-
const el = anchorRef.current?.querySelector("button, [href], [tabindex]");
|
|
1273
|
-
el?.focus();
|
|
1274
|
-
};
|
|
1275
|
-
const hide = () => {
|
|
1276
|
-
const el = popoverEl();
|
|
1277
|
-
if (el && isOpen(el)) el.hidePopover();
|
|
1278
|
-
};
|
|
1279
|
-
const browseSections = (0, import_react5.useMemo)(() => {
|
|
1280
|
-
const list = [];
|
|
1281
|
-
if (quickPicks?.length) list.push({ key: "quick", title: labels.quickPicks, emojis: [...new Set(quickPicks)] });
|
|
1282
|
-
if (recent.length) list.push({ key: "recent", title: labels.recent, emojis: recent });
|
|
1283
|
-
if (data) {
|
|
1284
|
-
data.groups.forEach((group, index) => {
|
|
1285
|
-
list.push({ key: group.key, title: group.name, emojis: data.emojis.filter((e) => e.group === index).map((e) => e.emoji) });
|
|
1286
|
-
});
|
|
1287
|
-
}
|
|
1288
|
-
return list;
|
|
1289
|
-
}, [data, quickPicks, recent, labels.quickPicks, labels.recent]);
|
|
1290
|
-
const sections = (0, import_react5.useMemo)(() => {
|
|
1291
|
-
if (!query.trim()) return browseSections;
|
|
1292
|
-
return data ? [{ key: "results", title: labels.search, emojis: searchEmoji(data, query).map((e) => e.emoji) }] : [];
|
|
1293
|
-
}, [query, data, browseSections, labels.search]);
|
|
1294
|
-
const flat = (0, import_react5.useMemo)(() => sections.flatMap((s) => s.emojis), [sections]);
|
|
1295
|
-
const sectionStart = (0, import_react5.useMemo)(() => {
|
|
1296
|
-
const starts = /* @__PURE__ */ new Map();
|
|
1297
|
-
let at = 0;
|
|
1298
|
-
for (const section of sections) {
|
|
1299
|
-
starts.set(section.key, at);
|
|
1300
|
-
at += section.emojis.length;
|
|
1301
|
-
}
|
|
1302
|
-
return starts;
|
|
1303
|
-
}, [sections]);
|
|
1304
|
-
const activeIndex = Math.min(active, Math.max(flat.length - 1, 0));
|
|
1305
|
-
const choose = (emoji) => {
|
|
1306
|
-
setRecent(pushRecentEmoji(emoji, recentStorageKey));
|
|
1307
|
-
onSelect(emoji);
|
|
1308
|
-
hide();
|
|
1309
|
-
};
|
|
1310
|
-
const focusCell = (0, import_react5.useCallback)((index, reveal = true) => {
|
|
1311
|
-
setActive(index);
|
|
1312
|
-
const find = () => gridRef.current?.querySelector(`[data-emoji-index="${index}"]`);
|
|
1313
|
-
const move = (cell2) => {
|
|
1314
|
-
cell2.focus({ preventScroll: true });
|
|
1315
|
-
if (reveal && gridRef.current) revealInGrid(gridRef.current, cell2);
|
|
1316
|
-
};
|
|
1317
|
-
const cell = find();
|
|
1318
|
-
if (cell) move(cell);
|
|
1319
|
-
else requestAnimationFrame(() => {
|
|
1320
|
-
const later = find();
|
|
1321
|
-
if (later) move(later);
|
|
1322
|
-
});
|
|
1323
|
-
}, []);
|
|
1324
|
-
const showSection = (key) => {
|
|
1325
|
-
const grid = gridRef.current;
|
|
1326
|
-
const section = grid?.querySelector(`[data-section="${key}"]`);
|
|
1327
|
-
if (!grid || !section) return;
|
|
1328
|
-
grid.scrollTop += section.getBoundingClientRect().top - grid.getBoundingClientRect().top;
|
|
1329
|
-
const start = sectionStart.get(key);
|
|
1330
|
-
if (start !== void 0) focusCell(start, false);
|
|
1331
|
-
};
|
|
1332
|
-
const onCategory = (key) => {
|
|
1333
|
-
if (!query.trim()) {
|
|
1334
|
-
showSection(key);
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
pendingJump.current = key;
|
|
1338
|
-
setQuery("");
|
|
1339
|
-
setActive(0);
|
|
1340
|
-
};
|
|
1341
|
-
useIsomorphicLayoutEffect3(() => {
|
|
1342
|
-
const key = pendingJump.current;
|
|
1343
|
-
if (key === null || query.trim()) return;
|
|
1344
|
-
pendingJump.current = null;
|
|
1345
|
-
showSection(key);
|
|
1346
|
-
}, [query, sections]);
|
|
1347
|
-
const onGridKeyDown = (event) => {
|
|
1348
|
-
const target = event.target;
|
|
1349
|
-
const index = Number(target.dataset.emojiIndex ?? -1);
|
|
1350
|
-
if (index < 0) return;
|
|
1351
|
-
const next = emojiGridMove(index, event.key, flat.length, EMOJI_GRID_COLUMNS);
|
|
1352
|
-
if (event.key === "ArrowUp" && next === null) {
|
|
1353
|
-
event.preventDefault();
|
|
1354
|
-
searchRef.current?.focus({ preventScroll: true });
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
|
-
if (next === null) return;
|
|
1358
|
-
event.preventDefault();
|
|
1359
|
-
focusCell(next);
|
|
1360
|
-
};
|
|
1361
|
-
const onSearchKeyDown = (event) => {
|
|
1362
|
-
if (event.key === "Escape") {
|
|
1363
|
-
event.preventDefault();
|
|
1364
|
-
hide();
|
|
1365
|
-
} else if (event.key === "ArrowDown" && flat.length) {
|
|
1366
|
-
event.preventDefault();
|
|
1367
|
-
focusCell(0);
|
|
1368
|
-
} else if (event.key === "Enter" && query.trim() && flat[0]) {
|
|
1369
|
-
event.preventDefault();
|
|
1370
|
-
choose(flat[0]);
|
|
1371
|
-
}
|
|
1372
|
-
};
|
|
1373
|
-
const toggleFromTrigger = (event) => {
|
|
1374
|
-
const el = popoverEl();
|
|
1375
|
-
if (!el || event.defaultPrevented) return;
|
|
1376
|
-
const was = pointerWasOpen.current;
|
|
1377
|
-
pointerWasOpen.current = null;
|
|
1378
|
-
if (was === true) return;
|
|
1379
|
-
if (isOpen(el)) el.hidePopover();
|
|
1380
|
-
else el.showPopover();
|
|
1381
|
-
};
|
|
1382
|
-
const triggerProps = (0, import_react5.isValidElement)(trigger) ? trigger.props : {};
|
|
1383
|
-
const triggerEl = (0, import_react5.isValidElement)(trigger) ? (0, import_react5.cloneElement)(trigger, {
|
|
1384
|
-
"aria-haspopup": "dialog",
|
|
1385
|
-
"aria-expanded": open,
|
|
1386
|
-
"aria-controls": popoverId,
|
|
1387
|
-
onPointerDown: (event) => {
|
|
1388
|
-
pointerWasOpen.current = isOpen(popoverEl());
|
|
1389
|
-
triggerProps.onPointerDown?.(event);
|
|
1390
|
-
},
|
|
1391
|
-
onClick: (event) => {
|
|
1392
|
-
triggerProps.onClick?.(event);
|
|
1393
|
-
toggleFromTrigger(event);
|
|
1394
|
-
}
|
|
1395
|
-
}) : trigger;
|
|
1396
|
-
const names = data?.names;
|
|
1397
|
-
let offset = 0;
|
|
1398
|
-
const status = failed ? labels.loadFailed : !data && open ? labels.loading : query.trim() && data && flat.length === 0 ? labels.noResults : "";
|
|
1399
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { ref: anchorRef, className: cx("uix-emoji-picker__anchor", className), children: [
|
|
1400
|
-
triggerEl,
|
|
1401
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1402
|
-
Popover,
|
|
1403
|
-
{
|
|
1404
|
-
id: popoverId,
|
|
1405
|
-
anchor: anchorRef,
|
|
1406
|
-
placement,
|
|
1407
|
-
className: "uix-emoji-picker-popover",
|
|
1408
|
-
role: "dialog",
|
|
1409
|
-
"aria-label": labels.dialog,
|
|
1410
|
-
closeWhenAnchorHidden: true,
|
|
1411
|
-
onAnchorHidden: () => {
|
|
1412
|
-
closedByAnchor.current = true;
|
|
1413
|
-
},
|
|
1414
|
-
children: open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uix-emoji-picker uix-emoji-picker--full", lang: locale, children: [
|
|
1415
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1416
|
-
"input",
|
|
1417
|
-
{
|
|
1418
|
-
ref: searchRef,
|
|
1419
|
-
type: "search",
|
|
1420
|
-
className: "uix-input uix-emoji-picker__search",
|
|
1421
|
-
"aria-label": labels.search,
|
|
1422
|
-
placeholder: labels.search,
|
|
1423
|
-
value: query,
|
|
1424
|
-
onChange: (event) => {
|
|
1425
|
-
setQuery(event.target.value);
|
|
1426
|
-
setActive(0);
|
|
1427
|
-
},
|
|
1428
|
-
onKeyDown: onSearchKeyDown,
|
|
1429
|
-
autoComplete: "off",
|
|
1430
|
-
spellCheck: false
|
|
1431
|
-
}
|
|
1432
|
-
),
|
|
1433
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1434
|
-
"div",
|
|
1435
|
-
{
|
|
1436
|
-
className: "uix-emoji-picker__nav",
|
|
1437
|
-
role: data ? "group" : void 0,
|
|
1438
|
-
"aria-label": data ? labels.categories : void 0,
|
|
1439
|
-
children: data ? browseSections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1440
|
-
"button",
|
|
1441
|
-
{
|
|
1442
|
-
type: "button",
|
|
1443
|
-
className: "uix-emoji-picker__nav-btn",
|
|
1444
|
-
"aria-label": section.title,
|
|
1445
|
-
title: section.title,
|
|
1446
|
-
onClick: () => onCategory(section.key),
|
|
1447
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", children: section.emojis[0] ? renderEmoji(section.emojis[0]) : null })
|
|
1448
|
-
},
|
|
1449
|
-
section.key
|
|
1450
|
-
)) : null
|
|
1451
|
-
}
|
|
1452
|
-
),
|
|
1453
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uix-emoji-picker__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
|
|
1454
|
-
sections.map((section) => {
|
|
1455
|
-
const start = offset;
|
|
1456
|
-
offset += section.emojis.length;
|
|
1457
|
-
if (!section.emojis.length) return null;
|
|
1458
|
-
const headingId = `${id}-${section.key}`;
|
|
1459
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "uix-emoji-picker__section", "data-section": section.key, "aria-labelledby": headingId, children: [
|
|
1460
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: headingId, className: "uix-emoji-picker__heading", children: section.title }),
|
|
1461
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "uix-emoji-picker__cells", role: "group", "aria-labelledby": headingId, children: section.emojis.map((emoji, i) => {
|
|
1462
|
-
const index = start + i;
|
|
1463
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1464
|
-
"button",
|
|
1465
|
-
{
|
|
1466
|
-
type: "button",
|
|
1467
|
-
className: "uix-emoji-picker__btn",
|
|
1468
|
-
"data-emoji-index": index,
|
|
1469
|
-
tabIndex: index === activeIndex ? 0 : -1,
|
|
1470
|
-
"aria-label": names?.get(emoji) ?? emoji,
|
|
1471
|
-
title: names?.get(emoji),
|
|
1472
|
-
onClick: () => choose(emoji),
|
|
1473
|
-
onFocus: () => setActive(index),
|
|
1474
|
-
children: renderEmoji(emoji)
|
|
1475
|
-
},
|
|
1476
|
-
`${section.key}-${emoji}`
|
|
1477
|
-
);
|
|
1478
|
-
}) })
|
|
1479
|
-
] }, section.key);
|
|
1480
|
-
}),
|
|
1481
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "uix-emoji-picker__status", role: "status", children: status })
|
|
1482
|
-
] })
|
|
1483
|
-
] }) : null
|
|
907
|
+
// src/rich-text/pipeline.ts
|
|
908
|
+
var indentContinuation = (h) => {
|
|
909
|
+
let first = true;
|
|
910
|
+
return {
|
|
911
|
+
...h,
|
|
912
|
+
renderChildren: (nodes) => {
|
|
913
|
+
const out = h.renderChildren(nodes);
|
|
914
|
+
if (!first) return out;
|
|
915
|
+
first = false;
|
|
916
|
+
return out.split("\n").map((line, n) => n > 0 && line ? h.indent(line) : line).join("\n");
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
};
|
|
920
|
+
var listItemRender = import_extension_list.ListItem.config.renderMarkdown;
|
|
921
|
+
var UixListItem = import_extension_list.ListItem.extend({
|
|
922
|
+
renderMarkdown: (node, h, ctx) => listItemRender(node, indentContinuation(h), ctx)
|
|
923
|
+
});
|
|
924
|
+
var UixTaskItem = import_extension_list.TaskItem.extend({
|
|
925
|
+
renderMarkdown: (node, h) => {
|
|
926
|
+
const prefix = `- [${node.attrs?.checked ? "x" : " "}] `;
|
|
927
|
+
const [first, ...rest] = node.content ?? [];
|
|
928
|
+
if (first?.type !== "paragraph" || !(first.content ?? []).some((c) => c.type === "hardBreak")) {
|
|
929
|
+
return (0, import_core2.renderNestedMarkdownContent)(node, h, prefix);
|
|
930
|
+
}
|
|
931
|
+
const lines = [[]];
|
|
932
|
+
for (const child of first.content ?? []) {
|
|
933
|
+
if (child.type === "hardBreak") lines.push([]);
|
|
934
|
+
else lines[lines.length - 1].push(child);
|
|
935
|
+
}
|
|
936
|
+
const paragraphs = lines.filter((line) => line.length > 0).map((content) => ({ type: "paragraph", content }));
|
|
937
|
+
return (0, import_core2.renderNestedMarkdownContent)({ ...node, content: [...paragraphs, ...rest] }, h, prefix);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
function createSchemaExtensions(options = {}) {
|
|
941
|
+
const isSafeUrl = options.isSafeUrl ?? defaultIsSafeUrl;
|
|
942
|
+
return [
|
|
943
|
+
import_starter_kit.StarterKit.configure({
|
|
944
|
+
underline: false,
|
|
945
|
+
listItem: false,
|
|
946
|
+
heading: { levels: [...options.headingLevels ?? [1, 2, 3]] },
|
|
947
|
+
link: {
|
|
948
|
+
openOnClick: false,
|
|
949
|
+
autolink: true,
|
|
950
|
+
linkOnPaste: true,
|
|
951
|
+
defaultProtocol: "https",
|
|
952
|
+
// Unsafe links never become marks: pasted or typed, they stay text.
|
|
953
|
+
isAllowedUri: (url) => isSafeUrl(url),
|
|
954
|
+
shouldAutoLink: (url) => isSafeUrl(url),
|
|
955
|
+
HTMLAttributes: { rel: "noopener noreferrer nofollow", target: null }
|
|
1484
956
|
}
|
|
1485
|
-
)
|
|
1486
|
-
|
|
957
|
+
}),
|
|
958
|
+
UixListItem,
|
|
959
|
+
import_extension_list.TaskList,
|
|
960
|
+
UixTaskItem.configure({ nested: true }),
|
|
961
|
+
import_extension_table.TableKit.configure({ table: { resizable: false } }),
|
|
962
|
+
options.image ?? import_extension_image.Image.configure({ inline: true, allowBase64: false }),
|
|
963
|
+
// HAR-1599: `@[Name](mention:<id>)` is one token in every editor and headless pipeline.
|
|
964
|
+
Mention
|
|
965
|
+
];
|
|
1487
966
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
967
|
+
var ENTITY_AFTER_AMP = /^(?:#\d+;|#x[\da-f]+;|[a-z][a-z\d]*;)/i;
|
|
968
|
+
var TAG_START = /^[a-z/!?]$/i;
|
|
1490
969
|
var ASCII_PUNCT2 = /[!-/:-@[-`{-~]/;
|
|
1491
|
-
var
|
|
1492
|
-
var
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
var ENTITY_GLOBAL = /&(?:#\d{1,7}|#[xX][\da-fA-F]{1,6}|[a-zA-Z][a-zA-Z\d]{1,31});/g;
|
|
1507
|
-
var BARE_URL_RE = /^https?:\/\/[^\s<>()]+/;
|
|
1508
|
-
var BR_RE = /^<br\s*\/?>/i;
|
|
1509
|
-
var AUTOLINK_RE = /^<((?:https?:|mailto:)[^\s<>]*)>/i;
|
|
1510
|
-
var decodeEntity = (match) => {
|
|
1511
|
-
const [, dec, hex, name] = match;
|
|
1512
|
-
const code = dec ? Number(dec) : hex ? parseInt(hex, 16) : void 0;
|
|
1513
|
-
if (code !== void 0) return code > 0 && code <= 1114111 ? String.fromCodePoint(code) : "\uFFFD";
|
|
1514
|
-
return name && Object.prototype.hasOwnProperty.call(NAMED_ENTITIES, name) ? NAMED_ENTITIES[name] : null;
|
|
1515
|
-
};
|
|
1516
|
-
var MAX_SCAN = 2e3;
|
|
1517
|
-
var MAX_DEPTH = 12;
|
|
1518
|
-
var findBracketEnd = (text, open) => {
|
|
1519
|
-
let depth = 0;
|
|
1520
|
-
const stop = Math.min(text.length, open + MAX_SCAN);
|
|
1521
|
-
for (let i = open; i < stop; i += 1) {
|
|
1522
|
-
const ch = text[i];
|
|
1523
|
-
if (ch === "\\") {
|
|
1524
|
-
i += 1;
|
|
970
|
+
var LIST_MARKER = /^(?:\d+|[ivxlcdmIVXLCDM]+|[a-zA-Z]{1,2})$/;
|
|
971
|
+
var BACKSLASH = String.fromCharCode(92);
|
|
972
|
+
function encodeText(text, options = {}) {
|
|
973
|
+
let out = "";
|
|
974
|
+
const chars = Array.from(text);
|
|
975
|
+
let lineStart = 0;
|
|
976
|
+
let inLead = options.lineStart ?? true;
|
|
977
|
+
for (let i = 0; i < chars.length; i += 1) {
|
|
978
|
+
const ch = chars[i];
|
|
979
|
+
const prev = chars[i - 1] ?? "";
|
|
980
|
+
const next = chars[i + 1] ?? "";
|
|
981
|
+
if (ch === "\n") {
|
|
982
|
+
out += ch;
|
|
983
|
+
lineStart = i + 1;
|
|
984
|
+
inLead = true;
|
|
1525
985
|
continue;
|
|
1526
986
|
}
|
|
1527
|
-
if (ch === "
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
987
|
+
if (inLead && (ch === " " || ch === " ")) {
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
const atStart = inLead;
|
|
991
|
+
inLead = false;
|
|
992
|
+
const lineRest = chars.slice(i).join("").split("\n")[0];
|
|
993
|
+
const endOrSpace = next === "" || next === "\n" || /\s/u.test(next);
|
|
994
|
+
const markerBefore = !atStart && LIST_MARKER.test(out.slice(out.lastIndexOf("\n") + 1));
|
|
995
|
+
if (
|
|
996
|
+
// `#` always: marked ends a list item at a line starting with `#`, space or not.
|
|
997
|
+
atStart && (ch === "#" || /[-+*]/.test(ch) && endOrSpace) || atStart && /[-=*_]/.test(ch) && new RegExp(`^(?:\\${ch}[ \\t]*)+$`).test(lineRest) || markerBefore && /[.)]/.test(ch) && endOrSpace
|
|
998
|
+
) {
|
|
999
|
+
out += BACKSLASH + ch;
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
if (ch === "#" && options.headingEnd && (prev === "" || /\s/u.test(prev)) && /^#+$/.test(lineRest)) {
|
|
1003
|
+
out += BACKSLASH + ch;
|
|
1004
|
+
continue;
|
|
1005
|
+
}
|
|
1006
|
+
switch (ch) {
|
|
1007
|
+
case BACKSLASH:
|
|
1008
|
+
out += ASCII_PUNCT2.test(next) || next === "" ? BACKSLASH + BACKSLASH : BACKSLASH;
|
|
1009
|
+
break;
|
|
1010
|
+
case "`":
|
|
1011
|
+
case "[":
|
|
1012
|
+
case "]":
|
|
1013
|
+
case "~":
|
|
1014
|
+
out += BACKSLASH + ch;
|
|
1015
|
+
break;
|
|
1016
|
+
case "|":
|
|
1017
|
+
out += options.inTable ? BACKSLASH + ch : ch;
|
|
1018
|
+
break;
|
|
1019
|
+
case "&":
|
|
1020
|
+
out += ENTITY_AFTER_AMP.test(chars.slice(i + 1, i + 40).join("")) ? "&" : ch;
|
|
1021
|
+
break;
|
|
1022
|
+
case "<":
|
|
1023
|
+
out += TAG_START.test(next) ? "<" : ch;
|
|
1024
|
+
break;
|
|
1025
|
+
case "!":
|
|
1026
|
+
out += next === "" && options.beforeLink ? BACKSLASH + ch : ch;
|
|
1027
|
+
break;
|
|
1028
|
+
case "*":
|
|
1029
|
+
case "_": {
|
|
1030
|
+
const spaced = (prev === "" || /\s/u.test(prev)) && (next === "" || /\s/u.test(next));
|
|
1031
|
+
const intraword = ch === "_" && /[\p{L}\p{N}]/u.test(prev) && /[\p{L}\p{N}]/u.test(next);
|
|
1032
|
+
out += spaced || intraword ? ch : BACKSLASH + ch;
|
|
1033
|
+
break;
|
|
1532
1034
|
}
|
|
1035
|
+
case ">":
|
|
1036
|
+
out += atStart ? BACKSLASH + ch : ch;
|
|
1037
|
+
break;
|
|
1038
|
+
default:
|
|
1039
|
+
out += ch;
|
|
1533
1040
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1041
|
+
}
|
|
1042
|
+
return out;
|
|
1043
|
+
}
|
|
1044
|
+
function createMarked() {
|
|
1045
|
+
return new import_marked.Marked({
|
|
1046
|
+
gfm: true,
|
|
1047
|
+
tokenizer: {
|
|
1048
|
+
html: () => void 0,
|
|
1049
|
+
// `<br>` is the one tag read: GFM tables have no other way to hold a line break,
|
|
1050
|
+
// and Tiptap writes hard breaks in cells that way.
|
|
1051
|
+
tag: (src) => {
|
|
1052
|
+
const br = /^<br\s*\/?>/i.exec(src);
|
|
1053
|
+
return br ? { type: "br", raw: br[0] } : void 0;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
var hasLink = (node) => (node?.marks ?? []).some((m) => (typeof m === "string" ? m : m.type) === "link");
|
|
1059
|
+
function patchManager(manager) {
|
|
1060
|
+
const internals = manager;
|
|
1061
|
+
internals.encodeTextForMarkdown = function encode(text, node, parentNode) {
|
|
1062
|
+
const inCode = parentNode?.type != null && internals.codeTypes.has(parentNode.type) || (node.marks ?? []).some((m) => internals.codeTypes.has(typeof m === "string" ? m : m.type));
|
|
1063
|
+
if (inCode) return text;
|
|
1064
|
+
const siblings = parentNode?.content ?? [];
|
|
1065
|
+
const index = siblings.indexOf(node);
|
|
1066
|
+
const previous = index > 0 ? siblings[index - 1] : void 0;
|
|
1067
|
+
const next = index >= 0 ? siblings[index + 1] : void 0;
|
|
1068
|
+
return encodeText(text, {
|
|
1069
|
+
inTable: !!internals.uixInTable,
|
|
1070
|
+
beforeLink: hasLink(next) && !hasLink(node),
|
|
1071
|
+
// Unknown position (node not found among its siblings): assume a line start, the safe side.
|
|
1072
|
+
lineStart: index <= 0 || previous?.type === "hardBreak",
|
|
1073
|
+
headingEnd: parentNode?.type === "heading" && (index < 0 || index === siblings.length - 1)
|
|
1074
|
+
});
|
|
1075
|
+
};
|
|
1076
|
+
return manager;
|
|
1077
|
+
}
|
|
1078
|
+
function serializeNode(manager, node) {
|
|
1079
|
+
const internals = manager;
|
|
1080
|
+
internals.uixInTable = node.type === "table";
|
|
1081
|
+
try {
|
|
1082
|
+
return manager.serialize({ type: "doc", content: [node] }).replace(/^\n+|\n+$/g, "");
|
|
1083
|
+
} finally {
|
|
1084
|
+
internals.uixInTable = false;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function createManager(extensions) {
|
|
1088
|
+
return patchManager(new import_markdown.MarkdownManager({ marked: createMarked(), extensions }));
|
|
1089
|
+
}
|
|
1090
|
+
var SEPARATOR_TOKENS = /* @__PURE__ */ new Set(["space", "def"]);
|
|
1091
|
+
var nodeKey = (node) => JSON.stringify(node);
|
|
1092
|
+
var normalize = (schema, content) => {
|
|
1093
|
+
if (content.length === 0) return [];
|
|
1094
|
+
const blocks = [];
|
|
1095
|
+
for (const node of content) {
|
|
1096
|
+
if (node.type && schema.nodes[node.type]?.isInline) {
|
|
1097
|
+
const last = blocks[blocks.length - 1];
|
|
1098
|
+
if (last?.type === "paragraph" && last.wrapped) last.content.push(node);
|
|
1099
|
+
else blocks.push({ type: "paragraph", content: [node], wrapped: true });
|
|
1100
|
+
} else {
|
|
1101
|
+
blocks.push(node);
|
|
1538
1102
|
}
|
|
1539
1103
|
}
|
|
1540
|
-
|
|
1104
|
+
for (const block of blocks) delete block.wrapped;
|
|
1105
|
+
const doc = schema.nodeFromJSON({ type: "doc", content: blocks });
|
|
1106
|
+
doc.check();
|
|
1107
|
+
return doc.toJSON().content ?? [];
|
|
1541
1108
|
};
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1109
|
+
function readMarkdown(source, manager, schema) {
|
|
1110
|
+
const normalized = source.replace(/\r\n?/g, "\n");
|
|
1111
|
+
const lexed = manager.instance.lexer(normalized);
|
|
1112
|
+
const origin = [];
|
|
1113
|
+
for (let i = 0, o = 0; i <= normalized.length; i += 1, o += 1) {
|
|
1114
|
+
origin.push(o);
|
|
1115
|
+
if (source[o] === "\r" && source[o + 1] === "\n") o += 1;
|
|
1116
|
+
}
|
|
1117
|
+
let cursor = 0;
|
|
1118
|
+
const tokens = lexed.map((t) => {
|
|
1119
|
+
const start = origin[cursor];
|
|
1120
|
+
cursor += t.raw.length;
|
|
1121
|
+
return { type: t.type, raw: source.slice(start, origin[cursor]) };
|
|
1122
|
+
});
|
|
1123
|
+
const joined = lexed.map((t) => t.raw).join("") === normalized ? tokens.map((t) => t.raw).join("") : null;
|
|
1124
|
+
const blocks = [];
|
|
1125
|
+
const eol = /\r\n/.test(source) && !/(^|[^\r])\n/.test(source) ? "\r\n" : "\n";
|
|
1126
|
+
let lead = "";
|
|
1127
|
+
let defs = [];
|
|
1128
|
+
const keepInLead = (raw) => {
|
|
1129
|
+
lead += raw;
|
|
1130
|
+
const text = raw.replace(/\s+$/u, "").replace(/^\s+/u, "");
|
|
1131
|
+
if (text) defs.push(text);
|
|
1132
|
+
};
|
|
1133
|
+
const pushBlock = (raw) => {
|
|
1134
|
+
let nodes;
|
|
1135
|
+
try {
|
|
1136
|
+
nodes = normalize(schema, manager.parse(raw).content ?? []);
|
|
1137
|
+
} catch {
|
|
1138
|
+
nodes = normalize(schema, [{ type: "paragraph", content: [{ type: "text", text: raw }] }]);
|
|
1552
1139
|
}
|
|
1553
|
-
if (
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
if (depth === 0) {
|
|
1557
|
-
end = i;
|
|
1558
|
-
break;
|
|
1559
|
-
}
|
|
1140
|
+
if (nodes.length === 0) {
|
|
1141
|
+
keepInLead(raw);
|
|
1142
|
+
return;
|
|
1560
1143
|
}
|
|
1144
|
+
blocks.push({ raw, lead, nodes, keys: nodes.map(nodeKey), defs });
|
|
1145
|
+
lead = "";
|
|
1146
|
+
defs = [];
|
|
1147
|
+
};
|
|
1148
|
+
if (joined !== source) {
|
|
1149
|
+
const core = source.replace(/\s+$/u, "");
|
|
1150
|
+
lead = source.slice(0, source.length - source.trimStart().length);
|
|
1151
|
+
if (core.trim()) pushBlock(core.trimStart());
|
|
1152
|
+
const tail = source.slice(core.length);
|
|
1153
|
+
return { source, blocks, tail, tailDefs: [], eol, doc: { type: "doc", content: blocks.flatMap((b) => b.nodes) } };
|
|
1561
1154
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
const url = rawUrl.replace(/^<(.*)>$/, "$1").replace(/\\([!-/:-@[-`{-~])/g, "$1").replace(ENTITY_GLOBAL, (match) => decodeEntity(ENTITY_RE.exec(match)) ?? match);
|
|
1567
|
-
const title = titled ? titled[2] ?? titled[3] : void 0;
|
|
1568
|
-
return title === void 0 ? { url, end } : { url, title, end };
|
|
1569
|
-
};
|
|
1570
|
-
var backtickRun = (text, at) => {
|
|
1571
|
-
let end = at;
|
|
1572
|
-
while (text[end] === "`") end += 1;
|
|
1573
|
-
return text.slice(at, end);
|
|
1574
|
-
};
|
|
1575
|
-
var findCloser = (text, from, delim, misses) => {
|
|
1576
|
-
if (from >= (misses.get(delim) ?? Infinity)) return -1;
|
|
1577
|
-
for (let i = from; i < text.length; i += 1) {
|
|
1578
|
-
const ch = text[i];
|
|
1579
|
-
if (ch === "\\") {
|
|
1580
|
-
i += 1;
|
|
1155
|
+
for (const token of tokens) {
|
|
1156
|
+
if (SEPARATOR_TOKENS.has(token.type) || token.raw.trim() === "") {
|
|
1157
|
+
if (token.type === "def") keepInLead(token.raw);
|
|
1158
|
+
else lead += token.raw;
|
|
1581
1159
|
continue;
|
|
1582
1160
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
if (text.startsWith(delim, i) && i > from && !/\s/u.test(text[i - 1])) {
|
|
1592
|
-
if (delim[0] === "_" && WORD.test(text[i + delim.length] ?? "")) continue;
|
|
1593
|
-
if (delim.length === 1 && text[i + 1] === delim) {
|
|
1594
|
-
i += 1;
|
|
1595
|
-
continue;
|
|
1596
|
-
}
|
|
1597
|
-
return i;
|
|
1598
|
-
}
|
|
1161
|
+
const leading = /^(?:[ \t]*\r?\n)*/.exec(token.raw)[0];
|
|
1162
|
+
lead += leading;
|
|
1163
|
+
const body = token.raw.slice(leading.length);
|
|
1164
|
+
const core = body.replace(/\s+$/u, "");
|
|
1165
|
+
pushBlock(core);
|
|
1166
|
+
lead += body.slice(core.length);
|
|
1599
1167
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1168
|
+
return {
|
|
1169
|
+
source,
|
|
1170
|
+
blocks,
|
|
1171
|
+
tail: lead,
|
|
1172
|
+
tailDefs: defs,
|
|
1173
|
+
eol,
|
|
1174
|
+
doc: { type: "doc", content: blocks.flatMap((b) => b.nodes) }
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
var isEmptyParagraph = (node) => node.type === "paragraph" && !(node.content ?? []).some((c) => c.type !== "text" || (c.text ?? "").trim() !== "");
|
|
1178
|
+
var matchesAt = (children, at, block) => block.keys.every((key, k) => children[at + k] === key);
|
|
1179
|
+
var LOOKAHEAD = 8;
|
|
1180
|
+
function writeMarkdown(doc, origin, manager) {
|
|
1181
|
+
const content = [...doc.content ?? []];
|
|
1182
|
+
while (content.length > 0 && isEmptyParagraph(content[content.length - 1])) content.pop();
|
|
1183
|
+
if (content.length === 0) {
|
|
1184
|
+
return origin.blocks.length === 0 ? origin.source : "";
|
|
1614
1185
|
}
|
|
1615
|
-
const
|
|
1186
|
+
const keys = content.map(nodeKey);
|
|
1187
|
+
const { blocks, eol } = origin;
|
|
1188
|
+
const pieces = [];
|
|
1616
1189
|
let i = 0;
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1190
|
+
let j = 0;
|
|
1191
|
+
while (i < content.length) {
|
|
1192
|
+
if (j < blocks.length && matchesAt(keys, i, blocks[j])) {
|
|
1193
|
+
pieces.push({ text: blocks[j].raw, slot: j, edited: false });
|
|
1194
|
+
i += blocks[j].keys.length;
|
|
1195
|
+
j += 1;
|
|
1623
1196
|
continue;
|
|
1624
1197
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
i
|
|
1628
|
-
|
|
1198
|
+
let skip = 0;
|
|
1199
|
+
for (let d = 1; d <= LOOKAHEAD && j + d < blocks.length; d += 1) {
|
|
1200
|
+
if (matchesAt(keys, i, blocks[j + d])) {
|
|
1201
|
+
skip = d;
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1629
1204
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
i = close + run.length;
|
|
1638
|
-
continue;
|
|
1205
|
+
let insert = 0;
|
|
1206
|
+
if (j < blocks.length) {
|
|
1207
|
+
for (let d = 1; d <= LOOKAHEAD && i + d < content.length; d += 1) {
|
|
1208
|
+
if (matchesAt(keys, i + d, blocks[j])) {
|
|
1209
|
+
insert = d;
|
|
1210
|
+
break;
|
|
1211
|
+
}
|
|
1639
1212
|
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1213
|
+
}
|
|
1214
|
+
if (skip && (!insert || skip <= insert)) {
|
|
1215
|
+
j += skip;
|
|
1642
1216
|
continue;
|
|
1643
1217
|
}
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1218
|
+
let text = serializeNode(manager, content[i]);
|
|
1219
|
+
if (eol !== "\n") text = text.replace(/\n/g, eol);
|
|
1220
|
+
const inPlace = !insert && j < blocks.length && blocks[j].keys.length === 1;
|
|
1221
|
+
pieces.push(inPlace ? { text, slot: j, edited: true } : { text, edited: true });
|
|
1222
|
+
i += 1;
|
|
1223
|
+
if (!insert && j < blocks.length) j += 1;
|
|
1224
|
+
}
|
|
1225
|
+
const blank = eol + eol;
|
|
1226
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
1227
|
+
let out = "";
|
|
1228
|
+
pieces.forEach((piece, n) => {
|
|
1229
|
+
const prev = pieces[n - 1];
|
|
1230
|
+
const slot = piece.slot;
|
|
1231
|
+
const lead = slot !== void 0 ? blocks[slot].lead : "";
|
|
1232
|
+
if (n === 0) {
|
|
1233
|
+
if (slot === 0) {
|
|
1234
|
+
out += lead;
|
|
1235
|
+
emitted.add(0);
|
|
1652
1236
|
}
|
|
1237
|
+
} else if (slot !== void 0 && prev?.slot === slot - 1 && // Next to a re-serialized block, only a separator with a blank line is safe.
|
|
1238
|
+
(!(piece.edited || prev.edited) || /\n[ \t]*\r?\n/.test(lead))) {
|
|
1239
|
+
out += lead;
|
|
1240
|
+
emitted.add(slot);
|
|
1241
|
+
} else {
|
|
1242
|
+
out += blank;
|
|
1653
1243
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1244
|
+
out += piece.text;
|
|
1245
|
+
});
|
|
1246
|
+
const last = pieces[pieces.length - 1];
|
|
1247
|
+
const tailKept = last.slot === blocks.length - 1;
|
|
1248
|
+
const lost = [
|
|
1249
|
+
...blocks.flatMap((block, n) => emitted.has(n) ? [] : block.defs),
|
|
1250
|
+
...tailKept ? [] : origin.tailDefs
|
|
1251
|
+
];
|
|
1252
|
+
if (lost.length) out += blank + lost.join(eol);
|
|
1253
|
+
if (tailKept) out += origin.tail;
|
|
1254
|
+
else if (/\n$/.test(origin.tail)) out += eol;
|
|
1255
|
+
return out;
|
|
1256
|
+
}
|
|
1257
|
+
function createPipeline(options = {}) {
|
|
1258
|
+
const extensions = createSchemaExtensions(options);
|
|
1259
|
+
const schema = (0, import_core2.getSchema)(extensions);
|
|
1260
|
+
const manager = createManager(extensions);
|
|
1261
|
+
return {
|
|
1262
|
+
extensions,
|
|
1263
|
+
schema,
|
|
1264
|
+
manager,
|
|
1265
|
+
read: (source) => readMarkdown(source, manager, schema),
|
|
1266
|
+
write: (doc, origin) => writeMarkdown(doc, origin, manager)
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// src/components/Composer.tsx
|
|
1271
|
+
var import_react2 = require("react");
|
|
1272
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1273
|
+
function Composer({ children, className, label = "Add a comment", ...props }) {
|
|
1274
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cx("uix-composer", className), ...props, children: import_react2.Children.map(children, (child) => {
|
|
1275
|
+
if (!(0, import_react2.isValidElement)(child) || child.type !== "textarea") return child;
|
|
1276
|
+
const p = child.props;
|
|
1277
|
+
return p["aria-label"] || p["aria-labelledby"] || p.id ? child : (0, import_react2.cloneElement)(child, { "aria-label": label });
|
|
1278
|
+
}) });
|
|
1279
|
+
}
|
|
1280
|
+
function ComposerBar({ children, className, ...props }) {
|
|
1281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cx("uix-composer__bar", className), ...props, children });
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// src/components/EditorIcons.tsx
|
|
1285
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1286
|
+
var PATHS = {
|
|
1287
|
+
heading1: "M4 12h8M4 18V6M12 18V6M17 12l3-2v8",
|
|
1288
|
+
heading2: "M4 12h8M4 18V6M12 18V6M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1",
|
|
1289
|
+
heading3: "M4 12h8M4 18V6M12 18V6M17.5 10.5c1.7-1 3.5 0 3.5 1.5a2 2 0 0 1-2 2M17 17.5c2 1.5 4 .3 4-1.5a2 2 0 0 0-2-2",
|
|
1290
|
+
bold: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8",
|
|
1291
|
+
italic: "M19 4h-9M14 20H5M15 4 9 20",
|
|
1292
|
+
strike: "M16 4H9a3 3 0 0 0-2.83 4M14 12a4 4 0 0 1 0 8H6M4 12h16",
|
|
1293
|
+
code: "m16 18 6-6-6-6M8 6l-6 6 6 6",
|
|
1294
|
+
bulletList: "M3 12h.01M3 18h.01M3 6h.01M8 12h13M8 18h13M8 6h13",
|
|
1295
|
+
orderedList: "M10 12h11M10 18h11M10 6h11M4 10h2M4 6h1v4M6 18H4c0-1 2-2 2-3s-1-1.5-2-1",
|
|
1296
|
+
taskList: "m3 17 2 2 4-4M3 7l2 2 4-4M13 6h8M13 12h8M13 18h8",
|
|
1297
|
+
blockquote: "M17 6H3M21 12H8M21 18H8M3 12v6",
|
|
1298
|
+
codeBlock: "m10 9-3 3 3 3M14 15l3-3-3-3M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2Z",
|
|
1299
|
+
horizontalRule: "M5 12h14",
|
|
1300
|
+
link: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
|
|
1301
|
+
table: "M12 3v18M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2ZM3 9h18M3 15h18",
|
|
1302
|
+
tableAddRow: "M4 3h16a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM12 15v6M9 18h6",
|
|
1303
|
+
tableAddColumn: "M4 3h6a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM18 9v6M15 12h6",
|
|
1304
|
+
tableDeleteRow: "M4 3h16a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM9 18h6",
|
|
1305
|
+
tableDeleteColumn: "M4 3h6a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1ZM15 12h6",
|
|
1306
|
+
trash: "M3 6h18M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",
|
|
1307
|
+
image: "M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2ZM9 7a2 2 0 1 0 0 4 2 2 0 0 0 0-4ZM21 15l-3.09-3.09a2 2 0 0 0-2.82 0L6 21",
|
|
1308
|
+
emoji: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01",
|
|
1309
|
+
addReaction: "M22 11v1a10 10 0 1 1-9-10M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01M16 5h6M19 2v6",
|
|
1310
|
+
undo: "M9 14 4 9l5-5M4 9h10.5a5.5 5.5 0 0 1 0 11H11",
|
|
1311
|
+
redo: "m15 14 5-5-5-5M20 9H9.5a5.5 5.5 0 0 0 0 11H13",
|
|
1312
|
+
sourceMode: "M10 12.5 8 15l2 2.5M14 12.5l2 2.5-2 2.5M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",
|
|
1313
|
+
richMode: "M14 2v4a2 2 0 0 0 2 2h4M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7ZM8 13h8M8 17h5",
|
|
1314
|
+
clock: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20ZM12 6v6l4 2",
|
|
1315
|
+
grip: "M9 5h.01M9 12h.01M9 19h.01M15 5h.01M15 12h.01M15 19h.01",
|
|
1316
|
+
more: "M5 12h.01M12 12h.01M19 12h.01"
|
|
1317
|
+
};
|
|
1318
|
+
function EditorIcon({ name, ...props }) {
|
|
1319
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1320
|
+
"svg",
|
|
1321
|
+
{
|
|
1322
|
+
viewBox: "0 0 24 24",
|
|
1323
|
+
fill: "none",
|
|
1324
|
+
stroke: "currentColor",
|
|
1325
|
+
strokeWidth: 2,
|
|
1326
|
+
strokeLinecap: "round",
|
|
1327
|
+
strokeLinejoin: "round",
|
|
1328
|
+
"aria-hidden": "true",
|
|
1329
|
+
focusable: "false",
|
|
1330
|
+
...props,
|
|
1331
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: PATHS[name] })
|
|
1663
1332
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/components/EmojiPicker.tsx
|
|
1337
|
+
var import_react5 = require("react");
|
|
1338
|
+
|
|
1339
|
+
// src/components/Popover.tsx
|
|
1340
|
+
var import_react4 = require("react");
|
|
1341
|
+
|
|
1342
|
+
// src/hooks/useAnchoredPosition.ts
|
|
1343
|
+
var import_react3 = require("react");
|
|
1344
|
+
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? import_react3.useLayoutEffect : import_react3.useEffect;
|
|
1345
|
+
var resolveAnchor = (a) => a && typeof a === "object" && "current" in a ? a.current : a ?? null;
|
|
1346
|
+
var anchoringSupported;
|
|
1347
|
+
function supportsAnchoring() {
|
|
1348
|
+
if (anchoringSupported === void 0) {
|
|
1349
|
+
try {
|
|
1350
|
+
const css = typeof CSS !== "undefined" ? CSS : void 0;
|
|
1351
|
+
anchoringSupported = !!css && typeof css.supports === "function" && css.supports("anchor-name", "--uix-a") && css.supports("position-anchor", "--uix-a") && css.supports("top", "round(anchor(bottom) + 1px, 1px)");
|
|
1352
|
+
} catch {
|
|
1353
|
+
anchoringSupported = false;
|
|
1677
1354
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1355
|
+
}
|
|
1356
|
+
return anchoringSupported;
|
|
1357
|
+
}
|
|
1358
|
+
var ANCHOR_BAND = 64;
|
|
1359
|
+
var anchorCount = 0;
|
|
1360
|
+
var namesOf = (value) => value.split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
|
|
1361
|
+
function addAnchorName(el, name) {
|
|
1362
|
+
const names = namesOf(getComputedStyle(el).getPropertyValue("anchor-name"));
|
|
1363
|
+
if (!names.includes(name)) names.push(name);
|
|
1364
|
+
el.style.setProperty("anchor-name", names.join(", "));
|
|
1365
|
+
}
|
|
1366
|
+
function removeAnchorName(el, name) {
|
|
1367
|
+
const names = namesOf(el.style.getPropertyValue("anchor-name")).filter((n) => n !== name);
|
|
1368
|
+
if (names.length) el.style.setProperty("anchor-name", names.join(", "));
|
|
1369
|
+
else el.style.removeProperty("anchor-name");
|
|
1370
|
+
}
|
|
1371
|
+
function translationOf(el) {
|
|
1372
|
+
const t = getComputedStyle(el).transform;
|
|
1373
|
+
if (!t || t === "none" || typeof DOMMatrixReadOnly === "undefined") return { x: 0, y: 0 };
|
|
1374
|
+
try {
|
|
1375
|
+
const m = new DOMMatrixReadOnly(t);
|
|
1376
|
+
return { x: m.m41, y: m.m42 };
|
|
1377
|
+
} catch {
|
|
1378
|
+
return { x: 0, y: 0 };
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
var signed = (n) => `${n < 0 ? "-" : "+"} ${Math.round(Math.abs(n) * 100) / 100}px`;
|
|
1382
|
+
var prepared = /* @__PURE__ */ new WeakSet();
|
|
1383
|
+
function useAnchoredPosition(anchor, floatingRef, { open, placement = "bottom-start", offset = 6, padding = 8, shiftMainAxis = true, capHeight = false, onAnchorHidden }) {
|
|
1384
|
+
const opts = (0, import_react3.useRef)({ placement, offset, padding, shiftMainAxis, capHeight });
|
|
1385
|
+
opts.current = { placement, offset, padding, shiftMainAxis, capHeight };
|
|
1386
|
+
const onAnchorHiddenRef = (0, import_react3.useRef)(onAnchorHidden);
|
|
1387
|
+
onAnchorHiddenRef.current = onAnchorHidden;
|
|
1388
|
+
const heightCap = (0, import_react3.useRef)(null);
|
|
1389
|
+
const session = (0, import_react3.useRef)(false);
|
|
1390
|
+
const side = (0, import_react3.useRef)(null);
|
|
1391
|
+
const written = (0, import_react3.useRef)("");
|
|
1392
|
+
const anchoring = (0, import_react3.useRef)(null);
|
|
1393
|
+
const anchoringFailed = (0, import_react3.useRef)(false);
|
|
1394
|
+
const anchorName = (0, import_react3.useRef)("");
|
|
1395
|
+
const release = (0, import_react3.useCallback)((keepPlace) => {
|
|
1396
|
+
const current = anchoring.current;
|
|
1397
|
+
if (!current) return;
|
|
1398
|
+
anchoring.current = null;
|
|
1399
|
+
written.current = "";
|
|
1400
|
+
const floating = floatingRef.current;
|
|
1401
|
+
if (floating) {
|
|
1402
|
+
const s = floating.style;
|
|
1403
|
+
if (keepPlace && current.anchor.isConnected) {
|
|
1404
|
+
const a = current.anchor.getBoundingClientRect();
|
|
1405
|
+
s.top = `${Math.round((current.yEdge === "bottom" ? a.bottom : a.top) + current.dy)}px`;
|
|
1406
|
+
s.left = `${Math.round((current.xEdge === "right" ? a.right : a.left) + current.dx)}px`;
|
|
1685
1407
|
}
|
|
1408
|
+
s.removeProperty("position-anchor");
|
|
1686
1409
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1410
|
+
removeAnchorName(current.anchor, current.name);
|
|
1411
|
+
}, [floatingRef]);
|
|
1412
|
+
const uncap = (0, import_react3.useCallback)(() => {
|
|
1413
|
+
const cap = heightCap.current;
|
|
1414
|
+
if (!cap) return;
|
|
1415
|
+
heightCap.current = null;
|
|
1416
|
+
const floating = floatingRef.current;
|
|
1417
|
+
if (!floating) return;
|
|
1418
|
+
floating.style.maxHeight = cap.inlineMaxHeight;
|
|
1419
|
+
if (cap.setOverflow) floating.style.overflowY = cap.inlineOverflowY;
|
|
1420
|
+
}, [floatingRef]);
|
|
1421
|
+
const reposition = (0, import_react3.useCallback)(() => {
|
|
1422
|
+
const anchorEl = resolveAnchor(anchor);
|
|
1423
|
+
const floating = floatingRef.current;
|
|
1424
|
+
if (!anchorEl || !floating) return;
|
|
1425
|
+
const s = floating.style;
|
|
1426
|
+
if (!prepared.has(floating)) {
|
|
1427
|
+
s.position = "fixed";
|
|
1428
|
+
s.inset = "auto";
|
|
1429
|
+
s.margin = "0";
|
|
1430
|
+
prepared.add(floating);
|
|
1431
|
+
written.current = "";
|
|
1694
1432
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1433
|
+
const { placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, capHeight: cap } = opts.current;
|
|
1434
|
+
if (cap) {
|
|
1435
|
+
if (!heightCap.current) {
|
|
1436
|
+
const own = getComputedStyle(floating);
|
|
1437
|
+
const setOverflow = !own.overflowY || own.overflowY === "visible";
|
|
1438
|
+
heightCap.current = {
|
|
1439
|
+
inlineMaxHeight: s.maxHeight,
|
|
1440
|
+
inlineOverflowY: s.overflowY,
|
|
1441
|
+
ownMaxHeight: own.maxHeight && own.maxHeight !== "none" ? own.maxHeight : "",
|
|
1442
|
+
setOverflow,
|
|
1443
|
+
written: ""
|
|
1444
|
+
};
|
|
1445
|
+
if (setOverflow) s.overflowY = "auto";
|
|
1708
1446
|
}
|
|
1709
|
-
const
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
if (ch === "h" && !WORD.test(text[i - 1] ?? "")) {
|
|
1715
|
-
const bare = BARE_URL_RE.exec(rest);
|
|
1716
|
-
if (bare) {
|
|
1717
|
-
const url = bare[0].replace(/[.,;:!?'"]+$/, "");
|
|
1718
|
-
out.push({ type: "link", href: url, children: [{ type: "text", value: url }], bare: true });
|
|
1719
|
-
i += url.length;
|
|
1720
|
-
continue;
|
|
1447
|
+
const room = `${Math.max(0, window.innerHeight - 2 * pad)}px`;
|
|
1448
|
+
const max = heightCap.current.ownMaxHeight ? `min(${room}, ${heightCap.current.ownMaxHeight})` : room;
|
|
1449
|
+
if (max !== heightCap.current.written) {
|
|
1450
|
+
s.maxHeight = max;
|
|
1451
|
+
heightCap.current.written = max;
|
|
1721
1452
|
}
|
|
1453
|
+
} else if (heightCap.current) {
|
|
1454
|
+
uncap();
|
|
1722
1455
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
var DELIMITER_ROW_RE = /^\s*\|?\s*:?-+:?\s*(?:\|\s*:?-+:?\s*)*\|?\s*$/;
|
|
1750
|
-
var indentOf = (s) => s.replace(/\t/g, " ").length;
|
|
1751
|
-
function splitTableRow(line) {
|
|
1752
|
-
let row = line.trim();
|
|
1753
|
-
if (row.startsWith("|")) row = row.slice(1);
|
|
1754
|
-
if (row.endsWith("|") && !row.endsWith("\\|")) row = row.slice(0, -1);
|
|
1755
|
-
const cells = [];
|
|
1756
|
-
let cell = "";
|
|
1757
|
-
let inCode = false;
|
|
1758
|
-
for (let i = 0; i < row.length; i += 1) {
|
|
1759
|
-
const ch = row[i];
|
|
1760
|
-
if (ch === "\\" && row[i + 1] === "|") {
|
|
1761
|
-
cell += "|";
|
|
1762
|
-
i += 1;
|
|
1763
|
-
continue;
|
|
1456
|
+
const a = anchorEl.getBoundingClientRect();
|
|
1457
|
+
const size = { width: floating.offsetWidth, height: floating.offsetHeight };
|
|
1458
|
+
const pos = computePosition(
|
|
1459
|
+
{ x: a.x, y: a.y, width: a.width, height: a.height },
|
|
1460
|
+
size,
|
|
1461
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
1462
|
+
{ placement: p, offset: o, padding: pad, shiftMainAxis: keepInView, stickySide: side.current ?? void 0 }
|
|
1463
|
+
);
|
|
1464
|
+
const measured = size.width > 0 || size.height > 0;
|
|
1465
|
+
if (measured && session.current) side.current = pos.side;
|
|
1466
|
+
if (floating.dataset.placement !== pos.placement) floating.dataset.placement = pos.placement;
|
|
1467
|
+
const writeFixed = (at) => {
|
|
1468
|
+
const left2 = `${Math.round(at.x)}px`;
|
|
1469
|
+
const top2 = `${Math.round(at.y)}px`;
|
|
1470
|
+
const key2 = `fixed ${left2} ${top2}`;
|
|
1471
|
+
if (key2 === written.current) return;
|
|
1472
|
+
s.left = left2;
|
|
1473
|
+
s.top = top2;
|
|
1474
|
+
written.current = key2;
|
|
1475
|
+
};
|
|
1476
|
+
const limit = pos.mainAxisRange;
|
|
1477
|
+
const nearLimit = limit !== null && (pos.mainAxisNatural < limit[0] + ANCHOR_BAND || pos.mainAxisNatural > limit[1] - ANCHOR_BAND);
|
|
1478
|
+
if (anchoringFailed.current || nearLimit || !supportsAnchoring()) {
|
|
1479
|
+
if (anchoring.current) release(false);
|
|
1480
|
+
writeFixed(pos);
|
|
1481
|
+
return;
|
|
1764
1482
|
}
|
|
1765
|
-
if (
|
|
1766
|
-
if (
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1483
|
+
if (anchoring.current && anchoring.current.anchor !== anchorEl) release(false);
|
|
1484
|
+
if (!anchoring.current) {
|
|
1485
|
+
anchorName.current || (anchorName.current = `--uix-anchor-${++anchorCount}`);
|
|
1486
|
+
addAnchorName(anchorEl, anchorName.current);
|
|
1487
|
+
s.setProperty("position-anchor", anchorName.current);
|
|
1488
|
+
anchoring.current = { anchor: anchorEl, name: anchorName.current, yEdge: "bottom", xEdge: "left", dy: 0, dx: 0, verified: false };
|
|
1489
|
+
written.current = "";
|
|
1770
1490
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1491
|
+
const current = anchoring.current;
|
|
1492
|
+
current.yEdge = pos.side === "bottom" ? "bottom" : "top";
|
|
1493
|
+
current.xEdge = pos.side === "right" ? "right" : "left";
|
|
1494
|
+
current.dy = pos.y - (current.yEdge === "bottom" ? a.bottom : a.top);
|
|
1495
|
+
current.dx = pos.x - (current.xEdge === "right" ? a.right : a.left);
|
|
1496
|
+
const top = `round(anchor(${current.yEdge}) ${signed(current.dy)}, 1px)`;
|
|
1497
|
+
const left = `round(anchor(${current.xEdge}) ${signed(current.dx)}, 1px)`;
|
|
1498
|
+
const key = `anchored ${top} ${left}`;
|
|
1499
|
+
if (key !== written.current) {
|
|
1500
|
+
s.top = top;
|
|
1501
|
+
s.left = left;
|
|
1502
|
+
written.current = key;
|
|
1503
|
+
}
|
|
1504
|
+
if (!current.verified && measured) {
|
|
1505
|
+
current.verified = true;
|
|
1506
|
+
const box = floating.getBoundingClientRect();
|
|
1507
|
+
const t = translationOf(floating);
|
|
1508
|
+
const dxc = box.left + box.width / 2 - t.x - (pos.x + size.width / 2);
|
|
1509
|
+
const dyc = box.top + box.height / 2 - t.y - (pos.y + size.height / 2);
|
|
1510
|
+
if (Math.abs(dxc) > 2 || Math.abs(dyc) > 2) {
|
|
1511
|
+
anchoringFailed.current = true;
|
|
1512
|
+
release(false);
|
|
1513
|
+
writeFixed(pos);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}, [anchor, floatingRef, release, uncap]);
|
|
1517
|
+
useIsomorphicLayoutEffect(() => {
|
|
1518
|
+
if (!open) return;
|
|
1519
|
+
session.current = true;
|
|
1520
|
+
reposition();
|
|
1521
|
+
const anchorEl = resolveAnchor(anchor);
|
|
1522
|
+
const floating = floatingRef.current;
|
|
1523
|
+
const onScroll = (event) => {
|
|
1524
|
+
const target = event.target;
|
|
1525
|
+
if (target && typeof target.contains === "function") {
|
|
1526
|
+
if (floating?.contains(target)) return;
|
|
1527
|
+
if (anchorEl && !target.contains(anchorEl)) return;
|
|
1528
|
+
}
|
|
1529
|
+
reposition();
|
|
1530
|
+
};
|
|
1531
|
+
const onResize = () => reposition();
|
|
1532
|
+
window.addEventListener("scroll", onScroll, { passive: true, capture: true });
|
|
1533
|
+
window.addEventListener("resize", onResize);
|
|
1534
|
+
const observer = typeof ResizeObserver === "function" ? new ResizeObserver(() => reposition()) : null;
|
|
1535
|
+
if (floating) observer?.observe(floating);
|
|
1536
|
+
if (anchorEl) observer?.observe(anchorEl);
|
|
1537
|
+
return () => {
|
|
1538
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
1539
|
+
window.removeEventListener("resize", onResize);
|
|
1540
|
+
observer?.disconnect();
|
|
1541
|
+
session.current = false;
|
|
1542
|
+
side.current = null;
|
|
1543
|
+
anchoringFailed.current = false;
|
|
1544
|
+
release(true);
|
|
1545
|
+
uncap();
|
|
1546
|
+
};
|
|
1547
|
+
}, [open, reposition, anchor, floatingRef, release, uncap]);
|
|
1548
|
+
const watchAnchor = open && !!onAnchorHidden;
|
|
1549
|
+
(0, import_react3.useEffect)(() => {
|
|
1550
|
+
const anchorEl = resolveAnchor(anchor);
|
|
1551
|
+
if (!watchAnchor || !anchorEl || typeof IntersectionObserver !== "function") return;
|
|
1552
|
+
const observer = new IntersectionObserver((entries) => {
|
|
1553
|
+
const latest = entries[entries.length - 1];
|
|
1554
|
+
if (latest && !latest.isIntersecting) onAnchorHiddenRef.current?.();
|
|
1555
|
+
});
|
|
1556
|
+
observer.observe(anchorEl);
|
|
1557
|
+
return () => observer.disconnect();
|
|
1558
|
+
}, [watchAnchor, anchor]);
|
|
1559
|
+
(0, import_react3.useEffect)(() => () => release(false), [release]);
|
|
1560
|
+
return reposition;
|
|
1775
1561
|
}
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
return null;
|
|
1562
|
+
|
|
1563
|
+
// src/components/Popover.tsx
|
|
1564
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1565
|
+
var useIsomorphicLayoutEffect2 = typeof document !== "undefined" ? import_react4.useLayoutEffect : import_react4.useEffect;
|
|
1566
|
+
var PLACING = "data-uix-placing";
|
|
1567
|
+
var isPopoverOpen = (el) => {
|
|
1568
|
+
try {
|
|
1569
|
+
return el.matches(":popover-open");
|
|
1570
|
+
} catch {
|
|
1571
|
+
return false;
|
|
1572
|
+
}
|
|
1788
1573
|
};
|
|
1789
|
-
var
|
|
1790
|
-
|
|
1791
|
-
|
|
1574
|
+
var settle = (el) => {
|
|
1575
|
+
if (!el.hasAttribute(PLACING)) return;
|
|
1576
|
+
getComputedStyle(el).getPropertyValue("transform");
|
|
1577
|
+
el.removeAttribute(PLACING);
|
|
1792
1578
|
};
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1579
|
+
function Popover({
|
|
1580
|
+
popover: popoverProp,
|
|
1581
|
+
anchor,
|
|
1582
|
+
placement = "bottom-start",
|
|
1583
|
+
offset = 6,
|
|
1584
|
+
closeWhenAnchorHidden = false,
|
|
1585
|
+
onAnchorHidden,
|
|
1586
|
+
capHeight = false,
|
|
1587
|
+
openOnHover = false,
|
|
1588
|
+
className,
|
|
1589
|
+
children,
|
|
1590
|
+
...props
|
|
1591
|
+
}) {
|
|
1592
|
+
const hover = !!openOnHover && !!anchor;
|
|
1593
|
+
const popover = popoverProp ?? (hover ? "manual" : "auto");
|
|
1594
|
+
const openDelay = typeof openOnHover === "object" ? openOnHover.openDelay ?? 300 : 300;
|
|
1595
|
+
const closeDelay = typeof openOnHover === "object" ? openOnHover.closeDelay ?? 150 : 150;
|
|
1596
|
+
const ref = (0, import_react4.useRef)(null);
|
|
1597
|
+
const [open, setOpen] = (0, import_react4.useState)(false);
|
|
1598
|
+
const openRef = (0, import_react4.useRef)(false);
|
|
1599
|
+
const onAnchorHiddenRef = (0, import_react4.useRef)(onAnchorHidden);
|
|
1600
|
+
onAnchorHiddenRef.current = onAnchorHidden;
|
|
1601
|
+
const popoverAttr = { popover };
|
|
1602
|
+
const reposition = useAnchoredPosition(anchor ?? null, ref, {
|
|
1603
|
+
open: open && !!anchor,
|
|
1604
|
+
placement,
|
|
1605
|
+
offset,
|
|
1606
|
+
capHeight,
|
|
1607
|
+
onAnchorHidden: closeWhenAnchorHidden ? () => {
|
|
1608
|
+
const el = ref.current;
|
|
1609
|
+
if (!el || !isPopoverOpen(el)) return;
|
|
1610
|
+
onAnchorHiddenRef.current?.();
|
|
1611
|
+
const active = el.ownerDocument.activeElement;
|
|
1612
|
+
if (active && el.contains(active)) active.blur();
|
|
1613
|
+
el.hidePopover();
|
|
1614
|
+
} : void 0
|
|
1615
|
+
});
|
|
1616
|
+
(0, import_react4.useEffect)(() => {
|
|
1617
|
+
const el = ref.current;
|
|
1618
|
+
if (!el || !anchor) return;
|
|
1619
|
+
const hold = (event) => {
|
|
1620
|
+
if (event.newState !== "open") return;
|
|
1621
|
+
el.setAttribute(PLACING, "");
|
|
1622
|
+
window.setTimeout(() => settle(el), 150);
|
|
1623
|
+
};
|
|
1624
|
+
const sync = () => {
|
|
1625
|
+
if (!isPopoverOpen(el)) {
|
|
1626
|
+
el.removeAttribute(PLACING);
|
|
1627
|
+
setOpen(false);
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
reposition();
|
|
1631
|
+
window.setTimeout(() => {
|
|
1632
|
+
if (!isPopoverOpen(el)) return;
|
|
1633
|
+
if (!openRef.current) {
|
|
1634
|
+
setOpen(true);
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
reposition();
|
|
1638
|
+
settle(el);
|
|
1639
|
+
}, 0);
|
|
1640
|
+
};
|
|
1641
|
+
el.addEventListener("beforetoggle", hold);
|
|
1642
|
+
el.addEventListener("toggle", sync);
|
|
1643
|
+
return () => {
|
|
1644
|
+
el.removeEventListener("beforetoggle", hold);
|
|
1645
|
+
el.removeEventListener("toggle", sync);
|
|
1646
|
+
el.removeAttribute(PLACING);
|
|
1647
|
+
};
|
|
1648
|
+
}, [anchor, reposition]);
|
|
1649
|
+
(0, import_react4.useEffect)(() => {
|
|
1650
|
+
const el = ref.current;
|
|
1651
|
+
const anchorEl = resolveAnchor(anchor);
|
|
1652
|
+
if (!hover || !el || !anchorEl) return;
|
|
1653
|
+
let timer;
|
|
1654
|
+
const inside = (node) => !!node && (anchorEl.contains(node) || el.contains(node));
|
|
1655
|
+
const stillWanted = () => {
|
|
1656
|
+
try {
|
|
1657
|
+
return anchorEl.matches(":hover") || el.matches(":hover") || inside(el.ownerDocument.activeElement);
|
|
1658
|
+
} catch {
|
|
1659
|
+
return false;
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
const show = (delay) => {
|
|
1663
|
+
window.clearTimeout(timer);
|
|
1664
|
+
timer = window.setTimeout(() => {
|
|
1665
|
+
if (!isPopoverOpen(el)) {
|
|
1666
|
+
try {
|
|
1667
|
+
el.showPopover?.();
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}, delay);
|
|
1672
|
+
};
|
|
1673
|
+
const hide = (delay) => {
|
|
1674
|
+
window.clearTimeout(timer);
|
|
1675
|
+
timer = window.setTimeout(() => {
|
|
1676
|
+
if (isPopoverOpen(el) && !stillWanted()) {
|
|
1677
|
+
try {
|
|
1678
|
+
el.hidePopover?.();
|
|
1679
|
+
} catch {
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}, delay);
|
|
1683
|
+
};
|
|
1684
|
+
const onEnter = (event) => {
|
|
1685
|
+
if (event.pointerType !== "touch") show(openDelay);
|
|
1686
|
+
};
|
|
1687
|
+
const onLeave = (event) => {
|
|
1688
|
+
if (event.pointerType !== "touch") hide(closeDelay);
|
|
1689
|
+
};
|
|
1690
|
+
const onFocusIn = () => show(0);
|
|
1691
|
+
const onFocusOut = (event) => {
|
|
1692
|
+
if (!inside(event.relatedTarget)) hide(closeDelay);
|
|
1693
|
+
};
|
|
1694
|
+
const onKeyDown = (event) => {
|
|
1695
|
+
if (event.key !== "Escape" || !isPopoverOpen(el)) return;
|
|
1696
|
+
window.clearTimeout(timer);
|
|
1697
|
+
const hadFocus = el.contains(el.ownerDocument.activeElement);
|
|
1698
|
+
try {
|
|
1699
|
+
el.hidePopover?.();
|
|
1700
|
+
} catch {
|
|
1814
1701
|
}
|
|
1815
|
-
|
|
1816
|
-
|
|
1702
|
+
if (hadFocus) {
|
|
1703
|
+
anchorEl.removeEventListener("focusin", onFocusIn);
|
|
1704
|
+
anchorEl.focus();
|
|
1705
|
+
anchorEl.addEventListener("focusin", onFocusIn);
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1708
|
+
for (const node of [anchorEl, el]) {
|
|
1709
|
+
node.addEventListener("pointerenter", onEnter);
|
|
1710
|
+
node.addEventListener("pointerleave", onLeave);
|
|
1711
|
+
node.addEventListener("focusout", onFocusOut);
|
|
1712
|
+
node.addEventListener("keydown", onKeyDown);
|
|
1817
1713
|
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1714
|
+
anchorEl.addEventListener("focusin", onFocusIn);
|
|
1715
|
+
return () => {
|
|
1716
|
+
window.clearTimeout(timer);
|
|
1717
|
+
for (const node of [anchorEl, el]) {
|
|
1718
|
+
node.removeEventListener("pointerenter", onEnter);
|
|
1719
|
+
node.removeEventListener("pointerleave", onLeave);
|
|
1720
|
+
node.removeEventListener("focusout", onFocusOut);
|
|
1721
|
+
node.removeEventListener("keydown", onKeyDown);
|
|
1722
|
+
}
|
|
1723
|
+
anchorEl.removeEventListener("focusin", onFocusIn);
|
|
1724
|
+
};
|
|
1725
|
+
}, [hover, anchor, openDelay, closeDelay]);
|
|
1726
|
+
useIsomorphicLayoutEffect2(() => {
|
|
1727
|
+
openRef.current = open;
|
|
1728
|
+
if (open && ref.current) settle(ref.current);
|
|
1729
|
+
}, [open]);
|
|
1730
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
// src/components/EmojiPicker.tsx
|
|
1734
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1735
|
+
var DEFAULT_EMOJI_PICKER_LABELS = {
|
|
1736
|
+
dialog: "Choose an emoji",
|
|
1737
|
+
search: "Search emoji",
|
|
1738
|
+
recent: "Recently used",
|
|
1739
|
+
quickPicks: "Frequently used",
|
|
1740
|
+
categories: "Emoji categories",
|
|
1741
|
+
noResults: "No emoji found",
|
|
1742
|
+
loading: "Loading emoji\u2026",
|
|
1743
|
+
loadFailed: "Emoji could not be loaded."
|
|
1821
1744
|
};
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
const
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1745
|
+
var EMOJI_GRID_COLUMNS = 8;
|
|
1746
|
+
var useIsomorphicLayoutEffect3 = typeof document !== "undefined" ? import_react5.useLayoutEffect : import_react5.useEffect;
|
|
1747
|
+
function revealInGrid(grid, cell) {
|
|
1748
|
+
const g = grid.getBoundingClientRect();
|
|
1749
|
+
const c = cell.getBoundingClientRect();
|
|
1750
|
+
const top = g.top + (parseFloat(getComputedStyle(grid).scrollPaddingTop) || 0);
|
|
1751
|
+
if (c.top < top) grid.scrollTop -= top - c.top;
|
|
1752
|
+
else if (c.bottom > g.bottom) grid.scrollTop += c.bottom - g.bottom;
|
|
1753
|
+
}
|
|
1754
|
+
function EmojiPicker({
|
|
1755
|
+
onSelect,
|
|
1756
|
+
trigger,
|
|
1757
|
+
labels: labelOverrides,
|
|
1758
|
+
locale = "en",
|
|
1759
|
+
quickPicks,
|
|
1760
|
+
loadData = loadEmojiData,
|
|
1761
|
+
recentStorageKey = DEFAULT_RECENT_KEY,
|
|
1762
|
+
placement = "bottom-start",
|
|
1763
|
+
onOpenChange,
|
|
1764
|
+
className,
|
|
1765
|
+
emojiImageBaseUrl
|
|
1766
|
+
}) {
|
|
1767
|
+
const renderEmoji = useEmojiRenderer(emojiImageBaseUrl);
|
|
1768
|
+
const labels = { ...DEFAULT_EMOJI_PICKER_LABELS, ...labelOverrides };
|
|
1769
|
+
const id = (0, import_react5.useId)();
|
|
1770
|
+
const popoverId = `${id}-picker`;
|
|
1771
|
+
const anchorRef = (0, import_react5.useRef)(null);
|
|
1772
|
+
const searchRef = (0, import_react5.useRef)(null);
|
|
1773
|
+
const gridRef = (0, import_react5.useRef)(null);
|
|
1774
|
+
const pointerWasOpen = (0, import_react5.useRef)(null);
|
|
1775
|
+
const closedByAnchor = (0, import_react5.useRef)(false);
|
|
1776
|
+
const pendingJump = (0, import_react5.useRef)(null);
|
|
1777
|
+
const [open, setOpen] = (0, import_react5.useState)(false);
|
|
1778
|
+
const [data, setData] = (0, import_react5.useState)(null);
|
|
1779
|
+
const [failed, setFailed] = (0, import_react5.useState)(false);
|
|
1780
|
+
const [query, setQuery] = (0, import_react5.useState)("");
|
|
1781
|
+
const [recent, setRecent] = (0, import_react5.useState)([]);
|
|
1782
|
+
const [active, setActive] = (0, import_react5.useState)(0);
|
|
1783
|
+
const popoverEl = () => typeof document === "undefined" ? null : document.getElementById(popoverId);
|
|
1784
|
+
const isOpen = (el) => {
|
|
1785
|
+
try {
|
|
1786
|
+
return !!el?.matches(":popover-open");
|
|
1787
|
+
} catch {
|
|
1788
|
+
return false;
|
|
1831
1789
|
}
|
|
1832
1790
|
};
|
|
1833
|
-
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1791
|
+
(0, import_react5.useEffect)(() => {
|
|
1792
|
+
const el = popoverEl();
|
|
1793
|
+
if (!el) return;
|
|
1794
|
+
const onToggle = (event) => {
|
|
1795
|
+
const { newState } = event;
|
|
1796
|
+
const nowOpen = newState ? newState === "open" : isOpen(el);
|
|
1797
|
+
setOpen(nowOpen);
|
|
1798
|
+
onOpenChange?.(nowOpen);
|
|
1799
|
+
if (nowOpen) {
|
|
1800
|
+
closedByAnchor.current = false;
|
|
1801
|
+
pendingJump.current = null;
|
|
1802
|
+
setRecent(readRecentEmoji(recentStorageKey));
|
|
1803
|
+
setQuery("");
|
|
1804
|
+
setActive(0);
|
|
1805
|
+
requestAnimationFrame(() => searchRef.current?.focus({ preventScroll: true }));
|
|
1806
|
+
} else if (closedByAnchor.current) {
|
|
1807
|
+
closedByAnchor.current = false;
|
|
1808
|
+
} else if (el.contains(document.activeElement) || document.activeElement === document.body) {
|
|
1809
|
+
focusTrigger();
|
|
1850
1810
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1811
|
+
};
|
|
1812
|
+
el.addEventListener("toggle", onToggle);
|
|
1813
|
+
return () => el.removeEventListener("toggle", onToggle);
|
|
1814
|
+
}, [popoverId, recentStorageKey, onOpenChange]);
|
|
1815
|
+
(0, import_react5.useEffect)(() => {
|
|
1816
|
+
if (!open || data?.locale === locale) return;
|
|
1817
|
+
let live = true;
|
|
1818
|
+
setFailed(false);
|
|
1819
|
+
loadData(locale).then(
|
|
1820
|
+
(loaded) => {
|
|
1821
|
+
if (live) setData(loaded);
|
|
1822
|
+
},
|
|
1823
|
+
() => {
|
|
1824
|
+
if (live) setFailed(true);
|
|
1825
|
+
}
|
|
1826
|
+
);
|
|
1827
|
+
return () => {
|
|
1828
|
+
live = false;
|
|
1829
|
+
};
|
|
1830
|
+
}, [open, locale, loadData, data?.locale]);
|
|
1831
|
+
const focusTrigger = () => {
|
|
1832
|
+
const el = anchorRef.current?.querySelector("button, [href], [tabindex]");
|
|
1833
|
+
el?.focus();
|
|
1834
|
+
};
|
|
1835
|
+
const hide = () => {
|
|
1836
|
+
const el = popoverEl();
|
|
1837
|
+
if (el && isOpen(el)) el.hidePopover();
|
|
1838
|
+
};
|
|
1839
|
+
const browseSections = (0, import_react5.useMemo)(() => {
|
|
1840
|
+
const list = [];
|
|
1841
|
+
if (quickPicks?.length) list.push({ key: "quick", title: labels.quickPicks, emojis: [...new Set(quickPicks)] });
|
|
1842
|
+
if (recent.length) list.push({ key: "recent", title: labels.recent, emojis: recent });
|
|
1843
|
+
if (data) {
|
|
1844
|
+
data.groups.forEach((group, index) => {
|
|
1845
|
+
list.push({ key: group.key, title: group.name, emojis: data.emojis.filter((e) => e.group === index).map((e) => e.emoji) });
|
|
1846
|
+
});
|
|
1854
1847
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1848
|
+
return list;
|
|
1849
|
+
}, [data, quickPicks, recent, labels.quickPicks, labels.recent]);
|
|
1850
|
+
const sections = (0, import_react5.useMemo)(() => {
|
|
1851
|
+
if (!query.trim()) return browseSections;
|
|
1852
|
+
return data ? [{ key: "results", title: labels.search, emojis: searchEmoji(data, query).map((e) => e.emoji) }] : [];
|
|
1853
|
+
}, [query, data, browseSections, labels.search]);
|
|
1854
|
+
const flat = (0, import_react5.useMemo)(() => sections.flatMap((s) => s.emojis), [sections]);
|
|
1855
|
+
const sectionStart = (0, import_react5.useMemo)(() => {
|
|
1856
|
+
const starts = /* @__PURE__ */ new Map();
|
|
1857
|
+
let at = 0;
|
|
1858
|
+
for (const section of sections) {
|
|
1859
|
+
starts.set(section.key, at);
|
|
1860
|
+
at += section.emojis.length;
|
|
1861
1861
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1862
|
+
return starts;
|
|
1863
|
+
}, [sections]);
|
|
1864
|
+
const activeIndex = Math.min(active, Math.max(flat.length - 1, 0));
|
|
1865
|
+
const choose = (emoji) => {
|
|
1866
|
+
setRecent(pushRecentEmoji(emoji, recentStorageKey));
|
|
1867
|
+
onSelect(emoji);
|
|
1868
|
+
hide();
|
|
1869
|
+
};
|
|
1870
|
+
const focusCell = (0, import_react5.useCallback)((index, reveal = true) => {
|
|
1871
|
+
setActive(index);
|
|
1872
|
+
const find = () => gridRef.current?.querySelector(`[data-emoji-index="${index}"]`);
|
|
1873
|
+
const move = (cell2) => {
|
|
1874
|
+
cell2.focus({ preventScroll: true });
|
|
1875
|
+
if (reveal && gridRef.current) revealInGrid(gridRef.current, cell2);
|
|
1876
|
+
};
|
|
1877
|
+
const cell = find();
|
|
1878
|
+
if (cell) move(cell);
|
|
1879
|
+
else requestAnimationFrame(() => {
|
|
1880
|
+
const later = find();
|
|
1881
|
+
if (later) move(later);
|
|
1882
|
+
});
|
|
1883
|
+
}, []);
|
|
1884
|
+
const showSection = (key) => {
|
|
1885
|
+
const grid = gridRef.current;
|
|
1886
|
+
const section = grid?.querySelector(`[data-section="${key}"]`);
|
|
1887
|
+
if (!grid || !section) return;
|
|
1888
|
+
grid.scrollTop += section.getBoundingClientRect().top - grid.getBoundingClientRect().top;
|
|
1889
|
+
const start = sectionStart.get(key);
|
|
1890
|
+
if (start !== void 0) focusCell(start, false);
|
|
1891
|
+
};
|
|
1892
|
+
const onCategory = (key) => {
|
|
1893
|
+
if (!query.trim()) {
|
|
1894
|
+
showSection(key);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
pendingJump.current = key;
|
|
1898
|
+
setQuery("");
|
|
1899
|
+
setActive(0);
|
|
1900
|
+
};
|
|
1901
|
+
useIsomorphicLayoutEffect3(() => {
|
|
1902
|
+
const key = pendingJump.current;
|
|
1903
|
+
if (key === null || query.trim()) return;
|
|
1904
|
+
pendingJump.current = null;
|
|
1905
|
+
showSection(key);
|
|
1906
|
+
}, [query, sections]);
|
|
1907
|
+
const onGridKeyDown = (event) => {
|
|
1908
|
+
const target = event.target;
|
|
1909
|
+
const index = Number(target.dataset.emojiIndex ?? -1);
|
|
1910
|
+
if (index < 0) return;
|
|
1911
|
+
const next = emojiGridMove(index, event.key, flat.length, EMOJI_GRID_COLUMNS);
|
|
1912
|
+
if (event.key === "ArrowUp" && next === null) {
|
|
1913
|
+
event.preventDefault();
|
|
1914
|
+
searchRef.current?.focus({ preventScroll: true });
|
|
1915
|
+
return;
|
|
1867
1916
|
}
|
|
1868
|
-
if (
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1917
|
+
if (next === null) return;
|
|
1918
|
+
event.preventDefault();
|
|
1919
|
+
focusCell(next);
|
|
1920
|
+
};
|
|
1921
|
+
const onSearchKeyDown = (event) => {
|
|
1922
|
+
if (event.key === "Escape") {
|
|
1923
|
+
event.preventDefault();
|
|
1924
|
+
hide();
|
|
1925
|
+
} else if (event.key === "ArrowDown" && flat.length) {
|
|
1926
|
+
event.preventDefault();
|
|
1927
|
+
focusCell(0);
|
|
1928
|
+
} else if (event.key === "Enter" && query.trim() && flat[0]) {
|
|
1929
|
+
event.preventDefault();
|
|
1930
|
+
choose(flat[0]);
|
|
1877
1931
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1932
|
+
};
|
|
1933
|
+
const toggleFromTrigger = (event) => {
|
|
1934
|
+
const el = popoverEl();
|
|
1935
|
+
if (!el || event.defaultPrevented) return;
|
|
1936
|
+
const was = pointerWasOpen.current;
|
|
1937
|
+
pointerWasOpen.current = null;
|
|
1938
|
+
if (was === true) return;
|
|
1939
|
+
if (isOpen(el)) el.hidePopover();
|
|
1940
|
+
else el.showPopover();
|
|
1941
|
+
};
|
|
1942
|
+
const triggerProps = (0, import_react5.isValidElement)(trigger) ? trigger.props : {};
|
|
1943
|
+
const triggerEl = (0, import_react5.isValidElement)(trigger) ? (0, import_react5.cloneElement)(trigger, {
|
|
1944
|
+
"aria-haspopup": "dialog",
|
|
1945
|
+
"aria-expanded": open,
|
|
1946
|
+
"aria-controls": popoverId,
|
|
1947
|
+
onPointerDown: (event) => {
|
|
1948
|
+
pointerWasOpen.current = isOpen(popoverEl());
|
|
1949
|
+
triggerProps.onPointerDown?.(event);
|
|
1950
|
+
},
|
|
1951
|
+
onClick: (event) => {
|
|
1952
|
+
triggerProps.onClick?.(event);
|
|
1953
|
+
toggleFromTrigger(event);
|
|
1893
1954
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1955
|
+
}) : trigger;
|
|
1956
|
+
const names = data?.names;
|
|
1957
|
+
let offset = 0;
|
|
1958
|
+
const status = failed ? labels.loadFailed : !data && open ? labels.loading : query.trim() && data && flat.length === 0 ? labels.noResults : "";
|
|
1959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { ref: anchorRef, className: cx("uix-emoji-picker__anchor", className), children: [
|
|
1960
|
+
triggerEl,
|
|
1961
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1962
|
+
Popover,
|
|
1963
|
+
{
|
|
1964
|
+
id: popoverId,
|
|
1965
|
+
anchor: anchorRef,
|
|
1966
|
+
placement,
|
|
1967
|
+
className: "uix-emoji-picker-popover",
|
|
1968
|
+
role: "dialog",
|
|
1969
|
+
"aria-label": labels.dialog,
|
|
1970
|
+
closeWhenAnchorHidden: true,
|
|
1971
|
+
onAnchorHidden: () => {
|
|
1972
|
+
closedByAnchor.current = true;
|
|
1973
|
+
},
|
|
1974
|
+
children: open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uix-emoji-picker uix-emoji-picker--full", lang: locale, children: [
|
|
1975
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1976
|
+
"input",
|
|
1977
|
+
{
|
|
1978
|
+
ref: searchRef,
|
|
1979
|
+
type: "search",
|
|
1980
|
+
className: "uix-input uix-emoji-picker__search",
|
|
1981
|
+
"aria-label": labels.search,
|
|
1982
|
+
placeholder: labels.search,
|
|
1983
|
+
value: query,
|
|
1984
|
+
onChange: (event) => {
|
|
1985
|
+
setQuery(event.target.value);
|
|
1986
|
+
setActive(0);
|
|
1987
|
+
},
|
|
1988
|
+
onKeyDown: onSearchKeyDown,
|
|
1989
|
+
autoComplete: "off",
|
|
1990
|
+
spellCheck: false
|
|
1991
|
+
}
|
|
1992
|
+
),
|
|
1993
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1994
|
+
"div",
|
|
1995
|
+
{
|
|
1996
|
+
className: "uix-emoji-picker__nav",
|
|
1997
|
+
role: data ? "group" : void 0,
|
|
1998
|
+
"aria-label": data ? labels.categories : void 0,
|
|
1999
|
+
children: data ? browseSections.map((section) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2000
|
+
"button",
|
|
2001
|
+
{
|
|
2002
|
+
type: "button",
|
|
2003
|
+
className: "uix-emoji-picker__nav-btn",
|
|
2004
|
+
"aria-label": section.title,
|
|
2005
|
+
title: section.title,
|
|
2006
|
+
onClick: () => onCategory(section.key),
|
|
2007
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": "true", children: section.emojis[0] ? renderEmoji(section.emojis[0]) : null })
|
|
2008
|
+
},
|
|
2009
|
+
section.key
|
|
2010
|
+
)) : null
|
|
2011
|
+
}
|
|
2012
|
+
),
|
|
2013
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "uix-emoji-picker__grid", ref: gridRef, onKeyDown: onGridKeyDown, children: [
|
|
2014
|
+
sections.map((section) => {
|
|
2015
|
+
const start = offset;
|
|
2016
|
+
offset += section.emojis.length;
|
|
2017
|
+
if (!section.emojis.length) return null;
|
|
2018
|
+
const headingId = `${id}-${section.key}`;
|
|
2019
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "uix-emoji-picker__section", "data-section": section.key, "aria-labelledby": headingId, children: [
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: headingId, className: "uix-emoji-picker__heading", children: section.title }),
|
|
2021
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "uix-emoji-picker__cells", role: "group", "aria-labelledby": headingId, children: section.emojis.map((emoji, i) => {
|
|
2022
|
+
const index = start + i;
|
|
2023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
2024
|
+
"button",
|
|
2025
|
+
{
|
|
2026
|
+
type: "button",
|
|
2027
|
+
className: "uix-emoji-picker__btn",
|
|
2028
|
+
"data-emoji-index": index,
|
|
2029
|
+
tabIndex: index === activeIndex ? 0 : -1,
|
|
2030
|
+
"aria-label": names?.get(emoji) ?? emoji,
|
|
2031
|
+
title: names?.get(emoji),
|
|
2032
|
+
onClick: () => choose(emoji),
|
|
2033
|
+
onFocus: () => setActive(index),
|
|
2034
|
+
children: renderEmoji(emoji)
|
|
2035
|
+
},
|
|
2036
|
+
`${section.key}-${emoji}`
|
|
2037
|
+
);
|
|
2038
|
+
}) })
|
|
2039
|
+
] }, section.key);
|
|
2040
|
+
}),
|
|
2041
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "uix-emoji-picker__status", role: "status", children: status })
|
|
2042
|
+
] })
|
|
2043
|
+
] }) : null
|
|
1911
2044
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
continue;
|
|
1915
|
-
}
|
|
1916
|
-
paragraph.push(trimmed);
|
|
1917
|
-
i += 1;
|
|
1918
|
-
}
|
|
1919
|
-
flush();
|
|
1920
|
-
return blocks;
|
|
2045
|
+
)
|
|
2046
|
+
] });
|
|
1921
2047
|
}
|
|
1922
2048
|
|
|
1923
2049
|
// src/components/Markdown.tsx
|
|
@@ -1953,6 +2079,13 @@ function renderInlines(nodes, policy) {
|
|
|
1953
2079
|
i
|
|
1954
2080
|
);
|
|
1955
2081
|
}
|
|
2082
|
+
case "mention": {
|
|
2083
|
+
const name = policy.resolveMention?.(node.id, node.label) ?? node.label;
|
|
2084
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "uix-mention", "data-mention-id": node.id, children: [
|
|
2085
|
+
"@",
|
|
2086
|
+
name
|
|
2087
|
+
] }, i);
|
|
2088
|
+
}
|
|
1956
2089
|
case "image": {
|
|
1957
2090
|
const resolved = policy.resolveImageSrc?.(node.src) ?? null;
|
|
1958
2091
|
if (resolved) {
|
|
@@ -2011,10 +2144,17 @@ function renderBlock(block, policy, headingOffset, key) {
|
|
|
2011
2144
|
);
|
|
2012
2145
|
}
|
|
2013
2146
|
}
|
|
2014
|
-
function Markdown({
|
|
2147
|
+
function Markdown({
|
|
2148
|
+
children,
|
|
2149
|
+
className,
|
|
2150
|
+
isSafeUrl = defaultMarkdownIsSafeUrl,
|
|
2151
|
+
resolveImageSrc,
|
|
2152
|
+
headingOffset = 2,
|
|
2153
|
+
resolveMention
|
|
2154
|
+
}) {
|
|
2015
2155
|
const source = typeof children === "string" ? children : "";
|
|
2016
2156
|
if (source.trim() === "") return null;
|
|
2017
|
-
const policy = { isSafeUrl, resolveImageSrc };
|
|
2157
|
+
const policy = { isSafeUrl, resolveImageSrc, resolveMention };
|
|
2018
2158
|
const blocks = parseMarkdown(source);
|
|
2019
2159
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: cx("uix-prose", "uix-markdown", className), "data-component": "markdown", children: blocks.map((block, i) => renderBlock(block, policy, headingOffset, i)) });
|
|
2020
2160
|
}
|
|
@@ -2069,6 +2209,10 @@ var DEFAULT_RICH_TEXT_LABELS = {
|
|
|
2069
2209
|
imagesUnsupported: "Images can't be added here.",
|
|
2070
2210
|
emoji: "Emoji",
|
|
2071
2211
|
emojiSuggestions: "Emoji suggestions",
|
|
2212
|
+
mentionSuggestions: "People",
|
|
2213
|
+
mentionSearching: "Searching\u2026",
|
|
2214
|
+
mentionEmpty: "No matching people",
|
|
2215
|
+
mentionFailed: "People could not be loaded.",
|
|
2072
2216
|
undo: "Undo",
|
|
2073
2217
|
redo: "Redo",
|
|
2074
2218
|
sourceMode: "Show markdown",
|
|
@@ -2099,6 +2243,8 @@ function suggestEmoji(query, limit = 8) {
|
|
|
2099
2243
|
}
|
|
2100
2244
|
return [...starts, ...tagged].slice(0, limit);
|
|
2101
2245
|
}
|
|
2246
|
+
var mentionSuggestionKey = new import_state.PluginKey("uixMentionSuggestion");
|
|
2247
|
+
var usableCandidates = (items) => items.filter((c) => isMentionId(c.id) && c.label.trim() !== "").slice(0, 20);
|
|
2102
2248
|
var htmlAttr = (value) => value === void 0 || value === false || value === "" ? void 0 : String(value);
|
|
2103
2249
|
var popoverOpen = (el) => {
|
|
2104
2250
|
try {
|
|
@@ -2197,8 +2343,10 @@ function RichTextEditor(props) {
|
|
|
2197
2343
|
className,
|
|
2198
2344
|
onBlur,
|
|
2199
2345
|
emojiImageBaseUrl,
|
|
2200
|
-
imagesUnavailableReason
|
|
2346
|
+
imagesUnavailableReason,
|
|
2347
|
+
onMentionSearch
|
|
2201
2348
|
} = props;
|
|
2349
|
+
const mentionsOn = typeof onMentionSearch === "function";
|
|
2202
2350
|
const emojiBase = (0, import_react7.useMemo)(() => normalizeEmojiImageBaseUrl(emojiImageBaseUrl), [emojiImageBaseUrl]);
|
|
2203
2351
|
const renderEmoji = useEmojiRenderer(emojiImageBaseUrl);
|
|
2204
2352
|
const labels = (0, import_react7.useMemo)(() => ({ ...DEFAULT_RICH_TEXT_LABELS, ...labelOverrides }), [labelOverrides]);
|
|
@@ -2206,10 +2354,11 @@ function RichTextEditor(props) {
|
|
|
2206
2354
|
const counterId = `${uid}-counter`;
|
|
2207
2355
|
const statusId = `${uid}-status`;
|
|
2208
2356
|
const suggestId = `${uid}-emoji`;
|
|
2357
|
+
const mentionId = `${uid}-people`;
|
|
2209
2358
|
const linkPopoverId = `${uid}-link`;
|
|
2210
2359
|
const controls = PRESETS[features];
|
|
2211
2360
|
const has = (control) => controls.has(control) && (control !== "emoji" || emoji) && (control !== "image" || !!onUploadImage);
|
|
2212
|
-
const latest = (0, import_react7.useRef)({ onChange, onSubmitShortcut, onBlur, isSafeUrl, resolveImageSrc, onUploadImage, placeholder, imagesOn: false, imagesOffText: "" });
|
|
2361
|
+
const latest = (0, import_react7.useRef)({ onChange, onSubmitShortcut, onBlur, isSafeUrl, resolveImageSrc, onUploadImage, onMentionSearch, placeholder, imagesOn: false, imagesOffText: "" });
|
|
2213
2362
|
latest.current = {
|
|
2214
2363
|
onChange,
|
|
2215
2364
|
onSubmitShortcut,
|
|
@@ -2217,6 +2366,7 @@ function RichTextEditor(props) {
|
|
|
2217
2366
|
isSafeUrl,
|
|
2218
2367
|
resolveImageSrc,
|
|
2219
2368
|
onUploadImage,
|
|
2369
|
+
onMentionSearch,
|
|
2220
2370
|
placeholder,
|
|
2221
2371
|
imagesOn: controls.has("image") && !!onUploadImage,
|
|
2222
2372
|
imagesOffText: imagesUnavailableReason?.trim() || labels.imagesUnsupported
|
|
@@ -2271,6 +2421,59 @@ function RichTextEditor(props) {
|
|
|
2271
2421
|
return false;
|
|
2272
2422
|
}
|
|
2273
2423
|
};
|
|
2424
|
+
const [mention, setMention] = (0, import_react7.useState)(null);
|
|
2425
|
+
const [mentionIndex, setMentionIndex] = (0, import_react7.useState)(0);
|
|
2426
|
+
const mentionState = (0, import_react7.useRef)({ mention, mentionIndex });
|
|
2427
|
+
mentionState.current = { mention, mentionIndex };
|
|
2428
|
+
const mentionSeq = (0, import_react7.useRef)(0);
|
|
2429
|
+
const mentionBridge = (0, import_react7.useRef)({ update: () => {
|
|
2430
|
+
}, keyDown: () => false });
|
|
2431
|
+
mentionBridge.current = {
|
|
2432
|
+
update: (p) => {
|
|
2433
|
+
const seq = ++mentionSeq.current;
|
|
2434
|
+
const search = latest.current.onMentionSearch;
|
|
2435
|
+
if (!p || !search) {
|
|
2436
|
+
setMention(null);
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
const rect = p.clientRect?.() ?? null;
|
|
2440
|
+
const base = { query: p.query, command: p.command, rect };
|
|
2441
|
+
setMention((prev) => ({ ...base, status: "loading", items: prev?.query && p.query.startsWith(prev.query) ? prev.items : [] }));
|
|
2442
|
+
setMentionIndex(0);
|
|
2443
|
+
Promise.resolve().then(() => search(p.query)).then(
|
|
2444
|
+
(found) => {
|
|
2445
|
+
if (seq !== mentionSeq.current) return;
|
|
2446
|
+
setMention({ ...base, status: "ready", items: usableCandidates(found ?? []) });
|
|
2447
|
+
},
|
|
2448
|
+
() => {
|
|
2449
|
+
if (seq !== mentionSeq.current) return;
|
|
2450
|
+
setMention({ ...base, status: "error", items: [] });
|
|
2451
|
+
}
|
|
2452
|
+
);
|
|
2453
|
+
},
|
|
2454
|
+
keyDown: ({ event }) => {
|
|
2455
|
+
const { mention: open, mentionIndex: index } = mentionState.current;
|
|
2456
|
+
if (!open) return false;
|
|
2457
|
+
if (event.key === "Escape") {
|
|
2458
|
+
mentionSeq.current += 1;
|
|
2459
|
+
setMention(null);
|
|
2460
|
+
return true;
|
|
2461
|
+
}
|
|
2462
|
+
const picks = open.status === "ready" ? open.items : [];
|
|
2463
|
+
if (picks.length === 0) return false;
|
|
2464
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
2465
|
+
const delta = event.key === "ArrowDown" ? 1 : -1;
|
|
2466
|
+
setMentionIndex((index + delta + picks.length) % picks.length);
|
|
2467
|
+
return true;
|
|
2468
|
+
}
|
|
2469
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
2470
|
+
const item = picks[Math.min(index, picks.length - 1)];
|
|
2471
|
+
if (item) open.command(item);
|
|
2472
|
+
return true;
|
|
2473
|
+
}
|
|
2474
|
+
return false;
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2274
2477
|
const headingKey = [...headingLevels].sort().join(",");
|
|
2275
2478
|
const extensions = (0, import_react7.useMemo)(() => {
|
|
2276
2479
|
const image = import_extension_image2.Image.extend({
|
|
@@ -2287,7 +2490,7 @@ function RichTextEditor(props) {
|
|
|
2287
2490
|
renderHTML({ node, HTMLAttributes }) {
|
|
2288
2491
|
const src = String(node.attrs.src ?? "");
|
|
2289
2492
|
const resolved = latest.current.resolveImageSrc?.(src) ?? null;
|
|
2290
|
-
if (resolved) return ["img", (0,
|
|
2493
|
+
if (resolved) return ["img", (0, import_core3.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { src: resolved })];
|
|
2291
2494
|
return ["span", { class: "uix-rich-text__image-link", "data-src": src, title: src }, node.attrs.alt || src];
|
|
2292
2495
|
}
|
|
2293
2496
|
}).configure({ inline: true, allowBase64: false });
|
|
@@ -2302,7 +2505,7 @@ function RichTextEditor(props) {
|
|
|
2302
2505
|
return findShortcode(name2)?.emoji ?? (name2 ? `:${name2}:` : "");
|
|
2303
2506
|
},
|
|
2304
2507
|
addInputRules() {
|
|
2305
|
-
return [new
|
|
2508
|
+
return [new import_core3.InputRule({
|
|
2306
2509
|
find: SHORTCODE_RE,
|
|
2307
2510
|
handler: ({ range, match, chain }) => {
|
|
2308
2511
|
const item = findShortcode(match[1] ?? "");
|
|
@@ -2337,7 +2540,7 @@ function RichTextEditor(props) {
|
|
|
2337
2540
|
})
|
|
2338
2541
|
}
|
|
2339
2542
|
});
|
|
2340
|
-
const shortcuts =
|
|
2543
|
+
const shortcuts = import_core3.Extension.create({
|
|
2341
2544
|
name: "uixRichTextShortcuts",
|
|
2342
2545
|
addKeyboardShortcuts() {
|
|
2343
2546
|
return {
|
|
@@ -2355,7 +2558,7 @@ function RichTextEditor(props) {
|
|
|
2355
2558
|
};
|
|
2356
2559
|
}
|
|
2357
2560
|
});
|
|
2358
|
-
const emojiImages =
|
|
2561
|
+
const emojiImages = import_core3.Extension.create({
|
|
2359
2562
|
name: "uixEmojiImages",
|
|
2360
2563
|
addProseMirrorPlugins() {
|
|
2361
2564
|
const key = new import_state.PluginKey("uixEmojiImages");
|
|
@@ -2393,6 +2596,30 @@ function RichTextEditor(props) {
|
|
|
2393
2596
|
})];
|
|
2394
2597
|
}
|
|
2395
2598
|
});
|
|
2599
|
+
const mentionSuggest = import_core3.Extension.create({
|
|
2600
|
+
name: "uixMentionSuggestion",
|
|
2601
|
+
addProseMirrorPlugins() {
|
|
2602
|
+
return [(0, import_suggestion.Suggestion)({
|
|
2603
|
+
editor: this.editor,
|
|
2604
|
+
char: "@",
|
|
2605
|
+
pluginKey: mentionSuggestionKey,
|
|
2606
|
+
items: () => [],
|
|
2607
|
+
command: ({ editor: ed, range, props: item }) => {
|
|
2608
|
+
ed.chain().focus().insertContentAt(range, [
|
|
2609
|
+
{ type: "mention", attrs: { id: item.id, label: item.label.replace(/\s+/gu, " ").trim() } },
|
|
2610
|
+
{ type: "text", text: " " }
|
|
2611
|
+
]).run();
|
|
2612
|
+
},
|
|
2613
|
+
allow: ({ state: state2, range }) => !state2.doc.resolve(range.from).parent.type.spec.code,
|
|
2614
|
+
render: () => ({
|
|
2615
|
+
onStart: (p) => mentionBridge.current.update(p),
|
|
2616
|
+
onUpdate: (p) => mentionBridge.current.update(p),
|
|
2617
|
+
onExit: () => mentionBridge.current.update(null),
|
|
2618
|
+
onKeyDown: (p) => mentionBridge.current.keyDown(p)
|
|
2619
|
+
})
|
|
2620
|
+
})];
|
|
2621
|
+
}
|
|
2622
|
+
});
|
|
2396
2623
|
return [
|
|
2397
2624
|
...createSchemaExtensions({
|
|
2398
2625
|
headingLevels: headingKey.split(",").map(Number),
|
|
@@ -2403,9 +2630,10 @@ function RichTextEditor(props) {
|
|
|
2403
2630
|
import_extensions.Placeholder.configure({ placeholder: () => latest.current.placeholder ?? "" }),
|
|
2404
2631
|
shortcuts,
|
|
2405
2632
|
...emoji ? [unicodeEmoji] : [],
|
|
2406
|
-
...emojiBase ? [emojiImages] : []
|
|
2633
|
+
...emojiBase ? [emojiImages] : [],
|
|
2634
|
+
...mentionsOn ? [mentionSuggest] : []
|
|
2407
2635
|
];
|
|
2408
|
-
}, [headingKey, emoji, emojiBase]);
|
|
2636
|
+
}, [headingKey, emoji, emojiBase, mentionsOn]);
|
|
2409
2637
|
const managerOf = (ed) => {
|
|
2410
2638
|
const manager = ed.markdown ?? ed.storage.markdown.manager;
|
|
2411
2639
|
return patchManager(manager);
|
|
@@ -2487,6 +2715,8 @@ function RichTextEditor(props) {
|
|
|
2487
2715
|
const invalid = props["aria-invalid"] || over;
|
|
2488
2716
|
const describedBy = [props["aria-describedby"], maxLength !== void 0 ? counterId : void 0, status ? statusId : void 0].filter(Boolean).join(" ") || void 0;
|
|
2489
2717
|
const suggestOpen = !!suggest && mode === "rich";
|
|
2718
|
+
const mentionOpen = !suggestOpen && !!mention && mode === "rich";
|
|
2719
|
+
const mentionActive = mentionOpen && mention?.status === "ready" && mention.items.length > 0 ? `${mentionId}-${Math.min(mentionIndex, mention.items.length - 1)}` : void 0;
|
|
2490
2720
|
const rows = minRows ?? (variant === "composer" ? 3 : 4);
|
|
2491
2721
|
(0, import_react7.useEffect)(() => {
|
|
2492
2722
|
if (!editor || editor.isDestroyed) return;
|
|
@@ -2504,9 +2734,9 @@ function RichTextEditor(props) {
|
|
|
2504
2734
|
"aria-required": htmlAttr(props["aria-required"]),
|
|
2505
2735
|
"aria-disabled": htmlAttr(disabled),
|
|
2506
2736
|
"aria-readonly": htmlAttr(readOnly),
|
|
2507
|
-
"aria-autocomplete": emoji ? "list" : void 0,
|
|
2508
|
-
"aria-controls": suggestOpen ? suggestId : void 0,
|
|
2509
|
-
"aria-activedescendant": suggestOpen ? `${suggestId}-${suggestIndex}` :
|
|
2737
|
+
"aria-autocomplete": emoji || mentionsOn ? "list" : void 0,
|
|
2738
|
+
"aria-controls": suggestOpen ? suggestId : mentionOpen ? mentionId : void 0,
|
|
2739
|
+
"aria-activedescendant": suggestOpen ? `${suggestId}-${suggestIndex}` : mentionActive,
|
|
2510
2740
|
class: "uix-rich-text__content uix-prose",
|
|
2511
2741
|
style: `min-height: calc(${rows} * 1lh + 2 * var(--uix-space-3))`
|
|
2512
2742
|
}).filter(([, v]) => v !== void 0))
|
|
@@ -2527,7 +2757,11 @@ function RichTextEditor(props) {
|
|
|
2527
2757
|
suggestOpen,
|
|
2528
2758
|
suggestId,
|
|
2529
2759
|
suggestIndex,
|
|
2530
|
-
rows
|
|
2760
|
+
rows,
|
|
2761
|
+
mentionsOn,
|
|
2762
|
+
mentionOpen,
|
|
2763
|
+
mentionId,
|
|
2764
|
+
mentionActive
|
|
2531
2765
|
]);
|
|
2532
2766
|
const state = (0, import_react8.useEditorState)({
|
|
2533
2767
|
editor,
|
|
@@ -2881,6 +3115,16 @@ function RichTextEditor(props) {
|
|
|
2881
3115
|
onPick: (item) => suggest.command(item),
|
|
2882
3116
|
renderEmoji
|
|
2883
3117
|
}
|
|
3118
|
+
) : null,
|
|
3119
|
+
mentionOpen && mention ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3120
|
+
MentionSuggestions,
|
|
3121
|
+
{
|
|
3122
|
+
id: mentionId,
|
|
3123
|
+
labels,
|
|
3124
|
+
state: mention,
|
|
3125
|
+
active: Math.min(mentionIndex, Math.max(mention.items.length - 1, 0)),
|
|
3126
|
+
onPick: (item) => mention.command(item)
|
|
3127
|
+
}
|
|
2884
3128
|
) : null
|
|
2885
3129
|
] });
|
|
2886
3130
|
const counter = maxLength !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { id: counterId, className: "uix-rich-text__counter", "data-over": over || void 0, children: formatLabel(labels.characterCount, { count: markdown.length, max: maxLength }) }) : null;
|
|
@@ -2915,8 +3159,7 @@ function RichTextEditor(props) {
|
|
|
2915
3159
|
hidden
|
|
2916
3160
|
] });
|
|
2917
3161
|
}
|
|
2918
|
-
function
|
|
2919
|
-
const listRef = (0, import_react7.useRef)(null);
|
|
3162
|
+
function useCaretPosition(listRef, rect, size) {
|
|
2920
3163
|
const [pos, setPos] = (0, import_react7.useState)(null);
|
|
2921
3164
|
(0, import_react7.useEffect)(() => {
|
|
2922
3165
|
const el = listRef.current;
|
|
@@ -2929,7 +3172,71 @@ function EmojiSuggestions({ id, label, items, query, active, rect, onPick, rende
|
|
|
2929
3172
|
{ placement: "bottom-start", offset: 4, padding: 8 }
|
|
2930
3173
|
);
|
|
2931
3174
|
setPos({ x: result.x, y: result.y });
|
|
2932
|
-
}, [rect,
|
|
3175
|
+
}, [listRef, rect, size]);
|
|
3176
|
+
return pos;
|
|
3177
|
+
}
|
|
3178
|
+
function MentionSuggestions({ id, labels, state, active, onPick }) {
|
|
3179
|
+
const listRef = (0, import_react7.useRef)(null);
|
|
3180
|
+
const items = state.status === "ready" ? state.items : [];
|
|
3181
|
+
const pos = useCaretPosition(listRef, state.rect, `${state.status}:${items.length}`);
|
|
3182
|
+
(0, import_react7.useEffect)(() => {
|
|
3183
|
+
document.getElementById(`${id}-${active}`)?.scrollIntoView?.({ block: "nearest" });
|
|
3184
|
+
}, [active, id]);
|
|
3185
|
+
const style = { left: pos?.x ?? -9999, top: pos?.y ?? -9999 };
|
|
3186
|
+
if (items.length === 0) {
|
|
3187
|
+
const note = state.status === "loading" ? labels.mentionSearching : state.status === "error" ? labels.mentionFailed : labels.mentionEmpty;
|
|
3188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3189
|
+
"div",
|
|
3190
|
+
{
|
|
3191
|
+
ref: listRef,
|
|
3192
|
+
id,
|
|
3193
|
+
role: "status",
|
|
3194
|
+
"aria-label": labels.mentionSuggestions,
|
|
3195
|
+
className: "uix-rich-text__suggest",
|
|
3196
|
+
"data-mention-list": "",
|
|
3197
|
+
"data-state": state.status === "ready" ? "empty" : state.status,
|
|
3198
|
+
style,
|
|
3199
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "uix-rich-text__suggest-note", children: note })
|
|
3200
|
+
}
|
|
3201
|
+
);
|
|
3202
|
+
}
|
|
3203
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
3204
|
+
"div",
|
|
3205
|
+
{
|
|
3206
|
+
ref: listRef,
|
|
3207
|
+
id,
|
|
3208
|
+
role: "listbox",
|
|
3209
|
+
"aria-label": labels.mentionSuggestions,
|
|
3210
|
+
"aria-busy": state.status === "loading" || void 0,
|
|
3211
|
+
className: "uix-rich-text__suggest",
|
|
3212
|
+
"data-mention-list": "",
|
|
3213
|
+
"data-state": "ready",
|
|
3214
|
+
style,
|
|
3215
|
+
children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
3216
|
+
"div",
|
|
3217
|
+
{
|
|
3218
|
+
id: `${id}-${i}`,
|
|
3219
|
+
role: "option",
|
|
3220
|
+
"aria-selected": i === active,
|
|
3221
|
+
className: "uix-rich-text__suggest-item uix-rich-text__suggest-item--person",
|
|
3222
|
+
"data-mention-id": item.id,
|
|
3223
|
+
onMouseDown: (event) => {
|
|
3224
|
+
event.preventDefault();
|
|
3225
|
+
onPick(item);
|
|
3226
|
+
},
|
|
3227
|
+
children: [
|
|
3228
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-rich-text__suggest-label", children: item.label }),
|
|
3229
|
+
item.description ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "uix-rich-text__suggest-detail", children: item.description }) : null
|
|
3230
|
+
]
|
|
3231
|
+
},
|
|
3232
|
+
item.id
|
|
3233
|
+
))
|
|
3234
|
+
}
|
|
3235
|
+
);
|
|
3236
|
+
}
|
|
3237
|
+
function EmojiSuggestions({ id, label, items, query, active, rect, onPick, renderEmoji }) {
|
|
3238
|
+
const listRef = (0, import_react7.useRef)(null);
|
|
3239
|
+
const pos = useCaretPosition(listRef, rect, items.length);
|
|
2933
3240
|
(0, import_react7.useEffect)(() => {
|
|
2934
3241
|
document.getElementById(`${id}-${active}`)?.scrollIntoView?.({ block: "nearest" });
|
|
2935
3242
|
}, [active, id]);
|
|
@@ -2979,8 +3286,13 @@ async function roundTripMarkdown(markdown, options) {
|
|
|
2979
3286
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2980
3287
|
0 && (module.exports = {
|
|
2981
3288
|
DEFAULT_RICH_TEXT_LABELS,
|
|
3289
|
+
MENTION_SCHEME,
|
|
2982
3290
|
RichTextEditor,
|
|
2983
3291
|
RichTextEditorFallback,
|
|
2984
3292
|
emojiImageFileName,
|
|
3293
|
+
findMentions,
|
|
3294
|
+
formatMention,
|
|
3295
|
+
isMentionId,
|
|
3296
|
+
readMention,
|
|
2985
3297
|
roundTripMarkdown
|
|
2986
3298
|
});
|