zidane 5.1.13 → 5.1.14
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-skiQGYs2.d.ts → agent-BuGxYfqh.d.ts} +11 -4
- package/dist/agent-BuGxYfqh.d.ts.map +1 -0
- package/dist/chat.d.ts +172 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/{index-CjPh6CRE.d.ts → index-Aaa1tP6E.d.ts} +2 -2
- package/dist/{index-CjPh6CRE.d.ts.map → index-Aaa1tP6E.d.ts.map} +1 -1
- package/dist/{index-YM7SipFz.d.ts → index-Cv5wED8j.d.ts} +2 -2
- package/dist/{index-YM7SipFz.d.ts.map → index-Cv5wED8j.d.ts.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/{login-Cc6Q-Fpu.js → login-DJscx_sS.js} +4 -4
- package/dist/{login-Cc6Q-Fpu.js.map → login-DJscx_sS.js.map} +1 -1
- package/dist/{mcp-CUt-N8zn.js → mcp-Bq_rD6e9.js} +2 -2
- package/dist/{mcp-CUt-N8zn.js.map → mcp-Bq_rD6e9.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{presets-Ce79MK4J.js → presets-BEruW0Ji.js} +2 -2
- package/dist/{presets-Ce79MK4J.js.map → presets-BEruW0Ji.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +2 -2
- package/dist/{tools-BG2wMa3X.js → tools-BBFu1UsV.js} +3 -3
- package/dist/{tools-BG2wMa3X.js.map → tools-BBFu1UsV.js.map} +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{tool-formatters-0aOMYbH-.d.ts → transcript-anchors-FJMZyLS4.d.ts} +242 -97
- package/dist/transcript-anchors-FJMZyLS4.d.ts.map +1 -0
- package/dist/tui.d.ts +55 -39
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +335 -314
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CDmQ2h-T.js → turn-operations-CeLlc7jt.js} +572 -91
- package/dist/turn-operations-CeLlc7jt.js.map +1 -0
- package/dist/{types-Bx_F8jet.js → types-IcokUOyC.js} +11 -4
- package/dist/{types-Bx_F8jet.js.map → types-IcokUOyC.js.map} +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/dist/agent-skiQGYs2.d.ts.map +0 -1
- package/dist/tool-formatters-0aOMYbH-.d.ts.map +0 -1
- package/dist/turn-operations-CDmQ2h-T.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { S as resolvePersistDir, b as cleanupPersistedSession, d as createAgent } from "./tools-
|
|
2
|
-
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-
|
|
3
|
-
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-
|
|
1
|
+
import { S as resolvePersistDir, b as cleanupPersistedSession, d as createAgent } from "./tools-BBFu1UsV.js";
|
|
2
|
+
import { s as McpOAuthProvider, t as connectMcpServers } from "./mcp-Bq_rD6e9.js";
|
|
3
|
+
import { C as summaryToTurn, a as selectFilesFromSession, r as buildPostCompactAttachments, s as compactConversation, t as loginMcpServer } from "./login-DJscx_sS.js";
|
|
4
4
|
import { n as formatTokenUsage } from "./stats-DgOvY7wd.js";
|
|
5
5
|
import { n as loadSession, t as createSession } from "./session-DtLD1Sl1.js";
|
|
6
6
|
import { createTuiStore } from "./session/sqlite.js";
|
|
7
|
-
import { $ as
|
|
7
|
+
import { $ as useMcpAuthState, $n as setProviderCredential, $t as lastContextSizeFromTurns, A as getSafelist, At as useSettings, B as buildModelCatalog, Bn as useCompletion, Bt as createDiscoverySlot, Cn as ensureKeybindingsFile, Ct as listProjectFiles, D as useSafeModeQueue, Dt as SETTINGS_TOGGLES, E as useSafeModeActions, Et as SETTINGS_CHOICES, F as suggestSafelistEntry, G as discoverProjectMcps, Gn as bootTick, Gt as useConfig, H as indexOfEntry, Hn as buildLinearRamp, Ht as useDiscovery, J as createFileMcpCredentialStore, Jt as deriveSessionTitle, Kn as shouldAutoCompact, Kt as resolveConfig, L as splitPromptSegments, Mn as uniqueSkillNamesFromReferences, Mr as buildBuildSystem, Nr as buildPlanSystem, Nt as resolveChipColor, Ot as SettingsProvider, Pn as createFilesCompletionProvider, Pt as resolveTheme, Q as useMcpAuthDispatch, Qt as isVisible, R as runOAuthLogin, St as shortId, T as SafeModeProvider, Tn as matchesBinding, Tt as DEFAULT_SETTINGS, U as buildMcpServers, Un as tryOpenBrowser, Ut as useDiscoveryOptional, V as filterModelCatalog, Vn as blendHsl, Vt as DiscoveryProvider, Wt as ConfigProvider, Xt as isEditErrorResult, Yt as eventsFromTurns, Z as McpAuthProvider, Zt as isTurnHighlighted, _ as turnContextSize, a as computeTurnAnchors, an as stripSpawnTokensLine, b as defaultSkillScanPaths, bt as compactPath, c as formatToolCall, cn as toolCallPreview, d as useSelectStyle, dn as buildContextualDiff, en as listSessionMeta, et as getMcpAuthStatus, f as useSurfaces, fn as buildUnifiedDiff, ft as useInteractionsActions, g as finalizeStreamingMarkdownForOwner, gn as filetypeFromPath, gt as truncateTrailing, h as finalizeStreamingMarkdown, hn as extractEditPayload, ht as hintsLength, i as turnAsText, in as selectableTurnIds, it as InteractionsProvider, j as isOnSafelist, jn as createSkillsCompletionProvider, k as addToSafelist, kt as clampFps, l as ThemeProvider, ln as toolResultText, lr as modelSupportsReasoning, lt as makeRequestInteraction, m as useTheme, mt as clipHintsToWidth, n as deleteTurnSafely, nn as marginTopFor, nt as splitMarkdownCodeBlocks, o as TOOL_DISPLAY, on as sumRunCosts, ot as buildResumedToolResultsTurn, pr as piIdOf, pt as useInteractionsQueue, qn as detectAuth, r as truncateTurnsAt, s as displayNameFor, sr as getContextWindow, st as createInteractionTools, u as useColors, un as turnSelectionOwnership, ut as pendingInteractionsFromTurns, v as useStreamBuffer, vt as generateSessionTitle, w as writeSessionExport, wt as useEnabledToggleSet, x as discoverProjectSkills, xt as fmtTokens, y as buildSkillsConfig, yn as findGitRoot, yr as accentColor, yt as ageString, z as supportsOAuth } from "./turn-operations-CeLlc7jt.js";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
import { Buffer } from "node:buffer";
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
13
14
|
import { BoxRenderable, CodeRenderable, RGBA, SyntaxStyle, TextRenderable, addDefaultParsers, createCliRenderer, decodePasteBytes, defaultTextareaKeyBindings, getTreeSitterClient, stripAnsiSequences } from "@opentui/core";
|
|
14
15
|
import { createRoot, useKeyboard, useRenderer, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
|
|
15
|
-
import { Fragment, jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
16
16
|
//#region src/tui/modal.tsx
|
|
17
17
|
const ModalContext = createContext(null);
|
|
18
18
|
function ModalRoot({ children }) {
|
|
@@ -318,95 +318,6 @@ function writeToClipboard(text) {
|
|
|
318
318
|
return osc || helper;
|
|
319
319
|
}
|
|
320
320
|
//#endregion
|
|
321
|
-
//#region src/tui/color-gradient.ts
|
|
322
|
-
/** Parse `#rrggbb` (case-insensitive) into `[r, g, b]` 0–255 integers. */
|
|
323
|
-
function parseHex(hex) {
|
|
324
|
-
const h = hex.replace("#", "");
|
|
325
|
-
return [
|
|
326
|
-
Number.parseInt(h.slice(0, 2), 16),
|
|
327
|
-
Number.parseInt(h.slice(2, 4), 16),
|
|
328
|
-
Number.parseInt(h.slice(4, 6), 16)
|
|
329
|
-
];
|
|
330
|
-
}
|
|
331
|
-
/** Convert sRGB 0–255 → HSL 0–1. */
|
|
332
|
-
function rgbToHsl(r, g, b) {
|
|
333
|
-
r /= 255;
|
|
334
|
-
g /= 255;
|
|
335
|
-
b /= 255;
|
|
336
|
-
const max = Math.max(r, g, b);
|
|
337
|
-
const min = Math.min(r, g, b);
|
|
338
|
-
const l = (max + min) / 2;
|
|
339
|
-
if (max === min) return [
|
|
340
|
-
0,
|
|
341
|
-
0,
|
|
342
|
-
l
|
|
343
|
-
];
|
|
344
|
-
const d = max - min;
|
|
345
|
-
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
346
|
-
let h;
|
|
347
|
-
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
|
|
348
|
-
else if (max === g) h = (b - r) / d + 2;
|
|
349
|
-
else h = (r - g) / d + 4;
|
|
350
|
-
return [
|
|
351
|
-
h / 6,
|
|
352
|
-
s,
|
|
353
|
-
l
|
|
354
|
-
];
|
|
355
|
-
}
|
|
356
|
-
/** Convert HSL 0–1 → sRGB 0–255. Standard piecewise formula. */
|
|
357
|
-
function hslToRgb(h, s, l) {
|
|
358
|
-
if (s === 0) return [
|
|
359
|
-
l * 255,
|
|
360
|
-
l * 255,
|
|
361
|
-
l * 255
|
|
362
|
-
];
|
|
363
|
-
const hue2rgb = (p, q, t) => {
|
|
364
|
-
if (t < 0) t += 1;
|
|
365
|
-
if (t > 1) t -= 1;
|
|
366
|
-
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
367
|
-
if (t < 1 / 2) return q;
|
|
368
|
-
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
369
|
-
return p;
|
|
370
|
-
};
|
|
371
|
-
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
372
|
-
const p = 2 * l - q;
|
|
373
|
-
return [
|
|
374
|
-
hue2rgb(p, q, h + 1 / 3) * 255,
|
|
375
|
-
hue2rgb(p, q, h) * 255,
|
|
376
|
-
hue2rgb(p, q, h - 1 / 3) * 255
|
|
377
|
-
];
|
|
378
|
-
}
|
|
379
|
-
function toHex(rgb) {
|
|
380
|
-
const pad = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
|
|
381
|
-
return `#${pad(rgb[0])}${pad(rgb[1])}${pad(rgb[2])}`;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Blend two hex colors in HSL space with shortest-path hue interpolation.
|
|
385
|
-
* `t` ∈ [0, 1]; `t=0` returns `from`, `t=1` returns `to`.
|
|
386
|
-
*/
|
|
387
|
-
function blendHsl(from, to, t) {
|
|
388
|
-
const [r1, g1, b1] = parseHex(from);
|
|
389
|
-
const [r2, g2, b2] = parseHex(to);
|
|
390
|
-
const [h1, s1, l1] = rgbToHsl(r1, g1, b1);
|
|
391
|
-
const [h2, s2, l2] = rgbToHsl(r2, g2, b2);
|
|
392
|
-
let dh = h2 - h1;
|
|
393
|
-
if (dh > .5) dh -= 1;
|
|
394
|
-
else if (dh < -.5) dh += 1;
|
|
395
|
-
return toHex(hslToRgb((h1 + dh * t + 1) % 1, s1 + (s2 - s1) * t, l1 + (l2 - l1) * t));
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Static gradient ramp of length `n` going from `from` (index 0) to
|
|
399
|
-
* `to` (index n-1) in HSL space. For the cycling A→B→A→B ramp the
|
|
400
|
-
* throbber uses, see `buildCycleRamp` in `crush-throbber.tsx`.
|
|
401
|
-
*/
|
|
402
|
-
function buildLinearRamp(from, to, n) {
|
|
403
|
-
if (n <= 0) return [];
|
|
404
|
-
if (n === 1) return [blendHsl(from, to, .5)];
|
|
405
|
-
const ramp = [];
|
|
406
|
-
for (let i = 0; i < n; i++) ramp.push(blendHsl(from, to, i / (n - 1)));
|
|
407
|
-
return ramp;
|
|
408
|
-
}
|
|
409
|
-
//#endregion
|
|
410
321
|
//#region src/tui/crush-throbber.tsx
|
|
411
322
|
/** @jsxImportSource @opentui/react */
|
|
412
323
|
const CRUSH_RUNES = "0123456789abcdefABCDEF~!@#$£€%^&*()+=_";
|
|
@@ -947,67 +858,6 @@ function metaSegmentsLength(meta) {
|
|
|
947
858
|
if (typeof meta === "string") return meta.length;
|
|
948
859
|
return meta.reduce((sum, seg) => sum + seg.text.length, 0);
|
|
949
860
|
}
|
|
950
|
-
/**
|
|
951
|
-
* Truncate `text` to at most `max` characters, replacing the trailing
|
|
952
|
-
* overflow with `…`. Edge cases:
|
|
953
|
-
* - `max <= 0` → empty string (no room to render at all).
|
|
954
|
-
* - `max === 1` → just the ellipsis glyph.
|
|
955
|
-
* - `text.length <= max` → unchanged.
|
|
956
|
-
*
|
|
957
|
-
* Trailing-style truncation matches the natural read order of titles:
|
|
958
|
-
* the prefix carries enough signal to identify the surface.
|
|
959
|
-
*
|
|
960
|
-
* Exported for unit-tests; consumers should normally lean on
|
|
961
|
-
* {@link TitleOverlay} instead of calling this directly.
|
|
962
|
-
*/
|
|
963
|
-
function truncateTrailing(text, max) {
|
|
964
|
-
if (max <= 0) return "";
|
|
965
|
-
if (text.length <= max) return text;
|
|
966
|
-
if (max === 1) return "…";
|
|
967
|
-
return `${text.slice(0, max - 1)}…`;
|
|
968
|
-
}
|
|
969
|
-
/**
|
|
970
|
-
* Plain-text width estimate for a list of {@link Hint}s rendered via
|
|
971
|
-
* `renderHintSpans` — `<key> <label> · <key> <label> · …`. Exported so
|
|
972
|
-
* the prompt-box overlay (in `screens.tsx`) can run the same responsive
|
|
973
|
-
* math as the bottom-bar footer when deciding whether trigger hints
|
|
974
|
-
* fit. Pure / total.
|
|
975
|
-
*/
|
|
976
|
-
function hintsLength(hints) {
|
|
977
|
-
if (hints.length === 0) return 0;
|
|
978
|
-
return hints.reduce((sum, h, i) => sum + hintLength(h) + (i > 0 ? 3 : 0), 0);
|
|
979
|
-
}
|
|
980
|
-
/** Plain-text width of a single hint as rendered by {@link renderHintSpans}. */
|
|
981
|
-
function hintLength(h) {
|
|
982
|
-
return h.key.length + 1 + h.label.length + (h.extra ? h.extra.key.length + 1 + h.extra.label.length : 0);
|
|
983
|
-
}
|
|
984
|
-
/** Stable empty list so callers can compare by reference. */
|
|
985
|
-
const EMPTY_HINTS = Object.freeze([]);
|
|
986
|
-
/**
|
|
987
|
-
* Return the longest prefix of `hints` whose rendered width via
|
|
988
|
-
* {@link renderHintSpans} fits within `budget`. Used to degrade hint
|
|
989
|
-
* rows gracefully at narrow terminal widths instead of letting OpenTUI
|
|
990
|
-
* wrap an absolutely-positioned `<text>` mid-segment (which paints the
|
|
991
|
-
* overflow over the prompt box's border and looks like garbled glyphs).
|
|
992
|
-
*
|
|
993
|
-
* Prefix-only (no reordering, no last-hint priority) so the survivors
|
|
994
|
-
* keep their authored order — the user's muscle memory for "leftmost
|
|
995
|
-
* hint = primary action" stays intact as the terminal shrinks.
|
|
996
|
-
*/
|
|
997
|
-
function clipHintsToWidth(hints, budget) {
|
|
998
|
-
if (budget <= 0 || hints.length === 0) return EMPTY_HINTS;
|
|
999
|
-
const out = [];
|
|
1000
|
-
let used = 0;
|
|
1001
|
-
for (let i = 0; i < hints.length; i++) {
|
|
1002
|
-
const h = hints[i];
|
|
1003
|
-
const cost = (i > 0 ? 3 : 0) + hintLength(h);
|
|
1004
|
-
if (used + cost > budget) break;
|
|
1005
|
-
out.push(h);
|
|
1006
|
-
used += cost;
|
|
1007
|
-
}
|
|
1008
|
-
if (out.length === 0) return EMPTY_HINTS;
|
|
1009
|
-
return out.length === hints.length ? hints : out;
|
|
1010
|
-
}
|
|
1011
861
|
function contextIndicatorLength(context) {
|
|
1012
862
|
const ratio = context.max > 0 ? context.used / context.max : 0;
|
|
1013
863
|
const pct = Math.round(ratio * 100);
|
|
@@ -1135,41 +985,6 @@ function Transcript({ events, settings, selectedTurnId = null, busy = false }) {
|
|
|
1135
985
|
});
|
|
1136
986
|
}
|
|
1137
987
|
/**
|
|
1138
|
-
* Per-item anchor ids for auto-scroll. Walks `items` in render order and,
|
|
1139
|
-
* for each event, returns either:
|
|
1140
|
-
* - `'turn-anchor-<turnId>'` — the first event of this turn (the
|
|
1141
|
-
* scrollbox's target).
|
|
1142
|
-
* - `undefined` — later event of an already-tagged turn (or a synthetic
|
|
1143
|
-
* event with no `turnId`).
|
|
1144
|
-
*
|
|
1145
|
-
* `ids[i]` is a tuple per item: length 1 for plain events, length N for
|
|
1146
|
-
* subagent runs (one entry per inner event). `idByTurn` is the inverse
|
|
1147
|
-
* lookup used by the scroll effect. `lastTurnId` is the most-recently-
|
|
1148
|
-
* rendered turn — the scroll effect special-cases it to snap to bottom.
|
|
1149
|
-
*
|
|
1150
|
-
* Exported so the anchor-tagging matrix can be unit-tested without rendering.
|
|
1151
|
-
*/
|
|
1152
|
-
function computeTurnAnchors(items) {
|
|
1153
|
-
const idByTurn = /* @__PURE__ */ new Map();
|
|
1154
|
-
let lastTurnId;
|
|
1155
|
-
const tag = (turnId) => {
|
|
1156
|
-
if (!turnId) return void 0;
|
|
1157
|
-
lastTurnId = turnId;
|
|
1158
|
-
if (idByTurn.has(turnId)) return void 0;
|
|
1159
|
-
const id = `turn-anchor-${turnId}`;
|
|
1160
|
-
idByTurn.set(turnId, id);
|
|
1161
|
-
return id;
|
|
1162
|
-
};
|
|
1163
|
-
const ids = [];
|
|
1164
|
-
for (const item of items) if (item.kind === "event") ids.push([tag(item.event.turnId)]);
|
|
1165
|
-
else ids.push(item.events.map((e) => tag(e.turnId)));
|
|
1166
|
-
return {
|
|
1167
|
-
idByTurn,
|
|
1168
|
-
ids,
|
|
1169
|
-
lastTurnId
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
/**
|
|
1173
988
|
* Walk the visible-event list once and group consecutive child events
|
|
1174
989
|
* (`depth > 0`) into runs so we can wrap each run in a single bordered
|
|
1175
990
|
* subagent box.
|
|
@@ -1589,7 +1404,7 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
1589
1404
|
flexDirection: "column",
|
|
1590
1405
|
flexShrink: 0,
|
|
1591
1406
|
alignSelf: "stretch",
|
|
1592
|
-
backgroundColor: surfaces.
|
|
1407
|
+
backgroundColor: surfaces.modal
|
|
1593
1408
|
});
|
|
1594
1409
|
this.body = body;
|
|
1595
1410
|
this.bag = options.bag;
|
|
@@ -1597,11 +1412,12 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
1597
1412
|
body.marginTop = 0;
|
|
1598
1413
|
body.marginBottom = 0;
|
|
1599
1414
|
body.drawUnstyledText = false;
|
|
1415
|
+
body.bg = surfaces.modal;
|
|
1600
1416
|
this.header = new BoxRenderable(ctx, {
|
|
1601
1417
|
flexDirection: "row",
|
|
1602
1418
|
height: 1,
|
|
1603
1419
|
alignSelf: "stretch",
|
|
1604
|
-
backgroundColor: surfaces.
|
|
1420
|
+
backgroundColor: surfaces.modal
|
|
1605
1421
|
});
|
|
1606
1422
|
this.langLabel = new TextRenderable(ctx, {
|
|
1607
1423
|
content: options.lang && options.lang.length > 0 ? options.lang : "code",
|
|
@@ -1610,7 +1426,7 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
1610
1426
|
});
|
|
1611
1427
|
this.spacer = new BoxRenderable(ctx, {
|
|
1612
1428
|
flexGrow: 1,
|
|
1613
|
-
backgroundColor: surfaces.
|
|
1429
|
+
backgroundColor: surfaces.modal
|
|
1614
1430
|
});
|
|
1615
1431
|
this.button = new TextRenderable(ctx, {
|
|
1616
1432
|
content: "[copy]",
|
|
@@ -1653,9 +1469,10 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
1653
1469
|
* `[copy]`/`[copied]` button colour (which depends on `this.copied`).
|
|
1654
1470
|
*/
|
|
1655
1471
|
applyTheme(colors, surfaces) {
|
|
1656
|
-
this.backgroundColor = surfaces.
|
|
1657
|
-
this.header.backgroundColor = surfaces.
|
|
1658
|
-
this.spacer.backgroundColor = surfaces.
|
|
1472
|
+
this.backgroundColor = surfaces.modal;
|
|
1473
|
+
this.header.backgroundColor = surfaces.modal;
|
|
1474
|
+
this.spacer.backgroundColor = surfaces.modal;
|
|
1475
|
+
this.body.bg = surfaces.modal;
|
|
1659
1476
|
this.langLabel.fg = colors.mute;
|
|
1660
1477
|
this.button.fg = this.copied ? colors.accent : colors.warn;
|
|
1661
1478
|
}
|
|
@@ -1684,11 +1501,23 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
1684
1501
|
set fg(value) {
|
|
1685
1502
|
this.body.fg = value;
|
|
1686
1503
|
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Always reports the live elevated-panel paint: see the setter doc.
|
|
1506
|
+
*/
|
|
1687
1507
|
get bg() {
|
|
1688
1508
|
return this.body.bg;
|
|
1689
1509
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1510
|
+
/**
|
|
1511
|
+
* Pinned to {@link ThemeSurfaces.modal} — the elevated code-panel
|
|
1512
|
+
* surface. The markdown's `applyCodeBlockRenderable` writes `bg` on
|
|
1513
|
+
* every delta from whatever surface the parent `<markdown>` carries
|
|
1514
|
+
* (the prose body paint). Forwarding that through would flip the
|
|
1515
|
+
* code panel back to the prose surface on every keystroke and erase
|
|
1516
|
+
* the visual lift. Theme switches are funnelled through
|
|
1517
|
+
* {@link applyTheme} instead, which rewrites `body.bg` directly.
|
|
1518
|
+
*/
|
|
1519
|
+
set bg(_value) {
|
|
1520
|
+
this.body.bg = this.bag.surfaces.modal;
|
|
1692
1521
|
}
|
|
1693
1522
|
get conceal() {
|
|
1694
1523
|
return this.body.conceal;
|
|
@@ -2007,6 +1836,156 @@ function ToolCallBlock({ event, display, dim }) {
|
|
|
2007
1836
|
});
|
|
2008
1837
|
}
|
|
2009
1838
|
//#endregion
|
|
1839
|
+
//#region src/tui/discovery-shell.tsx
|
|
1840
|
+
/**
|
|
1841
|
+
* SWR throttles. `files` is short so a long-open `@` popover picks up
|
|
1842
|
+
* new files within seconds; `skills` is long because SKILL.md changes
|
|
1843
|
+
* are rare and the walk is cheap enough that we already eager-load it.
|
|
1844
|
+
*/
|
|
1845
|
+
const FILES_REFRESH_THROTTLE_MS = 3e3;
|
|
1846
|
+
const SKILLS_REFRESH_THROTTLE_MS = 3e4;
|
|
1847
|
+
function debugLog$1(...args) {
|
|
1848
|
+
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/tui] ${args.map((a) => a instanceof Error ? a.message : String(a)).join(" ")}\n`);
|
|
1849
|
+
}
|
|
1850
|
+
function DiscoveryShell({ children }) {
|
|
1851
|
+
const config = useConfig();
|
|
1852
|
+
const dispatchAuth = useMcpAuthDispatch();
|
|
1853
|
+
const dispatchAuthRef = useRef(dispatchAuth);
|
|
1854
|
+
dispatchAuthRef.current = dispatchAuth;
|
|
1855
|
+
const [projectDir] = useState(() => findGitRoot(process.cwd()) ?? process.cwd());
|
|
1856
|
+
const dataDir = config.paths.userDir;
|
|
1857
|
+
const mcpCredentialStore = useMemo(() => createFileMcpCredentialStore(dataDir), [dataDir]);
|
|
1858
|
+
const [skillsCatalog, setSkillsCatalog] = useState([]);
|
|
1859
|
+
const [mcpsCatalog, setMcpsCatalog] = useState([]);
|
|
1860
|
+
const [mcpsErrors, setMcpsErrors] = useState([]);
|
|
1861
|
+
const [filesCatalog, setFilesCatalog] = useState([]);
|
|
1862
|
+
const filesSlotRef = useRef(null);
|
|
1863
|
+
const skillsSlotRef = useRef(null);
|
|
1864
|
+
useEffect(() => {
|
|
1865
|
+
filesSlotRef.current?.abort();
|
|
1866
|
+
skillsSlotRef.current?.abort();
|
|
1867
|
+
setFilesCatalog([]);
|
|
1868
|
+
setSkillsCatalog([]);
|
|
1869
|
+
const filesSlot = createDiscoverySlot({
|
|
1870
|
+
throttleMs: FILES_REFRESH_THROTTLE_MS,
|
|
1871
|
+
walk: (signal) => listProjectFiles({
|
|
1872
|
+
cwd: projectDir,
|
|
1873
|
+
signal
|
|
1874
|
+
}),
|
|
1875
|
+
onLoad: (items) => {
|
|
1876
|
+
if (filesSlotRef.current === filesSlot) setFilesCatalog(items);
|
|
1877
|
+
},
|
|
1878
|
+
onError: (err, phase) => {
|
|
1879
|
+
debugLog$1(`listProjectFiles ${phase} failed`, err);
|
|
1880
|
+
}
|
|
1881
|
+
});
|
|
1882
|
+
const skillsSlot = createDiscoverySlot({
|
|
1883
|
+
throttleMs: SKILLS_REFRESH_THROTTLE_MS,
|
|
1884
|
+
walk: () => discoverProjectSkills({
|
|
1885
|
+
cwd: projectDir,
|
|
1886
|
+
prefix: config.prefix
|
|
1887
|
+
}),
|
|
1888
|
+
onLoad: (items) => {
|
|
1889
|
+
if (skillsSlotRef.current === skillsSlot) setSkillsCatalog(items);
|
|
1890
|
+
},
|
|
1891
|
+
onError: (err, phase) => {
|
|
1892
|
+
debugLog$1(`discoverProjectSkills ${phase} failed`, err);
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
filesSlotRef.current = filesSlot;
|
|
1896
|
+
skillsSlotRef.current = skillsSlot;
|
|
1897
|
+
skillsSlot.ensure().then((skills) => bootTick(`discovery:skills (${skills.length})`));
|
|
1898
|
+
try {
|
|
1899
|
+
const { servers, errors } = discoverProjectMcps({
|
|
1900
|
+
cwd: projectDir,
|
|
1901
|
+
prefix: config.prefix
|
|
1902
|
+
});
|
|
1903
|
+
setMcpsCatalog(servers);
|
|
1904
|
+
setMcpsErrors(errors);
|
|
1905
|
+
bootTick(`discovery:mcps (${servers.length} servers, ${errors.length} parse errors)`);
|
|
1906
|
+
for (const entry of servers) {
|
|
1907
|
+
if (entry.config.auth !== "oauth") continue;
|
|
1908
|
+
const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
|
|
1909
|
+
dispatchAuthRef.current(hasTokens ? {
|
|
1910
|
+
type: "auth-success",
|
|
1911
|
+
name: entry.config.name
|
|
1912
|
+
} : {
|
|
1913
|
+
type: "auth-required",
|
|
1914
|
+
name: entry.config.name,
|
|
1915
|
+
reason: "no-tokens"
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
} catch (err) {
|
|
1919
|
+
debugLog$1("discoverProjectMcps failed", err);
|
|
1920
|
+
}
|
|
1921
|
+
return () => {
|
|
1922
|
+
filesSlot.abort();
|
|
1923
|
+
skillsSlot.abort();
|
|
1924
|
+
};
|
|
1925
|
+
}, [
|
|
1926
|
+
projectDir,
|
|
1927
|
+
config.prefix,
|
|
1928
|
+
mcpCredentialStore
|
|
1929
|
+
]);
|
|
1930
|
+
const ensureFiles = useCallback(() => filesSlotRef.current?.ensure() ?? Promise.resolve([]), []);
|
|
1931
|
+
const ensureSkills = useCallback(() => skillsSlotRef.current?.ensure() ?? Promise.resolve([]), []);
|
|
1932
|
+
const refreshFiles = useCallback(() => filesSlotRef.current?.refresh() ?? Promise.resolve(), []);
|
|
1933
|
+
const refreshSkills = useCallback(() => skillsSlotRef.current?.refresh() ?? Promise.resolve(), []);
|
|
1934
|
+
const refreshMcps = useCallback(() => {
|
|
1935
|
+
try {
|
|
1936
|
+
const { servers, errors } = discoverProjectMcps({
|
|
1937
|
+
cwd: projectDir,
|
|
1938
|
+
prefix: config.prefix
|
|
1939
|
+
});
|
|
1940
|
+
setMcpsCatalog(servers);
|
|
1941
|
+
setMcpsErrors(errors);
|
|
1942
|
+
for (const entry of servers) {
|
|
1943
|
+
if (entry.config.auth !== "oauth") continue;
|
|
1944
|
+
const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
|
|
1945
|
+
dispatchAuthRef.current(hasTokens ? {
|
|
1946
|
+
type: "auth-success",
|
|
1947
|
+
name: entry.config.name
|
|
1948
|
+
} : {
|
|
1949
|
+
type: "auth-required",
|
|
1950
|
+
name: entry.config.name,
|
|
1951
|
+
reason: "no-tokens"
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
} catch (err) {
|
|
1955
|
+
debugLog$1("refreshMcps failed", err);
|
|
1956
|
+
}
|
|
1957
|
+
return Promise.resolve();
|
|
1958
|
+
}, [
|
|
1959
|
+
projectDir,
|
|
1960
|
+
config.prefix,
|
|
1961
|
+
mcpCredentialStore
|
|
1962
|
+
]);
|
|
1963
|
+
return /* @__PURE__ */ jsx(DiscoveryProvider, {
|
|
1964
|
+
value: useMemo(() => ({
|
|
1965
|
+
skillsCatalog,
|
|
1966
|
+
mcpsCatalog,
|
|
1967
|
+
mcpsErrors,
|
|
1968
|
+
filesCatalog,
|
|
1969
|
+
refreshSkills,
|
|
1970
|
+
refreshMcps,
|
|
1971
|
+
refreshFiles,
|
|
1972
|
+
ensureSkills,
|
|
1973
|
+
ensureFiles
|
|
1974
|
+
}), [
|
|
1975
|
+
skillsCatalog,
|
|
1976
|
+
mcpsCatalog,
|
|
1977
|
+
mcpsErrors,
|
|
1978
|
+
filesCatalog,
|
|
1979
|
+
refreshSkills,
|
|
1980
|
+
refreshMcps,
|
|
1981
|
+
refreshFiles,
|
|
1982
|
+
ensureSkills,
|
|
1983
|
+
ensureFiles
|
|
1984
|
+
]),
|
|
1985
|
+
children
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
//#endregion
|
|
2010
1989
|
//#region src/tui/effort-picker.tsx
|
|
2011
1990
|
const BASE_LEVELS = [
|
|
2012
1991
|
{
|
|
@@ -5829,13 +5808,17 @@ function anchorIdFor(index) {
|
|
|
5829
5808
|
}
|
|
5830
5809
|
const COL_TITLE = " ";
|
|
5831
5810
|
const SPACER_CHECKBOX_WIDTH = " ";
|
|
5832
|
-
function SettingsModal({ skillsCatalog
|
|
5811
|
+
function SettingsModal({ skillsCatalog: skillsCatalogProp, mcpsCatalog: mcpsCatalogProp, mcpsErrors: mcpsErrorsProp, actions } = {}) {
|
|
5833
5812
|
const COLOR = useColors();
|
|
5834
5813
|
const SURFACE = useSurfaces();
|
|
5835
5814
|
const { settings, toggle: toggleBoolean, setSetting } = useSettings();
|
|
5836
5815
|
const authState = useMcpAuthState();
|
|
5837
5816
|
const inputRef = useRef(null);
|
|
5838
5817
|
const scrollboxRef = useRef(null);
|
|
5818
|
+
const discovery = useDiscoveryOptional();
|
|
5819
|
+
const skillsCatalog = discovery?.skillsCatalog ?? skillsCatalogProp ?? [];
|
|
5820
|
+
const mcpsCatalog = discovery?.mcpsCatalog ?? mcpsCatalogProp ?? [];
|
|
5821
|
+
const mcpsErrors = discovery?.mcpsErrors ?? mcpsErrorsProp;
|
|
5839
5822
|
const skillsToggle = useEnabledToggleSet({
|
|
5840
5823
|
catalog: skillsCatalog,
|
|
5841
5824
|
keyOf: (s) => s.name,
|
|
@@ -5975,6 +5958,13 @@ function SettingsModal({ skillsCatalog = [], mcpsCatalog = [], mcpsErrors, actio
|
|
|
5975
5958
|
}
|
|
5976
5959
|
if (key.name === "escape" && focusedMcpStatus.kind === "authorizing") actions?.onCancelLoginMcp?.(focusedMcp.config.name);
|
|
5977
5960
|
}
|
|
5961
|
+
if (key.ctrl && key.name === "r") {
|
|
5962
|
+
if (activeTab === "skills" && actions?.onRefreshSkills) {
|
|
5963
|
+
actions.onRefreshSkills();
|
|
5964
|
+
return;
|
|
5965
|
+
}
|
|
5966
|
+
if (activeTab === "mcps" && actions?.onRefreshMcps) actions.onRefreshMcps();
|
|
5967
|
+
}
|
|
5978
5968
|
});
|
|
5979
5969
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
5980
5970
|
title: "settings",
|
|
@@ -6067,7 +6057,9 @@ function SettingsModal({ skillsCatalog = [], mcpsCatalog = [], mcpsErrors, actio
|
|
|
6067
6057
|
children: /* @__PURE__ */ jsx(Hints, {
|
|
6068
6058
|
activeTab,
|
|
6069
6059
|
focusedMcp,
|
|
6070
|
-
focusedMcpStatus
|
|
6060
|
+
focusedMcpStatus,
|
|
6061
|
+
canRefreshSkills: !!actions?.onRefreshSkills,
|
|
6062
|
+
canRefreshMcps: !!actions?.onRefreshMcps
|
|
6071
6063
|
})
|
|
6072
6064
|
})
|
|
6073
6065
|
]
|
|
@@ -6522,11 +6514,12 @@ function renderMcpErrors(errors, home, warnColor) {
|
|
|
6522
6514
|
}, err.path))
|
|
6523
6515
|
});
|
|
6524
6516
|
}
|
|
6525
|
-
function Hints({ activeTab, focusedMcp, focusedMcpStatus }) {
|
|
6517
|
+
function Hints({ activeTab, focusedMcp, focusedMcpStatus, canRefreshSkills, canRefreshMcps }) {
|
|
6526
6518
|
const COLOR = useColors();
|
|
6527
6519
|
const showLogin = activeTab === "mcps" && !!focusedMcp && !!focusedMcpStatus && canLogin$1(focusedMcp, focusedMcpStatus);
|
|
6528
6520
|
const showLogout = activeTab === "mcps" && !!focusedMcpStatus && canLogout$1(focusedMcpStatus);
|
|
6529
6521
|
const showCancel = activeTab === "mcps" && focusedMcpStatus?.kind === "authorizing";
|
|
6522
|
+
const showRefresh = activeTab === "skills" && canRefreshSkills || activeTab === "mcps" && canRefreshMcps;
|
|
6530
6523
|
return /* @__PURE__ */ jsxs("text", {
|
|
6531
6524
|
fg: COLOR.mute,
|
|
6532
6525
|
children: [
|
|
@@ -6561,6 +6554,14 @@ function Hints({ activeTab, focusedMcp, focusedMcpStatus }) {
|
|
|
6561
6554
|
}),
|
|
6562
6555
|
" logout"
|
|
6563
6556
|
] }),
|
|
6557
|
+
showRefresh && /* @__PURE__ */ jsxs("span", { children: [
|
|
6558
|
+
" · ",
|
|
6559
|
+
/* @__PURE__ */ jsx("span", {
|
|
6560
|
+
fg: COLOR.warn,
|
|
6561
|
+
children: "ctrl+R"
|
|
6562
|
+
}),
|
|
6563
|
+
" refresh"
|
|
6564
|
+
] }),
|
|
6564
6565
|
showCancel ? /* @__PURE__ */ jsxs("span", { children: [
|
|
6565
6566
|
" · ",
|
|
6566
6567
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -6976,16 +6977,20 @@ function ThemedShell() {
|
|
|
6976
6977
|
const { settings } = useSettings();
|
|
6977
6978
|
return /* @__PURE__ */ jsx(ThemeProvider, {
|
|
6978
6979
|
theme: useMemo(() => resolveTheme(settings.theme), [settings.theme]),
|
|
6979
|
-
children: /* @__PURE__ */ jsx(MdStyleProvider, { children: /* @__PURE__ */ jsx(ChipStyleProvider, { children: /* @__PURE__ */ jsx(SafeModeProvider, { children: /* @__PURE__ */ jsx(InteractionsProvider, { children: /* @__PURE__ */ jsx(McpAuthProvider, { children: /* @__PURE__ */ jsx(ModalRoot, { children: /* @__PURE__ */ jsx(AppShell, {}) }) }) }) }) }) })
|
|
6980
|
+
children: /* @__PURE__ */ jsx(MdStyleProvider, { children: /* @__PURE__ */ jsx(ChipStyleProvider, { children: /* @__PURE__ */ jsx(SafeModeProvider, { children: /* @__PURE__ */ jsx(InteractionsProvider, { children: /* @__PURE__ */ jsx(McpAuthProvider, { children: /* @__PURE__ */ jsx(DiscoveryShell, { children: /* @__PURE__ */ jsx(ModalRoot, { children: /* @__PURE__ */ jsx(AppShell, {}) }) }) }) }) }) }) })
|
|
6980
6981
|
});
|
|
6981
6982
|
}
|
|
6982
6983
|
function AppShell() {
|
|
6984
|
+
bootTick("AppShell:render-enter");
|
|
6983
6985
|
const renderer = useRenderer();
|
|
6984
6986
|
const modal = useModal();
|
|
6985
6987
|
const config = useConfig();
|
|
6986
6988
|
const { settings } = useSettings();
|
|
6987
6989
|
const COLOR = useColors();
|
|
6988
6990
|
const SURFACE = useSurfaces();
|
|
6991
|
+
useEffect(() => {
|
|
6992
|
+
bootTick("AppShell:first-effect (post-first-paint)");
|
|
6993
|
+
}, []);
|
|
6989
6994
|
const queue = useSafeModeQueue();
|
|
6990
6995
|
const { requestApproval, resolveHead, denyAll } = useSafeModeActions();
|
|
6991
6996
|
const pendingApproval = queue[0] ?? null;
|
|
@@ -7045,70 +7050,11 @@ function AppShell() {
|
|
|
7045
7050
|
safelistRef.current = null;
|
|
7046
7051
|
}, [dataDir, projectDir]);
|
|
7047
7052
|
const sessionSafelistRef = useRef(/* @__PURE__ */ new Set());
|
|
7048
|
-
const
|
|
7049
|
-
const [mcpsCatalog, setMcpsCatalog] = useState([]);
|
|
7050
|
-
const [mcpsErrors, setMcpsErrors] = useState([]);
|
|
7051
|
-
const [filesCatalog, setFilesCatalog] = useState([]);
|
|
7053
|
+
const { skillsCatalog, mcpsCatalog, filesCatalog, ensureFiles: ensureFilesCatalog, ensureSkills: ensureSkillsCatalog, refreshSkills: onRefreshSkills, refreshMcps: onRefreshMcps } = useDiscovery();
|
|
7052
7054
|
const mcpCredentialStore = useMemo(() => createFileMcpCredentialStore(dataDir), [dataDir]);
|
|
7053
7055
|
const dispatchAuth = useMcpAuthDispatch();
|
|
7054
7056
|
const dispatchAuthRef = useRef(dispatchAuth);
|
|
7055
7057
|
dispatchAuthRef.current = dispatchAuth;
|
|
7056
|
-
useEffect(() => {
|
|
7057
|
-
const ac = new AbortController();
|
|
7058
|
-
let cancelled = false;
|
|
7059
|
-
(async () => {
|
|
7060
|
-
try {
|
|
7061
|
-
const skills = await discoverProjectSkills({
|
|
7062
|
-
cwd: projectDir,
|
|
7063
|
-
prefix: config.prefix
|
|
7064
|
-
});
|
|
7065
|
-
if (!cancelled) setSkillsCatalog(skills);
|
|
7066
|
-
} catch (err) {
|
|
7067
|
-
debugLog("discoverProjectSkills failed", err);
|
|
7068
|
-
}
|
|
7069
|
-
})();
|
|
7070
|
-
(async () => {
|
|
7071
|
-
try {
|
|
7072
|
-
const files = await listProjectFiles({
|
|
7073
|
-
cwd: projectDir,
|
|
7074
|
-
signal: ac.signal
|
|
7075
|
-
});
|
|
7076
|
-
if (!cancelled) setFilesCatalog(files);
|
|
7077
|
-
} catch (err) {
|
|
7078
|
-
debugLog("listProjectFiles failed", err);
|
|
7079
|
-
}
|
|
7080
|
-
})();
|
|
7081
|
-
try {
|
|
7082
|
-
const { servers, errors } = discoverProjectMcps({
|
|
7083
|
-
cwd: projectDir,
|
|
7084
|
-
prefix: config.prefix
|
|
7085
|
-
});
|
|
7086
|
-
setMcpsCatalog(servers);
|
|
7087
|
-
setMcpsErrors(errors);
|
|
7088
|
-
for (const entry of servers) {
|
|
7089
|
-
if (entry.config.auth !== "oauth") continue;
|
|
7090
|
-
const hasTokens = !!mcpCredentialStore.load(entry.config.name)?.tokens;
|
|
7091
|
-
dispatchAuthRef.current(hasTokens ? {
|
|
7092
|
-
type: "auth-success",
|
|
7093
|
-
name: entry.config.name
|
|
7094
|
-
} : {
|
|
7095
|
-
type: "auth-required",
|
|
7096
|
-
name: entry.config.name,
|
|
7097
|
-
reason: "no-tokens"
|
|
7098
|
-
});
|
|
7099
|
-
}
|
|
7100
|
-
} catch (err) {
|
|
7101
|
-
debugLog("discoverProjectMcps failed", err);
|
|
7102
|
-
}
|
|
7103
|
-
return () => {
|
|
7104
|
-
cancelled = true;
|
|
7105
|
-
ac.abort();
|
|
7106
|
-
};
|
|
7107
|
-
}, [
|
|
7108
|
-
projectDir,
|
|
7109
|
-
config.prefix,
|
|
7110
|
-
mcpCredentialStore
|
|
7111
|
-
]);
|
|
7112
7058
|
const skillsCatalogRef = useRef(skillsCatalog);
|
|
7113
7059
|
skillsCatalogRef.current = skillsCatalog;
|
|
7114
7060
|
const enabledSkillsRef = useRef(settings.enabledSkills);
|
|
@@ -7119,10 +7065,18 @@ function AppShell() {
|
|
|
7119
7065
|
enabledMcpsRef.current = settings.enabledMcps;
|
|
7120
7066
|
const filesCatalogRef = useRef(filesCatalog);
|
|
7121
7067
|
filesCatalogRef.current = filesCatalog;
|
|
7068
|
+
const ensureFilesCatalogRef = useRef(ensureFilesCatalog);
|
|
7069
|
+
ensureFilesCatalogRef.current = ensureFilesCatalog;
|
|
7070
|
+
const ensureSkillsCatalogRef = useRef(ensureSkillsCatalog);
|
|
7071
|
+
ensureSkillsCatalogRef.current = ensureSkillsCatalog;
|
|
7122
7072
|
const completionProviders = useMemo(() => [createSkillsCompletionProvider({
|
|
7123
7073
|
getCatalog: () => skillsCatalogRef.current,
|
|
7124
|
-
getEnabled: () => enabledSkillsRef.current
|
|
7125
|
-
|
|
7074
|
+
getEnabled: () => enabledSkillsRef.current,
|
|
7075
|
+
ensureCatalog: () => ensureSkillsCatalogRef.current()
|
|
7076
|
+
}), createFilesCompletionProvider({
|
|
7077
|
+
getCatalog: () => filesCatalogRef.current,
|
|
7078
|
+
ensureCatalog: () => ensureFilesCatalogRef.current()
|
|
7079
|
+
})], [filesCatalog, skillsCatalog]);
|
|
7126
7080
|
/**
|
|
7127
7081
|
* Single source of truth for "should this call execute?". Returns true to
|
|
7128
7082
|
* let the call through, false to refuse it. Handles three short-circuits:
|
|
@@ -7687,13 +7641,19 @@ function AppShell() {
|
|
|
7687
7641
|
const sessionMatchesProject = settings.showAllProjects || data?.projectRoot != null && data.projectRoot === projectDir;
|
|
7688
7642
|
if (data && sessionMatchesProject) {
|
|
7689
7643
|
await activateSession(lastResumedSessionId, resumeProvider.key);
|
|
7644
|
+
bootTick(`resume:activate (sessionId=${lastResumedSessionId.slice(-8)})`);
|
|
7690
7645
|
return;
|
|
7691
7646
|
}
|
|
7692
7647
|
}
|
|
7693
7648
|
const list = await refreshSessions();
|
|
7694
7649
|
if (cancelled) return;
|
|
7695
|
-
if (list.length === 0)
|
|
7696
|
-
|
|
7650
|
+
if (list.length === 0) {
|
|
7651
|
+
await activateSession(null, resumeProvider.key);
|
|
7652
|
+
bootTick("resume:fresh-session (empty list)");
|
|
7653
|
+
} else {
|
|
7654
|
+
setScreen("sessions");
|
|
7655
|
+
bootTick(`resume:sessions-list (${list.length})`);
|
|
7656
|
+
}
|
|
7697
7657
|
})();
|
|
7698
7658
|
return () => {
|
|
7699
7659
|
cancelled = true;
|
|
@@ -8578,18 +8538,15 @@ function AppShell() {
|
|
|
8578
8538
|
return;
|
|
8579
8539
|
}
|
|
8580
8540
|
if (matchesBinding(key, keybindings.openSettings) && screen !== "auth") {
|
|
8581
|
-
modal.open(/* @__PURE__ */ jsx(SettingsModal, {
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
onCancelLoginMcp
|
|
8591
|
-
}
|
|
8592
|
-
}));
|
|
8541
|
+
modal.open(/* @__PURE__ */ jsx(SettingsModal, { actions: {
|
|
8542
|
+
onReauth,
|
|
8543
|
+
onOpenKeybindings: onOpenKeybindingsFile,
|
|
8544
|
+
onLoginMcp,
|
|
8545
|
+
onLogoutMcp,
|
|
8546
|
+
onCancelLoginMcp,
|
|
8547
|
+
onRefreshSkills,
|
|
8548
|
+
onRefreshMcps
|
|
8549
|
+
} }));
|
|
8593
8550
|
return;
|
|
8594
8551
|
}
|
|
8595
8552
|
if (matchesBinding(key, keybindings.openSessionDetails)) {
|
|
@@ -8691,23 +8648,16 @@ function AppShell() {
|
|
|
8691
8648
|
drop: keybindings.dropQueuedMessage
|
|
8692
8649
|
}), [keybindings]);
|
|
8693
8650
|
const promptTriggerHints = useMemo(() => {
|
|
8694
|
-
|
|
8695
|
-
if (filesCatalog.length > 0) out.push({
|
|
8651
|
+
return [{
|
|
8696
8652
|
key: "@",
|
|
8697
8653
|
label: "files",
|
|
8698
8654
|
keyColor: resolveChipColor(SURFACE.chips, "files").bg
|
|
8699
|
-
}
|
|
8700
|
-
if (skillsCatalog.length > 0) out.push({
|
|
8655
|
+
}, {
|
|
8701
8656
|
key: "/",
|
|
8702
8657
|
label: "skills",
|
|
8703
8658
|
keyColor: resolveChipColor(SURFACE.chips, "skills").bg
|
|
8704
|
-
}
|
|
8705
|
-
|
|
8706
|
-
}, [
|
|
8707
|
-
filesCatalog,
|
|
8708
|
-
skillsCatalog,
|
|
8709
|
-
SURFACE
|
|
8710
|
-
]);
|
|
8659
|
+
}];
|
|
8660
|
+
}, [SURFACE]);
|
|
8711
8661
|
const contextUsage = useMemo(() => {
|
|
8712
8662
|
if (screen !== "chat" || !picked) return null;
|
|
8713
8663
|
const descriptor = providerRegistry[picked.provider.key];
|
|
@@ -9085,16 +9035,45 @@ function resolveLocalParsers() {
|
|
|
9085
9035
|
return resolved;
|
|
9086
9036
|
}
|
|
9087
9037
|
let registered = false;
|
|
9038
|
+
let workerInitStarted = false;
|
|
9088
9039
|
/**
|
|
9089
|
-
*
|
|
9090
|
-
*
|
|
9091
|
-
*
|
|
9040
|
+
* Synchronously append every URL-fetched + locally-vendored grammar to
|
|
9041
|
+
* OpenTUI's global parser registry. Cheap (just a couple of `Array.push`
|
|
9042
|
+
* calls into a module-level table) and idempotent — subsequent calls
|
|
9043
|
+
* are no-ops.
|
|
9044
|
+
*
|
|
9045
|
+
* Split out from {@link initTreeSitterWorker} so the boot path can land
|
|
9046
|
+
* the registry *synchronously* on its critical path (no awaiting
|
|
9047
|
+
* required) and defer the heavier worker spin-up below until after the
|
|
9048
|
+
* first frame is on screen. OpenTUI's `highlightOnce` self-initialises
|
|
9049
|
+
* the worker on first use anyway — registering parsers ahead of time is
|
|
9050
|
+
* the only piece that genuinely has to happen synchronously.
|
|
9092
9051
|
*/
|
|
9093
|
-
|
|
9052
|
+
function registerTreeSitterParsers() {
|
|
9094
9053
|
if (registered) return;
|
|
9095
9054
|
registered = true;
|
|
9096
9055
|
addDefaultParsers([...EXTRA_PARSERS, ...resolveLocalParsers()]);
|
|
9097
|
-
|
|
9056
|
+
}
|
|
9057
|
+
/**
|
|
9058
|
+
* Spin up OpenTUI's parser worker thread. Idempotent — concurrent
|
|
9059
|
+
* callers share a single in-flight promise; subsequent calls after
|
|
9060
|
+
* resolution are no-ops.
|
|
9061
|
+
*
|
|
9062
|
+
* Safe to fire-and-forget after the renderer mounts: the worker boot
|
|
9063
|
+
* is the heaviest step in tree-sitter setup (~40–100ms on most
|
|
9064
|
+
* machines) and nothing visible needs it until the first fenced code
|
|
9065
|
+
* block highlight, which is many frames away even on the fastest
|
|
9066
|
+
* sessions. If a `<markdown>` element happens to call
|
|
9067
|
+
* `highlightOnce()` before our explicit init completes, OpenTUI's
|
|
9068
|
+
* client guards that call with its own `if (!this.initialized) await
|
|
9069
|
+
* this.initialize()` — no race.
|
|
9070
|
+
*/
|
|
9071
|
+
function initTreeSitterWorker() {
|
|
9072
|
+
if (!workerInitStarted) {
|
|
9073
|
+
workerInitStarted = true;
|
|
9074
|
+
registerTreeSitterParsers();
|
|
9075
|
+
}
|
|
9076
|
+
return getTreeSitterClient().initialize();
|
|
9098
9077
|
}
|
|
9099
9078
|
//#endregion
|
|
9100
9079
|
//#region src/tui/mcps-settings.tsx
|
|
@@ -9125,7 +9104,7 @@ async function setupTreeSitter() {
|
|
|
9125
9104
|
* Errors (`DiscoveryError[]`) surface in a warn-colored preamble so a
|
|
9126
9105
|
* broken `mcps.json` is loud rather than invisible.
|
|
9127
9106
|
*/
|
|
9128
|
-
function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin }) {
|
|
9107
|
+
function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin, onRefresh }) {
|
|
9129
9108
|
const COLOR = useColors();
|
|
9130
9109
|
const home = homedir();
|
|
9131
9110
|
const authState = useMcpAuthState();
|
|
@@ -9166,7 +9145,11 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin }
|
|
|
9166
9145
|
onLogin(name);
|
|
9167
9146
|
return;
|
|
9168
9147
|
}
|
|
9169
|
-
if (key.name === "o" && canLogout(status))
|
|
9148
|
+
if (key.name === "o" && canLogout(status)) {
|
|
9149
|
+
onLogout(name);
|
|
9150
|
+
return;
|
|
9151
|
+
}
|
|
9152
|
+
if (key.name === "r" && onRefresh) onRefresh();
|
|
9170
9153
|
});
|
|
9171
9154
|
if (catalog.length === 0) return /* @__PURE__ */ jsxs(Modal, {
|
|
9172
9155
|
title: "mcp servers",
|
|
@@ -9227,10 +9210,17 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin }
|
|
|
9227
9210
|
}),
|
|
9228
9211
|
/* @__PURE__ */ jsxs("text", {
|
|
9229
9212
|
fg: COLOR.mute,
|
|
9230
|
-
children: [
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9213
|
+
children: [
|
|
9214
|
+
onRefresh && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
9215
|
+
fg: COLOR.warn,
|
|
9216
|
+
children: "r"
|
|
9217
|
+
}), " refresh · "] }),
|
|
9218
|
+
/* @__PURE__ */ jsx("span", {
|
|
9219
|
+
fg: COLOR.warn,
|
|
9220
|
+
children: "esc"
|
|
9221
|
+
}),
|
|
9222
|
+
" close"
|
|
9223
|
+
]
|
|
9234
9224
|
})
|
|
9235
9225
|
]
|
|
9236
9226
|
});
|
|
@@ -9272,7 +9262,7 @@ function McpsSettingsModal({ catalog, errors, onLogin, onLogout, onCancelLogin }
|
|
|
9272
9262
|
})
|
|
9273
9263
|
}),
|
|
9274
9264
|
focusedEntry && focusedStatus && renderDetailPanel(focusedEntry, focusedStatus, COLOR),
|
|
9275
|
-
renderActionHints(focusedEntry, focusedStatus, COLOR)
|
|
9265
|
+
renderActionHints(focusedEntry, focusedStatus, !!onRefresh, COLOR)
|
|
9276
9266
|
]
|
|
9277
9267
|
});
|
|
9278
9268
|
}
|
|
@@ -9383,7 +9373,7 @@ function renderDetailPanel(entry, status, COLOR) {
|
|
|
9383
9373
|
});
|
|
9384
9374
|
return null;
|
|
9385
9375
|
}
|
|
9386
|
-
function renderActionHints(entry, status, COLOR) {
|
|
9376
|
+
function renderActionHints(entry, status, showRefresh, COLOR) {
|
|
9387
9377
|
const effectiveStatus = status ?? { kind: "idle" };
|
|
9388
9378
|
const canL = entry ? canLogin(entry, effectiveStatus) : false;
|
|
9389
9379
|
const canO = canLogout(effectiveStatus);
|
|
@@ -9417,6 +9407,14 @@ function renderActionHints(entry, status, COLOR) {
|
|
|
9417
9407
|
}),
|
|
9418
9408
|
" logout"
|
|
9419
9409
|
] }),
|
|
9410
|
+
showRefresh && /* @__PURE__ */ jsxs("span", { children: [
|
|
9411
|
+
" · ",
|
|
9412
|
+
/* @__PURE__ */ jsx("span", {
|
|
9413
|
+
fg: COLOR.warn,
|
|
9414
|
+
children: "r"
|
|
9415
|
+
}),
|
|
9416
|
+
" refresh"
|
|
9417
|
+
] }),
|
|
9420
9418
|
canCancel && /* @__PURE__ */ jsxs("span", { children: [
|
|
9421
9419
|
" · ",
|
|
9422
9420
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -9467,7 +9465,7 @@ function displayPath(path, home) {
|
|
|
9467
9465
|
* (chat layer) — a GUI shell can build its own toggle list against the
|
|
9468
9466
|
* same hook without pulling OpenTUI.
|
|
9469
9467
|
*/
|
|
9470
|
-
function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, emptyState, preamble }) {
|
|
9468
|
+
function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, emptyState, preamble, onRefresh }) {
|
|
9471
9469
|
const COLOR = useColors();
|
|
9472
9470
|
const { enabledSet, toggle } = useEnabledToggleSet({
|
|
9473
9471
|
catalog,
|
|
@@ -9486,7 +9484,7 @@ function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, empt
|
|
|
9486
9484
|
else if (key.name === "return" || key.name === "space") {
|
|
9487
9485
|
const entry = catalog[safeCursor];
|
|
9488
9486
|
if (entry) toggle(keyOf(entry));
|
|
9489
|
-
}
|
|
9487
|
+
} else if (key.ctrl && key.name === "r" && onRefresh) onRefresh();
|
|
9490
9488
|
});
|
|
9491
9489
|
if (catalog.length === 0) return /* @__PURE__ */ jsxs(Modal, {
|
|
9492
9490
|
title,
|
|
@@ -9495,10 +9493,17 @@ function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, empt
|
|
|
9495
9493
|
emptyState,
|
|
9496
9494
|
/* @__PURE__ */ jsxs("text", {
|
|
9497
9495
|
fg: COLOR.mute,
|
|
9498
|
-
children: [
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9496
|
+
children: [
|
|
9497
|
+
onRefresh && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
9498
|
+
fg: COLOR.warn,
|
|
9499
|
+
children: "ctrl+R"
|
|
9500
|
+
}), " refresh · "] }),
|
|
9501
|
+
/* @__PURE__ */ jsx("span", {
|
|
9502
|
+
fg: COLOR.warn,
|
|
9503
|
+
children: "esc"
|
|
9504
|
+
}),
|
|
9505
|
+
" close"
|
|
9506
|
+
]
|
|
9502
9507
|
})
|
|
9503
9508
|
]
|
|
9504
9509
|
});
|
|
@@ -9548,6 +9553,10 @@ function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, empt
|
|
|
9548
9553
|
children: "↵"
|
|
9549
9554
|
}),
|
|
9550
9555
|
" toggle · ",
|
|
9556
|
+
onRefresh && /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
9557
|
+
fg: COLOR.warn,
|
|
9558
|
+
children: "ctrl+R"
|
|
9559
|
+
}), " refresh · "] }),
|
|
9551
9560
|
/* @__PURE__ */ jsx("span", {
|
|
9552
9561
|
fg: COLOR.warn,
|
|
9553
9562
|
children: "esc"
|
|
@@ -9564,8 +9573,13 @@ function ToggleListModal({ catalog, keyOf, settingKey, title, renderDetail, empt
|
|
|
9564
9573
|
* List + toggle modal for discovered skills. State machine + keyboard +
|
|
9565
9574
|
* row geometry live in `<ToggleListModal>`; this file just supplies the
|
|
9566
9575
|
* skill-specific column (description) and the empty-state hint.
|
|
9576
|
+
*
|
|
9577
|
+
* Pass `onRefresh` to expose `ctrl+R` (force re-scan) on the list. The
|
|
9578
|
+
* standalone modal is for embedders — the in-app flow goes through
|
|
9579
|
+
* `<SettingsModal>` which exposes the same affordance via its own
|
|
9580
|
+
* `SettingsActions.onRefreshSkills` plumbing.
|
|
9567
9581
|
*/
|
|
9568
|
-
function SkillsSettingsModal({ catalog }) {
|
|
9582
|
+
function SkillsSettingsModal({ catalog, onRefresh }) {
|
|
9569
9583
|
const COLOR = useColors();
|
|
9570
9584
|
return /* @__PURE__ */ jsx(ToggleListModal, {
|
|
9571
9585
|
catalog,
|
|
@@ -9573,6 +9587,7 @@ function SkillsSettingsModal({ catalog }) {
|
|
|
9573
9587
|
settingKey: "enabledSkills",
|
|
9574
9588
|
title: "skills",
|
|
9575
9589
|
renderDetail: (skill) => skill.description,
|
|
9590
|
+
onRefresh,
|
|
9576
9591
|
emptyState: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("text", {
|
|
9577
9592
|
fg: COLOR.dim,
|
|
9578
9593
|
children: "No skills discovered."
|
|
@@ -9662,14 +9677,14 @@ let runTuiInvoked = false;
|
|
|
9662
9677
|
async function runTui(options = {}) {
|
|
9663
9678
|
if (runTuiInvoked) throw new Error("runTui() can only be invoked once per process. Compose `<App config={resolveConfig(...)} />` against your own renderer if you need to run multiple TUIs in the same lifetime.");
|
|
9664
9679
|
runTuiInvoked = true;
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
});
|
|
9680
|
+
bootTick("runTui:enter");
|
|
9681
|
+
registerTreeSitterParsers();
|
|
9682
|
+
bootTick("runTui:after-registerTreeSitterParsers");
|
|
9669
9683
|
const config = resolveConfig({
|
|
9670
9684
|
...options,
|
|
9671
9685
|
store: options.store ?? ((paths) => createTuiStore(paths.db))
|
|
9672
9686
|
});
|
|
9687
|
+
bootTick("runTui:after-resolveConfig");
|
|
9673
9688
|
let done = () => {};
|
|
9674
9689
|
const exited = new Promise((resolve) => {
|
|
9675
9690
|
done = resolve;
|
|
@@ -9684,7 +9699,13 @@ async function runTui(options = {}) {
|
|
|
9684
9699
|
maxFps: bootFps,
|
|
9685
9700
|
gatherStats
|
|
9686
9701
|
});
|
|
9702
|
+
bootTick("runTui:after-createCliRenderer");
|
|
9687
9703
|
createRoot(renderer).render(/* @__PURE__ */ jsx(App, { config }));
|
|
9704
|
+
bootTick("runTui:after-mount");
|
|
9705
|
+
initTreeSitterWorker().then(() => bootTick("runTui:after-treeSitterWorker"), (err) => {
|
|
9706
|
+
const cause = err instanceof Error ? err.message : String(err);
|
|
9707
|
+
process.stderr.write(`[zidane/tui] tree-sitter setup failed: ${cause}\n`);
|
|
9708
|
+
});
|
|
9688
9709
|
await exited;
|
|
9689
9710
|
if (gatherStats) try {
|
|
9690
9711
|
const s = renderer.getStats();
|
|
@@ -9695,6 +9716,6 @@ async function runTui(options = {}) {
|
|
|
9695
9716
|
process.exit(0);
|
|
9696
9717
|
}
|
|
9697
9718
|
//#endregion
|
|
9698
|
-
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TOOL_DISPLAY, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, displayNameFor, formatToolCall, hintsLength, isEditErrorResult, isTurnHighlighted, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, selectableTurnIds, splitPromptSegments, turnSelectionOwnership, useMdStyle, useModal, useModalAwareFocus };
|
|
9719
|
+
export { AgentPickerModal, App, AuthScreen, ChatScreen, CompletionPopup, EffortPickerModal, Footer, InteractionBlock, McpsSettingsModal, Modal, ModalRoot, ModelPickerModal, SessionDetailsModal, SessionsScreen, SettingsModal, SkillsSettingsModal, Spinner, StatusSpinner, TOOL_DISPLAY, TitleOverlay, ToggleListModal, Transcript, TurnDetailsModal, accentColor, buildMdStyle, clipHintsToWidth, computeTurnAnchors, displayNameFor, formatToolCall, hintsLength, isEditErrorResult, isTurnHighlighted, isVisible, marginTopFor, onInputSubmit, renderHintSpans, runTui, selectableTurnIds, splitMarkdownCodeBlocks, splitPromptSegments, truncateTrailing, turnSelectionOwnership, useMdStyle, useModal, useModalAwareFocus };
|
|
9699
9720
|
|
|
9700
9721
|
//# sourceMappingURL=tui.js.map
|