zidane 6.2.24 → 6.2.26
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-CNZqD1Fw.js} +3 -3
- package/dist/{acp-CTn1kFvP.js.map → acp-CNZqD1Fw.js.map} +1 -1
- package/dist/acp-cli.js +4 -4
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-tLfGSSQw.js → agent--VPMZ0nT.js} +267 -16
- package/dist/agent--VPMZ0nT.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +2 -2
- package/dist/{auth-CpdQebUy.js → auth-DIT_DMwa.js} +2 -2
- package/dist/{auth-CpdQebUy.js.map → auth-DIT_DMwa.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 +1 -1
- 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 +3 -3
- 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-AVZ6pYpi.d.ts} +212 -96
- package/dist/index-AVZ6pYpi.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -5
- 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/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/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/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +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.d.ts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{tool-formatters-CZJYzP4x.d.ts → tool-formatters-Dual3Hdl.d.ts} +2 -2
- package/dist/{tool-formatters-CZJYzP4x.d.ts.map → tool-formatters-Dual3Hdl.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-CJ2dmpQk.d.ts → transcript-anchors-CG85bieG.d.ts} +2 -2
- package/dist/transcript-anchors-CG85bieG.d.ts.map +1 -0
- package/dist/{transcript-anchors-Ded7iJjG.js → transcript-anchors-CnmxZ8QR.js} +88 -12
- package/dist/transcript-anchors-CnmxZ8QR.js.map +1 -0
- package/dist/tui.d.ts +44 -7
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +1397 -1124
- 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-BUYaSCL3.d.ts} +3 -3
- package/dist/{turn-operations-DZYpXcU1.d.ts.map → turn-operations-BUYaSCL3.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/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/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,8 +1,8 @@
|
|
|
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-CnmxZ8QR.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--VPMZ0nT.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";
|
|
@@ -12,14 +12,14 @@ import { replaceDynamicSection } from "./prompt.js";
|
|
|
12
12
|
import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-jLsc_kMH.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-DIT_DMwa.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,31 +1295,25 @@ 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
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
})
|
|
1018
|
-
]
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
function EmptyState$1() {
|
|
1022
|
-
const COLOR = useColors();
|
|
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
|
+
] })
|
|
1312
|
+
]
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
function EmptyState$1() {
|
|
1316
|
+
const COLOR = useColors();
|
|
1023
1317
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
1024
1318
|
title: "select agent",
|
|
1025
1319
|
children: [/* @__PURE__ */ jsx$1("text", {
|
|
@@ -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
|
}
|
|
@@ -1589,290 +1892,96 @@ function pickHelper() {
|
|
|
1589
1892
|
if (process.env.WAYLAND_DISPLAY) return {
|
|
1590
1893
|
cmd: "wl-copy",
|
|
1591
1894
|
args: []
|
|
1592
|
-
};
|
|
1593
|
-
if (process.env.DISPLAY) return {
|
|
1594
|
-
cmd: "xclip",
|
|
1595
|
-
args: ["-selection", "clipboard"]
|
|
1596
|
-
};
|
|
1597
|
-
return null;
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Spawn the helper and pipe `text` over stdin. Fire-and-forget: we
|
|
1601
|
-
* don't await the child's exit because the OS clipboard is populated
|
|
1602
|
-
* synchronously by `pbcopy` / `clip.exe` / `wl-copy` / `xclip` once
|
|
1603
|
-
* stdin closes, and a typical drag-select is human-scale latency we
|
|
1604
|
-
* don't need to gate the next frame on.
|
|
1605
|
-
*
|
|
1606
|
-
* Returns `true` if the spawn looked plausibly successful, `false`
|
|
1607
|
-
* if we couldn't even attach to the child's stdin (binary missing,
|
|
1608
|
-
* 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.
|
|
1780
|
-
*/
|
|
1781
|
-
function dispatchHintKey(renderer, spec) {
|
|
1782
|
-
const parsed = parseHintKeySpec(spec);
|
|
1783
|
-
if (!parsed) return false;
|
|
1784
|
-
renderer.keyInput.processParsedKey(parsed);
|
|
1785
|
-
return true;
|
|
1895
|
+
};
|
|
1896
|
+
if (process.env.DISPLAY) return {
|
|
1897
|
+
cmd: "xclip",
|
|
1898
|
+
args: ["-selection", "clipboard"]
|
|
1899
|
+
};
|
|
1900
|
+
return null;
|
|
1786
1901
|
}
|
|
1787
1902
|
/**
|
|
1788
|
-
*
|
|
1789
|
-
*
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
*
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1903
|
+
* Spawn the helper and pipe `text` over stdin. Fire-and-forget: we
|
|
1904
|
+
* don't await the child's exit because the OS clipboard is populated
|
|
1905
|
+
* synchronously by `pbcopy` / `clip.exe` / `wl-copy` / `xclip` once
|
|
1906
|
+
* stdin closes, and a typical drag-select is human-scale latency we
|
|
1907
|
+
* don't need to gate the next frame on.
|
|
1908
|
+
*
|
|
1909
|
+
* Returns `true` if the spawn looked plausibly successful, `false`
|
|
1910
|
+
* if we couldn't even attach to the child's stdin (binary missing,
|
|
1911
|
+
* permission denied, etc.). An `error` event after spawn flips the
|
|
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.
|
|
1798
1916
|
*/
|
|
1799
|
-
function
|
|
1800
|
-
const
|
|
1801
|
-
if (!
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
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
|
-
] });
|
|
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
|
+
}
|
|
1828
1933
|
}
|
|
1829
1934
|
/**
|
|
1830
|
-
*
|
|
1831
|
-
*
|
|
1832
|
-
*
|
|
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.
|
|
1833
1938
|
*/
|
|
1834
|
-
function
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
onMouseOver: () => setHovered(true),
|
|
1850
|
-
onMouseOut: () => setHovered(false),
|
|
1851
|
-
children: hintSpans(hint, COLOR, hovered)
|
|
1852
|
-
});
|
|
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
|
+
}
|
|
1949
|
+
}
|
|
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,
|
|
@@ -2893,8 +3047,73 @@ function StatusSpinner({ color }) {
|
|
|
2893
3047
|
fg: color,
|
|
2894
3048
|
children: useSpinnerFrame()
|
|
2895
3049
|
});
|
|
2896
|
-
}
|
|
2897
|
-
|
|
3050
|
+
}
|
|
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(() => {
|
|
3141
|
+
if (!searchQuery) return NO_SEARCH_MATCHES;
|
|
2922
3142
|
const direct = /* @__PURE__ */ new Set();
|
|
2923
3143
|
const callIds = /* @__PURE__ */ new Set();
|
|
2924
3144
|
const childIds = /* @__PURE__ */ new Set();
|
|
2925
|
-
if (!searchQuery) return {
|
|
2926
|
-
direct,
|
|
2927
|
-
callIds,
|
|
2928
|
-
childIds
|
|
2929
|
-
};
|
|
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)]);
|
|
@@ -19925,6 +20169,7 @@ function AppShell() {
|
|
|
19925
20169
|
});
|
|
19926
20170
|
agent.hooks.hook("agent:done", () => {
|
|
19927
20171
|
pendingAnnotationsRef.current.clear();
|
|
20172
|
+
pruneTodosByRun(session);
|
|
19928
20173
|
});
|
|
19929
20174
|
subscribeMcpToolsCache(agent.hooks, { dataDir });
|
|
19930
20175
|
return agent;
|
|
@@ -19991,7 +20236,9 @@ function AppShell() {
|
|
|
19991
20236
|
const cachedLive = liveSessionSearchIndexRef.current;
|
|
19992
20237
|
if (cachedLive?.sessionId === meta.id && cachedLive.updatedAt === meta.updatedAt && cachedLive.turnCount === live.turns.length) index = cachedLive.index;
|
|
19993
20238
|
else {
|
|
20239
|
+
const staleLive = liveSessionSearchIndexRef.current;
|
|
19994
20240
|
index = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(live.turns, abortController.signal);
|
|
20241
|
+
if (staleLive) historySearchWorkerRef.current?.release(staleLive.index);
|
|
19995
20242
|
liveSessionSearchIndexRef.current = {
|
|
19996
20243
|
sessionId: meta.id,
|
|
19997
20244
|
updatedAt: meta.updatedAt,
|
|
@@ -20006,15 +20253,25 @@ function AppShell() {
|
|
|
20006
20253
|
} else {
|
|
20007
20254
|
const turns = await store.getTurns(meta.id);
|
|
20008
20255
|
index = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
20256
|
+
historySearchWorkerRef.current?.slim(index);
|
|
20009
20257
|
const cache = sessionSearchCacheRef.current;
|
|
20258
|
+
const stale = cache.get(meta.id);
|
|
20259
|
+
if (stale) historySearchWorkerRef.current?.release(stale.index);
|
|
20010
20260
|
cache.set(meta.id, {
|
|
20011
20261
|
updatedAt: meta.updatedAt,
|
|
20012
20262
|
index
|
|
20013
20263
|
});
|
|
20014
|
-
|
|
20264
|
+
let totalBytes = 0;
|
|
20265
|
+
for (const entry of cache.values()) totalBytes += entry.index.bytes;
|
|
20266
|
+
while (cache.size > 1 && (cache.size > SESSION_SEARCH_INDEX_CACHE_MAX || totalBytes > SESSION_SEARCH_INDEX_CACHE_MAX_BYTES)) {
|
|
20015
20267
|
const oldest = cache.keys().next().value;
|
|
20016
20268
|
if (oldest === void 0) break;
|
|
20269
|
+
const evicted = cache.get(oldest);
|
|
20017
20270
|
cache.delete(oldest);
|
|
20271
|
+
if (evicted) {
|
|
20272
|
+
totalBytes -= evicted.index.bytes;
|
|
20273
|
+
historySearchWorkerRef.current?.release(evicted.index);
|
|
20274
|
+
}
|
|
20018
20275
|
}
|
|
20019
20276
|
}
|
|
20020
20277
|
if (generation !== sessionSearchGenerationRef.current) return;
|
|
@@ -20022,7 +20279,24 @@ function AppShell() {
|
|
|
20022
20279
|
if (match) matches.set(meta.id, match);
|
|
20023
20280
|
} catch (error) {
|
|
20024
20281
|
if (abortController.signal.aborted) throw abortController.signal.reason instanceof DOMException ? abortController.signal.reason : error;
|
|
20025
|
-
|
|
20282
|
+
const broken = sessionSearchCacheRef.current.get(meta.id);
|
|
20283
|
+
if (broken) {
|
|
20284
|
+
sessionSearchCacheRef.current.delete(meta.id);
|
|
20285
|
+
historySearchWorkerRef.current?.release(broken.index);
|
|
20286
|
+
try {
|
|
20287
|
+
const turns = await store.getTurns(meta.id);
|
|
20288
|
+
const retry = await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
20289
|
+
try {
|
|
20290
|
+
const match = await historySearchWorkerRef.current.history(retry, query, 3, abortController.signal);
|
|
20291
|
+
if (match) matches.set(meta.id, match);
|
|
20292
|
+
} finally {
|
|
20293
|
+
historySearchWorkerRef.current?.release(retry);
|
|
20294
|
+
}
|
|
20295
|
+
} catch (retryError) {
|
|
20296
|
+
if (abortController.signal.aborted) throw abortController.signal.reason instanceof DOMException ? abortController.signal.reason : retryError;
|
|
20297
|
+
debugLog("session content search retry failed", retryError);
|
|
20298
|
+
}
|
|
20299
|
+
} else debugLog("session content search skipped a session", error);
|
|
20026
20300
|
}
|
|
20027
20301
|
done++;
|
|
20028
20302
|
if (done === sessions.length || done % 4 === 0) setSessionContentSearchProgress({
|
|
@@ -21172,6 +21446,7 @@ function AppShell() {
|
|
|
21172
21446
|
const ctx = {
|
|
21173
21447
|
args: invocation.args,
|
|
21174
21448
|
argv: invocation.argv,
|
|
21449
|
+
invokedAs: invocation.invokedAs,
|
|
21175
21450
|
...attachments.length > 0 ? { attachments: attachments.map((a) => ({
|
|
21176
21451
|
name: a.name,
|
|
21177
21452
|
mediaType: a.mediaType,
|
|
@@ -21921,6 +22196,10 @@ function AppShell() {
|
|
|
21921
22196
|
const onLoginMcp = useCallback(async (name) => {
|
|
21922
22197
|
const entry = mcpsCatalogRef.current.find((d) => d.config.name === name);
|
|
21923
22198
|
if (!entry) return;
|
|
22199
|
+
for (const [other, ctrl] of mcpLoginAbortsRef.current) if (other !== name && !ctrl.signal.aborted) {
|
|
22200
|
+
debugLog(`mcp-login: refusing concurrent login for "${name}"`, `"${other}" is still authorizing`);
|
|
22201
|
+
return;
|
|
22202
|
+
}
|
|
21924
22203
|
mcpLoginAbortsRef.current.get(name)?.abort();
|
|
21925
22204
|
const ac = new AbortController();
|
|
21926
22205
|
mcpLoginAbortsRef.current.set(name, ac);
|
|
@@ -21932,6 +22211,7 @@ function AppShell() {
|
|
|
21932
22211
|
await loginMcpServer(entry.config, {
|
|
21933
22212
|
store: mcpCredentialStore,
|
|
21934
22213
|
signal: ac.signal,
|
|
22214
|
+
renderCallbackPage: renderDefaultCallbackPage,
|
|
21935
22215
|
hooks: agentRef.current?.hooks,
|
|
21936
22216
|
onAuthorizationUrl: (url) => tryOpenBrowser(url.toString())
|
|
21937
22217
|
});
|
|
@@ -22024,6 +22304,7 @@ function AppShell() {
|
|
|
22024
22304
|
(async () => {
|
|
22025
22305
|
const cached = transcriptSearchIndexRef.current;
|
|
22026
22306
|
const index = cached?.sessionId === session.id && cached.events === events ? cached.index : await (historySearchWorkerRef.current ??= new HistorySearchWorkerClient()).build(turns, abortController.signal);
|
|
22307
|
+
if (cached && cached.index !== index) historySearchWorkerRef.current?.release(cached.index);
|
|
22027
22308
|
transcriptSearchIndexRef.current = {
|
|
22028
22309
|
sessionId: session.id,
|
|
22029
22310
|
events,
|
|
@@ -22151,7 +22432,14 @@ function AppShell() {
|
|
|
22151
22432
|
store,
|
|
22152
22433
|
...source.projectRoot ? { projectRoot: source.projectRoot } : {}
|
|
22153
22434
|
});
|
|
22154
|
-
|
|
22435
|
+
const rehomed = await rehomePersistedStubs(slice, resolvePersistDir({
|
|
22436
|
+
userDir: cacheDir,
|
|
22437
|
+
sessionId: source.id
|
|
22438
|
+
}), resolvePersistDir({
|
|
22439
|
+
userDir: cacheDir,
|
|
22440
|
+
sessionId: fork.id
|
|
22441
|
+
}));
|
|
22442
|
+
fork.setTurns(rehomed);
|
|
22155
22443
|
fork.setRuns(inheritedRuns);
|
|
22156
22444
|
try {
|
|
22157
22445
|
await fork.save();
|
|
@@ -22164,7 +22452,8 @@ function AppShell() {
|
|
|
22164
22452
|
}, [
|
|
22165
22453
|
picked,
|
|
22166
22454
|
store,
|
|
22167
|
-
activateSession
|
|
22455
|
+
activateSession,
|
|
22456
|
+
cacheDir
|
|
22168
22457
|
]);
|
|
22169
22458
|
const onDeleteTurn = useCallback(async (turnId) => {
|
|
22170
22459
|
const session = sessionRef.current;
|
|
@@ -22508,9 +22797,7 @@ function AppShell() {
|
|
|
22508
22797
|
onExportSession,
|
|
22509
22798
|
keybindings
|
|
22510
22799
|
]);
|
|
22511
|
-
const
|
|
22512
|
-
const id = selectedTurnId;
|
|
22513
|
-
if (!id) return;
|
|
22800
|
+
const openTurnDetails = useCallback((id) => {
|
|
22514
22801
|
const session = sessionRef.current;
|
|
22515
22802
|
if (!session) return;
|
|
22516
22803
|
const turn = session.turns.find((t) => t.id === id);
|
|
@@ -22541,7 +22828,6 @@ function AppShell() {
|
|
|
22541
22828
|
}));
|
|
22542
22829
|
}, [
|
|
22543
22830
|
modal,
|
|
22544
|
-
selectedTurnId,
|
|
22545
22831
|
turnIds,
|
|
22546
22832
|
transcriptSearchMode,
|
|
22547
22833
|
transcriptSearchQuery,
|
|
@@ -22551,7 +22837,54 @@ function AppShell() {
|
|
|
22551
22837
|
onEditTurn,
|
|
22552
22838
|
keybindings
|
|
22553
22839
|
]);
|
|
22840
|
+
const openSelectedTurn = useCallback(() => {
|
|
22841
|
+
if (selectedTurnId) openTurnDetails(selectedTurnId);
|
|
22842
|
+
}, [selectedTurnId, openTurnDetails]);
|
|
22843
|
+
const onTranscriptSelectTurn = useCallback((id) => {
|
|
22844
|
+
if (!(transcriptSearchMode ? transcriptSearchTurnIds : turnIds).includes(id)) return false;
|
|
22845
|
+
setSelectedTurnId(id);
|
|
22846
|
+
return true;
|
|
22847
|
+
}, [
|
|
22848
|
+
transcriptSearchMode,
|
|
22849
|
+
transcriptSearchTurnIds,
|
|
22850
|
+
turnIds
|
|
22851
|
+
]);
|
|
22852
|
+
const onTranscriptOpenTurn = useCallback((id) => {
|
|
22853
|
+
if (!(transcriptSearchMode ? transcriptSearchTurnIds : turnIds).includes(id)) return false;
|
|
22854
|
+
setSelectedTurnId(id);
|
|
22855
|
+
openTurnDetails(id);
|
|
22856
|
+
return true;
|
|
22857
|
+
}, [
|
|
22858
|
+
transcriptSearchMode,
|
|
22859
|
+
transcriptSearchTurnIds,
|
|
22860
|
+
turnIds,
|
|
22861
|
+
openTurnDetails
|
|
22862
|
+
]);
|
|
22554
22863
|
useKeyboard((key) => {
|
|
22864
|
+
if (showQuitConfirmRef.current) {
|
|
22865
|
+
if (key.name === "escape") {
|
|
22866
|
+
setShowQuitConfirm(false);
|
|
22867
|
+
setQuitConfirmSelected("cancel");
|
|
22868
|
+
return;
|
|
22869
|
+
}
|
|
22870
|
+
if (key.name === "c" && key.ctrl && !key.shift) {
|
|
22871
|
+
renderer.destroy();
|
|
22872
|
+
return;
|
|
22873
|
+
}
|
|
22874
|
+
if (key.name === "return") {
|
|
22875
|
+
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
22876
|
+
else {
|
|
22877
|
+
setShowQuitConfirm(false);
|
|
22878
|
+
setQuitConfirmSelected("cancel");
|
|
22879
|
+
}
|
|
22880
|
+
return;
|
|
22881
|
+
}
|
|
22882
|
+
if (key.name === "tab" || key.name === "left" || key.name === "right") {
|
|
22883
|
+
setQuitConfirmSelected((prev) => prev === "cancel" ? "quit" : "cancel");
|
|
22884
|
+
return;
|
|
22885
|
+
}
|
|
22886
|
+
return;
|
|
22887
|
+
}
|
|
22555
22888
|
if (modal.isOpen) return;
|
|
22556
22889
|
if (transcriptSearchMode && screen === "chat") {
|
|
22557
22890
|
if (key.name === "escape") onTranscriptSearchModeChange(false);
|
|
@@ -22763,7 +23096,7 @@ function AppShell() {
|
|
|
22763
23096
|
}));
|
|
22764
23097
|
return;
|
|
22765
23098
|
}
|
|
22766
|
-
if (key.ctrl && !key.shift && !key.meta && (key.name === "c" || key.name === "x")
|
|
23099
|
+
if (key.ctrl && !key.shift && !key.meta && (key.name === "c" || key.name === "x")) {
|
|
22767
23100
|
const editor = renderer.currentFocusedEditor;
|
|
22768
23101
|
if (editor?.hasSelection()) {
|
|
22769
23102
|
const text = editor.getSelectedText();
|
|
@@ -22772,30 +23105,6 @@ function AppShell() {
|
|
|
22772
23105
|
return;
|
|
22773
23106
|
}
|
|
22774
23107
|
}
|
|
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
23108
|
if (key.name === "c" && key.ctrl && !key.shift) {
|
|
22800
23109
|
const mode = settingsRef.current.ctrlCBehavior;
|
|
22801
23110
|
if (mode === "quit") {
|
|
@@ -23049,6 +23358,8 @@ function AppShell() {
|
|
|
23049
23358
|
enterSelectsCompletion: (providerId) => providerId === "chats",
|
|
23050
23359
|
onPopupOpenChange,
|
|
23051
23360
|
selectedTurnId,
|
|
23361
|
+
onSelectTurn: onTranscriptSelectTurn,
|
|
23362
|
+
onOpenTurn: onTranscriptOpenTurn,
|
|
23052
23363
|
transcriptSearchMode,
|
|
23053
23364
|
transcriptSearchQuery,
|
|
23054
23365
|
transcriptSearchEvents,
|
|
@@ -23072,15 +23383,16 @@ function AppShell() {
|
|
|
23072
23383
|
status: transcriptSearchMode && transcriptSearchSearching ? "searching" : pendingInteraction?.kind === "question" ? "asking" : compacting ? "compacting" : retryInfo && busy ? "retrying" : busy ? "busy" : null,
|
|
23073
23384
|
compactionTokens,
|
|
23074
23385
|
compactionStartedAt,
|
|
23075
|
-
retry: retryInfo
|
|
23386
|
+
retry: retryInfo,
|
|
23387
|
+
contextKey: keybindings.openContextPanel
|
|
23076
23388
|
}),
|
|
23077
23389
|
showQuitConfirm && /* @__PURE__ */ jsxs("box", {
|
|
23078
23390
|
position: "absolute",
|
|
23079
23391
|
top: "50%",
|
|
23080
23392
|
left: "50%",
|
|
23081
23393
|
marginTop: -3,
|
|
23082
|
-
marginLeft: -
|
|
23083
|
-
width:
|
|
23394
|
+
marginLeft: -21,
|
|
23395
|
+
width: 42,
|
|
23084
23396
|
height: 7,
|
|
23085
23397
|
borderStyle: "rounded",
|
|
23086
23398
|
borderColor: COLOR.warn,
|
|
@@ -23089,35 +23401,52 @@ function AppShell() {
|
|
|
23089
23401
|
alignItems: "center",
|
|
23090
23402
|
justifyContent: "center",
|
|
23091
23403
|
gap: 1,
|
|
23092
|
-
children: [
|
|
23093
|
-
|
|
23094
|
-
|
|
23095
|
-
|
|
23096
|
-
|
|
23097
|
-
|
|
23098
|
-
|
|
23099
|
-
event
|
|
23100
|
-
|
|
23101
|
-
|
|
23404
|
+
children: [
|
|
23405
|
+
/* @__PURE__ */ jsx$1("text", { children: "Quit zidane?" }),
|
|
23406
|
+
/* @__PURE__ */ jsxs("box", {
|
|
23407
|
+
flexDirection: "row",
|
|
23408
|
+
gap: 2,
|
|
23409
|
+
children: [/* @__PURE__ */ jsx$1("box", {
|
|
23410
|
+
backgroundColor: quitConfirmSelected === "cancel" ? COLOR.accent : void 0,
|
|
23411
|
+
onMouseDown: (event) => {
|
|
23412
|
+
event.stopPropagation();
|
|
23413
|
+
event.preventDefault();
|
|
23414
|
+
if (quitConfirmSelectedRef.current === "cancel") setShowQuitConfirm(false);
|
|
23415
|
+
else setQuitConfirmSelected("cancel");
|
|
23416
|
+
},
|
|
23417
|
+
children: /* @__PURE__ */ jsx$1("text", {
|
|
23418
|
+
fg: quitConfirmSelected === "cancel" ? SURFACE.modal : COLOR.dim,
|
|
23419
|
+
children: " Cancel "
|
|
23420
|
+
})
|
|
23421
|
+
}), /* @__PURE__ */ jsx$1("box", {
|
|
23422
|
+
backgroundColor: quitConfirmSelected === "quit" ? COLOR.warn : void 0,
|
|
23423
|
+
onMouseDown: (event) => {
|
|
23424
|
+
event.stopPropagation();
|
|
23425
|
+
event.preventDefault();
|
|
23426
|
+
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
23427
|
+
else setQuitConfirmSelected("quit");
|
|
23428
|
+
},
|
|
23429
|
+
children: /* @__PURE__ */ jsx$1("text", {
|
|
23430
|
+
fg: quitConfirmSelected === "quit" ? SURFACE.modal : COLOR.dim,
|
|
23431
|
+
children: " Quit "
|
|
23432
|
+
})
|
|
23433
|
+
})]
|
|
23434
|
+
}),
|
|
23435
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
23436
|
+
{
|
|
23437
|
+
key: "tab",
|
|
23438
|
+
label: "switch"
|
|
23102
23439
|
},
|
|
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");
|
|
23440
|
+
{
|
|
23441
|
+
key: "↵",
|
|
23442
|
+
label: "confirm"
|
|
23114
23443
|
},
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
}
|
|
23119
|
-
})
|
|
23120
|
-
|
|
23444
|
+
{
|
|
23445
|
+
key: "esc",
|
|
23446
|
+
label: "cancel"
|
|
23447
|
+
}
|
|
23448
|
+
] })
|
|
23449
|
+
]
|
|
23121
23450
|
})
|
|
23122
23451
|
]
|
|
23123
23452
|
});
|
|
@@ -23534,30 +23863,27 @@ function EffortPickerModal({ current, supportsAdaptive, onPick }) {
|
|
|
23534
23863
|
highlightBg: SURFACE.selection
|
|
23535
23864
|
}, level.id))
|
|
23536
23865
|
}),
|
|
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
|
-
})
|
|
23866
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
23867
|
+
{
|
|
23868
|
+
key: "↑↓",
|
|
23869
|
+
label: "navigate",
|
|
23870
|
+
labelColor: COLOR.dim
|
|
23871
|
+
},
|
|
23872
|
+
{
|
|
23873
|
+
key: "↵",
|
|
23874
|
+
label: "select",
|
|
23875
|
+
labelColor: COLOR.dim
|
|
23876
|
+
},
|
|
23877
|
+
{
|
|
23878
|
+
key: "esc",
|
|
23879
|
+
label: "close",
|
|
23880
|
+
labelColor: COLOR.dim
|
|
23881
|
+
},
|
|
23882
|
+
{
|
|
23883
|
+
key: "",
|
|
23884
|
+
label: `${filtered.length} / ${levels.length} level${levels.length === 1 ? "" : "s"}`
|
|
23885
|
+
}
|
|
23886
|
+
] })
|
|
23561
23887
|
]
|
|
23562
23888
|
});
|
|
23563
23889
|
}
|
|
@@ -23648,20 +23974,13 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
|
|
|
23648
23974
|
children: [
|
|
23649
23975
|
preamble,
|
|
23650
23976
|
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
|
-
})
|
|
23977
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [onRefresh && {
|
|
23978
|
+
key: "ctrl+r",
|
|
23979
|
+
label: "refresh"
|
|
23980
|
+
}, {
|
|
23981
|
+
key: "esc",
|
|
23982
|
+
label: "close"
|
|
23983
|
+
}] })
|
|
23665
23984
|
]
|
|
23666
23985
|
});
|
|
23667
23986
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
@@ -23697,30 +24016,24 @@ function ToggleListModal({ catalog, keyOf, settingKey, gcKeys, title, renderDeta
|
|
|
23697
24016
|
}, name);
|
|
23698
24017
|
})
|
|
23699
24018
|
}),
|
|
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
|
-
})
|
|
24019
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
24020
|
+
{
|
|
24021
|
+
key: "↑↓",
|
|
24022
|
+
label: "navigate"
|
|
24023
|
+
},
|
|
24024
|
+
{
|
|
24025
|
+
key: "↵",
|
|
24026
|
+
label: "toggle"
|
|
24027
|
+
},
|
|
24028
|
+
onRefresh && {
|
|
24029
|
+
key: "ctrl+r",
|
|
24030
|
+
label: "refresh"
|
|
24031
|
+
},
|
|
24032
|
+
{
|
|
24033
|
+
key: "esc",
|
|
24034
|
+
label: "close"
|
|
24035
|
+
}
|
|
24036
|
+
] })
|
|
23724
24037
|
]
|
|
23725
24038
|
});
|
|
23726
24039
|
}
|
|
@@ -24030,20 +24343,13 @@ function McpsSettingsModal({ catalog, errors, toolsByServer, onLogin, onLogout,
|
|
|
24030
24343
|
children: " { \"mcpServers\": { ... } } "
|
|
24031
24344
|
}), "wrapper from Claude Code / Cursor also works."]
|
|
24032
24345
|
}),
|
|
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
|
-
})
|
|
24346
|
+
/* @__PURE__ */ jsx$1(ModalHints, { hints: [onRefresh && {
|
|
24347
|
+
key: "r",
|
|
24348
|
+
label: "refresh"
|
|
24349
|
+
}, {
|
|
24350
|
+
key: "esc",
|
|
24351
|
+
label: "close"
|
|
24352
|
+
}] })
|
|
24047
24353
|
]
|
|
24048
24354
|
});
|
|
24049
24355
|
const groups = [];
|
|
@@ -24098,8 +24404,7 @@ function McpsSettingsModal({ catalog, errors, toolsByServer, onLogin, onLogout,
|
|
|
24098
24404
|
focusedEntry,
|
|
24099
24405
|
focusedStatus,
|
|
24100
24406
|
expanded,
|
|
24101
|
-
showRefresh: !!onRefresh
|
|
24102
|
-
COLOR
|
|
24407
|
+
showRefresh: !!onRefresh
|
|
24103
24408
|
})
|
|
24104
24409
|
]
|
|
24105
24410
|
});
|
|
@@ -24330,7 +24635,7 @@ function renderDetailPanel(entry, status, COLOR) {
|
|
|
24330
24635
|
});
|
|
24331
24636
|
return null;
|
|
24332
24637
|
}
|
|
24333
|
-
function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded, showRefresh
|
|
24638
|
+
function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded, showRefresh }) {
|
|
24334
24639
|
const effectiveStatus = focusedStatus ?? { kind: "idle" };
|
|
24335
24640
|
const onServerRow = focusedRow?.kind === "server";
|
|
24336
24641
|
const canL = onServerRow && focusedEntry ? canLogin(focusedEntry, effectiveStatus) : false;
|
|
@@ -24338,77 +24643,44 @@ function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded,
|
|
|
24338
24643
|
const canCancel = effectiveStatus.kind === "authorizing";
|
|
24339
24644
|
const canExpand = onServerRow && focusedEntry ? !expanded.has(focusedEntry.config.name) : false;
|
|
24340
24645
|
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
|
-
});
|
|
24646
|
+
return /* @__PURE__ */ jsx$1(ModalHints, { hints: [
|
|
24647
|
+
{
|
|
24648
|
+
key: "↑↓",
|
|
24649
|
+
label: "navigate"
|
|
24650
|
+
},
|
|
24651
|
+
{
|
|
24652
|
+
key: "↵",
|
|
24653
|
+
label: "toggle"
|
|
24654
|
+
},
|
|
24655
|
+
canExpand && {
|
|
24656
|
+
key: "→",
|
|
24657
|
+
label: "expand"
|
|
24658
|
+
},
|
|
24659
|
+
canCollapse && {
|
|
24660
|
+
key: "←",
|
|
24661
|
+
label: "collapse"
|
|
24662
|
+
},
|
|
24663
|
+
canL && {
|
|
24664
|
+
key: "l",
|
|
24665
|
+
label: "login"
|
|
24666
|
+
},
|
|
24667
|
+
canO && {
|
|
24668
|
+
key: "o",
|
|
24669
|
+
label: "logout"
|
|
24670
|
+
},
|
|
24671
|
+
showRefresh && {
|
|
24672
|
+
key: "r",
|
|
24673
|
+
label: "refresh"
|
|
24674
|
+
},
|
|
24675
|
+
canCancel && {
|
|
24676
|
+
key: "esc",
|
|
24677
|
+
label: "cancel"
|
|
24678
|
+
},
|
|
24679
|
+
!canCancel && {
|
|
24680
|
+
key: "esc",
|
|
24681
|
+
label: "close"
|
|
24682
|
+
}
|
|
24683
|
+
] });
|
|
24412
24684
|
}
|
|
24413
24685
|
function renderErrors(errors, home, warnColor) {
|
|
24414
24686
|
if (!errors || errors.length === 0) return null;
|
|
@@ -24633,6 +24905,7 @@ async function runTui(options = {}) {
|
|
|
24633
24905
|
debounceDelay: 0,
|
|
24634
24906
|
targetFps: bootFps,
|
|
24635
24907
|
maxFps: bootFps,
|
|
24908
|
+
useMouse: config.initialSettings.mouseInteractions ?? DEFAULT_SETTINGS.mouseInteractions,
|
|
24636
24909
|
gatherStats
|
|
24637
24910
|
});
|
|
24638
24911
|
bootTick("runTui:after-createCliRenderer");
|