bookish-press 0.9.5 → 0.11.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 (148) hide show
  1. package/dist/components/Auth.svelte +37 -21
  2. package/dist/components/BookPreview.svelte +4 -4
  3. package/dist/components/BookPreview.svelte.d.ts +1 -1
  4. package/dist/components/Link.svelte +19 -12
  5. package/dist/components/app/Button.svelte +1 -8
  6. package/dist/components/app/Link.svelte +1 -7
  7. package/dist/components/app/Options.svelte +1 -6
  8. package/dist/components/app/TextInput.svelte +2 -8
  9. package/dist/components/chapter/Atom.svelte +2 -1
  10. package/dist/components/chapter/Atom.svelte.d.ts +1 -1
  11. package/dist/components/chapter/Block.svelte +9 -9
  12. package/dist/components/chapter/Block.svelte.d.ts +1 -1
  13. package/dist/components/chapter/Callout.svelte.d.ts +1 -1
  14. package/dist/components/chapter/CaptionedCode.svelte +1 -0
  15. package/dist/components/chapter/CaptionedCode.svelte.d.ts +1 -1
  16. package/dist/components/chapter/ChapterBody.svelte +5 -2
  17. package/dist/components/chapter/ChapterBody.svelte.d.ts +1 -1
  18. package/dist/components/chapter/ChapterBody.test.js +3 -3
  19. package/dist/components/chapter/Citations.svelte +17 -16
  20. package/dist/components/chapter/Citations.svelte.d.ts +1 -1
  21. package/dist/components/chapter/Code.svelte +8 -14
  22. package/dist/components/chapter/Comment.svelte +7 -12
  23. package/dist/components/chapter/Comment.svelte.d.ts +1 -1
  24. package/dist/components/chapter/Definition.svelte +8 -9
  25. package/dist/components/chapter/Definition.svelte.d.ts +1 -1
  26. package/dist/components/chapter/Embed.svelte +41 -61
  27. package/dist/components/chapter/Embed.svelte.d.ts +1 -1
  28. package/dist/components/chapter/ErrorMessage.svelte.d.ts +1 -1
  29. package/dist/components/chapter/Figure.svelte +14 -14
  30. package/dist/components/chapter/Figure.svelte.d.ts +4 -4
  31. package/dist/components/chapter/Footnote.svelte +11 -17
  32. package/dist/components/chapter/Footnote.svelte.d.ts +1 -1
  33. package/dist/components/chapter/Format.svelte +11 -11
  34. package/dist/components/chapter/Format.svelte.d.ts +1 -1
  35. package/dist/components/chapter/InlineCode.svelte.d.ts +1 -1
  36. package/dist/components/chapter/Label.svelte +1 -1
  37. package/dist/components/chapter/Label.svelte.d.ts +1 -1
  38. package/dist/components/chapter/LineBreak.svelte +1 -0
  39. package/dist/components/chapter/LineBreak.svelte.d.ts +1 -1
  40. package/dist/components/chapter/Link.svelte +14 -12
  41. package/dist/components/chapter/Link.svelte.d.ts +1 -1
  42. package/dist/components/chapter/List.svelte +1 -1
  43. package/dist/components/chapter/List.svelte.d.ts +1 -1
  44. package/dist/components/chapter/Marginal.svelte +36 -42
  45. package/dist/components/chapter/Paragraph.svelte +4 -4
  46. package/dist/components/chapter/Paragraph.svelte.d.ts +1 -1
  47. package/dist/components/chapter/Positioned.svelte +1 -0
  48. package/dist/components/chapter/Positioned.svelte.d.ts +1 -1
  49. package/dist/components/chapter/Python.svelte +42 -31
  50. package/dist/components/chapter/Python.svelte.d.ts +1 -1
  51. package/dist/components/chapter/Quote.svelte.d.ts +1 -1
  52. package/dist/components/chapter/Rule.svelte +1 -0
  53. package/dist/components/chapter/Rule.svelte.d.ts +1 -1
  54. package/dist/components/chapter/Table.svelte.d.ts +1 -1
  55. package/dist/components/chapter/Text.svelte +41 -50
  56. package/dist/components/chapter/Text.svelte.d.ts +1 -1
  57. package/dist/components/controls/DarkToggle.svelte +4 -4
  58. package/dist/components/editor/BookishEditor.svelte +605 -569
  59. package/dist/components/editor/BookishEditor.svelte.d.ts +1 -1
  60. package/dist/components/editor/CalloutEditor.svelte +1 -1
  61. package/dist/components/editor/CalloutEditor.svelte.d.ts +1 -1
  62. package/dist/components/editor/CaptionedCodeEditor.svelte +1 -1
  63. package/dist/components/editor/CaptionedCodeEditor.svelte.d.ts +1 -1
  64. package/dist/components/editor/CaretContext.d.ts +14 -14
  65. package/dist/components/editor/CaretState.d.ts +3 -3
  66. package/dist/components/editor/CaretView.svelte +8 -19
  67. package/dist/components/editor/CitationsEditor.svelte +10 -18
  68. package/dist/components/editor/CitationsEditor.svelte.d.ts +1 -1
  69. package/dist/components/editor/Clipboard.d.ts +1 -1
  70. package/dist/components/editor/Command.d.ts +1 -1
  71. package/dist/components/editor/Commands.js +19 -19
  72. package/dist/components/editor/ConfirmButton.svelte +1 -6
  73. package/dist/components/editor/DefinitionEditor.svelte +9 -7
  74. package/dist/components/editor/DefinitionEditor.svelte.d.ts +1 -1
  75. package/dist/components/editor/EmbedEditor.svelte +25 -34
  76. package/dist/components/editor/EmbedEditor.svelte.d.ts +1 -1
  77. package/dist/components/editor/ImageChooser.svelte +8 -7
  78. package/dist/components/editor/ImageChooser.svelte.d.ts +1 -1
  79. package/dist/components/editor/ImageThumbnail.svelte +21 -18
  80. package/dist/components/editor/InlineCodeEditor.svelte +1 -1
  81. package/dist/components/editor/InlineCodeEditor.svelte.d.ts +1 -1
  82. package/dist/components/editor/LabelEditor.svelte +2 -2
  83. package/dist/components/editor/LabelEditor.svelte.d.ts +1 -1
  84. package/dist/components/editor/LanguageEditor.svelte +8 -1
  85. package/dist/components/editor/LinkEditor.svelte +42 -44
  86. package/dist/components/editor/LinkEditor.svelte.d.ts +1 -1
  87. package/dist/components/editor/Permissions.svelte +23 -29
  88. package/dist/components/editor/PositionEditor.svelte +1 -1
  89. package/dist/components/editor/PositionEditor.svelte.d.ts +1 -1
  90. package/dist/components/editor/QuoteEditor.svelte +1 -1
  91. package/dist/components/editor/QuoteEditor.svelte.d.ts +1 -1
  92. package/dist/components/editor/Switch.svelte +5 -11
  93. package/dist/components/editor/TableEditor.svelte +1 -1
  94. package/dist/components/editor/TableEditor.svelte.d.ts +1 -1
  95. package/dist/components/editor/TextEditor.svelte +61 -58
  96. package/dist/components/editor/Toggle.svelte +16 -14
  97. package/dist/components/editor/Toolbar.svelte +81 -94
  98. package/dist/components/editor/UndoState.d.ts +2 -2
  99. package/dist/components/page/Acknowledgements.svelte +2 -8
  100. package/dist/components/page/Authors.svelte +12 -21
  101. package/dist/components/page/BookLoader.svelte +32 -34
  102. package/dist/components/page/BulkReferenceEditor.svelte +21 -41
  103. package/dist/components/page/Chapter.svelte +139 -114
  104. package/dist/components/page/Chapter.svelte.d.ts +1 -1
  105. package/dist/components/page/ChapterTitle.svelte +1 -1
  106. package/dist/components/page/Contexts.js +3 -1
  107. package/dist/components/page/DefinitionView.svelte +30 -42
  108. package/dist/components/page/DefinitionView.svelte.d.ts +1 -1
  109. package/dist/components/page/Description.svelte +2 -8
  110. package/dist/components/page/DownloadEPUB.svelte +57 -45
  111. package/dist/components/page/EditableHeader.svelte.d.ts +1 -1
  112. package/dist/components/page/Edition.svelte +44 -30
  113. package/dist/components/page/Edition.svelte.d.ts +1 -1
  114. package/dist/components/page/EditionLoader.svelte +101 -100
  115. package/dist/components/page/Editions.svelte +13 -13
  116. package/dist/components/page/Glossary.svelte +9 -10
  117. package/dist/components/page/Header.svelte +15 -28
  118. package/dist/components/page/Index.svelte +17 -18
  119. package/dist/components/page/License.svelte +2 -8
  120. package/dist/components/page/Media.svelte +7 -14
  121. package/dist/components/page/MediaPreview.svelte +14 -17
  122. package/dist/components/page/Outline.svelte +74 -66
  123. package/dist/components/page/Page.svelte +23 -17
  124. package/dist/components/page/PossibleChapter.svelte +2 -2
  125. package/dist/components/page/PossibleReference.svelte +1 -1
  126. package/dist/components/page/PossibleReference.svelte.d.ts +2 -2
  127. package/dist/components/page/Print.svelte +5 -8
  128. package/dist/components/page/Publish.svelte +34 -40
  129. package/dist/components/page/Reference.svelte +9 -11
  130. package/dist/components/page/Reference.svelte.d.ts +1 -1
  131. package/dist/components/page/References.svelte +1 -1
  132. package/dist/components/page/Search.svelte +39 -40
  133. package/dist/components/page/SubdomainEditor.svelte +11 -10
  134. package/dist/components/page/TableOfContents.svelte +17 -27
  135. package/dist/components/page/TableOfContentsImage.svelte +2 -2
  136. package/dist/components/page/TableOfContentsRow.svelte +9 -22
  137. package/dist/components/page/TableOfContentsRow.svelte.d.ts +2 -2
  138. package/dist/components/page/ThemeEditor.svelte +10 -17
  139. package/dist/components/page/ThemeEditorPreview.svelte +3 -6
  140. package/dist/components/page/ThemeEditorPreview.svelte.d.ts +1 -1
  141. package/dist/components/page/ThemeVariableEditor.svelte +6 -6
  142. package/dist/components/page/Unknown.svelte +1 -1
  143. package/dist/models/Firebase.js +8 -19
  144. package/dist/models/FirebaseAvailable.d.ts +9 -0
  145. package/dist/models/FirebaseAvailable.js +10 -0
  146. package/dist/models/book/BookMedia.d.ts +2 -1
  147. package/dist/models/book/BookMedia.js +33 -9
  148. package/package.json +18 -14
