@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,63 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let { title, children }: { title?: string; children: Snippet } = $props();
5
+ </script>
6
+
7
+ <div class="step">
8
+ {#if title}
9
+ <div class="step-title">{title}</div>
10
+ {/if}
11
+ <div class="step-body">
12
+ {@render children()}
13
+ </div>
14
+ </div>
15
+
16
+ <style>
17
+ .step {
18
+ position: relative;
19
+ margin-left: 0.85rem;
20
+ padding-left: 2.25rem;
21
+ padding-bottom: 1.5rem;
22
+ border-left: 2px solid var(--border);
23
+ }
24
+
25
+ .step:last-child {
26
+ border-left-color: transparent;
27
+ padding-bottom: 0;
28
+ }
29
+
30
+ .step::before {
31
+ counter-increment: axerity-step;
32
+ content: counter(axerity-step);
33
+ position: absolute;
34
+ top: -0.2rem;
35
+ left: 0;
36
+ transform: translateX(-50%);
37
+ display: grid;
38
+ place-items: center;
39
+ width: 1.7rem;
40
+ height: 1.7rem;
41
+ border-radius: 9999px;
42
+ background-color: var(--surface-raised);
43
+ border: 1px solid var(--border-strong);
44
+ color: var(--fg);
45
+ font-size: 0.8rem;
46
+ font-weight: 600;
47
+ font-variant-numeric: tabular-nums;
48
+ }
49
+
50
+ .step-title {
51
+ margin-bottom: 0.5rem;
52
+ font-weight: 600;
53
+ line-height: 1.7rem;
54
+ color: var(--fg);
55
+ }
56
+
57
+ .step-body :global(> :first-child) {
58
+ margin-top: 0;
59
+ }
60
+ .step-body :global(> :last-child) {
61
+ margin-bottom: 0;
62
+ }
63
+ </style>
@@ -0,0 +1,16 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let { children }: { children: Snippet } = $props();
5
+ </script>
6
+
7
+ <div class="steps">
8
+ {@render children()}
9
+ </div>
10
+
11
+ <style>
12
+ .steps {
13
+ counter-reset: axerity-step;
14
+ margin: 1.5rem 0;
15
+ }
16
+ </style>
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import { getContext, onDestroy, untrack } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import { TABS, type TabsContext } from './tabs-context';
5
+
6
+ let { title, icon, children }: { title: string; icon?: string; children: Snippet } = $props();
7
+
8
+ const tabs = getContext<TabsContext>(TABS);
9
+ const id = untrack(() => tabs.register(title, icon));
10
+
11
+ onDestroy(() => tabs.unregister(id));
12
+ </script>
13
+
14
+ {#if tabs.isActive(id)}
15
+ <div role="tabpanel" class="tab-panel">
16
+ {@render children()}
17
+ </div>
18
+ {/if}
19
+
20
+ <style>
21
+ .tab-panel :global(> :first-child) {
22
+ margin-top: 0;
23
+ }
24
+ .tab-panel :global(> :last-child) {
25
+ margin-bottom: 0;
26
+ }
27
+ </style>
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import { setContext } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import { TABS, type TabsContext } from './tabs-context';
5
+ import { tabGroups } from './tabs-store.svelte';
6
+ import DynamicIcon from '$lib/components/DynamicIcon.svelte';
7
+
8
+ let { group, children }: { group?: string; children: Snippet } = $props();
9
+
10
+ interface TabMeta {
11
+ id: number;
12
+ title: string;
13
+ icon?: string;
14
+ }
15
+
16
+ const tabs = $state<TabMeta[]>([]);
17
+ let localValue = $state<string | undefined>(undefined);
18
+ let counter = 0;
19
+
20
+ $effect(() => {
21
+ if (group) tabGroups.init(group);
22
+ });
23
+
24
+ const activeValue = $derived(group ? tabGroups.get(group) : localValue);
25
+
26
+ const activeId = $derived.by(() => {
27
+ const match = activeValue !== undefined ? tabs.find((t) => t.title === activeValue) : undefined;
28
+ return match?.id ?? tabs[0]?.id;
29
+ });
30
+
31
+ function select(title: string) {
32
+ if (group) tabGroups.set(group, title);
33
+ else localValue = title;
34
+ }
35
+
36
+ setContext<TabsContext>(TABS, {
37
+ register(title, icon) {
38
+ const id = counter++;
39
+ tabs.push({ id, title, icon });
40
+ return id;
41
+ },
42
+ unregister(id) {
43
+ const index = tabs.findIndex((tab) => tab.id === id);
44
+ if (index !== -1) tabs.splice(index, 1);
45
+ },
46
+ isActive: (id) => activeId === id
47
+ });
48
+ </script>
49
+
50
+ <div class="my-5 flex flex-col overflow-hidden rounded-xl border border-border">
51
+ <div class="order-2 p-4">
52
+ {@render children()}
53
+ </div>
54
+
55
+ <div
56
+ role="tablist"
57
+ class="order-1 flex gap-1 overflow-x-auto border-b border-border bg-bg-subtle px-2"
58
+ >
59
+ {#each tabs as tab (tab.id)}
60
+ <button
61
+ type="button"
62
+ role="tab"
63
+ aria-selected={activeId === tab.id}
64
+ onclick={() => select(tab.title)}
65
+ class="-mb-px flex shrink-0 items-center gap-1.5 border-b-2 px-3 py-2.5 text-sm font-medium transition
66
+ {activeId === tab.id ? 'border-accent text-fg' : 'border-transparent text-fg-muted hover:text-fg'}"
67
+ >
68
+ {#if tab.icon}
69
+ <DynamicIcon name={tab.icon} size={15} />
70
+ {/if}
71
+ {tab.title}
72
+ </button>
73
+ {/each}
74
+ </div>
75
+ </div>
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let { tip, children }: { tip: string; children: Snippet } = $props();
5
+ </script>
6
+
7
+ <span class="tooltip group relative inline-flex">
8
+ <button type="button" class="tooltip-trigger" aria-label={tip}>
9
+ {@render children()}
10
+ </button>
11
+ <span
12
+ role="tooltip"
13
+ class="tooltip-pop pointer-events-none absolute bottom-full left-1/2 z-50 mb-2 w-max max-w-xs -translate-x-1/2 scale-95 rounded-md bg-fg px-2.5 py-1.5 text-xs leading-snug font-medium text-surface opacity-0 shadow-md transition duration-150 group-focus-within:scale-100 group-focus-within:opacity-100 group-hover:scale-100 group-hover:opacity-100"
14
+ >
15
+ {tip}
16
+ <span
17
+ class="tooltip-arrow absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-fg"
18
+ ></span>
19
+ </span>
20
+ </span>
21
+
22
+ <style>
23
+ .tooltip-trigger {
24
+ cursor: help;
25
+ text-decoration: underline dotted from-font;
26
+ text-underline-offset: 0.2em;
27
+ }
28
+ .tooltip-trigger:focus-visible {
29
+ outline: 2px solid var(--border-strong);
30
+ outline-offset: 2px;
31
+ border-radius: 2px;
32
+ }
33
+ </style>
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let { children }: { children: Snippet } = $props();
5
+ </script>
6
+
7
+ <div
8
+ class="tree my-5 overflow-hidden rounded-lg border border-border bg-surface p-2 font-mono text-sm text-fg"
9
+ >
10
+ {@render children()}
11
+ </div>
@@ -0,0 +1,187 @@
1
+ <script lang="ts" module>
2
+ export interface TypeTableProp {
3
+ /** Short type signature, shown in the collapsed row (e.g. `string`). */
4
+ type?: string;
5
+ /** Short prose describing the property. */
6
+ description?: string;
7
+ /** Full type signature, shown in the expanded detail (e.g. `string | undefined`). */
8
+ typeDescription?: string;
9
+ /** Optional href turning the expanded type into a link. */
10
+ typeDescriptionLink?: string;
11
+ /** Default value, shown in the expanded detail. */
12
+ default?: string;
13
+ required?: boolean;
14
+ deprecated?: boolean;
15
+ }
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ import { SvelteSet } from 'svelte/reactivity';
20
+ import { slide } from 'svelte/transition';
21
+ import ChevronDown from '@lucide/svelte/icons/chevron-down';
22
+ import { withBase } from '$lib/base';
23
+
24
+ let { type }: { type: Record<string, TypeTableProp> } = $props();
25
+
26
+ const entries = $derived(Object.entries(type));
27
+
28
+ // Rows are open by default; track the ones the reader has collapsed.
29
+ const collapsed = new SvelteSet<string>();
30
+ const isOpen = (name: string) => !collapsed.has(name);
31
+ function toggle(name: string) {
32
+ if (collapsed.has(name)) collapsed.delete(name);
33
+ else collapsed.add(name);
34
+ }
35
+ </script>
36
+
37
+ <div class="tt">
38
+ <div class="tt-head">
39
+ <span>Prop</span>
40
+ <span>Type</span>
41
+ <span class="tt-spacer"></span>
42
+ </div>
43
+
44
+ {#each entries as [name, prop] (name)}
45
+ <div class="tt-item">
46
+ <button
47
+ type="button"
48
+ class="tt-summary"
49
+ aria-expanded={isOpen(name)}
50
+ onclick={() => toggle(name)}
51
+ >
52
+ <span class="tt-prop" class:tt-deprecated={prop.deprecated}>
53
+ {name}{prop.required ? '' : '?'}
54
+ </span>
55
+ <span class="tt-type">{prop.type ?? '—'}</span>
56
+ <ChevronDown size={16} class="tt-chevron {isOpen(name) ? 'tt-chevron-open' : ''}" />
57
+ </button>
58
+
59
+ {#if isOpen(name)}
60
+ <div class="tt-detail" transition:slide={{ duration: 150 }}>
61
+ {#if prop.description}
62
+ <p class="tt-desc">{prop.description}</p>
63
+ {/if}
64
+ <div class="tt-meta-row">
65
+ <span class="tt-meta-label">Type</span>
66
+ {#if prop.typeDescriptionLink}
67
+ <a class="tt-code tt-link" href={withBase(prop.typeDescriptionLink)}>
68
+ {prop.typeDescription ?? prop.type ?? '—'}
69
+ </a>
70
+ {:else}
71
+ <span class="tt-code">{prop.typeDescription ?? prop.type ?? '—'}</span>
72
+ {/if}
73
+ </div>
74
+ {#if prop.default !== undefined}
75
+ <div class="tt-meta-row">
76
+ <span class="tt-meta-label">Default</span>
77
+ <span class="tt-code">{prop.default}</span>
78
+ </div>
79
+ {/if}
80
+ </div>
81
+ {/if}
82
+ </div>
83
+ {/each}
84
+ </div>
85
+
86
+ <style>
87
+ .tt {
88
+ margin: 1.5rem 0;
89
+ padding: 0.4rem;
90
+ border: 1px solid var(--border);
91
+ border-radius: 0.5rem;
92
+ background-color: var(--bg-subtle);
93
+ }
94
+
95
+ .tt-head {
96
+ display: grid;
97
+ grid-template-columns: 1fr 1.4fr auto;
98
+ gap: 1rem;
99
+ padding: 0.5rem 0.85rem;
100
+ font-size: 0.78rem;
101
+ color: var(--fg-subtle);
102
+ }
103
+
104
+ .tt-item {
105
+ overflow: hidden;
106
+ border: 1px solid var(--border);
107
+ border-radius: 0.375rem;
108
+ background-color: var(--surface);
109
+ }
110
+ .tt-item + .tt-item {
111
+ margin-top: 0.4rem;
112
+ }
113
+
114
+ .tt-summary {
115
+ display: grid;
116
+ grid-template-columns: 1fr 1.4fr auto;
117
+ gap: 1rem;
118
+ align-items: center;
119
+ width: 100%;
120
+ padding: 0.6rem 0.85rem;
121
+ text-align: left;
122
+ cursor: pointer;
123
+ }
124
+
125
+ .tt-prop {
126
+ font-family: var(--font-mono);
127
+ font-size: 0.85rem;
128
+ font-weight: 600;
129
+ color: var(--accent);
130
+ }
131
+ .tt-deprecated {
132
+ text-decoration: line-through;
133
+ opacity: 0.7;
134
+ }
135
+
136
+ .tt-type {
137
+ overflow: hidden;
138
+ font-family: var(--font-mono);
139
+ font-size: 0.85rem;
140
+ color: var(--fg-muted);
141
+ text-overflow: ellipsis;
142
+ white-space: nowrap;
143
+ }
144
+
145
+ /* :global because the class lands on the <svg> rendered by the icon component. */
146
+ .tt-summary :global(.tt-chevron) {
147
+ color: var(--fg-subtle);
148
+ transition: transform 0.15s ease;
149
+ }
150
+ .tt-summary :global(.tt-chevron-open) {
151
+ transform: rotate(180deg);
152
+ }
153
+
154
+ .tt-detail {
155
+ padding: 0 0.85rem 0.85rem;
156
+ border-top: 1px solid var(--border);
157
+ }
158
+
159
+ .tt-desc {
160
+ margin: 0.75rem 0 0;
161
+ font-size: 0.9rem;
162
+ line-height: 1.6;
163
+ color: var(--fg-muted);
164
+ }
165
+
166
+ .tt-meta-row {
167
+ display: flex;
168
+ gap: 1rem;
169
+ padding-top: 0.7rem;
170
+ }
171
+ .tt-meta-label {
172
+ flex-shrink: 0;
173
+ width: 4.5rem;
174
+ font-size: 0.85rem;
175
+ color: var(--fg-subtle);
176
+ }
177
+ .tt-code {
178
+ font-family: var(--font-mono);
179
+ font-size: 0.85rem;
180
+ color: var(--fg);
181
+ }
182
+ .tt-link {
183
+ color: var(--accent);
184
+ text-decoration: underline;
185
+ text-underline-offset: 2px;
186
+ }
187
+ </style>
@@ -0,0 +1,32 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let { label, date, children }: { label: string; date?: string; children: Snippet } = $props();
5
+ </script>
6
+
7
+ <div class="update relative border-l border-border pb-8 pl-6">
8
+ <span class="update-dot absolute top-1.5 -left-[5px] h-2.5 w-2.5 rounded-full bg-border-strong"
9
+ ></span>
10
+ <div class="mb-2 flex flex-wrap items-baseline gap-x-3 gap-y-1">
11
+ <h3 class="m-0 text-base font-semibold text-fg">{label}</h3>
12
+ {#if date}
13
+ <span class="text-xs text-fg-subtle">{date}</span>
14
+ {/if}
15
+ </div>
16
+ <div class="update-body text-sm text-fg-muted">
17
+ {@render children()}
18
+ </div>
19
+ </div>
20
+
21
+ <style>
22
+ .update:last-child {
23
+ border-color: transparent;
24
+ padding-bottom: 0;
25
+ }
26
+ .update-body :global(> :first-child) {
27
+ margin-top: 0;
28
+ }
29
+ .update-body :global(> :last-child) {
30
+ margin-bottom: 0;
31
+ }
32
+ </style>
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ let { src, title = 'Video', poster }: { src: string; title?: string; poster?: string } = $props();
3
+
4
+ const FILE_RE = /\.(mp4|webm|ogg|ogv|mov|m4v)(\?.*)?$/i;
5
+
6
+ function parseUrl(input: string): URL | null {
7
+ try {
8
+ return new URL(input);
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+
14
+ function youtubeId(input: string): string | null {
15
+ if (/^[\w-]{11}$/.test(input)) return input;
16
+ const url = parseUrl(input);
17
+ if (!url) return null;
18
+ if (url.hostname.includes('youtu.be')) return url.pathname.slice(1) || null;
19
+ const v = url.searchParams.get('v');
20
+ if (v) return v;
21
+ const parts = url.pathname.split('/');
22
+ const embedIndex = parts.indexOf('embed');
23
+ if (embedIndex >= 0 && parts[embedIndex + 1]) return parts[embedIndex + 1];
24
+ return null;
25
+ }
26
+
27
+ function vimeoId(input: string): string | null {
28
+ const url = parseUrl(input);
29
+ if (!url || !url.hostname.includes('vimeo')) return null;
30
+ const match = url.pathname.match(/\/(?:video\/)?(\d+)/);
31
+ return match ? match[1] : null;
32
+ }
33
+
34
+ const resolved = $derived.by(() => {
35
+ if (FILE_RE.test(src)) return { kind: 'file' as const, url: src };
36
+
37
+ const yt = youtubeId(src);
38
+ if (yt) return { kind: 'embed' as const, url: `https://www.youtube-nocookie.com/embed/${yt}` };
39
+
40
+ const vimeo = vimeoId(src);
41
+ if (vimeo) return { kind: 'embed' as const, url: `https://player.vimeo.com/video/${vimeo}` };
42
+
43
+ return { kind: 'embed' as const, url: src };
44
+ });
45
+ </script>
46
+
47
+ <div
48
+ class="video my-6 aspect-video w-full overflow-hidden rounded-xl border border-border bg-black"
49
+ >
50
+ {#if resolved.kind === 'file'}
51
+ <!-- svelte-ignore a11y_media_has_caption -->
52
+ <video src={resolved.url} {poster} {title} class="h-full w-full" controls playsinline></video>
53
+ {:else}
54
+ <iframe
55
+ {title}
56
+ src={resolved.url}
57
+ class="h-full w-full"
58
+ loading="lazy"
59
+ referrerpolicy="strict-origin-when-cross-origin"
60
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
61
+ allowfullscreen
62
+ ></iframe>
63
+ {/if}
64
+ </div>
@@ -0,0 +1 @@
1
+ export const ACCORDION_GROUP = Symbol('accordion-group');
@@ -0,0 +1,80 @@
1
+ <script lang="ts">
2
+ import { setContext } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import { API, type ApiContext, type ApiExampleEntry } from './api-context';
5
+ import ApiExamplePanel from './ApiExamplePanel.svelte';
6
+
7
+ let { children }: { children: Snippet } = $props();
8
+
9
+ const examples = $state<ApiExampleEntry[]>([]);
10
+ let counter = 0;
11
+
12
+ const requests = $derived(examples.filter((example) => example.kind === 'request'));
13
+ const responses = $derived(examples.filter((example) => example.kind === 'response'));
14
+ const objects = $derived(examples.filter((example) => example.kind === 'object'));
15
+
16
+ setContext<ApiContext>(API, {
17
+ registerExample(entry) {
18
+ const id = counter++;
19
+ examples.push({ ...entry, id });
20
+ return id;
21
+ },
22
+ unregisterExample(id) {
23
+ const index = examples.findIndex((example) => example.id === id);
24
+ if (index !== -1) examples.splice(index, 1);
25
+ }
26
+ });
27
+ </script>
28
+
29
+ <div class="api">
30
+ <div class="api-main">
31
+ {@render children()}
32
+ </div>
33
+
34
+ <div class="api-rail">
35
+ <div class="api-rail-sticky">
36
+ {#each objects as object (object.id)}
37
+ <ApiExamplePanel label={object.title} entries={[object]} />
38
+ {/each}
39
+ <ApiExamplePanel label="Request" entries={requests} />
40
+ <ApiExamplePanel label="Response" entries={responses} />
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <style>
46
+ .api {
47
+ display: grid;
48
+ grid-template-columns: minmax(0, 1fr);
49
+ gap: 2rem;
50
+ }
51
+
52
+ @media (min-width: 1024px) {
53
+ .api {
54
+ grid-template-columns: minmax(0, 1fr) 28rem;
55
+ gap: 2.5rem;
56
+ align-items: start;
57
+ }
58
+ }
59
+
60
+ .api-main :global(h2) {
61
+ margin-top: 1.75rem;
62
+ font-size: 1.2rem;
63
+ }
64
+ .api-main :global(h2:first-child) {
65
+ margin-top: 0;
66
+ }
67
+
68
+ .api-rail-sticky {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 1rem;
72
+ }
73
+
74
+ @media (min-width: 1024px) {
75
+ .api-rail-sticky {
76
+ position: sticky;
77
+ top: calc(var(--spacing-header) + 1.5rem);
78
+ }
79
+ }
80
+ </style>
@@ -0,0 +1,100 @@
1
+ <script lang="ts">
2
+ import type { ApiExampleEntry } from './api-context';
3
+
4
+ let { label, entries }: { label: string; entries: ApiExampleEntry[] } = $props();
5
+
6
+ let activeId = $state<number | undefined>(undefined);
7
+ const active = $derived(entries.find((entry) => entry.id === activeId) ?? entries[0]);
8
+ const multiple = $derived(entries.length > 1);
9
+ </script>
10
+
11
+ {#if entries.length}
12
+ <div class="panel">
13
+ <div class="panel-head">
14
+ <span class="panel-label">{label}</span>
15
+ {#if multiple}
16
+ <div class="panel-tabs" role="tablist">
17
+ {#each entries as entry (entry.id)}
18
+ <button
19
+ type="button"
20
+ role="tab"
21
+ aria-selected={active?.id === entry.id}
22
+ class:active={active?.id === entry.id}
23
+ onclick={() => (activeId = entry.id)}
24
+ >
25
+ {entry.title}
26
+ </button>
27
+ {/each}
28
+ </div>
29
+ {/if}
30
+ </div>
31
+ <div class="panel-body">
32
+ {#if active}
33
+ {@render active.snippet()}
34
+ {/if}
35
+ </div>
36
+ </div>
37
+ {/if}
38
+
39
+ <style>
40
+ .panel {
41
+ overflow: hidden;
42
+ border: 1px solid var(--border);
43
+ border-radius: 0.5rem;
44
+ background-color: var(--surface-raised);
45
+ }
46
+
47
+ .panel-head {
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ gap: 0.75rem;
52
+ padding: 0.4rem 0.4rem 0.4rem 0.85rem;
53
+ border-bottom: 1px solid var(--border);
54
+ background-color: var(--bg-subtle);
55
+ }
56
+
57
+ .panel-label {
58
+ font-size: 0.78rem;
59
+ font-weight: 600;
60
+ color: var(--fg-muted);
61
+ }
62
+
63
+ .panel-tabs {
64
+ display: flex;
65
+ gap: 0.15rem;
66
+ overflow-x: auto;
67
+ }
68
+ .panel-tabs button {
69
+ flex-shrink: 0;
70
+ padding: 0.2rem 0.55rem;
71
+ border-radius: 0.3rem;
72
+ font-family: var(--font-mono);
73
+ font-size: 0.72rem;
74
+ color: var(--fg-muted);
75
+ cursor: pointer;
76
+ transition:
77
+ color 0.12s,
78
+ background-color 0.12s;
79
+ }
80
+ .panel-tabs button:hover {
81
+ color: var(--fg);
82
+ }
83
+ .panel-tabs button.active {
84
+ color: var(--fg);
85
+ background-color: var(--surface);
86
+ }
87
+
88
+ .panel-body :global(.code-block) {
89
+ margin: 0;
90
+ }
91
+ .panel-body :global(pre.shiki) {
92
+ border: 0;
93
+ border-radius: 0;
94
+ background-color: transparent !important;
95
+ }
96
+
97
+ .panel-body :global(.code-header) {
98
+ border-top: 0;
99
+ }
100
+ </style>