Skip to content

Commit 97c98e9

Browse files
committed
examples use node: api
1 parent 4139212 commit 97c98e9

4 files changed

Lines changed: 107 additions & 33 deletions

File tree

examples/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ with information about your terminal, shell, operating system and any other
66
information that could be pertinent to reproducing the issue.
77

88
> [!NOTE]
9-
> Run the commands in this document from the repository root.
9+
> Run the commands in this document from the repository root. These examples use
10+
> `node:` terminal APIs so the same files can be run with either Deno or Node.
1011
1112
## Prerequisites
1213

@@ -24,6 +25,8 @@ Run it with:
2425

2526
```sh
2627
deno run examples/keyboard/index.ts
28+
# or
29+
node examples/keyboard/index.ts
2730
```
2831

2932
What it shows:
@@ -48,6 +51,8 @@ Run it with:
4851

4952
```sh
5053
deno run examples/inline-regions/index.ts
54+
# or
55+
node examples/inline-regions/index.ts
5156
```
5257

5358
What it shows:

examples/inline-regions/index.ts

Lines changed: 39 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
* 4. Commit — restore cursor past region, advance with \n
99
*/
1010

11-
import { main, type Operation, sleep, until } from "effection";
11+
import { Buffer } from "node:buffer";
12+
import { readSync } from "node:fs";
13+
import process from "node:process";
14+
import { ensure, main, type Operation, sleep, until } from "effection";
1215
import {
1316
close,
1417
createInput,
@@ -28,8 +31,23 @@ import {
2831
import { cursor, settings } from "../../settings.ts";
2932
import { validated } from "../../validate.ts";
3033

34+
function terminalSize(): { columns: number; rows: number } {
35+
return process.stdout.isTTY
36+
? {
37+
columns: process.stdout.columns ?? 80,
38+
rows: process.stdout.rows ?? 24,
39+
}
40+
: { columns: 80, rows: 24 };
41+
}
42+
43+
function setRawMode(enabled: boolean): void {
44+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
45+
process.stdin.setRawMode(enabled);
46+
}
47+
}
48+
3149
const encode = (s: string) => new TextEncoder().encode(s);
32-
const write = (b: Uint8Array) => Deno.stdout.writeSync(b);
50+
const write = (b: Uint8Array) => process.stdout.write(Buffer.from(b));
3351

3452
const GREEN = rgba(80, 250, 123);
3553
const GRAY = rgba(100, 100, 100);
@@ -49,10 +67,10 @@ function* queryCursor(): Operation<CursorEvent> {
4967
let parser = yield* until(createInput({ escLatency: 100 }));
5068
write(DSR());
5169

52-
let buf = new Uint8Array(32);
70+
let buf = Buffer.allocUnsafe(32);
5371
while (true) {
54-
let n = Deno.stdin.readSync(buf);
55-
if (n === null) continue;
72+
let n = readSync(process.stdin.fd, buf, 0, buf.length, null);
73+
if (n === 0) continue;
5674
let result = parser.scan(buf.subarray(0, n));
5775
for (let ev of result.events) {
5876
if (ev.type === "cursor") {
@@ -62,16 +80,16 @@ function* queryCursor(): Operation<CursorEvent> {
6280
}
6381
}
6482

65-
function waitKey() {
66-
let buf = new Uint8Array(32);
83+
function waitKey(): void {
84+
let buf = Buffer.allocUnsafe(32);
6785
while (true) {
68-
let n = Deno.stdin.readSync(buf);
69-
if (n === null) continue;
86+
let n = readSync(process.stdin.fd, buf, 0, buf.length, null);
87+
if (n === 0) continue;
7088
for (let i = 0; i < n; i++) {
7189
if (buf[i] === 0x03) {
72-
Deno.stdin.setRaw(false);
90+
setRawMode(false);
7391
write(SHOWCURSOR());
74-
Deno.exit(0);
92+
process.exit(0);
7593
}
7694
}
7795
return;
@@ -112,7 +130,7 @@ function* transaction(
112130
frames: number,
113131
interval: number,
114132
): Operation<void> {
115-
let { columns } = Deno.consoleSize();
133+
let { columns } = terminalSize();
116134

117135
write(encode("\n".repeat(height)));
118136

@@ -142,17 +160,23 @@ function say(msg: string) {
142160
write(encode(msg + "\n"));
143161
}
144162

145-
function pause() {
163+
function pause(): void {
146164
waitKey();
147165
write(encode("\n"));
148166
}
149167

150168
await main(function* () {
151-
let { columns } = Deno.consoleSize();
152-
Deno.stdin.setRaw(true);
169+
let { columns } = terminalSize();
170+
setRawMode(true);
153171
let tty = settings(cursor(false));
154172
write(tty.apply);
155173

174+
yield* ensure(() => {
175+
setRawMode(false);
176+
write(CSI("0m"));
177+
write(tty.revert);
178+
});
179+
156180
// Introduction
157181
say("Clayterm can render entire scenes, but it can also render");
158182
say('"inline" for a streaming UI. This is useful for semi-interactive');
@@ -338,6 +362,4 @@ await main(function* () {
338362

339363
write(CSI("0m"));
340364
write(encode("\n"));
341-
write(tty.revert);
342-
Deno.stdin.setRaw(false);
343365
});

examples/keyboard/index.ts

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// deno-lint-ignore-file no-fallthrough
2+
import { Buffer } from "node:buffer";
3+
import process from "node:process";
24
import {
35
createChannel,
46
each,
@@ -33,6 +35,54 @@ import {
3335
import { useInput } from "./use-input.ts";
3436
import { useStdin } from "./use-stdin.ts";
3537

38+
function terminalSize(): { columns: number; rows: number } {
39+
return process.stdout.isTTY
40+
? {
41+
columns: process.stdout.columns ?? 80,
42+
rows: process.stdout.rows ?? 24,
43+
}
44+
: { columns: 80, rows: 24 };
45+
}
46+
47+
function setRawMode(enabled: boolean): void {
48+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") {
49+
process.stdin.setRawMode(enabled);
50+
}
51+
}
52+
53+
function writeStdout(bytes: Uint8Array): void {
54+
process.stdout.write(Buffer.from(bytes));
55+
}
56+
57+
function equalBytes(a: Uint8Array, b: Uint8Array): boolean {
58+
if (a.length !== b.length) {
59+
return false;
60+
}
61+
for (let i = 0; i < a.length; i++) {
62+
if (a[i] !== b[i]) {
63+
return false;
64+
}
65+
}
66+
return true;
67+
}
68+
69+
function equalSetting(a: Setting, b: Setting): boolean {
70+
return equalBytes(a.apply, b.apply) && equalBytes(a.revert, b.revert);
71+
}
72+
73+
// Avoid rewriting terminal input modes on every mousemove. Deno's `node:` TTY
74+
// compatibility layer on Windows is sensitive to that churn even when the
75+
// effective settings are unchanged.
76+
function updateFlagsIfChanged(current: Setting, next: Setting): Setting {
77+
if (equalSetting(current, next)) {
78+
return current;
79+
}
80+
81+
writeStdout(current.revert);
82+
writeStdout(next.apply);
83+
return next;
84+
}
85+
3686
const active = rgba(60, 120, 220);
3787
const inactive = rgba(50, 50, 60);
3888
const on = rgba(40, 180, 80);
@@ -563,34 +613,33 @@ function ttyFlags(ctx: AppContext): Setting {
563613
}
564614

565615
await main(function* () {
566-
let { columns, rows } = Deno.stdout.isTerminal()
567-
? Deno.consoleSize()
568-
: { columns: 80, rows: 24 };
616+
let { columns, rows } = terminalSize();
569617

570-
Deno.stdin.setRaw(true);
618+
setRawMode(true);
571619

572620
let stdin = yield* useStdin();
573621
let input = useInput(stdin);
574622

575623
let term = yield* until(createTerm({ width: columns, height: rows }));
576624

577625
let tty = settings(alternateBuffer(), cursor(false));
578-
Deno.stdout.writeSync(tty.apply);
626+
writeStdout(tty.apply);
579627

580628
let modality = recognizer();
581629
let context = modality.next().value;
582630

583631
let flags = ttyFlags(context);
584-
Deno.stdout.writeSync(flags.apply);
632+
writeStdout(flags.apply);
585633

586634
yield* ensure(() => {
587-
Deno.stdout.writeSync(flags.revert);
588-
Deno.stdout.writeSync(tty.revert);
635+
setRawMode(false);
636+
writeStdout(flags.revert);
637+
writeStdout(tty.revert);
589638
});
590639

591640
let { output } = term.render(keyboard(context));
592641

593-
Deno.stdout.writeSync(output);
642+
writeStdout(output);
594643

595644
let pointer = {
596645
events: createChannel<PointerEvent, void>(),
@@ -616,9 +665,7 @@ await main(function* () {
616665
context = { ...context, logged: prev };
617666
}
618667

619-
Deno.stdout.writeSync(flags.revert);
620-
flags = ttyFlags(context);
621-
Deno.stdout.writeSync(flags.apply);
668+
flags = updateFlagsIfChanged(flags, ttyFlags(context));
622669

623670
if (context["Capture mouse events"]) {
624671
if ("x" in event) {
@@ -640,7 +687,7 @@ await main(function* () {
640687
yield* pointer.events.send(event);
641688
}
642689

643-
Deno.stdout.writeSync(output);
690+
writeStdout(output);
644691

645692
yield* each.next();
646693
}

examples/keyboard/use-stdin.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
type Stream,
99
until,
1010
} from "effection";
11+
import process from "node:process";
1112

1213
export function useStdin(): Operation<Stream<Uint8Array, void>> {
1314
return resource(function* (provide) {
1415
let channel = createChannel<Uint8Array, void>();
15-
16-
let iterator = Deno.stdin.readable[Symbol.asyncIterator]();
16+
let iterator = process.stdin.iterator() as AsyncIterator<Uint8Array>;
1717

1818
yield* spawn(function* () {
1919
let next = yield* until(iterator.next());

0 commit comments

Comments
 (0)