bookish-press 0.4.0 → 0.4.16

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 (394) hide show
  1. package/README.md +3 -1
  2. package/components/Auth.svelte +19 -14
  3. package/components/Auth.svelte.d.ts +4 -4
  4. package/components/Authentication.d.ts +2 -2
  5. package/components/BookPreview.svelte +77 -59
  6. package/components/BookPreview.svelte.d.ts +5 -5
  7. package/components/External.svelte +1 -1
  8. package/components/External.svelte.d.ts +3 -3
  9. package/components/Link.svelte +49 -3
  10. package/components/Link.svelte.d.ts +3 -4
  11. package/components/app/BookList.svelte +19 -0
  12. package/components/app/BookList.svelte.d.ts +18 -0
  13. package/components/app/Button.svelte +49 -0
  14. package/components/app/Button.svelte.d.ts +22 -0
  15. package/components/app/Feedback.svelte +45 -0
  16. package/components/app/Feedback.svelte.d.ts +18 -0
  17. package/components/app/Head.svelte +12 -0
  18. package/components/app/Head.svelte.d.ts +27 -0
  19. package/components/app/Large.svelte +8 -0
  20. package/components/app/Large.svelte.d.ts +27 -0
  21. package/components/app/Lead.svelte +12 -0
  22. package/components/app/Lead.svelte.d.ts +27 -0
  23. package/components/app/Link.svelte +24 -0
  24. package/components/app/Link.svelte.d.ts +20 -0
  25. package/components/app/Options.svelte +24 -0
  26. package/components/app/Options.svelte.d.ts +19 -0
  27. package/components/app/Paragraph.svelte +19 -0
  28. package/components/app/Paragraph.svelte.d.ts +18 -0
  29. package/components/app/Table.svelte +32 -0
  30. package/components/app/Table.svelte.d.ts +27 -0
  31. package/components/app/TextInput.svelte +37 -0
  32. package/components/app/TextInput.svelte.d.ts +22 -0
  33. package/components/app/Writing.svelte +13 -0
  34. package/components/app/Writing.svelte.d.ts +27 -0
  35. package/components/chapter/Atom.svelte +20 -6
  36. package/components/chapter/Atom.svelte.d.ts +3 -3
  37. package/components/chapter/Block.svelte +23 -20
  38. package/components/chapter/Block.svelte.d.ts +4 -4
  39. package/components/chapter/Callout.svelte +38 -7
  40. package/components/chapter/Callout.svelte.d.ts +4 -4
  41. package/components/chapter/CaptionedCode.svelte +66 -23
  42. package/components/chapter/CaptionedCode.svelte.d.ts +4 -4
  43. package/components/chapter/ChapterBody.svelte +61 -9
  44. package/components/chapter/ChapterBody.svelte.d.ts +4 -4
  45. package/components/chapter/Citations.svelte +86 -20
  46. package/components/chapter/Citations.svelte.d.ts +4 -4
  47. package/components/chapter/Code.svelte +152 -21
  48. package/components/chapter/Code.svelte.d.ts +3 -3
  49. package/components/chapter/Comment.svelte +38 -16
  50. package/components/chapter/Comment.svelte.d.ts +3 -3
  51. package/components/chapter/Definition.svelte +52 -18
  52. package/components/chapter/Definition.svelte.d.ts +5 -8
  53. package/components/chapter/Embed.svelte +154 -66
  54. package/components/chapter/Embed.svelte.d.ts +5 -4
  55. package/components/chapter/ErrorMessage.svelte +7 -2
  56. package/components/chapter/ErrorMessage.svelte.d.ts +4 -4
  57. package/components/chapter/ExtendedPrism.d.ts +2 -0
  58. package/components/chapter/ExtendedPrism.js +69 -0
  59. package/components/chapter/Figure.svelte +90 -0
  60. package/components/chapter/Figure.svelte.d.ts +24 -0
  61. package/components/chapter/Footnote.svelte +75 -12
  62. package/components/chapter/Footnote.svelte.d.ts +4 -4
  63. package/components/chapter/Format.svelte +60 -37
  64. package/components/chapter/Format.svelte.d.ts +4 -4
  65. package/components/chapter/InlineCode.svelte +15 -5
  66. package/components/chapter/InlineCode.svelte.d.ts +4 -4
  67. package/components/chapter/Label.svelte +37 -8
  68. package/components/chapter/Label.svelte.d.ts +4 -4
  69. package/components/chapter/Link.svelte +23 -18
  70. package/components/chapter/Link.svelte.d.ts +4 -4
  71. package/components/chapter/List.svelte +75 -8
  72. package/components/chapter/List.svelte.d.ts +4 -4
  73. package/components/chapter/Marginal.svelte +169 -27
  74. package/components/chapter/Marginal.svelte.d.ts +6 -3
  75. package/components/chapter/Paragraph.svelte +74 -6
  76. package/components/chapter/Paragraph.svelte.d.ts +4 -4
  77. package/components/chapter/Positioned.svelte +33 -0
  78. package/components/chapter/Positioned.svelte.d.ts +19 -0
  79. package/components/chapter/Problem.svelte +15 -0
  80. package/components/chapter/Problem.svelte.d.ts +27 -0
  81. package/components/chapter/Python.svelte +68 -21
  82. package/components/chapter/Python.svelte.d.ts +4 -4
  83. package/components/chapter/Quote.svelte +64 -11
  84. package/components/chapter/Quote.svelte.d.ts +4 -4
  85. package/components/chapter/Rule.svelte +12 -1
  86. package/components/chapter/Rule.svelte.d.ts +4 -4
  87. package/components/chapter/Table.svelte +36 -25
  88. package/components/chapter/Table.svelte.d.ts +4 -4
  89. package/components/chapter/Text.svelte +61 -36
  90. package/components/chapter/Text.svelte.d.ts +5 -4
  91. package/components/editor/BookishEditor.svelte +343 -237
  92. package/components/editor/BookishEditor.svelte.d.ts +5 -5
  93. package/components/editor/CalloutEditor.svelte +5 -5
  94. package/components/editor/CalloutEditor.svelte.d.ts +4 -4
  95. package/components/editor/CaptionedCodeEditor.svelte +16 -12
  96. package/components/editor/CaptionedCodeEditor.svelte.d.ts +4 -4
  97. package/components/editor/CaretContext.d.ts +48 -16
  98. package/components/editor/CaretState.d.ts +16 -44
  99. package/components/editor/CaretUtilities.d.ts +2 -2
  100. package/components/editor/CaretView.svelte +80 -0
  101. package/components/editor/CaretView.svelte.d.ts +24 -0
  102. package/components/editor/CitationsEditor.svelte +36 -25
  103. package/components/editor/CitationsEditor.svelte.d.ts +4 -4
  104. package/components/editor/Clipboard.d.ts +2 -2
  105. package/components/editor/Command.d.ts +13 -12
  106. package/components/editor/Commands.d.ts +1 -1
  107. package/components/editor/Commands.js +1114 -571
  108. package/components/editor/ConfirmButton.svelte +18 -12
  109. package/components/editor/ConfirmButton.svelte.d.ts +5 -4
  110. package/components/editor/DefinitionEditor.svelte +29 -21
  111. package/components/editor/DefinitionEditor.svelte.d.ts +4 -4
  112. package/components/editor/EmbedEditor.svelte +93 -43
  113. package/components/editor/EmbedEditor.svelte.d.ts +3 -3
  114. package/components/editor/Icon.svelte +17 -0
  115. package/components/editor/Icon.svelte.d.ts +16 -0
  116. package/components/editor/ImageChooser.svelte +53 -34
  117. package/components/editor/ImageChooser.svelte.d.ts +3 -3
  118. package/components/editor/InlineCodeEditor.svelte +7 -4
  119. package/components/editor/InlineCodeEditor.svelte.d.ts +4 -4
  120. package/components/editor/LabelEditor.svelte +20 -11
  121. package/components/editor/LabelEditor.svelte.d.ts +4 -4
  122. package/components/editor/LanguageEditor.svelte +10 -7
  123. package/components/editor/LanguageEditor.svelte.d.ts +3 -3
  124. package/components/editor/LinkEditor.svelte +64 -39
  125. package/components/editor/LinkEditor.svelte.d.ts +4 -4
  126. package/components/editor/Note.svelte +3 -3
  127. package/components/editor/Note.svelte.d.ts +6 -2
  128. package/components/editor/PositionEditor.svelte +9 -6
  129. package/components/editor/PositionEditor.svelte.d.ts +4 -4
  130. package/components/editor/QuoteEditor.svelte +7 -2
  131. package/components/editor/QuoteEditor.svelte.d.ts +4 -4
  132. package/components/editor/Switch.svelte +55 -28
  133. package/components/editor/Switch.svelte.d.ts +4 -4
  134. package/components/editor/TableEditor.svelte +10 -0
  135. package/components/editor/TableEditor.svelte.d.ts +17 -0
  136. package/components/editor/TextEditor.svelte +83 -45
  137. package/components/editor/TextEditor.svelte.d.ts +5 -4
  138. package/components/editor/Toggle.svelte +11 -8
  139. package/components/editor/Toggle.svelte.d.ts +4 -4
  140. package/components/editor/Toolbar.svelte +192 -144
  141. package/components/editor/Toolbar.svelte.d.ts +5 -8
  142. package/components/editor/ToolbarGroup.svelte +18 -23
  143. package/components/editor/ToolbarGroup.svelte.d.ts +3 -4
  144. package/components/editor/ToolbarSpacer.svelte +8 -1
  145. package/components/editor/ToolbarSpacer.svelte.d.ts +6 -2
  146. package/components/editor/URLEditor.svelte +6 -8
  147. package/components/editor/URLEditor.svelte.d.ts +3 -3
  148. package/components/editor/UndoState.d.ts +3 -3
  149. package/components/editor/icons/annotate.svg +1 -0
  150. package/components/editor/icons/block.svg +1 -0
  151. package/components/editor/icons/bold.svg +1 -0
  152. package/components/editor/icons/bullets.svg +1 -0
  153. package/components/editor/icons/callout.svg +1 -0
  154. package/components/editor/icons/citation.svg +8 -0
  155. package/components/editor/icons/clear.svg +1 -0
  156. package/components/editor/icons/clipboard.svg +1 -0
  157. package/components/editor/icons/code.svg +1 -0
  158. package/components/editor/icons/columnafter.svg +1 -0
  159. package/components/editor/icons/columnbefore.svg +1 -0
  160. package/components/editor/icons/comment.svg +1 -0
  161. package/components/editor/icons/copy.svg +1 -0
  162. package/components/editor/icons/cut.svg +1 -0
  163. package/components/editor/icons/define.svg +7 -0
  164. package/components/editor/icons/deletecolumn.svg +1 -0
  165. package/components/editor/icons/deleterow.svg +1 -0
  166. package/components/editor/icons/footnote.svg +8 -0
  167. package/components/editor/icons/header1.svg +7 -0
  168. package/components/editor/icons/header2.svg +7 -0
  169. package/components/editor/icons/header3.svg +7 -0
  170. package/components/editor/icons/home.svg +1 -0
  171. package/components/editor/icons/indent.svg +1 -0
  172. package/components/editor/icons/italic.svg +1 -0
  173. package/components/editor/icons/label.svg +1 -0
  174. package/components/editor/icons/left.svg +1 -0
  175. package/components/editor/icons/level.svg +1 -0
  176. package/components/editor/icons/link.svg +1 -0
  177. package/components/editor/icons/list.svg +1 -0
  178. package/components/editor/icons/media.svg +1 -0
  179. package/components/editor/icons/middle.svg +1 -0
  180. package/components/editor/icons/numbers.svg +10 -0
  181. package/components/editor/icons/paragraph.svg +1 -0
  182. package/components/editor/icons/paste.svg +10 -0
  183. package/components/editor/icons/quote.svg +1 -0
  184. package/components/editor/icons/redo.svg +1 -0
  185. package/components/editor/icons/right.svg +1 -0
  186. package/components/editor/icons/rowabove.svg +1 -0
  187. package/components/editor/icons/rowbelow.svg +1 -0
  188. package/components/editor/icons/rule.svg +1 -0
  189. package/components/editor/icons/sub.svg +1 -0
  190. package/components/editor/icons/super.svg +1 -0
  191. package/components/editor/icons/table.svg +1 -0
  192. package/components/editor/icons/text.svg +1 -0
  193. package/components/editor/icons/time.svg +1 -0
  194. package/components/editor/icons/undo.svg +1 -0
  195. package/components/editor/icons/unindent.svg +1 -0
  196. package/components/editor/icons/unlink.svg +1 -0
  197. package/components/page/Acknowledgements.svelte +31 -22
  198. package/components/page/Acknowledgements.svelte.d.ts +4 -4
  199. package/components/page/Authors.svelte +34 -14
  200. package/components/page/Authors.svelte.d.ts +6 -6
  201. package/components/page/BulkReferenceEditor.svelte +53 -22
  202. package/components/page/BulkReferenceEditor.svelte.d.ts +4 -4
  203. package/components/page/Chapter.svelte +196 -141
  204. package/components/page/Chapter.svelte.d.ts +3 -3
  205. package/components/page/ChapterContext.d.ts +4 -4
  206. package/components/page/ChapterNumber.svelte +9 -0
  207. package/components/page/ChapterNumber.svelte.d.ts +27 -0
  208. package/components/page/ChapterTitle.svelte +16 -0
  209. package/components/page/ChapterTitle.svelte.d.ts +18 -0
  210. package/components/page/Contexts.d.ts +31 -20
  211. package/components/page/Contexts.js +47 -17
  212. package/components/page/DefinitionView.svelte +115 -86
  213. package/components/page/DefinitionView.svelte.d.ts +3 -3
  214. package/components/page/Description.svelte +27 -18
  215. package/components/page/Description.svelte.d.ts +4 -4
  216. package/components/page/Edition.svelte +127 -74
  217. package/components/page/Edition.svelte.d.ts +4 -4
  218. package/components/page/Editions.svelte +144 -0
  219. package/components/page/Editions.svelte.d.ts +14 -0
  220. package/components/page/Glossary.svelte +57 -48
  221. package/components/page/Glossary.svelte.d.ts +4 -4
  222. package/components/page/Header.svelte +129 -44
  223. package/components/page/Header.svelte.d.ts +6 -6
  224. package/components/page/Index.svelte +114 -80
  225. package/components/page/Index.svelte.d.ts +4 -4
  226. package/components/page/Instructions.svelte +18 -16
  227. package/components/page/Instructions.svelte.d.ts +4 -4
  228. package/components/page/License.svelte +29 -21
  229. package/components/page/License.svelte.d.ts +4 -4
  230. package/components/page/Loading.svelte +102 -12
  231. package/components/page/Loading.svelte.d.ts +6 -2
  232. package/components/page/Media.svelte +77 -51
  233. package/components/page/Media.svelte.d.ts +4 -4
  234. package/components/page/MediaPreview.svelte +26 -8
  235. package/components/page/MediaPreview.svelte.d.ts +3 -3
  236. package/components/page/Muted.svelte +10 -0
  237. package/components/page/Muted.svelte.d.ts +27 -0
  238. package/components/page/Outline.svelte +279 -59
  239. package/components/page/Outline.svelte.d.ts +3 -3
  240. package/components/page/Page.svelte +40 -9
  241. package/components/page/Page.svelte.d.ts +3 -3
  242. package/components/page/PageHeader.svelte +14 -0
  243. package/components/page/PageHeader.svelte.d.ts +18 -0
  244. package/components/page/PageParagraph.svelte +18 -0
  245. package/components/page/PageParagraph.svelte.d.ts +27 -0
  246. package/components/page/PossibleChapter.svelte +7 -7
  247. package/components/page/PossibleChapter.svelte.d.ts +4 -4
  248. package/components/page/PossibleReference.svelte +6 -11
  249. package/components/page/PossibleReference.svelte.d.ts +6 -7
  250. package/components/page/Print.svelte +27 -3
  251. package/components/page/Print.svelte.d.ts +4 -4
  252. package/components/page/Reference.svelte +256 -113
  253. package/components/page/Reference.svelte.d.ts +5 -5
  254. package/components/page/References.svelte +39 -47
  255. package/components/page/References.svelte.d.ts +4 -4
  256. package/components/page/Rows.svelte +54 -0
  257. package/components/page/Rows.svelte.d.ts +27 -0
  258. package/components/page/Search.svelte +86 -63
  259. package/components/page/Search.svelte.d.ts +4 -4
  260. package/components/page/Status.svelte +43 -35
  261. package/components/page/Status.svelte.d.ts +4 -9
  262. package/components/page/SubdomainEditor.svelte +40 -27
  263. package/components/page/SubdomainEditor.svelte.d.ts +4 -4
  264. package/components/page/TableOfContents.svelte +225 -190
  265. package/components/page/TableOfContents.svelte.d.ts +4 -4
  266. package/components/page/TableOfContentsImage.svelte +8 -8
  267. package/components/page/TableOfContentsImage.svelte.d.ts +3 -3
  268. package/components/page/TableOfContentsRow.svelte +96 -33
  269. package/components/page/TableOfContentsRow.svelte.d.ts +4 -4
  270. package/components/page/ThemeEditor.svelte +174 -39
  271. package/components/page/ThemeEditor.svelte.d.ts +4 -4
  272. package/components/page/ThemeEditorPreview.svelte +35 -23
  273. package/components/page/ThemeEditorPreview.svelte.d.ts +5 -5
  274. package/components/page/ThemeSetEditor.svelte +15 -13
  275. package/components/page/ThemeSetEditor.svelte.d.ts +4 -4
  276. package/components/page/ThemeVariableEditor.svelte +24 -15
  277. package/components/page/ThemeVariableEditor.svelte.d.ts +3 -3
  278. package/components/page/Title.svelte +12 -0
  279. package/components/page/Title.svelte.d.ts +27 -0
  280. package/components/page/Unknown.svelte +25 -21
  281. package/components/page/Unknown.svelte.d.ts +4 -4
  282. package/components/page/margins.js +50 -33
  283. package/index.d.ts +14 -0
  284. package/index.js +14 -0
  285. package/models/CRUD.d.ts +17 -0
  286. package/models/CRUD.js +194 -0
  287. package/models/Firebase.js +17 -15
  288. package/models/book/Book.d.ts +24 -28
  289. package/models/book/Book.js +120 -113
  290. package/models/book/BookMedia.d.ts +3 -3
  291. package/models/book/BookMedia.js +22 -16
  292. package/models/book/BookSaveStatus.d.ts +7 -0
  293. package/models/book/BookSaveStatus.js +8 -0
  294. package/models/book/Chapter.d.ts +46 -39
  295. package/models/book/Chapter.js +180 -198
  296. package/models/book/ChapterID.js +7 -7
  297. package/models/book/Definition.d.ts +1 -1
  298. package/models/book/Edition.d.ts +62 -60
  299. package/models/book/Edition.js +385 -303
  300. package/models/book/Reference.d.ts +24 -0
  301. package/models/book/Reference.js +78 -0
  302. package/models/book/Schema.d.ts +169 -0
  303. package/models/book/Schema.js +114 -0
  304. package/models/book/Theme.d.ts +44 -37
  305. package/models/book/Theme.js +452 -52
  306. package/models/book/ThemeColors.d.ts +1 -1
  307. package/models/book/Words.d.ts +2 -0
  308. package/models/book/Words.js +110 -0
  309. package/models/chapter/AtomNode.d.ts +4 -4
  310. package/models/chapter/AtomNode.js +20 -13
  311. package/models/chapter/BlockNode.d.ts +9 -10
  312. package/models/chapter/BlockNode.js +32 -13
  313. package/models/chapter/BlocksNode.d.ts +10 -10
  314. package/models/chapter/BlocksNode.js +315 -120
  315. package/models/chapter/BlocksNode.test.js +494 -228
  316. package/models/chapter/Bookkeeping.d.ts +1 -1
  317. package/models/chapter/CalloutNode.d.ts +3 -3
  318. package/models/chapter/CalloutNode.js +34 -12
  319. package/models/chapter/Caret.d.ts +9 -7
  320. package/models/chapter/Caret.js +11 -6
  321. package/models/chapter/ChapterNode.d.ts +11 -11
  322. package/models/chapter/ChapterNode.js +70 -33
  323. package/models/chapter/CitationsNode.d.ts +3 -3
  324. package/models/chapter/CitationsNode.js +31 -12
  325. package/models/chapter/CodeNode.d.ts +11 -6
  326. package/models/chapter/CodeNode.js +143 -34
  327. package/models/chapter/CommentNode.d.ts +5 -5
  328. package/models/chapter/CommentNode.js +29 -13
  329. package/models/chapter/DefinitionNode.d.ts +2 -2
  330. package/models/chapter/DefinitionNode.js +15 -7
  331. package/models/chapter/Edit.d.ts +3 -3
  332. package/models/chapter/EmbedNode.d.ts +9 -5
  333. package/models/chapter/EmbedNode.js +108 -35
  334. package/models/chapter/ErrorNode.d.ts +2 -2
  335. package/models/chapter/ErrorNode.js +34 -12
  336. package/models/chapter/FootnoteNode.d.ts +5 -5
  337. package/models/chapter/FootnoteNode.js +34 -16
  338. package/models/chapter/FormatNode.d.ts +10 -10
  339. package/models/chapter/FormatNode.js +237 -106
  340. package/models/chapter/FormatNode.test.js +87 -45
  341. package/models/chapter/InlineCodeNode.d.ts +2 -2
  342. package/models/chapter/InlineCodeNode.js +22 -9
  343. package/models/chapter/LabelNode.d.ts +4 -4
  344. package/models/chapter/LabelNode.js +31 -13
  345. package/models/chapter/LinkNode.d.ts +2 -2
  346. package/models/chapter/LinkNode.js +21 -9
  347. package/models/chapter/ListNode.d.ts +8 -8
  348. package/models/chapter/ListNode.js +99 -38
  349. package/models/chapter/ListNode.test.js +29 -24
  350. package/models/chapter/MetadataNode.d.ts +3 -3
  351. package/models/chapter/MetadataNode.js +13 -5
  352. package/models/chapter/Node.d.ts +5 -3
  353. package/models/chapter/Node.js +76 -29
  354. package/models/chapter/Node.test.js +20 -18
  355. package/models/chapter/ParagraphNode.d.ts +6 -6
  356. package/models/chapter/ParagraphNode.js +84 -29
  357. package/models/chapter/ParagraphNode.test.js +22 -13
  358. package/models/chapter/Parser.d.ts +25 -25
  359. package/models/chapter/Parser.js +201 -189
  360. package/models/chapter/Parser.test.js +16 -16
  361. package/models/chapter/Position.d.ts +1 -1
  362. package/models/chapter/QuoteNode.d.ts +6 -6
  363. package/models/chapter/QuoteNode.js +61 -23
  364. package/models/chapter/RootNode.d.ts +4 -4
  365. package/models/chapter/RuleNode.d.ts +2 -2
  366. package/models/chapter/RuleNode.js +31 -11
  367. package/models/chapter/TableNode.d.ts +8 -6
  368. package/models/chapter/TableNode.js +89 -34
  369. package/models/chapter/TableNode.test.js +22 -24
  370. package/models/chapter/TextNode.d.ts +6 -6
  371. package/models/chapter/TextNode.js +72 -32
  372. package/models/chapter/TextNode.test.js +4 -4
  373. package/models/loadEdition.d.ts +1 -1
  374. package/models/loadEdition.js +10 -6
  375. package/package.json +104 -25
  376. package/util/getSubdomain.js +8 -6
  377. package/util/isMobile.js +1 -1
  378. package/util/mineReference.d.ts +3 -2
  379. package/util/mineReference.js +67 -29
  380. package/util/scrollToEyeLevel.js +6 -1
  381. package/components/chapter/renderPosition.d.ts +0 -3
  382. package/components/chapter/renderPosition.js +0 -4
  383. package/components/editor/ToolbarIcon.svelte +0 -22
  384. package/components/editor/ToolbarIcon.svelte.d.ts +0 -16
  385. package/components/page/Alert.svelte +0 -11
  386. package/components/page/Alert.svelte.d.ts +0 -23
  387. package/components/page/Revisions.svelte +0 -124
  388. package/components/page/Revisions.svelte.d.ts +0 -14
  389. package/models/BookLoader.d.ts +0 -2
  390. package/models/BookLoader.js +0 -67
  391. package/models/Firestore.d.ts +0 -22
  392. package/models/Firestore.js +0 -194
  393. package/models/chapter/ReferenceNode.d.ts +0 -29
  394. package/models/chapter/ReferenceNode.js +0 -48
