@axle-lang/ui 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 (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/app.css +337 -0
  4. package/dist/components/Seo.svelte +38 -0
  5. package/dist/components/Seo.svelte.d.ts +8 -0
  6. package/dist/components/atoms/Badge.svelte +11 -0
  7. package/dist/components/atoms/Badge.svelte.d.ts +8 -0
  8. package/dist/components/atoms/Button.svelte +32 -0
  9. package/dist/components/atoms/Button.svelte.d.ts +12 -0
  10. package/dist/components/atoms/Callout.svelte +11 -0
  11. package/dist/components/atoms/Callout.svelte.d.ts +8 -0
  12. package/dist/components/atoms/CodeChip.svelte +11 -0
  13. package/dist/components/atoms/CodeChip.svelte.d.ts +8 -0
  14. package/dist/components/atoms/Eyebrow.svelte +9 -0
  15. package/dist/components/atoms/Eyebrow.svelte.d.ts +8 -0
  16. package/dist/components/atoms/HandNote.svelte +15 -0
  17. package/dist/components/atoms/HandNote.svelte.d.ts +9 -0
  18. package/dist/components/atoms/Icon.svelte +40 -0
  19. package/dist/components/atoms/Icon.svelte.d.ts +11 -0
  20. package/dist/components/atoms/Kbd.svelte +11 -0
  21. package/dist/components/atoms/Kbd.svelte.d.ts +8 -0
  22. package/dist/components/atoms/Logo.svelte +18 -0
  23. package/dist/components/atoms/Logo.svelte.d.ts +6 -0
  24. package/dist/components/atoms/SearchButton.svelte +23 -0
  25. package/dist/components/atoms/SearchButton.svelte.d.ts +8 -0
  26. package/dist/components/atoms/Tag.svelte +21 -0
  27. package/dist/components/atoms/Tag.svelte.d.ts +10 -0
  28. package/dist/components/atoms/ThemeToggle.svelte +30 -0
  29. package/dist/components/atoms/ThemeToggle.svelte.d.ts +6 -0
  30. package/dist/components/code/Code.svelte +22 -0
  31. package/dist/components/code/Code.svelte.d.ts +9 -0
  32. package/dist/components/code/CodeWindow.svelte +42 -0
  33. package/dist/components/code/CodeWindow.svelte.d.ts +13 -0
  34. package/dist/components/code/CopyCommand.svelte +28 -0
  35. package/dist/components/code/CopyCommand.svelte.d.ts +7 -0
  36. package/dist/components/landing/BenchExplorer.svelte +157 -0
  37. package/dist/components/landing/BenchExplorer.svelte.d.ts +7 -0
  38. package/dist/components/landing/CTASection.svelte +12 -0
  39. package/dist/components/landing/CTASection.svelte.d.ts +11 -0
  40. package/dist/components/landing/ComparisonPanel.svelte +75 -0
  41. package/dist/components/landing/ComparisonPanel.svelte.d.ts +19 -0
  42. package/dist/components/landing/FeatureCard.svelte +19 -0
  43. package/dist/components/landing/FeatureCard.svelte.d.ts +10 -0
  44. package/dist/components/landing/FeatureGrid.svelte +26 -0
  45. package/dist/components/landing/FeatureGrid.svelte.d.ts +13 -0
  46. package/dist/components/landing/Hero.svelte +23 -0
  47. package/dist/components/landing/Hero.svelte.d.ts +14 -0
  48. package/dist/components/landing/RecipeDeck.svelte +137 -0
  49. package/dist/components/landing/RecipeDeck.svelte.d.ts +19 -0
  50. package/dist/components/layout/DocLayout.svelte +36 -0
  51. package/dist/components/layout/DocLayout.svelte.d.ts +21 -0
  52. package/dist/components/layout/PageShell.svelte +15 -0
  53. package/dist/components/layout/PageShell.svelte.d.ts +7 -0
  54. package/dist/components/molecules/Card.svelte +18 -0
  55. package/dist/components/molecules/Card.svelte.d.ts +9 -0
  56. package/dist/components/molecules/DataTable.svelte +43 -0
  57. package/dist/components/molecules/DataTable.svelte.d.ts +12 -0
  58. package/dist/components/molecules/PageHeading.svelte +23 -0
  59. package/dist/components/molecules/PageHeading.svelte.d.ts +10 -0
  60. package/dist/components/molecules/Section.svelte +20 -0
  61. package/dist/components/molecules/Section.svelte.d.ts +10 -0
  62. package/dist/components/molecules/SectionHeading.svelte +27 -0
  63. package/dist/components/molecules/SectionHeading.svelte.d.ts +10 -0
  64. package/dist/components/nav/DocHeader.svelte +44 -0
  65. package/dist/components/nav/DocHeader.svelte.d.ts +7 -0
  66. package/dist/components/nav/Footer.svelte +67 -0
  67. package/dist/components/nav/Footer.svelte.d.ts +3 -0
  68. package/dist/components/nav/Header.svelte +55 -0
  69. package/dist/components/nav/Header.svelte.d.ts +6 -0
  70. package/dist/components/nav/PrevNext.svelte +27 -0
  71. package/dist/components/nav/PrevNext.svelte.d.ts +13 -0
  72. package/dist/components/nav/RightRail.svelte +30 -0
  73. package/dist/components/nav/RightRail.svelte.d.ts +11 -0
  74. package/dist/components/nav/Sidebar.svelte +33 -0
  75. package/dist/components/nav/Sidebar.svelte.d.ts +18 -0
  76. package/dist/components/reference/MethodDetail.svelte +72 -0
  77. package/dist/components/reference/MethodDetail.svelte.d.ts +7 -0
  78. package/dist/components/reference/MethodSummary.svelte +41 -0
  79. package/dist/components/reference/MethodSummary.svelte.d.ts +8 -0
  80. package/dist/i18n/index.d.ts +47 -0
  81. package/dist/i18n/index.js +23 -0
  82. package/dist/i18n/messages/en.d.ts +43 -0
  83. package/dist/i18n/messages/en.js +50 -0
  84. package/dist/index.d.ts +47 -0
  85. package/dist/index.js +54 -0
  86. package/dist/site/site.d.ts +45 -0
  87. package/dist/site/site.js +66 -0
  88. package/dist/utils/cn.d.ts +6 -0
  89. package/dist/utils/cn.js +9 -0
  90. package/dist/utils/icons.d.ts +26 -0
  91. package/dist/utils/icons.js +55 -0
  92. package/dist/utils/tokenizer.d.ts +12 -0
  93. package/dist/utils/tokenizer.js +181 -0
  94. package/dist/utils/types.d.ts +50 -0
  95. package/dist/utils/types.js +4 -0
  96. package/dist/utils/variants.d.ts +20 -0
  97. package/dist/utils/variants.js +50 -0
  98. package/package.json +77 -0
@@ -0,0 +1,11 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ /** Keyboard key cap. */
4
+ let { class: klass = '', children }: { class?: string; children?: Snippet } = $props();
5
+ </script>
6
+
7
+ <kbd
8
+ class="rounded border border-line bg-canvas px-1.5 py-0.5 text-[11px] font-sans text-muted {klass}"
9
+ >
10
+ {@render children?.()}
11
+ </kbd>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ class?: string;
4
+ children?: Snippet;
5
+ };
6
+ declare const Kbd: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Kbd = ReturnType<typeof Kbd>;
8
+ export default Kbd;
@@ -0,0 +1,18 @@
1
+ <script lang="ts">
2
+ /** The Axle axle/wordmark glyph. */
3
+ let { size = 22 }: { size?: number } = $props();
4
+ </script>
5
+
6
+ <svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true">
7
+ <circle cx="6" cy="12" r="3" stroke="currentColor" stroke-width="2" />
8
+ <circle cx="18" cy="12" r="3" stroke="currentColor" stroke-width="2" />
9
+ <line
10
+ x1="9"
11
+ y1="12"
12
+ x2="15"
13
+ y2="12"
14
+ stroke="var(--accent)"
15
+ stroke-width="2.6"
16
+ stroke-linecap="round"
17
+ />
18
+ </svg>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ size?: number;
3
+ };
4
+ declare const Logo: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Logo = ReturnType<typeof Logo>;
6
+ export default Logo;
@@ -0,0 +1,23 @@
1
+ <script lang="ts">
2
+ import Icon from './Icon.svelte';
3
+ import Kbd from './Kbd.svelte';
4
+
5
+ /**
6
+ * Search trigger button (wire `onclick` to your command palette).
7
+ */
8
+ let {
9
+ label = 'Search',
10
+ onclick,
11
+ class: klass = ''
12
+ }: { label?: string; onclick?: () => void; class?: string } = $props();
13
+ </script>
14
+
15
+ <button
16
+ type="button"
17
+ {onclick}
18
+ class="flex items-center gap-2 rounded-lg border border-line bg-surface pl-3 pr-2 h-9 text-[13px] text-faint hover:border-faint transition-colors {klass}"
19
+ >
20
+ <Icon name="search" />
21
+ <span>{label}</span>
22
+ <Kbd class="ml-auto">⌘K</Kbd>
23
+ </button>
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ label?: string;
3
+ onclick?: () => void;
4
+ class?: string;
5
+ };
6
+ declare const SearchButton: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SearchButton = ReturnType<typeof SearchButton>;
8
+ export default SearchButton;
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { IconName } from '../../utils/icons.js';
4
+ import Icon from './Icon.svelte';
5
+
6
+ /**
7
+ * Small uppercase accent pill with an optional leading icon (the
8
+ * "Package" / "Reference" / "Benchmarks" kind labels).
9
+ */
10
+ let {
11
+ icon = '',
12
+ class: klass = '',
13
+ children
14
+ }: { icon?: IconName | ''; class?: string; children?: Snippet } = $props();
15
+ </script>
16
+
17
+ <div
18
+ class="inline-flex items-center gap-1.5 rounded-md bg-accent-soft text-accent px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wider {klass}"
19
+ >
20
+ {#if icon}<Icon name={icon} size={13} />{/if}{@render children?.()}
21
+ </div>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { IconName } from '../../utils/icons.js';
3
+ type $$ComponentProps = {
4
+ icon?: IconName | '';
5
+ class?: string;
6
+ children?: Snippet;
7
+ };
8
+ declare const Tag: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Tag = ReturnType<typeof Tag>;
10
+ export default Tag;
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ import Icon from './Icon.svelte';
3
+
4
+ /** Toggles the `dark` class on <html> and persists the choice. */
5
+ let { class: klass = '' }: { class?: string } = $props();
6
+
7
+ let dark = $state(false);
8
+
9
+ $effect(() => {
10
+ // init from storage / system once on mount
11
+ const saved = localStorage.getItem('axle-theme');
12
+ dark = saved ? saved === 'dark' : document.documentElement.classList.contains('dark');
13
+ document.documentElement.classList.toggle('dark', dark);
14
+ });
15
+
16
+ function toggle() {
17
+ dark = !dark;
18
+ document.documentElement.classList.toggle('dark', dark);
19
+ localStorage.setItem('axle-theme', dark ? 'dark' : 'light');
20
+ }
21
+ </script>
22
+
23
+ <button
24
+ type="button"
25
+ onclick={toggle}
26
+ aria-label="Toggle theme"
27
+ class="h-9 w-9 grid place-items-center rounded-lg border border-line bg-surface text-muted hover:text-fg transition-colors {klass}"
28
+ >
29
+ <Icon name="theme" />
30
+ </button>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ class?: string;
3
+ };
4
+ declare const ThemeToggle: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type ThemeToggle = ReturnType<typeof ThemeToggle>;
6
+ export default ThemeToggle;
@@ -0,0 +1,22 @@
1
+ <script lang="ts">
2
+ import { highlight } from '../../utils/tokenizer.js';
3
+
4
+ /** Syntax-highlighted code. Renders inside a <pre><code>. */
5
+ let {
6
+ code = '',
7
+ fontSize = 13,
8
+ lineHeight = 22,
9
+ class: klass = ''
10
+ }: {
11
+ code?: string;
12
+ fontSize?: number;
13
+ lineHeight?: number;
14
+ class?: string;
15
+ } = $props();
16
+ const html = $derived(highlight(code));
17
+ </script>
18
+
19
+ <pre class="m-0 font-mono overflow-x-auto axle-scroll {klass}" style="white-space:pre"><code
20
+ class="font-mono block"
21
+ style="font-size:{fontSize}px;line-height:{lineHeight}px">{@html html}</code
22
+ ></pre>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ code?: string;
3
+ fontSize?: number;
4
+ lineHeight?: number;
5
+ class?: string;
6
+ };
7
+ declare const Code: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Code = ReturnType<typeof Code>;
9
+ export default Code;
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ /**
5
+ * Dark code window with macOS traffic lights and a filename tab.
6
+ * Always uses the dark syntax palette (.dark-code).
7
+ */
8
+ let {
9
+ file = '',
10
+ meta = '',
11
+ lights = true,
12
+ class: klass = '',
13
+ header,
14
+ children,
15
+ footer
16
+ }: {
17
+ file?: string;
18
+ meta?: string;
19
+ lights?: boolean;
20
+ class?: string;
21
+ header?: Snippet;
22
+ children?: Snippet;
23
+ footer?: Snippet;
24
+ } = $props();
25
+ </script>
26
+
27
+ <div
28
+ class="rounded-xl border border-black/10 dark:border-white/10 bg-codebg dark-code overflow-hidden {klass}"
29
+ >
30
+ <div class="flex items-center gap-2 px-4 h-10 border-b border-white/[0.08]">
31
+ {#if lights}
32
+ <span class="h-2.5 w-2.5 rounded-full bg-[#ff5f57]"></span>
33
+ <span class="h-2.5 w-2.5 rounded-full bg-[#febc2e]"></span>
34
+ <span class="h-2.5 w-2.5 rounded-full bg-[#28c840]"></span>
35
+ {/if}
36
+ {#if file}<span class="ml-2 text-[12px] text-white/45 font-mono">{file}</span>{/if}
37
+ {@render header?.()}
38
+ {#if meta}<span class="ml-auto text-[11px] text-white/30 font-mono">{meta}</span>{/if}
39
+ </div>
40
+ <div class="p-5">{@render children?.()}</div>
41
+ {@render footer?.()}
42
+ </div>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ file?: string;
4
+ meta?: string;
5
+ lights?: boolean;
6
+ class?: string;
7
+ header?: Snippet;
8
+ children?: Snippet;
9
+ footer?: Snippet;
10
+ };
11
+ declare const CodeWindow: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type CodeWindow = ReturnType<typeof CodeWindow>;
13
+ export default CodeWindow;
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import Icon from '../atoms/Icon.svelte';
3
+
4
+ /** Copyable command row (e.g. install script). */
5
+ let { command = '', class: klass = '' }: { command?: string; class?: string } = $props();
6
+
7
+ let copied = $state(false);
8
+ function copy() {
9
+ navigator.clipboard?.writeText(command);
10
+ copied = true;
11
+ setTimeout(() => (copied = false), 1400);
12
+ }
13
+ </script>
14
+
15
+ <div
16
+ class="flex items-center gap-3 rounded-xl border border-line bg-surface pl-4 pr-2 h-12 font-mono text-[13px] {klass}"
17
+ >
18
+ <span class="text-accent select-none">$</span>
19
+ <span class="text-fg truncate flex-1">{command}</span>
20
+ <button
21
+ type="button"
22
+ onclick={copy}
23
+ class="shrink-0 flex items-center gap-1.5 rounded-md px-2.5 h-8 text-[12px] font-sans text-muted hover:text-fg hover:bg-surface2 transition-colors"
24
+ >
25
+ <Icon name={copied ? 'check' : 'copy'} size={14} class={copied ? 'text-accent' : ''} />
26
+ {copied ? 'Copied' : 'Copy'}
27
+ </button>
28
+ </div>
@@ -0,0 +1,7 @@
1
+ type $$ComponentProps = {
2
+ command?: string;
3
+ class?: string;
4
+ };
5
+ declare const CopyCommand: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type CopyCommand = ReturnType<typeof CopyCommand>;
7
+ export default CopyCommand;
@@ -0,0 +1,157 @@
1
+ <script lang="ts">
2
+ // Full benchmark view (à la `axle bench`). Programs grouped by category;
3
+ // each shows a metric table (columns grouped Run / Compile / Output,
4
+ // best value per column highlighted, rows sorted fastest-run first) and
5
+ // the per-language source in a collapsible block.
6
+ import Code from '../code/Code.svelte';
7
+ import Card from '../molecules/Card.svelte';
8
+ import { cn } from '../../utils/cn.js';
9
+ import { tab } from '../../utils/variants.js';
10
+ import { t } from 'svelte-i18n';
11
+ import { K } from '../../i18n/index.js';
12
+ import type { BenchData, BenchResult, Metric } from '../../utils/types.js';
13
+
14
+ let { data }: { data: BenchData } = $props();
15
+
16
+ let selected = $state<Record<string, string>>({});
17
+ const pick = (name: string, sources: Record<string, string>) =>
18
+ selected[name] ?? Object.keys(sources)[0];
19
+
20
+ // Consecutive metrics sharing a `group` form a spanning header cell.
21
+ const groups = $derived.by(() => {
22
+ const out: { group: string; metrics: Metric[] }[] = [];
23
+ for (const m of data.metrics) {
24
+ const last = out[out.length - 1];
25
+ if (last && last.group === m.group) last.metrics.push(m);
26
+ else out.push({ group: m.group, metrics: [m] });
27
+ }
28
+ return out;
29
+ });
30
+ const startsGroup = (i: number) => i > 0 && data.metrics[i - 1].group !== data.metrics[i].group;
31
+
32
+ function fmt(v: number | string, unit: string) {
33
+ const n = Number(v) || 0;
34
+ if (unit === 'B') return n >= 1024 ? (n / 1024).toFixed(0) + ' KB' : n + ' B';
35
+ if (unit === 'KB') return n >= 1024 ? (n / 1024).toFixed(1) + ' MB' : n + ' KB';
36
+ if (unit === 'ms') return n >= 1000 ? (n / 1000).toFixed(2) + ' s' : n + ' ms';
37
+ return String(n);
38
+ }
39
+ const best = (results: BenchResult[], key: string) =>
40
+ Math.min(...results.map((r) => Number(r[key]) || Infinity));
41
+ const sorted = (results: BenchResult[]) =>
42
+ [...results].sort((a, b) => (Number(a.runMs) || 0) - (Number(b.runMs) || 0));
43
+ const slug = (s: string) =>
44
+ s
45
+ .toLowerCase()
46
+ .replace(/[^a-z0-9]+/g, '-')
47
+ .replace(/(^-|-$)/g, '');
48
+ </script>
49
+
50
+ {#if data.categories.length > 1}
51
+ <nav
52
+ class="sticky top-16 z-10 -mx-2 mb-4 flex flex-wrap gap-1.5 border-b border-line bg-canvas/85 px-2 py-2 backdrop-blur"
53
+ >
54
+ {#each data.categories as cat}
55
+ <a
56
+ href={'#cat-' + slug(cat.name)}
57
+ class="flex h-7 items-center rounded-md border border-line bg-surface pl-2.5 text-[12px] text-muted hover:text-fg"
58
+ >{cat.name}
59
+ <span class="ml-2.5 flex h-full items-center border-l border-line px-2 text-faint"
60
+ >{cat.programs.length}</span
61
+ ></a
62
+ >
63
+ {/each}
64
+ </nav>
65
+ {/if}
66
+
67
+ {#each data.categories as cat}
68
+ <div id={'cat-' + slug(cat.name)} class="mt-14 first:mt-0 scroll-mt-24">
69
+ <h2
70
+ class="text-[12px] font-semibold uppercase tracking-[0.1em] text-faint border-b border-line pb-2"
71
+ >
72
+ {cat.name}
73
+ </h2>
74
+
75
+ {#each cat.programs as p}
76
+ <section class="mt-7">
77
+ <h3 class="text-[18px] font-semibold tracking-tight font-mono">{p.name}</h3>
78
+ {#if p.blurb}<p class="mt-1 text-[14px] text-muted">{p.blurb}</p>{/if}
79
+
80
+ <Card class="mt-4 overflow-x-auto axle-scroll">
81
+ <table class="w-full text-[13px] border-separate border-spacing-0">
82
+ <thead>
83
+ <tr class="bg-surface text-[11px] uppercase tracking-wider text-faint">
84
+ <th rowspan="2" class="text-left font-semibold py-2 px-4 align-bottom"
85
+ >{$t(K.bench.variant)}</th
86
+ >
87
+ {#each groups as g}
88
+ <th
89
+ colspan={g.metrics.length}
90
+ class="font-semibold py-2 px-4 text-center border-l border-line"
91
+ >
92
+ {g.group}
93
+ </th>
94
+ {/each}
95
+ </tr>
96
+ <tr class="bg-surface text-[11px] uppercase tracking-wider text-faint">
97
+ {#each data.metrics as m, i}
98
+ <th
99
+ class={cn(
100
+ 'font-medium py-1.5 px-4 text-right whitespace-nowrap',
101
+ startsGroup(i) && 'border-l border-line'
102
+ )}
103
+ >
104
+ {m.label}
105
+ </th>
106
+ {/each}
107
+ </tr>
108
+ </thead>
109
+ <tbody>
110
+ {#each sorted(p.results) as r}
111
+ <tr class="hover:bg-surface/60 transition-colors">
112
+ <td class="py-2 px-4 border-t border-line font-mono whitespace-nowrap">
113
+ {r.lang} <span class="text-faint">{r.opt}</span>
114
+ </td>
115
+ {#each data.metrics as m, i}
116
+ {@const isBest = (Number(r[m.key]) || 0) === best(p.results, m.key)}
117
+ <td
118
+ class={cn(
119
+ 'py-2 px-4 border-t border-line text-right font-mono whitespace-nowrap',
120
+ startsGroup(i) && 'border-l border-line',
121
+ isBest ? 'text-accent font-semibold' : 'text-muted'
122
+ )}>{fmt(r[m.key], m.unit)}</td
123
+ >
124
+ {/each}
125
+ </tr>
126
+ {/each}
127
+ </tbody>
128
+ </table>
129
+ </Card>
130
+
131
+ <details class="group mt-3">
132
+ <summary
133
+ class="cursor-pointer select-none text-[13px] text-muted hover:text-fg inline-flex items-center gap-1.5"
134
+ >
135
+ <span class="transition-transform group-open:rotate-90 text-faint">▸</span>
136
+ {$t(K.bench.source)}
137
+ </summary>
138
+ <Card tone="surface" class="mt-2 p-3">
139
+ <div class="flex flex-wrap gap-1">
140
+ {#each Object.keys(p.sources) as l}
141
+ <button
142
+ type="button"
143
+ onclick={() => (selected = { ...selected, [p.name]: l })}
144
+ class={tab({ active: pick(p.name, p.sources) === l, size: 'sm', tone: 'canvas' })}
145
+ >{l}</button
146
+ >
147
+ {/each}
148
+ </div>
149
+ <div class="mt-3">
150
+ <Code code={p.sources[pick(p.name, p.sources)]} fontSize={12.5} lineHeight={20} />
151
+ </div>
152
+ </Card>
153
+ </details>
154
+ </section>
155
+ {/each}
156
+ </div>
157
+ {/each}
@@ -0,0 +1,7 @@
1
+ import type { BenchData } from '../../utils/types.js';
2
+ type $$ComponentProps = {
3
+ data: BenchData;
4
+ };
5
+ declare const BenchExplorer: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type BenchExplorer = ReturnType<typeof BenchExplorer>;
7
+ export default BenchExplorer;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Closing CTA shell — centered. All content (kicker, heading, buttons) in children.
4
+ */
5
+ import type { Snippet } from 'svelte';
6
+
7
+ let { class: klass = '', children }: { class?: string; children?: Snippet } = $props();
8
+ </script>
9
+
10
+ <section class="max-w-[1180px] mx-auto px-6 py-24 text-center {klass}">
11
+ {@render children?.()}
12
+ </section>
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Closing CTA shell — centered. All content (kicker, heading, buttons) in children.
3
+ */
4
+ import type { Snippet } from 'svelte';
5
+ type $$ComponentProps = {
6
+ class?: string;
7
+ children?: Snippet;
8
+ };
9
+ declare const CTASection: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type CTASection = ReturnType<typeof CTASection>;
11
+ export default CTASection;
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import Code from '../code/Code.svelte';
3
+ import Section from '../molecules/Section.svelte';
4
+ import SectionHeading from '../molecules/SectionHeading.svelte';
5
+ import Card from '../molecules/Card.svelte';
6
+ import { tab, segmented } from '../../utils/variants.js';
7
+
8
+ /**
9
+ * "Side by side" — pick a task (scenario) × a language. Both selections persist
10
+ * independently. Data comes from the route (parsed from content/scenarios/*.md).
11
+ */
12
+ interface Scenario {
13
+ label: string;
14
+ file: string;
15
+ axle: string;
16
+ langs: Record<string, { src: string; note: string }>;
17
+ }
18
+
19
+ let { scenarios = [] }: { scenarios?: Scenario[] } = $props();
20
+
21
+ let si = $state(0);
22
+ let li = $state(0);
23
+
24
+ const scenario = $derived(scenarios[si]);
25
+ const langKeys = $derived(scenario ? Object.keys(scenario.langs) : []);
26
+ const lang = $derived(langKeys[Math.min(li, langKeys.length - 1)]);
27
+ const ref = $derived((scenario && lang && scenario.langs[lang]) || { src: '', note: '' });
28
+ </script>
29
+
30
+ <Section bordered>
31
+ <SectionHeading eyebrow="Side by side" title="The same job. Far less ceremony.">
32
+ Pick a task, then pick the language you would otherwise reach for. Same outcome — Axle just asks
33
+ less of you to get there.
34
+ </SectionHeading>
35
+
36
+ <div class="mt-8 flex flex-wrap gap-2">
37
+ {#each scenarios as s, i}
38
+ <button type="button" onclick={() => (si = i)} class={segmented({ active: i === si })}
39
+ >{s.label}</button
40
+ >
41
+ {/each}
42
+ </div>
43
+
44
+ <Card class="mt-5 overflow-hidden">
45
+ <div class="grid md:grid-cols-2">
46
+ <div class="bg-surface p-5 md:border-r border-line">
47
+ <div class="flex items-center gap-2 mb-3.5">
48
+ <span
49
+ class="text-[11px] font-semibold uppercase tracking-wider text-accent rounded bg-accent-soft px-2 py-0.5"
50
+ >Axle</span
51
+ >
52
+ <span class="text-[12px] text-faint font-mono">{scenario?.file}</span>
53
+ </div>
54
+ <Code code={scenario?.axle ?? ''} fontSize={13} lineHeight={21} />
55
+ </div>
56
+
57
+ <div class="bg-canvas p-5 border-t md:border-t-0 border-line">
58
+ <div class="flex items-center gap-1 mb-3.5">
59
+ {#each langKeys as l, i}
60
+ <button
61
+ type="button"
62
+ onclick={() => (li = i)}
63
+ class={tab({ active: l === lang, size: 'sm' })}>{l}</button
64
+ >
65
+ {/each}
66
+ </div>
67
+ <Code code={ref.src} fontSize={13} lineHeight={21} />
68
+ </div>
69
+ </div>
70
+ <div class="px-5 py-3 border-t border-line flex items-start gap-2.5 text-[13.5px] text-muted">
71
+ <span class="mt-[7px] h-1.5 w-1.5 shrink-0 rounded-full bg-accent"></span>
72
+ <span class="leading-relaxed">{ref.note}</span>
73
+ </div>
74
+ </Card>
75
+ </Section>
@@ -0,0 +1,19 @@
1
+ /**
2
+ * "Side by side" — pick a task (scenario) × a language. Both selections persist
3
+ * independently. Data comes from the route (parsed from content/scenarios/*.md).
4
+ */
5
+ interface Scenario {
6
+ label: string;
7
+ file: string;
8
+ axle: string;
9
+ langs: Record<string, {
10
+ src: string;
11
+ note: string;
12
+ }>;
13
+ }
14
+ type $$ComponentProps = {
15
+ scenarios?: Scenario[];
16
+ };
17
+ declare const ComparisonPanel: import("svelte").Component<$$ComponentProps, {}, "">;
18
+ type ComparisonPanel = ReturnType<typeof ComparisonPanel>;
19
+ export default ComparisonPanel;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import Icon from '../atoms/Icon.svelte';
3
+ import type { Snippet } from 'svelte';
4
+ import type { IconName } from '../../utils/icons.js';
5
+ /**
6
+ * One feature cell. Description goes in children.
7
+ */
8
+ let { icon, title, children }: { icon?: IconName; title: string; children?: Snippet } = $props();
9
+ </script>
10
+
11
+ <div class="bg-canvas p-7 flex flex-col gap-3 hover:bg-surface transition-colors">
12
+ {#if icon}
13
+ <div class="h-9 w-9 grid place-items-center rounded-lg bg-accent-soft text-accent">
14
+ <Icon name={icon} size={18} />
15
+ </div>
16
+ {/if}
17
+ <h3 class="text-[16px] font-semibold tracking-tight mt-1">{title}</h3>
18
+ <p class="text-[14px] leading-relaxed text-muted">{@render children?.()}</p>
19
+ </div>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { IconName } from '../../utils/icons.js';
3
+ type $$ComponentProps = {
4
+ icon?: IconName;
5
+ title: string;
6
+ children?: Snippet;
7
+ };
8
+ declare const FeatureCard: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type FeatureCard = ReturnType<typeof FeatureCard>;
10
+ export default FeatureCard;
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Feature grid shell. `header` snippet = eyebrow + heading; `children` = the
4
+ * <FeatureCard> cells. Route composes the cards.
5
+ */
6
+ import type { Snippet } from 'svelte';
7
+
8
+ let {
9
+ header,
10
+ class: klass = '',
11
+ children
12
+ }: { header?: Snippet; class?: string; children?: Snippet } = $props();
13
+ </script>
14
+
15
+ <section class="border-t border-line bg-surface {klass}">
16
+ <div class="max-w-[1180px] mx-auto px-6 py-20">
17
+ {#if header}
18
+ <div class="max-w-[620px]">{@render header()}</div>
19
+ {/if}
20
+ <div
21
+ class="mt-10 grid sm:grid-cols-2 lg:grid-cols-3 gap-px bg-line rounded-2xl overflow-hidden border border-line"
22
+ >
23
+ {@render children?.()}
24
+ </div>
25
+ </div>
26
+ </section>
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Feature grid shell. `header` snippet = eyebrow + heading; `children` = the
3
+ * <FeatureCard> cells. Route composes the cards.
4
+ */
5
+ import type { Snippet } from 'svelte';
6
+ type $$ComponentProps = {
7
+ header?: Snippet;
8
+ class?: string;
9
+ children?: Snippet;
10
+ };
11
+ declare const FeatureGrid: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type FeatureGrid = ReturnType<typeof FeatureGrid>;
13
+ export default FeatureGrid;