linecraft 0.1.0 → 0.2.0

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 (283) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +313 -64
  3. package/lib/api/color.d.ts +6 -0
  4. package/lib/api/color.d.ts.map +1 -0
  5. package/lib/api/color.js +9 -0
  6. package/lib/api/color.js.map +1 -0
  7. package/lib/api/color.test.d.ts +2 -0
  8. package/lib/api/color.test.d.ts.map +1 -0
  9. package/lib/api/color.test.js +23 -0
  10. package/lib/api/color.test.js.map +1 -0
  11. package/lib/api/flex.d.ts +55 -0
  12. package/lib/api/flex.d.ts.map +1 -0
  13. package/lib/api/flex.js +86 -0
  14. package/lib/api/flex.js.map +1 -0
  15. package/lib/api/flex.test.d.ts +2 -0
  16. package/lib/api/flex.test.d.ts.map +1 -0
  17. package/lib/api/flex.test.js +82 -0
  18. package/lib/api/flex.test.js.map +1 -0
  19. package/lib/api/grid.d.ts +22 -0
  20. package/lib/api/grid.d.ts.map +1 -0
  21. package/lib/api/grid.js +65 -0
  22. package/lib/api/grid.js.map +1 -0
  23. package/lib/api/grid.test.d.ts +2 -0
  24. package/lib/api/grid.test.d.ts.map +1 -0
  25. package/lib/api/grid.test.js +48 -0
  26. package/lib/api/grid.test.js.map +1 -0
  27. package/lib/components/base.d.ts +53 -0
  28. package/lib/components/base.d.ts.map +1 -0
  29. package/lib/components/base.js +47 -0
  30. package/lib/components/base.js.map +1 -0
  31. package/lib/components/col.d.ts +35 -0
  32. package/lib/components/col.d.ts.map +1 -0
  33. package/lib/components/col.js +168 -0
  34. package/lib/components/col.js.map +1 -0
  35. package/lib/components/col.test.d.ts +2 -0
  36. package/lib/components/col.test.d.ts.map +1 -0
  37. package/lib/components/col.test.js +96 -0
  38. package/lib/components/col.test.js.map +1 -0
  39. package/lib/components/index.d.ts +3 -0
  40. package/lib/components/index.d.ts.map +1 -0
  41. package/lib/components/index.js +5 -0
  42. package/lib/components/index.js.map +1 -0
  43. package/lib/components/progress-bar-grid.d.ts +20 -0
  44. package/lib/components/progress-bar-grid.d.ts.map +1 -0
  45. package/lib/components/progress-bar-grid.js +44 -0
  46. package/lib/components/progress-bar-grid.js.map +1 -0
  47. package/lib/components/progress-bar-grid.test.d.ts +2 -0
  48. package/lib/components/progress-bar-grid.test.d.ts.map +1 -0
  49. package/lib/components/progress-bar-grid.test.js +101 -0
  50. package/lib/components/progress-bar-grid.test.js.map +1 -0
  51. package/lib/components/progress-bar.d.ts +26 -3
  52. package/lib/components/progress-bar.d.ts.map +1 -1
  53. package/lib/components/progress-bar.js +62 -6
  54. package/lib/components/progress-bar.js.map +1 -1
  55. package/lib/components/progress-bar.test.d.ts +2 -0
  56. package/lib/components/progress-bar.test.d.ts.map +1 -0
  57. package/lib/components/progress-bar.test.js +153 -0
  58. package/lib/components/progress-bar.test.js.map +1 -0
  59. package/lib/components/prompt.d.ts +14 -0
  60. package/lib/components/prompt.d.ts.map +1 -0
  61. package/lib/components/prompt.js +77 -0
  62. package/lib/components/prompt.js.map +1 -0
  63. package/lib/components/renderable.d.ts +42 -0
  64. package/lib/components/renderable.d.ts.map +1 -0
  65. package/lib/components/renderable.js +225 -0
  66. package/lib/components/renderable.js.map +1 -0
  67. package/lib/components/spinner.d.ts +2 -2
  68. package/lib/components/spinner.d.ts.map +1 -1
  69. package/lib/components/spinner.js +3 -1
  70. package/lib/components/spinner.js.map +1 -1
  71. package/lib/components/spinner.test.d.ts +2 -0
  72. package/lib/components/spinner.test.d.ts.map +1 -0
  73. package/lib/components/spinner.test.js +168 -0
  74. package/lib/components/spinner.test.js.map +1 -0
  75. package/lib/components/style.d.ts +16 -0
  76. package/lib/components/style.d.ts.map +1 -0
  77. package/lib/components/style.js +72 -0
  78. package/lib/components/style.js.map +1 -0
  79. package/lib/components/style.test.d.ts +2 -0
  80. package/lib/components/style.test.d.ts.map +1 -0
  81. package/lib/components/style.test.js +135 -0
  82. package/lib/components/style.test.js.map +1 -0
  83. package/lib/components/text.d.ts +24 -0
  84. package/lib/components/text.d.ts.map +1 -0
  85. package/lib/components/text.js +65 -0
  86. package/lib/components/text.js.map +1 -0
  87. package/lib/drawing/boxes.d.ts +33 -0
  88. package/lib/drawing/boxes.d.ts.map +1 -0
  89. package/lib/drawing/boxes.js +76 -0
  90. package/lib/drawing/boxes.js.map +1 -0
  91. package/lib/index.d.ts +18 -11
  92. package/lib/index.d.ts.map +1 -1
  93. package/lib/index.js +21 -10
  94. package/lib/index.js.map +1 -1
  95. package/lib/index.test.d.ts +2 -0
  96. package/lib/index.test.d.ts.map +1 -0
  97. package/lib/index.test.js +80 -0
  98. package/lib/index.test.js.map +1 -0
  99. package/lib/layout/flex.d.ts +39 -0
  100. package/lib/layout/flex.d.ts.map +1 -0
  101. package/lib/layout/flex.js +300 -0
  102. package/lib/layout/flex.js.map +1 -0
  103. package/lib/layout/flex.test.d.ts +2 -0
  104. package/lib/layout/flex.test.d.ts.map +1 -0
  105. package/lib/layout/flex.test.js +206 -0
  106. package/lib/layout/flex.test.js.map +1 -0
  107. package/lib/layout/grid.d.ts +53 -0
  108. package/lib/layout/grid.d.ts.map +1 -0
  109. package/lib/layout/grid.js +421 -0
  110. package/lib/layout/grid.js.map +1 -0
  111. package/lib/layout/grid.test.d.ts +2 -0
  112. package/lib/layout/grid.test.d.ts.map +1 -0
  113. package/lib/layout/grid.test.js +139 -0
  114. package/lib/layout/grid.test.js.map +1 -0
  115. package/lib/native/ansi.d.ts +104 -0
  116. package/lib/native/ansi.d.ts.map +1 -0
  117. package/lib/native/ansi.js +120 -0
  118. package/lib/native/ansi.js.map +1 -0
  119. package/lib/native/ansi.test.d.ts +2 -0
  120. package/lib/native/ansi.test.d.ts.map +1 -0
  121. package/lib/native/ansi.test.js +57 -0
  122. package/lib/native/ansi.test.js.map +1 -0
  123. package/lib/native/buffer.d.ts +32 -0
  124. package/lib/native/buffer.d.ts.map +1 -0
  125. package/lib/native/buffer.js +49 -0
  126. package/lib/native/buffer.js.map +1 -0
  127. package/lib/native/buffer.test.d.ts +2 -0
  128. package/lib/native/buffer.test.d.ts.map +1 -0
  129. package/lib/native/buffer.test.js +64 -0
  130. package/lib/native/buffer.test.js.map +1 -0
  131. package/lib/native/diff.d.ts +20 -0
  132. package/lib/native/diff.d.ts.map +1 -0
  133. package/lib/native/diff.js +33 -0
  134. package/lib/native/diff.js.map +1 -0
  135. package/lib/native/diff.test.d.ts +2 -0
  136. package/lib/native/diff.test.d.ts.map +1 -0
  137. package/lib/native/diff.test.js +106 -0
  138. package/lib/native/diff.test.js.map +1 -0
  139. package/lib/native/region-old.d.ts +117 -0
  140. package/lib/native/region-old.d.ts.map +1 -0
  141. package/lib/native/region-old.js +539 -0
  142. package/lib/native/region-old.js.map +1 -0
  143. package/lib/native/region-renderer.d.ts +167 -0
  144. package/lib/native/region-renderer.d.ts.map +1 -0
  145. package/lib/native/region-renderer.js +1238 -0
  146. package/lib/native/region-renderer.js.map +1 -0
  147. package/lib/native/region-simple.d.ts +44 -0
  148. package/lib/native/region-simple.d.ts.map +1 -0
  149. package/lib/native/region-simple.js +290 -0
  150. package/lib/native/region-simple.js.map +1 -0
  151. package/lib/native/region.d.ts +53 -0
  152. package/lib/native/region.d.ts.map +1 -0
  153. package/lib/native/region.js +426 -0
  154. package/lib/native/region.js.map +1 -0
  155. package/lib/native/region.test.d.ts +2 -0
  156. package/lib/native/region.test.d.ts.map +1 -0
  157. package/lib/native/region.test.js +248 -0
  158. package/lib/native/region.test.js.map +1 -0
  159. package/lib/native/throttle.d.ts +29 -0
  160. package/lib/native/throttle.d.ts.map +1 -0
  161. package/lib/native/throttle.js +57 -0
  162. package/lib/native/throttle.js.map +1 -0
  163. package/lib/native/throttle.test.d.ts +2 -0
  164. package/lib/native/throttle.test.d.ts.map +1 -0
  165. package/lib/native/throttle.test.js +86 -0
  166. package/lib/native/throttle.test.js.map +1 -0
  167. package/lib/native.d.ts +5 -11
  168. package/lib/native.d.ts.map +1 -1
  169. package/lib/native.js +8 -64
  170. package/lib/native.js.map +1 -1
  171. package/lib/region.d.ts +48 -5
  172. package/lib/region.d.ts.map +1 -1
  173. package/lib/region.js +474 -36
  174. package/lib/region.js.map +1 -1
  175. package/lib/region.test.d.ts +2 -0
  176. package/lib/region.test.d.ts.map +1 -0
  177. package/lib/region.test.js +227 -0
  178. package/lib/region.test.js.map +1 -0
  179. package/lib/region.visual.test.d.ts +2 -0
  180. package/lib/region.visual.test.d.ts.map +1 -0
  181. package/lib/region.visual.test.js +55 -0
  182. package/lib/region.visual.test.js.map +1 -0
  183. package/lib/test-helpers/capturable-terminal.d.ts +61 -0
  184. package/lib/test-helpers/capturable-terminal.d.ts.map +1 -0
  185. package/lib/test-helpers/capturable-terminal.js +113 -0
  186. package/lib/test-helpers/capturable-terminal.js.map +1 -0
  187. package/lib/test-helpers/capturable-terminal.test.d.ts +2 -0
  188. package/lib/test-helpers/capturable-terminal.test.d.ts.map +1 -0
  189. package/lib/test-helpers/capturable-terminal.test.js +45 -0
  190. package/lib/test-helpers/capturable-terminal.test.js.map +1 -0
  191. package/lib/test-helpers/mock-region.d.ts +21 -0
  192. package/lib/test-helpers/mock-region.d.ts.map +1 -0
  193. package/lib/test-helpers/mock-region.js +37 -0
  194. package/lib/test-helpers/mock-region.js.map +1 -0
  195. package/lib/test-helpers/virtual-terminal-diff-reflow.test.d.ts +2 -0
  196. package/lib/test-helpers/virtual-terminal-diff-reflow.test.d.ts.map +1 -0
  197. package/lib/test-helpers/virtual-terminal-diff-reflow.test.js +256 -0
  198. package/lib/test-helpers/virtual-terminal-diff-reflow.test.js.map +1 -0
  199. package/lib/test-helpers/virtual-terminal-flex-resize.test.d.ts +2 -0
  200. package/lib/test-helpers/virtual-terminal-flex-resize.test.d.ts.map +1 -0
  201. package/lib/test-helpers/virtual-terminal-flex-resize.test.js +438 -0
  202. package/lib/test-helpers/virtual-terminal-flex-resize.test.js.map +1 -0
  203. package/lib/test-helpers/virtual-terminal.d.ts +176 -0
  204. package/lib/test-helpers/virtual-terminal.d.ts.map +1 -0
  205. package/lib/test-helpers/virtual-terminal.js +492 -0
  206. package/lib/test-helpers/virtual-terminal.js.map +1 -0
  207. package/lib/test-helpers/virtual-terminal.test.d.ts +2 -0
  208. package/lib/test-helpers/virtual-terminal.test.d.ts.map +1 -0
  209. package/lib/test-helpers/virtual-terminal.test.js +219 -0
  210. package/lib/test-helpers/virtual-terminal.test.js.map +1 -0
  211. package/lib/ts/components/spinner.test.d.ts +2 -0
  212. package/lib/ts/components/spinner.test.d.ts.map +1 -0
  213. package/lib/ts/components/spinner.test.js +168 -0
  214. package/lib/ts/components/spinner.test.js.map +1 -0
  215. package/lib/ts/utils/colors.test.d.ts +2 -0
  216. package/lib/ts/utils/colors.test.d.ts.map +1 -0
  217. package/lib/ts/utils/colors.test.js +87 -0
  218. package/lib/ts/utils/colors.test.js.map +1 -0
  219. package/lib/types.d.ts +8 -2
  220. package/lib/types.d.ts.map +1 -1
  221. package/lib/types.js.map +1 -1
  222. package/lib/utils/colors-simple.d.ts +51 -0
  223. package/lib/utils/colors-simple.d.ts.map +1 -0
  224. package/lib/utils/colors-simple.js +59 -0
  225. package/lib/utils/colors-simple.js.map +1 -0
  226. package/lib/utils/colors.d.ts +1 -1
  227. package/lib/utils/colors.d.ts.map +1 -1
  228. package/lib/utils/colors.js.map +1 -1
  229. package/lib/utils/colors.test.d.ts +2 -0
  230. package/lib/utils/colors.test.d.ts.map +1 -0
  231. package/lib/utils/colors.test.js +87 -0
  232. package/lib/utils/colors.test.js.map +1 -0
  233. package/lib/utils/cursor-position.d.ts +22 -0
  234. package/lib/utils/cursor-position.d.ts.map +1 -0
  235. package/lib/utils/cursor-position.js +139 -0
  236. package/lib/utils/cursor-position.js.map +1 -0
  237. package/lib/utils/debug-log.d.ts +2 -0
  238. package/lib/utils/debug-log.d.ts.map +1 -0
  239. package/lib/utils/debug-log.js +36 -0
  240. package/lib/utils/debug-log.js.map +1 -0
  241. package/lib/utils/terminal.d.ts +27 -0
  242. package/lib/utils/terminal.d.ts.map +1 -0
  243. package/lib/utils/terminal.js +116 -0
  244. package/lib/utils/terminal.js.map +1 -0
  245. package/lib/utils/text.d.ts +21 -0
  246. package/lib/utils/text.d.ts.map +1 -0
  247. package/lib/utils/text.js +82 -0
  248. package/lib/utils/text.js.map +1 -0
  249. package/lib/utils/wait-for-spacebar.d.ts +10 -0
  250. package/lib/utils/wait-for-spacebar.d.ts.map +1 -0
  251. package/lib/utils/wait-for-spacebar.js +71 -0
  252. package/lib/utils/wait-for-spacebar.js.map +1 -0
  253. package/package.json +31 -28
  254. package/.cursor/plan.md +0 -952
  255. package/TESTING.md +0 -102
  256. package/build.zig +0 -100
  257. package/examples/basic-progress.ts +0 -21
  258. package/examples/multi-lane.ts +0 -29
  259. package/examples/spinner.ts +0 -20
  260. package/examples/test-basic.ts +0 -23
  261. package/src/ts/components/progress-bar.ts +0 -53
  262. package/src/ts/components/spinner.ts +0 -56
  263. package/src/ts/index.ts +0 -37
  264. package/src/ts/native.ts +0 -86
  265. package/src/ts/region.ts +0 -89
  266. package/src/ts/types/ffi-napi.d.ts +0 -11
  267. package/src/ts/types/ref-napi.d.ts +0 -5
  268. package/src/ts/types.ts +0 -53
  269. package/src/ts/utils/colors.ts +0 -72
  270. package/src/zig/ansi.zig +0 -21
  271. package/src/zig/buffer.zig +0 -37
  272. package/src/zig/diff.zig +0 -43
  273. package/src/zig/region.zig +0 -292
  274. package/src/zig/renderer.zig +0 -92
  275. package/src/zig/test_ansi.zig +0 -66
  276. package/src/zig/test_buffer.zig +0 -82
  277. package/src/zig/test_diff.zig +0 -220
  278. package/src/zig/test_integration.zig +0 -76
  279. package/src/zig/test_region.zig +0 -191
  280. package/src/zig/test_runner.zig +0 -27
  281. package/src/zig/test_throttle.zig +0 -59
  282. package/src/zig/throttle.zig +0 -38
  283. package/tsconfig.json +0 -21
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Move cursor to absolute position (1-based coordinates)
3
+ * Format: \x1b[row;colH
4
+ */
5
+ export declare function moveCursorTo(x: number, y: number): string;
6
+ /**
7
+ * Move cursor up N lines
8
+ * Format: \x1b[nA
9
+ */
10
+ export declare function moveCursorUp(n: number): string;
11
+ /**
12
+ * Move cursor down N lines
13
+ * Format: \x1b[nB
14
+ */
15
+ export declare function moveCursorDown(n: number): string;
16
+ /**
17
+ * Move cursor right N columns
18
+ * Format: \x1b[nC
19
+ */
20
+ export declare function moveCursorRight(n: number): string;
21
+ /**
22
+ * Move cursor left N columns
23
+ * Format: \x1b[nD
24
+ */
25
+ export declare function moveCursorLeft(n: number): string;
26
+ /**
27
+ * Move cursor to start of line (move left a large number to ensure we're at column 1)
28
+ * Format: \x1b[1000D
29
+ * This is more reliable than \r in some terminals
30
+ * From: https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
31
+ */
32
+ export declare const MOVE_TO_START_OF_LINE = "\u001B[1000D";
33
+ export declare const CLEAR_LINE = "\u001B[2K";
34
+ export declare const CLEAR_TO_END = "\u001B[0K";
35
+ export declare const CLEAR_TO_START = "\u001B[1K";
36
+ export declare const HIDE_CURSOR = "\u001B[?25l";
37
+ export declare const SHOW_CURSOR = "\u001B[?25h";
38
+ export declare const SAVE_CURSOR = "\u001B[s";
39
+ export declare const RESTORE_CURSOR = "\u001B[u";
40
+ export declare const RESET = "\u001B[0m";
41
+ /**
42
+ * Delete N lines starting from the current line
43
+ * Format: \x1b[nM (deletes n lines, shifting content up)
44
+ */
45
+ export declare function deleteLines(n: number): string;
46
+ /**
47
+ * Erase Display (ED) - Clear from cursor to end of screen
48
+ * Format: \x1b[J or \x1b[0J
49
+ * This clears everything from the cursor position to the end of the screen
50
+ */
51
+ export declare const ERASE_TO_END = "\u001B[J";
52
+ /**
53
+ * Erase Display (ED) - Clear from cursor to beginning of screen
54
+ * Format: \x1b[1J
55
+ * This clears everything from the beginning of the screen to the cursor position
56
+ */
57
+ export declare const ERASE_TO_START = "\u001B[1J";
58
+ /**
59
+ * Erase Display (ED) - Clear entire screen
60
+ * Format: \x1b[2J
61
+ * This clears the entire screen and moves cursor to home position
62
+ */
63
+ export declare const ERASE_SCREEN = "\u001B[2J";
64
+ /**
65
+ * Disable terminal auto-wrap mode (DECAWM off)
66
+ *
67
+ * When auto-wrap is disabled:
68
+ * - Text that exceeds terminal width is cut off (doesn't wrap to next line)
69
+ * - Useful for right-pinned elements like OhMyZsh prompts
70
+ * - Content stays at fixed column position, making updates easier
71
+ *
72
+ * Equivalent to: `tput rmam` (reset mode automatic margins)
73
+ * Format: \x1b[?7l
74
+ *
75
+ * IMPORTANT: Always re-enable auto-wrap after writing non-wrapping content!
76
+ * Use ENABLE_AUTO_WRAP to restore default behavior.
77
+ */
78
+ export declare const DISABLE_AUTO_WRAP = "\u001B[?7l";
79
+ /**
80
+ * Enable terminal auto-wrap mode (DECAWM on)
81
+ *
82
+ * Re-enables automatic wrapping (default terminal behavior).
83
+ * Text that exceeds terminal width will wrap to the next line.
84
+ *
85
+ * Equivalent to: `tput smam` (set mode automatic margins)
86
+ * Format: \x1b[?7h
87
+ *
88
+ * This is the default terminal state. Use this to restore after DISABLE_AUTO_WRAP.
89
+ */
90
+ export declare const ENABLE_AUTO_WRAP = "\u001B[?7h";
91
+ /**
92
+ * Query cursor position (DSR - Device Status Report)
93
+ * Format: \x1b[6n
94
+ *
95
+ * Sends a query to the terminal asking for the current cursor position.
96
+ * Terminal responds with: \x1b[row;colR (e.g., \x1b[10;5R means row 10, column 5)
97
+ *
98
+ * This can be used to query the actual cursor position after resize/scroll,
99
+ * instead of relying on SAVE/RESTORE which may become invalid.
100
+ *
101
+ * Note: Requires reading from stdin and parsing the response asynchronously.
102
+ */
103
+ export declare const QUERY_CURSOR_POSITION = "\u001B[6n";
104
+ //# sourceMappingURL=ansi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.d.ts","sourceRoot":"","sources":["../../src/native/ansi.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,iBAAe,CAAC;AAGlD,eAAO,MAAM,UAAU,cAAY,CAAC;AACpC,eAAO,MAAM,YAAY,cAAY,CAAC;AACtC,eAAO,MAAM,cAAc,cAAY,CAAC;AACxC,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,WAAW,aAAW,CAAC;AACpC,eAAO,MAAM,cAAc,aAAW,CAAC;AACvC,eAAO,MAAM,KAAK,cAAY,CAAC;AAE/B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,aAAW,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,cAAc,cAAY,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,YAAY,cAAY,CAAC;AAEtC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,eAAa,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,eAAa,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,cAAY,CAAC"}
@@ -0,0 +1,120 @@
1
+ // ANSI escape code generation - TypeScript implementation
2
+ // Optimized for Node.js string operations
3
+ /**
4
+ * Move cursor to absolute position (1-based coordinates)
5
+ * Format: \x1b[row;colH
6
+ */
7
+ export function moveCursorTo(x, y) {
8
+ // Use template literals - V8 optimizes these well
9
+ return `\x1b[${y};${x}H`;
10
+ }
11
+ /**
12
+ * Move cursor up N lines
13
+ * Format: \x1b[nA
14
+ */
15
+ export function moveCursorUp(n) {
16
+ return `\x1b[${n}A`;
17
+ }
18
+ /**
19
+ * Move cursor down N lines
20
+ * Format: \x1b[nB
21
+ */
22
+ export function moveCursorDown(n) {
23
+ return `\x1b[${n}B`;
24
+ }
25
+ /**
26
+ * Move cursor right N columns
27
+ * Format: \x1b[nC
28
+ */
29
+ export function moveCursorRight(n) {
30
+ return `\x1b[${n}C`;
31
+ }
32
+ /**
33
+ * Move cursor left N columns
34
+ * Format: \x1b[nD
35
+ */
36
+ export function moveCursorLeft(n) {
37
+ return `\x1b[${n}D`;
38
+ }
39
+ /**
40
+ * Move cursor to start of line (move left a large number to ensure we're at column 1)
41
+ * Format: \x1b[1000D
42
+ * This is more reliable than \r in some terminals
43
+ * From: https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html
44
+ */
45
+ export const MOVE_TO_START_OF_LINE = '\x1b[1000D';
46
+ // ANSI constants - pre-computed for performance
47
+ export const CLEAR_LINE = '\x1b[2K';
48
+ export const CLEAR_TO_END = '\x1b[0K';
49
+ export const CLEAR_TO_START = '\x1b[1K';
50
+ export const HIDE_CURSOR = '\x1b[?25l';
51
+ export const SHOW_CURSOR = '\x1b[?25h';
52
+ export const SAVE_CURSOR = '\x1b[s';
53
+ export const RESTORE_CURSOR = '\x1b[u';
54
+ export const RESET = '\x1b[0m';
55
+ /**
56
+ * Delete N lines starting from the current line
57
+ * Format: \x1b[nM (deletes n lines, shifting content up)
58
+ */
59
+ export function deleteLines(n) {
60
+ return `\x1b[${n}M`;
61
+ }
62
+ /**
63
+ * Erase Display (ED) - Clear from cursor to end of screen
64
+ * Format: \x1b[J or \x1b[0J
65
+ * This clears everything from the cursor position to the end of the screen
66
+ */
67
+ export const ERASE_TO_END = '\x1b[J';
68
+ /**
69
+ * Erase Display (ED) - Clear from cursor to beginning of screen
70
+ * Format: \x1b[1J
71
+ * This clears everything from the beginning of the screen to the cursor position
72
+ */
73
+ export const ERASE_TO_START = '\x1b[1J';
74
+ /**
75
+ * Erase Display (ED) - Clear entire screen
76
+ * Format: \x1b[2J
77
+ * This clears the entire screen and moves cursor to home position
78
+ */
79
+ export const ERASE_SCREEN = '\x1b[2J';
80
+ /**
81
+ * Disable terminal auto-wrap mode (DECAWM off)
82
+ *
83
+ * When auto-wrap is disabled:
84
+ * - Text that exceeds terminal width is cut off (doesn't wrap to next line)
85
+ * - Useful for right-pinned elements like OhMyZsh prompts
86
+ * - Content stays at fixed column position, making updates easier
87
+ *
88
+ * Equivalent to: `tput rmam` (reset mode automatic margins)
89
+ * Format: \x1b[?7l
90
+ *
91
+ * IMPORTANT: Always re-enable auto-wrap after writing non-wrapping content!
92
+ * Use ENABLE_AUTO_WRAP to restore default behavior.
93
+ */
94
+ export const DISABLE_AUTO_WRAP = '\x1b[?7l';
95
+ /**
96
+ * Enable terminal auto-wrap mode (DECAWM on)
97
+ *
98
+ * Re-enables automatic wrapping (default terminal behavior).
99
+ * Text that exceeds terminal width will wrap to the next line.
100
+ *
101
+ * Equivalent to: `tput smam` (set mode automatic margins)
102
+ * Format: \x1b[?7h
103
+ *
104
+ * This is the default terminal state. Use this to restore after DISABLE_AUTO_WRAP.
105
+ */
106
+ export const ENABLE_AUTO_WRAP = '\x1b[?7h';
107
+ /**
108
+ * Query cursor position (DSR - Device Status Report)
109
+ * Format: \x1b[6n
110
+ *
111
+ * Sends a query to the terminal asking for the current cursor position.
112
+ * Terminal responds with: \x1b[row;colR (e.g., \x1b[10;5R means row 10, column 5)
113
+ *
114
+ * This can be used to query the actual cursor position after resize/scroll,
115
+ * instead of relying on SAVE/RESTORE which may become invalid.
116
+ *
117
+ * Note: Requires reading from stdin and parsing the response asynchronously.
118
+ */
119
+ export const QUERY_CURSOR_POSITION = '\x1b[6n';
120
+ //# sourceMappingURL=ansi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/native/ansi.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,0CAA0C;AAE1C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,CAAS;IAC/C,kDAAkD;IAClD,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS;IACvC,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,CAAS;IACtC,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAElD,gDAAgD;AAChD,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC;AACpC,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AACvC,MAAM,CAAC,MAAM,KAAK,GAAG,SAAS,CAAC;AAE/B;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,QAAQ,CAAC,GAAG,CAAC;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AAEtC;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ansi.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.test.d.ts","sourceRoot":"","sources":["../../src/native/ansi.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,57 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import * as ansi from './ansi';
3
+ describe('ANSI', () => {
4
+ describe('moveCursorTo', () => {
5
+ it('should generate correct ANSI for cursor position', () => {
6
+ const seq = ansi.moveCursorTo(10, 5);
7
+ expect(seq).toBe('\x1b[5;10H');
8
+ });
9
+ it('should handle zero coordinates', () => {
10
+ const seq = ansi.moveCursorTo(0, 0);
11
+ expect(seq).toBe('\x1b[0;0H');
12
+ });
13
+ it('should handle large coordinates', () => {
14
+ const seq = ansi.moveCursorTo(200, 100);
15
+ expect(seq).toBe('\x1b[100;200H');
16
+ });
17
+ });
18
+ describe('moveCursorUp', () => {
19
+ it('should generate correct ANSI for moving up', () => {
20
+ const seq = ansi.moveCursorUp(3);
21
+ expect(seq).toBe('\x1b[3A');
22
+ });
23
+ it('should handle zero', () => {
24
+ const seq = ansi.moveCursorUp(0);
25
+ expect(seq).toBe('\x1b[0A');
26
+ });
27
+ });
28
+ describe('moveCursorDown', () => {
29
+ it('should generate correct ANSI for moving down', () => {
30
+ const seq = ansi.moveCursorDown(2);
31
+ expect(seq).toBe('\x1b[2B');
32
+ });
33
+ });
34
+ describe('moveCursorRight', () => {
35
+ it('should generate correct ANSI for moving right', () => {
36
+ const seq = ansi.moveCursorRight(5);
37
+ expect(seq).toBe('\x1b[5C');
38
+ });
39
+ });
40
+ describe('moveCursorLeft', () => {
41
+ it('should generate correct ANSI for moving left', () => {
42
+ const seq = ansi.moveCursorLeft(3);
43
+ expect(seq).toBe('\x1b[3D');
44
+ });
45
+ });
46
+ describe('constants', () => {
47
+ it('should have correct ANSI constants', () => {
48
+ expect(ansi.CLEAR_LINE).toBe('\x1b[2K');
49
+ expect(ansi.HIDE_CURSOR).toBe('\x1b[?25l');
50
+ expect(ansi.SHOW_CURSOR).toBe('\x1b[?25h');
51
+ expect(ansi.SAVE_CURSOR).toBe('\x1b[s');
52
+ expect(ansi.RESTORE_CURSOR).toBe('\x1b[u');
53
+ expect(ansi.RESET).toBe('\x1b[0m');
54
+ });
55
+ });
56
+ });
57
+ //# sourceMappingURL=ansi.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.test.js","sourceRoot":"","sources":["../../src/native/ansi.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * RenderBuffer batches ANSI operations to minimize syscalls.
3
+ *
4
+ * Node.js stdout.write() is buffered, but batching multiple operations
5
+ * into a single write is still more efficient than multiple small writes.
6
+ */
7
+ export declare class RenderBuffer {
8
+ private buffer;
9
+ private readonly stdout;
10
+ constructor(stdout?: NodeJS.WriteStream);
11
+ /**
12
+ * Append data to the buffer (does not write immediately)
13
+ */
14
+ write(data: string): void;
15
+ /**
16
+ * Flush all buffered data to stdout in a single write.
17
+ * This minimizes syscalls and improves performance.
18
+ *
19
+ * Note: process.stdout.write() is synchronous by default (blocks until written),
20
+ * so we don't need writeSync. For tests, we can mock stdout.write.
21
+ */
22
+ flush(): void;
23
+ /**
24
+ * Clear the buffer without writing
25
+ */
26
+ clear(): void;
27
+ /**
28
+ * Get current buffer size (for debugging)
29
+ */
30
+ get size(): number;
31
+ }
32
+ //# sourceMappingURL=buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.d.ts","sourceRoot":"","sources":["../../src/native/buffer.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAEhC,MAAM,GAAE,MAAM,CAAC,WAA4B;IAIvD;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB;;;;;;OAMG;IACH,KAAK,IAAI,IAAI;IASb;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,49 @@
1
+ // Render buffer for batching ANSI operations - TypeScript implementation
2
+ // Optimized for Node.js stdout writes
3
+ /**
4
+ * RenderBuffer batches ANSI operations to minimize syscalls.
5
+ *
6
+ * Node.js stdout.write() is buffered, but batching multiple operations
7
+ * into a single write is still more efficient than multiple small writes.
8
+ */
9
+ export class RenderBuffer {
10
+ buffer = '';
11
+ stdout;
12
+ constructor(stdout = process.stdout) {
13
+ this.stdout = stdout;
14
+ }
15
+ /**
16
+ * Append data to the buffer (does not write immediately)
17
+ */
18
+ write(data) {
19
+ this.buffer += data;
20
+ }
21
+ /**
22
+ * Flush all buffered data to stdout in a single write.
23
+ * This minimizes syscalls and improves performance.
24
+ *
25
+ * Note: process.stdout.write() is synchronous by default (blocks until written),
26
+ * so we don't need writeSync. For tests, we can mock stdout.write.
27
+ */
28
+ flush() {
29
+ if (this.buffer.length > 0) {
30
+ // process.stdout.write() is synchronous and blocks until written
31
+ // This is fine for terminal output where we want immediate rendering
32
+ this.stdout.write(this.buffer);
33
+ this.buffer = '';
34
+ }
35
+ }
36
+ /**
37
+ * Clear the buffer without writing
38
+ */
39
+ clear() {
40
+ this.buffer = '';
41
+ }
42
+ /**
43
+ * Get current buffer size (for debugging)
44
+ */
45
+ get size() {
46
+ return this.buffer.length;
47
+ }
48
+ }
49
+ //# sourceMappingURL=buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../src/native/buffer.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,sCAAsC;AAEtC;;;;;GAKG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,GAAW,EAAE,CAAC;IACX,MAAM,CAAqB;IAE5C,YAAY,SAA6B,OAAO,CAAC,MAAM;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IACtB,CAAC;IAED;;;;;;OAMG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,iEAAiE;YACjE,qEAAqE;YACrE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACnB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=buffer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.test.d.ts","sourceRoot":"","sources":["../../src/native/buffer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { RenderBuffer } from './buffer';
3
+ describe('RenderBuffer', () => {
4
+ describe('initialization', () => {
5
+ it('should initialize with empty buffer', () => {
6
+ const buffer = new RenderBuffer();
7
+ expect(buffer.size).toBe(0);
8
+ });
9
+ it('should use process.stdout by default', () => {
10
+ const buffer = new RenderBuffer();
11
+ expect(buffer).toBeDefined();
12
+ expect(buffer.size).toBe(0);
13
+ });
14
+ });
15
+ describe('write', () => {
16
+ it('should append data to buffer', () => {
17
+ const buffer = new RenderBuffer();
18
+ buffer.write('hello');
19
+ buffer.write(' world');
20
+ expect(buffer.size).toBe(11);
21
+ });
22
+ it('should handle large content', () => {
23
+ const buffer = new RenderBuffer();
24
+ const largeContent = 'x'.repeat(1000);
25
+ buffer.write(largeContent);
26
+ expect(buffer.size).toBe(1000);
27
+ });
28
+ });
29
+ describe('clear', () => {
30
+ it('should clear the buffer', () => {
31
+ const buffer = new RenderBuffer();
32
+ buffer.write('test');
33
+ buffer.clear();
34
+ expect(buffer.size).toBe(0);
35
+ });
36
+ });
37
+ describe('flush', () => {
38
+ it('should write buffered data to stdout', () => {
39
+ const buffer = new RenderBuffer();
40
+ buffer.write('hello');
41
+ buffer.write(' world');
42
+ buffer.flush();
43
+ // Should have written to stdout (visible in test output)
44
+ expect(buffer.size).toBe(0);
45
+ });
46
+ it('should not write if buffer is empty', () => {
47
+ const buffer = new RenderBuffer();
48
+ buffer.flush();
49
+ // Should not throw
50
+ expect(buffer.size).toBe(0);
51
+ });
52
+ it('should handle multiple writes and flush', () => {
53
+ const buffer = new RenderBuffer();
54
+ buffer.write('part1');
55
+ buffer.write('part2');
56
+ buffer.write('part3');
57
+ expect(buffer.size).toBe(15);
58
+ buffer.flush();
59
+ // Should have written to stdout (visible in test output)
60
+ expect(buffer.size).toBe(0);
61
+ });
62
+ });
63
+ });
64
+ //# sourceMappingURL=buffer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer.test.js","sourceRoot":"","sources":["../../src/native/buffer.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export type DiffOp = {
2
+ type: 'no_change';
3
+ } | {
4
+ type: 'update_line';
5
+ line: number;
6
+ content: string;
7
+ } | {
8
+ type: 'delete_line';
9
+ line: number;
10
+ } | {
11
+ type: 'insert_line';
12
+ line: number;
13
+ content: string;
14
+ };
15
+ /**
16
+ * Diff two frames to generate minimal update operations.
17
+ * This minimizes the number of writes to stdout by only updating changed lines.
18
+ */
19
+ export declare function diffFrames(prev: string[], curr: string[]): DiffOp[];
20
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/native/diff.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAyBnE"}
@@ -0,0 +1,33 @@
1
+ // Diffing algorithm for efficient updates - TypeScript implementation
2
+ // Optimized for Node.js performance
3
+ /**
4
+ * Diff two frames to generate minimal update operations.
5
+ * This minimizes the number of writes to stdout by only updating changed lines.
6
+ */
7
+ export function diffFrames(prev, curr) {
8
+ const ops = [];
9
+ const maxLen = Math.max(prev.length, curr.length);
10
+ for (let i = 0; i < maxLen; i++) {
11
+ const prevLine = i < prev.length ? prev[i] : null;
12
+ const currLine = i < curr.length ? curr[i] : null;
13
+ if (prevLine === null && currLine !== null) {
14
+ // Line inserted
15
+ ops.push({ type: 'insert_line', line: i, content: currLine });
16
+ }
17
+ else if (prevLine !== null && currLine === null) {
18
+ // Line deleted
19
+ ops.push({ type: 'delete_line', line: i });
20
+ }
21
+ else if (prevLine !== null && currLine !== null) {
22
+ // Check if line changed (use strict equality for fast comparison)
23
+ if (prevLine !== currLine) {
24
+ ops.push({ type: 'update_line', line: i, content: currLine });
25
+ }
26
+ else {
27
+ ops.push({ type: 'no_change' });
28
+ }
29
+ }
30
+ }
31
+ return ops;
32
+ }
33
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/native/diff.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,oCAAoC;AAQpC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,IAAc,EAAE,IAAc;IACvD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAElD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAClD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAElD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC3C,gBAAgB;YAChB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChE,CAAC;aAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAClD,eAAe;YACf,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAClD,kEAAkE;YAClE,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1B,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAChE,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diff.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.test.d.ts","sourceRoot":"","sources":["../../src/native/diff.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { diffFrames } from './diff';
3
+ describe('diffFrames', () => {
4
+ describe('identical frames', () => {
5
+ it('should return no_change ops for identical frames', () => {
6
+ const prev = ['line1', 'line2'];
7
+ const curr = ['line1', 'line2'];
8
+ const ops = diffFrames(prev, curr);
9
+ expect(ops).toHaveLength(2);
10
+ expect(ops[0].type).toBe('no_change');
11
+ expect(ops[1].type).toBe('no_change');
12
+ });
13
+ });
14
+ describe('changed line', () => {
15
+ it('should detect line updates', () => {
16
+ const prev = ['line1', 'line2'];
17
+ const curr = ['line1', 'line2_changed'];
18
+ const ops = diffFrames(prev, curr);
19
+ expect(ops).toHaveLength(2);
20
+ expect(ops[0].type).toBe('no_change');
21
+ expect(ops[1].type).toBe('update_line');
22
+ if (ops[1].type === 'update_line') {
23
+ expect(ops[1].line).toBe(1);
24
+ expect(ops[1].content).toBe('line2_changed');
25
+ }
26
+ });
27
+ });
28
+ describe('inserted line', () => {
29
+ it('should detect new lines', () => {
30
+ const prev = ['line1'];
31
+ const curr = ['line1', 'line2'];
32
+ const ops = diffFrames(prev, curr);
33
+ expect(ops).toHaveLength(2);
34
+ expect(ops[0].type).toBe('no_change');
35
+ expect(ops[1].type).toBe('insert_line');
36
+ if (ops[1].type === 'insert_line') {
37
+ expect(ops[1].line).toBe(1);
38
+ expect(ops[1].content).toBe('line2');
39
+ }
40
+ });
41
+ });
42
+ describe('deleted line', () => {
43
+ it('should detect removed lines', () => {
44
+ const prev = ['line1', 'line2'];
45
+ const curr = ['line1'];
46
+ const ops = diffFrames(prev, curr);
47
+ expect(ops).toHaveLength(2);
48
+ expect(ops[0].type).toBe('no_change');
49
+ expect(ops[1].type).toBe('delete_line');
50
+ if (ops[1].type === 'delete_line') {
51
+ expect(ops[1].line).toBe(1);
52
+ }
53
+ });
54
+ });
55
+ describe('multiple changes', () => {
56
+ it('should handle complex diffs', () => {
57
+ const prev = ['line1', 'line2', 'line3'];
58
+ const curr = ['line1_changed', 'line2', 'line3', 'line4'];
59
+ const ops = diffFrames(prev, curr);
60
+ expect(ops).toHaveLength(4);
61
+ expect(ops[0].type).toBe('update_line');
62
+ expect(ops[1].type).toBe('no_change');
63
+ expect(ops[2].type).toBe('no_change');
64
+ expect(ops[3].type).toBe('insert_line');
65
+ });
66
+ });
67
+ describe('empty frames', () => {
68
+ it('should handle empty frames', () => {
69
+ const prev = [];
70
+ const curr = [];
71
+ const ops = diffFrames(prev, curr);
72
+ expect(ops).toHaveLength(0);
73
+ });
74
+ });
75
+ describe('completely different frames', () => {
76
+ it('should detect all changes', () => {
77
+ const prev = ['old1', 'old2'];
78
+ const curr = ['new1', 'new2'];
79
+ const ops = diffFrames(prev, curr);
80
+ expect(ops).toHaveLength(2);
81
+ expect(ops[0].type).toBe('update_line');
82
+ expect(ops[1].type).toBe('update_line');
83
+ });
84
+ });
85
+ describe('edge cases', () => {
86
+ it('should handle prev longer than curr', () => {
87
+ const prev = ['line1', 'line2', 'line3'];
88
+ const curr = ['line1'];
89
+ const ops = diffFrames(prev, curr);
90
+ expect(ops).toHaveLength(3);
91
+ expect(ops[0].type).toBe('no_change');
92
+ expect(ops[1].type).toBe('delete_line');
93
+ expect(ops[2].type).toBe('delete_line');
94
+ });
95
+ it('should handle curr longer than prev', () => {
96
+ const prev = ['line1'];
97
+ const curr = ['line1', 'line2', 'line3'];
98
+ const ops = diffFrames(prev, curr);
99
+ expect(ops).toHaveLength(3);
100
+ expect(ops[0].type).toBe('no_change');
101
+ expect(ops[1].type).toBe('insert_line');
102
+ expect(ops[2].type).toBe('insert_line');
103
+ });
104
+ });
105
+ });
106
+ //# sourceMappingURL=diff.test.js.map