@liminis/editor 0.1.0-rc.1

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 (219) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +376 -0
  3. package/dist/annotations/anchor-align.d.ts +50 -0
  4. package/dist/annotations/anchor-align.js +74 -0
  5. package/dist/annotations/anchor-model.d.ts +81 -0
  6. package/dist/annotations/anchor-model.js +80 -0
  7. package/dist/annotations/anchor-resolver.d.ts +92 -0
  8. package/dist/annotations/anchor-resolver.js +231 -0
  9. package/dist/annotations/block-structure.d.ts +65 -0
  10. package/dist/annotations/block-structure.js +113 -0
  11. package/dist/annotations/types.d.ts +159 -0
  12. package/dist/annotations/types.js +50 -0
  13. package/dist/annotations.d.ts +25 -0
  14. package/dist/annotations.js +27 -0
  15. package/dist/app/App.d.ts +64 -0
  16. package/dist/app/App.js +180 -0
  17. package/dist/app/editor/AmbientCorrectionPlugin.d.ts +18 -0
  18. package/dist/app/editor/AmbientCorrectionPlugin.js +185 -0
  19. package/dist/app/editor/AnchorScrollPlugin.d.ts +16 -0
  20. package/dist/app/editor/AnchorScrollPlugin.js +134 -0
  21. package/dist/app/editor/AnnotationMarkerPlugin.d.ts +24 -0
  22. package/dist/app/editor/AnnotationMarkerPlugin.js +224 -0
  23. package/dist/app/editor/AnnotationPlugin.d.ts +42 -0
  24. package/dist/app/editor/AnnotationPlugin.js +135 -0
  25. package/dist/app/editor/AnnotationSurface.d.ts +34 -0
  26. package/dist/app/editor/AnnotationSurface.js +115 -0
  27. package/dist/app/editor/AssetContext.d.ts +20 -0
  28. package/dist/app/editor/AssetContext.js +81 -0
  29. package/dist/app/editor/BlockClickPlugin.d.ts +8 -0
  30. package/dist/app/editor/BlockClickPlugin.js +126 -0
  31. package/dist/app/editor/CodeBlockPlugin.d.ts +1 -0
  32. package/dist/app/editor/CodeBlockPlugin.js +219 -0
  33. package/dist/app/editor/CorrectionPanelPlugin.d.ts +14 -0
  34. package/dist/app/editor/CorrectionPanelPlugin.js +333 -0
  35. package/dist/app/editor/DragHandlePlugin.d.ts +1 -0
  36. package/dist/app/editor/DragHandlePlugin.js +300 -0
  37. package/dist/app/editor/Editor.d.ts +84 -0
  38. package/dist/app/editor/Editor.js +539 -0
  39. package/dist/app/editor/FrontmatterPlugin.d.ts +19 -0
  40. package/dist/app/editor/FrontmatterPlugin.js +301 -0
  41. package/dist/app/editor/ImageModal.d.ts +13 -0
  42. package/dist/app/editor/ImageModal.js +267 -0
  43. package/dist/app/editor/ImagePlugin.d.ts +3 -0
  44. package/dist/app/editor/ImagePlugin.js +187 -0
  45. package/dist/app/editor/LinkClickPlugin.d.ts +23 -0
  46. package/dist/app/editor/LinkClickPlugin.js +90 -0
  47. package/dist/app/editor/MarkdownShortcutsPlugin.d.ts +49 -0
  48. package/dist/app/editor/MarkdownShortcutsPlugin.js +373 -0
  49. package/dist/app/editor/OrderedTaskListPlugin.d.ts +21 -0
  50. package/dist/app/editor/OrderedTaskListPlugin.js +78 -0
  51. package/dist/app/editor/SearchPlugin.d.ts +1 -0
  52. package/dist/app/editor/SearchPlugin.js +379 -0
  53. package/dist/app/editor/SelectionContextMenuPlugin.d.ts +29 -0
  54. package/dist/app/editor/SelectionContextMenuPlugin.js +160 -0
  55. package/dist/app/editor/SlashMenu.d.ts +11 -0
  56. package/dist/app/editor/SlashMenu.js +494 -0
  57. package/dist/app/editor/SlashMenuPlugin.d.ts +1 -0
  58. package/dist/app/editor/SlashMenuPlugin.js +168 -0
  59. package/dist/app/editor/TableActionsPlugin.d.ts +1 -0
  60. package/dist/app/editor/TableActionsPlugin.js +398 -0
  61. package/dist/app/editor/TogglePlugin.d.ts +1 -0
  62. package/dist/app/editor/TogglePlugin.js +159 -0
  63. package/dist/app/editor/Toolbar.d.ts +10 -0
  64. package/dist/app/editor/Toolbar.js +263 -0
  65. package/dist/app/editor/WikiLinkExistencePlugin.d.ts +17 -0
  66. package/dist/app/editor/WikiLinkExistencePlugin.js +104 -0
  67. package/dist/app/editor/WikiLinkFormatPlugin.d.ts +17 -0
  68. package/dist/app/editor/WikiLinkFormatPlugin.js +203 -0
  69. package/dist/app/editor/annotation-marks.d.ts +177 -0
  70. package/dist/app/editor/annotation-marks.js +750 -0
  71. package/dist/app/editor/annotationCommands.d.ts +13 -0
  72. package/dist/app/editor/annotationCommands.js +11 -0
  73. package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +35 -0
  74. package/dist/app/editor/c4/C4InteractiveRenderer.js +299 -0
  75. package/dist/app/editor/c4/edge-clipping.d.ts +24 -0
  76. package/dist/app/editor/c4/edge-clipping.js +139 -0
  77. package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +38 -0
  78. package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +112 -0
  79. package/dist/app/editor/c4/layout.d.ts +25 -0
  80. package/dist/app/editor/c4/layout.js +839 -0
  81. package/dist/app/editor/c4/parser.d.ts +19 -0
  82. package/dist/app/editor/c4/parser.js +410 -0
  83. package/dist/app/editor/c4/render-to-string.d.ts +24 -0
  84. package/dist/app/editor/c4/render-to-string.js +34 -0
  85. package/dist/app/editor/c4/renderer.d.ts +64 -0
  86. package/dist/app/editor/c4/renderer.js +569 -0
  87. package/dist/app/editor/c4/types.d.ts +203 -0
  88. package/dist/app/editor/c4/types.js +43 -0
  89. package/dist/app/editor/correction-yaml.d.ts +10 -0
  90. package/dist/app/editor/correction-yaml.js +40 -0
  91. package/dist/app/editor/editorNodes.d.ts +9 -0
  92. package/dist/app/editor/editorNodes.js +36 -0
  93. package/dist/app/editor/index.d.ts +1 -0
  94. package/dist/app/editor/index.js +1 -0
  95. package/dist/app/editor/mark-overlap-resolver.d.ts +6 -0
  96. package/dist/app/editor/mark-overlap-resolver.js +45 -0
  97. package/dist/app/editor/nodes/C4Component.d.ts +14 -0
  98. package/dist/app/editor/nodes/C4Component.js +284 -0
  99. package/dist/app/editor/nodes/C4Node.d.ts +32 -0
  100. package/dist/app/editor/nodes/C4Node.js +112 -0
  101. package/dist/app/editor/nodes/CalloutNode.d.ts +23 -0
  102. package/dist/app/editor/nodes/CalloutNode.js +94 -0
  103. package/dist/app/editor/nodes/CustomLinkNode.d.ts +57 -0
  104. package/dist/app/editor/nodes/CustomLinkNode.js +209 -0
  105. package/dist/app/editor/nodes/CustomListItemNode.d.ts +46 -0
  106. package/dist/app/editor/nodes/CustomListItemNode.js +120 -0
  107. package/dist/app/editor/nodes/CustomListNode.d.ts +27 -0
  108. package/dist/app/editor/nodes/CustomListNode.js +54 -0
  109. package/dist/app/editor/nodes/DefinitionListNode.d.ts +44 -0
  110. package/dist/app/editor/nodes/DefinitionListNode.js +151 -0
  111. package/dist/app/editor/nodes/DiagramContextMenu.d.ts +45 -0
  112. package/dist/app/editor/nodes/DiagramContextMenu.js +116 -0
  113. package/dist/app/editor/nodes/EquationComponent.d.ts +17 -0
  114. package/dist/app/editor/nodes/EquationComponent.js +130 -0
  115. package/dist/app/editor/nodes/EquationNode.d.ts +43 -0
  116. package/dist/app/editor/nodes/EquationNode.js +179 -0
  117. package/dist/app/editor/nodes/FootnoteNode.d.ts +40 -0
  118. package/dist/app/editor/nodes/FootnoteNode.js +151 -0
  119. package/dist/app/editor/nodes/FrontmatterNode.d.ts +24 -0
  120. package/dist/app/editor/nodes/FrontmatterNode.js +88 -0
  121. package/dist/app/editor/nodes/HorizontalRuleNode.d.ts +17 -0
  122. package/dist/app/editor/nodes/HorizontalRuleNode.js +57 -0
  123. package/dist/app/editor/nodes/HtmlNode.d.ts +32 -0
  124. package/dist/app/editor/nodes/HtmlNode.js +121 -0
  125. package/dist/app/editor/nodes/ImageComponent.d.ts +11 -0
  126. package/dist/app/editor/nodes/ImageComponent.js +228 -0
  127. package/dist/app/editor/nodes/ImageNode.d.ts +53 -0
  128. package/dist/app/editor/nodes/ImageNode.js +183 -0
  129. package/dist/app/editor/nodes/ListItemParagraphBreakNode.d.ts +42 -0
  130. package/dist/app/editor/nodes/ListItemParagraphBreakNode.js +95 -0
  131. package/dist/app/editor/nodes/MermaidComponent.d.ts +11 -0
  132. package/dist/app/editor/nodes/MermaidComponent.js +216 -0
  133. package/dist/app/editor/nodes/MermaidNode.d.ts +27 -0
  134. package/dist/app/editor/nodes/MermaidNode.js +96 -0
  135. package/dist/app/editor/nodes/ToggleNode.d.ts +56 -0
  136. package/dist/app/editor/nodes/ToggleNode.js +237 -0
  137. package/dist/app/editor/nodes/diagram-context-menu.d.ts +11 -0
  138. package/dist/app/editor/nodes/diagram-context-menu.js +70 -0
  139. package/dist/app/editor/nodes/diagram-utils.d.ts +13 -0
  140. package/dist/app/editor/nodes/diagram-utils.js +29 -0
  141. package/dist/app/editor/nodes/index.d.ts +30 -0
  142. package/dist/app/editor/nodes/index.js +17 -0
  143. package/dist/app/editor/utils.d.ts +2 -0
  144. package/dist/app/editor/utils.js +17 -0
  145. package/dist/app/mapper/index.d.ts +3 -0
  146. package/dist/app/mapper/index.js +2 -0
  147. package/dist/app/mapper/lexicalToMdast.d.ts +62 -0
  148. package/dist/app/mapper/lexicalToMdast.js +1937 -0
  149. package/dist/app/mapper/mdastToLexical.d.ts +31 -0
  150. package/dist/app/mapper/mdastToLexical.js +1075 -0
  151. package/dist/contract.d.ts +8 -0
  152. package/dist/contract.js +8 -0
  153. package/dist/headless.d.ts +40 -0
  154. package/dist/headless.js +45 -0
  155. package/dist/host/context.d.ts +20 -0
  156. package/dist/host/context.js +24 -0
  157. package/dist/host/defaults.d.ts +21 -0
  158. package/dist/host/defaults.js +47 -0
  159. package/dist/host/messages.d.ts +26 -0
  160. package/dist/host/messages.js +47 -0
  161. package/dist/host/types.d.ts +75 -0
  162. package/dist/host/types.js +13 -0
  163. package/dist/index.d.ts +35 -0
  164. package/dist/index.js +51 -0
  165. package/dist/markdown/annotate-sentinels.d.ts +42 -0
  166. package/dist/markdown/annotate-sentinels.js +86 -0
  167. package/dist/markdown/parse.d.ts +56 -0
  168. package/dist/markdown/parse.js +516 -0
  169. package/dist/markdown/stringify.d.ts +7 -0
  170. package/dist/markdown/stringify.js +439 -0
  171. package/dist/markdown/vendor/mdast-util-wiki-link/LICENSE +21 -0
  172. package/dist/markdown/vendor/mdast-util-wiki-link/README.md +41 -0
  173. package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.d.ts +72 -0
  174. package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.js +127 -0
  175. package/dist/markdown/vendor/mdast-util-wiki-link/index.d.ts +8 -0
  176. package/dist/markdown/vendor/mdast-util-wiki-link/index.js +6 -0
  177. package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.d.ts +17 -0
  178. package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.js +33 -0
  179. package/dist/markdown.d.ts +28 -0
  180. package/dist/markdown.js +44 -0
  181. package/dist/mathjax-config.d.ts +89 -0
  182. package/dist/mathjax-config.js +133 -0
  183. package/dist/nodes.d.ts +34 -0
  184. package/dist/nodes.js +34 -0
  185. package/dist/stores/correctionStore.d.ts +15 -0
  186. package/dist/stores/correctionStore.js +12 -0
  187. package/dist/stores/editorStore.d.ts +16 -0
  188. package/dist/stores/editorStore.js +27 -0
  189. package/dist/styles.css +2477 -0
  190. package/dist/types.d.ts +296 -0
  191. package/dist/types.js +91 -0
  192. package/dist/utils/file-types.d.ts +10 -0
  193. package/dist/utils/file-types.js +46 -0
  194. package/docs/annotations.md +134 -0
  195. package/docs/decisions/README.md +72 -0
  196. package/docs/decisions/adr-002.md +27 -0
  197. package/docs/decisions/adr-007.md +27 -0
  198. package/docs/decisions/adr-010.md +26 -0
  199. package/docs/decisions/adr-024.md +124 -0
  200. package/docs/decisions/adr-024b.md +0 -0
  201. package/docs/decisions/adr-025.md +103 -0
  202. package/docs/decisions/adr-027.md +66 -0
  203. package/docs/decisions/adr-057.md +67 -0
  204. package/docs/decisions/adr-070.md +131 -0
  205. package/docs/decisions/adr-075.md +410 -0
  206. package/docs/decisions/adr-076.md +183 -0
  207. package/docs/decisions/adr-077.md +410 -0
  208. package/docs/decisions/adr-078.md +243 -0
  209. package/docs/decisions/adr-079.md +167 -0
  210. package/docs/decisions/adr-080.md +325 -0
  211. package/docs/decisions/adr-081.md +108 -0
  212. package/docs/decisions/adr-082.md +123 -0
  213. package/docs/decisions/adr-083.md +204 -0
  214. package/docs/decisions/adr-084.md +179 -0
  215. package/docs/editor-api.md +106 -0
  216. package/docs/markdown-pipeline.md +282 -0
  217. package/docs/provenance.md +106 -0
  218. package/docs/zusammen-editor-capability-map.md +232 -0
  219. package/package.json +175 -0
@@ -0,0 +1,398 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState, useRef } from 'react';
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import { $createParagraphNode, $createTextNode, $getRoot, } from 'lexical';
5
+ import { $createTableCellNode, $createTableRowNode, $isTableCellNode, $isTableNode, $isTableRowNode, TableCellHeaderStates, } from '@lexical/table';
6
+ function TableActionsMenu({ tableNode, rowIndex, colIndex, position, onClose }) {
7
+ const [editor] = useLexicalComposerContext();
8
+ const menuRef = useRef(null);
9
+ // Close on click outside
10
+ useEffect(() => {
11
+ const handleClickOutside = (e) => {
12
+ if (menuRef.current && !menuRef.current.contains(e.target)) {
13
+ onClose();
14
+ }
15
+ };
16
+ document.addEventListener('mousedown', handleClickOutside);
17
+ return () => document.removeEventListener('mousedown', handleClickOutside);
18
+ }, [onClose]);
19
+ // Close on escape
20
+ useEffect(() => {
21
+ const handleKeyDown = (e) => {
22
+ if (e.key === 'Escape') {
23
+ e.preventDefault();
24
+ onClose();
25
+ }
26
+ };
27
+ document.addEventListener('keydown', handleKeyDown);
28
+ return () => document.removeEventListener('keydown', handleKeyDown);
29
+ }, [onClose]);
30
+ const getColumnCount = useCallback(() => {
31
+ return editor.getEditorState().read(() => {
32
+ const rows = tableNode.getChildren();
33
+ if (rows.length > 0 && $isTableRowNode(rows[0])) {
34
+ return rows[0].getChildren().length;
35
+ }
36
+ return 0;
37
+ });
38
+ }, [editor, tableNode]);
39
+ const isHeaderRow = useCallback(() => {
40
+ return editor.getEditorState().read(() => {
41
+ const rows = tableNode.getChildren();
42
+ const row = rows[rowIndex];
43
+ if ($isTableRowNode(row)) {
44
+ const cells = row.getChildren();
45
+ if (cells.length > 0 && $isTableCellNode(cells[0])) {
46
+ return cells[0].hasHeaderState(TableCellHeaderStates.ROW);
47
+ }
48
+ }
49
+ return false;
50
+ });
51
+ }, [editor, tableNode, rowIndex]);
52
+ const addRowAbove = useCallback(() => {
53
+ editor.update(() => {
54
+ const columnCount = getColumnCount();
55
+ const rows = tableNode.getChildren();
56
+ // Check if we're adding above the header row
57
+ const targetRow = rows[rowIndex];
58
+ const isInsertingAboveHeader = rowIndex === 0 && $isTableRowNode(targetRow);
59
+ const newRow = $createTableRowNode();
60
+ for (let i = 0; i < columnCount; i++) {
61
+ // If inserting above header, new row becomes the header
62
+ const headerState = isInsertingAboveHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
63
+ const cell = $createTableCellNode(headerState);
64
+ const paragraph = $createParagraphNode();
65
+ paragraph.append($createTextNode(''));
66
+ cell.append(paragraph);
67
+ newRow.append(cell);
68
+ }
69
+ if (rowIndex === 0) {
70
+ tableNode.getFirstChild()?.insertBefore(newRow);
71
+ // Convert old header row to regular row
72
+ if ($isTableRowNode(targetRow)) {
73
+ for (const cell of targetRow.getChildren()) {
74
+ if ($isTableCellNode(cell)) {
75
+ cell.setHeaderStyles(TableCellHeaderStates.NO_STATUS);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ else {
81
+ const targetRow = rows[rowIndex];
82
+ if (targetRow) {
83
+ targetRow.insertBefore(newRow);
84
+ }
85
+ }
86
+ });
87
+ onClose();
88
+ }, [editor, tableNode, rowIndex, getColumnCount, onClose]);
89
+ const addRowBelow = useCallback(() => {
90
+ editor.update(() => {
91
+ const columnCount = getColumnCount();
92
+ const rows = tableNode.getChildren();
93
+ const newRow = $createTableRowNode();
94
+ for (let i = 0; i < columnCount; i++) {
95
+ const cell = $createTableCellNode(TableCellHeaderStates.NO_STATUS);
96
+ const paragraph = $createParagraphNode();
97
+ paragraph.append($createTextNode(''));
98
+ cell.append(paragraph);
99
+ newRow.append(cell);
100
+ }
101
+ const targetRow = rows[rowIndex];
102
+ if (targetRow) {
103
+ targetRow.insertAfter(newRow);
104
+ }
105
+ });
106
+ onClose();
107
+ }, [editor, tableNode, rowIndex, getColumnCount, onClose]);
108
+ const addColumnLeft = useCallback(() => {
109
+ editor.update(() => {
110
+ const rows = tableNode.getChildren();
111
+ for (let i = 0; i < rows.length; i++) {
112
+ const row = rows[i];
113
+ if ($isTableRowNode(row)) {
114
+ const cells = row.getChildren();
115
+ const isHeader = i === 0;
116
+ const headerState = isHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
117
+ const newCell = $createTableCellNode(headerState);
118
+ const paragraph = $createParagraphNode();
119
+ paragraph.append($createTextNode(isHeader ? 'Header' : ''));
120
+ newCell.append(paragraph);
121
+ if (colIndex === 0) {
122
+ row.getFirstChild()?.insertBefore(newCell);
123
+ }
124
+ else {
125
+ const targetCell = cells[colIndex];
126
+ if (targetCell) {
127
+ targetCell.insertBefore(newCell);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ });
133
+ onClose();
134
+ }, [editor, tableNode, colIndex, onClose]);
135
+ const addColumnRight = useCallback(() => {
136
+ editor.update(() => {
137
+ const rows = tableNode.getChildren();
138
+ for (let i = 0; i < rows.length; i++) {
139
+ const row = rows[i];
140
+ if ($isTableRowNode(row)) {
141
+ const cells = row.getChildren();
142
+ const isHeader = i === 0;
143
+ const headerState = isHeader ? TableCellHeaderStates.ROW : TableCellHeaderStates.NO_STATUS;
144
+ const newCell = $createTableCellNode(headerState);
145
+ const paragraph = $createParagraphNode();
146
+ paragraph.append($createTextNode(isHeader ? 'Header' : ''));
147
+ newCell.append(paragraph);
148
+ const targetCell = cells[colIndex];
149
+ if (targetCell) {
150
+ targetCell.insertAfter(newCell);
151
+ }
152
+ }
153
+ }
154
+ });
155
+ onClose();
156
+ }, [editor, tableNode, colIndex, onClose]);
157
+ const deleteRow = useCallback(() => {
158
+ editor.update(() => {
159
+ const rows = tableNode.getChildren();
160
+ // Don't delete if it's the only row
161
+ if (rows.length <= 1) {
162
+ return;
163
+ }
164
+ const targetRow = rows[rowIndex];
165
+ if (targetRow && $isTableRowNode(targetRow)) {
166
+ // If deleting header row, make next row the header
167
+ if (rowIndex === 0 && rows.length > 1) {
168
+ const nextRow = rows[1];
169
+ if ($isTableRowNode(nextRow)) {
170
+ for (const cell of nextRow.getChildren()) {
171
+ if ($isTableCellNode(cell)) {
172
+ cell.setHeaderStyles(TableCellHeaderStates.ROW);
173
+ }
174
+ }
175
+ }
176
+ }
177
+ targetRow.remove();
178
+ }
179
+ });
180
+ onClose();
181
+ }, [editor, tableNode, rowIndex, onClose]);
182
+ const deleteColumn = useCallback(() => {
183
+ editor.update(() => {
184
+ const rows = tableNode.getChildren();
185
+ // Don't delete if it's the only column
186
+ if (rows.length > 0 && $isTableRowNode(rows[0])) {
187
+ const columnCount = rows[0].getChildren().length;
188
+ if (columnCount <= 1) {
189
+ return;
190
+ }
191
+ }
192
+ for (const row of rows) {
193
+ if ($isTableRowNode(row)) {
194
+ const cells = row.getChildren();
195
+ const targetCell = cells[colIndex];
196
+ if (targetCell) {
197
+ targetCell.remove();
198
+ }
199
+ }
200
+ }
201
+ });
202
+ onClose();
203
+ }, [editor, tableNode, colIndex, onClose]);
204
+ const toggleHeaderRow = useCallback(() => {
205
+ editor.update(() => {
206
+ const rows = tableNode.getChildren();
207
+ const currentRow = rows[rowIndex];
208
+ if ($isTableRowNode(currentRow)) {
209
+ const currentIsHeader = isHeaderRow();
210
+ const newHeaderState = currentIsHeader
211
+ ? TableCellHeaderStates.NO_STATUS
212
+ : TableCellHeaderStates.ROW;
213
+ for (const cell of currentRow.getChildren()) {
214
+ if ($isTableCellNode(cell)) {
215
+ cell.setHeaderStyles(newHeaderState);
216
+ }
217
+ }
218
+ }
219
+ });
220
+ onClose();
221
+ }, [editor, tableNode, rowIndex, isHeaderRow, onClose]);
222
+ const moveRowUp = useCallback(() => {
223
+ editor.update(() => {
224
+ if (rowIndex === 0)
225
+ return; // Can't move first row up
226
+ const rows = tableNode.getChildren();
227
+ const currentRow = rows[rowIndex];
228
+ const previousRow = rows[rowIndex - 1];
229
+ if ($isTableRowNode(currentRow) && $isTableRowNode(previousRow)) {
230
+ previousRow.insertBefore(currentRow);
231
+ // Handle header state changes
232
+ if (rowIndex === 1) {
233
+ // Moving to first position, make it header
234
+ for (const cell of currentRow.getChildren()) {
235
+ if ($isTableCellNode(cell)) {
236
+ cell.setHeaderStyles(TableCellHeaderStates.ROW);
237
+ }
238
+ }
239
+ // Remove header from old first row
240
+ for (const cell of previousRow.getChildren()) {
241
+ if ($isTableCellNode(cell)) {
242
+ cell.setHeaderStyles(TableCellHeaderStates.NO_STATUS);
243
+ }
244
+ }
245
+ }
246
+ }
247
+ });
248
+ onClose();
249
+ }, [editor, tableNode, rowIndex, onClose]);
250
+ const moveRowDown = useCallback(() => {
251
+ editor.update(() => {
252
+ const rows = tableNode.getChildren();
253
+ if (rowIndex >= rows.length - 1)
254
+ return; // Can't move last row down
255
+ const currentRow = rows[rowIndex];
256
+ const nextRow = rows[rowIndex + 1];
257
+ if ($isTableRowNode(currentRow) && $isTableRowNode(nextRow)) {
258
+ nextRow.insertAfter(currentRow);
259
+ // Handle header state changes
260
+ if (rowIndex === 0) {
261
+ // Moving away from first position, remove header
262
+ for (const cell of currentRow.getChildren()) {
263
+ if ($isTableCellNode(cell)) {
264
+ cell.setHeaderStyles(TableCellHeaderStates.NO_STATUS);
265
+ }
266
+ }
267
+ // Make new first row the header
268
+ for (const cell of nextRow.getChildren()) {
269
+ if ($isTableCellNode(cell)) {
270
+ cell.setHeaderStyles(TableCellHeaderStates.ROW);
271
+ }
272
+ }
273
+ }
274
+ }
275
+ });
276
+ onClose();
277
+ }, [editor, tableNode, rowIndex, onClose]);
278
+ const moveColumnLeft = useCallback(() => {
279
+ editor.update(() => {
280
+ if (colIndex === 0)
281
+ return; // Can't move first column left
282
+ const rows = tableNode.getChildren();
283
+ for (const row of rows) {
284
+ if ($isTableRowNode(row)) {
285
+ const cells = row.getChildren();
286
+ const currentCell = cells[colIndex];
287
+ const previousCell = cells[colIndex - 1];
288
+ if (currentCell && previousCell) {
289
+ previousCell.insertBefore(currentCell);
290
+ }
291
+ }
292
+ }
293
+ });
294
+ onClose();
295
+ }, [editor, tableNode, colIndex, onClose]);
296
+ const moveColumnRight = useCallback(() => {
297
+ editor.update(() => {
298
+ const rows = tableNode.getChildren();
299
+ if (rows.length === 0)
300
+ return;
301
+ const firstRow = rows[0];
302
+ if (!$isTableRowNode(firstRow))
303
+ return;
304
+ if (colIndex >= firstRow.getChildren().length - 1)
305
+ return; // Can't move last column right
306
+ for (const row of rows) {
307
+ if ($isTableRowNode(row)) {
308
+ const cells = row.getChildren();
309
+ const currentCell = cells[colIndex];
310
+ const nextCell = cells[colIndex + 1];
311
+ if (currentCell && nextCell) {
312
+ nextCell.insertAfter(currentCell);
313
+ }
314
+ }
315
+ }
316
+ });
317
+ onClose();
318
+ }, [editor, tableNode, colIndex, onClose]);
319
+ const deleteTable = useCallback(() => {
320
+ editor.update(() => {
321
+ tableNode.remove();
322
+ });
323
+ onClose();
324
+ }, [editor, tableNode, onClose]);
325
+ return (_jsxs("div", { ref: menuRef, className: "table-actions-menu", style: {
326
+ position: 'fixed',
327
+ top: position.top,
328
+ left: position.left,
329
+ }, children: [_jsxs("div", { className: "table-actions-section", children: [_jsx("div", { className: "table-actions-header", children: "Row" }), _jsxs("button", { className: "table-actions-button", onClick: addRowAbove, children: [_jsx("span", { className: "table-actions-icon", children: "\u2191" }), "Add row above"] }), _jsxs("button", { className: "table-actions-button", onClick: addRowBelow, children: [_jsx("span", { className: "table-actions-icon", children: "\u2193" }), "Add row below"] }), _jsxs("button", { className: "table-actions-button", onClick: moveRowUp, children: [_jsx("span", { className: "table-actions-icon", children: "\u2B06" }), "Move row up"] }), _jsxs("button", { className: "table-actions-button", onClick: moveRowDown, children: [_jsx("span", { className: "table-actions-icon", children: "\u2B07" }), "Move row down"] }), _jsxs("button", { className: "table-actions-button", onClick: toggleHeaderRow, children: [_jsx("span", { className: "table-actions-icon", children: isHeaderRow() ? '☐' : '☑' }), isHeaderRow() ? 'Remove header' : 'Make header'] }), _jsxs("button", { className: "table-actions-button table-actions-danger", onClick: deleteRow, children: [_jsx("span", { className: "table-actions-icon", children: "\u2715" }), "Delete row"] })] }), _jsx("div", { className: "table-actions-divider" }), _jsxs("div", { className: "table-actions-section", children: [_jsx("div", { className: "table-actions-header", children: "Column" }), _jsxs("button", { className: "table-actions-button", onClick: addColumnLeft, children: [_jsx("span", { className: "table-actions-icon", children: "\u2190" }), "Add column left"] }), _jsxs("button", { className: "table-actions-button", onClick: addColumnRight, children: [_jsx("span", { className: "table-actions-icon", children: "\u2192" }), "Add column right"] }), _jsxs("button", { className: "table-actions-button", onClick: moveColumnLeft, children: [_jsx("span", { className: "table-actions-icon", children: "\u2B05" }), "Move column left"] }), _jsxs("button", { className: "table-actions-button", onClick: moveColumnRight, children: [_jsx("span", { className: "table-actions-icon", children: "\u27A1" }), "Move column right"] }), _jsxs("button", { className: "table-actions-button table-actions-danger", onClick: deleteColumn, children: [_jsx("span", { className: "table-actions-icon", children: "\u2715" }), "Delete column"] })] }), _jsx("div", { className: "table-actions-divider" }), _jsx("div", { className: "table-actions-section", children: _jsxs("button", { className: "table-actions-button table-actions-danger", onClick: deleteTable, children: [_jsx("span", { className: "table-actions-icon", children: "\uD83D\uDDD1" }), "Delete table"] }) })] }));
330
+ }
331
+ export function TableActionsPlugin() {
332
+ const [editor] = useLexicalComposerContext();
333
+ const [menuState, setMenuState] = useState(null);
334
+ useEffect(() => {
335
+ const handleContextMenu = (e) => {
336
+ const target = e.target;
337
+ const cellElement = target.closest('td, th');
338
+ if (!cellElement)
339
+ return;
340
+ // Find the table element
341
+ const tableElement = cellElement.closest('table');
342
+ if (!tableElement)
343
+ return;
344
+ e.preventDefault();
345
+ // Find the row and column indices from the DOM
346
+ const rowElement = cellElement.closest('tr');
347
+ if (!rowElement)
348
+ return;
349
+ const rows = Array.from(tableElement.querySelectorAll(':scope > tbody > tr, :scope > tr'));
350
+ const rowIndex = rows.indexOf(rowElement);
351
+ const cells = Array.from(rowElement.querySelectorAll(':scope > td, :scope > th'));
352
+ const colIndex = cells.indexOf(cellElement);
353
+ if (rowIndex === -1 || colIndex === -1)
354
+ return;
355
+ // Find which table this is by checking all tables in the editor
356
+ const editorElement = editor.getRootElement();
357
+ if (!editorElement)
358
+ return;
359
+ const allTables = Array.from(editorElement.querySelectorAll('table'));
360
+ const tableIndex = allTables.indexOf(tableElement);
361
+ if (tableIndex === -1)
362
+ return;
363
+ editor.getEditorState().read(() => {
364
+ // Find all table nodes in the editor
365
+ const root = $getRoot();
366
+ const tableNodes = [];
367
+ for (const child of root.getChildren()) {
368
+ if ($isTableNode(child)) {
369
+ tableNodes.push(child);
370
+ }
371
+ }
372
+ const tableNode = tableNodes[tableIndex];
373
+ if (!tableNode)
374
+ return;
375
+ setMenuState({
376
+ tableNode,
377
+ rowIndex,
378
+ colIndex,
379
+ position: {
380
+ top: e.clientY,
381
+ left: e.clientX,
382
+ },
383
+ });
384
+ });
385
+ };
386
+ const editorElement = editor.getRootElement();
387
+ if (editorElement) {
388
+ editorElement.addEventListener('contextmenu', handleContextMenu);
389
+ return () => editorElement.removeEventListener('contextmenu', handleContextMenu);
390
+ }
391
+ }, [editor]);
392
+ const handleClose = useCallback(() => {
393
+ setMenuState(null);
394
+ }, []);
395
+ if (!menuState)
396
+ return null;
397
+ return (_jsx(TableActionsMenu, { tableNode: menuState.tableNode, rowIndex: menuState.rowIndex, colIndex: menuState.colIndex, position: menuState.position, onClose: handleClose }));
398
+ }
@@ -0,0 +1 @@
1
+ export declare function TogglePlugin(): null;
@@ -0,0 +1,159 @@
1
+ import { useEffect } from 'react';
2
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
3
+ import { $getNodeByKey, $getRoot, COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, $getSelection, $isRangeSelection, } from 'lexical';
4
+ import { $isToggleContainerNode, $isToggleTitleNode, $isToggleContentNode, } from './nodes/index.js';
5
+ export function TogglePlugin() {
6
+ const [editor] = useLexicalComposerContext();
7
+ // Handle click on toggle arrow to expand/collapse
8
+ useEffect(() => {
9
+ const handleClick = (event) => {
10
+ const target = event.target;
11
+ // Check if clicked on toggle-title
12
+ const toggleTitle = target.closest('.toggle-title');
13
+ if (!toggleTitle)
14
+ return;
15
+ // Check if the click is on the arrow area (first 32px)
16
+ const rect = toggleTitle.getBoundingClientRect();
17
+ const clickX = event.clientX - rect.left;
18
+ // Only toggle on arrow click (first 32px) to allow editing title text
19
+ if (clickX > 32)
20
+ return;
21
+ event.preventDefault();
22
+ event.stopPropagation();
23
+ // Find the toggle container DOM element
24
+ const container = toggleTitle.closest('.toggle-container');
25
+ if (!container)
26
+ return;
27
+ // Find the corresponding Lexical node by traversing editor state
28
+ editor.update(() => {
29
+ const root = $getRoot();
30
+ // Walk through all nodes to find toggle containers
31
+ const findToggleContainer = (element) => {
32
+ // Try to get the node key from the element
33
+ const key = element.getAttribute('data-lexical-node-key');
34
+ if (key) {
35
+ const node = $getNodeByKey(key);
36
+ if ($isToggleContainerNode(node)) {
37
+ return node;
38
+ }
39
+ }
40
+ // Fallback: search for the toggle container by matching DOM elements
41
+ for (const child of root.getChildren()) {
42
+ if ($isToggleContainerNode(child)) {
43
+ const domElement = editor.getElementByKey(child.getKey());
44
+ if (domElement === container) {
45
+ return child;
46
+ }
47
+ }
48
+ }
49
+ return null;
50
+ };
51
+ const toggleNode = findToggleContainer(container);
52
+ if (toggleNode) {
53
+ toggleNode.toggleOpen();
54
+ }
55
+ });
56
+ };
57
+ const rootElement = editor.getRootElement();
58
+ if (rootElement) {
59
+ rootElement.addEventListener('click', handleClick);
60
+ return () => {
61
+ rootElement.removeEventListener('click', handleClick);
62
+ };
63
+ }
64
+ }, [editor]);
65
+ // Handle arrow key navigation between title and content
66
+ useEffect(() => {
67
+ const handleArrowDown = () => {
68
+ const selection = $getSelection();
69
+ if (!$isRangeSelection(selection))
70
+ return false;
71
+ const anchorNode = selection.anchor.getNode();
72
+ // Check if we're in a paragraph that's inside a toggle title
73
+ let titleNode = null;
74
+ let node = anchorNode;
75
+ while (node) {
76
+ const parent = node.getParent();
77
+ if ($isToggleTitleNode(parent)) {
78
+ titleNode = parent;
79
+ break;
80
+ }
81
+ if ($isToggleTitleNode(node)) {
82
+ titleNode = node;
83
+ break;
84
+ }
85
+ node = parent;
86
+ }
87
+ if (titleNode) {
88
+ const containerNode = titleNode.getParent();
89
+ if ($isToggleContainerNode(containerNode)) {
90
+ // Open if closed
91
+ if (!containerNode.getOpen()) {
92
+ containerNode.setOpen(true);
93
+ }
94
+ // Find content node
95
+ const children = containerNode.getChildren();
96
+ const contentNode = children.find($isToggleContentNode);
97
+ if (contentNode) {
98
+ const firstChild = contentNode.getFirstChild();
99
+ if (firstChild) {
100
+ firstChild.selectStart();
101
+ return true;
102
+ }
103
+ }
104
+ }
105
+ }
106
+ return false;
107
+ };
108
+ const handleArrowUp = () => {
109
+ const selection = $getSelection();
110
+ if (!$isRangeSelection(selection))
111
+ return false;
112
+ const anchorNode = selection.anchor.getNode();
113
+ // Check if we're in a paragraph that's inside toggle content, at the start
114
+ let contentNode = null;
115
+ let node = anchorNode;
116
+ while (node) {
117
+ const parent = node.getParent();
118
+ if ($isToggleContentNode(parent)) {
119
+ contentNode = parent;
120
+ break;
121
+ }
122
+ if ($isToggleContentNode(node)) {
123
+ contentNode = node;
124
+ break;
125
+ }
126
+ node = parent;
127
+ }
128
+ if (contentNode && selection.anchor.offset === 0) {
129
+ const containerNode = contentNode.getParent();
130
+ const firstContentChild = contentNode.getFirstChild();
131
+ // Only move if we're at the very start of the first child
132
+ if ($isToggleContainerNode(containerNode) && firstContentChild) {
133
+ // Check if anchor is at start of first element
134
+ const anchorTop = anchorNode.getTopLevelElement?.() || anchorNode.getParent();
135
+ if (anchorTop === firstContentChild || anchorNode === firstContentChild) {
136
+ // Find title node
137
+ const children = containerNode.getChildren();
138
+ const titleNode = children.find($isToggleTitleNode);
139
+ if (titleNode) {
140
+ const titleParagraph = titleNode.getFirstChild();
141
+ if (titleParagraph) {
142
+ titleParagraph.selectEnd();
143
+ return true;
144
+ }
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return false;
150
+ };
151
+ const removeDown = editor.registerCommand(KEY_ARROW_DOWN_COMMAND, handleArrowDown, COMMAND_PRIORITY_LOW);
152
+ const removeUp = editor.registerCommand(KEY_ARROW_UP_COMMAND, handleArrowUp, COMMAND_PRIORITY_LOW);
153
+ return () => {
154
+ removeDown();
155
+ removeUp();
156
+ };
157
+ }, [editor]);
158
+ return null;
159
+ }
@@ -0,0 +1,10 @@
1
+ /** One toolbar-surfaced annotation kind's affordance, plain data only (FR-001/FR-009). */
2
+ export interface ToolbarAnnotationAffordance {
3
+ kind: string;
4
+ label: string;
5
+ }
6
+ interface ToolbarProps {
7
+ annotationAffordances?: ToolbarAnnotationAffordance[];
8
+ }
9
+ export declare function Toolbar({ annotationAffordances }: ToolbarProps): import("react").JSX.Element | null;
10
+ export {};