silvery 0.3.0 → 0.4.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/README.md +41 -145
- package/package.json +64 -12
- 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
package/examples/web/build.ts
DELETED
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
* Build web examples
|
|
4
|
-
*
|
|
5
|
-
* Bundles the React apps for browser usage.
|
|
6
|
-
* Run: bun run examples/web/build.ts
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { mkdir, cp, readdir } from "node:fs/promises"
|
|
10
|
-
import { join, dirname } from "node:path"
|
|
11
|
-
|
|
12
|
-
const __dirname = dirname(new URL(import.meta.url).pathname)
|
|
13
|
-
const distDir = join(__dirname, "dist")
|
|
14
|
-
const docsDistDir = join(__dirname, "../../docs/public/examples/dist")
|
|
15
|
-
|
|
16
|
-
// Ensure dist directories exist
|
|
17
|
-
await mkdir(distDir, { recursive: true })
|
|
18
|
-
await mkdir(docsDistDir, { recursive: true })
|
|
19
|
-
|
|
20
|
-
// Browser-safe defines for Node.js globals.
|
|
21
|
-
// loggily and @silvery/ansi access process.env at module init,
|
|
22
|
-
// which throws ReferenceError in browsers where `process` is undefined.
|
|
23
|
-
const browserDefines: Record<string, string> = {
|
|
24
|
-
"process.env.NODE_ENV": '"production"',
|
|
25
|
-
"process.env.LOG_LEVEL": "undefined",
|
|
26
|
-
"process.env.TRACE": "undefined",
|
|
27
|
-
"process.env.TRACE_FORMAT": "undefined",
|
|
28
|
-
"process.env.DEBUG": "undefined",
|
|
29
|
-
"process.env.NO_COLOR": "undefined",
|
|
30
|
-
"process.env.FORCE_COLOR": "undefined",
|
|
31
|
-
"process.env.TERM": "undefined",
|
|
32
|
-
"process.env.TERM_PROGRAM": "undefined",
|
|
33
|
-
"process.env.COLORTERM": "undefined",
|
|
34
|
-
"process.env.CI": "undefined",
|
|
35
|
-
"process.env.GITHUB_ACTIONS": "undefined",
|
|
36
|
-
"process.env.KITTY_WINDOW_ID": "undefined",
|
|
37
|
-
"process.env.WT_SESSION": "undefined",
|
|
38
|
-
"process.env.LANG": "undefined",
|
|
39
|
-
"process.env.LC_ALL": "undefined",
|
|
40
|
-
"process.env.LC_CTYPE": "undefined",
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Browser process shim — ansi/logger access process.stdout, process.stdin,
|
|
44
|
-
// process.stderr, and dynamic process.env[key] at module init.
|
|
45
|
-
// The `define` map above handles static process.env.KEY references, but
|
|
46
|
-
// process.stdout/stdin/stderr and process.env[dynamic] need a real object.
|
|
47
|
-
const processShim = `
|
|
48
|
-
// Polyfill Symbol.dispose for Safari and older browsers that lack
|
|
49
|
-
// TC39 Explicit Resource Management. Bun's __using helper uses a
|
|
50
|
-
// polyfilled __dispose, but property definitions like [Symbol.dispose]
|
|
51
|
-
// need the global symbol to exist.
|
|
52
|
-
Symbol.dispose ??= Symbol.for("Symbol.dispose");
|
|
53
|
-
Symbol.asyncDispose ??= Symbol.for("Symbol.asyncDispose");
|
|
54
|
-
if (typeof globalThis.process === "undefined") {
|
|
55
|
-
globalThis.process = {
|
|
56
|
-
env: { NODE_ENV: "production" },
|
|
57
|
-
stdout: { write() {}, columns: 80, rows: 24, isTTY: false },
|
|
58
|
-
stdin: { isTTY: false, setRawMode() {}, on() {}, resume() {} },
|
|
59
|
-
stderr: { write() {} },
|
|
60
|
-
emit() {},
|
|
61
|
-
on() {},
|
|
62
|
-
platform: "browser",
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
`
|
|
66
|
-
|
|
67
|
-
// Plugin to stub Node.js built-in modules that can't be resolved in browsers.
|
|
68
|
-
// Using `external` leaves bare `import "child_process"` in the output, which
|
|
69
|
-
// browsers can't resolve (they require relative paths). This plugin replaces
|
|
70
|
-
// the import with an inline empty module instead.
|
|
71
|
-
const nodeStubPlugin: import("bun").BunPlugin = {
|
|
72
|
-
name: "node-stub",
|
|
73
|
-
setup(build) {
|
|
74
|
-
const stubs: Record<string, string> = {
|
|
75
|
-
child_process: "export function spawnSync() { return { status: 1, stdout: '', stderr: '' } }",
|
|
76
|
-
"node:process": "export default globalThis.process",
|
|
77
|
-
"node:zlib": "export function deflateSync(buf) { return buf }",
|
|
78
|
-
}
|
|
79
|
-
for (const mod of Object.keys(stubs)) {
|
|
80
|
-
build.onResolve({ filter: new RegExp(`^${mod}$`) }, (args) => ({
|
|
81
|
-
path: args.path,
|
|
82
|
-
namespace: "node-stub",
|
|
83
|
-
}))
|
|
84
|
-
build.onLoad({ filter: new RegExp(`^${mod}$`), namespace: "node-stub" }, (args) => ({
|
|
85
|
-
contents: stubs[args.path]!,
|
|
86
|
-
loader: "js",
|
|
87
|
-
}))
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Shared build options for all browser targets.
|
|
93
|
-
// External: packages not needed in browser builds.
|
|
94
|
-
// yoga-wasm-web is an optional layout engine (WASM, not needed for demos).
|
|
95
|
-
// ws is used by React DevTools connection (not needed in browser).
|
|
96
|
-
// Note: flexture IS bundled — all renderers use it for layout via browser-renderer.ts.
|
|
97
|
-
const sharedOptions = {
|
|
98
|
-
outdir: distDir,
|
|
99
|
-
target: "browser" as const,
|
|
100
|
-
format: "esm" as const,
|
|
101
|
-
minify: false,
|
|
102
|
-
sourcemap: "external" as const,
|
|
103
|
-
define: browserDefines,
|
|
104
|
-
banner: processShim,
|
|
105
|
-
external: ["yoga-wasm-web", "ws", "@termless/core"],
|
|
106
|
-
plugins: [nodeStubPlugin],
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// =============================================================================
|
|
110
|
-
// Registry Generation — scan examples and write viewer-registry.ts
|
|
111
|
-
//
|
|
112
|
-
// Showcase metadata is auto-discovered from:
|
|
113
|
-
// 1. Terminal example `meta` exports (ExampleMeta) — the single source of truth
|
|
114
|
-
// 2. SHOWCASES registry keys in web/showcases/index.ts
|
|
115
|
-
// No hardcoded metadata arrays — everything flows from the examples themselves.
|
|
116
|
-
// =============================================================================
|
|
117
|
-
|
|
118
|
-
const skipFiles = new Set([
|
|
119
|
-
"interactive/clipboard.tsx",
|
|
120
|
-
"interactive/_input-debug.tsx",
|
|
121
|
-
"interactive/_textarea-bare.tsx",
|
|
122
|
-
"runtime/hello-runtime.tsx",
|
|
123
|
-
"inline/scrollback.tsx",
|
|
124
|
-
"inline/inline-nontty.tsx",
|
|
125
|
-
])
|
|
126
|
-
|
|
127
|
-
const categories = [
|
|
128
|
-
{ dir: "layout", color: "#cba6f7", label: "Layout" },
|
|
129
|
-
{ dir: "interactive", color: "#89dceb", label: "Interactive" },
|
|
130
|
-
{ dir: "runtime", color: "#a6e3a1", label: "Runtime" },
|
|
131
|
-
{ dir: "inline", color: "#fab387", label: "Inline" },
|
|
132
|
-
] as const
|
|
133
|
-
|
|
134
|
-
interface RegistryEntry {
|
|
135
|
-
key: string
|
|
136
|
-
name: string
|
|
137
|
-
description: string
|
|
138
|
-
features: string[]
|
|
139
|
-
category: string
|
|
140
|
-
categoryColor: string
|
|
141
|
-
source: string
|
|
142
|
-
type: "showcase" | "example"
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Extract ExampleMeta from source text via regex (avoids importing modules at build time). */
|
|
146
|
-
function extractMeta(source: string): { name?: string; description?: string; features: string[] } {
|
|
147
|
-
const metaMatch = source.match(/export const meta:\s*ExampleMeta\s*=\s*\{([^}]+)\}/)
|
|
148
|
-
if (!metaMatch) return { features: [] }
|
|
149
|
-
const metaStr = metaMatch[1]!
|
|
150
|
-
const nameMatch = metaStr.match(/name:\s*"([^"]+)"/)
|
|
151
|
-
const descMatch = metaStr.match(/description:\s*"([^"]+)"/)
|
|
152
|
-
const featMatch = metaStr.match(/features:\s*\[([^\]]*)\]/)
|
|
153
|
-
return {
|
|
154
|
-
name: nameMatch?.[1],
|
|
155
|
-
description: descMatch?.[1],
|
|
156
|
-
features: featMatch?.[1]?.match(/"([^"]+)"/g)?.map((s) => s.replace(/"/g, "")) ?? [],
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/** Map from SHOWCASES registry keys to their terminal example source paths.
|
|
161
|
-
* Flagship showcases and their legacy aliases both map to the same source. */
|
|
162
|
-
const SHOWCASE_SOURCE_MAP: Record<string, string> = {
|
|
163
|
-
// Flagship keys
|
|
164
|
-
aichat: "interactive/aichat/index.tsx",
|
|
165
|
-
gallery: "interactive/gallery.tsx",
|
|
166
|
-
kanban: "interactive/kanban.tsx",
|
|
167
|
-
explorer: "interactive/explorer.tsx",
|
|
168
|
-
wizard: "interactive/cli-wizard.tsx",
|
|
169
|
-
dashboard: "layout/dashboard.tsx",
|
|
170
|
-
terminal: "interactive/terminal.tsx",
|
|
171
|
-
components: "interactive/components.tsx",
|
|
172
|
-
theme: "interactive/theme.tsx",
|
|
173
|
-
|
|
174
|
-
// Legacy / additional keys
|
|
175
|
-
"ai-chat": "interactive/aichat/index.tsx",
|
|
176
|
-
"cli-wizard": "interactive/cli-wizard.tsx",
|
|
177
|
-
"dev-tools": "interactive/dev-tools.tsx",
|
|
178
|
-
"data-explorer": "interactive/data-explorer.tsx",
|
|
179
|
-
scroll: "interactive/scroll.tsx",
|
|
180
|
-
"search-filter": "interactive/search-filter.tsx",
|
|
181
|
-
transform: "interactive/transform.tsx",
|
|
182
|
-
textarea: "interactive/textarea.tsx",
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function generateRegistry(): Promise<void> {
|
|
186
|
-
const entries: RegistryEntry[] = []
|
|
187
|
-
|
|
188
|
-
// --- Showcase entries: auto-discover from SHOWCASES keys + terminal meta ---
|
|
189
|
-
const { SHOWCASES } = await import("./showcases/index.js")
|
|
190
|
-
for (const key of Object.keys(SHOWCASES)) {
|
|
191
|
-
const sourcePath = SHOWCASE_SOURCE_MAP[key]
|
|
192
|
-
let name = key
|
|
193
|
-
let description = ""
|
|
194
|
-
let features: string[] = []
|
|
195
|
-
|
|
196
|
-
if (sourcePath) {
|
|
197
|
-
// Read meta from the terminal example source
|
|
198
|
-
const source = await Bun.file(join(__dirname, "..", sourcePath)).text()
|
|
199
|
-
const meta = extractMeta(source)
|
|
200
|
-
if (meta.name) name = meta.name
|
|
201
|
-
if (meta.description) description = meta.description
|
|
202
|
-
features = meta.features
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
entries.push({
|
|
206
|
-
key: `showcase-${key}`,
|
|
207
|
-
name,
|
|
208
|
-
description,
|
|
209
|
-
features,
|
|
210
|
-
category: "Showcases",
|
|
211
|
-
categoryColor: "#f9e2af",
|
|
212
|
-
source: "",
|
|
213
|
-
type: "showcase",
|
|
214
|
-
})
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// --- Scan example directories ---
|
|
218
|
-
for (const cat of categories) {
|
|
219
|
-
const dir = join(__dirname, "..", cat.dir)
|
|
220
|
-
const files = (await readdir(dir)).filter((f) => f.endsWith(".tsx") && !skipFiles.has(`${cat.dir}/${f}`))
|
|
221
|
-
|
|
222
|
-
for (const file of files.sort()) {
|
|
223
|
-
const source = await Bun.file(join(dir, file)).text()
|
|
224
|
-
const key = file.replace(".tsx", "")
|
|
225
|
-
const meta = extractMeta(source)
|
|
226
|
-
|
|
227
|
-
entries.push({
|
|
228
|
-
key,
|
|
229
|
-
name: meta.name ?? key,
|
|
230
|
-
description: meta.description ?? "",
|
|
231
|
-
features: meta.features,
|
|
232
|
-
category: cat.label,
|
|
233
|
-
categoryColor: cat.color,
|
|
234
|
-
source,
|
|
235
|
-
type: "example",
|
|
236
|
-
})
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Write registry file
|
|
241
|
-
const registryContent = `// AUTO-GENERATED by build.ts — do not edit manually
|
|
242
|
-
|
|
243
|
-
export interface ExampleEntry {
|
|
244
|
-
key: string
|
|
245
|
-
name: string
|
|
246
|
-
description: string
|
|
247
|
-
features: string[]
|
|
248
|
-
category: string
|
|
249
|
-
categoryColor: string
|
|
250
|
-
source: string
|
|
251
|
-
type: "showcase" | "example"
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export const REGISTRY: ExampleEntry[] = ${JSON.stringify(entries, null, 2)}
|
|
255
|
-
`
|
|
256
|
-
await Bun.write(join(__dirname, "viewer-registry.ts"), registryContent)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Generate registry before building (viewer-app imports it)
|
|
260
|
-
await generateRegistry()
|
|
261
|
-
|
|
262
|
-
// Build canvas app
|
|
263
|
-
const canvasResult = await Bun.build({
|
|
264
|
-
entrypoints: [join(__dirname, "canvas-app.tsx")],
|
|
265
|
-
...sharedOptions,
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
if (!canvasResult.success) {
|
|
269
|
-
console.error("Canvas build failed:")
|
|
270
|
-
for (const log of canvasResult.logs) {
|
|
271
|
-
console.error(log)
|
|
272
|
-
}
|
|
273
|
-
process.exit(1)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Build DOM app
|
|
277
|
-
const domResult = await Bun.build({
|
|
278
|
-
entrypoints: [join(__dirname, "dom-app.tsx")],
|
|
279
|
-
...sharedOptions,
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
if (!domResult.success) {
|
|
283
|
-
console.error("DOM build failed:")
|
|
284
|
-
for (const log of domResult.logs) {
|
|
285
|
-
console.error(log)
|
|
286
|
-
}
|
|
287
|
-
process.exit(1)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Build xterm app
|
|
291
|
-
const xtermResult = await Bun.build({
|
|
292
|
-
entrypoints: [join(__dirname, "xterm-app.tsx")],
|
|
293
|
-
...sharedOptions,
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
if (!xtermResult.success) {
|
|
297
|
-
console.error("xterm build failed:")
|
|
298
|
-
for (const log of xtermResult.logs) {
|
|
299
|
-
console.error(log)
|
|
300
|
-
}
|
|
301
|
-
process.exit(1)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Build showcase app (use-case demos for docs site)
|
|
305
|
-
const showcaseResult = await Bun.build({
|
|
306
|
-
entrypoints: [join(__dirname, "showcase-app.tsx")],
|
|
307
|
-
...sharedOptions,
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
if (!showcaseResult.success) {
|
|
311
|
-
console.error("Showcase build failed:")
|
|
312
|
-
for (const log of showcaseResult.logs) {
|
|
313
|
-
console.error(log)
|
|
314
|
-
}
|
|
315
|
-
process.exit(1)
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Build viewer app (unified example browser)
|
|
319
|
-
const viewerResult = await Bun.build({
|
|
320
|
-
entrypoints: [join(__dirname, "viewer-app.tsx")],
|
|
321
|
-
...sharedOptions,
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
if (!viewerResult.success) {
|
|
325
|
-
console.error("Viewer build failed:")
|
|
326
|
-
for (const log of viewerResult.logs) {
|
|
327
|
-
console.error(log)
|
|
328
|
-
}
|
|
329
|
-
process.exit(1)
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Copy built files to VitePress public dir for docs site
|
|
333
|
-
await cp(distDir, docsDistDir, { recursive: true })
|
|
334
|
-
|
|
335
|
-
// Copy showcase.html to docs public dir
|
|
336
|
-
await cp(join(__dirname, "showcase.html"), join(__dirname, "../../docs/public/examples/showcase.html"))
|
|
337
|
-
|
|
338
|
-
// Copy viewer.html to docs public dir (if it exists)
|
|
339
|
-
try {
|
|
340
|
-
await cp(join(__dirname, "viewer.html"), join(__dirname, "../../docs/public/examples/viewer.html"))
|
|
341
|
-
} catch {
|
|
342
|
-
// viewer.html may not exist yet — skip silently
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Copy xterm.css to docs public dir (needed by showcase.html in production)
|
|
346
|
-
await mkdir(join(__dirname, "../../docs/public/examples/xterm"), { recursive: true })
|
|
347
|
-
await cp(
|
|
348
|
-
join(__dirname, "../../node_modules/@xterm/xterm/css/xterm.css"),
|
|
349
|
-
join(__dirname, "../../docs/public/examples/xterm/xterm.css"),
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
console.log("✓ Generated examples/web/viewer-registry.ts")
|
|
353
|
-
console.log("✓ Built examples/web/dist/canvas-app.js")
|
|
354
|
-
console.log("✓ Built examples/web/dist/dom-app.js")
|
|
355
|
-
console.log("✓ Built examples/web/dist/xterm-app.js")
|
|
356
|
-
console.log("✓ Built examples/web/dist/showcase-app.js")
|
|
357
|
-
console.log("✓ Built examples/web/dist/viewer-app.js")
|
|
358
|
-
console.log("✓ Copied to docs/public/examples/dist/")
|
|
359
|
-
console.log("✓ Copied showcase.html to docs/public/examples/")
|
|
360
|
-
console.log("\nOpen in browser:")
|
|
361
|
-
console.log(" examples/web/canvas.html")
|
|
362
|
-
console.log(" examples/web/dom.html")
|
|
363
|
-
console.log(" examples/web/xterm.html")
|
|
364
|
-
console.log(" examples/web/showcase.html?demo=dashboard")
|
|
365
|
-
console.log(" examples/web/viewer.html")
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canvas Adapter Demo
|
|
3
|
-
*
|
|
4
|
-
* Demonstrates silvery rendering React components to Canvas.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useState } from "react"
|
|
8
|
-
import { renderToCanvas, Box, Text, useContentRect } from "../../packages/ui/src/canvas/index.js"
|
|
9
|
-
|
|
10
|
-
// Component that shows its dimensions
|
|
11
|
-
function SizeDisplay() {
|
|
12
|
-
const { width, height } = useContentRect()
|
|
13
|
-
return (
|
|
14
|
-
<Text color="green">
|
|
15
|
-
Size: {Math.round(width)}px × {Math.round(height)}px
|
|
16
|
-
</Text>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Demo component with various styles
|
|
21
|
-
function App() {
|
|
22
|
-
return (
|
|
23
|
-
<Box flexDirection="column" padding={1}>
|
|
24
|
-
<Box borderStyle="single" borderColor="cyan" padding={1}>
|
|
25
|
-
<Box flexDirection="column">
|
|
26
|
-
<Text bold color="cyan">
|
|
27
|
-
silvery Canvas Rendering
|
|
28
|
-
</Text>
|
|
29
|
-
<SizeDisplay />
|
|
30
|
-
</Box>
|
|
31
|
-
</Box>
|
|
32
|
-
|
|
33
|
-
<Box marginTop={1} borderStyle="round" borderColor="magenta" padding={1}>
|
|
34
|
-
<Box flexDirection="column">
|
|
35
|
-
<Text color="magenta">Text Styles</Text>
|
|
36
|
-
<Box flexDirection="row" gap={2}>
|
|
37
|
-
<Text>Normal</Text>
|
|
38
|
-
<Text bold>Bold</Text>
|
|
39
|
-
<Text italic>Italic</Text>
|
|
40
|
-
</Box>
|
|
41
|
-
<Box flexDirection="row" gap={2}>
|
|
42
|
-
<Text underline>Underline</Text>
|
|
43
|
-
<Text strikethrough>Strike</Text>
|
|
44
|
-
<Text underlineStyle="curly" underlineColor="red">
|
|
45
|
-
Curly
|
|
46
|
-
</Text>
|
|
47
|
-
</Box>
|
|
48
|
-
</Box>
|
|
49
|
-
</Box>
|
|
50
|
-
|
|
51
|
-
<Box marginTop={1} flexDirection="row" gap={1}>
|
|
52
|
-
<Box backgroundColor="red" padding={1}>
|
|
53
|
-
<Text color="white">Red</Text>
|
|
54
|
-
</Box>
|
|
55
|
-
<Box backgroundColor="green" padding={1}>
|
|
56
|
-
<Text color="black">Green</Text>
|
|
57
|
-
</Box>
|
|
58
|
-
<Box backgroundColor="blue" padding={1}>
|
|
59
|
-
<Text color="white">Blue</Text>
|
|
60
|
-
</Box>
|
|
61
|
-
</Box>
|
|
62
|
-
|
|
63
|
-
<Box marginTop={1}>
|
|
64
|
-
<Text dim>Layout by Flexx, rendered to OffscreenCanvas, drawn to visible canvas</Text>
|
|
65
|
-
</Box>
|
|
66
|
-
</Box>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Mount to canvas
|
|
71
|
-
const canvas = document.getElementById("canvas") as HTMLCanvasElement
|
|
72
|
-
if (canvas) {
|
|
73
|
-
const instance = renderToCanvas(<App />, canvas, {
|
|
74
|
-
fontSize: 14,
|
|
75
|
-
fontFamily: "monospace",
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
// Expose for debugging
|
|
79
|
-
;(window as any).silveryInstance = instance
|
|
80
|
-
}
|
package/examples/web/canvas.html
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>silvery Canvas Demo</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
box-sizing: border-box;
|
|
10
|
-
}
|
|
11
|
-
body {
|
|
12
|
-
margin: 0;
|
|
13
|
-
padding: 20px;
|
|
14
|
-
background: #1a1a2e;
|
|
15
|
-
color: #eee;
|
|
16
|
-
font-family: system-ui, sans-serif;
|
|
17
|
-
}
|
|
18
|
-
h1 {
|
|
19
|
-
color: #4ec9b0;
|
|
20
|
-
margin-bottom: 10px;
|
|
21
|
-
}
|
|
22
|
-
p {
|
|
23
|
-
color: #808080;
|
|
24
|
-
margin-top: 0;
|
|
25
|
-
}
|
|
26
|
-
#canvas {
|
|
27
|
-
border: 1px solid #333;
|
|
28
|
-
display: block;
|
|
29
|
-
}
|
|
30
|
-
.info {
|
|
31
|
-
margin-top: 20px;
|
|
32
|
-
padding: 15px;
|
|
33
|
-
background: #16213e;
|
|
34
|
-
border-radius: 4px;
|
|
35
|
-
}
|
|
36
|
-
.info h3 {
|
|
37
|
-
color: #9cdcfe;
|
|
38
|
-
margin-top: 0;
|
|
39
|
-
}
|
|
40
|
-
.info ul {
|
|
41
|
-
margin: 0;
|
|
42
|
-
padding-left: 20px;
|
|
43
|
-
}
|
|
44
|
-
.info li {
|
|
45
|
-
margin: 5px 0;
|
|
46
|
-
}
|
|
47
|
-
code {
|
|
48
|
-
background: #0f0f1a;
|
|
49
|
-
padding: 2px 6px;
|
|
50
|
-
border-radius: 3px;
|
|
51
|
-
font-family: monospace;
|
|
52
|
-
}
|
|
53
|
-
</style>
|
|
54
|
-
</head>
|
|
55
|
-
<body>
|
|
56
|
-
<h1>silvery Canvas Adapter</h1>
|
|
57
|
-
<p>React components rendered to HTML5 Canvas via silvery</p>
|
|
58
|
-
|
|
59
|
-
<canvas id="canvas" width="500" height="300"></canvas>
|
|
60
|
-
|
|
61
|
-
<div class="info">
|
|
62
|
-
<h3>How it works</h3>
|
|
63
|
-
<ul>
|
|
64
|
-
<li>React components use <code><Box></code> and <code><Text></code> from silvery</li>
|
|
65
|
-
<li><code>useContentRect()</code> returns pixel dimensions during render</li>
|
|
66
|
-
<li>Layout computed by Flexture (pure JS flexbox)</li>
|
|
67
|
-
<li>Rendered to OffscreenCanvas, then drawn to visible canvas</li>
|
|
68
|
-
</ul>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<div class="info">
|
|
72
|
-
<h3>Code</h3>
|
|
73
|
-
<pre><code>import { renderToCanvas, Box, Text, useContentRect } from '@silvery/term/canvas';
|
|
74
|
-
|
|
75
|
-
function App() {
|
|
76
|
-
const { width, height } = useContentRect();
|
|
77
|
-
return (
|
|
78
|
-
<Box borderStyle="single">
|
|
79
|
-
<Text>Size: {width}px × {height}px</Text>
|
|
80
|
-
</Box>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
renderToCanvas(<App />, canvas);</code></pre>
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
<script type="module" src="./dist/canvas-app.js"></script>
|
|
88
|
-
</body>
|
|
89
|
-
</html>
|
package/examples/web/dom-app.tsx
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DOM Adapter Demo
|
|
3
|
-
*
|
|
4
|
-
* Demonstrates silvery rendering React components to DOM elements.
|
|
5
|
-
* Advantages: text selection, accessibility, CSS integration.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import React, { useState } from "react"
|
|
9
|
-
import { renderToDOM, Box, Text, useContentRect } from "../../packages/term/src/dom/index.js"
|
|
10
|
-
|
|
11
|
-
// Component that shows its dimensions
|
|
12
|
-
function SizeDisplay() {
|
|
13
|
-
const { width, height } = useContentRect()
|
|
14
|
-
return (
|
|
15
|
-
<Text color="green">
|
|
16
|
-
Size: {Math.round(width)}px × {Math.round(height)}px
|
|
17
|
-
</Text>
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Demo component with various styles
|
|
22
|
-
function App() {
|
|
23
|
-
return (
|
|
24
|
-
<Box flexDirection="column" padding={1}>
|
|
25
|
-
<Box borderStyle="single" borderColor="cyan" padding={1}>
|
|
26
|
-
<Box flexDirection="column">
|
|
27
|
-
<Text bold color="cyan">
|
|
28
|
-
silvery DOM Rendering
|
|
29
|
-
</Text>
|
|
30
|
-
<SizeDisplay />
|
|
31
|
-
</Box>
|
|
32
|
-
</Box>
|
|
33
|
-
|
|
34
|
-
<Box marginTop={1} borderStyle="round" borderColor="magenta" padding={1}>
|
|
35
|
-
<Box flexDirection="column">
|
|
36
|
-
<Text color="magenta">Text Styles (try selecting!)</Text>
|
|
37
|
-
<Box flexDirection="row" gap={2}>
|
|
38
|
-
<Text>Normal</Text>
|
|
39
|
-
<Text bold>Bold</Text>
|
|
40
|
-
<Text italic>Italic</Text>
|
|
41
|
-
</Box>
|
|
42
|
-
<Box flexDirection="row" gap={2}>
|
|
43
|
-
<Text underline>Underline</Text>
|
|
44
|
-
<Text strikethrough>Strike</Text>
|
|
45
|
-
<Text underlineStyle="wavy" underlineColor="red">
|
|
46
|
-
Wavy
|
|
47
|
-
</Text>
|
|
48
|
-
</Box>
|
|
49
|
-
</Box>
|
|
50
|
-
</Box>
|
|
51
|
-
|
|
52
|
-
<Box marginTop={1} flexDirection="row" gap={1}>
|
|
53
|
-
<Box backgroundColor="red" padding={1}>
|
|
54
|
-
<Text color="white">Red</Text>
|
|
55
|
-
</Box>
|
|
56
|
-
<Box backgroundColor="green" padding={1}>
|
|
57
|
-
<Text color="black">Green</Text>
|
|
58
|
-
</Box>
|
|
59
|
-
<Box backgroundColor="blue" padding={1}>
|
|
60
|
-
<Text color="white">Blue</Text>
|
|
61
|
-
</Box>
|
|
62
|
-
</Box>
|
|
63
|
-
|
|
64
|
-
<Box marginTop={1}>
|
|
65
|
-
<Text dim>Text is selectable! Screen readers work. CSS hover states available.</Text>
|
|
66
|
-
</Box>
|
|
67
|
-
</Box>
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Mount to container
|
|
72
|
-
const container = document.getElementById("app") as HTMLElement
|
|
73
|
-
if (container) {
|
|
74
|
-
const instance = renderToDOM(<App />, container, {
|
|
75
|
-
fontSize: 14,
|
|
76
|
-
fontFamily: "monospace",
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// Expose for debugging
|
|
80
|
-
;(window as any).silveryInstance = instance
|
|
81
|
-
}
|