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.
- package/dist/components/Auth.svelte +37 -21
- package/dist/components/BookPreview.svelte +4 -4
- package/dist/components/BookPreview.svelte.d.ts +1 -1
- package/dist/components/Link.svelte +19 -12
- package/dist/components/app/Button.svelte +1 -8
- package/dist/components/app/Link.svelte +1 -7
- package/dist/components/app/Options.svelte +1 -6
- package/dist/components/app/TextInput.svelte +2 -8
- package/dist/components/chapter/Atom.svelte +2 -1
- package/dist/components/chapter/Atom.svelte.d.ts +1 -1
- package/dist/components/chapter/Block.svelte +9 -9
- package/dist/components/chapter/Block.svelte.d.ts +1 -1
- package/dist/components/chapter/Callout.svelte.d.ts +1 -1
- package/dist/components/chapter/CaptionedCode.svelte +1 -0
- package/dist/components/chapter/CaptionedCode.svelte.d.ts +1 -1
- package/dist/components/chapter/ChapterBody.svelte +5 -2
- package/dist/components/chapter/ChapterBody.svelte.d.ts +1 -1
- package/dist/components/chapter/ChapterBody.test.js +3 -3
- package/dist/components/chapter/Citations.svelte +17 -16
- package/dist/components/chapter/Citations.svelte.d.ts +1 -1
- package/dist/components/chapter/Code.svelte +8 -14
- package/dist/components/chapter/Comment.svelte +7 -12
- package/dist/components/chapter/Comment.svelte.d.ts +1 -1
- package/dist/components/chapter/Definition.svelte +8 -9
- package/dist/components/chapter/Definition.svelte.d.ts +1 -1
- package/dist/components/chapter/Embed.svelte +41 -61
- package/dist/components/chapter/Embed.svelte.d.ts +1 -1
- package/dist/components/chapter/ErrorMessage.svelte.d.ts +1 -1
- package/dist/components/chapter/Figure.svelte +14 -14
- package/dist/components/chapter/Figure.svelte.d.ts +4 -4
- package/dist/components/chapter/Footnote.svelte +11 -17
- package/dist/components/chapter/Footnote.svelte.d.ts +1 -1
- package/dist/components/chapter/Format.svelte +11 -11
- package/dist/components/chapter/Format.svelte.d.ts +1 -1
- package/dist/components/chapter/InlineCode.svelte.d.ts +1 -1
- package/dist/components/chapter/Label.svelte +1 -1
- package/dist/components/chapter/Label.svelte.d.ts +1 -1
- package/dist/components/chapter/LineBreak.svelte +1 -0
- package/dist/components/chapter/LineBreak.svelte.d.ts +1 -1
- package/dist/components/chapter/Link.svelte +14 -12
- package/dist/components/chapter/Link.svelte.d.ts +1 -1
- package/dist/components/chapter/List.svelte +1 -1
- package/dist/components/chapter/List.svelte.d.ts +1 -1
- package/dist/components/chapter/Marginal.svelte +36 -42
- package/dist/components/chapter/Paragraph.svelte +4 -4
- package/dist/components/chapter/Paragraph.svelte.d.ts +1 -1
- package/dist/components/chapter/Positioned.svelte +1 -0
- package/dist/components/chapter/Positioned.svelte.d.ts +1 -1
- package/dist/components/chapter/Python.svelte +42 -31
- package/dist/components/chapter/Python.svelte.d.ts +1 -1
- package/dist/components/chapter/Quote.svelte.d.ts +1 -1
- package/dist/components/chapter/Rule.svelte +1 -0
- package/dist/components/chapter/Rule.svelte.d.ts +1 -1
- package/dist/components/chapter/Table.svelte.d.ts +1 -1
- package/dist/components/chapter/Text.svelte +41 -50
- package/dist/components/chapter/Text.svelte.d.ts +1 -1
- package/dist/components/controls/DarkToggle.svelte +4 -4
- package/dist/components/editor/BookishEditor.svelte +605 -569
- package/dist/components/editor/BookishEditor.svelte.d.ts +1 -1
- package/dist/components/editor/CalloutEditor.svelte +1 -1
- package/dist/components/editor/CalloutEditor.svelte.d.ts +1 -1
- package/dist/components/editor/CaptionedCodeEditor.svelte +1 -1
- package/dist/components/editor/CaptionedCodeEditor.svelte.d.ts +1 -1
- package/dist/components/editor/CaretContext.d.ts +14 -14
- package/dist/components/editor/CaretState.d.ts +3 -3
- package/dist/components/editor/CaretView.svelte +8 -19
- package/dist/components/editor/CitationsEditor.svelte +10 -18
- package/dist/components/editor/CitationsEditor.svelte.d.ts +1 -1
- package/dist/components/editor/Clipboard.d.ts +1 -1
- package/dist/components/editor/Command.d.ts +1 -1
- package/dist/components/editor/Commands.js +19 -19
- package/dist/components/editor/ConfirmButton.svelte +1 -6
- package/dist/components/editor/DefinitionEditor.svelte +9 -7
- package/dist/components/editor/DefinitionEditor.svelte.d.ts +1 -1
- package/dist/components/editor/EmbedEditor.svelte +25 -34
- package/dist/components/editor/EmbedEditor.svelte.d.ts +1 -1
- package/dist/components/editor/ImageChooser.svelte +8 -7
- package/dist/components/editor/ImageChooser.svelte.d.ts +1 -1
- package/dist/components/editor/ImageThumbnail.svelte +21 -18
- package/dist/components/editor/InlineCodeEditor.svelte +1 -1
- package/dist/components/editor/InlineCodeEditor.svelte.d.ts +1 -1
- package/dist/components/editor/LabelEditor.svelte +2 -2
- package/dist/components/editor/LabelEditor.svelte.d.ts +1 -1
- package/dist/components/editor/LanguageEditor.svelte +8 -1
- package/dist/components/editor/LinkEditor.svelte +42 -44
- package/dist/components/editor/LinkEditor.svelte.d.ts +1 -1
- package/dist/components/editor/Permissions.svelte +23 -29
- package/dist/components/editor/PositionEditor.svelte +1 -1
- package/dist/components/editor/PositionEditor.svelte.d.ts +1 -1
- package/dist/components/editor/QuoteEditor.svelte +1 -1
- package/dist/components/editor/QuoteEditor.svelte.d.ts +1 -1
- package/dist/components/editor/Switch.svelte +5 -11
- package/dist/components/editor/TableEditor.svelte +1 -1
- package/dist/components/editor/TableEditor.svelte.d.ts +1 -1
- package/dist/components/editor/TextEditor.svelte +61 -58
- package/dist/components/editor/Toggle.svelte +16 -14
- package/dist/components/editor/Toolbar.svelte +81 -94
- package/dist/components/editor/UndoState.d.ts +2 -2
- package/dist/components/page/Acknowledgements.svelte +2 -8
- package/dist/components/page/Authors.svelte +12 -21
- package/dist/components/page/BookLoader.svelte +32 -34
- package/dist/components/page/BulkReferenceEditor.svelte +21 -41
- package/dist/components/page/Chapter.svelte +139 -114
- package/dist/components/page/Chapter.svelte.d.ts +1 -1
- package/dist/components/page/ChapterTitle.svelte +1 -1
- package/dist/components/page/Contexts.js +3 -1
- package/dist/components/page/DefinitionView.svelte +30 -42
- package/dist/components/page/DefinitionView.svelte.d.ts +1 -1
- package/dist/components/page/Description.svelte +2 -8
- package/dist/components/page/DownloadEPUB.svelte +57 -45
- package/dist/components/page/EditableHeader.svelte.d.ts +1 -1
- package/dist/components/page/Edition.svelte +44 -30
- package/dist/components/page/Edition.svelte.d.ts +1 -1
- package/dist/components/page/EditionLoader.svelte +101 -100
- package/dist/components/page/Editions.svelte +13 -13
- package/dist/components/page/Glossary.svelte +9 -10
- package/dist/components/page/Header.svelte +15 -28
- package/dist/components/page/Index.svelte +17 -18
- package/dist/components/page/License.svelte +2 -8
- package/dist/components/page/Media.svelte +7 -14
- package/dist/components/page/MediaPreview.svelte +14 -17
- package/dist/components/page/Outline.svelte +74 -66
- package/dist/components/page/Page.svelte +23 -17
- package/dist/components/page/PossibleChapter.svelte +2 -2
- package/dist/components/page/PossibleReference.svelte +1 -1
- package/dist/components/page/PossibleReference.svelte.d.ts +2 -2
- package/dist/components/page/Print.svelte +5 -8
- package/dist/components/page/Publish.svelte +34 -40
- package/dist/components/page/Reference.svelte +9 -11
- package/dist/components/page/Reference.svelte.d.ts +1 -1
- package/dist/components/page/References.svelte +1 -1
- package/dist/components/page/Search.svelte +39 -40
- package/dist/components/page/SubdomainEditor.svelte +11 -10
- package/dist/components/page/TableOfContents.svelte +17 -27
- package/dist/components/page/TableOfContentsImage.svelte +2 -2
- package/dist/components/page/TableOfContentsRow.svelte +9 -22
- package/dist/components/page/TableOfContentsRow.svelte.d.ts +2 -2
- package/dist/components/page/ThemeEditor.svelte +10 -17
- package/dist/components/page/ThemeEditorPreview.svelte +3 -6
- package/dist/components/page/ThemeEditorPreview.svelte.d.ts +1 -1
- package/dist/components/page/ThemeVariableEditor.svelte +6 -6
- package/dist/components/page/Unknown.svelte +1 -1
- package/dist/models/Firebase.js +8 -19
- package/dist/models/FirebaseAvailable.d.ts +9 -0
- package/dist/models/FirebaseAvailable.js +10 -0
- package/dist/models/book/BookMedia.d.ts +2 -1
- package/dist/models/book/BookMedia.js +33 -9
- 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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
void meta;
|
|
19
|
+
void number;
|
|
20
|
+
void letter;
|
|
21
|
+
void focused;
|
|
22
|
+
$chapter?.requestLayout();
|
|
29
23
|
});
|
|
30
24
|
</script>
|
|
31
25
|
|
|
@@ -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 =
|
|
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 !==
|
|
26
|
+
let showPlaceholder = $derived(node.isEmptyText() && placeholder !== undefined && (isChapterEditable() || isEditionEditable()));
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<svelte:element
|
|
@@ -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 ===
|
|
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}>
|
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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,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
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
43
|
+
hovered = true;
|
|
47
44
|
}
|
|
48
45
|
function handleExit() {
|
|
49
|
-
|
|
46
|
+
hovered = false;
|
|
50
47
|
}
|
|
51
48
|
onMount(() => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 =
|
|
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 ===
|
|
9
|
-
let classes = $derived(node.getLevel() === 0 ?
|
|
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 ===
|
|
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}
|
|
@@ -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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
36
|
+
return typeof window !== "undefined" && window.hasOwnProperty("Sk");
|
|
30
37
|
}
|
|
31
38
|
function reset() {
|
|
32
|
-
|
|
39
|
+
code = startCode;
|
|
33
40
|
}
|
|
34
41
|
function handleEdit(newCode) {
|
|
35
|
-
|
|
42
|
+
code = newCode;
|
|
36
43
|
}
|
|
37
44
|
function handleOutput(out) {
|
|
38
|
-
|
|
45
|
+
// Update the state to re-render.
|
|
46
|
+
output += out;
|
|
39
47
|
}
|
|
40
48
|
function start() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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,61 +1,52 @@
|
|
|
1
1
|
<script lang="ts">import { getChapter, isChapterEditable } from "../page/Contexts";
|
|
2
|
-
let { node, placeholder =
|
|
2
|
+
let { node, placeholder = undefined } = $props();
|
|
3
3
|
let context = getChapter();
|
|
4
4
|
let editable = isChapterEditable();
|
|
5
5
|
function replaceMultipleSpacesWithNonBreakingSpaces(original) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|