@fiduswriter/editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (525) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +22 -0
  3. package/dist/bibliography/tools.js +53 -0
  4. package/dist/bibliography/tools.js.map +1 -0
  5. package/dist/citations/index.js +339 -0
  6. package/dist/citations/index.js.map +1 -0
  7. package/dist/citations/render.js +47 -0
  8. package/dist/citations/render.js.map +1 -0
  9. package/dist/clipboard/copy/index.js +2 -0
  10. package/dist/clipboard/copy/index.js.map +1 -0
  11. package/dist/clipboard/copy/schema.js +54 -0
  12. package/dist/clipboard/copy/schema.js.map +1 -0
  13. package/dist/clipboard/copy/serializers.js +132 -0
  14. package/dist/clipboard/copy/serializers.js.map +1 -0
  15. package/dist/clipboard/paste/fidus_writer.js +45 -0
  16. package/dist/clipboard/paste/fidus_writer.js.map +1 -0
  17. package/dist/clipboard/paste/general.js +76 -0
  18. package/dist/clipboard/paste/general.js.map +1 -0
  19. package/dist/clipboard/paste/google_docs.js +24 -0
  20. package/dist/clipboard/paste/google_docs.js.map +1 -0
  21. package/dist/clipboard/paste/html.js +68 -0
  22. package/dist/clipboard/paste/html.js.map +1 -0
  23. package/dist/clipboard/paste/index.js +3 -0
  24. package/dist/clipboard/paste/index.js.map +1 -0
  25. package/dist/clipboard/paste/libreoffice_writer.js +27 -0
  26. package/dist/clipboard/paste/libreoffice_writer.js.map +1 -0
  27. package/dist/clipboard/paste/microsoft_word.js +79 -0
  28. package/dist/clipboard/paste/microsoft_word.js.map +1 -0
  29. package/dist/clipboard/paste/text.js +41 -0
  30. package/dist/clipboard/paste/text.js.map +1 -0
  31. package/dist/collab/chat.js +154 -0
  32. package/dist/collab/chat.js.map +1 -0
  33. package/dist/collab/colors.js +62 -0
  34. package/dist/collab/colors.js.map +1 -0
  35. package/dist/collab/doc.js +1090 -0
  36. package/dist/collab/doc.js.map +1 -0
  37. package/dist/collab/index.js +75 -0
  38. package/dist/collab/index.js.map +1 -0
  39. package/dist/collab/merge/changeset.js +90 -0
  40. package/dist/collab/merge/changeset.js.map +1 -0
  41. package/dist/collab/merge/editor.js +708 -0
  42. package/dist/collab/merge/editor.js.map +1 -0
  43. package/dist/collab/merge/footnotes.js +149 -0
  44. package/dist/collab/merge/footnotes.js.map +1 -0
  45. package/dist/collab/merge/index.js +270 -0
  46. package/dist/collab/merge/index.js.map +1 -0
  47. package/dist/collab/merge/recreate_transform.js +259 -0
  48. package/dist/collab/merge/recreate_transform.js.map +1 -0
  49. package/dist/collab/merge/schema.js +117 -0
  50. package/dist/collab/merge/schema.js.map +1 -0
  51. package/dist/collab/merge/state_plugins/action.js +138 -0
  52. package/dist/collab/merge/state_plugins/action.js.map +1 -0
  53. package/dist/collab/merge/state_plugins/clipboard.js +32 -0
  54. package/dist/collab/merge/state_plugins/clipboard.js.map +1 -0
  55. package/dist/collab/merge/state_plugins/diff.js +541 -0
  56. package/dist/collab/merge/state_plugins/diff.js.map +1 -0
  57. package/dist/collab/merge/state_plugins/index.js +3 -0
  58. package/dist/collab/merge/state_plugins/index.js.map +1 -0
  59. package/dist/collab/merge/tools.js +175 -0
  60. package/dist/collab/merge/tools.js.map +1 -0
  61. package/dist/collab/templates.js +10 -0
  62. package/dist/collab/templates.js.map +1 -0
  63. package/dist/comments/comment.js +17 -0
  64. package/dist/comments/comment.js.map +1 -0
  65. package/dist/comments/editors/answer.js +43 -0
  66. package/dist/comments/editors/answer.js.map +1 -0
  67. package/dist/comments/editors/comment.js +278 -0
  68. package/dist/comments/editors/comment.js.map +1 -0
  69. package/dist/comments/editors/index.js +4 -0
  70. package/dist/comments/editors/index.js.map +1 -0
  71. package/dist/comments/editors/notify.js +13 -0
  72. package/dist/comments/editors/notify.js.map +1 -0
  73. package/dist/comments/editors/schema.js +60 -0
  74. package/dist/comments/editors/schema.js.map +1 -0
  75. package/dist/comments/index.js +11 -0
  76. package/dist/comments/index.js.map +1 -0
  77. package/dist/comments/interactions.js +379 -0
  78. package/dist/comments/interactions.js.map +1 -0
  79. package/dist/comments/store.js +438 -0
  80. package/dist/comments/store.js.map +1 -0
  81. package/dist/contacts/add_dialog.js +88 -0
  82. package/dist/contacts/add_dialog.js.map +1 -0
  83. package/dist/contacts/templates.js +23 -0
  84. package/dist/contacts/templates.js.map +1 -0
  85. package/dist/copyright_dialog/index.js +148 -0
  86. package/dist/copyright_dialog/index.js.map +1 -0
  87. package/dist/copyright_dialog/templates.js +48 -0
  88. package/dist/copyright_dialog/templates.js.map +1 -0
  89. package/dist/databases/bibliography.js +162 -0
  90. package/dist/databases/bibliography.js.map +1 -0
  91. package/dist/databases/images.js +260 -0
  92. package/dist/databases/images.js.map +1 -0
  93. package/dist/databases/index.js +55 -0
  94. package/dist/databases/index.js.map +1 -0
  95. package/dist/dialogs/citation.js +321 -0
  96. package/dist/dialogs/citation.js.map +1 -0
  97. package/dist/dialogs/code_block.js +192 -0
  98. package/dist/dialogs/code_block.js.map +1 -0
  99. package/dist/dialogs/contributor.js +132 -0
  100. package/dist/dialogs/contributor.js.map +1 -0
  101. package/dist/dialogs/figure.js +378 -0
  102. package/dist/dialogs/figure.js.map +1 -0
  103. package/dist/dialogs/index.js +11 -0
  104. package/dist/dialogs/index.js.map +1 -0
  105. package/dist/dialogs/language.js +47 -0
  106. package/dist/dialogs/language.js.map +1 -0
  107. package/dist/dialogs/link.js +243 -0
  108. package/dist/dialogs/link.js.map +1 -0
  109. package/dist/dialogs/math.js +121 -0
  110. package/dist/dialogs/math.js.map +1 -0
  111. package/dist/dialogs/ordered_list.js +67 -0
  112. package/dist/dialogs/ordered_list.js.map +1 -0
  113. package/dist/dialogs/revision.js +36 -0
  114. package/dist/dialogs/revision.js.map +1 -0
  115. package/dist/dialogs/subsup.js +109 -0
  116. package/dist/dialogs/subsup.js.map +1 -0
  117. package/dist/dialogs/table.js +217 -0
  118. package/dist/dialogs/table.js.map +1 -0
  119. package/dist/dialogs/templates.js +366 -0
  120. package/dist/dialogs/templates.js.map +1 -0
  121. package/dist/document_template/exporter.js +83 -0
  122. package/dist/document_template/exporter.js.map +1 -0
  123. package/dist/document_template/index.js +327 -0
  124. package/dist/document_template/index.js.map +1 -0
  125. package/dist/documents/access_rights/index.js +532 -0
  126. package/dist/documents/access_rights/index.js.map +1 -0
  127. package/dist/documents/access_rights/templates.js +150 -0
  128. package/dist/documents/access_rights/templates.js.map +1 -0
  129. package/dist/e2ee/encryptor.js +201 -0
  130. package/dist/e2ee/encryptor.js.map +1 -0
  131. package/dist/e2ee/key-manager.js +171 -0
  132. package/dist/e2ee/key-manager.js.map +1 -0
  133. package/dist/e2ee/passphrase-crypto.js +365 -0
  134. package/dist/e2ee/passphrase-crypto.js.map +1 -0
  135. package/dist/e2ee/passphrase-dialog.js +548 -0
  136. package/dist/e2ee/passphrase-dialog.js.map +1 -0
  137. package/dist/e2ee/passphrase-manager.js +350 -0
  138. package/dist/e2ee/passphrase-manager.js.map +1 -0
  139. package/dist/e2ee/password-dialog.js +552 -0
  140. package/dist/e2ee/password-dialog.js.map +1 -0
  141. package/dist/e2ee/snapshot-manager.js +238 -0
  142. package/dist/e2ee/snapshot-manager.js.map +1 -0
  143. package/dist/exporter/native/copy.js +29 -0
  144. package/dist/exporter/native/copy.js.map +1 -0
  145. package/dist/exporter/native/file.js +19 -0
  146. package/dist/exporter/native/file.js.map +1 -0
  147. package/dist/exporter/native/index.js +4 -0
  148. package/dist/exporter/native/index.js.map +1 -0
  149. package/dist/exporter/native/revision.js +54 -0
  150. package/dist/exporter/native/revision.js.map +1 -0
  151. package/dist/exporter/native/zip.js +2 -0
  152. package/dist/exporter/native/zip.js.map +1 -0
  153. package/dist/footnotes/editor.js +180 -0
  154. package/dist/footnotes/editor.js.map +1 -0
  155. package/dist/footnotes/index.js +15 -0
  156. package/dist/footnotes/index.js.map +1 -0
  157. package/dist/footnotes/layout.js +125 -0
  158. package/dist/footnotes/layout.js.map +1 -0
  159. package/dist/footnotes/state_plugins/access_rights.js +25 -0
  160. package/dist/footnotes/state_plugins/access_rights.js.map +1 -0
  161. package/dist/footnotes/state_plugins/index.js +2 -0
  162. package/dist/footnotes/state_plugins/index.js.map +1 -0
  163. package/dist/images/edit_dialog/edit_dialog/index.js +186 -0
  164. package/dist/images/edit_dialog/edit_dialog/index.js.map +1 -0
  165. package/dist/images/edit_dialog/edit_dialog/model.js +152 -0
  166. package/dist/images/edit_dialog/edit_dialog/model.js.map +1 -0
  167. package/dist/images/edit_dialog/edit_dialog/templates.js +33 -0
  168. package/dist/images/edit_dialog/edit_dialog/templates.js.map +1 -0
  169. package/dist/images/edit_dialog/model.js +6 -0
  170. package/dist/images/edit_dialog/model.js.map +1 -0
  171. package/dist/images/selection_dialog/index.js +149 -0
  172. package/dist/images/selection_dialog/index.js.map +1 -0
  173. package/dist/index.js +1002 -0
  174. package/dist/index.js.map +1 -0
  175. package/dist/keymap.js +73 -0
  176. package/dist/keymap.js.map +1 -0
  177. package/dist/marginboxes/index.js +674 -0
  178. package/dist/marginboxes/index.js.map +1 -0
  179. package/dist/marginboxes/templates.js +447 -0
  180. package/dist/marginboxes/templates.js.map +1 -0
  181. package/dist/menus/code_block/index.js +2 -0
  182. package/dist/menus/code_block/index.js.map +1 -0
  183. package/dist/menus/code_block/model.js +49 -0
  184. package/dist/menus/code_block/model.js.map +1 -0
  185. package/dist/menus/figure/index.js +3 -0
  186. package/dist/menus/figure/index.js.map +1 -0
  187. package/dist/menus/figure/model.js +80 -0
  188. package/dist/menus/figure/model.js.map +1 -0
  189. package/dist/menus/figure/utils.js +6 -0
  190. package/dist/menus/figure/utils.js.map +1 -0
  191. package/dist/menus/headerbar/index.js +3 -0
  192. package/dist/menus/headerbar/index.js.map +1 -0
  193. package/dist/menus/headerbar/model.js +743 -0
  194. package/dist/menus/headerbar/model.js.map +1 -0
  195. package/dist/menus/headerbar/view.js +579 -0
  196. package/dist/menus/headerbar/view.js.map +1 -0
  197. package/dist/menus/image/index.js +2 -0
  198. package/dist/menus/image/index.js.map +1 -0
  199. package/dist/menus/image/model.js +32 -0
  200. package/dist/menus/image/model.js.map +1 -0
  201. package/dist/menus/index.js +10 -0
  202. package/dist/menus/index.js.map +1 -0
  203. package/dist/menus/navigator/index.js +2 -0
  204. package/dist/menus/navigator/index.js.map +1 -0
  205. package/dist/menus/navigator/model.js +29 -0
  206. package/dist/menus/navigator/model.js.map +1 -0
  207. package/dist/menus/ordered_list/index.js +2 -0
  208. package/dist/menus/ordered_list/index.js.map +1 -0
  209. package/dist/menus/ordered_list/model.js +17 -0
  210. package/dist/menus/ordered_list/model.js.map +1 -0
  211. package/dist/menus/selection/index.js +3 -0
  212. package/dist/menus/selection/index.js.map +1 -0
  213. package/dist/menus/selection/model.js +108 -0
  214. package/dist/menus/selection/model.js.map +1 -0
  215. package/dist/menus/selection/view.js +125 -0
  216. package/dist/menus/selection/view.js.map +1 -0
  217. package/dist/menus/table/index.js +2 -0
  218. package/dist/menus/table/index.js.map +1 -0
  219. package/dist/menus/table/model.js +319 -0
  220. package/dist/menus/table/model.js.map +1 -0
  221. package/dist/menus/toolbar/index.js +3 -0
  222. package/dist/menus/toolbar/index.js.map +1 -0
  223. package/dist/menus/toolbar/model.js +747 -0
  224. package/dist/menus/toolbar/model.js.map +1 -0
  225. package/dist/menus/toolbar/view.js +314 -0
  226. package/dist/menus/toolbar/view.js.map +1 -0
  227. package/dist/navigator/index.js +435 -0
  228. package/dist/navigator/index.js.map +1 -0
  229. package/dist/no_collab_save/index.js +169 -0
  230. package/dist/no_collab_save/index.js.map +1 -0
  231. package/dist/plugins/citation_dialog/index.js +2 -0
  232. package/dist/plugins/citation_dialog/index.js.map +1 -0
  233. package/dist/plugins/editor/index.js +2 -0
  234. package/dist/plugins/editor/index.js.map +1 -0
  235. package/dist/state_plugins/access_rights.js +30 -0
  236. package/dist/state_plugins/access_rights.js.map +1 -0
  237. package/dist/state_plugins/citation_render.js +78 -0
  238. package/dist/state_plugins/citation_render.js.map +1 -0
  239. package/dist/state_plugins/clipboard.js +217 -0
  240. package/dist/state_plugins/clipboard.js.map +1 -0
  241. package/dist/state_plugins/code_block.js +165 -0
  242. package/dist/state_plugins/code_block.js.map +1 -0
  243. package/dist/state_plugins/collab_carets.js +115 -0
  244. package/dist/state_plugins/collab_carets.js.map +1 -0
  245. package/dist/state_plugins/comments.js +197 -0
  246. package/dist/state_plugins/comments.js.map +1 -0
  247. package/dist/state_plugins/contributor_input/add_button.js +101 -0
  248. package/dist/state_plugins/contributor_input/add_button.js.map +1 -0
  249. package/dist/state_plugins/contributor_input/dropup.js +273 -0
  250. package/dist/state_plugins/contributor_input/dropup.js.map +1 -0
  251. package/dist/state_plugins/contributor_input/helpers.js +23 -0
  252. package/dist/state_plugins/contributor_input/helpers.js.map +1 -0
  253. package/dist/state_plugins/contributor_input/index.js +3 -0
  254. package/dist/state_plugins/contributor_input/index.js.map +1 -0
  255. package/dist/state_plugins/contributor_input/node_view.js +76 -0
  256. package/dist/state_plugins/contributor_input/node_view.js.map +1 -0
  257. package/dist/state_plugins/contributor_input/plugin.js +236 -0
  258. package/dist/state_plugins/contributor_input/plugin.js.map +1 -0
  259. package/dist/state_plugins/document_template.js +213 -0
  260. package/dist/state_plugins/document_template.js.map +1 -0
  261. package/dist/state_plugins/figure.js +90 -0
  262. package/dist/state_plugins/figure.js.map +1 -0
  263. package/dist/state_plugins/footnote_markers.js +187 -0
  264. package/dist/state_plugins/footnote_markers.js.map +1 -0
  265. package/dist/state_plugins/headerbar.js +10 -0
  266. package/dist/state_plugins/headerbar.js.map +1 -0
  267. package/dist/state_plugins/index.js +27 -0
  268. package/dist/state_plugins/index.js.map +1 -0
  269. package/dist/state_plugins/inline_math.js +719 -0
  270. package/dist/state_plugins/inline_math.js.map +1 -0
  271. package/dist/state_plugins/inline_reference/dropup.js +182 -0
  272. package/dist/state_plugins/inline_reference/dropup.js.map +1 -0
  273. package/dist/state_plugins/inline_reference/index.js +2 -0
  274. package/dist/state_plugins/inline_reference/index.js.map +1 -0
  275. package/dist/state_plugins/inline_reference/plugin.js +1134 -0
  276. package/dist/state_plugins/inline_reference/plugin.js.map +1 -0
  277. package/dist/state_plugins/jump_hidden_nodes.js +47 -0
  278. package/dist/state_plugins/jump_hidden_nodes.js.map +1 -0
  279. package/dist/state_plugins/links.js +622 -0
  280. package/dist/state_plugins/links.js.map +1 -0
  281. package/dist/state_plugins/marginboxes.js +13 -0
  282. package/dist/state_plugins/marginboxes.js.map +1 -0
  283. package/dist/state_plugins/ordered_list_menu.js +72 -0
  284. package/dist/state_plugins/ordered_list_menu.js.map +1 -0
  285. package/dist/state_plugins/placeholders.js +98 -0
  286. package/dist/state_plugins/placeholders.js.map +1 -0
  287. package/dist/state_plugins/search.js +212 -0
  288. package/dist/state_plugins/search.js.map +1 -0
  289. package/dist/state_plugins/selection_menu.js +10 -0
  290. package/dist/state_plugins/selection_menu.js.map +1 -0
  291. package/dist/state_plugins/settings.js +191 -0
  292. package/dist/state_plugins/settings.js.map +1 -0
  293. package/dist/state_plugins/table.js +104 -0
  294. package/dist/state_plugins/table.js.map +1 -0
  295. package/dist/state_plugins/tag_input/helpers.js +55 -0
  296. package/dist/state_plugins/tag_input/helpers.js.map +1 -0
  297. package/dist/state_plugins/tag_input/index.js +3 -0
  298. package/dist/state_plugins/tag_input/index.js.map +1 -0
  299. package/dist/state_plugins/tag_input/node_view.js +77 -0
  300. package/dist/state_plugins/tag_input/node_view.js.map +1 -0
  301. package/dist/state_plugins/tag_input/plugin.js +199 -0
  302. package/dist/state_plugins/tag_input/plugin.js.map +1 -0
  303. package/dist/state_plugins/tag_input/tag_editor.js +159 -0
  304. package/dist/state_plugins/tag_input/tag_editor.js.map +1 -0
  305. package/dist/state_plugins/tag_input/tag_editor_plugins.js +61 -0
  306. package/dist/state_plugins/tag_input/tag_editor_plugins.js.map +1 -0
  307. package/dist/state_plugins/toc_render.js +142 -0
  308. package/dist/state_plugins/toc_render.js.map +1 -0
  309. package/dist/state_plugins/toolbar.js +10 -0
  310. package/dist/state_plugins/toolbar.js.map +1 -0
  311. package/dist/state_plugins/track/find_selected_changes.js +82 -0
  312. package/dist/state_plugins/track/find_selected_changes.js.map +1 -0
  313. package/dist/state_plugins/track/helpers.js +67 -0
  314. package/dist/state_plugins/track/helpers.js.map +1 -0
  315. package/dist/state_plugins/track/index.js +3 -0
  316. package/dist/state_plugins/track/index.js.map +1 -0
  317. package/dist/state_plugins/track/plugin.js +114 -0
  318. package/dist/state_plugins/track/plugin.js.map +1 -0
  319. package/dist/tools/index.js +4 -0
  320. package/dist/tools/index.js.map +1 -0
  321. package/dist/tools/key_bindings/index.js +20 -0
  322. package/dist/tools/key_bindings/index.js.map +1 -0
  323. package/dist/tools/key_bindings/templates.js +68 -0
  324. package/dist/tools/key_bindings/templates.js.map +1 -0
  325. package/dist/tools/search_replace/index.js +275 -0
  326. package/dist/tools/search_replace/index.js.map +1 -0
  327. package/dist/tools/search_replace/templates.js +18 -0
  328. package/dist/tools/search_replace/templates.js.map +1 -0
  329. package/dist/tools/word_count/index.js +40 -0
  330. package/dist/tools/word_count/index.js.map +1 -0
  331. package/dist/tools/word_count/templates.js +27 -0
  332. package/dist/tools/word_count/templates.js.map +1 -0
  333. package/dist/track/accept.js +66 -0
  334. package/dist/track/accept.js.map +1 -0
  335. package/dist/track/accept_all.js +51 -0
  336. package/dist/track/accept_all.js.map +1 -0
  337. package/dist/track/accept_all_no_insertions.js +63 -0
  338. package/dist/track/accept_all_no_insertions.js.map +1 -0
  339. package/dist/track/amend_transaction.js +390 -0
  340. package/dist/track/amend_transaction.js.map +1 -0
  341. package/dist/track/delete.js +62 -0
  342. package/dist/track/delete.js.map +1 -0
  343. package/dist/track/index.js +6 -0
  344. package/dist/track/index.js.map +1 -0
  345. package/dist/track/mod.js +86 -0
  346. package/dist/track/mod.js.map +1 -0
  347. package/dist/track/reject.js +67 -0
  348. package/dist/track/reject.js.map +1 -0
  349. package/dist/track/reject_all.js +56 -0
  350. package/dist/track/reject_all.js.map +1 -0
  351. package/package.json +80 -0
  352. package/src/bibliography/tools.js +58 -0
  353. package/src/citations/index.js +443 -0
  354. package/src/citations/render.js +68 -0
  355. package/src/clipboard/copy/index.js +1 -0
  356. package/src/clipboard/copy/schema.js +62 -0
  357. package/src/clipboard/copy/serializers.js +179 -0
  358. package/src/clipboard/paste/fidus_writer.js +55 -0
  359. package/src/clipboard/paste/general.js +80 -0
  360. package/src/clipboard/paste/google_docs.js +26 -0
  361. package/src/clipboard/paste/html.js +76 -0
  362. package/src/clipboard/paste/index.js +2 -0
  363. package/src/clipboard/paste/libreoffice_writer.js +32 -0
  364. package/src/clipboard/paste/microsoft_word.js +87 -0
  365. package/src/clipboard/paste/text.js +58 -0
  366. package/src/collab/chat.js +195 -0
  367. package/src/collab/colors.js +67 -0
  368. package/src/collab/doc.js +1436 -0
  369. package/src/collab/index.js +81 -0
  370. package/src/collab/merge/changeset.js +103 -0
  371. package/src/collab/merge/editor.js +1065 -0
  372. package/src/collab/merge/footnotes.js +175 -0
  373. package/src/collab/merge/index.js +401 -0
  374. package/src/collab/merge/recreate_transform.js +326 -0
  375. package/src/collab/merge/schema.js +135 -0
  376. package/src/collab/merge/state_plugins/action.js +171 -0
  377. package/src/collab/merge/state_plugins/clipboard.js +32 -0
  378. package/src/collab/merge/state_plugins/diff.js +780 -0
  379. package/src/collab/merge/state_plugins/index.js +2 -0
  380. package/src/collab/merge/tools.js +217 -0
  381. package/src/collab/templates.js +10 -0
  382. package/src/comments/comment.js +29 -0
  383. package/src/comments/editors/answer.js +55 -0
  384. package/src/comments/editors/comment.js +318 -0
  385. package/src/comments/editors/index.js +4 -0
  386. package/src/comments/editors/notify.js +13 -0
  387. package/src/comments/editors/schema.js +65 -0
  388. package/src/comments/index.js +11 -0
  389. package/src/comments/interactions.js +465 -0
  390. package/src/comments/store.js +513 -0
  391. package/src/contacts/add_dialog.js +110 -0
  392. package/src/contacts/templates.js +28 -0
  393. package/src/copyright_dialog/index.js +179 -0
  394. package/src/copyright_dialog/templates.js +56 -0
  395. package/src/databases/bibliography.js +179 -0
  396. package/src/databases/images.js +320 -0
  397. package/src/databases/index.js +69 -0
  398. package/src/dialogs/citation.js +428 -0
  399. package/src/dialogs/code_block.js +242 -0
  400. package/src/dialogs/contributor.js +173 -0
  401. package/src/dialogs/figure.js +479 -0
  402. package/src/dialogs/index.js +10 -0
  403. package/src/dialogs/language.js +58 -0
  404. package/src/dialogs/link.js +358 -0
  405. package/src/dialogs/math.js +148 -0
  406. package/src/dialogs/ordered_list.js +84 -0
  407. package/src/dialogs/revision.js +43 -0
  408. package/src/dialogs/subsup.js +116 -0
  409. package/src/dialogs/table.js +258 -0
  410. package/src/dialogs/templates.js +426 -0
  411. package/src/document_template/exporter.js +97 -0
  412. package/src/document_template/index.js +483 -0
  413. package/src/documents/access_rights/index.js +693 -0
  414. package/src/documents/access_rights/templates.js +178 -0
  415. package/src/e2ee/encryptor.js +228 -0
  416. package/src/e2ee/key-manager.js +202 -0
  417. package/src/e2ee/passphrase-crypto.js +606 -0
  418. package/src/e2ee/passphrase-dialog.js +655 -0
  419. package/src/e2ee/passphrase-manager.js +502 -0
  420. package/src/e2ee/password-dialog.js +652 -0
  421. package/src/e2ee/snapshot-manager.js +305 -0
  422. package/src/exporter/native/copy.js +54 -0
  423. package/src/exporter/native/file.js +37 -0
  424. package/src/exporter/native/index.js +3 -0
  425. package/src/exporter/native/revision.js +86 -0
  426. package/src/exporter/native/zip.js +1 -0
  427. package/src/footnotes/editor.js +227 -0
  428. package/src/footnotes/index.js +16 -0
  429. package/src/footnotes/layout.js +190 -0
  430. package/src/footnotes/state_plugins/access_rights.js +39 -0
  431. package/src/footnotes/state_plugins/index.js +1 -0
  432. package/src/images/edit_dialog/edit_dialog/index.js +222 -0
  433. package/src/images/edit_dialog/edit_dialog/model.js +183 -0
  434. package/src/images/edit_dialog/edit_dialog/templates.js +41 -0
  435. package/src/images/edit_dialog/model.js +5 -0
  436. package/src/images/selection_dialog/index.js +176 -0
  437. package/src/index.js +1252 -0
  438. package/src/keymap.js +102 -0
  439. package/src/marginboxes/index.js +946 -0
  440. package/src/marginboxes/templates.js +573 -0
  441. package/src/menus/code_block/index.js +1 -0
  442. package/src/menus/code_block/model.js +52 -0
  443. package/src/menus/figure/index.js +2 -0
  444. package/src/menus/figure/model.js +91 -0
  445. package/src/menus/figure/utils.js +5 -0
  446. package/src/menus/headerbar/index.js +2 -0
  447. package/src/menus/headerbar/model.js +1077 -0
  448. package/src/menus/headerbar/view.js +675 -0
  449. package/src/menus/image/index.js +1 -0
  450. package/src/menus/image/model.js +32 -0
  451. package/src/menus/index.js +9 -0
  452. package/src/menus/navigator/index.js +1 -0
  453. package/src/menus/navigator/model.js +28 -0
  454. package/src/menus/ordered_list/index.js +1 -0
  455. package/src/menus/ordered_list/model.js +19 -0
  456. package/src/menus/selection/index.js +2 -0
  457. package/src/menus/selection/model.js +134 -0
  458. package/src/menus/selection/view.js +154 -0
  459. package/src/menus/table/index.js +1 -0
  460. package/src/menus/table/model.js +414 -0
  461. package/src/menus/toolbar/index.js +2 -0
  462. package/src/menus/toolbar/model.js +920 -0
  463. package/src/menus/toolbar/view.js +354 -0
  464. package/src/navigator/index.js +515 -0
  465. package/src/no_collab_save/index.js +209 -0
  466. package/src/plugins/citation_dialog/index.js +1 -0
  467. package/src/plugins/editor/index.js +1 -0
  468. package/src/state_plugins/access_rights.js +47 -0
  469. package/src/state_plugins/citation_render.js +88 -0
  470. package/src/state_plugins/clipboard.js +289 -0
  471. package/src/state_plugins/code_block.js +201 -0
  472. package/src/state_plugins/collab_carets.js +152 -0
  473. package/src/state_plugins/comments.js +259 -0
  474. package/src/state_plugins/contributor_input/add_button.js +134 -0
  475. package/src/state_plugins/contributor_input/dropup.js +318 -0
  476. package/src/state_plugins/contributor_input/helpers.js +24 -0
  477. package/src/state_plugins/contributor_input/index.js +2 -0
  478. package/src/state_plugins/contributor_input/node_view.js +86 -0
  479. package/src/state_plugins/contributor_input/plugin.js +318 -0
  480. package/src/state_plugins/document_template.js +315 -0
  481. package/src/state_plugins/figure.js +108 -0
  482. package/src/state_plugins/footnote_markers.js +238 -0
  483. package/src/state_plugins/headerbar.js +12 -0
  484. package/src/state_plugins/index.js +73 -0
  485. package/src/state_plugins/inline_math.js +918 -0
  486. package/src/state_plugins/inline_reference/dropup.js +208 -0
  487. package/src/state_plugins/inline_reference/index.js +5 -0
  488. package/src/state_plugins/inline_reference/plugin.js +1429 -0
  489. package/src/state_plugins/jump_hidden_nodes.js +58 -0
  490. package/src/state_plugins/links.js +830 -0
  491. package/src/state_plugins/marginboxes.js +14 -0
  492. package/src/state_plugins/ordered_list_menu.js +84 -0
  493. package/src/state_plugins/placeholders.js +123 -0
  494. package/src/state_plugins/search.js +251 -0
  495. package/src/state_plugins/selection_menu.js +12 -0
  496. package/src/state_plugins/settings.js +223 -0
  497. package/src/state_plugins/table.js +127 -0
  498. package/src/state_plugins/tag_input/helpers.js +69 -0
  499. package/src/state_plugins/tag_input/index.js +2 -0
  500. package/src/state_plugins/tag_input/node_view.js +85 -0
  501. package/src/state_plugins/tag_input/plugin.js +278 -0
  502. package/src/state_plugins/tag_input/tag_editor.js +202 -0
  503. package/src/state_plugins/tag_input/tag_editor_plugins.js +74 -0
  504. package/src/state_plugins/toc_render.js +160 -0
  505. package/src/state_plugins/toolbar.js +12 -0
  506. package/src/state_plugins/track/find_selected_changes.js +107 -0
  507. package/src/state_plugins/track/helpers.js +108 -0
  508. package/src/state_plugins/track/index.js +6 -0
  509. package/src/state_plugins/track/plugin.js +140 -0
  510. package/src/tools/index.js +3 -0
  511. package/src/tools/key_bindings/index.js +21 -0
  512. package/src/tools/key_bindings/templates.js +68 -0
  513. package/src/tools/search_replace/index.js +421 -0
  514. package/src/tools/search_replace/templates.js +19 -0
  515. package/src/tools/word_count/index.js +59 -0
  516. package/src/tools/word_count/templates.js +34 -0
  517. package/src/track/accept.js +102 -0
  518. package/src/track/accept_all.js +103 -0
  519. package/src/track/accept_all_no_insertions.js +102 -0
  520. package/src/track/amend_transaction.js +562 -0
  521. package/src/track/delete.js +92 -0
  522. package/src/track/index.js +8 -0
  523. package/src/track/mod.js +116 -0
  524. package/src/track/reject.js +116 -0
  525. package/src/track/reject_all.js +121 -0
