silvery 0.9.0 → 0.11.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/CHANGELOG.md ADDED
@@ -0,0 +1,271 @@
1
+ # Changelog
2
+
3
+ All notable changes to Silvery are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
+ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.11.0] - 2026-04-09
11
+
12
+ ### Added
13
+
14
+ - **Ink 7.0 compat — BackgroundContext shim** — exposes Ink 7.0's context-based background inheritance API via `packages/ink/src/bg-context.ts`. Makes +27 Ink 7.0 tests pass on the compat layer.
15
+ - **Ink 7.0 compat — maxFps render throttle** — `maxFps` option in the compat renderer throttles render rate to match Ink 7.0's behavior.
16
+ - **Ink 7.0 compat — debug cursor API shim** — Ink-compatible cursor debug API for visibility/position interaction tests.
17
+ - **Ink 7.0 compat — `wrap="hard"`** — character-level text truncation for Ink 7.0 parity (vs word-boundary wrapping).
18
+ - **Ink 7.0 compat — per-side `borderBackgroundColor`** — `borderTopBackgroundColor`, `borderRightBackgroundColor`, etc., matching Ink 7.0's per-side border background prop API.
19
+ - **Text — CJK wide-character overlay clearing** — when overwriting a continuation cell, the owning wide-char cell is cleared to a space. Fixes rendering when CJK cells are partially occluded.
20
+ - **Flexily — overflow clipping at edges** — left/right overflow clipping works cleanly with borders and margins via `minCol` parameter in text render.
21
+ - **Pre-built `dist/` via tsup** — silvery now ships both raw TypeScript source (for Bun consumers, `src/`) and pre-built `.js` + `.d.ts` (for npm consumers, `dist/`) via conditional exports. Zero build step for Bun users; instant imports for everyone else.
22
+ - **Long-lived `Ag` renderer in `createApp`** — reuse Ag instance across frames instead of creating per-render. Combined with dirty node SET optimization for O(1) layout-dirty checks.
23
+ - **Dirty node SET + propsEqual collapse** — per-node dirty tracking with independent flags, 3-pass prop comparison collapsed into 1 pass. Yields measurable perf win on kanban and memo'd workloads.
24
+ - **Atomicity framing in docs + blog** — docs now explain the three axes of atomicity (time, space, content) that the layout-first pipeline + cell-level diff + DEC mode 2026 enable. See the updated homepage, `silvery-vs-ink.md`, and the forthcoming blog post on Claude Code's rendering dilemma.
25
+ - **Interactions runtime** — selection, find, copy-mode, and drag as composable runtime features (`SelectionFeature`, `FindFeature`, `CopyModeFeature`, `DragFeature`) in `@silvery/ag-term/features/`. Each feature is wired automatically by its provider (`withDomEvents` for selection and drag, `withFocus` for find and copy-mode).
26
+ - **InputRouter** — centralized input routing in `@silvery/create/internal/` dispatches keyboard and mouse events to registered feature handlers with priority ordering.
27
+ - **CapabilityRegistry** — runtime capability discovery in `@silvery/create/internal/`. Features register themselves; React components access them via `CapabilityRegistryContext`.
28
+ - **`useSelection` hook** — reads selection state from the CapabilityRegistry. Replaces the old `useTerminalSelection` + `TerminalSelectionProvider` pattern.
29
+ - **Composition docs** — new guide pages: [Providers and Plugins](docs/guide/providers.md) and [Headless Machines](docs/guide/headless-machines.md).
30
+ - **`@silvery/commander`: typed inline arg syntax** — `command("deploy <service> [env]")` now parses positional arguments embedded in the command name string and contributes them to the typed `Args` tuple and `ArgsRecord`.
31
+ - **`@silvery/commander`: `.actionMerged()`** — explicit opt-in for the merged named-object form. Receives `(params, cmd)` where `params` contains all positional args (camelCased) merged with options.
32
+ - **`@silvery/commander`: multi-line console blocks in help sections** — `addHelpSection` row terms can now contain `\n`-separated lines.
33
+ - **`@silvery/commander`: shell prompt detection across all sections** — lines starting with `$ `, `# `, `> `, or `❯ ` get console-block styling in any `addHelpSection`.
34
+ - **Rect hook rename** — `contentRect` → `boxRect`, `renderRect` → `screenRect`, `screenRect` → `scrollRect`. Six hooks consolidated into three via overloads. Migration guide in docs.
35
+
36
+ ### Fixed
37
+
38
+ - **STRICT env bug** — `isStrictOutput()` treated the string `"0"` as truthy, so `SILVERY_STRICT=0` didn't actually disable STRICT mode. Bench runs before the fix paid full O(cells) verification overhead every iteration; post-fix numbers are 2.5-5.2× faster than Ink 7.0 on mounted workloads (all 16 scenarios).
39
+ - **Render phase typo** — `AgNode["boxRectt"]` → `AgNode["boxRect"]` in render-phase.ts.
40
+ - **Output phase — text clipping at left edge** — text in `overflow="hidden"` containers now clips correctly at the left edge via `minCol` parameter.
41
+ - **Ink compat — kitty keyboard default flags** — matches Ink's byte-wise compat for disambiguate escape codes.
42
+ - **Ink compat — stderr replay frame in debug mode** — emits replay frame for stderr writes during debug mode capture.
43
+ - **`useCallbackRect` subscription stability** — `getRect` was captured per-render, invalidating subscriptions on every re-render. Wrapped in a ref for stability, matching the `callbackRef` pattern.
44
+ - **Contentprops dead code** — removed deprecated `propsEqual` / `layoutPropsChanged` / `contentPropsChanged` with zero callers.
45
+
46
+ ### Performance
47
+
48
+ - **Output phase — combined SGR codes** — combine consecutive SGR codes into a single escape sequence where possible.
49
+ - **2.5-5.2× faster than Ink 7.0** on mounted workloads (cursor move, kanban card edit, memo'd list toggles). Wins all 16 benchmark scenarios. Run `bun run bench` to reproduce.
50
+ - **28-192× less output** than full redraw on incremental updates — cell-level buffer diff + relative cursor addressing.
51
+ - **Bundle parity with Ink+Yoga** — `silvery/runtime` is 114.9 KB gzipped vs Ink+Yoga's 116.6 KB (0.99×). `silvery/ink` compat layer is 119.2 KB (+2.2 KB over Ink baseline).
52
+
53
+ ### Documentation
54
+
55
+ - **Homepage** — "React for modern terminal apps" hero. Merged Responsive Layout and Atomic Rendering cards (same architectural root). Replaced "100x" claim with 2.5-5.2× honest numbers. Bulleted rendering mode list (inline incremental / fullscreen / static / virtual).
56
+ - **silvery-vs-ink.md** — added "The atomicity story" section covering time/space/content atomicity. Replaced cold-init perf table with canonical mounted benchmarks. Updated compat stats to 918+/931 (~98.6%) against Ink 7.0.
57
+ - **why-silvery.md, faq.md, README.md, about.md, compatibility.md** — uniform update to new framing and numbers.
58
+
59
+ ### Breaking Changes
60
+
61
+ - **Removed hooks** — `useTerminalSelection`, `usePointerState`, `useFind`, `useFindProvider`, `useCopyMode`, `useCopyProvider` are superseded by the feature-based architecture. The old hooks still exist for backwards compatibility but are no longer the recommended API.
62
+ - **Text selection** now activates automatically via `withDomEvents()` — no explicit hook setup required.
63
+ - **Find** now activates automatically via `withFocus()` with `Ctrl+F` — no explicit `useFind` setup required.
64
+ - **Copy-mode** now activates automatically via `withFocus()` with `Esc, v` — no explicit `useCopyMode` setup required.
65
+ - **Rect hook rename** (see Added): if you use `contentRect` / `renderRect` / `screenRect` names, they've been renamed. See the Layout Coordinates guide for the migration.
66
+
67
+ ## [0.9.0] - 2026-03-29
68
+
69
+ ### Added
70
+
71
+ - **Interactive canvas rendering** — `renderToCanvas()` now supports full keyboard input via hidden textarea, RuntimeContext, FocusManager, ThemeProvider, and CursorProvider. Showcase demos switched from xterm.js to canvas.
72
+ - **Variable-height virtualizer** — `VirtualList` supports dynamic item heights via measurement, not just fixed `itemHeight`.
73
+ - **Canvas input handler** — new `createCanvasInput()` for DOM KeyboardEvent → terminal escape sequence conversion.
74
+
75
+ ### Fixed
76
+
77
+ - **Kitty keyboard: shifted punctuation** — `Shift+1` now correctly produces `!` (not `1`) via `shifted_codepoint`. Default Kitty flags upgraded to `DISAMBIGUATE | REPORT_EVENTS | REPORT_ALL_KEYS` (11). Warns when shifted info is missing.
78
+ - **`matchHotkey` layout-independent** — single-character hotkeys (`"!"`, `"J"`, `"@"`) skip shift check. Works across all keyboard layouts.
79
+ - **Mouse mode 1003** — restored any-event tracking for hover support.
80
+ - **Commander: `NO_COLOR`** — `colorizeHelp` now respects `NO_COLOR` environment variable.
81
+
82
+ ### Documentation
83
+
84
+ - Example pages: added `npx silvery examples` run commands with code-group tabs (npm/bun/pnpm/vp).
85
+ - Removed placeholder blog and live-demo pages.
86
+ - Showcase inventory updated for canvas renderer.
87
+
88
+ ## [0.4.0] - 2026-03-23
89
+
90
+ ### Breaking Changes
91
+
92
+ - **Era2 package renames** — `@silvery/react` → `@silvery/ag-react`, `@silvery/term` → `@silvery/ag-term`, `@silvery/ui` merged into `@silvery/ag-react/ui`, `@silvery/compat` → `@silvery/ink`
93
+ - **TeaNode → AgNode** — core node type renamed across the entire codebase
94
+ - **createApp moved to @silvery/create** — canonical import is now `@silvery/create/create-app` (ag-term re-exports for backwards compat)
95
+
96
+ ### Added
97
+
98
+ - **`@silvery/ag` package** — core types (AgNode, BoxProps, keys, focus) extracted from tea
99
+ - **`render()` beginner API** — zero-ceremony entry point: `await render(<App />).run()`
100
+ - **Component-tier examples** — 7 simple examples using `run()` + React hooks (no TEA required)
101
+ - **Subpath exports** — `silvery/runtime`, `silvery/theme`, `silvery/ui` for targeted imports
102
+ - **Build script** — `bun run build` produces pre-built JS bundles via Bun.build()
103
+
104
+ ### Changed
105
+
106
+ - **3 public packages** — `silvery`, `@silvery/create`, `@silvery/test`. Internal packages (`@silvery/ag`, `@silvery/ag-react`, `@silvery/ag-term`, `@silvery/theme`, `@silvery/ink`) are published but not user-facing.
107
+ - **Examples reorganized** — `examples/components/` (no TEA) and `examples/apps/` (with TEA)
108
+ - **Docs rewritten** — rendering-first positioning, TEA is optional, Ink-compatible messaging
109
+
110
+ ### Fixed
111
+
112
+ - Border text overflow: scroll indicator text now truncates when box is narrower than indicator text
113
+ - Package export resolution in vitest (dist/ vs src/ conditions)
114
+
115
+ ## [0.3.0] - 2026-03-20
116
+
117
+ README rewrite, website positioning, docs site restructuring. No code changes.
118
+
119
+ ## [0.2.0] - 2026-03-09
120
+
121
+ The Silvery release. Complete rename from hightea to silvery, monorepo restructured as `@silvery/*` packages, and ecosystem-wide migration (loggily, flexily, @silvery/theme).
122
+
123
+ ### Added
124
+
125
+ - `silvery/ink` and `silvery/chalk` compatibility subpaths for zero-effort migration from Ink/Chalk.
126
+ - `@silvery/ink` package for ink/chalk API compatibility layer.
127
+ - `@silvery/theme` package (absorbed from standalone swatch project) with 45 built-in color palettes and 33 semantic design tokens.
128
+ - `@silvery/test` package with unified `createRenderer()` API, auto-refreshing locators, and Playwright-style assertions.
129
+ - `@silvery/create` package for optional TEA (The Elm Architecture) state machines with `zustand-tea` middleware.
130
+ - `@silvery/ag-react/ui` package with 23+ components: ModalDialog, Toast, SplitView, CommandPalette, SelectList, Table, ScrollbackView, ErrorBoundary, Tabs, and more.
131
+ - VitePress documentation site at silvery.dev with migration guide, API reference, and live xterm.js demos.
132
+ - Examples directory with interactive demos (dashboard, live-resize, outline, scrollback, Kitty protocol).
133
+
134
+ ### Changed
135
+
136
+ - **Renamed**: hightea -> silvery across all packages, imports, docs, and URLs.
137
+ - **Renamed**: decant -> loggily (logging library).
138
+ - **Renamed**: flexture -> flexily (layout engine).
139
+ - **Renamed**: swatch -> @silvery/theme (absorbed into monorepo).
140
+ - Package structure: monolith split into `@silvery/ag-react`, `@silvery/ag-term`, `@silvery/create`, `@silvery/ansi`, `@silvery/ag-react/ui`, `@silvery/theme`, `@silvery/test`, `@silvery/ink`.
141
+ - GitHub repositories renamed: beorn/hightea -> beorn/silvery, beorn/decant -> beorn/loggily, beorn/flexture -> beorn/flexily.
142
+
143
+ ## [0.1.0] - 2026-03-05
144
+
145
+ The hightea release. Renamed from inkx to hightea, with new domain (hightea.dev) and monorepo consolidation.
146
+
147
+ ### Added
148
+
149
+ - Custom domain: hightea.dev.
150
+ - `@hightea/core` package for shared types.
151
+ - `@hightea/ansi` merged into monorepo (previously standalone `@beorn/chalkx`).
152
+ - `@hightea/ui` merged into monorepo (previously standalone `@beorn/inkx-ui`).
153
+
154
+ ### Changed
155
+
156
+ - **Renamed**: inkx -> hightea, chalkx -> @hightea/ansi across all source, docs, and URLs.
157
+ - Package structure: `@beorn/inkx` -> `@hightea/term`, `@beorn/chalkx` -> `@hightea/ansi`, `@beorn/inkx-ui` -> `@hightea/ui`.
158
+
159
+ ## [0.0.x] - 2026-01-19 to 2026-03-04
160
+
161
+ The inkx era. Initial development as a high-performance Ink alternative with incremental rendering.
162
+
163
+ ### Added — Rendering Engine
164
+
165
+ - **Incremental content phase**: per-node dirty tracking with 7 independent flags (`contentDirty`, `stylePropsDirty`, `bgDirty`, `subtreeDirty`, `childrenDirty`, `layoutDirty`, `layoutChangedThisFrame`). Only changed nodes re-render, producing 28-192x fewer bytes on typical incremental updates.
166
+ - **Three-tier scroll optimization**: buffer shift (Tier 1, scroll-only changes), full viewport clear (Tier 2, structural changes), and subtree-dirty-only (Tier 3, targeted re-render).
167
+ - **Sticky children**: `position="sticky"` with two-pass rendering (normal flow first, then sticky headers on top). Works inside and outside scroll containers.
168
+ - **Inline incremental rendering**: `createOutputPhase()` with instance-scoped cursor tracking. Relative cursor positioning for inline mode achieves parity with fullscreen incremental rendering.
169
+ - **Text background inheritance**: explicit `inheritedBg` parameter through the render tree, decoupling text rendering from buffer state. Eliminates `getCellBg` mismatches on incremental renders.
170
+ - **BgSegment tracking**: strips ANSI background from text content and tracks bg ranges per-segment, preventing background bleed across wrapped lines.
171
+ - **SILVERY_STRICT mode**: verifies incremental render produces identical output to fresh render, cell-by-cell, including vt100 ANSI output verification.
172
+ - **CJK/wide character support**: correct cursor advancement, continuation cells, and boundary handling for double-width characters.
173
+ - **True color row pre-check**: `rowExtrasEquals()` for Map-based data (true colors, underline colors, hyperlinks) prevents stale color artifacts.
174
+
175
+ ### Added — Layout
176
+
177
+ - **Pluggable layout engine**: Yoga and Flexily adapters with zero-allocation option.
178
+ - `useBoxRect()` / `useScrollRect()` hooks for synchronous layout feedback (no useEffect, no layout thrashing).
179
+ - `overflow="scroll"` containers with `scrollTo` (edge-based) and `scrollOffset` (explicit) control.
180
+ - `position="sticky"` with `stickyTop` / `stickyBottom` offsets.
181
+ - `position="absolute"` with three-pass paint-order rendering (normal flow, sticky, absolute).
182
+ - `outlineStyle` prop: border characters that overlap content without affecting layout (CSS `outline` equivalent).
183
+ - `overflowIndicator` prop: scroll indicators on bordered or borderless containers.
184
+ - `display="none"` support.
185
+
186
+ ### Added — Input and Terminal Protocols
187
+
188
+ - **Kitty keyboard protocol**: full support with key press/repeat/release events, super/hyper/capsLock/numLock modifiers, and auto-detection (`detectKittySupport`).
189
+ - **SGR mouse events**: DOM-level mouse event system with hit testing, `pointerEvents` prop, and cross-chunk buffering.
190
+ - **Bracketed paste**: automatic paste detection and handling.
191
+ - **Clipboard**: read/write via OSC 52.
192
+ - **Images**: sixel and Kitty image protocol support.
193
+ - **Hyperlinks**: native OSC 8 hyperlink rendering in the pipeline.
194
+ - **Synchronized updates**: mode 2026 for flicker-free rendering.
195
+ - **Cursor styles**: DECSCUSR cursor shape control.
196
+ - **Terminal queries**: cursor position, colors, device attributes, focus, DECRQM, pixel size.
197
+ - **Terminal capability detection**: `termtest` diagnostic suite for terminal feature verification.
198
+
199
+ ### Added — Components and Hooks
200
+
201
+ - Core: `Box`, `Text`, `Transform`.
202
+ - Layout: `VirtualList`, `HorizontalVirtualList`, `ScrollbackView`, `ScrollbackList`, `SplitView`, `Fill`.
203
+ - Input: `TextInput`, `TextArea` (with selection and DECSCUSR cursor styles), `EditContext` (unified text editing with invertible ops), `InputBoundary`.
204
+ - UI: `SelectList`, `Table`, `Spinner`, `ProgressBar`, `Image`, `Link`, `Tabs`, `Toast`, `ModalDialog`, `CommandPalette`, `ErrorBoundary` (with resetKeys).
205
+ - Focus: DOM-native focus system with spatial navigation, peer scope system (`activateScope`), `useFocusable`, `useFocusWithin`.
206
+ - Hooks: `useInput`, `useApp`, `useBoxRect`, `useScrollRect`, `useCursor`, `useScrollback`, `useTerm`, `useConsole`.
207
+ - Runtime: `render()`, `run()`, `createApp()`, `createStore()`, `createTerm()`.
208
+ - Testing: `createRenderer()` with auto-refreshing locators, `withDiagnostics()`, `debugTree()`.
209
+
210
+ ### Added — Theming
211
+
212
+ - `@silvery/theme` with `ColorPalette` and `Theme` types (33 semantic tokens).
213
+ - 45 built-in color palettes (Snazzy, Monokai, and more).
214
+ - `$token` color resolution in all style props via active theme context.
215
+ - `Box` `theme` prop for per-subtree theme override.
216
+ - `detectTheme()` / `deriveTheme()` for automatic terminal theme detection.
217
+
218
+ ### Added — State Management
219
+
220
+ - TEA (The Elm Architecture) optional integration via `@silvery/create`.
221
+ - `zustand-tea` middleware: TEA effects for Zustand stores.
222
+ - `createSlice()` helper for ops-as-data patterns.
223
+ - Four-level state management progression documented: component -> store -> ops-as-data -> pure machine.
224
+
225
+ ### Added — Developer Experience
226
+
227
+ - `SILVERY_INSTRUMENT=1`: exposes skip/render counts, cascade depth, scroll tier decisions.
228
+ - `DEBUG=silvery:* DEBUG_LOG=/tmp/silvery.log`: pipeline debug output.
229
+ - `renderString()` for static one-shot rendering.
230
+ - `app.resize()` for virtual terminal resize in tests.
231
+ - Bundle size measurement script for all entry points.
232
+ - Headless screenshot support.
233
+ - Slow frame warnings and render profiling (`SILVERY_PROFILE_RENDER`).
234
+
235
+ ### Added — Browser Support
236
+
237
+ - xterm.js render target with VitePress live demo component.
238
+ - `Symbol.dispose` polyfill for Safari compatibility.
239
+ - `child_process` stub for browser builds.
240
+
241
+ ### Fixed
242
+
243
+ - Scrollback compaction loop and resize corruption in `run()` runtime.
244
+ - `ScrollbackView` footer auto-sizes to content (deprecate `footerHeight`).
245
+ - `useBoxRect` returning 0x0 in xterm renderer.
246
+ - SGR mouse cross-chunk buffering and browser renderer coordinates.
247
+ - Text background bleed across wrapped lines (BgSegment fix).
248
+ - Output phase true-color row pre-check skipping Map diffs.
249
+ - CJK wide character cursor drift in `bufferToAnsi`.
250
+ - Scrollback promotion jump-up in leftover erasure.
251
+ - Window resize: clear scrollback + screen instead of selective re-emit.
252
+ - Inline mode resize: smart clear and persistent cursor tracking.
253
+ - Raw mode auto-enable in `render()` API for interactive apps.
254
+ - `useInput` graceful no-op without InputContext (renderStatic compat).
255
+ - Focus system: `useFocusWithin` edge cases and peer scope memory.
256
+
257
+ ### Performance
258
+
259
+ - Incremental rendering: 28-192x fewer bytes per keystroke vs full re-render.
260
+ - Buffer shift optimization for scroll-only changes (Tier 1).
261
+ - Measure function caching: eliminates O(n) measure overhead.
262
+ - `contentDirty` scoping: border-only paint changes no longer cascade through child subtrees.
263
+ - `layoutChangedThisFrame` flag: eliminates permanent O(N) content-phase recalculation from stale `prevLayout`.
264
+ - Zero-allocation Flexily layout engine option.
265
+
266
+ [Unreleased]: https://github.com/beorn/silvery/compare/v0.9.0...HEAD
267
+ [0.9.0]: https://github.com/beorn/silvery/compare/v0.4.0...v0.9.0
268
+ [0.4.0]: https://github.com/beorn/silvery/compare/v0.2.0...v0.4.0
269
+ [0.2.0]: https://github.com/beorn/silvery/compare/v0.1.0...v0.2.0
270
+ [0.1.0]: https://github.com/beorn/silvery/compare/v0.0.1...v0.1.0
271
+ [0.0.x]: https://github.com/beorn/silvery/releases/tag/v0.0.1
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Silvery
2
2
 
3
- **Polished Terminal UIs in React.**
3
+ **React for modern terminal apps.**
4
4
 
5
- Ink-compatible React renderer for terminals same `Box`, `Text`, `useInput` API you know. Plus everything you wish Ink had.
5
+ Ink-compatible React renderer with an atomic rendering pipeline. Same `Box`, `Text`, `useInput` API you know. 99% Ink 7.0 compatible, 2.5–5.2× faster on mounted workloads, bundle-parity with Ink+Yoga. Plus everything you wish Ink had.
6
6
 
7
7
  > **Work in progress.** APIs may change. Feedback welcome.
8
8
 
@@ -37,15 +37,20 @@ await render(<Counter />).run()
37
37
 
38
38
  ### Better
39
39
 
40
- - **Smaller install** — <500 KB gzipped (Ink 6 pulls 16MB into node_modules)
41
- - **Pure TypeScript, zero native deps** — no WASM, no build steps works on Alpine, CI, Docker, everywhere
42
- - **Incremental rendering** — per-node dirty tracking, [~100x faster interactive updates](tests/perf/render.bench.ts)
43
- - **Responsive layout** — `useContentRect()` returns actual dimensions synchronously during render
44
- - **Dynamic scrollback** — renders (and re-renders!) into the terminal's scroll history, not just alternate screen
40
+ - **Atomic rendering pipeline** — layout runs before render (no two-pass flash), cell-level buffer diff (no line redraws), synchronized frame output via DEC mode 2026 (no tearing). A class of rendering bugs architecturally impossible: no flicker, no component dropout on scroll, no half-updated frames
41
+ - **2.5–5.2× faster than Ink 7.0 on mounted workloads** — wins all 16 benchmark scenarios. Only changed cells emit to the terminal; incremental output is 28–192× smaller than full redraw. Run `bun run bench` in the repo to reproduce
42
+ - **Bundle parity with Ink+Yoga** — 114.9 KB gzipped runtime vs Ink+Yoga's 116.6 KB (0.99× tied). Pure TypeScript, no WASM, zero native deps — works on Alpine, CI, Docker, everywhere
43
+ - **Responsive layout** — `useBoxRect()` returns actual dimensions synchronously during render
44
+ - **Dynamic scrollback** — live React zone at the bottom, completed items graduate to terminal-owned scrollback. Cmd+F works natively. The thing Claude Code spent six months trying to retrofit into Ink
45
45
  - **Scrollable containers** — `overflow="scroll"` with automatic measurement and clipping
46
+ - **99% Ink 7.0 compatible** — 918+/931 Ink tests pass on silvery's compat layer. Drop-in migration via `@silvery/ink`
46
47
  - **Theme system** — 38 palettes, semantic design/color tokens (`$primary`, `$error`), auto-detects terminal colors
47
- - **30+ components** — TextInput, TextArea, SelectList, VirtualList, Table, Tabs, CommandPalette, ModalDialog, Toast, and more
48
+ - **30+ components** — TextInput, TextArea, SelectList, ListView, Table, TreeView, Console, Tabs, CommandPalette, ModalDialog, Toast, and more
48
49
  - **Focus system** — scoped focus, arrow-key directional nav, click-to-focus
50
+ - **Text selection** — mouse drag, word/line selection, `userSelect` boundaries, Alt+drag override. Works out of the box with `withDomEvents()`
51
+ - **Find** — `Ctrl+F` buffer search with match highlighting and `n`/`N` navigation. Works out of the box with `withFocus()`
52
+ - **Copy-mode** — `Esc, v` for vim-style keyboard-driven text selection and yanking
53
+ - **Drag-and-drop** — mouse drag with hit testing, automatic via `withDomEvents()`
49
54
  - **Extremely composable** — use as just a renderer (`render`), add a runtime (`run`), or build full apps with any React state library (useState, Zustand, Jotai, Redux). Swap terminal backends (real TTY, headless, xterm.js emulator) for testing. Embed silvery components in existing CLIs. Use the layout engine standalone. Render to terminal, or (experimental) Canvas, or DOM
50
55
  - **Most complete terminal protocol support** — 100+ escape sequences, all auto-negotiated: 12 OSC (hyperlinks, clipboard, palette, text sizing, semantic prompts, notifications), 35+ CSI (cursor, mouse modes, paste, focus, sync output, device queries), 50+ SGR (6 underline styles, underline colors, truecolor, 256-color), full Kitty keyboard (5 flags), full SGR mouse (any-event, drag, wheel)
51
56
 
package/dist/chalk.js ADDED
@@ -0,0 +1,4 @@
1
+ var CI_ENVS=["CI","GITHUB_ACTIONS","GITLAB_CI","JENKINS_URL","BUILDKITE","CIRCLECI","TRAVIS"];function detectColor(stdout){if(process.env.NO_COLOR!==void 0)return null;let forceColor=process.env.FORCE_COLOR;if(forceColor!==void 0){if(forceColor==="0"||forceColor==="false")return null;if(forceColor==="1")return"basic";if(forceColor==="2")return"256";if(forceColor==="3")return"truecolor";return"basic"}if(!stdout.isTTY)return null;if(process.env.TERM==="dumb")return null;let colorTerm=process.env.COLORTERM;if(colorTerm==="truecolor"||colorTerm==="24bit")return"truecolor";let term=process.env.TERM??"";if(term.includes("truecolor")||term.includes("24bit")||term.includes("xterm-ghostty")||term.includes("xterm-kitty")||term.includes("wezterm"))return"truecolor";if(term.includes("256color")||term.includes("256"))return"256";let termProgram=process.env.TERM_PROGRAM;if(termProgram==="iTerm.app"||termProgram==="Apple_Terminal")return termProgram==="iTerm.app"?"truecolor":"256";if(termProgram==="Ghostty"||termProgram==="WezTerm")return"truecolor";if(process.env.KITTY_WINDOW_ID)return"truecolor";if(term.includes("xterm")||term.includes("color")||term.includes("ansi"))return"basic";if(CI_ENVS.some((env)=>process.env[env]!==void 0))return"basic";if(process.env.WT_SESSION)return"truecolor";return"basic"}import stringWidth from"string-width";var MODIFIERS={reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29],overline:[53,55]},FG_COLORS={black:30,red:31,green:32,yellow:33,blue:34,magenta:35,cyan:36,white:37,blackBright:90,gray:90,grey:90,redBright:91,greenBright:92,yellowBright:93,blueBright:94,magentaBright:95,cyanBright:96,whiteBright:97},BG_COLORS={bgBlack:40,bgRed:41,bgGreen:42,bgYellow:43,bgBlue:44,bgMagenta:45,bgCyan:46,bgWhite:47,bgBlackBright:100,bgGray:100,bgGrey:100,bgRedBright:101,bgGreenBright:102,bgYellowBright:103,bgBlueBright:104,bgMagentaBright:105,bgCyanBright:106,bgWhiteBright:107},ANSI_16_COLORS=[[0,0,0],[128,0,0],[0,128,0],[128,128,0],[0,0,128],[128,0,128],[0,128,128],[192,192,192],[128,128,128],[255,0,0],[0,255,0],[255,255,0],[0,0,255],[255,0,255],[0,255,255],[255,255,255]];function nearestAnsi16(r,g,b){let bestIdx=0,bestDist=1/0;for(let i=0;i<16;i++){let[cr,cg,cb]=ANSI_16_COLORS[i],dist=(r-cr)**2+(g-cg)**2+(b-cb)**2;if(dist<bestDist)bestDist=dist,bestIdx=i}return bestIdx}function rgbToAnsi256(r,g,b){if(r===g&&g===b){if(r<8)return 16;if(r>248)return 231;return Math.round((r-8)/247*24)+232}let ri=Math.round(r/255*5),gi=Math.round(g/255*5),bi=Math.round(b/255*5);return 16+36*ri+6*gi+bi}function fgFromRgb(r,g,b,level){if(level==="truecolor")return`38;2;${r};${g};${b}`;if(level==="256")return`38;5;${rgbToAnsi256(r,g,b)}`;let idx=nearestAnsi16(r,g,b);return idx<8?`${30+idx}`:`${82+idx}`}function bgFromRgb(r,g,b,level){if(level==="truecolor")return`48;2;${r};${g};${b}`;if(level==="256")return`48;5;${rgbToAnsi256(r,g,b)}`;let idx=nearestAnsi16(r,g,b);return idx<8?`${40+idx}`:`${92+idx}`}function hexToRgb(hex){if(hex[0]!=="#")return null;let h=hex.slice(1);if(h.length===3)return[parseInt(h[0]+h[0],16),parseInt(h[1]+h[1],16),parseInt(h[2]+h[2],16)];if(h.length===6)return[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16)];return null}var THEME_TOKEN_DEFAULTS={primary:33,secondary:36,accent:35,error:31,warning:33,success:32,info:36,muted:2,link:34,border:90,surface:37};function resolveToken(name,theme){if(!theme)return;let token=name.startsWith("$")?name.slice(1):name;if(token.startsWith("color")){let idx=parseInt(token.slice(5),10);if(idx>=0&&idx<16&&theme.palette&&idx<theme.palette.length)return theme.palette[idx]}let key=token.replace(/-/g,""),val=theme[key];return typeof val==="string"?val:void 0}var ESC="\x1B[",KNOWN_METHODS=new Set(["hex","rgb","bgHex","bgRgb","ansi256","bgAnsi256","resolve"]),THEME_TOKENS=new Set(["primary","secondary","accent","error","warning","success","info","muted","link","border","surface"]);function fromChalkLevel(n){if(n<=0)return null;if(n===1)return"basic";if(n===2)return"256";return"truecolor"}function toChalkLevel(cl){if(cl===null)return 0;if(cl==="basic")return 1;if(cl==="256")return 2;return 3}function createStyle(options){let ref={level:null,theme:options?.theme};if(options?.level!==void 0)ref.level=options.level;else try{ref.level=detectColor(process.stdout)}catch{ref.level=null}return createChainWithRef({opens:[],closes:[]},ref)}var style=createStyle();function createChainWithRef(state,ref){let proxyRef={proxy:null},proxy=new Proxy(function(){},{apply(_target,_thisArg,args){let level=ref.level;if(state.visible&&level===null)return"";let text;if(args.length===0)text="";else if(Array.isArray(args[0])&&"raw"in args[0])text=String.raw(args[0],...args.slice(1));else if(args.length>1)text=args.map((a)=>String(a??"")).join(" ");else text=String(args[0]??"");if(text==="")return"";if(level===null||state.opens.length===0)return text;let open=`${ESC}${state.opens.join(";")}m`,close=`${ESC}${state.closes.join(";")}m`;for(let closeCode of state.closes){let closeSeq=`${ESC}${closeCode}m`,parts=text.split(closeSeq);if(parts.length>1)text=parts.join(`${closeSeq}${open}`)}if(text.includes(`
2
+ `))text=text.replace(/\r?\n/g,`${close}$&${open}`);return`${open}${text}${close}`},get(_target,prop){if(typeof prop==="symbol")return;if(prop==="level")return toChalkLevel(ref.level);if(prop==="resolve")return(token)=>resolveToken(token,ref.theme);if(prop==="visible")return createChainWithRef({...state,visible:!0},ref);if(prop==="call"||prop==="apply"||prop==="bind")return Function.prototype[prop].bind(proxyRef.proxy);let level=ref.level;if(prop==="hex"||prop==="bgHex")return(color)=>{if(level===null)return createChainWithRef(state,ref);let rgb=hexToRgb(color);if(!rgb)return createChainWithRef(state,ref);let code=prop==="hex"?fgFromRgb(rgb[0],rgb[1],rgb[2],level):bgFromRgb(rgb[0],rgb[1],rgb[2],level),close=prop==="hex"?"39":"49";return createChainWithRef({opens:[...state.opens,code],closes:[...state.closes,close]},ref)};if(prop==="rgb"||prop==="bgRgb")return(r,g,b)=>{if(level===null)return createChainWithRef(state,ref);let code=prop==="rgb"?fgFromRgb(r,g,b,level):bgFromRgb(r,g,b,level),close=prop==="rgb"?"39":"49";return createChainWithRef({opens:[...state.opens,code],closes:[...state.closes,close]},ref)};if(prop==="ansi256")return(code)=>{if(level===null)return createChainWithRef(state,ref);return createChainWithRef({opens:[...state.opens,`38;5;${code}`],closes:[...state.closes,"39"]},ref)};if(prop==="bgAnsi256")return(code)=>{if(level===null)return createChainWithRef(state,ref);return createChainWithRef({opens:[...state.opens,`48;5;${code}`],closes:[...state.closes,"49"]},ref)};if(prop in MODIFIERS){if(level===null)return createChainWithRef(state,ref);let[open,close]=MODIFIERS[prop];return createChainWithRef({opens:[...state.opens,String(open)],closes:[...state.closes,String(close)]},ref)}if(prop in FG_COLORS){if(level===null)return createChainWithRef(state,ref);return createChainWithRef({opens:[...state.opens,String(FG_COLORS[prop])],closes:[...state.closes,"39"]},ref)}if(prop in BG_COLORS){if(level===null)return createChainWithRef(state,ref);return createChainWithRef({opens:[...state.opens,String(BG_COLORS[prop])],closes:[...state.closes,"49"]},ref)}if(THEME_TOKENS.has(prop)){if(level===null)return createChainWithRef(state,ref);let hex=resolveToken(prop,ref.theme);if(hex){let rgb=hexToRgb(hex);if(rgb){let code=fgFromRgb(rgb[0],rgb[1],rgb[2],level);if(prop==="link")return createChainWithRef({opens:[...state.opens,code,"4"],closes:[...state.closes,"39","24"]},ref);return createChainWithRef({opens:[...state.opens,code],closes:[...state.closes,"39"]},ref)}}let fallback=THEME_TOKEN_DEFAULTS[prop];if(fallback!==void 0){if(prop==="muted")return createChainWithRef({opens:[...state.opens,String(fallback)],closes:[...state.closes,"22"]},ref);if(prop==="link")return createChainWithRef({opens:[...state.opens,String(fallback),"4"],closes:[...state.closes,"39","24"]},ref);return createChainWithRef({opens:[...state.opens,String(fallback)],closes:[...state.closes,"39"]},ref)}}return},set(_target,prop,value){if(prop==="level")return ref.level=fromChalkLevel(value),!0;return!1},has(_target,prop){if(prop==="level")return!0;if(typeof prop==="symbol")return!1;return prop in MODIFIERS||prop in FG_COLORS||prop in BG_COLORS||THEME_TOKENS.has(prop)||KNOWN_METHODS.has(prop)}});return proxyRef.proxy=proxy,proxy}function toChalkLevel2(cl){if(cl===null)return 0;if(cl==="basic")return 1;if(cl==="256")return 2;return 3}function fromChalkLevel2(level){if(level===0)return null;if(level===1)return"basic";if(level===2)return"256";return"truecolor"}var detectedColor=typeof process<"u"&&process.stdout?detectColor(process.stdout):null,chalk=createStyle({level:detectedColor}),chalk_default=chalk;class Chalk{constructor(options){if(options?.level!==void 0){let lvl=options.level;if(typeof lvl!=="number"||!Number.isInteger(lvl)||lvl<0||lvl>3)throw Error("The `level` option should be an integer from 0 to 3")}return createStyle({level:fromChalkLevel2(options?.level??toChalkLevel2(detectedColor))})}}var detectedLevel=toChalkLevel2(detectedColor),supportsColor=detectedLevel===0?!1:{level:detectedLevel},supportsColorStderr=(()=>{if(!process?.stderr)return!1;let level=toChalkLevel2(detectColor(process.stderr));return level===0?!1:{level}})(),modifierNames=["reset","bold","dim","italic","underline","overline","inverse","hidden","strikethrough","visible"],foregroundColorNames=["black","red","green","yellow","blue","magenta","cyan","white","gray","grey","blackBright","redBright","greenBright","yellowBright","blueBright","magentaBright","cyanBright","whiteBright"],backgroundColorNames=["bgBlack","bgRed","bgGreen","bgYellow","bgBlue","bgMagenta","bgCyan","bgWhite","bgGray","bgGrey","bgBlackBright","bgRedBright","bgGreenBright","bgYellowBright","bgBlueBright","bgMagentaBright","bgCyanBright","bgWhiteBright"],colorNames=[...foregroundColorNames,...backgroundColorNames];export{toChalkLevel2 as toChalkLevel,supportsColorStderr,supportsColor,modifierNames,fromChalkLevel2 as fromChalkLevel,foregroundColorNames,detectColor,chalk_default as default,colorNames,backgroundColorNames,Chalk};
3
+
4
+ //# debugId=1708C60C7309496864756E2164756E21