tiptap-markdown-react 0.9.5 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,26 +1,27 @@
1
1
  "use client";
2
- import { Markdown } from "@tiptap/markdown";
3
- import { c as renderOrUpdateChart, d as destroyChart } from "./scrollToTocHeading-BSGt1H9o.js";
4
- import { C, a, R, b, f, m, r, s } from "./scrollToTocHeading-BSGt1H9o.js";
2
+ import { Markdown, MarkdownManager } from "@tiptap/markdown";
3
+ import { c as renderOrUpdateChart, d as destroyChart } from "./scrollToTocHeading-BnfiGfjk.js";
4
+ import { C, a, R, b, f, m, r, s } from "./scrollToTocHeading-BnfiGfjk.js";
5
5
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
6
6
  import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
7
7
  import Image from "@tiptap/extension-image";
8
8
  import { TableOfContents } from "@tiptap/extension-table-of-contents";
9
9
  import { NodeViewWrapper, ReactNodeViewRenderer, NodeViewContent, useEditor, EditorContent, useEditorState } from "@tiptap/react";
10
10
  import { useRef, useState, useMemo, useEffect, forwardRef, useImperativeHandle, useId, useCallback, useContext, createContext } from "react";
11
- import { e as enrichMarkdownCitations, b as styles$5 } from "./ReportContent-Cal7Nph4.js";
12
- import { R as R2, a as a2, s as s2 } from "./ReportContent-Cal7Nph4.js";
13
- import { C as CitationRef, i as mergeCommentIntervals, h as mapCommentAnchors, p as prepareChartMarkdown, j as reportChart, b as baseExtensions, f as CommentMark, l as lowlight, m as makeTocGetId, k as parseCodeTokenAsChartOrCodeBlock, a as pureCodeBlock, c as pureImage, n as renderMathInto, o as subscribeMathClick } from "./extractToc-BeiEVpT3.js";
14
- import { g, e, d, s as s3 } from "./extractToc-BeiEVpT3.js";
11
+ import { e as enrichMarkdownCitations, b as styles$5 } from "./ReportContent-XJE1P0zp.js";
12
+ import { R as R2, a as a2, s as s2 } from "./ReportContent-XJE1P0zp.js";
13
+ import { C as CitationRef, i as mergeCommentIntervals, h as mapCommentAnchors, p as prepareChartMarkdown, j as reportChart, b as baseExtensions, f as CommentMark, l as lowlight, m as makeTocGetId, k as parseCodeTokenAsChartOrCodeBlock, a as pureCodeBlock, c as pureImage, n as renderMathInto, o as subscribeMathClick, d as pureChart } from "./extractToc-BrN-Lwwy.js";
14
+ import { g, e, s as s3 } from "./extractToc-BrN-Lwwy.js";
15
15
  import { Slice, Fragment } from "@tiptap/pm/model";
16
16
  import { Extension, Node as Node$1 } from "@tiptap/core";
17
- import { PluginKey, Plugin } from "@tiptap/pm/state";
17
+ import { PluginKey, Plugin, NodeSelection, TextSelection } from "@tiptap/pm/state";
18
18
  import { DecorationSet, Decoration } from "@tiptap/pm/view";
19
19
  import { FileHandlePlugin } from "@tiptap/extension-file-handler";
20
+ import { skipTrailingNodeMeta } from "@tiptap/extensions";
21
+ import { CellSelection, isInTable, selectedRect, rowIsHeader } from "@tiptap/pm/tables";
20
22
  import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
21
23
  import * as Popover from "@radix-ui/react-popover";
22
24
  import { createPortal } from "react-dom";
23
- import { isInTable, selectedRect, rowIsHeader, CellSelection } from "@tiptap/pm/tables";
24
25
  function CitationRefView({ node, extension }) {
25
26
  const index = String(node.attrs.index ?? "");
26
27
  const url = node.attrs.url || null;
@@ -452,6 +453,78 @@ const MarkdownFileDrop = Extension.create({
452
453
  ];
453
454
  }
454
455
  });