@@ -1,37 +1,89 @@
1
- import CalloutNode from "../../models/chapter/CalloutNode";
2
- import CitationsNode from "../../models/chapter/CitationsNode";
3
- import CodeNode from "../../models/chapter/CodeNode";
4
- import CommentNode from "../../models/chapter/CommentNode";
5
- import DefinitionNode from "../../models/chapter/DefinitionNode";
6
- import EmbedNode from "../../models/chapter/EmbedNode";
7
- import FootnoteNode from "../../models/chapter/FootnoteNode";
8
- import FormatNode from "../../models/chapter/FormatNode";
9
- import InlineCodeNode from "../../models/chapter/InlineCodeNode";
10
- import LabelNode from "../../models/chapter/LabelNode";
11
- import LinkNode from "../../models/chapter/LinkNode";
12
- import ListNode from "../../models/chapter/ListNode";
13
- import ParagraphNode from "../../models/chapter/ParagraphNode";
14
- import QuoteNode from "../../models/chapter/QuoteNode";
15
- import RuleNode from "../../models/chapter/RuleNode";
16
- import TableNode from "../../models/chapter/TableNode";
17
- import TextNode from "../../models/chapter/TextNode";
18
- import AtomNode from "../../models/chapter/AtomNode";
19
- import BlocksNode from "../../models/chapter/BlocksNode";
20
- // SVG Icons from: https://thenounproject.com/browse/collection-icon/minio-text-editor-bold-13520/?p=1
1
+ import CalloutNode from '../../models/chapter/CalloutNode';
2
+ import CitationsNode from '../../models/chapter/CitationsNode';
3
+ import CodeNode from '../../models/chapter/CodeNode';
4
+ import CommentNode from '../../models/chapter/CommentNode';
5
+ import DefinitionNode from '../../models/chapter/DefinitionNode';
6
+ import EmbedNode from '../../models/chapter/EmbedNode';
7
+ import FootnoteNode from '../../models/chapter/FootnoteNode';
8
+ import FormatNode from '../../models/chapter/FormatNode';
9
+ import InlineCodeNode from '../../models/chapter/InlineCodeNode';
10
+ import LabelNode from '../../models/chapter/LabelNode';
11
+ import LinkNode from '../../models/chapter/LinkNode';
12
+ import ListNode from '../../models/chapter/ListNode';
13
+ import ParagraphNode from '../../models/chapter/ParagraphNode';
14
+ import QuoteNode from '../../models/chapter/QuoteNode';
15
+ import RuleNode from '../../models/chapter/RuleNode';
16
+ import TableNode from '../../models/chapter/TableNode';
17
+ import TextNode from '../../models/chapter/TextNode';
18
+ import AtomNode from '../../models/chapter/AtomNode';
19
+ import BlocksNode from '../../models/chapter/BlocksNode';
20
+ import BoldIcon from './icons/bold.svg?raw';
21
+ import BulletsIcon from './icons/bullets.svg?raw';
22
+ import CalloutIcon from './icons/callout.svg?raw';
23
+ import CitationIcon from './icons/citation.svg?raw';
24
+ import ClearIcon from './icons/clear.svg?raw';
25
+ import CodeIcon from './icons/code.svg?raw';
26
+ import ColumnAfterIcon from './icons/columnafter.svg?raw';
27
+ import ColumnBeforeIcon from './icons/columnbefore.svg?raw';
28
+ import CommentIcon from './icons/comment.svg?raw';
29
+ import CopyIcon from './icons/copy.svg?raw';
30
+ import CutIcon from './icons/cut.svg?raw';
31
+ import DefineIcon from './icons/define.svg?raw';
32
+ import DeleteColumnIcon from './icons/deletecolumn.svg?raw';
33
+ import DeleteRowIcon from './icons/deleterow.svg?raw';
34
+ import FootnoteIcon from './icons/footnote.svg?raw';
35
+ import Header1Icon from './icons/header1.svg?raw';
36
+ import Header2Icon from './icons/header2.svg?raw';
37
+ import Header3Icon from './icons/header3.svg?raw';
38
+ import IndentIcon from './icons/indent.svg?raw';
39
+ import ItalicIcon from './icons/italic.svg?raw';
40
+ import LabelIcon from './icons/label.svg?raw';
41
+ import LinkIcon from './icons/link.svg?raw';
42
+ import MediaIcon from './icons/media.svg?raw';
43
+ import NumbersIcon from './icons/numbers.svg?raw';
44
+ import ParagraphIcon from './icons/paragraph.svg?raw';
45
+ import PasteIcon from './icons/paste.svg?raw';
46
+ import QuoteIcon from './icons/quote.svg?raw';
47
+ import RedoIcon from './icons/redo.svg?raw';
48
+ import RowAboveIcon from './icons/rowabove.svg?raw';
49
+ import RowBelowIcon from './icons/rowbelow.svg?raw';
50
+ import RuleIcon from './icons/rule.svg?raw';
51
+ import SubscriptIcon from './icons/sub.svg?raw';
52
+ import SuperscriptIcon from './icons/super.svg?raw';
53
+ import TableIcon from './icons/table.svg?raw';
54
+ import UndoIcon from './icons/undo.svg?raw';
55
+ import UnindentIcon from './icons/unindent.svg?raw';
56
+ /**
57
+ * Icons:
58
+ * From: https://www.streamlinehq.com/icons/streamline-mini-line
59
+ * All use CSS currentColor
60
+ * No background
61
+ * SVG
62
+ * 48 px
63
+ * stroke width 1
64
+ * */
21
65
  function insertTableRowColumn(context, table, format, row, before) {
22
66
  const location = table.locate(format);
23
67
  if (location === undefined)
24
68
  return;
25
- return rootWithNode(context, table, row ? table.withNewRow(location.row + (before ? 0 : 1)) : table.withNewColumn(location.column + (before ? 0 : 1)), t => t.getCell(location.row + (row && !before ? 1 : 0), location.column + (!row && !before ? 1 : 0)).getFirstCaret());
69
+ return rootWithNode(context, table, row
70
+ ? table.withNewRow(location.row + (before ? 0 : 1))
71
+ : table.withNewColumn(location.column + (before ? 0 : 1)), (t) => t.getCell(location.row + (row && !before ? 1 : 0), location.column + (!row && !before ? 1 : 0)).getFirstCaret());
26
72
  }
