recomposable 1.1.5 → 1.1.7

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.
@@ -18,25 +18,8 @@ exports.wrapPlainLine = wrapPlainLine;
18
18
  exports.renderLogView = renderLogView;
19
19
  exports.renderExecView = renderExecView;
20
20
  const state_1 = require("./state");
21
+ const theme_1 = require("./theme");
21
22
  const ESC = '\x1b[';
22
- const RESET = `${ESC}0m`;
23
- const BOLD = `${ESC}1m`;
24
- const DIM = `${ESC}2m`;
25
- const REVERSE = `${ESC}7m`;
26
- const FG_GREEN = `${ESC}32m`;
27
- const FG_YELLOW = `${ESC}33m`;
28
- const FG_RED = `${ESC}31m`;
29
- const FG_GRAY = `${ESC}90m`;
30
- const FG_CYAN = `${ESC}36m`;
31
- const FG_WHITE = `${ESC}37m`;
32
- const ITALIC = `${ESC}3m`;
33
- const BG_HIGHLIGHT = `${ESC}48;5;237m`;
34
- const LOGO = [
35
- ` ${ITALIC}${BOLD}${FG_CYAN}\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u252C\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2510 \u252C \u250C\u2500\u2510${RESET}`,
36
- ` ${ITALIC}${BOLD}${FG_CYAN}\u251C\u252C\u2518\u251C\u2524 \u2502 \u2502 \u2502\u2502\u2502\u2502\u251C\u2500\u2518\u2502 \u2502\u2514\u2500\u2510\u251C\u2500\u2524\u251C\u2534\u2510\u2502 \u251C\u2524${RESET}`,
37
- ` ${ITALIC}${BOLD}${FG_CYAN}\u2534\u2514\u2500\u2514\u2500\u2518\u2514\u2500\u2518\u2514\u2500\u2518\u2534 \u2534\u2534 \u2514\u2500\u2518\u2514\u2500\u2518\u2534 \u2534\u2514\u2500\u2518\u2534\u2500\u2518\u2514\u2500\u2518${RESET}`,
38
- ` ${DIM}docker compose manager${RESET}`,
39
- ];
40
23
  function visLen(str) {
41
24
  return str.replace(/\x1b\[[0-9;]*m/g, '').length;
42
25
  }
@@ -50,13 +33,17 @@ function padVisibleStart(str, width) {
50
33
  }
51
34
  exports.CLEAR_EOL = `${ESC}K`;
52
35
  exports.CLEAR_EOS = `${ESC}J`;
53
- const PATTERN_COLORS = [FG_YELLOW, FG_RED, FG_CYAN, FG_WHITE];
36
+ function patternColors() {
37
+ const p = (0, theme_1.getActivePalette)();
38
+ return [p.yellow, p.red, p.cyan, p.magenta];
39
+ }
54
40
  function logLineColor(line, patterns) {
41
+ const colors = patternColors();
55
42
  let color = null;
56
43
  for (let pi = 0; pi < patterns.length; pi++) {
57
44
  const group = Array.isArray(patterns[pi]) ? patterns[pi] : [patterns[pi]];
58
45
  if (group.some(p => line.includes(p))) {
59
- color = PATTERN_COLORS[pi % PATTERN_COLORS.length];
46
+ color = colors[pi % colors.length];
60
47
  }
61
48
  }
62
49
  return color;
@@ -64,6 +51,7 @@ function logLineColor(line, patterns) {
64
51
  // Cached separator line — recomputed only when terminal width changes
65
52
  let cachedSepColumns = 0;
66
53
  let cachedSepLine = '';
54
+ let cachedSepGray = '';
67
55
  function patternLabel(pattern) {
68
56
  return pattern.replace(/^[\[\(\{<]/, '').replace(/[\]\)\}>]$/, '');
69
57
  }
@@ -75,30 +63,33 @@ function parseTimestamp(ts) {
75
63
  return isNaN(d.getTime()) ? null : d;
76
64
  }
77
65
  function relativeTime(ts) {
66
+ const { gray, dim, reset } = (0, theme_1.getActivePalette)();
78
67
  const date = parseTimestamp(ts);
79
68
  if (!date)
80
- return `${FG_GRAY}-${RESET}`;
69
+ return `${gray}-${reset}`;
81
70
  const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
82
71
  if (seconds < 0)
83
- return `${FG_GRAY}-${RESET}`;
72
+ return `${gray}-${reset}`;
84
73
  if (seconds < 60)
85
- return `${DIM}${seconds}s ago${RESET}`;
74
+ return `${dim}${seconds}s ago${reset}`;
86
75
  const minutes = Math.floor(seconds / 60);
87
76
  if (minutes < 60)
88
- return `${DIM}${minutes}m ago${RESET}`;
77
+ return `${dim}${minutes}m ago${reset}`;
89
78
  const hours = Math.floor(minutes / 60);
90
79
  if (hours < 24)
91
- return `${DIM}${hours}h ago${RESET}`;
80
+ return `${dim}${hours}h ago${reset}`;
92
81
  const days = Math.floor(hours / 24);
93
- return `${DIM}${days}d ago${RESET}`;
82
+ return `${dim}${days}d ago${reset}`;
94
83
  }
95
84
  function clearScreen() {
96
85
  return `${ESC}H${ESC}?25l`;
97
86
  }
98
87
  function separatorLine(columns) {
99
- if (columns !== cachedSepColumns) {
88
+ const { gray, reset } = (0, theme_1.getActivePalette)();
89
+ if (columns !== cachedSepColumns || gray !== cachedSepGray) {
100
90
  cachedSepColumns = columns;
101
- cachedSepLine = ` ${FG_GRAY}${'\u2500'.repeat(Math.max(0, columns - 2))}${RESET}`;
91
+ cachedSepGray = gray;
92
+ cachedSepLine = ` ${gray}${'\u2500'.repeat(Math.max(0, columns - 2))}${reset}`;
102
93
  }
103
94
  return cachedSepLine;
104
95
  }
@@ -106,31 +97,33 @@ function showCursor() {
106
97
  return `${ESC}?25h`;
107
98
  }
108
99
  function statusIcon(status, isRebuilding, isRestarting, isStopping, isStarting) {
100
+ const { yellow, gray, red, green, reset } = (0, theme_1.getActivePalette)();
109
101
  if (isRebuilding || isRestarting || isStopping || isStarting)
110
- return `${FG_YELLOW}\u25CF${RESET}`;
102
+ return `${yellow}\u25CF${reset}`;
111
103
  if (!status)
112
- return `${FG_GRAY}\u25CB${RESET}`;
104
+ return `${gray}\u25CB${reset}`;
113
105
  const { state, health } = status;
114
106
  if (state === 'running') {
115
107
  if (health === 'unhealthy')
116
- return `${FG_RED}\u25CF${RESET}`;
117
- return `${FG_GREEN}\u25CF${RESET}`;
108
+ return `${red}\u25CF${reset}`;
109
+ return `${green}\u25CF${reset}`;
118
110
  }
119
111
  if (state === 'restarting')
120
- return `${FG_YELLOW}\u25CF${RESET}`;
121
- return `${FG_GRAY}\u25CB${RESET}`;
112
+ return `${yellow}\u25CF${reset}`;
113
+ return `${gray}\u25CB${reset}`;
122
114
  }
123
115
  function statusText(status, isRebuilding, isRestarting, isStopping, isStarting) {
116
+ const { yellow, gray, red, green, dim, reset } = (0, theme_1.getActivePalette)();
124
117
  if (isStopping)
125
- return `${FG_YELLOW}STOPPING...${RESET}`;
118
+ return `${yellow}STOPPING...${reset}`;
126
119
  if (isStarting)
127
- return `${FG_YELLOW}STARTING...${RESET}`;
120
+ return `${yellow}STARTING...${reset}`;
128
121
  if (isRestarting)
129
- return `${FG_YELLOW}RESTARTING...${RESET}`;
122
+ return `${yellow}RESTARTING...${reset}`;
130
123
  if (isRebuilding)
131
- return `${FG_YELLOW}REBUILDING...${RESET}`;
124
+ return `${yellow}REBUILDING...${reset}`;
132
125
  if (!status)
133
- return `${FG_GRAY}stopped${RESET}`;
126
+ return `${gray}stopped${reset}`;
134
127
  const { state, health } = status;
135
128
  let text = state;
136
129
  if (health && health !== 'none' && health !== '') {
@@ -138,14 +131,14 @@ function statusText(status, isRebuilding, isRestarting, isStopping, isStarting)
138
131
  }
139
132
  if (state === 'running') {
140
133
  if (health === 'unhealthy')
141
- return `${FG_RED}${text}${RESET}`;
142
- return `${FG_GREEN}${text}${RESET}`;
134
+ return `${red}${text}${reset}`;
135
+ return `${green}${text}${reset}`;
143
136
  }
144
137
  if (state === 'exited')
145
- return `${FG_GRAY}${text}${RESET}`;
138
+ return `${gray}${text}${reset}`;
146
139
  if (state === 'restarting')
147
- return `${FG_YELLOW}${text}${RESET}`;
148
- return `${DIM}${text}${RESET}`;
140
+ return `${yellow}${text}${reset}`;
141
+ return `${dim}${text}${reset}`;
149
142
  }
150
143
  function formatMem(bytes) {
151
144
  if (bytes <= 0)
@@ -156,12 +149,23 @@ function formatMem(bytes) {
156
149
  return `${Math.round(bytes / (1024 * 1024))}M`;
157
150
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}G`;
158
151
  }
152
+ function renderLogo() {
153
+ const { italic, bold, dim, reset, logo } = (0, theme_1.getActivePalette)();
154
+ const color = logo || '';
155
+ return [
156
+ ` ${color}${italic}${bold}\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u252C\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2500\u2510\u250C\u2510 \u252C \u250C\u2500\u2510${reset}`,
157
+ ` ${color}${italic}${bold}\u251C\u252C\u2518\u251C\u2524 \u2502 \u2502 \u2502\u2502\u2502\u2502\u251C\u2500\u2518\u2502 \u2502\u2514\u2500\u2510\u251C\u2500\u2524\u251C\u2534\u2510\u2502 \u251C\u2524${reset}`,
158
+ ` ${color}${italic}${bold}\u2534\u2514\u2500\u2514\u2500\u2518\u2514\u2500\u2518\u2514\u2500\u2518\u2534 \u2534\u2534 \u2514\u2500\u2518\u2514\u2500\u2518\u2534 \u2534\u2514\u2500\u2518\u2534\u2500\u2518\u2514\u2500\u2518${reset}`,
159
+ ` ${dim}docker compose manager${reset}`,
160
+ ];
161
+ }
159
162
  function renderLegend(opts = {}) {
163
+ const { reverse, dim, reset } = (0, theme_1.getActivePalette)();
160
164
  const { logPanelActive = false, logsScrollMode = false, noCacheActive = false, noDepsActive = false, watchActive = false, execMode = false, execInline = false, worktreePickerActive = false } = opts;
161
165
  const item = (text, active) => {
162
166
  if (active)
163
- return `${BG_HIGHLIGHT} ${text} ${RESET}`;
164
- return `${DIM}${text}${RESET}`;
167
+ return `${reverse} ${text} ${reset}`;
168
+ return `${dim}${text}${reset}`;
165
169
  };
166
170
  if (worktreePickerActive) {
167
171
  return [
@@ -217,12 +221,14 @@ function renderLegend(opts = {}) {
217
221
  ].join(' ');
218
222
  }
219
223
  function renderListView(state) {
224
+ const { reset, bold, dim, reverse, green, yellow, red, gray, cyan, magenta } = (0, theme_1.getActivePalette)();
220
225
  const columns = process.stdout.columns ?? 80;
221
226
  const rows = process.stdout.rows ?? 24;
222
227
  const patterns = state.config.logScanPatterns || [];
228
+ const pColors = patternColors();
223
229
  const sep = separatorLine(columns);
224
230
  const buf = [];
225
- for (const line of LOGO) {
231
+ for (const line of renderLogo()) {
226
232
  buf.push(line);
227
233
  }
228
234
  const watchActive = state.watching.size > 0;
@@ -234,44 +240,44 @@ function renderListView(state) {
234
240
  buf.push(sep);
235
241
  buf.push(` ${help}`);
236
242
  // Single column header row (not repeated per group)
237
- let colHeader = `${DIM} ${'SERVICE'.padEnd(24)} `;
243
+ let colHeader = `${dim} ${'SERVICE'.padEnd(24)} `;
238
244
  colHeader += `${'STATUS'.padEnd(22)} ${'BUILT'.padEnd(12)} ${'RESTARTED'.padEnd(12)}`;
239
245
  for (const p of patterns)
240
246
  colHeader += patternLabel(Array.isArray(p) ? p[0] : p).padStart(5) + ' ';
241
247
  colHeader += ` ${'CPU/MEM'.padStart(16)} ${'PORTS'.padEnd(14)}`;
242
248
  if (state.showWorktreeColumn)
243
249
  colHeader += ` ${'WORKTREE'.padEnd(15)}`;
244
- buf.push(colHeader + RESET);
250
+ buf.push(colHeader + reset);
245
251
  const headerHeight = buf.length;
246
252
  const bottomBuf = [];
247
253
  if (state.worktreePickerActive) {
248
254
  const selEntry = state.flatList[state.cursor];
249
255
  if (selEntry) {
250
256
  bottomBuf.push(sep);
251
- bottomBuf.push(` ${FG_CYAN}switch worktree ${BOLD}${selEntry.service}${RESET}`);
252
- bottomBuf.push(` ${DIM}j/k navigate Enter confirm Esc cancel${RESET}`);
257
+ bottomBuf.push(` ${cyan}switch worktree ${bold}${selEntry.service}${reset}`);
258
+ bottomBuf.push(` ${dim}j/k navigate Enter confirm Esc cancel${reset}`);
253
259
  for (let wi = 0; wi < state.worktreePickerEntries.length; wi++) {
254
260
  const wt = state.worktreePickerEntries[wi];
255
261
  const isSelected = wi === state.worktreePickerCursor;
256
- const prefix = isSelected ? `${REVERSE}` : '';
257
- const suffix = isSelected ? `${RESET}` : '';
262
+ const prefix = isSelected ? `${reverse}` : '';
263
+ const suffix = isSelected ? `${reset}` : '';
258
264
  const currentTag = (state.worktreePickerCurrentPath && state.worktreePickerCurrentPath === wt.path)
259
- ? ` ${DIM}(current)${RESET}` : '';
260
- bottomBuf.push(` ${prefix} ${wt.branch} ${DIM}${wt.path}${RESET}${currentTag}${suffix}`);
265
+ ? ` ${dim}(current)${reset}` : '';
266
+ bottomBuf.push(` ${prefix} ${wt.branch} ${dim}${wt.path}${reset}${currentTag}${suffix}`);
261
267
  }
262
268
  }
263
269
  }
264
270
  else if (state.execActive && state.execService) {
265
271
  bottomBuf.push(sep);
266
- const runningIndicator = state.execChild ? `${FG_YELLOW}running${RESET}` : `${FG_GREEN}ready${RESET}`;
267
- const cwdInfo = state.execCwd ? ` ${DIM}${state.execCwd}${RESET}` : '';
268
- bottomBuf.push(` ${FG_CYAN}exec ${BOLD}${state.execService}${RESET} ${runningIndicator}${cwdInfo}`);
272
+ const runningIndicator = state.execChild ? `${yellow}running${reset}` : `${green}ready${reset}`;
273
+ const cwdInfo = state.execCwd ? ` ${dim}${state.execCwd}${reset}` : '';
274
+ bottomBuf.push(` ${cyan}exec ${bold}${state.execService}${reset} ${runningIndicator}${cwdInfo}`);
269
275
  const maxOutputLines = Math.max(1, (state.config.bottomLogCount || 10) - 1);
270
276
  const outputStart = Math.max(0, state.execOutputLines.length - maxOutputLines);
271
277
  for (let i = outputStart; i < state.execOutputLines.length; i++) {
272
278
  bottomBuf.push(truncateLine(` ${state.execOutputLines[i]}`, columns));
273
279
  }
274
- bottomBuf.push(`${FG_GREEN}$ ${RESET}${state.execInput}${BOLD}_${RESET}`);
280
+ bottomBuf.push(`${green}$ ${reset}${state.execInput}${bold}_${reset}`);
275
281
  }
276
282
  else if (state.showBottomLogs) {
277
283
  const selEntry = state.flatList[state.cursor];
@@ -281,23 +287,23 @@ function renderListView(state) {
281
287
  const cascade = state.cascading.get(sk);
282
288
  if (cascade) {
283
289
  bottomBuf.push(sep);
284
- bottomBuf.push(` ${FG_YELLOW}cascading ${BOLD}${selEntry.service}${RESET}`);
290
+ bottomBuf.push(` ${yellow}cascading ${bold}${selEntry.service}${reset}`);
285
291
  for (let si = 0; si < cascade.steps.length; si++) {
286
292
  const step = cascade.steps[si];
287
293
  let marker;
288
294
  switch (step.status) {
289
295
  case 'completed':
290
- marker = `${FG_GREEN}[done]${RESET}`;
296
+ marker = `${green}[done]${reset}`;
291
297
  break;
292
298
  case 'in_progress':
293
- marker = `${FG_YELLOW}[>>> ]${RESET}`;
299
+ marker = `${yellow}[>>> ]${reset}`;
294
300
  break;
295
301
  case 'failed':
296
- marker = `${FG_RED}[FAIL]${RESET}`;
302
+ marker = `${red}[FAIL]${reset}`;
297
303
  break;
298
- default: marker = `${DIM}[ ]${RESET}`;
304
+ default: marker = `${dim}[ ]${reset}`;
299
305
  }
300
- bottomBuf.push(` ${marker} ${step.action} ${BOLD}${step.service}${RESET}`);
306
+ bottomBuf.push(` ${marker} ${step.action} ${bold}${step.service}${reset}`);
301
307
  }
302
308
  }
303
309
  const info = state.bottomLogLines.get(sk);
@@ -306,33 +312,33 @@ function renderListView(state) {
306
312
  bottomBuf.push(sep);
307
313
  }
308
314
  const isFailed = info.action === 'build_failed' || info.action === 'restart_failed' || info.action === 'stop_failed' || info.action === 'start_failed' || info.action === 'switch_failed';
309
- const actionColor = isFailed ? FG_RED
310
- : info.action === 'rebuilding' || info.action === 'restarting' || info.action === 'stopping' || info.action === 'starting' || info.action === 'cascading' || info.action === 'switching' ? FG_YELLOW
311
- : info.action === 'watching' ? FG_CYAN : FG_GREEN;
315
+ const actionColor = isFailed ? red
316
+ : info.action === 'rebuilding' || info.action === 'restarting' || info.action === 'stopping' || info.action === 'starting' || info.action === 'cascading' || info.action === 'switching' ? yellow
317
+ : info.action === 'watching' ? cyan : green;
312
318
  const actionLabel = isFailed ? info.action.replace('_', ' ').toUpperCase() : info.action;
313
- let headerLine = ` ${actionColor}${actionLabel} ${BOLD}${info.service}${RESET}`;
319
+ let headerLine = ` ${actionColor}${actionLabel} ${bold}${info.service}${reset}`;
314
320
  const bq = state.bottomSearchQuery || '';
315
321
  if (bq && !state.bottomSearchActive) {
316
322
  if (state.bottomSearchLoading) {
317
- headerLine += ` ${FG_YELLOW}searching "${bq}"...${RESET}`;
323
+ headerLine += ` ${yellow}searching "${bq}"...${reset}`;
318
324
  }
319
325
  else {
320
326
  const totalMatches = state.bottomSearchTotalMatches;
321
327
  headerLine += totalMatches > 0
322
- ? ` ${DIM}search: "${bq}" (${totalMatches} match${totalMatches !== 1 ? 'es' : ''} in full log)${RESET}`
323
- : ` ${FG_RED}search: "${bq}" (no matches)${RESET}`;
328
+ ? ` ${dim}search: "${bq}" (${totalMatches} match${totalMatches !== 1 ? 'es' : ''} in full log)${reset}`
329
+ : ` ${red}search: "${bq}" (no matches)${reset}`;
324
330
  }
325
331
  }
326
332
  bottomBuf.push(headerLine);
327
333
  if (info.lines.length === 0 && info.action === 'logs') {
328
- bottomBuf.push(` ${DIM}loading...${RESET}`);
334
+ bottomBuf.push(` ${dim}loading...${reset}`);
329
335
  }
330
336
  const searchQuery = bq && !state.bottomSearchActive ? bq : '';
331
337
  const maxBottomLines = state.config.bottomLogCount || 10;
332
338
  const visibleLines = info.lines.slice(-maxBottomLines);
333
339
  for (const line of visibleLines) {
334
340
  let coloredLine = line.substring(0, columns - 4);
335
- const lineColor = logLineColor(coloredLine, patterns) || FG_GRAY;
341
+ const lineColor = logLineColor(coloredLine, patterns) || gray;
336
342
  if (searchQuery) {
337
343
  const lowerLine = coloredLine.toLowerCase();
338
344
  const lowerQ = searchQuery.toLowerCase();
@@ -346,16 +352,16 @@ function renderListView(state) {
346
352
  break;
347
353
  }
348
354
  result += coloredLine.substring(pos, idx);
349
- result += `${REVERSE}${FG_YELLOW}${coloredLine.substring(idx, idx + searchQuery.length)}${RESET}${lineColor}`;
355
+ result += `${reverse}${yellow}${coloredLine.substring(idx, idx + searchQuery.length)}${reset}${lineColor}`;
350
356
  pos = idx + searchQuery.length;
351
357
  }
352
358
  coloredLine = result;
353
359
  }
354
360
  }
355
- bottomBuf.push(` ${lineColor}${coloredLine}${RESET}`);
361
+ bottomBuf.push(` ${lineColor}${coloredLine}${reset}`);
356
362
  }
357
363
  if (state.bottomSearchActive) {
358
- bottomBuf.push(`${BOLD}/${RESET}${state.bottomSearchQuery}${BOLD}_${RESET}`);
364
+ bottomBuf.push(`${bold}/${reset}${state.bottomSearchQuery}${bold}_${reset}`);
359
365
  }
360
366
  }
361
367
  }
@@ -394,8 +400,8 @@ function renderListView(state) {
394
400
  break;
395
401
  case 'header': {
396
402
  const group = state.groups[stub.groupIdx];
397
- const label = ` ${BOLD}${group.label}${RESET}`;
398
- buf.push(group.error ? `${label} ${FG_RED}(${group.error})${RESET}` : label);
403
+ const label = ` ${bold}${group.label}${reset}`;
404
+ buf.push(group.error ? `${label} ${red}(${group.error})${reset}` : label);
399
405
  break;
400
406
  }
401
407
  case 'service': {
@@ -411,7 +417,7 @@ function renderListView(state) {
411
417
  const isCascading = state.cascading.has(sk);
412
418
  const icon = statusIcon(st, rebuilding || isCascading, restarting, stopping, starting);
413
419
  const stext = statusText(st, rebuilding || isCascading, restarting, stopping, starting);
414
- const watchIndicator = isWatching ? `${FG_CYAN}W${RESET}` : ' ';
420
+ const watchIndicator = isWatching ? `${cyan}W${reset}` : ' ';
415
421
  const wtBranch = st ? st.worktree : null;
416
422
  const name = entry.service.padEnd(24);
417
423
  const statusPadded = padVisible(stext, 22);
@@ -424,25 +430,25 @@ function renderListView(state) {
424
430
  const cpuDanger = state.config.cpuDangerThreshold || 100;
425
431
  const memWarn = (state.config.memWarnThreshold || 512) * 1024 * 1024;
426
432
  const memDanger = (state.config.memDangerThreshold || 1024) * 1024 * 1024;
427
- let color = DIM;
433
+ let color = dim;
428
434
  if (cpu > cpuDanger || mem > memDanger)
429
- color = FG_RED;
435
+ color = red;
430
436
  else if (cpu > cpuWarn || mem > memWarn)
431
- color = FG_YELLOW;
437
+ color = yellow;
432
438
  const cpuText = cpu.toFixed(1) + '%';
433
439
  const memText = formatMem(mem);
434
- cpuMemStr = padVisible(`${color}${cpuText} / ${memText}${RESET}`, 16);
440
+ cpuMemStr = padVisible(`${color}${cpuText} / ${memText}${reset}`, 16);
435
441
  }
436
442
  else {
437
- cpuMemStr = padVisible(`${DIM}-${RESET}`, 16);
443
+ cpuMemStr = padVisible(`${dim}-${reset}`, 16);
438
444
  }
439
445
  let portsStr;
440
446
  if (st && st.ports && st.ports.length > 0) {
441
447
  const portsText = st.ports.map(p => p.published).join(' ');
442
- portsStr = padVisible(`${DIM}${portsText}${RESET}`, 14);
448
+ portsStr = padVisible(`${dim}${portsText}${reset}`, 14);
443
449
  }
444
450
  else {
445
- portsStr = padVisible(`${DIM}-${RESET}`, 14);
451
+ portsStr = padVisible(`${dim}-${reset}`, 14);
446
452
  }
447
453
  const built = padVisible(relativeTime(st ? st.createdAt : null), 12);
448
454
  const restarted = padVisible(relativeTime(st ? st.startedAt : null), 12);
@@ -452,22 +458,26 @@ function renderListView(state) {
452
458
  for (let pi = 0; pi < patterns.length; pi++) {
453
459
  const key = Array.isArray(patterns[pi]) ? patterns[pi][0] : patterns[pi];
454
460
  const count = logCounts ? (logCounts.get(key) || 0) : 0;
455
- const color = count > 0 ? PATTERN_COLORS[pi % PATTERN_COLORS.length] : DIM;
456
- const countText = count > 0 ? `${color}${count}${RESET}` : `${color}-${RESET}`;
461
+ const color = count > 0 ? pColors[pi % pColors.length] : dim;
462
+ const countText = count > 0 ? `${color}${count}${reset}` : `${color}-${reset}`;
457
463
  countsStr += padVisibleStart(countText, 5) + ' ';
458
464
  }
459
465
  let worktreeCol = '';
460
466
  if (state.showWorktreeColumn) {
461
467
  const wtLabel = (0, state_1.worktreeLabel)(st ? st.worktree : null);
462
- const wtColor = (wtBranch && wtBranch !== 'main') ? FG_YELLOW : DIM;
463
- worktreeCol = ` ${wtColor}${wtLabel.padEnd(15)}${RESET}`;
468
+ const wtColor = (wtBranch && wtBranch !== 'main') ? yellow : dim;
469
+ worktreeCol = ` ${wtColor}${wtLabel.padEnd(15)}${reset}`;
464
470
  }
465
- let row = ` ${watchIndicator}${icon} ${FG_WHITE}${name}${RESET} ${statusPadded} ${built} ${restarted}${countsStr} ${cpuMemStr} ${portsStr}${worktreeCol}`;
471
+ let row = ` ${watchIndicator}${icon} ${bold}${name}${reset} ${statusPadded} ${built} ${restarted}${countsStr} ${cpuMemStr} ${portsStr}${worktreeCol}`;
466
472
  if (isSelected) {
467
- // Re-apply BG after every RESET so highlight spans the full row;
468
- // promote dim/gray text to white so it's readable on the highlight background
469
- row = row.replace(/\x1b\[2m/g, FG_WHITE).replace(/\x1b\[90m/g, FG_WHITE);
470
- row = `${BG_HIGHLIGHT}${row.replace(/\x1b\[0m/g, `${RESET}${BG_HIGHLIGHT}`)}${' '.repeat(Math.max(0, columns - visLen(row)))}${RESET}`;
473
+ const { highlightBg, dim: dimCode } = (0, theme_1.getActivePalette)();
474
+ // Use explicit bg color for highlight bar so colored text stays readable;
475
+ // strip dim/gray so text pops on the highlight bg; make fg colors bold
476
+ const dimEsc = dimCode.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
477
+ row = row.replace(/\x1b\[2m/g, '').replace(/\x1b\[90m/g, '').replace(new RegExp(dimEsc, 'g'), '');
478
+ row = row.replace(/\x1b\[1;3([1-6])m/g, '\x1b[1;3$1m');
479
+ row = row.replace(/\x1b\[3([1-6])m/g, '\x1b[1;3$1m');
480
+ row = `${highlightBg}${bold}${row.replace(/\x1b\[0m/g, `${reset}${highlightBg}${bold}`)}${' '.repeat(Math.max(0, columns - visLen(row)))}${reset}`;
471
481
  }
472
482
  buf.push(row);
473
483
  break;
@@ -483,6 +493,7 @@ function renderListView(state) {
483
493
  return buf.join(exports.CLEAR_EOL + '\n');
484
494
  }
485
495
  function truncateLine(str, maxWidth) {
496
+ const { reset } = (0, theme_1.getActivePalette)();
486
497
  let visPos = 0;
487
498
  let rawPos = 0;
488
499
  while (rawPos < str.length) {
@@ -499,7 +510,7 @@ function truncateLine(str, maxWidth) {
499
510
  }
500
511
  }
501
512
  if (visPos >= maxWidth) {
502
- return str.substring(0, rawPos) + RESET;
513
+ return str.substring(0, rawPos) + reset;
503
514
  }
504
515
  visPos++;
505
516
  rawPos++;
@@ -507,6 +518,7 @@ function truncateLine(str, maxWidth) {
507
518
  return str;
508
519
  }
509
520
  function highlightSearchInLine(line, query, baseColor) {
521
+ const { reverse, yellow, reset } = (0, theme_1.getActivePalette)();
510
522
  if (!query)
511
523
  return line;
512
524
  const lowerLine = line.toLowerCase();
@@ -521,7 +533,7 @@ function highlightSearchInLine(line, query, baseColor) {
521
533
  break;
522
534
  }
523
535
  result += line.substring(pos, idx);
524
- result += `${REVERSE}${FG_YELLOW}${line.substring(idx, idx + query.length)}${RESET}${restore}`;
536
+ result += `${reverse}${yellow}${line.substring(idx, idx + query.length)}${reset}${restore}`;
525
537
  pos = idx + query.length;
526
538
  }
527
539
  return result;
@@ -536,10 +548,11 @@ function wrapPlainLine(line, width) {
536
548
  return result;
537
549
  }
538
550
  function renderLogView(state) {
551
+ const { reset, bold, dim, green, yellow, red } = (0, theme_1.getActivePalette)();
539
552
  const columns = process.stdout.columns ?? 80;
540
553
  const rows = process.stdout.rows ?? 24;
541
554
  const buf = [];
542
- for (const line of LOGO) {
555
+ for (const line of renderLogo()) {
543
556
  buf.push(line);
544
557
  }
545
558
  buf.push(separatorLine(columns));
@@ -553,36 +566,36 @@ function renderLogView(state) {
553
566
  const buildInfo = state.bottomLogLines.get(state.logBuildKey);
554
567
  const isBuilding = state.rebuilding.has(state.logBuildKey) || state.cascading.has(state.logBuildKey);
555
568
  if (buildInfo && buildInfo.action === 'build_failed') {
556
- statusLine = ` ${FG_RED}build failed ${BOLD}${serviceName}${RESET}`;
569
+ statusLine = ` ${red}build failed ${bold}${serviceName}${reset}`;
557
570
  }
558
571
  else if (isBuilding) {
559
- statusLine = ` ${FG_YELLOW}rebuilding ${BOLD}${serviceName}${RESET}`;
572
+ statusLine = ` ${yellow}rebuilding ${bold}${serviceName}${reset}`;
560
573
  }
561
574
  else {
562
- statusLine = ` ${FG_GREEN}build logs ${BOLD}${serviceName}${RESET}`;
575
+ statusLine = ` ${green}build logs ${bold}${serviceName}${reset}`;
563
576
  }
564
577
  }
565
578
  else {
566
- statusLine = ` ${FG_GREEN}full logs ${BOLD}${serviceName}${RESET}`;
579
+ statusLine = ` ${green}full logs ${bold}${serviceName}${reset}`;
567
580
  }
568
581
  let scrollStatus;
569
582
  if (state.logAutoScroll) {
570
- scrollStatus = `${FG_GREEN}live${RESET}`;
583
+ scrollStatus = `${green}live${reset}`;
571
584
  }
572
585
  else {
573
586
  const currentLine = Math.max(1, totalLines - state.logScrollOffset);
574
587
  const pct = totalLines > 0 ? Math.round((currentLine / totalLines) * 100) : 100;
575
- scrollStatus = `${FG_YELLOW}paused ${DIM}line ${currentLine} / ${totalLines} (${pct}%)${RESET}`;
588
+ scrollStatus = `${yellow}paused ${dim}line ${currentLine} / ${totalLines} (${pct}%)${reset}`;
576
589
  }
577
590
  statusLine += ` ${scrollStatus}`;
578
591
  if (state.logSearchPending || state.logHistoryLoading) {
579
- statusLine += ` ${FG_YELLOW}loading history...${RESET}`;
592
+ statusLine += ` ${yellow}loading history...${reset}`;
580
593
  }
581
594
  else if (state.logSearchQuery && state.logSearchMatches.length > 0) {
582
- statusLine += ` ${DIM}match ${state.logSearchMatchIdx + 1}/${state.logSearchMatches.length}${RESET}`;
595
+ statusLine += ` ${dim}match ${state.logSearchMatchIdx + 1}/${state.logSearchMatches.length}${reset}`;
583
596
  }
584
597
  else if (state.logSearchQuery && state.logSearchMatches.length === 0) {
585
- statusLine += ` ${FG_RED}no matches${RESET}`;
598
+ statusLine += ` ${red}no matches${reset}`;
586
599
  }
587
600
  buf.push(statusLine);
588
601
  const bottomReserved = state.logSearchActive ? 1 : 0;
@@ -596,7 +609,7 @@ function renderLogView(state) {
596
609
  endLine = Math.max(Math.min(availableRows, totalLines), totalLines - state.logScrollOffset);
597
610
  }
598
611
  if (totalLines === 0) {
599
- buf.push(` ${DIM}loading...${RESET}`);
612
+ buf.push(` ${dim}loading...${reset}`);
600
613
  }
601
614
  const searchQuery = state.logSearchQuery || '';
602
615
  const matchSet = searchQuery ? new Set(state.logSearchMatches) : null;
@@ -614,7 +627,7 @@ function renderLogView(state) {
614
627
  segment = highlightSearchInLine(segment, searchQuery, lineColor || undefined);
615
628
  }
616
629
  if (lineColor) {
617
- segment = `${lineColor}${segment}${RESET}`;
630
+ segment = `${lineColor}${segment}${reset}`;
618
631
  }
619
632
  displayLines.push(segment);
620
633
  }
@@ -630,23 +643,24 @@ function renderLogView(state) {
630
643
  buf.push('');
631
644
  }
632
645
  if (state.logSearchActive) {
633
- buf.push(`${BOLD}/${RESET}${state.logSearchQuery}${BOLD}_${RESET}`);
646
+ buf.push(`${bold}/${reset}${state.logSearchQuery}${bold}_${reset}`);
634
647
  }
635
648
  return buf.join(exports.CLEAR_EOL + '\n');
636
649
  }
637
650
  function renderExecView(state) {
651
+ const { reset, bold, dim, green, yellow, cyan } = (0, theme_1.getActivePalette)();
638
652
  const columns = process.stdout.columns ?? 80;
639
653
  const rows = process.stdout.rows ?? 24;
640
654
  const buf = [];
641
- for (const line of LOGO) {
655
+ for (const line of renderLogo()) {
642
656
  buf.push(line);
643
657
  }
644
658
  buf.push(separatorLine(columns));
645
659
  buf.push(` ${renderLegend({ execMode: true })}`);
646
660
  const serviceName = state.execService || '???';
647
- const runningIndicator = state.execChild ? `${FG_YELLOW}running${RESET}` : `${FG_GREEN}ready${RESET}`;
648
- const cwdInfo = state.execCwd ? ` ${DIM}${state.execCwd}${RESET}` : '';
649
- buf.push(` ${FG_CYAN}exec ${BOLD}${serviceName}${RESET} ${runningIndicator}${cwdInfo}`);
661
+ const runningIndicator = state.execChild ? `${yellow}running${reset}` : `${green}ready${reset}`;
662
+ const cwdInfo = state.execCwd ? ` ${dim}${state.execCwd}${reset}` : '';
663
+ buf.push(` ${cyan}exec ${bold}${serviceName}${reset} ${runningIndicator}${cwdInfo}`);
650
664
  const headerHeight = buf.length;
651
665
  // Reserve 1 line for the prompt at the bottom
652
666
  const availableRows = Math.max(1, rows - headerHeight - 1);
@@ -662,7 +676,7 @@ function renderExecView(state) {
662
676
  buf.push('');
663
677
  }
664
678
  // Command prompt
665
- buf.push(`${FG_GREEN}$ ${RESET}${state.execInput}${BOLD}_${RESET}`);
679
+ buf.push(`${green}$ ${reset}${state.execInput}${bold}_${reset}`);
666
680
  return buf.join(exports.CLEAR_EOL + '\n');
667
681
  }
668
682
  //# sourceMappingURL=renderer.js.map