silvery 0.11.0 → 0.11.2
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 +71 -37
- package/bin/silvery.ts +51 -0
- package/dist/index.js +65 -60
- package/dist/ink.js +61 -51
- package/dist/runtime.js +50 -45
- package/package.json +27 -6
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
# Silvery
|
|
1
|
+
# Silvery — React for modern terminal apps
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Powerful apps. Polished UIs. Proudly terminal.**
|
|
4
4
|
|
|
5
|
-
Ink-compatible React renderer
|
|
6
|
-
|
|
7
|
-
> **Work in progress.** APIs may change. Feedback welcome.
|
|
5
|
+
Ink-compatible React renderer for interactive terminal apps. Same `Box`, `Text`, `useInput` API you know. 3–6× faster in mounted rerender benchmarks.
|
|
8
6
|
|
|
9
7
|
```console
|
|
10
8
|
$ npm install silvery react
|
|
@@ -33,43 +31,75 @@ await render(<Counter />).run()
|
|
|
33
31
|
|
|
34
32
|
- **React 18 + 19** — hooks, refs, effects, suspense — all works
|
|
35
33
|
- **Flexbox layout** — `Box` with `flexDirection`, `padding`, `gap`, `flexGrow`, just like Ink
|
|
36
|
-
- **Ink/
|
|
37
|
-
|
|
38
|
-
###
|
|
39
|
-
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
- **
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
- **
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
34
|
+
- **[Ink-compatible](https://silvery.dev/guide/silvery-vs-ink#compatibility)** — most code works with just an import change. 918/931 Ink 7.0 tests pass on silvery's compat layer. Drop-in migration via [`@silvery/ink`](https://silvery.dev/guide/silvery-vs-ink). See the [full feature comparison](https://silvery.dev/guide/silvery-vs-ink)
|
|
35
|
+
|
|
36
|
+
### Shiny new stuff
|
|
37
|
+
|
|
38
|
+
- **[Best-in-class performance](https://silvery.dev/guide/silvery-vs-ink#performance-size)** — 3–6× faster than Ink in mounted rerender benchmarks, 10–20× less terminal output. Cell-level dirty tracking, only changed cells emit. Per-node skip for unchanged subtrees. Works in inline mode with native scrollback, not just fullscreen
|
|
39
|
+
- **Pure TypeScript, zero native deps** — no WASM, no build steps. [Layout via Flexily](https://silvery.dev/guide/layout-engine) (or Yoga). Works on Alpine, CI, Docker, everywhere
|
|
40
|
+
- **[Web-like responsive layout](https://silvery.dev/guide/silvery-vs-ink#responsive-layout)** — `useBoxRect()` returns actual dimensions during render. No post-render measurement, no two-pass layout cycle. Enables:
|
|
41
|
+
- [Scroll containers](https://silvery.dev/guide/scrolling) — `overflow="scroll"` with virtualization
|
|
42
|
+
- [Sticky positioning](https://silvery.dev/guide/layout-coordinates) — `position="sticky"` for headers and footers
|
|
43
|
+
- [ANSI-aware compositing](https://silvery.dev/guide/ansi-layering) — color blending with alpha across overlapping layers
|
|
44
|
+
- **[Inline, fullscreen, or both](https://silvery.dev/guide/runtime-layers)** — same components, one-line switch. All with incremental rendering:
|
|
45
|
+
- [Fullscreen](https://silvery.dev/guide/runtime-getting-started) — alt screen, traditional TUI
|
|
46
|
+
- [Inline with dynamic scrollback](https://silvery.dev/examples/scrollback) — live React zone at bottom, completed items graduate to terminal-owned scrollback. Native Cmd+F and text selection
|
|
47
|
+
- [Virtual inline](https://silvery.dev/design/dynamic-scrollback) — alt screen + app-managed scrollback history, scrollable and searchable
|
|
48
|
+
- **[Web-like interaction](https://silvery.dev/guide/event-handling)** — full keyboard and mouse events that just work. Modifier keys, mouse buttons, and drag all combine seamlessly into a single event model. Enables:
|
|
49
|
+
- [Focus scopes](https://silvery.dev/guide/silvery-vs-ink#focus-system) — spatial arrow-key nav, Tab/Escape, click-to-focus
|
|
50
|
+
- [Text selection](https://silvery.dev/guide/text-selection) — mouse drag, word/line, `userSelect` boundaries, Alt+drag override
|
|
51
|
+
- [Find](https://silvery.dev/guide/find) — `Ctrl+F` with match highlighting and `n`/`N` navigation
|
|
52
|
+
- [Copy-mode](https://silvery.dev/guide/clipboard) — `Esc, v` for vim-style keyboard selection and yanking
|
|
53
|
+
- [Drag-and-drop](https://silvery.dev/guide/event-handling) — mouse drag with hit testing
|
|
54
|
+
- **[Rich component library](https://silvery.dev/guides/components)** — 45+ components: TextInput, SelectList, ListView, Table, TreeView, Tabs, CommandPalette, ModalDialog, Toast, and more. Every component automatically participates in focus, mouse, and keybindings (readline, vim) — no wiring needed. [38 theme palettes](https://silvery.dev/guide/styling) with semantic tokens (`$primary`, `$error`) and auto-detected terminal colors
|
|
55
|
+
- **[Playwright-style testing](https://silvery.dev/guide/testing)** — 3,000+ tests. Full access to terminal internals (scrollback buffer, cursor position, cell styles, window dimensions):
|
|
56
|
+
- `createRenderer` — fast unit tests with auto-refreshing CSS locators, cell-level color assertions, frame-by-frame inspection
|
|
57
|
+
- [Termless](https://termless.dev) — like Playwright for terminals. Full ANSI fidelity with [10 swappable backends](https://termless.dev/guide/backends) (xterm.js, Ghostty, Alacritty, WezTerm, Kitty, and more)
|
|
58
|
+
- [`SILVERY_STRICT`](https://silvery.dev/guide/debugging) — multi-level verification: buffer (incremental vs fresh), ANSI (internal parser), terminal (cross-backend), and accumulated replay
|
|
59
|
+
- **[Composable architecture](https://silvery.dev/guide/providers)** — every layer is independently swappable. [DI](https://silvery.dev/guide/providers) via `pipe()` providers:
|
|
60
|
+
- [Layout](https://silvery.dev/guide/layout-engine) — Flexily or Yoga
|
|
61
|
+
- [State](https://silvery.dev/guide/runtime-layers) — BYO (useState, Zustand, Jotai, Redux)
|
|
62
|
+
- [Term](https://silvery.dev/guide/runtime-layers) — real, headless, emulator
|
|
63
|
+
- [App](https://silvery.dev/guide/runtime-layers) — from stringify to rich app (withFocus, withDomEvents, withCommands). Render to terminal, Canvas, or DOM
|
|
64
|
+
- **[All modern terminal protocols](https://silvery.dev/guide/silvery-vs-ink#terminal-protocol-coverage)** — [60 years of terminal protocols](https://terminfo.dev/about), unified into clean APIs. 100+ escape sequences you'll never have to write — auto-negotiated and gracefully degraded: [Kitty keyboard](https://terminfo.dev) + [SGR mouse](https://terminfo.dev) become rich events with modifiers; [hyperlinks](https://terminfo.dev) are just props; [clipboard](https://terminfo.dev) is a function call. Truecolor, underline styles, synchronized output, bracketed paste, focus reporting, resize detection, inline images, and [more](https://silvery.dev/guide/silvery-vs-ink#terminal-protocol-coverage)
|
|
65
|
+
|
|
66
|
+
### Why Silvery?
|
|
67
|
+
|
|
68
|
+
Silvery grew out of building a complex terminal app — a multi-pane workspace with thousands of nodes. Components needed to know their size during render. Updates needed to be fast. Scroll containers, mouse events, focus scopes, and Playwright-style testing needed to just work. What started as a renderer grew into a layout engine, then 45+ components, theming, testing infrastructure, and eventually a framework.
|
|
69
|
+
|
|
70
|
+
Along the way, three principles emerged. Take the best from the web, stay true to the terminal, and raise the bar for developer ergonomics, architecture composability, and performance.
|
|
71
|
+
|
|
72
|
+
[The Silvery Way](https://silvery.dev/guide/the-silvery-way) · [Silvery vs Ink](https://silvery.dev/guide/silvery-vs-ink) · [About](https://silvery.dev/about)
|
|
73
|
+
|
|
74
|
+
### Next steps
|
|
75
|
+
|
|
76
|
+
- [Quick start](https://silvery.dev/getting-started/quick-start) — install, first app, deploy
|
|
77
|
+
- [Interactive examples](https://silvery.dev/examples) — `npx silvery examples` to try them locally
|
|
78
|
+
- [Silvery vs Ink](https://silvery.dev/guide/silvery-vs-ink) — feature comparison and migration guide
|
|
56
79
|
|
|
57
80
|
## Packages
|
|
58
81
|
|
|
59
|
-
| Package
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
82
|
+
| Package | Description |
|
|
83
|
+
| --------------------------------- | ----------------------------------------------------------------- |
|
|
84
|
+
| `silvery` | Components, hooks, renderer — the one package you need |
|
|
85
|
+
| `@silvery/ink` / `@silvery/chalk` | Ink compatibility — 918/931 Ink 7.0 tests, 32/32 Chalk tests |
|
|
86
|
+
| `@silvery/test` | Playwright-style testing — locators, `press()`, buffer assertions |
|
|
87
|
+
| `@silvery/create` | Composable app builder — `pipe()` providers |
|
|
88
|
+
| `@silvery/theme` | 38 palettes, semantic tokens, auto-detect |
|
|
89
|
+
| `@silvery/commander` | **Beautiful CLIs for free** — help renders through Silvery itself |
|
|
90
|
+
| `@silvery/headless` | Pure state machines — portable, no React |
|
|
91
|
+
| `@silvery/ansi` | Terminal primitives — styling, SGR, detection |
|
|
64
92
|
|
|
65
93
|
## Ecosystem
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
| [
|
|
72
|
-
| [
|
|
95
|
+
Standalone projects Silvery builds on — each stands on its own:
|
|
96
|
+
|
|
97
|
+
| Project | What |
|
|
98
|
+
| -------------------------------------- | ----------------------------------------------------------------------- |
|
|
99
|
+
| [Flexily](https://beorn.codes/flexily) | Pure JS flexbox layout engine (Yoga-compatible, 2.5× faster, zero WASM) |
|
|
100
|
+
| [Termless](https://termless.dev) | Headless terminal testing — like Playwright for terminal apps |
|
|
101
|
+
| [terminfo.dev](https://terminfo.dev) | Terminal feature compatibility database (161 features, 19 terminals) |
|
|
102
|
+
| [Loggily](https://beorn.codes/loggily) | Structured logging + tracing + metrics |
|
|
73
103
|
|
|
74
104
|
## Coming
|
|
75
105
|
|
|
@@ -77,7 +107,11 @@ await render(<Counter />).run()
|
|
|
77
107
|
- **Frameworks** — Svelte, Solid.js, Vue adapters
|
|
78
108
|
- **@silvery/create** — Structured state management with commands, keybindings, effects-as-data
|
|
79
109
|
|
|
80
|
-
**Runtimes:** Bun >= 1.0 and Node.js >=
|
|
110
|
+
**Runtimes:** Bun >= 1.0 and Node.js >= 23.6. CLI (`silvery` command) requires Bun.
|
|
111
|
+
|
|
112
|
+
## Inspirations
|
|
113
|
+
|
|
114
|
+
Silvery builds on ideas from [Ink](https://github.com/vadimdemedes/ink) (React for terminals), [Ratatui](https://ratatui.rs/) (cell-level buffer model), [shadcn/ui](https://ui.shadcn.com/) (polished defaults, semantic theming), [SlateJS](https://www.slatejs.org/) (plugin composition, operations-as-data), [The Elm Architecture](https://guide.elm-lang.org/architecture/) / [BubbleTea](https://github.com/charmbracelet/bubbletea) (TEA state machines), the CSS/Web platform (flexbox, container queries, DOM events, focus scopes), [VS Code](https://code.visualstudio.com/) (command palette, keybindings), [Playwright](https://playwright.dev/) (locator-based testing), [ProseMirror](https://prosemirror.net/) (selection model), [Blessed](https://github.com/chjj/blessed) (rich terminal UIs in JS), and [Textual](https://textual.textualize.io/) (CSS-like terminal theming).
|
|
81
115
|
|
|
82
116
|
## License
|
|
83
117
|
|
package/bin/silvery.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* silvery CLI — delegates to @silvery/examples
|
|
4
|
+
* Works on both Node.js 23.6+ and Bun.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve, dirname, join } from "node:path"
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs"
|
|
8
|
+
import { spawn } from "node:child_process"
|
|
9
|
+
import { fileURLToPath } from "node:url"
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
12
|
+
const __dirname = dirname(__filename)
|
|
13
|
+
|
|
14
|
+
// Find package root by walking up looking for package.json with name "silvery"
|
|
15
|
+
function findPackageRoot(startDir: string): string {
|
|
16
|
+
let dir = startDir
|
|
17
|
+
for (let i = 0; i < 10; i++) {
|
|
18
|
+
const pkg = join(dir, "package.json")
|
|
19
|
+
if (existsSync(pkg)) {
|
|
20
|
+
try {
|
|
21
|
+
const json = JSON.parse(readFileSync(pkg, "utf8"))
|
|
22
|
+
if (json.name === "silvery") return dir
|
|
23
|
+
} catch {}
|
|
24
|
+
}
|
|
25
|
+
const parent = dirname(dir)
|
|
26
|
+
if (parent === dir) break
|
|
27
|
+
dir = parent
|
|
28
|
+
}
|
|
29
|
+
return dirname(startDir)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Detect runtime for spawning child processes
|
|
33
|
+
const runtime = typeof globalThis.Bun !== "undefined" ? "bun" : "node"
|
|
34
|
+
|
|
35
|
+
const root = findPackageRoot(__dirname)
|
|
36
|
+
const args = process.argv.slice(2)
|
|
37
|
+
const examplesCli = resolve(root, "packages/examples/bin/cli.ts")
|
|
38
|
+
|
|
39
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
40
|
+
const pkg = JSON.parse(readFileSync(resolve(root, "package.json"), "utf8"))
|
|
41
|
+
console.log(`silvery ${pkg.version}`)
|
|
42
|
+
process.exit(0)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Strip "examples" subcommand — the examples CLI handles bare args as example names
|
|
46
|
+
const delegateArgs = args[0] === "examples" ? args.slice(1) : args
|
|
47
|
+
|
|
48
|
+
const proc = spawn(runtime, ["run", examplesCli, ...delegateArgs], {
|
|
49
|
+
stdio: "inherit",
|
|
50
|
+
})
|
|
51
|
+
proc.on("exit", (code) => process.exit(code ?? 1))
|