@lapis-notes/lapis-plugin-docs 0.0.0-stage → 0.2.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 (197) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +188 -2
  3. package/dist/docs/DocsBlockMarkdownEditor.svelte +37 -0
  4. package/dist/docs/DocsBlockMarkdownEditor.svelte.d.ts +13 -0
  5. package/dist/docs/DocsBlockRefPicker.svelte +75 -0
  6. package/dist/docs/DocsBlockRefPicker.svelte.d.ts +11 -0
  7. package/dist/docs/DocsBookEditor.svelte +141 -0
  8. package/dist/docs/DocsBookEditor.svelte.d.ts +10 -0
  9. package/dist/docs/DocsBookNavigation.svelte +838 -0
  10. package/dist/docs/DocsBookNavigation.svelte.d.ts +11 -0
  11. package/dist/docs/DocsBooksColumn.svelte +82 -0
  12. package/dist/docs/DocsBooksColumn.svelte.d.ts +10 -0
  13. package/dist/docs/DocsCardBackMatter.svelte +32 -0
  14. package/dist/docs/DocsCardBackMatter.svelte.d.ts +10 -0
  15. package/dist/docs/DocsCardBlockExtra.svelte +43 -0
  16. package/dist/docs/DocsCardBlockExtra.svelte.d.ts +9 -0
  17. package/dist/docs/DocsCardPreviewFlip.svelte +143 -0
  18. package/dist/docs/DocsCardPreviewFlip.svelte.d.ts +17 -0
  19. package/dist/docs/DocsCardPreviewStack.svelte +139 -0
  20. package/dist/docs/DocsCardPreviewStack.svelte.d.ts +13 -0
  21. package/dist/docs/DocsCardPrompt.svelte +33 -0
  22. package/dist/docs/DocsCardPrompt.svelte.d.ts +8 -0
  23. package/dist/docs/DocsCardReviewAnswer.svelte +45 -0
  24. package/dist/docs/DocsCardReviewAnswer.svelte.d.ts +8 -0
  25. package/dist/docs/DocsCardTagChips.svelte +19 -0
  26. package/dist/docs/DocsCardTagChips.svelte.d.ts +7 -0
  27. package/dist/docs/DocsCardsPanel.svelte +232 -0
  28. package/dist/docs/DocsCardsPanel.svelte.d.ts +17 -0
  29. package/dist/docs/DocsCardsReview.css +1050 -0
  30. package/dist/docs/DocsChapterFooter.svelte +248 -0
  31. package/dist/docs/DocsChapterFooter.svelte.d.ts +11 -0
  32. package/dist/docs/DocsChapterScrollRing.svelte +101 -0
  33. package/dist/docs/DocsChapterScrollRing.svelte.d.ts +6 -0
  34. package/dist/docs/DocsDisplayPanel.css +627 -0
  35. package/dist/docs/DocsDisplayPanel.svelte +78 -0
  36. package/dist/docs/DocsDisplayPanel.svelte.d.ts +16 -0
  37. package/dist/docs/DocsEditorPanel.css +325 -0
  38. package/dist/docs/DocsEditorPanel.svelte +570 -0
  39. package/dist/docs/DocsEditorPanel.svelte.d.ts +22 -0
  40. package/dist/docs/DocsFlipCard.svelte +68 -0
  41. package/dist/docs/DocsFlipCard.svelte.d.ts +8 -0
  42. package/dist/docs/DocsFolderDirectoryList.svelte +206 -0
  43. package/dist/docs/DocsFolderDirectoryList.svelte.d.ts +14 -0
  44. package/dist/docs/DocsFolderPanel.css +357 -0
  45. package/dist/docs/DocsFolderPanel.svelte +102 -0
  46. package/dist/docs/DocsFolderPanel.svelte.d.ts +17 -0
  47. package/dist/docs/DocsLinksPanel.css +61 -0
  48. package/dist/docs/DocsLinksPanel.svelte +55 -0
  49. package/dist/docs/DocsLinksPanel.svelte.d.ts +11 -0
  50. package/dist/docs/DocsMarkdownField.svelte +93 -0
  51. package/dist/docs/DocsMarkdownField.svelte.d.ts +11 -0
  52. package/dist/docs/DocsMarkdownPreview.svelte +278 -0
  53. package/dist/docs/DocsMarkdownPreview.svelte.d.ts +17 -0
  54. package/dist/docs/DocsReviewPanel.svelte +63 -0
  55. package/dist/docs/DocsReviewPanel.svelte.d.ts +11 -0
  56. package/dist/docs/DocsRichBlock.svelte +291 -0
  57. package/dist/docs/DocsRichBlock.svelte.d.ts +13 -0
  58. package/dist/docs/DocsRichDisplay.svelte +316 -0
  59. package/dist/docs/DocsRichDisplay.svelte.d.ts +14 -0
  60. package/dist/docs/DocsSlidesPreview.css +243 -0
  61. package/dist/docs/DocsSlidesPreview.svelte +206 -0
  62. package/dist/docs/DocsSlidesPreview.svelte.d.ts +10 -0
  63. package/dist/docs/DocsWorkspace.css +570 -0
  64. package/dist/docs/DocsWorkspace.svelte +831 -0
  65. package/dist/docs/DocsWorkspace.svelte.d.ts +48 -0
  66. package/dist/docs/DocsWorkspaceToolbar.svelte +211 -0
  67. package/dist/docs/DocsWorkspaceToolbar.svelte.d.ts +27 -0
  68. package/dist/docs/candidates/ChoiceMenu.svelte +114 -0
  69. package/dist/docs/candidates/ChoiceMenu.svelte.d.ts +10 -0
  70. package/dist/docs/candidates/FlipCardDeck.svelte +817 -0
  71. package/dist/docs/candidates/FlipCardDeck.svelte.d.ts +37 -0
  72. package/dist/docs/candidates/MarkdownFloatingToc.svelte +313 -0
  73. package/dist/docs/candidates/MarkdownFloatingToc.svelte.d.ts +14 -0
  74. package/dist/docs/candidates/SearchableChoicePicker.css +161 -0
  75. package/dist/docs/candidates/SearchableChoicePicker.svelte +208 -0
  76. package/dist/docs/candidates/SearchableChoicePicker.svelte.d.ts +19 -0
  77. package/dist/docs/candidates/StructuredTable.svelte +98 -0
  78. package/dist/docs/candidates/StructuredTable.svelte.d.ts +8 -0
  79. package/dist/docs/candidates/StructuredTableEditor.svelte +374 -0
  80. package/dist/docs/candidates/StructuredTableEditor.svelte.d.ts +12 -0
  81. package/dist/docs/candidates/TagEditor.svelte +31 -0
  82. package/dist/docs/candidates/TagEditor.svelte.d.ts +14 -0
  83. package/dist/docs/candidates/WorkspaceHistoryControls.svelte +112 -0
  84. package/dist/docs/candidates/WorkspaceHistoryControls.svelte.d.ts +8 -0
  85. package/dist/docs/candidates/markdown-floating-toc.css +151 -0
  86. package/dist/docs/candidates/markdown-floating-toc.d.ts +1 -0
  87. package/dist/docs/candidates/markdown-floating-toc.js +1 -0
  88. package/dist/docs/docs-anki-import.d.ts +34 -0
  89. package/dist/docs/docs-anki-import.js +516 -0
  90. package/dist/docs/docs-book-view-model.d.ts +9 -0
  91. package/dist/docs/docs-book-view-model.js +71 -0
  92. package/dist/docs/docs-card-answer-markdown.d.ts +1 -0
  93. package/dist/docs/docs-card-answer-markdown.js +63 -0
  94. package/dist/docs/docs-card-display.d.ts +10 -0
  95. package/dist/docs/docs-card-display.js +18 -0
  96. package/dist/docs/docs-display-focus.d.ts +7 -0
  97. package/dist/docs/docs-display-focus.js +42 -0
  98. package/dist/docs/docs-document-surface.d.ts +7 -0
  99. package/dist/docs/docs-document-surface.js +25 -0
  100. package/dist/docs/docs-folder-directory.d.ts +21 -0
  101. package/dist/docs/docs-folder-directory.js +56 -0
  102. package/dist/docs/docs-link-mode.d.ts +16 -0
  103. package/dist/docs/docs-link-mode.js +94 -0
  104. package/dist/docs/docs-workspace-helpers.d.ts +25 -0
  105. package/dist/docs/docs-workspace-helpers.js +195 -0
  106. package/dist/docs/docs.tokens.css +13 -0
  107. package/dist/docs/import-anki-package.d.ts +10 -0
  108. package/dist/docs/import-anki-package.js +30 -0
  109. package/dist/docs/lapis-markdown-extensions.d.ts +9 -0
  110. package/dist/docs/lapis-markdown-extensions.js +128 -0
  111. package/dist/docs/pin-reading-chrome.d.ts +4 -0
  112. package/dist/docs/pin-reading-chrome.js +32 -0
  113. package/dist/docs/sidebar/DocsFoldersSidebar.svelte +134 -0
  114. package/dist/docs/sidebar/DocsFoldersSidebar.svelte.d.ts +15 -0
  115. package/dist/docs/sidebar/DocsLinksSidebar.svelte +105 -0
  116. package/dist/docs/sidebar/DocsLinksSidebar.svelte.d.ts +11 -0
  117. package/dist/docs/sidebar/DocsSidebar.css +346 -0
  118. package/dist/docs/sidebar/DocsSidebar.svelte +430 -0
  119. package/dist/docs/sidebar/DocsSidebar.svelte.d.ts +18 -0
  120. package/dist/docs/sidebar/DocsSidebarHost.svelte +139 -0
  121. package/dist/docs/sidebar/DocsSidebarHost.svelte.d.ts +12 -0
  122. package/dist/docs/sidebar/DocsTagsSidebar.svelte +176 -0
  123. package/dist/docs/sidebar/DocsTagsSidebar.svelte.d.ts +12 -0
  124. package/dist/docs/sidebar/docs-book-note-tree.d.ts +23 -0
  125. package/dist/docs/sidebar/docs-book-note-tree.js +88 -0
  126. package/dist/docs/sidebar/docs-note-sort.d.ts +26 -0
  127. package/dist/docs/sidebar/docs-note-sort.js +54 -0
  128. package/dist/docs/sidebar/docs-sidebar-filters.d.ts +17 -0
  129. package/dist/docs/sidebar/docs-sidebar-filters.js +74 -0
  130. package/dist/docs/sidebar/docs-sidebar-ui-storage.d.ts +24 -0
  131. package/dist/docs/sidebar/docs-sidebar-ui-storage.js +87 -0
  132. package/dist/docs/sidebar/docs-source-folder-tree.d.ts +16 -0
  133. package/dist/docs/sidebar/docs-source-folder-tree.js +143 -0
  134. package/dist/docs/sidebar/docs-tag-tree.d.ts +17 -0
  135. package/dist/docs/sidebar/docs-tag-tree.js +84 -0
  136. package/dist/docs-cards-view.d.ts +18 -0
  137. package/dist/docs-cards-view.js +52 -0
  138. package/dist/docs-document-view.d.ts +24 -0
  139. package/dist/docs-document-view.js +127 -0
  140. package/dist/docs-document-workspace.svelte +215 -0
  141. package/dist/docs-document-workspace.svelte.d.ts +16 -0
  142. package/dist/docs-index.d.ts +5 -0
  143. package/dist/docs-index.js +49 -0
  144. package/dist/docs-path.d.ts +6 -0
  145. package/dist/docs-path.js +10 -0
  146. package/dist/docs-plugin.d.ts +21 -0
  147. package/dist/docs-plugin.js +140 -0
  148. package/dist/docs-repository.d.ts +15 -0
  149. package/dist/docs-repository.js +75 -0
  150. package/dist/docs-review-view.d.ts +18 -0
  151. package/dist/docs-review-view.js +52 -0
  152. package/dist/docs-settings-tab.d.ts +8 -0
  153. package/dist/docs-settings-tab.js +25 -0
  154. package/dist/docs-settings.d.ts +17 -0
  155. package/dist/docs-settings.js +38 -0
  156. package/dist/docs-view.d.ts +22 -0
  157. package/dist/docs-view.js +51 -0
  158. package/dist/docs-workspace.svelte +394 -0
  159. package/dist/docs-workspace.svelte.d.ts +15 -0
  160. package/dist/index.d.ts +12 -0
  161. package/dist/index.js +12 -0
  162. package/dist/model/book.d.ts +6 -0
  163. package/dist/model/book.js +101 -0
  164. package/dist/model/coding-runtime.d.ts +4 -0
  165. package/dist/model/coding-runtime.js +9 -0
  166. package/dist/model/docs-card-context.d.ts +32 -0
  167. package/dist/model/docs-card-context.js +89 -0
  168. package/dist/model/docs-card-preview-stack-motion.d.ts +51 -0
  169. package/dist/model/docs-card-preview-stack-motion.js +94 -0
  170. package/dist/model/docs-media-context.d.ts +9 -0
  171. package/dist/model/docs-media-context.js +18 -0
  172. package/dist/model/docs-note-list.d.ts +8 -0
  173. package/dist/model/docs-note-list.js +25 -0
  174. package/dist/model/docs-relative-media.d.ts +30 -0
  175. package/dist/model/docs-relative-media.js +116 -0
  176. package/dist/model/docs-review-constants.d.ts +4 -0
  177. package/dist/model/docs-review-constants.js +13 -0
  178. package/dist/model/fsrs.d.ts +14 -0
  179. package/dist/model/fsrs.js +153 -0
  180. package/dist/model/graph.d.ts +12 -0
  181. package/dist/model/graph.js +169 -0
  182. package/dist/model/index.d.ts +14 -0
  183. package/dist/model/index.js +14 -0
  184. package/dist/model/markdown-normalize.d.ts +11 -0
  185. package/dist/model/markdown-normalize.js +755 -0
  186. package/dist/model/markdown.d.ts +167 -0
  187. package/dist/model/markdown.js +365 -0
  188. package/dist/model/sample.d.ts +7 -0
  189. package/dist/model/sample.js +524 -0
  190. package/dist/model/types.d.ts +328 -0
  191. package/dist/model/types.js +1 -0
  192. package/dist/model/workspace.d.ts +6 -0
  193. package/dist/model/workspace.js +81 -0
  194. package/dist/storybook.css +52 -0
  195. package/dist/styles.css +1 -0
  196. package/manifest.json +11 -0
  197. package/package.json +147 -4
