@mrquake/quakecode-cli 0.64.4 → 0.64.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.
@@ -42,6 +42,7 @@ import { ModelSelectorComponent } from "./components/model-selector.js";
42
42
  import { OAuthSelectorComponent } from "./components/oauth-selector.js";
43
43
  import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
44
44
  import { SessionSelectorComponent } from "./components/session-selector.js";
45
+ import { SessionTabsComponent } from "./components/session-tabs.js";
45
46
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
46
47
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
47
48
  import { ToolExecutionComponent } from "./components/tool-execution.js";
@@ -161,6 +162,8 @@ export class InteractiveMode {
161
162
  this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
162
163
  paddingX: editorPaddingX,
163
164
  autocompleteMaxVisible,
165
+ noBorders: true,
166
+ prompt: theme.bold(theme.fg("accent", "› ")),
164
167
  });
165
168
  this.editor = this.defaultEditor;
166
169
  this.editorContainer = new Container();
@@ -291,14 +294,16 @@ export class InteractiveMode {
291
294
  this.fdPath = fdPath;
292
295
  // Add header container as first child
293
296
  this.ui.addChild(this.headerContainer);
297
+ this.headerContainer.addChild(new SessionTabsComponent(() => this.getSessionTabLabel(), () => DISPLAY_NAME));
298
+ this.headerContainer.addChild(new Spacer(1));
294
299
  // Add header with keybindings from config (unless silenced)
295
300
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
296
301
  const currentModel = this.session.model?.id ?? "no model";
297
302
  const cwd = this.sessionManager.getCwd();
298
303
  const owner = os.userInfo().username;
299
304
  const title = `${DISPLAY_NAME} v${this.version}`;
300
- const leftInner = 34;
301
- const maxTotalWidth = Math.max(118, Math.min(this.ui.terminal.columns - 6, 168));
305
+ const leftInner = 44;
306
+ const maxTotalWidth = Math.max(128, Math.min(this.ui.terminal.columns - 6, 172));
302
307
  const rightInner = maxTotalWidth - leftInner - 7;
303
308
  const totalWidth = leftInner + rightInner + 7;
304
309
  const border = (s) => theme.fg("borderAccent", s);
@@ -412,17 +417,21 @@ export class InteractiveMode {
412
417
  // Initialize available provider count for footer display
413
418
  await this.updateAvailableProviderCount();
414
419
  }
415
- /**
416
- * Update terminal title with session name and cwd.
417
- */
420
+ getSessionTabLabel() {
421
+ return this.sessionManager.getSessionName() || path.basename(this.sessionManager.getCwd()) || "new session";
422
+ }
418
423
  updateTerminalTitle() {
419
- const cwdBasename = path.basename(this.sessionManager.getCwd());
420
424
  const sessionName = this.sessionManager.getSessionName();
421
- if (sessionName) {
422
- this.ui.terminal.setTitle(`${DISPLAY_NAME} - ${sessionName} - ${cwdBasename}`);
425
+ const title = sessionName ? `${DISPLAY_NAME} - ${sessionName}` : DISPLAY_NAME;
426
+ // Add spinner if agent is thinking or working
427
+ const isActive = this.session.agent.state.isStreaming || this.session.agent.state.pendingToolCalls.size > 0;
428
+ if (isActive) {
429
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
430
+ const frame = frames[Math.floor(Date.now() / 100) % frames.length];
431
+ this.ui.terminal.setTitle(`${frame} ${title}`);
423
432
  }
424
433
  else {
425
- this.ui.terminal.setTitle(`${DISPLAY_NAME} - ${cwdBasename}`);
434
+ this.ui.terminal.setTitle(title);
426
435
  }
427
436
  }
428
437
  /**
@@ -449,6 +458,13 @@ export class InteractiveMode {
449
458
  this.showWarning(warning);
450
459
  }
451
460
  });
461
+ // Start thinking/working animation loop for terminal title
462
+ setInterval(() => {
463
+ const state = this.session.agent.state;
464
+ if (state.isStreaming || state.pendingToolCalls.size > 0) {
465
+ this.updateTerminalTitle();
466
+ }
467
+ }, 100);
452
468
  // Show startup warnings
453
469
  const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
454
470
  if (migratedProviders && migratedProviders.length > 0) {
@@ -1895,7 +1911,27 @@ export class InteractiveMode {
1895
1911
  this.loadingAnimation.stop();
1896
1912
  }
1897
1913
  this.statusContainer.clear();
1898
- this.loadingAnimation = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), this.defaultWorkingMessage);
1914
+ // Create loading animation with "shimmer" effect support
1915
+ this.loadingAnimation = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => {
1916
+ // Custom "shimmer" effect for the working message text
1917
+ const chars = text.split("");
1918
+ if (chars.length === 0)
1919
+ return "";
1920
+ const sweepSeconds = 2.0;
1921
+ const padding = 10;
1922
+ const period = chars.length + padding * 2;
1923
+ const posF = ((Date.now() / 1000) % sweepSeconds) / sweepSeconds * period;
1924
+ const pos = Math.floor(posF);
1925
+ const bandHalfWidth = 5;
1926
+ return chars.map((ch, i) => {
1927
+ const dist = Math.abs(i + padding - pos);
1928
+ if (dist <= bandHalfWidth) {
1929
+ // Glow effect: Bold + Accent color
1930
+ return theme.bold(theme.fg("accent", ch));
1931
+ }
1932
+ return theme.fg("muted", ch);
1933
+ }).join("");
1934
+ }, this.defaultWorkingMessage);
1899
1935
  this.statusContainer.addChild(this.loadingAnimation);
1900
1936
  // Apply any pending working message queued before loader existed
1901
1937
  if (this.pendingWorkingMessage !== undefined) {