tiptap-markdown-react 0.19.1 → 1.0.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.
Files changed (59) hide show
  1. package/README.md +23 -37
  2. package/THIRD_PARTY_LICENSES.md +155 -1
  3. package/dist/{ReportContent-B8jIzhy3.js → ReportContent-w08GFm8v.js} +16 -8
  4. package/dist/{ReportContent-B8jIzhy3.js.map → ReportContent-w08GFm8v.js.map} +1 -1
  5. package/dist/blockDrag/actions.d.ts +30 -0
  6. package/dist/blockDrag/actions.test.d.ts +1 -0
  7. package/dist/blockDrag/listDrop.d.ts +21 -0
  8. package/dist/blockDrag/listDrop.test.d.ts +1 -0
  9. package/dist/blurSelection.dom.test.d.ts +1 -0
  10. package/dist/characterCount.d.ts +1 -0
  11. package/dist/characterCount.dom.test.d.ts +1 -0
  12. package/dist/characterCount.test.d.ts +1 -0
  13. package/dist/chart/createChart.d.ts +0 -3
  14. package/dist/components/BlockDragHandle.d.ts +14 -0
  15. package/dist/components/BlockDragHandle.dom.test.d.ts +1 -0
  16. package/dist/components/CodeBlockView.d.ts +2 -2
  17. package/dist/components/ColorPalette.d.ts +4 -3
  18. package/dist/components/EditorToolbar.d.ts +4 -3
  19. package/dist/components/FindReplaceBar.d.ts +4 -3
  20. package/dist/components/LinkPopover.d.ts +4 -3
  21. package/dist/components/MarkdownPreview.d.ts +4 -1
  22. package/dist/components/MarkdownWysiwygEditor.d.ts +30 -9
  23. package/dist/components/TocPanel.d.ts +4 -3
  24. package/dist/detailsMarkdown.d.ts +4 -0
  25. package/dist/detailsMarkdown.test.d.ts +1 -0
  26. package/dist/emoji/EmojiSuggestion.d.ts +23 -0
  27. package/dist/emoji/emojiCategories.d.ts +52 -0
  28. package/dist/emoji/emojiMarkdown.test.d.ts +1 -0
  29. package/dist/emoji/emojiSuggestion.dom.test.d.ts +1 -0
  30. package/dist/emoji/filterEmoji.d.ts +7 -0
  31. package/dist/emoji/markdownEmoji.d.ts +8 -0
  32. package/dist/{extractToc-CGrBMGvM.js → extractToc-Cr470fBy.js} +659 -5
  33. package/dist/extractToc-Cr470fBy.js.map +1 -0
  34. package/dist/fonts/OFL.txt +95 -0
  35. package/dist/i18n/index.d.ts +4 -0
  36. package/dist/i18n/store.d.ts +16 -0
  37. package/dist/i18n/types.d.ts +19 -0
  38. package/dist/i18n/zh.d.ts +3 -0
  39. package/dist/icons/index.d.ts +5 -0
  40. package/dist/imageUpload.d.ts +0 -3
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +1983 -407
  43. package/dist/index.js.map +1 -1
  44. package/dist/labels.d.ts +56 -2
  45. package/dist/numberingFormat.d.ts +67 -0
  46. package/dist/numberingFormat.test.d.ts +1 -0
  47. package/dist/reader.js +2 -2
  48. package/dist/{scrollToTocHeading-B2QmL4wi.js → scrollToTocHeading-kPgsn3cu.js} +2 -2
  49. package/dist/{scrollToTocHeading-B2QmL4wi.js.map → scrollToTocHeading-kPgsn3cu.js.map} +1 -1
  50. package/dist/server.js +4 -4
  51. package/dist/shortcuts/ShortcutPanel.d.ts +4 -4
  52. package/dist/shortcuts/types.d.ts +2 -2
  53. package/dist/slashMenu/SlashMenuExtension.d.ts +0 -3
  54. package/dist/slashMenu/items.d.ts +2 -2
  55. package/dist/style-inline.css +2110 -294
  56. package/dist/style.css +2110 -294
  57. package/dist/typography.test.d.ts +1 -0
  58. package/package.json +19 -3
  59. package/dist/extractToc-CGrBMGvM.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,29 +1,33 @@
1
1
  "use client";
2
2
  import { Markdown, MarkdownManager } from "@tiptap/markdown";
3
- import { c as renderOrUpdateChart, d as destroyChart } from "./scrollToTocHeading-B2QmL4wi.js";
4
- import { C, a, R, b, f, m, r, s } from "./scrollToTocHeading-B2QmL4wi.js";
3
+ import { c as renderOrUpdateChart, d as destroyChart } from "./scrollToTocHeading-kPgsn3cu.js";
4
+ import { C, a, R, b, f, m, r, s } from "./scrollToTocHeading-kPgsn3cu.js";
5
5
  import { jsx, jsxs, Fragment as Fragment$2 } from "react/jsx-runtime";
6
6
  import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
7
7
  import FindAndReplace, { FindAndReplacePluginKey, createSearchRegex } from "@tiptap/extension-find-and-replace";
8
8
  import { default as default2 } from "@tiptap/extension-find-and-replace";
9
+ import Typography from "@tiptap/extension-typography";
9
10
  import Image from "@tiptap/extension-image";
10
11
  import { TableOfContents } from "@tiptap/extension-table-of-contents";
11
- import { skipTrailingNodeMeta, Placeholder } from "@tiptap/extensions";
12
+ import { skipTrailingNodeMeta, Placeholder, Selection as Selection$1, CharacterCount } from "@tiptap/extensions";
12
13
  import { NodeViewWrapper, ReactNodeViewRenderer, useEditorState, ReactRenderer, NodeViewContent, useEditor, EditorContent } from "@tiptap/react";
13
14
  import { Extension, Node as Node$1, mergeAttributes } from "@tiptap/core";
14
15
  import { createPortal } from "react-dom";
15
- import { useState, useEffect, useCallback, useRef, useMemo, forwardRef, useImperativeHandle, Fragment as Fragment$1, useLayoutEffect, useId, useContext, createContext } from "react";
16
- import { e as enrichMarkdownCitations, c as styles$c } from "./ReportContent-B8jIzhy3.js";
17
- import { R as R2, a as a2, b as b2, s as s2 } from "./ReportContent-B8jIzhy3.js";
18
- import { C as CitationRef, i as mergeCommentIntervals, h as mapCommentAnchors, n as normalizeImageAlign, p as prepareChartMarkdown, j as reportChart, b as baseExtensions, f as CommentMark, l as lowlight, m as makeTocGetId, k as parseCodeTokenAsChartOrCodeBlock, o as imageMarkdownTokenizer, q as imageParseMarkdown, t as imageRenderMarkdown, u as imageNodeHtml, v as imageFigureParseRule, w as imageCaptionAttribute, x as imageAlignAttribute, y as normalizeImageCaption, a as pureCodeBlock, c as pureImage, z as renderMathInto, A as subscribeMathClick, d as pureChart } from "./extractToc-CGrBMGvM.js";
19
- import { P, g, e, s as s3 } from "./extractToc-CGrBMGvM.js";
16
+ import { useState, useEffect, useCallback, useRef, useMemo, useSyncExternalStore, useLayoutEffect, forwardRef, useId, useImperativeHandle, Fragment as Fragment$1, useContext, createContext } from "react";
17
+ import { e as enrichMarkdownCitations, c as styles$e } from "./ReportContent-w08GFm8v.js";
18
+ import { R as R2, a as a2, b as b2, s as s2 } from "./ReportContent-w08GFm8v.js";
19
+ import { C as CitationRef, i as mergeCommentIntervals, h as mapCommentAnchors, n as normalizeImageAlign, p as prepareChartMarkdown, j as reportChart, f as CommentMark, b as baseExtensions, l as lowlight, m as makeTocGetId, k as parseCodeTokenAsChartOrCodeBlock, o as imageMarkdownTokenizer, q as imageParseMarkdown, t as imageRenderMarkdown, u as imageNodeHtml, v as imageFigureParseRule, w as imageCaptionAttribute, x as imageAlignAttribute, y as normalizeImageCaption, a as pureCodeBlock, c as pureImage, z as renderMathInto, A as readCurrentNumberingFormat, B as subscribeMathClick, D as applyNumberingFormat, N as NUMBERING_FORMAT_IDS, E as NUMBERING_FORMAT_LABELS, F as NUMBERING_FORMAT_PREVIEWS, d as pureChart } from "./extractToc-Cr470fBy.js";
20
+ import { P, g, e, s as s3 } from "./extractToc-Cr470fBy.js";
20
21
  import { Slice, Fragment } from "@tiptap/pm/model";
21
22
  import { PluginKey, Plugin, Selection, TextSelection, NodeSelection } from "@tiptap/pm/state";
22
23
  import { DecorationSet, Decoration } from "@tiptap/pm/view";
24
+ import { EmojiSuggestionPluginKey } from "@tiptap/extension-emoji";
23
25
  import Suggestion from "@tiptap/suggestion";
24
26
  import { FileHandlePlugin } from "@tiptap/extension-file-handler";
25
27
  import { CellSelection, TableMap, isInTable, selectedRect, rowIsHeader, cellAround } from "@tiptap/pm/tables";
26
28
  import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
29
+ import { DragHandle } from "@tiptap/extension-drag-handle-react";
30
+ import { dropPoint } from "@tiptap/pm/transform";
27
31
  import * as Popover from "@radix-ui/react-popover";
28
32
  function CitationRefView({ node, extension }) {
29
33
  const index = String(node.attrs.index ?? "");
@@ -118,8 +122,8 @@ function sliceHasCommentMarks(slice, markType) {
118
122
  }
119
123
  function blockBounds(doc) {
120
124
  const bounds = [];
121
- doc.forEach((child, offset) => {
122
- bounds.push({ start: offset, end: offset + child.nodeSize });
125
+ doc.forEach((child, offset2) => {
126
+ bounds.push({ start: offset2, end: offset2 + child.nodeSize });
123
127
  });
124
128
  return bounds;
125
129
  }
@@ -511,6 +515,12 @@ const ListOrderedIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
511
515
  /* @__PURE__ */ jsx("path", { d: "M4 10h2" }),
512
516
  /* @__PURE__ */ jsx("path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1" })
513
517
  ] });
518
+ const DetailsIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
519
+ /* @__PURE__ */ jsx("path", { d: "m9 7 4 5-4 5" }),
520
+ /* @__PURE__ */ jsx("path", { d: "M15 7h5" }),
521
+ /* @__PURE__ */ jsx("path", { d: "M15 12h5" }),
522
+ /* @__PURE__ */ jsx("path", { d: "M15 17h5" })
523
+ ] });
514
524
  const ListChecksIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
515
525
  /* @__PURE__ */ jsx("path", { d: "m3 17 2 2 4-4" }),
516
526
  /* @__PURE__ */ jsx("path", { d: "m3 7 2 2 4-4" }),
@@ -624,6 +634,19 @@ const ChevronsUpDownIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
624
634
  /* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }),
625
635
  /* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })
626
636
  ] });
637
+ const GripIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, fill: "currentColor", strokeWidth: 0, children: [
638
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "6", r: "1.25" }),
639
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "6", r: "1.25" }),
640
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "12", r: "1.25" }),
641
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "12", r: "1.25" }),
642
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "18", r: "1.25" }),
643
+ /* @__PURE__ */ jsx("circle", { cx: "15", cy: "18", r: "1.25" })
644
+ ] });
645
+ const DownloadIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
646
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
647
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
648
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
649
+ ] });
627
650
  const KeyboardIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
