@crouton-kit/crouter 0.3.27 → 0.3.29

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 (343) hide show
  1. package/dist/build-root.js +4 -2
  2. package/dist/builtin-memory/crouter-development/marketplaces.md +7 -6
  3. package/dist/builtin-memory/crouter-development/personas/base-prompt.md +1 -1
  4. package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +1 -1
  5. package/dist/builtin-memory/crouter-development/personas.md +1 -1
  6. package/dist/builtin-memory/crouter-development/plugins.md +20 -18
  7. package/dist/builtin-memory/design.md +1 -1
  8. package/dist/builtin-memory/development.md +1 -1
  9. package/dist/builtin-memory/internal/INDEX.md +1 -1
  10. package/dist/builtin-memory/internal/examples/INDEX.md +1 -1
  11. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  12. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  13. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  14. package/dist/builtin-memory/planning.md +1 -1
  15. package/dist/builtin-memory/spec.md +6 -4
  16. package/dist/builtin-personas/orchestration-kernel.md +4 -5
  17. package/dist/builtin-personas/runtime-base.md +8 -4
  18. package/dist/builtin-personas/spec/PERSONA.md +8 -4
  19. package/dist/builtin-personas/spec/orchestrator.md +5 -3
  20. package/dist/builtin-personas/spec/requirements/PERSONA.md +10 -0
  21. package/dist/builtin-views/canvas/core.mjs +586 -0
  22. package/dist/builtin-views/canvas/text.mjs +58 -0
  23. package/dist/builtin-views/canvas/tui.mjs +165 -0
  24. package/dist/builtin-views/canvas/web.jsx +120 -0
  25. package/dist/builtin-views/git-pr/core.mjs +673 -0
  26. package/dist/builtin-views/git-pr/text.mjs +84 -0
  27. package/dist/builtin-views/git-pr/tui.mjs +302 -0
  28. package/dist/builtin-views/git-pr/web.jsx +216 -0
  29. package/dist/builtin-views/inbox/_lib/render.mjs +1 -3
  30. package/dist/builtin-views/inbox/core.mjs +1273 -0
  31. package/dist/builtin-views/inbox/text.mjs +73 -0
  32. package/dist/builtin-views/inbox/tui.mjs +312 -0
  33. package/dist/builtin-views/inbox/web.jsx +188 -0
  34. package/dist/builtin-views/linkedin/core.mjs +906 -0
  35. package/dist/builtin-views/linkedin/text.mjs +69 -0
  36. package/dist/builtin-views/linkedin/tui.mjs +427 -0
  37. package/dist/builtin-views/linkedin/web.jsx +206 -0
  38. package/dist/builtin-views/workspace-sidebar/core.mjs +621 -0
  39. package/dist/builtin-views/workspace-sidebar/text.mjs +53 -0
  40. package/dist/builtin-views/workspace-sidebar/tui.mjs +142 -0
  41. package/dist/builtin-views/workspace-sidebar/web.jsx +109 -0
  42. package/dist/cli.js +3 -3
  43. package/dist/clients/attach/__tests__/action-parity.test.js +48 -0
  44. package/dist/clients/attach/__tests__/clipboard-image.test.js +76 -0
  45. package/dist/clients/attach/__tests__/editor-newline.test.js +57 -0
  46. package/dist/clients/attach/__tests__/git-info.test.js +31 -0
  47. package/dist/clients/attach/__tests__/onrequest-wired.test.js +20 -0
  48. package/dist/clients/attach/__tests__/reconnect-giveup.test.js +30 -0
  49. package/dist/clients/attach/__tests__/slash-quit-copy.test.js +42 -0
  50. package/dist/clients/attach/__tests__/titled-editor.test.d.ts +1 -0
  51. package/dist/clients/attach/__tests__/titled-editor.test.js +39 -0
  52. package/dist/clients/attach/attach-cmd.js +445 -41
  53. package/dist/clients/attach/auth-pickers.d.ts +9 -0
  54. package/dist/clients/attach/auth-pickers.js +194 -0
  55. package/dist/clients/attach/canvas-panels.d.ts +12 -0
  56. package/dist/clients/attach/canvas-panels.js +78 -0
  57. package/dist/clients/attach/chat-view.d.ts +27 -2
  58. package/dist/clients/attach/chat-view.js +98 -9
  59. package/dist/clients/attach/clipboard-image.d.ts +47 -8
  60. package/dist/clients/attach/clipboard-image.js +150 -26
  61. package/dist/clients/attach/clipboard-text.d.ts +3 -0
  62. package/dist/clients/attach/clipboard-text.js +39 -0
  63. package/dist/clients/attach/config-load.d.ts +54 -7
  64. package/dist/clients/attach/config-load.js +99 -3
  65. package/dist/clients/attach/context-message.d.ts +12 -0
  66. package/dist/clients/attach/context-message.js +72 -0
  67. package/dist/clients/attach/git-info.d.ts +20 -0
  68. package/dist/clients/attach/git-info.js +44 -0
  69. package/dist/clients/attach/graph-overlay.d.ts +43 -0
  70. package/dist/clients/attach/graph-overlay.js +306 -0
  71. package/dist/clients/attach/input-controller.d.ts +81 -13
  72. package/dist/clients/attach/input-controller.js +307 -67
  73. package/dist/clients/attach/pickers.d.ts +58 -0
  74. package/dist/clients/attach/pickers.js +171 -0
  75. package/dist/clients/attach/slash-commands.d.ts +44 -1
  76. package/dist/clients/attach/slash-commands.js +250 -27
  77. package/dist/clients/attach/titled-editor.d.ts +33 -0
  78. package/dist/clients/attach/titled-editor.js +84 -0
  79. package/dist/clients/attach/view-socket.d.ts +37 -2
  80. package/dist/clients/attach/view-socket.js +131 -7
  81. package/dist/clients/web/dev-server.d.ts +7 -0
  82. package/dist/clients/web/dev-server.js +59 -0
  83. package/dist/clients/web/events.d.ts +14 -0
  84. package/dist/clients/web/events.js +151 -0
  85. package/dist/clients/web/server.d.ts +18 -0
  86. package/dist/clients/web/server.js +450 -0
  87. package/dist/clients/web/web-cmd.d.ts +2 -0
  88. package/dist/clients/web/web-cmd.js +120 -0
  89. package/dist/commands/canvas.js +1 -2
  90. package/dist/commands/chord.js +1 -1
  91. package/dist/commands/dashboard.js +5 -1
  92. package/dist/commands/human/prompts.js +3 -3
  93. package/dist/commands/human/queue.d.ts +17 -0
  94. package/dist/commands/human/queue.js +111 -4
  95. package/dist/commands/memory/__tests__/lint-schema.test.js +27 -4
  96. package/dist/commands/memory/lint.d.ts +5 -4
  97. package/dist/commands/memory/lint.js +23 -7
  98. package/dist/commands/memory/read.js +2 -2
  99. package/dist/commands/memory/shared.d.ts +7 -1
  100. package/dist/commands/memory/shared.js +18 -3
  101. package/dist/commands/memory/write.js +20 -7
  102. package/dist/commands/memory.js +4 -4
  103. package/dist/commands/node.d.ts +1 -1
  104. package/dist/commands/node.js +64 -65
  105. package/dist/commands/pkg.js +1 -2
  106. package/dist/commands/revive.js +11 -3
  107. package/dist/commands/sys/feedback.d.ts +1 -0
  108. package/dist/commands/sys/feedback.js +163 -0
  109. package/dist/commands/sys/sync.d.ts +1 -0
  110. package/dist/commands/sys/sync.js +187 -0
  111. package/dist/commands/sys.js +4 -2
  112. package/dist/commands/view-cycle.js +2 -2
  113. package/dist/commands/view-list.js +8 -8
  114. package/dist/commands/view-new.js +21 -17
  115. package/dist/commands/view-pick.js +1 -1
  116. package/dist/commands/view-run.js +35 -14
  117. package/dist/commands/view.js +8 -6
  118. package/dist/commands/workspace.d.ts +2 -0
  119. package/dist/commands/workspace.js +161 -0
  120. package/dist/core/__tests__/broker-double-spawn.test.d.ts +1 -0
  121. package/dist/core/__tests__/broker-double-spawn.test.js +143 -0
  122. package/dist/core/__tests__/broker-fork-seam.test.d.ts +1 -0
  123. package/dist/core/__tests__/broker-fork-seam.test.js +102 -0
  124. package/dist/core/__tests__/broker-sdk-wiring.test.js +2 -0
  125. package/dist/core/__tests__/broker-snapshot-history.test.d.ts +1 -0
  126. package/dist/core/__tests__/broker-snapshot-history.test.js +105 -0
  127. package/dist/core/__tests__/child-death-wake.test.js +56 -111
  128. package/dist/core/__tests__/close.test.js +14 -20
  129. package/dist/core/__tests__/context-intro.test.js +19 -19
  130. package/dist/core/__tests__/daemon-boot.test.js +30 -23
  131. package/dist/core/__tests__/draw-style.test.js +1 -1
  132. package/dist/core/__tests__/error-stall-recycle.test.d.ts +1 -0
  133. package/dist/core/__tests__/error-stall-recycle.test.js +141 -0
  134. package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -1
  135. package/dist/core/__tests__/fixtures/fake-engine.js +32 -2
  136. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  137. package/dist/core/__tests__/focuses.test.js +25 -13
  138. package/dist/core/__tests__/fork.test.js +22 -5
  139. package/dist/core/__tests__/full/broker-control-preempt.test.d.ts +1 -0
  140. package/dist/core/__tests__/full/broker-control-preempt.test.js +61 -0
  141. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +1 -0
  142. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +57 -0
  143. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +1 -0
  144. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +80 -0
  145. package/dist/core/__tests__/full/broker-pane-resolution.test.js +90 -0
  146. package/dist/core/__tests__/full/cascade-close.test.js +22 -8
  147. package/dist/core/__tests__/full/detach-focus.test.js +66 -169
  148. package/dist/core/__tests__/helpers/harness.d.ts +6 -9
  149. package/dist/core/__tests__/helpers/harness.js +19 -36
  150. package/dist/core/__tests__/human-node-not-supervised.test.d.ts +1 -0
  151. package/dist/core/__tests__/human-node-not-supervised.test.js +83 -0
  152. package/dist/core/__tests__/human-stranded-deliver.test.d.ts +1 -0
  153. package/dist/core/__tests__/human-stranded-deliver.test.js +108 -0
  154. package/dist/core/__tests__/lifecycle.test.js +1 -19
  155. package/dist/core/__tests__/live-mutation-verbs.test.js +5 -5
  156. package/dist/core/__tests__/memory-resolver.test.js +1 -1
  157. package/dist/core/__tests__/on-read-dedup-resume.test.d.ts +1 -0
  158. package/dist/core/__tests__/on-read-dedup-resume.test.js +81 -0
  159. package/dist/core/__tests__/refresh-stall-recycle.test.d.ts +1 -0
  160. package/dist/core/__tests__/refresh-stall-recycle.test.js +88 -0
  161. package/dist/core/__tests__/relaunch-root.test.d.ts +1 -0
  162. package/dist/core/__tests__/relaunch-root.test.js +114 -0
  163. package/dist/core/__tests__/reset.test.js +19 -79
  164. package/dist/core/__tests__/steer-note.test.js +9 -7
  165. package/dist/core/__tests__/tmux-surface.test.js +13 -7
  166. package/dist/core/bootstrap.d.ts +0 -3
  167. package/dist/core/bootstrap.js +1 -143
  168. package/dist/core/canvas/browse/__tests__/model.test.js +86 -1
  169. package/dist/core/canvas/browse/__tests__/render.test.js +2 -1
  170. package/dist/core/canvas/browse/app.js +131 -8
  171. package/dist/core/canvas/browse/model.d.ts +23 -5
  172. package/dist/core/canvas/browse/model.js +68 -13
  173. package/dist/core/canvas/browse/render.d.ts +7 -1
  174. package/dist/core/canvas/browse/render.js +178 -47
  175. package/dist/core/canvas/canvas.js +1 -1
  176. package/dist/core/canvas/focuses.d.ts +4 -4
  177. package/dist/core/canvas/focuses.js +7 -7
  178. package/dist/core/canvas/nav-model.d.ts +162 -0
  179. package/dist/core/canvas/nav-model.js +487 -0
  180. package/dist/core/canvas/paths.d.ts +7 -0
  181. package/dist/core/canvas/paths.js +9 -0
  182. package/dist/core/canvas/render.d.ts +68 -9
  183. package/dist/core/canvas/render.js +195 -61
  184. package/dist/core/canvas/types.d.ts +16 -23
  185. package/dist/core/config.js +1 -4
  186. package/dist/core/memory-resolver.js +43 -5
  187. package/dist/core/runtime/bearings.d.ts +1 -1
  188. package/dist/core/runtime/bearings.js +8 -8
  189. package/dist/core/runtime/branded-host.d.ts +16 -0
  190. package/dist/core/runtime/branded-host.js +127 -0
  191. package/dist/core/runtime/broker-protocol.d.ts +212 -6
  192. package/dist/core/runtime/broker-sdk.d.ts +0 -12
  193. package/dist/core/runtime/broker-sdk.js +77 -6
  194. package/dist/core/runtime/broker.d.ts +5 -1
  195. package/dist/core/runtime/broker.js +520 -59
  196. package/dist/core/runtime/close.d.ts +12 -2
  197. package/dist/core/runtime/close.js +37 -16
  198. package/dist/core/runtime/front-door.js +14 -7
  199. package/dist/core/runtime/host.d.ts +14 -34
  200. package/dist/core/runtime/host.js +14 -50
  201. package/dist/core/runtime/launch.d.ts +12 -9
  202. package/dist/core/runtime/launch.js +27 -15
  203. package/dist/core/runtime/lifecycle.d.ts +1 -1
  204. package/dist/core/runtime/lifecycle.js +15 -19
  205. package/dist/core/runtime/nodes.d.ts +0 -55
  206. package/dist/core/runtime/nodes.js +6 -74
  207. package/dist/core/runtime/placement.d.ts +91 -334
  208. package/dist/core/runtime/placement.js +262 -828
  209. package/dist/core/runtime/promote.d.ts +2 -0
  210. package/dist/core/runtime/promote.js +24 -6
  211. package/dist/core/runtime/recap.d.ts +8 -0
  212. package/dist/core/runtime/recap.js +107 -0
  213. package/dist/core/runtime/recycle.js +25 -61
  214. package/dist/core/runtime/reset.d.ts +43 -37
  215. package/dist/core/runtime/reset.js +131 -218
  216. package/dist/core/runtime/revive.d.ts +9 -29
  217. package/dist/core/runtime/revive.js +45 -135
  218. package/dist/core/runtime/spawn.d.ts +15 -11
  219. package/dist/core/runtime/spawn.js +168 -136
  220. package/dist/core/runtime/surface-bg.d.ts +11 -0
  221. package/dist/core/runtime/surface-bg.js +68 -0
  222. package/dist/core/runtime/tmux.d.ts +15 -43
  223. package/dist/core/runtime/tmux.js +38 -80
  224. package/dist/core/scope.d.ts +1 -2
  225. package/dist/core/scope.js +1 -5
  226. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.d.ts +1 -0
  227. package/dist/core/skill-sync/__tests__/dry-run-wrote-count.test.js +57 -0
  228. package/dist/core/skill-sync/builtins.d.ts +42 -0
  229. package/dist/core/skill-sync/builtins.js +112 -0
  230. package/dist/core/skill-sync/claude-plugins.d.ts +23 -0
  231. package/dist/core/skill-sync/claude-plugins.js +71 -0
  232. package/dist/core/skill-sync/engine.d.ts +42 -0
  233. package/dist/core/skill-sync/engine.js +633 -0
  234. package/dist/core/skill-sync/export.d.ts +23 -0
  235. package/dist/core/skill-sync/export.js +86 -0
  236. package/dist/core/skill-sync/manifest.d.ts +64 -0
  237. package/dist/core/skill-sync/manifest.js +181 -0
  238. package/dist/core/skill-sync/profile.d.ts +76 -0
  239. package/dist/core/skill-sync/profile.js +173 -0
  240. package/dist/core/skill-sync/snapshot.d.ts +57 -0
  241. package/dist/core/skill-sync/snapshot.js +120 -0
  242. package/dist/core/spawn.d.ts +14 -0
  243. package/dist/core/spawn.js +29 -9
  244. package/dist/core/substrate/index.d.ts +2 -2
  245. package/dist/core/substrate/index.js +7 -7
  246. package/dist/core/substrate/injected-store.d.ts +10 -0
  247. package/dist/core/substrate/injected-store.js +55 -0
  248. package/dist/core/substrate/on-read.js +68 -6
  249. package/dist/core/substrate/render.d.ts +8 -11
  250. package/dist/core/substrate/render.js +29 -43
  251. package/dist/core/substrate/schema.d.ts +16 -11
  252. package/dist/core/substrate/schema.js +32 -31
  253. package/dist/core/tui/host.d.ts +12 -4
  254. package/dist/core/tui/host.js +280 -149
  255. package/dist/core/view/bridge.d.ts +10 -0
  256. package/dist/core/view/bridge.js +31 -0
  257. package/dist/core/view/chrome.d.ts +9 -0
  258. package/dist/core/view/chrome.js +22 -0
  259. package/dist/core/view/contract.d.ts +171 -0
  260. package/dist/core/view/contract.js +23 -0
  261. package/dist/core/view/loader.d.ts +31 -0
  262. package/dist/core/view/loader.js +188 -0
  263. package/dist/core/view/transport-local.d.ts +7 -0
  264. package/dist/core/view/transport-local.js +70 -0
  265. package/dist/core/view/transport.d.ts +4 -0
  266. package/dist/core/view/transport.js +15 -0
  267. package/dist/daemon/crtrd.d.ts +38 -27
  268. package/dist/daemon/crtrd.js +303 -376
  269. package/dist/daemon/manage.js +6 -1
  270. package/dist/index.js +1 -1
  271. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +28 -20
  272. package/dist/pi-extensions/canvas-context-intro.js +4 -4
  273. package/dist/pi-extensions/canvas-doc-substrate.js +29 -20
  274. package/dist/pi-extensions/canvas-nav.js +35 -385
  275. package/dist/pi-extensions/canvas-recap.d.ts +37 -0
  276. package/dist/pi-extensions/canvas-recap.js +236 -0
  277. package/dist/pi-extensions/canvas-resume.js +2 -1
  278. package/dist/pi-extensions/canvas-stophook.d.ts +15 -13
  279. package/dist/pi-extensions/canvas-stophook.js +119 -103
  280. package/dist/pi-extensions/canvas-view.js +2 -1
  281. package/dist/pi-extensions/widget-order-bus.d.ts +6 -0
  282. package/dist/pi-extensions/widget-order-bus.js +34 -0
  283. package/dist/prompts/view.d.ts +2 -2
  284. package/dist/prompts/view.js +145 -73
  285. package/dist/types.d.ts +1 -6
  286. package/dist/types.js +1 -3
  287. package/dist/web/ViewChrome.d.ts +7 -0
  288. package/dist/web/ViewChrome.js +28 -0
  289. package/dist/web/ViewPane.d.ts +39 -0
  290. package/dist/web/ViewPane.js +48 -0
  291. package/dist/web/index.d.ts +6 -0
  292. package/dist/web/index.js +16 -0
  293. package/dist/web/runtime.d.ts +39 -0
  294. package/dist/web/runtime.js +133 -0
  295. package/dist/web/states.d.ts +24 -0
  296. package/dist/web/states.js +24 -0
  297. package/dist/web/transport-http.d.ts +5 -0
  298. package/dist/web/transport-http.js +28 -0
  299. package/dist/web-client/assets/index-BUvQb4hR.css +2 -0
  300. package/dist/web-client/assets/index-ClLQXYAE.js +10 -0
  301. package/dist/web-client/index.html +13 -0
  302. package/package.json +20 -6
  303. package/dist/builtin-views/canvas/client.mjs +0 -303
  304. package/dist/builtin-views/canvas/view.mjs +0 -576
  305. package/dist/builtin-views/git-pr/client.mjs +0 -440
  306. package/dist/builtin-views/git-pr/view.mjs +0 -675
  307. package/dist/builtin-views/inbox/sources/gmail.mjs +0 -965
  308. package/dist/builtin-views/inbox/sources/linkedin.mjs +0 -427
  309. package/dist/builtin-views/inbox/view.mjs +0 -889
  310. package/dist/builtin-views/linkedin/client.mjs +0 -610
  311. package/dist/builtin-views/linkedin/view.mjs +0 -1171
  312. package/dist/commands/pkg/bridge.d.ts +0 -1
  313. package/dist/commands/pkg/bridge.js +0 -137
  314. package/dist/commands/skill/author.d.ts +0 -3
  315. package/dist/commands/skill/author.js +0 -140
  316. package/dist/commands/skill/shared.d.ts +0 -3
  317. package/dist/commands/skill/shared.js +0 -19
  318. package/dist/commands/skill.d.ts +0 -2
  319. package/dist/commands/skill.js +0 -21
  320. package/dist/commands/tmux-spread.d.ts +0 -2
  321. package/dist/commands/tmux-spread.js +0 -144
  322. package/dist/core/__tests__/full/daemon-liveness-pane.full.test.js +0 -292
  323. package/dist/core/__tests__/full/placement-focus.test.js +0 -309
  324. package/dist/core/__tests__/full/placement-reconcile.test.js +0 -212
  325. package/dist/core/__tests__/full/placement-revive.test.js +0 -238
  326. package/dist/core/__tests__/full/placement-teardown.test.js +0 -221
  327. package/dist/core/__tests__/home-session.test.js +0 -186
  328. package/dist/core/__tests__/relaunch.test.js +0 -335
  329. package/dist/core/bridge-map.d.ts +0 -19
  330. package/dist/core/bridge-map.js +0 -73
  331. package/dist/core/tui/contract.d.ts +0 -83
  332. package/dist/core/tui/contract.js +0 -8
  333. package/dist/core/tui/loader.d.ts +0 -16
  334. package/dist/core/tui/loader.js +0 -94
  335. package/dist/prompts/skill.d.ts +0 -2
  336. package/dist/prompts/skill.js +0 -650
  337. /package/dist/{core/__tests__/full/daemon-liveness-pane.full.test.d.ts → clients/attach/__tests__/action-parity.test.d.ts} +0 -0
  338. /package/dist/{core/__tests__/full/placement-focus.test.d.ts → clients/attach/__tests__/clipboard-image.test.d.ts} +0 -0
  339. /package/dist/{core/__tests__/full/placement-reconcile.test.d.ts → clients/attach/__tests__/editor-newline.test.d.ts} +0 -0
  340. /package/dist/{core/__tests__/full/placement-revive.test.d.ts → clients/attach/__tests__/git-info.test.d.ts} +0 -0
  341. /package/dist/{core/__tests__/full/placement-teardown.test.d.ts → clients/attach/__tests__/onrequest-wired.test.d.ts} +0 -0
  342. /package/dist/{core/__tests__/home-session.test.d.ts → clients/attach/__tests__/reconnect-giveup.test.d.ts} +0 -0
  343. /package/dist/{core/__tests__/relaunch.test.d.ts → clients/attach/__tests__/slash-quit-copy.test.d.ts} +0 -0