27
73
  function deleteTableRowColumn(context, table, format, row) {
28
74
  const location = table.locate(format);
29
75
  if (location === undefined)
30
76
  return;
31
- return rootWithNode(context, table, row ? table.withoutRow(location.row) : table.withoutColumn(location.column), t => {
32
- return (row ?
33
- t.getCell(location.row === table.getRowCount() ? location.row - 1 : location.row, location.column) :
34
- t.getCell(location.row, location.column === table.getColumnCount() ? location.column - 1 : location.column))?.getFirstCaret();
77
+ return rootWithNode(context, table, row
78
+ ? table.withoutRow(location.row)
79
+ : table.withoutColumn(location.column), (t) => {
80
+ return (row
81
+ ? t.getCell(location.row === table.getRowCount()
82
+ ? location.row - 1
83
+ : location.row, location.column)
84
+ : t.getCell(location.row, location.column === table.getColumnCount()
85
+ ? location.column - 1
86
+ : location.column))?.getFirstCaret();
35
87
  });
36
88
  }
37
89
  // A helper function that encapsulates boilerplate for replacing a node in a root and updating a caret.
@@ -48,245 +100,496 @@ function rootWithNode(context, original, replacement, caret) {
48
100
  if (caret !== undefined && newCaret === undefined)
49
101
  return;
50
102
  // Return the edit
51
- return { root: newRoot, range: newCaret !== undefined ? { start: newCaret, end: newCaret } : context.range };
103
+ return {
104
+ root: newRoot,
105
+ range: newCaret !== undefined
106
+ ? { start: newCaret, end: newCaret }
107
+ : context.range,
108
+ };
52
109
  }
53
110
  // An ordered list of command specifications for keyboard and mouse input.
54
111
  const commands = [
55
112
  {
56
- label: "—↑",
57
- description: "insert row above",
58
- category: "table",
59
- control: false, alt: true, shift: false, key: "ArrowUp",
60
- visible: context => context.table !== undefined,
61
- active: context => context.table !== undefined && context.format !== undefined,
62
- handler: context => context.table !== undefined && context.format !== undefined ? insertTableRowColumn(context, context.table, context.format, true, true) : undefined
63
- },
64
- {
65
- label: "—↓",
66
- description: "insert row below",
67
- category: "table",
68
- control: false, alt: true, shift: false, key: "ArrowDown",
69
- visible: context => context.table !== undefined,
70
- active: context => context.table !== undefined && context.format !== undefined,
71
- handler: context => context.table !== undefined && context.format !== undefined ? insertTableRowColumn(context, context.table, context.format, true, false) : undefined
72
- },
73
- {
74
- label: "→|",
75
- description: "insert column after",
76
- category: "table",
77
- control: false, alt: true, shift: false, key: "ArrowRight",
78
- visible: context => context.table !== undefined,
79
- active: context => context.table !== undefined && context.format !== undefined,
80
- handler: context => context.table !== undefined && context.format !== undefined ? insertTableRowColumn(context, context.table, context.format, false, false) : undefined
81
- },
82
- {
83
- label: "|←",
84
- description: "insert column before",
85
- category: "table",
86
- control: false, alt: true, shift: false, key: "ArrowLeft",
87
- visible: context => context.table !== undefined,
88
- active: context => context.table !== undefined && context.format !== undefined,
89
- handler: context => context.table !== undefined && context.format !== undefined ? insertTableRowColumn(context, context.table, context.format, false, true) : undefined
90
- },
91
- {
92
- label: "\u232B—",
93
- description: "delete row",
94
- category: "table",
95
- control: false, alt: true, shift: false, key: "Backspace",
96
- visible: context => context.table !== undefined,
97
- active: context => context.format !== undefined && context.table !== undefined && context.table.getRowCount() > 1,
98
- handler: context => context.format !== undefined && context.table !== undefined ? deleteTableRowColumn(context, context.table, context.format, true) : undefined
99
- },
100
- {
101
- label: "\u232B|",
102
- description: "delete column",
103
- category: "table",
104
- control: false, alt: true, shift: true, key: "Backspace",
105
- visible: context => context.table !== undefined,
106
- active: context => context.format !== undefined && context.table !== undefined && context.table.getColumnCount() > 1,
107
- handler: context => context.format !== undefined && context.table !== undefined ? deleteTableRowColumn(context, context.table, context.format, false) : undefined
108
- },
109
- {
110
- description: "move to previous character",
111
- category: "navigation",
112
- control: false, alt: false, shift: false, key: "ArrowLeft",
113
+ icon: RowAboveIcon,
114
+ description: 'insert row above',
115
+ category: 'table',
116
+ mutates: true,
117
+ control: false,
118
+ alt: true,
119
+ shift: false,
120
+ key: 'ArrowUp',
121
+ visible: (context) => context.table !== undefined,
122
+ active: (context) => context.table !== undefined && context.format !== undefined,
123
+ handler: (context) => context.table !== undefined && context.format !== undefined
124
+ ? insertTableRowColumn(context, context.table, context.format, true, true)
125
+ : undefined,
126
+ },
127
+ {
128
+ icon: RowBelowIcon,
129
+ description: 'insert row below',
130
+ category: 'table',
131
+ mutates: true,
132
+ control: false,
133
+ alt: true,
134
+ shift: false,
135
+ key: 'ArrowDown',
136
+ visible: (context) => context.table !== undefined,
137
+ active: (context) => context.table !== undefined && context.format !== undefined,
138
+ handler: (context) => context.table !== undefined && context.format !== undefined
139
+ ? insertTableRowColumn(context, context.table, context.format, true, false)
140
+ : undefined,
141
+ },
142
+ {
143
+ icon: ColumnAfterIcon,
144
+ description: 'insert column after',
145
+ category: 'table',
146
+ mutates: true,
147
+ control: false,
148
+ alt: true,
149
+ shift: false,
150
+ key: 'ArrowRight',
151
+ visible: (context) => context.table !== undefined,
152
+ active: (context) => context.table !== undefined && context.format !== undefined,
153
+ handler: (context) => context.table !== undefined && context.format !== undefined
154
+ ? insertTableRowColumn(context, context.table, context.format, false, false)
155
+ : undefined,
156
+ },
157
+ {
158
+ icon: ColumnBeforeIcon,
159
+ description: 'insert column before',
160
+ category: 'table',
161
+ mutates: true,
162
+ control: false,
163
+ alt: true,
164
+ shift: false,
165
+ key: 'ArrowLeft',
166
+ visible: (context) => context.table !== undefined,
167
+ active: (context) => context.table !== undefined && context.format !== undefined,
168
+ handler: (context) => context.table !== undefined && context.format !== undefined
169
+ ? insertTableRowColumn(context, context.table, context.format, false, true)
170
+ : undefined,
171
+ },
172
+ {
173
+ icon: DeleteRowIcon,
174
+ description: 'delete row',
175
+ category: 'table',
176
+ mutates: true,
177
+ control: false,
178
+ alt: true,
179
+ shift: false,
180
+ key: 'Backspace',
181
+ visible: (context) => context.table !== undefined,
182
+ active: (context) => context.format !== undefined &&
183
+ context.table !== undefined &&
184
+ context.table.getRowCount() > 1,
185
+ handler: (context) => context.format !== undefined && context.table !== undefined
186
+ ? deleteTableRowColumn(context, context.table, context.format, true)
187
+ : undefined,
188
+ },
189
+ {
190
+ icon: DeleteColumnIcon,
191
+ description: 'delete column',
192
+ category: 'table',
193
+ mutates: true,
194
+ control: false,
195
+ alt: true,
196
+ shift: true,
197
+ key: 'Backspace',
198
+ visible: (context) => context.table !== undefined,
199
+ active: (context) => context.format !== undefined &&
200
+ context.table !== undefined &&
201
+ context.table.getColumnCount() > 1,
202
+ handler: (context) => context.format !== undefined && context.table !== undefined
203
+ ? deleteTableRowColumn(context, context.table, context.format, false)
204
+ : undefined,
205
+ },
206
+ {
207
+ icon: '➡️',
208
+ description: 'next paragraph',
209
+ category: 'navigation',
210
+ mutates: false,
211
+ control: false,
212
+ alt: false,
213
+ shift: false,
214
+ key: 'Tab',
215
+ visible: () => false,
216
+ active: (context) => context.format !== undefined,
217
+ handler: (context) => {
218
+ if (context.format) {
219
+ let caret = context.root
220
+ .getNodeAfter(context.format, (node) => node instanceof FormatNode)
221
+ ?.getFirstCaret();
222
+ if (caret)
223
+ return {
224
+ root: context.root,
225
+ range: { start: caret, end: caret },
226
+ };
227
+ }
228
+ },
229
+ },
230
+ {
231
+ icon: '←',
232
+ description: 'previous paragraph',
233
+ category: 'navigation',
234
+ mutates: false,
235
+ control: false,
236
+ alt: false,
237
+ shift: true,
238
+ key: 'Tab',
113
239
  visible: () => false,
114
- active: context => context.endIsTextOrAtom,
115
- handler: context => {
116
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
240
+ active: (context) => context.format !== undefined,
241
+ // If we're in a table, find the previous cell.
242
+ handler: (context) => {
243
+ if (context.format) {
244
+ let caret = context.root
245
+ .getNodeBefore(context.format, (node) => node instanceof FormatNode)
246
+ ?.getFirstCaret();
247
+ if (caret)
248
+ return {
249
+ root: context.root,
250
+ range: { start: caret, end: caret },
251
+ };
252
+ }
253
+ },
254
+ },
255
+ {
256
+ icon: '←',
257
+ description: 'move to previous character',
258
+ category: 'navigation',
259
+ mutates: false,
260
+ control: false,
261
+ alt: false,
262
+ shift: false,
263
+ key: 'ArrowLeft',
264
+ visible: () => false,
265
+ active: (context) => context.endIsTextOrAtom,
266
+ handler: (context) => {
267
+ if (context.end.node instanceof TextNode ||
268
+ context.end.node instanceof AtomNode ||
269
+ context.end.node instanceof EmbedNode) {
117
270
  const nextCaret = context.root.getAdjacentCaret(context.end, false);
118
271
  const sortedRange = context.root.sortRange(context.range);
119
- return { root: context.root, range: nextCaret === undefined ? { start: sortedRange.start, end: sortedRange.start } : { start: nextCaret, end: nextCaret } };
272
+ return {
273
+ root: context.root,
274
+ range: nextCaret === undefined
275
+ ? {
276
+ start: sortedRange.start,
277
+ end: sortedRange.start,
278
+ }
279
+ : { start: nextCaret, end: nextCaret },
280
+ };
120
281
  }
121
- }
282
+ },
122
283
  },
123
284
  {
124
- description: "move to previous word",
125
- category: "navigation",
126
- control: false, alt: true, shift: false, key: "ArrowLeft",
285
+ icon: '← word',
286
+ description: 'move to previous word',
287
+ category: 'navigation',
288
+ mutates: false,
289
+ control: false,
290
+ alt: true,
291
+ shift: false,
292
+ key: 'ArrowLeft',
127
293
  visible: false,
128
- active: context => context.endIsTextOrAtom,
129
- handler: context => {
130
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
294
+ active: (context) => context.endIsTextOrAtom,
295
+ handler: (context) => {
296
+ if (context.end.node instanceof TextNode ||
297
+ context.end.node instanceof AtomNode ||
298
+ context.end.node instanceof EmbedNode) {
131
299
  const previous = context.end.node.previousWord(context.root, context.end.index);
132
- return { root: context.root, range: { start: previous, end: previous } };
300
+ return {
301
+ root: context.root,
302
+ range: { start: previous, end: previous },
303
+ };
133
304
  }
134
- }
305
+ },
135
306
  },
136
307
  {
137
- description: "move to paragraph start",
138
- category: "navigation",
139
- control: true, alt: false, shift: false, key: "ArrowLeft",
308
+ icon: '← paragraph',
309
+ description: 'move to paragraph start',
310
+ category: 'navigation',
311
+ mutates: false,
312
+ control: true,
313
+ alt: false,
314
+ shift: false,
315
+ key: 'ArrowLeft',
140
316
  visible: false,
141
- active: context => context.format !== undefined,
142
- handler: context => {
317
+ active: (context) => context.format !== undefined,
318
+ handler: (context) => {
143
319
  if (context.format !== undefined) {
144
320
  const first = context.format.getFirstTextNode();
145
- return { root: context.root, range: { start: { node: first, index: 0 }, end: { node: first, index: 0 } } };
321
+ return {
322
+ root: context.root,
323
+ range: {
324
+ start: { node: first, index: 0 },
325
+ end: { node: first, index: 0 },
326
+ },
327
+ };
146
328
  }
147
- }
329
+ },
148
330
  },
149
331
  {
150
- description: "expand selection to previous character",
151
- category: "selection",
152
- control: false, alt: false, shift: true, key: "ArrowLeft",
332
+ icon: '← select',
333
+ description: 'expand selection to previous character',
334
+ category: 'selection',
335
+ mutates: false,
336
+ control: false,
337
+ alt: false,
338
+ shift: true,
339
+ key: 'ArrowLeft',
153
340
  visible: false,
154
- active: context => context.endIsTextOrAtom,
155
- handler: context => {
156
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
341
+ active: (context) => context.endIsTextOrAtom,
342
+ handler: (context) => {
343
+ if (context.end.node instanceof TextNode ||
344
+ context.end.node instanceof AtomNode ||
345
+ context.end.node instanceof EmbedNode) {
157
346
  const previous = context.root.getAdjacentCaret(context.end, false);
158
347
  if (previous === undefined)
159
348
  return;
160
- return { root: context.root, range: { start: context.start, end: previous } };
349
+ return {
350
+ root: context.root,
351
+ range: { start: context.start, end: previous },
352
+ };
161
353
  }
162
- }
354
+ },
163
355
  },
164
356
  {
165
- description: "expand selection to previous word",
166
- category: "selection",
167
- control: false, alt: true, shift: true, key: "ArrowLeft",
357
+ icon: 'select ← word',
358
+ description: 'expand selection to previous word',
359
+ category: 'selection',
360
+ mutates: false,
361
+ control: false,
362
+ alt: true,
363
+ shift: true,
364
+ key: 'ArrowLeft',
168
365
  visible: false,
169
- active: context => context.endIsTextOrAtom,
170
- handler: context => {
171
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
366
+ active: (context) => context.endIsTextOrAtom,
367
+ handler: (context) => {
368
+ if (context.end.node instanceof TextNode ||
369
+ context.end.node instanceof AtomNode ||
370
+ context.end.node instanceof EmbedNode) {
172
371
  const previous = context.end.node.previousWord(context.root, context.end.index);
173
- return { root: context.root, range: { start: context.start, end: previous } };
372
+ return {
373
+ root: context.root,
374
+ range: { start: context.start, end: previous },
375
+ };
174
376
  }
175
- }
377
+ },
176
378
  },
177
379
  {
178
- description: "expand selection to start",
179
- category: "selection",
180
- control: true, alt: false, shift: true, key: "ArrowLeft",
380
+ icon: 'select start',
381
+ description: 'expand selection to start',
382
+ category: 'selection',
383
+ mutates: false,
384
+ control: true,
385
+ alt: false,
386
+ shift: true,
387
+ key: 'ArrowLeft',
181
388
  visible: false,
182
- active: context => context.format !== undefined,
183
- handler: context => {
389
+ active: (context) => context.format !== undefined,
390
+ handler: (context) => {
184
391
  if (context.format !== undefined) {
185
392
  const first = context.format.getFirstTextNode();
186
- return { root: context.root, range: { start: context.start, end: { node: first, index: 0 } } };
393
+ return {
394
+ root: context.root,
395
+ range: {
396
+ start: context.start,
397
+ end: { node: first, index: 0 },
398
+ },
399
+ };
187
400
  }
188
- }
401
+ },
189
402
  },
190
403
  {
191
- description: "move to next character",
192
- category: "navigation",
193
- control: false, alt: false, shift: false, key: "ArrowRight",
404
+ icon: '→',
405
+ description: 'move to next character',
406
+ category: 'navigation',
407
+ mutates: false,
408
+ control: false,
409
+ alt: false,
410
+ shift: false,
411
+ key: 'ArrowRight',
194
412
  visible: false,
195
- active: context => context.endIsTextOrAtom,
196
- handler: context => {
197
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
413
+ active: (context) => context.endIsTextOrAtom,
414
+ handler: (context) => {
415
+ if (context.end.node instanceof TextNode ||
416
+ context.end.node instanceof AtomNode ||
417
+ context.end.node instanceof EmbedNode) {
198
418
  const nextCaret = context.root.getAdjacentCaret(context.end, true);
199
419
  const sortedRange = context.root.sortRange(context.range);
200
420
  // If there is no next caret, move the caret to the largest of the current start and end.
201
- return { root: context.root, range: nextCaret === undefined ? { start: sortedRange.end, end: sortedRange.end } : { start: nextCaret, end: nextCaret } };
421
+ return {
422
+ root: context.root,
423
+ range: nextCaret === undefined
424
+ ? { start: sortedRange.end, end: sortedRange.end }
425
+ : { start: nextCaret, end: nextCaret },
426
+ };
202
427
  }
203
- }
428
+ },
204
429
  },
205
430
  {
206
- description: "move to next word",
207
- category: "navigation",
208
- control: false, alt: true, shift: false, key: "ArrowRight",
431
+ icon: '→ word',
432
+ description: 'move to next word',
433
+ category: 'navigation',
434
+ mutates: false,
435
+ control: false,
436
+ alt: true,
437
+ shift: false,
438
+ key: 'ArrowRight',
209
439
  visible: false,
210
- active: context => context.endIsTextOrAtom,
211
- handler: context => {
212
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
440
+ active: (context) => context.endIsTextOrAtom,
441
+ handler: (context) => {
442
+ if (context.end.node instanceof TextNode ||
443
+ context.end.node instanceof AtomNode ||
444
+ context.end.node instanceof EmbedNode) {
213
445
  const next = context.end.node.nextWord(context.root, context.end.index);
214
- return { root: context.root, range: { start: next, end: next } };
446
+ return {
447
+ root: context.root,
448
+ range: { start: next, end: next },
449
+ };
215
450
  }
216
- }
451
+ },
217
452
  },
218
453
  {
219
- description: "move to paragraph end",
220
- category: "navigation",
221
- control: true, alt: false, shift: false, key: "ArrowRight",
454
+ icon: '→ end',
455
+ description: 'move to paragraph end',
456
+ category: 'navigation',
457
+ mutates: false,
458
+ control: true,
459
+ alt: false,
460
+ shift: false,
461
+ key: 'ArrowRight',
222
462
  visible: false,
223
- active: context => context.format !== undefined,
224
- handler: context => {
463
+ active: (context) => context.format !== undefined,
464
+ handler: (context) => {
225
465
  if (context.format !== undefined) {
226
466
  const last = context.format.getLastTextNode();
227
467
  const caret = { node: last, index: last.getLength() };
228
- return { root: context.root, range: { start: caret, end: caret } };
468
+ return {
469
+ root: context.root,
470
+ range: { start: caret, end: caret },
471
+ };
229
472
  }
230
- }
473
+ },
231
474
  },
232
475
  {
233
- description: "expand selection to next character",
234
- category: "selection",
235
- control: false, alt: false, shift: true, key: "ArrowRight",
476
+ icon: 'select →',
477
+ description: 'expand selection to next character',
478
+ category: 'selection',
479
+ mutates: false,
480
+ control: false,
481
+ alt: false,
482
+ shift: true,
483
+ key: 'ArrowRight',
236
484
  visible: false,
237
- active: context => context.endIsTextOrAtom,
238
- handler: context => {
239
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
485
+ active: (context) => context.endIsTextOrAtom,
486
+ handler: (context) => {
487
+ if (context.end.node instanceof TextNode ||
488
+ context.end.node instanceof AtomNode ||
489
+ context.end.node instanceof EmbedNode) {
240
490
  const next = context.root.getAdjacentCaret(context.end, true);
241
491
  if (next === undefined)
242
492
  return;
243
- return { root: context.root, range: { start: context.start, end: next } };
493
+ return {
494
+ root: context.root,
495
+ range: { start: context.start, end: next },
496
+ };
244
497
  }
245
- }
498
+ },
246
499
  },
247
500
  {
248
- description: "expand selection to next word",
249
- category: "selection",
250
- control: false, alt: true, shift: true, key: "ArrowRight",
501
+ icon: 'select → word',
502
+ description: 'expand selection to next word',
503
+ category: 'selection',
504
+ mutates: false,
505
+ control: false,
506
+ alt: true,
507
+ shift: true,
508
+ key: 'ArrowRight',
251
509
  visible: false,
252
- active: context => context.endIsTextOrAtom,
253
- handler: context => {
254
- if (context.end.node instanceof TextNode || context.end.node instanceof AtomNode) {
510
+ active: (context) => context.endIsTextOrAtom,
511
+ handler: (context) => {
512
+ if (context.end.node instanceof TextNode ||
513
+ context.end.node instanceof AtomNode ||
514
+ context.end.node instanceof EmbedNode) {
255
515
  const previous = context.end.node.nextWord(context.root, context.end.index);
256
- return { root: context.root, range: { start: context.start, end: previous } };
516
+ return {
517
+ root: context.root,
518
+ range: { start: context.start, end: previous },
519
+ };
257
520
  }
258
- }
521
+ },
259
522
  },
260
523
  {
261
- description: "expand selection to end",
262
- category: "selection",
263
- control: true, alt: false, shift: true, key: "ArrowRight",
524
+ icon: 'select → end',
525
+ description: 'expand selection to end',
526
+ category: 'selection',
527
+ mutates: false,
528
+ control: true,
529
+ alt: false,
530
+ shift: true,
531
+ key: 'ArrowRight',
264
532
  visible: false,
265
- active: context => context.format !== undefined,
266
- handler: context => {
533
+ active: (context) => context.format !== undefined,
534
+ handler: (context) => {
267
535
  if (context.format !== undefined) {
268
536
  const last = context.format.getLastTextNode();
269
- return { root: context.root, range: { start: context.start, end: { node: last, index: last.getLength() } } };
537
+ return {
538
+ root: context.root,
539
+ range: {
540
+ start: context.start,
541
+ end: { node: last, index: last.getLength() },
542
+ },
543
+ };
270
544
  }
271
- }
545
+ },
272
546
  },
273
547
  {
274
- description: "expand selection to all",
275
- category: "selection",
276
- control: true, alt: false, shift: false, key: "a",
548
+ icon: 'select all',
549
+ description: 'expand selection to all',
550
+ category: 'selection',
551
+ mutates: false,
552
+ control: true,
553
+ alt: false,
554
+ shift: false,
555
+ key: 'a',
277
556
  visible: false,
278
557
  active: () => true,
279
- handler: context => {
558
+ handler: (context) => {
280
559
  // If in a footnote, expand to the footnote.
281
560
  if (context.atom) {
282
561
  const format = context.atom.getMeta();
283
- return { root: context.root, range: { start: format.getFirstCaret(), end: format.getLastCaret() } };
562
+ return {
563
+ root: context.root,
564
+ range: {
565
+ start: format.getFirstCaret(),
566
+ end: format.getLastCaret(),
567
+ },
568
+ };
569
+ }
570
+ // If in a code node, expand to the code.
571
+ else if (context.block instanceof CodeNode &&
572
+ context.range.start.node === context.block.getCodeNode()) {
573
+ const code = context.block.getCodeNode();
574
+ return {
575
+ root: context.root,
576
+ range: {
577
+ start: { node: code, index: 0 },
578
+ end: { node: code, index: code.getLength() },
579
+ },
580
+ };
284
581
  }
285
- // If not in a paragraph, expand to all of the formats of the block.
286
- else if (context.paragraph === undefined && context.block) {
287
- const formats = context.block.getFormats();
288
- if (formats.length > 0)
289
- return { root: context.root, range: { start: formats[0].getFirstCaret(), end: formats[formats.length - 1].getLastCaret() } };
582
+ // If in a non paragraph block, expand to the format of whatever format we're in.
583
+ else if (context.paragraph === undefined &&
584
+ context.block &&
585
+ context.format) {
586
+ return {
587
+ root: context.root,
588
+ range: {
589
+ start: context.format.getFirstCaret(),
590
+ end: context.format.getLastCaret(),
591
+ },
592
+ };
290
593
  }
291
594
  else {
292
595
  // Find the first and last caret of the entire chapter.
@@ -296,64 +599,98 @@ const commands = [
296
599
  root: context.root,
297
600
  range: {
298
601
  start: { node: text[0], index: 0 },
299
- end: { node: text[text.length - 1], index: text[text.length - 1].getLength() }
300
- }
602
+ end: {
603
+ node: text[text.length - 1],
604
+ index: text[text.length - 1].getLength(),
605
+ },
606
+ },
301
607
  };
302
608
  }
303
609
  }
304
- }
610
+ },
305
611
  },
306
612
  {
307
- description: "move up one line",
308
- category: "navigation",
309
- control: false, alt: false, shift: false, key: "ArrowUp",
613
+ icon: '↑ line',
614
+ description: 'move up one line',
615
+ category: 'navigation',
616
+ mutates: false,
617
+ control: false,
618
+ alt: false,
619
+ shift: false,
620
+ key: 'ArrowUp',
310
621
  visible: false,
311
- active: context => context.startIsTextOrAtom,
622
+ active: (context) => context.startIsTextOrAtom,
312
623
  handler: (context, utilities) => {
313
624
  const above = utilities.getCaretOnLine(context.start, false);
314
625
  return { root: context.root, range: { start: above, end: above } };
315
- }
626
+ },
316
627
  },
317
628
  {
318
- description: "move selection up one line",
319
- category: "selection",
320
- control: false, alt: false, shift: true, key: "ArrowUp",
629
+ icon: 'select ↑ line',
630
+ description: 'move selection up one line',
631
+ category: 'selection',
632
+ mutates: false,
633
+ control: false,
634
+ alt: false,
635
+ shift: true,
636
+ key: 'ArrowUp',
321
637
  visible: false,
322
- active: context => context.startIsTextOrAtom,
638
+ active: (context) => context.startIsTextOrAtom,
323
639
  handler: (context, utilities) => {
324
640
  const above = utilities.getCaretOnLine(context.end, false);
325
- return { root: context.root, range: { start: context.start, end: above } };
326
- }
641
+ return {
642
+ root: context.root,
643
+ range: { start: context.start, end: above },
644
+ };
645
+ },
327
646
  },
328
647
  {
329
- description: "move up down line",
330
- category: "navigation",
331
- control: false, alt: false, shift: false, key: "ArrowDown",
648
+ icon: 'select ↓ line',
649
+ description: 'move up down line',
650
+ category: 'navigation',
651
+ mutates: false,
652
+ control: false,
653
+ alt: false,
654
+ shift: false,
655
+ key: 'ArrowDown',
332
656
  visible: false,
333
- active: context => context.startIsTextOrAtom,
657
+ active: (context) => context.startIsTextOrAtom,
334
658
  handler: (context, utilities) => {
335
659
  const below = utilities.getCaretOnLine(context.start, true);
336
660
  return { root: context.root, range: { start: below, end: below } };
337
- }
661
+ },
338
662
  },
