@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.10
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 +499 -313
- package/THIRD_PARTY_NOTICES.md +70 -23
- package/dist/Text-DV9CuzAT.d.ts +452 -0
- package/dist/ansi.d.ts +217 -0
- package/dist/ansi.js +87 -0
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.js +3 -0
- package/dist/cell-_ZVhbfl0.js +44 -0
- package/dist/color-CkbalRqK.js +2 -0
- package/dist/color-policy-BAC9-TZX.js +592 -0
- package/dist/color-policy-CCxuHIdD.d.ts +22 -0
- package/dist/color-profile-CyeHnG1T.d.ts +97 -0
- package/dist/color-profile-DHhQHY55.js +36 -0
- package/dist/color.d.ts +21 -0
- package/dist/color.js +3 -0
- package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
- package/dist/detect-B3dL4Q11.js +374 -0
- package/dist/detect-Db6GbKOm.d.ts +195 -0
- package/dist/{devtools-QpCMm9JH.mjs → devtools-DbthxoD1.js} +23 -24
- package/dist/env-YVw64yZS.js +9 -0
- package/dist/escapes-CB_6CWOE.d.ts +72 -0
- package/dist/geometry-BxXOzJgo.d.ts +11 -0
- package/dist/index-D48vQhhe.d.ts +21 -0
- package/dist/index-DDVME65c.d.ts +919 -0
- package/dist/index.d.ts +1310 -0
- package/dist/index.js +3209 -0
- package/dist/osc-BFKKSqpg.js +71 -0
- package/dist/osc-Cn0fw77g.d.ts +23 -0
- package/dist/paint-Cx-zC_sX.d.ts +81 -0
- package/dist/query-BNc2B8GD.d.ts +152 -0
- package/dist/router.d.ts +392 -0
- package/dist/router.js +709 -0
- package/dist/sample-Cqw1bjUL.js +445 -0
- package/dist/screen-CgC2WlVM.d.ts +49 -0
- package/dist/screen-CiPytswf.js +342 -0
- package/dist/screen.d.ts +5 -0
- package/dist/screen.js +5 -0
- package/dist/semantic-text-style-DIMzC7xt.js +91 -0
- package/dist/serialize-BTkAZgw1.js +79 -0
- package/dist/session-DDQ5V300.js +723 -0
- package/dist/sgr-BhwaWAJB.js +246 -0
- package/dist/store-C1P5fOUi.d.ts +72 -0
- package/dist/string-width-CijQwpIk.js +69 -0
- package/dist/strip-BvU4toXG.js +6 -0
- package/dist/terminal.d.ts +121 -0
- package/dist/terminal.js +2 -0
- package/dist/tokenize-AjqbvtiT.js +1242 -0
- package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
- package/dist/truncate-D31fhU6i.js +562 -0
- package/dist/use-focus-CcCIdCWA.js +1337 -0
- package/dist/yoga-5jKhYCJC.js +3465 -0
- package/dist/yoga.d.ts +2 -0
- package/dist/yoga.js +2 -0
- package/package.json +70 -24
- package/src/ansi/chalk.ts +138 -0
- package/src/ansi/cursor.ts +46 -0
- package/src/ansi/east-asian-width.ts +259 -0
- package/src/ansi/escapes.ts +120 -0
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +27 -0
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/sgr.ts +234 -0
- package/src/ansi/slice.ts +43 -0
- package/src/ansi/string-width.ts +121 -0
- package/src/ansi/strip.ts +13 -0
- package/src/ansi/tokenize.ts +380 -0
- package/src/ansi/truncate.ts +196 -0
- package/src/ansi/wrap.ts +765 -0
- package/src/ansi-tokenizer.ts +510 -0
- package/src/capabilities/color-policy.ts +34 -0
- package/src/capabilities/detect.ts +608 -0
- package/src/capabilities/index.ts +37 -0
- package/src/capabilities/query.ts +679 -0
- package/src/capabilities/store.ts +394 -0
- package/src/code-excerpt.ts +39 -0
- package/src/color/index.ts +3 -0
- package/src/color/paint.ts +169 -0
- package/src/color/palette.ts +48 -0
- package/src/color/sample.ts +323 -0
- package/src/color.ts +1 -0
- package/src/components/AccessibilityContext.ts +5 -0
- package/src/components/AnimationContext.ts +24 -0
- package/src/components/AnsiText.tsx +42 -0
- package/src/components/App.tsx +878 -0
- package/src/components/AppContext.ts +111 -0
- package/src/components/BackgroundContext.ts +7 -0
- package/src/components/Box.tsx +100 -0
- package/src/components/CursorContext.ts +19 -0
- package/src/components/ErrorBoundary.tsx +39 -0
- package/src/components/ErrorOverview.tsx +135 -0
- package/src/components/FocusContext.ts +30 -0
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/Newline.tsx +16 -0
- package/src/components/Spacer.tsx +11 -0
- package/src/components/Static.tsx +60 -0
- package/src/components/StderrContext.ts +24 -0
- package/src/components/StdinContext.ts +48 -0
- package/src/components/StdoutContext.ts +26 -0
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +122 -0
- package/src/components/Transform.tsx +38 -0
- package/src/components/VirtualList.tsx +128 -0
- package/src/cursor-position.ts +103 -0
- package/src/devtools.ts +103 -0
- package/src/dom.ts +301 -0
- package/src/env.ts +12 -0
- package/src/get-max-width.ts +11 -0
- package/src/global.d.ts +38 -0
- package/src/glyphs.ts +99 -0
- package/src/hooks/use-animation.ts +142 -0
- package/src/hooks/use-app.ts +8 -0
- package/src/hooks/use-box-metrics.ts +134 -0
- package/src/hooks/use-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +33 -0
- package/src/hooks/use-focus-manager.ts +62 -0
- package/src/hooks/use-focus.ts +82 -0
- package/src/hooks/use-input.ts +267 -0
- package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
- package/src/hooks/use-paste.ts +78 -0
- package/src/hooks/use-stderr.ts +8 -0
- package/src/hooks/use-stdin.ts +10 -0
- package/src/hooks/use-stdout.ts +8 -0
- package/src/hooks/use-terminal-osc.ts +59 -0
- package/src/hooks/use-virtual-scroll.ts +84 -0
- package/src/hooks/use-window-size.ts +37 -0
- package/src/index.ts +96 -0
- package/src/ink.tsx +1452 -0
- package/src/input-parser.ts +303 -0
- package/src/instances.ts +9 -0
- package/src/kitty-keyboard.ts +185 -0
- package/src/measure-element.ts +62 -0
- package/src/measure-text.ts +31 -0
- package/src/paint-tree.ts +220 -0
- package/src/parse-keypress.ts +515 -0
- package/src/parse-stack-line.ts +138 -0
- package/src/patch-console.ts +106 -0
- package/src/quick-lru.ts +85 -0
- package/src/reconciler.ts +425 -0
- package/src/render-background.ts +59 -0
- package/src/render-border.ts +167 -0
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +146 -0
- package/src/render.ts +284 -0
- package/src/router/components.tsx +343 -0
- package/src/router/context.ts +41 -0
- package/src/router/history.ts +194 -0
- package/src/router/hooks.tsx +391 -0
- package/src/router/index.ts +34 -0
- package/src/router/matcher.ts +571 -0
- package/src/sanitize-ansi.ts +33 -0
- package/src/screen/ansi.ts +184 -0
- package/src/screen/canvas.ts +160 -0
- package/src/screen/cell.ts +138 -0
- package/src/screen/color-profile.ts +47 -0
- package/src/screen/geometry.ts +9 -0
- package/src/screen/index.ts +6 -0
- package/src/screen/screen.ts +305 -0
- package/src/screen/serialize.ts +129 -0
- package/src/screen.ts +1 -0
- package/src/semantic-text-style.ts +118 -0
- package/src/signal-exit.ts +106 -0
- package/src/squash-text-nodes.ts +37 -0
- package/src/stream.ts +28 -0
- package/src/structured-text.ts +325 -0
- package/src/styles.ts +753 -0
- package/src/terminal/index.ts +2 -0
- package/src/terminal/inline-presenter.ts +120 -0
- package/src/terminal/input.ts +92 -0
- package/src/terminal/render-scheduler.ts +37 -0
- package/src/terminal/screen-presenter.ts +242 -0
- package/src/terminal/session.ts +408 -0
- package/src/terminal-size.ts +58 -0
- package/src/terminal.ts +1 -0
- package/src/testing/browser.ts +588 -0
- package/src/testing/emulators.ts +205 -0
- package/src/testing/explorer-app/index.html +12 -0
- package/src/testing/explorer-app/main.ts +381 -0
- package/src/testing/explorer-app/style.css +194 -0
- package/src/testing/explorer-app/tsconfig.json +15 -0
- package/src/testing/explorer-app/vite-env.d.ts +1 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/keys.ts +56 -0
- package/src/testing/live.ts +85 -0
- package/src/testing/matchers.ts +70 -0
- package/src/testing/public.ts +94 -0
- package/src/testing/terminal.ts +361 -0
- package/src/testing/vitest.ts +157 -0
- package/src/throttle.ts +73 -0
- package/src/transform-adapter.ts +14 -0
- package/src/types.ts +10 -0
- package/src/virtual-scroll.ts +133 -0
- package/src/wrap-text.ts +54 -0
- package/src/yoga/config.ts +57 -0
- package/src/yoga/core/absoluteLayout.ts +626 -0
- package/src/yoga/core/baseline.ts +66 -0
- package/src/yoga/core/cache.ts +136 -0
- package/src/yoga/core/calculateLayout.ts +2926 -0
- package/src/yoga/core/config.ts +104 -0
- package/src/yoga/core/flexLine.ts +177 -0
- package/src/yoga/core/helpers.ts +293 -0
- package/src/yoga/core/layoutResults.ts +167 -0
- package/src/yoga/core/node.ts +611 -0
- package/src/yoga/core/numeric.ts +44 -0
- package/src/yoga/core/pixelGrid.ts +151 -0
- package/src/yoga/core/style.ts +887 -0
- package/src/yoga/core/types.ts +224 -0
- package/src/yoga/generated/YGEnums.ts +263 -0
- package/src/yoga/index.ts +19 -0
- package/src/yoga/node.ts +1140 -0
- package/dist/index.d.mts +0 -2379
- package/dist/index.mjs +0 -10072
package/README.md
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
1
|
<div align="center">
|
|
6
2
|
<br>
|
|
7
3
|
<h1>Sigil</h1>
|
|
@@ -15,13 +11,29 @@
|
|
|
15
11
|
- **Self-contained**: the only runtime dependencies are `react-reconciler` and `scheduler`. Everything else — the ANSI/terminal subsystem (`src/ansi/`), styling, width measurement, wrapping — is first-class TypeScript in this repository.
|
|
16
12
|
- **Integrated layout engine**: layout is computed by a TypeScript port of [Yoga](https://github.com/facebook/yoga) (`src/yoga/`) instead of the Yoga WASM binary, continuously verified against an f64-patched reference Yoga build via differential fuzzing.
|
|
17
13
|
- **Modern toolchain**: built with [Vite+](https://viteplus.dev) (Vitest, Oxlint, Oxfmt, tsdown) and TypeScript 7.
|
|
14
|
+
- **Structured terminal core**: cells retain semantic colors, gradients, links, and graphemes through layout and composition, then serialize for the target terminal profile.
|
|
15
|
+
|
|
16
|
+
Low-level integrations are available through focused `ansi`, `capabilities`, `color`, `router`, `screen`, `terminal`, and `yoga` subpaths. See the [architecture guide](docs/architecture.md), [API stability policy](docs/api-stability.md), [color semantics](docs/color-semantics.md), and [performance notes](docs/performance.md).
|
|
18
17
|
|
|
19
18
|
Upstream Ink and Yoga are tracked as submodules in `.vendor/` — upstream's test suites run against this codebase as part of the regular test run. See `THIRD_PARTY_NOTICES.md` for attribution.
|
|
20
19
|
|
|
21
|
-
The
|
|
20
|
+
The familiar Ink component and hook surface remains available from the package root. Sigil also adds terminal-aware APIs described below. The longer API reference is inherited from Ink and uses Sigil imports where applicable.
|
|
21
|
+
|
|
22
|
+
## Sigil extensions
|
|
23
|
+
|
|
24
|
+
- **Structured rendering** — layout produces a semantic `Screen` of grapheme-aware cells before terminal-specific ANSI serialization. Import low-level primitives from `@alchemy.run/sigil/screen`, `@alchemy.run/sigil/color`, and `@alchemy.run/sigil/terminal`.
|
|
25
|
+
- **Terminal capabilities** — `getCapabilities()` returns the current snapshot, while `capabilities.subscribe()` and `useCapabilitiesChange()` observe live terminal reports. The framework-free API is available from `@alchemy.run/sigil/capabilities`.
|
|
26
|
+
- **Terminal integrations** — `<Hyperlink>` provides OSC 8 links with a fallback. `useTitle`, `useWorkingDirectory`, `useProgress`, `useNotification`, `useClipboard`, and `usePointerShape` manage session-scoped OSC state.
|
|
27
|
+
- **ANSI-aware text** — `<AnsiText>` interprets ANSI-styled input without flattening its semantic colors before rendering.
|
|
28
|
+
- **Routing** — `@alchemy.run/sigil/router` provides an in-memory router designed for terminal applications. See the [routing recipe](recipes/routing.md).
|
|
29
|
+
- **Testing** — the repository includes an internal emulator-backed E2E harness and interactive explorer. These utilities are not part of the public API while their design is evolving.
|
|
30
|
+
|
|
31
|
+
Sigil requires Node.js 22 or newer and React 19.2 or newer.
|
|
22
32
|
|
|
23
33
|
---
|
|
24
34
|
|
|
35
|
+
[](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
|
|
36
|
+
|
|
25
37
|
Ink provides the same component-based UI building experience that React offers in the browser, but for command-line apps.
|
|
26
38
|
It uses [Yoga](https://github.com/facebook/yoga) to build Flexbox layouts in the terminal, so most CSS-like properties are available in Ink as well.
|
|
27
39
|
If you are already familiar with React, you already know Ink.
|
|
@@ -60,19 +72,19 @@ import React, { useState, useEffect } from "react";
|
|
|
60
72
|
import { render, Text } from "@alchemy.run/sigil";
|
|
61
73
|
|
|
62
74
|
const Counter = () => {
|
|
63
|
-
|
|
75
|
+
const [counter, setCounter] = useState(0);
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const timer = setInterval(() => {
|
|
79
|
+
setCounter((previousCounter) => previousCounter + 1);
|
|
80
|
+
}, 100);
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
return () => {
|
|
83
|
+
clearInterval(timer);
|
|
84
|
+
};
|
|
85
|
+
}, []);
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
return <Text color="green">{counter} tests passed</Text>;
|
|
76
88
|
};
|
|
77
89
|
|
|
78
90
|
render(<Counter />);
|
|
@@ -152,6 +164,7 @@ _(PRs welcome. Append new entries at the end. Repos must have 100+ stars and sho
|
|
|
152
164
|
- [`<Spacer>`](#spacer)
|
|
153
165
|
- [`<Static>`](#static)
|
|
154
166
|
- [`<Transform>`](#transform)
|
|
167
|
+
- [`<VirtualList>`](#virtuallist)
|
|
155
168
|
- [Hooks](#hooks)
|
|
156
169
|
- [`useInput`](#useinputinputhandler-options)
|
|
157
170
|
- [`usePaste`](#usepastehandler-options)
|
|
@@ -159,6 +172,7 @@ _(PRs welcome. Append new entries at the end. Repos must have 100+ stars and sho
|
|
|
159
172
|
- [`useStdin`](#usestdin)
|
|
160
173
|
- [`useStdout`](#usestdout)
|
|
161
174
|
- [`useBoxMetrics`](#useboxmetricsref)
|
|
175
|
+
- [`useVirtualScroll`](#usevirtualscrolloptions)
|
|
162
176
|
- [`useStderr`](#usestderr)
|
|
163
177
|
- [`useWindowSize`](#usewindowsize)
|
|
164
178
|
- [`useFocus`](#usefocusoptions)
|
|
@@ -202,7 +216,7 @@ npm install --save-dev @babel/preset-react
|
|
|
202
216
|
|
|
203
217
|
```json
|
|
204
218
|
{
|
|
205
|
-
|
|
219
|
+
"presets": ["@babel/preset-react"]
|
|
206
220
|
}
|
|
207
221
|
```
|
|
208
222
|
|
|
@@ -266,18 +280,18 @@ This component can display text and change its style to make it bold, underlined
|
|
|
266
280
|
import { render, Text } from "@alchemy.run/sigil";
|
|
267
281
|
|
|
268
282
|
const Example = () => (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
283
|
+
<>
|
|
284
|
+
<Text color="green">I am green</Text>
|
|
285
|
+
<Text color="black" backgroundColor="white">
|
|
286
|
+
I am black on white
|
|
287
|
+
</Text>
|
|
288
|
+
<Text color="#ffffff">I am white</Text>
|
|
289
|
+
<Text bold>I am bold</Text>
|
|
290
|
+
<Text italic>I am italic</Text>
|
|
291
|
+
<Text underline>I am underline</Text>
|
|
292
|
+
<Text strikethrough>I am strikethrough</Text>
|
|
293
|
+
<Text inverse>I am inversed</Text>
|
|
294
|
+
</>
|
|
281
295
|
);
|
|
282
296
|
|
|
283
297
|
render(<Example />);
|
|
@@ -324,7 +338,7 @@ Dim the color (make it less bright).
|
|
|
324
338
|
|
|
325
339
|
```jsx
|
|
326
340
|
<Text color="red" dimColor>
|
|
327
|
-
|
|
341
|
+
Dimmed Red
|
|
328
342
|
</Text>
|
|
329
343
|
```
|
|
330
344
|
|
|
@@ -367,7 +381,7 @@ Invert background and foreground colors.
|
|
|
367
381
|
|
|
368
382
|
```jsx
|
|
369
383
|
<Text inverse color="yellow">
|
|
370
|
-
|
|
384
|
+
Inversed Yellow
|
|
371
385
|
</Text>
|
|
372
386
|
```
|
|
373
387
|
|
|
@@ -421,9 +435,9 @@ It's like `<div style="display: flex">` in the browser.
|
|
|
421
435
|
import { render, Box, Text } from "@alchemy.run/sigil";
|
|
422
436
|
|
|
423
437
|
const Example = () => (
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
438
|
+
<Box margin={2}>
|
|
439
|
+
<Text>This is a box with margin</Text>
|
|
440
|
+
</Box>
|
|
427
441
|
);
|
|
428
442
|
|
|
429
443
|
render(<Example />);
|
|
@@ -440,17 +454,17 @@ You can also set it as a percentage, which will calculate the width based on the
|
|
|
440
454
|
|
|
441
455
|
```jsx
|
|
442
456
|
<Box width={4}>
|
|
443
|
-
|
|
457
|
+
<Text>X</Text>
|
|
444
458
|
</Box>
|
|
445
459
|
//=> 'X '
|
|
446
460
|
```
|
|
447
461
|
|
|
448
462
|
```jsx
|
|
449
463
|
<Box width={10}>
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
464
|
+
<Box width="50%">
|
|
465
|
+
<Text>X</Text>
|
|
466
|
+
</Box>
|
|
467
|
+
<Text>Y</Text>
|
|
454
468
|
</Box>
|
|
455
469
|
//=> 'X Y'
|
|
456
470
|
```
|
|
@@ -464,17 +478,17 @@ You can also set it as a percentage, which will calculate the height based on th
|
|
|
464
478
|
|
|
465
479
|
```jsx
|
|
466
480
|
<Box height={4}>
|
|
467
|
-
|
|
481
|
+
<Text>X</Text>
|
|
468
482
|
</Box>
|
|
469
483
|
//=> 'X\n\n\n'
|
|
470
484
|
```
|
|
471
485
|
|
|
472
486
|
```jsx
|
|
473
487
|
<Box height={6} flexDirection="column">
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
488
|
+
<Box height="50%">
|
|
489
|
+
<Text>X</Text>
|
|
490
|
+
</Box>
|
|
491
|
+
<Text>Y</Text>
|
|
478
492
|
</Box>
|
|
479
493
|
//=> 'X\n\n\nY\n\n'
|
|
480
494
|
```
|
|
@@ -648,9 +662,9 @@ Size of the gap between an element's columns and rows. A shorthand for `columnGa
|
|
|
648
662
|
|
|
649
663
|
```jsx
|
|
650
664
|
<Box gap={1} width={3} flexWrap="wrap">
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
665
|
+
<Text>A</Text>
|
|
666
|
+
<Text>B</Text>
|
|
667
|
+
<Text>C</Text>
|
|
654
668
|
</Box>
|
|
655
669
|
// A B
|
|
656
670
|
//
|
|
@@ -666,8 +680,8 @@ Size of the gap between an element's columns.
|
|
|
666
680
|
|
|
667
681
|
```jsx
|
|
668
682
|
<Box columnGap={1}>
|
|
669
|
-
|
|
670
|
-
|
|
683
|
+
<Text>A</Text>
|
|
684
|
+
<Text>B</Text>
|
|
671
685
|
</Box>
|
|
672
686
|
// A B
|
|
673
687
|
```
|
|
@@ -681,8 +695,8 @@ Size of the gap between an element's rows.
|
|
|
681
695
|
|
|
682
696
|
```jsx
|
|
683
697
|
<Box flexDirection="column" rowGap={1}>
|
|
684
|
-
|
|
685
|
-
|
|
698
|
+
<Text>A</Text>
|
|
699
|
+
<Text>B</Text>
|
|
686
700
|
</Box>
|
|
687
701
|
// A
|
|
688
702
|
//
|
|
@@ -700,10 +714,10 @@ See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
|
|
|
700
714
|
|
|
701
715
|
```jsx
|
|
702
716
|
<Box>
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
717
|
+
<Text>Label:</Text>
|
|
718
|
+
<Box flexGrow={1}>
|
|
719
|
+
<Text>Fills all remaining space</Text>
|
|
720
|
+
</Box>
|
|
707
721
|
</Box>
|
|
708
722
|
```
|
|
709
723
|
|
|
@@ -716,12 +730,12 @@ See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
|
|
|
716
730
|
|
|
717
731
|
```jsx
|
|
718
732
|
<Box width={20}>
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
733
|
+
<Box flexShrink={2} width={10}>
|
|
734
|
+
<Text>Will be 1/4</Text>
|
|
735
|
+
</Box>
|
|
736
|
+
<Box width={10}>
|
|
737
|
+
<Text>Will be 3/4</Text>
|
|
738
|
+
</Box>
|
|
725
739
|
</Box>
|
|
726
740
|
```
|
|
727
741
|
|
|
@@ -733,20 +747,20 @@ See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/).
|
|
|
733
747
|
|
|
734
748
|
```jsx
|
|
735
749
|
<Box width={6}>
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
750
|
+
<Box flexBasis={3}>
|
|
751
|
+
<Text>X</Text>
|
|
752
|
+
</Box>
|
|
753
|
+
<Text>Y</Text>
|
|
740
754
|
</Box>
|
|
741
755
|
//=> 'X Y'
|
|
742
756
|
```
|
|
743
757
|
|
|
744
758
|
```jsx
|
|
745
759
|
<Box width={6}>
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
760
|
+
<Box flexBasis="50%">
|
|
761
|
+
<Text>X</Text>
|
|
762
|
+
</Box>
|
|
763
|
+
<Text>Y</Text>
|
|
750
764
|
</Box>
|
|
751
765
|
//=> 'X Y'
|
|
752
766
|
```
|
|
@@ -799,8 +813,8 @@ See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
|
|
|
799
813
|
|
|
800
814
|
```jsx
|
|
801
815
|
<Box width={2} flexWrap="wrap">
|
|
802
|
-
|
|
803
|
-
|
|
816
|
+
<Text>A</Text>
|
|
817
|
+
<Text>BC</Text>
|
|
804
818
|
</Box>
|
|
805
819
|
// A
|
|
806
820
|
// B C
|
|
@@ -808,9 +822,9 @@ See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
|
|
|
808
822
|
|
|
809
823
|
```jsx
|
|
810
824
|
<Box flexDirection="column" height={2} flexWrap="wrap">
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
825
|
+
<Text>A</Text>
|
|
826
|
+
<Text>B</Text>
|
|
827
|
+
<Text>C</Text>
|
|
814
828
|
</Box>
|
|
815
829
|
// A C
|
|
816
830
|
// B
|
|
@@ -1049,37 +1063,37 @@ Ink uses border styles from the [`cli-boxes`](https://github.com/sindresorhus/cl
|
|
|
1049
1063
|
|
|
1050
1064
|
```jsx
|
|
1051
1065
|
<Box flexDirection="column">
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1066
|
+
<Box>
|
|
1067
|
+
<Box borderStyle="single" marginRight={2}>
|
|
1068
|
+
<Text>single</Text>
|
|
1069
|
+
</Box>
|
|
1070
|
+
|
|
1071
|
+
<Box borderStyle="double" marginRight={2}>
|
|
1072
|
+
<Text>double</Text>
|
|
1073
|
+
</Box>
|
|
1074
|
+
|
|
1075
|
+
<Box borderStyle="round" marginRight={2}>
|
|
1076
|
+
<Text>round</Text>
|
|
1077
|
+
</Box>
|
|
1078
|
+
|
|
1079
|
+
<Box borderStyle="bold">
|
|
1080
|
+
<Text>bold</Text>
|
|
1081
|
+
</Box>
|
|
1082
|
+
</Box>
|
|
1083
|
+
|
|
1084
|
+
<Box marginTop={1}>
|
|
1085
|
+
<Box borderStyle="singleDouble" marginRight={2}>
|
|
1086
|
+
<Text>singleDouble</Text>
|
|
1087
|
+
</Box>
|
|
1088
|
+
|
|
1089
|
+
<Box borderStyle="doubleSingle" marginRight={2}>
|
|
1090
|
+
<Text>doubleSingle</Text>
|
|
1091
|
+
</Box>
|
|
1092
|
+
|
|
1093
|
+
<Box borderStyle="classic">
|
|
1094
|
+
<Text>classic</Text>
|
|
1095
|
+
</Box>
|
|
1096
|
+
</Box>
|
|
1083
1097
|
</Box>
|
|
1084
1098
|
```
|
|
1085
1099
|
|
|
@@ -1089,18 +1103,18 @@ Alternatively, pass a custom border style like so:
|
|
|
1089
1103
|
|
|
1090
1104
|
```jsx
|
|
1091
1105
|
<Box
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1106
|
+
borderStyle={{
|
|
1107
|
+
topLeft: "↘",
|
|
1108
|
+
top: "↓",
|
|
1109
|
+
topRight: "↙",
|
|
1110
|
+
left: "→",
|
|
1111
|
+
bottomLeft: "↗",
|
|
1112
|
+
bottom: "↑",
|
|
1113
|
+
bottomRight: "↖",
|
|
1114
|
+
right: "←",
|
|
1115
|
+
}}
|
|
1102
1116
|
>
|
|
1103
|
-
|
|
1117
|
+
<Text>Custom</Text>
|
|
1104
1118
|
</Box>
|
|
1105
1119
|
```
|
|
1106
1120
|
|
|
@@ -1115,7 +1129,7 @@ A shorthand for setting `borderTopColor`, `borderRightColor`, `borderBottomColor
|
|
|
1115
1129
|
|
|
1116
1130
|
```jsx
|
|
1117
1131
|
<Box borderStyle="round" borderColor="green">
|
|
1118
|
-
|
|
1132
|
+
<Text>Green Rounded Box</Text>
|
|
1119
1133
|
</Box>
|
|
1120
1134
|
```
|
|
1121
1135
|
|
|
@@ -1130,7 +1144,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
|
|
|
1130
1144
|
|
|
1131
1145
|
```jsx
|
|
1132
1146
|
<Box borderStyle="round" borderTopColor="green">
|
|
1133
|
-
|
|
1147
|
+
<Text>Hello world</Text>
|
|
1134
1148
|
</Box>
|
|
1135
1149
|
```
|
|
1136
1150
|
|
|
@@ -1143,7 +1157,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
|
|
|
1143
1157
|
|
|
1144
1158
|
```jsx
|
|
1145
1159
|
<Box borderStyle="round" borderRightColor="green">
|
|
1146
|
-
|
|
1160
|
+
<Text>Hello world</Text>
|
|
1147
1161
|
</Box>
|
|
1148
1162
|
```
|
|
1149
1163
|
|
|
@@ -1156,7 +1170,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
|
|
|
1156
1170
|
|
|
1157
1171
|
```jsx
|
|
1158
1172
|
<Box borderStyle="round" borderBottomColor="green">
|
|
1159
|
-
|
|
1173
|
+
<Text>Hello world</Text>
|
|
1160
1174
|
</Box>
|
|
1161
1175
|
```
|
|
1162
1176
|
|
|
@@ -1169,7 +1183,7 @@ Accepts the same values as [`color`](#color) in `<Text>` component.
|
|
|
1169
1183
|
|
|
1170
1184
|
```jsx
|
|
1171
1185
|
<Box borderStyle="round" borderLeftColor="green">
|
|
1172
|
-
|
|
1186
|
+
<Text>Hello world</Text>
|
|
1173
1187
|
</Box>
|
|
1174
1188
|
```
|
|
1175
1189
|
|
|
@@ -1183,7 +1197,7 @@ A shorthand for setting `borderTopDimColor`, `borderBottomDimColor`, `borderLeft
|
|
|
1183
1197
|
|
|
1184
1198
|
```jsx
|
|
1185
1199
|
<Box borderStyle="round" borderDimColor>
|
|
1186
|
-
|
|
1200
|
+
<Text>Hello world</Text>
|
|
1187
1201
|
</Box>
|
|
1188
1202
|
```
|
|
1189
1203
|
|
|
@@ -1196,7 +1210,7 @@ Dim the top border color.
|
|
|
1196
1210
|
|
|
1197
1211
|
```jsx
|
|
1198
1212
|
<Box borderStyle="round" borderTopDimColor>
|
|
1199
|
-
|
|
1213
|
+
<Text>Hello world</Text>
|
|
1200
1214
|
</Box>
|
|
1201
1215
|
```
|
|
1202
1216
|
|
|
@@ -1209,7 +1223,7 @@ Dim the bottom border color.
|
|
|
1209
1223
|
|
|
1210
1224
|
```jsx
|
|
1211
1225
|
<Box borderStyle="round" borderBottomDimColor>
|
|
1212
|
-
|
|
1226
|
+
<Text>Hello world</Text>
|
|
1213
1227
|
</Box>
|
|
1214
1228
|
```
|
|
1215
1229
|
|
|
@@ -1222,7 +1236,7 @@ Dim the left border color.
|
|
|
1222
1236
|
|
|
1223
1237
|
```jsx
|
|
1224
1238
|
<Box borderStyle="round" borderLeftDimColor>
|
|
1225
|
-
|
|
1239
|
+
<Text>Hello world</Text>
|
|
1226
1240
|
</Box>
|
|
1227
1241
|
```
|
|
1228
1242
|
|
|
@@ -1235,7 +1249,7 @@ Dim the right border color.
|
|
|
1235
1249
|
|
|
1236
1250
|
```jsx
|
|
1237
1251
|
<Box borderStyle="round" borderRightDimColor>
|
|
1238
|
-
|
|
1252
|
+
<Text>Hello world</Text>
|
|
1239
1253
|
</Box>
|
|
1240
1254
|
```
|
|
1241
1255
|
|
|
@@ -1249,7 +1263,7 @@ A shorthand for setting `borderTopBackgroundColor`, `borderRightBackgroundColor`
|
|
|
1249
1263
|
|
|
1250
1264
|
```jsx
|
|
1251
1265
|
<Box borderStyle="round" borderColor="white" borderBackgroundColor="green">
|
|
1252
|
-
|
|
1266
|
+
<Text>Hello world</Text>
|
|
1253
1267
|
</Box>
|
|
1254
1268
|
```
|
|
1255
1269
|
|
|
@@ -1263,7 +1277,7 @@ Falls back to `borderBackgroundColor` if not specified.
|
|
|
1263
1277
|
|
|
1264
1278
|
```jsx
|
|
1265
1279
|
<Box borderStyle="round" borderColor="white" borderTopBackgroundColor="green">
|
|
1266
|
-
|
|
1280
|
+
<Text>Hello world</Text>
|
|
1267
1281
|
</Box>
|
|
1268
1282
|
```
|
|
1269
1283
|
|
|
@@ -1277,7 +1291,7 @@ Falls back to `borderBackgroundColor` if not specified.
|
|
|
1277
1291
|
|
|
1278
1292
|
```jsx
|
|
1279
1293
|
<Box borderStyle="round" borderColor="white" borderBottomBackgroundColor="green">
|
|
1280
|
-
|
|
1294
|
+
<Text>Hello world</Text>
|
|
1281
1295
|
</Box>
|
|
1282
1296
|
```
|
|
1283
1297
|
|
|
@@ -1291,7 +1305,7 @@ Falls back to `borderBackgroundColor` if not specified.
|
|
|
1291
1305
|
|
|
1292
1306
|
```jsx
|
|
1293
1307
|
<Box borderStyle="round" borderColor="white" borderRightBackgroundColor="green">
|
|
1294
|
-
|
|
1308
|
+
<Text>Hello world</Text>
|
|
1295
1309
|
</Box>
|
|
1296
1310
|
```
|
|
1297
1311
|
|
|
@@ -1305,7 +1319,7 @@ Falls back to `borderBackgroundColor` if not specified.
|
|
|
1305
1319
|
|
|
1306
1320
|
```jsx
|
|
1307
1321
|
<Box borderStyle="round" borderColor="white" borderLeftBackgroundColor="green">
|
|
1308
|
-
|
|
1322
|
+
<Text>Hello world</Text>
|
|
1309
1323
|
</Box>
|
|
1310
1324
|
```
|
|
1311
1325
|
|
|
@@ -1349,27 +1363,29 @@ Accepts the same values as [`color`](#color) in the `<Text>` component.
|
|
|
1349
1363
|
|
|
1350
1364
|
```jsx
|
|
1351
1365
|
<Box flexDirection="column">
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1366
|
+
<Box backgroundColor="red" width={20} height={5} alignSelf="flex-start">
|
|
1367
|
+
<Text>Red background</Text>
|
|
1368
|
+
</Box>
|
|
1355
1369
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1370
|
+
<Box backgroundColor="#FF8800" width={20} height={3} marginTop={1} alignSelf="flex-start">
|
|
1371
|
+
<Text>Orange background</Text>
|
|
1372
|
+
</Box>
|
|
1359
1373
|
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1374
|
+
<Box backgroundColor="rgb(0, 255, 0)" width={20} height={3} marginTop={1} alignSelf="flex-start">
|
|
1375
|
+
<Text>Green background</Text>
|
|
1376
|
+
</Box>
|
|
1363
1377
|
</Box>
|
|
1364
1378
|
```
|
|
1365
1379
|
|
|
1366
1380
|
The background color fills the entire `<Box>` area and is inherited by child `<Text>` components unless they specify their own `backgroundColor`.
|
|
1367
1381
|
|
|
1382
|
+
Use `backgroundColor=""` to paint the box with explicit blank cells on the terminal's default background. This is useful for opaque compositor layers: lower-layer glyphs are erased without assuming what color the user's terminal background is. Omitting `backgroundColor` keeps the box transparent.
|
|
1383
|
+
|
|
1368
1384
|
```jsx
|
|
1369
1385
|
<Box backgroundColor="blue" alignSelf="flex-start">
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1386
|
+
<Text>Blue inherited </Text>
|
|
1387
|
+
<Text backgroundColor="yellow">Yellow override </Text>
|
|
1388
|
+
<Text>Blue inherited again</Text>
|
|
1373
1389
|
</Box>
|
|
1374
1390
|
```
|
|
1375
1391
|
|
|
@@ -1377,7 +1393,7 @@ Background colors work with borders and padding:
|
|
|
1377
1393
|
|
|
1378
1394
|
```jsx
|
|
1379
1395
|
<Box backgroundColor="cyan" borderStyle="round" padding={1} alignSelf="flex-start">
|
|
1380
|
-
|
|
1396
|
+
<Text>Background with border and padding</Text>
|
|
1381
1397
|
</Box>
|
|
1382
1398
|
```
|
|
1383
1399
|
|
|
@@ -1399,11 +1415,11 @@ Number of newlines to insert.
|
|
|
1399
1415
|
import { render, Text, Newline } from "@alchemy.run/sigil";
|
|
1400
1416
|
|
|
1401
1417
|
const Example = () => (
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1418
|
+
<Text>
|
|
1419
|
+
<Text color="green">Hello</Text>
|
|
1420
|
+
<Newline />
|
|
1421
|
+
<Text color="red">World</Text>
|
|
1422
|
+
</Text>
|
|
1407
1423
|
);
|
|
1408
1424
|
|
|
1409
1425
|
render(<Example />);
|
|
@@ -1427,11 +1443,11 @@ For example, using `<Spacer>` in a `<Box>` with default flex direction (`row`) w
|
|
|
1427
1443
|
import { render, Box, Text, Spacer } from "@alchemy.run/sigil";
|
|
1428
1444
|
|
|
1429
1445
|
const Example = () => (
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1446
|
+
<Box>
|
|
1447
|
+
<Text>Left</Text>
|
|
1448
|
+
<Spacer />
|
|
1449
|
+
<Text>Right</Text>
|
|
1450
|
+
</Box>
|
|
1435
1451
|
);
|
|
1436
1452
|
|
|
1437
1453
|
render(<Example />);
|
|
@@ -1444,11 +1460,11 @@ Note that the container needs to be tall enough to see this in effect.
|
|
|
1444
1460
|
import { render, Box, Text, Spacer } from "@alchemy.run/sigil";
|
|
1445
1461
|
|
|
1446
1462
|
const Example = () => (
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1463
|
+
<Box flexDirection="column" height={10}>
|
|
1464
|
+
<Text>Top</Text>
|
|
1465
|
+
<Spacer />
|
|
1466
|
+
<Text>Bottom</Text>
|
|
1467
|
+
</Box>
|
|
1452
1468
|
);
|
|
1453
1469
|
|
|
1454
1470
|
render(<Example />);
|
|
@@ -1472,51 +1488,51 @@ import React, { useState, useEffect } from "react";
|
|
|
1472
1488
|
import { render, Static, Box, Text } from "@alchemy.run/sigil";
|
|
1473
1489
|
|
|
1474
1490
|
const Example = () => {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1491
|
+
const [tests, setTests] = useState([]);
|
|
1492
|
+
|
|
1493
|
+
useEffect(() => {
|
|
1494
|
+
let completedTests = 0;
|
|
1495
|
+
let timer;
|
|
1496
|
+
|
|
1497
|
+
const run = () => {
|
|
1498
|
+
// Fake 10 completed tests
|
|
1499
|
+
if (completedTests++ < 10) {
|
|
1500
|
+
setTests((previousTests) => [
|
|
1501
|
+
...previousTests,
|
|
1502
|
+
{
|
|
1503
|
+
id: previousTests.length,
|
|
1504
|
+
title: `Test #${previousTests.length + 1}`,
|
|
1505
|
+
},
|
|
1506
|
+
]);
|
|
1507
|
+
|
|
1508
|
+
timer = setTimeout(run, 100);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
run();
|
|
1513
|
+
|
|
1514
|
+
return () => {
|
|
1515
|
+
clearTimeout(timer);
|
|
1516
|
+
};
|
|
1517
|
+
}, []);
|
|
1518
|
+
|
|
1519
|
+
return (
|
|
1520
|
+
<>
|
|
1521
|
+
{/* This part will be rendered once to the terminal */}
|
|
1522
|
+
<Static items={tests}>
|
|
1523
|
+
{(test) => (
|
|
1524
|
+
<Box key={test.id}>
|
|
1525
|
+
<Text color="green">✔ {test.title}</Text>
|
|
1526
|
+
</Box>
|
|
1527
|
+
)}
|
|
1528
|
+
</Static>
|
|
1529
|
+
|
|
1530
|
+
{/* This part keeps updating as state changes */}
|
|
1531
|
+
<Box marginTop={1}>
|
|
1532
|
+
<Text dimColor>Completed tests: {tests.length}</Text>
|
|
1533
|
+
</Box>
|
|
1534
|
+
</>
|
|
1535
|
+
);
|
|
1520
1536
|
};
|
|
1521
1537
|
|
|
1522
1538
|
render(<Example />);
|
|
@@ -1558,16 +1574,16 @@ Note that a `key` must be assigned to the root component.
|
|
|
1558
1574
|
|
|
1559
1575
|
```jsx
|
|
1560
1576
|
<Static items={["a", "b", "c"]}>
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1577
|
+
{(item, index) => {
|
|
1578
|
+
// This function is called for every item in ['a', 'b', 'c']
|
|
1579
|
+
// `item` is 'a', 'b', 'c'
|
|
1580
|
+
// `index` is 0, 1, 2
|
|
1581
|
+
return (
|
|
1582
|
+
<Box key={index}>
|
|
1583
|
+
<Text>Item: {item}</Text>
|
|
1584
|
+
</Box>
|
|
1585
|
+
);
|
|
1586
|
+
}}
|
|
1571
1587
|
</Static>
|
|
1572
1588
|
```
|
|
1573
1589
|
|
|
@@ -1588,9 +1604,9 @@ That's what the `<Transform>` component does: it gives you an output string of i
|
|
|
1588
1604
|
import { render, Transform } from "@alchemy.run/sigil";
|
|
1589
1605
|
|
|
1590
1606
|
const Example = () => (
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1607
|
+
<Transform transform={(output) => output.toUpperCase()}>
|
|
1608
|
+
<Text>Hello World</Text>
|
|
1609
|
+
</Transform>
|
|
1594
1610
|
);
|
|
1595
1611
|
|
|
1596
1612
|
render(<Example />);
|
|
@@ -1606,18 +1622,18 @@ For example, to implement a hanging indent component, you can indent all the lin
|
|
|
1606
1622
|
import { render, Transform } from "@alchemy.run/sigil";
|
|
1607
1623
|
|
|
1608
1624
|
const HangingIndent = ({ indent = 4, children }) => (
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1625
|
+
<Transform transform={(line, index) => (index === 0 ? line : " ".repeat(indent) + line)}>
|
|
1626
|
+
{children}
|
|
1627
|
+
</Transform>
|
|
1612
1628
|
);
|
|
1613
1629
|
|
|
1614
1630
|
const text =
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1631
|
+
"WHEN I WROTE the following pages, or rather the bulk of them, " +
|
|
1632
|
+
"I lived alone, in the woods, a mile from any neighbor, in a " +
|
|
1633
|
+
"house which I had built myself, on the shore of Walden Pond, " +
|
|
1634
|
+
"in Concord, Massachusetts, and earned my living by the labor " +
|
|
1635
|
+
"of my hands only. I lived there two years and two months. At " +
|
|
1636
|
+
"present I am a sojourner in civilized life again.";
|
|
1621
1637
|
|
|
1622
1638
|
render(<HangingIndent indent={4}>{text}</HangingIndent>);
|
|
1623
1639
|
```
|
|
@@ -1641,6 +1657,87 @@ Type: `number`
|
|
|
1641
1657
|
|
|
1642
1658
|
The zero-indexed line number of the line that's currently being transformed.
|
|
1643
1659
|
|
|
1660
|
+
### `<VirtualList>`
|
|
1661
|
+
|
|
1662
|
+
A vertically windowed list. Only the items that intersect the viewport are rendered, inside a clipped box that scrolls by whole rows. Items may have different heights, and the item at the top edge may be partially visible.
|
|
1663
|
+
|
|
1664
|
+
Give it a fixed `height`, or omit it and bound an ancestor instead: the list then takes the height of its content and shrinks to whatever rows the container leaves over. Siblings that must keep their size need `flexShrink={0}`, because every `<Box>` shrinks by default.
|
|
1665
|
+
|
|
1666
|
+
```jsx
|
|
1667
|
+
import { useState } from "react";
|
|
1668
|
+
import { render, Box, Text, VirtualList, useInput, useWindowSize } from "@alchemy.run/sigil";
|
|
1669
|
+
|
|
1670
|
+
const entries = Array.from({ length: 200 }, (_, index) => `entry ${index}`);
|
|
1671
|
+
|
|
1672
|
+
const Example = () => {
|
|
1673
|
+
const { rows } = useWindowSize();
|
|
1674
|
+
const [cursor, setCursor] = useState(0);
|
|
1675
|
+
|
|
1676
|
+
useInput((_, key) => {
|
|
1677
|
+
if (key.upArrow) setCursor((index) => Math.max(0, index - 1));
|
|
1678
|
+
if (key.downArrow) setCursor((index) => Math.min(entries.length - 1, index + 1));
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
return (
|
|
1682
|
+
<Box flexDirection="column" maxHeight={rows}>
|
|
1683
|
+
<Box flexShrink={0}>
|
|
1684
|
+
<Text bold>Entries</Text>
|
|
1685
|
+
</Box>
|
|
1686
|
+
<VirtualList
|
|
1687
|
+
items={entries}
|
|
1688
|
+
itemHeight={() => 1}
|
|
1689
|
+
focusedIndex={cursor}
|
|
1690
|
+
renderItem={(entry, index) => <Text inverse={index === cursor}>{entry}</Text>}
|
|
1691
|
+
/>
|
|
1692
|
+
<Box flexShrink={0}>
|
|
1693
|
+
<Text dimColor>↑/↓ move</Text>
|
|
1694
|
+
</Box>
|
|
1695
|
+
</Box>
|
|
1696
|
+
);
|
|
1697
|
+
};
|
|
1698
|
+
|
|
1699
|
+
render(<Example />);
|
|
1700
|
+
```
|
|
1701
|
+
|
|
1702
|
+
#### items
|
|
1703
|
+
|
|
1704
|
+
Type: `ReadonlyArray<Item>`
|
|
1705
|
+
|
|
1706
|
+
Items to window over.
|
|
1707
|
+
|
|
1708
|
+
#### itemHeight
|
|
1709
|
+
|
|
1710
|
+
Type: `(item: Item, index: number) => number`
|
|
1711
|
+
|
|
1712
|
+
Height of an item in rows. It must match what `renderItem` produces for it: the window is computed from these numbers, never from the rendered output.
|
|
1713
|
+
|
|
1714
|
+
#### renderItem
|
|
1715
|
+
|
|
1716
|
+
Type: `(item: Item, index: number) => ReactNode`
|
|
1717
|
+
|
|
1718
|
+
Render one item. Only items intersecting the viewport are rendered.
|
|
1719
|
+
|
|
1720
|
+
#### getKey
|
|
1721
|
+
|
|
1722
|
+
Type: `(item: Item, index: number) => React.Key`
|
|
1723
|
+
|
|
1724
|
+
React key for an item. Defaults to its index.
|
|
1725
|
+
|
|
1726
|
+
#### focusedIndex
|
|
1727
|
+
|
|
1728
|
+
Type: `number`
|
|
1729
|
+
|
|
1730
|
+
Item to keep fully visible. When it changes, the list scrolls as little as necessary to show it. An item taller than the viewport is aligned to its top.
|
|
1731
|
+
|
|
1732
|
+
#### height
|
|
1733
|
+
|
|
1734
|
+
Type: `number`
|
|
1735
|
+
|
|
1736
|
+
Viewport height in rows. When omitted the list shrinks to the space its container leaves over, as described above.
|
|
1737
|
+
|
|
1738
|
+
> [!NOTE]
|
|
1739
|
+
> Until the first layout pass has measured the viewport, every item is rendered inside the clipped box so the first frame already looks right.
|
|
1740
|
+
|
|
1644
1741
|
## Hooks
|
|
1645
1742
|
|
|
1646
1743
|
### useInput(inputHandler, options?)
|
|
@@ -1955,7 +2052,7 @@ import { useApp } from "@alchemy.run/sigil";
|
|
|
1955
2052
|
const { suspendTerminal } = useApp();
|
|
1956
2053
|
|
|
1957
2054
|
await suspendTerminal(async () => {
|
|
1958
|
-
|
|
2055
|
+
await runEditor();
|
|
1959
2056
|
});
|
|
1960
2057
|
```
|
|
1961
2058
|
|
|
@@ -1965,9 +2062,9 @@ When called without a callback, it returns a suspension you resume yourself. `re
|
|
|
1965
2062
|
const suspension = await suspendTerminal();
|
|
1966
2063
|
|
|
1967
2064
|
try {
|
|
1968
|
-
|
|
2065
|
+
await runEditor();
|
|
1969
2066
|
} finally {
|
|
1970
|
-
|
|
2067
|
+
await suspension.resume();
|
|
1971
2068
|
}
|
|
1972
2069
|
```
|
|
1973
2070
|
|
|
@@ -2013,9 +2110,9 @@ A component using `setRawMode` might want to use `isRawModeSupported` to nicely
|
|
|
2013
2110
|
import { useStdin } from "@alchemy.run/sigil";
|
|
2014
2111
|
|
|
2015
2112
|
const Example = () => {
|
|
2016
|
-
|
|
2113
|
+
const { isRawModeSupported } = useStdin();
|
|
2017
2114
|
|
|
2018
|
-
|
|
2115
|
+
return isRawModeSupported ? <MyInputComponent /> : <MyComponentThatDoesntUseInput />;
|
|
2019
2116
|
};
|
|
2020
2117
|
```
|
|
2021
2118
|
|
|
@@ -2116,14 +2213,14 @@ import { useRef } from "react";
|
|
|
2116
2213
|
import { Box, Text, useBoxMetrics } from "@alchemy.run/sigil";
|
|
2117
2214
|
|
|
2118
2215
|
const Example = () => {
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2216
|
+
const ref = useRef(null);
|
|
2217
|
+
const { width, height, left, top, hasMeasured } = useBoxMetrics(ref);
|
|
2218
|
+
|
|
2219
|
+
return (
|
|
2220
|
+
<Box ref={ref}>
|
|
2221
|
+
<Text>{hasMeasured ? `${width}x${height} at ${left},${top}` : "Measuring..."}</Text>
|
|
2222
|
+
</Box>
|
|
2223
|
+
);
|
|
2127
2224
|
};
|
|
2128
2225
|
```
|
|
2129
2226
|
|
|
@@ -2160,6 +2257,99 @@ Whether the currently tracked element has been measured.
|
|
|
2160
2257
|
> [!NOTE]
|
|
2161
2258
|
> The hook returns `{width: 0, height: 0, left: 0, top: 0}` until the first layout pass completes. It also returns zeros when the tracked ref is detached.
|
|
2162
2259
|
|
|
2260
|
+
### useVirtualScroll(options)
|
|
2261
|
+
|
|
2262
|
+
A React hook that owns the scroll position of a windowed list and returns which items to render for it. The position is clamped to the scrollable range and, while `focusedIndex` is set, moved as little as necessary to keep that item fully visible.
|
|
2263
|
+
|
|
2264
|
+
[`<VirtualList>`](#virtuallist) wraps this hook. Use it directly to draw your own chrome around the window, such as overflow markers or a scrollbar.
|
|
2265
|
+
|
|
2266
|
+
```jsx
|
|
2267
|
+
import { Box, Text, useVirtualScroll } from "@alchemy.run/sigil";
|
|
2268
|
+
|
|
2269
|
+
const Example = ({ lines, cursor }) => {
|
|
2270
|
+
const { start, end, offset, hiddenAbove, hiddenBelow } = useVirtualScroll({
|
|
2271
|
+
count: lines.length,
|
|
2272
|
+
itemHeight: () => 1,
|
|
2273
|
+
viewportHeight: 10,
|
|
2274
|
+
focusedIndex: cursor,
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
return (
|
|
2278
|
+
<Box flexDirection="column">
|
|
2279
|
+
<Text dimColor>{hiddenAbove > 0 ? `↑ ${hiddenAbove} more` : ""}</Text>
|
|
2280
|
+
<Box flexDirection="column" height={10} overflowY="hidden">
|
|
2281
|
+
<Box flexDirection="column" flexShrink={0} marginTop={offset}>
|
|
2282
|
+
{lines.slice(start, end).map((line, index) => (
|
|
2283
|
+
<Text key={start + index} inverse={start + index === cursor}>
|
|
2284
|
+
{line}
|
|
2285
|
+
</Text>
|
|
2286
|
+
))}
|
|
2287
|
+
</Box>
|
|
2288
|
+
</Box>
|
|
2289
|
+
<Text dimColor>{hiddenBelow > 0 ? `↓ ${hiddenBelow} more` : ""}</Text>
|
|
2290
|
+
</Box>
|
|
2291
|
+
);
|
|
2292
|
+
};
|
|
2293
|
+
```
|
|
2294
|
+
|
|
2295
|
+
#### options
|
|
2296
|
+
|
|
2297
|
+
##### count
|
|
2298
|
+
|
|
2299
|
+
Type: `number`
|
|
2300
|
+
|
|
2301
|
+
Number of items in the list.
|
|
2302
|
+
|
|
2303
|
+
##### itemHeight
|
|
2304
|
+
|
|
2305
|
+
Type: `(index: number) => number`
|
|
2306
|
+
|
|
2307
|
+
Height of the item at `index` in rows. It must match what the item renders.
|
|
2308
|
+
|
|
2309
|
+
##### viewportHeight
|
|
2310
|
+
|
|
2311
|
+
Type: `number`
|
|
2312
|
+
|
|
2313
|
+
Rows available to show items.
|
|
2314
|
+
|
|
2315
|
+
##### focusedIndex
|
|
2316
|
+
|
|
2317
|
+
Type: `number`
|
|
2318
|
+
|
|
2319
|
+
Item that must stay fully visible. An item taller than the viewport is aligned to the top. Out-of-range values are ignored.
|
|
2320
|
+
|
|
2321
|
+
#### Result
|
|
2322
|
+
|
|
2323
|
+
##### start, end
|
|
2324
|
+
|
|
2325
|
+
Type: `number`
|
|
2326
|
+
|
|
2327
|
+
The items intersecting the viewport are `[start, end)`.
|
|
2328
|
+
|
|
2329
|
+
##### offset
|
|
2330
|
+
|
|
2331
|
+
Type: `number`
|
|
2332
|
+
|
|
2333
|
+
Position of the `start` item relative to the top of the viewport. Zero or negative: a negative value means the item is partially scrolled out above. Apply it as `marginTop` on the box holding the rendered items.
|
|
2334
|
+
|
|
2335
|
+
##### scrollTop, maxScrollTop, totalHeight
|
|
2336
|
+
|
|
2337
|
+
Type: `number`
|
|
2338
|
+
|
|
2339
|
+
The effective position, the largest position that still fills the viewport, and the height of every item combined, all in rows.
|
|
2340
|
+
|
|
2341
|
+
##### hiddenAbove, hiddenBelow
|
|
2342
|
+
|
|
2343
|
+
Type: `number`
|
|
2344
|
+
|
|
2345
|
+
Rows scrolled out above the viewport and rows left below it.
|
|
2346
|
+
|
|
2347
|
+
##### scrollTo(top), scrollBy(delta)
|
|
2348
|
+
|
|
2349
|
+
Type: `(rows: number) => void`
|
|
2350
|
+
|
|
2351
|
+
Move the viewport. While `focusedIndex` is set, a position that would hide it is corrected on the next render.
|
|
2352
|
+
|
|
2163
2353
|
### useStderr()
|
|
2164
2354
|
|
|
2165
2355
|
A React hook that returns the stderr stream and stderr-related utilities.
|
|
@@ -2217,13 +2407,13 @@ A React hook that returns the current terminal dimensions and re-renders the com
|
|
|
2217
2407
|
import { Text, useWindowSize } from "@alchemy.run/sigil";
|
|
2218
2408
|
|
|
2219
2409
|
const Example = () => {
|
|
2220
|
-
|
|
2410
|
+
const { columns, rows } = useWindowSize();
|
|
2221
2411
|
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2412
|
+
return (
|
|
2413
|
+
<Text>
|
|
2414
|
+
{columns}x{rows}
|
|
2415
|
+
</Text>
|
|
2416
|
+
);
|
|
2227
2417
|
};
|
|
2228
2418
|
```
|
|
2229
2419
|
|
|
@@ -2240,7 +2430,7 @@ Type: `number`
|
|
|
2240
2430
|
Number of rows (vertical character cells).
|
|
2241
2431
|
|
|
2242
2432
|
> [!NOTE]
|
|
2243
|
-
>
|
|
2433
|
+
> On terminals that send in-band size reports (mode 2048, e.g. Ghostty, kitty, foot) the frame is repainted as soon as the report arrives, since the emulator has already rewrapped by then. Elsewhere the size comes from the PTY, which can run ahead of or behind the emulator, so resize events are coalesced: the frame is erased and repainted about 50 ms after the size stops changing and is briefly absent during a window drag. Painting mid-rewrap is what leaves ghost rows behind.
|
|
2244
2434
|
|
|
2245
2435
|
### useFocus(options?)
|
|
2246
2436
|
|
|
@@ -2277,9 +2467,9 @@ Set a component's focus ID, which can be used to programmatically focus the comp
|
|
|
2277
2467
|
import { render, useFocus, Text } from "@alchemy.run/sigil";
|
|
2278
2468
|
|
|
2279
2469
|
const Example = () => {
|
|
2280
|
-
|
|
2470
|
+
const { isFocused } = useFocus();
|
|
2281
2471
|
|
|
2282
|
-
|
|
2472
|
+
return <Text>{isFocused ? "I am focused" : "I am not focused"}</Text>;
|
|
2283
2473
|
};
|
|
2284
2474
|
|
|
2285
2475
|
render(<Example />);
|
|
@@ -2413,9 +2603,9 @@ The ID of the currently focused component, or `undefined` if no component is foc
|
|
|
2413
2603
|
import { Text, useFocusManager } from "@alchemy.run/sigil";
|
|
2414
2604
|
|
|
2415
2605
|
const Example = () => {
|
|
2416
|
-
|
|
2606
|
+
const { activeId } = useFocusManager();
|
|
2417
2607
|
|
|
2418
|
-
|
|
2608
|
+
return <Text>Focused: {activeId ?? "none"}</Text>;
|
|
2419
2609
|
};
|
|
2420
2610
|
```
|
|
2421
2611
|
|
|
@@ -2430,21 +2620,21 @@ import { Box, Text, useCursor } from "@alchemy.run/sigil";
|
|
|
2430
2620
|
import stringWidth from "string-width";
|
|
2431
2621
|
|
|
2432
2622
|
const TextInput = () => {
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2623
|
+
const [text, setText] = useState("");
|
|
2624
|
+
const { setCursorPosition } = useCursor();
|
|
2625
|
+
|
|
2626
|
+
const prompt = "> ";
|
|
2627
|
+
setCursorPosition({ x: stringWidth(prompt + text), y: 1 });
|
|
2628
|
+
|
|
2629
|
+
return (
|
|
2630
|
+
<Box flexDirection="column">
|
|
2631
|
+
<Text>Type here:</Text>
|
|
2632
|
+
<Text>
|
|
2633
|
+
{prompt}
|
|
2634
|
+
{text}
|
|
2635
|
+
</Text>
|
|
2636
|
+
</Box>
|
|
2637
|
+
);
|
|
2448
2638
|
};
|
|
2449
2639
|
```
|
|
2450
2640
|
|
|
@@ -2481,11 +2671,11 @@ This is useful when you want to render different output for screen readers.
|
|
|
2481
2671
|
import { useIsScreenReaderEnabled, Text } from "@alchemy.run/sigil";
|
|
2482
2672
|
|
|
2483
2673
|
const Example = () => {
|
|
2484
|
-
|
|
2674
|
+
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
2485
2675
|
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2676
|
+
return (
|
|
2677
|
+
<Text>{isScreenReaderEnabled ? "Screen reader is enabled" : "Screen reader is disabled"}</Text>
|
|
2678
|
+
);
|
|
2489
2679
|
};
|
|
2490
2680
|
```
|
|
2491
2681
|
|
|
@@ -2497,10 +2687,10 @@ A React hook that drives animations. Returns a frame counter, elapsed time, fram
|
|
|
2497
2687
|
import { Text, useAnimation } from "@alchemy.run/sigil";
|
|
2498
2688
|
|
|
2499
2689
|
const Spinner = () => {
|
|
2500
|
-
|
|
2501
|
-
|
|
2690
|
+
const { frame } = useAnimation({ interval: 80 });
|
|
2691
|
+
const characters = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2502
2692
|
|
|
2503
|
-
|
|
2693
|
+
return <Text>{characters[frame % characters.length]}</Text>;
|
|
2504
2694
|
};
|
|
2505
2695
|
```
|
|
2506
2696
|
|
|
@@ -2622,11 +2812,11 @@ Return `true` to take ownership of a chunk: Ink will not display it, letting the
|
|
|
2622
2812
|
const [transcript, setTranscript] = useState([]);
|
|
2623
2813
|
|
|
2624
2814
|
render(<App transcript={transcript} />, {
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2815
|
+
patchConsole: "stdio",
|
|
2816
|
+
onCapturedOutput(stream, data) {
|
|
2817
|
+
setTranscript((previous) => [...previous, { stream, data }]);
|
|
2818
|
+
return true;
|
|
2819
|
+
},
|
|
2630
2820
|
});
|
|
2631
2821
|
```
|
|
2632
2822
|
|
|
@@ -2642,7 +2832,7 @@ To run code after output is flushed, use [`waitUntilRenderFlush()`](#waituntilre
|
|
|
2642
2832
|
###### isScreenReaderEnabled
|
|
2643
2833
|
|
|
2644
2834
|
Type: `boolean`\
|
|
2645
|
-
Default: `process.env['
|
|
2835
|
+
Default: `process.env['SIGIL_SCREEN_READER'] === 'true'`
|
|
2646
2836
|
|
|
2647
2837
|
Enable screen reader support. See [Screen Reader Support](#screen-reader-support).
|
|
2648
2838
|
|
|
@@ -2663,14 +2853,6 @@ This controls how frequently the UI can update to prevent excessive re-rendering
|
|
|
2663
2853
|
Higher values allow more frequent updates but may impact performance.
|
|
2664
2854
|
Setting it to a lower value may be useful for components that update very frequently, to reduce CPU usage.
|
|
2665
2855
|
|
|
2666
|
-
###### incrementalRendering
|
|
2667
|
-
|
|
2668
|
-
Type: `boolean`\
|
|
2669
|
-
Default: `false`
|
|
2670
|
-
|
|
2671
|
-
Enable incremental rendering mode which only updates changed lines instead of redrawing the entire output.
|
|
2672
|
-
This can reduce flickering and improve performance for frequently updating UIs.
|
|
2673
|
-
|
|
2674
2856
|
###### concurrent
|
|
2675
2857
|
|
|
2676
2858
|
Type: `boolean`\
|
|
@@ -2748,10 +2930,10 @@ render(<MyApp />, { kittyKeyboard: { mode: "auto" } });
|
|
|
2748
2930
|
import { render } from "@alchemy.run/sigil";
|
|
2749
2931
|
|
|
2750
2932
|
render(<MyApp />, {
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2933
|
+
kittyKeyboard: {
|
|
2934
|
+
mode: "enabled",
|
|
2935
|
+
flags: ["disambiguateEscapeCodes", "reportEventTypes"],
|
|
2936
|
+
},
|
|
2755
2937
|
});
|
|
2756
2938
|
```
|
|
2757
2939
|
|
|
@@ -2803,9 +2985,9 @@ Useful for generating documentation, writing output to files, testing, or any sc
|
|
|
2803
2985
|
import { renderToString, Text, Box } from "@alchemy.run/sigil";
|
|
2804
2986
|
|
|
2805
2987
|
const output = renderToString(
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2988
|
+
<Box padding={1}>
|
|
2989
|
+
<Text color="green">Hello World</Text>
|
|
2990
|
+
</Box>,
|
|
2809
2991
|
);
|
|
2810
2992
|
|
|
2811
2993
|
console.log(output);
|
|
@@ -2836,7 +3018,7 @@ Width of the virtual terminal in columns. Controls where text wrapping occurs.
|
|
|
2836
3018
|
|
|
2837
3019
|
```jsx
|
|
2838
3020
|
const output = renderToString(<Text>{"A".repeat(100)}</Text>, {
|
|
2839
|
-
|
|
3021
|
+
columns: 40,
|
|
2840
3022
|
});
|
|
2841
3023
|
// Text wraps at 40 columns
|
|
2842
3024
|
```
|
|
@@ -2938,20 +3120,20 @@ See [Refs](https://reactjs.org/docs/refs-and-the-dom.html) for more information
|
|
|
2938
3120
|
import { render, measureElement, Box, Text } from "@alchemy.run/sigil";
|
|
2939
3121
|
|
|
2940
3122
|
const Example = () => {
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
3123
|
+
const ref = useRef();
|
|
3124
|
+
|
|
3125
|
+
useEffect(() => {
|
|
3126
|
+
const { x, y, width, height } = measureElement(ref.current);
|
|
3127
|
+
// x = 0, y = 0, width = 100, height = 1
|
|
3128
|
+
}, []);
|
|
3129
|
+
|
|
3130
|
+
return (
|
|
3131
|
+
<Box width={100}>
|
|
3132
|
+
<Box ref={ref}>
|
|
3133
|
+
<Text>This box will stretch to 100 width</Text>
|
|
3134
|
+
</Box>
|
|
3135
|
+
</Box>
|
|
3136
|
+
);
|
|
2955
3137
|
};
|
|
2956
3138
|
|
|
2957
3139
|
render(<Example />);
|
|
@@ -2979,10 +3161,10 @@ Check out [ink-testing-library](https://github.com/vadimdemedes/ink-testing-libr
|
|
|
2979
3161
|
|
|
2980
3162
|

|
|
2981
3163
|
|
|
2982
|
-
Ink supports [React Devtools](https://github.com/facebook/react/tree/master/packages/react-devtools) out of the box. To enable integration with React Devtools in your Ink-based CLI, first ensure you have installed the optional `react-devtools-core` dependency, and then run your app with the `
|
|
3164
|
+
Ink supports [React Devtools](https://github.com/facebook/react/tree/master/packages/react-devtools) out of the box. To enable integration with React Devtools in your Ink-based CLI, first ensure you have installed the optional `react-devtools-core` dependency, and then run your app with the `SIGIL_DEV=true` environment variable:
|
|
2983
3165
|
|
|
2984
3166
|
```sh
|
|
2985
|
-
|
|
3167
|
+
SIGIL_DEV=true my-cli
|
|
2986
3168
|
```
|
|
2987
3169
|
|
|
2988
3170
|
Then, start React Devtools itself:
|
|
@@ -3001,7 +3183,7 @@ You can even inspect and change the props of components, and see the results imm
|
|
|
3001
3183
|
|
|
3002
3184
|
Ink has basic support for screen readers.
|
|
3003
3185
|
|
|
3004
|
-
To enable it, you can either pass the `isScreenReaderEnabled` option to the `render` function or set the `
|
|
3186
|
+
To enable it, you can either pass the `isScreenReaderEnabled` option to the `render` function or set the `SIGIL_SCREEN_READER` environment variable to `true`.
|
|
3005
3187
|
|
|
3006
3188
|
Ink implements a small subset of functionality from the [ARIA specification](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA).
|
|
3007
3189
|
|
|
@@ -3015,7 +3197,7 @@ For example, for this code:
|
|
|
3015
3197
|
|
|
3016
3198
|
```jsx
|
|
3017
3199
|
<Box aria-role="checkbox" aria-state={{ checked: true }}>
|
|
3018
|
-
|
|
3200
|
+
<Text>Accept terms and conditions</Text>
|
|
3019
3201
|
</Box>
|
|
3020
3202
|
```
|
|
3021
3203
|
|
|
@@ -3031,8 +3213,8 @@ For example, if you are building a progress bar, you can use `aria-label` to pro
|
|
|
3031
3213
|
|
|
3032
3214
|
```jsx
|
|
3033
3215
|
<Box>
|
|
3034
|
-
|
|
3035
|
-
|
|
3216
|
+
<Box width="50%" height={1} backgroundColor="green" />
|
|
3217
|
+
<Text aria-label="Progress: 50%">50%</Text>
|
|
3036
3218
|
</Box>
|
|
3037
3219
|
```
|
|
3038
3220
|
|
|
@@ -3168,6 +3350,10 @@ npm run example examples/[example name]
|
|
|
3168
3350
|
- [Static](examples/static/static.tsx) - Use the `<Static>` component to render permanent output.
|
|
3169
3351
|
- [Child process](examples/subprocess-output) - Renders output from a child process.
|
|
3170
3352
|
- [Router](examples/router/router.tsx) - Navigate between routes using React Router's `MemoryRouter`.
|
|
3353
|
+
- [Charm brightness](examples/charm-brightness/brightness.tsx) - A native port of Lip Gloss's progressive lightening and darkening demo.
|
|
3354
|
+
- [Charm canvas](examples/charm-canvas/canvas.tsx) - Layered fields and gradient-bordered cards inspired by Lip Gloss's compositor demo.
|
|
3355
|
+
- [Charm layout](examples/charm-layout/layout.tsx) - The Lip Gloss layout showcase rebuilt with React, Yoga, gradients, and compositing.
|
|
3356
|
+
- [Charm profile](examples/charm-profile/profile.tsx) - The Lip Gloss SSH profile showcase adapted to Sigil's stream-scoped terminal capabilities.
|
|
3171
3357
|
|
|
3172
3358
|
## Continuous Integration
|
|
3173
3359
|
|