bookish-press 0.9.4 → 0.10.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 (154) 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 +83 -41
  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 +33 -39
  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 +52 -48
  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/epub/build.d.ts +6 -0
  144. package/dist/epub/build.js +16 -3
  145. package/dist/epub/build.test.js +65 -0
  146. package/dist/epub/images.d.ts +7 -5
  147. package/dist/epub/images.js +15 -10
  148. package/dist/epub/images.test.js +8 -6
  149. package/dist/epub/package.js +25 -3
  150. package/dist/models/FirebaseAvailable.d.ts +9 -0
  151. package/dist/models/FirebaseAvailable.js +10 -0
  152. package/dist/models/book/BookMedia.d.ts +2 -1
  153. package/dist/models/book/BookMedia.js +33 -9
  154. package/package.json +18 -14
@@ -4,38 +4,54 @@ import { writable } from "svelte/store";
4
4
  import { auth } from "../models/Firebase";
5
5
  import { USER } from "./page/Contexts";
6
6
  let { children } = $props();
7
+ // Track the user and user loading status in state
7
8
  let loading = $state(true);
9
+ // The store that tracks the auth context.
8
10
  const authentication = writable({
9
- user: null,
10
- loading: true,
11
- login,
12
- logout
11
+ user: null,
12
+ loading: true,
13
+ login,
14
+ logout
13
15
  });
16
+ // Login using Firebase's password-less email verification
14
17
  async function login(email) {
15
- if (auth) {
16
- const actionCodeSettings = {
17
- url: location.origin + "/confirm",
18
- handleCodeInApp: true
19
- };
20
- await sendSignInLinkToEmail(auth, email, actionCodeSettings);
21
- window.localStorage.setItem("email", email);
22
- }
18
+ // If connected to firebase...
19
+ if (auth) {
20
+ const actionCodeSettings = {
21
+ url: location.origin + "/confirm",
22
+ handleCodeInApp: true
23
+ };
24
+ // Ask Firebase to log in with the given email
25
+ await sendSignInLinkToEmail(auth, email, actionCodeSettings);
26
+ // Remember the email in local storage so we don't have to ask for it again
27
+ // after returning to the link above.
28
+ window.localStorage.setItem("email", email);
29
+ }
23
30
  }
24
31
  function updateAuth(user) {
25
- authentication.set({ user, loading, login, logout });
32
+ authentication.set({
33
+ user,
34
+ loading,
35
+ login,
36
+ logout
37
+ });
26
38
  }
39
+ // Logout using Firebase's authentication framework.
27
40
  function logout() {
28
- if (auth) {
29
- return auth.signOut().then(() => updateAuth(null));
30
- }
41
+ if (auth) {
42
+ return auth.signOut().then(() => updateAuth(null));
43
+ }
31
44
  }
45
+ // On mount, subscribe to authentication changes.
32
46
  if (auth) {
33
- const unsubscribe = auth.onAuthStateChanged((user) => {
34
- loading = false;
35
- updateAuth(user);
36
- });
37
- onDestroy(unsubscribe);
47
+ const unsubscribe = auth.onAuthStateChanged((user) => {
48
+ loading = false;
49
+ updateAuth(user);
50
+ });
51
+ // On unmount, stop subscribing.
52
+ onDestroy(unsubscribe);
38
53
  }
54
+ // Expose the state and the login/logout functionality.
39
55
  setContext(USER, authentication);
40
56
  </script>
41
57
 
@@ -1,5 +1,5 @@
1
- <script lang="ts">import EmbedNode from "../models/chapter/EmbedNode";
2
- import Parser from "../models/chapter/Parser";
1
+ <script lang="ts">import EmbedNode from "../models/chapter/EmbedNode.js";
2
+ import Parser from "../models/chapter/Parser.js";
3
3
  import ChapterBody from "./chapter/ChapterBody.svelte";
4
4
  import Embed from "./chapter/Embed.svelte";
5
5
  import Link from "./app/Link.svelte";
@@ -10,8 +10,8 @@ let authors = $derived(book.getAuthors());
10
10
  let description = $derived(book.getDescription());
11
11
  let subdomain = $derived(book.getSubdomain());
12
12
  let title = $derived(book.getTitle());
