@visual-z/xeno 0.2.1 → 0.4.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 (282) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +126 -34
  3. package/docs/app-server.md +84 -0
  4. package/docs/install.md +51 -13
  5. package/docs/providers.md +24 -3
  6. package/package.json +7 -5
  7. package/src/apps/app-server.ts +28 -0
  8. package/src/apps/approve.ts +3 -3
  9. package/src/apps/cli.ts +25 -10
  10. package/src/apps/commands.ts +45 -8
  11. package/src/apps/completions.ts +78 -22
  12. package/src/apps/control-transport.ts +89 -0
  13. package/src/apps/image-draft.ts +46 -0
  14. package/src/apps/model-catalog.ts +2 -1
  15. package/src/apps/project-trust.ts +2 -42
  16. package/src/apps/provider-connect.ts +346 -0
  17. package/src/apps/render.ts +22 -47
  18. package/src/apps/resources.ts +5 -2
  19. package/src/apps/shell.ts +333 -1542
  20. package/src/apps/startup-input.ts +45 -9
  21. package/src/apps/startup.ts +149 -94
  22. package/src/apps/ui/app.ts +1553 -0
  23. package/src/apps/ui/control-model.ts +26 -0
  24. package/src/apps/ui/extension-host.ts +194 -0
  25. package/src/apps/ui/image-cache.ts +78 -0
  26. package/src/apps/ui/palette.ts +88 -0
  27. package/src/apps/ui/project.ts +127 -0
  28. package/src/apps/ui/standalone.ts +146 -0
  29. package/src/apps/ui/status-model.ts +68 -0
  30. package/src/apps/ui/surface.ts +86 -0
  31. package/src/apps/ui/theme.ts +86 -0
  32. package/src/core/acceptance.ts +107 -0
  33. package/src/core/action-defaults.ts +47 -295
  34. package/src/core/benchmark.ts +2 -2
  35. package/src/core/branch-receipt.ts +24 -0
  36. package/src/core/checkpoints.ts +64 -0
  37. package/src/core/churn.ts +2 -0
  38. package/src/core/command-artifacts.ts +94 -0
  39. package/src/core/command-contract.ts +35 -0
  40. package/src/core/completion-action.ts +20 -0
  41. package/src/core/completion-review.ts +37 -21
  42. package/src/core/context-detail.ts +37 -0
  43. package/src/core/context-policy.ts +74 -1
  44. package/src/core/continuation.ts +17 -2
  45. package/src/core/control-protocol.ts +62 -0
  46. package/src/core/control-view.ts +42 -0
  47. package/src/core/derive.ts +29 -3
  48. package/src/core/draft-images.ts +56 -0
  49. package/src/core/environment.ts +145 -0
  50. package/src/core/evaluation-coverage.ts +74 -0
  51. package/src/core/event-validation.ts +81 -6
  52. package/src/core/events.ts +99 -3
  53. package/src/core/evidence-links.ts +7 -0
  54. package/src/core/evidence.ts +13 -3
  55. package/src/core/execution.ts +8 -2
  56. package/src/core/experiment.ts +133 -0
  57. package/src/core/export.ts +9 -0
  58. package/src/core/failure-recovery.ts +173 -0
  59. package/src/core/file-precondition.ts +72 -0
  60. package/src/core/handoff.ts +65 -4
  61. package/src/core/harness-diagnostics.ts +206 -0
  62. package/src/core/history-query.ts +38 -2
  63. package/src/core/history.ts +38 -1
  64. package/src/core/integration-health.ts +34 -0
  65. package/src/core/keymap.ts +8 -20
  66. package/src/core/model-picker.ts +64 -0
  67. package/src/core/native-eval.ts +25 -0
  68. package/src/core/note-guidance.ts +34 -0
  69. package/src/core/oauth-fields.ts +44 -0
  70. package/src/core/observation-view.ts +49 -8
  71. package/src/core/permissions.ts +24 -2
  72. package/src/core/process-receipt.ts +105 -0
  73. package/src/core/process-state.ts +37 -0
  74. package/src/core/program-status.ts +31 -0
  75. package/src/core/project.ts +70 -19
  76. package/src/core/prompt-policy.ts +37 -0
  77. package/src/core/query-eval-contract.ts +14 -0
  78. package/src/core/read-observation.ts +82 -0
  79. package/src/core/recovery-corpus.ts +17 -0
  80. package/src/core/recovery-evaluation.ts +60 -0
  81. package/src/core/recovery-sources.ts +74 -0
  82. package/src/core/request-progress.ts +87 -0
  83. package/src/core/request-state.ts +114 -0
  84. package/src/core/runtime-target.ts +94 -0
  85. package/src/core/search-observation.ts +97 -0
  86. package/src/core/session-tree.ts +147 -0
  87. package/src/core/settings.ts +33 -10
  88. package/src/core/skills.ts +44 -0
  89. package/src/core/sql-query.ts +211 -0
  90. package/src/core/theme.ts +36 -58
  91. package/src/core/tool-contract.ts +51 -2
  92. package/src/core/tool-discovery.ts +87 -0
  93. package/src/core/tool-failure.ts +60 -0
  94. package/src/core/tool-input.ts +6 -0
  95. package/src/core/tool-mode-protocol.ts +25 -0
  96. package/src/core/transcript.ts +19 -4
  97. package/src/core/trial-lifecycle.ts +14 -0
  98. package/src/core/tui-mode.ts +29 -0
  99. package/src/core/update-check.ts +97 -0
  100. package/src/core/version.ts +55 -0
  101. package/src/core/work-state.ts +49 -3
  102. package/src/core/working-set.ts +91 -0
  103. package/src/runtime/auth.ts +11 -4
  104. package/src/runtime/branch-jobs.ts +36 -0
  105. package/src/runtime/catalog.ts +13 -3
  106. package/src/runtime/clipboard.ts +15 -4
  107. package/src/runtime/command-artifacts.ts +64 -0
  108. package/src/runtime/control-connection.ts +104 -0
  109. package/src/runtime/environment-tools.ts +60 -0
  110. package/src/runtime/evidence-notes.ts +4 -2
  111. package/src/runtime/exec-sessions.ts +206 -61
  112. package/src/runtime/execution-control.ts +1 -0
  113. package/src/runtime/extension-ui.ts +22 -30
  114. package/src/runtime/extensions.ts +95 -16
  115. package/src/runtime/harness-comparison.ts +27 -7
  116. package/src/runtime/harness-eval.ts +462 -0
  117. package/src/runtime/history-cache.ts +57 -0
  118. package/src/runtime/history-tools.ts +62 -12
  119. package/src/runtime/integrations.ts +210 -0
  120. package/src/runtime/loop.ts +1088 -282
  121. package/src/runtime/model-discovery.ts +208 -0
  122. package/src/runtime/model.ts +9 -0
  123. package/src/runtime/native-eval-fixtures.ts +33 -0
  124. package/src/runtime/native-eval.ts +146 -0
  125. package/src/runtime/oauth.ts +80 -3
  126. package/src/runtime/pi-provider.ts +169 -12
  127. package/src/runtime/program-status.ts +38 -0
  128. package/src/runtime/provider-connect.ts +109 -0
  129. package/src/runtime/provider-presets.ts +87 -0
  130. package/src/runtime/provider-request.ts +13 -2
  131. package/src/runtime/provider.ts +23 -9
  132. package/src/runtime/query-eval-verifier.ts +28 -0
  133. package/src/runtime/recovery-coverage.ts +71 -0
  134. package/src/runtime/recovery-evidence.ts +76 -0
  135. package/src/runtime/recovery-fixtures.ts +116 -0
  136. package/src/runtime/runtime-target.ts +149 -0
  137. package/src/runtime/session-control.ts +198 -0
  138. package/src/runtime/tool-broker.ts +137 -0
  139. package/src/runtime/tool-discovery.ts +57 -0
  140. package/src/runtime/tool-mode-eval.ts +146 -0
  141. package/src/runtime/tool-queue.ts +77 -0
  142. package/src/runtime/tools.ts +628 -183
  143. package/src/runtime/trial-control.ts +45 -0
  144. package/src/runtime/update-check.ts +47 -0
  145. package/src/runtime/window-trial.ts +2 -1
  146. package/src/runtime/working-notes.ts +33 -3
  147. package/src/runtime/workspace-lifecycle.ts +97 -0
  148. package/src/store/acceptance.ts +15 -0
  149. package/src/store/checkpoint-history.ts +43 -0
  150. package/src/store/command-artifacts.ts +96 -0
  151. package/src/store/command.ts +65 -37
  152. package/src/store/file-checkpoints.ts +104 -0
  153. package/src/store/file-index.ts +13 -1
  154. package/src/store/git-checkpoints.ts +67 -0
  155. package/src/store/observations.ts +61 -20
  156. package/src/store/process-receipts.ts +34 -0
  157. package/src/store/query-source.ts +13 -8
  158. package/src/store/recovery.ts +2 -3
  159. package/src/store/replay.ts +16 -4
  160. package/src/store/runtime-target.ts +64 -0
  161. package/src/store/search.ts +171 -132
  162. package/src/store/session-archive.ts +4 -0
  163. package/src/store/source-cancellation.ts +6 -0
  164. package/src/store/update-cache.ts +36 -0
  165. package/src/store/workspace-observation.ts +113 -0
  166. package/src/tui/ansi-spans.ts +126 -0
  167. package/src/tui/style.ts +19 -16
  168. package/src/tui/ui/atomic-draft.ts +121 -0
  169. package/src/tui/ui/canvas.ts +96 -0
  170. package/src/tui/ui/colors.ts +70 -0
  171. package/src/tui/ui/composer.ts +200 -0
  172. package/src/tui/ui/control-panel.ts +23 -0
  173. package/src/tui/ui/dialog.ts +379 -0
  174. package/src/tui/ui/draft.ts +23 -0
  175. package/src/tui/ui/edge.ts +186 -0
  176. package/src/tui/ui/image-size.ts +40 -0
  177. package/src/tui/ui/ink.ts +210 -0
  178. package/src/tui/ui/items.ts +40 -0
  179. package/src/tui/ui/keys.ts +40 -0
  180. package/src/tui/ui/layout.ts +74 -0
  181. package/src/tui/ui/list-model.ts +86 -0
  182. package/src/tui/ui/list-view.ts +172 -0
  183. package/src/tui/ui/mascot.ts +67 -0
  184. package/src/tui/ui/messages.ts +518 -0
  185. package/src/tui/ui/motion.ts +28 -0
  186. package/src/tui/ui/paths.ts +10 -0
  187. package/src/tui/ui/pictures.ts +78 -0
  188. package/src/tui/ui/screen.ts +421 -0
  189. package/src/tui/ui/status.ts +279 -0
  190. package/src/tui/ui/styled.ts +85 -0
  191. package/src/tui/ui/testing.ts +5 -0
  192. package/src/tui/ui/tool-card.ts +150 -0
  193. package/src/apps/extension-ui.ts +0 -330
  194. package/src/apps/resource-browser.ts +0 -75
  195. package/src/apps/session-browser.ts +0 -113
  196. package/src/apps/session-picker.ts +0 -55
  197. package/src/tui/ansi.ts +0 -67
  198. package/src/tui/autocomplete.ts +0 -29
  199. package/src/tui/chat-viewport.ts +0 -47
  200. package/src/tui/complete.ts +0 -87
  201. package/src/tui/editor.ts +0 -940
  202. package/src/tui/host.ts +0 -264
  203. package/src/tui/input.ts +0 -75
  204. package/src/tui/keys.ts +0 -378
  205. package/src/tui/markdown.ts +0 -17
  206. package/src/tui/render.ts +0 -153
  207. package/src/tui/select.ts +0 -258
  208. package/src/tui/settings.ts +0 -44
  209. package/src/tui/statusline.ts +0 -162
  210. package/src/tui/system-theme.ts +0 -700
  211. package/src/tui/terminal.ts +0 -30
  212. package/src/tui/text.ts +0 -52
  213. package/src/tui/toolview.ts +0 -386
  214. package/src/tui/transcript.ts +0 -123
  215. package/src/tui/tree.ts +0 -471
  216. package/src/vendor/pi-tui/LICENSE.upstream +0 -21
  217. package/src/vendor/pi-tui/UPSTREAM.json +0 -50
  218. package/src/vendor/pi-tui/VENDOR.md +0 -25
  219. package/src/vendor/pi-tui/alt-screen-search.ts +0 -327
  220. package/src/vendor/pi-tui/autocomplete.ts +0 -860
  221. package/src/vendor/pi-tui/colors.ts +0 -367
  222. package/src/vendor/pi-tui/components/alt-screen-flash.ts +0 -51
  223. package/src/vendor/pi-tui/components/box.ts +0 -166
  224. package/src/vendor/pi-tui/components/cancellable-loader.ts +0 -40
  225. package/src/vendor/pi-tui/components/editor.ts +0 -2472
  226. package/src/vendor/pi-tui/components/h-stack.ts +0 -44
  227. package/src/vendor/pi-tui/components/image.ts +0 -127
  228. package/src/vendor/pi-tui/components/input.ts +0 -494
  229. package/src/vendor/pi-tui/components/loader.ts +0 -101
  230. package/src/vendor/pi-tui/components/markdown.ts +0 -1021
  231. package/src/vendor/pi-tui/components/mouse-region.ts +0 -33
  232. package/src/vendor/pi-tui/components/scroll-view.ts +0 -224
  233. package/src/vendor/pi-tui/components/select-list.ts +0 -273
  234. package/src/vendor/pi-tui/components/settings-list.ts +0 -328
  235. package/src/vendor/pi-tui/components/spacer.ts +0 -28
  236. package/src/vendor/pi-tui/components/stack.ts +0 -154
  237. package/src/vendor/pi-tui/components/text.ts +0 -107
  238. package/src/vendor/pi-tui/components/truncated-text.ts +0 -65
  239. package/src/vendor/pi-tui/components/v-stack.ts +0 -33
  240. package/src/vendor/pi-tui/editor-component.ts +0 -74
  241. package/src/vendor/pi-tui/fuzzy.ts +0 -138
  242. package/src/vendor/pi-tui/index.ts +0 -186
  243. package/src/vendor/pi-tui/keybindings.ts +0 -320
  244. package/src/vendor/pi-tui/keys.ts +0 -1401
  245. package/src/vendor/pi-tui/kill-ring.ts +0 -46
  246. package/src/vendor/pi-tui/latex.ts +0 -1506
  247. package/src/vendor/pi-tui/layout-node.ts +0 -51
  248. package/src/vendor/pi-tui/layout.ts +0 -449
  249. package/src/vendor/pi-tui/native/UPSTREAM.json +0 -23
  250. package/src/vendor/pi-tui/native/clipboard.h +0 -208
  251. package/src/vendor/pi-tui/native/darwin/README.md +0 -22
  252. package/src/vendor/pi-tui/native/darwin/build.sh +0 -69
  253. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
  254. package/src/vendor/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
  255. package/src/vendor/pi-tui/native/darwin/src/darwin-platform.m +0 -132
  256. package/src/vendor/pi-tui/native/linux/README.md +0 -23
  257. package/src/vendor/pi-tui/native/linux/build.sh +0 -65
  258. package/src/vendor/pi-tui/native/linux/prebuilds/linux-arm64/linux-platform-x11.node +0 -0
  259. package/src/vendor/pi-tui/native/linux/prebuilds/linux-x64/linux-platform-x11.node +0 -0
  260. package/src/vendor/pi-tui/native/linux/src/clipboard-worker.h +0 -70
  261. package/src/vendor/pi-tui/native/linux/src/linux-platform-x11.c +0 -274
  262. package/src/vendor/pi-tui/native/napi.h +0 -91
  263. package/src/vendor/pi-tui/native/win32/README.md +0 -31
  264. package/src/vendor/pi-tui/native/win32/build.mjs +0 -232
  265. package/src/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-platform.node +0 -0
  266. package/src/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-platform.node +0 -0
  267. package/src/vendor/pi-tui/native/win32/src/win32-platform.c +0 -241
  268. package/src/vendor/pi-tui/native-modifiers.ts +0 -13
  269. package/src/vendor/pi-tui/native-module-path.ts +0 -31
  270. package/src/vendor/pi-tui/native-platform.ts +0 -65
  271. package/src/vendor/pi-tui/oklab.ts +0 -233
  272. package/src/vendor/pi-tui/stdin-buffer.ts +0 -444
  273. package/src/vendor/pi-tui/terminal-colors.ts +0 -91
  274. package/src/vendor/pi-tui/terminal-image.ts +0 -731
  275. package/src/vendor/pi-tui/terminal.ts +0 -554
  276. package/src/vendor/pi-tui/tui-alt-screen.ts +0 -1750
  277. package/src/vendor/pi-tui/tui-main-screen.ts +0 -655
  278. package/src/vendor/pi-tui/tui.ts +0 -1493
  279. package/src/vendor/pi-tui/undo-stack.ts +0 -28
  280. package/src/vendor/pi-tui/utils.ts +0 -1385
  281. package/src/vendor/pi-tui/wheel-scroll.ts +0 -82
  282. package/src/vendor/pi-tui/word-navigation.ts +0 -117
