startall 0.0.22 → 0.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -0
  2. package/index.js +129 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -43,6 +43,7 @@ Traditional solutions fall short:
43
43
  - Per-process visibility toggles (`Space` or `1-9`)
44
44
  - Per-pane filters (different views in each pane)
45
45
  - **Custom pane naming**: Label panes for easier identification (`n`)
46
+ - **Column view**: Instant one-pane-per-script layout with `=` key (toggle back to restore your layout)
46
47
  - **Persistent layouts**: Your pane configuration is saved between sessions
47
48
  - **Process-specific views**: Show/hide specific processes in each pane
48
49
  - **Colored output**: Each process gets unique color-coded output
@@ -122,6 +123,7 @@ That's it! The TUI will:
122
123
  - `\` - Open command palette
123
124
  - `|` - Split pane vertically (left/right)
124
125
  - `_` - Split pane horizontally (top/bottom)
126
+ - `=` - Toggle column view (one column per script, auto-generated)
125
127
  - `x` - Close current pane (if >1 pane exists)
126
128
  - `Tab` - Next pane
127
129
  - `Shift+Tab` - Previous pane
package/index.js CHANGED
@@ -429,6 +429,15 @@ function gitPush() {
429
429
  }
430
430
  }
431
431
 
432
+ function gitPull() {
433
+ try {
434
+ const output = execSync('git pull', { stdio: 'pipe', windowsHide: true, timeout: 30000 }).toString().trim();
435
+ return { success: true, output: output || 'Pulled successfully' };
436
+ } catch (err) {
437
+ return { success: false, error: err.stderr?.toString() || err.message };
438
+ }
439
+ }
440
+
432
441
  // Detect git repo once at startup
433
442
  const IS_GIT_REPO = isGitRepo();
434
443
 
@@ -606,6 +615,10 @@ class ProcessManager {
606
615
  this.paneLineCount = new Map(); // Track how many lines we've rendered per pane
607
616
  this.uiJustRebuilt = false; // Flag to skip redundant render after buildRunningUI
608
617
 
618
+ // Column view state (one pane per script, side by side)
619
+ this.isColumnView = false; // Whether column view is active
620
+ this.savedPaneRoot = null; // Saved pane tree to restore when toggling back
621
+
609
622
  // Copy mode state (select text to copy)
610
623
  this.isCopyMode = false; // Whether in copy/select mode
611
624
  this.copyModeCursor = 0; // Current cursor line index within visible lines
@@ -842,14 +855,17 @@ class ProcessManager {
842
855
  this.buildRunningUI();
843
856
  } else if (keyName === '|') {
844
857
  // Quick vertical split
858
+ this.exitColumnViewMode();
845
859
  this.splitCurrentPane('vertical');
846
860
  this.buildRunningUI();
847
861
  } else if (keyName === '_') {
848
862
  // Quick horizontal split
863
+ this.exitColumnViewMode();
849
864
  this.splitCurrentPane('horizontal');
850
865
  this.buildRunningUI();
851
866
  } else if (keyName === 'x' && getAllPaneIds(this.paneRoot).length > 1) {
852
867
  // Close current pane (only if more than one)
868
+ this.exitColumnViewMode();
853
869
  this.closeCurrentPane();
854
870
  this.buildRunningUI();
855
871
  } else if (keyName === 'space') {
@@ -985,6 +1001,9 @@ class ProcessManager {
985
1001
  } else if (keyName === 'y') {
986
1002
  // Enter copy mode (select text to copy)
987
1003
  this.enterCopyMode();
1004
+ } else if (keyName === '=') {
1005
+ // Toggle column view (one pane per script)
1006
+ this.toggleColumnView();
988
1007
  } else if (keyName === 'g' && IS_GIT_REPO) {
989
1008
  // Open git modal (commit & push)
990
1009
  this.openGitModal();
@@ -1674,6 +1693,67 @@ class ProcessManager {
1674
1693
  }
1675
1694
  }
1676
1695
 
1696
+ // Generate a column view pane tree: one pane per running script, side by side
1697
+ generateColumnViewPaneTree() {
1698
+ // Get the scripts that are currently selected/running
1699
+ const runningScripts = this.scripts.filter(s => {
1700
+ const proc = this.processes.get(s.name);
1701
+ return proc && (proc.status === 'running' || proc.status === 'crashed' || proc.status === 'exited');
1702
+ });
1703
+
1704
+ if (runningScripts.length === 0) {
1705
+ return createPane([]);
1706
+ }
1707
+
1708
+ if (runningScripts.length === 1) {
1709
+ return createPane([runningScripts[0].name]);
1710
+ }
1711
+
1712
+ // Create a vertical split with one pane per script
1713
+ const panes = runningScripts.map(s => {
1714
+ const pane = createPane([s.name]);
1715
+ pane.name = s.displayName;
1716
+ return pane;
1717
+ });
1718
+
1719
+ return createSplit('vertical', panes);
1720
+ }
1721
+
1722
+ // Toggle between column view (one pane per script) and the normal saved layout
1723
+ toggleColumnView() {
1724
+ if (this.isColumnView) {
1725
+ // Restore the saved pane tree
1726
+ if (this.savedPaneRoot) {
1727
+ this.paneRoot = this.savedPaneRoot;
1728
+ this.savedPaneRoot = null;
1729
+ } else {
1730
+ this.paneRoot = createPane([]);
1731
+ }
1732
+ this.isColumnView = false;
1733
+ } else {
1734
+ // Save current pane tree and switch to column view
1735
+ this.savedPaneRoot = this.paneRoot;
1736
+ this.paneRoot = this.generateColumnViewPaneTree();
1737
+ this.isColumnView = true;
1738
+ }
1739
+
1740
+ // Focus the first pane in the new tree
1741
+ const allPanes = getAllPaneIds(this.paneRoot);
1742
+ if (allPanes.length > 0) {
1743
+ this.focusedPaneId = allPanes[0];
1744
+ }
1745
+
1746
+ this.buildRunningUI();
1747
+ }
1748
+
1749
+ // Exit column view mode without restoring saved layout (e.g. user manually split/closed a pane)
1750
+ exitColumnViewMode() {
1751
+ if (this.isColumnView) {
1752
+ this.isColumnView = false;
1753
+ this.savedPaneRoot = null;
1754
+ }
1755
+ }
1756
+
1677
1757
  // Move the currently selected process to the focused pane
1678
1758
  moveProcessToCurrentPane() {
1679
1759
  const scriptName = this.scripts[this.selectedIndex]?.name;
@@ -1760,6 +1840,8 @@ class ProcessManager {
1760
1840
 
1761
1841
  // Save the current pane layout to config (debounced to avoid excessive disk writes)
1762
1842
  savePaneLayout() {
1843
+ // Don't save the auto-generated column view layout - it's transient
1844
+ if (this.isColumnView) return;
1763
1845
  this.config.paneLayout = serializePaneTree(this.paneRoot);
1764
1846
  debouncedSaveConfig(this.config);
1765
1847
  }
@@ -2592,6 +2674,22 @@ class ProcessManager {
2592
2674
  this.refreshGitStatus();
2593
2675
  this.buildRunningUI();
2594
2676
  }, 10);
2677
+ } else if (keyName === 'l') {
2678
+ // Pull
2679
+ this.gitModalPhase = 'pulling';
2680
+ this.gitModalOutput = ['Pulling...'];
2681
+ this.buildRunningUI();
2682
+ setTimeout(() => {
2683
+ const result = gitPull();
2684
+ if (result.success) {
2685
+ this.gitModalOutput = [result.output];
2686
+ } else {
2687
+ this.gitModalOutput = [`Pull failed: ${result.error}`];
2688
+ }
2689
+ this.gitModalPhase = 'result';
2690
+ this.refreshGitStatus();
2691
+ this.buildRunningUI();
2692
+ }, 10);
2595
2693
  } else if (keyName === 'r') {
2596
2694
  // Refresh status
2597
2695
  this.refreshGitStatus();
@@ -2670,6 +2768,22 @@ class ProcessManager {
2670
2768
  this.refreshGitStatus();
2671
2769
  this.buildRunningUI();
2672
2770
  }, 10);
2771
+ } else if (keyName === 'l') {
2772
+ // Allow pulling from result phase
2773
+ this.gitModalPhase = 'pulling';
2774
+ this.gitModalOutput = ['Pulling...'];
2775
+ this.buildRunningUI();
2776
+ setTimeout(() => {
2777
+ const result = gitPull();
2778
+ if (result.success) {
2779
+ this.gitModalOutput = [result.output];
2780
+ } else {
2781
+ this.gitModalOutput = [`Pull failed: ${result.error}`];
2782
+ }
2783
+ this.gitModalPhase = 'result';
2784
+ this.refreshGitStatus();
2785
+ this.buildRunningUI();
2786
+ }, 10);
2673
2787
  } else {
2674
2788
  this.gitModalPhase = 'status';
2675
2789
  this.refreshGitStatus();
@@ -3762,6 +3876,7 @@ class ProcessManager {
3762
3876
  let titleIcon = '';
3763
3877
  if (this.gitModalPhase === 'committing') titleIcon = '...';
3764
3878
  else if (this.gitModalPhase === 'pushing') titleIcon = '...';
3879
+ else if (this.gitModalPhase === 'pulling') titleIcon = '...';
3765
3880
  else titleIcon = '';
3766
3881
  const title = ` Git: ${branch} ${titleIcon}`;
3767
3882
 
@@ -3851,9 +3966,9 @@ class ProcessManager {
3851
3966
  content: t`${fg(COLORS.textDim)('All changes will be staged and committed.')}`,
3852
3967
  });
3853
3968
  overlay.add(commitHint);
3854
- } else if (this.gitModalPhase === 'committing' || this.gitModalPhase === 'pushing') {
3969
+ } else if (this.gitModalPhase === 'committing' || this.gitModalPhase === 'pushing' || this.gitModalPhase === 'pulling') {
3855
3970
  // Show busy indicator
3856
- const busyText = this.gitModalPhase === 'committing' ? 'Committing...' : 'Pushing...';
3971
+ const busyText = this.gitModalPhase === 'committing' ? 'Committing...' : this.gitModalPhase === 'pushing' ? 'Pushing...' : 'Pulling...';
3857
3972
  const busyLine = new TextRenderable(this.renderer, {
3858
3973
  id: 'git-busy',
3859
3974
  content: t`${fg(COLORS.warning)(busyText)}`,
@@ -4007,7 +4122,7 @@ class ProcessManager {
4007
4122
  });
4008
4123
  hintBar.add(hint);
4009
4124
  });
4010
- } else if (this.gitModalPhase === 'committing' || this.gitModalPhase === 'pushing') {
4125
+ } else if (this.gitModalPhase === 'committing' || this.gitModalPhase === 'pushing' || this.gitModalPhase === 'pulling') {
4011
4126
  const hint = new TextRenderable(this.renderer, {
4012
4127
  id: 'git-hint-busy',
4013
4128
  content: t`${fg(COLORS.warning)('Please wait...')}`,
@@ -4018,6 +4133,7 @@ class ProcessManager {
4018
4133
  { key: 'c', desc: 'commit', color: COLORS.success },
4019
4134
  { key: 'a', desc: 'stage all', color: COLORS.warning },
4020
4135
  { key: 'p', desc: 'push', color: COLORS.cyan },
4136
+ { key: 'l', desc: 'pull', color: COLORS.cyan },
4021
4137
  { key: 'r', desc: 'refresh', color: COLORS.magenta },
4022
4138
  { key: 'esc', desc: 'close', color: COLORS.error },
4023
4139
  ];
@@ -4176,6 +4292,15 @@ class ProcessManager {
4176
4292
  });
4177
4293
  leftSide.add(statusIndicator);
4178
4294
 
4295
+ // Column view indicator
4296
+ if (this.isColumnView) {
4297
+ const columnIndicator = new TextRenderable(this.renderer, {
4298
+ id: 'column-view-indicator',
4299
+ content: t`${fg(COLORS.cyan)('COLUMNS')}`,
4300
+ });
4301
+ leftSide.add(columnIndicator);
4302
+ }
4303
+
4179
4304
  // Git branch indicator
4180
4305
  if (IS_GIT_REPO) {
4181
4306
  const branch = this.gitBranch || getGitBranch();
@@ -4286,6 +4411,7 @@ class ProcessManager {
4286
4411
  // Pane & navigation
4287
4412
  [
4288
4413
  { key: '\\', desc: 'panes', color: COLORS.cyan },
4414
+ { key: '=', desc: this.isColumnView ? 'merged' : 'columns', color: COLORS.cyan },
4289
4415
  { key: '1-9', desc: 'toggle', color: COLORS.success },
4290
4416
  ],
4291
4417
  // Process control
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "startall",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {