@tiramisu-docs/kit 0.1.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 (99) hide show
  1. package/README.md +103 -0
  2. package/components.json +14 -0
  3. package/dist/bin/mcp.d.ts +2 -0
  4. package/dist/bin/mcp.js +4 -0
  5. package/dist/config.d.ts +99 -0
  6. package/dist/config.js +36 -0
  7. package/dist/highlight.d.ts +10 -0
  8. package/dist/highlight.js +93 -0
  9. package/dist/index.d.ts +6 -0
  10. package/dist/index.js +3 -0
  11. package/dist/lib/components/index.d.ts +16 -0
  12. package/dist/lib/components/index.js +18 -0
  13. package/dist/lib/components/tiramisu/lang-icons.d.ts +4 -0
  14. package/dist/lib/components/tiramisu/lang-icons.js +77 -0
  15. package/dist/lib/components/ui/alert/index.d.ts +5 -0
  16. package/dist/lib/components/ui/alert/index.js +6 -0
  17. package/dist/lib/components/ui/badge/index.d.ts +2 -0
  18. package/dist/lib/components/ui/badge/index.js +1 -0
  19. package/dist/lib/components/ui/button/index.d.ts +4 -0
  20. package/dist/lib/components/ui/button/index.js +2 -0
  21. package/dist/lib/components/ui/card/index.d.ts +8 -0
  22. package/dist/lib/components/ui/card/index.js +10 -0
  23. package/dist/lib/components/ui/collapsible/index.d.ts +1 -0
  24. package/dist/lib/components/ui/collapsible/index.js +1 -0
  25. package/dist/lib/components/ui/dropdown-menu/index.d.ts +18 -0
  26. package/dist/lib/components/ui/dropdown-menu/index.js +18 -0
  27. package/dist/lib/components/ui/scroll-area/index.d.ts +1 -0
  28. package/dist/lib/components/ui/scroll-area/index.js +1 -0
  29. package/dist/lib/components/ui/separator/index.d.ts +1 -0
  30. package/dist/lib/components/ui/separator/index.js +1 -0
  31. package/dist/lib/components/ui/sheet/index.d.ts +3 -0
  32. package/dist/lib/components/ui/sheet/index.js +3 -0
  33. package/dist/lib/components/ui/tabs/index.d.ts +5 -0
  34. package/dist/lib/components/ui/tabs/index.js +7 -0
  35. package/dist/lib/open-links.d.ts +22 -0
  36. package/dist/lib/open-links.js +33 -0
  37. package/dist/lib/routes/docs/[...slug]/+page.d.ts +25 -0
  38. package/dist/lib/routes/docs/[...slug]/+page.js +109 -0
  39. package/dist/lib/utils.d.ts +5 -0
  40. package/dist/lib/utils.js +5 -0
  41. package/dist/mcp.d.ts +24 -0
  42. package/dist/mcp.js +155 -0
  43. package/dist/scan.d.ts +15 -0
  44. package/dist/scan.js +72 -0
  45. package/dist/seo.d.ts +63 -0
  46. package/dist/seo.js +160 -0
  47. package/dist/tiramisu-grammar.d.ts +2 -0
  48. package/dist/tiramisu-grammar.js +77 -0
  49. package/dist/types.d.ts +66 -0
  50. package/dist/types.js +1 -0
  51. package/dist/vite.d.ts +33 -0
  52. package/dist/vite.js +406 -0
  53. package/package.json +74 -0
  54. package/src/config.ts +133 -0
  55. package/src/highlight.ts +110 -0
  56. package/src/index.ts +6 -0
  57. package/src/lib/components/DocPage.svelte +430 -0
  58. package/src/lib/components/DocsLayout.svelte +145 -0
  59. package/src/lib/components/Footer.svelte +26 -0
  60. package/src/lib/components/Navbar.svelte +117 -0
  61. package/src/lib/components/PageFooter.svelte +63 -0
  62. package/src/lib/components/PrevNextNav.svelte +83 -0
  63. package/src/lib/components/SearchDialog.svelte +130 -0
  64. package/src/lib/components/Sidebar.svelte +237 -0
  65. package/src/lib/components/TableOfContents.svelte +50 -0
  66. package/src/lib/components/TopBar.svelte +407 -0
  67. package/src/lib/components/index.ts +19 -0
  68. package/src/lib/components/tiramisu/Accordion.svelte +16 -0
  69. package/src/lib/components/tiramisu/Badge.svelte +16 -0
  70. package/src/lib/components/tiramisu/Callout.svelte +26 -0
  71. package/src/lib/components/tiramisu/CodeBlock.svelte +56 -0
  72. package/src/lib/components/tiramisu/CodeTabs.svelte +123 -0
  73. package/src/lib/components/tiramisu/Demo.svelte +15 -0
  74. package/src/lib/components/tiramisu/FileTree.svelte +67 -0
  75. package/src/lib/components/tiramisu/MathBlock.svelte +26 -0
  76. package/src/lib/components/tiramisu/Mermaid.svelte +30 -0
  77. package/src/lib/components/tiramisu/NavCard.svelte +49 -0
  78. package/src/lib/components/tiramisu/Steps.svelte +60 -0
  79. package/src/lib/components/tiramisu/Tabs.svelte +87 -0
  80. package/src/lib/components/tiramisu/ZoomImage.svelte +114 -0
  81. package/src/lib/components/tiramisu/lang-icons.ts +81 -0
  82. package/src/lib/open-links.ts +50 -0
  83. package/src/lib/routes/docs/[...slug]/+page.svelte +26 -0
  84. package/src/lib/routes/docs/[...slug]/+page.ts +117 -0
  85. package/src/lib/styles/theme.css +222 -0
  86. package/src/lib/utils.ts +10 -0
  87. package/src/mcp.ts +180 -0
  88. package/src/scan.ts +92 -0
  89. package/src/seo.ts +193 -0
  90. package/src/tiramisu-grammar.ts +80 -0
  91. package/src/types.ts +71 -0
  92. package/src/virtual.d.ts +11 -0
  93. package/src/vite.ts +478 -0
  94. package/tests/config.test.ts +60 -0
  95. package/tests/mcp.test.ts +116 -0
  96. package/tests/scan.test.ts +48 -0
  97. package/tests/seo.test.ts +174 -0
  98. package/tests/vite.test.ts +283 -0
  99. package/tsconfig.json +19 -0