@@ -0,0 +1,1429 @@
1
+ import {
2
+ NodeSelection,
3
+ Plugin,
4
+ PluginKey,
5
+ TextSelection
6
+ } from "prosemirror-state"
7
+ import {Decoration, DecorationSet} from "prosemirror-view"
8
+ import {COMMENT_ONLY_ROLES, READ_ONLY_ROLES} from "../.."
9
+ import {elementDisabled} from "../../menus/toolbar/model"
10
+ import {
11
+ buildBibliographyList,
12
+ buildCrossRefList,
13
+ createInlineReferenceDropUp,
14
+ filterBibliography,
15
+ filterCrossRefs
16
+ } from "./dropup"
17
+
18
+ const key = new PluginKey("inlineReference")
19
+
20
+ export const getInlineReferenceState = state => key.getState(state)
21
+ export const setInlineReferenceState = (tr, state) => tr.setMeta(key, state)
22
+
23
+ /**
24
+ * Check if citations can be inserted at the given position.
25
+ * Checks both the ProseMirror schema and document-template restrictions
26
+ * (allowed elements per doc part, protected sections) — the same conditions
27
+ * that disable the toolbar citation button.
28
+ */
29
+ function canActivateInlineReference(editor) {
30
+ // Mirror the template-based restriction check used by the toolbar button.
31
+ if (
32
+ editor &&
33
+ elementDisabled(editor, "citation") &&
34
+ elementDisabled(editor, "cross_reference")
35
+ ) {
36
+ return false
37
+ }
38
+ return true
39
+ }
40
+
41
+ /**
42
+ * Parse inline citation text into references array.
43
+ * Format: @key[prefix][locator];key2[prefix2][locator2]
44
+ * Returns {references, format} or null if any key cannot be resolved.
45
+ */
46
+ function parseCitationText(text, bibEntries, bibDB) {
47
+ let format = "autocite"
48
+ let body = text
49
+ if (body.startsWith("@@")) {
50
+ format = "textcite"
51
+ body = body.slice(2)
52
+ } else if (body.startsWith("@")) {
53
+ body = body.slice(1)
54
+ }
55
+
56
+ if (!body) {
57
+ return null
58
+ }
59
+
60
+ const references = []
61
+ const parts = body.split(";")
62
+
63
+ for (const part of parts) {
64
+ const match = part.match(/^([^\[]+)(?:\[(.*?)\])?(?:\[(.*?)\])?$/)
65
+ if (!match) {
66
+ return null
67
+ }
68
+ const entryKey = match[1].trim()
69
+ if (!entryKey) {
70
+ continue
71
+ }
72
+ const bibEntry = bibEntries.find(entry => entry.entry_key === entryKey)
73
+ if (!bibEntry) {
74
+ return null
75
+ }
76
+ let id = bibEntry.id
77
+ if (bibEntry.source === "user") {
78
+ // bibEntry.entry is the raw entry object from the user's bibDB
79
+ // (stored by buildBibliographyList). We must NOT look it up via
80
+ // bibDB.db[id] because bibDB is the *document* bibDB and the user's
81
+ // id doesn't exist there yet — that lookup returns undefined, which
82
+ // addReference then writes into the document DB, corrupting it.
83
+ id = bibDB.addReference(bibEntry.entry, id)
84
+ }
85
+ const ref = {id}
86
+ if (match[2]) {
87
+ ref.prefix = match[2]
88
+ }
89
+ if (match[3]) {
90
+ ref.locator = match[3]
91
+ }
92
+ references.push(ref)
93
+ }
94
+
95
+ if (!references.length) {
96
+ return null
97
+ }
98
+
99
+ return {references, format}
100
+ }
101
+
102
+ /**
103
+ * Extract the active reference index based on cursor offset.
104
+ */
105
+ function getActiveRefIndex(text, cursorOffset) {
106
+ let pos = 0
107
+ const parts = text.split(";")
108
+ for (let i = 0; i < parts.length; i++) {
109
+ const partLen = parts[i].length
110
+ if (cursorOffset <= pos + partLen) {
111
+ return i
112
+ }
113
+ pos += partLen + 1
114
+ }
115
+ return Math.max(0, parts.length - 1)
116
+ }
117
+
118
+ /**
119
+ * Get the query (key being typed) for the active reference.
120
+ */
121
+ function getActiveQuery(text, activeIndex) {
122
+ const parts = text.split(";")
123
+ let part = parts[activeIndex] || ""
124
+ if (part.startsWith("@@")) {
125
+ part = part.slice(2)
126
+ } else if (part.startsWith("@")) {
127
+ part = part.slice(1)
128
+ }
129
+ const match = part.match(/^([^\[]*)/)
130
+ return match ? match[1] : ""
131
+ }
132
+
133
+ /**
134
+ * Replace the key of the active reference while preserving prefix/locator.
135
+ */
136
+ function replaceActiveKey(text, activeIndex, newKey) {
137
+ const parts = text.split(";")
138
+ let part = parts[activeIndex] || ""
139
+ let trigger = ""
140
+ if (part.startsWith("@@")) {
141
+ trigger = "@@"
142
+ part = part.slice(2)
143
+ } else if (part.startsWith("@")) {
144
+ trigger = "@"
145
+ part = part.slice(1)
146
+ }
147
+ const suffixMatch = part.match(/^[^\[]*(\[.*)$/)
148
+ const suffix = suffixMatch ? suffixMatch[1] : ""
149
+ parts[activeIndex] = trigger + newKey + suffix
150
+ return parts.join(";")
151
+ }
152
+
153
+ /**
154
+ * Convert a citation node back to its inline text representation.
155
+ */
156
+ function citationToText(node, bibEntries) {
157
+ const format = node.attrs.format
158
+ const references = node.attrs.references || []
159
+ const parts = references.map(ref => {
160
+ const entry = bibEntries.find(e => e.id === ref.id)
161
+ const key = entry ? entry.entry_key : ""
162
+ let part = key
163
+ if (ref.prefix) {
164
+ part += `[${ref.prefix}]`
165
+ }
166
+ if (ref.locator) {
167
+ part += `[${ref.locator}]`
168
+ }
169
+ return part
170
+ })
171
+ const body = parts.join(";")
172
+ if (format === "textcite") {
173
+ return "@@" + body
174
+ }
175
+ return "@" + body
176
+ }
177
+
178
+ function getInputCursorPos(el) {
179
+ const sel = window.getSelection()
180
+ if (!sel || !sel.rangeCount) {
181
+ return (el.textContent || "").length
182
+ }
183
+ const range = sel.getRangeAt(0)
184
+ if (!el.contains(range.startContainer)) {
185
+ return (el.textContent || "").length
186
+ }
187
+ return range.startOffset
188
+ }
189
+
190
+ function setInputCursorPos(el, pos) {
191
+ const textNode = el.firstChild
192
+ if (!textNode) {
193
+ return
194
+ }
195
+ const range = document.createRange()
196
+ const safePos = Math.max(0, Math.min(pos, textNode.length))
197
+ range.setStart(textNode, safePos)
198
+ range.collapse(true)
199
+ const sel = window.getSelection()
200
+ if (sel) {
201
+ sel.removeAllRanges()
202
+ sel.addRange(range)
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Create the inline citation widget DOM element.
208
+ */
209
+ function createInlineReferenceWidget(editor, pluginState, key) {
210
+ const view = editor.currentView || editor.view
211
+ const container = document.createElement("span")
212
+ container.className = "inline-reference-widget"
213
+
214
+ // Build the cross-reference target list once at widget creation time.
215
+ const crossRefList = buildCrossRefList(editor)
216
+
217
+ const input = document.createElement("span")
218
+ input.contentEditable = "plaintext-only"
219
+ input.spellcheck = false
220
+ input.className = "inline-reference-input"
221
+ input.textContent = pluginState.query
222
+
223
+ const dropUpWrapper = document.createElement("span")
224
+ dropUpWrapper.className = "drop-up-outer inline-reference-drop-up"
225
+
226
+ function renderDropUp() {
227
+ const currentState = key.getState(view.state)
228
+ if (!currentState?.active) {
229
+ return
230
+ }
231
+ const rawText = input.textContent || ""
232
+ const citDisabled = elementDisabled(editor, "citation")
233
+ const xrefDisabled = elementDisabled(editor, "cross_reference")
234
+ const isXrefMode = !xrefDisabled && rawText.startsWith("@#")
235
+ let citationMatches, xrefMatches, activeCitIndex
236
+ if (isXrefMode) {
237
+ // Cross-reference mode: only show cross-refs, filter by id/text
238
+ citationMatches = []
239
+ xrefMatches = filterCrossRefs(crossRefList, rawText.slice(2))
240
+ activeCitIndex = -1
241
+ } else {
242
+ const cursorPos = getInputCursorPos(input)
243
+ activeCitIndex = getActiveRefIndex(rawText, cursorPos)
244
+ const query = getActiveQuery(rawText, activeCitIndex)
245
+ citationMatches = citDisabled
246
+ ? []
247
+ : filterBibliography(currentState.bibList, query)
248
+ xrefMatches = xrefDisabled
249
+ ? []
250
+ : filterCrossRefs(crossRefList, query)
251
+ }
252
+ const totalMatches = citationMatches.length + xrefMatches.length
253
+ const selectedIndex = currentState.listActive
254
+ ? Math.max(
255
+ 0,
256
+ Math.min(currentState.selectedIndex, totalMatches - 1)
257
+ )
258
+ : -1
259
+ dropUpWrapper.innerHTML = ""
260
+ const dropUp = createInlineReferenceDropUp(
261
+ citationMatches,
262
+ selectedIndex,
263
+ idx => {
264
+ if (idx < citationMatches.length) {
265
+ const entry = citationMatches[idx]
266
+ if (!entry) {
267
+ return
268
+ }
269
+ const newText = replaceActiveKey(
270
+ rawText,
271
+ activeCitIndex,
272
+ entry.entry_key
273
+ )
274
+ input.textContent = newText
275
+ // Move cursor to end of replaced key
276
+ const newCursor = newText.indexOf(
277
+ entry.entry_key,
278
+ newText.indexOf("@") + 1
279
+ )
280
+ setInputCursorPos(input, newCursor + entry.entry_key.length)
281
+ view.dispatch(
282
+ view.state.tr.setMeta(key, {
283
+ action: "updateQuery",
284
+ query: input.textContent
285
+ })
286
+ )
287
+ renderDropUp()
288
+ } else {
289
+ // Cross-reference selected: fill input with @#id, stay open
290
+ const target = xrefMatches[idx - citationMatches.length]
291
+ if (!target) {
292
+ return
293
+ }
294
+ input.textContent = `@#${target.id}`
295
+ setInputCursorPos(input, input.textContent.length)
296
+ view.dispatch(
297
+ view.state.tr.setMeta(key, {
298
+ action: "updateQuery",
299
+ query: input.textContent
300
+ })
301
+ )
302
+ renderDropUp()
303
+ }
304
+ },
305
+ xrefMatches
306
+ )
307
+ dropUpWrapper.appendChild(dropUp)
308
+ // Scroll selected item into view
309
+ const selectedItem = dropUp.querySelector(
310
+ ".inline-reference-drop-up-item.fw-selected"
311
+ )
312
+ if (selectedItem) {
313
+ selectedItem.scrollIntoView({block: "nearest"})
314
+ }
315
+ }
316
+
317
+ input.addEventListener("input", event => {
318
+ event.stopPropagation()
319
+ const text = input.textContent || ""
320
+ if (!text) {
321
+ // Everything including `@` was deleted — remove the node.
322
+ view.dispatch(view.state.tr.setMeta(key, {action: "cancelDelete"}))
323
+ view.focus()
324
+ return
325
+ }
326
+ if (!text.startsWith("@")) {
327
+ // `@` was removed but content remains — restore it.
328
+ const cursorPos = getInputCursorPos(input)
329
+ input.textContent = "@" + text
330
+ setInputCursorPos(input, cursorPos + 1)
331
+ }
332
+ view.dispatch(
333
+ view.state.tr.setMeta(key, {
334
+ action: "updateQuery",
335
+ query: input.textContent
336
+ })
337
+ )
338
+ renderDropUp()
339
+ })
340
+
341
+ input.addEventListener("keypress", event => {
342
+ event.stopPropagation()
343
+ })
344
+
345
+ input.addEventListener("keydown", event => {
346
+ event.stopPropagation()
347
+ const currentState = key.getState(view.state)
348
+ if (!currentState?.active) {
349
+ return
350
+ }
351
+
352
+ switch (event.key) {
353
+ case "ArrowLeft":
354
+ if (getInputCursorPos(input) === 0) {
355
+ event.preventDefault()
356
+ view.dispatch(
357
+ view.state.tr.setMeta(key, {action: "cancelLeft"})
358
+ )
359
+ view.focus()
360
+ }
361
+ break
362
+ case "ArrowRight":
363
+ if (
364
+ getInputCursorPos(input) ===
365
+ (input.textContent || "").length
366
+ ) {
367
+ event.preventDefault()
368
+ view.dispatch(
369
+ view.state.tr.setMeta(key, {action: "commitAndRight"})
370
+ )
371
+ view.focus()
372
+ }
373
+ break
374
+ case "ArrowDown":
375
+ event.preventDefault()
376
+ {
377
+ const currentState = key.getState(view.state)
378
+ if (!currentState.listActive) {
379
+ view.dispatch(
380
+ view.state.tr.setMeta(key, {
381
+ action: "select",
382
+ index: 0,
383
+ listActive: true
384
+ })
385
+ )
386
+ } else {
387
+ view.dispatch(
388
+ view.state.tr.setMeta(key, {
389
+ action: "select",
390
+ index: currentState.selectedIndex + 1,
391
+ listActive: true
392
+ })
393
+ )
394
+ }
395
+ renderDropUp()
396
+ }
397
+ break
398
+ case "ArrowUp":
399
+ event.preventDefault()
400
+ {
401
+ const currentState = key.getState(view.state)
402
+ if (
403
+ currentState.listActive &&
404
+ currentState.selectedIndex > 0
405
+ ) {
406
+ view.dispatch(
407
+ view.state.tr.setMeta(key, {
408
+ action: "select",
409
+ index: currentState.selectedIndex - 1,
410
+ listActive: true
411
+ })
412
+ )
413
+ } else {
414
+ view.dispatch(
415
+ view.state.tr.setMeta(key, {
416
+ action: "select",
417
+ index: -1,
418
+ listActive: false
419
+ })
420
+ )
421
+ }
422
+ renderDropUp()
423
+ }
424
+ break
425
+ case "Enter":
426
+ event.preventDefault()
427
+ {
428
+ const currentState = key.getState(view.state)
429
+ const rawText = input.textContent || ""
430
+ const citDisabledEnter = elementDisabled(editor, "citation")
431
+ const xrefDisabledEnter = elementDisabled(
432
+ editor,
433
+ "cross_reference"
434
+ )
435
+ const isXrefMode =
436
+ !xrefDisabledEnter && rawText.startsWith("@#")
437
+ let citMatches, xrefMatchesEnter, activeIndexEnter
438
+ if (isXrefMode) {
439
+ citMatches = []
440
+ xrefMatchesEnter = filterCrossRefs(
441
+ crossRefList,
442
+ rawText.slice(2)
443
+ )
444
+ activeIndexEnter = -1
445
+ } else {
446
+ const cursorPos = getInputCursorPos(input)
447
+ activeIndexEnter = getActiveRefIndex(rawText, cursorPos)
448
+ const query = getActiveQuery(rawText, activeIndexEnter)
449
+ citMatches = citDisabledEnter
450
+ ? []
451
+ : filterBibliography(currentState.bibList, query)
452
+ xrefMatchesEnter = xrefDisabledEnter
453
+ ? []
454
+ : filterCrossRefs(crossRefList, query)
455
+ }
456
+ const totalEnter =
457
+ citMatches.length + xrefMatchesEnter.length
458
+ const selectedIndex = Math.max(
459
+ 0,
460
+ Math.min(currentState.selectedIndex, totalEnter - 1)
461
+ )
462
+ if (
463
+ currentState.listActive &&
464
+ totalEnter > 0 &&
465
+ currentState.selectedIndex >= 0
466
+ ) {
467
+ if (selectedIndex < citMatches.length) {
468
+ // Insert selected citation key into input
469
+ const entry = citMatches[selectedIndex]
470
+ const newText = replaceActiveKey(
471
+ rawText,
472
+ activeIndexEnter,
473
+ entry.entry_key
474
+ )
475
+ input.textContent = newText
476
+ const newCursor =
477
+ newText.indexOf(
478
+ entry.entry_key,
479
+ newText.indexOf("@") + 1
480
+ ) + entry.entry_key.length
481
+ setInputCursorPos(input, newCursor)
482
+ view.dispatch(
483
+ view.state.tr.setMeta(key, {
484
+ action: "updateQuery",
485
+ query: input.textContent
486
+ })
487
+ )
488
+ renderDropUp()
489
+ } else {
490
+ // Cross-reference selected: fill input with @#id, stay open
491
+ const target =
492
+ xrefMatchesEnter[
493
+ selectedIndex - citMatches.length
494
+ ]
495
+ input.textContent = `@#${target.id}`
496
+ setInputCursorPos(input, input.textContent.length)
497
+ view.dispatch(
498
+ view.state.tr.setMeta(key, {
499
+ action: "updateQuery",
500
+ query: input.textContent
501
+ })
502
+ )
503
+ renderDropUp()
504
+ }
505
+ } else {
506
+ // Not in list mode or no matches: commit what we have
507
+ view.dispatch(
508
+ view.state.tr.setMeta(key, {action: "commit"})
509
+ )
510
+ view.focus()
511
+ }
512
+ }
513
+ break
514
+ case "Escape":
515
+ event.preventDefault()
516
+ view.dispatch(view.state.tr.setMeta(key, {action: "cancel"}))
517
+ view.focus()
518
+ break
519
+ case "Home":
520
+ event.preventDefault()
521
+ setInputCursorPos(input, 0)
522
+ break
523
+ case "End":
524
+ event.preventDefault()
525
+ setInputCursorPos(input, (input.textContent || "").length)
526
+ break
527
+ case "Backspace":
528
+ // When only `@` remains, delete the whole node.
529
+ if ((input.textContent || "").length <= 1) {
530
+ event.preventDefault()
531
+ view.dispatch(
532
+ view.state.tr.setMeta(key, {action: "cancelDelete"})
533
+ )
534
+ view.focus()
535
+ }
536
+ break
537
+ case "Delete":
538
+ // Same as Backspace when only `@` is left.
539
+ if ((input.textContent || "").length <= 1) {
540
+ event.preventDefault()
541
+ view.dispatch(
542
+ view.state.tr.setMeta(key, {action: "cancelDelete"})
543
+ )
544
+ view.focus()
545
+ }
546
+ break
547
+ case "Tab":
548
+ event.preventDefault()
549
+ view.dispatch(view.state.tr.setMeta(key, {action: "commit"}))
550
+ break
551
+ default:
552
+ break
553
+ }
554
+ })
555
+
556
+ input.addEventListener("focusout", event => {
557
+ const related = event.relatedTarget
558
+ if (container.contains(related)) {
559
+ return
560
+ }
561
+ const myWidgetId = pluginState.widgetId
562
+ setTimeout(() => {
563
+ const currentState = key.getState(view.state)
564
+ if (currentState?.active && currentState.widgetId === myWidgetId) {
565
+ view.dispatch(view.state.tr.setMeta(key, {action: "commit"}))
566
+ }
567
+ }, 1)
568
+ })
569
+
570
+ container.addEventListener("mousedown", event => {
571
+ event.stopPropagation()
572
+ })
573
+
574
+ container.appendChild(input)
575
+ container.appendChild(dropUpWrapper)
576
+
577
+ // Focus after mounting
578
+ setTimeout(() => {
579
+ input.focus()
580
+ // Place cursor based on entry direction
581
+ if (pluginState.cursorAtStart === true) {
582
+ setInputCursorPos(input, 0)
583
+ } else {
584
+ setInputCursorPos(input, (input.textContent || "").length)
585
+ }
586
+ renderDropUp()
587
+ }, 0)
588
+
589
+ return container
590
+ }
591
+
592
+ export const inlineReferencePlugin = options => {
593
+ const editor = options.editor
594
+ const bibDB = editor.mod.db.bibDB
595
+
596
+ const enabled =
597
+ editor.app.config.user?.preferences?.inline_references === true &&
598
+ !READ_ONLY_ROLES.includes(editor.docInfo.access_rights) &&
599
+ !COMMENT_ONLY_ROLES.includes(editor.docInfo.access_rights)
600
+
601
+ if (!enabled) {
602
+ return new Plugin({
603
+ key,
604
+ state: {
605
+ init() {
606
+ return {active: false}
607
+ },
608
+ apply() {
609
+ return {active: false}
610
+ }
611
+ }
612
+ })
613
+ }
614
+
615
+ return new Plugin({
616
+ key,
617
+ state: {
618
+ init() {
619
+ return {
620
+ active: false,
621
+ from: 0,
622
+ query: "",
623
+ selectedIndex: -1,
624
+ listActive: false,
625
+ isEdit: false,
626
+ referenceNodePos: 0,
627
+ bibList: [],
628
+ widgetId: undefined,
629
+ cursorAtStart: undefined,
630
+ decos: DecorationSet.empty
631
+ }
632
+ },
633
+ apply(tr, prev, _oldState, _state) {
634
+ let next = {...prev}
635
+ const meta = tr.getMeta(key)
636
+
637
+ if (meta?.action === "activate") {
638
+ next = {
639
+ active: true,
640
+ from: meta.from,
641
+ query: meta.query || "",
642
+ selectedIndex: -1,
643
+ listActive: false,
644
+ isEdit: meta.isEdit || false,
645
+ referenceNodePos: meta.referenceNodePos || 0,
646
+ bibList: meta.bibList || [],
647
+ widgetId: Math.random().toString(36).slice(2),
648
+ cursorAtStart: meta.cursorAtStart,
649
+ decos: DecorationSet.empty
650
+ }
651
+ } else if (meta?.action === "deactivate") {
652
+ next = {
653
+ active: false,
654
+ from: 0,
655
+ query: "",
656
+ selectedIndex: -1,
657
+ listActive: false,
658
+ isEdit: false,
659
+ referenceNodePos: 0,
660
+ bibList: [],
661
+ widgetId: undefined,
662
+ cursorAtStart: undefined,
663
+ decos: DecorationSet.empty
664
+ }
665
+ } else if (meta?.action === "select") {
666
+ next.selectedIndex = meta.index
667
+ if (meta.listActive !== undefined) {
668
+ next.listActive = meta.listActive
669
+ }
670
+ } else if (meta?.action === "updateQuery") {
671
+ next.query = meta.query
672
+ next.selectedIndex = -1
673
+ next.listActive = false
674
+ }
675
+
676
+ if (next.active) {
677
+ if (!prev.active || prev.from !== next.from) {
678
+ // Position changed or just activated: create new widget
679
+ const deco = Decoration.widget(
680
+ next.from,
681
+ () => {
682
+ return createInlineReferenceWidget(
683
+ editor,
684
+ next,
685
+ key
686
+ )
687
+ },
688
+ {
689
+ key: "inline-reference-widget",
690
+ side: -1
691
+ }
692
+ )
693
+ const referenceNodeDeco = Decoration.node(
694
+ next.from,
695
+ next.from + 1,
696
+ {
697
+ class: "hide"
698
+ }
699
+ )
700
+ next.decos = DecorationSet.create(_state.doc, [
701
+ deco,
702
+ referenceNodeDeco
703
+ ])
704
+ } else if (tr.docChanged) {
705
+ // Document changed while active: map old decorations
706
+ next.decos = prev.decos.map(tr.mapping, _state.doc)
707
+ } else {
708
+ // Same position, doc unchanged: keep existing widget
709
+ next.decos = prev.decos
710
+ }
711
+ } else {
712
+ next.decos = DecorationSet.empty
713
+ }
714
+
715
+ return next
716
+ }
717
+ },
718
+ appendTransaction: (trs, oldState, newState) => {
719
+ const oldPluginState = key.getState(oldState)
720
+ const meta = trs.find(tr => tr.getMeta(key))?.getMeta(key)
721
+
722
+ if (
723
+ meta?.action === "commit" ||
724
+ meta?.action === "commitAndRight"
725
+ ) {
726
+ if (!oldPluginState.active) {
727
+ return null
728
+ }
729
+ const query = oldPluginState.query
730
+ // Cross-reference mode: query starts with '@#'
731
+ if (
732
+ query.startsWith("@#") &&
733
+ !elementDisabled(editor, "cross_reference")
734
+ ) {
735
+ const xrefId = query.slice(2)
736
+ const crossRefList = buildCrossRefList(editor)
737
+ const target = crossRefList.find(t => t.id === xrefId)
738
+ const crossRefNode =
739
+ newState.schema.nodes.cross_reference.create({
740
+ id: xrefId || false,
741
+ title: target ? target.text : null
742
+ })
743
+ if (oldPluginState.isEdit) {
744
+ const existingNode = newState.doc.nodeAt(
745
+ oldPluginState.referenceNodePos
746
+ )
747
+ const tr = newState.tr.replaceWith(
748
+ oldPluginState.referenceNodePos,
749
+ oldPluginState.referenceNodePos +
750
+ (existingNode?.nodeSize || 1),
751
+ crossRefNode
752
+ )
753
+ tr.setSelection(
754
+ TextSelection.create(
755
+ tr.doc,
756
+ oldPluginState.referenceNodePos +
757
+ crossRefNode.nodeSize
758
+ )
759
+ )
760
+ return tr.setMeta(key, {action: "deactivate"})
761
+ } else {
762
+ const tr = newState.tr.insert(
763
+ oldPluginState.from,
764
+ crossRefNode
765
+ )
766
+ const newPos =
767
+ oldPluginState.from + crossRefNode.nodeSize
768
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
769
+ return tr.setMeta(key, {action: "deactivate"})
770
+ }
771
+ }
772
+ const parsed = !elementDisabled(editor, "citation")
773
+ ? parseCitationText(query, oldPluginState.bibList, bibDB)
774
+ : null
775
+ if (!parsed) {
776
+ if (oldPluginState.isEdit) {
777
+ const node = newState.doc.nodeAt(
778
+ oldPluginState.referenceNodePos
779
+ )
780
+ const newPos =
781
+ oldPluginState.referenceNodePos +
782
+ (node?.nodeSize || 1)
783
+ const tr = newState.tr.setSelection(
784
+ TextSelection.create(newState.doc, newPos)
785
+ )
786
+ return tr.setMeta(key, {action: "deactivate"})
787
+ }
788
+ // Invalid reference text: insert as plain text with caret after
789
+ const tr = newState.tr.insertText(
790
+ query,
791
+ oldPluginState.from
792
+ )
793
+ const newPos = oldPluginState.from + query.length
794
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
795
+ return tr.setMeta(key, {action: "deactivate"})
796
+ }
797
+ const citationNode = newState.schema.nodes.citation.create({
798
+ format: parsed.format,
799
+ references: parsed.references
800
+ })
801
+ if (oldPluginState.isEdit) {
802
+ const existingNode = newState.doc.nodeAt(
803
+ oldPluginState.referenceNodePos
804
+ )
805
+ const tr = newState.tr.replaceWith(
806
+ oldPluginState.referenceNodePos,
807
+ oldPluginState.referenceNodePos +
808
+ (existingNode?.nodeSize || 1),
809
+ citationNode
810
+ )
811
+ const newPos =
812
+ oldPluginState.referenceNodePos + citationNode.nodeSize
813
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
814
+ return tr.setMeta(key, {action: "deactivate"})
815
+ } else {
816
+ const tr = newState.tr.insert(
817
+ oldPluginState.from,
818
+ citationNode
819
+ )
820
+ const newPos = oldPluginState.from + citationNode.nodeSize
821
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
822
+ return tr.setMeta(key, {action: "deactivate"})
823
+ }
824
+ }
825
+
826
+ if (meta?.action === "cancel") {
827
+ if (oldPluginState.isEdit) {
828
+ // Editing: just deactivate, leave reference unchanged
829
+ const node = newState.doc.nodeAt(
830
+ oldPluginState.referenceNodePos
831
+ )
832
+ const newPos =
833
+ oldPluginState.referenceNodePos + (node?.nodeSize || 1)
834
+ const tr = newState.tr.setSelection(
835
+ TextSelection.create(newState.doc, newPos)
836
+ )
837
+ return tr.setMeta(key, {action: "deactivate"})
838
+ } else {
839
+ // New reference: insert query as plain text with caret after
840
+ const tr = newState.tr.insertText(
841
+ oldPluginState.query,
842
+ oldPluginState.from
843
+ )
844
+ const newPos =
845
+ oldPluginState.from + oldPluginState.query.length
846
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
847
+ return tr.setMeta(key, {action: "deactivate"})
848
+ }
849
+ }
850
+
851
+ if (meta?.action === "cancelDelete") {
852
+ if (oldPluginState.isEdit) {
853
+ // Delete the reference node entirely.
854
+ const existingNode = newState.doc.nodeAt(
855
+ oldPluginState.referenceNodePos
856
+ )
857
+ const tr = newState.tr.delete(
858
+ oldPluginState.referenceNodePos,
859
+ oldPluginState.referenceNodePos +
860
+ (existingNode?.nodeSize || 1)
861
+ )
862
+ tr.setSelection(
863
+ TextSelection.create(
864
+ tr.doc,
865
+ oldPluginState.referenceNodePos
866
+ )
867
+ )
868
+ return tr.setMeta(key, {action: "deactivate"})
869
+ } else {
870
+ // New node: just deactivate — don't insert anything.
871
+ return newState.tr.setMeta(key, {action: "deactivate"})
872
+ }
873
+ }
874
+
875
+ if (meta?.action === "cancelLeft") {
876
+ if (oldPluginState.isEdit) {
877
+ const tr = newState.tr.setSelection(
878
+ TextSelection.create(
879
+ newState.doc,
880
+ oldPluginState.referenceNodePos
881
+ )
882
+ )
883
+ return tr.setMeta(key, {action: "deactivate"})
884
+ } else {
885
+ const tr = newState.tr.insertText(
886
+ oldPluginState.query,
887
+ oldPluginState.from
888
+ )
889
+ tr.setSelection(
890
+ TextSelection.create(tr.doc, oldPluginState.from)
891
+ )
892
+ return tr.setMeta(key, {action: "deactivate"})
893
+ }
894
+ }
895
+
896
+ if (meta?.action === "openDialog") {
897
+ if (oldPluginState.active && oldPluginState.isEdit) {
898
+ // Deactivate the inline editor and leave a NodeSelection on
899
+ // the citation so CitationDialog opens with its content.
900
+ const node = newState.doc.nodeAt(
901
+ oldPluginState.referenceNodePos
902
+ )
903
+ if (node?.type.name === "citation") {
904
+ return newState.tr
905
+ .setSelection(
906
+ NodeSelection.create(
907
+ newState.doc,
908
+ oldPluginState.referenceNodePos
909
+ )
910
+ )
911
+ .setMeta(key, {action: "deactivate"})
912
+ }
913
+ }
914
+ return null
915
+ }
916
+
917
+ if (meta?.action === "insertCrossRef") {
918
+ if (!oldPluginState.active) {
919
+ return null
920
+ }
921
+ const crossRefNode =
922
+ newState.schema.nodes.cross_reference.create({
923
+ id: meta.id,
924
+ title: meta.title
925
+ })
926
+ if (oldPluginState.isEdit) {
927
+ // Replace the existing citation node with a cross_reference.
928
+ const existingNode = newState.doc.nodeAt(
929
+ oldPluginState.referenceNodePos
930
+ )
931
+ const tr = newState.tr.replaceWith(
932
+ oldPluginState.referenceNodePos,
933
+ oldPluginState.referenceNodePos +
934
+ (existingNode?.nodeSize || 1),
935
+ crossRefNode
936
+ )
937
+ tr.setSelection(
938
+ TextSelection.create(
939
+ tr.doc,
940
+ oldPluginState.referenceNodePos +
941
+ crossRefNode.nodeSize
942
+ )
943
+ )
944
+ return tr.setMeta(key, {action: "deactivate"})
945
+ } else {
946
+ const tr = newState.tr.insert(
947
+ oldPluginState.from,
948
+ crossRefNode
949
+ )
950
+ tr.setSelection(
951
+ TextSelection.create(
952
+ tr.doc,
953
+ oldPluginState.from + crossRefNode.nodeSize
954
+ )
955
+ )
956
+ return tr.setMeta(key, {action: "deactivate"})
957
+ }
958
+ }
959
+
960
+ if (oldPluginState.active) {
961
+ // If selection moved away from the widget, commit
962
+ if (
963
+ trs.some(tr => tr.selectionSet) &&
964
+ (newState.selection.from !== oldPluginState.from ||
965
+ newState.selection.to !== oldPluginState.from)
966
+ ) {
967
+ const query = oldPluginState.query
968
+ // Cross-reference mode
969
+ if (
970
+ query.startsWith("@#") &&
971
+ !elementDisabled(editor, "cross_reference")
972
+ ) {
973
+ const xrefId = query.slice(2)
974
+ if (xrefId && oldPluginState.isEdit) {
975
+ const crossRefList = buildCrossRefList(editor)
976
+ const target = crossRefList.find(
977
+ t => t.id === xrefId
978
+ )
979
+ const crossRefNode =
980
+ newState.schema.nodes.cross_reference.create({
981
+ id: xrefId,
982
+ title: target ? target.text : null
983
+ })
984
+ const existingNode = newState.doc.nodeAt(
985
+ oldPluginState.referenceNodePos
986
+ )
987
+ return newState.tr
988
+ .replaceWith(
989
+ oldPluginState.referenceNodePos,
990
+ oldPluginState.referenceNodePos +
991
+ (existingNode?.nodeSize || 1),
992
+ crossRefNode
993
+ )
994
+ .setMeta(key, {action: "deactivate"})
995
+ } else if (xrefId && !oldPluginState.isEdit) {
996
+ const crossRefList = buildCrossRefList(editor)
997
+ const target = crossRefList.find(
998
+ t => t.id === xrefId
999
+ )
1000
+ const crossRefNode =
1001
+ newState.schema.nodes.cross_reference.create({
1002
+ id: xrefId,
1003
+ title: target ? target.text : null
1004
+ })
1005
+ return newState.tr
1006
+ .insert(oldPluginState.from, crossRefNode)
1007
+ .setMeta(key, {action: "deactivate"})
1008
+ } else {
1009
+ return newState.tr.setMeta(key, {
1010
+ action: "deactivate"
1011
+ })
1012
+ }
1013
+ }
1014
+ const parsed = !elementDisabled(editor, "citation")
1015
+ ? parseCitationText(
1016
+ query,
1017
+ oldPluginState.bibList,
1018
+ bibDB
1019
+ )
1020
+ : null
1021
+ if (parsed) {
1022
+ const citationNode =
1023
+ newState.schema.nodes.citation.create({
1024
+ format: parsed.format,
1025
+ references: parsed.references
1026
+ })
1027
+ if (oldPluginState.isEdit) {
1028
+ const existingNode = newState.doc.nodeAt(
1029
+ oldPluginState.referenceNodePos
1030
+ )
1031
+ return newState.tr
1032
+ .replaceWith(
1033
+ oldPluginState.referenceNodePos,
1034
+ oldPluginState.referenceNodePos +
1035
+ (existingNode?.nodeSize || 1),
1036
+ citationNode
1037
+ )
1038
+ .setMeta(key, {action: "deactivate"})
1039
+ } else {
1040
+ return newState.tr
1041
+ .insert(oldPluginState.from, citationNode)
1042
+ .setMeta(key, {action: "deactivate"})
1043
+ }
1044
+ } else if (!oldPluginState.isEdit) {
1045
+ // Invalid reference text on blur/selection-move: insert as plain text
1046
+ const tr = newState.tr.insertText(
1047
+ query,
1048
+ oldPluginState.from
1049
+ )
1050
+ const newPos = oldPluginState.from + query.length
1051
+ tr.setSelection(TextSelection.create(tr.doc, newPos))
1052
+ return tr.setMeta(key, {action: "deactivate"})
1053
+ } else {
1054
+ return newState.tr.setMeta(key, {
1055
+ action: "deactivate"
1056
+ })
1057
+ }
1058
+ }
1059
+ } else {
1060
+ // Check if a citation node is selected for re-editing (keyboard)
1061
+ const selection = newState.selection
1062
+ if (
1063
+ selection instanceof NodeSelection &&
1064
+ selection.node.type.name === "citation"
1065
+ ) {
1066
+ const citationNode = selection.node
1067
+ const from = selection.from
1068
+ const bibList = buildBibliographyList(editor)
1069
+ const text = citationToText(citationNode, bibList)
1070
+ return newState.tr.setMeta(key, {
1071
+ action: "activate",
1072
+ from: from,
1073
+ query: text,
1074
+ isEdit: true,
1075
+ referenceNodePos: from,
1076
+ bibList: bibList
1077
+ })
1078
+ }
1079
+
1080
+ // Check if a cross_reference node is selected for re-editing (keyboard)
1081
+ if (
1082
+ selection instanceof NodeSelection &&
1083
+ selection.node.type.name === "cross_reference"
1084
+ ) {
1085
+ const crossNode = selection.node
1086
+ const from = selection.from
1087
+ return newState.tr.setMeta(key, {
1088
+ action: "activate",
1089
+ from: from,
1090
+ query: `@#${crossNode.attrs.id || ""}`,
1091
+ isEdit: true,
1092
+ referenceNodePos: from,
1093
+ bibList: buildBibliographyList(editor)
1094
+ })
1095
+ }
1096
+
1097
+ // Check for @ keypress activation via doc change
1098
+ // (handles paste/mobile where handleKeyDown may not fire)
1099
+ const tr = trs.find(tr => tr.docChanged)
1100
+ if (tr) {
1101
+ const $pos = newState.selection.$head
1102
+ if (canActivateInlineReference(editor)) {
1103
+ const beforeText = newState.doc.textBetween(
1104
+ Math.max(0, $pos.pos - 3),
1105
+ $pos.pos,
1106
+ ""
1107
+ )
1108
+ if (beforeText.endsWith("@")) {
1109
+ const lastChar = beforeText.slice(-2, -1)
1110
+ if (!lastChar || !/[a-zA-Z0-9]/.test(lastChar)) {
1111
+ const tr = newState.tr
1112
+ .delete($pos.pos - 1, $pos.pos)
1113
+ .setMeta(key, {
1114
+ action: "activate",
1115
+ from: $pos.pos - 1,
1116
+ query: "@",
1117
+ isEdit: false,
1118
+ bibList: buildBibliographyList(editor)
1119
+ })
1120
+ return tr
1121
+ }
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ return null
1128
+ },
1129
+ props: {
1130
+ decorations(state) {
1131
+ const pluginState = key.getState(state)
1132
+ return pluginState?.decos || DecorationSet.empty
1133
+ },
1134
+ handleClickOn(_view, _pos, node, nodePos, _event, _direct) {
1135
+ if (node.type.name === "citation") {
1136
+ const bibList = buildBibliographyList(editor)
1137
+ const text = citationToText(node, bibList)
1138
+ const tr = _view.state.tr.setSelection(
1139
+ TextSelection.create(
1140
+ _view.state.doc,
1141
+ nodePos + node.nodeSize
1142
+ )
1143
+ )
1144
+ tr.setMeta(key, {
1145
+ action: "activate",
1146
+ from: nodePos,
1147
+ query: text,
1148
+ isEdit: true,
1149
+ referenceNodePos: nodePos,
1150
+ bibList: bibList
1151
+ })
1152
+ _view.dispatch(tr)
1153
+ return true
1154
+ }
1155
+ if (node.type.name === "cross_reference") {
1156
+ const tr = _view.state.tr.setSelection(
1157
+ TextSelection.create(
1158
+ _view.state.doc,
1159
+ nodePos + node.nodeSize
1160
+ )
1161
+ )
1162
+ tr.setMeta(key, {
1163
+ action: "activate",
1164
+ from: nodePos,
1165
+ query: `@#${node.attrs.id || ""}`,
1166
+ isEdit: true,
1167
+ referenceNodePos: nodePos,
1168
+ bibList: buildBibliographyList(editor)
1169
+ })
1170
+ _view.dispatch(tr)
1171
+ return true
1172
+ }
1173
+ return false
1174
+ },
1175
+ handleDOMEvents: {
1176
+ mousedown(view, event) {
1177
+ if (event.ctrlKey || event.metaKey || event.shiftKey) {
1178
+ return false
1179
+ }
1180
+ if (event.button !== 0) {
1181
+ return false
1182
+ }
1183
+ const citTarget = event.target.closest(".citation")
1184
+ const xrefTarget =
1185
+ !citTarget && event.target.closest(".cross-reference")
1186
+ const target = citTarget || xrefTarget
1187
+ if (!target) {
1188
+ return false
1189
+ }
1190
+ let pos = view.posAtDOM(target, 0)
1191
+ let node = view.state.doc.nodeAt(pos)
1192
+ if (
1193
+ !node ||
1194
+ (node.type.name !== "citation" &&
1195
+ node.type.name !== "cross_reference")
1196
+ ) {
1197
+ return false
1198
+ }
1199
+ const pluginState = key.getState(view.state)
1200
+ if (pluginState?.active) {
1201
+ // Commit current widget first
1202
+ const commitTr = view.state.tr.setMeta(key, {
1203
+ action: "commit"
1204
+ })
1205
+ view.dispatch(commitTr)
1206
+ pos = commitTr.mapping.map(pos)
1207
+ node = view.state.doc.nodeAt(pos)
1208
+ if (
1209
+ !node ||
1210
+ (node.type.name !== "citation" &&
1211
+ node.type.name !== "cross_reference")
1212
+ ) {
1213
+ return false
1214
+ }
1215
+ }
1216
+ let query, bibList
1217
+ if (node.type.name === "citation") {
1218
+ bibList = buildBibliographyList(editor)
1219
+ query = citationToText(node, bibList)
1220
+ } else {
1221
+ bibList = buildBibliographyList(editor)
1222
+ query = `@#${node.attrs.id || ""}`
1223
+ }
1224
+ const tr = view.state.tr.setSelection(
1225
+ TextSelection.create(
1226
+ view.state.doc,
1227
+ pos + node.nodeSize
1228
+ )
1229
+ )
1230
+ tr.setMeta(key, {
1231
+ action: "activate",
1232
+ from: pos,
1233
+ query,
1234
+ isEdit: true,
1235
+ referenceNodePos: pos,
1236
+ bibList
1237
+ })
1238
+ view.dispatch(tr)
1239
+ event.stopPropagation()
1240
+ event.preventDefault()
1241
+ return true
1242
+ },
1243
+ click(view, event) {
1244
+ // Fallback click handler for touch devices and cases where
1245
+ // mousedown/handleClickOn did not fire.
1246
+ const pluginState = key.getState(view.state)
1247
+ if (pluginState?.active) {
1248
+ const target = event.target.closest(
1249
+ ".inline-reference-widget"
1250
+ )
1251
+ if (target) {
1252
+ // Click inside widget: let widget handle it
1253
+ return false
1254
+ }
1255
+ // When widget is active, ignore all other clicks.
1256
+ // mousedown already handled citation clicks.
1257
+ // focusout handles clicks outside the editor.
1258
+ // We must prevent default because the browser may
1259
+ // retarget click to a parent element after mousedown
1260
+ // prevented the default.
1261
+ event.preventDefault()
1262
+ event.stopPropagation()
1263
+ return true
1264
+ }
1265
+ // Widget inactive: try to activate a clicked citation
1266
+ if (event.ctrlKey || event.metaKey || event.shiftKey) {
1267
+ return false
1268
+ }
1269
+ const target = event.target.closest(".citation")
1270
+ if (!target) {
1271
+ return false
1272
+ }
1273
+ const pos = view.posAtDOM(target, 0)
1274
+ const node = view.state.doc.nodeAt(pos)
1275
+ if (!node || node.type.name !== "citation") {
1276
+ return false
1277
+ }
1278
+ const bibList = buildBibliographyList(editor)
1279
+ const text = citationToText(node, bibList)
1280
+ const tr = view.state.tr.setSelection(
1281
+ TextSelection.create(
1282
+ view.state.doc,
1283
+ pos + node.nodeSize
1284
+ )
1285
+ )
1286
+ tr.setMeta(key, {
1287
+ action: "activate",
1288
+ from: pos,
1289
+ query: text,
1290
+ isEdit: true,
1291
+ referenceNodePos: pos,
1292
+ bibList: bibList
1293
+ })
1294
+ view.dispatch(tr)
1295
+ return true
1296
+ }
1297
+ },
1298
+ handleKeyDown(view, event) {
1299
+ const pluginState = key.getState(view.state)
1300
+ if (!pluginState?.active) {
1301
+ if (event.key === "@") {
1302
+ const $pos = view.state.selection.$head
1303
+ if (canActivateInlineReference(editor)) {
1304
+ const beforeText = view.state.doc.textBetween(
1305
+ Math.max(0, $pos.pos - 1),
1306
+ $pos.pos,
1307
+ ""
1308
+ )
1309
+ const lastChar = beforeText.slice(-1)
1310
+ if (!lastChar || !/[a-zA-Z0-9]/.test(lastChar)) {
1311
+ event.preventDefault()
1312
+ view.dispatch(
1313
+ view.state.tr.setMeta(key, {
1314
+ action: "activate",
1315
+ from: $pos.pos,
1316
+ query: "@",
1317
+ isEdit: false,
1318
+ bibList: buildBibliographyList(editor)
1319
+ })
1320
+ )
1321
+ return true
1322
+ }
1323
+ }
1324
+ }
1325
+ // ArrowRight into citation or cross_reference from the left
1326
+ if (event.key === "ArrowRight") {
1327
+ const sel = view.state.selection
1328
+ if (sel.empty) {
1329
+ const $pos = sel.$head
1330
+ const nodeAfter = $pos.nodeAfter
1331
+ if (
1332
+ nodeAfter &&
1333
+ nodeAfter.type.name === "citation"
1334
+ ) {
1335
+ event.preventDefault()
1336
+ const from = $pos.pos
1337
+ const bibList = buildBibliographyList(editor)
1338
+ const text = citationToText(nodeAfter, bibList)
1339
+ view.dispatch(
1340
+ view.state.tr.setMeta(key, {
1341
+ action: "activate",
1342
+ from: from,
1343
+ query: text,
1344
+ isEdit: true,
1345
+ referenceNodePos: from,
1346
+ bibList: bibList,
1347
+ cursorAtStart: true
1348
+ })
1349
+ )
1350
+ return true
1351
+ }
1352
+ if (
1353
+ nodeAfter &&
1354
+ nodeAfter.type.name === "cross_reference"
1355
+ ) {
1356
+ event.preventDefault()
1357
+ const from = $pos.pos
1358
+ view.dispatch(
1359
+ view.state.tr.setMeta(key, {
1360
+ action: "activate",
1361
+ from: from,
1362
+ query: `@#${nodeAfter.attrs.id || ""}`,
1363
+ isEdit: true,
1364
+ referenceNodePos: from,
1365
+ bibList: buildBibliographyList(editor),
1366
+ cursorAtStart: true
1367
+ })
1368
+ )
1369
+ return true
1370
+ }
1371
+ }
1372
+ }
1373
+ // ArrowLeft into citation or cross_reference from the right
1374
+ if (event.key === "ArrowLeft") {
1375
+ const sel = view.state.selection
1376
+ if (sel.empty) {
1377
+ const $pos = sel.$head
1378
+ const nodeBefore = $pos.nodeBefore
1379
+ if (
1380
+ nodeBefore &&
1381
+ nodeBefore.type.name === "citation"
1382
+ ) {
1383
+ event.preventDefault()
1384
+ const from = $pos.pos - nodeBefore.nodeSize
1385
+ const bibList = buildBibliographyList(editor)
1386
+ const text = citationToText(nodeBefore, bibList)
1387
+ view.dispatch(
1388
+ view.state.tr.setMeta(key, {
1389
+ action: "activate",
1390
+ from: from,
1391
+ query: text,
1392
+ isEdit: true,
1393
+ referenceNodePos: from,
1394
+ bibList: bibList,
1395
+ cursorAtStart: false
1396
+ })
1397
+ )
1398
+ return true
1399
+ }
1400
+ if (
1401
+ nodeBefore &&
1402
+ nodeBefore.type.name === "cross_reference"
1403
+ ) {
1404
+ event.preventDefault()
1405
+ const from = $pos.pos - nodeBefore.nodeSize
1406
+ view.dispatch(
1407
+ view.state.tr.setMeta(key, {
1408
+ action: "activate",
1409
+ from: from,
1410
+ query: `@#${nodeBefore.attrs.id || ""}`,
1411
+ isEdit: true,
1412
+ referenceNodePos: from,
1413
+ bibList: buildBibliographyList(editor),
1414
+ cursorAtStart: false
1415
+ })
1416
+ )
1417
+ return true
1418
+ }
1419
+ }
1420
+ }
1421
+ return false
1422
+ }
1423
+ // When active, the widget traps its own key events.
1424
+ // Only handle navigation keys that should move the main cursor.
1425
+ return false
1426
+ }
1427
+ }
1428
+ })
1429
+ }