@silvery/examples 0.17.3 → 0.17.5
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/dist/UPNG-ShUlaTDh.mjs +5074 -0
- package/dist/__vite-browser-external-2447137e-Bopa5BFR.mjs +4 -0
- package/dist/_banner-A70_y2Vi.mjs +43 -0
- package/dist/ansi-0VXlUmNn.mjs +16397 -0
- package/dist/apng-B0gRaDVT.mjs +3 -0
- package/dist/apng-BTRDTfDW.mjs +68 -0
- package/dist/apps/aichat/index.mjs +1298 -0
- package/dist/apps/app-todo.mjs +138 -0
- package/dist/apps/async-data.mjs +203 -0
- package/dist/apps/cli-wizard.mjs +338 -0
- package/dist/apps/clipboard.mjs +197 -0
- package/dist/apps/components.mjs +863 -0
- package/dist/apps/data-explorer.mjs +482 -0
- package/dist/apps/dev-tools.mjs +396 -0
- package/dist/apps/explorer.mjs +697 -0
- package/dist/apps/gallery.mjs +765 -0
- package/dist/apps/inline-bench.mjs +115 -0
- package/dist/apps/kanban.mjs +279 -0
- package/dist/apps/layout-ref.mjs +186 -0
- package/dist/apps/outline.mjs +202 -0
- package/dist/apps/paste-demo.mjs +188 -0
- package/dist/apps/scroll.mjs +85 -0
- package/dist/apps/search-filter.mjs +286 -0
- package/dist/apps/selection.mjs +354 -0
- package/dist/apps/spatial-focus-demo.mjs +387 -0
- package/dist/apps/task-list.mjs +257 -0
- package/dist/apps/terminal-caps-demo.mjs +314 -0
- package/dist/apps/terminal.mjs +871 -0
- package/dist/apps/text-selection-demo.mjs +253 -0
- package/dist/apps/textarea.mjs +177 -0
- package/dist/apps/theme.mjs +660 -0
- package/dist/apps/transform.mjs +214 -0
- package/dist/apps/virtual-10k.mjs +421 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backends-Dj-11kZF.mjs +1179 -0
- package/dist/backends-U3QwStfO.mjs +3 -0
- package/dist/{cli.mjs → bin/cli.mjs} +15 -19
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/components/counter.mjs +47 -0
- package/dist/components/hello.mjs +30 -0
- package/dist/components/progress-bar.mjs +58 -0
- package/dist/components/select-list.mjs +84 -0
- package/dist/components/spinner.mjs +56 -0
- package/dist/components/text-input.mjs +61 -0
- package/dist/components/virtual-list.mjs +50 -0
- package/dist/flexily-zero-adapter-ByVzLTFP.mjs +3374 -0
- package/dist/gif-B6NGH5gs.mjs +3 -0
- package/dist/gif-CfkOF-iG.mjs +71 -0
- package/dist/gifenc-BI4ihP_T.mjs +728 -0
- package/dist/key-mapping-5oYQdAQE.mjs +3 -0
- package/dist/key-mapping-D4LR1go6.mjs +130 -0
- package/dist/layout/dashboard.mjs +1203 -0
- package/dist/layout/live-resize.mjs +302 -0
- package/dist/layout/overflow.mjs +69 -0
- package/dist/layout/text-layout.mjs +334 -0
- package/dist/node-nsrAOjH4.mjs +1083 -0
- package/dist/plugins-CT0DdV_E.mjs +3056 -0
- package/dist/resvg-js-Cnk2o49d.mjs +201 -0
- package/dist/src-9ZhfQyzD.mjs +814 -0
- package/dist/src-CUUOuRH6.mjs +5322 -0
- package/dist/src-jO3Zuzjj.mjs +23538 -0
- package/dist/usingCtx-CsEf0xO3.mjs +57 -0
- package/dist/yoga-adapter-BSQHuMV9.mjs +237 -0
- package/package.json +21 -14
- package/_banner.tsx +0 -60
- package/apps/aichat/components.tsx +0 -469
- package/apps/aichat/index.tsx +0 -220
- package/apps/aichat/script.ts +0 -460
- package/apps/aichat/state.ts +0 -325
- package/apps/aichat/types.ts +0 -19
- package/apps/app-todo.tsx +0 -201
- package/apps/async-data.tsx +0 -196
- package/apps/cli-wizard.tsx +0 -332
- package/apps/clipboard.tsx +0 -183
- package/apps/components.tsx +0 -658
- package/apps/data-explorer.tsx +0 -490
- package/apps/dev-tools.tsx +0 -395
- package/apps/explorer.tsx +0 -731
- package/apps/gallery.tsx +0 -653
- package/apps/inline-bench.tsx +0 -138
- package/apps/kanban.tsx +0 -265
- package/apps/layout-ref.tsx +0 -173
- package/apps/outline.tsx +0 -160
- package/apps/panes/index.tsx +0 -203
- package/apps/paste-demo.tsx +0 -185
- package/apps/scroll.tsx +0 -80
- package/apps/search-filter.tsx +0 -240
- package/apps/selection.tsx +0 -346
- package/apps/spatial-focus-demo.tsx +0 -372
- package/apps/task-list.tsx +0 -271
- package/apps/terminal-caps-demo.tsx +0 -317
- package/apps/terminal.tsx +0 -784
- package/apps/text-selection-demo.tsx +0 -193
- package/apps/textarea.tsx +0 -155
- package/apps/theme.tsx +0 -515
- package/apps/transform.tsx +0 -229
- package/apps/virtual-10k.tsx +0 -405
- package/apps/vterm-demo/index.tsx +0 -216
- package/components/counter.tsx +0 -49
- package/components/hello.tsx +0 -38
- package/components/progress-bar.tsx +0 -52
- package/components/select-list.tsx +0 -54
- package/components/spinner.tsx +0 -44
- package/components/text-input.tsx +0 -61
- package/components/virtual-list.tsx +0 -56
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs.map +0 -1
- package/layout/dashboard.tsx +0 -953
- package/layout/live-resize.tsx +0 -282
- package/layout/overflow.tsx +0 -51
- package/layout/text-layout.tsx +0 -283
package/apps/theme.tsx
DELETED
|
@@ -1,515 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Theme Explorer Example
|
|
3
|
-
*
|
|
4
|
-
* Browse all built-in palettes with live color preview.
|
|
5
|
-
* First entry is always the detected terminal theme.
|
|
6
|
-
* Left panel: scrollable list with mini color swatches.
|
|
7
|
-
* Right panel: live preview wrapped in ThemeProvider showing
|
|
8
|
-
* semantic tokens, ANSI 16-color table, surfaces, components, and typography.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import React, { useState } from "react"
|
|
12
|
-
import {
|
|
13
|
-
render,
|
|
14
|
-
Box,
|
|
15
|
-
Text,
|
|
16
|
-
Link,
|
|
17
|
-
Kbd,
|
|
18
|
-
Muted,
|
|
19
|
-
H1,
|
|
20
|
-
H2,
|
|
21
|
-
H3,
|
|
22
|
-
Strong,
|
|
23
|
-
Small,
|
|
24
|
-
Lead,
|
|
25
|
-
Code,
|
|
26
|
-
P,
|
|
27
|
-
Blockquote,
|
|
28
|
-
ProgressBar,
|
|
29
|
-
Spinner,
|
|
30
|
-
Badge,
|
|
31
|
-
Toggle,
|
|
32
|
-
Divider,
|
|
33
|
-
ThemeProvider,
|
|
34
|
-
useInput,
|
|
35
|
-
useApp,
|
|
36
|
-
createTerm,
|
|
37
|
-
detectTheme,
|
|
38
|
-
type Key,
|
|
39
|
-
type Theme,
|
|
40
|
-
} from "silvery"
|
|
41
|
-
import {
|
|
42
|
-
builtinPalettes,
|
|
43
|
-
deriveTheme,
|
|
44
|
-
ansi16DarkTheme,
|
|
45
|
-
ansi16LightTheme,
|
|
46
|
-
type ColorPalette,
|
|
47
|
-
type ThemeAdjustment,
|
|
48
|
-
} from "@silvery/theme"
|
|
49
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
50
|
-
|
|
51
|
-
export const meta: ExampleMeta = {
|
|
52
|
-
name: "Theme Explorer",
|
|
53
|
-
description: "Browse built-in palettes with live color preview",
|
|
54
|
-
demo: true,
|
|
55
|
-
features: ["ThemeProvider", "builtinThemes", "detectTheme", "semantic tokens", "components"],
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// Types
|
|
60
|
-
// ============================================================================
|
|
61
|
-
|
|
62
|
-
export interface ThemeEntry {
|
|
63
|
-
name: string
|
|
64
|
-
palette: ColorPalette | null // null for detected theme
|
|
65
|
-
theme: Theme
|
|
66
|
-
adjustments: ThemeAdjustment[]
|
|
67
|
-
detected?: boolean
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ============================================================================
|
|
71
|
-
// Data (populated in main, after detection)
|
|
72
|
-
// ============================================================================
|
|
73
|
-
|
|
74
|
-
let allEntries: ThemeEntry[] = []
|
|
75
|
-
|
|
76
|
-
// ============================================================================
|
|
77
|
-
// Components
|
|
78
|
-
// ============================================================================
|
|
79
|
-
|
|
80
|
-
/** Small color swatch: 2 colored block chars */
|
|
81
|
-
function Swatch({ color }: { color: string }) {
|
|
82
|
-
return <Text color={color}>{"██"}</Text>
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Mini swatch: 4 colored blocks showing palette character */
|
|
86
|
-
function MiniSwatch({ palette }: { palette: ColorPalette }) {
|
|
87
|
-
return (
|
|
88
|
-
<Text>
|
|
89
|
-
<Text color={palette.red}>{"█"}</Text>
|
|
90
|
-
<Text color={palette.green}>{"█"}</Text>
|
|
91
|
-
<Text color={palette.blue}>{"█"}</Text>
|
|
92
|
-
<Text color={palette.yellow}>{"█"}</Text>
|
|
93
|
-
</Text>
|
|
94
|
-
)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Mini swatch from theme tokens (for detected theme without a palette) */
|
|
98
|
-
function ThemeMiniSwatch({ theme }: { theme: Theme }) {
|
|
99
|
-
return (
|
|
100
|
-
<Text>
|
|
101
|
-
<Text color={theme.error}>{"█"}</Text>
|
|
102
|
-
<Text color={theme.success}>{"█"}</Text>
|
|
103
|
-
<Text color={theme.info}>{"█"}</Text>
|
|
104
|
-
<Text color={theme.primary}>{"█"}</Text>
|
|
105
|
-
</Text>
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Left panel: theme list with color swatches */
|
|
110
|
-
function ThemeList({ entries, selectedIndex }: { entries: ThemeEntry[]; selectedIndex: number }) {
|
|
111
|
-
return (
|
|
112
|
-
<Box flexDirection="column" width={30} borderStyle="single" overflow="scroll" scrollTo={selectedIndex}>
|
|
113
|
-
<Box paddingX={1}>
|
|
114
|
-
<Text bold color="$primary">
|
|
115
|
-
Palettes
|
|
116
|
-
</Text>
|
|
117
|
-
<Muted> ({entries.length})</Muted>
|
|
118
|
-
</Box>
|
|
119
|
-
<Divider />
|
|
120
|
-
<Box flexDirection="column" paddingX={1}>
|
|
121
|
-
{entries.map((entry, i) => {
|
|
122
|
-
const isSelected = i === selectedIndex
|
|
123
|
-
return (
|
|
124
|
-
<Box key={entry.name}>
|
|
125
|
-
<Text inverse={isSelected}>
|
|
126
|
-
{isSelected ? "▸" : " "} {entry.name.padEnd(18)}
|
|
127
|
-
</Text>
|
|
128
|
-
<Text> </Text>
|
|
129
|
-
{entry.palette ? <MiniSwatch palette={entry.palette} /> : <ThemeMiniSwatch theme={entry.theme} />}
|
|
130
|
-
</Box>
|
|
131
|
-
)
|
|
132
|
-
})}
|
|
133
|
-
</Box>
|
|
134
|
-
</Box>
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Semantic token showcase row */
|
|
139
|
-
function SemanticTokens() {
|
|
140
|
-
const tokens: Array<{ name: string; token: string; icon: string }> = [
|
|
141
|
-
{ name: "primary", token: "$primary", icon: "●" },
|
|
142
|
-
{ name: "success", token: "$success", icon: "✓" },
|
|
143
|
-
{ name: "warning", token: "$warning", icon: "⚠" },
|
|
144
|
-
{ name: "error", token: "$error", icon: "✗" },
|
|
145
|
-
{ name: "info", token: "$info", icon: "ℹ" },
|
|
146
|
-
{ name: "accent", token: "$accent", icon: "◆" },
|
|
147
|
-
{ name: "muted", token: "$muted", icon: "○" },
|
|
148
|
-
{ name: "link", token: "$link", icon: "🔗" },
|
|
149
|
-
]
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<Box flexDirection="column">
|
|
153
|
-
<H2>Semantic Tokens</H2>
|
|
154
|
-
<Box flexDirection="row" flexWrap="wrap" gap={1} paddingX={1}>
|
|
155
|
-
{tokens.map((t) => (
|
|
156
|
-
<Text key={t.name} color={t.token}>
|
|
157
|
-
{t.icon} {t.name}
|
|
158
|
-
</Text>
|
|
159
|
-
))}
|
|
160
|
-
</Box>
|
|
161
|
-
</Box>
|
|
162
|
-
)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** ANSI 16-color table */
|
|
166
|
-
function AnsiColorTable({ palette }: { palette: ColorPalette }) {
|
|
167
|
-
const normal = [
|
|
168
|
-
palette.black,
|
|
169
|
-
palette.red,
|
|
170
|
-
palette.green,
|
|
171
|
-
palette.yellow,
|
|
172
|
-
palette.blue,
|
|
173
|
-
palette.magenta,
|
|
174
|
-
palette.cyan,
|
|
175
|
-
palette.white,
|
|
176
|
-
]
|
|
177
|
-
const bright = [
|
|
178
|
-
palette.brightBlack,
|
|
179
|
-
palette.brightRed,
|
|
180
|
-
palette.brightGreen,
|
|
181
|
-
palette.brightYellow,
|
|
182
|
-
palette.brightBlue,
|
|
183
|
-
palette.brightMagenta,
|
|
184
|
-
palette.brightCyan,
|
|
185
|
-
palette.brightWhite,
|
|
186
|
-
]
|
|
187
|
-
|
|
188
|
-
return (
|
|
189
|
-
<Box flexDirection="column">
|
|
190
|
-
<H2>ANSI 16 Colors</H2>
|
|
191
|
-
<Box paddingX={1} gap={1}>
|
|
192
|
-
<Box>
|
|
193
|
-
<Muted>0-7 </Muted>
|
|
194
|
-
{normal.map((c, i) => (
|
|
195
|
-
<Swatch key={i} color={c} />
|
|
196
|
-
))}
|
|
197
|
-
</Box>
|
|
198
|
-
</Box>
|
|
199
|
-
<Box paddingX={1} gap={1}>
|
|
200
|
-
<Box>
|
|
201
|
-
<Muted>8-15 </Muted>
|
|
202
|
-
{bright.map((c, i) => (
|
|
203
|
-
<Swatch key={i} color={c} />
|
|
204
|
-
))}
|
|
205
|
-
</Box>
|
|
206
|
-
</Box>
|
|
207
|
-
</Box>
|
|
208
|
-
)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** ANSI 16-color table from theme palette array (for detected theme) */
|
|
212
|
-
function ThemeAnsiColorTable({ palette }: { palette: string[] }) {
|
|
213
|
-
const normal = palette.slice(0, 8)
|
|
214
|
-
const bright = palette.slice(8, 16)
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<Box flexDirection="column">
|
|
218
|
-
<H2>ANSI 16 Colors</H2>
|
|
219
|
-
<Box paddingX={1} gap={1}>
|
|
220
|
-
<Box>
|
|
221
|
-
<Muted>0-7 </Muted>
|
|
222
|
-
{normal.map((c, i) => (
|
|
223
|
-
<Swatch key={i} color={c} />
|
|
224
|
-
))}
|
|
225
|
-
</Box>
|
|
226
|
-
</Box>
|
|
227
|
-
<Box paddingX={1} gap={1}>
|
|
228
|
-
<Box>
|
|
229
|
-
<Muted>8-15 </Muted>
|
|
230
|
-
{bright.map((c, i) => (
|
|
231
|
-
<Swatch key={i} color={c} />
|
|
232
|
-
))}
|
|
233
|
-
</Box>
|
|
234
|
-
</Box>
|
|
235
|
-
</Box>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/** Component showcase — real silvery components using theme tokens */
|
|
240
|
-
function ComponentShowcase() {
|
|
241
|
-
return (
|
|
242
|
-
<Box flexDirection="column">
|
|
243
|
-
<H2>Components</H2>
|
|
244
|
-
<Box flexDirection="column" paddingX={1} gap={1}>
|
|
245
|
-
{/* Links */}
|
|
246
|
-
<Box gap={2}>
|
|
247
|
-
<Link href="https://silvery.dev">silvery.dev</Link>
|
|
248
|
-
<Link href="https://example.com" color="$primary">
|
|
249
|
-
primary link
|
|
250
|
-
</Link>
|
|
251
|
-
<Link href="https://example.com" dim>
|
|
252
|
-
dim link
|
|
253
|
-
</Link>
|
|
254
|
-
</Box>
|
|
255
|
-
|
|
256
|
-
{/* Badges */}
|
|
257
|
-
<Box gap={1} flexWrap="wrap">
|
|
258
|
-
<Badge label="default" variant="default" />
|
|
259
|
-
<Badge label="success" variant="success" />
|
|
260
|
-
<Badge label="error" variant="error" />
|
|
261
|
-
<Badge label="warning" variant="warning" />
|
|
262
|
-
<Badge label="primary" variant="primary" />
|
|
263
|
-
</Box>
|
|
264
|
-
|
|
265
|
-
{/* Progress + Spinner */}
|
|
266
|
-
<Box gap={2}>
|
|
267
|
-
<Box gap={1}>
|
|
268
|
-
<Muted>Progress:</Muted>
|
|
269
|
-
<Box width={16}>
|
|
270
|
-
<ProgressBar value={0.65} />
|
|
271
|
-
</Box>
|
|
272
|
-
</Box>
|
|
273
|
-
<Box gap={1}>
|
|
274
|
-
<Spinner />
|
|
275
|
-
<Muted>Loading...</Muted>
|
|
276
|
-
</Box>
|
|
277
|
-
</Box>
|
|
278
|
-
|
|
279
|
-
{/* Toggle */}
|
|
280
|
-
<Box gap={2}>
|
|
281
|
-
<Box gap={1}>
|
|
282
|
-
<Toggle value={true} onChange={() => {}} label="Enabled" />
|
|
283
|
-
</Box>
|
|
284
|
-
<Box gap={1}>
|
|
285
|
-
<Toggle value={false} onChange={() => {}} label="Disabled" />
|
|
286
|
-
</Box>
|
|
287
|
-
</Box>
|
|
288
|
-
|
|
289
|
-
{/* Dialog box */}
|
|
290
|
-
<Box borderStyle="round" paddingX={1} flexDirection="column">
|
|
291
|
-
<Text bold color="$primary">
|
|
292
|
-
Dialog Title
|
|
293
|
-
</Text>
|
|
294
|
-
<Text>Body text with default colors.</Text>
|
|
295
|
-
<Muted>Muted secondary info</Muted>
|
|
296
|
-
</Box>
|
|
297
|
-
|
|
298
|
-
{/* Input border */}
|
|
299
|
-
<Box borderStyle="single" borderColor="$inputborder" paddingX={1} width={30}>
|
|
300
|
-
<Text color="$disabledfg">Search...</Text>
|
|
301
|
-
</Box>
|
|
302
|
-
|
|
303
|
-
{/* Focus border */}
|
|
304
|
-
<Box borderStyle="single" borderColor="$focusborder" paddingX={1} width={30}>
|
|
305
|
-
<Text>Focused input</Text>
|
|
306
|
-
</Box>
|
|
307
|
-
</Box>
|
|
308
|
-
</Box>
|
|
309
|
-
)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/** Typography samples */
|
|
313
|
-
function TypographySamples() {
|
|
314
|
-
return (
|
|
315
|
-
<Box flexDirection="column">
|
|
316
|
-
<H2>Typography</H2>
|
|
317
|
-
<Box flexDirection="column" paddingX={1}>
|
|
318
|
-
<H1>Heading 1</H1>
|
|
319
|
-
<H2>Heading 2</H2>
|
|
320
|
-
<H3>Heading 3</H3>
|
|
321
|
-
<Strong>Strong text</Strong>
|
|
322
|
-
<Lead>Lead text (italic)</Lead>
|
|
323
|
-
<P>Normal paragraph text</P>
|
|
324
|
-
<Muted>Muted text</Muted>
|
|
325
|
-
<Small>Small text</Small>
|
|
326
|
-
<Box gap={1}>
|
|
327
|
-
<Kbd>Kbd</Kbd>
|
|
328
|
-
<Kbd>⌘K</Kbd>
|
|
329
|
-
<Kbd>Enter</Kbd>
|
|
330
|
-
</Box>
|
|
331
|
-
<Code>inline code</Code>
|
|
332
|
-
<Blockquote>Blockquote text</Blockquote>
|
|
333
|
-
</Box>
|
|
334
|
-
</Box>
|
|
335
|
-
)
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/** Surface pairs showcase */
|
|
339
|
-
function SurfacePairs() {
|
|
340
|
-
return (
|
|
341
|
-
<Box flexDirection="column">
|
|
342
|
-
<H2>Surfaces</H2>
|
|
343
|
-
<Box flexDirection="column" paddingX={1} gap={0}>
|
|
344
|
-
<Box gap={1}>
|
|
345
|
-
<Box backgroundColor="$surfacebg" paddingX={1}>
|
|
346
|
-
<Text color="$surface">surface</Text>
|
|
347
|
-
</Box>
|
|
348
|
-
<Box backgroundColor="$inversebg" paddingX={1}>
|
|
349
|
-
<Text color="$inverse">inverse</Text>
|
|
350
|
-
</Box>
|
|
351
|
-
<Box backgroundColor="$mutedbg" paddingX={1}>
|
|
352
|
-
<Text>muted bg</Text>
|
|
353
|
-
</Box>
|
|
354
|
-
</Box>
|
|
355
|
-
<Box gap={1}>
|
|
356
|
-
<Box backgroundColor="$primary" paddingX={1}>
|
|
357
|
-
<Text color="$primaryfg">primary</Text>
|
|
358
|
-
</Box>
|
|
359
|
-
<Box backgroundColor="$error" paddingX={1}>
|
|
360
|
-
<Text color="$errorfg">error</Text>
|
|
361
|
-
</Box>
|
|
362
|
-
<Box backgroundColor="$success" paddingX={1}>
|
|
363
|
-
<Text color="$successfg">success</Text>
|
|
364
|
-
</Box>
|
|
365
|
-
</Box>
|
|
366
|
-
<Box gap={1}>
|
|
367
|
-
<Box backgroundColor="$selectionbg" paddingX={1}>
|
|
368
|
-
<Text color="$selection">selection</Text>
|
|
369
|
-
</Box>
|
|
370
|
-
<Box backgroundColor="$popoverbg" paddingX={1}>
|
|
371
|
-
<Text color="$popover">popover</Text>
|
|
372
|
-
</Box>
|
|
373
|
-
<Box backgroundColor="$accent" paddingX={1}>
|
|
374
|
-
<Text color="$accentfg">accent</Text>
|
|
375
|
-
</Box>
|
|
376
|
-
</Box>
|
|
377
|
-
</Box>
|
|
378
|
-
</Box>
|
|
379
|
-
)
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/** Contrast adjustments made during derivation */
|
|
383
|
-
function AdjustmentLog({ adjustments = [] }: { adjustments?: ThemeAdjustment[] }) {
|
|
384
|
-
if (adjustments.length === 0) {
|
|
385
|
-
return (
|
|
386
|
-
<Box flexDirection="column">
|
|
387
|
-
<H2>Contrast Adjustments</H2>
|
|
388
|
-
<Box paddingX={1}>
|
|
389
|
-
<Text color="$success">✓ No adjustments needed — all tokens meet contrast targets</Text>
|
|
390
|
-
</Box>
|
|
391
|
-
</Box>
|
|
392
|
-
)
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return (
|
|
396
|
-
<Box flexDirection="column">
|
|
397
|
-
<H2>Contrast Adjustments ({adjustments.length})</H2>
|
|
398
|
-
<Box flexDirection="column" paddingX={1}>
|
|
399
|
-
{adjustments.map((adj, i) => (
|
|
400
|
-
<Box key={i} gap={1}>
|
|
401
|
-
<Text bold>{adj.token.padEnd(12)}</Text>
|
|
402
|
-
<Text color={adj.from}>{"██"}</Text>
|
|
403
|
-
<Muted>→</Muted>
|
|
404
|
-
<Text color={adj.to}>{"██"}</Text>
|
|
405
|
-
<Muted>
|
|
406
|
-
{adj.ratioBefore.toFixed(1)}→{adj.ratioAfter.toFixed(1)}:1 (target {adj.target}:1)
|
|
407
|
-
</Muted>
|
|
408
|
-
</Box>
|
|
409
|
-
))}
|
|
410
|
-
</Box>
|
|
411
|
-
</Box>
|
|
412
|
-
)
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/** Right panel: live preview wrapped in selected ThemeProvider */
|
|
416
|
-
function ThemePreview({ entry }: { entry: ThemeEntry }) {
|
|
417
|
-
const label = entry.detected ? "(detected)" : entry.palette?.dark === false ? "(light)" : "(dark)"
|
|
418
|
-
|
|
419
|
-
return (
|
|
420
|
-
<ThemeProvider theme={entry.theme}>
|
|
421
|
-
<Box theme={entry.theme} flexDirection="column" flexGrow={1} borderStyle="single" overflow="scroll">
|
|
422
|
-
<Box paddingX={1} gap={1}>
|
|
423
|
-
<H1>{entry.name}</H1>
|
|
424
|
-
<Muted>{label}</Muted>
|
|
425
|
-
</Box>
|
|
426
|
-
<Divider />
|
|
427
|
-
<Box flexDirection="column" gap={1}>
|
|
428
|
-
<SemanticTokens />
|
|
429
|
-
{entry.palette ? (
|
|
430
|
-
<AnsiColorTable palette={entry.palette} />
|
|
431
|
-
) : (
|
|
432
|
-
<ThemeAnsiColorTable palette={entry.theme.palette} />
|
|
433
|
-
)}
|
|
434
|
-
<SurfacePairs />
|
|
435
|
-
<ComponentShowcase />
|
|
436
|
-
<TypographySamples />
|
|
437
|
-
<AdjustmentLog adjustments={entry.adjustments} />
|
|
438
|
-
</Box>
|
|
439
|
-
</Box>
|
|
440
|
-
</ThemeProvider>
|
|
441
|
-
)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function HelpBar() {
|
|
445
|
-
return (
|
|
446
|
-
<Muted>
|
|
447
|
-
{" "}
|
|
448
|
-
<Kbd>j/k</Kbd> navigate <Kbd>Esc/q</Kbd> quit
|
|
449
|
-
</Muted>
|
|
450
|
-
)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
export function ThemeExplorer({ entries }: { entries: ThemeEntry[] }) {
|
|
454
|
-
const { exit } = useApp()
|
|
455
|
-
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
456
|
-
|
|
457
|
-
useInput((input: string, key: Key) => {
|
|
458
|
-
if (input === "q" || key.escape) {
|
|
459
|
-
exit()
|
|
460
|
-
}
|
|
461
|
-
if (key.downArrow || input === "j") {
|
|
462
|
-
setSelectedIndex((i) => Math.min(i + 1, entries.length - 1))
|
|
463
|
-
}
|
|
464
|
-
if (key.upArrow || input === "k") {
|
|
465
|
-
setSelectedIndex((i) => Math.max(i - 1, 0))
|
|
466
|
-
}
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
const entry = entries[selectedIndex]!
|
|
470
|
-
|
|
471
|
-
return (
|
|
472
|
-
<Box flexDirection="column" height="100%" padding={1}>
|
|
473
|
-
<Box flexGrow={1} flexDirection="row" gap={1} overflow="hidden">
|
|
474
|
-
<ThemeList entries={entries} selectedIndex={selectedIndex} />
|
|
475
|
-
<ThemePreview entry={entry} />
|
|
476
|
-
</Box>
|
|
477
|
-
<HelpBar />
|
|
478
|
-
</Box>
|
|
479
|
-
)
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// ============================================================================
|
|
483
|
-
// Main
|
|
484
|
-
// ============================================================================
|
|
485
|
-
|
|
486
|
-
export async function main() {
|
|
487
|
-
// Detect terminal theme BEFORE entering alternate screen
|
|
488
|
-
const detectedTheme = await detectTheme()
|
|
489
|
-
|
|
490
|
-
const builtinEntries: ThemeEntry[] = Object.entries(builtinPalettes).map(([name, palette]) => {
|
|
491
|
-
const adjustments: ThemeAdjustment[] = []
|
|
492
|
-
const theme = deriveTheme(palette, "truecolor", adjustments)
|
|
493
|
-
return { name, palette, theme, adjustments }
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
allEntries = [
|
|
497
|
-
{ name: "Detected", palette: null, theme: detectedTheme, adjustments: [], detected: true },
|
|
498
|
-
{ name: "ANSI 16 Dark", palette: null, theme: ansi16DarkTheme, adjustments: [] },
|
|
499
|
-
{ name: "ANSI 16 Light", palette: null, theme: ansi16LightTheme, adjustments: [] },
|
|
500
|
-
...builtinEntries,
|
|
501
|
-
]
|
|
502
|
-
|
|
503
|
-
using term = createTerm()
|
|
504
|
-
const { waitUntilExit } = await render(
|
|
505
|
-
<ExampleBanner meta={meta} controls="j/k navigate Esc/q quit">
|
|
506
|
-
<ThemeExplorer entries={allEntries} />
|
|
507
|
-
</ExampleBanner>,
|
|
508
|
-
term,
|
|
509
|
-
)
|
|
510
|
-
await waitUntilExit()
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
if (import.meta.main) {
|
|
514
|
-
await main()
|
|
515
|
-
}
|