bookish-press 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/components/Auth.svelte +52 -0
  4. package/components/Auth.svelte.d.ts +16 -0
  5. package/components/Authentication.d.ts +8 -0
  6. package/components/Authentication.js +1 -0
  7. package/components/BookPreview.svelte +87 -0
  8. package/components/BookPreview.svelte.d.ts +18 -0
  9. package/components/External.svelte +4 -0
  10. package/components/External.svelte.d.ts +18 -0
  11. package/components/Link.svelte +5 -0
  12. package/components/Link.svelte.d.ts +19 -0
  13. package/components/chapter/Atom.svelte +36 -0
  14. package/components/chapter/Atom.svelte.d.ts +19 -0
  15. package/components/chapter/Block.svelte +32 -0
  16. package/components/chapter/Block.svelte.d.ts +17 -0
  17. package/components/chapter/Callout.svelte +10 -0
  18. package/components/chapter/Callout.svelte.d.ts +17 -0
  19. package/components/chapter/CaptionedCode.svelte +34 -0
  20. package/components/chapter/CaptionedCode.svelte.d.ts +17 -0
  21. package/components/chapter/ChapterBody.svelte +23 -0
  22. package/components/chapter/ChapterBody.svelte.d.ts +18 -0
  23. package/components/chapter/Citations.svelte +66 -0
  24. package/components/chapter/Citations.svelte.d.ts +17 -0
  25. package/components/chapter/Code.svelte +35 -0
  26. package/components/chapter/Code.svelte.d.ts +22 -0
  27. package/components/chapter/Comment.svelte +46 -0
  28. package/components/chapter/Comment.svelte.d.ts +17 -0
  29. package/components/chapter/Definition.svelte +38 -0
  30. package/components/chapter/Definition.svelte.d.ts +20 -0
  31. package/components/chapter/Embed.svelte +122 -0
  32. package/components/chapter/Embed.svelte.d.ts +18 -0
  33. package/components/chapter/ErrorMessage.svelte +4 -0
  34. package/components/chapter/ErrorMessage.svelte.d.ts +17 -0
  35. package/components/chapter/Footnote.svelte +37 -0
  36. package/components/chapter/Footnote.svelte.d.ts +19 -0
  37. package/components/chapter/Format.svelte +57 -0
  38. package/components/chapter/Format.svelte.d.ts +18 -0
  39. package/components/chapter/InlineCode.svelte +12 -0
  40. package/components/chapter/InlineCode.svelte.d.ts +17 -0
  41. package/components/chapter/Label.svelte +20 -0
  42. package/components/chapter/Label.svelte.d.ts +17 -0
  43. package/components/chapter/Link.svelte +51 -0
  44. package/components/chapter/Link.svelte.d.ts +17 -0
  45. package/components/chapter/List.svelte +14 -0
  46. package/components/chapter/List.svelte.d.ts +17 -0
  47. package/components/chapter/Marginal.svelte +48 -0
  48. package/components/chapter/Marginal.svelte.d.ts +19 -0
  49. package/components/chapter/Paragraph.svelte +15 -0
  50. package/components/chapter/Paragraph.svelte.d.ts +18 -0
  51. package/components/chapter/Python.svelte +81 -0
  52. package/components/chapter/Python.svelte.d.ts +18 -0
  53. package/components/chapter/Quote.svelte +16 -0
  54. package/components/chapter/Quote.svelte.d.ts +17 -0
  55. package/components/chapter/Rule.svelte +4 -0
  56. package/components/chapter/Rule.svelte.d.ts +17 -0
  57. package/components/chapter/Table.svelte +29 -0
  58. package/components/chapter/Table.svelte.d.ts +17 -0
  59. package/components/chapter/Text.svelte +62 -0
  60. package/components/chapter/Text.svelte.d.ts +17 -0
  61. package/components/chapter/renderPosition.d.ts +3 -0
  62. package/components/chapter/renderPosition.js +4 -0
  63. package/components/editor/BookishEditor.svelte +755 -0
  64. package/components/editor/BookishEditor.svelte.d.ts +22 -0
  65. package/components/editor/CalloutEditor.svelte +10 -0
  66. package/components/editor/CalloutEditor.svelte.d.ts +17 -0
  67. package/components/editor/CaptionedCodeEditor.svelte +22 -0
  68. package/components/editor/CaptionedCodeEditor.svelte.d.ts +17 -0
  69. package/components/editor/CaretContext.d.ts +18 -0
  70. package/components/editor/CaretContext.js +1 -0
  71. package/components/editor/CaretState.d.ts +47 -0
  72. package/components/editor/CaretState.js +1 -0
  73. package/components/editor/CaretUtilities.d.ts +5 -0
  74. package/components/editor/CaretUtilities.js +1 -0
  75. package/components/editor/CitationsEditor.svelte +42 -0
  76. package/components/editor/CitationsEditor.svelte.d.ts +17 -0
  77. package/components/editor/Clipboard.d.ts +3 -0
  78. package/components/editor/Clipboard.js +1 -0
  79. package/components/editor/Command.d.ts +18 -0
  80. package/components/editor/Command.js +1 -0
  81. package/components/editor/Commands.d.ts +3 -0
  82. package/components/editor/Commands.js +951 -0
  83. package/components/editor/ConfirmButton.svelte +43 -0
  84. package/components/editor/ConfirmButton.svelte.d.ts +18 -0
  85. package/components/editor/DefinitionEditor.svelte +28 -0
  86. package/components/editor/DefinitionEditor.svelte.d.ts +17 -0
  87. package/components/editor/EmbedEditor.svelte +87 -0
  88. package/components/editor/EmbedEditor.svelte.d.ts +17 -0
  89. package/components/editor/ImageChooser.svelte +70 -0
  90. package/components/editor/ImageChooser.svelte.d.ts +18 -0
  91. package/components/editor/InlineCodeEditor.svelte +9 -0
  92. package/components/editor/InlineCodeEditor.svelte.d.ts +17 -0
  93. package/components/editor/LabelEditor.svelte +23 -0
  94. package/components/editor/LabelEditor.svelte.d.ts +17 -0
  95. package/components/editor/LanguageEditor.svelte +11 -0
  96. package/components/editor/LanguageEditor.svelte.d.ts +17 -0
  97. package/components/editor/LinkEditor.svelte +75 -0
  98. package/components/editor/LinkEditor.svelte.d.ts +17 -0
  99. package/components/editor/Note.svelte +9 -0
  100. package/components/editor/Note.svelte.d.ts +23 -0
  101. package/components/editor/PositionEditor.svelte +14 -0
  102. package/components/editor/PositionEditor.svelte.d.ts +18 -0
  103. package/components/editor/QuoteEditor.svelte +5 -0
  104. package/components/editor/QuoteEditor.svelte.d.ts +17 -0
  105. package/components/editor/Switch.svelte +56 -0
  106. package/components/editor/Switch.svelte.d.ts +20 -0
  107. package/components/editor/TextEditor.svelte +193 -0
  108. package/components/editor/TextEditor.svelte.d.ts +23 -0
  109. package/components/editor/Toggle.svelte +38 -0
  110. package/components/editor/Toggle.svelte.d.ts +19 -0
  111. package/components/editor/Toolbar.svelte +202 -0
  112. package/components/editor/Toolbar.svelte.d.ts +20 -0
  113. package/components/editor/ToolbarGroup.svelte +41 -0
  114. package/components/editor/ToolbarGroup.svelte.d.ts +19 -0
  115. package/components/editor/ToolbarIcon.svelte +22 -0
  116. package/components/editor/ToolbarIcon.svelte.d.ts +16 -0
  117. package/components/editor/ToolbarSpacer.svelte +1 -0
  118. package/components/editor/ToolbarSpacer.svelte.d.ts +19 -0
  119. package/components/editor/URLEditor.svelte +15 -0
  120. package/components/editor/URLEditor.svelte.d.ts +18 -0
  121. package/components/editor/UndoState.d.ts +8 -0
  122. package/components/editor/UndoState.js +1 -0
  123. package/components/page/Acknowledgements.svelte +29 -0
  124. package/components/page/Acknowledgements.svelte.d.ts +14 -0
  125. package/components/page/Alert.svelte +11 -0
  126. package/components/page/Alert.svelte.d.ts +23 -0
  127. package/components/page/Authors.svelte +62 -0
  128. package/components/page/Authors.svelte.d.ts +20 -0
  129. package/components/page/BulkReferenceEditor.svelte +32 -0
  130. package/components/page/BulkReferenceEditor.svelte.d.ts +14 -0
  131. package/components/page/Chapter.svelte +301 -0
  132. package/components/page/Chapter.svelte.d.ts +18 -0
  133. package/components/page/ChapterContext.d.ts +10 -0
  134. package/components/page/ChapterContext.js +1 -0
  135. package/components/page/Contexts.d.ts +27 -0
  136. package/components/page/Contexts.js +21 -0
  137. package/components/page/DefinitionView.svelte +129 -0
  138. package/components/page/DefinitionView.svelte.d.ts +18 -0
  139. package/components/page/Description.svelte +22 -0
  140. package/components/page/Description.svelte.d.ts +14 -0
  141. package/components/page/Edition.svelte +106 -0
  142. package/components/page/Edition.svelte.d.ts +21 -0
  143. package/components/page/Glossary.svelte +61 -0
  144. package/components/page/Glossary.svelte.d.ts +14 -0
  145. package/components/page/Header.svelte +105 -0
  146. package/components/page/Header.svelte.d.ts +27 -0
  147. package/components/page/Index.svelte +117 -0
  148. package/components/page/Index.svelte.d.ts +14 -0
  149. package/components/page/Instructions.svelte +30 -0
  150. package/components/page/Instructions.svelte.d.ts +16 -0
  151. package/components/page/License.svelte +29 -0
  152. package/components/page/License.svelte.d.ts +14 -0
  153. package/components/page/Loading.svelte +13 -0
  154. package/components/page/Loading.svelte.d.ts +19 -0
  155. package/components/page/Media.svelte +76 -0
  156. package/components/page/Media.svelte.d.ts +14 -0
  157. package/components/page/MediaPreview.svelte +11 -0
  158. package/components/page/MediaPreview.svelte.d.ts +19 -0
  159. package/components/page/Outline.svelte +173 -0
  160. package/components/page/Outline.svelte.d.ts +19 -0
  161. package/components/page/Page.svelte +53 -0
  162. package/components/page/Page.svelte.d.ts +19 -0
  163. package/components/page/PossibleChapter.svelte +14 -0
  164. package/components/page/PossibleChapter.svelte.d.ts +14 -0
  165. package/components/page/PossibleReference.svelte +16 -0
  166. package/components/page/PossibleReference.svelte.d.ts +19 -0
  167. package/components/page/Print.svelte +8 -0
  168. package/components/page/Print.svelte.d.ts +14 -0
  169. package/components/page/Reference.svelte +145 -0
  170. package/components/page/Reference.svelte.d.ts +17 -0
  171. package/components/page/References.svelte +58 -0
  172. package/components/page/References.svelte.d.ts +14 -0
  173. package/components/page/Revisions.svelte +124 -0
  174. package/components/page/Revisions.svelte.d.ts +14 -0
  175. package/components/page/Search.svelte +108 -0
  176. package/components/page/Search.svelte.d.ts +14 -0
  177. package/components/page/Status.svelte +49 -0
  178. package/components/page/Status.svelte.d.ts +19 -0
  179. package/components/page/SubdomainEditor.svelte +30 -0
  180. package/components/page/SubdomainEditor.svelte.d.ts +14 -0
  181. package/components/page/TableOfContents.svelte +234 -0
  182. package/components/page/TableOfContents.svelte.d.ts +14 -0
  183. package/components/page/TableOfContentsImage.svelte +15 -0
  184. package/components/page/TableOfContentsImage.svelte.d.ts +16 -0
  185. package/components/page/TableOfContentsRow.svelte +57 -0
  186. package/components/page/TableOfContentsRow.svelte.d.ts +24 -0
  187. package/components/page/ThemeEditor.svelte +52 -0
  188. package/components/page/ThemeEditor.svelte.d.ts +14 -0
  189. package/components/page/ThemeEditorPreview.svelte +37 -0
  190. package/components/page/ThemeEditorPreview.svelte.d.ts +17 -0
  191. package/components/page/ThemeSetEditor.svelte +17 -0
  192. package/components/page/ThemeSetEditor.svelte.d.ts +18 -0
  193. package/components/page/ThemeVariableEditor.svelte +28 -0
  194. package/components/page/ThemeVariableEditor.svelte.d.ts +18 -0
  195. package/components/page/Unknown.svelte +29 -0
  196. package/components/page/Unknown.svelte.d.ts +16 -0
  197. package/components/page/margins.d.ts +2 -0
  198. package/components/page/margins.js +132 -0
  199. package/models/BookLoader.d.ts +2 -0
  200. package/models/BookLoader.js +67 -0
  201. package/models/Firebase.d.ts +4 -0
  202. package/models/Firebase.js +26 -0
  203. package/models/Firestore.d.ts +22 -0
  204. package/models/Firestore.js +194 -0
  205. package/models/book/Book.d.ts +60 -0
  206. package/models/book/Book.js +135 -0
  207. package/models/book/BookMedia.d.ts +23 -0
  208. package/models/book/BookMedia.js +102 -0
  209. package/models/book/Chapter.d.ts +70 -0
  210. package/models/book/Chapter.js +221 -0
  211. package/models/book/ChapterID.d.ts +10 -0
  212. package/models/book/ChapterID.js +10 -0
  213. package/models/book/Definition.d.ts +6 -0
  214. package/models/book/Definition.js +1 -0
  215. package/models/book/Edition.d.ts +111 -0
  216. package/models/book/Edition.js +466 -0
  217. package/models/book/Theme.d.ts +106 -0
  218. package/models/book/Theme.js +76 -0
  219. package/models/book/ThemeColors.d.ts +15 -0
  220. package/models/book/ThemeColors.js +1 -0
  221. package/models/chapter/AtomNode.d.ts +18 -0
  222. package/models/chapter/AtomNode.js +31 -0
  223. package/models/chapter/BlockNode.d.ts +21 -0
  224. package/models/chapter/BlockNode.js +117 -0
  225. package/models/chapter/BlocksNode.d.ts +50 -0
  226. package/models/chapter/BlocksNode.js +709 -0
  227. package/models/chapter/BlocksNode.test.d.ts +1 -0
  228. package/models/chapter/BlocksNode.test.js +302 -0
  229. package/models/chapter/Bookkeeping.d.ts +4 -0
  230. package/models/chapter/Bookkeeping.js +1 -0
  231. package/models/chapter/CalloutNode.d.ts +17 -0
  232. package/models/chapter/CalloutNode.js +36 -0
  233. package/models/chapter/Caret.d.ts +21 -0
  234. package/models/chapter/Caret.js +69 -0
  235. package/models/chapter/ChapterNode.d.ts +34 -0
  236. package/models/chapter/ChapterNode.js +80 -0
  237. package/models/chapter/CitationsNode.d.ts +16 -0
  238. package/models/chapter/CitationsNode.js +17 -0
  239. package/models/chapter/CodeNode.d.ts +30 -0
  240. package/models/chapter/CodeNode.js +64 -0
  241. package/models/chapter/CommentNode.d.ts +18 -0
  242. package/models/chapter/CommentNode.js +32 -0
  243. package/models/chapter/DefinitionNode.d.ts +12 -0
  244. package/models/chapter/DefinitionNode.js +20 -0
  245. package/models/chapter/Edit.d.ts +7 -0
  246. package/models/chapter/Edit.js +1 -0
  247. package/models/chapter/EmbedNode.d.ts +40 -0
  248. package/models/chapter/EmbedNode.js +89 -0
  249. package/models/chapter/ErrorNode.d.ts +17 -0
  250. package/models/chapter/ErrorNode.js +21 -0
  251. package/models/chapter/FootnoteNode.d.ts +18 -0
  252. package/models/chapter/FootnoteNode.js +29 -0
  253. package/models/chapter/FormatNode.d.ts +60 -0
  254. package/models/chapter/FormatNode.js +614 -0
  255. package/models/chapter/FormatNode.test.d.ts +1 -0
  256. package/models/chapter/FormatNode.test.js +54 -0
  257. package/models/chapter/InlineCodeNode.d.ts +12 -0
  258. package/models/chapter/InlineCodeNode.js +18 -0
  259. package/models/chapter/LabelNode.d.ts +17 -0
  260. package/models/chapter/LabelNode.js +22 -0
  261. package/models/chapter/LinkNode.d.ts +12 -0
  262. package/models/chapter/LinkNode.js +16 -0
  263. package/models/chapter/ListNode.d.ts +51 -0
  264. package/models/chapter/ListNode.js +370 -0
  265. package/models/chapter/ListNode.test.d.ts +1 -0
  266. package/models/chapter/ListNode.test.js +49 -0
  267. package/models/chapter/MetadataNode.d.ts +17 -0
  268. package/models/chapter/MetadataNode.js +24 -0
  269. package/models/chapter/Node.d.ts +36 -0
  270. package/models/chapter/Node.js +173 -0
  271. package/models/chapter/Node.test.d.ts +1 -0
  272. package/models/chapter/Node.test.js +27 -0
  273. package/models/chapter/ParagraphNode.d.ts +33 -0
  274. package/models/chapter/ParagraphNode.js +93 -0
  275. package/models/chapter/ParagraphNode.test.d.ts +1 -0
  276. package/models/chapter/ParagraphNode.test.js +21 -0
  277. package/models/chapter/Parser.d.ts +75 -0
  278. package/models/chapter/Parser.js +824 -0
  279. package/models/chapter/Parser.test.d.ts +1 -0
  280. package/models/chapter/Parser.test.js +25 -0
  281. package/models/chapter/Position.d.ts +2 -0
  282. package/models/chapter/Position.js +1 -0
  283. package/models/chapter/QuoteNode.d.ts +25 -0
  284. package/models/chapter/QuoteNode.js +61 -0
  285. package/models/chapter/ReferenceNode.d.ts +29 -0
  286. package/models/chapter/ReferenceNode.js +48 -0
  287. package/models/chapter/RootNode.d.ts +5 -0
  288. package/models/chapter/RootNode.js +1 -0
  289. package/models/chapter/RuleNode.d.ts +15 -0
  290. package/models/chapter/RuleNode.js +16 -0
  291. package/models/chapter/TableNode.d.ts +35 -0
  292. package/models/chapter/TableNode.js +176 -0
  293. package/models/chapter/TableNode.test.d.ts +1 -0
  294. package/models/chapter/TableNode.test.js +31 -0
  295. package/models/chapter/TextNode.d.ts +36 -0
  296. package/models/chapter/TextNode.js +136 -0
  297. package/models/chapter/TextNode.test.d.ts +1 -0
  298. package/models/chapter/TextNode.test.js +6 -0
  299. package/models/loadEdition.d.ts +5 -0
  300. package/models/loadEdition.js +16 -0
  301. package/package.json +189 -0
  302. package/util/getSubdomain.d.ts +3 -0
  303. package/util/getSubdomain.js +22 -0
  304. package/util/isMobile.d.ts +2 -0
  305. package/util/isMobile.js +6 -0
  306. package/util/mineReference.d.ts +3 -0
  307. package/util/mineReference.js +62 -0
  308. package/util/scrollToEyeLevel.d.ts +2 -0
  309. package/util/scrollToEyeLevel.js +5 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { test, expect } from 'vitest';
