@pandi-coding-agent/pi-cante 0.80.2 → 0.80.3-cante.2

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.
Files changed (58) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/dist/core/agent-session.d.ts +2 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +30 -3
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/compaction.d.ts +0 -2
  7. package/dist/core/compaction/compaction.d.ts.map +1 -1
  8. package/dist/core/compaction/compaction.js +1 -9
  9. package/dist/core/compaction/compaction.js.map +1 -1
  10. package/dist/core/extensions/index.d.ts +1 -1
  11. package/dist/core/extensions/index.d.ts.map +1 -1
  12. package/dist/core/extensions/index.js.map +1 -1
  13. package/dist/core/extensions/types.d.ts +8 -1
  14. package/dist/core/extensions/types.d.ts.map +1 -1
  15. package/dist/core/extensions/types.js.map +1 -1
  16. package/dist/core/http-dispatcher.d.ts.map +1 -1
  17. package/dist/core/http-dispatcher.js +28 -1
  18. package/dist/core/http-dispatcher.js.map +1 -1
  19. package/dist/core/settings-manager.d.ts +3 -3
  20. package/dist/core/settings-manager.d.ts.map +1 -1
  21. package/dist/core/settings-manager.js +8 -8
  22. package/dist/core/settings-manager.js.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  27. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  28. package/dist/modes/interactive/components/assistant-message.js +15 -7
  29. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  30. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  31. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  32. package/dist/modes/interactive/components/settings-selector.js +13 -1
  33. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  34. package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  35. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  36. package/dist/modes/interactive/components/status-indicator.js +60 -0
  37. package/dist/modes/interactive/components/status-indicator.js.map +1 -0
  38. package/dist/modes/interactive/components/user-message.d.ts +6 -2
  39. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  40. package/dist/modes/interactive/components/user-message.js +18 -5
  41. package/dist/modes/interactive/components/user-message.js.map +1 -1
  42. package/dist/modes/interactive/interactive-mode.d.ts +5 -7
  43. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  44. package/dist/modes/interactive/interactive-mode.js +88 -116
  45. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  46. package/docs/extensions.md +14 -0
  47. package/docs/settings.md +1 -0
  48. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  49. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  50. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  51. package/examples/extensions/gondolin/package-lock.json +2 -2
  52. package/examples/extensions/gondolin/package.json +1 -1
  53. package/examples/extensions/sandbox/package-lock.json +2 -2
  54. package/examples/extensions/sandbox/package.json +1 -1
  55. package/examples/extensions/with-deps/package-lock.json +2 -2
  56. package/examples/extensions/with-deps/package.json +1 -1
  57. package/npm-shrinkwrap.json +44 -44
  58. package/package.json +14 -14
@@ -7,7 +7,7 @@ import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
9
  import { getProviders, } from "@earendil-works/pi-ai/compat";
10
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
10
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
11
11
  import chalk from "chalk";
12
12
  import { spawn, spawnSync } from "child_process";
13
13
  import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
@@ -40,7 +40,6 @@ import { BashExecutionComponent } from "./components/bash-execution.js";
40
40
  import { BorderedLoader } from "./components/bordered-loader.js";
41
41
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
42
42
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
43
- import { CountdownTimer } from "./components/countdown-timer.js";
44
43
  import { CustomEditor } from "./components/custom-editor.js";
45
44
  import { CustomMessageComponent } from "./components/custom-message.js";
46
45
  import { DaxnutsComponent } from "./components/daxnuts.js";
@@ -58,6 +57,7 @@ import { ScopedModelsSelectorComponent } from "./components/scoped-models-select
58
57
  import { SessionSelectorComponent } from "./components/session-selector.js";
59
58
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
60
59
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
60
+ import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
61
61
  import { ToolExecutionComponent } from "./components/tool-execution.js";
62
62
  import { TreeSelectorComponent } from "./components/tree-selector.js";
63
63
  import { TrustSelectorComponent } from "./components/trust-selector.js";