339
663
  {
340
- description: "move selection down one line",
341
- category: "selection",
342
- control: false, alt: false, shift: true, key: "ArrowDown",
664
+ icon: 'select ↓ line',
665
+ description: 'move selection down one line',
666
+ category: 'selection',
667
+ mutates: false,
668
+ control: false,
669
+ alt: false,
670
+ shift: true,
671
+ key: 'ArrowDown',
343
672
  visible: false,
344
- active: context => context.startIsTextOrAtom,
673
+ active: (context) => context.startIsTextOrAtom,
345
674
  handler: (context, utilities) => {
346
675
  const below = utilities.getCaretOnLine(context.end, true);
347
- return { root: context.root, range: { start: context.start, end: below } };
348
- }
676
+ return {
677
+ root: context.root,
678
+ range: { start: context.start, end: below },
679
+ };
680
+ },
349
681
  },
350
682
  {
351
- description: "delete previous character",
352
- category: "text",
353
- control: false, alt: false, shift: false, key: "Backspace",
683
+ icon: 'delete ←',
684
+ description: 'delete previous character',
685
+ category: 'text',
686
+ mutates: false,
687
+ control: false,
688
+ alt: false,
689
+ shift: false,
690
+ key: 'Backspace',
354
691
  visible: false,
355
692
  active: true,
356
- handler: context => {
693
+ handler: (context) => {
357
694
  if (context.root === undefined)
358
695
  return;
359
696
  if (context.isSelection) {
@@ -368,15 +705,20 @@ const commands = [
368
705
  return;
369
706
  return rootWithNode(context, context.root, edit.root, () => edit.range.start);
370
707
  }
371
- }
708
+ },
372
709
  },
373
710
  {
374
- description: "delete next character",
375
- category: "text",
376
- control: false, alt: false, shift: false, key: "Delete",
711
+ icon: 'delete →',
712
+ description: 'delete next character',
713
+ category: 'text',
714
+ mutates: true,
715
+ control: false,
716
+ alt: false,
717
+ shift: false,
718
+ key: 'Delete',
377
719
  visible: false,
378
720
  active: true,
379
- handler: context => {
721
+ handler: (context) => {
380
722
  if (context.root === undefined)
381
723
  return;
382
724
  if (context.isSelection) {
@@ -391,36 +733,53 @@ const commands = [
391
733
  return;
392
734
  return rootWithNode(context, context.root, edit.root, () => edit.range.start);
393
735
  }
394
- }
736
+ },
395
737
  },
396
738
  {
397
- description: "insert code newline",
398
- category: "text",
399
- control: false, alt: false, shift: false, key: "Enter",
739
+ icon: 'new line',
740
+ description: 'insert code newline',
741
+ category: 'text',
742
+ mutates: true,
743
+ control: false,
744
+ alt: false,
745
+ shift: false,
746
+ key: 'Enter',
400
747
  visible: false,
401
- active: context => context.code !== undefined,
402
- handler: context => {
748
+ active: (context) => context.code !== undefined,
749
+ handler: (context) => {
403
750
  if (context.code?.getCodeNode() !== context.start.node)
404
751
  return;
405
- const newText = context.start.node.withCharacterAt("\n", context.start.index);
752
+ const newText = context.start.node.withCharacterAt('\n', context.start.index);
406
753
  if (newText === undefined)
407
754
  return;
408
- return rootWithNode(context, context.start.node, newText, text => { return { node: text, index: context.start.index + 1 }; });
409
- }
755
+ return rootWithNode(context, context.start.node, newText, (text) => {
756
+ return { node: text, index: context.start.index + 1 };
757
+ });
758
+ },
410
759
  },
411
760
  {
412
- description: "split list item",
413
- category: "list",
414
- control: false, alt: false, shift: false, key: "Enter",
761
+ icon: 'split list',
762
+ description: 'split list item',
763
+ category: 'list',
764
+ mutates: true,
765
+ control: false,
766
+ alt: false,
767
+ shift: false,
768
+ key: 'Enter',
415
769
  visible: false,
416
- active: context => context.list !== undefined,
417
- handler: context => {
770
+ active: (context) => context.list !== undefined,
771
+ handler: (context) => {
418
772
  if (context.list === undefined)
419
773
  return;
420
774
  const lastItem = context.list.getLastItem();
421
775
  const lastCaret = lastItem?.getLastCaret();
422
776
  // If we're at the last item in the list and it's empty, then add a
423
- if (lastItem !== undefined && lastCaret !== undefined && lastCaret.node instanceof TextNode && lastCaret.node.getLength() === 0 && lastCaret.node === context.end.node && lastCaret.index === context.end.index) {
777
+ if (lastItem !== undefined &&
778
+ lastCaret !== undefined &&
779
+ lastCaret.node instanceof TextNode &&
780
+ lastCaret.node.getLength() === 0 &&
781
+ lastCaret.node === context.end.node &&
782
+ lastCaret.index === context.end.index) {
424
783
  const listParent = context.list.getParent(context.root);
425
784
  if (listParent instanceof ListNode) {
426
785
  const newSublist = context.list.withoutItem(lastItem);
@@ -438,14 +797,16 @@ const commands = [
438
797
  const listWithoutItem = context.list.withoutItem(lastItem);
439
798
  if (listWithoutItem === undefined)
440
799
  return;
441
- const newBlocks = listParent.withChildReplaced(context.list, listWithoutItem)?.withBlockInsertedAfter(listWithoutItem, new ParagraphNode(0, lastItem));
800
+ const newBlocks = listParent
801
+ .withChildReplaced(context.list, listWithoutItem)
802
+ ?.withBlockInsertedAfter(listWithoutItem, new ParagraphNode(0, lastItem));
442
803
  if (newBlocks === undefined)
443
804
  return;
444
805
  return rootWithNode(context, listParent, newBlocks, () => lastCaret);
445
806
  }
446
807
  }
447
808
  else
448
- return rootWithNode(context, context.list, context.list?.withItemSplit(context.range.start), newList => {
809
+ return rootWithNode(context, context.list, context.list?.withItemSplit(context.range.start), (newList) => {
449
810
  const index = context.list?.getItemContaining(context.range.start);
450
811
  if (index === undefined)
451
812
  return;
@@ -454,374 +815,528 @@ const commands = [
454
815
  return;
455
816
  return item.getFirstCaret();
456
817
  });
457
- }
818
+ },
458
819
  },
459
820
  {
460
- description: "split paragraph",
461
- category: "paragraph",
462
- control: false, alt: false, shift: false, key: "Enter",
821
+ icon: 'split paragraph',
822
+ description: 'split paragraph',
823
+ category: 'text',
824
+ mutates: true,
825
+ control: false,
826
+ alt: false,
827
+ shift: false,
828
+ key: 'Enter',
463
829
  visible: false,
464
- active: context => context.atom === undefined && context.blocks !== undefined,
465
- handler: context => {
830
+ active: (context) => context.atom === undefined && context.blocks !== undefined,
831
+ handler: (context) => {
466
832
  const edit = context.blocks?.withSelectionSplit(context.range);
467
833
  if (edit === undefined)
468
834
  return;
469
835
  return rootWithNode(context, context.blocks, edit.root, () => edit.range.start);
470
- }
471
- },
472
- {
473
- label: "indent",
474
- icon: "indent.svg",
475
- description: "indent list item",
476
- category: "list",
477
- control: false, alt: false, shift: false, key: "Tab",
478
- visible: context => context.list !== undefined,
479
- active: context => context.list !== undefined,
480
- handler: context => rootWithNode(context, context.blocks, context.blocks?.withListsIndented(context.range, true))
481
- },
482
- {
483
- label: "unindent",
484
- icon: 'svg/unindent.svg',
485
- description: "unindent list item",
486
- category: "list",
487
- control: false, alt: false, shift: true, key: "Tab",
488
- visible: context => context.list !== undefined,
489
- active: context => context.list !== undefined && context.list.isInside(context.root, ListNode),
490
- handler: context => rootWithNode(context, context.blocks, context.blocks?.withListsIndented(context.range, false))
491
- },
492
- {
493
- label: "plain",
494
- icon: "clear.svg",
495
- description: "clear formatting",
496
- category: "text",
497
- control: true, alt: false, shift: false, key: "0",
836
+ },
837
+ },
838
+ {
839
+ icon: IndentIcon,
840
+ description: 'indent list item',
841
+ category: 'list',
842
+ mutates: true,
843
+ control: false,
844
+ alt: false,
845
+ shift: false,
846
+ key: 'Tab',
847
+ visible: (context) => context.list !== undefined,
848
+ active: (context) => context.list !== undefined,
849
+ handler: (context) => rootWithNode(context, context.blocks, context.blocks?.withListsIndented(context.range, true)),
850
+ },
851
+ {
852
+ icon: UnindentIcon,
853
+ description: 'unindent list item',
854
+ mutates: true,
855
+ category: 'list',
856
+ control: false,
857
+ alt: false,
858
+ shift: true,
859
+ key: 'Tab',
860
+ visible: (context) => context.list !== undefined,
861
+ active: (context) => context.list !== undefined &&
862
+ context.list.isInside(context.root, ListNode),
863
+ handler: (context) => rootWithNode(context, context.blocks, context.blocks?.withListsIndented(context.range, false)),
864
+ },
865
+ {
866
+ icon: ClearIcon,
867
+ description: 'clear formatting',
868
+ mutates: true,
869
+ category: 'text',
870
+ control: true,
871
+ alt: false,
872
+ shift: false,
873
+ key: '0',
498
874
  visible: true,
499
- active: context => context.startIsText || context.endIsText,
500
- handler: context => context.root.withRangeFormatted(context.range, "")
875
+ active: (context) => context.startIsText || context.endIsText,
876
+ handler: (context) => context.root.withRangeFormatted(context.range, ''),
501
877
  },
502
878
  {
503
- label: "bold",
504
- icon: "bold.svg",
505
- description: "bold",
506
- category: "text",
507
- control: true, alt: false, shift: false, key: "b",
879
+ icon: BoldIcon,
880
+ description: 'bold',
881
+ category: 'text',
882
+ mutates: true,
883
+ control: true,
884
+ alt: false,
885
+ shift: false,
886
+ key: 'b',
508
887
  visible: true,
509
- active: context => context.startIsText || context.endIsText,
510
- handler: context => context.root.withRangeFormatted(context.range, "*")
888
+ active: (context) => context.startIsText || context.endIsText,
889
+ handler: (context) => context.root.withRangeFormatted(context.range, '*'),
511
890
  },
512
891
  {
513
- description: "italic",
514
- icon: "italic.svg",
515
- category: "text",
516
- control: true, alt: false, shift: false, key: "i",
892
+ description: 'italic',
893
+ icon: ItalicIcon,
894
+ category: 'text',
895
+ mutates: true,
896
+ control: true,
897
+ alt: false,
898
+ shift: false,
899
+ key: 'i',
517
900
  visible: true,
518
- active: context => context.startIsText || context.endIsText,
519
- handler: context => context.root.withRangeFormatted(context.range, "_")
901
+ active: (context) => context.startIsText || context.endIsText,
902
+ handler: (context) => context.root.withRangeFormatted(context.range, '_'),
520
903
  },
521
904
  {
522
- label: "sub\u2099",
523
- icon: "sub.svg",
524
- description: "subscript",
525
- category: "text",
526
- control: true, alt: false, shift: false, key: ",",
905
+ icon: SubscriptIcon,
906
+ description: 'subscript',
907
+ category: 'text',
908
+ mutates: true,
909
+ control: true,
910
+ alt: false,
911
+ shift: false,
912
+ key: ',',
527
913
  visible: true,
528
- active: context => context.startIsText || context.endIsText,
529
- handler: context => context.root.withRangeFormatted(context.range, "v")
914
+ active: (context) => context.startIsText || context.endIsText,
915
+ handler: (context) => context.root.withRangeFormatted(context.range, 'v'),
530
916
  },
531
917
  {
532
- label: "super\u207F",
533
- icon: "super.svg",
534
- description: "superscript",
535
- category: "text",
536
- control: true, alt: false, shift: false, key: ".",
918
+ icon: SuperscriptIcon,
919
+ description: 'superscript',
920
+ category: 'text',
921
+ mutates: true,
922
+ control: true,
923
+ alt: false,
924
+ shift: false,
925
+ key: '.',
537
926
  visible: true,
538
- active: context => context.startIsText || context.endIsText,
539
- handler: context => context.root.withRangeFormatted(context.range, "^")
927
+ active: (context) => context.startIsText || context.endIsText,
928
+ handler: (context) => context.root.withRangeFormatted(context.range, '^'),
540
929
  },
541
930
  {
542
- label: "<code>",
543
- icon: "code.svg",
544
- description: "toggle code",
545
- category: "annotation",
546
- control: true, alt: false, shift: false, key: "j",
931
+ icon: CodeIcon,
932
+ description: 'toggle code',
933
+ category: 'annotation',
934
+ mutates: true,
935
+ control: true,
936
+ alt: false,
937
+ shift: false,
938
+ key: 'e',
547
939
  visible: true,
548
- active: context => context.startIsText || context.endIsText,
549
- handler: context => context.meta instanceof InlineCodeNode ?
550
- rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText())) :
551
- context.root.withSegmentAtSelection(context.range, text => new InlineCodeNode(new TextNode(text)))
940
+ active: (context) => context.startIsText || context.endIsText,
941
+ handler: (context) => context.meta instanceof InlineCodeNode
942
+ ? rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText()))
943
+ : context.root.withSegmentAtSelection(context.range, (text) => new InlineCodeNode(new TextNode(text))),
552
944
  },
553
945
  {
554
- label: "link ⚭",
555
- icon: "link.svg",
556
- description: "toggle link",
557
- category: "annotation",
558
- control: true, alt: false, shift: false, key: "k",
946
+ icon: LinkIcon,
947
+ description: 'toggle link',
948
+ category: 'annotation',
949
+ mutates: true,
950
+ control: true,
951
+ alt: false,
952
+ shift: false,
953
+ key: 'k',
559
954
  visible: true,
560
- active: context => (context.atom === undefined && context.meta === undefined) || context.meta instanceof LinkNode,
561
- handler: context => context.meta instanceof LinkNode ?
562
- rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText())) :
563
- context.root.withSegmentAtSelection(context.range, text => new LinkNode(new TextNode(text)))
564
- },
565
- {
566
- label: "Aa",
567
- description: "toggle definition",
568
- category: "annotation",
569
- control: true, alt: false, shift: false, key: "d",
570
- visible: context => context.chapter && context.atom === undefined && context.meta === undefined,
571
- active: context => context.chapter && context.atom === undefined && context.meta === undefined,
572
- handler: context => context.meta instanceof DefinitionNode ?
573
- rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText())) :
574
- context.root.withSegmentAtSelection(context.range, text => new DefinitionNode(new TextNode(text)))
575
- },
576
- {
577
- label: "a\u1D43",
578
- description: "insert footnote",
579
- category: "annotation",
580
- control: true, alt: false, shift: false, key: "f",
581
- visible: context => context.chapter && context.atom === undefined && context.meta === undefined,
582
- active: context => context.chapter && context.atom === undefined && context.meta === undefined,
583
- handler: context => context.root.withSegmentAtSelection(context.range, text => new FootnoteNode(new FormatNode("", [new TextNode(text)])))
584
- },
585
- {
586
- label: "a\u00b9",
587
- description: "insert citations",
588
- category: "annotation",
589
- control: true, alt: false, shift: false, key: "t",
590
- visible: context => context.chapter && context.atom === undefined && context.meta === undefined,
591
- active: context => context.chapter && context.atom === undefined && context.meta === undefined,
592
- handler: context => context.root.withSegmentAtSelection(context.range, () => new CitationsNode([]))
593
- },
594
- {
595
- label: "•",
596
- description: "insert label",
597
- category: "annotation",
598
- control: true, alt: false, shift: false, key: "l",
599
- visible: context => context.chapter && context.atom === undefined && context.meta === undefined,
600
- active: context => context.chapter && context.atom === undefined && context.meta === undefined,
601
- handler: context => context.root.withSegmentAtSelection(context.range, () => new LabelNode(""))
602
- },
603
- {
604
- label: "comment",
605
- icon: "comment.svg",
606
- description: "insert comment",
607
- category: "annotation",
608
- control: true, alt: false, shift: false, key: "'",
609
- visible: context => context.chapter && context.atom === undefined && context.meta === undefined,
610
- active: context => context.chapter && context.atom === undefined && context.meta === undefined,
611
- handler: context => context.root.withSegmentAtSelection(context.range, text => new CommentNode(new FormatNode("", [new TextNode(text)])))
612
- },
613
- {
614
- label: "paragraph",
615
- icon: "paragraph.svg",
616
- description: "format as paragraph",
617
- category: "level",
618
- control: true, alt: true, shift: false, code: "Digit0",
619
- visible: context => context.paragraph !== undefined && context.paragraph.getLevel() !== 0,
620
- active: context => context.paragraph !== undefined && context.paragraph.getLevel() !== 0,
621
- handler: context => {
955
+ active: (context) => (context.atom === undefined && context.meta === undefined) ||
956
+ context.meta instanceof LinkNode,
957
+ handler: (context) => context.meta instanceof LinkNode
958
+ ? rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText()))
959
+ : context.root.withSegmentAtSelection(context.range, (text) => new LinkNode(new TextNode(text))),
960
+ },
961
+ {
962
+ icon: DefineIcon,
963
+ description: 'toggle definition',
964
+ category: 'annotation',
965
+ mutates: true,
966
+ control: true,
967
+ alt: false,
968
+ shift: false,
969
+ key: 'd',
970
+ visible: (context) => context.chapter &&
971
+ context.atom === undefined &&
972
+ context.meta === undefined,
973
+ active: (context) => context.chapter &&
974
+ context.atom === undefined &&
975
+ context.meta === undefined,
976
+ handler: (context) => context.meta instanceof DefinitionNode
977
+ ? rootWithNode(context, context.format, context.format?.withSegmentReplaced(context.meta, context.meta.getText()))
978
+ : context.root.withSegmentAtSelection(context.range, (text) => new DefinitionNode(new TextNode(text))),
979
+ },
980
+ {
981
+ icon: FootnoteIcon,
982
+ description: 'insert footnote',
983
+ category: 'annotation',
984
+ mutates: true,
985
+ control: true,
986
+ alt: false,
987
+ shift: false,
988
+ key: 'f',
989
+ visible: (context) => context.chapter &&
990
+ context.atom === undefined &&
991
+ context.meta === undefined,
992
+ active: (context) => context.chapter &&
993
+ context.atom === undefined &&
994
+ context.meta === undefined,
995
+ handler: (context) => context.root.withSegmentAtSelection(context.range, (text) => new FootnoteNode(new FormatNode('', [new TextNode(text)]))),
996
+ },
997
+ {
998
+ icon: CitationIcon,
999
+ description: 'insert citations',
1000
+ category: 'annotation',
1001
+ mutates: true,
1002
+ control: true,
1003
+ alt: false,
1004
+ shift: false,
1005
+ key: 'o',
1006
+ visible: (context) => context.chapter &&
1007
+ context.atom === undefined &&
1008
+ context.meta === undefined,
1009
+ active: (context) => context.chapter &&
1010
+ context.atom === undefined &&
1011
+ context.meta === undefined,
1012
+ handler: (context) => context.root.withSegmentAtSelection(context.range, () => new CitationsNode([])),
1013
+ },
1014
+ {
1015
+ icon: LabelIcon,
1016
+ description: 'insert label',
1017
+ category: 'annotation',
1018
+ mutates: true,
1019
+ control: true,
1020
+ alt: false,
1021
+ shift: false,
1022
+ key: 'l',
1023
+ visible: (context) => context.chapter &&
1024
+ context.atom === undefined &&
1025
+ context.meta === undefined,
1026
+ active: (context) => context.chapter &&
1027
+ context.atom === undefined &&
1028
+ context.meta === undefined,
1029
+ handler: (context) => context.root.withSegmentAtSelection(context.range, () => new LabelNode('')),
1030
+ },
1031
+ {
1032
+ icon: CommentIcon,
1033
+ description: 'insert comment',
1034
+ category: 'annotation',
1035
+ mutates: true,
1036
+ control: true,
1037
+ alt: false,
1038
+ shift: false,
1039
+ key: "'",
1040
+ visible: (context) => context.chapter &&
1041
+ context.atom === undefined &&
1042
+ context.meta === undefined,
1043
+ active: (context) => context.chapter &&
1044
+ context.atom === undefined &&
1045
+ context.meta === undefined,
1046
+ handler: (context) => context.root.withSegmentAtSelection(context.range, (text) => new CommentNode(new FormatNode('', [new TextNode(text)]))),
1047
+ },
1048
+ {
1049
+ icon: ParagraphIcon,
1050
+ description: 'format as paragraph',
1051
+ category: 'level',
1052
+ mutates: true,
1053
+ control: true,
1054
+ alt: true,
1055
+ shift: false,
1056
+ code: 'Digit0',
1057
+ visible: (context) => context.paragraph !== undefined &&
1058
+ context.paragraph.getLevel() !== 0,
1059
+ active: (context) => context.paragraph !== undefined &&
1060
+ context.paragraph.getLevel() !== 0,
1061
+ handler: (context) => {
622
1062
  if (context.paragraph === undefined)
623
1063
  return;
624
1064
  return rootWithNode(context, context.paragraph, context.paragraph.withLevel(0));
625
- }
626
- },
627
- {
628
- label: "h1",
629
- description: "format as 1st level header",
630
- category: "level",
631
- control: true, alt: true, shift: false, code: "Digit1",
632
- visible: context => context.paragraph !== undefined,
633
- active: context => context.paragraph !== undefined && context.paragraph.getLevel() !== 1,
634
- handler: context => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(1))
635
- },
636
- {
637
- label: "h2",
638
- description: "format as 2nd level header",
639
- category: "level",
640
- control: true, alt: true, shift: false, code: "Digit2",
641
- visible: context => context.paragraph !== undefined,
642
- active: context => context.paragraph !== undefined && context.paragraph.getLevel() !== 2,
643
- handler: context => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(2))
644
- },
645
- {
646
- label: "h3",
647
- description: "format as 3rd level header",
648
- category: "level",
649
- control: true, alt: true, shift: false, code: "Digit3",
650
- visible: context => context.paragraph !== undefined,
651
- active: context => context.paragraph !== undefined && context.paragraph.getLevel() !== 3,
652
- handler: context => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(3))
653
- },
654
- {
655
- label: "\u2014",
656
- description: "insert horizontal rule",
657
- category: "block",
658
- control: true, alt: false, shift: true, key: "h",
659
- visible: context => context.paragraph !== undefined,
660
- active: context => context.paragraph !== undefined && context.atParagraphStart,
661
- handler: context => context.paragraph ? rootWithNode(context, context.blocks, context.blocks?.withBlockInsertedBefore(context.paragraph, new RuleNode())) : undefined
662
- },
663
- {
664
- label: "🄰",
665
- description: "insert callout",
666
- category: "block",
667
- control: true, alt: false, shift: true, key: "e",
668
- visible: context => context.paragraph !== undefined,
669
- active: context => context.paragraph !== undefined && context.atParagraphStart,
670
- handler: context => {
1065
+ },
1066
+ },
1067
+ {
1068
+ icon: Header1Icon,
1069
+ description: 'format as 1st level header',
1070
+ category: 'level',
1071
+ mutates: true,
1072
+ control: true,
1073
+ alt: true,
1074
+ shift: false,
1075
+ code: 'Digit1',
1076
+ visible: (context) => context.paragraph !== undefined,
1077
+ active: (context) => context.paragraph !== undefined &&
1078
+ context.paragraph.getLevel() !== 1,
1079
+ handler: (context) => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(1)),
1080
+ },
1081
+ {
1082
+ icon: Header2Icon,
1083
+ description: 'format as 2nd level header',
1084
+ category: 'level',
1085
+ mutates: true,
1086
+ control: true,
1087
+ alt: true,
1088
+ shift: false,
1089
+ code: 'Digit2',
1090
+ visible: (context) => context.paragraph !== undefined,
1091
+ active: (context) => context.paragraph !== undefined &&
1092
+ context.paragraph.getLevel() !== 2,
1093
+ handler: (context) => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(2)),
1094
+ },
1095
+ {
1096
+ icon: Header3Icon,
1097
+ description: 'format as 3rd level header',
1098
+ category: 'level',
1099
+ mutates: true,
1100
+ control: true,
1101
+ alt: true,
1102
+ shift: false,
1103
+ code: 'Digit3',
1104
+ visible: (context) => context.paragraph !== undefined,
1105
+ active: (context) => context.paragraph !== undefined &&
1106
+ context.paragraph.getLevel() !== 3,
1107
+ handler: (context) => rootWithNode(context, context.paragraph, context.paragraph?.withLevel(3)),
1108
+ },
1109
+ {
1110
+ icon: RuleIcon,
1111
+ description: 'insert horizontal rule',
1112
+ category: 'block',
1113
+ mutates: true,
1114
+ control: true,
1115
+ alt: false,
1116
+ shift: false,
1117
+ key: 'u',
1118
+ visible: (context) => context.paragraph !== undefined,
1119
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1120
+ handler: (context) => context.paragraph
1121
+ ? rootWithNode(context, context.blocks, context.blocks?.withBlockInsertedBefore(context.paragraph, new RuleNode()))
1122
+ : undefined,
1123
+ },
1124
+ {
1125
+ icon: CalloutIcon,
1126
+ description: 'insert callout',
1127
+ category: 'block',
1128
+ mutates: true,
1129
+ control: true,
1130
+ alt: false,
1131
+ shift: true,
1132
+ key: '-',
1133
+ visible: (context) => context.paragraph !== undefined,
1134
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1135
+ handler: (context) => {
671
1136
  // If the caret is in a paragraph in a blocks node, insert a callout before the current paragraph.
672
1137
  if (context.blocks && context.paragraph) {
673
1138
  // Make a new callout node with an empty paragraph, insert it before the paragraph the caret is in, and place the caret inside the empty paragraph.
674
1139
  const newParagraph = new ParagraphNode();
675
1140
  return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, new CalloutNode([newParagraph])), () => newParagraph.getFirstCaret());
676
1141
  }