13
- let embed = $derived(cover === null ? null : Parser.parseEmbed(void 0, cover));
14
- let url = $derived(subdomain === null || write ? refID === void 0 ? "" : (write ? "/write/" : "/") + refID : `/${subdomain}`);
13
+ let embed = $derived(cover === null ? null : Parser.parseEmbed(undefined, cover));
14
+ let url = $derived(subdomain === null || write ? refID === undefined ? "" : (write ? "/write/" : "/") + refID : `/${subdomain}`);
15
15
  </script>
16
16
 
17
17
  <article class="book-preview">
@@ -1,4 +1,4 @@
1
- import type Book from '../models/book/Book';
1
+ import type Book from '../models/book/Book.js';
2
2
  interface Props {
3
3
  book: Book;
4
4
  write?: boolean;
@@ -2,18 +2,25 @@
2
2
  let { to, linked = true, children } = $props();
3
3
  let link = $state();
4
4
  function scroll(event) {
5
- if (link === void 0) return;
6
- const hash = new URL(link.href).hash;
7
- if (hash === "") return;
8
- event.preventDefault();
9
- const anchorId = new URL(link.href).hash.replace("#", "");
10
- const anchor = document.getElementById(anchorId);
11
- if (anchor === null) return;
12
- window.scrollTo({
13
- top: anchor.offsetTop,
14
- behavior: "smooth"
15
- });
16
- history.pushState({}, "", hash);
5
+ // No element? Bail.
6
+ if (link === undefined) return;
7
+ // No hash? No scroll.
8
+ const hash = new URL(link.href).hash;
9
+ if (hash === "") return;
10
+ // Prevent the browser from scrolling.
11
+ event.preventDefault();
12
+ // Get the anchor
13
+ const anchorId = new URL(link.href).hash.replace("#", "");
14
+ const anchor = document.getElementById(anchorId);
15
+ // No anchor? Bail.
16
+ if (anchor === null) return;
17
+ // Scroll to the anchor.
18
+ window.scrollTo({
19
+ top: anchor.offsetTop,
20
+ behavior: "smooth"
21
+ });
22
+ // Update the hash (without scrolling)
23
+ history.pushState({}, "", hash);
17
24
  }
18
25
  </script>
19
26
 
@@ -1,11 +1,4 @@
1
- <script lang="ts">let {
2
- type = null,
3
- tooltip,
4
- disabled = false,
5
- command,
6
- class: classes,
7
- children
8
- } = $props();
1
+ <script lang="ts">let { type = null, tooltip, disabled = false, command, class: classes, children } = $props();
9
2
  </script>
10
3
 
11
4
  <button
@@ -1,11 +1,5 @@
1
1
  <script lang="ts">import { page } from "$app/state";
2
- let {
3
- to,
4
- before = void 0,
5
- title = void 0,
6
- external = false,
7
- children
8
- } = $props();
2
+ let { to, before = undefined, title = undefined, external = false, children } = $props();
9
3
  let at = $derived(to === page.url.pathname);
10
4
  </script>
11
5
 
@@ -1,9 +1,4 @@
1
- <script lang="ts">let {
2
- options,
3
- changed,
4
- value = $bindable(),
5
- label
6
- } = $props();
1
+ <script lang="ts">let { options, changed, value = $bindable(), label } = $props();
7
2
  </script>
8
3
 
9
4
  <select bind:value onchange={() => changed(value)} aria-label={label}>
@@ -1,13 +1,7 @@
1
- <script lang="ts">let {
2
- text = $bindable(""),
3
- autocomplete = null,
4
- type,
5
- placeholder,
6
- disabled
7
- } = $props();
1
+ <script lang="ts">let { text = $bindable(""), autocomplete = null, type, placeholder, disabled } = $props();
8
2
  let input = $state();
9
3
  export function value() {
10
- return text;
4
+ return text;
11
5
  }
12
6
  </script>
13
7
 
@@ -1,6 +1,7 @@
1
- <script lang="ts">import { getCaret } from "../page/Contexts";
1
+ <script lang="ts">import { getCaret } from "../page/Contexts.js";
2
2
  let { node, children } = $props();
3
3
  let caret = getCaret();
4
+ // Is the caret on this link?
4
5
  let selected = $derived($caret && $caret.range && $caret.range.start.node === node);
5
6
  </script>
6
7
 
@@ -1,4 +1,4 @@
1
- import type AtomNode from '../../models/chapter/AtomNode';
1
+ import type AtomNode from '../../models/chapter/AtomNode.js';
2
2
  interface Props {
3
3
  node: AtomNode<any>;
4
4
  children?: import('svelte').Snippet;
@@ -1,12 +1,12 @@
1
- <script lang="ts">import CalloutNode from "../../models/chapter/CalloutNode";
2
- import CodeNode from "../../models/chapter/CodeNode";
3
- import EmbedNode from "../../models/chapter/EmbedNode";
4
- import ErrorNode from "../../models/chapter/ErrorNode";
5
- import ListNode from "../../models/chapter/ListNode";
6
- import ParagraphNode from "../../models/chapter/ParagraphNode";
7
- import QuoteNode from "../../models/chapter/QuoteNode";
8
- import RuleNode from "../../models/chapter/RuleNode";
9
- import TableNode from "../../models/chapter/TableNode";
1
+ <script lang="ts">import CalloutNode from "../../models/chapter/CalloutNode.js";
2
+ import CodeNode from "../../models/chapter/CodeNode.js";
3
+ import EmbedNode from "../../models/chapter/EmbedNode.js";
4
+ import ErrorNode from "../../models/chapter/ErrorNode.js";
5
+ import ListNode from "../../models/chapter/ListNode.js";
6
+ import ParagraphNode from "../../models/chapter/ParagraphNode.js";
7
+ import QuoteNode from "../../models/chapter/QuoteNode.js";
8
+ import RuleNode from "../../models/chapter/RuleNode.js";
9
+ import TableNode from "../../models/chapter/TableNode.js";
10
10
  import Callout from "./Callout.svelte";
11
11
  import CaptionedCode from "./CaptionedCode.svelte";
12
12
  import Embed from "./Embed.svelte";
@@ -1,4 +1,4 @@
1
- import type BlockNode from '../../models/chapter/BlockNode';
1
+ import type BlockNode from '../../models/chapter/BlockNode.js';
2
2
  interface Props {
3
3
  node: BlockNode;
4
4
  }
@@ -1,4 +1,4 @@
1
- import type CalloutNode from '../../models/chapter/CalloutNode';
1
+ import type CalloutNode from '../../models/chapter/CalloutNode.js';
2
2
  interface Props {
3
3
  node: CalloutNode;
4
4
  }
@@ -7,6 +7,7 @@ import Button from "../app/Button.svelte";
7
7
  let { node } = $props();
8
8
  let language = $derived(node.getLanguage());
9
9
  let editable = isChapterEditable();
10
+ // const languages = [ "C", "C++", "CSS", "Go", "HTML", "Java", "JavaScript", "Markdown", "Plaintext", "Python", "TypeScript" ];
10
11
  let editing = $state(true);
11
12
  </script>
12
13
 
@@ -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
  }
@@ -1,4 +1,4 @@
1
- <script lang="ts">import ParagraphNode from "../../models/chapter/ParagraphNode";
1
+ <script lang="ts">import ParagraphNode from "../../models/chapter/ParagraphNode.js";
2
2
  import { writable } from "svelte/store";
3
3
  import { setContext, untrack } from "svelte";
4
4
  import { ROOT } from "../page/Contexts";
@@ -8,10 +8,13 @@ import Problem from "./Problem.svelte";
8
8
  let { node, placeholder = "", editable = false } = $props();
9
9
  let errors = $derived(node.getErrors());
10
10
  let blocks = $derived(node.getBlocks());
11
+ // Make this chapter node available to all children in a reactive store.
12
+ // The store is deliberately seeded with the initial node (untracked);
13
+ // the effect below keeps it in sync when the prop changes.
11
14
  let root = writable(untrack(() => node));
12
15
  setContext(ROOT, root);
13
16
  $effect.pre(() => {
14
- root.set(node);
17
+ root.set(node);
15
18
  });
16
19
  </script>
17
20
 
@@ -1,4 +1,4 @@
1
- import type ChapterNode from '../../models/chapter/ChapterNode';
1
+ import type ChapterNode from '../../models/chapter/ChapterNode.js';
2
2
  interface Props {
3
3
  node: ChapterNode;
4
4
  placeholder?: string;
@@ -2,10 +2,10 @@ import { beforeAll, expect, test, vi } from 'vitest';
2
2
  import { render } from '@testing-library/svelte';
3
3
  import { tick } from 'svelte';
4
4
  import { writable } from 'svelte/store';
5
- import Parser from '../../models/chapter/Parser';
6
- import Chapter from '../../models/book/Chapter';
5
+ import Parser from '../../models/chapter/Parser.js';
6
+ import Chapter from '../../models/book/Chapter.js';
7
7
  import ChapterBody from './ChapterBody.svelte';
8
- import { CARET, CHAPTER, EDITION } from '../page/Contexts';
8
+ import { CARET, CHAPTER, EDITION } from '../page/Contexts.js';
9
9
  // The page state only exists inside a running SvelteKit app; Contexts.ts
10
10
  // reads it to decide whether a route is editable.
11
11
  vi.mock('$app/state', () => ({
@@ -7,23 +7,24 @@ let { node } = $props();
7
7
  let chapter = getChapter();
8
8
  let edition = getEdition();
9
9
  let root = getRoot();
10
- let citations = $derived(
11
- node.getMeta().sort((a, b) => {
12
- if ($root === void 0) return 0;
13
- let aNumber = $root.getCitationNumber(a);
14
- let bNumber = $root.getCitationNumber(b);
15
- if (aNumber === null) {
16
- if (bNumber === null) return 0;
17
- else return 1;
18
- } else {
19
- if (bNumber === null) return -1;
20
- else return aNumber - bNumber;
21
- }
22
- })
23
- );
10
+ // Sort citations numerically, however they're numbered.
11
+ let citations = $derived(node.getMeta().sort((a, b) => {
12
+ if ($root === undefined) return 0;
13
+ let aNumber = $root.getCitationNumber(a);
14
+ let bNumber = $root.getCitationNumber(b);
15
+ if (aNumber === null) {
16
+ if (bNumber === null) return 0;
17
+ else return 1;
18
+ } else {
19
+ if (bNumber === null) return -1;
20
+ else return aNumber - bNumber;
21
+ }
22
+ }));
23
+ // Whenever anything that affects these citations' rendered size or
24
+ // position changes, request a chapter-wide marginal layout pass.
24
25
  $effect(() => {
25
- void citations;
26
- $chapter?.requestLayout();
26
+ void citations;
27
+ $chapter?.requestLayout();
27
28
  });
28
29
  </script>
29
30
 
@@ -1,4 +1,4 @@
1
- import type CitationsNode from '../../models/chapter/CitationsNode';
1
+ import type CitationsNode from '../../models/chapter/CitationsNode.js';
2
2
  interface Props {
3
3
  node: CitationsNode;
4
4
  }
@@ -1,22 +1,16 @@
1
1
  <script lang="ts">import Prism from "./ExtendedPrism";
2
- let {
3
- editable,
4
- inline,
5
- nodeID,
6
- edited = void 0,
7
- language = void 0,
8
- code
9
- } = $props();
2
+ let { editable, inline, nodeID, edited = undefined, language = undefined, code } = $props();
10
3
  let element = $state(null);
4
+ // Whenever the code or language changes, re-highlight the element.
11
5
  $effect(() => {
12
- void code;
13
- void language;
14
- if (element) {
15
- Prism.highlightElement(element);
16
- }
6
+ void code;
7
+ void language;
8
+ if (element) {
9
+ Prism.highlightElement(element);
10
+ }
17
11
  });
18
12
  function handleChange() {
19
- if (element && edited) edited(element.innerText);
13
+ if (element && edited) edited(element.innerText);
20
14
  }
21
15
  </script>
22
16
 
@@ -1,12 +1,7 @@
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
- isChapterEditable,
7
- getCaret,
8
- getRoot
9
- } from "../page/Contexts";
4
+ import { getChapter, isChapterEditable, getCaret, getRoot } from "../page/Contexts.js";
10
5
  import Icon from "../editor/Icon.svelte";
11
6
  import CommentIcon from "../editor/icons/comment.svg?raw";
12
7
  let { node } = $props();
@@ -14,13 +9,13 @@ let chapter = getChapter();
14
9
  let editable = isChapterEditable();
15
10
  let root = getRoot();
16
11
  let caret = getCaret();
17
- let focused = $derived(
18
- $caret && $caret.range && node.contains($caret.range.start.node)
19
- );
12
+ let focused = $derived($caret && $caret.range && node.contains($caret.range.start.node));
13
+ // Whenever anything that affects this comment's rendered size or
14
+ // position changes, request a chapter-wide marginal layout pass.
20
15
  $effect(() => {
21
- void node;
22
- void focused;
23
- $chapter?.requestLayout();
16
+ void node;
17
+ void focused;
18
+ $chapter?.requestLayout();
24
19
  });
25
20
  </script>
26
21
 
@@ -1,4 +1,4 @@
1
- import type CommentNode from '../../models/chapter/CommentNode';
1
+ import type CommentNode from '../../models/chapter/CommentNode.js';
2
2
  interface Props {
3
3
  node: CommentNode;
4
4
  }
@@ -1,25 +1,24 @@
1
- <script lang="ts">import Parser from "../../models/chapter/Parser";
1
+ <script lang="ts">import Parser from "../../models/chapter/Parser.js";
2
2
  import Text from "./Text.svelte";
3
3
  import Format from "./Format.svelte";
4
4
  import Marginal from "./Marginal.svelte";
5
5
  import Problem from "./Problem.svelte";
6
- import {
7
- getChapter,
8
- getEdition,
9
- isChapterEditable
10
- } from "../page/Contexts";
6
+ import { getChapter, getEdition, isChapterEditable } from "../page/Contexts";
11
7
  let { node } = $props();
12
8
  let editable = isChapterEditable();
13
9
  let edition = getEdition();
14
10
  let chapter = getChapter();
11
+ // Find the definition.
15
12
  let glossaryID = $derived(node.getMeta());
16
13
  let phrase = $derived(node.getText());
17
14
  let glossary = $derived($edition?.getGlossary() ?? {});
18
15
  let entry = $derived(glossary[glossaryID]);
16
+ // Whenever anything that affects this definition's rendered size or
17
+ // position changes, request a chapter-wide marginal layout pass.
19
18
  $effect(() => {
20
- void phrase;
21
- void entry;
22
- $chapter?.requestLayout();
19
+ void phrase;
20
+ void entry;
21
+ $chapter?.requestLayout();
23
22
  });
24
23
  </script>
25
24
 
@@ -1,4 +1,4 @@
1
- import type DefinitionNode from '../../models/chapter/DefinitionNode';
1
+ import type DefinitionNode from '../../models/chapter/DefinitionNode.js';
2
2
  interface Props {
3
3
  node: DefinitionNode;
4
4
  }
@@ -1,87 +1,67 @@
1
- <script lang="ts">import { storage } from "../../models/Firebase";
2
- import {
3
- getCaret,
4
- getBook,
5
- getBase,
6
- isEditionEditable
7
- } from "../page/Contexts";
1
+ <script lang="ts">import { getCaret, getBook, getBase, isEditionEditable } from "../page/Contexts.js";
8
2
  import Figure from "./Figure.svelte";
9
3
  import { getContext } from "svelte";
10
- let {
11
- node,
12
- editable = true,
13
- placeholder = "",
14
- imageOnly = false
15
- } = $props();
4
+ let { node, editable = true, placeholder = "", imageOnly = false } = $props();
16
5
  let url = $derived(node.getURL());
17
6
  let description = $derived(node.getDescription());
18
7
  let caret = getCaret();
19
8
  let book = getBook();
20
9
  let base = getBase();
21
10
  let editionEditable = isEditionEditable();
11
+ // Bookish editor should have passed down a way to set the active editor.
22
12
  let claimCaret = getContext("claimeditor");
23
13
  let dragging = $state(false);
24
- let dragFeedback = $state(void 0);
14
+ let dragFeedback = $state(undefined);
25
15
  let imageError = $state(false);
26
16
  $effect.pre(() => {
27
- if (node) imageError = false;
17
+ if (node) imageError = false;
28
18
  });
29
19
  function handleDrop(event) {
30
- event.preventDefault();
31
- if (event.dataTransfer?.items) {
32
- const imageFiles = [...event.dataTransfer.items].filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter(
33
- (file) => file !== null && /.*\.(jpg|jpeg|png)/i.test(file.name)
34
- );
35
- if (imageFiles.length === 0)
36
- dragFeedback = "Only JPEG or PNG images are allowed.";
37
- else if (imageFiles.length > 1)
38
- dragFeedback = "Only one image at a time please!";
39
- else {
40
- dragFeedback = "Uploading\u2026";
41
- const file = imageFiles[0];
42
- const media = $book?.getMedia();
43
- if (file === null) return;
44
- if (storage === void 0) return;
45
- if (media === void 0) return;
46
- media.upload(
47
- file,
48
- (progress) => dragFeedback = `${progress}% done`,
49
- (error) => {
50
- dragFeedback = error;
51
- dragging = false;
52
- },
53
- (url2, thumbnails) => {
54
- $caret?.edit(
55
- node,
56
- node.withURLs(url2, thumbnails).withDescription("")
57
- );
58
- dragging = false;
59
- dragFeedback = void 0;
60
- }
61
- );
62
- }
63
- }
20
+ event.preventDefault();
21
+ if (event.dataTransfer?.items) {
22
+ // Get the files and convert them to file opens, then filter by allowed file extensions
23
+ const imageFiles = [...event.dataTransfer.items].filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter((file) => file !== null && /.*\.(jpg|jpeg|png)/i.test(file.name));
24
+ if (imageFiles.length === 0) dragFeedback = "Only JPEG or PNG images are allowed.";
25
+ else if (imageFiles.length > 1) dragFeedback = "Only one image at a time please!";
26
+ else {
27
+ dragFeedback = "Uploading…";
28
+ const file = imageFiles[0];
29
+ const media = $book?.getMedia();
30
+ if (file === null) return;
31
+ if (media === undefined) return;
32
+ media.upload(file, (progress) => dragFeedback = `${progress}% done`, (error) => {
33
+ dragFeedback = error;
34
+ dragging = false;
35
+ }, (url, thumbnails) => {
36
+ // Upload completed successfully, now we can get the download URL
37
+ $caret?.edit(node, node.withURLs(url, thumbnails).withDescription(""));
38
+ dragging = false;
39
+ dragFeedback = undefined;
40
+ });
41
+ }
42
+ }
64
43
  }
65
44
  function handleDrag(event) {
66
- event?.preventDefault();
67
- dragging = true;
68
- dragFeedback = "Drop to upload\u2026";
69
- if (claimCaret) claimCaret();
45
+ event?.preventDefault();
46
+ dragging = true;
47
+ dragFeedback = "Drop to upload…";
48
+ // If we have a way to claim the caret, claim it, since we want to be able to make an edit with the active caret.
49
+ if (claimCaret) claimCaret();
70
50
  }
71
51
  function handleDragLeave(event) {
72
- if (event.target !== event.currentTarget) return;
73
- event?.preventDefault();
74
- dragging = false;
75
- dragFeedback = void 0;
52
+ if (event.target !== event.currentTarget) return;
53
+ event?.preventDefault();
54
+ dragging = false;
55
+ dragFeedback = undefined;
76
56
  }
77
57
  function handleLoad() {
78
- imageError = false;
58
+ imageError = false;
79
59
  }
80
60
  function handleError() {
81
- imageError = true;
61
+ imageError = true;
82
62
  }
83
63
  function handleClick() {
84
- if (claimCaret) claimCaret();
64
+ if (claimCaret) claimCaret();
85
65
  }
86
66
  </script>
87
67
 
@@ -134,7 +114,7 @@ function handleClick() {
134
114
  frameBorder="0"
135
115
  allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
136
116
  allowFullScreen
137
- ></iframe>
117
+ ></iframe>
138
118
  </article>
139
119
  {:else}
140
120
  <img
@@ -1,4 +1,4 @@
1
- import type EmbedNode from '../../models/chapter/EmbedNode';
1
+ import type EmbedNode from '../../models/chapter/EmbedNode.js';
2
2
  interface Props {
3
3
  node: EmbedNode;
4
4
  editable?: boolean;
@@ -1,4 +1,4 @@
1
- import type ErrorNode from '../../models/chapter/ErrorNode';
1
+ import type ErrorNode from '../../models/chapter/ErrorNode.js';
2
2
  interface Props {
3
3
  node: ErrorNode;
4
4
  }