@effect-tui/react 0.1.0-alpha.1
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 +21 -0
- package/README.md +138 -0
- package/dist/jsx-dev-runtime.d.ts +3 -0
- package/dist/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx-dev-runtime.js +3 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.d.ts +47 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +6 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/src/codeblock.d.ts +9 -0
- package/dist/src/codeblock.d.ts.map +1 -0
- package/dist/src/codeblock.js +24 -0
- package/dist/src/codeblock.js.map +1 -0
- package/dist/src/constants.d.ts +3 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +3 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/debug/DiagnosticsPanel.d.ts +7 -0
- package/dist/src/debug/DiagnosticsPanel.d.ts.map +1 -0
- package/dist/src/debug/DiagnosticsPanel.js +13 -0
- package/dist/src/debug/DiagnosticsPanel.js.map +1 -0
- package/dist/src/highlight.d.ts +20 -0
- package/dist/src/highlight.d.ts.map +1 -0
- package/dist/src/highlight.js +51 -0
- package/dist/src/highlight.js.map +1 -0
- package/dist/src/hooks/index.d.ts +4 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +3 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/use-keyboard.d.ts +18 -0
- package/dist/src/hooks/use-keyboard.d.ts.map +1 -0
- package/dist/src/hooks/use-keyboard.js +26 -0
- package/dist/src/hooks/use-keyboard.js.map +1 -0
- package/dist/src/hooks/use-paste.d.ts +5 -0
- package/dist/src/hooks/use-paste.d.ts.map +1 -0
- package/dist/src/hooks/use-paste.js +14 -0
- package/dist/src/hooks/use-paste.js.map +1 -0
- package/dist/src/hooks/useFrameStats.d.ts +7 -0
- package/dist/src/hooks/useFrameStats.d.ts.map +1 -0
- package/dist/src/hooks/useFrameStats.js +28 -0
- package/dist/src/hooks/useFrameStats.js.map +1 -0
- package/dist/src/hosts/base.d.ts +22 -0
- package/dist/src/hosts/base.d.ts.map +1 -0
- package/dist/src/hosts/base.js +53 -0
- package/dist/src/hosts/base.js.map +1 -0
- package/dist/src/hosts/box.d.ts +26 -0
- package/dist/src/hosts/box.d.ts.map +1 -0
- package/dist/src/hosts/box.js +84 -0
- package/dist/src/hosts/box.js.map +1 -0
- package/dist/src/hosts/canvas.d.ts +48 -0
- package/dist/src/hosts/canvas.d.ts.map +1 -0
- package/dist/src/hosts/canvas.js +109 -0
- package/dist/src/hosts/canvas.js.map +1 -0
- package/dist/src/hosts/codeblock.d.ts +32 -0
- package/dist/src/hosts/codeblock.d.ts.map +1 -0
- package/dist/src/hosts/codeblock.js +118 -0
- package/dist/src/hosts/codeblock.js.map +1 -0
- package/dist/src/hosts/hstack.d.ts +18 -0
- package/dist/src/hosts/hstack.d.ts.map +1 -0
- package/dist/src/hosts/hstack.js +45 -0
- package/dist/src/hosts/hstack.js.map +1 -0
- package/dist/src/hosts/index.d.ts +16 -0
- package/dist/src/hosts/index.d.ts.map +1 -0
- package/dist/src/hosts/index.js +40 -0
- package/dist/src/hosts/index.js.map +1 -0
- package/dist/src/hosts/spacer.d.ts +19 -0
- package/dist/src/hosts/spacer.d.ts.map +1 -0
- package/dist/src/hosts/spacer.js +28 -0
- package/dist/src/hosts/spacer.js.map +1 -0
- package/dist/src/hosts/text.d.ts +43 -0
- package/dist/src/hosts/text.d.ts.map +1 -0
- package/dist/src/hosts/text.js +148 -0
- package/dist/src/hosts/text.js.map +1 -0
- package/dist/src/hosts/vstack.d.ts +18 -0
- package/dist/src/hosts/vstack.d.ts.map +1 -0
- package/dist/src/hosts/vstack.js +45 -0
- package/dist/src/hosts/vstack.js.map +1 -0
- package/dist/src/hosts/zstack.d.ts +20 -0
- package/dist/src/hosts/zstack.d.ts.map +1 -0
- package/dist/src/hosts/zstack.js +65 -0
- package/dist/src/hosts/zstack.js.map +1 -0
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +20 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/inline/index.d.ts +32 -0
- package/dist/src/inline/index.d.ts.map +1 -0
- package/dist/src/inline/index.js +111 -0
- package/dist/src/inline/index.js.map +1 -0
- package/dist/src/jsx.d.ts +2 -0
- package/dist/src/jsx.d.ts.map +1 -0
- package/dist/src/jsx.js +4 -0
- package/dist/src/jsx.js.map +1 -0
- package/dist/src/motion/color-motion-value.d.ts +32 -0
- package/dist/src/motion/color-motion-value.d.ts.map +1 -0
- package/dist/src/motion/color-motion-value.js +80 -0
- package/dist/src/motion/color-motion-value.js.map +1 -0
- package/dist/src/motion/color.d.ts +30 -0
- package/dist/src/motion/color.d.ts.map +1 -0
- package/dist/src/motion/color.js +172 -0
- package/dist/src/motion/color.js.map +1 -0
- package/dist/src/motion/color.test.d.ts +2 -0
- package/dist/src/motion/color.test.d.ts.map +1 -0
- package/dist/src/motion/color.test.js +97 -0
- package/dist/src/motion/color.test.js.map +1 -0
- package/dist/src/motion/event-emitter.d.ts +18 -0
- package/dist/src/motion/event-emitter.d.ts.map +1 -0
- package/dist/src/motion/event-emitter.js +30 -0
- package/dist/src/motion/event-emitter.js.map +1 -0
- package/dist/src/motion/frame.d.ts +9 -0
- package/dist/src/motion/frame.d.ts.map +1 -0
- package/dist/src/motion/frame.js +51 -0
- package/dist/src/motion/frame.js.map +1 -0
- package/dist/src/motion/hooks.d.ts +75 -0
- package/dist/src/motion/hooks.d.ts.map +1 -0
- package/dist/src/motion/hooks.js +190 -0
- package/dist/src/motion/hooks.js.map +1 -0
- package/dist/src/motion/index.d.ts +4 -0
- package/dist/src/motion/index.d.ts.map +1 -0
- package/dist/src/motion/index.js +7 -0
- package/dist/src/motion/index.js.map +1 -0
- package/dist/src/motion/motion-value.d.ts +40 -0
- package/dist/src/motion/motion-value.d.ts.map +1 -0
- package/dist/src/motion/motion-value.js +109 -0
- package/dist/src/motion/motion-value.js.map +1 -0
- package/dist/src/motion/motion-value.test.d.ts +2 -0
- package/dist/src/motion/motion-value.test.d.ts.map +1 -0
- package/dist/src/motion/motion-value.test.js +177 -0
- package/dist/src/motion/motion-value.test.js.map +1 -0
- package/dist/src/motion/spring-math.d.ts +28 -0
- package/dist/src/motion/spring-math.d.ts.map +1 -0
- package/dist/src/motion/spring-math.js +81 -0
- package/dist/src/motion/spring-math.js.map +1 -0
- package/dist/src/motion/types.d.ts +25 -0
- package/dist/src/motion/types.d.ts.map +1 -0
- package/dist/src/motion/types.js +13 -0
- package/dist/src/motion/types.js.map +1 -0
- package/dist/src/output.d.ts +47 -0
- package/dist/src/output.d.ts.map +1 -0
- package/dist/src/output.js +125 -0
- package/dist/src/output.js.map +1 -0
- package/dist/src/profiler.d.ts +6 -0
- package/dist/src/profiler.d.ts.map +1 -0
- package/dist/src/profiler.js +73 -0
- package/dist/src/profiler.js.map +1 -0
- package/dist/src/reconciler/host-config.d.ts +16 -0
- package/dist/src/reconciler/host-config.d.ts.map +1 -0
- package/dist/src/reconciler/host-config.js +174 -0
- package/dist/src/reconciler/host-config.js.map +1 -0
- package/dist/src/reconciler/types.d.ts +52 -0
- package/dist/src/reconciler/types.d.ts.map +1 -0
- package/dist/src/reconciler/types.js +2 -0
- package/dist/src/reconciler/types.js.map +1 -0
- package/dist/src/renderer.d.ts +101 -0
- package/dist/src/renderer.d.ts.map +1 -0
- package/dist/src/renderer.js +509 -0
- package/dist/src/renderer.js.map +1 -0
- package/dist/src/terminal.d.ts +37 -0
- package/dist/src/terminal.d.ts.map +1 -0
- package/dist/src/terminal.js +65 -0
- package/dist/src/terminal.js.map +1 -0
- package/dist/src/test/index.d.ts +3 -0
- package/dist/src/test/index.d.ts.map +1 -0
- package/dist/src/test/index.js +3 -0
- package/dist/src/test/index.js.map +1 -0
- package/dist/src/test/mock-streams.d.ts +44 -0
- package/dist/src/test/mock-streams.d.ts.map +1 -0
- package/dist/src/test/mock-streams.js +136 -0
- package/dist/src/test/mock-streams.js.map +1 -0
- package/dist/src/test/render-tui.d.ts +47 -0
- package/dist/src/test/render-tui.d.ts.map +1 -0
- package/dist/src/test/render-tui.js +76 -0
- package/dist/src/test/render-tui.js.map +1 -0
- package/dist/src/trace/SpanTree.d.ts +10 -0
- package/dist/src/trace/SpanTree.d.ts.map +1 -0
- package/dist/src/trace/SpanTree.js +104 -0
- package/dist/src/trace/SpanTree.js.map +1 -0
- package/dist/src/trace/index.d.ts +30 -0
- package/dist/src/trace/index.d.ts.map +1 -0
- package/dist/src/trace/index.js +142 -0
- package/dist/src/trace/index.js.map +1 -0
- package/dist/src/trace/location.d.ts +9 -0
- package/dist/src/trace/location.d.ts.map +1 -0
- package/dist/src/trace/location.js +88 -0
- package/dist/src/trace/location.js.map +1 -0
- package/dist/src/trace/span-processor.d.ts +16 -0
- package/dist/src/trace/span-processor.d.ts.map +1 -0
- package/dist/src/trace/span-processor.js +54 -0
- package/dist/src/trace/span-processor.js.map +1 -0
- package/dist/src/trace/span-state.d.ts +79 -0
- package/dist/src/trace/span-state.d.ts.map +1 -0
- package/dist/src/trace/span-state.js +229 -0
- package/dist/src/trace/span-state.js.map +1 -0
- package/dist/src/trace/tui-logger.d.ts +8 -0
- package/dist/src/trace/tui-logger.d.ts.map +1 -0
- package/dist/src/trace/tui-logger.js +70 -0
- package/dist/src/trace/tui-logger.js.map +1 -0
- package/dist/src/utils/border.d.ts +31 -0
- package/dist/src/utils/border.d.ts.map +1 -0
- package/dist/src/utils/border.js +81 -0
- package/dist/src/utils/border.js.map +1 -0
- package/dist/src/utils/flex-layout.d.ts +20 -0
- package/dist/src/utils/flex-layout.d.ts.map +1 -0
- package/dist/src/utils/flex-layout.js +85 -0
- package/dist/src/utils/flex-layout.js.map +1 -0
- package/dist/src/utils/index.d.ts +5 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +5 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/padding.d.ts +26 -0
- package/dist/src/utils/padding.d.ts.map +1 -0
- package/dist/src/utils/padding.js +34 -0
- package/dist/src/utils/padding.js.map +1 -0
- package/dist/src/utils/styles.d.ts +13 -0
- package/dist/src/utils/styles.d.ts.map +1 -0
- package/dist/src/utils/styles.js +5 -0
- package/dist/src/utils/styles.js.map +1 -0
- package/dist/src/visualize/index.d.ts +50 -0
- package/dist/src/visualize/index.d.ts.map +1 -0
- package/dist/src/visualize/index.js +194 -0
- package/dist/src/visualize/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +94 -0
- package/src/codeblock.tsx +47 -0
- package/src/constants.ts +2 -0
- package/src/debug/DiagnosticsPanel.tsx +38 -0
- package/src/highlight.ts +76 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/use-keyboard.ts +37 -0
- package/src/hooks/use-paste.ts +14 -0
- package/src/hooks/useFrameStats.ts +32 -0
- package/src/hosts/base.ts +65 -0
- package/src/hosts/box.ts +105 -0
- package/src/hosts/canvas.ts +155 -0
- package/src/hosts/codeblock.ts +145 -0
- package/src/hosts/hstack.ts +64 -0
- package/src/hosts/index.ts +45 -0
- package/src/hosts/spacer.ts +40 -0
- package/src/hosts/text.ts +175 -0
- package/src/hosts/vstack.ts +64 -0
- package/src/hosts/zstack.ts +77 -0
- package/src/index.ts +62 -0
- package/src/inline/index.tsx +181 -0
- package/src/jsx.ts +3 -0
- package/src/motion/color-motion-value.ts +90 -0
- package/src/motion/color.test.ts +115 -0
- package/src/motion/color.ts +191 -0
- package/src/motion/event-emitter.ts +35 -0
- package/src/motion/frame.ts +59 -0
- package/src/motion/hooks.ts +237 -0
- package/src/motion/index.ts +17 -0
- package/src/motion/motion-value.test.ts +222 -0
- package/src/motion/motion-value.ts +140 -0
- package/src/motion/spring-math.ts +114 -0
- package/src/motion/types.ts +34 -0
- package/src/output.ts +156 -0
- package/src/profiler.ts +88 -0
- package/src/reconciler/host-config.ts +277 -0
- package/src/reconciler/types.ts +66 -0
- package/src/renderer.ts +661 -0
- package/src/terminal.ts +67 -0
- package/src/test/index.ts +8 -0
- package/src/test/mock-streams.ts +149 -0
- package/src/test/render-tui.ts +118 -0
- package/src/trace/SpanTree.tsx +195 -0
- package/src/trace/index.tsx +205 -0
- package/src/trace/location.ts +90 -0
- package/src/trace/span-processor.ts +65 -0
- package/src/trace/span-state.ts +286 -0
- package/src/trace/tui-logger.ts +72 -0
- package/src/utils/border.ts +108 -0
- package/src/utils/flex-layout.ts +125 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/padding.ts +45 -0
- package/src/utils/styles.ts +14 -0
- package/src/visualize/index.tsx +305 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { KeyMsg } from "@effect-tui/core";
|
|
3
|
+
/**
|
|
4
|
+
* Mock stdout for testing. Captures all writes and simulates terminal properties.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MockStdout extends EventEmitter {
|
|
7
|
+
columns: number;
|
|
8
|
+
rows: number;
|
|
9
|
+
buf: string;
|
|
10
|
+
constructor(width?: number, height?: number);
|
|
11
|
+
write(s: string): boolean;
|
|
12
|
+
/** Clear captured output */
|
|
13
|
+
clear(): void;
|
|
14
|
+
/** Simulate terminal resize */
|
|
15
|
+
resize(width: number, height: number): void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Mock stdin for testing. Simulates TTY input with key event emission.
|
|
19
|
+
*/
|
|
20
|
+
export declare class MockStdin extends EventEmitter {
|
|
21
|
+
isTTY: boolean;
|
|
22
|
+
setRawMode(_mode: boolean): this;
|
|
23
|
+
resume(): this;
|
|
24
|
+
pause(): this;
|
|
25
|
+
/** Send raw bytes (for testing decodeKeys directly) */
|
|
26
|
+
sendRaw(data: Buffer): void;
|
|
27
|
+
/** Send a key by encoding it to bytes */
|
|
28
|
+
sendKey(key: KeyMsg): void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Encode a KeyMsg to raw bytes for stdin simulation.
|
|
32
|
+
* This is the reverse of decodeKeys().
|
|
33
|
+
*/
|
|
34
|
+
export declare function encodeKey(key: KeyMsg): Buffer;
|
|
35
|
+
/**
|
|
36
|
+
* Strip ANSI escape codes from a string for easier assertions.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stripAnsi(str: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Extract visible text lines from raw terminal output.
|
|
41
|
+
* Removes ANSI codes and splits by newlines.
|
|
42
|
+
*/
|
|
43
|
+
export declare function getVisibleLines(output: string): string[];
|
|
44
|
+
//# sourceMappingURL=mock-streams.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-streams.d.ts","sourceRoot":"","sources":["../../../src/test/mock-streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAE9C;;GAEG;AACH,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,SAAK;gBAEI,KAAK,SAAK,EAAE,MAAM,SAAK;IAMnC,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO;IAKzB,4BAA4B;IAC5B,KAAK,IAAI,IAAI;IAIb,+BAA+B;IAC/B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAK5C;AAED;;GAEG;AACH,qBAAa,SAAU,SAAQ,YAAY;IACzC,KAAK,UAAO;IAEZ,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIhC,MAAM,IAAI,IAAI;IAId,KAAK,IAAI,IAAI;IAIb,uDAAuD;IACvD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI3B,yCAAyC;IACzC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;CAI3B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4D7C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAKxD"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
/**
|
|
3
|
+
* Mock stdout for testing. Captures all writes and simulates terminal properties.
|
|
4
|
+
*/
|
|
5
|
+
export class MockStdout extends EventEmitter {
|
|
6
|
+
columns;
|
|
7
|
+
rows;
|
|
8
|
+
buf = "";
|
|
9
|
+
constructor(width = 80, height = 24) {
|
|
10
|
+
super();
|
|
11
|
+
this.columns = width;
|
|
12
|
+
this.rows = height;
|
|
13
|
+
}
|
|
14
|
+
write(s) {
|
|
15
|
+
this.buf += s;
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
/** Clear captured output */
|
|
19
|
+
clear() {
|
|
20
|
+
this.buf = "";
|
|
21
|
+
}
|
|
22
|
+
/** Simulate terminal resize */
|
|
23
|
+
resize(width, height) {
|
|
24
|
+
this.columns = width;
|
|
25
|
+
this.rows = height;
|
|
26
|
+
this.emit("resize");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Mock stdin for testing. Simulates TTY input with key event emission.
|
|
31
|
+
*/
|
|
32
|
+
export class MockStdin extends EventEmitter {
|
|
33
|
+
isTTY = true;
|
|
34
|
+
setRawMode(_mode) {
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
resume() {
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
pause() {
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
/** Send raw bytes (for testing decodeKeys directly) */
|
|
44
|
+
sendRaw(data) {
|
|
45
|
+
this.emit("data", data);
|
|
46
|
+
}
|
|
47
|
+
/** Send a key by encoding it to bytes */
|
|
48
|
+
sendKey(key) {
|
|
49
|
+
const encoded = encodeKey(key);
|
|
50
|
+
this.emit("data", encoded);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Encode a KeyMsg to raw bytes for stdin simulation.
|
|
55
|
+
* This is the reverse of decodeKeys().
|
|
56
|
+
*/
|
|
57
|
+
export function encodeKey(key) {
|
|
58
|
+
const name = key.name;
|
|
59
|
+
const text = key.text;
|
|
60
|
+
// Control keys (Ctrl + printable character)
|
|
61
|
+
if (key.ctrl && text) {
|
|
62
|
+
const lower = text.toLowerCase();
|
|
63
|
+
if (lower === "c")
|
|
64
|
+
return Buffer.from([0x03]);
|
|
65
|
+
if (lower === "d")
|
|
66
|
+
return Buffer.from([0x04]);
|
|
67
|
+
if (lower === "z")
|
|
68
|
+
return Buffer.from([0x1a]);
|
|
69
|
+
if (lower === "w")
|
|
70
|
+
return Buffer.from([0x17]);
|
|
71
|
+
// Ctrl+letter = letter code - 0x60
|
|
72
|
+
if (text.length === 1) {
|
|
73
|
+
const code = lower.charCodeAt(0) - 0x60;
|
|
74
|
+
if (code >= 1 && code <= 26)
|
|
75
|
+
return Buffer.from([code]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Special keys (escape sequences)
|
|
79
|
+
switch (name) {
|
|
80
|
+
case "up":
|
|
81
|
+
return Buffer.from([0x1b, 0x5b, 0x41]);
|
|
82
|
+
case "down":
|
|
83
|
+
return Buffer.from([0x1b, 0x5b, 0x42]);
|
|
84
|
+
case "right":
|
|
85
|
+
return Buffer.from([0x1b, 0x5b, 0x43]);
|
|
86
|
+
case "left":
|
|
87
|
+
return Buffer.from([0x1b, 0x5b, 0x44]);
|
|
88
|
+
case "home":
|
|
89
|
+
return Buffer.from([0x1b, 0x5b, 0x48]);
|
|
90
|
+
case "end":
|
|
91
|
+
return Buffer.from([0x1b, 0x5b, 0x46]);
|
|
92
|
+
case "pageup":
|
|
93
|
+
return Buffer.from([0x1b, 0x5b, 0x35, 0x7e]);
|
|
94
|
+
case "pagedown":
|
|
95
|
+
return Buffer.from([0x1b, 0x5b, 0x36, 0x7e]);
|
|
96
|
+
case "delete":
|
|
97
|
+
return Buffer.from([0x1b, 0x5b, 0x33, 0x7e]);
|
|
98
|
+
case "insert":
|
|
99
|
+
return Buffer.from([0x1b, 0x5b, 0x32, 0x7e]);
|
|
100
|
+
case "escape":
|
|
101
|
+
return Buffer.from([0x1b]);
|
|
102
|
+
case "tab":
|
|
103
|
+
return Buffer.from([0x09]);
|
|
104
|
+
case "enter":
|
|
105
|
+
case "return":
|
|
106
|
+
return Buffer.from([0x0d]);
|
|
107
|
+
case "backspace":
|
|
108
|
+
return Buffer.from([0x7f]);
|
|
109
|
+
case "space":
|
|
110
|
+
return Buffer.from([0x20]);
|
|
111
|
+
}
|
|
112
|
+
// Regular character
|
|
113
|
+
if (text) {
|
|
114
|
+
return Buffer.from(text, "utf8");
|
|
115
|
+
}
|
|
116
|
+
// Unknown key, return empty
|
|
117
|
+
return Buffer.from([]);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Strip ANSI escape codes from a string for easier assertions.
|
|
121
|
+
*/
|
|
122
|
+
export function stripAnsi(str) {
|
|
123
|
+
// eslint-disable-next-line no-control-regex
|
|
124
|
+
return str.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Extract visible text lines from raw terminal output.
|
|
128
|
+
* Removes ANSI codes and splits by newlines.
|
|
129
|
+
*/
|
|
130
|
+
export function getVisibleLines(output) {
|
|
131
|
+
const stripped = stripAnsi(output);
|
|
132
|
+
// Replace carriage returns with newlines for consistency
|
|
133
|
+
const normalized = stripped.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
134
|
+
return normalized.split("\n");
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=mock-streams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-streams.js","sourceRoot":"","sources":["../../../src/test/mock-streams.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReactElement } from "react";
|
|
2
|
+
import type { KeyMsg } from "@effect-tui/core";
|
|
3
|
+
import { MockStdout, MockStdin } from "./mock-streams.js";
|
|
4
|
+
export interface RenderTUIOptions {
|
|
5
|
+
width?: number;
|
|
6
|
+
height?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface RenderTUIResult {
|
|
9
|
+
/** Get last rendered frame as raw string (includes ANSI codes) */
|
|
10
|
+
lastFrameRaw(): string;
|
|
11
|
+
/** Get last rendered frame with ANSI codes stripped */
|
|
12
|
+
lastFrame(): string;
|
|
13
|
+
/** Get visible text lines from last frame */
|
|
14
|
+
lines(): string[];
|
|
15
|
+
/** Send a key event */
|
|
16
|
+
sendKey(key: KeyMsg): void;
|
|
17
|
+
/** Trigger a render cycle and process React updates */
|
|
18
|
+
flush(): void;
|
|
19
|
+
/** Unmount the component and stop the renderer */
|
|
20
|
+
unmount(): void;
|
|
21
|
+
/** Access mock stdout for advanced assertions */
|
|
22
|
+
stdout: MockStdout;
|
|
23
|
+
/** Access mock stdin for advanced input simulation */
|
|
24
|
+
stdin: MockStdin;
|
|
25
|
+
/** Resize the terminal */
|
|
26
|
+
resize(width: number, height: number): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Render a TUI component for testing.
|
|
30
|
+
* Similar to ink-testing-library's render().
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const { lastFrame, sendKey, flush, unmount } = renderTUI(<Counter />)
|
|
35
|
+
*
|
|
36
|
+
* expect(lastFrame()).toContain("Count: 0")
|
|
37
|
+
*
|
|
38
|
+
* sendKey({ key: "up" })
|
|
39
|
+
* flush()
|
|
40
|
+
*
|
|
41
|
+
* expect(lastFrame()).toContain("Count: 1")
|
|
42
|
+
*
|
|
43
|
+
* unmount()
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function renderTUI(element: ReactElement, options?: RenderTUIOptions): RenderTUIResult;
|
|
47
|
+
//# sourceMappingURL=render-tui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-tui.d.ts","sourceRoot":"","sources":["../../../src/test/render-tui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EAAE,UAAU,EAAE,SAAS,EAA8B,MAAM,mBAAmB,CAAA;AAErF,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,kEAAkE;IAClE,YAAY,IAAI,MAAM,CAAA;IACtB,uDAAuD;IACvD,SAAS,IAAI,MAAM,CAAA;IACnB,6CAA6C;IAC7C,KAAK,IAAI,MAAM,EAAE,CAAA;IACjB,uBAAuB;IACvB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,uDAAuD;IACvD,KAAK,IAAI,IAAI,CAAA;IACb,kDAAkD;IAClD,OAAO,IAAI,IAAI,CAAA;IACf,iDAAiD;IACjD,MAAM,EAAE,UAAU,CAAA;IAClB,sDAAsD;IACtD,KAAK,EAAE,SAAS,CAAA;IAChB,0BAA0B;IAC1B,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAmE5F"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { createRenderer, createRoot } from "../renderer.js";
|
|
2
|
+
import { flushPassiveEffects, flushSync, discreteUpdates } from "../reconciler/host-config.js";
|
|
3
|
+
import { MockStdout, MockStdin, stripAnsi, getVisibleLines } from "./mock-streams.js";
|
|
4
|
+
/**
|
|
5
|
+
* Render a TUI component for testing.
|
|
6
|
+
* Similar to ink-testing-library's render().
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const { lastFrame, sendKey, flush, unmount } = renderTUI(<Counter />)
|
|
11
|
+
*
|
|
12
|
+
* expect(lastFrame()).toContain("Count: 0")
|
|
13
|
+
*
|
|
14
|
+
* sendKey({ key: "up" })
|
|
15
|
+
* flush()
|
|
16
|
+
*
|
|
17
|
+
* expect(lastFrame()).toContain("Count: 1")
|
|
18
|
+
*
|
|
19
|
+
* unmount()
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function renderTUI(element, options) {
|
|
23
|
+
const width = options?.width ?? 80;
|
|
24
|
+
const height = options?.height ?? 24;
|
|
25
|
+
const stdout = new MockStdout(width, height);
|
|
26
|
+
const stdin = new MockStdin();
|
|
27
|
+
const renderer = createRenderer({
|
|
28
|
+
stdout: stdout,
|
|
29
|
+
stdin: stdin,
|
|
30
|
+
manualMode: true,
|
|
31
|
+
skipTerminalSetup: true,
|
|
32
|
+
});
|
|
33
|
+
const root = createRoot(renderer);
|
|
34
|
+
root.render(element, true); // sync mode
|
|
35
|
+
// Flush effects
|
|
36
|
+
flushPassiveEffects();
|
|
37
|
+
// Initial render
|
|
38
|
+
renderer.flush();
|
|
39
|
+
const flush = () => {
|
|
40
|
+
// Flush React updates synchronously
|
|
41
|
+
flushSync(() => { });
|
|
42
|
+
flushPassiveEffects();
|
|
43
|
+
// Clear buffer before re-render to get clean frame
|
|
44
|
+
stdout.clear();
|
|
45
|
+
renderer.requestRender();
|
|
46
|
+
renderer.flush();
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
lastFrameRaw() {
|
|
50
|
+
return stdout.buf;
|
|
51
|
+
},
|
|
52
|
+
lastFrame() {
|
|
53
|
+
return stripAnsi(stdout.buf);
|
|
54
|
+
},
|
|
55
|
+
lines() {
|
|
56
|
+
return getVisibleLines(stdout.buf);
|
|
57
|
+
},
|
|
58
|
+
sendKey(key) {
|
|
59
|
+
// Use discreteUpdates to ensure React processes state updates synchronously
|
|
60
|
+
discreteUpdates(() => {
|
|
61
|
+
stdin.sendKey(key);
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
flush,
|
|
65
|
+
unmount() {
|
|
66
|
+
root.unmount();
|
|
67
|
+
},
|
|
68
|
+
stdout,
|
|
69
|
+
stdin,
|
|
70
|
+
resize(w, h) {
|
|
71
|
+
stdout.resize(w, h);
|
|
72
|
+
flush();
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=render-tui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-tui.js","sourceRoot":"","sources":["../../../src/test/render-tui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3D,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AA4BrF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,SAAS,CAAC,OAAqB,EAAE,OAA0B;IACzE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,EAAE,CAAA;IAClC,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,EAAE,CAAA;IAEpC,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC5C,MAAM,KAAK,GAAG,IAAI,SAAS,EAAE,CAAA;IAE7B,MAAM,QAAQ,GAAG,cAAc,CAAC;QAC9B,MAAM,EAAE,MAAa;QACrB,KAAK,EAAE,KAAY;QACnB,UAAU,EAAE,IAAI;QAChB,iBAAiB,EAAE,IAAI;KACxB,CAAC,CAAA;IAEF,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;IACjC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA,CAAC,YAAY;IAEvC,gBAAgB;IAChB,mBAAmB,EAAE,CAAA;IAErB,iBAAiB;IACjB,QAAQ,CAAC,KAAK,EAAE,CAAA;IAEhB,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,oCAAoC;QACpC,SAAS,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACnB,mBAAmB,EAAE,CAAA;QACrB,mDAAmD;QACnD,MAAM,CAAC,KAAK,EAAE,CAAA;QACd,QAAQ,CAAC,aAAa,EAAE,CAAA;QACxB,QAAQ,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC,CAAA;IAED,OAAO;QACL,YAAY;YACV,OAAO,MAAM,CAAC,GAAG,CAAA;QACnB,CAAC;QAED,SAAS;YACP,OAAO,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAC9B,CAAC;QAED,KAAK;YACH,OAAO,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACpC,CAAC;QAED,OAAO,CAAC,GAAW;YACjB,4EAA4E;YAC5E,eAAe,CAAC,GAAG,EAAE;gBACnB,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACpB,CAAC,CAAC,CAAA;QACJ,CAAC;QAED,KAAK;QAEL,OAAO;YACL,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;QAED,MAAM;QACN,KAAK;QAEL,MAAM,CAAC,CAAS,EAAE,CAAS;YACzB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACnB,KAAK,EAAE,CAAA;QACT,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SpanTreeState } from "./span-state.js";
|
|
2
|
+
interface SpanTreeProps {
|
|
3
|
+
state: SpanTreeState;
|
|
4
|
+
spinnerIndex: number;
|
|
5
|
+
serviceName?: string;
|
|
6
|
+
showExitPrompt?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function SpanTree({ state, spinnerIndex, serviceName, showExitPrompt }: SpanTreeProps): import("react").ReactNode;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=SpanTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpanTree.d.ts","sourceRoot":"","sources":["../../../src/trace/SpanTree.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAqJ9D,UAAU,aAAa;IACrB,KAAK,EAAE,aAAa,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,aAAa,6BAmC3F"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "@effect-tui/react/jsx-runtime";
|
|
2
|
+
// React component for visualizing span tree
|
|
3
|
+
import { Colors } from "@effect-tui/core";
|
|
4
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠸", "⠴", "⠦", "⠇"];
|
|
5
|
+
function formatDuration(ms) {
|
|
6
|
+
if (ms < 1000)
|
|
7
|
+
return `${Math.round(ms)}ms`;
|
|
8
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
9
|
+
}
|
|
10
|
+
// Map opacity (0-1) to gray level (8-23 for visible range)
|
|
11
|
+
function opacityToGray(opacity) {
|
|
12
|
+
const minGray = 8;
|
|
13
|
+
const maxGray = 23;
|
|
14
|
+
return Math.round(minGray + opacity * (maxGray - minGray));
|
|
15
|
+
}
|
|
16
|
+
// Interpolate RGB color based on opacity
|
|
17
|
+
function dimColor(r, g, b, opacity) {
|
|
18
|
+
const minOpacity = 0.3; // Don't go completely dark
|
|
19
|
+
const factor = minOpacity + opacity * (1 - minOpacity);
|
|
20
|
+
return Colors.rgb(Math.round(r * factor), Math.round(g * factor), Math.round(b * factor));
|
|
21
|
+
}
|
|
22
|
+
// Get log level color
|
|
23
|
+
function logLevelColor(level, opacity) {
|
|
24
|
+
switch (level) {
|
|
25
|
+
case "ERROR":
|
|
26
|
+
return dimColor(255, 80, 80, opacity);
|
|
27
|
+
case "WARN":
|
|
28
|
+
return dimColor(255, 200, 0, opacity);
|
|
29
|
+
case "INFO":
|
|
30
|
+
return dimColor(100, 180, 255, opacity);
|
|
31
|
+
default:
|
|
32
|
+
return Colors.gray(opacityToGray(opacity));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function SpanRow({ node, state, spinnerIndex, prefix, isLast }) {
|
|
36
|
+
const connector = isLast ? "└─" : "├─";
|
|
37
|
+
const childPrefix = prefix + (isLast ? " " : "│ ");
|
|
38
|
+
const opacity = node.opacity;
|
|
39
|
+
const grayLevel = opacityToGray(opacity);
|
|
40
|
+
const isSelected = state.selectedSpanId === node.id;
|
|
41
|
+
const isExpanded = state.expandedSpanIds.has(node.id);
|
|
42
|
+
// Status icon
|
|
43
|
+
let icon;
|
|
44
|
+
let iconColor;
|
|
45
|
+
if (node.status === "running") {
|
|
46
|
+
icon = SPINNER_FRAMES[spinnerIndex];
|
|
47
|
+
iconColor = Colors.brightCyan;
|
|
48
|
+
}
|
|
49
|
+
else if (node.status === "success") {
|
|
50
|
+
icon = "✓";
|
|
51
|
+
iconColor = dimColor(0, 255, 0, opacity);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
icon = "✗";
|
|
55
|
+
iconColor = dimColor(255, 80, 80, opacity);
|
|
56
|
+
}
|
|
57
|
+
// Name color - selected gets blue bg with bright text
|
|
58
|
+
const nameColor = isSelected
|
|
59
|
+
? Colors.brightWhite
|
|
60
|
+
: node.status === "running"
|
|
61
|
+
? Colors.brightWhite
|
|
62
|
+
: Colors.gray(grayLevel);
|
|
63
|
+
const nameBg = isSelected ? Colors.rgb(30, 60, 120) : undefined;
|
|
64
|
+
// Duration display
|
|
65
|
+
const durationStr = node.duration !== undefined
|
|
66
|
+
? formatDuration(node.duration)
|
|
67
|
+
: node.status === "running"
|
|
68
|
+
? formatDuration(Date.now() - node.startTime)
|
|
69
|
+
: "";
|
|
70
|
+
const durationColor = Colors.gray(Math.max(8, grayLevel - 4));
|
|
71
|
+
// Log counts from logEntries
|
|
72
|
+
const logCounts = { info: 0, warn: 0, error: 0, debug: 0 };
|
|
73
|
+
for (const entry of node.logEntries) {
|
|
74
|
+
if (entry.level === "INFO")
|
|
75
|
+
logCounts.info++;
|
|
76
|
+
else if (entry.level === "WARN")
|
|
77
|
+
logCounts.warn++;
|
|
78
|
+
else if (entry.level === "ERROR")
|
|
79
|
+
logCounts.error++;
|
|
80
|
+
else
|
|
81
|
+
logCounts.debug++;
|
|
82
|
+
}
|
|
83
|
+
const logParts = [];
|
|
84
|
+
if (logCounts.error > 0)
|
|
85
|
+
logParts.push({ text: `${logCounts.error} err`, color: dimColor(255, 80, 80, opacity) });
|
|
86
|
+
if (logCounts.warn > 0)
|
|
87
|
+
logParts.push({ text: `${logCounts.warn} warn`, color: dimColor(255, 200, 0, opacity) });
|
|
88
|
+
if (logCounts.info > 0)
|
|
89
|
+
logParts.push({ text: `${logCounts.info} info`, color: dimColor(100, 180, 255, opacity) });
|
|
90
|
+
if (logCounts.debug > 0)
|
|
91
|
+
logParts.push({ text: `${logCounts.debug} dbg`, color: Colors.gray(grayLevel) });
|
|
92
|
+
return (_jsxs("vstack", { children: [_jsxs("hstack", { children: [_jsxs("text", { fg: Colors.gray(Math.max(8, grayLevel - 2)), children: [prefix, connector] }), _jsxs("text", { fg: iconColor, children: [" ", icon, " "] }), _jsx("text", { fg: nameColor, bg: nameBg, children: node.name }), durationStr && _jsxs("text", { fg: durationColor, children: [" ", durationStr] }), logParts.length > 0 && _jsx("text", { fg: durationColor, children: " [" }), logParts.map((p, i) => (_jsxs("text", { fg: p.color, children: [i > 0 ? " " : "", p.text] }, i))), logParts.length > 0 && _jsx("text", { fg: durationColor, children: "]" }), node.error && _jsxs("text", { fg: Colors.red, children: [" (", node.error, ")"] })] }), isExpanded &&
|
|
93
|
+
node.logEntries.map((entry, i) => (_jsxs("hstack", { children: [_jsxs("text", { fg: Colors.gray(10), children: [childPrefix, node.children.length > 0 ? "│ " : " "] }), _jsx("text", { fg: logLevelColor(entry.level, opacity), children: entry.level }), _jsxs("text", { fg: Colors.gray(16), children: [" ", entry.message] })] }, i))), node.children.map((child, i) => (_jsx(SpanRow, { node: child, state: state, spinnerIndex: spinnerIndex, prefix: childPrefix, isLast: i === node.children.length - 1 }, child.id)))] }));
|
|
94
|
+
}
|
|
95
|
+
export function SpanTree({ state, spinnerIndex, serviceName, showExitPrompt }) {
|
|
96
|
+
// No subscription needed - parent's 60fps loop drives re-renders
|
|
97
|
+
const roots = state.getRoots();
|
|
98
|
+
if (roots.length === 0) {
|
|
99
|
+
return _jsx("text", { fg: Colors.gray(10), children: "Waiting for spans..." });
|
|
100
|
+
}
|
|
101
|
+
const hasAnyLogs = state.getSpansWithLogs().length > 0;
|
|
102
|
+
return (_jsxs("vstack", { children: [serviceName && (_jsx("text", { fg: Colors.brightWhite, bold: true, children: serviceName })), roots.map((root, i) => (_jsx(SpanRow, { node: root, state: state, spinnerIndex: spinnerIndex, prefix: "", isLast: i === roots.length - 1 }, root.id))), showExitPrompt && (_jsxs("text", { fg: Colors.gray(12), children: ["\n", hasAnyLogs ? "↑↓ navigate, Enter expand, O open, q to exit" : "q to exit"] }))] }));
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=SpanTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpanTree.js","sourceRoot":"","sources":["../../../src/trace/SpanTree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as OtelResource from "@effect/opentelemetry/Resource";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
export { SpanTreeState } from "./span-state.js";
|
|
4
|
+
export { TuiSpanProcessor } from "./span-processor.js";
|
|
5
|
+
export { SpanTree } from "./SpanTree.js";
|
|
6
|
+
export { makeTuiLogger } from "./tui-logger.js";
|
|
7
|
+
export interface TraceVisualizeOptions {
|
|
8
|
+
/** Service name for OpenTelemetry resource (default: "effect-app") */
|
|
9
|
+
serviceName?: string;
|
|
10
|
+
/** Show diagnostics panel inside the trace UI */
|
|
11
|
+
debugPanel?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Run an Effect program with TUI visualization of OpenTelemetry spans.
|
|
15
|
+
*
|
|
16
|
+
* Shows a tree of spans as they execute, with spinners for in-progress
|
|
17
|
+
* and checkmarks/X for completed spans.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const program = Effect.gen(function* () {
|
|
22
|
+
* yield* Effect.sleep(1000).pipe(Effect.withSpan("step-1"))
|
|
23
|
+
* yield* Effect.sleep(500).pipe(Effect.withSpan("step-2"))
|
|
24
|
+
* }).pipe(Effect.withSpan("main"))
|
|
25
|
+
*
|
|
26
|
+
* await traceVisualize(program)
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const traceVisualize: <A, E, R>(effect: Effect.Effect<A, E, R>, options?: TraceVisualizeOptions) => Effect.Effect<void, never, Exclude<R, OtelResource.Resource>>;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/trace/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,YAAY,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,MAAM,EAAiB,MAAM,QAAQ,CAAA;AAS9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAK/C,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAsGD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EACpC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC9B,UAAU,qBAAqB,KAC9B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAqDM,CAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@effect-tui/react/jsx-runtime";
|
|
2
|
+
// OpenTelemetry trace visualization for Effect programs
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { isAppPath } from "./location.js";
|
|
7
|
+
import * as NodeSdk from "@effect/opentelemetry/NodeSdk";
|
|
8
|
+
import { Effect, Layer, Logger } from "effect";
|
|
9
|
+
import { createRenderer, createRoot } from "../renderer.js";
|
|
10
|
+
import { useKeyboard } from "../hooks/index.js";
|
|
11
|
+
import { SpanTreeState } from "./span-state.js";
|
|
12
|
+
import { TuiSpanProcessor } from "./span-processor.js";
|
|
13
|
+
import { SpanTree } from "./SpanTree.js";
|
|
14
|
+
import { makeTuiLogger } from "./tui-logger.js";
|
|
15
|
+
import { DiagnosticsPanel } from "../debug/DiagnosticsPanel.js";
|
|
16
|
+
export { SpanTreeState } from "./span-state.js";
|
|
17
|
+
export { TuiSpanProcessor } from "./span-processor.js";
|
|
18
|
+
export { SpanTree } from "./SpanTree.js";
|
|
19
|
+
export { makeTuiLogger } from "./tui-logger.js";
|
|
20
|
+
const FRAME_INTERVAL_MS = 16; // ~60fps
|
|
21
|
+
const COMPLETION_DELAY_MS = 500;
|
|
22
|
+
// Wrapper component that drives 60fps render loop
|
|
23
|
+
function SpanTreeWrapper({ state, renderer, serviceName, done, showDebug, }) {
|
|
24
|
+
const [, forceUpdate] = React.useState(0);
|
|
25
|
+
const openInEditor = React.useCallback((location) => {
|
|
26
|
+
const normalizedFile = location.file.startsWith("file://") ? fileURLToPath(location.file) : location.file;
|
|
27
|
+
if (!isAppPath(normalizedFile)) {
|
|
28
|
+
if (process.env.EFFECT_TUI_DEBUG_STACKS === "1") {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.error("[effect-tui] refusing to open non-app path", normalizedFile);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const target = location.column !== undefined
|
|
35
|
+
? `${normalizedFile}:${location.line}:${location.column}`
|
|
36
|
+
: `${normalizedFile}:${location.line}`;
|
|
37
|
+
const candidates = [
|
|
38
|
+
process.env.EFFECT_TUI_EDITOR ? `${process.env.EFFECT_TUI_EDITOR} ${target}` : null,
|
|
39
|
+
process.env.EDITOR ? `${process.env.EDITOR} ${target}` : null,
|
|
40
|
+
`code -g ${target}`,
|
|
41
|
+
`open ${target}`,
|
|
42
|
+
`xdg-open ${target}`,
|
|
43
|
+
].filter(Boolean);
|
|
44
|
+
for (const cmd of candidates) {
|
|
45
|
+
try {
|
|
46
|
+
const child = spawn(cmd, { shell: true, stdio: "ignore", detached: true });
|
|
47
|
+
child.unref();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// keep trying next candidate
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.error("Could not launch editor. Set EFFECT_TUI_EDITOR or EDITOR to your preferred command.");
|
|
56
|
+
}, []);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
const interval = setInterval(() => {
|
|
59
|
+
state.tick();
|
|
60
|
+
forceUpdate((n) => n + 1);
|
|
61
|
+
renderer.requestRender();
|
|
62
|
+
}, FRAME_INTERVAL_MS);
|
|
63
|
+
return () => clearInterval(interval);
|
|
64
|
+
}, [renderer, state]);
|
|
65
|
+
// Handle keyboard
|
|
66
|
+
useKeyboard(React.useCallback((key) => {
|
|
67
|
+
// q quits when done (Ctrl+C always works via renderer)
|
|
68
|
+
if (done && key.text === "q") {
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
// Navigate between spans with logs
|
|
72
|
+
if (key.name === "up") {
|
|
73
|
+
state.selectPrev();
|
|
74
|
+
}
|
|
75
|
+
else if (key.name === "down") {
|
|
76
|
+
state.selectNext();
|
|
77
|
+
}
|
|
78
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
79
|
+
// Toggle expand selected span's logs
|
|
80
|
+
state.toggleExpand();
|
|
81
|
+
}
|
|
82
|
+
else if (key.name === "char" && key.text?.toLowerCase() === "o") {
|
|
83
|
+
const loc = state.getOpenTarget();
|
|
84
|
+
if (loc) {
|
|
85
|
+
openInEditor(loc);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
state.toggleExpand();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}, [done, state, openInEditor]));
|
|
92
|
+
return (_jsxs("vstack", { spacing: 1, children: [_jsx(SpanTree, { state: state, spinnerIndex: state.spinnerIndex, serviceName: serviceName, showExitPrompt: done }), showDebug && _jsx(DiagnosticsPanel, { title: "Trace Diagnostics" })] }));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Run an Effect program with TUI visualization of OpenTelemetry spans.
|
|
96
|
+
*
|
|
97
|
+
* Shows a tree of spans as they execute, with spinners for in-progress
|
|
98
|
+
* and checkmarks/X for completed spans.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const program = Effect.gen(function* () {
|
|
103
|
+
* yield* Effect.sleep(1000).pipe(Effect.withSpan("step-1"))
|
|
104
|
+
* yield* Effect.sleep(500).pipe(Effect.withSpan("step-2"))
|
|
105
|
+
* }).pipe(Effect.withSpan("main"))
|
|
106
|
+
*
|
|
107
|
+
* await traceVisualize(program)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export const traceVisualize = (effect, options) => Effect.gen(function* () {
|
|
111
|
+
// Create state and processor
|
|
112
|
+
const state = new SpanTreeState();
|
|
113
|
+
// Create renderer - handles terminal setup (raw mode, keyboard)
|
|
114
|
+
const renderer = createRenderer({ mode: "inline" });
|
|
115
|
+
const root = createRoot(renderer);
|
|
116
|
+
// Connect processor to renderer
|
|
117
|
+
const requestRender = () => renderer.requestRender();
|
|
118
|
+
const processor = new TuiSpanProcessor(state, requestRender);
|
|
119
|
+
const serviceName = options?.serviceName ?? "effect-app";
|
|
120
|
+
const showDebug = options?.debugPanel ?? false;
|
|
121
|
+
// Create OpenTelemetry layer
|
|
122
|
+
const TracerLive = NodeSdk.layer(() => ({
|
|
123
|
+
resource: { serviceName },
|
|
124
|
+
spanProcessor: processor,
|
|
125
|
+
}));
|
|
126
|
+
// Create TUI logger that feeds into state (replaces console logger)
|
|
127
|
+
const tuiLogger = makeTuiLogger(state, requestRender);
|
|
128
|
+
const TuiLoggerLive = Logger.replace(Logger.defaultLogger, tuiLogger);
|
|
129
|
+
// Mount React component
|
|
130
|
+
root.render(_jsx(SpanTreeWrapper, { state: state, renderer: renderer, serviceName: serviceName, done: false, showDebug: showDebug }));
|
|
131
|
+
// Combine tracing layer with TUI logger
|
|
132
|
+
const Live = Layer.merge(TracerLive, TuiLoggerLive);
|
|
133
|
+
// Run the effect with tracing (ignore result, visualization shows success/failure)
|
|
134
|
+
yield* effect.pipe(Effect.provide(Live), Effect.ignore);
|
|
135
|
+
// Wait a bit for final render
|
|
136
|
+
yield* Effect.sleep(COMPLETION_DELAY_MS);
|
|
137
|
+
// Show exit prompt - component handles Enter keypress with process.exit(0)
|
|
138
|
+
root.render(_jsx(SpanTreeWrapper, { state: state, renderer: renderer, serviceName: serviceName, done: true, showDebug: showDebug }));
|
|
139
|
+
// Keep process alive until user presses Enter (component will call process.exit)
|
|
140
|
+
return yield* Effect.never;
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/trace/index.tsx"],"names":[],"mappings":"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"}
|