numux 1.10.3 → 1.11.0

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/numux.js CHANGED
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
22
22
  var require_package = __commonJS((exports, module) => {
23
23
  module.exports = {
24
24
  name: "numux",
25
- version: "1.10.3",
25
+ version: "1.11.0",
26
26
  description: "Terminal multiplexer with dependency orchestration",
27
27
  type: "module",
28
28
  license: "MIT",
@@ -122,6 +122,16 @@ var FLAGS = [
122
122
  key: "autoColors",
123
123
  description: "Auto-assign colors to processes based on their name"
124
124
  },
125
+ {
126
+ type: "value",
127
+ long: "--env-file",
128
+ short: "-e",
129
+ key: "envFile",
130
+ description: 'Env file path, or "false" to disable env file loading',
131
+ valueName: "<path|false>",
132
+ completionHint: "file",
133
+ parse: (raw) => raw === "false" ? false : raw
134
+ },
125
135
  {
126
136
  type: "value",
127
137
  long: "--config",
@@ -970,7 +980,7 @@ function validateConfig(raw, warnings) {
970
980
  throw new Error("Config must define at least one process");
971
981
  }
972
982
  const globalCwd = typeof config.cwd === "string" ? config.cwd : undefined;
973
- const globalEnvFile = validateStringOrStringArray(config.envFile);
983
+ const globalEnvFile = validateEnvFile(config.envFile);
974
984
  let globalEnv;
975
985
  if (config.env && typeof config.env === "object") {
976
986
  for (const [k, v] of Object.entries(config.env)) {
@@ -1066,7 +1076,11 @@ function validateStringOrStringArray(value) {
1066
1076
  return value;
1067
1077
  return;
1068
1078
  }
1069
- var validateEnvFile = validateStringOrStringArray;
1079
+ function validateEnvFile(value) {
1080
+ if (value === false)
1081
+ return false;
1082
+ return validateStringOrStringArray(value);
1083
+ }
1070
1084
  function validateErrorMatcher(name, value) {
1071
1085
  if (value === true)
1072
1086
  return true;
@@ -2119,6 +2133,37 @@ var STATUS_ICON_HEX = {
2119
2133
  skipped: "#888888"
2120
2134
  };
2121
2135
  var TERMINAL_STATUSES = new Set(["finished", "stopped", "failed", "skipped"]);
2136
+ function formatTab(name, status) {
2137
+ return `${STATUS_ICONS[status]} ${name}`;
2138
+ }
2139
+ function formatDescription(status, exitCode, restartCount) {
2140
+ let desc = status;
2141
+ if ((status === "failed" || status === "stopped") && exitCode != null && exitCode !== 0) {
2142
+ desc = `exit ${exitCode}`;
2143
+ }
2144
+ if (restartCount && restartCount > 0) {
2145
+ desc += ` \xD7${restartCount}`;
2146
+ }
2147
+ return desc;
2148
+ }
2149
+ function getDisplayOrder(originalNames, statuses) {
2150
+ const active = originalNames.filter((n) => !TERMINAL_STATUSES.has(statuses.get(n)));
2151
+ const terminal = originalNames.filter((n) => TERMINAL_STATUSES.has(statuses.get(n)));
2152
+ return [...active, ...terminal];
2153
+ }
2154
+ function resolveOptionColors(names, statuses, processColors, inputWaiting, erroredProcesses) {
2155
+ return names.map((name) => {
2156
+ const status = statuses.get(name);
2157
+ const waiting = inputWaiting.has(name);
2158
+ const errored = erroredProcesses.has(name);
2159
+ const statusHex = waiting ? "#ffaa00" : errored ? "#ff5555" : STATUS_ICON_HEX[status];
2160
+ const processHex = processColors.get(name);
2161
+ return {
2162
+ iconHex: statusHex ?? processHex ?? "#888888",
2163
+ nameHex: processHex ?? null
2164
+ };
2165
+ });
2166
+ }
2122
2167
 
2123
2168
  class ColoredSelectRenderable extends SelectRenderable {
2124
2169
  _optionColors = [];
@@ -2126,12 +2171,11 @@ class ColoredSelectRenderable extends SelectRenderable {
2126
2171
  this._optionColors = colors;
2127
2172
  this.requestRender();
2128
2173
  }
2129
- renderSelf(buffer, deltaTime) {
2130
- const wasDirty = this.isDirty;
2131
- super.renderSelf(buffer, deltaTime);
2132
- if (wasDirty && this.frameBuffer && this._optionColors.length > 0) {
2133
- this.colorizeOptions();
2134
- }
2174
+ renderSelf(_buffer, _deltaTime) {
2175
+ if (!(this.visible && this.frameBuffer))
2176
+ return;
2177
+ if (this.isDirty)
2178
+ this.renderOptions();
2135
2179
  }
2136
2180
  onMouseEvent(event) {
2137
2181
  if (event.type === "down") {
@@ -2144,28 +2188,45 @@ class ColoredSelectRenderable extends SelectRenderable {
2144
2188
  }
2145
2189
  }
2146
2190
  }
2147
- colorizeOptions() {
2191
+ renderOptions() {
2192
+ if (!this.frameBuffer || this.options.length === 0)
2193
+ return;
2148
2194
  const fb = this.frameBuffer;
2195
+ const bgColor = this._focused ? this._focusedBackgroundColor : this._backgroundColor;
2196
+ fb.clear(bgColor);
2149
2197
  const scrollOffset = this.scrollOffset;
2150
2198
  const maxVisibleItems = this.maxVisibleItems;
2151
2199
  const linesPerItem = this.linesPerItem;
2200
+ const fontHeight = this.fontHeight;
2152
2201
  const selectedIndex = this.getSelectedIndex();
2153
- const options = this.options;
2154
- const visibleCount = Math.min(maxVisibleItems, options.length - scrollOffset);
2202
+ const showDescription = this._showDescription;
2155
2203
  const baseTextColor = this._focused ? this._focusedTextColor : this._textColor;
2156
2204
  const selectedTextColor = this._selectedTextColor;
2157
- const lineWidth = fb.width;
2205
+ const descColor = this._descriptionColor;
2206
+ const selectedDescColor = this._selectedDescriptionColor;
2207
+ const selectedBgColor = this._selectedBackgroundColor;
2208
+ const itemSpacing = this._itemSpacing;
2209
+ const visibleCount = Math.min(maxVisibleItems, this.options.length - scrollOffset);
2158
2210
  for (let i = 0;i < visibleCount; i++) {
2159
2211
  const actualIndex = scrollOffset + i;
2160
- const itemY = i * linesPerItem;
2161
- const optName = options[actualIndex].name;
2212
+ const option = this.options[actualIndex];
2162
2213
  const isSelected = actualIndex === selectedIndex;
2163
- const defaultColor = isSelected ? selectedTextColor : baseTextColor;
2214
+ const itemY = i * linesPerItem;
2215
+ if (itemY + linesPerItem - 1 >= this.height)
2216
+ break;
2217
+ if (isSelected) {
2218
+ fb.fillRect(0, itemY, this.width, linesPerItem - itemSpacing, selectedBgColor);
2219
+ }
2164
2220
  const colors = this._optionColors[actualIndex];
2165
- const textColor = colors?.name ?? defaultColor;
2166
- fb.drawText(optName.padEnd(lineWidth), 1, itemY, textColor);
2221
+ const defaultColor = isSelected ? selectedTextColor : baseTextColor;
2222
+ const nameColor = colors?.name ?? defaultColor;
2223
+ fb.drawText(option.name, 1, itemY, nameColor);
2167
2224
  if (colors?.icon) {
2168
- fb.drawText(optName.charAt(0), 1, itemY, colors.icon);
2225
+ fb.drawText(option.name.charAt(0), 1, itemY, colors.icon);
2226
+ }
2227
+ if (showDescription && itemY + fontHeight < this.height) {
2228
+ const dc = isSelected ? selectedDescColor : descColor;
2229
+ fb.drawText(option.description, 3, itemY + fontHeight, dc);
2169
2230
  }
2170
2231
  }
2171
2232
  }
@@ -2191,7 +2252,7 @@ class TabBar {
2191
2252
  width: "100%",
2192
2253
  height: "100%",
2193
2254
  options: names.map((n) => ({
2194
- name: this.formatTab(n, "pending"),
2255
+ name: formatTab(n, "pending"),
2195
2256
  description: "pending"
2196
2257
  })),
2197
2258
  selectedBackgroundColor: "#334455",
@@ -2214,7 +2275,7 @@ class TabBar {
2214
2275
  }
2215
2276
  updateStatus(name, status, exitCode, restartCount) {
2216
2277
  this.statuses.set(name, status);
2217
- this.baseDescriptions.set(name, this.formatDescription(status, exitCode, restartCount));
2278
+ this.baseDescriptions.set(name, formatDescription(status, exitCode, restartCount));
2218
2279
  if (TERMINAL_STATUSES.has(status) || status === "stopping") {
2219
2280
  this.inputWaiting.delete(name);
2220
2281
  }
@@ -2246,9 +2307,9 @@ class TabBar {
2246
2307
  refreshOptions() {
2247
2308
  const currentIdx = this.renderable.getSelectedIndex();
2248
2309
  const currentName = this.names[currentIdx];
2249
- this.names = this.getDisplayOrder();
2310
+ this.names = getDisplayOrder(this.originalNames, this.statuses);
2250
2311
  this.renderable.options = this.names.map((n) => ({
2251
- name: this.formatTab(n, this.statuses.get(n)),
2312
+ name: formatTab(n, this.statuses.get(n)),
2252
2313
  description: this.getDescription(n)
2253
2314
  }));
2254
2315
  const newIdx = this.names.indexOf(currentName);
@@ -2257,11 +2318,6 @@ class TabBar {
2257
2318
  }
2258
2319
  this.updateOptionColors();
2259
2320
  }
2260
- getDisplayOrder() {
2261
- const active = this.originalNames.filter((n) => !TERMINAL_STATUSES.has(this.statuses.get(n)));
2262
- const terminal = this.originalNames.filter((n) => TERMINAL_STATUSES.has(this.statuses.get(n)));
2263
- return [...active, ...terminal];
2264
- }
2265
2321
  getDescription(name) {
2266
2322
  if (this.inputWaiting.has(name))
2267
2323
  return "awaiting input";
@@ -2270,33 +2326,13 @@ class TabBar {
2270
2326
  return this.baseDescriptions.get(name) ?? "pending";
2271
2327
  }
2272
2328
  updateOptionColors() {
2273
- const colors = this.names.map((name) => {
2274
- const status = this.statuses.get(name);
2275
- const waiting = this.inputWaiting.has(name);
2276
- const errored = this.erroredProcesses.has(name);
2277
- const statusHex = waiting ? "#ffaa00" : errored ? "#ff5555" : STATUS_ICON_HEX[status];
2278
- const processHex = this.processColors.get(name);
2279
- return {
2280
- icon: parseColor(statusHex ?? processHex ?? "#888888"),
2281
- name: processHex ? parseColor(processHex) : null
2282
- };
2283
- });
2329
+ const resolved = resolveOptionColors(this.names, this.statuses, this.processColors, this.inputWaiting, this.erroredProcesses);
2330
+ const colors = resolved.map((c) => ({
2331
+ icon: parseColor(c.iconHex),
2332
+ name: c.nameHex ? parseColor(c.nameHex) : null
2333
+ }));
2284
2334
  this.renderable.setOptionColors(colors);
2285
2335
  }
2286
- formatDescription(status, exitCode, restartCount) {
2287
- let desc = status;
2288
- if ((status === "failed" || status === "stopped") && exitCode != null && exitCode !== 0) {
2289
- desc = `exit ${exitCode}`;
2290
- }
2291
- if (restartCount && restartCount > 0) {
2292
- desc += ` \xD7${restartCount}`;
2293
- }
2294
- return desc;
2295
- }
2296
- formatTab(name, status) {
2297
- const icon = STATUS_ICONS[status];
2298
- return `${icon} ${name}`;
2299
- }
2300
2336
  getSelectedIndex() {
2301
2337
  return this.renderable.getSelectedIndex();
2302
2338
  }
@@ -3114,6 +3150,11 @@ async function main() {
3114
3150
  }
3115
3151
  }
3116
3152
  }
3153
+ if (parsed.envFile !== undefined) {
3154
+ for (const proc of Object.values(config.processes)) {
3155
+ proc.envFile = parsed.envFile;
3156
+ }
3157
+ }
3117
3158
  if (parsed.noWatch) {
3118
3159
  for (const proc of Object.values(config.processes)) {
3119
3160
  delete proc.watch;
package/dist/types.d.ts CHANGED
@@ -2,7 +2,7 @@ export interface NumuxProcessConfig {
2
2
  command: string;
3
3
  cwd?: string;
4
4
  env?: Record<string, string>;
5
- envFile?: string | string[];
5
+ envFile?: string | string[] | false;
6
6
  dependsOn?: string[];
7
7
  readyPattern?: string;
8
8
  persistent?: boolean;
@@ -24,7 +24,7 @@ export type NumuxScriptPattern = Omit<NumuxProcessConfig, 'command'> & {
24
24
  export interface NumuxConfig {
25
25
  cwd?: string;
26
26
  env?: Record<string, string>;
27
- envFile?: string | string[];
27
+ envFile?: string | string[] | false;
28
28
  processes: Record<string, NumuxProcessConfig | NumuxScriptPattern | string>;
29
29
  }
30
30
  /** Validated config with all shorthand expanded to full objects */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numux",
3
- "version": "1.10.3",
3
+ "version": "1.11.0",
4
4
  "description": "Terminal multiplexer with dependency orchestration",
5
5
  "type": "module",
6
6
  "license": "MIT",