@dexto/tui 1.6.8 → 1.6.10
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/components/overlays/LoginOverlay.cjs +41 -50
- package/dist/components/overlays/LoginOverlay.d.ts.map +1 -1
- package/dist/components/overlays/LoginOverlay.js +43 -44
- package/dist/components/overlays/ModelSelectorRefactored.cjs +543 -221
- package/dist/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
- package/dist/components/overlays/ModelSelectorRefactored.js +553 -223
- package/dist/components/overlays/SessionSelectorRefactored.cjs +3 -0
- package/dist/components/overlays/SessionSelectorRefactored.d.ts.map +1 -1
- package/dist/components/overlays/SessionSelectorRefactored.js +3 -0
- package/dist/containers/OverlayContainer.cjs +35 -3
- package/dist/containers/OverlayContainer.d.ts.map +1 -1
- package/dist/containers/OverlayContainer.js +36 -3
- package/dist/hooks/useInputOrchestrator.cjs +1 -1
- package/dist/hooks/useInputOrchestrator.d.ts.map +1 -1
- package/dist/hooks/useInputOrchestrator.js +1 -1
- package/dist/host/index.cjs +12 -13
- package/dist/host/index.d.ts +23 -15
- package/dist/host/index.d.ts.map +1 -1
- package/dist/host/index.js +10 -11
- package/dist/index.d.cts +17 -12
- package/dist/interactive-commands/auth/index.d.ts +1 -1
- package/dist/interactive-commands/commands.cjs +2 -0
- package/dist/interactive-commands/commands.d.ts.map +1 -1
- package/dist/interactive-commands/commands.js +3 -1
- package/dist/interactive-commands/model/index.cjs +1 -1
- package/dist/interactive-commands/model/index.js +1 -1
- package/dist/interactive-commands/session/index.cjs +2 -0
- package/dist/interactive-commands/session/index.d.ts +2 -1
- package/dist/interactive-commands/session/index.d.ts.map +1 -1
- package/dist/interactive-commands/session/index.js +2 -1
- package/dist/interactive-commands/session/session-commands.cjs +26 -0
- package/dist/interactive-commands/session/session-commands.d.ts +5 -0
- package/dist/interactive-commands/session/session-commands.d.ts.map +1 -1
- package/dist/interactive-commands/session/session-commands.js +25 -0
- package/dist/utils/modelOrdering.cjs +106 -0
- package/dist/utils/modelOrdering.d.ts +7 -0
- package/dist/utils/modelOrdering.d.ts.map +1 -0
- package/dist/utils/modelOrdering.js +81 -0
- package/dist/utils/modelOrdering.test.cjs +59 -0
- package/dist/utils/modelOrdering.test.d.ts +2 -0
- package/dist/utils/modelOrdering.test.d.ts.map +1 -0
- package/dist/utils/modelOrdering.test.js +61 -0
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import {
|
|
3
3
|
useState,
|
|
4
4
|
useEffect,
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
listOllamaModels,
|
|
15
15
|
DEFAULT_OLLAMA_URL,
|
|
16
16
|
getLocalModelById,
|
|
17
|
+
getCuratedModelRefsForProviders,
|
|
17
18
|
getOpenRouterModelCacheInfo,
|
|
18
19
|
getReasoningProfile,
|
|
19
20
|
refreshOpenRouterModelCache
|
|
@@ -23,37 +24,198 @@ import {
|
|
|
23
24
|
deleteCustomModel,
|
|
24
25
|
getAllInstalledModels,
|
|
25
26
|
loadGlobalPreferences,
|
|
26
|
-
isDextoAuthEnabled
|
|
27
|
+
isDextoAuthEnabled,
|
|
28
|
+
loadModelPickerState,
|
|
29
|
+
toggleFavoriteModel,
|
|
30
|
+
toModelPickerKey
|
|
27
31
|
} from "@dexto/agent-management";
|
|
28
32
|
import { getLLMProviderDisplayName } from "../../utils/llm-provider-display.js";
|
|
29
33
|
import { getMaxVisibleItemsForTerminalRows } from "../../utils/overlaySizing.js";
|
|
34
|
+
import { compareModelsLatestFirst, isDeprecatedModelStatus } from "../../utils/modelOrdering.js";
|
|
35
|
+
import {
|
|
36
|
+
getCachedStringWidth,
|
|
37
|
+
stripUnsafeCharacters,
|
|
38
|
+
toCodePoints
|
|
39
|
+
} from "../../utils/textUtils.js";
|
|
30
40
|
import { HintBar } from "../shared/HintBar.js";
|
|
41
|
+
const FEATURED_SECTION_LIMIT = 8;
|
|
42
|
+
const MODEL_SELECTOR_TABS = [
|
|
43
|
+
{ id: "all-models", label: "All" },
|
|
44
|
+
{ id: "featured", label: "Featured" },
|
|
45
|
+
{ id: "recents", label: "Recents" },
|
|
46
|
+
{ id: "favorites", label: "Favorites" },
|
|
47
|
+
{ id: "custom", label: "Custom" }
|
|
48
|
+
];
|
|
49
|
+
const PROVIDER_COLLATOR = new Intl.Collator("en", { sensitivity: "base" });
|
|
50
|
+
const PROVIDER_TOKEN_PATTERN = /[^a-z0-9]/g;
|
|
51
|
+
function normalizeProviderToken(value) {
|
|
52
|
+
return value.toLowerCase().replace(PROVIDER_TOKEN_PATTERN, "");
|
|
53
|
+
}
|
|
54
|
+
function toReleaseDateLookupKey(provider, modelName) {
|
|
55
|
+
return `${provider}::${modelName.toLowerCase()}`;
|
|
56
|
+
}
|
|
57
|
+
function splitGatewayModelName(modelName) {
|
|
58
|
+
const slashIndex = modelName.indexOf("/");
|
|
59
|
+
if (slashIndex <= 0 || slashIndex >= modelName.length - 1) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
providerPrefix: modelName.slice(0, slashIndex),
|
|
64
|
+
unprefixedName: modelName.slice(slashIndex + 1)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function createReleaseDateResolver({
|
|
68
|
+
allModels,
|
|
69
|
+
providers
|
|
70
|
+
}) {
|
|
71
|
+
const releaseDateByProviderAndName = /* @__PURE__ */ new Map();
|
|
72
|
+
const releaseDateCandidatesByName = /* @__PURE__ */ new Map();
|
|
73
|
+
const latestReleaseDateByName = /* @__PURE__ */ new Map();
|
|
74
|
+
const providerByToken = /* @__PURE__ */ new Map();
|
|
75
|
+
for (const provider of providers) {
|
|
76
|
+
const token = normalizeProviderToken(provider);
|
|
77
|
+
if (token && !providerByToken.has(token)) {
|
|
78
|
+
providerByToken.set(token, provider);
|
|
79
|
+
}
|
|
80
|
+
const modelsForProvider = allModels[provider];
|
|
81
|
+
if (!modelsForProvider || modelsForProvider.length === 0) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
for (const model of modelsForProvider) {
|
|
85
|
+
const releaseDate = model.releaseDate;
|
|
86
|
+
if (!releaseDate) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const lowerName = model.name.toLowerCase();
|
|
90
|
+
releaseDateByProviderAndName.set(
|
|
91
|
+
toReleaseDateLookupKey(provider, lowerName),
|
|
92
|
+
releaseDate
|
|
93
|
+
);
|
|
94
|
+
const existingLatest = latestReleaseDateByName.get(lowerName);
|
|
95
|
+
if (!existingLatest || releaseDate > existingLatest) {
|
|
96
|
+
latestReleaseDateByName.set(lowerName, releaseDate);
|
|
97
|
+
}
|
|
98
|
+
const candidates = releaseDateCandidatesByName.get(lowerName) ?? [];
|
|
99
|
+
candidates.push({ provider, releaseDate });
|
|
100
|
+
releaseDateCandidatesByName.set(lowerName, candidates);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return (provider, modelName, explicitReleaseDate) => {
|
|
104
|
+
if (explicitReleaseDate) {
|
|
105
|
+
return explicitReleaseDate;
|
|
106
|
+
}
|
|
107
|
+
const lowerName = modelName.toLowerCase();
|
|
108
|
+
const sameProviderDate = releaseDateByProviderAndName.get(
|
|
109
|
+
toReleaseDateLookupKey(provider, lowerName)
|
|
110
|
+
);
|
|
111
|
+
if (sameProviderDate) {
|
|
112
|
+
return sameProviderDate;
|
|
113
|
+
}
|
|
114
|
+
const openRouterDate = releaseDateByProviderAndName.get(
|
|
115
|
+
toReleaseDateLookupKey("openrouter", lowerName)
|
|
116
|
+
);
|
|
117
|
+
if (openRouterDate) {
|
|
118
|
+
return openRouterDate;
|
|
119
|
+
}
|
|
120
|
+
const parsedGatewayModel = splitGatewayModelName(modelName);
|
|
121
|
+
if (!parsedGatewayModel) {
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
const unprefixedName = parsedGatewayModel.unprefixedName.toLowerCase();
|
|
125
|
+
const preferredProvider = providerByToken.get(
|
|
126
|
+
normalizeProviderToken(parsedGatewayModel.providerPrefix)
|
|
127
|
+
);
|
|
128
|
+
if (preferredProvider) {
|
|
129
|
+
const providerDate = releaseDateByProviderAndName.get(
|
|
130
|
+
toReleaseDateLookupKey(preferredProvider, unprefixedName)
|
|
131
|
+
);
|
|
132
|
+
if (providerDate) {
|
|
133
|
+
return providerDate;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const candidates = releaseDateCandidatesByName.get(unprefixedName);
|
|
137
|
+
if (candidates && preferredProvider) {
|
|
138
|
+
const preferredProviderCandidate = candidates.find(
|
|
139
|
+
(candidate) => candidate.provider === preferredProvider
|
|
140
|
+
);
|
|
141
|
+
if (preferredProviderCandidate) {
|
|
142
|
+
return preferredProviderCandidate.releaseDate;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return latestReleaseDateByName.get(unprefixedName);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function getNextModelSelectorTab(current) {
|
|
149
|
+
const currentIndex = MODEL_SELECTOR_TABS.findIndex((tab) => tab.id === current);
|
|
150
|
+
const nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % MODEL_SELECTOR_TABS.length;
|
|
151
|
+
return MODEL_SELECTOR_TABS[nextIndex]?.id ?? "all-models";
|
|
152
|
+
}
|
|
153
|
+
function getPreviousModelSelectorTab(current) {
|
|
154
|
+
const currentIndex = MODEL_SELECTOR_TABS.findIndex((tab) => tab.id === current);
|
|
155
|
+
const previousIndex = currentIndex < 0 ? 0 : (currentIndex - 1 + MODEL_SELECTOR_TABS.length) % MODEL_SELECTOR_TABS.length;
|
|
156
|
+
return MODEL_SELECTOR_TABS[previousIndex]?.id ?? "all-models";
|
|
157
|
+
}
|
|
158
|
+
function compareModelOptionsForDisplay(left, right) {
|
|
159
|
+
const byRecency = compareModelsLatestFirst(left, right);
|
|
160
|
+
if (byRecency !== 0) {
|
|
161
|
+
return byRecency;
|
|
162
|
+
}
|
|
163
|
+
const byProvider = PROVIDER_COLLATOR.compare(left.provider, right.provider);
|
|
164
|
+
if (byProvider !== 0) {
|
|
165
|
+
return byProvider;
|
|
166
|
+
}
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
function toModelIdentityKey(model) {
|
|
170
|
+
return toModelPickerKey({ provider: model.provider, model: model.name });
|
|
171
|
+
}
|
|
172
|
+
function normalizeLineText(value) {
|
|
173
|
+
return stripUnsafeCharacters(value).replace(/\r?\n/g, " ").replace(/\s+/g, " ").trim();
|
|
174
|
+
}
|
|
175
|
+
function formatLineToWidth(value, width) {
|
|
176
|
+
if (width <= 0) return "";
|
|
177
|
+
const normalized = normalizeLineText(value);
|
|
178
|
+
if (!normalized) {
|
|
179
|
+
return " ".repeat(width);
|
|
180
|
+
}
|
|
181
|
+
const normalizedWidth = getCachedStringWidth(normalized);
|
|
182
|
+
if (normalizedWidth <= width) {
|
|
183
|
+
return normalized + " ".repeat(width - normalizedWidth);
|
|
184
|
+
}
|
|
185
|
+
if (width === 1) {
|
|
186
|
+
return "\u2026";
|
|
187
|
+
}
|
|
188
|
+
const ellipsis = "\u2026";
|
|
189
|
+
const targetWidth = width - getCachedStringWidth(ellipsis);
|
|
190
|
+
let truncated = "";
|
|
191
|
+
for (const char of toCodePoints(normalized)) {
|
|
192
|
+
const candidate = `${truncated}${char}`;
|
|
193
|
+
if (getCachedStringWidth(candidate) > targetWidth) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
truncated = candidate;
|
|
197
|
+
}
|
|
198
|
+
const withEllipsis = `${truncated}${ellipsis}`;
|
|
199
|
+
const finalWidth = getCachedStringWidth(withEllipsis);
|
|
200
|
+
if (finalWidth >= width) {
|
|
201
|
+
return withEllipsis;
|
|
202
|
+
}
|
|
203
|
+
return withEllipsis + " ".repeat(width - finalWidth);
|
|
204
|
+
}
|
|
31
205
|
function isAddCustomOption(item) {
|
|
32
206
|
return "type" in item && item.type === "add-custom";
|
|
33
207
|
}
|
|
208
|
+
function isModelOption(item) {
|
|
209
|
+
return !("type" in item);
|
|
210
|
+
}
|
|
34
211
|
function getRowPrefix({
|
|
35
212
|
isSelected,
|
|
36
213
|
isDefault,
|
|
37
214
|
isCurrent,
|
|
38
|
-
isCustom
|
|
215
|
+
isCustom,
|
|
216
|
+
isFavorite
|
|
39
217
|
}) {
|
|
40
|
-
return `${isSelected ? "\u203A" : " "} ${isDefault ? "\u2713" : " "} ${isCurrent ? "\u25CF" : " "} ${
|
|
41
|
-
}
|
|
42
|
-
function computeNextSelection(currentIndex, itemsLength, viewportItems) {
|
|
43
|
-
const nextIndex = currentIndex;
|
|
44
|
-
let nextOffset = 0;
|
|
45
|
-
const modelsLength = Math.max(0, itemsLength - 1);
|
|
46
|
-
if (nextIndex > 0) {
|
|
47
|
-
const modelIndex = nextIndex - 1;
|
|
48
|
-
if (modelIndex < nextOffset) {
|
|
49
|
-
nextOffset = modelIndex;
|
|
50
|
-
} else if (modelIndex >= nextOffset + viewportItems) {
|
|
51
|
-
nextOffset = Math.max(0, modelIndex - viewportItems + 1);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const maxOffset = Math.max(0, modelsLength - viewportItems);
|
|
55
|
-
nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
|
|
56
|
-
return { index: nextIndex, offset: nextOffset };
|
|
218
|
+
return `${isSelected ? "\u203A" : " "} ${isDefault ? "\u2713" : " "} ${isCurrent ? "\u25CF" : " "} ${isFavorite ? "\u2605" : isCustom ? "\u25C7" : " "}`;
|
|
57
219
|
}
|
|
58
220
|
const REASONING_VARIANT_DESCRIPTIONS = {
|
|
59
221
|
disabled: "Disable reasoning (fastest)",
|
|
@@ -88,7 +250,8 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
88
250
|
onEditCustomModel,
|
|
89
251
|
agent
|
|
90
252
|
}, ref) {
|
|
91
|
-
const { rows: terminalRows } = useTerminalSize();
|
|
253
|
+
const { rows: terminalRows, columns: terminalColumns } = useTerminalSize();
|
|
254
|
+
const overlayWidth = useMemo(() => Math.max(20, terminalColumns - 2), [terminalColumns]);
|
|
92
255
|
const maxVisibleItems = useMemo(() => {
|
|
93
256
|
return getMaxVisibleItemsForTerminalRows({
|
|
94
257
|
rows: terminalRows,
|
|
@@ -98,6 +261,8 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
98
261
|
}, [terminalRows]);
|
|
99
262
|
const [models, setModels] = useState([]);
|
|
100
263
|
const [customModels, setCustomModels] = useState([]);
|
|
264
|
+
const [modelPickerState, setModelPickerState] = useState(null);
|
|
265
|
+
const [activeTab, setActiveTab] = useState("all-models");
|
|
101
266
|
const [isLoading, setIsLoading] = useState(false);
|
|
102
267
|
const [selection, setSelection] = useState({ index: 0, offset: 0 });
|
|
103
268
|
const [searchQuery, setSearchQuery] = useState("");
|
|
@@ -141,6 +306,8 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
141
306
|
setPendingReasoningModel(null);
|
|
142
307
|
setIsSettingDefault(false);
|
|
143
308
|
setReasoningVariantIndex(0);
|
|
309
|
+
setActiveTab("all-models");
|
|
310
|
+
setModelPickerState(null);
|
|
144
311
|
if (deleteTimeoutRef.current) {
|
|
145
312
|
clearTimeout(deleteTimeoutRef.current);
|
|
146
313
|
deleteTimeoutRef.current = null;
|
|
@@ -164,11 +331,13 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
164
331
|
loadCustomModels(),
|
|
165
332
|
loadGlobalPreferences().catch(() => null)
|
|
166
333
|
]);
|
|
334
|
+
const pickerState = await loadModelPickerState().catch(() => null);
|
|
167
335
|
const modelList = [];
|
|
168
336
|
const defaultProvider = preferences?.llm.provider;
|
|
169
337
|
const defaultModel = preferences?.llm.model;
|
|
170
338
|
const defaultBaseURL = preferences?.llm.baseURL;
|
|
171
339
|
const defaultReasoningVariant = preferences?.llm.reasoning?.variant;
|
|
340
|
+
const resolveReleaseDate = createReleaseDateResolver({ allModels, providers });
|
|
172
341
|
let ollamaModels = [];
|
|
173
342
|
let localModels = [];
|
|
174
343
|
try {
|
|
@@ -209,8 +378,16 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
209
378
|
if (provider === "dexto-nova" && !isDextoAuthEnabled()) {
|
|
210
379
|
continue;
|
|
211
380
|
}
|
|
212
|
-
const providerModels = allModels[provider];
|
|
381
|
+
const providerModels = [...allModels[provider]].sort(compareModelsLatestFirst);
|
|
213
382
|
for (const model of providerModels) {
|
|
383
|
+
if (isDeprecatedModelStatus(model.status)) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const releaseDate = resolveReleaseDate(
|
|
387
|
+
provider,
|
|
388
|
+
model.name,
|
|
389
|
+
model.releaseDate
|
|
390
|
+
);
|
|
214
391
|
const originalProvider = "originalProvider" in model ? model.originalProvider : void 0;
|
|
215
392
|
modelList.push({
|
|
216
393
|
provider,
|
|
@@ -220,6 +397,8 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
220
397
|
isDefault: provider === defaultProvider && model.name === defaultModel,
|
|
221
398
|
isCurrent: provider === currentConfig.provider && model.name === currentConfig.model,
|
|
222
399
|
isCustom: false,
|
|
400
|
+
...releaseDate !== void 0 ? { releaseDate } : {},
|
|
401
|
+
...model.status !== void 0 ? { status: model.status } : {},
|
|
223
402
|
...defaultReasoningVariant && provider === defaultProvider && model.name === defaultModel ? { reasoningVariant: defaultReasoningVariant } : {},
|
|
224
403
|
...defaultBaseURL && provider === defaultProvider && model.name === defaultModel ? { baseURL: defaultBaseURL } : {},
|
|
225
404
|
// Store original provider for display purposes
|
|
@@ -255,7 +434,15 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
255
434
|
}
|
|
256
435
|
const vertexModels = allModels["vertex"];
|
|
257
436
|
if (vertexModels) {
|
|
258
|
-
for (const model of vertexModels) {
|
|
437
|
+
for (const model of [...vertexModels].sort(compareModelsLatestFirst)) {
|
|
438
|
+
if (isDeprecatedModelStatus(model.status)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const releaseDate = resolveReleaseDate(
|
|
442
|
+
"vertex",
|
|
443
|
+
model.name,
|
|
444
|
+
model.releaseDate
|
|
445
|
+
);
|
|
259
446
|
modelList.push({
|
|
260
447
|
provider: "vertex",
|
|
261
448
|
name: model.name,
|
|
@@ -264,23 +451,73 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
264
451
|
isDefault: defaultProvider === "vertex" && defaultModel === model.name,
|
|
265
452
|
isCurrent: currentConfig.provider === "vertex" && currentConfig.model === model.name,
|
|
266
453
|
isCustom: false,
|
|
454
|
+
...releaseDate !== void 0 ? { releaseDate } : {},
|
|
455
|
+
...model.status !== void 0 ? { status: model.status } : {},
|
|
267
456
|
...defaultReasoningVariant && defaultProvider === "vertex" && defaultModel === model.name ? { reasoningVariant: defaultReasoningVariant } : {}
|
|
268
457
|
});
|
|
269
458
|
}
|
|
270
459
|
}
|
|
271
460
|
if (!cancelled) {
|
|
272
|
-
|
|
461
|
+
const dedupedByKey = /* @__PURE__ */ new Map();
|
|
462
|
+
const dedupeOrder = [];
|
|
463
|
+
for (const model of modelList) {
|
|
464
|
+
const key = toModelIdentityKey(model);
|
|
465
|
+
const existing = dedupedByKey.get(key);
|
|
466
|
+
if (!existing) {
|
|
467
|
+
dedupedByKey.set(key, model);
|
|
468
|
+
dedupeOrder.push(key);
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const preferred = model.isCustom && !existing.isCustom ? model : existing;
|
|
472
|
+
const secondary = preferred === existing ? model : existing;
|
|
473
|
+
const mergedBaseURL = preferred.baseURL ?? secondary.baseURL;
|
|
474
|
+
const mergedReasoningVariant = preferred.reasoningVariant ?? secondary.reasoningVariant;
|
|
475
|
+
const mergedOriginalProvider = preferred.originalProvider ?? secondary.originalProvider;
|
|
476
|
+
const mergedReleaseDate = preferred.releaseDate ?? secondary.releaseDate;
|
|
477
|
+
const mergedStatus = preferred.status ?? secondary.status;
|
|
478
|
+
const mergedModel = {
|
|
479
|
+
...preferred,
|
|
480
|
+
isDefault: preferred.isDefault || secondary.isDefault,
|
|
481
|
+
isCurrent: preferred.isCurrent || secondary.isCurrent,
|
|
482
|
+
displayName: preferred.displayName ?? secondary.displayName,
|
|
483
|
+
maxInputTokens: Math.max(
|
|
484
|
+
preferred.maxInputTokens,
|
|
485
|
+
secondary.maxInputTokens
|
|
486
|
+
)
|
|
487
|
+
};
|
|
488
|
+
if (mergedBaseURL !== void 0) {
|
|
489
|
+
mergedModel.baseURL = mergedBaseURL;
|
|
490
|
+
}
|
|
491
|
+
if (mergedReasoningVariant !== void 0) {
|
|
492
|
+
mergedModel.reasoningVariant = mergedReasoningVariant;
|
|
493
|
+
}
|
|
494
|
+
if (mergedOriginalProvider !== void 0) {
|
|
495
|
+
mergedModel.originalProvider = mergedOriginalProvider;
|
|
496
|
+
}
|
|
497
|
+
if (mergedReleaseDate !== void 0) {
|
|
498
|
+
mergedModel.releaseDate = mergedReleaseDate;
|
|
499
|
+
}
|
|
500
|
+
if (mergedStatus !== void 0) {
|
|
501
|
+
mergedModel.status = mergedStatus;
|
|
502
|
+
}
|
|
503
|
+
dedupedByKey.set(key, mergedModel);
|
|
504
|
+
}
|
|
505
|
+
const dedupedModelList = dedupeOrder.map((key) => dedupedByKey.get(key)).filter((model) => model !== void 0);
|
|
506
|
+
setModels(dedupedModelList);
|
|
273
507
|
setCustomModels(loadedCustomModels);
|
|
508
|
+
setModelPickerState(pickerState);
|
|
274
509
|
setIsLoading(false);
|
|
275
|
-
const currentIndex =
|
|
510
|
+
const currentIndex = dedupedModelList.findIndex((m) => m.isCurrent);
|
|
276
511
|
if (currentIndex >= 0) {
|
|
277
|
-
const nextIndex = currentIndex
|
|
512
|
+
const nextIndex = currentIndex;
|
|
278
513
|
const nextMaxVisibleItems = maxVisibleItemsRef.current;
|
|
279
|
-
const
|
|
280
|
-
|
|
514
|
+
const maxOffset = Math.max(
|
|
515
|
+
0,
|
|
516
|
+
dedupedModelList.length - nextMaxVisibleItems
|
|
517
|
+
);
|
|
281
518
|
const nextOffset = Math.min(
|
|
282
519
|
maxOffset,
|
|
283
|
-
Math.max(0, currentIndex -
|
|
520
|
+
Math.max(0, currentIndex - nextMaxVisibleItems + 1)
|
|
284
521
|
);
|
|
285
522
|
selectedIndexRef.current = nextIndex;
|
|
286
523
|
setSelection({ index: nextIndex, offset: nextOffset });
|
|
@@ -292,6 +529,7 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
292
529
|
`Failed to fetch models: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
293
530
|
);
|
|
294
531
|
setModels([]);
|
|
532
|
+
setModelPickerState(null);
|
|
295
533
|
setIsLoading(false);
|
|
296
534
|
}
|
|
297
535
|
}
|
|
@@ -301,35 +539,99 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
301
539
|
cancelled = true;
|
|
302
540
|
};
|
|
303
541
|
}, [isVisible, agent, refreshVersion]);
|
|
304
|
-
const
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
542
|
+
const favoriteKeySet = useMemo(
|
|
543
|
+
() => new Set(
|
|
544
|
+
(modelPickerState?.favorites ?? []).map(
|
|
545
|
+
(entry) => toModelPickerKey({
|
|
546
|
+
provider: entry.provider,
|
|
547
|
+
model: entry.model
|
|
548
|
+
})
|
|
549
|
+
)
|
|
550
|
+
),
|
|
551
|
+
[modelPickerState]
|
|
552
|
+
);
|
|
553
|
+
const matchesSearch = useCallback(
|
|
554
|
+
(model) => {
|
|
555
|
+
if (!searchQuery.trim()) {
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
const query = searchQuery.toLowerCase().replace(/[\s-]+/g, "");
|
|
311
559
|
const name = model.name.toLowerCase().replace(/[\s-]+/g, "");
|
|
312
560
|
const displayName = (model.displayName || "").toLowerCase().replace(/[\s-]+/g, "");
|
|
313
561
|
const provider = model.provider.toLowerCase().replace(/[\s-]+/g, "");
|
|
314
562
|
return name.includes(query) || displayName.includes(query) || provider.includes(query);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
563
|
+
},
|
|
564
|
+
[searchQuery]
|
|
565
|
+
);
|
|
566
|
+
const filteredItems = useMemo(() => {
|
|
567
|
+
const addCustomOption = { type: "add-custom" };
|
|
568
|
+
const hasSearchQuery = searchQuery.trim().length > 0;
|
|
569
|
+
const allCandidates = [...models].sort(compareModelOptionsForDisplay);
|
|
570
|
+
const modelsByKey = new Map(
|
|
571
|
+
allCandidates.map((model) => [
|
|
572
|
+
toModelPickerKey({ provider: model.provider, model: model.name }),
|
|
573
|
+
model
|
|
574
|
+
])
|
|
575
|
+
);
|
|
576
|
+
const toUniqueMatchingModels = (candidates, limit) => {
|
|
577
|
+
const deduped = [];
|
|
578
|
+
const seen = /* @__PURE__ */ new Set();
|
|
579
|
+
for (const candidate of candidates) {
|
|
580
|
+
if (!candidate || !matchesSearch(candidate)) {
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
const key = toModelPickerKey({
|
|
584
|
+
provider: candidate.provider,
|
|
585
|
+
model: candidate.name
|
|
586
|
+
});
|
|
587
|
+
if (seen.has(key)) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
seen.add(key);
|
|
591
|
+
deduped.push(candidate);
|
|
592
|
+
if (limit !== void 0 && deduped.length >= limit) {
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return deduped;
|
|
597
|
+
};
|
|
598
|
+
const providersInModels = Array.from(
|
|
599
|
+
new Set(models.map((model) => model.provider))
|
|
600
|
+
);
|
|
601
|
+
const featuredCandidates = getCuratedModelRefsForProviders({
|
|
602
|
+
providers: providersInModels,
|
|
603
|
+
max: FEATURED_SECTION_LIMIT
|
|
604
|
+
}).map((ref2) => modelsByKey.get(toModelPickerKey(ref2)));
|
|
605
|
+
const recentsFromState = (modelPickerState?.recents ?? []).map(
|
|
606
|
+
(entry) => modelsByKey.get(toModelPickerKey({ provider: entry.provider, model: entry.model }))
|
|
607
|
+
);
|
|
608
|
+
const favoritesFromState = (modelPickerState?.favorites ?? []).map(
|
|
609
|
+
(entry) => modelsByKey.get(toModelPickerKey({ provider: entry.provider, model: entry.model }))
|
|
610
|
+
);
|
|
611
|
+
const customCandidates = allCandidates.filter((model) => model.isCustom);
|
|
612
|
+
const tabModels = hasSearchQuery ? toUniqueMatchingModels(allCandidates) : activeTab === "all-models" ? toUniqueMatchingModels(allCandidates) : activeTab === "featured" ? toUniqueMatchingModels(featuredCandidates) : activeTab === "recents" ? toUniqueMatchingModels(recentsFromState) : activeTab === "favorites" ? toUniqueMatchingModels(favoritesFromState) : toUniqueMatchingModels(customCandidates);
|
|
613
|
+
return activeTab === "custom" && !hasSearchQuery ? [addCustomOption, ...tabModels] : tabModels;
|
|
614
|
+
}, [activeTab, matchesSearch, modelPickerState, models, searchQuery]);
|
|
615
|
+
const hasAddCustomOption = activeTab === "custom" && searchQuery.trim().length === 0;
|
|
616
|
+
const modelStartIndex = hasAddCustomOption ? 1 : 0;
|
|
617
|
+
const listViewportItems = hasAddCustomOption ? modelsViewportItems : maxVisibleItems;
|
|
318
618
|
useEffect(() => {
|
|
319
619
|
setSelection((prev) => {
|
|
320
620
|
const maxIndex = Math.max(0, filteredItems.length - 1);
|
|
321
621
|
const nextIndex = Math.min(prev.index, maxIndex);
|
|
322
622
|
let nextOffset = prev.offset;
|
|
323
|
-
const nextModelsLength = Math.max(0, filteredItems.length -
|
|
324
|
-
if (nextIndex
|
|
325
|
-
const modelIndex = nextIndex -
|
|
623
|
+
const nextModelsLength = Math.max(0, filteredItems.length - modelStartIndex);
|
|
624
|
+
if (nextIndex >= modelStartIndex) {
|
|
625
|
+
const modelIndex = nextIndex - modelStartIndex;
|
|
326
626
|
if (modelIndex < nextOffset) {
|
|
327
627
|
nextOffset = modelIndex;
|
|
328
|
-
} else if (modelIndex >= nextOffset +
|
|
329
|
-
nextOffset = Math.max(0, modelIndex -
|
|
628
|
+
} else if (modelIndex >= nextOffset + listViewportItems) {
|
|
629
|
+
nextOffset = Math.max(0, modelIndex - listViewportItems + 1);
|
|
330
630
|
}
|
|
631
|
+
} else {
|
|
632
|
+
nextOffset = 0;
|
|
331
633
|
}
|
|
332
|
-
const maxOffset = Math.max(0, nextModelsLength -
|
|
634
|
+
const maxOffset = Math.max(0, nextModelsLength - listViewportItems);
|
|
333
635
|
nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
|
|
334
636
|
if (nextIndex === prev.index && nextOffset === prev.offset) {
|
|
335
637
|
return prev;
|
|
@@ -337,7 +639,7 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
337
639
|
selectedIndexRef.current = nextIndex;
|
|
338
640
|
return { index: nextIndex, offset: nextOffset };
|
|
339
641
|
});
|
|
340
|
-
}, [filteredItems.length,
|
|
642
|
+
}, [filteredItems.length, listViewportItems, modelStartIndex]);
|
|
341
643
|
const handleDeleteCustomModel = useCallback(
|
|
342
644
|
async (model) => {
|
|
343
645
|
if (!model.isCustom) return;
|
|
@@ -354,6 +656,23 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
354
656
|
},
|
|
355
657
|
[agent]
|
|
356
658
|
);
|
|
659
|
+
const handleToggleFavoriteModel = useCallback(
|
|
660
|
+
async (model) => {
|
|
661
|
+
try {
|
|
662
|
+
await toggleFavoriteModel({
|
|
663
|
+
provider: model.provider,
|
|
664
|
+
model: model.name
|
|
665
|
+
});
|
|
666
|
+
const nextState = await loadModelPickerState();
|
|
667
|
+
setModelPickerState(nextState);
|
|
668
|
+
} catch (error) {
|
|
669
|
+
agent.logger.error(
|
|
670
|
+
`Failed to toggle favorite model: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
[agent]
|
|
675
|
+
);
|
|
357
676
|
const clearActionState = () => {
|
|
358
677
|
setCustomModelAction(null);
|
|
359
678
|
setPendingDeleteConfirm(false);
|
|
@@ -452,27 +771,44 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
452
771
|
}
|
|
453
772
|
const itemsLength = filteredItems.length;
|
|
454
773
|
const currentItem = filteredItems[selectedIndexRef.current];
|
|
455
|
-
const
|
|
456
|
-
const
|
|
457
|
-
|
|
774
|
+
const selectedModel = currentItem && isModelOption(currentItem) ? currentItem : null;
|
|
775
|
+
const isCustomActionItem = selectedModel?.isCustom ?? false;
|
|
776
|
+
const isSelectableItem = selectedModel !== null;
|
|
777
|
+
if (key.tab) {
|
|
778
|
+
clearActionState();
|
|
779
|
+
setActiveTab((prev) => getNextModelSelectorTab(prev));
|
|
780
|
+
selectedIndexRef.current = 0;
|
|
781
|
+
setSelection({ index: 0, offset: 0 });
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
if (key.ctrl && input === "f" && isSelectableItem) {
|
|
785
|
+
const item = selectedModel;
|
|
786
|
+
if (!item) return true;
|
|
787
|
+
clearActionState();
|
|
788
|
+
void handleToggleFavoriteModel(item);
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
if (key.ctrl && key.rightArrow) {
|
|
458
792
|
if (!isSelectableItem) return false;
|
|
459
793
|
if (customModelAction === null) {
|
|
460
|
-
|
|
461
|
-
setCustomModelAction("edit");
|
|
462
|
-
} else {
|
|
463
|
-
setCustomModelAction("default");
|
|
464
|
-
}
|
|
794
|
+
setCustomModelAction("favorite");
|
|
465
795
|
return true;
|
|
466
796
|
}
|
|
467
|
-
if (customModelAction === "
|
|
797
|
+
if (customModelAction === "favorite") {
|
|
468
798
|
setCustomModelAction("default");
|
|
469
799
|
return true;
|
|
470
800
|
}
|
|
471
801
|
if (customModelAction === "default") {
|
|
802
|
+
if (isCustomActionItem) {
|
|
803
|
+
setCustomModelAction("edit");
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
return true;
|
|
807
|
+
}
|
|
808
|
+
if (customModelAction === "edit") {
|
|
472
809
|
if (isCustomActionItem) {
|
|
473
810
|
setCustomModelAction("delete");
|
|
474
811
|
setPendingDeleteConfirm(false);
|
|
475
|
-
return true;
|
|
476
812
|
}
|
|
477
813
|
return true;
|
|
478
814
|
}
|
|
@@ -480,9 +816,9 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
480
816
|
return true;
|
|
481
817
|
}
|
|
482
818
|
}
|
|
483
|
-
if (key.leftArrow) {
|
|
819
|
+
if (key.ctrl && key.leftArrow) {
|
|
484
820
|
if (customModelAction === "delete") {
|
|
485
|
-
setCustomModelAction("
|
|
821
|
+
setCustomModelAction("edit");
|
|
486
822
|
setPendingDeleteConfirm(false);
|
|
487
823
|
if (deleteTimeoutRef.current) {
|
|
488
824
|
clearTimeout(deleteTimeoutRef.current);
|
|
@@ -491,19 +827,33 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
491
827
|
return true;
|
|
492
828
|
}
|
|
493
829
|
if (customModelAction === "default") {
|
|
494
|
-
|
|
495
|
-
setCustomModelAction("edit");
|
|
496
|
-
} else {
|
|
497
|
-
setCustomModelAction(null);
|
|
498
|
-
}
|
|
830
|
+
setCustomModelAction("favorite");
|
|
499
831
|
return true;
|
|
500
832
|
}
|
|
501
833
|
if (customModelAction === "edit") {
|
|
834
|
+
setCustomModelAction("default");
|
|
835
|
+
return true;
|
|
836
|
+
}
|
|
837
|
+
if (customModelAction === "favorite") {
|
|
502
838
|
setCustomModelAction(null);
|
|
503
839
|
return true;
|
|
504
840
|
}
|
|
505
841
|
return false;
|
|
506
842
|
}
|
|
843
|
+
if (key.rightArrow) {
|
|
844
|
+
clearActionState();
|
|
845
|
+
setActiveTab((prev) => getNextModelSelectorTab(prev));
|
|
846
|
+
selectedIndexRef.current = 0;
|
|
847
|
+
setSelection({ index: 0, offset: 0 });
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
if (key.leftArrow) {
|
|
851
|
+
clearActionState();
|
|
852
|
+
setActiveTab((prev) => getPreviousModelSelectorTab(prev));
|
|
853
|
+
selectedIndexRef.current = 0;
|
|
854
|
+
setSelection({ index: 0, offset: 0 });
|
|
855
|
+
return true;
|
|
856
|
+
}
|
|
507
857
|
if (input && !key.return && !key.upArrow && !key.downArrow && !key.tab) {
|
|
508
858
|
if (customModelAction) {
|
|
509
859
|
clearActionState();
|
|
@@ -532,16 +882,18 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
532
882
|
selectedIndexRef.current = nextIndex;
|
|
533
883
|
setSelection((prev) => {
|
|
534
884
|
let nextOffset = prev.offset;
|
|
535
|
-
const nextModelsLength = Math.max(0, itemsLength -
|
|
536
|
-
if (nextIndex
|
|
537
|
-
const modelIndex = nextIndex -
|
|
885
|
+
const nextModelsLength = Math.max(0, itemsLength - modelStartIndex);
|
|
886
|
+
if (nextIndex >= modelStartIndex) {
|
|
887
|
+
const modelIndex = nextIndex - modelStartIndex;
|
|
538
888
|
if (modelIndex < prev.offset) {
|
|
539
889
|
nextOffset = modelIndex;
|
|
540
|
-
} else if (modelIndex >= prev.offset +
|
|
541
|
-
nextOffset = Math.max(0, modelIndex -
|
|
890
|
+
} else if (modelIndex >= prev.offset + listViewportItems) {
|
|
891
|
+
nextOffset = Math.max(0, modelIndex - listViewportItems + 1);
|
|
542
892
|
}
|
|
893
|
+
} else {
|
|
894
|
+
nextOffset = 0;
|
|
543
895
|
}
|
|
544
|
-
const maxOffset = Math.max(0, nextModelsLength -
|
|
896
|
+
const maxOffset = Math.max(0, nextModelsLength - listViewportItems);
|
|
545
897
|
nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
|
|
546
898
|
return { index: nextIndex, offset: nextOffset };
|
|
547
899
|
});
|
|
@@ -555,16 +907,18 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
555
907
|
selectedIndexRef.current = nextIndex;
|
|
556
908
|
setSelection((prev) => {
|
|
557
909
|
let nextOffset = prev.offset;
|
|
558
|
-
const nextModelsLength = Math.max(0, itemsLength -
|
|
559
|
-
if (nextIndex
|
|
560
|
-
const modelIndex = nextIndex -
|
|
910
|
+
const nextModelsLength = Math.max(0, itemsLength - modelStartIndex);
|
|
911
|
+
if (nextIndex >= modelStartIndex) {
|
|
912
|
+
const modelIndex = nextIndex - modelStartIndex;
|
|
561
913
|
if (modelIndex < prev.offset) {
|
|
562
914
|
nextOffset = modelIndex;
|
|
563
|
-
} else if (modelIndex >= prev.offset +
|
|
564
|
-
nextOffset = Math.max(0, modelIndex -
|
|
915
|
+
} else if (modelIndex >= prev.offset + listViewportItems) {
|
|
916
|
+
nextOffset = Math.max(0, modelIndex - listViewportItems + 1);
|
|
565
917
|
}
|
|
918
|
+
} else {
|
|
919
|
+
nextOffset = 0;
|
|
566
920
|
}
|
|
567
|
-
const maxOffset = Math.max(0, nextModelsLength -
|
|
921
|
+
const maxOffset = Math.max(0, nextModelsLength - listViewportItems);
|
|
568
922
|
nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
|
|
569
923
|
return { index: nextIndex, offset: nextOffset };
|
|
570
924
|
});
|
|
@@ -577,6 +931,10 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
577
931
|
onAddCustomModel();
|
|
578
932
|
return true;
|
|
579
933
|
}
|
|
934
|
+
if (customModelAction === "favorite") {
|
|
935
|
+
void handleToggleFavoriteModel(item);
|
|
936
|
+
return true;
|
|
937
|
+
}
|
|
580
938
|
if (customModelAction === "edit" && item.isCustom) {
|
|
581
939
|
const customModel = customModels.find(
|
|
582
940
|
(cm) => cm.name === item.name && (cm.provider ?? "openai-compatible") === item.provider
|
|
@@ -635,7 +993,8 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
635
993
|
isLoading,
|
|
636
994
|
filteredItems,
|
|
637
995
|
maxVisibleItems,
|
|
638
|
-
|
|
996
|
+
listViewportItems,
|
|
997
|
+
modelStartIndex,
|
|
639
998
|
onClose,
|
|
640
999
|
onSelectModel,
|
|
641
1000
|
onSetDefaultModel,
|
|
@@ -645,14 +1004,18 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
645
1004
|
pendingDeleteConfirm,
|
|
646
1005
|
customModels,
|
|
647
1006
|
handleDeleteCustomModel,
|
|
1007
|
+
handleToggleFavoriteModel,
|
|
648
1008
|
pendingReasoningModel,
|
|
649
1009
|
reasoningVariantIndex,
|
|
650
1010
|
reasoningVariantOptions,
|
|
651
1011
|
isSettingDefault,
|
|
1012
|
+
activeTab,
|
|
1013
|
+
agent,
|
|
652
1014
|
beginReasoningVariantSelection
|
|
653
1015
|
]
|
|
654
1016
|
);
|
|
655
1017
|
if (!isVisible) return null;
|
|
1018
|
+
const blankLine = " ".repeat(overlayWidth);
|
|
656
1019
|
if (pendingReasoningModel) {
|
|
657
1020
|
const totalOptions = reasoningVariantOptions.length;
|
|
658
1021
|
const reasoningVisibleItems = Math.min(maxVisibleItems, totalOptions);
|
|
@@ -665,200 +1028,167 @@ const ModelSelector = forwardRef(function ModelSelector2({
|
|
|
665
1028
|
reasoningOffset + reasoningVisibleItems
|
|
666
1029
|
);
|
|
667
1030
|
const selectedReasoningOption = reasoningVariantOptions[reasoningVariantIndex] ?? reasoningVariantOptions[0];
|
|
668
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
669
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
|
|
1031
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: overlayWidth, children: [
|
|
1032
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
|
|
670
1033
|
"Reasoning Variant",
|
|
671
1034
|
isSettingDefault ? /* @__PURE__ */ jsx(Text, { color: "gray", children: " (default)" }) : null
|
|
672
1035
|
] }) }),
|
|
673
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, children: /* @__PURE__ */ jsx(Text, { color: "gray",
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1036
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: formatLineToWidth(
|
|
1037
|
+
pendingReasoningModel.displayName || pendingReasoningModel.name,
|
|
1038
|
+
overlayWidth
|
|
1039
|
+
) }) }),
|
|
1040
|
+
/* @__PURE__ */ jsx(
|
|
1041
|
+
Box,
|
|
1042
|
+
{
|
|
1043
|
+
flexDirection: "column",
|
|
1044
|
+
height: maxVisibleItems,
|
|
1045
|
+
marginTop: 1,
|
|
1046
|
+
width: overlayWidth,
|
|
1047
|
+
children: Array.from({ length: maxVisibleItems }, (_, rowIndex) => {
|
|
1048
|
+
const option = visibleReasoningOptions[rowIndex];
|
|
1049
|
+
if (!option) {
|
|
1050
|
+
return /* @__PURE__ */ jsx(
|
|
1051
|
+
Box,
|
|
1052
|
+
{
|
|
1053
|
+
paddingX: 0,
|
|
1054
|
+
paddingY: 0,
|
|
1055
|
+
width: overlayWidth,
|
|
1056
|
+
children: /* @__PURE__ */ jsx(Text, { children: blankLine })
|
|
1057
|
+
},
|
|
1058
|
+
`reasoning-empty-${rowIndex}`
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
const actualIndex = reasoningOffset + rowIndex;
|
|
1062
|
+
const isSelected = actualIndex === reasoningVariantIndex;
|
|
1063
|
+
return /* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : "gray", bold: isSelected, children: formatLineToWidth(
|
|
1064
|
+
`${isSelected ? "\u203A" : " "} ${option.label}`,
|
|
1065
|
+
overlayWidth
|
|
1066
|
+
) }) }, option.value);
|
|
1067
|
+
})
|
|
678
1068
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
bold: isSelected,
|
|
686
|
-
wrap: "truncate-end",
|
|
687
|
-
children: [
|
|
688
|
-
isSelected ? "\u203A" : " ",
|
|
689
|
-
" ",
|
|
690
|
-
option.label
|
|
691
|
-
]
|
|
692
|
-
}
|
|
693
|
-
) }, option.value);
|
|
694
|
-
}) }),
|
|
695
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: /* @__PURE__ */ jsx(Text, { color: "gray", wrap: "truncate-end", children: selectedReasoningOption?.description ?? "" }) }),
|
|
696
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, children: /* @__PURE__ */ jsx(HintBar, { hints: ["\u2191\u2193 navigate", "Enter select", "Esc back"] }) })
|
|
1069
|
+
),
|
|
1070
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, width: overlayWidth, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: formatLineToWidth(
|
|
1071
|
+
selectedReasoningOption?.description ?? "",
|
|
1072
|
+
overlayWidth
|
|
1073
|
+
) }) }),
|
|
1074
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(HintBar, { hints: ["\u2191\u2193 navigate", "Enter select", "Esc back"] }) })
|
|
697
1075
|
] });
|
|
698
1076
|
}
|
|
699
1077
|
const selectedIndex = selection.index;
|
|
700
1078
|
const scrollOffset = selection.offset;
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
);
|
|
704
|
-
const visibleModels = modelsOnly.slice(scrollOffset, scrollOffset + modelsViewportItems);
|
|
1079
|
+
const listItems = filteredItems.filter((item) => !isAddCustomOption(item));
|
|
1080
|
+
const visibleItems = listItems.slice(scrollOffset, scrollOffset + listViewportItems);
|
|
705
1081
|
const selectedItem = filteredItems[selectedIndex];
|
|
706
|
-
const hasActionableItems = selectedItem &&
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
1082
|
+
const hasActionableItems = Boolean(selectedItem && isModelOption(selectedItem));
|
|
1083
|
+
const searchLine = formatLineToWidth(
|
|
1084
|
+
`Search: ${searchQuery || "Type to filter models\u2026"}`,
|
|
1085
|
+
overlayWidth
|
|
1086
|
+
);
|
|
1087
|
+
const addCustomLine = hasAddCustomOption ? formatLineToWidth(
|
|
1088
|
+
`${getRowPrefix({
|
|
1089
|
+
isSelected: selectedIndex === 0,
|
|
1090
|
+
isDefault: false,
|
|
1091
|
+
isCurrent: false,
|
|
1092
|
+
isCustom: false,
|
|
1093
|
+
isFavorite: false
|
|
1094
|
+
})} Add custom model\u2026`,
|
|
1095
|
+
overlayWidth
|
|
1096
|
+
) : "";
|
|
1097
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: overlayWidth, children: [
|
|
1098
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Models" }) }),
|
|
1099
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, flexDirection: "row", children: MODEL_SELECTOR_TABS.map((tab) => /* @__PURE__ */ jsx(
|
|
1100
|
+
Box,
|
|
1101
|
+
{
|
|
1102
|
+
marginRight: 1,
|
|
1103
|
+
borderStyle: "round",
|
|
1104
|
+
borderColor: activeTab === tab.id ? "cyan" : "gray",
|
|
1105
|
+
paddingX: 1,
|
|
1106
|
+
children: /* @__PURE__ */ jsx(
|
|
1107
|
+
Text,
|
|
1108
|
+
{
|
|
1109
|
+
color: activeTab === tab.id ? "cyan" : "gray",
|
|
1110
|
+
bold: activeTab === tab.id,
|
|
1111
|
+
children: tab.label
|
|
1112
|
+
}
|
|
1113
|
+
)
|
|
1114
|
+
},
|
|
1115
|
+
tab.id
|
|
1116
|
+
)) }),
|
|
1117
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(Text, { color: searchQuery ? "white" : "gray", children: searchLine }) }),
|
|
1118
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, width: overlayWidth, children: [
|
|
1119
|
+
hasAddCustomOption && /* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, width: overlayWidth, children: /* @__PURE__ */ jsx(
|
|
737
1120
|
Text,
|
|
738
1121
|
{
|
|
739
1122
|
color: selectedIndex === 0 ? "green" : "gray",
|
|
740
1123
|
bold: selectedIndex === 0,
|
|
741
|
-
|
|
742
|
-
children: [
|
|
743
|
-
getRowPrefix({
|
|
744
|
-
isSelected: selectedIndex === 0,
|
|
745
|
-
isDefault: false,
|
|
746
|
-
isCurrent: false,
|
|
747
|
-
isCustom: false
|
|
748
|
-
}),
|
|
749
|
-
" ",
|
|
750
|
-
"Add custom model\u2026"
|
|
751
|
-
]
|
|
1124
|
+
children: addCustomLine
|
|
752
1125
|
}
|
|
753
1126
|
) }),
|
|
754
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", height:
|
|
755
|
-
|
|
1127
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", height: listViewportItems, width: overlayWidth, children: isLoading || listItems.length === 0 ? Array.from({ length: listViewportItems }, (_, index) => /* @__PURE__ */ jsx(
|
|
1128
|
+
Box,
|
|
1129
|
+
{
|
|
1130
|
+
paddingX: 0,
|
|
1131
|
+
paddingY: 0,
|
|
1132
|
+
width: overlayWidth,
|
|
1133
|
+
children: /* @__PURE__ */ jsx(Text, { children: blankLine })
|
|
1134
|
+
},
|
|
1135
|
+
`model-empty-${index}`
|
|
1136
|
+
)) : Array.from({ length: listViewportItems }, (_, rowIndex) => {
|
|
1137
|
+
const item = visibleItems[rowIndex];
|
|
756
1138
|
if (!item) {
|
|
757
1139
|
return /* @__PURE__ */ jsx(
|
|
758
1140
|
Box,
|
|
759
1141
|
{
|
|
760
1142
|
paddingX: 0,
|
|
761
1143
|
paddingY: 0,
|
|
762
|
-
|
|
1144
|
+
width: overlayWidth,
|
|
1145
|
+
children: /* @__PURE__ */ jsx(Text, { children: blankLine })
|
|
763
1146
|
},
|
|
764
1147
|
`model-empty-${rowIndex}`
|
|
765
1148
|
);
|
|
766
1149
|
}
|
|
767
|
-
const actualIndex =
|
|
1150
|
+
const actualIndex = modelStartIndex + scrollOffset + rowIndex;
|
|
768
1151
|
const isSelected = actualIndex === selectedIndex;
|
|
769
1152
|
const providerDisplay = getLLMProviderDisplayName(item.provider);
|
|
770
1153
|
const name = item.displayName || item.name;
|
|
1154
|
+
const isFavorite = favoriteKeySet.has(
|
|
1155
|
+
toModelPickerKey({
|
|
1156
|
+
provider: item.provider,
|
|
1157
|
+
model: item.name
|
|
1158
|
+
})
|
|
1159
|
+
);
|
|
771
1160
|
const prefix = getRowPrefix({
|
|
772
1161
|
isSelected,
|
|
773
1162
|
isDefault: item.isDefault,
|
|
774
1163
|
isCurrent: item.isCurrent,
|
|
775
|
-
isCustom: item.isCustom
|
|
1164
|
+
isCustom: item.isCustom,
|
|
1165
|
+
isFavorite
|
|
776
1166
|
});
|
|
777
|
-
return /* @__PURE__ */
|
|
1167
|
+
return /* @__PURE__ */ jsx(
|
|
778
1168
|
Box,
|
|
779
1169
|
{
|
|
780
1170
|
flexDirection: "row",
|
|
781
1171
|
paddingX: 0,
|
|
782
1172
|
paddingY: 0,
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
bold: isSelected,
|
|
789
|
-
wrap: "truncate-end",
|
|
790
|
-
children: [
|
|
791
|
-
prefix,
|
|
792
|
-
" ",
|
|
793
|
-
name,
|
|
794
|
-
" (",
|
|
795
|
-
providerDisplay,
|
|
796
|
-
")"
|
|
797
|
-
]
|
|
798
|
-
}
|
|
799
|
-
) }),
|
|
800
|
-
isSelected && /* @__PURE__ */ jsxs(Box, { flexDirection: "row", marginLeft: 1, children: [
|
|
801
|
-
item.isCustom && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
802
|
-
/* @__PURE__ */ jsxs(
|
|
803
|
-
Text,
|
|
804
|
-
{
|
|
805
|
-
color: customModelAction === "edit" ? "green" : "gray",
|
|
806
|
-
bold: customModelAction === "edit",
|
|
807
|
-
inverse: customModelAction === "edit",
|
|
808
|
-
children: [
|
|
809
|
-
" ",
|
|
810
|
-
"Edit",
|
|
811
|
-
" "
|
|
812
|
-
]
|
|
813
|
-
}
|
|
814
|
-
),
|
|
815
|
-
/* @__PURE__ */ jsx(Text, { children: " " })
|
|
816
|
-
] }),
|
|
817
|
-
/* @__PURE__ */ jsxs(
|
|
818
|
-
Text,
|
|
819
|
-
{
|
|
820
|
-
color: customModelAction === "default" ? "cyan" : "gray",
|
|
821
|
-
bold: customModelAction === "default",
|
|
822
|
-
inverse: customModelAction === "default",
|
|
823
|
-
children: [
|
|
824
|
-
" ",
|
|
825
|
-
"Set as Default",
|
|
826
|
-
" "
|
|
827
|
-
]
|
|
828
|
-
}
|
|
829
|
-
),
|
|
830
|
-
item.isCustom && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
831
|
-
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
832
|
-
/* @__PURE__ */ jsxs(
|
|
833
|
-
Text,
|
|
834
|
-
{
|
|
835
|
-
color: customModelAction === "delete" ? "red" : "gray",
|
|
836
|
-
bold: customModelAction === "delete",
|
|
837
|
-
inverse: customModelAction === "delete",
|
|
838
|
-
children: [
|
|
839
|
-
" ",
|
|
840
|
-
"Delete",
|
|
841
|
-
" "
|
|
842
|
-
]
|
|
843
|
-
}
|
|
844
|
-
)
|
|
845
|
-
] })
|
|
846
|
-
] })
|
|
847
|
-
]
|
|
1173
|
+
width: overlayWidth,
|
|
1174
|
+
children: /* @__PURE__ */ jsx(Text, { color: isSelected ? "cyan" : "gray", bold: isSelected, children: formatLineToWidth(
|
|
1175
|
+
`${prefix} ${name} (${providerDisplay})`,
|
|
1176
|
+
overlayWidth
|
|
1177
|
+
) })
|
|
848
1178
|
},
|
|
849
|
-
|
|
1179
|
+
`model-${activeTab}-${actualIndex}-${toModelIdentityKey(item)}`
|
|
850
1180
|
);
|
|
851
1181
|
}) })
|
|
852
1182
|
] }),
|
|
853
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: /* @__PURE__ */ jsx(
|
|
854
|
-
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, children: /* @__PURE__ */ jsx(
|
|
1183
|
+
/* @__PURE__ */ jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, width: overlayWidth, children: /* @__PURE__ */ jsx(
|
|
855
1184
|
HintBar,
|
|
856
1185
|
{
|
|
857
1186
|
hints: [
|
|
858
1187
|
"\u2191\u2193 navigate",
|
|
859
|
-
"Enter select",
|
|
1188
|
+
"Enter select/apply",
|
|
860
1189
|
"Esc close",
|
|
861
|
-
|
|
1190
|
+
"\u2190\u2192 switch tab",
|
|
1191
|
+
hasActionableItems ? "Ctrl+F quick favorite" : ""
|
|
862
1192
|
]
|
|
863
1193
|
}
|
|
864
1194
|
) })
|