zidane 6.2.23 → 6.2.24

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 (80) hide show
  1. package/dist/{acp-B7wH6LhE.js → acp-CTn1kFvP.js} +4 -4
  2. package/dist/{acp-B7wH6LhE.js.map → acp-CTn1kFvP.js.map} +1 -1
  3. package/dist/acp-cli.js +2 -2
  4. package/dist/acp.d.ts +1 -1
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-BSIMsEtD.js → agent-tLfGSSQw.js} +59 -25
  7. package/dist/agent-tLfGSSQw.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/chat/pure.d.ts +3 -3
  11. package/dist/chat/pure.js +1 -1
  12. package/dist/chat.d.ts +4 -4
  13. package/dist/chat.js +4 -4
  14. package/dist/contexts/daytona.d.ts +1 -1
  15. package/dist/contexts/e2b.d.ts +1 -1
  16. package/dist/eval.d.ts +1 -1
  17. package/dist/eval.js +1 -1
  18. package/dist/extensions.d.ts +1 -1
  19. package/dist/extensions.js +1 -1
  20. package/dist/headless.d.ts +1 -1
  21. package/dist/headless.js +2 -2
  22. package/dist/{index-Bv9qhj4n.d.ts → index-8zm_hwdY.d.ts} +44 -3
  23. package/dist/index-8zm_hwdY.d.ts.map +1 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.js +6 -6
  26. package/dist/{login-BkZ_d5sT.js → login-DfJGuHUj.js} +2 -2
  27. package/dist/{login-BkZ_d5sT.js.map → login-DfJGuHUj.js.map} +1 -1
  28. package/dist/mcp.d.ts +1 -1
  29. package/dist/output/stream-json.d.ts +1 -1
  30. package/dist/output/terminal.d.ts +1 -1
  31. package/dist/presets.d.ts +1 -1
  32. package/dist/presets.js +1 -1
  33. package/dist/providers/anthropic.d.ts +1 -1
  34. package/dist/providers/arcee.d.ts +1 -1
  35. package/dist/providers/baseten.d.ts +1 -1
  36. package/dist/providers/cerebras.d.ts +1 -1
  37. package/dist/providers/local.d.ts +1 -1
  38. package/dist/providers/openai-compat.d.ts +1 -1
  39. package/dist/providers/openai.d.ts +1 -1
  40. package/dist/providers/openrouter.d.ts +1 -1
  41. package/dist/providers/xai.d.ts +1 -1
  42. package/dist/providers.d.ts +1 -1
  43. package/dist/{resolve-x5mlFtLX.js → resolve-BPTA1RpX.js} +23 -3
  44. package/dist/resolve-BPTA1RpX.js.map +1 -0
  45. package/dist/restate.d.ts +1 -1
  46. package/dist/session/sqlite.d.ts +1 -1
  47. package/dist/{session-Dbvz1ZUE.js → session-jLsc_kMH.js} +22 -3
  48. package/dist/session-jLsc_kMH.js.map +1 -0
  49. package/dist/session.d.ts +2 -2
  50. package/dist/session.js +2 -2
  51. package/dist/{skills-C1vGKbqv.js → skills-C7eIjWYm.js} +2 -2
  52. package/dist/{skills-C1vGKbqv.js.map → skills-C7eIjWYm.js.map} +1 -1
  53. package/dist/skills.d.ts +1 -1
  54. package/dist/skills.js +2 -2
  55. package/dist/{tool-formatters-D9P6Znip.d.ts → tool-formatters-CZJYzP4x.d.ts} +2 -2
  56. package/dist/{tool-formatters-D9P6Znip.d.ts.map → tool-formatters-CZJYzP4x.d.ts.map} +1 -1
  57. package/dist/tools/fetch-url.d.ts +1 -1
  58. package/dist/tools/web-search.d.ts +1 -1
  59. package/dist/tools.d.ts +1 -1
  60. package/dist/tools.js +1 -1
  61. package/dist/{transcript-anchors-Ci5888xS.d.ts → transcript-anchors-CJ2dmpQk.d.ts} +2 -2
  62. package/dist/{transcript-anchors-Ci5888xS.d.ts.map → transcript-anchors-CJ2dmpQk.d.ts.map} +1 -1
  63. package/dist/{transcript-anchors-CqzIr74w.js → transcript-anchors-Ded7iJjG.js} +11 -7
  64. package/dist/transcript-anchors-Ded7iJjG.js.map +1 -0
  65. package/dist/tui.d.ts +15 -5
  66. package/dist/tui.d.ts.map +1 -1
  67. package/dist/tui.js +742 -315
  68. package/dist/tui.js.map +1 -1
  69. package/dist/{turn-operations-CokV32wf.js → turn-operations-ClGa1vnb.js} +6 -2
  70. package/dist/{turn-operations-CokV32wf.js.map → turn-operations-ClGa1vnb.js.map} +1 -1
  71. package/dist/{turn-operations-C3y6A82G.d.ts → turn-operations-DZYpXcU1.d.ts} +3 -3
  72. package/dist/{turn-operations-C3y6A82G.d.ts.map → turn-operations-DZYpXcU1.d.ts.map} +1 -1
  73. package/dist/types-BIarq1qE.js.map +1 -1
  74. package/dist/types.d.ts +1 -1
  75. package/package.json +1 -1
  76. package/dist/agent-BSIMsEtD.js.map +0 -1
  77. package/dist/index-Bv9qhj4n.d.ts.map +0 -1
  78. package/dist/resolve-x5mlFtLX.js.map +0 -1
  79. package/dist/session-Dbvz1ZUE.js.map +0 -1
  80. package/dist/transcript-anchors-CqzIr74w.js.map +0 -1
package/dist/tui.js CHANGED
@@ -1,25 +1,25 @@
1
- import { $ as McpAuthProvider, $t as filterChainFallbacks, A as runOAuthLogin, At as useSettings, B as mcpToolsCachePath, Ct as DEFAULT_SETTINGS, D as formatPathForCwd, Dn as accentColor, Dt as SETTINGS_TOGGLES, Et as SETTINGS_INPUTS, F as parseJsonTolerant, G as useMcpToolToggleMap, Gt as useDiscoveryOptional, Ht as createDiscoverySlot, I as parseMcpsFile, J as indexOfServerRow, Jt as resolveConfig, Kt as ConfigProvider, L as parseClaudeState, M as buildMcpServers, N as defaultMcpsConfigPaths, Nn as TODO_STATUS_GLYPHS, Nt as resolveChipColor, O as fetchOAuthRedirect, Ot as SettingsProvider, P as discoverProjectMcps, Pt as resolveTheme, Qt as chainsEqual, Sr as usageByModel, St as useEnabledToggleSet, T as suggestSafelistEntries, Tt as SETTINGS_CHOICES, U as subscribeMcpToolsCache, Un as useActiveTodos, Ut as DiscoveryProvider, V as refreshMcpToolsCatalog, Wt as useDiscovery, X as createFileMcpCredentialStore, Xt as buildRunModel, Y as parentServerName, Yt as resolveStoragePaths, Zt as chainPositionOf, _ as useSafeModeQueue, _r as sumRunCosts, _t as hintsLength, a as useSurfaces, an as tryOpenBrowser, at as InteractionsProvider, b as getSafelist, br as toolResultText, c as useStreamBuffer, cn as buildUpdateHint, cr as deriveSessionTitle, ct as createInteractionTools, d as discoverProjectSkills, dr as listSessionMeta, dt as pendingInteractionsFromTurns, en as providerKeySetChanged, er as buildBuildSystem, et as useMcpAuthDispatch, fr as loadState, g as useSafeModeActions, gr as stripSpawnTokensLine, gt as clipHintsToWidth, h as SafeModeProvider, hr as serverToolResultSummary, ht as EMPTY_HINTS, i as useSelectStyle, in as useCompletion, ir as createChatTurnsReadTool, it as ASK_USER_TOOL, j as supportsOAuth, k as oauthUsesManualCodePaste, kt as clampFps, l as buildSkillsConfig, ln as useUpdateCheck, lr as eventsFromTurns, m as writeSessionExport, mt as useInteractionsQueue, n as ThemeProvider, nr as envSection, nt as getMcpAuthStatus, or as compactSummaryEvent, ot as PRESENT_PLAN_TOOL, pr as marginTopFor, pt as useInteractionsActions, q as buildVisibleMcpRows, qt as useConfig, r as useColors, rn as toggleChainEntry, rr as chatReferenceTokensForPrompt, s as useTheme, sn as bootTick, st as buildResumedToolResultsTurn, t as computeTurnAnchors, tn as restoreChainFallbacks, tr as buildPlanSystem, tt as useMcpAuthState, u as defaultSkillScanPaths, ur as lastContextSizeFromTurns, ut as makeRequestInteraction, vn as chatAutoCompactBehavior, vt as truncateTrailing, wt as SETTINGS_CATEGORIES, x as isOnSafelist, xr as updateToolEventOutcomes, xt as listProjectFiles, y as addToSafelist, yn as discoverAgentsMd, yr as toolCallPreview, yt as buildHints, z as loadMcpToolsCache } from "./transcript-anchors-CqzIr74w.js";
2
- import { $ as composeExtensionPreset, B as ExtensionInstallError, Bt as ensureKeybindingsFile, Et as createModelSearchTool, H as installExtensionFromSpec, Ht as groupBindings, J as createExtensionHud, K as removeInstalledExtension, P as summaryToTurn, St as tailTruncate, Ut as keybindingsPath, Vt as formatBindingForDisplay, Wt as matchesBinding, X as discoverAndBuildExtensionRegistry, Y as registerExtensionHostModules, Yt as resolveExtensionKeybindings, _ as buildPostCompactAttachments, c as applyCompactSummaryCutoff, dn as sanitizeExtensionConfigActionPatch, en as inlineLoadedExtension, et as disposeExtensionRegistry, f as cleanupPersistedSession, g as resolveTasksDir, h as resolvePersistDir, hn as effectiveContextWindow, ht as isSpawnTool, it as createExtensionHostRuntimeBridge, lt as basic_default, m as resolveMcpWarningsDir, mn as OUTPUT_RESERVE_TOKENS, nn as parseExtensionScanPathEnv, on as coerceExtensionConfigInput, q as updateInstalledExtension, rn as scanExtensionDirectories, rt as partitionExtensionCatalog, t as createAgent, tt as filterExtensionRegistry, un as resolveExtensionConfigValue, wt as looksBinary, x as compactConversation, y as selectFilesFromSession, z as createExtensionPromptBridge, zt as KEYBINDING_KEY_COL_WIDTH } from "./agent-BSIMsEtD.js";
1
+ import { $ as McpAuthProvider, $t as filterChainFallbacks, A as runOAuthLogin, At as useSettings, B as mcpToolsCachePath, Ct as DEFAULT_SETTINGS, D as formatPathForCwd, Dn as accentColor, Dt as SETTINGS_TOGGLES, Et as SETTINGS_INPUTS, F as parseJsonTolerant, G as useMcpToolToggleMap, Gt as useDiscoveryOptional, Ht as createDiscoverySlot, I as parseMcpsFile, J as indexOfServerRow, Jt as resolveConfig, Kt as ConfigProvider, L as parseClaudeState, M as buildMcpServers, N as defaultMcpsConfigPaths, Nn as TODO_STATUS_GLYPHS, Nt as resolveChipColor, O as fetchOAuthRedirect, Ot as SettingsProvider, P as discoverProjectMcps, Pt as resolveTheme, Qt as chainsEqual, Sr as usageByModel, St as useEnabledToggleSet, T as suggestSafelistEntries, Tt as SETTINGS_CHOICES, U as subscribeMcpToolsCache, Un as useActiveTodos, Ut as DiscoveryProvider, V as refreshMcpToolsCatalog, Wt as useDiscovery, X as createFileMcpCredentialStore, Xt as buildRunModel, Y as parentServerName, Yt as resolveStoragePaths, Zt as chainPositionOf, _ as useSafeModeQueue, _r as sumRunCosts, _t as hintsLength, a as useSurfaces, an as tryOpenBrowser, at as InteractionsProvider, b as getSafelist, br as toolResultText, c as useStreamBuffer, cn as buildUpdateHint, cr as deriveSessionTitle, ct as createInteractionTools, d as discoverProjectSkills, dr as listSessionMeta, dt as pendingInteractionsFromTurns, en as providerKeySetChanged, er as buildBuildSystem, et as useMcpAuthDispatch, fr as loadState, g as useSafeModeActions, gr as stripSpawnTokensLine, gt as clipHintsToWidth, h as SafeModeProvider, hr as serverToolResultSummary, ht as EMPTY_HINTS, i as useSelectStyle, in as useCompletion, ir as createChatTurnsReadTool, it as ASK_USER_TOOL, j as supportsOAuth, k as oauthUsesManualCodePaste, kt as clampFps, l as buildSkillsConfig, ln as useUpdateCheck, lr as eventsFromTurns, m as writeSessionExport, mt as useInteractionsQueue, n as ThemeProvider, nr as envSection, nt as getMcpAuthStatus, or as compactSummaryEvent, ot as PRESENT_PLAN_TOOL, pr as marginTopFor, pt as useInteractionsActions, q as buildVisibleMcpRows, qt as useConfig, r as useColors, rn as toggleChainEntry, rr as chatReferenceTokensForPrompt, s as useTheme, sn as bootTick, st as buildResumedToolResultsTurn, t as computeTurnAnchors, tn as restoreChainFallbacks, tr as buildPlanSystem, tt as useMcpAuthState, u as defaultSkillScanPaths, ur as lastContextSizeFromTurns, ut as makeRequestInteraction, vn as chatAutoCompactBehavior, vt as truncateTrailing, wt as SETTINGS_CATEGORIES, x as isOnSafelist, xr as updateToolEventOutcomes, xt as listProjectFiles, y as addToSafelist, yn as discoverAgentsMd, yr as toolCallPreview, yt as buildHints, z as loadMcpToolsCache } from "./transcript-anchors-Ded7iJjG.js";
2
+ import { $ as composeExtensionPreset, B as ExtensionInstallError, Bt as ensureKeybindingsFile, Et as createModelSearchTool, H as installExtensionFromSpec, Ht as groupBindings, J as createExtensionHud, K as removeInstalledExtension, P as summaryToTurn, St as tailTruncate, Ut as keybindingsPath, Vt as formatBindingForDisplay, Wt as matchesBinding, X as discoverAndBuildExtensionRegistry, Y as registerExtensionHostModules, Yt as resolveExtensionKeybindings, _ as buildPostCompactAttachments, c as applyCompactSummaryCutoff, dn as sanitizeExtensionConfigActionPatch, en as inlineLoadedExtension, et as disposeExtensionRegistry, f as cleanupPersistedSession, g as resolveTasksDir, h as resolvePersistDir, hn as effectiveContextWindow, ht as isSpawnTool, it as createExtensionHostRuntimeBridge, lt as basic_default, m as resolveMcpWarningsDir, mn as OUTPUT_RESERVE_TOKENS, nn as parseExtensionScanPathEnv, on as coerceExtensionConfigInput, q as updateInstalledExtension, rn as scanExtensionDirectories, rt as partitionExtensionCatalog, t as createAgent, tt as filterExtensionRegistry, un as resolveExtensionConfigValue, wt as looksBinary, x as compactConversation, y as selectFilesFromSession, z as createExtensionPromptBridge, zt as KEYBINDING_KEY_COL_WIDTH } from "./agent-tLfGSSQw.js";
3
3
  import { a as formatDuration, c as previewLine, i as fmtTokens, l as shortId, n as compactPath, o as formatTaskStatus, r as fmtCost, s as formatTaskSummary, t as ageString } from "./format-BF745pLL.js";
4
4
  import { n as appendCapped } from "./output-cap-kBUHBlnq.js";
5
5
  import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-A4ep-iJs.js";
6
6
  import { l as errorMessage } from "./errors-DJUxZg9b.js";
7
- import { M as findGitRoot, j as projectUserPaths } from "./resolve-x5mlFtLX.js";
7
+ import { M as findGitRoot, j as projectUserPaths } from "./resolve-BPTA1RpX.js";
8
8
  import { a as createRingBufferSink, i as createLoggingHooks, l as logFileTimestamp, n as createFileSink, o as fanoutSink, r as createLogger, s as formatLogLine, t as consoleSink } from "./logger-DBX9uYOw.js";
9
9
  import { i as sniffImageMediaType } from "./media-sniff-Bn76JxAu.js";
10
10
  import { d as McpOAuthProvider, i as nonOwningMcpView, n as connectMcpServers, p as hasAuthorizationHeader } from "./mcp-H6bqyM94.js";
11
- import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
12
11
  import { replaceDynamicSection } from "./prompt.js";
13
- import { a as getExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, s as setExtensionSessionState, t as createSession } from "./session-Dbvz1ZUE.js";
12
+ import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-jLsc_kMH.js";
13
+ import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
14
14
  import { o as withTimeout } from "./timeout-BDetbuN6.js";
15
15
  import { a as generateStructuredData, c as shouldAutoGenerateTitle, f as commandBlockToEvent, g as parseCommandInvocation, h as executeCommand, n as patchInlineGhost, p as commandBlocksToContent, r as resolveInlineSuggestion, s as generateSessionTitle, t as createPathAutocompleteProvider } from "./inline-autocomplete-BAwBcZtD.js";
16
16
  import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
17
- import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-C1vGKbqv.js";
18
- import { i as ZIDANE_EXTENSION_SKILL_NAME, l as withZidaneExtensionSkill, o as extensionDirForCwd, r as EXTENSION_EDIT_METADATA_KEY, s as extensionScaffold, t as loginMcpServer } from "./login-BkZ_d5sT.js";
17
+ import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-C7eIjWYm.js";
18
+ import { i as ZIDANE_EXTENSION_SKILL_NAME, l as withZidaneExtensionSkill, o as extensionDirForCwd, r as EXTENSION_EDIT_METADATA_KEY, s as extensionScaffold, t as loginMcpServer } from "./login-DfJGuHUj.js";
19
19
  import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning } from "./auth-CpdQebUy.js";
20
20
  import { createDockerContext } from "./contexts/docker.js";
21
21
  import { createTuiStore } from "./session/sqlite.js";
22
- import { $ as extractEditPayload, B as isVisible, C as chunkMarkdownForRender, E as stripHtmlCommentsOutsideCode, F as BASH_CALL_ID_PREFIX, G as streamEventSearchText, H as promptHistoryFromEvents, I as EDIT_TOOL_NAMES, J as buildContextualDiff, K as turnSelectionOwnership, L as isEditErrorResult, N as blendHsl, O as createSkillsCompletionProvider, P as buildLinearRamp, R as isTurnHighlighted, T as splitMarkdownCodeBlocks, U as selectableTurnIds, V as isVisibleDuringSearch, W as selectionAfterTurnRemoval, X as clipDiffForRender, Y as buildUnifiedDiff, _ as turnContextSize, a as TOOL_DISPLAY, at as buildEditOutcomesAnnotation, b as filterModelCatalog, ct as parseEditOutcomesFromResult, d as finalizeStreamingMarkdown, dt as stripEditOutcomesAnnotation, et as filetypeFromPath, f as finalizeStreamingMarkdownForOwner, ft as summarizeOutcomes, g as revealEarlierTranscriptEvents, h as replaceTranscriptTurnEvents, ht as createChatsCompletionProvider, i as turnAsText, j as createFilesCompletionProvider, k as uniqueSkillNamesFromReferences, lt as resolveApprovalForPayload, n as deleteTurnSafely, o as displayNameFor, p as hiddenEarlierTranscriptCount, r as truncateTurnsAt, rt as summarizeEditPayload, s as formatToolCall, st as mergeApprovalAndBodyOutcomes, tt as previewEditPayload, u as capTranscriptEvents, ut as rewriteMultiEditHeader, v as splitPromptSegments, w as normalizeMarkdownCodeFences, y as buildModelCatalog, z as isUserBashEvent } from "./turn-operations-CokV32wf.js";
22
+ import { $ as extractEditPayload, B as isVisible, C as chunkMarkdownForRender, E as stripHtmlCommentsOutsideCode, F as BASH_CALL_ID_PREFIX, G as streamEventSearchText, H as promptHistoryFromEvents, I as EDIT_TOOL_NAMES, J as buildContextualDiff, K as turnSelectionOwnership, L as isEditErrorResult, N as blendHsl, O as createSkillsCompletionProvider, P as buildLinearRamp, R as isTurnHighlighted, T as splitMarkdownCodeBlocks, U as selectableTurnIds, V as isVisibleDuringSearch, W as selectionAfterTurnRemoval, X as clipDiffForRender, Y as buildUnifiedDiff, _ as turnContextSize, a as TOOL_DISPLAY, at as buildEditOutcomesAnnotation, b as filterModelCatalog, ct as parseEditOutcomesFromResult, d as finalizeStreamingMarkdown, dt as stripEditOutcomesAnnotation, et as filetypeFromPath, f as finalizeStreamingMarkdownForOwner, ft as summarizeOutcomes, g as revealEarlierTranscriptEvents, h as replaceTranscriptTurnEvents, ht as createChatsCompletionProvider, i as turnAsText, j as createFilesCompletionProvider, k as uniqueSkillNamesFromReferences, lt as resolveApprovalForPayload, n as deleteTurnSafely, o as displayNameFor, p as hiddenEarlierTranscriptCount, r as truncateTurnsAt, rt as summarizeEditPayload, s as formatToolCall, st as mergeApprovalAndBodyOutcomes, tt as previewEditPayload, u as capTranscriptEvents, ut as rewriteMultiEditHeader, v as splitPromptSegments, w as normalizeMarkdownCodeFences, y as buildModelCatalog, z as isUserBashEvent } from "./turn-operations-ClGa1vnb.js";
23
23
  import { a as searchHighlightRanges, c as searchSessionHistoryIndexAsync, i as highlightSegments, n as buildSessionSearchIndexAsync, r as filterSessionSearchCatalog, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
24
24
  import { basename, dirname, join, relative, resolve } from "node:path";
25
25
  import { Buffer } from "node:buffer";
@@ -483,7 +483,12 @@ async function importClaudeSessions(store, opts = {}) {
483
483
  failed: []
484
484
  };
485
485
  for (const file of listClaudeSessionFiles(opts)) try {
486
- const converted = convertClaudeSession(readFileSync(file.path, "utf8"), { fallbackId: basename(file.path, ".jsonl") });
486
+ const fallbackId = basename(file.path, ".jsonl");
487
+ if (await store.load(fallbackId)) {
488
+ result.skipped++;
489
+ continue;
490
+ }
491
+ const converted = convertClaudeSession(readFileSync(file.path, "utf8"), { fallbackId });
487
492
  if (!converted) {
488
493
  result.empty++;
489
494
  continue;
@@ -1676,6 +1681,200 @@ function segmentDiffStatLine(line) {
1676
1681
  }];
1677
1682
  }
1678
1683
  //#endregion
1684
+ //#region src/tui/clickable.tsx
1685
+ /** Glyph / word aliases → OpenTUI key names (`parse.keypress` conventions). */
1686
+ const KEY_NAME_ALIASES = {
1687
+ "esc": "escape",
1688
+ "↵": "return",
1689
+ "enter": "return",
1690
+ "tab": "tab",
1691
+ "space": "space",
1692
+ "⌫": "backspace",
1693
+ "backspace": "backspace",
1694
+ "pgup": "pageup",
1695
+ "pgdn": "pagedown",
1696
+ "↑": "up",
1697
+ "↓": "down",
1698
+ "←": "left",
1699
+ "→": "right",
1700
+ "↑↓": "down",
1701
+ "←→": "right",
1702
+ "del": "delete"
1703
+ };
1704
+ /** Decorative pseudo-keys that must never dispatch (chips, markers). */
1705
+ const NON_KEYS = /* @__PURE__ */ new Set([
1706
+ "✦",
1707
+ "·",
1708
+ "…"
1709
+ ]);
1710
+ /**
1711
+ * Parse a hint key spec (`ctrl+g`, `shift+↵`, `esc`, `a`, `↑↓`) into a
1712
+ * dispatchable {@link ParsedKey}, or `null` when the spec is decorative
1713
+ * or ambiguous (the hint then renders non-clickable). Modifier segments
1714
+ * are order-insensitive; the final segment is the key itself.
1715
+ */
1716
+ function parseHintKeySpec(spec) {
1717
+ const trimmed = spec.trim().toLowerCase();
1718
+ if (!trimmed || NON_KEYS.has(trimmed)) return null;
1719
+ const segments = (trimmed.split("/")[0] ?? "").split("+").map((s) => s.trim()).filter(Boolean);
1720
+ if (segments.length === 0) return null;
1721
+ const keyPart = segments[segments.length - 1] ?? "";
1722
+ const modifiers = segments.slice(0, -1);
1723
+ const mods = {
1724
+ ctrl: false,
1725
+ meta: false,
1726
+ shift: false,
1727
+ option: false
1728
+ };
1729
+ for (const mod of modifiers) if (mod === "ctrl" || mod === "control") mods.ctrl = true;
1730
+ else if (mod === "shift") mods.shift = true;
1731
+ else if (mod === "alt" || mod === "option") mods.option = true;
1732
+ else if (mod === "meta" || mod === "cmd" || mod === "super") mods.meta = true;
1733
+ else return null;
1734
+ const name = KEY_NAME_ALIASES[keyPart] ?? (keyPart.length === 1 ? keyPart : null);
1735
+ if (!name) return null;
1736
+ const sequence = keySequence(name, mods);
1737
+ return {
1738
+ name,
1739
+ ...mods,
1740
+ sequence,
1741
+ number: /^\d$/.test(name),
1742
+ raw: sequence,
1743
+ eventType: "press",
1744
+ source: "raw"
1745
+ };
1746
+ }
1747
+ /** Byte sequences real terminals send for the named specials we synthesize. */
1748
+ const NAMED_KEY_SEQUENCES = {
1749
+ return: "\r",
1750
+ tab: " ",
1751
+ escape: "\x1B",
1752
+ space: " ",
1753
+ backspace: "",
1754
+ delete: "\x1B[3~",
1755
+ up: "\x1B[A",
1756
+ down: "\x1B[B",
1757
+ right: "\x1B[C",
1758
+ left: "\x1B[D",
1759
+ pageup: "\x1B[5~",
1760
+ pagedown: "\x1B[6~"
1761
+ };
1762
+ /**
1763
+ * The `sequence`/`raw` a REAL keypress would carry — without it the
1764
+ * synthetic event is second-class: focused inputs insert text from
1765
+ * `key.sequence` (so a clicked `@ files` hint must actually type `@`),
1766
+ * and some handlers match on `sequence` rather than `name`. Modifier
1767
+ * chords return `''`: their handlers dispatch on `name` + flags, and
1768
+ * fabricating control bytes would risk double interpretation.
1769
+ */
1770
+ function keySequence(name, mods) {
1771
+ if (mods.ctrl || mods.meta || mods.option) return "";
1772
+ if (mods.shift) return name === "tab" ? "\x1B[Z" : "";
1773
+ if (name.length === 1) return name;
1774
+ return NAMED_KEY_SEQUENCES[name] ?? "";
1775
+ }
1776
+ /**
1777
+ * Dispatch a hint key spec through the renderer's key pipeline. Returns
1778
+ * `false` for unparseable specs (no-op). Exposed for non-hint surfaces
1779
+ * that want the same click==keypress contract.
1780
+ */
1781
+ function dispatchHintKey(renderer, spec) {
1782
+ const parsed = parseHintKeySpec(spec);
1783
+ if (!parsed) return false;
1784
+ renderer.keyInput.processParsedKey(parsed);
1785
+ return true;
1786
+ }
1787
+ /**
1788
+ * True when a row click landed on the row's `[✓] ` checkbox cell — the
1789
+ * columns `[startCol, startCol + width)` of the FIRST line of the target
1790
+ * renderable. Rows that must stay single `<text>` renderables (the MCP
1791
+ * list documents an OpenTUI layout regression forbidding box wrappers)
1792
+ * can't make the checkbox its own renderable, so zone-splitting works by
1793
+ * coordinate instead: `event.x/y` are absolute, the target renderable
1794
+ * knows its own absolute origin, and the difference is the glyph column.
1795
+ * The `y` check pins line 0 so wrapped continuation lines can't false-
1796
+ * positive into the checkbox range. Degrades to `false` (row semantics)
1797
+ * when the target's geometry is unavailable.
1798
+ */
1799
+ function inCheckboxZone(event, startCol, width = 4) {
1800
+ const target = event.target;
1801
+ if (!target || typeof target.x !== "number" || typeof target.y !== "number") return false;
1802
+ if (event.y - target.y !== 0) return false;
1803
+ const col = event.x - target.x;
1804
+ return col >= startCol && col < startCol + width;
1805
+ }
1806
+ /** Spans for one hint's text content — shared by hover and rest states. */
1807
+ function hintSpans(hint, COLOR, hovered) {
1808
+ const keyColor = hovered ? COLOR.accent : hint.keyColor ?? COLOR.warn;
1809
+ const labelColor = hovered ? COLOR.accent : hint.labelColor ?? COLOR.dim;
1810
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1811
+ hint.key !== "" && /* @__PURE__ */ jsx$1("span", {
1812
+ fg: keyColor,
1813
+ children: hint.key
1814
+ }),
1815
+ hint.extra && /* @__PURE__ */ jsx$1("span", {
1816
+ fg: hint.extra.keyColor ?? COLOR.mute,
1817
+ children: hint.extra.key
1818
+ }),
1819
+ /* @__PURE__ */ jsx$1("span", {
1820
+ fg: labelColor,
1821
+ children: hint.key === "" ? hint.label : ` ${hint.label}`
1822
+ }),
1823
+ hint.extra && /* @__PURE__ */ jsx$1("span", {
1824
+ fg: hint.extra.labelColor ?? COLOR.dim,
1825
+ children: ` ${hint.extra.label}`
1826
+ })
1827
+ ] });
1828
+ }
1829
+ /**
1830
+ * One hint as its own `<text>` renderable — clickable when its key spec
1831
+ * parses, hover-highlighted in the theme accent. Non-parseable hints
1832
+ * render identically but inert, so decorative chips cost nothing.
1833
+ */
1834
+ function ClickableHint({ hint }) {
1835
+ const COLOR = useColors();
1836
+ const renderer = useRenderer();
1837
+ const [hovered, setHovered] = useState(false);
1838
+ if (!(parseHintKeySpec(hint.key) !== null)) return /* @__PURE__ */ jsx$1("text", {
1839
+ selectable: false,
1840
+ children: hintSpans(hint, COLOR, false)
1841
+ });
1842
+ return /* @__PURE__ */ jsx$1("text", {
1843
+ selectable: false,
1844
+ onMouseDown: (event) => {
1845
+ event.stopPropagation();
1846
+ event.preventDefault();
1847
+ dispatchHintKey(renderer, hint.key);
1848
+ },
1849
+ onMouseOver: () => setHovered(true),
1850
+ onMouseOut: () => setHovered(false),
1851
+ children: hintSpans(hint, COLOR, hovered)
1852
+ });
1853
+ }
1854
+ /**
1855
+ * A row of clickable hints with ` · ` separators — the renderable-per-hint
1856
+ * replacement for the span-based `renderHintSpans`. Text content is
1857
+ * byte-identical to the span renderer, so existing `hintsLength` width
1858
+ * math keeps holding.
1859
+ */
1860
+ function HintRow({ hints }) {
1861
+ const COLOR = useColors();
1862
+ return /* @__PURE__ */ jsx$1("box", {
1863
+ style: {
1864
+ flexDirection: "row",
1865
+ flexWrap: "wrap"
1866
+ },
1867
+ children: hints.map((hint, i) => /* @__PURE__ */ jsxs("box", {
1868
+ style: { flexDirection: "row" },
1869
+ children: [i > 0 && /* @__PURE__ */ jsx$1("text", {
1870
+ selectable: false,
1871
+ fg: COLOR.mute,
1872
+ children: " · "
1873
+ }), /* @__PURE__ */ jsx$1(ClickableHint, { hint })]
1874
+ }, `${hint.key}\u0000${hint.label}\u0000${i}`))
1875
+ });
1876
+ }
1877
+ //#endregion
1679
1878
  //#region src/tui/crush-throbber.tsx