@@ -1,327 +0,0 @@
1
- import { Input } from "./components/input.ts";
2
- import { getKeybindings } from "./keybindings.ts";
3
- import type { Component, Focusable } from "./tui.ts";
4
- import { getGraphemeSegmenter, stripTerminalSequences, truncateToWidth, visibleWidth } from "./utils.ts";
5
-
6
- const segmenter = getGraphemeSegmenter();
7
-
8
- interface SearchSourceSpan {
9
- textStart: number;
10
- textEnd: number;
11
- row: number;
12
- startCol: number;
13
- endCol: number;
14
- linearColumns: boolean;
15
- }
16
-
17
- interface SearchCorpus {
18
- text: string;
19
- spans: SearchSourceSpan[];
20
- }
21
-
22
- export interface AltScreenSearchSegment {
23
- row: number;
24
- startCol: number;
25
- endCol: number;
26
- }
27
-
28
- export interface AltScreenSearchMatch {
29
- segments: AltScreenSearchSegment[];
30
- }
31
-
32
- const PRINTABLE_ASCII = /^[\x20-\x7e]*$/;
33
-
34
- function buildSearchCorpus(lines: readonly string[]): SearchCorpus {
35
- const chunks: string[] = [];
36
- const spans: SearchSourceSpan[] = [];
37
- let textLength = 0;
38
- let pendingSeparator = false;
39
-
40
- const appendSeparator = (): void => {
41
- if (!pendingSeparator) return;
42
- chunks.push(" ");
43
- textLength += 1;
44
- pendingSeparator = false;
45
- };
46
-
47
- for (let row = 0; row < lines.length; row++) {
48
- const line = stripTerminalSequences(lines[row] ?? "");
49
- let column = 0;
50
-
51
- // Rendered transcripts are overwhelmingly ASCII. Index complete non-space
52
- // runs at once instead of segmenting and allocating one mapping per cell.
53
- if (PRINTABLE_ASCII.test(line)) {
54
- let index = 0;
55
- while (index < line.length) {
56
- if (line.charCodeAt(index) === 0x20) {
57
- if (textLength > 0) pendingSeparator = true;
58
- column += 1;
59
- index += 1;
60
- continue;
61
- }
62
- let end = index + 1;
63
- while (end < line.length && line.charCodeAt(end) !== 0x20) end += 1;
64
- appendSeparator();
65
- const text = line.slice(index, end);
66
- chunks.push(text);
67
- spans.push({
68
- textStart: textLength,
69
- textEnd: textLength + text.length,
70
- row,
71
- startCol: column,
72
- endCol: column + text.length,
73
- linearColumns: true,
74
- });
75
- textLength += text.length;
76
- column += text.length;
77
- index = end;
78
- }
79
- } else {
80
- for (const grapheme of segmenter.segment(line)) {
81
- const text = grapheme.segment;
82
- const width = visibleWidth(text);
83
- if (/^\s+$/u.test(text)) {
84
- if (textLength > 0) pendingSeparator = true;
85
- column += width;
86
- continue;
87
- }
88
- appendSeparator();
89
- chunks.push(text);
90
- spans.push({
91
- textStart: textLength,
92
- textEnd: textLength + text.length,
93
- row,
94
- startCol: column,
95
- endCol: column + width,
96
- linearColumns: false,
97
- });
98
- textLength += text.length;
99
- column += width;
100
- }
101
- }
102
- if (textLength > 0) pendingSeparator = true;
103
- }
104
-
105
- return { text: chunks.join(""), spans };
106
- }
107
-
108
- function normalizeQuery(query: string): string {
109
- return query.replace(/\s+/gu, " ").trim();
110
- }
111
-
112
- function escapeRegExp(text: string): string {
113
- return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
114
- }
115
-
116
- function findSearchCorpusMatches(corpus: SearchCorpus, normalizedQuery: string): AltScreenSearchMatch[] {
117
- if (!normalizedQuery) return [];
118
- const expression = new RegExp(escapeRegExp(normalizedQuery), "giu");
119
- const matches: AltScreenSearchMatch[] = [];
120
- let spanIndex = 0;
121
-
122
- for (const match of corpus.text.matchAll(expression)) {
123
- const start = match.index;
124
- const end = start + match[0].length;
125
- while (spanIndex < corpus.spans.length && corpus.spans[spanIndex]!.textEnd <= start) spanIndex += 1;
126
-
127
- const segments: AltScreenSearchSegment[] = [];
128
- for (let index = spanIndex; index < corpus.spans.length; index++) {
129
- const span = corpus.spans[index]!;
130
- if (span.textStart >= end) break;
131
- if (span.textEnd <= start) continue;
132
- const startCol = span.linearColumns
133
- ? span.startCol + Math.max(start, span.textStart) - span.textStart
134
- : span.startCol;
135
- const endCol = span.linearColumns ? span.startCol + Math.min(end, span.textEnd) - span.textStart : span.endCol;
136
- const previous = segments[segments.length - 1];
137
- if (previous && previous.row === span.row && startCol <= previous.endCol) {
138
- previous.endCol = Math.max(previous.endCol, endCol);
139
- } else {
140
- segments.push({ row: span.row, startCol, endCol });
141
- }
142
- }
143
- while (spanIndex < corpus.spans.length && corpus.spans[spanIndex]!.textEnd <= end) spanIndex += 1;
144
- if (segments.length > 0) matches.push({ segments });
145
- }
146
-
147
- return matches;
148
- }
149
-
150
- export interface AltScreenSearchResult {
151
- matches: AltScreenSearchMatch[];
152
- changed: boolean;
153
- }
154
-
155
- /** Cache the searchable corpus and matches while rendered transcript lines remain unchanged. */
156
- export class AltScreenSearchIndex {
157
- private sourceLines: string[] | undefined;
158
- private corpus: SearchCorpus | undefined;
159
- private normalizedQuery: string | undefined;
160
- private matches: AltScreenSearchMatch[] = [];
161
-
162
- search(lines: readonly string[], query: string): AltScreenSearchResult {
163
- let sourceChanged = this.sourceLines?.length !== lines.length;
164
- if (!sourceChanged && this.sourceLines) {
165
- for (let index = 0; index < lines.length; index++) {
166
- if (this.sourceLines[index] === lines[index]) continue;
167
- sourceChanged = true;
168
- break;
169
- }
170
- }
171
- if (sourceChanged || !this.corpus) {
172
- this.sourceLines = Array.from(lines);
173
- this.corpus = buildSearchCorpus(lines);
174
- }
175
-
176
- const normalizedQuery = normalizeQuery(query);
177
- const changed = sourceChanged || normalizedQuery !== this.normalizedQuery;
178
- if (changed) {
179
- this.normalizedQuery = normalizedQuery;
180
- this.matches = findSearchCorpusMatches(this.corpus, normalizedQuery);
181
- }
182
- return { matches: this.matches, changed };
183
- }
184
- }
185
-
186
- export function findAltScreenSearchMatches(lines: readonly string[], query: string): AltScreenSearchMatch[] {
187
- const normalizedQuery = normalizeQuery(query);
188
- return normalizedQuery ? findSearchCorpusMatches(buildSearchCorpus(lines), normalizedQuery) : [];
189
- }
190
-
191
- export function getAltScreenSearchMatchKey(match: AltScreenSearchMatch): string {
192
- const first = match.segments[0];
193
- const last = match.segments[match.segments.length - 1];
194
- return first && last ? `${first.row}:${first.startCol}:${last.row}:${last.endCol}` : "";
195
- }
196
-
197
- export class AltScreenSearchComponent implements Component, Focusable {
198
- private readonly input = new Input({
199
- prompt: " ",
200
- placeholder: "Find in transcript",
201
- placeholderStyle: (text) => `\x1b[2m${text}\x1b[22m`,
202
- });
203
- private readonly onQueryChange: (query: string) => void;
204
- private readonly navigationButtonStyle: (text: string, hovered: boolean) => string;
205
- private resultCount = 0;
206
- private resultIndex = -1;
207
- private previousButtonStart = -1;
208
- private previousButtonEnd = -1;
209
- private nextButtonStart = -1;
210
- private nextButtonEnd = -1;
211
- private hoveredNavigationDirection: -1 | 1 | undefined;
212
- private _focused = false;
213
-
214
- constructor(
215
- onQueryChange: (query: string) => void,
216
- navigationButtonStyle: (text: string, hovered: boolean) => string = (text) => text,
217
- ) {
218
- this.onQueryChange = onQueryChange;
219
- this.navigationButtonStyle = navigationButtonStyle;
220
- }
221
-
222
- get focused(): boolean {
223
- return this._focused;
224
- }
225
-
226
- set focused(value: boolean) {
227
- this._focused = value;
228
- this.input.focused = value;
229
- }
230
-
231
- setResult(index: number, count: number): void {
232
- this.resultIndex = index;
233
- this.resultCount = count;
234
- }
235
-
236
- getNavigationDirectionAt(row: number, column: number): -1 | 1 | undefined {
237
- if (row !== 2) return undefined;
238
- if (column >= this.previousButtonStart && column < this.previousButtonEnd) return -1;
239
- if (column >= this.nextButtonStart && column < this.nextButtonEnd) return 1;
240
- return undefined;
241
- }
242
-
243
- setHoveredNavigationDirection(direction: -1 | 1 | undefined): boolean {
244
- if (direction === this.hoveredNavigationDirection) return false;
245
- this.hoveredNavigationDirection = direction;
246
- return true;
247
- }
248
-
249
- handleInput(data: string): void {
250
- const previous = this.input.getValue();
251
- this.input.handleInput(data);
252
- const query = this.input.getValue();
253
- if (query !== previous) this.onQueryChange(query);
254
- }
255
-
256
- invalidate(): void {
257
- this.input.invalidate();
258
- }
259
-
260
- render(width: number): string[] {
261
- const safeWidth = Math.max(1, width);
262
- const innerWidth = Math.max(0, safeWidth - 2);
263
- const formatKey = (key: string | undefined): string =>
264
- key
265
- ? key
266
- .split("+")
267
- .map((part) => {
268
- if (process.platform === "darwin" && part.toLowerCase() === "alt") return "Option";
269
- return part.charAt(0).toUpperCase() + part.slice(1);
270
- })
271
- .join("+")
272
- : "Unbound";
273
- const keybindings = getKeybindings();
274
- const previousKey = formatKey(keybindings.getKeys("tui.altScreen.searchPrevious")[0]);
275
- const nextKey = formatKey(keybindings.getKeys("tui.altScreen.searchNext")[0]);
276
- const query = this.input.getValue();
277
- const result = !query
278
- ? ""
279
- : this.resultCount === 0
280
- ? "No matches"
281
- : `${this.resultIndex + 1}/${this.resultCount}`;
282
- const resultSpace = Math.max(0, innerWidth - 3);
283
- const visibleResult = truncateToWidth(result, resultSpace, "");
284
- const resultText = visibleResult ? `\x1b[2m ${visibleResult} \x1b[22m` : "";
285
- const inputWidth = Math.max(0, innerWidth - visibleWidth(resultText));
286
- const inputLine = truncateToWidth(this.input.render(Math.max(1, inputWidth))[0] ?? "", inputWidth, "");
287
- const inputPadding = " ".repeat(Math.max(0, inputWidth - visibleWidth(inputLine)));
288
- const content = `${inputLine}${inputPadding}${resultText}`;
289
-
290
- let previousButton = `↑ ${previousKey}`;
291
- let nextButton = `↓ ${nextKey}`;
292
- let separator = " · ";
293
- const outerGapWidth = 1;
294
- const availableControlsWidth = Math.max(0, innerWidth - outerGapWidth * 2 - 1);
295
- let controlsWidth = visibleWidth(previousButton) + visibleWidth(separator) + visibleWidth(nextButton);
296
- if (controlsWidth > availableControlsWidth) {
297
- previousButton = "↑";
298
- nextButton = "↓";
299
- separator = " ";
300
- controlsWidth = visibleWidth(previousButton) + visibleWidth(separator) + visibleWidth(nextButton);
301
- }
302
- const showButtons = controlsWidth <= availableControlsWidth;
303
- const renderedButtons = showButtons
304
- ? this.navigationButtonStyle(previousButton, this.hoveredNavigationDirection === -1) +
305
- separator +
306
- this.navigationButtonStyle(nextButton, this.hoveredNavigationDirection === 1)
307
- : "";
308
- const outerGapsWidth = showButtons ? outerGapWidth * 2 : 0;
309
- const rightRuleWidth = renderedButtons && innerWidth > controlsWidth + outerGapsWidth ? 1 : 0;
310
- const leftRuleWidth = Math.max(
311
- 0,
312
- innerWidth - (showButtons ? controlsWidth : 0) - outerGapsWidth - rightRuleWidth,
313
- );
314
- const previousStart = 1 + leftRuleWidth + outerGapWidth;
315
- this.previousButtonStart = showButtons ? previousStart : -1;
316
- this.previousButtonEnd = showButtons ? previousStart + visibleWidth(previousButton) : -1;
317
- this.nextButtonStart = showButtons ? this.previousButtonEnd + visibleWidth(separator) : -1;
318
- this.nextButtonEnd = showButtons ? this.nextButtonStart + visibleWidth(nextButton) : -1;
319
-
320
- if (safeWidth === 1) return ["┌", "│", "└"];
321
- return [
322
- `┌${"─".repeat(innerWidth)}┐`,
323
- `│${content}│`,
324
- `└${"─".repeat(leftRuleWidth)}${renderedButtons ? " " : ""}${renderedButtons}${renderedButtons ? " " : ""}${"─".repeat(rightRuleWidth)}┘`,
325
- ];
326
- }
327
- }