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