@leo000001/opencode-quota-sidebar 4.1.0 → 4.1.1
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/README.md +12 -6
- package/README.zh-CN.md +522 -516
- package/dist/cli.d.ts +23 -5
- package/dist/cli.js +256 -31
- package/dist/tui.tsx +5 -3
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ type CliServerCommand = {
|
|
|
12
12
|
shell?: boolean;
|
|
13
13
|
};
|
|
14
14
|
type SpawnedCliServerProcess = ReturnType<typeof spawn>;
|
|
15
|
+
type CliPortResolver = (preferredPort: number, attemptedPorts: ReadonlySet<number>) => Promise<number>;
|
|
16
|
+
type StartedCliServer = {
|
|
17
|
+
url: string;
|
|
18
|
+
pid?: number;
|
|
19
|
+
close: () => void;
|
|
20
|
+
};
|
|
15
21
|
export declare function releaseCliServerProcess(proc: {
|
|
16
22
|
stdin?: {
|
|
17
23
|
destroy: () => void;
|
|
@@ -45,12 +51,24 @@ export declare function terminateCliServerProcess(proc: {
|
|
|
45
51
|
export declare function extractCliServerUrl(output: string): string | undefined;
|
|
46
52
|
export declare function parseCliArgs(argv: string[]): CliCommand;
|
|
47
53
|
export declare function cliBaseUrl(): string;
|
|
48
|
-
export declare function cliServerCommandCandidates(platform?: NodeJS.Platform): CliServerCommand[];
|
|
54
|
+
export declare function cliServerCommandCandidates(platform?: NodeJS.Platform, port?: number): CliServerCommand[];
|
|
55
|
+
export declare function isCliServerPortConflictFailure(failure: unknown): boolean;
|
|
56
|
+
export declare function registerCliShutdownCleanup(cleanup: () => void, options?: {
|
|
57
|
+
targetProcess?: NodeJS.Process;
|
|
58
|
+
killProcess?: typeof process.kill;
|
|
59
|
+
}): () => void;
|
|
60
|
+
export declare function reserveCliServerPort(preferredPort?: number, attemptedPorts?: ReadonlySet<number>): Promise<number>;
|
|
49
61
|
export declare function closeCliServerProcess(proc: SpawnedCliServerProcess, platform?: NodeJS.Platform, killProcess?: typeof process.kill, spawnProcess?: typeof spawn): void;
|
|
50
|
-
export declare function tryStartCliOpencodeServer(candidate: CliServerCommand, spawnProcess?: typeof spawn, closeProcess?: typeof closeCliServerProcess): Promise<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
export declare function tryStartCliOpencodeServer(candidate: CliServerCommand, spawnProcess?: typeof spawn, closeProcess?: typeof closeCliServerProcess): Promise<StartedCliServer>;
|
|
63
|
+
export declare function spawnCliServerWatchdog(targetPid: number | undefined, ttlMs?: number, spawnProcess?: typeof spawn, nodeExecPath?: string): import("child_process").ChildProcess | undefined;
|
|
64
|
+
export declare function startCliOpencodeServer(options?: {
|
|
65
|
+
platform?: NodeJS.Platform;
|
|
66
|
+
spawnProcess?: typeof spawn;
|
|
67
|
+
spawnWatchdogProcess?: typeof spawn;
|
|
68
|
+
closeProcess?: typeof closeCliServerProcess;
|
|
69
|
+
reservePort?: CliPortResolver;
|
|
70
|
+
tempServerTtlMs?: number;
|
|
71
|
+
}): Promise<StartedCliServer>;
|
|
54
72
|
export declare function runCli(argv: string[]): Promise<string>;
|
|
55
73
|
export declare function cliExitCodeForError(message: string): 0 | 1;
|
|
56
74
|
export declare function cliShouldRunMain(argv1?: string, modulePath?: string, resolvePath?: (filePath: string) => string): boolean;
|
package/dist/cli.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { realpathSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawn } from 'node:child_process';
|
|
5
|
+
import { createServer } from 'node:net';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import { createOpencodeClient } from '@opencode-ai/sdk/client';
|
|
7
8
|
import { cliCurrentLabel, renderCliDashboard, renderCliHistoryDashboard, } from './cli_render.js';
|
|
@@ -38,8 +39,16 @@ function strictFilterHistoryProviders(history, allowedProviderIDs) {
|
|
|
38
39
|
return filterHistoryProvidersForDisplay(history, allowedProviderIDs);
|
|
39
40
|
}
|
|
40
41
|
const DEFAULT_OPENCODE_BASE_URL = 'http://localhost:4096';
|
|
41
|
-
const
|
|
42
|
+
const DEFAULT_OPENCODE_HOST = '127.0.0.1';
|
|
43
|
+
const DEFAULT_OPENCODE_PORT = 4096;
|
|
44
|
+
const CLI_SERVER_TIMEOUT_MS = 60_000;
|
|
42
45
|
const CLI_FORCE_EXIT_DELAY_MS = 100;
|
|
46
|
+
const CLI_SERVER_PORT_RETRY_LIMIT = 5;
|
|
47
|
+
const CLI_PORT_RESERVE_RETRY_LIMIT = 16;
|
|
48
|
+
const CLI_TEMP_SERVER_TTL_MS = 10 * 60_000;
|
|
49
|
+
const CLI_TEMP_SERVER_HINT = 'CLI note: if the temporary OpenCode server keeps failing, run `opencode serve` manually to inspect logs, or set `OPENCODE_BASE_URL`.';
|
|
50
|
+
const CLI_SHUTDOWN_HOOK_KEY = Symbol('quota-sidebar.cliShutdownHook');
|
|
51
|
+
const CLI_SHUTDOWN_CALLBACKS_KEY = Symbol('quota-sidebar.cliShutdownCallbacks');
|
|
43
52
|
export function releaseCliServerProcess(proc) {
|
|
44
53
|
// The CLI only needs the child pipes until the server prints its listen URL.
|
|
45
54
|
// After that, unref/destroy them so the parent process can exit cleanly.
|
|
@@ -174,24 +183,158 @@ export function cliBaseUrl() {
|
|
|
174
183
|
function isDefaultBaseUrl() {
|
|
175
184
|
return !process.env.OPENCODE_BASE_URL?.trim();
|
|
176
185
|
}
|
|
177
|
-
export function cliServerCommandCandidates(platform = process.platform) {
|
|
178
|
-
const directArgs = [
|
|
186
|
+
export function cliServerCommandCandidates(platform = process.platform, port = DEFAULT_OPENCODE_PORT) {
|
|
187
|
+
const directArgs = [
|
|
188
|
+
'serve',
|
|
189
|
+
`--hostname=${DEFAULT_OPENCODE_HOST}`,
|
|
190
|
+
`--port=${port}`,
|
|
191
|
+
];
|
|
192
|
+
const directCommand = `opencode serve --hostname=${DEFAULT_OPENCODE_HOST} --port=${port}`;
|
|
179
193
|
if (platform === 'win32') {
|
|
180
194
|
return [
|
|
181
195
|
{ command: 'opencode.cmd', args: directArgs },
|
|
182
196
|
{
|
|
183
|
-
command:
|
|
197
|
+
command: directCommand,
|
|
184
198
|
args: [],
|
|
185
199
|
shell: true,
|
|
186
200
|
},
|
|
187
201
|
{
|
|
188
202
|
command: 'bash',
|
|
189
|
-
args: ['-lc',
|
|
203
|
+
args: ['-lc', directCommand],
|
|
190
204
|
},
|
|
191
205
|
];
|
|
192
206
|
}
|
|
193
207
|
return [{ command: 'opencode', args: directArgs }];
|
|
194
208
|
}
|
|
209
|
+
function cliServerFailureOutput(failure) {
|
|
210
|
+
if (typeof failure !== 'object' ||
|
|
211
|
+
failure === null ||
|
|
212
|
+
!('output' in failure)) {
|
|
213
|
+
return '';
|
|
214
|
+
}
|
|
215
|
+
const output = failure.output;
|
|
216
|
+
return typeof output === 'string' ? output : '';
|
|
217
|
+
}
|
|
218
|
+
function cliServerFailureError(failure) {
|
|
219
|
+
if (typeof failure === 'object' && failure !== null && 'error' in failure) {
|
|
220
|
+
const error = failure.error;
|
|
221
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
222
|
+
}
|
|
223
|
+
return failure instanceof Error ? failure : new Error(String(failure));
|
|
224
|
+
}
|
|
225
|
+
function isRecoverableCliServerFailure(failure) {
|
|
226
|
+
return (typeof failure === 'object' &&
|
|
227
|
+
failure !== null &&
|
|
228
|
+
'recoverable' in failure &&
|
|
229
|
+
failure.recoverable === true);
|
|
230
|
+
}
|
|
231
|
+
export function isCliServerPortConflictFailure(failure) {
|
|
232
|
+
const text = [
|
|
233
|
+
cliServerFailureError(failure).message,
|
|
234
|
+
cliServerFailureOutput(failure),
|
|
235
|
+
]
|
|
236
|
+
.filter(Boolean)
|
|
237
|
+
.join('\n');
|
|
238
|
+
return /eaddrinuse|address already in use|port .*already in use|listen .*in use/i.test(text);
|
|
239
|
+
}
|
|
240
|
+
function describeCliError(error) {
|
|
241
|
+
return error instanceof Error ? error.message : String(error);
|
|
242
|
+
}
|
|
243
|
+
function formatCliTempServerFailure(prefix, error) {
|
|
244
|
+
return `${prefix}: ${describeCliError(error)}\n\n${CLI_TEMP_SERVER_HINT}`;
|
|
245
|
+
}
|
|
246
|
+
function runCliShutdownCallbacks(targetProcess) {
|
|
247
|
+
const callbacks = targetProcess[CLI_SHUTDOWN_CALLBACKS_KEY];
|
|
248
|
+
if (!callbacks)
|
|
249
|
+
return;
|
|
250
|
+
for (const callback of Array.from(callbacks)) {
|
|
251
|
+
try {
|
|
252
|
+
callback();
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
// Best-effort cleanup for CLI temp servers.
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
export function registerCliShutdownCleanup(cleanup, options) {
|
|
260
|
+
const targetProcess = options?.targetProcess ||
|
|
261
|
+
process;
|
|
262
|
+
const callbacks = targetProcess[CLI_SHUTDOWN_CALLBACKS_KEY] ||
|
|
263
|
+
(targetProcess[CLI_SHUTDOWN_CALLBACKS_KEY] = new Set());
|
|
264
|
+
callbacks.add(cleanup);
|
|
265
|
+
if (!targetProcess[CLI_SHUTDOWN_HOOK_KEY]) {
|
|
266
|
+
targetProcess[CLI_SHUTDOWN_HOOK_KEY] = true;
|
|
267
|
+
targetProcess.once('exit', () => {
|
|
268
|
+
runCliShutdownCallbacks(targetProcess);
|
|
269
|
+
});
|
|
270
|
+
targetProcess.once('uncaughtExceptionMonitor', () => {
|
|
271
|
+
runCliShutdownCallbacks(targetProcess);
|
|
272
|
+
});
|
|
273
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
274
|
+
targetProcess.once(signal, () => {
|
|
275
|
+
runCliShutdownCallbacks(targetProcess);
|
|
276
|
+
if (typeof targetProcess.pid === 'number' && targetProcess.pid > 0) {
|
|
277
|
+
;
|
|
278
|
+
(options?.killProcess ?? process.kill)(targetProcess.pid, signal);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return () => {
|
|
284
|
+
callbacks.delete(cleanup);
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function tryReserveCliServerPort(port) {
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
const server = createServer();
|
|
290
|
+
let settled = false;
|
|
291
|
+
const finish = (value, error) => {
|
|
292
|
+
if (settled)
|
|
293
|
+
return;
|
|
294
|
+
settled = true;
|
|
295
|
+
server.removeAllListeners();
|
|
296
|
+
if (error) {
|
|
297
|
+
reject(error);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
resolve(value);
|
|
301
|
+
};
|
|
302
|
+
server.once('error', (error) => {
|
|
303
|
+
const code = error.code;
|
|
304
|
+
if (code === 'EADDRINUSE' || code === 'EACCES') {
|
|
305
|
+
finish(undefined);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
finish(undefined, error);
|
|
309
|
+
});
|
|
310
|
+
server.listen({ host: DEFAULT_OPENCODE_HOST, port, exclusive: true }, () => {
|
|
311
|
+
const address = server.address();
|
|
312
|
+
const resolvedPort = typeof address === 'object' && address ? address.port : port;
|
|
313
|
+
server.close((error) => {
|
|
314
|
+
if (error) {
|
|
315
|
+
finish(undefined, error);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
finish(resolvedPort);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
server.unref();
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
export async function reserveCliServerPort(preferredPort = DEFAULT_OPENCODE_PORT, attemptedPorts = new Set()) {
|
|
325
|
+
if (!attemptedPorts.has(preferredPort)) {
|
|
326
|
+
const preferred = await tryReserveCliServerPort(preferredPort);
|
|
327
|
+
if (preferred !== undefined)
|
|
328
|
+
return preferred;
|
|
329
|
+
}
|
|
330
|
+
for (let attempt = 0; attempt < CLI_PORT_RESERVE_RETRY_LIMIT; attempt++) {
|
|
331
|
+
const fallback = await tryReserveCliServerPort(0);
|
|
332
|
+
if (fallback === undefined || attemptedPorts.has(fallback))
|
|
333
|
+
continue;
|
|
334
|
+
return fallback;
|
|
335
|
+
}
|
|
336
|
+
throw new Error('Failed to reserve a local port for the temporary OpenCode server');
|
|
337
|
+
}
|
|
195
338
|
function releaseCliServerPipes(proc, inspect, onError, onExit) {
|
|
196
339
|
if (inspect) {
|
|
197
340
|
proc.stdout?.removeListener('data', inspect);
|
|
@@ -307,35 +450,97 @@ export async function tryStartCliOpencodeServer(candidate, spawnProcess = spawn,
|
|
|
307
450
|
});
|
|
308
451
|
return {
|
|
309
452
|
url,
|
|
453
|
+
pid: proc.pid,
|
|
310
454
|
close: () => closeProcess(proc),
|
|
311
455
|
};
|
|
312
456
|
}
|
|
313
|
-
|
|
314
|
-
|
|
457
|
+
export function spawnCliServerWatchdog(targetPid, ttlMs = CLI_TEMP_SERVER_TTL_MS, spawnProcess = spawn, nodeExecPath = process.execPath) {
|
|
458
|
+
if (typeof targetPid !== 'number' ||
|
|
459
|
+
targetPid <= 0 ||
|
|
460
|
+
!Number.isFinite(ttlMs) ||
|
|
461
|
+
ttlMs <= 0) {
|
|
462
|
+
return undefined;
|
|
463
|
+
}
|
|
464
|
+
const script = [
|
|
465
|
+
"const { spawn } = require('node:child_process')",
|
|
466
|
+
`const pid = ${JSON.stringify(targetPid)}`,
|
|
467
|
+
`const ttl = ${JSON.stringify(Math.floor(ttlMs))}`,
|
|
468
|
+
'setTimeout(() => {',
|
|
469
|
+
" if (process.platform === 'win32') {",
|
|
470
|
+
" const killer = spawn('taskkill', ['/PID', String(pid), '/T', '/F'], { stdio: 'ignore', windowsHide: true })",
|
|
471
|
+
' killer.unref?.()',
|
|
472
|
+
' process.exit(0)',
|
|
473
|
+
' }',
|
|
474
|
+
' try {',
|
|
475
|
+
" process.kill(-pid, 'SIGTERM')",
|
|
476
|
+
' } catch (error) {',
|
|
477
|
+
" if (!error || (typeof error === 'object' && error !== null && error.code !== 'ESRCH')) {",
|
|
478
|
+
" try { process.kill(pid, 'SIGTERM') } catch {}",
|
|
479
|
+
' }',
|
|
480
|
+
' }',
|
|
481
|
+
' process.exit(0)',
|
|
482
|
+
'}, ttl)',
|
|
483
|
+
].join('\n');
|
|
484
|
+
const proc = spawnProcess(nodeExecPath, ['-e', script], {
|
|
485
|
+
stdio: 'ignore',
|
|
486
|
+
detached: true,
|
|
487
|
+
windowsHide: true,
|
|
488
|
+
});
|
|
489
|
+
proc.unref();
|
|
490
|
+
return proc;
|
|
491
|
+
}
|
|
492
|
+
export async function startCliOpencodeServer(options) {
|
|
493
|
+
const platform = options?.platform ?? process.platform;
|
|
494
|
+
const spawnProcess = options?.spawnProcess ?? spawn;
|
|
495
|
+
const spawnWatchdogProcess = options?.spawnWatchdogProcess ?? spawn;
|
|
496
|
+
const closeProcess = options?.closeProcess ?? closeCliServerProcess;
|
|
497
|
+
const resolvePort = options?.reservePort ?? reserveCliServerPort;
|
|
498
|
+
const tempServerTtlMs = options?.tempServerTtlMs ?? CLI_TEMP_SERVER_TTL_MS;
|
|
315
499
|
let lastError;
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
:
|
|
330
|
-
|
|
500
|
+
const attemptedPorts = new Set();
|
|
501
|
+
for (let attempt = 0; attempt < CLI_SERVER_PORT_RETRY_LIMIT; attempt++) {
|
|
502
|
+
const port = await resolvePort(DEFAULT_OPENCODE_PORT, attemptedPorts);
|
|
503
|
+
attemptedPorts.add(port);
|
|
504
|
+
for (const candidate of cliServerCommandCandidates(platform, port)) {
|
|
505
|
+
try {
|
|
506
|
+
const server = await tryStartCliOpencodeServer(candidate, spawnProcess, closeProcess);
|
|
507
|
+
let watchdogProc;
|
|
508
|
+
try {
|
|
509
|
+
watchdogProc = spawnCliServerWatchdog(server.pid, tempServerTtlMs, spawnWatchdogProcess);
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
server.close();
|
|
513
|
+
throw new Error(`Failed to start temporary OpenCode server watchdog: ${describeCliError(error)}`);
|
|
514
|
+
}
|
|
515
|
+
const stopWatchdog = () => {
|
|
516
|
+
if (!watchdogProc)
|
|
517
|
+
return;
|
|
518
|
+
const proc = watchdogProc;
|
|
519
|
+
watchdogProc = undefined;
|
|
520
|
+
closeProcess(proc);
|
|
521
|
+
};
|
|
522
|
+
return {
|
|
523
|
+
...server,
|
|
524
|
+
close: () => {
|
|
525
|
+
stopWatchdog();
|
|
526
|
+
server.close();
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
catch (failure) {
|
|
531
|
+
lastError = failure;
|
|
532
|
+
if (isCliServerPortConflictFailure(failure))
|
|
533
|
+
break;
|
|
534
|
+
if (!isRecoverableCliServerFailure(failure)) {
|
|
535
|
+
throw cliServerFailureError(failure);
|
|
536
|
+
}
|
|
331
537
|
}
|
|
332
538
|
}
|
|
539
|
+
if (!isCliServerPortConflictFailure(lastError))
|
|
540
|
+
break;
|
|
333
541
|
}
|
|
334
|
-
|
|
335
|
-
? lastError
|
|
336
|
-
: lastError;
|
|
337
|
-
throw error instanceof Error
|
|
338
|
-
? error
|
|
542
|
+
throw lastError
|
|
543
|
+
? cliServerFailureError(lastError)
|
|
339
544
|
: new Error('Failed to start OpenCode server');
|
|
340
545
|
}
|
|
341
546
|
async function resolvePathInfo(directory) {
|
|
@@ -360,7 +565,27 @@ async function resolvePathInfo(directory) {
|
|
|
360
565
|
if (!isDefaultBaseUrl()) {
|
|
361
566
|
throw new Error(`Failed to connect to OpenCode API at ${cliBaseUrl()}: ${error instanceof Error ? error.message : String(error)}`);
|
|
362
567
|
}
|
|
363
|
-
|
|
568
|
+
let server;
|
|
569
|
+
try {
|
|
570
|
+
server = await startCliOpencodeServer();
|
|
571
|
+
}
|
|
572
|
+
catch (startError) {
|
|
573
|
+
throw new Error(formatCliTempServerFailure('Failed to auto-start a temporary OpenCode server', startError));
|
|
574
|
+
}
|
|
575
|
+
let closed = false;
|
|
576
|
+
const unregisterTempServerCleanup = registerCliShutdownCleanup(() => {
|
|
577
|
+
if (closed)
|
|
578
|
+
return;
|
|
579
|
+
closed = true;
|
|
580
|
+
server.close();
|
|
581
|
+
});
|
|
582
|
+
const closeTempServer = () => {
|
|
583
|
+
if (closed)
|
|
584
|
+
return;
|
|
585
|
+
closed = true;
|
|
586
|
+
unregisterTempServerCleanup();
|
|
587
|
+
server.close();
|
|
588
|
+
};
|
|
364
589
|
try {
|
|
365
590
|
const client = createOpencodeClient({
|
|
366
591
|
directory,
|
|
@@ -375,12 +600,12 @@ async function resolvePathInfo(directory) {
|
|
|
375
600
|
client,
|
|
376
601
|
worktree: data.worktree || directory,
|
|
377
602
|
directory: data.directory || directory,
|
|
378
|
-
close:
|
|
603
|
+
close: closeTempServer,
|
|
379
604
|
};
|
|
380
605
|
}
|
|
381
606
|
catch (innerError) {
|
|
382
|
-
|
|
383
|
-
throw innerError;
|
|
607
|
+
closeTempServer();
|
|
608
|
+
throw new Error(formatCliTempServerFailure(`Failed to query the temporary OpenCode server at ${server.url}`, innerError));
|
|
384
609
|
}
|
|
385
610
|
}
|
|
386
611
|
}
|
package/dist/tui.tsx
CHANGED
|
@@ -253,7 +253,6 @@ function QuotaGroupBlock(props: {
|
|
|
253
253
|
group: SidebarQuotaGroup
|
|
254
254
|
bullet: boolean
|
|
255
255
|
}) {
|
|
256
|
-
const detailColor = quotaToneColor(props.api, props.group.tone)
|
|
257
256
|
const content = (
|
|
258
257
|
<box gap={0}>
|
|
259
258
|
<text>
|
|
@@ -261,11 +260,14 @@ function QuotaGroupBlock(props: {
|
|
|
261
260
|
{props.group.shortLabel}
|
|
262
261
|
</span>
|
|
263
262
|
<Show when={props.group.detail}>
|
|
264
|
-
<span style={{ fg:
|
|
263
|
+
<span style={{ fg: props.api.theme.current.textMuted }}>
|
|
264
|
+
{' '}
|
|
265
|
+
{props.group.detail}
|
|
266
|
+
</span>
|
|
265
267
|
</Show>
|
|
266
268
|
</text>
|
|
267
269
|
<For each={props.group.continuationLines}>
|
|
268
|
-
{(line) => <text fg={
|
|
270
|
+
{(line) => <text fg={props.api.theme.current.textMuted}>{line}</text>}
|
|
269
271
|
</For>
|
|
270
272
|
</box>
|
|
271
273
|
)
|
package/package.json
CHANGED