recomposable 1.1.5 → 1.1.6

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,25 @@ 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 } = (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
+ row = row.replace(/\x1b\[2m/g, '').replace(/\x1b\[90m/g, '');
477
+ row = row.replace(/\x1b\[1;3([1-6])m/g, '\x1b[1;3$1m');
478
+ row = row.replace(/\x1b\[3([1-6])m/g, '\x1b[1;3$1m');
479
+ row = `${highlightBg}${bold}${row.replace(/\x1b\[0m/g, `${reset}${highlightBg}${bold}`)}${' '.repeat(Math.max(0, columns - visLen(row)))}${reset}`;
471
480
  }
472
481
  buf.push(row);
473
482
  break;
@@ -483,6 +492,7 @@ function renderListView(state) {
483
492
  return buf.join(exports.CLEAR_EOL + '\n');
484
493
  }
485
494
  function truncateLine(str, maxWidth) {
495
+ const { reset } = (0, theme_1.getActivePalette)();
486
496
  let visPos = 0;
487
497
  let rawPos = 0;
488
498
  while (rawPos < str.length) {
@@ -499,7 +509,7 @@ function truncateLine(str, maxWidth) {
499
509
  }
500
510
  }
501
511
  if (visPos >= maxWidth) {
502
- return str.substring(0, rawPos) + RESET;
512
+ return str.substring(0, rawPos) + reset;
503
513
  }
504
514
  visPos++;
505
515
  rawPos++;
@@ -507,6 +517,7 @@ function truncateLine(str, maxWidth) {
507
517
  return str;
508
518
  }
509
519
  function highlightSearchInLine(line, query, baseColor) {
520
+ const { reverse, yellow, reset } = (0, theme_1.getActivePalette)();
510
521
  if (!query)
511
522
  return line;
512
523
  const lowerLine = line.toLowerCase();
@@ -521,7 +532,7 @@ function highlightSearchInLine(line, query, baseColor) {
521
532
  break;
522
533
  }
523
534
  result += line.substring(pos, idx);
524
- result += `${REVERSE}${FG_YELLOW}${line.substring(idx, idx + query.length)}${RESET}${restore}`;
535
+ result += `${reverse}${yellow}${line.substring(idx, idx + query.length)}${reset}${restore}`;
525
536
  pos = idx + query.length;
526
537
  }
527
538
  return result;
@@ -536,10 +547,11 @@ function wrapPlainLine(line, width) {
536
547
  return result;
537
548
  }
538
549
  function renderLogView(state) {
550
+ const { reset, bold, dim, green, yellow, red } = (0, theme_1.getActivePalette)();
539
551
  const columns = process.stdout.columns ?? 80;
540
552
  const rows = process.stdout.rows ?? 24;
541
553
  const buf = [];
542
- for (const line of LOGO) {
554
+ for (const line of renderLogo()) {
543
555
  buf.push(line);
544
556
  }
545
557
  buf.push(separatorLine(columns));
@@ -553,36 +565,36 @@ function renderLogView(state) {
553
565
  const buildInfo = state.bottomLogLines.get(state.logBuildKey);
554
566
  const isBuilding = state.rebuilding.has(state.logBuildKey) || state.cascading.has(state.logBuildKey);
555
567
  if (buildInfo && buildInfo.action === 'build_failed') {
556
- statusLine = ` ${FG_RED}build failed ${BOLD}${serviceName}${RESET}`;
568
+ statusLine = ` ${red}build failed ${bold}${serviceName}${reset}`;
557
569
  }
558
570
  else if (isBuilding) {
559
- statusLine = ` ${FG_YELLOW}rebuilding ${BOLD}${serviceName}${RESET}`;
571
+ statusLine = ` ${yellow}rebuilding ${bold}${serviceName}${reset}`;
560
572
  }
561
573
  else {
562
- statusLine = ` ${FG_GREEN}build logs ${BOLD}${serviceName}${RESET}`;
574
+ statusLine = ` ${green}build logs ${bold}${serviceName}${reset}`;
563
575
  }
564
576
  }
565
577
  else {
566
- statusLine = ` ${FG_GREEN}full logs ${BOLD}${serviceName}${RESET}`;
578
+ statusLine = ` ${green}full logs ${bold}${serviceName}${reset}`;
567
579
  }
568
580
  let scrollStatus;
569
581
  if (state.logAutoScroll) {
570
- scrollStatus = `${FG_GREEN}live${RESET}`;
582
+ scrollStatus = `${green}live${reset}`;
571
583
  }
572
584
  else {
573
585
  const currentLine = Math.max(1, totalLines - state.logScrollOffset);
574
586
  const pct = totalLines > 0 ? Math.round((currentLine / totalLines) * 100) : 100;
575
- scrollStatus = `${FG_YELLOW}paused ${DIM}line ${currentLine} / ${totalLines} (${pct}%)${RESET}`;
587
+ scrollStatus = `${yellow}paused ${dim}line ${currentLine} / ${totalLines} (${pct}%)${reset}`;
576
588
  }
577
589
  statusLine += ` ${scrollStatus}`;
578
590
  if (state.logSearchPending || state.logHistoryLoading) {
579
- statusLine += ` ${FG_YELLOW}loading history...${RESET}`;
591
+ statusLine += ` ${yellow}loading history...${reset}`;
580
592
  }
581
593
  else if (state.logSearchQuery && state.logSearchMatches.length > 0) {
582
- statusLine += ` ${DIM}match ${state.logSearchMatchIdx + 1}/${state.logSearchMatches.length}${RESET}`;
594
+ statusLine += ` ${dim}match ${state.logSearchMatchIdx + 1}/${state.logSearchMatches.length}${reset}`;
583
595
  }
584
596
  else if (state.logSearchQuery && state.logSearchMatches.length === 0) {
585
- statusLine += ` ${FG_RED}no matches${RESET}`;
597
+ statusLine += ` ${red}no matches${reset}`;
586
598
  }
587
599
  buf.push(statusLine);
588
600
  const bottomReserved = state.logSearchActive ? 1 : 0;
@@ -596,7 +608,7 @@ function renderLogView(state) {
596
608
  endLine = Math.max(Math.min(availableRows, totalLines), totalLines - state.logScrollOffset);
597
609
  }
598
610
  if (totalLines === 0) {
599
- buf.push(` ${DIM}loading...${RESET}`);
611
+ buf.push(` ${dim}loading...${reset}`);
600
612
  }
601
613
  const searchQuery = state.logSearchQuery || '';
602
614
  const matchSet = searchQuery ? new Set(state.logSearchMatches) : null;
@@ -614,7 +626,7 @@ function renderLogView(state) {
614
626
  segment = highlightSearchInLine(segment, searchQuery, lineColor || undefined);
615
627
  }
616
628
  if (lineColor) {
617
- segment = `${lineColor}${segment}${RESET}`;
629
+ segment = `${lineColor}${segment}${reset}`;
618
630
  }
619
631
  displayLines.push(segment);
620
632
  }
@@ -630,23 +642,24 @@ function renderLogView(state) {
630
642
  buf.push('');
631
643
  }
632
644
  if (state.logSearchActive) {
633
- buf.push(`${BOLD}/${RESET}${state.logSearchQuery}${BOLD}_${RESET}`);
645
+ buf.push(`${bold}/${reset}${state.logSearchQuery}${bold}_${reset}`);
634
646
  }
635
647
  return buf.join(exports.CLEAR_EOL + '\n');
636
648
  }
637
649
  function renderExecView(state) {
650
+ const { reset, bold, dim, green, yellow, cyan } = (0, theme_1.getActivePalette)();
638
651
  const columns = process.stdout.columns ?? 80;
639
652
  const rows = process.stdout.rows ?? 24;
640
653
  const buf = [];
641
- for (const line of LOGO) {
654
+ for (const line of renderLogo()) {
642
655
  buf.push(line);
643
656
  }
644
657
  buf.push(separatorLine(columns));
645
658
  buf.push(` ${renderLegend({ execMode: true })}`);
646
659
  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}`);
660
+ const runningIndicator = state.execChild ? `${yellow}running${reset}` : `${green}ready${reset}`;
661
+ const cwdInfo = state.execCwd ? ` ${dim}${state.execCwd}${reset}` : '';
662
+ buf.push(` ${cyan}exec ${bold}${serviceName}${reset} ${runningIndicator}${cwdInfo}`);
650
663
  const headerHeight = buf.length;
651
664
  // Reserve 1 line for the prompt at the bottom
652
665
  const availableRows = Math.max(1, rows - headerHeight - 1);
@@ -662,7 +675,7 @@ function renderExecView(state) {
662
675
  buf.push('');
663
676
  }
664
677
  // Command prompt
665
- buf.push(`${FG_GREEN}$ ${RESET}${state.execInput}${BOLD}_${RESET}`);
678
+ buf.push(`${green}$ ${reset}${state.execInput}${bold}_${reset}`);
666
679
  return buf.join(exports.CLEAR_EOL + '\n');
667
680
  }
668
681
  //# sourceMappingURL=renderer.js.map