wraithterm 0.0.0-stage → 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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -2
  3. package/css/xterm.css +285 -0
  4. package/dist/index.d.ts +88 -0
  5. package/dist/index.js +707 -0
  6. package/dist/keycodes.d.ts +1 -0
  7. package/dist/keycodes.js +179 -0
  8. package/dist/protocol.d.ts +117 -0
  9. package/dist/protocol.js +1 -0
  10. package/dist/types.d.ts +66 -0
  11. package/dist/types.js +19 -0
  12. package/dist/vite.d.ts +11 -0
  13. package/dist/vite.js +11 -0
  14. package/dist/worker.d.ts +1 -0
  15. package/dist/worker.js +302 -0
  16. package/dist/xterm-api.d.ts +1956 -0
  17. package/dist/xterm-atlas.d.ts +39 -0
  18. package/dist/xterm-atlas.js +176 -0
  19. package/dist/xterm-internals.d.ts +120 -0
  20. package/dist/xterm-internals.js +19 -0
  21. package/dist/xterm-renderer.d.ts +77 -0
  22. package/dist/xterm-renderer.js +540 -0
  23. package/dist/xterm.d.ts +17 -0
  24. package/dist/xterm.js +35 -0
  25. package/docs/ARCHITECTURE.md +179 -0
  26. package/docs/COMPATIBILITY.md +66 -0
  27. package/docs/PERFORMANCE.md +122 -0
  28. package/docs/TOP_TIER.md +124 -0
  29. package/docs/XTERM_API.md +163 -0
  30. package/docs/compatibility/xterm-chrome.json +34 -0
  31. package/licenses/Ghostty-MIT.txt +21 -0
  32. package/licenses/JetBrainsMono-OFL.txt +93 -0
  33. package/licenses/NOTICE.md +93 -0
  34. package/licenses/Zig-MIT.txt +21 -0
  35. package/licenses/rust/arrayvec-0.7.8/LICENSE-APACHE +201 -0
  36. package/licenses/rust/arrayvec-0.7.8/LICENSE-MIT +25 -0
  37. package/licenses/rust/autocfg-1.5.1/LICENSE-APACHE +201 -0
  38. package/licenses/rust/autocfg-1.5.1/LICENSE-MIT +25 -0
  39. package/licenses/rust/bit-set-0.10.0/LICENSE-APACHE +201 -0
  40. package/licenses/rust/bit-set-0.10.0/LICENSE-MIT +25 -0
  41. package/licenses/rust/bit-vec-0.9.1/LICENSE-APACHE +201 -0
  42. package/licenses/rust/bit-vec-0.9.1/LICENSE-MIT +25 -0
  43. package/licenses/rust/bitflags-2.13.2/LICENSE-APACHE +201 -0
  44. package/licenses/rust/bitflags-2.13.2/LICENSE-MIT +25 -0
  45. package/licenses/rust/bumpalo-3.20.3/LICENSE-APACHE +201 -0
  46. package/licenses/rust/bumpalo-3.20.3/LICENSE-MIT +25 -0
  47. package/licenses/rust/bytemuck-1.25.2/LICENSE-APACHE +61 -0
  48. package/licenses/rust/bytemuck-1.25.2/LICENSE-MIT +9 -0
  49. package/licenses/rust/bytemuck-1.25.2/LICENSE-ZLIB +11 -0
  50. package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-APACHE +61 -0
  51. package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-MIT +9 -0
  52. package/licenses/rust/bytemuck_derive-1.12.1/LICENSE-ZLIB +11 -0
  53. package/licenses/rust/cfg-if-1.0.5/LICENSE-APACHE +201 -0
  54. package/licenses/rust/cfg-if-1.0.5/LICENSE-MIT +25 -0
  55. package/licenses/rust/cfg_aliases-0.2.2/LICENSE +9 -0
  56. package/licenses/rust/codespan-reporting-0.13.1/LICENSE +201 -0
  57. package/licenses/rust/crunchy-0.2.4/LICENSE +21 -0
  58. package/licenses/rust/document-features-0.2.12/LICENSE-APACHE +73 -0
  59. package/licenses/rust/document-features-0.2.12/LICENSE-MIT +19 -0
  60. package/licenses/rust/equivalent-1.0.2/LICENSE-APACHE +201 -0
  61. package/licenses/rust/equivalent-1.0.2/LICENSE-MIT +25 -0
  62. package/licenses/rust/foldhash-0.2.0/LICENSE +19 -0
  63. package/licenses/rust/font-types-0.12.6/LICENSE-APACHE +67 -0
  64. package/licenses/rust/font-types-0.12.6/LICENSE-MIT +25 -0
  65. package/licenses/rust/futures-core-0.3.34/LICENSE-APACHE +202 -0
  66. package/licenses/rust/futures-core-0.3.34/LICENSE-MIT +26 -0
  67. package/licenses/rust/futures-task-0.3.34/LICENSE-APACHE +202 -0
  68. package/licenses/rust/futures-task-0.3.34/LICENSE-MIT +26 -0
  69. package/licenses/rust/futures-util-0.3.34/LICENSE-APACHE +202 -0
  70. package/licenses/rust/futures-util-0.3.34/LICENSE-MIT +26 -0
  71. package/licenses/rust/half-2.7.1/LICENSE-APACHE +176 -0
  72. package/licenses/rust/half-2.7.1/LICENSE-MIT +19 -0
  73. package/licenses/rust/harfrust-0.13.3/LICENSE +22 -0
  74. package/licenses/rust/hashbrown-0.17.1/LICENSE-APACHE +201 -0
  75. package/licenses/rust/hashbrown-0.17.1/LICENSE-MIT +25 -0
  76. package/licenses/rust/indexmap-2.14.2/LICENSE-APACHE +201 -0
  77. package/licenses/rust/indexmap-2.14.2/LICENSE-MIT +25 -0
  78. package/licenses/rust/js-sys-0.3.106/LICENSE-APACHE +201 -0
  79. package/licenses/rust/js-sys-0.3.106/LICENSE-MIT +25 -0
  80. package/licenses/rust/libc-0.2.190/LICENSE-APACHE +176 -0
  81. package/licenses/rust/libc-0.2.190/LICENSE-MIT +25 -0
  82. package/licenses/rust/libloading-0.8.9/LICENSE +12 -0
  83. package/licenses/rust/libm-0.2.16/LICENSE.txt +258 -0
  84. package/licenses/rust/litrs-1.0.0/LICENSE-APACHE +176 -0
  85. package/licenses/rust/litrs-1.0.0/LICENSE-MIT +25 -0
  86. package/licenses/rust/lock_api-0.4.14/LICENSE-APACHE +201 -0
  87. package/licenses/rust/lock_api-0.4.14/LICENSE-MIT +25 -0
  88. package/licenses/rust/log-0.4.34/LICENSE-APACHE +201 -0
  89. package/licenses/rust/log-0.4.34/LICENSE-MIT +25 -0
  90. package/licenses/rust/naga-30.0.1/LICENSE.APACHE +176 -0
  91. package/licenses/rust/naga-30.0.1/LICENSE.MIT +21 -0
  92. package/licenses/rust/naga-types-30.0.1/LICENSE.APACHE +176 -0
  93. package/licenses/rust/naga-types-30.0.1/LICENSE.MIT +21 -0
  94. package/licenses/rust/num-traits-0.2.19/LICENSE-APACHE +201 -0
  95. package/licenses/rust/num-traits-0.2.19/LICENSE-MIT +25 -0
  96. package/licenses/rust/once_cell-1.21.4/LICENSE-APACHE +201 -0
  97. package/licenses/rust/once_cell-1.21.4/LICENSE-MIT +23 -0
  98. package/licenses/rust/parking_lot-0.12.5/LICENSE-APACHE +201 -0
  99. package/licenses/rust/parking_lot-0.12.5/LICENSE-MIT +25 -0
  100. package/licenses/rust/parking_lot_core-0.9.12/LICENSE-APACHE +201 -0
  101. package/licenses/rust/parking_lot_core-0.9.12/LICENSE-MIT +25 -0
  102. package/licenses/rust/pin-project-lite-0.2.17/LICENSE-APACHE +177 -0
  103. package/licenses/rust/pin-project-lite-0.2.17/LICENSE-MIT +23 -0
  104. package/licenses/rust/portable-atomic-1.15.0/LICENSE-APACHE +177 -0
  105. package/licenses/rust/portable-atomic-1.15.0/LICENSE-MIT +23 -0
  106. package/licenses/rust/portable-atomic-util-0.2.8/LICENSE-APACHE +177 -0
  107. package/licenses/rust/portable-atomic-util-0.2.8/LICENSE-MIT +23 -0
  108. package/licenses/rust/proc-macro2-1.0.107/LICENSE-APACHE +176 -0
  109. package/licenses/rust/proc-macro2-1.0.107/LICENSE-MIT +23 -0
  110. package/licenses/rust/quote-1.0.47/LICENSE-APACHE +176 -0
  111. package/licenses/rust/quote-1.0.47/LICENSE-MIT +23 -0
  112. package/licenses/rust/raw-window-handle-0.6.2/LICENSE-APACHE.md +177 -0
  113. package/licenses/rust/raw-window-handle-0.6.2/LICENSE-MIT.md +21 -0
  114. package/licenses/rust/raw-window-handle-0.6.2/LICENSE-ZLIB.md +11 -0
  115. package/licenses/rust/read-fonts-0.41.0/LICENSE-APACHE +67 -0
  116. package/licenses/rust/read-fonts-0.41.0/LICENSE-MIT +25 -0
  117. package/licenses/rust/read-fonts-0.43.3/LICENSE-APACHE +67 -0
  118. package/licenses/rust/read-fonts-0.43.3/LICENSE-MIT +25 -0
  119. package/licenses/rust/redox_syscall-0.5.18/LICENSE +22 -0
  120. package/licenses/rust/renderdoc-sys-1.1.0/LICENSE-APACHE +201 -0
  121. package/licenses/rust/renderdoc-sys-1.1.0/LICENSE-MIT +25 -0
  122. package/licenses/rust/rustc-hash-1.1.0/LICENSE-APACHE +201 -0
  123. package/licenses/rust/rustc-hash-1.1.0/LICENSE-MIT +23 -0
  124. package/licenses/rust/rustversion-1.0.23/LICENSE-APACHE +176 -0
  125. package/licenses/rust/rustversion-1.0.23/LICENSE-MIT +23 -0
  126. package/licenses/rust/scopeguard-1.2.0/LICENSE-APACHE +201 -0
  127. package/licenses/rust/scopeguard-1.2.0/LICENSE-MIT +25 -0
  128. package/licenses/rust/serde-1.0.229/LICENSE-APACHE +176 -0
  129. package/licenses/rust/serde-1.0.229/LICENSE-MIT +23 -0
  130. package/licenses/rust/serde_core-1.0.229/LICENSE-APACHE +176 -0
  131. package/licenses/rust/serde_core-1.0.229/LICENSE-MIT +23 -0
  132. package/licenses/rust/serde_derive-1.0.229/LICENSE-APACHE +176 -0
  133. package/licenses/rust/serde_derive-1.0.229/LICENSE-MIT +23 -0
  134. package/licenses/rust/skrifa-0.44.0/LICENSE-APACHE +67 -0
  135. package/licenses/rust/skrifa-0.44.0/LICENSE-MIT +25 -0
  136. package/licenses/rust/slab-0.4.12/LICENSE +25 -0
  137. package/licenses/rust/smallvec-1.16.2/LICENSE-APACHE +201 -0
  138. package/licenses/rust/smallvec-1.16.2/LICENSE-MIT +25 -0
  139. package/licenses/rust/static_assertions-1.1.0/LICENSE-APACHE +202 -0
  140. package/licenses/rust/static_assertions-1.1.0/LICENSE-MIT +21 -0
  141. package/licenses/rust/swash-0.2.10/LICENSE-APACHE +201 -0
  142. package/licenses/rust/swash-0.2.10/LICENSE-MIT +25 -0
  143. package/licenses/rust/syn-2.0.119/LICENSE-APACHE +176 -0
  144. package/licenses/rust/syn-2.0.119/LICENSE-MIT +23 -0
  145. package/licenses/rust/syn-3.0.6/LICENSE-APACHE +176 -0
  146. package/licenses/rust/syn-3.0.6/LICENSE-MIT +23 -0
  147. package/licenses/rust/termcolor-1.4.1/COPYING +3 -0
  148. package/licenses/rust/termcolor-1.4.1/LICENSE-MIT +21 -0
  149. package/licenses/rust/termcolor-1.4.1/UNLICENSE +24 -0
  150. package/licenses/rust/thiserror-2.0.21/LICENSE-APACHE +176 -0
  151. package/licenses/rust/thiserror-2.0.21/LICENSE-MIT +23 -0
  152. package/licenses/rust/thiserror-impl-2.0.21/LICENSE-APACHE +176 -0
  153. package/licenses/rust/thiserror-impl-2.0.21/LICENSE-MIT +23 -0
  154. package/licenses/rust/tokio-1.53.2/LICENSE +21 -0
  155. package/licenses/rust/unicode-ident-1.0.26/LICENSE-APACHE +176 -0
  156. package/licenses/rust/unicode-ident-1.0.26/LICENSE-MIT +23 -0
  157. package/licenses/rust/unicode-ident-1.0.26/LICENSE-UNICODE +39 -0
  158. package/licenses/rust/unicode-width-0.2.2/LICENSE-APACHE +201 -0
  159. package/licenses/rust/unicode-width-0.2.2/LICENSE-MIT +25 -0
  160. package/licenses/rust/wasm-bindgen-0.2.129/LICENSE-APACHE +201 -0
  161. package/licenses/rust/wasm-bindgen-0.2.129/LICENSE-MIT +25 -0
  162. package/licenses/rust/wasm-bindgen-futures-0.4.79/LICENSE-APACHE +201 -0
  163. package/licenses/rust/wasm-bindgen-futures-0.4.79/LICENSE-MIT +25 -0
  164. package/licenses/rust/wasm-bindgen-macro-0.2.129/LICENSE-APACHE +201 -0
  165. package/licenses/rust/wasm-bindgen-macro-0.2.129/LICENSE-MIT +25 -0
  166. package/licenses/rust/wasm-bindgen-macro-support-0.2.129/LICENSE-APACHE +201 -0
  167. package/licenses/rust/wasm-bindgen-macro-support-0.2.129/LICENSE-MIT +25 -0
  168. package/licenses/rust/wasm-bindgen-shared-0.2.129/LICENSE-APACHE +201 -0
  169. package/licenses/rust/wasm-bindgen-shared-0.2.129/LICENSE-MIT +25 -0
  170. package/licenses/rust/web-sys-0.3.106/LICENSE-APACHE +201 -0
  171. package/licenses/rust/web-sys-0.3.106/LICENSE-MIT +25 -0
  172. package/licenses/rust/wgpu-30.0.1/LICENSE.APACHE +176 -0
  173. package/licenses/rust/wgpu-30.0.1/LICENSE.MIT +21 -0
  174. package/licenses/rust/wgpu-core-30.0.1/LICENSE.APACHE +176 -0
  175. package/licenses/rust/wgpu-core-30.0.1/LICENSE.MIT +21 -0
  176. package/licenses/rust/wgpu-core-deps-windows-linux-android-30.0.1/LICENSE.APACHE +176 -0
  177. package/licenses/rust/wgpu-core-deps-windows-linux-android-30.0.1/LICENSE.MIT +21 -0
  178. package/licenses/rust/wgpu-hal-30.0.1/LICENSE.APACHE +176 -0
  179. package/licenses/rust/wgpu-hal-30.0.1/LICENSE.MIT +21 -0
  180. package/licenses/rust/wgpu-naga-bridge-30.0.1/LICENSE.APACHE +176 -0
  181. package/licenses/rust/wgpu-naga-bridge-30.0.1/LICENSE.MIT +21 -0
  182. package/licenses/rust/wgpu-types-30.0.1/LICENSE.APACHE +176 -0
  183. package/licenses/rust/wgpu-types-30.0.1/LICENSE.MIT +21 -0
  184. package/licenses/rust/winapi-util-0.1.11/COPYING +3 -0
  185. package/licenses/rust/winapi-util-0.1.11/LICENSE-MIT +21 -0
  186. package/licenses/rust/winapi-util-0.1.11/UNLICENSE +24 -0
  187. package/licenses/rust/windows-link-0.2.1/license-apache-2.0 +201 -0
  188. package/licenses/rust/windows-link-0.2.1/license-mit +21 -0
  189. package/licenses/rust/windows-sys-0.61.2/license-apache-2.0 +201 -0
  190. package/licenses/rust/windows-sys-0.61.2/license-mit +21 -0
  191. package/licenses/rust/yazi-0.2.1/LICENSE-APACHE +201 -0
  192. package/licenses/rust/yazi-0.2.1/LICENSE-MIT +19 -0
  193. package/licenses/rust/zeno-0.3.3/LICENSE-APACHE +201 -0
  194. package/licenses/rust/zeno-0.3.3/LICENSE-MIT +25 -0
  195. package/licenses/rust/zerocopy-0.8.62/LICENSE-APACHE +202 -0
  196. package/licenses/rust/zerocopy-0.8.62/LICENSE-BSD +24 -0
  197. package/licenses/rust/zerocopy-0.8.62/LICENSE-MIT +26 -0
  198. package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-APACHE +202 -0
  199. package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-BSD +24 -0
  200. package/licenses/rust/zerocopy-derive-0.8.62/LICENSE-MIT +26 -0
  201. package/licenses/xterm-MIT.txt +21 -0
  202. package/package.json +69 -4
  203. package/wasm/Ghostty-MIT.txt +21 -0
  204. package/wasm/JetBrainsMono-Regular.ttf +0 -0
  205. package/wasm/OFL.txt +93 -0
  206. package/wasm/optimization.json +6 -0
  207. package/wasm/wraith_engine.d.ts +176 -0
  208. package/wasm/wraith_engine.js +2057 -0
  209. package/wasm/wraith_engine_bg.wasm +0 -0
  210. package/wasm/wraith_engine_bg.wasm.d.ts +71 -0
