react-retro-display-tty-ansi-ascii 0.1.23

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joshua Bellamy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,662 @@
1
+ [![Feature Tour Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii.webp)](https://github.com/user-attachments/assets/c0ccd3ff-147b-4b8f-b214-47bddb2419d1)
2
+
3
+ # react-retro-display-tty-ansi-ascii
4
+
5
+ [![npm](https://img.shields.io/npm/v/react-retro-display-tty-ansi-ascii?label=npm)](https://www.npmjs.com/package/react-retro-display-tty-ansi-ascii)
6
+ [![tests](https://img.shields.io/endpoint?url=https%3A%2F%2Ftest-station.smysnk.com%2Fapi%2Fbadges%2Ftests.json%3FprojectKey%3Dreact-retro-display-tty-ansi-ascii)](https://test-station.smysnk.com/projects/react-retro-display-tty-ansi-ascii)
7
+ [![coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Ftest-station.smysnk.com%2Fapi%2Fbadges%2Fcoverage.json%3FprojectKey%3Dreact-retro-display-tty-ansi-ascii)](https://test-station.smysnk.com/projects/react-retro-display-tty-ansi-ascii)
8
+ [![health](https://img.shields.io/endpoint?url=https%3A%2F%2Ftest-station.smysnk.com%2Fapi%2Fbadges%2Fhealth.json%3FprojectKey%3Dreact-retro-display-tty-ansi-ascii)](https://test-station.smysnk.com/projects/react-retro-display-tty-ansi-ascii)
9
+
10
+ Storybook: [smysnk.github.io/react-retro-display-tty-ansi-ascii](https://smysnk.github.io/react-retro-display-tty-ansi-ascii/)
11
+
12
+ `react-retro-display-tty-ansi-ascii` is a React component for calm, terminal-flavored interfaces.
13
+ It can be a read-only display, a controlled editable surface, a controller-driven terminal,
14
+ or a small command prompt without changing visual language. It also understands ANSI styling,
15
+ semantic display color modes, and an xterm-checked terminal behavior surface for real control
16
+ character playback. It can also project itself onto either dark or light LCD glass without
17
+ asking the whole app shell to follow.
18
+
19
+ Latest test report: [test-station.smysnk.com/projects/react-retro-display-tty-ansi-ascii](https://test-station.smysnk.com/projects/react-retro-display-tty-ansi-ascii)
20
+
21
+ ## Getting Started
22
+
23
+ Install the package, bring in the shared stylesheet, and start with the simplest thing:
24
+
25
+ ```bash
26
+ npm install react-retro-display-tty-ansi-ascii
27
+ ```
28
+
29
+ ```tsx
30
+ import { RetroScreen } from "react-retro-display-tty-ansi-ascii";
31
+ import "react-retro-display-tty-ansi-ascii/styles.css";
32
+
33
+ export function StatusCard() {
34
+ return (
35
+ <RetroScreen
36
+ mode="value"
37
+ value="SYSTEM READY"
38
+ color="#97ff9b"
39
+ />
40
+ );
41
+ }
42
+ ```
43
+
44
+ That is the whole entry point.
45
+ You hand the component a mode, a value or controller when needed, and let it handle the grid,
46
+ wrapping, cursor rendering, and terminal feel.
47
+
48
+ ## Display Padding
49
+
50
+ Use `displayPadding` when the screen content should sit tighter to the glass or breathe a little
51
+ more. The prop accepts:
52
+
53
+ - a number for uniform pixel padding
54
+ - a CSS length string for uniform padding
55
+ - an object with `block` and `inline`
56
+ - an object with per-side `top`, `right`, `bottom`, and `left`
57
+
58
+ ```tsx
59
+ <RetroScreen mode="value" value="Tight framing" displayPadding={8} />
60
+
61
+ <RetroScreen mode="value" value="Room to breathe" displayPadding="1.25rem" />
62
+
63
+ <RetroScreen
64
+ mode="terminal"
65
+ displayPadding={{ block: 10, inline: 14 }}
66
+ value="measured from the padded screen area"
67
+ />
68
+
69
+ <RetroScreen
70
+ mode="prompt"
71
+ displayPadding={{ top: 6, right: 10, bottom: 12, left: 10 }}
72
+ />
73
+ ```
74
+
75
+ RetroScreen also shows a focus glow around the shell by default so editable, prompt, and terminal
76
+ surfaces clearly read as active. Disable it when you want a quieter shell:
77
+
78
+ ```tsx
79
+ <RetroScreen mode="terminal" focusGlow={false} />
80
+ ```
81
+
82
+ Because rows and columns are measured from the visible screen area, tighter padding yields a
83
+ denser grid and looser padding yields fewer cells.
84
+
85
+ ## Resizable Panels
86
+
87
+ Use `resizable` when the panel itself should be draggable instead of only responding to layout
88
+ changes around it. The live Storybook demo now shows a visible mouse cursor grabbing the real
89
+ handles, including the optional leading-edge handles, so the docs match the shipped interaction.
90
+
91
+ [![Resizable Panel Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-resizable-panel.webp)](https://github.com/user-attachments/assets/227b03f2-23a1-43eb-91b1-e326988313da)
92
+
93
+ ```tsx
94
+ <RetroScreen
95
+ mode="terminal"
96
+ resizable="both"
97
+ resizableLeadingEdges
98
+ displayPadding={{ block: 12, inline: 14 }}
99
+ value={[
100
+ "All resize handles are live here.",
101
+ "",
102
+ "Drag left, right, top, bottom, or either corner.",
103
+ "Rows and columns recompute as the panel changes size."
104
+ ].join("\n")}
105
+ />
106
+ ```
107
+
108
+ Reach for:
109
+
110
+ - `resizable="width"` when the panel should only stretch sideways
111
+ - `resizable="height"` when it should stack or collapse vertically
112
+ - `resizable` or `resizable="both"` for freeform terminal panes
113
+ - `resizableLeadingEdges` when left, top, and top-left handles should join the same interaction surface
114
+
115
+ ## Light And Dark Surface Modes
116
+
117
+ Use `displaySurfaceMode` when the LCD itself should read like a light instrument panel or a
118
+ dark night-ops surface. This is separate from the host page theme, so the same ANSI-rich
119
+ terminal content can sit inside bright docs, dark dashboards, or a side-by-side comparison view.
120
+
121
+ [![Light And Dark Hosts Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-light-dark-hosts.webp)](https://github.com/user-attachments/assets/fb758db8-d014-46b3-96fc-cc649bf0c475)
122
+
123
+ ```tsx
124
+ <RetroScreen
125
+ mode="terminal"
126
+ value={[
127
+ "\u001b[1mLIGHT SURFACE\u001b[0m",
128
+ "\u001b[38;5;160mR\u001b[38;5;214mA\u001b[38;5;190mI\u001b[38;5;45mN\u001b[38;5;39mB\u001b[38;5;141mO\u001b[38;5;201mW\u001b[0m contrast check",
129
+ "\u001b[38;2;194;94;0mamber\u001b[0m \u001b[38;2;0;104;181mblue\u001b[0m \u001b[38;2;108;40;148mviolet\u001b[0m"
130
+ ].join("\n")}
131
+ displaySurfaceMode="light"
132
+ displayColorMode="ansi-extended"
133
+ displayPadding={{ block: 12, inline: 14 }}
134
+ />
135
+
136
+ <RetroScreen
137
+ mode="terminal"
138
+ value={[
139
+ "\u001b[1mDARK SURFACE\u001b[0m",
140
+ "\u001b[38;5;160mR\u001b[38;5;214mA\u001b[38;5;190mI\u001b[38;5;45mN\u001b[38;5;39mB\u001b[38;5;141mO\u001b[38;5;201mW\u001b[0m contrast check",
141
+ "\u001b[38;2;255;176;86mamber\u001b[0m \u001b[38;2;102;198;255mblue\u001b[0m \u001b[38;2;214;145;255mviolet\u001b[0m"
142
+ ].join("\n")}
143
+ displaySurfaceMode="dark"
144
+ displayColorMode="ansi-extended"
145
+ displayPadding={{ block: 12, inline: 14 }}
146
+ />
147
+ ```
148
+
149
+ Reach for `displaySurfaceMode="light"` when the LCD should feel like paper, enamel, or a sunlit
150
+ instrument panel. Keep `displaySurfaceMode="dark"` for the classic terminal-glass look. The
151
+ same ANSI palette will still be remapped for readable contrast against each surface.
152
+
153
+ ## Modes Of Use
154
+
155
+ ### 1. Quiet output
156
+
157
+ Use `mode="value"` when the display is just there to speak.
158
+
159
+ [![Quiet Output Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-quiet-output.webp)](https://github.com/user-attachments/assets/df971b76-3de6-4686-833a-8fad19a66832)
160
+
161
+ ```tsx
162
+ <RetroScreen
163
+ mode="value"
164
+ value="LINK STABLE\nAwaiting operator input."
165
+ />
166
+ ```
167
+
168
+ ### 2. Signal intercept
169
+
170
+ Use a controller when the display should reveal text over time and the cadence matters as much as the message.
171
+
172
+ [![White Rabbit Signal Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-white-rabbit-signal.webp)](https://github.com/user-attachments/assets/f05998aa-40a0-4c51-9612-025b5e5f5b29)
173
+
174
+ ```tsx
175
+ import { useEffect } from "react";
176
+ import {
177
+ RetroScreen,
178
+ createRetroScreenController
179
+ } from "react-retro-display-tty-ansi-ascii";
180
+
181
+ const controller = createRetroScreenController({
182
+ rows: 5,
183
+ cols: 34,
184
+ cursorMode: "solid"
185
+ });
186
+
187
+ export function WhiteRabbitSignal() {
188
+ useEffect(() => {
189
+ controller.reset();
190
+ controller.write("Wake up, Neo...");
191
+ }, []);
192
+
193
+ return <RetroScreen mode="terminal" controller={controller} />;
194
+ }
195
+ ```
196
+
197
+ The Storybook version uses timed writes and screen clears so the four-line sequence lands like a late-night intercepted signal instead of a static quote.
198
+
199
+ ### 3. Matrix code rain
200
+
201
+ Use the terminal renderer as a dense animated display surface when you want the operators' green rain instead of a message prompt.
202
+
203
+ [![Matrix Code Rain Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-matrix-code-rain.webp)](https://github.com/user-attachments/assets/df2c7fdc-cd27-4296-8f70-1373fdfb63aa)
204
+
205
+ ```tsx
206
+ import type { CSSProperties } from "react";
207
+ import { RetroScreen, createRetroScreenController } from "react-retro-display-tty-ansi-ascii";
208
+
209
+ const controller = createRetroScreenController({ rows: 24, cols: 58 });
210
+ const matrixFontStyle = {
211
+ "--retro-screen-font-family": "\"Matrix\""
212
+ } as CSSProperties;
213
+
214
+ <RetroScreen
215
+ mode="terminal"
216
+ controller={controller}
217
+ gridMode="static"
218
+ rows={24}
219
+ cols={58}
220
+ displayColorMode="ansi-extended"
221
+ color="#8efe8e"
222
+ displayFontScale={1.05}
223
+ displayRowScale={1.08}
224
+ displayPadding={{ block: 12, inline: 14 }}
225
+ style={matrixFontStyle}
226
+ />
227
+ ```
228
+
229
+ The Storybook demo keeps the glyphs planted on the grid and moves waves of illumination through them so the effect feels closer to the movie than a simple falling-text loop. It uses the bundled `Matrix` font.
230
+
231
+ ### 4. Editable drafting
232
+
233
+ Turn on `editable` when you want the same surface to behave like a controlled input.
234
+
235
+ [![Editable Drafting Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-editable-drafting.webp)](https://github.com/user-attachments/assets/5670d23a-c794-46a0-bb40-784f04a2da14)
236
+
237
+ ```tsx
238
+ import { useState } from "react";
239
+
240
+ export function DraftPad() {
241
+ const [value, setValue] = useState("");
242
+
243
+ return (
244
+ <RetroScreen
245
+ mode="value"
246
+ value={value}
247
+ editable
248
+ autoFocus
249
+ placeholder="Write a line, then press Enter."
250
+ onChange={setValue}
251
+ onSubmit={(submitted) => {
252
+ console.log("submitted:", submitted);
253
+ }}
254
+ />
255
+ );
256
+ }
257
+ ```
258
+
259
+ ### 5. Terminal output from a controller
260
+
261
+ Use a controller when the display should follow external writes over time.
262
+
263
+ [![Terminal Output Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-terminal-output.webp)](https://github.com/user-attachments/assets/57258c51-5afd-4164-87dd-d30b85726a26)
264
+
265
+ ```tsx
266
+ import { useEffect } from "react";
267
+ import {
268
+ RetroScreen,
269
+ createRetroScreenController
270
+ } from "react-retro-display-tty-ansi-ascii";
271
+
272
+ const controller = createRetroScreenController({
273
+ rows: 9,
274
+ cols: 46,
275
+ cursorMode: "hollow"
276
+ });
277
+
278
+ export function StreamedTerminal() {
279
+ useEffect(() => {
280
+ controller.reset();
281
+ controller.writeln("BOOT react-retro-display-tty-ansi-ascii");
282
+ controller.write("\u001b[1mREADY\u001b[0m ansi parser online");
283
+ }, []);
284
+
285
+ return <RetroScreen mode="terminal" controller={controller} />;
286
+ }
287
+ ```
288
+
289
+ If you already have a terminal-like buffer as a string, `mode="terminal"` also accepts `value`
290
+ or `initialBuffer`.
291
+
292
+ ## Live TTY Websocket Bridge
293
+
294
+ `RetroScreen` can also act as the browser-side surface for a real TTY session. The transport
295
+ stays outside the component, while the component handles geometry, keyboard capture, paste,
296
+ focus reporting, mouse reporting, alternate-screen rendering, title updates, and bell metadata.
297
+
298
+ [![Live Tty Terminal Bridge Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-live-tty-terminal-bridge.webp)](https://github.com/user-attachments/assets/8e525d78-ddf1-4604-bdcc-d05f6d817489)
299
+
300
+ The demo sequence is recorded from a live shell session and stages the kind of workload this
301
+ bridge is built for: a live-updating `top` session, a fullscreen `vim` pass, and a `nano`
302
+ screen with help bars and cursor-owned chrome.
303
+
304
+ ```tsx
305
+ import {
306
+ RetroScreen,
307
+ createRetroScreenWebSocketSession
308
+ } from "react-retro-display-tty-ansi-ascii";
309
+
310
+ const session = createRetroScreenWebSocketSession({
311
+ url: "ws://127.0.0.1:8787",
312
+ openPayload: {
313
+ cwd: "/workspace",
314
+ term: "xterm-256color"
315
+ }
316
+ });
317
+
318
+ export function LiveShell() {
319
+ return (
320
+ <RetroScreen
321
+ mode="terminal"
322
+ session={session}
323
+ autoFocus
324
+ displayColorMode="ansi-extended"
325
+ displayPadding={{ block: 12, inline: 14 }}
326
+ />
327
+ );
328
+ }
329
+ ```
330
+
331
+ For local development, the repo includes a reference `node-pty` websocket backend:
332
+
333
+ ```bash
334
+ yarn tty:server
335
+ ```
336
+
337
+ By default, that example server starts a themed demo shell rooted at `~/tty-demo` with the
338
+ prompt `operator@retro:~/tty-demo$`, so the live story and the recorded bridge demo share the
339
+ same shell framing.
340
+
341
+ There is also a dedicated Storybook story for this path. It now defaults to the local example
342
+ server at `ws://127.0.0.1:8787`, so if `yarn tty:server` is already running you can open the
343
+ `Live Tty Terminal Bridge` story directly without adding any extra query params.
344
+
345
+ If you want to override the target or the open payload, use:
346
+
347
+ ```js
348
+ window.__RETRO_SCREEN_TTY_DEMO__ = {
349
+ url: "ws://127.0.0.1:8787",
350
+ openPayload: {
351
+ cwd: "/Users/josh/play/react-retro-display",
352
+ term: "xterm-256color"
353
+ }
354
+ };
355
+ ```
356
+
357
+ The example server now supports token checks, origin checks, idle timeouts, payload-size limits,
358
+ and optional command/cwd/env override restrictions. See
359
+ [examples/node-tty-websocket-server/README.md](/Users/josh/play/react-retro-display/examples/node-tty-websocket-server/README.md)
360
+ for the available flags.
361
+
362
+ ### 6. Prompt-first interaction
363
+
364
+ Use `mode="prompt"` when the interface should feel like a guided shell.
365
+
366
+ [![Prompt Interaction Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-prompt-loop.webp)](https://github.com/user-attachments/assets/75eec8ea-6da7-41f4-98be-f17e5284980a)
367
+
368
+ ```tsx
369
+ <RetroScreen
370
+ mode="prompt"
371
+ autoFocus
372
+ promptChar="$"
373
+ acceptanceText="READY"
374
+ rejectionText="DENIED"
375
+ onCommand={async (command) => {
376
+ if (command === "status") {
377
+ return {
378
+ accepted: true,
379
+ response: ["grid synced", "cursor stable"]
380
+ };
381
+ }
382
+
383
+ return {
384
+ accepted: false,
385
+ response: "unknown command"
386
+ };
387
+ }}
388
+ />
389
+ ```
390
+
391
+ ## Display Buffer And Follow Mode
392
+
393
+ Terminal and prompt surfaces now expose a real display buffer instead of only showing the live
394
+ viewport. That means you can scroll back through recent output, inspect older lines, then return
395
+ to the live tail when you are ready to follow the stream again.
396
+
397
+ Built-in behavior:
398
+
399
+ - `PageUp` and `PageDown` move through the display buffer
400
+ - mouse wheel scrolling moves through the same history
401
+ - `End` returns terminal mode to the live tail
402
+ - auto-follow starts enabled, turns off when you scroll back, and turns back on when you return to the bottom
403
+
404
+ Use `bufferSize` to control how many rows of history the component-managed terminal or prompt
405
+ surface keeps, and `defaultAutoFollow` if you want the view to start detached from the tail.
406
+
407
+ ```tsx
408
+ <RetroScreen
409
+ mode="terminal"
410
+ bufferSize={400}
411
+ defaultAutoFollow
412
+ value={[
413
+ "line-01 warm boot",
414
+ "line-02 telemetry stable",
415
+ "line-03 waiting for operator"
416
+ ].join("\n")}
417
+ />
418
+ ```
419
+
420
+ If you are driving the component with your own controller, configure the underlying buffer size on
421
+ the controller itself:
422
+
423
+ ```tsx
424
+ const controller = createRetroScreenController({
425
+ rows: 9,
426
+ cols: 46,
427
+ scrollback: 400
428
+ });
429
+
430
+ <RetroScreen mode="terminal" controller={controller} />
431
+ ```
432
+
433
+ The browser suite now covers this path directly, including paging, wheel scrolling, anchored
434
+ scrollback while new lines arrive, and auto-follow recovery back to the live tail.
435
+
436
+ ## Auto Resize And Geometry Probing
437
+
438
+ When rows and columns matter to the program inside the display, listen to `onGeometryChange`,
439
+ turn that measurement into a terminal-style reply, and redraw from the reported size. The demo
440
+ below simulates a terminal app issuing `CSI 18 t`, receiving `CSI 8;<rows>;<cols>t`, then
441
+ repainting a full border and centered dimensions every time the panel resizes. The current demo
442
+ shows a visible cursor dragging the real resize handles, pauses if you intervene manually, and
443
+ still cycles through tight screen padding, multiple border alphabets, oversized glyph styles,
444
+ plus every monochrome and ANSI display mode so the same terminal program can be watched under
445
+ different visual projections.
446
+
447
+ [![Auto Resize Probe Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-auto-resize-probe.webp)](https://github.com/user-attachments/assets/2dfd68d7-284c-4895-ad5f-fb513cb09c80)
448
+
449
+ ```tsx
450
+ import {
451
+ RetroScreen,
452
+ createRetroScreenController
453
+ } from "react-retro-display-tty-ansi-ascii";
454
+
455
+ const controller = createRetroScreenController({
456
+ rows: 9,
457
+ cols: 34,
458
+ cursorMode: "solid"
459
+ });
460
+
461
+ export function ResizingTerminalProbe() {
462
+ return (
463
+ <RetroScreen
464
+ mode="terminal"
465
+ controller={controller}
466
+ displayPadding={{ block: 8, inline: 10 }}
467
+ onGeometryChange={(geometry) => {
468
+ const nextReply = `\u001b[8;${geometry.rows};${geometry.cols}t`;
469
+
470
+ console.log("terminal reply:", nextReply);
471
+ controller.reset();
472
+ controller.resize(geometry.rows, geometry.cols);
473
+ redrawBorderAndMetrics(controller, geometry.rows, geometry.cols);
474
+ }}
475
+ />
476
+ );
477
+ }
478
+ ```
479
+
480
+ This is useful for terminal-style dashboards, resize-aware prompts, or retro UIs that need to
481
+ center content, draw frames, or adapt layouts from the actual LCD grid instead of from CSS alone.
482
+ It is also a good place to project `displayColorMode` changes when you want the terminal behavior
483
+ to stay fixed while the display mood shifts around it.
484
+
485
+ ## Terminal Color Modes
486
+
487
+ Use `displayColorMode` to decide how semantic terminal color should be projected onto the screen.
488
+ The phosphor modes keep the retro LCD personality even when the source emits ANSI color. The ANSI
489
+ modes preserve more of the source terminal palette.
490
+
491
+ [![Display Color Modes Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-display-color-modes.webp)](https://github.com/user-attachments/assets/3536a8f4-fe5d-401d-af79-1f2e707f2ce5)
492
+
493
+ Available modes:
494
+
495
+ - `phosphor-green`
496
+ - `phosphor-amber`
497
+ - `phosphor-ice`
498
+ - `ansi-classic`
499
+ - `ansi-extended`
500
+
501
+ ```tsx
502
+ <RetroScreen
503
+ mode="terminal"
504
+ displayColorMode="ansi-extended"
505
+ value={[
506
+ "\u001b[31mALERT\u001b[0m \u001b[32mlink stable\u001b[0m",
507
+ "\u001b[38;5;196mindexed 196\u001b[0m from the 256-color palette",
508
+ "\u001b[38;2;255;180;120mtruecolor 255,180,120\u001b[0m"
509
+ ].join("\n")}
510
+ />
511
+ ```
512
+
513
+ Reach for `ansi-classic` when you want the familiar 16-color terminal profile, or
514
+ `ansi-extended` when 256-color and truecolor cells should survive all the way to the display.
515
+
516
+ ## ANSI Art Playback
517
+
518
+ Storybook now includes a dedicated `Bad Apple ANSI` demo that loads the real ANSI release,
519
+ decodes the original IBM VGA / CP437 bytes outside the display component, and then feeds those
520
+ bytes into the reusable `RetroScreenAnsiPlayer` wrapper. The player incrementally materializes
521
+ stabilized full-screen `80 x 25` snapshots while the parent owns byte loading and streaming. The
522
+ demo uses the full `BADAPPLE.ANS` payload, not a trimmed excerpt, and tightens the glyph scale so
523
+ the character rows visually sit flush instead of leaving air between scanlines.
524
+
525
+ [![Bad Apple ANSI Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-bad-apple-ansi.webp)](https://github.com/user-attachments/assets/82d505be-5296-4139-ab64-83aae59804ad)
526
+
527
+ The README clip is a 30-second capture of the real ANSI-art playback path, not a separate video renderer.
528
+
529
+ Credit for the original ANSI release goes to [Mistigris](https://mistigris.org/).
530
+
531
+ Open it here:
532
+ [smysnk.github.io/react-retro-display-tty-ansi-ascii/?path=/story/retroscreen-display-buffer--bad-apple-ansi](https://smysnk.github.io/react-retro-display-tty-ansi-ascii/?path=/story/retroscreen-display-buffer--bad-apple-ansi)
533
+
534
+ The Storybook demo is backed by the bundled asset at
535
+ [src/stories/assets/bad-apple.ans](/Users/josh/play/react-retro-display/src/stories/assets/bad-apple.ans).
536
+
537
+ The key wiring for this kind of ANSI-art playback is:
538
+
539
+ ```tsx
540
+ <RetroScreenAnsiPlayer
541
+ byteStream={asset.byteStream}
542
+ rows={25}
543
+ cols={80}
544
+ frameDelayMs={asset.frameDelayMs}
545
+ complete
546
+ loop
547
+ displayColorMode="ansi-classic"
548
+ displayPadding={{ block: 8, inline: 12 }}
549
+ displayFontScale={1}
550
+ displayRowScale={2}
551
+ style={{ width: "1010px", height: "642px" }}
552
+ />
553
+ ```
554
+
555
+ Use `RetroScreenAnsiPlayer` when a parent is responsible for supplying ANSI bytes or byte chunks,
556
+ including incremental streams. Keep the asset loading outside the display component, pass the
557
+ native `rows` and `cols` so the art is not reflowed, and use `displayFontScale` plus
558
+ `displayRowScale` to tune how the art occupies the grid. `BADAPPLE.ANS` uses lots of upper-half
559
+ and lower-half block characters (`▀` / `▄`), so the demo keeps the font scale neutral and doubles
560
+ the row scale to visually fuse those half-block rows into a continuous image instead of separated
561
+ text lines. For the Bad Apple panel, the demo also uses a container
562
+ size that lands on exact `12x24` cell geometry after padding and bezel chrome, which avoids
563
+ fractional row heights and helps eliminate subpixel seams.
564
+
565
+ ## Control-Character Playback
566
+
567
+ The terminal path is now tested against an xterm oracle and can faithfully replay real control
568
+ character effects like carriage return rewrites, erase-in-line, scroll regions, insert-line
569
+ updates, ANSI 16-color, indexed 256-color, and truecolor output.
570
+
571
+ [![Control Character Replay Demo](https://raw.githubusercontent.com/smysnk/react-retro-display-tty-ansi-ascii/main/docs/assets/react-retro-display-tty-ansi-ascii-control-character-replay.webp)](https://github.com/user-attachments/assets/682d8e92-7871-420e-9e6d-a6a92b60c0fe)
572
+
573
+ ```tsx
574
+ import {
575
+ RetroScreen,
576
+ createRetroScreenController
577
+ } from "react-retro-display-tty-ansi-ascii";
578
+
579
+ const controller = createRetroScreenController({ rows: 6, cols: 34 });
580
+
581
+ controller.write("Downloading fixtures... 12%");
582
+ controller.write("\rDownloading fixtures... 73%");
583
+ controller.write("\r\u001b[32mDownloaded fixtures.\u001b[0m\u001b[K\r\n");
584
+ controller.write("\u001b[2;6r");
585
+ controller.write(
586
+ "\u001b[6;1H\u001b[L\u001b[38;2;255;180;120mrecorded regression fixture\u001b[0m"
587
+ );
588
+
589
+ <RetroScreen
590
+ mode="terminal"
591
+ controller={controller}
592
+ displayColorMode="ansi-extended"
593
+ />
594
+ ```
595
+
596
+ The same trace fixtures used in Storybook are also exercised in the terminal verification layers:
597
+
598
+ ```bash
599
+ yarn test:conformance
600
+ yarn test:tty
601
+ yarn test:e2e:tty
602
+ yarn test:e2e
603
+ ```
604
+
605
+ The TTY-specific checks skip themselves automatically in environments where `node-pty` cannot
606
+ allocate a TTY session, but they run normally on TTY-capable developer machines and CI runners.
607
+
608
+ ## Ease Of Integration
609
+
610
+ The component is intentionally small at the edge:
611
+
612
+ - Start with `mode="value"` when all you need is a beautiful terminal-like readout.
613
+ - Add `editable` if the content should be controlled by React state.
614
+ - Switch to `mode="terminal"` when output is driven by a stream or controller.
615
+ - Switch to `mode="prompt"` when commands and responses should live in one transcript.
616
+ - Listen to `onGeometryChange` if rows and columns matter to the rest of your app.
617
+
618
+ ## Storybook
619
+
620
+ Storybook now acts as the living demo surface for the package.
621
+ It includes stories for the main user journeys:
622
+
623
+ - read-only display
624
+ - editable drafting
625
+ - controller-fed terminal output
626
+ - display buffer paging and follow mode
627
+ - resizable live panels with scripted handle demos
628
+ - auto-resize geometry probing
629
+ - live TTY bridge wiring
630
+ - ANSI art playback
631
+ - ANSI styling
632
+ - display color mode projection
633
+ - light and dark surface modes
634
+ - control-character replay fixtures
635
+ - prompt interaction
636
+ - responsive geometry
637
+ - a capture-ready feature tour
638
+
639
+ Run it locally with:
640
+
641
+ ```bash
642
+ npm install
643
+ npm run storybook
644
+ ```
645
+
646
+ ## Development
647
+
648
+ ```bash
649
+ npm install
650
+ npm run build
651
+ npm run test
652
+ npm run test:unit
653
+ npm run storybook
654
+ ```
655
+
656
+ Useful extra checks:
657
+
658
+ ```bash
659
+ yarn test:tty
660
+ yarn test:e2e:tty
661
+ yarn perf:terminal
662
+ ```