@@ -153,7 +153,8 @@ export class InteractiveMode {
153
153
  isInitialized = false;
154
154
  onInputCallback;
155
155
  pendingUserInputs = [];
156
- loadingAnimation = undefined;
156
+ activeStatusIndicator = undefined;
157
+ idleStatus = new IdleStatus();
157
158
  workingMessage = undefined;
158
159
  workingVisible = true;
159
160
  workingIndicatorOptions = undefined;
@@ -177,6 +178,7 @@ export class InteractiveMode {
177
178
  toolOutputExpanded = false;
178
179
  // Thinking block visibility state
179
180
  hideThinkingBlock = false;
181
+ outputPad = 1;
180
182
  // Skill commands: command name -> skill file path
181
183
  skillCommands = new Map();
182
184
  // Agent subscription unsubscribe function
@@ -189,11 +191,8 @@ export class InteractiveMode {
189
191
  // Track pending bash components (shown in pending area, moved to chat on submit)
190
192
  pendingBashComponents = [];
191
193
  // Auto-compaction state
192
- autoCompactionLoader = undefined;
193
194
  autoCompactionEscapeHandler;
194
195
  // Auto-retry state
195
- retryLoader = undefined;
196
- retryCountdown = undefined;
197
196
  retryEscapeHandler;
198
197
  // Messages queued while compaction is running
199
198
  compactionQueuedMessages = [];
@@ -269,6 +268,7 @@ export class InteractiveMode {
269
268
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
270
269
  // Load hide thinking block setting
271
270
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
271
+ this.outputPad = this.settingsManager.getOutputPad();
272
272
  // Register themes from resource loader and initialize
273
273
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
274
274
  this.themeController = new InteractiveThemeController(this.ui, this.settingsManager, (message) => this.showError(message), () => this.updateEditorBorderColor());
@@ -1157,11 +1157,7 @@ export class InteractiveMode {
1157
1157
  commandContextActions: {
1158
1158
  waitForIdle: () => this.session.agent.waitForIdle(),
1159
1159
  newSession: async (options) => {
1160
- if (this.loadingAnimation) {
1161
- this.loadingAnimation.stop();
1162
- this.loadingAnimation = undefined;
1163
- }
1164
- this.statusContainer.clear();
1160
+ this.clearStatusIndicator();
1165
1161
  try {
1166
1162
  return await this.runtimeHost.newSession(options);
1167
1163
  }
@@ -1231,8 +1227,13 @@ export class InteractiveMode {
1231
1227
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1232
1228
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
1233
1229
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
1230
+ this.outputPad = this.settingsManager.getOutputPad();
1234
1231
  this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
1235
- this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
1232
+ const clearOnShrink = this.settingsManager.getClearOnShrink();
1233
+ this.ui.setClearOnShrink(clearOnShrink);
1234
+ if (!clearOnShrink && !this.activeStatusIndicator) {
1235
+ this.statusContainer.clear();
1236
+ }
1236
1237
  const editorPaddingX = this.settingsManager.getEditorPaddingX();
1237
1238
  const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
1238
1239
  this.defaultEditor.setPaddingX(editorPaddingX);
@@ -1345,36 +1346,41 @@ export class InteractiveMode {
1345
1346
  this.footerDataProvider.setExtensionStatus(key, text);
1346
1347
  this.ui.requestRender();
1347
1348
  }
1348
- getWorkingLoaderMessage() {
1349
- return this.workingMessage ?? this.defaultWorkingMessage;
1350
- }
1351
- createWorkingLoader() {
1352
- return new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), this.getWorkingLoaderMessage(), this.workingIndicatorOptions);
1349
+ showStatusIndicator(indicator) {
1350
+ this.activeStatusIndicator?.dispose();
1351
+ this.activeStatusIndicator = indicator;
1352
+ this.statusContainer.clear();
1353
+ this.statusContainer.addChild(indicator);
1353
1354
  }
1354
- stopWorkingLoader() {
1355
- if (this.loadingAnimation) {
1356
- this.loadingAnimation.stop();
1357
- this.loadingAnimation = undefined;
1355
+ clearStatusIndicator(kind) {
1356
+ if (kind && this.activeStatusIndicator?.kind !== kind) {
1357
+ return;
1358
1358
  }
1359
+ const hadActiveStatusIndicator = this.activeStatusIndicator !== undefined;
1360
+ this.activeStatusIndicator?.dispose();
1361
+ this.activeStatusIndicator = undefined;
1359
1362
  this.statusContainer.clear();
1363
+ if (hadActiveStatusIndicator && this.ui.getClearOnShrink()) {
1364
+ this.statusContainer.addChild(this.idleStatus);
1365
+ }
1360
1366
  }
1361
1367
  setWorkingVisible(visible) {
1362
1368
  this.workingVisible = visible;
1363
1369
  if (!visible) {
1364
- this.stopWorkingLoader();
1370
+ this.clearStatusIndicator("working");
1365
1371
  this.ui.requestRender();
1366
1372
  return;
1367
1373
  }
1368
- if (this.session.isStreaming && !this.loadingAnimation) {
1369
- this.statusContainer.clear();
1370
- this.loadingAnimation = this.createWorkingLoader();
1371
- this.statusContainer.addChild(this.loadingAnimation);
1374
+ if (this.session.isStreaming && this.activeStatusIndicator?.kind !== "working") {
1375
+ this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1372
1376
  }
1373
1377
  this.ui.requestRender();
1374
1378
  }
1375
1379
  setWorkingIndicator(options) {
1376
1380
  this.workingIndicatorOptions = options;
1377
- this.loadingAnimation?.setIndicator(options);
1381
+ if (this.activeStatusIndicator?.kind === "working") {
1382
+ this.activeStatusIndicator.setIndicator(options);
1383
+ }
1378
1384
  this.ui.requestRender();
1379
1385
  }
1380
1386
  setHiddenThinkingLabel(label) {
@@ -1462,8 +1468,8 @@ export class InteractiveMode {
1462
1468
  this.workingMessage = undefined;
1463
1469
  this.workingVisible = true;
1464
1470
  this.setWorkingIndicator();
1465
- if (this.loadingAnimation) {
1466
- this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
1471
+ if (this.activeStatusIndicator?.kind === "working") {
1472
+ this.activeStatusIndicator.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
1467
1473
  }
1468
1474
  this.setHiddenThinkingLabel();
1469
1475
  }
@@ -1603,8 +1609,8 @@ export class InteractiveMode {
1603
1609
  setStatus: (key, text) => this.setExtensionStatus(key, text),
1604
1610
  setWorkingMessage: (message) => {
1605
1611
  this.workingMessage = message;
1606
- if (this.loadingAnimation) {
1607
- this.loadingAnimation.setMessage(message ?? this.defaultWorkingMessage);
1612
+ if (this.activeStatusIndicator?.kind === "working") {
1613
+ this.activeStatusIndicator.setMessage(message ?? this.defaultWorkingMessage);
1608
1614
  }
1609
1615
  },
1610
1616
  setWorkingVisible: (visible) => this.setWorkingVisible(visible),
@@ -2223,18 +2229,11 @@ export class InteractiveMode {
2223
2229
  this.defaultEditor.onEscape = this.retryEscapeHandler;
2224
2230
  this.retryEscapeHandler = undefined;
2225
2231
  }
2226
- if (this.retryCountdown) {
2227
- this.retryCountdown.dispose();
2228
- this.retryCountdown = undefined;
2229
- }
2230
- if (this.retryLoader) {
2231
- this.retryLoader.stop();
2232
- this.retryLoader = undefined;
2233
- }
2234
- this.stopWorkingLoader();
2235
2232
  if (this.workingVisible) {
2236
- this.loadingAnimation = this.createWorkingLoader();
2237
- this.statusContainer.addChild(this.loadingAnimation);
2233
+ this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
2234
+ }
2235
+ else {
2236
+ this.clearStatusIndicator();
2238
2237
  }
2239
2238
  this.ui.requestRender();
2240
2239
  break;
@@ -2262,7 +2261,7 @@ export class InteractiveMode {
2262
2261
  this.ui.requestRender();
2263
2262
  }
2264
2263
  else if (event.message.role === "assistant") {
2265
- this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
2264
+ this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
2266
2265
  this.streamingMessage = event.message;
2267
2266
  this.chatContainer.addChild(this.streamingComponent);
2268
2267
  this.streamingComponent.updateContent(this.streamingMessage);
@@ -2370,11 +2369,7 @@ export class InteractiveMode {
2370
2369
  if (this.settingsManager.getShowTerminalProgress()) {
2371
2370
  this.ui.terminal.setProgress(false);
2372
2371
  }
2373
- if (this.loadingAnimation) {
2374
- this.loadingAnimation.stop();
2375
- this.loadingAnimation = undefined;
2376
- this.statusContainer.clear();
2377
- }
2372
+ this.clearStatusIndicator("working");
2378
2373
  if (this.streamingComponent) {
2379
2374
  this.chatContainer.removeChild(this.streamingComponent);
2380
2375
  this.streamingComponent = undefined;
@@ -2393,13 +2388,7 @@ export class InteractiveMode {
2393
2388
  this.defaultEditor.onEscape = () => {
2394
2389
  this.session.abortCompaction();
2395
2390
  };
2396
- this.statusContainer.clear();
2397
- const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
2398
- const label = event.reason === "manual"
2399
- ? `Compacting context... ${cancelHint}`
2400
- : `${event.reason === "overflow" ? "Context overflow detected, " : ""}Auto-compacting... ${cancelHint}`;
2401
- this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
2402
- this.statusContainer.addChild(this.autoCompactionLoader);
2391
+ this.showStatusIndicator(new CompactionStatusIndicator(this.ui, event.reason));
2403
2392
  this.ui.requestRender();
2404
2393
  break;
2405
2394
  }
@@ -2411,11 +2400,7 @@ export class InteractiveMode {
2411
2400
  this.defaultEditor.onEscape = this.autoCompactionEscapeHandler;
2412
2401
  this.autoCompactionEscapeHandler = undefined;
2413
2402
  }
2414
- if (this.autoCompactionLoader) {
2415
- this.autoCompactionLoader.stop();
2416
- this.autoCompactionLoader = undefined;
2417
- this.statusContainer.clear();
2418
- }
2403
+ this.clearStatusIndicator("compaction");
2419
2404
  if (event.aborted) {
2420
2405
  if (event.reason === "manual") {
2421
2406
  this.showError("Compaction cancelled");
@@ -2449,17 +2434,7 @@ export class InteractiveMode {
2449
2434
  this.defaultEditor.onEscape = () => {
2450
2435
  this.session.abortRetry();
2451
2436
  };
2452
- // Show retry indicator
2453
- this.statusContainer.clear();
2454
- this.retryCountdown?.dispose();
2455
- const retryMessage = (seconds) => `Retrying (${event.attempt}/${event.maxAttempts}) in ${seconds}s... (${keyText("app.interrupt")} to cancel)`;
2456
- this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), retryMessage(Math.ceil(event.delayMs / 1000)));
2457
- this.retryCountdown = new CountdownTimer(event.delayMs, this.ui, (seconds) => {
2458
- this.retryLoader?.setMessage(retryMessage(seconds));
2459
- }, () => {
2460
- this.retryCountdown = undefined;
2461
- });
2462
- this.statusContainer.addChild(this.retryLoader);
2437
+ this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
2463
2438
  this.ui.requestRender();
2464
2439
  break;
2465
2440
  }
@@ -2469,16 +2444,7 @@ export class InteractiveMode {
2469
2444
  this.defaultEditor.onEscape = this.retryEscapeHandler;
2470
2445
  this.retryEscapeHandler = undefined;
2471
2446
  }
2472
- if (this.retryCountdown) {
2473
- this.retryCountdown.dispose();
2474
- this.retryCountdown = undefined;
2475
- }
2476
- // Stop loader
2477
- if (this.retryLoader) {
2478
- this.retryLoader.stop();
2479
- this.retryLoader = undefined;
2480
- this.statusContainer.clear();
2481
- }
2447
+ this.clearStatusIndicator("retry");
2482
2448
  // Show error only on final failure (success shows normal response)
2483
2449
  if (!event.success) {
2484
2450
  this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
@@ -2569,12 +2535,12 @@ export class InteractiveMode {
2569
2535
  // Render user message separately if present
2570
2536
  if (skillBlock.userMessage) {
2571
2537
  this.chatContainer.addChild(new Spacer(1));
2572
- const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings());
2538
+ const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad);
2573
2539
  this.chatContainer.addChild(userComponent);
2574
2540
  }
2575
2541
  }
2576
2542
  else {
2577
- const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings());
2543
+ const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad);
2578
2544
  this.chatContainer.addChild(userComponent);
2579
2545
  }
2580
2546
  if (options?.populateHistory) {
@@ -2584,7 +2550,7 @@ export class InteractiveMode {
2584
2550
  break;
2585
2551
  }
2586
2552
  case "assistant": {
2587
- const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel);
2553
+ const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad);
2588
2554
  this.chatContainer.addChild(assistantComponent);
2589
2555
  break;
2590
2556
  }
@@ -3302,6 +3268,7 @@ export class InteractiveMode {
3302
3268
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
3303
3269
  defaultProjectTrust: this.settingsManager.getDefaultProjectTrust(),
3304
3270
  editorPaddingX: this.settingsManager.getEditorPaddingX(),
3271
+ outputPad: this.settingsManager.getOutputPad(),
3305
3272
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
3306
3273
  quietStartup: this.settingsManager.getQuietStartup(),
3307
3274
  clearOnShrink: this.settingsManager.getClearOnShrink(),
@@ -3403,6 +3370,23 @@ export class InteractiveMode {
3403
3370
  this.editor.setPaddingX(padding);
3404
3371
  }
3405
3372
  },
3373
+ onOutputPadChange: (padding) => {
3374
+ this.settingsManager.setOutputPad(padding);
3375
+ this.outputPad = padding;
3376
+ if (this.streamingComponent || this.session.isStreaming) {
3377
+ for (const child of this.chatContainer.children) {
3378
+ if (child instanceof AssistantMessageComponent || child instanceof UserMessageComponent) {
3379
+ child.setOutputPad(padding);
3380
+ }
3381
+ }
3382
+ if (this.streamingComponent) {
3383
+ this.streamingComponent.setOutputPad(padding);
3384
+ }
3385
+ this.ui.requestRender();
3386
+ return;
3387
+ }
3388
+ this.rebuildChatFromMessages();
3389
+ },
3406
3390
  onAutocompleteMaxVisibleChange: (maxVisible) => {
3407
3391
  this.settingsManager.setAutocompleteMaxVisible(maxVisible);
3408
3392
  this.defaultEditor.setAutocompleteMaxVisible(maxVisible);
@@ -3413,6 +3397,9 @@ export class InteractiveMode {
3413
3397
  onClearOnShrinkChange: (enabled) => {
3414
3398
  this.settingsManager.setClearOnShrink(enabled);
3415
3399
  this.ui.setClearOnShrink(enabled);
3400
+ if (!enabled && !this.activeStatusIndicator) {
3401
+ this.statusContainer.clear();
3402
+ }
3416
3403
  },
3417
3404
  onShowTerminalProgressChange: (enabled) => {
3418
3405
  this.settingsManager.setShowTerminalProgress(enabled);
@@ -3730,16 +3717,16 @@ export class InteractiveMode {
3730
3717
  break;
3731
3718
  }
3732
3719
  }
3733
- // Set up escape handler and loader if summarizing
3734
- let summaryLoader;
3720
+ // Set up escape handler and status indicator if summarizing
3721
+ let showingSummaryIndicator = false;
3735
3722
  const originalOnEscape = this.defaultEditor.onEscape;
3736
3723
  if (wantsSummary) {
3737
3724
  this.defaultEditor.onEscape = () => {
3738
3725
  this.session.abortBranchSummary();
3739
3726
  };
3740
3727
  this.chatContainer.addChild(new Spacer(1));
3741
- summaryLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Summarizing branch... (${keyText("app.interrupt")} to cancel)`);
3742
- this.statusContainer.addChild(summaryLoader);
3728
+ this.showStatusIndicator(new BranchSummaryStatusIndicator(this.ui));
3729
+ showingSummaryIndicator = true;
3743
3730
  this.ui.requestRender();
3744
3731
  }
3745
3732
  try {
@@ -3770,9 +3757,8 @@ export class InteractiveMode {
3770
3757
  this.showError(error instanceof Error ? error.message : String(error));
3771
3758
  }
3772
3759
  finally {
3773
- if (summaryLoader) {
3774
- summaryLoader.stop();
3775
- this.statusContainer.clear();
3760
+ if (showingSummaryIndicator) {
3761
+ this.clearStatusIndicator("branchSummary");
3776
3762
  }
3777
3763
  this.defaultEditor.onEscape = originalOnEscape;
3778
3764
  }
@@ -3813,11 +3799,7 @@ export class InteractiveMode {
3813
3799
  });
3814
3800
  }
3815
3801
  async handleResumeSession(sessionPath, options) {
3816
- if (this.loadingAnimation) {
3817
- this.loadingAnimation.stop();
3818
- this.loadingAnimation = undefined;
3819
- }
3820
- this.statusContainer.clear();
3802
+ this.clearStatusIndicator();
3821
3803
  try {
3822
3804
  const result = await this.runtimeHost.switchSession(sessionPath, {
3823
3805
  withSession: options?.withSession,
@@ -4210,6 +4192,7 @@ export class InteractiveMode {
4210
4192
  return;
4211
4193
  }
4212
4194
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
4195
+ this.outputPad = this.settingsManager.getOutputPad();
4213
4196
  this.rebuildChatFromMessages();
4214
4197
  chatRestoredBeforeSessionStart = true;
4215
4198
  };
@@ -4233,7 +4216,11 @@ export class InteractiveMode {
4233
4216
  this.editor.setAutocompleteMaxVisible?.(autocompleteMaxVisible);
4234
4217
  }
4235
4218
  this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
4236
- this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
4219
+ const clearOnShrink = this.settingsManager.getClearOnShrink();
4220
+ this.ui.setClearOnShrink(clearOnShrink);
4221
+ if (!clearOnShrink && !this.activeStatusIndicator) {
4222
+ this.statusContainer.clear();
4223
+ }
4237
4224
  this.setupAutocompleteProvider();
4238
4225
  const runner = this.session.extensionRunner;
4239
4226
  this.setupExtensionShortcuts(runner);
@@ -4312,11 +4299,7 @@ export class InteractiveMode {
4312
4299
  return;
4313
4300
  }
4314
4301
  try {
4315
- if (this.loadingAnimation) {
4316
- this.loadingAnimation.stop();
4317
- this.loadingAnimation = undefined;
4318
- }
4319
- this.statusContainer.clear();
4302
+ this.clearStatusIndicator();
4320
4303
  const result = await this.runtimeHost.importFromJsonl(inputPath);
4321
4304
  if (result.cancelled) {
4322
4305
  this.showStatus("Import cancelled");
@@ -4642,11 +4625,7 @@ export class InteractiveMode {
4642
4625
  this.ui.requestRender();
4643
4626
  }
4644
4627
  async handleClearCommand() {
4645
- if (this.loadingAnimation) {
4646
- this.loadingAnimation.stop();
4647
- this.loadingAnimation = undefined;
4648
- }
4649
- this.statusContainer.clear();
4628
+ this.clearStatusIndicator();
4650
4629
  try {
4651
4630
  const result = await this.runtimeHost.newSession();
4652
4631
  if (result.cancelled) {
@@ -4773,11 +4752,7 @@ export class InteractiveMode {
4773
4752
  this.ui.requestRender();
4774
4753
  }
4775
4754
  async handleCompactCommand(customInstructions) {
4776
- if (this.loadingAnimation) {
4777
- this.loadingAnimation.stop();
4778
- this.loadingAnimation = undefined;
4779
- }
4780
- this.statusContainer.clear();
4755
+ this.clearStatusIndicator();
4781
4756
  try {
4782
4757
  await this.session.compact(customInstructions);
4783
4758
  }
@@ -4789,10 +4764,7 @@ export class InteractiveMode {
4789
4764
  if (this.settingsManager.getShowTerminalProgress()) {
4790
4765
  this.ui.terminal.setProgress(false);
4791
4766
  }
4792
- if (this.loadingAnimation) {
4793
- this.loadingAnimation.stop();
4794
- this.loadingAnimation = undefined;
4795
- }
4767
+ this.clearStatusIndicator();
4796
4768
  this.themeController.disableAutoSync();
4797
4769
  this.clearExtensionTerminalInputListeners();
4798
4770
  this.footer.dispose();