zidane 6.2.24 → 6.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{acp-CTn1kFvP.js → acp-R4-zVIud.js} +4 -4
- package/dist/{acp-CTn1kFvP.js.map → acp-R4-zVIud.js.map} +1 -1
- package/dist/acp-cli.js +5 -5
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-tLfGSSQw.js → agent-DIe8yzcb.js} +343 -30
- package/dist/agent-DIe8yzcb.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{anthropic-uKvj1yle.js → anthropic-BEYY0hVK.js} +66 -10
- package/dist/anthropic-BEYY0hVK.js.map +1 -0
- package/dist/{auth-CpdQebUy.js → auth-C4YPRDN4.js} +3 -3
- package/dist/{auth-CpdQebUy.js.map → auth-C4YPRDN4.js.map} +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat/pure.js +1 -1
- package/dist/chat.d.ts +4 -4
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +5 -5
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/daytona.d.ts.map +1 -1
- package/dist/contexts/daytona.js +9 -2
- package/dist/contexts/daytona.js.map +1 -1
- package/dist/contexts/docker.js +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/contexts/e2b.d.ts.map +1 -1
- package/dist/contexts/e2b.js +3 -4
- package/dist/contexts/e2b.js.map +1 -1
- package/dist/{contexts-A4ep-iJs.js → contexts-0rmrBhTK.js} +8 -5
- package/dist/contexts-0rmrBhTK.js.map +1 -0
- package/dist/contexts.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +2 -2
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/{output-cap-kBUHBlnq.js → glob-DluQFSw1.js} +2 -50
- package/dist/glob-DluQFSw1.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +5 -5
- package/dist/history-search-worker.js +6 -1
- package/dist/history-search-worker.js.map +1 -1
- package/dist/{index-8zm_hwdY.d.ts → index-B6eBI_OD.d.ts} +312 -96
- package/dist/index-B6eBI_OD.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +12 -9
- package/dist/index.js.map +1 -1
- package/dist/{inline-autocomplete-BAwBcZtD.js → inline-autocomplete-CbcdZpfC.js} +2 -1
- package/dist/inline-autocomplete-CbcdZpfC.js.map +1 -0
- package/dist/{login-DfJGuHUj.js → login-afiadwf0.js} +34 -18
- package/dist/login-afiadwf0.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/{messages-CvHfYB7f.js → messages-Dy9t7DbQ.js} +97 -6
- package/dist/messages-Dy9t7DbQ.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/output-cap-DM1HrgIp.js +100 -0
- package/dist/output-cap-DM1HrgIp.js.map +1 -0
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +4 -3
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/{run-summary-nD3IQ4ZQ.js → run-summary-BU_NgwUT.js} +17 -1
- package/dist/run-summary-BU_NgwUT.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-jLsc_kMH.js → session-DkVnYGm9.js} +2 -2
- package/dist/{session-jLsc_kMH.js.map → session-DkVnYGm9.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +1 -1
- package/dist/{tool-formatters-CZJYzP4x.d.ts → tool-formatters-DdchId_3.d.ts} +2 -2
- package/dist/{tool-formatters-CZJYzP4x.d.ts.map → tool-formatters-DdchId_3.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-Ded7iJjG.js → transcript-anchors-DOuzgpJx.js} +88 -12
- package/dist/transcript-anchors-DOuzgpJx.js.map +1 -0
- package/dist/{transcript-anchors-CJ2dmpQk.d.ts → transcript-anchors-DvXLpbIc.d.ts} +2 -2
- package/dist/transcript-anchors-DvXLpbIc.d.ts.map +1 -0
- package/dist/tui.d.ts +44 -7
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1387 -1106
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-ClGa1vnb.js → turn-operations-4tnvKwfJ.js} +77 -13
- package/dist/turn-operations-4tnvKwfJ.js.map +1 -0
- package/dist/{turn-operations-DZYpXcU1.d.ts → turn-operations-HRTRJIcy.d.ts} +3 -3
- package/dist/{turn-operations-DZYpXcU1.d.ts.map → turn-operations-HRTRJIcy.d.ts.map} +1 -1
- package/dist/types-BIarq1qE.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +2 -1
- package/dist/agent-tLfGSSQw.js.map +0 -1
- package/dist/anthropic-uKvj1yle.js.map +0 -1
- package/dist/contexts-A4ep-iJs.js.map +0 -1
- package/dist/index-8zm_hwdY.d.ts.map +0 -1
- package/dist/inline-autocomplete-BAwBcZtD.js.map +0 -1
- package/dist/login-DfJGuHUj.js.map +0 -1
- package/dist/messages-CvHfYB7f.js.map +0 -1
- package/dist/output-cap-kBUHBlnq.js.map +0 -1
- package/dist/run-summary-nD3IQ4ZQ.js.map +0 -1
- package/dist/transcript-anchors-CJ2dmpQk.d.ts.map +0 -1
- package/dist/transcript-anchors-Ded7iJjG.js.map +0 -1
- package/dist/turn-operations-ClGa1vnb.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-
|
|
2
|
-
import {
|
|
1
|
+
import { $ as McpAuthProvider, $t as filterChainFallbacks, A as runOAuthLogin, At as useSettings, B as mcpToolsCachePath, Bn as pruneTodosByRun, 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-DOuzgpJx.js";
|
|
2
|
+
import { B as createExtensionPromptBridge, Bt as KEYBINDING_KEY_COL_WIDTH, Ct as tailTruncate, Dt as createModelSearchTool, F as summaryToTurn, Gt as matchesBinding, Ht as formatBindingForDisplay, J as updateInstalledExtension, S as compactConversation, Tt as looksBinary, U as installExtensionFromSpec, Ut as groupBindings, V as ExtensionInstallError, Vt as ensureKeybindingsFile, Wt as keybindingsPath, X as registerExtensionHostModules, Xt as resolveExtensionKeybindings, Y as createExtensionHud, Z as discoverAndBuildExtensionRegistry, _ as resolveTasksDir, at as createExtensionHostRuntimeBridge, b as selectFilesFromSession, c as applyCompactSummaryCutoff, dn as resolveExtensionConfigValue, et as composeExtensionPreset, f as cleanupPersistedSession, fn as sanitizeExtensionConfigActionPatch, g as resolvePersistDir, gn as effectiveContextWindow, gt as isSpawnTool, h as resolveMcpWarningsDir, hn as OUTPUT_RESERVE_TOKENS, in as scanExtensionDirectories, it as partitionExtensionCatalog, m as rehomePersistedStubs, nt as filterExtensionRegistry, q as removeInstalledExtension, rn as parseExtensionScanPathEnv, sn as coerceExtensionConfigInput, t as createAgent, tn as inlineLoadedExtension, tt as disposeExtensionRegistry, ut as basic_default, v as buildPostCompactAttachments } from "./agent-DIe8yzcb.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
|
-
import { n as appendCapped } from "./output-cap-
|
|
5
|
-
import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-
|
|
4
|
+
import { n as appendCapped } from "./output-cap-DM1HrgIp.js";
|
|
5
|
+
import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-0rmrBhTK.js";
|
|
6
6
|
import { l as errorMessage } from "./errors-DJUxZg9b.js";
|
|
7
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
11
|
import { replaceDynamicSection } from "./prompt.js";
|
|
12
|
-
import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-
|
|
12
|
+
import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-DkVnYGm9.js";
|
|
13
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
|
-
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-
|
|
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-CbcdZpfC.js";
|
|
16
16
|
import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
|
|
17
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-
|
|
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-
|
|
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-afiadwf0.js";
|
|
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, w as renderDefaultCallbackPage } from "./auth-C4YPRDN4.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-
|
|
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-4tnvKwfJ.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";
|
|
@@ -198,33 +198,70 @@ function claudeProjectSlug(path) {
|
|
|
198
198
|
* `projectRoot` is derived.
|
|
199
199
|
*/
|
|
200
200
|
function listClaudeSessionFiles(opts = {}) {
|
|
201
|
+
const out = [];
|
|
202
|
+
walkClaudeSessionFiles(opts, opts.allProjects ? (file) => out.push(file) : (file, scoped) => {
|
|
203
|
+
if (scoped()) out.push(file);
|
|
204
|
+
});
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Detection-preview counts — scoped and all-projects figures from ONE
|
|
209
|
+
* directory walk (the naive two-listing approach reads every project dir
|
|
210
|
+
* twice). The scoped classification (with its per-file cwd peek for
|
|
211
|
+
* candidate dirs) runs at most once per file via the lazy `scoped()`
|
|
212
|
+
* thunk.
|
|
213
|
+
*/
|
|
214
|
+
function countClaudeSessionFiles(opts = {}) {
|
|
215
|
+
let project = 0;
|
|
216
|
+
let all = 0;
|
|
217
|
+
walkClaudeSessionFiles({
|
|
218
|
+
...opts,
|
|
219
|
+
allProjects: false
|
|
220
|
+
}, (_file, scoped) => {
|
|
221
|
+
all++;
|
|
222
|
+
if (scoped()) project++;
|
|
223
|
+
}, true);
|
|
224
|
+
return {
|
|
225
|
+
project,
|
|
226
|
+
all
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Shared walk under `~/.claude/projects`. `visit` receives every
|
|
231
|
+
* transcript of every CANDIDATE dir (scoped mode) or every dir
|
|
232
|
+
* (`allProjects` / `visitAllDirs`), plus a lazy `scoped()` thunk that
|
|
233
|
+
* answers "does this file belong to the current project?" — lazy because
|
|
234
|
+
* the answer can cost a `transcriptProjectRoot` peek (up to the whole
|
|
235
|
+
* file), which pure all-projects listings never need.
|
|
236
|
+
*/
|
|
237
|
+
function walkClaudeSessionFiles(opts, visit, visitAllDirs = false) {
|
|
201
238
|
const { projectRoot, home } = roots(opts);
|
|
202
239
|
const projectsDir = resolve(home, ".claude/projects");
|
|
203
|
-
if (!existsSync(projectsDir)) return
|
|
240
|
+
if (!existsSync(projectsDir)) return;
|
|
204
241
|
const slugs = [claudeProjectSlug(projectRoot), claudeProjectSlug(realpathOr(projectRoot))];
|
|
205
242
|
const exact = new Set(slugs);
|
|
206
243
|
const prefixes = [...new Set(slugs.map((s) => `${s}-`))];
|
|
207
244
|
const wantedRoots = /* @__PURE__ */ new Set([projectRoot, realpathOr(projectRoot)]);
|
|
208
|
-
const out = [];
|
|
209
245
|
for (const entry of readdirSync(projectsDir, { withFileTypes: true })) {
|
|
210
246
|
if (!entry.isDirectory()) continue;
|
|
211
247
|
const exactMatch = exact.has(entry.name);
|
|
212
|
-
|
|
248
|
+
const candidate = exactMatch || prefixes.some((p) => entry.name.startsWith(p));
|
|
249
|
+
if (!opts.allProjects && !visitAllDirs && !candidate) continue;
|
|
213
250
|
const projectDir = join(projectsDir, entry.name);
|
|
214
251
|
for (const file of readdirSync(projectDir)) {
|
|
215
252
|
if (!file.endsWith(".jsonl")) continue;
|
|
216
253
|
const path = join(projectDir, file);
|
|
217
|
-
|
|
254
|
+
const scoped = () => {
|
|
255
|
+
if (!candidate) return false;
|
|
218
256
|
const root = transcriptProjectRoot(path);
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
257
|
+
return root === null ? exactMatch : wantedRoots.has(root) || wantedRoots.has(realpathOr(root));
|
|
258
|
+
};
|
|
259
|
+
visit({
|
|
222
260
|
path,
|
|
223
261
|
projectDir
|
|
224
|
-
});
|
|
262
|
+
}, scoped);
|
|
225
263
|
}
|
|
226
264
|
}
|
|
227
|
-
return out;
|
|
228
265
|
}
|
|
229
266
|
/**
|
|
230
267
|
* Peek a transcript's project root: scan the first chunk for the first
|
|
@@ -363,7 +400,7 @@ function convertClaudeSession(jsonl, opts = {}) {
|
|
|
363
400
|
});
|
|
364
401
|
}
|
|
365
402
|
if (turns.length === 0) return null;
|
|
366
|
-
const id =
|
|
403
|
+
const id = opts.fallbackId ?? claudeSessionId ?? crypto.randomUUID();
|
|
367
404
|
const projectRoot = cwd ? findGitRoot(cwd) ?? cwd : void 0;
|
|
368
405
|
return {
|
|
369
406
|
session: {
|
|
@@ -375,7 +412,8 @@ function convertClaudeSession(jsonl, opts = {}) {
|
|
|
375
412
|
metadata: {
|
|
376
413
|
...title ? { title } : {},
|
|
377
414
|
importedFrom: "claude-code",
|
|
378
|
-
claudeSessionId: id
|
|
415
|
+
claudeSessionId: id,
|
|
416
|
+
...claudeSessionId && claudeSessionId !== id ? { claudeLineageId: claudeSessionId } : {}
|
|
379
417
|
},
|
|
380
418
|
createdAt: turns[0].createdAt,
|
|
381
419
|
updatedAt: turns[turns.length - 1].createdAt
|
|
@@ -493,10 +531,6 @@ async function importClaudeSessions(store, opts = {}) {
|
|
|
493
531
|
result.empty++;
|
|
494
532
|
continue;
|
|
495
533
|
}
|
|
496
|
-
if (await store.load(converted.session.id)) {
|
|
497
|
-
result.skipped++;
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
534
|
await store.save(converted.session);
|
|
501
535
|
result.imported++;
|
|
502
536
|
} catch (err) {
|
|
@@ -524,7 +558,8 @@ function importClaudeMcps(opts = {}) {
|
|
|
524
558
|
const result = {
|
|
525
559
|
copied: [],
|
|
526
560
|
skipped: [],
|
|
527
|
-
failed: []
|
|
561
|
+
failed: [],
|
|
562
|
+
duplicates: []
|
|
528
563
|
};
|
|
529
564
|
const nativeNames = /* @__PURE__ */ new Set();
|
|
530
565
|
for (const candidate of defaultMcpsConfigPaths({
|
|
@@ -567,7 +602,10 @@ function importClaudeMcps(opts = {}) {
|
|
|
567
602
|
}
|
|
568
603
|
const seen = /* @__PURE__ */ new Set();
|
|
569
604
|
for (const { servers, scope } of sources) for (const server of servers) {
|
|
570
|
-
if (seen.has(server.name))
|
|
605
|
+
if (seen.has(server.name)) {
|
|
606
|
+
result.duplicates.push(server.name);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
571
609
|
seen.add(server.name);
|
|
572
610
|
if (nativeNames.has(server.name)) {
|
|
573
611
|
result.skipped.push(server.name);
|
|
@@ -684,13 +722,28 @@ async function importFromClaudeCode(store, opts = {}) {
|
|
|
684
722
|
if (opts.skills !== false) summary.skills = importClaudeSkills(opts);
|
|
685
723
|
return summary;
|
|
686
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Per-catalog fzf instance, keyed on catalog reference identity —
|
|
727
|
+
* `suggest()` runs per keystroke and `new Fzf(...)` walks the whole
|
|
728
|
+
* command list to build its match structures. The getter returns a
|
|
729
|
+
* stable array until a registry rebuild swaps it.
|
|
730
|
+
*/
|
|
731
|
+
const fzfByCatalog = /* @__PURE__ */ new WeakMap();
|
|
732
|
+
function fzfFor(catalog) {
|
|
733
|
+
let fzf = fzfByCatalog.get(catalog);
|
|
734
|
+
if (!fzf) {
|
|
735
|
+
fzf = new Fzf(catalog, {
|
|
736
|
+
selector: (c) => c.name,
|
|
737
|
+
casing: "case-insensitive"
|
|
738
|
+
});
|
|
739
|
+
fzfByCatalog.set(catalog, fzf);
|
|
740
|
+
}
|
|
741
|
+
return fzf;
|
|
742
|
+
}
|
|
687
743
|
/** Filter + rank commands against a query (name-priority, then description, then fuzzy on name). */
|
|
688
744
|
function scoreCommands(catalog, query) {
|
|
689
745
|
const q = query.trim().toLowerCase();
|
|
690
|
-
const fuzzyHits = q.length > 0 ? new Set(
|
|
691
|
-
selector: (c) => c.name,
|
|
692
|
-
casing: "case-insensitive"
|
|
693
|
-
}).find(q).map((r) => r.item)) : null;
|
|
746
|
+
const fuzzyHits = q.length > 0 ? new Set(fzfFor(catalog).find(q).map((r) => r.item)) : null;
|
|
694
747
|
const scored = [];
|
|
695
748
|
for (const command of catalog) {
|
|
696
749
|
const name = command.name.toLowerCase();
|
|
@@ -810,12 +863,252 @@ function attachMcpWarnLog(hooks, opts) {
|
|
|
810
863
|
} };
|
|
811
864
|
}
|
|
812
865
|
//#endregion
|
|
866
|
+
//#region src/tui/clickable.tsx
|
|
867
|
+
/** Glyph / word aliases → OpenTUI key names (`parse.keypress` conventions). */
|
|
868
|
+
const KEY_NAME_ALIASES = {
|
|
869
|
+
"esc": "escape",
|
|
870
|
+
"↵": "return",
|
|
871
|
+
"enter": "return",
|
|
872
|
+
"tab": "tab",
|
|
873
|
+
"space": "space",
|
|
874
|
+
"⌫": "backspace",
|
|
875
|
+
"backspace": "backspace",
|
|
876
|
+
"pgup": "pageup",
|
|
877
|
+
"pgdn": "pagedown",
|
|
878
|
+
"↑": "up",
|
|
879
|
+
"↓": "down",
|
|
880
|
+
"←": "left",
|
|
881
|
+
"→": "right",
|
|
882
|
+
"↑↓": "down",
|
|
883
|
+
"←→": "right",
|
|
884
|
+
"del": "delete"
|
|
885
|
+
};
|
|
886
|
+
/** Decorative pseudo-keys that must never dispatch (chips, markers). */
|
|
887
|
+
const NON_KEYS = /* @__PURE__ */ new Set([
|
|
888
|
+
"✦",
|
|
889
|
+
"·",
|
|
890
|
+
"…"
|
|
891
|
+
]);
|
|
892
|
+
/** Mac-style modifier glyphs → word modifiers (`⌘k`, `⌘+k`, `⇧↵`, …). */
|
|
893
|
+
const GLYPH_MODIFIERS = {
|
|
894
|
+
"⌘": "meta",
|
|
895
|
+
"⌃": "ctrl",
|
|
896
|
+
"⌥": "option",
|
|
897
|
+
"⇧": "shift"
|
|
898
|
+
};
|
|
899
|
+
/**
|
|
900
|
+
* Parse a hint key spec (`ctrl+g`, `shift+↵`, `esc`, `a`, `↑↓`) into a
|
|
901
|
+
* dispatchable {@link ParsedKey}, or `null` when the spec is decorative
|
|
902
|
+
* or ambiguous (the hint then renders non-clickable). Modifier segments
|
|
903
|
+
* are order-insensitive; the final segment is the key itself.
|
|
904
|
+
*/
|
|
905
|
+
function parseHintKeySpec(spec) {
|
|
906
|
+
const trimmed = spec.trim().toLowerCase();
|
|
907
|
+
if (!trimmed || NON_KEYS.has(trimmed)) return null;
|
|
908
|
+
let first = trimmed.split("/")[0] ?? "";
|
|
909
|
+
first = first.replace(/[⌘⌃⌥⇧]/g, (glyph) => `${GLYPH_MODIFIERS[glyph]}+`).replace(/\+\+/g, "+");
|
|
910
|
+
if (first.startsWith("^") && first.length > 1) first = `ctrl+${first.slice(1)}`;
|
|
911
|
+
const segments = first.split("+").map((s) => s.trim()).filter(Boolean);
|
|
912
|
+
if (segments.length === 0) return null;
|
|
913
|
+
const keyPart = segments[segments.length - 1] ?? "";
|
|
914
|
+
const modifiers = segments.slice(0, -1);
|
|
915
|
+
const mods = {
|
|
916
|
+
ctrl: false,
|
|
917
|
+
meta: false,
|
|
918
|
+
shift: false,
|
|
919
|
+
option: false
|
|
920
|
+
};
|
|
921
|
+
for (const mod of modifiers) if (mod === "ctrl" || mod === "control") mods.ctrl = true;
|
|
922
|
+
else if (mod === "shift") mods.shift = true;
|
|
923
|
+
else if (mod === "alt" || mod === "option") mods.option = true;
|
|
924
|
+
else if (mod === "meta" || mod === "cmd" || mod === "super") mods.meta = true;
|
|
925
|
+
else return null;
|
|
926
|
+
const name = KEY_NAME_ALIASES[keyPart] ?? (/^f(?:[1-9]|1[0-2])$/.test(keyPart) ? keyPart : null) ?? (keyPart.length === 1 ? keyPart : null);
|
|
927
|
+
if (!name) return null;
|
|
928
|
+
const sequence = keySequence(name, mods);
|
|
929
|
+
return {
|
|
930
|
+
name,
|
|
931
|
+
...mods,
|
|
932
|
+
sequence,
|
|
933
|
+
number: /^\d$/.test(name),
|
|
934
|
+
raw: sequence,
|
|
935
|
+
eventType: "press",
|
|
936
|
+
source: "raw"
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
/** Byte sequences real terminals send for the named specials we synthesize. */
|
|
940
|
+
const NAMED_KEY_SEQUENCES = {
|
|
941
|
+
return: "\r",
|
|
942
|
+
tab: " ",
|
|
943
|
+
escape: "\x1B",
|
|
944
|
+
space: " ",
|
|
945
|
+
backspace: "",
|
|
946
|
+
delete: "\x1B[3~",
|
|
947
|
+
up: "\x1B[A",
|
|
948
|
+
down: "\x1B[B",
|
|
949
|
+
right: "\x1B[C",
|
|
950
|
+
left: "\x1B[D",
|
|
951
|
+
pageup: "\x1B[5~",
|
|
952
|
+
pagedown: "\x1B[6~"
|
|
953
|
+
};
|
|
954
|
+
/**
|
|
955
|
+
* The `sequence`/`raw` a REAL keypress would carry — without it the
|
|
956
|
+
* synthetic event is second-class: focused inputs insert text from
|
|
957
|
+
* `key.sequence` (so a clicked `@ files` hint must actually type `@`),
|
|
958
|
+
* and some handlers match on `sequence` rather than `name`. Modifier
|
|
959
|
+
* chords return `''`: their handlers dispatch on `name` + flags, and
|
|
960
|
+
* fabricating control bytes would risk double interpretation.
|
|
961
|
+
*/
|
|
962
|
+
function keySequence(name, mods) {
|
|
963
|
+
if (mods.ctrl || mods.meta || mods.option) return "";
|
|
964
|
+
if (mods.shift) return name === "tab" ? "\x1B[Z" : "";
|
|
965
|
+
if (name.length === 1) return name;
|
|
966
|
+
return NAMED_KEY_SEQUENCES[name] ?? "";
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Dispatch a hint key spec through the renderer's key pipeline. Returns
|
|
970
|
+
* `false` for unparseable specs (no-op). Exposed for non-hint surfaces
|
|
971
|
+
* that want the same click==keypress contract.
|
|
972
|
+
*
|
|
973
|
+
* CONTRACT NOTE: `processParsedKey` wraps the parsed key in a real
|
|
974
|
+
* `KeyEvent`, so handlers receive the same object type as terminal
|
|
975
|
+
* input — but synthesized modifier chords carry an empty
|
|
976
|
+
* `sequence`/`raw` (fabricating control bytes would risk double
|
|
977
|
+
* interpretation by byte-level consumers). Every handler in this app
|
|
978
|
+
* discriminates on `name` + modifier flags, which is the contract this
|
|
979
|
+
* dispatch guarantees; a future handler matching on non-empty
|
|
980
|
+
* `sequence` for chords would see synthesized clicks differ from real
|
|
981
|
+
* keystrokes.
|
|
982
|
+
*/
|
|
983
|
+
function dispatchHintKey(renderer, spec) {
|
|
984
|
+
const parsed = parseHintKeySpec(spec);
|
|
985
|
+
if (!parsed) return false;
|
|
986
|
+
renderer.keyInput.processParsedKey(parsed);
|
|
987
|
+
return true;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* True when a row click landed on the row's `[✓] ` checkbox cell — the
|
|
991
|
+
* columns `[startCol, startCol + width)` of the FIRST line of the target
|
|
992
|
+
* renderable. Rows that must stay single `<text>` renderables (the MCP
|
|
993
|
+
* list documents an OpenTUI layout regression forbidding box wrappers)
|
|
994
|
+
* can't make the checkbox its own renderable, so zone-splitting works by
|
|
995
|
+
* coordinate instead: `event.x/y` are absolute, the target renderable
|
|
996
|
+
* knows its own absolute origin, and the difference is the glyph column.
|
|
997
|
+
* The `y` check pins line 0 so wrapped continuation lines can't false-
|
|
998
|
+
* positive into the checkbox range. Degrades to `false` (row semantics)
|
|
999
|
+
* when the target's geometry is unavailable.
|
|
1000
|
+
*/
|
|
1001
|
+
function inCheckboxZone(event, startCol, width = 4) {
|
|
1002
|
+
const target = event.target;
|
|
1003
|
+
if (!target || typeof target.x !== "number" || typeof target.y !== "number") return false;
|
|
1004
|
+
if (event.y - target.y !== 0) return false;
|
|
1005
|
+
const col = event.x - target.x;
|
|
1006
|
+
return col >= startCol && col < startCol + width;
|
|
1007
|
+
}
|
|
1008
|
+
/** Spans for one hint's text content — shared by hover and rest states. */
|
|
1009
|
+
function hintSpans(hint, COLOR, hovered) {
|
|
1010
|
+
const keyColor = hovered ? COLOR.accent : hint.keyColor ?? COLOR.warn;
|
|
1011
|
+
const labelColor = hovered ? COLOR.accent : hint.labelColor ?? COLOR.dim;
|
|
1012
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1013
|
+
hint.key !== "" && /* @__PURE__ */ jsx$1("span", {
|
|
1014
|
+
fg: keyColor,
|
|
1015
|
+
children: hint.key
|
|
1016
|
+
}),
|
|
1017
|
+
hint.extra && /* @__PURE__ */ jsx$1("span", {
|
|
1018
|
+
fg: hovered ? COLOR.accent : hint.extra.keyColor ?? COLOR.mute,
|
|
1019
|
+
children: hint.extra.key
|
|
1020
|
+
}),
|
|
1021
|
+
/* @__PURE__ */ jsx$1("span", {
|
|
1022
|
+
fg: labelColor,
|
|
1023
|
+
children: hint.key === "" ? hint.label : ` ${hint.label}`
|
|
1024
|
+
}),
|
|
1025
|
+
hint.extra && /* @__PURE__ */ jsx$1("span", {
|
|
1026
|
+
fg: hovered ? COLOR.accent : hint.extra.labelColor ?? COLOR.dim,
|
|
1027
|
+
children: ` ${hint.extra.label}`
|
|
1028
|
+
})
|
|
1029
|
+
] });
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* One hint as its own `<text>` renderable — clickable when its key spec
|
|
1033
|
+
* parses, hover-highlighted in the theme accent. Non-parseable hints
|
|
1034
|
+
* render identically but inert, so decorative chips cost nothing.
|
|
1035
|
+
*/
|
|
1036
|
+
function ClickableHint({ hint }) {
|
|
1037
|
+
const COLOR = useColors();
|
|
1038
|
+
const renderer = useRenderer();
|
|
1039
|
+
const [hovered, setHovered] = useState(false);
|
|
1040
|
+
if (!(parseHintKeySpec(hint.key) !== null)) return /* @__PURE__ */ jsx$1("text", {
|
|
1041
|
+
selectable: false,
|
|
1042
|
+
children: hintSpans(hint, COLOR, false)
|
|
1043
|
+
});
|
|
1044
|
+
return /* @__PURE__ */ jsx$1("text", {
|
|
1045
|
+
selectable: false,
|
|
1046
|
+
onMouseDown: (event) => {
|
|
1047
|
+
event.stopPropagation();
|
|
1048
|
+
event.preventDefault();
|
|
1049
|
+
dispatchHintKey(renderer, hint.key);
|
|
1050
|
+
},
|
|
1051
|
+
onMouseOver: () => setHovered(true),
|
|
1052
|
+
onMouseOut: () => setHovered(false),
|
|
1053
|
+
children: hintSpans(hint, COLOR, hovered)
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Modal-footer flavor of {@link HintRow}: same clickable hints, but with
|
|
1058
|
+
* the modal convention's mute labels (footers historically rendered as
|
|
1059
|
+
* `<text fg={mute}>` prose) and falsy-entry filtering so call sites can
|
|
1060
|
+
* express conditional hints inline:
|
|
1061
|
+
*
|
|
1062
|
+
* ```tsx
|
|
1063
|
+
* <ModalHints hints={[
|
|
1064
|
+
* { key: '↑↓', label: 'navigate' },
|
|
1065
|
+
* onRefresh && { key: 'ctrl+r', label: 'refresh' },
|
|
1066
|
+
* { key: 'esc', label: 'close' },
|
|
1067
|
+
* ]}
|
|
1068
|
+
* />
|
|
1069
|
+
* ```
|
|
1070
|
+
*
|
|
1071
|
+
* Per-hint `keyColor`/`labelColor` overrides still win over the defaults.
|
|
1072
|
+
*/
|
|
1073
|
+
function ModalHints({ hints }) {
|
|
1074
|
+
const COLOR = useColors();
|
|
1075
|
+
return /* @__PURE__ */ jsx$1(HintRow, { hints: hints.filter((h) => Boolean(h)).map((h) => ({
|
|
1076
|
+
labelColor: COLOR.mute,
|
|
1077
|
+
...h
|
|
1078
|
+
})) });
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* A row of clickable hints with ` · ` separators — the renderable-per-hint
|
|
1082
|
+
* replacement for the span-based `renderHintSpans`. Text content is
|
|
1083
|
+
* byte-identical to the span renderer, so existing `hintsLength` width
|
|
1084
|
+
* math keeps holding.
|
|
1085
|
+
*/
|
|
1086
|
+
function HintRow({ hints }) {
|
|
1087
|
+
const COLOR = useColors();
|
|
1088
|
+
return /* @__PURE__ */ jsx$1("box", {
|
|
1089
|
+
style: {
|
|
1090
|
+
flexDirection: "row",
|
|
1091
|
+
flexWrap: "wrap"
|
|
1092
|
+
},
|
|
1093
|
+
children: hints.map((hint, i) => /* @__PURE__ */ jsxs("box", {
|
|
1094
|
+
style: { flexDirection: "row" },
|
|
1095
|
+
children: [i > 0 && /* @__PURE__ */ jsx$1("text", {
|
|
1096
|
+
selectable: false,
|
|
1097
|
+
fg: COLOR.mute,
|
|
1098
|
+
children: " · "
|
|
1099
|
+
}), /* @__PURE__ */ jsx$1(ClickableHint, { hint })]
|
|
1100
|
+
}, `${hint.key}\u0000${hint.label}\u0000${i}`))
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
//#endregion
|
|
813
1104
|
//#region src/tui/modal.tsx
|
|
814
1105
|
const ModalContext = createContext(null);
|
|
815
1106
|
function ModalRoot({ children }) {
|
|
816
1107
|
const [active, setActive] = useState(null);
|
|
817
1108
|
const [lockCount, setLockCount] = useState(0);
|
|
818
1109
|
const [escLockCount, setEscLockCount] = useState(0);
|
|
1110
|
+
const backdropRef = useRef(null);
|
|
1111
|
+
const renderer = useRenderer();
|
|
819
1112
|
const api = useMemo(() => ({
|
|
820
1113
|
open: (node) => setActive(node),
|
|
821
1114
|
close: () => setActive(null),
|
|
@@ -842,6 +1135,7 @@ function ModalRoot({ children }) {
|
|
|
842
1135
|
},
|
|
843
1136
|
children
|
|
844
1137
|
}), active && /* @__PURE__ */ jsx$1("box", {
|
|
1138
|
+
ref: backdropRef,
|
|
845
1139
|
style: {
|
|
846
1140
|
position: "absolute",
|
|
847
1141
|
top: 0,
|
|
@@ -852,6 +1146,12 @@ function ModalRoot({ children }) {
|
|
|
852
1146
|
justifyContent: "center",
|
|
853
1147
|
zIndex: 100
|
|
854
1148
|
},
|
|
1149
|
+
onMouseDown: (event) => {
|
|
1150
|
+
if (event.target !== backdropRef.current) return;
|
|
1151
|
+
event.stopPropagation();
|
|
1152
|
+
event.preventDefault();
|
|
1153
|
+
dispatchHintKey(renderer, "esc");
|
|
1154
|
+
},
|
|
855
1155
|
children: active
|
|
856
1156
|
})]
|
|
857
1157
|
});
|
|
@@ -995,26 +1295,20 @@ function AgentPickerModal({ agents, currentAgentId, onPick }) {
|
|
|
995
1295
|
if (option) onPick(option.value);
|
|
996
1296
|
}
|
|
997
1297
|
}),
|
|
998
|
-
/* @__PURE__ */
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
fg: COLOR.warn,
|
|
1013
|
-
children: "esc"
|
|
1014
|
-
}),
|
|
1015
|
-
" close"
|
|
1016
|
-
]
|
|
1017
|
-
})
|
|
1298
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
1299
|
+
{
|
|
1300
|
+
key: "↑↓",
|
|
1301
|
+
label: "navigate"
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
key: "↵",
|
|
1305
|
+
label: "select"
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
key: "esc",
|
|
1309
|
+
label: "close"
|
|
1310
|
+
}
|
|
1311
|
+
] })
|
|
1018
1312
|
]
|
|
1019
1313
|
});
|
|
1020
1314
|
}
|
|
@@ -1222,31 +1516,28 @@ function CancelToolModal({ inFlight, onCancel, onCancelAll, onClose }) {
|
|
|
1222
1516
|
callIdColWidth,
|
|
1223
1517
|
childColWidth
|
|
1224
1518
|
}, row.callId))
|
|
1225
|
-
}), /* @__PURE__ */
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
" close"
|
|
1248
|
-
]
|
|
1249
|
-
})]
|
|
1519
|
+
}), /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
1520
|
+
{
|
|
1521
|
+
key: "↑↓",
|
|
1522
|
+
label: "navigate",
|
|
1523
|
+
labelColor: COLOR.dim
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
key: "↵",
|
|
1527
|
+
label: "cancel selected",
|
|
1528
|
+
labelColor: COLOR.dim
|
|
1529
|
+
},
|
|
1530
|
+
{
|
|
1531
|
+
key: "a",
|
|
1532
|
+
label: "cancel all",
|
|
1533
|
+
labelColor: COLOR.dim
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
key: "esc",
|
|
1537
|
+
label: "close",
|
|
1538
|
+
labelColor: COLOR.dim
|
|
1539
|
+
}
|
|
1540
|
+
] })]
|
|
1250
1541
|
});
|
|
1251
1542
|
}
|
|
1252
1543
|
function CancelToolRow({ row, isFocused, highlightBg, elapsedColWidth, callIdColWidth, childColWidth }) {
|
|
@@ -1359,13 +1650,7 @@ function ClaudeImportModal({ store, cwd, prefix, onDone }) {
|
|
|
1359
1650
|
const [summary, setSummary] = useState(null);
|
|
1360
1651
|
const [error, setError] = useState(null);
|
|
1361
1652
|
const runningRef = useRef(false);
|
|
1362
|
-
const counts = useMemo(() => ({
|
|
1363
|
-
project: listClaudeSessionFiles({ cwd }).length,
|
|
1364
|
-
all: listClaudeSessionFiles({
|
|
1365
|
-
cwd,
|
|
1366
|
-
allProjects: true
|
|
1367
|
-
}).length
|
|
1368
|
-
}), [cwd]);
|
|
1653
|
+
const counts = useMemo(() => countClaudeSessionFiles({ cwd }), [cwd]);
|
|
1369
1654
|
const rows = [
|
|
1370
1655
|
{
|
|
1371
1656
|
key: "sessions",
|
|
@@ -1420,18 +1705,19 @@ function ClaudeImportModal({ store, cwd, prefix, onDone }) {
|
|
|
1420
1705
|
await onDone?.(result);
|
|
1421
1706
|
} catch {}
|
|
1422
1707
|
};
|
|
1708
|
+
/** ↵/space/click on a row: the Import row runs, every other row toggles. */
|
|
1709
|
+
const activateRow = (row) => {
|
|
1710
|
+
if (row.key === "run") runImport();
|
|
1711
|
+
else setOptions((prev) => ({
|
|
1712
|
+
...prev,
|
|
1713
|
+
[row.key]: !prev[row.key]
|
|
1714
|
+
}));
|
|
1715
|
+
};
|
|
1423
1716
|
useKeyboard((key) => {
|
|
1424
1717
|
if (phase !== "pick") return;
|
|
1425
1718
|
if (key.name === "up" || key.ctrl && key.name === "p") setCursor((c) => (c - 1 + rows.length) % rows.length);
|
|
1426
1719
|
else if (key.name === "down" || key.ctrl && key.name === "n") setCursor((c) => (c + 1) % rows.length);
|
|
1427
|
-
else if (key.name === "return" || key.name === "space")
|
|
1428
|
-
const row = rows[cursor];
|
|
1429
|
-
if (row.key === "run") runImport();
|
|
1430
|
-
else setOptions((prev) => ({
|
|
1431
|
-
...prev,
|
|
1432
|
-
[row.key]: !prev[row.key]
|
|
1433
|
-
}));
|
|
1434
|
-
}
|
|
1720
|
+
else if (key.name === "return" || key.name === "space") activateRow(rows[cursor]);
|
|
1435
1721
|
});
|
|
1436
1722
|
if (phase === "running") return /* @__PURE__ */ jsx$1(Modal, {
|
|
1437
1723
|
title: "Import from Claude Code",
|
|
@@ -1458,26 +1744,35 @@ function ClaudeImportModal({ store, cwd, prefix, onDone }) {
|
|
|
1458
1744
|
summary.sessions.failed.length > 0 ? `, ${summary.sessions.failed.length} failed` : ""
|
|
1459
1745
|
]
|
|
1460
1746
|
}),
|
|
1747
|
+
summary?.sessions && failLine(summary.sessions.failed.map((f) => f.error)) !== null && /* @__PURE__ */ jsx$1("text", {
|
|
1748
|
+
fg: COLOR.error,
|
|
1749
|
+
children: failLine(summary.sessions.failed.map((f) => f.error))
|
|
1750
|
+
}),
|
|
1461
1751
|
summary?.mcps && /* @__PURE__ */ jsx$1("text", {
|
|
1462
1752
|
fg: COLOR.dim,
|
|
1463
1753
|
children: copyLine("mcp servers", summary.mcps)
|
|
1464
1754
|
}),
|
|
1755
|
+
summary?.mcps && failLine(summary.mcps.failed.map((f) => `${f.name}: ${f.error}`)) !== null && /* @__PURE__ */ jsx$1("text", {
|
|
1756
|
+
fg: COLOR.error,
|
|
1757
|
+
children: failLine(summary.mcps.failed.map((f) => `${f.name}: ${f.error}`))
|
|
1758
|
+
}),
|
|
1465
1759
|
summary?.skills && /* @__PURE__ */ jsx$1("text", {
|
|
1466
1760
|
fg: COLOR.dim,
|
|
1467
1761
|
children: copyLine("skills", summary.skills)
|
|
1468
1762
|
}),
|
|
1763
|
+
summary?.skills && failLine(summary.skills.failed.map((f) => `${f.name}: ${f.error}`)) !== null && /* @__PURE__ */ jsx$1("text", {
|
|
1764
|
+
fg: COLOR.error,
|
|
1765
|
+
children: failLine(summary.skills.failed.map((f) => `${f.name}: ${f.error}`))
|
|
1766
|
+
}),
|
|
1469
1767
|
summary?.notes.map((note) => /* @__PURE__ */ jsx$1("text", {
|
|
1470
1768
|
fg: COLOR.mute,
|
|
1471
1769
|
children: `· ${note}`
|
|
1472
1770
|
}, note))
|
|
1473
1771
|
]
|
|
1474
|
-
}), /* @__PURE__ */
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
children: "esc"
|
|
1479
|
-
}), " close"]
|
|
1480
|
-
})]
|
|
1772
|
+
}), /* @__PURE__ */ jsx$1(ModalHints, { hints: [{
|
|
1773
|
+
key: "esc",
|
|
1774
|
+
label: "close"
|
|
1775
|
+
}] })]
|
|
1481
1776
|
});
|
|
1482
1777
|
const enabledFor = (key) => key === "run" ? void 0 : options[key];
|
|
1483
1778
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
@@ -1489,6 +1784,14 @@ function ClaudeImportModal({ store, cwd, prefix, onDone }) {
|
|
|
1489
1784
|
const enabled = enabledFor(row.key);
|
|
1490
1785
|
return /* @__PURE__ */ jsxs("text", {
|
|
1491
1786
|
fg: focused ? COLOR.brand : COLOR.dim,
|
|
1787
|
+
selectable: false,
|
|
1788
|
+
onMouseOver: () => setCursor(i),
|
|
1789
|
+
onMouseDown: (event) => {
|
|
1790
|
+
event.stopPropagation();
|
|
1791
|
+
event.preventDefault();
|
|
1792
|
+
setCursor(i);
|
|
1793
|
+
activateRow(row);
|
|
1794
|
+
},
|
|
1492
1795
|
children: [
|
|
1493
1796
|
/* @__PURE__ */ jsx$1("span", {
|
|
1494
1797
|
fg: focused ? COLOR.brand : COLOR.mute,
|
|
@@ -1509,32 +1812,32 @@ function ClaudeImportModal({ store, cwd, prefix, onDone }) {
|
|
|
1509
1812
|
]
|
|
1510
1813
|
}, row.key);
|
|
1511
1814
|
})
|
|
1512
|
-
}), /* @__PURE__ */
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
"
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
fg: COLOR.warn,
|
|
1527
|
-
children: "esc"
|
|
1528
|
-
}),
|
|
1529
|
-
" close"
|
|
1530
|
-
]
|
|
1531
|
-
})]
|
|
1815
|
+
}), /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
1816
|
+
{
|
|
1817
|
+
key: "↑↓",
|
|
1818
|
+
label: "navigate"
|
|
1819
|
+
},
|
|
1820
|
+
{
|
|
1821
|
+
key: "↵",
|
|
1822
|
+
label: "toggle / run"
|
|
1823
|
+
},
|
|
1824
|
+
{
|
|
1825
|
+
key: "esc",
|
|
1826
|
+
label: "close"
|
|
1827
|
+
}
|
|
1828
|
+
] })]
|
|
1532
1829
|
});
|
|
1533
1830
|
}
|
|
1831
|
+
/** First failure reason (+count of the rest), or null when nothing failed. */
|
|
1832
|
+
function failLine(errors) {
|
|
1833
|
+
if (errors.length === 0) return null;
|
|
1834
|
+
return errors.length > 1 ? ` ⚠ ${errors[0]} (+${errors.length - 1} more)` : ` ⚠ ${errors[0]}`;
|
|
1835
|
+
}
|
|
1534
1836
|
function copyLine(label, result) {
|
|
1535
1837
|
const parts = [];
|
|
1536
1838
|
parts.push(result.copied.length > 0 ? `${result.copied.length} copied (${result.copied.join(", ")})` : "none copied");
|
|
1537
1839
|
if (result.skipped.length > 0) parts.push(`${result.skipped.length} skipped as native (${result.skipped.join(", ")})`);
|
|
1840
|
+
if (result.duplicates && result.duplicates.length > 0) parts.push(`${result.duplicates.length} duplicate source entr${result.duplicates.length === 1 ? "y" : "ies"} dropped (${result.duplicates.join(", ")})`);
|
|
1538
1841
|
if (result.failed.length > 0) parts.push(`${result.failed.length} failed`);
|
|
1539
1842
|
return `${label}: ${parts.join(" · ")}`;
|
|
1540
1843
|
}
|
|
@@ -1606,273 +1909,79 @@ function pickHelper() {
|
|
|
1606
1909
|
* Returns `true` if the spawn looked plausibly successful, `false`
|
|
1607
1910
|
* if we couldn't even attach to the child's stdin (binary missing,
|
|
1608
1911
|
* permission denied, etc.). An `error` event after spawn flips the
|
|
1609
|
-
* returned value to `false` retroactively — but by then the caller
|
|
1610
|
-
* has already seen `true` and moved on. We rely on the boolean only
|
|
1611
|
-
* for the synchronous "should we report progress to the user?" hint,
|
|
1612
|
-
* not as a hard contract.
|
|
1613
|
-
*/
|
|
1614
|
-
function spawnHelper(text) {
|
|
1615
|
-
const helper = pickHelper();
|
|
1616
|
-
if (!helper) return false;
|
|
1617
|
-
try {
|
|
1618
|
-
const child = spawn(helper.cmd, [...helper.args], { stdio: [
|
|
1619
|
-
"pipe",
|
|
1620
|
-
"ignore",
|
|
1621
|
-
"ignore"
|
|
1622
|
-
] });
|
|
1623
|
-
child.on("error", () => {});
|
|
1624
|
-
child.stdin?.on("error", () => {});
|
|
1625
|
-
child.stdin?.end(text, "utf8");
|
|
1626
|
-
return true;
|
|
1627
|
-
} catch {
|
|
1628
|
-
return false;
|
|
1629
|
-
}
|
|
1630
|
-
}
|
|
1631
|
-
/**
|
|
1632
|
-
* Emit the OSC 52 sequence on stdout. Skips silently when stdout
|
|
1633
|
-
* isn't a TTY so `bun run app | tee log` doesn't dump literal escape
|
|
1634
|
-
* bytes into a pipe.
|
|
1635
|
-
*/
|
|
1636
|
-
function writeOsc52(text) {
|
|
1637
|
-
if (typeof process === "undefined" || !process.stdout?.write) return false;
|
|
1638
|
-
if (!process.stdout.isTTY) return false;
|
|
1639
|
-
try {
|
|
1640
|
-
const encoded = Buffer.from(text, "utf8").toString("base64");
|
|
1641
|
-
process.stdout.write(`\x1B]52;c;${encoded}\x07`);
|
|
1642
|
-
return true;
|
|
1643
|
-
} catch {
|
|
1644
|
-
return false;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
function writeToClipboard(text) {
|
|
1648
|
-
const osc = writeOsc52(text);
|
|
1649
|
-
const helper = spawnHelper(text);
|
|
1650
|
-
return osc || helper;
|
|
1651
|
-
}
|
|
1652
|
-
//#endregion
|
|
1653
|
-
//#region src/chat/diff-stat.ts
|
|
1654
|
-
/**
|
|
1655
|
-
* Split a line into segments: `+N` runs are `add`, `−N`/`-N` runs are `del`,
|
|
1656
|
-
* everything else `plain`. Counts only match at a word boundary (start or
|
|
1657
|
-
* after whitespace/`|`/`·`), so `file-42.ts` stays plain.
|
|
1658
|
-
*/
|
|
1659
|
-
function segmentDiffStatLine(line) {
|
|
1660
|
-
const segments = [];
|
|
1661
|
-
const re = /(?<=^|[\s|·])(\+\d+|[−-]\d+)(?=$|[\s,·])/g;
|
|
1662
|
-
let last = 0;
|
|
1663
|
-
for (let m = re.exec(line); m !== null; m = re.exec(line)) {
|
|
1664
|
-
if (m.index > last) segments.push({
|
|
1665
|
-
text: line.slice(last, m.index),
|
|
1666
|
-
tone: "plain"
|
|
1667
|
-
});
|
|
1668
|
-
segments.push({
|
|
1669
|
-
text: m[0],
|
|
1670
|
-
tone: m[0].startsWith("+") ? "add" : "del"
|
|
1671
|
-
});
|
|
1672
|
-
last = m.index + m[0].length;
|
|
1673
|
-
}
|
|
1674
|
-
if (last < line.length) segments.push({
|
|
1675
|
-
text: line.slice(last),
|
|
1676
|
-
tone: "plain"
|
|
1677
|
-
});
|
|
1678
|
-
return segments.length > 0 ? segments : [{
|
|
1679
|
-
text: line,
|
|
1680
|
-
tone: "plain"
|
|
1681
|
-
}];
|
|
1682
|
-
}
|
|
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.
|
|
1912
|
+
* returned value to `false` retroactively — but by then the caller
|
|
1913
|
+
* has already seen `true` and moved on. We rely on the boolean only
|
|
1914
|
+
* for the synchronous "should we report progress to the user?" hint,
|
|
1915
|
+
* not as a hard contract.
|
|
1780
1916
|
*/
|
|
1781
|
-
function
|
|
1782
|
-
const
|
|
1783
|
-
if (!
|
|
1784
|
-
|
|
1785
|
-
|
|
1917
|
+
function spawnHelper(text) {
|
|
1918
|
+
const helper = pickHelper();
|
|
1919
|
+
if (!helper) return false;
|
|
1920
|
+
try {
|
|
1921
|
+
const child = spawn(helper.cmd, [...helper.args], { stdio: [
|
|
1922
|
+
"pipe",
|
|
1923
|
+
"ignore",
|
|
1924
|
+
"ignore"
|
|
1925
|
+
] });
|
|
1926
|
+
child.on("error", () => {});
|
|
1927
|
+
child.stdin?.on("error", () => {});
|
|
1928
|
+
child.stdin?.end(text, "utf8");
|
|
1929
|
+
return true;
|
|
1930
|
+
} catch {
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1786
1933
|
}
|
|
1787
1934
|
/**
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
*
|
|
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.
|
|
1935
|
+
* Emit the OSC 52 sequence on stdout. Skips silently when stdout
|
|
1936
|
+
* isn't a TTY so `bun run app | tee log` doesn't dump literal escape
|
|
1937
|
+
* bytes into a pipe.
|
|
1798
1938
|
*/
|
|
1799
|
-
function
|
|
1800
|
-
|
|
1801
|
-
if (!
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
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
|
-
] });
|
|
1939
|
+
function writeOsc52(text) {
|
|
1940
|
+
if (typeof process === "undefined" || !process.stdout?.write) return false;
|
|
1941
|
+
if (!process.stdout.isTTY) return false;
|
|
1942
|
+
try {
|
|
1943
|
+
const encoded = Buffer.from(text, "utf8").toString("base64");
|
|
1944
|
+
process.stdout.write(`\x1B]52;c;${encoded}\x07`);
|
|
1945
|
+
return true;
|
|
1946
|
+
} catch {
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1828
1949
|
}
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
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
|
-
});
|
|
1950
|
+
function writeToClipboard(text) {
|
|
1951
|
+
const osc = writeOsc52(text);
|
|
1952
|
+
const helper = spawnHelper(text);
|
|
1953
|
+
return osc || helper;
|
|
1853
1954
|
}
|
|
1955
|
+
//#endregion
|
|
1956
|
+
//#region src/chat/diff-stat.ts
|
|
1854
1957
|
/**
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1858
|
-
* math keeps holding.
|
|
1958
|
+
* Split a line into segments: `+N` runs are `add`, `−N`/`-N` runs are `del`,
|
|
1959
|
+
* everything else `plain`. Counts only match at a word boundary (start or
|
|
1960
|
+
* after whitespace/`|`/`·`), so `file-42.ts` stays plain.
|
|
1859
1961
|
*/
|
|
1860
|
-
function
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1962
|
+
function segmentDiffStatLine(line) {
|
|
1963
|
+
const segments = [];
|
|
1964
|
+
const re = /(?<=^|[\s|·])(\+\d+|[−-]\d+)(?=$|[\s,·])/g;
|
|
1965
|
+
let last = 0;
|
|
1966
|
+
for (let m = re.exec(line); m !== null; m = re.exec(line)) {
|
|
1967
|
+
if (m.index > last) segments.push({
|
|
1968
|
+
text: line.slice(last, m.index),
|
|
1969
|
+
tone: "plain"
|
|
1970
|
+
});
|
|
1971
|
+
segments.push({
|
|
1972
|
+
text: m[0],
|
|
1973
|
+
tone: m[0].startsWith("+") ? "add" : "del"
|
|
1974
|
+
});
|
|
1975
|
+
last = m.index + m[0].length;
|
|
1976
|
+
}
|
|
1977
|
+
if (last < line.length) segments.push({
|
|
1978
|
+
text: line.slice(last),
|
|
1979
|
+
tone: "plain"
|
|
1875
1980
|
});
|
|
1981
|
+
return segments.length > 0 ? segments : [{
|
|
1982
|
+
text: line,
|
|
1983
|
+
tone: "plain"
|
|
1984
|
+
}];
|
|
1876
1985
|
}
|
|
1877
1986
|
//#endregion
|
|
1878
1987
|
//#region src/tui/crush-throbber.tsx
|
|
@@ -2510,7 +2619,7 @@ function onInputSubmit(handler) {
|
|
|
2510
2619
|
* Width tiering is driven by plain-text length estimates — close enough
|
|
2511
2620
|
* since the segments are ASCII-heavy.
|
|
2512
2621
|
*/
|
|
2513
|
-
function Footer({ hints, context, cost = null, status = null, compactionTokens = null, compactionStartedAt = null, retry = null }) {
|
|
2622
|
+
function Footer({ hints, context, cost = null, status = null, compactionTokens = null, compactionStartedAt = null, retry = null, contextKey = null }) {
|
|
2514
2623
|
const { width } = useTerminalDimensions();
|
|
2515
2624
|
const showCost = typeof cost === "number" && cost > 0;
|
|
2516
2625
|
const inner = Math.max(0, width - 2);
|
|
@@ -2542,7 +2651,8 @@ function Footer({ hints, context, cost = null, status = null, compactionTokens =
|
|
|
2542
2651
|
/* @__PURE__ */ jsx$1("box", { style: { flexGrow: 1 } }),
|
|
2543
2652
|
/* @__PURE__ */ jsx$1(RightStatus, {
|
|
2544
2653
|
cost: showCost ? cost : null,
|
|
2545
|
-
context
|
|
2654
|
+
context,
|
|
2655
|
+
contextKey: contextKey ?? null
|
|
2546
2656
|
})
|
|
2547
2657
|
]
|
|
2548
2658
|
});
|
|
@@ -2569,22 +2679,43 @@ function Footer({ hints, context, cost = null, status = null, compactionTokens =
|
|
|
2569
2679
|
},
|
|
2570
2680
|
children: [/* @__PURE__ */ jsx$1("box", { style: { flexGrow: 1 } }), /* @__PURE__ */ jsx$1(RightStatus, {
|
|
2571
2681
|
cost: showCost ? cost : null,
|
|
2572
|
-
context
|
|
2682
|
+
context,
|
|
2683
|
+
contextKey: contextKey ?? null
|
|
2573
2684
|
})]
|
|
2574
2685
|
})]
|
|
2575
2686
|
});
|
|
2576
2687
|
}
|
|
2577
|
-
function RightStatus({ cost, context }) {
|
|
2688
|
+
function RightStatus({ cost, context, contextKey }) {
|
|
2578
2689
|
const COLOR = useColors();
|
|
2690
|
+
const renderer = useRenderer();
|
|
2691
|
+
const [hovered, setHovered] = useState(false);
|
|
2579
2692
|
if (cost == null && !context) return null;
|
|
2580
|
-
|
|
2581
|
-
cost != null && /* @__PURE__ */ jsx$1(CostIndicator, {
|
|
2693
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2694
|
+
cost != null && /* @__PURE__ */ jsx$1(CostIndicator, {
|
|
2695
|
+
cost,
|
|
2696
|
+
accent: hovered
|
|
2697
|
+
}),
|
|
2582
2698
|
cost != null && context && /* @__PURE__ */ jsx$1("span", {
|
|
2583
2699
|
fg: COLOR.mute,
|
|
2584
2700
|
children: " · "
|
|
2585
2701
|
}),
|
|
2586
|
-
context && /* @__PURE__ */ jsx$1(ContextIndicator, {
|
|
2702
|
+
context && /* @__PURE__ */ jsx$1(ContextIndicator, {
|
|
2703
|
+
context,
|
|
2704
|
+
accent: hovered
|
|
2705
|
+
})
|
|
2587
2706
|
] });
|
|
2707
|
+
if (!(contextKey !== null && parseHintKeySpec(contextKey) !== null)) return /* @__PURE__ */ jsx$1("text", { children: body });
|
|
2708
|
+
return /* @__PURE__ */ jsx$1("text", {
|
|
2709
|
+
selectable: false,
|
|
2710
|
+
onMouseDown: (event) => {
|
|
2711
|
+
event.stopPropagation();
|
|
2712
|
+
event.preventDefault();
|
|
2713
|
+
dispatchHintKey(renderer, contextKey);
|
|
2714
|
+
},
|
|
2715
|
+
onMouseOver: () => setHovered(true),
|
|
2716
|
+
onMouseOut: () => setHovered(false),
|
|
2717
|
+
children: body
|
|
2718
|
+
});
|
|
2588
2719
|
}
|
|
2589
2720
|
/**
|
|
2590
2721
|
* Right-edge status in the Footer's left section. Busy stays glyph-only;
|
|
@@ -2666,14 +2797,15 @@ function renderHintSpans(hints, COLOR) {
|
|
|
2666
2797
|
})
|
|
2667
2798
|
] }, i));
|
|
2668
2799
|
}
|
|
2669
|
-
function ContextIndicator({ context }) {
|
|
2800
|
+
function ContextIndicator({ context, accent = false }) {
|
|
2670
2801
|
const COLOR = useColors();
|
|
2671
2802
|
const ratio = context.max > 0 ? context.used / context.max : 0;
|
|
2672
2803
|
const pct = Math.round(ratio * 100);
|
|
2673
|
-
const color = ratio >= .85 ? COLOR.error : ratio >= .6 ? COLOR.warn : COLOR.dim;
|
|
2804
|
+
const color = accent ? COLOR.accent : ratio >= .85 ? COLOR.error : ratio >= .6 ? COLOR.warn : COLOR.dim;
|
|
2805
|
+
const muted = accent ? COLOR.accent : COLOR.mute;
|
|
2674
2806
|
return /* @__PURE__ */ jsxs("span", { children: [
|
|
2675
2807
|
/* @__PURE__ */ jsx$1("span", {
|
|
2676
|
-
fg:
|
|
2808
|
+
fg: muted,
|
|
2677
2809
|
children: "ctx "
|
|
2678
2810
|
}),
|
|
2679
2811
|
/* @__PURE__ */ jsx$1("span", {
|
|
@@ -2681,7 +2813,7 @@ function ContextIndicator({ context }) {
|
|
|
2681
2813
|
children: fmtTokens(context.used)
|
|
2682
2814
|
}),
|
|
2683
2815
|
/* @__PURE__ */ jsx$1("span", {
|
|
2684
|
-
fg:
|
|
2816
|
+
fg: muted,
|
|
2685
2817
|
children: ` / ${fmtTokens(context.max)} `
|
|
2686
2818
|
}),
|
|
2687
2819
|
/* @__PURE__ */ jsx$1("span", {
|
|
@@ -2690,11 +2822,11 @@ function ContextIndicator({ context }) {
|
|
|
2690
2822
|
})
|
|
2691
2823
|
] });
|
|
2692
2824
|
}
|
|
2693
|
-
function CostIndicator({ cost }) {
|
|
2825
|
+
function CostIndicator({ cost, accent = false }) {
|
|
2694
2826
|
const COLOR = useColors();
|
|
2695
|
-
const fg = COLOR.money ?? COLOR.warn;
|
|
2827
|
+
const fg = accent ? COLOR.accent : COLOR.money ?? COLOR.warn;
|
|
2696
2828
|
return /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx$1("span", {
|
|
2697
|
-
fg: COLOR.mute,
|
|
2829
|
+
fg: accent ? COLOR.accent : COLOR.mute,
|
|
2698
2830
|
children: "$"
|
|
2699
2831
|
}), /* @__PURE__ */ jsx$1("span", {
|
|
2700
2832
|
fg,
|
|
@@ -2763,8 +2895,11 @@ function renderGradientText(text, from, to) {
|
|
|
2763
2895
|
* </box>
|
|
2764
2896
|
* ```
|
|
2765
2897
|
*/
|
|
2766
|
-
function TitleOverlay({ title, meta = null, titleColor, parentWidth, statusIcon = null, statusIconCells = 1 }) {
|
|
2898
|
+
function TitleOverlay({ title, meta = null, titleColor, parentWidth, statusIcon = null, statusIconCells = 1, clickKey = null }) {
|
|
2767
2899
|
const COLOR = useColors();
|
|
2900
|
+
const renderer = useRenderer();
|
|
2901
|
+
const [titleHovered, setTitleHovered] = useState(false);
|
|
2902
|
+
const clickable = clickKey !== null && parseHintKeySpec(clickKey) !== null;
|
|
2768
2903
|
const { width: termWidth } = useTerminalDimensions();
|
|
2769
2904
|
const useGradient = titleColor === void 0;
|
|
2770
2905
|
const gradientFrom = COLOR.throbber?.to ?? COLOR.accent;
|
|
@@ -2777,32 +2912,51 @@ function TitleOverlay({ title, meta = null, titleColor, parentWidth, statusIcon
|
|
|
2777
2912
|
const showMeta = meta != null && metaLen > 0 && title.length + iconReserve + TITLE_OVERLAY_WRAP + TITLE_OVERLAY_GAP + metaLen + TITLE_OVERLAY_META_WRAP <= inner;
|
|
2778
2913
|
const titleBudget = (showMeta ? inner - (metaLen + TITLE_OVERLAY_META_WRAP) - TITLE_OVERLAY_GAP - TITLE_OVERLAY_WRAP : inner - TITLE_OVERLAY_WRAP) - iconReserve;
|
|
2779
2914
|
const visibleTitle = titleBudget <= 0 ? "" : truncateTrailing(title, titleBudget);
|
|
2780
|
-
|
|
2915
|
+
const titleBody = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2916
|
+
/* @__PURE__ */ jsx$1("span", {
|
|
2917
|
+
fg: COLOR.mute,
|
|
2918
|
+
children: " "
|
|
2919
|
+
}),
|
|
2920
|
+
titleHovered ? /* @__PURE__ */ jsx$1("span", {
|
|
2921
|
+
fg: COLOR.accent,
|
|
2922
|
+
children: visibleTitle
|
|
2923
|
+
}) : useGradient && visibleTitle.length > 0 ? renderGradientText(visibleTitle, gradientFrom, gradientTo) : /* @__PURE__ */ jsx$1("span", {
|
|
2924
|
+
fg,
|
|
2925
|
+
children: visibleTitle
|
|
2926
|
+
}),
|
|
2927
|
+
statusIcon && /* @__PURE__ */ jsx$1("span", {
|
|
2928
|
+
fg: COLOR.mute,
|
|
2929
|
+
children: " "
|
|
2930
|
+
}),
|
|
2931
|
+
statusIcon,
|
|
2932
|
+
/* @__PURE__ */ jsx$1("span", {
|
|
2933
|
+
fg: COLOR.mute,
|
|
2934
|
+
children: " "
|
|
2935
|
+
})
|
|
2936
|
+
] });
|
|
2937
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [(visibleTitle || statusIcon) && (clickable ? /* @__PURE__ */ jsx$1("text", {
|
|
2781
2938
|
style: {
|
|
2782
2939
|
position: "absolute",
|
|
2783
2940
|
top: 0,
|
|
2784
2941
|
left: 1
|
|
2785
2942
|
},
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
})
|
|
2804
|
-
]
|
|
2805
|
-
}), showMeta && meta && /* @__PURE__ */ jsxs("text", {
|
|
2943
|
+
selectable: false,
|
|
2944
|
+
onMouseDown: (event) => {
|
|
2945
|
+
event.stopPropagation();
|
|
2946
|
+
event.preventDefault();
|
|
2947
|
+
dispatchHintKey(renderer, clickKey);
|
|
2948
|
+
},
|
|
2949
|
+
onMouseOver: () => setTitleHovered(true),
|
|
2950
|
+
onMouseOut: () => setTitleHovered(false),
|
|
2951
|
+
children: titleBody
|
|
2952
|
+
}) : /* @__PURE__ */ jsx$1("text", {
|
|
2953
|
+
style: {
|
|
2954
|
+
position: "absolute",
|
|
2955
|
+
top: 0,
|
|
2956
|
+
left: 1
|
|
2957
|
+
},
|
|
2958
|
+
children: titleBody
|
|
2959
|
+
})), showMeta && meta && /* @__PURE__ */ jsxs("text", {
|
|
2806
2960
|
style: {
|
|
2807
2961
|
position: "absolute",
|
|
2808
2962
|
top: 0,
|
|
@@ -2894,7 +3048,72 @@ function StatusSpinner({ color }) {
|
|
|
2894
3048
|
children: useSpinnerFrame()
|
|
2895
3049
|
});
|
|
2896
3050
|
}
|
|
2897
|
-
|
|
3051
|
+
/**
|
|
3052
|
+
* Bordered container for one run of subagent events. The box's border +
|
|
3053
|
+
* left padding give the visual "this is a subagent" affordance, so events
|
|
3054
|
+
* inside render with `depthOffset: 1` — a direct child of the parent
|
|
3055
|
+
* (depth 1) sits flush against the box's inner padding rather than being
|
|
3056
|
+
* indented twice. Grandchildren (depth ≥ 2) still indent further, so
|
|
3057
|
+
* nested subagents remain visually distinct.
|
|
3058
|
+
*
|
|
3059
|
+
* Memoized like {@link EventLine} so completed historical runs don't
|
|
3060
|
+
* re-execute per drain tick — but with a custom comparator:
|
|
3061
|
+
* `partitionTranscript` / `computeTurnAnchors` rebuild the per-run
|
|
3062
|
+
* `events` / `anchorIds` arrays on every transcript change, so the default
|
|
3063
|
+
* shallow compare would never bail while streaming. The inner event
|
|
3064
|
+
* references (and the deterministic anchor-id strings) ARE stable until
|
|
3065
|
+
* their content changes, so an element-wise compare recovers the bail-out.
|
|
3066
|
+
*/
|
|
3067
|
+
const SubagentBlock = memo(({ events, previous, selectedTurnId = null, anchorIds, codeSettleTargets, codeSettleEpoch = 0, live, searchQuery, searchTitleMatches }) => {
|
|
3068
|
+
const COLOR = useColors();
|
|
3069
|
+
const childIds = useMemo(() => {
|
|
3070
|
+
const set = /* @__PURE__ */ new Set();
|
|
3071
|
+
for (const e of events) if (e.childId) set.add(e.childId);
|
|
3072
|
+
return Array.from(set);
|
|
3073
|
+
}, [events]);
|
|
3074
|
+
const title = childIds.length === 0 ? " subagent " : childIds.length === 1 ? ` ${childIds[0]} ` : ` subagents · ${childIds.join(", ")} `;
|
|
3075
|
+
const marginTop = previous ? 1 : 0;
|
|
3076
|
+
const hideChildLabel = childIds.length === 1;
|
|
3077
|
+
return /* @__PURE__ */ jsx$1("box", {
|
|
3078
|
+
title,
|
|
3079
|
+
style: {
|
|
3080
|
+
border: true,
|
|
3081
|
+
borderColor: COLOR.brand,
|
|
3082
|
+
paddingLeft: 1,
|
|
3083
|
+
paddingRight: 1,
|
|
3084
|
+
paddingTop: 0,
|
|
3085
|
+
paddingBottom: 0,
|
|
3086
|
+
marginTop,
|
|
3087
|
+
flexDirection: "column",
|
|
3088
|
+
flexShrink: 0,
|
|
3089
|
+
alignSelf: "stretch"
|
|
3090
|
+
},
|
|
3091
|
+
children: events.map((evt, i) => /* @__PURE__ */ jsx$1(EventLine, {
|
|
3092
|
+
event: evt,
|
|
3093
|
+
previous: events[i - 1],
|
|
3094
|
+
depthOffset: 1,
|
|
3095
|
+
selected: selectedTurnId !== null && evt.turnId === selectedTurnId,
|
|
3096
|
+
anchorId: anchorIds?.[i],
|
|
3097
|
+
hideChildLabel,
|
|
3098
|
+
settleCodeEpoch: codeSettleTargets?.has(evt) ? codeSettleEpoch : 0,
|
|
3099
|
+
liveSpawn: evt.kind === "spawn-start" ? live?.find((spawn) => spawn.id === evt.childId) : void 0,
|
|
3100
|
+
searchQuery,
|
|
3101
|
+
searchTitleMatch: evt.kind === "tool" && evt.callId !== void 0 && searchTitleMatches?.callIds.has(evt.callId) || (evt.kind === "spawn-start" || evt.kind === "spawn-end") && evt.childId !== void 0 && searchTitleMatches?.childIds.has(evt.childId)
|
|
3102
|
+
}, i))
|
|
3103
|
+
});
|
|
3104
|
+
}, (prev, next) => sameElements(prev.events, next.events) && sameElements(prev.anchorIds, next.anchorIds) && prev.previous === next.previous && prev.selectedTurnId === next.selectedTurnId && prev.codeSettleTargets === next.codeSettleTargets && prev.codeSettleEpoch === next.codeSettleEpoch && prev.live === next.live && prev.searchQuery === next.searchQuery && prev.searchTitleMatches === next.searchTitleMatches);
|
|
3105
|
+
/** Element-wise `===` over possibly-rebuilt arrays — see {@link SubagentBlock}. */
|
|
3106
|
+
function sameElements(a, b) {
|
|
3107
|
+
if (a === b) return true;
|
|
3108
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
3109
|
+
return a.every((value, i) => value === b[i]);
|
|
3110
|
+
}
|
|
3111
|
+
const NO_SEARCH_MATCHES = {
|
|
3112
|
+
direct: /* @__PURE__ */ new Set(),
|
|
3113
|
+
callIds: /* @__PURE__ */ new Set(),
|
|
3114
|
+
childIds: /* @__PURE__ */ new Set()
|
|
3115
|
+
};
|
|
3116
|
+
function Transcript({ events, settings, selectedTurnId = null, busy = false, searchQuery, searchPending = false, onLoadEarlier, onSelectTurn, onOpenTurn }) {
|
|
2898
3117
|
const COLOR = useColors();
|
|
2899
3118
|
const showThrobber = busy && settings.showThrobber;
|
|
2900
3119
|
const codeSettle = useRef({
|
|
@@ -2919,14 +3138,10 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
2919
3138
|
const throbberLabel = events.length > 0 && events[events.length - 1].kind === "thinking" ? "Thinking" : void 0;
|
|
2920
3139
|
const ownership = useMemo(() => turnSelectionOwnership(events), [events]);
|
|
2921
3140
|
const searchMatches = useMemo(() => {
|
|
2922
|
-
|
|
2923
|
-
const
|
|
2924
|
-
const
|
|
2925
|
-
|
|
2926
|
-
direct,
|
|
2927
|
-
callIds,
|
|
2928
|
-
childIds
|
|
2929
|
-
};
|
|
3141
|
+
if (!searchQuery) return NO_SEARCH_MATCHES;
|
|
3142
|
+
const direct = /* @__PURE__ */ new Set();
|
|
3143
|
+
const callIds = /* @__PURE__ */ new Set();
|
|
3144
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
2930
3145
|
const stack = [];
|
|
2931
3146
|
for (const event of events) {
|
|
2932
3147
|
if (event.kind === "spawn-start") stack.push(event);
|
|
@@ -2955,9 +3170,13 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
2955
3170
|
searchMatches
|
|
2956
3171
|
]);
|
|
2957
3172
|
const scrollboxRef = useRef(null);
|
|
3173
|
+
const mouseSelectionRef = useRef(false);
|
|
3174
|
+
const pressedTurnRef = useRef(null);
|
|
2958
3175
|
const rowVisibility = useRef(createRowVisibilityStore());
|
|
2959
3176
|
const loadEarlierRef = useRef(onLoadEarlier);
|
|
2960
3177
|
loadEarlierRef.current = onLoadEarlier;
|
|
3178
|
+
const eventsRef = useRef(events);
|
|
3179
|
+
eventsRef.current = events;
|
|
2961
3180
|
const earlierLoad = useRef(null);
|
|
2962
3181
|
const earlierLoadArmed = useRef(false);
|
|
2963
3182
|
useEffect(() => {
|
|
@@ -2970,7 +3189,7 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
2970
3189
|
});
|
|
2971
3190
|
const topThreshold = Math.max(4, Math.floor(sb.viewport.height / 4));
|
|
2972
3191
|
if (sb.scrollTop > topThreshold) earlierLoadArmed.current = true;
|
|
2973
|
-
else if (earlierLoadArmed.current && !earlierLoad.current && loadEarlierRef.current && hiddenEarlierTranscriptCount(
|
|
3192
|
+
else if (earlierLoadArmed.current && !earlierLoad.current && loadEarlierRef.current && hiddenEarlierTranscriptCount(eventsRef.current) > 0) {
|
|
2974
3193
|
earlierLoadArmed.current = false;
|
|
2975
3194
|
const snapshot = {
|
|
2976
3195
|
oldHeight: sb.scrollHeight,
|
|
@@ -2984,7 +3203,7 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
2984
3203
|
}
|
|
2985
3204
|
}, 200);
|
|
2986
3205
|
return () => clearInterval(timer);
|
|
2987
|
-
}, [
|
|
3206
|
+
}, []);
|
|
2988
3207
|
useEffect(() => {
|
|
2989
3208
|
const pending = earlierLoad.current;
|
|
2990
3209
|
if (!pending) return;
|
|
@@ -3038,6 +3257,10 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
3038
3257
|
}, [items, events]);
|
|
3039
3258
|
useEffect(() => {
|
|
3040
3259
|
if (!selectedTurnId) return;
|
|
3260
|
+
if (mouseSelectionRef.current) {
|
|
3261
|
+
mouseSelectionRef.current = false;
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3041
3264
|
let handle = 0;
|
|
3042
3265
|
let attempts = 0;
|
|
3043
3266
|
const align = () => {
|
|
@@ -3098,7 +3321,7 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
3098
3321
|
children: [items.map((item, i) => {
|
|
3099
3322
|
if (item.kind === "event") {
|
|
3100
3323
|
const live = liveByItem.get(i);
|
|
3101
|
-
|
|
3324
|
+
const line = /* @__PURE__ */ jsx$1(EventLine, {
|
|
3102
3325
|
event: item.event,
|
|
3103
3326
|
previous: item.previous,
|
|
3104
3327
|
selected: isTurnHighlighted(item.event, selectedTurnId, ownership),
|
|
@@ -3107,6 +3330,33 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false, sea
|
|
|
3107
3330
|
liveSpawn: live?.[0],
|
|
3108
3331
|
searchQuery,
|
|
3109
3332
|
searchTitleMatch: item.event.kind === "tool" && item.event.callId !== void 0 && searchMatches.callIds.has(item.event.callId) || (item.event.kind === "spawn-start" || item.event.kind === "spawn-end") && item.event.childId !== void 0 && searchMatches.childIds.has(item.event.childId)
|
|
3333
|
+
});
|
|
3334
|
+
const selectActive = selectedTurnId !== null && (onSelectTurn !== void 0 || onOpenTurn !== void 0);
|
|
3335
|
+
const targetTurn = item.event.turnId && !item.event.childId ? ownership.get(item.event.turnId) ?? item.event.turnId : null;
|
|
3336
|
+
const active = selectActive && targetTurn !== null;
|
|
3337
|
+
return /* @__PURE__ */ jsx$1("box", {
|
|
3338
|
+
style: { flexDirection: "column" },
|
|
3339
|
+
onMouseOver: active ? () => {
|
|
3340
|
+
if (targetTurn === selectedTurnId) return;
|
|
3341
|
+
if (onSelectTurn?.(targetTurn) !== false) mouseSelectionRef.current = true;
|
|
3342
|
+
} : void 0,
|
|
3343
|
+
onMouseDown: active && onOpenTurn ? (event) => {
|
|
3344
|
+
pressedTurnRef.current = event.button === 0 ? {
|
|
3345
|
+
turnId: targetTurn,
|
|
3346
|
+
x: event.x,
|
|
3347
|
+
y: event.y
|
|
3348
|
+
} : null;
|
|
3349
|
+
} : void 0,
|
|
3350
|
+
onMouseUp: active && onOpenTurn ? (event) => {
|
|
3351
|
+
const pressed = pressedTurnRef.current;
|
|
3352
|
+
pressedTurnRef.current = null;
|
|
3353
|
+
if (event.button !== 0 || pressed === null || pressed.turnId !== targetTurn) return;
|
|
3354
|
+
if (pressed.x !== event.x || pressed.y !== event.y) return;
|
|
3355
|
+
event.stopPropagation();
|
|
3356
|
+
event.preventDefault();
|
|
3357
|
+
if (onOpenTurn(targetTurn) !== false && targetTurn !== selectedTurnId) mouseSelectionRef.current = true;
|
|
3358
|
+
} : void 0,
|
|
3359
|
+
children: line
|
|
3110
3360
|
}, i);
|
|
3111
3361
|
}
|
|
3112
3362
|
return /* @__PURE__ */ jsx$1(SubagentBlock, {
|
|
@@ -3254,52 +3504,6 @@ function partitionTranscript(events, settings, searchReveal) {
|
|
|
3254
3504
|
return items;
|
|
3255
3505
|
}
|
|
3256
3506
|
/**
|
|
3257
|
-
* Bordered container for one run of subagent events. The box's border +
|
|
3258
|
-
* left padding give the visual "this is a subagent" affordance, so events
|
|
3259
|
-
* inside render with `depthOffset: 1` — a direct child of the parent
|
|
3260
|
-
* (depth 1) sits flush against the box's inner padding rather than being
|
|
3261
|
-
* indented twice. Grandchildren (depth ≥ 2) still indent further, so
|
|
3262
|
-
* nested subagents remain visually distinct.
|
|
3263
|
-
*/
|
|
3264
|
-
function SubagentBlock({ events, previous, selectedTurnId = null, anchorIds, codeSettleTargets, codeSettleEpoch = 0, live, searchQuery, searchTitleMatches }) {
|
|
3265
|
-
const COLOR = useColors();
|
|
3266
|
-
const childIds = useMemo(() => {
|
|
3267
|
-
const set = /* @__PURE__ */ new Set();
|
|
3268
|
-
for (const e of events) if (e.childId) set.add(e.childId);
|
|
3269
|
-
return Array.from(set);
|
|
3270
|
-
}, [events]);
|
|
3271
|
-
const title = childIds.length === 0 ? " subagent " : childIds.length === 1 ? ` ${childIds[0]} ` : ` subagents · ${childIds.join(", ")} `;
|
|
3272
|
-
const marginTop = previous ? 1 : 0;
|
|
3273
|
-
const hideChildLabel = childIds.length === 1;
|
|
3274
|
-
return /* @__PURE__ */ jsx$1("box", {
|
|
3275
|
-
title,
|
|
3276
|
-
style: {
|
|
3277
|
-
border: true,
|
|
3278
|
-
borderColor: COLOR.brand,
|
|
3279
|
-
paddingLeft: 1,
|
|
3280
|
-
paddingRight: 1,
|
|
3281
|
-
paddingTop: 0,
|
|
3282
|
-
paddingBottom: 0,
|
|
3283
|
-
marginTop,
|
|
3284
|
-
flexDirection: "column",
|
|
3285
|
-
flexShrink: 0,
|
|
3286
|
-
alignSelf: "stretch"
|
|
3287
|
-
},
|
|
3288
|
-
children: events.map((evt, i) => /* @__PURE__ */ jsx$1(EventLine, {
|
|
3289
|
-
event: evt,
|
|
3290
|
-
previous: events[i - 1],
|
|
3291
|
-
depthOffset: 1,
|
|
3292
|
-
selected: selectedTurnId !== null && evt.turnId === selectedTurnId,
|
|
3293
|
-
anchorId: anchorIds?.[i],
|
|
3294
|
-
hideChildLabel,
|
|
3295
|
-
settleCodeEpoch: codeSettleTargets?.has(evt) ? codeSettleEpoch : 0,
|
|
3296
|
-
liveSpawn: evt.kind === "spawn-start" ? live?.find((spawn) => spawn.id === evt.childId) : void 0,
|
|
3297
|
-
searchQuery,
|
|
3298
|
-
searchTitleMatch: evt.kind === "tool" && evt.callId !== void 0 && searchTitleMatches?.callIds.has(evt.callId) || (evt.kind === "spawn-start" || evt.kind === "spawn-end") && evt.childId !== void 0 && searchTitleMatches?.childIds.has(evt.childId)
|
|
3299
|
-
}, i))
|
|
3300
|
-
});
|
|
3301
|
-
}
|
|
3302
|
-
/**
|
|
3303
3507
|
* Stable one-row subagent lifecycle layout. Identity/model/task stay in
|
|
3304
3508
|
* the left lane for the whole run; only the right tail changes:
|
|
3305
3509
|
* active `2 turns · 14s · ⠋ tool call`, done `4 turns`.
|
|
@@ -4919,29 +5123,23 @@ function ContextPanelModal({ breakdown, modelUsage }) {
|
|
|
4919
5123
|
children: "~"
|
|
4920
5124
|
}), " estimated — exact counts unavailable for this provider"]
|
|
4921
5125
|
}),
|
|
4922
|
-
/* @__PURE__ */ jsx$1(
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
" close"
|
|
4940
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx$1("span", {
|
|
4941
|
-
fg: COLOR.warn,
|
|
4942
|
-
children: "esc"
|
|
4943
|
-
}), " close"] })
|
|
4944
|
-
})
|
|
5126
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
5127
|
+
expandableIds.length > 0 && {
|
|
5128
|
+
key: "↑↓",
|
|
5129
|
+
label: "navigate",
|
|
5130
|
+
labelColor: COLOR.dim
|
|
5131
|
+
},
|
|
5132
|
+
expandableIds.length > 0 && {
|
|
5133
|
+
key: "↵/space",
|
|
5134
|
+
label: "expand",
|
|
5135
|
+
labelColor: COLOR.dim
|
|
5136
|
+
},
|
|
5137
|
+
{
|
|
5138
|
+
key: "esc",
|
|
5139
|
+
label: "close",
|
|
5140
|
+
labelColor: COLOR.dim
|
|
5141
|
+
}
|
|
5142
|
+
] })
|
|
4945
5143
|
]
|
|
4946
5144
|
});
|
|
4947
5145
|
}
|
|
@@ -5450,26 +5648,23 @@ function CreateExtensionModal({ userRoot, projectRoot, loadProjectExtensions, on
|
|
|
5450
5648
|
fg: COLOR.error,
|
|
5451
5649
|
children: error
|
|
5452
5650
|
}),
|
|
5453
|
-
/* @__PURE__ */
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
" cancel"
|
|
5471
|
-
]
|
|
5472
|
-
})
|
|
5651
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
5652
|
+
{
|
|
5653
|
+
key: "tab",
|
|
5654
|
+
label: "scope",
|
|
5655
|
+
labelColor: COLOR.dim
|
|
5656
|
+
},
|
|
5657
|
+
{
|
|
5658
|
+
key: "↵",
|
|
5659
|
+
label: "create",
|
|
5660
|
+
labelColor: COLOR.dim
|
|
5661
|
+
},
|
|
5662
|
+
{
|
|
5663
|
+
key: "esc",
|
|
5664
|
+
label: "cancel",
|
|
5665
|
+
labelColor: COLOR.dim
|
|
5666
|
+
}
|
|
5667
|
+
] })
|
|
5473
5668
|
]
|
|
5474
5669
|
});
|
|
5475
5670
|
}
|
|
@@ -5778,40 +5973,37 @@ function CwdPickerModal({ currentCwd, onPick }) {
|
|
|
5778
5973
|
}, result.item.path);
|
|
5779
5974
|
})
|
|
5780
5975
|
}),
|
|
5781
|
-
/* @__PURE__ */
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
})
|
|
5813
|
-
]
|
|
5814
|
-
})
|
|
5976
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
5977
|
+
{
|
|
5978
|
+
key: "↑↓",
|
|
5979
|
+
label: "navigate",
|
|
5980
|
+
labelColor: COLOR.dim
|
|
5981
|
+
},
|
|
5982
|
+
{
|
|
5983
|
+
key: "tab",
|
|
5984
|
+
label: "enter dir",
|
|
5985
|
+
labelColor: COLOR.dim
|
|
5986
|
+
},
|
|
5987
|
+
{
|
|
5988
|
+
key: "←",
|
|
5989
|
+
label: "parent",
|
|
5990
|
+
labelColor: COLOR.dim
|
|
5991
|
+
},
|
|
5992
|
+
{
|
|
5993
|
+
key: "↵",
|
|
5994
|
+
label: "select",
|
|
5995
|
+
labelColor: COLOR.dim
|
|
5996
|
+
},
|
|
5997
|
+
{
|
|
5998
|
+
key: "esc",
|
|
5999
|
+
label: "close",
|
|
6000
|
+
labelColor: COLOR.dim
|
|
6001
|
+
},
|
|
6002
|
+
{
|
|
6003
|
+
key: "",
|
|
6004
|
+
label: `${results.length} match${results.length === 1 ? "" : "es"}`
|
|
6005
|
+
}
|
|
6006
|
+
] })
|
|
5815
6007
|
]
|
|
5816
6008
|
});
|
|
5817
6009
|
}
|
|
@@ -6098,6 +6290,21 @@ async function resolveExtensionModalNode(node) {
|
|
|
6098
6290
|
let nextClientId = 1;
|
|
6099
6291
|
/** Mirror of the TUI's ZIDANE_DEBUG-gated logger — silent by default. */
|
|
6100
6292
|
const debugFallback = process.env.ZIDANE_DEBUG ? (op, error) => console.error(`[zidane/tui] history search ${op} fell back to the main thread:`, error) : () => {};
|
|
6293
|
+
/**
|
|
6294
|
+
* Rough searchable-size estimate for degraded builds — string block
|
|
6295
|
+
* payloads only, mirroring what index extraction keeps (attachments /
|
|
6296
|
+
* base64 are stripped there). Cheap enough to run inline; only feeds the
|
|
6297
|
+
* byte budget, so precision doesn't matter.
|
|
6298
|
+
*/
|
|
6299
|
+
function estimateTurnsBytes(turns) {
|
|
6300
|
+
let total = 0;
|
|
6301
|
+
for (const turn of turns) for (const block of turn.content) {
|
|
6302
|
+
const b = block;
|
|
6303
|
+
if (typeof b.text === "string") total += b.text.length;
|
|
6304
|
+
if (typeof b.output === "string") total += b.output.length;
|
|
6305
|
+
}
|
|
6306
|
+
return total;
|
|
6307
|
+
}
|
|
6101
6308
|
function workerUrl() {
|
|
6102
6309
|
if (typeof Bun !== "undefined" && Bun.isStandaloneExecutable) return new URL("./history-search-worker.ts", import.meta.url);
|
|
6103
6310
|
return new URL(import.meta.url.endsWith(".ts") ? "./history-search-worker.ts" : "./history-search-worker.js", import.meta.url);
|
|
@@ -6112,6 +6319,12 @@ var HistorySearchWorkerClient = class {
|
|
|
6112
6319
|
this.createWorker = createWorker;
|
|
6113
6320
|
}
|
|
6114
6321
|
indexes = /* @__PURE__ */ new WeakMap();
|
|
6322
|
+
/**
|
|
6323
|
+
* Handles whose index lives in the worker (a successful `build`). Only
|
|
6324
|
+
* these are safe to `slim()` — a degraded handle's turns are the sole
|
|
6325
|
+
* source for its main-thread fallback index.
|
|
6326
|
+
*/
|
|
6327
|
+
workerBuilt = /* @__PURE__ */ new WeakSet();
|
|
6115
6328
|
registrations = /* @__PURE__ */ new Map();
|
|
6116
6329
|
rebuilds = /* @__PURE__ */ new Map();
|
|
6117
6330
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -6129,10 +6342,11 @@ var HistorySearchWorkerClient = class {
|
|
|
6129
6342
|
if (this.unavailable) return {
|
|
6130
6343
|
indexId,
|
|
6131
6344
|
length: turns.length,
|
|
6345
|
+
bytes: estimateTurnsBytes(turns),
|
|
6132
6346
|
turns
|
|
6133
6347
|
};
|
|
6134
6348
|
try {
|
|
6135
|
-
|
|
6349
|
+
const handle = {
|
|
6136
6350
|
...await this.request({
|
|
6137
6351
|
type: "build",
|
|
6138
6352
|
indexId,
|
|
@@ -6140,16 +6354,47 @@ var HistorySearchWorkerClient = class {
|
|
|
6140
6354
|
}, signal),
|
|
6141
6355
|
turns
|
|
6142
6356
|
};
|
|
6357
|
+
this.workerBuilt.add(handle);
|
|
6358
|
+
return handle;
|
|
6143
6359
|
} catch (error) {
|
|
6144
6360
|
if (signal?.aborted) throw signal.reason;
|
|
6145
6361
|
debugFallback("build", error);
|
|
6146
6362
|
return {
|
|
6147
6363
|
indexId,
|
|
6148
6364
|
length: turns.length,
|
|
6365
|
+
bytes: estimateTurnsBytes(turns),
|
|
6149
6366
|
turns
|
|
6150
6367
|
};
|
|
6151
6368
|
}
|
|
6152
6369
|
}
|
|
6370
|
+
/**
|
|
6371
|
+
* Drop the handle's main-thread turn retention once the worker owns the
|
|
6372
|
+
* index. Callers whose turns are alive anyway (live session, transcript
|
|
6373
|
+
* search) skip this; the cross-session cache calls it so 64 cached
|
|
6374
|
+
* handles don't pin 64 full sessions on the main thread. A slimmed
|
|
6375
|
+
* handle can't rebuild after a worker-side eviction — the caller is
|
|
6376
|
+
* expected to self-heal by rebuilding from the store on failure.
|
|
6377
|
+
*/
|
|
6378
|
+
slim(index) {
|
|
6379
|
+
if (!this.workerBuilt.has(index)) return;
|
|
6380
|
+
index.turns = void 0;
|
|
6381
|
+
}
|
|
6382
|
+
/**
|
|
6383
|
+
* Main-thread eviction notice: free the handle's retained data AND the
|
|
6384
|
+
* worker's cached index. Without this the worker kept its copy until
|
|
6385
|
+
* its own LRU happened to cycle it out. Fire-and-forget by design.
|
|
6386
|
+
*/
|
|
6387
|
+
release(index) {
|
|
6388
|
+
index.turns = void 0;
|
|
6389
|
+
index.fallback = void 0;
|
|
6390
|
+
if (!this.worker) return;
|
|
6391
|
+
try {
|
|
6392
|
+
this.worker.postMessage({
|
|
6393
|
+
type: "release",
|
|
6394
|
+
indexId: index.indexId
|
|
6395
|
+
});
|
|
6396
|
+
} catch {}
|
|
6397
|
+
}
|
|
6153
6398
|
async search(index, query, limit, signal) {
|
|
6154
6399
|
if (this.unavailable) return searchSessionIndexAsync(await this.fallbackIndex(index, signal), query, {
|
|
6155
6400
|
limit,
|
|
@@ -6219,6 +6464,7 @@ var HistorySearchWorkerClient = class {
|
|
|
6219
6464
|
rebuild(index) {
|
|
6220
6465
|
const inFlight = this.rebuilds.get(index.indexId);
|
|
6221
6466
|
if (inFlight) return inFlight;
|
|
6467
|
+
if (!index.turns) return Promise.reject(/* @__PURE__ */ new Error(`History search index released: ${index.indexId}`));
|
|
6222
6468
|
const rebuild = this.request({
|
|
6223
6469
|
type: "build",
|
|
6224
6470
|
indexId: index.indexId,
|
|
@@ -6229,10 +6475,13 @@ var HistorySearchWorkerClient = class {
|
|
|
6229
6475
|
}
|
|
6230
6476
|
fallbackIndex(index, signal) {
|
|
6231
6477
|
if (!("indexId" in index)) return Promise.resolve(index);
|
|
6232
|
-
if (!index.fallback)
|
|
6233
|
-
index.
|
|
6234
|
-
|
|
6235
|
-
|
|
6478
|
+
if (!index.fallback) {
|
|
6479
|
+
if (!index.turns) return Promise.reject(/* @__PURE__ */ new Error(`History search index released: ${index.indexId}`));
|
|
6480
|
+
index.fallback = buildSessionSearchIndexAsync(index.turns).catch((error) => {
|
|
6481
|
+
index.fallback = void 0;
|
|
6482
|
+
throw error;
|
|
6483
|
+
});
|
|
6484
|
+
}
|
|
6236
6485
|
signal?.throwIfAborted();
|
|
6237
6486
|
return index.fallback;
|
|
6238
6487
|
}
|
|
@@ -6288,6 +6537,10 @@ var HistorySearchWorkerClient = class {
|
|
|
6288
6537
|
}
|
|
6289
6538
|
});
|
|
6290
6539
|
this.registrations.set(indexId, registration);
|
|
6540
|
+
registration.catch(() => {
|
|
6541
|
+
this.registrations.delete(indexId);
|
|
6542
|
+
this.indexes.delete(index);
|
|
6543
|
+
});
|
|
6291
6544
|
return registration.then(() => indexId);
|
|
6292
6545
|
}
|
|
6293
6546
|
request(payload, signal) {
|
|
@@ -6658,26 +6911,23 @@ function ModelOptionsPickerModal({ supportsReasoning, supportsAdaptive, currentE
|
|
|
6658
6911
|
})]
|
|
6659
6912
|
}, `option:${row.option.id}`);
|
|
6660
6913
|
})]
|
|
6661
|
-
}), /* @__PURE__ */
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
" close"
|
|
6679
|
-
]
|
|
6680
|
-
})]
|
|
6914
|
+
}), /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
6915
|
+
{
|
|
6916
|
+
key: "↑↓",
|
|
6917
|
+
label: "navigate",
|
|
6918
|
+
labelColor: COLOR.dim
|
|
6919
|
+
},
|
|
6920
|
+
{
|
|
6921
|
+
key: "↵/space",
|
|
6922
|
+
label: "select/toggle",
|
|
6923
|
+
labelColor: COLOR.dim
|
|
6924
|
+
},
|
|
6925
|
+
{
|
|
6926
|
+
key: "esc",
|
|
6927
|
+
label: "close",
|
|
6928
|
+
labelColor: COLOR.dim
|
|
6929
|
+
}
|
|
6930
|
+
] })]
|
|
6681
6931
|
});
|
|
6682
6932
|
}
|
|
6683
6933
|
function SectionHeader({ label, color }) {
|
|
@@ -6998,38 +7248,37 @@ function ModelPickerModal({ providers, modelsFor, current, onPick, chain, onAppl
|
|
|
6998
7248
|
chainPosition: chainMode ? chainPositionOf(chainState, entry.providerKey, entry.model.id) : null
|
|
6999
7249
|
}, `${entry.providerKey}:${entry.model.id}`))
|
|
7000
7250
|
}),
|
|
7001
|
-
/* @__PURE__ */
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
7031
|
-
|
|
7032
|
-
})
|
|
7251
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
7252
|
+
{
|
|
7253
|
+
key: "↑↓",
|
|
7254
|
+
label: "navigate",
|
|
7255
|
+
labelColor: COLOR.dim
|
|
7256
|
+
},
|
|
7257
|
+
{
|
|
7258
|
+
key: "↵",
|
|
7259
|
+
label: chainMode ? "add/remove" : "select",
|
|
7260
|
+
labelColor: COLOR.dim
|
|
7261
|
+
},
|
|
7262
|
+
chainMode && {
|
|
7263
|
+
key: "ctrl+shift+↑↓",
|
|
7264
|
+
label: "reorder",
|
|
7265
|
+
labelColor: COLOR.dim
|
|
7266
|
+
},
|
|
7267
|
+
{
|
|
7268
|
+
key: "esc",
|
|
7269
|
+
label: chainMode ? "apply" : "close",
|
|
7270
|
+
labelColor: COLOR.dim
|
|
7271
|
+
},
|
|
7272
|
+
chainMode && {
|
|
7273
|
+
key: "",
|
|
7274
|
+
label: `${chainState.length} selected`,
|
|
7275
|
+
labelColor: COLOR.brand
|
|
7276
|
+
},
|
|
7277
|
+
{
|
|
7278
|
+
key: "",
|
|
7279
|
+
label: `${filtered.length} / ${catalog.length} model${catalog.length === 1 ? "" : "s"}`
|
|
7280
|
+
}
|
|
7281
|
+
] })
|
|
7033
7282
|
]
|
|
7034
7283
|
});
|
|
7035
7284
|
}
|
|
@@ -7190,38 +7439,37 @@ function ModelSelectList({ catalog, multi = false, initial, onApply, onCancel, v
|
|
|
7190
7439
|
chainPosition: multi ? picks.includes(entry.model.id) ? picks.indexOf(entry.model.id) + 1 : null : null
|
|
7191
7440
|
}, `${entry.providerKey}:${entry.model.id}`))
|
|
7192
7441
|
}),
|
|
7193
|
-
/* @__PURE__ */
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
})
|
|
7442
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
7443
|
+
{
|
|
7444
|
+
key: "↑↓",
|
|
7445
|
+
label: "navigate",
|
|
7446
|
+
labelColor: COLOR.dim
|
|
7447
|
+
},
|
|
7448
|
+
{
|
|
7449
|
+
key: "↵",
|
|
7450
|
+
label: multi ? "add/remove" : "select",
|
|
7451
|
+
labelColor: COLOR.dim
|
|
7452
|
+
},
|
|
7453
|
+
multi && {
|
|
7454
|
+
key: "ctrl+shift+↑↓",
|
|
7455
|
+
label: "reorder",
|
|
7456
|
+
labelColor: COLOR.dim
|
|
7457
|
+
},
|
|
7458
|
+
{
|
|
7459
|
+
key: "esc",
|
|
7460
|
+
label: multi ? "apply" : "cancel",
|
|
7461
|
+
labelColor: COLOR.dim
|
|
7462
|
+
},
|
|
7463
|
+
multi && {
|
|
7464
|
+
key: "",
|
|
7465
|
+
label: `${picks.length} selected`,
|
|
7466
|
+
labelColor: COLOR.brand
|
|
7467
|
+
},
|
|
7468
|
+
{
|
|
7469
|
+
key: "",
|
|
7470
|
+
label: `${filtered.length} / ${deduped.length} model${deduped.length === 1 ? "" : "s"}`
|
|
7471
|
+
}
|
|
7472
|
+
] })
|
|
7225
7473
|
]
|
|
7226
7474
|
});
|
|
7227
7475
|
}
|
|
@@ -7768,33 +8016,27 @@ function CompletionPopup({ state, visibleRows = 6 }) {
|
|
|
7768
8016
|
})
|
|
7769
8017
|
}, item.id);
|
|
7770
8018
|
})
|
|
7771
|
-
}), /* @__PURE__ */
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
fg: COLOR.warn,
|
|
7793
|
-
children: "esc"
|
|
7794
|
-
}),
|
|
7795
|
-
" close"
|
|
7796
|
-
]
|
|
7797
|
-
})] });
|
|
8019
|
+
}), /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
8020
|
+
{
|
|
8021
|
+
key: "↑↓",
|
|
8022
|
+
label: "navigate"
|
|
8023
|
+
},
|
|
8024
|
+
{
|
|
8025
|
+
key: enterSelects ? "↵" : "tab",
|
|
8026
|
+
label: "select"
|
|
8027
|
+
},
|
|
8028
|
+
enterSelects ? {
|
|
8029
|
+
key: "tab",
|
|
8030
|
+
label: "scope"
|
|
8031
|
+
} : {
|
|
8032
|
+
key: "↵",
|
|
8033
|
+
label: "submit"
|
|
8034
|
+
},
|
|
8035
|
+
{
|
|
8036
|
+
key: "esc",
|
|
8037
|
+
label: "close"
|
|
8038
|
+
}
|
|
8039
|
+
] })] });
|
|
7798
8040
|
}
|
|
7799
8041
|
return /* @__PURE__ */ jsxs("box", {
|
|
7800
8042
|
style: {
|
|
@@ -9919,11 +10161,11 @@ function toneColor(tone, COLOR) {
|
|
|
9919
10161
|
* the parent picker's `useKeyboard` (via a ref) to suppress single-key
|
|
9920
10162
|
* shortcuts (`l` / `o` / `r`) while the user is typing into the input.
|
|
9921
10163
|
*/
|
|
9922
|
-
function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, inputFocused }) {
|
|
10164
|
+
function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, inputFocused, fill = false }) {
|
|
9923
10165
|
const COLOR = useColors();
|
|
9924
10166
|
const inputRef = useRef(null);
|
|
9925
10167
|
const [hint, setHint] = useState(null);
|
|
9926
|
-
const status = hint && hint.tone !== "error" ? hint.text : "complete the login in your browser — waiting for the callback…";
|
|
10168
|
+
const status = hint && hint.tone !== "error" ? hint.text : authUrl === null ? "starting login flow…" : "complete the login in your browser — waiting for the callback…";
|
|
9927
10169
|
const onSubmit = useCallback(() => {
|
|
9928
10170
|
const value = inputRef.current?.value?.trim() ?? "";
|
|
9929
10171
|
if (!value) return;
|
|
@@ -9953,7 +10195,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9953
10195
|
return /* @__PURE__ */ jsxs("box", {
|
|
9954
10196
|
style: {
|
|
9955
10197
|
flexDirection: "column",
|
|
9956
|
-
flexShrink: 0
|
|
10198
|
+
...fill ? { flexGrow: 1 } : { flexShrink: 0 }
|
|
9957
10199
|
},
|
|
9958
10200
|
children: [/* @__PURE__ */ jsxs("box", {
|
|
9959
10201
|
style: {
|
|
@@ -9961,11 +10203,12 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9961
10203
|
border: true,
|
|
9962
10204
|
borderColor: COLOR.border,
|
|
9963
10205
|
padding: 1,
|
|
9964
|
-
gap: 1
|
|
10206
|
+
gap: 1,
|
|
10207
|
+
...fill ? { flexGrow: 1 } : {}
|
|
9965
10208
|
},
|
|
9966
10209
|
children: [
|
|
9967
10210
|
/* @__PURE__ */ jsx$1(Spinner, { label: status }),
|
|
9968
|
-
/* @__PURE__ */ jsx$1(OAuthAuthBlock, {
|
|
10211
|
+
authUrl !== null && /* @__PURE__ */ jsx$1(OAuthAuthBlock, {
|
|
9969
10212
|
authUrl,
|
|
9970
10213
|
maxLineWidth,
|
|
9971
10214
|
chromeWidth: (chromeWidth ?? 14) + 4,
|
|
@@ -11347,7 +11590,7 @@ const EMPTY_QUEUED_MESSAGES = [];
|
|
|
11347
11590
|
function shouldRenderTranscriptSearch(searchMode, query) {
|
|
11348
11591
|
return searchMode && query.trim().length > 0;
|
|
11349
11592
|
}
|
|
11350
|
-
function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EMPTY_QUEUED_MESSAGES, queueSelectionIndex = null, queueShortcuts, onEnterQueueFromEmptyPrompt, settings, onSubmit, session, pending, onApproval, pendingInteraction, onInteraction, completionProviders, inlineAutocompleteProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectedTurnId, transcriptSearchMode = false, transcriptSearchQuery = "", transcriptSearchEvents = [], transcriptSearchSearching = false, onTranscriptSearchModeChange, onTranscriptSearchQueryChange, onTranscriptSearchNavigate, onTranscriptSearchOpen, onLoadEarlierTranscript, promptTriggerHints, attachImageBinding, editorBinding, sessionDetailsBinding, liveSession = null }) {
|
|
11593
|
+
function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EMPTY_QUEUED_MESSAGES, queueSelectionIndex = null, queueShortcuts, onEnterQueueFromEmptyPrompt, settings, onSubmit, session, pending, onApproval, pendingInteraction, onInteraction, completionProviders, inlineAutocompleteProviders, onCompletionTab, enterSelectsCompletion, onPopupOpenChange, selectedTurnId, onSelectTurn, onOpenTurn, transcriptSearchMode = false, transcriptSearchQuery = "", transcriptSearchEvents = [], transcriptSearchSearching = false, onTranscriptSearchModeChange, onTranscriptSearchQueryChange, onTranscriptSearchNavigate, onTranscriptSearchOpen, onLoadEarlierTranscript, promptTriggerHints, attachImageBinding, editorBinding, sessionDetailsBinding, liveSession = null }) {
|
|
11351
11594
|
const COLOR = useColors();
|
|
11352
11595
|
const titleText = session?.title ?? "New session";
|
|
11353
11596
|
const sessionShortcutKey = formatBindingForDisplay(sessionDetailsBinding);
|
|
@@ -11437,6 +11680,8 @@ function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EM
|
|
|
11437
11680
|
events: displayedEvents,
|
|
11438
11681
|
settings,
|
|
11439
11682
|
selectedTurnId: selectedTurnId ?? null,
|
|
11683
|
+
onSelectTurn,
|
|
11684
|
+
onOpenTurn,
|
|
11440
11685
|
searchQuery: transcriptSearchActive ? transcriptSearchQuery : void 0,
|
|
11441
11686
|
searchPending: transcriptSearchSearching,
|
|
11442
11687
|
busy: !transcriptSearchActive && busy && !pending && !pendingInteraction,
|
|
@@ -11485,7 +11730,8 @@ function ChatScreen({ cwd, events, busy, compacting = false, queuedMessages = EM
|
|
|
11485
11730
|
] }),
|
|
11486
11731
|
/* @__PURE__ */ jsx$1(TitleOverlay, {
|
|
11487
11732
|
title: titleText,
|
|
11488
|
-
meta: metaSegments
|
|
11733
|
+
meta: metaSegments,
|
|
11734
|
+
clickKey: sessionDetailsBinding ?? null
|
|
11489
11735
|
})
|
|
11490
11736
|
]
|
|
11491
11737
|
});
|
|
@@ -13159,81 +13405,64 @@ function ActionRow$2({ pending, copyStatus, titleStatus, titleError, hasGenerate
|
|
|
13159
13405
|
})]
|
|
13160
13406
|
});
|
|
13161
13407
|
}
|
|
13162
|
-
if (copyStatus === "copied") return /* @__PURE__ */
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13202
|
-
|
|
13203
|
-
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
13221
|
-
fg: COLOR.warn,
|
|
13222
|
-
children: keys.delete
|
|
13223
|
-
}),
|
|
13224
|
-
" delete · ",
|
|
13225
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
13226
|
-
fg: COLOR.warn,
|
|
13227
|
-
children: keys.copy
|
|
13228
|
-
}),
|
|
13229
|
-
" copy · ",
|
|
13230
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
13231
|
-
fg: COLOR.warn,
|
|
13232
|
-
children: "esc"
|
|
13233
|
-
}),
|
|
13234
|
-
" close"
|
|
13235
|
-
]
|
|
13236
|
-
});
|
|
13408
|
+
if (copyStatus === "copied") return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
13409
|
+
{
|
|
13410
|
+
key: "",
|
|
13411
|
+
label: "✓ session id copied",
|
|
13412
|
+
labelColor: COLOR.accent
|
|
13413
|
+
},
|
|
13414
|
+
{
|
|
13415
|
+
key: keys.delete,
|
|
13416
|
+
label: "delete",
|
|
13417
|
+
labelColor: COLOR.dim
|
|
13418
|
+
},
|
|
13419
|
+
{
|
|
13420
|
+
key: "esc",
|
|
13421
|
+
label: "close",
|
|
13422
|
+
labelColor: COLOR.dim
|
|
13423
|
+
}
|
|
13424
|
+
] });
|
|
13425
|
+
if (copyStatus === "failed") return /* @__PURE__ */ jsx$1(ModalHints, { hints: [{
|
|
13426
|
+
key: "",
|
|
13427
|
+
label: "copy failed (terminal may not support OSC 52)",
|
|
13428
|
+
labelColor: COLOR.error
|
|
13429
|
+
}, {
|
|
13430
|
+
key: "esc",
|
|
13431
|
+
label: "close",
|
|
13432
|
+
labelColor: COLOR.dim
|
|
13433
|
+
}] });
|
|
13434
|
+
return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
13435
|
+
hasGenerate && {
|
|
13436
|
+
key: keys.generate,
|
|
13437
|
+
label: "title",
|
|
13438
|
+
labelColor: COLOR.dim
|
|
13439
|
+
},
|
|
13440
|
+
hasExport && {
|
|
13441
|
+
key: `${keys.markdown}/${keys.json}`,
|
|
13442
|
+
label: "export",
|
|
13443
|
+
labelColor: COLOR.dim
|
|
13444
|
+
},
|
|
13445
|
+
hasCompact && {
|
|
13446
|
+
key: keys.compact,
|
|
13447
|
+
label: "compact",
|
|
13448
|
+
labelColor: COLOR.dim
|
|
13449
|
+
},
|
|
13450
|
+
{
|
|
13451
|
+
key: keys.delete,
|
|
13452
|
+
label: "delete",
|
|
13453
|
+
labelColor: COLOR.dim
|
|
13454
|
+
},
|
|
13455
|
+
{
|
|
13456
|
+
key: keys.copy,
|
|
13457
|
+
label: "copy",
|
|
13458
|
+
labelColor: COLOR.dim
|
|
13459
|
+
},
|
|
13460
|
+
{
|
|
13461
|
+
key: "esc",
|
|
13462
|
+
label: "close",
|
|
13463
|
+
labelColor: COLOR.dim
|
|
13464
|
+
}
|
|
13465
|
+
] });
|
|
13237
13466
|
}
|
|
13238
13467
|
/**
|
|
13239
13468
|
* Sum token + cost figures across every {@link SessionRun}. Preference
|
|
@@ -14852,10 +15081,18 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
14852
15081
|
useEffect(() => {
|
|
14853
15082
|
setMcpRemoveArmed(null);
|
|
14854
15083
|
}, [focusedMcp?.config.name, activeTab]);
|
|
14855
|
-
const
|
|
15084
|
+
const authorizingMcp = activeTab === "mcps" ? mcpsCatalog.map((entry) => ({
|
|
15085
|
+
entry,
|
|
15086
|
+
status: getMcpAuthStatus(authState, entry.config.name)
|
|
15087
|
+
})).find(({ status }) => status.kind === "authorizing") : void 0;
|
|
15088
|
+
const oauthPasteActive = !!authorizingMcp && authorizingMcp.status.kind === "authorizing" && !!authorizingMcp.status.url;
|
|
14856
15089
|
useEffect(() => {
|
|
14857
|
-
if (!oauthPasteActive && !editingInputKey) inputRef.current?.focus();
|
|
14858
|
-
}, [
|
|
15090
|
+
if (!oauthPasteActive && !editingInputKey && !authorizingMcp) inputRef.current?.focus();
|
|
15091
|
+
}, [
|
|
15092
|
+
oauthPasteActive,
|
|
15093
|
+
editingInputKey,
|
|
15094
|
+
authorizingMcp
|
|
15095
|
+
]);
|
|
14859
15096
|
useEffect(() => {
|
|
14860
15097
|
if (editingInputKey) editInputRef.current?.focus();
|
|
14861
15098
|
}, [editingInputKey]);
|
|
@@ -15008,8 +15245,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15008
15245
|
}
|
|
15009
15246
|
if (extInstallActive || skillInstallActive || skillImportAllActive) return;
|
|
15010
15247
|
if (mcpAddActive) return;
|
|
15011
|
-
if (key.name === "escape" && oauthPasteActive &&
|
|
15012
|
-
actions?.onCancelLoginMcp?.(
|
|
15248
|
+
if (key.name === "escape" && oauthPasteActive && authorizingMcp) {
|
|
15249
|
+
actions?.onCancelLoginMcp?.(authorizingMcp.entry.config.name);
|
|
15013
15250
|
return;
|
|
15014
15251
|
}
|
|
15015
15252
|
if (oauthPasteActive) return;
|
|
@@ -15147,7 +15384,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15147
15384
|
actions?.onLogoutMcp?.(focusedMcp.config.name);
|
|
15148
15385
|
return;
|
|
15149
15386
|
}
|
|
15150
|
-
if (key.name === "escape" &&
|
|
15387
|
+
if (key.name === "escape" && authorizingMcp) actions?.onCancelLoginMcp?.(authorizingMcp.entry.config.name);
|
|
15151
15388
|
}
|
|
15152
15389
|
if (key.ctrl && !key.shift && key.name === "l" && activeTab === "logs") {
|
|
15153
15390
|
key.preventDefault();
|
|
@@ -15272,7 +15509,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15272
15509
|
onSelect: clickTab
|
|
15273
15510
|
})
|
|
15274
15511
|
}),
|
|
15275
|
-
/* @__PURE__ */ jsx$1("box", {
|
|
15512
|
+
!authorizingMcp && /* @__PURE__ */ jsx$1("box", {
|
|
15276
15513
|
style: {
|
|
15277
15514
|
border: true,
|
|
15278
15515
|
borderColor: COLOR.borderActive,
|
|
@@ -15290,7 +15527,19 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15290
15527
|
style: { flexGrow: 1 }
|
|
15291
15528
|
})
|
|
15292
15529
|
}),
|
|
15293
|
-
/* @__PURE__ */
|
|
15530
|
+
authorizingMcp ? /* @__PURE__ */ jsx$1("box", {
|
|
15531
|
+
style: {
|
|
15532
|
+
flexGrow: 1,
|
|
15533
|
+
flexDirection: "column"
|
|
15534
|
+
},
|
|
15535
|
+
children: /* @__PURE__ */ jsx$1(McpAuthorizingPanel, {
|
|
15536
|
+
serverName: authorizingMcp.entry.config.name,
|
|
15537
|
+
authUrl: authorizingMcp.status.kind === "authorizing" ? authorizingMcp.status.url ?? null : null,
|
|
15538
|
+
maxLineWidth: 154,
|
|
15539
|
+
inputFocused: true,
|
|
15540
|
+
fill: true
|
|
15541
|
+
})
|
|
15542
|
+
}) : /* @__PURE__ */ jsxs("scrollbox", {
|
|
15294
15543
|
ref: scrollboxRef,
|
|
15295
15544
|
focusable: false,
|
|
15296
15545
|
style: {
|
|
@@ -15385,7 +15634,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15385
15634
|
highlightBg: SURFACE.selection
|
|
15386
15635
|
})
|
|
15387
15636
|
}),
|
|
15388
|
-
activeTab === "mcps" && focusedMcp && focusedMcpStatus && /* @__PURE__ */ jsx$1("box", {
|
|
15637
|
+
activeTab === "mcps" && !authorizingMcp && focusedMcp && focusedMcpStatus && /* @__PURE__ */ jsx$1("box", {
|
|
15389
15638
|
style: { flexShrink: 0 },
|
|
15390
15639
|
children: renderMcpDetailPanel(focusedMcp, focusedMcpStatus, COLOR)
|
|
15391
15640
|
}),
|
|
@@ -15455,27 +15704,27 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
15455
15704
|
style: { flexGrow: 1 }
|
|
15456
15705
|
})
|
|
15457
15706
|
}),
|
|
15458
|
-
/* @__PURE__ */
|
|
15459
|
-
|
|
15460
|
-
|
|
15461
|
-
|
|
15462
|
-
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
}
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
})
|
|
15707
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
15708
|
+
{
|
|
15709
|
+
key: "↵",
|
|
15710
|
+
label: "save"
|
|
15711
|
+
},
|
|
15712
|
+
{
|
|
15713
|
+
key: "esc",
|
|
15714
|
+
label: "cancel"
|
|
15715
|
+
},
|
|
15716
|
+
{
|
|
15717
|
+
key: "",
|
|
15718
|
+
label: `empty = ${editingInputItem.placeholder}`
|
|
15719
|
+
}
|
|
15720
|
+
] })
|
|
15473
15721
|
]
|
|
15474
15722
|
}),
|
|
15475
15723
|
/* @__PURE__ */ jsx$1("box", {
|
|
15476
15724
|
style: { flexShrink: 0 },
|
|
15477
15725
|
children: /* @__PURE__ */ jsx$1(Hints, {
|
|
15478
15726
|
activeTab,
|
|
15727
|
+
authorizing: !!authorizingMcp,
|
|
15479
15728
|
focusedMcp,
|
|
15480
15729
|
focusedMcpRow,
|
|
15481
15730
|
focusedMcpStatus,
|
|
@@ -16816,29 +17065,7 @@ function EmptyRow({ label }) {
|
|
|
16816
17065
|
});
|
|
16817
17066
|
}
|
|
16818
17067
|
function renderMcpDetailPanel(entry, status, COLOR) {
|
|
16819
|
-
if (status.kind === "authorizing")
|
|
16820
|
-
if (!status.url) return /* @__PURE__ */ jsxs("box", {
|
|
16821
|
-
style: {
|
|
16822
|
-
flexDirection: "column",
|
|
16823
|
-
border: ["top"],
|
|
16824
|
-
borderColor: COLOR.border,
|
|
16825
|
-
paddingTop: 1
|
|
16826
|
-
},
|
|
16827
|
-
children: [/* @__PURE__ */ jsx$1("text", {
|
|
16828
|
-
fg: COLOR.brand,
|
|
16829
|
-
children: `Authorizing ${entry.config.name}`
|
|
16830
|
-
}), /* @__PURE__ */ jsx$1("text", {
|
|
16831
|
-
fg: COLOR.dim,
|
|
16832
|
-
children: "Starting login flow…"
|
|
16833
|
-
})]
|
|
16834
|
-
});
|
|
16835
|
-
return /* @__PURE__ */ jsx$1(McpAuthorizingPanel, {
|
|
16836
|
-
serverName: entry.config.name,
|
|
16837
|
-
authUrl: status.url,
|
|
16838
|
-
maxLineWidth: 154,
|
|
16839
|
-
inputFocused: true
|
|
16840
|
-
});
|
|
16841
|
-
}
|
|
17068
|
+
if (status.kind === "authorizing") return null;
|
|
16842
17069
|
if (status.kind === "error") return /* @__PURE__ */ jsxs("box", {
|
|
16843
17070
|
style: {
|
|
16844
17071
|
flexDirection: "column",
|
|
@@ -16938,12 +17165,12 @@ function renderMcpErrors(errors, home, warnColor, dimColor) {
|
|
|
16938
17165
|
}, err.path))
|
|
16939
17166
|
});
|
|
16940
17167
|
}
|
|
16941
|
-
function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefreshSkills, canRefreshMcps, canEditExtension, canCreateExtension, canUpdateExtension, canRemoveExtension, canInstallExtension, canInstallSkill, canAddMcp, canRemoveSkill, canRemoveMcp, focusedExtensionExpandable }) {
|
|
17168
|
+
function Hints({ activeTab, authorizing = false, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefreshSkills, canRefreshMcps, canEditExtension, canCreateExtension, canUpdateExtension, canRemoveExtension, canInstallExtension, canInstallSkill, canAddMcp, canRemoveSkill, canRemoveMcp, focusedExtensionExpandable }) {
|
|
16942
17169
|
const COLOR = useColors();
|
|
16943
17170
|
const onServerRow = focusedMcpRow?.kind === "server";
|
|
16944
17171
|
const showLogin = activeTab === "mcps" && onServerRow && !!focusedMcp && !!focusedMcpStatus && canLogin$1(focusedMcp, focusedMcpStatus);
|
|
16945
17172
|
const showLogout = activeTab === "mcps" && onServerRow && !!focusedMcpStatus && canLogout$1(focusedMcpStatus);
|
|
16946
|
-
const showCancel = activeTab === "mcps" &&
|
|
17173
|
+
const showCancel = activeTab === "mcps" && authorizing;
|
|
16947
17174
|
const showRefresh = activeTab === "skills" && canRefreshSkills || activeTab === "mcps" && canRefreshMcps;
|
|
16948
17175
|
return /* @__PURE__ */ jsx$1(HintRow, { hints: [
|
|
16949
17176
|
{
|
|
@@ -17677,26 +17904,23 @@ function TurnDetailsModal({ turn, toolResultTurns = [], searchQuery, index, tota
|
|
|
17677
17904
|
children: "— no previewable content —"
|
|
17678
17905
|
})
|
|
17679
17906
|
}),
|
|
17680
|
-
editing ? /* @__PURE__ */
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
|
|
17695
|
-
|
|
17696
|
-
|
|
17697
|
-
" cancel"
|
|
17698
|
-
]
|
|
17699
|
-
}) : /* @__PURE__ */ jsx$1(ActionRow, {
|
|
17907
|
+
editing ? /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
17908
|
+
{
|
|
17909
|
+
key: "↵",
|
|
17910
|
+
label: "save",
|
|
17911
|
+
labelColor: COLOR.dim
|
|
17912
|
+
},
|
|
17913
|
+
{
|
|
17914
|
+
key: "shift+↵",
|
|
17915
|
+
label: "newline",
|
|
17916
|
+
labelColor: COLOR.dim
|
|
17917
|
+
},
|
|
17918
|
+
{
|
|
17919
|
+
key: "esc",
|
|
17920
|
+
label: "cancel",
|
|
17921
|
+
labelColor: COLOR.dim
|
|
17922
|
+
}
|
|
17923
|
+
] }) : /* @__PURE__ */ jsx$1(ActionRow, {
|
|
17700
17924
|
pending,
|
|
17701
17925
|
copyStatus,
|
|
17702
17926
|
canCopy: fullText.length > 0,
|
|
@@ -17758,81 +17982,72 @@ function ActionRow({ pending, copyStatus, canCopy, canEdit, forkKey, deleteKey,
|
|
|
17758
17982
|
" cancel"
|
|
17759
17983
|
]
|
|
17760
17984
|
});
|
|
17761
|
-
if (copyStatus === "copied") return /* @__PURE__ */
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
17801
|
-
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
|
|
17806
|
-
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
17815
|
-
|
|
17816
|
-
|
|
17817
|
-
|
|
17818
|
-
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
|
|
17826
|
-
|
|
17827
|
-
}),
|
|
17828
|
-
canEdit ? " edit · " : " (no text) · ",
|
|
17829
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
17830
|
-
fg: COLOR.warn,
|
|
17831
|
-
children: "esc"
|
|
17832
|
-
}),
|
|
17833
|
-
" close"
|
|
17834
|
-
]
|
|
17835
|
-
});
|
|
17985
|
+
if (copyStatus === "copied") return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
17986
|
+
{
|
|
17987
|
+
key: "",
|
|
17988
|
+
label: "✓ copied",
|
|
17989
|
+
labelColor: COLOR.accent
|
|
17990
|
+
},
|
|
17991
|
+
{
|
|
17992
|
+
key: forkKey,
|
|
17993
|
+
label: "fork",
|
|
17994
|
+
labelColor: COLOR.dim
|
|
17995
|
+
},
|
|
17996
|
+
{
|
|
17997
|
+
key: deleteKey,
|
|
17998
|
+
label: "delete",
|
|
17999
|
+
labelColor: COLOR.dim
|
|
18000
|
+
},
|
|
18001
|
+
{
|
|
18002
|
+
key: editKey,
|
|
18003
|
+
label: canEdit ? "edit" : "(no text)",
|
|
18004
|
+
keyColor: canEdit ? COLOR.warn : COLOR.mute,
|
|
18005
|
+
labelColor: COLOR.dim
|
|
18006
|
+
},
|
|
18007
|
+
{
|
|
18008
|
+
key: "esc",
|
|
18009
|
+
label: "close",
|
|
18010
|
+
labelColor: COLOR.dim
|
|
18011
|
+
}
|
|
18012
|
+
] });
|
|
18013
|
+
if (copyStatus === "failed") return /* @__PURE__ */ jsx$1(ModalHints, { hints: [{
|
|
18014
|
+
key: "",
|
|
18015
|
+
label: "copy failed (terminal may not support OSC 52)",
|
|
18016
|
+
labelColor: COLOR.error
|
|
18017
|
+
}, {
|
|
18018
|
+
key: "esc",
|
|
18019
|
+
label: "close",
|
|
18020
|
+
labelColor: COLOR.dim
|
|
18021
|
+
}] });
|
|
18022
|
+
return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
18023
|
+
{
|
|
18024
|
+
key: forkKey,
|
|
18025
|
+
label: "fork",
|
|
18026
|
+
labelColor: COLOR.dim
|
|
18027
|
+
},
|
|
18028
|
+
{
|
|
18029
|
+
key: deleteKey,
|
|
18030
|
+
label: "delete",
|
|
18031
|
+
labelColor: COLOR.dim
|
|
18032
|
+
},
|
|
18033
|
+
{
|
|
18034
|
+
key: copyKey,
|
|
18035
|
+
label: canCopy ? "copy" : "(nothing to copy)",
|
|
18036
|
+
keyColor: canCopy ? COLOR.warn : COLOR.mute,
|
|
18037
|
+
labelColor: COLOR.dim
|
|
18038
|
+
},
|
|
18039
|
+
{
|
|
18040
|
+
key: editKey,
|
|
18041
|
+
label: canEdit ? "edit" : "(no text)",
|
|
18042
|
+
keyColor: canEdit ? COLOR.warn : COLOR.mute,
|
|
18043
|
+
labelColor: COLOR.dim
|
|
18044
|
+
},
|
|
18045
|
+
{
|
|
18046
|
+
key: "esc",
|
|
18047
|
+
label: "close",
|
|
18048
|
+
labelColor: COLOR.dim
|
|
18049
|
+
}
|
|
18050
|
+
] });
|
|
17836
18051
|
}
|
|
17837
18052
|
function buildPreviewEntries(turns, limit) {
|
|
17838
18053
|
const entries = [];
|
|
@@ -17969,10 +18184,20 @@ const BASH_PAINT_INTERVAL_MS = 100;
|
|
|
17969
18184
|
const BASH_RESULT_MAX_BYTES = 32 * 1024;
|
|
17970
18185
|
/** Rolling-tail window while output streams — matches the renderer's bash display cap. */
|
|
17971
18186
|
const BASH_STREAM_TAIL_LINES = 10;
|
|
17972
|
-
/**
|
|
18187
|
+
/**
|
|
18188
|
+
* Last `n` lines of `text` (terminal-style rolling tail for live bash
|
|
18189
|
+
* output). Backward newline scan, not `split` — this runs per output
|
|
18190
|
+
* chunk against the full accumulation (up to 1MB), and re-splitting the
|
|
18191
|
+
* whole snapshot every chunk is O(n²) in total streamed bytes.
|
|
18192
|
+
*/
|
|
17973
18193
|
function lastLines(text, n) {
|
|
17974
|
-
|
|
17975
|
-
|
|
18194
|
+
let idx = text.length;
|
|
18195
|
+
for (let i = 0; i < n; i++) {
|
|
18196
|
+
if (idx === 0) return text;
|
|
18197
|
+
idx = text.lastIndexOf("\n", idx - 1);
|
|
18198
|
+
if (idx === -1) return text;
|
|
18199
|
+
}
|
|
18200
|
+
return text.slice(idx + 1);
|
|
17976
18201
|
}
|
|
17977
18202
|
/**
|
|
17978
18203
|
* Poll cadence for out-of-band extension install detection (see the
|
|
@@ -17983,6 +18208,14 @@ const EXTENSION_WATCH_POLL_MS = 3e3;
|
|
|
17983
18208
|
/** LRU bound for cross-session search indexes (full lowercased text per session). */
|
|
17984
18209
|
const SESSION_SEARCH_INDEX_CACHE_MAX = 64;
|
|
17985
18210
|
/**
|
|
18211
|
+
* Byte budget for the same cache, using each handle's indexed-text size
|
|
18212
|
+
* (reported by the worker build). The count cap alone lets 64 huge
|
|
18213
|
+
* sessions pin hundreds of MB (index copy in the worker, plus any
|
|
18214
|
+
* main-thread fallback indexes); the budget evicts LRU-first once the
|
|
18215
|
+
* summed estimate crosses it, keeping worst-case retention predictable.
|
|
18216
|
+
*/
|
|
18217
|
+
const SESSION_SEARCH_INDEX_CACHE_MAX_BYTES = 64 * 1024 * 1024;
|
|
18218
|
+
/**
|
|
17986
18219
|
* Surface failures that are normally silenced (teardown / save) when the
|
|
17987
18220
|
* `ZIDANE_DEBUG` env var is set. Logging via `console.error` would otherwise
|
|
17988
18221
|
* trigger OpenTUI's error console overlay and clutter the UI for end users.
|
|
@@ -18224,6 +18457,9 @@ function AppShell() {
|
|
|
18224
18457
|
if (renderer.targetFps !== fps) renderer.targetFps = fps;
|
|
18225
18458
|
if (renderer.maxFps !== fps) renderer.maxFps = fps;
|
|
18226
18459
|
}, [renderer, settings.targetFps]);
|
|
18460
|
+
useEffect(() => {
|
|
18461
|
+
if (renderer.useMouse !== settings.mouseInteractions) renderer.useMouse = settings.mouseInteractions;
|
|
18462
|
+
}, [renderer, settings.mouseInteractions]);
|
|
18227
18463
|
const lastCopiedRef = useRef("");
|
|
18228
18464
|
useSelectionHandler((selection) => {
|
|
18229
18465
|
if (selection.isDragging) return;
|
|
@@ -18970,9 +19206,17 @@ function AppShell() {
|
|
|
18970
19206
|
store,
|
|
18971
19207
|
getAllowedTokens: () => chatReferenceTokensRef.current
|
|
18972
19208
|
}), [store]);
|
|
19209
|
+
const visibleChatCatalogCache = useRef(/* @__PURE__ */ new WeakMap());
|
|
18973
19210
|
const visibleChatCatalog = useCallback((catalog) => {
|
|
18974
|
-
const currentId = currentSessionIdRef.current;
|
|
18975
|
-
|
|
19211
|
+
const currentId = currentSessionIdRef.current ?? null;
|
|
19212
|
+
const hit = visibleChatCatalogCache.current.get(catalog);
|
|
19213
|
+
if (hit && hit.currentId === currentId) return hit.result;
|
|
19214
|
+
const result = currentId ? catalog.filter((entry) => entry.id !== currentId) : [...catalog];
|
|
19215
|
+
visibleChatCatalogCache.current.set(catalog, {
|
|
19216
|
+
currentId,
|
|
19217
|
+
result
|
|
19218
|
+
});
|
|
19219
|
+
return result;
|
|
18976
19220
|
}, []);
|
|
18977
19221
|
const ensureChatCatalog = useCallback(async () => {
|
|
18978
19222
|
const [projectList, allList] = await Promise.all([listSessionMeta(store, { projectRoot: projectDir }), listSessionMeta(store)]);
|
|
@@ -19795,6 +20039,14 @@ function AppShell() {
|
|
|
19795
20039
|
setLastInputTokens(ctx.effectiveTokens);
|
|
19796
20040
|
lastInputTokensRef.current = ctx.effectiveTokens;
|
|
19797
20041
|
});
|
|
20042
|
+
agent.hooks.hook("history:repair", (ctx) => {
|
|
20043
|
+
if (sessionRef.current?.id !== session.id) return;
|
|
20044
|
+
const blocks = `${ctx.removedBlocks} stale reasoning block${ctx.removedBlocks === 1 ? "" : "s"}`;
|
|
20045
|
+
stream.appendImmediate({
|
|
20046
|
+
kind: "info",
|
|
20047
|
+
text: `🩹 Repaired history: removed ${blocks} the provider rejected${ctx.persisted ? "" : " (session not yet persisted)"}.`
|
|
20048
|
+
});
|
|
20049
|
+
});
|
|
19798
20050
|
const spawnStartedAt = /* @__PURE__ */ new Map();
|
|
19799
20051
|
agent.hooks.hook("spawn:before", ({ id, task, depth, model, resumesChildId }) => {
|
|
19800
20052
|
const startedAt = Date.now();
|
|
@@ -19925,6 +20177,7 @@ function AppShell() {
|
|
|
19925
20177
|
});
|
|
19926
20178
|
agent.hooks.hook("agent:done", () => {
|
|
19927
20179
|
pendingAnnotationsRef.current.clear();
|
|
20180
|
+
pruneTodosByRun(session);
|
|
19928
20181
|
});
|
|
19929
20182
|
subscribeMcpToolsCache(agent.hooks, { dataDir });
|
|
19930
20183
|
return agent;
|
|
@@ -19991,7 +20244,9 @@ function AppShell() {
|
|
|
19991
20244
|
const cachedLive = liveSessionSearchIndexRef.current;
|
|
19992
20245
|
if (cachedLive?.sessionId === meta.id && cachedLive.updatedAt === meta.updatedAt && cachedLive.turnCount === live.turns.length) index = cachedLive.index;
|
|
19993
20246
|
else {
|
|
20247
|
+
const staleLive = liveSessionSearchIndexRef.current;
|
|
19994
20248
|
index = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(live.turns, abortController.signal);
|
|
20249
|
+
if (staleLive) historySearchWorkerRef.current?.release(staleLive.index);
|
|
19995
20250
|
liveSessionSearchIndexRef.current = {
|
|
19996
20251
|
sessionId: meta.id,
|
|
19997
20252
|
updatedAt: meta.updatedAt,
|
|
@@ -20006,15 +20261,25 @@ function AppShell() {
|
|
|
20006
20261
|
} else {
|
|
20007
20262
|
const turns = await store.getTurns(meta.id);
|
|
20008
20263
|
index = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
20264
|
+
historySearchWorkerRef.current?.slim(index);
|
|
20009
20265
|
const cache = sessionSearchCacheRef.current;
|
|
20266
|
+
const stale = cache.get(meta.id);
|
|
20267
|
+
if (stale) historySearchWorkerRef.current?.release(stale.index);
|
|
20010
20268
|
cache.set(meta.id, {
|
|
20011
20269
|
updatedAt: meta.updatedAt,
|
|
20012
20270
|
index
|
|
20013
20271
|
});
|
|
20014
|
-
|
|
20272
|
+
let totalBytes = 0;
|
|
20273
|
+
for (const entry of cache.values()) totalBytes += entry.index.bytes;
|
|
20274
|
+
while (cache.size > 1 && (cache.size > SESSION_SEARCH_INDEX_CACHE_MAX || totalBytes > SESSION_SEARCH_INDEX_CACHE_MAX_BYTES)) {
|
|
20015
20275
|
const oldest = cache.keys().next().value;
|
|
20016
20276
|
if (oldest === void 0) break;
|
|
20277
|
+
const evicted = cache.get(oldest);
|
|
20017
20278
|
cache.delete(oldest);
|
|
20279
|
+
if (evicted) {
|
|
20280
|
+
totalBytes -= evicted.index.bytes;
|
|
20281
|
+
historySearchWorkerRef.current?.release(evicted.index);
|
|
20282
|
+
}
|
|
20018
20283
|
}
|
|
20019
20284
|
}
|
|
20020
20285
|
if (generation !== sessionSearchGenerationRef.current) return;
|
|
@@ -20022,7 +20287,24 @@ function AppShell() {
|
|
|
20022
20287
|
if (match) matches.set(meta.id, match);
|
|
20023
20288
|
} catch (error) {
|
|
20024
20289
|
if (abortController.signal.aborted) throw abortController.signal.reason instanceof DOMException ? abortController.signal.reason : error;
|
|
20025
|
-
|
|
20290
|
+
const broken = sessionSearchCacheRef.current.get(meta.id);
|
|
20291
|
+
if (broken) {
|
|
20292
|
+
sessionSearchCacheRef.current.delete(meta.id);
|
|
20293
|
+
historySearchWorkerRef.current?.release(broken.index);
|
|
20294
|
+
try {
|
|
20295
|
+
const turns = await store.getTurns(meta.id);
|
|
20296
|
+
const retry = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
20297
|
+
try {
|
|
20298
|
+
const match = await historySearchWorkerRef.current.history(retry, query, 3, abortController.signal);
|
|
20299
|
+
if (match) matches.set(meta.id, match);
|
|
20300
|
+
} finally {
|
|
20301
|
+
historySearchWorkerRef.current?.release(retry);
|
|
20302
|
+
}
|
|
20303
|
+
} catch (retryError) {
|
|
20304
|
+
if (abortController.signal.aborted) throw abortController.signal.reason instanceof DOMException ? abortController.signal.reason : retryError;
|
|
20305
|
+
debugLog("session content search retry failed", retryError);
|
|
20306
|
+
}
|
|
20307
|
+
} else debugLog("session content search skipped a session", error);
|
|
20026
20308
|
}
|
|
20027
20309
|
done++;
|
|
20028
20310
|
if (done === sessions.length || done % 4 === 0) setSessionContentSearchProgress({
|
|
@@ -21172,6 +21454,7 @@ function AppShell() {
|
|
|
21172
21454
|
const ctx = {
|
|
21173
21455
|
args: invocation.args,
|
|
21174
21456
|
argv: invocation.argv,
|
|
21457
|
+
invokedAs: invocation.invokedAs,
|
|
21175
21458
|
...attachments.length > 0 ? { attachments: attachments.map((a) => ({
|
|
21176
21459
|
name: a.name,
|
|
21177
21460
|
mediaType: a.mediaType,
|
|
@@ -21921,6 +22204,10 @@ function AppShell() {
|
|
|
21921
22204
|
const onLoginMcp = useCallback(async (name) => {
|
|
21922
22205
|
const entry = mcpsCatalogRef.current.find((d) => d.config.name === name);
|
|
21923
22206
|
if (!entry) return;
|
|
22207
|
+
for (const [other, ctrl] of mcpLoginAbortsRef.current) if (other !== name && !ctrl.signal.aborted) {
|
|
22208
|
+
debugLog(`mcp-login: refusing concurrent login for "${name}"`, `"${other}" is still authorizing`);
|
|
22209
|
+
return;
|
|
22210
|
+
}
|
|
21924
22211
|
mcpLoginAbortsRef.current.get(name)?.abort();
|
|
21925
22212
|
const ac = new AbortController();
|
|
21926
22213
|
mcpLoginAbortsRef.current.set(name, ac);
|
|
@@ -21932,6 +22219,7 @@ function AppShell() {
|
|
|
21932
22219
|
await loginMcpServer(entry.config, {
|
|
21933
22220
|
store: mcpCredentialStore,
|
|
21934
22221
|
signal: ac.signal,
|
|
22222
|
+
renderCallbackPage: renderDefaultCallbackPage,
|
|
21935
22223
|
hooks: agentRef.current?.hooks,
|
|
21936
22224
|
onAuthorizationUrl: (url) => tryOpenBrowser(url.toString())
|
|
21937
22225
|
});
|
|
@@ -22024,6 +22312,7 @@ function AppShell() {
|
|
|
22024
22312
|
(async () => {
|
|
22025
22313
|
const cached = transcriptSearchIndexRef.current;
|
|
22026
22314
|
const index = cached?.sessionId === session.id && cached.events === events ? cached.index : await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
22315
|
+
if (cached && cached.index !== index) historySearchWorkerRef.current?.release(cached.index);
|
|
22027
22316
|
transcriptSearchIndexRef.current = {
|
|
22028
22317
|
sessionId: session.id,
|
|
22029
22318
|
events,
|
|
@@ -22151,7 +22440,14 @@ function AppShell() {
|
|
|
22151
22440
|
store,
|
|
22152
22441
|
...source.projectRoot ? { projectRoot: source.projectRoot } : {}
|
|
22153
22442
|
});
|
|
22154
|
-
|
|
22443
|
+
const rehomed = await rehomePersistedStubs(slice, resolvePersistDir({
|
|
22444
|
+
userDir: cacheDir,
|
|
22445
|
+
sessionId: source.id
|
|
22446
|
+
}), resolvePersistDir({
|
|
22447
|
+
userDir: cacheDir,
|
|
22448
|
+
sessionId: fork.id
|
|
22449
|
+
}));
|
|
22450
|
+
fork.setTurns(rehomed);
|
|
22155
22451
|
fork.setRuns(inheritedRuns);
|
|
22156
22452
|
try {
|
|
22157
22453
|
await fork.save();
|
|
@@ -22164,7 +22460,8 @@ function AppShell() {
|
|
|
22164
22460
|
}, [
|
|
22165
22461
|
picked,
|
|
22166
22462
|
store,
|
|
22167
|
-
activateSession
|
|
22463
|
+
activateSession,
|
|
22464
|
+
cacheDir
|
|
22168
22465
|
]);
|
|
22169
22466
|
const onDeleteTurn = useCallback(async (turnId) => {
|
|
22170
22467
|
const session = sessionRef.current;
|
|
@@ -22508,9 +22805,7 @@ function AppShell() {
|
|
|
22508
22805
|
onExportSession,
|
|
22509
22806
|
keybindings
|
|
22510
22807
|
]);
|
|
22511
|
-
const
|
|
22512
|
-
const id = selectedTurnId;
|
|
22513
|
-
if (!id) return;
|
|
22808
|
+
const openTurnDetails = useCallback((id) => {
|
|
22514
22809
|
const session = sessionRef.current;
|
|
22515
22810
|
if (!session) return;
|
|
22516
22811
|
const turn = session.turns.find((t) => t.id === id);
|
|
@@ -22541,7 +22836,6 @@ function AppShell() {
|
|
|
22541
22836
|
}));
|
|
22542
22837
|
}, [
|
|
22543
22838
|
modal,
|
|
22544
|
-
selectedTurnId,
|
|
22545
22839
|
turnIds,
|
|
22546
22840
|
transcriptSearchMode,
|
|
22547
22841
|
transcriptSearchQuery,
|
|
@@ -22551,7 +22845,54 @@ function AppShell() {
|
|
|
22551
22845
|
onEditTurn,
|
|
22552
22846
|
keybindings
|
|
22553
22847
|
]);
|
|
22848
|
+
const openSelectedTurn = useCallback(() => {
|
|
22849
|
+
if (selectedTurnId) openTurnDetails(selectedTurnId);
|
|
22850
|
+
}, [selectedTurnId, openTurnDetails]);
|
|
22851
|
+
const onTranscriptSelectTurn = useCallback((id) => {
|
|
22852
|
+
if (!(transcriptSearchMode ? transcriptSearchTurnIds : turnIds).includes(id)) return false;
|
|
22853
|
+
setSelectedTurnId(id);
|
|
22854
|
+
return true;
|
|
22855
|
+
}, [
|
|
22856
|
+
transcriptSearchMode,
|
|
22857
|
+
transcriptSearchTurnIds,
|
|
22858
|
+
turnIds
|
|
22859
|
+
]);
|
|
22860
|
+
const onTranscriptOpenTurn = useCallback((id) => {
|
|
22861
|
+
if (!(transcriptSearchMode ? transcriptSearchTurnIds : turnIds).includes(id)) return false;
|
|
22862
|
+
setSelectedTurnId(id);
|
|
22863
|
+
openTurnDetails(id);
|
|
22864
|
+
return true;
|
|
22865
|
+
}, [
|
|
22866
|
+
transcriptSearchMode,
|
|
22867
|
+
transcriptSearchTurnIds,
|
|
22868
|
+
turnIds,
|
|
22869
|
+
openTurnDetails
|
|
22870
|
+
]);
|
|
22554
22871
|
useKeyboard((key) => {
|
|
22872
|
+
if (showQuitConfirmRef.current) {
|
|
22873
|
+
if (key.name === "escape") {
|
|
22874
|
+
setShowQuitConfirm(false);
|
|
22875
|
+
setQuitConfirmSelected("cancel");
|
|
22876
|
+
return;
|
|
22877
|
+
}
|
|
22878
|
+
if (key.name === "c" && key.ctrl && !key.shift) {
|
|
22879
|
+
renderer.destroy();
|
|
22880
|
+
return;
|
|
22881
|
+
}
|
|
22882
|
+
if (key.name === "return") {
|
|
22883
|
+
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
22884
|
+
else {
|
|
22885
|
+
setShowQuitConfirm(false);
|
|
22886
|
+
setQuitConfirmSelected("cancel");
|
|
22887
|
+
}
|
|
22888
|
+
return;
|
|
22889
|
+
}
|
|
22890
|
+
if (key.name === "tab" || key.name === "left" || key.name === "right") {
|
|
22891
|
+
setQuitConfirmSelected((prev) => prev === "cancel" ? "quit" : "cancel");
|
|
22892
|
+
return;
|
|
22893
|
+
}
|
|
22894
|
+
return;
|
|
22895
|
+
}
|
|
22555
22896
|
if (modal.isOpen) return;
|
|
22556
22897
|
if (transcriptSearchMode && screen === "chat") {
|
|
22557
22898
|
if (key.name === "escape") onTranscriptSearchModeChange(false);
|
|
@@ -22763,7 +23104,7 @@ function AppShell() {
|
|
|
22763
23104
|
}));
|
|
22764
23105
|
return;
|
|
22765
23106
|
}
|
|
22766
|
-
if (key.ctrl && !key.shift && !key.meta && (key.name === "c" || key.name === "x")
|
|
23107
|
+
if (key.ctrl && !key.shift && !key.meta && (key.name === "c" || key.name === "x")) {
|
|
22767
23108
|
const editor = renderer.currentFocusedEditor;
|
|
22768
23109
|
if (editor?.hasSelection()) {
|
|
22769
23110
|
const text = editor.getSelectedText();
|
|
@@ -22772,30 +23113,6 @@ function AppShell() {
|
|
|
22772
23113
|
return;
|
|
22773
23114
|
}
|
|
22774
23115
|
}
|
|
22775
|
-
if (showQuitConfirmRef.current) {
|
|
22776
|
-
if (key.name === "escape") {
|
|
22777
|
-
setShowQuitConfirm(false);
|
|
22778
|
-
setQuitConfirmSelected("cancel");
|
|
22779
|
-
return;
|
|
22780
|
-
}
|
|
22781
|
-
if (key.name === "c" && key.ctrl && !key.shift) {
|
|
22782
|
-
renderer.destroy();
|
|
22783
|
-
return;
|
|
22784
|
-
}
|
|
22785
|
-
if (key.name === "return") {
|
|
22786
|
-
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
22787
|
-
else {
|
|
22788
|
-
setShowQuitConfirm(false);
|
|
22789
|
-
setQuitConfirmSelected("cancel");
|
|
22790
|
-
}
|
|
22791
|
-
return;
|
|
22792
|
-
}
|
|
22793
|
-
if (key.name === "tab" || key.name === "left" || key.name === "right") {
|
|
22794
|
-
setQuitConfirmSelected((prev) => prev === "cancel" ? "quit" : "cancel");
|
|
22795
|
-
return;
|
|
22796
|
-
}
|
|
22797
|
-
return;
|
|
22798
|
-
}
|
|
22799
23116
|
if (key.name === "c" && key.ctrl && !key.shift) {
|
|
22800
23117
|
const mode = settingsRef.current.ctrlCBehavior;
|
|
22801
23118
|
if (mode === "quit") {
|
|
@@ -23049,6 +23366,8 @@ function AppShell() {
|
|
|
23049
23366
|
enterSelectsCompletion: (providerId) => providerId === "chats",
|
|
23050
23367
|
onPopupOpenChange,
|
|
23051
23368
|
selectedTurnId,
|
|
23369
|
+
onSelectTurn: onTranscriptSelectTurn,
|
|
23370
|
+
onOpenTurn: onTranscriptOpenTurn,
|
|
23052
23371
|
transcriptSearchMode,
|
|
23053
23372
|
transcriptSearchQuery,
|
|
23054
23373
|
transcriptSearchEvents,
|
|
@@ -23072,15 +23391,16 @@ function AppShell() {
|
|
|
23072
23391
|
status: transcriptSearchMode && transcriptSearchSearching ? "searching" : pendingInteraction?.kind === "question" ? "asking" : compacting ? "compacting" : retryInfo && busy ? "retrying" : busy ? "busy" : null,
|
|
23073
23392
|
compactionTokens,
|
|
23074
23393
|
compactionStartedAt,
|
|
23075
|
-
retry: retryInfo
|
|
23394
|
+
retry: retryInfo,
|
|
23395
|
+
contextKey: keybindings.openContextPanel
|
|
23076
23396
|
}),
|
|
23077
23397
|
showQuitConfirm && /* @__PURE__ */ jsxs("box", {
|
|
23078
23398
|
position: "absolute",
|
|
23079
23399
|
top: "50%",
|
|
23080
23400
|
left: "50%",
|
|
23081
23401
|
marginTop: -3,
|
|
23082
|
-
marginLeft: -
|
|
23083
|
-
width:
|
|
23402
|
+
marginLeft: -21,
|
|
23403
|
+
width: 42,
|
|
23084
23404
|
height: 7,
|
|
23085
23405
|
borderStyle: "rounded",
|
|
23086
23406
|
borderColor: COLOR.warn,
|
|
@@ -23089,35 +23409,52 @@ function AppShell() {
|
|
|
23089
23409
|
alignItems: "center",
|
|
23090
23410
|
justifyContent: "center",
|
|
23091
23411
|
gap: 1,
|
|
23092
|
-
children: [
|
|
23093
|
-
|
|
23094
|
-
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
event
|
|
23100
|
-
|
|
23101
|
-
|
|
23412
|
+
children: [
|
|
23413
|
+
/* @__PURE__ */ jsx$1("text", { children: "Quit zidane?" }),
|
|
23414
|
+
/* @__PURE__ */ jsxs("box", {
|
|
23415
|
+
flexDirection: "row",
|
|
23416
|
+
gap: 2,
|
|
23417
|
+
children: [/* @__PURE__ */ jsx$1("box", {
|
|
23418
|
+
backgroundColor: quitConfirmSelected === "cancel" ? COLOR.accent : void 0,
|
|
23419
|
+
onMouseDown: (event) => {
|
|
23420
|
+
event.stopPropagation();
|
|
23421
|
+
event.preventDefault();
|
|
23422
|
+
if (quitConfirmSelectedRef.current === "cancel") setShowQuitConfirm(false);
|
|
23423
|
+
else setQuitConfirmSelected("cancel");
|
|
23424
|
+
},
|
|
23425
|
+
children: /* @__PURE__ */ jsx$1("text", {
|
|
23426
|
+
fg: quitConfirmSelected === "cancel" ? SURFACE.modal : COLOR.dim,
|
|
23427
|
+
children: " Cancel "
|
|
23428
|
+
})
|
|
23429
|
+
}), /* @__PURE__ */ jsx$1("box", {
|
|
23430
|
+
backgroundColor: quitConfirmSelected === "quit" ? COLOR.warn : void 0,
|
|
23431
|
+
onMouseDown: (event) => {
|
|
23432
|
+
event.stopPropagation();
|
|
23433
|
+
event.preventDefault();
|
|
23434
|
+
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
23435
|
+
else setQuitConfirmSelected("quit");
|
|
23436
|
+
},
|
|
23437
|
+
children: /* @__PURE__ */ jsx$1("text", {
|
|
23438
|
+
fg: quitConfirmSelected === "quit" ? SURFACE.modal : COLOR.dim,
|
|
23439
|
+
children: " Quit "
|
|
23440
|
+
})
|
|
23441
|
+
})]
|
|
23442
|
+
}),
|
|
23443
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
23444
|
+
{
|
|
23445
|
+
key: "tab",
|
|
23446
|
+
label: "switch"
|
|
23102
23447
|
},
|
|
23103
|
-
|
|
23104
|
-
|
|
23105
|
-
|
|
23106
|
-
})
|
|
23107
|
-
}), /* @__PURE__ */ jsx$1("box", {
|
|
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");
|
|
23448
|
+
{
|
|
23449
|
+
key: "↵",
|
|
23450
|
+
label: "confirm"
|
|
23114
23451
|
},
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
}
|
|
23119
|
-
})
|
|
23120
|
-
|
|
23452
|
+
{
|
|
23453
|
+
key: "esc",
|
|
23454
|
+
label: "cancel"
|
|
23455
|
+
}
|
|
23456
|
+
] })
|
|
23457
|
+
]
|
|
23121
23458
|
})
|
|
23122
23459
|
]
|
|
23123
23460
|
});
|
|
@@ -23534,30 +23871,27 @@ function EffortPickerModal({ current, supportsAdaptive, onPick }) {
|
|
|
23534
23871
|
highlightBg: SURFACE.selection
|
|
23535
23872
|
}, level.id))
|
|
23536
23873
|
}),
|
|
23537
|
-
/* @__PURE__ */
|
|
23538
|
-
|
|
23539
|
-
|
|
23540
|
-
|
|
23541
|
-
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
|
|
23558
|
-
})
|
|
23559
|
-
]
|
|
23560
|
-
})
|
|
23874
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
23875
|
+
{
|
|
23876
|
+
key: "↑↓",
|
|
23877
|
+
label: "navigate",
|
|
23878
|
+
labelColor: COLOR.dim
|
|
23879
|
+
},
|
|
23880
|
+
{
|
|
23881
|
+
key: "↵",
|
|
23882
|
+
label: "select",
|
|
23883
|
+
labelColor: COLOR.dim
|
|
23884
|
+
},
|
|
23885
|
+
{
|
|
23886
|
+
key: "esc",
|
|
23887
|
+
label: "close",
|
|
23888
|
+
labelColor: COLOR.dim
|
|
23889
|
+
},
|
|
23890
|
+
{
|
|
23891
|
+
key: "",
|
|
23892
|
+
label: `${filtered.length} / ${levels.length} level${levels.length === 1 ? "" : "s"}`
|
|
23893
|
+
}
|
|
23894
|
+
] })
|
|
23561
23895
|
]
|
|
23562
23896
|
});
|
|
23563
23897
|
}
|
|
@@ -23648,20 +23982,13 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
|
|
|
23648
23982
|
children: [
|
|
23649
23983
|
preamble,
|
|
23650
23984
|
emptyState,
|
|
23651
|
-
/* @__PURE__ */
|
|
23652
|
-
|
|
23653
|
-
|
|
23654
|
-
|
|
23655
|
-
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
23659
|
-
fg: COLOR.warn,
|
|
23660
|
-
children: "esc"
|
|
23661
|
-
}),
|
|
23662
|
-
" close"
|
|
23663
|
-
]
|
|
23664
|
-
})
|
|
23985
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [onRefresh && {
|
|
23986
|
+
key: "ctrl+r",
|
|
23987
|
+
label: "refresh"
|
|
23988
|
+
}, {
|
|
23989
|
+
key: "esc",
|
|
23990
|
+
label: "close"
|
|
23991
|
+
}] })
|
|
23665
23992
|
]
|
|
23666
23993
|
});
|
|
23667
23994
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
@@ -23697,30 +24024,24 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
|
|
|
23697
24024
|
}, name);
|
|
23698
24025
|
})
|
|
23699
24026
|
}),
|
|
23700
|
-
/* @__PURE__ */
|
|
23701
|
-
|
|
23702
|
-
|
|
23703
|
-
|
|
23704
|
-
|
|
23705
|
-
|
|
23706
|
-
|
|
23707
|
-
|
|
23708
|
-
|
|
23709
|
-
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
|
|
23713
|
-
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
fg: COLOR.warn,
|
|
23719
|
-
children: "esc"
|
|
23720
|
-
}),
|
|
23721
|
-
" close"
|
|
23722
|
-
]
|
|
23723
|
-
})
|
|
24027
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
24028
|
+
{
|
|
24029
|
+
key: "↑↓",
|
|
24030
|
+
label: "navigate"
|
|
24031
|
+
},
|
|
24032
|
+
{
|
|
24033
|
+
key: "↵",
|
|
24034
|
+
label: "toggle"
|
|
24035
|
+
},
|
|
24036
|
+
onRefresh && {
|
|
24037
|
+
key: "ctrl+r",
|
|
24038
|
+
label: "refresh"
|
|
24039
|
+
},
|
|
24040
|
+
{
|
|
24041
|
+
key: "esc",
|
|
24042
|
+
label: "close"
|
|
24043
|
+
}
|
|
24044
|
+
] })
|
|
23724
24045
|
]
|
|
23725
24046
|
});
|
|
23726
24047
|
}
|
|
@@ -24030,20 +24351,13 @@ function McpsSettingsModal({ catalog, errors, toolsByServer, onLogin, onLogout,
|
|
|
24030
24351
|
children: " { \"mcpServers\": { ... } } "
|
|
24031
24352
|
}), "wrapper from Claude Code / Cursor also works."]
|
|
24032
24353
|
}),
|
|
24033
|
-
/* @__PURE__ */
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
24041
|
-
fg: COLOR.warn,
|
|
24042
|
-
children: "esc"
|
|
24043
|
-
}),
|
|
24044
|
-
" close"
|
|
24045
|
-
]
|
|
24046
|
-
})
|
|
24354
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [onRefresh && {
|
|
24355
|
+
key: "r",
|
|
24356
|
+
label: "refresh"
|
|
24357
|
+
}, {
|
|
24358
|
+
key: "esc",
|
|
24359
|
+
label: "close"
|
|
24360
|
+
}] })
|
|
24047
24361
|
]
|
|
24048
24362
|
});
|
|
24049
24363
|
const groups = [];
|
|
@@ -24098,8 +24412,7 @@ function McpsSettingsModal({ catalog, errors, toolsByServer, onLogin, onLogout,
|
|
|
24098
24412
|
focusedEntry,
|
|
24099
24413
|
focusedStatus,
|
|
24100
24414
|
expanded,
|
|
24101
|
-
showRefresh: !!onRefresh
|
|
24102
|
-
COLOR
|
|
24415
|
+
showRefresh: !!onRefresh
|
|
24103
24416
|
})
|
|
24104
24417
|
]
|
|
24105
24418
|
});
|
|
@@ -24330,7 +24643,7 @@ function renderDetailPanel(entry, status, COLOR) {
|
|
|
24330
24643
|
});
|
|
24331
24644
|
return null;
|
|
24332
24645
|
}
|
|
24333
|
-
function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded, showRefresh
|
|
24646
|
+
function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded, showRefresh }) {
|
|
24334
24647
|
const effectiveStatus = focusedStatus ?? { kind: "idle" };
|
|
24335
24648
|
const onServerRow = focusedRow?.kind === "server";
|
|
24336
24649
|
const canL = onServerRow && focusedEntry ? canLogin(focusedEntry, effectiveStatus) : false;
|
|
@@ -24338,77 +24651,44 @@ function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded,
|
|
|
24338
24651
|
const canCancel = effectiveStatus.kind === "authorizing";
|
|
24339
24652
|
const canExpand = onServerRow && focusedEntry ? !expanded.has(focusedEntry.config.name) : false;
|
|
24340
24653
|
const canCollapse = focusedRow?.kind === "tool" || (onServerRow && focusedEntry ? expanded.has(focusedEntry.config.name) : false);
|
|
24341
|
-
return /* @__PURE__ */
|
|
24342
|
-
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
24350
|
-
|
|
24351
|
-
|
|
24352
|
-
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24356
|
-
|
|
24357
|
-
|
|
24358
|
-
|
|
24359
|
-
|
|
24360
|
-
|
|
24361
|
-
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
" · ",
|
|
24380
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
24381
|
-
fg: COLOR.warn,
|
|
24382
|
-
children: "o"
|
|
24383
|
-
}),
|
|
24384
|
-
" logout"
|
|
24385
|
-
] }),
|
|
24386
|
-
showRefresh && /* @__PURE__ */ jsxs("span", { children: [
|
|
24387
|
-
" · ",
|
|
24388
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
24389
|
-
fg: COLOR.warn,
|
|
24390
|
-
children: "r"
|
|
24391
|
-
}),
|
|
24392
|
-
" refresh"
|
|
24393
|
-
] }),
|
|
24394
|
-
canCancel && /* @__PURE__ */ jsxs("span", { children: [
|
|
24395
|
-
" · ",
|
|
24396
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
24397
|
-
fg: COLOR.warn,
|
|
24398
|
-
children: "esc"
|
|
24399
|
-
}),
|
|
24400
|
-
" cancel"
|
|
24401
|
-
] }),
|
|
24402
|
-
!canCancel && /* @__PURE__ */ jsxs("span", { children: [
|
|
24403
|
-
" · ",
|
|
24404
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
24405
|
-
fg: COLOR.warn,
|
|
24406
|
-
children: "esc"
|
|
24407
|
-
}),
|
|
24408
|
-
" close"
|
|
24409
|
-
] })
|
|
24410
|
-
]
|
|
24411
|
-
});
|
|
24654
|
+
return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
24655
|
+
{
|
|
24656
|
+
key: "↑↓",
|
|
24657
|
+
label: "navigate"
|
|
24658
|
+
},
|
|
24659
|
+
{
|
|
24660
|
+
key: "↵",
|
|
24661
|
+
label: "toggle"
|
|
24662
|
+
},
|
|
24663
|
+
canExpand && {
|
|
24664
|
+
key: "→",
|
|
24665
|
+
label: "expand"
|
|
24666
|
+
},
|
|
24667
|
+
canCollapse && {
|
|
24668
|
+
key: "←",
|
|
24669
|
+
label: "collapse"
|
|
24670
|
+
},
|
|
24671
|
+
canL && {
|
|
24672
|
+
key: "l",
|
|
24673
|
+
label: "login"
|
|
24674
|
+
},
|
|
24675
|
+
canO && {
|
|
24676
|
+
key: "o",
|
|
24677
|
+
label: "logout"
|
|
24678
|
+
},
|
|
24679
|
+
showRefresh && {
|
|
24680
|
+
key: "r",
|
|
24681
|
+
label: "refresh"
|
|
24682
|
+
},
|
|
24683
|
+
canCancel && {
|
|
24684
|
+
key: "esc",
|
|
24685
|
+
label: "cancel"
|
|
24686
|
+
},
|
|
24687
|
+
!canCancel && {
|
|
24688
|
+
key: "esc",
|
|
24689
|
+
label: "close"
|
|
24690
|
+
}
|
|
24691
|
+
] });
|
|
24412
24692
|
}
|
|
24413
24693
|
function renderErrors(errors, home, warnColor) {
|
|
24414
24694
|
if (!errors || errors.length === 0) return null;
|
|
@@ -24633,6 +24913,7 @@ async function runTui(options = {}) {
|
|
|
24633
24913
|
debounceDelay: 0,
|
|
24634
24914
|
targetFps: bootFps,
|
|
24635
24915
|
maxFps: bootFps,
|
|
24916
|
+
useMouse: config.initialSettings.mouseInteractions ?? DEFAULT_SETTINGS.mouseInteractions,
|
|
24636
24917
|
gatherStats
|
|
24637
24918
|
});
|
|
24638
24919
|
bootTick("runTui:after-createCliRenderer");
|