@composer-app/mcp 0.0.4-beta.2 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-D62LPVSX.js → chunk-IDX65B2W.js} +1548 -99
- package/dist/cli.js +6 -6
- package/dist/mcp.js +1 -1
- package/package.json +7 -3
|
@@ -33,6 +33,9 @@ function getActivityMap(doc) {
|
|
|
33
33
|
function getActivityStateMap(doc) {
|
|
34
34
|
return doc.getMap("activityState");
|
|
35
35
|
}
|
|
36
|
+
function stateKey(activityId, userId) {
|
|
37
|
+
return `${activityId}:${userId}`;
|
|
38
|
+
}
|
|
36
39
|
function emitActivity(doc, event, opts) {
|
|
37
40
|
if (opts?.silent) return;
|
|
38
41
|
const activity = getActivityMap(doc);
|
|
@@ -65,6 +68,20 @@ function pruneIfOverCap(doc) {
|
|
|
65
68
|
});
|
|
66
69
|
}
|
|
67
70
|
}
|
|
71
|
+
function markActivityReadForThread(doc, threadId, userId) {
|
|
72
|
+
const activity = getActivityMap(doc);
|
|
73
|
+
const stateMap = getActivityStateMap(doc);
|
|
74
|
+
activity.forEach((value) => {
|
|
75
|
+
const event = value;
|
|
76
|
+
if (event.threadId === threadId) {
|
|
77
|
+
const key = stateKey(event.id, userId);
|
|
78
|
+
const existing = stateMap.get(key);
|
|
79
|
+
if (!existing?.read) {
|
|
80
|
+
stateMap.set(key, { read: true, dismissed: existing?.dismissed ?? false });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
68
85
|
function textPreview(text, maxLen = 80) {
|
|
69
86
|
if (!text) return void 0;
|
|
70
87
|
return text.length > maxLen ? text.slice(0, maxLen) + "\u2026" : text;
|
|
@@ -358,29 +375,1187 @@ var removeAwarenessStates = (awareness, clients, origin) => {
|
|
|
358
375
|
/** @type {MetaClientState} */
|
|
359
376
|
awareness.meta.get(clientID)
|
|
360
377
|
);
|
|
361
|
-
awareness.meta.set(clientID, {
|
|
362
|
-
clock: curMeta.clock + 1,
|
|
363
|
-
lastUpdated: getUnixTime()
|
|
364
|
-
});
|
|
378
|
+
awareness.meta.set(clientID, {
|
|
379
|
+
clock: curMeta.clock + 1,
|
|
380
|
+
lastUpdated: getUnixTime()
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
removed.push(clientID);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (removed.length > 0) {
|
|
387
|
+
awareness.emit("change", [{ added: [], updated: [], removed }, origin]);
|
|
388
|
+
awareness.emit("update", [{ added: [], updated: [], removed }, origin]);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/roomState.ts
|
|
393
|
+
import WebSocket from "ws";
|
|
394
|
+
|
|
395
|
+
// ../shared/src/editor-extensions.ts
|
|
396
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
397
|
+
import { Code } from "@tiptap/extension-code";
|
|
398
|
+
import CodeBlock from "@tiptap/extension-code-block";
|
|
399
|
+
|
|
400
|
+
// ../node_modules/@tiptap/extension-blockquote/dist/index.js
|
|
401
|
+
import { mergeAttributes, Node, wrappingInputRule } from "@tiptap/core";
|
|
402
|
+
import { jsx } from "@tiptap/core/jsx-runtime";
|
|
403
|
+
var inputRegex = /^\s*>\s$/;
|
|
404
|
+
var Blockquote = Node.create({
|
|
405
|
+
name: "blockquote",
|
|
406
|
+
addOptions() {
|
|
407
|
+
return {
|
|
408
|
+
HTMLAttributes: {}
|
|
409
|
+
};
|
|
410
|
+
},
|
|
411
|
+
content: "block+",
|
|
412
|
+
group: "block",
|
|
413
|
+
defining: true,
|
|
414
|
+
parseHTML() {
|
|
415
|
+
return [{ tag: "blockquote" }];
|
|
416
|
+
},
|
|
417
|
+
renderHTML({ HTMLAttributes }) {
|
|
418
|
+
return /* @__PURE__ */ jsx("blockquote", { ...mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), children: /* @__PURE__ */ jsx("slot", {}) });
|
|
419
|
+
},
|
|
420
|
+
parseMarkdown: (token, helpers) => {
|
|
421
|
+
var _a;
|
|
422
|
+
const parseBlockChildren = (_a = helpers.parseBlockChildren) != null ? _a : helpers.parseChildren;
|
|
423
|
+
return helpers.createNode("blockquote", void 0, parseBlockChildren(token.tokens || []));
|
|
424
|
+
},
|
|
425
|
+
renderMarkdown: (node, h) => {
|
|
426
|
+
if (!node.content) {
|
|
427
|
+
return "";
|
|
428
|
+
}
|
|
429
|
+
const prefix = ">";
|
|
430
|
+
const result = [];
|
|
431
|
+
node.content.forEach((child, index) => {
|
|
432
|
+
var _a, _b;
|
|
433
|
+
const childContent = (_b = (_a = h.renderChild) == null ? void 0 : _a.call(h, child, index)) != null ? _b : h.renderChildren([child]);
|
|
434
|
+
const lines = childContent.split("\n");
|
|
435
|
+
const linesWithPrefix = lines.map((line) => {
|
|
436
|
+
if (line.trim() === "") {
|
|
437
|
+
return prefix;
|
|
438
|
+
}
|
|
439
|
+
return `${prefix} ${line}`;
|
|
440
|
+
});
|
|
441
|
+
result.push(linesWithPrefix.join("\n"));
|
|
442
|
+
});
|
|
443
|
+
return result.join(`
|
|
444
|
+
${prefix}
|
|
445
|
+
`);
|
|
446
|
+
},
|
|
447
|
+
addCommands() {
|
|
448
|
+
return {
|
|
449
|
+
setBlockquote: () => ({ commands }) => {
|
|
450
|
+
return commands.wrapIn(this.name);
|
|
451
|
+
},
|
|
452
|
+
toggleBlockquote: () => ({ commands }) => {
|
|
453
|
+
return commands.toggleWrap(this.name);
|
|
454
|
+
},
|
|
455
|
+
unsetBlockquote: () => ({ commands }) => {
|
|
456
|
+
return commands.lift(this.name);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
},
|
|
460
|
+
addKeyboardShortcuts() {
|
|
461
|
+
return {
|
|
462
|
+
"Mod-Shift-b": () => this.editor.commands.toggleBlockquote()
|
|
463
|
+
};
|
|
464
|
+
},
|
|
465
|
+
addInputRules() {
|
|
466
|
+
return [
|
|
467
|
+
wrappingInputRule({
|
|
468
|
+
find: inputRegex,
|
|
469
|
+
type: this.type
|
|
470
|
+
})
|
|
471
|
+
];
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// ../node_modules/@tiptap/extension-list/dist/index.js
|
|
476
|
+
import { mergeAttributes as mergeAttributes2, Node as Node2, wrappingInputRule as wrappingInputRule2 } from "@tiptap/core";
|
|
477
|
+
import { mergeAttributes as mergeAttributes22, Node as Node22, renderNestedMarkdownContent } from "@tiptap/core";
|
|
478
|
+
import { Extension } from "@tiptap/core";
|
|
479
|
+
import { getNodeType } from "@tiptap/core";
|
|
480
|
+
import { getNodeAtPosition } from "@tiptap/core";
|
|
481
|
+
import { isAtStartOfNode, isNodeActive } from "@tiptap/core";
|
|
482
|
+
import { getNodeType as getNodeType2 } from "@tiptap/core";
|
|
483
|
+
import { isAtEndOfNode, isNodeActive as isNodeActive2 } from "@tiptap/core";
|
|
484
|
+
import { Extension as Extension2 } from "@tiptap/core";
|
|
485
|
+
import { mergeAttributes as mergeAttributes3, Node as Node3, wrappingInputRule as wrappingInputRule22 } from "@tiptap/core";
|
|
486
|
+
import {
|
|
487
|
+
getRenderedAttributes,
|
|
488
|
+
mergeAttributes as mergeAttributes4,
|
|
489
|
+
Node as Node4,
|
|
490
|
+
renderNestedMarkdownContent as renderNestedMarkdownContent2,
|
|
491
|
+
wrappingInputRule as wrappingInputRule3
|
|
492
|
+
} from "@tiptap/core";
|
|
493
|
+
import { mergeAttributes as mergeAttributes5, Node as Node5, parseIndentedBlocks } from "@tiptap/core";
|
|
494
|
+
var __defProp = Object.defineProperty;
|
|
495
|
+
var __export = (target, all) => {
|
|
496
|
+
for (var name in all)
|
|
497
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
498
|
+
};
|
|
499
|
+
var ListItemName = "listItem";
|
|
500
|
+
var TextStyleName = "textStyle";
|
|
501
|
+
var bulletListInputRegex = /^\s*([-+*])\s$/;
|
|
502
|
+
var BulletList = Node2.create({
|
|
503
|
+
name: "bulletList",
|
|
504
|
+
addOptions() {
|
|
505
|
+
return {
|
|
506
|
+
itemTypeName: "listItem",
|
|
507
|
+
HTMLAttributes: {},
|
|
508
|
+
keepMarks: false,
|
|
509
|
+
keepAttributes: false
|
|
510
|
+
};
|
|
511
|
+
},
|
|
512
|
+
group: "block list",
|
|
513
|
+
content() {
|
|
514
|
+
return `${this.options.itemTypeName}+`;
|
|
515
|
+
},
|
|
516
|
+
parseHTML() {
|
|
517
|
+
return [{ tag: "ul" }];
|
|
518
|
+
},
|
|
519
|
+
renderHTML({ HTMLAttributes }) {
|
|
520
|
+
return ["ul", mergeAttributes2(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
521
|
+
},
|
|
522
|
+
markdownTokenName: "list",
|
|
523
|
+
parseMarkdown: (token, helpers) => {
|
|
524
|
+
if (token.type !== "list" || token.ordered) {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
type: "bulletList",
|
|
529
|
+
content: token.items ? helpers.parseChildren(token.items) : []
|
|
530
|
+
};
|
|
531
|
+
},
|
|
532
|
+
renderMarkdown: (node, h) => {
|
|
533
|
+
if (!node.content) {
|
|
534
|
+
return "";
|
|
535
|
+
}
|
|
536
|
+
return h.renderChildren(node.content, "\n");
|
|
537
|
+
},
|
|
538
|
+
markdownOptions: {
|
|
539
|
+
indentsContent: true
|
|
540
|
+
},
|
|
541
|
+
addCommands() {
|
|
542
|
+
return {
|
|
543
|
+
toggleBulletList: () => ({ commands, chain }) => {
|
|
544
|
+
if (this.options.keepAttributes) {
|
|
545
|
+
return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName, this.editor.getAttributes(TextStyleName)).run();
|
|
546
|
+
}
|
|
547
|
+
return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
},
|
|
551
|
+
addKeyboardShortcuts() {
|
|
552
|
+
return {
|
|
553
|
+
"Mod-Shift-8": () => this.editor.commands.toggleBulletList()
|
|
554
|
+
};
|
|
555
|
+
},
|
|
556
|
+
addInputRules() {
|
|
557
|
+
let inputRule = wrappingInputRule2({
|
|
558
|
+
find: bulletListInputRegex,
|
|
559
|
+
type: this.type
|
|
560
|
+
});
|
|
561
|
+
if (this.options.keepMarks || this.options.keepAttributes) {
|
|
562
|
+
inputRule = wrappingInputRule2({
|
|
563
|
+
find: bulletListInputRegex,
|
|
564
|
+
type: this.type,
|
|
565
|
+
keepMarks: this.options.keepMarks,
|
|
566
|
+
keepAttributes: this.options.keepAttributes,
|
|
567
|
+
getAttributes: () => {
|
|
568
|
+
return this.editor.getAttributes(TextStyleName);
|
|
569
|
+
},
|
|
570
|
+
editor: this.editor
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
return [inputRule];
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
var ListItem = Node22.create({
|
|
577
|
+
name: "listItem",
|
|
578
|
+
addOptions() {
|
|
579
|
+
return {
|
|
580
|
+
HTMLAttributes: {},
|
|
581
|
+
bulletListTypeName: "bulletList",
|
|
582
|
+
orderedListTypeName: "orderedList"
|
|
583
|
+
};
|
|
584
|
+
},
|
|
585
|
+
content: "paragraph block*",
|
|
586
|
+
defining: true,
|
|
587
|
+
parseHTML() {
|
|
588
|
+
return [
|
|
589
|
+
{
|
|
590
|
+
tag: "li"
|
|
591
|
+
}
|
|
592
|
+
];
|
|
593
|
+
},
|
|
594
|
+
renderHTML({ HTMLAttributes }) {
|
|
595
|
+
return ["li", mergeAttributes22(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
596
|
+
},
|
|
597
|
+
markdownTokenName: "list_item",
|
|
598
|
+
parseMarkdown: (token, helpers) => {
|
|
599
|
+
var _a;
|
|
600
|
+
if (token.type !== "list_item") {
|
|
601
|
+
return [];
|
|
602
|
+
}
|
|
603
|
+
const parseBlockChildren = (_a = helpers.parseBlockChildren) != null ? _a : helpers.parseChildren;
|
|
604
|
+
let content = [];
|
|
605
|
+
if (token.tokens && token.tokens.length > 0) {
|
|
606
|
+
const hasParagraphTokens = token.tokens.some((t) => t.type === "paragraph");
|
|
607
|
+
if (hasParagraphTokens) {
|
|
608
|
+
content = parseBlockChildren(token.tokens);
|
|
609
|
+
} else {
|
|
610
|
+
const firstToken = token.tokens[0];
|
|
611
|
+
if (firstToken && firstToken.type === "text" && firstToken.tokens && firstToken.tokens.length > 0) {
|
|
612
|
+
const inlineContent = helpers.parseInline(firstToken.tokens);
|
|
613
|
+
content = [
|
|
614
|
+
{
|
|
615
|
+
type: "paragraph",
|
|
616
|
+
content: inlineContent
|
|
617
|
+
}
|
|
618
|
+
];
|
|
619
|
+
if (token.tokens.length > 1) {
|
|
620
|
+
const remainingTokens = token.tokens.slice(1);
|
|
621
|
+
const additionalContent = parseBlockChildren(remainingTokens);
|
|
622
|
+
content.push(...additionalContent);
|
|
623
|
+
}
|
|
624
|
+
} else {
|
|
625
|
+
content = parseBlockChildren(token.tokens);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (content.length === 0) {
|
|
630
|
+
content = [
|
|
631
|
+
{
|
|
632
|
+
type: "paragraph",
|
|
633
|
+
content: []
|
|
634
|
+
}
|
|
635
|
+
];
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
type: "listItem",
|
|
639
|
+
content
|
|
640
|
+
};
|
|
641
|
+
},
|
|
642
|
+
renderMarkdown: (node, h, ctx) => {
|
|
643
|
+
return renderNestedMarkdownContent(
|
|
644
|
+
node,
|
|
645
|
+
h,
|
|
646
|
+
(context) => {
|
|
647
|
+
var _a, _b;
|
|
648
|
+
if (context.parentType === "bulletList") {
|
|
649
|
+
return "- ";
|
|
650
|
+
}
|
|
651
|
+
if (context.parentType === "orderedList") {
|
|
652
|
+
const start = ((_b = (_a = context.meta) == null ? void 0 : _a.parentAttrs) == null ? void 0 : _b.start) || 1;
|
|
653
|
+
return `${start + context.index}. `;
|
|
654
|
+
}
|
|
655
|
+
return "- ";
|
|
656
|
+
},
|
|
657
|
+
ctx
|
|
658
|
+
);
|
|
659
|
+
},
|
|
660
|
+
addKeyboardShortcuts() {
|
|
661
|
+
return {
|
|
662
|
+
Enter: () => this.editor.commands.splitListItem(this.name),
|
|
663
|
+
Tab: () => this.editor.commands.sinkListItem(this.name),
|
|
664
|
+
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
var listHelpers_exports = {};
|
|
669
|
+
__export(listHelpers_exports, {
|
|
670
|
+
findListItemPos: () => findListItemPos,
|
|
671
|
+
getNextListDepth: () => getNextListDepth,
|
|
672
|
+
handleBackspace: () => handleBackspace,
|
|
673
|
+
handleDelete: () => handleDelete,
|
|
674
|
+
hasListBefore: () => hasListBefore,
|
|
675
|
+
hasListItemAfter: () => hasListItemAfter,
|
|
676
|
+
hasListItemBefore: () => hasListItemBefore,
|
|
677
|
+
listItemHasSubList: () => listItemHasSubList,
|
|
678
|
+
nextListIsDeeper: () => nextListIsDeeper,
|
|
679
|
+
nextListIsHigher: () => nextListIsHigher
|
|
680
|
+
});
|
|
681
|
+
var findListItemPos = (typeOrName, state) => {
|
|
682
|
+
const { $from } = state.selection;
|
|
683
|
+
const nodeType = getNodeType(typeOrName, state.schema);
|
|
684
|
+
let currentNode = null;
|
|
685
|
+
let currentDepth = $from.depth;
|
|
686
|
+
let currentPos = $from.pos;
|
|
687
|
+
let targetDepth = null;
|
|
688
|
+
while (currentDepth > 0 && targetDepth === null) {
|
|
689
|
+
currentNode = $from.node(currentDepth);
|
|
690
|
+
if (currentNode.type === nodeType) {
|
|
691
|
+
targetDepth = currentDepth;
|
|
692
|
+
} else {
|
|
693
|
+
currentDepth -= 1;
|
|
694
|
+
currentPos -= 1;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
if (targetDepth === null) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
return { $pos: state.doc.resolve(currentPos), depth: targetDepth };
|
|
701
|
+
};
|
|
702
|
+
var getNextListDepth = (typeOrName, state) => {
|
|
703
|
+
const listItemPos = findListItemPos(typeOrName, state);
|
|
704
|
+
if (!listItemPos) {
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4);
|
|
708
|
+
return depth;
|
|
709
|
+
};
|
|
710
|
+
var hasListBefore = (editorState, name, parentListTypes) => {
|
|
711
|
+
const { $anchor } = editorState.selection;
|
|
712
|
+
const previousNodePos = Math.max(0, $anchor.pos - 2);
|
|
713
|
+
const previousNode = editorState.doc.resolve(previousNodePos).node();
|
|
714
|
+
if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
return true;
|
|
718
|
+
};
|
|
719
|
+
var hasListItemBefore = (typeOrName, state) => {
|
|
720
|
+
var _a;
|
|
721
|
+
const { $anchor } = state.selection;
|
|
722
|
+
const $targetPos = state.doc.resolve($anchor.pos - 2);
|
|
723
|
+
if ($targetPos.index() === 0) {
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
return true;
|
|
730
|
+
};
|
|
731
|
+
var listItemHasSubList = (typeOrName, state, node) => {
|
|
732
|
+
if (!node) {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
735
|
+
const nodeType = getNodeType2(typeOrName, state.schema);
|
|
736
|
+
let hasSubList = false;
|
|
737
|
+
node.descendants((child) => {
|
|
738
|
+
if (child.type === nodeType) {
|
|
739
|
+
hasSubList = true;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
return hasSubList;
|
|
743
|
+
};
|
|
744
|
+
var handleBackspace = (editor, name, parentListTypes) => {
|
|
745
|
+
if (editor.commands.undoInputRule()) {
|
|
746
|
+
return true;
|
|
747
|
+
}
|
|
748
|
+
if (editor.state.selection.from !== editor.state.selection.to) {
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
|
|
752
|
+
const { $anchor } = editor.state.selection;
|
|
753
|
+
const $listPos = editor.state.doc.resolve($anchor.before() - 1);
|
|
754
|
+
const listDescendants = [];
|
|
755
|
+
$listPos.node().descendants((node, pos) => {
|
|
756
|
+
if (node.type.name === name) {
|
|
757
|
+
listDescendants.push({ node, pos });
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
const lastItem = listDescendants.at(-1);
|
|
761
|
+
if (!lastItem) {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1);
|
|
765
|
+
return editor.chain().cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end()).joinForward().run();
|
|
766
|
+
}
|
|
767
|
+
if (!isNodeActive(editor.state, name)) {
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
if (!isAtStartOfNode(editor.state)) {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
const listItemPos = findListItemPos(name, editor.state);
|
|
774
|
+
if (!listItemPos) {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
|
|
778
|
+
const prevNode = $prev.node(listItemPos.depth);
|
|
779
|
+
const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
|
|
780
|
+
if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {
|
|
781
|
+
return editor.commands.joinItemBackward();
|
|
782
|
+
}
|
|
783
|
+
return editor.chain().liftListItem(name).run();
|
|
784
|
+
};
|
|
785
|
+
var nextListIsDeeper = (typeOrName, state) => {
|
|
786
|
+
const listDepth = getNextListDepth(typeOrName, state);
|
|
787
|
+
const listItemPos = findListItemPos(typeOrName, state);
|
|
788
|
+
if (!listItemPos || !listDepth) {
|
|
789
|
+
return false;
|
|
790
|
+
}
|
|
791
|
+
if (listDepth > listItemPos.depth) {
|
|
792
|
+
return true;
|
|
793
|
+
}
|
|
794
|
+
return false;
|
|
795
|
+
};
|
|
796
|
+
var nextListIsHigher = (typeOrName, state) => {
|
|
797
|
+
const listDepth = getNextListDepth(typeOrName, state);
|
|
798
|
+
const listItemPos = findListItemPos(typeOrName, state);
|
|
799
|
+
if (!listItemPos || !listDepth) {
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
if (listDepth < listItemPos.depth) {
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
return false;
|
|
806
|
+
};
|
|
807
|
+
var handleDelete = (editor, name) => {
|
|
808
|
+
if (!isNodeActive2(editor.state, name)) {
|
|
809
|
+
return false;
|
|
810
|
+
}
|
|
811
|
+
if (!isAtEndOfNode(editor.state, name)) {
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
const { selection } = editor.state;
|
|
815
|
+
const { $from, $to } = selection;
|
|
816
|
+
if (!selection.empty && $from.sameParent($to)) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
if (nextListIsDeeper(name, editor.state)) {
|
|
820
|
+
return editor.chain().focus(editor.state.selection.from + 4).lift(name).joinBackward().run();
|
|
821
|
+
}
|
|
822
|
+
if (nextListIsHigher(name, editor.state)) {
|
|
823
|
+
return editor.chain().joinForward().joinBackward().run();
|
|
824
|
+
}
|
|
825
|
+
return editor.commands.joinItemForward();
|
|
826
|
+
};
|
|
827
|
+
var hasListItemAfter = (typeOrName, state) => {
|
|
828
|
+
var _a;
|
|
829
|
+
const { $anchor } = state.selection;
|
|
830
|
+
const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2);
|
|
831
|
+
if ($targetPos.index() === $targetPos.parent.childCount - 1) {
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
834
|
+
if (((_a = $targetPos.nodeAfter) == null ? void 0 : _a.type.name) !== typeOrName) {
|
|
835
|
+
return false;
|
|
836
|
+
}
|
|
837
|
+
return true;
|
|
838
|
+
};
|
|
839
|
+
var ListKeymap = Extension.create({
|
|
840
|
+
name: "listKeymap",
|
|
841
|
+
addOptions() {
|
|
842
|
+
return {
|
|
843
|
+
listTypes: [
|
|
844
|
+
{
|
|
845
|
+
itemName: "listItem",
|
|
846
|
+
wrapperNames: ["bulletList", "orderedList"]
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
itemName: "taskItem",
|
|
850
|
+
wrapperNames: ["taskList"]
|
|
851
|
+
}
|
|
852
|
+
]
|
|
853
|
+
};
|
|
854
|
+
},
|
|
855
|
+
addKeyboardShortcuts() {
|
|
856
|
+
return {
|
|
857
|
+
Delete: ({ editor }) => {
|
|
858
|
+
let handled = false;
|
|
859
|
+
this.options.listTypes.forEach(({ itemName }) => {
|
|
860
|
+
if (editor.state.schema.nodes[itemName] === void 0) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (handleDelete(editor, itemName)) {
|
|
864
|
+
handled = true;
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
return handled;
|
|
868
|
+
},
|
|
869
|
+
"Mod-Delete": ({ editor }) => {
|
|
870
|
+
let handled = false;
|
|
871
|
+
this.options.listTypes.forEach(({ itemName }) => {
|
|
872
|
+
if (editor.state.schema.nodes[itemName] === void 0) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
if (handleDelete(editor, itemName)) {
|
|
876
|
+
handled = true;
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
return handled;
|
|
880
|
+
},
|
|
881
|
+
Backspace: ({ editor }) => {
|
|
882
|
+
let handled = false;
|
|
883
|
+
this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
|
|
884
|
+
if (editor.state.schema.nodes[itemName] === void 0) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (handleBackspace(editor, itemName, wrapperNames)) {
|
|
888
|
+
handled = true;
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
return handled;
|
|
892
|
+
},
|
|
893
|
+
"Mod-Backspace": ({ editor }) => {
|
|
894
|
+
let handled = false;
|
|
895
|
+
this.options.listTypes.forEach(({ itemName, wrapperNames }) => {
|
|
896
|
+
if (editor.state.schema.nodes[itemName] === void 0) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (handleBackspace(editor, itemName, wrapperNames)) {
|
|
900
|
+
handled = true;
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
return handled;
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
var ORDERED_LIST_ITEM_REGEX = /^(\s*)(\d+)\.\s+(.*)$/;
|
|
909
|
+
var INDENTED_LINE_REGEX = /^\s/;
|
|
910
|
+
function isBlockContentLine(line) {
|
|
911
|
+
const trimmedLine = line.trimStart();
|
|
912
|
+
return /^[-+*]\s+/.test(trimmedLine) || /^\d+\.\s+/.test(trimmedLine) || /^>\s?/.test(trimmedLine) || /^```/.test(trimmedLine) || /^~~~/.test(trimmedLine);
|
|
913
|
+
}
|
|
914
|
+
function splitItemContent(contentLines) {
|
|
915
|
+
const paragraphLines = [];
|
|
916
|
+
const blockLines = [];
|
|
917
|
+
let reachedBlockBoundary = false;
|
|
918
|
+
contentLines.forEach((line) => {
|
|
919
|
+
if (reachedBlockBoundary) {
|
|
920
|
+
blockLines.push(line);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
if (line.trim() === "") {
|
|
924
|
+
reachedBlockBoundary = true;
|
|
925
|
+
blockLines.push(line);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (paragraphLines.length > 0 && isBlockContentLine(line)) {
|
|
929
|
+
reachedBlockBoundary = true;
|
|
930
|
+
blockLines.push(line);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
paragraphLines.push(line);
|
|
934
|
+
});
|
|
935
|
+
return {
|
|
936
|
+
paragraphLines,
|
|
937
|
+
blockLines
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
function collectOrderedListItems(lines) {
|
|
941
|
+
const listItems = [];
|
|
942
|
+
let currentLineIndex = 0;
|
|
943
|
+
let consumed = 0;
|
|
944
|
+
while (currentLineIndex < lines.length) {
|
|
945
|
+
const line = lines[currentLineIndex];
|
|
946
|
+
const match = line.match(ORDERED_LIST_ITEM_REGEX);
|
|
947
|
+
if (!match) {
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
const [, indent, number, content] = match;
|
|
951
|
+
const indentLevel = indent.length;
|
|
952
|
+
const itemContentLines = [content];
|
|
953
|
+
let nextLineIndex = currentLineIndex + 1;
|
|
954
|
+
const itemLines = [line];
|
|
955
|
+
let sawBlankLine = false;
|
|
956
|
+
while (nextLineIndex < lines.length) {
|
|
957
|
+
const nextLine = lines[nextLineIndex];
|
|
958
|
+
const nextMatch = nextLine.match(ORDERED_LIST_ITEM_REGEX);
|
|
959
|
+
if (nextMatch) {
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
if (nextLine.trim() === "") {
|
|
963
|
+
itemLines.push(nextLine);
|
|
964
|
+
itemContentLines.push("");
|
|
965
|
+
sawBlankLine = true;
|
|
966
|
+
nextLineIndex += 1;
|
|
967
|
+
} else if (nextLine.match(INDENTED_LINE_REGEX)) {
|
|
968
|
+
itemLines.push(nextLine);
|
|
969
|
+
itemContentLines.push(nextLine.slice(indentLevel + 2));
|
|
970
|
+
nextLineIndex += 1;
|
|
971
|
+
} else {
|
|
972
|
+
if (sawBlankLine) {
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
itemLines.push(nextLine);
|
|
976
|
+
itemContentLines.push(nextLine);
|
|
977
|
+
nextLineIndex += 1;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
listItems.push({
|
|
981
|
+
indent: indentLevel,
|
|
982
|
+
number: parseInt(number, 10),
|
|
983
|
+
content: itemContentLines.join("\n").trim(),
|
|
984
|
+
contentLines: itemContentLines,
|
|
985
|
+
raw: itemLines.join("\n")
|
|
986
|
+
});
|
|
987
|
+
consumed = nextLineIndex;
|
|
988
|
+
currentLineIndex = nextLineIndex;
|
|
989
|
+
}
|
|
990
|
+
return [listItems, consumed];
|
|
991
|
+
}
|
|
992
|
+
function buildNestedStructure(items, baseIndent, lexer) {
|
|
993
|
+
const result = [];
|
|
994
|
+
let currentIndex = 0;
|
|
995
|
+
while (currentIndex < items.length) {
|
|
996
|
+
const item = items[currentIndex];
|
|
997
|
+
if (item.indent === baseIndent) {
|
|
998
|
+
const { paragraphLines, blockLines } = splitItemContent(item.contentLines);
|
|
999
|
+
const mainText = paragraphLines.join("\n").trim();
|
|
1000
|
+
const tokens = [];
|
|
1001
|
+
if (mainText) {
|
|
1002
|
+
tokens.push({
|
|
1003
|
+
type: "paragraph",
|
|
1004
|
+
raw: mainText,
|
|
1005
|
+
tokens: lexer.inlineTokens(mainText)
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
const additionalContent = blockLines.join("\n").trim();
|
|
1009
|
+
if (additionalContent) {
|
|
1010
|
+
const blockTokens = lexer.blockTokens(additionalContent);
|
|
1011
|
+
tokens.push(...blockTokens);
|
|
1012
|
+
}
|
|
1013
|
+
let lookAheadIndex = currentIndex + 1;
|
|
1014
|
+
const nestedItems = [];
|
|
1015
|
+
while (lookAheadIndex < items.length && items[lookAheadIndex].indent > baseIndent) {
|
|
1016
|
+
nestedItems.push(items[lookAheadIndex]);
|
|
1017
|
+
lookAheadIndex += 1;
|
|
1018
|
+
}
|
|
1019
|
+
if (nestedItems.length > 0) {
|
|
1020
|
+
const nextIndent = Math.min(...nestedItems.map((nestedItem) => nestedItem.indent));
|
|
1021
|
+
const nestedListItems = buildNestedStructure(nestedItems, nextIndent, lexer);
|
|
1022
|
+
tokens.push({
|
|
1023
|
+
type: "list",
|
|
1024
|
+
ordered: true,
|
|
1025
|
+
start: nestedItems[0].number,
|
|
1026
|
+
items: nestedListItems,
|
|
1027
|
+
raw: nestedItems.map((nestedItem) => nestedItem.raw).join("\n")
|
|
1028
|
+
});
|
|
1029
|
+
}
|
|
1030
|
+
result.push({
|
|
1031
|
+
type: "list_item",
|
|
1032
|
+
raw: item.raw,
|
|
1033
|
+
tokens
|
|
1034
|
+
});
|
|
1035
|
+
currentIndex = lookAheadIndex;
|
|
1036
|
+
} else {
|
|
1037
|
+
currentIndex += 1;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
return result;
|
|
1041
|
+
}
|
|
1042
|
+
function parseListItems(items, helpers) {
|
|
1043
|
+
return items.map((item) => {
|
|
1044
|
+
if (item.type !== "list_item") {
|
|
1045
|
+
return helpers.parseChildren([item])[0];
|
|
1046
|
+
}
|
|
1047
|
+
const content = [];
|
|
1048
|
+
if (item.tokens && item.tokens.length > 0) {
|
|
1049
|
+
item.tokens.forEach((itemToken) => {
|
|
1050
|
+
if (itemToken.type === "paragraph" || itemToken.type === "list" || itemToken.type === "blockquote" || itemToken.type === "code") {
|
|
1051
|
+
content.push(...helpers.parseChildren([itemToken]));
|
|
1052
|
+
} else if (itemToken.type === "text" && itemToken.tokens) {
|
|
1053
|
+
const inlineContent = helpers.parseChildren([itemToken]);
|
|
1054
|
+
content.push({
|
|
1055
|
+
type: "paragraph",
|
|
1056
|
+
content: inlineContent
|
|
1057
|
+
});
|
|
1058
|
+
} else {
|
|
1059
|
+
const parsed = helpers.parseChildren([itemToken]);
|
|
1060
|
+
if (parsed.length > 0) {
|
|
1061
|
+
content.push(...parsed);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
return {
|
|
1067
|
+
type: "listItem",
|
|
1068
|
+
content
|
|
1069
|
+
};
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
var ListItemName2 = "listItem";
|
|
1073
|
+
var TextStyleName2 = "textStyle";
|
|
1074
|
+
var orderedListInputRegex = /^(\d+)\.\s$/;
|
|
1075
|
+
var OrderedList = Node3.create({
|
|
1076
|
+
name: "orderedList",
|
|
1077
|
+
addOptions() {
|
|
1078
|
+
return {
|
|
1079
|
+
itemTypeName: "listItem",
|
|
1080
|
+
HTMLAttributes: {},
|
|
1081
|
+
keepMarks: false,
|
|
1082
|
+
keepAttributes: false
|
|
1083
|
+
};
|
|
1084
|
+
},
|
|
1085
|
+
group: "block list",
|
|
1086
|
+
content() {
|
|
1087
|
+
return `${this.options.itemTypeName}+`;
|
|
1088
|
+
},
|
|
1089
|
+
addAttributes() {
|
|
1090
|
+
return {
|
|
1091
|
+
start: {
|
|
1092
|
+
default: 1,
|
|
1093
|
+
parseHTML: (element) => {
|
|
1094
|
+
return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
|
|
1095
|
+
}
|
|
1096
|
+
},
|
|
1097
|
+
type: {
|
|
1098
|
+
default: null,
|
|
1099
|
+
parseHTML: (element) => element.getAttribute("type")
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
},
|
|
1103
|
+
parseHTML() {
|
|
1104
|
+
return [
|
|
1105
|
+
{
|
|
1106
|
+
tag: "ol"
|
|
1107
|
+
}
|
|
1108
|
+
];
|
|
1109
|
+
},
|
|
1110
|
+
renderHTML({ HTMLAttributes }) {
|
|
1111
|
+
const { start, ...attributesWithoutStart } = HTMLAttributes;
|
|
1112
|
+
return start === 1 ? ["ol", mergeAttributes3(this.options.HTMLAttributes, attributesWithoutStart), 0] : ["ol", mergeAttributes3(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
1113
|
+
},
|
|
1114
|
+
markdownTokenName: "list",
|
|
1115
|
+
parseMarkdown: (token, helpers) => {
|
|
1116
|
+
if (token.type !== "list" || !token.ordered) {
|
|
1117
|
+
return [];
|
|
1118
|
+
}
|
|
1119
|
+
const startValue = token.start || 1;
|
|
1120
|
+
const content = token.items ? parseListItems(token.items, helpers) : [];
|
|
1121
|
+
if (startValue !== 1) {
|
|
1122
|
+
return {
|
|
1123
|
+
type: "orderedList",
|
|
1124
|
+
attrs: { start: startValue },
|
|
1125
|
+
content
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
return {
|
|
1129
|
+
type: "orderedList",
|
|
1130
|
+
content
|
|
1131
|
+
};
|
|
1132
|
+
},
|
|
1133
|
+
renderMarkdown: (node, h) => {
|
|
1134
|
+
if (!node.content) {
|
|
1135
|
+
return "";
|
|
1136
|
+
}
|
|
1137
|
+
return h.renderChildren(node.content, "\n");
|
|
1138
|
+
},
|
|
1139
|
+
markdownTokenizer: {
|
|
1140
|
+
name: "orderedList",
|
|
1141
|
+
level: "block",
|
|
1142
|
+
start: (src) => {
|
|
1143
|
+
const match = src.match(/^(\s*)(\d+)\.\s+/);
|
|
1144
|
+
const index = match == null ? void 0 : match.index;
|
|
1145
|
+
return index !== void 0 ? index : -1;
|
|
1146
|
+
},
|
|
1147
|
+
tokenize: (src, _tokens, lexer) => {
|
|
1148
|
+
var _a;
|
|
1149
|
+
const lines = src.split("\n");
|
|
1150
|
+
const [listItems, consumed] = collectOrderedListItems(lines);
|
|
1151
|
+
if (listItems.length === 0) {
|
|
1152
|
+
return void 0;
|
|
1153
|
+
}
|
|
1154
|
+
const items = buildNestedStructure(listItems, 0, lexer);
|
|
1155
|
+
if (items.length === 0) {
|
|
1156
|
+
return void 0;
|
|
1157
|
+
}
|
|
1158
|
+
const startValue = ((_a = listItems[0]) == null ? void 0 : _a.number) || 1;
|
|
1159
|
+
return {
|
|
1160
|
+
type: "list",
|
|
1161
|
+
ordered: true,
|
|
1162
|
+
start: startValue,
|
|
1163
|
+
items,
|
|
1164
|
+
raw: lines.slice(0, consumed).join("\n")
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
},
|
|
1168
|
+
markdownOptions: {
|
|
1169
|
+
indentsContent: true
|
|
1170
|
+
},
|
|
1171
|
+
addCommands() {
|
|
1172
|
+
return {
|
|
1173
|
+
toggleOrderedList: () => ({ commands, chain }) => {
|
|
1174
|
+
if (this.options.keepAttributes) {
|
|
1175
|
+
return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName2, this.editor.getAttributes(TextStyleName2)).run();
|
|
1176
|
+
}
|
|
1177
|
+
return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks);
|
|
1178
|
+
}
|
|
1179
|
+
};
|
|
1180
|
+
},
|
|
1181
|
+
addKeyboardShortcuts() {
|
|
1182
|
+
return {
|
|
1183
|
+
"Mod-Shift-7": () => this.editor.commands.toggleOrderedList()
|
|
1184
|
+
};
|
|
1185
|
+
},
|
|
1186
|
+
addInputRules() {
|
|
1187
|
+
let inputRule = wrappingInputRule22({
|
|
1188
|
+
find: orderedListInputRegex,
|
|
1189
|
+
type: this.type,
|
|
1190
|
+
getAttributes: (match) => ({ start: +match[1] }),
|
|
1191
|
+
joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1]
|
|
1192
|
+
});
|
|
1193
|
+
if (this.options.keepMarks || this.options.keepAttributes) {
|
|
1194
|
+
inputRule = wrappingInputRule22({
|
|
1195
|
+
find: orderedListInputRegex,
|
|
1196
|
+
type: this.type,
|
|
1197
|
+
keepMarks: this.options.keepMarks,
|
|
1198
|
+
keepAttributes: this.options.keepAttributes,
|
|
1199
|
+
getAttributes: (match) => ({ start: +match[1], ...this.editor.getAttributes(TextStyleName2) }),
|
|
1200
|
+
joinPredicate: (match, node) => node.childCount + node.attrs.start === +match[1],
|
|
1201
|
+
editor: this.editor
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
return [inputRule];
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
var inputRegex2 = /^\s*(\[([( |x])?\])\s$/;
|
|
1208
|
+
var TaskItem = Node4.create({
|
|
1209
|
+
name: "taskItem",
|
|
1210
|
+
addOptions() {
|
|
1211
|
+
return {
|
|
1212
|
+
nested: false,
|
|
1213
|
+
HTMLAttributes: {},
|
|
1214
|
+
taskListTypeName: "taskList",
|
|
1215
|
+
a11y: void 0
|
|
1216
|
+
};
|
|
1217
|
+
},
|
|
1218
|
+
content() {
|
|
1219
|
+
return this.options.nested ? "paragraph block*" : "paragraph+";
|
|
1220
|
+
},
|
|
1221
|
+
defining: true,
|
|
1222
|
+
addAttributes() {
|
|
1223
|
+
return {
|
|
1224
|
+
checked: {
|
|
1225
|
+
default: false,
|
|
1226
|
+
keepOnSplit: false,
|
|
1227
|
+
parseHTML: (element) => {
|
|
1228
|
+
const dataChecked = element.getAttribute("data-checked");
|
|
1229
|
+
return dataChecked === "" || dataChecked === "true";
|
|
1230
|
+
},
|
|
1231
|
+
renderHTML: (attributes) => ({
|
|
1232
|
+
"data-checked": attributes.checked
|
|
1233
|
+
})
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
},
|
|
1237
|
+
parseHTML() {
|
|
1238
|
+
return [
|
|
1239
|
+
{
|
|
1240
|
+
tag: `li[data-type="${this.name}"]`,
|
|
1241
|
+
priority: 51
|
|
1242
|
+
}
|
|
1243
|
+
];
|
|
1244
|
+
},
|
|
1245
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1246
|
+
return [
|
|
1247
|
+
"li",
|
|
1248
|
+
mergeAttributes4(this.options.HTMLAttributes, HTMLAttributes, {
|
|
1249
|
+
"data-type": this.name
|
|
1250
|
+
}),
|
|
1251
|
+
[
|
|
1252
|
+
"label",
|
|
1253
|
+
[
|
|
1254
|
+
"input",
|
|
1255
|
+
{
|
|
1256
|
+
type: "checkbox",
|
|
1257
|
+
checked: node.attrs.checked ? "checked" : null
|
|
1258
|
+
}
|
|
1259
|
+
],
|
|
1260
|
+
["span"]
|
|
1261
|
+
],
|
|
1262
|
+
["div", 0]
|
|
1263
|
+
];
|
|
1264
|
+
},
|
|
1265
|
+
parseMarkdown: (token, h) => {
|
|
1266
|
+
const content = [];
|
|
1267
|
+
if (token.tokens && token.tokens.length > 0) {
|
|
1268
|
+
content.push(h.createNode("paragraph", {}, h.parseInline(token.tokens)));
|
|
1269
|
+
} else if (token.text) {
|
|
1270
|
+
content.push(h.createNode("paragraph", {}, [h.createNode("text", { text: token.text })]));
|
|
1271
|
+
} else {
|
|
1272
|
+
content.push(h.createNode("paragraph", {}, []));
|
|
1273
|
+
}
|
|
1274
|
+
if (token.nestedTokens && token.nestedTokens.length > 0) {
|
|
1275
|
+
const nestedContent = h.parseChildren(token.nestedTokens);
|
|
1276
|
+
content.push(...nestedContent);
|
|
1277
|
+
}
|
|
1278
|
+
return h.createNode("taskItem", { checked: token.checked || false }, content);
|
|
1279
|
+
},
|
|
1280
|
+
renderMarkdown: (node, h) => {
|
|
1281
|
+
var _a;
|
|
1282
|
+
const checkedChar = ((_a = node.attrs) == null ? void 0 : _a.checked) ? "x" : " ";
|
|
1283
|
+
const prefix = `- [${checkedChar}] `;
|
|
1284
|
+
return renderNestedMarkdownContent2(node, h, prefix);
|
|
1285
|
+
},
|
|
1286
|
+
addKeyboardShortcuts() {
|
|
1287
|
+
const shortcuts = {
|
|
1288
|
+
Enter: () => this.editor.commands.splitListItem(this.name),
|
|
1289
|
+
"Shift-Tab": () => this.editor.commands.liftListItem(this.name)
|
|
1290
|
+
};
|
|
1291
|
+
if (!this.options.nested) {
|
|
1292
|
+
return shortcuts;
|
|
1293
|
+
}
|
|
1294
|
+
return {
|
|
1295
|
+
...shortcuts,
|
|
1296
|
+
Tab: () => this.editor.commands.sinkListItem(this.name)
|
|
1297
|
+
};
|
|
1298
|
+
},
|
|
1299
|
+
addNodeView() {
|
|
1300
|
+
return ({ node, HTMLAttributes, getPos, editor }) => {
|
|
1301
|
+
const listItem = document.createElement("li");
|
|
1302
|
+
const checkboxWrapper = document.createElement("label");
|
|
1303
|
+
const checkboxStyler = document.createElement("span");
|
|
1304
|
+
const checkbox = document.createElement("input");
|
|
1305
|
+
const content = document.createElement("div");
|
|
1306
|
+
const updateA11Y = (currentNode) => {
|
|
1307
|
+
var _a, _b;
|
|
1308
|
+
checkbox.ariaLabel = ((_b = (_a = this.options.a11y) == null ? void 0 : _a.checkboxLabel) == null ? void 0 : _b.call(_a, currentNode, checkbox.checked)) || `Task item checkbox for ${currentNode.textContent || "empty task item"}`;
|
|
1309
|
+
};
|
|
1310
|
+
updateA11Y(node);
|
|
1311
|
+
checkboxWrapper.contentEditable = "false";
|
|
1312
|
+
checkbox.type = "checkbox";
|
|
1313
|
+
checkbox.addEventListener("mousedown", (event) => event.preventDefault());
|
|
1314
|
+
checkbox.addEventListener("change", (event) => {
|
|
1315
|
+
if (!editor.isEditable && !this.options.onReadOnlyChecked) {
|
|
1316
|
+
checkbox.checked = !checkbox.checked;
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const { checked } = event.target;
|
|
1320
|
+
if (editor.isEditable && typeof getPos === "function") {
|
|
1321
|
+
editor.chain().focus(void 0, { scrollIntoView: false }).command(({ tr }) => {
|
|
1322
|
+
const position = getPos();
|
|
1323
|
+
if (typeof position !== "number") {
|
|
1324
|
+
return false;
|
|
1325
|
+
}
|
|
1326
|
+
const currentNode = tr.doc.nodeAt(position);
|
|
1327
|
+
tr.setNodeMarkup(position, void 0, {
|
|
1328
|
+
...currentNode == null ? void 0 : currentNode.attrs,
|
|
1329
|
+
checked
|
|
1330
|
+
});
|
|
1331
|
+
return true;
|
|
1332
|
+
}).run();
|
|
1333
|
+
}
|
|
1334
|
+
if (!editor.isEditable && this.options.onReadOnlyChecked) {
|
|
1335
|
+
if (!this.options.onReadOnlyChecked(node, checked)) {
|
|
1336
|
+
checkbox.checked = !checkbox.checked;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => {
|
|
1341
|
+
listItem.setAttribute(key, value);
|
|
1342
|
+
});
|
|
1343
|
+
listItem.dataset.checked = node.attrs.checked;
|
|
1344
|
+
checkbox.checked = node.attrs.checked;
|
|
1345
|
+
checkboxWrapper.append(checkbox, checkboxStyler);
|
|
1346
|
+
listItem.append(checkboxWrapper, content);
|
|
1347
|
+
Object.entries(HTMLAttributes).forEach(([key, value]) => {
|
|
1348
|
+
listItem.setAttribute(key, value);
|
|
1349
|
+
});
|
|
1350
|
+
let prevRenderedAttributeKeys = new Set(Object.keys(HTMLAttributes));
|
|
1351
|
+
return {
|
|
1352
|
+
dom: listItem,
|
|
1353
|
+
contentDOM: content,
|
|
1354
|
+
update: (updatedNode) => {
|
|
1355
|
+
if (updatedNode.type !== this.type) {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
listItem.dataset.checked = updatedNode.attrs.checked;
|
|
1359
|
+
checkbox.checked = updatedNode.attrs.checked;
|
|
1360
|
+
updateA11Y(updatedNode);
|
|
1361
|
+
const extensionAttributes = editor.extensionManager.attributes;
|
|
1362
|
+
const newHTMLAttributes = getRenderedAttributes(updatedNode, extensionAttributes);
|
|
1363
|
+
const newKeys = new Set(Object.keys(newHTMLAttributes));
|
|
1364
|
+
const staticAttrs = this.options.HTMLAttributes;
|
|
1365
|
+
prevRenderedAttributeKeys.forEach((key) => {
|
|
1366
|
+
if (!newKeys.has(key)) {
|
|
1367
|
+
if (key in staticAttrs) {
|
|
1368
|
+
listItem.setAttribute(key, staticAttrs[key]);
|
|
1369
|
+
} else {
|
|
1370
|
+
listItem.removeAttribute(key);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
Object.entries(newHTMLAttributes).forEach(([key, value]) => {
|
|
1375
|
+
if (value === null || value === void 0) {
|
|
1376
|
+
if (key in staticAttrs) {
|
|
1377
|
+
listItem.setAttribute(key, staticAttrs[key]);
|
|
1378
|
+
} else {
|
|
1379
|
+
listItem.removeAttribute(key);
|
|
1380
|
+
}
|
|
1381
|
+
} else {
|
|
1382
|
+
listItem.setAttribute(key, value);
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
prevRenderedAttributeKeys = newKeys;
|
|
1386
|
+
return true;
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
};
|
|
1390
|
+
},
|
|
1391
|
+
addInputRules() {
|
|
1392
|
+
return [
|
|
1393
|
+
wrappingInputRule3({
|
|
1394
|
+
find: inputRegex2,
|
|
1395
|
+
type: this.type,
|
|
1396
|
+
getAttributes: (match) => ({
|
|
1397
|
+
checked: match[match.length - 1] === "x"
|
|
1398
|
+
})
|
|
1399
|
+
})
|
|
1400
|
+
];
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
var TaskList = Node5.create({
|
|
1404
|
+
name: "taskList",
|
|
1405
|
+
addOptions() {
|
|
1406
|
+
return {
|
|
1407
|
+
itemTypeName: "taskItem",
|
|
1408
|
+
HTMLAttributes: {}
|
|
1409
|
+
};
|
|
1410
|
+
},
|
|
1411
|
+
group: "block list",
|
|
1412
|
+
content() {
|
|
1413
|
+
return `${this.options.itemTypeName}+`;
|
|
1414
|
+
},
|
|
1415
|
+
parseHTML() {
|
|
1416
|
+
return [
|
|
1417
|
+
{
|
|
1418
|
+
tag: `ul[data-type="${this.name}"]`,
|
|
1419
|
+
priority: 51
|
|
1420
|
+
}
|
|
1421
|
+
];
|
|
1422
|
+
},
|
|
1423
|
+
renderHTML({ HTMLAttributes }) {
|
|
1424
|
+
return ["ul", mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
|
|
1425
|
+
},
|
|
1426
|
+
parseMarkdown: (token, h) => {
|
|
1427
|
+
return h.createNode("taskList", {}, h.parseChildren(token.items || []));
|
|
1428
|
+
},
|
|
1429
|
+
renderMarkdown: (node, h) => {
|
|
1430
|
+
if (!node.content) {
|
|
1431
|
+
return "";
|
|
1432
|
+
}
|
|
1433
|
+
return h.renderChildren(node.content, "\n");
|
|
1434
|
+
},
|
|
1435
|
+
markdownTokenizer: {
|
|
1436
|
+
name: "taskList",
|
|
1437
|
+
level: "block",
|
|
1438
|
+
start(src) {
|
|
1439
|
+
var _a;
|
|
1440
|
+
const index = (_a = src.match(/^\s*[-+*]\s+\[([ xX])\]\s+/)) == null ? void 0 : _a.index;
|
|
1441
|
+
return index !== void 0 ? index : -1;
|
|
1442
|
+
},
|
|
1443
|
+
tokenize(src, tokens, lexer) {
|
|
1444
|
+
const parseTaskListContent = (content) => {
|
|
1445
|
+
const nestedResult = parseIndentedBlocks(
|
|
1446
|
+
content,
|
|
1447
|
+
{
|
|
1448
|
+
itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
|
|
1449
|
+
extractItemData: (match) => ({
|
|
1450
|
+
indentLevel: match[1].length,
|
|
1451
|
+
mainContent: match[4],
|
|
1452
|
+
checked: match[3].toLowerCase() === "x"
|
|
1453
|
+
}),
|
|
1454
|
+
createToken: (data, nestedTokens) => ({
|
|
1455
|
+
type: "taskItem",
|
|
1456
|
+
raw: "",
|
|
1457
|
+
mainContent: data.mainContent,
|
|
1458
|
+
indentLevel: data.indentLevel,
|
|
1459
|
+
checked: data.checked,
|
|
1460
|
+
text: data.mainContent,
|
|
1461
|
+
tokens: lexer.inlineTokens(data.mainContent),
|
|
1462
|
+
nestedTokens
|
|
1463
|
+
}),
|
|
1464
|
+
// Allow recursive nesting
|
|
1465
|
+
customNestedParser: parseTaskListContent
|
|
1466
|
+
},
|
|
1467
|
+
lexer
|
|
1468
|
+
);
|
|
1469
|
+
if (nestedResult) {
|
|
1470
|
+
return [
|
|
1471
|
+
{
|
|
1472
|
+
type: "taskList",
|
|
1473
|
+
raw: nestedResult.raw,
|
|
1474
|
+
items: nestedResult.items
|
|
1475
|
+
}
|
|
1476
|
+
];
|
|
1477
|
+
}
|
|
1478
|
+
return lexer.blockTokens(content);
|
|
1479
|
+
};
|
|
1480
|
+
const result = parseIndentedBlocks(
|
|
1481
|
+
src,
|
|
1482
|
+
{
|
|
1483
|
+
itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/,
|
|
1484
|
+
extractItemData: (match) => ({
|
|
1485
|
+
indentLevel: match[1].length,
|
|
1486
|
+
mainContent: match[4],
|
|
1487
|
+
checked: match[3].toLowerCase() === "x"
|
|
1488
|
+
}),
|
|
1489
|
+
createToken: (data, nestedTokens) => ({
|
|
1490
|
+
type: "taskItem",
|
|
1491
|
+
raw: "",
|
|
1492
|
+
mainContent: data.mainContent,
|
|
1493
|
+
indentLevel: data.indentLevel,
|
|
1494
|
+
checked: data.checked,
|
|
1495
|
+
text: data.mainContent,
|
|
1496
|
+
tokens: lexer.inlineTokens(data.mainContent),
|
|
1497
|
+
nestedTokens
|
|
1498
|
+
}),
|
|
1499
|
+
// Use the recursive parser for nested content
|
|
1500
|
+
customNestedParser: parseTaskListContent
|
|
1501
|
+
},
|
|
1502
|
+
lexer
|
|
1503
|
+
);
|
|
1504
|
+
if (!result) {
|
|
1505
|
+
return void 0;
|
|
365
1506
|
}
|
|
366
|
-
|
|
1507
|
+
return {
|
|
1508
|
+
type: "taskList",
|
|
1509
|
+
raw: result.raw,
|
|
1510
|
+
items: result.items
|
|
1511
|
+
};
|
|
367
1512
|
}
|
|
1513
|
+
},
|
|
1514
|
+
markdownOptions: {
|
|
1515
|
+
indentsContent: true
|
|
1516
|
+
},
|
|
1517
|
+
addCommands() {
|
|
1518
|
+
return {
|
|
1519
|
+
toggleTaskList: () => ({ commands }) => {
|
|
1520
|
+
return commands.toggleList(this.name, this.options.itemTypeName);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
},
|
|
1524
|
+
addKeyboardShortcuts() {
|
|
1525
|
+
return {
|
|
1526
|
+
"Mod-Shift-9": () => this.editor.commands.toggleTaskList()
|
|
1527
|
+
};
|
|
368
1528
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
1529
|
+
});
|
|
1530
|
+
var ListKit = Extension2.create({
|
|
1531
|
+
name: "listKit",
|
|
1532
|
+
addExtensions() {
|
|
1533
|
+
const extensions = [];
|
|
1534
|
+
if (this.options.bulletList !== false) {
|
|
1535
|
+
extensions.push(BulletList.configure(this.options.bulletList));
|
|
1536
|
+
}
|
|
1537
|
+
if (this.options.listItem !== false) {
|
|
1538
|
+
extensions.push(ListItem.configure(this.options.listItem));
|
|
1539
|
+
}
|
|
1540
|
+
if (this.options.listKeymap !== false) {
|
|
1541
|
+
extensions.push(ListKeymap.configure(this.options.listKeymap));
|
|
1542
|
+
}
|
|
1543
|
+
if (this.options.orderedList !== false) {
|
|
1544
|
+
extensions.push(OrderedList.configure(this.options.orderedList));
|
|
1545
|
+
}
|
|
1546
|
+
if (this.options.taskItem !== false) {
|
|
1547
|
+
extensions.push(TaskItem.configure(this.options.taskItem));
|
|
1548
|
+
}
|
|
1549
|
+
if (this.options.taskList !== false) {
|
|
1550
|
+
extensions.push(TaskList.configure(this.options.taskList));
|
|
1551
|
+
}
|
|
1552
|
+
return extensions;
|
|
372
1553
|
}
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
// src/roomState.ts
|
|
376
|
-
import WebSocket from "ws";
|
|
1554
|
+
});
|
|
377
1555
|
|
|
378
1556
|
// ../shared/src/editor-extensions.ts
|
|
379
|
-
import
|
|
380
|
-
import
|
|
381
|
-
import CodeBlock from "@tiptap/extension-code-block";
|
|
382
|
-
import TaskList from "@tiptap/extension-task-list";
|
|
383
|
-
import TaskItem from "@tiptap/extension-task-item";
|
|
1557
|
+
import TaskList2 from "@tiptap/extension-task-list";
|
|
1558
|
+
import TaskItem2 from "@tiptap/extension-task-item";
|
|
384
1559
|
import Highlight from "@tiptap/extension-highlight";
|
|
385
1560
|
import Subscript from "@tiptap/extension-subscript";
|
|
386
1561
|
import Superscript from "@tiptap/extension-superscript";
|
|
@@ -479,7 +1654,7 @@ var ImageBlockExtension = Image.extend({
|
|
|
479
1654
|
var imageBlockExtensions_default = ImageBlockExtension;
|
|
480
1655
|
|
|
481
1656
|
// ../shared/src/videoBlockNode.ts
|
|
482
|
-
import { Node, mergeAttributes } from "@tiptap/core";
|
|
1657
|
+
import { Node as Node6, mergeAttributes as mergeAttributes6 } from "@tiptap/core";
|
|
483
1658
|
|
|
484
1659
|
// ../shared/src/embedParser.ts
|
|
485
1660
|
var YOUTUBE_HOSTS = /* @__PURE__ */ new Set([
|
|
@@ -581,7 +1756,7 @@ function detectVideoProvider(url) {
|
|
|
581
1756
|
}
|
|
582
1757
|
}
|
|
583
1758
|
var IFRAME_SANDBOX = "allow-scripts allow-same-origin allow-presentation allow-popups";
|
|
584
|
-
var VideoBlockNode =
|
|
1759
|
+
var VideoBlockNode = Node6.create({
|
|
585
1760
|
name: "videoBlock",
|
|
586
1761
|
group: "block",
|
|
587
1762
|
addOptions() {
|
|
@@ -696,7 +1871,7 @@ var VideoBlockNode = Node.create({
|
|
|
696
1871
|
renderHTML({ node, HTMLAttributes }) {
|
|
697
1872
|
const attrs = node.attrs;
|
|
698
1873
|
if (attrs.src) {
|
|
699
|
-
const merged =
|
|
1874
|
+
const merged = mergeAttributes6(HTMLAttributes, {
|
|
700
1875
|
src: attrs.src,
|
|
701
1876
|
controls: ""
|
|
702
1877
|
});
|
|
@@ -707,7 +1882,7 @@ var VideoBlockNode = Node.create({
|
|
|
707
1882
|
return ["video", merged];
|
|
708
1883
|
}
|
|
709
1884
|
if (attrs.embedUrl) {
|
|
710
|
-
const merged =
|
|
1885
|
+
const merged = mergeAttributes6(HTMLAttributes, {
|
|
711
1886
|
src: attrs.embedUrl,
|
|
712
1887
|
sandbox: IFRAME_SANDBOX,
|
|
713
1888
|
frameborder: "0",
|
|
@@ -720,7 +1895,7 @@ var VideoBlockNode = Node.create({
|
|
|
720
1895
|
}
|
|
721
1896
|
return ["iframe", merged];
|
|
722
1897
|
}
|
|
723
|
-
return ["div",
|
|
1898
|
+
return ["div", mergeAttributes6(HTMLAttributes, { "data-video-error": "missing-src" })];
|
|
724
1899
|
},
|
|
725
1900
|
/**
|
|
726
1901
|
* Link-first markdown serialization (Phase 2 Unit 3):
|
|
@@ -771,7 +1946,7 @@ function filenameFromUrl(url) {
|
|
|
771
1946
|
var videoBlockNode_default = VideoBlockNode;
|
|
772
1947
|
|
|
773
1948
|
// ../shared/src/mediaKeyboardNav.ts
|
|
774
|
-
import { Extension } from "@tiptap/core";
|
|
1949
|
+
import { Extension as Extension3 } from "@tiptap/core";
|
|
775
1950
|
|
|
776
1951
|
// ../node_modules/prosemirror-model/dist/index.js
|
|
777
1952
|
function findDiffStart(a, b, pos) {
|
|
@@ -1805,7 +2980,7 @@ var NodeRange = class {
|
|
|
1805
2980
|
}
|
|
1806
2981
|
};
|
|
1807
2982
|
var emptyAttrs = /* @__PURE__ */ Object.create(null);
|
|
1808
|
-
var
|
|
2983
|
+
var Node7 = class _Node {
|
|
1809
2984
|
/**
|
|
1810
2985
|
@internal
|
|
1811
2986
|
*/
|
|
@@ -2206,7 +3381,7 @@ var Node2 = class _Node {
|
|
|
2206
3381
|
return node;
|
|
2207
3382
|
}
|
|
2208
3383
|
};
|
|
2209
|
-
|
|
3384
|
+
Node7.prototype.text = void 0;
|
|
2210
3385
|
function wrapMarks(marks, str) {
|
|
2211
3386
|
for (let i = marks.length - 1; i >= 0; i--)
|
|
2212
3387
|
str = marks[i].type.name + "(" + str + ")";
|
|
@@ -3846,7 +5021,7 @@ var MEDIA_NODE_TYPES = /* @__PURE__ */ new Set(["image", "videoBlock"]);
|
|
|
3846
5021
|
function isMediaNodeSelection(sel) {
|
|
3847
5022
|
return sel instanceof NodeSelection && MEDIA_NODE_TYPES.has(sel.node.type.name);
|
|
3848
5023
|
}
|
|
3849
|
-
var MediaKeyboardNav =
|
|
5024
|
+
var MediaKeyboardNav = Extension3.create({
|
|
3850
5025
|
name: "mediaKeyboardNav",
|
|
3851
5026
|
addProseMirrorPlugins() {
|
|
3852
5027
|
return [
|
|
@@ -3890,8 +5065,251 @@ var MediaKeyboardNav = Extension.create({
|
|
|
3890
5065
|
});
|
|
3891
5066
|
var mediaKeyboardNav_default = MediaKeyboardNav;
|
|
3892
5067
|
|
|
5068
|
+
// ../shared/src/inlineIconNode.ts
|
|
5069
|
+
import { Node as Node8, mergeAttributes as mergeAttributes7 } from "@tiptap/core";
|
|
5070
|
+
var INLINE_ICON_ALLOWLIST = [
|
|
5071
|
+
"message-square-plus",
|
|
5072
|
+
"pen-line"
|
|
5073
|
+
];
|
|
5074
|
+
function isInlineIconName(value) {
|
|
5075
|
+
return INLINE_ICON_ALLOWLIST.includes(value);
|
|
5076
|
+
}
|
|
5077
|
+
var INLINE_ICON_NODE_NAME = "inlineIcon";
|
|
5078
|
+
var InlineIconNode = Node8.create({
|
|
5079
|
+
name: INLINE_ICON_NODE_NAME,
|
|
5080
|
+
group: "inline",
|
|
5081
|
+
inline: true,
|
|
5082
|
+
atom: true,
|
|
5083
|
+
selectable: false,
|
|
5084
|
+
addAttributes() {
|
|
5085
|
+
return {
|
|
5086
|
+
name: {
|
|
5087
|
+
default: null,
|
|
5088
|
+
parseHTML: (el) => {
|
|
5089
|
+
const raw = el.getAttribute("data-icon");
|
|
5090
|
+
return raw && isInlineIconName(raw) ? raw : null;
|
|
5091
|
+
},
|
|
5092
|
+
renderHTML: (attrs) => attrs.name ? { "data-icon": attrs.name } : {}
|
|
5093
|
+
}
|
|
5094
|
+
};
|
|
5095
|
+
},
|
|
5096
|
+
parseHTML() {
|
|
5097
|
+
return [{ tag: "span[data-inline-icon]" }];
|
|
5098
|
+
},
|
|
5099
|
+
renderHTML({ HTMLAttributes }) {
|
|
5100
|
+
return [
|
|
5101
|
+
"span",
|
|
5102
|
+
mergeAttributes7(HTMLAttributes, {
|
|
5103
|
+
"data-inline-icon": "true",
|
|
5104
|
+
class: "inline-icon"
|
|
5105
|
+
})
|
|
5106
|
+
];
|
|
5107
|
+
},
|
|
5108
|
+
renderMarkdown(node) {
|
|
5109
|
+
const name = node.attrs?.name;
|
|
5110
|
+
return name && isInlineIconName(name) ? `:icon[${name}]` : "";
|
|
5111
|
+
}
|
|
5112
|
+
});
|
|
5113
|
+
|
|
5114
|
+
// ../shared/src/markdown.ts
|
|
5115
|
+
import { getSchema } from "@tiptap/core";
|
|
5116
|
+
import { MarkdownManager } from "@tiptap/markdown";
|
|
5117
|
+
import {
|
|
5118
|
+
prosemirrorJSONToYXmlFragment,
|
|
5119
|
+
yXmlFragmentToProsemirrorJSON
|
|
5120
|
+
} from "@tiptap/y-tiptap";
|
|
5121
|
+
|
|
5122
|
+
// ../shared/src/frontmatter.ts
|
|
5123
|
+
function extractFrontmatter(text) {
|
|
5124
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
5125
|
+
if (!match) return null;
|
|
5126
|
+
return { yaml: match[1].trim(), body: match[2] };
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
// ../shared/src/markdown.ts
|
|
5130
|
+
var EMPTY_LIST_ITEM_SENTINEL = "<!-- composer-empty-list-item -->";
|
|
5131
|
+
function markdownToDocJSON(markdown, extensions = editorExtensions) {
|
|
5132
|
+
const manager = new MarkdownManager({ extensions });
|
|
5133
|
+
const parsed = manager.parse(markdown);
|
|
5134
|
+
const doc = parsed && parsed.type === "doc" ? parsed : { type: "doc", content: parsed?.content ?? [] };
|
|
5135
|
+
repairListItemSchema(doc);
|
|
5136
|
+
return doc;
|
|
5137
|
+
}
|
|
5138
|
+
function repairListItemSchema(node) {
|
|
5139
|
+
if (node.type === "listItem" || node.type === "taskItem") {
|
|
5140
|
+
if (!node.content || node.content.length === 0) {
|
|
5141
|
+
node.content = [{ type: "paragraph" }];
|
|
5142
|
+
return;
|
|
5143
|
+
}
|
|
5144
|
+
if (node.content[0]?.type !== "paragraph") {
|
|
5145
|
+
node.content = [{ type: "paragraph" }, ...node.content];
|
|
5146
|
+
}
|
|
5147
|
+
const first = node.content[0];
|
|
5148
|
+
if (first?.type === "paragraph" && paragraphIsEmptyListItemSentinel(first)) {
|
|
5149
|
+
node.content = [{ type: "paragraph" }, ...node.content.slice(1)];
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
if (node.content) {
|
|
5153
|
+
for (const child of node.content) repairListItemSchema(child);
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
5156
|
+
function paragraphIsEmptyListItemSentinel(paragraph) {
|
|
5157
|
+
if (!paragraph.content || paragraph.content.length === 0) return false;
|
|
5158
|
+
let sawSentinel = false;
|
|
5159
|
+
for (const child of paragraph.content) {
|
|
5160
|
+
if (child.type === "paragraph" && (!child.content || child.content.length === 0)) {
|
|
5161
|
+
sawSentinel = true;
|
|
5162
|
+
continue;
|
|
5163
|
+
}
|
|
5164
|
+
if (child.type !== "text") return false;
|
|
5165
|
+
if (child.text === EMPTY_LIST_ITEM_SENTINEL) {
|
|
5166
|
+
sawSentinel = true;
|
|
5167
|
+
continue;
|
|
5168
|
+
}
|
|
5169
|
+
if ((child.text ?? "").trim() !== "") return false;
|
|
5170
|
+
}
|
|
5171
|
+
return sawSentinel;
|
|
5172
|
+
}
|
|
5173
|
+
function writeMarkdownToYFragment(fragment, markdown, extensions = editorExtensions) {
|
|
5174
|
+
const doc = fragment.doc;
|
|
5175
|
+
if (!doc) throw new Error("fragment must be attached to a Y.Doc");
|
|
5176
|
+
const fm = extractFrontmatter(markdown);
|
|
5177
|
+
const body = fm ? fm.body : markdown;
|
|
5178
|
+
const schema = getSchema(extensions);
|
|
5179
|
+
const bodyDoc = markdownToDocJSON(body, extensions);
|
|
5180
|
+
const children = [];
|
|
5181
|
+
if (fm) {
|
|
5182
|
+
children.push({
|
|
5183
|
+
type: "frontmatter",
|
|
5184
|
+
attrs: { language: "yaml" },
|
|
5185
|
+
content: fm.yaml ? [{ type: "text", text: fm.yaml }] : void 0
|
|
5186
|
+
});
|
|
5187
|
+
}
|
|
5188
|
+
if (bodyDoc.content) children.push(...bodyDoc.content);
|
|
5189
|
+
const fullDoc = { type: "doc", content: children };
|
|
5190
|
+
doc.transact(() => {
|
|
5191
|
+
prosemirrorJSONToYXmlFragment(schema, fullDoc, fragment);
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
function markdownFromYFragment(fragment, extensions = editorExtensions) {
|
|
5195
|
+
const json = yXmlFragmentToProsemirrorJSON(fragment);
|
|
5196
|
+
if (!json || !json.content || json.content.length === 0) return "";
|
|
5197
|
+
const manager = new MarkdownManager({ extensions });
|
|
5198
|
+
return manager.serialize(json);
|
|
5199
|
+
}
|
|
5200
|
+
|
|
3893
5201
|
// ../shared/src/editor-extensions.ts
|
|
3894
5202
|
var CodeWithCombinableMarks = Code.extend({ excludes: "" });
|
|
5203
|
+
var markText = (node) => node.content?.[0]?.text ?? "";
|
|
5204
|
+
var SubscriptWithMarkdown = Subscript.extend({
|
|
5205
|
+
renderMarkdown: (node) => `<sub>${markText(node)}</sub>`
|
|
5206
|
+
});
|
|
5207
|
+
var SuperscriptWithMarkdown = Superscript.extend({
|
|
5208
|
+
renderMarkdown: (node) => `<sup>${markText(node)}</sup>`
|
|
5209
|
+
});
|
|
5210
|
+
var BlockquoteWithStableEmptyMarkdown = Blockquote.extend({
|
|
5211
|
+
renderMarkdown: (node, h) => {
|
|
5212
|
+
if (!node.content) return "";
|
|
5213
|
+
const rendered = node.content.map((child, index) => {
|
|
5214
|
+
const childContent = h.renderChild?.(child, index) ?? h.renderChildren([child]);
|
|
5215
|
+
return childContent.split("\n").map((line) => line.trim() === "" ? ">" : `> ${line}`).join("\n");
|
|
5216
|
+
});
|
|
5217
|
+
const nonEmpty = rendered.filter((part) => part.replace(/^>\s*/gm, "").trim() !== "");
|
|
5218
|
+
if (nonEmpty.length === 0) return "";
|
|
5219
|
+
return nonEmpty.join("\n>\n");
|
|
5220
|
+
}
|
|
5221
|
+
});
|
|
5222
|
+
function renderStableListItemMarkdown(node, h, ctx = {}) {
|
|
5223
|
+
if (!node.content) return "";
|
|
5224
|
+
const prefix = ctx.itemPrefix ?? (ctx.parentType === "orderedList" ? `${(ctx.meta?.parentAttrs?.start ?? 1) + (ctx.index ?? 0)}. ` : "- ");
|
|
5225
|
+
const [content, ...children] = node.content;
|
|
5226
|
+
const renderedMain = content ? h.renderChildren([content]) : "";
|
|
5227
|
+
const mainContent = renderedMain.trim() === "" ? EMPTY_LIST_ITEM_SENTINEL : renderedMain;
|
|
5228
|
+
let output = `${prefix}${mainContent}`;
|
|
5229
|
+
for (const [index, child] of children.entries()) {
|
|
5230
|
+
const childContent = h.renderChild?.(child, index + 1) ?? h.renderChildren([child]);
|
|
5231
|
+
const indentedChild = childContent.split("\n").map((line) => h.indent(line || "")).join("\n");
|
|
5232
|
+
output += child.type === "paragraph" ? `
|
|
5233
|
+
|
|
5234
|
+
${indentedChild}` : `
|
|
5235
|
+
${indentedChild}`;
|
|
5236
|
+
}
|
|
5237
|
+
return output;
|
|
5238
|
+
}
|
|
5239
|
+
var ListItemWithStableEmptyMarkdown = ListItem.extend({
|
|
5240
|
+
renderMarkdown: renderStableListItemMarkdown
|
|
5241
|
+
});
|
|
5242
|
+
var TaskItemWithStableEmptyMarkdown = TaskItem2.extend({
|
|
5243
|
+
renderMarkdown: (node, h) => renderStableListItemMarkdown(node, h, {
|
|
5244
|
+
itemPrefix: `- [${node.attrs?.checked ? "x" : " "}] `
|
|
5245
|
+
})
|
|
5246
|
+
});
|
|
5247
|
+
function normalizeAlign(value) {
|
|
5248
|
+
return value === "left" || value === "right" || value === "center" ? value : null;
|
|
5249
|
+
}
|
|
5250
|
+
function sanitizeCellText(raw) {
|
|
5251
|
+
return raw.replace(/\r?\n/g, "<br>").replace(/\s+/g, " ").trim().replace(/\|/g, "\\|");
|
|
5252
|
+
}
|
|
5253
|
+
function renderTableCellSafeMarkdown(node, h) {
|
|
5254
|
+
if (!node?.content || node.content.length === 0) return "";
|
|
5255
|
+
const rows = [];
|
|
5256
|
+
for (const rowNode of node.content) {
|
|
5257
|
+
const cells = [];
|
|
5258
|
+
for (const cellNode of rowNode.content ?? []) {
|
|
5259
|
+
const children = cellNode.content;
|
|
5260
|
+
let raw = "";
|
|
5261
|
+
if (Array.isArray(children) && children.length > 1) {
|
|
5262
|
+
raw = children.map((c) => sanitizeCellText(h.renderChildren(c))).join("<br>");
|
|
5263
|
+
} else {
|
|
5264
|
+
raw = sanitizeCellText(children ? h.renderChildren(children) : "");
|
|
5265
|
+
}
|
|
5266
|
+
cells.push({
|
|
5267
|
+
text: raw,
|
|
5268
|
+
isHeader: cellNode.type === "tableHeader",
|
|
5269
|
+
align: normalizeAlign(cellNode.attrs?.align)
|
|
5270
|
+
});
|
|
5271
|
+
}
|
|
5272
|
+
rows.push(cells);
|
|
5273
|
+
}
|
|
5274
|
+
const columnCount = rows.reduce((max, r) => Math.max(max, r.length), 0);
|
|
5275
|
+
if (columnCount === 0) return "";
|
|
5276
|
+
const colWidths = new Array(columnCount).fill(0);
|
|
5277
|
+
for (const r of rows) {
|
|
5278
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
5279
|
+
const len = (r[i]?.text || "").length;
|
|
5280
|
+
if (len > colWidths[i]) colWidths[i] = len;
|
|
5281
|
+
if (colWidths[i] < 3) colWidths[i] = 3;
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
const pad = (s, width) => s + " ".repeat(Math.max(0, width - s.length));
|
|
5285
|
+
const headerRow = rows[0];
|
|
5286
|
+
const hasHeader = headerRow.some((c) => c.isHeader);
|
|
5287
|
+
const colAlignments = new Array(columnCount).fill(null);
|
|
5288
|
+
for (const r of rows) {
|
|
5289
|
+
for (let i = 0; i < columnCount; i += 1) {
|
|
5290
|
+
if (!colAlignments[i] && r[i]?.align) colAlignments[i] = r[i].align;
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
let out = "\n";
|
|
5294
|
+
const headerTexts = new Array(columnCount).fill(0).map((_, i) => hasHeader ? headerRow[i]?.text || "" : "");
|
|
5295
|
+
out += `| ${headerTexts.map((t, i) => pad(t, colWidths[i])).join(" | ")} |
|
|
5296
|
+
`;
|
|
5297
|
+
out += `| ${colWidths.map((w, index) => {
|
|
5298
|
+
const dashCount = Math.max(3, w);
|
|
5299
|
+
const a = colAlignments[index];
|
|
5300
|
+
if (a === "left") return `:${"-".repeat(dashCount)}`;
|
|
5301
|
+
if (a === "right") return `${"-".repeat(dashCount)}:`;
|
|
5302
|
+
if (a === "center") return `:${"-".repeat(dashCount)}:`;
|
|
5303
|
+
return "-".repeat(dashCount);
|
|
5304
|
+
}).join(" | ")} |
|
|
5305
|
+
`;
|
|
5306
|
+
const body = hasHeader ? rows.slice(1) : rows;
|
|
5307
|
+
for (const r of body) {
|
|
5308
|
+
out += `| ${new Array(columnCount).fill(0).map((_, i) => pad(r[i]?.text || "", colWidths[i])).join(" | ")} |
|
|
5309
|
+
`;
|
|
5310
|
+
}
|
|
5311
|
+
return out;
|
|
5312
|
+
}
|
|
3895
5313
|
var TableWithId = Table.extend({
|
|
3896
5314
|
addAttributes() {
|
|
3897
5315
|
return {
|
|
@@ -3902,7 +5320,8 @@ var TableWithId = Table.extend({
|
|
|
3902
5320
|
renderHTML: (attrs) => attrs.tableId ? { "data-table-id": attrs.tableId } : {}
|
|
3903
5321
|
}
|
|
3904
5322
|
};
|
|
3905
|
-
}
|
|
5323
|
+
},
|
|
5324
|
+
renderMarkdown: renderTableCellSafeMarkdown
|
|
3906
5325
|
});
|
|
3907
5326
|
var FrontmatterSchema = CodeBlock.extend({
|
|
3908
5327
|
name: "frontmatter",
|
|
@@ -3920,10 +5339,13 @@ ${text}
|
|
|
3920
5339
|
function buildEditorExtensions(opts = {}) {
|
|
3921
5340
|
const table = opts.table ?? TableWithId;
|
|
3922
5341
|
const frontmatter = opts.frontmatter ?? FrontmatterSchema;
|
|
5342
|
+
const inlineIcon = opts.inlineIcon ?? InlineIconNode;
|
|
3923
5343
|
return [
|
|
3924
5344
|
StarterKit.configure({
|
|
3925
5345
|
undoRedo: false,
|
|
5346
|
+
blockquote: false,
|
|
3926
5347
|
code: false,
|
|
5348
|
+
listItem: false,
|
|
3927
5349
|
link: {
|
|
3928
5350
|
openOnClick: true,
|
|
3929
5351
|
HTMLAttributes: { target: "_blank", rel: "noopener noreferrer" },
|
|
@@ -3931,14 +5353,16 @@ function buildEditorExtensions(opts = {}) {
|
|
|
3931
5353
|
}
|
|
3932
5354
|
}),
|
|
3933
5355
|
CodeWithCombinableMarks,
|
|
5356
|
+
BlockquoteWithStableEmptyMarkdown,
|
|
5357
|
+
ListItemWithStableEmptyMarkdown,
|
|
3934
5358
|
imageBlockExtensions_default.configure({ roomId: opts.roomId, awareness: opts.awareness }),
|
|
3935
5359
|
videoBlockNode_default.configure({ roomId: opts.roomId, awareness: opts.awareness }),
|
|
3936
5360
|
mediaKeyboardNav_default,
|
|
3937
|
-
|
|
3938
|
-
|
|
5361
|
+
TaskList2,
|
|
5362
|
+
TaskItemWithStableEmptyMarkdown.configure({ nested: true }),
|
|
3939
5363
|
Highlight,
|
|
3940
|
-
|
|
3941
|
-
|
|
5364
|
+
SubscriptWithMarkdown,
|
|
5365
|
+
SuperscriptWithMarkdown,
|
|
3942
5366
|
table.configure({
|
|
3943
5367
|
resizable: true,
|
|
3944
5368
|
handleWidth: 5,
|
|
@@ -3949,59 +5373,12 @@ function buildEditorExtensions(opts = {}) {
|
|
|
3949
5373
|
TableRow,
|
|
3950
5374
|
TableCell,
|
|
3951
5375
|
TableHeader,
|
|
3952
|
-
frontmatter
|
|
5376
|
+
frontmatter,
|
|
5377
|
+
inlineIcon
|
|
3953
5378
|
];
|
|
3954
5379
|
}
|
|
3955
5380
|
var editorExtensions = buildEditorExtensions();
|
|
3956
5381
|
|
|
3957
|
-
// ../shared/src/frontmatter.ts
|
|
3958
|
-
function extractFrontmatter(text) {
|
|
3959
|
-
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
3960
|
-
if (!match) return null;
|
|
3961
|
-
return { yaml: match[1].trim(), body: match[2] };
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
// ../shared/src/markdown.ts
|
|
3965
|
-
import { getSchema } from "@tiptap/core";
|
|
3966
|
-
import { MarkdownManager } from "@tiptap/markdown";
|
|
3967
|
-
import {
|
|
3968
|
-
prosemirrorJSONToYXmlFragment,
|
|
3969
|
-
yXmlFragmentToProsemirrorJSON
|
|
3970
|
-
} from "@tiptap/y-tiptap";
|
|
3971
|
-
function markdownToDocJSON(markdown, extensions = editorExtensions) {
|
|
3972
|
-
const manager = new MarkdownManager({ extensions });
|
|
3973
|
-
const parsed = manager.parse(markdown);
|
|
3974
|
-
if (parsed && parsed.type === "doc") return parsed;
|
|
3975
|
-
return { type: "doc", content: parsed?.content ?? [] };
|
|
3976
|
-
}
|
|
3977
|
-
function writeMarkdownToYFragment(fragment, markdown, extensions = editorExtensions) {
|
|
3978
|
-
const doc = fragment.doc;
|
|
3979
|
-
if (!doc) throw new Error("fragment must be attached to a Y.Doc");
|
|
3980
|
-
const fm = extractFrontmatter(markdown);
|
|
3981
|
-
const body = fm ? fm.body : markdown;
|
|
3982
|
-
const schema = getSchema(extensions);
|
|
3983
|
-
const bodyDoc = markdownToDocJSON(body, extensions);
|
|
3984
|
-
const children = [];
|
|
3985
|
-
if (fm) {
|
|
3986
|
-
children.push({
|
|
3987
|
-
type: "frontmatter",
|
|
3988
|
-
attrs: { language: "yaml" },
|
|
3989
|
-
content: fm.yaml ? [{ type: "text", text: fm.yaml }] : void 0
|
|
3990
|
-
});
|
|
3991
|
-
}
|
|
3992
|
-
if (bodyDoc.content) children.push(...bodyDoc.content);
|
|
3993
|
-
const fullDoc = { type: "doc", content: children };
|
|
3994
|
-
doc.transact(() => {
|
|
3995
|
-
prosemirrorJSONToYXmlFragment(schema, fullDoc, fragment);
|
|
3996
|
-
});
|
|
3997
|
-
}
|
|
3998
|
-
function markdownFromYFragment(fragment, extensions = editorExtensions) {
|
|
3999
|
-
const json = yXmlFragmentToProsemirrorJSON(fragment);
|
|
4000
|
-
if (!json || !json.content || json.content.length === 0) return "";
|
|
4001
|
-
const manager = new MarkdownManager({ extensions });
|
|
4002
|
-
return manager.serialize(json);
|
|
4003
|
-
}
|
|
4004
|
-
|
|
4005
5382
|
// ../shared/src/insertMedia.ts
|
|
4006
5383
|
import { nanoid as nanoid2 } from "nanoid";
|
|
4007
5384
|
|
|
@@ -5615,6 +6992,27 @@ var TOOL_DEFS = [
|
|
|
5615
6992
|
required: ["roomId", "threadId"]
|
|
5616
6993
|
}
|
|
5617
6994
|
},
|
|
6995
|
+
{
|
|
6996
|
+
name: "composer_list_threads",
|
|
6997
|
+
description: 'List comment and suggestion threads in the room, with full reply history. Same per-thread shape as `composer_get_thread` minus `sectionMarkdown` (omitted so the payload stays tractable across many threads \u2014 call `composer_get_full_doc` or `composer_get_thread` for surrounding context). Sorted by `createdAt` ascending so the agent reads them in the order they were posted. Use when you need to harvest discussion across the whole doc \u2014 e.g. "the user asked questions in the doc and other people answered in comment threads, summarize the answers". Optional `kind` filter narrows to one type when suggestion churn or comment chatter would be noise. Optional `status` filter drives the "which threads are still open" audit AND is the primary lever for shrinking the payload: `"open"` excludes resolved comments and accepted/rejected suggestions, which accumulate for the room\'s whole lifetime (they are never deleted), so on a long-lived doc the default `"all"` can be large.',
|
|
6998
|
+
inputSchema: {
|
|
6999
|
+
type: "object",
|
|
7000
|
+
properties: {
|
|
7001
|
+
roomId: { type: "string" },
|
|
7002
|
+
kind: {
|
|
7003
|
+
type: "string",
|
|
7004
|
+
enum: ["comment", "suggestion", "all"],
|
|
7005
|
+
description: 'Which thread kind to return. `"comment"` = Q&A / discussion threads only; `"suggestion"` = text-replacement proposals only; `"all"` (default) = both, with the `kind` field on each entry as the discriminator.'
|
|
7006
|
+
},
|
|
7007
|
+
status: {
|
|
7008
|
+
type: "string",
|
|
7009
|
+
enum: ["open", "resolved", "all"],
|
|
7010
|
+
description: 'Lifecycle filter. `"open"` = unresolved comments + pending suggestions (a thread with no terminal marker counts as open). `"resolved"` = resolved comments + accepted/rejected suggestions. `"all"` (default) = no filter. Use `"open"` for the unresolved-threads audit and to keep the response small on busy docs.'
|
|
7011
|
+
}
|
|
7012
|
+
},
|
|
7013
|
+
required: ["roomId"]
|
|
7014
|
+
}
|
|
7015
|
+
},
|
|
5618
7016
|
{
|
|
5619
7017
|
name: "composer_add_comment",
|
|
5620
7018
|
description: "Post a new top-level comment anchored to a text span anywhere in the doc. Anchor is { headingId, textToFind, occurrence? }. Use this to flag something the user didn't ask about \u2014 cross-referencing related sections, raising a concern elsewhere in the doc, or seeding a thread on a new span. Use `composer_reply_comment` instead when continuing an existing thread. Accepts optional `state` (ack-first flow: post with `state: \"thinking\"` to start the live indicator immediately) and `mentions` (array of target userIds \u2014 the invoker's userId from the mention event payload, for the `@invoker` backlink).\n**`textToFind` matches the doc's stored text, not the markdown source.** Inline marks (`**bold**`, `*italic*`, `` `code` ``, `_emphasis_`, `~~strike~~`) are stored as Y.Marks \u2014 write plain text, no markers. Top-level blocks join with `\\n\\n`; list items have NO separator (three bullets `- a / - b / - c` are stored as `abc`). Copy from `Section as the matcher sees it` in any `text_not_found` error if unsure. Returns { id } on success or an isError result if the anchor cannot be resolved.",
|
|
@@ -6130,23 +7528,8 @@ function handleGetFullDoc(args) {
|
|
|
6130
7528
|
const state = getOrError(roomId);
|
|
6131
7529
|
return okResult({ markdown: serializeDocAsMarkdown(state.doc) });
|
|
6132
7530
|
}
|
|
6133
|
-
function
|
|
6134
|
-
const
|
|
6135
|
-
const roomId = asString(a.roomId, "roomId");
|
|
6136
|
-
const threadId = asString(a.threadId, "threadId");
|
|
6137
|
-
const state = getOrError(roomId);
|
|
6138
|
-
const commentRaw = state.doc.getMap("comments").get(threadId);
|
|
6139
|
-
const suggestionRaw = state.doc.getMap("suggestions").get(threadId);
|
|
6140
|
-
const raw = commentRaw ?? suggestionRaw;
|
|
6141
|
-
if (!raw) {
|
|
6142
|
-
return errorResult(`thread not found: ${threadId}`);
|
|
6143
|
-
}
|
|
6144
|
-
const kind = commentRaw ? "comment" : "suggestion";
|
|
6145
|
-
const anchoredContext = resolveAnchoredContext(
|
|
6146
|
-
state.doc,
|
|
6147
|
-
raw.anchorFrom,
|
|
6148
|
-
raw.anchorTo
|
|
6149
|
-
);
|
|
7531
|
+
function shapeThread(doc, threadId, raw, kind, opts) {
|
|
7532
|
+
const anchoredContext = resolveAnchoredContext(doc, raw.anchorFrom, raw.anchorTo);
|
|
6150
7533
|
const replies = Array.isArray(raw.replies) ? raw.replies : [];
|
|
6151
7534
|
const shapedReplies = replies.filter(
|
|
6152
7535
|
(r) => !!r && typeof r === "object" && typeof r.id === "string" && typeof r.text === "string"
|
|
@@ -6159,7 +7542,7 @@ function handleGetThread(args) {
|
|
|
6159
7542
|
mentions: Array.isArray(r.mentions) ? r.mentions.filter((m) => typeof m === "string") : void 0,
|
|
6160
7543
|
createdAt: typeof r.createdAt === "number" ? r.createdAt : void 0
|
|
6161
7544
|
}));
|
|
6162
|
-
return
|
|
7545
|
+
return {
|
|
6163
7546
|
threadId,
|
|
6164
7547
|
kind,
|
|
6165
7548
|
body: typeof raw.text === "string" ? raw.text : void 0,
|
|
@@ -6175,9 +7558,72 @@ function handleGetThread(args) {
|
|
|
6175
7558
|
anchoredText: anchoredContext.anchoredText,
|
|
6176
7559
|
headingId: anchoredContext.headingId,
|
|
6177
7560
|
headingText: anchoredContext.headingText,
|
|
6178
|
-
sectionMarkdown: anchoredContext.sectionMarkdown,
|
|
7561
|
+
sectionMarkdown: opts.includeSectionMarkdown ? anchoredContext.sectionMarkdown : void 0,
|
|
6179
7562
|
replies: shapedReplies
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7565
|
+
function handleGetThread(args) {
|
|
7566
|
+
const a = asObject(args);
|
|
7567
|
+
const roomId = asString(a.roomId, "roomId");
|
|
7568
|
+
const threadId = asString(a.threadId, "threadId");
|
|
7569
|
+
const state = getOrError(roomId);
|
|
7570
|
+
const commentRaw = state.doc.getMap("comments").get(threadId);
|
|
7571
|
+
const suggestionRaw = state.doc.getMap("suggestions").get(threadId);
|
|
7572
|
+
const raw = commentRaw ?? suggestionRaw;
|
|
7573
|
+
if (!raw) {
|
|
7574
|
+
return errorResult(`thread not found: ${threadId}`);
|
|
7575
|
+
}
|
|
7576
|
+
const kind = commentRaw ? "comment" : "suggestion";
|
|
7577
|
+
return okResult(
|
|
7578
|
+
shapeThread(state.doc, threadId, raw, kind, { includeSectionMarkdown: true })
|
|
7579
|
+
);
|
|
7580
|
+
}
|
|
7581
|
+
function handleListThreads(args) {
|
|
7582
|
+
const a = asObject(args);
|
|
7583
|
+
const roomId = asString(a.roomId, "roomId");
|
|
7584
|
+
const state = getOrError(roomId);
|
|
7585
|
+
const kindArg = asOptionalString(a.kind, "kind") ?? "all";
|
|
7586
|
+
if (kindArg !== "all" && kindArg !== "comment" && kindArg !== "suggestion") {
|
|
7587
|
+
return errorResult(`kind must be one of "all", "comment", "suggestion" (got "${kindArg}")`);
|
|
7588
|
+
}
|
|
7589
|
+
const statusArg = asOptionalString(a.status, "status") ?? "all";
|
|
7590
|
+
if (statusArg !== "all" && statusArg !== "open" && statusArg !== "resolved") {
|
|
7591
|
+
return errorResult(`status must be one of "all", "open", "resolved" (got "${statusArg}")`);
|
|
7592
|
+
}
|
|
7593
|
+
const out = [];
|
|
7594
|
+
if (kindArg === "all" || kindArg === "comment") {
|
|
7595
|
+
const comments = state.doc.getMap("comments");
|
|
7596
|
+
comments.forEach((raw, id) => {
|
|
7597
|
+
if (raw && typeof raw === "object") {
|
|
7598
|
+
out.push(shapeThread(state.doc, id, raw, "comment", {
|
|
7599
|
+
includeSectionMarkdown: false
|
|
7600
|
+
}));
|
|
7601
|
+
}
|
|
7602
|
+
});
|
|
7603
|
+
}
|
|
7604
|
+
if (kindArg === "all" || kindArg === "suggestion") {
|
|
7605
|
+
const suggestions = state.doc.getMap("suggestions");
|
|
7606
|
+
suggestions.forEach((raw, id) => {
|
|
7607
|
+
if (raw && typeof raw === "object") {
|
|
7608
|
+
out.push(shapeThread(state.doc, id, raw, "suggestion", {
|
|
7609
|
+
includeSectionMarkdown: false
|
|
7610
|
+
}));
|
|
7611
|
+
}
|
|
7612
|
+
});
|
|
7613
|
+
}
|
|
7614
|
+
if (statusArg !== "all") {
|
|
7615
|
+
const isOpen = (t) => t.kind === "comment" ? t.resolved !== true : t.status === void 0 || t.status === "pending";
|
|
7616
|
+
const wantOpen = statusArg === "open";
|
|
7617
|
+
for (let i = out.length - 1; i >= 0; i--) {
|
|
7618
|
+
if (isOpen(out[i]) !== wantOpen) out.splice(i, 1);
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
out.sort((a2, b) => {
|
|
7622
|
+
const aT = a2.createdAt ?? Number.POSITIVE_INFINITY;
|
|
7623
|
+
const bT = b.createdAt ?? Number.POSITIVE_INFINITY;
|
|
7624
|
+
return aT - bT;
|
|
6180
7625
|
});
|
|
7626
|
+
return okResult({ threads: out });
|
|
6181
7627
|
}
|
|
6182
7628
|
function performAddComment(state, a) {
|
|
6183
7629
|
const anchor = asAnchor(a.anchor);
|
|
@@ -6452,6 +7898,7 @@ function performResolveThread(state, a) {
|
|
|
6452
7898
|
participantUserIds: getParticipantUserIds(existing),
|
|
6453
7899
|
createdAt
|
|
6454
7900
|
});
|
|
7901
|
+
markActivityReadForThread(state.doc, threadId, state.identity.userId);
|
|
6455
7902
|
});
|
|
6456
7903
|
return okResult({ threadId, resolved: true });
|
|
6457
7904
|
}
|
|
@@ -6624,6 +8071,8 @@ async function dispatchTool(name, args, signal) {
|
|
|
6624
8071
|
return handleGetFullDoc(args);
|
|
6625
8072
|
case "composer_get_thread":
|
|
6626
8073
|
return handleGetThread(args);
|
|
8074
|
+
case "composer_list_threads":
|
|
8075
|
+
return handleListThreads(args);
|
|
6627
8076
|
case "composer_add_comment":
|
|
6628
8077
|
return handleAddComment(args);
|
|
6629
8078
|
case "composer_reply_comment":
|