zidane 5.0.3 → 5.0.5
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/chat.d.ts +59 -2
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +2 -2
- package/dist/{theme-pJv47erq.d.ts → theme-C3JHZ5y9.d.ts} +31 -3
- package/dist/theme-C3JHZ5y9.d.ts.map +1 -0
- package/dist/tui.d.ts +25 -15
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +529 -75
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CHS2Prne.js → turn-operations-DZ3TrljX.js} +100 -8
- package/dist/turn-operations-DZ3TrljX.js.map +1 -0
- package/package.json +1 -1
- package/dist/theme-pJv47erq.d.ts.map +0 -1
- package/dist/turn-operations-CHS2Prne.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -2,7 +2,7 @@ import { d as createAgent } from "./tools-CLazLRb4.js";
|
|
|
2
2
|
import { n as formatTokenUsage } from "./stats-DZIsGqzu.js";
|
|
3
3
|
import { n as loadSession, t as createSession } from "./session-791hhrFa.js";
|
|
4
4
|
import { createTuiStore } from "./session/sqlite.js";
|
|
5
|
-
import {
|
|
5
|
+
import { $ as SETTINGS_TOGGLES, C as useSafeModeQueue, D as isOnSafelist, E as getSafelist, Et as findGitRoot, F as supportsOAuth, G as ageString, I as buildModelCatalog, J as shortId, K as compactPath, L as filterModelCatalog, Lt as useCompletion, N as splitPromptSegments, Ot as createSkillsCompletionProvider, P as runOAuthLogin, Q as SETTINGS_CHOICES, Qt as modelSupportsReasoning, R as indexOfEntry, Rt as detectAuth, S as useSafeModeActions, St as stripSpawnTokensLine, T as addToSafelist, Tt as toolResultText, V as discoverProjectMcps, W as generateSessionTitle, Wt as setProviderCredential, X as useEnabledToggleSet, Xt as getContextWindow, Y as listProjectFiles, Z as DEFAULT_SETTINGS, _ as discoverProjectSkills, _t as lastContextSizeFromTurns, a as ThemeProvider, at as resolveTheme, b as writeSessionExport, c as useSurfaces, d as finalizeStreamingMarkdown, dt as ConfigProvider, et as SettingsProvider, f as finalizeStreamingMarkdownForOwner, ft as useConfig, g as defaultSkillScanPaths, gt as eventsFromTurns, h as buildSkillsConfig, ht as deriveSessionTitle, i as turnAsText, it as resolveChipColor, j as suggestSafelistEntry, jt as createFilesCompletionProvider, kt as uniqueSkillNamesFromReferences, m as useStreamBuffer, n as deleteTurnSafely, nn as piIdOf, o as useColors, p as turnContextSize, pt as resolveConfig, q as fmtTokens, r as truncateTurnsAt, s as useSelectStyle, tt as useSettings, u as useTheme, vt as listSessionMeta, wt as toolCallPreview, x as SafeModeProvider, xt as selectableTurnIds, z as buildMcpServers } from "./turn-operations-DZ3TrljX.js";
|
|
6
6
|
import { Buffer } from "node:buffer";
|
|
7
7
|
import { createContext, createElement, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { RGBA, SyntaxStyle, addDefaultParsers, createCliRenderer, defaultTextareaKeyBindings, getTreeSitterClient } from "@opentui/core";
|
|
@@ -108,7 +108,7 @@ function Modal({ title, bottomTitle, onClose, disableEscape = false, children, m
|
|
|
108
108
|
//#endregion
|
|
109
109
|
//#region src/tui/agent-picker.tsx
|
|
110
110
|
/** Cap the scroll window — a long custom registry shouldn't push the modal off-screen. */
|
|
111
|
-
const VISIBLE_ROW_CAP
|
|
111
|
+
const VISIBLE_ROW_CAP = 10;
|
|
112
112
|
/**
|
|
113
113
|
* Modal that lists the registered {@link AgentProfile}s and lets the user
|
|
114
114
|
* pick one. Rows show: `● selected · label description`.
|
|
@@ -130,8 +130,8 @@ function AgentPickerModal({ agents, currentAgentId, onPick }) {
|
|
|
130
130
|
description: p.description,
|
|
131
131
|
value: p.id
|
|
132
132
|
})), [profiles, currentAgentId]);
|
|
133
|
-
if (profiles.length === 0) return /* @__PURE__ */ jsx(EmptyState$
|
|
134
|
-
const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP
|
|
133
|
+
if (profiles.length === 0) return /* @__PURE__ */ jsx(EmptyState$1, {});
|
|
134
|
+
const visibleRows = Math.min(options.length, VISIBLE_ROW_CAP);
|
|
135
135
|
const currentMissing = initialIndex < 0;
|
|
136
136
|
const safeIndex = currentMissing ? 0 : initialIndex;
|
|
137
137
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
@@ -176,7 +176,7 @@ function AgentPickerModal({ agents, currentAgentId, onPick }) {
|
|
|
176
176
|
]
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
|
-
function EmptyState$
|
|
179
|
+
function EmptyState$1() {
|
|
180
180
|
const COLOR = useColors();
|
|
181
181
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
182
182
|
title: "select agent",
|
|
@@ -474,9 +474,17 @@ function renderHintSpans(hints, COLOR) {
|
|
|
474
474
|
fg: h.keyColor ?? COLOR.warn,
|
|
475
475
|
children: h.key
|
|
476
476
|
}),
|
|
477
|
+
h.extra && /* @__PURE__ */ jsx("span", {
|
|
478
|
+
fg: h.extra.keyColor ?? COLOR.mute,
|
|
479
|
+
children: h.extra.key
|
|
480
|
+
}),
|
|
477
481
|
/* @__PURE__ */ jsx("span", {
|
|
478
482
|
fg: h.labelColor ?? COLOR.dim,
|
|
479
483
|
children: ` ${h.label}`
|
|
484
|
+
}),
|
|
485
|
+
h.extra && /* @__PURE__ */ jsx("span", {
|
|
486
|
+
fg: h.extra.labelColor ?? COLOR.dim,
|
|
487
|
+
children: ` ${h.extra.label}`
|
|
480
488
|
})
|
|
481
489
|
] }, i));
|
|
482
490
|
}
|
|
@@ -645,7 +653,11 @@ function truncateTrailing(text, max) {
|
|
|
645
653
|
*/
|
|
646
654
|
function hintsLength(hints) {
|
|
647
655
|
if (hints.length === 0) return 0;
|
|
648
|
-
return hints.reduce((sum, h, i) =>
|
|
656
|
+
return hints.reduce((sum, h, i) => {
|
|
657
|
+
const base = h.key.length + 1 + h.label.length;
|
|
658
|
+
const extra = h.extra ? h.extra.key.length + 1 + h.extra.label.length : 0;
|
|
659
|
+
return sum + base + extra + (i > 0 ? 3 : 0);
|
|
660
|
+
}, 0);
|
|
649
661
|
}
|
|
650
662
|
function contextIndicatorLength(context) {
|
|
651
663
|
const ratio = context.max > 0 ? context.used / context.max : 0;
|
|
@@ -721,7 +733,7 @@ function Transcript({ events, settings, selectedTurnId = null }) {
|
|
|
721
733
|
});
|
|
722
734
|
return () => cancelAnimationFrame(handle);
|
|
723
735
|
}, [selectedTurnId, anchors]);
|
|
724
|
-
if (items.length === 0) return /* @__PURE__ */ jsx(EmptyState
|
|
736
|
+
if (items.length === 0) return /* @__PURE__ */ jsx(EmptyState, {});
|
|
725
737
|
return /* @__PURE__ */ jsx("scrollbox", {
|
|
726
738
|
ref: scrollboxRef,
|
|
727
739
|
focusable: false,
|
|
@@ -893,7 +905,7 @@ function SubagentBlock({ events, previous, selectedTurnId = null, anchorIds }) {
|
|
|
893
905
|
}, i))
|
|
894
906
|
});
|
|
895
907
|
}
|
|
896
|
-
function EmptyState
|
|
908
|
+
function EmptyState() {
|
|
897
909
|
return /* @__PURE__ */ jsx("box", {
|
|
898
910
|
style: {
|
|
899
911
|
flexGrow: 1,
|
|
@@ -1203,6 +1215,192 @@ function ToolResultBlock({ text, indent }) {
|
|
|
1203
1215
|
});
|
|
1204
1216
|
}
|
|
1205
1217
|
//#endregion
|
|
1218
|
+
//#region src/tui/effort-picker.tsx
|
|
1219
|
+
const BASE_LEVELS = [
|
|
1220
|
+
{
|
|
1221
|
+
id: "off",
|
|
1222
|
+
description: "no reasoning — fastest, smallest output"
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
id: "minimal",
|
|
1226
|
+
description: "tiny reasoning budget (gpt-5 family)"
|
|
1227
|
+
},
|
|
1228
|
+
{
|
|
1229
|
+
id: "low",
|
|
1230
|
+
description: "short reasoning pass"
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
id: "medium",
|
|
1234
|
+
description: "balanced — sensible default"
|
|
1235
|
+
},
|
|
1236
|
+
{
|
|
1237
|
+
id: "high",
|
|
1238
|
+
description: "deep reasoning — slowest, longest"
|
|
1239
|
+
}
|
|
1240
|
+
];
|
|
1241
|
+
const ADAPTIVE_LEVEL = {
|
|
1242
|
+
id: "adaptive",
|
|
1243
|
+
description: "model decides per-turn (Anthropic)"
|
|
1244
|
+
};
|
|
1245
|
+
function EffortPickerModal({ current, supportsAdaptive, onPick }) {
|
|
1246
|
+
const COLOR = useColors();
|
|
1247
|
+
const SURFACE = useSurfaces();
|
|
1248
|
+
const inputRef = useRef(null);
|
|
1249
|
+
const [query, setQuery] = useState("");
|
|
1250
|
+
const levels = useMemo(() => {
|
|
1251
|
+
return (supportsAdaptive ? [...BASE_LEVELS, ADAPTIVE_LEVEL] : BASE_LEVELS).map((l) => ({
|
|
1252
|
+
...l,
|
|
1253
|
+
searchCorpus: `${l.id} ${l.description}`.toLowerCase()
|
|
1254
|
+
}));
|
|
1255
|
+
}, [supportsAdaptive]);
|
|
1256
|
+
const filtered = useMemo(() => {
|
|
1257
|
+
const trimmed = query.trim().toLowerCase();
|
|
1258
|
+
if (!trimmed) return levels;
|
|
1259
|
+
const terms = trimmed.split(/\s+/);
|
|
1260
|
+
return levels.filter((l) => terms.every((t) => l.searchCorpus.includes(t)));
|
|
1261
|
+
}, [levels, query]);
|
|
1262
|
+
const [selectedIdx, setSelectedIdx] = useState(() => {
|
|
1263
|
+
const idx = levels.findIndex((l) => l.id === current);
|
|
1264
|
+
if (idx >= 0) return idx;
|
|
1265
|
+
const fallback = levels.findIndex((l) => l.id === "medium");
|
|
1266
|
+
return fallback < 0 ? 0 : fallback;
|
|
1267
|
+
});
|
|
1268
|
+
const handleQueryChange = useCallback((next) => {
|
|
1269
|
+
setQuery(next);
|
|
1270
|
+
setSelectedIdx(0);
|
|
1271
|
+
}, []);
|
|
1272
|
+
const safeIndex = filtered.length === 0 ? 0 : Math.min(selectedIdx, filtered.length - 1);
|
|
1273
|
+
const commit = () => {
|
|
1274
|
+
const row = filtered[safeIndex];
|
|
1275
|
+
if (row) onPick(row.id);
|
|
1276
|
+
};
|
|
1277
|
+
useEffect(() => {
|
|
1278
|
+
inputRef.current?.focus();
|
|
1279
|
+
}, []);
|
|
1280
|
+
useKeyboard((key) => {
|
|
1281
|
+
if (key.name === "up") {
|
|
1282
|
+
setSelectedIdx((i) => Math.max(0, i - 1));
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
if (key.name === "down") {
|
|
1286
|
+
setSelectedIdx((i) => Math.min(filtered.length - 1, i + 1));
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
if (key.name === "return") commit();
|
|
1290
|
+
});
|
|
1291
|
+
return /* @__PURE__ */ jsxs(Modal, {
|
|
1292
|
+
title: "select reasoning effort",
|
|
1293
|
+
maxWidth: 80,
|
|
1294
|
+
children: [
|
|
1295
|
+
/* @__PURE__ */ jsx("box", {
|
|
1296
|
+
style: {
|
|
1297
|
+
border: true,
|
|
1298
|
+
borderColor: COLOR.borderActive,
|
|
1299
|
+
paddingLeft: 1,
|
|
1300
|
+
paddingRight: 1,
|
|
1301
|
+
height: 3
|
|
1302
|
+
},
|
|
1303
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
1304
|
+
ref: inputRef,
|
|
1305
|
+
focused: true,
|
|
1306
|
+
placeholder: "search effort levels…",
|
|
1307
|
+
onInput: handleQueryChange,
|
|
1308
|
+
onSubmit: () => {},
|
|
1309
|
+
style: { flexGrow: 1 }
|
|
1310
|
+
})
|
|
1311
|
+
}),
|
|
1312
|
+
/* @__PURE__ */ jsx("box", {
|
|
1313
|
+
style: {
|
|
1314
|
+
flexDirection: "column",
|
|
1315
|
+
flexShrink: 0
|
|
1316
|
+
},
|
|
1317
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsxs("text", {
|
|
1318
|
+
fg: COLOR.dim,
|
|
1319
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1320
|
+
fg: COLOR.mute,
|
|
1321
|
+
children: "no levels match "
|
|
1322
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1323
|
+
fg: COLOR.warn,
|
|
1324
|
+
children: query.trim()
|
|
1325
|
+
})]
|
|
1326
|
+
}) : filtered.map((level, i) => /* @__PURE__ */ jsx(EffortRow, {
|
|
1327
|
+
level,
|
|
1328
|
+
isCurrent: level.id === current,
|
|
1329
|
+
isFocused: i === safeIndex,
|
|
1330
|
+
highlightBg: SURFACE.selection
|
|
1331
|
+
}, level.id))
|
|
1332
|
+
}),
|
|
1333
|
+
/* @__PURE__ */ jsxs("text", {
|
|
1334
|
+
fg: COLOR.dim,
|
|
1335
|
+
children: [
|
|
1336
|
+
/* @__PURE__ */ jsx("span", {
|
|
1337
|
+
fg: COLOR.warn,
|
|
1338
|
+
children: "↑↓"
|
|
1339
|
+
}),
|
|
1340
|
+
" navigate · ",
|
|
1341
|
+
/* @__PURE__ */ jsx("span", {
|
|
1342
|
+
fg: COLOR.warn,
|
|
1343
|
+
children: "↵"
|
|
1344
|
+
}),
|
|
1345
|
+
" select · ",
|
|
1346
|
+
/* @__PURE__ */ jsx("span", {
|
|
1347
|
+
fg: COLOR.warn,
|
|
1348
|
+
children: "esc"
|
|
1349
|
+
}),
|
|
1350
|
+
" close · ",
|
|
1351
|
+
/* @__PURE__ */ jsx("span", {
|
|
1352
|
+
fg: COLOR.mute,
|
|
1353
|
+
children: `${filtered.length} / ${levels.length} level${levels.length === 1 ? "" : "s"}`
|
|
1354
|
+
})
|
|
1355
|
+
]
|
|
1356
|
+
})
|
|
1357
|
+
]
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
/**
|
|
1361
|
+
* Single row in the picker. Mirrors `ModelRow` in `model-picker.tsx`:
|
|
1362
|
+
* `●` marker for the current pick, single-space middle-dot separators,
|
|
1363
|
+
* focused row gets the `surfaces.selection` background lift.
|
|
1364
|
+
*/
|
|
1365
|
+
function EffortRow({ level, isCurrent, isFocused, highlightBg }) {
|
|
1366
|
+
const COLOR = useColors();
|
|
1367
|
+
const marker = isCurrent ? "●" : " ";
|
|
1368
|
+
return /* @__PURE__ */ jsx("box", {
|
|
1369
|
+
style: {
|
|
1370
|
+
height: 1,
|
|
1371
|
+
paddingLeft: 1,
|
|
1372
|
+
paddingRight: 1,
|
|
1373
|
+
flexShrink: 0,
|
|
1374
|
+
backgroundColor: isFocused ? highlightBg : void 0
|
|
1375
|
+
},
|
|
1376
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
1377
|
+
wrapMode: "none",
|
|
1378
|
+
children: [
|
|
1379
|
+
/* @__PURE__ */ jsx("span", {
|
|
1380
|
+
fg: isCurrent ? COLOR.brand : COLOR.mute,
|
|
1381
|
+
children: marker
|
|
1382
|
+
}),
|
|
1383
|
+
/* @__PURE__ */ jsx("span", {
|
|
1384
|
+
fg: COLOR.mute,
|
|
1385
|
+
children: " "
|
|
1386
|
+
}),
|
|
1387
|
+
/* @__PURE__ */ jsx("span", {
|
|
1388
|
+
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
1389
|
+
children: level.id
|
|
1390
|
+
}),
|
|
1391
|
+
/* @__PURE__ */ jsx("span", {
|
|
1392
|
+
fg: COLOR.mute,
|
|
1393
|
+
children: " · "
|
|
1394
|
+
}),
|
|
1395
|
+
/* @__PURE__ */ jsx("span", {
|
|
1396
|
+
fg: COLOR.mute,
|
|
1397
|
+
children: level.description
|
|
1398
|
+
})
|
|
1399
|
+
]
|
|
1400
|
+
})
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
//#endregion
|
|
1206
1404
|
//#region src/tui/toggle-list-modal.tsx
|
|
1207
1405
|
/**
|
|
1208
1406
|
* Generic list-with-checkboxes modal. Powers both the Skills and MCP
|
|
@@ -1365,50 +1563,134 @@ function McpsSettingsModal({ catalog }) {
|
|
|
1365
1563
|
}
|
|
1366
1564
|
//#endregion
|
|
1367
1565
|
//#region src/tui/model-picker.tsx
|
|
1368
|
-
/** Cap the visible scroll window so a 30-model list doesn't push the modal off-screen. */
|
|
1369
|
-
const VISIBLE_ROW_CAP = 12;
|
|
1370
1566
|
/**
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1567
|
+
* Cross-provider, searchable model picker.
|
|
1568
|
+
*
|
|
1569
|
+
* The picker unions every available provider's models into one flat
|
|
1570
|
+
* catalog, lets the user filter with a typed query, and returns both
|
|
1571
|
+
* the chosen provider and model id on commit. Pick a model from a
|
|
1572
|
+
* different provider than the current one and the caller is expected
|
|
1573
|
+
* to swap the active `ProviderAuth` + rebuild the agent — see
|
|
1574
|
+
* `app.tsx`'s `onPickModel` for the canonical wiring.
|
|
1375
1575
|
*
|
|
1376
|
-
*
|
|
1576
|
+
* Geometry:
|
|
1577
|
+
* - Top: single-row search input. The input owns focus so the user
|
|
1578
|
+
* can type immediately; ↑/↓/↵/⎋ are intercepted before the input's
|
|
1579
|
+
* text handler sees them so navigation works without losing focus.
|
|
1580
|
+
* - Middle: windowed list of catalog rows around the current
|
|
1581
|
+
* selection. Each row shows the model name (or id) in brand color,
|
|
1582
|
+
* the provider label in dim, and a compact capability suffix
|
|
1583
|
+
* (`ctx 200k · reasoning · vision`).
|
|
1584
|
+
* - Bottom: shortcut hint row.
|
|
1585
|
+
*
|
|
1586
|
+
* Empty states:
|
|
1587
|
+
* - No available providers → "no providers configured" notice.
|
|
1588
|
+
* - Query has no matches → "no matches" row, keep the search input
|
|
1589
|
+
* live so the user can backspace out.
|
|
1377
1590
|
*/
|
|
1378
|
-
|
|
1591
|
+
/** Visible rows in the windowed list. Keeps the modal a stable size on long catalogs. */
|
|
1592
|
+
const VISIBLE_ROWS = 10;
|
|
1593
|
+
function ModelPickerModal({ providers, modelsFor, current, onPick }) {
|
|
1379
1594
|
const COLOR = useColors();
|
|
1380
|
-
const
|
|
1381
|
-
const
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1595
|
+
const SURFACE = useSurfaces();
|
|
1596
|
+
const inputRef = useRef(null);
|
|
1597
|
+
const [query, setQuery] = useState("");
|
|
1598
|
+
useEffect(() => {
|
|
1599
|
+
inputRef.current?.focus();
|
|
1600
|
+
}, []);
|
|
1601
|
+
const catalog = useMemo(() => buildModelCatalog({
|
|
1602
|
+
providers,
|
|
1603
|
+
modelsFor,
|
|
1604
|
+
current
|
|
1605
|
+
}), [
|
|
1606
|
+
providers,
|
|
1607
|
+
modelsFor,
|
|
1608
|
+
current
|
|
1609
|
+
]);
|
|
1610
|
+
const filtered = useMemo(() => filterModelCatalog(catalog, query), [catalog, query]);
|
|
1611
|
+
const [selectedIdx, setSelectedIdx] = useState(() => Math.max(0, indexOfEntry(catalog, current)));
|
|
1612
|
+
const handleQueryChange = useCallback((next) => {
|
|
1613
|
+
setQuery(next);
|
|
1614
|
+
setSelectedIdx(0);
|
|
1615
|
+
}, []);
|
|
1616
|
+
const safeIndex = filtered.length === 0 ? 0 : Math.min(selectedIdx, filtered.length - 1);
|
|
1617
|
+
const commit = () => {
|
|
1618
|
+
const row = filtered[safeIndex];
|
|
1619
|
+
if (row) onPick({
|
|
1620
|
+
providerKey: row.providerKey,
|
|
1621
|
+
modelId: row.model.id
|
|
1622
|
+
});
|
|
1623
|
+
};
|
|
1624
|
+
const viewport = useMemo(() => {
|
|
1625
|
+
if (filtered.length <= VISIBLE_ROWS) return {
|
|
1626
|
+
start: 0,
|
|
1627
|
+
slice: filtered
|
|
1628
|
+
};
|
|
1629
|
+
const half = Math.floor(VISIBLE_ROWS / 2);
|
|
1630
|
+
let start = Math.max(0, safeIndex - half);
|
|
1631
|
+
if (start + VISIBLE_ROWS > filtered.length) start = filtered.length - VISIBLE_ROWS;
|
|
1632
|
+
return {
|
|
1633
|
+
start,
|
|
1634
|
+
slice: filtered.slice(start, start + VISIBLE_ROWS)
|
|
1635
|
+
};
|
|
1636
|
+
}, [filtered, safeIndex]);
|
|
1637
|
+
useKeyboard((key) => {
|
|
1638
|
+
if (key.name === "up") {
|
|
1639
|
+
setSelectedIdx((i) => Math.max(0, i - 1));
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
if (key.name === "down") {
|
|
1643
|
+
setSelectedIdx((i) => Math.min(filtered.length - 1, i + 1));
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
if (key.name === "return") commit();
|
|
1647
|
+
});
|
|
1648
|
+
if (providers.length === 0) return /* @__PURE__ */ jsx(EmptyProvidersState, {});
|
|
1391
1649
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
1392
1650
|
title: "select model",
|
|
1651
|
+
maxWidth: 100,
|
|
1393
1652
|
children: [
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1653
|
+
/* @__PURE__ */ jsx("box", {
|
|
1654
|
+
style: {
|
|
1655
|
+
border: true,
|
|
1656
|
+
borderColor: COLOR.borderActive,
|
|
1657
|
+
paddingLeft: 1,
|
|
1658
|
+
paddingRight: 1,
|
|
1659
|
+
height: 3
|
|
1660
|
+
},
|
|
1661
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
1662
|
+
ref: inputRef,
|
|
1663
|
+
focused: true,
|
|
1664
|
+
placeholder: "search models — provider, name, capability…",
|
|
1665
|
+
onInput: handleQueryChange,
|
|
1666
|
+
onSubmit: () => {},
|
|
1667
|
+
style: { flexGrow: 1 }
|
|
1668
|
+
})
|
|
1397
1669
|
}),
|
|
1398
|
-
/* @__PURE__ */ jsx("
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1670
|
+
/* @__PURE__ */ jsx("box", {
|
|
1671
|
+
style: {
|
|
1672
|
+
flexDirection: "column",
|
|
1673
|
+
height: VISIBLE_ROWS,
|
|
1674
|
+
flexShrink: 0
|
|
1675
|
+
},
|
|
1676
|
+
children: filtered.length === 0 ? /* @__PURE__ */ jsxs("text", {
|
|
1677
|
+
fg: COLOR.dim,
|
|
1678
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1679
|
+
fg: COLOR.mute,
|
|
1680
|
+
children: "no models match "
|
|
1681
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1682
|
+
fg: COLOR.warn,
|
|
1683
|
+
children: query.trim()
|
|
1684
|
+
})]
|
|
1685
|
+
}) : viewport.slice.map((entry, i) => /* @__PURE__ */ jsx(ModelRow, {
|
|
1686
|
+
entry,
|
|
1687
|
+
isCurrent: entry.providerKey === current.providerKey && entry.model.id === current.modelId,
|
|
1688
|
+
isFocused: viewport.start + i === safeIndex,
|
|
1689
|
+
highlightBg: SURFACE.selection
|
|
1690
|
+
}, `${entry.providerKey}:${entry.model.id}`))
|
|
1409
1691
|
}),
|
|
1410
1692
|
/* @__PURE__ */ jsxs("text", {
|
|
1411
|
-
fg: COLOR.
|
|
1693
|
+
fg: COLOR.dim,
|
|
1412
1694
|
children: [
|
|
1413
1695
|
/* @__PURE__ */ jsx("span", {
|
|
1414
1696
|
fg: COLOR.warn,
|
|
@@ -1424,38 +1706,94 @@ function ModelPickerModal({ models, currentModelId, onPick }) {
|
|
|
1424
1706
|
fg: COLOR.warn,
|
|
1425
1707
|
children: "esc"
|
|
1426
1708
|
}),
|
|
1427
|
-
" close"
|
|
1709
|
+
" close · ",
|
|
1710
|
+
/* @__PURE__ */ jsx("span", {
|
|
1711
|
+
fg: COLOR.mute,
|
|
1712
|
+
children: `${filtered.length} / ${catalog.length} model${catalog.length === 1 ? "" : "s"}`
|
|
1713
|
+
})
|
|
1428
1714
|
]
|
|
1429
1715
|
})
|
|
1430
1716
|
]
|
|
1431
1717
|
});
|
|
1432
1718
|
}
|
|
1433
|
-
|
|
1719
|
+
/**
|
|
1720
|
+
* Single row in the picker. Renders the model name in `brand`, the
|
|
1721
|
+
* provider tag in `dim`, and the capability suffix in `mute`. The
|
|
1722
|
+
* focused row gets a subtle selection background (same surface as
|
|
1723
|
+
* select-turn mode in the transcript) so it pops without a separate
|
|
1724
|
+
* marker glyph competing with the `●` "current" indicator.
|
|
1725
|
+
*/
|
|
1726
|
+
function ModelRow({ entry, isCurrent, isFocused, highlightBg }) {
|
|
1727
|
+
const COLOR = useColors();
|
|
1728
|
+
const marker = isCurrent ? "●" : " ";
|
|
1729
|
+
return /* @__PURE__ */ jsx("box", {
|
|
1730
|
+
style: {
|
|
1731
|
+
height: 1,
|
|
1732
|
+
paddingLeft: 1,
|
|
1733
|
+
paddingRight: 1,
|
|
1734
|
+
flexShrink: 0,
|
|
1735
|
+
backgroundColor: isFocused ? highlightBg : void 0
|
|
1736
|
+
},
|
|
1737
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
1738
|
+
wrapMode: "none",
|
|
1739
|
+
children: [
|
|
1740
|
+
/* @__PURE__ */ jsx("span", {
|
|
1741
|
+
fg: isCurrent ? COLOR.brand : COLOR.mute,
|
|
1742
|
+
children: marker
|
|
1743
|
+
}),
|
|
1744
|
+
/* @__PURE__ */ jsx("span", {
|
|
1745
|
+
fg: COLOR.mute,
|
|
1746
|
+
children: " "
|
|
1747
|
+
}),
|
|
1748
|
+
/* @__PURE__ */ jsx("span", {
|
|
1749
|
+
fg: isFocused ? COLOR.brand : COLOR.dim,
|
|
1750
|
+
children: entry.model.name ?? entry.model.id
|
|
1751
|
+
}),
|
|
1752
|
+
/* @__PURE__ */ jsx("span", {
|
|
1753
|
+
fg: COLOR.mute,
|
|
1754
|
+
children: " · "
|
|
1755
|
+
}),
|
|
1756
|
+
/* @__PURE__ */ jsx("span", {
|
|
1757
|
+
fg: COLOR.model,
|
|
1758
|
+
children: entry.providerLabel
|
|
1759
|
+
}),
|
|
1760
|
+
/* @__PURE__ */ jsx("span", {
|
|
1761
|
+
fg: COLOR.mute,
|
|
1762
|
+
children: " · "
|
|
1763
|
+
}),
|
|
1764
|
+
/* @__PURE__ */ jsx("span", {
|
|
1765
|
+
fg: COLOR.mute,
|
|
1766
|
+
children: describeModel(entry.model)
|
|
1767
|
+
})
|
|
1768
|
+
]
|
|
1769
|
+
})
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
function EmptyProvidersState() {
|
|
1434
1773
|
const COLOR = useColors();
|
|
1435
1774
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
1436
1775
|
title: "select model",
|
|
1437
1776
|
children: [/* @__PURE__ */ jsx("text", {
|
|
1438
1777
|
fg: COLOR.dim,
|
|
1439
|
-
children: "No
|
|
1778
|
+
children: "No authed providers — configure one via"
|
|
1440
1779
|
}), /* @__PURE__ */ jsxs("text", {
|
|
1441
|
-
fg: COLOR.
|
|
1780
|
+
fg: COLOR.dim,
|
|
1442
1781
|
children: [
|
|
1443
|
-
"Set",
|
|
1444
1782
|
/* @__PURE__ */ jsx("span", {
|
|
1445
1783
|
fg: COLOR.model,
|
|
1446
|
-
children: "
|
|
1784
|
+
children: " settings → re-configure providers"
|
|
1447
1785
|
}),
|
|
1448
|
-
"
|
|
1786
|
+
" or ",
|
|
1449
1787
|
/* @__PURE__ */ jsx("span", {
|
|
1450
1788
|
fg: COLOR.model,
|
|
1451
|
-
children: "
|
|
1789
|
+
children: "esc → sessions → settings"
|
|
1452
1790
|
}),
|
|
1453
|
-
"
|
|
1791
|
+
" first."
|
|
1454
1792
|
]
|
|
1455
1793
|
})]
|
|
1456
1794
|
});
|
|
1457
1795
|
}
|
|
1458
|
-
/** "ctx 200k · reasoning · vision" — compact
|
|
1796
|
+
/** "ctx 200k · reasoning · vision" — compact capability blurb. */
|
|
1459
1797
|
function describeModel(m) {
|
|
1460
1798
|
const parts = [`ctx ${fmtTokens(m.contextWindow)}`];
|
|
1461
1799
|
if (m.reasoning) parts.push("reasoning");
|
|
@@ -4031,6 +4369,7 @@ function AppShell() {
|
|
|
4031
4369
|
const SURFACE = useSurfaces();
|
|
4032
4370
|
const queue = useSafeModeQueue();
|
|
4033
4371
|
const { requestApproval, resolveHead, denyAll } = useSafeModeActions();
|
|
4372
|
+
const pendingApproval = queue[0] ?? null;
|
|
4034
4373
|
const { providers: providerRegistry, agents: agentRegistry, initialAgentId, store, stateStore, modelsFor, resumeProvider, initialPicked, initialState } = config;
|
|
4035
4374
|
const lastResumedSessionId = initialState.lastSessionId;
|
|
4036
4375
|
const dataDir = config.paths.userDir;
|
|
@@ -4160,9 +4499,15 @@ function AppShell() {
|
|
|
4160
4499
|
const descriptor = providerRegistry[provider.key];
|
|
4161
4500
|
if (!descriptor) return null;
|
|
4162
4501
|
const remembered = initialState.lastModelByProvider?.[provider.key];
|
|
4163
|
-
|
|
4502
|
+
const model = modelId ?? remembered ?? descriptor.defaultModel ?? descriptor.factory().meta.defaultModel;
|
|
4503
|
+
const effort = effortForModel(descriptor, model, initialState.lastEffortByModel);
|
|
4504
|
+
return effort ? {
|
|
4505
|
+
provider,
|
|
4506
|
+
model,
|
|
4507
|
+
effort
|
|
4508
|
+
} : {
|
|
4164
4509
|
provider,
|
|
4165
|
-
model
|
|
4510
|
+
model
|
|
4166
4511
|
};
|
|
4167
4512
|
}, [providerRegistry, initialState]);
|
|
4168
4513
|
const buildAgent = useCallback((session, key) => {
|
|
@@ -4391,6 +4736,13 @@ function AppShell() {
|
|
|
4391
4736
|
settings.showAllProjects,
|
|
4392
4737
|
sessionProjectRoot
|
|
4393
4738
|
]);
|
|
4739
|
+
const [availableProviders, setAvailableProviders] = useState([]);
|
|
4740
|
+
const refreshAvailableProviders = useCallback(() => {
|
|
4741
|
+
setAvailableProviders(detectAuth(config.paths.userDir, providerRegistry).filter((p) => p.available));
|
|
4742
|
+
}, [config.paths.userDir, providerRegistry]);
|
|
4743
|
+
useEffect(() => {
|
|
4744
|
+
refreshAvailableProviders();
|
|
4745
|
+
}, [refreshAvailableProviders]);
|
|
4394
4746
|
const onPickProvider = useCallback(async (p) => {
|
|
4395
4747
|
const next = makePicked(p);
|
|
4396
4748
|
if (!next) return;
|
|
@@ -4399,13 +4751,15 @@ function AppShell() {
|
|
|
4399
4751
|
...stateStore.load(),
|
|
4400
4752
|
lastProvider: p.key
|
|
4401
4753
|
});
|
|
4754
|
+
refreshAvailableProviders();
|
|
4402
4755
|
if ((await refreshSessions()).length === 0) await activateSession(null, p.key);
|
|
4403
4756
|
else setScreen("sessions");
|
|
4404
4757
|
}, [
|
|
4405
4758
|
refreshSessions,
|
|
4406
4759
|
activateSession,
|
|
4407
4760
|
makePicked,
|
|
4408
|
-
stateStore
|
|
4761
|
+
stateStore,
|
|
4762
|
+
refreshAvailableProviders
|
|
4409
4763
|
]);
|
|
4410
4764
|
const onCreateSession = useCallback(async () => {
|
|
4411
4765
|
if (picked) await activateSession(null, picked.provider.key);
|
|
@@ -4426,24 +4780,87 @@ function AppShell() {
|
|
|
4426
4780
|
denyAll();
|
|
4427
4781
|
agentRef.current?.abort();
|
|
4428
4782
|
}, [denyAll]);
|
|
4429
|
-
|
|
4783
|
+
/**
|
|
4784
|
+
* Pick a `{ providerKey, modelId }` tuple from the cross-provider
|
|
4785
|
+
* model picker. Two branches:
|
|
4786
|
+
*
|
|
4787
|
+
* - **Same provider, different model** — update `picked.model` +
|
|
4788
|
+
* remember it in `lastModelByProvider`. The active agent keeps
|
|
4789
|
+
* running; `agent.run({ model })` accepts the model per-call so
|
|
4790
|
+
* no session rebuild is needed.
|
|
4791
|
+
* - **Different provider** — swap the active `ProviderAuth`,
|
|
4792
|
+
* persist BOTH `lastProvider` + `lastModelByProvider`, then
|
|
4793
|
+
* re-activate the current session against the new provider's
|
|
4794
|
+
* factory. The session id (and conversation history) is
|
|
4795
|
+
* preserved; only the bound agent + provider instance change.
|
|
4796
|
+
*
|
|
4797
|
+
* Either branch re-resolves the reasoning effort: a non-reasoning
|
|
4798
|
+
* model loses its `effort`, a reasoning model gets the remembered
|
|
4799
|
+
* per-model value (falling back to a sensible default).
|
|
4800
|
+
*/
|
|
4801
|
+
const onPickModel = useCallback(async (next) => {
|
|
4802
|
+
const nextProvider = availableProviders.find((p) => p.key === next.providerKey);
|
|
4803
|
+
if (!nextProvider) {
|
|
4804
|
+
debugLog("onPickModel: unknown provider key", next.providerKey);
|
|
4805
|
+
modal.close();
|
|
4806
|
+
return;
|
|
4807
|
+
}
|
|
4808
|
+
const descriptor = providerRegistry[nextProvider.key];
|
|
4809
|
+
const prior = stateStore.load();
|
|
4810
|
+
const providerChanged = picked?.provider.key !== nextProvider.key;
|
|
4811
|
+
stateStore.save({
|
|
4812
|
+
...prior,
|
|
4813
|
+
...providerChanged ? { lastProvider: nextProvider.key } : {},
|
|
4814
|
+
lastModelByProvider: {
|
|
4815
|
+
...prior.lastModelByProvider,
|
|
4816
|
+
[nextProvider.key]: next.modelId
|
|
4817
|
+
}
|
|
4818
|
+
});
|
|
4819
|
+
const nextEffort = descriptor ? effortForModel(descriptor, next.modelId, prior.lastEffortByModel) : void 0;
|
|
4820
|
+
setPicked(nextEffort ? {
|
|
4821
|
+
provider: nextProvider,
|
|
4822
|
+
model: next.modelId,
|
|
4823
|
+
effort: nextEffort
|
|
4824
|
+
} : {
|
|
4825
|
+
provider: nextProvider,
|
|
4826
|
+
model: next.modelId
|
|
4827
|
+
});
|
|
4828
|
+
modal.close();
|
|
4829
|
+
if (providerChanged && currentSession && !busy && !pendingApproval) await activateSession(currentSession.id, nextProvider.key);
|
|
4830
|
+
}, [
|
|
4831
|
+
availableProviders,
|
|
4832
|
+
providerRegistry,
|
|
4833
|
+
stateStore,
|
|
4834
|
+
modal,
|
|
4835
|
+
picked,
|
|
4836
|
+
currentSession,
|
|
4837
|
+
busy,
|
|
4838
|
+
pendingApproval,
|
|
4839
|
+
activateSession
|
|
4840
|
+
]);
|
|
4841
|
+
const onPickEffort = useCallback((effort) => {
|
|
4430
4842
|
setPicked((prev) => {
|
|
4431
4843
|
if (!prev) return prev;
|
|
4432
4844
|
const prior = stateStore.load();
|
|
4433
4845
|
stateStore.save({
|
|
4434
4846
|
...prior,
|
|
4435
|
-
|
|
4436
|
-
...prior.
|
|
4437
|
-
[prev.
|
|
4847
|
+
lastEffortByModel: {
|
|
4848
|
+
...prior.lastEffortByModel,
|
|
4849
|
+
[prev.model]: effort
|
|
4438
4850
|
}
|
|
4439
4851
|
});
|
|
4440
4852
|
return {
|
|
4441
4853
|
...prev,
|
|
4442
|
-
|
|
4854
|
+
effort
|
|
4443
4855
|
};
|
|
4444
4856
|
});
|
|
4445
4857
|
modal.close();
|
|
4446
4858
|
}, [modal, stateStore]);
|
|
4859
|
+
const modelHasReasoning = useMemo(() => {
|
|
4860
|
+
if (!picked) return false;
|
|
4861
|
+
const descriptor = providerRegistry[picked.provider.key];
|
|
4862
|
+
return !!descriptor && modelSupportsReasoning(descriptor, picked.model);
|
|
4863
|
+
}, [picked, providerRegistry]);
|
|
4447
4864
|
const onPickAgent = useCallback(async (id) => {
|
|
4448
4865
|
const profile = agentRegistry[id];
|
|
4449
4866
|
if (!profile) return;
|
|
@@ -4500,7 +4917,8 @@ function AppShell() {
|
|
|
4500
4917
|
try {
|
|
4501
4918
|
await agent.run({
|
|
4502
4919
|
model: picked.model,
|
|
4503
|
-
prompt
|
|
4920
|
+
prompt,
|
|
4921
|
+
...picked.effort ? { thinking: picked.effort } : {}
|
|
4504
4922
|
});
|
|
4505
4923
|
await session.save().catch((err) => debugLog("session.save failed", err));
|
|
4506
4924
|
setCurrentSession((prev) => prev ? {
|
|
@@ -4521,7 +4939,6 @@ function AppShell() {
|
|
|
4521
4939
|
setBusy(false);
|
|
4522
4940
|
}
|
|
4523
4941
|
}, [picked, stream]);
|
|
4524
|
-
const pendingApproval = queue[0] ?? null;
|
|
4525
4942
|
const onReauth = useMemo(() => {
|
|
4526
4943
|
if (busy || pendingApproval) return void 0;
|
|
4527
4944
|
return () => {
|
|
@@ -4792,7 +5209,7 @@ function AppShell() {
|
|
|
4792
5209
|
}
|
|
4793
5210
|
return;
|
|
4794
5211
|
}
|
|
4795
|
-
if (key.ctrl && key.name === "
|
|
5212
|
+
if (key.ctrl && key.name === "o" && screen !== "auth") {
|
|
4796
5213
|
modal.open(/* @__PURE__ */ jsx(SettingsModal, { actions: {
|
|
4797
5214
|
onReauth,
|
|
4798
5215
|
onOpenSkills: onOpenSkillsSettings,
|
|
@@ -4812,12 +5229,25 @@ function AppShell() {
|
|
|
4812
5229
|
}
|
|
4813
5230
|
if (key.ctrl && key.name === "m" && screen === "chat" && picked && !busy) {
|
|
4814
5231
|
modal.open(/* @__PURE__ */ jsx(ModelPickerModal, {
|
|
4815
|
-
|
|
4816
|
-
|
|
5232
|
+
providers: availableProviders,
|
|
5233
|
+
modelsFor,
|
|
5234
|
+
current: {
|
|
5235
|
+
providerKey: picked.provider.key,
|
|
5236
|
+
modelId: picked.model
|
|
5237
|
+
},
|
|
4817
5238
|
onPick: onPickModel
|
|
4818
5239
|
}));
|
|
4819
5240
|
return;
|
|
4820
5241
|
}
|
|
5242
|
+
if (key.ctrl && key.name === "n" && screen === "chat" && picked && !busy && modelHasReasoning) {
|
|
5243
|
+
const descriptor = providerRegistry[picked.provider.key];
|
|
5244
|
+
modal.open(/* @__PURE__ */ jsx(EffortPickerModal, {
|
|
5245
|
+
current: picked.effort,
|
|
5246
|
+
supportsAdaptive: !!descriptor && piIdOf(descriptor) === "anthropic",
|
|
5247
|
+
onPick: onPickEffort
|
|
5248
|
+
}));
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
4821
5251
|
if (key.ctrl && key.name === "s" && screen === "chat" && !busy && !pendingApproval) {
|
|
4822
5252
|
enterSelectMode();
|
|
4823
5253
|
return;
|
|
@@ -4848,6 +5278,9 @@ function AppShell() {
|
|
|
4848
5278
|
hasMultipleAgents,
|
|
4849
5279
|
modelLabel: picked?.model ?? null,
|
|
4850
5280
|
modelColor: COLOR.model,
|
|
5281
|
+
effortLabel: modelHasReasoning ? picked?.effort ?? "medium" : null,
|
|
5282
|
+
effortColor: COLOR.warn,
|
|
5283
|
+
effortKeyColor: COLOR.warn,
|
|
4851
5284
|
agentLabel: pickedAgent.label,
|
|
4852
5285
|
agentColor: accentColor(pickedAgent.accent, COLOR)
|
|
4853
5286
|
}), [
|
|
@@ -4858,7 +5291,8 @@ function AppShell() {
|
|
|
4858
5291
|
hasMultipleAgents,
|
|
4859
5292
|
picked,
|
|
4860
5293
|
pickedAgent,
|
|
4861
|
-
COLOR
|
|
5294
|
+
COLOR,
|
|
5295
|
+
modelHasReasoning
|
|
4862
5296
|
]);
|
|
4863
5297
|
const promptTriggerHints = useMemo(() => {
|
|
4864
5298
|
const out = [];
|
|
@@ -4941,12 +5375,25 @@ function AppShell() {
|
|
|
4941
5375
|
});
|
|
4942
5376
|
}
|
|
4943
5377
|
/**
|
|
5378
|
+
* Resolve the reasoning effort to seed `Picked.effort` for the given model.
|
|
5379
|
+
* Returns `undefined` when the model has no reasoning knob; otherwise the
|
|
5380
|
+
* per-model remembered value, defaulting to `'medium'` (sensible middle
|
|
5381
|
+
* ground when the user has never picked an explicit effort for this model).
|
|
5382
|
+
*/
|
|
5383
|
+
function effortForModel(descriptor, modelId, remembered) {
|
|
5384
|
+
if (!modelSupportsReasoning(descriptor, modelId)) return void 0;
|
|
5385
|
+
return remembered?.[modelId] ?? "medium";
|
|
5386
|
+
}
|
|
5387
|
+
/**
|
|
4944
5388
|
* Build the footer's shortcut hints for the current screen. On the chat
|
|
4945
5389
|
* screen the model id rides next to its `ctrl+m` shortcut and the agent
|
|
4946
5390
|
* label rides next to `shift+tab`, each in its accent color — the bar
|
|
4947
|
-
* doubles as the status display without needing separate badges.
|
|
5391
|
+
* doubles as the status display without needing separate badges. When
|
|
5392
|
+
* the active model exposes reasoning, the `ctrl+m` hint grows a
|
|
5393
|
+
* secondary `/n` chord with the current effort label, surfacing the
|
|
5394
|
+
* effort picker as a discoverable, in-place affordance.
|
|
4948
5395
|
*/
|
|
4949
|
-
function buildHints({ screen, busy, pending, currentSession, hasMultipleAgents, modelLabel, modelColor, agentLabel, agentColor }) {
|
|
5396
|
+
function buildHints({ screen, busy, pending, currentSession, hasMultipleAgents, modelLabel, modelColor, effortLabel, effortColor, effortKeyColor, agentLabel, agentColor }) {
|
|
4950
5397
|
if (pending) return [
|
|
4951
5398
|
{
|
|
4952
5399
|
key: "↑↓",
|
|
@@ -4993,7 +5440,7 @@ function buildHints({ screen, busy, pending, currentSession, hasMultipleAgents,
|
|
|
4993
5440
|
label: "session"
|
|
4994
5441
|
},
|
|
4995
5442
|
{
|
|
4996
|
-
key: "ctrl
|
|
5443
|
+
key: "ctrl+o",
|
|
4997
5444
|
label: "settings"
|
|
4998
5445
|
},
|
|
4999
5446
|
{
|
|
@@ -5001,23 +5448,30 @@ function buildHints({ screen, busy, pending, currentSession, hasMultipleAgents,
|
|
|
5001
5448
|
label: currentSession ? "back" : "exit"
|
|
5002
5449
|
}
|
|
5003
5450
|
];
|
|
5451
|
+
const modelHint = modelLabel ? {
|
|
5452
|
+
key: "ctrl+m",
|
|
5453
|
+
label: modelLabel,
|
|
5454
|
+
labelColor: modelColor,
|
|
5455
|
+
...effortLabel ? { extra: {
|
|
5456
|
+
key: "/n",
|
|
5457
|
+
keyColor: effortKeyColor,
|
|
5458
|
+
label: effortLabel,
|
|
5459
|
+
labelColor: effortColor
|
|
5460
|
+
} } : {}
|
|
5461
|
+
} : null;
|
|
5004
5462
|
return [
|
|
5005
5463
|
...hasMultipleAgents ? [{
|
|
5006
5464
|
key: "shift+tab",
|
|
5007
5465
|
label: agentLabel,
|
|
5008
5466
|
labelColor: agentColor
|
|
5009
5467
|
}] : [],
|
|
5010
|
-
...
|
|
5011
|
-
key: "ctrl+m",
|
|
5012
|
-
label: modelLabel,
|
|
5013
|
-
labelColor: modelColor
|
|
5014
|
-
}] : [],
|
|
5468
|
+
...modelHint ? [modelHint] : [],
|
|
5015
5469
|
...currentSession ? [{
|
|
5016
5470
|
key: "ctrl+x",
|
|
5017
5471
|
label: "session"
|
|
5018
5472
|
}] : [],
|
|
5019
5473
|
{
|
|
5020
|
-
key: "ctrl
|
|
5474
|
+
key: "ctrl+o",
|
|
5021
5475
|
label: "settings"
|
|
5022
5476
|
},
|
|
5023
5477
|
{
|