bookish-press 0.4.1 → 0.4.21

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 (395) 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 -75
  217. package/components/page/Edition.svelte.d.ts +4 -4
  218. package/components/page/Editions.svelte +158 -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 +207 -0
  287. package/models/Firebase.d.ts +1 -0
  288. package/models/Firebase.js +21 -15
  289. package/models/book/Book.d.ts +26 -30
  290. package/models/book/Book.js +120 -113
  291. package/models/book/BookMedia.d.ts +3 -3
  292. package/models/book/BookMedia.js +22 -16
  293. package/models/book/BookSaveStatus.d.ts +7 -0
  294. package/models/book/BookSaveStatus.js +8 -0
  295. package/models/book/Chapter.d.ts +46 -39
  296. package/models/book/Chapter.js +180 -198
  297. package/models/book/ChapterID.js +7 -7
  298. package/models/book/Definition.d.ts +1 -1
  299. package/models/book/Edition.d.ts +62 -60
  300. package/models/book/Edition.js +385 -303
  301. package/models/book/Reference.d.ts +24 -0
  302. package/models/book/Reference.js +78 -0
  303. package/models/book/Schema.d.ts +169 -0
  304. package/models/book/Schema.js +114 -0
  305. package/models/book/Theme.d.ts +44 -37
  306. package/models/book/Theme.js +452 -52
  307. package/models/book/ThemeColors.d.ts +1 -1
  308. package/models/book/Words.d.ts +2 -0
  309. package/models/book/Words.js +110 -0
  310. package/models/chapter/AtomNode.d.ts +4 -4
  311. package/models/chapter/AtomNode.js +20 -13
  312. package/models/chapter/BlockNode.d.ts +9 -10
  313. package/models/chapter/BlockNode.js +32 -13
  314. package/models/chapter/BlocksNode.d.ts +10 -10
  315. package/models/chapter/BlocksNode.js +315 -120
  316. package/models/chapter/BlocksNode.test.js +494 -228
  317. package/models/chapter/Bookkeeping.d.ts +1 -1
  318. package/models/chapter/CalloutNode.d.ts +3 -3
  319. package/models/chapter/CalloutNode.js +34 -12
  320. package/models/chapter/Caret.d.ts +9 -7
  321. package/models/chapter/Caret.js +11 -6
  322. package/models/chapter/ChapterNode.d.ts +11 -11
  323. package/models/chapter/ChapterNode.js +70 -33
  324. package/models/chapter/CitationsNode.d.ts +3 -3
  325. package/models/chapter/CitationsNode.js +31 -12
  326. package/models/chapter/CodeNode.d.ts +11 -6
  327. package/models/chapter/CodeNode.js +143 -34
  328. package/models/chapter/CommentNode.d.ts +5 -5
  329. package/models/chapter/CommentNode.js +29 -13
  330. package/models/chapter/DefinitionNode.d.ts +2 -2
  331. package/models/chapter/DefinitionNode.js +15 -7
  332. package/models/chapter/Edit.d.ts +3 -3
  333. package/models/chapter/EmbedNode.d.ts +9 -5
  334. package/models/chapter/EmbedNode.js +108 -35
  335. package/models/chapter/ErrorNode.d.ts +2 -2
  336. package/models/chapter/ErrorNode.js +34 -12
  337. package/models/chapter/FootnoteNode.d.ts +5 -5
  338. package/models/chapter/FootnoteNode.js +34 -16
  339. package/models/chapter/FormatNode.d.ts +10 -10
  340. package/models/chapter/FormatNode.js +237 -106
  341. package/models/chapter/FormatNode.test.js +87 -45
  342. package/models/chapter/InlineCodeNode.d.ts +2 -2
  343. package/models/chapter/InlineCodeNode.js +22 -9
  344. package/models/chapter/LabelNode.d.ts +4 -4
  345. package/models/chapter/LabelNode.js +31 -13
  346. package/models/chapter/LinkNode.d.ts +2 -2
  347. package/models/chapter/LinkNode.js +21 -9
  348. package/models/chapter/ListNode.d.ts +8 -8
  349. package/models/chapter/ListNode.js +99 -38
  350. package/models/chapter/ListNode.test.js +29 -24
  351. package/models/chapter/MetadataNode.d.ts +3 -3
  352. package/models/chapter/MetadataNode.js +13 -5
  353. package/models/chapter/Node.d.ts +5 -3
  354. package/models/chapter/Node.js +76 -29
  355. package/models/chapter/Node.test.js +20 -18
  356. package/models/chapter/ParagraphNode.d.ts +6 -6
  357. package/models/chapter/ParagraphNode.js +84 -29
  358. package/models/chapter/ParagraphNode.test.js +22 -13
  359. package/models/chapter/Parser.d.ts +25 -25
  360. package/models/chapter/Parser.js +201 -189
  361. package/models/chapter/Parser.test.js +16 -16
  362. package/models/chapter/Position.d.ts +1 -1
  363. package/models/chapter/QuoteNode.d.ts +6 -6
  364. package/models/chapter/QuoteNode.js +61 -23
  365. package/models/chapter/RootNode.d.ts +4 -4
  366. package/models/chapter/RuleNode.d.ts +2 -2
  367. package/models/chapter/RuleNode.js +31 -11
  368. package/models/chapter/TableNode.d.ts +8 -6
  369. package/models/chapter/TableNode.js +89 -34
  370. package/models/chapter/TableNode.test.js +22 -24
  371. package/models/chapter/TextNode.d.ts +6 -6
  372. package/models/chapter/TextNode.js +72 -32
  373. package/models/chapter/TextNode.test.js +4 -4
  374. package/models/loadEdition.d.ts +1 -1
  375. package/models/loadEdition.js +10 -6
  376. package/package.json +104 -25
  377. package/util/getSubdomain.js +8 -6
  378. package/util/isMobile.js +1 -1
  379. package/util/mineReference.d.ts +3 -2
  380. package/util/mineReference.js +67 -29
  381. package/util/scrollToEyeLevel.js +6 -1
  382. package/components/chapter/renderPosition.d.ts +0 -3
  383. package/components/chapter/renderPosition.js +0 -4
  384. package/components/editor/ToolbarIcon.svelte +0 -22
  385. package/components/editor/ToolbarIcon.svelte.d.ts +0 -16
  386. package/components/page/Alert.svelte +0 -11
  387. package/components/page/Alert.svelte.d.ts +0 -23
  388. package/components/page/Revisions.svelte +0 -124
  389. package/components/page/Revisions.svelte.d.ts +0 -14
  390. package/models/BookLoader.d.ts +0 -2
  391. package/models/BookLoader.js +0 -67
  392. package/models/Firestore.d.ts +0 -22
  393. package/models/Firestore.js +0 -194
  394. package/models/chapter/ReferenceNode.d.ts +0 -29
  395. package/models/chapter/ReferenceNode.js +0 -48