456
+ const PENDING_ANCHOR_META = "tmr:pendingAnchor";
457
+ const pendingAnchorPluginKey = new PluginKey(
458
+ "tmrPendingAnchor"
459
+ );
460
+ const PENDING_ANCHOR_CLASS = "tmr-pending-anchor";
461
+ const pendingAnchorExtension = Extension.create({
462
+ name: "pendingAnchor",
463
+ addProseMirrorPlugins() {
464
+ return [
465
+ new Plugin({
466
+ key: pendingAnchorPluginKey,
467
+ state: {
468
+ init: () => ({ decorations: DecorationSet.empty }),
469
+ apply(tr, value) {
470
+ const next = tr.getMeta(PENDING_ANCHOR_META);
471
+ if (next) return { decorations: buildDecorations(tr.doc, next) };
472
+ if (tr.docChanged) {
473
+ return { decorations: value.decorations.map(tr.mapping, tr.doc) };
474
+ }
475
+ return value;
476
+ }
477
+ },
478
+ props: {
479
+ decorations(state) {
480
+ var _a;
481
+ return ((_a = pendingAnchorPluginKey.getState(state)) == null ? void 0 : _a.decorations) ?? null;
482
+ }
483
+ }
484
+ })
485
+ ];
486
+ }
487
+ });
488
+ function setPendingAnchors(editor, anchors) {
489
+ editor.view.dispatch(
490
+ editor.state.tr.setMeta(PENDING_ANCHOR_META, anchors).setMeta(skipTrailingNodeMeta, true).setMeta("addToHistory", false)
491
+ );
492
+ }
493
+ function buildDecorations(doc, anchors) {
494
+ const bounds = [];
495
+ doc.forEach((child, offset) => {
496
+ bounds.push({ from: offset, to: offset + child.nodeSize });
497
+ });
498
+ const decorations = [];
499
+ for (const anchor of anchors) {
500
+ for (const range of anchor.ranges) {
501
+ const from = Math.max(0, Math.min(range.from, range.to));
502
+ const to = Math.min(doc.content.size, Math.max(range.from, range.to));
503
+ if (from >= to) continue;
504
+ for (const bound of bounds) {
505
+ if (bound.to <= from || bound.from >= to) continue;
506
+ const attrs = {
507
+ class: PENDING_ANCHOR_CLASS,
508
+ "data-pending-id": anchor.id
509
+ };
510
+ if (from <= bound.from && to >= bound.to) {
511
+ decorations.push(Decoration.node(bound.from, bound.to, attrs));
512
+ } else {
513
+ decorations.push(
514
+ Decoration.inline(Math.max(from, bound.from), Math.min(to, bound.to), attrs)
515
+ );
516
+ }
517
+ }
518
+ }
519
+ }
520
+ return decorations.length ? DecorationSet.create(doc, decorations) : DecorationSet.empty;
521
+ }
522
+ function selectionKind(selection) {
523
+ if (selection instanceof CellSelection) return "cell";
524
+ if (selection instanceof NodeSelection) return "node";
525
+ if (selection instanceof TextSelection) return "text";
526
+ return "text";
527
+ }
455
528
  function looksLikeMarkdown(text) {
456
529
  return /^#{1,6}\s/m.test(text) || // 标题
457
530
  /\*\*[^*\n]+\*\*/.test(text) || // 加粗
@@ -1055,7 +1128,10 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
1055
1128
  onCommentClick,
1056
1129
  onActiveCommentChange,
1057
1130
  showCommentGutter = true,
1058
- commentInteractive = true
1131
+ commentInteractive = true,
1132
+ pendingAnchors,
1133
+ onAnchorClick,
1134
+ onSelectionChange
1059
1135
  }, ref) {
1060
1136
  const preparedInitial = prepareChartMarkdown(
1061
1137
  enrichMarkdownCitations(initialMarkdown, sources ?? [])
@@ -1074,6 +1150,7 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
1074
1150
  interactive: commentInteractive
1075
1151
  }),
1076
1152
  Markdown,
1153
+ pendingAnchorExtension,
1077
1154
  TableOfContents.configure({
1078
1155
  getId: makeTocGetId(),
1079
1156
  onUpdate: (anchors) => {
@@ -1122,6 +1199,47 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
1122
1199
  )
1123
1200
  );
1124
1201
  }, [editor, activeCommentId]);
