@dorsk/tsumikit 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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/autoresize.d.ts +11 -0
  4. package/dist/autoresize.js +24 -0
  5. package/dist/components/atoms/Badge.svelte +72 -0
  6. package/dist/components/atoms/Badge.svelte.d.ts +12 -0
  7. package/dist/components/atoms/Button.svelte +156 -0
  8. package/dist/components/atoms/Button.svelte.d.ts +13 -0
  9. package/dist/components/atoms/Card.svelte +46 -0
  10. package/dist/components/atoms/Card.svelte.d.ts +11 -0
  11. package/dist/components/atoms/Checkbox.svelte +99 -0
  12. package/dist/components/atoms/Checkbox.svelte.d.ts +10 -0
  13. package/dist/components/atoms/Chip.svelte +53 -0
  14. package/dist/components/atoms/Chip.svelte.d.ts +11 -0
  15. package/dist/components/atoms/Heading.svelte +66 -0
  16. package/dist/components/atoms/Heading.svelte.d.ts +13 -0
  17. package/dist/components/atoms/Icon.svelte +151 -0
  18. package/dist/components/atoms/Icon.svelte.d.ts +18 -0
  19. package/dist/components/atoms/Input.svelte +42 -0
  20. package/dist/components/atoms/Input.svelte.d.ts +10 -0
  21. package/dist/components/atoms/Link.svelte +31 -0
  22. package/dist/components/atoms/Link.svelte.d.ts +10 -0
  23. package/dist/components/atoms/Progress.svelte +59 -0
  24. package/dist/components/atoms/Progress.svelte.d.ts +9 -0
  25. package/dist/components/atoms/Select.svelte +95 -0
  26. package/dist/components/atoms/Select.svelte.d.ts +11 -0
  27. package/dist/components/atoms/Slider.svelte +136 -0
  28. package/dist/components/atoms/Slider.svelte.d.ts +14 -0
  29. package/dist/components/atoms/Switch.svelte +64 -0
  30. package/dist/components/atoms/Switch.svelte.d.ts +8 -0
  31. package/dist/components/atoms/Text.svelte +127 -0
  32. package/dist/components/atoms/Text.svelte.d.ts +16 -0
  33. package/dist/components/atoms/Textarea.svelte +62 -0
  34. package/dist/components/atoms/Textarea.svelte.d.ts +11 -0
  35. package/dist/components/layouts/AppShell.svelte +304 -0
  36. package/dist/components/layouts/AppShell.svelte.d.ts +21 -0
  37. package/dist/components/layouts/AutoGrid.svelte +36 -0
  38. package/dist/components/layouts/AutoGrid.svelte.d.ts +12 -0
  39. package/dist/components/layouts/Cluster.svelte +45 -0
  40. package/dist/components/layouts/Cluster.svelte.d.ts +14 -0
  41. package/dist/components/layouts/Container.svelte +40 -0
  42. package/dist/components/layouts/Container.svelte.d.ts +13 -0
  43. package/dist/components/layouts/NavItem.svelte +95 -0
  44. package/dist/components/layouts/NavItem.svelte.d.ts +14 -0
  45. package/dist/components/layouts/Stack.svelte +44 -0
  46. package/dist/components/layouts/Stack.svelte.d.ts +13 -0
  47. package/dist/components/molecules/Accordion.svelte +94 -0
  48. package/dist/components/molecules/Accordion.svelte.d.ts +16 -0
  49. package/dist/components/molecules/CodeBlock.svelte +119 -0
  50. package/dist/components/molecules/CodeBlock.svelte.d.ts +17 -0
  51. package/dist/components/molecules/CopyButton.svelte +80 -0
  52. package/dist/components/molecules/CopyButton.svelte.d.ts +13 -0
  53. package/dist/components/molecules/Dropzone.svelte +140 -0
  54. package/dist/components/molecules/Dropzone.svelte.d.ts +13 -0
  55. package/dist/components/molecules/Field.svelte +57 -0
  56. package/dist/components/molecules/Field.svelte.d.ts +12 -0
  57. package/dist/components/molecules/FileButton.svelte +68 -0
  58. package/dist/components/molecules/FileButton.svelte.d.ts +14 -0
  59. package/dist/components/molecules/FontScalePicker.svelte +21 -0
  60. package/dist/components/molecules/FontScalePicker.svelte.d.ts +6 -0
  61. package/dist/components/molecules/IconButton.svelte +36 -0
  62. package/dist/components/molecules/IconButton.svelte.d.ts +13 -0
  63. package/dist/components/molecules/Menu.svelte +120 -0
  64. package/dist/components/molecules/Menu.svelte.d.ts +17 -0
  65. package/dist/components/molecules/Modal.svelte +263 -0
  66. package/dist/components/molecules/Modal.svelte.d.ts +13 -0
  67. package/dist/components/molecules/OptionButton.svelte +76 -0
  68. package/dist/components/molecules/OptionButton.svelte.d.ts +10 -0
  69. package/dist/components/molecules/Popover.svelte +125 -0
  70. package/dist/components/molecules/Popover.svelte.d.ts +18 -0
  71. package/dist/components/molecules/RadioGroup.svelte +110 -0
  72. package/dist/components/molecules/RadioGroup.svelte.d.ts +16 -0
  73. package/dist/components/molecules/SelectButton.svelte +52 -0
  74. package/dist/components/molecules/SelectButton.svelte.d.ts +15 -0
  75. package/dist/components/molecules/Tabs.svelte +119 -0
  76. package/dist/components/molecules/Tabs.svelte.d.ts +15 -0
  77. package/dist/components/molecules/ThemePicker.svelte +22 -0
  78. package/dist/components/molecules/ThemePicker.svelte.d.ts +6 -0
  79. package/dist/components/molecules/Toaster.svelte +73 -0
  80. package/dist/components/molecules/Toaster.svelte.d.ts +18 -0
  81. package/dist/components/molecules/Toggle.svelte +68 -0
  82. package/dist/components/molecules/Toggle.svelte.d.ts +11 -0
  83. package/dist/components/molecules/Tooltip.svelte +106 -0
  84. package/dist/components/molecules/Tooltip.svelte.d.ts +10 -0
  85. package/dist/components/organisms/DataTable.svelte +145 -0
  86. package/dist/components/organisms/DataTable.svelte.d.ts +43 -0
  87. package/dist/env.d.ts +1 -0
  88. package/dist/env.js +4 -0
  89. package/dist/index.d.ts +46 -0
  90. package/dist/index.js +56 -0
  91. package/dist/stores/fontscale.svelte.d.ts +15 -0
  92. package/dist/stores/fontscale.svelte.js +49 -0
  93. package/dist/stores/theme.svelte.d.ts +96 -0
  94. package/dist/stores/theme.svelte.js +71 -0
  95. package/dist/stores/toast.svelte.d.ts +19 -0
  96. package/dist/stores/toast.svelte.js +26 -0
  97. package/dist/styles/app.css +522 -0
  98. package/dist/styles/variables.css +651 -0
  99. package/package.json +71 -0
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ // Small inline label primitive — a flat, rounded tag for secondary metadata
3
+ // (status, model/effort, cwd). Lighter than a Badge: no tone palette, just the
4
+ // muted pill. Polymorphic via `as`; an `as="button"` chip is interactive
5
+ // (hover affordance). `mono` switches to the monospace family (paths, ids).
6
+ import type { Snippet } from 'svelte';
7
+
8
+ let {
9
+ as = 'span',
10
+ mono = false,
11
+ class: klass = '',
12
+ children,
13
+ ...rest
14
+ }: {
15
+ as?: 'span' | 'button';
16
+ mono?: boolean;
17
+ class?: string;
18
+ children?: Snippet;
19
+ [key: string]: unknown;
20
+ } = $props();
21
+ </script>
22
+
23
+ <svelte:element this={as} class="chip {klass}" class:mono class:interactive={as === 'button'} {...rest}>
24
+ {@render children?.()}
25
+ </svelte:element>
26
+
27
+ <style>
28
+ .chip {
29
+ display: inline-flex;
30
+ align-items: center;
31
+ gap: var(--sp-1);
32
+ font-size: var(--fs-xs);
33
+ color: var(--text-muted);
34
+ background: var(--bg-elevated-2);
35
+ border: 1px solid var(--border);
36
+ border-radius: var(--r-pill);
37
+ padding: 0.1rem var(--sp-2);
38
+ max-width: 100%;
39
+ }
40
+ .mono {
41
+ font-family: var(--font-mono);
42
+ }
43
+ .interactive {
44
+ cursor: pointer;
45
+ transition:
46
+ border-color 0.12s var(--ease),
47
+ color 0.12s var(--ease);
48
+ }
49
+ .interactive:hover {
50
+ border-color: var(--border-strong);
51
+ color: var(--text);
52
+ }
53
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ as?: 'span' | 'button';
4
+ mono?: boolean;
5
+ class?: string;
6
+ children?: Snippet;
7
+ [key: string]: unknown;
8
+ };
9
+ declare const Chip: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type Chip = ReturnType<typeof Chip>;
11
+ export default Chip;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ // Heading primitive: the ONLY place an <h1>–<h6> is emitted. `level` picks both
3
+ // the semantic tag and the default display size; `size` overrides the visual
4
+ // size independently of the level (e.g. toolbar chrome pinned smaller than its
5
+ // heading rank). All sizes/weights/colours come from theme tokens.
6
+ import type { Snippet } from 'svelte';
7
+
8
+ type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
9
+
10
+ let {
11
+ level = 2,
12
+ size,
13
+ tone = 'default',
14
+ class: klass = '',
15
+ children,
16
+ ...rest
17
+ }: {
18
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
19
+ size?: Size;
20
+ tone?: 'default' | 'muted' | 'faint';
21
+ class?: string;
22
+ children?: Snippet;
23
+ [key: string]: unknown;
24
+ } = $props();
25
+
26
+ // Default display size per rank (h1 biggest). `size` overrides it.
27
+ const DEFAULT_SIZE: Record<number, Size> = { 1: '2xl', 2: 'xl', 3: 'lg', 4: 'md', 5: 'sm', 6: 'xs' };
28
+ const fs = $derived(size ?? DEFAULT_SIZE[level]);
29
+ </script>
30
+
31
+ <svelte:element this={`h${level}`} class="heading fs-{fs} tone-{tone} {klass}" {...rest}>
32
+ {@render children?.()}
33
+ </svelte:element>
34
+
35
+ <style>
36
+ .heading {
37
+ margin: 0;
38
+ font-weight: var(--fw-semibold);
39
+ line-height: var(--lh-tight);
40
+ color: var(--text);
41
+ }
42
+ .tone-muted {
43
+ color: var(--text-muted);
44
+ }
45
+ .tone-faint {
46
+ color: var(--text-faint);
47
+ }
48
+ .fs-xs {
49
+ font-size: var(--fs-xs);
50
+ }
51
+ .fs-sm {
52
+ font-size: var(--fs-sm);
53
+ }
54
+ .fs-md {
55
+ font-size: var(--fs-md);
56
+ }
57
+ .fs-lg {
58
+ font-size: var(--fs-lg);
59
+ }
60
+ .fs-xl {
61
+ font-size: var(--fs-xl);
62
+ }
63
+ .fs-2xl {
64
+ font-size: var(--fs-2xl);
65
+ }
66
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type Size = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
3
+ type $$ComponentProps = {
4
+ level?: 1 | 2 | 3 | 4 | 5 | 6;
5
+ size?: Size;
6
+ tone?: 'default' | 'muted' | 'faint';
7
+ class?: string;
8
+ children?: Snippet;
9
+ [key: string]: unknown;
10
+ };
11
+ declare const Heading: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Heading = ReturnType<typeof Heading>;
13
+ export default Heading;
@@ -0,0 +1,151 @@
1
+ <script lang="ts" module>
2
+ // Open icon system. A curated, dependency-free set ships as named glyphs, but
3
+ // the atom is NOT closed: pass a `children` snippet of raw <path>/<circle>/…
4
+ // (24×24 viewBox) to render any icon without adding it to the registry, or
5
+ // register project icons by extending PATHS in your own wrapper. Sized at 1em
6
+ // so it tracks the surrounding text (and the --fs-scale font control).
7
+ export type IconName =
8
+ | 'archive'
9
+ | 'back'
10
+ | 'check'
11
+ | 'chevron-down'
12
+ | 'copy'
13
+ | 'download'
14
+ | 'edit'
15
+ | 'external'
16
+ | 'filter'
17
+ | 'folder'
18
+ | 'fork'
19
+ | 'image'
20
+ | 'info'
21
+ | 'link'
22
+ | 'live'
23
+ | 'markdown'
24
+ | 'menu'
25
+ | 'more'
26
+ | 'plus'
27
+ | 'retry'
28
+ | 'search'
29
+ | 'send'
30
+ | 'settings'
31
+ | 'star'
32
+ | 'stop'
33
+ | 'trash'
34
+ | 'upload'
35
+ | 'warning'
36
+ | 'x';
37
+
38
+ // Glyphs that read better filled than stroked.
39
+ const FILLED = new Set<IconName>(['stop', 'star', 'live']);
40
+ </script>
41
+
42
+ <script lang="ts">
43
+ import type { Snippet } from 'svelte';
44
+
45
+ let {
46
+ name,
47
+ size,
48
+ label,
49
+ children,
50
+ ...rest
51
+ }: {
52
+ /** Named glyph from the registry. Omit when supplying `children`. */
53
+ name?: IconName;
54
+ /** Explicit pixel size. Omit to track the surrounding text (1em) — best
55
+ * for icons sitting inline with a label. */
56
+ size?: number;
57
+ /** When set, the icon is exposed to AT with this label; otherwise it is
58
+ * decorative (aria-hidden) and the parent control carries the label. */
59
+ label?: string;
60
+ /** Raw SVG markup (24×24 viewBox) — overrides `name`. */
61
+ children?: Snippet;
62
+ [key: string]: unknown;
63
+ } = $props();
64
+
65
+ const filled = $derived(name ? FILLED.has(name) : false);
66
+ </script>
67
+
68
+ <svg
69
+ class="icon"
70
+ style={size ? `font-size: ${size}px` : undefined}
71
+ viewBox="0 0 24 24"
72
+ fill={filled ? 'currentColor' : 'none'}
73
+ stroke={filled ? 'none' : 'currentColor'}
74
+ stroke-width="2"
75
+ stroke-linecap="round"
76
+ stroke-linejoin="round"
77
+ role={label ? 'img' : undefined}
78
+ aria-label={label}
79
+ aria-hidden={label ? undefined : 'true'}
80
+ {...rest}
81
+ >
82
+ {#if children}
83
+ {@render children()}
84
+ {:else if name === 'search'}
85
+ <circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" />
86
+ {:else if name === 'back'}
87
+ <path d="m15 18-6-6 6-6" />
88
+ {:else if name === 'check'}
89
+ <path d="M20 6 9 17l-5-5" />
90
+ {:else if name === 'x'}
91
+ <path d="M18 6 6 18" /><path d="m6 6 12 12" />
92
+ {:else if name === 'plus'}
93
+ <path d="M12 5v14" /><path d="M5 12h14" />
94
+ {:else if name === 'chevron-down'}
95
+ <path d="m6 9 6 6 6-6" />
96
+ {:else if name === 'menu'}
97
+ <path d="M4 6h16" /><path d="M4 12h16" /><path d="M4 18h16" />
98
+ {:else if name === 'archive'}
99
+ <rect x="3" y="4" width="18" height="4" rx="1" /><path d="M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8" /><path d="M9 12h6" />
100
+ {:else if name === 'trash'}
101
+ <path d="M3 6h18" /><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2" /><path d="M6 6v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V6" /><path d="M10 11v6" /><path d="M14 11v6" />
102
+ {:else if name === 'download'}
103
+ <path d="M12 3v12" /><path d="m7 10 5 5 5-5" /><path d="M4 19h16" />
104
+ {:else if name === 'upload'}
105
+ <path d="M12 15V3" /><path d="m7 8 5-5 5 5" /><path d="M4 19h16" />
106
+ {:else if name === 'copy'}
107
+ <rect x="9" y="9" width="12" height="12" rx="2" /><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
108
+ {:else if name === 'edit'}
109
+ <path d="M12 20h9" /><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z" />
110
+ {:else if name === 'folder'}
111
+ <path d="M3 7a2 2 0 0 1 2-2h5l2 2h7a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />
112
+ {:else if name === 'link'}
113
+ <path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
114
+ {:else if name === 'external'}
115
+ <path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5" />
116
+ {:else if name === 'markdown'}
117
+ <rect x="3" y="5" width="18" height="14" rx="2" /><path d="M7 15V9l3 3 3-3v6" /><path d="m15 11 2 2 2-2" />
118
+ {:else if name === 'image'}
119
+ <rect x="3" y="3" width="18" height="18" rx="2" /><circle cx="8.5" cy="8.5" r="1.8" /><path d="m21 15-4.5-4.5L7 21" />
120
+ {:else if name === 'fork'}
121
+ <circle cx="6" cy="6" r="2.5" /><circle cx="18" cy="6" r="2.5" /><circle cx="12" cy="19" r="2.5" /><path d="M6 8.5v2a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3v-2" /><path d="M12 13.5v3" />
122
+ {:else if name === 'more'}
123
+ <circle cx="5" cy="12" r="1.6" /><circle cx="12" cy="12" r="1.6" /><circle cx="19" cy="12" r="1.6" />
124
+ {:else if name === 'settings'}
125
+ <circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" />
126
+ {:else if name === 'retry'}
127
+ <path d="M21 12a9 9 0 1 1-2.64-6.36" /><path d="M21 3v6h-6" />
128
+ {:else if name === 'stop'}
129
+ <rect x="6" y="6" width="12" height="12" rx="1.5" />
130
+ {:else if name === 'star'}
131
+ <path d="M12 2.5l2.9 5.9 6.5.95-4.7 4.58 1.1 6.47L12 17.9l-5.8 3.05 1.1-6.47-4.7-4.58 6.5-.95z" />
132
+ {:else if name === 'live'}
133
+ <circle cx="12" cy="12" r="5" />
134
+ {:else if name === 'send'}
135
+ <path d="M22 2 11 13" /><path d="M22 2 15 22l-4-9-9-4z" />
136
+ {:else if name === 'filter'}
137
+ <path d="M3 4h18l-7 8v6l-4 2v-10z" />
138
+ {:else if name === 'info'}
139
+ <circle cx="12" cy="12" r="9" /><path d="M12 11v5" /><path d="M12 8h.01" />
140
+ {:else if name === 'warning'}
141
+ <path d="M10.3 3.6 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.6a2 2 0 0 0-3.4 0Z" /><path d="M12 9v4" /><path d="M12 17h.01" />
142
+ {/if}
143
+ </svg>
144
+
145
+ <style>
146
+ .icon {
147
+ flex: none;
148
+ width: 1em;
149
+ height: 1em;
150
+ }
151
+ </style>
@@ -0,0 +1,18 @@
1
+ export type IconName = 'archive' | 'back' | 'check' | 'chevron-down' | 'copy' | 'download' | 'edit' | 'external' | 'filter' | 'folder' | 'fork' | 'image' | 'info' | 'link' | 'live' | 'markdown' | 'menu' | 'more' | 'plus' | 'retry' | 'search' | 'send' | 'settings' | 'star' | 'stop' | 'trash' | 'upload' | 'warning' | 'x';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ /** Named glyph from the registry. Omit when supplying `children`. */
5
+ name?: IconName;
6
+ /** Explicit pixel size. Omit to track the surrounding text (1em) — best
7
+ * for icons sitting inline with a label. */
8
+ size?: number;
9
+ /** When set, the icon is exposed to AT with this label; otherwise it is
10
+ * decorative (aria-hidden) and the parent control carries the label. */
11
+ label?: string;
12
+ /** Raw SVG markup (24×24 viewBox) — overrides `name`. */
13
+ children?: Snippet;
14
+ [key: string]: unknown;
15
+ };
16
+ declare const Icon: import("svelte").Component<$$ComponentProps, {}, "">;
17
+ type Icon = ReturnType<typeof Icon>;
18
+ export default Icon;
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ // Text input primitive. Owns its styling from theme tokens; supports
3
+ // `bind:value` and passes through every native <input> attribute. `mono`
4
+ // switches to the monospace family (paths, tokens, env values).
5
+ import type { HTMLInputAttributes } from 'svelte/elements';
6
+
7
+ type Props = HTMLInputAttributes & {
8
+ mono?: boolean;
9
+ class?: string;
10
+ value?: HTMLInputAttributes['value'];
11
+ el?: HTMLInputElement | null;
12
+ };
13
+
14
+ let {
15
+ mono = false,
16
+ class: klass = '',
17
+ value = $bindable(),
18
+ el = $bindable(null),
19
+ ...rest
20
+ }: Props = $props();
21
+ </script>
22
+
23
+ <input bind:this={el} class="input {klass}" class:mono bind:value {...rest} />
24
+
25
+ <style>
26
+ .input {
27
+ width: 100%;
28
+ padding: var(--sp-3);
29
+ background: var(--bg);
30
+ border: 1px solid var(--border-strong);
31
+ border-radius: var(--r-md);
32
+ color: var(--text);
33
+ transition: border-color 0.12s var(--ease);
34
+ }
35
+ .input:focus {
36
+ outline: none;
37
+ border-color: var(--accent);
38
+ }
39
+ .mono {
40
+ font-family: var(--font-mono);
41
+ }
42
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ type Props = HTMLInputAttributes & {
3
+ mono?: boolean;
4
+ class?: string;
5
+ value?: HTMLInputAttributes['value'];
6
+ el?: HTMLInputElement | null;
7
+ };
8
+ declare const Input: import("svelte").Component<Props, {}, "value" | "el">;
9
+ type Input = ReturnType<typeof Input>;
10
+ export default Input;
@@ -0,0 +1,31 @@
1
+ <script lang="ts">
2
+ // Inline text link — underlined accent text with no button chrome. Renders an
3
+ // <a> when `href` is given, otherwise a <button> (for in-page actions that
4
+ // read as links, e.g. "Open ChatGPT again").
5
+ import type { Snippet } from 'svelte';
6
+
7
+ let {
8
+ href,
9
+ class: klass = '',
10
+ children,
11
+ ...rest
12
+ }: { href?: string; class?: string; children?: Snippet; [key: string]: unknown } = $props();
13
+ </script>
14
+
15
+ {#if href}
16
+ <a {href} class="link {klass}" {...rest}>{@render children?.()}</a>
17
+ {:else}
18
+ <button type="button" class="link {klass}" {...rest}>{@render children?.()}</button>
19
+ {/if}
20
+
21
+ <style>
22
+ .link {
23
+ background: none;
24
+ border: none;
25
+ padding: 0;
26
+ color: var(--accent, var(--text));
27
+ cursor: pointer;
28
+ text-decoration: underline;
29
+ font: inherit;
30
+ }
31
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ href?: string;
4
+ class?: string;
5
+ children?: Snippet;
6
+ [key: string]: unknown;
7
+ };
8
+ declare const Link: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type Link = ReturnType<typeof Link>;
10
+ export default Link;
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ // Progress bar. Determinate when `value` is a number (0..max); omit `value`
3
+ // for an indeterminate animation. Uses role="progressbar" with the right
4
+ // aria-value* attributes. Token-styled.
5
+ let {
6
+ value,
7
+ max = 100,
8
+ label,
9
+ class: klass = ''
10
+ }: {
11
+ value?: number;
12
+ max?: number;
13
+ label?: string;
14
+ class?: string;
15
+ } = $props();
16
+
17
+ const pct = $derived(value == null ? 0 : Math.max(0, Math.min(100, (value / max) * 100)));
18
+ const indeterminate = $derived(value == null);
19
+ </script>
20
+
21
+ <div
22
+ class="progress {klass}"
23
+ class:indeterminate
24
+ role="progressbar"
25
+ aria-label={label}
26
+ aria-valuemin={indeterminate ? undefined : 0}
27
+ aria-valuemax={indeterminate ? undefined : max}
28
+ aria-valuenow={indeterminate ? undefined : value}
29
+ >
30
+ <div class="bar" style={indeterminate ? undefined : `width: ${pct}%`}></div>
31
+ </div>
32
+
33
+ <style>
34
+ .progress {
35
+ width: 100%;
36
+ height: 0.5rem;
37
+ background: var(--bg-elevated-2);
38
+ border-radius: var(--r-pill);
39
+ overflow: hidden;
40
+ }
41
+ .bar {
42
+ height: 100%;
43
+ background: var(--accent);
44
+ border-radius: inherit;
45
+ transition: width 0.2s var(--ease);
46
+ }
47
+ .progress.indeterminate .bar {
48
+ width: 40%;
49
+ animation: progress-slide 1.1s ease-in-out infinite;
50
+ }
51
+ @keyframes progress-slide {
52
+ 0% {
53
+ transform: translateX(-110%);
54
+ }
55
+ 100% {
56
+ transform: translateX(310%);
57
+ }
58
+ }
59
+ </style>
@@ -0,0 +1,9 @@
1
+ type $$ComponentProps = {
2
+ value?: number;
3
+ max?: number;
4
+ label?: string;
5
+ class?: string;
6
+ };
7
+ declare const Progress: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Progress = ReturnType<typeof Progress>;
9
+ export default Progress;
@@ -0,0 +1,95 @@
1
+ <script lang="ts">
2
+ // Native <select> primitive. Owns its styling from theme tokens; supports
3
+ // `bind:value` and passes through every native attribute. Options are slotted
4
+ // children so call-sites keep full control over <option> rendering.
5
+ //
6
+ // The default variant hides the OS chevron (appearance: none) and draws its
7
+ // own, so the control looks identical across platforms and the chevron sits
8
+ // at a consistent, token-defined offset that adapts to the theme.
9
+ //
10
+ // `variant="ghost"` makes the control fill its (positioned) parent fully
11
+ // transparently — for the "native select overlaid on a custom trigger" pattern
12
+ // (SelectButton): the platform popup with no custom outside-click logic, while
13
+ // a styled trigger shows through underneath.
14
+ import type { Snippet } from 'svelte';
15
+ import type { HTMLSelectAttributes } from 'svelte/elements';
16
+ import Icon from './Icon.svelte';
17
+
18
+ type Props = HTMLSelectAttributes & {
19
+ variant?: 'default' | 'ghost';
20
+ class?: string;
21
+ value?: HTMLSelectAttributes['value'];
22
+ children?: Snippet;
23
+ };
24
+
25
+ let {
26
+ variant = 'default',
27
+ class: klass = '',
28
+ value = $bindable(),
29
+ children,
30
+ ...rest
31
+ }: Props = $props();
32
+ </script>
33
+
34
+ {#if variant === 'ghost'}
35
+ <select class="select ghost {klass}" bind:value {...rest}>
36
+ {@render children?.()}
37
+ </select>
38
+ {:else}
39
+ <div class="select-wrap {klass}">
40
+ <select class="select" bind:value {...rest}>
41
+ {@render children?.()}
42
+ </select>
43
+ <span class="select-chevron" aria-hidden="true">
44
+ <Icon name="chevron-down" size={16} />
45
+ </span>
46
+ </div>
47
+ {/if}
48
+
49
+ <style>
50
+ .select-wrap {
51
+ position: relative;
52
+ display: block;
53
+ width: 100%;
54
+ }
55
+ .select {
56
+ width: 100%;
57
+ padding: var(--sp-3);
58
+ background: var(--bg);
59
+ border: 1px solid var(--border-strong);
60
+ border-radius: var(--r-md);
61
+ color: var(--text);
62
+ transition: border-color 0.12s var(--ease);
63
+ }
64
+ /* Default variant: drop the OS chevron, reserve room for our own. */
65
+ .select-wrap .select {
66
+ appearance: none;
67
+ -webkit-appearance: none;
68
+ padding-right: calc(var(--sp-3) + 1.25rem);
69
+ }
70
+ .select:focus {
71
+ outline: none;
72
+ border-color: var(--accent);
73
+ }
74
+ .select-chevron {
75
+ position: absolute;
76
+ top: 50%;
77
+ right: var(--sp-3);
78
+ transform: translateY(-50%);
79
+ display: inline-flex;
80
+ color: var(--text-muted);
81
+ pointer-events: none; /* clicks fall through to the select */
82
+ }
83
+ /* Transparent overlay: fills the positioned parent, the trigger shows through. */
84
+ .select.ghost {
85
+ position: absolute;
86
+ inset: 0;
87
+ width: 100%;
88
+ height: 100%;
89
+ padding: 0;
90
+ opacity: 0;
91
+ border: none;
92
+ background: none;
93
+ cursor: pointer;
94
+ }
95
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLSelectAttributes } from 'svelte/elements';
3
+ type Props = HTMLSelectAttributes & {
4
+ variant?: 'default' | 'ghost';
5
+ class?: string;
6
+ value?: HTMLSelectAttributes['value'];
7
+ children?: Snippet;
8
+ };
9
+ declare const Select: import("svelte").Component<Props, {}, "value">;
10
+ type Select = ReturnType<typeof Select>;
11
+ export default Select;