1680
1879
  /** @jsxImportSource @opentui/react */
1681
1880
  const CRUSH_RUNES = "0123456789abcdefABCDEF~!@#$£€%^&*()+=_";
@@ -2311,7 +2510,7 @@ function onInputSubmit(handler) {
2311
2510
  * Width tiering is driven by plain-text length estimates — close enough
2312
2511
  * since the segments are ASCII-heavy.
2313
2512
  */
2314
- function Footer({ hints, context, cost = null, status = null, compactionTokens = null, compactionStartedAt = null }) {
2513
+ function Footer({ hints, context, cost = null, status = null, compactionTokens = null, compactionStartedAt = null, retry = null }) {
2315
2514
  const { width } = useTerminalDimensions();
2316
2515
  const showCost = typeof cost === "number" && cost > 0;
2317
2516
  const inner = Math.max(0, width - 2);
@@ -2325,8 +2524,8 @@ function Footer({ hints, context, cost = null, status = null, compactionTokens =
2325
2524
  const timer = setInterval(() => setCompactTick((t) => t + 1), 1e3);
2326
2525
  return () => clearInterval(timer);
2327
2526
  }, [status]);
2328
- const statusLabel = status === "compacting" ? compactingLabel(compactionTokens, compactionStartedAt) : status === "searching" ? " Searching messages…" : "";
2329
- const sW = status === "compacting" || status === "searching" ? 2 + statusLabel.length : status === "asking" ? 3 : status ? 2 : 0;
2527
+ const statusLabel = status === "compacting" ? compactingLabel(compactionTokens, compactionStartedAt) : status === "searching" ? " Searching messages…" : status === "retrying" ? retryingLabel(retry) : "";
2528
+ const sW = status === "compacting" || status === "searching" || status === "retrying" ? 2 + statusLabel.length : status === "asking" ? 3 : status ? 2 : 0;
2330
2529
  if (hW + sW + (rightW > 0 ? rightW + 1 : 0) <= inner) return /* @__PURE__ */ jsxs("box", {
2331
2530
  style: {
2332
2531
  flexDirection: "row",
@@ -2395,17 +2594,25 @@ function RightStatus({ cost, context }) {
2395
2594
  function FooterStatusIcon({ status, label = "" }) {
2396
2595
  const COLOR = useColors();
2397
2596
  if (!status) return null;
2398
- const glyph = status === "asking" ? /* @__PURE__ */ jsx$1("span", { children: "❓" }) : status === "busy" ? /* @__PURE__ */ jsx$1(StatusSpinner, { color: COLOR.warn }) : /* @__PURE__ */ jsx$1(StatusSpinner, { color: COLOR.accent });
2597
+ const glyph = status === "asking" ? /* @__PURE__ */ jsx$1("span", { children: "❓" }) : status === "busy" || status === "retrying" ? /* @__PURE__ */ jsx$1(StatusSpinner, { color: COLOR.warn }) : /* @__PURE__ */ jsx$1(StatusSpinner, { color: COLOR.accent });
2399
2598
  return /* @__PURE__ */ jsxs("text", { children: [
2400
2599
  /* @__PURE__ */ jsx$1("span", { children: " " }),
2401
2600
  glyph,
2402
2601
  label && /* @__PURE__ */ jsx$1("span", {
2403
- fg: COLOR.accent,
2602
+ fg: status === "retrying" ? COLOR.warn : COLOR.accent,
2404
2603
  children: label
2405
2604
  })
2406
2605
  ] });
2407
2606
  }
2408
2607
  /**
2608
+ * Footer label for the retrying state — ` Retrying (n/max)...` where `n`
2609
+ * is the attempt about to be issued. Plain string for the same width-math
2610
+ * reason as {@link compactingLabel}.
2611
+ */
2612
+ function retryingLabel(retry) {
2613
+ return retry ? ` Retrying (${retry.attempt}/${retry.max})...` : " Retrying...";
2614
+ }
2615
+ /**
2409
2616
  * Footer label for the compacting state — ` Compacting...` plus liveness:
2410
2617
  * a `~2.1k` output-token estimate once the summary streams, and an elapsed
2411
2618
  * suffix while it doesn't (prompt ingestion is minutes of silence on
@@ -2419,11 +2626,7 @@ function compactingLabel(estTokens, startedAt) {
2419
2626
  return typeof estTokens === "number" && estTokens > 0 ? `${base} ~${fmtTokens(estTokens)}${elapsed}` : `${base}${elapsed}`;
2420
2627
  }
2421
2628
  function HintsText({ hints }) {
2422
- const COLOR = useColors();
2423
- return /* @__PURE__ */ jsx$1("text", {
2424
- fg: COLOR.dim,
2425
- children: renderHintSpans(hints, COLOR)
2426
- });
2629
+ return /* @__PURE__ */ jsx$1(HintRow, { hints });
2427
2630
  }
2428
2631
  /**
2429
2632
  * Pure renderer for a list of {@link Hint}s as colored spans —
@@ -7851,7 +8054,7 @@ const BULK_ACTIONS = [
7851
8054
  destructive: true
7852
8055
  }
7853
8056
  ];
7854
- function ActionBar({ actions, selected, customLabel }) {
8057
+ function ActionBar({ actions, selected, customLabel, onActivate, onHover }) {
7855
8058
  const COLOR = useColors();
7856
8059
  const SURFACE = useSurfaces();
7857
8060
  return /* @__PURE__ */ jsx$1("box", {
@@ -7869,6 +8072,12 @@ function ActionBar({ actions, selected, customLabel }) {
7869
8072
  marginRight: 2,
7870
8073
  flexShrink: 0
7871
8074
  },
8075
+ onMouseDown: (event) => {
8076
+ event.stopPropagation();
8077
+ event.preventDefault();
8078
+ onActivate?.(action, i);
8079
+ },
8080
+ onMouseOver: () => onHover?.(i),
7872
8081
  children: isSelected ? /* @__PURE__ */ jsx$1("text", {
7873
8082
  bg: tint,
7874
8083
  fg: SURFACE.background,
@@ -8030,7 +8239,9 @@ function SingleEditApprovalModal({ request, payload, priorContent, priorError, t
8030
8239
  })]
8031
8240
  }), /* @__PURE__ */ jsx$1(ActionBar, {
8032
8241
  actions: BULK_ACTIONS,
8033
- selected
8242
+ selected,
8243
+ onActivate: (action) => decide(action.decision),
8244
+ onHover: setSelected
8034
8245
  })]
8035
8246
  }), /* @__PURE__ */ jsxs("text", {
8036
8247
  style: {
@@ -8355,6 +8566,10 @@ function MultiEditApprovalModal({ request, payload, priorContent, priorError, ta
8355
8566
  customLabel: {
8356
8567
  idx: 0,
8357
8568
  label: applyLabel
8569
+ },
8570
+ onActivate: (action) => decide(resolveDecision(action.decision)),
8571
+ onHover: (i) => {
8572
+ if (zone === "actions") setActionIdx(i);
8358
8573
  }
8359
8574
  })
8360
8575
  ]
@@ -9341,6 +9556,19 @@ function MultiSelectQuestionInput({ question, initialChoiceIds, onSubmit }) {
9341
9556
  const isPicked = picked.has(choice.id);
9342
9557
  return /* @__PURE__ */ jsxs("text", {
9343
9558
  wrapMode: "word",
9559
+ onMouseDown: (event) => {
9560
+ event.stopPropagation();
9561
+ event.preventDefault();
9562
+ setCursor(i);
9563
+ if (!inCheckboxZone(event, 5)) return;
9564
+ setPicked((prev) => {
9565
+ const next = new Set(prev);
9566
+ if (next.has(choice.id)) next.delete(choice.id);
9567
+ else next.add(choice.id);
9568
+ return next;
9569
+ });
9570
+ },
9571
+ onMouseOver: () => setCursor(i),
9344
9572
  children: [
9345
9573
  /* @__PURE__ */ jsx$1("span", {
9346
9574
  fg: COLOR.mute,
@@ -9364,25 +9592,28 @@ function MultiSelectQuestionInput({ question, initialChoiceIds, onSubmit }) {
9364
9592
  })
9365
9593
  ]
9366
9594
  }, choice.id);
9367
- }), /* @__PURE__ */ jsxs("text", {
9368
- fg: COLOR.mute,
9369
- children: [
9370
- " ",
9371
- /* @__PURE__ */ jsx$1("span", {
9372
- fg: COLOR.warn,
9373
- children: "space"
9374
- }),
9375
- " toggle · ",
9376
- /* @__PURE__ */ jsx$1("span", {
9377
- fg: COLOR.warn,
9378
- children: "↵"
9379
- }),
9380
- ` submit${required ? " · " : ""}`,
9381
- required && /* @__PURE__ */ jsx$1("span", {
9382
- fg: COLOR.warn,
9383
- children: "required"
9384
- })
9385
- ]
9595
+ }), /* @__PURE__ */ jsxs("box", {
9596
+ style: { flexDirection: "row" },
9597
+ children: [/* @__PURE__ */ jsx$1("text", {
9598
+ fg: COLOR.mute,
9599
+ children: " "
9600
+ }), /* @__PURE__ */ jsx$1(HintRow, { hints: [
9601
+ {
9602
+ key: "space",
9603
+ label: "toggle",
9604
+ labelColor: COLOR.mute
9605
+ },
9606
+ {
9607
+ key: "↵",
9608
+ label: "submit",
9609
+ labelColor: COLOR.mute
9610
+ },
9611
+ ...required ? [{
9612
+ key: "",
9613
+ label: "required",
9614
+ labelColor: COLOR.warn
9615
+ }] : []
9616
+ ] })]
9386
9617
  })]
9387
9618
  });
9388
9619
  }
@@ -9407,7 +9638,9 @@ function OptionList({ items, initialCursor, onPick }) {
9407
9638
  if (typeof initialCursor !== "number" || items.length === 0) return 0;
9408
9639
  return Math.max(0, Math.min(items.length - 1, initialCursor));
9409
9640
  });
9641
+ const [clickArmed, setClickArmed] = useState(false);
9410
9642
  const moveCursor = useCallback((delta) => {
9643
+ setClickArmed(true);
9411
9644
  setCursor((c) => {
9412
9645
  if (items.length === 0) return c;
9413
9646
  return ((c + delta) % items.length + items.length) % items.length;
@@ -9438,6 +9671,16 @@ function OptionList({ items, initialCursor, onPick }) {
9438
9671
  const isCursor = i === cursor;
9439
9672
  return /* @__PURE__ */ jsxs("text", {
9440
9673
  wrapMode: "word",
9674
+ onMouseDown: (event) => {
9675
+ event.stopPropagation();
9676
+ event.preventDefault();
9677
+ if (isCursor && clickArmed) {
9678
+ onPick(item.id);
9679
+ return;
9680
+ }
9681
+ setCursor(i);
9682
+ setClickArmed(true);
9683
+ },
9441
9684
  children: [
9442
9685
  /* @__PURE__ */ jsx$1("span", {
9443
9686
  fg: COLOR.mute,
@@ -9680,6 +9923,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
9680
9923
  const COLOR = useColors();
9681
9924
  const inputRef = useRef(null);
9682
9925
  const [hint, setHint] = useState(null);
9926
+ const status = hint && hint.tone !== "error" ? hint.text : "complete the login in your browser — waiting for the callback…";
9683
9927
  const onSubmit = useCallback(() => {
9684
9928
  const value = inputRef.current?.value?.trim() ?? "";
9685
9929
  if (!value) return;
@@ -9709,32 +9953,36 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
9709
9953
  return /* @__PURE__ */ jsxs("box", {
9710
9954
  style: {
9711
9955
  flexDirection: "column",
9712
- border: ["top"],
9713
- borderColor: COLOR.border,
9714
- paddingTop: 1
9956
+ flexShrink: 0
9715
9957
  },
9716
- children: [
9717
- /* @__PURE__ */ jsx$1("text", {
9718
- fg: COLOR.brand,
9719
- children: `Authorizing ${serverName}`
9720
- }),
9721
- /* @__PURE__ */ jsx$1("text", {
9722
- fg: COLOR.dim,
9723
- children: "Your browser should have opened — complete the login, then return here."
9724
- }),
9725
- /* @__PURE__ */ jsx$1(OAuthAuthBlock, {
9726
- authUrl,
9727
- maxLineWidth,
9728
- chromeWidth,
9729
- paste: {
9730
- inputRef,
9731
- focused: inputFocused,
9732
- onSubmit,
9733
- placeholder: "paste redirect URL and press enter…",
9734
- hint: hint ?? void 0
9735
- }
9736
- })
9737
- ]
9958
+ children: [/* @__PURE__ */ jsxs("box", {
9959
+ style: {
9960
+ flexDirection: "column",
9961
+ border: true,
9962
+ borderColor: COLOR.border,
9963
+ padding: 1,
9964
+ gap: 1
9965
+ },
9966
+ children: [
9967
+ /* @__PURE__ */ jsx$1(Spinner, { label: status }),
9968
+ /* @__PURE__ */ jsx$1(OAuthAuthBlock, {
9969
+ authUrl,
9970
+ maxLineWidth,
9971
+ chromeWidth: (chromeWidth ?? 14) + 4,
9972
+ paste: {
9973
+ inputRef,
9974
+ focused: inputFocused,
9975
+ onSubmit,
9976
+ placeholder: "paste redirect URL and press enter…",
9977
+ hint: hint?.tone === "error" ? hint : void 0
9978
+ }
9979
+ }),
9980
+ /* @__PURE__ */ jsx$1("text", {
9981
+ fg: COLOR.dim,
9982
+ children: "esc cancel — the server stays signed out until you retry"
9983
+ })
9984
+ ]
9985
+ }), /* @__PURE__ */ jsx$1(TitleOverlay, { title: `authorizing ${serverName} — OAuth` })]
9738
9986
  });
9739
9987
  }
9740
9988
  //#endregion
@@ -10796,7 +11044,13 @@ function SessionsScreen({ sessions, currentId, focusedSessionId, onPick, onCreat
10796
11044
  focused: idx === cursorIndex && focused,
10797
11045
  isCurrent: row.kind === "session" && row.rowId === currentId,
10798
11046
  showProject: showAllProjects,
10799
- currentProjectRoot
11047
+ currentProjectRoot,
11048
+ onHover: focused ? () => moveCursor(idx) : void 0,
11049
+ onClick: focused ? () => {
11050
+ moveCursor(idx);
11051
+ if (row.kind === "new") onCreate();
11052
+ else onPick(row.rowId);
11053
+ } : void 0
10800
11054
  }, row.rowId)), filteredSessions.length === 0 && query.trim().length > 0 && /* @__PURE__ */ jsxs("text", {
10801
11055
  wrapMode: "none",
10802
11056
  children: [
@@ -10838,7 +11092,7 @@ function SessionsScreen({ sessions, currentId, focusedSessionId, onPick, onCreat
10838
11092
  * exactly the width of the focus + current markers above — so the first
10839
11093
  * stat (the turn count) sits flush under the title's first letter.
10840
11094
  */
10841
- function SessionRow({ row, anchorId, focused, isCurrent, separator = false, showProject = false, currentProjectRoot }) {
11095
+ function SessionRow({ row, anchorId, focused, isCurrent, separator = false, showProject = false, currentProjectRoot, onClick, onHover }) {
10842
11096
  const COLOR = useColors();
10843
11097
  const SURFACE = useSurfaces();
10844
11098
  const STATS_INDENT = " ";
@@ -10854,9 +11108,18 @@ function SessionRow({ row, anchorId, focused, isCurrent, separator = false, show
10854
11108
  borderColor: COLOR.border
10855
11109
  } : {}
10856
11110
  };
11111
+ const mouseProps = {
11112
+ ...onClick ? { onMouseDown: (event) => {
11113
+ event.stopPropagation();
11114
+ event.preventDefault();
11115
+ onClick();
11116
+ } } : {},
11117
+ ...onHover ? { onMouseOver: () => onHover() } : {}
11118
+ };
10857
11119
  if (row.kind === "new") return /* @__PURE__ */ jsxs("box", {
10858
11120
  id: anchorId,
10859
11121
  style: rowStyle,
11122
+ ...mouseProps,
10860
11123
  children: [/* @__PURE__ */ jsxs("text", {
10861
11124
  wrapMode: "none",
10862
11125
  children: [
@@ -10890,6 +11153,7 @@ function SessionRow({ row, anchorId, focused, isCurrent, separator = false, show
10890
11153
  return /* @__PURE__ */ jsxs("box", {
10891
11154
  id: anchorId,
10892
11155
  style: rowStyle,
11156
+ ...mouseProps,
10893
11157
  children: [
10894
11158
  /* @__PURE__ */ jsxs("text", {
10895
11159
  wrapMode: "none",
@@ -12302,7 +12566,6 @@ const PROMPT_HINTS_SELECT_QUEUE = [{
12302
12566
  * shortcuts that actually do something in the active mode.
12303
12567
  */
12304
12568
  function PromptHints({ selectMode, searchMode = false, triggerHints, busy = false }) {
12305
- const COLOR = useColors();
12306
12569
  const { settings } = useSettings();
12307
12570
  const { width: termWidth } = useTerminalDimensions();
12308
12571
  const minimalResting = !selectMode && !searchMode && !busy && settings.uiMode === "minimal";
@@ -12319,21 +12582,21 @@ function PromptHints({ selectMode, searchMode = false, triggerHints, busy = fals
12319
12582
  termWidth
12320
12583
  ]);
12321
12584
  if (hints.length === 0) return null;
12322
- return /* @__PURE__ */ jsxs("text", {
12585
+ return /* @__PURE__ */ jsxs("box", {
12323
12586
  style: {
12324
12587
  position: "absolute",
12325
12588
  top: 0,
12326
- right: 1
12589
+ right: 1,
12590
+ flexDirection: "row"
12327
12591
  },
12328
- fg: COLOR.dim,
12329
12592
  children: [
12330
- /* @__PURE__ */ jsx$1("span", {
12331
- fg: COLOR.mute,
12593
+ /* @__PURE__ */ jsx$1("text", {
12594
+ selectable: false,
12332
12595
  children: " "
12333
12596
  }),
12334
- renderHintSpans(hints, COLOR),
12335
- /* @__PURE__ */ jsx$1("span", {
12336
- fg: COLOR.mute,
12597
+ /* @__PURE__ */ jsx$1(HintRow, { hints }),
12598
+ /* @__PURE__ */ jsx$1("text", {
12599
+ selectable: false,
12337
12600
  children: " "
12338
12601
  })
12339
12602
  ]
@@ -14434,8 +14697,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14434
14697
  [activeTab]: 0
14435
14698
  }));
14436
14699
  }, [activeTab]);
14437
- const expandFocusedMcpRow = useCallback(() => {
14438
- const row = mcpVisibleRows[cursor];
14700
+ const expandMcpRowAt = useCallback((index) => {
14701
+ const row = mcpVisibleRows[index];
14439
14702
  if (!row || row.kind !== "server") return;
14440
14703
  const name = row.entry.config.name;
14441
14704
  if ((mcpToolsByServer[name]?.length ?? 0) === 0) fireMcpToolsRefresh();
@@ -14447,12 +14710,11 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14447
14710
  });
14448
14711
  }, [
14449
14712
  mcpVisibleRows,
14450
- cursor,
14451
14713
  mcpToolsByServer,
14452
14714
  fireMcpToolsRefresh
14453
14715
  ]);
14454
- const collapseFocusedMcpRow = useCallback(() => {
14455
- const row = mcpVisibleRows[cursor];
14716
+ const collapseMcpRowAt = useCallback((index) => {
14717
+ const row = mcpVisibleRows[index];
14456
14718
  if (!row) return;
14457
14719
  const parentName = parentServerName(row);
14458
14720
  if (row.kind !== "server") {
@@ -14468,7 +14730,18 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14468
14730
  next.delete(parentName);
14469
14731
  return next;
14470
14732
  });
14471
- }, [mcpVisibleRows, cursor]);
14733
+ }, [mcpVisibleRows]);
14734
+ const toggleExpandMcpRowAt = useCallback((index) => {
14735
+ const row = mcpVisibleRows[index];
14736
+ if (!row) return;
14737
+ if (expandedMcps.has(parentServerName(row))) collapseMcpRowAt(index);
14738
+ else expandMcpRowAt(index);
14739
+ }, [
14740
+ mcpVisibleRows,
14741
+ expandedMcps,
14742
+ collapseMcpRowAt,
14743
+ expandMcpRowAt
14744
+ ]);
14472
14745
  /**
14473
14746
  * Single `tab` press toggles between expanded / collapsed based on
14474
14747
  * the focused row's current state. The split helpers above remain
@@ -14476,21 +14749,9 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14476
14749
  * users expect — expand-only / collapse-only). Toggle is the
14477
14750
  * default that the footer hint advertises.
14478
14751
  */
14479
- const toggleExpandFocusedMcpRow = useCallback(() => {
14480
- const row = mcpVisibleRows[cursor];
14481
- if (!row) return;
14482
- const parentName = parentServerName(row);
14483
- if (expandedMcps.has(parentName)) collapseFocusedMcpRow();
14484
- else expandFocusedMcpRow();
14485
- }, [
14486
- mcpVisibleRows,
14487
- cursor,
14488
- expandedMcps,
14489
- collapseFocusedMcpRow,
14490
- expandFocusedMcpRow
14491
- ]);
14492
- const toggleExpandFocusedExtensionRow = useCallback(() => {
14493
- const row = extensionVisibleRows[cursor];
14752
+ const toggleExpandFocusedMcpRow = useCallback(() => toggleExpandMcpRowAt(cursor), [toggleExpandMcpRowAt, cursor]);
14753
+ const toggleExpandExtensionRowAt = useCallback((index) => {
14754
+ const row = extensionVisibleRows[index];
14494
14755
  const parentName = parentExtensionName(row);
14495
14756
  if (!parentName) return;
14496
14757
  setExpandedExtensions((prev) => {
@@ -14507,7 +14768,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14507
14768
  } else next.add(parentName);
14508
14769
  return next;
14509
14770
  });
14510
- }, [extensionVisibleRows, cursor]);
14771
+ }, [extensionVisibleRows]);
14772
+ const toggleExpandFocusedExtensionRow = useCallback(() => toggleExpandExtensionRowAt(cursor), [toggleExpandExtensionRowAt, cursor]);
14511
14773
  const mcpRowCount = mcpVisibleRows.length;
14512
14774
  const extensionRowCount = extensionVisibleRows.length;
14513
14775
  useEffect(() => {
@@ -14597,6 +14859,148 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14597
14859
  useEffect(() => {
14598
14860
  if (editingInputKey) editInputRef.current?.focus();
14599
14861
  }, [editingInputKey]);
14862
+ /**
14863
+ * Activate the row at `index` on the ACTIVE tab — the ↵ semantics
14864
+ * (toggle / cycle / open editor / run action), extracted so row
14865
+ * CLICKS can activate the clicked row directly. Cursor-based
14866
+ * callers (the keyboard handler) pass `cursor`.
14867
+ */
14868
+ const activateRowAt = useCallback((index) => {
14869
+ if (isCategoryTab(activeTab)) {
14870
+ const it = filteredByCategory[activeTab][index];
14871
+ if (!it) return;
14872
+ if (it.kind === "toggle") toggleBoolean(it.key);
14873
+ else if (it.kind === "choice") {
14874
+ const current = settings[it.key];
14875
+ const idx = it.options.findIndex((o) => o.value === current);
14876
+ const next = it.options[(idx + 1) % it.options.length];
14877
+ if (next) setSetting(it.key, next.value);
14878
+ } else if (it.kind === "input") setEditingInputKey(it.key);
14879
+ else it.onPick();
14880
+ return;
14881
+ }
14882
+ if (activeTab === "skills") {
14883
+ const s = filteredSkills[index];
14884
+ if (s) skillsToggle.toggle(s.name);
14885
+ return;
14886
+ }
14887
+ if (activeTab === "extensions") {
14888
+ const row = extensionVisibleRows[index];
14889
+ if (!row) return;
14890
+ if (row.kind === "extension") extensionsToggle.toggle(row.ext.name);
14891
+ else if (row.kind === "config") activateConfigRow(row.extName, row.item);
14892
+ return;
14893
+ }
14894
+ if (activeTab === "mcps") {
14895
+ const row = mcpVisibleRows[index];
14896
+ if (!row) return;
14897
+ if (row.kind === "server") {
14898
+ const name = row.entry.config.name;
14899
+ const enabling = !mcpsToggle.enabledSet.has(name);
14900
+ mcpsToggle.toggle(name);
14901
+ if (enabling && (mcpToolsByServer[name]?.length ?? 0) === 0) fireMcpToolsRefresh();
14902
+ } else if (row.kind === "tool") mcpToolToggleMap[row.serverName]?.toggle(row.tool.name);
14903
+ return;
14904
+ }
14905
+ if (activeTab === "keybindings") {
14906
+ actions?.onOpenKeybindings?.();
14907
+ return;
14908
+ }
14909
+ if (activeTab === "authentication") {
14910
+ const provider = filteredProviders[index];
14911
+ if (provider) actions?.onConfigureProvider?.(provider);
14912
+ }
14913
+ }, [
14914
+ activeTab,
14915
+ filteredByCategory,
14916
+ settings,
14917
+ setSetting,
14918
+ toggleBoolean,
14919
+ filteredSkills,
14920
+ skillsToggle,
14921
+ extensionVisibleRows,
14922
+ extensionsToggle,
14923
+ activateConfigRow,
14924
+ mcpVisibleRows,
14925
+ mcpsToggle,
14926
+ mcpToolsByServer,
14927
+ fireMcpToolsRefresh,
14928
+ mcpToolToggleMap,
14929
+ actions,
14930
+ filteredProviders
14931
+ ]);
14932
+ /**
14933
+ * True while an inline editor or overlay dialog owns the modal's input
14934
+ * — the same states the keyboard handler early-returns on. Mouse paths
14935
+ * (`clickRowAt`, tab-strip clicks) must honor it too: a click on a row
14936
+ * or tab underneath an open dialog would otherwise toggle settings or
14937
+ * switch tabs behind the user's back. Also drives the lists' `focused`
14938
+ * prop, so "list looks focused" and "list accepts input" cannot drift.
14939
+ */
14940
+ const modalInputCaptured = oauthPasteActive || configEditActive || editingInputKey !== null || extInstallActive || skillInstallActive || skillImportAllActive || mcpAddActive;
14941
+ /**
14942
+ * Row CLICK dispatch: focus the clicked row, then act on it. Expandable
14943
+ * headers (MCP servers, extensions with config) expand/collapse — the
14944
+ * disclosure is the row's primary affordance; everything else gets the
14945
+ * ↵ semantics (toggle / cycle / edit / action). Rows stay single
14946
+ * renderables (the MCP list documents an OpenTUI layout regression that
14947
+ * forbids box wrappers there), so the whole row is one click target —
14948
+ * the enable checkbox on expandable headers remains ↵.
14949
+ */
14950
+ /**
14951
+ * Row CLICK dispatch, by ZONE:
14952
+ *
14953
+ * - `checkbox` — the click landed on the row's `[✓] ` cell (rows
14954
+ * compute this by glyph column — see `inCheckboxZone`): flip the
14955
+ * toggle (↵ semantics). The checkbox is a SOLO target, including
14956
+ * the enable checkbox on MCP server / extension header rows.
14957
+ * - `row` — anywhere else: expandable headers expand/collapse;
14958
+ * checkbox-bearing rows only FOCUS (a body click must never flip
14959
+ * state the user didn't aim at); rows whose whole body is one
14960
+ * action (choices, inputs, actions, auth) activate.
14961
+ */
14962
+ const clickRowAt = useCallback((index, zone = "row") => {
14963
+ if (modalInputCaptured) return;
14964
+ setCursorByTab((prev) => ({
14965
+ ...prev,
14966
+ [activeTab]: index
14967
+ }));
14968
+ if (zone === "checkbox") {
14969
+ activateRowAt(index);
14970
+ return;
14971
+ }
14972
+ if (activeTab === "mcps") {
14973
+ if (mcpVisibleRows[index]?.kind === "server") toggleExpandMcpRowAt(index);
14974
+ return;
14975
+ }
14976
+ if (activeTab === "extensions") {
14977
+ const row = extensionVisibleRows[index];
14978
+ if (row?.kind === "extension") {
14979
+ if (row.hasConfig) toggleExpandExtensionRowAt(index);
14980
+ return;
14981
+ }
14982
+ if (row?.kind === "config" && row.item.type === "boolean") return;
14983
+ activateRowAt(index);
14984
+ return;
14985
+ }
14986
+ if (activeTab === "skills") return;
14987
+ if (isCategoryTab(activeTab) && filteredByCategory[activeTab][index]?.kind === "toggle") return;
14988
+ activateRowAt(index);
14989
+ }, [
14990
+ activeTab,
14991
+ modalInputCaptured,
14992
+ mcpVisibleRows,
14993
+ extensionVisibleRows,
14994
+ filteredByCategory,
14995
+ toggleExpandMcpRowAt,
14996
+ toggleExpandExtensionRowAt,
14997
+ activateRowAt
14998
+ ]);
14999
+ /** Tab-strip clicks — same input-capture guard as row clicks. */
15000
+ const clickTab = useCallback((id) => {
15001
+ if (modalInputCaptured) return;
15002
+ setActiveTab(id);
15003
+ }, [modalInputCaptured]);
14600
15004
  useKeyboard((key) => {
14601
15005
  if (configEditActive) {
14602
15006
  if (key.name === "escape" && configEdit?.kind !== "model") setConfigEdit(null);
@@ -14659,51 +15063,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14659
15063
  return;
14660
15064
  }
14661
15065
  if (key.name === "return") {
14662
- if (isCategoryTab(activeTab)) {
14663
- const it = filteredByCategory[activeTab][cursor];
14664
- if (!it) return;
14665
- if (it.kind === "toggle") toggleBoolean(it.key);
14666
- else if (it.kind === "choice") {
14667
- const current = settings[it.key];
14668
- const idx = it.options.findIndex((o) => o.value === current);
14669
- const next = it.options[(idx + 1) % it.options.length];
14670
- if (next) setSetting(it.key, next.value);
14671
- } else if (it.kind === "input") setEditingInputKey(it.key);
14672
- else it.onPick();
14673
- return;
14674
- }
14675
- if (activeTab === "skills") {
14676
- const s = filteredSkills[cursor];
14677
- if (s) skillsToggle.toggle(s.name);
14678
- return;
14679
- }
14680
- if (activeTab === "extensions") {
14681
- const row = extensionVisibleRows[cursor];
14682
- if (!row) return;
14683
- if (row.kind === "extension") extensionsToggle.toggle(row.ext.name);
14684
- else if (row.kind === "config") activateConfigRow(row.extName, row.item);
14685
- return;
14686
- }
14687
- if (activeTab === "mcps") {
14688
- const row = mcpVisibleRows[cursor];
14689
- if (!row) return;
14690
- if (row.kind === "server") {
14691
- const name = row.entry.config.name;
14692
- const enabling = !mcpsToggle.enabledSet.has(name);
14693
- mcpsToggle.toggle(name);
14694
- if (enabling && (mcpToolsByServer[name]?.length ?? 0) === 0) fireMcpToolsRefresh();
14695
- } else if (row.kind === "tool") mcpToolToggleMap[row.serverName]?.toggle(row.tool.name);
14696
- return;
14697
- }
14698
- if (activeTab === "keybindings") {
14699
- actions?.onOpenKeybindings?.();
14700
- return;
14701
- }
14702
- if (activeTab === "authentication") {
14703
- const provider = filteredProviders[cursor];
14704
- if (provider) actions?.onConfigureProvider?.(provider);
14705
- return;
14706
- }
15066
+ activateRowAt(cursor);
14707
15067
  return;
14708
15068
  }
14709
15069
  if (activeTab === "extensions") {
@@ -14908,7 +15268,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14908
15268
  children: /* @__PURE__ */ jsx$1(TabStrip, {
14909
15269
  order: tabOrder,
14910
15270
  active: activeTab,
14911
- counts: tabCounts
15271
+ counts: tabCounts,
15272
+ onSelect: clickTab
14912
15273
  })
14913
15274
  }),
14914
15275
  /* @__PURE__ */ jsx$1("box", {
@@ -14922,7 +15283,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14922
15283
  },
14923
15284
  children: /* @__PURE__ */ jsx$1("input", {
14924
15285
  ref: inputRef,
14925
- focused: !oauthPasteActive && !configEditActive && !editingInputKey && !extInstallActive && !skillInstallActive && !skillImportAllActive && !mcpAddActive,
15286
+ focused: !modalInputCaptured,
14926
15287
  placeholder: searchPlaceholder(activeTab),
14927
15288
  onInput: handleQueryChange,
14928
15289
  onSubmit: () => {},
@@ -14941,6 +15302,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14941
15302
  viewportCulling: false,
14942
15303
  children: [
14943
15304
  isCategoryTab(activeTab) && /* @__PURE__ */ jsx$1(GeneralList, {
15305
+ onRowClick: clickRowAt,
14944
15306
  items: filteredByCategory[activeTab],
14945
15307
  cursor,
14946
15308
  highlightBg: SURFACE.selection,
@@ -14949,6 +15311,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14949
15311
  editingKey: editingInputKey
14950
15312
  }),
14951
15313
  activeTab === "skills" && /* @__PURE__ */ jsx$1(SkillsList, {
15314
+ onRowClick: clickRowAt,
14952
15315
  items: filteredSkills,
14953
15316
  enabledSet: skillsToggle.enabledSet,
14954
15317
  totalCount: skillsCatalog.length,
@@ -14958,6 +15321,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14958
15321
  contentCols
14959
15322
  }),
14960
15323
  activeTab === "extensions" && /* @__PURE__ */ jsx$1(ExtensionsList, {
15324
+ onRowClick: clickRowAt,
14961
15325
  rows: extensionVisibleRows,
14962
15326
  enabledSet: extensionsToggle.enabledSet,
14963
15327
  extensionConfig: settings.extensionConfig,
@@ -14977,6 +15341,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
14977
15341
  actionProbes
14978
15342
  }),
14979
15343
  activeTab === "mcps" && /* @__PURE__ */ jsx$1(McpsList, {
15344
+ onRowClick: clickRowAt,
14980
15345
  rows: mcpVisibleRows,
14981
15346
  filteredCount: filteredMcps.length,
14982
15347
  enabledSet: mcpsToggle.enabledSet,
@@ -15183,9 +15548,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
15183
15548
  ]
15184
15549
  });
15185
15550
  }
15186
- function TabStrip({ order, active, counts }) {
15551
+ function TabStrip({ order, active, counts, onSelect }) {
15187
15552
  const COLOR = useColors();
15188
15553
  const SURFACE = useSurfaces();
15554
+ const [hoveredTab, setHoveredTab] = useState(null);
15189
15555
  return /* @__PURE__ */ jsx$1("box", {
15190
15556
  style: {
15191
15557
  flexDirection: "row",
@@ -15193,11 +15559,19 @@ function TabStrip({ order, active, counts }) {
15193
15559
  },
15194
15560
  children: order.map((id, i) => {
15195
15561
  const isActive = id === active;
15562
+ const hovered = hoveredTab === id && !isActive;
15196
15563
  const label = TAB_LABELS[id];
15197
15564
  const tabCounts = counts[id];
15198
15565
  const showBadge = !!tabCounts;
15199
15566
  const badge = tabCounts ? tabCounts.current !== tabCounts.total ? `${tabCounts.current}/${tabCounts.total}` : `${tabCounts.total}` : "";
15200
15567
  return /* @__PURE__ */ jsx$1("box", {
15568
+ onMouseDown: (event) => {
15569
+ event.stopPropagation();
15570
+ event.preventDefault();
15571
+ onSelect(id);
15572
+ },
15573
+ onMouseOver: () => setHoveredTab(id),
15574
+ onMouseOut: () => setHoveredTab((current) => current === id ? null : current),
15201
15575
  style: {
15202
15576
  paddingLeft: 1,
15203
15577
  paddingRight: 1,
@@ -15207,10 +15581,10 @@ function TabStrip({ order, active, counts }) {
15207
15581
  children: /* @__PURE__ */ jsxs("text", {
15208
15582
  wrapMode: "none",
15209
15583
  children: [/* @__PURE__ */ jsx$1("span", {
15210
- fg: isActive ? COLOR.brand : COLOR.dim,
15584
+ fg: isActive ? COLOR.brand : hovered ? COLOR.accent : COLOR.dim,
15211
15585
  children: label
15212
15586
  }), showBadge && /* @__PURE__ */ jsx$1("span", {
15213
- fg: isActive ? COLOR.brand : COLOR.mute,
15587
+ fg: isActive ? COLOR.brand : hovered ? COLOR.accent : COLOR.mute,
15214
15588
  children: ` ${badge}`
15215
15589
  })]
15216
15590
  })
@@ -15218,7 +15592,7 @@ function TabStrip({ order, active, counts }) {
15218
15592
  })
15219
15593
  });
15220
15594
  }
15221
- function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKey }) {
15595
+ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKey, onRowClick }) {
15222
15596
  const { settings } = useSettings();
15223
15597
  if (items.length === 0) return /* @__PURE__ */ jsx$1(EmptyRow, { label: query ? `no settings match "${query}"` : "no settings" });
15224
15598
  const descCols = Math.max(0, contentCols - 1 - 1 - 6);
@@ -15229,6 +15603,7 @@ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKe
15229
15603
  const bg = focused ? highlightBg : void 0;
15230
15604
  const id = anchorIdFor(i);
15231
15605
  if (item.kind === "toggle") return /* @__PURE__ */ jsx$1(ToggleRow, {
15606
+ onClick: (zone) => onRowClick?.(i, zone),
15232
15607
  id,
15233
15608
  label: item.label,
15234
15609
  description: truncateToCols(item.description, descCols),
@@ -15240,6 +15615,7 @@ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKe
15240
15615
  const current = settings[item.key];
15241
15616
  const opt = item.options.find((o) => o.value === current);
15242
15617
  return /* @__PURE__ */ jsx$1(ChoiceRow, {
15618
+ onClick: () => onRowClick?.(i),
15243
15619
  id,
15244
15620
  label: item.label,
15245
15621
  description: truncateToCols(item.description, descCols),
@@ -15250,6 +15626,7 @@ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKe
15250
15626
  }, item.key);
15251
15627
  }
15252
15628
  if (item.kind === "input") return /* @__PURE__ */ jsx$1(InputRow, {
15629
+ onClick: () => onRowClick?.(i),
15253
15630
  id,
15254
15631
  label: item.label,
15255
15632
  description: truncateToCols(item.description, descCols),
@@ -15270,7 +15647,7 @@ function GeneralList({ items, cursor, highlightBg, query, contentCols, editingKe
15270
15647
  });
15271
15648
  }
15272
15649
  const EXTENSION_CONFIG_INDENT = 3;
15273
- function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCount, cursor, highlightBg, query, contentCols, editing, inputRef, onCommitEdit, modelCatalog, onApplyModelEdit, onCancelEdit, actionStatus, actionProbes }) {
15650
+ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCount, cursor, highlightBg, query, contentCols, editing, inputRef, onCommitEdit, modelCatalog, onApplyModelEdit, onCancelEdit, actionStatus, actionProbes, onRowClick }) {
15274
15651
  const COLOR = useColors();
15275
15652
  const home = homedir();
15276
15653
  if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
@@ -15420,6 +15797,7 @@ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCo
15420
15797
  if (item.type === "action") {
15421
15798
  const status = actionStatus && actionStatus.extName === group.ext.name && actionStatus.key === item.key ? actionStatus : null;
15422
15799
  childEls.push(/* @__PURE__ */ jsx$1(ConfigActionRow, {
15800
+ onClick: () => onRowClick?.(index),
15423
15801
  id: anchorIdFor(index),
15424
15802
  indent: EXTENSION_CONFIG_INDENT,
15425
15803
  label: item.label,
@@ -15430,6 +15808,7 @@ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCo
15430
15808
  bg: childBg
15431
15809
  }, item.key));
15432
15810
  } else if (item.type === "boolean") childEls.push(/* @__PURE__ */ jsx$1(ToggleRow, {
15811
+ onClick: (zone) => onRowClick?.(index, zone),
15433
15812
  id: anchorIdFor(index),
15434
15813
  indent: EXTENSION_CONFIG_INDENT,
15435
15814
  label: item.label,
@@ -15441,6 +15820,7 @@ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCo
15441
15820
  else if (item.type === "enum") {
15442
15821
  const opt = item.options.find((o) => o.value === value);
15443
15822
  childEls.push(/* @__PURE__ */ jsx$1(ChoiceRow, {
15823
+ onClick: () => onRowClick?.(index),
15444
15824
  id: anchorIdFor(index),
15445
15825
  indent: EXTENSION_CONFIG_INDENT,
15446
15826
  label: item.label,
@@ -15489,6 +15869,11 @@ function ExtensionsList({ rows, enabledSet, extensionConfig, categories, totalCo
15489
15869
  id: anchorIdFor(group.index),
15490
15870
  wrapMode: "none",
15491
15871
  bg,
15872
+ onMouseDown: onRowClick && ((event) => {
15873
+ event.stopPropagation();
15874
+ event.preventDefault();
15875
+ onRowClick(group.index, inCheckboxZone(event, 4) ? "checkbox" : "row");
15876
+ }),
15492
15877
  children: [
15493
15878
  /* @__PURE__ */ jsx$1("span", {
15494
15879
  fg: focused ? COLOR.brand : COLOR.mute,
@@ -15568,11 +15953,16 @@ function CreateExtensionButton() {
15568
15953
  })]
15569
15954
  });
15570
15955
  }
15571
- function ConfigActionRow({ id, label, description, status, probe, focused, bg, indent = 0 }) {
15956
+ function ConfigActionRow({ id, label, description, status, probe, focused, bg, indent = 0, onClick }) {
15572
15957
  const COLOR = useColors();
15573
15958
  const statusText = (status?.state === "busy" ? "running…" : status?.text) ?? probe?.message;
15574
15959
  const statusColor = status ? status.state === "error" ? COLOR.error : status.state === "busy" ? COLOR.mute : COLOR.accent : probe ? probe.state === "error" ? COLOR.error : probe.state === "off" ? COLOR.mute : COLOR.accent : COLOR.mute;
15575
15960
  return /* @__PURE__ */ jsxs("box", {
15961
+ onMouseDown: onClick && ((event) => {
15962
+ event.stopPropagation();
15963
+ event.preventDefault();
15964
+ onClick();
15965
+ }),
15576
15966
  id,
15577
15967
  style: {
15578
15968
  flexDirection: "column",
@@ -15745,7 +16135,7 @@ function ConfigModelPickerRow({ id, label, description, catalog, multi, initial,
15745
16135
  ]
15746
16136
  });
15747
16137
  }
15748
- function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query, contentCols }) {
16138
+ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query, contentCols, onRowClick }) {
15749
16139
  const COLOR = useColors();
15750
16140
  if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
15751
16141
  style: { flexDirection: "column" },
@@ -15790,6 +16180,11 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
15790
16180
  const description = truncateToCols(entry.description ?? "", descCols);
15791
16181
  const origin = skillOriginLabel(entry);
15792
16182
  return /* @__PURE__ */ jsxs("box", {
16183
+ onMouseDown: onRowClick && ((event) => {
16184
+ event.stopPropagation();
16185
+ event.preventDefault();
16186
+ onRowClick(i, "row");
16187
+ }),
15793
16188
  id: anchorIdFor(i),
15794
16189
  style: {
15795
16190
  flexDirection: "column",
@@ -15800,6 +16195,11 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
15800
16195
  },
15801
16196
  children: [/* @__PURE__ */ jsxs("text", {
15802
16197
  wrapMode: "none",
16198
+ onMouseDown: onRowClick && ((event) => {
16199
+ event.stopPropagation();
16200
+ event.preventDefault();
16201
+ onRowClick(i, inCheckboxZone(event, 2) ? "checkbox" : "row");
16202
+ }),
15803
16203
  children: [
15804
16204
  /* @__PURE__ */ jsx$1("span", {
15805
16205
  fg: focused ? COLOR.brand : COLOR.mute,
@@ -15827,7 +16227,7 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query,
15827
16227
  })
15828
16228
  });
15829
16229
  }
15830
- function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServer, toolsRefreshing, expanded, totalCount, cursor, highlightBg, query, errors, authState, contentCols }) {
16230
+ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServer, toolsRefreshing, expanded, totalCount, cursor, highlightBg, query, errors, authState, contentCols, onRowClick }) {
15831
16231
  const COLOR = useColors();
15832
16232
  const home = homedir();
15833
16233
  if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
@@ -15897,6 +16297,7 @@ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServe
15897
16297
  children: [renderMcpErrors(errors, home, COLOR.warn, COLOR.dim), groups.map((group) => /* @__PURE__ */ jsxs("box", {
15898
16298
  style: { flexDirection: "column" },
15899
16299
  children: [renderServerListRow({
16300
+ onClick: onRowClick ? (zone) => onRowClick(group.serverIndex, zone) : void 0,
15900
16301
  entry: group.server.entry,
15901
16302
  focused: group.serverIndex === cursor,
15902
16303
  bg: group.serverIndex === cursor ? highlightBg : void 0,
@@ -15912,6 +16313,7 @@ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServe
15912
16313
  const focused = index === cursor;
15913
16314
  const bg = focused ? highlightBg : void 0;
15914
16315
  if (row.kind === "tool") return renderToolListRow({
16316
+ onClick: onRowClick ? (zone) => onRowClick(index, zone) : void 0,
15915
16317
  serverName: row.serverName,
15916
16318
  tool: row.tool,
15917
16319
  focused,
@@ -15933,7 +16335,7 @@ function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServe
15933
16335
  }, `group:${group.server.entry.config.name}`))]
15934
16336
  });
15935
16337
  }
15936
- function renderServerListRow({ entry, focused, bg, enabled, expanded, toolEnabledSet, toolTotal, anchorId, status, COLOR, contentCols }) {
16338
+ function renderServerListRow({ onClick, entry, focused, bg, enabled, expanded, toolEnabledSet, toolTotal, anchorId, status, COLOR, contentCols }) {
15937
16339
  const name = entry.config.name;
15938
16340
  const chevron = expanded ? "▾" : "▸";
15939
16341
  const toolCountChip = toolEnabledSet && typeof toolTotal === "number" ? ` (${toolEnabledSet.size}/${toolTotal})` : "";
@@ -15944,6 +16346,11 @@ function renderServerListRow({ entry, focused, bg, enabled, expanded, toolEnable
15944
16346
  id: anchorId,
15945
16347
  wrapMode: "none",
15946
16348
  bg,
16349
+ onMouseDown: onClick && ((event) => {
16350
+ event.stopPropagation();
16351
+ event.preventDefault();
16352
+ onClick(inCheckboxZone(event, 4) ? "checkbox" : "row");
16353
+ }),
15947
16354
  children: [
15948
16355
  /* @__PURE__ */ jsx$1("span", {
15949
16356
  fg: focused ? COLOR.brand : COLOR.mute,
@@ -15975,13 +16382,18 @@ function renderServerListRow({ entry, focused, bg, enabled, expanded, toolEnable
15975
16382
  }
15976
16383
  const TOOL_ROW_INDENT$1 = " ";
15977
16384
  const TOOL_ROW_FOCUS_INDENT$1 = " ▶ ";
15978
- function renderToolListRow({ serverName, tool, focused, bg, enabled, anchorId, COLOR, contentCols }) {
16385
+ function renderToolListRow({ onClick, serverName, tool, focused, bg, enabled, anchorId, COLOR, contentCols }) {
15979
16386
  const descBudget = Math.max(0, contentCols - 6 - 4 - tool.name.length - 2);
15980
16387
  const description = truncateToCols(tool.description ?? "", descBudget);
15981
16388
  return /* @__PURE__ */ jsxs("text", {
15982
16389
  id: anchorId,
15983
16390
  wrapMode: "none",
15984
16391
  bg,
16392
+ onMouseDown: onClick && ((event) => {
16393
+ event.stopPropagation();
16394
+ event.preventDefault();
16395
+ onClick(inCheckboxZone(event, 6) ? "checkbox" : "row");
16396
+ }),
15985
16397
  children: [
15986
16398
  /* @__PURE__ */ jsx$1("span", {
15987
16399
  fg: focused ? COLOR.brand : COLOR.mute,
@@ -16204,9 +16616,14 @@ function AuthenticationList({ view, providers, cursor, highlightBg, query }) {
16204
16616
  ]
16205
16617
  });
16206
16618
  }
16207
- function ToggleRow({ id, label, description, enabled, focused, bg, indent = 0 }) {
16619
+ function ToggleRow({ id, label, description, enabled, focused, bg, indent = 0, onClick }) {
16208
16620
  const COLOR = useColors();
16209
16621
  return /* @__PURE__ */ jsxs("box", {
16622
+ onMouseDown: onClick && ((event) => {
16623
+ event.stopPropagation();
16624
+ event.preventDefault();
16625
+ onClick("row");
16626
+ }),
16210
16627
  id,
16211
16628
  style: {
16212
16629
  flexDirection: "column",
@@ -16217,6 +16634,11 @@ function ToggleRow({ id, label, description, enabled, focused, bg, indent = 0 })
16217
16634
  },
16218
16635
  children: [/* @__PURE__ */ jsxs("text", {
16219
16636
  wrapMode: "none",
16637
+ onMouseDown: onClick && ((event) => {
16638
+ event.stopPropagation();
16639
+ event.preventDefault();
16640
+ onClick(inCheckboxZone(event, 2) ? "checkbox" : "row");
16641
+ }),
16220
16642
  children: [
16221
16643
  /* @__PURE__ */ jsx$1("span", {
16222
16644
  fg: focused ? COLOR.brand : COLOR.mute,
@@ -16238,9 +16660,14 @@ function ToggleRow({ id, label, description, enabled, focused, bg, indent = 0 })
16238
16660
  })]
16239
16661
  });
16240
16662
  }
16241
- function ChoiceRow({ id, label, description, value, cyclable, focused, bg, indent = 0 }) {
16663
+ function ChoiceRow({ id, label, description, value, cyclable, focused, bg, indent = 0, onClick }) {
16242
16664
  const COLOR = useColors();
16243
16665
  return /* @__PURE__ */ jsxs("box", {
16666
+ onMouseDown: onClick && ((event) => {
16667
+ event.stopPropagation();
16668
+ event.preventDefault();
16669
+ onClick();
16670
+ }),
16244
16671
  id,
16245
16672
  style: {
16246
16673
  flexDirection: "column",
@@ -16284,9 +16711,14 @@ function ChoiceRow({ id, label, description, value, cyclable, focused, bg, inden
16284
16711
  })]
16285
16712
  });
16286
16713
  }
16287
- function ActionRow$1({ id, label, description, focused, bg }) {
16714
+ function ActionRow$1({ id, label, description, focused, bg, onClick }) {
16288
16715
  const COLOR = useColors();
16289
16716
  return /* @__PURE__ */ jsxs("box", {
16717
+ onMouseDown: onClick && ((event) => {
16718
+ event.stopPropagation();
16719
+ event.preventDefault();
16720
+ onClick();
16721
+ }),
16290
16722
  id,
16291
16723
  style: {
16292
16724
  flexDirection: "column",
@@ -16322,10 +16754,15 @@ function ActionRow$1({ id, label, description, focused, bg }) {
16322
16754
  })]
16323
16755
  });
16324
16756
  }
16325
- function InputRow({ id, label, description, value, placeholder, editing, focused, bg }) {
16757
+ function InputRow({ id, label, description, value, placeholder, editing, focused, bg, onClick }) {
16326
16758
  const COLOR = useColors();
16327
16759
  const hasValue = value.length > 0;
16328
16760
  return /* @__PURE__ */ jsxs("box", {
16761
+ onMouseDown: onClick && ((event) => {
16762
+ event.stopPropagation();
16763
+ event.preventDefault();
16764
+ onClick();
16765
+ }),
16329
16766
  id,
16330
16767
  style: {
16331
16768
  flexDirection: "column",
@@ -16508,155 +16945,89 @@ function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefr
16508
16945
  const showLogout = activeTab === "mcps" && onServerRow && !!focusedMcpStatus && canLogout$1(focusedMcpStatus);
16509
16946
  const showCancel = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing";
16510
16947
  const showRefresh = activeTab === "skills" && canRefreshSkills || activeTab === "mcps" && canRefreshMcps;
16511
- return /* @__PURE__ */ jsxs("text", {
16512
- fg: COLOR.mute,
16513
- children: [
16514
- /* @__PURE__ */ jsx$1("span", {
16515
- fg: COLOR.warn,
16516
- children: "←→"
16517
- }),
16518
- " tabs · ",
16519
- /* @__PURE__ */ jsx$1("span", {
16520
- fg: COLOR.warn,
16521
- children: "↑↓"
16522
- }),
16523
- activeTab === "keybindings" || activeTab === "logs" ? " scroll · " : " navigate · ",
16524
- activeTab === "logs" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx$1("span", {
16525
- fg: COLOR.warn,
16526
- children: "ctrl+l"
16527
- }), " level · type to filter"] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx$1("span", {
16528
- fg: COLOR.warn,
16529
- children: "↵"
16530
- }), isCategoryTab(activeTab) ? " toggle/cycle/select" : activeTab === "keybindings" ? " edit file" : activeTab === "authentication" ? " switch / re-authenticate" : " toggle"] }),
16531
- activeTab === "mcps" && focusedMcpRow && /* @__PURE__ */ jsxs("span", { children: [
16532
- " · ",
16533
- /* @__PURE__ */ jsx$1("span", {
16534
- fg: COLOR.warn,
16535
- children: "tab"
16536
- }),
16537
- focusedMcpRow.kind === "server" ? " expand/collapse" : " collapse"
16538
- ] }),
16539
- activeTab === "extensions" && focusedExtensionExpandable && /* @__PURE__ */ jsxs("span", { children: [
16540
- " · ",
16541
- /* @__PURE__ */ jsx$1("span", {
16542
- fg: COLOR.warn,
16543
- children: "tab"
16544
- }),
16545
- " settings"
16546
- ] }),
16547
- activeTab === "extensions" && canEditExtension && /* @__PURE__ */ jsxs("span", { children: [
16548
- " · ",
16549
- /* @__PURE__ */ jsx$1("span", {
16550
- fg: COLOR.warn,
16551
- children: "ctrl+e"
16552
- }),
16553
- " edit"
16554
- ] }),
16555
- activeTab === "extensions" && canCreateExtension && /* @__PURE__ */ jsxs("span", { children: [
16556
- " · ",
16557
- /* @__PURE__ */ jsx$1("span", {
16558
- fg: COLOR.warn,
16559
- children: "ctrl+a"
16560
- }),
16561
- " create"
16562
- ] }),
16563
- activeTab === "extensions" && canUpdateExtension && /* @__PURE__ */ jsxs("span", { children: [
16564
- " · ",
16565
- /* @__PURE__ */ jsx$1("span", {
16566
- fg: COLOR.warn,
16567
- children: "ctrl+u"
16568
- }),
16569
- " update"
16570
- ] }),
16571
- activeTab === "extensions" && canRemoveExtension && /* @__PURE__ */ jsxs("span", { children: [
16572
- " · ",
16573
- /* @__PURE__ */ jsx$1("span", {
16574
- fg: COLOR.warn,
16575
- children: "ctrl+x"
16576
- }),
16577
- " remove"
16578
- ] }),
16579
- activeTab === "extensions" && canInstallExtension && /* @__PURE__ */ jsxs("span", { children: [
16580
- " · ",
16581
- /* @__PURE__ */ jsx$1("span", {
16582
- fg: COLOR.warn,
16583
- children: "ctrl+g"
16584
- }),
16585
- " install"
16586
- ] }),
16587
- activeTab === "skills" && canInstallSkill && /* @__PURE__ */ jsxs("span", { children: [
16588
- " · ",
16589
- /* @__PURE__ */ jsx$1("span", {
16590
- fg: COLOR.warn,
16591
- children: "ctrl+g"
16592
- }),
16593
- " install"
16594
- ] }),
16595
- activeTab === "skills" && canRemoveSkill && /* @__PURE__ */ jsxs("span", { children: [
16596
- " · ",
16597
- /* @__PURE__ */ jsx$1("span", {
16598
- fg: COLOR.warn,
16599
- children: "ctrl+x"
16600
- }),
16601
- " delete"
16602
- ] }),
16603
- activeTab === "mcps" && canAddMcp && /* @__PURE__ */ jsxs("span", { children: [
16604
- " · ",
16605
- /* @__PURE__ */ jsx$1("span", {
16606
- fg: COLOR.warn,
16607
- children: "ctrl+g"
16608
- }),
16609
- " add server"
16610
- ] }),
16611
- activeTab === "mcps" && canRemoveMcp && onServerRow && /* @__PURE__ */ jsxs("span", { children: [
16612
- " · ",
16613
- /* @__PURE__ */ jsx$1("span", {
16614
- fg: COLOR.warn,
16615
- children: "ctrl+x"
16616
- }),
16617
- " remove"
16618
- ] }),
16619
- showLogin && /* @__PURE__ */ jsxs("span", { children: [
16620
- " · ",
16621
- /* @__PURE__ */ jsx$1("span", {
16622
- fg: COLOR.warn,
16623
- children: "ctrl+L"
16624
- }),
16625
- " login"
16626
- ] }),
16627
- showLogout && /* @__PURE__ */ jsxs("span", { children: [
16628
- " · ",
16629
- /* @__PURE__ */ jsx$1("span", {
16630
- fg: COLOR.warn,
16631
- children: "ctrl+O"
16632
- }),
16633
- " logout"
16634
- ] }),
16635
- showRefresh && /* @__PURE__ */ jsxs("span", { children: [
16636
- " · ",
16637
- /* @__PURE__ */ jsx$1("span", {
16638
- fg: COLOR.warn,
16639
- children: "ctrl+r"
16640
- }),
16641
- " refresh"
16642
- ] }),
16643
- showCancel ? /* @__PURE__ */ jsxs("span", { children: [
16644
- " · ",
16645
- /* @__PURE__ */ jsx$1("span", {
16646
- fg: COLOR.warn,
16647
- children: "esc"
16648
- }),
16649
- " cancel"
16650
- ] }) : /* @__PURE__ */ jsxs("span", { children: [
16651
- " · ",
16652
- /* @__PURE__ */ jsx$1("span", {
16653
- fg: COLOR.warn,
16654
- children: "esc"
16655
- }),
16656
- " close"
16657
- ] })
16658
- ]
16659
- });
16948
+ return /* @__PURE__ */ jsx$1(HintRow, { hints: [
16949
+ {
16950
+ key: "←→",
16951
+ label: "tabs"
16952
+ },
16953
+ {
16954
+ key: "↑↓",
16955
+ label: activeTab === "keybindings" || activeTab === "logs" ? "scroll" : "navigate"
16956
+ },
16957
+ ...activeTab === "logs" ? [{
16958
+ key: "ctrl+l",
16959
+ label: "level"
16960
+ }, {
16961
+ key: "",
16962
+ label: "type to filter"
16963
+ }] : [{
16964
+ key: "↵",
16965
+ label: isCategoryTab(activeTab) ? "toggle/cycle/select" : activeTab === "keybindings" ? "edit file" : activeTab === "authentication" ? "switch / re-authenticate" : "toggle"
16966
+ }],
16967
+ ...activeTab === "mcps" && focusedMcpRow ? [{
16968
+ key: "tab",
16969
+ label: focusedMcpRow.kind === "server" ? "expand/collapse" : "collapse"
16970
+ }] : [],
16971
+ ...activeTab === "extensions" && focusedExtensionExpandable ? [{
16972
+ key: "tab",
16973
+ label: "settings"
16974
+ }] : [],
16975
+ ...activeTab === "extensions" && canEditExtension ? [{
16976
+ key: "ctrl+e",
16977
+ label: "edit"
16978
+ }] : [],
16979
+ ...activeTab === "extensions" && canCreateExtension ? [{
16980
+ key: "ctrl+a",
16981
+ label: "create"
16982
+ }] : [],
16983
+ ...activeTab === "extensions" && canUpdateExtension ? [{
16984
+ key: "ctrl+u",
16985
+ label: "update"
16986
+ }] : [],
16987
+ ...activeTab === "extensions" && canRemoveExtension ? [{
16988
+ key: "ctrl+x",
16989
+ label: "remove"
16990
+ }] : [],
16991
+ ...activeTab === "extensions" && canInstallExtension ? [{
16992
+ key: "ctrl+g",
16993
+ label: "install"
16994
+ }] : [],
16995
+ ...activeTab === "skills" && canInstallSkill ? [{
16996
+ key: "ctrl+g",
16997
+ label: "install"
16998
+ }] : [],
16999
+ ...activeTab === "skills" && canRemoveSkill ? [{
17000
+ key: "ctrl+x",
17001
+ label: "delete"
17002
+ }] : [],
17003
+ ...activeTab === "mcps" && canAddMcp ? [{
17004
+ key: "ctrl+g",
17005
+ label: "add server"
17006
+ }] : [],
17007
+ ...activeTab === "mcps" && canRemoveMcp && onServerRow ? [{
17008
+ key: "ctrl+x",
17009
+ label: "remove"
17010
+ }] : [],
17011
+ ...showLogin ? [{
17012
+ key: "ctrl+L",
17013
+ label: "login"
17014
+ }] : [],
17015
+ ...showLogout ? [{
17016
+ key: "ctrl+O",
17017
+ label: "logout"
17018
+ }] : [],
17019
+ ...showRefresh ? [{
17020
+ key: "ctrl+r",
17021
+ label: "refresh"
17022
+ }] : [],
17023
+ {
17024
+ key: "esc",
17025
+ label: showCancel ? "cancel" : "close"
17026
+ }
17027
+ ].map((h) => ({
17028
+ ...h,
17029
+ labelColor: COLOR.mute
17030
+ })) });
16660
17031
  }
16661
17032
  function generalCorpus(item) {
16662
17033
  const parts = [item.label, item.description];
@@ -16796,7 +17167,10 @@ function composeTerminalTitle(opts) {
16796
17167
  * bring it back later. No-op when `out` isn't a TTY.
16797
17168
  */
16798
17169
  function saveTerminalTitle(out = process.stdout) {
16799
- if (out.isTTY) out.write(TITLE_STACK_PUSH);
17170
+ if (!out.isTTY) return;
17171
+ try {
17172
+ out.write(TITLE_STACK_PUSH);
17173
+ } catch {}
16800
17174
  }
16801
17175
  /**
16802
17176
  * Clear our title and pop the previously-saved one. No-op when `out`
@@ -16804,7 +17178,10 @@ function saveTerminalTitle(out = process.stdout) {
16804
17178
  * otherwise the clear would wipe a title zidane never set.
16805
17179
  */
16806
17180
  function restoreTerminalTitle(out = process.stdout) {
16807
- if (out.isTTY) out.write(TITLE_STACK_RESTORE);
17181
+ if (!out.isTTY) return;
17182
+ try {
17183
+ out.write(TITLE_STACK_RESTORE);
17184
+ } catch {}
16808
17185
  }
16809
17186
  //#endregion
16810
17187
  //#region src/tui/todos-modal.tsx
@@ -18258,6 +18635,22 @@ function AppShell() {
18258
18635
  */
18259
18636
  const [compacting, setCompacting] = useState(false);
18260
18637
  /**
18638
+ * Live provider-retry indicator — set by `stream:retry` (and the bubbled
18639
+ * `child:stream:retry`, since a stalled subagent stalls the pipeline the
18640
+ * user is watching), cleared when the SAME source streams output again
18641
+ * or the run ends. Keyed by source (`'parent'` / childId) so a parallel
18642
+ * batch can't hide one child's still-active backoff behind a sibling's
18643
+ * output — the motivating /review case runs many children at once.
18644
+ * Drives the footer's `Retrying (n/max)` status; last retry wins the
18645
+ * single slot.
18646
+ */
18647
+ const [retryInfo, setRetryInfo] = useState(null);
18648
+ /** Source-scoped functional clear — bails re-render when nothing matches (hot path: every stream delta). */
18649
+ const clearRetryInfo = useCallback((source) => setRetryInfo((prev) => prev !== null && prev.source === source ? null : prev), []);
18650
+ useEffect(() => {
18651
+ if (!busy) setRetryInfo((prev) => prev === null ? prev : null);
18652
+ }, [busy]);
18653
+ /**
18261
18654
  * Rough token estimate of the in-flight compaction summary, painted
18262
18655
  * next to the footer's "Compacting…" label (`null` = no live estimate
18263
18656
  * yet). Fed by the loop's throttled `compact:progress` hook and the
@@ -19181,8 +19574,26 @@ function AppShell() {
19181
19574
  });
19182
19575
  if (pendingAutoLoginMcpRef.current === name) pendingAutoLoginMcpRef.current = null;
19183
19576
  });
19184
- agent.hooks.hook("stream:thinking", ({ delta, turnId }) => stream.queueStreamDelta("thinking", delta, { turnId }));
19185
- agent.hooks.hook("stream:text", ({ delta, turnId }) => stream.queueStreamDelta("markdown", delta, { turnId }));
19577
+ agent.hooks.hook("stream:thinking", ({ delta, turnId }) => {
19578
+ clearRetryInfo("parent");
19579
+ stream.queueStreamDelta("thinking", delta, { turnId });
19580
+ });
19581
+ agent.hooks.hook("stream:text", ({ delta, turnId }) => {
19582
+ clearRetryInfo("parent");
19583
+ stream.queueStreamDelta("markdown", delta, { turnId });
19584
+ });
19585
+ agent.hooks.hook("stream:retry", ({ nextAttempt, maxAttempts }) => setRetryInfo({
19586
+ attempt: nextAttempt,
19587
+ max: maxAttempts,
19588
+ source: "parent"
19589
+ }));
19590
+ agent.hooks.hook("child:stream:retry", ({ nextAttempt, maxAttempts, childId }) => setRetryInfo({
19591
+ attempt: nextAttempt,
19592
+ max: maxAttempts,
19593
+ source: childId
19594
+ }));
19595
+ agent.hooks.hook("stream:error", () => clearRetryInfo("parent"));
19596
+ agent.hooks.hook("child:stream:error", ({ childId }) => clearRetryInfo(childId));
19186
19597
  agent.hooks.hook("stream:server_tool_use", ({ id, name, input, turnId }) => {
19187
19598
  stream.appendImmediate({
19188
19599
  kind: "tool",
@@ -19432,6 +19843,7 @@ function AppShell() {
19432
19843
  });
19433
19844
  });
19434
19845
  agent.hooks.hook("child:stream:thinking", ({ delta, childId, depth, turnId }) => {
19846
+ clearRetryInfo(childId);
19435
19847
  stream.queueStreamDelta("thinking", delta, {
19436
19848
  childId,
19437
19849
  depth,
@@ -19439,6 +19851,7 @@ function AppShell() {
19439
19851
  });
19440
19852
  });
19441
19853
  agent.hooks.hook("child:stream:text", ({ delta, childId, depth, turnId }) => {
19854
+ clearRetryInfo(childId);
19442
19855
  stream.queueStreamDelta("markdown", delta, {
19443
19856
  childId,
19444
19857
  depth,
@@ -21539,9 +21952,10 @@ function AppShell() {
21539
21952
  ]);
21540
21953
  onLoginMcpRef.current = onLoginMcp;
21541
21954
  const onLogoutMcp = useCallback((name) => {
21955
+ const hadTokens = !!mcpCredentialStore.load(name)?.tokens;
21542
21956
  mcpCredentialStore.delete(name);
21543
21957
  const entry = mcpsCatalogRef.current.find((d) => d.config.name === name);
21544
- dispatchAuth(entry?.config.auth === "oauth" ? {
21958
+ dispatchAuth(entry?.config.auth === "oauth" || hadTokens ? {
21545
21959
  type: "auth-required",
21546
21960
  name,
21547
21961
  reason: "no-tokens"
@@ -22655,9 +23069,10 @@ function AppShell() {
22655
23069
  hints,
22656
23070
  context: contextUsage,
22657
23071
  cost: footerCost,
22658
- status: transcriptSearchMode && transcriptSearchSearching ? "searching" : pendingInteraction?.kind === "question" ? "asking" : compacting ? "compacting" : busy ? "busy" : null,
23072
+ status: transcriptSearchMode && transcriptSearchSearching ? "searching" : pendingInteraction?.kind === "question" ? "asking" : compacting ? "compacting" : retryInfo && busy ? "retrying" : busy ? "busy" : null,
22659
23073
  compactionTokens,
22660
- compactionStartedAt
23074
+ compactionStartedAt,
23075
+ retry: retryInfo
22661
23076
  }),
22662
23077
  showQuitConfirm && /* @__PURE__ */ jsxs("box", {
22663
23078
  position: "absolute",
@@ -22679,12 +23094,24 @@ function AppShell() {
22679
23094
  gap: 2,
22680
23095
  children: [/* @__PURE__ */ jsx$1("box", {
22681
23096
  backgroundColor: quitConfirmSelected === "cancel" ? COLOR.accent : void 0,
23097
+ onMouseDown: (event) => {
23098
+ event.stopPropagation();
23099
+ event.preventDefault();
23100
+ if (quitConfirmSelectedRef.current === "cancel") setShowQuitConfirm(false);
23101
+ else setQuitConfirmSelected("cancel");
23102
+ },
22682
23103
  children: /* @__PURE__ */ jsx$1("text", {
22683
23104
  fg: quitConfirmSelected === "cancel" ? SURFACE.modal : COLOR.dim,
22684
23105
  children: " Cancel "
22685
23106
  })
22686
23107
  }), /* @__PURE__ */ jsx$1("box", {
22687
23108
  backgroundColor: quitConfirmSelected === "quit" ? COLOR.warn : void 0,
23109
+ onMouseDown: (event) => {
23110
+ event.stopPropagation();
23111
+ event.preventDefault();
23112
+ if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
23113
+ else setQuitConfirmSelected("quit");
23114
+ },
22688
23115
  children: /* @__PURE__ */ jsx$1("text", {
22689
23116
  fg: quitConfirmSelected === "quit" ? SURFACE.modal : COLOR.dim,
22690
23117
  children: " Quit "