1202
+ useEffect(() => {
1203
+ if (!editor) return;
1204
+ setPendingAnchors(editor, pendingAnchors ?? []);
1205
+ }, [editor, pendingAnchors]);
1206
+ const onAnchorClickRef = useRef(onAnchorClick);
1207
+ useEffect(() => {
1208
+ onAnchorClickRef.current = onAnchorClick;
1209
+ }, [onAnchorClick]);
1210
+ useEffect(() => {
1211
+ if (!editor) return;
1212
+ const dom = editor.view.dom;
1213
+ const onClick = (event) => {
1214
+ var _a, _b, _c;
1215
+ const target = event.target;
1216
+ const id = (_b = (_a = target == null ? void 0 : target.closest) == null ? void 0 : _a.call(target, "[data-pending-id]")) == null ? void 0 : _b.getAttribute("data-pending-id");
1217
+ if (id) (_c = onAnchorClickRef.current) == null ? void 0 : _c.call(onAnchorClickRef, id);
1218
+ };
1219
+ dom.addEventListener("click", onClick);
1220
+ return () => dom.removeEventListener("click", onClick);
1221
+ }, [editor]);
1222
+ const onSelectionChangeRef = useRef(onSelectionChange);
1223
+ useEffect(() => {
1224
+ onSelectionChangeRef.current = onSelectionChange;
1225
+ }, [onSelectionChange]);
1226
+ useEffect(() => {
1227
+ if (!editor) return;
1228
+ const onSelectionUpdate = () => {
1229
+ var _a;
1230
+ const { from, to, empty } = editor.state.selection;
1231
+ (_a = onSelectionChangeRef.current) == null ? void 0 : _a.call(onSelectionChangeRef, {
1232
+ from,
1233
+ to,
1234
+ empty,
1235
+ kind: selectionKind(editor.state.selection)
1236
+ });
1237
+ };
1238
+ editor.on("selectionUpdate", onSelectionUpdate);
1239
+ return () => {
1240
+ editor.off("selectionUpdate", onSelectionUpdate);
1241
+ };
1242
+ }, [editor]);
1125
1243
  const onCommentClickRef = useRef(onCommentClick);
1126
1244
  const onActiveCommentChangeRef = useRef(onActiveCommentChange);
1127
1245
  onCommentClickRef.current = onCommentClick;
@@ -2067,8 +2185,8 @@ function EditorToolbar({
2067
2185
  if (!coords) return;
2068
2186
  const $pos = editor.state.doc.resolve(coords.pos);
2069
2187
  let inTable = false;
2070
- for (let d2 = $pos.depth; d2 > 0; d2--) {
2071
- if ($pos.node(d2).type.name === "table") {
2188
+ for (let d = $pos.depth; d > 0; d--) {
2189
+ if ($pos.node(d).type.name === "table") {
2072
2190
  inTable = true;
2073
2191
  break;
2074
2192
  }
@@ -2984,6 +3102,121 @@ function CommentPopover({
2984
3102
  ) })
2985
3103
  ] });
2986
3104
  }
3105
+ const PROBE_ID = "__anchor-range-probe__";
3106
+ function findRangeByAnchor(editor, anchor) {
3107
+ if (!anchor || !anchor.exact) return null;
3108
+ const [result] = mapCommentAnchors(editor.state.doc, [
3109
+ { commentId: PROBE_ID, segments: [anchor] }
3110
+ ]);
3111
+ const range = result == null ? void 0 : result.ranges[0];
3112
+ if (!range) return null;
3113
+ return {
3114
+ from: range.from,
3115
+ to: range.to,
3116
+ text: editor.state.doc.textBetween(range.from, range.to, "\n\n"),
3117
+ status: result.status
3118
+ };
3119
+ }
3120
+ const BLOCK_SEPARATOR = "\n\n";
3121
+ function topLevelBlocks(editor) {
3122
+ const blocks = [];
3123
+ editor.state.doc.forEach((node, offset) => {
3124
+ blocks.push({
3125
+ json: node.toJSON(),
3126
+ from: offset,
3127
+ to: offset + node.nodeSize
3128
+ });
3129
+ });
3130
+ return blocks;
3131
+ }
3132
+ function getMarkdownForRange(editor, from, to, options = {}) {
3133
+ const manager = editor.markdown;
3134
+ if (!manager) return null;
3135
+ const size = editor.state.doc.content.size;
3136
+ let start = Math.max(0, Math.min(from, to));
3137
+ let end = Math.min(size, Math.max(from, to));
3138
+ if (start >= end) return null;
3139
+ const blocks = topLevelBlocks(editor);
3140
+ if (blocks.length === 0) return null;
3141
+ const expand = options.expandToBlocks !== false;
3142
+ let first = 0;
3143
+ let last = blocks.length - 1;
3144
+ if (expand) {
3145
+ first = blocks.findIndex((b2) => b2.to > start);
3146
+ if (first < 0) return null;
3147
+ for (let i = blocks.length - 1; i >= first; i -= 1) {
3148
+ if (blocks[i].from < end) {
3149
+ last = i;
3150
+ break;
3151
+ }
3152
+ }
3153
+ start = blocks[first].from;
3154
+ end = blocks[last].to;
3155
+ }
3156
+ const parts = blocks.map((b2) => manager.serialize({ type: "doc", content: [b2.json] }));
3157
+ const full = editor.getMarkdown();
3158
+ if (expand && parts.join(BLOCK_SEPARATOR) === full) {
3159
+ const markdown2 = parts.slice(first, last + 1).join(BLOCK_SEPARATOR);
3160
+ if (!markdown2) return null;
3161
+ const at2 = first === 0 ? 0 : parts.slice(0, first).join(BLOCK_SEPARATOR).length + BLOCK_SEPARATOR.length;
3162
+ return { markdown: markdown2, from: at2, to: at2 + markdown2.length };
3163
+ }
3164
+ const markdown = manager.serialize(
3165
+ editor.state.doc.cut(start, end).toJSON()
3166
+ );
3167
+ if (!markdown) return null;
3168
+ const at = full.indexOf(markdown);
3169
+ if (at < 0 || full.indexOf(markdown, at + 1) >= 0) return null;
3170
+ return { markdown, from: at, to: at + markdown.length };
3171
+ }
3172
+ function normalizeMarkdown(markdown) {
3173
+ if (!markdown) return markdown;
3174
+ const manager = getManager();
3175
+ return manager.serialize(manager.parse(prepareChartMarkdown(markdown)));
3176
+ }
3177
+ let sharedManager = null;
3178
+ function getManager() {
3179
+ sharedManager ?? (sharedManager = new MarkdownManager({
3180
+ extensions: [...baseExtensions, pureCodeBlock, pureImage, pureChart, CitationRef]
3181
+ }));
3182
+ return sharedManager;
3183
+ }
3184
+ function replaceRangeWithMarkdown(editor, range, markdown, options = {}) {
3185
+ const prepared = prepareChartMarkdown(
3186
+ enrichMarkdownCitations(markdown ?? "", options.sources ?? [])
3187
+ );
3188
+ if (!prepared.trim()) return false;
3189
+ let from = Math.max(0, Math.min(range.from, range.to));
3190
+ let to = Math.max(range.from, range.to);
3191
+ if (from === to) return false;
3192
+ if (tryInsertMarkdownAt(editor, from, to, prepared)) return true;
3193
+ for (const block of splitTopLevelBlocks(prepared)) {
3194
+ const before = editor.state.doc.content.size;
3195
+ const changed = tryInsertMarkdownAt(editor, from, to, block) || insertPlainParagraphAt(editor, from, to, stripToPlainText(block));
3196
+ if (!changed) continue;
3197
+ const inserted = editor.state.doc.content.size - before + (to - from);
3198
+ from += inserted;
3199
+ to = from;
3200
+ }
3201
+ return true;
3202
+ }
3203
+ function tryInsertMarkdownAt(editor, from, to, markdown) {
3204
+ try {
3205
+ return editor.chain().insertContentAt({ from, to }, markdown, { contentType: "markdown" }).run();
3206
+ } catch {
3207
+ return false;
3208
+ }
3209
+ }
3210
+ function insertPlainParagraphAt(editor, from, to, text) {
3211
+ try {
3212
+ return editor.chain().insertContentAt(
3213
+ { from, to },
3214
+ { type: "paragraph", content: text ? [{ type: "text", text }] : [] }
3215
+ ).run();
3216
+ } catch {
3217
+ return false;
3218
+ }
3219
+ }
2987
3220
  export {
2988
3221
  BoldIcon,
2989
3222
  COMMENT_ACTIVE_META,
@@ -3017,6 +3250,7 @@ export {
3017
3250
  MarkdownPreview,
3018
3251
  MarkdownWysiwygEditor,
3019
3252
  MenuIcon,
3253
+ PENDING_ANCHOR_CLASS,
3020
3254
  RedoIcon,
3021
3255
  R2 as ReportContent,
3022
3256
  R as ReportContentInteractive,
@@ -3050,7 +3284,9 @@ export {
3050
3284
  enrichMarkdownCitations,
3051
3285
  e as extractToc,
3052
3286
  f as findCitationRefElement,
3287
+ findRangeByAnchor,
3053
3288
  focusComment,
3289
+ getMarkdownForRange,
3054
3290
  insertMarkdown,
3055
3291
  looksLikeMarkdown,
3056
3292
  lowlight,
@@ -3059,13 +3295,16 @@ export {
3059
3295
  mergeCommentIntervals,
3060
3296
  m as mountChartsInContainer,
3061
3297
  nextComment,
3298
+ normalizeMarkdown,
3062
3299
  prepareChartMarkdown,
3063
- d as pureChart,
3300
+ pureChart,
3064
3301
  pureCodeBlock,
3065
3302
  pureImage,
3066
3303
  r as readCitationAttrs,
3304
+ replaceRangeWithMarkdown,
3067
3305
  reportChart,
3068
3306
  s as scrollToTocHeading,
3307
+ selectionKind,
3069
3308
  s2 as serializeChartPayload,
3070
3309
  s3 as stabilizeMarkdown,
3071
3310
  stripCommentAnchorMarks,