@axerity/cli 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 (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/axerity.default.json +135 -0
  4. package/axerity.schema.json +268 -0
  5. package/bin/axerity.js +305 -0
  6. package/mdsvex.config.js +261 -0
  7. package/package.json +103 -0
  8. package/scripts/prepare-engine.mjs +20 -0
  9. package/src/app.d.ts +17 -0
  10. package/src/app.html +39 -0
  11. package/src/content/demo/api/meta.json +5 -0
  12. package/src/content/demo/api/pet/add-pet.md +105 -0
  13. package/src/content/demo/api/pet/delete-pet.md +70 -0
  14. package/src/content/demo/api/pet/find-by-status.md +72 -0
  15. package/src/content/demo/api/pet/find-by-tags.md +64 -0
  16. package/src/content/demo/api/pet/get-pet.md +99 -0
  17. package/src/content/demo/api/pet/meta.json +15 -0
  18. package/src/content/demo/api/pet/pet-object.md +112 -0
  19. package/src/content/demo/api/pet/update-pet-with-form.md +69 -0
  20. package/src/content/demo/api/pet/update-pet.md +79 -0
  21. package/src/content/demo/api/pet/upload-image.md +79 -0
  22. package/src/content/demo/api/store/delete-order.md +62 -0
  23. package/src/content/demo/api/store/get-order.md +70 -0
  24. package/src/content/demo/api/store/inventory.md +54 -0
  25. package/src/content/demo/api/store/meta.json +5 -0
  26. package/src/content/demo/api/store/order-object.md +77 -0
  27. package/src/content/demo/api/store/place-order.md +83 -0
  28. package/src/content/demo/api/user/create-user.md +69 -0
  29. package/src/content/demo/api/user/create-with-list.md +57 -0
  30. package/src/content/demo/api/user/delete-user.md +61 -0
  31. package/src/content/demo/api/user/get-user.md +69 -0
  32. package/src/content/demo/api/user/login.md +80 -0
  33. package/src/content/demo/api/user/logout.md +45 -0
  34. package/src/content/demo/api/user/meta.json +14 -0
  35. package/src/content/demo/api/user/update-user.md +69 -0
  36. package/src/content/demo/api/user/user-object.md +85 -0
  37. package/src/content/demo/changelog.md +44 -0
  38. package/src/content/demo/components/accordion.md +70 -0
  39. package/src/content/demo/components/api.md +185 -0
  40. package/src/content/demo/components/badge.md +34 -0
  41. package/src/content/demo/components/callout.md +83 -0
  42. package/src/content/demo/components/cards.md +88 -0
  43. package/src/content/demo/components/code-group.md +55 -0
  44. package/src/content/demo/components/columns.md +42 -0
  45. package/src/content/demo/components/frame.md +51 -0
  46. package/src/content/demo/components/icon.md +54 -0
  47. package/src/content/demo/components/kbd.md +28 -0
  48. package/src/content/demo/components/meta.json +26 -0
  49. package/src/content/demo/components/roadmap.md +86 -0
  50. package/src/content/demo/components/steps.md +72 -0
  51. package/src/content/demo/components/tabs.md +146 -0
  52. package/src/content/demo/components/tooltip.md +44 -0
  53. package/src/content/demo/components/tree.md +83 -0
  54. package/src/content/demo/components/type-table.md +77 -0
  55. package/src/content/demo/components/update.md +48 -0
  56. package/src/content/demo/components/video.md +56 -0
  57. package/src/content/demo/components/webhooks.md +109 -0
  58. package/src/content/demo/components/websockets.md +101 -0
  59. package/src/content/demo/configuration/ai.md +40 -0
  60. package/src/content/demo/configuration/cli.md +46 -0
  61. package/src/content/demo/configuration/deployment.md +105 -0
  62. package/src/content/demo/configuration/index.md +92 -0
  63. package/src/content/demo/configuration/layouts.md +51 -0
  64. package/src/content/demo/configuration/meta.json +5 -0
  65. package/src/content/demo/configuration/navigation.md +167 -0
  66. package/src/content/demo/configuration/openapi.md +103 -0
  67. package/src/content/demo/configuration/search.md +36 -0
  68. package/src/content/demo/index.md +59 -0
  69. package/src/content/demo/installation.md +49 -0
  70. package/src/content/demo/meta.json +15 -0
  71. package/src/content/demo/quick-start.md +47 -0
  72. package/src/content/demo/theming/advanced.md +116 -0
  73. package/src/content/demo/theming/code.md +66 -0
  74. package/src/content/demo/theming/colors.md +103 -0
  75. package/src/content/demo/theming/index.md +88 -0
  76. package/src/content/demo/theming/layout.md +71 -0
  77. package/src/content/demo/theming/meta.json +5 -0
  78. package/src/content/demo/theming/themes.md +99 -0
  79. package/src/content/demo/theming/typography.md +83 -0
  80. package/src/content/demo/writing/code-blocks.md +154 -0
  81. package/src/content/demo/writing/diagrams.md +44 -0
  82. package/src/content/demo/writing/frontmatter.md +33 -0
  83. package/src/content/demo/writing/markdown.md +62 -0
  84. package/src/content/demo/writing/meta.json +5 -0
  85. package/src/hooks.server.ts +49 -0
  86. package/src/lib/assets/favicon.svg +1 -0
  87. package/src/lib/base.ts +12 -0
  88. package/src/lib/components/DynamicIcon.svelte +26 -0
  89. package/src/lib/components/docs/Analytics.svelte +38 -0
  90. package/src/lib/components/docs/Banner.svelte +44 -0
  91. package/src/lib/components/docs/Breadcrumbs.svelte +38 -0
  92. package/src/lib/components/docs/CopyPageMenu.svelte +119 -0
  93. package/src/lib/components/docs/DocsLayout.svelte +192 -0
  94. package/src/lib/components/docs/Footer.svelte +60 -0
  95. package/src/lib/components/docs/Mermaid.svelte +39 -0
  96. package/src/lib/components/docs/Navbar.svelte +144 -0
  97. package/src/lib/components/docs/PageMeta.svelte +35 -0
  98. package/src/lib/components/docs/PageNav.svelte +44 -0
  99. package/src/lib/components/docs/SearchDialog.svelte +182 -0
  100. package/src/lib/components/docs/Sidebar.svelte +85 -0
  101. package/src/lib/components/docs/SidebarDropdown.svelte +56 -0
  102. package/src/lib/components/docs/SidebarFooterLinks.svelte +19 -0
  103. package/src/lib/components/docs/SidebarGroup.svelte +54 -0
  104. package/src/lib/components/docs/SidebarLink.svelte +67 -0
  105. package/src/lib/components/docs/TableOfContents.svelte +77 -0
  106. package/src/lib/components/docs/ThemeToggle.svelte +19 -0
  107. package/src/lib/components/docs/VersionSwitcher.svelte +80 -0
  108. package/src/lib/components/kit/Accordion.svelte +60 -0
  109. package/src/lib/components/kit/AccordionGroup.svelte +13 -0
  110. package/src/lib/components/kit/Badge.svelte +32 -0
  111. package/src/lib/components/kit/Callout.svelte +51 -0
  112. package/src/lib/components/kit/Card.svelte +72 -0
  113. package/src/lib/components/kit/CardGroup.svelte +21 -0
  114. package/src/lib/components/kit/CodeGroup.svelte +65 -0
  115. package/src/lib/components/kit/Columns.svelte +26 -0
  116. package/src/lib/components/kit/Event.svelte +23 -0
  117. package/src/lib/components/kit/EventList.svelte +9 -0
  118. package/src/lib/components/kit/File.svelte +15 -0
  119. package/src/lib/components/kit/Folder.svelte +46 -0
  120. package/src/lib/components/kit/Frame.svelte +81 -0
  121. package/src/lib/components/kit/Icon.svelte +17 -0
  122. package/src/lib/components/kit/Kbd.svelte +11 -0
  123. package/src/lib/components/kit/Roadmap.svelte +15 -0
  124. package/src/lib/components/kit/RoadmapItem.svelte +109 -0
  125. package/src/lib/components/kit/Step.svelte +63 -0
  126. package/src/lib/components/kit/Steps.svelte +16 -0
  127. package/src/lib/components/kit/Tab.svelte +27 -0
  128. package/src/lib/components/kit/Tabs.svelte +75 -0
  129. package/src/lib/components/kit/Tooltip.svelte +33 -0
  130. package/src/lib/components/kit/Tree.svelte +11 -0
  131. package/src/lib/components/kit/TypeTable.svelte +187 -0
  132. package/src/lib/components/kit/Update.svelte +32 -0
  133. package/src/lib/components/kit/Video.svelte +64 -0
  134. package/src/lib/components/kit/accordion-context.ts +1 -0
  135. package/src/lib/components/kit/api/Api.svelte +80 -0
  136. package/src/lib/components/kit/api/ApiExamplePanel.svelte +100 -0
  137. package/src/lib/components/kit/api/ApiField.svelte +124 -0
  138. package/src/lib/components/kit/api/Channel.svelte +121 -0
  139. package/src/lib/components/kit/api/Endpoint.svelte +116 -0
  140. package/src/lib/components/kit/api/Enum.svelte +44 -0
  141. package/src/lib/components/kit/api/EnumValues.svelte +35 -0
  142. package/src/lib/components/kit/api/Expandable.svelte +70 -0
  143. package/src/lib/components/kit/api/Message.svelte +67 -0
  144. package/src/lib/components/kit/api/ObjectExample.svelte +11 -0
  145. package/src/lib/components/kit/api/RequestExample.svelte +11 -0
  146. package/src/lib/components/kit/api/ResponseExample.svelte +11 -0
  147. package/src/lib/components/kit/api/Webhook.svelte +115 -0
  148. package/src/lib/components/kit/api/api-context.ts +15 -0
  149. package/src/lib/components/kit/tabs-context.ts +8 -0
  150. package/src/lib/components/kit/tabs-store.svelte.ts +28 -0
  151. package/src/lib/config/site.ts +34 -0
  152. package/src/lib/content/index.ts +50 -0
  153. package/src/lib/content/raw.ts +21 -0
  154. package/src/lib/content/tree.ts +169 -0
  155. package/src/lib/index.ts +79 -0
  156. package/src/lib/nav-match.ts +23 -0
  157. package/src/lib/openapi/generate.ts +629 -0
  158. package/src/lib/server/og.ts +140 -0
  159. package/src/lib/state/search.svelte.ts +9 -0
  160. package/src/lib/state/theme.svelte.ts +58 -0
  161. package/src/lib/types.ts +216 -0
  162. package/src/params/docpage.ts +3 -0
  163. package/src/params/mdfile.ts +3 -0
  164. package/src/routes/+error.svelte +46 -0
  165. package/src/routes/+layout.svelte +25 -0
  166. package/src/routes/[...path=mdfile]/+server.ts +21 -0
  167. package/src/routes/[...slug=docpage]/+page.svelte +63 -0
  168. package/src/routes/[...slug=docpage]/+page.ts +44 -0
  169. package/src/routes/layout.css +897 -0
  170. package/src/routes/llms-full.txt/+server.ts +22 -0
  171. package/src/routes/llms.txt/+server.ts +20 -0
  172. package/src/routes/og/[...slug]/+server.ts +77 -0
  173. package/src/routes/rss.xml/+server.ts +65 -0
  174. package/src/routes/search.json/+server.ts +54 -0
  175. package/src/routes/sitemap.xml/+server.ts +21 -0
  176. package/static/favicon-dark.svg +6 -0
  177. package/static/favicon-light.svg +6 -0
  178. package/static/favicon.svg +14 -0
  179. package/static/fonts/geist-400.ttf +0 -0
  180. package/static/fonts/geist-600.ttf +0 -0
  181. package/static/fonts/geist-700.ttf +0 -0
  182. package/static/og-image.png +0 -0
  183. package/static/robots.txt +4 -0
  184. package/svelte.config.js +35 -0
  185. package/tsconfig.json +20 -0
  186. package/vite.config.ts +46 -0
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/state';
3
+ import { base } from '$app/paths';
4
+ import { fade } from 'svelte/transition';
5
+ import Copy from '@lucide/svelte/icons/copy';
6
+ import Check from '@lucide/svelte/icons/check';
7
+ import ChevronDown from '@lucide/svelte/icons/chevron-down';
8
+ import FileText from '@lucide/svelte/icons/file-text';
9
+ import ExternalLink from '@lucide/svelte/icons/external-link';
10
+ import { getRawMarkdown } from '$lib/content/raw';
11
+
12
+ let open = $state(false);
13
+ let copied = $state(false);
14
+
15
+ const slug = $derived(page.url.pathname.slice(base.length).replace(/^\//, ''));
16
+
17
+ async function copyPage() {
18
+ const markdown = await getRawMarkdown(slug);
19
+ if (markdown == null) return;
20
+ try {
21
+ await navigator.clipboard.writeText(markdown);
22
+ copied = true;
23
+ setTimeout(() => (copied = false), 1500);
24
+ } catch {
25
+ copied = false;
26
+ }
27
+ open = false;
28
+ }
29
+
30
+ function viewMarkdown() {
31
+ window.open(`${page.url.pathname.replace(/\/$/, '')}.md`, '_blank');
32
+ open = false;
33
+ }
34
+
35
+ function ask(endpoint: string) {
36
+ const prompt = `Read ${page.url.href} and help me with questions about this page.`;
37
+ window.open(`${endpoint}${encodeURIComponent(prompt)}`, '_blank');
38
+ open = false;
39
+ }
40
+ </script>
41
+
42
+ <div class="relative">
43
+ <div class="flex items-center rounded-md border border-border bg-surface text-sm">
44
+ <button
45
+ type="button"
46
+ onclick={copyPage}
47
+ class="flex items-center gap-2 py-1.5 pr-2 pl-3 text-fg-muted transition hover:text-fg"
48
+ >
49
+ {#if copied}
50
+ <Check size={15} class="text-fg" />
51
+ {:else}
52
+ <Copy size={15} />
53
+ {/if}
54
+ <span>{copied ? 'Copied' : 'Copy page'}</span>
55
+ </button>
56
+ <button
57
+ type="button"
58
+ onclick={() => (open = !open)}
59
+ aria-label="More options"
60
+ aria-expanded={open}
61
+ class="border-l border-border px-1.5 py-1.5 text-fg-subtle transition hover:text-fg"
62
+ >
63
+ <ChevronDown size={15} class="transition-transform {open ? 'rotate-180' : ''}" />
64
+ </button>
65
+ </div>
66
+
67
+ {#if open}
68
+ <button
69
+ type="button"
70
+ class="fixed inset-0 z-40 cursor-default"
71
+ aria-label="Close menu"
72
+ onclick={() => (open = false)}
73
+ ></button>
74
+ <div
75
+ class="absolute right-0 z-50 mt-1.5 w-72 overflow-hidden rounded-lg border border-border bg-surface-raised p-1 shadow-lg"
76
+ transition:fade={{ duration: 100 }}
77
+ >
78
+ {#snippet item(
79
+ Icon: typeof Copy,
80
+ title: string,
81
+ subtitle: string,
82
+ onclick: () => void,
83
+ external = false
84
+ )}
85
+ <button
86
+ type="button"
87
+ {onclick}
88
+ class="flex w-full items-start gap-3 rounded-md px-2.5 py-2 text-left transition hover:bg-bg-subtle"
89
+ >
90
+ <Icon size={17} class="mt-0.5 shrink-0 text-fg-muted" />
91
+ <span class="min-w-0 flex-1">
92
+ <span class="flex items-center gap-1 text-sm font-medium text-fg">
93
+ {title}
94
+ {#if external}<ExternalLink size={12} class="text-fg-subtle" />{/if}
95
+ </span>
96
+ <span class="block text-xs text-fg-subtle">{subtitle}</span>
97
+ </span>
98
+ </button>
99
+ {/snippet}
100
+
101
+ {@render item(Copy, 'Copy page', 'Copy page as Markdown for LLMs', copyPage)}
102
+ {@render item(FileText, 'View as Markdown', 'Open the raw Markdown', viewMarkdown)}
103
+ {@render item(
104
+ ExternalLink,
105
+ 'Open in ChatGPT',
106
+ 'Ask questions about this page',
107
+ () => ask('https://chatgpt.com/?q='),
108
+ true
109
+ )}
110
+ {@render item(
111
+ ExternalLink,
112
+ 'Open in Claude',
113
+ 'Ask questions about this page',
114
+ () => ask('https://claude.ai/new?q='),
115
+ true
116
+ )}
117
+ </div>
118
+ {/if}
119
+ </div>
@@ -0,0 +1,192 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { fade, fly } from 'svelte/transition';
4
+ import { afterNavigate } from '$app/navigation';
5
+ import type { NavLink, NavSection, SiteConfig } from '$lib/types';
6
+ import Banner from './Banner.svelte';
7
+ import Mermaid from './Mermaid.svelte';
8
+ import Navbar from './Navbar.svelte';
9
+ import Sidebar from './Sidebar.svelte';
10
+ import TableOfContents from './TableOfContents.svelte';
11
+ import PageNav from './PageNav.svelte';
12
+ import PageMeta from './PageMeta.svelte';
13
+ import Footer from './Footer.svelte';
14
+ import CopyPageMenu from './CopyPageMenu.svelte';
15
+ import SearchDialog from './SearchDialog.svelte';
16
+ import Breadcrumbs from './Breadcrumbs.svelte';
17
+ import SidebarFooterLinks from './SidebarFooterLinks.svelte';
18
+
19
+ let {
20
+ site,
21
+ sidebar,
22
+ flatPages = [],
23
+ wide = false,
24
+ editUrl,
25
+ updated,
26
+ resolveVersion,
27
+ children
28
+ }: {
29
+ site: SiteConfig;
30
+ sidebar: NavSection[];
31
+ flatPages?: NavLink[];
32
+ wide?: boolean;
33
+ editUrl?: string;
34
+ updated?: string;
35
+ resolveVersion?: (pathname: string, versionPath: string) => string;
36
+ children: Snippet;
37
+ } = $props();
38
+
39
+ // 'boxed' centers the whole shell in a max-width container; 'flat' (default)
40
+ // is full-bleed with the sidebar flush against the left edge.
41
+ const containerClass = $derived(
42
+ (site.layout ?? 'flat') === 'boxed'
43
+ ? 'mx-auto flex w-full max-w-400 px-0 sm:px-6'
44
+ : 'flex w-full px-0 sm:px-6'
45
+ );
46
+
47
+ const sidebarVariants = {
48
+ flush:
49
+ 'sticky top-(--spacing-header) h-[calc(100vh-var(--spacing-header))] border-r border-border bg-sidebar px-4',
50
+ card: 'sticky top-[calc(var(--spacing-header)_+_1rem)] my-4 mr-3 h-[calc(100vh_-_var(--spacing-header)_-_2rem)] overflow-hidden rounded-xl border border-border bg-sidebar px-3',
51
+ floating:
52
+ 'sticky top-[calc(var(--spacing-header)_+_0.75rem)] my-3 mr-2 h-[calc(100vh_-_var(--spacing-header)_-_1.5rem)] overflow-hidden rounded-2xl border border-border bg-sidebar px-3 shadow-sm'
53
+ } as const;
54
+
55
+ const asideClass = $derived(
56
+ `hidden w-sidebar shrink-0 flex-col lg:flex ${sidebarVariants[site.sidebar?.variant ?? 'flush']}`
57
+ );
58
+
59
+ let mobileOpen = $state(false);
60
+
61
+ // Close the mobile drawer after navigating to a new page.
62
+ afterNavigate(() => {
63
+ mobileOpen = false;
64
+ });
65
+
66
+ // Delegated copy-to-clipboard for code blocks. One listener handles every
67
+ // block (including those rendered after client-side navigation), since the
68
+ // buttons are static markup from the Shiki highlighter.
69
+ $effect(() => {
70
+ async function onClick(event: MouseEvent) {
71
+ const target = event.target;
72
+ if (!(target instanceof Element)) return;
73
+ const button = target.closest('.copy-button');
74
+ if (!button) return;
75
+
76
+ // Lines are separate elements (the "\n" text nodes are stripped), so
77
+ // rejoin them with newlines for a faithful copy.
78
+ const pre = button.closest('.code-block')?.querySelector('pre');
79
+ const lines = pre?.querySelectorAll('.line');
80
+ const code =
81
+ lines && lines.length
82
+ ? Array.from(lines)
83
+ .map((line) => line.textContent)
84
+ .join('\n')
85
+ : (pre?.textContent ?? '');
86
+ try {
87
+ await navigator.clipboard.writeText(code);
88
+ button.setAttribute('data-copied', 'true');
89
+ setTimeout(() => button.removeAttribute('data-copied'), 1500);
90
+ } catch {
91
+ // Clipboard unavailable (e.g. insecure context) — fail quietly.
92
+ }
93
+ }
94
+
95
+ document.addEventListener('click', onClick);
96
+ return () => document.removeEventListener('click', onClick);
97
+ });
98
+ </script>
99
+
100
+ <div class="min-h-screen bg-bg">
101
+ {#if site.banner}
102
+ <Banner banner={site.banner} />
103
+ {/if}
104
+ <Navbar {site} {resolveVersion} onMenuClick={() => (mobileOpen = true)} />
105
+ <SearchDialog />
106
+ <Mermaid />
107
+
108
+ <div class={containerClass}>
109
+ <!-- Sidebar (desktop) -->
110
+ <aside class={asideClass}>
111
+ <div class="min-h-0 flex-1 overflow-y-auto py-8">
112
+ <Sidebar sections={sidebar} dropdowns={site.dropdowns} />
113
+ </div>
114
+ {#if site.sidebarLinks && site.sidebarLinks.length}
115
+ <div class="shrink-0 pb-6">
116
+ <SidebarFooterLinks links={site.sidebarLinks} />
117
+ </div>
118
+ {/if}
119
+ </aside>
120
+
121
+ <!-- Content -->
122
+ <main
123
+ class="flex min-h-[calc(100vh-var(--spacing-header))] min-w-0 flex-1 flex-col px-4 py-8 sm:px-8 lg:px-12"
124
+ >
125
+ <div class="mx-auto w-full {wide ? 'max-w-7xl' : 'max-w-content'}">
126
+ <div class="mb-3 flex items-center gap-3 {wide ? 'lg:max-w-[calc(100%-30.5rem)]' : ''}">
127
+ <Breadcrumbs {sidebar} />
128
+ <div class="ml-auto shrink-0">
129
+ <CopyPageMenu />
130
+ </div>
131
+ </div>
132
+ </div>
133
+ <article
134
+ id="doc-content"
135
+ class="doc-content mx-auto w-full {wide ? 'doc-api max-w-7xl' : 'max-w-content'}"
136
+ >
137
+ {@render children()}
138
+ </article>
139
+
140
+ <!-- Prev/next + footer are pushed to the bottom of the viewport on short
141
+ pages (mt-auto on the first block). On API (wide) pages they stay
142
+ aligned to the content column, not under the code rail. -->
143
+ <div class="mx-auto mt-auto w-full {wide ? 'max-w-7xl' : 'max-w-content'}">
144
+ <div class={wide ? 'lg:max-w-[calc(100%-30.5rem)]' : ''}>
145
+ <PageMeta {editUrl} {updated} />
146
+ <PageNav pages={flatPages} />
147
+ </div>
148
+ </div>
149
+
150
+ <div class="mx-auto w-full {wide ? 'max-w-7xl' : 'max-w-content'}">
151
+ <div class={wide ? 'lg:max-w-[calc(100%-30.5rem)]' : ''}>
152
+ <Footer {site} />
153
+ </div>
154
+ </div>
155
+ </main>
156
+
157
+ <!-- Table of contents (hidden in wide/API mode, which has no room for it) -->
158
+ {#if !wide}
159
+ <aside
160
+ class="sticky top-(--spacing-header) hidden h-[calc(100vh-var(--spacing-header))] w-toc shrink-0 overflow-y-auto py-8 pl-4 xl:block"
161
+ >
162
+ <TableOfContents />
163
+ </aside>
164
+ {/if}
165
+ </div>
166
+ </div>
167
+
168
+ <!-- Mobile sidebar drawer -->
169
+ {#if mobileOpen}
170
+ <div class="fixed inset-0 z-50 lg:hidden">
171
+ <button
172
+ type="button"
173
+ class="absolute inset-0 bg-black/40 backdrop-blur-sm"
174
+ aria-label="Close navigation"
175
+ onclick={() => (mobileOpen = false)}
176
+ transition:fade={{ duration: 150 }}
177
+ ></button>
178
+ <div
179
+ class="absolute top-0 left-0 flex h-full w-72 max-w-[85%] flex-col border-r border-border bg-sidebar"
180
+ transition:fly={{ x: -300, duration: 200 }}
181
+ >
182
+ <div class="min-h-0 flex-1 overflow-y-auto p-6">
183
+ <Sidebar sections={sidebar} dropdowns={site.dropdowns} />
184
+ </div>
185
+ {#if site.sidebarLinks && site.sidebarLinks.length}
186
+ <div class="shrink-0 px-6 pb-6">
187
+ <SidebarFooterLinks links={site.sidebarLinks} />
188
+ </div>
189
+ {/if}
190
+ </div>
191
+ </div>
192
+ {/if}
@@ -0,0 +1,60 @@
1
+ <script lang="ts">
2
+ import type { SiteConfig } from '$lib/types';
3
+ import DynamicIcon from '$lib/components/DynamicIcon.svelte';
4
+
5
+ let { site }: { site: SiteConfig } = $props();
6
+ </script>
7
+
8
+ <footer
9
+ class="mt-10 flex flex-col items-start justify-between gap-4 border-t border-border pt-6 text-sm text-fg-subtle sm:flex-row sm:items-center"
10
+ >
11
+ <div class="flex flex-col gap-3">
12
+ <div class="flex items-center gap-3">
13
+ {#if site.github}
14
+ <a
15
+ href={site.github}
16
+ target="_blank"
17
+ rel="noreferrer"
18
+ class="transition hover:text-fg"
19
+ aria-label="GitHub"
20
+ >
21
+ <svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
22
+ <path
23
+ d="M12 1.27a11 11 0 0 0-3.48 21.46c.55.09.73-.24.73-.53v-1.85c-3.03.66-3.67-1.45-3.67-1.45-.5-1.27-1.21-1.61-1.21-1.61-.99-.68.07-.66.07-.66 1.1.08 1.67 1.13 1.67 1.13.97 1.67 2.55 1.19 3.17.91.1-.71.38-1.19.69-1.46-2.42-.28-4.96-1.21-4.96-5.38 0-1.19.42-2.16 1.13-2.92-.11-.28-.49-1.39.11-2.89 0 0 .92-.3 3.02 1.12a10.4 10.4 0 0 1 5.5 0c2.1-1.42 3.02-1.12 3.02-1.12.6 1.5.22 2.61.11 2.89.71.76 1.13 1.73 1.13 2.92 0 4.18-2.55 5.1-4.98 5.37.39.34.74 1 .74 2.03v3.01c0 .29.18.63.74.52A11 11 0 0 0 12 1.27"
24
+ />
25
+ </svg>
26
+ </a>
27
+ {/if}
28
+ {#each site.social ?? [] as link (link.href)}
29
+ <a
30
+ href={link.href}
31
+ target="_blank"
32
+ rel="noreferrer"
33
+ class="transition hover:text-fg"
34
+ aria-label={link.label ?? link.icon}
35
+ >
36
+ <DynamicIcon name={link.icon} size={17} />
37
+ </a>
38
+ {/each}
39
+ </div>
40
+
41
+ {#if site.footer?.links?.length}
42
+ <nav class="flex flex-wrap items-center gap-x-4 gap-y-1" aria-label="Footer">
43
+ {#each site.footer.links as link (link.href)}
44
+ <a href={link.href} class="transition hover:text-fg">{link.title}</a>
45
+ {/each}
46
+ </nav>
47
+ {/if}
48
+ </div>
49
+
50
+ {#if site.footer?.note}
51
+ <p>{site.footer.note}</p>
52
+ {:else}
53
+ <p>
54
+ Powered by
55
+ <a href="https://axerity.com" class="font-semibold text-fg-muted transition hover:text-fg">
56
+ Axerity
57
+ </a>
58
+ </p>
59
+ {/if}
60
+ </footer>
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ import { tick } from 'svelte';
3
+ import { afterNavigate } from '$app/navigation';
4
+ import { theme } from '$lib/state/theme.svelte';
5
+
6
+ type MermaidApi = typeof import('mermaid').default;
7
+ let mermaid: MermaidApi | null = null;
8
+
9
+ async function render(resolved: 'light' | 'dark') {
10
+ const blocks = Array.from(document.querySelectorAll('pre.mermaid')) as HTMLElement[];
11
+ if (!blocks.length) return;
12
+
13
+ if (!mermaid) mermaid = (await import('mermaid')).default;
14
+
15
+ for (const block of blocks) {
16
+ if (block.dataset.src === undefined) block.dataset.src = block.textContent ?? '';
17
+ block.removeAttribute('data-processed');
18
+ block.innerHTML = block.dataset.src;
19
+ }
20
+
21
+ mermaid.initialize({
22
+ startOnLoad: false,
23
+ securityLevel: 'strict',
24
+ theme: resolved === 'dark' ? 'dark' : 'neutral',
25
+ fontFamily: 'inherit'
26
+ });
27
+
28
+ await mermaid.run({ nodes: blocks });
29
+ }
30
+
31
+ $effect(() => {
32
+ const resolved = theme.resolved;
33
+ tick().then(() => render(resolved));
34
+ });
35
+
36
+ afterNavigate(() => {
37
+ tick().then(() => render(theme.resolved));
38
+ });
39
+ </script>
@@ -0,0 +1,144 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/state';
3
+ import Menu from '@lucide/svelte/icons/menu';
4
+ import Search from '@lucide/svelte/icons/search';
5
+ import type { SiteConfig } from '$lib/types';
6
+ import { activeFor } from '$lib/nav-match';
7
+ import DynamicIcon from '$lib/components/DynamicIcon.svelte';
8
+ import ThemeToggle from './ThemeToggle.svelte';
9
+ import VersionSwitcher from './VersionSwitcher.svelte';
10
+ import { searchState } from '$lib/state/search.svelte';
11
+
12
+ let {
13
+ site,
14
+ onMenuClick,
15
+ resolveVersion
16
+ }: {
17
+ site: SiteConfig;
18
+ onMenuClick?: () => void;
19
+ resolveVersion?: (pathname: string, versionPath: string) => string;
20
+ } = $props();
21
+
22
+ const innerClass = $derived(
23
+ (site.layout ?? 'flat') === 'boxed'
24
+ ? 'mx-auto flex h-full max-w-400 items-center gap-4 px-4 sm:px-6'
25
+ : 'flex h-full items-center gap-4 px-4 sm:px-6'
26
+ );
27
+
28
+ const activeDropdown = $derived(
29
+ site.dropdowns && site.dropdowns.length
30
+ ? (activeFor(page.url.pathname, site.dropdowns) ?? site.dropdowns[0])
31
+ : undefined
32
+ );
33
+
34
+ const tabs = $derived(activeDropdown?.tabs ?? site.topNav);
35
+ const activeTab = $derived(activeFor(page.url.pathname, tabs));
36
+ </script>
37
+
38
+ <header
39
+ class="sticky top-0 z-40 h-(--spacing-header) border-b border-border bg-header/80 backdrop-blur-md"
40
+ >
41
+ <div class={innerClass}>
42
+ <!-- Mobile sidebar toggle -->
43
+ <button
44
+ type="button"
45
+ onclick={onMenuClick}
46
+ class="-ml-1 inline-flex h-9 w-9 items-center justify-center rounded-lg text-fg-muted hover:bg-bg-subtle hover:text-fg lg:hidden"
47
+ aria-label="Open navigation"
48
+ >
49
+ <Menu size={20} />
50
+ </button>
51
+
52
+ <a
53
+ href={site.logo?.href ?? '/'}
54
+ class="flex shrink-0 items-center gap-2"
55
+ aria-label={site.logo?.alt ?? site.name}
56
+ >
57
+ {#if site.logo?.light || site.logo?.dark}
58
+ {#if site.logo.light}
59
+ <img
60
+ src={site.logo.light}
61
+ alt={site.logo.alt ?? site.name}
62
+ class="h-6 w-auto dark:hidden"
63
+ />
64
+ {/if}
65
+ {#if site.logo.dark}
66
+ <img
67
+ src={site.logo.dark}
68
+ alt={site.logo.alt ?? site.name}
69
+ class="hidden h-6 w-auto dark:block"
70
+ />
71
+ {/if}
72
+ {:else}
73
+ <span class="text-base font-semibold text-fg">{site.name}</span>
74
+ {/if}
75
+ </a>
76
+
77
+ {#if site.versions && site.versions.length}
78
+ <VersionSwitcher versions={site.versions} {resolveVersion} />
79
+ {/if}
80
+
81
+ <nav class="hidden items-center gap-1 md:flex" aria-label="Main">
82
+ {#each tabs as tab (tab.href)}
83
+ <a
84
+ href={tab.href}
85
+ target={tab.external ? '_blank' : undefined}
86
+ rel={tab.external ? 'noreferrer' : undefined}
87
+ aria-current={tab === activeTab ? 'page' : undefined}
88
+ class="flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition
89
+ {tab === activeTab ? 'text-accent' : 'text-fg-muted hover:text-fg'}"
90
+ >
91
+ {#if tab.icon}
92
+ <DynamicIcon name={tab.icon} size={15} />
93
+ {/if}
94
+ {tab.title}
95
+ </a>
96
+ {/each}
97
+ </nav>
98
+
99
+ <div class="flex flex-1 items-center justify-end gap-2">
100
+ <!-- Search (full pill on sm+, icon-only on mobile) -->
101
+ <button
102
+ type="button"
103
+ onclick={() => (searchState.open = true)}
104
+ class="hidden w-56 items-center gap-2 rounded-lg border border-border bg-surface px-3 py-1.5 text-sm text-fg-subtle transition hover:border-border-strong hover:text-fg-muted sm:flex lg:w-72"
105
+ aria-label="Search documentation"
106
+ >
107
+ <Search size={15} />
108
+ <span>Search</span>
109
+ <kbd
110
+ class="ml-auto rounded border border-border bg-bg-subtle px-1.5 py-0.5 font-mono text-[10px] text-fg-subtle"
111
+ >
112
+ ⌘K
113
+ </kbd>
114
+ </button>
115
+ <button
116
+ type="button"
117
+ onclick={() => (searchState.open = true)}
118
+ class="inline-flex h-9 w-9 items-center justify-center rounded-lg text-fg-muted transition hover:bg-bg-subtle hover:text-fg sm:hidden"
119
+ aria-label="Search documentation"
120
+ >
121
+ <Search size={18} />
122
+ </button>
123
+
124
+ {#if site.github}
125
+ <a
126
+ href={site.github}
127
+ target="_blank"
128
+ rel="noreferrer"
129
+ class="inline-flex h-9 w-9 items-center justify-center rounded-lg text-fg-muted transition hover:bg-bg-subtle hover:text-fg"
130
+ aria-label="GitHub repository"
131
+ >
132
+ <!-- Lucide dropped brand marks, so the GitHub glyph is inlined. -->
133
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
134
+ <path
135
+ d="M12 1.27a11 11 0 0 0-3.48 21.46c.55.09.73-.24.73-.53v-1.85c-3.03.66-3.67-1.45-3.67-1.45-.5-1.27-1.21-1.61-1.21-1.61-.99-.68.07-.66.07-.66 1.1.08 1.67 1.13 1.67 1.13.97 1.67 2.55 1.19 3.17.91.1-.71.38-1.19.69-1.46-2.42-.28-4.96-1.21-4.96-5.38 0-1.19.42-2.16 1.13-2.92-.11-.28-.49-1.39.11-2.89 0 0 .92-.3 3.02 1.12a10.4 10.4 0 0 1 5.5 0c2.1-1.42 3.02-1.12 3.02-1.12.6 1.5.22 2.61.11 2.89.71.76 1.13 1.73 1.13 2.92 0 4.18-2.55 5.1-4.98 5.37.39.34.74 1 .74 2.03v3.01c0 .29.18.63.74.52A11 11 0 0 0 12 1.27"
136
+ />
137
+ </svg>
138
+ </a>
139
+ {/if}
140
+
141
+ <ThemeToggle />
142
+ </div>
143
+ </div>
144
+ </header>
@@ -0,0 +1,35 @@
1
+ <script lang="ts">
2
+ import PencilLine from '@lucide/svelte/icons/pencil-line';
3
+
4
+ let { editUrl, updated }: { editUrl?: string; updated?: string } = $props();
5
+
6
+ const formatted = $derived.by(() => {
7
+ if (!updated) return undefined;
8
+ const date = new Date(updated);
9
+ if (Number.isNaN(date.getTime())) return updated;
10
+ return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
11
+ });
12
+ </script>
13
+
14
+ {#if editUrl || formatted}
15
+ <div
16
+ class="mt-8 flex flex-wrap items-center justify-between gap-2 border-t border-border pt-4 text-sm text-fg-subtle"
17
+ >
18
+ {#if formatted}
19
+ <span>Last updated {formatted}</span>
20
+ {:else}
21
+ <span></span>
22
+ {/if}
23
+ {#if editUrl}
24
+ <a
25
+ href={editUrl}
26
+ target="_blank"
27
+ rel="noreferrer"
28
+ class="inline-flex items-center gap-1.5 transition hover:text-fg"
29
+ >
30
+ <PencilLine size={14} />
31
+ Edit this page
32
+ </a>
33
+ {/if}
34
+ </div>
35
+ {/if}
@@ -0,0 +1,44 @@
1
+ <script lang="ts">
2
+ import { page } from '$app/state';
3
+ import ChevronLeft from '@lucide/svelte/icons/chevron-left';
4
+ import ChevronRight from '@lucide/svelte/icons/chevron-right';
5
+ import type { NavLink } from '$lib/types';
6
+
7
+ let { pages }: { pages: NavLink[] } = $props();
8
+
9
+ const index = $derived(pages.findIndex((entry) => entry.href === page.url.pathname));
10
+ const prev = $derived(index > 0 ? pages[index - 1] : undefined);
11
+ const next = $derived(index >= 0 && index < pages.length - 1 ? pages[index + 1] : undefined);
12
+ </script>
13
+
14
+ {#if prev || next}
15
+ <nav class="mt-12 grid gap-3 border-t border-border pt-6 sm:grid-cols-2" aria-label="Pagination">
16
+ {#if prev}
17
+ <a
18
+ href={prev.href}
19
+ class="flex items-center gap-3 rounded-lg border border-border p-4 transition hover:border-border-strong hover:bg-bg-subtle"
20
+ >
21
+ <ChevronLeft size={18} class="shrink-0 text-fg-subtle" />
22
+ <span class="min-w-0">
23
+ <span class="block text-xs text-fg-subtle">Previous</span>
24
+ <span class="block truncate text-sm font-medium text-fg">{prev.title}</span>
25
+ </span>
26
+ </a>
27
+ {:else}
28
+ <span class="hidden sm:block"></span>
29
+ {/if}
30
+
31
+ {#if next}
32
+ <a
33
+ href={next.href}
34
+ class="flex items-center justify-end gap-3 rounded-lg border border-border p-4 text-right transition hover:border-border-strong hover:bg-bg-subtle"
35
+ >
36
+ <span class="min-w-0">
37
+ <span class="block text-xs text-fg-subtle">Next</span>
38
+ <span class="block truncate text-sm font-medium text-fg">{next.title}</span>
39
+ </span>
40
+ <ChevronRight size={18} class="shrink-0 text-fg-subtle" />
41
+ </a>
42
+ {/if}
43
+ </nav>
44
+ {/if}