677
- }
1142
+ },
678
1143
  },
679
1144
  {
680
- label: "‟",
681
- icon: "quote.svg",
682
- description: "insert quote",
683
- category: "block",
684
- control: true, alt: false, shift: true, key: "u",
685
- visible: context => context.paragraph !== undefined,
686
- active: context => context.paragraph !== undefined && context.atParagraphStart,
687
- handler: context => {
1145
+ icon: QuoteIcon,
1146
+ description: 'insert quote',
1147
+ category: 'block',
1148
+ mutates: true,
1149
+ control: true,
1150
+ alt: false,
1151
+ shift: true,
1152
+ key: "'",
1153
+ visible: (context) => context.paragraph !== undefined,
1154
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1155
+ handler: (context) => {
688
1156
  if (context.blocks && context.paragraph) {
689
1157
  // Make a new quote and place the caret inside the quote's first empty paragraph.
690
1158
  const newParagraph = new ParagraphNode();
691
1159
  return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, new QuoteNode([newParagraph])), () => newParagraph.getFirstCaret());
692
1160
  }
693
- }
1161
+ },
694
1162
  },
695
1163
  {
696
- label: "<code>",
697
- icon: "code.svg",
698
- description: "insert code",
699
- category: "block",
700
- control: true, alt: false, shift: true, key: "s",
701
- visible: context => context.paragraph !== undefined,
702
- active: context => context.paragraph !== undefined && context.atParagraphStart,
703
- handler: context => {
1164
+ icon: CodeIcon,
1165
+ description: 'insert code',
1166
+ category: 'block',
1167
+ mutates: true,
1168
+ control: true,
1169
+ alt: false,
1170
+ shift: true,
1171
+ key: 's',
1172
+ visible: (context) => context.paragraph !== undefined,
1173
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1174
+ handler: (context) => {
704
1175
  if (context.blocks && context.paragraph) {
705
- const newCode = new CodeNode(new TextNode(), "plaintext", "|");
706
- return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, newCode), () => { return { node: newCode.getCodeNode(), index: 0 }; });
1176
+ const newCode = new CodeNode(new TextNode(), 'plaintext', '|');
1177
+ return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, newCode), () => {
1178
+ return { node: newCode.getCodeNode(), index: 0 };
1179
+ });
707
1180
  }
