@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.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +138 -0
  3. package/dist/jsx-dev-runtime.d.ts +3 -0
  4. package/dist/jsx-dev-runtime.d.ts.map +1 -0
  5. package/dist/jsx-dev-runtime.js +3 -0
  6. package/dist/jsx-dev-runtime.js.map +1 -0
  7. package/dist/jsx-runtime.d.ts +47 -0
  8. package/dist/jsx-runtime.d.ts.map +1 -0
  9. package/dist/jsx-runtime.js +6 -0
  10. package/dist/jsx-runtime.js.map +1 -0
  11. package/dist/src/codeblock.d.ts +9 -0
  12. package/dist/src/codeblock.d.ts.map +1 -0
  13. package/dist/src/codeblock.js +24 -0
  14. package/dist/src/codeblock.js.map +1 -0
  15. package/dist/src/constants.d.ts +3 -0
  16. package/dist/src/constants.d.ts.map +1 -0
  17. package/dist/src/constants.js +3 -0
  18. package/dist/src/constants.js.map +1 -0
  19. package/dist/src/debug/DiagnosticsPanel.d.ts +7 -0
  20. package/dist/src/debug/DiagnosticsPanel.d.ts.map +1 -0
  21. package/dist/src/debug/DiagnosticsPanel.js +13 -0
  22. package/dist/src/debug/DiagnosticsPanel.js.map +1 -0
  23. package/dist/src/highlight.d.ts +20 -0
  24. package/dist/src/highlight.d.ts.map +1 -0
  25. package/dist/src/highlight.js +51 -0
  26. package/dist/src/highlight.js.map +1 -0
  27. package/dist/src/hooks/index.d.ts +4 -0
  28. package/dist/src/hooks/index.d.ts.map +1 -0
  29. package/dist/src/hooks/index.js +3 -0
  30. package/dist/src/hooks/index.js.map +1 -0
  31. package/dist/src/hooks/use-keyboard.d.ts +18 -0
  32. package/dist/src/hooks/use-keyboard.d.ts.map +1 -0
  33. package/dist/src/hooks/use-keyboard.js +26 -0
  34. package/dist/src/hooks/use-keyboard.js.map +1 -0
  35. package/dist/src/hooks/use-paste.d.ts +5 -0
  36. package/dist/src/hooks/use-paste.d.ts.map +1 -0
  37. package/dist/src/hooks/use-paste.js +14 -0
  38. package/dist/src/hooks/use-paste.js.map +1 -0
  39. package/dist/src/hooks/useFrameStats.d.ts +7 -0
  40. package/dist/src/hooks/useFrameStats.d.ts.map +1 -0
  41. package/dist/src/hooks/useFrameStats.js +28 -0
  42. package/dist/src/hooks/useFrameStats.js.map +1 -0
  43. package/dist/src/hosts/base.d.ts +22 -0
  44. package/dist/src/hosts/base.d.ts.map +1 -0
  45. package/dist/src/hosts/base.js +53 -0
  46. package/dist/src/hosts/base.js.map +1 -0
  47. package/dist/src/hosts/box.d.ts +26 -0
  48. package/dist/src/hosts/box.d.ts.map +1 -0
  49. package/dist/src/hosts/box.js +84 -0
  50. package/dist/src/hosts/box.js.map +1 -0
  51. package/dist/src/hosts/canvas.d.ts +48 -0
  52. package/dist/src/hosts/canvas.d.ts.map +1 -0
  53. package/dist/src/hosts/canvas.js +109 -0
  54. package/dist/src/hosts/canvas.js.map +1 -0
  55. package/dist/src/hosts/codeblock.d.ts +32 -0
  56. package/dist/src/hosts/codeblock.d.ts.map +1 -0
  57. package/dist/src/hosts/codeblock.js +118 -0
  58. package/dist/src/hosts/codeblock.js.map +1 -0
  59. package/dist/src/hosts/hstack.d.ts +18 -0
  60. package/dist/src/hosts/hstack.d.ts.map +1 -0
  61. package/dist/src/hosts/hstack.js +45 -0
  62. package/dist/src/hosts/hstack.js.map +1 -0
  63. package/dist/src/hosts/index.d.ts +16 -0
  64. package/dist/src/hosts/index.d.ts.map +1 -0
  65. package/dist/src/hosts/index.js +40 -0
  66. package/dist/src/hosts/index.js.map +1 -0
  67. package/dist/src/hosts/spacer.d.ts +19 -0
  68. package/dist/src/hosts/spacer.d.ts.map +1 -0
  69. package/dist/src/hosts/spacer.js +28 -0
  70. package/dist/src/hosts/spacer.js.map +1 -0
  71. package/dist/src/hosts/text.d.ts +43 -0
  72. package/dist/src/hosts/text.d.ts.map +1 -0
  73. package/dist/src/hosts/text.js +148 -0
  74. package/dist/src/hosts/text.js.map +1 -0
  75. package/dist/src/hosts/vstack.d.ts +18 -0
  76. package/dist/src/hosts/vstack.d.ts.map +1 -0
  77. package/dist/src/hosts/vstack.js +45 -0
  78. package/dist/src/hosts/vstack.js.map +1 -0
  79. package/dist/src/hosts/zstack.d.ts +20 -0
  80. package/dist/src/hosts/zstack.d.ts.map +1 -0
  81. package/dist/src/hosts/zstack.js +65 -0
  82. package/dist/src/hosts/zstack.js.map +1 -0
  83. package/dist/src/index.d.ts +20 -0
  84. package/dist/src/index.d.ts.map +1 -0
  85. package/dist/src/index.js +20 -0
  86. package/dist/src/index.js.map +1 -0
  87. package/dist/src/inline/index.d.ts +32 -0
  88. package/dist/src/inline/index.d.ts.map +1 -0
  89. package/dist/src/inline/index.js +111 -0
  90. package/dist/src/inline/index.js.map +1 -0
  91. package/dist/src/jsx.d.ts +2 -0
  92. package/dist/src/jsx.d.ts.map +1 -0
  93. package/dist/src/jsx.js +4 -0
  94. package/dist/src/jsx.js.map +1 -0
  95. package/dist/src/motion/color-motion-value.d.ts +32 -0
  96. package/dist/src/motion/color-motion-value.d.ts.map +1 -0
  97. package/dist/src/motion/color-motion-value.js +80 -0
  98. package/dist/src/motion/color-motion-value.js.map +1 -0
  99. package/dist/src/motion/color.d.ts +30 -0
  100. package/dist/src/motion/color.d.ts.map +1 -0
  101. package/dist/src/motion/color.js +172 -0
  102. package/dist/src/motion/color.js.map +1 -0
  103. package/dist/src/motion/color.test.d.ts +2 -0
  104. package/dist/src/motion/color.test.d.ts.map +1 -0
  105. package/dist/src/motion/color.test.js +97 -0
  106. package/dist/src/motion/color.test.js.map +1 -0
  107. package/dist/src/motion/event-emitter.d.ts +18 -0
  108. package/dist/src/motion/event-emitter.d.ts.map +1 -0
  109. package/dist/src/motion/event-emitter.js +30 -0
  110. package/dist/src/motion/event-emitter.js.map +1 -0
  111. package/dist/src/motion/frame.d.ts +9 -0
  112. package/dist/src/motion/frame.d.ts.map +1 -0
  113. package/dist/src/motion/frame.js +51 -0
  114. package/dist/src/motion/frame.js.map +1 -0
  115. package/dist/src/motion/hooks.d.ts +75 -0
  116. package/dist/src/motion/hooks.d.ts.map +1 -0
  117. package/dist/src/motion/hooks.js +190 -0
  118. package/dist/src/motion/hooks.js.map +1 -0
  119. package/dist/src/motion/index.d.ts +4 -0
  120. package/dist/src/motion/index.d.ts.map +1 -0
  121. package/dist/src/motion/index.js +7 -0
  122. package/dist/src/motion/index.js.map +1 -0
  123. package/dist/src/motion/motion-value.d.ts +40 -0
  124. package/dist/src/motion/motion-value.d.ts.map +1 -0
  125. package/dist/src/motion/motion-value.js +109 -0
  126. package/dist/src/motion/motion-value.js.map +1 -0
  127. package/dist/src/motion/motion-value.test.d.ts +2 -0
  128. package/dist/src/motion/motion-value.test.d.ts.map +1 -0
  129. package/dist/src/motion/motion-value.test.js +177 -0
  130. package/dist/src/motion/motion-value.test.js.map +1 -0
  131. package/dist/src/motion/spring-math.d.ts +28 -0
  132. package/dist/src/motion/spring-math.d.ts.map +1 -0
  133. package/dist/src/motion/spring-math.js +81 -0
  134. package/dist/src/motion/spring-math.js.map +1 -0
  135. package/dist/src/motion/types.d.ts +25 -0
  136. package/dist/src/motion/types.d.ts.map +1 -0
  137. package/dist/src/motion/types.js +13 -0
  138. package/dist/src/motion/types.js.map +1 -0
  139. package/dist/src/output.d.ts +47 -0
  140. package/dist/src/output.d.ts.map +1 -0
  141. package/dist/src/output.js +125 -0
  142. package/dist/src/output.js.map +1 -0
  143. package/dist/src/profiler.d.ts +6 -0
  144. package/dist/src/profiler.d.ts.map +1 -0
  145. package/dist/src/profiler.js +73 -0
  146. package/dist/src/profiler.js.map +1 -0
  147. package/dist/src/reconciler/host-config.d.ts +16 -0
  148. package/dist/src/reconciler/host-config.d.ts.map +1 -0
  149. package/dist/src/reconciler/host-config.js +174 -0
  150. package/dist/src/reconciler/host-config.js.map +1 -0
  151. package/dist/src/reconciler/types.d.ts +52 -0
  152. package/dist/src/reconciler/types.d.ts.map +1 -0
  153. package/dist/src/reconciler/types.js +2 -0
  154. package/dist/src/reconciler/types.js.map +1 -0
  155. package/dist/src/renderer.d.ts +101 -0
  156. package/dist/src/renderer.d.ts.map +1 -0
  157. package/dist/src/renderer.js +509 -0
  158. package/dist/src/renderer.js.map +1 -0
  159. package/dist/src/terminal.d.ts +37 -0
  160. package/dist/src/terminal.d.ts.map +1 -0
  161. package/dist/src/terminal.js +65 -0
  162. package/dist/src/terminal.js.map +1 -0
  163. package/dist/src/test/index.d.ts +3 -0
  164. package/dist/src/test/index.d.ts.map +1 -0
  165. package/dist/src/test/index.js +3 -0
  166. package/dist/src/test/index.js.map +1 -0
  167. package/dist/src/test/mock-streams.d.ts +44 -0
  168. package/dist/src/test/mock-streams.d.ts.map +1 -0
  169. package/dist/src/test/mock-streams.js +136 -0
  170. package/dist/src/test/mock-streams.js.map +1 -0
  171. package/dist/src/test/render-tui.d.ts +47 -0
  172. package/dist/src/test/render-tui.d.ts.map +1 -0
  173. package/dist/src/test/render-tui.js +76 -0
  174. package/dist/src/test/render-tui.js.map +1 -0
  175. package/dist/src/trace/SpanTree.d.ts +10 -0
  176. package/dist/src/trace/SpanTree.d.ts.map +1 -0
  177. package/dist/src/trace/SpanTree.js +104 -0
  178. package/dist/src/trace/SpanTree.js.map +1 -0
  179. package/dist/src/trace/index.d.ts +30 -0
  180. package/dist/src/trace/index.d.ts.map +1 -0
  181. package/dist/src/trace/index.js +142 -0
  182. package/dist/src/trace/index.js.map +1 -0
  183. package/dist/src/trace/location.d.ts +9 -0
  184. package/dist/src/trace/location.d.ts.map +1 -0
  185. package/dist/src/trace/location.js +88 -0
  186. package/dist/src/trace/location.js.map +1 -0
  187. package/dist/src/trace/span-processor.d.ts +16 -0
  188. package/dist/src/trace/span-processor.d.ts.map +1 -0
  189. package/dist/src/trace/span-processor.js +54 -0
  190. package/dist/src/trace/span-processor.js.map +1 -0
  191. package/dist/src/trace/span-state.d.ts +79 -0
  192. package/dist/src/trace/span-state.d.ts.map +1 -0
  193. package/dist/src/trace/span-state.js +229 -0
  194. package/dist/src/trace/span-state.js.map +1 -0
  195. package/dist/src/trace/tui-logger.d.ts +8 -0
  196. package/dist/src/trace/tui-logger.d.ts.map +1 -0
  197. package/dist/src/trace/tui-logger.js +70 -0
  198. package/dist/src/trace/tui-logger.js.map +1 -0
  199. package/dist/src/utils/border.d.ts +31 -0
  200. package/dist/src/utils/border.d.ts.map +1 -0
  201. package/dist/src/utils/border.js +81 -0
  202. package/dist/src/utils/border.js.map +1 -0
  203. package/dist/src/utils/flex-layout.d.ts +20 -0
  204. package/dist/src/utils/flex-layout.d.ts.map +1 -0
  205. package/dist/src/utils/flex-layout.js +85 -0
  206. package/dist/src/utils/flex-layout.js.map +1 -0
  207. package/dist/src/utils/index.d.ts +5 -0
  208. package/dist/src/utils/index.d.ts.map +1 -0
  209. package/dist/src/utils/index.js +5 -0
  210. package/dist/src/utils/index.js.map +1 -0
  211. package/dist/src/utils/padding.d.ts +26 -0
  212. package/dist/src/utils/padding.d.ts.map +1 -0
  213. package/dist/src/utils/padding.js +34 -0
  214. package/dist/src/utils/padding.js.map +1 -0
  215. package/dist/src/utils/styles.d.ts +13 -0
  216. package/dist/src/utils/styles.d.ts.map +1 -0
  217. package/dist/src/utils/styles.js +5 -0
  218. package/dist/src/utils/styles.js.map +1 -0
  219. package/dist/src/visualize/index.d.ts +50 -0
  220. package/dist/src/visualize/index.d.ts.map +1 -0
  221. package/dist/src/visualize/index.js +194 -0
  222. package/dist/src/visualize/index.js.map +1 -0
  223. package/dist/tsconfig.tsbuildinfo +1 -0
  224. package/package.json +94 -0
  225. package/src/codeblock.tsx +47 -0
  226. package/src/constants.ts +2 -0
  227. package/src/debug/DiagnosticsPanel.tsx +38 -0
  228. package/src/highlight.ts +76 -0
  229. package/src/hooks/index.ts +3 -0
  230. package/src/hooks/use-keyboard.ts +37 -0
  231. package/src/hooks/use-paste.ts +14 -0
  232. package/src/hooks/useFrameStats.ts +32 -0
  233. package/src/hosts/base.ts +65 -0
  234. package/src/hosts/box.ts +105 -0
  235. package/src/hosts/canvas.ts +155 -0
  236. package/src/hosts/codeblock.ts +145 -0
  237. package/src/hosts/hstack.ts +64 -0
  238. package/src/hosts/index.ts +45 -0
  239. package/src/hosts/spacer.ts +40 -0
  240. package/src/hosts/text.ts +175 -0
  241. package/src/hosts/vstack.ts +64 -0
  242. package/src/hosts/zstack.ts +77 -0
  243. package/src/index.ts +62 -0
  244. package/src/inline/index.tsx +181 -0
  245. package/src/jsx.ts +3 -0
  246. package/src/motion/color-motion-value.ts +90 -0
  247. package/src/motion/color.test.ts +115 -0
  248. package/src/motion/color.ts +191 -0
  249. package/src/motion/event-emitter.ts +35 -0
  250. package/src/motion/frame.ts +59 -0
  251. package/src/motion/hooks.ts +237 -0
  252. package/src/motion/index.ts +17 -0
  253. package/src/motion/motion-value.test.ts +222 -0
  254. package/src/motion/motion-value.ts +140 -0
  255. package/src/motion/spring-math.ts +114 -0
  256. package/src/motion/types.ts +34 -0
  257. package/src/output.ts +156 -0
  258. package/src/profiler.ts +88 -0
  259. package/src/reconciler/host-config.ts +277 -0
  260. package/src/reconciler/types.ts +66 -0
  261. package/src/renderer.ts +661 -0
  262. package/src/terminal.ts +67 -0
  263. package/src/test/index.ts +8 -0
  264. package/src/test/mock-streams.ts +149 -0
  265. package/src/test/render-tui.ts +118 -0
  266. package/src/trace/SpanTree.tsx +195 -0
  267. package/src/trace/index.tsx +205 -0
  268. package/src/trace/location.ts +90 -0
  269. package/src/trace/span-processor.ts +65 -0
  270. package/src/trace/span-state.ts +286 -0
  271. package/src/trace/tui-logger.ts +72 -0
  272. package/src/utils/border.ts +108 -0
  273. package/src/utils/flex-layout.ts +125 -0
  274. package/src/utils/index.ts +4 -0
  275. package/src/utils/padding.ts +45 -0
  276. package/src/utils/styles.ts +14 -0
  277. 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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"}