2
+ import Parser from "./Parser";
3
+ test("Empty chapter", () => {
4
+ expect(Parser.parseChapter(undefined, "").toBookdown()).toBe("");
5
+ });
6
+ test("Single paragraph", () => {
7
+ expect(Parser.parseChapter(undefined, "hello").toBookdown()).toBe("hello");
8
+ });
9
+ test("Peek", () => {
10
+ const p = new Parser(undefined, "# hello");
11
+ expect(p.peek()).toBe("#");
12
+ expect(p.read()).toBe("#");
13
+ expect(p.peek()).toBe(" ");
14
+ });
15
+ test("More", () => {
16
+ const p = new Parser(undefined, "#");
17
+ expect(p.more()).toBe(true);
18
+ const p2 = new Parser(undefined, "");
19
+ expect(p2.more()).toBe(false);
20
+ });
21
+ test("Whitespace", () => {
22
+ const p = new Parser(undefined, " Hi");
23
+ p.readWhitespace();
24
+ expect(p.peek()).toBe("H");
25
+ });
@@ -0,0 +1,2 @@
1
+ declare type Position = "|" | "<" | ">";
2
+ export default Position;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import type Node from "./Node";
2
+ import BlockNode from "./BlockNode";
3
+ import type Position from "./Position";
4
+ import FormatNode from "./FormatNode";
5
+ import BlocksNode from "./BlocksNode";
6
+ import type { CaretRange } from "./Caret";
7
+ export default class QuoteNode extends BlocksNode {
8
+ #private;
9
+ constructor(elements: BlockNode[], credit?: FormatNode, position?: Position);
10
+ getType(): string;
11
+ getCredit(): FormatNode;
12
+ getPosition(): Position;
13
+ getFormats(): FormatNode[];
14
+ toText(): string;
15
+ toBookdown(): string;
16
+ toHTML(): string;
17
+ getChildren(): Node[];
18
+ getParentOf(node: Node): Node | undefined;
19
+ copy(): this;
20
+ create(blocks: BlockNode[]): BlocksNode;
21
+ withChildReplaced(node: BlockNode | FormatNode, replacement: BlockNode | FormatNode | undefined): this | undefined;
22
+ withCredit(credit: FormatNode | undefined): QuoteNode;
23
+ withPosition(position: Position): QuoteNode;
24
+ withContentInRange(range: CaretRange): this | undefined;
25
+ }
@@ -0,0 +1,61 @@
1
+ import BlockNode from "./BlockNode";
2
+ import FormatNode from "./FormatNode";
3
+ import BlocksNode from "./BlocksNode";
4
+ import TextNode from "./TextNode";
5
+ export default class QuoteNode extends BlocksNode {
6
+ #credit;
7
+ #position;
8
+ constructor(elements, credit, position = "|") {
9
+ super(elements);
10
+ this.#position = position;
11
+ this.#credit = credit === undefined ? new FormatNode("", [new TextNode()]) : credit.withTextIfEmpty();
12
+ }
13
+ getType() { return "quote"; }
14
+ getCredit() { return this.#credit; }
15
+ getPosition() { return this.#position; }
16
+ getFormats() { return [this.#credit]; }
17
+ toText() {
18
+ return this.getBlocks().map(element => element.toText()).join(" ") + (this.#credit ? " " + this.#credit.toText() : "");
19
+ }
20
+ toBookdown() {
21
+ return `"\n${this.getBlocks().map(element => element.toBookdown()).join("\n\n")}\n"${this.#position === "|" ? "" : this.#position}${this.#credit ? this.#credit.toBookdown() : ""}`;
22
+ }
23
+ toHTML() { return `"${super.toHTML()}"<center>${this.#credit.toHTML()}</center>`; }
24
+ getChildren() { return [...this.getBlocks(), this.#credit]; }
25
+ getParentOf(node) {
26
+ if (node === this.#credit)
27
+ return this;
28
+ const creditParent = this.#credit.getParentOf(node);
29
+ if (creditParent !== undefined)
30
+ return creditParent;
31
+ return super.getParentOf(node);
32
+ }
33
+ copy() { return new QuoteNode(this.getBlocks().map(b => b.copy())); }
34
+ create(blocks) { return new QuoteNode(blocks, this.#credit, this.#position); }
35
+ withChildReplaced(node, replacement) {
36
+ // Replace the credit.
37
+ if (this.#credit === node && (replacement instanceof FormatNode || replacement === undefined))
38
+ return new QuoteNode(this.getBlocks(), replacement, this.#position);
39
+ // Replace a block.
40
+ if (replacement !== undefined && !(replacement instanceof BlockNode))
41
+ return;
42
+ const index = this.getBlocks().indexOf(node);
43
+ if (index < 0)
44
+ return;
45
+ // Make a new blocks node, assigning and re-parenting the replacement.
46
+ const blocks = replacement === undefined ?
47
+ [...this.getBlocks().slice(0, index), ...this.getBlocks().slice(index + 1)] :
48
+ [...this.getBlocks().slice(0, index), replacement, ...this.getBlocks().slice(index + 1)];
49
+ return new QuoteNode(blocks, this.#credit, this.#position);
50
+ }
51
+ withCredit(credit) { return new QuoteNode(this.getBlocks(), credit, this.#position); }
52
+ withPosition(position) { return new QuoteNode(this.getBlocks(), this.#credit, position); }
53
+ withContentInRange(range) {
54
+ // First get the blocks in range.
55
+ const newQuote = super.withContentInRange(range);
56
+ if (newQuote === undefined)
57
+ return;
58
+ const newCredit = this.getBlocks().find(b => b.contains(range.end.node)) !== undefined ? new FormatNode("", [new TextNode()]) : this.#credit.withContentInRange(range);
59
+ return newQuote.withNodeReplaced(this.#credit, newCredit);
60
+ }
61
+ }
@@ -0,0 +1,29 @@
1
+ import Node from "./Node";
2
+ export default class ReferenceNode extends Node {
3
+ readonly citationID: string;
4
+ readonly authors: string;
5
+ readonly year: string;
6
+ readonly title: string;
7
+ readonly source: string;
8
+ readonly url: string;
9
+ readonly summary: string;
10
+ readonly short: boolean;
11
+ constructor(citationID: string, authors?: string, year?: string, title?: string, source?: string, url?: string, summary?: string, short?: boolean);
12
+ getType(): string;
13
+ getChildren(): never[];
14
+ getParentOf(): Node | undefined;
15
+ toText(): string;
16
+ toBookdown(): string;
17
+ toHTML(): string;
18
+ toList(): string[];
19
+ copy(): this;
20
+ withChildReplaced(): undefined;
21
+ withContentInRange(): this | undefined;
22
+ withCitationID(id: string): ReferenceNode;
23
+ withAuthors(authors: string): ReferenceNode;
24
+ withYear(year: string): ReferenceNode;
25
+ withTitle(title: string): ReferenceNode;
26
+ withSource(source: string): ReferenceNode;
27
+ withURL(url: string): ReferenceNode;
28
+ withSummary(summary: string): ReferenceNode;
29
+ }
@@ -0,0 +1,48 @@
1
+ import Node from "./Node";
2
+ export default class ReferenceNode extends Node {
3
+ citationID;
4
+ authors;
5
+ year;
6
+ title;
7
+ source;
8
+ url;
9
+ summary;
10
+ short;
11
+ constructor(citationID, authors = "", year = "", title = "", source = "", url = "", summary = "", short = false) {
12
+ super();
13
+ this.citationID = citationID;
14
+ this.authors = authors;
15
+ this.year = year;
16
+ this.title = title;
17
+ this.source = source;
18
+ this.url = url;
19
+ this.summary = summary;
20
+ this.short = short;
21
+ }
22
+ getType() { return "reference"; }
23
+ getChildren() { return []; }
24
+ getParentOf() { return undefined; }
25
+ toText() { return this.authors + " " + this.year + " " + this.title + " " + this.source + (this.summary ? this.summary : ""); }
26
+ toBookdown() { return ""; }
27
+ toHTML() { return ""; }
28
+ toList() {
29
+ const list = [this.authors, this.year, this.title, this.source];
30
+ if (this.url)
31
+ list.push(this.url);
32
+ if (this.summary)
33
+ list.push(this.summary);
34
+ return list;
35
+ }
36
+ copy() {
37
+ return new ReferenceNode(this.citationID, this.authors, this.year, this.title, this.source, this.url, this.summary, this.short);
38
+ }
39
+ withChildReplaced() { return undefined; }
40
+ withContentInRange() { return this.copy(); }
41
+ withCitationID(id) { return new ReferenceNode(id, this.authors, this.year, this.title, this.source, this.url, this.summary, this.short); }
42
+ withAuthors(authors) { return new ReferenceNode(this.citationID, authors, this.year, this.title, this.source, this.url, this.summary, this.short); }
43
+ withYear(year) { return new ReferenceNode(this.citationID, this.authors, year, this.title, this.source, this.url, this.summary, this.short); }
44
+ withTitle(title) { return new ReferenceNode(this.citationID, this.authors, this.year, title, this.source, this.url, this.summary, this.short); }
45
+ withSource(source) { return new ReferenceNode(this.citationID, this.authors, this.year, this.title, source, this.url, this.summary, this.short); }
46
+ withURL(url) { return new ReferenceNode(this.citationID, this.authors, this.year, this.title, this.source, url, this.summary, this.short); }
47
+ withSummary(summary) { return new ReferenceNode(this.citationID, this.authors, this.year, this.title, this.source, this.url, summary, this.short); }
48
+ }
@@ -0,0 +1,5 @@
1
+ import type ChapterNode from "./ChapterNode";
2
+ import type EmbedNode from "./EmbedNode";
3
+ import type FormatNode from "./FormatNode";
4
+ declare type RootNode = ChapterNode | FormatNode | EmbedNode;
5
+ export default RootNode;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import type Node from "./Node";
2
+ import BlockNode from "./BlockNode";
3
+ export default class RuleNode extends BlockNode {
4
+ constructor();
5
+ getType(): string;
6
+ toText(): string;
7
+ toBookdown(): string;
8
+ toHTML(): string;
9
+ getParentOf(): Node | undefined;
10
+ getFormats(): never[];
11
+ getChildren(): Node[];
12
+ copy(): this;
13
+ withChildReplaced(): undefined;
14
+ withContentInRange(): this | undefined;
15
+ }
@@ -0,0 +1,16 @@
1
+ import BlockNode from "./BlockNode";
2
+ export default class RuleNode extends BlockNode {
3
+ constructor() {
4
+ super();
5
+ }
6
+ getType() { return "rule"; }
7
+ toText() { return ""; }
8
+ toBookdown() { return "-"; }
9
+ toHTML() { return `<hr/>`; }
10
+ getParentOf() { return undefined; }
11
+ getFormats() { return []; }
12
+ getChildren() { return []; }
13
+ copy() { return new RuleNode(); }
14
+ withChildReplaced() { return undefined; }
15
+ withContentInRange() { return this.copy(); }
16
+ }
@@ -0,0 +1,35 @@
1
+ import type Node from "./Node";
2
+ import type Position from "./Position";
3
+ import FormatNode from "./FormatNode";
4
+ import BlockNode from "./BlockNode";
5
+ import type { CaretRange } from "./Caret";
6
+ declare type Location = {
7
+ row: number;
8
+ column: number;
9
+ };
10
+ export default class TableNode extends BlockNode {
11
+ #private;
12
+ constructor(rows: FormatNode[][], position?: Position, caption?: FormatNode);
13
+ getType(): string;
14
+ getRows(): FormatNode[][];
15
+ getRowCount(): number;
16
+ getColumnCount(): number;
17
+ getPosition(): Position;
18
+ getCaption(): FormatNode;
19
+ getFormats(): FormatNode[];
20
+ toText(): string;
21
+ toBookdown(): string;
22
+ toHTML(): string;
23
+ getChildren(): FormatNode[];
24
+ locate(format: FormatNode): Location | undefined;
25
+ getCell(row: number, col: number): FormatNode | undefined;
26
+ getParentOf(node: Node): Node | undefined;
27
+ copy(): this;
28
+ withChildReplaced(node: FormatNode, replacement: FormatNode | undefined): this | undefined;
29
+ withNewRow(index: number): TableNode | undefined;
30
+ withoutRow(index: number): TableNode | undefined;
31
+ withoutColumn(index: number): TableNode | undefined;
32
+ withNewColumn(index: number): TableNode | undefined;
33
+ withContentInRange(range: CaretRange): this | undefined;
34
+ }
35
+ export {};
@@ -0,0 +1,176 @@
1
+ import FormatNode from "./FormatNode";
2
+ import TextNode from "./TextNode";
3
+ import BlockNode from "./BlockNode";
4
+ export default class TableNode extends BlockNode {
5
+ #rows;
6
+ #caption;
7
+ #position;
8
+ constructor(rows, position, caption) {
9
+ super();
10
+ this.#rows = rows.map(r => r.map(c => c.withTextIfEmpty()));
11
+ this.#position = position ? position : "|";
12
+ this.#caption = caption === undefined ? new FormatNode("", [new TextNode()]) : caption.withTextIfEmpty();
13
+ }
14
+ getType() { return "table"; }
15
+ getRows() { return this.#rows; }
16
+ getRowCount() { return this.#rows.length; }
17
+ getColumnCount() { return this.#rows.length === 0 ? 0 : this.#rows[0].length; }
18
+ getPosition() { return this.#position; }
19
+ getCaption() { return this.#caption; }
20
+ getFormats() {
21
+ const formats = [];
22
+ this.#rows.forEach(row => row.forEach(cell => { formats.push(cell); }));
23
+ return [...formats, this.#caption];
24
+ }
25
+ toText() {
26
+ return this.#rows.map(row => row.map(cell => cell.toText()).join(", ")).join(", ");
27
+ }
28
+ toBookdown() {
29
+ return this.#rows.map(row => `,${row.map(cell => cell.toBookdown()).join("|")}`).join("\n") +
30
+ `\n${this.#position === "|" ? "" : this.#position}${this.#caption ? "" + this.#caption.toBookdown() : ""}`;
31
+ }
32
+ toHTML() {
33
+ return `<table><tbody>${this.#rows.map(row => `<tr>${row.map(cell => `<td>${cell.toHTML()}</td>`).join("")}`).join("")}</tbody></table><center>${this.#caption.toHTML()}</center>`;
34
+ }
35
+ getChildren() {
36
+ const children = [];
37
+ this.#rows.forEach(row => row.forEach(cell => children.push(cell)));
38
+ children.push(this.#caption);
39
+ return children;
40
+ }
41
+ locate(format) {
42
+ for (let r = 0; r < this.#rows.length; r++) {
43
+ const row = this.#rows[r];
44
+ for (let c = 0; c < row.length; c++) {
45
+ if (row[c] === format)
46
+ return { row: r, column: c };
47
+ }
48
+ }
49
+ }
50
+ getCell(row, col) {
51
+ return row >= 0 && row < this.#rows.length && this.#rows.length > 0 && col >= 0 && col < this.#rows[0].length ?
52
+ this.#rows[row][col] :
53
+ undefined;
54
+ }
55
+ getParentOf(node) {
56
+ for (let r = 0; r < this.#rows.length; r++) {
57
+ const row = this.#rows[r];
58
+ for (let c = 0; c < row.length; c++) {
59
+ if (row[c] === node)
60
+ return this;
61
+ const parent = row[c].getParentOf(node);
62
+ if (parent !== undefined)
63
+ return parent;
64
+ }
65
+ }
66
+ return node === this.#caption ? this : this.#caption.getParentOf(node);
67
+ }
68
+ copy() {
69
+ const rows = [];
70
+ this.#rows.forEach(row => {
71
+ const cells = [];
72
+ row.forEach(cell => cells.push(cell.copy()));
73
+ rows.push(cells);
74
+ });
75
+ return new TableNode(rows, this.#position, this.#caption ? this.#caption.copy() : undefined);
76
+ }
77
+ withChildReplaced(node, replacement) {
78
+ // If it's just the caption, make a new node with the new credit.
79
+ if (node === this.#caption && (replacement instanceof FormatNode || replacement === undefined))
80
+ return new TableNode(this.#rows, this.#position, replacement);
81
+ // If it's a cell, update the cell.
82
+ if (node instanceof FormatNode && replacement instanceof FormatNode) {
83
+ // Does this table contain node we're replacing?
84
+ let updateRow = 0;
85
+ let updateColumn = -1;
86
+ for (; updateRow < this.#rows.length; updateRow++) {
87
+ updateColumn = this.#rows[updateRow].indexOf(node);
88
+ if (updateColumn >= 0)
89
+ break;
90
+ }
91
+ // Make new rows with the updated cell.
92
+ if (updateRow < this.#rows.length && updateColumn >= 0) {
93
+ const newRows = this.#rows.map(row => [...row]);
94
+ newRows[updateRow][updateColumn] = replacement;
95
+ return new TableNode(newRows, this.#position, this.#caption);
96
+ }
97
+ }
98
+ }
99
+ withNewRow(index) {
100
+ if (index < 0 || index > this.#rows.length)
101
+ return;
102
+ // Figure out how many columns the first row has, then create a row with that many columns
103
+ // and an empty FormatNode.
104
+ const columnCount = this.#rows.length === 0 ? 0 : this.#rows[0].length;
105
+ const column = [];
106
+ for (let c = 0; c < columnCount; c++)
107
+ column.push(new FormatNode("", [new TextNode()]));
108
+ const newRows = this.#rows.slice();
109
+ newRows.splice(index, 0, column);
110
+ return new TableNode(newRows, this.#position, this.#caption);
111
+ }
112
+ withoutRow(index) {
113
+ if (index < 0 || index >= this.#rows.length)
114
+ return;
115
+ const newRows = this.#rows.slice();
116
+ newRows.splice(index, 1);
117
+ return new TableNode(newRows, this.#position, this.#caption);
118
+ }
119
+ withoutColumn(index) {
120
+ if (index < 0 || index >= this.getColumnCount())
121
+ return;
122
+ const newRows = this.#rows.slice();
123
+ for (let r = 0; r < this.#rows.length; r++)
124
+ newRows[r].splice(index, 1);
125
+ return new TableNode(newRows, this.#position, this.#caption);
126
+ }
127
+ withNewColumn(index) {
128
+ if (this.#rows.length > 0 && (index < 0 || index > this.#rows[0].length))
129
+ return;
130
+ // Figure out how many rows the table has and add a column
131
+ const newRows = this.#rows.slice();
132
+ for (let r = 0; r < this.#rows.length; r++) {
133
+ newRows[r] = newRows[r].slice();
134
+ newRows[r].splice(index, 0, new FormatNode("", [new TextNode()]));
135
+ }
136
+ return new TableNode(newRows, this.#position, this.#caption);
137
+ }
138
+ withContentInRange(range) {
139
+ const containsStart = this.contains(range.start.node);
140
+ const containsEnd = this.contains(range.end.node);
141
+ if (!containsStart && !containsEnd)
142
+ return this.copy();
143
+ // If it contains the start, skip rows until we find the cell with the start node.
144
+ // If it contains the end, skip rows after we find the cell with the end node.
145
+ let r = 0, c = 0;
146
+ let foundStart = false;
147
+ let foundEnd = false;
148
+ let newRows = [];
149
+ for (; r < this.#rows.length; r++, c = 0) {
150
+ const row = this.#rows[r];
151
+ // Skip this row if this table contains the start node and we haven't found it and the row doesn't contain it
152
+ if (containsStart && !foundStart && row.find(r => r.contains(range.start.node)) === undefined)
153
+ continue;
154
+ // Stop if we found the end row.
155
+ if (containsEnd && foundEnd)
156
+ break;
157
+ newRows.push([]);
158
+ for (; c < row.length; c++) {
159
+ if (!foundStart && row[c].contains(range.start.node))
160
+ foundStart = true;
161
+ const newCell = row[c].withContentInRange(range);
162
+ if (newCell === undefined)
163
+ return;
164
+ newRows[newRows.length - 1].push((((containsStart && foundStart) || !containsStart) && ((containsEnd && !foundEnd) || !containsEnd)) ?
165
+ newCell :
166
+ new FormatNode("", [new TextNode()]));
167
+ if (row[c].contains(range.end.node))
168
+ foundEnd = true;
169
+ }
170
+ }
171
+ const newCaption = this.#caption.withContentInRange(range);
172
+ if (newCaption === undefined)
173
+ return;
174
+ return new TableNode(newRows, this.#position, newCaption);
175
+ }
176
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { test, expect } from 'vitest';
2
+ import FormatNode from "./FormatNode";
3
+ import TextNode from "./TextNode";
4
+ import TableNode from "./TableNode";
5
+ const tl = new FormatNode("", [new TextNode("tl")]);
6
+ const tr = new FormatNode("", [new TextNode("tr")]);
7
+ const bl = new FormatNode("", [new TextNode("bl")]);
8
+ const br = new FormatNode("", [new TextNode("br")]);
9
+ const caption = new FormatNode("", [new TextNode("hi")]);
10
+ const table = new TableNode([
11
+ [tl, tr],
12
+ [bl, br],
13
+ ], "|", caption);
14
+ test("Replace", () => {
15
+ expect(table.withChildReplaced(tl, new FormatNode("", [new TextNode("new")]))?.toBookdown())
16
+ .toBe(",new|tr\n,bl|br\nhi");
17
+ expect(table.withChildReplaced(caption, new FormatNode("", [new TextNode("bye")]))?.toBookdown())
18
+ .toBe(",tl|tr\n,bl|br\nbye");
19
+ });
20
+ test("Insert", () => {
21
+ expect(table.withNewRow(0)?.toBookdown())
22
+ .toBe(",|\n,tl|tr\n,bl|br\nhi");
23
+ expect(table.withNewColumn(0)?.toBookdown())
24
+ .toBe(",|tl|tr\n,|bl|br\nhi");
25
+ });
26
+ test("Delete", () => {
27
+ expect(table.withoutRow(0)?.toBookdown())
28
+ .toBe(",bl|br\nhi");
29
+ expect(table.withoutColumn(0)?.toBookdown())
30
+ .toBe(",tr\n,br\nhi");
31
+ });
@@ -0,0 +1,36 @@
1
+ import type Caret from "./Caret";
2
+ import type { CaretRange } from "./Caret";
3
+ import FormatNode from "./FormatNode";
4
+ import Node from "./Node";
5
+ import BlocksNode from "./BlocksNode";
6
+ import type RootNode from "./RootNode";
7
+ export default class TextNode extends Node {
8
+ #private;
9
+ constructor(text?: string);
10
+ getType(): string;
11
+ getText(): string;
12
+ getLength(): number;
13
+ getCaretPositionCount(): number;
14
+ getParentOf(): Node | undefined;
15
+ getFirstCaret(): Caret;
16
+ getLastCaret(): Caret;
17
+ toText(): string;
18
+ toBookdown(): string;
19
+ toHTML(): string;
20
+ getChildren(): never[];
21
+ copy(): this;
22
+ isItalic(root: Node): boolean;
23
+ isBold(root: Node): boolean;
24
+ isEmpty(): boolean;
25
+ getFormat(root: Node): FormatNode | undefined;
26
+ getFormatRoot(root: Node): FormatNode | undefined;
27
+ getBlocks(root: Node): BlocksNode | undefined;
28
+ getNextCaret(index: number): Caret | undefined;
29
+ getPreviousCaret(index: number): Caret | undefined;
30
+ nextWord(root: RootNode, index?: number): Caret;
31
+ previousWord(root: RootNode, index?: number): Caret;
32
+ withChildReplaced(): undefined;
33
+ withCharacterAt(char: string, index: number): TextNode | undefined;
34
+ withoutRange(range: CaretRange): TextNode | undefined;
35
+ withContentInRange(range: CaretRange): this | undefined;
36
+ }
@@ -0,0 +1,136 @@
1
+ import FormatNode from "./FormatNode";
2
+ import Node from "./Node";
3
+ import AtomNode from "./AtomNode";
4
+ import BlocksNode from "./BlocksNode";
5
+ export default class TextNode extends Node {
6
+ #text;
7
+ constructor(text = "") {
8
+ super();
9
+ this.#text = text;
10
+ }
11
+ getType() { return "text"; }
12
+ getText() { return this.#text; }
13
+ getLength() { return this.#text.length; }
14
+ getCaretPositionCount() { return this.#text.length; }
15
+ getParentOf() { return undefined; }
16
+ getFirstCaret() { return { node: this, index: 0 }; }
17
+ getLastCaret() { return { node: this, index: this.#text.length }; }
18
+ toText() { return this.#text; }
19
+ toBookdown() {
20
+ // Escape all characters with special meaning inside content nodes: _*`<^{~\[@% and :'s with no space after
21
+ return this.#text
22
+ .replace(/\\/g, '\\\\') // This has to go first! Otherwise it breaks all of the others below.
23
+ .replace(/_/g, '\\_')
24
+ .replace(/\*/g, '\\*')
25
+ .replace(/`/g, '\\`')
26
+ .replace(/</g, '\\<')
27
+ .replace(/\^/g, '\\^')
28
+ .replace(/{/g, '\\{')
29
+ .replace(/~/g, '\\~')
30
+ .replace(/ %/g, ' \\%')
31
+ .replace(/\[/g, '\\[')
32
+ .replace(/@/g, '\\@')
33
+ .replace(/(:)([a-z])/g, '\\:$2');
34
+ }
35
+ toHTML() {
36
+ return this.#text
37
+ .replace("<", "&lt;")
38
+ .replace(">", "&gt;")
39
+ .replace("&", "&amp;")
40
+ .replace('"', "&quot;")
41
+ .replace("'", "&#39;");
42
+ }
43
+ getChildren() { return []; }
44
+ copy() { return new TextNode(this.#text); }
45
+ isItalic(root) { return this.getAncestors(root).filter(p => p instanceof FormatNode && p.getFormat() === "_").length > 0; }
46
+ isBold(root) { return this.getAncestors(root).filter(p => p instanceof FormatNode && p.getFormat() === "*").length > 0; }
47
+ isEmpty() { return this.getLength() === 0; }
48
+ getFormat(root) {
49
+ return this.getClosestParentOfType(root, FormatNode);
50
+ }
51
+ getFormatRoot(root) {
52
+ // The format root is the highest format in the tree that contains this text.
53
+ // There is one exception to this, however: formats inside atom nodes shouldn't traverse
54
+ // past the atom node, since should be isolated from edits outside the atom.
55
+ const atom = this.getClosestParentMatching(root, p => p instanceof AtomNode);
56
+ if (atom && atom.getMeta() instanceof FormatNode)
57
+ return atom.getMeta();
58
+ // If this isn't in an atom, just return the highest format this is in.
59
+ return this.getFarthestParentMatching(root, p => p instanceof FormatNode);
60
+ }
61
+ getBlocks(root) {
62
+ return this.getClosestParentOfType(root, BlocksNode);
63
+ }
64
+ getNextCaret(index) {
65
+ return index < this.#text.length ? { node: this, index: index + 1 } : undefined;
66
+ }
67
+ getPreviousCaret(index) {
68
+ return index > 0 ? { node: this, index: index - 1 } : undefined;
69
+ }
70
+ nextWord(root, index) {
71
+ if (index === undefined)
72
+ index = 0;
73
+ // Search for the space after the given index.
74
+ let i = index + 1;
75
+ while (i < this.getLength() && this.#text.charAt(i).match(/\W/))
76
+ i++;
77
+ while (i < this.getLength() && this.#text.charAt(i).match(/\w/))
78
+ i++;
79
+ // If we found one in this node, return it.
80
+ if (i < this.getLength() || (i === this.getLength() && this.#text.charAt(i - 1).match(/\w/)))
81
+ return { node: this, index: i };
82
+ // Otherwise, find the next text node's next word boundary.
83
+ const format = this.getFormatRoot(root);
84
+ const formatTextNodes = format ? format.getTextNodes() : undefined;
85
+ const nextNode = root.getNextTextOrAtom(this);
86
+ const nextWord = nextNode?.nextWord(root);
87
+ // If there isn't one, just go to the end of this.
88
+ if (formatTextNodes && nextNode && this === formatTextNodes[formatTextNodes.length - 1])
89
+ return { node: nextNode, index: 0 };
90
+ else if (nextWord === undefined)
91
+ return { node: this, index: this.#text.length };
92
+ else
93
+ return nextWord;
94
+ }
95
+ previousWord(root, index) {
96
+ if (index === undefined)
97
+ index = this.#text.length;
98
+ // Scan left until we find a word character, then keep scanning until we reach a non-word character.
99
+ let i = index - 1;
100
+ while (i > 0 && this.#text.charAt(i - 1).match(/\W/))
101
+ i--;
102
+ while (i > 0 && this.#text.charAt(i - 1).match(/\w/))
103
+ i--;
104
+ // If we found one in this node, return it.
105
+ if (i > 0 || (i === 0 && this.#text.charAt(0).match(/\w/)))
106
+ return { node: this, index: i };
107
+ // Otherwise, find the next text node's next word boundary.
108
+ const format = this.getFormatRoot(root);
109
+ const formatTextNodes = format ? format.getTextNodes() : undefined;
110
+ const previousNode = root.getPreviousTextOrAtom(this);
111
+ const previousWord = previousNode?.previousWord(root);
112
+ // If there isn't one, just go to the end of this.
113
+ if (formatTextNodes && previousNode && this === formatTextNodes[0])
114
+ return { node: previousNode, index: previousNode instanceof TextNode ? previousNode.getLength() : 0 };
115
+ else if (previousWord === undefined)
116
+ return { node: this, index: 0 };
117
+ else
118
+ return previousWord;
119
+ }
120
+ withChildReplaced() { return undefined; }
121
+ withCharacterAt(char, index) {
122
+ if (index < 0 || index > this.#text.length)
123
+ return undefined;
124
+ return new TextNode(this.#text.slice(0, index) + char + this.#text.slice(index));
125
+ }
126
+ withoutRange(range) {
127
+ if (range.start.node !== this && range.end.node !== this)
128
+ return;
129
+ return new TextNode(this.#text.substring(0, range.start.index) + this.#text.substring(range.end.index));
130
+ }
131
+ withContentInRange(range) {
132
+ const includesStart = range.start.node === this;
133
+ const includesEnd = range.end.node === this;
134
+ return new TextNode(this.#text.substring(includesStart ? range.start.index : 0, includesEnd ? range.end.index : this.#text.length));
135
+ }
136
+ }