@@ -1,24 +1,24 @@
1
1
  <script lang="ts">import Format from "./Format.svelte";
2
2
  import Positioned from "./Positioned.svelte";
3
3
  import { getCaret, isChapterEditable } from "../page/Contexts";
4
- let {
5
- node,
6
- caption,
7
- credit = void 0,
8
- focusable = true,
9
- children
10
- } = $props();
4
+ let { node, caption, credit = undefined, focusable = true, children } = $props();
11
5
  let editable = isChapterEditable();
12
6
  let caret = getCaret();
13
7
  let selected = $derived($caret?.range?.start.node === node);
14
8
  function focusFigure() {
15
- if (!editable || $caret === void 0) return;
16
- const firstCaret = node.getFirstCaret();
17
- if (firstCaret)
18
- $caret.setCaret({
19
- start: firstCaret,
20
- end: firstCaret
21
- });
9
+ if (!editable || $caret === undefined) return;
10
+ // if (node instanceof EmbedNode) {
11
+ // return $caret.setCaret({
12
+ // start: { node, index: 0 },
13
+ // end: { node, index: 0 },
14
+ // });
15
+ // } else {
16
+ const firstCaret = node.getFirstCaret();
17
+ if (firstCaret) $caret.setCaret({
18
+ start: firstCaret,
19
+ end: firstCaret
20
+ });
21
+ // }
22
22
  }