628
651
  /* @__PURE__ */ jsx("rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }),
629
652
  /* @__PURE__ */ jsx("path", { d: "M6 8h.01" }),
@@ -698,8 +721,10 @@ const defaultToolbarLabels = {
698
721
  importDocumentConverting: "Converting…",
699
722
  importDocumentFailed: "Import failed",
700
723
  blockquote: "Blockquote",
724
+ details: "Toggle",
701
725
  bulletList: "Bullet list",
702
726
  orderedList: "Ordered list",
727
+ numberingFormat: "Numbering format",
703
728
  taskList: "Task list",
704
729
  more: "More",
705
730
  search: "Find & replace",
@@ -735,6 +760,23 @@ const defaultToolbarLabels = {
735
760
  mathNewInline: "New equation",
736
761
  mathNewBlock: "Add a TeX equation"
737
762
  };
763
+ const defaultBlockDragLabels = {
764
+ dragHandle: "Drag to move",
765
+ menuLabel: "Block actions",
766
+ turnInto: "Turn into",
767
+ paragraph: "Normal text",
768
+ headingLabel: (level) => `Heading ${level}`,
769
+ bulletList: "Bullet list",
770
+ orderedList: "Ordered list",
771
+ taskList: "Task list",
772
+ blockquote: "Blockquote",
773
+ codeBlock: "Code block",
774
+ copy: "Copy",
775
+ duplicate: "Duplicate",
776
+ resetFormatting: "Reset formatting",
777
+ downloadImage: "Download image",
778
+ delete: "Delete"
779
+ };
738
780
  const defaultSlashMenuLabels = {
739
781
  menuLabel: "Insert block",
740
782
  groupText: "Text",
@@ -746,6 +788,7 @@ const defaultSlashMenuLabels = {
746
788
  orderedList: "Ordered list",
747
789
  taskList: "Task list",
748
790
  blockquote: "Blockquote",
791
+ details: "Toggle",
749
792
  codeBlock: "Code block",
750
793
  divider: "Divider",
751
794
  table: "Table",
@@ -783,7 +826,19 @@ const defaultShortcutLabels = {
783
826
  alignCenter: "Align center",
784
827
  alignRight: "Align right",
785
828
  alignJustify: "Justify",
786
- imageUpload: "Upload image"
829
+ imageUpload: "Upload image",
830
+ smartQuote: "Smart quotes",
831
+ ellipsis: "Ellipsis",
832
+ emDash: "Em dash",
833
+ emoji: "Emoji"
834
+ };
835
+ const defaultCharacterCountLabels = {
836
+ label: "Document statistics",
837
+ characters: (count) => `${count} ${count === 1 ? "character" : "characters"}`,
838
+ words: (count) => `${count} ${count === 1 ? "word" : "words"}`
839
+ };
840
+ const defaultEmojiSuggestionLabels = {
841
+ menuLabel: "Emoji"
787
842
  };
788
843
  const defaultLinkPopoverLabels = {
789
844
  field: "Enter URL",
@@ -806,16 +861,8 @@ const defaultCodeBlockLabels = {
806
861
  delete: "Delete code block",
807
862
  copy: "Copy code",
808
863
  expand: "Expand code",
809
- collapse: "Collapse code"
810
- };
811
- const defaultCommentLabels = {
812
- open: "Open",
813
- resolved: "Resolved",
814
- dismissed: "Dismissed",
815
- outdated: "Outdated",
816
- ambiguous: "Ambiguous",
817
- partial: "Partially matched",
818
- gutterTitle: "Comment"
864
+ collapse: "Collapse code",
865
+ language: "Code language"
819
866
  };
820
867
  const defaultFindLabels = {
821
868
  find: "Find",
@@ -845,6 +892,260 @@ const defaultImageUploadLabels = {
845
892
  failed: "Upload failed",
846
893
  fallbackAlt: "image"
847
894
  };
895
+ const zh = {
896
+ toolbar: {
897
+ undo: "撤销",
898
+ redo: "重做",
899
+ style: "样式",
900
+ normalText: "正文",
901
+ headingLabel: (level) => `标题 ${level}`,
902
+ fontSize: "字号",
903
+ fontSizeDefault: "默认",
904
+ bold: "加粗",
905
+ italic: "斜体",
906
+ underline: "下划线",
907
+ strike: "删除线",
908
+ code: "行内代码",
909
+ textColor: "文字颜色",
910
+ highlight: "高亮",
911
+ colorNone: "无",
912
+ colorTheme: "主题色",
913
+ scriptMenu: "上标 / 下标",
914
+ scriptNormal: "常规",
915
+ superscript: "上标",
916
+ subscript: "下标",
917
+ link: "链接",
918
+ align: "对齐",
919
+ alignLeft: "左对齐",
920
+ alignCenter: "居中",
921
+ alignRight: "右对齐",
922
+ alignJustify: "两端对齐",
923
+ indent: "缩进",
924
+ indentIncrease: "增加缩进",
925
+ indentDecrease: "减少缩进",
926
+ lineHeight: "行高",
927
+ lineHeightDefault: "默认",
928
+ linkPrompt: "输入链接地址",
929
+ linkApply: "应用链接",
930
+ linkOpen: "在新窗口打开",
931
+ linkRemove: "移除链接",
932
+ linkInvalid: "编辑器不接受这个地址",
933
+ image: "图片",
934
+ imageUploadFailed: "图片上传失败",
935
+ imageAlign: "图片对齐",
936
+ imageAlignLeft: "左对齐",
937
+ imageAlignCenter: "居中",
938
+ imageAlignRight: "右对齐",
939
+ imageCaption: "图片描述",
940
+ imageCaptionPlaceholder: "添加描述…",
941
+ importMarkdownFailed: "Markdown 导入失败",
942
+ importDocument: "导入",
943
+ importDocumentHint: "导入文件",
944
+ importDocumentOther: "文档",
945
+ importDocumentBusy: "正在导入…",
946
+ importDocumentUploading: (percent) => `上传中 ${percent}%`,
947
+ importDocumentConverting: "正在转换…",
948
+ importDocumentFailed: "导入失败",
949
+ blockquote: "引用",
950
+ details: "折叠块",
951
+ bulletList: "无序列表",
952
+ orderedList: "有序列表",
953
+ numberingFormat: "编号格式",
954
+ taskList: "任务列表",
955
+ more: "更多",
956
+ search: "查找替换",
957
+ codeBlock: "代码块",
958
+ hr: "分隔线",
959
+ importMarkdown: "Markdown",
960
+ tableInsert: "插入表格",
961
+ tableRowMenu: "行操作",
962
+ tableColumnMenu: "列操作",
963
+ tableClearContent: "清空内容",
964
+ tableResetCellStyles: "重置单元格样式",
965
+ tableAppendRow: "添加行",
966
+ tableAppendColumn: "添加列",
967
+ tableSizeSelected: (cols, rows) => `${cols} × ${rows}`,
968
+ tableAddColumnBefore: "在左侧插入列",
969
+ tableAddColumnAfter: "在右侧插入列",
970
+ tableDeleteColumn: "删除列",
971
+ tableAddRowBefore: "在上方插入行",
972
+ tableAddRowAfter: "在下方插入行",
973
+ tableDeleteRow: "删除行",
974
+ tableAddColumnBeforeN: (n) => `在左侧插入 ${n} 列`,
975
+ tableAddColumnAfterN: (n) => `在右侧插入 ${n} 列`,
976
+ tableDeleteColumnN: (n) => `删除 ${n} 列`,
977
+ tableAddRowBeforeN: (n) => `在上方插入 ${n} 行`,
978
+ tableAddRowAfterN: (n) => `在下方插入 ${n} 行`,
979
+ tableDeleteRowN: (n) => `删除 ${n} 行`,
980
+ tableDeleteTable: "删除表格",
981
+ inlineMath: "行内公式",
982
+ blockMath: "块级公式",
983
+ mathPlaceholder: "E = mc^2",
984
+ mathDone: "完成",
985
+ mathCancel: "取消",
986
+ mathNewInline: "新建公式",
987
+ mathNewBlock: "插入 TeX 公式"
988
+ },
989
+ slashMenu: {
990
+ menuLabel: "插入块",
991
+ groupText: "文本",
992
+ groupList: "列表",
993
+ groupAdvanced: "高级",
994
+ normalText: "正文",
995
+ headingLabel: (level) => `标题 ${level}`,
996
+ bulletList: "无序列表",
997
+ orderedList: "有序列表",
998
+ taskList: "任务列表",
999
+ blockquote: "引用",
1000
+ details: "折叠块",
1001
+ codeBlock: "代码块",
1002
+ divider: "分隔线",
1003
+ table: "表格",
1004
+ inlineMath: "行内公式",
1005
+ blockMath: "块级公式"
1006
+ },
1007
+ chart: {
1008
+ tabLabel: (chartType, index) => `${chartType} ${index + 1}`,
1009
+ done: "完成",
1010
+ cancel: "取消",
1011
+ configLabel: "图表配置(JSON)",
1012
+ tableLabel: "数据表(Markdown)",
1013
+ editTitle: "编辑图表"
1014
+ },
1015
+ blockDrag: {
1016
+ dragHandle: "拖动以移动",
1017
+ menuLabel: "块操作",
1018
+ turnInto: "转换成",
1019
+ paragraph: "正文",
1020
+ headingLabel: (level) => `标题 ${level}`,
1021
+ bulletList: "无序列表",
1022
+ orderedList: "有序列表",
1023
+ taskList: "任务列表",
1024
+ blockquote: "引用",
1025
+ codeBlock: "代码块",
1026
+ copy: "复制",
1027
+ duplicate: "再制一份",
1028
+ resetFormatting: "清除格式",
1029
+ downloadImage: "下载图片",
1030
+ delete: "删除"
1031
+ },
1032
+ shortcut: {
1033
+ panelTitle: "快捷键",
1034
+ close: "关闭",
1035
+ colFormat: "格式",
1036
+ colShortcut: "快捷键",
1037
+ colMarkdown: "Markdown",
1038
+ formatGroup: "格式",
1039
+ insertGroup: "插入",
1040
+ editGroup: "编辑",
1041
+ bold: "加粗",
1042
+ italic: "斜体",
1043
+ strike: "删除线",
1044
+ underline: "下划线",
1045
+ inlineCode: "行内代码",
1046
+ highlight: "高亮",
1047
+ headingLabel: (level) => `标题 ${level}`,
1048
+ paragraph: "正文",
1049
+ blockquote: "引用",
1050
+ bulletList: "无序列表",
1051
+ orderedList: "有序列表",
1052
+ taskList: "任务列表",
1053
+ codeBlock: "代码块",
1054
+ divider: "分隔线",
1055
+ undo: "撤销",
1056
+ redo: "重做",
1057
+ hardBreak: "硬换行",
1058
+ findReplace: "查找替换",
1059
+ alignLeft: "左对齐",
1060
+ alignCenter: "居中",
1061
+ alignRight: "右对齐",
1062
+ alignJustify: "两端对齐",
1063
+ imageUpload: "上传图片",
1064
+ smartQuote: "智能引号",
1065
+ ellipsis: "省略号",
1066
+ emDash: "破折号",
1067
+ emoji: "表情"
1068
+ },
1069
+ characterCount: {
1070
+ label: "字数统计",
1071
+ characters: (count) => `${count} 字符`,
1072
+ words: (count) => `${count} 词`
1073
+ },
1074
+ emoji: {
1075
+ menuLabel: "表情"
1076
+ },
1077
+ link: {
1078
+ field: "输入链接地址",
1079
+ apply: "应用链接",
1080
+ open: "在新窗口打开",
1081
+ remove: "移除链接",
1082
+ invalid: "编辑器不接受这个地址"
1083
+ },
1084
+ palette: {
1085
+ none: "无",
1086
+ theme: "主题色"
1087
+ },
1088
+ toc: {
1089
+ title: "目录",
1090
+ expand: "展开",
1091
+ collapse: "收起"
1092
+ },
1093
+ codeBlock: {
1094
+ autoDetect: "自动识别",
1095
+ delete: "删除代码块",
1096
+ copy: "复制代码",
1097
+ expand: "展开代码",
1098
+ collapse: "收起代码",
1099
+ language: "代码语言"
1100
+ },
1101
+ find: {
1102
+ find: "查找",
1103
+ replace: "替换为",
1104
+ next: "下一处",
1105
+ previous: "上一处",
1106
+ replaceOne: "替换",
1107
+ replaceAll: "全部替换",
1108
+ close: "关闭",
1109
+ caseSensitive: "区分大小写",
1110
+ wholeWord: "全词匹配",
1111
+ useRegex: "正则表达式",
1112
+ counter: (current, total) => `${current} / ${total}`,
1113
+ invalidRegex: "正则无效",
1114
+ readOnly: "只读"
1115
+ },
1116
+ imageUpload: {
1117
+ dropzoneClick: "点击上传",
1118
+ dropzoneRest: ",或拖拽到此处",
1119
+ dropzoneLimits: (limit, maxSizeMb) => {
1120
+ const count = `最多 ${limit} 个文件`;
1121
+ return maxSizeMb == null ? `${count}。` : `${count},每个 ${maxSizeMb}MB。`;
1122
+ },
1123
+ uploadingCount: (count) => `正在上传 ${count} 个文件`,
1124
+ clearAll: "全部清除",
1125
+ remove: "移除",
1126
+ failed: "上传失败",
1127
+ fallbackAlt: "图片"
1128
+ }
1129
+ };
1130
+ const en = {
1131
+ toolbar: defaultToolbarLabels,
1132
+ slashMenu: defaultSlashMenuLabels,
1133
+ chart: defaultChartLabels,
1134
+ blockDrag: defaultBlockDragLabels,
1135
+ shortcut: defaultShortcutLabels,
1136
+ characterCount: defaultCharacterCountLabels,
1137
+ emoji: defaultEmojiSuggestionLabels,
1138
+ link: defaultLinkPopoverLabels,
1139
+ palette: defaultColorPaletteLabels,
1140
+ toc: defaultTocLabels,
1141
+ codeBlock: defaultCodeBlockLabels,
1142
+ find: defaultFindLabels,
1143
+ imageUpload: defaultImageUploadLabels
1144
+ };
1145
+ const bundles = { en, zh };
1146
+ function getLocaleBundle(locale = "en") {
1147
+ return bundles[locale] ?? en;
1148
+ }
848
1149
  const bar$1 = "_bar_1h61e_3";
849
1150
  const row$1 = "_row_1h61e_25";
850
1151
  const field$1 = "_field_1h61e_43";
@@ -858,7 +1159,7 @@ const toggleOn = "_toggleOn_1h61e_135";
858
1159
  const iconGroup = "_iconGroup_1h61e_29";
859
1160
  const spacer = "_spacer_1h61e_150";
860
1161
  const readOnly = "_readOnly_1h61e_155";
861
- const styles$b = {
1162
+ const styles$d = {
862
1163
  bar: bar$1,
863
1164
  row: row$1,
864
1165
  field: field$1,
@@ -884,12 +1185,12 @@ function warnMissingExtensionOnce() {
884
1185
  function FindReplaceBar({
885
1186
  editor,
886
1187
  onClose,
887
- labels,
1188
+ locale = "en",
888
1189
  inputRef,
889
1190
  className,
890
1191
  style
891
1192
  }) {
892
- const t = { ...defaultFindLabels, ...labels };
1193
+ const t = getLocaleBundle(locale).find;
893
1194
  const [term, setTerm] = useState("");
894
1195
  const [replaceTerm, setReplaceTerm] = useState("");
895
1196
  const hasExtension = hasFindReplaceExtension(editor);
@@ -982,11 +1283,11 @@ function FindReplaceBar({
982
1283
  });
983
1284
  };
984
1285
  if (!hasExtension) return null;
985
- const toggleClass = (on) => `${styles$b.btn} ${styles$b.toggle}${on ? ` ${styles$b.toggleOn}` : ""}`;
1286
+ const toggleClass = (on) => `${styles$d.btn} ${styles$d.toggle}${on ? ` ${styles$d.toggleOn}` : ""}`;
986
1287
  return /* @__PURE__ */ jsxs(
987
1288
  "div",
988
1289
  {
989
- className: className ? `${styles$b.bar} ${className}` : styles$b.bar,
1290
+ className: className ? `${styles$d.bar} ${className}` : styles$d.bar,
990
1291
  style,
991
1292
  "data-find-bar": "",
992
1293
  role: "dialog",
@@ -999,14 +1300,14 @@ function FindReplaceBar({
999
1300
  onClose();
1000
1301
  },
1001
1302
  children: [
1002
- /* @__PURE__ */ jsxs("div", { className: styles$b.row, children: [
1003
- /* @__PURE__ */ jsxs("span", { className: styles$b.field, children: [
1004
- /* @__PURE__ */ jsx(SearchIcon, { size: 14, className: styles$b.fieldIcon }),
1303
+ /* @__PURE__ */ jsxs("div", { className: styles$d.row, children: [
1304
+ /* @__PURE__ */ jsxs("span", { className: styles$d.field, children: [
1305
+ /* @__PURE__ */ jsx(SearchIcon, { size: 14, className: styles$d.fieldIcon }),
1005
1306
  /* @__PURE__ */ jsx(
1006
1307
  "input",
1007
1308
  {
1008
1309
  ref: inputRef,
1009
- className: styles$b.input,
1310
+ className: styles$d.input,
1010
1311
  "data-find-field": "search",
1011
1312
  value: term,
1012
1313
  onChange: (e2) => setTerm(e2.target.value),
@@ -1020,7 +1321,7 @@ function FindReplaceBar({
1020
1321
  /* @__PURE__ */ jsx(
1021
1322
  "span",
1022
1323
  {
1023
- className: `${styles$b.counter}${invalidPattern ? ` ${styles$b.counterInvalid}` : ""}`,
1324
+ className: `${styles$d.counter}${invalidPattern ? ` ${styles$d.counterInvalid}` : ""}`,
1024
1325
  "data-find-counter": "",
1025
1326
  "aria-live": "polite",
1026
1327
  title: invalidPattern ? t.invalidRegex : void 0,
@@ -1056,13 +1357,13 @@ function FindReplaceBar({
1056
1357
  children: "ab"
1057
1358
  }
1058
1359
  ),
1059
- /* @__PURE__ */ jsx("span", { className: styles$b.spacer }),
1060
- /* @__PURE__ */ jsxs("span", { className: styles$b.iconGroup, children: [
1360
+ /* @__PURE__ */ jsx("span", { className: styles$d.spacer }),
1361
+ /* @__PURE__ */ jsxs("span", { className: styles$d.iconGroup, children: [
1061
1362
  /* @__PURE__ */ jsx(
1062
1363
  "button",
1063
1364
  {
1064
1365
  type: "button",
1065
- className: styles$b.btn,
1366
+ className: styles$d.btn,
1066
1367
  "data-find-action": "previous",
1067
1368
  title: t.previous,
1068
1369
  "aria-label": t.previous,
@@ -1076,7 +1377,7 @@ function FindReplaceBar({
1076
1377
  "button",
1077
1378
  {
1078
1379
  type: "button",
1079
- className: styles$b.btn,
1380
+ className: styles$d.btn,
1080
1381
  "data-find-action": "next",
1081
1382
  title: t.next,
1082
1383
  "aria-label": t.next,
@@ -1090,7 +1391,7 @@ function FindReplaceBar({
1090
1391
  "button",
1091
1392
  {
1092
1393
  type: "button",
1093
- className: styles$b.btn,
1394
+ className: styles$d.btn,
1094
1395
  "data-find-action": "close",
1095
1396
  title: t.close,
1096
1397
  "aria-label": t.close,
@@ -1100,11 +1401,11 @@ function FindReplaceBar({
1100
1401
  )
1101
1402
  ] })
1102
1403
  ] }),
1103
- editor.isEditable ? /* @__PURE__ */ jsxs("div", { className: styles$b.row, children: [
1104
- /* @__PURE__ */ jsx("span", { className: styles$b.field, children: /* @__PURE__ */ jsx(
1404
+ editor.isEditable ? /* @__PURE__ */ jsxs("div", { className: styles$d.row, children: [
1405
+ /* @__PURE__ */ jsx("span", { className: styles$d.field, children: /* @__PURE__ */ jsx(
1105
1406
  "input",
1106
1407
  {
1107
- className: styles$b.input,
1408
+ className: styles$d.input,
1108
1409
  "data-find-field": "replace",
1109
1410
  value: replaceTerm,
1110
1411
  onChange: (e2) => setReplaceTerm(e2.target.value),
@@ -1114,12 +1415,12 @@ function FindReplaceBar({
1114
1415
  spellCheck: false
1115
1416
  }
1116
1417
  ) }),
1117
- /* @__PURE__ */ jsx("span", { className: styles$b.spacer }),
1418
+ /* @__PURE__ */ jsx("span", { className: styles$d.spacer }),
1118
1419
  /* @__PURE__ */ jsx(
1119
1420
  "button",
1120
1421
  {
1121
1422
  type: "button",
1122
- className: styles$b.btn,
1423
+ className: styles$d.btn,
1123
1424
  "data-find-action": "replace",
1124
1425
  title: t.replaceOne,
1125
1426
  "aria-label": t.replaceOne,
@@ -1133,7 +1434,7 @@ function FindReplaceBar({
1133
1434
  "button",
1134
1435
  {
1135
1436
  type: "button",
1136
- className: styles$b.btn,
1437
+ className: styles$d.btn,
1137
1438
  "data-find-action": "replace-all",
1138
1439
  title: t.replaceAll,
1139
1440
  "aria-label": t.replaceAll,
@@ -1143,7 +1444,7 @@ function FindReplaceBar({
1143
1444
  children: /* @__PURE__ */ jsx(ReplaceAllIcon, { size: 16 })
1144
1445
  }
1145
1446
  )
1146
- ] }) : /* @__PURE__ */ jsx("div", { className: styles$b.row, children: /* @__PURE__ */ jsx("span", { className: styles$b.readOnly, children: t.readOnly }) })
1447
+ ] }) : /* @__PURE__ */ jsx("div", { className: styles$d.row, children: /* @__PURE__ */ jsx("span", { className: styles$d.readOnly, children: t.readOnly }) })
1147
1448
  ]
1148
1449
  }
1149
1450
  );
@@ -1230,6 +1531,65 @@ const ImportPlaceholder = Node$1.create({
1230
1531
  };
1231
1532
  }
1232
1533
  });
1534
+ const serverBundle = getLocaleBundle("en");
1535
+ const stores = /* @__PURE__ */ new WeakMap();
1536
+ function ensure(editor) {
1537
+ let entry = stores.get(editor);
1538
+ if (!entry) {
1539
+ entry = {
1540
+ locale: "en",
1541
+ bundle: serverBundle,
1542
+ listeners: /* @__PURE__ */ new Set(),
1543
+ pendingNotify: false
1544
+ };
1545
+ stores.set(editor, entry);
1546
+ }
1547
+ return entry;
1548
+ }
1549
+ function setEditorLocale(editor, locale) {
1550
+ const entry = ensure(editor);
1551
+ if (entry.locale === locale) return;
1552
+ entry.locale = locale;
1553
+ entry.bundle = getLocaleBundle(locale);
1554
+ entry.pendingNotify = true;
1555
+ }
1556
+ function flushEditorLocale(editor) {
1557
+ const entry = stores.get(editor);
1558
+ if (!(entry == null ? void 0 : entry.pendingNotify)) return;
1559
+ entry.pendingNotify = false;
1560
+ entry.listeners.forEach((listener) => listener());
1561
+ }
1562
+ function getEditorLocale(editor) {
1563
+ var _a;
1564
+ return ((_a = stores.get(editor)) == null ? void 0 : _a.locale) ?? "en";
1565
+ }
1566
+ function getEditorBundle(editor) {
1567
+ var _a;
1568
+ return ((_a = stores.get(editor)) == null ? void 0 : _a.bundle) ?? serverBundle;
1569
+ }
1570
+ function subscribeEditorLocale(editor, listener) {
1571
+ const entry = ensure(editor);
1572
+ entry.listeners.add(listener);
1573
+ return () => {
1574
+ entry.listeners.delete(listener);
1575
+ };
1576
+ }
1577
+ const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
1578
+ function usePublishEditorLocale(editor, locale) {
1579
+ if (editor) setEditorLocale(editor, locale);
1580
+ useIsoLayoutEffect(() => {
1581
+ if (!editor || editor.isDestroyed) return;
1582
+ flushEditorLocale(editor);
1583
+ }, [editor, locale]);
1584
+ }
1585
+ function useEditorBundle(editor) {
1586
+ const subscribe = useCallback(
1587
+ (onStoreChange) => subscribeEditorLocale(editor, onStoreChange),
1588
+ [editor]
1589
+ );
1590
+ const getSnapshot = useCallback(() => getEditorBundle(editor), [editor]);
1591
+ return useSyncExternalStore(subscribe, getSnapshot, () => serverBundle);
1592
+ }
1233
1593
  const wrap = "_wrap_1k6n1_8";
1234
1594
  const dropzone = "_dropzone_1k6n1_24";
1235
1595
  const dragActive = "_dragActive_1k6n1_43";
@@ -1255,7 +1615,7 @@ const fileName = "_fileName_1k6n1_205";
1255
1615
  const actions = "_actions_1k6n1_215";
1256
1616
  const progressText = "_progressText_1k6n1_222";
1257
1617
  const remove = "_remove_1k6n1_228";
1258
- const styles$a = {
1618
+ const styles$c = {
1259
1619
  wrap,
1260
1620
  dropzone,
1261
1621
  dragActive,
@@ -1312,10 +1672,7 @@ function focusNextNode(editor) {
1312
1672
  function ImageUploadView(props) {
1313
1673
  const { node, extension, editor, getPos } = props;
1314
1674
  const options = extension.options;
1315
- const labels = {
1316
- ...defaultImageUploadLabels,
1317
- ...options.labels
1318
- };
1675
+ const labels = useEditorBundle(editor).imageUpload;
1319
1676
  const accept = String(node.attrs.accept ?? "image/*");
1320
1677
  const limit = Number(node.attrs.limit ?? 1) || 1;
1321
1678
  const maxSize = Number(node.attrs.maxSize ?? 0) || 0;
@@ -1433,14 +1790,14 @@ function ImageUploadView(props) {
1433
1790
  const hasFiles = items.length > 0;
1434
1791
  const maxSizeMb = maxSize > 0 ? toMb(maxSize) : null;
1435
1792
  const dropzoneClass = [
1436
- styles$a.dropzone,
1437
- dragActive2 ? styles$a.dragActive : "",
1438
- dragOver2 ? styles$a.dragOver : ""
1793
+ styles$c.dropzone,
1794
+ dragActive2 ? styles$c.dragActive : "",
1795
+ dragOver2 ? styles$c.dragOver : ""
1439
1796
  ].filter(Boolean).join(" ");
1440
1797
  return /* @__PURE__ */ jsxs(
1441
1798
  NodeViewWrapper,
1442
1799
  {
1443
- className: styles$a.wrap,
1800
+ className: styles$c.wrap,
1444
1801
  "data-image-upload": "",
1445
1802
  tabIndex: editable ? 0 : void 0,
1446
1803
  onClick: openPicker,
@@ -1472,28 +1829,28 @@ function ImageUploadView(props) {
1472
1829
  },
1473
1830
  onDrop,
1474
1831
  children: [
1475
- /* @__PURE__ */ jsxs("span", { className: styles$a.fileShape, "aria-hidden": true, children: [
1832
+ /* @__PURE__ */ jsxs("span", { className: styles$c.fileShape, "aria-hidden": true, children: [
1476
1833
  /* @__PURE__ */ jsx(FileDocIcon, {}),
1477
1834
  /* @__PURE__ */ jsx(FileCornerIcon, {}),
1478
- /* @__PURE__ */ jsx("span", { className: styles$a.fileBadge, children: /* @__PURE__ */ jsx(CloudUploadIcon, {}) })
1835
+ /* @__PURE__ */ jsx("span", { className: styles$c.fileBadge, children: /* @__PURE__ */ jsx(CloudUploadIcon, {}) })
1479
1836
  ] }),
1480
- /* @__PURE__ */ jsxs("span", { className: styles$a.text, children: [
1481
- /* @__PURE__ */ jsxs("span", { className: styles$a.textMain, children: [
1482
- /* @__PURE__ */ jsx("em", { className: styles$a.textEm, children: labels.dropzoneClick }),
1837
+ /* @__PURE__ */ jsxs("span", { className: styles$c.text, children: [
1838
+ /* @__PURE__ */ jsxs("span", { className: styles$c.textMain, children: [
1839
+ /* @__PURE__ */ jsx("em", { className: styles$c.textEm, children: labels.dropzoneClick }),
1483
1840
  labels.dropzoneRest
1484
1841
  ] }),
1485
- /* @__PURE__ */ jsx("span", { className: styles$a.subtext, children: labels.dropzoneLimits(limit, maxSizeMb) })
1842
+ /* @__PURE__ */ jsx("span", { className: styles$c.subtext, children: labels.dropzoneLimits(limit, maxSizeMb) })
1486
1843
  ] })
1487
1844
  ]
1488
1845
  }
1489
- ) : /* @__PURE__ */ jsxs("div", { className: styles$a.previews, "data-image-upload-list": "", children: [
1490
- items.length > 1 && /* @__PURE__ */ jsxs("div", { className: styles$a.header, children: [
1846
+ ) : /* @__PURE__ */ jsxs("div", { className: styles$c.previews, "data-image-upload-list": "", children: [
1847
+ items.length > 1 && /* @__PURE__ */ jsxs("div", { className: styles$c.header, children: [
1491
1848
  /* @__PURE__ */ jsx("span", { children: labels.uploadingCount(items.length) }),
1492
1849
  /* @__PURE__ */ jsx(
1493
1850
  "button",
1494
1851
  {
1495
1852
  type: "button",
1496
- className: styles$a.clearAll,
1853
+ className: styles$c.clearAll,
1497
1854
  onClick: (e2) => {
1498
1855
  e2.stopPropagation();
1499
1856
  clearAll2();
@@ -1505,27 +1862,27 @@ function ImageUploadView(props) {
1505
1862
  items.map((it) => /* @__PURE__ */ jsxs(
1506
1863
  "div",
1507
1864
  {
1508
- className: styles$a.preview,
1865
+ className: styles$c.preview,
1509
1866
  "data-image-upload-item": "",
1510
1867
  children: [
1511
1868
  it.status === "uploading" && /* @__PURE__ */ jsx(
1512
1869
  "div",
1513
1870
  {
1514
- className: styles$a.progress,
1871
+ className: styles$c.progress,
1515
1872
  style: { width: `${it.progress}%` },
1516
1873
  "aria-hidden": true
1517
1874
  }
1518
1875
  ),
1519
- /* @__PURE__ */ jsxs("div", { className: styles$a.previewContent, children: [
1520
- /* @__PURE__ */ jsxs("span", { className: styles$a.fileInfo, children: [
1521
- /* @__PURE__ */ jsx("span", { className: styles$a.fileIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(CloudUploadIcon, {}) }),
1522
- /* @__PURE__ */ jsxs("span", { className: styles$a.details, children: [
1523
- /* @__PURE__ */ jsx("span", { className: styles$a.fileName, children: it.file.name }),
1524
- /* @__PURE__ */ jsx("span", { className: styles$a.subtext, children: it.status === "error" ? labels.failed : formatFileSize(it.file.size) })
1876
+ /* @__PURE__ */ jsxs("div", { className: styles$c.previewContent, children: [
1877
+ /* @__PURE__ */ jsxs("span", { className: styles$c.fileInfo, children: [
1878
+ /* @__PURE__ */ jsx("span", { className: styles$c.fileIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(CloudUploadIcon, {}) }),
1879
+ /* @__PURE__ */ jsxs("span", { className: styles$c.details, children: [
1880
+ /* @__PURE__ */ jsx("span", { className: styles$c.fileName, children: it.file.name }),
1881
+ /* @__PURE__ */ jsx("span", { className: styles$c.subtext, children: it.status === "error" ? labels.failed : formatFileSize(it.file.size) })
1525
1882
  ] })
1526
1883
  ] }),
1527
- /* @__PURE__ */ jsxs("span", { className: styles$a.actions, children: [
1528
- it.status === "uploading" && /* @__PURE__ */ jsxs("span", { className: styles$a.progressText, children: [
1884
+ /* @__PURE__ */ jsxs("span", { className: styles$c.actions, children: [
1885
+ it.status === "uploading" && /* @__PURE__ */ jsxs("span", { className: styles$c.progressText, children: [
1529
1886
  Math.round(it.progress),
1530
1887
  "%"
1531
1888
  ] }),
@@ -1533,7 +1890,7 @@ function ImageUploadView(props) {
1533
1890
  "button",
1534
1891
  {
1535
1892
  type: "button",
1536
- className: styles$a.remove,
1893
+ className: styles$c.remove,
1537
1894
  "data-image-upload-remove": "",
1538
1895
  title: labels.remove,
1539
1896
  "aria-label": labels.remove,
@@ -1592,7 +1949,7 @@ function FileDocIcon() {
1592
1949
  viewBox: "0 0 43 57",
1593
1950
  fill: "none",
1594
1951
  "aria-hidden": true,
1595
- className: styles$a.fileShapeDoc,
1952
+ className: styles$c.fileShapeDoc,
1596
1953
  children: /* @__PURE__ */ jsx(
1597
1954
  "path",
1598
1955
  {
@@ -1615,7 +1972,7 @@ function FileCornerIcon() {
1615
1972
  viewBox: "0 0 10 10",
1616
1973
  fill: "currentColor",
1617
1974
  "aria-hidden": true,
1618
- className: styles$a.fileShapeCorner,
1975
+ className: styles$c.fileShapeCorner,
1619
1976
  children: /* @__PURE__ */ jsx("path", { d: "M0 0.75H0.343146C1.40401 0.75 2.42143 1.17143 3.17157 1.92157L8.82843 7.57843C9.57857 8.32857 10 9.34599 10 10.4069V10.75H4C1.79086 10.75 0 8.95914 0 6.75V0.75Z" })
1620
1977
  }
1621
1978
  );
@@ -1639,8 +1996,7 @@ const ImageUploadNode = Node$1.create({
1639
1996
  maxSize: 0,
1640
1997
  type: "image",
1641
1998
  onError: void 0,
1642
- onSuccess: void 0,
1643
- labels: void 0
1999
+ onSuccess: void 0
1644
2000
  };
1645
2001
  },
1646
2002
  addAttributes() {
@@ -1739,23 +2095,305 @@ function setImageAlignAt(editor, imageElement, align) {
1739
2095
  return false;
1740
2096
  }
1741
2097
  }
2098
+ const popupHost$1 = "_popupHost_xhti8_5";
2099
+ const panel$1 = "_panel_xhti8_11";
2100
+ const menu$3 = "_menu_xhti8_22";
2101
+ const tabs = "_tabs_xhti8_32";
2102
+ const tab = "_tab_xhti8_32";
2103
+ const tabActive = "_tabActive_xhti8_40";
2104
+ const cell = "_cell_xhti8_71";
2105
+ const cellActive = "_cellActive_xhti8_72";
2106
+ const styles$b = {
2107
+ popupHost: popupHost$1,
2108
+ panel: panel$1,
2109
+ menu: menu$3,
2110
+ tabs,
2111
+ tab,
2112
+ tabActive,
2113
+ cell,
2114
+ cellActive
2115
+ };
2116
+ const EMOJI_CATEGORIES = [
2117
+ { id: "smileys", group: "", label: "Smileys", icon: "😀" },
2118
+ { id: "people", group: "people & body", label: "People", icon: "👋" },
2119
+ { id: "nature", group: "animals & nature", label: "Animals & nature", icon: "🐻" },
2120
+ { id: "food", group: "food & drink", label: "Food & drink", icon: "🍔" },
2121
+ { id: "travel", group: "travel & places", label: "Travel & places", icon: "✈️" },
2122
+ { id: "activities", group: "activities", label: "Activities", icon: "⚽" },
2123
+ { id: "objects", group: "objects", label: "Objects", icon: "💡" },
2124
+ { id: "symbols", group: "symbols", label: "Symbols", icon: "💯" },
2125
+ { id: "flags", group: "flags", label: "Flags", icon: "🏁" }
2126
+ ];
2127
+ function isEmojiComponent(item2) {
2128
+ return item2.group === "components";
2129
+ }
2130
+ function itemsInCategory(items, id) {
2131
+ const category = EMOJI_CATEGORIES.find((entry) => entry.id === id);
2132
+ if (!category) return [];
2133
+ return items.filter((item2) => (item2.group ?? "") === category.group);
2134
+ }
2135
+ function isRegionalIndicator(item2) {
2136
+ return item2.name.startsWith("regional_indicator_");
2137
+ }
2138
+ function filterEmojiItems(items, query) {
2139
+ const q = query.trim().toLowerCase();
2140
+ const usable = items.filter(
2141
+ (item2) => !!item2.emoji && !isRegionalIndicator(item2) && !isEmojiComponent(item2)
2142
+ );
2143
+ if (!q) return usable;
2144
+ const primary = [];
2145
+ const secondary = [];
2146
+ for (const item2 of usable) {
2147
+ const nameHit = item2.name.toLowerCase().startsWith(q) || item2.shortcodes.some((code) => code.toLowerCase().startsWith(q));
2148
+ if (nameHit) primary.push(item2);
2149
+ else if (item2.tags.some((tag) => tag.toLowerCase().startsWith(q))) {
2150
+ secondary.push(item2);
2151
+ }
2152
+ }
2153
+ return [...primary, ...secondary];
2154
+ }
2155
+ const COLUMNS = 8;
2156
+ function isComposing$1(event, view) {
2157
+ return view.composing || event.isComposing || event.keyCode === 229;
2158
+ }
2159
+ const themeVars = [
2160
+ "--tmr-text",
2161
+ "--tmr-accent",
2162
+ "--tmr-toolbar-border",
2163
+ "--tmr-toolbar-muted"
2164
+ ];
2165
+ function copyThemeVars(from, to) {
2166
+ const computed = getComputedStyle(from);
2167
+ for (const name of themeVars) {
2168
+ const value = computed.getPropertyValue(name);
2169
+ if (value) to.style.setProperty(name, value);
2170
+ }
2171
+ }
2172
+ function moveIndex(index, key, length, shiftKey) {
2173
+ if (key === "ArrowRight" || key === "Tab" && !shiftKey) {
2174
+ return (index + 1) % length;
2175
+ }
2176
+ if (key === "ArrowLeft" || key === "Tab" && shiftKey) {
2177
+ return (index - 1 + length) % length;
2178
+ }
2179
+ if (key === "ArrowDown") {
2180
+ const next = index + COLUMNS;
2181
+ return next < length ? next : null;
2182
+ }
2183
+ if (key === "ArrowUp") {
2184
+ const next = index - COLUMNS;
2185
+ return next >= 0 ? next : null;
2186
+ }
2187
+ return null;
2188
+ }
2189
+ function keepCellInView(menu2, cell2) {
2190
+ const menuRect = menu2.getBoundingClientRect();
2191
+ const cellRect = cell2.getBoundingClientRect();
2192
+ if (cellRect.top < menuRect.top) menu2.scrollTop -= menuRect.top - cellRect.top;
2193
+ else if (cellRect.bottom > menuRect.bottom) {
2194
+ menu2.scrollTop += cellRect.bottom - menuRect.bottom;
2195
+ }
2196
+ }
2197
+ const EmojiSuggestionPopup = forwardRef(function EmojiSuggestionPopup2({ items, query, menuLabel, command }, ref) {
2198
+ var _a;
2199
+ const gridId = useId();
2200
+ const browsing = !query.trim();
2201
+ const [categoryId, setCategoryId] = useState(EMOJI_CATEGORIES[0].id);
2202
+ const [index, setIndex] = useState(0);
2203
+ const menuRef = useRef(null);
2204
+ const cellRefs = useRef([]);
2205
+ const categories = useMemo(
2206
+ () => EMOJI_CATEGORIES.filter((category) => itemsInCategory(items, category.id).length > 0),
2207
+ [items]
2208
+ );
2209
+ const activeId = categories.some((category) => category.id === categoryId) ? categoryId : ((_a = categories[0]) == null ? void 0 : _a.id) ?? EMOJI_CATEGORIES[0].id;
2210
+ const visible = browsing ? itemsInCategory(items, activeId) : items;
2211
+ useEffect(() => {
2212
+ var _a2;
2213
+ setIndex(0);
2214
+ (_a2 = menuRef.current) == null ? void 0 : _a2.scrollTo(0, 0);
2215
+ }, [query, activeId]);
2216
+ useEffect(() => {
2217
+ const menu2 = menuRef.current;
2218
+ const cell2 = cellRefs.current[index];
2219
+ if (menu2 && cell2) keepCellInView(menu2, cell2);
2220
+ }, [index, activeId, query]);
2221
+ useImperativeHandle(
2222
+ ref,
2223
+ () => ({
2224
+ onKeyDown(event) {
2225
+ if (!visible.length) return false;
2226
+ const next = moveIndex(index, event.key, visible.length, event.shiftKey);
2227
+ if (next !== null) {
2228
+ setIndex(next);
2229
+ return true;
2230
+ }
2231
+ if (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "Tab") {
2232
+ return true;
2233
+ }
2234
+ if (event.key === "Enter") {
2235
+ const item2 = visible[index];
2236
+ if (!item2) return false;
2237
+ command(item2);
2238
+ return true;
2239
+ }
2240
+ return false;
2241
+ }
2242
+ }),
2243
+ [visible, index, command]
2244
+ );
2245
+ if (!visible.length) return null;
2246
+ return /* @__PURE__ */ jsxs("div", { className: styles$b.panel, children: [
2247
+ /* @__PURE__ */ jsx(
2248
+ "div",
2249
+ {
2250
+ id: gridId,
2251
+ ref: menuRef,
2252
+ className: styles$b.menu,
2253
+ role: "listbox",
2254
+ "aria-label": menuLabel,
2255
+ children: visible.map((item2, i) => {
2256
+ const shortcode = `:${item2.name}:`;
2257
+ return /* @__PURE__ */ jsx(
2258
+ "button",
2259
+ {
2260
+ ref: (node) => {
2261
+ cellRefs.current[i] = node;
2262
+ },
2263
+ type: "button",
2264
+ role: "option",
2265
+ "aria-label": shortcode,
2266
+ "aria-selected": i === index,
2267
+ title: shortcode,
2268
+ className: i === index ? styles$b.cellActive : styles$b.cell,
2269
+ tabIndex: -1,
2270
+ onMouseEnter: () => setIndex(i),
2271
+ onMouseDown: (event) => {
2272
+ event.preventDefault();
2273
+ command(item2);
2274
+ },
2275
+ children: item2.emoji
2276
+ },
2277
+ item2.name
2278
+ );
2279
+ })
2280
+ }
2281
+ ),
2282
+ browsing ? /* @__PURE__ */ jsx("div", { className: styles$b.tabs, role: "tablist", "aria-label": menuLabel, children: categories.map((category) => {
2283
+ const selected = category.id === activeId;
2284
+ return /* @__PURE__ */ jsx(
2285
+ "button",
2286
+ {
2287
+ type: "button",
2288
+ role: "tab",
2289
+ "aria-selected": selected,
2290
+ "aria-controls": gridId,
2291
+ "aria-label": category.label,
2292
+ title: category.label,
2293
+ className: selected ? styles$b.tabActive : styles$b.tab,
2294
+ tabIndex: -1,
2295
+ onMouseDown: (event) => {
2296
+ event.preventDefault();
2297
+ setCategoryId(category.id);
2298
+ },
2299
+ children: category.icon
2300
+ },
2301
+ category.id
2302
+ );
2303
+ }) }) : null
2304
+ ] });
2305
+ });
2306
+ function configureEmojiSuggestion(extension, enabled) {
2307
+ if (!enabled) {
2308
+ return extension.configure({
2309
+ suggestion: { allow: () => false }
2310
+ });
2311
+ }
2312
+ return extension.configure({
2313
+ suggestion: {
2314
+ pluginKey: EmojiSuggestionPluginKey,
2315
+ items: ({ editor, query }) => filterEmojiItems(editor.storage.emoji.emojis, query),
2316
+ allow: ({
2317
+ editor,
2318
+ state,
2319
+ range
2320
+ }) => {
2321
+ if (!editor.isEditable) return false;
2322
+ if (editor.isActive("code") || editor.isActive("codeBlock")) return false;
2323
+ const type = state.schema.nodes.emoji;
2324
+ if (!type) return false;
2325
+ return !!state.doc.resolve(range.from).parent.type.contentMatch.matchType(type);
2326
+ },
2327
+ render: () => {
2328
+ let renderer = null;
2329
+ let unmountPopup = null;
2330
+ return {
2331
+ onStart: (props) => {
2332
+ renderer = new ReactRenderer(EmojiSuggestionPopup, {
2333
+ editor: props.editor,
2334
+ props: {
2335
+ items: props.items,
2336
+ query: props.query,
2337
+ menuLabel: getEditorBundle(props.editor).emoji.menuLabel,
2338
+ command: props.command
2339
+ },
2340
+ className: styles$b.popupHost
2341
+ });
2342
+ copyThemeVars(props.editor.view.dom, renderer.element);
2343
+ unmountPopup = props.mount(renderer.element);
2344
+ },
2345
+ onUpdate: (props) => {
2346
+ renderer == null ? void 0 : renderer.updateProps({
2347
+ items: props.items,
2348
+ query: props.query,
2349
+ menuLabel: getEditorBundle(props.editor).emoji.menuLabel,
2350
+ command: props.command
2351
+ });
2352
+ },
2353
+ onKeyDown: (props) => {
2354
+ var _a;
2355
+ if (!renderer || isComposing$1(props.event, props.view)) return false;
2356
+ return ((_a = renderer.ref) == null ? void 0 : _a.onKeyDown(props.event)) ?? false;
2357
+ },
2358
+ onExit: () => {
2359
+ unmountPopup == null ? void 0 : unmountPopup();
2360
+ unmountPopup = null;
2361
+ renderer == null ? void 0 : renderer.destroy();
2362
+ renderer = null;
2363
+ }
2364
+ };
2365
+ }
2366
+ }
2367
+ });
2368
+ }
2369
+ const CJK_IDEOGRAPH = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/g;
2370
+ function countWords(text2) {
2371
+ let cjk = 0;
2372
+ const rest = text2.replace(CJK_IDEOGRAPH, () => {
2373
+ cjk += 1;
2374
+ return " ";
2375
+ });
2376
+ const words = rest.split(/\s+/).filter((word) => /[\p{L}\p{N}]/u.test(word));
2377
+ return cjk + words.length;
2378
+ }
1742
2379
  const popupHost = "_popupHost_1noza_11";
1743
- const menu$1 = "_menu_1noza_1";
1744
- const groupLabel = "_groupLabel_1noza_43";
1745
- const item$1 = "_item_1noza_53";
1746
- const itemActive = "_itemActive_1noza_54";
1747
- const itemIcon = "_itemIcon_1noza_79";
2380
+ const menu$2 = "_menu_1noza_1";
2381
+ const groupLabel$1 = "_groupLabel_1noza_43";
2382
+ const item$2 = "_item_1noza_53";
2383
+ const itemActive$1 = "_itemActive_1noza_54";
2384
+ const itemIcon$1 = "_itemIcon_1noza_79";
1748
2385
  const itemTitle = "_itemTitle_1noza_102";
1749
- const styles$9 = {
2386
+ const styles$a = {
1750
2387
  popupHost,
1751
- menu: menu$1,
1752
- groupLabel,
1753
- item: item$1,
1754
- itemActive,
1755
- itemIcon,
2388
+ menu: menu$2,
2389
+ groupLabel: groupLabel$1,
2390
+ item: item$2,
2391
+ itemActive: itemActive$1,
2392
+ itemIcon: itemIcon$1,
1756
2393
  itemTitle
1757
2394
  };
1758
- function createDefaultSlashMenuItems(labels = defaultSlashMenuLabels) {
2395
+ function createDefaultSlashMenuItems(locale = "en") {
2396
+ const labels = getLocaleBundle(locale).slashMenu;
1759
2397
  return [
1760
2398
  {
1761
2399
  id: "normal-text",
@@ -1823,6 +2461,17 @@ function createDefaultSlashMenuItems(labels = defaultSlashMenuLabels) {
1823
2461
  editor.chain().focus().deleteRange(range).toggleBlockquote().run();
1824
2462
  }
1825
2463
  },
2464
+ {
2465
+ id: "details",
2466
+ title: labels.details,
2467
+ group: labels.groupAdvanced,
2468
+ icon: "▸",
2469
+ aliases: ["zhedie", "折叠", "toggle", "details", "collapse"],
2470
+ order: 8,
2471
+ apply: (editor, range) => {
2472
+ editor.chain().focus().deleteRange(range).setDetails().run();
2473
+ }
2474
+ },
1826
2475
  {
1827
2476
  id: "code-block",
1828
2477
  title: labels.codeBlock,
@@ -1935,11 +2584,11 @@ const SlashMenuPopup = forwardRef(function SlashMenuPopup2({ items, query, menuL
1935
2584
  );
1936
2585
  if (!items.length) return null;
1937
2586
  let optionIndex = -1;
1938
- return /* @__PURE__ */ jsx("div", { className: styles$9.menu, role: "listbox", "aria-label": menuLabel, children: withGroupHeaders(items).map(([header2, item2]) => {
2587
+ return /* @__PURE__ */ jsx("div", { className: styles$a.menu, role: "listbox", "aria-label": menuLabel, children: withGroupHeaders(items).map(([header2, item2]) => {
1939
2588
  optionIndex += 1;
1940
2589
  const i = optionIndex;
1941
2590
  return /* @__PURE__ */ jsxs(Fragment$1, { children: [
1942
- header2 !== null ? /* @__PURE__ */ jsx("div", { className: styles$9.groupLabel, "aria-hidden": "true", children: header2 }) : null,
2591
+ header2 !== null ? /* @__PURE__ */ jsx("div", { className: styles$a.groupLabel, "aria-hidden": "true", children: header2 }) : null,
1943
2592
  /* @__PURE__ */ jsxs(
1944
2593
  "button",
1945
2594
  {
@@ -1947,7 +2596,7 @@ const SlashMenuPopup = forwardRef(function SlashMenuPopup2({ items, query, menuL
1947
2596
  role: "option",
1948
2597
  "aria-selected": i === index,
1949
2598
  title: item2.title,
1950
- className: i === index ? styles$9.itemActive : styles$9.item,
2599
+ className: i === index ? styles$a.itemActive : styles$a.item,
1951
2600
  tabIndex: -1,
1952
2601
  onMouseEnter: () => setIndex(i),
1953
2602
  onMouseDown: (e2) => {
@@ -1955,8 +2604,8 @@ const SlashMenuPopup = forwardRef(function SlashMenuPopup2({ items, query, menuL
1955
2604
  command(item2);
1956
2605
  },
1957
2606
  children: [
1958
- item2.icon != null ? /* @__PURE__ */ jsx("span", { className: styles$9.itemIcon, "aria-hidden": "true", children: item2.icon }) : /* @__PURE__ */ jsx("span", { className: styles$9.itemIcon, "aria-hidden": "true" }),
1959
- /* @__PURE__ */ jsx("span", { className: styles$9.itemTitle, children: item2.title })
2607
+ item2.icon != null ? /* @__PURE__ */ jsx("span", { className: styles$a.itemIcon, "aria-hidden": "true", children: item2.icon }) : /* @__PURE__ */ jsx("span", { className: styles$a.itemIcon, "aria-hidden": "true" }),
2608
+ /* @__PURE__ */ jsx("span", { className: styles$a.itemTitle, children: item2.title })
1960
2609
  ]
1961
2610
  }
1962
2611
  )
@@ -1969,14 +2618,10 @@ function isComposing(event, view) {
1969
2618
  const SlashMenu = Extension.create({
1970
2619
  name: "slashMenu",
1971
2620
  addOptions() {
1972
- return { items: void 0, labels: void 0 };
2621
+ return { items: void 0 };
1973
2622
  },
1974
2623
  addProseMirrorPlugins() {
1975
- const { items: extraItems, labels } = this.options;
1976
- const allItems = [
1977
- ...createDefaultSlashMenuItems({ ...defaultSlashMenuLabels, ...labels }),
1978
- ...extraItems ?? []
1979
- ];
2624
+ const { items: extraItems } = this.options;
1980
2625
  let renderer = null;
1981
2626
  let unmountPopup = null;
1982
2627
  return [
@@ -1989,14 +2634,19 @@ const SlashMenu = Extension.create({
1989
2634
  allowSpaces: false,
1990
2635
  decorationClass: "tmr-slash-suggestion",
1991
2636
  decorationEmptyClass: "tmr-slash-suggestion-empty",
1992
- items: ({ query }) => filterSlashItems(allItems, query),
2637
+ items: ({ editor, query }) => filterSlashItems(
2638
+ [
2639
+ ...createDefaultSlashMenuItems(getEditorLocale(editor)),
2640
+ ...extraItems ?? []
2641
+ ],
2642
+ query
2643
+ ),
1993
2644
  command: ({ editor, range, props: item2 }) => {
1994
2645
  item2.apply(editor, range);
1995
2646
  },
1996
2647
  allow: ({ editor }) => editor.isEditable && !editor.isActive("codeBlock"),
1997
2648
  render: () => ({
1998
2649
  onStart: (props) => {
1999
- var _a;
2000
2650
  renderer = new ReactRenderer(
2001
2651
  SlashMenuPopup,
2002
2652
  {
@@ -2004,10 +2654,10 @@ const SlashMenu = Extension.create({
2004
2654
  props: {
2005
2655
  items: props.items,
2006
2656
  query: props.query,
2007
- menuLabel: ((_a = this.options.labels) == null ? void 0 : _a.menuLabel) ?? defaultSlashMenuLabels.menuLabel,
2657
+ menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,
2008
2658
  command: props.command
2009
2659
  },
2010
- className: styles$9.popupHost
2660
+ className: styles$a.popupHost
2011
2661
  }
2012
2662
  );
2013
2663
  const pmStyle = getComputedStyle(props.editor.view.dom);
@@ -2026,6 +2676,7 @@ const SlashMenu = Extension.create({
2026
2676
  renderer == null ? void 0 : renderer.updateProps({
2027
2677
  items: props.items,
2028
2678
  query: props.query,
2679
+ menuLabel: getEditorBundle(props.editor).slashMenu.menuLabel,
2029
2680
  command: props.command
2030
2681
  });
2031
2682
  },
@@ -2059,7 +2710,7 @@ const rowKeys = "_rowKeys_ljnq6_127";
2059
2710
  const rowMd = "_rowMd_ljnq6_128";
2060
2711
  const keycap = "_keycap_ljnq6_138";
2061
2712
  const none = "_none_ljnq6_163";
2062
- const styles$8 = {
2713
+ const styles$9 = {
2063
2714
  drawer,
2064
2715
  closeBtn,
2065
2716
  head,
@@ -2075,7 +2726,8 @@ const styles$8 = {
2075
2726
  keycap,
2076
2727
  none
2077
2728
  };
2078
- function buildShortcutGroups(labels) {
2729
+ function buildShortcutGroups(locale = "en") {
2730
+ const labels = getLocaleBundle(locale).shortcut;
2079
2731
  return [
2080
2732
  {
2081
2733
  id: "format",
@@ -2087,6 +2739,9 @@ function buildShortcutGroups(labels) {
2087
2739
  { id: "underline", label: labels.underline, keys: ["Mod", "u"] },
2088
2740
  { id: "inline-code", label: labels.inlineCode, keys: ["Mod", "e"], markdown: { marker: "`x`" } },
2089
2741
  { id: "highlight", label: labels.highlight, keys: ["Mod", "Shift", "h"], markdown: { marker: "==x==" } },
2742
+ { id: "smart-quote", label: labels.smartQuote, markdown: { marker: '"' } },
2743
+ { id: "ellipsis", label: labels.ellipsis, markdown: { marker: "..." } },
2744
+ { id: "em-dash", label: labels.emDash, markdown: { marker: "--" } },
2090
2745
  { id: "align-left", label: labels.alignLeft, keys: ["Mod", "Shift", "l"] },
2091
2746
  { id: "align-center", label: labels.alignCenter, keys: ["Mod", "Shift", "e"] },
2092
2747
  { id: "align-right", label: labels.alignRight, keys: ["Mod", "Shift", "r"] },
@@ -2110,7 +2765,8 @@ function buildShortcutGroups(labels) {
2110
2765
  { id: "ordered-list", label: labels.orderedList, keys: ["Mod", "Shift", "7"], markdown: { marker: "1.", terminator: "Space" } },
2111
2766
  { id: "task-list", label: labels.taskList, markdown: { marker: "[]", terminator: "Space" } },
2112
2767
  { id: "code-block", label: labels.codeBlock, markdown: { marker: "```", terminator: "Space" } },
2113
- { id: "divider", label: labels.divider, markdown: { marker: "---", terminator: "Space" } }
2768
+ { id: "divider", label: labels.divider, markdown: { marker: "---", terminator: "Space" } },
2769
+ { id: "emoji", label: labels.emoji, markdown: { marker: ":smile:" } }
2114
2770
  ]
2115
2771
  },
2116
2772
  {
@@ -2135,11 +2791,12 @@ function keycapText(key, mac) {
2135
2791
  if (/^[a-z]$/.test(key)) return key.toUpperCase();
2136
2792
  return key;
2137
2793
  }
2138
- function ShortcutPanel({ open, onClose, labels }) {
2139
- const t = useMemo(
2140
- () => ({ ...defaultShortcutLabels, ...labels }),
2141
- [labels]
2142
- );
2794
+ function ShortcutPanel({
2795
+ open,
2796
+ onClose,
2797
+ locale = "en"
2798
+ }) {
2799
+ const t = getLocaleBundle(locale).shortcut;
2143
2800
  useEffect(() => {
2144
2801
  if (!open) return;
2145
2802
  const onKey = (e2) => {
@@ -2149,15 +2806,15 @@ function ShortcutPanel({ open, onClose, labels }) {
2149
2806
  return () => document.removeEventListener("keydown", onKey);
2150
2807
  }, [open, onClose]);
2151
2808
  const groups = useMemo(
2152
- () => open ? buildShortcutGroups(t) : [],
2153
- [open, t]
2809
+ () => open ? buildShortcutGroups(locale) : [],
2810
+ [open, locale]
2154
2811
  );
2155
2812
  const mac = useMemo(isMacPlatform, []);
2156
2813
  if (!open) return null;
2157
2814
  return /* @__PURE__ */ jsxs(
2158
2815
  "aside",
2159
2816
  {
2160
- className: styles$8.drawer,
2817
+ className: styles$9.drawer,
2161
2818
  role: "dialog",
2162
2819
  "aria-modal": "false",
2163
2820
  "aria-label": t.panelTitle,
@@ -2166,27 +2823,27 @@ function ShortcutPanel({ open, onClose, labels }) {
2166
2823
  "button",
2167
2824
  {
2168
2825
  type: "button",
2169
- className: styles$8.closeBtn,
2826
+ className: styles$9.closeBtn,
2170
2827
  title: t.close,
2171
2828
  "aria-label": t.close,
2172
2829
  onClick: onClose,
2173
2830
  children: /* @__PURE__ */ jsx(XIcon, { size: 16 })
2174
2831
  }
2175
2832
  ),
2176
- /* @__PURE__ */ jsxs("div", { className: styles$8.head, children: [
2177
- /* @__PURE__ */ jsx("span", { className: styles$8.colFormat, children: t.colFormat }),
2178
- /* @__PURE__ */ jsx("span", { className: styles$8.colShortcut, children: t.colShortcut }),
2179
- /* @__PURE__ */ jsx("span", { className: styles$8.colMarkdown, children: t.colMarkdown })
2833
+ /* @__PURE__ */ jsxs("div", { className: styles$9.head, children: [
2834
+ /* @__PURE__ */ jsx("span", { className: styles$9.colFormat, children: t.colFormat }),
2835
+ /* @__PURE__ */ jsx("span", { className: styles$9.colShortcut, children: t.colShortcut }),
2836
+ /* @__PURE__ */ jsx("span", { className: styles$9.colMarkdown, children: t.colMarkdown })
2180
2837
  ] }),
2181
- /* @__PURE__ */ jsx("div", { className: styles$8.body, children: groups.map((group2) => /* @__PURE__ */ jsxs("section", { className: styles$8.group, children: [
2182
- /* @__PURE__ */ jsx("h3", { className: styles$8.groupTitle, children: group2.title }),
2183
- group2.entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: styles$8.row, children: [
2184
- /* @__PURE__ */ jsx("span", { className: styles$8.rowLabel, children: entry.label }),
2185
- /* @__PURE__ */ jsx("span", { className: styles$8.rowKeys, children: entry.keys ? entry.keys.map((key, i) => /* @__PURE__ */ jsx("kbd", { className: styles$8.keycap, children: keycapText(key, mac) }, i)) : /* @__PURE__ */ jsx("span", { className: styles$8.none, children: "—" }) }),
2186
- /* @__PURE__ */ jsx("span", { className: styles$8.rowMd, children: entry.markdown ? /* @__PURE__ */ jsxs(Fragment$2, { children: [
2187
- /* @__PURE__ */ jsx("kbd", { className: styles$8.keycap, children: entry.markdown.marker }),
2188
- entry.markdown.terminator ? /* @__PURE__ */ jsx("kbd", { className: styles$8.keycap, children: entry.markdown.terminator }) : null
2189
- ] }) : /* @__PURE__ */ jsx("span", { className: styles$8.none, children: "—" }) })
2838
+ /* @__PURE__ */ jsx("div", { className: styles$9.body, children: groups.map((group2) => /* @__PURE__ */ jsxs("section", { className: styles$9.group, children: [
2839
+ /* @__PURE__ */ jsx("h3", { className: styles$9.groupTitle, children: group2.title }),
2840
+ group2.entries.map((entry) => /* @__PURE__ */ jsxs("div", { className: styles$9.row, children: [
2841
+ /* @__PURE__ */ jsx("span", { className: styles$9.rowLabel, children: entry.label }),
2842
+ /* @__PURE__ */ jsx("span", { className: styles$9.rowKeys, children: entry.keys ? entry.keys.map((key, i) => /* @__PURE__ */ jsx("kbd", { className: styles$9.keycap, children: keycapText(key, mac) }, i)) : /* @__PURE__ */ jsx("span", { className: styles$9.none, children: "—" }) }),
2843
+ /* @__PURE__ */ jsx("span", { className: styles$9.rowMd, children: entry.markdown ? /* @__PURE__ */ jsxs(Fragment$2, { children: [
2844
+ /* @__PURE__ */ jsx("kbd", { className: styles$9.keycap, children: entry.markdown.marker }),
2845
+ entry.markdown.terminator ? /* @__PURE__ */ jsx("kbd", { className: styles$9.keycap, children: entry.markdown.terminator }) : null
2846
+ ] }) : /* @__PURE__ */ jsx("span", { className: styles$9.none, children: "—" }) })
2190
2847
  ] }, entry.id))
2191
2848
  ] }, group2.id)) })
2192
2849
  ]
@@ -2270,8 +2927,8 @@ function setPendingAnchors(editor, anchors) {
2270
2927
  }
2271
2928
  function buildDecorations(doc, anchors) {
2272
2929
  const bounds = [];
2273
- doc.forEach((child, offset) => {
2274
- bounds.push({ from: offset, to: offset + child.nodeSize });
2930
+ doc.forEach((child, offset2) => {
2931
+ bounds.push({ from: offset2, to: offset2 + child.nodeSize });
2275
2932
  });
2276
2933
  const decorations = [];
2277
2934
  for (const anchor of anchors) {
@@ -2363,6 +3020,7 @@ function ChartView({
2363
3020
  const canvasRef = useRef(null);
2364
3021
  const chartRef = useRef(null);
2365
3022
  const [active2, setActive2] = useState(0);
3023
+ const labels = useEditorBundle(editor).chart;
2366
3024
  const payload = useMemo(
2367
3025
  () => ({
2368
3026
  config: node.attrs.config ?? [],
@@ -2407,10 +3065,6 @@ function ChartView({
2407
3065
  chart: null
2408
3066
  });
2409
3067
  }, [safeIndex]);
2410
- const labels = {
2411
- ...defaultChartLabels,
2412
- ...extension.options.chartLabels
2413
- };
2414
3068
  const selectChart = () => {
2415
3069
  if (!editor || !canSelect) return;
2416
3070
  const pos = getPos();
@@ -2456,14 +3110,13 @@ function ChartView({
2456
3110
  );
2457
3111
  }
2458
3112
  function createChart(options = {}) {
2459
- const { editable = false, chartTheme = null, chartLabels } = options;
3113
+ const { editable = false, chartTheme = null } = options;
2460
3114
  return reportChart.extend({
2461
3115
  addOptions() {
2462
3116
  return {
2463
3117
  HTMLAttributes: {},
2464
3118
  chartEditable: editable,
2465
- chartTheme,
2466
- chartLabels
3119
+ chartTheme
2467
3120
  };
2468
3121
  },
2469
3122
  addNodeView() {
@@ -2490,7 +3143,7 @@ const gutterLine = "_gutterLine_k8th1_184";
2490
3143
  const readonlyActions = "_readonlyActions_k8th1_188";
2491
3144
  const actionBtn = "_actionBtn_k8th1_194";
2492
3145
  const langBraces = "_langBraces_k8th1_213";
2493
- const styles$7 = {
3146
+ const styles$8 = {
2494
3147
  wrapper,
2495
3148
  header: header$1,
2496
3149
  langTrigger,
@@ -2535,14 +3188,9 @@ function CodeBlockView({
2535
3188
  node,
2536
3189
  updateAttributes,
2537
3190
  deleteNode,
2538
- extension,
2539
3191
  editor
2540
3192
  }) {
2541
- var _a;
2542
- const labels = {
2543
- ...defaultCodeBlockLabels,
2544
- ...((_a = extension == null ? void 0 : extension.options) == null ? void 0 : _a.codeBlockLabels) ?? {}
2545
- };
3193
+ const labels = useEditorBundle(editor).codeBlock;
2546
3194
  const language = node.attrs.language || "";
2547
3195
  const current = LANGUAGES.find((l) => l.value === language);
2548
3196
  const currentLabel = current ? current.value === "" ? labels.autoDetect : current.label : labels.autoDetect;
@@ -2567,13 +3215,13 @@ function CodeBlockView({
2567
3215
  return () => mo.disconnect();
2568
3216
  }, [readonly2]);
2569
3217
  const handleCopy = () => {
2570
- var _a2;
3218
+ var _a;
2571
3219
  const text2 = node.textContent ?? "";
2572
3220
  const done = () => {
2573
3221
  setCopied(true);
2574
3222
  window.setTimeout(() => setCopied(false), 1500);
2575
3223
  };
2576
- if ((_a2 = navigator.clipboard) == null ? void 0 : _a2.writeText) {
3224
+ if ((_a = navigator.clipboard) == null ? void 0 : _a.writeText) {
2577
3225
  navigator.clipboard.writeText(text2).then(done, () => {
2578
3226
  });
2579
3227
  return;
@@ -2590,117 +3238,890 @@ function CodeBlockView({
2590
3238
  ta.remove();
2591
3239
  };
2592
3240
  const wrapperClass = [
2593
- styles$7.wrapper,
2594
- readonly2 ? styles$7.readonly : "",
2595
- folded2 ? styles$7.folded : ""
3241
+ styles$8.wrapper,
3242
+ readonly2 ? styles$8.readonly : "",
3243
+ folded2 ? styles$8.folded : ""
2596
3244
  ].filter(Boolean).join(" ");
2597
3245
  return /* @__PURE__ */ jsxs(NodeViewWrapper, { className: wrapperClass, children: [
2598
- editable ? /* @__PURE__ */ jsxs("div", { className: styles$7.header, contentEditable: false, children: [
3246
+ editable ? /* @__PURE__ */ jsxs("div", { className: styles$8.header, contentEditable: false, children: [
2599
3247
  /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
2600
3248
  /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
2601
3249
  "button",
2602
3250
  {
2603
3251
  type: "button",
2604
- className: styles$7.langTrigger,
2605
- "aria-label": "Code language",
3252
+ className: styles$8.langTrigger,
3253
+ "aria-label": labels.language,
2606
3254
  children: [
2607
3255
  /* @__PURE__ */ jsx("span", { children: currentLabel }),
2608
- /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14, className: styles$7.langChevron })
3256
+ /* @__PURE__ */ jsx(ChevronDownIcon, { size: 14, className: styles$8.langChevron })
2609
3257
  ]
2610
3258
  }
2611
3259
  ) }),
2612
3260
  /* @__PURE__ */ jsx(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx(
2613
3261
  DropdownMenu.Content,
2614
3262
  {
2615
- className: styles$7.langMenu,
2616
- sideOffset: 4,
2617
- align: "end",
2618
- children: LANGUAGES.map((l) => {
2619
- const optionLabel = l.value === "" ? labels.autoDetect : l.label;
2620
- const selected = l.value === language;
2621
- return /* @__PURE__ */ jsxs(
2622
- DropdownMenu.Item,
2623
- {
2624
- className: `${styles$7.langItem}${selected ? ` ${styles$7.langItemSelected}` : ""}`,
2625
- onSelect: () => updateAttributes({ language: l.value }),
2626
- children: [
2627
- /* @__PURE__ */ jsx("span", { className: styles$7.langItemCheck, children: selected ? /* @__PURE__ */ jsx(CheckIcon, { size: 14 }) : null }),
2628
- optionLabel
2629
- ]
3263
+ className: styles$8.langMenu,
3264
+ sideOffset: 4,
3265
+ align: "end",
3266
+ children: LANGUAGES.map((l) => {
3267
+ const optionLabel = l.value === "" ? labels.autoDetect : l.label;
3268
+ const selected = l.value === language;
3269
+ return /* @__PURE__ */ jsxs(
3270
+ DropdownMenu.Item,
3271
+ {
3272
+ className: `${styles$8.langItem}${selected ? ` ${styles$8.langItemSelected}` : ""}`,
3273
+ onSelect: () => updateAttributes({ language: l.value }),
3274
+ children: [
3275
+ /* @__PURE__ */ jsx("span", { className: styles$8.langItemCheck, children: selected ? /* @__PURE__ */ jsx(CheckIcon, { size: 14 }) : null }),
3276
+ optionLabel
3277
+ ]
3278
+ },
3279
+ l.value
3280
+ );
3281
+ })
3282
+ }
3283
+ ) })
3284
+ ] }),
3285
+ /* @__PURE__ */ jsx(
3286
+ "button",
3287
+ {
3288
+ type: "button",
3289
+ className: styles$8.deleteBtn,
3290
+ title: labels.delete,
3291
+ "aria-label": labels.delete,
3292
+ onClick: () => deleteNode(),
3293
+ children: /* @__PURE__ */ jsx(TrashIcon, {})
3294
+ }
3295
+ )
3296
+ ] }) : (
3297
+ // 只读头部:左侧语言标签,右侧复制 + 展开收起(展开后按钮保留,供收回)
3298
+ /* @__PURE__ */ jsxs("div", { className: styles$8.readonlyHeader, contentEditable: false, children: [
3299
+ /* @__PURE__ */ jsxs("span", { className: styles$8.langBadge, children: [
3300
+ /* @__PURE__ */ jsx("span", { className: styles$8.langBraces, children: "{}" }),
3301
+ currentLabel
3302
+ ] }),
3303
+ /* @__PURE__ */ jsxs("span", { className: styles$8.readonlyActions, children: [
3304
+ /* @__PURE__ */ jsx(
3305
+ "button",
3306
+ {
3307
+ type: "button",
3308
+ className: styles$8.actionBtn,
3309
+ onClick: handleCopy,
3310
+ "aria-label": labels.copy,
3311
+ title: labels.copy,
3312
+ children: copied ? /* @__PURE__ */ jsx(CheckIcon, { size: 14 }) : /* @__PURE__ */ jsx(CopyIcon, { size: 14 })
3313
+ }
3314
+ ),
3315
+ /* @__PURE__ */ jsx(
3316
+ "button",
3317
+ {
3318
+ type: "button",
3319
+ className: styles$8.actionBtn,
3320
+ onClick: () => setFolded((v) => !v),
3321
+ "aria-label": folded2 ? labels.expand : labels.collapse,
3322
+ title: folded2 ? labels.expand : labels.collapse,
3323
+ "aria-expanded": !folded2,
3324
+ children: /* @__PURE__ */ jsx(ChevronsUpDownIcon, { size: 14 })
3325
+ }
3326
+ )
3327
+ ] })
3328
+ ] })
3329
+ ),
3330
+ /* @__PURE__ */ jsxs("pre", { ref: preRef, className: styles$8.pre, children: [
3331
+ readonly2 ? /* @__PURE__ */ jsx(
3332
+ "span",
3333
+ {
3334
+ className: styles$8.gutter,
3335
+ contentEditable: false,
3336
+ "aria-hidden": "true",
3337
+ children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx("span", { className: styles$8.gutterLine, children: i + 1 }, i))
3338
+ }
3339
+ ) : null,
3340
+ /* @__PURE__ */ jsx(NodeViewContent, { as: "code" })
3341
+ ] })
3342
+ ] });
3343
+ }
3344
+ function evaluate(value, param) {
3345
+ return typeof value === "function" ? value(param) : value;
3346
+ }
3347
+ function getSide(placement) {
3348
+ return placement.split("-")[0];
3349
+ }
3350
+ function getAlignment(placement) {
3351
+ return placement.split("-")[1];
3352
+ }
3353
+ function getSideAxis(placement) {
3354
+ const firstChar = placement[0];
3355
+ return firstChar === "t" || firstChar === "b" ? "y" : "x";
3356
+ }
3357
+ const originSides = /* @__PURE__ */ new Set(["left", "top"]);
3358
+ async function convertValueToCoords(state, options) {
3359
+ const {
3360
+ placement,
3361
+ platform,
3362
+ elements
3363
+ } = state;
3364
+ const rtl = await (platform.isRTL == null ? void 0 : platform.isRTL(elements.floating));
3365
+ const side = getSide(placement);
3366
+ const alignment = getAlignment(placement);
3367
+ const isVertical = getSideAxis(placement) === "y";
3368
+ const mainAxisMulti = originSides.has(side) ? -1 : 1;
3369
+ const crossAxisMulti = rtl && isVertical ? -1 : 1;
3370
+ const rawValue = evaluate(options, state);
3371
+ let {
3372
+ mainAxis,
3373
+ crossAxis,
3374
+ alignmentAxis
3375
+ } = typeof rawValue === "number" ? {
3376
+ mainAxis: rawValue,
3377
+ crossAxis: 0,
3378
+ alignmentAxis: null
3379
+ } : {
3380
+ mainAxis: rawValue.mainAxis || 0,
3381
+ crossAxis: rawValue.crossAxis || 0,
3382
+ alignmentAxis: rawValue.alignmentAxis
3383
+ };
3384
+ if (alignment && typeof alignmentAxis === "number") {
3385
+ crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis;
3386
+ }
3387
+ return isVertical ? {
3388
+ x: crossAxis * crossAxisMulti,
3389
+ y: mainAxis * mainAxisMulti
3390
+ } : {
3391
+ x: mainAxis * mainAxisMulti,
3392
+ y: crossAxis * crossAxisMulti
3393
+ };
3394
+ }
3395
+ const offset$1 = function(options) {
3396
+ if (options === void 0) {
3397
+ options = 0;
3398
+ }
3399
+ return {
3400
+ name: "offset",
3401
+ options,
3402
+ async fn(state) {
3403
+ var _middlewareData$offse, _middlewareData$arrow;
3404
+ const {
3405
+ x,
3406
+ y,
3407
+ placement,
3408
+ middlewareData
3409
+ } = state;
3410
+ const diffCoords = await convertValueToCoords(state, options);
3411
+ if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
3412
+ return {};
3413
+ }
3414
+ return {
3415
+ x: x + diffCoords.x,
3416
+ y: y + diffCoords.y,
3417
+ data: {
3418
+ ...diffCoords,
3419
+ placement
3420
+ }
3421
+ };
3422
+ }
3423
+ };
3424
+ };
3425
+ const offset = offset$1;
3426
+ const TURN_INTO_KINDS = [
3427
+ "paragraph",
3428
+ "heading-1",
3429
+ "heading-2",
3430
+ "heading-3",
3431
+ "bulletList",
3432
+ "orderedList",
3433
+ "taskList",
3434
+ "blockquote",
3435
+ "codeBlock"
3436
+ ];
3437
+ const TURNABLE = /* @__PURE__ */ new Set([
3438
+ "paragraph",
3439
+ "heading",
3440
+ "bulletList",
3441
+ "orderedList",
3442
+ "taskList",
3443
+ "listItem",
3444
+ "taskItem",
3445
+ "blockquote",
3446
+ "codeBlock"
3447
+ ]);
3448
+ const LIFT_BEFORE_TURN = /* @__PURE__ */ new Set([
3449
+ "listItem",
3450
+ "taskItem",
3451
+ "bulletList",
3452
+ "orderedList",
3453
+ "taskList",
3454
+ "blockquote",
3455
+ "codeBlock"
3456
+ ]);
3457
+ function canTurnBlockInto(node) {
3458
+ return !!node && TURNABLE.has(node.type.name);
3459
+ }
3460
+ function currentTurnIntoKind(editor, pos, node) {
3461
+ const name = node.type.name;
3462
+ if (name === "paragraph") return "paragraph";
3463
+ if (name === "heading") {
3464
+ const level = Number(node.attrs.level);
3465
+ if (level === 1 || level === 2 || level === 3) {
3466
+ return `heading-${level}`;
3467
+ }
3468
+ return null;
3469
+ }
3470
+ if (name === "bulletList") return "bulletList";
3471
+ if (name === "orderedList") return "orderedList";
3472
+ if (name === "taskList") return "taskList";
3473
+ if (name === "blockquote") return "blockquote";
3474
+ if (name === "codeBlock") return "codeBlock";
3475
+ if (name === "listItem" || name === "taskItem") {
3476
+ const parent = editor.state.doc.resolve(pos).parent.type.name;
3477
+ if (parent === "bulletList") return "bulletList";
3478
+ if (parent === "orderedList") return "orderedList";
3479
+ if (parent === "taskList") return "taskList";
3480
+ }
3481
+ return null;
3482
+ }
3483
+ function firstTextPos(node, pos) {
3484
+ if (node.isTextblock) return pos + 1;
3485
+ let found = null;
3486
+ node.descendants((child, offset2) => {
3487
+ if (found != null) return false;
3488
+ if (child.isTextblock) {
3489
+ found = pos + 1 + offset2 + 1;
3490
+ return false;
3491
+ }
3492
+ return true;
3493
+ });
3494
+ return found;
3495
+ }
3496
+ function turnBlockInto(editor, pos, kind) {
3497
+ const node = editor.state.doc.nodeAt(pos);
3498
+ if (!node || !canTurnBlockInto(node)) return false;
3499
+ const textPos = firstTextPos(node, pos);
3500
+ if (textPos == null) return false;
3501
+ const chain = editor.chain().setTextSelection(textPos);
3502
+ if (LIFT_BEFORE_TURN.has(node.type.name)) chain.clearNodes();
3503
+ if (kind === "heading-1" || kind === "heading-2" || kind === "heading-3") {
3504
+ const level = Number(kind.slice(-1));
3505
+ chain.setHeading({ level });
3506
+ } else if (kind === "paragraph") {
3507
+ if (!LIFT_BEFORE_TURN.has(node.type.name)) chain.setParagraph();
3508
+ } else if (kind === "bulletList") {
3509
+ chain.toggleBulletList();
3510
+ } else if (kind === "orderedList") {
3511
+ chain.toggleOrderedList();
3512
+ } else if (kind === "taskList") {
3513
+ chain.toggleTaskList();
3514
+ } else if (kind === "blockquote") {
3515
+ chain.toggleBlockquote();
3516
+ } else if (kind === "codeBlock") {
3517
+ chain.setCodeBlock();
3518
+ }
3519
+ return chain.run();
3520
+ }
3521
+ function duplicateBlock(editor, pos) {
3522
+ const node = editor.state.doc.nodeAt(pos);
3523
+ if (!node) return false;
3524
+ return editor.chain().insertContentAt(pos + node.nodeSize, node.toJSON()).run();
3525
+ }
3526
+ function deleteBlock(editor, pos) {
3527
+ const node = editor.state.doc.nodeAt(pos);
3528
+ if (!node) return false;
3529
+ let from = pos;
3530
+ let to = pos + node.nodeSize;
3531
+ const $pos = editor.state.doc.resolve(pos);
3532
+ const parent = $pos.parent;
3533
+ if ((node.type.name === "listItem" || node.type.name === "taskItem") && parent.childCount === 1) {
3534
+ from = pos - $pos.parentOffset - 1;
3535
+ const list2 = editor.state.doc.nodeAt(from);
3536
+ if (list2) to = from + list2.nodeSize;
3537
+ }
3538
+ if (from <= 0 && to >= editor.state.doc.content.size) {
3539
+ return editor.chain().insertContentAt(
3540
+ { from: 0, to: editor.state.doc.content.size },
3541
+ { type: "paragraph" }
3542
+ ).run();
3543
+ }
3544
+ return editor.chain().deleteRange({ from, to }).run();
3545
+ }
3546
+ function blockMarkdown(editor, pos) {
3547
+ var _a;
3548
+ const node = editor.state.doc.nodeAt(pos);
3549
+ if (!node) return null;
3550
+ const serialized = (_a = editor.markdown) == null ? void 0 : _a.serialize(node.toJSON());
3551
+ if (typeof serialized === "string") return serialized;
3552
+ return node.textContent;
3553
+ }
3554
+ function canResetBlockFormatting(node) {
3555
+ if (!node || node.isAtom) return false;
3556
+ if (node.type.name === "image" || node.type.name === "horizontalRule") {
3557
+ return false;
3558
+ }
3559
+ if (node.isTextblock) return true;
3560
+ let found = false;
3561
+ node.descendants((child) => {
3562
+ if (child.isTextblock) {
3563
+ found = true;
3564
+ return false;
3565
+ }
3566
+ return true;
3567
+ });
3568
+ return found;
3569
+ }
3570
+ function withoutBlockStyles(node) {
3571
+ const next = { ...node.attrs };
3572
+ if ("textAlign" in next) next.textAlign = null;
3573
+ if ("indent" in next) next.indent = null;
3574
+ if ("lineHeight" in next) next.lineHeight = null;
3575
+ return next;
3576
+ }
3577
+ function resetBlockFormatting(editor, pos) {
3578
+ const node = editor.state.doc.nodeAt(pos);
3579
+ if (!node || !canResetBlockFormatting(node)) return false;
3580
+ const from = pos + 1;
3581
+ const to = pos + node.nodeSize - 1;
3582
+ let tr = editor.state.tr;
3583
+ if (to > from) {
3584
+ for (const mark of Object.values(editor.schema.marks)) {
3585
+ tr = tr.removeMark(from, to, mark);
3586
+ }
3587
+ }
3588
+ const markup = [];
3589
+ if (node.isTextblock) {
3590
+ markup.push({ pos, attrs: withoutBlockStyles(node) });
3591
+ }
3592
+ node.descendants((child, offset2) => {
3593
+ if (!child.isTextblock) return;
3594
+ markup.push({
3595
+ pos: pos + 1 + offset2,
3596
+ attrs: withoutBlockStyles(child)
3597
+ });
3598
+ });
3599
+ for (const item2 of markup) {
3600
+ tr = tr.setNodeMarkup(item2.pos, void 0, item2.attrs);
3601
+ }
3602
+ if (!tr.docChanged) return false;
3603
+ editor.view.dispatch(tr);
3604
+ return true;
3605
+ }
3606
+ function isImageBlock(node) {
3607
+ return (node == null ? void 0 : node.type.name) === "image" && typeof node.attrs.src === "string" && node.attrs.src.length > 0;
3608
+ }
3609
+ function downloadImageBlock(node) {
3610
+ var _a;
3611
+ if (!isImageBlock(node)) return;
3612
+ const src = node.attrs.src;
3613
+ const alt = typeof node.attrs.alt === "string" ? node.attrs.alt.trim() : "";
3614
+ const fromUrl = ((_a = src.split("/").pop()) == null ? void 0 : _a.split("?")[0]) ?? "";
3615
+ const a3 = document.createElement("a");
3616
+ a3.href = src;
3617
+ a3.download = alt || fromUrl || "image";
3618
+ a3.rel = "noopener";
3619
+ a3.target = "_blank";
3620
+ document.body.appendChild(a3);
3621
+ a3.click();
3622
+ a3.remove();
3623
+ }
3624
+ const LIST_NAMES = /* @__PURE__ */ new Set(["bulletList", "orderedList", "taskList"]);
3625
+ const draggingViews = /* @__PURE__ */ new WeakSet();
3626
+ const blockDragListGuardKey = new PluginKey("blockDragListGuard");
3627
+ function setBlockHandleDragging(view, dragging) {
3628
+ if (dragging) draggingViews.add(view);
3629
+ else draggingViews.delete(view);
3630
+ }
3631
+ function isBlockHandleDragging(view) {
3632
+ return draggingViews.has(view);
3633
+ }
3634
+ function outermostListDepth(doc, pos) {
3635
+ const safe = Math.max(0, Math.min(pos, doc.content.size));
3636
+ const $pos = doc.resolve(safe);
3637
+ let listDepth = -1;
3638
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
3639
+ if (LIST_NAMES.has($pos.node(depth).type.name)) listDepth = depth;
3640
+ }
3641
+ return listDepth;
3642
+ }
3643
+ function isPosInsideList(doc, pos) {
3644
+ return outermostListDepth(doc, pos) > 0;
3645
+ }
3646
+ function listEscapeEdge(view, pos, clientY) {
3647
+ const listDepth = outermostListDepth(view.state.doc, pos);
3648
+ if (listDepth < 0) return null;
3649
+ const $pos = view.state.doc.resolve(
3650
+ Math.max(0, Math.min(pos, view.state.doc.content.size))
3651
+ );
3652
+ const start = $pos.before(listDepth);
3653
+ const node = $pos.node(listDepth);
3654
+ const dom = view.nodeDOM(start);
3655
+ let after = pos >= start + node.nodeSize / 2;
3656
+ if (dom instanceof HTMLElement) {
3657
+ const rect = dom.getBoundingClientRect();
3658
+ if (rect.height > 0) after = clientY >= rect.top + rect.height / 2;
3659
+ }
3660
+ return after ? start + node.nodeSize : start;
3661
+ }
3662
+ function moveDraggedSlice(view, slice, insertPos) {
3663
+ const { state } = view;
3664
+ const { from, to } = state.selection;
3665
+ if (insertPos > from && insertPos < to) return true;
3666
+ const tr = state.tr;
3667
+ tr.deleteSelection();
3668
+ const pos = tr.mapping.map(insertPos, 1);
3669
+ const block = slice.openStart === 0 && slice.openEnd === 0 && slice.content.childCount === 1 ? slice.content.firstChild : null;
3670
+ if (block) tr.replaceRangeWith(pos, pos, block);
3671
+ else tr.replaceRange(pos, pos, slice);
3672
+ if (tr.doc.eq(state.doc)) return true;
3673
+ const $pos = tr.doc.resolve(Math.max(0, Math.min(pos, tr.doc.content.size)));
3674
+ const near = TextSelection.findFrom($pos, 1, true) ?? TextSelection.findFrom($pos, -1, true);
3675
+ if (near) tr.setSelection(near);
3676
+ try {
3677
+ view.focus();
3678
+ } catch {
3679
+ }
3680
+ view.dispatch(tr.setMeta("uiEvent", "drop").scrollIntoView());
3681
+ return true;
3682
+ }
3683
+ function snappedListEdge(view, clientX, clientY, slice) {
3684
+ const coords = view.posAtCoords({ left: clientX, top: clientY });
3685
+ if (!coords) return null;
3686
+ const natural = dropPoint(view.state.doc, coords.pos, slice);
3687
+ const probe = natural ?? coords.pos;
3688
+ if (!isPosInsideList(view.state.doc, probe)) return null;
3689
+ return listEscapeEdge(view, probe, clientY);
3690
+ }
3691
+ function placeBlockDropCursor(view, pos) {
3692
+ const parent = view.dom.offsetParent;
3693
+ if (!(parent instanceof HTMLElement)) return;
3694
+ const cursor = parent.querySelector(
3695
+ ".prosemirror-dropcursor-block, .prosemirror-dropcursor-inline"
3696
+ );
3697
+ if (!(cursor instanceof HTMLElement)) return;
3698
+ const $pos = view.state.doc.resolve(pos);
3699
+ const before = $pos.nodeBefore;
3700
+ const anchorPos = before ? pos - before.nodeSize : pos;
3701
+ const anchor = view.nodeDOM(anchorPos);
3702
+ if (!(anchor instanceof HTMLElement)) return;
3703
+ const anchorRect = anchor.getBoundingClientRect();
3704
+ let lineTop = before ? anchorRect.bottom : anchorRect.top;
3705
+ if (before && $pos.nodeAfter) {
3706
+ const next = view.nodeDOM(pos);
3707
+ if (next instanceof HTMLElement) {
3708
+ lineTop = (anchorRect.bottom + next.getBoundingClientRect().top) / 2;
3709
+ }
3710
+ }
3711
+ const editorRect = view.dom.getBoundingClientRect();
3712
+ const scaleX = editorRect.width / view.dom.offsetWidth || 1;
3713
+ const scaleY = editorRect.height / view.dom.offsetHeight || 1;
3714
+ const parentRect = parent.getBoundingClientRect();
3715
+ const parentScaleX = parentRect.width / parent.offsetWidth || 1;
3716
+ const parentScaleY = parentRect.height / parent.offsetHeight || 1;
3717
+ const parentLeft = parentRect.left - parent.scrollLeft * parentScaleX;
3718
+ const parentTop = parentRect.top - parent.scrollTop * parentScaleY;
3719
+ const half = scaleY;
3720
+ cursor.classList.add("prosemirror-dropcursor-block");
3721
+ cursor.classList.remove("prosemirror-dropcursor-inline");
3722
+ cursor.style.left = `${(anchorRect.left - parentLeft) / scaleX}px`;
3723
+ cursor.style.top = `${(lineTop - half - parentTop) / scaleY}px`;
3724
+ cursor.style.width = `${(anchorRect.right - anchorRect.left) / scaleX}px`;
3725
+ cursor.style.height = `${2 * half / scaleY}px`;
3726
+ }
3727
+ function blockDragListGuardPlugin() {
3728
+ return new Plugin({
3729
+ key: blockDragListGuardKey,
3730
+ props: {
3731
+ handleDrop(view, event, slice, moved) {
3732
+ if (!moved || !isBlockHandleDragging(view)) return false;
3733
+ const edge = snappedListEdge(view, event.clientX, event.clientY, slice);
3734
+ if (edge == null) return false;
3735
+ moveDraggedSlice(view, slice, edge);
3736
+ return true;
3737
+ }
3738
+ },
3739
+ view(editorView) {
3740
+ const onDragOver = (event) => {
3741
+ var _a;
3742
+ const slice = (_a = editorView.dragging) == null ? void 0 : _a.slice;
3743
+ if (!slice || !isBlockHandleDragging(editorView)) return;
3744
+ const edge = snappedListEdge(
3745
+ editorView,
3746
+ event.clientX,
3747
+ event.clientY,
3748
+ slice
3749
+ );
3750
+ if (edge == null) return;
3751
+ placeBlockDropCursor(editorView, edge);
3752
+ };
3753
+ editorView.dom.addEventListener("dragover", onDragOver);
3754
+ return {
3755
+ destroy() {
3756
+ editorView.dom.removeEventListener("dragover", onDragOver);
3757
+ setBlockHandleDragging(editorView, false);
3758
+ }
3759
+ };
3760
+ }
3761
+ });
3762
+ }
3763
+ const cluster = "_cluster_5edcs_4";
3764
+ const clusterOnTable = "_clusterOnTable_5edcs_24";
3765
+ const button = "_button_5edcs_28";
3766
+ const grip = "_grip_5edcs_50";
3767
+ const menu$1 = "_menu_5edcs_61";
3768
+ const groupLabel = "_groupLabel_5edcs_84";
3769
+ const divider$3 = "_divider_5edcs_94";
3770
+ const item$1 = "_item_5edcs_100";
3771
+ const itemActive = "_itemActive_5edcs_118";
3772
+ const itemDanger = "_itemDanger_5edcs_123";
3773
+ const itemIcon = "_itemIcon_5edcs_132";
3774
+ const styles$7 = {
3775
+ cluster,
3776
+ clusterOnTable,
3777
+ button,
3778
+ grip,
3779
+ menu: menu$1,
3780
+ groupLabel,
3781
+ divider: divider$3,
3782
+ item: item$1,
3783
+ itemActive,
3784
+ itemDanger,
3785
+ itemIcon
3786
+ };
3787
+ const HANDLE_POSITION = {
3788
+ placement: "left-start",
3789
+ strategy: "absolute",
3790
+ middleware: [offset({ mainAxis: -6, crossAxis: 4 })]
3791
+ };
3792
+ const TABLE_ANCHOR_LIFT = 24;
3793
+ const THEME_VARS = [
3794
+ "--tmr-text",
3795
+ "--tmr-accent",
3796
+ "--tmr-toolbar-border",
3797
+ "--tmr-toolbar-muted",
3798
+ "--tmr-popover-bg",
3799
+ "--tmr-danger",
3800
+ "--tmr-code-bg"
3801
+ ];
3802
+ function lockDragHandle(editor, locked2) {
3803
+ if (editor.isDestroyed) return;
3804
+ editor.view.dispatch(
3805
+ editor.state.tr.setMeta("lockDragHandle", locked2).setMeta("addToHistory", false)
3806
+ );
3807
+ }
3808
+ async function copyMarkdown(markdown) {
3809
+ try {
3810
+ await navigator.clipboard.writeText(markdown);
3811
+ return;
3812
+ } catch {
3813
+ }
3814
+ const area = document.createElement("textarea");
3815
+ area.value = markdown;
3816
+ area.setAttribute("readonly", "");
3817
+ area.style.position = "fixed";
3818
+ area.style.left = "-9999px";
3819
+ document.body.appendChild(area);
3820
+ area.select();
3821
+ document.execCommand("copy");
3822
+ area.remove();
3823
+ }
3824
+ const TURN_ICON = {
3825
+ paragraph: "¶",
3826
+ "heading-1": "H1",
3827
+ "heading-2": "H2",
3828
+ "heading-3": "H3",
3829
+ bulletList: "•",
3830
+ orderedList: "1.",
3831
+ taskList: "✓",
3832
+ blockquote: "❝",
3833
+ codeBlock: "{ }"
3834
+ };
3835
+ function BlockDragHandle({ editor }) {
3836
+ const t = useEditorBundle(editor).blockDrag;
3837
+ const [target, setTarget] = useState(null);
3838
+ const [menu2, setMenu] = useState(null);
3839
+ const draggedRef = useRef(false);
3840
+ const targetRef = useRef(null);
3841
+ const menuRef = useRef(null);
3842
+ const closeMenu = useCallback(() => {
3843
+ setMenu(null);
3844
+ lockDragHandle(editor, false);
3845
+ }, [editor]);
3846
+ useEffect(() => {
3847
+ const plugin = blockDragListGuardPlugin();
3848
+ editor.registerPlugin(plugin);
3849
+ return () => {
3850
+ setBlockHandleDragging(editor.view, false);
3851
+ if (!editor.isDestroyed) editor.unregisterPlugin(blockDragListGuardKey);
3852
+ };
3853
+ }, [editor]);
3854
+ useEffect(() => {
3855
+ if (!menu2) return;
3856
+ const onKey = (event) => {
3857
+ if (event.key !== "Escape") return;
3858
+ event.preventDefault();
3859
+ closeMenu();
3860
+ };
3861
+ const onDown = (event) => {
3862
+ const el = event.target;
3863
+ if (!(el instanceof Element)) return;
3864
+ if (el.closest("[data-block-drag-menu], [data-block-drag-grip]")) return;
3865
+ closeMenu();
3866
+ };
3867
+ const onScroll = () => closeMenu();
3868
+ document.addEventListener("keydown", onKey);
3869
+ document.addEventListener("mousedown", onDown);
3870
+ document.addEventListener("scroll", onScroll, true);
3871
+ return () => {
3872
+ document.removeEventListener("keydown", onKey);
3873
+ document.removeEventListener("mousedown", onDown);
3874
+ document.removeEventListener("scroll", onScroll, true);
3875
+ };
3876
+ }, [menu2, closeMenu]);
3877
+ useEffect(() => {
3878
+ if (!menu2 || !menuRef.current) return;
3879
+ const style = getComputedStyle(editor.view.dom);
3880
+ for (const name of THEME_VARS) {
3881
+ const value = style.getPropertyValue(name);
3882
+ if (value) menuRef.current.style.setProperty(name, value);
3883
+ }
3884
+ }, [menu2, editor]);
3885
+ useEffect(() => () => lockDragHandle(editor, false), [editor, closeMenu]);
3886
+ const openMenu = (anchor) => {
3887
+ if (!target) return;
3888
+ const rect = anchor.getBoundingClientRect();
3889
+ const estimated = 380;
3890
+ const y = rect.top + estimated > window.innerHeight ? Math.max(8, window.innerHeight - estimated - 8) : rect.top;
3891
+ const x = Math.min(rect.right + 6, window.innerWidth - 220);
3892
+ lockDragHandle(editor, true);
3893
+ setMenu({ x, y, pos: target.pos, node: target.node });
3894
+ };
3895
+ const run = (action) => {
3896
+ action();
3897
+ editor.commands.focus();
3898
+ closeMenu();
3899
+ };
3900
+ const activeKind = menu2 ? currentTurnIntoKind(editor, menu2.pos, menu2.node) : null;
3901
+ const showTurn = menu2 ? canTurnBlockInto(menu2.node) : false;
3902
+ const showReset = menu2 ? canResetBlockFormatting(menu2.node) : false;
3903
+ const showDownload = menu2 ? isImageBlock(menu2.node) : false;
3904
+ const turnLabel = (kind) => {
3905
+ if (kind === "paragraph") return t.paragraph;
3906
+ if (kind === "heading-1") return t.headingLabel(1);
3907
+ if (kind === "heading-2") return t.headingLabel(2);
3908
+ if (kind === "heading-3") return t.headingLabel(3);
3909
+ if (kind === "bulletList") return t.bulletList;
3910
+ if (kind === "orderedList") return t.orderedList;
3911
+ if (kind === "taskList") return t.taskList;
3912
+ if (kind === "blockquote") return t.blockquote;
3913
+ return t.codeBlock;
3914
+ };
3915
+ return /* @__PURE__ */ jsxs(Fragment$2, { children: [
3916
+ /* @__PURE__ */ jsx(
3917
+ DragHandle,
3918
+ {
3919
+ editor,
3920
+ computePositionConfig: HANDLE_POSITION,
3921
+ onNodeChange: ({ node, pos }) => {
3922
+ if (!node || pos < 0) {
3923
+ targetRef.current = null;
3924
+ setTarget(null);
3925
+ return;
3926
+ }
3927
+ const next = { node, pos };
3928
+ targetRef.current = next;
3929
+ setTarget(next);
3930
+ },
3931
+ getReferencedVirtualElement: () => {
3932
+ const current = targetRef.current;
3933
+ if (!current || current.node.type.name !== "table") return null;
3934
+ const pos = current.pos;
3935
+ return {
3936
+ getBoundingClientRect: () => {
3937
+ let dom = null;
3938
+ try {
3939
+ dom = editor.view.nodeDOM(pos);
3940
+ } catch {
3941
+ dom = null;
3942
+ }
3943
+ if (!(dom instanceof HTMLElement)) return new DOMRect();
3944
+ const rect = dom.getBoundingClientRect();
3945
+ return new DOMRect(
3946
+ rect.x,
3947
+ rect.y - TABLE_ANCHOR_LIFT,
3948
+ rect.width,
3949
+ rect.height
3950
+ );
3951
+ }
3952
+ };
3953
+ },
3954
+ onElementDragStart: () => {
3955
+ draggedRef.current = true;
3956
+ setBlockHandleDragging(editor.view, true);
3957
+ },
3958
+ onElementDragEnd: () => {
3959
+ setBlockHandleDragging(editor.view, false);
3960
+ setTimeout(() => {
3961
+ draggedRef.current = false;
3962
+ }, 0);
3963
+ },
3964
+ children: /* @__PURE__ */ jsx(
3965
+ "div",
3966
+ {
3967
+ className: `${styles$7.cluster} ${(target == null ? void 0 : target.node.type.name) === "table" ? styles$7.clusterOnTable : ""}`,
3968
+ "data-block-drag": "",
3969
+ children: /* @__PURE__ */ jsx(
3970
+ "div",
3971
+ {
3972
+ role: "button",
3973
+ tabIndex: 0,
3974
+ className: `${styles$7.button} ${styles$7.grip}`,
3975
+ "data-block-drag-grip": "",
3976
+ title: t.dragHandle,
3977
+ "aria-label": t.dragHandle,
3978
+ "aria-haspopup": "menu",
3979
+ "aria-expanded": menu2 != null,
3980
+ onKeyDown: (event) => {
3981
+ if (event.key !== "Enter" && event.key !== " ") return;
3982
+ event.preventDefault();
3983
+ if (menu2) closeMenu();
3984
+ else openMenu(event.currentTarget);
2630
3985
  },
2631
- l.value
2632
- );
2633
- })
3986
+ onClick: (event) => {
3987
+ if (draggedRef.current) return;
3988
+ if (menu2) {
3989
+ closeMenu();
3990
+ return;
3991
+ }
3992
+ openMenu(event.currentTarget);
3993
+ },
3994
+ children: /* @__PURE__ */ jsx(GripIcon, { size: 14 })
3995
+ }
3996
+ )
2634
3997
  }
2635
- ) })
2636
- ] }),
2637
- /* @__PURE__ */ jsx(
2638
- "button",
2639
- {
2640
- type: "button",
2641
- className: styles$7.deleteBtn,
2642
- title: labels.delete,
2643
- "aria-label": labels.delete,
2644
- onClick: () => deleteNode(),
2645
- children: /* @__PURE__ */ jsx(TrashIcon, {})
2646
- }
2647
- )
2648
- ] }) : (
2649
- // 只读头部:左侧语言标签,右侧复制 + 展开收起(展开后按钮保留,供收回)
2650
- /* @__PURE__ */ jsxs("div", { className: styles$7.readonlyHeader, contentEditable: false, children: [
2651
- /* @__PURE__ */ jsxs("span", { className: styles$7.langBadge, children: [
2652
- /* @__PURE__ */ jsx("span", { className: styles$7.langBraces, children: "{}" }),
2653
- currentLabel
2654
- ] }),
2655
- /* @__PURE__ */ jsxs("span", { className: styles$7.readonlyActions, children: [
2656
- /* @__PURE__ */ jsx(
2657
- "button",
2658
- {
2659
- type: "button",
2660
- className: styles$7.actionBtn,
2661
- onClick: handleCopy,
2662
- "aria-label": labels.copy,
2663
- title: labels.copy,
2664
- children: copied ? /* @__PURE__ */ jsx(CheckIcon, { size: 14 }) : /* @__PURE__ */ jsx(CopyIcon, { size: 14 })
2665
- }
2666
- ),
2667
- /* @__PURE__ */ jsx(
2668
- "button",
2669
- {
2670
- type: "button",
2671
- className: styles$7.actionBtn,
2672
- onClick: () => setFolded((v) => !v),
2673
- "aria-label": folded2 ? labels.expand : labels.collapse,
2674
- title: folded2 ? labels.expand : labels.collapse,
2675
- "aria-expanded": !folded2,
2676
- children: /* @__PURE__ */ jsx(ChevronsUpDownIcon, { size: 14 })
2677
- }
2678
- )
2679
- ] })
2680
- ] })
3998
+ )
3999
+ }
2681
4000
  ),
2682
- /* @__PURE__ */ jsxs("pre", { ref: preRef, className: styles$7.pre, children: [
2683
- readonly2 ? /* @__PURE__ */ jsx(
2684
- "span",
4001
+ menu2 ? createPortal(
4002
+ /* @__PURE__ */ jsxs(
4003
+ "div",
2685
4004
  {
2686
- className: styles$7.gutter,
2687
- contentEditable: false,
2688
- "aria-hidden": "true",
2689
- children: Array.from({ length: lineCount }, (_, i) => /* @__PURE__ */ jsx("span", { className: styles$7.gutterLine, children: i + 1 }, i))
4005
+ ref: menuRef,
4006
+ className: styles$7.menu,
4007
+ "data-block-drag-menu": "",
4008
+ role: "menu",
4009
+ "aria-label": t.menuLabel,
4010
+ style: { left: menu2.x, top: menu2.y },
4011
+ onMouseDown: (event) => event.stopPropagation(),
4012
+ children: [
4013
+ showTurn ? /* @__PURE__ */ jsxs(Fragment$2, { children: [
4014
+ /* @__PURE__ */ jsx("div", { className: styles$7.groupLabel, children: t.turnInto }),
4015
+ TURN_INTO_KINDS.map((kind) => /* @__PURE__ */ jsxs(
4016
+ "button",
4017
+ {
4018
+ type: "button",
4019
+ role: "menuitem",
4020
+ className: kind === activeKind ? `${styles$7.item} ${styles$7.itemActive}` : styles$7.item,
4021
+ "data-block-drag-action": kind,
4022
+ onClick: () => run(() => turnBlockInto(editor, menu2.pos, kind)),
4023
+ children: [
4024
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: TURN_ICON[kind] }),
4025
+ /* @__PURE__ */ jsx("span", { children: turnLabel(kind) })
4026
+ ]
4027
+ },
4028
+ kind
4029
+ )),
4030
+ /* @__PURE__ */ jsx("div", { className: styles$7.divider })
4031
+ ] }) : null,
4032
+ /* @__PURE__ */ jsxs(
4033
+ "button",
4034
+ {
4035
+ type: "button",
4036
+ role: "menuitem",
4037
+ className: styles$7.item,
4038
+ "data-block-drag-action": "copy",
4039
+ onClick: () => run(() => {
4040
+ const markdown = blockMarkdown(editor, menu2.pos);
4041
+ if (markdown != null) void copyMarkdown(markdown);
4042
+ }),
4043
+ children: [
4044
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(CopyIcon, {}) }),
4045
+ /* @__PURE__ */ jsx("span", { children: t.copy })
4046
+ ]
4047
+ }
4048
+ ),
4049
+ /* @__PURE__ */ jsxs(
4050
+ "button",
4051
+ {
4052
+ type: "button",
4053
+ role: "menuitem",
4054
+ className: styles$7.item,
4055
+ "data-block-drag-action": "duplicate",
4056
+ onClick: () => run(() => duplicateBlock(editor, menu2.pos)),
4057
+ children: [
4058
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(CopyIcon, {}) }),
4059
+ /* @__PURE__ */ jsx("span", { children: t.duplicate })
4060
+ ]
4061
+ }
4062
+ ),
4063
+ showReset ? /* @__PURE__ */ jsxs(
4064
+ "button",
4065
+ {
4066
+ type: "button",
4067
+ role: "menuitem",
4068
+ className: styles$7.item,
4069
+ "data-block-drag-action": "reset",
4070
+ onClick: () => run(() => resetBlockFormatting(editor, menu2.pos)),
4071
+ children: [
4072
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(EraserIcon, {}) }),
4073
+ /* @__PURE__ */ jsx("span", { children: t.resetFormatting })
4074
+ ]
4075
+ }
4076
+ ) : null,
4077
+ showDownload ? /* @__PURE__ */ jsxs(
4078
+ "button",
4079
+ {
4080
+ type: "button",
4081
+ role: "menuitem",
4082
+ className: styles$7.item,
4083
+ "data-block-drag-action": "download",
4084
+ onClick: () => run(() => downloadImageBlock(menu2.node)),
4085
+ children: [
4086
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(DownloadIcon, {}) }),
4087
+ /* @__PURE__ */ jsx("span", { children: t.downloadImage })
4088
+ ]
4089
+ }
4090
+ ) : null,
4091
+ /* @__PURE__ */ jsx("div", { className: styles$7.divider }),
4092
+ /* @__PURE__ */ jsxs(
4093
+ "button",
4094
+ {
4095
+ type: "button",
4096
+ role: "menuitem",
4097
+ className: `${styles$7.item} ${styles$7.itemDanger}`,
4098
+ "data-block-drag-action": "delete",
4099
+ onClick: () => run(() => deleteBlock(editor, menu2.pos)),
4100
+ children: [
4101
+ /* @__PURE__ */ jsx("span", { className: styles$7.itemIcon, "aria-hidden": true, children: /* @__PURE__ */ jsx(TrashIcon, {}) }),
4102
+ /* @__PURE__ */ jsx("span", { children: t.delete })
4103
+ ]
4104
+ }
4105
+ )
4106
+ ]
2690
4107
  }
2691
- ) : null,
2692
- /* @__PURE__ */ jsx(NodeViewContent, { as: "code" })
2693
- ] })
4108
+ ),
4109
+ document.body
4110
+ ) : null
2694
4111
  ] });
2695
4112
  }
4113
+ function useIsMobile(breakpoint = 768) {
4114
+ const [isMobile, setIsMobile] = useState(false);
4115
+ useEffect(() => {
4116
+ const mq = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
4117
+ const update = () => setIsMobile(mq.matches);
4118
+ update();
4119
+ mq.addEventListener("change", update);
4120
+ return () => mq.removeEventListener("change", update);
4121
+ }, [breakpoint]);
4122
+ return isMobile;
4123
+ }
2696
4124
  const CodeBlock = CodeBlockLowlight.extend({
2697
- addOptions() {
2698
- var _a;
2699
- return {
2700
- ...(_a = this.parent) == null ? void 0 : _a.call(this),
2701
- codeBlockLabels: void 0
2702
- };
2703
- },
2704
4125
  // Built-in `code` tokens often win over chart's custom fence tokenizer;
2705
4126
  // upgrade ```tmr-chart here so the mid-column card editor renders ChartView.
2706
4127
  parseMarkdown: (token, helpers) => parseCodeTokenAsChartOrCodeBlock(token, helpers, "codeBlock"),
@@ -2846,16 +4267,48 @@ const ImageWithConfirmDelete = Image.extend({
2846
4267
  }
2847
4268
  });
2848
4269
  const DEFAULT_FIND_BAR_OFFSET = { top: 4, right: 4 };
2849
- function findBarOffsetStyle(offset) {
4270
+ function findBarOffsetStyle(offset2) {
2850
4271
  const px = (v) => typeof v === "number" ? `${v}px` : v;
2851
4272
  return {
2852
4273
  position: "absolute",
2853
- top: px(offset.top),
2854
- right: px(offset.right),
2855
- bottom: px(offset.bottom),
2856
- left: px(offset.left)
4274
+ top: px(offset2.top),
4275
+ right: px(offset2.right),
4276
+ bottom: px(offset2.bottom),
4277
+ left: px(offset2.left)
2857
4278
  };
2858
4279
  }
4280
+ function CharacterCountStatus({
4281
+ editor,
4282
+ locale
4283
+ }) {
4284
+ const t = getLocaleBundle(locale).characterCount;
4285
+ const counts = useEditorState({
4286
+ editor,
4287
+ selector: ({ editor: current }) => {
4288
+ const storage = current == null ? void 0 : current.storage.characterCount;
4289
+ if (!storage) return { characters: 0, words: 0 };
4290
+ return {
4291
+ characters: storage.characters(),
4292
+ words: storage.words()
4293
+ };
4294
+ }
4295
+ });
4296
+ return /* @__PURE__ */ jsxs(
4297
+ "div",
4298
+ {
4299
+ className: styles$e.charCount,
4300
+ role: "group",
4301
+ "aria-label": t.label,
4302
+ "data-characters": counts.characters,
4303
+ "data-words": counts.words,
4304
+ children: [
4305
+ /* @__PURE__ */ jsx("span", { children: t.characters(counts.characters) }),
4306
+ /* @__PURE__ */ jsx("span", { className: styles$e.charCountSep, "aria-hidden": "true", children: "·" }),
4307
+ /* @__PURE__ */ jsx("span", { children: t.words(counts.words) })
4308
+ ]
4309
+ }
4310
+ );
4311
+ }
2859
4312
  function editorOwnsKeyboard(editor) {
2860
4313
  const active2 = document.activeElement;
2861
4314
  const inEditor = !!active2 && editor.view.dom.contains(active2);
@@ -2866,6 +4319,7 @@ function editorOwnsKeyboard(editor) {
2866
4319
  const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2867
4320
  initialMarkdown = "",
2868
4321
  editable = true,
4322
+ locale = "en",
2869
4323
  sources,
2870
4324
  renderCitation,
2871
4325
  placeholder,
@@ -2874,7 +4328,6 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2874
4328
  markdownPaste = true,
2875
4329
  markdownFileDrop = true,
2876
4330
  extraExtensions,
2877
- codeBlockLabels,
2878
4331
  className,
2879
4332
  comments,
2880
4333
  activeCommentId,
@@ -2890,26 +4343,31 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2890
4343
  findBarContainer,
2891
4344
  findBarOffset = DEFAULT_FIND_BAR_OFFSET,
2892
4345
  findShortcut = true,
2893
- findLabels,
2894
4346
  onFindOpenChange,
2895
4347
  slashMenu = true,
2896
- slashMenuLabels,
4348
+ typography = true,
4349
+ emojiSuggestion = true,
4350
+ blockDrag = true,
2897
4351
  shortcutPanel = true,
2898
- shortcutLabels,
2899
4352
  shortcutFabContainer,
2900
4353
  imageUpload,
2901
- imageResize = true
4354
+ imageResize = true,
4355
+ characterCount = false
2902
4356
  }, ref) {
2903
4357
  const preparedInitial = prepareChartMarkdown(
2904
4358
  enrichMarkdownCitations(initialMarkdown, sources ?? [])
2905
4359
  );
4360
+ const blockDragMobile = useIsMobile(768);
4361
+ const showBlockDrag = blockDrag && editable && !blockDragMobile;
2906
4362
  const [findOpen, setFindOpen] = useState(false);
2907
4363
  const [shortcutsOpen, setShortcutsOpen] = useState(false);
2908
4364
  const findInputRef = useRef(null);
2909
4365
  const editor = useEditor({
2910
4366
  extensions: [
2911
- ...baseExtensions,
2912
- CodeBlock.configure({ lowlight, codeBlockLabels }),
4367
+ ...baseExtensions.map(
4368
+ (ext) => ext.name === "emoji" ? configureEmojiSuggestion(ext, emojiSuggestion) : ext
4369
+ ),
4370
+ CodeBlock.configure({ lowlight }),
2913
4371
  ImageWithConfirmDelete.configure({
2914
4372
  inline: false,
2915
4373
  // 图片缩放(默认开、默认等比、默认左右两条手柄):false 关掉;对象透传官方配置
@@ -2950,6 +4408,13 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2950
4408
  // - searchDebounceMs 关掉:官方防抖走 setTimeout,抛错会落在异步回调里兜不住;
2951
4409
  // 防抖改由浮动条自己做(见 findReplace.ts)。
2952
4410
  ...findReplace ? [FindAndReplace.configure({ injectCSS: false, searchDebounceMs: 0 })] : [],
4411
+ // 失焦选区:MIT 的 Selection。非空文本选区失焦后打上 .tmr-blur-selection,
4412
+ // 样式在 content.module.css。节点选区(图片、代码块整块)官方不保留。
4413
+ Selection$1.configure({ className: "tmr-blur-selection" }),
4414
+ // 字数统计:MIT 的 @tiptap/extensions CharacterCount(不是 @tiptap-pro)。
4415
+ // 只调用它的 storage,统计条是本库自绘。无 limit,不裁剪正文。
4416
+ // 词数换成自写的 countWords,否则整段中文会被默认的按空格切分算成 1 个词。
4417
+ ...characterCount ? [CharacterCount.configure({ wordCounter: countWords })] : [],
2953
4418
  TableOfContents.configure({
2954
4419
  getId: makeTocGetId(),
2955
4420
  onUpdate: (anchors) => {
@@ -2961,7 +4426,9 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2961
4426
  ...markdownPaste ? [MarkdownPaste] : [],
2962
4427
  ...markdownFileDrop ? [MarkdownFileDrop] : [],
2963
4428
  // 斜杠菜单(键入 / 唤起):默认开,只读态由 suggestion 状态机自身短路
2964
- ...slashMenu ? [SlashMenu.configure({ labels: slashMenuLabels })] : [],
4429
+ ...slashMenu ? [SlashMenu] : [],
4430
+ // 智能标点:输入规则,不增加节点,所以不进 baseExtensions。
4431
+ ...typography ? [Typography] : [],
2965
4432
  ...extraExtensions ?? []
2966
4433
  ],
2967
4434
  content: preparedInitial,
@@ -2971,10 +4438,11 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
2971
4438
  immediatelyRender: false,
2972
4439
  editorProps: {
2973
4440
  attributes: {
2974
- class: styles$c.editorContent
4441
+ class: styles$e.editorContent
2975
4442
  }
2976
4443
  }
2977
4444
  });
4445
+ usePublishEditorLocale(editor, locale);
2978
4446
  useEffect(() => {
2979
4447
  onEditorReady == null ? void 0 : onEditorReady(editor);
2980
4448
  return () => onEditorReady == null ? void 0 : onEditorReady(null);
@@ -3167,22 +4635,19 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
3167
4635
  FindReplaceBar,
3168
4636
  {
3169
4637
  editor,
3170
- labels: findLabels,
4638
+ locale,
3171
4639
  onClose: closeFind,
3172
4640
  inputRef: findInputRef,
3173
- className: styles$c.findBar,
4641
+ className: styles$e.findBar,
3174
4642
  style: findBarOffsetStyle(findBarOffset)
3175
4643
  }
3176
4644
  ) : null;
3177
- const shortcutT = useMemo(
3178
- () => ({ ...defaultShortcutLabels, ...shortcutLabels }),
3179
- [shortcutLabels]
3180
- );
4645
+ const shortcutT = getLocaleBundle(locale).shortcut;
3181
4646
  const [shortcutFabTarget, setShortcutFabTarget] = useState(
3182
4647
  null
3183
4648
  );
3184
- const useIsoLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
3185
- useIsoLayoutEffect(() => {
4649
+ const useIsoLayoutEffect2 = typeof window === "undefined" ? useEffect : useLayoutEffect;
4650
+ useIsoLayoutEffect2(() => {
3186
4651
  const target = typeof shortcutFabContainer === "function" ? shortcutFabContainer() : shortcutFabContainer ?? null;
3187
4652
  setShortcutFabTarget((prev) => prev === target ? prev : target);
3188
4653
  });
@@ -3190,7 +4655,7 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
3190
4655
  "button",
3191
4656
  {
3192
4657
  type: "button",
3193
- className: shortcutFabTarget ? `${styles$c.shortcutFab} ${styles$c.shortcutFabHosted}` : styles$c.shortcutFab,
4658
+ className: shortcutFabTarget ? `${styles$e.shortcutFab} ${styles$e.shortcutFabHosted}` : styles$e.shortcutFab,
3194
4659
  title: shortcutT.panelTitle,
3195
4660
  "aria-label": shortcutT.panelTitle,
3196
4661
  "aria-expanded": shortcutsOpen,
@@ -3198,32 +4663,49 @@ const MarkdownWysiwygEditor = forwardRef(function MarkdownWysiwygEditor2({
3198
4663
  children: /* @__PURE__ */ jsx(KeyboardIcon, { size: 18 })
3199
4664
  }
3200
4665
  ) : null;
3201
- return /* @__PURE__ */ jsxs("div", { className: styles$c.editorHost, children: [
3202
- findBarNode && !findBarTarget ? /* @__PURE__ */ jsx("div", { className: styles$c.findBarAnchor, children: findBarNode }) : null,
3203
- findBarNode && findBarTarget ? createPortal(findBarNode, findBarTarget) : null,
3204
- /* @__PURE__ */ jsx(
3205
- EditorContent,
3206
- {
3207
- editor,
3208
- className: className ? `${styles$c.editorScroll} ${className}` : styles$c.editorScroll
3209
- }
3210
- ),
3211
- shortcutFabNode && shortcutFabTarget ? createPortal(shortcutFabNode, shortcutFabTarget) : shortcutFabNode,
3212
- shortcutPanel && editable ? /* @__PURE__ */ jsx(
3213
- ShortcutPanel,
3214
- {
3215
- open: shortcutsOpen,
3216
- onClose: () => setShortcutsOpen(false),
3217
- labels: shortcutLabels
3218
- }
3219
- ) : null
3220
- ] });
4666
+ return /* @__PURE__ */ jsxs(
4667
+ "div",
4668
+ {
4669
+ className: styles$e.editorHost,
4670
+ "data-block-drag-gutter": showBlockDrag ? "" : void 0,
4671
+ children: [
4672
+ findBarNode && !findBarTarget ? /* @__PURE__ */ jsx("div", { className: styles$e.findBarAnchor, children: findBarNode }) : null,
4673
+ findBarNode && findBarTarget ? createPortal(findBarNode, findBarTarget) : null,
4674
+ /* @__PURE__ */ jsx(
4675
+ EditorContent,
4676
+ {
4677
+ editor,
4678
+ className: [
4679
+ styles$e.editorScroll,
4680
+ showBlockDrag ? styles$e.withBlockDrag : "",
4681
+ className ?? ""
4682
+ ].filter(Boolean).join(" ")
4683
+ }
4684
+ ),
4685
+ editor && showBlockDrag ? /* @__PURE__ */ jsx(BlockDragHandle, { editor }) : null,
4686
+ editor && characterCount ? /* @__PURE__ */ jsxs("div", { className: styles$e.editorStatus, children: [
4687
+ /* @__PURE__ */ jsx(CharacterCountStatus, { editor, locale }),
4688
+ shortcutFabNode && !shortcutFabTarget ? shortcutFabNode : null
4689
+ ] }) : shortcutFabNode && !shortcutFabTarget ? shortcutFabNode : null,
4690
+ shortcutFabNode && shortcutFabTarget ? createPortal(shortcutFabNode, shortcutFabTarget) : null,
4691
+ shortcutPanel && editable ? /* @__PURE__ */ jsx(
4692
+ ShortcutPanel,
4693
+ {
4694
+ open: shortcutsOpen,
4695
+ onClose: () => setShortcutsOpen(false),
4696
+ locale
4697
+ }
4698
+ ) : null
4699
+ ]
4700
+ }
4701
+ );
3221
4702
  });
3222
4703
  function MarkdownPreview({
3223
4704
  markdown,
3224
4705
  sources,
3225
4706
  renderCitation,
3226
- className
4707
+ className,
4708
+ locale = "en"
3227
4709
  }) {
3228
4710
  const prepared = useMemo(
3229
4711
  () => prepareChartMarkdown(
@@ -3258,11 +4740,12 @@ function MarkdownPreview({
3258
4740
  editable: false,
3259
4741
  immediatelyRender: false,
3260
4742
  editorProps: {
3261
- attributes: { class: styles$c.editorContent }
4743
+ attributes: { class: styles$e.editorContent }
3262
4744
  }
3263
4745
  },
3264
4746
  [extensions]
3265
4747
  );
4748
+ usePublishEditorLocale(editor, locale);
3266
4749
  useEffect(() => {
3267
4750
  if (!editor) return;
3268
4751
  editor.commands.setContent(prepared, { contentType: "markdown" });
@@ -3271,7 +4754,7 @@ function MarkdownPreview({
3271
4754
  EditorContent,
3272
4755
  {
3273
4756
  editor,
3274
- className: className ?? styles$c.editorScroll
4757
+ className: className ?? styles$e.editorScroll
3275
4758
  }
3276
4759
  );
3277
4760
  }
@@ -3343,45 +4826,57 @@ function splitTopLevelBlocks(markdown) {
3343
4826
  function stripToPlainText(markdown) {
3344
4827
  return markdown.replace(/!\[[^\]]*\]\([^)]+\)/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/^#{1,6}\s+/gm, "").replace(/^[ \t]*[-*+]\s+/gm, "").replace(/^[ \t]*\d+\.\s+/gm, "").replace(/[*_~`]+/g, "").replace(/<[^>]+>/g, "").replace(/\|/g, " ").replace(/\s+/g, " ").trim();
3345
4828
  }
3346
- const toolbar = "_toolbar_vghdk_2";
3347
- const inner = "_inner_vghdk_15";
3348
- const btn$2 = "_btn_vghdk_35";
3349
- const btnActive$1 = "_btnActive_vghdk_58";
3350
- const txtIcon = "_txtIcon_vghdk_74";
3351
- const colorIcon = "_colorIcon_vghdk_82";
3352
- const colorIconGlyph = "_colorIconGlyph_vghdk_90";
3353
- const colorIconBar = "_colorIconBar_vghdk_96";
3354
- const scriptTrigger = "_scriptTrigger_vghdk_103";
3355
- const scriptTriggerGlyph = "_scriptTriggerGlyph_vghdk_123";
3356
- const divider$2 = "_divider_vghdk_131";
3357
- const styleTrigger = "_styleTrigger_vghdk_140";
3358
- const styleTriggerLabel = "_styleTriggerLabel_vghdk_169";
3359
- const styleTriggerCaret = "_styleTriggerCaret_vghdk_177";
3360
- const iconTrigger = "_iconTrigger_vghdk_184";
3361
- const styleItem = "_styleItem_vghdk_192";
3362
- const styleIcon = "_styleIcon_vghdk_198";
3363
- const scriptGlyph = "_scriptGlyph_vghdk_208";
3364
- const menu = "_menu_vghdk_220";
3365
- const menuItem = "_menuItem_vghdk_231";
3366
- const menuItemSelected = "_menuItemSelected_vghdk_258";
3367
- const tableInsertCaret = "_tableInsertCaret_vghdk_271";
3368
- const tableSizePicker = "_tableSizePicker_vghdk_279";
3369
- const tableSizeGrid = "_tableSizeGrid_vghdk_289";
3370
- const tableSizeCell = "_tableSizeCell_vghdk_294";
3371
- const tableSizeCellActive = "_tableSizeCellActive_vghdk_304";
3372
- const tableSizeLabel = "_tableSizeLabel_vghdk_309";
3373
- const popoverContent$1 = "_popoverContent_vghdk_318";
3374
- const mathLayer = "_mathLayer_vghdk_322";
3375
- const mathStack = "_mathStack_vghdk_327";
3376
- const mathStackBlock = "_mathStackBlock_vghdk_334";
3377
- const mathHint = "_mathHint_vghdk_339";
3378
- const mathHintBlock = "_mathHintBlock_vghdk_351";
3379
- const mathHintIcon = "_mathHintIcon_vghdk_357";
3380
- const mathField = "_mathField_vghdk_388";
3381
- const mathFieldBlock = "_mathFieldBlock_vghdk_400";
3382
- const mathInput = "_mathInput_vghdk_407";
3383
- const mathDone = "_mathDone_vghdk_425";
3384
- const mathDoneKbd = "_mathDoneKbd_vghdk_450";
4829
+ const toolbar = "_toolbar_1l6t0_2";
4830
+ const inner = "_inner_1l6t0_15";
4831
+ const btn$2 = "_btn_1l6t0_35";
4832
+ const btnActive$1 = "_btnActive_1l6t0_58";
4833
+ const txtIcon = "_txtIcon_1l6t0_74";
4834
+ const colorIcon = "_colorIcon_1l6t0_82";
4835
+ const colorIconGlyph = "_colorIconGlyph_1l6t0_90";
4836
+ const colorIconBar = "_colorIconBar_1l6t0_96";
4837
+ const scriptTrigger = "_scriptTrigger_1l6t0_103";
4838
+ const scriptTriggerGlyph = "_scriptTriggerGlyph_1l6t0_123";
4839
+ const divider$2 = "_divider_1l6t0_131";
4840
+ const styleTrigger = "_styleTrigger_1l6t0_140";
4841
+ const styleTriggerLabel = "_styleTriggerLabel_1l6t0_174";
4842
+ const styleTriggerCaret = "_styleTriggerCaret_1l6t0_183";
4843
+ const iconTrigger = "_iconTrigger_1l6t0_190";
4844
+ const styleItem = "_styleItem_1l6t0_198";
4845
+ const styleIcon = "_styleIcon_1l6t0_204";
4846
+ const scriptGlyph = "_scriptGlyph_1l6t0_214";
4847
+ const menu = "_menu_1l6t0_226";
4848
+ const menuItem = "_menuItem_1l6t0_237";
4849
+ const menuItemSelected = "_menuItemSelected_1l6t0_264";
4850
+ const listSplit = "_listSplit_1l6t0_279";
4851
+ const listSplitActive = "_listSplitActive_1l6t0_285";
4852
+ const numberingCaret = "_numberingCaret_1l6t0_297";
4853
+ const numberingCaretActive = "_numberingCaretActive_1l6t0_327";
4854
+ const numberingMenu = "_numberingMenu_1l6t0_331";
4855
+ const numberingGrid = "_numberingGrid_1l6t0_341";
4856
+ const numberingOption = "_numberingOption_1l6t0_347";
4857
+ const numberingOptionActive = "_numberingOptionActive_1l6t0_362";
4858
+ const numberingPreview = "_numberingPreview_1l6t0_367";
4859
+ const numberingPreviewRow = "_numberingPreviewRow_1l6t0_373";
4860
+ const numberingPreviewMarker = "_numberingPreviewMarker_1l6t0_388";
4861
+ const numberingPreviewLine = "_numberingPreviewLine_1l6t0_396";
4862
+ const tableInsertCaret = "_tableInsertCaret_1l6t0_402";
4863
+ const tableSizePicker = "_tableSizePicker_1l6t0_410";
4864
+ const tableSizeGrid = "_tableSizeGrid_1l6t0_420";
4865
+ const tableSizeCell = "_tableSizeCell_1l6t0_425";
4866
+ const tableSizeCellActive = "_tableSizeCellActive_1l6t0_435";
4867
+ const tableSizeLabel = "_tableSizeLabel_1l6t0_440";
4868
+ const popoverContent$1 = "_popoverContent_1l6t0_449";
4869
+ const mathLayer = "_mathLayer_1l6t0_453";
4870
+ const mathStack = "_mathStack_1l6t0_458";
4871
+ const mathStackBlock = "_mathStackBlock_1l6t0_465";
4872
+ const mathHint = "_mathHint_1l6t0_470";
4873
+ const mathHintBlock = "_mathHintBlock_1l6t0_482";
4874
+ const mathHintIcon = "_mathHintIcon_1l6t0_488";
4875
+ const mathField = "_mathField_1l6t0_519";
4876
+ const mathFieldBlock = "_mathFieldBlock_1l6t0_531";
4877
+ const mathInput = "_mathInput_1l6t0_538";
4878
+ const mathDone = "_mathDone_1l6t0_556";
4879
+ const mathDoneKbd = "_mathDoneKbd_1l6t0_581";
3385
4880
  const styles$6 = {
3386
4881
  toolbar,
3387
4882
  inner,
@@ -3404,6 +4899,18 @@ const styles$6 = {
3404
4899
  menu,
3405
4900
  menuItem,
3406
4901
  menuItemSelected,
4902
+ listSplit,
4903
+ listSplitActive,
4904
+ numberingCaret,
4905
+ numberingCaretActive,
4906
+ numberingMenu,
4907
+ numberingGrid,
4908
+ numberingOption,
4909
+ numberingOptionActive,
4910
+ numberingPreview,
4911
+ numberingPreviewRow,
4912
+ numberingPreviewMarker,
4913
+ numberingPreviewLine,
3407
4914
  tableInsertCaret,
3408
4915
  tableSizePicker,
3409
4916
  tableSizeGrid,
@@ -3452,8 +4959,8 @@ const PALETTE = [
3452
4959
  ["#5c1010", "#700818", "#7c4808", "#7c6808", "#205818", "#10403c", "#183c78", "#0c3060", "#3c1460", "#601838"]
3453
4960
  ];
3454
4961
  const THEME_COLORS = ["#ff6719", "#ff5500"];
3455
- function ColorPalette({ value, onPick, labels }) {
3456
- const t = { ...defaultColorPaletteLabels, ...labels };
4962
+ function ColorPalette({ value, onPick, locale = "en" }) {
4963
+ const t = getLocaleBundle(locale).palette;
3457
4964
  const current = value == null ? void 0 : value.toLowerCase();
3458
4965
  return /* @__PURE__ */ jsxs("div", { className: styles$5.palette, children: [
3459
4966
  /* @__PURE__ */ jsxs(
@@ -3830,7 +5337,7 @@ const styles$3 = {
3830
5337
  function LinkPopover({
3831
5338
  editor,
3832
5339
  trigger,
3833
- labels,
5340
+ locale = "en",
3834
5341
  className,
3835
5342
  style
3836
5343
  }) {
@@ -3847,7 +5354,7 @@ function LinkPopover({
3847
5354
  LinkPanel,
3848
5355
  {
3849
5356
  editor,
3850
- labels,
5357
+ locale,
3851
5358
  className,
3852
5359
  style,
3853
5360
  onClose: () => setOpen(false)
@@ -3859,12 +5366,12 @@ function LinkPopover({
3859
5366
  }
3860
5367
  function LinkPanel({
3861
5368
  editor,
3862
- labels,
5369
+ locale,
3863
5370
  className,
3864
5371
  style,
3865
5372
  onClose
3866
5373
  }) {
3867
- const t = { ...defaultLinkPopoverLabels, ...labels };
5374
+ const t = getLocaleBundle(locale).link;
3868
5375
  const [url, setUrl] = useState(() => readLinkHref(editor));
3869
5376
  const [invalid, setInvalid] = useState(false);
3870
5377
  const canEdit = canSetLink(editor);
@@ -4146,15 +5653,15 @@ function appendColumnToTable(editor) {
4146
5653
  selectLineAt(editor, table, "col", TableMap.get(table.node).width - 1);
4147
5654
  return addColumnsAfter(editor, 1);
4148
5655
  }
4149
- const handle = "_handle_qjreh_9";
4150
- const handleRow = "_handleRow_qjreh_30";
4151
- const handleCol = "_handleCol_qjreh_40";
4152
- const extend = "_extend_qjreh_71";
4153
- const tableBubble = "_tableBubble_qjreh_117";
4154
- const tableMenuItem = "_tableMenuItem_qjreh_130";
4155
- const tableMenuItemIcon = "_tableMenuItemIcon_qjreh_156";
4156
- const tableMenuItemLabel = "_tableMenuItemLabel_qjreh_166";
4157
- const tableMenuDivider = "_tableMenuDivider_qjreh_171";
5656
+ const handle = "_handle_1m19y_9";
5657
+ const handleRow = "_handleRow_1m19y_30";
5658
+ const handleCol = "_handleCol_1m19y_40";
5659
+ const extend = "_extend_1m19y_71";
5660
+ const tableBubble = "_tableBubble_1m19y_117";
5661
+ const tableMenuItem = "_tableMenuItem_1m19y_130";
5662
+ const tableMenuItemIcon = "_tableMenuItemIcon_1m19y_156";
5663
+ const tableMenuItemLabel = "_tableMenuItemLabel_1m19y_166";
5664
+ const tableMenuDivider = "_tableMenuDivider_1m19y_171";
4158
5665
  const styles$2 = {
4159
5666
  handle,
4160
5667
  handleRow,
@@ -4166,9 +5673,9 @@ const styles$2 = {
4166
5673
  tableMenuItemLabel,
4167
5674
  tableMenuDivider
4168
5675
  };
4169
- function edgeFlags(editor, cell) {
5676
+ function edgeFlags(editor, cell2) {
4170
5677
  try {
4171
- const $cell = cellAround(editor.state.doc.resolve(editor.view.posAtDOM(cell, 0)));
5678
+ const $cell = cellAround(editor.state.doc.resolve(editor.view.posAtDOM(cell2, 0)));
4172
5679
  if (!$cell) return { onLastRow: false, onLastCol: false };
4173
5680
  const map = TableMap.get($cell.node(-1));
4174
5681
  const rect = map.findCell($cell.pos - $cell.start(-1));
@@ -4180,26 +5687,28 @@ function edgeFlags(editor, cell) {
4180
5687
  return { onLastRow: false, onLastCol: false };
4181
5688
  }
4182
5689
  }
4183
- const COL_HANDLE_GAP = 22;
4184
- const ROW_HANDLE_GAP = 26;
5690
+ const COL_HANDLE_GAP = 18;
5691
+ const COL_HANDLE_HEIGHT = 12;
5692
+ const ROW_HANDLE_GAP = 20;
5693
+ const ROW_HANDLE_WIDTH = 12;
4185
5694
  const EXTEND_GAP = 6;
4186
- const EXTEND_THICKNESS = 16;
5695
+ const EXTEND_THICKNESS = 12;
4187
5696
  const HOVER_KEEP_MS = 160;
4188
- function measureCell(editor, cell) {
4189
- const table = cell.closest("table");
4190
- const row2 = cell.closest("tr");
5697
+ function measureCell(editor, cell2) {
5698
+ const table = cell2.closest("table");
5699
+ const row2 = cell2.closest("tr");
4191
5700
  if (!table || !row2) return null;
4192
5701
  try {
4193
- editor.view.posAtDOM(cell, 0);
5702
+ editor.view.posAtDOM(cell2, 0);
4194
5703
  } catch {
4195
5704
  return null;
4196
5705
  }
4197
- const cellRect = cell.getBoundingClientRect();
5706
+ const cellRect = cell2.getBoundingClientRect();
4198
5707
  const rowRect = row2.getBoundingClientRect();
4199
5708
  const tableRect = table.getBoundingClientRect();
4200
- const edges = edgeFlags(editor, cell);
5709
+ const edges = edgeFlags(editor, cell2);
4201
5710
  return {
4202
- cell,
5711
+ cell: cell2,
4203
5712
  ...edges,
4204
5713
  rowTop: rowRect.top,
4205
5714
  rowHeight: rowRect.height,
@@ -4211,10 +5720,10 @@ function measureCell(editor, cell) {
4211
5720
  tableBottom: tableRect.bottom
4212
5721
  };
4213
5722
  }
4214
- function selectRowOrColumn(editor, cell, kind) {
5723
+ function selectRowOrColumn(editor, cell2, kind) {
4215
5724
  let pos;
4216
5725
  try {
4217
- pos = editor.view.posAtDOM(cell, 0);
5726
+ pos = editor.view.posAtDOM(cell2, 0);
4218
5727
  } catch {
4219
5728
  return false;
4220
5729
  }
@@ -4317,9 +5826,9 @@ function TableHandles({ editor, labels }) {
4317
5826
  const closeMenu = useCallback(() => setMenu(null), []);
4318
5827
  const remeasureHover = useCallback(() => {
4319
5828
  var _a;
4320
- const cell = (_a = hoverRef.current) == null ? void 0 : _a.cell;
4321
- if (!cell) return;
4322
- const next = cell.isConnected ? measureCell(editor, cell) : null;
5829
+ const cell2 = (_a = hoverRef.current) == null ? void 0 : _a.cell;
5830
+ if (!cell2) return;
5831
+ const next = cell2.isConnected ? measureCell(editor, cell2) : null;
4323
5832
  hoverRef.current = next;
4324
5833
  setHover(next);
4325
5834
  }, [editor]);
@@ -4338,8 +5847,8 @@ function TableHandles({ editor, labels }) {
4338
5847
  cancelPendingClear();
4339
5848
  return;
4340
5849
  }
4341
- const cell = target && editor.view.dom.contains(target) ? target.closest("td, th") : null;
4342
- if (!cell) {
5850
+ const cell2 = target && editor.view.dom.contains(target) ? target.closest("td, th") : null;
5851
+ if (!cell2) {
4343
5852
  if (hoverRef.current && clearTimerRef.current === null) {
4344
5853
  clearTimerRef.current = setTimeout(() => {
4345
5854
  clearTimerRef.current = null;
@@ -4349,8 +5858,8 @@ function TableHandles({ editor, labels }) {
4349
5858
  return;
4350
5859
  }
4351
5860
  cancelPendingClear();
4352
- if (((_b = hoverRef.current) == null ? void 0 : _b.cell) === cell) return;
4353
- const next = measureCell(editor, cell);
5861
+ if (((_b = hoverRef.current) == null ? void 0 : _b.cell) === cell2) return;
5862
+ const next = measureCell(editor, cell2);
4354
5863
  hoverRef.current = next;
4355
5864
  setHover(next);
4356
5865
  };
@@ -4430,8 +5939,8 @@ function TableHandles({ editor, labels }) {
4430
5939
  className: `${styles$2.handle} ${styles$2.handleRow}`,
4431
5940
  style: {
4432
5941
  left: hover.tableLeft - ROW_HANDLE_GAP,
4433
- // 长度跟着行高走(原来固定 28px 居中,行高时就显得太短)
4434
5942
  top: hover.rowTop,
5943
+ width: ROW_HANDLE_WIDTH,
4435
5944
  height: hover.rowHeight
4436
5945
  },
4437
5946
  title: labels.tableRowMenu,
@@ -4448,10 +5957,10 @@ function TableHandles({ editor, labels }) {
4448
5957
  "data-table-handle": "col",
4449
5958
  className: `${styles$2.handle} ${styles$2.handleCol}`,
4450
5959
  style: {
4451
- // 长度跟着列宽走,两端与单元格对齐
4452
5960
  left: hover.colLeft,
4453
5961
  top: hover.tableTop - COL_HANDLE_GAP,
4454
- width: hover.colWidth
5962
+ width: hover.colWidth,
5963
+ height: COL_HANDLE_HEIGHT
4455
5964
  },
4456
5965
  title: labels.tableColumnMenu,
4457
5966
  "aria-label": labels.tableColumnMenu,
@@ -4816,7 +6325,9 @@ const MenuCloseCtx = createContext(() => {
4816
6325
  });
4817
6326
  function MenuPopover({
4818
6327
  trigger,
4819
- children
6328
+ children,
6329
+ contentClassName,
6330
+ align = "start"
4820
6331
  }) {
4821
6332
  const [open, setOpen] = useState(false);
4822
6333
  const close = useCallback(() => setOpen(false), []);
@@ -4825,9 +6336,9 @@ function MenuPopover({
4825
6336
  /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
4826
6337
  Popover.Content,
4827
6338
  {
4828
- className: styles$6.menu,
6339
+ className: contentClassName ? `${styles$6.menu} ${contentClassName}` : styles$6.menu,
4829
6340
  sideOffset: 4,
4830
- align: "start",
6341
+ align,
4831
6342
  onOpenAutoFocus: (e2) => e2.preventDefault(),
4832
6343
  onCloseAutoFocus: (e2) => e2.preventDefault(),
4833
6344
  children: /* @__PURE__ */ jsx(MenuCloseCtx.Provider, { value: close, children })
@@ -4857,6 +6368,43 @@ function MenuItem({
4857
6368
  }
4858
6369
  );
4859
6370
  }
6371
+ function NumberingFormatGrid({
6372
+ current,
6373
+ onSelect
6374
+ }) {
6375
+ const close = useContext(MenuCloseCtx);
6376
+ return /* @__PURE__ */ jsx("div", { className: styles$6.numberingGrid, role: "group", "aria-label": "Numbering formats", children: NUMBERING_FORMAT_IDS.map((format) => {
6377
+ const active2 = current === format;
6378
+ return /* @__PURE__ */ jsx(
6379
+ "button",
6380
+ {
6381
+ type: "button",
6382
+ role: "menuitem",
6383
+ "aria-label": NUMBERING_FORMAT_LABELS[format],
6384
+ "aria-pressed": active2,
6385
+ className: `${styles$6.numberingOption}${active2 ? ` ${styles$6.numberingOptionActive}` : ""}`,
6386
+ onMouseDown: (e2) => e2.preventDefault(),
6387
+ onClick: () => {
6388
+ onSelect(format);
6389
+ close();
6390
+ },
6391
+ children: /* @__PURE__ */ jsx("span", { className: styles$6.numberingPreview, children: NUMBERING_FORMAT_PREVIEWS[format].map((row2, index) => /* @__PURE__ */ jsxs(
6392
+ "span",
6393
+ {
6394
+ className: styles$6.numberingPreviewRow,
6395
+ "data-depth": row2.depth,
6396
+ children: [
6397
+ /* @__PURE__ */ jsx("span", { className: styles$6.numberingPreviewMarker, children: row2.marker }),
6398
+ /* @__PURE__ */ jsx("span", { className: styles$6.numberingPreviewLine })
6399
+ ]
6400
+ },
6401
+ `${format}-${index}`
6402
+ )) })
6403
+ },
6404
+ format
6405
+ );
6406
+ }) });
6407
+ }
4860
6408
  const TABLE_PICKER_COLS = 10;
4861
6409
  const TABLE_PICKER_ROWS = 8;
4862
6410
  function TableInsertItem({
@@ -4949,7 +6497,7 @@ function ColorPopover({
4949
6497
  bar: bar2,
4950
6498
  value,
4951
6499
  onPick,
4952
- paletteLabels
6500
+ locale
4953
6501
  }) {
4954
6502
  const [open, setOpen] = useState(false);
4955
6503
  return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
@@ -4983,7 +6531,7 @@ function ColorPopover({
4983
6531
  onPick(c);
4984
6532
  setOpen(false);
4985
6533
  },
4986
- labels: paletteLabels
6534
+ locale
4987
6535
  }
4988
6536
  )
4989
6537
  }
@@ -5000,13 +6548,13 @@ function EditorToolbar({
5000
6548
  importAccept,
5001
6549
  importMenuItems,
5002
6550
  showImport = true,
5003
- labels,
6551
+ locale = "en",
5004
6552
  onSearch,
5005
6553
  searchActive = false,
5006
6554
  extraToolbarItems,
5007
6555
  className
5008
6556
  }) {
5009
- const t = { ...defaultToolbarLabels, ...labels };
6557
+ const t = getLocaleBundle(locale).toolbar;
5010
6558
  const state = useEditorState({
5011
6559
  editor,
5012
6560
  selector: ({ editor: e2 }) => ({
@@ -5024,7 +6572,9 @@ function EditorToolbar({
5024
6572
  isParagraph: e2.isActive("paragraph"),
5025
6573
  bulletList: e2.isActive("bulletList"),
5026
6574
  orderedList: e2.isActive("orderedList"),
6575
+ numberingFormat: readCurrentNumberingFormat(e2),
5027
6576
  blockquote: e2.isActive("blockquote"),
6577
+ details: e2.isActive("details"),
5028
6578
  textColor: e2.getAttributes("textStyle").color || void 0,
5029
6579
  highlightColor: e2.getAttributes("highlight").color || void 0,
5030
6580
  superscript: e2.isActive("superscript"),
@@ -5397,7 +6947,7 @@ function EditorToolbar({
5397
6947
  bar: state.textColor || "#ff3b30",
5398
6948
  value: state.textColor,
5399
6949
  onPick: applyTextColor,
5400
- paletteLabels: { none: t.colorNone, theme: t.colorTheme }
6950
+ locale
5401
6951
  }
5402
6952
  ),
5403
6953
  /* @__PURE__ */ jsx(
@@ -5408,7 +6958,7 @@ function EditorToolbar({
5408
6958
  bar: state.highlightColor || "#ffd83d",
5409
6959
  value: state.highlightColor,
5410
6960
  onPick: applyHighlight,
5411
- paletteLabels: { none: t.colorNone, theme: t.colorTheme }
6961
+ locale
5412
6962
  }
5413
6963
  ),
5414
6964
  /* @__PURE__ */ jsxs(
@@ -5476,13 +7026,7 @@ function EditorToolbar({
5476
7026
  LinkPopover,
5477
7027
  {
5478
7028
  editor,
5479
- labels: {
5480
- field: t.linkPrompt,
5481
- apply: t.linkApply,
5482
- open: t.linkOpen,
5483
- remove: t.linkRemove,
5484
- invalid: t.linkInvalid
5485
- },
7029
+ locale,
5486
7030
  trigger: /* @__PURE__ */ jsx(ToolbarButton, { title: t.link, active: state.link, children: /* @__PURE__ */ jsx(LinkIcon, {}) })
5487
7031
  }
5488
7032
  ),
@@ -5543,6 +7087,19 @@ function EditorToolbar({
5543
7087
  children: /* @__PURE__ */ jsx("span", { className: styles$6.txtIcon, children: "❝" })
5544
7088
  }
5545
7089
  ),
7090
+ /* @__PURE__ */ jsx(
7091
+ ToolbarButton,
7092
+ {
7093
+ title: t.details,
7094
+ active: state.details,
7095
+ onClick: () => {
7096
+ const next = editor.chain().focus();
7097
+ if (editor.isActive("details")) next.unsetDetails().run();
7098
+ else next.setDetails().run();
7099
+ },
7100
+ children: /* @__PURE__ */ jsx(DetailsIcon, {})
7101
+ }
7102
+ ),
5546
7103
  /* @__PURE__ */ jsx(Divider, {}),
5547
7104
  /* @__PURE__ */ jsx(
5548
7105
  MenuPopover,
@@ -5663,13 +7220,46 @@ function EditorToolbar({
5663
7220
  children: /* @__PURE__ */ jsx(ListUnorderedIcon, {})
5664
7221
  }
5665
7222
  ),
5666
- /* @__PURE__ */ jsx(
5667
- ToolbarButton,
7223
+ /* @__PURE__ */ jsxs(
7224
+ "span",
5668
7225
  {
5669
- title: t.orderedList,
5670
- active: state.orderedList,
5671
- onClick: () => chain().toggleOrderedList().run(),
5672
- children: /* @__PURE__ */ jsx(ListOrderedIcon, {})
7226
+ className: `${styles$6.listSplit}${state.orderedList ? ` ${styles$6.listSplitActive}` : ""}`,
7227
+ children: [
7228
+ /* @__PURE__ */ jsx(
7229
+ ToolbarButton,
7230
+ {
7231
+ title: t.orderedList,
7232
+ active: state.orderedList,
7233
+ onClick: () => chain().toggleOrderedList().run(),
7234
+ children: /* @__PURE__ */ jsx(ListOrderedIcon, {})
7235
+ }
7236
+ ),
7237
+ /* @__PURE__ */ jsx(
7238
+ MenuPopover,
7239
+ {
7240
+ align: "end",
7241
+ contentClassName: styles$6.numberingMenu,
7242
+ trigger: /* @__PURE__ */ jsx(
7243
+ "button",
7244
+ {
7245
+ type: "button",
7246
+ className: `${styles$6.numberingCaret}${state.numberingFormat ? ` ${styles$6.numberingCaretActive}` : ""}`,
7247
+ title: t.numberingFormat,
7248
+ "aria-label": t.numberingFormat,
7249
+ onMouseDown: (e2) => e2.preventDefault(),
7250
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: 12 })
7251
+ }
7252
+ ),
7253
+ children: /* @__PURE__ */ jsx(
7254
+ NumberingFormatGrid,
7255
+ {
7256
+ current: state.numberingFormat,
7257
+ onSelect: (format) => applyNumberingFormat(editor, format)
7258
+ }
7259
+ )
7260
+ }
7261
+ )
7262
+ ]
5673
7263
  }
5674
7264
  ),
5675
7265
  /* @__PURE__ */ jsx(
@@ -5779,17 +7369,6 @@ function EditorToolbar({
5779
7369
  /* @__PURE__ */ jsx(ImageAlignTools, { editor, labels: t })
5780
7370
  ] });
5781
7371
  }
5782
- function useIsMobile(breakpoint = 768) {
5783
- const [isMobile, setIsMobile] = useState(false);
5784
- useEffect(() => {
5785
- const mq = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
5786
- const update = () => setIsMobile(mq.matches);
5787
- update();
5788
- mq.addEventListener("change", update);
5789
- return () => mq.removeEventListener("change", update);
5790
- }, [breakpoint]);
5791
- return isMobile;
5792
- }
5793
7372
  const desktop = "_desktop_euqdt_3";
5794
7373
  const header = "_header_euqdt_19";
5795
7374
  const headerTitle = "_headerTitle_euqdt_27";
@@ -5828,8 +7407,13 @@ const styles$1 = {
5828
7407
  mobilePanel,
5829
7408
  mobilePanelOpen
5830
7409
  };
5831
- function TocPanel({ items, activeId, onItemClick, labels }) {
5832
- const t = { ...defaultTocLabels, ...labels };
7410
+ function TocPanel({
7411
+ items,
7412
+ activeId,
7413
+ onItemClick,
7414
+ locale = "en"
7415
+ }) {
7416
+ const t = getLocaleBundle(locale).toc;
5833
7417
  const isMobile = useIsMobile();
5834
7418
  const [collapsed, setCollapsed] = useState(false);
5835
7419
  const [mobileOpen, setMobileOpen] = useState(false);
@@ -6000,11 +7584,11 @@ function findRangeByAnchor(editor, anchor) {
6000
7584
  const BLOCK_SEPARATOR = "\n\n";
6001
7585
  function topLevelBlocks(editor) {
6002
7586
  const blocks = [];
6003
- editor.state.doc.forEach((node, offset) => {
7587
+ editor.state.doc.forEach((node, offset2) => {
6004
7588
  blocks.push({
6005
7589
  json: node.toJSON(),
6006
- from: offset,
6007
- to: offset + node.nodeSize
7590
+ from: offset2,
7591
+ to: offset2 + node.nodeSize
6008
7592
  });
6009
7593
  });
6010
7594
  return blocks;
@@ -6134,12 +7718,15 @@ export {
6134
7718
  CommentMark,
6135
7719
  CommentPopover,
6136
7720
  CopyIcon,
7721
+ DetailsIcon,
7722
+ DownloadIcon,
6137
7723
  EditorToolbar,
6138
7724
  EraserIcon,
6139
7725
  ExternalLinkIcon,
6140
7726
  FIND_DEBOUNCE_MS,
6141
7727
  default2 as FindAndReplace,
6142
7728
  FindReplaceBar,
7729
+ GripIcon,
6143
7730
  ImageAlignTools,
6144
7731
  ImageIcon,
6145
7732
  ImageUploadNode,
@@ -6198,17 +7785,6 @@ export {
6198
7785
  createChart,
6199
7786
  createCitationRef,
6200
7787
  createDefaultSlashMenuItems,
6201
- defaultChartLabels,
6202
- defaultCodeBlockLabels,
6203
- defaultColorPaletteLabels,
6204
- defaultCommentLabels,
6205
- defaultFindLabels,
6206
- defaultImageUploadLabels,
6207
- defaultLinkPopoverLabels,
6208
- defaultShortcutLabels,
6209
- defaultSlashMenuLabels,
6210
- defaultTocLabels,
6211
- defaultToolbarLabels,
6212
7788
  enrichMarkdownCitations,
6213
7789
  b2 as extractFootnoteSources,
6214
7790
  e as extractToc,