silvery 0.3.0 → 0.4.1
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/README.md +41 -145
- package/dist/chalk.js +3 -0
- package/dist/chalk.js.map +11 -0
- package/dist/index.js +340 -0
- package/dist/index.js.map +282 -0
- package/dist/ink.js +129 -0
- package/dist/ink.js.map +140 -0
- package/dist/runtime.js +394 -0
- package/dist/runtime.js.map +286 -0
- package/dist/theme.js +343 -0
- package/dist/theme.js.map +286 -0
- package/dist/ui/animation.js +3 -0
- package/dist/ui/animation.js.map +15 -0
- package/dist/ui/ansi.js +3 -0
- package/dist/ui/ansi.js.map +10 -0
- package/dist/ui/cli.js +8 -0
- package/dist/ui/cli.js.map +14 -0
- package/dist/ui/display.js +4 -0
- package/dist/ui/display.js.map +10 -0
- package/dist/ui/image.js +4 -0
- package/dist/ui/image.js.map +15 -0
- package/dist/ui/input.js +3 -0
- package/dist/ui/input.js.map +11 -0
- package/dist/ui/progress.js +8 -0
- package/dist/ui/progress.js.map +20 -0
- package/dist/ui/react.js +3 -0
- package/dist/ui/react.js.map +15 -0
- package/dist/ui/utils.js +3 -0
- package/dist/ui/utils.js.map +10 -0
- package/dist/ui/wrappers.js +14 -0
- package/dist/ui/wrappers.js.map +19 -0
- package/dist/ui.js +17 -0
- package/dist/ui.js.map +20 -0
- package/package.json +67 -15
- package/src/index.ts +67 -1
- package/src/runtime.ts +4 -0
- package/src/theme.ts +4 -0
- package/src/ui/animation.ts +2 -0
- package/src/ui/ansi.ts +2 -0
- package/src/ui/cli.ts +2 -0
- package/src/ui/display.ts +2 -0
- package/src/ui/image.ts +2 -0
- package/src/ui/input.ts +2 -0
- package/src/ui/progress.ts +2 -0
- package/src/ui/react.ts +2 -0
- package/src/ui/utils.ts +2 -0
- package/src/ui/wrappers.ts +2 -0
- package/src/ui.ts +4 -0
- package/examples/CLAUDE.md +0 -75
- package/examples/_banner.tsx +0 -60
- package/examples/cli.ts +0 -228
- package/examples/index.md +0 -101
- package/examples/inline/inline-nontty.tsx +0 -98
- package/examples/inline/inline-progress.tsx +0 -79
- package/examples/inline/inline-simple.tsx +0 -63
- package/examples/inline/scrollback.tsx +0 -185
- package/examples/interactive/_input-debug.tsx +0 -110
- package/examples/interactive/_stdin-test.ts +0 -71
- package/examples/interactive/_textarea-bare.tsx +0 -45
- package/examples/interactive/aichat/components.tsx +0 -468
- package/examples/interactive/aichat/index.tsx +0 -207
- package/examples/interactive/aichat/script.ts +0 -460
- package/examples/interactive/aichat/state.ts +0 -326
- package/examples/interactive/aichat/types.ts +0 -19
- package/examples/interactive/app-todo.tsx +0 -198
- package/examples/interactive/async-data.tsx +0 -208
- package/examples/interactive/cli-wizard.tsx +0 -332
- package/examples/interactive/clipboard.tsx +0 -183
- package/examples/interactive/components.tsx +0 -463
- package/examples/interactive/data-explorer.tsx +0 -506
- package/examples/interactive/dev-tools.tsx +0 -379
- package/examples/interactive/explorer.tsx +0 -747
- package/examples/interactive/gallery.tsx +0 -652
- package/examples/interactive/inline-bench.tsx +0 -136
- package/examples/interactive/kanban.tsx +0 -267
- package/examples/interactive/layout-ref.tsx +0 -185
- package/examples/interactive/outline.tsx +0 -171
- package/examples/interactive/paste-demo.tsx +0 -198
- package/examples/interactive/scroll.tsx +0 -77
- package/examples/interactive/search-filter.tsx +0 -240
- package/examples/interactive/task-list.tsx +0 -279
- package/examples/interactive/terminal.tsx +0 -798
- package/examples/interactive/textarea.tsx +0 -103
- package/examples/interactive/theme.tsx +0 -336
- package/examples/interactive/transform.tsx +0 -256
- package/examples/interactive/virtual-10k.tsx +0 -413
- package/examples/kitty/canvas.tsx +0 -519
- package/examples/kitty/generate-samples.ts +0 -236
- package/examples/kitty/image-component.tsx +0 -273
- package/examples/kitty/images.tsx +0 -604
- package/examples/kitty/input.tsx +0 -371
- package/examples/kitty/keys.tsx +0 -378
- package/examples/kitty/paint.tsx +0 -1017
- package/examples/layout/dashboard.tsx +0 -551
- package/examples/layout/live-resize.tsx +0 -290
- package/examples/layout/overflow.tsx +0 -51
- package/examples/playground/README.md +0 -69
- package/examples/playground/build.ts +0 -61
- package/examples/playground/index.html +0 -420
- package/examples/playground/playground-app.tsx +0 -416
- package/examples/runtime/elm-counter.tsx +0 -206
- package/examples/runtime/hello-runtime.tsx +0 -73
- package/examples/runtime/pipe-composition.tsx +0 -184
- package/examples/runtime/run-counter.tsx +0 -78
- package/examples/runtime/runtime-counter.tsx +0 -197
- package/examples/screenshots/generate.tsx +0 -563
- package/examples/scrollback-perf.tsx +0 -230
- package/examples/viewer.tsx +0 -654
- package/examples/web/build.ts +0 -365
- package/examples/web/canvas-app.tsx +0 -80
- package/examples/web/canvas.html +0 -89
- package/examples/web/dom-app.tsx +0 -81
- package/examples/web/dom.html +0 -113
- package/examples/web/showcase-app.tsx +0 -107
- package/examples/web/showcase.html +0 -34
- package/examples/web/showcases/index.tsx +0 -56
- package/examples/web/viewer-app.tsx +0 -555
- package/examples/web/viewer.html +0 -30
- package/examples/web/xterm-app.tsx +0 -105
- package/examples/web/xterm.html +0 -118
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TextArea Example
|
|
3
|
-
*
|
|
4
|
-
* A simple note editor demonstrating:
|
|
5
|
-
* - Multi-line text input with word wrapping
|
|
6
|
-
* - Cursor movement (arrow keys, Home/End, Ctrl+A/E)
|
|
7
|
-
* - Kill operations (Ctrl+K, Ctrl+U)
|
|
8
|
-
* - Scrolling within the textarea (PageUp/PageDown)
|
|
9
|
-
* - Submit with Ctrl+Enter
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import React, { useState } from "react"
|
|
13
|
-
import {
|
|
14
|
-
render,
|
|
15
|
-
Box,
|
|
16
|
-
Text,
|
|
17
|
-
H1,
|
|
18
|
-
Strong,
|
|
19
|
-
Muted,
|
|
20
|
-
TextArea,
|
|
21
|
-
useInput,
|
|
22
|
-
useApp,
|
|
23
|
-
createTerm,
|
|
24
|
-
type Key,
|
|
25
|
-
} from "../../src/index.js"
|
|
26
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
27
|
-
|
|
28
|
-
export const meta: ExampleMeta = {
|
|
29
|
-
name: "TextArea",
|
|
30
|
-
description: "Multi-line text input with word wrap, scrolling, and kill operations",
|
|
31
|
-
features: ["TextArea", "useContentRect()", "Ctrl+Enter submit"],
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function NoteEditor(): JSX.Element {
|
|
35
|
-
const { exit } = useApp()
|
|
36
|
-
const [notes, setNotes] = useState<string[]>([])
|
|
37
|
-
const [value, setValue] = useState("")
|
|
38
|
-
|
|
39
|
-
useInput((input: string, key: Key) => {
|
|
40
|
-
if (key.escape) {
|
|
41
|
-
exit()
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
function handleSubmit(text: string) {
|
|
46
|
-
if (text.trim()) {
|
|
47
|
-
setNotes((prev) => [...prev, text.trim()])
|
|
48
|
-
setValue("")
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<Box flexDirection="column" padding={1}>
|
|
54
|
-
{notes.length > 0 && (
|
|
55
|
-
<Box flexDirection="column" marginBottom={1}>
|
|
56
|
-
{notes.map((note, i) => (
|
|
57
|
-
<Box key={i} borderStyle="round" borderColor="gray" paddingX={1}>
|
|
58
|
-
<Text>
|
|
59
|
-
<Strong color="$success">#{i + 1}</Strong> {note}
|
|
60
|
-
</Text>
|
|
61
|
-
</Box>
|
|
62
|
-
))}
|
|
63
|
-
</Box>
|
|
64
|
-
)}
|
|
65
|
-
|
|
66
|
-
<Box borderStyle="single" borderColor="$primary" flexDirection="column">
|
|
67
|
-
<Box paddingX={1}>
|
|
68
|
-
<H1>New Note</H1>
|
|
69
|
-
</Box>
|
|
70
|
-
<Box paddingX={1}>
|
|
71
|
-
<TextArea
|
|
72
|
-
value={value}
|
|
73
|
-
onChange={setValue}
|
|
74
|
-
onSubmit={handleSubmit}
|
|
75
|
-
height={6}
|
|
76
|
-
placeholder="Start typing..."
|
|
77
|
-
/>
|
|
78
|
-
</Box>
|
|
79
|
-
</Box>
|
|
80
|
-
|
|
81
|
-
<Box marginTop={1}>
|
|
82
|
-
<Muted>
|
|
83
|
-
{notes.length} note{notes.length !== 1 ? "s" : ""} submitted
|
|
84
|
-
</Muted>
|
|
85
|
-
</Box>
|
|
86
|
-
</Box>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function main() {
|
|
91
|
-
using term = createTerm()
|
|
92
|
-
const { waitUntilExit } = await render(
|
|
93
|
-
<ExampleBanner meta={meta} controls="Ctrl+Enter submit Esc quit">
|
|
94
|
-
<NoteEditor />
|
|
95
|
-
</ExampleBanner>,
|
|
96
|
-
term,
|
|
97
|
-
)
|
|
98
|
-
await waitUntilExit()
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (import.meta.main) {
|
|
102
|
-
main().catch(console.error)
|
|
103
|
-
}
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Theme Explorer Example
|
|
3
|
-
*
|
|
4
|
-
* Browse all built-in palettes with live color preview.
|
|
5
|
-
* Left panel: scrollable list with mini color swatches.
|
|
6
|
-
* Right panel: live preview wrapped in ThemeProvider showing
|
|
7
|
-
* semantic tokens, ANSI 16-color table, surfaces, sample UI, and typography.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState } from "react"
|
|
11
|
-
import {
|
|
12
|
-
render,
|
|
13
|
-
Box,
|
|
14
|
-
Text,
|
|
15
|
-
Kbd,
|
|
16
|
-
Muted,
|
|
17
|
-
H1,
|
|
18
|
-
H2,
|
|
19
|
-
H3,
|
|
20
|
-
Strong,
|
|
21
|
-
Small,
|
|
22
|
-
Lead,
|
|
23
|
-
Code,
|
|
24
|
-
P,
|
|
25
|
-
ProgressBar,
|
|
26
|
-
Badge,
|
|
27
|
-
Divider,
|
|
28
|
-
ThemeProvider,
|
|
29
|
-
useInput,
|
|
30
|
-
useApp,
|
|
31
|
-
createTerm,
|
|
32
|
-
type Key,
|
|
33
|
-
} from "../../src/index.js"
|
|
34
|
-
import { builtinPalettes, deriveTheme, type ColorPalette } from "@silvery/theme"
|
|
35
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
36
|
-
|
|
37
|
-
export const meta: ExampleMeta = {
|
|
38
|
-
name: "Theme Explorer",
|
|
39
|
-
description: "Browse built-in palettes with live color preview",
|
|
40
|
-
demo: true,
|
|
41
|
-
features: ["ThemeProvider", "builtinThemes", "semantic tokens", "ANSI colors"],
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// ============================================================================
|
|
45
|
-
// Data
|
|
46
|
-
// ============================================================================
|
|
47
|
-
|
|
48
|
-
const paletteEntries = Object.entries(builtinPalettes).map(([name, palette]) => ({
|
|
49
|
-
name,
|
|
50
|
-
palette,
|
|
51
|
-
theme: deriveTheme(palette),
|
|
52
|
-
}))
|
|
53
|
-
|
|
54
|
-
// ============================================================================
|
|
55
|
-
// Components
|
|
56
|
-
// ============================================================================
|
|
57
|
-
|
|
58
|
-
/** Small color swatch: 2 colored block chars */
|
|
59
|
-
function Swatch({ color }: { color: string }): JSX.Element {
|
|
60
|
-
return <Text color={color}>{"██"}</Text>
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Mini swatch: 4 colored blocks showing palette character */
|
|
64
|
-
function MiniSwatch({ palette }: { palette: ColorPalette }): JSX.Element {
|
|
65
|
-
return (
|
|
66
|
-
<Text>
|
|
67
|
-
<Text color={palette.red}>{"█"}</Text>
|
|
68
|
-
<Text color={palette.green}>{"█"}</Text>
|
|
69
|
-
<Text color={palette.blue}>{"█"}</Text>
|
|
70
|
-
<Text color={palette.yellow}>{"█"}</Text>
|
|
71
|
-
</Text>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Left panel: theme list with color swatches */
|
|
76
|
-
function ThemeList({ selectedIndex }: { selectedIndex: number }): JSX.Element {
|
|
77
|
-
return (
|
|
78
|
-
<Box flexDirection="column" width={30} borderStyle="single" overflow="scroll" scrollTo={selectedIndex}>
|
|
79
|
-
<Box paddingX={1}>
|
|
80
|
-
<Text bold color="$primary">
|
|
81
|
-
Palettes
|
|
82
|
-
</Text>
|
|
83
|
-
<Muted> ({paletteEntries.length})</Muted>
|
|
84
|
-
</Box>
|
|
85
|
-
<Divider />
|
|
86
|
-
<Box flexDirection="column" paddingX={1}>
|
|
87
|
-
{paletteEntries.map((entry, i) => {
|
|
88
|
-
const isSelected = i === selectedIndex
|
|
89
|
-
const p = entry.palette
|
|
90
|
-
return (
|
|
91
|
-
<Box key={entry.name}>
|
|
92
|
-
<Text inverse={isSelected}>
|
|
93
|
-
{isSelected ? "▸" : " "} {entry.name.padEnd(20)}
|
|
94
|
-
</Text>
|
|
95
|
-
<Text> </Text>
|
|
96
|
-
<MiniSwatch palette={p} />
|
|
97
|
-
</Box>
|
|
98
|
-
)
|
|
99
|
-
})}
|
|
100
|
-
</Box>
|
|
101
|
-
</Box>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Semantic token showcase row */
|
|
106
|
-
function SemanticTokens(): JSX.Element {
|
|
107
|
-
const tokens: Array<{ name: string; token: string; icon: string }> = [
|
|
108
|
-
{ name: "primary", token: "$primary", icon: "●" },
|
|
109
|
-
{ name: "success", token: "$success", icon: "✓" },
|
|
110
|
-
{ name: "warning", token: "$warning", icon: "⚠" },
|
|
111
|
-
{ name: "error", token: "$error", icon: "✗" },
|
|
112
|
-
{ name: "info", token: "$info", icon: "ℹ" },
|
|
113
|
-
{ name: "accent", token: "$accent", icon: "◆" },
|
|
114
|
-
{ name: "muted", token: "$muted", icon: "○" },
|
|
115
|
-
{ name: "link", token: "$link", icon: "→" },
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
return (
|
|
119
|
-
<Box flexDirection="column">
|
|
120
|
-
<H2>Semantic Tokens</H2>
|
|
121
|
-
<Box flexDirection="row" flexWrap="wrap" gap={1} paddingX={1}>
|
|
122
|
-
{tokens.map((t) => (
|
|
123
|
-
<Text key={t.name} color={t.token}>
|
|
124
|
-
{t.icon} {t.name}
|
|
125
|
-
</Text>
|
|
126
|
-
))}
|
|
127
|
-
</Box>
|
|
128
|
-
</Box>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** ANSI 16-color table */
|
|
133
|
-
function AnsiColorTable({ palette }: { palette: ColorPalette }): JSX.Element {
|
|
134
|
-
const normal = [
|
|
135
|
-
palette.black,
|
|
136
|
-
palette.red,
|
|
137
|
-
palette.green,
|
|
138
|
-
palette.yellow,
|
|
139
|
-
palette.blue,
|
|
140
|
-
palette.magenta,
|
|
141
|
-
palette.cyan,
|
|
142
|
-
palette.white,
|
|
143
|
-
]
|
|
144
|
-
const bright = [
|
|
145
|
-
palette.brightBlack,
|
|
146
|
-
palette.brightRed,
|
|
147
|
-
palette.brightGreen,
|
|
148
|
-
palette.brightYellow,
|
|
149
|
-
palette.brightBlue,
|
|
150
|
-
palette.brightMagenta,
|
|
151
|
-
palette.brightCyan,
|
|
152
|
-
palette.brightWhite,
|
|
153
|
-
]
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<Box flexDirection="column">
|
|
157
|
-
<H2>ANSI 16 Colors</H2>
|
|
158
|
-
<Box paddingX={1} gap={1}>
|
|
159
|
-
<Box>
|
|
160
|
-
<Muted>0-7 </Muted>
|
|
161
|
-
{normal.map((c, i) => (
|
|
162
|
-
<Swatch key={i} color={c} />
|
|
163
|
-
))}
|
|
164
|
-
</Box>
|
|
165
|
-
</Box>
|
|
166
|
-
<Box paddingX={1} gap={1}>
|
|
167
|
-
<Box>
|
|
168
|
-
<Muted>8-15 </Muted>
|
|
169
|
-
{bright.map((c, i) => (
|
|
170
|
-
<Swatch key={i} color={c} />
|
|
171
|
-
))}
|
|
172
|
-
</Box>
|
|
173
|
-
</Box>
|
|
174
|
-
</Box>
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/** Sample UI elements using theme tokens */
|
|
179
|
-
function SampleUI(): JSX.Element {
|
|
180
|
-
return (
|
|
181
|
-
<Box flexDirection="column">
|
|
182
|
-
<H2>Sample UI</H2>
|
|
183
|
-
<Box flexDirection="column" paddingX={1} gap={1}>
|
|
184
|
-
<Box borderStyle="round" paddingX={1} flexDirection="column">
|
|
185
|
-
<Text bold color="$primary">
|
|
186
|
-
Dialog Title
|
|
187
|
-
</Text>
|
|
188
|
-
<Text>This is body text using default colors.</Text>
|
|
189
|
-
<Box gap={2}>
|
|
190
|
-
<Badge variant="success">Passed</Badge>
|
|
191
|
-
<Badge variant="error">Failed</Badge>
|
|
192
|
-
<Badge variant="warning">Pending</Badge>
|
|
193
|
-
</Box>
|
|
194
|
-
</Box>
|
|
195
|
-
<Box gap={1}>
|
|
196
|
-
<Muted>Progress:</Muted>
|
|
197
|
-
<Box width={20}>
|
|
198
|
-
<ProgressBar value={0.65} />
|
|
199
|
-
</Box>
|
|
200
|
-
</Box>
|
|
201
|
-
</Box>
|
|
202
|
-
</Box>
|
|
203
|
-
)
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/** Typography samples */
|
|
207
|
-
function TypographySamples(): JSX.Element {
|
|
208
|
-
return (
|
|
209
|
-
<Box flexDirection="column">
|
|
210
|
-
<H2>Typography</H2>
|
|
211
|
-
<Box flexDirection="column" paddingX={1}>
|
|
212
|
-
<H1>Heading 1</H1>
|
|
213
|
-
<H2>Heading 2</H2>
|
|
214
|
-
<H3>Heading 3</H3>
|
|
215
|
-
<Strong>Strong text</Strong>
|
|
216
|
-
<Lead>Lead text (italic)</Lead>
|
|
217
|
-
<P>Normal paragraph text</P>
|
|
218
|
-
<Muted>Muted text</Muted>
|
|
219
|
-
<Small>Small text</Small>
|
|
220
|
-
<Code>inline code</Code>
|
|
221
|
-
</Box>
|
|
222
|
-
</Box>
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** Surface pairs showcase */
|
|
227
|
-
function SurfacePairs(): JSX.Element {
|
|
228
|
-
return (
|
|
229
|
-
<Box flexDirection="column">
|
|
230
|
-
<H2>Surfaces</H2>
|
|
231
|
-
<Box flexDirection="column" paddingX={1} gap={0}>
|
|
232
|
-
<Box gap={1}>
|
|
233
|
-
<Box backgroundColor="$surfacebg" paddingX={1}>
|
|
234
|
-
<Text color="$surface">surface</Text>
|
|
235
|
-
</Box>
|
|
236
|
-
<Box backgroundColor="$inversebg" paddingX={1}>
|
|
237
|
-
<Text color="$inverse">inverse</Text>
|
|
238
|
-
</Box>
|
|
239
|
-
<Box backgroundColor="$mutedbg" paddingX={1}>
|
|
240
|
-
<Text>muted bg</Text>
|
|
241
|
-
</Box>
|
|
242
|
-
</Box>
|
|
243
|
-
<Box gap={1}>
|
|
244
|
-
<Box backgroundColor="$primary" paddingX={1}>
|
|
245
|
-
<Text color="$primaryfg">primary</Text>
|
|
246
|
-
</Box>
|
|
247
|
-
<Box backgroundColor="$error" paddingX={1}>
|
|
248
|
-
<Text color="$errorfg">error</Text>
|
|
249
|
-
</Box>
|
|
250
|
-
<Box backgroundColor="$success" paddingX={1}>
|
|
251
|
-
<Text color="$successfg">success</Text>
|
|
252
|
-
</Box>
|
|
253
|
-
</Box>
|
|
254
|
-
</Box>
|
|
255
|
-
</Box>
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/** Right panel: live preview wrapped in selected ThemeProvider */
|
|
260
|
-
function ThemePreview({ entry }: { entry: (typeof paletteEntries)[number] }): JSX.Element {
|
|
261
|
-
return (
|
|
262
|
-
<ThemeProvider theme={entry.theme}>
|
|
263
|
-
<Box flexDirection="column" flexGrow={1} borderStyle="single" overflow="scroll" backgroundColor="$bg">
|
|
264
|
-
<Box paddingX={1} gap={1}>
|
|
265
|
-
<H1>{entry.name}</H1>
|
|
266
|
-
<Muted>{entry.palette.dark === false ? "(light)" : "(dark)"}</Muted>
|
|
267
|
-
</Box>
|
|
268
|
-
<Divider />
|
|
269
|
-
<Box flexDirection="column" gap={1}>
|
|
270
|
-
<SemanticTokens />
|
|
271
|
-
<AnsiColorTable palette={entry.palette} />
|
|
272
|
-
<SurfacePairs />
|
|
273
|
-
<SampleUI />
|
|
274
|
-
<TypographySamples />
|
|
275
|
-
</Box>
|
|
276
|
-
</Box>
|
|
277
|
-
</ThemeProvider>
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function HelpBar(): JSX.Element {
|
|
282
|
-
return (
|
|
283
|
-
<Muted>
|
|
284
|
-
{" "}
|
|
285
|
-
<Kbd>j/k</Kbd> navigate <Kbd>Esc/q</Kbd> quit
|
|
286
|
-
</Muted>
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
export function ThemeExplorer(): JSX.Element {
|
|
291
|
-
const { exit } = useApp()
|
|
292
|
-
const [selectedIndex, setSelectedIndex] = useState(0)
|
|
293
|
-
|
|
294
|
-
useInput((input: string, key: Key) => {
|
|
295
|
-
if (input === "q" || key.escape) {
|
|
296
|
-
exit()
|
|
297
|
-
}
|
|
298
|
-
if (key.downArrow || input === "j") {
|
|
299
|
-
setSelectedIndex((i) => Math.min(i + 1, paletteEntries.length - 1))
|
|
300
|
-
}
|
|
301
|
-
if (key.upArrow || input === "k") {
|
|
302
|
-
setSelectedIndex((i) => Math.max(i - 1, 0))
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
const entry = paletteEntries[selectedIndex]!
|
|
307
|
-
|
|
308
|
-
return (
|
|
309
|
-
<Box flexDirection="column" height="100%" padding={1}>
|
|
310
|
-
<Box flexGrow={1} flexDirection="row" gap={1} overflow="hidden">
|
|
311
|
-
<ThemeList selectedIndex={selectedIndex} />
|
|
312
|
-
<ThemePreview entry={entry} />
|
|
313
|
-
</Box>
|
|
314
|
-
<HelpBar />
|
|
315
|
-
</Box>
|
|
316
|
-
)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// ============================================================================
|
|
320
|
-
// Main
|
|
321
|
-
// ============================================================================
|
|
322
|
-
|
|
323
|
-
async function main() {
|
|
324
|
-
using term = createTerm()
|
|
325
|
-
const { waitUntilExit } = await render(
|
|
326
|
-
<ExampleBanner meta={meta} controls="j/k navigate Esc/q quit">
|
|
327
|
-
<ThemeExplorer />
|
|
328
|
-
</ExampleBanner>,
|
|
329
|
-
term,
|
|
330
|
-
)
|
|
331
|
-
await waitUntilExit()
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (import.meta.main) {
|
|
335
|
-
main().catch(console.error)
|
|
336
|
-
}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transform Component Demo
|
|
3
|
-
*
|
|
4
|
-
* Shows the Transform component for text post-processing. Each transform
|
|
5
|
-
* applies a string transformation to every line of rendered text output.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Multiple transforms: uppercase, leetspeak, reverse, ROT13, etc.
|
|
9
|
-
* - Cycle through transforms with j/k
|
|
10
|
-
* - Shows original and transformed text side by side
|
|
11
|
-
* - Uses Transform from silvery components
|
|
12
|
-
*
|
|
13
|
-
* Run: bun vendor/silvery/examples/interactive/transform.tsx
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import React, { useState } from "react"
|
|
17
|
-
import {
|
|
18
|
-
render,
|
|
19
|
-
Box,
|
|
20
|
-
Text,
|
|
21
|
-
H1,
|
|
22
|
-
Small,
|
|
23
|
-
Kbd,
|
|
24
|
-
Muted,
|
|
25
|
-
Transform,
|
|
26
|
-
useInput,
|
|
27
|
-
useApp,
|
|
28
|
-
createTerm,
|
|
29
|
-
type Key,
|
|
30
|
-
} from "../../src/index.js"
|
|
31
|
-
import { ExampleBanner, type ExampleMeta } from "../_banner.js"
|
|
32
|
-
|
|
33
|
-
export const meta: ExampleMeta = {
|
|
34
|
-
name: "Transform",
|
|
35
|
-
description: "Text post-processing with the Transform component",
|
|
36
|
-
features: ["Transform", "transform function", "side-by-side comparison"],
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// ============================================================================
|
|
40
|
-
// Transforms
|
|
41
|
-
// ============================================================================
|
|
42
|
-
|
|
43
|
-
const leetMap: Record<string, string> = {
|
|
44
|
-
a: "4",
|
|
45
|
-
e: "3",
|
|
46
|
-
i: "1",
|
|
47
|
-
o: "0",
|
|
48
|
-
s: "5",
|
|
49
|
-
t: "7",
|
|
50
|
-
A: "4",
|
|
51
|
-
E: "3",
|
|
52
|
-
I: "1",
|
|
53
|
-
O: "0",
|
|
54
|
-
S: "5",
|
|
55
|
-
T: "7",
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const rot13Char = (c: string): string => {
|
|
59
|
-
const code = c.charCodeAt(0)
|
|
60
|
-
if (code >= 65 && code <= 90) return String.fromCharCode(((code - 65 + 13) % 26) + 65)
|
|
61
|
-
if (code >= 97 && code <= 122) return String.fromCharCode(((code - 97 + 13) % 26) + 97)
|
|
62
|
-
return c
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
interface TransformDef {
|
|
66
|
-
name: string
|
|
67
|
-
description: string
|
|
68
|
-
fn: (line: string) => string
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const transforms: TransformDef[] = [
|
|
72
|
-
{
|
|
73
|
-
name: "Uppercase",
|
|
74
|
-
description: "Convert all characters to upper case",
|
|
75
|
-
fn: (s: string) => s.toUpperCase(),
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
name: "Lowercase",
|
|
79
|
-
description: "Convert all characters to lower case",
|
|
80
|
-
fn: (s: string) => s.toLowerCase(),
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
name: "Leetspeak",
|
|
84
|
-
description: "Replace letters with numbers (a=4, e=3, i=1, ...)",
|
|
85
|
-
fn: (s: string) =>
|
|
86
|
-
s
|
|
87
|
-
.split("")
|
|
88
|
-
.map((c) => leetMap[c] ?? c)
|
|
89
|
-
.join(""),
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
name: "Reverse",
|
|
93
|
-
description: "Reverse each line of text",
|
|
94
|
-
fn: (s: string) => s.split("").reverse().join(""),
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
name: "ROT13",
|
|
98
|
-
description: "Caesar cipher — shift each letter by 13 positions",
|
|
99
|
-
fn: (s: string) => s.split("").map(rot13Char).join(""),
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "Alternating Case",
|
|
103
|
-
description: "Alternate between upper and lower case characters",
|
|
104
|
-
fn: (s: string) =>
|
|
105
|
-
s
|
|
106
|
-
.split("")
|
|
107
|
-
.map((c, i) => (i % 2 === 0 ? c.toUpperCase() : c.toLowerCase()))
|
|
108
|
-
.join(""),
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
name: "Spaces to Dots",
|
|
112
|
-
description: "Replace spaces with middle dots for visibility",
|
|
113
|
-
fn: (s: string) => s.replace(/ /g, "·"),
|
|
114
|
-
},
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
// ============================================================================
|
|
118
|
-
// Sample Text
|
|
119
|
-
// ============================================================================
|
|
120
|
-
|
|
121
|
-
const sampleLines = [
|
|
122
|
-
"The quick brown fox jumps",
|
|
123
|
-
"over the lazy dog on a",
|
|
124
|
-
"beautiful sunny afternoon.",
|
|
125
|
-
"",
|
|
126
|
-
"Pack my box with five dozen",
|
|
127
|
-
"liquor jugs and enjoy them.",
|
|
128
|
-
]
|
|
129
|
-
|
|
130
|
-
// ============================================================================
|
|
131
|
-
// Components
|
|
132
|
-
// ============================================================================
|
|
133
|
-
|
|
134
|
-
function TransformSelector({
|
|
135
|
-
current,
|
|
136
|
-
transforms: items,
|
|
137
|
-
}: {
|
|
138
|
-
current: number
|
|
139
|
-
transforms: TransformDef[]
|
|
140
|
-
}): JSX.Element {
|
|
141
|
-
return (
|
|
142
|
-
<Box flexDirection="column" overflow="scroll" scrollTo={current} height={7}>
|
|
143
|
-
{items.map((t, index) => {
|
|
144
|
-
const isSelected = index === current
|
|
145
|
-
return (
|
|
146
|
-
<Box key={t.name} paddingX={1}>
|
|
147
|
-
<Text
|
|
148
|
-
color={isSelected ? "$bg" : undefined}
|
|
149
|
-
backgroundColor={isSelected ? "$primary" : undefined}
|
|
150
|
-
bold={isSelected}
|
|
151
|
-
>
|
|
152
|
-
{isSelected ? " > " : " "}
|
|
153
|
-
{t.name}
|
|
154
|
-
</Text>
|
|
155
|
-
</Box>
|
|
156
|
-
)
|
|
157
|
-
})}
|
|
158
|
-
</Box>
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function TextPanel({
|
|
163
|
-
title,
|
|
164
|
-
titleColor,
|
|
165
|
-
children,
|
|
166
|
-
}: {
|
|
167
|
-
title: string
|
|
168
|
-
titleColor: string
|
|
169
|
-
children: React.ReactNode
|
|
170
|
-
}): JSX.Element {
|
|
171
|
-
return (
|
|
172
|
-
<Box flexDirection="column" flexGrow={1} borderStyle="round" borderColor="$border" paddingX={1}>
|
|
173
|
-
<Box marginBottom={1}>
|
|
174
|
-
<H1 color={titleColor}>{title}</H1>
|
|
175
|
-
</Box>
|
|
176
|
-
{children}
|
|
177
|
-
</Box>
|
|
178
|
-
)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function TransformDemo(): JSX.Element {
|
|
182
|
-
const { exit } = useApp()
|
|
183
|
-
const [currentIndex, setCurrentIndex] = useState(0)
|
|
184
|
-
|
|
185
|
-
const current = transforms[currentIndex]!
|
|
186
|
-
|
|
187
|
-
useInput((input: string, key: Key) => {
|
|
188
|
-
if (input === "q" || key.escape) {
|
|
189
|
-
exit()
|
|
190
|
-
return
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (key.upArrow || input === "k") {
|
|
194
|
-
setCurrentIndex((prev) => Math.max(0, prev - 1))
|
|
195
|
-
}
|
|
196
|
-
if (key.downArrow || input === "j") {
|
|
197
|
-
setCurrentIndex((prev) => Math.min(transforms.length - 1, prev + 1))
|
|
198
|
-
}
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
return (
|
|
202
|
-
<Box flexDirection="column" padding={1} gap={1}>
|
|
203
|
-
{/* Transform selector */}
|
|
204
|
-
<Box flexDirection="column" borderStyle="round" borderColor="$primary" paddingX={1}>
|
|
205
|
-
<Box marginBottom={1} gap={1}>
|
|
206
|
-
<H1>Transform</H1>
|
|
207
|
-
<Small>
|
|
208
|
-
— {current.name}: {current.description}
|
|
209
|
-
</Small>
|
|
210
|
-
</Box>
|
|
211
|
-
<TransformSelector current={currentIndex} transforms={transforms} />
|
|
212
|
-
</Box>
|
|
213
|
-
|
|
214
|
-
{/* Side-by-side comparison */}
|
|
215
|
-
<Box flexDirection="row" gap={1}>
|
|
216
|
-
<TextPanel title="Original" titleColor="$muted">
|
|
217
|
-
<Box flexDirection="column">
|
|
218
|
-
{sampleLines.map((line, i) => (
|
|
219
|
-
<Text key={i}>{line || " "}</Text>
|
|
220
|
-
))}
|
|
221
|
-
</Box>
|
|
222
|
-
</TextPanel>
|
|
223
|
-
|
|
224
|
-
<TextPanel title={`${current.name}`} titleColor="$warning">
|
|
225
|
-
<Transform transform={current.fn}>
|
|
226
|
-
<Text>{sampleLines.join("\n")}</Text>
|
|
227
|
-
</Transform>
|
|
228
|
-
</TextPanel>
|
|
229
|
-
</Box>
|
|
230
|
-
|
|
231
|
-
<Muted>
|
|
232
|
-
{" "}
|
|
233
|
-
<Kbd>j/k</Kbd> select transform <Kbd>Esc/q</Kbd> quit
|
|
234
|
-
</Muted>
|
|
235
|
-
</Box>
|
|
236
|
-
)
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// ============================================================================
|
|
240
|
-
// Main
|
|
241
|
-
// ============================================================================
|
|
242
|
-
|
|
243
|
-
async function main() {
|
|
244
|
-
using term = createTerm()
|
|
245
|
-
const { waitUntilExit } = await render(
|
|
246
|
-
<ExampleBanner meta={meta} controls="j/k select transform Esc/q quit">
|
|
247
|
-
<TransformDemo />
|
|
248
|
-
</ExampleBanner>,
|
|
249
|
-
term,
|
|
250
|
-
)
|
|
251
|
-
await waitUntilExit()
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (import.meta.main) {
|
|
255
|
-
main().catch(console.error)
|
|
256
|
-
}
|