@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,51 +0,0 @@
1
- import type { Component } from "./tui.ts";
2
-
3
- export const LAYOUT_NODE = Symbol.for("@earendil-works/pi-tui/layout-node");
4
-
5
- export interface LayoutViewport {
6
- width: number;
7
- height: number;
8
- }
9
-
10
- export interface StackLayoutEntry {
11
- component: Component;
12
- basis?: number | "auto";
13
- grow?: number;
14
- shrink?: number;
15
- minSize?: number;
16
- maxSize?: number;
17
- visible?: (viewport: LayoutViewport) => boolean;
18
- }
19
-
20
- export interface StackLayoutNode {
21
- type: "vstack" | "hstack";
22
- entries: readonly StackLayoutEntry[];
23
- gap: number;
24
- align: "stretch" | "start" | "center" | "end";
25
- }
26
-
27
- export interface ScrollLayoutState {
28
- readonly scrollTop: number;
29
- readonly primary: boolean;
30
- readonly overscroll: "chain" | "contain";
31
- readonly viewportHeight: number;
32
- getContentWidth(width: number): number;
33
- updateLayout(contentHeight: number, viewportHeight: number, requestRender: () => void): void;
34
- }
35
-
36
- export interface ScrollLayoutNode {
37
- type: "scroll";
38
- component: Component;
39
- state: ScrollLayoutState;
40
- }
41
-
42
- export type LayoutNode = StackLayoutNode | ScrollLayoutNode;
43
-
44
- export interface LayoutComponent extends Component {
45
- [LAYOUT_NODE](): LayoutNode;
46
- }
47
-
48
- export function getLayoutNode(component: Component): LayoutNode | undefined {
49
- const candidate = component as Partial<LayoutComponent>;
50
- return typeof candidate[LAYOUT_NODE] === "function" ? candidate[LAYOUT_NODE]() : undefined;
51
- }
@@ -1,449 +0,0 @@
1
- import type { ScrollView } from "./components/scroll-view.ts";
2
- import { allocateStackSizes, visibleStackEntries } from "./components/stack.ts";
3
- import { getLayoutNode } from "./layout-node.ts";
4
- import { cropKittyImageLine, getKittyImageMetadata, isImageLine } from "./terminal-image.ts";
5
- import { type Component, CURSOR_MARKER, compositeTuiLine } from "./tui.ts";
6
- import {
7
- extractAnsiCode,
8
- getActiveBackgroundAnsi,
9
- getGraphemeCellRange,
10
- sliceByColumn,
11
- visibleWidth,
12
- } from "./utils.ts";
13
-
14
- const OSC133_ZONE_PREFIX = /^(?:\x1b\]133;[ABC](?:\x07|\x1b\\))+/;
15
-
16
- export interface LayoutRect {
17
- x: number;
18
- y: number;
19
- width: number;
20
- height: number;
21
- }
22
-
23
- export interface LayoutBox {
24
- component: Component;
25
- rect: LayoutRect;
26
- clip: LayoutRect;
27
- children: LayoutBox[];
28
- parent?: LayoutBox;
29
- lines?: readonly string[];
30
- lineOffset?: number;
31
- scrollView?: ScrollView;
32
- scrollContentLines?: readonly string[];
33
- layer: number;
34
- }
35
-
36
- export interface LayoutFrame {
37
- root: LayoutBox;
38
- width: number;
39
- height: number;
40
- lines: string[];
41
- primaryScrollView?: ScrollView;
42
- }
43
-
44
- export interface ScrollbarGeometry {
45
- column: number;
46
- trackTop: number;
47
- trackHeight: number;
48
- thumbTop: number;
49
- thumbHeight: number;
50
- maxScrollTop: number;
51
- }
52
-
53
- interface LayoutContext {
54
- viewport: { width: number; height: number };
55
- renderCache: Map<Component, Map<number, string[]>>;
56
- requestRender: () => void;
57
- primaryScrollView: ScrollView | undefined;
58
- }
59
-
60
- function intersect(a: LayoutRect, b: LayoutRect): LayoutRect {
61
- const x = Math.max(a.x, b.x);
62
- const y = Math.max(a.y, b.y);
63
- const right = Math.min(a.x + a.width, b.x + b.width);
64
- const bottom = Math.min(a.y + a.height, b.y + b.height);
65
- return { x, y, width: Math.max(0, right - x), height: Math.max(0, bottom - y) };
66
- }
67
-
68
- function renderCached(context: LayoutContext, component: Component, width: number): string[] {
69
- const safeWidth = Math.max(1, Math.floor(width));
70
- let widths = context.renderCache.get(component);
71
- if (!widths) {
72
- widths = new Map<number, string[]>();
73
- context.renderCache.set(component, widths);
74
- }
75
- let lines = widths.get(safeWidth);
76
- if (!lines) {
77
- lines = component.render(safeWidth);
78
- widths.set(safeWidth, lines);
79
- }
80
- return lines;
81
- }
82
-
83
- function measureHeight(context: LayoutContext, component: Component, width: number): number {
84
- return renderCached(context, component, width).length;
85
- }
86
-
87
- function measureWidth(context: LayoutContext, component: Component, width: number): number {
88
- return renderCached(context, component, width).reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
89
- }
90
-
91
- function withParent(box: LayoutBox, parent: LayoutBox): LayoutBox {
92
- box.parent = parent;
93
- return box;
94
- }
95
-
96
- function translateBox(box: LayoutBox, deltaY: number): void {
97
- box.rect.y += deltaY;
98
- for (const child of box.children) translateBox(child, deltaY);
99
- }
100
-
101
- function updateClips(box: LayoutBox, parentClip: LayoutRect): void {
102
- box.clip = intersect(parentClip, box.rect);
103
- for (const child of box.children) updateClips(child, box.clip);
104
- }
105
-
106
- function layoutComponent(
107
- context: LayoutContext,
108
- component: Component,
109
- x: number,
110
- y: number,
111
- width: number,
112
- height: number | undefined,
113
- clip: LayoutRect,
114
- ): LayoutBox {
115
- const safeWidth = Math.max(1, Math.floor(width));
116
- const node = getLayoutNode(component);
117
- if (!node) {
118
- const lines = renderCached(context, component, safeWidth);
119
- const allocatedHeight = height === undefined ? lines.length : Math.max(0, Math.floor(height));
120
- let lineOffset = 0;
121
- if (lines.length > allocatedHeight && allocatedHeight > 0) {
122
- const cursorLine = lines.findIndex((line) => line.includes(CURSOR_MARKER));
123
- if (cursorLine >= allocatedHeight) lineOffset = cursorLine - allocatedHeight + 1;
124
- }
125
- return {
126
- component,
127
- rect: { x, y, width: safeWidth, height: allocatedHeight },
128
- clip: intersect(clip, { x, y, width: safeWidth, height: allocatedHeight }),
129
- children: [],
130
- lines,
131
- lineOffset,
132
- layer: 0,
133
- };
134
- }
135
-
136
- if (node.type === "scroll") {
137
- const previousScrollTop = node.state.scrollTop;
138
- const contentWidth = node.state.getContentWidth(safeWidth);
139
- const childBox = layoutComponent(
140
- context,
141
- node.component,
142
- x,
143
- y - previousScrollTop,
144
- contentWidth,
145
- undefined,
146
- clip,
147
- );
148
- const contentHeight = childBox.rect.height;
149
- const viewportHeight = height === undefined ? contentHeight : Math.max(0, Math.floor(height));
150
- node.state.updateLayout(contentHeight, viewportHeight, context.requestRender);
151
- translateBox(childBox, previousScrollTop - node.state.scrollTop);
152
- const scrollView = node.state as ScrollView;
153
- if (node.state.primary || !context.primaryScrollView) context.primaryScrollView = scrollView;
154
- const rect = { x, y, width: safeWidth, height: viewportHeight };
155
- const childClip = intersect(clip, rect);
156
- const box: LayoutBox = {
157
- component,
158
- rect,
159
- clip: childClip,
160
- children: [childBox],
161
- scrollView,
162
- scrollContentLines: renderCached(context, node.component, contentWidth),
163
- layer: 0,
164
- };
165
- childBox.parent = box;
166
- updateClips(childBox, childClip);
167
- return box;
168
- }
169
-
170
- const entries = visibleStackEntries(node.entries, context.viewport);
171
- const gapTotal = Math.max(0, entries.length - 1) * node.gap;
172
- if (node.type === "vstack") {
173
- const intrinsicHeights = entries.map((entry) =>
174
- typeof entry.basis === "number" ? entry.basis : measureHeight(context, entry.component, safeWidth),
175
- );
176
- const sizes = allocateStackSizes(entries, intrinsicHeights, height, node.gap);
177
- const naturalHeight = sizes.reduce((sum, size) => sum + size, 0) + gapTotal;
178
- const allocatedHeight = height === undefined ? naturalHeight : Math.max(0, Math.floor(height));
179
- const rect = { x, y, width: safeWidth, height: allocatedHeight };
180
- const box: LayoutBox = {
181
- component,
182
- rect,
183
- clip: intersect(clip, rect),
184
- children: [],
185
- layer: 0,
186
- };
187
- let childY = y;
188
- for (let index = 0; index < entries.length; index++) {
189
- box.children.push(
190
- withParent(
191
- layoutComponent(context, entries[index]!.component, x, childY, safeWidth, sizes[index]!, box.clip),
192
- box,
193
- ),
194
- );
195
- childY += sizes[index]! + node.gap;
196
- }
197
- return box;
198
- }
199
-
200
- const intrinsicWidths = entries.map((entry) =>
201
- typeof entry.basis === "number" ? entry.basis : measureWidth(context, entry.component, safeWidth),
202
- );
203
- const widths = allocateStackSizes(entries, intrinsicWidths, safeWidth, node.gap);
204
- const intrinsicHeights = entries.map((entry, index) =>
205
- measureHeight(context, entry.component, Math.max(1, widths[index]!)),
206
- );
207
- const allocatedHeight =
208
- height === undefined
209
- ? intrinsicHeights.reduce((max, childHeight) => Math.max(max, childHeight), 0)
210
- : Math.max(0, height);
211
- const rect = { x, y, width: safeWidth, height: allocatedHeight };
212
- const box: LayoutBox = {
213
- component,
214
- rect,
215
- clip: intersect(clip, rect),
216
- children: [],
217
- layer: 0,
218
- };
219
- let childX = x;
220
- for (let index = 0; index < entries.length; index++) {
221
- const naturalChildHeight = intrinsicHeights[index]!;
222
- const childHeight = node.align === "stretch" ? allocatedHeight : Math.min(allocatedHeight, naturalChildHeight);
223
- let childY = y;
224
- if (node.align === "center") childY += Math.floor((allocatedHeight - childHeight) / 2);
225
- else if (node.align === "end") childY += allocatedHeight - childHeight;
226
- const childWidth = widths[index]!;
227
- if (childWidth === 0) {
228
- box.children.push({
229
- component: entries[index]!.component,
230
- rect: { x: childX, y: childY, width: 0, height: childHeight },
231
- clip: { x: childX, y: childY, width: 0, height: 0 },
232
- children: [],
233
- parent: box,
234
- layer: 0,
235
- });
236
- } else {
237
- box.children.push(
238
- withParent(
239
- layoutComponent(context, entries[index]!.component, childX, childY, childWidth, childHeight, box.clip),
240
- box,
241
- ),
242
- );
243
- }
244
- childX += childWidth + node.gap;
245
- }
246
- return box;
247
- }
248
-
249
- function replaceScrollbarCell(
250
- line: string,
251
- column: number,
252
- totalWidth: number,
253
- replacement: string,
254
- preserveTargetBackground: boolean,
255
- ): string {
256
- if (isImageLine(line)) return line;
257
-
258
- const graphemeRange = getGraphemeCellRange(line, column);
259
- const start = graphemeRange?.start ?? column;
260
- const end = graphemeRange?.end ?? column + 1;
261
- const before = sliceByColumn(line, 0, start, true);
262
- const target = sliceByColumn(line, start, end - start, true);
263
- const after = sliceByColumn(line, end, Math.max(0, totalWidth - end), true);
264
-
265
- let targetPrefix = "";
266
- let targetIndex = 0;
267
- while (targetIndex < target.length) {
268
- const ansi = extractAnsiCode(target, targetIndex);
269
- if (!ansi) break;
270
- targetPrefix += ansi.code;
271
- targetIndex += ansi.length;
272
- }
273
- const beforePadding = " ".repeat(Math.max(0, start - visibleWidth(before)));
274
- const cellPaddingBefore = " ".repeat(Math.max(0, column - start));
275
- const cellPaddingAfter = " ".repeat(Math.max(0, end - column - 1));
276
- const targetStyle = `\x1b[0m\x1b]8;;\x07${preserveTargetBackground ? getActiveBackgroundAnsi(targetPrefix) : ""}`;
277
- return `${before}${beforePadding}${targetStyle}${cellPaddingBefore}${replacement}${cellPaddingAfter}${after}`;
278
- }
279
-
280
- export function getScrollbarGeometry(box: LayoutBox, includeHiddenAuto = false): ScrollbarGeometry | undefined {
281
- if (!box.scrollView || box.rect.width <= 0 || box.rect.height <= 0) return undefined;
282
-
283
- const contentHeight = box.children[0]?.rect.height ?? box.scrollContentLines?.length ?? 0;
284
- const trackHeight = box.rect.height;
285
- const canRevealHiddenAuto = includeHiddenAuto && box.scrollView.scrollbar === "auto" && contentHeight > trackHeight;
286
- if (!box.scrollView.isScrollbarVisible && !canRevealHiddenAuto) return undefined;
287
-
288
- const minThumbHeight = Math.min(2, trackHeight);
289
- const thumbHeight = Math.max(
290
- minThumbHeight,
291
- Math.min(trackHeight, Math.round((trackHeight * trackHeight) / contentHeight)),
292
- );
293
- const maxScrollTop = Math.max(0, contentHeight - trackHeight);
294
- const maxThumbTop = trackHeight - thumbHeight;
295
- const thumbOffset = maxScrollTop === 0 ? 0 : Math.round((box.scrollView.scrollTop / maxScrollTop) * maxThumbTop);
296
- const column = box.rect.x + box.rect.width - 1;
297
- if (column < box.clip.x || column >= box.clip.x + box.clip.width) return undefined;
298
-
299
- return {
300
- column,
301
- trackTop: box.rect.y,
302
- trackHeight,
303
- thumbTop: box.rect.y + thumbOffset,
304
- thumbHeight,
305
- maxScrollTop,
306
- };
307
- }
308
-
309
- function paintScrollbar(box: LayoutBox, screen: string[], totalWidth: number): void {
310
- const geometry = getScrollbarGeometry(box);
311
- if (!geometry || !box.scrollView) return;
312
-
313
- for (let offset = 0; offset < geometry.trackHeight; offset++) {
314
- const row = geometry.trackTop + offset;
315
- if (row < box.clip.y || row >= box.clip.y + box.clip.height || row < 0 || row >= screen.length) continue;
316
- const isThumb = row >= geometry.thumbTop && row < geometry.thumbTop + geometry.thumbHeight;
317
- const replacement = isThumb
318
- ? box.scrollView.scrollbarThumbStyle(box.scrollView.isScrollbarActive ? "█" : "┃")
319
- : box.scrollView.scrollbarTrackStyle("│");
320
- screen[row] = replaceScrollbarCell(
321
- screen[row] ?? "",
322
- geometry.column,
323
- totalWidth,
324
- replacement,
325
- box.scrollView.scrollbar !== "always",
326
- );
327
- }
328
- }
329
-
330
- function paintBox(box: LayoutBox, screen: string[], totalWidth: number): void {
331
- if (box.lines) {
332
- const offset = box.lineOffset ?? 0;
333
- const firstRow = Math.max(box.rect.y, box.clip.y, 0);
334
- const lastRow = Math.min(box.rect.y + box.rect.height, box.clip.y + box.clip.height, screen.length);
335
- for (let row = firstRow; row < lastRow; row++) {
336
- const sourceLine = box.lines[offset + row - box.rect.y];
337
- if (sourceLine === undefined) continue;
338
- let line = sourceLine.replace(OSC133_ZONE_PREFIX, "");
339
- const imageMetadata = getKittyImageMetadata(line);
340
- if (imageMetadata) {
341
- const clipBottom = Math.min(screen.length, box.clip.y + box.clip.height);
342
- const visibleRows = Math.min(imageMetadata.rows, clipBottom - row);
343
- if (visibleRows < imageMetadata.rows) line = cropKittyImageLine(line, 0, visibleRows);
344
- }
345
- // Fast path: a full-width box painting onto an untouched row can use the
346
- // source line reference directly. Compositing here would rebuild the row
347
- // string through ANSI/grapheme segmentation every frame; padding is
348
- // unnecessary because rows are written with erase-line and the final
349
- // width clamp still truncates over-wide lines.
350
- if (box.rect.x === 0 && box.rect.width >= totalWidth && (isImageLine(line) || !screen[row])) {
351
- screen[row] = line;
352
- } else {
353
- screen[row] = compositeTuiLine(screen[row] ?? "", line, box.rect.x, box.rect.width, totalWidth);
354
- }
355
- }
356
- }
357
- for (const child of box.children) paintBox(child, screen, totalWidth);
358
-
359
- if (box.scrollView && box.scrollContentLines && box.scrollView.scrollTop > 0 && box.rect.height > 0) {
360
- for (let imageRow = box.scrollView.scrollTop - 1; imageRow >= 0; imageRow--) {
361
- const imageLine = box.scrollContentLines[imageRow] ?? "";
362
- const metadata = getKittyImageMetadata(imageLine);
363
- if (metadata) {
364
- const hiddenRows = box.scrollView.scrollTop - imageRow;
365
- if (hiddenRows < metadata.rows) {
366
- const visibleRows = Math.min(box.rect.height, metadata.rows - hiddenRows);
367
- const cropped = cropKittyImageLine(imageLine, hiddenRows, visibleRows);
368
- if (box.rect.x === 0 && box.rect.width >= totalWidth) screen[box.rect.y] = cropped;
369
- }
370
- break;
371
- }
372
- if (imageLine !== "") break;
373
- }
374
- }
375
-
376
- paintScrollbar(box, screen, totalWidth);
377
- }
378
-
379
- export function renderLayoutFrame(
380
- root: Component,
381
- width: number,
382
- height: number,
383
- requestRender: () => void,
384
- ): LayoutFrame {
385
- const safeWidth = Math.max(1, Math.floor(width));
386
- const safeHeight = Math.max(1, Math.floor(height));
387
- const context: LayoutContext = {
388
- viewport: { width: safeWidth, height: safeHeight },
389
- renderCache: new Map(),
390
- requestRender,
391
- primaryScrollView: undefined,
392
- };
393
- const rootBox = layoutComponent(context, root, 0, 0, safeWidth, safeHeight, {
394
- x: 0,
395
- y: 0,
396
- width: safeWidth,
397
- height: safeHeight,
398
- });
399
- const lines = Array.from({ length: safeHeight }, () => "");
400
- paintBox(rootBox, lines, safeWidth);
401
- return {
402
- root: rootBox,
403
- width: safeWidth,
404
- height: safeHeight,
405
- lines,
406
- ...(context.primaryScrollView === undefined ? {} : { primaryScrollView: context.primaryScrollView }),
407
- };
408
- }
409
-
410
- function containsPoint(rect: LayoutRect, x: number, y: number): boolean {
411
- return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
412
- }
413
-
414
- /** Return the visual hit path from the deepest component to the layout root. */
415
- export function getLayoutBoxesAt(frame: LayoutFrame, x: number, y: number): LayoutBox[] {
416
- const result: Array<{ box: LayoutBox; depth: number }> = [];
417
- const visit = (box: LayoutBox, depth: number): void => {
418
- if (!containsPoint(box.clip, x, y)) return;
419
- result.push({ box, depth });
420
- for (const child of box.children) visit(child, depth + 1);
421
- };
422
- visit(frame.root, 0);
423
- result.sort((a, b) => b.box.layer - a.box.layer || b.depth - a.depth);
424
- return result.map(({ box }) => box);
425
- }
426
-
427
- export function getScrollViewBox(frame: LayoutFrame, scrollView: ScrollView): LayoutBox | undefined {
428
- const visit = (box: LayoutBox): LayoutBox | undefined => {
429
- if (box.scrollView === scrollView) return box;
430
- for (const child of box.children) {
431
- const match = visit(child);
432
- if (match) return match;
433
- }
434
- return undefined;
435
- };
436
- return visit(frame.root);
437
- }
438
-
439
- export function getScrollViewsAt(frame: LayoutFrame, x: number, y: number): ScrollView[] {
440
- const result: Array<{ scrollView: ScrollView; depth: number }> = [];
441
- const visit = (box: LayoutBox, depth: number): void => {
442
- if (!containsPoint(box.clip, x, y)) return;
443
- if (box.scrollView && containsPoint(box.rect, x, y)) result.push({ scrollView: box.scrollView, depth });
444
- for (const child of box.children) visit(child, depth + 1);
445
- };
446
- visit(frame.root, 0);
447
- result.sort((a, b) => b.depth - a.depth);
448
- return result.map((entry) => entry.scrollView);
449
- }
@@ -1,23 +0,0 @@
1
- {
2
- "commit": "02eed88fd8912e54a804ddebd409e2e4c08ac5ef",
3
- "files": {
4
- "clipboard.h": "4e7d0a70f8e530017307816f097d355b94227c4d",
5
- "darwin/README.md": "74c5c5911b480e3cffa23b18ec39a00a68fb71bd",
6
- "darwin/build.sh": "65f7fa4f8cdc19be744b9519e95687bbb046df0e",
7
- "darwin/prebuilds/darwin-arm64/darwin-platform.node": "3294b6bffd36eaab70d61134e9955992f31f9a8f",
8
- "darwin/prebuilds/darwin-x64/darwin-platform.node": "014383575508654d9f898ee25631b88db8d9740c",
9
- "darwin/src/darwin-platform.m": "5cdd8f9122309f2e4ed0aa107fe1f556b9f8934d",
10
- "linux/README.md": "5efdb5cb69af75d5b596423a11a8411138a43ccd",
11
- "linux/build.sh": "121b6c45d56020fb2bb78aa6f1a5627f0c094986",
12
- "linux/prebuilds/linux-arm64/linux-platform-x11.node": "6d3abc4e33fe4fc8f9029cda1201a3a2aee5c050",
13
- "linux/prebuilds/linux-x64/linux-platform-x11.node": "54fcd629b88533ee6c788a830038aa46cf78c1d7",
14
- "linux/src/clipboard-worker.h": "93c79d0e7112096b86f38d6fdb1e21c0a72293d4",
15
- "linux/src/linux-platform-x11.c": "578a850e43de0c4a00cff4f46c8d1cd31dc74a5f",
16
- "napi.h": "816a8202e2fd4d69d3d1f834fbe353b7ef71744a",
17
- "win32/README.md": "f3b58607cbaa893d1cdff1b0321ed39e793677ea",
18
- "win32/build.mjs": "df1ed206131af71e22d042f5924649e7d742c09b",
19
- "win32/prebuilds/win32-arm64/win32-platform.node": "ec330840e70b863b2b0f295437b78378fb8ac84b",
20
- "win32/prebuilds/win32-x64/win32-platform.node": "2d0b749b53a17d6049f3e1629c73a847d3851d04",
21
- "win32/src/win32-platform.c": "a637dda892b695a525ca2cb6b22ee9a8a55cae59"
22
- }
23
- }