@gajae-code/coding-agent 0.16.1 → 0.16.4
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/CHANGELOG.md +70 -0
- package/dist/types/cli/args.d.ts +2 -2
- package/dist/types/cli/root-flags.d.ts +4 -2
- package/dist/types/cli.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/config/autorouting-tier-map.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +9 -0
- package/dist/types/config/settings-schema.d.ts +29 -1
- package/dist/types/discovery/claude-plugins.d.ts +3 -1
- package/dist/types/discovery/helpers.d.ts +2 -0
- package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/settings-selector.d.ts +6 -1
- package/dist/types/modes/components/status-line/command.d.ts +35 -0
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +5 -0
- package/dist/types/modes/components/tool-status-header.d.ts +14 -0
- package/dist/types/modes/controllers/event-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/runtime-mcp/client.d.ts +9 -1
- package/dist/types/runtime-mcp/manager.d.ts +7 -1
- package/dist/types/runtime-mcp/pool.d.ts +22 -3
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
- package/dist/types/runtime-mcp/types.d.ts +22 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
- package/dist/types/sdk/cli/rows.d.ts +7 -0
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/session-runtime.d.ts +23 -0
- package/dist/types/sdk/session-api-key.d.ts +26 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/setup/paseo/announce.d.ts +111 -0
- package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +2 -1
- package/dist/types/tools/fetch.d.ts +7 -0
- package/dist/types/tools/index.d.ts +8 -2
- package/dist/types/tools/path-utils.d.ts +2 -0
- package/dist/types/tools/search.d.ts +6 -0
- package/dist/types/utils/external-editor.d.ts +5 -0
- package/package.json +7 -7
- package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
- package/src/cli/args.ts +20 -9
- package/src/cli/root-flags.ts +5 -3
- package/src/config/autorouting-tier-map.ts +1 -0
- package/src/config/file-lock.ts +181 -17
- package/src/config/model-preset-registry.ts +5 -3
- package/src/config/model-profiles.ts +40 -0
- package/src/config/model-registry.ts +57 -11
- package/src/config/model-resolver.ts +48 -0
- package/src/config/settings-schema.ts +33 -1
- package/src/config/settings.ts +1 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
- package/src/discovery/builtin.ts +13 -4
- package/src/discovery/claude-plugins.ts +12 -4
- package/src/discovery/helpers.ts +10 -2
- package/src/extensibility/gjc-plugins/compiler.ts +208 -58
- package/src/extensibility/gjc-plugins/installer.ts +13 -12
- package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
- package/src/extensibility/gjc-plugins/paths.ts +13 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
- package/src/extensibility/runtime-skill-discovery.ts +77 -151
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +44 -16
- package/src/gjc-runtime/repository-binding.ts +1 -1
- package/src/gjc-runtime/session-state-lock.ts +94 -6
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/main.ts +72 -34
- package/src/modes/DESIGN.md +54 -0
- package/src/modes/acp/acp-agent.ts +29 -14
- package/src/modes/components/assistant-message.ts +22 -9
- package/src/modes/components/model-selector.ts +166 -33
- package/src/modes/components/settings-selector.ts +1075 -235
- package/src/modes/components/status-line/command.ts +190 -0
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/segments.ts +32 -5
- package/src/modes/components/status-line/types.ts +5 -0
- package/src/modes/components/tool-status-header.ts +393 -21
- package/src/modes/controllers/command-controller.ts +2 -0
- package/src/modes/controllers/event-controller.ts +148 -5
- package/src/modes/controllers/selector-controller.ts +21 -0
- package/src/modes/interactive-mode.ts +47 -1
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/system-prompt.md +8 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/search.md +2 -0
- package/src/runtime-mcp/client.ts +60 -17
- package/src/runtime-mcp/manager.ts +171 -14
- package/src/runtime-mcp/pool.test.ts +209 -0
- package/src/runtime-mcp/pool.ts +224 -23
- package/src/runtime-mcp/transports/stdio.ts +158 -24
- package/src/runtime-mcp/types.ts +24 -0
- package/src/sdk/acp/adapter.ts +1 -1
- package/src/sdk/broker/broker.ts +5 -4
- package/src/sdk/bus/chat-daemon-control.ts +1 -1
- package/src/sdk/bus/chat-daemon-runtime.ts +44 -1
- package/src/sdk/bus/slack-daemon.ts +92 -2
- package/src/sdk/bus/slack-live-provider.ts +12 -0
- package/src/sdk/bus/slack-provider.ts +5 -0
- package/src/sdk/cli/rows.ts +47 -2
- package/src/sdk/cli/session-cli.ts +47 -23
- package/src/sdk/host/events.ts +25 -2
- package/src/sdk/host/host.ts +4 -1
- package/src/sdk/host/session-runtime.test.ts +36 -25
- package/src/sdk/host/session-runtime.ts +279 -43
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +60 -37
- package/src/sdk/session-api-key.ts +94 -0
- package/src/sdk/session.ts +73 -30
- package/src/sdk/transport/serve-cli.ts +3 -3
- package/src/session/agent-session.ts +123 -38
- package/src/session/contribution-prep.ts +214 -21
- package/src/setup/paseo/announce.ts +450 -0
- package/src/setup/paseo/setup-deps.ts +15 -3
- package/src/slash-commands/builtin-registry.ts +1 -0
- package/src/system-prompt.ts +4 -0
- package/src/task/executor.ts +2 -1
- package/src/task/index.ts +17 -4
- package/src/tools/bash.ts +50 -21
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +29 -1
- package/src/tools/index.ts +8 -2
- package/src/tools/job.ts +8 -2
- package/src/tools/monitor.ts +8 -2
- package/src/tools/path-utils.ts +7 -0
- package/src/tools/search.ts +116 -58
- package/src/tools/skill.ts +22 -2
- package/src/tools/subagent.ts +3 -1
- package/src/tools/tool-catalog.generated.ts +9 -2
- package/src/utils/external-editor.ts +16 -2
- package/src/web/insane/bridge.ts +113 -23
- package/src/web/search/providers/perplexity.ts +102 -41
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
|
|
3
|
+
import { resolveOAuthStorageProvider } from "@gajae-code/ai/core";
|
|
3
4
|
import { type Component, truncateToWidth, visibleWidth } from "@gajae-code/tui";
|
|
4
|
-
import { formatCount, getProjectDir } from "@gajae-code/utils";
|
|
5
|
+
import { formatCount, getProjectDir, logger } from "@gajae-code/utils";
|
|
6
|
+
import { getShellConfig } from "@gajae-code/utils/shell-config";
|
|
5
7
|
import {
|
|
6
8
|
type AppKeybinding,
|
|
7
9
|
KEYBINDINGS,
|
|
@@ -19,6 +21,11 @@ import type { ActionRegistry, FocusDomain } from "../action-registry";
|
|
|
19
21
|
import { EMPTY_JOBS_SNAPSHOT, type JobsSnapshot } from "../jobs-observer";
|
|
20
22
|
import { sanitizeStatusText } from "../shared";
|
|
21
23
|
import { renderSkillHudBar } from "./skill-hud/render";
|
|
24
|
+
import {
|
|
25
|
+
normalizeStatusLineCommandOptions,
|
|
26
|
+
runStatusLineCommand,
|
|
27
|
+
type StatusLineCommandOptions,
|
|
28
|
+
} from "./status-line/command";
|
|
22
29
|
import { lookupCurrentPrCached } from "./status-line/gh";
|
|
23
30
|
import {
|
|
24
31
|
canReuseCachedPr,
|
|
@@ -40,6 +47,7 @@ export interface StatusLineSegmentOptions {
|
|
|
40
47
|
git?: { showBranch?: boolean; showStaged?: boolean; showUnstaged?: boolean; showUntracked?: boolean };
|
|
41
48
|
time?: { format?: "12h" | "24h"; showSeconds?: boolean };
|
|
42
49
|
usage?: { mode?: "used" | "remaining" };
|
|
50
|
+
command?: StatusLineCommandOptions;
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
export interface StatusLineSettings {
|
|
@@ -49,6 +57,7 @@ export interface StatusLineSettings {
|
|
|
49
57
|
separator?: StatusLineSeparatorStyle;
|
|
50
58
|
segmentOptions?: StatusLineSegmentOptions;
|
|
51
59
|
previewHighlightSegment?: StatusLineSegmentId;
|
|
60
|
+
previewHighlightStyle?: "focus" | "selected";
|
|
52
61
|
showHookStatus?: boolean;
|
|
53
62
|
showSkillHud?: boolean;
|
|
54
63
|
showActionHints?: boolean;
|
|
@@ -62,6 +71,7 @@ export interface StatusLineComponentOptions {
|
|
|
62
71
|
getKeybindings?: () => KeybindingsManager;
|
|
63
72
|
focusDomain?: FocusDomain;
|
|
64
73
|
keyDisplayContext?: KeyDisplayContext;
|
|
74
|
+
onUpdate?: () => void;
|
|
65
75
|
}
|
|
66
76
|
|
|
67
77
|
export interface StatusLineActionHint {
|
|
@@ -69,6 +79,14 @@ export interface StatusLineActionHint {
|
|
|
69
79
|
content: string;
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
export interface StatusLinePreviewParts {
|
|
83
|
+
left: string[];
|
|
84
|
+
leftIds: StatusLineSegmentId[];
|
|
85
|
+
right: string[];
|
|
86
|
+
rightIds: (StatusLineSegmentId | null)[];
|
|
87
|
+
separator: SeparatorDef;
|
|
88
|
+
}
|
|
89
|
+
|
|
72
90
|
const ACTION_HINT_PRIORITY: readonly AppKeybinding[] = [
|
|
73
91
|
"app.message.sendNow",
|
|
74
92
|
"app.message.followUp",
|
|
@@ -136,6 +154,7 @@ interface CollectedStatusSegments {
|
|
|
136
154
|
/** Parallel to `right`; null for action hints, job counts and the version tag. */
|
|
137
155
|
rightSegIds: (StatusLineSegmentId | null)[];
|
|
138
156
|
previewHighlightSegment: StatusLineSegmentId | undefined;
|
|
157
|
+
previewHighlightStyle: "focus" | "selected";
|
|
139
158
|
sessionAccent: boolean | undefined;
|
|
140
159
|
leftSepWidth: number;
|
|
141
160
|
rightSepWidth: number;
|
|
@@ -155,6 +174,8 @@ export class StatusLineComponent implements Component {
|
|
|
155
174
|
#branchInFlight = false;
|
|
156
175
|
#gitWatcher: fs.FSWatcher | null = null;
|
|
157
176
|
#onBranchChange: (() => void) | null = null;
|
|
177
|
+
#onUpdate: (() => void) | null = null;
|
|
178
|
+
#disposed = false;
|
|
158
179
|
#autoCompactEnabled: boolean = true;
|
|
159
180
|
#hookStatuses: Map<string, string> = new Map();
|
|
160
181
|
#subagentCount: number = 0;
|
|
@@ -195,9 +216,22 @@ export class StatusLineComponent implements Component {
|
|
|
195
216
|
|
|
196
217
|
// Provider usage caching (5-min TTL, OAuth/sub only)
|
|
197
218
|
#cachedUsage: SegmentContext["usage"] = null;
|
|
219
|
+
#cachedUsageReports: unknown = null;
|
|
220
|
+
#cachedUsageProvider: string | undefined;
|
|
198
221
|
#usageFetchedAt = 0;
|
|
199
222
|
#usageInFlight = false;
|
|
200
223
|
|
|
224
|
+
// Configured command segment cache. Command execution is always backgrounded;
|
|
225
|
+
// rendering only reads these bounded, last-known values.
|
|
226
|
+
#commandConfigKey: string | undefined;
|
|
227
|
+
#commandOutput: string | null = null;
|
|
228
|
+
#commandFailed = false;
|
|
229
|
+
#commandFetchedAt = 0;
|
|
230
|
+
#commandInFlightKey: string | undefined;
|
|
231
|
+
#commandAbortController: AbortController | undefined;
|
|
232
|
+
#commandRefreshTimer: ReturnType<typeof setTimeout> | undefined;
|
|
233
|
+
#commandDiagnosticKey: string | undefined;
|
|
234
|
+
|
|
201
235
|
constructor(
|
|
202
236
|
private readonly session: AgentSession,
|
|
203
237
|
options: StatusLineComponentOptions = {},
|
|
@@ -218,6 +252,7 @@ export class StatusLineComponent implements Component {
|
|
|
218
252
|
this.#getKeybindings = options.getKeybindings;
|
|
219
253
|
this.#focusDomain = options.focusDomain ?? "composer";
|
|
220
254
|
this.#keyDisplayContext = options.keyDisplayContext;
|
|
255
|
+
this.#onUpdate = options.onUpdate ?? null;
|
|
221
256
|
}
|
|
222
257
|
|
|
223
258
|
updateSettings(settings: StatusLineSettings): void {
|
|
@@ -300,12 +335,175 @@ export class StatusLineComponent implements Component {
|
|
|
300
335
|
}
|
|
301
336
|
|
|
302
337
|
dispose(): void {
|
|
338
|
+
this.#disposed = true;
|
|
339
|
+
this.#onBranchChange = null;
|
|
340
|
+
this.#onUpdate = null;
|
|
341
|
+
this.#cancelCommandExecution();
|
|
342
|
+
this.#clearCommandRefreshTimer();
|
|
303
343
|
if (this.#gitWatcher) {
|
|
304
344
|
this.#gitWatcher.close();
|
|
305
345
|
this.#gitWatcher = null;
|
|
306
346
|
}
|
|
307
347
|
}
|
|
308
348
|
|
|
349
|
+
#notifyUpdate(): void {
|
|
350
|
+
if (this.#onUpdate) this.#onUpdate();
|
|
351
|
+
else this.#onBranchChange?.();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#logCommandDiagnostic(key: string, message: string, details: Record<string, unknown> = {}): void {
|
|
355
|
+
if (this.#commandDiagnosticKey === key) return;
|
|
356
|
+
this.#commandDiagnosticKey = key;
|
|
357
|
+
logger.warn(message, details);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#clearCommandRefreshTimer(): void {
|
|
361
|
+
if (this.#commandRefreshTimer) {
|
|
362
|
+
clearTimeout(this.#commandRefreshTimer);
|
|
363
|
+
this.#commandRefreshTimer = undefined;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#cancelCommandExecution(): void {
|
|
368
|
+
this.#commandAbortController?.abort();
|
|
369
|
+
this.#commandAbortController = undefined;
|
|
370
|
+
this.#commandInFlightKey = undefined;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
#disableCommandSegment(): void {
|
|
374
|
+
this.#clearCommandRefreshTimer();
|
|
375
|
+
this.#cancelCommandExecution();
|
|
376
|
+
this.#commandConfigKey = undefined;
|
|
377
|
+
this.#commandOutput = null;
|
|
378
|
+
this.#commandFailed = false;
|
|
379
|
+
this.#commandFetchedAt = 0;
|
|
380
|
+
this.#commandDiagnosticKey = undefined;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#armCommandRefreshTimer(key: string, refreshMs: number): void {
|
|
384
|
+
this.#clearCommandRefreshTimer();
|
|
385
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
386
|
+
this.#commandRefreshTimer = setTimeout(
|
|
387
|
+
() => {
|
|
388
|
+
this.#commandRefreshTimer = undefined;
|
|
389
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
390
|
+
this.#notifyUpdate();
|
|
391
|
+
},
|
|
392
|
+
Math.max(0, refreshMs),
|
|
393
|
+
);
|
|
394
|
+
this.#commandRefreshTimer.unref?.();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
#trustedCommandOptions(): StatusLineCommandOptions | undefined {
|
|
398
|
+
const globalLeft = this.session.settings.getGlobal("statusLine.leftSegments");
|
|
399
|
+
const globalRight = this.session.settings.getGlobal("statusLine.rightSegments");
|
|
400
|
+
const includesCommand = (value: unknown): boolean => Array.isArray(value) && value.includes("command");
|
|
401
|
+
if (!includesCommand(globalLeft) && !includesCommand(globalRight)) return undefined;
|
|
402
|
+
|
|
403
|
+
const globalSegmentOptions = this.session.settings.getGlobal("statusLine.segmentOptions");
|
|
404
|
+
if (!globalSegmentOptions || typeof globalSegmentOptions !== "object" || Array.isArray(globalSegmentOptions)) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
const commandOptions = (globalSegmentOptions as Record<string, unknown>).command;
|
|
408
|
+
const resolved = normalizeStatusLineCommandOptions(commandOptions);
|
|
409
|
+
return resolved.command ? resolved : undefined;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#refreshCommandInBackground(options: StatusLineCommandOptions | undefined): string {
|
|
413
|
+
const resolved = normalizeStatusLineCommandOptions(options);
|
|
414
|
+
const key = JSON.stringify(resolved);
|
|
415
|
+
if (this.#commandConfigKey !== key) {
|
|
416
|
+
this.#clearCommandRefreshTimer();
|
|
417
|
+
this.#cancelCommandExecution();
|
|
418
|
+
this.#commandConfigKey = key;
|
|
419
|
+
this.#commandOutput = null;
|
|
420
|
+
this.#commandFailed = false;
|
|
421
|
+
this.#commandFetchedAt = 0;
|
|
422
|
+
this.#commandDiagnosticKey = undefined;
|
|
423
|
+
this.#renderedRowsCache = undefined;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (!resolved.command) {
|
|
427
|
+
this.#commandFailed = true;
|
|
428
|
+
this.#logCommandDiagnostic(key, "Status line command segment is configured without a command");
|
|
429
|
+
return key;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const now = Date.now();
|
|
433
|
+
if (this.#commandInFlightKey === key) return key;
|
|
434
|
+
const elapsed = now - this.#commandFetchedAt;
|
|
435
|
+
if (this.#commandFetchedAt > 0 && elapsed < resolved.refreshMs) {
|
|
436
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs - elapsed);
|
|
437
|
+
return key;
|
|
438
|
+
}
|
|
439
|
+
this.#clearCommandRefreshTimer();
|
|
440
|
+
|
|
441
|
+
this.#commandInFlightKey = key;
|
|
442
|
+
const abortController = new AbortController();
|
|
443
|
+
this.#commandAbortController = abortController;
|
|
444
|
+
let shellConfig: { shell: string; args: string[]; env: Record<string, string> };
|
|
445
|
+
try {
|
|
446
|
+
shellConfig = getShellConfig(this.session.settings.getGlobal("shellPath"));
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (this.#commandInFlightKey === key && this.#commandAbortController === abortController) {
|
|
449
|
+
this.#commandInFlightKey = undefined;
|
|
450
|
+
this.#commandAbortController = undefined;
|
|
451
|
+
}
|
|
452
|
+
this.#commandFailed = true;
|
|
453
|
+
this.#commandFetchedAt = Date.now();
|
|
454
|
+
this.#logCommandDiagnostic(key, "Status line command shell configuration failed", {
|
|
455
|
+
error: sanitizeStatusText(error instanceof Error ? error.message : String(error)).slice(0, 256),
|
|
456
|
+
});
|
|
457
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs);
|
|
458
|
+
return key;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
void runStatusLineCommand(resolved.command, {
|
|
462
|
+
cwd: getProjectDir(),
|
|
463
|
+
shell: shellConfig.shell,
|
|
464
|
+
shellArgs: shellConfig.args,
|
|
465
|
+
env: shellConfig.env,
|
|
466
|
+
timeoutMs: resolved.timeoutMs,
|
|
467
|
+
signal: abortController.signal,
|
|
468
|
+
})
|
|
469
|
+
.then(result => {
|
|
470
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
471
|
+
this.#commandFetchedAt = Date.now();
|
|
472
|
+
if (result.timedOut || result.exitCode !== 0) {
|
|
473
|
+
this.#commandFailed = true;
|
|
474
|
+
this.#logCommandDiagnostic(key, "Status line command failed", {
|
|
475
|
+
exitCode: result.exitCode,
|
|
476
|
+
timedOut: result.timedOut,
|
|
477
|
+
stderr: result.stderr,
|
|
478
|
+
outputTruncated: result.outputTruncated,
|
|
479
|
+
});
|
|
480
|
+
} else {
|
|
481
|
+
this.#commandOutput = result.stdout;
|
|
482
|
+
this.#commandFailed = false;
|
|
483
|
+
this.#commandDiagnosticKey = undefined;
|
|
484
|
+
}
|
|
485
|
+
this.#renderedRowsCache = undefined;
|
|
486
|
+
this.#notifyUpdate();
|
|
487
|
+
})
|
|
488
|
+
.catch(error => {
|
|
489
|
+
if (this.#disposed || this.#commandConfigKey !== key) return;
|
|
490
|
+
this.#commandFetchedAt = Date.now();
|
|
491
|
+
this.#commandFailed = true;
|
|
492
|
+
this.#logCommandDiagnostic(key, "Status line command could not start", {
|
|
493
|
+
error: sanitizeStatusText(error instanceof Error ? error.message : String(error)).slice(0, 256),
|
|
494
|
+
});
|
|
495
|
+
this.#renderedRowsCache = undefined;
|
|
496
|
+
this.#notifyUpdate();
|
|
497
|
+
})
|
|
498
|
+
.finally(() => {
|
|
499
|
+
if (this.#commandInFlightKey !== key || this.#commandAbortController !== abortController) return;
|
|
500
|
+
this.#commandInFlightKey = undefined;
|
|
501
|
+
this.#commandAbortController = undefined;
|
|
502
|
+
this.#armCommandRefreshTimer(key, resolved.refreshMs);
|
|
503
|
+
});
|
|
504
|
+
return key;
|
|
505
|
+
}
|
|
506
|
+
|
|
309
507
|
invalidate(): void {
|
|
310
508
|
this.#invalidateGitCaches();
|
|
311
509
|
}
|
|
@@ -502,6 +700,8 @@ export class StatusLineComponent implements Component {
|
|
|
502
700
|
void fetcher
|
|
503
701
|
.call(this.session)
|
|
504
702
|
.then(reports => {
|
|
703
|
+
this.#cachedUsageReports = reports;
|
|
704
|
+
this.#cachedUsageProvider = this.#activeUsageProvider();
|
|
505
705
|
this.#cachedUsage = this.#normalizeUsageReports(reports);
|
|
506
706
|
this.#usageFetchedAt = Date.now();
|
|
507
707
|
if (this.#onBranchChange) {
|
|
@@ -521,7 +721,9 @@ export class StatusLineComponent implements Component {
|
|
|
521
721
|
}
|
|
522
722
|
|
|
523
723
|
#normalizeUsageReports(reports: unknown): SegmentContext["usage"] {
|
|
524
|
-
|
|
724
|
+
const activeProvider = this.#activeUsageProvider();
|
|
725
|
+
|
|
726
|
+
if (!activeProvider || !Array.isArray(reports)) return null;
|
|
525
727
|
const windows: NonNullable<SegmentContext["usage"]>["windows"] = [];
|
|
526
728
|
const seen = new Set<string>();
|
|
527
729
|
const now = Date.now();
|
|
@@ -555,20 +757,24 @@ export class StatusLineComponent implements Component {
|
|
|
555
757
|
for (const report of reports) {
|
|
556
758
|
if (!report || typeof report !== "object") continue;
|
|
557
759
|
const provider = (report as { provider?: unknown }).provider;
|
|
558
|
-
const providerId = typeof provider === "string" ? provider : undefined;
|
|
760
|
+
const providerId = typeof provider === "string" ? resolveOAuthStorageProvider(provider) : undefined;
|
|
761
|
+
if (providerId !== activeProvider) continue;
|
|
559
762
|
const limits = (report as { limits?: unknown }).limits;
|
|
560
763
|
if (!Array.isArray(limits)) continue;
|
|
561
764
|
for (const limit of limits) {
|
|
562
765
|
if (!limit || typeof limit !== "object") continue;
|
|
563
766
|
const l = limit as {
|
|
564
767
|
id?: unknown;
|
|
565
|
-
scope?: { windowId?: string; tier?: string; modelId?: string };
|
|
768
|
+
scope?: { provider?: unknown; windowId?: string; tier?: string; modelId?: string };
|
|
566
769
|
window?: { id?: string; resetsAt?: number };
|
|
567
770
|
amount?: { usedFraction?: number };
|
|
568
771
|
};
|
|
569
772
|
const fraction = l.amount?.usedFraction;
|
|
570
773
|
if (typeof fraction !== "number") continue;
|
|
571
774
|
const id = typeof l.id === "string" ? l.id : "";
|
|
775
|
+
const scopeProvider =
|
|
776
|
+
typeof l.scope?.provider === "string" ? resolveOAuthStorageProvider(l.scope.provider) : undefined;
|
|
777
|
+
if (scopeProvider !== undefined && scopeProvider !== providerId) continue;
|
|
572
778
|
const windowId = l.scope?.windowId ?? l.window?.id;
|
|
573
779
|
const tier = l.scope?.tier;
|
|
574
780
|
const modelId = l.scope?.modelId;
|
|
@@ -584,9 +790,9 @@ export class StatusLineComponent implements Component {
|
|
|
584
790
|
}
|
|
585
791
|
} else if (windowId === "5h" && !tier) {
|
|
586
792
|
pushWindow(`${providerId ?? "provider"}:5h`, "5h", fraction, resetsAt, "m");
|
|
587
|
-
} else if (windowId === "7d" && !tier) {
|
|
793
|
+
} else if (windowId === "7d" && !tier && providerId !== "grok-build" && providerId !== "xai") {
|
|
588
794
|
pushWindow(`${providerId ?? "provider"}:7d`, "7d", fraction, resetsAt, "h");
|
|
589
|
-
} else if (providerId === "grok-build" && windowId === "weekly" && !tier) {
|
|
795
|
+
} else if (providerId === "grok-build" && id === "grok-build:weekly" && windowId === "weekly" && !tier) {
|
|
590
796
|
pushWindow("grok-build:weekly", "weekly", fraction, resetsAt, "h");
|
|
591
797
|
}
|
|
592
798
|
}
|
|
@@ -595,17 +801,32 @@ export class StatusLineComponent implements Component {
|
|
|
595
801
|
return windows.length > 0 ? { windows } : null;
|
|
596
802
|
}
|
|
597
803
|
|
|
804
|
+
#activeUsageProvider(): string | undefined {
|
|
805
|
+
const model = this.session.state.model ?? this.session.model;
|
|
806
|
+
if (!model || typeof model !== "object") return undefined;
|
|
807
|
+
const provider = (model as { provider?: unknown }).provider;
|
|
808
|
+
return typeof provider === "string" && provider.length > 0 ? resolveOAuthStorageProvider(provider) : undefined;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
#syncCachedUsageForActiveProvider(): void {
|
|
812
|
+
const activeProvider = this.#activeUsageProvider();
|
|
813
|
+
if (this.#cachedUsageProvider === activeProvider) return;
|
|
814
|
+
this.#cachedUsageProvider = activeProvider;
|
|
815
|
+
this.#cachedUsage = this.#normalizeUsageReports(this.#cachedUsageReports);
|
|
816
|
+
}
|
|
817
|
+
|
|
598
818
|
#buildSegmentContext(
|
|
599
819
|
width: number,
|
|
600
820
|
effectiveSettings: Required<
|
|
601
821
|
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
602
822
|
> &
|
|
603
823
|
StatusLineSettings,
|
|
824
|
+
previewOnly = false,
|
|
604
825
|
): SegmentContext {
|
|
605
826
|
const state = this.session.state;
|
|
606
827
|
|
|
607
|
-
|
|
608
|
-
this.refreshUsageInBackground();
|
|
828
|
+
this.#syncCachedUsageForActiveProvider();
|
|
829
|
+
if (!previewOnly) this.refreshUsageInBackground();
|
|
609
830
|
|
|
610
831
|
// Get usage statistics
|
|
611
832
|
const aggregateUsageStats = this.session.sessionManager?.getUsageStatistics() ?? {
|
|
@@ -633,6 +854,18 @@ export class StatusLineComponent implements Component {
|
|
|
633
854
|
// treats a null value as hidden, so gating here is behavior-identical.
|
|
634
855
|
const prSegmentActive =
|
|
635
856
|
effectiveSettings.leftSegments.includes("pr") || effectiveSettings.rightSegments.includes("pr");
|
|
857
|
+
const commandSegmentActive =
|
|
858
|
+
effectiveSettings.leftSegments.includes("command") || effectiveSettings.rightSegments.includes("command");
|
|
859
|
+
const commandOptions = commandSegmentActive ? this.#trustedCommandOptions() : undefined;
|
|
860
|
+
let commandKey: string | undefined = this.#commandConfigKey;
|
|
861
|
+
if (!previewOnly) {
|
|
862
|
+
if (commandOptions) {
|
|
863
|
+
commandKey = this.#refreshCommandInBackground(commandOptions);
|
|
864
|
+
} else {
|
|
865
|
+
this.#disableCommandSegment();
|
|
866
|
+
commandKey = undefined;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
636
869
|
|
|
637
870
|
return {
|
|
638
871
|
session: this.session,
|
|
@@ -651,9 +884,19 @@ export class StatusLineComponent implements Component {
|
|
|
651
884
|
git: {
|
|
652
885
|
branch: this.#getCurrentBranch(),
|
|
653
886
|
status: this.#getGitStatus(),
|
|
654
|
-
pr: prSegmentActive ? this.#lookupPr() : null,
|
|
887
|
+
pr: prSegmentActive ? (previewOnly ? (this.#cachedPr ?? null) : this.#lookupPr()) : null,
|
|
655
888
|
},
|
|
656
889
|
usage: this.#cachedUsage,
|
|
890
|
+
command:
|
|
891
|
+
commandSegmentActive && (previewOnly || commandKey !== undefined)
|
|
892
|
+
? {
|
|
893
|
+
output: this.#commandOutput,
|
|
894
|
+
failed: this.#commandFailed,
|
|
895
|
+
pending: previewOnly
|
|
896
|
+
? this.#commandOutput === null && !this.#commandFailed
|
|
897
|
+
: this.#commandInFlightKey === commandKey,
|
|
898
|
+
}
|
|
899
|
+
: undefined,
|
|
657
900
|
};
|
|
658
901
|
}
|
|
659
902
|
|
|
@@ -730,7 +973,14 @@ export class StatusLineComponent implements Component {
|
|
|
730
973
|
if (available <= 0 || count <= 0) return "";
|
|
731
974
|
const withCount = `…+${count}`;
|
|
732
975
|
const text = visibleWidth(withCount) <= available ? withCount : "…";
|
|
733
|
-
return theme.fg("dim", text);
|
|
976
|
+
return truncateToWidth(theme.fg("dim", text), available);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
#renderPreviewHighlight(content: string, style: "focus" | "selected"): string {
|
|
980
|
+
const bgColor = style === "selected" ? "warning" : "text";
|
|
981
|
+
const bgAnsi = theme.getFgAnsi(bgColor).replace("\x1b[38;", "\x1b[48;");
|
|
982
|
+
const fgAnsi = theme.getBgAnsi("selectedBg").replace("\x1b[48;", "\x1b[38;");
|
|
983
|
+
return `${bgAnsi}${fgAnsi}> ${Bun.stripANSI(content)} <\x1b[0m`;
|
|
734
984
|
}
|
|
735
985
|
|
|
736
986
|
#renderStatusGroup(
|
|
@@ -795,8 +1045,9 @@ export class StatusLineComponent implements Component {
|
|
|
795
1045
|
Pick<StatusLineSettings, "leftSegments" | "rightSegments" | "separator" | "segmentOptions">
|
|
796
1046
|
> &
|
|
797
1047
|
StatusLineSettings,
|
|
1048
|
+
previewOnly = false,
|
|
798
1049
|
): CollectedStatusSegments {
|
|
799
|
-
const ctx = this.#buildSegmentContext(width, effectiveSettings);
|
|
1050
|
+
const ctx = this.#buildSegmentContext(width, effectiveSettings, previewOnly);
|
|
800
1051
|
const separatorDef = getSeparator(effectiveSettings.separator ?? "powerline-thin", theme);
|
|
801
1052
|
|
|
802
1053
|
// Use the subtle surface tone (the same elevated background as user-message
|
|
@@ -808,12 +1059,14 @@ export class StatusLineComponent implements Component {
|
|
|
808
1059
|
const sepAnsi = theme.getFgAnsi("statusLineSep");
|
|
809
1060
|
|
|
810
1061
|
const previewHighlightSegment = effectiveSettings.previewHighlightSegment;
|
|
1062
|
+
const previewHighlightStyle = effectiveSettings.previewHighlightStyle ?? "focus";
|
|
811
1063
|
const highlightSegment = (segId: StatusLineSegmentId, content: string): string =>
|
|
812
|
-
previewHighlightSegment === segId ?
|
|
1064
|
+
previewHighlightSegment === segId ? this.#renderPreviewHighlight(content, previewHighlightStyle) : content;
|
|
813
1065
|
|
|
814
1066
|
const left: string[] = [];
|
|
815
1067
|
const leftSegIds: StatusLineSegmentId[] = [];
|
|
816
1068
|
for (const segId of effectiveSettings.leftSegments) {
|
|
1069
|
+
if (segId === "command" && !ctx.command) continue;
|
|
817
1070
|
const rendered = renderSegment(segId, ctx);
|
|
818
1071
|
if (rendered.visible && rendered.content) {
|
|
819
1072
|
left.push(highlightSegment(segId, rendered.content));
|
|
@@ -834,6 +1087,7 @@ export class StatusLineComponent implements Component {
|
|
|
834
1087
|
this.#keyDisplayContext,
|
|
835
1088
|
);
|
|
836
1089
|
for (const segId of effectiveSettings.rightSegments) {
|
|
1090
|
+
if (segId === "command" && !ctx.command) continue;
|
|
837
1091
|
const rendered = renderSegment(segId, ctx);
|
|
838
1092
|
if (rendered.visible && rendered.content) {
|
|
839
1093
|
right.push(highlightSegment(segId, rendered.content));
|
|
@@ -869,6 +1123,7 @@ export class StatusLineComponent implements Component {
|
|
|
869
1123
|
right,
|
|
870
1124
|
rightSegIds,
|
|
871
1125
|
previewHighlightSegment,
|
|
1126
|
+
previewHighlightStyle,
|
|
872
1127
|
sessionAccent: effectiveSettings.sessionAccent,
|
|
873
1128
|
leftSepWidth: visibleWidth(separatorDef.left),
|
|
874
1129
|
rightSepWidth: visibleWidth(separatorDef.right),
|
|
@@ -965,7 +1220,7 @@ export class StatusLineComponent implements Component {
|
|
|
965
1220
|
|
|
966
1221
|
#buildStatusLine(width: number, precollected?: CollectedStatusSegments): string {
|
|
967
1222
|
const seg = precollected ?? this.#collectStatusSegments(width, this.#resolveSettings());
|
|
968
|
-
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment } = seg;
|
|
1223
|
+
const { ctx, separatorDef, bgAnsi, fgAnsi, sepAnsi, previewHighlightSegment, previewHighlightStyle } = seg;
|
|
969
1224
|
const { leftSepWidth, rightSepWidth, leftCapWidth, rightCapWidth } = seg;
|
|
970
1225
|
const topFillWidth = Math.max(0, width);
|
|
971
1226
|
|
|
@@ -998,7 +1253,10 @@ export class StatusLineComponent implements Component {
|
|
|
998
1253
|
const overflow = totalWidth() - budget;
|
|
999
1254
|
const shrunk = this.#shrinkPathToWidth(left[pathIdx], ctx, overflow);
|
|
1000
1255
|
if (shrunk !== null) {
|
|
1001
|
-
left[pathIdx] =
|
|
1256
|
+
left[pathIdx] =
|
|
1257
|
+
previewHighlightSegment === "path"
|
|
1258
|
+
? this.#renderPreviewHighlight(shrunk, previewHighlightStyle)
|
|
1259
|
+
: shrunk;
|
|
1002
1260
|
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1003
1261
|
}
|
|
1004
1262
|
}
|
|
@@ -1087,9 +1345,9 @@ export class StatusLineComponent implements Component {
|
|
|
1087
1345
|
* being dropped. Falls back to the polished justified single row whenever
|
|
1088
1346
|
* everything fits on one line.
|
|
1089
1347
|
*/
|
|
1090
|
-
#buildStatusRows(width: number, maxRows: number): string[] {
|
|
1348
|
+
#buildStatusRows(width: number, maxRows: number, previewOnly = false): string[] {
|
|
1091
1349
|
const effectiveSettings = this.#resolveSettings();
|
|
1092
|
-
const seg = this.#collectStatusSegments(width, effectiveSettings);
|
|
1350
|
+
const seg = this.#collectStatusSegments(width, effectiveSettings, previewOnly);
|
|
1093
1351
|
const cacheKey = JSON.stringify({
|
|
1094
1352
|
width,
|
|
1095
1353
|
maxRows,
|
|
@@ -1124,14 +1382,14 @@ export class StatusLineComponent implements Component {
|
|
|
1124
1382
|
.join(","),
|
|
1125
1383
|
version: this.#version,
|
|
1126
1384
|
});
|
|
1127
|
-
if (this.#renderedRowsCache?.key === cacheKey) {
|
|
1385
|
+
if (!previewOnly && this.#renderedRowsCache?.key === cacheKey) {
|
|
1128
1386
|
this.#renderedRowsCacheHits++;
|
|
1129
1387
|
return [...this.#renderedRowsCache.rows];
|
|
1130
1388
|
}
|
|
1131
|
-
this.#renderedRowsCacheMisses++;
|
|
1389
|
+
if (!previewOnly) this.#renderedRowsCacheMisses++;
|
|
1132
1390
|
|
|
1133
1391
|
if (seg.left.length === 0 && seg.right.length === 0) {
|
|
1134
|
-
this.#renderedRowsCache = { key: cacheKey, rows: [] };
|
|
1392
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows: [] };
|
|
1135
1393
|
return [];
|
|
1136
1394
|
}
|
|
1137
1395
|
|
|
@@ -1209,7 +1467,7 @@ export class StatusLineComponent implements Component {
|
|
|
1209
1467
|
if (dropped > 0) {
|
|
1210
1468
|
const priorityRow = this.#priorityRowFor(seg, [], [], topFillWidth);
|
|
1211
1469
|
if (priorityRow !== null) {
|
|
1212
|
-
this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1470
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows: [priorityRow] };
|
|
1213
1471
|
return [priorityRow];
|
|
1214
1472
|
}
|
|
1215
1473
|
}
|
|
@@ -1249,7 +1507,7 @@ export class StatusLineComponent implements Component {
|
|
|
1249
1507
|
if (packedRows.length === 0 && marker) rows = [marker];
|
|
1250
1508
|
}
|
|
1251
1509
|
|
|
1252
|
-
this.#renderedRowsCache = { key: cacheKey, rows };
|
|
1510
|
+
if (!previewOnly) this.#renderedRowsCache = { key: cacheKey, rows };
|
|
1253
1511
|
return [...rows];
|
|
1254
1512
|
}
|
|
1255
1513
|
|
|
@@ -1271,6 +1529,120 @@ export class StatusLineComponent implements Component {
|
|
|
1271
1529
|
return this.#buildStatusRows(width, this.#resolveMaxRows()).join("\n");
|
|
1272
1530
|
}
|
|
1273
1531
|
|
|
1532
|
+
getPreviewContentForSettings(width: number, previewSettings: StatusLineSettings): string {
|
|
1533
|
+
const previousSettings = this.#settings;
|
|
1534
|
+
this.#settings = { ...previousSettings, previewHighlightSegment: undefined, ...previewSettings };
|
|
1535
|
+
try {
|
|
1536
|
+
return this.#buildStatusRows(width, this.#resolveMaxRows(), true).join("\n");
|
|
1537
|
+
} finally {
|
|
1538
|
+
this.#settings = previousSettings;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
#placePreviewSegments(
|
|
1543
|
+
seg: CollectedStatusSegments,
|
|
1544
|
+
width: number,
|
|
1545
|
+
): {
|
|
1546
|
+
left: string[];
|
|
1547
|
+
leftIds: StatusLineSegmentId[];
|
|
1548
|
+
right: string[];
|
|
1549
|
+
rightIds: (StatusLineSegmentId | null)[];
|
|
1550
|
+
} {
|
|
1551
|
+
const {
|
|
1552
|
+
ctx,
|
|
1553
|
+
previewHighlightSegment,
|
|
1554
|
+
previewHighlightStyle,
|
|
1555
|
+
leftSepWidth,
|
|
1556
|
+
rightSepWidth,
|
|
1557
|
+
leftCapWidth,
|
|
1558
|
+
rightCapWidth,
|
|
1559
|
+
} = seg;
|
|
1560
|
+
const rank = this.#priorityRanker(seg);
|
|
1561
|
+
const layout = (budget: number) => {
|
|
1562
|
+
const left = [...seg.left];
|
|
1563
|
+
const leftIds = [...seg.leftSegIds];
|
|
1564
|
+
const right = [...seg.right];
|
|
1565
|
+
const rightIds = [...seg.rightSegIds];
|
|
1566
|
+
let leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1567
|
+
let rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1568
|
+
const totalWidth = () => leftWidth + rightWidth + (left.length > 0 && right.length > 0 ? 1 : 0);
|
|
1569
|
+
let dropped = 0;
|
|
1570
|
+
|
|
1571
|
+
const pathIdx = leftIds.indexOf("path");
|
|
1572
|
+
if (pathIdx >= 0 && totalWidth() > budget) {
|
|
1573
|
+
const shrunk = this.#shrinkPathToWidth(left[pathIdx], ctx, totalWidth() - budget);
|
|
1574
|
+
if (shrunk !== null) {
|
|
1575
|
+
left[pathIdx] =
|
|
1576
|
+
previewHighlightSegment === "path"
|
|
1577
|
+
? this.#renderPreviewHighlight(shrunk, previewHighlightStyle)
|
|
1578
|
+
: shrunk;
|
|
1579
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
while (totalWidth() > budget && (left.length > 0 || right.length > 0)) {
|
|
1583
|
+
let victimSide: "left" | "right" | null = null;
|
|
1584
|
+
let victimIndex = -1;
|
|
1585
|
+
let victimRank = Number.POSITIVE_INFINITY;
|
|
1586
|
+
for (let index = right.length - 1; index >= 0; index -= 1) {
|
|
1587
|
+
const candidate = rank(rightIds[index]);
|
|
1588
|
+
if (candidate < victimRank) {
|
|
1589
|
+
victimRank = candidate;
|
|
1590
|
+
victimSide = "right";
|
|
1591
|
+
victimIndex = index;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
for (let index = left.length - 1; index >= 0; index -= 1) {
|
|
1595
|
+
const candidate = rank(leftIds[index]);
|
|
1596
|
+
if (candidate < victimRank) {
|
|
1597
|
+
victimRank = candidate;
|
|
1598
|
+
victimSide = "left";
|
|
1599
|
+
victimIndex = index;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
if (victimSide === null) break;
|
|
1603
|
+
if (victimSide === "right") {
|
|
1604
|
+
right.splice(victimIndex, 1);
|
|
1605
|
+
rightIds.splice(victimIndex, 1);
|
|
1606
|
+
rightWidth = this.#groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1607
|
+
} else {
|
|
1608
|
+
left.splice(victimIndex, 1);
|
|
1609
|
+
leftIds.splice(victimIndex, 1);
|
|
1610
|
+
leftWidth = this.#groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1611
|
+
}
|
|
1612
|
+
dropped += 1;
|
|
1613
|
+
}
|
|
1614
|
+
return { left, leftIds, right, rightIds, dropped };
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
let placed = layout(Math.max(0, width));
|
|
1618
|
+
let reserved = 0;
|
|
1619
|
+
if (placed.dropped > 0 && width > 0) {
|
|
1620
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
1621
|
+
const needed = Math.min(width, visibleWidth(`…+${placed.dropped}`));
|
|
1622
|
+
if (needed <= reserved) break;
|
|
1623
|
+
reserved = needed;
|
|
1624
|
+
placed = layout(Math.max(0, width - reserved));
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return placed;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
getPreviewPartsForSettings(width: number, previewSettings: StatusLineSettings): StatusLinePreviewParts {
|
|
1631
|
+
const previousSettings = this.#settings;
|
|
1632
|
+
this.#settings = { ...previousSettings, ...previewSettings };
|
|
1633
|
+
this.#resolvedSettingsCache = undefined;
|
|
1634
|
+
this.#resolvedSettingsFingerprint = undefined;
|
|
1635
|
+
try {
|
|
1636
|
+
const seg = this.#collectStatusSegments(width, this.#resolveSettings(), true);
|
|
1637
|
+
const placed = this.#placePreviewSegments(seg, width);
|
|
1638
|
+
return { ...placed, separator: seg.separatorDef };
|
|
1639
|
+
} finally {
|
|
1640
|
+
this.#settings = previousSettings;
|
|
1641
|
+
this.#resolvedSettingsCache = undefined;
|
|
1642
|
+
this.#resolvedSettingsFingerprint = undefined;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1274
1646
|
getCacheStatsForTest(): { rowHits: number; rowMisses: number } {
|
|
1275
1647
|
return { rowHits: this.#renderedRowsCacheHits, rowMisses: this.#renderedRowsCacheMisses };
|
|
1276
1648
|
}
|
|
@@ -1043,6 +1043,7 @@ export class CommandController {
|
|
|
1043
1043
|
this.ctx.ui.requestRender();
|
|
1044
1044
|
|
|
1045
1045
|
prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
|
|
1046
|
+
this.ctx.resetAssistantTextPresentation();
|
|
1046
1047
|
this.ctx.chatContainer.clear();
|
|
1047
1048
|
this.ctx.pendingMessagesContainer.clear();
|
|
1048
1049
|
this.ctx.compactionQueuedMessages = [];
|
|
@@ -1083,6 +1084,7 @@ export class CommandController {
|
|
|
1083
1084
|
this.ctx.ui.requestRender();
|
|
1084
1085
|
|
|
1085
1086
|
prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
|
|
1087
|
+
this.ctx.resetAssistantTextPresentation();
|
|
1086
1088
|
this.ctx.chatContainer.clear();
|
|
1087
1089
|
this.ctx.pendingMessagesContainer.clear();
|
|
1088
1090
|
this.ctx.compactionQueuedMessages = [];
|