@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.
- package/README.md +103 -0
- package/components.json +14 -0
- package/dist/bin/mcp.d.ts +2 -0
- package/dist/bin/mcp.js +4 -0
- package/dist/config.d.ts +99 -0
- package/dist/config.js +36 -0
- package/dist/highlight.d.ts +10 -0
- package/dist/highlight.js +93 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/lib/components/index.d.ts +16 -0
- package/dist/lib/components/index.js +18 -0
- package/dist/lib/components/tiramisu/lang-icons.d.ts +4 -0
- package/dist/lib/components/tiramisu/lang-icons.js +77 -0
- package/dist/lib/components/ui/alert/index.d.ts +5 -0
- package/dist/lib/components/ui/alert/index.js +6 -0
- package/dist/lib/components/ui/badge/index.d.ts +2 -0
- package/dist/lib/components/ui/badge/index.js +1 -0
- package/dist/lib/components/ui/button/index.d.ts +4 -0
- package/dist/lib/components/ui/button/index.js +2 -0
- package/dist/lib/components/ui/card/index.d.ts +8 -0
- package/dist/lib/components/ui/card/index.js +10 -0
- package/dist/lib/components/ui/collapsible/index.d.ts +1 -0
- package/dist/lib/components/ui/collapsible/index.js +1 -0
- package/dist/lib/components/ui/dropdown-menu/index.d.ts +18 -0
- package/dist/lib/components/ui/dropdown-menu/index.js +18 -0
- package/dist/lib/components/ui/scroll-area/index.d.ts +1 -0
- package/dist/lib/components/ui/scroll-area/index.js +1 -0
- package/dist/lib/components/ui/separator/index.d.ts +1 -0
- package/dist/lib/components/ui/separator/index.js +1 -0
- package/dist/lib/components/ui/sheet/index.d.ts +3 -0
- package/dist/lib/components/ui/sheet/index.js +3 -0
- package/dist/lib/components/ui/tabs/index.d.ts +5 -0
- package/dist/lib/components/ui/tabs/index.js +7 -0
- package/dist/lib/open-links.d.ts +22 -0
- package/dist/lib/open-links.js +33 -0
- package/dist/lib/routes/docs/[...slug]/+page.d.ts +25 -0
- package/dist/lib/routes/docs/[...slug]/+page.js +109 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +5 -0
- package/dist/mcp.d.ts +24 -0
- package/dist/mcp.js +155 -0
- package/dist/scan.d.ts +15 -0
- package/dist/scan.js +72 -0
- package/dist/seo.d.ts +63 -0
- package/dist/seo.js +160 -0
- package/dist/tiramisu-grammar.d.ts +2 -0
- package/dist/tiramisu-grammar.js +77 -0
- package/dist/types.d.ts +66 -0
- package/dist/types.js +1 -0
- package/dist/vite.d.ts +33 -0
- package/dist/vite.js +406 -0
- package/package.json +74 -0
- package/src/config.ts +133 -0
- package/src/highlight.ts +110 -0
- package/src/index.ts +6 -0
- package/src/lib/components/DocPage.svelte +430 -0
- package/src/lib/components/DocsLayout.svelte +145 -0
- package/src/lib/components/Footer.svelte +26 -0
- package/src/lib/components/Navbar.svelte +117 -0
- package/src/lib/components/PageFooter.svelte +63 -0
- package/src/lib/components/PrevNextNav.svelte +83 -0
- package/src/lib/components/SearchDialog.svelte +130 -0
- package/src/lib/components/Sidebar.svelte +237 -0
- package/src/lib/components/TableOfContents.svelte +50 -0
- package/src/lib/components/TopBar.svelte +407 -0
- package/src/lib/components/index.ts +19 -0
- package/src/lib/components/tiramisu/Accordion.svelte +16 -0
- package/src/lib/components/tiramisu/Badge.svelte +16 -0
- package/src/lib/components/tiramisu/Callout.svelte +26 -0
- package/src/lib/components/tiramisu/CodeBlock.svelte +56 -0
- package/src/lib/components/tiramisu/CodeTabs.svelte +123 -0
- package/src/lib/components/tiramisu/Demo.svelte +15 -0
- package/src/lib/components/tiramisu/FileTree.svelte +67 -0
- package/src/lib/components/tiramisu/MathBlock.svelte +26 -0
- package/src/lib/components/tiramisu/Mermaid.svelte +30 -0
- package/src/lib/components/tiramisu/NavCard.svelte +49 -0
- package/src/lib/components/tiramisu/Steps.svelte +60 -0
- package/src/lib/components/tiramisu/Tabs.svelte +87 -0
- package/src/lib/components/tiramisu/ZoomImage.svelte +114 -0
- package/src/lib/components/tiramisu/lang-icons.ts +81 -0
- package/src/lib/open-links.ts +50 -0
- package/src/lib/routes/docs/[...slug]/+page.svelte +26 -0
- package/src/lib/routes/docs/[...slug]/+page.ts +117 -0
- package/src/lib/styles/theme.css +222 -0
- package/src/lib/utils.ts +10 -0
- package/src/mcp.ts +180 -0
- package/src/scan.ts +92 -0
- package/src/seo.ts +193 -0
- package/src/tiramisu-grammar.ts +80 -0
- package/src/types.ts +71 -0
- package/src/virtual.d.ts +11 -0
- package/src/vite.ts +478 -0
- package/tests/config.test.ts +60 -0
- package/tests/mcp.test.ts +116 -0
- package/tests/scan.test.ts +48 -0
- package/tests/seo.test.ts +174 -0
- package/tests/vite.test.ts +283 -0
- 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>
|