@@ -0,0 +1,43 @@
1
+ import { type Component, type TUI } from '@earendil-works/pi-tui';
2
+ import type { AttachPalette } from './config-load.js';
3
+ export declare class GraphOverlay implements Component {
4
+ private readonly tui;
5
+ private readonly self;
6
+ private readonly getAsks;
7
+ private readonly palette;
8
+ private handle;
9
+ /** Manual fold OVERRIDES (h collapses → userCollapsed, l expands → userExpanded);
10
+ * both override the default activity-driven policy and survive open/close. */
11
+ private readonly userExpanded;
12
+ private readonly userCollapsed;
13
+ private readonly folds;
14
+ /** Cursor as a node id (indices shift as topology changes). */
15
+ private cursorId;
16
+ /** Viewport scroll offset (row index of the top visible tree row). */
17
+ private scrollTop;
18
+ /** Transient y/n confirm gate (kill). */
19
+ private pendingConfirm;
20
+ constructor(tui: TUI, self: string, getAsks: () => Record<string, number>, palette: AttachPalette);
21
+ isOpen(): boolean;
22
+ open(): void;
23
+ close(): void;
24
+ toggle(): void;
25
+ /** Repaint if shown (the low-rate ask poll calls this). */
26
+ refresh(): void;
27
+ invalidate(): void;
28
+ render(width: number): string[];
29
+ /** A top/bottom border row: `<lc>─ <label> ────<rc>`, frame in the theme border
30
+ * color, the label keeping its own styling. The label is visible-truncated to
31
+ * the available span so a long label (a narrow pane, or a long `kill <name>?`
32
+ * confirm) never spills past the corner. */
33
+ private borderRow;
34
+ handleInput(data: string): void;
35
+ private dispatch;
36
+ /** Focus node `id` from the overlay by shelling `crtr node focus <id> --pane <here>`.
37
+ * Under the one-viewer-per-node model `--pane` is the ANCHOR to open beside: if
38
+ * `id` has no viewer yet a fresh one splits beside this pane; if it already has
39
+ * one, that viewer is navigated in place (this pane is left as-is). We pass
40
+ * TMUX_PANE explicitly so the anchor is THIS pane even if env resolution differs. */
41
+ private focusTarget;
42
+ private shellCrtr;
43
+ }
@@ -0,0 +1,306 @@
1
+ // graph-overlay.ts — the alt+g GRAPH navigator overlay for `crtr attach` (Unit Q).
2
+ //
3
+ // Native reimplementation of canvas-nav.ts's GRAPH modal as a pi-tui OVERLAY
4
+ // (the viewer has no pi extension host). `tui.showOverlay(this, …)` mounts this
5
+ // Component as a BOUNDED, centered modal (~72% of the terminal, themed frame) and
6
+ // CAPTURES keyboard focus (mirrors extension-dialogs.ts);
7
+ // `OverlayHandle.hide()` tears it down and restores focus to the editor. While
8
+ // shown, every key routes to handleInput() — a fold-aware NERDTree of the local
9
+ // subscription graph with the canvas-nav keymap:
10
+ // j/k move · h/l fold/ascend-descend · g/G top/bottom · ↵ focus (swap into
11
+ // this pane) · m focus manager · x kill (y/n confirm) · esc close.
12
+ // `e` is a tmux-menu prefixBind, not a GRAPH key, so it is swallowed (per
13
+ // canvas-nav). All model/render comes from the shared nav-model layer; keys are
14
+ // decoded with pi-tui's canonical matchesKey (kitty/CSI-u aware).
15
+ //
16
+ // Enter/m/x SHELL `crtr` out-of-process (node focus / node close) — the viewer
17
+ // itself never spawns pi or opens a session (the §0 one-writer invariant). It
18
+ // passes its own pane as `crtr node focus --pane <anchor>`: under the
19
+ // one-viewer-per-node model the target's existing viewer is navigated in place,
20
+ // or a fresh viewer opens BESIDE this pane — never a second pane for the same node.
21
+ import { execFile } from 'node:child_process';
22
+ import { matchesKey, truncateToWidth } from '@earendil-works/pi-tui';
23
+ import { fullName } from '../../core/canvas/index.js';
24
+ import { beginFrame, cNode, managerOf, sortedChildIds, climbRoot, computeSubtreeActivity, buildGraphModel, renderGraphRow, focusedNodeIds, shortId, visibleWidth, VIEWPORT_FALLBACK_ROWS, DIM, RESET, BOLD, YELLOW, } from '../../core/canvas/nav-model.js';
25
+ // A BOUNDED, centered modal (CTO ruling: not full-bleed). Width/height are capped
26
+ // at ~72% of the terminal; the overlay clips our lines to maxHeight, and render()
27
+ // budgets the panel to the SAME fraction (HEIGHT_PCT) so the framed box is never
28
+ // clipped. margin keeps it off the screen edges.
29
+ const HEIGHT_PCT = 0.72;
30
+ const OVERLAY_OPTIONS = { anchor: 'center', width: '72%', minWidth: 48, maxHeight: '72%', margin: 1 };
31
+ // Viewer-specific hint: nav-model's shared GRAPH_HINT advertises "e expand",
32
+ // but that key is the tmux pane-expand prefix-bind canvas-nav installs in its
33
+ // host pane — the attach overlay has no such bind and swallows `e`, so the key
34
+ // would do nothing here. Drop it.
35
+ const GRAPH_HINT = `${DIM}jk move · hl fold · ↵ focus · x kill · m mgr · esc${RESET}`;
36
+ export class GraphOverlay {
37
+ tui;
38
+ self;
39
+ getAsks;
40
+ palette;
41
+ handle;
42
+ /** Manual fold OVERRIDES (h collapses → userCollapsed, l expands → userExpanded);
43
+ * both override the default activity-driven policy and survive open/close. */
44
+ userExpanded = new Set();
45
+ userCollapsed = new Set();
46
+ folds = { userExpanded: this.userExpanded, userCollapsed: this.userCollapsed };
47
+ /** Cursor as a node id (indices shift as topology changes). */
48
+ cursorId;
49
+ /** Viewport scroll offset (row index of the top visible tree row). */
50
+ scrollTop = 0;
51
+ /** Transient y/n confirm gate (kill). */
52
+ pendingConfirm;
53
+ constructor(tui, self, getAsks, palette) {
54
+ this.tui = tui;
55
+ this.self = self;
56
+ this.getAsks = getAsks;
57
+ this.palette = palette;
58
+ }
59
+ isOpen() {
60
+ return this.handle !== undefined;
61
+ }
62
+ open() {
63
+ if (this.handle !== undefined)
64
+ return;
65
+ this.scrollTop = 0;
66
+ this.pendingConfirm = undefined;
67
+ if (this.cursorId === undefined || cNode(this.cursorId) === null)
68
+ this.cursorId = this.self;
69
+ this.handle = this.tui.showOverlay(this, OVERLAY_OPTIONS);
70
+ this.tui.requestRender();
71
+ }
72
+ close() {
73
+ if (this.handle === undefined)
74
+ return;
75
+ this.pendingConfirm = undefined;
76
+ this.handle.hide();
77
+ this.handle = undefined;
78
+ this.tui.requestRender();
79
+ }
80
+ toggle() {
81
+ if (this.handle === undefined)
82
+ this.open();
83
+ else
84
+ this.close();
85
+ }
86
+ /** Repaint if shown (the low-rate ask poll calls this). */
87
+ refresh() {
88
+ if (this.handle !== undefined)
89
+ this.tui.requestRender();
90
+ }
91
+ invalidate() {
92
+ /* no cached render state — rebuilt every render() */
93
+ }
94
+ // -------------------------------------------------------------------------
95
+ render(width) {
96
+ // Fresh snapshot per paint (mirror canvas-nav's per-frame cache).
97
+ beginFrame();
98
+ const asks = this.getAsks();
99
+ const activity = computeSubtreeActivity(climbRoot(this.self), this.self);
100
+ const rows = buildGraphModel(this.self, this.folds, activity.expand);
101
+ // Re-resolve the cursor id → row (it may have vanished under a fold/close).
102
+ let cursorIdx = rows.findIndex((r) => r.id === this.cursorId);
103
+ if (cursorIdx < 0) {
104
+ cursorIdx = rows.findIndex((r) => r.id === this.self);
105
+ if (cursorIdx < 0)
106
+ cursorIdx = 0;
107
+ }
108
+ this.cursorId = rows[cursorIdx]?.id ?? this.self;
109
+ // Bounded panel budget (centered modal, not full-bleed): the box height
110
+ // shrinks to content but is capped at HEIGHT_PCT of the terminal — matching
111
+ // the overlay's maxHeight so our framed lines are never clipped. 2 rows go to
112
+ // the top/bottom border, the rest to tree rows.
113
+ const term = process.stdout.rows ?? VIEWPORT_FALLBACK_ROWS;
114
+ // Never exceed the overlay's own maxHeight (term-2 after the margin), so the
115
+ // bottom border is never slice-clipped even on a very short terminal.
116
+ const maxPanelH = Math.max(3, Math.min(Math.floor(term * HEIGHT_PCT), term - 2));
117
+ const treeArea = Math.max(2, Math.min(rows.length, maxPanelH - 2));
118
+ // Track the cursor within treeArea, reserving ↑/↓ "more" indicators. Up to
119
+ // 4 passes converge the mutual dependency between window size and indicators
120
+ // (an indicator steals a tree row → can re-home scrollTop → toggles the other
121
+ // indicator); see canvas-nav.ts renderGraph for the rationale.
122
+ let viewportH = treeArea;
123
+ for (let pass = 0; pass < 4; pass++) {
124
+ if (cursorIdx < this.scrollTop)
125
+ this.scrollTop = cursorIdx;
126
+ if (cursorIdx >= this.scrollTop + viewportH)
127
+ this.scrollTop = cursorIdx - viewportH + 1;
128
+ this.scrollTop = Math.max(0, Math.min(this.scrollTop, Math.max(0, rows.length - viewportH)));
129
+ const fit = treeArea - (this.scrollTop > 0 ? 1 : 0) - (this.scrollTop + viewportH < rows.length ? 1 : 0);
130
+ if (fit === viewportH)
131
+ break;
132
+ viewportH = Math.max(1, fit);
133
+ }
134
+ const end = Math.min(rows.length, this.scrollTop + viewportH);
135
+ const focused = focusedNodeIds();
136
+ const body = [];
137
+ if (this.scrollTop > 0)
138
+ body.push(`${DIM} ↑ ${this.scrollTop} more${RESET}`);
139
+ for (let i = this.scrollTop; i < end; i++) {
140
+ body.push(renderGraphRow(rows[i], i === cursorIdx, focused, activity.activeBelow, asks));
141
+ }
142
+ if (end < rows.length)
143
+ body.push(`${DIM} ↓ ${rows.length - end} more${RESET}`);
144
+ while (body.length < treeArea)
145
+ body.push(''); // fill so the overlay is opaque
146
+ const title = `${BOLD}⌗ canvas graph${RESET} ${DIM}(${cursorIdx + 1}/${rows.length})${RESET}`;
147
+ const hint = this.pendingConfirm !== undefined
148
+ ? `${YELLOW}${this.pendingConfirm.label} ${BOLD}y/n${RESET}`
149
+ : GRAPH_HINT;
150
+ // Frame the panel: title in the top border, hint in the bottom border, tree
151
+ // rows boxed with themed side bars (truncate/pad to the inner width so the
152
+ // right edge stays flush and opaque).
153
+ const B = this.palette.border;
154
+ const innerW = Math.max(1, width - 4); // "│ " + content + " │"
155
+ // Hard-cut (empty ellipsis) + pad to innerW: ANSI-aware, so the cursor fill
156
+ // bar reaches the right border flush with no stray "…" and the box edge is
157
+ // opaque.
158
+ const boxed = body.map((line) => `${B('│')} ${truncateToWidth(line, innerW, '', true)} ${B('│')}`);
159
+ // Paint the WHOLE modal — border rows + boxed body + opaque space-fill — on
160
+ // the theme's distinct-surface background (selectedBg) so the float reads as
161
+ // a separate surface, not a hole in the viewer behind it (CTO ruling).
162
+ const S = this.palette.surface;
163
+ return [
164
+ this.borderRow('╭', '╮', title, width),
165
+ ...boxed,
166
+ this.borderRow('╰', '╯', hint, width),
167
+ ].map(S);
168
+ }
169
+ /** A top/bottom border row: `<lc>─ <label> ────<rc>`, frame in the theme border
170
+ * color, the label keeping its own styling. The label is visible-truncated to
171
+ * the available span so a long label (a narrow pane, or a long `kill <name>?`
172
+ * confirm) never spills past the corner. */
173
+ borderRow(lc, rc, label, width) {
174
+ const B = this.palette.border;
175
+ const span = Math.max(1, width - 5); // lc ─ _ <span> _ ─… rc
176
+ const lbl = truncateToWidth(label, span, '…');
177
+ const fill = Math.max(0, width - 5 - visibleWidth(lbl));
178
+ return `${B(`${lc}─`)} ${lbl} ${B('─'.repeat(fill) + rc)}`;
179
+ }
180
+ // -------------------------------------------------------------------------
181
+ handleInput(data) {
182
+ try {
183
+ this.dispatch(data);
184
+ }
185
+ catch {
186
+ /* a key handler is best-effort; never throw out of the input pump */
187
+ }
188
+ }
189
+ dispatch(data) {
190
+ // y/n confirm gate takes precedence over everything.
191
+ if (this.pendingConfirm !== undefined) {
192
+ if (matchesKey(data, 'y')) {
193
+ const act = this.pendingConfirm.action;
194
+ this.pendingConfirm = undefined;
195
+ act();
196
+ }
197
+ else {
198
+ this.pendingConfirm = undefined; // any other key cancels
199
+ }
200
+ this.tui.requestRender();
201
+ return;
202
+ }
203
+ if (matchesKey(data, 'escape')) {
204
+ this.close();
205
+ return;
206
+ }
207
+ const rows = buildGraphModel(this.self, this.folds);
208
+ let idx = rows.findIndex((r) => r.id === this.cursorId);
209
+ if (idx < 0)
210
+ idx = Math.max(0, rows.findIndex((r) => r.id === this.self));
211
+ const cur = rows[idx];
212
+ if (matchesKey(data, 'j')) {
213
+ idx = Math.min(rows.length - 1, idx + 1);
214
+ this.cursorId = rows[idx]?.id ?? this.cursorId;
215
+ this.tui.requestRender();
216
+ return;
217
+ }
218
+ if (matchesKey(data, 'k')) {
219
+ idx = Math.max(0, idx - 1);
220
+ this.cursorId = rows[idx]?.id ?? this.cursorId;
221
+ this.tui.requestRender();
222
+ return;
223
+ }
224
+ if (matchesKey(data, 'g')) {
225
+ this.cursorId = rows[0]?.id ?? this.cursorId;
226
+ this.tui.requestRender();
227
+ return;
228
+ }
229
+ if (matchesKey(data, 'shift+g')) {
230
+ this.cursorId = rows[rows.length - 1]?.id ?? this.cursorId;
231
+ this.tui.requestRender();
232
+ return;
233
+ }
234
+ if (matchesKey(data, 'h')) {
235
+ if (cur !== undefined && cur.hasKids && !cur.collapsed) {
236
+ this.userCollapsed.add(cur.id);
237
+ this.userExpanded.delete(cur.id);
238
+ }
239
+ else {
240
+ const p = managerOf(this.cursorId ?? this.self);
241
+ if (p !== undefined && rows.some((r) => r.id === p))
242
+ this.cursorId = p;
243
+ }
244
+ this.tui.requestRender();
245
+ return;
246
+ }
247
+ if (matchesKey(data, 'l')) {
248
+ if (cur !== undefined && cur.collapsed && cur.hasKids) {
249
+ this.userExpanded.add(cur.id);
250
+ this.userCollapsed.delete(cur.id);
251
+ }
252
+ else if (cur !== undefined && cur.hasKids) {
253
+ const c = sortedChildIds(cur.id)[0];
254
+ if (c !== undefined)
255
+ this.cursorId = c;
256
+ }
257
+ this.tui.requestRender();
258
+ return;
259
+ }
260
+ if (matchesKey(data, 'enter')) {
261
+ if (this.cursorId !== undefined)
262
+ this.focusTarget(this.cursorId);
263
+ this.close();
264
+ return;
265
+ }
266
+ if (matchesKey(data, 'm')) {
267
+ const mgr = managerOf(this.self);
268
+ if (mgr !== undefined) {
269
+ this.focusTarget(mgr);
270
+ this.close();
271
+ }
272
+ return;
273
+ }
274
+ if (matchesKey(data, 'x')) {
275
+ const target = this.cursorId ?? this.self;
276
+ const n = cNode(target);
277
+ const nm = n !== null ? fullName(n) : shortId(target);
278
+ this.pendingConfirm = { label: `kill ${nm}?`, action: () => this.shellCrtr(['node', 'close', '--node', target]) };
279
+ this.tui.requestRender();
280
+ return;
281
+ }
282
+ // Modal: swallow everything else (incl. the `e` tmux-menu bind) so a stray
283
+ // key never reaches the editor underneath.
284
+ }
285
+ // -------------------------------------------------------------------------
286
+ /** Focus node `id` from the overlay by shelling `crtr node focus <id> --pane <here>`.
287
+ * Under the one-viewer-per-node model `--pane` is the ANCHOR to open beside: if
288
+ * `id` has no viewer yet a fresh one splits beside this pane; if it already has
289
+ * one, that viewer is navigated in place (this pane is left as-is). We pass
290
+ * TMUX_PANE explicitly so the anchor is THIS pane even if env resolution differs. */
291
+ focusTarget(id) {
292
+ const argv = ['node', 'focus', id];
293
+ const pane = process.env['TMUX_PANE'];
294
+ if (pane !== undefined && pane !== '')
295
+ argv.push('--pane', pane);
296
+ this.shellCrtr(argv);
297
+ }
298
+ shellCrtr(argv) {
299
+ try {
300
+ execFile('crtr', argv, () => { });
301
+ }
302
+ catch {
303
+ /* best-effort */
304
+ }
305
+ }
306
+ }
@@ -1,6 +1,8 @@
1
1
  import type { CustomEditor } from '@earendil-works/pi-coding-agent';