@@ -0,0 +1,50 @@
1
+ <script lang="ts">
2
+ import type { Heading } from "@tiramisu-docs/core"
3
+
4
+ let { headings }: { headings: Heading[] } = $props()
5
+ let activeId = $state("")
6
+
7
+ $effect(() => {
8
+ const observer = new IntersectionObserver(
9
+ (entries: IntersectionObserverEntry[]) => {
10
+ for (const entry of entries) {
11
+ if (entry.isIntersecting) {
12
+ activeId = entry.target.id
13
+ }
14
+ }
15
+ },
16
+ { rootMargin: "-64px 0px -75% 0px" }
17
+ )
18
+
19
+ const elements = headings
20
+ .map((h: Heading) => document.getElementById(h.id))
21
+ .filter((el): el is HTMLElement => el !== null)
22
+
23
+ for (const el of elements) observer.observe(el)
24
+
25
+ return () => observer.disconnect()
26
+ })
27
+ </script>
28
+
29
+ <nav>
30
+ <p class="mb-3 flex items-center gap-1.5 text-sm font-medium text-foreground">
31
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="opacity-60"><line x1="8" x2="21" y1="6" y2="6"/><line x1="8" x2="21" y1="12" y2="12"/><line x1="8" x2="21" y1="18" y2="18"/><line x1="3" x2="3.01" y1="6" y2="6"/><line x1="3" x2="3.01" y1="12" y2="12"/><line x1="3" x2="3.01" y1="18" y2="18"/></svg>
32
+ On this page
33
+ </p>
34
+ <ul class="space-y-0.5 border-l border-border">
35
+ {#each headings as heading}
36
+ {@const active = activeId === heading.id}
37
+ <li style="padding-left: {(heading.level - 2) * 0.75}rem">
38
+ <a
39
+ href="#{heading.id}"
40
+ class="block -ml-px py-1 pl-3 text-[13px] transition-colors
41
+ {active
42
+ ? 'border-l-2 border-primary font-medium text-primary'
43
+ : 'text-muted-foreground hover:text-foreground'}"
44
+ >
45
+ {heading.text}
46
+ </a>
47
+ </li>
48
+ {/each}
49
+ </ul>
50
+ </nav>
@@ -0,0 +1,407 @@
1
+ <script lang="ts">
2
+ import { page } from "$app/stores";
3
+ import type { ResolvedConfig, LocaleConfig } from "../../config.js";
4
+ import type { ResolvedSection } from "../../types.js";
5
+
6
+ let {
7
+ config,
8
+ sections = [],
9
+ locale,
10
+ locales,
11
+ onSearchClick,
12
+ onMenuClick,
13
+ }: { config: ResolvedConfig; sections?: ResolvedSection[]; locale?: string; locales?: LocaleConfig[]; onSearchClick: () => void; onMenuClick?: () => void } = $props();
14
+
15
+ let dark = $state(false);
16
+
17
+ function initTheme() {
18
+ if (typeof window === "undefined") return;
19
+ const stored = window.localStorage.getItem("theme");
20
+ dark =
21
+ stored === "dark" ||
22
+ (!stored && window.matchMedia("(prefers-color-scheme: dark)").matches);
23
+ document.documentElement.classList.toggle("dark", dark);
24
+ }
25
+
26
+ function toggleTheme() {
27
+ if (typeof window === "undefined") return;
28
+ dark = !dark;
29
+ document.documentElement.classList.toggle("dark", dark);
30
+ window.localStorage.setItem("theme", dark ? "dark" : "light");
31
+ }
32
+
33
+ function isActiveSection(section: ResolvedSection, pathname: string): boolean {
34
+ if (section.path) {
35
+ const prefix = locale
36
+ ? `/docs/${locale}/${section.path}`
37
+ : `/docs/${section.path}`;
38
+ return pathname.startsWith(prefix);
39
+ }
40
+ if (section.children) {
41
+ return section.children.some((c: ResolvedSection) => isActiveSection(c, pathname));
42
+ }
43
+ return false;
44
+ }
45
+
46
+ function sectionHref(section: ResolvedSection): string {
47
+ if (section.href) return section.href;
48
+ if (section.path) {
49
+ return locale
50
+ ? `/docs/${locale}/${section.path}`
51
+ : `/docs/${section.path}`;
52
+ }
53
+ return locale ? `/docs/${locale}` : "/docs";
54
+ }
55
+
56
+ $effect(() => {
57
+ initTheme();
58
+ });
59
+
60
+ let openDropdown: string | null = $state(null);
61
+ </script>
62
+
63
+ <!-- Row 1: logo, hamburger (mobile), search, theme, language -->
64
+ <header
65
+ class="sticky top-0 z-50 border-b bg-background/95 backdrop-blur-md supports-[backdrop-filter]:bg-background/85"
66
+ >
67
+ <div class="mx-auto flex h-14 max-w-[90rem] items-center gap-4 px-4">
68
+ <!-- Hamburger (mobile only, shown when onMenuClick is provided) -->
69
+ {#if onMenuClick}
70
+ <button
71
+ onclick={onMenuClick}
72
+ class="inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground lg:hidden"
73
+ aria-label="Toggle menu"
74
+ >
75
+ <svg
76
+ xmlns="http://www.w3.org/2000/svg"
77
+ width="20"
78
+ height="20"
79
+ viewBox="0 0 24 24"
80
+ fill="none"
81
+ stroke="currentColor"
82
+ stroke-width="2"
83
+ stroke-linecap="round"
84
+ stroke-linejoin="round"
85
+ >
86
+ <line x1="4" x2="20" y1="12" y2="12"></line>
87
+ <line x1="4" x2="20" y1="6" y2="6"></line>
88
+ <line x1="4" x2="20" y1="18" y2="18"></line>
89
+ </svg>
90
+ </button>
91
+ {/if}
92
+
93
+ <!-- Logo + title -->
94
+ <a href="/" class="flex items-center gap-2">
95
+ {#if config.logo.light || config.logo.dark}
96
+ <img src={config.logo.light} alt="" class="h-5 w-5 dark:hidden" />
97
+ <img
98
+ src={config.logo.dark || config.logo.light}
99
+ alt=""
100
+ class="hidden h-5 w-5 dark:block"
101
+ />
102
+ {/if}
103
+ <span class="text-sm font-bold">{config.title}</span>
104
+ </a>
105
+
106
+ <div class="flex-1"></div>
107
+
108
+ <!-- Search bar -->
109
+ <button
110
+ onclick={onSearchClick}
111
+ class="hidden h-8 w-56 items-center gap-2 rounded-md border bg-muted/40 px-2.5 text-[13px] text-muted-foreground transition-colors hover:bg-muted sm:flex"
112
+ >
113
+ <svg
114
+ xmlns="http://www.w3.org/2000/svg"
115
+ width="14"
116
+ height="14"
117
+ viewBox="0 0 24 24"
118
+ fill="none"
119
+ stroke="currentColor"
120
+ stroke-width="2"
121
+ stroke-linecap="round"
122
+ stroke-linejoin="round"
123
+ class="shrink-0 opacity-60"
124
+ >
125
+ <circle cx="11" cy="11" r="8"></circle>
126
+ <path d="m21 21-4.3-4.3"></path>
127
+ </svg>
128
+ <span class="flex-1 text-left">Search</span>
129
+ <div class="flex items-center gap-0.5">
130
+ <kbd
131
+ class="rounded border bg-background px-1 font-mono text-[10px] text-muted-foreground"
132
+ >⌘</kbd
133
+ >
134
+ <kbd
135
+ class="rounded border bg-background px-1 font-mono text-[10px] text-muted-foreground"
136
+ >K</kbd
137
+ >
138
+ </div>
139
+ </button>
140
+ <!-- Search icon (mobile) -->
141
+ <button
142
+ onclick={onSearchClick}
143
+ class="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground sm:hidden"
144
+ aria-label="Search"
145
+ >
146
+ <svg
147
+ xmlns="http://www.w3.org/2000/svg"
148
+ width="16"
149
+ height="16"
150
+ viewBox="0 0 24 24"
151
+ fill="none"
152
+ stroke="currentColor"
153
+ stroke-width="2"
154
+ stroke-linecap="round"
155
+ stroke-linejoin="round"
156
+ >
157
+ <circle cx="11" cy="11" r="8"></circle>
158
+ <path d="m21 21-4.3-4.3"></path>
159
+ </svg>
160
+ </button>
161
+
162
+ {#if locales?.length > 1}
163
+ <div class="relative">
164
+ <button
165
+ onclick={() =>
166
+ (openDropdown = openDropdown === "lang" ? null : "lang")}
167
+ class="inline-flex h-8 items-center gap-1.5 rounded-md px-2 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground"
168
+ >
169
+ {#each locales as loc}
170
+ {#if loc.code === locale}
171
+ {loc.flag ?? ""} {loc.label}
172
+ {/if}
173
+ {/each}
174
+ <svg
175
+ xmlns="http://www.w3.org/2000/svg"
176
+ width="12"
177
+ height="12"
178
+ viewBox="0 0 24 24"
179
+ fill="none"
180
+ stroke="currentColor"
181
+ stroke-width="2"
182
+ stroke-linecap="round"
183
+ stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg
184
+ >
185
+ </button>
186
+ {#if openDropdown === "lang"}
187
+ <div
188
+ class="absolute right-0 top-full z-50 mt-1 min-w-[10rem] rounded-md border bg-popover p-1 shadow-md"
189
+ >
190
+ {#each locales as loc}
191
+ {@const currentPath = $page.url.pathname}
192
+ {@const newPath = currentPath.replace(
193
+ `/docs/${locale}`,
194
+ `/docs/${loc.code}`,
195
+ )}
196
+ <a
197
+ href={newPath}
198
+ onclick={() => (openDropdown = null)}
199
+ class="flex items-center gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent
200
+ {loc.code === locale
201
+ ? 'font-medium text-foreground'
202
+ : 'text-muted-foreground'}"
203
+ >
204
+ {#if loc.flag}<span>{loc.flag}</span>{/if}
205
+ {loc.label}
206
+ </a>
207
+ {/each}
208
+ </div>
209
+ {/if}
210
+ </div>
211
+ {/if}
212
+
213
+ {#if config.github?.repo}
214
+ <a
215
+ href="https://github.com/{config.github.repo}"
216
+ target="_blank"
217
+ rel="noopener noreferrer"
218
+ class="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
219
+ aria-label="GitHub"
220
+ >
221
+ <iconify-icon icon="mdi:github" width="18" height="18"></iconify-icon>
222
+ </a>
223
+ {/if}
224
+
225
+ <button
226
+ onclick={toggleTheme}
227
+ class="inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-accent-foreground"
228
+ aria-label="Toggle dark mode"
229
+ >
230
+ <svg
231
+ class="h-4 w-4 dark:hidden"
232
+ xmlns="http://www.w3.org/2000/svg"
233
+ viewBox="0 0 24 24"
234
+ fill="none"
235
+ stroke="currentColor"
236
+ stroke-width="2"
237
+ stroke-linecap="round"
238
+ stroke-linejoin="round"
239
+ >
240
+ <circle cx="12" cy="12" r="4"></circle>
241
+ <path d="M12 2v2"></path><path d="M12 20v2"></path>
242
+ <path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"
243
+ ></path>
244
+ <path d="M2 12h2"></path><path d="M20 12h2"></path>
245
+ <path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"
246
+ ></path>
247
+ </svg>
248
+ <svg
249
+ class="hidden h-4 w-4 dark:block"
250
+ xmlns="http://www.w3.org/2000/svg"
251
+ viewBox="0 0 24 24"
252
+ fill="none"
253
+ stroke="currentColor"
254
+ stroke-width="2"
255
+ stroke-linecap="round"
256
+ stroke-linejoin="round"
257
+ >
258
+ <path
259
+ d="M21.752 15.002A9.72 9.72 0 0 1 18 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 0 0 3 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 0 0 9.002-5.998Z"
260
+ />
261
+ </svg>
262
+ </button>
263
+ </div>
264
+
265
+ <!-- Row 2: section tabs (scrollable) -->
266
+ {#if sections?.length > 0}
267
+ <div class="border-t">
268
+ <nav
269
+ class="scrollbar-none mx-auto flex max-w-[90rem] items-center gap-0.5 overflow-x-auto px-4 py-1"
270
+ >
271
+ {#each sections as section}
272
+ {#if section.children}
273
+ <!-- Dropdown section -->
274
+ <div class="relative">
275
+ <button
276
+ onclick={() =>
277
+ (openDropdown =
278
+ openDropdown === section.label ? null : section.label)}
279
+ class="flex shrink-0 items-center gap-1.5 rounded-md px-3 py-1.5 text-sm whitespace-nowrap transition-all duration-150
280
+ {isActiveSection(section, $page.url.pathname)
281
+ ? 'text-foreground font-medium'
282
+ : 'text-muted-foreground hover:text-foreground hover:bg-accent'}"
283
+ >
284
+ {#if section.icon}
285
+ <iconify-icon
286
+ icon={section.icon.includes(":")
287
+ ? section.icon
288
+ : `lucide:${section.icon}`}
289
+ width="14"
290
+ height="14"
291
+ class="shrink-0"
292
+ ></iconify-icon>
293
+ {/if}
294
+ {section.label}
295
+ <svg
296
+ xmlns="http://www.w3.org/2000/svg"
297
+ width="12"
298
+ height="12"
299
+ viewBox="0 0 24 24"
300
+ fill="none"
301
+ stroke="currentColor"
302
+ stroke-width="2"
303
+ stroke-linecap="round"
304
+ stroke-linejoin="round"><path d="m6 9 6 6 6-6"></path></svg
305
+ >
306
+ </button>
307
+ {#if openDropdown === section.label}
308
+ <div
309
+ class="absolute left-0 top-full z-50 mt-0.5 min-w-[10rem] rounded-md border bg-popover p-1 shadow-md"
310
+ >
311
+ {#each section.children as child}
312
+ <a
313
+ href={sectionHref(child)}
314
+ onclick={() => (openDropdown = null)}
315
+ class="block rounded-sm px-3 py-1.5 text-sm hover:bg-accent
316
+ {isActiveSection(child, $page.url.pathname)
317
+ ? 'font-medium text-foreground'
318
+ : 'text-muted-foreground'}"
319
+ >
320
+ {child.label}
321
+ </a>
322
+ {/each}
323
+ </div>
324
+ {/if}
325
+ </div>
326
+ {:else if section.href}
327
+ <!-- External link -->
328
+ <a
329
+ href={section.href}
330
+ target="_blank"
331
+ rel="noopener noreferrer"
332
+ class="flex shrink-0 items-center gap-1.5 rounded-md px-3 py-1.5 text-sm whitespace-nowrap text-muted-foreground transition-all duration-150 hover:text-foreground hover:bg-accent"
333
+ >
334
+ {#if section.icon}
335
+ <iconify-icon
336
+ icon={section.icon.includes(":")
337
+ ? section.icon
338
+ : `lucide:${section.icon}`}
339
+ width="14"
340
+ height="14"
341
+ class="shrink-0"
342
+ ></iconify-icon>
343
+ {/if}
344
+ {section.label}
345
+ <svg
346
+ xmlns="http://www.w3.org/2000/svg"
347
+ width="10"
348
+ height="10"
349
+ viewBox="0 0 24 24"
350
+ fill="none"
351
+ stroke="currentColor"
352
+ stroke-width="2"
353
+ stroke-linecap="round"
354
+ stroke-linejoin="round"
355
+ ><path
356
+ d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"
357
+ ></path><polyline points="15 3 21 3 21 9"></polyline><line
358
+ x1="10"
359
+ x2="21"
360
+ y1="14"
361
+ y2="3"
362
+ ></line></svg
363
+ >
364
+ </a>
365
+ {:else}
366
+ <!-- Path section -->
367
+ <a
368
+ href={sectionHref(section)}
369
+ class="flex shrink-0 items-center gap-1.5 rounded-md px-3 py-1.5 text-sm whitespace-nowrap transition-all duration-150
370
+ {isActiveSection(section, $page.url.pathname)
371
+ ? 'text-foreground font-medium'
372
+ : 'text-muted-foreground hover:text-foreground hover:bg-accent'}"
373
+ >
374
+ {#if section.icon}
375
+ <iconify-icon
376
+ icon={section.icon.includes(":")
377
+ ? section.icon
378
+ : `lucide:${section.icon}`}
379
+ width="14"
380
+ height="14"
381
+ class="shrink-0"
382
+ ></iconify-icon>
383
+ {/if}
384
+ {section.label}
385
+ </a>
386
+ {/if}
387
+ {/each}
388
+ </nav>
389
+ </div>
390
+ {/if}
391
+ </header>
392
+
393
+ <svelte:window
394
+ onclick={(e: MouseEvent) => {
395
+ if (openDropdown && (e.target as HTMLElement).closest && !(e.target as HTMLElement).closest(".relative")) openDropdown = null;
396
+ }}
397
+ />
398
+
399
+ <style>
400
+ .scrollbar-none {
401
+ scrollbar-width: none;
402
+ -ms-overflow-style: none;
403
+ }
404
+ .scrollbar-none::-webkit-scrollbar {
405
+ display: none;
406
+ }
407
+ </style>
@@ -0,0 +1,19 @@
1
+ // Layout
2
+ export { default as DocsLayout } from "./DocsLayout.svelte"
3
+ export { default as DocPage } from "./DocPage.svelte"
4
+ export { default as PrevNextNav } from "./PrevNextNav.svelte"
5
+
6
+ // Tiramisu built-ins
7
+ export { default as Accordion } from "./tiramisu/Accordion.svelte"
8
+ export { default as Badge } from "./tiramisu/Badge.svelte"
9
+ export { default as Callout } from "./tiramisu/Callout.svelte"
10
+ export { default as CodeBlock } from "./tiramisu/CodeBlock.svelte"
11
+ export { default as CodeTabs } from "./tiramisu/CodeTabs.svelte"
12
+ export { default as Demo } from "./tiramisu/Demo.svelte"
13
+ export { default as FileTree } from "./tiramisu/FileTree.svelte"
14
+ export { default as MathBlock } from "./tiramisu/MathBlock.svelte"
15
+ export { default as Mermaid } from "./tiramisu/Mermaid.svelte"
16
+ export { default as NavCard } from "./tiramisu/NavCard.svelte"
17
+ export { default as Steps } from "./tiramisu/Steps.svelte"
18
+ export { default as Tabs } from "./tiramisu/Tabs.svelte"
19
+ export { default as ZoomImage } from "./tiramisu/ZoomImage.svelte"
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import Collapsible from "$lib/components/ui/collapsible/collapsible.svelte"
3
+
4
+ let { title = "Details", children }: { title?: string; children?: import("svelte").Snippet } =
5
+ $props()
6
+ let open = $state(false)
7
+ </script>
8
+
9
+ <Collapsible bind:open class="my-2 rounded-lg border px-4 py-3">
10
+ {#snippet trigger()}
11
+ <span class="text-sm font-medium">{title}</span>
12
+ {/snippet}
13
+ <div class="pt-2 text-sm text-muted-foreground">
14
+ {@render children?.()}
15
+ </div>
16
+ </Collapsible>
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+
4
+ let { variant = "default", children }: { variant?: string; children: Snippet } = $props()
5
+
6
+ const variants = {
7
+ default: "bg-primary/10 text-primary border-primary/20",
8
+ secondary: "bg-muted text-muted-foreground border-border",
9
+ destructive: "bg-destructive/10 text-destructive border-destructive/20",
10
+ outline: "bg-transparent text-foreground border-border",
11
+ }
12
+ </script>
13
+
14
+ <span class="inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors {variants[variant] ?? variants.default}">
15
+ {@render children()}
16
+ </span>
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import * as Alert from "$lib/components/ui/alert/index.js"
4
+
5
+ let { type = "info", children }: { type?: string; children: Snippet } = $props()
6
+
7
+ const variantMap = { info: "info", warning: "warning", error: "destructive", success: "success" }
8
+ const variant = $derived(variantMap[type] ?? "info")
9
+ const title = $derived(type.charAt(0).toUpperCase() + type.slice(1))
10
+ </script>
11
+
12
+ <Alert.Root {variant} class="my-4">
13
+ {#if type === "info"}
14
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" x2="12" y1="16" y2="12"/><line x1="12" x2="12.01" y1="8" y2="8"/></svg>
15
+ {:else if type === "warning"}
16
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><line x1="12" x2="12" y1="9" y2="13"/><line x1="12" x2="12.01" y1="17" y2="17"/></svg>
17
+ {:else if type === "error"}
18
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>
19
+ {:else if type === "success"}
20
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>
21
+ {/if}
22
+ <Alert.Title>{title}</Alert.Title>
23
+ <Alert.Description class="block">
24
+ {@render children()}
25
+ </Alert.Description>
26
+ </Alert.Root>
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import { getLangIcon } from "./lang-icons.js"
4
+
5
+ let { language = "", icon = "", code = "", children }: { language?: string; icon?: string; code?: string; children?: Snippet } = $props()
6
+ let copied = $state(false)
7
+
8
+ const langIcon = $derived(icon || getLangIcon(language))
9
+
10
+ async function copyCode() {
11
+ const text = code.replace(/<[^>]*>/g, "")
12
+ await navigator.clipboard.writeText(text)
13
+ copied = true
14
+ setTimeout(() => (copied = false), 2000)
15
+ }
16
+ </script>
17
+
18
+ <div class="group relative my-4 overflow-hidden rounded-lg border border-border">
19
+ {#if language}
20
+ <div class="flex items-center justify-between border-b border-border bg-muted/50 px-3 py-1.5">
21
+ <span class="flex items-center gap-1.5 text-xs text-muted-foreground">
22
+ {#if langIcon}
23
+ <iconify-icon icon={langIcon} width="14" height="14" class="shrink-0"></iconify-icon>
24
+ {/if}
25
+ {language}
26
+ </span>
27
+ <button
28
+ onclick={copyCode}
29
+ class="inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground group-hover:opacity-100"
30
+ aria-label="Copy code"
31
+ >
32
+ {#if copied}
33
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
34
+ Copied
35
+ {:else}
36
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
37
+ Copy
38
+ {/if}
39
+ </button>
40
+ </div>
41
+ {/if}
42
+ <pre class="overflow-x-auto p-4 text-sm leading-relaxed"><code>{@html code}</code></pre>
43
+ {#if !language}
44
+ <button
45
+ onclick={copyCode}
46
+ class="absolute right-2 top-2 inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs text-muted-foreground opacity-0 transition-opacity hover:text-foreground group-hover:opacity-100"
47
+ aria-label="Copy code"
48
+ >
49
+ {#if copied}
50
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
51
+ {:else}
52
+ <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
53
+ {/if}
54
+ </button>
55
+ {/if}
56
+ </div>