4runr-os 2.1.4 → 2.1.5
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.
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/tui_mk1/kernel.d.ts.map +1 -1
- package/dist/tui_mk1/kernel.js +107 -4
- package/dist/tui_mk1/kernel.js.map +1 -1
- package/dist/tui_mk1/layout/layoutEngine.d.ts +38 -19
- package/dist/tui_mk1/layout/layoutEngine.d.ts.map +1 -1
- package/dist/tui_mk1/layout/layoutEngine.js +521 -228
- package/dist/tui_mk1/layout/layoutEngine.js.map +1 -1
- package/dist/tui_mk1/log.d.ts +23 -0
- package/dist/tui_mk1/log.d.ts.map +1 -0
- package/dist/tui_mk1/log.js +125 -0
- package/dist/tui_mk1/log.js.map +1 -0
- package/dist/tui_mk1/logger.d.ts.map +1 -1
- package/dist/tui_mk1/logger.js +20 -10
- package/dist/tui_mk1/logger.js.map +1 -1
- package/dist/tui_mk1/mk1App.d.ts +13 -18
- package/dist/tui_mk1/mk1App.d.ts.map +1 -1
- package/dist/tui_mk1/mk1App.js +334 -157
- package/dist/tui_mk1/mk1App.js.map +1 -1
- package/dist/tui_mk1/resizeController.d.ts.map +1 -1
- package/dist/tui_mk1/resizeController.js +15 -1
- package/dist/tui_mk1/resizeController.js.map +1 -1
- package/dist/tui_mk1/stdoutGuard.d.ts.map +1 -1
- package/dist/tui_mk1/stdoutGuard.js +7 -0
- package/dist/tui_mk1/stdoutGuard.js.map +1 -1
- package/dist/tui_mk1/terminalRestore.d.ts.map +1 -1
- package/dist/tui_mk1/terminalRestore.js +19 -1
- package/dist/tui_mk1/terminalRestore.js.map +1 -1
- package/dist/tui_mk1/ui/offenderScanner.d.ts +31 -0
- package/dist/tui_mk1/ui/offenderScanner.d.ts.map +1 -0
- package/dist/tui_mk1/ui/offenderScanner.js +80 -0
- package/dist/tui_mk1/ui/offenderScanner.js.map +1 -0
- package/dist/tui_mk1/ui/safe.d.ts +9 -0
- package/dist/tui_mk1/ui/safe.d.ts.map +1 -0
- package/dist/tui_mk1/ui/safe.js +29 -0
- package/dist/tui_mk1/ui/safe.js.map +1 -0
- package/dist/tui_mk1/ui/safeText.d.ts +22 -0
- package/dist/tui_mk1/ui/safeText.d.ts.map +1 -0
- package/dist/tui_mk1/ui/safeText.js +50 -0
- package/dist/tui_mk1/ui/safeText.js.map +1 -0
- package/dist/tui_mk1/ui/uiBuilder.d.ts +44 -0
- package/dist/tui_mk1/ui/uiBuilder.d.ts.map +1 -0
- package/dist/tui_mk1/ui/uiBuilder.js +467 -0
- package/dist/tui_mk1/ui/uiBuilder.js.map +1 -0
- package/dist/tui_mk1/viewport/safeViewport.d.ts +10 -1
- package/dist/tui_mk1/viewport/safeViewport.d.ts.map +1 -1
- package/dist/tui_mk1/viewport/safeViewport.js +47 -15
- package/dist/tui_mk1/viewport/safeViewport.js.map +1 -1
- package/dist/tui_mk2/kernel.d.ts +28 -0
- package/dist/tui_mk2/kernel.d.ts.map +1 -0
- package/dist/tui_mk2/kernel.js +86 -0
- package/dist/tui_mk2/kernel.js.map +1 -0
- package/dist/tui_mk2/mk2App.d.ts +33 -0
- package/dist/tui_mk2/mk2App.d.ts.map +1 -0
- package/dist/tui_mk2/mk2App.js +93 -0
- package/dist/tui_mk2/mk2App.js.map +1 -0
- package/package.json +8 -7
- package/dist/tui_mk1/ui/panels/createSection.d.ts +0 -18
- package/dist/tui_mk1/ui/panels/createSection.d.ts.map +0 -1
- package/dist/tui_mk1/ui/panels/createSection.js +0 -42
- package/dist/tui_mk1/ui/panels/createSection.js.map +0 -1
- package/dist/tui_mk1/ui/panels.d.ts +0 -23
- package/dist/tui_mk1/ui/panels.d.ts.map +0 -1
- package/dist/tui_mk1/ui/panels.js +0 -108
- package/dist/tui_mk1/ui/panels.js.map +0 -1
- package/dist/tui_mk1/ui/render/sectionRenderer.d.ts +0 -21
- package/dist/tui_mk1/ui/render/sectionRenderer.d.ts.map +0 -1
- package/dist/tui_mk1/ui/render/sectionRenderer.js +0 -32
- package/dist/tui_mk1/ui/render/sectionRenderer.js.map +0 -1
- package/dist/tui_mk1/ui/widgetManager.d.ts +0 -58
- package/dist/tui_mk1/ui/widgetManager.d.ts.map +0 -1
- package/dist/tui_mk1/ui/widgetManager.js +0 -197
- package/dist/tui_mk1/ui/widgetManager.js.map +0 -1
- package/dist/ui/boot/sequence.d.ts +0 -10
- package/dist/ui/boot/sequence.d.ts.map +0 -1
- package/dist/ui/boot/sequence.js +0 -171
- package/dist/ui/boot/sequence.js.map +0 -1
- package/dist/ui/constraints/layoutSpec.d.ts +0 -47
- package/dist/ui/constraints/layoutSpec.d.ts.map +0 -1
- package/dist/ui/constraints/layoutSpec.js +0 -60
- package/dist/ui/constraints/layoutSpec.js.map +0 -1
- package/dist/ui/constraints/unknownHandling.d.ts +0 -29
- package/dist/ui/constraints/unknownHandling.d.ts.map +0 -1
- package/dist/ui/constraints/unknownHandling.js +0 -60
- package/dist/ui/constraints/unknownHandling.js.map +0 -1
- package/dist/ui/drilldowns/feed.d.ts +0 -11
- package/dist/ui/drilldowns/feed.d.ts.map +0 -1
- package/dist/ui/drilldowns/feed.js +0 -68
- package/dist/ui/drilldowns/feed.js.map +0 -1
- package/dist/ui/drilldowns/index.d.ts +0 -7
- package/dist/ui/drilldowns/index.d.ts.map +0 -1
- package/dist/ui/drilldowns/index.js +0 -8
- package/dist/ui/drilldowns/index.js.map +0 -1
- package/dist/ui/drilldowns/posture.d.ts +0 -11
- package/dist/ui/drilldowns/posture.d.ts.map +0 -1
- package/dist/ui/drilldowns/posture.js +0 -74
- package/dist/ui/drilldowns/posture.js.map +0 -1
- package/dist/ui/intelligence-posture-view.d.ts +0 -22
- package/dist/ui/intelligence-posture-view.d.ts.map +0 -1
- package/dist/ui/intelligence-posture-view.js +0 -169
- package/dist/ui/intelligence-posture-view.js.map +0 -1
- package/dist/ui/navigation/keymaps.d.ts +0 -26
- package/dist/ui/navigation/keymaps.d.ts.map +0 -1
- package/dist/ui/navigation/keymaps.js +0 -135
- package/dist/ui/navigation/keymaps.js.map +0 -1
- package/dist/ui/navigation/palette.d.ts +0 -10
- package/dist/ui/navigation/palette.d.ts.map +0 -1
- package/dist/ui/navigation/palette.js +0 -133
- package/dist/ui/navigation/palette.js.map +0 -1
- package/dist/ui/navigation/state.d.ts +0 -47
- package/dist/ui/navigation/state.d.ts.map +0 -1
- package/dist/ui/navigation/state.js +0 -84
- package/dist/ui/navigation/state.js.map +0 -1
- package/dist/ui/navigation/types.d.ts +0 -38
- package/dist/ui/navigation/types.d.ts.map +0 -1
- package/dist/ui/navigation/types.js +0 -36
- package/dist/ui/navigation/types.js.map +0 -1
- package/dist/ui/panels/active-assets.d.ts +0 -12
- package/dist/ui/panels/active-assets.d.ts.map +0 -1
- package/dist/ui/panels/active-assets.js +0 -83
- package/dist/ui/panels/active-assets.js.map +0 -1
- package/dist/ui/panels/capability-flags.d.ts +0 -12
- package/dist/ui/panels/capability-flags.d.ts.map +0 -1
- package/dist/ui/panels/capability-flags.js +0 -59
- package/dist/ui/panels/capability-flags.js.map +0 -1
- package/dist/ui/panels/command-surface.d.ts +0 -12
- package/dist/ui/panels/command-surface.d.ts.map +0 -1
- package/dist/ui/panels/command-surface.js +0 -55
- package/dist/ui/panels/command-surface.js.map +0 -1
- package/dist/ui/panels/network-origin.d.ts +0 -12
- package/dist/ui/panels/network-origin.d.ts.map +0 -1
- package/dist/ui/panels/network-origin.js +0 -79
- package/dist/ui/panels/network-origin.js.map +0 -1
- package/dist/ui/panels/operations-feed.d.ts +0 -12
- package/dist/ui/panels/operations-feed.d.ts.map +0 -1
- package/dist/ui/panels/operations-feed.js +0 -90
- package/dist/ui/panels/operations-feed.js.map +0 -1
- package/dist/ui/panels/posture.d.ts +0 -12
- package/dist/ui/panels/posture.d.ts.map +0 -1
- package/dist/ui/panels/posture.js +0 -84
- package/dist/ui/panels/posture.js.map +0 -1
- package/dist/ui/panels/resources.d.ts +0 -11
- package/dist/ui/panels/resources.d.ts.map +0 -1
- package/dist/ui/panels/resources.js +0 -88
- package/dist/ui/panels/resources.js.map +0 -1
- package/dist/ui/primitives/Panel.d.ts +0 -25
- package/dist/ui/primitives/Panel.d.ts.map +0 -1
- package/dist/ui/primitives/Panel.js +0 -59
- package/dist/ui/primitives/Panel.js.map +0 -1
- package/dist/ui/rendering/metricRenderer.d.ts +0 -24
- package/dist/ui/rendering/metricRenderer.d.ts.map +0 -1
- package/dist/ui/rendering/metricRenderer.js +0 -86
- package/dist/ui/rendering/metricRenderer.js.map +0 -1
- package/dist/ui/runtime/hub.d.ts +0 -12
- package/dist/ui/runtime/hub.d.ts.map +0 -1
- package/dist/ui/runtime/hub.js +0 -486
- package/dist/ui/runtime/hub.js.map +0 -1
- package/dist/ui/runtime/hubValidation.d.ts +0 -23
- package/dist/ui/runtime/hubValidation.d.ts.map +0 -1
- package/dist/ui/runtime/hubValidation.js +0 -90
- package/dist/ui/runtime/hubValidation.js.map +0 -1
- package/dist/ui/runtime/index.d.ts +0 -29
- package/dist/ui/runtime/index.d.ts.map +0 -1
- package/dist/ui/runtime/index.js +0 -297
- package/dist/ui/runtime/index.js.map +0 -1
- package/dist/ui/runtime/no-tui.d.ts +0 -12
- package/dist/ui/runtime/no-tui.d.ts.map +0 -1
- package/dist/ui/runtime/no-tui.js +0 -77
- package/dist/ui/runtime/no-tui.js.map +0 -1
- package/dist/ui/runtime/state-builder.d.ts +0 -13
- package/dist/ui/runtime/state-builder.d.ts.map +0 -1
- package/dist/ui/runtime/state-builder.js +0 -114
- package/dist/ui/runtime/state-builder.js.map +0 -1
- package/dist/ui/runtime/terminalSizeCheck.d.ts +0 -10
- package/dist/ui/runtime/terminalSizeCheck.d.ts.map +0 -1
- package/dist/ui/runtime/terminalSizeCheck.js +0 -51
- package/dist/ui/runtime/terminalSizeCheck.js.map +0 -1
- package/dist/ui/runtime/tuiLogGate.d.ts +0 -22
- package/dist/ui/runtime/tuiLogGate.d.ts.map +0 -1
- package/dist/ui/runtime/tuiLogGate.js +0 -68
- package/dist/ui/runtime/tuiLogGate.js.map +0 -1
- package/dist/ui/state/types.d.ts +0 -72
- package/dist/ui/state/types.d.ts.map +0 -1
- package/dist/ui/state/types.js +0 -6
- package/dist/ui/state/types.js.map +0 -1
- package/dist/ui/theme/borders.d.ts +0 -20
- package/dist/ui/theme/borders.d.ts.map +0 -1
- package/dist/ui/theme/borders.js +0 -55
- package/dist/ui/theme/borders.js.map +0 -1
- package/dist/ui/theme/tokens.d.ts +0 -28
- package/dist/ui/theme/tokens.d.ts.map +0 -1
- package/dist/ui/theme/tokens.js +0 -50
- package/dist/ui/theme/tokens.js.map +0 -1
- package/dist/ui/theme/typography.d.ts +0 -14
- package/dist/ui/theme/typography.d.ts.map +0 -1
- package/dist/ui/theme/typography.js +0 -30
- package/dist/ui/theme/typography.js.map +0 -1
- package/dist/ui/v3/collectors/assets.collector.d.ts +0 -20
- package/dist/ui/v3/collectors/assets.collector.d.ts.map +0 -1
- package/dist/ui/v3/collectors/assets.collector.js +0 -80
- package/dist/ui/v3/collectors/assets.collector.js.map +0 -1
- package/dist/ui/v3/collectors/capabilities.collector.d.ts +0 -18
- package/dist/ui/v3/collectors/capabilities.collector.d.ts.map +0 -1
- package/dist/ui/v3/collectors/capabilities.collector.js +0 -113
- package/dist/ui/v3/collectors/capabilities.collector.js.map +0 -1
- package/dist/ui/v3/collectors/network.collector.d.ts +0 -18
- package/dist/ui/v3/collectors/network.collector.d.ts.map +0 -1
- package/dist/ui/v3/collectors/network.collector.js +0 -37
- package/dist/ui/v3/collectors/network.collector.js.map +0 -1
- package/dist/ui/v3/collectors/posture.derive.d.ts +0 -24
- package/dist/ui/v3/collectors/posture.derive.d.ts.map +0 -1
- package/dist/ui/v3/collectors/posture.derive.js +0 -57
- package/dist/ui/v3/collectors/posture.derive.js.map +0 -1
- package/dist/ui/v3/collectors/resources.d.ts +0 -23
- package/dist/ui/v3/collectors/resources.d.ts.map +0 -1
- package/dist/ui/v3/collectors/resources.js +0 -136
- package/dist/ui/v3/collectors/resources.js.map +0 -1
- package/dist/ui/v3/commands/commandEngine.d.ts +0 -77
- package/dist/ui/v3/commands/commandEngine.d.ts.map +0 -1
- package/dist/ui/v3/commands/commandEngine.js +0 -3289
- package/dist/ui/v3/commands/commandEngine.js.map +0 -1
- package/dist/ui/v3/commands/commandResult.d.ts +0 -25
- package/dist/ui/v3/commands/commandResult.d.ts.map +0 -1
- package/dist/ui/v3/commands/commandResult.js +0 -19
- package/dist/ui/v3/commands/commandResult.js.map +0 -1
- package/dist/ui/v3/commands/diagnose.d.ts +0 -17
- package/dist/ui/v3/commands/diagnose.d.ts.map +0 -1
- package/dist/ui/v3/commands/diagnose.js +0 -62
- package/dist/ui/v3/commands/diagnose.js.map +0 -1
- package/dist/ui/v3/commands/errorClassifier.d.ts +0 -23
- package/dist/ui/v3/commands/errorClassifier.d.ts.map +0 -1
- package/dist/ui/v3/commands/errorClassifier.js +0 -63
- package/dist/ui/v3/commands/errorClassifier.js.map +0 -1
- package/dist/ui/v3/commands/parser.d.ts +0 -14
- package/dist/ui/v3/commands/parser.d.ts.map +0 -1
- package/dist/ui/v3/commands/parser.js +0 -29
- package/dist/ui/v3/commands/parser.js.map +0 -1
- package/dist/ui/v3/commands/router.d.ts +0 -13
- package/dist/ui/v3/commands/router.d.ts.map +0 -1
- package/dist/ui/v3/commands/router.js +0 -150
- package/dist/ui/v3/commands/router.js.map +0 -1
- package/dist/ui/v3/config/gateway.d.ts +0 -40
- package/dist/ui/v3/config/gateway.d.ts.map +0 -1
- package/dist/ui/v3/config/gateway.js +0 -113
- package/dist/ui/v3/config/gateway.js.map +0 -1
- package/dist/ui/v3/core/event.d.ts +0 -19
- package/dist/ui/v3/core/event.d.ts.map +0 -1
- package/dist/ui/v3/core/event.js +0 -7
- package/dist/ui/v3/core/event.js.map +0 -1
- package/dist/ui/v3/core/eventBus.d.ts +0 -39
- package/dist/ui/v3/core/eventBus.d.ts.map +0 -1
- package/dist/ui/v3/core/eventBus.js +0 -79
- package/dist/ui/v3/core/eventBus.js.map +0 -1
- package/dist/ui/v3/core/feedStore.d.ts +0 -34
- package/dist/ui/v3/core/feedStore.d.ts.map +0 -1
- package/dist/ui/v3/core/feedStore.js +0 -46
- package/dist/ui/v3/core/feedStore.js.map +0 -1
- package/dist/ui/v3/core/logger.d.ts +0 -40
- package/dist/ui/v3/core/logger.d.ts.map +0 -1
- package/dist/ui/v3/core/logger.js +0 -191
- package/dist/ui/v3/core/logger.js.map +0 -1
- package/dist/ui/v3/core/opEvent.d.ts +0 -15
- package/dist/ui/v3/core/opEvent.d.ts.map +0 -1
- package/dist/ui/v3/core/opEvent.js +0 -7
- package/dist/ui/v3/core/opEvent.js.map +0 -1
- package/dist/ui/v3/index.d.ts +0 -8
- package/dist/ui/v3/index.d.ts.map +0 -1
- package/dist/ui/v3/index.js +0 -51
- package/dist/ui/v3/index.js.map +0 -1
- package/dist/ui/v3/runtime/moduleConfig.d.ts +0 -21
- package/dist/ui/v3/runtime/moduleConfig.d.ts.map +0 -1
- package/dist/ui/v3/runtime/moduleConfig.js +0 -41
- package/dist/ui/v3/runtime/moduleConfig.js.map +0 -1
- package/dist/ui/v3/section0/index.d.ts +0 -22
- package/dist/ui/v3/section0/index.d.ts.map +0 -1
- package/dist/ui/v3/section0/index.js +0 -88
- package/dist/ui/v3/section0/index.js.map +0 -1
- package/dist/ui/v3/section0/runtime/createScreen.d.ts +0 -27
- package/dist/ui/v3/section0/runtime/createScreen.d.ts.map +0 -1
- package/dist/ui/v3/section0/runtime/createScreen.js +0 -55
- package/dist/ui/v3/section0/runtime/createScreen.js.map +0 -1
- package/dist/ui/v3/section0/runtime/lifecycle.d.ts +0 -53
- package/dist/ui/v3/section0/runtime/lifecycle.d.ts.map +0 -1
- package/dist/ui/v3/section0/runtime/lifecycle.js +0 -172
- package/dist/ui/v3/section0/runtime/lifecycle.js.map +0 -1
- package/dist/ui/v3/section1/index.d.ts +0 -19
- package/dist/ui/v3/section1/index.d.ts.map +0 -1
- package/dist/ui/v3/section1/index.js +0 -413
- package/dist/ui/v3/section1/index.js.map +0 -1
- package/dist/ui/v3/section1/runtime/commandLine.d.ts +0 -49
- package/dist/ui/v3/section1/runtime/commandLine.d.ts.map +0 -1
- package/dist/ui/v3/section1/runtime/commandLine.js +0 -183
- package/dist/ui/v3/section1/runtime/commandLine.js.map +0 -1
- package/dist/ui/v3/section1/runtime/focusLock.d.ts +0 -24
- package/dist/ui/v3/section1/runtime/focusLock.d.ts.map +0 -1
- package/dist/ui/v3/section1/runtime/focusLock.js +0 -44
- package/dist/ui/v3/section1/runtime/focusLock.js.map +0 -1
- package/dist/ui/v3/state/assertUiState.d.ts +0 -27
- package/dist/ui/v3/state/assertUiState.d.ts.map +0 -1
- package/dist/ui/v3/state/assertUiState.js +0 -89
- package/dist/ui/v3/state/assertUiState.js.map +0 -1
- package/dist/ui/v3/state/capabilitiesStore.d.ts +0 -54
- package/dist/ui/v3/state/capabilitiesStore.d.ts.map +0 -1
- package/dist/ui/v3/state/capabilitiesStore.js +0 -76
- package/dist/ui/v3/state/capabilitiesStore.js.map +0 -1
- package/dist/ui/v3/state/defaultState.d.ts +0 -19
- package/dist/ui/v3/state/defaultState.d.ts.map +0 -1
- package/dist/ui/v3/state/defaultState.js +0 -28
- package/dist/ui/v3/state/defaultState.js.map +0 -1
- package/dist/ui/v3/state/gatewayConnectionStore.d.ts +0 -72
- package/dist/ui/v3/state/gatewayConnectionStore.d.ts.map +0 -1
- package/dist/ui/v3/state/gatewayConnectionStore.js +0 -108
- package/dist/ui/v3/state/gatewayConnectionStore.js.map +0 -1
- package/dist/ui/v3/state/initializePostureState.d.ts +0 -23
- package/dist/ui/v3/state/initializePostureState.d.ts.map +0 -1
- package/dist/ui/v3/state/initializePostureState.js +0 -41
- package/dist/ui/v3/state/initializePostureState.js.map +0 -1
- package/dist/ui/v3/state/panelStore.d.ts +0 -80
- package/dist/ui/v3/state/panelStore.d.ts.map +0 -1
- package/dist/ui/v3/state/panelStore.js +0 -131
- package/dist/ui/v3/state/panelStore.js.map +0 -1
- package/dist/ui/v3/state/resourcesData.d.ts +0 -15
- package/dist/ui/v3/state/resourcesData.d.ts.map +0 -1
- package/dist/ui/v3/state/resourcesData.js +0 -7
- package/dist/ui/v3/state/resourcesData.js.map +0 -1
- package/dist/ui/v3/state/uiState.d.ts +0 -22
- package/dist/ui/v3/state/uiState.d.ts.map +0 -1
- package/dist/ui/v3/state/uiState.js +0 -13
- package/dist/ui/v3/state/uiState.js.map +0 -1
- package/dist/ui/v3/state/uiStateBuilder.d.ts +0 -32
- package/dist/ui/v3/state/uiStateBuilder.d.ts.map +0 -1
- package/dist/ui/v3/state/uiStateBuilder.js +0 -73
- package/dist/ui/v3/state/uiStateBuilder.js.map +0 -1
- package/dist/ui/v3/state/uiStateTypes.d.ts +0 -59
- package/dist/ui/v3/state/uiStateTypes.d.ts.map +0 -1
- package/dist/ui/v3/state/uiStateTypes.js +0 -8
- package/dist/ui/v3/state/uiStateTypes.js.map +0 -1
- package/dist/ui/v3/state/value.d.ts +0 -80
- package/dist/ui/v3/state/value.d.ts.map +0 -1
- package/dist/ui/v3/state/value.js +0 -96
- package/dist/ui/v3/state/value.js.map +0 -1
- package/dist/ui/v3/tui/geometry.d.ts +0 -83
- package/dist/ui/v3/tui/geometry.d.ts.map +0 -1
- package/dist/ui/v3/tui/geometry.js +0 -201
- package/dist/ui/v3/tui/geometry.js.map +0 -1
- package/dist/ui/v3/tui/startTui.d.ts +0 -37
- package/dist/ui/v3/tui/startTui.d.ts.map +0 -1
- package/dist/ui/v3/tui/startTui.js +0 -61
- package/dist/ui/v3/tui/startTui.js.map +0 -1
- package/dist/ui/v3/tui/terminalMode.d.ts +0 -31
- package/dist/ui/v3/tui/terminalMode.d.ts.map +0 -1
- package/dist/ui/v3/tui/terminalMode.js +0 -76
- package/dist/ui/v3/tui/terminalMode.js.map +0 -1
- package/dist/ui/v3/ui/debugUtils.d.ts +0 -67
- package/dist/ui/v3/ui/debugUtils.d.ts.map +0 -1
- package/dist/ui/v3/ui/debugUtils.js +0 -238
- package/dist/ui/v3/ui/debugUtils.js.map +0 -1
- package/dist/ui/v3/ui/focus.d.ts +0 -28
- package/dist/ui/v3/ui/focus.d.ts.map +0 -1
- package/dist/ui/v3/ui/focus.js +0 -38
- package/dist/ui/v3/ui/focus.js.map +0 -1
- package/dist/ui/v3/ui/layout/hubLayout.d.ts +0 -43
- package/dist/ui/v3/ui/layout/hubLayout.d.ts.map +0 -1
- package/dist/ui/v3/ui/layout/hubLayout.js +0 -170
- package/dist/ui/v3/ui/layout/hubLayout.js.map +0 -1
- package/dist/ui/v3/ui/layout/phase1Layout.d.ts +0 -63
- package/dist/ui/v3/ui/layout/phase1Layout.d.ts.map +0 -1
- package/dist/ui/v3/ui/layout/phase1Layout.js +0 -274
- package/dist/ui/v3/ui/layout/phase1Layout.js.map +0 -1
- package/dist/ui/v3/ui/layout/phase1Layout.test.d.ts +0 -5
- package/dist/ui/v3/ui/layout/phase1Layout.test.d.ts.map +0 -1
- package/dist/ui/v3/ui/layout/phase1Layout.test.js +0 -120
- package/dist/ui/v3/ui/layout/phase1Layout.test.js.map +0 -1
- package/dist/ui/v3/ui/minimalRuntime.d.ts +0 -14
- package/dist/ui/v3/ui/minimalRuntime.d.ts.map +0 -1
- package/dist/ui/v3/ui/minimalRuntime.js +0 -111
- package/dist/ui/v3/ui/minimalRuntime.js.map +0 -1
- package/dist/ui/v3/ui/panels/AssetsPanel.d.ts +0 -17
- package/dist/ui/v3/ui/panels/AssetsPanel.d.ts.map +0 -1
- package/dist/ui/v3/ui/panels/AssetsPanel.js +0 -53
- package/dist/ui/v3/ui/panels/AssetsPanel.js.map +0 -1
- package/dist/ui/v3/ui/panels/CapabilitiesPanel.d.ts +0 -20
- package/dist/ui/v3/ui/panels/CapabilitiesPanel.d.ts.map +0 -1
- package/dist/ui/v3/ui/panels/CapabilitiesPanel.js +0 -67
- package/dist/ui/v3/ui/panels/CapabilitiesPanel.js.map +0 -1
- package/dist/ui/v3/ui/panels/NetworkPanel.d.ts +0 -31
- package/dist/ui/v3/ui/panels/NetworkPanel.d.ts.map +0 -1
- package/dist/ui/v3/ui/panels/NetworkPanel.js +0 -153
- package/dist/ui/v3/ui/panels/NetworkPanel.js.map +0 -1
- package/dist/ui/v3/ui/panels/PosturePanel.d.ts +0 -16
- package/dist/ui/v3/ui/panels/PosturePanel.d.ts.map +0 -1
- package/dist/ui/v3/ui/panels/PosturePanel.js +0 -60
- package/dist/ui/v3/ui/panels/PosturePanel.js.map +0 -1
- package/dist/ui/v3/ui/panels/ResourcesPanel.d.ts +0 -20
- package/dist/ui/v3/ui/panels/ResourcesPanel.d.ts.map +0 -1
- package/dist/ui/v3/ui/panels/ResourcesPanel.js +0 -66
- package/dist/ui/v3/ui/panels/ResourcesPanel.js.map +0 -1
- package/dist/ui/v3/ui/phase1Runtime.d.ts +0 -29
- package/dist/ui/v3/ui/phase1Runtime.d.ts.map +0 -1
- package/dist/ui/v3/ui/phase1Runtime.js +0 -1648
- package/dist/ui/v3/ui/phase1Runtime.js.map +0 -1
- package/dist/ui/v3/ui/phase1RuntimeClean.d.ts +0 -34
- package/dist/ui/v3/ui/phase1RuntimeClean.d.ts.map +0 -1
- package/dist/ui/v3/ui/phase1RuntimeClean.js +0 -1841
- package/dist/ui/v3/ui/phase1RuntimeClean.js.map +0 -1
- package/dist/ui/v3/ui/primitives/Panel.d.ts +0 -39
- package/dist/ui/v3/ui/primitives/Panel.d.ts.map +0 -1
- package/dist/ui/v3/ui/primitives/Panel.js +0 -105
- package/dist/ui/v3/ui/primitives/Panel.js.map +0 -1
- package/dist/ui/v3/ui/theme.d.ts +0 -37
- package/dist/ui/v3/ui/theme.d.ts.map +0 -1
- package/dist/ui/v3/ui/theme.js +0 -40
- package/dist/ui/v3/ui/theme.js.map +0 -1
- package/dist/ui/v3/ui/uiRuntime.d.ts +0 -40
- package/dist/ui/v3/ui/uiRuntime.d.ts.map +0 -1
- package/dist/ui/v3/ui/uiRuntime.js +0 -60
- package/dist/ui/v3/ui/uiRuntime.js.map +0 -1
- package/dist/ui/v3/ui/widgets/CommandLine.d.ts +0 -26
- package/dist/ui/v3/ui/widgets/CommandLine.d.ts.map +0 -1
- package/dist/ui/v3/ui/widgets/CommandLine.js +0 -67
- package/dist/ui/v3/ui/widgets/CommandLine.js.map +0 -1
- package/dist/ui/v3/v1Adapters/agents.d.ts +0 -72
- package/dist/ui/v3/v1Adapters/agents.d.ts.map +0 -1
- package/dist/ui/v3/v1Adapters/agents.js +0 -182
- package/dist/ui/v3/v1Adapters/agents.js.map +0 -1
- package/dist/ui/v3/v1Adapters/config.d.ts +0 -67
- package/dist/ui/v3/v1Adapters/config.d.ts.map +0 -1
- package/dist/ui/v3/v1Adapters/config.js +0 -78
- package/dist/ui/v3/v1Adapters/config.js.map +0 -1
- package/dist/ui/v3/v1Adapters/connect.d.ts +0 -77
- package/dist/ui/v3/v1Adapters/connect.d.ts.map +0 -1
- package/dist/ui/v3/v1Adapters/connect.js +0 -576
- package/dist/ui/v3/v1Adapters/connect.js.map +0 -1
- package/dist/ui/v3/v1Adapters/httpDebug.d.ts +0 -19
- package/dist/ui/v3/v1Adapters/httpDebug.d.ts.map +0 -1
- package/dist/ui/v3/v1Adapters/httpDebug.js +0 -60
- package/dist/ui/v3/v1Adapters/httpDebug.js.map +0 -1
- package/dist/ui/v3/v1Adapters/runs.d.ts +0 -77
- package/dist/ui/v3/v1Adapters/runs.d.ts.map +0 -1
- package/dist/ui/v3/v1Adapters/runs.js +0 -339
- package/dist/ui/v3/v1Adapters/runs.js.map +0 -1
- package/dist/ui/v4/engine/renderFrame.d.ts +0 -47
- package/dist/ui/v4/engine/renderFrame.d.ts.map +0 -1
- package/dist/ui/v4/engine/renderFrame.js +0 -653
- package/dist/ui/v4/engine/renderFrame.js.map +0 -1
- package/dist/ui/v4/engine/resizeController.d.ts +0 -48
- package/dist/ui/v4/engine/resizeController.d.ts.map +0 -1
- package/dist/ui/v4/engine/resizeController.js +0 -285
- package/dist/ui/v4/engine/resizeController.js.map +0 -1
- package/dist/ui/v4/engine/safeViewport.d.ts +0 -47
- package/dist/ui/v4/engine/safeViewport.d.ts.map +0 -1
- package/dist/ui/v4/engine/safeViewport.js +0 -123
- package/dist/ui/v4/engine/safeViewport.js.map +0 -1
- package/dist/ui/v4/engine/terminalProfile.d.ts +0 -56
- package/dist/ui/v4/engine/terminalProfile.d.ts.map +0 -1
- package/dist/ui/v4/engine/terminalProfile.js +0 -115
- package/dist/ui/v4/engine/terminalProfile.js.map +0 -1
- package/dist/ui/v4/index.d.ts +0 -28
- package/dist/ui/v4/index.d.ts.map +0 -1
- package/dist/ui/v4/index.js +0 -993
- package/dist/ui/v4/index.js.map +0 -1
- package/dist/ui/v4/layout/layoutEngine.d.ts +0 -62
- package/dist/ui/v4/layout/layoutEngine.d.ts.map +0 -1
- package/dist/ui/v4/layout/layoutEngine.js +0 -294
- package/dist/ui/v4/layout/layoutEngine.js.map +0 -1
- package/dist/ui/v4/runtime/keepAlive.d.ts +0 -21
- package/dist/ui/v4/runtime/keepAlive.d.ts.map +0 -1
- package/dist/ui/v4/runtime/keepAlive.js +0 -149
- package/dist/ui/v4/runtime/keepAlive.js.map +0 -1
- package/dist/ui/v4/runtime/logger.d.ts +0 -35
- package/dist/ui/v4/runtime/logger.d.ts.map +0 -1
- package/dist/ui/v4/runtime/logger.js +0 -109
- package/dist/ui/v4/runtime/logger.js.map +0 -1
- package/dist/ui/v5/debug/assertNoOverflow.d.ts +0 -28
- package/dist/ui/v5/debug/assertNoOverflow.d.ts.map +0 -1
- package/dist/ui/v5/debug/assertNoOverflow.js +0 -63
- package/dist/ui/v5/debug/assertNoOverflow.js.map +0 -1
- package/dist/ui/v5/debug/debugCommands.d.ts +0 -20
- package/dist/ui/v5/debug/debugCommands.d.ts.map +0 -1
- package/dist/ui/v5/debug/debugCommands.js +0 -461
- package/dist/ui/v5/debug/debugCommands.js.map +0 -1
- package/dist/ui/v5/debugCommands.d.ts +0 -20
- package/dist/ui/v5/debugCommands.d.ts.map +0 -1
- package/dist/ui/v5/debugCommands.js +0 -81
- package/dist/ui/v5/debugCommands.js.map +0 -1
- package/dist/ui/v5/guardrails/stdoutGuard.d.ts +0 -23
- package/dist/ui/v5/guardrails/stdoutGuard.d.ts.map +0 -1
- package/dist/ui/v5/guardrails/stdoutGuard.js +0 -94
- package/dist/ui/v5/guardrails/stdoutGuard.js.map +0 -1
- package/dist/ui/v5/guardrails/terminalRestore.d.ts +0 -17
- package/dist/ui/v5/guardrails/terminalRestore.d.ts.map +0 -1
- package/dist/ui/v5/guardrails/terminalRestore.js +0 -47
- package/dist/ui/v5/guardrails/terminalRestore.js.map +0 -1
- package/dist/ui/v5/index.d.ts +0 -30
- package/dist/ui/v5/index.d.ts.map +0 -1
- package/dist/ui/v5/index.js +0 -243
- package/dist/ui/v5/index.js.map +0 -1
- package/dist/ui/v5/kernel/kernel.d.ts +0 -81
- package/dist/ui/v5/kernel/kernel.d.ts.map +0 -1
- package/dist/ui/v5/kernel/kernel.js +0 -339
- package/dist/ui/v5/kernel/kernel.js.map +0 -1
- package/dist/ui/v5/kernel.d.ts +0 -75
- package/dist/ui/v5/kernel.d.ts.map +0 -1
- package/dist/ui/v5/kernel.js +0 -289
- package/dist/ui/v5/kernel.js.map +0 -1
- package/dist/ui/v5/layout/clampRect.d.ts +0 -28
- package/dist/ui/v5/layout/clampRect.d.ts.map +0 -1
- package/dist/ui/v5/layout/clampRect.js +0 -45
- package/dist/ui/v5/layout/clampRect.js.map +0 -1
- package/dist/ui/v5/layout/layoutEngine.d.ts +0 -16
- package/dist/ui/v5/layout/layoutEngine.d.ts.map +0 -1
- package/dist/ui/v5/layout/layoutEngine.js +0 -99
- package/dist/ui/v5/layout/layoutEngine.js.map +0 -1
- package/dist/ui/v5/renderGate.d.ts +0 -19
- package/dist/ui/v5/renderGate.d.ts.map +0 -1
- package/dist/ui/v5/renderGate.js +0 -36
- package/dist/ui/v5/renderGate.js.map +0 -1
- package/dist/ui/v5/resize/resizeController.d.ts +0 -62
- package/dist/ui/v5/resize/resizeController.d.ts.map +0 -1
- package/dist/ui/v5/resize/resizeController.js +0 -141
- package/dist/ui/v5/resize/resizeController.js.map +0 -1
- package/dist/ui/v5/resizeController.d.ts +0 -55
- package/dist/ui/v5/resizeController.d.ts.map +0 -1
- package/dist/ui/v5/resizeController.js +0 -124
- package/dist/ui/v5/resizeController.js.map +0 -1
- package/dist/ui/v5/runtime/keepAlive.d.ts +0 -37
- package/dist/ui/v5/runtime/keepAlive.d.ts.map +0 -1
- package/dist/ui/v5/runtime/keepAlive.js +0 -122
- package/dist/ui/v5/runtime/keepAlive.js.map +0 -1
- package/dist/ui/v5/runtime/restoreTerminal.d.ts +0 -34
- package/dist/ui/v5/runtime/restoreTerminal.d.ts.map +0 -1
- package/dist/ui/v5/runtime/restoreTerminal.js +0 -100
- package/dist/ui/v5/runtime/restoreTerminal.js.map +0 -1
- package/dist/ui/v5/runtime/stdoutGuard.d.ts +0 -42
- package/dist/ui/v5/runtime/stdoutGuard.d.ts.map +0 -1
- package/dist/ui/v5/runtime/stdoutGuard.js +0 -156
- package/dist/ui/v5/runtime/stdoutGuard.js.map +0 -1
- package/dist/ui/v5/viewport/getViewport.d.ts +0 -23
- package/dist/ui/v5/viewport/getViewport.d.ts.map +0 -1
- package/dist/ui/v5/viewport/getViewport.js +0 -117
- package/dist/ui/v5/viewport/getViewport.js.map +0 -1
- package/dist/ui/v5/viewport.d.ts +0 -41
- package/dist/ui/v5/viewport.d.ts.map +0 -1
- package/dist/ui/v5/viewport.js +0 -90
- package/dist/ui/v5/viewport.js.map +0 -1
- package/dist/ui/widgets/flagRow.d.ts +0 -25
- package/dist/ui/widgets/flagRow.d.ts.map +0 -1
- package/dist/ui/widgets/flagRow.js +0 -57
- package/dist/ui/widgets/flagRow.js.map +0 -1
- package/dist/ui/widgets/index.d.ts +0 -9
- package/dist/ui/widgets/index.d.ts.map +0 -1
- package/dist/ui/widgets/index.js +0 -9
- package/dist/ui/widgets/index.js.map +0 -1
- package/dist/ui/widgets/meter.d.ts +0 -18
- package/dist/ui/widgets/meter.d.ts.map +0 -1
- package/dist/ui/widgets/meter.js +0 -38
- package/dist/ui/widgets/meter.js.map +0 -1
- package/dist/ui/widgets/miniMap.d.ts +0 -26
- package/dist/ui/widgets/miniMap.d.ts.map +0 -1
- package/dist/ui/widgets/miniMap.js +0 -94
- package/dist/ui/widgets/miniMap.js.map +0 -1
- package/dist/ui/widgets/sparkline.d.ts +0 -17
- package/dist/ui/widgets/sparkline.d.ts.map +0 -1
- package/dist/ui/widgets/sparkline.js +0 -65
- 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
|