@moraya/core 0.1.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 (58) hide show
  1. package/CHANGELOG.md +344 -0
  2. package/LICENSE +85 -0
  3. package/README.md +82 -0
  4. package/dist/adapters/browser-media-resolver.d.ts +21 -0
  5. package/dist/adapters/browser-media-resolver.js +24 -0
  6. package/dist/adapters/browser-media-resolver.js.map +1 -0
  7. package/dist/commands.d.ts +35 -0
  8. package/dist/commands.js +976 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/doc-cache.d.ts +29 -0
  11. package/dist/doc-cache.js +50 -0
  12. package/dist/doc-cache.js.map +1 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.js +4534 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/markdown.d.ts +46 -0
  17. package/dist/markdown.js +1553 -0
  18. package/dist/markdown.js.map +1 -0
  19. package/dist/plugins/code-block-view.d.ts +52 -0
  20. package/dist/plugins/code-block-view.js +686 -0
  21. package/dist/plugins/code-block-view.js.map +1 -0
  22. package/dist/plugins/cursor-syntax.d.ts +27 -0
  23. package/dist/plugins/cursor-syntax.js +122 -0
  24. package/dist/plugins/cursor-syntax.js.map +1 -0
  25. package/dist/plugins/definition-list.d.ts +23 -0
  26. package/dist/plugins/definition-list.js +12 -0
  27. package/dist/plugins/definition-list.js.map +1 -0
  28. package/dist/plugins/editor-props-plugin.d.ts +36 -0
  29. package/dist/plugins/editor-props-plugin.js +1963 -0
  30. package/dist/plugins/editor-props-plugin.js.map +1 -0
  31. package/dist/plugins/emoji.d.ts +21 -0
  32. package/dist/plugins/emoji.js +42 -0
  33. package/dist/plugins/emoji.js.map +1 -0
  34. package/dist/plugins/enter-handler.d.ts +26 -0
  35. package/dist/plugins/enter-handler.js +193 -0
  36. package/dist/plugins/enter-handler.js.map +1 -0
  37. package/dist/plugins/highlight.d.ts +39 -0
  38. package/dist/plugins/highlight.js +283 -0
  39. package/dist/plugins/highlight.js.map +1 -0
  40. package/dist/plugins/inline-code-convert.d.ts +32 -0
  41. package/dist/plugins/inline-code-convert.js +173 -0
  42. package/dist/plugins/inline-code-convert.js.map +1 -0
  43. package/dist/plugins/link-text-plugin.d.ts +22 -0
  44. package/dist/plugins/link-text-plugin.js +194 -0
  45. package/dist/plugins/link-text-plugin.js.map +1 -0
  46. package/dist/plugins/mermaid-renderer.d.ts +24 -0
  47. package/dist/plugins/mermaid-renderer.js +80 -0
  48. package/dist/plugins/mermaid-renderer.js.map +1 -0
  49. package/dist/schema.d.ts +48 -0
  50. package/dist/schema.js +847 -0
  51. package/dist/schema.js.map +1 -0
  52. package/dist/setup.d.ts +104 -0
  53. package/dist/setup.js +4393 -0
  54. package/dist/setup.js.map +1 -0
  55. package/dist/types.d.ts +107 -0
  56. package/dist/types.js +10 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +121 -0
@@ -0,0 +1,193 @@
1
+ // src/plugins/enter-handler.ts
2
+ import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
3
+ import {
4
+ splitBlock,
5
+ chainCommands,
6
+ newlineInCode,
7
+ createParagraphNear,
8
+ liftEmptyBlock
9
+ } from "prosemirror-commands";
10
+ import { addRowAfter } from "prosemirror-tables";
11
+ var enterHandlerKey = new PluginKey("moraya-enter-handler");
12
+ function parsePipeTableHeader(text) {
13
+ if (!/^\|(.+\|)+\s*$/.test(text)) return null;
14
+ const cells = text.split("|").slice(1, -1).map((s) => s.trim());
15
+ if (cells.length < 2) return null;
16
+ if (cells.every((c) => /^:?-+:?$/.test(c))) return null;
17
+ return cells;
18
+ }
19
+ function buildTableFromHeaders(schema, headers) {
20
+ const tableType = schema.nodes.table;
21
+ const headerRowType = schema.nodes.table_header_row;
22
+ const dataRowType = schema.nodes.table_row;
23
+ const headerCellType = schema.nodes.table_header;
24
+ const dataCellType = schema.nodes.table_cell;
25
+ const paragraphType = schema.nodes.paragraph;
26
+ if (!tableType || !headerRowType || !dataRowType || !headerCellType || !dataCellType || !paragraphType) {
27
+ return null;
28
+ }
29
+ const headerCells = headers.map((text) => {
30
+ const para = text ? paragraphType.create(null, schema.text(text)) : paragraphType.create();
31
+ return headerCellType.create({ alignment: "left" }, para);
32
+ });
33
+ const emptyCells = headers.map(
34
+ () => dataCellType.createAndFill({ alignment: "left" })
35
+ );
36
+ const headerRow = headerRowType.create(null, headerCells);
37
+ const dataRow = dataRowType.create(null, emptyCells);
38
+ return tableType.create(null, [headerRow, dataRow]);
39
+ }
40
+ function createEnterHandlerPlugin() {
41
+ const enterCommand = chainCommands(
42
+ newlineInCode,
43
+ createParagraphNear,
44
+ liftEmptyBlock,
45
+ splitBlock
46
+ );
47
+ return new Plugin({
48
+ key: enterHandlerKey,
49
+ props: {
50
+ handleKeyDown(view, event) {
51
+ if (event.isComposing || event.key !== "Enter") return false;
52
+ const { $from } = view.state.selection;
53
+ let inTable = false;
54
+ let cellDepth = -1;
55
+ let inListItem = false;
56
+ for (let d = $from.depth; d > 0; d--) {
57
+ const nodeName = $from.node(d).type.name;
58
+ if (nodeName === "table_cell" || nodeName === "table_header") {
59
+ inTable = true;
60
+ cellDepth = d;
61
+ break;
62
+ }
63
+ if (nodeName === "list_item") {
64
+ inListItem = true;
65
+ break;
66
+ }
67
+ }
68
+ if (inTable) {
69
+ if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {
70
+ event.preventDefault();
71
+ addRowAfter(view.state, view.dispatch);
72
+ const { $from: $cur } = view.state.selection;
73
+ for (let d = $cur.depth; d > 0; d--) {
74
+ const name = $cur.node(d).type.name;
75
+ if (name === "table_row" || name === "table_header_row") {
76
+ try {
77
+ const rowEnd = $cur.after(d);
78
+ const $newRow = view.state.doc.resolve(rowEnd + 1);
79
+ view.dispatch(
80
+ view.state.tr.setSelection(TextSelection.near($newRow)).scrollIntoView()
81
+ );
82
+ } catch {
83
+ }
84
+ break;
85
+ }
86
+ }
87
+ return true;
88
+ }
89
+ if (event.shiftKey && !event.ctrlKey && !event.metaKey) {
90
+ event.preventDefault();
91
+ const hardbreak = view.state.schema.nodes.hardbreak;
92
+ if (hardbreak) {
93
+ const tr = view.state.tr.replaceSelectionWith(hardbreak.create({ isInline: false }));
94
+ view.dispatch(tr.scrollIntoView());
95
+ }
96
+ return true;
97
+ }
98
+ if (!event.shiftKey && !event.ctrlKey && !event.metaKey) {
99
+ event.preventDefault();
100
+ if (cellDepth < 2) return true;
101
+ const rowDepth = cellDepth - 1;
102
+ const tableDepth = cellDepth - 2;
103
+ const colIndex = $from.index(rowDepth);
104
+ const rowIndex = $from.index(tableDepth);
105
+ const tableNode = $from.node(tableDepth);
106
+ const tableStart = $from.start(tableDepth);
107
+ if (rowIndex === tableNode.childCount - 1) {
108
+ const tableEnd = $from.after(tableDepth);
109
+ const afterNode = view.state.doc.nodeAt(tableEnd);
110
+ if (afterNode) {
111
+ const $target = view.state.doc.resolve(tableEnd + 1);
112
+ view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView());
113
+ } else {
114
+ const paragraph = view.state.schema.nodes.paragraph;
115
+ if (paragraph) {
116
+ const tr = view.state.tr.insert(tableEnd, paragraph.create());
117
+ const $target = tr.doc.resolve(tableEnd + 1);
118
+ tr.setSelection(TextSelection.near($target));
119
+ view.dispatch(tr.scrollIntoView());
120
+ }
121
+ }
122
+ } else {
123
+ const nextRow = tableNode.child(rowIndex + 1);
124
+ const safeCol = Math.min(colIndex, nextRow.childCount - 1);
125
+ let targetPos = tableStart;
126
+ for (let r = 0; r <= rowIndex; r++) {
127
+ targetPos += tableNode.child(r).nodeSize;
128
+ }
129
+ targetPos += 1;
130
+ for (let c = 0; c < safeCol; c++) {
131
+ targetPos += nextRow.child(c).nodeSize;
132
+ }
133
+ targetPos += 1;
134
+ const $target = view.state.doc.resolve(targetPos);
135
+ view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView());
136
+ }
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ if (inListItem) return false;
142
+ if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {
143
+ if ($from.parent.type.name === "paragraph") {
144
+ const text = $from.parent.textContent;
145
+ const match = $from.parentOffset === text.length ? text.match(/^```(\S*)\s*$/) : null;
146
+ if (match) {
147
+ const language = match[1] ?? "";
148
+ const codeBlockType = view.state.schema.nodes.code_block;
149
+ if (codeBlockType) {
150
+ const pos = $from.before();
151
+ const end = $from.after();
152
+ const tr = view.state.tr;
153
+ tr.replaceWith(pos, end, codeBlockType.create({ language }));
154
+ view.dispatch(tr);
155
+ return true;
156
+ }
157
+ }
158
+ const headers = $from.parentOffset === text.length ? parsePipeTableHeader(text) : null;
159
+ if (headers) {
160
+ const $para = view.state.doc.resolve($from.before());
161
+ const parentNode = $para.node($para.depth);
162
+ const tableType = view.state.schema.nodes.table;
163
+ if (tableType && parentNode.type.contentMatch.matchType(tableType)) {
164
+ const tableNode = buildTableFromHeaders(view.state.schema, headers);
165
+ if (tableNode) {
166
+ const pos = $from.before();
167
+ const end = $from.after();
168
+ const tr = view.state.tr;
169
+ tr.replaceWith(pos, end, tableNode);
170
+ const inserted = tr.doc.nodeAt(pos);
171
+ if (inserted && inserted.childCount >= 2) {
172
+ const headerRowSize = inserted.child(0).nodeSize;
173
+ const $dataRow = tr.doc.resolve(pos + 1 + headerRowSize + 1);
174
+ tr.setSelection(TextSelection.near($dataRow));
175
+ }
176
+ tr.scrollIntoView();
177
+ view.dispatch(tr);
178
+ return true;
179
+ }
180
+ }
181
+ }
182
+ }
183
+ return enterCommand(view.state, view.dispatch, view);
184
+ }
185
+ return false;
186
+ }
187
+ }
188
+ });
189
+ }
190
+ export {
191
+ createEnterHandlerPlugin
192
+ };
193
+ //# sourceMappingURL=enter-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/enter-handler.ts"],"sourcesContent":["/**\n * Enter key handler — unified plugin for all Enter-key variants.\n *\n * In table cells:\n * - `Ctrl/Cmd+Enter` → add a new row below\n * - `Shift+Enter` → insert hard break (`<br>`)\n * - Plain `Enter` → move to same column in next row; exit table from last row\n *\n * In paragraphs:\n * - `Enter` → split the current block into a new paragraph (no `<br/>`).\n * - `Enter` after ` ``` ` or ` ```language ` → create code block.\n * - `Enter` after `| col1 | col2 |` → create GFM table with header + empty data row.\n *\n * Uses `handleKeyDown` (props-level) which has higher priority than keymaps,\n * ensuring this runs before the base keymap's hardbreak / splitBlock handlers.\n */\n\nimport { Plugin, PluginKey, TextSelection } from 'prosemirror-state'\nimport type { Schema, Node as PmNode } from 'prosemirror-model'\nimport {\n splitBlock,\n chainCommands,\n newlineInCode,\n createParagraphNear,\n liftEmptyBlock,\n} from 'prosemirror-commands'\nimport { addRowAfter } from 'prosemirror-tables'\n\nconst enterHandlerKey = new PluginKey('moraya-enter-handler')\n\n/**\n * Parse a pipe-delimited table header line into cell texts.\n * Returns null if the text is not a valid table header (needs >= 2 columns,\n * must start/end with |, rejects separator-only rows like `| --- | --- |`).\n */\nfunction parsePipeTableHeader(text: string): string[] | null {\n if (!/^\\|(.+\\|)+\\s*$/.test(text)) return null\n\n const cells = text.split('|').slice(1, -1).map(s => s.trim())\n if (cells.length < 2) return null\n\n // Reject separator-only rows (e.g. | --- | :---: | ---: |)\n if (cells.every(c => /^:?-+:?$/.test(c))) return null\n\n return cells\n}\n\n/**\n * Build a GFM table node from header text values using the supplied schema.\n * Creates: header row (pre-filled) + one empty data row.\n */\nfunction buildTableFromHeaders(schema: Schema, headers: string[]): PmNode | null {\n const tableType = schema.nodes.table\n const headerRowType = schema.nodes.table_header_row\n const dataRowType = schema.nodes.table_row\n const headerCellType = schema.nodes.table_header\n const dataCellType = schema.nodes.table_cell\n const paragraphType = schema.nodes.paragraph\n\n if (!tableType || !headerRowType || !dataRowType ||\n !headerCellType || !dataCellType || !paragraphType) {\n return null\n }\n\n const headerCells = headers.map(text => {\n const para = text\n ? paragraphType.create(null, schema.text(text))\n : paragraphType.create()\n return headerCellType.create({ alignment: 'left' }, para)\n })\n\n const emptyCells = headers.map(() =>\n dataCellType.createAndFill({ alignment: 'left' })!,\n )\n\n const headerRow = headerRowType.create(null, headerCells)\n const dataRow = dataRowType.create(null, emptyCells)\n\n return tableType.create(null, [headerRow, dataRow])\n}\n\n/**\n * Enter handler plugin. Operates entirely off `view.state.schema`, so it works\n * against any consumer-injected schema produced by `createSchema(config)`.\n */\nexport function createEnterHandlerPlugin(): Plugin {\n const enterCommand = chainCommands(\n newlineInCode,\n createParagraphNear,\n liftEmptyBlock,\n splitBlock,\n )\n\n return new Plugin({\n key: enterHandlerKey,\n props: {\n handleKeyDown(view, event) {\n if (event.isComposing || event.key !== 'Enter') return false\n\n const { $from } = view.state.selection\n\n // Single depth traversal to determine context: table cell or list item\n let inTable = false\n let cellDepth = -1\n let inListItem = false\n for (let d = $from.depth; d > 0; d--) {\n const nodeName = $from.node(d).type.name\n if (nodeName === 'table_cell' || nodeName === 'table_header') {\n inTable = true\n cellDepth = d\n break\n }\n if (nodeName === 'list_item') {\n inListItem = true\n break\n }\n }\n\n // ── Table cell ──\n if (inTable) {\n // Ctrl/Cmd+Enter → add row after and move cursor there\n if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {\n event.preventDefault()\n addRowAfter(view.state, view.dispatch)\n\n const { $from: $cur } = view.state.selection\n for (let d = $cur.depth; d > 0; d--) {\n const name = $cur.node(d).type.name\n if (name === 'table_row' || name === 'table_header_row') {\n try {\n const rowEnd = $cur.after(d)\n const $newRow = view.state.doc.resolve(rowEnd + 1)\n view.dispatch(\n view.state.tr.setSelection(TextSelection.near($newRow)).scrollIntoView(),\n )\n } catch { /* new row at table boundary */ }\n break\n }\n }\n return true\n }\n\n // Shift+Enter → insert hard break\n if (event.shiftKey && !event.ctrlKey && !event.metaKey) {\n event.preventDefault()\n const hardbreak = view.state.schema.nodes.hardbreak\n if (hardbreak) {\n const tr = view.state.tr.replaceSelectionWith(hardbreak.create({ isInline: false }))\n view.dispatch(tr.scrollIntoView())\n }\n return true\n }\n\n // Plain Enter → move to same column in next row; exit table from last row\n if (!event.shiftKey && !event.ctrlKey && !event.metaKey) {\n event.preventDefault()\n\n if (cellDepth < 2) return true // safety guard\n\n const rowDepth = cellDepth - 1\n const tableDepth = cellDepth - 2\n const colIndex = $from.index(rowDepth)\n const rowIndex = $from.index(tableDepth)\n const tableNode = $from.node(tableDepth)\n const tableStart = $from.start(tableDepth)\n\n if (rowIndex === tableNode.childCount - 1) {\n // Last row → exit table: move to next block, or insert paragraph\n const tableEnd = $from.after(tableDepth)\n const afterNode = view.state.doc.nodeAt(tableEnd)\n if (afterNode) {\n const $target = view.state.doc.resolve(tableEnd + 1)\n view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView())\n } else {\n const paragraph = view.state.schema.nodes.paragraph\n if (paragraph) {\n const tr = view.state.tr.insert(tableEnd, paragraph.create())\n const $target = tr.doc.resolve(tableEnd + 1)\n tr.setSelection(TextSelection.near($target))\n view.dispatch(tr.scrollIntoView())\n }\n }\n } else {\n // Move to same column in next row\n const nextRow = tableNode.child(rowIndex + 1)\n const safeCol = Math.min(colIndex, nextRow.childCount - 1)\n let targetPos = tableStart\n for (let r = 0; r <= rowIndex; r++) {\n targetPos += tableNode.child(r).nodeSize\n }\n targetPos += 1 // enter next row\n for (let c = 0; c < safeCol; c++) {\n targetPos += nextRow.child(c).nodeSize\n }\n targetPos += 1 // enter target cell\n const $target = view.state.doc.resolve(targetPos)\n view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView())\n }\n return true\n }\n\n return false\n }\n\n // ── List item: let prosemirror-schema-list keymap handle splitListItem ──\n if (inListItem) return false\n\n // ── Plain Enter (no modifiers) in non-table, non-list context ──\n if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {\n // Check if current line is a code fence (```language)\n if ($from.parent.type.name === 'paragraph') {\n const text = $from.parent.textContent\n\n // ```language or bare ``` creates a code block.\n // Guard: cursor must be at end of paragraph (user finished typing the fence).\n const match = $from.parentOffset === text.length\n ? text.match(/^```(\\S*)\\s*$/)\n : null\n if (match) {\n const language = match[1] ?? ''\n const codeBlockType = view.state.schema.nodes.code_block\n if (codeBlockType) {\n const pos = $from.before()\n const end = $from.after()\n const tr = view.state.tr\n tr.replaceWith(pos, end, codeBlockType.create({ language }))\n view.dispatch(tr)\n return true\n }\n }\n\n // Pipe-separated table header: | col1 | col2 | ... |\n // Only trigger when cursor is at the END of the paragraph (same guard\n // as the code-fence check above). If the cursor is mid-line the user\n // is editing the header text, not finishing it — fall through to splitBlock.\n const headers = $from.parentOffset === text.length\n ? parsePipeTableHeader(text)\n : null\n if (headers) {\n // Ensure the parent context allows a table node (not inside blockquote/table cell)\n const $para = view.state.doc.resolve($from.before())\n const parentNode = $para.node($para.depth)\n const tableType = view.state.schema.nodes.table\n if (tableType && parentNode.type.contentMatch.matchType(tableType)) {\n const tableNode = buildTableFromHeaders(view.state.schema, headers)\n if (tableNode) {\n const pos = $from.before()\n const end = $from.after()\n const tr = view.state.tr\n tr.replaceWith(pos, end, tableNode)\n\n // Place cursor in first cell of the data row\n const inserted = tr.doc.nodeAt(pos)\n if (inserted && inserted.childCount >= 2) {\n const headerRowSize = inserted.child(0).nodeSize\n const $dataRow = tr.doc.resolve(pos + 1 + headerRowSize + 1)\n tr.setSelection(TextSelection.near($dataRow))\n }\n tr.scrollIntoView()\n view.dispatch(tr)\n return true\n }\n }\n }\n }\n return enterCommand(view.state, view.dispatch, view)\n }\n\n return false\n },\n },\n })\n}\n"],"mappings":";AAiBA,SAAS,QAAQ,WAAW,qBAAqB;AAEjD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mBAAmB;AAE5B,IAAM,kBAAkB,IAAI,UAAU,sBAAsB;AAO5D,SAAS,qBAAqB,MAA+B;AAC3D,MAAI,CAAC,iBAAiB,KAAK,IAAI,EAAG,QAAO;AAEzC,QAAM,QAAQ,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAC5D,MAAI,MAAM,SAAS,EAAG,QAAO;AAG7B,MAAI,MAAM,MAAM,OAAK,WAAW,KAAK,CAAC,CAAC,EAAG,QAAO;AAEjD,SAAO;AACT;AAMA,SAAS,sBAAsB,QAAgB,SAAkC;AAC/E,QAAM,YAAY,OAAO,MAAM;AAC/B,QAAM,gBAAgB,OAAO,MAAM;AACnC,QAAM,cAAc,OAAO,MAAM;AACjC,QAAM,iBAAiB,OAAO,MAAM;AACpC,QAAM,eAAe,OAAO,MAAM;AAClC,QAAM,gBAAgB,OAAO,MAAM;AAEnC,MAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,eACjC,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,eAAe;AACtD,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,QAAQ,IAAI,UAAQ;AACtC,UAAM,OAAO,OACT,cAAc,OAAO,MAAM,OAAO,KAAK,IAAI,CAAC,IAC5C,cAAc,OAAO;AACzB,WAAO,eAAe,OAAO,EAAE,WAAW,OAAO,GAAG,IAAI;AAAA,EAC1D,CAAC;AAED,QAAM,aAAa,QAAQ;AAAA,IAAI,MAC7B,aAAa,cAAc,EAAE,WAAW,OAAO,CAAC;AAAA,EAClD;AAEA,QAAM,YAAY,cAAc,OAAO,MAAM,WAAW;AACxD,QAAM,UAAU,YAAY,OAAO,MAAM,UAAU;AAEnD,SAAO,UAAU,OAAO,MAAM,CAAC,WAAW,OAAO,CAAC;AACpD;AAMO,SAAS,2BAAmC;AACjD,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,IAAI,OAAO;AAAA,IAChB,KAAK;AAAA,IACL,OAAO;AAAA,MACL,cAAc,MAAM,OAAO;AACzB,YAAI,MAAM,eAAe,MAAM,QAAQ,QAAS,QAAO;AAEvD,cAAM,EAAE,MAAM,IAAI,KAAK,MAAM;AAG7B,YAAI,UAAU;AACd,YAAI,YAAY;AAChB,YAAI,aAAa;AACjB,iBAAS,IAAI,MAAM,OAAO,IAAI,GAAG,KAAK;AACpC,gBAAM,WAAW,MAAM,KAAK,CAAC,EAAE,KAAK;AACpC,cAAI,aAAa,gBAAgB,aAAa,gBAAgB;AAC5D,sBAAU;AACV,wBAAY;AACZ;AAAA,UACF;AACA,cAAI,aAAa,aAAa;AAC5B,yBAAa;AACb;AAAA,UACF;AAAA,QACF;AAGA,YAAI,SAAS;AAEX,eAAK,MAAM,WAAW,MAAM,YAAY,CAAC,MAAM,UAAU;AACvD,kBAAM,eAAe;AACrB,wBAAY,KAAK,OAAO,KAAK,QAAQ;AAErC,kBAAM,EAAE,OAAO,KAAK,IAAI,KAAK,MAAM;AACnC,qBAAS,IAAI,KAAK,OAAO,IAAI,GAAG,KAAK;AACnC,oBAAM,OAAO,KAAK,KAAK,CAAC,EAAE,KAAK;AAC/B,kBAAI,SAAS,eAAe,SAAS,oBAAoB;AACvD,oBAAI;AACF,wBAAM,SAAS,KAAK,MAAM,CAAC;AAC3B,wBAAM,UAAU,KAAK,MAAM,IAAI,QAAQ,SAAS,CAAC;AACjD,uBAAK;AAAA,oBACH,KAAK,MAAM,GAAG,aAAa,cAAc,KAAK,OAAO,CAAC,EAAE,eAAe;AAAA,kBACzE;AAAA,gBACF,QAAQ;AAAA,gBAAkC;AAC1C;AAAA,cACF;AAAA,YACF;AACA,mBAAO;AAAA,UACT;AAGA,cAAI,MAAM,YAAY,CAAC,MAAM,WAAW,CAAC,MAAM,SAAS;AACtD,kBAAM,eAAe;AACrB,kBAAM,YAAY,KAAK,MAAM,OAAO,MAAM;AAC1C,gBAAI,WAAW;AACb,oBAAM,KAAK,KAAK,MAAM,GAAG,qBAAqB,UAAU,OAAO,EAAE,UAAU,MAAM,CAAC,CAAC;AACnF,mBAAK,SAAS,GAAG,eAAe,CAAC;AAAA,YACnC;AACA,mBAAO;AAAA,UACT;AAGA,cAAI,CAAC,MAAM,YAAY,CAAC,MAAM,WAAW,CAAC,MAAM,SAAS;AACvD,kBAAM,eAAe;AAErB,gBAAI,YAAY,EAAG,QAAO;AAE1B,kBAAM,WAAa,YAAY;AAC/B,kBAAM,aAAa,YAAY;AAC/B,kBAAM,WAAa,MAAM,MAAM,QAAQ;AACvC,kBAAM,WAAa,MAAM,MAAM,UAAU;AACzC,kBAAM,YAAa,MAAM,KAAK,UAAU;AACxC,kBAAM,aAAa,MAAM,MAAM,UAAU;AAEzC,gBAAI,aAAa,UAAU,aAAa,GAAG;AAEzC,oBAAM,WAAW,MAAM,MAAM,UAAU;AACvC,oBAAM,YAAY,KAAK,MAAM,IAAI,OAAO,QAAQ;AAChD,kBAAI,WAAW;AACb,sBAAM,UAAU,KAAK,MAAM,IAAI,QAAQ,WAAW,CAAC;AACnD,qBAAK,SAAS,KAAK,MAAM,GAAG,aAAa,cAAc,KAAK,OAAO,CAAC,EAAE,eAAe,CAAC;AAAA,cACxF,OAAO;AACL,sBAAM,YAAY,KAAK,MAAM,OAAO,MAAM;AAC1C,oBAAI,WAAW;AACb,wBAAM,KAAK,KAAK,MAAM,GAAG,OAAO,UAAU,UAAU,OAAO,CAAC;AAC5D,wBAAM,UAAU,GAAG,IAAI,QAAQ,WAAW,CAAC;AAC3C,qBAAG,aAAa,cAAc,KAAK,OAAO,CAAC;AAC3C,uBAAK,SAAS,GAAG,eAAe,CAAC;AAAA,gBACnC;AAAA,cACF;AAAA,YACF,OAAO;AAEL,oBAAM,UAAU,UAAU,MAAM,WAAW,CAAC;AAC5C,oBAAM,UAAU,KAAK,IAAI,UAAU,QAAQ,aAAa,CAAC;AACzD,kBAAI,YAAY;AAChB,uBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,6BAAa,UAAU,MAAM,CAAC,EAAE;AAAA,cAClC;AACA,2BAAa;AACb,uBAAS,IAAI,GAAG,IAAI,SAAS,KAAK;AAChC,6BAAa,QAAQ,MAAM,CAAC,EAAE;AAAA,cAChC;AACA,2BAAa;AACb,oBAAM,UAAU,KAAK,MAAM,IAAI,QAAQ,SAAS;AAChD,mBAAK,SAAS,KAAK,MAAM,GAAG,aAAa,cAAc,KAAK,OAAO,CAAC,EAAE,eAAe,CAAC;AAAA,YACxF;AACA,mBAAO;AAAA,UACT;AAEA,iBAAO;AAAA,QACT;AAGA,YAAI,WAAY,QAAO;AAGvB,YAAI,CAAC,MAAM,YAAY,CAAC,MAAM,WAAW,CAAC,MAAM,SAAS;AAEvD,cAAI,MAAM,OAAO,KAAK,SAAS,aAAa;AAC1C,kBAAM,OAAO,MAAM,OAAO;AAI1B,kBAAM,QAAQ,MAAM,iBAAiB,KAAK,SACtC,KAAK,MAAM,eAAe,IAC1B;AACJ,gBAAI,OAAO;AACT,oBAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,oBAAM,gBAAgB,KAAK,MAAM,OAAO,MAAM;AAC9C,kBAAI,eAAe;AACjB,sBAAM,MAAM,MAAM,OAAO;AACzB,sBAAM,MAAM,MAAM,MAAM;AACxB,sBAAM,KAAK,KAAK,MAAM;AACtB,mBAAG,YAAY,KAAK,KAAK,cAAc,OAAO,EAAE,SAAS,CAAC,CAAC;AAC3D,qBAAK,SAAS,EAAE;AAChB,uBAAO;AAAA,cACT;AAAA,YACF;AAMA,kBAAM,UAAU,MAAM,iBAAiB,KAAK,SACxC,qBAAqB,IAAI,IACzB;AACJ,gBAAI,SAAS;AAEX,oBAAM,QAAQ,KAAK,MAAM,IAAI,QAAQ,MAAM,OAAO,CAAC;AACnD,oBAAM,aAAa,MAAM,KAAK,MAAM,KAAK;AACzC,oBAAM,YAAY,KAAK,MAAM,OAAO,MAAM;AAC1C,kBAAI,aAAa,WAAW,KAAK,aAAa,UAAU,SAAS,GAAG;AAClE,sBAAM,YAAY,sBAAsB,KAAK,MAAM,QAAQ,OAAO;AAClE,oBAAI,WAAW;AACb,wBAAM,MAAM,MAAM,OAAO;AACzB,wBAAM,MAAM,MAAM,MAAM;AACxB,wBAAM,KAAK,KAAK,MAAM;AACtB,qBAAG,YAAY,KAAK,KAAK,SAAS;AAGlC,wBAAM,WAAW,GAAG,IAAI,OAAO,GAAG;AAClC,sBAAI,YAAY,SAAS,cAAc,GAAG;AACxC,0BAAM,gBAAgB,SAAS,MAAM,CAAC,EAAE;AACxC,0BAAM,WAAW,GAAG,IAAI,QAAQ,MAAM,IAAI,gBAAgB,CAAC;AAC3D,uBAAG,aAAa,cAAc,KAAK,QAAQ,CAAC;AAAA,kBAC9C;AACA,qBAAG,eAAe;AAClB,uBAAK,SAAS,EAAE;AAChB,yBAAO;AAAA,gBACT;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA,iBAAO,aAAa,KAAK,OAAO,KAAK,UAAU,IAAI;AAAA,QACrD;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,39 @@
1
+ import { Plugin } from 'prosemirror-state';
2
+
3
+ /**
4
+ * Syntax highlighting plugin for `code_block` nodes.
5
+ *
6
+ * Faithful 1:1 migration from Moraya desktop `src/lib/editor/plugins/highlight.ts`
7
+ * (replaces the prior no-op stub). Applies ProseMirror Decoration spans with
8
+ * `hljs-*` CSS classes. Schema-agnostic via `state.schema` lookups.
9
+ *
10
+ * Performance contract (Moraya CLAUDE.md "Performance Coding Standards" §6):
11
+ * - Per-block cache keyed by `(language, code)` — switching back to a
12
+ * previously highlighted file skips all hljs calls.
13
+ * - On doc change: cheap `decorationSet.map(tr.mapping)` keeps positions in
14
+ * sync without re-highlighting (no hljs calls in the hot path).
15
+ * - After 300 ms idle: full re-highlight via metadata-only transaction,
16
+ * re-using the per-block cache where possible.
17
+ * - File-switch path (`tr.getMeta('file-switch')`): rebuild from scratch.
18
+ * - Full-delete path (`tr.getMeta('full-delete')`): rebuild on the tiny doc.
19
+ *
20
+ * Tier 1 lazy load: this module bundles hljs + 39 language definitions
21
+ * (~250 KB minified). It is loaded via `dynamic import()` so it forms a
22
+ * separate Vite/Rollup chunk and only ships to consumers that actually use
23
+ * code blocks.
24
+ */
25
+
26
+ /**
27
+ * Plugin that adds syntax highlighting to code blocks.
28
+ *
29
+ * highlight.js is expensive (especially `highlightAuto` which tests all
30
+ * registered languages). Strategy:
31
+ * 1. On doc change: cheaply map existing decorations through the transaction
32
+ * (adjust positions for inserts/deletes — no hljs calls).
33
+ * 2. After 300 ms idle: run a full re-highlight and dispatch a metadata-only
34
+ * transaction to flush new decorations into the view.
35
+ * 3. File-switch / full-delete metas: rebuild from scratch.
36
+ */
37
+ declare function createHighlightPlugin(): Plugin;
38
+
39
+ export { createHighlightPlugin };
@@ -0,0 +1,283 @@
1
+ // src/plugins/highlight.ts
2
+ import { Plugin, PluginKey } from "prosemirror-state";
3
+ import { Decoration, DecorationSet } from "prosemirror-view";
4
+ import hljs from "highlight.js/lib/core";
5
+ import javascript from "highlight.js/lib/languages/javascript";
6
+ import typescript from "highlight.js/lib/languages/typescript";
7
+ import python from "highlight.js/lib/languages/python";
8
+ import rust from "highlight.js/lib/languages/rust";
9
+ import css from "highlight.js/lib/languages/css";
10
+ import xml from "highlight.js/lib/languages/xml";
11
+ import json from "highlight.js/lib/languages/json";
12
+ import bash from "highlight.js/lib/languages/bash";
13
+ import sql from "highlight.js/lib/languages/sql";
14
+ import java from "highlight.js/lib/languages/java";
15
+ import cpp from "highlight.js/lib/languages/cpp";
16
+ import c from "highlight.js/lib/languages/c";
17
+ import go from "highlight.js/lib/languages/go";
18
+ import ruby from "highlight.js/lib/languages/ruby";
19
+ import php from "highlight.js/lib/languages/php";
20
+ import swift from "highlight.js/lib/languages/swift";
21
+ import kotlin from "highlight.js/lib/languages/kotlin";
22
+ import yaml from "highlight.js/lib/languages/yaml";
23
+ import markdown from "highlight.js/lib/languages/markdown";
24
+ import diff from "highlight.js/lib/languages/diff";
25
+ import lua from "highlight.js/lib/languages/lua";
26
+ import scss from "highlight.js/lib/languages/scss";
27
+ import csharp from "highlight.js/lib/languages/csharp";
28
+ import dart from "highlight.js/lib/languages/dart";
29
+ import r from "highlight.js/lib/languages/r";
30
+ import perl from "highlight.js/lib/languages/perl";
31
+ import scala from "highlight.js/lib/languages/scala";
32
+ import objectivec from "highlight.js/lib/languages/objectivec";
33
+ import dockerfile from "highlight.js/lib/languages/dockerfile";
34
+ import ini from "highlight.js/lib/languages/ini";
35
+ import powershell from "highlight.js/lib/languages/powershell";
36
+ import makefile from "highlight.js/lib/languages/makefile";
37
+ import groovy from "highlight.js/lib/languages/groovy";
38
+ import elixir from "highlight.js/lib/languages/elixir";
39
+ import haskell from "highlight.js/lib/languages/haskell";
40
+ import protobuf from "highlight.js/lib/languages/protobuf";
41
+ import graphql from "highlight.js/lib/languages/graphql";
42
+ import latex from "highlight.js/lib/languages/latex";
43
+ import nginx from "highlight.js/lib/languages/nginx";
44
+ import shell from "highlight.js/lib/languages/shell";
45
+ hljs.registerLanguage("javascript", javascript);
46
+ hljs.registerLanguage("js", javascript);
47
+ hljs.registerLanguage("typescript", typescript);
48
+ hljs.registerLanguage("ts", typescript);
49
+ hljs.registerLanguage("python", python);
50
+ hljs.registerLanguage("py", python);
51
+ hljs.registerLanguage("rust", rust);
52
+ hljs.registerLanguage("rs", rust);
53
+ hljs.registerLanguage("css", css);
54
+ hljs.registerLanguage("html", xml);
55
+ hljs.registerLanguage("xml", xml);
56
+ hljs.registerLanguage("json", json);
57
+ hljs.registerLanguage("bash", bash);
58
+ hljs.registerLanguage("sh", bash);
59
+ hljs.registerLanguage("sql", sql);
60
+ hljs.registerLanguage("java", java);
61
+ hljs.registerLanguage("cpp", cpp);
62
+ hljs.registerLanguage("c", c);
63
+ hljs.registerLanguage("go", go);
64
+ hljs.registerLanguage("ruby", ruby);
65
+ hljs.registerLanguage("rb", ruby);
66
+ hljs.registerLanguage("php", php);
67
+ hljs.registerLanguage("swift", swift);
68
+ hljs.registerLanguage("kotlin", kotlin);
69
+ hljs.registerLanguage("kt", kotlin);
70
+ hljs.registerLanguage("yaml", yaml);
71
+ hljs.registerLanguage("yml", yaml);
72
+ hljs.registerLanguage("markdown", markdown);
73
+ hljs.registerLanguage("md", markdown);
74
+ hljs.registerLanguage("diff", diff);
75
+ hljs.registerLanguage("lua", lua);
76
+ hljs.registerLanguage("scss", scss);
77
+ hljs.registerLanguage("svelte", xml);
78
+ hljs.registerLanguage("jsx", javascript);
79
+ hljs.registerLanguage("tsx", typescript);
80
+ hljs.registerLanguage("csharp", csharp);
81
+ hljs.registerLanguage("cs", csharp);
82
+ hljs.registerLanguage("dart", dart);
83
+ hljs.registerLanguage("r", r);
84
+ hljs.registerLanguage("perl", perl);
85
+ hljs.registerLanguage("pl", perl);
86
+ hljs.registerLanguage("scala", scala);
87
+ hljs.registerLanguage("objectivec", objectivec);
88
+ hljs.registerLanguage("objc", objectivec);
89
+ hljs.registerLanguage("dockerfile", dockerfile);
90
+ hljs.registerLanguage("docker", dockerfile);
91
+ hljs.registerLanguage("ini", ini);
92
+ hljs.registerLanguage("toml", ini);
93
+ hljs.registerLanguage("powershell", powershell);
94
+ hljs.registerLanguage("ps", powershell);
95
+ hljs.registerLanguage("ps1", powershell);
96
+ hljs.registerLanguage("makefile", makefile);
97
+ hljs.registerLanguage("make", makefile);
98
+ hljs.registerLanguage("groovy", groovy);
99
+ hljs.registerLanguage("elixir", elixir);
100
+ hljs.registerLanguage("ex", elixir);
101
+ hljs.registerLanguage("haskell", haskell);
102
+ hljs.registerLanguage("hs", haskell);
103
+ hljs.registerLanguage("protobuf", protobuf);
104
+ hljs.registerLanguage("proto", protobuf);
105
+ hljs.registerLanguage("graphql", graphql);
106
+ hljs.registerLanguage("gql", graphql);
107
+ hljs.registerLanguage("latex", latex);
108
+ hljs.registerLanguage("tex", latex);
109
+ hljs.registerLanguage("nginx", nginx);
110
+ hljs.registerLanguage("nginxconf", nginx);
111
+ hljs.registerLanguage("shell", shell);
112
+ var highlightPluginKey = new PluginKey("moraya-syntax-highlight");
113
+ function scopeToClasses(scope) {
114
+ const parts = scope.split(".");
115
+ const classes = [`hljs-${parts[0]}`];
116
+ for (let i = 1; i < parts.length; i++) {
117
+ classes.push(`${parts[i]}_`);
118
+ }
119
+ return classes;
120
+ }
121
+ function flattenHljsTree(nodes, parentClasses = []) {
122
+ const result = [];
123
+ for (const node of nodes) {
124
+ if (typeof node === "string") {
125
+ if (node.length > 0) {
126
+ result.push({ text: node, classes: parentClasses });
127
+ }
128
+ } else {
129
+ const classes = node.scope ? [...parentClasses, ...scopeToClasses(node.scope)] : parentClasses;
130
+ if (node.children) {
131
+ result.push(...flattenHljsTree(node.children, classes));
132
+ }
133
+ }
134
+ }
135
+ return result;
136
+ }
137
+ var HLJS_CACHE_MAX = 100;
138
+ var hljsCache = /* @__PURE__ */ new Map();
139
+ function hljsCacheKey(language, code) {
140
+ return language + "\0" + code;
141
+ }
142
+ function getDecorations(doc) {
143
+ const decorations = [];
144
+ doc.descendants((node, pos) => {
145
+ if (node.type.name !== "code_block") return;
146
+ const language = node.attrs.language || "";
147
+ const code = node.textContent;
148
+ if (!code) return;
149
+ if (!language) return;
150
+ if (!hljs.getLanguage(language)) return;
151
+ const cKey = hljsCacheKey(language, code);
152
+ const blockStart = pos + 1;
153
+ const cachedSpans = hljsCache.get(cKey);
154
+ if (cachedSpans) {
155
+ for (const span of cachedSpans) {
156
+ const from = blockStart + span.relOffset;
157
+ const to = from + span.length;
158
+ if (from < to) {
159
+ decorations.push(Decoration.inline(from, to, { class: span.classes }));
160
+ }
161
+ }
162
+ return;
163
+ }
164
+ let result;
165
+ try {
166
+ result = hljs.highlight(code, { language, ignoreIllegals: true });
167
+ } catch {
168
+ return;
169
+ }
170
+ const emitter = result;
171
+ const rootNode = emitter._emitter?.rootNode ?? emitter._emitter?.root;
172
+ if (!rootNode?.children) return;
173
+ const spans = flattenHljsTree(rootNode.children);
174
+ const toCache = [];
175
+ let offset = 0;
176
+ for (const span of spans) {
177
+ const relOffset = offset;
178
+ const length = span.text.length;
179
+ offset += length;
180
+ if (span.classes.length > 0 && length > 0) {
181
+ const classes = span.classes.join(" ");
182
+ toCache.push({ relOffset, length, classes });
183
+ decorations.push(
184
+ Decoration.inline(blockStart + relOffset, blockStart + relOffset + length, { class: classes })
185
+ );
186
+ }
187
+ }
188
+ if (hljsCache.size >= HLJS_CACHE_MAX) {
189
+ const oldest = hljsCache.keys().next().value;
190
+ if (oldest !== void 0) hljsCache.delete(oldest);
191
+ }
192
+ hljsCache.set(cKey, toCache);
193
+ });
194
+ return DecorationSet.create(doc, decorations);
195
+ }
196
+ function createHighlightPlugin() {
197
+ let debounceTimer = null;
198
+ let needsRefresh = false;
199
+ let currentView = null;
200
+ return new Plugin({
201
+ key: highlightPluginKey,
202
+ state: {
203
+ init(_, state) {
204
+ return getDecorations(state.doc);
205
+ },
206
+ apply(tr, decorationSet, _oldState, newState) {
207
+ if (!tr.docChanged) {
208
+ if (needsRefresh) {
209
+ needsRefresh = false;
210
+ return getDecorations(newState.doc);
211
+ }
212
+ return decorationSet;
213
+ }
214
+ if (tr.getMeta("file-switch")) {
215
+ if (debounceTimer !== null) {
216
+ clearTimeout(debounceTimer);
217
+ debounceTimer = null;
218
+ }
219
+ return getDecorations(newState.doc);
220
+ }
221
+ if (tr.getMeta("full-delete")) {
222
+ if (debounceTimer !== null) {
223
+ clearTimeout(debounceTimer);
224
+ debounceTimer = null;
225
+ }
226
+ needsRefresh = false;
227
+ return getDecorations(newState.doc);
228
+ }
229
+ const mapped = decorationSet.map(tr.mapping, newState.doc);
230
+ let affectsCodeBlock = false;
231
+ const docSize = newState.doc.content.size;
232
+ tr.mapping.maps.forEach((stepMap) => {
233
+ if (affectsCodeBlock) return;
234
+ stepMap.forEach((from, to) => {
235
+ if (affectsCodeBlock) return;
236
+ newState.doc.nodesBetween(
237
+ Math.max(0, from),
238
+ Math.min(to, docSize),
239
+ (node) => {
240
+ if (node.type.name === "code_block") affectsCodeBlock = true;
241
+ return !affectsCodeBlock;
242
+ }
243
+ );
244
+ });
245
+ });
246
+ if (!affectsCodeBlock) return mapped;
247
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
248
+ debounceTimer = setTimeout(() => {
249
+ debounceTimer = null;
250
+ needsRefresh = true;
251
+ try {
252
+ if (currentView && !currentView.isDestroyed) {
253
+ currentView.dispatch(currentView.state.tr.setMeta("highlight-refresh", true));
254
+ }
255
+ } catch {
256
+ }
257
+ }, 300);
258
+ return mapped;
259
+ }
260
+ },
261
+ props: {
262
+ decorations(state) {
263
+ return this.getState(state);
264
+ }
265
+ },
266
+ view(editorView) {
267
+ currentView = editorView;
268
+ return {
269
+ destroy() {
270
+ currentView = null;
271
+ if (debounceTimer !== null) {
272
+ clearTimeout(debounceTimer);
273
+ debounceTimer = null;
274
+ }
275
+ }
276
+ };
277
+ }
278
+ });
279
+ }
280
+ export {
281
+ createHighlightPlugin
282
+ };
283
+ //# sourceMappingURL=highlight.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/highlight.ts"],"sourcesContent":["/**\n * Syntax highlighting plugin for `code_block` nodes.\n *\n * Faithful 1:1 migration from Moraya desktop `src/lib/editor/plugins/highlight.ts`\n * (replaces the prior no-op stub). Applies ProseMirror Decoration spans with\n * `hljs-*` CSS classes. Schema-agnostic via `state.schema` lookups.\n *\n * Performance contract (Moraya CLAUDE.md \"Performance Coding Standards\" §6):\n * - Per-block cache keyed by `(language, code)` — switching back to a\n * previously highlighted file skips all hljs calls.\n * - On doc change: cheap `decorationSet.map(tr.mapping)` keeps positions in\n * sync without re-highlighting (no hljs calls in the hot path).\n * - After 300 ms idle: full re-highlight via metadata-only transaction,\n * re-using the per-block cache where possible.\n * - File-switch path (`tr.getMeta('file-switch')`): rebuild from scratch.\n * - Full-delete path (`tr.getMeta('full-delete')`): rebuild on the tiny doc.\n *\n * Tier 1 lazy load: this module bundles hljs + 39 language definitions\n * (~250 KB minified). It is loaded via `dynamic import()` so it forms a\n * separate Vite/Rollup chunk and only ships to consumers that actually use\n * code blocks.\n */\n\nimport { Plugin, PluginKey } from 'prosemirror-state'\nimport { Decoration, DecorationSet, type EditorView } from 'prosemirror-view'\nimport type { Node as PmNode } from 'prosemirror-model'\nimport hljs from 'highlight.js/lib/core'\n\n// Common languages\nimport javascript from 'highlight.js/lib/languages/javascript'\nimport typescript from 'highlight.js/lib/languages/typescript'\nimport python from 'highlight.js/lib/languages/python'\nimport rust from 'highlight.js/lib/languages/rust'\nimport css from 'highlight.js/lib/languages/css'\nimport xml from 'highlight.js/lib/languages/xml'\nimport json from 'highlight.js/lib/languages/json'\nimport bash from 'highlight.js/lib/languages/bash'\nimport sql from 'highlight.js/lib/languages/sql'\nimport java from 'highlight.js/lib/languages/java'\nimport cpp from 'highlight.js/lib/languages/cpp'\nimport c from 'highlight.js/lib/languages/c'\nimport go from 'highlight.js/lib/languages/go'\nimport ruby from 'highlight.js/lib/languages/ruby'\nimport php from 'highlight.js/lib/languages/php'\nimport swift from 'highlight.js/lib/languages/swift'\nimport kotlin from 'highlight.js/lib/languages/kotlin'\nimport yaml from 'highlight.js/lib/languages/yaml'\nimport markdown from 'highlight.js/lib/languages/markdown'\nimport diff from 'highlight.js/lib/languages/diff'\nimport lua from 'highlight.js/lib/languages/lua'\nimport scss from 'highlight.js/lib/languages/scss'\nimport csharp from 'highlight.js/lib/languages/csharp'\nimport dart from 'highlight.js/lib/languages/dart'\nimport r from 'highlight.js/lib/languages/r'\nimport perl from 'highlight.js/lib/languages/perl'\nimport scala from 'highlight.js/lib/languages/scala'\nimport objectivec from 'highlight.js/lib/languages/objectivec'\nimport dockerfile from 'highlight.js/lib/languages/dockerfile'\nimport ini from 'highlight.js/lib/languages/ini'\nimport powershell from 'highlight.js/lib/languages/powershell'\nimport makefile from 'highlight.js/lib/languages/makefile'\nimport groovy from 'highlight.js/lib/languages/groovy'\nimport elixir from 'highlight.js/lib/languages/elixir'\nimport haskell from 'highlight.js/lib/languages/haskell'\nimport protobuf from 'highlight.js/lib/languages/protobuf'\nimport graphql from 'highlight.js/lib/languages/graphql'\nimport latex from 'highlight.js/lib/languages/latex'\nimport nginx from 'highlight.js/lib/languages/nginx'\nimport shell from 'highlight.js/lib/languages/shell'\n\n// Register languages (idempotent — no-op on subsequent calls)\nhljs.registerLanguage('javascript', javascript)\nhljs.registerLanguage('js', javascript)\nhljs.registerLanguage('typescript', typescript)\nhljs.registerLanguage('ts', typescript)\nhljs.registerLanguage('python', python)\nhljs.registerLanguage('py', python)\nhljs.registerLanguage('rust', rust)\nhljs.registerLanguage('rs', rust)\nhljs.registerLanguage('css', css)\nhljs.registerLanguage('html', xml)\nhljs.registerLanguage('xml', xml)\nhljs.registerLanguage('json', json)\nhljs.registerLanguage('bash', bash)\nhljs.registerLanguage('sh', bash)\nhljs.registerLanguage('sql', sql)\nhljs.registerLanguage('java', java)\nhljs.registerLanguage('cpp', cpp)\nhljs.registerLanguage('c', c)\nhljs.registerLanguage('go', go)\nhljs.registerLanguage('ruby', ruby)\nhljs.registerLanguage('rb', ruby)\nhljs.registerLanguage('php', php)\nhljs.registerLanguage('swift', swift)\nhljs.registerLanguage('kotlin', kotlin)\nhljs.registerLanguage('kt', kotlin)\nhljs.registerLanguage('yaml', yaml)\nhljs.registerLanguage('yml', yaml)\nhljs.registerLanguage('markdown', markdown)\nhljs.registerLanguage('md', markdown)\nhljs.registerLanguage('diff', diff)\nhljs.registerLanguage('lua', lua)\nhljs.registerLanguage('scss', scss)\nhljs.registerLanguage('svelte', xml)\nhljs.registerLanguage('jsx', javascript)\nhljs.registerLanguage('tsx', typescript)\n\nhljs.registerLanguage('csharp', csharp)\nhljs.registerLanguage('cs', csharp)\nhljs.registerLanguage('dart', dart)\nhljs.registerLanguage('r', r)\nhljs.registerLanguage('perl', perl)\nhljs.registerLanguage('pl', perl)\nhljs.registerLanguage('scala', scala)\nhljs.registerLanguage('objectivec', objectivec)\nhljs.registerLanguage('objc', objectivec)\nhljs.registerLanguage('dockerfile', dockerfile)\nhljs.registerLanguage('docker', dockerfile)\nhljs.registerLanguage('ini', ini)\nhljs.registerLanguage('toml', ini)\nhljs.registerLanguage('powershell', powershell)\nhljs.registerLanguage('ps', powershell)\nhljs.registerLanguage('ps1', powershell)\nhljs.registerLanguage('makefile', makefile)\nhljs.registerLanguage('make', makefile)\nhljs.registerLanguage('groovy', groovy)\nhljs.registerLanguage('elixir', elixir)\nhljs.registerLanguage('ex', elixir)\nhljs.registerLanguage('haskell', haskell)\nhljs.registerLanguage('hs', haskell)\nhljs.registerLanguage('protobuf', protobuf)\nhljs.registerLanguage('proto', protobuf)\nhljs.registerLanguage('graphql', graphql)\nhljs.registerLanguage('gql', graphql)\nhljs.registerLanguage('latex', latex)\nhljs.registerLanguage('tex', latex)\nhljs.registerLanguage('nginx', nginx)\nhljs.registerLanguage('nginxconf', nginx)\nhljs.registerLanguage('shell', shell)\n\nconst highlightPluginKey = new PluginKey('moraya-syntax-highlight')\n\ninterface HljsNode {\n scope?: string\n children?: (HljsNode | string)[]\n}\n\n/**\n * Convert an hljs scope string to CSS class names.\n * \"keyword\" → [\"hljs-keyword\"]\n * \"title.function\" → [\"hljs-title\", \"function_\"]\n */\nfunction scopeToClasses(scope: string): string[] {\n const parts = scope.split('.')\n const classes = [`hljs-${parts[0]}`]\n for (let i = 1; i < parts.length; i++) {\n classes.push(`${parts[i]}_`)\n }\n return classes\n}\n\n/**\n * Flatten the hljs emitter tree into a list of {text, classes} spans.\n */\nfunction flattenHljsTree(\n nodes: (HljsNode | string)[],\n parentClasses: string[] = [],\n): { text: string; classes: string[] }[] {\n const result: { text: string; classes: string[] }[] = []\n\n for (const node of nodes) {\n if (typeof node === 'string') {\n if (node.length > 0) {\n result.push({ text: node, classes: parentClasses })\n }\n } else {\n // hljs v11 uses `scope` (e.g. \"keyword\", \"title.function\").\n // Dotted scopes become multiple classes.\n const classes = node.scope\n ? [...parentClasses, ...scopeToClasses(node.scope)]\n : parentClasses\n if (node.children) {\n result.push(...flattenHljsTree(node.children, classes))\n }\n }\n }\n\n return result\n}\n\n// ── Per-block hljs result cache ────────────────────────────────\n// Caches the relative-offset spans for each (language, code) pair so that\n// switching back to a previously-highlighted file skips all hljs calls.\n// FIFO eviction at 100 entries.\n\ninterface CachedSpan {\n relOffset: number\n length: number\n classes: string\n}\n\nconst HLJS_CACHE_MAX = 100\nconst hljsCache = new Map<string, CachedSpan[]>()\n\nfunction hljsCacheKey(language: string, code: string): string {\n return language + '\\0' + code\n}\n\n/**\n * Build ProseMirror decorations for all `code_block` nodes in `doc`.\n */\nfunction getDecorations(doc: PmNode): DecorationSet {\n const decorations: Decoration[] = []\n\n doc.descendants((node, pos) => {\n if (node.type.name !== 'code_block') return\n\n const language = (node.attrs.language as string) || ''\n const code = node.textContent\n\n if (!code) return\n if (!language) return // No language label: skip (avoid expensive highlightAuto)\n if (!hljs.getLanguage(language)) return // Unrecognized language\n\n const cKey = hljsCacheKey(language, code)\n const blockStart = pos + 1 // code content starts after opening tag\n\n // Per-block cache hit\n const cachedSpans = hljsCache.get(cKey)\n if (cachedSpans) {\n for (const span of cachedSpans) {\n const from = blockStart + span.relOffset\n const to = from + span.length\n if (from < to) {\n decorations.push(Decoration.inline(from, to, { class: span.classes }))\n }\n }\n return\n }\n\n // Cache miss — run hljs\n let result\n try {\n result = hljs.highlight(code, { language, ignoreIllegals: true })\n } catch {\n return\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const emitter = result as any\n const rootNode = emitter._emitter?.rootNode ?? emitter._emitter?.root\n if (!rootNode?.children) return\n\n const spans = flattenHljsTree(rootNode.children)\n\n // Build relative-offset spans for caching + absolute decorations\n const toCache: CachedSpan[] = []\n let offset = 0\n\n for (const span of spans) {\n const relOffset = offset\n const length = span.text.length\n offset += length\n\n if (span.classes.length > 0 && length > 0) {\n const classes = span.classes.join(' ')\n toCache.push({ relOffset, length, classes })\n decorations.push(\n Decoration.inline(blockStart + relOffset, blockStart + relOffset + length, { class: classes }),\n )\n }\n }\n\n // Store in cache (FIFO eviction)\n if (hljsCache.size >= HLJS_CACHE_MAX) {\n const oldest = hljsCache.keys().next().value\n if (oldest !== undefined) hljsCache.delete(oldest)\n }\n hljsCache.set(cKey, toCache)\n })\n\n return DecorationSet.create(doc, decorations)\n}\n\n/**\n * Plugin that adds syntax highlighting to code blocks.\n *\n * highlight.js is expensive (especially `highlightAuto` which tests all\n * registered languages). Strategy:\n * 1. On doc change: cheaply map existing decorations through the transaction\n * (adjust positions for inserts/deletes — no hljs calls).\n * 2. After 300 ms idle: run a full re-highlight and dispatch a metadata-only\n * transaction to flush new decorations into the view.\n * 3. File-switch / full-delete metas: rebuild from scratch.\n */\nexport function createHighlightPlugin(): Plugin {\n let debounceTimer: ReturnType<typeof setTimeout> | null = null\n let needsRefresh = false\n let currentView: EditorView | null = null\n\n return new Plugin({\n key: highlightPluginKey,\n state: {\n init(_, state) {\n return getDecorations(state.doc)\n },\n apply(tr, decorationSet, _oldState, newState) {\n if (!tr.docChanged) {\n // Non-doc transaction: if a debounced refresh completed, apply it now.\n if (needsRefresh) {\n needsRefresh = false\n return getDecorations(newState.doc)\n }\n return decorationSet\n }\n\n // File switch: rebuild from scratch\n if (tr.getMeta('file-switch')) {\n if (debounceTimer !== null) { clearTimeout(debounceTimer); debounceTimer = null }\n return getDecorations(newState.doc)\n }\n\n // Full-delete: new doc is tiny (single empty paragraph)\n if (tr.getMeta('full-delete')) {\n if (debounceTimer !== null) { clearTimeout(debounceTimer); debounceTimer = null }\n needsRefresh = false\n return getDecorations(newState.doc)\n }\n\n // Map existing decorations cheaply through the transaction\n const mapped = decorationSet.map(tr.mapping, newState.doc)\n\n // Short-circuit: skip 300ms debounce + hljs re-parse if no code_block touched.\n let affectsCodeBlock = false\n const docSize = newState.doc.content.size\n tr.mapping.maps.forEach((stepMap) => {\n if (affectsCodeBlock) return\n stepMap.forEach((from, to) => {\n if (affectsCodeBlock) return\n newState.doc.nodesBetween(\n Math.max(0, from),\n Math.min(to, docSize),\n (node) => {\n if (node.type.name === 'code_block') affectsCodeBlock = true\n return !affectsCodeBlock\n },\n )\n })\n })\n\n if (!affectsCodeBlock) return mapped\n\n // Schedule a full re-highlight after typing pause\n if (debounceTimer !== null) clearTimeout(debounceTimer)\n debounceTimer = setTimeout(() => {\n debounceTimer = null\n needsRefresh = true\n // Dispatch a metadata-only transaction to trigger apply() which will\n // detect needsRefresh and rebuild decorations from scratch.\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (currentView && !(currentView as any).isDestroyed) {\n currentView.dispatch(currentView.state.tr.setMeta('highlight-refresh', true))\n }\n } catch { /* view may be destroyed */ }\n }, 300)\n\n return mapped\n },\n },\n props: {\n decorations(state) {\n return this.getState(state)\n },\n },\n view(editorView) {\n currentView = editorView\n return {\n destroy() {\n currentView = null\n if (debounceTimer !== null) {\n clearTimeout(debounceTimer)\n debounceTimer = null\n }\n },\n }\n },\n })\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,32 @@
1
+ import { Plugin } from 'prosemirror-state';
2
+
3
+ /**
4
+ * Inline mark convert plugin — three responsibilities:
5
+ *
6
+ * 1. **Backtick collapse**: Auto-converts `` `text` `` patterns to code marks
7
+ * when the cursor leaves the backtick pair. Handles the workflow where the
8
+ * user types two backticks first, moves the cursor between them, types
9
+ * content, then leaves.
10
+ *
11
+ * 2. **Cursor target**: Inserts a zero-width space (U+200B) after formatting
12
+ * marks (`code`, `strong`, `em`, `strike_through`) at the end of textblocks.
13
+ * WebKit can't position the caret after certain inline elements when there
14
+ * is no subsequent text node, so the ZWSP provides a DOM target for both
15
+ * keyboard navigation and mouse clicks.
16
+ *
17
+ * 3. **Stored marks at code–ZWSP boundary**: code is `inclusive: false` so
18
+ * `marks()` at the boundary excludes it. The plugin proactively sets stored
19
+ * marks so typing at the boundary still extends code. ArrowRight clears the
20
+ * stored marks (handled in `editor-props-plugin.ts` `'code-escape'` meta).
21
+ * `strong` / `em` / `strike_through` are `inclusive: true` so `marks()`
22
+ * already includes them at the boundary — no `storedMarks` manipulation
23
+ * needed for those.
24
+ *
25
+ * The U+200B is stripped during markdown serialization (see `serializeMarkdown`).
26
+ */
27
+
28
+ /** Zero-width space used as cursor anchor after trailing formatting marks. */
29
+ declare const ZWSP = "\u200B";
30
+ declare function createInlineCodeConvertPlugin(): Plugin;
31
+
32
+ export { ZWSP, createInlineCodeConvertPlugin };