@@ -1,24 +1,25 @@
1
- <script>import ChapterNode from "../../models/chapter/ChapterNode";
2
- import Parser from "../../models/chapter/Parser";
3
- import LinkNode from "../../models/chapter/LinkNode";
4
- import ParagraphNode from "../../models/chapter/ParagraphNode";
5
- import TextNode from "../../models/chapter/TextNode";
6
- import AtomNode from "../../models/chapter/AtomNode";
7
- import FormatNode from "../../models/chapter/FormatNode";
8
- import BlocksNode from "../../models/chapter/BlocksNode";
9
- import ListNode from "../../models/chapter/ListNode";
10
- import TableNode from "../../models/chapter/TableNode";
11
- import MetadataNode from "../../models/chapter/MetadataNode";
12
- import CodeNode from "../../models/chapter/CodeNode";
13
- import BlockNode from "../../models/chapter/BlockNode";
14
- import EmbedNode from "../../models/chapter/EmbedNode";
15
- import ErrorNode from "../../models/chapter/ErrorNode";
16
- import { caretRangeToIndexRange, indexRangeToCaretRange } from "../../models/chapter/Caret";
17
- import commands from "./Commands";
18
- import Toolbar from "./Toolbar.svelte";
19
- import { CARET, getEdition } from "../page/Contexts";
20
- import { afterUpdate, onMount, setContext } from "svelte";
21
- import { writable } from "svelte/store";
1
+ <script>import BookishNode from '../../models/chapter/Node';
2
+ import ChapterNode from '../../models/chapter/ChapterNode';
3
+ import Parser from '../../models/chapter/Parser';
4
+ import LinkNode from '../../models/chapter/LinkNode';
5
+ import ParagraphNode from '../../models/chapter/ParagraphNode';
6
+ import TextNode from '../../models/chapter/TextNode';
7
+ import AtomNode from '../../models/chapter/AtomNode';
8
+ import FormatNode from '../../models/chapter/FormatNode';
9
+ import BlocksNode from '../../models/chapter/BlocksNode';
10
+ import ListNode from '../../models/chapter/ListNode';
11
+ import TableNode from '../../models/chapter/TableNode';
12
+ import MetadataNode from '../../models/chapter/MetadataNode';
13
+ import CodeNode from '../../models/chapter/CodeNode';
14
+ import BlockNode from '../../models/chapter/BlockNode';
15
+ import EmbedNode from '../../models/chapter/EmbedNode';
16
+ import ErrorNode from '../../models/chapter/ErrorNode';
17
+ import { caretRangeToIndexRange, indexRangeToCaretRange, } from '../../models/chapter/Caret';
18
+ import commands from './Commands';
19
+ import { getEdition } from '../page/Contexts';
20
+ import { afterUpdate, onMount, setContext } from 'svelte';
21
+ import { getCaret } from '../page/Contexts';
22
+ import CaretView from './CaretView.svelte';
22
23
  const IDLE_TIME = 500;
23
24
  export let ast;
24
25
  export let save;
@@ -38,6 +39,7 @@ let clipboard = undefined;
38
39
  let editedNode = ast;
39
40
  let ignoredInput = false;
40
41
  let edition = getEdition();
