startall 0.0.1 → 0.0.2

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 +11 -1
  2. package/index.js +49 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -114,7 +114,17 @@ Existing tools either:
114
114
  - Built with [OpenTUI](https://github.com/openmux/opentui) for a modern terminal UI
115
115
  - Uses standard Node.js `child_process` (no PTY required = Windows support)
116
116
  - Parses `package.json` scripts automatically
117
- - Saves configuration in `startall.json` with structure: `{ defaultSelection: [], ignore: [] }`
117
+ - Saves configuration in `startall.json`:
118
+ ```json
119
+ {
120
+ "defaultSelection": ["frontend", "backend"],
121
+ "include": ["dev:*"],
122
+ "ignore": ["*:test"]
123
+ }
124
+ ```
125
+ - `include` (optional): if defined, only scripts matching these patterns are shown
126
+ - `ignore`: scripts matching these patterns are hidden
127
+ - Both support wildcards (`*`)
118
128
 
119
129
  ## Roadmap
120
130
 
package/index.js CHANGED
@@ -1,10 +1,11 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env bun
2
2
 
3
- import { createCliRenderer, TextRenderable, BoxRenderable, ScrollBoxRenderable, t, fg } from '@opentui/core';
4
- import { spawn } from 'child_process';
5
- import { readFileSync, writeFileSync, existsSync } from 'fs';
6
- import { join } from 'path';
7
- import kill from 'tree-kill';
3
+ import { createCliRenderer, TextRenderable, BoxRenderable, ScrollBoxRenderable, t, fg } from '@opentui/core';
4
+ import { spawn } from 'child_process';
5
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
6
+ import { join } from 'path';
7
+ import kill from 'tree-kill';
8
+ import stripAnsi from 'strip-ansi';
8
9
 
9
10
  // Configuration
10
11
  const CONFIG_FILE = process.argv[2] || 'startall.json';
@@ -16,6 +17,11 @@ function matchesPattern(str, pattern) {
16
17
  return regex.test(str);
17
18
  }
18
19
 
20
+ function isIncluded(name, includePatterns) {
21
+ if (!includePatterns) return true;
22
+ return includePatterns.some(pattern => matchesPattern(name, pattern));
23
+ }
24
+
19
25
  function isIgnored(name, ignorePatterns) {
20
26
  return ignorePatterns.some(pattern => matchesPattern(name, pattern));
21
27
  }
@@ -65,7 +71,9 @@ class ProcessManager {
65
71
  constructor(renderer, scripts) {
66
72
  this.renderer = renderer;
67
73
  this.config = loadConfig();
68
- this.scripts = scripts.filter(s => !isIgnored(s.name, this.config.ignore));
74
+ this.scripts = scripts
75
+ .filter(s => isIncluded(s.name, this.config.include))
76
+ .filter(s => !isIgnored(s.name, this.config.ignore));
69
77
  this.phase = 'selection'; // 'selection' | 'running'
70
78
  this.selectedScripts = new Set(this.config.defaultSelection);
71
79
  this.countdown = COUNTDOWN_SECONDS;
@@ -779,11 +787,40 @@ class ProcessManager {
779
787
  const line = linesToShow[i];
780
788
  const processColor = this.processColors.get(line.process) || '#FFFFFF';
781
789
 
782
- // Truncate long lines to prevent wrapping (terminal width - prefix length - padding)
783
- const maxWidth = Math.max(40, this.renderer.width - line.process.length - 10);
784
- const truncatedText = line.text.length > maxWidth
785
- ? line.text.substring(0, maxWidth - 3) + '...'
786
- : line.text;
790
+ // Truncate long lines to prevent wrapping (terminal width - prefix length - padding)
791
+ const maxWidth = Math.max(40, this.renderer.width - line.process.length - 10);
792
+ const visibleLength = stripAnsi(line.text).length;
793
+ let truncatedText = line.text;
794
+ if (visibleLength > maxWidth) {
795
+ // Truncate by visible characters, preserving ANSI codes
796
+ let visible = 0;
797
+ let i = 0;
798
+ const ansiRegex = /\x1b\[[0-9;]*m/g;
799
+ let lastIndex = 0;
800
+ let result = '';
801
+ let match;
802
+ const text = line.text;
803
+ while ((match = ansiRegex.exec(text)) !== null) {
804
+ const before = text.slice(lastIndex, match.index);
805
+ for (const char of before) {
806
+ if (visible >= maxWidth - 3) break;
807
+ result += char;
808
+ visible++;
809
+ }
810
+ if (visible >= maxWidth - 3) break;
811
+ result += match[0]; // Keep ANSI code
812
+ lastIndex = ansiRegex.lastIndex;
813
+ }
814
+ if (visible < maxWidth - 3) {
815
+ const remaining = text.slice(lastIndex);
816
+ for (const char of remaining) {
817
+ if (visible >= maxWidth - 3) break;
818
+ result += char;
819
+ visible++;
820
+ }
821
+ }
822
+ truncatedText = result + '\x1b[0m...'; // Reset and add ellipsis
823
+ }
787
824
 
788
825
  const outputLine = new TextRenderable(this.renderer, {
789
826
  id: `output-${i}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "startall",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {