@jotx-labs/editor 2.4.130

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 (261) hide show
  1. package/dist/bridge/BridgeContext.d.ts +14 -0
  2. package/dist/bridge/BridgeContext.d.ts.map +1 -0
  3. package/dist/bridge/BridgeContext.js +20 -0
  4. package/dist/bridge/BridgeContext.js.map +1 -0
  5. package/dist/bridge/bridge-utils.d.ts +24 -0
  6. package/dist/bridge/bridge-utils.d.ts.map +1 -0
  7. package/dist/bridge/bridge-utils.js +77 -0
  8. package/dist/bridge/bridge-utils.js.map +1 -0
  9. package/dist/bridge/types.d.ts +34 -0
  10. package/dist/bridge/types.d.ts.map +1 -0
  11. package/dist/bridge/types.js +27 -0
  12. package/dist/bridge/types.js.map +1 -0
  13. package/dist/bridge/vscode-api.d.ts +13 -0
  14. package/dist/bridge/vscode-api.d.ts.map +1 -0
  15. package/dist/bridge/vscode-api.js +27 -0
  16. package/dist/bridge/vscode-api.js.map +1 -0
  17. package/dist/components/AttachBlockNodeView.d.ts +2 -0
  18. package/dist/components/AttachBlockNodeView.d.ts.map +1 -0
  19. package/dist/components/AttachBlockNodeView.js +37 -0
  20. package/dist/components/AttachBlockNodeView.js.map +1 -0
  21. package/dist/components/BlockMenu.d.ts +11 -0
  22. package/dist/components/BlockMenu.d.ts.map +1 -0
  23. package/dist/components/BlockMenu.js +344 -0
  24. package/dist/components/BlockMenu.js.map +1 -0
  25. package/dist/components/ButtonNodeView.d.ts +17 -0
  26. package/dist/components/ButtonNodeView.d.ts.map +1 -0
  27. package/dist/components/ButtonNodeView.js +55 -0
  28. package/dist/components/ButtonNodeView.js.map +1 -0
  29. package/dist/components/CardNodeView.d.ts +18 -0
  30. package/dist/components/CardNodeView.d.ts.map +1 -0
  31. package/dist/components/CardNodeView.js +46 -0
  32. package/dist/components/CardNodeView.js.map +1 -0
  33. package/dist/components/ChartNodeView.d.ts +4 -0
  34. package/dist/components/ChartNodeView.d.ts.map +1 -0
  35. package/dist/components/ChartNodeView.js +247 -0
  36. package/dist/components/ChartNodeView.js.map +1 -0
  37. package/dist/components/CodeBlockNodeView.d.ts +4 -0
  38. package/dist/components/CodeBlockNodeView.d.ts.map +1 -0
  39. package/dist/components/CodeBlockNodeView.js +105 -0
  40. package/dist/components/CodeBlockNodeView.js.map +1 -0
  41. package/dist/components/CodeReferenceNodeView.d.ts +4 -0
  42. package/dist/components/CodeReferenceNodeView.d.ts.map +1 -0
  43. package/dist/components/CodeReferenceNodeView.js +71 -0
  44. package/dist/components/CodeReferenceNodeView.js.map +1 -0
  45. package/dist/components/DateTimeNodeView.d.ts +3 -0
  46. package/dist/components/DateTimeNodeView.d.ts.map +1 -0
  47. package/dist/components/DateTimeNodeView.js +55 -0
  48. package/dist/components/DateTimeNodeView.js.map +1 -0
  49. package/dist/components/EditorRibbon.d.ts +13 -0
  50. package/dist/components/EditorRibbon.d.ts.map +1 -0
  51. package/dist/components/EditorRibbon.js +12 -0
  52. package/dist/components/EditorRibbon.js.map +1 -0
  53. package/dist/components/FloatImageBlockNodeView.d.ts +6 -0
  54. package/dist/components/FloatImageBlockNodeView.d.ts.map +1 -0
  55. package/dist/components/FloatImageBlockNodeView.js +96 -0
  56. package/dist/components/FloatImageBlockNodeView.js.map +1 -0
  57. package/dist/components/GridCardNodeView.d.ts +13 -0
  58. package/dist/components/GridCardNodeView.d.ts.map +1 -0
  59. package/dist/components/GridCardNodeView.js +31 -0
  60. package/dist/components/GridCardNodeView.js.map +1 -0
  61. package/dist/components/ImageBlockNodeView.d.ts +7 -0
  62. package/dist/components/ImageBlockNodeView.d.ts.map +1 -0
  63. package/dist/components/ImageBlockNodeView.js +132 -0
  64. package/dist/components/ImageBlockNodeView.js.map +1 -0
  65. package/dist/components/ImageToolbar.d.ts +6 -0
  66. package/dist/components/ImageToolbar.d.ts.map +1 -0
  67. package/dist/components/ImageToolbar.js +67 -0
  68. package/dist/components/ImageToolbar.js.map +1 -0
  69. package/dist/components/JotxEditor.d.ts +17 -0
  70. package/dist/components/JotxEditor.d.ts.map +1 -0
  71. package/dist/components/JotxEditor.js +1121 -0
  72. package/dist/components/JotxEditor.js.map +1 -0
  73. package/dist/components/JotxLinkNodeView.d.ts +4 -0
  74. package/dist/components/JotxLinkNodeView.d.ts.map +1 -0
  75. package/dist/components/JotxLinkNodeView.js +123 -0
  76. package/dist/components/JotxLinkNodeView.js.map +1 -0
  77. package/dist/components/LinkDialog.d.ts +14 -0
  78. package/dist/components/LinkDialog.d.ts.map +1 -0
  79. package/dist/components/LinkDialog.js +33 -0
  80. package/dist/components/LinkDialog.js.map +1 -0
  81. package/dist/components/MathNodeView.d.ts +3 -0
  82. package/dist/components/MathNodeView.d.ts.map +1 -0
  83. package/dist/components/MathNodeView.js +67 -0
  84. package/dist/components/MathNodeView.js.map +1 -0
  85. package/dist/components/MermaidNodeView.d.ts +4 -0
  86. package/dist/components/MermaidNodeView.d.ts.map +1 -0
  87. package/dist/components/MermaidNodeView.js +442 -0
  88. package/dist/components/MermaidNodeView.js.map +1 -0
  89. package/dist/components/NodePickerDialog.d.ts +12 -0
  90. package/dist/components/NodePickerDialog.d.ts.map +1 -0
  91. package/dist/components/NodePickerDialog.js +90 -0
  92. package/dist/components/NodePickerDialog.js.map +1 -0
  93. package/dist/components/ReadonlyBlockRenderer.d.ts +13 -0
  94. package/dist/components/ReadonlyBlockRenderer.d.ts.map +1 -0
  95. package/dist/components/ReadonlyBlockRenderer.js +78 -0
  96. package/dist/components/ReadonlyBlockRenderer.js.map +1 -0
  97. package/dist/components/ReadonlyMermaid.d.ts +10 -0
  98. package/dist/components/ReadonlyMermaid.d.ts.map +1 -0
  99. package/dist/components/ReadonlyMermaid.js +70 -0
  100. package/dist/components/ReadonlyMermaid.js.map +1 -0
  101. package/dist/components/SearchBar.d.ts +16 -0
  102. package/dist/components/SearchBar.d.ts.map +1 -0
  103. package/dist/components/SearchBar.js +52 -0
  104. package/dist/components/SearchBar.js.map +1 -0
  105. package/dist/components/SectionNodeView.d.ts +4 -0
  106. package/dist/components/SectionNodeView.d.ts.map +1 -0
  107. package/dist/components/SectionNodeView.js +39 -0
  108. package/dist/components/SectionNodeView.js.map +1 -0
  109. package/dist/components/SlashMenu.d.ts +21 -0
  110. package/dist/components/SlashMenu.d.ts.map +1 -0
  111. package/dist/components/SlashMenu.js +356 -0
  112. package/dist/components/SlashMenu.js.map +1 -0
  113. package/dist/components/TableToolbar.d.ts +10 -0
  114. package/dist/components/TableToolbar.d.ts.map +1 -0
  115. package/dist/components/TableToolbar.js +189 -0
  116. package/dist/components/TableToolbar.js.map +1 -0
  117. package/dist/components/ToggleNodeView.d.ts +4 -0
  118. package/dist/components/ToggleNodeView.d.ts.map +1 -0
  119. package/dist/components/ToggleNodeView.js +39 -0
  120. package/dist/components/ToggleNodeView.js.map +1 -0
  121. package/dist/components/UrlInputDialog.d.ts +13 -0
  122. package/dist/components/UrlInputDialog.d.ts.map +1 -0
  123. package/dist/components/UrlInputDialog.js +39 -0
  124. package/dist/components/UrlInputDialog.js.map +1 -0
  125. package/dist/components/VideoBlockNodeView.d.ts +12 -0
  126. package/dist/components/VideoBlockNodeView.d.ts.map +1 -0
  127. package/dist/components/VideoBlockNodeView.js +97 -0
  128. package/dist/components/VideoBlockNodeView.js.map +1 -0
  129. package/dist/contexts/NodeManagerContext.d.ts +34 -0
  130. package/dist/contexts/NodeManagerContext.d.ts.map +1 -0
  131. package/dist/contexts/NodeManagerContext.js +31 -0
  132. package/dist/contexts/NodeManagerContext.js.map +1 -0
  133. package/dist/extensions/AttachNode.d.ts +7 -0
  134. package/dist/extensions/AttachNode.d.ts.map +1 -0
  135. package/dist/extensions/AttachNode.js +50 -0
  136. package/dist/extensions/AttachNode.js.map +1 -0
  137. package/dist/extensions/BlockOpsExtension.d.ts +17 -0
  138. package/dist/extensions/BlockOpsExtension.d.ts.map +1 -0
  139. package/dist/extensions/BlockOpsExtension.js +248 -0
  140. package/dist/extensions/BlockOpsExtension.js.map +1 -0
  141. package/dist/extensions/ButtonNode.d.ts +7 -0
  142. package/dist/extensions/ButtonNode.d.ts.map +1 -0
  143. package/dist/extensions/ButtonNode.js +59 -0
  144. package/dist/extensions/ButtonNode.js.map +1 -0
  145. package/dist/extensions/CalloutActionsExtension.d.ts +3 -0
  146. package/dist/extensions/CalloutActionsExtension.d.ts.map +1 -0
  147. package/dist/extensions/CalloutActionsExtension.js +41 -0
  148. package/dist/extensions/CalloutActionsExtension.js.map +1 -0
  149. package/dist/extensions/CalloutNode.d.ts +7 -0
  150. package/dist/extensions/CalloutNode.d.ts.map +1 -0
  151. package/dist/extensions/CalloutNode.js +48 -0
  152. package/dist/extensions/CalloutNode.js.map +1 -0
  153. package/dist/extensions/CardNode.d.ts +7 -0
  154. package/dist/extensions/CardNode.d.ts.map +1 -0
  155. package/dist/extensions/CardNode.js +64 -0
  156. package/dist/extensions/CardNode.js.map +1 -0
  157. package/dist/extensions/ChartNode.d.ts +3 -0
  158. package/dist/extensions/ChartNode.d.ts.map +1 -0
  159. package/dist/extensions/ChartNode.js +60 -0
  160. package/dist/extensions/ChartNode.js.map +1 -0
  161. package/dist/extensions/CodeBlockNode.d.ts +7 -0
  162. package/dist/extensions/CodeBlockNode.d.ts.map +1 -0
  163. package/dist/extensions/CodeBlockNode.js +57 -0
  164. package/dist/extensions/CodeBlockNode.js.map +1 -0
  165. package/dist/extensions/CodeReferenceNode.d.ts +3 -0
  166. package/dist/extensions/CodeReferenceNode.d.ts.map +1 -0
  167. package/dist/extensions/CodeReferenceNode.js +46 -0
  168. package/dist/extensions/CodeReferenceNode.js.map +1 -0
  169. package/dist/extensions/DateTimeNode.d.ts +3 -0
  170. package/dist/extensions/DateTimeNode.d.ts.map +1 -0
  171. package/dist/extensions/DateTimeNode.js +43 -0
  172. package/dist/extensions/DateTimeNode.js.map +1 -0
  173. package/dist/extensions/FloatImageBlockNode.d.ts +7 -0
  174. package/dist/extensions/FloatImageBlockNode.d.ts.map +1 -0
  175. package/dist/extensions/FloatImageBlockNode.js +73 -0
  176. package/dist/extensions/FloatImageBlockNode.js.map +1 -0
  177. package/dist/extensions/GridCardNode.d.ts +7 -0
  178. package/dist/extensions/GridCardNode.d.ts.map +1 -0
  179. package/dist/extensions/GridCardNode.js +56 -0
  180. package/dist/extensions/GridCardNode.js.map +1 -0
  181. package/dist/extensions/ImageBlockNode.d.ts +7 -0
  182. package/dist/extensions/ImageBlockNode.d.ts.map +1 -0
  183. package/dist/extensions/ImageBlockNode.js +76 -0
  184. package/dist/extensions/ImageBlockNode.js.map +1 -0
  185. package/dist/extensions/ImageNode.d.ts +10 -0
  186. package/dist/extensions/ImageNode.d.ts.map +1 -0
  187. package/dist/extensions/ImageNode.js +67 -0
  188. package/dist/extensions/ImageNode.js.map +1 -0
  189. package/dist/extensions/JotxLinkNode.d.ts +3 -0
  190. package/dist/extensions/JotxLinkNode.d.ts.map +1 -0
  191. package/dist/extensions/JotxLinkNode.js +43 -0
  192. package/dist/extensions/JotxLinkNode.js.map +1 -0
  193. package/dist/extensions/JotxTable.d.ts +9 -0
  194. package/dist/extensions/JotxTable.d.ts.map +1 -0
  195. package/dist/extensions/JotxTable.js +40 -0
  196. package/dist/extensions/JotxTable.js.map +1 -0
  197. package/dist/extensions/LinkNode.d.ts +7 -0
  198. package/dist/extensions/LinkNode.d.ts.map +1 -0
  199. package/dist/extensions/LinkNode.js +54 -0
  200. package/dist/extensions/LinkNode.js.map +1 -0
  201. package/dist/extensions/MathNode.d.ts +3 -0
  202. package/dist/extensions/MathNode.d.ts.map +1 -0
  203. package/dist/extensions/MathNode.js +61 -0
  204. package/dist/extensions/MathNode.js.map +1 -0
  205. package/dist/extensions/MermaidNode.d.ts +3 -0
  206. package/dist/extensions/MermaidNode.d.ts.map +1 -0
  207. package/dist/extensions/MermaidNode.js +59 -0
  208. package/dist/extensions/MermaidNode.js.map +1 -0
  209. package/dist/extensions/SearchExtension.d.ts +40 -0
  210. package/dist/extensions/SearchExtension.d.ts.map +1 -0
  211. package/dist/extensions/SearchExtension.js +186 -0
  212. package/dist/extensions/SearchExtension.js.map +1 -0
  213. package/dist/extensions/SectionNode.d.ts +11 -0
  214. package/dist/extensions/SectionNode.d.ts.map +1 -0
  215. package/dist/extensions/SectionNode.js +58 -0
  216. package/dist/extensions/SectionNode.js.map +1 -0
  217. package/dist/extensions/SlashMenuExtension.d.ts +17 -0
  218. package/dist/extensions/SlashMenuExtension.d.ts.map +1 -0
  219. package/dist/extensions/SlashMenuExtension.js +146 -0
  220. package/dist/extensions/SlashMenuExtension.js.map +1 -0
  221. package/dist/extensions/SpellCheckExtension.d.ts +14 -0
  222. package/dist/extensions/SpellCheckExtension.d.ts.map +1 -0
  223. package/dist/extensions/SpellCheckExtension.js +278 -0
  224. package/dist/extensions/SpellCheckExtension.js.map +1 -0
  225. package/dist/extensions/TableFilterExtension.d.ts +12 -0
  226. package/dist/extensions/TableFilterExtension.d.ts.map +1 -0
  227. package/dist/extensions/TableFilterExtension.js +75 -0
  228. package/dist/extensions/TableFilterExtension.js.map +1 -0
  229. package/dist/extensions/ToggleNode.d.ts +11 -0
  230. package/dist/extensions/ToggleNode.d.ts.map +1 -0
  231. package/dist/extensions/ToggleNode.js +58 -0
  232. package/dist/extensions/ToggleNode.js.map +1 -0
  233. package/dist/extensions/VideoBlockNode.d.ts +7 -0
  234. package/dist/extensions/VideoBlockNode.d.ts.map +1 -0
  235. package/dist/extensions/VideoBlockNode.js +95 -0
  236. package/dist/extensions/VideoBlockNode.js.map +1 -0
  237. package/dist/extensions/formatting.d.ts +2 -0
  238. package/dist/extensions/formatting.d.ts.map +1 -0
  239. package/dist/extensions/formatting.js +10 -0
  240. package/dist/extensions/formatting.js.map +1 -0
  241. package/dist/index.d.ts +61 -0
  242. package/dist/index.d.ts.map +1 -0
  243. package/dist/index.js +132 -0
  244. package/dist/index.js.map +1 -0
  245. package/dist/services/ImageStorage.d.ts +38 -0
  246. package/dist/services/ImageStorage.d.ts.map +1 -0
  247. package/dist/services/ImageStorage.js +32 -0
  248. package/dist/services/ImageStorage.js.map +1 -0
  249. package/dist/services/SpellCheckService 2.d.ts +50 -0
  250. package/dist/services/SpellCheckService 2.d.ts.map +1 -0
  251. package/dist/services/SpellCheckService 2.js +131 -0
  252. package/dist/services/SpellCheckService 2.js.map +1 -0
  253. package/dist/services/SpellCheckService.d.ts +50 -0
  254. package/dist/services/SpellCheckService.d.ts.map +1 -0
  255. package/dist/services/SpellCheckService.js +131 -0
  256. package/dist/services/SpellCheckService.js.map +1 -0
  257. package/dist/utils/logger.d.ts +14 -0
  258. package/dist/utils/logger.d.ts.map +1 -0
  259. package/dist/utils/logger.js +25 -0
  260. package/dist/utils/logger.js.map +1 -0
  261. package/package.json +56 -0
@@ -0,0 +1,1121 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JotxEditor = JotxEditor;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ /**
9
+ * JotxEditor - Main Tiptap editor with block support
10
+ * Modular, extensible, beautiful
11
+ */
12
+ const react_1 = require("react");
13
+ const react_2 = require("@tiptap/react");
14
+ const core_1 = require("@tiptap/core");
15
+ const starter_kit_1 = __importDefault(require("@tiptap/starter-kit"));
16
+ const extension_placeholder_1 = __importDefault(require("@tiptap/extension-placeholder"));
17
+ const extension_task_list_1 = __importDefault(require("@tiptap/extension-task-list"));
18
+ const extension_task_item_1 = __importDefault(require("@tiptap/extension-task-item"));
19
+ const extension_link_1 = __importDefault(require("@tiptap/extension-link"));
20
+ const extension_table_row_1 = __importDefault(require("@tiptap/extension-table-row"));
21
+ const extension_table_cell_1 = __importDefault(require("@tiptap/extension-table-cell"));
22
+ const extension_table_header_1 = __importDefault(require("@tiptap/extension-table-header"));
23
+ const extension_highlight_1 = __importDefault(require("@tiptap/extension-highlight"));
24
+ // Define Global Attribute Extension for Block IDs
25
+ const BlockIdExtension = core_1.Extension.create({
26
+ name: 'blockId',
27
+ addGlobalAttributes() {
28
+ return [
29
+ {
30
+ types: [
31
+ 'heading', 'paragraph', 'codeBlock', 'blockquote',
32
+ 'imageBlock', 'videoBlock', 'floatImageBlock',
33
+ 'math', 'mermaid', 'chart', 'datetime', 'linkCard',
34
+ 'callout', 'toggle', 'section', 'jotxlink',
35
+ 'bulletList', 'orderedList', 'listItem', 'taskItem', 'taskList',
36
+ 'horizontalRule', 'linkBlock', 'attach', 'table', 'tableRow', 'tableCell',
37
+ 'codeReference'
38
+ ],
39
+ attributes: {
40
+ blockId: {
41
+ default: null,
42
+ keepOnSplit: false,
43
+ renderHTML: attributes => {
44
+ if (!attributes.blockId)
45
+ return {};
46
+ return { 'data-id': attributes.blockId };
47
+ },
48
+ parseHTML: element => element.getAttribute('data-id'),
49
+ },
50
+ },
51
+ },
52
+ ];
53
+ },
54
+ });
55
+ const react_renderer_1 = require("@jotx-labs/react-renderer");
56
+ const SlashMenuExtension_1 = require("../extensions/SlashMenuExtension");
57
+ const formatting_1 = require("../extensions/formatting");
58
+ const CalloutNode_1 = require("../extensions/CalloutNode");
59
+ const AttachNode_1 = require("../extensions/AttachNode");
60
+ const MermaidNode_1 = require("../extensions/MermaidNode");
61
+ const ChartNode_1 = require("../extensions/ChartNode");
62
+ const JotxLinkNode_1 = require("../extensions/JotxLinkNode");
63
+ const ImageNode_1 = require("../extensions/ImageNode");
64
+ const ImageBlockNode_1 = require("../extensions/ImageBlockNode");
65
+ const vscode_api_1 = require("../bridge/vscode-api");
66
+ const FloatImageBlockNode_1 = require("../extensions/FloatImageBlockNode");
67
+ const VideoBlockNode_1 = require("../extensions/VideoBlockNode");
68
+ const CodeBlockNode_1 = require("../extensions/CodeBlockNode");
69
+ const CodeReferenceNode_1 = require("../extensions/CodeReferenceNode");
70
+ const JotxTable_1 = require("../extensions/JotxTable");
71
+ const TableFilterExtension_1 = require("../extensions/TableFilterExtension");
72
+ const DateTimeNode_1 = require("../extensions/DateTimeNode");
73
+ const MathNode_1 = require("../extensions/MathNode");
74
+ const BlockOpsExtension_1 = require("../extensions/BlockOpsExtension");
75
+ const CalloutActionsExtension_1 = require("../extensions/CalloutActionsExtension");
76
+ const LinkNode_1 = require("../extensions/LinkNode");
77
+ const GridCardNode_1 = require("../extensions/GridCardNode");
78
+ const CardNode_1 = require("../extensions/CardNode");
79
+ const ButtonNode_1 = require("../extensions/ButtonNode");
80
+ const ToggleNode_1 = require("../extensions/ToggleNode");
81
+ const UrlInputDialog_1 = require("./UrlInputDialog");
82
+ const SectionNode_1 = require("../extensions/SectionNode");
83
+ const SlashMenu_1 = require("./SlashMenu");
84
+ require("./JotxEditor.css");
85
+ require("./ImageVideoBlocks.css");
86
+ const bridge_utils_1 = require("../bridge/bridge-utils");
87
+ const logger_1 = require("../utils/logger");
88
+ const LinkDialog_1 = require("./LinkDialog");
89
+ const TableToolbar_1 = require("./TableToolbar");
90
+ const ImageToolbar_1 = require("./ImageToolbar");
91
+ const BlockMenu_1 = require("./BlockMenu");
92
+ const SearchBar_1 = require("./SearchBar");
93
+ const SearchExtension_1 = require("../extensions/SearchExtension");
94
+ const SpellCheckExtension_1 = require("../extensions/SpellCheckExtension");
95
+ const EditorRibbon_1 = require("./EditorRibbon");
96
+ require("./SearchHighlight.css");
97
+ require("./SpellCheck.css");
98
+ function JotxEditor({ documentId, documentType, tiptapDoc, onChange, ribbonExpanded = false }) {
99
+ logger_1.logger.log('🎨 JotxEditor component rendering (jotx 2.0)', { documentId, documentType, tiptapDoc });
100
+ // Debug: Check for jotxlink nodes in the document
101
+ const checkForJotxLink = (content) => {
102
+ let count = 0;
103
+ for (const node of content || []) {
104
+ if (node.type === 'jotxlink')
105
+ count++;
106
+ if (node.content)
107
+ count += checkForJotxLink(node.content);
108
+ }
109
+ return count;
110
+ };
111
+ const jotxLinkCount = tiptapDoc?.content ? checkForJotxLink(tiptapDoc.content) : 0;
112
+ logger_1.logger.log(`🔗 JotxLink nodes in document: ${jotxLinkCount}`);
113
+ const [showSlashMenu, setShowSlashMenu] = (0, react_1.useState)(false);
114
+ const [slashMenuPosition, setSlashMenuPosition] = (0, react_1.useState)({ x: 0, y: 0 });
115
+ const [slashMenuQuery, setSlashMenuQuery] = (0, react_1.useState)('');
116
+ const [calloutToolbar, setCalloutToolbar] = (0, react_1.useState)({ visible: false, x: 0, y: 0, variant: 'info' });
117
+ const [showLinkDialog, setShowLinkDialog] = (0, react_1.useState)(false);
118
+ const [showSearchBar, setShowSearchBar] = (0, react_1.useState)(false);
119
+ const [searchState, setSearchState] = (0, react_1.useState)({ currentMatch: 0, totalMatches: 0 });
120
+ const [showUrlInputDialog, setShowUrlInputDialog] = (0, react_1.useState)(false);
121
+ const [urlInputType, setUrlInputType] = (0, react_1.useState)('image');
122
+ const [showTemplatePicker, setShowTemplatePicker] = (0, react_1.useState)(false);
123
+ const [availableTemplates, setAvailableTemplates] = (0, react_1.useState)([]);
124
+ const [editorContext, setEditorContext] = (0, react_1.useState)('paragraph');
125
+ const isLoadingExternalChange = (0, react_1.useRef)(false);
126
+ logger_1.logger.log('⚙️ Creating Tiptap editor with initial content...');
127
+ logger_1.logger.log('🔍 Registering extensions:', {
128
+ ImageBlockNode: ImageBlockNode_1.ImageBlockNode.name,
129
+ FloatImageBlockNode: FloatImageBlockNode_1.FloatImageBlockNode.name,
130
+ VideoBlockNode: VideoBlockNode_1.VideoBlockNode.name
131
+ });
132
+ // Initialize Generic Adapter
133
+ const genericAdapter = new react_renderer_1.GenericTiptapAdapter();
134
+ const genericExtensions = genericAdapter.getExtensions();
135
+ const editor = (0, react_2.useEditor)({
136
+ content: tiptapDoc || { type: 'doc', content: [] },
137
+ extensions: [
138
+ BlockIdExtension, // Add Custom Global Attribute Extension
139
+ BlockOpsExtension_1.BlockOpsExtension,
140
+ CalloutActionsExtension_1.CalloutActionsExtension,
141
+ SearchExtension_1.SearchExtension,
142
+ starter_kit_1.default.configure({
143
+ codeBlock: false, // Registry (We use CodeBlockNode)
144
+ heading: {},
145
+ bulletList: {},
146
+ orderedList: {},
147
+ listItem: {},
148
+ dropcursor: {
149
+ color: 'var(--vscode-editorCursor-foreground)',
150
+ width: 2,
151
+ },
152
+ gapcursor: false,
153
+ }),
154
+ // Dynamic blocks from Registry
155
+ ...genericExtensions,
156
+ // Keep these for now until migrated
157
+ CodeReferenceNode_1.CodeReferenceNode,
158
+ CodeBlockNode_1.CodeBlockNode,
159
+ extension_link_1.default.configure({
160
+ openOnClick: true,
161
+ autolink: true,
162
+ linkOnPaste: false
163
+ }),
164
+ ImageNode_1.ImageNode.configure({
165
+ inline: false,
166
+ allowBase64: true
167
+ }),
168
+ ImageBlockNode_1.ImageBlockNode,
169
+ FloatImageBlockNode_1.FloatImageBlockNode,
170
+ VideoBlockNode_1.VideoBlockNode,
171
+ extension_task_list_1.default.configure({}),
172
+ extension_task_item_1.default.configure({
173
+ nested: true
174
+ }),
175
+ // Custom table nodes so we can persist filter/sort state as data attributes
176
+ JotxTable_1.JotxTable.configure({ resizable: true }),
177
+ extension_table_row_1.default,
178
+ extension_table_header_1.default,
179
+ extension_table_cell_1.default,
180
+ TableFilterExtension_1.TableFilterExtension,
181
+ extension_highlight_1.default.configure({
182
+ multicolor: false
183
+ }),
184
+ CalloutNode_1.CalloutNode,
185
+ AttachNode_1.AttachNode,
186
+ LinkNode_1.LinkNode,
187
+ MermaidNode_1.MermaidNode,
188
+ ChartNode_1.ChartNode,
189
+ JotxLinkNode_1.JotxLinkNode,
190
+ DateTimeNode_1.DateTimeNode,
191
+ MathNode_1.MathNode,
192
+ ToggleNode_1.ToggleNode,
193
+ SectionNode_1.SectionNode,
194
+ GridCardNode_1.GridCardNode,
195
+ CardNode_1.CardNode,
196
+ ButtonNode_1.ButtonNode,
197
+ extension_placeholder_1.default.configure({
198
+ placeholder: ({ node }) => {
199
+ if (node.type.name === 'heading') {
200
+ return 'Heading';
201
+ }
202
+ return 'Type \'/\' for commands...';
203
+ }
204
+ }),
205
+ ...formatting_1.FormattingExtensions,
206
+ SlashMenuExtension_1.SlashMenuExtension.configure({
207
+ onOpen: (position, query) => {
208
+ logger_1.logger.log('🎉 SlashMenu onOpen called!', { position, query });
209
+ setShowSlashMenu(true);
210
+ setSlashMenuPosition(position);
211
+ setSlashMenuQuery(query);
212
+ },
213
+ onClose: () => {
214
+ logger_1.logger.log('🚪 SlashMenu onClose called!');
215
+ setShowSlashMenu(false);
216
+ setSlashMenuQuery('');
217
+ },
218
+ onQueryChange: (query) => {
219
+ logger_1.logger.log('📝 SlashMenu onQueryChange:', query);
220
+ setSlashMenuQuery(query);
221
+ }
222
+ }),
223
+ SpellCheckExtension_1.SpellCheckExtension.configure({
224
+ enabled: true,
225
+ debounceMs: 300,
226
+ })
227
+ ],
228
+ editorProps: {
229
+ attributes: {
230
+ class: 'jotx-editor-content'
231
+ },
232
+ handlePaste: (_view, event) => {
233
+ try {
234
+ const dt = event.clipboardData;
235
+ if (!dt)
236
+ return false;
237
+ // 2) Check for images first (raster or SVG)
238
+ const items = Array.from(dt.items || []);
239
+ const imgItem = items.find((it) => it.kind === 'file' && it.type.startsWith('image/'));
240
+ if (imgItem) {
241
+ // Image file from clipboard - AUTO CREATE IMAGE BLOCK
242
+ const file = imgItem.getAsFile();
243
+ if (!file)
244
+ return false;
245
+ (async () => {
246
+ logger_1.logger.log('📋 PASTED IMAGE - Auto-creating image block', file);
247
+ // Convert to PNG (preferred) via canvas
248
+ let pngBase64 = null;
249
+ try {
250
+ const bmp = await window.createImageBitmap?.(file);
251
+ if (bmp) {
252
+ const canvas = document.createElement('canvas');
253
+ canvas.width = bmp.width;
254
+ canvas.height = bmp.height;
255
+ const ctx = canvas.getContext('2d');
256
+ ctx?.drawImage(bmp, 0, 0);
257
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
258
+ if (blob) {
259
+ pngBase64 = await new Promise((resolve, reject) => {
260
+ const reader = new FileReader();
261
+ reader.onerror = () => reject(new Error('Failed to read PNG blob'));
262
+ reader.onload = () => {
263
+ const s = (reader.result || '').toString();
264
+ const idx = s.indexOf(',');
265
+ resolve(idx >= 0 ? s.slice(idx + 1) : s);
266
+ };
267
+ reader.readAsDataURL(blob);
268
+ });
269
+ }
270
+ }
271
+ }
272
+ catch {
273
+ // ignore, fallback below
274
+ }
275
+ const base64 = pngBase64 ||
276
+ (await new Promise((resolve, reject) => {
277
+ const reader = new FileReader();
278
+ reader.onerror = () => reject(new Error('Failed to read image from clipboard'));
279
+ reader.onload = () => {
280
+ const s = (reader.result || '').toString();
281
+ const idx = s.indexOf(',');
282
+ resolve(idx >= 0 ? s.slice(idx + 1) : s);
283
+ };
284
+ reader.readAsDataURL(file);
285
+ }));
286
+ const res = await (0, bridge_utils_1.savePastedImage)({ mime: 'image/png', base64 });
287
+ // AUTO INSERT IMAGE BLOCK (not inline image)
288
+ editor
289
+ ?.chain()
290
+ .focus()
291
+ .insertContent([
292
+ {
293
+ type: 'imageBlock',
294
+ attrs: {
295
+ src: res.webviewUri,
296
+ alt: '',
297
+ width: 800,
298
+ height: 'auto',
299
+ align: 'center',
300
+ caption: ''
301
+ }
302
+ },
303
+ { type: 'paragraph' }
304
+ ])
305
+ .run();
306
+ logger_1.logger.log('✅ Image block auto-created!');
307
+ })();
308
+ event.preventDefault();
309
+ return true;
310
+ }
311
+ // 1) SVG paste as text (fallback)
312
+ const text = dt.getData('text/plain') || '';
313
+ if (text.trim().startsWith('<svg')) {
314
+ ;
315
+ (async () => {
316
+ const res = await (0, bridge_utils_1.savePastedImage)({ mime: 'image/svg+xml', text });
317
+ editor
318
+ ?.chain()
319
+ .focus()
320
+ .insertContent([
321
+ {
322
+ type: 'imageBlock',
323
+ attrs: {
324
+ src: res.webviewUri,
325
+ alt: '',
326
+ width: 800,
327
+ height: 'auto',
328
+ align: 'center',
329
+ caption: ''
330
+ }
331
+ },
332
+ { type: 'paragraph' }
333
+ ])
334
+ .run();
335
+ })();
336
+ event.preventDefault();
337
+ return true;
338
+ }
339
+ // No image found
340
+ return false;
341
+ }
342
+ catch (e) {
343
+ logger_1.logger.warn('paste handler failed', e);
344
+ return false;
345
+ }
346
+ }
347
+ },
348
+ onUpdate: ({ editor }) => {
349
+ // Don't trigger onChange if we're loading an external change
350
+ if (isLoadingExternalChange.current) {
351
+ logger_1.logger.log('⏭️ Skipping onChange - loading external change');
352
+ return;
353
+ }
354
+ // Send Tiptap JSON instead of HTML for proper conversion
355
+ const json = editor.getJSON();
356
+ onChange?.(JSON.stringify(json));
357
+ }
358
+ });
359
+ // Update search state from plugin
360
+ const updateSearchState = () => {
361
+ if (!editor) {
362
+ setSearchState({ currentMatch: 0, totalMatches: 0 });
363
+ return;
364
+ }
365
+ const state = editor.state;
366
+ const pluginState = SearchExtension_1.searchPluginKey.getState(state);
367
+ if (!pluginState) {
368
+ setSearchState({ currentMatch: 0, totalMatches: 0 });
369
+ return;
370
+ }
371
+ setSearchState({
372
+ currentMatch: pluginState.matches.length > 0 ? pluginState.currentIndex + 1 : 0,
373
+ totalMatches: pluginState.matches.length
374
+ });
375
+ };
376
+ // Search handlers
377
+ const handleSearch = (query) => {
378
+ if (!editor)
379
+ return;
380
+ editor.commands.setSearchTerm(query);
381
+ // Update state and scroll after plugin updates
382
+ setTimeout(() => {
383
+ updateSearchState();
384
+ const state = editor.state;
385
+ const pluginState = SearchExtension_1.searchPluginKey.getState(state);
386
+ if (pluginState && pluginState.matches.length > 0) {
387
+ scrollToCurrentMatch();
388
+ }
389
+ }, 10);
390
+ };
391
+ const handleNextMatch = () => {
392
+ if (!editor)
393
+ return;
394
+ editor.commands.nextSearchMatch();
395
+ setTimeout(() => {
396
+ updateSearchState();
397
+ scrollToCurrentMatch();
398
+ }, 10);
399
+ };
400
+ const handlePreviousMatch = () => {
401
+ if (!editor)
402
+ return;
403
+ editor.commands.previousSearchMatch();
404
+ setTimeout(() => {
405
+ updateSearchState();
406
+ scrollToCurrentMatch();
407
+ }, 10);
408
+ };
409
+ const handleCloseSearch = () => {
410
+ if (!editor)
411
+ return;
412
+ editor.commands.clearSearch();
413
+ setShowSearchBar(false);
414
+ setSearchState({ currentMatch: 0, totalMatches: 0 });
415
+ };
416
+ const scrollToCurrentMatch = () => {
417
+ if (!editor)
418
+ return;
419
+ const state = editor.state;
420
+ const pluginState = SearchExtension_1.searchPluginKey.getState(state);
421
+ if (!pluginState || pluginState.matches.length === 0)
422
+ return;
423
+ const currentMatch = pluginState.matches[pluginState.currentIndex];
424
+ if (!currentMatch)
425
+ return;
426
+ try {
427
+ // Find the DOM node at the current match position
428
+ const view = editor.view;
429
+ const domAtPos = view.domAtPos(currentMatch.from);
430
+ if (domAtPos.node) {
431
+ // Get the element (either the node itself or its parent if it's a text node)
432
+ const element = domAtPos.node.nodeType === Node.TEXT_NODE
433
+ ? domAtPos.node.parentElement
434
+ : domAtPos.node;
435
+ if (element && element instanceof HTMLElement) {
436
+ // Scroll the element into view with smooth behavior
437
+ element.scrollIntoView({
438
+ behavior: 'smooth',
439
+ block: 'center',
440
+ inline: 'nearest'
441
+ });
442
+ }
443
+ }
444
+ }
445
+ catch (error) {
446
+ logger_1.logger.warn('Failed to scroll to match:', error);
447
+ }
448
+ };
449
+ // Keyboard shortcuts for search
450
+ (0, react_1.useEffect)(() => {
451
+ const handleKeyDown = (e) => {
452
+ // Cmd/Ctrl+F to open search
453
+ if ((e.metaKey || e.ctrlKey) && e.key === 'f') {
454
+ e.preventDefault();
455
+ setShowSearchBar(true);
456
+ return;
457
+ }
458
+ // Escape to close search (only if search is open)
459
+ if (showSearchBar && e.key === 'Escape') {
460
+ e.preventDefault();
461
+ e.stopPropagation();
462
+ handleCloseSearch();
463
+ return;
464
+ }
465
+ };
466
+ window.addEventListener('keydown', handleKeyDown, true); // Use capture phase
467
+ return () => window.removeEventListener('keydown', handleKeyDown, true);
468
+ }, [showSearchBar, editor]);
469
+ (0, react_1.useEffect)(() => {
470
+ logger_1.logger.log('✅ Editor initialized:', !!editor);
471
+ if (editor && !editor.isDestroyed) {
472
+ logger_1.logger.log('🔍 Registered node types in schema:', Object.keys(editor.schema.nodes));
473
+ logger_1.logger.log('🔍 imageBlock node:', editor.schema.nodes.imageBlock);
474
+ logger_1.logger.log('🔍 floatImageBlock node:', editor.schema.nodes.floatImageBlock);
475
+ logger_1.logger.log('🔍 videoBlock node:', editor.schema.nodes.videoBlock);
476
+ const currentContent = JSON.stringify(editor.getJSON());
477
+ const newContent = JSON.stringify(tiptapDoc);
478
+ logger_1.logger.log('📄 Editor content update check:');
479
+ logger_1.logger.log(' - New tiptapDoc:', newContent?.substring(0, 200));
480
+ logger_1.logger.log(' - Current content:', currentContent?.substring(0, 200));
481
+ logger_1.logger.log(' - Are different?', currentContent !== newContent);
482
+ if (currentContent !== newContent) {
483
+ logger_1.logger.log(' ➡️ Setting content in Tiptap from external change');
484
+ isLoadingExternalChange.current = true;
485
+ // Update content without triggering onUpdate callback
486
+ editor.commands.setContent(tiptapDoc || { type: 'doc', content: [] });
487
+ // Reset flag immediately after setContent completes
488
+ setTimeout(() => {
489
+ isLoadingExternalChange.current = false;
490
+ logger_1.logger.log(' ✅ Content set, onChange re-enabled');
491
+ }, 50);
492
+ }
493
+ else {
494
+ logger_1.logger.log(' ⏭️ Content unchanged, skipping');
495
+ }
496
+ }
497
+ }, [tiptapDoc, editor]);
498
+ const handleSlashCommand = (command) => {
499
+ logger_1.logger.log('⚡ handleSlashCommand:', command);
500
+ if (!editor) {
501
+ logger_1.logger.error('❌ Editor not available!');
502
+ return;
503
+ }
504
+ setShowSlashMenu(false);
505
+ // Delete the "/query" trigger text before inserting the block (Notion-like UX)
506
+ try {
507
+ const pluginState = SlashMenuExtension_1.slashMenuPluginKey.getState(editor.state);
508
+ if (pluginState?.range?.from != null && pluginState?.range?.to != null) {
509
+ editor
510
+ .chain()
511
+ .focus()
512
+ .deleteRange({ from: pluginState.range.from, to: pluginState.range.to })
513
+ .run();
514
+ }
515
+ }
516
+ catch {
517
+ // non-fatal
518
+ }
519
+ switch (command) {
520
+ case 'heading1':
521
+ logger_1.logger.log('📝 Setting Heading 1');
522
+ editor.chain().focus().setNode('heading', { level: 1 }).run();
523
+ break;
524
+ case 'heading2':
525
+ logger_1.logger.log('📝 Setting Heading 2');
526
+ editor.chain().focus().setNode('heading', { level: 2 }).run();
527
+ break;
528
+ case 'heading3':
529
+ logger_1.logger.log('📝 Setting Heading 3');
530
+ editor.chain().focus().setNode('heading', { level: 3 }).run();
531
+ break;
532
+ case 'paragraph':
533
+ logger_1.logger.log('📝 Setting Paragraph');
534
+ editor.chain().focus().setParagraph().run();
535
+ break;
536
+ case 'bulletList':
537
+ logger_1.logger.log('📝 Setting Bullet List');
538
+ editor.chain().focus().toggleBulletList().run();
539
+ break;
540
+ case 'orderedList':
541
+ logger_1.logger.log('📝 Setting Ordered List');
542
+ editor.chain().focus().toggleOrderedList().run();
543
+ break;
544
+ case 'checklist':
545
+ logger_1.logger.log('📝 Setting Checklist');
546
+ editor.chain().focus().toggleTaskList().run();
547
+ break;
548
+ case 'blockquote':
549
+ logger_1.logger.log('📝 Setting Blockquote');
550
+ editor.chain().focus().toggleBlockquote().run();
551
+ break;
552
+ case 'codeBlock':
553
+ logger_1.logger.log('📝 Setting Code Block');
554
+ editor.chain().focus().setNode('codeBlock', { language: 'plaintext' }).run();
555
+ break;
556
+ case 'divider':
557
+ logger_1.logger.log('📝 Setting Divider');
558
+ editor.chain().focus().setHorizontalRule().run();
559
+ // Force update to ensure persistence
560
+ setTimeout(() => editor.commands.focus(), 10);
561
+ break;
562
+ case 'callout': {
563
+ editor.chain().focus().wrapIn('callout', { variant: 'info' }).run();
564
+ break;
565
+ }
566
+ case 'image': {
567
+ setUrlInputType('image');
568
+ setShowUrlInputDialog(true);
569
+ break;
570
+ }
571
+ case 'link': {
572
+ setShowLinkDialog(true);
573
+ break;
574
+ }
575
+ case 'attach': {
576
+ ;
577
+ (async () => {
578
+ const picked = await (0, bridge_utils_1.pickAttachmentFile)();
579
+ if (!picked)
580
+ return;
581
+ editor
582
+ .chain()
583
+ .focus()
584
+ .insertContent([
585
+ {
586
+ type: 'attach',
587
+ attrs: { path: picked.path },
588
+ content: [{ type: 'paragraph', content: [{ type: 'text', text: picked.name }] }]
589
+ },
590
+ { type: 'paragraph' }
591
+ ])
592
+ .run();
593
+ })();
594
+ break;
595
+ }
596
+ case 'table': {
597
+ editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run();
598
+ break;
599
+ }
600
+ case 'mermaid': {
601
+ editor
602
+ .chain()
603
+ .focus()
604
+ .insertContent([
605
+ {
606
+ type: 'mermaid',
607
+ attrs: {
608
+ src: 'flowchart TD\n A[Start] --> B{Decision}\n B -->|Yes| C[OK]\n B -->|No| D[Fix]\n'
609
+ }
610
+ },
611
+ { type: 'paragraph' }
612
+ ])
613
+ .run();
614
+ break;
615
+ }
616
+ case 'chart': {
617
+ const json = JSON.stringify({ labels: ['Jan', 'Feb', 'Mar', 'Apr'], datasets: [{ label: 'Events', data: [12, 19, 8, 15] }] }, null, 2);
618
+ editor.chain().focus().insertContent([{ type: 'chart', attrs: { chartType: 'bar', json } }, { type: 'paragraph' }]).run();
619
+ break;
620
+ }
621
+ case 'datetime': {
622
+ editor
623
+ .chain()
624
+ .focus()
625
+ .insertContent([{ type: 'datetime', attrs: { mode: 'datetime', value: '' } }, { type: 'paragraph' }])
626
+ .run();
627
+ break;
628
+ }
629
+ case 'math': {
630
+ editor.chain().focus().insertContent([{ type: 'math', attrs: { src: '', display: 'block' } }, { type: 'paragraph' }]).run();
631
+ break;
632
+ }
633
+ case 'jotxlink': {
634
+ editor.chain().focus().insertContent([{ type: 'jotxlink', attrs: { refType: '', refId: '' } }, { type: 'paragraph' }]).run();
635
+ break;
636
+ }
637
+ case 'toggle': {
638
+ editor
639
+ .chain()
640
+ .focus()
641
+ .insertContent([
642
+ {
643
+ type: 'toggle',
644
+ attrs: { title: 'Toggle', collapsed: false },
645
+ content: [{ type: 'paragraph' }]
646
+ },
647
+ { type: 'paragraph' }
648
+ ])
649
+ .run();
650
+ break;
651
+ }
652
+ case 'section': {
653
+ editor
654
+ .chain()
655
+ .focus()
656
+ .insertContent([
657
+ {
658
+ type: 'section',
659
+ attrs: { title: 'Section', collapsed: false },
660
+ content: [{ type: 'paragraph' }]
661
+ },
662
+ { type: 'paragraph' }
663
+ ])
664
+ .run();
665
+ break;
666
+ }
667
+ case 'gridcard': {
668
+ editor
669
+ .chain()
670
+ .focus()
671
+ .insertContent([
672
+ {
673
+ type: 'gridcard',
674
+ attrs: { layout: '2x2' },
675
+ content: [
676
+ { type: 'card', attrs: { title: 'Card 1', value: '', icon: '📝', color: 'teal', link: '' } },
677
+ { type: 'card', attrs: { title: 'Card 2', value: '', icon: '📊', color: 'blue', link: '' } },
678
+ { type: 'card', attrs: { title: 'Card 3', value: '', icon: '🚀', color: 'green', link: '' } },
679
+ { type: 'card', attrs: { title: 'Card 4', value: '', icon: '⚙️', color: 'purple', link: '' } }
680
+ ]
681
+ },
682
+ { type: 'paragraph' }
683
+ ])
684
+ .run();
685
+ break;
686
+ }
687
+ case 'button': {
688
+ editor
689
+ .chain()
690
+ .focus()
691
+ .insertContent([
692
+ { type: 'button', attrs: { label: 'Click Me', link: '', style: 'primary', align: 'left' } },
693
+ { type: 'paragraph' }
694
+ ])
695
+ .run();
696
+ break;
697
+ }
698
+ case 'imageBlock': {
699
+ logger_1.logger.log('📝 Inserting Image Block');
700
+ logger_1.logger.log('🔍 Editor state BEFORE insert:', editor.state.doc.toJSON());
701
+ logger_1.logger.log('🔍 Available node types:', Object.keys(editor.schema.nodes));
702
+ logger_1.logger.log('🔍 ImageBlock node exists?', !!editor.schema.nodes.imageBlock);
703
+ const content = {
704
+ type: 'imageBlock',
705
+ attrs: {
706
+ src: '',
707
+ alt: '',
708
+ width: 800,
709
+ height: 'auto',
710
+ align: 'center',
711
+ caption: ''
712
+ }
713
+ };
714
+ logger_1.logger.log('🔍 Inserting content:', JSON.stringify(content, null, 2));
715
+ try {
716
+ const result = editor
717
+ .chain()
718
+ .focus()
719
+ .insertContent([content, { type: 'paragraph' }])
720
+ .run();
721
+ logger_1.logger.log('✅ insertContent result:', result);
722
+ logger_1.logger.log('🔍 Editor state AFTER insert:', editor.state.doc.toJSON());
723
+ }
724
+ catch (error) {
725
+ logger_1.logger.error('❌ insertContent ERROR:', error);
726
+ }
727
+ break;
728
+ }
729
+ case 'floatImageBlock': {
730
+ logger_1.logger.log('📝 Inserting Float Image Block (CONTAINER)');
731
+ logger_1.logger.log('🔍 FloatImageBlock node exists?', !!editor.schema.nodes.floatImageBlock);
732
+ // Float image is now a CONTAINER with content inside
733
+ const content = {
734
+ type: 'floatImageBlock',
735
+ attrs: {
736
+ src: '',
737
+ alt: '',
738
+ width: 400,
739
+ height: 'auto',
740
+ float: 'left',
741
+ caption: ''
742
+ },
743
+ content: [
744
+ {
745
+ type: 'paragraph'
746
+ }
747
+ ]
748
+ };
749
+ logger_1.logger.log('🔍 Inserting content:', JSON.stringify(content, null, 2));
750
+ try {
751
+ const result = editor
752
+ .chain()
753
+ .insertContent([content, { type: 'paragraph' }])
754
+ .run();
755
+ logger_1.logger.log('✅ insertContent result:', result);
756
+ }
757
+ catch (error) {
758
+ logger_1.logger.error('❌ insertContent ERROR:', error);
759
+ }
760
+ break;
761
+ }
762
+ case 'videoBlock': {
763
+ logger_1.logger.log('📝 Inserting Video Block');
764
+ logger_1.logger.log('🔍 VideoBlock node exists?', !!editor.schema.nodes.videoBlock);
765
+ const content = {
766
+ type: 'videoBlock',
767
+ attrs: {
768
+ type: 'youtube',
769
+ url: '',
770
+ src: '',
771
+ title: '',
772
+ width: 800,
773
+ height: 450,
774
+ controls: 'true',
775
+ autoplay: 'false',
776
+ muted: 'false',
777
+ loop: 'false'
778
+ }
779
+ };
780
+ logger_1.logger.log('🔍 Inserting content:', JSON.stringify(content, null, 2));
781
+ try {
782
+ const result = editor
783
+ .chain()
784
+ .focus()
785
+ .insertContent([content, { type: 'paragraph' }])
786
+ .run();
787
+ logger_1.logger.log('✅ insertContent result:', result);
788
+ }
789
+ catch (error) {
790
+ logger_1.logger.error('❌ insertContent ERROR:', error);
791
+ }
792
+ break;
793
+ }
794
+ case 'properties': {
795
+ // Disallow nesting properties/table/headings inside table cells
796
+ if (editor.isActive('table')) {
797
+ return;
798
+ }
799
+ // Insert as a valid ProseMirror table node (NOT raw HTML) so it's editable everywhere.
800
+ // No samples: header + 1 empty row.
801
+ editor
802
+ .chain()
803
+ .focus()
804
+ .insertContent([
805
+ {
806
+ type: 'table',
807
+ content: [
808
+ {
809
+ type: 'tableRow',
810
+ content: [
811
+ { type: 'tableHeader', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Key' }] }] },
812
+ { type: 'tableHeader', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Value' }] }] },
813
+ { type: 'tableHeader', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Type' }] }] }
814
+ ]
815
+ },
816
+ {
817
+ type: 'tableRow',
818
+ content: [
819
+ { type: 'tableCell', content: [{ type: 'paragraph' }] },
820
+ { type: 'tableCell', content: [{ type: 'paragraph' }] },
821
+ { type: 'tableCell', content: [{ type: 'paragraph', content: [{ type: 'text', text: 'text' }] }] }
822
+ ]
823
+ }
824
+ ]
825
+ },
826
+ { type: 'paragraph' }
827
+ ])
828
+ .run();
829
+ break;
830
+ }
831
+ case 'codeReference': {
832
+ editor
833
+ .chain()
834
+ .focus()
835
+ .insertContent([
836
+ {
837
+ type: 'codeReference',
838
+ attrs: { path: '', lines: '', language: '' }
839
+ },
840
+ { type: 'paragraph' }
841
+ ])
842
+ .run();
843
+ break;
844
+ }
845
+ case 'useTemplate': {
846
+ // Request template list from backend
847
+ logger_1.logger.log('🔵 useTemplate clicked - requesting template list from backend');
848
+ const vscode = (0, vscode_api_1.getVSCodeAPI)();
849
+ vscode.postMessage({
850
+ type: 'requestTemplateList'
851
+ });
852
+ logger_1.logger.log('📤 postMessage sent: requestTemplateList');
853
+ break;
854
+ }
855
+ }
856
+ // CRITICAL: Force trigger save after any block insertion
857
+ // Tiptap's onUpdate may not fire synchronously, causing data loss on close
858
+ setTimeout(() => {
859
+ if (editor) {
860
+ const json = editor.getJSON();
861
+ onChange?.(JSON.stringify(json));
862
+ logger_1.logger.log('🔄 Flushed save after block insertion');
863
+ }
864
+ }, 50);
865
+ };
866
+ const getTableContext = () => {
867
+ if (!editor)
868
+ return { inTableCell: false, inPropertiesTable: false };
869
+ const inTableCell = editor.isActive('table');
870
+ if (!inTableCell)
871
+ return { inTableCell: false, inPropertiesTable: false };
872
+ try {
873
+ const { from } = editor.state.selection;
874
+ const domAt = editor.view.domAtPos(from).node;
875
+ const el = domAt?.nodeType === 1 ? domAt : domAt?.parentElement;
876
+ const table = el?.closest?.('table');
877
+ if (!table)
878
+ return { inTableCell: true, inPropertiesTable: false };
879
+ const ths = Array.from(table.querySelectorAll('th')).slice(0, 3);
880
+ const header = ths.map((t) => (t.textContent || '').trim().toLowerCase());
881
+ const inPropertiesTable = header[0] === 'key' && header[1] === 'value' && header[2] === 'type';
882
+ return { inTableCell: true, inPropertiesTable };
883
+ }
884
+ catch {
885
+ return { inTableCell: true, inPropertiesTable: false };
886
+ }
887
+ };
888
+ const getActiveCalloutVariant = () => {
889
+ if (!editor)
890
+ return null;
891
+ const { $from } = editor.state.selection;
892
+ for (let d = $from.depth; d > 0; d--) {
893
+ const node = $from.node(d);
894
+ if (node.type?.name === 'callout') {
895
+ return node.attrs?.variant || 'info';
896
+ }
897
+ }
898
+ return null;
899
+ };
900
+ const setCalloutVariant = (variant) => {
901
+ if (!editor)
902
+ return;
903
+ const { $from } = editor.state.selection;
904
+ for (let d = $from.depth; d > 0; d--) {
905
+ const node = $from.node(d);
906
+ if (node.type?.name === 'callout') {
907
+ const pos = $from.before(d);
908
+ const tr = editor.state.tr.setNodeMarkup(pos, undefined, {
909
+ ...node.attrs,
910
+ variant
911
+ });
912
+ editor.view.dispatch(tr);
913
+ editor.commands.focus();
914
+ setCalloutToolbar((s) => ({ ...s, variant }));
915
+ return;
916
+ }
917
+ }
918
+ };
919
+ // Floating callout toolbar (variant picker)
920
+ (0, react_1.useEffect)(() => {
921
+ if (!editor)
922
+ return;
923
+ const update = () => {
924
+ const variant = getActiveCalloutVariant();
925
+ if (!variant) {
926
+ setCalloutToolbar((s) => (s.visible ? { ...s, visible: false } : s));
927
+ return;
928
+ }
929
+ const v = variant;
930
+ const { from } = editor.state.selection;
931
+ const coords = editor.view.coordsAtPos(from);
932
+ const inTable = editor.isActive('table');
933
+ // Position above selection; if inside a table, offset further up so it doesn't overlap table toolbar
934
+ const x = Math.max(12, Math.min(window.innerWidth - 220, coords.left));
935
+ const y = Math.max(12, coords.top - (inTable ? 92 : 48));
936
+ setCalloutToolbar({ visible: true, x, y, variant: v || 'info' });
937
+ };
938
+ update();
939
+ editor.on('selectionUpdate', update);
940
+ editor.on('transaction', update);
941
+ return () => {
942
+ editor.off('selectionUpdate', update);
943
+ editor.off('transaction', update);
944
+ };
945
+ // eslint-disable-next-line react-hooks/exhaustive-deps
946
+ }, [editor]);
947
+ // Context detection for ribbon toolbar
948
+ (0, react_1.useEffect)(() => {
949
+ if (!editor)
950
+ return;
951
+ const detectContext = () => {
952
+ if (editor.isActive('table')) {
953
+ setEditorContext('table');
954
+ }
955
+ else if (editor.isActive('bulletList') || editor.isActive('orderedList') || editor.isActive('taskList')) {
956
+ setEditorContext('list');
957
+ }
958
+ else if (editor.isActive('mermaid')) {
959
+ setEditorContext('mermaid');
960
+ }
961
+ else if (editor.isActive('chart')) {
962
+ setEditorContext('chart');
963
+ }
964
+ else if (editor.isActive('codeBlock')) {
965
+ setEditorContext('code');
966
+ }
967
+ else if (editor.isActive('math')) {
968
+ setEditorContext('math');
969
+ }
970
+ else if (editor.isActive('callout')) {
971
+ setEditorContext('callout');
972
+ }
973
+ else if (editor.isActive('image') || editor.isActive('imageBlock')) {
974
+ setEditorContext('image');
975
+ }
976
+ else if (editor.isActive('heading')) {
977
+ setEditorContext('heading');
978
+ }
979
+ else {
980
+ setEditorContext('paragraph');
981
+ }
982
+ };
983
+ detectContext();
984
+ editor.on('selectionUpdate', detectContext);
985
+ editor.on('transaction', detectContext);
986
+ return () => {
987
+ editor.off('selectionUpdate', detectContext);
988
+ editor.off('transaction', detectContext);
989
+ };
990
+ }, [editor]);
991
+ // Update search state when editor state changes
992
+ (0, react_1.useEffect)(() => {
993
+ if (!editor || !showSearchBar)
994
+ return;
995
+ const updateHandler = () => {
996
+ updateSearchState();
997
+ };
998
+ editor.on('transaction', updateHandler);
999
+ return () => {
1000
+ editor.off('transaction', updateHandler);
1001
+ };
1002
+ }, [editor, showSearchBar]);
1003
+ // Listen for template list from backend
1004
+ (0, react_1.useEffect)(() => {
1005
+ const handleTemplateList = (event) => {
1006
+ logger_1.logger.log('📥 Received templateList event:', event.detail);
1007
+ const templates = event.detail;
1008
+ logger_1.logger.log('📋 Templates:', templates);
1009
+ setAvailableTemplates(templates);
1010
+ setShowTemplatePicker(true);
1011
+ logger_1.logger.log('✅ Modal should now be visible');
1012
+ };
1013
+ const handleTemplateContent = (event) => {
1014
+ logger_1.logger.log('📥 handleTemplateContent called');
1015
+ logger_1.logger.log('📥 event.detail:', event.detail);
1016
+ const tiptapBlocks = event.detail;
1017
+ logger_1.logger.log('📥 tiptapBlocks:', tiptapBlocks);
1018
+ logger_1.logger.log('📥 tiptapBlocks?.content:', tiptapBlocks?.content);
1019
+ logger_1.logger.log('📥 editor available:', !!editor);
1020
+ if (editor && tiptapBlocks && tiptapBlocks.content) {
1021
+ logger_1.logger.log('📥 Inserting template content:', tiptapBlocks.content.length, 'blocks');
1022
+ // Insert the template blocks at the current cursor position
1023
+ editor
1024
+ .chain()
1025
+ .focus()
1026
+ .insertContent(tiptapBlocks.content)
1027
+ .run();
1028
+ logger_1.logger.log('✅ Template inserted successfully');
1029
+ }
1030
+ else {
1031
+ logger_1.logger.warn('⚠️ Cannot insert template - missing data:', {
1032
+ hasEditor: !!editor,
1033
+ hasTiptapBlocks: !!tiptapBlocks,
1034
+ hasContent: !!tiptapBlocks?.content
1035
+ });
1036
+ }
1037
+ };
1038
+ window.addEventListener('templateList', handleTemplateList);
1039
+ window.addEventListener('templateContent', handleTemplateContent);
1040
+ return () => {
1041
+ window.removeEventListener('templateList', handleTemplateList);
1042
+ window.removeEventListener('templateContent', handleTemplateContent);
1043
+ };
1044
+ }, [editor]);
1045
+ // Open links externally (VS Code) instead of navigating inside the webview
1046
+ (0, react_1.useEffect)(() => {
1047
+ if (!editor)
1048
+ return;
1049
+ const el = editor.view.dom;
1050
+ const onClick = (e) => {
1051
+ const target = e.target;
1052
+ const a = target?.closest?.('a[href]');
1053
+ if (!a)
1054
+ return;
1055
+ const href = a.getAttribute('href') || '';
1056
+ if (!href)
1057
+ return;
1058
+ e.preventDefault();
1059
+ e.stopPropagation();
1060
+ (0, bridge_utils_1.openExternal)(href).catch(() => { });
1061
+ };
1062
+ el.addEventListener('click', onClick);
1063
+ return () => el.removeEventListener('click', onClick);
1064
+ }, [editor]);
1065
+ // Close link dialog on Escape
1066
+ (0, react_1.useEffect)(() => {
1067
+ if (!showLinkDialog)
1068
+ return;
1069
+ const onKeyDown = (e) => {
1070
+ if (e.key === 'Escape')
1071
+ setShowLinkDialog(false);
1072
+ };
1073
+ window.addEventListener('keydown', onKeyDown);
1074
+ return () => window.removeEventListener('keydown', onKeyDown);
1075
+ }, [showLinkDialog]);
1076
+ if (!editor) {
1077
+ return (0, jsx_runtime_1.jsx)("div", { className: "jotx-editor-loading", children: "Initializing editor..." });
1078
+ }
1079
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "jotx-editor", children: [(0, jsx_runtime_1.jsx)(EditorRibbon_1.EditorRibbon, { editor: editor, expanded: ribbonExpanded, context: editorContext }), (0, jsx_runtime_1.jsxs)("div", { className: "jotx-editor-wrapper", children: [(0, jsx_runtime_1.jsx)(LinkDialog_1.LinkDialog, { isOpen: showLinkDialog, onCancel: () => setShowLinkDialog(false), onSubmit: ({ href, label }) => {
1080
+ setShowLinkDialog(false);
1081
+ const text = (label || href).trim();
1082
+ editor
1083
+ ?.chain()
1084
+ .focus()
1085
+ // insert as its own paragraph so it roundtrips cleanly as `def link`
1086
+ .insertContent([
1087
+ {
1088
+ type: 'paragraph',
1089
+ content: [
1090
+ {
1091
+ type: 'text',
1092
+ text,
1093
+ marks: [{ type: 'link', attrs: { href } }]
1094
+ }
1095
+ ]
1096
+ },
1097
+ { type: 'paragraph' }
1098
+ ])
1099
+ .run();
1100
+ } }), calloutToolbar.visible && ((0, jsx_runtime_1.jsx)("div", { className: "jotx-callout-toolbar jotx-callout-toolbar-floating", style: { left: `${calloutToolbar.x}px`, top: `${calloutToolbar.y}px` }, children: ['info', 'warning', 'success', 'danger'].map((v) => ((0, jsx_runtime_1.jsx)("button", { "data-variant": v, className: `jotx-callout-chip ${calloutToolbar.variant === v ? 'active' : ''}`, onMouseDown: (e) => {
1101
+ // prevent editor losing selection
1102
+ e.preventDefault();
1103
+ }, onClick: (e) => {
1104
+ e.preventDefault();
1105
+ e.stopPropagation();
1106
+ setCalloutVariant(v);
1107
+ }, title: `Callout: ${v}`, children: v }, v))) })), (0, jsx_runtime_1.jsx)(BlockMenu_1.BlockMenu, { editor: editor }), (0, jsx_runtime_1.jsx)(TableToolbar_1.TableToolbar, { editor: editor }), (0, jsx_runtime_1.jsx)(ImageToolbar_1.ImageToolbar, { editor: editor }), (0, jsx_runtime_1.jsx)(SearchBar_1.SearchBar, { visible: showSearchBar, onSearch: handleSearch, onNext: handleNextMatch, onPrevious: handlePreviousMatch, onClose: handleCloseSearch, currentMatch: searchState.currentMatch, totalMatches: searchState.totalMatches }), (0, jsx_runtime_1.jsx)(react_2.EditorContent, { editor: editor })] }), showSlashMenu && ((0, jsx_runtime_1.jsx)(SlashMenu_1.SlashMenu, { documentType: documentType, query: slashMenuQuery, position: slashMenuPosition, context: getTableContext(), onSelect: handleSlashCommand, onClose: () => setShowSlashMenu(false) })), showUrlInputDialog && ((0, jsx_runtime_1.jsx)(UrlInputDialog_1.UrlInputDialog, { isOpen: showUrlInputDialog, title: 'Insert Image URL', onCancel: () => setShowUrlInputDialog(false), onSubmit: ({ url }) => {
1108
+ setShowUrlInputDialog(false);
1109
+ if (urlInputType === 'image') {
1110
+ editor.chain().focus().setImage({ src: url, alt: '', dataSrc: null, width: 70, align: 'left' }).run();
1111
+ }
1112
+ } })), showTemplatePicker && ((0, jsx_runtime_1.jsx)("div", { className: "jotx-modal-overlay", onClick: () => setShowTemplatePicker(false), children: (0, jsx_runtime_1.jsxs)("div", { className: "jotx-modal-content jotx-template-picker", onClick: (e) => e.stopPropagation(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "jotx-modal-header", children: [(0, jsx_runtime_1.jsx)("h3", { children: "\uD83D\uDCCB Choose a Template" }), (0, jsx_runtime_1.jsx)("button", { className: "jotx-modal-close", onClick: () => setShowTemplatePicker(false), children: "\u2715" })] }), (0, jsx_runtime_1.jsx)("div", { className: "jotx-template-list", children: availableTemplates.map((template) => ((0, jsx_runtime_1.jsxs)("button", { className: "jotx-template-item", onClick: () => {
1113
+ setShowTemplatePicker(false);
1114
+ const vscode = (0, vscode_api_1.getVSCodeAPI)();
1115
+ vscode.postMessage({
1116
+ type: 'requestTemplateContent',
1117
+ fileName: template.fileName
1118
+ });
1119
+ }, children: [(0, jsx_runtime_1.jsx)("span", { className: "jotx-template-icon", children: "\uD83D\uDCC4" }), (0, jsx_runtime_1.jsx)("span", { className: "jotx-template-name", children: template.name })] }, template.fileName))) })] }) }))] }));
1120
+ }
1121
+ //# sourceMappingURL=JotxEditor.js.map