4runr-os 1.0.79 → 1.0.81
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/ui/v3/section0/index.d.ts +22 -0
- package/dist/ui/v3/section0/index.d.ts.map +1 -0
- package/dist/ui/v3/section0/index.js +88 -0
- package/dist/ui/v3/section0/index.js.map +1 -0
- package/dist/ui/v3/section0/runtime/createScreen.d.ts +27 -0
- package/dist/ui/v3/section0/runtime/createScreen.d.ts.map +1 -0
- package/dist/ui/v3/section0/runtime/createScreen.js +55 -0
- package/dist/ui/v3/section0/runtime/createScreen.js.map +1 -0
- package/dist/ui/v3/section0/runtime/lifecycle.d.ts +53 -0
- package/dist/ui/v3/section0/runtime/lifecycle.d.ts.map +1 -0
- package/dist/ui/v3/section0/runtime/lifecycle.js +172 -0
- package/dist/ui/v3/section0/runtime/lifecycle.js.map +1 -0
- package/dist/ui/v3/section1/index.d.ts +19 -0
- package/dist/ui/v3/section1/index.d.ts.map +1 -0
- package/dist/ui/v3/section1/index.js +174 -0
- package/dist/ui/v3/section1/index.js.map +1 -0
- package/dist/ui/v3/section1/runtime/commandLine.d.ts +49 -0
- package/dist/ui/v3/section1/runtime/commandLine.d.ts.map +1 -0
- package/dist/ui/v3/section1/runtime/commandLine.js +160 -0
- package/dist/ui/v3/section1/runtime/commandLine.js.map +1 -0
- package/dist/ui/v3/section1/runtime/focusLock.d.ts +24 -0
- package/dist/ui/v3/section1/runtime/focusLock.d.ts.map +1 -0
- package/dist/ui/v3/section1/runtime/focusLock.js +44 -0
- package/dist/ui/v3/section1/runtime/focusLock.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Section 0 - Stable App Shell
|
|
4
|
+
*
|
|
5
|
+
* Objective: Build a minimal TUI app shell that:
|
|
6
|
+
* - starts reliably
|
|
7
|
+
* - exits cleanly
|
|
8
|
+
* - restores the terminal every time
|
|
9
|
+
* - does not register duplicate handlers or multiple render loops
|
|
10
|
+
*
|
|
11
|
+
* No UI panels, no commands, no collectors yet. Just a stable shell.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Start Section 0 TUI shell
|
|
15
|
+
*
|
|
16
|
+
* Rules:
|
|
17
|
+
* - One render loop only (or none for Section 0 - just render once)
|
|
18
|
+
* - No console.log() while TUI is active
|
|
19
|
+
* - Exit must always restore terminal state
|
|
20
|
+
*/
|
|
21
|
+
export declare function startSection0(): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/v3/section0/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAaH;;;;;;;GAOG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAwDnD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Section 0 - Stable App Shell
|
|
4
|
+
*
|
|
5
|
+
* Objective: Build a minimal TUI app shell that:
|
|
6
|
+
* - starts reliably
|
|
7
|
+
* - exits cleanly
|
|
8
|
+
* - restores the terminal every time
|
|
9
|
+
* - does not register duplicate handlers or multiple render loops
|
|
10
|
+
*
|
|
11
|
+
* No UI panels, no commands, no collectors yet. Just a stable shell.
|
|
12
|
+
*/
|
|
13
|
+
import { createScreen } from './runtime/createScreen.js';
|
|
14
|
+
import { initializeLifecycle } from './runtime/lifecycle.js';
|
|
15
|
+
import blessed from 'neo-blessed';
|
|
16
|
+
const blessedLib = blessed;
|
|
17
|
+
// Suppress console.log during TUI (write debug logs to file if needed)
|
|
18
|
+
const originalConsoleLog = console.log;
|
|
19
|
+
const originalConsoleError = console.error;
|
|
20
|
+
/**
|
|
21
|
+
* Start Section 0 TUI shell
|
|
22
|
+
*
|
|
23
|
+
* Rules:
|
|
24
|
+
* - One render loop only (or none for Section 0 - just render once)
|
|
25
|
+
* - No console.log() while TUI is active
|
|
26
|
+
* - Exit must always restore terminal state
|
|
27
|
+
*/
|
|
28
|
+
export async function startSection0() {
|
|
29
|
+
// Suppress console.log during TUI
|
|
30
|
+
console.log = () => { };
|
|
31
|
+
// Keep console.error for fatal errors (but restore before using)
|
|
32
|
+
let consoleErrorRestored = false;
|
|
33
|
+
const restoreConsoleError = () => {
|
|
34
|
+
if (!consoleErrorRestored) {
|
|
35
|
+
console.error = originalConsoleError;
|
|
36
|
+
consoleErrorRestored = true;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
try {
|
|
40
|
+
// Create screen (singleton, guarded)
|
|
41
|
+
const screen = createScreen();
|
|
42
|
+
// Initialize lifecycle (exit handlers, guarded against duplicates)
|
|
43
|
+
initializeLifecycle(screen);
|
|
44
|
+
// Create a single centered text node
|
|
45
|
+
const message = '4Runr OS V3 — Shell OK (press q to exit)';
|
|
46
|
+
const textNode = blessedLib.box({
|
|
47
|
+
top: Math.floor((screen.height - 1) / 2),
|
|
48
|
+
left: Math.floor((screen.width - message.length) / 2),
|
|
49
|
+
width: message.length,
|
|
50
|
+
height: 1,
|
|
51
|
+
content: message,
|
|
52
|
+
tags: true,
|
|
53
|
+
style: {
|
|
54
|
+
fg: 'cyan',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
screen.append(textNode);
|
|
58
|
+
// Single render (no loop for Section 0)
|
|
59
|
+
screen.render();
|
|
60
|
+
// Restore console.log after successful start (for future use if needed)
|
|
61
|
+
// But keep it suppressed during TUI operation
|
|
62
|
+
// console.log = originalConsoleLog; // Commented: keep suppressed during TUI
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// Restore console.error for error output
|
|
66
|
+
restoreConsoleError();
|
|
67
|
+
console.error('FATAL: Section 0 TUI failed to start:', error);
|
|
68
|
+
// Ensure terminal is restored even on error
|
|
69
|
+
if (process.stdout.isTTY) {
|
|
70
|
+
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
71
|
+
process.stdout.write('\x1b[?1049l'); // Exit alternate screen
|
|
72
|
+
}
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// If run directly (not imported), start the TUI
|
|
77
|
+
// Simple check: if this file is executed directly, start the TUI
|
|
78
|
+
const scriptPath = process.argv[1]?.replace(/\\/g, '/') || '';
|
|
79
|
+
const isMainModule = scriptPath.includes('section0/index.js') ||
|
|
80
|
+
scriptPath.includes('section0/index.ts') ||
|
|
81
|
+
import.meta.url.includes('section0/index');
|
|
82
|
+
if (isMainModule) {
|
|
83
|
+
startSection0().catch((error) => {
|
|
84
|
+
console.error('FATAL: Section 0 failed:', error);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ui/v3/section0/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,OAAO,MAAM,aAAa,CAAC;AAGlC,MAAM,UAAU,GAAG,OAAc,CAAC;AAElC,uEAAuE;AACvE,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC;AACvC,MAAM,oBAAoB,GAAG,OAAO,CAAC,KAAK,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,kCAAkC;IAClC,OAAO,CAAC,GAAG,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;IAEvB,iEAAiE;IACjE,IAAI,oBAAoB,GAAG,KAAK,CAAC;IACjC,MAAM,mBAAmB,GAAG,GAAG,EAAE;QAC/B,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,GAAG,oBAAoB,CAAC;YACrC,oBAAoB,GAAG,IAAI,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,CAAC;QACH,qCAAqC;QACrC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAE9B,mEAAmE;QACnE,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAE5B,qCAAqC;QACrC,MAAM,OAAO,GAAG,0CAA0C,CAAC;QAC3D,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC;YAC9B,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACxC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,KAAK,EAAE,OAAO,CAAC,MAAM;YACrB,MAAM,EAAE,CAAC;YACT,OAAO,EAAE,OAAO;YAChB,IAAI,EAAE,IAAI;YACV,KAAK,EAAE;gBACL,EAAE,EAAE,MAAM;aACX;SACF,CAAgB,CAAC;QAElB,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAExB,wCAAwC;QACxC,MAAM,CAAC,MAAM,EAAE,CAAC;QAEhB,wEAAwE;QACxE,8CAA8C;QAC9C,6EAA6E;IAE/E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yCAAyC;QACzC,mBAAmB,EAAE,CAAC;QACtB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;QAE9D,4CAA4C;QAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc;YACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,wBAAwB;QAC/D,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,iEAAiE;AACjE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;AAC9D,MAAM,YAAY,GAAG,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACxC,UAAU,CAAC,QAAQ,CAAC,mBAAmB,CAAC;IACxC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;AAEhE,IAAI,YAAY,EAAE,CAAC;IACjB,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QAC9B,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 0 - Create Screen
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Initialize neo-blessed screen cleanly
|
|
6
|
+
* - Ensure only one screen instance exists
|
|
7
|
+
* - Configure screen with proper settings
|
|
8
|
+
*/
|
|
9
|
+
import type { Widgets } from 'neo-blessed';
|
|
10
|
+
/**
|
|
11
|
+
* Create or get the singleton screen instance
|
|
12
|
+
*
|
|
13
|
+
* Rules:
|
|
14
|
+
* - Must only create one screen instance
|
|
15
|
+
* - If screen already exists, return it (do not create another)
|
|
16
|
+
* - Configure with smartCSR, fullUnicode, dockBorders, warnings: false
|
|
17
|
+
*/
|
|
18
|
+
export declare function createScreen(): Widgets.Screen;
|
|
19
|
+
/**
|
|
20
|
+
* Get the current screen instance (may be null)
|
|
21
|
+
*/
|
|
22
|
+
export declare function getScreen(): Widgets.Screen | null;
|
|
23
|
+
/**
|
|
24
|
+
* Clear the screen instance reference (for testing/cleanup)
|
|
25
|
+
*/
|
|
26
|
+
export declare function clearScreen(): void;
|
|
27
|
+
//# sourceMappingURL=createScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createScreen.d.ts","sourceRoot":"","sources":["../../../../../src/ui/v3/section0/runtime/createScreen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAM3C;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAAC,MAAM,CAyB7C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAEjD;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAElC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 0 - Create Screen
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Initialize neo-blessed screen cleanly
|
|
6
|
+
* - Ensure only one screen instance exists
|
|
7
|
+
* - Configure screen with proper settings
|
|
8
|
+
*/
|
|
9
|
+
import blessed from 'neo-blessed';
|
|
10
|
+
const blessedLib = blessed;
|
|
11
|
+
let screenInstance = null;
|
|
12
|
+
/**
|
|
13
|
+
* Create or get the singleton screen instance
|
|
14
|
+
*
|
|
15
|
+
* Rules:
|
|
16
|
+
* - Must only create one screen instance
|
|
17
|
+
* - If screen already exists, return it (do not create another)
|
|
18
|
+
* - Configure with smartCSR, fullUnicode, dockBorders, warnings: false
|
|
19
|
+
*/
|
|
20
|
+
export function createScreen() {
|
|
21
|
+
if (screenInstance) {
|
|
22
|
+
return screenInstance;
|
|
23
|
+
}
|
|
24
|
+
screenInstance = blessedLib.screen({
|
|
25
|
+
smartCSR: true,
|
|
26
|
+
fullUnicode: true,
|
|
27
|
+
dockBorders: true,
|
|
28
|
+
warnings: false,
|
|
29
|
+
title: '4Runr OS (Terminal UI V3)',
|
|
30
|
+
});
|
|
31
|
+
if (!screenInstance) {
|
|
32
|
+
throw new Error('Failed to create blessed screen');
|
|
33
|
+
}
|
|
34
|
+
// Hide cursor on start (will be restored on exit)
|
|
35
|
+
if (screenInstance.program && screenInstance.program.hideCursor) {
|
|
36
|
+
screenInstance.program.hideCursor();
|
|
37
|
+
}
|
|
38
|
+
else if (process.stdout.isTTY) {
|
|
39
|
+
process.stdout.write('\x1b[?25l'); // ANSI hide cursor
|
|
40
|
+
}
|
|
41
|
+
return screenInstance;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the current screen instance (may be null)
|
|
45
|
+
*/
|
|
46
|
+
export function getScreen() {
|
|
47
|
+
return screenInstance;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Clear the screen instance reference (for testing/cleanup)
|
|
51
|
+
*/
|
|
52
|
+
export function clearScreen() {
|
|
53
|
+
screenInstance = null;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=createScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createScreen.js","sourceRoot":"","sources":["../../../../../src/ui/v3/section0/runtime/createScreen.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,OAAO,MAAM,aAAa,CAAC;AAGlC,MAAM,UAAU,GAAG,OAAc,CAAC;AAElC,IAAI,cAAc,GAA0B,IAAI,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY;IAC1B,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC;QACjC,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,KAAK;QACf,KAAK,EAAE,2BAA2B;KACnC,CAAmB,CAAC;IAErB,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,kDAAkD;IAClD,IAAK,cAAsB,CAAC,OAAO,IAAK,cAAsB,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QACjF,cAAsB,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IAC/C,CAAC;SAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,mBAAmB;IACxD,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 0 - Lifecycle Management
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Attach exit handlers (single registration, guarded)
|
|
6
|
+
* - Implement safe shutdown that always restores terminal
|
|
7
|
+
* - Track handler attachment to prevent duplicates
|
|
8
|
+
*/
|
|
9
|
+
import type { Widgets } from 'neo-blessed';
|
|
10
|
+
/**
|
|
11
|
+
* Attach exit handlers to screen
|
|
12
|
+
*
|
|
13
|
+
* Rules:
|
|
14
|
+
* - Must attach handlers exactly once (protected by exitHandlersAttached guard)
|
|
15
|
+
* - If called again, must no-op
|
|
16
|
+
* - Registers: q → exit, C-c → exit, escape → exit (optional)
|
|
17
|
+
*/
|
|
18
|
+
export declare function attachExitHandlers(screen: Widgets.Screen): void;
|
|
19
|
+
/**
|
|
20
|
+
* Attach process-level exit handlers
|
|
21
|
+
*
|
|
22
|
+
* Rules:
|
|
23
|
+
* - Must attach handlers exactly once (protected by exitHandlersAttached guard)
|
|
24
|
+
* - If called again, must no-op
|
|
25
|
+
* - Registers: SIGINT, SIGTERM
|
|
26
|
+
* - Tracks listeners for cleanup
|
|
27
|
+
*/
|
|
28
|
+
export declare function attachProcessExitHandlers(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Initialize all exit handlers (screen + process)
|
|
31
|
+
*
|
|
32
|
+
* This is the main entry point for lifecycle setup.
|
|
33
|
+
*/
|
|
34
|
+
export declare function initializeLifecycle(screen: Widgets.Screen): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get internal debug counters (for checkpoint verification)
|
|
37
|
+
*
|
|
38
|
+
* These counters verify:
|
|
39
|
+
* - Exit handlers attach exactly once
|
|
40
|
+
* - Shutdown runs exactly once
|
|
41
|
+
*
|
|
42
|
+
* Example expectation: Pressing q triggers exactly one shutdown path.
|
|
43
|
+
*/
|
|
44
|
+
export declare function getDebugCounters(): {
|
|
45
|
+
shutdownCallCount: number;
|
|
46
|
+
screenHandlerAttachmentCount: number;
|
|
47
|
+
processHandlerAttachmentCount: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Reset lifecycle state (for testing only)
|
|
51
|
+
*/
|
|
52
|
+
export declare function resetLifecycle(): void;
|
|
53
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../src/ui/v3/section0/runtime/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAmF3C;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAsB/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAuBhD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAGhE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,IAAI;IAClC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,4BAA4B,EAAE,MAAM,CAAC;IACrC,6BAA6B,EAAE,MAAM,CAAC;CACvC,CAMA;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAQrC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 0 - Lifecycle Management
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Attach exit handlers (single registration, guarded)
|
|
6
|
+
* - Implement safe shutdown that always restores terminal
|
|
7
|
+
* - Track handler attachment to prevent duplicates
|
|
8
|
+
*/
|
|
9
|
+
import { getScreen } from './createScreen.js';
|
|
10
|
+
// Guards to prevent duplicate handler attachment
|
|
11
|
+
let screenHandlersAttached = false;
|
|
12
|
+
let processHandlersAttached = false;
|
|
13
|
+
let shuttingDown = false;
|
|
14
|
+
// Internal debug counters (NOT console.log - for checkpoint verification)
|
|
15
|
+
// These track handler attachment and shutdown calls
|
|
16
|
+
let shutdownCallCount = 0;
|
|
17
|
+
let screenHandlerAttachmentCount = 0;
|
|
18
|
+
let processHandlerAttachmentCount = 0;
|
|
19
|
+
// Track attached process listeners for cleanup
|
|
20
|
+
const attachedProcessListeners = [];
|
|
21
|
+
/**
|
|
22
|
+
* Shutdown function that always restores terminal state
|
|
23
|
+
*
|
|
24
|
+
* Rules:
|
|
25
|
+
* - Must run exactly once (protected by shuttingDown guard)
|
|
26
|
+
* - Steps (in order):
|
|
27
|
+
* 1. screen.program.showCursor()
|
|
28
|
+
* 2. screen.destroy() (restores terminal modes)
|
|
29
|
+
* 3. Remove any process listeners we attached
|
|
30
|
+
* 4. process.exit(0)
|
|
31
|
+
*/
|
|
32
|
+
function shutdown(reason) {
|
|
33
|
+
// Internal debug counter
|
|
34
|
+
shutdownCallCount++;
|
|
35
|
+
// Guard: ensure shutdown runs exactly once
|
|
36
|
+
if (shuttingDown) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
shuttingDown = true;
|
|
40
|
+
const screen = getScreen();
|
|
41
|
+
if (screen) {
|
|
42
|
+
try {
|
|
43
|
+
// Step 1: Show cursor
|
|
44
|
+
if (screen.program && screen.program.showCursor) {
|
|
45
|
+
screen.program.showCursor();
|
|
46
|
+
}
|
|
47
|
+
else if (process.stdout.isTTY) {
|
|
48
|
+
process.stdout.write('\x1b[?25h'); // ANSI show cursor
|
|
49
|
+
}
|
|
50
|
+
// Step 2: Destroy screen (restores terminal modes)
|
|
51
|
+
screen.destroy();
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Ignore destroy errors, but ensure cursor is shown
|
|
55
|
+
if (process.stdout.isTTY) {
|
|
56
|
+
process.stdout.write('\x1b[?25h');
|
|
57
|
+
process.stdout.write('\x1b[?1049l'); // Exit alternate screen if needed
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// No screen, but ensure cursor is visible
|
|
63
|
+
if (process.stdout.isTTY) {
|
|
64
|
+
process.stdout.write('\x1b[?25h');
|
|
65
|
+
process.stdout.write('\x1b[?1049l');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Step 3: Remove process listeners we attached
|
|
69
|
+
for (const { event, listener } of attachedProcessListeners) {
|
|
70
|
+
try {
|
|
71
|
+
process.removeListener(event, listener);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
// Ignore removal errors
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
attachedProcessListeners.length = 0;
|
|
78
|
+
// Step 4: Exit
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Attach exit handlers to screen
|
|
83
|
+
*
|
|
84
|
+
* Rules:
|
|
85
|
+
* - Must attach handlers exactly once (protected by exitHandlersAttached guard)
|
|
86
|
+
* - If called again, must no-op
|
|
87
|
+
* - Registers: q → exit, C-c → exit, escape → exit (optional)
|
|
88
|
+
*/
|
|
89
|
+
export function attachExitHandlers(screen) {
|
|
90
|
+
// Guard: ensure handlers are attached exactly once
|
|
91
|
+
if (screenHandlersAttached) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
screenHandlersAttached = true;
|
|
95
|
+
screenHandlerAttachmentCount++;
|
|
96
|
+
// q → exit
|
|
97
|
+
screen.key(['q', 'Q'], () => {
|
|
98
|
+
shutdown('q pressed');
|
|
99
|
+
});
|
|
100
|
+
// C-c → exit
|
|
101
|
+
screen.key(['C-c'], () => {
|
|
102
|
+
shutdown('Ctrl+C pressed');
|
|
103
|
+
});
|
|
104
|
+
// escape → exit (optional, as requested)
|
|
105
|
+
screen.key(['escape'], () => {
|
|
106
|
+
shutdown('Escape pressed');
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Attach process-level exit handlers
|
|
111
|
+
*
|
|
112
|
+
* Rules:
|
|
113
|
+
* - Must attach handlers exactly once (protected by exitHandlersAttached guard)
|
|
114
|
+
* - If called again, must no-op
|
|
115
|
+
* - Registers: SIGINT, SIGTERM
|
|
116
|
+
* - Tracks listeners for cleanup
|
|
117
|
+
*/
|
|
118
|
+
export function attachProcessExitHandlers() {
|
|
119
|
+
// Guard: ensure handlers are attached exactly once
|
|
120
|
+
if (processHandlersAttached) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
processHandlersAttached = true;
|
|
124
|
+
processHandlerAttachmentCount++;
|
|
125
|
+
const sigintHandler = () => {
|
|
126
|
+
shutdown('SIGINT received');
|
|
127
|
+
};
|
|
128
|
+
const sigtermHandler = () => {
|
|
129
|
+
shutdown('SIGTERM received');
|
|
130
|
+
};
|
|
131
|
+
process.on('SIGINT', sigintHandler);
|
|
132
|
+
process.on('SIGTERM', sigtermHandler);
|
|
133
|
+
attachedProcessListeners.push({ event: 'SIGINT', listener: sigintHandler }, { event: 'SIGTERM', listener: sigtermHandler });
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Initialize all exit handlers (screen + process)
|
|
137
|
+
*
|
|
138
|
+
* This is the main entry point for lifecycle setup.
|
|
139
|
+
*/
|
|
140
|
+
export function initializeLifecycle(screen) {
|
|
141
|
+
attachExitHandlers(screen);
|
|
142
|
+
attachProcessExitHandlers();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get internal debug counters (for checkpoint verification)
|
|
146
|
+
*
|
|
147
|
+
* These counters verify:
|
|
148
|
+
* - Exit handlers attach exactly once
|
|
149
|
+
* - Shutdown runs exactly once
|
|
150
|
+
*
|
|
151
|
+
* Example expectation: Pressing q triggers exactly one shutdown path.
|
|
152
|
+
*/
|
|
153
|
+
export function getDebugCounters() {
|
|
154
|
+
return {
|
|
155
|
+
shutdownCallCount,
|
|
156
|
+
screenHandlerAttachmentCount,
|
|
157
|
+
processHandlerAttachmentCount,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Reset lifecycle state (for testing only)
|
|
162
|
+
*/
|
|
163
|
+
export function resetLifecycle() {
|
|
164
|
+
screenHandlersAttached = false;
|
|
165
|
+
processHandlersAttached = false;
|
|
166
|
+
shuttingDown = false;
|
|
167
|
+
attachedProcessListeners.length = 0;
|
|
168
|
+
shutdownCallCount = 0;
|
|
169
|
+
screenHandlerAttachmentCount = 0;
|
|
170
|
+
processHandlerAttachmentCount = 0;
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../../../src/ui/v3/section0/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Section 1 - Command Line is the Only Input
|
|
4
|
+
*
|
|
5
|
+
* Clean implementation that fixes:
|
|
6
|
+
* - "typing 4x" → typing appears exactly once
|
|
7
|
+
* - Cursor offset → cursor aligned immediately after prompt
|
|
8
|
+
* - Focus leaks → focus locked to command input
|
|
9
|
+
*
|
|
10
|
+
* Layout: 3 widgets at bottom
|
|
11
|
+
* - Label box: "4runr> " (read-only)
|
|
12
|
+
* - Input textbox: only focusable + only writable
|
|
13
|
+
* - Status strip: read-only status info
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Start Section 1 runtime
|
|
17
|
+
*/
|
|
18
|
+
export declare function startSection1(): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/ui/v3/section1/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAsGH;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAgFnD"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Section 1 - Command Line is the Only Input
|
|
4
|
+
*
|
|
5
|
+
* Clean implementation that fixes:
|
|
6
|
+
* - "typing 4x" → typing appears exactly once
|
|
7
|
+
* - Cursor offset → cursor aligned immediately after prompt
|
|
8
|
+
* - Focus leaks → focus locked to command input
|
|
9
|
+
*
|
|
10
|
+
* Layout: 3 widgets at bottom
|
|
11
|
+
* - Label box: "4runr> " (read-only)
|
|
12
|
+
* - Input textbox: only focusable + only writable
|
|
13
|
+
* - Status strip: read-only status info
|
|
14
|
+
*/
|
|
15
|
+
import blessed from 'neo-blessed';
|
|
16
|
+
import { createScreen } from '../section0/runtime/createScreen.js';
|
|
17
|
+
import { initializeLifecycle } from '../section0/runtime/lifecycle.js';
|
|
18
|
+
import { computePhase1Layout } from '../ui/layout/phase1Layout.js';
|
|
19
|
+
import { createCommandLineWidgets, focusCommandInput, attachCommandLineHandlers, updateStatusStrip } from './runtime/commandLine.js';
|
|
20
|
+
import { lockAllPanelsFocus, overrideTabNavigation } from './runtime/focusLock.js';
|
|
21
|
+
const blessedLib = blessed;
|
|
22
|
+
let screen = null;
|
|
23
|
+
let panels = [];
|
|
24
|
+
let commandLineWidgets = null;
|
|
25
|
+
// Command history
|
|
26
|
+
const commandHistory = [];
|
|
27
|
+
let historyIndex = -1;
|
|
28
|
+
/**
|
|
29
|
+
* Create all panels (read-only, non-focusable)
|
|
30
|
+
*/
|
|
31
|
+
function createPanels(screen, layout) {
|
|
32
|
+
if (!layout)
|
|
33
|
+
return [];
|
|
34
|
+
const panelNames = ['POSTURE', 'RESOURCES', 'ASSETS', 'OPERATIONS', 'NETWORK', 'CAPABILITIES'];
|
|
35
|
+
const panelRects = [
|
|
36
|
+
layout.posture,
|
|
37
|
+
layout.resources,
|
|
38
|
+
layout.assets,
|
|
39
|
+
layout.operations,
|
|
40
|
+
layout.network,
|
|
41
|
+
layout.capabilities,
|
|
42
|
+
];
|
|
43
|
+
const createdPanels = [];
|
|
44
|
+
for (let i = 0; i < panelNames.length; i++) {
|
|
45
|
+
const rect = panelRects[i];
|
|
46
|
+
const name = panelNames[i];
|
|
47
|
+
const panel = blessedLib.box({
|
|
48
|
+
top: rect.top,
|
|
49
|
+
left: rect.left,
|
|
50
|
+
width: rect.width,
|
|
51
|
+
height: rect.height,
|
|
52
|
+
border: { type: 'line' },
|
|
53
|
+
tags: true,
|
|
54
|
+
content: `\n ${name} Panel\n\n Content will appear here...`,
|
|
55
|
+
style: {
|
|
56
|
+
border: { fg: 'cyan' },
|
|
57
|
+
fg: 'white',
|
|
58
|
+
},
|
|
59
|
+
// All panels are non-focusable (will be locked by focusLock)
|
|
60
|
+
focusable: false,
|
|
61
|
+
keyable: false,
|
|
62
|
+
mouse: false,
|
|
63
|
+
clickable: false,
|
|
64
|
+
keys: false,
|
|
65
|
+
input: false,
|
|
66
|
+
});
|
|
67
|
+
panel.setLabel(` {cyan-fg}${name}{/}`);
|
|
68
|
+
screen.append(panel);
|
|
69
|
+
createdPanels.push(panel);
|
|
70
|
+
}
|
|
71
|
+
return createdPanels;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Handle command submission
|
|
75
|
+
*/
|
|
76
|
+
function handleCommandSubmit(value) {
|
|
77
|
+
// Add to history
|
|
78
|
+
if (value.trim()) {
|
|
79
|
+
commandHistory.push(value);
|
|
80
|
+
if (commandHistory.length > 100) {
|
|
81
|
+
commandHistory.shift(); // Keep last 100
|
|
82
|
+
}
|
|
83
|
+
historyIndex = -1; // Reset history index
|
|
84
|
+
}
|
|
85
|
+
// TODO: Route command (will be implemented in Section 2)
|
|
86
|
+
// For now, just log to operations panel
|
|
87
|
+
const operationsPanel = panels[3]; // OPERATIONS is index 3
|
|
88
|
+
if (operationsPanel) {
|
|
89
|
+
const timestamp = new Date().toISOString();
|
|
90
|
+
const newLine = `[${timestamp}] Command: ${value}`;
|
|
91
|
+
// Get existing content via getContent() or track separately
|
|
92
|
+
const existingContent = operationsPanel.content || '';
|
|
93
|
+
operationsPanel.setContent(existingContent + '\n' + newLine);
|
|
94
|
+
}
|
|
95
|
+
// Update status strip
|
|
96
|
+
if (commandLineWidgets) {
|
|
97
|
+
updateStatusStrip(commandLineWidgets, `MODE: LOCAL | LAST: ${value.substring(0, 20)} | POSTURE: OK`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Start Section 1 runtime
|
|
102
|
+
*/
|
|
103
|
+
export async function startSection1() {
|
|
104
|
+
// Suppress console.log during TUI
|
|
105
|
+
const originalLog = console.log;
|
|
106
|
+
console.log = () => { };
|
|
107
|
+
try {
|
|
108
|
+
// Create screen (from Section 0)
|
|
109
|
+
screen = createScreen();
|
|
110
|
+
if (!screen) {
|
|
111
|
+
throw new Error('Failed to create screen');
|
|
112
|
+
}
|
|
113
|
+
// Initialize lifecycle (exit handlers from Section 0)
|
|
114
|
+
initializeLifecycle(screen);
|
|
115
|
+
// Compute layout
|
|
116
|
+
const layoutResult = computePhase1Layout(screen.width, screen.height);
|
|
117
|
+
if (!layoutResult.ok || !layoutResult.layout) {
|
|
118
|
+
throw new Error(layoutResult.errorMessage || 'Failed to compute layout');
|
|
119
|
+
}
|
|
120
|
+
const layout = layoutResult.layout;
|
|
121
|
+
// Create panels (read-only)
|
|
122
|
+
panels = createPanels(screen, layout);
|
|
123
|
+
// Create command line widgets (3 widgets: label, input, status)
|
|
124
|
+
const commandLineRect = layout.commandLine;
|
|
125
|
+
commandLineWidgets = createCommandLineWidgets(screen, commandLineRect.top, commandLineRect.left, commandLineRect.width, 1 // Height is 1 for single-line input
|
|
126
|
+
);
|
|
127
|
+
// Lock focus on all panels (they redirect clicks to input)
|
|
128
|
+
lockAllPanelsFocus(panels, () => {
|
|
129
|
+
if (commandLineWidgets) {
|
|
130
|
+
focusCommandInput(commandLineWidgets, 'panel-click');
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// Override Tab navigation to refocus input
|
|
134
|
+
overrideTabNavigation(screen, () => {
|
|
135
|
+
if (commandLineWidgets) {
|
|
136
|
+
focusCommandInput(commandLineWidgets, 'tab');
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// Attach command line handlers (deduplicated)
|
|
140
|
+
attachCommandLineHandlers(screen, commandLineWidgets, handleCommandSubmit);
|
|
141
|
+
// Initial focus (once on startup, after render)
|
|
142
|
+
// Use setImmediate to ensure focus happens after initial render
|
|
143
|
+
setImmediate(() => {
|
|
144
|
+
if (commandLineWidgets) {
|
|
145
|
+
focusCommandInput(commandLineWidgets, 'startup');
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
// Initial render
|
|
149
|
+
screen.render();
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
// Restore console.error for error output
|
|
153
|
+
console.error = originalLog;
|
|
154
|
+
console.error('FATAL: Section 1 failed:', error);
|
|
155
|
+
// Ensure terminal is restored
|
|
156
|
+
if (process.stdout.isTTY) {
|
|
157
|
+
process.stdout.write('\x1b[?25h');
|
|
158
|
+
process.stdout.write('\x1b[?1049l');
|
|
159
|
+
}
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// If run directly (not imported), start the TUI
|
|
164
|
+
const scriptPath = process.argv[1]?.replace(/\\/g, '/') || '';
|
|
165
|
+
const isMainModule = scriptPath.includes('section1/index.js') ||
|
|
166
|
+
scriptPath.includes('section1/index.ts') ||
|
|
167
|
+
import.meta.url.includes('section1/index');
|
|
168
|
+
if (isMainModule) {
|
|
169
|
+
startSection1().catch((error) => {
|
|
170
|
+
console.error('FATAL: Section 1 failed:', error);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ui/v3/section1/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 1 - Command Line Input System
|
|
3
|
+
*
|
|
4
|
+
* Clean implementation with:
|
|
5
|
+
* - 3 widgets: Label box, Input textbox, Status strip
|
|
6
|
+
* - Only textbox is focusable
|
|
7
|
+
* - Deduplicated handler registration
|
|
8
|
+
* - Proper cursor alignment
|
|
9
|
+
* - Focus lock
|
|
10
|
+
*/
|
|
11
|
+
import type { Widgets } from 'neo-blessed';
|
|
12
|
+
export interface CommandLineWidgets {
|
|
13
|
+
labelBox: Widgets.Box;
|
|
14
|
+
inputTextbox: Widgets.Textbox;
|
|
15
|
+
statusStrip: Widgets.Box;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create the three command line widgets
|
|
19
|
+
*
|
|
20
|
+
* Layout:
|
|
21
|
+
* [Label Box: "4runr> "] [Input Textbox] [Status Strip]
|
|
22
|
+
*/
|
|
23
|
+
export declare function createCommandLineWidgets(screen: Widgets.Screen, top: number, left: number, width: number, height: number): CommandLineWidgets;
|
|
24
|
+
/**
|
|
25
|
+
* Focus command input (without creating refocus loops)
|
|
26
|
+
*
|
|
27
|
+
* Rules:
|
|
28
|
+
* - Call only: once on startup, after submit, after command handling
|
|
29
|
+
* - Do NOT call inside: screen.on("render"), periodic loops, panel renders
|
|
30
|
+
*/
|
|
31
|
+
export declare function focusCommandInput(widgets: CommandLineWidgets, reason?: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Attach command line handlers (deduplicated)
|
|
34
|
+
*
|
|
35
|
+
* Rules:
|
|
36
|
+
* - Attach exactly once (guarded)
|
|
37
|
+
* - Use textbox events only for typing + Enter submission
|
|
38
|
+
* - Use screen keys only for global exit shortcuts (q, Ctrl+C)
|
|
39
|
+
*/
|
|
40
|
+
export declare function attachCommandLineHandlers(screen: Widgets.Screen, widgets: CommandLineWidgets, onSubmit: (value: string) => void, onCancel?: () => void): void;
|
|
41
|
+
/**
|
|
42
|
+
* Update status strip content
|
|
43
|
+
*/
|
|
44
|
+
export declare function updateStatusStrip(widgets: CommandLineWidgets, content: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Reset handler attachment guard (for testing)
|
|
47
|
+
*/
|
|
48
|
+
export declare function resetCommandLineHandlers(): void;
|
|
49
|
+
//# sourceMappingURL=commandLine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandLine.d.ts","sourceRoot":"","sources":["../../../../../src/ui/v3/section1/runtime/commandLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC;IAC9B,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC;CAC1B;AASD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,OAAO,CAAC,MAAM,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,kBAAkB,CAmEpB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CASN;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,CAAC,MAAM,EACtB,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,QAAQ,CAAC,EAAE,MAAM,IAAI,GACpB,IAAI,CAyCN;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,MAAM,GACd,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 1 - Command Line Input System
|
|
3
|
+
*
|
|
4
|
+
* Clean implementation with:
|
|
5
|
+
* - 3 widgets: Label box, Input textbox, Status strip
|
|
6
|
+
* - Only textbox is focusable
|
|
7
|
+
* - Deduplicated handler registration
|
|
8
|
+
* - Proper cursor alignment
|
|
9
|
+
* - Focus lock
|
|
10
|
+
*/
|
|
11
|
+
import blessed from 'neo-blessed';
|
|
12
|
+
const blessedLib = blessed;
|
|
13
|
+
const PROMPT = '4runr> ';
|
|
14
|
+
const PROMPT_WIDTH = PROMPT.length;
|
|
15
|
+
const GAP = 1; // Gap between label and input
|
|
16
|
+
// Guard to prevent duplicate handler attachment
|
|
17
|
+
let commandHandlersAttached = false;
|
|
18
|
+
/**
|
|
19
|
+
* Create the three command line widgets
|
|
20
|
+
*
|
|
21
|
+
* Layout:
|
|
22
|
+
* [Label Box: "4runr> "] [Input Textbox] [Status Strip]
|
|
23
|
+
*/
|
|
24
|
+
export function createCommandLineWidgets(screen, top, left, width, height) {
|
|
25
|
+
// Calculate widths
|
|
26
|
+
const statusWidth = 40; // Fixed width for status strip
|
|
27
|
+
const inputWidth = width - PROMPT_WIDTH - GAP - statusWidth - GAP;
|
|
28
|
+
// 1. Label box (read-only, non-focusable)
|
|
29
|
+
const labelBox = blessedLib.box({
|
|
30
|
+
top,
|
|
31
|
+
left,
|
|
32
|
+
width: PROMPT_WIDTH,
|
|
33
|
+
height: 1,
|
|
34
|
+
content: PROMPT,
|
|
35
|
+
tags: false, // No tags to avoid width issues
|
|
36
|
+
mouse: false,
|
|
37
|
+
keys: false,
|
|
38
|
+
focusable: false,
|
|
39
|
+
clickable: false,
|
|
40
|
+
});
|
|
41
|
+
// 2. Input textbox (only focusable + only writable)
|
|
42
|
+
const inputTextbox = blessedLib.textbox({
|
|
43
|
+
top,
|
|
44
|
+
left: left + PROMPT_WIDTH + GAP,
|
|
45
|
+
width: inputWidth,
|
|
46
|
+
height: 1,
|
|
47
|
+
inputOnFocus: true,
|
|
48
|
+
keys: true,
|
|
49
|
+
mouse: true,
|
|
50
|
+
censor: false,
|
|
51
|
+
padding: {
|
|
52
|
+
left: 0,
|
|
53
|
+
right: 0,
|
|
54
|
+
top: 0,
|
|
55
|
+
bottom: 0,
|
|
56
|
+
},
|
|
57
|
+
style: {
|
|
58
|
+
fg: 'white',
|
|
59
|
+
focus: {
|
|
60
|
+
fg: 'white',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// 3. Status strip (read-only, non-focusable)
|
|
65
|
+
const statusStrip = blessedLib.box({
|
|
66
|
+
top,
|
|
67
|
+
left: left + PROMPT_WIDTH + GAP + inputWidth + GAP,
|
|
68
|
+
width: statusWidth,
|
|
69
|
+
height: 1,
|
|
70
|
+
content: 'MODE: LOCAL | CONNECT: ... | POSTURE: ...',
|
|
71
|
+
tags: false,
|
|
72
|
+
mouse: false,
|
|
73
|
+
keys: false,
|
|
74
|
+
focusable: false,
|
|
75
|
+
clickable: false,
|
|
76
|
+
});
|
|
77
|
+
// Append to screen
|
|
78
|
+
screen.append(labelBox);
|
|
79
|
+
screen.append(inputTextbox);
|
|
80
|
+
screen.append(statusStrip);
|
|
81
|
+
return {
|
|
82
|
+
labelBox,
|
|
83
|
+
inputTextbox,
|
|
84
|
+
statusStrip,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Focus command input (without creating refocus loops)
|
|
89
|
+
*
|
|
90
|
+
* Rules:
|
|
91
|
+
* - Call only: once on startup, after submit, after command handling
|
|
92
|
+
* - Do NOT call inside: screen.on("render"), periodic loops, panel renders
|
|
93
|
+
*/
|
|
94
|
+
export function focusCommandInput(widgets, reason) {
|
|
95
|
+
// Use setImmediate to ensure focus happens after current render cycle
|
|
96
|
+
setImmediate(() => {
|
|
97
|
+
try {
|
|
98
|
+
widgets.inputTextbox.focus();
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
// Ignore focus errors (widget might be destroyed)
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Attach command line handlers (deduplicated)
|
|
107
|
+
*
|
|
108
|
+
* Rules:
|
|
109
|
+
* - Attach exactly once (guarded)
|
|
110
|
+
* - Use textbox events only for typing + Enter submission
|
|
111
|
+
* - Use screen keys only for global exit shortcuts (q, Ctrl+C)
|
|
112
|
+
*/
|
|
113
|
+
export function attachCommandLineHandlers(screen, widgets, onSubmit, onCancel) {
|
|
114
|
+
// Guard: ensure handlers attach exactly once
|
|
115
|
+
if (commandHandlersAttached) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
commandHandlersAttached = true;
|
|
119
|
+
const { inputTextbox } = widgets;
|
|
120
|
+
// Single submit handler (textbox.on("submit"))
|
|
121
|
+
inputTextbox.on('submit', (value) => {
|
|
122
|
+
const trimmed = value.trim();
|
|
123
|
+
if (trimmed) {
|
|
124
|
+
onSubmit(trimmed);
|
|
125
|
+
}
|
|
126
|
+
// Clear input
|
|
127
|
+
inputTextbox.clearValue();
|
|
128
|
+
// Refocus (after submit)
|
|
129
|
+
focusCommandInput(widgets, 'submit');
|
|
130
|
+
});
|
|
131
|
+
// Optional cancel handler
|
|
132
|
+
if (onCancel) {
|
|
133
|
+
inputTextbox.on('cancel', () => {
|
|
134
|
+
inputTextbox.clearValue();
|
|
135
|
+
onCancel();
|
|
136
|
+
focusCommandInput(widgets, 'cancel');
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// Handle Escape key to clear input
|
|
140
|
+
inputTextbox.key('escape', () => {
|
|
141
|
+
inputTextbox.clearValue();
|
|
142
|
+
focusCommandInput(widgets, 'escape');
|
|
143
|
+
});
|
|
144
|
+
// Do NOT attach screen-level keypress handlers for typing
|
|
145
|
+
// The textbox handles all typing automatically
|
|
146
|
+
// Only use screen keys for global shortcuts (already handled in Section 0)
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Update status strip content
|
|
150
|
+
*/
|
|
151
|
+
export function updateStatusStrip(widgets, content) {
|
|
152
|
+
widgets.statusStrip.setContent(content);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Reset handler attachment guard (for testing)
|
|
156
|
+
*/
|
|
157
|
+
export function resetCommandLineHandlers() {
|
|
158
|
+
commandHandlersAttached = false;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=commandLine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandLine.js","sourceRoot":"","sources":["../../../../../src/ui/v3/section1/runtime/commandLine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 1 - Focus Lock
|
|
3
|
+
*
|
|
4
|
+
* Ensures focus never leaves the command input
|
|
5
|
+
* - Panels are non-focusable
|
|
6
|
+
* - Clicking panels refocuses input
|
|
7
|
+
* - Tab navigation is disabled or overridden
|
|
8
|
+
*/
|
|
9
|
+
import type { Widgets } from 'neo-blessed';
|
|
10
|
+
/**
|
|
11
|
+
* Apply focus lock to a panel widget
|
|
12
|
+
*
|
|
13
|
+
* Makes the panel non-focusable and redirects clicks to command input
|
|
14
|
+
*/
|
|
15
|
+
export declare function lockPanelFocus(panel: Widgets.Box, refocusInput: () => void): void;
|
|
16
|
+
/**
|
|
17
|
+
* Lock focus on all panels
|
|
18
|
+
*/
|
|
19
|
+
export declare function lockAllPanelsFocus(panels: Widgets.Box[], refocusInput: () => void): void;
|
|
20
|
+
/**
|
|
21
|
+
* Override Tab key to refocus input instead of moving focus
|
|
22
|
+
*/
|
|
23
|
+
export declare function overrideTabNavigation(screen: Widgets.Screen, refocusInput: () => void): void;
|
|
24
|
+
//# sourceMappingURL=focusLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusLock.d.ts","sourceRoot":"","sources":["../../../../../src/ui/v3/section1/runtime/focusLock.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,CAAC,GAAG,EAClB,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAcN;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,EACrB,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAIN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,OAAO,CAAC,MAAM,EACtB,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAIN"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section 1 - Focus Lock
|
|
3
|
+
*
|
|
4
|
+
* Ensures focus never leaves the command input
|
|
5
|
+
* - Panels are non-focusable
|
|
6
|
+
* - Clicking panels refocuses input
|
|
7
|
+
* - Tab navigation is disabled or overridden
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Apply focus lock to a panel widget
|
|
11
|
+
*
|
|
12
|
+
* Makes the panel non-focusable and redirects clicks to command input
|
|
13
|
+
*/
|
|
14
|
+
export function lockPanelFocus(panel, refocusInput) {
|
|
15
|
+
// Make panel non-focusable
|
|
16
|
+
panel.focusable = false;
|
|
17
|
+
panel.keyable = false;
|
|
18
|
+
panel.mouse = false;
|
|
19
|
+
panel.clickable = false;
|
|
20
|
+
panel.keys = false;
|
|
21
|
+
panel.input = false;
|
|
22
|
+
panel.inputOnFocus = false;
|
|
23
|
+
// If mouse is needed for future drilldowns, redirect clicks to refocus input
|
|
24
|
+
panel.on('click', () => {
|
|
25
|
+
refocusInput();
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Lock focus on all panels
|
|
30
|
+
*/
|
|
31
|
+
export function lockAllPanelsFocus(panels, refocusInput) {
|
|
32
|
+
for (const panel of panels) {
|
|
33
|
+
lockPanelFocus(panel, refocusInput);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Override Tab key to refocus input instead of moving focus
|
|
38
|
+
*/
|
|
39
|
+
export function overrideTabNavigation(screen, refocusInput) {
|
|
40
|
+
screen.key('tab', () => {
|
|
41
|
+
refocusInput();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=focusLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focusLock.js","sourceRoot":"","sources":["../../../../../src/ui/v3/section1/runtime/focusLock.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAkB,EAClB,YAAwB;IAExB,2BAA2B;IAC1B,KAAa,CAAC,SAAS,GAAG,KAAK,CAAC;IAChC,KAAa,CAAC,OAAO,GAAG,KAAK,CAAC;IAC9B,KAAa,CAAC,KAAK,GAAG,KAAK,CAAC;IAC5B,KAAa,CAAC,SAAS,GAAG,KAAK,CAAC;IAChC,KAAa,CAAC,IAAI,GAAG,KAAK,CAAC;IAC3B,KAAa,CAAC,KAAK,GAAG,KAAK,CAAC;IAC5B,KAAa,CAAC,YAAY,GAAG,KAAK,CAAC;IAEpC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACrB,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,YAAwB;IAExB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,cAAc,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAsB,EACtB,YAAwB;IAExB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE;QACrB,YAAY,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC"}
|