@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.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/app.css +337 -0
- package/dist/components/Seo.svelte +38 -0
- package/dist/components/Seo.svelte.d.ts +8 -0
- package/dist/components/atoms/Badge.svelte +11 -0
- package/dist/components/atoms/Badge.svelte.d.ts +8 -0
- package/dist/components/atoms/Button.svelte +32 -0
- package/dist/components/atoms/Button.svelte.d.ts +12 -0
- package/dist/components/atoms/Callout.svelte +11 -0
- package/dist/components/atoms/Callout.svelte.d.ts +8 -0
- package/dist/components/atoms/CodeChip.svelte +11 -0
- package/dist/components/atoms/CodeChip.svelte.d.ts +8 -0
- package/dist/components/atoms/Eyebrow.svelte +9 -0
- package/dist/components/atoms/Eyebrow.svelte.d.ts +8 -0
- package/dist/components/atoms/HandNote.svelte +15 -0
- package/dist/components/atoms/HandNote.svelte.d.ts +9 -0
- package/dist/components/atoms/Icon.svelte +40 -0
- package/dist/components/atoms/Icon.svelte.d.ts +11 -0
- package/dist/components/atoms/Kbd.svelte +11 -0
- package/dist/components/atoms/Kbd.svelte.d.ts +8 -0
- package/dist/components/atoms/Logo.svelte +18 -0
- package/dist/components/atoms/Logo.svelte.d.ts +6 -0
- package/dist/components/atoms/SearchButton.svelte +23 -0
- package/dist/components/atoms/SearchButton.svelte.d.ts +8 -0
- package/dist/components/atoms/Tag.svelte +21 -0
- package/dist/components/atoms/Tag.svelte.d.ts +10 -0
- package/dist/components/atoms/ThemeToggle.svelte +30 -0
- package/dist/components/atoms/ThemeToggle.svelte.d.ts +6 -0
- package/dist/components/code/Code.svelte +22 -0
- package/dist/components/code/Code.svelte.d.ts +9 -0
- package/dist/components/code/CodeWindow.svelte +42 -0
- package/dist/components/code/CodeWindow.svelte.d.ts +13 -0
- package/dist/components/code/CopyCommand.svelte +28 -0
- package/dist/components/code/CopyCommand.svelte.d.ts +7 -0
- package/dist/components/landing/BenchExplorer.svelte +157 -0
- package/dist/components/landing/BenchExplorer.svelte.d.ts +7 -0
- package/dist/components/landing/CTASection.svelte +12 -0
- package/dist/components/landing/CTASection.svelte.d.ts +11 -0
- package/dist/components/landing/ComparisonPanel.svelte +75 -0
- package/dist/components/landing/ComparisonPanel.svelte.d.ts +19 -0
- package/dist/components/landing/FeatureCard.svelte +19 -0
- package/dist/components/landing/FeatureCard.svelte.d.ts +10 -0
- package/dist/components/landing/FeatureGrid.svelte +26 -0
- package/dist/components/landing/FeatureGrid.svelte.d.ts +13 -0
- package/dist/components/landing/Hero.svelte +23 -0
- package/dist/components/landing/Hero.svelte.d.ts +14 -0
- package/dist/components/landing/RecipeDeck.svelte +137 -0
- package/dist/components/landing/RecipeDeck.svelte.d.ts +19 -0
- package/dist/components/layout/DocLayout.svelte +36 -0
- package/dist/components/layout/DocLayout.svelte.d.ts +21 -0
- package/dist/components/layout/PageShell.svelte +15 -0
- package/dist/components/layout/PageShell.svelte.d.ts +7 -0
- package/dist/components/molecules/Card.svelte +18 -0
- package/dist/components/molecules/Card.svelte.d.ts +9 -0
- package/dist/components/molecules/DataTable.svelte +43 -0
- package/dist/components/molecules/DataTable.svelte.d.ts +12 -0
- package/dist/components/molecules/PageHeading.svelte +23 -0
- package/dist/components/molecules/PageHeading.svelte.d.ts +10 -0
- package/dist/components/molecules/Section.svelte +20 -0
- package/dist/components/molecules/Section.svelte.d.ts +10 -0
- package/dist/components/molecules/SectionHeading.svelte +27 -0
- package/dist/components/molecules/SectionHeading.svelte.d.ts +10 -0
- package/dist/components/nav/DocHeader.svelte +44 -0
- package/dist/components/nav/DocHeader.svelte.d.ts +7 -0
- package/dist/components/nav/Footer.svelte +67 -0
- package/dist/components/nav/Footer.svelte.d.ts +3 -0
- package/dist/components/nav/Header.svelte +55 -0
- package/dist/components/nav/Header.svelte.d.ts +6 -0
- package/dist/components/nav/PrevNext.svelte +27 -0
- package/dist/components/nav/PrevNext.svelte.d.ts +13 -0
- package/dist/components/nav/RightRail.svelte +30 -0
- package/dist/components/nav/RightRail.svelte.d.ts +11 -0
- package/dist/components/nav/Sidebar.svelte +33 -0
- package/dist/components/nav/Sidebar.svelte.d.ts +18 -0
- package/dist/components/reference/MethodDetail.svelte +72 -0
- package/dist/components/reference/MethodDetail.svelte.d.ts +7 -0
- package/dist/components/reference/MethodSummary.svelte +41 -0
- package/dist/components/reference/MethodSummary.svelte.d.ts +8 -0
- package/dist/i18n/index.d.ts +47 -0
- package/dist/i18n/index.js +23 -0
- package/dist/i18n/messages/en.d.ts +43 -0
- package/dist/i18n/messages/en.js +50 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +54 -0
- package/dist/site/site.d.ts +45 -0
- package/dist/site/site.js +66 -0
- package/dist/utils/cn.d.ts +6 -0
- package/dist/utils/cn.js +9 -0
- package/dist/utils/icons.d.ts +26 -0
- package/dist/utils/icons.js +55 -0
- package/dist/utils/tokenizer.d.ts +12 -0
- package/dist/utils/tokenizer.js +181 -0
- package/dist/utils/types.d.ts +50 -0
- package/dist/utils/types.js +4 -0
- package/dist/utils/variants.d.ts +20 -0
- package/dist/utils/variants.js +50 -0
- 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,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,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,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,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,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,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;
|