@jmoyers/harness 0.1.10 → 0.1.20

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 (239) hide show
  1. package/README.md +31 -35
  2. package/package.json +31 -11
  3. package/packages/harness-ai/src/anthropic-protocol.ts +68 -68
  4. package/packages/harness-ai/src/stream-text.ts +13 -91
  5. package/packages/harness-ui/src/frame-primitives.ts +158 -0
  6. package/packages/harness-ui/src/index.ts +18 -0
  7. package/packages/harness-ui/src/interaction/conversation-input-forwarder.ts +221 -0
  8. package/packages/harness-ui/src/interaction/conversation-selection-input.ts +213 -0
  9. package/packages/harness-ui/src/interaction/global-shortcut-input.ts +172 -0
  10. package/{src/ui → packages/harness-ui/src/interaction}/input-preflight.ts +10 -12
  11. package/{src/ui → packages/harness-ui/src/interaction}/input-token-router.ts +120 -69
  12. package/packages/harness-ui/src/interaction/input.ts +420 -0
  13. package/packages/harness-ui/src/interaction/left-nav-input.ts +166 -0
  14. package/{src/ui → packages/harness-ui/src/interaction}/main-pane-pointer-input.ts +91 -23
  15. package/{src/ui → packages/harness-ui/src/interaction}/pointer-routing-input.ts +112 -48
  16. package/packages/harness-ui/src/interaction/rail-pointer-input.ts +62 -0
  17. package/packages/harness-ui/src/interaction/repository-fold-input.ts +118 -0
  18. package/packages/harness-ui/src/kit.ts +476 -0
  19. package/packages/harness-ui/src/layout.ts +238 -0
  20. package/{src/ui/modals/manager.ts → packages/harness-ui/src/modal-manager.ts} +94 -64
  21. package/{src/ui → packages/harness-ui/src}/screen.ts +53 -26
  22. package/packages/harness-ui/src/surface.ts +252 -0
  23. package/packages/harness-ui/src/text-layout.ts +210 -0
  24. package/packages/nim-core/src/contracts.ts +239 -0
  25. package/packages/nim-core/src/event-store.ts +299 -0
  26. package/packages/nim-core/src/events.ts +53 -0
  27. package/packages/nim-core/src/index.ts +9 -0
  28. package/packages/nim-core/src/provider-router.ts +129 -0
  29. package/packages/nim-core/src/providers/anthropic-driver.ts +291 -0
  30. package/packages/nim-core/src/runtime-factory.ts +49 -0
  31. package/packages/nim-core/src/runtime.ts +1797 -0
  32. package/packages/nim-core/src/session-store.ts +516 -0
  33. package/packages/nim-core/src/telemetry.ts +48 -0
  34. package/packages/nim-test-tui/src/index.ts +150 -0
  35. package/packages/nim-ui-core/src/index.ts +1 -0
  36. package/packages/nim-ui-core/src/projection.ts +87 -0
  37. package/scripts/codex-live-mux-runtime.ts +2 -3721
  38. package/scripts/control-plane-daemon.ts +24 -2
  39. package/scripts/harness-bin.js +5 -0
  40. package/scripts/harness-commands.ts +300 -0
  41. package/scripts/harness-runtime.ts +82 -0
  42. package/scripts/harness.ts +33 -3007
  43. package/scripts/nim-tui-smoke.ts +748 -0
  44. package/src/cli/auth/runtime.ts +948 -0
  45. package/src/cli/default-gateway-pointer.ts +193 -0
  46. package/src/cli/gateway/runtime.ts +1872 -0
  47. package/src/cli/parsing/flags.ts +23 -0
  48. package/src/cli/parsing/session.ts +42 -0
  49. package/src/cli/runtime/context.ts +193 -0
  50. package/src/cli/runtime-app/application.ts +392 -0
  51. package/src/cli/runtime-infra/gateway-control.ts +729 -0
  52. package/{scripts/harness-inspector.ts → src/cli/workflows/inspector.ts} +14 -11
  53. package/src/cli/workflows/runtime.ts +965 -0
  54. package/src/clients/tui/left-rail-interactions.ts +519 -0
  55. package/src/clients/tui/main-pane-interactions.ts +509 -0
  56. package/src/clients/tui/modal-input-routing.ts +71 -0
  57. package/src/clients/tui/render-snapshot-adapter.ts +88 -0
  58. package/src/clients/web/synced-selectors.ts +132 -0
  59. package/src/codex/live-session.ts +82 -29
  60. package/src/config/config-core.ts +361 -10
  61. package/src/config/harness-paths.ts +4 -7
  62. package/src/config/harness-runtime-migration.ts +142 -19
  63. package/src/config/harness.config.template.jsonc +33 -0
  64. package/src/config/secrets-core.ts +92 -4
  65. package/src/control-plane/agent-realtime-api.ts +82 -427
  66. package/src/control-plane/prompt/thread-title-namer.ts +49 -23
  67. package/src/control-plane/session-summary.ts +10 -81
  68. package/src/control-plane/status/reducer-base.ts +12 -12
  69. package/src/control-plane/status/reducers/claude-status-reducer.ts +3 -3
  70. package/src/control-plane/status/reducers/codex-status-reducer.ts +4 -4
  71. package/src/control-plane/status/reducers/cursor-status-reducer.ts +3 -3
  72. package/src/control-plane/stream-client.ts +12 -2
  73. package/src/control-plane/stream-command-parser.ts +83 -143
  74. package/src/control-plane/stream-protocol.ts +53 -37
  75. package/src/control-plane/stream-server-background.ts +18 -2
  76. package/src/control-plane/stream-server-command.ts +376 -69
  77. package/src/control-plane/stream-server-session-runtime.ts +3 -2
  78. package/src/control-plane/stream-server.ts +943 -80
  79. package/src/control-plane/stream-session-runtime-types.ts +41 -0
  80. package/src/{mux/live-mux/control-plane-records.ts → core/contracts/records.ts} +24 -97
  81. package/src/core/state/observed-stream-cursor.ts +43 -0
  82. package/src/core/state/synced-observed-state.ts +273 -0
  83. package/src/core/store/harness-synced-store.ts +81 -0
  84. package/src/diff/budget.ts +136 -0
  85. package/src/diff/build.ts +289 -0
  86. package/src/diff/chunker.ts +146 -0
  87. package/src/diff/git-invoke.ts +315 -0
  88. package/src/diff/git-parse.ts +472 -0
  89. package/src/diff/hash.ts +70 -0
  90. package/src/diff/index.ts +24 -0
  91. package/src/diff/normalize.ts +134 -0
  92. package/src/diff/types.ts +178 -0
  93. package/src/diff-ui/args.ts +346 -0
  94. package/src/diff-ui/commands.ts +123 -0
  95. package/src/diff-ui/finder.ts +94 -0
  96. package/src/diff-ui/highlight.ts +127 -0
  97. package/src/diff-ui/index.ts +2 -0
  98. package/src/diff-ui/model.ts +141 -0
  99. package/src/diff-ui/pager.ts +412 -0
  100. package/src/diff-ui/render.ts +337 -0
  101. package/src/diff-ui/runtime.ts +379 -0
  102. package/src/diff-ui/state.ts +224 -0
  103. package/src/diff-ui/types.ts +236 -0
  104. package/src/domain/conversations.ts +11 -7
  105. package/src/domain/workspace.ts +76 -4
  106. package/src/mux/control-plane-op-queue.ts +93 -7
  107. package/src/mux/conversation-rail.ts +28 -71
  108. package/src/mux/dual-pane-core.ts +13 -13
  109. package/src/mux/harness-core-ui.ts +313 -42
  110. package/src/mux/input-shortcuts.ts +22 -112
  111. package/src/mux/keybinding-catalog.ts +340 -0
  112. package/src/mux/keybinding-registry.ts +103 -0
  113. package/src/mux/live-mux/command-menu-open-in.ts +280 -0
  114. package/src/mux/live-mux/command-menu.ts +167 -4
  115. package/src/mux/live-mux/conversation-state.ts +13 -0
  116. package/src/mux/live-mux/directory-resolution.ts +1 -1
  117. package/src/mux/live-mux/git-parsing.ts +16 -0
  118. package/src/mux/live-mux/git-snapshot.ts +33 -2
  119. package/src/mux/live-mux/global-shortcut-handlers.ts +6 -0
  120. package/src/mux/live-mux/home-pane-drop.ts +1 -1
  121. package/src/mux/live-mux/home-pane-pointer.ts +10 -0
  122. package/src/mux/live-mux/input-forwarding.ts +59 -2
  123. package/src/mux/live-mux/left-nav-activation.ts +124 -7
  124. package/src/mux/live-mux/left-nav.ts +35 -0
  125. package/src/mux/live-mux/link-click.ts +292 -0
  126. package/src/mux/live-mux/modal-command-menu-handler.ts +46 -9
  127. package/src/mux/live-mux/modal-conversation-handlers.ts +5 -1
  128. package/src/mux/live-mux/modal-input-reducers.ts +106 -8
  129. package/src/mux/live-mux/modal-overlays.ts +210 -31
  130. package/src/mux/live-mux/modal-pointer.ts +3 -7
  131. package/src/mux/live-mux/modal-prompt-handlers.ts +107 -1
  132. package/src/mux/live-mux/modal-release-notes-handler.ts +111 -0
  133. package/src/mux/live-mux/modal-task-editor-handler.ts +16 -11
  134. package/src/mux/live-mux/pointer-routing.ts +5 -2
  135. package/src/mux/live-mux/project-pane-pointer.ts +8 -0
  136. package/src/mux/live-mux/rail-layout.ts +33 -30
  137. package/src/mux/live-mux/release-notes.ts +383 -0
  138. package/src/mux/live-mux/render-trace-analysis.ts +52 -7
  139. package/src/mux/live-mux/repository-folding.ts +3 -0
  140. package/src/mux/live-mux/selection.ts +0 -4
  141. package/src/mux/live-mux/session-diagnostics-paths.ts +21 -0
  142. package/src/mux/project-pane-github-review.ts +271 -0
  143. package/src/mux/render-frame.ts +4 -0
  144. package/src/mux/runtime-app/codex-live-mux-runtime.ts +5191 -0
  145. package/src/mux/task-composer.ts +21 -14
  146. package/src/mux/task-focused-pane.ts +118 -117
  147. package/src/mux/task-screen-keybindings.ts +19 -82
  148. package/src/mux/workspace-rail-model.ts +270 -104
  149. package/src/mux/workspace-rail.ts +45 -22
  150. package/src/pty/session-broker.ts +1 -1
  151. package/{scripts → src/recording}/terminal-recording-gif-lib.ts +2 -2
  152. package/src/services/control-plane.ts +50 -32
  153. package/src/services/conversation-lifecycle.ts +118 -87
  154. package/src/services/conversation-startup-hydration.ts +20 -12
  155. package/src/services/directory-hydration.ts +21 -16
  156. package/src/services/event-persistence.ts +7 -0
  157. package/src/services/left-rail-pointer-handler.ts +329 -0
  158. package/src/services/mux-ui-state-persistence.ts +5 -1
  159. package/src/services/recording.ts +34 -26
  160. package/src/services/runtime-command-menu-agent-tools.ts +1 -1
  161. package/src/services/runtime-control-actions.ts +79 -61
  162. package/src/services/runtime-control-plane-ops.ts +122 -83
  163. package/src/services/runtime-conversation-actions.ts +40 -26
  164. package/src/services/runtime-conversation-activation.ts +82 -30
  165. package/src/services/runtime-conversation-starter.ts +80 -48
  166. package/src/services/runtime-conversation-title-edit.ts +91 -80
  167. package/src/services/runtime-envelope-handler.ts +107 -105
  168. package/src/services/runtime-git-state.ts +42 -29
  169. package/src/services/runtime-layout-resize.ts +3 -1
  170. package/src/services/runtime-left-rail-render.ts +99 -63
  171. package/src/services/runtime-nim-cli-session.ts +438 -0
  172. package/src/services/runtime-nim-session.ts +705 -0
  173. package/src/services/runtime-nim-tool-bridge.ts +141 -0
  174. package/src/services/runtime-observed-event-projection-pipeline.ts +45 -0
  175. package/src/services/runtime-process-wiring.ts +29 -36
  176. package/src/services/runtime-project-pane-github-review-cache.ts +164 -0
  177. package/src/services/runtime-render-flush.ts +63 -70
  178. package/src/services/runtime-render-lifecycle.ts +65 -64
  179. package/src/services/runtime-render-orchestrator.ts +55 -45
  180. package/src/services/runtime-render-pipeline.ts +106 -103
  181. package/src/services/runtime-render-state.ts +62 -49
  182. package/src/services/runtime-repository-actions.ts +97 -70
  183. package/src/services/runtime-right-pane-render.ts +80 -53
  184. package/src/services/runtime-shutdown.ts +38 -35
  185. package/src/services/runtime-stream-subscriptions.ts +35 -27
  186. package/src/services/runtime-task-composer-persistence.ts +71 -59
  187. package/src/services/runtime-task-composer-snapshot.ts +14 -0
  188. package/src/services/runtime-task-editor-actions.ts +46 -29
  189. package/src/services/runtime-task-pane-actions.ts +220 -134
  190. package/src/services/runtime-task-pane-shortcuts.ts +323 -123
  191. package/src/services/runtime-workspace-observed-effect-queue.ts +25 -0
  192. package/src/services/runtime-workspace-observed-events.ts +33 -184
  193. package/src/services/runtime-workspace-observed-transition-policy.ts +228 -0
  194. package/src/services/session-diagnostics-store.ts +217 -0
  195. package/src/services/startup-background-resume.ts +26 -21
  196. package/src/services/startup-orchestrator.ts +16 -13
  197. package/src/services/startup-paint-tracker.ts +29 -21
  198. package/src/services/startup-persisted-conversation-queue.ts +19 -13
  199. package/src/services/startup-settled-gate.ts +25 -15
  200. package/src/services/startup-shutdown.ts +18 -22
  201. package/src/services/startup-state-hydration.ts +44 -34
  202. package/src/services/startup-visibility.ts +12 -4
  203. package/src/services/task-pane-selection-actions.ts +89 -72
  204. package/src/services/task-planning-hydration.ts +24 -18
  205. package/src/services/task-planning-observed-events.ts +50 -52
  206. package/src/services/workspace-observed-events.ts +66 -63
  207. package/src/storage/storage-lifecycle-core.ts +438 -0
  208. package/src/store/control-plane-store-normalize.ts +33 -242
  209. package/src/store/control-plane-store-types.ts +1 -35
  210. package/src/store/control-plane-store.ts +396 -56
  211. package/src/store/event-store.ts +397 -3
  212. package/src/terminal/snapshot-oracle.ts +207 -94
  213. package/src/ui/mux-theme.ts +112 -8
  214. package/src/ui/panes/home-gridfire.ts +40 -31
  215. package/src/ui/panes/home.ts +10 -2
  216. package/src/ui/panes/nim.ts +315 -0
  217. package/src/mux/live-mux/actions-task.ts +0 -115
  218. package/src/mux/live-mux/left-rail-actions.ts +0 -118
  219. package/src/mux/live-mux/left-rail-conversation-click.ts +0 -82
  220. package/src/mux/live-mux/left-rail-pointer.ts +0 -74
  221. package/src/mux/live-mux/task-pane-shortcuts.ts +0 -206
  222. package/src/services/runtime-directory-actions.ts +0 -164
  223. package/src/services/runtime-input-pipeline.ts +0 -50
  224. package/src/services/runtime-input-router.ts +0 -189
  225. package/src/services/runtime-main-pane-input.ts +0 -230
  226. package/src/services/runtime-modal-input.ts +0 -119
  227. package/src/services/runtime-navigation-input.ts +0 -197
  228. package/src/services/runtime-rail-input.ts +0 -278
  229. package/src/services/runtime-task-pane.ts +0 -62
  230. package/src/services/runtime-workspace-actions.ts +0 -158
  231. package/src/ui/conversation-input-forwarder.ts +0 -114
  232. package/src/ui/conversation-selection-input.ts +0 -103
  233. package/src/ui/global-shortcut-input.ts +0 -89
  234. package/src/ui/input.ts +0 -238
  235. package/src/ui/kit.ts +0 -509
  236. package/src/ui/left-nav-input.ts +0 -80
  237. package/src/ui/left-rail-pointer-input.ts +0 -148
  238. package/src/ui/repository-fold-input.ts +0 -91
  239. package/src/ui/surface.ts +0 -224