708
- }
1181
+ },
709
1182
  },
710
1183
  {
711
- label: "image/video",
712
- icon: "media.svg",
713
- description: "insert image or video",
714
- category: "block",
715
- control: true, alt: false, shift: true, key: "p",
716
- visible: context => context.paragraph !== undefined,
717
- active: context => context.paragraph !== undefined && context.atParagraphStart,
718
- handler: context => {
1184
+ icon: MediaIcon,
1185
+ description: 'insert image or video',
1186
+ category: 'block',
1187
+ mutates: true,
1188
+ control: true,
1189
+ alt: false,
1190
+ shift: true,
1191
+ key: 'i',
1192
+ visible: (context) => context.paragraph !== undefined,
1193
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1194
+ handler: (context) => {
719
1195
  if (context.blocks && context.paragraph) {
720
1196
  // Place the caret inside the code's code node.
721
- const newEmbed = new EmbedNode("", "");
1197
+ const newEmbed = new EmbedNode('', '');
722
1198
  return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, newEmbed), () => newEmbed.getCaption().getFirstCaret());
723
1199
  }
724
- }
1200
+ },
725
1201
  },
726
1202
  {
727
- label: "▦",
728
- description: "insert table",
729
- category: "block",
730
- control: true, alt: false, shift: true, key: "\\",
731
- visible: context => context.paragraph !== undefined,
732
- active: context => context.paragraph !== undefined && context.atParagraphStart,
733
- handler: context => {
1203
+ icon: TableIcon,
1204
+ description: 'insert table',
1205
+ category: 'block',
1206
+ mutates: true,
1207
+ control: true,
1208
+ alt: false,
1209
+ shift: false,
1210
+ key: '[',
1211
+ visible: (context) => context.paragraph !== undefined,
1212
+ active: (context) => context.paragraph !== undefined && context.atParagraphStart,
1213
+ handler: (context) => {
734
1214
  if (context.blocks && context.paragraph) {
735
1215
  // Create a 3x3 of table rows and columns
736
1216
  // Return a caret corresponding to the first cell.
737
1217
  const newRows = [[], [], []];
738
1218
  for (let r = 0; r < Math.max(1, 3); r++) {
739
1219
  for (let c = 0; c < Math.max(1, 3); c++)
740
- newRows[r].push(new FormatNode("", [new TextNode()]));
1220
+ newRows[r].push(new FormatNode('', [new TextNode()]));
741
1221
  }
742
- const newTable = new TableNode(newRows, "|", new FormatNode("", [new TextNode()]));
743
- return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, newTable), () => { return { node: newTable.getRows()[0][0].getTextNodes()[0], index: 0 }; });
1222
+ const newTable = new TableNode(newRows, '|', new FormatNode('', [new TextNode()]));
1223
+ return rootWithNode(context, context.blocks, context.blocks.withBlockInsertedBefore(context.paragraph, newTable), () => {
1224
+ return {
1225
+ node: newTable.getRows()[0][0].getTextNodes()[0],
1226
+ index: 0,
1227
+ };
1228
+ });
744
1229
  }
745
- }
746
- },
747
- {
748
- label: "bulleted",
749
- icon: "bullets.svg",
750
- description: "convert paragraph to bulleted list item",
751
- category: "list",
752
- control: true, alt: false, shift: true, key: "7",
753
- visible: context => context.paragraph !== undefined && context.list === undefined,
754
- active: context => context.paragraph !== undefined && context.list === undefined,
755
- handler: context => rootWithNode(context, context.blocks, context.blocks?.withParagraphsAsLists(context.range, false))
756
- },
757
- {
758
- label: "numbered",
759
- icon: "numbers.svg",
760
- description: "convert paragraph to numbered list item",
761
- category: "list",
762
- control: true, alt: false, shift: true, key: "8",
763
- visible: context => context.paragraph !== undefined && context.list === undefined,
764
- active: context => context.paragraph !== undefined && context.list === undefined,
765
- handler: context => rootWithNode(context, context.blocks, context.blocks?.withParagraphsAsLists(context.range, true))
766
- },
767
- {
768
- label: "bulleted",
769
- icon: "bullets.svg",
770
- description: "convert numbered list item to bulleted",
771
- category: "list",
772
- control: true, alt: false, shift: true, key: "7",
773
- visible: context => context.list !== undefined && context.list.isNumbered(),
774
- active: context => context.list !== undefined && context.list.isNumbered(),
775
- handler: context => context.list ? rootWithNode(context, context.blocks, context.blocks?.withListAsStyle(context.list, false)) : undefined
776
- },
777
- {
778
- label: "numbered",
779
- icon: "numbers.svg",
780
- description: "convert bulleted list item to numbered",
781
- category: "list",
782
- control: true, alt: false, shift: true, key: "8",
783
- visible: context => context.list !== undefined && !context.list.isNumbered(),
784
- active: context => context.list !== undefined && !context.list.isNumbered(),
785
- handler: context => context.list ? rootWithNode(context, context.blocks, context.blocks?.withListAsStyle(context.list, true)) : undefined
786
- },
787
- {
788
- label: "paragraph",
789
- icon: "paragraph.svg",
790
- description: "convert bulleted list item to paragraph",
791
- category: "list",
792
- control: true, alt: false, shift: true, key: ["7", "8"],
793
- visible: context => context.list !== undefined,
794
- active: context => context.list !== undefined,
795
- handler: context => rootWithNode(context, context.blocks, context.blocks?.withListsAsParagraphs(context.range))
796
- },
797
- {
798
- label: "undo",
799
- icon: "undo.svg",
800
- description: "undo the last command",
801
- category: "history",
802
- control: true, alt: false, shift: false, key: ["z"],
1230
+ },
1231
+ },
1232
+ {
1233
+ icon: BulletsIcon,
1234
+ description: 'convert paragraph to bulleted list item',
1235
+ category: 'list',
1236
+ mutates: true,
1237
+ control: true,
1238
+ alt: false,
1239
+ shift: true,
1240
+ key: '7',
1241
+ visible: (context) => context.paragraph !== undefined && context.list === undefined,
1242
+ active: (context) => context.paragraph !== undefined && context.list === undefined,
1243
+ handler: (context) => rootWithNode(context, context.blocks, context.blocks?.withParagraphsAsLists(context.range, false)),
1244
+ },
1245
+ {
1246
+ icon: NumbersIcon,
1247
+ description: 'convert paragraph to numbered list item',
1248
+ category: 'list',
1249
+ mutates: true,
1250
+ control: true,
1251
+ alt: false,
1252
+ shift: true,
1253
+ key: '8',
1254
+ visible: (context) => context.paragraph !== undefined && context.list === undefined,
1255
+ active: (context) => context.paragraph !== undefined && context.list === undefined,
1256
+ handler: (context) => rootWithNode(context, context.blocks, context.blocks?.withParagraphsAsLists(context.range, true)),
1257
+ },
1258
+ {
1259
+ icon: BulletsIcon,
1260
+ description: 'convert numbered list item to bulleted',
1261
+ category: 'list',
1262
+ mutates: true,
1263
+ control: true,
1264
+ alt: false,
1265
+ shift: true,
1266
+ key: '7',
1267
+ visible: (context) => context.list !== undefined && context.list.isNumbered(),
1268
+ active: (context) => context.list !== undefined && context.list.isNumbered(),
1269
+ handler: (context) => context.list
1270
+ ? rootWithNode(context, context.blocks, context.blocks?.withListAsStyle(context.list, false))
1271
+ : undefined,
1272
+ },
1273
+ {
1274
+ icon: NumbersIcon,
1275
+ description: 'convert bulleted list item to numbered',
1276
+ category: 'list',
1277
+ mutates: true,
1278
+ control: true,
1279
+ alt: false,
1280
+ shift: true,
1281
+ key: '8',
1282
+ visible: (context) => context.list !== undefined && !context.list.isNumbered(),
1283
+ active: (context) => context.list !== undefined && !context.list.isNumbered(),
1284
+ handler: (context) => context.list
1285
+ ? rootWithNode(context, context.blocks, context.blocks?.withListAsStyle(context.list, true))
1286
+ : undefined,
1287
+ },
1288
+ {
1289
+ icon: ParagraphIcon,
1290
+ description: 'convert bulleted list item to paragraph',
1291
+ category: 'list',
1292
+ mutates: true,
1293
+ control: true,
1294
+ alt: false,
1295
+ shift: true,
1296
+ key: ['7', '8'],
1297
+ visible: (context) => context.list !== undefined,
1298
+ active: (context) => context.list !== undefined,
1299
+ handler: (context) => rootWithNode(context, context.blocks, context.blocks?.withListsAsParagraphs(context.range)),
1300
+ },
1301
+ {
1302
+ icon: UndoIcon,
1303
+ description: 'undo the last command',
1304
+ category: 'history',
1305
+ mutates: false,
1306
+ control: true,
1307
+ alt: false,
1308
+ shift: false,
1309
+ key: 'z',
803
1310
  visible: true,
804
- active: context => context.undoStack.length > 0 && context.undoPosition < context.undoStack.length - 1,
805
- handler: context => context.undo()
1311
+ active: (context) => context.undoStack.length > 0 &&
1312
+ context.undoPosition < context.undoStack.length - 1,
1313
+ handler: (context) => context.undo(),
806
1314
  },