23
23
  </script>
24
24
 
@@ -1,7 +1,7 @@
1
- import type TableNode from '../../models/chapter/TableNode';
2
- import type EmbedNode from '../../models/chapter/EmbedNode';
3
- import type CodeNode from '../../models/chapter/CodeNode';
4
- import type FormatNode from '../../models/chapter/FormatNode';
1
+ import type TableNode from '../../models/chapter/TableNode.js';
2
+ import type EmbedNode from '../../models/chapter/EmbedNode.js';
3
+ import type CodeNode from '../../models/chapter/CodeNode.js';
4
+ import type FormatNode from '../../models/chapter/FormatNode.js';
5
5
  interface Props {
6
6
  node: TableNode | EmbedNode | CodeNode;
7
7
  caption: FormatNode | undefined;
@@ -1,31 +1,25 @@
1
1
  <script lang="ts">import Atom from "./Atom.svelte";
2
2
  import Marginal from "./Marginal.svelte";
3
3
  import Format from "./Format.svelte";
4
- import {
5
- getChapter,
6
- getEdition,
7
- getCaret,
8
- getRoot
9
- } from "../page/Contexts";
4
+ import { getChapter, getEdition, getCaret, getRoot } from "../page/Contexts.js";
10
5
  let { node } = $props();
11
6
  let meta = $derived(node.getMeta());
12
7
  let caret = getCaret();
13
8
  let chapter = getChapter();
14
9
  let edition = getEdition();
15
10
  let root = getRoot();
11
+ // What footnote number is this?
16
12
  let number = $derived($root.getFootnotes().indexOf(node));
17
- let letter = $derived(
18
- number === void 0 ? void 0 : $edition?.getFootnoteSymbol(number)
19
- );
20
- let focused = $derived(
21
- $caret && $caret.range && $caret.range.start.node.hasAncestor($root, node)
22
- );
13
+ let letter = $derived(number === undefined ? undefined : $edition?.getFootnoteSymbol(number));
14
+ let focused = $derived($caret && $caret.range && $caret.range.start.node.hasAncestor($root, node));
15
+ // Whenever anything that affects this footnote's rendered size or
16
+ // position changes, request a chapter-wide marginal layout pass.
23
17
  $effect(() => {
24
- void meta;
25
- void number;
26
- void letter;
27
- void focused;
28
- $chapter?.requestLayout();
18
+ void meta;
19
+ void number;
20
+ void letter;
21
+ void focused;
22
+ $chapter?.requestLayout();
29
23
  });
30
24
  </script>
31
25
 
@@ -1,4 +1,4 @@
1
- import type FootnoteNode from '../../models/chapter/FootnoteNode';
1
+ import type FootnoteNode from '../../models/chapter/FootnoteNode.js';
2
2
  interface Props {
3
3
  node: FootnoteNode;
4
4
  }
@@ -1,12 +1,12 @@
1
- <script lang="ts">import FormatNode from "../../models/chapter/FormatNode";
2
- import CitationsNode from "../../models/chapter/CitationsNode";
3
- import CommentNode from "../../models/chapter/CommentNode";
4
- import DefinitionNode from "../../models/chapter/DefinitionNode";
5
- import FootnoteNode from "../../models/chapter/FootnoteNode";
6
- import InlineCodeNode from "../../models/chapter/InlineCodeNode";
7
- import LabelNode from "../../models/chapter/LabelNode";
8
- import LinkNode from "../../models/chapter/LinkNode";
9
- import TextNode from "../../models/chapter/TextNode";
1
+ <script lang="ts">import FormatNode from "../../models/chapter/FormatNode.js";
2
+ import CitationsNode from "../../models/chapter/CitationsNode.js";
3
+ import CommentNode from "../../models/chapter/CommentNode.js";
4
+ import DefinitionNode from "../../models/chapter/DefinitionNode.js";
5
+ import FootnoteNode from "../../models/chapter/FootnoteNode.js";
6
+ import InlineCodeNode from "../../models/chapter/InlineCodeNode.js";
7
+ import LabelNode from "../../models/chapter/LabelNode.js";
8
+ import LinkNode from "../../models/chapter/LinkNode.js";
9
+ import TextNode from "../../models/chapter/TextNode.js";
10
10
  import Problem from "./Problem.svelte";
11
11
  import Citations from "./Citations.svelte";
12
12
  import Definition from "./Definition.svelte";
@@ -20,10 +20,10 @@ import Format from "./Format.svelte";
20
20
  import { isChapterEditable, isEditionEditable } from "../page/Contexts";
21
21
  import LineBreakNode from "../../models/chapter/LineBreakNode";
22
22
  import LineBreak from "./LineBreak.svelte";
23
- let { node, placeholder = void 0 } = $props();
23
+ let { node, placeholder = undefined } = $props();
24
24
  let format = $derived(node.getFormat());
25
25
  let tag = $derived(format === "*" ? "strong" : format === "_" ? "em" : format === "^" ? "sup" : format === "v" ? "sub" : "span");
26
- let showPlaceholder = $derived(node.isEmptyText() && placeholder !== void 0 && (isChapterEditable() || isEditionEditable()));
26
+ let showPlaceholder = $derived(node.isEmptyText() && placeholder !== undefined && (isChapterEditable() || isEditionEditable()));
27
27
  </script>
28
28
 
29
29
  <svelte:element
@@ -1,4 +1,4 @@
1
- import FormatNode from '../../models/chapter/FormatNode';
1
+ import FormatNode from '../../models/chapter/FormatNode.js';
2
2
  import Format from './Format.svelte';
3
3
  interface Props {
4
4
  node: FormatNode;
@@ -1,4 +1,4 @@
1
- import type InlineCodeNode from '../../models/chapter/InlineCodeNode';
1
+ import type InlineCodeNode from '../../models/chapter/InlineCodeNode.js';
2
2
  interface Props {
3
3
  node: InlineCodeNode;
4
4
  }
@@ -5,7 +5,7 @@ let { node } = $props();
5
5
  let editable = isChapterEditable();
6
6
  let chapter = getChapter();
7
7
  let root = getRoot();
8
- let duplicate = $derived($root === void 0 ? false : $root.getLabels().filter((l) => l.getMeta() === node.getMeta()).length > 1);
8
+ let duplicate = $derived($root === undefined ? false : $root.getLabels().filter((l) => l.getMeta() === node.getMeta()).length > 1);
9
9
  </script>
10
10
 
11
11
  <Atom {node}>
@@ -1,4 +1,4 @@
1
- import type LabelNode from '../../models/chapter/LabelNode';
1
+ import type LabelNode from '../../models/chapter/LabelNode.js';
2
2
  interface Props {
3
3
  node: LabelNode;
4
4
  }
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">let { node } = $props();
2
+ export {};
2
3
  </script>
3
4
 
4
5
  <br data-nodeid={node.nodeID} />
@@ -1,4 +1,4 @@
1
- import type LineBreakNode from '../../models/chapter/LineBreakNode';
1
+ import type LineBreakNode from '../../models/chapter/LineBreakNode.js';
2
2
  interface Props {
3
3
  node: LineBreakNode;
4
4
  }
@@ -8,18 +8,20 @@ let edition = getEdition();
8
8
  let url = $derived(node.getMeta());
9
9
  let content = $derived(node.getText());
10
10
  function isInvalidChapterLink() {
11
- if (!url.startsWith("http")) {
12
- let chapter = url;
13
- if (url.indexOf(":") >= 0) {
14
- let parts = chapter.split(":");
15
- chapter = parts[0];
16
- } else if (url.indexOf("#") >= 0) {
17
- let parts = chapter.split("#");
18
- chapter = parts[0];
19
- }
20
- if (chapter !== "" && !$edition?.hasChapter(chapter)) return true;
21
- }
22
- return false;
11
+ // If it's internal, validate it.
12
+ if (!url.startsWith("http")) {
13
+ // Pull out any labels and just get the chapter name.
14
+ let chapter = url;
15
+ if (url.indexOf(":") >= 0) {
16
+ let parts = chapter.split(":");
17
+ chapter = parts[0];
18
+ } else if (url.indexOf("#") >= 0) {
19
+ let parts = chapter.split("#");
20
+ chapter = parts[0];
21
+ }
22
+ if (chapter !== "" && !$edition?.hasChapter(chapter)) return true;
23
+ }
24
+ return false;
23
25
  }
24
26
  </script>
25
27
 
@@ -1,4 +1,4 @@
1
- import type LinkNode from '../../models/chapter/LinkNode';
1
+ import type LinkNode from '../../models/chapter/LinkNode.js';
2
2
  interface Props {
3
3
  node: LinkNode;
4
4
  }
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">import List from "./List.svelte";
2
- import ListNode from "../../models/chapter/ListNode";
2
+ import ListNode from "../../models/chapter/ListNode.js";
3
3
  import Format from "./Format.svelte";
4
4
  let { node } = $props();
5
5
  </script>
@@ -1,5 +1,5 @@
1
1
  import List from './List.svelte';
2
- import ListNode from '../../models/chapter/ListNode';
2
+ import ListNode from '../../models/chapter/ListNode.js';
3
3
  interface Props {
4
4
  node: ListNode;
5
5
  }
@@ -1,60 +1,54 @@
1
- <script lang="ts">import { isMobile, watchMobile } from "../../util/isMobile";
1
+ <script lang="ts">import { isMobile, watchMobile } from "../../util/isMobile.js";
2
2
  import { onMount } from "svelte";
3
- import {
4
- getCaret,
5
- getChapter,
6
- isChapterEditable
7
- } from "../page/Contexts";
3
+ import { getCaret, getChapter, isChapterEditable } from "../page/Contexts.js";
8
4
  import AtomNode from "../../models/chapter/AtomNode";
9
5
  let { node, id, label, interactor, content } = $props();
10
6
  let hovered = $state(false);
11
7
  let chapter = getChapter();
12
8
  let editable = isChapterEditable();
13
9
  let caret = getCaret();
10
+ // If there's no marginal selected or this is different from the current selection, this is hidden.
14
11
  let selectedMarginal = $derived($chapter?.marginal);
15
- let isHidden = $derived(
16
- editable ? (
17
- // If editable, it's hidden if the caret is not inside the marginal content.
18
- !($caret?.range?.start.node === node || $caret?.range?.start.node.getAncestors($caret.root).some((ancestor) => ancestor === node))
19
- ) : (
20
- // If not editable, it's hidden if it's not selected.
21
- $selectedMarginal !== id
22
- )
23
- );
12
+ let isHidden = $derived(editable ? !($caret?.range?.start.node === node || $caret?.range?.start.node.getAncestors($caret.root).some((ancestor) => ancestor === node)) : // If not editable, it's hidden if it's not selected.
13
+ $selectedMarginal !== id);
24
14
  function toggle(event, interacted) {
25
- if (editable) {
26
- if (interacted) {
27
- if (editable && $caret && node instanceof AtomNode) {
28
- $caret.setCaret({
29
- start: { node, index: 0 },
30
- end: { node, index: 0 }
31
- });
32
- if (event) {
33
- event.stopPropagation();
34
- event.preventDefault();
35
- }
36
- }
37
- return;
38
- }
39
- }
40
- if ($chapter) {
41
- if (isMobile() && isHidden) $chapter.marginal.set(id);
42
- else $chapter.marginal.set(void 0);
43
- }
15
+ if (editable) {
16
+ if (interacted) {
17
+ if (editable && $caret && node instanceof AtomNode) {
18
+ // Select this so that the view stays focused.
19
+ $caret.setCaret({
20
+ start: {
21
+ node,
22
+ index: 0
23
+ },
24
+ end: {
25
+ node,
26
+ index: 0
27
+ }
28
+ });
29
+ if (event) {
30
+ event.stopPropagation();
31
+ event.preventDefault();
32
+ }
33
+ }
34
+ return;
35
+ }
36
+ }
37
+ if ($chapter) {
38
+ if (isMobile() && isHidden) $chapter.marginal.set(id);
39
+ else $chapter.marginal.set(undefined);
40
+ }
44
41
  }
45
42
  function handleEnter() {
46
- hovered = true;
43
+ hovered = true;
47
44
  }
48
45
  function handleExit() {
49
- hovered = false;
46
+ hovered = false;
50
47
  }
51
48
  onMount(() => {
52
- const mediaWatch = watchMobile();
53
- mediaWatch.addEventListener("change", () => toggle(void 0, false));
54
- return () => mediaWatch.removeEventListener(
55
- "change",
56
- () => toggle(void 0, false)
57
- );
49
+ const mediaWatch = watchMobile();
50
+ mediaWatch.addEventListener("change", () => toggle(undefined, false));
51
+ return () => mediaWatch.removeEventListener("change", () => toggle(undefined, false));
58
52
  });
59
53
  </script>
60
54
 
@@ -1,14 +1,14 @@
1
1
  <script lang="ts">import Format from "./Format.svelte";
2
2
  import { getChapter, getEdition, getRoot } from "../page/Contexts";
3
- let { node, placeholder = void 0 } = $props();
3
+ let { node, placeholder = undefined } = $props();
4
4
  let level = $derived(node.getLevel());
5
5
  let chapter = getChapter();
6
6
  let edition = getEdition();
7
7
  let root = getRoot();
8
- let id = $derived(node.getLevel() === 0 || $edition === void 0 ? void 0 : "header-" + ($root.getHeaders().indexOf(node) ?? ""));
9
- let classes = $derived(node.getLevel() === 0 ? void 0 : "page-header" + ($chapter && $chapter.highlightedID === id ? " bookish-content-highlight" : ""));
8
+ let id = $derived(node.getLevel() === 0 || $edition === undefined ? undefined : "header-" + ($root.getHeaders().indexOf(node) ?? ""));
9
+ let classes = $derived(node.getLevel() === 0 ? undefined : "page-header" + ($chapter && $chapter.highlightedID === id ? " bookish-content-highlight" : ""));
10
10
  let firstParagraph = $derived($root.getFirstParagraph());
11
- let dropcap = $derived(placeholder === void 0 && node === firstParagraph && /^[a-zA-Z]/.test(firstParagraph.getFirstTextNode().getText()));
11
+ let dropcap = $derived(placeholder === undefined && node === firstParagraph && /^[a-zA-Z]/.test(firstParagraph.getFirstTextNode().getText()));
12
12
  </script>
13
13
 
14
14
  {#if level === 0}
@@ -1,4 +1,4 @@
1
- import type ParagraphNode from '../../models/chapter/ParagraphNode';
1
+ import type ParagraphNode from '../../models/chapter/ParagraphNode.js';
2
2
  interface Props {
3
3
  node: ParagraphNode;
4
4
  placeholder?: string | undefined;
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">let { position, children } = $props();
2
+ export {};
2
3
  </script>
3
4
 
4
5
  <div
@@ -1,4 +1,4 @@
1
- import type Position from '../../models/chapter/Position';
1
+ import type Position from '../../models/chapter/Position.js';
2
2
  interface Props {
3
3
  position: Position;
4
4
  children?: import('svelte').Snippet;
@@ -3,56 +3,67 @@
3
3
  <script lang="ts">import { onMount, untrack } from "svelte";
4
4
  import Code from "./Code.svelte";
5
5
  let { node, startCode } = $props();
6
+ // Start with whatever code was passed in.
7
+ // Deliberately captures the initial code (untracked): it's the
8
+ // baseline that the reset button restores.
6
9
  let code = $state(untrack(() => startCode));
10
+ // What is currently rendered to the console (not always program output).
7
11
  let output = $state("");
12
+ // Whether the runtime is loaded. Initialized based on presence of global.
8
13
  let loaded = $state(skulptLoaded());
9
14
  let ref = $state(null);
10
15
  onMount(() => {
11
- if (document.getElementById("skulpt") === null) {
12
- let script = document.createElement("script");
13
- script.type = "text/javascript";
14
- script.async = true;
15
- script.id = "skulpt";
16
- script.onload = () => loaded = true;
17
- script.src = "https://cdn.jsdelivr.net/combine/npm/skulpt@1.2.0/dist/skulpt.min.js,npm/skulpt@1.2.0/dist/skulpt-stdlib.min.js";
18
- document.getElementsByTagName("head")[0].appendChild(script);
19
- }
16
+ // Dynamically load the script to minimize payload and reduce index complexity.
17
+ if (document.getElementById("skulpt") === null) {
18
+ let script = document.createElement("script");
19
+ script.type = "text/javascript";
20
+ script.async = true;
21
+ script.id = "skulpt";
22
+ script.onload = () => loaded = true;
23
+ script.src = "https://cdn.jsdelivr.net/combine/npm/skulpt@1.2.0/dist/skulpt.min.js,npm/skulpt@1.2.0/dist/skulpt-stdlib.min.js";
24
+ document.getElementsByTagName("head")[0].appendChild(script);
25
+ }
20
26
  });
27
+ // Always scroll to the bottom of the output when it changes.
21
28
  $effect(() => {
22
- void output;
23
- if (ref) {
24
- let outputView = ref.querySelector(".bookish-python-output");
25
- if (outputView) outputView.scrollTop = outputView.scrollHeight;
26
- }
29
+ void output;
30
+ if (ref) {
31
+ let outputView = ref.querySelector(".bookish-python-output");
32
+ if (outputView) outputView.scrollTop = outputView.scrollHeight;
33
+ }
27
34
  });
28
35
  function skulptLoaded() {
29
- return typeof window !== "undefined" && window.hasOwnProperty("Sk");
36
+ return typeof window !== "undefined" && window.hasOwnProperty("Sk");
30
37
  }
31
38
  function reset() {
32
- code = startCode;
39
+ code = startCode;
33
40
  }
34
41
  function handleEdit(newCode) {
35
- code = newCode;
42
+ code = newCode;
36
43
  }
37
44
  function handleOutput(out) {
38
- output += out;
45
+ // Update the state to re-render.
46
+ output += out;
39
47
  }
40
48
  function start() {
41
- if (skulptLoaded()) {
42
- output = "Executing...";
43
- setTimeout(() => execute(), 500);
44
- }
49
+ if (skulptLoaded()) {
50
+ // Communicate that executing is starting, wait a second, then execute the program.
51
+ // This is important in case the user wants to run it again; it provides confirmation that it was run again.
52
+ output = "Executing...";
53
+ setTimeout(() => execute(), 500);
54
+ }
45
55
  }
46
56
  function execute() {
47
- output = "";
48
- if (Sk) {
49
- Sk.configure({ output: handleOutput });
50
- try {
51
- if (Sk) Sk.importMainWithBody("<stdin>", false, code, true);
52
- } catch (error) {
53
- handleOutput(error);
54
- }
55
- }
57
+ // Reset the output, run the code, and update it's output.
58
+ output = "";
59
+ if (Sk) {
60
+ Sk.configure({ output: handleOutput });
61
+ try {
62
+ if (Sk) Sk.importMainWithBody("<stdin>", false, code, true);
63
+ } catch (error) {
64
+ handleOutput(error);
65
+ }
66
+ }
56
67
  }
57
68
  let lines = $derived(output.split("\n"));
58
69
  </script>
@@ -1,4 +1,4 @@
1
- import type CodeNode from '../../models/chapter/CodeNode';
1
+ import type CodeNode from '../../models/chapter/CodeNode.js';
2
2
  interface Props {
3
3
  node: CodeNode;
4
4
  startCode: string;
@@ -1,4 +1,4 @@
1
- import type QuoteNode from '../../models/chapter/QuoteNode';
1
+ import type QuoteNode from '../../models/chapter/QuoteNode.js';
2
2
  interface Props {
3
3
  node: QuoteNode;
4
4
  }
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">let { node } = $props();
2
+ export {};
2
3
  </script>
3
4
 
4
5
  <hr data-nodeid={node.nodeID} />
@@ -1,4 +1,4 @@
1
- import type RuleNode from '../../models/chapter/RuleNode';
1
+ import type RuleNode from '../../models/chapter/RuleNode.js';
2
2
  interface Props {
3
3
  node: RuleNode;
4
4
  }
@@ -1,4 +1,4 @@
1
- import type TableNode from '../../models/chapter/TableNode';
1
+ import type TableNode from '../../models/chapter/TableNode.js';
2
2
  interface Props {
3
3
  node: TableNode;
4
4
  }
@@ -1,61 +1,52 @@
1
1
  <script lang="ts">import { getChapter, isChapterEditable } from "../page/Contexts";
2
- let { node, placeholder = void 0 } = $props();
2
+ let { node, placeholder = undefined } = $props();
3
3
  let context = getChapter();
4
4
  let editable = isChapterEditable();
5
5
  function replaceMultipleSpacesWithNonBreakingSpaces(original) {
6
- let revisedText = "";
7
- for (let i = 0; i < original.length; i++) {
8
- let c = original.charAt(i);
9
- if (c === " " && (i === 0 || i === original.length - 1 || original.charAt(i - 1) === " "))
10
- revisedText += "\xA0";
11
- else revisedText += c;
12
- }
13
- return revisedText;
6
+ let revisedText = "";
7
+ for (let i = 0; i < original.length; i++) {
8
+ // If its a space and its at the beginning, the end, or the previous character was a space, make it a non-breaking space.
9
+ let c = original.charAt(i);
10
+ if (c === " " && (i === 0 || i === original.length - 1 || original.charAt(i - 1) === " ")) revisedText += "\xA0";
11
+ else revisedText += c;
12
+ }
13
+ return revisedText;
14
14
  }
15
+ // Manipulate text for rendering.
15
16
  let text = $derived.by(() => {
16
- let revised = replaceMultipleSpacesWithNonBreakingSpaces(
17
- node.getText()
18
- );
19
- if (revised.length > 0 && revised.charAt(revised.length - 1) === "\n")
20
- revised = revised + "\uFEFF";
21
- if (revised.length === 0) revised = placeholder ?? "\uFEFF";
22
- return revised;
17
+ // Replace any spaces at the beginning or end of the string with explicit non-breaking spaces to ensure that they render.
18
+ let revised = replaceMultipleSpacesWithNonBreakingSpaces(node.getText());
19
+ // If the text ends with a newline, render a non-breaking space at the end.
20
+ if (revised.length > 0 && revised.charAt(revised.length - 1) === "\n") revised = revised + "";
21
+ // If there's no text, render a non-breaking space, or a placeholder if provided.
22
+ if (revised.length === 0) revised = placeholder ?? "";
23
+ return revised;
23
24
  });
25
+ // Compute highlights, if highlighted
24
26
  let segments = $derived.by(() => {
25
- if (editable) return void 0;
26
- if (!$context || !$context.highlightedWord) return void 0;
27
- const query = $context.highlightedWord;
28
- const lowerText = text.toLowerCase();
29
- if (lowerText.indexOf(query) < 0) return void 0;
30
- const indices = [];
31
- for (let i = 0; i < text.length; ++i) {
32
- if (lowerText.substring(i, i + query.length) === query) {
33
- indices.push(i);
34
- }
35
- }
36
- const list = [];
37
- for (let i = 0; i < indices.length; i++) {
38
- list.push([
39
- text.substring(
40
- i === 0 ? 0 : indices[i - 1] + query.length,
41
- indices[i]
42
- ),
43
- false
44
- ]);
45
- list.push([
46
- text.substring(indices[i], indices[i] + query.length),
47
- true
48
- ]);
49
- }
50
- if (indices[indices.length - 1] < text.length - 1)
51
- list.push([
52
- text.substring(
53
- indices[indices.length - 1] + query.length,
54
- text.length
55
- ),
56
- false
57
- ]);
58
- return list;
27
+ if (editable) return undefined;
28
+ // Is there a query we're supposed to highlight? If so, highlight it.
29
+ if (!$context || !$context.highlightedWord) return undefined;
30
+ const query = $context.highlightedWord;
31
+ const lowerText = text.toLowerCase();
32
+ // Does this text contain the query? Highlight it.
33
+ if (lowerText.indexOf(query) < 0) return undefined;
34
+ // Find all the matches
35
+ const indices = [];
36
+ for (let i = 0; i < text.length; ++i) {
37
+ if (lowerText.substring(i, i + query.length) === query) {
38
+ indices.push(i);
39
+ }
40
+ }
41
+ // Go through each one and construct contents for the span to return.
42
+ const list = [];
43
+ for (let i = 0; i < indices.length; i++) {
44
+ // Push the text from the end of the last match or the start of the string.
45
+ list.push([text.substring(i === 0 ? 0 : indices[i - 1] + query.length, indices[i]), false]);
46
+ list.push([text.substring(indices[i], indices[i] + query.length), true]);
47
+ }
48
+ if (indices[indices.length - 1] < text.length - 1) list.push([text.substring(indices[indices.length - 1] + query.length, text.length), false]);
49
+ return list;
59
50
  });
60
51
  </script>
61
52
 
@@ -1,4 +1,4 @@
1
- import type TextNode from '../../models/chapter/TextNode';
1
+ import type TextNode from '../../models/chapter/TextNode.js';
2
2
  interface Props {
3
3
  node: TextNode;
4
4
  placeholder?: string | undefined;