@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,37 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type FlipCardDeckMode = 'browse' | 'study';
3
+ export type FlipCardDeckContext = {
4
+ key: string;
5
+ index: number;
6
+ count: number;
7
+ peek: boolean;
8
+ revealed: boolean;
9
+ reveal: () => void;
10
+ hide: () => void;
11
+ toggle: () => void;
12
+ next: () => void;
13
+ previous: () => void;
14
+ goTo: (index: number) => void;
15
+ reset: () => void;
16
+ };
17
+ type $$ComponentProps = {
18
+ items: unknown[];
19
+ keyFor?: (item: unknown, index: number) => string;
20
+ revealedByKey: Record<string, boolean>;
21
+ ariaLabel?: string;
22
+ emptyLabel?: string;
23
+ mode?: FlipCardDeckMode;
24
+ initialIndex?: number;
25
+ blockSwipeWhenRevealed?: boolean;
26
+ indexControlLabel?: string;
27
+ indexControlTarget?: number;
28
+ frontActionLabel?: string;
29
+ backActionLabel?: string;
30
+ frontHeader?: Snippet<[unknown, FlipCardDeckContext]>;
31
+ front: Snippet<[unknown, FlipCardDeckContext]>;
32
+ back: Snippet<[unknown, FlipCardDeckContext]>;
33
+ backActions?: Snippet<[unknown, FlipCardDeckContext]>;
34
+ };
35
+ declare const FlipCardDeck: import("svelte").Component<$$ComponentProps, {}, "revealedByKey">;
36
+ type FlipCardDeck = ReturnType<typeof FlipCardDeck>;
37
+ export default FlipCardDeck;
@@ -0,0 +1,313 @@
1
+ <script lang="ts">
2
+ /**
3
+ * DESIGN_CORE_CANDIDATE: MarkdownFloatingToc — ported for Docs; promote to design-core/forms later.
4
+ */
5
+ import "./markdown-floating-toc.css";
6
+ import type { MarkdownFloatingTocMode } from "./markdown-floating-toc";
7
+
8
+ type TocHeading = {
9
+ key: string;
10
+ id: string;
11
+ title: string;
12
+ level: number;
13
+ element: HTMLElement;
14
+ };
15
+
16
+ let {
17
+ root = null,
18
+ mode = "auto",
19
+ compact = false,
20
+ disabled = false,
21
+ }: {
22
+ root?: HTMLElement | null;
23
+ mode?: MarkdownFloatingTocMode;
24
+ compact?: boolean;
25
+ disabled?: boolean;
26
+ } = $props();
27
+
28
+ let headings = $state<TocHeading[]>([]);
29
+ let activeId = $state("");
30
+ let hoverOpen = $state(false);
31
+ let pinnedOpen = $state(false);
32
+ let focusOpen = $state(false);
33
+ let containerElement = $state<HTMLDivElement | null>(null);
34
+ let panelElement = $state<HTMLElement | null>(null);
35
+
36
+ const canCollect = $derived(Boolean(root) && mode !== "off" && !disabled && (mode === "always" || !compact));
37
+ const isVisible = $derived(canCollect && headings.length >= (mode === "always" ? 1 : 2));
38
+ const isOpen = $derived(isVisible && (hoverOpen || pinnedOpen || focusOpen));
39
+
40
+ function collectHeadings() {
41
+ if (!root || !canCollect) {
42
+ headings = [];
43
+ activeId = "";
44
+ return;
45
+ }
46
+
47
+ const seenKeys = new Map<string, number>();
48
+ const next = Array.from(root.querySelectorAll<HTMLElement>("[data-cv-heading='true']"))
49
+ .map((element, index) => {
50
+ const id = element.id;
51
+ const title = element.dataset.cvHeadingTitle?.trim() || element.textContent?.trim() || "";
52
+ const level = Number.parseInt(element.dataset.cvHeadingLevel ?? element.tagName.slice(1), 10);
53
+ if (!id || !title || !Number.isFinite(level)) return null;
54
+ const baseKey = id;
55
+ const count = seenKeys.get(baseKey) ?? 0;
56
+ seenKeys.set(baseKey, count + 1);
57
+ return {
58
+ // Duplicate DOM ids can appear across nested markdown previews; keep
59
+ // each list item unique for Svelte keyed each blocks.
60
+ key: count === 0 ? baseKey : `${baseKey}__${count + 1}-${index}`,
61
+ id,
62
+ title,
63
+ level,
64
+ element,
65
+ };
66
+ })
67
+ .filter((heading): heading is TocHeading => Boolean(heading));
68
+
69
+ headings = next;
70
+ if (!next.some((heading) => heading.id === activeId)) {
71
+ activeId = next[0]?.id ?? "";
72
+ }
73
+ }
74
+
75
+ function findScrollRoot(element: HTMLElement) {
76
+ let current: HTMLElement | null = element;
77
+ while (current) {
78
+ const style = getComputedStyle(current);
79
+ const overflowY = `${style.overflowY} ${style.overflow}`;
80
+ if (/(auto|scroll)/.test(overflowY) && current.scrollHeight > current.clientHeight) {
81
+ return current;
82
+ }
83
+ current = current.parentElement;
84
+ }
85
+ return null;
86
+ }
87
+
88
+ function markerWidthFor(level: number) {
89
+ const clampedLevel = Math.min(6, Math.max(1, Math.round(level)));
90
+ return Number((1.24 - (clampedLevel - 1) * 0.16).toFixed(2));
91
+ }
92
+
93
+ function updateActiveHeading() {
94
+ if (!headings.length) {
95
+ activeId = "";
96
+ return;
97
+ }
98
+
99
+ const scrollRoot = root ? findScrollRoot(root) : null;
100
+ const rootTop = scrollRoot?.getBoundingClientRect().top ?? 0;
101
+ const offset = 96;
102
+ let currentHeading = headings[0];
103
+
104
+ for (const heading of headings) {
105
+ if (!heading.element.isConnected) continue;
106
+ const top = heading.element.getBoundingClientRect().top - rootTop;
107
+ if (top <= offset) {
108
+ currentHeading = heading;
109
+ } else {
110
+ break;
111
+ }
112
+ }
113
+
114
+ activeId = currentHeading?.id ?? "";
115
+ }
116
+
117
+ function handleFocusOut() {
118
+ window.setTimeout(() => {
119
+ if (!containerElement?.contains(document.activeElement)) {
120
+ focusOpen = false;
121
+ }
122
+ }, 0);
123
+ }
124
+
125
+ function navigateToHeading(heading: TocHeading) {
126
+ const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
127
+ heading.element.scrollIntoView({
128
+ behavior: reduceMotion ? "auto" : "smooth",
129
+ block: "start",
130
+ });
131
+ if (!heading.element.hasAttribute("tabindex")) {
132
+ heading.element.setAttribute("tabindex", "-1");
133
+ }
134
+ heading.element.focus({ preventScroll: true });
135
+ activeId = heading.id;
136
+ scrollPanelItemIntoView(heading.id);
137
+ }
138
+
139
+ function scrollPanelItemIntoView(headingId: string) {
140
+ requestAnimationFrame(() => {
141
+ const item = Array.from(panelElement?.querySelectorAll<HTMLButtonElement>(".markdown-floating-toc__item") ?? [])
142
+ .find((button) => button.dataset.headingId === headingId);
143
+
144
+ item?.scrollIntoView({
145
+ block: "nearest",
146
+ inline: "nearest",
147
+ });
148
+ });
149
+ }
150
+
151
+ function handleRailHeadingClick(event: MouseEvent, heading: TocHeading) {
152
+ event.preventDefault();
153
+ event.stopPropagation();
154
+ pinnedOpen = true;
155
+ navigateToHeading(heading);
156
+ }
157
+
158
+ function handlePanelHeadingClick(event: MouseEvent, heading: TocHeading) {
159
+ event.preventDefault();
160
+ event.stopPropagation();
161
+ navigateToHeading(heading);
162
+ pinnedOpen = false;
163
+ }
164
+
165
+ function handleDocumentPointerDown(event: PointerEvent) {
166
+ const target = event.target;
167
+ if (target instanceof Node && containerElement?.contains(target)) return;
168
+
169
+ hoverOpen = false;
170
+ pinnedOpen = false;
171
+ focusOpen = false;
172
+ }
173
+
174
+ $effect(() => {
175
+ if (!root || !canCollect) {
176
+ headings = [];
177
+ activeId = "";
178
+ return;
179
+ }
180
+
181
+ let raf = 0;
182
+ const scheduleCollect = () => {
183
+ cancelAnimationFrame(raf);
184
+ raf = requestAnimationFrame(collectHeadings);
185
+ };
186
+
187
+ collectHeadings();
188
+
189
+ const mutationObserver = new MutationObserver(scheduleCollect);
190
+ mutationObserver.observe(root, {
191
+ attributes: true,
192
+ attributeFilter: ["id", "data-cv-heading", "data-cv-heading-level", "data-cv-heading-title"],
193
+ childList: true,
194
+ characterData: true,
195
+ subtree: true,
196
+ });
197
+
198
+ return () => {
199
+ cancelAnimationFrame(raf);
200
+ mutationObserver.disconnect();
201
+ };
202
+ });
203
+
204
+ $effect(() => {
205
+ if (!root || !isVisible || !headings.length) return;
206
+
207
+ let raf = 0;
208
+ const scrollRoot = findScrollRoot(root);
209
+ const scheduleUpdate = () => {
210
+ cancelAnimationFrame(raf);
211
+ raf = requestAnimationFrame(updateActiveHeading);
212
+ };
213
+
214
+ scheduleUpdate();
215
+ window.addEventListener("scroll", scheduleUpdate, { passive: true });
216
+ window.addEventListener("resize", scheduleUpdate);
217
+ scrollRoot?.addEventListener("scroll", scheduleUpdate, { passive: true });
218
+
219
+ let intersectionObserver: IntersectionObserver | null = null;
220
+ if ("IntersectionObserver" in window) {
221
+ intersectionObserver = new IntersectionObserver(scheduleUpdate, {
222
+ root: scrollRoot,
223
+ rootMargin: "-15% 0px -70% 0px",
224
+ threshold: [0, 1],
225
+ });
226
+ for (const heading of headings) {
227
+ intersectionObserver.observe(heading.element);
228
+ }
229
+ }
230
+
231
+ return () => {
232
+ cancelAnimationFrame(raf);
233
+ window.removeEventListener("scroll", scheduleUpdate);
234
+ window.removeEventListener("resize", scheduleUpdate);
235
+ scrollRoot?.removeEventListener("scroll", scheduleUpdate);
236
+ intersectionObserver?.disconnect();
237
+ };
238
+ });
239
+
240
+ $effect(() => {
241
+ if (!isOpen) return;
242
+
243
+ document.addEventListener("pointerdown", handleDocumentPointerDown, true);
244
+ return () => {
245
+ document.removeEventListener("pointerdown", handleDocumentPointerDown, true);
246
+ };
247
+ });
248
+ </script>
249
+
250
+ {#if isVisible}
251
+ <div
252
+ class={`markdown-floating-toc ${isOpen ? "is-open" : ""}`}
253
+ role="group"
254
+ aria-label="Document outline"
255
+ bind:this={containerElement}
256
+ style={`--toc-heading-count: ${headings.length}`}
257
+ onpointerenter={() => (hoverOpen = true)}
258
+ onpointerleave={() => (hoverOpen = false)}
259
+ onfocusin={() => (focusOpen = true)}
260
+ onfocusout={handleFocusOut}
261
+ >
262
+ <div
263
+ class="markdown-floating-toc__rail"
264
+ role="list"
265
+ aria-label="Document heading markers"
266
+ >
267
+ {#each headings as heading (heading.key)}
268
+ <button
269
+ type="button"
270
+ class="markdown-floating-toc__rail-marker"
271
+ aria-label={`Open outline and scroll to ${heading.title}`}
272
+ aria-current={activeId === heading.id ? "true" : undefined}
273
+ data-level={heading.level}
274
+ data-heading-id={heading.id}
275
+ onclick={(event) => handleRailHeadingClick(event, heading)}
276
+ >
277
+ <span
278
+ class={`markdown-floating-toc__rail-line ${activeId === heading.id ? "is-active" : ""}`}
279
+ aria-hidden="true"
280
+ style={`--toc-marker-width: ${markerWidthFor(heading.level)}rem`}
281
+ ></span>
282
+ </button>
283
+ {/each}
284
+ </div>
285
+
286
+ {#if isOpen}
287
+ <nav
288
+ class="markdown-floating-toc__panel"
289
+ aria-label="Markdown table of contents"
290
+ bind:this={panelElement}
291
+ >
292
+ <p class="markdown-floating-toc__eyebrow">On this page</p>
293
+ <ol>
294
+ {#each headings as heading (heading.key)}
295
+ <li>
296
+ <button
297
+ type="button"
298
+ class={`markdown-floating-toc__item ${activeId === heading.id ? "is-active" : ""}`}
299
+ aria-current={activeId === heading.id ? "true" : undefined}
300
+ data-heading-id={heading.id}
301
+ data-level={heading.level}
302
+ style={`--toc-indent: ${(heading.level - 1) * 0.68}rem`}
303
+ onclick={(event) => handlePanelHeadingClick(event, heading)}
304
+ >
305
+ {heading.title}
306
+ </button>
307
+ </li>
308
+ {/each}
309
+ </ol>
310
+ </nav>
311
+ {/if}
312
+ </div>
313
+ {/if}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * DESIGN_CORE_CANDIDATE: MarkdownFloatingToc — ported for Docs; promote to design-core/forms later.
3
+ */
4
+ import "./markdown-floating-toc.css";
5
+ import type { MarkdownFloatingTocMode } from "./markdown-floating-toc";
6
+ type $$ComponentProps = {
7
+ root?: HTMLElement | null;
8
+ mode?: MarkdownFloatingTocMode;
9
+ compact?: boolean;
10
+ disabled?: boolean;
11
+ };
12
+ declare const MarkdownFloatingToc: import("svelte").Component<$$ComponentProps, {}, "">;
13
+ type MarkdownFloatingToc = ReturnType<typeof MarkdownFloatingToc>;
14
+ export default MarkdownFloatingToc;
@@ -0,0 +1,161 @@
1
+ .cv-searchable-choice-picker {
2
+ position: relative;
3
+ width: fit-content;
4
+ max-width: 100%;
5
+ min-width: 0;
6
+ }
7
+
8
+ .cv-searchable-choice-trigger {
9
+ display: inline-flex;
10
+ min-height: 1.65rem;
11
+ max-width: 100%;
12
+ align-items: center;
13
+ gap: 0.35rem;
14
+ border: 0;
15
+ border-radius: 0;
16
+ background: transparent;
17
+ color: var(--cv-form-foreground, var(--kanban-foreground, var(--foreground)));
18
+ cursor: pointer;
19
+ font: inherit;
20
+ font-size: 0.82rem;
21
+ outline: 0;
22
+ padding: 0.45rem 0;
23
+ text-align: left;
24
+ }
25
+
26
+ .cv-searchable-choice-trigger:hover,
27
+ .cv-searchable-choice-trigger:focus-visible,
28
+ .cv-searchable-choice-trigger[aria-expanded="true"] {
29
+ color: var(--cv-form-foreground, var(--kanban-foreground, var(--foreground)));
30
+ outline: 0;
31
+ }
32
+
33
+ .cv-searchable-choice-trigger.is-placeholder {
34
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
35
+ }
36
+
37
+ .cv-searchable-choice-trigger :global(svg) {
38
+ width: 0.85rem;
39
+ height: 0.85rem;
40
+ flex: 0 0 auto;
41
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
42
+ }
43
+
44
+ .cv-searchable-choice-trigger-label {
45
+ min-width: 0;
46
+ overflow: hidden;
47
+ text-overflow: ellipsis;
48
+ white-space: nowrap;
49
+ }
50
+
51
+ .cv-searchable-choice-popover {
52
+ position: absolute;
53
+ top: calc(100% + 0.3rem);
54
+ left: 0;
55
+ z-index: 40;
56
+ width: min(18rem, 80vw);
57
+ }
58
+
59
+ .cv-searchable-choice-command {
60
+ display: grid;
61
+ overflow: hidden;
62
+ border: 1px solid var(--cv-form-border, var(--kanban-border, var(--border)));
63
+ border-radius: 0.45rem;
64
+ background: var(--cv-form-popover, var(--kanban-card, var(--popover)));
65
+ box-shadow: 0 1rem 2rem
66
+ var(--cv-form-shadow, var(--kanban-shadow, rgb(15 23 42 / 12%)));
67
+ }
68
+
69
+ .cv-searchable-choice-command-input-row {
70
+ display: flex;
71
+ min-width: 0;
72
+ align-items: center;
73
+ gap: 0.4rem;
74
+ border-bottom: 1px solid var(--cv-form-border, var(--kanban-border, var(--border)));
75
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
76
+ padding: 0.35rem 0.5rem;
77
+ }
78
+
79
+ .cv-searchable-choice-command-input-row svg {
80
+ width: 0.85rem;
81
+ height: 0.85rem;
82
+ flex: 0 0 auto;
83
+ }
84
+
85
+ .cv-searchable-choice-command-input {
86
+ min-width: 0;
87
+ flex: 1 1 auto;
88
+ border: 0;
89
+ background: transparent;
90
+ color: var(--cv-form-foreground, var(--kanban-foreground, var(--foreground)));
91
+ font: inherit;
92
+ font-size: 0.8rem;
93
+ outline: 0;
94
+ padding: 0;
95
+ }
96
+
97
+ .cv-searchable-choice-command-input::placeholder {
98
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
99
+ }
100
+
101
+ .cv-searchable-choice-command-list {
102
+ max-height: 14rem;
103
+ overflow: auto;
104
+ outline: 0;
105
+ }
106
+
107
+ .cv-searchable-choice-command-group {
108
+ display: grid;
109
+ min-width: 0;
110
+ }
111
+
112
+ .cv-searchable-choice-command-item {
113
+ display: grid;
114
+ min-width: 0;
115
+ gap: 0.1rem;
116
+ border-bottom: 1px solid var(--cv-form-border, var(--kanban-border, var(--border)));
117
+ color: inherit;
118
+ cursor: pointer;
119
+ padding: 0.45rem 0.55rem;
120
+ text-align: left;
121
+ outline: 0;
122
+ }
123
+
124
+ .cv-searchable-choice-command-item:last-child {
125
+ border-bottom: 0;
126
+ }
127
+
128
+ .cv-searchable-choice-command-item:hover,
129
+ .cv-searchable-choice-command-item:focus-visible,
130
+ .cv-searchable-choice-command-item[data-selected] {
131
+ background: var(
132
+ --cv-form-selection,
133
+ color-mix(in srgb, var(--card-color, var(--primary)) 9%, transparent)
134
+ );
135
+ outline: 0;
136
+ }
137
+
138
+ .cv-searchable-choice-command-item.is-active {
139
+ color: var(--card-color, var(--primary));
140
+ }
141
+
142
+ .cv-searchable-choice-option-label {
143
+ font-size: 0.82rem;
144
+ font-weight: 600;
145
+ }
146
+
147
+ .cv-searchable-choice-option-value {
148
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
149
+ font-size: 0.74rem;
150
+ }
151
+
152
+ .cv-searchable-choice-create-label {
153
+ font-size: 0.82rem;
154
+ font-weight: 600;
155
+ }
156
+
157
+ .cv-searchable-choice-empty {
158
+ padding: 0.45rem 0.55rem;
159
+ color: var(--cv-form-muted, var(--kanban-muted, var(--muted-foreground)));
160
+ font-size: 0.8rem;
161
+ }