miyuan-editor 0.0.3

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 (58) hide show
  1. package/README.md +525 -0
  2. package/dist/core/index.cjs.js +40 -0
  3. package/dist/core/index.esm.js +4 -0
  4. package/dist/dist-5Q_Z9Ell.js +6390 -0
  5. package/dist/dist-5Q_Z9Ell.js.map +1 -0
  6. package/dist/dist-CMM6n8DO.cjs +4629 -0
  7. package/dist/dist-CMM6n8DO.cjs.map +1 -0
  8. package/dist/dist-CRSJDo2G.cjs +6617 -0
  9. package/dist/dist-CRSJDo2G.cjs.map +1 -0
  10. package/dist/dist-CZw77IJK.js +4612 -0
  11. package/dist/dist-CZw77IJK.js.map +1 -0
  12. package/dist/dist-CnVrDtsI.js +556 -0
  13. package/dist/dist-CnVrDtsI.js.map +1 -0
  14. package/dist/dist-rItBfhNb.cjs +591 -0
  15. package/dist/dist-rItBfhNb.cjs.map +1 -0
  16. package/dist/export-utils-CYaNoyVg.cjs +167 -0
  17. package/dist/export-utils-CYaNoyVg.cjs.map +1 -0
  18. package/dist/export-utils-DN0Gu8Vu.js +144 -0
  19. package/dist/export-utils-DN0Gu8Vu.js.map +1 -0
  20. package/dist/extension-BPFuYyzN.cjs +338 -0
  21. package/dist/extension-BPFuYyzN.cjs.map +1 -0
  22. package/dist/extension-Cl6x5MDR.js +321 -0
  23. package/dist/extension-Cl6x5MDR.js.map +1 -0
  24. package/dist/extensions/index.cjs.js +3462 -0
  25. package/dist/extensions/index.cjs.js.map +1 -0
  26. package/dist/extensions/index.esm.js +3412 -0
  27. package/dist/extensions/index.esm.js.map +1 -0
  28. package/dist/prompt-B4AOP8f_.js +24143 -0
  29. package/dist/prompt-B4AOP8f_.js.map +1 -0
  30. package/dist/prompt-CGLw2O21.cjs +25530 -0
  31. package/dist/prompt-CGLw2O21.cjs.map +1 -0
  32. package/dist/react/index.cjs.js +839 -0
  33. package/dist/react/index.cjs.js.map +1 -0
  34. package/dist/react/index.esm.js +820 -0
  35. package/dist/react/index.esm.js.map +1 -0
  36. package/dist/shortcut-panel-BskGXV8n.js +49468 -0
  37. package/dist/shortcut-panel-BskGXV8n.js.map +1 -0
  38. package/dist/shortcut-panel-yP4RPTFt.cjs +49563 -0
  39. package/dist/shortcut-panel-yP4RPTFt.cjs.map +1 -0
  40. package/dist/toc-extension-BESc0uEW.js +150 -0
  41. package/dist/toc-extension-BESc0uEW.js.map +1 -0
  42. package/dist/toc-extension-SRvSuskn.cjs +173 -0
  43. package/dist/toc-extension-SRvSuskn.cjs.map +1 -0
  44. package/dist/toolbar-config-Cgc9mV2v.js +243 -0
  45. package/dist/toolbar-config-Cgc9mV2v.js.map +1 -0
  46. package/dist/toolbar-config-Cjt_fPMi.cjs +260 -0
  47. package/dist/toolbar-config-Cjt_fPMi.cjs.map +1 -0
  48. package/dist/ui/index.cjs.js +18 -0
  49. package/dist/ui/index.esm.js +3 -0
  50. package/dist/vue/index.cjs.js +323 -0
  51. package/dist/vue/index.cjs.js.map +1 -0
  52. package/dist/vue/index.esm.js +307 -0
  53. package/dist/vue/index.esm.js.map +1 -0
  54. package/dist/vue2/index.cjs.js +323 -0
  55. package/dist/vue2/index.cjs.js.map +1 -0
  56. package/dist/vue2/index.esm.js +307 -0
  57. package/dist/vue2/index.esm.js.map +1 -0
  58. package/package.json +116 -0
