startall 0.0.10 → 0.0.11

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 (2) hide show
  1. package/index.js +153 -33
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -357,6 +357,10 @@ class ProcessManager {
357
357
  this.splitMode = false; // Whether waiting for split command after Ctrl+b
358
358
  this.showSplitMenu = false; // Whether to show the command palette
359
359
  this.splitMenuIndex = 0; // Selected item in split menu
360
+ this.paneScrollPositions = new Map(); // Store scroll positions per pane ID
361
+ this.paneScrollBoxes = new Map(); // Store ScrollBox references per pane ID
362
+ this.paneFilterState = new Map(); // Track filter state per pane to detect changes
363
+ this.paneLineCount = new Map(); // Track how many lines we've rendered per pane
360
364
 
361
365
  // Assign colors to each script
362
366
  this.processColors = new Map();
@@ -1811,9 +1815,94 @@ class ProcessManager {
1811
1815
  }
1812
1816
 
1813
1817
  updateRunningUI() {
1814
- // Just rebuild the entire UI - simpler and more reliable
1815
- // OpenTUI doesn't have great incremental update support anyway
1816
- this.buildRunningUI();
1818
+ // Update existing panes instead of rebuilding everything
1819
+ if (this.paneScrollBoxes.size > 0) {
1820
+ // Update each pane's content
1821
+ for (const [paneId, scrollBox] of this.paneScrollBoxes.entries()) {
1822
+ const pane = findPaneById(this.paneRoot, paneId);
1823
+ if (pane && scrollBox && scrollBox.content) {
1824
+ // Check if filter state changed or if paused (requires rebuild)
1825
+ const currentFilterState = JSON.stringify({
1826
+ filter: pane.filter || '',
1827
+ hidden: pane.hidden || [],
1828
+ processes: pane.processes || [],
1829
+ colorFilter: pane.colorFilter || null,
1830
+ });
1831
+ const previousFilterState = this.paneFilterState.get(paneId);
1832
+ const filterChanged = currentFilterState !== previousFilterState;
1833
+ const needsRebuild = filterChanged || this.isPaused;
1834
+
1835
+ if (needsRebuild) {
1836
+ // Filter changed - need to rebuild all content
1837
+ this.paneFilterState.set(paneId, currentFilterState);
1838
+
1839
+ // Remove all children
1840
+ if (scrollBox.content.children) {
1841
+ while (scrollBox.content.children.length > 0) {
1842
+ const child = scrollBox.content.children[0];
1843
+ if (child && child.id) {
1844
+ scrollBox.content.remove(child.id);
1845
+ } else {
1846
+ break;
1847
+ }
1848
+ }
1849
+ }
1850
+
1851
+ // Rebuild all content
1852
+ const height = scrollBox.height || this.renderer.height - 6;
1853
+ this.buildPaneOutput(pane, scrollBox.content, height);
1854
+
1855
+ // Update line count after rebuild
1856
+ const lines = this.getOutputLinesForPane(pane);
1857
+ this.paneLineCount.set(paneId, lines.length);
1858
+
1859
+ // Auto-scroll to bottom after filter change
1860
+ if (!this.isPaused) {
1861
+ scrollBox.scrollTo({ x: 0, y: Number.MAX_SAFE_INTEGER });
1862
+ }
1863
+ } else {
1864
+ // No filter change - just append new lines
1865
+ const lines = this.getOutputLinesForPane(pane);
1866
+ const lastRenderedCount = this.paneLineCount.get(paneId) || 0;
1867
+
1868
+ if (lines.length > lastRenderedCount) {
1869
+ const newLines = lines.slice(lastRenderedCount);
1870
+
1871
+ for (let i = 0; i < newLines.length; i++) {
1872
+ const line = newLines[i];
1873
+ const lineIndex = lastRenderedCount + i;
1874
+ const processColor = this.processColors.get(line.process) || COLORS.text;
1875
+ const trimmedText = line.text.trim();
1876
+
1877
+ const outputLine = new TextRenderable(this.renderer, {
1878
+ id: `output-${pane.id}-${lineIndex}`,
1879
+ content: t`${fg(processColor)(`[${line.process}]`)} ${trimmedText}`,
1880
+ bg: '#000000',
1881
+ });
1882
+
1883
+ scrollBox.content.add(outputLine);
1884
+ }
1885
+
1886
+ // Update line count
1887
+ this.paneLineCount.set(paneId, lines.length);
1888
+
1889
+ // Auto-scroll to bottom if not paused
1890
+ if (!this.isPaused) {
1891
+ scrollBox.scrollTo({ x: 0, y: Number.MAX_SAFE_INTEGER });
1892
+ }
1893
+ }
1894
+ }
1895
+
1896
+ // Update scrollbar visibility based on pause state
1897
+ if (scrollBox.verticalScrollBar) {
1898
+ scrollBox.verticalScrollBar.width = this.isPaused ? 1 : 0;
1899
+ }
1900
+ }
1901
+ }
1902
+ } else {
1903
+ // First time or no panes exist - do full rebuild
1904
+ this.buildRunningUI();
1905
+ }
1817
1906
  }
1818
1907
 
1819
1908
  // Build a single pane's output area
@@ -1821,36 +1910,24 @@ class ProcessManager {
1821
1910
  const isFocused = pane.id === this.focusedPaneId;
1822
1911
  const lines = this.getOutputLinesForPane(pane);
1823
1912
 
1824
- // Calculate visible lines - use global pause state
1825
- const outputHeight = Math.max(3, height - 2);
1826
- let linesToShow = this.isPaused ? lines : lines.slice(-outputHeight);
1913
+ // Don't slice - show all lines and let ScrollBox handle scrolling
1914
+ const linesToShow = lines;
1827
1915
 
1828
- // Add lines in reverse order (newest first)
1829
- for (let i = linesToShow.length - 1; i >= 0; i--) {
1916
+ // Add lines (oldest first, so newest is at bottom)
1917
+ for (let i = 0; i < linesToShow.length; i++) {
1830
1918
  const line = linesToShow[i];
1831
1919
  const processColor = this.processColors.get(line.process) || COLORS.text;
1832
1920
 
1833
- // No truncation - let OpenTUI handle text wrapping naturally
1921
+ // Trim whitespace and let text wrap naturally - ScrollBox will handle overflow
1922
+ const trimmedText = line.text.trim();
1834
1923
  const outputLine = new TextRenderable(this.renderer, {
1835
1924
  id: `output-${pane.id}-${i}`,
1836
- content: t`${fg(processColor)(`[${line.process}]`)} ${line.text}`,
1925
+ content: t`${fg(processColor)(`[${line.process}]`)} ${trimmedText}`,
1837
1926
  bg: '#000000', // Black background for pane content
1838
1927
  });
1839
1928
 
1840
1929
  container.add(outputLine);
1841
1930
  }
1842
-
1843
- // Fill remaining vertical space with blank lines
1844
- const emptyLinesNeeded = Math.max(0, outputHeight - linesToShow.length);
1845
- for (let j = 0; j < emptyLinesNeeded; j++) {
1846
- const emptyLine = new TextRenderable(this.renderer, {
1847
- id: `empty-${pane.id}-${j}`,
1848
- content: ' ',
1849
- bg: '#000000', // Black background for empty lines
1850
- });
1851
-
1852
- container.add(emptyLine);
1853
- }
1854
1931
  }
1855
1932
 
1856
1933
  // Count how many vertical panes exist (for width calculation)
@@ -1900,22 +1977,54 @@ class ProcessManager {
1900
1977
  backgroundColor: '#000000', // Black background for pane container
1901
1978
  });
1902
1979
 
1903
- // Output content - use BoxRenderable that fills remaining space
1980
+ // Output content - use ScrollBox to handle text wrapping properly
1904
1981
  // Use passed height or calculate default for line count calculation
1905
1982
  const height = availableHeight ? Math.max(5, availableHeight - 2) : Math.max(5, this.renderer.height - 6);
1906
1983
 
1907
- const outputBox = new BoxRenderable(this.renderer, {
1984
+ const outputBox = new ScrollBoxRenderable(this.renderer, {
1908
1985
  id: `pane-output-${pane.id}`,
1909
- flexDirection: 'column',
1910
- flexGrow: 1,
1911
- flexShrink: 1,
1912
- flexBasis: 0,
1913
- overflow: 'hidden',
1914
- paddingLeft: 1,
1915
- backgroundColor: '#000000', // Black background for pane
1986
+ height: height,
1987
+ scrollX: false, // Disable horizontal scrollbar entirely
1988
+ scrollY: true, // Enable vertical scrolling
1989
+ focusable: true, // Enable mouse interactions and keyboard scrolling
1990
+ style: {
1991
+ rootOptions: {
1992
+ flexGrow: 1,
1993
+ flexShrink: 1,
1994
+ flexBasis: 0,
1995
+ paddingLeft: 1,
1996
+ backgroundColor: '#000000',
1997
+ },
1998
+ contentOptions: {
1999
+ backgroundColor: '#000000',
2000
+ width: '100%', // Fill container width for proper text wrapping
2001
+ },
2002
+ },
1916
2003
  });
1917
2004
 
1918
- this.buildPaneOutput(pane, outputBox, height);
2005
+ // Show scrollbar when paused, hide when not paused
2006
+ if (outputBox.verticalScrollBar) {
2007
+ outputBox.verticalScrollBar.width = this.isPaused ? 1 : 0;
2008
+ }
2009
+
2010
+ // Store ScrollBox reference for this pane
2011
+ this.paneScrollBoxes.set(pane.id, outputBox);
2012
+
2013
+ this.buildPaneOutput(pane, outputBox.content, height);
2014
+
2015
+ // Restore or set scroll position
2016
+ setTimeout(() => {
2017
+ if (!outputBox || !outputBox.scrollTo) return;
2018
+
2019
+ if (this.isPaused && this.paneScrollPositions.has(pane.id)) {
2020
+ // Restore saved scroll position when paused
2021
+ const savedPos = this.paneScrollPositions.get(pane.id);
2022
+ outputBox.scrollTo(savedPos);
2023
+ } else if (!this.isPaused) {
2024
+ // Auto-scroll to bottom when not paused
2025
+ outputBox.scrollTo({ x: 0, y: Number.MAX_SAFE_INTEGER });
2026
+ }
2027
+ }, 0);
1919
2028
 
1920
2029
  paneContainer.add(outputBox);
1921
2030
  return paneContainer;
@@ -2011,6 +2120,16 @@ class ProcessManager {
2011
2120
  }
2012
2121
 
2013
2122
  buildRunningUI() {
2123
+ // Save scroll positions before destroying
2124
+ for (const [paneId, scrollBox] of this.paneScrollBoxes.entries()) {
2125
+ if (scrollBox) {
2126
+ this.paneScrollPositions.set(paneId, {
2127
+ x: scrollBox.scrollLeft || 0,
2128
+ y: scrollBox.scrollTop || 0,
2129
+ });
2130
+ }
2131
+ }
2132
+
2014
2133
  // Remove old containers if they exist - use destroyRecursively to clean up all children
2015
2134
  if (this.selectionContainer) {
2016
2135
  this.renderer.root.remove(this.selectionContainer);
@@ -2029,8 +2148,9 @@ class ProcessManager {
2029
2148
  this.runningContainer.destroyRecursively();
2030
2149
  this.runningContainer = null;
2031
2150
  }
2032
- // Clear outputBox reference since it was destroyed with runningContainer
2151
+ // Clear outputBox reference and scrollbox map since they were destroyed
2033
2152
  this.outputBox = null;
2153
+ this.paneScrollBoxes.clear();
2034
2154
 
2035
2155
  // Create main container - full screen with black background
2036
2156
  const mainContainer = new BoxRenderable(this.renderer, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "startall",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {