4runr-os 2.1.4 → 2.1.6

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 (566) hide show
  1. package/dist/index.js +16 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/tui_mk1/kernel.d.ts.map +1 -1
  4. package/dist/tui_mk1/kernel.js +107 -4
  5. package/dist/tui_mk1/kernel.js.map +1 -1
  6. package/dist/tui_mk1/layout/layoutEngine.d.ts +38 -19
  7. package/dist/tui_mk1/layout/layoutEngine.d.ts.map +1 -1
  8. package/dist/tui_mk1/layout/layoutEngine.js +521 -228
  9. package/dist/tui_mk1/layout/layoutEngine.js.map +1 -1
  10. package/dist/tui_mk1/log.d.ts +23 -0
  11. package/dist/tui_mk1/log.d.ts.map +1 -0
  12. package/dist/tui_mk1/log.js +125 -0
  13. package/dist/tui_mk1/log.js.map +1 -0
  14. package/dist/tui_mk1/logger.d.ts.map +1 -1
  15. package/dist/tui_mk1/logger.js +20 -10
  16. package/dist/tui_mk1/logger.js.map +1 -1
  17. package/dist/tui_mk1/mk1App.d.ts +13 -18
  18. package/dist/tui_mk1/mk1App.d.ts.map +1 -1
  19. package/dist/tui_mk1/mk1App.js +334 -157
  20. package/dist/tui_mk1/mk1App.js.map +1 -1
  21. package/dist/tui_mk1/resizeController.d.ts.map +1 -1
  22. package/dist/tui_mk1/resizeController.js +15 -1
  23. package/dist/tui_mk1/resizeController.js.map +1 -1
  24. package/dist/tui_mk1/stdoutGuard.d.ts.map +1 -1
  25. package/dist/tui_mk1/stdoutGuard.js +7 -0
  26. package/dist/tui_mk1/stdoutGuard.js.map +1 -1
  27. package/dist/tui_mk1/terminalRestore.d.ts.map +1 -1
  28. package/dist/tui_mk1/terminalRestore.js +19 -1
  29. package/dist/tui_mk1/terminalRestore.js.map +1 -1
  30. package/dist/tui_mk1/ui/offenderScanner.d.ts +31 -0
  31. package/dist/tui_mk1/ui/offenderScanner.d.ts.map +1 -0
  32. package/dist/tui_mk1/ui/offenderScanner.js +80 -0
  33. package/dist/tui_mk1/ui/offenderScanner.js.map +1 -0
  34. package/dist/tui_mk1/ui/safe.d.ts +9 -0
  35. package/dist/tui_mk1/ui/safe.d.ts.map +1 -0
  36. package/dist/tui_mk1/ui/safe.js +29 -0
  37. package/dist/tui_mk1/ui/safe.js.map +1 -0
  38. package/dist/tui_mk1/ui/safeText.d.ts +22 -0
  39. package/dist/tui_mk1/ui/safeText.d.ts.map +1 -0
  40. package/dist/tui_mk1/ui/safeText.js +50 -0
  41. package/dist/tui_mk1/ui/safeText.js.map +1 -0
  42. package/dist/tui_mk1/ui/uiBuilder.d.ts +44 -0
  43. package/dist/tui_mk1/ui/uiBuilder.d.ts.map +1 -0
  44. package/dist/tui_mk1/ui/uiBuilder.js +467 -0
  45. package/dist/tui_mk1/ui/uiBuilder.js.map +1 -0
  46. package/dist/tui_mk1/viewport/safeViewport.d.ts +10 -1
  47. package/dist/tui_mk1/viewport/safeViewport.d.ts.map +1 -1
  48. package/dist/tui_mk1/viewport/safeViewport.js +47 -15
  49. package/dist/tui_mk1/viewport/safeViewport.js.map +1 -1
  50. package/dist/tui_mk2/kernel.d.ts +28 -0
  51. package/dist/tui_mk2/kernel.d.ts.map +1 -0
  52. package/dist/tui_mk2/kernel.js +138 -0
  53. package/dist/tui_mk2/kernel.js.map +1 -0
  54. package/dist/tui_mk2/mk2App.d.ts +33 -0
  55. package/dist/tui_mk2/mk2App.d.ts.map +1 -0
  56. package/dist/tui_mk2/mk2App.js +135 -0
  57. package/dist/tui_mk2/mk2App.js.map +1 -0
  58. package/package.json +8 -7
  59. package/dist/tui_mk1/ui/panels/createSection.d.ts +0 -18
  60. package/dist/tui_mk1/ui/panels/createSection.d.ts.map +0 -1
  61. package/dist/tui_mk1/ui/panels/createSection.js +0 -42
  62. package/dist/tui_mk1/ui/panels/createSection.js.map +0 -1
  63. package/dist/tui_mk1/ui/panels.d.ts +0 -23
  64. package/dist/tui_mk1/ui/panels.d.ts.map +0 -1
  65. package/dist/tui_mk1/ui/panels.js +0 -108
  66. package/dist/tui_mk1/ui/panels.js.map +0 -1
  67. package/dist/tui_mk1/ui/render/sectionRenderer.d.ts +0 -21
  68. package/dist/tui_mk1/ui/render/sectionRenderer.d.ts.map +0 -1
  69. package/dist/tui_mk1/ui/render/sectionRenderer.js +0 -32
  70. package/dist/tui_mk1/ui/render/sectionRenderer.js.map +0 -1
  71. package/dist/tui_mk1/ui/widgetManager.d.ts +0 -58
  72. package/dist/tui_mk1/ui/widgetManager.d.ts.map +0 -1
  73. package/dist/tui_mk1/ui/widgetManager.js +0 -197
  74. package/dist/tui_mk1/ui/widgetManager.js.map +0 -1
  75. package/dist/ui/boot/sequence.d.ts +0 -10
  76. package/dist/ui/boot/sequence.d.ts.map +0 -1
  77. package/dist/ui/boot/sequence.js +0 -171
  78. package/dist/ui/boot/sequence.js.map +0 -1
  79. package/dist/ui/constraints/layoutSpec.d.ts +0 -47
  80. package/dist/ui/constraints/layoutSpec.d.ts.map +0 -1
  81. package/dist/ui/constraints/layoutSpec.js +0 -60
  82. package/dist/ui/constraints/layoutSpec.js.map +0 -1
  83. package/dist/ui/constraints/unknownHandling.d.ts +0 -29
  84. package/dist/ui/constraints/unknownHandling.d.ts.map +0 -1
  85. package/dist/ui/constraints/unknownHandling.js +0 -60
  86. package/dist/ui/constraints/unknownHandling.js.map +0 -1
  87. package/dist/ui/drilldowns/feed.d.ts +0 -11
  88. package/dist/ui/drilldowns/feed.d.ts.map +0 -1
  89. package/dist/ui/drilldowns/feed.js +0 -68
  90. package/dist/ui/drilldowns/feed.js.map +0 -1
  91. package/dist/ui/drilldowns/index.d.ts +0 -7
  92. package/dist/ui/drilldowns/index.d.ts.map +0 -1
  93. package/dist/ui/drilldowns/index.js +0 -8
  94. package/dist/ui/drilldowns/index.js.map +0 -1
  95. package/dist/ui/drilldowns/posture.d.ts +0 -11
  96. package/dist/ui/drilldowns/posture.d.ts.map +0 -1
  97. package/dist/ui/drilldowns/posture.js +0 -74
  98. package/dist/ui/drilldowns/posture.js.map +0 -1
  99. package/dist/ui/intelligence-posture-view.d.ts +0 -22
  100. package/dist/ui/intelligence-posture-view.d.ts.map +0 -1
  101. package/dist/ui/intelligence-posture-view.js +0 -169
  102. package/dist/ui/intelligence-posture-view.js.map +0 -1
  103. package/dist/ui/navigation/keymaps.d.ts +0 -26
  104. package/dist/ui/navigation/keymaps.d.ts.map +0 -1
  105. package/dist/ui/navigation/keymaps.js +0 -135
  106. package/dist/ui/navigation/keymaps.js.map +0 -1
  107. package/dist/ui/navigation/palette.d.ts +0 -10
  108. package/dist/ui/navigation/palette.d.ts.map +0 -1
  109. package/dist/ui/navigation/palette.js +0 -133
  110. package/dist/ui/navigation/palette.js.map +0 -1
  111. package/dist/ui/navigation/state.d.ts +0 -47
  112. package/dist/ui/navigation/state.d.ts.map +0 -1
  113. package/dist/ui/navigation/state.js +0 -84
  114. package/dist/ui/navigation/state.js.map +0 -1
  115. package/dist/ui/navigation/types.d.ts +0 -38
  116. package/dist/ui/navigation/types.d.ts.map +0 -1
  117. package/dist/ui/navigation/types.js +0 -36
  118. package/dist/ui/navigation/types.js.map +0 -1
  119. package/dist/ui/panels/active-assets.d.ts +0 -12
  120. package/dist/ui/panels/active-assets.d.ts.map +0 -1
  121. package/dist/ui/panels/active-assets.js +0 -83
  122. package/dist/ui/panels/active-assets.js.map +0 -1
  123. package/dist/ui/panels/capability-flags.d.ts +0 -12
  124. package/dist/ui/panels/capability-flags.d.ts.map +0 -1
  125. package/dist/ui/panels/capability-flags.js +0 -59
  126. package/dist/ui/panels/capability-flags.js.map +0 -1
  127. package/dist/ui/panels/command-surface.d.ts +0 -12
  128. package/dist/ui/panels/command-surface.d.ts.map +0 -1
  129. package/dist/ui/panels/command-surface.js +0 -55
  130. package/dist/ui/panels/command-surface.js.map +0 -1
  131. package/dist/ui/panels/network-origin.d.ts +0 -12
  132. package/dist/ui/panels/network-origin.d.ts.map +0 -1
  133. package/dist/ui/panels/network-origin.js +0 -79
  134. package/dist/ui/panels/network-origin.js.map +0 -1
  135. package/dist/ui/panels/operations-feed.d.ts +0 -12
  136. package/dist/ui/panels/operations-feed.d.ts.map +0 -1
  137. package/dist/ui/panels/operations-feed.js +0 -90
  138. package/dist/ui/panels/operations-feed.js.map +0 -1
  139. package/dist/ui/panels/posture.d.ts +0 -12
  140. package/dist/ui/panels/posture.d.ts.map +0 -1
  141. package/dist/ui/panels/posture.js +0 -84
  142. package/dist/ui/panels/posture.js.map +0 -1
  143. package/dist/ui/panels/resources.d.ts +0 -11
  144. package/dist/ui/panels/resources.d.ts.map +0 -1
  145. package/dist/ui/panels/resources.js +0 -88
  146. package/dist/ui/panels/resources.js.map +0 -1
  147. package/dist/ui/primitives/Panel.d.ts +0 -25
  148. package/dist/ui/primitives/Panel.d.ts.map +0 -1
  149. package/dist/ui/primitives/Panel.js +0 -59
  150. package/dist/ui/primitives/Panel.js.map +0 -1
  151. package/dist/ui/rendering/metricRenderer.d.ts +0 -24
  152. package/dist/ui/rendering/metricRenderer.d.ts.map +0 -1
  153. package/dist/ui/rendering/metricRenderer.js +0 -86
  154. package/dist/ui/rendering/metricRenderer.js.map +0 -1
  155. package/dist/ui/runtime/hub.d.ts +0 -12
  156. package/dist/ui/runtime/hub.d.ts.map +0 -1
  157. package/dist/ui/runtime/hub.js +0 -486
  158. package/dist/ui/runtime/hub.js.map +0 -1
  159. package/dist/ui/runtime/hubValidation.d.ts +0 -23
  160. package/dist/ui/runtime/hubValidation.d.ts.map +0 -1
  161. package/dist/ui/runtime/hubValidation.js +0 -90
  162. package/dist/ui/runtime/hubValidation.js.map +0 -1
  163. package/dist/ui/runtime/index.d.ts +0 -29
  164. package/dist/ui/runtime/index.d.ts.map +0 -1
  165. package/dist/ui/runtime/index.js +0 -297
  166. package/dist/ui/runtime/index.js.map +0 -1
  167. package/dist/ui/runtime/no-tui.d.ts +0 -12
  168. package/dist/ui/runtime/no-tui.d.ts.map +0 -1
  169. package/dist/ui/runtime/no-tui.js +0 -77
  170. package/dist/ui/runtime/no-tui.js.map +0 -1
  171. package/dist/ui/runtime/state-builder.d.ts +0 -13
  172. package/dist/ui/runtime/state-builder.d.ts.map +0 -1
  173. package/dist/ui/runtime/state-builder.js +0 -114
  174. package/dist/ui/runtime/state-builder.js.map +0 -1
  175. package/dist/ui/runtime/terminalSizeCheck.d.ts +0 -10
  176. package/dist/ui/runtime/terminalSizeCheck.d.ts.map +0 -1
  177. package/dist/ui/runtime/terminalSizeCheck.js +0 -51
  178. package/dist/ui/runtime/terminalSizeCheck.js.map +0 -1
  179. package/dist/ui/runtime/tuiLogGate.d.ts +0 -22
  180. package/dist/ui/runtime/tuiLogGate.d.ts.map +0 -1
  181. package/dist/ui/runtime/tuiLogGate.js +0 -68
  182. package/dist/ui/runtime/tuiLogGate.js.map +0 -1
  183. package/dist/ui/state/types.d.ts +0 -72
  184. package/dist/ui/state/types.d.ts.map +0 -1
  185. package/dist/ui/state/types.js +0 -6
  186. package/dist/ui/state/types.js.map +0 -1
  187. package/dist/ui/theme/borders.d.ts +0 -20
  188. package/dist/ui/theme/borders.d.ts.map +0 -1
  189. package/dist/ui/theme/borders.js +0 -55
  190. package/dist/ui/theme/borders.js.map +0 -1
  191. package/dist/ui/theme/tokens.d.ts +0 -28
  192. package/dist/ui/theme/tokens.d.ts.map +0 -1
  193. package/dist/ui/theme/tokens.js +0 -50
  194. package/dist/ui/theme/tokens.js.map +0 -1
  195. package/dist/ui/theme/typography.d.ts +0 -14
  196. package/dist/ui/theme/typography.d.ts.map +0 -1
  197. package/dist/ui/theme/typography.js +0 -30
  198. package/dist/ui/theme/typography.js.map +0 -1
  199. package/dist/ui/v3/collectors/assets.collector.d.ts +0 -20
  200. package/dist/ui/v3/collectors/assets.collector.d.ts.map +0 -1
  201. package/dist/ui/v3/collectors/assets.collector.js +0 -80
  202. package/dist/ui/v3/collectors/assets.collector.js.map +0 -1
  203. package/dist/ui/v3/collectors/capabilities.collector.d.ts +0 -18
  204. package/dist/ui/v3/collectors/capabilities.collector.d.ts.map +0 -1
  205. package/dist/ui/v3/collectors/capabilities.collector.js +0 -113
  206. package/dist/ui/v3/collectors/capabilities.collector.js.map +0 -1
  207. package/dist/ui/v3/collectors/network.collector.d.ts +0 -18
  208. package/dist/ui/v3/collectors/network.collector.d.ts.map +0 -1
  209. package/dist/ui/v3/collectors/network.collector.js +0 -37
  210. package/dist/ui/v3/collectors/network.collector.js.map +0 -1
  211. package/dist/ui/v3/collectors/posture.derive.d.ts +0 -24
  212. package/dist/ui/v3/collectors/posture.derive.d.ts.map +0 -1
  213. package/dist/ui/v3/collectors/posture.derive.js +0 -57
  214. package/dist/ui/v3/collectors/posture.derive.js.map +0 -1
  215. package/dist/ui/v3/collectors/resources.d.ts +0 -23
  216. package/dist/ui/v3/collectors/resources.d.ts.map +0 -1
  217. package/dist/ui/v3/collectors/resources.js +0 -136
  218. package/dist/ui/v3/collectors/resources.js.map +0 -1
  219. package/dist/ui/v3/commands/commandEngine.d.ts +0 -77
  220. package/dist/ui/v3/commands/commandEngine.d.ts.map +0 -1
  221. package/dist/ui/v3/commands/commandEngine.js +0 -3289
  222. package/dist/ui/v3/commands/commandEngine.js.map +0 -1
  223. package/dist/ui/v3/commands/commandResult.d.ts +0 -25
  224. package/dist/ui/v3/commands/commandResult.d.ts.map +0 -1
  225. package/dist/ui/v3/commands/commandResult.js +0 -19
  226. package/dist/ui/v3/commands/commandResult.js.map +0 -1
  227. package/dist/ui/v3/commands/diagnose.d.ts +0 -17
  228. package/dist/ui/v3/commands/diagnose.d.ts.map +0 -1
  229. package/dist/ui/v3/commands/diagnose.js +0 -62
  230. package/dist/ui/v3/commands/diagnose.js.map +0 -1
  231. package/dist/ui/v3/commands/errorClassifier.d.ts +0 -23
  232. package/dist/ui/v3/commands/errorClassifier.d.ts.map +0 -1
  233. package/dist/ui/v3/commands/errorClassifier.js +0 -63
  234. package/dist/ui/v3/commands/errorClassifier.js.map +0 -1
  235. package/dist/ui/v3/commands/parser.d.ts +0 -14
  236. package/dist/ui/v3/commands/parser.d.ts.map +0 -1
  237. package/dist/ui/v3/commands/parser.js +0 -29
  238. package/dist/ui/v3/commands/parser.js.map +0 -1
  239. package/dist/ui/v3/commands/router.d.ts +0 -13
  240. package/dist/ui/v3/commands/router.d.ts.map +0 -1
  241. package/dist/ui/v3/commands/router.js +0 -150
  242. package/dist/ui/v3/commands/router.js.map +0 -1
  243. package/dist/ui/v3/config/gateway.d.ts +0 -40
  244. package/dist/ui/v3/config/gateway.d.ts.map +0 -1
  245. package/dist/ui/v3/config/gateway.js +0 -113
  246. package/dist/ui/v3/config/gateway.js.map +0 -1
  247. package/dist/ui/v3/core/event.d.ts +0 -19
  248. package/dist/ui/v3/core/event.d.ts.map +0 -1
  249. package/dist/ui/v3/core/event.js +0 -7
  250. package/dist/ui/v3/core/event.js.map +0 -1
  251. package/dist/ui/v3/core/eventBus.d.ts +0 -39
  252. package/dist/ui/v3/core/eventBus.d.ts.map +0 -1
  253. package/dist/ui/v3/core/eventBus.js +0 -79
  254. package/dist/ui/v3/core/eventBus.js.map +0 -1
  255. package/dist/ui/v3/core/feedStore.d.ts +0 -34
  256. package/dist/ui/v3/core/feedStore.d.ts.map +0 -1
  257. package/dist/ui/v3/core/feedStore.js +0 -46
  258. package/dist/ui/v3/core/feedStore.js.map +0 -1
  259. package/dist/ui/v3/core/logger.d.ts +0 -40
  260. package/dist/ui/v3/core/logger.d.ts.map +0 -1
  261. package/dist/ui/v3/core/logger.js +0 -191
  262. package/dist/ui/v3/core/logger.js.map +0 -1
  263. package/dist/ui/v3/core/opEvent.d.ts +0 -15
  264. package/dist/ui/v3/core/opEvent.d.ts.map +0 -1
  265. package/dist/ui/v3/core/opEvent.js +0 -7
  266. package/dist/ui/v3/core/opEvent.js.map +0 -1
  267. package/dist/ui/v3/index.d.ts +0 -8
  268. package/dist/ui/v3/index.d.ts.map +0 -1
  269. package/dist/ui/v3/index.js +0 -51
  270. package/dist/ui/v3/index.js.map +0 -1
  271. package/dist/ui/v3/runtime/moduleConfig.d.ts +0 -21
  272. package/dist/ui/v3/runtime/moduleConfig.d.ts.map +0 -1
  273. package/dist/ui/v3/runtime/moduleConfig.js +0 -41
  274. package/dist/ui/v3/runtime/moduleConfig.js.map +0 -1
  275. package/dist/ui/v3/section0/index.d.ts +0 -22
  276. package/dist/ui/v3/section0/index.d.ts.map +0 -1
  277. package/dist/ui/v3/section0/index.js +0 -88
  278. package/dist/ui/v3/section0/index.js.map +0 -1
  279. package/dist/ui/v3/section0/runtime/createScreen.d.ts +0 -27
  280. package/dist/ui/v3/section0/runtime/createScreen.d.ts.map +0 -1
  281. package/dist/ui/v3/section0/runtime/createScreen.js +0 -55
  282. package/dist/ui/v3/section0/runtime/createScreen.js.map +0 -1
  283. package/dist/ui/v3/section0/runtime/lifecycle.d.ts +0 -53
  284. package/dist/ui/v3/section0/runtime/lifecycle.d.ts.map +0 -1
  285. package/dist/ui/v3/section0/runtime/lifecycle.js +0 -172
  286. package/dist/ui/v3/section0/runtime/lifecycle.js.map +0 -1
  287. package/dist/ui/v3/section1/index.d.ts +0 -19
  288. package/dist/ui/v3/section1/index.d.ts.map +0 -1
  289. package/dist/ui/v3/section1/index.js +0 -413
  290. package/dist/ui/v3/section1/index.js.map +0 -1
  291. package/dist/ui/v3/section1/runtime/commandLine.d.ts +0 -49
  292. package/dist/ui/v3/section1/runtime/commandLine.d.ts.map +0 -1
  293. package/dist/ui/v3/section1/runtime/commandLine.js +0 -183
  294. package/dist/ui/v3/section1/runtime/commandLine.js.map +0 -1
  295. package/dist/ui/v3/section1/runtime/focusLock.d.ts +0 -24
  296. package/dist/ui/v3/section1/runtime/focusLock.d.ts.map +0 -1
  297. package/dist/ui/v3/section1/runtime/focusLock.js +0 -44
  298. package/dist/ui/v3/section1/runtime/focusLock.js.map +0 -1
  299. package/dist/ui/v3/state/assertUiState.d.ts +0 -27
  300. package/dist/ui/v3/state/assertUiState.d.ts.map +0 -1
  301. package/dist/ui/v3/state/assertUiState.js +0 -89
  302. package/dist/ui/v3/state/assertUiState.js.map +0 -1
  303. package/dist/ui/v3/state/capabilitiesStore.d.ts +0 -54
  304. package/dist/ui/v3/state/capabilitiesStore.d.ts.map +0 -1
  305. package/dist/ui/v3/state/capabilitiesStore.js +0 -76
  306. package/dist/ui/v3/state/capabilitiesStore.js.map +0 -1
  307. package/dist/ui/v3/state/defaultState.d.ts +0 -19
  308. package/dist/ui/v3/state/defaultState.d.ts.map +0 -1
  309. package/dist/ui/v3/state/defaultState.js +0 -28
  310. package/dist/ui/v3/state/defaultState.js.map +0 -1
  311. package/dist/ui/v3/state/gatewayConnectionStore.d.ts +0 -72
  312. package/dist/ui/v3/state/gatewayConnectionStore.d.ts.map +0 -1
  313. package/dist/ui/v3/state/gatewayConnectionStore.js +0 -108
  314. package/dist/ui/v3/state/gatewayConnectionStore.js.map +0 -1
  315. package/dist/ui/v3/state/initializePostureState.d.ts +0 -23
  316. package/dist/ui/v3/state/initializePostureState.d.ts.map +0 -1
  317. package/dist/ui/v3/state/initializePostureState.js +0 -41
  318. package/dist/ui/v3/state/initializePostureState.js.map +0 -1
  319. package/dist/ui/v3/state/panelStore.d.ts +0 -80
  320. package/dist/ui/v3/state/panelStore.d.ts.map +0 -1
  321. package/dist/ui/v3/state/panelStore.js +0 -131
  322. package/dist/ui/v3/state/panelStore.js.map +0 -1
  323. package/dist/ui/v3/state/resourcesData.d.ts +0 -15
  324. package/dist/ui/v3/state/resourcesData.d.ts.map +0 -1
  325. package/dist/ui/v3/state/resourcesData.js +0 -7
  326. package/dist/ui/v3/state/resourcesData.js.map +0 -1
  327. package/dist/ui/v3/state/uiState.d.ts +0 -22
  328. package/dist/ui/v3/state/uiState.d.ts.map +0 -1
  329. package/dist/ui/v3/state/uiState.js +0 -13
  330. package/dist/ui/v3/state/uiState.js.map +0 -1
  331. package/dist/ui/v3/state/uiStateBuilder.d.ts +0 -32
  332. package/dist/ui/v3/state/uiStateBuilder.d.ts.map +0 -1
  333. package/dist/ui/v3/state/uiStateBuilder.js +0 -73
  334. package/dist/ui/v3/state/uiStateBuilder.js.map +0 -1
  335. package/dist/ui/v3/state/uiStateTypes.d.ts +0 -59
  336. package/dist/ui/v3/state/uiStateTypes.d.ts.map +0 -1
  337. package/dist/ui/v3/state/uiStateTypes.js +0 -8
  338. package/dist/ui/v3/state/uiStateTypes.js.map +0 -1
  339. package/dist/ui/v3/state/value.d.ts +0 -80
  340. package/dist/ui/v3/state/value.d.ts.map +0 -1
  341. package/dist/ui/v3/state/value.js +0 -96
  342. package/dist/ui/v3/state/value.js.map +0 -1
  343. package/dist/ui/v3/tui/geometry.d.ts +0 -83
  344. package/dist/ui/v3/tui/geometry.d.ts.map +0 -1
  345. package/dist/ui/v3/tui/geometry.js +0 -201
  346. package/dist/ui/v3/tui/geometry.js.map +0 -1
  347. package/dist/ui/v3/tui/startTui.d.ts +0 -37
  348. package/dist/ui/v3/tui/startTui.d.ts.map +0 -1
  349. package/dist/ui/v3/tui/startTui.js +0 -61
  350. package/dist/ui/v3/tui/startTui.js.map +0 -1
  351. package/dist/ui/v3/tui/terminalMode.d.ts +0 -31
  352. package/dist/ui/v3/tui/terminalMode.d.ts.map +0 -1
  353. package/dist/ui/v3/tui/terminalMode.js +0 -76
  354. package/dist/ui/v3/tui/terminalMode.js.map +0 -1
  355. package/dist/ui/v3/ui/debugUtils.d.ts +0 -67
  356. package/dist/ui/v3/ui/debugUtils.d.ts.map +0 -1
  357. package/dist/ui/v3/ui/debugUtils.js +0 -238
  358. package/dist/ui/v3/ui/debugUtils.js.map +0 -1
  359. package/dist/ui/v3/ui/focus.d.ts +0 -28
  360. package/dist/ui/v3/ui/focus.d.ts.map +0 -1
  361. package/dist/ui/v3/ui/focus.js +0 -38
  362. package/dist/ui/v3/ui/focus.js.map +0 -1
  363. package/dist/ui/v3/ui/layout/hubLayout.d.ts +0 -43
  364. package/dist/ui/v3/ui/layout/hubLayout.d.ts.map +0 -1
  365. package/dist/ui/v3/ui/layout/hubLayout.js +0 -170
  366. package/dist/ui/v3/ui/layout/hubLayout.js.map +0 -1
  367. package/dist/ui/v3/ui/layout/phase1Layout.d.ts +0 -63
  368. package/dist/ui/v3/ui/layout/phase1Layout.d.ts.map +0 -1
  369. package/dist/ui/v3/ui/layout/phase1Layout.js +0 -274
  370. package/dist/ui/v3/ui/layout/phase1Layout.js.map +0 -1
  371. package/dist/ui/v3/ui/layout/phase1Layout.test.d.ts +0 -5
  372. package/dist/ui/v3/ui/layout/phase1Layout.test.d.ts.map +0 -1
  373. package/dist/ui/v3/ui/layout/phase1Layout.test.js +0 -120
  374. package/dist/ui/v3/ui/layout/phase1Layout.test.js.map +0 -1
  375. package/dist/ui/v3/ui/minimalRuntime.d.ts +0 -14
  376. package/dist/ui/v3/ui/minimalRuntime.d.ts.map +0 -1
  377. package/dist/ui/v3/ui/minimalRuntime.js +0 -111
  378. package/dist/ui/v3/ui/minimalRuntime.js.map +0 -1
  379. package/dist/ui/v3/ui/panels/AssetsPanel.d.ts +0 -17
  380. package/dist/ui/v3/ui/panels/AssetsPanel.d.ts.map +0 -1
  381. package/dist/ui/v3/ui/panels/AssetsPanel.js +0 -53
  382. package/dist/ui/v3/ui/panels/AssetsPanel.js.map +0 -1
  383. package/dist/ui/v3/ui/panels/CapabilitiesPanel.d.ts +0 -20
  384. package/dist/ui/v3/ui/panels/CapabilitiesPanel.d.ts.map +0 -1
  385. package/dist/ui/v3/ui/panels/CapabilitiesPanel.js +0 -67
  386. package/dist/ui/v3/ui/panels/CapabilitiesPanel.js.map +0 -1
  387. package/dist/ui/v3/ui/panels/NetworkPanel.d.ts +0 -31
  388. package/dist/ui/v3/ui/panels/NetworkPanel.d.ts.map +0 -1
  389. package/dist/ui/v3/ui/panels/NetworkPanel.js +0 -153
  390. package/dist/ui/v3/ui/panels/NetworkPanel.js.map +0 -1
  391. package/dist/ui/v3/ui/panels/PosturePanel.d.ts +0 -16
  392. package/dist/ui/v3/ui/panels/PosturePanel.d.ts.map +0 -1
  393. package/dist/ui/v3/ui/panels/PosturePanel.js +0 -60
  394. package/dist/ui/v3/ui/panels/PosturePanel.js.map +0 -1
  395. package/dist/ui/v3/ui/panels/ResourcesPanel.d.ts +0 -20
  396. package/dist/ui/v3/ui/panels/ResourcesPanel.d.ts.map +0 -1
  397. package/dist/ui/v3/ui/panels/ResourcesPanel.js +0 -66
  398. package/dist/ui/v3/ui/panels/ResourcesPanel.js.map +0 -1
  399. package/dist/ui/v3/ui/phase1Runtime.d.ts +0 -29
  400. package/dist/ui/v3/ui/phase1Runtime.d.ts.map +0 -1
  401. package/dist/ui/v3/ui/phase1Runtime.js +0 -1648
  402. package/dist/ui/v3/ui/phase1Runtime.js.map +0 -1
  403. package/dist/ui/v3/ui/phase1RuntimeClean.d.ts +0 -34
  404. package/dist/ui/v3/ui/phase1RuntimeClean.d.ts.map +0 -1
  405. package/dist/ui/v3/ui/phase1RuntimeClean.js +0 -1841
  406. package/dist/ui/v3/ui/phase1RuntimeClean.js.map +0 -1
  407. package/dist/ui/v3/ui/primitives/Panel.d.ts +0 -39
  408. package/dist/ui/v3/ui/primitives/Panel.d.ts.map +0 -1
  409. package/dist/ui/v3/ui/primitives/Panel.js +0 -105
  410. package/dist/ui/v3/ui/primitives/Panel.js.map +0 -1
  411. package/dist/ui/v3/ui/theme.d.ts +0 -37
  412. package/dist/ui/v3/ui/theme.d.ts.map +0 -1
  413. package/dist/ui/v3/ui/theme.js +0 -40
  414. package/dist/ui/v3/ui/theme.js.map +0 -1
  415. package/dist/ui/v3/ui/uiRuntime.d.ts +0 -40
  416. package/dist/ui/v3/ui/uiRuntime.d.ts.map +0 -1
  417. package/dist/ui/v3/ui/uiRuntime.js +0 -60
  418. package/dist/ui/v3/ui/uiRuntime.js.map +0 -1
  419. package/dist/ui/v3/ui/widgets/CommandLine.d.ts +0 -26
  420. package/dist/ui/v3/ui/widgets/CommandLine.d.ts.map +0 -1
  421. package/dist/ui/v3/ui/widgets/CommandLine.js +0 -67
  422. package/dist/ui/v3/ui/widgets/CommandLine.js.map +0 -1
  423. package/dist/ui/v3/v1Adapters/agents.d.ts +0 -72
  424. package/dist/ui/v3/v1Adapters/agents.d.ts.map +0 -1
  425. package/dist/ui/v3/v1Adapters/agents.js +0 -182
  426. package/dist/ui/v3/v1Adapters/agents.js.map +0 -1
  427. package/dist/ui/v3/v1Adapters/config.d.ts +0 -67
  428. package/dist/ui/v3/v1Adapters/config.d.ts.map +0 -1
  429. package/dist/ui/v3/v1Adapters/config.js +0 -78
  430. package/dist/ui/v3/v1Adapters/config.js.map +0 -1
  431. package/dist/ui/v3/v1Adapters/connect.d.ts +0 -77
  432. package/dist/ui/v3/v1Adapters/connect.d.ts.map +0 -1
  433. package/dist/ui/v3/v1Adapters/connect.js +0 -576
  434. package/dist/ui/v3/v1Adapters/connect.js.map +0 -1
  435. package/dist/ui/v3/v1Adapters/httpDebug.d.ts +0 -19
  436. package/dist/ui/v3/v1Adapters/httpDebug.d.ts.map +0 -1
  437. package/dist/ui/v3/v1Adapters/httpDebug.js +0 -60
  438. package/dist/ui/v3/v1Adapters/httpDebug.js.map +0 -1
  439. package/dist/ui/v3/v1Adapters/runs.d.ts +0 -77
  440. package/dist/ui/v3/v1Adapters/runs.d.ts.map +0 -1
  441. package/dist/ui/v3/v1Adapters/runs.js +0 -339
  442. package/dist/ui/v3/v1Adapters/runs.js.map +0 -1
  443. package/dist/ui/v4/engine/renderFrame.d.ts +0 -47
  444. package/dist/ui/v4/engine/renderFrame.d.ts.map +0 -1
  445. package/dist/ui/v4/engine/renderFrame.js +0 -653
  446. package/dist/ui/v4/engine/renderFrame.js.map +0 -1
  447. package/dist/ui/v4/engine/resizeController.d.ts +0 -48
  448. package/dist/ui/v4/engine/resizeController.d.ts.map +0 -1
  449. package/dist/ui/v4/engine/resizeController.js +0 -285
  450. package/dist/ui/v4/engine/resizeController.js.map +0 -1
  451. package/dist/ui/v4/engine/safeViewport.d.ts +0 -47
  452. package/dist/ui/v4/engine/safeViewport.d.ts.map +0 -1
  453. package/dist/ui/v4/engine/safeViewport.js +0 -123
  454. package/dist/ui/v4/engine/safeViewport.js.map +0 -1
  455. package/dist/ui/v4/engine/terminalProfile.d.ts +0 -56
  456. package/dist/ui/v4/engine/terminalProfile.d.ts.map +0 -1
  457. package/dist/ui/v4/engine/terminalProfile.js +0 -115
  458. package/dist/ui/v4/engine/terminalProfile.js.map +0 -1
  459. package/dist/ui/v4/index.d.ts +0 -28
  460. package/dist/ui/v4/index.d.ts.map +0 -1
  461. package/dist/ui/v4/index.js +0 -993
  462. package/dist/ui/v4/index.js.map +0 -1
  463. package/dist/ui/v4/layout/layoutEngine.d.ts +0 -62
  464. package/dist/ui/v4/layout/layoutEngine.d.ts.map +0 -1
  465. package/dist/ui/v4/layout/layoutEngine.js +0 -294
  466. package/dist/ui/v4/layout/layoutEngine.js.map +0 -1
  467. package/dist/ui/v4/runtime/keepAlive.d.ts +0 -21
  468. package/dist/ui/v4/runtime/keepAlive.d.ts.map +0 -1
  469. package/dist/ui/v4/runtime/keepAlive.js +0 -149
  470. package/dist/ui/v4/runtime/keepAlive.js.map +0 -1
  471. package/dist/ui/v4/runtime/logger.d.ts +0 -35
  472. package/dist/ui/v4/runtime/logger.d.ts.map +0 -1
  473. package/dist/ui/v4/runtime/logger.js +0 -109
  474. package/dist/ui/v4/runtime/logger.js.map +0 -1
  475. package/dist/ui/v5/debug/assertNoOverflow.d.ts +0 -28
  476. package/dist/ui/v5/debug/assertNoOverflow.d.ts.map +0 -1
  477. package/dist/ui/v5/debug/assertNoOverflow.js +0 -63
  478. package/dist/ui/v5/debug/assertNoOverflow.js.map +0 -1
  479. package/dist/ui/v5/debug/debugCommands.d.ts +0 -20
  480. package/dist/ui/v5/debug/debugCommands.d.ts.map +0 -1
  481. package/dist/ui/v5/debug/debugCommands.js +0 -461
  482. package/dist/ui/v5/debug/debugCommands.js.map +0 -1
  483. package/dist/ui/v5/debugCommands.d.ts +0 -20
  484. package/dist/ui/v5/debugCommands.d.ts.map +0 -1
  485. package/dist/ui/v5/debugCommands.js +0 -81
  486. package/dist/ui/v5/debugCommands.js.map +0 -1
  487. package/dist/ui/v5/guardrails/stdoutGuard.d.ts +0 -23
  488. package/dist/ui/v5/guardrails/stdoutGuard.d.ts.map +0 -1
  489. package/dist/ui/v5/guardrails/stdoutGuard.js +0 -94
  490. package/dist/ui/v5/guardrails/stdoutGuard.js.map +0 -1
  491. package/dist/ui/v5/guardrails/terminalRestore.d.ts +0 -17
  492. package/dist/ui/v5/guardrails/terminalRestore.d.ts.map +0 -1
  493. package/dist/ui/v5/guardrails/terminalRestore.js +0 -47
  494. package/dist/ui/v5/guardrails/terminalRestore.js.map +0 -1
  495. package/dist/ui/v5/index.d.ts +0 -30
  496. package/dist/ui/v5/index.d.ts.map +0 -1
  497. package/dist/ui/v5/index.js +0 -243
  498. package/dist/ui/v5/index.js.map +0 -1
  499. package/dist/ui/v5/kernel/kernel.d.ts +0 -81
  500. package/dist/ui/v5/kernel/kernel.d.ts.map +0 -1
  501. package/dist/ui/v5/kernel/kernel.js +0 -339
  502. package/dist/ui/v5/kernel/kernel.js.map +0 -1
  503. package/dist/ui/v5/kernel.d.ts +0 -75
  504. package/dist/ui/v5/kernel.d.ts.map +0 -1
  505. package/dist/ui/v5/kernel.js +0 -289
  506. package/dist/ui/v5/kernel.js.map +0 -1
  507. package/dist/ui/v5/layout/clampRect.d.ts +0 -28
  508. package/dist/ui/v5/layout/clampRect.d.ts.map +0 -1
  509. package/dist/ui/v5/layout/clampRect.js +0 -45
  510. package/dist/ui/v5/layout/clampRect.js.map +0 -1
  511. package/dist/ui/v5/layout/layoutEngine.d.ts +0 -16
  512. package/dist/ui/v5/layout/layoutEngine.d.ts.map +0 -1
  513. package/dist/ui/v5/layout/layoutEngine.js +0 -99
  514. package/dist/ui/v5/layout/layoutEngine.js.map +0 -1
  515. package/dist/ui/v5/renderGate.d.ts +0 -19
  516. package/dist/ui/v5/renderGate.d.ts.map +0 -1
  517. package/dist/ui/v5/renderGate.js +0 -36
  518. package/dist/ui/v5/renderGate.js.map +0 -1
  519. package/dist/ui/v5/resize/resizeController.d.ts +0 -62
  520. package/dist/ui/v5/resize/resizeController.d.ts.map +0 -1
  521. package/dist/ui/v5/resize/resizeController.js +0 -141
  522. package/dist/ui/v5/resize/resizeController.js.map +0 -1
  523. package/dist/ui/v5/resizeController.d.ts +0 -55
  524. package/dist/ui/v5/resizeController.d.ts.map +0 -1
  525. package/dist/ui/v5/resizeController.js +0 -124
  526. package/dist/ui/v5/resizeController.js.map +0 -1
  527. package/dist/ui/v5/runtime/keepAlive.d.ts +0 -37
  528. package/dist/ui/v5/runtime/keepAlive.d.ts.map +0 -1
  529. package/dist/ui/v5/runtime/keepAlive.js +0 -122
  530. package/dist/ui/v5/runtime/keepAlive.js.map +0 -1
  531. package/dist/ui/v5/runtime/restoreTerminal.d.ts +0 -34
  532. package/dist/ui/v5/runtime/restoreTerminal.d.ts.map +0 -1
  533. package/dist/ui/v5/runtime/restoreTerminal.js +0 -100
  534. package/dist/ui/v5/runtime/restoreTerminal.js.map +0 -1
  535. package/dist/ui/v5/runtime/stdoutGuard.d.ts +0 -42
  536. package/dist/ui/v5/runtime/stdoutGuard.d.ts.map +0 -1
  537. package/dist/ui/v5/runtime/stdoutGuard.js +0 -156
  538. package/dist/ui/v5/runtime/stdoutGuard.js.map +0 -1
  539. package/dist/ui/v5/viewport/getViewport.d.ts +0 -23
  540. package/dist/ui/v5/viewport/getViewport.d.ts.map +0 -1
  541. package/dist/ui/v5/viewport/getViewport.js +0 -117
  542. package/dist/ui/v5/viewport/getViewport.js.map +0 -1
  543. package/dist/ui/v5/viewport.d.ts +0 -41
  544. package/dist/ui/v5/viewport.d.ts.map +0 -1
  545. package/dist/ui/v5/viewport.js +0 -90
  546. package/dist/ui/v5/viewport.js.map +0 -1
  547. package/dist/ui/widgets/flagRow.d.ts +0 -25
  548. package/dist/ui/widgets/flagRow.d.ts.map +0 -1
  549. package/dist/ui/widgets/flagRow.js +0 -57
  550. package/dist/ui/widgets/flagRow.js.map +0 -1
  551. package/dist/ui/widgets/index.d.ts +0 -9
  552. package/dist/ui/widgets/index.d.ts.map +0 -1
  553. package/dist/ui/widgets/index.js +0 -9
  554. package/dist/ui/widgets/index.js.map +0 -1
  555. package/dist/ui/widgets/meter.d.ts +0 -18
  556. package/dist/ui/widgets/meter.d.ts.map +0 -1
  557. package/dist/ui/widgets/meter.js +0 -38
  558. package/dist/ui/widgets/meter.js.map +0 -1
  559. package/dist/ui/widgets/miniMap.d.ts +0 -26
  560. package/dist/ui/widgets/miniMap.d.ts.map +0 -1
  561. package/dist/ui/widgets/miniMap.js +0 -94
  562. package/dist/ui/widgets/miniMap.js.map +0 -1
  563. package/dist/ui/widgets/sparkline.d.ts +0 -17
  564. package/dist/ui/widgets/sparkline.d.ts.map +0 -1
  565. package/dist/ui/widgets/sparkline.js +0 -65
  566. package/dist/ui/widgets/sparkline.js.map +0 -1
@@ -1,1841 +0,0 @@
1
- /**
2
- * Phase 1 Runtime - CLEAN IMPLEMENTATION
3
- *
4
- * Following strict lifecycle rules:
5
- * - Single instance guard
6
- * - Resize updates layout only (no recreate)
7
- * - Zero stdout writes
8
- * - Clean unbinding
9
- */
10
- import blessed from 'neo-blessed';
11
- import { computePhase1Layout, MIN_WIDTH, MIN_HEIGHT } from './layout/phase1Layout.js';
12
- import { isUIMounted, setUIRuntime, getUIRuntime, destroyUIRuntime } from './uiRuntime.js';
13
- import { feedStore } from '../core/feedStore.js';
14
- import { eventBus } from '../core/eventBus.js';
15
- import { enableTuiMode, disableBootPhase } from '../core/logger.js';
16
- import { buildUiState } from '../state/uiStateBuilder.js';
17
- import { defaultUiState } from '../state/defaultState.js';
18
- import { initializePostureState } from '../state/initializePostureState.js';
19
- import { renderResourcesPanel } from './panels/ResourcesPanel.js';
20
- import { renderPosturePanel } from './panels/PosturePanel.js';
21
- import { renderAssetsPanel } from './panels/AssetsPanel.js';
22
- import { renderNetworkPanel } from './panels/NetworkPanel.js';
23
- import { renderCapabilitiesPanel } from './panels/CapabilitiesPanel.js';
24
- import { isAvailable } from '../state/value.js';
25
- import { parse, execute, UiAction } from '../commands/commandEngine.js';
26
- import { BOOT_ID, isDebugMode, trackWidget, trackListenerBinding, installStdoutMonkeypatch, updateResizeStats, getResizeStats } from './debugUtils.js';
27
- import { getTerminalGeometry, getTerminalSize, isGeometryStable, formatGeometry } from '../tui/geometry.js';
28
- import { detectTerminalMode, getDetectionLog } from '../tui/terminalMode.js';
29
- const blessedLib = blessed;
30
- const PROMPT = '4runr> ';
31
- // Phase B1: Global mount guard - ONE SCREEN. ONE MOUNT. FOREVER.
32
- if (globalThis.__TUI_MOUNTED__) {
33
- throw new Error('TUI already mounted globally - cannot mount twice');
34
- }
35
- // Step 1: Mount guard - ensures UI mounted exactly once
36
- let mounted = false;
37
- // Step 2: Single widget registry (source of truth)
38
- let widgets = null;
39
- // Current UI state
40
- let currentUiState = {
41
- ...defaultUiState,
42
- posture: initializePostureState()
43
- };
44
- // Command history
45
- const commandHistory = [];
46
- let historyIndex = -1;
47
- // Input state
48
- let commandInputValue = PROMPT;
49
- let cursorPosition = PROMPT.length;
50
- let scrollOffset = 0;
51
- // Phase C1: Resize debounce (prevent resize storm)
52
- let resizeDebounceTimer = null;
53
- let lastAppliedSize = null;
54
- const RESIZE_DEBOUNCE_MS = 100; // 100ms debounce for smooth resize
55
- let resizeEventCount = 0;
56
- let resizeApplyCount = 0;
57
- let layoutUIInProgress = false; // Guard against re-entrant layoutUI calls
58
- let isResizing = false; // Freeze flag to prevent state updates during resize
59
- let resizeLock = false; // Atomic resize lock - prevents concurrent resize operations
60
- let resizeTraceEnabled = false; // Enable resize trace logging
61
- let layoutOverlayEnabled = false; // Enable live layout overlay in Operations panel
62
- let firstResizeSinceMount = true; // Track first resize for immediate render
63
- // Resize Truth Panel: Track dimension sources and timing (for debug commands only)
64
- let lastResizeEventAt = null;
65
- let layoutAppliedAt = null;
66
- let lastLayoutCols = null;
67
- let lastLayoutRows = null;
68
- let lastComputedLayout = null; // Track last computed layout for overlay
69
- let resizePollingTimer = null;
70
- let lastPolledSize = null;
71
- /**
72
- * Toggle resize trace mode (exported for debug commands)
73
- */
74
- export function setResizeTraceEnabled(enabled) {
75
- resizeTraceEnabled = enabled;
76
- }
77
- /**
78
- * Get resize trace status (exported for debug commands)
79
- */
80
- export function getResizeTraceEnabled() {
81
- return resizeTraceEnabled;
82
- }
83
- /**
84
- * Toggle layout overlay mode (exported for debug commands)
85
- */
86
- export function setLayoutOverlayEnabled(enabled) {
87
- layoutOverlayEnabled = enabled;
88
- }
89
- /**
90
- * Get layout overlay status (exported for debug commands)
91
- */
92
- export function getLayoutOverlayEnabled() {
93
- return layoutOverlayEnabled;
94
- }
95
- // Resize handler guard (Step 4A: bind once)
96
- let resizeHandlerBound = false;
97
- /**
98
- * Issue 2 - Step 1: Single logger that routes to eventBus
99
- * NEVER writes to stdout
100
- */
101
- function log(tag, msg, level = 'INFO') {
102
- eventBus.emit({
103
- tag,
104
- msg: isDebugMode() ? `[boot=${BOOT_ID}] ${msg}` : msg,
105
- level
106
- });
107
- }
108
- /**
109
- * Step 3: Safe append helper - prevents duplicate appends
110
- */
111
- function safeAppend(screen, widget) {
112
- // If widget already has a parent, do not append again
113
- if (widget.parent) {
114
- log('SYS', 'WARN: Widget already has parent, skipping append', 'WARN');
115
- return;
116
- }
117
- // Check if widget already in screen.children
118
- const children = screen.children || [];
119
- if (children.includes(widget)) {
120
- log('SYS', 'WARN: Widget already in screen.children, skipping append', 'WARN');
121
- return;
122
- }
123
- // Safe to append
124
- screen.append(widget);
125
- }
126
- /**
127
- * A) mountUI() - Create widgets ONCE
128
- * Issue 1 - Step 1: Hard mount guard
129
- */
130
- function mountUI(screen, layout) {
131
- // Issue 1 - Step 1: Hard guard - mount exactly once
132
- if (mounted) {
133
- // Track UI rebuild attempts (should never happen)
134
- updateResizeStats({ uiRebuildCount: (getResizeStats().uiRebuildCount || 0) + 1 });
135
- throw new Error('UI already mounted - cannot mount twice');
136
- }
137
- mounted = true;
138
- // Issue 1 - Step 2: Create all widgets as singletons
139
- const createdWidgets = {
140
- posture: createPanel(screen, layout.posture, 'POSTURE'),
141
- resources: createPanel(screen, layout.resources, 'RESOURCES'),
142
- assets: createPanel(screen, layout.assets, 'ASSETS'),
143
- operations: createPanel(screen, layout.operations, 'OPERATIONS'),
144
- network: createPanel(screen, layout.network, 'NETWORK'),
145
- capabilities: createPanel(screen, layout.capabilities, 'CAPABILITIES'),
146
- statusStrip: createStatusStrip(screen, layout),
147
- commandLine: createCommandLine(screen, layout),
148
- };
149
- // Step 3: Append all widgets ONCE using safeAppend
150
- safeAppend(screen, createdWidgets.posture);
151
- safeAppend(screen, createdWidgets.resources);
152
- safeAppend(screen, createdWidgets.assets);
153
- safeAppend(screen, createdWidgets.operations);
154
- safeAppend(screen, createdWidgets.network);
155
- safeAppend(screen, createdWidgets.capabilities);
156
- safeAppend(screen, createdWidgets.statusStrip);
157
- safeAppend(screen, createdWidgets.commandLine);
158
- // Step 6: Debug - log initial widget count
159
- const initialCount = screen.children?.length || 0;
160
- log('SYS', `UI mounted: ${initialCount} widgets`);
161
- return createdWidgets;
162
- }
163
- /**
164
- * B) layoutUI() - Update positions ONLY
165
- * Issue 1 - Step 3: NEVER creates or appends widgets
166
- *
167
- * @param screen - The blessed screen object
168
- * @param widgets - The UI widgets to update
169
- * @param explicitCols - Optional explicit columns (from resize handler, more accurate than screen)
170
- * @param explicitRows - Optional explicit rows (from resize handler, more accurate than screen)
171
- */
172
- function layoutUI(screen, widgets, explicitCols, explicitRows) {
173
- // HARD GUARD: Prevent re-entrant calls (critical for preventing duplicate widgets)
174
- if (layoutUIInProgress) {
175
- if (isDebugMode()) {
176
- log('DBG', 'layoutUI already in progress, skipping duplicate call');
177
- }
178
- return;
179
- }
180
- layoutUIInProgress = true;
181
- try {
182
- // Step 2 & 5: Get geometry from authoritative source
183
- // If explicit dimensions provided (from resize handler), use those (most accurate during resize)
184
- // Otherwise, read from screen using getTerminalGeometry (which prioritizes stdout for native terminals)
185
- let cols;
186
- let rows;
187
- if (explicitCols !== undefined && explicitRows !== undefined) {
188
- // Use explicit dimensions from resize handler (most accurate during resize on Windows Terminal)
189
- cols = explicitCols;
190
- rows = explicitRows;
191
- }
192
- else {
193
- // Fallback: Read from screen (for initial mount or when explicit dimensions not provided)
194
- const geo = getTerminalGeometry(screen);
195
- cols = geo.cols;
196
- rows = geo.rows;
197
- }
198
- // Step 4 & 5: Compute layout using dimensions
199
- const layoutResult = computePhase1Layout(cols, rows, screen);
200
- if (!layoutResult.ok || !layoutResult.layout) {
201
- log('SYS', `Terminal too small: ${cols}x${rows} (need ${MIN_WIDTH}x${MIN_HEIGHT})`, 'WARN');
202
- return;
203
- }
204
- const layout = layoutResult.layout;
205
- // Store layout for overlay
206
- lastComputedLayout = layout;
207
- // Update widget positions/sizes ONLY (no recreate, no re-append)
208
- updateWidgetLayout(widgets.posture, layout.posture);
209
- updateWidgetLayout(widgets.resources, layout.resources);
210
- updateWidgetLayout(widgets.assets, layout.assets);
211
- updateWidgetLayout(widgets.operations, layout.operations);
212
- updateWidgetLayout(widgets.network, layout.network);
213
- updateWidgetLayout(widgets.capabilities, layout.capabilities);
214
- // Update status strip and command line (deterministic calculation)
215
- const statusWidth = Math.floor(layout.commandLine.width * 0.2);
216
- const commandWidth = layout.commandLine.width - statusWidth - 1;
217
- updateWidgetLayout(widgets.statusStrip, {
218
- top: layout.commandLine.top,
219
- left: layout.commandLine.left,
220
- width: statusWidth,
221
- height: layout.commandLine.height,
222
- });
223
- // CRITICAL: Hard clamp command line dimensions (prevents collapse)
224
- // Minimum: 60 width, 3 height (if drawing border)
225
- const MIN_COMMAND_LINE_WIDTH = 60;
226
- const MIN_COMMAND_LINE_HEIGHT = 3;
227
- const clampedCommandWidth = Math.max(MIN_COMMAND_LINE_WIDTH, commandWidth);
228
- const clampedCommandHeight = Math.max(MIN_COMMAND_LINE_HEIGHT, layout.commandLine.height);
229
- // Ensure command line doesn't overflow terminal bounds
230
- const geo = getTerminalGeometry(screen);
231
- const cmdLeft = layout.commandLine.left + statusWidth + 1;
232
- const maxCommandWidth = geo.safeCols - cmdLeft - 1; // Leave 1 char margin
233
- const finalCommandWidth = Math.min(clampedCommandWidth, maxCommandWidth);
234
- // Final validation: ensure command line fits within terminal
235
- const cmdRight = cmdLeft + finalCommandWidth;
236
- const cmdBottom = layout.commandLine.top + clampedCommandHeight;
237
- // Final validation before applying layout
238
- const finalCmdLeft = layout.commandLine.left + statusWidth + 1;
239
- let finalCmdWidth = finalCommandWidth;
240
- let finalCmdHeight = clampedCommandHeight;
241
- if (cmdRight > geo.safeCols || cmdBottom > geo.safeRows) {
242
- if (isDebugMode()) {
243
- log('ERR', `Command line overflow: right=${cmdRight} (max=${geo.safeCols}), bottom=${cmdBottom} (max=${geo.safeRows}) - clamping`, 'WARN');
244
- }
245
- // Fallback to safe minimal layout
246
- finalCmdWidth = Math.max(MIN_COMMAND_LINE_WIDTH, Math.min(finalCommandWidth, geo.safeCols - cmdLeft - 1));
247
- finalCmdHeight = Math.max(MIN_COMMAND_LINE_HEIGHT, Math.min(clampedCommandHeight, geo.safeRows - layout.commandLine.top - 1));
248
- }
249
- // Assert: cmdLeft + cmdWidth <= cols, cmdTop + cmdHeight <= rows
250
- const finalCmdRight = finalCmdLeft + finalCmdWidth;
251
- const finalCmdBottom = layout.commandLine.top + finalCmdHeight;
252
- if (finalCmdRight > geo.safeCols) {
253
- if (isDebugMode()) {
254
- log('ERR', `Command line right edge ${finalCmdRight} > terminal cols ${geo.safeCols} - clamping`, 'WARN');
255
- }
256
- finalCmdWidth = Math.max(MIN_COMMAND_LINE_WIDTH, geo.safeCols - finalCmdLeft - 1);
257
- }
258
- if (finalCmdBottom > geo.safeRows) {
259
- if (isDebugMode()) {
260
- log('ERR', `Command line bottom edge ${finalCmdBottom} > terminal rows ${geo.safeRows} - clamping`, 'WARN');
261
- }
262
- finalCmdHeight = Math.max(MIN_COMMAND_LINE_HEIGHT, geo.safeRows - layout.commandLine.top - 1);
263
- }
264
- updateWidgetLayout(widgets.commandLine, {
265
- top: layout.commandLine.top,
266
- left: finalCmdLeft,
267
- width: finalCmdWidth,
268
- height: finalCmdHeight,
269
- });
270
- // Update all panel content (may need to reflow for new width)
271
- // IMPORTANT: Do NOT clear command line content during relayout - preserve user input
272
- updateAllPanels(widgets);
273
- // CRITICAL: Restore command line display after relayout (preserves cursor position and input)
274
- // Re-display the current input value with cursor to ensure it's visible after resize
275
- try {
276
- const availableWidth = Math.max(1, widgets.commandLine.width - 4);
277
- const fullText = commandInputValue;
278
- const cursorIndex = cursorPosition;
279
- // Update scroll offset if needed for new width
280
- if (cursorIndex - scrollOffset >= availableWidth - 1) {
281
- scrollOffset = cursorIndex - availableWidth + 2;
282
- }
283
- if (cursorIndex < scrollOffset) {
284
- scrollOffset = Math.max(0, cursorIndex - 5);
285
- }
286
- const visibleStart = scrollOffset;
287
- const visibleEnd = Math.min(fullText.length, visibleStart + availableWidth - 1);
288
- let visibleText = fullText.substring(visibleStart, visibleEnd);
289
- const cursorPosInVisible = cursorIndex - visibleStart;
290
- const beforeCursor = visibleText.substring(0, cursorPosInVisible);
291
- const afterCursor = visibleText.substring(cursorPosInVisible);
292
- const displayValue = beforeCursor + '{inverse} {/}' + afterCursor;
293
- // Restore command line display (preserves user's typed input)
294
- widgets.commandLine.setContent(displayValue);
295
- }
296
- catch (e) {
297
- // If update fails, at least preserve the prompt
298
- widgets.commandLine.setContent(PROMPT);
299
- }
300
- // CRITICAL: Restore focus AFTER all updates (prevents "typing into nothing")
301
- // This must be LAST to ensure focus is not stolen by other widgets
302
- // Hide cursor at screen level (we render cursor manually in command line)
303
- if (screen.program && screen.program.hideCursor) {
304
- screen.program.hideCursor();
305
- }
306
- // Ensure stdin is active
307
- if (process.stdin.isTTY) {
308
- process.stdin.setRawMode(true);
309
- process.stdin.setEncoding('utf8');
310
- if (process.stdin.isPaused()) {
311
- process.stdin.resume();
312
- }
313
- }
314
- // Restore focus
315
- screen.focused = widgets.commandLine;
316
- widgets.commandLine.focus();
317
- // Debug logging (only in debug mode to prevent spam)
318
- if (isDebugMode()) {
319
- const cmdLineInfo = {
320
- x: finalCmdLeft,
321
- y: layout.commandLine.top,
322
- w: finalCmdWidth,
323
- h: finalCmdHeight,
324
- focused: screen.focused === widgets.commandLine,
325
- hidden: widgets.commandLine.hidden || false,
326
- detached: widgets.commandLine.detached || false,
327
- screenDims: `${screen.width}x${screen.height}`,
328
- stdoutDims: `${process.stdout.columns ?? 'N/A'}x${process.stdout.rows ?? 'N/A'}`
329
- };
330
- log('DBG', `[RELAYOUT] commandLine: x=${cmdLineInfo.x} y=${cmdLineInfo.y} w=${cmdLineInfo.w} h=${cmdLineInfo.h} focused=${cmdLineInfo.focused} hidden=${cmdLineInfo.hidden}`);
331
- // Warn if command line is too small
332
- if (finalCommandWidth < MIN_COMMAND_LINE_WIDTH) {
333
- log('ERR', `Command line width too small: ${finalCommandWidth} < ${MIN_COMMAND_LINE_WIDTH}`, 'WARN');
334
- }
335
- // Warn if focus lost
336
- if (screen.focused !== widgets.commandLine) {
337
- log('WARN', `Command line focus lost - restored`, 'WARN');
338
- }
339
- }
340
- }
341
- finally {
342
- layoutUIInProgress = false;
343
- }
344
- }
345
- /**
346
- * Step 1: Single instance guard
347
- */
348
- export async function startPhase1Runtime() {
349
- // HARD GUARD: If UI already mounted, return immediately
350
- if (isUIMounted()) {
351
- // Step 7: Use log() function, not eventBus directly (cleaner)
352
- log('ERR', 'UI already mounted - cannot mount twice', 'ERROR');
353
- return;
354
- }
355
- // Log boot ID and terminal size
356
- log('SYS', `TUI starting (bootId=${BOOT_ID})`);
357
- // Detect and log terminal mode (once at boot)
358
- const terminalMode = detectTerminalMode();
359
- const detectionLog = getDetectionLog();
360
- log('SYS', `Terminal mode: ${terminalMode}`);
361
- if (isDebugMode()) {
362
- for (const line of detectionLog) {
363
- log('DBG', line);
364
- }
365
- }
366
- // Hard guard: Check global TUI initialization flag
367
- if (globalThis.__TUI_INITIALIZED__) {
368
- log('ERR', 'TUI already initialized - refusing to re-init', 'ERROR');
369
- throw new Error('TUI already initialized - cannot reinitialize');
370
- }
371
- globalThis.__TUI_INITIALIZED__ = true;
372
- // Issue 2 - Step 2: Patch console in TUI mode (failsafe)
373
- // This prevents any library or accidental console.log from breaking the TUI
374
- const originalLog = console.log;
375
- const originalError = console.error;
376
- const originalWarn = console.warn;
377
- console.log = (...args) => {
378
- log('DBG', args.map(a => String(a)).join(' '));
379
- };
380
- console.error = (...args) => {
381
- log('ERR', args.map(a => String(a)).join(' '), 'ERROR');
382
- };
383
- console.warn = (...args) => {
384
- log('WARN', args.map(a => String(a)).join(' '), 'WARN');
385
- };
386
- // Step 5: Enable TUI mode (routes stdout writes to eventBus)
387
- enableTuiMode();
388
- // Set up raw input
389
- if (process.stdin.isTTY) {
390
- process.stdin.setRawMode(true);
391
- process.stdin.resume();
392
- process.stdin.setEncoding('utf8');
393
- }
394
- // Create screen (ONCE)
395
- const screen = blessedLib.screen({
396
- smartCSR: false,
397
- fastCSR: false,
398
- title: '4Runr OS',
399
- fullUnicode: true,
400
- sendFocus: false,
401
- warnings: false,
402
- autoPadding: false,
403
- dockBorders: false,
404
- });
405
- if (!screen) {
406
- throw new Error('Failed to create screen');
407
- }
408
- // Log actual screen dimensions
409
- log('SYS', `Screen size: ${screen.width}x${screen.height}`);
410
- // Hide cursor
411
- if (screen.program?.hideCursor) {
412
- screen.program.hideCursor();
413
- }
414
- // Step 2: Get geometry from authoritative source (screen values are authoritative)
415
- // Pass screen so geometry can read screen.cols/rows directly
416
- const geo = getTerminalGeometry(screen);
417
- // Initialize last applied size (prevents duplicate processing on first resize)
418
- lastAppliedSize = { cols: geo.cols, rows: geo.rows };
419
- const layoutResult = computePhase1Layout(geo.cols, geo.rows, screen);
420
- if (!layoutResult.ok || !layoutResult.layout) {
421
- const errorMsg = layoutResult.errorMessage || 'Terminal too small';
422
- screen.destroy();
423
- throw new Error(errorMsg);
424
- }
425
- const layout = layoutResult.layout;
426
- // Debug: Log layout dimensions
427
- if (isDebugMode()) {
428
- log('DBG', `Layout computed for ${geo.cols}x${geo.rows} (source: ${geo.source})`);
429
- log('DBG', `Left: ${layout.posture.width}w, Center: ${layout.operations.width}w, Right: ${layout.network.width}w`);
430
- log('DBG', `Right col position: left=${layout.network.left}, width=${layout.network.width}`);
431
- log('DBG', `Right edge: ${layout.network.left + layout.network.width} (screen cols: ${geo.cols})`);
432
- }
433
- // A) mountUI() - Create widgets ONCE
434
- widgets = mountUI(screen, layout);
435
- // Track initial mount (not a rebuild)
436
- updateResizeStats({ uiRebuildCount: 0 });
437
- // Store unbind functions
438
- const unbindFns = [];
439
- // Step 3A: Install stdout monkeypatch (debug mode)
440
- const uninstallStdoutMonkeypatch = installStdoutMonkeypatch();
441
- unbindFns.push(uninstallStdoutMonkeypatch);
442
- // Restore console on cleanup
443
- unbindFns.push(() => {
444
- console.log = originalLog;
445
- console.error = originalError;
446
- console.warn = originalWarn;
447
- });
448
- // Step 6: Error handlers
449
- const uncaughtHandler = (error) => {
450
- log('ERR', `Uncaught: ${error.message}`, 'ERROR');
451
- };
452
- const unhandledHandler = (reason) => {
453
- log('ERR', `Unhandled rejection: ${String(reason)}`, 'ERROR');
454
- };
455
- process.on('uncaughtException', uncaughtHandler);
456
- process.on('unhandledRejection', unhandledHandler);
457
- unbindFns.push(() => {
458
- process.removeListener('uncaughtException', uncaughtHandler);
459
- process.removeListener('unhandledRejection', unhandledHandler);
460
- });
461
- // Setup input handling
462
- setupInputHandling(screen, widgets.commandLine, unbindFns);
463
- // Step 2 & 3: Setup resize handling (layout update only)
464
- setupResizeHandling(screen, widgets, unbindFns);
465
- // Setup signal handlers
466
- const sigintHandler = () => cleanExit(0);
467
- const sigtermHandler = () => cleanExit(0);
468
- process.on('SIGINT', sigintHandler);
469
- process.on('SIGTERM', sigtermHandler);
470
- unbindFns.push(() => {
471
- process.removeListener('SIGINT', sigintHandler);
472
- process.removeListener('SIGTERM', sigtermHandler);
473
- });
474
- // Store runtime
475
- const runtime = {
476
- screen,
477
- widgets,
478
- isMounted: true,
479
- unbindFns,
480
- };
481
- setUIRuntime(runtime);
482
- // Subscribe to eventBus for Operations panel
483
- eventBus.subscribe((event) => {
484
- feedStore.push(event);
485
- if (widgets) {
486
- updateOperationsPanel(widgets.operations);
487
- screen.render();
488
- }
489
- });
490
- // Initial content update
491
- updateAllPanels(widgets);
492
- // Initial render
493
- screen.render();
494
- // Phase B1: Mark as globally mounted
495
- globalThis.__TUI_MOUNTED__ = true;
496
- // Disable boot phase (enable strict stdout blocking)
497
- disableBootPhase();
498
- // Start background state updates
499
- startBackgroundUpdates(widgets, screen);
500
- // Log boot
501
- eventBus.emit({
502
- tag: 'SYS',
503
- msg: 'TUI initialized',
504
- level: 'INFO'
505
- });
506
- }
507
- /**
508
- * Hard clear terminal buffer - removes all artifacts
509
- */
510
- function hardClearTerminal(screen) {
511
- try {
512
- const program = screen.program;
513
- // Method 1: blessed's program clear (most reliable)
514
- if (program && typeof program.clear === 'function') {
515
- program.clear();
516
- if (typeof program.home === 'function') {
517
- program.home();
518
- }
519
- // Also clear scrollback to remove ALL artifacts
520
- if (typeof program.eraseInDisplay === 'function') {
521
- program.eraseInDisplay('all'); // Clear entire display including scrollback
522
- }
523
- return;
524
- }
525
- // Method 2: ANSI escape codes
526
- // \x1b[2J = Clear entire screen
527
- // \x1b[3J = Clear scrollback buffer (prevents ghosting)
528
- // \x1b[H = Move cursor to home position (0,0)
529
- if (process.stdout.isTTY) {
530
- process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
531
- }
532
- }
533
- catch (e) {
534
- // If clear fails, try ANSI escape as last resort
535
- if (process.stdout.isTTY) {
536
- process.stdout.write('\x1b[2J\x1b[3J\x1b[H');
537
- }
538
- }
539
- }
540
- /**
541
- * Nuclear teardown - completely destroy all widgets
542
- * This is the ONLY way to guarantee no ghosting on Windows Terminal
543
- */
544
- function nuclearTeardownWidgets(screen, widgets) {
545
- try {
546
- // Step 1: Detach all widgets from screen
547
- const widgetList = [
548
- widgets.posture,
549
- widgets.resources,
550
- widgets.assets,
551
- widgets.operations,
552
- widgets.network,
553
- widgets.capabilities,
554
- widgets.statusStrip,
555
- widgets.commandLine,
556
- ];
557
- for (const widget of widgetList) {
558
- if (widget && typeof widget.detach === 'function') {
559
- widget.detach();
560
- }
561
- }
562
- // Step 2: Remove from screen.children manually (blessed sometimes doesn't clean up)
563
- const screenChildren = screen.children || [];
564
- for (let i = screenChildren.length - 1; i >= 0; i--) {
565
- const child = screenChildren[i];
566
- if (widgetList.includes(child)) {
567
- screenChildren.splice(i, 1);
568
- }
569
- }
570
- // Step 3: Destroy all widgets
571
- for (const widget of widgetList) {
572
- if (widget && typeof widget.destroy === 'function') {
573
- widget.destroy();
574
- }
575
- }
576
- // Step 4: Clear any remaining children (safety)
577
- while (screenChildren.length > 0) {
578
- const child = screenChildren.pop();
579
- if (child && typeof child.destroy === 'function') {
580
- child.destroy();
581
- }
582
- }
583
- }
584
- catch (e) {
585
- // Log but don't throw - we're in cleanup mode
586
- if (isDebugMode()) {
587
- log('DBG', `Nuclear teardown error: ${e}`, 'WARN');
588
- }
589
- }
590
- }
591
- /**
592
- * Nuclear rebuild - completely destroy and recreate all widgets
593
- * This is the most reliable way to prevent ghosting on Windows Terminal
594
- */
595
- function nuclearRebuildUI(cols, rows, screen, oldWidgets) {
596
- // Step 1: Hard clear terminal buffer FIRST
597
- hardClearTerminal(screen);
598
- // Step 2: Nuclear teardown of old widgets
599
- nuclearTeardownWidgets(screen, oldWidgets);
600
- // Step 3: Recompute layout with new dimensions
601
- const geo = getTerminalGeometry(screen);
602
- const layout = computePhase1Layout(cols, rows, screen);
603
- // Step 4: Reset mount guard (allow remount)
604
- mounted = false;
605
- // Step 5: Recreate all widgets
606
- const newWidgets = mountUI(screen, layout);
607
- // Step 6: Restore input state
608
- const textbox = newWidgets.commandLine.children?.[0];
609
- if (textbox && typeof textbox.setValue === 'function') {
610
- textbox.setValue(commandInputValue);
611
- if (typeof textbox.setCursor === 'function') {
612
- textbox.setCursor(cursorPosition - PROMPT.length);
613
- }
614
- }
615
- // Step 7: Restore focus
616
- screen.focused = newWidgets.commandLine;
617
- newWidgets.commandLine.focus();
618
- // Step 8: Full render
619
- screen.render();
620
- return newWidgets;
621
- }
622
- /**
623
- * Clean up duplicate widgets - prevents progressive degradation
624
- * Called before each resize to ensure widget counts stay stable
625
- */
626
- function cleanupDuplicateWidgets(screen, validWidgets) {
627
- try {
628
- const screenChildren = screen.children || [];
629
- const validWidgetSet = new Set([
630
- validWidgets.posture,
631
- validWidgets.resources,
632
- validWidgets.assets,
633
- validWidgets.operations,
634
- validWidgets.network,
635
- validWidgets.capabilities,
636
- validWidgets.statusStrip,
637
- validWidgets.commandLine,
638
- ]);
639
- // Track counts of each valid widget
640
- const widgetCounts = new Map();
641
- for (const widget of validWidgetSet) {
642
- widgetCounts.set(widget, 0);
643
- }
644
- // Count occurrences and find duplicates
645
- const duplicates = [];
646
- const orphanWidgets = [];
647
- for (let i = screenChildren.length - 1; i >= 0; i--) {
648
- const child = screenChildren[i];
649
- if (validWidgetSet.has(child)) {
650
- const count = widgetCounts.get(child) || 0;
651
- widgetCounts.set(child, count + 1);
652
- // If we've seen this widget before, it's a duplicate (keep first, remove rest)
653
- if (count > 0) {
654
- duplicates.push(child);
655
- }
656
- }
657
- else {
658
- // Widget not in valid set - it's an orphan
659
- orphanWidgets.push(child);
660
- }
661
- }
662
- // Remove duplicates and orphans
663
- const toRemove = [...duplicates, ...orphanWidgets];
664
- if (toRemove.length > 0) {
665
- if (isDebugMode() || resizeTraceEnabled) {
666
- log('DBG', `[RESIZE] Cleaning up ${toRemove.length} duplicate/orphan widgets (${duplicates.length} dup, ${orphanWidgets.length} orphan)`);
667
- }
668
- for (const widget of toRemove) {
669
- try {
670
- // Detach and destroy
671
- if (typeof widget.detach === 'function') {
672
- widget.detach();
673
- }
674
- if (typeof widget.destroy === 'function') {
675
- widget.destroy();
676
- }
677
- // Remove from screen.children array
678
- const index = screenChildren.indexOf(widget);
679
- if (index >= 0) {
680
- screenChildren.splice(index, 1);
681
- }
682
- }
683
- catch (e) {
684
- // Ignore cleanup errors
685
- }
686
- }
687
- }
688
- }
689
- catch (e) {
690
- // Ignore cleanup errors - don't break resize if cleanup fails
691
- if (isDebugMode()) {
692
- log('DBG', `Cleanup error: ${e}`, 'WARN');
693
- }
694
- }
695
- }
696
- /**
697
- * Atomic resize application - SAFE UPDATE STRATEGY
698
- *
699
- * Updates widget positions/sizes without destroying them (prevents blank screen).
700
- * Uses scrollback clear to remove artifacts while keeping widgets alive.
701
- *
702
- * Sequence:
703
- * 1. Freeze rendering (isResizing = true)
704
- * 2. Clean up duplicate widgets (prevents progressive degradation)
705
- * 3. Get fresh dimensions
706
- * 4. Update widget positions/sizes (layoutUI)
707
- * 5. Clear scrollback (prevents ghost artifacts)
708
- * 6. Update all panels (redraw content)
709
- * 7. Restore focus
710
- * 8. Unfreeze rendering
711
- */
712
- function applyResizeAtomic(cols, rows, screen, currentWidgets) {
713
- // ATOMIC LOCK: Prevent concurrent resize operations
714
- if (resizeLock) {
715
- if (resizeTraceEnabled || isDebugMode()) {
716
- log('DBG', `[RESIZE] Resize already in progress, skipping duplicate call`);
717
- }
718
- return;
719
- }
720
- resizeLock = true;
721
- try {
722
- // Step 1: Freeze rendering (ignore state updates during resize)
723
- isResizing = true;
724
- // CRITICAL: Clean up duplicate widgets BEFORE layout (prevents progressive degradation)
725
- // This ensures widget counts stay stable across multiple resize cycles
726
- cleanupDuplicateWidgets(screen, currentWidgets);
727
- // Resize trace logging
728
- if (resizeTraceEnabled || isDebugMode()) {
729
- const childrenCount = (screen.children || []).length;
730
- log('DBG', `[RESIZE TRACE] Apply #${resizeApplyCount}: ${cols}x${rows}, children=${childrenCount}`);
731
- }
732
- // Step 2: Get FRESH dimensions (Windows Terminal fullscreen fix)
733
- // Re-read dimensions using getTerminalSize() to get the latest values
734
- const freshSize = getTerminalSize(screen);
735
- const useCols = freshSize.cols;
736
- const useRows = freshSize.rows;
737
- if (resizeTraceEnabled && (useCols !== cols || useRows !== rows)) {
738
- log('DBG', `[RESIZE] Dimensions updated: ${cols}x${rows} -> ${useCols}x${useRows} (source=${freshSize.source})`);
739
- }
740
- // Step 3: HARD CLEAR terminal buffer BEFORE layout (prevents leftover buffer lines)
741
- // This is critical: old borders/text can remain in rows that no widget redraws over
742
- try {
743
- const program = screen.program;
744
- if (program) {
745
- // Clear entire screen buffer
746
- if (typeof program.clear === 'function') {
747
- program.clear();
748
- }
749
- // Clear scrollback to prevent ghost artifacts
750
- if (typeof program.eraseInDisplay === 'function') {
751
- program.eraseInDisplay('all');
752
- }
753
- // Move cursor home
754
- if (typeof program.home === 'function') {
755
- program.home();
756
- }
757
- // Flush to ensure clear is applied
758
- if (typeof program.flush === 'function') {
759
- program.flush();
760
- }
761
- }
762
- }
763
- catch (e) {
764
- // Ignore clear errors - not critical if it fails
765
- }
766
- // Step 4: SAFE UPDATE - update positions/sizes without destroying widgets
767
- // This prevents blank screen issues while still handling resize correctly
768
- layoutUI(screen, currentWidgets, useCols, useRows);
769
- // Track layout application time and dimensions (for Resize Truth Panel)
770
- layoutAppliedAt = Date.now();
771
- lastLayoutCols = useCols;
772
- lastLayoutRows = useRows;
773
- // Step 5: Update all panels with current state (redraw content)
774
- // Temporarily allow panel updates during resize (we explicitly want this)
775
- // This redraws content with the new layout dimensions
776
- const wasResizing = isResizing;
777
- isResizing = false; // Temporarily disable guard to allow updates
778
- try {
779
- updateAllPanels(currentWidgets);
780
- }
781
- finally {
782
- isResizing = wasResizing; // Restore guard
783
- }
784
- // Step 6: Restore command line focus and input state
785
- // Hide cursor at screen level
786
- if (screen.program && screen.program.hideCursor) {
787
- screen.program.hideCursor();
788
- }
789
- // Ensure stdin is active
790
- if (process.stdin.isTTY) {
791
- process.stdin.setRawMode(true);
792
- process.stdin.setEncoding('utf8');
793
- if (process.stdin.isPaused()) {
794
- process.stdin.resume();
795
- }
796
- }
797
- // Restore focus
798
- screen.focused = currentWidgets.commandLine;
799
- currentWidgets.commandLine.focus();
800
- // Step 7: Final render (screen is already cleared, this repaints everything)
801
- screen.render();
802
- // CRITICAL for Windows Terminal fullscreen: Second-pass resize after render
803
- // Windows Terminal fullscreen can have a race condition where blessed updates
804
- // dimensions AFTER the first render. Do a second pass after 80ms to catch this.
805
- if (process.platform === 'win32') {
806
- setTimeout(() => {
807
- try {
808
- // Re-read dimensions after a brief delay (blessed may have updated)
809
- const delayedSize = getTerminalSize(screen);
810
- if (delayedSize.cols !== useCols || delayedSize.rows !== useRows) {
811
- if (resizeTraceEnabled || isDebugMode()) {
812
- log('DBG', `[RESIZE] Second-pass: ${useCols}x${useRows} -> ${delayedSize.cols}x${delayedSize.rows}`);
813
- }
814
- // Dimensions changed - reapply resize
815
- applyResizeAtomic(delayedSize.cols, delayedSize.rows, screen, currentWidgets);
816
- }
817
- }
818
- catch (e) {
819
- // Ignore errors in second-pass
820
- }
821
- }, 80);
822
- }
823
- // Step 8: Validate no duplication occurred
824
- const screenChildren = screen.children || [];
825
- const widgetCount = screenChildren.length;
826
- const commandLineCount = screenChildren.filter((w) => w === currentWidgets.commandLine).length;
827
- const postureCount = screenChildren.filter((w) => w === currentWidgets.posture).length;
828
- const operationsCount = screenChildren.filter((w) => w === currentWidgets.operations).length;
829
- if (commandLineCount > 1 || postureCount > 1 || operationsCount > 1) {
830
- log('ERR', `DUPLICATION DETECTED: commandLine=${commandLineCount}, posture=${postureCount}, operations=${operationsCount}`, 'ERROR');
831
- if (isDebugMode()) {
832
- log('DBG', `Total screen children: ${widgetCount} (expected ~8 widgets)`);
833
- log('DBG', `Widgets: ${screenChildren.map((w) => w.type || 'unknown').join(', ')}`);
834
- }
835
- }
836
- if (resizeTraceEnabled || (isDebugMode() && resizeApplyCount % 5 === 0)) {
837
- const finalChildrenCount = (screen.children || []).length;
838
- log('DBG', `[RESIZE TRACE] Complete: children=${finalChildrenCount}, size=${useCols}x${useRows}`);
839
- }
840
- }
841
- finally {
842
- // Step 8: Unfreeze rendering and release atomic lock
843
- isResizing = false;
844
- resizeLock = false;
845
- }
846
- }
847
- /**
848
- * Schedule resize application with smooth behavior
849
- *
850
- * Strategy:
851
- * - First resize: render immediately (fast response)
852
- * - Subsequent resizes: debounce 100ms (smooth, no spam)
853
- *
854
- * Ensures:
855
- * - Debounce: Multiple rapid resize events result in single layout update
856
- * - Ignore duplicates: Skip if size hasn't changed from last applied
857
- * - Atomic: Full redraw from scratch, not incremental
858
- */
859
- function scheduleResize(nextCols, nextRows, screen, widgets) {
860
- // Check if this is the same as last applied (ignore duplicates)
861
- if (lastAppliedSize && lastAppliedSize.cols === nextCols && lastAppliedSize.rows === nextRows) {
862
- return; // Same size, no need to recompute
863
- }
864
- // First resize: render immediately for fast response
865
- if (firstResizeSinceMount) {
866
- firstResizeSinceMount = false;
867
- resizeApplyCount++;
868
- lastAppliedSize = { cols: nextCols, rows: nextRows };
869
- updateResizeStats({ eventCount: resizeEventCount, applyCount: resizeApplyCount });
870
- // Immediate atomic resize
871
- applyResizeAtomic(nextCols, nextRows, screen, widgets);
872
- // Schedule a final debounced resize to catch any rapid subsequent changes
873
- if (resizeDebounceTimer) {
874
- clearTimeout(resizeDebounceTimer);
875
- }
876
- resizeDebounceTimer = setTimeout(() => {
877
- resizeDebounceTimer = null;
878
- // Check if size changed during debounce
879
- const currentGeo = getTerminalGeometry(screen);
880
- if (currentGeo.cols !== nextCols || currentGeo.rows !== nextRows) {
881
- resizeApplyCount++;
882
- lastAppliedSize = { cols: currentGeo.cols, rows: currentGeo.rows };
883
- updateResizeStats({ eventCount: resizeEventCount, applyCount: resizeApplyCount });
884
- applyResizeAtomic(currentGeo.cols, currentGeo.rows, screen, widgets);
885
- }
886
- }, RESIZE_DEBOUNCE_MS);
887
- return;
888
- }
889
- // Subsequent resizes: cancel pending and schedule new debounced resize
890
- if (resizeDebounceTimer) {
891
- clearTimeout(resizeDebounceTimer);
892
- }
893
- resizeDebounceTimer = setTimeout(() => {
894
- resizeDebounceTimer = null;
895
- resizeApplyCount++;
896
- // Update last applied size BEFORE processing (prevents duplicate processing)
897
- lastAppliedSize = { cols: nextCols, rows: nextRows };
898
- updateResizeStats({ eventCount: resizeEventCount, applyCount: resizeApplyCount });
899
- // Get current geometry (may have changed during debounce)
900
- const geo = getTerminalGeometry(screen);
901
- // Use current geometry if it changed during debounce
902
- const finalCols = geo.cols !== nextCols ? geo.cols : nextCols;
903
- const finalRows = geo.rows !== nextRows ? geo.rows : nextRows;
904
- if (geo.cols !== nextCols || geo.rows !== nextRows) {
905
- if (isDebugMode()) {
906
- log('DBG', `Size changed during debounce: requested ${nextCols}x${nextRows}, using ${finalCols}x${finalRows}`);
907
- }
908
- }
909
- // Atomic resize application
910
- applyResizeAtomic(finalCols, finalRows, screen, widgets);
911
- // SECOND PASS: Check if size changed again after 60ms (catches mid-transition changes)
912
- // This prevents partial redraws during Windows Terminal fullscreen transitions
913
- setTimeout(() => {
914
- try {
915
- const secondPassGeo = getTerminalGeometry(screen);
916
- if (secondPassGeo.cols !== finalCols || secondPassGeo.rows !== finalRows) {
917
- if (resizeTraceEnabled || isDebugMode()) {
918
- log('DBG', `[RESIZE] Second-pass debounce: ${finalCols}x${finalRows} -> ${secondPassGeo.cols}x${secondPassGeo.rows}`);
919
- }
920
- // Size changed again - apply once more
921
- resizeApplyCount++;
922
- lastAppliedSize = { cols: secondPassGeo.cols, rows: secondPassGeo.rows };
923
- updateResizeStats({ eventCount: resizeEventCount, applyCount: resizeApplyCount });
924
- applyResizeAtomic(secondPassGeo.cols, secondPassGeo.rows, screen, widgets);
925
- }
926
- }
927
- catch (e) {
928
- // Ignore second-pass errors
929
- }
930
- }, 60);
931
- }, RESIZE_DEBOUNCE_MS);
932
- }
933
- /**
934
- * Step 2: Resize handler - UPDATE LAYOUT ONLY
935
- * Step 4A: Bind once guard
936
- */
937
- function setupResizeHandling(screen, widgets, unbindFns) {
938
- // Step 4A: Hard guard - resize handler bound exactly once
939
- if (resizeHandlerBound) {
940
- log('SYS', 'WARN: Resize handler already bound, skipping', 'WARN');
941
- return;
942
- }
943
- resizeHandlerBound = true;
944
- updateResizeStats({ handlerBound: true });
945
- // Common resize handler (used by all resize signals: screen, stdout, SIGWINCH, program, polling)
946
- const handleResize = (source = 'unknown') => {
947
- resizeEventCount++;
948
- lastResizeEventAt = Date.now();
949
- if (resizeTraceEnabled || (isDebugMode() && resizeEventCount % 5 === 0)) {
950
- log('DBG', `[RESIZE] Event #${resizeEventCount} from: ${source}`);
951
- }
952
- // Check geometry stability (reject impossible sizes)
953
- if (!isGeometryStable(screen)) {
954
- if (isDebugMode()) {
955
- const geo = getTerminalGeometry(screen);
956
- log('DBG', `Unstable geometry detected: ${formatGeometry(geo)} - skipping`);
957
- }
958
- return;
959
- }
960
- // CRITICAL: Use getTerminalSize() as SINGLE SOURCE OF TRUTH
961
- // This ensures consistency between resize handler and layoutUI
962
- // getTerminalSize() prioritizes screen.program.cols/rows or screen.width/height
963
- // NOT process.stdout (which can lag on Windows fullscreen transitions)
964
- const currentSize = getTerminalSize(screen);
965
- const cols = currentSize.cols;
966
- const rows = currentSize.rows;
967
- // Log dimension source for debugging fullscreen issues
968
- if (resizeTraceEnabled || (isDebugMode() && resizeEventCount % 10 === 0)) {
969
- const stdoutCols = process.stdout.columns ?? 'N/A';
970
- const stdoutRows = process.stdout.rows ?? 'N/A';
971
- const programCols = screen.program?.cols ?? 'N/A';
972
- const programRows = screen.program?.rows ?? 'N/A';
973
- log('DBG', `[RESIZE] Using ${currentSize.source}: ${cols}x${rows} (stdout=${stdoutCols}x${stdoutRows}, program=${programCols}x${programRows})`);
974
- }
975
- // Check if geometry actually changed from last known
976
- // Compare against lastAppliedSize to avoid duplicate processing
977
- if (lastAppliedSize && lastAppliedSize.cols === cols && lastAppliedSize.rows === rows) {
978
- if (resizeTraceEnabled || (isDebugMode() && resizeEventCount % 20 === 0)) {
979
- log('DBG', `[RESIZE] Ignored: size unchanged (${cols}x${rows})`);
980
- }
981
- return; // No-op resize, ignore
982
- }
983
- // Schedule resize application (debounced + duplicate guard)
984
- scheduleResize(cols, rows, screen, widgets);
985
- };
986
- // Debug: Track listener binding
987
- trackListenerBinding('resize');
988
- // Count resize listeners (must be exactly 1 for screen + optionally 1 for stdout)
989
- let listenerCount = 0;
990
- // Instrument ALL resize signals (comprehensive coverage)
991
- // 1. Blessed screen resize event
992
- screen.on('resize', () => handleResize('screen'));
993
- listenerCount++;
994
- // 2. process.stdout resize (for Windows compatibility)
995
- if (typeof process.stdout.on === 'function') {
996
- process.stdout.on('resize', () => handleResize('stdout'));
997
- listenerCount++;
998
- }
999
- // 3. SIGWINCH signal (may not work on Windows but log it anyway)
1000
- if (typeof process.on === 'function') {
1001
- try {
1002
- process.on('SIGWINCH', () => handleResize('SIGWINCH'));
1003
- listenerCount++;
1004
- }
1005
- catch (e) {
1006
- // SIGWINCH not supported (e.g., Windows)
1007
- }
1008
- }
1009
- // 4. Blessed program resize (if available)
1010
- if (screen.program && typeof screen.program.on === 'function') {
1011
- try {
1012
- screen.program.on('resize', () => handleResize('program'));
1013
- listenerCount++;
1014
- }
1015
- catch (e) {
1016
- // Program resize not available
1017
- }
1018
- }
1019
- // Update stats with listener count
1020
- updateResizeStats({ listenerCount });
1021
- if (isDebugMode()) {
1022
- log('DBG', `Resize listeners bound: ${listenerCount} (screen, stdout, SIGWINCH, program)`);
1023
- }
1024
- // 5. Polling fallback (for Windows + browser terminals where events don't fire reliably)
1025
- // Windows fullscreen/maximize often fires multiple resizes (or sometimes none)
1026
- // Use 250ms interval on Windows, 100ms elsewhere
1027
- const isWindows = process.platform === 'win32';
1028
- const POLL_INTERVAL_MS = isWindows ? 250 : 100;
1029
- resizePollingTimer = setInterval(() => {
1030
- try {
1031
- // Use getTerminalSize() which prioritizes screen.program.cols/rows or screen.width/height
1032
- // This is the SAME source used by handleResize() and layoutUI() for consistency
1033
- const currentSize = getTerminalSize(screen);
1034
- const currentCols = currentSize.cols;
1035
- const currentRows = currentSize.rows;
1036
- // Check if size changed from last polled size OR from last applied size
1037
- let shouldTrigger = false;
1038
- if (lastPolledSize) {
1039
- if (lastPolledSize.cols !== currentCols || lastPolledSize.rows !== currentRows) {
1040
- shouldTrigger = true;
1041
- }
1042
- }
1043
- else {
1044
- // Initialize last polled size
1045
- lastPolledSize = { cols: currentCols, rows: currentRows };
1046
- }
1047
- // Also check against lastAppliedSize (catches cases where event fired but didn't apply)
1048
- // This is critical for fullscreen transitions where events might miss or lag
1049
- if (!shouldTrigger && lastAppliedSize) {
1050
- if (lastAppliedSize.cols !== currentCols || lastAppliedSize.rows !== currentRows) {
1051
- shouldTrigger = true;
1052
- if (resizeTraceEnabled || (isDebugMode() && resizeEventCount % 10 === 0)) {
1053
- log('DBG', `[POLL] Size mismatch: applied=${lastAppliedSize.cols}x${lastAppliedSize.rows}, current=${currentCols}x${currentRows}`);
1054
- }
1055
- }
1056
- }
1057
- if (shouldTrigger) {
1058
- // Size changed - trigger resize via polling
1059
- if (resizeTraceEnabled) {
1060
- const oldSize = lastPolledSize || lastAppliedSize || { cols: 0, rows: 0 };
1061
- log('DBG', `[POLL] Size changed: ${oldSize.cols}x${oldSize.rows} -> ${currentCols}x${currentRows} (source=${currentSize.source})`);
1062
- }
1063
- handleResize('polling');
1064
- // Update last polled size
1065
- lastPolledSize = { cols: currentCols, rows: currentRows };
1066
- }
1067
- }
1068
- catch (e) {
1069
- // Ignore polling errors
1070
- }
1071
- }, POLL_INTERVAL_MS);
1072
- // Clean up polling timer on unbind
1073
- unbindFns.push(() => {
1074
- if (resizePollingTimer) {
1075
- clearInterval(resizePollingTimer);
1076
- resizePollingTimer = null;
1077
- }
1078
- });
1079
- unbindFns.push(() => {
1080
- // Unbind all resize listeners
1081
- screen.off('resize', handleResize);
1082
- if (typeof process.stdout.removeListener === 'function') {
1083
- process.stdout.removeListener('resize', handleResize);
1084
- }
1085
- try {
1086
- process.removeListener('SIGWINCH', handleResize);
1087
- }
1088
- catch (e) {
1089
- // SIGWINCH not supported
1090
- }
1091
- if (screen.program && typeof screen.program.off === 'function') {
1092
- try {
1093
- screen.program.off('resize', handleResize);
1094
- }
1095
- catch (e) {
1096
- // Program resize not available
1097
- }
1098
- }
1099
- // Clear pending resize timer
1100
- if (resizeDebounceTimer) {
1101
- clearTimeout(resizeDebounceTimer);
1102
- resizeDebounceTimer = null;
1103
- }
1104
- // Clear polling timer
1105
- if (resizePollingTimer) {
1106
- clearInterval(resizePollingTimer);
1107
- resizePollingTimer = null;
1108
- }
1109
- });
1110
- // Disable automatic Resize Truth Panel updates - it interferes with command line
1111
- // User can manually check resize status with debug commands if needed
1112
- // Resize Truth Panel will still update when Operations panel naturally updates (via eventBus)
1113
- }
1114
- /**
1115
- * Update widget layout (geometry only)
1116
- */
1117
- function updateWidgetLayout(widget, rect) {
1118
- widget.top = rect.top;
1119
- widget.left = rect.left;
1120
- widget.width = rect.width;
1121
- widget.height = rect.height;
1122
- }
1123
- /**
1124
- * Create a read-only panel
1125
- */
1126
- function createPanel(screen, rect, title) {
1127
- const panel = blessedLib.box({
1128
- top: rect.top,
1129
- left: rect.left,
1130
- width: rect.width,
1131
- height: rect.height,
1132
- border: { type: 'line' },
1133
- tags: true,
1134
- content: '',
1135
- style: {
1136
- border: { fg: 'cyan' },
1137
- },
1138
- wrap: false,
1139
- scrollable: false,
1140
- clickable: false,
1141
- keys: false,
1142
- mouse: false,
1143
- input: false,
1144
- focusable: false, // CRITICAL: Panels must NOT be focusable (prevents cursor from appearing in wrong widget)
1145
- cursor: {
1146
- artificial: false, // Don't show cursor in panels
1147
- shape: 'line',
1148
- blink: false,
1149
- },
1150
- });
1151
- // Explicitly disable cursor in panel
1152
- if (panel.setCursor) {
1153
- panel.setCursor(false);
1154
- }
1155
- panel.setLabel(` {cyan-fg}${title}{/}`);
1156
- // Debug: Track widget creation
1157
- trackWidget(panel, title);
1158
- return panel;
1159
- }
1160
- /**
1161
- * Create status strip
1162
- */
1163
- function createStatusStrip(screen, layout) {
1164
- const statusWidth = Math.floor(layout.commandLine.width * 0.2);
1165
- const strip = blessedLib.box({
1166
- top: layout.commandLine.top,
1167
- left: layout.commandLine.left,
1168
- width: statusWidth,
1169
- height: layout.commandLine.height,
1170
- border: { type: 'line' },
1171
- tags: true,
1172
- content: ' OFFLINE',
1173
- style: {
1174
- border: { fg: 'grey' },
1175
- fg: 'grey',
1176
- },
1177
- clickable: false,
1178
- keys: false,
1179
- mouse: false,
1180
- input: false,
1181
- focusable: false,
1182
- });
1183
- strip.setLabel(' {grey-fg}STATUS{/}');
1184
- // Debug: Track widget creation
1185
- trackWidget(strip, 'STATUS_STRIP');
1186
- return strip;
1187
- }
1188
- /**
1189
- * Create command line
1190
- */
1191
- function createCommandLine(screen, layout) {
1192
- const statusWidth = Math.floor(layout.commandLine.width * 0.2);
1193
- // Hard clamp command line dimensions (prevents collapse)
1194
- // Minimum: 60 width, 3 height (if drawing border)
1195
- const MIN_COMMAND_LINE_WIDTH = 60;
1196
- const MIN_COMMAND_LINE_HEIGHT = 3;
1197
- const commandWidth = Math.max(MIN_COMMAND_LINE_WIDTH, layout.commandLine.width - statusWidth - 1);
1198
- const commandHeight = Math.max(MIN_COMMAND_LINE_HEIGHT, layout.commandLine.height);
1199
- // Ensure command line fits within terminal bounds
1200
- const geo = getTerminalGeometry(screen);
1201
- const cmdLeft = layout.commandLine.left + statusWidth + 1;
1202
- const maxCommandWidth = geo.safeCols - cmdLeft - 1; // Leave 1 char margin
1203
- const finalCommandWidth = Math.min(commandWidth, maxCommandWidth);
1204
- // Validate: cmdLeft + finalCommandWidth <= cols, cmdTop + commandHeight <= rows
1205
- const cmdRight = cmdLeft + finalCommandWidth;
1206
- const cmdBottom = layout.commandLine.top + commandHeight;
1207
- if (cmdRight > geo.safeCols || cmdBottom > geo.safeRows) {
1208
- if (isDebugMode()) {
1209
- log('ERR', `Command line layout error: right=${cmdRight} (max=${geo.safeCols}), bottom=${cmdBottom} (max=${geo.safeRows})`, 'WARN');
1210
- }
1211
- // Fallback to safe minimal layout
1212
- const safeWidth = Math.max(MIN_COMMAND_LINE_WIDTH, Math.min(finalCommandWidth, geo.safeCols - cmdLeft - 1));
1213
- const safeHeight = Math.max(MIN_COMMAND_LINE_HEIGHT, Math.min(commandHeight, geo.safeRows - layout.commandLine.top - 1));
1214
- // Use safe dimensions (will be applied in updateWidgetLayout)
1215
- }
1216
- const cmdLine = blessedLib.box({
1217
- top: layout.commandLine.top,
1218
- left: layout.commandLine.left + statusWidth + 1,
1219
- width: commandWidth,
1220
- height: commandHeight,
1221
- border: { type: 'line' },
1222
- tags: true,
1223
- content: PROMPT,
1224
- style: {
1225
- border: { fg: 'cyan' },
1226
- fg: 'white',
1227
- },
1228
- focusable: true, // MUST be focusable
1229
- keyable: true, // MUST be keyable
1230
- input: false, // We handle input via stdin, not blessed input
1231
- keys: false, // We handle keys via stdin, not blessed keys
1232
- vi: false, // Disable vi mode
1233
- alwaysScroll: false,
1234
- cursor: {
1235
- artificial: true, // Use artificial cursor (we render it manually)
1236
- shape: 'block',
1237
- blink: false,
1238
- },
1239
- });
1240
- // Explicitly ensure cursor handling
1241
- if (cmdLine.setCursor) {
1242
- // Don't let blessed manage cursor - we handle it manually
1243
- cmdLine.setCursor(false);
1244
- }
1245
- cmdLine.setLabel(' {cyan-fg}COMMAND LINE{/}');
1246
- // Debug: Track widget creation
1247
- trackWidget(cmdLine, 'COMMAND_LINE');
1248
- return cmdLine;
1249
- }
1250
- /**
1251
- * Setup input handling
1252
- */
1253
- function setupInputHandling(screen, commandLine, unbindFns) {
1254
- const updateDisplay = () => {
1255
- const availableWidth = commandLine.width - 4;
1256
- const fullText = commandInputValue;
1257
- const cursorIndex = cursorPosition;
1258
- if (cursorIndex - scrollOffset >= availableWidth - 1) {
1259
- scrollOffset = cursorIndex - availableWidth + 2;
1260
- }
1261
- if (cursorIndex < scrollOffset) {
1262
- scrollOffset = Math.max(0, cursorIndex - 5);
1263
- }
1264
- const visibleStart = scrollOffset;
1265
- const visibleEnd = Math.min(fullText.length, visibleStart + availableWidth - 1);
1266
- let visibleText = fullText.substring(visibleStart, visibleEnd);
1267
- const cursorPosInVisible = cursorIndex - visibleStart;
1268
- const beforeCursor = visibleText.substring(0, cursorPosInVisible);
1269
- const afterCursor = visibleText.substring(cursorPosInVisible);
1270
- const displayValue = beforeCursor + '{inverse} {/}' + afterCursor;
1271
- commandLine.setContent(displayValue);
1272
- screen.render();
1273
- };
1274
- const handleSubmit = async (value) => {
1275
- const commandPart = value.startsWith(PROMPT) ? value.slice(PROMPT.length).trim() : value.trim();
1276
- if (!commandPart) {
1277
- return;
1278
- }
1279
- // Add to history
1280
- commandHistory.push(commandPart);
1281
- if (commandHistory.length > 100) {
1282
- commandHistory.shift();
1283
- }
1284
- historyIndex = -1;
1285
- // Clear input
1286
- commandInputValue = PROMPT;
1287
- cursorPosition = PROMPT.length;
1288
- scrollOffset = 0;
1289
- updateDisplay();
1290
- // Emit command event
1291
- eventBus.emit({
1292
- tag: 'CMD',
1293
- msg: commandPart,
1294
- level: 'INFO'
1295
- });
1296
- // Execute command
1297
- const parsed = parse(commandPart);
1298
- if (!parsed.name)
1299
- return;
1300
- const ctx = {};
1301
- try {
1302
- const result = await execute(parsed, ctx);
1303
- // Emit result events
1304
- for (const event of result.events) {
1305
- eventBus.emit(event);
1306
- }
1307
- // Handle UI state update
1308
- if (result.uiStateUpdate) {
1309
- currentUiState = result.uiStateUpdate(currentUiState);
1310
- const runtime = getUIRuntime();
1311
- updateAllPanels(runtime.widgets);
1312
- // CRITICAL: Restore command line focus after command execution
1313
- // (prevents focus loss after command updates panels)
1314
- runtime.screen.focused = runtime.widgets.commandLine;
1315
- runtime.widgets.commandLine.focus();
1316
- runtime.screen.render();
1317
- }
1318
- // Handle actions
1319
- if (result.action === UiAction.EXIT) {
1320
- cleanExit(0);
1321
- }
1322
- else if (result.action === UiAction.CLEAR_FEED) {
1323
- feedStore.clear();
1324
- }
1325
- }
1326
- catch (error) {
1327
- eventBus.emit({
1328
- tag: 'ERR',
1329
- msg: `Command error: ${error.message}`,
1330
- level: 'ERROR'
1331
- });
1332
- }
1333
- };
1334
- const stdinListener = (data) => {
1335
- const input = data.toString();
1336
- if (input === '\x03') { // Ctrl+C
1337
- cleanExit(0);
1338
- return;
1339
- }
1340
- if (input === '\r' || input === '\n') { // Enter
1341
- const cmd = commandInputValue.startsWith(PROMPT)
1342
- ? commandInputValue.slice(PROMPT.length).trim()
1343
- : commandInputValue.trim();
1344
- if (cmd) {
1345
- handleSubmit(commandInputValue).catch(err => {
1346
- eventBus.emit({
1347
- tag: 'ERR',
1348
- msg: `Handler error: ${err.message}`,
1349
- level: 'ERROR'
1350
- });
1351
- });
1352
- }
1353
- else {
1354
- commandInputValue = PROMPT;
1355
- cursorPosition = PROMPT.length;
1356
- updateDisplay();
1357
- }
1358
- return;
1359
- }
1360
- if (input === '\x7f' || input === '\x08') { // Backspace
1361
- if (cursorPosition > PROMPT.length) {
1362
- commandInputValue = commandInputValue.substring(0, cursorPosition - 1) + commandInputValue.substring(cursorPosition);
1363
- cursorPosition--;
1364
- updateDisplay();
1365
- }
1366
- return;
1367
- }
1368
- if (input === '\x1b[D') { // Left arrow
1369
- if (cursorPosition > PROMPT.length) {
1370
- cursorPosition--;
1371
- updateDisplay();
1372
- }
1373
- return;
1374
- }
1375
- if (input === '\x1b[C') { // Right arrow
1376
- if (cursorPosition < commandInputValue.length) {
1377
- cursorPosition++;
1378
- updateDisplay();
1379
- }
1380
- return;
1381
- }
1382
- // Printable characters
1383
- if (input.length === 1 && input >= ' ' && input <= '~') {
1384
- commandInputValue = commandInputValue.substring(0, cursorPosition) + input + commandInputValue.substring(cursorPosition);
1385
- cursorPosition++;
1386
- updateDisplay();
1387
- }
1388
- };
1389
- process.stdin.on('data', stdinListener);
1390
- unbindFns.push(() => {
1391
- process.stdin.removeListener('data', stdinListener);
1392
- });
1393
- // CRITICAL: Focus command line and hide cursor on screen (we render cursor manually in command line)
1394
- // Hide cursor at screen level (blessed will try to show it otherwise)
1395
- if (screen.program && screen.program.hideCursor) {
1396
- screen.program.hideCursor();
1397
- }
1398
- // Focus command line (this ensures input goes to command line)
1399
- screen.focused = commandLine;
1400
- commandLine.focus();
1401
- // Ensure stdin is properly set up for raw input
1402
- if (process.stdin.isTTY) {
1403
- process.stdin.setRawMode(true);
1404
- process.stdin.setEncoding('utf8');
1405
- process.stdin.resume();
1406
- }
1407
- }
1408
- /**
1409
- * Update all panel content
1410
- *
1411
- * Guard: Skip updates during resize to prevent corruption
1412
- */
1413
- function updateAllPanels(widgets) {
1414
- // Skip state updates during resize (prevents corruption)
1415
- if (isResizing) {
1416
- return;
1417
- }
1418
- // Store current focus before updates (in case something steals it)
1419
- const wasCommandLineFocused = widgets.commandLine && (getUIRuntime()?.screen.focused === widgets.commandLine);
1420
- updatePanel(widgets.posture, renderPosturePanel(currentUiState.posture));
1421
- updatePanel(widgets.resources, renderResourcesPanel(currentUiState.resources));
1422
- updatePanel(widgets.assets, renderAssetsPanel(currentUiState.assets));
1423
- updateOperationsPanel(widgets.operations);
1424
- updatePanel(widgets.network, renderNetworkPanel(currentUiState.network));
1425
- updatePanel(widgets.capabilities, renderCapabilitiesPanel(currentUiState.capabilities));
1426
- // Update status strip
1427
- const statusValue = currentUiState.statusStrip;
1428
- if (isAvailable(statusValue)) {
1429
- const data = statusValue.value;
1430
- widgets.statusStrip.setContent(` ${data.left} | ${data.right} `);
1431
- }
1432
- else {
1433
- widgets.statusStrip.setContent(' OFFLINE');
1434
- }
1435
- // CRITICAL: Restore command line focus if it was focused (prevents panels from stealing focus)
1436
- if (wasCommandLineFocused && widgets.commandLine) {
1437
- const runtime = getUIRuntime();
1438
- if (runtime) {
1439
- runtime.screen.focused = widgets.commandLine;
1440
- widgets.commandLine.focus();
1441
- }
1442
- }
1443
- }
1444
- /**
1445
- * Update a panel with content lines
1446
- *
1447
- * Fix: Clear panel content before writing to prevent vertical artifacts
1448
- */
1449
- function updatePanel(panel, lines) {
1450
- const panelWidth = panel.width;
1451
- const panelHeight = panel.height;
1452
- const innerWidth = Math.max(1, panelWidth - 4);
1453
- const innerHeight = Math.max(1, panelHeight - 2);
1454
- // Clear panel content first (prevent vertical artifacts)
1455
- // Fill with blank lines to ensure old content is overwritten
1456
- const blankLines = [];
1457
- for (let i = 0; i < innerHeight; i++) {
1458
- blankLines.push(' '.repeat(innerWidth));
1459
- }
1460
- const clearedContent = '\n ' + blankLines.join('\n ');
1461
- panel.setContent(clearedContent);
1462
- // Now set actual content
1463
- const truncatedLines = lines.slice(0, innerHeight).map(line => {
1464
- if (line.length > innerWidth) {
1465
- return line.substring(0, innerWidth - 3) + '...';
1466
- }
1467
- // Pad with spaces to full width (ensures old content is overwritten)
1468
- return line.padEnd(innerWidth, ' ');
1469
- });
1470
- const content = '\n ' + truncatedLines.join('\n ');
1471
- panel.setContent(content);
1472
- }
1473
- /**
1474
- * Get Resize Truth Panel line (single line status)
1475
- */
1476
- function getResizeTruthLine() {
1477
- const runtime = getUIRuntime();
1478
- if (!runtime)
1479
- return 'RESIZE: runtime not available';
1480
- const screen = runtime.screen;
1481
- const stdoutCols = process.stdout.columns ?? 'N/A';
1482
- const stdoutRows = process.stdout.rows ?? 'N/A';
1483
- const screenWidth = screen.width ?? 'N/A';
1484
- const screenHeight = screen.height ?? 'N/A';
1485
- const programCols = screen.program?.cols ?? 'N/A';
1486
- const programRows = screen.program?.rows ?? 'N/A';
1487
- const geo = getTerminalGeometry(screen);
1488
- const resizeEventTime = lastResizeEventAt ? new Date(lastResizeEventAt).toLocaleTimeString() : 'never';
1489
- const layoutTime = layoutAppliedAt ? new Date(layoutAppliedAt).toLocaleTimeString() : 'never';
1490
- const layoutDims = lastLayoutCols !== null && lastLayoutRows !== null
1491
- ? `${lastLayoutCols}x${lastLayoutRows}`
1492
- : 'none';
1493
- // Format: single line with key metrics
1494
- return `RESIZE: stdout=${stdoutCols}x${stdoutRows} screen=${screenWidth}x${screenHeight} program=${programCols}x${programRows} | ` +
1495
- `used=${geo.cols}x${geo.rows}(${geo.source}) layout=${layoutDims} | ` +
1496
- `event=${resizeEventTime} applied=${layoutTime}`;
1497
- }
1498
- /**
1499
- * Update operations panel
1500
- *
1501
- * Fix: Clear panel content before writing to prevent vertical artifacts
1502
- * Adds: Resize Truth Panel line at top when in debug mode
1503
- */
1504
- function updateOperationsPanel(panel) {
1505
- const panelHeight = panel.height;
1506
- const panelWidth = panel.width;
1507
- const innerHeight = Math.max(1, panelHeight - 2);
1508
- const innerWidth = Math.max(1, panelWidth - 2);
1509
- // Clear panel content first (prevent vertical artifacts)
1510
- const blankLines = [];
1511
- for (let i = 0; i < innerHeight; i++) {
1512
- blankLines.push(' '.repeat(innerWidth));
1513
- }
1514
- const clearedContent = '\n ' + blankLines.join('\n ');
1515
- panel.setContent(clearedContent);
1516
- const lines = [];
1517
- // Add layout overlay if enabled (live instrumentation)
1518
- if (layoutOverlayEnabled && lastComputedLayout) {
1519
- const runtime = getUIRuntime();
1520
- const screen = runtime.screen;
1521
- const geo = getTerminalGeometry(screen);
1522
- // Line 1: Terminal dimensions (both sources)
1523
- const line1 = `TERM: scr=${screen.width}x${screen.height} out=${process.stdout.columns ?? 'N/A'}x${process.stdout.rows ?? 'N/A'}`;
1524
- lines.push(line1.substring(0, innerWidth).padEnd(innerWidth, ' '));
1525
- // Line 2: Layout widths
1526
- const leftW = lastComputedLayout.posture.width;
1527
- const centerW = lastComputedLayout.operations.width;
1528
- const rightW = lastComputedLayout.network.width;
1529
- const totalW = leftW + centerW + rightW + 4; // +4 for gutters
1530
- const line2 = `LAYOUT: L=${leftW} C=${centerW} R=${rightW} tot=${totalW} safe=${geo.safeCols}`;
1531
- lines.push(line2.substring(0, innerWidth).padEnd(innerWidth, ' '));
1532
- // Line 3: Resize stats
1533
- const line3 = `RESIZE: cnt=${resizeApplyCount} mode=${geo.mode}`;
1534
- lines.push(line3.substring(0, innerWidth).padEnd(innerWidth, ' '));
1535
- // Separator
1536
- lines.push('─'.repeat(innerWidth));
1537
- }
1538
- // Now set actual content (events)
1539
- const availableLines = innerHeight - lines.length;
1540
- const events = feedStore.getLatest(availableLines);
1541
- if (events.length === 0 && lines.length === 0) {
1542
- lines.push('No operations yet'.padEnd(innerWidth, ' '));
1543
- }
1544
- else {
1545
- for (const event of events.slice(-availableLines)) {
1546
- const time = new Date(event.ts).toLocaleTimeString();
1547
- const line = `${time} [${event.tag}] ${event.msg}`;
1548
- if (line.length > innerWidth) {
1549
- lines.push(line.substring(0, innerWidth - 3) + '...');
1550
- }
1551
- else {
1552
- // Pad with spaces to full width
1553
- lines.push(line.padEnd(innerWidth, ' '));
1554
- }
1555
- }
1556
- }
1557
- const content = '\n ' + lines.join('\n ');
1558
- panel.setContent(content);
1559
- }
1560
- /**
1561
- * Start background state updates
1562
- *
1563
- * Guard: Skip updates during resize to prevent corruption
1564
- */
1565
- function startBackgroundUpdates(widgets, screen) {
1566
- const interval = setInterval(async () => {
1567
- try {
1568
- // Skip state updates during resize (prevents corruption)
1569
- if (isResizing) {
1570
- return;
1571
- }
1572
- const newState = await buildUiState(currentUiState);
1573
- currentUiState = newState;
1574
- updateAllPanels(widgets);
1575
- // CRITICAL: Restore command line focus after background updates
1576
- // (prevents focus from being lost during background panel updates)
1577
- // Hide cursor at screen level
1578
- if (screen.program && screen.program.hideCursor) {
1579
- screen.program.hideCursor();
1580
- }
1581
- // Ensure stdin is active
1582
- if (process.stdin.isTTY && process.stdin.isPaused()) {
1583
- process.stdin.resume();
1584
- }
1585
- screen.focused = widgets.commandLine;
1586
- widgets.commandLine.focus();
1587
- screen.render();
1588
- }
1589
- catch (error) {
1590
- // Ignore errors
1591
- }
1592
- }, 1500);
1593
- const runtime = getUIRuntime();
1594
- runtime.unbindFns.push(() => clearInterval(interval));
1595
- }
1596
- /**
1597
- * Export layout dump for debug commands
1598
- */
1599
- export function dumpLayout() {
1600
- const runtime = getUIRuntime();
1601
- const widgets = runtime.widgets;
1602
- const screen = runtime.screen;
1603
- const geo = getTerminalGeometry(screen);
1604
- // Recompute layout to see calculations - use screen dimensions directly
1605
- const screenCols = screen.cols ?? screen.width ?? geo.cols;
1606
- const screenRows = screen.rows ?? screen.height ?? geo.rows;
1607
- const layoutResult = computePhase1Layout(screenCols, screenRows, screen);
1608
- const lines = [];
1609
- lines.push(`=== COMPREHENSIVE LAYOUT DEBUG ===`);
1610
- lines.push(``);
1611
- // Terminal geometry - ALL dimension sources
1612
- lines.push(`--- TERMINAL DIMENSIONS (ALL SOURCES) ---`);
1613
- lines.push(`screen.width: ${screen.width ?? 'N/A'}`);
1614
- lines.push(`screen.height: ${screen.height ?? 'N/A'}`);
1615
- lines.push(`screen.cols: ${screen.cols ?? 'N/A'}`);
1616
- lines.push(`screen.rows: ${screen.rows ?? 'N/A'}`);
1617
- lines.push(`screen.program.cols: ${screen.program?.cols ?? 'N/A'}`);
1618
- lines.push(`screen.program.rows: ${screen.program?.rows ?? 'N/A'}`);
1619
- lines.push(`process.stdout.columns: ${process.stdout.columns ?? 'N/A'}`);
1620
- lines.push(`process.stdout.rows: ${process.stdout.rows ?? 'N/A'}`);
1621
- lines.push(`process.stderr.columns: ${process.stderr.columns ?? 'N/A'}`);
1622
- lines.push(`process.stderr.rows: ${process.stderr.rows ?? 'N/A'}`);
1623
- lines.push(``);
1624
- lines.push(`--- GEOMETRY SOURCE OF TRUTH ---`);
1625
- lines.push(`Used source: ${geo.source}`);
1626
- lines.push(`Raw terminal: ${geo.cols} cols x ${geo.rows} rows`);
1627
- lines.push(`Safe terminal: ${geo.safeCols} cols x ${geo.safeRows} rows`);
1628
- lines.push(`Mode: ${geo.mode}`);
1629
- lines.push(`Last applied size: ${lastAppliedSize ? `${lastAppliedSize.cols}x${lastAppliedSize.rows}` : 'none'}`);
1630
- // Dimension mismatch warnings
1631
- const programCols = screen.program?.cols;
1632
- const programRows = screen.program?.rows;
1633
- const stdoutCols = process.stdout.columns;
1634
- const stdoutRows = process.stdout.rows;
1635
- if (programCols && stdoutCols && programCols !== stdoutCols) {
1636
- lines.push(`⚠ WARNING: screen.program.cols (${programCols}) !== process.stdout.columns (${stdoutCols})`);
1637
- }
1638
- if (programRows && stdoutRows && programRows !== stdoutRows) {
1639
- lines.push(`⚠ WARNING: screen.program.rows (${programRows}) !== process.stdout.rows (${stdoutRows})`);
1640
- }
1641
- lines.push(``);
1642
- // Layout calculations (if available)
1643
- if (layoutResult.ok && layoutResult.layout) {
1644
- const layout = layoutResult.layout;
1645
- const GUTTER = 1;
1646
- const COMMAND_LINE_HEIGHT = 3;
1647
- const totalGutters = GUTTER * 2;
1648
- const availableWidth = geo.safeCols - totalGutters;
1649
- const contentHeight = geo.safeRows - COMMAND_LINE_HEIGHT - GUTTER;
1650
- lines.push(`--- LAYOUT CALCULATIONS ---`);
1651
- lines.push(`Available width: ${geo.safeCols} - ${totalGutters} gutters = ${availableWidth}`);
1652
- lines.push(`Available height: ${geo.safeRows} - ${COMMAND_LINE_HEIGHT} cmd - ${GUTTER} gutter = ${contentHeight}`);
1653
- lines.push(``);
1654
- lines.push(`Column widths:`);
1655
- lines.push(` Left: ${layout.posture.width} (22% min 28)`);
1656
- lines.push(` Center: ${layout.operations.width} (50%)`);
1657
- lines.push(` Right: ${layout.network.width} (remainder)`);
1658
- lines.push(` Total columns: ${layout.posture.width + layout.operations.width + layout.network.width}`);
1659
- lines.push(` Total with gutters: ${layout.posture.width + layout.operations.width + layout.network.width + totalGutters}`);
1660
- lines.push(``);
1661
- // Calculate right edge
1662
- const rightColLeft = layout.posture.width + GUTTER + layout.operations.width + GUTTER;
1663
- const rightColRight = rightColLeft + layout.network.width;
1664
- const commandLineRight = layout.commandLine.left + layout.commandLine.width;
1665
- lines.push(`Position analysis:`);
1666
- lines.push(` Right column left: ${rightColLeft}`);
1667
- lines.push(` Right column right: ${rightColRight}`);
1668
- lines.push(` Command line right: ${commandLineRight}`);
1669
- lines.push(` Terminal safeCols: ${geo.safeCols}`);
1670
- lines.push(` Terminal raw cols: ${geo.cols}`);
1671
- lines.push(` Gap on right: ${geo.safeCols - rightColRight} chars`);
1672
- lines.push(` Command line gap: ${geo.safeCols - commandLineRight} chars`);
1673
- lines.push(``);
1674
- lines.push(`Height analysis:`);
1675
- lines.push(` Content height: ${contentHeight}`);
1676
- lines.push(` Operations height: ${layout.operations.height}`);
1677
- lines.push(` Command line top: ${layout.commandLine.top}`);
1678
- lines.push(` Command line bottom: ${layout.commandLine.top + layout.commandLine.height}`);
1679
- lines.push(` Terminal safeRows: ${geo.safeRows}`);
1680
- lines.push(` Terminal raw rows: ${geo.rows}`);
1681
- lines.push(` Gap on bottom: ${geo.safeRows - (layout.commandLine.top + layout.commandLine.height)} rows`);
1682
- lines.push(``);
1683
- }
1684
- else {
1685
- lines.push(`⚠ Layout computation failed: ${layoutResult.errorMessage}`);
1686
- lines.push(``);
1687
- }
1688
- // Actual widget positions
1689
- lines.push(`--- ACTUAL WIDGET POSITIONS ---`);
1690
- const panels = [
1691
- { name: 'POSTURE', widget: widgets.posture },
1692
- { name: 'RESOURCES', widget: widgets.resources },
1693
- { name: 'ASSETS', widget: widgets.assets },
1694
- { name: 'OPERATIONS', widget: widgets.operations },
1695
- { name: 'NETWORK', widget: widgets.network },
1696
- { name: 'CAPABILITIES', widget: widgets.capabilities },
1697
- { name: 'STATUS_STRIP', widget: widgets.statusStrip },
1698
- { name: 'COMMAND_LINE', widget: widgets.commandLine },
1699
- ];
1700
- let maxRight = 0;
1701
- let maxBottom = 0;
1702
- for (const { name, widget } of panels) {
1703
- const top = widget.top;
1704
- const left = widget.left;
1705
- const width = widget.width;
1706
- const height = widget.height;
1707
- const right = left + width;
1708
- const bottom = top + height;
1709
- maxRight = Math.max(maxRight, right);
1710
- maxBottom = Math.max(maxBottom, bottom);
1711
- lines.push(`${name}:`);
1712
- lines.push(` position: (${left}, ${top})`);
1713
- lines.push(` size: ${width}w x ${height}h`);
1714
- lines.push(` bounds: left=${left} right=${right} top=${top} bottom=${bottom}`);
1715
- lines.push(` right edge: ${right} (terminal safeCols: ${geo.safeCols}, raw cols: ${geo.cols})`);
1716
- lines.push(` bottom edge: ${bottom} (terminal safeRows: ${geo.safeRows}, raw rows: ${geo.rows})`);
1717
- // Check for overflow (right edge must be <= cols - 1)
1718
- const maxRightEdge = geo.safeCols - 1;
1719
- if (right > maxRightEdge) {
1720
- lines.push(` ⚠ OVERFLOW: right edge ${right} > ${maxRightEdge} (will cause scrollbar!)`);
1721
- }
1722
- lines.push(``);
1723
- }
1724
- lines.push(`--- SUMMARY ---`);
1725
- lines.push(`Max rendered width: ${maxRight} (terminal safeCols: ${geo.safeCols}, raw cols: ${geo.cols})`);
1726
- lines.push(`Max rendered height: ${maxBottom} (terminal safeRows: ${geo.safeRows}, raw rows: ${geo.rows})`);
1727
- lines.push(`Unused width: ${geo.safeCols - maxRight} chars (${((geo.safeCols - maxRight) / geo.safeCols * 100).toFixed(1)}%)`);
1728
- lines.push(`Unused height: ${geo.safeRows - maxBottom} rows (${((geo.safeRows - maxBottom) / geo.safeRows * 100).toFixed(1)}%)`);
1729
- lines.push(``);
1730
- // Overflow checks
1731
- const maxAllowedRight = geo.safeCols - 1;
1732
- if (maxRight > maxAllowedRight) {
1733
- lines.push(`⚠ CRITICAL: Layout overflow detected! Max right edge ${maxRight} > ${maxAllowedRight} (will cause horizontal scrollbar)`);
1734
- }
1735
- if (maxRight < geo.safeCols - 5) {
1736
- lines.push(`⚠ WARNING: Significant unused width detected (${geo.safeCols - maxRight} chars)`);
1737
- }
1738
- if (maxBottom < geo.safeRows - 5) {
1739
- lines.push(`⚠ WARNING: Significant unused height detected (${geo.safeRows - maxBottom} rows)`);
1740
- }
1741
- // Command line widget state (CRITICAL for input debugging)
1742
- lines.push(`--- COMMAND LINE WIDGET STATE ---`);
1743
- const cmd = widgets.commandLine;
1744
- const cmdLeft = cmd.left;
1745
- const cmdTop = cmd.top;
1746
- const cmdWidth = cmd.width;
1747
- const cmdHeight = cmd.height;
1748
- const cmdRight = cmdLeft + cmdWidth;
1749
- const cmdBottom = cmdTop + cmdHeight;
1750
- lines.push(`Position: left=${cmdLeft} top=${cmdTop} width=${cmdWidth} height=${cmdHeight}`);
1751
- lines.push(`Bounds: right=${cmdRight} bottom=${cmdBottom}`);
1752
- lines.push(`hidden: ${cmd.hidden ?? false}`);
1753
- lines.push(`detached: ${cmd.detached ?? false}`);
1754
- lines.push(`visible: ${cmd.visible ?? true}`);
1755
- lines.push(`focusable: ${cmd.focusable ?? false}`);
1756
- lines.push(`keyable: ${cmd.keyable ?? false}`);
1757
- lines.push(`input: ${cmd.input ?? false}`);
1758
- lines.push(`keys: ${cmd.keys ?? false}`);
1759
- lines.push(`mouse: ${cmd.mouse ?? false}`);
1760
- lines.push(`inputOnFocus: ${cmd.inputOnFocus ?? false}`);
1761
- lines.push(``);
1762
- lines.push(`Focus state:`);
1763
- lines.push(` screen.focused === cmd: ${screen.focused === cmd}`);
1764
- lines.push(` screen.focused?.type: ${screen.focused?.type ?? 'N/A'}`);
1765
- lines.push(` screen.focused?.name: ${screen.focused?.name ?? 'N/A'}`);
1766
- lines.push(` screen.focused?.content: ${screen.focused?.content?.substring(0, 20) ?? 'N/A'}...`);
1767
- lines.push(``);
1768
- // Check for global key handlers that might swallow input
1769
- lines.push(`--- KEY HANDLER CHECK ---`);
1770
- const screenListeners = screen._listeners || {};
1771
- const keyListeners = screenListeners.keypress || [];
1772
- const keyBindings = screen._bindings || {};
1773
- const globalKeyCount = Object.keys(keyBindings).length;
1774
- lines.push(`Global screen.key() bindings: ${globalKeyCount}`);
1775
- lines.push(`Global screen.on('keypress') listeners: ${keyListeners.length}`);
1776
- if (globalKeyCount > 0 || keyListeners.length > 0) {
1777
- lines.push(`⚠ WARNING: Global key handlers detected - may swallow input!`);
1778
- if (globalKeyCount > 0) {
1779
- lines.push(` Bound keys: ${Object.keys(keyBindings).slice(0, 10).join(', ')}${Object.keys(keyBindings).length > 10 ? '...' : ''}`);
1780
- }
1781
- }
1782
- else {
1783
- lines.push(`✓ No global key handlers (good - input goes to command line)`);
1784
- }
1785
- lines.push(``);
1786
- // Layout clamp validation
1787
- lines.push(`--- LAYOUT CLAMP VALIDATION ---`);
1788
- const cmdMinWidth = 60;
1789
- const cmdMinHeight = 3;
1790
- const cmdMaxWidth = geo.safeCols - 4;
1791
- const cmdMaxHeight = geo.safeRows - 2;
1792
- if (cmdWidth < cmdMinWidth) {
1793
- lines.push(`⚠ ERROR: Command line width ${cmdWidth} < minimum ${cmdMinWidth}`);
1794
- }
1795
- else {
1796
- lines.push(`✓ Command line width OK: ${cmdWidth} >= ${cmdMinWidth}`);
1797
- }
1798
- if (cmdHeight < cmdMinHeight) {
1799
- lines.push(`⚠ ERROR: Command line height ${cmdHeight} < minimum ${cmdMinHeight}`);
1800
- }
1801
- else {
1802
- lines.push(`✓ Command line height OK: ${cmdHeight} >= ${cmdMinHeight}`);
1803
- }
1804
- if (cmdRight > geo.safeCols) {
1805
- lines.push(`⚠ ERROR: Command line right edge ${cmdRight} > terminal cols ${geo.safeCols}`);
1806
- }
1807
- else {
1808
- lines.push(`✓ Command line right edge OK: ${cmdRight} <= ${geo.safeCols}`);
1809
- }
1810
- if (cmdBottom > geo.safeRows) {
1811
- lines.push(`⚠ ERROR: Command line bottom edge ${cmdBottom} > terminal rows ${geo.safeRows}`);
1812
- }
1813
- else {
1814
- lines.push(`✓ Command line bottom edge OK: ${cmdBottom} <= ${geo.safeRows}`);
1815
- }
1816
- lines.push(``);
1817
- lines.push(`=== END LAYOUT DEBUG ===`);
1818
- // Limit output to prevent hang/spam (max 50 lines for comprehensive debug)
1819
- const MAX_DEBUG_LINES = 50;
1820
- if (lines.length > MAX_DEBUG_LINES) {
1821
- return [
1822
- ...lines.slice(0, MAX_DEBUG_LINES - 2),
1823
- `... (${lines.length - MAX_DEBUG_LINES} more lines truncated) ...`,
1824
- lines[lines.length - 1]
1825
- ];
1826
- }
1827
- return lines;
1828
- }
1829
- /**
1830
- * Step 4: Clean exit
1831
- */
1832
- function cleanExit(code) {
1833
- destroyUIRuntime();
1834
- // Restore terminal
1835
- if (process.stdout.isTTY) {
1836
- process.stdout.write('\x1b[?25h'); // Show cursor
1837
- process.stdout.write('\x1b[?1049l'); // Exit alternate screen
1838
- }
1839
- process.exit(code);
1840
- }
1841
- //# sourceMappingURL=phase1RuntimeClean.js.map