2
- import type { KeybindingsManager, TUI } from '@earendil-works/pi-tui';
3
- import { type BrokerSnapshot, type ClientToBroker, type RpcExtensionUIRequest, type RpcExtensionUIResponse } from '../../core/runtime/broker-protocol.js';
2
+ import type { Component, KeybindingsManager, TUI } from '@earendil-works/pi-tui';
3
+ import { type BrokerDataFrame, type BrokerSnapshot, type ClientToBroker, type RpcExtensionUIRequest, type RpcExtensionUIResponse } from '../../core/runtime/broker-protocol.js';
4
+ import type { ReadOpRequest } from './view-socket.js';
5
+ import { type Picker, type PickerControls } from './pickers.js';
4
6
  export interface InputControllerHooks {
5
7
  /** Send a command frame to the broker. */
6
8
  onCommand: (frame: ClientToBroker) => void;
@@ -11,20 +13,56 @@ export interface InputControllerHooks {
11
13
  * reports notices up the same way it reports commands. Absent → notices are
12
14
  * dropped silently. */
13
15
  onNotice?: (message: string) => void;
16
+ /** OPTIONAL: the canvas node this viewer is attached to — forwarded to the
17
+ * slash context so `/promote` targets it (Unit Q wires it from runAttach). */
18
+ nodeId?: string;
19
+ /** OPTIONAL: toggle the GRAPH overlay — forwarded to the slash context so
20
+ * `/graph` opens/closes it (Unit Q wires it from runAttach). */
21
+ onGraph?: () => void;
22
+ /** OPTIONAL: detach the viewer — forwarded to the slash context so `/quit`
23
+ * tears down the TUI/socket (the engine keeps running). Wired by attach-cmd. */
24
+ onQuit?: () => void;
25
+ /** OPTIONAL: copy the last assistant message to the clipboard — forwarded to
26
+ * the slash context so `/copy` works. Wired by attach-cmd (it owns ChatView). */
27
+ onCopy?: () => void;
28
+ /** OPTIONAL: Ctrl+O / app.tools.expand — flip global tool-output expansion in
29
+ * the render layer (ChatView owns the components). Absent → the key no-ops. */
30
+ onToggleToolsExpand?: () => void;
31
+ /** OPTIONAL: Ctrl+T / app.thinking.toggle — flip thinking-block visibility in
32
+ * the render layer. Absent → the key no-ops. */
33
+ onToggleThinking?: () => void;
34
+ /** OPTIONAL: issue a correlated read-op and resolve with the broker's `data`
35
+ * reply. MUST be wired by attach-cmd (`onRequest: (f) => socket.request(f)`)
36
+ * for the native pickers to work; when absent every picker degrades to a
37
+ * text-arg notice. */
38
+ onRequest?: (frame: ReadOpRequest) => Promise<BrokerDataFrame>;
39
+ /** OPTIONAL pair: mount/unmount a native picker INLINE in the viewer's layout
40
+ * (attach-cmd slots it directly under the editor). When BOTH are wired,
41
+ * showPicker uses them instead of a centered overlay; absent → overlay
42
+ * fallback. The controller stays layout-agnostic either way (T5/T7 own it). */
43
+ onMountPicker?: (component: Component) => void;
44
+ onUnmountPicker?: () => void;
45
+ /** OPTIONAL: open the native /login provider picker (viewer-local auth flow). */
46
+ openLoginPicker?: () => void;
47
+ /** OPTIONAL: open the native /logout provider picker (viewer-local auth flow). */
48
+ openLogoutPicker?: () => void;
14
49
  }
15
50
  export declare class InputController {
16
51
  private readonly tui;
17
52
  private readonly editor;
18
53
  private readonly keybindings;
19
54
  private readonly hooks;
20
- /** Images pasted since the last send, attached to the next prompt/follow-up. */
21
- private pendingImages;
22
- /** Running base64-byte total of `pendingImages` (review M2 aggregate budget). */
23
- private pendingImageBytes;
24
55
  /** The currently-rendered blocking dialog, if any (for supersede/dismiss). */
25
56
  private dialog;
57
+ /** The currently-open native picker overlay, if any (for supersede/dismiss). */
58
+ private picker;
59
+ /** Monotonic open counter — a slow read-op reply whose `seq` is no longer the
60
+ * latest is dropped, so an earlier picker never pops over a later request. */
61
+ private openSeq;
26
62
  /** Latest engine state from `welcome`/`session_info_changed` (for `/session`). */
27
63
  private state;
64
+ /** Timestamp of the last lone Esc, for double-tap → tree detection (0 = none). */
65
+ private lastEscapeAt;
28
66
  constructor(tui: TUI, editor: CustomEditor, keybindings: KeybindingsManager, hooks: InputControllerHooks);
29
67
  /** Render a blocking extension dialog and route its answer to the broker.
30
68
  * Supersedes any dialog already on screen (e.g. a re-route on control
@@ -35,20 +73,50 @@ export declare class InputController {
35
73
  setState(state: BrokerSnapshot['state']): void;
36
74
  private wire;
37
75
  private slashContext;
76
+ /** Send a command frame to the broker (picker selection sink). */
77
+ private send;
78
+ /** Fetch a picker payload (narrowed on `kind`), then build + show its overlay
79
+ * iff this open is still the latest (m4: a stale slow reply is dropped). Any
80
+ * failure — unwired channel, wrong kind, request error, or a throwing builder
81
+ * — surfaces a notice instead of an unhandled rejection (m5). */
82
+ private openPicker;
83
+ /** Show a picker built by `build` (given a `close` that tears it down).
84
+ * Mounted INLINE under the editor when attach-cmd wires onMountPicker/
85
+ * onUnmountPicker (the integrated chrome path); otherwise falls back to a
86
+ * centered overlay. Supersedes any picker already on screen. Routes focus to
87
+ * the builder's declared focus target (an inner list for fork/settings) and
88
+ * ALWAYS restores editor focus on close — `OverlayHandle.hide()` only
89
+ * auto-restores when the OUTER component held focus, which is not the case
90
+ * once we focus an inner child. A throwing builder is caught into a notice (m5). */
91
+ private showPicker;
92
+ private openModelPicker;
93
+ private openSessionPicker;
94
+ private openTreePicker;
95
+ private openForkPicker;
96
+ private openSettingsPicker;
97
+ private openScopedModelsPicker;
98
+ /** Open a viewer-local auth picker (login/logout) — uses the same showPicker
99
+ * machinery as read-op pickers, but with a locally-built component (no read-op
100
+ * request; auth operations are viewer-local in tmux-local attach). */
101
+ openAuthPicker(build: (controls: PickerControls) => Picker): void;
38
102
  private handleSubmit;
39
103
  private handleFollowUp;
104
+ /** Ctrl+G / app.editor.external — open the current editor text in $VISUAL/$EDITOR
105
+ * and load the saved result back. Ported from pi's `openExternalEditor`: stop
106
+ * the TUI to release the terminal, run the editor with inherited stdio, and on
107
+ * a clean exit (status 0) replace the editor text; the socket keeps running in
108
+ * the background throughout. */
109
+ private openExternalEditor;
110
+ /** Alt+Up / app.message.dequeue — clear the broker's queued steering + follow-up
111
+ * messages and restore them to the editor, prepended to any in-progress text
112
+ * (pi's `restoreQueuedMessagesToEditor`: queued text first, joined by blank
113
+ * lines). A read-AND-mutate op over the correlated request channel. */
114
+ private handleDequeue;
40
115
  /** Send a drive frame iff the WHOLE encoded frame fits under MAX_FRAME_BYTES
41
116
  * (the broker destroys the socket on any line over its 24 MiB read cap). Over
42
117
  * the ceiling → notify + refuse so the caller leaves the editor + pending
43
118
  * images intact for the user to trim, never a socket-destroying overflow. */
44
119
  private emitDrive;
45
120
  private handlePaste;
46
- /** Snapshot the pending images as an `images?` payload (undefined when none)
47
- * WITHOUT clearing — so a frame refused by `emitDrive` (too large) keeps them
48
- * for the user to trim. The caller clears via `clearPendingImages` only after a
49
- * successful send. */
50
- private pendingImagesPayload;
51
- /** Drop all pending images + reset the aggregate byte counter (after a send). */
52
- private clearPendingImages;
53
121
  private notify;
54
122
  }