42
+ let activeEditor = getCaret();
41
43
  onMount(() => {
42
44
  const caret = editedNode.getFirstCaret();
43
45
  if (caret)
@@ -47,15 +49,15 @@ onMount(() => {
47
49
  element.focus();
48
50
  }
49
51
  // Listen to selection changes
50
- document.addEventListener("selectionchange", handleSelectionChange);
52
+ document.addEventListener('selectionchange', handleSelectionChange);
51
53
  // Stop listening to selection changes
52
54
  return () => {
53
- document.removeEventListener("selectionchange", handleSelectionChange);
55
+ document.removeEventListener('selectionchange', handleSelectionChange);
54
56
  };
55
57
  });
56
58
  onMount(() => {
57
59
  // When input happens, set a timer and then see if idle time has elapsed. If it has, track time since last keystroke to control caret blinking behavior.
58
- const keystrokeTimer = setTimeout(() => {
60
+ const keystrokeTimer = setInterval(() => {
59
61
  // Remember that we're idle so we can render things accordingly.
60
62
  const isIdle = Date.now() - lastInputTime > IDLE_TIME;
61
63
  if (isIdle) {
@@ -83,19 +85,26 @@ afterUpdate(() => {
83
85
  const sortedRange = caretRange;
84
86
  let startNode = document.querySelector(`[data-nodeid='${sortedRange.start.node.nodeID}`);
85
87
  let endNode = document.querySelector(`[data-nodeid='${sortedRange.end.node.nodeID}`);
86
- // If we found both selected nodes and we have a reference to the DOM, update the
88
+ // If we found both selected nodes and we have a reference to the DOM, update the
87
89
  // browser's selection if necessary and measure it's position so we can draw our own caret.
88
90
  if (startNode && endNode && element) {
89
91
  // If the current selection isn't different from the new one, then we don't do any of this.
90
- // This prevents an infinite loop when, for example, the user clicks, the browser selection changes,
92
+ // This prevents an infinite loop when, for example, the user clicks, the browser selection changes,
91
93
  // and we update the caret range, which updates the browser selection, ad infinitum.
92
94
  let rangeChanged = true;
93
95
  let currentRange = undefined;
94
96
  if (docSelection.rangeCount > 0) {
95
97
  currentRange = docSelection.getRangeAt(0);
96
98
  // If our caret range is non-empty TextNodes at a particular index and equal to the browser's caret range, then nothing's changed, so we shouldn't update the selection.
97
- const startIsEqual = (startNode.childNodes.length > 0 && currentRange.startContainer === startNode.childNodes[0] && currentRange.startOffset === sortedRange.start.index);
98
- const endIsEqual = (endNode.childNodes.length > 0 && currentRange.endContainer === endNode.childNodes[0] && currentRange.endOffset === sortedRange.end.index);
99
+ const startIsEqual = startNode.childNodes.length > 0 &&
100
+ currentRange.startContainer ===
101
+ startNode.childNodes[0] &&
102
+ currentRange.startOffset ===
103
+ sortedRange.start.index;
104
+ const endIsEqual = endNode.childNodes.length > 0 &&
105
+ currentRange.endContainer ===
106
+ endNode.childNodes[0] &&
107
+ currentRange.endOffset === sortedRange.end.index;
99
108
  if (startIsEqual && endIsEqual)
100
109
  rangeChanged = false;
101
110
  }
@@ -105,7 +114,8 @@ afterUpdate(() => {
105
114
  // We only do this to the end node if it's different from the start node.
106
115
  // We leave them in there so that the "rangeChanged" guard below can track them.
107
116
  const emptyStart = startNode.childNodes.length === 0;
108
- const emptyEnd = startNode !== endNode && endNode.childNodes.length === 0;
117
+ const emptyEnd = startNode !== endNode &&
118
+ endNode.childNodes.length === 0;
109
119
  if (emptyStart)
110
120
  startNode.appendChild(document.createTextNode('\ufeff'));
111
121
  if (emptyEnd)
@@ -113,7 +123,9 @@ afterUpdate(() => {
113
123
  // If we're just after a new line, set the selection to the character after.
114
124
  const afterNewLine = sortedRange.start.node instanceof TextNode &&
115
125
  sortedRange.start.index > 0 &&
116
- sortedRange.start.node.getText().charAt(sortedRange.start.index - 1) === "\n";
126
+ sortedRange.start.node
127
+ .getText()
128
+ .charAt(sortedRange.start.index - 1) === '\n';
117
129
  // If the range has changed, set the selection so we can measure it's location.
118
130
  // We guard this to avoid triggering unnecessary selection change events that don't actually modify the selection.
119
131
  if (rangeChanged) {
@@ -122,7 +134,7 @@ afterUpdate(() => {
122
134
  }
123
135
  catch (e) {
124
136
  console.error(e);
125
- throw Error(`Error setting caret range was set to ${startNode.childNodes[0]}:${sortedRange.start.index} - ${endNode.childNodes[0]}:${sortedRange.end.index}`);
137
+ console.error(`Error setting caret range, trying to set to ${startNode.childNodes[0]}:${sortedRange.start.index} - ${endNode.childNodes[0]}:${sortedRange.end.index}`);
126
138
  }
127
139
  currentRange = docSelection.getRangeAt(0);
128
140
  }
@@ -130,26 +142,35 @@ afterUpdate(() => {
130
142
  if (currentRange) {
131
143
  const rangeRect = currentRange.getBoundingClientRect();
132
144
  const textRect = startNode.getBoundingClientRect();
133
- const lineHeightString = window.getComputedStyle(startNode).getPropertyValue("line-height");
134
- const lineHeight = lineHeightString.endsWith("px") ? parseInt(lineHeightString.substring(0, lineHeightString.length - 2)) : undefined;
145
+ const lineHeightString = window
146
+ .getComputedStyle(startNode)
147
+ .getPropertyValue('line-height');
148
+ const lineHeight = lineHeightString.endsWith('px')
149
+ ? parseInt(lineHeightString.substring(0, lineHeightString.length - 2))
150
+ : undefined;
135
151
  // Is the caret in relatively positioned ancestors above the editor? Undo their offsets.
136
- let caretAncestor = startNode.closest(".bookish-editor");
152
+ let caretAncestor = startNode.closest('.bookish-editor');
137
153
  let relativeX = 0;
138
154
  let relativeY = 0;
139
155
  while (caretAncestor != null) {
140
- if (window.getComputedStyle(caretAncestor).position === "relative") {
156
+ if (window.getComputedStyle(caretAncestor)
157
+ .position === 'relative') {
141
158
  relativeX += caretAncestor.offsetLeft;
142
159
  relativeY += caretAncestor.offsetTop;
143
160
  }
144
161
  caretAncestor = caretAncestor.parentElement;
145
162
  }
146
163
  // If we're after a new line, calculate the correct position, since selections don't actually render to the next line.
147
- const left = afterNewLine ? textRect.left : rangeRect.left;
148
- const top = afterNewLine && lineHeight ? rangeRect.top + lineHeight : rangeRect.top;
164
+ const left = afterNewLine
165
+ ? textRect.left
166
+ : rangeRect.left;
167
+ const top = afterNewLine && lineHeight
168
+ ? rangeRect.top + lineHeight
169
+ : rangeRect.top;
149
170
  const position = {
150
171
  x: left + window.scrollX - relativeX,
151
172
  y: top + window.scrollY - relativeY,
152
- height: rangeRect.height
173
+ height: rangeRect.height,
153
174
  };
154
175
  newCaretPosition = position;
155
176
  }
@@ -161,38 +182,30 @@ afterUpdate(() => {
161
182
  }
162
183
  // Save the new caret position so we render it.
163
184
  caretCoordinate = newCaretPosition;
164
- // If the caret position is outside the window, and the editor is focused, scroll to make the caret visible.
165
- if (newCaretPosition && editorFocused) {
166
- const caretTop = newCaretPosition.y;
167
- const caretBottom = caretTop + newCaretPosition.height;
168
- const windowHeight = window.innerHeight;
169
- const toolbar = element?.querySelector(".bookish-editor-toolbar");
170
- const toolbarHeight = toolbar ? toolbar.clientHeight : 0;
171
- const buffer = newCaretPosition.height * 5;
172
- if (caretTop < window.scrollY + buffer + toolbarHeight) {
173
- window.scrollTo({ top: caretTop - buffer, behavior: 'smooth' });
174
- }
175
- else if (caretBottom > window.scrollY + windowHeight - buffer) {
176
- window.scrollTo({ top: caretBottom - (windowHeight - buffer), behavior: 'smooth' });
177
- }
178
- }
179
185
  });
180
186
  function rangeToCaret(domNode, rangeIndex) {
181
187
  // If it's a text node, find the closest TextNode parent
182
188
  if (domNode.nodeType === Node.TEXT_NODE) {
183
189
  let parent = domNode.parentNode;
184
190
  // Keep looking up until we find the TextNode span with the node ID.
185
- while (parent && !(parent instanceof HTMLElement && parent.classList.contains("bookish-text")))
191
+ while (parent &&
192
+ !(parent instanceof HTMLElement &&
193
+ parent.classList.contains('bookish-text')))
186
194
  parent = parent.parentNode;
187
195
  if (parent && parent.dataset.nodeid) {
188
196
  const node = editedNode.getNode(parseInt(parent.dataset.nodeid));
189
197
  if (node instanceof TextNode)
190
198
  // Account for the zero-width spaces that we insert in order to make selections possible on empty text nodes.
191
- return { node: node, index: Math.min(rangeIndex, node.getLength()) };
199
+ return {
200
+ node: node,
201
+ index: Math.min(rangeIndex, node.getLength()),
202
+ };
192
203
  }
193
204
  }
194
205
  // If it's an element, see if it has a nodeID and handle it accordingly.
195
- else if (domNode.nodeType === Node.ELEMENT_NODE && domNode instanceof HTMLElement && domNode.dataset.nodeid) {
206
+ else if (domNode.nodeType === Node.ELEMENT_NODE &&
207
+ domNode instanceof HTMLElement &&
208
+ domNode.dataset.nodeid) {
196
209
  const node = editedNode.getNode(parseInt(domNode.dataset.nodeid));
197
210
  // These assume that triple clicks on paragraphs in the browser choose text nodes for the selection start
198
211
  // and spans or paragraph nodes for the end.
@@ -213,7 +226,7 @@ function handleSelectionChange() {
213
226
  keyboardIdle = false;
214
227
  // Map the browser selection to our caret range model.
215
228
  const selection = document.getSelection();
216
- if (selection && selection.anchorNode && selection.anchorOffset && selection.focusNode && selection.focusOffset) {
229
+ if (selection && selection.anchorNode && selection.focusNode) {
217
230
  const start = rangeToCaret(selection.anchorNode, selection.anchorOffset);
218
231
  const end = rangeToCaret(selection.focusNode, selection.focusOffset);
219
232
  // If we found to text nodes, set the selection
@@ -234,9 +247,17 @@ function getCaretCoordinate(caret) {
234
247
  range.setStart(domNode.childNodes.length === 0 ? domNode : domNode.childNodes[0], domNode.childNodes.length === 0 ? 0 : caret.index);
235
248
  const characterRect = range.getBoundingClientRect();
236
249
  if (characterRect.left > 0)
237
- return { top: characterRect.top, left: characterRect.left, height: characterRect.height };
250
+ return {
251
+ top: characterRect.top,
252
+ left: characterRect.left,
253
+ height: characterRect.height,
254
+ };
238
255
  const elementRect = domNode.getBoundingClientRect();
239
- return { top: elementRect.top, left: elementRect.left, height: elementRect.height };
256
+ return {
257
+ top: elementRect.top,
258
+ left: elementRect.left,
259
+ height: elementRect.height,
260
+ };
240
261
  }
241
262
  function getCaretOnLine(caret, below) {
242
263
  if (caret.node instanceof TextNode || caret.node instanceof AtomNode) {
@@ -248,7 +269,8 @@ function getCaretOnLine(caret, below) {
248
269
  let previousCandidate = undefined;
249
270
  let previousCoordinate = undefined;
250
271
  if (startCoordinate) {
251
- while (candidate.node instanceof TextNode || candidate.node instanceof AtomNode) {
272
+ while (candidate.node instanceof TextNode ||
273
+ candidate.node instanceof AtomNode) {
252
274
  const candidateCoordinate = getCaretCoordinate(candidate);
253
275
  // Did we make it to the next line and the character before/after? If we're going down,
254
276
  // we're looking for a top that's below the bottom. If we're going up, we're looking for a bottom
@@ -256,21 +278,43 @@ function getCaretOnLine(caret, below) {
256
278
  if (candidateCoordinate) {
257
279
  const startTop = startCoordinate.top;
258
280
  const candidateTop = candidateCoordinate.top;
259
- const closerCandidate = previousCoordinate && previousCandidate && Math.abs(startCoordinate.left - previousCoordinate.left) < Math.abs(startCoordinate.left - candidateCoordinate.left) ? previousCandidate : candidate;
281
+ const closerCandidate = previousCoordinate &&
282
+ previousCandidate &&
283
+ Math.abs(startCoordinate.left - previousCoordinate.left) <
284
+ Math.abs(startCoordinate.left -
285
+ candidateCoordinate.left)
286
+ ? previousCandidate
287
+ : candidate;
260
288
  if (below) {
261
289
  // If this candidate is on the line below the start line, then return the last candidate on the line below the start line.
262
- if (previousCandidate && previousCoordinate && previousCoordinate.top > startTop + startCoordinate.height && candidateTop > previousCoordinate.top + previousCoordinate.height)
290
+ if (previousCandidate &&
291
+ previousCoordinate &&
292
+ previousCoordinate.top >
293
+ startTop + startCoordinate.height &&
294
+ candidateTop >
295
+ previousCoordinate.top +
296
+ previousCoordinate.height)
263
297
  return previousCandidate;
264
298
  // If this is just below the start position, stop and return the closer of the current and previous candidates.
265
- else if (candidateTop > startTop + startCoordinate.height && candidateCoordinate.left >= startCoordinate.left)
299
+ else if (candidateTop >
300
+ startTop + startCoordinate.height &&
301
+ candidateCoordinate.left >= startCoordinate.left)
266
302
  return closerCandidate;
267
303
  }
268
304
  else {
269
305
  // If this candidate is on the live above the line above the start line, then return the last candidate on the line above the start line.
270
- if (previousCandidate && previousCoordinate && previousCoordinate.top + previousCoordinate.height < startTop && candidateTop + candidateCoordinate.height < previousCoordinate.top)
306
+ if (previousCandidate &&
307
+ previousCoordinate &&
308
+ previousCoordinate.top +
309
+ previousCoordinate.height <
310
+ startTop &&
311
+ candidateTop + candidateCoordinate.height <
312
+ previousCoordinate.top)
271
313
  return previousCandidate;
272
314
  // If this is just above the start position, stop and return the closer of the current and previous candidates.
273
- if (candidateTop + candidateCoordinate.height < startTop && candidateCoordinate.left <= startCoordinate.left)
315
+ if (candidateTop + candidateCoordinate.height <
316
+ startTop &&
317
+ candidateCoordinate.left <= startCoordinate.left)
274
318
  return closerCandidate;
275
319
  }
276
320
  }
@@ -279,7 +323,8 @@ function getCaretOnLine(caret, below) {
279
323
  if (nextCandidate === undefined)
280
324
  break;
281
325
  // If the caret didn't move, we stop searching, something is wrong.
282
- if (nextCandidate.node === candidate.node && nextCandidate.index === candidate.index) {
326
+ if (nextCandidate.node === candidate.node &&
327
+ nextCandidate.index === candidate.index) {
283
328
  break;
284
329
  }
285
330
  // Otherwise, we advance to the next candidate.
@@ -301,17 +346,24 @@ function atParagraphStart() {
301
346
  caretRange.start.index !== caretRange.end.index ||
302
347
  !(caretRange.start.node instanceof TextNode))
303
348
  return false;
304
- const firstCaret = caretRange.start.node.getFormatRoot(editedNode)?.getFirstCaret();
305
- return firstCaret !== undefined && firstCaret.node === caretRange.start.node && firstCaret.index === caretRange.start.index;
349
+ const firstCaret = caretRange.start.node
350
+ .getFormatRoot(editedNode)
351
+ ?.getFirstCaret();
352
+ return (firstCaret !== undefined &&
353
+ firstCaret.node === caretRange.start.node &&
354
+ firstCaret.index === caretRange.start.index);
306
355
  }
307
356
  function undo() {
308
357
  // Grab the next state
309
358
  let undoState = undoStack[undoPosition + 1];
310
359
  // Restore the content of the chapter.
311
- let node = ast instanceof ChapterNode ? Parser.parseChapter($edition, undoState.bookdown) :
312
- ast instanceof FormatNode ? Parser.parseFormat($edition, undoState.bookdown) :
313
- ast instanceof EmbedNode ? Parser.parseEmbed($edition, undoState.bookdown) :
314
- undefined;
360
+ let node = ast instanceof ChapterNode
361
+ ? Parser.parseChapter($edition, undoState.bookdown)
362
+ : ast instanceof FormatNode
363
+ ? Parser.parseFormat($edition, undoState.bookdown)
364
+ : ast instanceof EmbedNode
365
+ ? Parser.parseEmbed($edition, undoState.bookdown)
366
+ : undefined;
315
367
  if (node === undefined || node instanceof ErrorNode)
316
368
  return;
317
369
  // Restore the view
@@ -331,10 +383,13 @@ function redo() {
331
383
  // Grab the next state
332
384
  let undoState = undoStack[undoPosition - 1];
333
385
  // Restore the content of the chapter.
334
- let node = ast instanceof ChapterNode ? Parser.parseChapter($edition, undoState.bookdown) :
335
- ast instanceof FormatNode ? Parser.parseFormat($edition, undoState.bookdown) :
336
- ast instanceof EmbedNode ? Parser.parseEmbed($edition, undoState.bookdown) :
337
- undefined;
386
+ let node = ast instanceof ChapterNode
387
+ ? Parser.parseChapter($edition, undoState.bookdown)
388
+ : ast instanceof FormatNode
389
+ ? Parser.parseFormat($edition, undoState.bookdown)
390
+ : ast instanceof EmbedNode
391
+ ? Parser.parseEmbed($edition, undoState.bookdown)
392
+ : undefined;
338
393
  if (node === undefined)
339
394
  return;
340
395
  editedNode = node;
@@ -354,39 +409,50 @@ function getCaretContext() {
354
409
  const nodes = editedNode.getNodes();
355
410
  let inside = false;
356
411
  let includesList = false;
357
- nodes.forEach(n => {
412
+ nodes.forEach((n) => {
358
413
  if (caretRange === undefined)
359
414
  return;
360
415
  if (n === caretRange.start.node)
361
416
  inside = true;
362
- if (inside && n.getClosestParentMatching(editedNode, p => p instanceof ListNode) !== undefined)
417
+ if (inside &&
418
+ n.getClosestParentMatching(editedNode, (p) => p instanceof ListNode) !== undefined)
363
419
  includesList = true;
364
420
  if (n === caretRange.end.node)
365
421
  inside = false;
366
422
  });
367
- const parents = editedNode.getParentsOf(caretRange.start.node)?.reverse();
423
+ const parents = editedNode
424
+ .getParentsOf(caretRange.start.node)
425
+ ?.reverse();
368
426
  return {
369
427
  // We make a new range so that setCaretRange always causes a re-render
370
428
  chapter: chapter,
371
429
  range: { start: caretRange.start, end: caretRange.end },
372
430
  start: caretRange.start,
373
431
  end: caretRange.end,
374
- isSelection: caretRange.start.node !== caretRange.end.node || caretRange.start.index !== caretRange.end.index,
432
+ isSelection: caretRange.start.node !== caretRange.end.node ||
433
+ caretRange.start.index !== caretRange.end.index,
375
434
  root: editedNode,
376
- blocks: parents?.find(n => n instanceof BlocksNode),
377
- paragraph: parents?.find(n => n instanceof ParagraphNode),
378
- block: parents?.find(n => n instanceof BlockNode),
379
- code: parents?.find(n => n instanceof CodeNode),
380
- list: parents?.find(n => n instanceof ListNode),
381
- atom: parents?.find(n => n instanceof AtomNode),
382
- meta: parents?.find(n => n instanceof MetadataNode),
435
+ blocks: parents?.find((n) => n instanceof BlocksNode),
436
+ paragraph: parents?.find((n) => n instanceof ParagraphNode),
437
+ block: parents?.find((n) => n instanceof BlockNode),
438
+ code: parents?.find((n) => n instanceof CodeNode),
439
+ list: parents?.find((n) => n instanceof ListNode),
440
+ atom: parents?.find((n) => n instanceof AtomNode),
441
+ meta: parents?.find((n) => n instanceof MetadataNode),
383
442
  includesList: includesList,
384
- table: parents?.find(n => n instanceof TableNode),
385
- format: (caretRange.end.node instanceof TextNode || caretRange.end.node instanceof AtomNode) ? caretRange.end.node.getFormatRoot(editedNode) : undefined,
443
+ table: parents?.find((n) => n instanceof TableNode),
444
+ format: caretRange.end.node instanceof TextNode ||
445
+ caretRange.end.node instanceof AtomNode
446
+ ? caretRange.end.node.getFormatRoot(editedNode)
447
+ : undefined,
386
448
  startIsText: caretRange.start.node instanceof TextNode,
387
449
  endIsText: caretRange.end.node instanceof TextNode,
388
- startIsTextOrAtom: caretRange.start.node instanceof TextNode || caretRange.start.node instanceof AtomNode,
389
- endIsTextOrAtom: caretRange.end.node instanceof TextNode || caretRange.end.node instanceof AtomNode,
450
+ startIsTextOrAtom: caretRange.start.node instanceof TextNode ||
451
+ caretRange.start.node instanceof AtomNode ||
452
+ caretRange.start.node instanceof EmbedNode,
453
+ endIsTextOrAtom: caretRange.end.node instanceof TextNode ||
454
+ caretRange.end.node instanceof AtomNode ||
455
+ caretRange.start.node instanceof EmbedNode,
390
456
  atParagraphStart: atParagraphStart(),
391
457
  undoStack: undoStack,
392
458
  undoPosition: undoPosition,
@@ -394,15 +460,18 @@ function getCaretContext() {
394
460
  redo: redo,
395
461
  clipboard: clipboard,
396
462
  handleCopy: handleCopy,
397
- handlePaste: handlePaste
463
+ handlePaste: handlePaste,
398
464
  };
399
465
  }
400
466
  function getUtilities() {
401
467
  return {
402
- getCaretOnLine: getCaretOnLine
468
+ getCaretOnLine: getCaretOnLine,
403
469
  };
404
470
  }
405
- function handleKeyDown(event) {
471
+ function handlePress(event) {
472
+ handleKey(event, true);
473
+ }
474
+ function handleKey(event, press = false) {
406
475
  // Only handle keystrokes when this is focused.
407
476
  // Otherwise, we let any focusable elements in this editor handle them.
408
477
  if (!editorFocused)
@@ -417,35 +486,51 @@ function handleKeyDown(event) {
417
486
  if (context === undefined)
418
487
  return;
419
488
  // Loop through the commands to see if there's a match.
420
- const unmatched = commands.every(command => {
489
+ const unmatched = commands.every((command) => {
421
490
  // If the keystroke and caret position matches the command signature, execute the command and update the caret range.
422
- if ((command.shift === undefined || command.shift === event.shiftKey) &&
423
- command.alt === event.altKey &&
424
- command.control === (event.ctrlKey || event.metaKey) &&
425
- (command.key === undefined || command.key === event.key || (Array.isArray(command.key) && command.key.includes(event.key))) &&
491
+ if ((command.press === undefined || command.press === press) &&
492
+ (command.shift === undefined ||
493
+ command.shift === event.shiftKey) &&
494
+ (command.alt === undefined || command.alt === event.altKey) &&
495
+ (command.control === undefined ||
496
+ command.control === (event.ctrlKey || event.metaKey)) &&
497
+ (command.key === undefined ||
498
+ (Array.isArray(command.key) &&
499
+ command.key.includes(event.key)) ||
500
+ (command.key instanceof Function &&
501
+ command.key(event.key)) ||
502
+ command.key === event.key) &&
426
503
  (command.code === undefined || command.code === event.code) &&
427
- (command.active === true || (command.active instanceof Function && command.active.call(undefined, context, event.key)))) {
428
- event.preventDefault();
429
- event.stopPropagation();
504
+ (command.active === true ||
505
+ (command.active instanceof Function &&
506
+ command.active.call(undefined, context, event.key)))) {
430
507
  // Execute the command
431
- executeCommand(command, event.key);
508
+ const handled = executeCommand(command, event.key);
509
+ if (handled) {
510
+ event.preventDefault();
511
+ event.stopPropagation();
512
+ }
432
513
  // Stop searching for a matching command.
433
- return false;
514
+ return handled;
434
515
  }
435
516
  // Continue searching for a matching command.
436
517
  return true;
437
518
  });
438
519
  if (unmatched) {
439
520
  // Toolbar navigation
440
- if (event.key === "Tab") {
441
- // If we've selected a FootnoteNode or Comment, navigate to the footnote or comment text.
442
- if (caretRange.start.node instanceof AtomNode && caretRange.start.node.getMeta() instanceof FormatNode) {
443
- const firstCaret = caretRange.start.node.getMeta().getFirstCaret();
521
+ if (event.key === 'Escape') {
522
+ // If we've selected a FootnoteNode or Comment, navigate to the footnote or comment text to edit it.
523
+ if (caretRange.start.node instanceof AtomNode &&
524
+ caretRange.start.node.getMeta() instanceof FormatNode) {
525
+ const firstCaret = caretRange.start.node
526
+ .getMeta()
527
+ .getFirstCaret();
444
528
  caretRange = { start: firstCaret, end: firstCaret };
445
529
  event.preventDefault();
446
530
  event.stopPropagation();
447
531
  return true;
448
532
  }
533
+ // If we're inside an atom node, select the atom itself.
449
534
  else if (caretRange.start.node.isInside(editedNode, AtomNode)) {
450
535
  const atom = caretRange.start.node.getClosestParentOfType(editedNode, AtomNode);
451
536
  if (atom) {
@@ -456,19 +541,19 @@ function handleKeyDown(event) {
456
541
  return true;
457
542
  }
458
543
  }
459
- if (element) {
460
- const controls = [
461
- element.querySelector(".bookish-editor-toolbar input"),
462
- element.querySelector(".bookish-editor-toolbar select"),
463
- element.querySelector(".bookish-editor-toolbar button")
464
- ];
465
- const match = controls.find(control => control && control instanceof HTMLElement);
466
- if (match && match instanceof HTMLElement) {
467
- match.focus();
468
- event.preventDefault();
469
- event.stopPropagation();
470
- return true;
471
- }
544
+ // Otherwise, if we have a reference to this editor's DOM element, find the nearest focusable element in it's toolbar and focus on that.
545
+ const controls = [
546
+ document.querySelector('.bookish-editor-toolbar [tabindex="0"]'),
547
+ document.querySelector('.bookish-editor-toolbar input'),
548
+ document.querySelector('.bookish-editor-toolbar select'),
549
+ document.querySelector('.bookish-editor-toolbar button'),
550
+ ];
551
+ const match = controls.find((control) => control && control instanceof HTMLElement);
552
+ if (match && match instanceof HTMLElement) {
553
+ match.focus();
554
+ event.preventDefault();
555
+ event.stopPropagation();
556
+ return true;
472
557
  }
473
558
  }
474
559
  }
@@ -483,20 +568,26 @@ function executeCommand(command, key) {
483
568
  if (results !== undefined) {
484
569
  const { root, range } = results;
485
570
  const newRange = { start: range.start, end: range.end };
486
- if (root === editedNode && command.category !== "navigation" && command.category !== "selection")
571
+ if (root === editedNode && command.mutates)
487
572
  console.error(`Warning: immutability violation on ${command.description}`);
488
573
  // Set the range to force a rerender, assuming something in the document changed.
489
574
  caretRange = newRange;
490
575
  // Save the copy in the undo stack if this isn't a navigation or selection state.
491
- if (command.category !== "navigation" && command.category !== "selection" && command.category !== "history") {
576
+ if (command.category !== 'navigation' &&
577
+ command.category !== 'selection' &&
578
+ command.category !== 'history') {
492
579
  saveEdit(root, newRange, command);
493
580
  }
581
+ return true;
494
582
  }
495
- else
583
+ else {
496
584
  ignoredInput = true;
585
+ return false;
586
+ }
497
587
  }
588
+ return false;
498
589
  }
499
- function saveEdit(newRoot, newRange, command) {
590
+ async function saveEdit(newRoot, newRange, command) {
500
591
  // Call the save callback.
501
592
  save(newRoot);
502
593
  // Initialize a new history if we don't have one.
@@ -505,25 +596,35 @@ function saveEdit(newRoot, newRange, command) {
505
596
  if (undoStack.length === 0 && caretRange !== undefined) {
506
597
  const currentRange = caretRangeToIndexRange(editedNode, caretRange);
507
598
  if (currentRange !== undefined)
508
- newStack = [{
599
+ newStack = [
600
+ {
509
601
  bookdown: editedNode.toBookdown(),
510
602
  command: undefined,
511
- range: currentRange
512
- }];
603
+ range: currentRange,
604
+ },
605
+ ];
513
606
  }
514
607
  // Change the chapter's AST.
515
608
  editedNode = newRoot;
516
609
  // Set the new undo stack, pre-pending the new command to the front.
517
610
  const newTextRange = caretRangeToIndexRange(newRoot, newRange);
518
611
  if (newTextRange !== undefined)
519
- undoStack = [{
612
+ undoStack = [
613
+ {
520
614
  bookdown: newRoot.toBookdown(),
521
615
  command: command,
522
- range: newTextRange
616
+ range: newTextRange,
523
617
  // If the undo position is beyond the front, clear everything before it, because we're changing history.
524
- }, ...(undoPosition > 0 ? newStack.slice(undoPosition) : newStack)];
618
+ },
619
+ ...(undoPosition > 0 ? newStack.slice(undoPosition) : newStack),
620
+ ];
525
621
  // Set the undo position to the last index.
526
622
  undoPosition = 0;
623
+ // Focus the editor on the new caret location, unless the toolbar is focused.
624
+ if (!toolbarIsFocused())
625
+ element?.focus();
626
+ // Update the caret
627
+ claimActiveEditor();
527
628
  }
528
629
  function editNode(previous, edited) {
529
630
  if (caretRange) {
@@ -533,8 +634,12 @@ function editNode(previous, edited) {
533
634
  saveEdit(newRoot, { start: caretRange.start, end: caretRange.end });
534
635
  // Update the range if the current range contains the previous node. This will generally
535
636
  // be true any time a selected atom node is edited.
536
- if (caretRange.start.node === previous && (edited instanceof AtomNode || edited instanceof TextNode))
537
- caretRange = { start: { node: edited, index: 0 }, end: { node: edited, index: 0 } };
637
+ if (caretRange.start.node === previous &&
638
+ (edited instanceof AtomNode || edited instanceof TextNode))
639
+ caretRange = {
640
+ start: { node: edited, index: 0 },
641
+ end: { node: edited, index: 0 },
642
+ };
538
643
  }
539
644
  }
540
645
  function handleMouseDown() {
@@ -546,20 +651,20 @@ function handleMouseDown() {
546
651
  if (caretRange)
547
652
  caretRange = { start: caretRange.start, end: caretRange.end };
548
653
  // If we've selected a non-TextNode, release it, so the browser is free to select a text node.
549
- if (caretRange && !(caretRange.start.node instanceof TextNode || caretRange.start.node instanceof AtomNode)) {
654
+ if (caretRange &&
655
+ !(caretRange.start.node instanceof TextNode ||
656
+ caretRange.start.node instanceof AtomNode ||
657
+ caretRange.start.node instanceof EmbedNode)) {
550
658
  caretRange = undefined;
551
659
  }
552
660
  }
553
- function handleKeyUp(event) {
554
- event.preventDefault();
555
- }
556
- function forceUpdate() {
557
- if (caretRange !== undefined) {
558
- caretRange = { start: caretRange.start, end: caretRange.end };
559
- }
661
+ function isFocused() {
662
+ return (document.activeElement === element ||
663
+ (element !== null && element?.contains(document.activeElement)) ||
664
+ toolbarIsFocused());
560
665
  }
561
666
  function updateFocus() {
562
- editorFocused = document.activeElement === element || (element !== null && element.contains(document.activeElement));
667
+ editorFocused = isFocused();
563
668
  }
564
669
  function handleCopy(node) {
565
670
  // Set the editor's clipboard.
@@ -568,15 +673,30 @@ function handleCopy(node) {
568
673
  if (navigator.clipboard) {
569
674
  navigator.clipboard.write([
570
675
  new ClipboardItem({
571
- "text/plain": new Blob([node.toBookdown()], { type: "text/plain" }),
572
- "text/html": new Blob([node.toHTML()], { type: "text/html" })
573
- })
676
+ 'text/plain': new Blob([node.toText()], {
677
+ type: 'text/plain',
678
+ }),
679
+ 'text/html': new Blob([node.toHTML()], {
680
+ type: 'text/html',
681
+ }),
682
+ }),
574
683
  ]);
575
684
  }
576
685
  else
577
686
  window.alert("Your browser doesn't support copying to the clipboard :(");
578
687
  }
579
- function handlePaste(context, node, save) {
688
+ function handlePaste(context, text, save) {
689
+ if (text instanceof BookishNode)
690
+ return handlePasteNode(context, text, save);
691
+ const plain = text.plain === undefined ? undefined : new TextNode(text.plain);
692
+ if (plain) {
693
+ const edit = handlePasteNode(context, plain, save);
694
+ if (edit)
695
+ return edit;
696
+ }
697
+ return undefined;
698
+ }
699
+ function handlePasteNode(context, node, save) {
580
700
  // Track revisions in these two variables.
581
701
  let newRoot = context.root;
582
702
  let newCaret = context.start;
@@ -619,112 +739,103 @@ function handlePaste(context, node, save) {
619
739
  return { root: newChapter, range: edit.range };
620
740
  }
621
741
  }
742
+ function toolbarIsFocused() {
743
+ const toolbar = document.querySelector('.bookish-editor-toolbar');
744
+ return toolbar !== null && toolbar.contains(document.activeElement);
745
+ }
622
746
  $: isAtom = caretRange && caretRange.start.node instanceof AtomNode;
623
- $: inAtom = caretRange && caretRange.start.node.isInside(editedNode, AtomNode);
624
- $: isSelection = caretRange && (caretRange.start.node !== caretRange.end.node || caretRange.start.index !== caretRange.end.index);
625
- $: isItalic = caretRange && !isSelection && caretRange.start.node instanceof TextNode && caretRange.start.node.isItalic(editedNode);
626
- $: isBold = caretRange && !isSelection && caretRange.start.node instanceof TextNode && caretRange.start.node.isBold(editedNode);
627
- $: isLink = caretRange && !isSelection && caretRange.start.node.getClosestParentMatching(editedNode, p => p instanceof LinkNode) !== undefined;
628
- let context = undefined;
747
+ $: inAtom =
748
+ caretRange && caretRange.start.node.isInside(editedNode, AtomNode);
749
+ $: isSelection =
750
+ caretRange !== undefined &&
751
+ (caretRange.start.node !== caretRange.end.node ||
752
+ caretRange.start.index !== caretRange.end.index);
753
+ $: isItalic =
754
+ caretRange !== undefined &&
755
+ isSelection === false &&
756
+ caretRange.start.node instanceof TextNode &&
757
+ caretRange.start.node.isItalic(editedNode);
758
+ $: isBold =
759
+ caretRange !== undefined &&
760
+ isSelection === false &&
761
+ caretRange.start.node instanceof TextNode &&
762
+ caretRange.start.node.isBold(editedNode);
763
+ $: isLink =
764
+ caretRange !== undefined &&
765
+ isSelection === false &&
766
+ caretRange.start.node.getClosestParentMatching(editedNode, (p) => p instanceof LinkNode) !== undefined;
767
+ // When the caret context or editor focus changes, update the active editor.
629
768
  $: {
630
- caretRange = caretRange;
631
- editedNode = editedNode;
632
- context = getCaretContext();
769
+ if ((caretRange || editorFocused) && isFocused())
770
+ claimActiveEditor();
771
+ else
772
+ activeEditor.set(undefined);
633
773
  }
634
- // Create a caret store for the editor and make it available to children.
635
- let caretStore = setContext(CARET, writable());
636
- // Update the caret store it whenever any of the below change.
637
- $: caretStore.set({
638
- range: caretRange,
639
- coordinate: caretCoordinate,
640
- forceUpdate: forceUpdate,
641
- setCaret: range => { caretRange = range; element?.focus(); },
642
- edit: editNode,
643
- context: context,
644
- root: editedNode,
645
- focused: editorFocused
646
- });
774
+ function claimActiveEditor() {
775
+ activeEditor.set({
776
+ range: caretRange,
777
+ coordinate: caretCoordinate,
778
+ setCaret: (range) => {
779
+ caretRange = range;
780
+ element?.focus();
781
+ },
782
+ edit: editNode,
783
+ executor: executeCommand,
784
+ context: getCaretContext(),
785
+ root: editedNode,
786
+ focused: editorFocused,
787
+ });
788
+ }
789
+ // Pass the function above to all children so they can claim the active editor for non-focus reasons.
790
+ // (Drag and drop, for example.)
791
+ setContext('claimeditor', claimActiveEditor);
647
792
  </script>
648
793
 
649
- <div
650
- class={`bookish-editor ${inAtom ? "bookish-editor-atom-focused" : ""}`}
794
+ <div
795
+ class={`bookish-editor ${inAtom ? 'bookish-editor-atom-focused' : ''}`}
651
796
  bind:this={element}
652
- on:keydown={handleKeyDown}
653
- on:keyup={handleKeyUp}
797
+ on:keydown={handleKey}
798
+ on:keypress={handlePress}
654
799
  on:mousedown|stopPropagation={handleMouseDown}
655
800
  on:focus={updateFocus}
656
801
  on:blur={updateFocus}
657
- role="textbox"
658
- tabIndex=0
802
+ tabIndex="0"
659
803
  >
660
- {#if context && caretCoordinate }
661
- <Toolbar context={context} executor={executeCommand} visible={editorFocused} />
662
- {/if}
663
- <!-- Draw a caret. We draw our own since this view isn't contentEditable and we can't show a caret.
804
+ <!-- The chapter content -->
805
+ <svelte:component this={component} node={editedNode} {placeholder} />
806
+ <!-- The caret. We render our own since this view isn't contentEditable and we can't show a caret.
664
807
  Customize the rendering based on the formatting applied to the text node. -->
665
- {#if caretCoordinate && caretRange && !isAtom && !isSelection && editorFocused }
666
- <div
667
- class={`bookish-editor-caret ${isLink ? "bookish-editor-caret-linked" : isItalic ? "bookish-editor-caret-italic" :""} ${isBold ? "bookish-editor-caret-bold" : ""} ${editorFocused && keyboardIdle ? "bookish-editor-caret-blink" : ""} ${!editorFocused ? "bookish-editor-caret-disabled" : ""} ${ignoredInput ? "bookish-editor-caret-ignored" : ""}`}
668
- style={`left: ${caretCoordinate.x}px; top: ${caretCoordinate.y}px; height: ${caretCoordinate.height}px;`}>
669
- </div>
808
+ {#if caretCoordinate && caretRange && !isAtom && isSelection === false && editorFocused}
809
+ <CaretView
810
+ left={caretCoordinate.x}
811
+ top={caretCoordinate.y}
812
+ height={caretCoordinate.height}
813
+ blink={editorFocused && keyboardIdle}
814
+ ignored={ignoredInput}
815
+ linked={isLink}
816
+ bold={isBold}
817
+ italic={isItalic}
818
+ disabled={!editorFocused}
819
+ />
670
820
  {/if}
671
- <svelte:component this={component} node={editedNode} placeholder={placeholder} />
672
821
  </div>
673
822
 
674
823
  <style>
824
+ .bookish-editor {
825
+ min-height: 2em;
826
+ }
827
+
675
828
  .bookish-editor:focus {
676
- outline: 2px solid var(--bookish-highlight-color);
677
- outline-offset: var(--bookish-app-chrome-padding);
678
- border-radius: var(--bookish-app-chrome-roundedness);
829
+ outline: var(--app-chrome-border-size) solid
830
+ var(--app-interactive-color);
831
+ outline-offset: var(--app-chrome-padding);
832
+ border-radius: var(--app-chrome-roundedness);
679
833
  }
680
834
 
681
835
  .bookish-editor.bookish-editor-atom-focused:focus {
682
836
  outline: none;
683
837
  }
684
838
 
685
- .bookish-editor-caret {
686
- left: 0;
687
- top: 0;
688
- width: 0px;
689
- background: none;
690
- outline: 1px solid var(--bookish-paragraph-color);
691
- display: inline-block;
692
- box-sizing: border-box;
693
- position: absolute;
694
- z-index: 1;
695
- }
696
-
697
- @keyframes bookish-editor-caret-blink {
698
- 100% {
699
- outline: none;
700
- }
701
- }
702
-
703
- .bookish-editor-caret-disabled {
704
- outline-color: var(--bookish-app-chrome-muted);
705
- }
706
-
707
- .bookish-editor-caret-blink {
708
- animation: bookish-editor-caret-blink 1s steps(2) infinite;
709
- }
710
-
711
- .bookish-editor-caret-italic {
712
- transform: skew(-10deg);
713
- }
714
-
715
- .bookish-editor-caret-bold {
716
- outline-width: 2px;
717
- }
718
-
719
- .bookish-editor-caret-ignored {
720
- animation: failure 100ms 10;
721
- }
722
-
723
- .bookish-editor-caret-linked {
724
- outline-width: 1px;
725
- outline-color: var(--bookish-link-color);
726
- }
727
-
728
839
  .bookish-editor-inline-editor {
729
840
  display: inline-block;
730
841
  position: relative;
@@ -743,13 +854,8 @@ $: caretStore.set({
743
854
  font-weight: bold;
744
855
  }
745
856
 
746
- :global(.bookish-editor .bookish-table table td) {
747
- border: var(--bookish-app-chrome-border-width) dashed var(--bookish-app-chrome-border-color);
748
- }
749
-
750
857
  .bookish-editor :global(hr) {
751
858
  margin-top: 1em;
752
- margin-bottom: 1em;
859
+ margin-bottom: 1em;
753
860
  }
754
-
755
- </style>
861
+ </style>