@@ -0,0 +1,838 @@
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import ArrowLeftIcon from '@lucide/svelte/icons/arrow-left';
4
+ import ArrowRightIcon from '@lucide/svelte/icons/arrow-right';
5
+ import BookOpenIcon from '@lucide/svelte/icons/book-open';
6
+ import CheckIcon from '@lucide/svelte/icons/check';
7
+ import ListIcon from '@lucide/svelte/icons/list';
8
+ import SearchIcon from '@lucide/svelte/icons/search';
9
+ import XIcon from '@lucide/svelte/icons/x';
10
+ import { Button } from '@lapismd/design-core/shadcn/button';
11
+ import { Input } from '@lapismd/design-core/shadcn/input';
12
+ import * as ScrollArea from '@lapismd/design-core/shadcn/scroll-area';
13
+ import type {
14
+ DocsBookChapter,
15
+ DocsBookChapterNavigation,
16
+ DocsBookNavigation
17
+ } from '../model';
18
+
19
+ let {
20
+ book,
21
+ activeNoteId,
22
+ chapterNavigation = null,
23
+ variant = 'toc',
24
+ onOpenNote = () => {}
25
+ }: {
26
+ book: DocsBookNavigation;
27
+ activeNoteId: string;
28
+ chapterNavigation?: DocsBookChapterNavigation | null;
29
+ variant?: 'toc' | 'floating';
30
+ onOpenNote?: (noteId: string) => void;
31
+ } = $props();
32
+
33
+ let floatingOpen = $state(false);
34
+ let searchOpen = $state(false);
35
+ let chapterQuery = $state('');
36
+ let searchInput = $state<HTMLInputElement | null>(null);
37
+
38
+ const progressPercent = $derived(Math.max(0, Math.min(100, book.progressPercent)));
39
+ const visibleChapters = $derived.by(() => {
40
+ const needle = chapterQuery.trim().toLowerCase();
41
+ if (!needle) return book.chapters;
42
+ return book.chapters.filter((chapter) =>
43
+ `${chapter.title} ${chapter.summary}`.toLowerCase().includes(needle)
44
+ );
45
+ });
46
+ const ringRadius = 15.5;
47
+ const ringCircumference = 2 * Math.PI * ringRadius;
48
+ const ringOffset = $derived(ringCircumference * (1 - progressPercent / 100));
49
+
50
+ function openNote(noteId: string) {
51
+ onOpenNote(noteId);
52
+ }
53
+
54
+ function isActive(chapter: DocsBookChapter) {
55
+ return chapter.resolved && chapter.noteId === activeNoteId;
56
+ }
57
+
58
+ function closeChapterSearch() {
59
+ searchOpen = false;
60
+ chapterQuery = '';
61
+ }
62
+
63
+ async function toggleChapterSearch() {
64
+ if (searchOpen) {
65
+ closeChapterSearch();
66
+ return;
67
+ }
68
+ searchOpen = true;
69
+ await tick();
70
+ searchInput?.focus();
71
+ }
72
+
73
+ function onChapterSearchKeydown(event: KeyboardEvent) {
74
+ if (event.key === 'Escape') closeChapterSearch();
75
+ }
76
+ </script>
77
+
78
+ {#if variant === 'toc'}
79
+ <section class="docs-book-toc" aria-label="Book table of contents">
80
+ <div class="docs-book-toc__header">
81
+ <div class="docs-book-toc__icon"><ListIcon aria-hidden="true" /></div>
82
+ <div class="docs-book-toc__heading">
83
+ <p>Table of Contents</p>
84
+ <span
85
+ >{book.chapters.length} chapters · {book.completedCount}/{book.resolvedCount} complete</span
86
+ >
87
+ </div>
88
+ <div class="docs-book-toc__search" class:is-open={searchOpen}>
89
+ {#if searchOpen}
90
+ <Input
91
+ bind:ref={searchInput}
92
+ class="docs-book-toc__search-input"
93
+ type="text"
94
+ placeholder="Search chapters"
95
+ aria-label="Search chapters"
96
+ bind:value={chapterQuery}
97
+ onkeydown={onChapterSearchKeydown}
98
+ />
99
+ {/if}
100
+ <Button
101
+ type="button"
102
+ variant="ghost"
103
+ size="icon-sm"
104
+ class="docs-book-toc__search-toggle"
105
+ aria-label={searchOpen ? "Hide chapter search" : "Search chapters"}
106
+ aria-expanded={searchOpen}
107
+ aria-pressed={searchOpen}
108
+ onclick={toggleChapterSearch}
109
+ >
110
+ <SearchIcon aria-hidden="true" />
111
+ </Button>
112
+ </div>
113
+ <div
114
+ class="docs-book-progress-ring"
115
+ role="progressbar"
116
+ aria-label="Book progress"
117
+ aria-valuemin={0}
118
+ aria-valuemax={100}
119
+ aria-valuenow={progressPercent}
120
+ >
121
+ <svg viewBox="0 0 36 36" aria-hidden="true">
122
+ <circle class="docs-book-progress-ring__track" cx="18" cy="18" r={ringRadius}></circle>
123
+ <circle
124
+ class="docs-book-progress-ring__fill"
125
+ cx="18"
126
+ cy="18"
127
+ r={ringRadius}
128
+ stroke-dasharray={ringCircumference}
129
+ stroke-dashoffset={ringOffset}
130
+ ></circle>
131
+ </svg>
132
+ {#if progressPercent >= 100}
133
+ <span class="docs-book-progress-ring__check"><CheckIcon /></span>
134
+ {:else}
135
+ <span class="docs-book-progress-ring__label">{progressPercent}%</span>
136
+ {/if}
137
+ </div>
138
+ </div>
139
+ <ScrollArea.Root class="docs-book-toc__scroll">
140
+ <ol class="docs-book-chapters">
141
+ {#each visibleChapters as chapter (chapter.index + chapter.noteId)}
142
+ <li
143
+ class:resolved={chapter.resolved}
144
+ class:is-active={isActive(chapter)}
145
+ class:is-complete={chapter.completed}
146
+ >
147
+ {#if chapter.resolved}
148
+ <button type="button" onclick={() => openNote(chapter.noteId)} aria-current={isActive(chapter) ? 'page' : undefined}>
149
+ <span class="docs-book-chapters__index">
150
+ {#if chapter.completed}
151
+ <CheckIcon aria-hidden="true" />
152
+ {:else}
153
+ {chapter.index + 1}
154
+ {/if}
155
+ </span>
156
+ <strong>{chapter.title}</strong>
157
+ {#if chapter.summary}
158
+ <small>{chapter.summary}</small>
159
+ {/if}
160
+ </button>
161
+ {:else}
162
+ <span class="docs-book-chapters__missing" aria-disabled="true">
163
+ <span class="docs-book-chapters__index">{chapter.index + 1}</span>
164
+ <strong>{chapter.title}</strong>
165
+ <small>Missing note</small>
166
+ </span>
167
+ {/if}
168
+ </li>
169
+ {:else}
170
+ <li class="docs-book-chapters__empty">No matching chapters.</li>
171
+ {/each}
172
+ </ol>
173
+ </ScrollArea.Root>
174
+ </section>
175
+ {:else}
176
+ <div class="docs-book-floating" class:is-open={floatingOpen}>
177
+ <button
178
+ type="button"
179
+ class="docs-book-floating__trigger"
180
+ aria-label={floatingOpen ? 'Hide book table of contents' : 'Open book table of contents'}
181
+ aria-expanded={floatingOpen}
182
+ aria-controls={`docs-book-floating-panel-${book.id}`}
183
+ onclick={() => (floatingOpen = !floatingOpen)}
184
+ >
185
+ <span
186
+ class="docs-book-progress-ring docs-book-progress-ring--trigger"
187
+ role="presentation"
188
+ aria-hidden="true"
189
+ >
190
+ <svg viewBox="0 0 36 36">
191
+ <circle class="docs-book-progress-ring__track" cx="18" cy="18" r={ringRadius}></circle>
192
+ <circle
193
+ class="docs-book-progress-ring__fill"
194
+ cx="18"
195
+ cy="18"
196
+ r={ringRadius}
197
+ stroke-dasharray={ringCircumference}
198
+ stroke-dashoffset={ringOffset}
199
+ ></circle>
200
+ </svg>
201
+ {#if progressPercent >= 100}
202
+ <span class="docs-book-progress-ring__check"><CheckIcon /></span>
203
+ {:else}
204
+ <BookOpenIcon />
205
+ {/if}
206
+ </span>
207
+ </button>
208
+ {#if floatingOpen}
209
+ <aside
210
+ id={`docs-book-floating-panel-${book.id}`}
211
+ class="docs-book-floating__panel"
212
+ aria-label={`${book.title} book navigation`}
213
+ >
214
+ <div class="docs-book-floating__header">
215
+ <div class="docs-book-floating__icon"><BookOpenIcon aria-hidden="true" /></div>
216
+ <div class="docs-book-floating__title">
217
+ <p>Book</p>
218
+ <button
219
+ type="button"
220
+ aria-current={activeNoteId === book.indexNoteId ? 'page' : undefined}
221
+ onclick={() => openNote(book.indexNoteId)}
222
+ >
223
+ {book.title}
224
+ </button>
225
+ <div class="docs-book-floating__progress">
226
+ <div
227
+ class="docs-book-floating__progress-track"
228
+ role="progressbar"
229
+ aria-label="Book progress"
230
+ aria-valuemin={0}
231
+ aria-valuemax={100}
232
+ aria-valuenow={progressPercent}
233
+ >
234
+ <span
235
+ class="docs-book-floating__progress-fill"
236
+ style={`width: ${progressPercent}%`}
237
+ ></span>
238
+ </div>
239
+ <small>{progressPercent}%</small>
240
+ </div>
241
+ </div>
242
+ <button
243
+ type="button"
244
+ class="docs-book-floating__close"
245
+ aria-label="Close book table of contents"
246
+ onclick={() => (floatingOpen = false)}
247
+ >
248
+ <XIcon aria-hidden="true" />
249
+ </button>
250
+ </div>
251
+ <ol class="docs-book-floating__chapters">
252
+ {#each book.chapters as chapter (chapter.index + chapter.noteId)}
253
+ <li
254
+ class:is-active={isActive(chapter)}
255
+ class:is-missing={!chapter.resolved}
256
+ class:is-complete={chapter.completed}
257
+ >
258
+ {#if chapter.resolved}
259
+ <button type="button" onclick={() => openNote(chapter.noteId)} aria-current={isActive(chapter) ? 'page' : undefined}>
260
+ <span class="docs-book-chapters__index">
261
+ {#if chapter.completed}
262
+ <CheckIcon aria-hidden="true" />
263
+ {:else}
264
+ {chapter.index + 1}
265
+ {/if}
266
+ </span>
267
+ <strong>{chapter.title}</strong>
268
+ </button>
269
+ {:else}
270
+ <span aria-disabled="true">
271
+ <span class="docs-book-chapters__index">{chapter.index + 1}</span>
272
+ <strong>{chapter.title}</strong>
273
+ </span>
274
+ {/if}
275
+ </li>
276
+ {/each}
277
+ </ol>
278
+ {#if chapterNavigation?.previousChapter || chapterNavigation?.nextChapter}
279
+ <div class="docs-book-floating__controls" aria-label="Chapter navigation">
280
+ {#if chapterNavigation.previousChapter}
281
+ <button type="button" onclick={() => {
282
+ const previous = chapterNavigation?.previousChapter;
283
+ if (previous) openNote(previous.noteId);
284
+ }}>
285
+ <ArrowLeftIcon aria-hidden="true" />
286
+ <span>Previous</span>
287
+ </button>
288
+ {:else}
289
+ <span aria-disabled="true">
290
+ <ArrowLeftIcon aria-hidden="true" />
291
+ <span>Previous</span>
292
+ </span>
293
+ {/if}
294
+ {#if chapterNavigation.nextChapter}
295
+ <button type="button" onclick={() => {
296
+ const next = chapterNavigation?.nextChapter;
297
+ if (next) openNote(next.noteId);
298
+ }}>
299
+ <span>Next</span>
300
+ <ArrowRightIcon aria-hidden="true" />
301
+ </button>
302
+ {:else}
303
+ <span aria-disabled="true">
304
+ <span>Next</span>
305
+ <ArrowRightIcon aria-hidden="true" />
306
+ </span>
307
+ {/if}
308
+ </div>
309
+ {/if}
310
+ </aside>
311
+ {/if}
312
+ </div>
313
+ {/if}
314
+
315
+ <style>
316
+ .docs-book-toc {
317
+ display: flex;
318
+ flex-direction: column;
319
+ gap: 1rem;
320
+ max-height: 50svh;
321
+ margin: 1.5rem 0;
322
+ padding: 1rem;
323
+ overflow: hidden;
324
+ border: 1px solid var(--border);
325
+ border-radius: 0.5rem;
326
+ background: var(--background);
327
+ box-shadow: 0 0.75rem 2rem color-mix(in srgb, var(--foreground) 8%, transparent);
328
+ }
329
+
330
+ .docs-book-toc__header,
331
+ .docs-book-floating__header {
332
+ display: flex;
333
+ align-items: center;
334
+ gap: 0.65rem;
335
+ min-width: 0;
336
+ }
337
+
338
+ .docs-book-toc__header {
339
+ position: sticky;
340
+ top: 0;
341
+ z-index: 1;
342
+ flex: 0 0 auto;
343
+ background: var(--background);
344
+ }
345
+
346
+ .docs-book-toc__heading {
347
+ min-width: 0;
348
+ flex: 1 1 auto;
349
+ }
350
+
351
+ .docs-book-toc__heading p,
352
+ .docs-book-toc__heading span {
353
+ overflow: hidden;
354
+ text-overflow: ellipsis;
355
+ white-space: nowrap;
356
+ }
357
+
358
+ .docs-book-toc__search {
359
+ display: flex;
360
+ flex: 0 0 auto;
361
+ align-items: center;
362
+ justify-content: flex-end;
363
+ min-width: 0;
364
+ margin-left: auto;
365
+ }
366
+
367
+ .docs-book-toc__search.is-open {
368
+ flex: 1 1 12rem;
369
+ gap: 0.35rem;
370
+ max-width: 18rem;
371
+ }
372
+
373
+ .docs-book-toc :global(button.docs-book-toc__search-toggle) {
374
+ width: 2rem;
375
+ height: 2rem;
376
+ flex: 0 0 auto;
377
+ border-radius: 0.5rem;
378
+ }
379
+
380
+ .docs-book-toc__search.is-open :global(button.docs-book-toc__search-toggle),
381
+ .docs-book-toc__search.is-open :global(button.docs-book-toc__search-toggle:hover),
382
+ .docs-book-toc__search.is-open :global(button.docs-book-toc__search-toggle:focus-visible) {
383
+ background: color-mix(in srgb, var(--primary) 16%, var(--background));
384
+ color: var(--primary);
385
+ }
386
+
387
+ .docs-book-toc :global(input.docs-book-toc__search-input) {
388
+ height: 2rem;
389
+ min-width: 0;
390
+ flex: 1 1 auto;
391
+ font-size: 0.82rem;
392
+ padding-inline: 0.5rem;
393
+ }
394
+
395
+ .docs-book-toc :global(.docs-book-toc__scroll) {
396
+ min-height: 0;
397
+ flex: 1 1 auto;
398
+ overflow: hidden;
399
+ }
400
+
401
+ .docs-book-toc :global(.docs-book-toc__scroll [data-ui-part='scroll-area-viewport']) {
402
+ max-height: calc(50svh - 5.75rem);
403
+ }
404
+
405
+ .docs-book-toc__icon,
406
+ .docs-book-floating__icon {
407
+ display: grid;
408
+ flex: 0 0 auto;
409
+ width: 2rem;
410
+ height: 2rem;
411
+ place-items: center;
412
+ border-radius: 0.5rem;
413
+ background: color-mix(in srgb, var(--primary) 10%, transparent);
414
+ color: var(--primary);
415
+ }
416
+
417
+ .docs-book-toc__icon :global(svg),
418
+ .docs-book-floating__icon :global(svg) {
419
+ width: 1rem;
420
+ height: 1rem;
421
+ }
422
+
423
+ .docs-book-floating__title {
424
+ min-width: 0;
425
+ flex: 1 1 auto;
426
+ }
427
+
428
+ .docs-book-toc__header p,
429
+ .docs-book-floating__header p {
430
+ margin: 0;
431
+ color: var(--muted-foreground);
432
+ font-size: 0.72rem;
433
+ font-weight: 700;
434
+ text-transform: uppercase;
435
+ }
436
+
437
+ .docs-book-toc__header span {
438
+ color: var(--muted-foreground);
439
+ font-size: 0.82rem;
440
+ }
441
+
442
+ .docs-book-progress-ring {
443
+ position: relative;
444
+ display: grid;
445
+ flex: 0 0 auto;
446
+ width: 2.35rem;
447
+ height: 2.35rem;
448
+ place-items: center;
449
+ }
450
+
451
+ .docs-book-progress-ring svg {
452
+ position: absolute;
453
+ inset: 0;
454
+ width: 100%;
455
+ height: 100%;
456
+ transform: rotate(-90deg);
457
+ }
458
+
459
+ .docs-book-progress-ring__track,
460
+ .docs-book-progress-ring__fill {
461
+ fill: none;
462
+ stroke-width: 3;
463
+ }
464
+
465
+ .docs-book-progress-ring__track {
466
+ stroke: color-mix(in srgb, var(--border) 85%, transparent);
467
+ }
468
+
469
+ .docs-book-progress-ring__fill {
470
+ stroke: #22c55e;
471
+ stroke-linecap: round;
472
+ transition: stroke-dashoffset 180ms ease;
473
+ }
474
+
475
+ .docs-book-progress-ring__label,
476
+ .docs-book-progress-ring__check {
477
+ position: relative;
478
+ z-index: 1;
479
+ color: var(--foreground);
480
+ font-size: 0.58rem;
481
+ font-weight: 700;
482
+ font-variant-numeric: tabular-nums;
483
+ }
484
+
485
+ .docs-book-progress-ring__check {
486
+ display: grid;
487
+ place-items: center;
488
+ color: #16a34a;
489
+ }
490
+
491
+ .docs-book-progress-ring__check :global(svg) {
492
+ width: 0.85rem;
493
+ height: 0.85rem;
494
+ }
495
+
496
+ .docs-book-progress-ring--trigger {
497
+ width: 100%;
498
+ height: 100%;
499
+ color: var(--foreground);
500
+ }
501
+
502
+ .docs-book-progress-ring--trigger > :global(.lucide) {
503
+ position: relative;
504
+ z-index: 1;
505
+ width: 0.95rem;
506
+ height: 0.95rem;
507
+ }
508
+
509
+ .docs-book-chapters,
510
+ .docs-book-floating__chapters {
511
+ display: grid;
512
+ gap: 0.45rem;
513
+ margin: 0;
514
+ padding: 0;
515
+ list-style: none;
516
+ }
517
+
518
+ .docs-book-chapters li > button,
519
+ .docs-book-chapters__missing {
520
+ display: grid;
521
+ grid-template-columns: 2rem minmax(0, 1fr);
522
+ gap: 0.2rem 0.75rem;
523
+ padding: 0.7rem;
524
+ border: 1px solid var(--border);
525
+ border-radius: 0.45rem;
526
+ background: transparent;
527
+ color: inherit;
528
+ font: inherit;
529
+ text-align: left;
530
+ text-decoration: none;
531
+ cursor: pointer;
532
+ width: 100%;
533
+ }
534
+
535
+ .docs-book-chapters li.is-active > button,
536
+ .docs-book-floating__chapters li.is-active > button {
537
+ border-color: color-mix(in srgb, var(--primary) 60%, var(--border));
538
+ background: color-mix(in srgb, var(--primary) 8%, transparent);
539
+ }
540
+
541
+ .docs-book-chapters li.is-complete > button .docs-book-chapters__index,
542
+ .docs-book-floating__chapters li.is-complete > button .docs-book-chapters__index {
543
+ background: color-mix(in srgb, #22c55e 18%, transparent);
544
+ color: #15803d;
545
+ }
546
+
547
+ .docs-book-chapters li > button:hover,
548
+ .docs-book-chapters li > button:focus-visible {
549
+ border-color: var(--ring);
550
+ outline: 0;
551
+ }
552
+
553
+ .docs-book-chapters__index {
554
+ display: grid;
555
+ width: 1.7rem;
556
+ height: 1.7rem;
557
+ place-items: center;
558
+ border-radius: 999px;
559
+ background: color-mix(in srgb, var(--foreground) 9%, transparent);
560
+ color: var(--muted-foreground);
561
+ font-size: 0.72rem;
562
+ font-weight: 700;
563
+ }
564
+
565
+ .docs-book-chapters li > button:has(small) .docs-book-chapters__index,
566
+ .docs-book-chapters__missing:has(small) .docs-book-chapters__index {
567
+ grid-row: 1 / span 2;
568
+ align-self: center;
569
+ }
570
+
571
+ .docs-book-chapters__index :global(svg) {
572
+ width: 0.85rem;
573
+ height: 0.85rem;
574
+ }
575
+
576
+ .docs-book-chapters strong {
577
+ align-self: center;
578
+ font-size: 0.95rem;
579
+ }
580
+
581
+ .docs-book-chapters small {
582
+ grid-column: 2;
583
+ color: var(--muted-foreground);
584
+ line-height: 1.45;
585
+ }
586
+
587
+ .docs-book-chapters__missing {
588
+ opacity: 0.62;
589
+ }
590
+
591
+ .docs-book-chapters__empty {
592
+ margin: 0;
593
+ color: var(--muted-foreground);
594
+ font-size: 0.82rem;
595
+ list-style: none;
596
+ }
597
+
598
+ .docs-book-floating {
599
+ /* Center on the same axis as the TOC rail / chapter scroll ring. */
600
+ --docs-book-floating-size: 2.35rem;
601
+ position: absolute;
602
+ top: 0.85rem;
603
+ right: max(
604
+ calc(
605
+ 0.8rem + var(--markdown-floating-toc-scrollbar-offset, 1rem) +
606
+ (var(--markdown-floating-toc-gutter, 1.45rem) - var(--docs-book-floating-size)) / 2
607
+ ),
608
+ calc(
609
+ env(safe-area-inset-right) + var(--markdown-floating-toc-scrollbar-offset, 1rem) +
610
+ (var(--markdown-floating-toc-gutter, 1.45rem) - var(--docs-book-floating-size)) / 2
611
+ )
612
+ );
613
+ z-index: 36;
614
+ width: var(--docs-book-floating-size);
615
+ pointer-events: none;
616
+ }
617
+
618
+ .docs-book-floating.is-open {
619
+ width: 20rem;
620
+ }
621
+
622
+ .docs-book-floating__trigger,
623
+ .docs-book-floating__close {
624
+ display: grid;
625
+ place-items: center;
626
+ border: 1px solid var(--border);
627
+ background: color-mix(in srgb, var(--background) 95%, transparent);
628
+ color: var(--foreground);
629
+ cursor: pointer;
630
+ backdrop-filter: blur(10px);
631
+ }
632
+
633
+ .docs-book-floating__trigger {
634
+ position: absolute;
635
+ top: 0;
636
+ right: 0;
637
+ width: var(--docs-book-floating-size);
638
+ height: var(--docs-book-floating-size);
639
+ padding: 0;
640
+ border-radius: 999px;
641
+ box-shadow: 0 0.7rem 1.5rem color-mix(in srgb, var(--foreground) 12%, transparent);
642
+ pointer-events: auto;
643
+ }
644
+
645
+ .docs-book-floating__trigger:hover,
646
+ .docs-book-floating__trigger:focus-visible,
647
+ .docs-book-floating__close:hover,
648
+ .docs-book-floating__close:focus-visible {
649
+ border-color: var(--ring);
650
+ color: var(--primary);
651
+ outline: 0;
652
+ }
653
+
654
+ .docs-book-floating__close :global(svg) {
655
+ width: 1rem;
656
+ height: 1rem;
657
+ }
658
+
659
+ .docs-book-floating__panel {
660
+ position: absolute;
661
+ top: 0;
662
+ right: 2.9rem;
663
+ z-index: 1;
664
+ display: grid;
665
+ width: 16.75rem;
666
+ max-height: min(70vh, 34rem);
667
+ gap: 0.75rem;
668
+ padding: 0.75rem;
669
+ overflow: auto;
670
+ border: 1px solid var(--border);
671
+ border-radius: 0.55rem;
672
+ background: color-mix(in srgb, var(--background) 95%, transparent);
673
+ box-shadow: 0 0.9rem 2rem color-mix(in srgb, var(--foreground) 12%, transparent);
674
+ backdrop-filter: blur(10px);
675
+ pointer-events: auto;
676
+ }
677
+
678
+ .docs-book-floating__title {
679
+ min-width: 0;
680
+ flex: 1 1 auto;
681
+ }
682
+
683
+ .docs-book-floating__progress {
684
+ display: flex;
685
+ align-items: center;
686
+ gap: 0.45rem;
687
+ margin-top: 0.35rem;
688
+ }
689
+
690
+ .docs-book-floating__progress-track {
691
+ position: relative;
692
+ flex: 1 1 auto;
693
+ height: 0.35rem;
694
+ overflow: hidden;
695
+ border: 1px solid color-mix(in srgb, var(--border) 80%, transparent);
696
+ border-radius: 999px;
697
+ background: color-mix(in srgb, var(--muted-foreground) 14%, transparent);
698
+ }
699
+
700
+ .docs-book-floating__progress-fill {
701
+ display: block;
702
+ height: 100%;
703
+ border-radius: inherit;
704
+ background: #22c55e;
705
+ transition: width 180ms ease;
706
+ }
707
+
708
+ .docs-book-floating__progress small {
709
+ flex: 0 0 auto;
710
+ min-width: 1.75rem;
711
+ color: var(--muted-foreground);
712
+ font-size: 0.72rem;
713
+ font-variant-numeric: tabular-nums;
714
+ text-align: right;
715
+ }
716
+
717
+ .docs-book-floating__close {
718
+ flex: 0 0 auto;
719
+ width: 1.85rem;
720
+ height: 1.85rem;
721
+ border-radius: 0.45rem;
722
+ }
723
+
724
+ .docs-book-floating__title > button {
725
+ display: block;
726
+ min-width: 0;
727
+ border: 0;
728
+ background: transparent;
729
+ color: var(--foreground);
730
+ font: inherit;
731
+ font-size: 0.84rem;
732
+ font-weight: 700;
733
+ line-height: 1.25;
734
+ text-decoration: none;
735
+ }
736
+
737
+ .docs-book-floating__title > button:hover,
738
+ .docs-book-floating__title > button:focus-visible {
739
+ color: var(--primary);
740
+ outline: 0;
741
+ }
742
+
743
+ .docs-book-floating__chapters {
744
+ gap: 0.15rem;
745
+ }
746
+
747
+ .docs-book-floating__chapters li > button,
748
+ .docs-book-floating__chapters li > span {
749
+ display: grid;
750
+ grid-template-columns: 1.7rem minmax(0, 1fr);
751
+ align-items: center;
752
+ gap: 0.5rem;
753
+ padding: 0.35rem;
754
+ border: 1px solid transparent;
755
+ border-radius: 0.4rem;
756
+ background: transparent;
757
+ color: inherit;
758
+ font: inherit;
759
+ text-align: left;
760
+ text-decoration: none;
761
+ cursor: pointer;
762
+ width: 100%;
763
+ }
764
+
765
+ .docs-book-floating__chapters li > button:hover,
766
+ .docs-book-floating__chapters li > button:focus-visible {
767
+ border-color: var(--ring);
768
+ outline: 0;
769
+ }
770
+
771
+ .docs-book-floating__chapters strong {
772
+ min-width: 0;
773
+ overflow: hidden;
774
+ font-size: 0.78rem;
775
+ font-weight: 650;
776
+ text-overflow: ellipsis;
777
+ white-space: nowrap;
778
+ }
779
+
780
+ .docs-book-floating__chapters li.is-missing {
781
+ opacity: 0.55;
782
+ }
783
+
784
+ .docs-book-floating__controls {
785
+ display: grid;
786
+ grid-template-columns: 1fr 1fr;
787
+ gap: 0.4rem;
788
+ padding-top: 0.35rem;
789
+ border-top: 1px solid var(--border);
790
+ }
791
+
792
+ .docs-book-floating__controls button,
793
+ .docs-book-floating__controls > span {
794
+ display: inline-flex;
795
+ align-items: center;
796
+ justify-content: center;
797
+ gap: 0.25rem;
798
+ min-height: 1.9rem;
799
+ border: 1px solid var(--border);
800
+ border-radius: 0.4rem;
801
+ background: transparent;
802
+ color: var(--foreground);
803
+ font: inherit;
804
+ font-size: 0.72rem;
805
+ cursor: pointer;
806
+ font-weight: 700;
807
+ text-decoration: none;
808
+ }
809
+
810
+ .docs-book-floating__controls button:hover,
811
+ .docs-book-floating__controls button:focus-visible {
812
+ border-color: var(--ring);
813
+ outline: 0;
814
+ }
815
+
816
+ .docs-book-floating__controls > span {
817
+ color: var(--muted-foreground);
818
+ opacity: 0.5;
819
+ }
820
+
821
+ .docs-book-floating__controls :global(svg) {
822
+ width: 0.8rem;
823
+ height: 0.8rem;
824
+ }
825
+
826
+ @media (max-width: 700px) {
827
+ .docs-book-floating.is-open {
828
+ width: calc(100vw - 1.5rem);
829
+ }
830
+
831
+ .docs-book-floating__panel {
832
+ top: 2.75rem;
833
+ right: 0;
834
+ width: min(16.75rem, calc(100vw - 1.5rem));
835
+ max-height: min(62vh, 28rem);
836
+ }
837
+ }
838
+ </style>