@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,606 @@
1
+ /**
2
+ * PassphraseCrypto - Client-side cryptography for the Personal Passphrase system.
3
+ *
4
+ * Handles:
5
+ * - Master key (MK) generation and storage
6
+ * - ECDH P-256 key pair generation and storage
7
+ * - Recovery key generation
8
+ * - Key-wrapping key (KWK) derivation from passphrase via PBKDF2
9
+ * - Encryption of MK and private key (SK) with KWK
10
+ * - Backup encryption of MK with recovery key
11
+ * - ECIES-style encryption of DEK with recipient's public key
12
+ * - Session storage management for MK and SK
13
+ */
14
+
15
+ export class PassphraseCrypto {
16
+ // --- Key Generation ---
17
+
18
+ /**
19
+ * Generate a new 256-bit AES-GCM master key.
20
+ * The key is marked extractable so it can be exported for sessionStorage.
21
+ */
22
+ static generateMasterKey() {
23
+ return crypto.subtle.generateKey(
24
+ {name: "AES-GCM", length: 256},
25
+ true, // extractable for sessionStorage
26
+ ["encrypt", "decrypt"]
27
+ )
28
+ }
29
+
30
+ /**
31
+ * Generate a random document password that is itself a valid raw DEK.
32
+ * Returns a 44-character base64-encoded 32-byte AES key.
33
+ * This password can be used directly as the encryption key without PBKDF2.
34
+ */
35
+ static async generateDocumentPassword() {
36
+ const key = await crypto.subtle.generateKey(
37
+ {name: "AES-GCM", length: 256},
38
+ true,
39
+ ["encrypt", "decrypt"]
40
+ )
41
+ const raw = await crypto.subtle.exportKey("raw", key)
42
+ return btoa(String.fromCharCode(...new Uint8Array(raw)))
43
+ }
44
+
45
+ /**
46
+ * Generate a new ECDH P-256 key pair.
47
+ * Both keys are marked extractable so the private key can be stored encrypted.
48
+ */
49
+ static generateKeyPair() {
50
+ return crypto.subtle.generateKey(
51
+ {name: "ECDH", namedCurve: "P-256"},
52
+ true, // extractable for encrypted storage
53
+ ["deriveKey"]
54
+ )
55
+ }
56
+
57
+ /**
58
+ * Generate a 128-bit recovery key as a hex string.
59
+ * This is shown to the user once during setup.
60
+ */
61
+ static generateRecoveryKey() {
62
+ const bytes = crypto.getRandomValues(new Uint8Array(16))
63
+ return PassphraseCrypto._bytesToHex(bytes)
64
+ }
65
+
66
+ /**
67
+ * Generate a random 16-byte salt for PBKDF2.
68
+ */
69
+ static generateSalt() {
70
+ return crypto.getRandomValues(new Uint8Array(16))
71
+ }
72
+
73
+ // --- Key Derivation ---
74
+
75
+ /**
76
+ * Derive a key-wrapping key (KWK) from a passphrase and salt using PBKDF2.
77
+ *
78
+ * @param {string} passphrase - The user's personal passphrase
79
+ * @param {Uint8Array} salt - 16-byte random salt
80
+ * @param {number} iterations - PBKDF2 iteration count (default 600000)
81
+ * @returns {Promise<CryptoKey>} An extractable AES-GCM-256 key
82
+ */
83
+ static async deriveKWK(passphrase, salt, iterations = 600000) {
84
+ const encoder = new TextEncoder()
85
+ const keyMaterial = await crypto.subtle.importKey(
86
+ "raw",
87
+ encoder.encode(passphrase),
88
+ "PBKDF2",
89
+ false,
90
+ ["deriveKey"]
91
+ )
92
+ return crypto.subtle.deriveKey(
93
+ {name: "PBKDF2", salt, iterations, hash: "SHA-256"},
94
+ keyMaterial,
95
+ {name: "AES-GCM", length: 256},
96
+ true, // extractable so it can be used to encrypt keys
97
+ ["encrypt", "decrypt"]
98
+ )
99
+ }
100
+
101
+ // --- Key Wrapping / Unwrapping ---
102
+
103
+ /**
104
+ * Wrap (encrypt) a CryptoKey with a wrapping key using AES-KW.
105
+ * Returns a Base64-encoded wrapped key.
106
+ */
107
+ static async wrapKey(keyToWrap, wrappingKey) {
108
+ const wrapped = await crypto.subtle.wrapKey(
109
+ "raw",
110
+ keyToWrap,
111
+ wrappingKey,
112
+ "AES-KW"
113
+ )
114
+ return PassphraseCrypto._bytesToBase64(new Uint8Array(wrapped))
115
+ }
116
+
117
+ /**
118
+ * Unwrap (decrypt) a wrapped key with a wrapping key.
119
+ * Returns the original CryptoKey.
120
+ */
121
+ static unwrapKey(wrappedKeyBase64, wrappingKey, keyType = "AES-GCM") {
122
+ const wrapped = PassphraseCrypto._base64ToBytes(wrappedKeyBase64)
123
+ const algorithm =
124
+ keyType === "AES-GCM"
125
+ ? {name: "AES-GCM", length: 256}
126
+ : {name: "HMAC", hash: "SHA-256", length: 256}
127
+ const usages =
128
+ keyType === "AES-GCM" ? ["encrypt", "decrypt"] : ["sign", "verify"]
129
+ return crypto.subtle.unwrapKey(
130
+ "raw",
131
+ wrapped,
132
+ wrappingKey,
133
+ "AES-KW",
134
+ algorithm,
135
+ true,
136
+ usages
137
+ )
138
+ }
139
+
140
+ /**
141
+ * Wrap a private key (JWK format) with a wrapping key.
142
+ * Returns a Base64-encoded wrapped JWK.
143
+ */
144
+ static async wrapPrivateKey(privateKey, wrappingKey) {
145
+ const wrapped = await crypto.subtle.wrapKey(
146
+ "jwk",
147
+ privateKey,
148
+ wrappingKey,
149
+ "AES-KW"
150
+ )
151
+ return PassphraseCrypto._bytesToBase64(new Uint8Array(wrapped))
152
+ }
153
+
154
+ /**
155
+ * Unwrap a private key (JWK format) with a wrapping key.
156
+ */
157
+ static unwrapPrivateKey(wrappedKeyBase64, wrappingKey) {
158
+ const wrapped = PassphraseCrypto._base64ToBytes(wrappedKeyBase64)
159
+ return crypto.subtle.unwrapKey(
160
+ "jwk",
161
+ wrapped,
162
+ wrappingKey,
163
+ "AES-KW",
164
+ {name: "ECDH", namedCurve: "P-256"},
165
+ true,
166
+ ["deriveKey"]
167
+ )
168
+ }
169
+
170
+ // --- AES-GCM encryption for key material (fallback when AES-KW not suitable) ---
171
+
172
+ /**
173
+ * Encrypt a raw key with AES-GCM.
174
+ * Returns Base64 string: iv (12 bytes) + ciphertext.
175
+ */
176
+ static async encryptKey(key, encryptionKey) {
177
+ const raw = await crypto.subtle.exportKey("raw", key)
178
+ const iv = crypto.getRandomValues(new Uint8Array(12))
179
+ const ciphertext = await crypto.subtle.encrypt(
180
+ {name: "AES-GCM", iv},
181
+ encryptionKey,
182
+ raw
183
+ )
184
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength)
185
+ combined.set(iv, 0)
186
+ combined.set(new Uint8Array(ciphertext), iv.length)
187
+ return PassphraseCrypto._bytesToBase64(combined)
188
+ }
189
+
190
+ /**
191
+ * Decrypt a raw key with AES-GCM.
192
+ */
193
+ static async decryptKey(
194
+ encryptedKeyBase64,
195
+ encryptionKey,
196
+ keyType = "AES-GCM"
197
+ ) {
198
+ const combined = PassphraseCrypto._base64ToBytes(encryptedKeyBase64)
199
+ const iv = combined.slice(0, 12)
200
+ const ciphertext = combined.slice(12)
201
+ const raw = await crypto.subtle.decrypt(
202
+ {name: "AES-GCM", iv},
203
+ encryptionKey,
204
+ ciphertext
205
+ )
206
+ const algorithm =
207
+ keyType === "AES-GCM"
208
+ ? {name: "AES-GCM", length: 256}
209
+ : {name: "HMAC", hash: "SHA-256", length: 256}
210
+ const usages =
211
+ keyType === "AES-GCM" ? ["encrypt", "decrypt"] : ["sign", "verify"]
212
+ return crypto.subtle.importKey("raw", raw, algorithm, true, usages)
213
+ }
214
+
215
+ /**
216
+ * Encrypt a private key (exported as JWK JSON string) with AES-GCM.
217
+ */
218
+ static async encryptPrivateKey(privateKey, encryptionKey) {
219
+ const jwk = await crypto.subtle.exportKey("jwk", privateKey)
220
+ const jwkString = JSON.stringify(jwk)
221
+ const iv = crypto.getRandomValues(new Uint8Array(12))
222
+ const encoded = new TextEncoder().encode(jwkString)
223
+ const ciphertext = await crypto.subtle.encrypt(
224
+ {name: "AES-GCM", iv},
225
+ encryptionKey,
226
+ encoded
227
+ )
228
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength)
229
+ combined.set(iv, 0)
230
+ combined.set(new Uint8Array(ciphertext), iv.length)
231
+ return PassphraseCrypto._bytesToBase64(combined)
232
+ }
233
+
234
+ /**
235
+ * Decrypt a private key (as JWK JSON string) with AES-GCM.
236
+ */
237
+ static async decryptPrivateKey(encryptedPrivateKeyBase64, encryptionKey) {
238
+ const combined = PassphraseCrypto._base64ToBytes(
239
+ encryptedPrivateKeyBase64
240
+ )
241
+ const iv = combined.slice(0, 12)
242
+ const ciphertext = combined.slice(12)
243
+ const decrypted = await crypto.subtle.decrypt(
244
+ {name: "AES-GCM", iv},
245
+ encryptionKey,
246
+ ciphertext
247
+ )
248
+ const jwkString = new TextDecoder().decode(decrypted)
249
+ const jwk = JSON.parse(jwkString)
250
+ return crypto.subtle.importKey(
251
+ "jwk",
252
+ jwk,
253
+ {name: "ECDH", namedCurve: "P-256"},
254
+ true,
255
+ ["deriveKey"]
256
+ )
257
+ }
258
+
259
+ // --- Public Key Import/Export ---
260
+
261
+ /**
262
+ * Export a public key to JWK JSON string for sharing.
263
+ */
264
+ static async exportPublicKey(publicKey) {
265
+ const jwk = await crypto.subtle.exportKey("jwk", publicKey)
266
+ return JSON.stringify(jwk)
267
+ }
268
+
269
+ /**
270
+ * Import a public key from JWK JSON string.
271
+ */
272
+ static importPublicKey(jwkString) {
273
+ const jwk = JSON.parse(jwkString)
274
+ return crypto.subtle.importKey(
275
+ "jwk",
276
+ jwk,
277
+ {name: "ECDH", namedCurve: "P-256"},
278
+ true,
279
+ []
280
+ )
281
+ }
282
+
283
+ // --- ECIES-style DEK encryption for sharing ---
284
+
285
+ /**
286
+ * Encrypt a DEK with a recipient's public key using ECIES-style encryption.
287
+ *
288
+ * 1. Generate an ephemeral ECDH key pair.
289
+ * 2. Derive a shared AES-GCM key from ephemeral private + recipient public.
290
+ * 3. Encrypt the DEK with the shared key.
291
+ * 4. Return ephemeral public key + encrypted DEK.
292
+ *
293
+ * @param {CryptoKey} dek - The document encryption key (AES-GCM key)
294
+ * @param {CryptoKey} recipientPublicKey - The recipient's ECDH public key
295
+ * @returns {Promise<Object>} {ephemeralPublicKeyJwk: string, encryptedDEK: string}
296
+ */
297
+ static async encryptDEKWithPublicKey(dek, recipientPublicKey) {
298
+ // Generate ephemeral key pair
299
+ const ephemeralPair = await crypto.subtle.generateKey(
300
+ {name: "ECDH", namedCurve: "P-256"},
301
+ true,
302
+ ["deriveKey"]
303
+ )
304
+
305
+ // Derive shared AES-GCM key
306
+ const sharedKey = await crypto.subtle.deriveKey(
307
+ {name: "ECDH", public: recipientPublicKey},
308
+ ephemeralPair.privateKey,
309
+ {name: "AES-GCM", length: 256},
310
+ false,
311
+ ["encrypt", "decrypt"]
312
+ )
313
+
314
+ // Export DEK as raw bytes, then encrypt with shared key
315
+ const dekRaw = await crypto.subtle.exportKey("raw", dek)
316
+ const iv = crypto.getRandomValues(new Uint8Array(12))
317
+ const ciphertext = await crypto.subtle.encrypt(
318
+ {name: "AES-GCM", iv},
319
+ sharedKey,
320
+ dekRaw
321
+ )
322
+
323
+ // Export ephemeral public key
324
+ const ephemeralPublicJwk = await crypto.subtle.exportKey(
325
+ "jwk",
326
+ ephemeralPair.publicKey
327
+ )
328
+
329
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength)
330
+ combined.set(iv, 0)
331
+ combined.set(new Uint8Array(ciphertext), iv.length)
332
+
333
+ return {
334
+ ephemeralPublicKeyJwk: JSON.stringify(ephemeralPublicJwk),
335
+ encryptedDEK: PassphraseCrypto._bytesToBase64(combined)
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Decrypt a DEK that was encrypted with the user's public key.
341
+ *
342
+ * @param {string} encryptedDEKBase64 - iv + ciphertext
343
+ * @param {string} ephemeralPublicKeyJwk - JSON string of ephemeral public key JWK
344
+ * @param {CryptoKey} privateKey - The user's ECDH private key
345
+ * @returns {Promise<CryptoKey>} The decrypted DEK (AES-GCM key)
346
+ */
347
+ static async decryptDEKWithPrivateKey(
348
+ encryptedDEKBase64,
349
+ ephemeralPublicKeyJwk,
350
+ privateKey
351
+ ) {
352
+ // Import ephemeral public key
353
+ const ephemeralPublicJwk = JSON.parse(ephemeralPublicKeyJwk)
354
+ const ephemeralPublicKey = await crypto.subtle.importKey(
355
+ "jwk",
356
+ ephemeralPublicJwk,
357
+ {name: "ECDH", namedCurve: "P-256"},
358
+ true,
359
+ []
360
+ )
361
+
362
+ // Derive shared AES-GCM key
363
+ const sharedKey = await crypto.subtle.deriveKey(
364
+ {name: "ECDH", public: ephemeralPublicKey},
365
+ privateKey,
366
+ {name: "AES-GCM", length: 256},
367
+ false,
368
+ ["encrypt", "decrypt"]
369
+ )
370
+
371
+ // Decrypt DEK
372
+ const combined = PassphraseCrypto._base64ToBytes(encryptedDEKBase64)
373
+ const iv = combined.slice(0, 12)
374
+ const ciphertext = combined.slice(12)
375
+ const dekRaw = await crypto.subtle.decrypt(
376
+ {name: "AES-GCM", iv},
377
+ sharedKey,
378
+ ciphertext
379
+ )
380
+
381
+ return crypto.subtle.importKey(
382
+ "raw",
383
+ dekRaw,
384
+ {name: "AES-GCM", length: 256},
385
+ true,
386
+ ["encrypt", "decrypt"]
387
+ )
388
+ }
389
+
390
+ /**
391
+ * Encrypt a string with AES-GCM using a direct key (e.g. master key).
392
+ */
393
+ static async encryptString(str, encryptionKey) {
394
+ const encoder = new TextEncoder()
395
+ const data = encoder.encode(str)
396
+ const iv = crypto.getRandomValues(new Uint8Array(12))
397
+ const ciphertext = await crypto.subtle.encrypt(
398
+ {name: "AES-GCM", iv},
399
+ encryptionKey,
400
+ data
401
+ )
402
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength)
403
+ combined.set(iv, 0)
404
+ combined.set(new Uint8Array(ciphertext), iv.length)
405
+ return PassphraseCrypto._bytesToBase64(combined)
406
+ }
407
+
408
+ /**
409
+ * Decrypt a string with AES-GCM using a direct key.
410
+ */
411
+ static async decryptString(encryptedBase64, encryptionKey) {
412
+ const combined = PassphraseCrypto._base64ToBytes(encryptedBase64)
413
+ const iv = combined.slice(0, 12)
414
+ const ciphertext = combined.slice(12)
415
+ const decrypted = await crypto.subtle.decrypt(
416
+ {name: "AES-GCM", iv},
417
+ encryptionKey,
418
+ ciphertext
419
+ )
420
+ const decoder = new TextDecoder()
421
+ return decoder.decode(decrypted)
422
+ }
423
+
424
+ /**
425
+ * Encrypt a string with a recipient's public key using ECIES-style encryption.
426
+ * Same as encryptDEKWithPublicKey but for string data instead of a CryptoKey.
427
+ */
428
+ static async encryptStringWithPublicKey(str, recipientPublicKey) {
429
+ const encoder = new TextEncoder()
430
+ const data = encoder.encode(str)
431
+
432
+ const ephemeralPair = await crypto.subtle.generateKey(
433
+ {name: "ECDH", namedCurve: "P-256"},
434
+ true,
435
+ ["deriveKey"]
436
+ )
437
+
438
+ const sharedKey = await crypto.subtle.deriveKey(
439
+ {name: "ECDH", public: recipientPublicKey},
440
+ ephemeralPair.privateKey,
441
+ {name: "AES-GCM", length: 256},
442
+ false,
443
+ ["encrypt", "decrypt"]
444
+ )
445
+
446
+ const iv = crypto.getRandomValues(new Uint8Array(12))
447
+ const ciphertext = await crypto.subtle.encrypt(
448
+ {name: "AES-GCM", iv},
449
+ sharedKey,
450
+ data
451
+ )
452
+
453
+ const ephemeralPublicJwk = await crypto.subtle.exportKey(
454
+ "jwk",
455
+ ephemeralPair.publicKey
456
+ )
457
+
458
+ const combined = new Uint8Array(iv.length + ciphertext.byteLength)
459
+ combined.set(iv, 0)
460
+ combined.set(new Uint8Array(ciphertext), iv.length)
461
+
462
+ return {
463
+ ephemeralPublicKeyJwk: JSON.stringify(ephemeralPublicJwk),
464
+ encryptedData: PassphraseCrypto._bytesToBase64(combined)
465
+ }
466
+ }
467
+
468
+ /**
469
+ * Decrypt a string that was encrypted with the user's public key.
470
+ * Returns the plaintext string.
471
+ */
472
+ static async decryptStringWithPrivateKey(
473
+ encryptedDataBase64,
474
+ ephemeralPublicKeyJwk,
475
+ privateKey
476
+ ) {
477
+ const ephemeralPublicJwk = JSON.parse(ephemeralPublicKeyJwk)
478
+ const ephemeralPublicKey = await crypto.subtle.importKey(
479
+ "jwk",
480
+ ephemeralPublicJwk,
481
+ {name: "ECDH", namedCurve: "P-256"},
482
+ true,
483
+ []
484
+ )
485
+
486
+ const sharedKey = await crypto.subtle.deriveKey(
487
+ {name: "ECDH", public: ephemeralPublicKey},
488
+ privateKey,
489
+ {name: "AES-GCM", length: 256},
490
+ false,
491
+ ["encrypt", "decrypt"]
492
+ )
493
+
494
+ const combined = PassphraseCrypto._base64ToBytes(encryptedDataBase64)
495
+ const iv = combined.slice(0, 12)
496
+ const ciphertext = combined.slice(12)
497
+ const decrypted = await crypto.subtle.decrypt(
498
+ {name: "AES-GCM", iv},
499
+ sharedKey,
500
+ ciphertext
501
+ )
502
+
503
+ const decoder = new TextDecoder()
504
+ return decoder.decode(decrypted)
505
+ }
506
+
507
+ // --- Session Storage ---
508
+
509
+ /**
510
+ * Store the master key and private key in sessionStorage.
511
+ * Both keys are exported and Base64-encoded.
512
+ */
513
+ static async storeKeysInSession(masterKey, privateKey) {
514
+ const mkRaw = await crypto.subtle.exportKey("raw", masterKey)
515
+ const mkBase64 = PassphraseCrypto._bytesToBase64(new Uint8Array(mkRaw))
516
+ sessionStorage.setItem("e2ee_master_key", mkBase64)
517
+
518
+ const jwk = await crypto.subtle.exportKey("jwk", privateKey)
519
+ sessionStorage.setItem("e2ee_private_key", JSON.stringify(jwk))
520
+ }
521
+
522
+ /**
523
+ * Retrieve the master key and private key from sessionStorage.
524
+ * Returns {masterKey: CryptoKey|null, privateKey: CryptoKey|null}
525
+ */
526
+ static async getKeysFromSession() {
527
+ const mkBase64 = sessionStorage.getItem("e2ee_master_key")
528
+ const skJwkString = sessionStorage.getItem("e2ee_private_key")
529
+ if (!mkBase64 || !skJwkString) {
530
+ return {masterKey: null, privateKey: null}
531
+ }
532
+ try {
533
+ const mkRaw = PassphraseCrypto._base64ToBytes(mkBase64)
534
+ const masterKey = await crypto.subtle.importKey(
535
+ "raw",
536
+ mkRaw,
537
+ {name: "AES-GCM", length: 256},
538
+ true,
539
+ ["encrypt", "decrypt"]
540
+ )
541
+ const skJwk = JSON.parse(skJwkString)
542
+ const privateKey = await crypto.subtle.importKey(
543
+ "jwk",
544
+ skJwk,
545
+ {name: "ECDH", namedCurve: "P-256"},
546
+ true,
547
+ ["deriveKey"]
548
+ )
549
+ return {masterKey, privateKey}
550
+ } catch (_e) {
551
+ PassphraseCrypto.clearKeysFromSession()
552
+ return {masterKey: null, privateKey: null}
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Check if keys are present in sessionStorage.
558
+ */
559
+ static hasKeysInSession() {
560
+ return !!(
561
+ sessionStorage.getItem("e2ee_master_key") &&
562
+ sessionStorage.getItem("e2ee_private_key")
563
+ )
564
+ }
565
+
566
+ /**
567
+ * Clear master key and private key from sessionStorage.
568
+ */
569
+ static clearKeysFromSession() {
570
+ sessionStorage.removeItem("e2ee_master_key")
571
+ sessionStorage.removeItem("e2ee_private_key")
572
+ }
573
+
574
+ // --- Helpers ---
575
+
576
+ static _bytesToBase64(bytes) {
577
+ let binary = ""
578
+ for (let i = 0; i < bytes.length; i++) {
579
+ binary += String.fromCharCode(bytes[i])
580
+ }
581
+ return btoa(binary)
582
+ }
583
+
584
+ static _base64ToBytes(base64) {
585
+ const binary = atob(base64)
586
+ const bytes = new Uint8Array(binary.length)
587
+ for (let i = 0; i < binary.length; i++) {
588
+ bytes[i] = binary.charCodeAt(i)
589
+ }
590
+ return bytes
591
+ }
592
+
593
+ static _bytesToHex(bytes) {
594
+ return Array.from(bytes)
595
+ .map(b => b.toString(16).padStart(2, "0"))
596
+ .join("")
597
+ }
598
+
599
+ static _hexToBytes(hex) {
600
+ const bytes = new Uint8Array(hex.length / 2)
601
+ for (let i = 0; i < hex.length; i += 2) {
602
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16)
603
+ }
604
+ return bytes
605
+ }
606
+ }