807
1315
  {
808
- label: "redo",
809
- icon: "redo.svg",
810
- description: "redo the most recently undone command",
811
- category: "history",
812
- control: true, alt: false, shift: true, key: ["z"],
1316
+ icon: RedoIcon,
1317
+ description: 'redo the most recently undone command',
1318
+ category: 'history',
1319
+ mutates: false,
1320
+ control: true,
1321
+ alt: false,
1322
+ shift: false,
1323
+ key: 'y',
813
1324
  visible: true,
814
- active: context => context.undoPosition > 0,
815
- handler: context => context.redo()
1325
+ active: (context) => context.undoPosition > 0,
1326
+ handler: (context) => context.redo(),
816
1327
  },
817
1328
  {
818
- label: "✂",
819
- description: "delete the selected content and copy it to the clipboard",
820
- category: "clipboard",
821
- control: true, alt: false, shift: false, key: ["x"],
1329
+ icon: CutIcon,
1330
+ description: 'delete the selected content and copy it to the clipboard',
1331
+ category: 'clipboard',
1332
+ mutates: true,
1333
+ control: true,
1334
+ alt: false,
1335
+ shift: false,
1336
+ key: 'x',
822
1337
  visible: true,
823
- active: context => context.root !== undefined && context.isSelection,
824
- handler: context => {
1338
+ active: (context) => context.root !== undefined && context.isSelection,
1339
+ handler: (context) => {
825
1340
  if (context.root === undefined)
826
1341
  return;
827
1342
  // Save the copied content to the clipboard
@@ -831,16 +1346,20 @@ const commands = [
831
1346
  return;
832
1347
  context.handleCopy(copy);
833
1348
  return rootWithNode(context, context.root, edit.root, () => edit.range.start);
834
- }
1349
+ },
835
1350
  },
836
1351
  {
837
- label: "🗎",
838
- description: "copy the selected content to the clipboard",
839
- category: "clipboard",
840
- control: true, alt: false, shift: false, key: ["c"],
1352
+ icon: CopyIcon,
1353
+ description: 'copy the selected content to the clipboard',
1354
+ category: 'clipboard',
1355
+ mutates: false,
1356
+ control: true,
1357
+ alt: false,
1358
+ shift: false,
1359
+ key: 'c',
841
1360
  visible: true,
842
- active: context => context.isSelection,
843
- handler: context => {
1361
+ active: (context) => context.isSelection,
1362
+ handler: (context) => {
844
1363
  if (context.root === undefined)
845
1364
  return;
846
1365
  // Save the copied content to the clipboard
@@ -848,52 +1367,70 @@ const commands = [
848
1367
  if (copy !== undefined)
849
1368
  context.handleCopy(copy);
850
1369
  return undefined;
851
- }
1370
+ },
852
1371
  },
853
1372
  {
854
- label: "⎘",
855
- description: "paste the content from the clipboard",
856
- category: "clipboard",
857
- control: true, alt: false, shift: false, key: ["v"],
1373
+ icon: PasteIcon,
1374
+ description: 'paste the content from the clipboard',
1375
+ category: 'clipboard',
1376
+ mutates: false,
1377
+ control: true,
1378
+ alt: false,
1379
+ shift: false,
1380
+ key: 'v',
858
1381
  visible: true,
859
1382
  active: () => true,
860
- handler: context => {
1383
+ handler: (context) => {
861
1384
  if (context.root === undefined)
862
1385
  return undefined;
863
1386
  // See if there's something on the clipboard.
864
1387
  if (navigator.clipboard) {
865
- navigator.clipboard.read()
866
- .then((items) => {
1388
+ navigator.clipboard.read().then((items) => {
867
1389
  // If there's nothing, paste whatever is on the editor clipboard, if there is something.
868
1390
  if (items.length === 0 && context.clipboard !== undefined)
869
1391
  context.handlePaste(context, context.clipboard, true);
870
1392
  else {
871
- for (const item of items) {
872
- for (const type of item.types) {
873
- if (type === "text/plain")
874
- item.getType(type)
875
- .then((blob) => {
876
- blob.text().then(text => {
877
- context.handlePaste(context, new TextNode(text), true);
878
- });
879
- });
1393
+ // See if there's data we can handle. Just look at the first.
1394
+ const item = items[0];
1395
+ // If there's something, try pasting that.
1396
+ if (item) {
1397
+ async function gatherClipboard() {
1398
+ // Eventually we should add HTML support.
1399
+ const contents = {
1400
+ plain: undefined,
1401
+ };
1402
+ try {
1403
+ {
1404
+ const blob = await item.getType('text/plain');
1405
+ const text = await blob.text();
1406
+ contents.plain = text;
1407
+ }
1408
+ context.handlePaste(context, contents, true);
1409
+ }
1410
+ catch (err) { }
880
1411
  }
1412
+ gatherClipboard();
881
1413
  }
1414
+ // Otherwise, try pasting what's on the app clipboard.
1415
+ else if (context.clipboard !== undefined)
1416
+ return context.handlePaste(context, context.clipboard, false);
882
1417
  }
883
1418
  });
884
1419
  }
885
- // If there is no OS clipboard access, but there is something in the editor clipboard, copy it.
886
- else if (context.clipboard !== undefined) {
887
- return context.handlePaste(context, context.clipboard, false);
888
- }
889
- return undefined;
890
- }
1420
+ // Return the current caret to avoid unhandled command feedback.
1421
+ return { root: context.root, range: context.range };
1422
+ },
891
1423
  },
892
1424
  {
893
- label: "insert",
894
- description: "insert character",
895
- category: "text",
896
- control: false, alt: false, shift: undefined, key: undefined,
1425
+ icon: 'insert symbol',
1426
+ description: 'insert character',
1427
+ category: 'text',
1428
+ press: true,
1429
+ mutates: true,
1430
+ control: undefined,
1431
+ alt: undefined,
1432
+ shift: undefined,
1433
+ key: (key) => key.length === 1,
897
1434
  visible: false,
898
1435
  active: (context, key) => context && key !== undefined && key.length === 1,
899
1436
  handler: (context, utilities, key) => {
@@ -910,7 +1447,8 @@ const commands = [
910
1447
  // If there's a selection, remove it before inserting, and insert at the caret returned.
911
1448
  if (context.isSelection) {
912
1449
  if (context.code) {
913
- if (context.code.getCodeNode() === sortedRange.start.node) {
1450
+ if (context.code.getCodeNode() ===
1451
+ sortedRange.start.node) {
914
1452
  const newText = insertionPoint.node.withoutRange(sortedRange);
915
1453
  if (newText === undefined)
916
1454
  return;
@@ -918,7 +1456,10 @@ const commands = [
918
1456
  if (edit === undefined)
919
1457
  return;
920
1458
  newRoot = edit;
921
- insertionPoint = { node: newText, index: sortedRange.start.index };
1459
+ insertionPoint = {
1460
+ node: newText,
1461
+ index: sortedRange.start.index,
1462
+ };
922
1463
  }
923
1464
  }
924
1465
  else {
@@ -943,9 +1484,11 @@ const commands = [
943
1484
  if (newRoot === undefined)
944
1485
  return;
945
1486
  // Update the chapter
946
- return rootWithNode(context, context.root, newRoot, () => { return { node: newText, index: insertionPoint.index + 1 }; });
1487
+ return rootWithNode(context, context.root, newRoot, () => {
1488
+ return { node: newText, index: insertionPoint.index + 1 };
1489
+ });
947
1490
  }
948
- }
949
- }
1491
+ },
1492
+ },
950
1493
  ];
951
1494
  export default commands;