zidane 5.6.7 → 5.6.9
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/{agent-D70rr6Uk.d.ts → agent-CZEvtmJk.d.ts} +45 -2
- package/dist/agent-CZEvtmJk.d.ts.map +1 -0
- package/dist/chat.d.ts +261 -5
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -3
- package/dist/{index-BjwwNjQd.d.ts → index-BBH6XWFt.d.ts} +2 -2
- package/dist/{index-BjwwNjQd.d.ts.map → index-BBH6XWFt.d.ts.map} +1 -1
- package/dist/{index-8mn3PIaa.d.ts → index-Cf-131kQ.d.ts} +2 -2
- package/dist/index-Cf-131kQ.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -7
- package/dist/{login-Btpliwct.js → login-CwLWX6vP.js} +17 -37
- package/dist/login-CwLWX6vP.js.map +1 -0
- package/dist/{mcp-ngMS0S6N.js → mcp-Wzf0qxaj.js} +120 -26
- package/dist/mcp-Wzf0qxaj.js.map +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-B5k4DAXy.js → messages-BfmXLDT4.js} +56 -2
- package/dist/messages-BfmXLDT4.js.map +1 -0
- package/dist/{presets-BXmWG3kd.js → presets-DAA0NaK_.js} +2 -2
- package/dist/{presets-BXmWG3kd.js.map → presets-DAA0NaK_.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-CaJE2ToS.js → providers-C_ahnRBS.js} +2 -2
- package/dist/{providers-CaJE2ToS.js.map → providers-C_ahnRBS.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-BoEW_wCR.js → session-PUzXZlG6.js} +2 -2
- package/dist/{session-BoEW_wCR.js.map → session-PUzXZlG6.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/{tools-FerA0zSl.js → tools-B9aQpZVx.js} +6 -4
- package/dist/tools-B9aQpZVx.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-C5Sp1Snh.d.ts → transcript-anchors-DQzjiKxZ.d.ts} +78 -4
- package/dist/transcript-anchors-DQzjiKxZ.d.ts.map +1 -0
- package/dist/tui.d.ts +47 -14
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +853 -199
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-D-OQYUgS.js → turn-operations-Bsmu9-qU.js} +404 -15
- package/dist/turn-operations-Bsmu9-qU.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/docs/CHAT.md +4 -1
- package/docs/SKILL.md +1 -0
- package/docs/TUI.md +1 -0
- package/package.json +1 -1
- package/dist/agent-D70rr6Uk.d.ts.map +0 -1
- package/dist/index-8mn3PIaa.d.ts.map +0 -1
- package/dist/login-Btpliwct.js.map +0 -1
- package/dist/mcp-ngMS0S6N.js.map +0 -1
- package/dist/messages-B5k4DAXy.js.map +0 -1
- package/dist/tools-FerA0zSl.js.map +0 -1
- package/dist/transcript-anchors-C5Sp1Snh.d.ts.map +0 -1
- package/dist/turn-operations-D-OQYUgS.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as resolvePersistDir, B as formatTaskStatus, H as previewLine, I as ageString, L as compactPath, O as cleanupPersistedSession, R as fmtTokens, U as shortId, V as formatTaskSummary, j as resolveTasksDir, p as createAgent, z as formatDuration } from "./tools-
|
|
1
|
+
import { $n as ensureKeybindingsFile, A as getSafelist, At as clipHintsToWidth, B as oauthUsesManualCodePaste, Bi as buildPlanSystem, Bt as SETTINGS_CATEGORIES, Cn as stripSpawnTokensLine, Cr as bootTick, D as useSafeModeQueue, Dn as toolResultText, Dt as useInteractionsActions, E as useSafeModeActions, En as toolCallPreview, F as suggestSafelistEntry, Fn as extractEditPayload, Fr as shouldAutoCompact, Ft as buildHints, G as indexOfEntry, Gn as resolveApprovalForPayload, Gt as useSettings, H as supportsOAuth, Hr as setProviderCredential, Ht as SETTINGS_TOGGLES, In as filetypeFromPath, Ir as detectAuth, J as discoverProjectMcps, Jn as summarizeOutcomes, Jt as resolveChipColor, K as buildMcpServers, Kn as rewriteMultiEditHeader, L as splitPromptSegments, Ln as previewEditPayload, Lt as listProjectFiles, Mn as buildUnifiedDiff, Mt as truncateTrailing, Oi as useActiveTodos, On as turnSelectionOwnership, Ot as useInteractionsQueue, Pr as AUTO_COMPACT_MIN_GROWTH_FRACTION, Pt as generateSessionTitle, Q as loadMcpToolsCache, Qn as KEYBINDING_KEY_COL_WIDTH, Qr as modelSupportsReasoning, R as formatPathForCwd, Rt as useEnabledToggleSet, Sn as selectableTurnIds, St as createInteractionTools, T as SafeModeProvider, Tr as useUpdateCheck, Tt as pendingInteractionsFromTurns, U as buildModelCatalog, Un as mergeApprovalAndBodyOutcomes, Ut as SettingsProvider, V as runOAuthLogin, Vi as envSection, Vn as buildEditOutcomesAnnotation, Vt as SETTINGS_CHOICES, W as filterModelCatalog, Wn as parseEditOutcomesFromResult, Wt as clampFps, Xn as KEYBINDING_DEFS, Xr as getContextWindow, Yt as resolveTheme, _ as turnContextSize, _n as lastContextSizeFromTurns, _t as splitMarkdownCodeBlocks, a as computeTurnAnchors, ai as findGitRoot, an as ConfigProvider, b as defaultSkillScanPaths, bn as marginTopFor, br as buildLinearRamp, c as formatToolCall, ct as parentServerName, d as useSelectStyle, er as formatBindingForDisplay, et as refreshMcpToolsCatalog, f as useSurfaces, fi as accentColor, fn as deriveSessionTitle, fr as createFilesCompletionProvider, ft as McpAuthProvider, g as finalizeStreamingMarkdownForOwner, gn as isVisible, h as finalizeStreamingMarkdown, hn as isTurnHighlighted, ht as getMcpAuthStatus, i as turnAsText, in as useDiscoveryOptional, it as useMcpToolToggleMap, j as isOnSafelist, jn as buildContextualDiff, jt as hintsLength, k as addToSafelist, kn as updateToolEventOutcomes, kt as EMPTY_HINTS, l as ThemeProvider, lr as createSkillsCompletionProvider, lt as createFileMcpCredentialStore, m as useTheme, mn as isEditErrorResult, mt as useMcpAuthState, n as deleteTurnSafely, ni as piIdOf, nn as DiscoveryProvider, nr as keybindingsPath, nt as subscribeMcpToolsCache, o as TOOL_DISPLAY, on as useConfig, ot as buildVisibleMcpRows, pn as eventsFromTurns, pt as useMcpAuthDispatch, qn as stripEditOutcomesAnnotation, r as truncateTurnsAt, ri as discoverAgentsMd, rn as useDiscovery, rr as matchesBinding, s as displayNameFor, sn as resolveConfig, st as indexOfServerRow, tn as createDiscoverySlot, tr as groupBindings, u as useColors, un as EDIT_TOOL_NAMES, ur as uniqueSkillNamesFromReferences, v as useStreamBuffer, vi as TODO_STATUS_GLYPHS, vn as listSessionMeta, vr as useCompletion, w as writeSessionExport, wn as sumRunCosts, wr as buildUpdateHint, wt as makeRequestInteraction, x as discoverProjectSkills, xr as tryOpenBrowser, xt as buildResumedToolResultsTurn, y as buildSkillsConfig, yr as blendHsl, yt as InteractionsProvider, z as fetchOAuthRedirect, zi as buildBuildSystem, zn as summarizeEditPayload, zt as DEFAULT_SETTINGS } from "./turn-operations-Bsmu9-qU.js";
|
|
2
|
+
import { A as resolvePersistDir, B as formatTaskStatus, H as previewLine, I as ageString, L as compactPath, O as cleanupPersistedSession, R as fmtTokens, U as shortId, V as formatTaskSummary, j as resolveTasksDir, p as createAgent, z as formatDuration } from "./tools-B9aQpZVx.js";
|
|
3
3
|
import { n as createProcessContext } from "./contexts-BOtMvzli.js";
|
|
4
4
|
import { c as errorMessage } from "./errors-DdZXnyXE.js";
|
|
5
|
-
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-
|
|
6
|
-
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-
|
|
5
|
+
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-Wzf0qxaj.js";
|
|
6
|
+
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-CwLWX6vP.js";
|
|
7
7
|
import { n as formatTokenUsage } from "./stats-Lc3zL3RM.js";
|
|
8
|
-
import { n as loadSession, t as createSession } from "./session-
|
|
8
|
+
import { n as loadSession, t as createSession } from "./session-PUzXZlG6.js";
|
|
9
9
|
import { createTuiStore } from "./session/sqlite.js";
|
|
10
10
|
import { basename, join, relative } from "node:path";
|
|
11
11
|
import { homedir } from "node:os";
|
|
@@ -1967,11 +1967,40 @@ function makeMarkdownRenderNode(bag) {
|
|
|
1967
1967
|
wrapper.marginTop = topMargin;
|
|
1968
1968
|
return wrapper;
|
|
1969
1969
|
}
|
|
1970
|
+
if (inner instanceof CodeRenderable) pinContentTrim(inner);
|
|
1970
1971
|
inner.marginTop = topMargin;
|
|
1971
1972
|
return inner;
|
|
1972
1973
|
};
|
|
1973
1974
|
}
|
|
1974
1975
|
/**
|
|
1976
|
+
* Install an instance-level `content` accessor on a prose
|
|
1977
|
+
* {@link CodeRenderable} that strips trailing newlines before
|
|
1978
|
+
* delegating to the prototype setter. Runs once per block on
|
|
1979
|
+
* creation; subsequent markdown delta passes call
|
|
1980
|
+
* `applyMarkdownCodeRenderable` which writes `token.raw` directly into
|
|
1981
|
+
* `.content` and re-enters our setter, so updates stay trimmed too.
|
|
1982
|
+
*
|
|
1983
|
+
* Idempotent — re-applies cleanly if a block is recreated on a
|
|
1984
|
+
* type-change (which destroys + re-creates the renderable anyway).
|
|
1985
|
+
*/
|
|
1986
|
+
function pinContentTrim(renderable) {
|
|
1987
|
+
const desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(renderable), "content");
|
|
1988
|
+
if (!desc?.set || !desc.get) return;
|
|
1989
|
+
const protoSet = desc.set;
|
|
1990
|
+
const protoGet = desc.get;
|
|
1991
|
+
Object.defineProperty(renderable, "content", {
|
|
1992
|
+
get() {
|
|
1993
|
+
return protoGet.call(this);
|
|
1994
|
+
},
|
|
1995
|
+
set(value) {
|
|
1996
|
+
const next = typeof value === "string" ? value.replace(/\n+$/, "") : value;
|
|
1997
|
+
protoSet.call(this, next);
|
|
1998
|
+
},
|
|
1999
|
+
configurable: true
|
|
2000
|
+
});
|
|
2001
|
+
renderable.content = renderable.content;
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
1975
2004
|
* Pull the trailing block index out of an OpenTUI renderable id. Returns
|
|
1976
2005
|
* `0` for any id that doesn't match the expected `…-block-N` shape — the
|
|
1977
2006
|
* spacing rule then treats unparseable ids as "first block" (no
|
|
@@ -2428,6 +2457,7 @@ function ToolCallBlock({ event, display, dim }) {
|
|
|
2428
2457
|
});
|
|
2429
2458
|
return /* @__PURE__ */ jsxs("text", {
|
|
2430
2459
|
fg: dim ? COLOR.dim : COLOR.model,
|
|
2460
|
+
wrapMode: "none",
|
|
2431
2461
|
children: [
|
|
2432
2462
|
/* @__PURE__ */ jsx("span", {
|
|
2433
2463
|
fg: COLOR.mute,
|
|
@@ -2807,6 +2837,7 @@ function CwdPickerModal({ currentCwd, onPick }) {
|
|
|
2807
2837
|
*/
|
|
2808
2838
|
const FILES_REFRESH_THROTTLE_MS = 3e3;
|
|
2809
2839
|
const SKILLS_REFRESH_THROTTLE_MS = 3e4;
|
|
2840
|
+
const MCP_TOOLS_CACHE_POLL_MS = 4e3;
|
|
2810
2841
|
function debugLog$1(...args) {
|
|
2811
2842
|
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/tui] ${args.map(errorMessage).join(" ")}\n`);
|
|
2812
2843
|
}
|
|
@@ -2822,6 +2853,7 @@ function DiscoveryShell({ children }) {
|
|
|
2822
2853
|
const [mcpsCatalog, setMcpsCatalog] = useState([]);
|
|
2823
2854
|
const [mcpsErrors, setMcpsErrors] = useState([]);
|
|
2824
2855
|
const [filesCatalog, setFilesCatalog] = useState([]);
|
|
2856
|
+
const [mcpToolsByServer, setMcpToolsByServer] = useState(() => readToolsByServer(dataDir));
|
|
2825
2857
|
const filesSlotRef = useRef(null);
|
|
2826
2858
|
const skillsSlotRef = useRef(null);
|
|
2827
2859
|
useEffect(() => {
|
|
@@ -2923,11 +2955,22 @@ function DiscoveryShell({ children }) {
|
|
|
2923
2955
|
config.prefix,
|
|
2924
2956
|
mcpCredentialStore
|
|
2925
2957
|
]);
|
|
2958
|
+
useEffect(() => {
|
|
2959
|
+
const id = setInterval(() => {
|
|
2960
|
+
try {
|
|
2961
|
+
setMcpToolsByServer(readToolsByServer(dataDir));
|
|
2962
|
+
} catch (err) {
|
|
2963
|
+
debugLog$1("mcp-tools-cache refresh failed", err);
|
|
2964
|
+
}
|
|
2965
|
+
}, MCP_TOOLS_CACHE_POLL_MS);
|
|
2966
|
+
return () => clearInterval(id);
|
|
2967
|
+
}, [dataDir]);
|
|
2926
2968
|
return /* @__PURE__ */ jsx(DiscoveryProvider, {
|
|
2927
2969
|
value: useMemo(() => ({
|
|
2928
2970
|
skillsCatalog,
|
|
2929
2971
|
mcpsCatalog,
|
|
2930
2972
|
mcpsErrors,
|
|
2973
|
+
mcpToolsByServer,
|
|
2931
2974
|
filesCatalog,
|
|
2932
2975
|
refreshSkills,
|
|
2933
2976
|
refreshMcps,
|
|
@@ -2938,6 +2981,7 @@ function DiscoveryShell({ children }) {
|
|
|
2938
2981
|
skillsCatalog,
|
|
2939
2982
|
mcpsCatalog,
|
|
2940
2983
|
mcpsErrors,
|
|
2984
|
+
mcpToolsByServer,
|
|
2941
2985
|
filesCatalog,
|
|
2942
2986
|
refreshSkills,
|
|
2943
2987
|
refreshMcps,
|
|
@@ -2948,6 +2992,19 @@ function DiscoveryShell({ children }) {
|
|
|
2948
2992
|
children
|
|
2949
2993
|
});
|
|
2950
2994
|
}
|
|
2995
|
+
/**
|
|
2996
|
+
* Read the on-disk MCP tools cache into a `serverName → tools` map.
|
|
2997
|
+
* Strips the per-entry timestamp / transport metadata that the cache
|
|
2998
|
+
* file keeps for diagnostics; consumers downstream of the context
|
|
2999
|
+
* only need the schemas. Best-effort — a corrupt cache produces an
|
|
3000
|
+
* empty map and the next bootstrap re-populates.
|
|
3001
|
+
*/
|
|
3002
|
+
function readToolsByServer(dataDir) {
|
|
3003
|
+
const cache = loadMcpToolsCache({ dataDir });
|
|
3004
|
+
const out = {};
|
|
3005
|
+
for (const [name, entry] of Object.entries(cache)) out[name] = entry.tools;
|
|
3006
|
+
return out;
|
|
3007
|
+
}
|
|
2951
3008
|
//#endregion
|
|
2952
3009
|
//#region src/tui/effort-picker.tsx
|
|
2953
3010
|
const BASE_LEVELS = [
|
|
@@ -6876,6 +6933,8 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onPopupOpenCh
|
|
|
6876
6933
|
const focused = useModalAwareFocus();
|
|
6877
6934
|
const COLOR = useColors();
|
|
6878
6935
|
const SURFACE = useSurfaces();
|
|
6936
|
+
const { settings } = useSettings();
|
|
6937
|
+
const attachmentsEnabled = settings.attachmentsEnabled;
|
|
6879
6938
|
const textareaRef = useRef(null);
|
|
6880
6939
|
/** Auto-grow: visible content rows the textarea currently occupies (clamped 1..5). */
|
|
6881
6940
|
const [contentLines, setContentLines] = useState(MIN_CONTENT_LINES);
|
|
@@ -6965,7 +7024,7 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onPopupOpenCh
|
|
|
6965
7024
|
if (!ta) return;
|
|
6966
7025
|
const normalized = stripAnsiSequences(decodePasteBytes(event.bytes)).replace(/\r\n?/g, "\n");
|
|
6967
7026
|
if (normalized.length === 0) return;
|
|
6968
|
-
if (!normalized.includes("\n")) {
|
|
7027
|
+
if (attachmentsEnabled && !normalized.includes("\n")) {
|
|
6969
7028
|
const trimmed = normalized.trim().replace(/^["']|["']$/g, "");
|
|
6970
7029
|
let resolved = trimmed.startsWith("file://") ? decodeURIComponent(trimmed.slice(7)) : trimmed;
|
|
6971
7030
|
if (resolved.startsWith("~/")) resolved = (process.env.HOME ?? "") + resolved.slice(1);
|
|
@@ -6983,7 +7042,7 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onPopupOpenCh
|
|
|
6983
7042
|
}
|
|
6984
7043
|
} catch {}
|
|
6985
7044
|
}
|
|
6986
|
-
if (normalized.includes("\n")) {
|
|
7045
|
+
if (attachmentsEnabled && normalized.includes("\n")) {
|
|
6987
7046
|
setAttachments((prev) => [...prev, {
|
|
6988
7047
|
name: "paste.txt",
|
|
6989
7048
|
content: Buffer.from(normalized, "utf-8"),
|
|
@@ -6998,7 +7057,7 @@ function PromptBlock({ userPrompts, onSubmit, completionProviders, onPopupOpenCh
|
|
|
6998
7057
|
if (part && part.length > 0) ta.insertText(part);
|
|
6999
7058
|
}
|
|
7000
7059
|
syncBuffer();
|
|
7001
|
-
}, [syncBuffer]);
|
|
7060
|
+
}, [syncBuffer, attachmentsEnabled]);
|
|
7002
7061
|
const cycleHistory = useCallback((direction) => {
|
|
7003
7062
|
if (userPrompts.length === 0 || !textareaRef.current) return;
|
|
7004
7063
|
if (historyRef.current === null) historyRef.current = {
|
|
@@ -7964,6 +8023,13 @@ function statusColor(status, COLOR) {
|
|
|
7964
8023
|
}
|
|
7965
8024
|
//#endregion
|
|
7966
8025
|
//#region src/tui/settings-modal.tsx
|
|
8026
|
+
/**
|
|
8027
|
+
* Stable empty default for `mcpToolsByServer` — using a fresh `{}` per
|
|
8028
|
+
* render would invalidate `useMemo` deps that reference it (and
|
|
8029
|
+
* needlessly re-run filtered / visible-row computation when the cache
|
|
8030
|
+
* is cold).
|
|
8031
|
+
*/
|
|
8032
|
+
const EMPTY_TOOLS = Object.freeze({});
|
|
7967
8033
|
const TAB_LABELS = {
|
|
7968
8034
|
...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, c.label])),
|
|
7969
8035
|
keybindings: "Keybindings",
|
|
@@ -7979,6 +8045,29 @@ function anchorIdFor(index) {
|
|
|
7979
8045
|
}
|
|
7980
8046
|
const COL_TITLE = " ";
|
|
7981
8047
|
const SPACER_CHECKBOX_WIDTH = " ";
|
|
8048
|
+
/**
|
|
8049
|
+
* Truncate `s` to `maxCols` terminal columns, appending an ellipsis
|
|
8050
|
+
* when it overflows. Used by every settings list to keep long
|
|
8051
|
+
* descriptions on a single line (`wrapMode="none"` clips silently
|
|
8052
|
+
* mid-word without an indicator; this gives the reader a clear "more
|
|
8053
|
+
* text was here" signal instead).
|
|
8054
|
+
*
|
|
8055
|
+
* Whitespace is normalised first so embedded `\n` in descriptions
|
|
8056
|
+
* doesn't blow up the budget — single space as separator everywhere.
|
|
8057
|
+
*
|
|
8058
|
+
* Column counting assumes 1 col per code point. Most descriptions in
|
|
8059
|
+
* this UI are ASCII; a CJK or wide-emoji description would over-count
|
|
8060
|
+
* its width by ~1 col per wide char but that just slightly under-
|
|
8061
|
+
* truncates rather than overflowing the row, which is the failure
|
|
8062
|
+
* mode we care about.
|
|
8063
|
+
*/
|
|
8064
|
+
function truncateToCols(s, maxCols) {
|
|
8065
|
+
const clean = s.replace(/\s+/g, " ").trim();
|
|
8066
|
+
if (maxCols <= 0) return "";
|
|
8067
|
+
if (clean.length <= maxCols) return clean;
|
|
8068
|
+
if (maxCols === 1) return "…";
|
|
8069
|
+
return `${clean.slice(0, maxCols - 1)}…`;
|
|
8070
|
+
}
|
|
7982
8071
|
function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCatalogProp, mcpsErrors: mcpsErrorsProp, keybindings, keybindingsPath, authentication, actions } = {}) {
|
|
7983
8072
|
const COLOR = useColors();
|
|
7984
8073
|
const SURFACE = useSurfaces();
|
|
@@ -7986,10 +8075,13 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
7986
8075
|
const authState = useMcpAuthState();
|
|
7987
8076
|
const inputRef = useRef(null);
|
|
7988
8077
|
const scrollboxRef = useRef(null);
|
|
8078
|
+
const { width: termWidth } = useTerminalDimensions();
|
|
8079
|
+
const contentCols = Math.max(40, Math.min(160, termWidth - 4) - 2 - 4 - 2 - 1);
|
|
7989
8080
|
const discovery = useDiscoveryOptional();
|
|
7990
8081
|
const skillsCatalog = discovery?.skillsCatalog ?? skillsCatalogProp ?? [];
|
|
7991
8082
|
const mcpsCatalog = discovery?.mcpsCatalog ?? mcpsCatalogProp ?? [];
|
|
7992
8083
|
const mcpsErrors = discovery?.mcpsErrors ?? mcpsErrorsProp;
|
|
8084
|
+
const mcpToolsByServer = discovery?.mcpToolsByServer ?? EMPTY_TOOLS;
|
|
7993
8085
|
const skillsToggle = useEnabledToggleSet({
|
|
7994
8086
|
catalog: skillsCatalog,
|
|
7995
8087
|
keyOf: (s) => s.name,
|
|
@@ -8000,6 +8092,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8000
8092
|
keyOf: (m) => m.config.name,
|
|
8001
8093
|
settingKey: "enabledMcps"
|
|
8002
8094
|
});
|
|
8095
|
+
const mcpToolToggleMap = useMcpToolToggleMap({ catalogByServer: mcpToolsByServer });
|
|
8096
|
+
const [expandedMcps, setExpandedMcps] = useState(() => /* @__PURE__ */ new Set());
|
|
8097
|
+
const onRefreshMcpToolsRef = useRef(actions?.onRefreshMcpTools);
|
|
8098
|
+
onRefreshMcpToolsRef.current = actions?.onRefreshMcpTools;
|
|
8003
8099
|
const tabOrder = useMemo(() => {
|
|
8004
8100
|
const tabs = [...SETTINGS_CATEGORIES.map((c) => c.id)];
|
|
8005
8101
|
if (keybindings) tabs.push("keybindings");
|
|
@@ -8013,6 +8109,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8013
8109
|
useEffect(() => {
|
|
8014
8110
|
if (!tabOrder.includes(activeTab)) setActiveTab(tabOrder[0]);
|
|
8015
8111
|
}, [tabOrder, activeTab]);
|
|
8112
|
+
useEffect(() => {
|
|
8113
|
+
if (activeTab !== "mcps") return;
|
|
8114
|
+
onRefreshMcpToolsRef.current?.();
|
|
8115
|
+
}, [activeTab]);
|
|
8016
8116
|
useEffect(() => {
|
|
8017
8117
|
inputRef.current?.focus();
|
|
8018
8118
|
}, []);
|
|
@@ -8061,13 +8161,22 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8061
8161
|
return buckets;
|
|
8062
8162
|
}, [generalItems, query]);
|
|
8063
8163
|
const filteredSkills = useMemo(() => skillsCatalog.filter((s) => matchesQuery(skillCorpus(s), query)), [skillsCatalog, query]);
|
|
8064
|
-
const filteredMcps = useMemo(() => mcpsCatalog.filter((m) => matchesQuery(mcpCorpus(m), query)), [
|
|
8164
|
+
const filteredMcps = useMemo(() => mcpsCatalog.filter((m) => matchesQuery(mcpCorpus(m, mcpToolsByServer), query)), [
|
|
8165
|
+
mcpsCatalog,
|
|
8166
|
+
query,
|
|
8167
|
+
mcpToolsByServer
|
|
8168
|
+
]);
|
|
8169
|
+
const mcpVisibleRows = useMemo(() => buildVisibleMcpRows(filteredMcps, expandedMcps, mcpToolsByServer), [
|
|
8170
|
+
filteredMcps,
|
|
8171
|
+
expandedMcps,
|
|
8172
|
+
mcpToolsByServer
|
|
8173
|
+
]);
|
|
8065
8174
|
const filteredProviders = useMemo(() => (authentication?.providers ?? []).filter((p) => matchesQuery(providerCorpus(p), query)), [authentication?.providers, query]);
|
|
8066
8175
|
const authRowCount = filteredProviders.length + (authentication ? 1 : 0);
|
|
8067
8176
|
const filteredSize = {
|
|
8068
8177
|
...Object.fromEntries(SETTINGS_CATEGORIES.map((c) => [c.id, filteredByCategory[c.id].length])),
|
|
8069
8178
|
skills: filteredSkills.length,
|
|
8070
|
-
mcps:
|
|
8179
|
+
mcps: mcpVisibleRows.length,
|
|
8071
8180
|
keybindings: 0,
|
|
8072
8181
|
authentication: authRowCount
|
|
8073
8182
|
};
|
|
@@ -8075,7 +8184,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8075
8184
|
const moveCursor = useCallback((delta) => setCursorByTab((prev) => {
|
|
8076
8185
|
const size = filteredSize[activeTab];
|
|
8077
8186
|
if (size === 0) return prev;
|
|
8078
|
-
const next = ((prev[activeTab] + delta) % size + size) % size;
|
|
8187
|
+
const next = ((Math.min(prev[activeTab], size - 1) + delta) % size + size) % size;
|
|
8079
8188
|
return {
|
|
8080
8189
|
...prev,
|
|
8081
8190
|
[activeTab]: next
|
|
@@ -8088,19 +8197,86 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8088
8197
|
[activeTab]: 0
|
|
8089
8198
|
}));
|
|
8090
8199
|
}, [activeTab]);
|
|
8200
|
+
const expandFocusedMcpRow = useCallback(() => {
|
|
8201
|
+
const row = mcpVisibleRows[cursor];
|
|
8202
|
+
if (!row || row.kind !== "server") return;
|
|
8203
|
+
const name = row.entry.config.name;
|
|
8204
|
+
setExpandedMcps((prev) => {
|
|
8205
|
+
if (prev.has(name)) return prev;
|
|
8206
|
+
const next = new Set(prev);
|
|
8207
|
+
next.add(name);
|
|
8208
|
+
return next;
|
|
8209
|
+
});
|
|
8210
|
+
}, [mcpVisibleRows, cursor]);
|
|
8211
|
+
const collapseFocusedMcpRow = useCallback(() => {
|
|
8212
|
+
const row = mcpVisibleRows[cursor];
|
|
8213
|
+
if (!row) return;
|
|
8214
|
+
const parentName = parentServerName(row);
|
|
8215
|
+
if (row.kind !== "server") {
|
|
8216
|
+
const parentIndex = indexOfServerRow(mcpVisibleRows, parentName);
|
|
8217
|
+
if (parentIndex >= 0) setCursorByTab((prev) => ({
|
|
8218
|
+
...prev,
|
|
8219
|
+
mcps: parentIndex
|
|
8220
|
+
}));
|
|
8221
|
+
}
|
|
8222
|
+
setExpandedMcps((prev) => {
|
|
8223
|
+
if (!prev.has(parentName)) return prev;
|
|
8224
|
+
const next = new Set(prev);
|
|
8225
|
+
next.delete(parentName);
|
|
8226
|
+
return next;
|
|
8227
|
+
});
|
|
8228
|
+
}, [mcpVisibleRows, cursor]);
|
|
8229
|
+
/**
|
|
8230
|
+
* Single `tab` press toggles between expanded / collapsed based on
|
|
8231
|
+
* the focused row's current state. The split helpers above remain
|
|
8232
|
+
* useful for `→` / `←` (a "tree-style" navigation contract some
|
|
8233
|
+
* users expect — expand-only / collapse-only). Toggle is the
|
|
8234
|
+
* default that the footer hint advertises.
|
|
8235
|
+
*/
|
|
8236
|
+
const toggleExpandFocusedMcpRow = useCallback(() => {
|
|
8237
|
+
const row = mcpVisibleRows[cursor];
|
|
8238
|
+
if (!row) return;
|
|
8239
|
+
const parentName = parentServerName(row);
|
|
8240
|
+
if (expandedMcps.has(parentName)) collapseFocusedMcpRow();
|
|
8241
|
+
else expandFocusedMcpRow();
|
|
8242
|
+
}, [
|
|
8243
|
+
mcpVisibleRows,
|
|
8244
|
+
cursor,
|
|
8245
|
+
expandedMcps,
|
|
8246
|
+
collapseFocusedMcpRow,
|
|
8247
|
+
expandFocusedMcpRow
|
|
8248
|
+
]);
|
|
8249
|
+
const mcpRowCount = mcpVisibleRows.length;
|
|
8091
8250
|
useEffect(() => {
|
|
8092
8251
|
if (activeTab === "keybindings") return;
|
|
8093
8252
|
const sb = scrollboxRef.current;
|
|
8094
8253
|
if (!sb) return;
|
|
8095
|
-
|
|
8096
|
-
|
|
8254
|
+
let inner = 0;
|
|
8255
|
+
const outer = requestAnimationFrame(() => {
|
|
8256
|
+
inner = requestAnimationFrame(() => {
|
|
8257
|
+
sb.scrollChildIntoView(anchorIdFor(cursor));
|
|
8258
|
+
});
|
|
8097
8259
|
});
|
|
8098
|
-
return () =>
|
|
8260
|
+
return () => {
|
|
8261
|
+
cancelAnimationFrame(outer);
|
|
8262
|
+
if (inner) cancelAnimationFrame(inner);
|
|
8263
|
+
};
|
|
8099
8264
|
}, [
|
|
8100
8265
|
cursor,
|
|
8101
8266
|
activeTab,
|
|
8102
|
-
query
|
|
8267
|
+
query,
|
|
8268
|
+
mcpRowCount
|
|
8103
8269
|
]);
|
|
8270
|
+
useEffect(() => {
|
|
8271
|
+
if (mcpRowCount === 0) return;
|
|
8272
|
+
setCursorByTab((prev) => {
|
|
8273
|
+
if (prev.mcps < mcpRowCount) return prev;
|
|
8274
|
+
return {
|
|
8275
|
+
...prev,
|
|
8276
|
+
mcps: mcpRowCount - 1
|
|
8277
|
+
};
|
|
8278
|
+
});
|
|
8279
|
+
}, [mcpRowCount]);
|
|
8104
8280
|
useEffect(() => {
|
|
8105
8281
|
if (activeTab !== "keybindings") return;
|
|
8106
8282
|
const sb = scrollboxRef.current;
|
|
@@ -8110,7 +8286,8 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8110
8286
|
});
|
|
8111
8287
|
return () => cancelAnimationFrame(handle);
|
|
8112
8288
|
}, [activeTab]);
|
|
8113
|
-
const
|
|
8289
|
+
const focusedMcpRow = activeTab === "mcps" ? mcpVisibleRows[cursor] : void 0;
|
|
8290
|
+
const focusedMcp = focusedMcpRow ? mcpsCatalog.find((m) => m.config.name === parentServerName(focusedMcpRow)) : void 0;
|
|
8114
8291
|
const focusedMcpStatus = focusedMcp ? getMcpAuthStatus(authState, focusedMcp.config.name) : void 0;
|
|
8115
8292
|
const oauthPasteActive = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing" && !!focusedMcpStatus.url;
|
|
8116
8293
|
useKeyboard((key) => {
|
|
@@ -8155,6 +8332,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8155
8332
|
});
|
|
8156
8333
|
return;
|
|
8157
8334
|
}
|
|
8335
|
+
if (activeTab === "mcps" && key.name === "tab") {
|
|
8336
|
+
toggleExpandFocusedMcpRow();
|
|
8337
|
+
return;
|
|
8338
|
+
}
|
|
8158
8339
|
if (key.name === "return") {
|
|
8159
8340
|
if (isCategoryTab(activeTab)) {
|
|
8160
8341
|
const it = filteredByCategory[activeTab][cursor];
|
|
@@ -8174,8 +8355,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8174
8355
|
return;
|
|
8175
8356
|
}
|
|
8176
8357
|
if (activeTab === "mcps") {
|
|
8177
|
-
const
|
|
8178
|
-
if (
|
|
8358
|
+
const row = mcpVisibleRows[cursor];
|
|
8359
|
+
if (!row) return;
|
|
8360
|
+
if (row.kind === "server") mcpsToggle.toggle(row.entry.config.name);
|
|
8361
|
+
else if (row.kind === "tool") mcpToolToggleMap[row.serverName]?.toggle(row.tool.name);
|
|
8179
8362
|
return;
|
|
8180
8363
|
}
|
|
8181
8364
|
if (activeTab === "keybindings") {
|
|
@@ -8198,11 +8381,12 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8198
8381
|
return;
|
|
8199
8382
|
}
|
|
8200
8383
|
if (activeTab === "mcps" && focusedMcp && focusedMcpStatus) {
|
|
8201
|
-
|
|
8384
|
+
const onServerRow = focusedMcpRow?.kind === "server";
|
|
8385
|
+
if (onServerRow && key.ctrl && key.name === "l" && canLogin$1(focusedMcp, focusedMcpStatus)) {
|
|
8202
8386
|
actions?.onLoginMcp?.(focusedMcp.config.name);
|
|
8203
8387
|
return;
|
|
8204
8388
|
}
|
|
8205
|
-
if (key.ctrl && key.name === "o" && canLogout$1(focusedMcpStatus)) {
|
|
8389
|
+
if (onServerRow && key.ctrl && key.name === "o" && canLogout$1(focusedMcpStatus)) {
|
|
8206
8390
|
actions?.onLogoutMcp?.(focusedMcp.config.name);
|
|
8207
8391
|
return;
|
|
8208
8392
|
}
|
|
@@ -8213,7 +8397,10 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8213
8397
|
actions.onRefreshSkills();
|
|
8214
8398
|
return;
|
|
8215
8399
|
}
|
|
8216
|
-
if (activeTab === "mcps"
|
|
8400
|
+
if (activeTab === "mcps") {
|
|
8401
|
+
if (actions?.onRefreshMcps) actions.onRefreshMcps();
|
|
8402
|
+
if (actions?.onRefreshMcpTools) actions.onRefreshMcpTools();
|
|
8403
|
+
}
|
|
8217
8404
|
}
|
|
8218
8405
|
});
|
|
8219
8406
|
const totalsByCategory = useMemo(() => {
|
|
@@ -8277,12 +8464,14 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8277
8464
|
minHeight: 4
|
|
8278
8465
|
},
|
|
8279
8466
|
stickyScroll: false,
|
|
8467
|
+
viewportCulling: false,
|
|
8280
8468
|
children: [
|
|
8281
8469
|
isCategoryTab(activeTab) && /* @__PURE__ */ jsx(GeneralList, {
|
|
8282
8470
|
items: filteredByCategory[activeTab],
|
|
8283
8471
|
cursor,
|
|
8284
8472
|
highlightBg: SURFACE.selection,
|
|
8285
|
-
query
|
|
8473
|
+
query,
|
|
8474
|
+
contentCols
|
|
8286
8475
|
}),
|
|
8287
8476
|
activeTab === "skills" && /* @__PURE__ */ jsx(SkillsList, {
|
|
8288
8477
|
items: filteredSkills,
|
|
@@ -8290,16 +8479,22 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8290
8479
|
totalCount: skillsCatalog.length,
|
|
8291
8480
|
cursor,
|
|
8292
8481
|
highlightBg: SURFACE.selection,
|
|
8293
|
-
query
|
|
8482
|
+
query,
|
|
8483
|
+
contentCols
|
|
8294
8484
|
}),
|
|
8295
8485
|
activeTab === "mcps" && /* @__PURE__ */ jsx(McpsList, {
|
|
8296
|
-
|
|
8486
|
+
rows: mcpVisibleRows,
|
|
8487
|
+
filteredCount: filteredMcps.length,
|
|
8297
8488
|
enabledSet: mcpsToggle.enabledSet,
|
|
8489
|
+
toolToggleMap: mcpToolToggleMap,
|
|
8490
|
+
toolsByServer: mcpToolsByServer,
|
|
8491
|
+
expanded: expandedMcps,
|
|
8298
8492
|
totalCount: mcpsCatalog.length,
|
|
8299
8493
|
cursor,
|
|
8300
8494
|
highlightBg: SURFACE.selection,
|
|
8301
8495
|
query,
|
|
8302
8496
|
errors: mcpsErrors,
|
|
8497
|
+
contentCols,
|
|
8303
8498
|
authState
|
|
8304
8499
|
}),
|
|
8305
8500
|
activeTab === "keybindings" && keybindings && /* @__PURE__ */ jsx(KeybindingsList, {
|
|
@@ -8331,6 +8526,7 @@ function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCata
|
|
|
8331
8526
|
children: /* @__PURE__ */ jsx(Hints, {
|
|
8332
8527
|
activeTab,
|
|
8333
8528
|
focusedMcp,
|
|
8529
|
+
focusedMcpRow,
|
|
8334
8530
|
focusedMcpStatus,
|
|
8335
8531
|
canRefreshSkills: !!actions?.onRefreshSkills,
|
|
8336
8532
|
canRefreshMcps: !!actions?.onRefreshMcps
|
|
@@ -8374,9 +8570,10 @@ function TabStrip({ order, active, counts }) {
|
|
|
8374
8570
|
})
|
|
8375
8571
|
});
|
|
8376
8572
|
}
|
|
8377
|
-
function GeneralList({ items, cursor, highlightBg, query }) {
|
|
8573
|
+
function GeneralList({ items, cursor, highlightBg, query, contentCols }) {
|
|
8378
8574
|
const { settings } = useSettings();
|
|
8379
8575
|
if (items.length === 0) return /* @__PURE__ */ jsx(EmptyRow, { label: query ? `no settings match "${query}"` : "no settings" });
|
|
8576
|
+
const descCols = Math.max(0, contentCols - 1 - 1 - 6);
|
|
8380
8577
|
return /* @__PURE__ */ jsx("box", {
|
|
8381
8578
|
style: { flexDirection: "column" },
|
|
8382
8579
|
children: items.map((item, i) => {
|
|
@@ -8386,7 +8583,7 @@ function GeneralList({ items, cursor, highlightBg, query }) {
|
|
|
8386
8583
|
if (item.kind === "toggle") return /* @__PURE__ */ jsx(ToggleRow, {
|
|
8387
8584
|
id,
|
|
8388
8585
|
label: item.label,
|
|
8389
|
-
description: item.description,
|
|
8586
|
+
description: truncateToCols(item.description, descCols),
|
|
8390
8587
|
enabled: settings[item.key],
|
|
8391
8588
|
focused,
|
|
8392
8589
|
bg
|
|
@@ -8397,7 +8594,7 @@ function GeneralList({ items, cursor, highlightBg, query }) {
|
|
|
8397
8594
|
return /* @__PURE__ */ jsx(ChoiceRow, {
|
|
8398
8595
|
id,
|
|
8399
8596
|
label: item.label,
|
|
8400
|
-
description: item.description,
|
|
8597
|
+
description: truncateToCols(item.description, descCols),
|
|
8401
8598
|
value: opt?.label ?? String(current),
|
|
8402
8599
|
cyclable: item.options.length > 1,
|
|
8403
8600
|
focused,
|
|
@@ -8407,14 +8604,14 @@ function GeneralList({ items, cursor, highlightBg, query }) {
|
|
|
8407
8604
|
return /* @__PURE__ */ jsx(ActionRow$1, {
|
|
8408
8605
|
id,
|
|
8409
8606
|
label: item.label,
|
|
8410
|
-
description: item.description,
|
|
8607
|
+
description: truncateToCols(item.description, descCols),
|
|
8411
8608
|
focused,
|
|
8412
8609
|
bg
|
|
8413
8610
|
}, item.id);
|
|
8414
8611
|
})
|
|
8415
8612
|
});
|
|
8416
8613
|
}
|
|
8417
|
-
function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query }) {
|
|
8614
|
+
function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query, contentCols }) {
|
|
8418
8615
|
const COLOR = useColors();
|
|
8419
8616
|
if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
|
|
8420
8617
|
style: { flexDirection: "column" },
|
|
@@ -8449,12 +8646,14 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query
|
|
|
8449
8646
|
})]
|
|
8450
8647
|
});
|
|
8451
8648
|
if (items.length === 0) return /* @__PURE__ */ jsx(EmptyRow, { label: `no skills match "${query}"` });
|
|
8649
|
+
const descCols = Math.max(0, contentCols - 1 - 1 - 6);
|
|
8452
8650
|
return /* @__PURE__ */ jsx("box", {
|
|
8453
8651
|
style: { flexDirection: "column" },
|
|
8454
8652
|
children: items.map((entry, i) => {
|
|
8455
8653
|
const focused = i === cursor;
|
|
8456
8654
|
const bg = focused ? highlightBg : void 0;
|
|
8457
8655
|
const enabled = enabledSet.has(entry.name);
|
|
8656
|
+
const description = truncateToCols(entry.description ?? "", descCols);
|
|
8458
8657
|
return /* @__PURE__ */ jsxs("box", {
|
|
8459
8658
|
id: anchorIdFor(i),
|
|
8460
8659
|
style: {
|
|
@@ -8483,13 +8682,13 @@ function SkillsList({ items, enabledSet, totalCount, cursor, highlightBg, query
|
|
|
8483
8682
|
}), /* @__PURE__ */ jsx("text", {
|
|
8484
8683
|
wrapMode: "none",
|
|
8485
8684
|
fg: COLOR.mute,
|
|
8486
|
-
children: `${COL_TITLE}${
|
|
8685
|
+
children: `${COL_TITLE}${description}`
|
|
8487
8686
|
})]
|
|
8488
8687
|
}, entry.name);
|
|
8489
8688
|
})
|
|
8490
8689
|
});
|
|
8491
8690
|
}
|
|
8492
|
-
function McpsList({
|
|
8691
|
+
function McpsList({ rows, filteredCount, enabledSet, toolToggleMap, toolsByServer, expanded, totalCount, cursor, highlightBg, query, errors, authState, contentCols }) {
|
|
8493
8692
|
const COLOR = useColors();
|
|
8494
8693
|
const home = homedir();
|
|
8495
8694
|
if (totalCount === 0) return /* @__PURE__ */ jsxs("box", {
|
|
@@ -8533,53 +8732,150 @@ function McpsList({ items, enabledSet, totalCount, cursor, highlightBg, query, e
|
|
|
8533
8732
|
})
|
|
8534
8733
|
]
|
|
8535
8734
|
});
|
|
8536
|
-
if (
|
|
8735
|
+
if (filteredCount === 0) return /* @__PURE__ */ jsxs("box", {
|
|
8537
8736
|
style: { flexDirection: "column" },
|
|
8538
8737
|
children: [renderMcpErrors(errors, home, COLOR.warn), /* @__PURE__ */ jsx(EmptyRow, { label: `no servers match "${query}"` })]
|
|
8539
8738
|
});
|
|
8739
|
+
const groups = [];
|
|
8740
|
+
for (let i = 0; i < rows.length; i++) {
|
|
8741
|
+
const row = rows[i];
|
|
8742
|
+
if (row.kind === "server") {
|
|
8743
|
+
groups.push({
|
|
8744
|
+
serverIndex: i,
|
|
8745
|
+
server: row,
|
|
8746
|
+
children: []
|
|
8747
|
+
});
|
|
8748
|
+
continue;
|
|
8749
|
+
}
|
|
8750
|
+
const last = groups[groups.length - 1];
|
|
8751
|
+
if (last) last.children.push({
|
|
8752
|
+
row,
|
|
8753
|
+
index: i
|
|
8754
|
+
});
|
|
8755
|
+
}
|
|
8540
8756
|
return /* @__PURE__ */ jsxs("box", {
|
|
8541
8757
|
style: { flexDirection: "column" },
|
|
8542
|
-
children: [renderMcpErrors(errors, home, COLOR.warn),
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
})]
|
|
8579
|
-
}, name);
|
|
8580
|
-
})]
|
|
8758
|
+
children: [renderMcpErrors(errors, home, COLOR.warn), groups.map((group) => /* @__PURE__ */ jsxs("box", {
|
|
8759
|
+
style: { flexDirection: "column" },
|
|
8760
|
+
children: [renderServerListRow({
|
|
8761
|
+
entry: group.server.entry,
|
|
8762
|
+
focused: group.serverIndex === cursor,
|
|
8763
|
+
bg: group.serverIndex === cursor ? highlightBg : void 0,
|
|
8764
|
+
enabled: enabledSet.has(group.server.entry.config.name),
|
|
8765
|
+
expanded: expanded.has(group.server.entry.config.name),
|
|
8766
|
+
toolEnabledSet: toolToggleMap[group.server.entry.config.name]?.enabledSet,
|
|
8767
|
+
toolTotal: toolsByServer[group.server.entry.config.name]?.length,
|
|
8768
|
+
anchorId: anchorIdFor(group.serverIndex),
|
|
8769
|
+
status: getMcpAuthStatus(authState, group.server.entry.config.name),
|
|
8770
|
+
COLOR,
|
|
8771
|
+
contentCols
|
|
8772
|
+
}), group.children.map(({ row, index }) => {
|
|
8773
|
+
const focused = index === cursor;
|
|
8774
|
+
const bg = focused ? highlightBg : void 0;
|
|
8775
|
+
if (row.kind === "tool") return renderToolListRow({
|
|
8776
|
+
serverName: row.serverName,
|
|
8777
|
+
tool: row.tool,
|
|
8778
|
+
focused,
|
|
8779
|
+
bg,
|
|
8780
|
+
enabled: toolToggleMap[row.serverName]?.enabledSet.has(row.tool.name) ?? true,
|
|
8781
|
+
anchorId: anchorIdFor(index),
|
|
8782
|
+
COLOR,
|
|
8783
|
+
contentCols
|
|
8784
|
+
});
|
|
8785
|
+
return renderEmptyToolsListRow({
|
|
8786
|
+
serverName: row.serverName,
|
|
8787
|
+
anchorId: anchorIdFor(index),
|
|
8788
|
+
focused,
|
|
8789
|
+
bg,
|
|
8790
|
+
COLOR
|
|
8791
|
+
});
|
|
8792
|
+
})]
|
|
8793
|
+
}, `group:${group.server.entry.config.name}`))]
|
|
8581
8794
|
});
|
|
8582
8795
|
}
|
|
8796
|
+
function renderServerListRow({ entry, focused, bg, enabled, expanded, toolEnabledSet, toolTotal, anchorId, status, COLOR, contentCols }) {
|
|
8797
|
+
const name = entry.config.name;
|
|
8798
|
+
const chevron = expanded ? "▾" : "▸";
|
|
8799
|
+
const toolCountChip = toolEnabledSet && typeof toolTotal === "number" ? ` (${toolEnabledSet.size}/${toolTotal})` : "";
|
|
8800
|
+
const detailRaw = mcpDetail(entry);
|
|
8801
|
+
const detailBudget = Math.max(0, contentCols - 2 - 2 - 4 - name.length - toolCountChip.length - 10 - 3);
|
|
8802
|
+
const detail = detailRaw ? truncateToCols(detailRaw, detailBudget) : "";
|
|
8803
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
8804
|
+
id: anchorId,
|
|
8805
|
+
wrapMode: "none",
|
|
8806
|
+
bg,
|
|
8807
|
+
children: [
|
|
8808
|
+
/* @__PURE__ */ jsx("span", {
|
|
8809
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
8810
|
+
children: focused ? "▶ " : " "
|
|
8811
|
+
}),
|
|
8812
|
+
/* @__PURE__ */ jsx("span", {
|
|
8813
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
8814
|
+
children: `${chevron} `
|
|
8815
|
+
}),
|
|
8816
|
+
/* @__PURE__ */ jsx("span", {
|
|
8817
|
+
fg: enabled ? COLOR.accent : COLOR.mute,
|
|
8818
|
+
children: enabled ? "[✓] " : "[ ] "
|
|
8819
|
+
}),
|
|
8820
|
+
/* @__PURE__ */ jsx("span", {
|
|
8821
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
8822
|
+
children: name
|
|
8823
|
+
}),
|
|
8824
|
+
toolCountChip && /* @__PURE__ */ jsx("span", {
|
|
8825
|
+
fg: COLOR.mute,
|
|
8826
|
+
children: toolCountChip
|
|
8827
|
+
}),
|
|
8828
|
+
renderInlineMcpBadge(status, COLOR),
|
|
8829
|
+
detail && /* @__PURE__ */ jsx("span", {
|
|
8830
|
+
fg: COLOR.mute,
|
|
8831
|
+
children: ` · ${detail}`
|
|
8832
|
+
})
|
|
8833
|
+
]
|
|
8834
|
+
}, `server:${name}`);
|
|
8835
|
+
}
|
|
8836
|
+
const TOOL_ROW_INDENT$1 = " ";
|
|
8837
|
+
const TOOL_ROW_FOCUS_INDENT$1 = " ▶ ";
|
|
8838
|
+
function renderToolListRow({ serverName, tool, focused, bg, enabled, anchorId, COLOR, contentCols }) {
|
|
8839
|
+
const descBudget = Math.max(0, contentCols - 6 - 4 - tool.name.length - 2);
|
|
8840
|
+
const description = truncateToCols(tool.description ?? "", descBudget);
|
|
8841
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
8842
|
+
id: anchorId,
|
|
8843
|
+
wrapMode: "none",
|
|
8844
|
+
bg,
|
|
8845
|
+
children: [
|
|
8846
|
+
/* @__PURE__ */ jsx("span", {
|
|
8847
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
8848
|
+
children: focused ? TOOL_ROW_FOCUS_INDENT$1 : TOOL_ROW_INDENT$1
|
|
8849
|
+
}),
|
|
8850
|
+
/* @__PURE__ */ jsx("span", {
|
|
8851
|
+
fg: enabled ? COLOR.accent : COLOR.mute,
|
|
8852
|
+
children: enabled ? "[✓] " : "[ ] "
|
|
8853
|
+
}),
|
|
8854
|
+
/* @__PURE__ */ jsx("span", {
|
|
8855
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
8856
|
+
children: tool.name
|
|
8857
|
+
}),
|
|
8858
|
+
description && /* @__PURE__ */ jsxs("span", {
|
|
8859
|
+
fg: focused ? COLOR.dim : COLOR.mute,
|
|
8860
|
+
children: [" ", description]
|
|
8861
|
+
})
|
|
8862
|
+
]
|
|
8863
|
+
}, `tool:${serverName}/${tool.name}`);
|
|
8864
|
+
}
|
|
8865
|
+
function renderEmptyToolsListRow({ serverName, anchorId, focused, bg, COLOR }) {
|
|
8866
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
8867
|
+
id: anchorId,
|
|
8868
|
+
wrapMode: "none",
|
|
8869
|
+
bg,
|
|
8870
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
8871
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
8872
|
+
children: focused ? TOOL_ROW_FOCUS_INDENT$1 : TOOL_ROW_INDENT$1
|
|
8873
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
8874
|
+
fg: COLOR.mute,
|
|
8875
|
+
children: "⟲ No cached tools yet. Send any prompt with this server enabled to populate."
|
|
8876
|
+
})]
|
|
8877
|
+
}, `tool-empty:${serverName}`);
|
|
8878
|
+
}
|
|
8583
8879
|
function KeybindingsList({ bindings, query }) {
|
|
8584
8880
|
const sections = useMemo(() => groupBindings(bindings), [bindings]);
|
|
8585
8881
|
const filteredSections = useMemo(() => {
|
|
@@ -8937,21 +9233,21 @@ function renderMcpDetailPanel(entry, status, COLOR) {
|
|
|
8937
9233
|
function renderInlineMcpBadge(status, COLOR) {
|
|
8938
9234
|
switch (status.kind) {
|
|
8939
9235
|
case "idle": return null;
|
|
8940
|
-
case "authed": return /* @__PURE__ */
|
|
9236
|
+
case "authed": return /* @__PURE__ */ jsx("span", {
|
|
8941
9237
|
fg: COLOR.accent,
|
|
8942
|
-
children:
|
|
9238
|
+
children: " · ✓ authed"
|
|
8943
9239
|
});
|
|
8944
|
-
case "needs-auth": return /* @__PURE__ */
|
|
9240
|
+
case "needs-auth": return /* @__PURE__ */ jsx("span", {
|
|
8945
9241
|
fg: COLOR.warn,
|
|
8946
|
-
children:
|
|
9242
|
+
children: " · ! needs login"
|
|
8947
9243
|
});
|
|
8948
|
-
case "authorizing": return /* @__PURE__ */
|
|
9244
|
+
case "authorizing": return /* @__PURE__ */ jsx("span", {
|
|
8949
9245
|
fg: COLOR.warn,
|
|
8950
|
-
children:
|
|
9246
|
+
children: " · … authorizing"
|
|
8951
9247
|
});
|
|
8952
|
-
case "error": return /* @__PURE__ */
|
|
9248
|
+
case "error": return /* @__PURE__ */ jsx("span", {
|
|
8953
9249
|
fg: COLOR.error,
|
|
8954
|
-
children:
|
|
9250
|
+
children: " · ✗ login failed"
|
|
8955
9251
|
});
|
|
8956
9252
|
}
|
|
8957
9253
|
}
|
|
@@ -8965,10 +9261,11 @@ function renderMcpErrors(errors, home, warnColor) {
|
|
|
8965
9261
|
}, err.path))
|
|
8966
9262
|
});
|
|
8967
9263
|
}
|
|
8968
|
-
function Hints({ activeTab, focusedMcp, focusedMcpStatus, canRefreshSkills, canRefreshMcps }) {
|
|
9264
|
+
function Hints({ activeTab, focusedMcp, focusedMcpRow, focusedMcpStatus, canRefreshSkills, canRefreshMcps }) {
|
|
8969
9265
|
const COLOR = useColors();
|
|
8970
|
-
const
|
|
8971
|
-
const
|
|
9266
|
+
const onServerRow = focusedMcpRow?.kind === "server";
|
|
9267
|
+
const showLogin = activeTab === "mcps" && onServerRow && !!focusedMcp && !!focusedMcpStatus && canLogin$1(focusedMcp, focusedMcpStatus);
|
|
9268
|
+
const showLogout = activeTab === "mcps" && onServerRow && !!focusedMcpStatus && canLogout$1(focusedMcpStatus);
|
|
8972
9269
|
const showCancel = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing";
|
|
8973
9270
|
const showRefresh = activeTab === "skills" && canRefreshSkills || activeTab === "mcps" && canRefreshMcps;
|
|
8974
9271
|
return /* @__PURE__ */ jsxs("text", {
|
|
@@ -8989,6 +9286,14 @@ function Hints({ activeTab, focusedMcp, focusedMcpStatus, canRefreshSkills, canR
|
|
|
8989
9286
|
children: "↵"
|
|
8990
9287
|
}),
|
|
8991
9288
|
isCategoryTab(activeTab) ? " toggle/cycle/select" : activeTab === "keybindings" ? " edit file" : activeTab === "authentication" ? " switch / re-authenticate" : " toggle",
|
|
9289
|
+
activeTab === "mcps" && focusedMcpRow && /* @__PURE__ */ jsxs("span", { children: [
|
|
9290
|
+
" · ",
|
|
9291
|
+
/* @__PURE__ */ jsx("span", {
|
|
9292
|
+
fg: COLOR.warn,
|
|
9293
|
+
children: "tab"
|
|
9294
|
+
}),
|
|
9295
|
+
focusedMcpRow.kind === "server" ? " expand/collapse" : " collapse"
|
|
9296
|
+
] }),
|
|
8992
9297
|
showLogin && /* @__PURE__ */ jsxs("span", { children: [
|
|
8993
9298
|
" · ",
|
|
8994
9299
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -9044,13 +9349,15 @@ function generalCorpus(item) {
|
|
|
9044
9349
|
function skillCorpus(s) {
|
|
9045
9350
|
return `${s.name} ${s.description ?? ""}`.toLowerCase();
|
|
9046
9351
|
}
|
|
9047
|
-
function mcpCorpus(m) {
|
|
9352
|
+
function mcpCorpus(m, toolsByServer) {
|
|
9353
|
+
const toolCorpus = (toolsByServer[m.config.name] ?? []).map((t) => `${t.name} ${t.description ?? ""}`).join(" ");
|
|
9048
9354
|
return [
|
|
9049
9355
|
m.config.name,
|
|
9050
9356
|
m.config.transport,
|
|
9051
9357
|
m.config.command ?? "",
|
|
9052
9358
|
m.config.url ?? "",
|
|
9053
|
-
(m.config.args ?? []).join(" ")
|
|
9359
|
+
(m.config.args ?? []).join(" "),
|
|
9360
|
+
toolCorpus
|
|
9054
9361
|
].join(" ").toLowerCase();
|
|
9055
9362
|
}
|
|
9056
9363
|
function keybindingCorpus(row) {
|
|
@@ -9897,6 +10204,8 @@ function AppShell() {
|
|
|
9897
10204
|
mcpsCatalogRef.current = mcpsCatalog;
|
|
9898
10205
|
const enabledMcpsRef = useRef(settings.enabledMcps);
|
|
9899
10206
|
enabledMcpsRef.current = settings.enabledMcps;
|
|
10207
|
+
const disabledMcpToolsRef = useRef(settings.disabledMcpTools);
|
|
10208
|
+
disabledMcpToolsRef.current = settings.disabledMcpTools;
|
|
9900
10209
|
const filesCatalogRef = useRef(filesCatalog);
|
|
9901
10210
|
filesCatalogRef.current = filesCatalog;
|
|
9902
10211
|
const ensureFilesCatalogRef = useRef(ensureFilesCatalog);
|
|
@@ -10034,6 +10343,15 @@ function AppShell() {
|
|
|
10034
10343
|
* otherwise.
|
|
10035
10344
|
*/
|
|
10036
10345
|
const runningRef = useRef(false);
|
|
10346
|
+
const lastCtrlCRef = useRef(0);
|
|
10347
|
+
const [showQuitConfirm, setShowQuitConfirm] = useState(false);
|
|
10348
|
+
const showQuitConfirmRef = useRef(false);
|
|
10349
|
+
showQuitConfirmRef.current = showQuitConfirm;
|
|
10350
|
+
const [quitConfirmSelected, setQuitConfirmSelected] = useState("cancel");
|
|
10351
|
+
const quitConfirmSelectedRef = useRef("cancel");
|
|
10352
|
+
quitConfirmSelectedRef.current = quitConfirmSelected;
|
|
10353
|
+
const settingsRef = useRef(settings);
|
|
10354
|
+
settingsRef.current = settings;
|
|
10037
10355
|
/** Token count from the most recent assistant turn (caching-aware). */
|
|
10038
10356
|
const [lastInputTokens, setLastInputTokens] = useState(0);
|
|
10039
10357
|
/**
|
|
@@ -10234,7 +10552,8 @@ function AppShell() {
|
|
|
10234
10552
|
});
|
|
10235
10553
|
const projectMcps = buildMcpServers({
|
|
10236
10554
|
discovered: mcpsCatalogRef.current,
|
|
10237
|
-
enabled: enabledMcpsRef.current
|
|
10555
|
+
enabled: enabledMcpsRef.current,
|
|
10556
|
+
...disabledMcpToolsRef.current ? { disabledTools: disabledMcpToolsRef.current } : {}
|
|
10238
10557
|
});
|
|
10239
10558
|
const allowInteraction = allowInteractionRef.current !== false;
|
|
10240
10559
|
const interactionTools = allowInteraction ? createInteractionTools({ requestInteraction: makeRequestInteraction(interactions) }) : {};
|
|
@@ -10399,6 +10718,15 @@ function AppShell() {
|
|
|
10399
10718
|
reason
|
|
10400
10719
|
});
|
|
10401
10720
|
});
|
|
10721
|
+
agent.hooks.hook("mcp:bootstrap:end", (ctx) => {
|
|
10722
|
+
if (ctx.ok) return;
|
|
10723
|
+
if (mcpsCatalogRef.current.find((d) => d.config.name === ctx.name)?.config.auth !== "oauth") return;
|
|
10724
|
+
dispatchAuthRef.current({
|
|
10725
|
+
type: "auth-error",
|
|
10726
|
+
name: ctx.name,
|
|
10727
|
+
error: ctx.error.message
|
|
10728
|
+
});
|
|
10729
|
+
});
|
|
10402
10730
|
agent.hooks.hook("mcp:auth:url", ({ name, url }) => {
|
|
10403
10731
|
dispatchAuthRef.current({
|
|
10404
10732
|
type: "auth-url",
|
|
@@ -10634,6 +10962,7 @@ function AppShell() {
|
|
|
10634
10962
|
agent.hooks.hook("agent:done", () => {
|
|
10635
10963
|
pendingAnnotationsRef.current.clear();
|
|
10636
10964
|
});
|
|
10965
|
+
subscribeMcpToolsCache(agent.hooks, { dataDir });
|
|
10637
10966
|
return agent;
|
|
10638
10967
|
}, [
|
|
10639
10968
|
providerRegistry,
|
|
@@ -11311,6 +11640,24 @@ function AppShell() {
|
|
|
11311
11640
|
const onCancelLoginMcp = useCallback((name) => {
|
|
11312
11641
|
mcpLoginAbortsRef.current.get(name)?.abort();
|
|
11313
11642
|
}, []);
|
|
11643
|
+
const onRefreshMcpTools = useCallback(async () => {
|
|
11644
|
+
const agent = agentRef.current;
|
|
11645
|
+
if (!agent) return;
|
|
11646
|
+
const servers = mcpsCatalogRef.current.map((d) => d.config);
|
|
11647
|
+
if (servers.length === 0) return;
|
|
11648
|
+
try {
|
|
11649
|
+
await refreshMcpToolsCatalog({
|
|
11650
|
+
servers,
|
|
11651
|
+
hooks: agent.hooks,
|
|
11652
|
+
buildAuthProvider: (cfg) => new McpOAuthProvider({
|
|
11653
|
+
name: cfg.name,
|
|
11654
|
+
store: mcpCredentialStore
|
|
11655
|
+
})
|
|
11656
|
+
});
|
|
11657
|
+
} catch (err) {
|
|
11658
|
+
debugLog("refreshMcpToolsCatalog failed", err);
|
|
11659
|
+
}
|
|
11660
|
+
}, [mcpCredentialStore]);
|
|
11314
11661
|
const onOpenKeybindingsFile = useCallback(() => {
|
|
11315
11662
|
modal.close();
|
|
11316
11663
|
(async () => {
|
|
@@ -11817,7 +12164,8 @@ function AppShell() {
|
|
|
11817
12164
|
onLogoutMcp,
|
|
11818
12165
|
onCancelLoginMcp,
|
|
11819
12166
|
onRefreshSkills,
|
|
11820
|
-
onRefreshMcps
|
|
12167
|
+
onRefreshMcps,
|
|
12168
|
+
onRefreshMcpTools
|
|
11821
12169
|
}
|
|
11822
12170
|
}));
|
|
11823
12171
|
return;
|
|
@@ -11910,6 +12258,58 @@ function AppShell() {
|
|
|
11910
12258
|
}));
|
|
11911
12259
|
return;
|
|
11912
12260
|
}
|
|
12261
|
+
if (showQuitConfirmRef.current) {
|
|
12262
|
+
if (key.name === "escape") {
|
|
12263
|
+
setShowQuitConfirm(false);
|
|
12264
|
+
setQuitConfirmSelected("cancel");
|
|
12265
|
+
return;
|
|
12266
|
+
}
|
|
12267
|
+
if (key.name === "c" && key.ctrl) {
|
|
12268
|
+
renderer.destroy();
|
|
12269
|
+
return;
|
|
12270
|
+
}
|
|
12271
|
+
if (key.name === "return") {
|
|
12272
|
+
if (quitConfirmSelectedRef.current === "quit") renderer.destroy();
|
|
12273
|
+
else {
|
|
12274
|
+
setShowQuitConfirm(false);
|
|
12275
|
+
setQuitConfirmSelected("cancel");
|
|
12276
|
+
}
|
|
12277
|
+
return;
|
|
12278
|
+
}
|
|
12279
|
+
if (key.name === "tab" || key.name === "left" || key.name === "right") {
|
|
12280
|
+
setQuitConfirmSelected((prev) => prev === "cancel" ? "quit" : "cancel");
|
|
12281
|
+
return;
|
|
12282
|
+
}
|
|
12283
|
+
return;
|
|
12284
|
+
}
|
|
12285
|
+
if (key.name === "c" && key.ctrl) {
|
|
12286
|
+
const mode = settingsRef.current.ctrlCBehavior;
|
|
12287
|
+
if (mode === "quit") {
|
|
12288
|
+
renderer.destroy();
|
|
12289
|
+
return;
|
|
12290
|
+
}
|
|
12291
|
+
if (mode === "quit-confirm") {
|
|
12292
|
+
setShowQuitConfirm(true);
|
|
12293
|
+
setQuitConfirmSelected("cancel");
|
|
12294
|
+
return;
|
|
12295
|
+
}
|
|
12296
|
+
const now = Date.now();
|
|
12297
|
+
if (now - lastCtrlCRef.current < 1500) {
|
|
12298
|
+
renderer.destroy();
|
|
12299
|
+
return;
|
|
12300
|
+
}
|
|
12301
|
+
lastCtrlCRef.current = now;
|
|
12302
|
+
if (runningRef.current || messageQueueRef.current.length > 0) {
|
|
12303
|
+
onAbort();
|
|
12304
|
+
return;
|
|
12305
|
+
}
|
|
12306
|
+
const eb = renderer.currentFocusedEditor;
|
|
12307
|
+
if (eb && eb.plainText.length > 0) {
|
|
12308
|
+
eb.clear();
|
|
12309
|
+
lastCtrlCRef.current = 0;
|
|
12310
|
+
}
|
|
12311
|
+
return;
|
|
12312
|
+
}
|
|
11913
12313
|
if (key.name !== "escape") return;
|
|
11914
12314
|
if (busy || pendingApproval) return onAbort();
|
|
11915
12315
|
if (popupOpenRef.current) return;
|
|
@@ -12025,54 +12425,90 @@ function AppShell() {
|
|
|
12025
12425
|
flexGrow: 1,
|
|
12026
12426
|
backgroundColor: SURFACE.background
|
|
12027
12427
|
},
|
|
12028
|
-
children: [
|
|
12029
|
-
|
|
12428
|
+
children: [
|
|
12429
|
+
/* @__PURE__ */ jsxs("box", {
|
|
12430
|
+
style: {
|
|
12431
|
+
flexDirection: "column",
|
|
12432
|
+
flexGrow: 1,
|
|
12433
|
+
paddingLeft: 1,
|
|
12434
|
+
paddingRight: 1
|
|
12435
|
+
},
|
|
12436
|
+
children: [
|
|
12437
|
+
screen === "auth" && /* @__PURE__ */ jsx(AuthScreen, { onPick: onPickProvider }),
|
|
12438
|
+
screen === "sessions" && /* @__PURE__ */ jsx(SessionsScreen, {
|
|
12439
|
+
sessions,
|
|
12440
|
+
currentId: currentSession?.id ?? null,
|
|
12441
|
+
focusedSessionId,
|
|
12442
|
+
onPick: onSwitchSession,
|
|
12443
|
+
onCreate: onCreateSession,
|
|
12444
|
+
onFocusChange: setFocusedSessionId,
|
|
12445
|
+
showAllProjects: settings.showAllProjects,
|
|
12446
|
+
currentProjectRoot: projectDir
|
|
12447
|
+
}),
|
|
12448
|
+
screen === "chat" && /* @__PURE__ */ jsx(ChatScreen, {
|
|
12449
|
+
cwd,
|
|
12450
|
+
events,
|
|
12451
|
+
busy,
|
|
12452
|
+
compacting,
|
|
12453
|
+
queuedMessages: queuedMessagePreviews,
|
|
12454
|
+
queueSelectionIndex,
|
|
12455
|
+
queueShortcuts,
|
|
12456
|
+
onEnterQueueFromEmptyPrompt: enterQueueSelectionFromEmptyPrompt,
|
|
12457
|
+
settings,
|
|
12458
|
+
onSubmit: onSubmitPrompt,
|
|
12459
|
+
session: currentSession,
|
|
12460
|
+
liveSession: sessionRef.current,
|
|
12461
|
+
pending: pendingApproval,
|
|
12462
|
+
onApproval: resolveHead,
|
|
12463
|
+
pendingInteraction,
|
|
12464
|
+
onInteraction: onInteractionResolve,
|
|
12465
|
+
completionProviders,
|
|
12466
|
+
onPopupOpenChange,
|
|
12467
|
+
selectedTurnId,
|
|
12468
|
+
promptTriggerHints
|
|
12469
|
+
})
|
|
12470
|
+
]
|
|
12471
|
+
}),
|
|
12472
|
+
/* @__PURE__ */ jsx(Footer, {
|
|
12473
|
+
hints,
|
|
12474
|
+
context: contextUsage,
|
|
12475
|
+
cost: footerCost,
|
|
12476
|
+
status: pendingInteraction?.kind === "question" ? "asking" : busy ? "busy" : compacting ? "compacting" : null
|
|
12477
|
+
}),
|
|
12478
|
+
showQuitConfirm && /* @__PURE__ */ jsxs("box", {
|
|
12479
|
+
position: "absolute",
|
|
12480
|
+
top: "50%",
|
|
12481
|
+
left: "50%",
|
|
12482
|
+
marginTop: -3,
|
|
12483
|
+
marginLeft: -18,
|
|
12484
|
+
width: 36,
|
|
12485
|
+
height: 7,
|
|
12486
|
+
borderStyle: "rounded",
|
|
12487
|
+
borderColor: COLOR.warn,
|
|
12488
|
+
backgroundColor: SURFACE.modal,
|
|
12030
12489
|
flexDirection: "column",
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
queueSelectionIndex,
|
|
12054
|
-
queueShortcuts,
|
|
12055
|
-
onEnterQueueFromEmptyPrompt: enterQueueSelectionFromEmptyPrompt,
|
|
12056
|
-
settings,
|
|
12057
|
-
onSubmit: onSubmitPrompt,
|
|
12058
|
-
session: currentSession,
|
|
12059
|
-
liveSession: sessionRef.current,
|
|
12060
|
-
pending: pendingApproval,
|
|
12061
|
-
onApproval: resolveHead,
|
|
12062
|
-
pendingInteraction,
|
|
12063
|
-
onInteraction: onInteractionResolve,
|
|
12064
|
-
completionProviders,
|
|
12065
|
-
onPopupOpenChange,
|
|
12066
|
-
selectedTurnId,
|
|
12067
|
-
promptTriggerHints
|
|
12068
|
-
})
|
|
12069
|
-
]
|
|
12070
|
-
}), /* @__PURE__ */ jsx(Footer, {
|
|
12071
|
-
hints,
|
|
12072
|
-
context: contextUsage,
|
|
12073
|
-
cost: footerCost,
|
|
12074
|
-
status: pendingInteraction?.kind === "question" ? "asking" : busy ? "busy" : compacting ? "compacting" : null
|
|
12075
|
-
})]
|
|
12490
|
+
alignItems: "center",
|
|
12491
|
+
justifyContent: "center",
|
|
12492
|
+
gap: 1,
|
|
12493
|
+
children: [/* @__PURE__ */ jsx("text", { children: "Quit zidane?" }), /* @__PURE__ */ jsxs("box", {
|
|
12494
|
+
flexDirection: "row",
|
|
12495
|
+
gap: 2,
|
|
12496
|
+
children: [/* @__PURE__ */ jsx("box", {
|
|
12497
|
+
backgroundColor: quitConfirmSelected === "cancel" ? COLOR.accent : void 0,
|
|
12498
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
12499
|
+
fg: quitConfirmSelected === "cancel" ? SURFACE.modal : COLOR.dim,
|
|
12500
|
+
children: " Cancel "
|
|
12501
|
+
})
|
|
12502
|
+
}), /* @__PURE__ */ jsx("box", {
|
|
12503
|
+
backgroundColor: quitConfirmSelected === "quit" ? COLOR.warn : void 0,
|
|
12504
|
+
children: /* @__PURE__ */ jsx("text", {
|
|
12505
|
+
fg: quitConfirmSelected === "quit" ? SURFACE.modal : COLOR.dim,
|
|
12506
|
+
children: " Quit "
|
|
12507
|
+
})
|
|
12508
|
+
})]
|
|
12509
|
+
})]
|
|
12510
|
+
})
|
|
12511
|
+
]
|
|
12076
12512
|
});
|
|
12077
12513
|
}
|
|
12078
12514
|
/**
|
|
@@ -12287,55 +12723,100 @@ function initTreeSitterWorker() {
|
|
|
12287
12723
|
//#endregion
|
|
12288
12724
|
//#region src/tui/mcps-settings.tsx
|
|
12289
12725
|
/**
|
|
12290
|
-
* MCP server picker.
|
|
12726
|
+
* MCP server picker. Hierarchical: each server is one row, and an
|
|
12727
|
+
* expanded server reveals one row per advertised tool. The full surface:
|
|
12291
12728
|
*
|
|
12292
|
-
*
|
|
12729
|
+
* ▼ [✓] linear stdio · npx -y mcp-remote … (5/7) ✓ authed
|
|
12730
|
+
* [✓] create_issue Create a new Linear issue
|
|
12731
|
+
* [✓] list_issues Query issues by status / assignee
|
|
12732
|
+
* [ ] update_issue Update fields on an existing issue
|
|
12733
|
+
* ▶ [✓] chrome-devtools stdio · npx -y chrome-devtools-mcp@latest
|
|
12293
12734
|
*
|
|
12294
12735
|
* Keybindings:
|
|
12295
12736
|
*
|
|
12296
|
-
* ↑ / ↓ / k / j
|
|
12297
|
-
*
|
|
12298
|
-
*
|
|
12299
|
-
*
|
|
12300
|
-
*
|
|
12737
|
+
* ↑ / ↓ / k / j navigate (over servers AND visible tool rows)
|
|
12738
|
+
* → / tab expand the focused server
|
|
12739
|
+
* ← / shift+tab collapse — on a tool row, also snaps the cursor
|
|
12740
|
+
* back to the parent server
|
|
12741
|
+
* enter / space toggle — server toggles `enabledMcps`; tool toggles
|
|
12742
|
+
* `disabledMcpTools[serverName]`
|
|
12743
|
+
* l login (server rows only — needs-auth / error)
|
|
12744
|
+
* o logout (server rows only — authed / error /
|
|
12745
|
+
* authorizing)
|
|
12746
|
+
* r refresh discovery
|
|
12747
|
+
* esc close — also cancels an in-flight `authorizing`
|
|
12748
|
+
* row
|
|
12301
12749
|
*
|
|
12302
|
-
* Status badge legend:
|
|
12750
|
+
* Status badge legend (server row only):
|
|
12303
12751
|
*
|
|
12304
12752
|
* ✓ authed tokens stored + bootstrap connected
|
|
12305
|
-
* ! needs login
|
|
12753
|
+
* ! needs login bootstrap needs OAuth, no tokens
|
|
12306
12754
|
* … authorizing interactive login in flight
|
|
12307
12755
|
* ✗ <error> login attempt failed
|
|
12308
12756
|
*
|
|
12309
|
-
* The
|
|
12310
|
-
*
|
|
12311
|
-
*
|
|
12757
|
+
* The (N/M) chip on a server header counts ENABLED / TOTAL advertised
|
|
12758
|
+
* tools, drawn only when the tool catalog has loaded. Without a catalog
|
|
12759
|
+
* (server never bootstrapped this profile, cache cold) the chip is
|
|
12760
|
+
* suppressed and the empty-state row under the expanded server explains
|
|
12761
|
+
* how to populate.
|
|
12312
12762
|
*
|
|
12313
12763
|
* Errors (`DiscoveryError[]`) surface in a warn-colored preamble so a
|
|
12314
12764
|
* broken `mcps.json` is loud rather than invisible.
|
|
12315
12765
|
*/
|
|
12316
|
-
function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin, onRefresh }) {
|
|
12766
|
+
function McpsSettingsModal({ catalog, errors, toolsByServer, onLogin, onLogout, onCancelLogin, onRefresh }) {
|
|
12317
12767
|
const COLOR = useColors();
|
|
12318
12768
|
const home = homedir();
|
|
12319
12769
|
const authState = useMcpAuthState();
|
|
12320
|
-
const { enabledSet, toggle } = useEnabledToggleSet({
|
|
12770
|
+
const { enabledSet: serverEnabledSet, toggle: toggleServer } = useEnabledToggleSet({
|
|
12321
12771
|
catalog,
|
|
12322
12772
|
keyOf: (d) => d.config.name,
|
|
12323
12773
|
settingKey: "enabledMcps"
|
|
12324
12774
|
});
|
|
12775
|
+
const toolToggleMap = useMcpToolToggleMap({ catalogByServer: toolsByServer });
|
|
12776
|
+
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
12325
12777
|
const [cursor, setCursorRaw] = useState(0);
|
|
12778
|
+
const rows = useMemo(() => buildVisibleMcpRows(catalog, expanded, toolsByServer), [
|
|
12779
|
+
catalog,
|
|
12780
|
+
expanded,
|
|
12781
|
+
toolsByServer
|
|
12782
|
+
]);
|
|
12326
12783
|
const moveCursor = useCallback((delta) => setCursorRaw((prev) => {
|
|
12327
|
-
if (
|
|
12328
|
-
return ((prev + delta) %
|
|
12329
|
-
}), [
|
|
12330
|
-
const safeCursor = Math.min(cursor, Math.max(0,
|
|
12331
|
-
const
|
|
12784
|
+
if (rows.length === 0) return prev;
|
|
12785
|
+
return ((prev + delta) % rows.length + rows.length) % rows.length;
|
|
12786
|
+
}), [rows.length]);
|
|
12787
|
+
const safeCursor = Math.min(cursor, Math.max(0, rows.length - 1));
|
|
12788
|
+
const focusedRow = rows[safeCursor];
|
|
12789
|
+
const focusedServerName = focusedRow ? focusedRow.kind === "server" ? focusedRow.entry.config.name : focusedRow.serverName : void 0;
|
|
12790
|
+
const focusedEntry = focusedServerName ? catalog.find((c) => c.config.name === focusedServerName) : void 0;
|
|
12332
12791
|
const focusedStatus = focusedEntry ? getMcpAuthStatus(authState, focusedEntry.config.name) : void 0;
|
|
12333
12792
|
const inputActive = focusedStatus?.kind === "authorizing" && !!focusedStatus.url;
|
|
12793
|
+
const collapseFocused = useCallback(() => {
|
|
12794
|
+
if (!focusedRow) return;
|
|
12795
|
+
const parentName = focusedRow.kind === "server" ? focusedRow.entry.config.name : focusedRow.serverName;
|
|
12796
|
+
if (focusedRow.kind !== "server") {
|
|
12797
|
+
const parentIndex = indexOfServerRow(rows, parentName);
|
|
12798
|
+
if (parentIndex >= 0) setCursorRaw(parentIndex);
|
|
12799
|
+
}
|
|
12800
|
+
setExpanded((prev) => {
|
|
12801
|
+
if (!prev.has(parentName)) return prev;
|
|
12802
|
+
const next = new Set(prev);
|
|
12803
|
+
next.delete(parentName);
|
|
12804
|
+
return next;
|
|
12805
|
+
});
|
|
12806
|
+
}, [focusedRow, rows]);
|
|
12807
|
+
const expandFocused = useCallback(() => {
|
|
12808
|
+
if (!focusedRow || focusedRow.kind !== "server") return;
|
|
12809
|
+
setExpanded((prev) => {
|
|
12810
|
+
if (prev.has(focusedRow.entry.config.name)) return prev;
|
|
12811
|
+
const next = new Set(prev);
|
|
12812
|
+
next.add(focusedRow.entry.config.name);
|
|
12813
|
+
return next;
|
|
12814
|
+
});
|
|
12815
|
+
}, [focusedRow]);
|
|
12334
12816
|
useKeyboard((key) => {
|
|
12335
12817
|
if (key.name === "escape" && focusedEntry) {
|
|
12336
|
-
|
|
12337
|
-
|
|
12338
|
-
onCancelLogin(name);
|
|
12818
|
+
if (getMcpAuthStatus(authState, focusedEntry.config.name).kind === "authorizing") {
|
|
12819
|
+
onCancelLogin(focusedEntry.config.name);
|
|
12339
12820
|
return;
|
|
12340
12821
|
}
|
|
12341
12822
|
}
|
|
@@ -12348,21 +12829,36 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin,
|
|
|
12348
12829
|
moveCursor(1);
|
|
12349
12830
|
return;
|
|
12350
12831
|
}
|
|
12351
|
-
if (
|
|
12352
|
-
|
|
12353
|
-
if (
|
|
12354
|
-
|
|
12355
|
-
|
|
12832
|
+
if (rows.length === 0) return;
|
|
12833
|
+
if (!focusedRow) return;
|
|
12834
|
+
if (key.name === "tab") {
|
|
12835
|
+
const row = rows[safeCursor];
|
|
12836
|
+
const parentName = row ? parentServerName(row) : void 0;
|
|
12837
|
+
if (parentName && expanded.has(parentName)) collapseFocused();
|
|
12838
|
+
else expandFocused();
|
|
12839
|
+
return;
|
|
12840
|
+
}
|
|
12841
|
+
if (key.name === "right") {
|
|
12842
|
+
expandFocused();
|
|
12843
|
+
return;
|
|
12844
|
+
}
|
|
12845
|
+
if (key.name === "left") {
|
|
12846
|
+
collapseFocused();
|
|
12847
|
+
return;
|
|
12848
|
+
}
|
|
12356
12849
|
if (key.name === "return" || key.name === "space") {
|
|
12357
|
-
|
|
12850
|
+
if (focusedRow.kind === "server") toggleServer(focusedRow.entry.config.name);
|
|
12851
|
+
else if (focusedRow.kind === "tool") toolToggleMap[focusedRow.serverName]?.toggle(focusedRow.tool.name);
|
|
12358
12852
|
return;
|
|
12359
12853
|
}
|
|
12360
|
-
if (
|
|
12361
|
-
|
|
12854
|
+
if (focusedRow.kind !== "server") return;
|
|
12855
|
+
const status = getMcpAuthStatus(authState, focusedRow.entry.config.name);
|
|
12856
|
+
if (key.name === "l" && canLogin(focusedRow.entry, status)) {
|
|
12857
|
+
onLogin(focusedRow.entry.config.name);
|
|
12362
12858
|
return;
|
|
12363
12859
|
}
|
|
12364
12860
|
if (key.name === "o" && canLogout(status)) {
|
|
12365
|
-
onLogout(name);
|
|
12861
|
+
onLogout(focusedRow.entry.config.name);
|
|
12366
12862
|
return;
|
|
12367
12863
|
}
|
|
12368
12864
|
if (key.name === "r" && onRefresh) onRefresh();
|
|
@@ -12445,46 +12941,185 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin,
|
|
|
12445
12941
|
})
|
|
12446
12942
|
]
|
|
12447
12943
|
});
|
|
12944
|
+
const groups = [];
|
|
12945
|
+
for (let i = 0; i < rows.length; i++) {
|
|
12946
|
+
const row = rows[i];
|
|
12947
|
+
if (row.kind === "server") {
|
|
12948
|
+
groups.push({
|
|
12949
|
+
serverIndex: i,
|
|
12950
|
+
server: row,
|
|
12951
|
+
children: []
|
|
12952
|
+
});
|
|
12953
|
+
continue;
|
|
12954
|
+
}
|
|
12955
|
+
const last = groups[groups.length - 1];
|
|
12956
|
+
if (last) last.children.push({
|
|
12957
|
+
row,
|
|
12958
|
+
index: i
|
|
12959
|
+
});
|
|
12960
|
+
}
|
|
12448
12961
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
12449
|
-
title: ` mcp servers · ${
|
|
12962
|
+
title: ` mcp servers · ${serverEnabledSet.size} / ${catalog.length} enabled `,
|
|
12450
12963
|
children: [
|
|
12451
12964
|
renderErrors(errors, home, COLOR.warn),
|
|
12452
12965
|
/* @__PURE__ */ jsx("box", {
|
|
12453
12966
|
style: { flexDirection: "column" },
|
|
12454
|
-
children:
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
|
|
12459
|
-
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
|
|
12463
|
-
|
|
12464
|
-
|
|
12465
|
-
|
|
12466
|
-
|
|
12467
|
-
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12475
|
-
|
|
12476
|
-
children: [" ", detailFor(entry)]
|
|
12477
|
-
}),
|
|
12478
|
-
renderInlineBadge(status, COLOR)
|
|
12479
|
-
]
|
|
12480
|
-
}, name);
|
|
12481
|
-
})
|
|
12967
|
+
children: groups.map((group) => /* @__PURE__ */ jsxs("box", {
|
|
12968
|
+
style: { flexDirection: "column" },
|
|
12969
|
+
children: [renderRow({
|
|
12970
|
+
row: group.server,
|
|
12971
|
+
focused: group.serverIndex === safeCursor,
|
|
12972
|
+
serverEnabledSet,
|
|
12973
|
+
toolEnabledSet: toolToggleMap[group.server.entry.config.name]?.enabledSet,
|
|
12974
|
+
toolCatalog: toolsByServer,
|
|
12975
|
+
authState,
|
|
12976
|
+
expanded,
|
|
12977
|
+
COLOR
|
|
12978
|
+
}), group.children.map(({ row, index }) => renderRow({
|
|
12979
|
+
row,
|
|
12980
|
+
focused: index === safeCursor,
|
|
12981
|
+
serverEnabledSet,
|
|
12982
|
+
toolEnabledSet: toolToggleMap[parentServerName(row)]?.enabledSet,
|
|
12983
|
+
toolCatalog: toolsByServer,
|
|
12984
|
+
authState,
|
|
12985
|
+
expanded,
|
|
12986
|
+
COLOR
|
|
12987
|
+
}))]
|
|
12988
|
+
}, `group:${group.server.entry.config.name}`))
|
|
12482
12989
|
}),
|
|
12483
12990
|
focusedEntry && focusedStatus && renderDetailPanel(focusedEntry, focusedStatus, COLOR),
|
|
12484
|
-
renderActionHints(
|
|
12991
|
+
renderActionHints({
|
|
12992
|
+
focusedRow,
|
|
12993
|
+
focusedEntry,
|
|
12994
|
+
focusedStatus,
|
|
12995
|
+
expanded,
|
|
12996
|
+
showRefresh: !!onRefresh,
|
|
12997
|
+
COLOR
|
|
12998
|
+
})
|
|
12485
12999
|
]
|
|
12486
13000
|
});
|
|
12487
13001
|
}
|
|
13002
|
+
function renderRow({ row, focused, serverEnabledSet, toolEnabledSet, toolCatalog, authState, expanded, COLOR }) {
|
|
13003
|
+
if (row.kind === "server") return renderServerRow({
|
|
13004
|
+
entry: row.entry,
|
|
13005
|
+
focused,
|
|
13006
|
+
serverEnabledSet,
|
|
13007
|
+
toolEnabledSet,
|
|
13008
|
+
toolCatalog,
|
|
13009
|
+
authState,
|
|
13010
|
+
expanded,
|
|
13011
|
+
COLOR
|
|
13012
|
+
});
|
|
13013
|
+
if (row.kind === "tool") return renderToolRow({
|
|
13014
|
+
row,
|
|
13015
|
+
focused,
|
|
13016
|
+
toolEnabledSet,
|
|
13017
|
+
COLOR
|
|
13018
|
+
});
|
|
13019
|
+
return renderEmptyToolsRow({
|
|
13020
|
+
row,
|
|
13021
|
+
focused,
|
|
13022
|
+
COLOR
|
|
13023
|
+
});
|
|
13024
|
+
}
|
|
13025
|
+
function renderServerRow({ entry, focused, serverEnabledSet, toolEnabledSet, toolCatalog, authState, expanded, COLOR }) {
|
|
13026
|
+
const name = entry.config.name;
|
|
13027
|
+
const enabled = serverEnabledSet.has(name);
|
|
13028
|
+
const status = getMcpAuthStatus(authState, name);
|
|
13029
|
+
const cached = toolCatalog[name];
|
|
13030
|
+
const chevron = expanded.has(name) ? "▼" : "◀";
|
|
13031
|
+
const toolCountChip = cached && toolEnabledSet ? ` (${toolEnabledSet.size}/${cached.length})` : "";
|
|
13032
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
13033
|
+
style: {
|
|
13034
|
+
flexDirection: "row",
|
|
13035
|
+
height: 1,
|
|
13036
|
+
paddingRight: 1
|
|
13037
|
+
},
|
|
13038
|
+
children: [
|
|
13039
|
+
/* @__PURE__ */ jsxs("text", {
|
|
13040
|
+
wrapMode: "none",
|
|
13041
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
13042
|
+
children: [
|
|
13043
|
+
/* @__PURE__ */ jsx("span", {
|
|
13044
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
13045
|
+
children: focused ? "▶ " : " "
|
|
13046
|
+
}),
|
|
13047
|
+
/* @__PURE__ */ jsx("span", {
|
|
13048
|
+
fg: enabled ? COLOR.accent : COLOR.mute,
|
|
13049
|
+
children: enabled ? "[✓] " : "[ ] "
|
|
13050
|
+
}),
|
|
13051
|
+
/* @__PURE__ */ jsx("span", {
|
|
13052
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
13053
|
+
children: name
|
|
13054
|
+
}),
|
|
13055
|
+
/* @__PURE__ */ jsxs("span", {
|
|
13056
|
+
fg: COLOR.mute,
|
|
13057
|
+
children: [" ", detailFor(entry)]
|
|
13058
|
+
}),
|
|
13059
|
+
toolCountChip && /* @__PURE__ */ jsx("span", {
|
|
13060
|
+
fg: COLOR.mute,
|
|
13061
|
+
children: toolCountChip
|
|
13062
|
+
}),
|
|
13063
|
+
renderInlineBadge(status, COLOR)
|
|
13064
|
+
]
|
|
13065
|
+
}),
|
|
13066
|
+
/* @__PURE__ */ jsx("box", { style: { flexGrow: 1 } }),
|
|
13067
|
+
/* @__PURE__ */ jsx("text", {
|
|
13068
|
+
wrapMode: "none",
|
|
13069
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
13070
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
13071
|
+
children: chevron
|
|
13072
|
+
})
|
|
13073
|
+
})
|
|
13074
|
+
]
|
|
13075
|
+
}, `server:${name}`);
|
|
13076
|
+
}
|
|
13077
|
+
const TOOL_ROW_INDENT = " ";
|
|
13078
|
+
const TOOL_ROW_FOCUS_INDENT = " ▶ ";
|
|
13079
|
+
function renderToolRow({ row, focused, toolEnabledSet, COLOR }) {
|
|
13080
|
+
const enabled = toolEnabledSet?.has(row.tool.name) ?? true;
|
|
13081
|
+
const description = trimToWidth(row.tool.description ?? "", 60);
|
|
13082
|
+
return /* @__PURE__ */ jsxs("text", {
|
|
13083
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
13084
|
+
children: [
|
|
13085
|
+
/* @__PURE__ */ jsx("span", {
|
|
13086
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
13087
|
+
children: focused ? TOOL_ROW_FOCUS_INDENT : TOOL_ROW_INDENT
|
|
13088
|
+
}),
|
|
13089
|
+
/* @__PURE__ */ jsx("span", {
|
|
13090
|
+
fg: enabled ? COLOR.accent : COLOR.mute,
|
|
13091
|
+
children: enabled ? "[✓] " : "[ ] "
|
|
13092
|
+
}),
|
|
13093
|
+
/* @__PURE__ */ jsx("span", {
|
|
13094
|
+
fg: focused ? COLOR.brand : COLOR.dim,
|
|
13095
|
+
children: row.tool.name
|
|
13096
|
+
}),
|
|
13097
|
+
description && /* @__PURE__ */ jsxs("span", {
|
|
13098
|
+
fg: COLOR.mute,
|
|
13099
|
+
children: [" ", description]
|
|
13100
|
+
})
|
|
13101
|
+
]
|
|
13102
|
+
}, `tool:${row.serverName}/${row.tool.name}`);
|
|
13103
|
+
}
|
|
13104
|
+
function renderEmptyToolsRow({ row, focused, COLOR }) {
|
|
13105
|
+
return /* @__PURE__ */ jsxs("text", { children: [/* @__PURE__ */ jsx("span", {
|
|
13106
|
+
fg: focused ? COLOR.brand : COLOR.mute,
|
|
13107
|
+
children: focused ? TOOL_ROW_FOCUS_INDENT : TOOL_ROW_INDENT
|
|
13108
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
13109
|
+
fg: COLOR.mute,
|
|
13110
|
+
children: "⟲ No cached tools yet. Send any prompt with this server enabled to populate."
|
|
13111
|
+
})] }, `tool-empty:${row.serverName}`);
|
|
13112
|
+
}
|
|
13113
|
+
/**
|
|
13114
|
+
* Cap a single-line description at `max` columns, suffixing `…` if it
|
|
13115
|
+
* overflows. Renderer uses one column per char; surrogate-pair safety
|
|
13116
|
+
* isn't a concern here because tool descriptions are ASCII in practice.
|
|
13117
|
+
*/
|
|
13118
|
+
function trimToWidth(s, max) {
|
|
13119
|
+
const clean = s.replace(/\s+/g, " ").trim();
|
|
13120
|
+
if (clean.length <= max) return clean;
|
|
13121
|
+
return `${clean.slice(0, Math.max(0, max - 1))}…`;
|
|
13122
|
+
}
|
|
12488
13123
|
function detailFor(entry) {
|
|
12489
13124
|
const transport = entry.config.transport;
|
|
12490
13125
|
return `${transport} · ${transport === "stdio" ? entry.config.command ?? "" : entry.config.url ?? ""}`;
|
|
@@ -12590,11 +13225,14 @@ function renderDetailPanel(entry, status, COLOR) {
|
|
|
12590
13225
|
});
|
|
12591
13226
|
return null;
|
|
12592
13227
|
}
|
|
12593
|
-
function renderActionHints(
|
|
12594
|
-
const effectiveStatus =
|
|
12595
|
-
const
|
|
12596
|
-
const
|
|
13228
|
+
function renderActionHints({ focusedRow, focusedEntry, focusedStatus, expanded, showRefresh, COLOR }) {
|
|
13229
|
+
const effectiveStatus = focusedStatus ?? { kind: "idle" };
|
|
13230
|
+
const onServerRow = focusedRow?.kind === "server";
|
|
13231
|
+
const canL = onServerRow && focusedEntry ? canLogin(focusedEntry, effectiveStatus) : false;
|
|
13232
|
+
const canO = onServerRow ? canLogout(effectiveStatus) : false;
|
|
12597
13233
|
const canCancel = effectiveStatus.kind === "authorizing";
|
|
13234
|
+
const canExpand = onServerRow && focusedEntry ? !expanded.has(focusedEntry.config.name) : false;
|
|
13235
|
+
const canCollapse = focusedRow?.kind === "tool" || (onServerRow && focusedEntry ? expanded.has(focusedEntry.config.name) : false);
|
|
12598
13236
|
return /* @__PURE__ */ jsxs("text", {
|
|
12599
13237
|
fg: COLOR.mute,
|
|
12600
13238
|
children: [
|
|
@@ -12608,6 +13246,22 @@ function renderActionHints(entry, status, showRefresh, COLOR) {
|
|
|
12608
13246
|
children: "↵"
|
|
12609
13247
|
}),
|
|
12610
13248
|
" toggle",
|
|
13249
|
+
canExpand && /* @__PURE__ */ jsxs("span", { children: [
|
|
13250
|
+
" · ",
|
|
13251
|
+
/* @__PURE__ */ jsx("span", {
|
|
13252
|
+
fg: COLOR.warn,
|
|
13253
|
+
children: "→"
|
|
13254
|
+
}),
|
|
13255
|
+
" expand"
|
|
13256
|
+
] }),
|
|
13257
|
+
canCollapse && /* @__PURE__ */ jsxs("span", { children: [
|
|
13258
|
+
" · ",
|
|
13259
|
+
/* @__PURE__ */ jsx("span", {
|
|
13260
|
+
fg: COLOR.warn,
|
|
13261
|
+
children: "←"
|
|
13262
|
+
}),
|
|
13263
|
+
" collapse"
|
|
13264
|
+
] }),
|
|
12611
13265
|
canL && /* @__PURE__ */ jsxs("span", { children: [
|
|
12612
13266
|
" · ",
|
|
12613
13267
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -12909,7 +13563,7 @@ async function runTui(options = {}) {
|
|
|
12909
13563
|
const bootFps = clampFps(config.initialSettings.targetFps ?? DEFAULT_SETTINGS.targetFps);
|
|
12910
13564
|
const gatherStats = !!process.env.ZIDANE_DEBUG;
|
|
12911
13565
|
const renderer = await createCliRenderer({
|
|
12912
|
-
exitOnCtrlC:
|
|
13566
|
+
exitOnCtrlC: false,
|
|
12913
13567
|
onDestroy: () => done(),
|
|
12914
13568
|
debounceDelay: 0,
|
|
12915
13569
|
targetFps: bootFps,
|