@@ -0,0 +1,150 @@
1
+ import { m as PluginKey, p as Plugin } from "./dist-5Q_Z9Ell.js";
2
+ //#region src/extensions/word-count-extension.ts
3
+ var wordCountKey = new PluginKey("wordCount");
4
+ function calculateStats(state) {
5
+ const doc = state.doc;
6
+ const text = doc.textBetween(0, doc.content.size, " ", "");
7
+ const characters = text.length;
8
+ const charactersWithoutSpaces = text.replace(/\s/g, "").length;
9
+ const words = text.trim().split(/\s+/).filter((w) => w.length > 0).length;
10
+ const sentences = text.split(/[.!?。!?]+/).filter((s) => s.trim().length > 0).length;
11
+ let paragraphs = 0;
12
+ doc.descendants((node) => {
13
+ if (node.type.name === "paragraph") paragraphs++;
14
+ });
15
+ const readingTime = Math.ceil(words / 200);
16
+ return {
17
+ characters,
18
+ charactersWithoutSpaces,
19
+ words,
20
+ sentences,
21
+ paragraphs,
22
+ readingTime
23
+ };
24
+ }
25
+ function WordCountExtension(config) {
26
+ return {
27
+ name: "wordCount",
28
+ type: "extension",
29
+ addCommands() {
30
+ return { getWordCount: () => (state) => {
31
+ return calculateStats(state);
32
+ } };
33
+ },
34
+ addPlugins() {
35
+ return [new Plugin({
36
+ key: wordCountKey,
37
+ state: {
38
+ init(_, state) {
39
+ const stats = calculateStats(state);
40
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(stats), 0);
41
+ return stats;
42
+ },
43
+ apply(tr, prevStats, _, newState) {
44
+ if (tr.docChanged) {
45
+ const stats = calculateStats(newState);
46
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(stats), 0);
47
+ return stats;
48
+ }
49
+ return prevStats;
50
+ }
51
+ }
52
+ })];
53
+ }
54
+ };
55
+ }
56
+ function getWordCountStats(state) {
57
+ return wordCountKey.getState(state) || calculateStats(state);
58
+ }
59
+ //#endregion
60
+ //#region src/extensions/toc-extension.ts
61
+ var tocKey = new PluginKey("tableOfContents");
62
+ function generateId(text, index) {
63
+ return `heading-${text.toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim() || index}`;
64
+ }
65
+ function extractToc(state) {
66
+ const toc = [];
67
+ let index = 0;
68
+ state.doc.descendants((node, pos) => {
69
+ if (node.type.name === "heading") {
70
+ const level = node.attrs.level;
71
+ const text = node.textContent;
72
+ if (text.trim()) {
73
+ toc.push({
74
+ id: generateId(text, index),
75
+ text,
76
+ level,
77
+ pos
78
+ });
79
+ index++;
80
+ }
81
+ }
82
+ });
83
+ return toc;
84
+ }
85
+ function TableOfContentsExtension(config) {
86
+ return {
87
+ name: "tableOfContents",
88
+ type: "extension",
89
+ addCommands() {
90
+ return {
91
+ getTableOfContents: () => (state) => {
92
+ return extractToc(state);
93
+ },
94
+ insertTableOfContents: () => (state, dispatch) => {
95
+ const toc = extractToc(state);
96
+ if (toc.length === 0) return false;
97
+ const tocType = state.schema.nodes["toc"];
98
+ if (!tocType) return false;
99
+ if (dispatch) {
100
+ const node = tocType.create({ items: JSON.stringify(toc) });
101
+ dispatch(state.tr.replaceSelectionWith(node).scrollIntoView());
102
+ }
103
+ return true;
104
+ },
105
+ updateTableOfContents: () => (state, dispatch) => {
106
+ const toc = extractToc(state);
107
+ let updated = false;
108
+ state.doc.descendants((node, pos) => {
109
+ if (node.type.name === "toc") {
110
+ const newItems = JSON.stringify(toc);
111
+ if (node.attrs.items !== newItems) {
112
+ if (dispatch) dispatch(state.tr.setNodeMarkup(pos, null, { items: newItems }));
113
+ updated = true;
114
+ }
115
+ }
116
+ });
117
+ return updated;
118
+ }
119
+ };
120
+ },
121
+ addPlugins() {
122
+ return [new Plugin({
123
+ key: tocKey,
124
+ state: {
125
+ init(_, state) {
126
+ const toc = extractToc(state);
127
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(toc), 0);
128
+ return toc;
129
+ },
130
+ apply(tr, prevToc, _, newState) {
131
+ if (tr.docChanged) {
132
+ const toc = extractToc(newState);
133
+ const hasHeadingChanged = JSON.stringify(toc) !== JSON.stringify(prevToc);
134
+ if (hasHeadingChanged && config?.onUpdate) setTimeout(() => config.onUpdate(toc), 0);
135
+ return hasHeadingChanged ? toc : prevToc;
136
+ }
137
+ return prevToc;
138
+ }
139
+ }
140
+ })];
141
+ }
142
+ };
143
+ }
144
+ function getTableOfContents(state) {
145
+ return tocKey.getState(state) || extractToc(state);
146
+ }
147
+ //#endregion
148
+ export { getWordCountStats as i, getTableOfContents as n, WordCountExtension as r, TableOfContentsExtension as t };
149
+
150
+ //# sourceMappingURL=toc-extension-BESc0uEW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc-extension-BESc0uEW.js","names":[],"sources":["../src/extensions/word-count-extension.ts","../src/extensions/toc-extension.ts"],"sourcesContent":["import type { Extension } from '../core/extension'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport type { EditorState } from 'prosemirror-state'\n\nexport interface WordCountStats {\n characters: number\n charactersWithoutSpaces: number\n words: number\n sentences: number\n paragraphs: number\n readingTime: number\n}\n\nexport interface WordCountConfig {\n onUpdate?: (stats: WordCountStats) => void\n}\n\nconst wordCountKey = new PluginKey('wordCount')\n\nfunction calculateStats(state: EditorState): WordCountStats {\n const doc = state.doc\n const text = doc.textBetween(0, doc.content.size, ' ', '')\n\n const characters = text.length\n const charactersWithoutSpaces = text.replace(/\\s/g, '').length\n\n const words = text.trim().split(/\\s+/).filter(w => w.length > 0).length\n\n const sentences = text.split(/[.!?。!?]+/).filter(s => s.trim().length > 0).length\n\n let paragraphs = 0\n doc.descendants((node) => {\n if (node.type.name === 'paragraph') {\n paragraphs++\n }\n })\n\n const readingTime = Math.ceil(words / 200)\n\n return {\n characters,\n charactersWithoutSpaces,\n words,\n sentences,\n paragraphs,\n readingTime,\n }\n}\n\nexport function WordCountExtension(config?: WordCountConfig): Extension {\n return {\n name: 'wordCount',\n type: 'extension',\n addCommands() {\n return {\n getWordCount: () => (state) => {\n return calculateStats(state) as unknown as boolean\n },\n }\n },\n addPlugins() {\n return [\n new Plugin({\n key: wordCountKey,\n state: {\n init(_, state) {\n const stats = calculateStats(state)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(stats), 0)\n }\n return stats\n },\n apply(tr, prevStats, _, newState) {\n if (tr.docChanged) {\n const stats = calculateStats(newState)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(stats), 0)\n }\n return stats\n }\n return prevStats\n },\n },\n }),\n ]\n },\n }\n}\n\nexport function getWordCountStats(state: EditorState): WordCountStats {\n const stats = wordCountKey.getState(state) as WordCountStats | undefined\n return stats || calculateStats(state)\n}\n","import type { Extension } from '../core/extension'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport type { EditorState } from 'prosemirror-state'\nimport type { Node } from 'prosemirror-model'\n\nexport interface TocItem {\n id: string\n text: string\n level: number\n pos: number\n}\n\nexport interface TocConfig {\n onUpdate?: (toc: TocItem[]) => void\n}\n\nconst tocKey = new PluginKey('tableOfContents')\n\nfunction generateId(text: string, index: number): string {\n const slug = text\n .toLowerCase()\n .replace(/[^\\w\\u4e00-\\u9fa5\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .trim()\n return `heading-${slug || index}`\n}\n\nfunction extractToc(state: EditorState): TocItem[] {\n const toc: TocItem[] = []\n let index = 0\n\n state.doc.descendants((node: Node, pos: number) => {\n if (node.type.name === 'heading') {\n const level = node.attrs.level as number\n const text = node.textContent\n if (text.trim()) {\n toc.push({\n id: generateId(text, index),\n text,\n level,\n pos,\n })\n index++\n }\n }\n })\n\n return toc\n}\n\nexport function TableOfContentsExtension(config?: TocConfig): Extension {\n return {\n name: 'tableOfContents',\n type: 'extension',\n addCommands() {\n return {\n getTableOfContents: () => (state) => {\n return extractToc(state) as unknown as boolean\n },\n insertTableOfContents: () => (state, dispatch) => {\n const toc = extractToc(state)\n if (toc.length === 0) return false\n\n const tocType = state.schema.nodes['toc']\n if (!tocType) return false\n\n if (dispatch) {\n const node = tocType.create({ items: JSON.stringify(toc) })\n const tr = state.tr.replaceSelectionWith(node)\n dispatch(tr.scrollIntoView())\n }\n return true\n },\n updateTableOfContents: () => (state, dispatch) => {\n const toc = extractToc(state)\n let updated = false\n\n state.doc.descendants((node, pos) => {\n if (node.type.name === 'toc') {\n const newItems = JSON.stringify(toc)\n if (node.attrs.items !== newItems) {\n if (dispatch) {\n const tr = state.tr.setNodeMarkup(pos, null, { items: newItems })\n dispatch(tr)\n }\n updated = true\n }\n }\n })\n\n return updated\n },\n }\n },\n addPlugins() {\n return [\n new Plugin({\n key: tocKey,\n state: {\n init(_, state) {\n const toc = extractToc(state)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(toc), 0)\n }\n return toc\n },\n apply(tr, prevToc, _, newState) {\n if (tr.docChanged) {\n const toc = extractToc(newState)\n const hasHeadingChanged = JSON.stringify(toc) !== JSON.stringify(prevToc)\n if (hasHeadingChanged && config?.onUpdate) {\n setTimeout(() => config.onUpdate!(toc), 0)\n }\n return hasHeadingChanged ? toc : prevToc\n }\n return prevToc\n },\n },\n }),\n ]\n },\n }\n}\n\nexport function getTableOfContents(state: EditorState): TocItem[] {\n const toc = tocKey.getState(state) as TocItem[] | undefined\n return toc || extractToc(state)\n}\n"],"mappings":"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"}
@@ -0,0 +1,173 @@
1
+ const require_dist = require("./dist-CRSJDo2G.cjs");
2
+ //#region src/extensions/word-count-extension.ts
3
+ var wordCountKey = new require_dist.PluginKey("wordCount");
4
+ function calculateStats(state) {
5
+ const doc = state.doc;
6
+ const text = doc.textBetween(0, doc.content.size, " ", "");
7
+ const characters = text.length;
8
+ const charactersWithoutSpaces = text.replace(/\s/g, "").length;
9
+ const words = text.trim().split(/\s+/).filter((w) => w.length > 0).length;
10
+ const sentences = text.split(/[.!?。!?]+/).filter((s) => s.trim().length > 0).length;
11
+ let paragraphs = 0;
12
+ doc.descendants((node) => {
13
+ if (node.type.name === "paragraph") paragraphs++;
14
+ });
15
+ const readingTime = Math.ceil(words / 200);
16
+ return {
17
+ characters,
18
+ charactersWithoutSpaces,
19
+ words,
20
+ sentences,
21
+ paragraphs,
22
+ readingTime
23
+ };
24
+ }
25
+ function WordCountExtension(config) {
26
+ return {
27
+ name: "wordCount",
28
+ type: "extension",
29
+ addCommands() {
30
+ return { getWordCount: () => (state) => {
31
+ return calculateStats(state);
32
+ } };
33
+ },
34
+ addPlugins() {
35
+ return [new require_dist.Plugin({
36
+ key: wordCountKey,
37
+ state: {
38
+ init(_, state) {
39
+ const stats = calculateStats(state);
40
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(stats), 0);
41
+ return stats;
42
+ },
43
+ apply(tr, prevStats, _, newState) {
44
+ if (tr.docChanged) {
45
+ const stats = calculateStats(newState);
46
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(stats), 0);
47
+ return stats;
48
+ }
49
+ return prevStats;
50
+ }
51
+ }
52
+ })];
53
+ }
54
+ };
55
+ }
56
+ function getWordCountStats(state) {
57
+ return wordCountKey.getState(state) || calculateStats(state);
58
+ }
59
+ //#endregion
60
+ //#region src/extensions/toc-extension.ts
61
+ var tocKey = new require_dist.PluginKey("tableOfContents");
62
+ function generateId(text, index) {
63
+ return `heading-${text.toLowerCase().replace(/[^\w\u4e00-\u9fa5\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim() || index}`;
64
+ }
65
+ function extractToc(state) {
66
+ const toc = [];
67
+ let index = 0;
68
+ state.doc.descendants((node, pos) => {
69
+ if (node.type.name === "heading") {
70
+ const level = node.attrs.level;
71
+ const text = node.textContent;
72
+ if (text.trim()) {
73
+ toc.push({
74
+ id: generateId(text, index),
75
+ text,
76
+ level,
77
+ pos
78
+ });
79
+ index++;
80
+ }
81
+ }
82
+ });
83
+ return toc;
84
+ }
85
+ function TableOfContentsExtension(config) {
86
+ return {
87
+ name: "tableOfContents",
88
+ type: "extension",
89
+ addCommands() {
90
+ return {
91
+ getTableOfContents: () => (state) => {
92
+ return extractToc(state);
93
+ },
94
+ insertTableOfContents: () => (state, dispatch) => {
95
+ const toc = extractToc(state);
96
+ if (toc.length === 0) return false;
97
+ const tocType = state.schema.nodes["toc"];
98
+ if (!tocType) return false;
99
+ if (dispatch) {
100
+ const node = tocType.create({ items: JSON.stringify(toc) });
101
+ dispatch(state.tr.replaceSelectionWith(node).scrollIntoView());
102
+ }
103
+ return true;
104
+ },
105
+ updateTableOfContents: () => (state, dispatch) => {
106
+ const toc = extractToc(state);
107
+ let updated = false;
108
+ state.doc.descendants((node, pos) => {
109
+ if (node.type.name === "toc") {
110
+ const newItems = JSON.stringify(toc);
111
+ if (node.attrs.items !== newItems) {
112
+ if (dispatch) dispatch(state.tr.setNodeMarkup(pos, null, { items: newItems }));
113
+ updated = true;
114
+ }
115
+ }
116
+ });
117
+ return updated;
118
+ }
119
+ };
120
+ },
121
+ addPlugins() {
122
+ return [new require_dist.Plugin({
123
+ key: tocKey,
124
+ state: {
125
+ init(_, state) {
126
+ const toc = extractToc(state);
127
+ if (config?.onUpdate) setTimeout(() => config.onUpdate(toc), 0);
128
+ return toc;
129
+ },
130
+ apply(tr, prevToc, _, newState) {
131
+ if (tr.docChanged) {
132
+ const toc = extractToc(newState);
133
+ const hasHeadingChanged = JSON.stringify(toc) !== JSON.stringify(prevToc);
134
+ if (hasHeadingChanged && config?.onUpdate) setTimeout(() => config.onUpdate(toc), 0);
135
+ return hasHeadingChanged ? toc : prevToc;
136
+ }
137
+ return prevToc;
138
+ }
139
+ }
140
+ })];
141
+ }
142
+ };
143
+ }
144
+ function getTableOfContents(state) {
145
+ return tocKey.getState(state) || extractToc(state);
146
+ }
147
+ //#endregion
148
+ Object.defineProperty(exports, "TableOfContentsExtension", {
149
+ enumerable: true,
150
+ get: function() {
151
+ return TableOfContentsExtension;
152
+ }
153
+ });
154
+ Object.defineProperty(exports, "WordCountExtension", {
155
+ enumerable: true,
156
+ get: function() {
157
+ return WordCountExtension;
158
+ }
159
+ });
160
+ Object.defineProperty(exports, "getTableOfContents", {
161
+ enumerable: true,
162
+ get: function() {
163
+ return getTableOfContents;
164
+ }
165
+ });
166
+ Object.defineProperty(exports, "getWordCountStats", {
167
+ enumerable: true,
168
+ get: function() {
169
+ return getWordCountStats;
170
+ }
171
+ });
172
+
173
+ //# sourceMappingURL=toc-extension-SRvSuskn.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toc-extension-SRvSuskn.cjs","names":[],"sources":["../src/extensions/word-count-extension.ts","../src/extensions/toc-extension.ts"],"sourcesContent":["import type { Extension } from '../core/extension'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport type { EditorState } from 'prosemirror-state'\n\nexport interface WordCountStats {\n characters: number\n charactersWithoutSpaces: number\n words: number\n sentences: number\n paragraphs: number\n readingTime: number\n}\n\nexport interface WordCountConfig {\n onUpdate?: (stats: WordCountStats) => void\n}\n\nconst wordCountKey = new PluginKey('wordCount')\n\nfunction calculateStats(state: EditorState): WordCountStats {\n const doc = state.doc\n const text = doc.textBetween(0, doc.content.size, ' ', '')\n\n const characters = text.length\n const charactersWithoutSpaces = text.replace(/\\s/g, '').length\n\n const words = text.trim().split(/\\s+/).filter(w => w.length > 0).length\n\n const sentences = text.split(/[.!?。!?]+/).filter(s => s.trim().length > 0).length\n\n let paragraphs = 0\n doc.descendants((node) => {\n if (node.type.name === 'paragraph') {\n paragraphs++\n }\n })\n\n const readingTime = Math.ceil(words / 200)\n\n return {\n characters,\n charactersWithoutSpaces,\n words,\n sentences,\n paragraphs,\n readingTime,\n }\n}\n\nexport function WordCountExtension(config?: WordCountConfig): Extension {\n return {\n name: 'wordCount',\n type: 'extension',\n addCommands() {\n return {\n getWordCount: () => (state) => {\n return calculateStats(state) as unknown as boolean\n },\n }\n },\n addPlugins() {\n return [\n new Plugin({\n key: wordCountKey,\n state: {\n init(_, state) {\n const stats = calculateStats(state)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(stats), 0)\n }\n return stats\n },\n apply(tr, prevStats, _, newState) {\n if (tr.docChanged) {\n const stats = calculateStats(newState)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(stats), 0)\n }\n return stats\n }\n return prevStats\n },\n },\n }),\n ]\n },\n }\n}\n\nexport function getWordCountStats(state: EditorState): WordCountStats {\n const stats = wordCountKey.getState(state) as WordCountStats | undefined\n return stats || calculateStats(state)\n}\n","import type { Extension } from '../core/extension'\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport type { EditorState } from 'prosemirror-state'\nimport type { Node } from 'prosemirror-model'\n\nexport interface TocItem {\n id: string\n text: string\n level: number\n pos: number\n}\n\nexport interface TocConfig {\n onUpdate?: (toc: TocItem[]) => void\n}\n\nconst tocKey = new PluginKey('tableOfContents')\n\nfunction generateId(text: string, index: number): string {\n const slug = text\n .toLowerCase()\n .replace(/[^\\w\\u4e00-\\u9fa5\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .trim()\n return `heading-${slug || index}`\n}\n\nfunction extractToc(state: EditorState): TocItem[] {\n const toc: TocItem[] = []\n let index = 0\n\n state.doc.descendants((node: Node, pos: number) => {\n if (node.type.name === 'heading') {\n const level = node.attrs.level as number\n const text = node.textContent\n if (text.trim()) {\n toc.push({\n id: generateId(text, index),\n text,\n level,\n pos,\n })\n index++\n }\n }\n })\n\n return toc\n}\n\nexport function TableOfContentsExtension(config?: TocConfig): Extension {\n return {\n name: 'tableOfContents',\n type: 'extension',\n addCommands() {\n return {\n getTableOfContents: () => (state) => {\n return extractToc(state) as unknown as boolean\n },\n insertTableOfContents: () => (state, dispatch) => {\n const toc = extractToc(state)\n if (toc.length === 0) return false\n\n const tocType = state.schema.nodes['toc']\n if (!tocType) return false\n\n if (dispatch) {\n const node = tocType.create({ items: JSON.stringify(toc) })\n const tr = state.tr.replaceSelectionWith(node)\n dispatch(tr.scrollIntoView())\n }\n return true\n },\n updateTableOfContents: () => (state, dispatch) => {\n const toc = extractToc(state)\n let updated = false\n\n state.doc.descendants((node, pos) => {\n if (node.type.name === 'toc') {\n const newItems = JSON.stringify(toc)\n if (node.attrs.items !== newItems) {\n if (dispatch) {\n const tr = state.tr.setNodeMarkup(pos, null, { items: newItems })\n dispatch(tr)\n }\n updated = true\n }\n }\n })\n\n return updated\n },\n }\n },\n addPlugins() {\n return [\n new Plugin({\n key: tocKey,\n state: {\n init(_, state) {\n const toc = extractToc(state)\n if (config?.onUpdate) {\n setTimeout(() => config.onUpdate!(toc), 0)\n }\n return toc\n },\n apply(tr, prevToc, _, newState) {\n if (tr.docChanged) {\n const toc = extractToc(newState)\n const hasHeadingChanged = JSON.stringify(toc) !== JSON.stringify(prevToc)\n if (hasHeadingChanged && config?.onUpdate) {\n setTimeout(() => config.onUpdate!(toc), 0)\n }\n return hasHeadingChanged ? toc : prevToc\n }\n return prevToc\n },\n },\n }),\n ]\n },\n }\n}\n\nexport function getTableOfContents(state: EditorState): TocItem[] {\n const toc = tocKey.getState(state) as TocItem[] | undefined\n return toc || extractToc(state)\n}\n"],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ //#region src/ui/toolbar-config.ts
2
+ var DEFAULT_TOOLBAR_CONFIG = { groups: [
3
+ [
4
+ {
5
+ type: "button",
6
+ name: "bold",
7
+ label: "B",
8
+ tip: "加粗 (Ctrl+B)",
9
+ command: "bold"
10
+ },
11
+ {
12
+ type: "button",
13
+ name: "italic",
14
+ label: "I",
15
+ tip: "斜体 (Ctrl+I)",
16
+ command: "italic"
17
+ },
18
+ {
19
+ type: "button",
20
+ name: "underline",
21
+ label: "U",
22
+ tip: "下划线 (Ctrl+U)",
23
+ command: "underline"
24
+ },
25
+ {
26
+ type: "button",
27
+ name: "strike",
28
+ label: "S",
29
+ tip: "删除线",
30
+ command: "strike"
31
+ },
32
+ {
33
+ type: "button",
34
+ name: "code",
35
+ label: "<>",
36
+ tip: "行内代码 (Ctrl+E)",
37
+ command: "code"
38
+ }
39
+ ],
40
+ [{
41
+ type: "button",
42
+ name: "textColor",
43
+ label: "A",
44
+ tip: "文字颜色",
45
+ command: "setTextColor"
46
+ }, {
47
+ type: "button",
48
+ name: "backgroundColor",
49
+ label: "🖊",
50
+ tip: "背景颜色",
51
+ command: "setBackgroundColor"
52
+ }],
53
+ [
54
+ {
55
+ type: "button",
56
+ name: "h1",
57
+ label: "H1",
58
+ tip: "标题 1 (Ctrl+Alt+1)",
59
+ command: "heading",
60
+ args: [1]
61
+ },
62
+ {
63
+ type: "button",
64
+ name: "h2",
65
+ label: "H2",
66
+ tip: "标题 2 (Ctrl+Alt+2)",
67
+ command: "heading",
68
+ args: [2]
69
+ },
70
+ {
71
+ type: "button",
72
+ name: "h3",
73
+ label: "H3",
74
+ tip: "标题 3 (Ctrl+Alt+3)",
75
+ command: "heading",
76
+ args: [3]
77
+ }
78
+ ],
79
+ [{
80
+ type: "select",
81
+ name: "fontSize"
82
+ }],
83
+ [
84
+ {
85
+ type: "button",
86
+ name: "blockquote",
87
+ label: "❝",
88
+ tip: "引用块 (Ctrl+Shift+B)",
89
+ command: "blockquote"
90
+ },
91
+ {
92
+ type: "button",
93
+ name: "bulletList",
94
+ label: "≡",
95
+ tip: "无序列表",
96
+ command: "bulletList"
97
+ },
98
+ {
99
+ type: "button",
100
+ name: "orderedList",
101
+ label: "≡.",
102
+ tip: "有序列表",
103
+ command: "orderedList"
104
+ },
105
+ {
106
+ type: "button",
107
+ name: "taskList",
108
+ label: "☑",
109
+ tip: "任务列表",
110
+ command: "taskList"
111
+ },
112
+ {
113
+ type: "button",
114
+ name: "codeBlock",
115
+ label: "{}",
116
+ tip: "代码块 (Ctrl+Shift+C)",
117
+ command: "codeBlock"
118
+ },
119
+ {
120
+ type: "button",
121
+ name: "horizontalRule",
122
+ label: "—",
123
+ tip: "分割线",
124
+ command: "horizontalRule"
125
+ }
126
+ ],
127
+ [{
128
+ type: "button",
129
+ name: "outdent",
130
+ label: "⇤",
131
+ tip: "减少缩进 (Shift+Tab)",
132
+ command: "outdent"
133
+ }, {
134
+ type: "button",
135
+ name: "indent",
136
+ label: "⇥",
137
+ tip: "增加缩进 (Tab)",
138
+ command: "indent"
139
+ }],
140
+ [
141
+ {
142
+ type: "button",
143
+ name: "alignLeft",
144
+ label: "☰",
145
+ tip: "左对齐",
146
+ command: "setTextAlign",
147
+ args: ["left"]
148
+ },
149
+ {
150
+ type: "button",
151
+ name: "alignCenter",
152
+ label: "☷",
153
+ tip: "居中",
154
+ command: "setTextAlign",
155
+ args: ["center"]
156
+ },
157
+ {
158
+ type: "button",
159
+ name: "alignRight",
160
+ label: "☵",
161
+ tip: "右对齐",
162
+ command: "setTextAlign",
163
+ args: ["right"]
164
+ }
165
+ ],
166
+ [{
167
+ type: "select",
168
+ name: "lineHeight"
169
+ }],
170
+ [{
171
+ type: "button",
172
+ name: "image",
173
+ label: "📷",
174
+ tip: "插入图片",
175
+ command: "insertImage"
176
+ }],
177
+ [{
178
+ type: "button",
179
+ name: "table",
180
+ label: "⊞",
181
+ tip: "插入表格 (Ctrl+Alt+T)",
182
+ command: "insertTable",
183
+ args: [3, 3]
184
+ }],
185
+ [{
186
+ type: "button",
187
+ name: "undo",
188
+ label: "↩",
189
+ tip: "撤销 (Ctrl+Z)",
190
+ command: "undo"
191
+ }, {
192
+ type: "button",
193
+ name: "redo",
194
+ label: "↪",
195
+ tip: "重做 (Ctrl+Y)",
196
+ command: "redo"
197
+ }]
198
+ ] };
199
+ var OPTIONAL_TOOLBAR_ITEMS = {
200
+ productCard: {
201
+ type: "button",
202
+ name: "productCard",
203
+ label: "🛍️",
204
+ tip: "商品卡片"
205
+ },
206
+ aiPolish: {
207
+ type: "button",
208
+ name: "aiPolish",
209
+ label: "AI润色",
210
+ tip: "AI 助手 (Ctrl+J)"
211
+ },
212
+ aiWriter: {
213
+ type: "button",
214
+ name: "aiWriter",
215
+ label: "AI写作",
216
+ tip: "AI 写作 (Ctrl+Shift+W)"
217
+ },
218
+ htmlImport: {
219
+ type: "button",
220
+ name: "htmlImport",
221
+ label: "📥",
222
+ tip: "导入 HTML"
223
+ }
224
+ };
225
+ function createToolbarConfig(options = {}) {
226
+ const groups = [...{ ...DEFAULT_TOOLBAR_CONFIG }.groups];
227
+ if (options.includeProductCard || options.includeAI || options.includeHtmlImport) {
228
+ const extraButtons = [];
229
+ if (options.includeHtmlImport) extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.htmlImport);
230
+ if (options.includeProductCard) extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.productCard);
231
+ if (options.includeAI) {
232
+ extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.aiPolish);
233
+ extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.aiWriter);
234
+ }
235
+ if (extraButtons.length > 0) groups.push(extraButtons);
236
+ }
237
+ if (options.customGroups) groups.push(...options.customGroups);
238
+ return { groups };
239
+ }
240
+ //#endregion
241
+ export { OPTIONAL_TOOLBAR_ITEMS as n, createToolbarConfig as r, DEFAULT_TOOLBAR_CONFIG as t };
242
+
243
+ //# sourceMappingURL=toolbar-config-Cgc9mV2v.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolbar-config-Cgc9mV2v.js","names":[],"sources":["../src/ui/toolbar-config.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Toolbar Configuration\n// ---------------------------------------------------------------------------\n\nexport interface ToolbarButton {\n type: 'button' | 'select' | 'divider' | 'custom'\n name?: string\n label?: string\n icon?: string\n tip?: string\n command?: string\n args?: unknown[]\n active?: boolean | ((state: unknown) => boolean)\n}\n\nexport interface ToolbarConfig {\n groups: ToolbarButton[][]\n}\n\n// ---------------------------------------------------------------------------\n// Default Toolbar Configuration\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_TOOLBAR_CONFIG: ToolbarConfig = {\n groups: [\n // Format marks\n [\n { type: 'button', name: 'bold', label: 'B', tip: '加粗 (Ctrl+B)', command: 'bold' },\n { type: 'button', name: 'italic', label: 'I', tip: '斜体 (Ctrl+I)', command: 'italic' },\n { type: 'button', name: 'underline', label: 'U', tip: '下划线 (Ctrl+U)', command: 'underline' },\n { type: 'button', name: 'strike', label: 'S', tip: '删除线', command: 'strike' },\n { type: 'button', name: 'code', label: '<>', tip: '行内代码 (Ctrl+E)', command: 'code' },\n ],\n // Colors\n [\n { type: 'button', name: 'textColor', label: 'A', tip: '文字颜色', command: 'setTextColor' },\n { type: 'button', name: 'backgroundColor', label: '🖊', tip: '背景颜色', command: 'setBackgroundColor' },\n ],\n // Headings\n [\n { type: 'button', name: 'h1', label: 'H1', tip: '标题 1 (Ctrl+Alt+1)', command: 'heading', args: [1] },\n { type: 'button', name: 'h2', label: 'H2', tip: '标题 2 (Ctrl+Alt+2)', command: 'heading', args: [2] },\n { type: 'button', name: 'h3', label: 'H3', tip: '标题 3 (Ctrl+Alt+3)', command: 'heading', args: [3] },\n ],\n // Font size\n [\n { type: 'select', name: 'fontSize' },\n ],\n // Block elements\n [\n { type: 'button', name: 'blockquote', label: '❝', tip: '引用块 (Ctrl+Shift+B)', command: 'blockquote' },\n { type: 'button', name: 'bulletList', label: '≡', tip: '无序列表', command: 'bulletList' },\n { type: 'button', name: 'orderedList', label: '≡.', tip: '有序列表', command: 'orderedList' },\n { type: 'button', name: 'taskList', label: '☑', tip: '任务列表', command: 'taskList' },\n { type: 'button', name: 'codeBlock', label: '{}', tip: '代码块 (Ctrl+Shift+C)', command: 'codeBlock' },\n { type: 'button', name: 'horizontalRule', label: '—', tip: '分割线', command: 'horizontalRule' },\n ],\n // Indent\n [\n { type: 'button', name: 'outdent', label: '⇤', tip: '减少缩进 (Shift+Tab)', command: 'outdent' },\n { type: 'button', name: 'indent', label: '⇥', tip: '增加缩进 (Tab)', command: 'indent' },\n ],\n // Alignment\n [\n { type: 'button', name: 'alignLeft', label: '☰', tip: '左对齐', command: 'setTextAlign', args: ['left'] },\n { type: 'button', name: 'alignCenter', label: '☷', tip: '居中', command: 'setTextAlign', args: ['center'] },\n { type: 'button', name: 'alignRight', label: '☵', tip: '右对齐', command: 'setTextAlign', args: ['right'] },\n ],\n // Line height\n [\n { type: 'select', name: 'lineHeight' },\n ],\n // Image\n [\n { type: 'button', name: 'image', label: '📷', tip: '插入图片', command: 'insertImage' },\n ],\n // Table\n [\n { type: 'button', name: 'table', label: '⊞', tip: '插入表格 (Ctrl+Alt+T)', command: 'insertTable', args: [3, 3] },\n ],\n // History\n [\n { type: 'button', name: 'undo', label: '↩', tip: '撤销 (Ctrl+Z)', command: 'undo' },\n { type: 'button', name: 'redo', label: '↪', tip: '重做 (Ctrl+Y)', command: 'redo' },\n ],\n ],\n}\n\n// ---------------------------------------------------------------------------\n// Optional Toolbar Items\n// ---------------------------------------------------------------------------\n\nexport const OPTIONAL_TOOLBAR_ITEMS = {\n productCard: { type: 'button' as const, name: 'productCard', label: '🛍️', tip: '商品卡片' },\n aiPolish: { type: 'button' as const, name: 'aiPolish', label: 'AI润色', tip: 'AI 助手 (Ctrl+J)' },\n aiWriter: { type: 'button' as const, name: 'aiWriter', label: 'AI写作', tip: 'AI 写作 (Ctrl+Shift+W)' },\n htmlImport: { type: 'button' as const, name: 'htmlImport', label: '📥', tip: '导入 HTML' },\n}\n\n// ---------------------------------------------------------------------------\n// Helper Functions\n// ---------------------------------------------------------------------------\n\nexport function createToolbarConfig(options: {\n includeProductCard?: boolean\n includeAI?: boolean\n includeHtmlImport?: boolean\n customGroups?: ToolbarButton[][]\n} = {}): ToolbarConfig {\n const config = { ...DEFAULT_TOOLBAR_CONFIG }\n const groups = [...config.groups]\n\n if (options.includeProductCard || options.includeAI || options.includeHtmlImport) {\n const extraButtons: ToolbarButton[] = []\n \n if (options.includeHtmlImport) {\n extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.htmlImport)\n }\n \n if (options.includeProductCard) {\n extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.productCard)\n }\n \n if (options.includeAI) {\n extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.aiPolish)\n extraButtons.push(OPTIONAL_TOOLBAR_ITEMS.aiWriter)\n }\n \n if (extraButtons.length > 0) {\n groups.push(extraButtons)\n }\n }\n\n if (options.customGroups) {\n groups.push(...options.customGroups)\n }\n\n return { groups }\n}\n"],"mappings":"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"}