startall 0.0.14 → 0.0.16
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/README.md +11 -3
- package/index.js +164 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,10 +56,12 @@ Traditional solutions fall short:
|
|
|
56
56
|
- **Quick process toggle**: Use number keys `1-9` for instant visibility control
|
|
57
57
|
|
|
58
58
|
### 🔧 Advanced Controls
|
|
59
|
-
- **Quick Commands**:
|
|
60
|
-
- Press
|
|
59
|
+
- **Quick Commands**: Run any script on-demand without adding it to the persistent processes
|
|
60
|
+
- Press `e` to open command picker and select any script
|
|
61
|
+
- Or assign keyboard shortcuts for instant access (press assigned key to run)
|
|
61
62
|
- Perfect for build scripts, tests, or any short-running command
|
|
62
|
-
-
|
|
63
|
+
- Output shown in popup overlay with Esc to close
|
|
64
|
+
- Configure shortcuts in settings (`o` → Quick Commands section)
|
|
63
65
|
- **Interactive input mode**: Send commands to running processes via stdin (`i`)
|
|
64
66
|
- Perfect for dev servers that accept commands (Vite, Rust watch, etc.)
|
|
65
67
|
- **Settings panel**: Configure ignore/include patterns, shortcuts, and more (`o`)
|
|
@@ -107,6 +109,7 @@ That's it! The TUI will:
|
|
|
107
109
|
- `s` - Stop/start selected process
|
|
108
110
|
- `r` - Restart selected process
|
|
109
111
|
- `i` - Send input to selected process (interactive mode)
|
|
112
|
+
- `e` - Execute any script (opens command picker)
|
|
110
113
|
- `a-z` - Run assigned quick command (if configured)
|
|
111
114
|
|
|
112
115
|
*Pane Management:*
|
|
@@ -150,6 +153,11 @@ That's it! The TUI will:
|
|
|
150
153
|
- `d` or `Backspace` - Delete pattern or shortcut
|
|
151
154
|
- `Esc` or `q` - Return to previous screen
|
|
152
155
|
|
|
156
|
+
**Run Command Picker:**
|
|
157
|
+
- `↑`/`↓` or `k`/`j` - Navigate scripts
|
|
158
|
+
- `Enter` - Run selected script
|
|
159
|
+
- `Esc` or `q` - Close picker
|
|
160
|
+
|
|
153
161
|
**Quick Commands Overlay:**
|
|
154
162
|
- `Esc` - Close overlay and stop command (if running)
|
|
155
163
|
|
package/index.js
CHANGED
|
@@ -379,6 +379,10 @@ class ProcessManager {
|
|
|
379
379
|
this.commandOverlayScript = ''; // Script name being executed
|
|
380
380
|
this.commandOverlayStatus = 'running'; // 'running' | 'exited' | 'crashed'
|
|
381
381
|
this.commandOverlayProcess = null; // Process reference
|
|
382
|
+
|
|
383
|
+
// Run command modal state
|
|
384
|
+
this.showRunCommandModal = false; // Whether the run command picker is visible
|
|
385
|
+
this.runCommandModalIndex = 0; // Selected index in the modal
|
|
382
386
|
this.outputBox = null; // Reference to the output container
|
|
383
387
|
this.destroyed = false; // Flag to prevent operations after cleanup
|
|
384
388
|
this.lastRenderedLineCount = 0; // Track how many lines we've rendered
|
|
@@ -494,6 +498,12 @@ class ProcessManager {
|
|
|
494
498
|
return;
|
|
495
499
|
}
|
|
496
500
|
|
|
501
|
+
// Handle run command modal
|
|
502
|
+
if (this.showRunCommandModal) {
|
|
503
|
+
this.handleRunCommandModalInput(keyName, keyEvent);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
497
507
|
// Handle split menu
|
|
498
508
|
if (this.showSplitMenu) {
|
|
499
509
|
this.handleSplitMenuInput(keyName, keyEvent);
|
|
@@ -708,6 +718,11 @@ class ProcessManager {
|
|
|
708
718
|
this.inputModeText = '';
|
|
709
719
|
this.buildRunningUI();
|
|
710
720
|
}
|
|
721
|
+
} else if (keyName === 'e') {
|
|
722
|
+
// Open run command modal
|
|
723
|
+
this.showRunCommandModal = true;
|
|
724
|
+
this.runCommandModalIndex = 0;
|
|
725
|
+
this.buildRunningUI();
|
|
711
726
|
} else if (keyName && keyName.length === 1 && !keyEvent.ctrl && !keyEvent.meta && !keyEvent.shift) {
|
|
712
727
|
// Check if this key is a custom shortcut
|
|
713
728
|
const shortcuts = this.config.shortcuts || {};
|
|
@@ -1266,6 +1281,28 @@ class ProcessManager {
|
|
|
1266
1281
|
}
|
|
1267
1282
|
}
|
|
1268
1283
|
|
|
1284
|
+
handleRunCommandModalInput(keyName, keyEvent) {
|
|
1285
|
+
if (keyName === 'escape' || keyName === 'q') {
|
|
1286
|
+
this.showRunCommandModal = false;
|
|
1287
|
+
this.buildRunningUI();
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (keyName === 'up' || keyName === 'k') {
|
|
1292
|
+
this.runCommandModalIndex = Math.max(0, this.runCommandModalIndex - 1);
|
|
1293
|
+
this.buildRunningUI();
|
|
1294
|
+
} else if (keyName === 'down' || keyName === 'j') {
|
|
1295
|
+
this.runCommandModalIndex = Math.min(this.allScripts.length - 1, this.runCommandModalIndex + 1);
|
|
1296
|
+
this.buildRunningUI();
|
|
1297
|
+
} else if (keyName === 'enter' || keyName === 'return') {
|
|
1298
|
+
const selectedScript = this.allScripts[this.runCommandModalIndex];
|
|
1299
|
+
if (selectedScript) {
|
|
1300
|
+
this.showRunCommandModal = false;
|
|
1301
|
+
this.executeCommand(selectedScript.name);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1269
1306
|
getSplitMenuItems() {
|
|
1270
1307
|
const allPanes = getAllPaneIds(this.paneRoot);
|
|
1271
1308
|
const items = [
|
|
@@ -2674,6 +2711,114 @@ class ProcessManager {
|
|
|
2674
2711
|
parent.add(overlay);
|
|
2675
2712
|
}
|
|
2676
2713
|
|
|
2714
|
+
// Build run command picker modal
|
|
2715
|
+
buildRunCommandModal(parent) {
|
|
2716
|
+
// Create centered overlay
|
|
2717
|
+
const overlay = new BoxRenderable(this.renderer, {
|
|
2718
|
+
id: 'run-command-modal',
|
|
2719
|
+
position: 'absolute',
|
|
2720
|
+
top: '20%',
|
|
2721
|
+
left: '25%',
|
|
2722
|
+
width: '50%',
|
|
2723
|
+
height: '60%',
|
|
2724
|
+
backgroundColor: COLORS.bgLight,
|
|
2725
|
+
border: true,
|
|
2726
|
+
borderStyle: 'rounded',
|
|
2727
|
+
borderColor: COLORS.accent,
|
|
2728
|
+
title: ' Run Command ',
|
|
2729
|
+
padding: 1,
|
|
2730
|
+
flexDirection: 'column',
|
|
2731
|
+
});
|
|
2732
|
+
|
|
2733
|
+
// Scrollable list of scripts
|
|
2734
|
+
const listBox = new ScrollBoxRenderable(this.renderer, {
|
|
2735
|
+
id: 'run-command-list',
|
|
2736
|
+
height: Math.floor(this.renderer.height * 0.6) - 4,
|
|
2737
|
+
scrollX: false,
|
|
2738
|
+
scrollY: true,
|
|
2739
|
+
focusable: true,
|
|
2740
|
+
style: {
|
|
2741
|
+
rootOptions: {
|
|
2742
|
+
flexGrow: 1,
|
|
2743
|
+
backgroundColor: COLORS.bgLight,
|
|
2744
|
+
},
|
|
2745
|
+
contentOptions: {
|
|
2746
|
+
backgroundColor: COLORS.bgLight,
|
|
2747
|
+
width: '100%',
|
|
2748
|
+
},
|
|
2749
|
+
},
|
|
2750
|
+
});
|
|
2751
|
+
|
|
2752
|
+
this.allScripts.forEach((script, idx) => {
|
|
2753
|
+
const isFocused = idx === this.runCommandModalIndex;
|
|
2754
|
+
const indicator = isFocused ? '>' : ' ';
|
|
2755
|
+
const bgColor = isFocused ? COLORS.bgHighlight : null;
|
|
2756
|
+
const processColor = this.processColors.get(script.name) || COLORS.text;
|
|
2757
|
+
|
|
2758
|
+
// Check if this script has a shortcut
|
|
2759
|
+
const shortcuts = this.config.shortcuts || {};
|
|
2760
|
+
let shortcutKey = null;
|
|
2761
|
+
for (const [key, scriptName] of Object.entries(shortcuts)) {
|
|
2762
|
+
if (scriptName === script.name) {
|
|
2763
|
+
shortcutKey = key;
|
|
2764
|
+
break;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
const itemContainer = new BoxRenderable(this.renderer, {
|
|
2769
|
+
id: `run-cmd-item-${idx}`,
|
|
2770
|
+
backgroundColor: bgColor,
|
|
2771
|
+
paddingLeft: 1,
|
|
2772
|
+
});
|
|
2773
|
+
|
|
2774
|
+
let content;
|
|
2775
|
+
if (shortcutKey) {
|
|
2776
|
+
content = t`${fg(isFocused ? COLORS.accent : COLORS.textDim)(indicator)} ${fg(processColor)(script.displayName)} ${fg(COLORS.textDim)(`(${shortcutKey})`)}`;
|
|
2777
|
+
} else {
|
|
2778
|
+
content = t`${fg(isFocused ? COLORS.accent : COLORS.textDim)(indicator)} ${fg(processColor)(script.displayName)}`;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
const itemText = new TextRenderable(this.renderer, {
|
|
2782
|
+
id: `run-cmd-text-${idx}`,
|
|
2783
|
+
content: content,
|
|
2784
|
+
});
|
|
2785
|
+
|
|
2786
|
+
itemContainer.add(itemText);
|
|
2787
|
+
listBox.content.add(itemContainer);
|
|
2788
|
+
});
|
|
2789
|
+
|
|
2790
|
+
// Auto-scroll to focused item
|
|
2791
|
+
if (listBox.scrollTo) {
|
|
2792
|
+
const lineHeight = 1;
|
|
2793
|
+
const viewportHeight = Math.floor(this.renderer.height * 0.6) - 4;
|
|
2794
|
+
const focusedY = this.runCommandModalIndex * lineHeight;
|
|
2795
|
+
if (focusedY < listBox.scrollTop || focusedY >= listBox.scrollTop + viewportHeight) {
|
|
2796
|
+
listBox.scrollTo({ x: 0, y: Math.max(0, focusedY - Math.floor(viewportHeight / 2)) });
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
overlay.add(listBox);
|
|
2801
|
+
|
|
2802
|
+
// Footer hint
|
|
2803
|
+
const hintBar = new BoxRenderable(this.renderer, {
|
|
2804
|
+
id: 'run-cmd-hint-bar',
|
|
2805
|
+
border: ['top'],
|
|
2806
|
+
borderStyle: 'single',
|
|
2807
|
+
borderColor: COLORS.border,
|
|
2808
|
+
paddingTop: 1,
|
|
2809
|
+
paddingLeft: 1,
|
|
2810
|
+
});
|
|
2811
|
+
|
|
2812
|
+
const hint = new TextRenderable(this.renderer, {
|
|
2813
|
+
id: 'run-cmd-hint',
|
|
2814
|
+
content: t`${fg(COLORS.textDim)('↑/↓ navigate')} ${fg(COLORS.accent)('Enter')} ${fg(COLORS.textDim)('run')} ${fg(COLORS.accent)('Esc')} ${fg(COLORS.textDim)('close')}`,
|
|
2815
|
+
});
|
|
2816
|
+
hintBar.add(hint);
|
|
2817
|
+
overlay.add(hintBar);
|
|
2818
|
+
|
|
2819
|
+
parent.add(overlay);
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2677
2822
|
buildRunningUI() {
|
|
2678
2823
|
// Save scroll positions before destroying
|
|
2679
2824
|
for (const [paneId, scrollBox] of this.paneScrollBoxes.entries()) {
|
|
@@ -2866,6 +3011,7 @@ class ProcessManager {
|
|
|
2866
3011
|
|
|
2867
3012
|
const shortcuts = [
|
|
2868
3013
|
{ key: '\\', desc: 'panes', color: COLORS.cyan },
|
|
3014
|
+
{ key: 'e', desc: 'execute', color: COLORS.warning },
|
|
2869
3015
|
{ key: '1-9', desc: 'toggle', color: COLORS.success },
|
|
2870
3016
|
{ key: 'i', desc: 'input', color: COLORS.success },
|
|
2871
3017
|
{ key: 'n', desc: 'name', color: COLORS.accent },
|
|
@@ -2878,6 +3024,19 @@ class ProcessManager {
|
|
|
2878
3024
|
{ key: 'q', desc: 'quit', color: COLORS.error },
|
|
2879
3025
|
];
|
|
2880
3026
|
|
|
3027
|
+
// Add configured quick command shortcuts
|
|
3028
|
+
const configShortcuts = this.config.shortcuts || {};
|
|
3029
|
+
for (const [key, scriptName] of Object.entries(configShortcuts)) {
|
|
3030
|
+
// Show first 3 shortcuts to avoid cluttering footer
|
|
3031
|
+
if (Object.keys(configShortcuts).length <= 3 || shortcuts.length < 15) {
|
|
3032
|
+
const script = this.allScripts.find(s => s.name === scriptName);
|
|
3033
|
+
if (script) {
|
|
3034
|
+
const shortDesc = script.displayName.length > 8 ? script.displayName.substring(0, 6) + '..' : script.displayName;
|
|
3035
|
+
shortcuts.splice(2, 0, { key, desc: shortDesc, color: this.processColors.get(script.name) || COLORS.text });
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
|
|
2881
3040
|
shortcuts.forEach(({ key, desc, color }) => {
|
|
2882
3041
|
const shortcut = new TextRenderable(this.renderer, {
|
|
2883
3042
|
id: `shortcut-${key}`,
|
|
@@ -2901,6 +3060,11 @@ class ProcessManager {
|
|
|
2901
3060
|
this.buildSplitMenuOverlay(mainContainer);
|
|
2902
3061
|
}
|
|
2903
3062
|
|
|
3063
|
+
// Add run command modal if active
|
|
3064
|
+
if (this.showRunCommandModal) {
|
|
3065
|
+
this.buildRunCommandModal(mainContainer);
|
|
3066
|
+
}
|
|
3067
|
+
|
|
2904
3068
|
// Add command output overlay if active
|
|
2905
3069
|
if (this.showCommandOverlay) {
|
|
2906
3070
|
this.buildCommandOverlay(mainContainer);
|