@@ -4,7 +4,7 @@ interface ScreenLike {
4
4
  markDirty(): void;
5
5
  }
6
6
 
7
- interface RuntimeRenderLifecycleOptions {
7
+ export interface RuntimeRenderLifecycleOptions {
8
8
  readonly screen: ScreenLike;
9
9
  readonly render: () => void;
10
10
  readonly isShuttingDown: () => boolean;
@@ -21,84 +21,85 @@ interface RuntimeRenderLifecycleOptions {
21
21
 
22
22
  const FATAL_EXIT_DELAY_MS = 1200;
23
23
 
24
- export class RuntimeRenderLifecycle {
25
- private readonly setImmediateFn: (callback: () => void) => void;
26
- private readonly setTimeoutFn: (
27
- callback: () => void,
28
- delayMs: number,
29
- ) => ReturnType<typeof setTimeout>;
30
- private readonly clearTimeoutFn: (timer: ReturnType<typeof setTimeout>) => void;
31
- private renderScheduled = false;
32
- private runtimeFatal: { origin: string; error: unknown } | null = null;
33
- private runtimeFatalExitTimer: ReturnType<typeof setTimeout> | null = null;
34
-
35
- constructor(private readonly options: RuntimeRenderLifecycleOptions) {
36
- this.setImmediateFn = options.setImmediateFn ?? setImmediate;
37
- this.setTimeoutFn = options.setTimeoutFn ?? setTimeout;
38
- this.clearTimeoutFn = options.clearTimeoutFn ?? clearTimeout;
39
- }
40
-
41
- hasFatal(): boolean {
42
- return this.runtimeFatal !== null;
43
- }
44
-
45
- clearRenderScheduled(): void {
46
- this.renderScheduled = false;
47
- }
48
-
49
- clearRuntimeFatalExitTimer(): void {
50
- if (this.runtimeFatalExitTimer === null) {
51
- return;
52
- }
53
- this.clearTimeoutFn(this.runtimeFatalExitTimer);
54
- this.runtimeFatalExitTimer = null;
55
- }
24
+ export interface RuntimeRenderLifecycle {
25
+ hasFatal(): boolean;
26
+ clearRenderScheduled(): void;
27
+ clearRuntimeFatalExitTimer(): void;
28
+ markDirty(): void;
29
+ scheduleRender(): void;
30
+ handleRuntimeFatal(origin: string, error: unknown): void;
31
+ }
56
32
 
57
- markDirty(): void {
58
- if (this.options.isShuttingDown()) {
59
- return;
60
- }
61
- this.options.screen.markDirty();
62
- this.scheduleRender();
63
- }
33
+ export function createRuntimeRenderLifecycle(
34
+ options: RuntimeRenderLifecycleOptions,
35
+ ): RuntimeRenderLifecycle {
36
+ const setImmediateFn = options.setImmediateFn ?? setImmediate;
37
+ const setTimeoutFn = options.setTimeoutFn ?? setTimeout;
38
+ const clearTimeoutFn = options.clearTimeoutFn ?? clearTimeout;
39
+ let renderScheduled = false;
40
+ let runtimeFatal: { origin: string; error: unknown } | null = null;
41
+ let runtimeFatalExitTimer: ReturnType<typeof setTimeout> | null = null;
64
42
 
65
- scheduleRender(): void {
66
- if (this.options.isShuttingDown() || this.renderScheduled) {
43
+ const scheduleRender = (): void => {
44
+ if (options.isShuttingDown() || renderScheduled) {
67
45
  return;
68
46
  }
69
- this.renderScheduled = true;
70
- this.setImmediateFn(() => {
71
- this.renderScheduled = false;
47
+ renderScheduled = true;
48
+ setImmediateFn(() => {
49
+ renderScheduled = false;
72
50
  try {
73
- this.options.render();
74
- if (this.options.screen.isDirty()) {
75
- this.scheduleRender();
51
+ options.render();
52
+ if (options.screen.isDirty()) {
53
+ scheduleRender();
76
54
  }
77
55
  } catch (error: unknown) {
78
- this.handleRuntimeFatal('render', error);
56
+ handleRuntimeFatal('render', error);
79
57
  }
80
58
  });
81
- }
59
+ };
82
60
 
83
- handleRuntimeFatal(origin: string, error: unknown): void {
84
- if (this.runtimeFatal !== null) {
61
+ const handleRuntimeFatal = (origin: string, error: unknown): void => {
62
+ if (runtimeFatal !== null) {
85
63
  return;
86
64
  }
87
- this.runtimeFatal = {
65
+ runtimeFatal = {
88
66
  origin,
89
67
  error,
90
68
  };
91
- this.options.setShuttingDown(true);
92
- this.options.setStop(true);
93
- this.options.screen.clearDirty();
94
- this.options.writeStderr(
95
- `[mux] fatal runtime error (${origin}): ${this.options.formatErrorMessage(error)}\n`,
69
+ options.setShuttingDown(true);
70
+ options.setStop(true);
71
+ options.screen.clearDirty();
72
+ options.writeStderr(
73
+ `[mux] fatal runtime error (${origin}): ${options.formatErrorMessage(error)}\n`,
96
74
  );
97
- this.options.restoreTerminalState();
98
- this.runtimeFatalExitTimer = this.setTimeoutFn(() => {
99
- this.options.writeStderr('[mux] fatal runtime error forced exit\n');
100
- this.options.exitProcess(1);
75
+ options.restoreTerminalState();
76
+ runtimeFatalExitTimer = setTimeoutFn(() => {
77
+ options.writeStderr('[mux] fatal runtime error forced exit\n');
78
+ options.exitProcess(1);
101
79
  }, FATAL_EXIT_DELAY_MS);
102
- this.runtimeFatalExitTimer.unref?.();
103
- }
80
+ runtimeFatalExitTimer.unref?.();
81
+ };
82
+
83
+ return {
84
+ hasFatal: (): boolean => runtimeFatal !== null,
85
+ clearRenderScheduled: (): void => {
86
+ renderScheduled = false;
87
+ },
88
+ clearRuntimeFatalExitTimer: (): void => {
89
+ if (runtimeFatalExitTimer === null) {
90
+ return;
91
+ }
92
+ clearTimeoutFn(runtimeFatalExitTimer);
93
+ runtimeFatalExitTimer = null;
94
+ },
95
+ markDirty: (): void => {
96
+ if (options.isShuttingDown()) {
97
+ return;
98
+ }
99
+ options.screen.markDirty();
100
+ scheduleRender();
101
+ },
102
+ scheduleRender,
103
+ handleRuntimeFatal,
104
+ };
104
105
  }
@@ -1,6 +1,7 @@
1
1
  interface RuntimeRenderStateResult<TConversation, TFrame, TSelection> {
2
2
  readonly projectPaneActive: boolean;
3
3
  readonly homePaneActive: boolean;
4
+ readonly nimPaneActive: boolean;
4
5
  readonly activeConversation: TConversation | null;
5
6
  readonly rightFrame: TFrame | null;
6
7
  readonly renderSelection: TSelection | null;
@@ -12,28 +13,35 @@ interface RuntimeLeftRailRenderResult<TRailViewRows> {
12
13
  readonly viewRows: TRailViewRows;
13
14
  }
14
15
 
15
- interface RuntimeRenderOrchestratorOptions<
16
+ export interface RuntimeRenderOrchestratorOptions<
16
17
  TLayout,
17
18
  TConversation,
18
19
  TFrame,
19
20
  TSelection,
20
21
  TSelectionDrag,
21
22
  TRailViewRows,
23
+ TRenderSnapshot,
22
24
  > {
23
25
  readonly isScreenDirty: () => boolean;
24
26
  readonly clearDirty: () => void;
27
+ readonly readRenderSnapshot: () => TRenderSnapshot;
25
28
  readonly prepareRenderState: (
26
29
  selection: TSelection | null,
27
30
  selectionDrag: TSelectionDrag | null,
28
31
  ) => RuntimeRenderStateResult<TConversation, TFrame, TSelection> | null;
29
- readonly renderLeftRail: (layout: TLayout) => RuntimeLeftRailRenderResult<TRailViewRows>;
32
+ readonly renderLeftRail: (
33
+ layout: TLayout,
34
+ snapshot: TRenderSnapshot,
35
+ ) => RuntimeLeftRailRenderResult<TRailViewRows>;
30
36
  readonly setLatestRailViewRows: (rows: TRailViewRows) => void;
31
37
  readonly renderRightRows: (input: {
32
38
  layout: TLayout;
33
39
  rightFrame: TFrame | null;
34
40
  homePaneActive: boolean;
41
+ nimPaneActive: boolean;
35
42
  projectPaneActive: boolean;
36
43
  activeDirectoryId: string | null;
44
+ snapshot: TRenderSnapshot;
37
45
  }) => readonly string[];
38
46
  readonly flushRender: (input: {
39
47
  layout: TLayout;
@@ -49,60 +57,62 @@ interface RuntimeRenderOrchestratorOptions<
49
57
  readonly activeDirectoryId: () => string | null;
50
58
  }
51
59
 
52
- interface RuntimeRenderOrchestratorInput<TLayout, TSelection, TSelectionDrag> {
60
+ export interface RuntimeRenderOrchestratorInput<TLayout, TSelection, TSelectionDrag> {
53
61
  readonly shuttingDown: boolean;
54
62
  readonly layout: TLayout;
55
63
  readonly selection: TSelection | null;
56
64
  readonly selectionDrag: TSelectionDrag | null;
57
65
  }
58
66
 
59
- export class RuntimeRenderOrchestrator<
67
+ export function orchestrateRuntimeRender<
60
68
  TLayout,
61
69
  TConversation,
62
70
  TFrame,
63
71
  TSelection,
64
72
  TSelectionDrag,
65
73
  TRailViewRows,
66
- > {
67
- constructor(
68
- private readonly options: RuntimeRenderOrchestratorOptions<
69
- TLayout,
70
- TConversation,
71
- TFrame,
72
- TSelection,
73
- TSelectionDrag,
74
- TRailViewRows
75
- >,
76
- ) {}
77
-
78
- render(input: RuntimeRenderOrchestratorInput<TLayout, TSelection, TSelectionDrag>): void {
79
- if (input.shuttingDown || !this.options.isScreenDirty()) {
80
- return;
81
- }
82
- const renderState = this.options.prepareRenderState(input.selection, input.selectionDrag);
83
- if (renderState === null) {
84
- this.options.clearDirty();
85
- return;
86
- }
87
- const rail = this.options.renderLeftRail(input.layout);
88
- this.options.setLatestRailViewRows(rail.viewRows);
89
- const rightRows = this.options.renderRightRows({
90
- layout: input.layout,
91
- rightFrame: renderState.rightFrame,
92
- homePaneActive: renderState.homePaneActive,
93
- projectPaneActive: renderState.projectPaneActive,
94
- activeDirectoryId: this.options.activeDirectoryId(),
95
- });
96
- this.options.flushRender({
97
- layout: input.layout,
98
- projectPaneActive: renderState.projectPaneActive,
99
- homePaneActive: renderState.homePaneActive,
100
- activeConversation: renderState.activeConversation,
101
- rightFrame: renderState.rightFrame,
102
- renderSelection: renderState.renderSelection,
103
- selectionRows: renderState.selectionRows,
104
- railAnsiRows: rail.ansiRows,
105
- rightRows,
106
- });
74
+ TRenderSnapshot,
75
+ >(
76
+ options: RuntimeRenderOrchestratorOptions<
77
+ TLayout,
78
+ TConversation,
79
+ TFrame,
80
+ TSelection,
81
+ TSelectionDrag,
82
+ TRailViewRows,
83
+ TRenderSnapshot
84
+ >,
85
+ input: RuntimeRenderOrchestratorInput<TLayout, TSelection, TSelectionDrag>,
86
+ ): void {
87
+ if (input.shuttingDown || !options.isScreenDirty()) {
88
+ return;
89
+ }
90
+ const renderState = options.prepareRenderState(input.selection, input.selectionDrag);
91
+ if (renderState === null) {
92
+ options.clearDirty();
93
+ return;
107
94
  }
95
+ const snapshot = options.readRenderSnapshot();
96
+ const rail = options.renderLeftRail(input.layout, snapshot);
97
+ options.setLatestRailViewRows(rail.viewRows);
98
+ const rightRows = options.renderRightRows({
99
+ layout: input.layout,
100
+ rightFrame: renderState.rightFrame,
101
+ homePaneActive: renderState.homePaneActive,
102
+ nimPaneActive: renderState.nimPaneActive,
103
+ projectPaneActive: renderState.projectPaneActive,
104
+ activeDirectoryId: options.activeDirectoryId(),
105
+ snapshot,
106
+ });
107
+ options.flushRender({
108
+ layout: input.layout,
109
+ projectPaneActive: renderState.projectPaneActive,
110
+ homePaneActive: renderState.homePaneActive,
111
+ activeConversation: renderState.activeConversation,
112
+ rightFrame: renderState.rightFrame,
113
+ renderSelection: renderState.renderSelection,
114
+ selectionRows: renderState.selectionRows,
115
+ railAnsiRows: rail.ansiRows,
116
+ rightRows,
117
+ });
108
118
  }
@@ -5,57 +5,46 @@ import type {
5
5
  TaskFocusedPaneTaskRecord,
6
6
  } from '../mux/task-focused-pane.ts';
7
7
  import type { TerminalSnapshotFrameCore } from '../terminal/snapshot-oracle.ts';
8
- import { RuntimeLeftRailRender } from './runtime-left-rail-render.ts';
9
- import { RuntimeRenderFlush } from './runtime-render-flush.ts';
10
- import { RuntimeRenderOrchestrator } from './runtime-render-orchestrator.ts';
11
- import { RuntimeRenderState } from './runtime-render-state.ts';
12
- import { RuntimeRightPaneRender } from './runtime-right-pane-render.ts';
8
+ import {
9
+ renderRuntimeLeftRail,
10
+ type RuntimeLeftRailRenderOptions,
11
+ type RuntimeLeftRailRenderSnapshot,
12
+ } from './runtime-left-rail-render.ts';
13
+ import { flushRuntimeRender, type RuntimeRenderFlushOptions } from './runtime-render-flush.ts';
14
+ import {
15
+ orchestrateRuntimeRender,
16
+ type RuntimeRenderOrchestratorInput,
17
+ type RuntimeRenderOrchestratorOptions,
18
+ } from './runtime-render-orchestrator.ts';
19
+ import {
20
+ prepareRuntimeRenderState,
21
+ type RuntimeRenderStateOptions,
22
+ } from './runtime-render-state.ts';
23
+ import {
24
+ renderRuntimeRightPaneRows,
25
+ type RuntimeRightPaneRenderOptions,
26
+ type RuntimeRightPaneRenderSnapshot,
27
+ } from './runtime-right-pane-render.ts';
13
28
 
14
29
  type RuntimeLayout = ReturnType<typeof computeDualPaneLayout>;
15
30
 
16
- type RuntimeRenderFlushOptions<TConversation, TModalOverlay, TStatusRow> = ConstructorParameters<
17
- typeof RuntimeRenderFlush<
18
- TConversation,
19
- TerminalSnapshotFrameCore,
20
- PaneSelection,
21
- RuntimeLayout,
22
- TModalOverlay,
23
- TStatusRow
24
- >
25
- >[0];
26
-
27
- type RuntimeRightPaneRenderOptions<
28
- TRepositoryRecord extends TaskFocusedPaneRepositoryRecord,
29
- TTaskRecord extends TaskFocusedPaneTaskRecord,
30
- > = ConstructorParameters<typeof RuntimeRightPaneRender<TRepositoryRecord, TTaskRecord>>[0];
31
-
32
- type RuntimeLeftRailRenderOptions<
31
+ export interface RuntimeRenderPipelineSnapshot<
33
32
  TDirectoryRecord,
34
33
  TConversation,
35
- TRepositoryRecord,
36
- TRepositorySnapshot,
37
- TGitSummary,
34
+ TRepositoryRecord extends TaskFocusedPaneRepositoryRecord,
35
+ TTaskRecord extends TaskFocusedPaneTaskRecord,
38
36
  TProcessUsage,
39
- TShortcutBindings,
40
- TRailViewRows,
41
- > = ConstructorParameters<
42
- typeof RuntimeLeftRailRender<
37
+ > {
38
+ readonly leftRail: RuntimeLeftRailRenderSnapshot<
43
39
  TDirectoryRecord,
44
40
  TConversation,
45
41
  TRepositoryRecord,
46
- TRepositorySnapshot,
47
- TGitSummary,
48
- TProcessUsage,
49
- TShortcutBindings,
50
- TRailViewRows
51
- >
52
- >[0];
53
-
54
- type RuntimeRenderStateOptions<TConversation> = ConstructorParameters<
55
- typeof RuntimeRenderState<TConversation, TerminalSnapshotFrameCore>
56
- >[0];
42
+ TProcessUsage
43
+ >;
44
+ readonly rightPane: RuntimeRightPaneRenderSnapshot<TRepositoryRecord, TTaskRecord>;
45
+ }
57
46
 
58
- interface RuntimeRenderPipelineOptions<
47
+ export interface RuntimeRenderPipelineOptions<
59
48
  TConversation,
60
49
  TRepositoryRecord extends TaskFocusedPaneRepositoryRecord,
61
50
  TTaskRecord extends TaskFocusedPaneTaskRecord,
@@ -63,12 +52,18 @@ interface RuntimeRenderPipelineOptions<
63
52
  TRepositorySnapshot,
64
53
  TGitSummary,
65
54
  TProcessUsage,
66
- TShortcutBindings,
67
55
  TRailViewRows,
68
56
  TModalOverlay,
69
57
  TStatusRow,
70
58
  > {
71
- readonly renderFlush: RuntimeRenderFlushOptions<TConversation, TModalOverlay, TStatusRow>;
59
+ readonly renderFlush: RuntimeRenderFlushOptions<
60
+ TConversation,
61
+ TerminalSnapshotFrameCore,
62
+ PaneSelection,
63
+ RuntimeLayout,
64
+ TModalOverlay,
65
+ TStatusRow
66
+ >;
72
67
  readonly rightPaneRender: RuntimeRightPaneRenderOptions<TRepositoryRecord, TTaskRecord>;
73
68
  readonly leftRailRender: RuntimeLeftRailRenderOptions<
74
69
  TDirectoryRecord,
@@ -77,28 +72,30 @@ interface RuntimeRenderPipelineOptions<
77
72
  TRepositorySnapshot,
78
73
  TGitSummary,
79
74
  TProcessUsage,
80
- TShortcutBindings,
81
75
  TRailViewRows
82
76
  >;
83
- readonly renderState: RuntimeRenderStateOptions<TConversation>;
77
+ readonly renderState: RuntimeRenderStateOptions<TConversation, TerminalSnapshotFrameCore>;
84
78
  readonly isScreenDirty: () => boolean;
85
79
  readonly clearDirty: () => void;
80
+ // Frame-local TUI snapshot read. React/web adapters should consume store selectors directly.
81
+ readonly readRenderSnapshot: () => RuntimeRenderPipelineSnapshot<
82
+ TDirectoryRecord,
83
+ TConversation,
84
+ TRepositoryRecord,
85
+ TTaskRecord,
86
+ TProcessUsage
87
+ >;
86
88
  readonly setLatestRailViewRows: (rows: TRailViewRows) => void;
87
89
  readonly activeDirectoryId: () => string | null;
88
90
  }
89
91
 
90
- type RuntimeRenderPipelineInput = Parameters<
91
- RuntimeRenderOrchestrator<
92
- RuntimeLayout,
93
- unknown,
94
- TerminalSnapshotFrameCore,
95
- PaneSelection,
96
- PaneSelectionDrag,
97
- unknown
98
- >['render']
99
- >[0];
92
+ type RuntimeRenderPipelineInput = RuntimeRenderOrchestratorInput<
93
+ RuntimeLayout,
94
+ PaneSelection,
95
+ PaneSelectionDrag
96
+ >;
100
97
 
101
- export class RuntimeRenderPipeline<
98
+ export function createRuntimeRenderPipeline<
102
99
  TConversation,
103
100
  TRepositoryRecord extends TaskFocusedPaneRepositoryRecord,
104
101
  TTaskRecord extends TaskFocusedPaneTaskRecord,
@@ -106,62 +103,68 @@ export class RuntimeRenderPipeline<
106
103
  TRepositorySnapshot,
107
104
  TGitSummary,
108
105
  TProcessUsage,
109
- TShortcutBindings,
110
106
  TRailViewRows,
111
107
  TModalOverlay,
112
108
  TStatusRow,
113
- > {
114
- private readonly renderOrchestrator: RuntimeRenderOrchestrator<
109
+ >(
110
+ options: RuntimeRenderPipelineOptions<
111
+ TConversation,
112
+ TRepositoryRecord,
113
+ TTaskRecord,
114
+ TDirectoryRecord,
115
+ TRepositorySnapshot,
116
+ TGitSummary,
117
+ TProcessUsage,
118
+ TRailViewRows,
119
+ TModalOverlay,
120
+ TStatusRow
121
+ >,
122
+ ): (input: RuntimeRenderPipelineInput) => void {
123
+ const renderOrchestratorOptions: RuntimeRenderOrchestratorOptions<
115
124
  RuntimeLayout,
116
125
  TConversation,
117
126
  TerminalSnapshotFrameCore,
118
127
  PaneSelection,
119
128
  PaneSelectionDrag,
120
- TRailViewRows
121
- >;
122
-
123
- constructor(
124
- options: RuntimeRenderPipelineOptions<
129
+ TRailViewRows,
130
+ RuntimeRenderPipelineSnapshot<
131
+ TDirectoryRecord,
125
132
  TConversation,
126
133
  TRepositoryRecord,
127
134
  TTaskRecord,
128
- TDirectoryRecord,
129
- TRepositorySnapshot,
130
- TGitSummary,
131
- TProcessUsage,
132
- TShortcutBindings,
133
- TRailViewRows,
134
- TModalOverlay,
135
- TStatusRow
136
- >,
137
- ) {
138
- const renderFlush = new RuntimeRenderFlush(options.renderFlush);
139
- const rightPaneRender = new RuntimeRightPaneRender(options.rightPaneRender);
140
- const leftRailRender = new RuntimeLeftRailRender(options.leftRailRender);
141
- const renderState = new RuntimeRenderState(options.renderState);
142
- this.renderOrchestrator = new RuntimeRenderOrchestrator({
143
- isScreenDirty: options.isScreenDirty,
144
- clearDirty: options.clearDirty,
145
- prepareRenderState: (selection, selectionDrag) =>
146
- renderState.prepareRenderState(selection, selectionDrag),
147
- renderLeftRail: (layout) => leftRailRender.render(layout),
148
- setLatestRailViewRows: options.setLatestRailViewRows,
149
- renderRightRows: (input) =>
150
- rightPaneRender.renderRightRows({
151
- layout: input.layout,
152
- rightFrame: input.rightFrame,
153
- homePaneActive: input.homePaneActive,
154
- projectPaneActive: input.projectPaneActive,
155
- activeDirectoryId: input.activeDirectoryId,
156
- }),
157
- flushRender: (input) => {
158
- renderFlush.flushRender(input);
159
- },
160
- activeDirectoryId: options.activeDirectoryId,
161
- });
162
- }
163
-
164
- render(input: RuntimeRenderPipelineInput): void {
165
- this.renderOrchestrator.render(input);
166
- }
135
+ TProcessUsage
136
+ >
137
+ > = {
138
+ isScreenDirty: options.isScreenDirty,
139
+ clearDirty: options.clearDirty,
140
+ readRenderSnapshot: options.readRenderSnapshot,
141
+ prepareRenderState: (selection, selectionDrag) => {
142
+ return prepareRuntimeRenderState(options.renderState, selection, selectionDrag);
143
+ },
144
+ renderLeftRail: (layout, snapshot) => {
145
+ return renderRuntimeLeftRail(options.leftRailRender, {
146
+ layout,
147
+ snapshot: snapshot.leftRail,
148
+ });
149
+ },
150
+ setLatestRailViewRows: options.setLatestRailViewRows,
151
+ renderRightRows: (input) => {
152
+ return renderRuntimeRightPaneRows(options.rightPaneRender, {
153
+ layout: input.layout,
154
+ rightFrame: input.rightFrame,
155
+ homePaneActive: input.homePaneActive,
156
+ nimPaneActive: input.nimPaneActive,
157
+ projectPaneActive: input.projectPaneActive,
158
+ activeDirectoryId: input.activeDirectoryId,
159
+ snapshot: input.snapshot.rightPane,
160
+ });
161
+ },
162
+ flushRender: (input) => {
163
+ flushRuntimeRender(options.renderFlush, input);
164
+ },
165
+ activeDirectoryId: options.activeDirectoryId,
166
+ };
167
+ return (input: RuntimeRenderPipelineInput): void => {
168
+ orchestrateRuntimeRender(renderOrchestratorOptions, input);
169
+ };
167
170
  }