package/dist/worker.js ADDED
@@ -0,0 +1,302 @@
1
+ /// <reference lib="webworker" />
2
+ import init, { Engine } from '../wasm/wraith_engine.js';
3
+ import { percentile } from './types.js';
4
+ const scope = self;
5
+ let engine;
6
+ let cols = 80, rows = 24;
7
+ let scheduled = false, visible = true, busy = false, disposed = false;
8
+ let raf = 0, holdTimer;
9
+ let samples = [], frameTimes = [];
10
+ let cursor = '';
11
+ let mouseTracking = false;
12
+ let serial = Promise.resolve();
13
+ const encoder = new TextEncoder(), decoder = new TextDecoder();
14
+ function post(reply, transfers = []) {
15
+ scope.postMessage(reply, transfers);
16
+ }
17
+ function result(id, value) {
18
+ post({ type: 'result', id, value });
19
+ }
20
+ function releaseAbandonedHold() {
21
+ if (!engine || disposed)
22
+ return;
23
+ if (busy) {
24
+ holdTimer = setTimeout(releaseAbandonedHold, 16);
25
+ return;
26
+ }
27
+ engine.release_hold();
28
+ schedule();
29
+ }
30
+ function drain() {
31
+ for (const [kind, bytes] of engine.take_events()) {
32
+ if (kind === 0)
33
+ post({ type: 'data', bytes }, [bytes.buffer]);
34
+ if (kind === 1)
35
+ post({ type: 'title', title: decoder.decode(bytes) });
36
+ if (kind === 2)
37
+ post({ type: 'bell' });
38
+ if (kind === 3) {
39
+ clearTimeout(holdTimer);
40
+ if (bytes[0])
41
+ holdTimer = setTimeout(releaseAbandonedHold, 150);
42
+ }
43
+ }
44
+ const mouse = engine.mode(1000) || engine.mode(1002) || engine.mode(1003) || engine.mode(9);
45
+ if (mouse !== mouseTracking) {
46
+ mouseTracking = mouse;
47
+ post({ type: 'modes', mouse });
48
+ }
49
+ }
50
+ function draw(time, force = false) {
51
+ const start = performance.now();
52
+ if (engine.render(time, force)) {
53
+ samples.push(performance.now() - start);
54
+ frameTimes.push(time);
55
+ if (samples.length > 512)
56
+ samples.shift();
57
+ while (frameTimes.length && frameTimes[0] < time - 1000)
58
+ frameTimes.shift();
59
+ const x = engine.cursor_x, y = engine.cursor_y, next = `${x},${y}`;
60
+ if (next !== cursor) {
61
+ cursor = next;
62
+ post({ type: 'cursor', x, y });
63
+ }
64
+ }
65
+ if (engine.needs_present)
66
+ schedule();
67
+ }
68
+ function schedule() {
69
+ if (!engine || scheduled || disposed || !visible || busy)
70
+ return;
71
+ scheduled = true;
72
+ raf = scope.requestAnimationFrame((time) => {
73
+ scheduled = false;
74
+ // An async GPU fence holds a Rust borrow across await. A previously queued
75
+ // animation callback must not enter the engine until that borrow ends.
76
+ if (busy || !engine || !visible || disposed)
77
+ return;
78
+ try {
79
+ draw(time);
80
+ }
81
+ catch (error) {
82
+ post({ type: 'error', error: String(error) });
83
+ }
84
+ });
85
+ }
86
+ const blinkTimer = setInterval(() => schedule(), 250);
87
+ const statsTimer = setInterval(() => {
88
+ if (engine && !disposed && !busy)
89
+ post({
90
+ type: 'stats',
91
+ values: Array.from(engine.metrics()),
92
+ samples: [...samples],
93
+ fps: frameTimes.filter((t) => t > performance.now() - 1000).length,
94
+ adapter: engine.adapter,
95
+ });
96
+ }, 500);
97
+ function payload(workload, iteration) {
98
+ if (workload === 'sparse')
99
+ return encoder.encode(`\x1b[${(iteration % rows) + 1};1H\x1b[38;5;${(iteration % 216) + 16}mframe ${String(iteration).padStart(8, '0')}\x1b[0m`);
100
+ if (workload === 'scroll')
101
+ return encoder.encode(`\x1b[${rows};1H\r\n\x1b[38;5;${(iteration % 216) + 16}m${String(iteration).padStart(8, '0')} ${'stream '.repeat(Math.ceil(cols / 7)).slice(0, cols - 10)}\x1b[0m`);
102
+ let output = '\x1b[H';
103
+ for (let row = 0; row < rows; row++) {
104
+ output += `\x1b[${row + 1};1H\x1b[38;5;${((iteration + row) % 216) + 16}m`;
105
+ for (let col = 0; col < cols; col++)
106
+ output += String.fromCharCode(33 + ((col + row + iteration) % 94));
107
+ }
108
+ return encoder.encode(output + '\x1b[0m');
109
+ }
110
+ async function benchmark(options) {
111
+ const count = Math.max(32, Math.min(10000, Math.floor(options.frames ?? 512)));
112
+ const warmup = Math.max(0, Math.min(512, Math.floor(options.warmup ?? 64)));
113
+ const workload = options.workload ?? 'dense';
114
+ if (!['dense', 'sparse', 'scroll'].includes(workload))
115
+ throw new Error('Unknown benchmark workload');
116
+ if (engine.mode(1049))
117
+ throw new Error('Run benchmarks outside an active full-screen terminal application');
118
+ if (engine.mode(2026))
119
+ throw new Error('Wait for synchronized output to finish before benchmarking');
120
+ const cursorWasVisible = engine.mode(25);
121
+ busy = true;
122
+ clearTimeout(holdTimer);
123
+ if (scheduled) {
124
+ scope.cancelAnimationFrame(raf);
125
+ scheduled = false;
126
+ }
127
+ const durations = [];
128
+ let bytes = 0, completed = 0;
129
+ try {
130
+ engine.write(encoder.encode('\x1b[?1049h\x1b[?25l'));
131
+ // Generate inputs outside the timed region. The same 64-frame cycle is
132
+ // reproducible; timestamps measure parser + snapshot + encode + submit.
133
+ const inputs = Array.from({ length: 64 }, (_, i) => payload(workload, i));
134
+ for (let i = 0; i < warmup; i++) {
135
+ engine.write(inputs[i % 64]);
136
+ engine.render(performance.now(), true);
137
+ if ((i + 1) % 32 === 0)
138
+ await engine.fence();
139
+ }
140
+ await engine.fence();
141
+ const start = performance.now();
142
+ for (let i = 0; i < count; i++) {
143
+ const input = inputs[i % 64], before = performance.now();
144
+ engine.write(input);
145
+ const rendered = engine.render(performance.now(), true);
146
+ durations.push(performance.now() - before);
147
+ bytes += input.byteLength;
148
+ if (rendered)
149
+ completed++;
150
+ if ((i + 1) % 32 === 0)
151
+ await engine.fence();
152
+ }
153
+ await engine.fence();
154
+ const elapsedMs = performance.now() - start;
155
+ return {
156
+ workload,
157
+ frames: completed,
158
+ cols,
159
+ rows,
160
+ elapsedMs,
161
+ fencedFramesPerSecond: (completed * 1000) / elapsedMs,
162
+ cpuP50Ms: percentile(durations, 0.5),
163
+ cpuP95Ms: percentile(durations, 0.95),
164
+ cpuP99Ms: percentile(durations, 0.99),
165
+ inputMiBPerSecond: bytes / (1024 * 1024) / (elapsedMs / 1000),
166
+ adapter: engine.adapter,
167
+ userAgent: scope.navigator.userAgent,
168
+ };
169
+ }
170
+ finally {
171
+ engine.write(encoder.encode('\x1b[?1049l' + (cursorWasVisible ? '\x1b[?25h' : '\x1b[?25l')));
172
+ busy = false;
173
+ drain();
174
+ schedule();
175
+ }
176
+ }
177
+ async function handle(command) {
178
+ if (command.type === 'init') {
179
+ await init();
180
+ cols = command.cols;
181
+ rows = command.rows;
182
+ engine = await Engine.create(command.canvas, command.font, cols, rows, command.size, command.lineHeight, command.history);
183
+ for (const font of command.fonts)
184
+ engine.add_font(font);
185
+ engine.theme(command.theme[0], command.theme[1], command.theme[2]);
186
+ post({
187
+ type: 'ready',
188
+ width: engine.cell_width,
189
+ height: engine.cell_height,
190
+ adapter: engine.adapter,
191
+ });
192
+ schedule();
193
+ return;
194
+ }
195
+ if (!engine || disposed)
196
+ throw new Error('Wraith engine is not running');
197
+ switch (command.type) {
198
+ case 'write':
199
+ engine.write(command.bytes);
200
+ drain();
201
+ schedule();
202
+ post({ type: 'written', ids: command.ids });
203
+ break;
204
+ case 'resize':
205
+ engine.resize(command.cols, command.rows);
206
+ cols = command.cols;
207
+ rows = command.rows;
208
+ schedule();
209
+ result(command.id);
210
+ break;
211
+ case 'key':
212
+ engine.key(command.key, command.mods, command.action, command.text, command.unshifted);
213
+ drain();
214
+ break;
215
+ case 'mouse':
216
+ engine.mouse(command.action, command.button, command.mods, command.x, command.y, command.pressed);
217
+ drain();
218
+ break;
219
+ case 'scroll':
220
+ engine.scroll(command.delta);
221
+ schedule();
222
+ break;
223
+ case 'select':
224
+ engine.select(command.x1, command.y1, command.x2, command.y2);
225
+ schedule();
226
+ break;
227
+ case 'paste': {
228
+ // Sanitize terminal control characters and bracketed-paste terminators.
229
+ const text = command.text.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, '');
230
+ const framed = engine.mode(2004) ? `\x1b[200~${text}\x1b[201~` : text.replace(/\r?\n/g, '\r');
231
+ const bytes = encoder.encode(framed);
232
+ post({ type: 'data', bytes }, [bytes.buffer]);
233
+ result(command.id);
234
+ break;
235
+ }
236
+ case 'focus':
237
+ if (engine.mode(1004)) {
238
+ const bytes = encoder.encode(command.focused ? '\x1b[I' : '\x1b[O');
239
+ post({ type: 'data', bytes }, [bytes.buffer]);
240
+ }
241
+ break;
242
+ case 'text':
243
+ result(command.id, engine.text(command.selected));
244
+ break;
245
+ case 'link':
246
+ result(command.id, engine.link(command.x, command.y));
247
+ break;
248
+ case 'theme':
249
+ engine.theme(command.colors[0], command.colors[1], command.colors[2]);
250
+ schedule();
251
+ break;
252
+ case 'flush':
253
+ busy = true;
254
+ if (scheduled) {
255
+ scope.cancelAnimationFrame(raf);
256
+ scheduled = false;
257
+ }
258
+ try {
259
+ draw(performance.now(), true);
260
+ await engine.fence();
261
+ result(command.id);
262
+ }
263
+ finally {
264
+ busy = false;
265
+ schedule();
266
+ }
267
+ break;
268
+ case 'benchmark':
269
+ result(command.id, await benchmark(command.options));
270
+ break;
271
+ case 'visibility':
272
+ visible = command.visible;
273
+ if (visible)
274
+ schedule();
275
+ break;
276
+ case 'dispose':
277
+ disposed = true;
278
+ clearInterval(blinkTimer);
279
+ clearInterval(statsTimer);
280
+ clearTimeout(holdTimer);
281
+ if (scheduled)
282
+ scope.cancelAnimationFrame(raf);
283
+ engine.free();
284
+ engine = undefined;
285
+ result(command.id);
286
+ scope.close();
287
+ break;
288
+ }
289
+ }
290
+ scope.onmessage = (event) => {
291
+ const command = event.data;
292
+ serial = serial
293
+ .then(() => handle(command))
294
+ .catch((error) => {
295
+ if (command.type === 'write')
296
+ post({ type: 'written', ids: command.ids, error: String(error) });
297
+ else if ('id' in command)
298
+ post({ type: 'result', id: command.id, error: String(error) });
299
+ else
300
+ post({ type: 'error', error: String(error) });
301
+ });
302
+ };