@adecore/agents-react 0.37.0 → 0.37.2

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.
@@ -3,8 +3,10 @@ import { siGithubcopilot } from 'simple-icons';
3
3
  * The marks of Anthropic, OpenAI, xAI and GitHub Copilot, and a monogram for a provider with no mark of its own. Lucide has no brand marks, so these are the icon rule's
4
4
  * exception for provider marks. One path each, in `currentColor`, so a row decides how loud the logo is.
5
5
  */
6
+ const CLAUDE_PATH = 'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z';
6
7
  export const PROVIDER_PATHS = {
7
- claude: 'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z',
8
+ claude: CLAUDE_PATH,
9
+ 'anthropic-claude-api': CLAUDE_PATH,
8
10
  codex: 'M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.554 5.554 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.019.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.001zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z',
9
11
  nanogpt: 'M5 20V4h2.6l8.8 11.2V4H19v16h-2.6L7.6 8.8V20z',
10
12
  grok: 'M6.469 8.776 16.512 23h-4.464L2.005 8.776H6.47Zm-.004 7.9 2.233 3.164L6.467 23H2l4.465-6.324ZM22 2.582V23h-3.659V7.764L22 2.582ZM22 1l-9.952 14.095-2.233-3.163L17.533 1H22Z',
@@ -1,4 +1,9 @@
1
- import type { AgentKind, ModelInfo, ProviderInfo } from '@adecore/agent-contracts';
1
+ import { type AgentKind, type ModelInfo, type ProviderInfo } from '@adecore/agent-contracts';
2
+ export declare function inPickerOrder<T extends {
3
+ kind: AgentKind;
4
+ name: string;
5
+ }>(providers: readonly T[]): T[];
6
+ export declare function separatesGroups(previous: AgentKind | undefined, kind: AgentKind): boolean;
2
7
  export declare const COMPACT_MODEL_LIMIT = 8;
3
8
  export type PickerEntry = {
4
9
  kind: 'model';
@@ -1,4 +1,14 @@
1
+ import { pickerGroupsOf, providerGroupOf } from '@adecore/agent-contracts';
1
2
  import { nameCollator } from '@adecore/ui/format';
3
+ /* The providers in the order a picker lists them: the frontier labs, the rest, then the API providers, each by name. */
4
+ export function inPickerOrder(providers) {
5
+ const groups = pickerGroupsOf(providers);
6
+ return [...groups.frontier, ...groups.others, ...groups.api];
7
+ }
8
+ /* Whether a separator goes between two providers next to each other in that order: where one group ends. */
9
+ export function separatesGroups(previous, kind) {
10
+ return previous !== undefined && providerGroupOf(previous) !== providerGroupOf(kind);
11
+ }
2
12
  /*
3
13
  * A provider with more live models than this keeps the list short: the model that is picked and the default,
4
14
  * with the rest behind one row that opens all of them. A list of three hundred models is not one to scroll.
@@ -10,7 +10,7 @@ import { useChatHost } from '../../host.js';
10
10
  import { ChatScopeContext } from '../../scope.js';
11
11
  import { useReportedModels } from '../../state/chats.js';
12
12
  import { ModelMarks } from './ModelMarks.js';
13
- import { pickerEntriesOf } from '../logic/picker-entries.js';
13
+ import { inPickerOrder, pickerEntriesOf, separatesGroups } from '../logic/picker-entries.js';
14
14
  import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences.js';
15
15
  import { forgetStashed, STASH_SHORTCUT, useStash } from '../stash.js';
16
16
  import { Button, Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
@@ -23,7 +23,7 @@ const triggerClass = 'flex h-7 shrink-0 items-center gap-1 whitespace-nowrap rou
23
23
  * provider's model is also how a chat picks its provider; with one CLI the group headers drop.
24
24
  */
25
25
  export function ModelPicker(t0) {
26
- const $ = _c(76);
26
+ const $ = _c(49);
27
27
  const {
28
28
  providers,
29
29
  provider,
@@ -111,7 +111,7 @@ export function ModelPicker(t0) {
111
111
  } else {
112
112
  t8 = $[15];
113
113
  }
114
- const entries = pickerEntriesOf(providers, trimmed, legacyOpen, expanded, {
114
+ const entries = pickerEntriesOf(inPickerOrder(providers), trimmed, legacyOpen, expanded, {
115
115
  provider,
116
116
  model: selection.model
117
117
  }, t8);
@@ -127,6 +127,10 @@ export function ModelPicker(t0) {
127
127
  t9 = $[17];
128
128
  }
129
129
  const requirementOf = t9;
130
+ const kindBefore = i => {
131
+ const before = entries[i - 1];
132
+ return before === undefined || before.kind === "legacy" ? undefined : before.provider.kind;
133
+ };
130
134
  let t10;
131
135
  if ($[18] !== requirementOf) {
132
136
  t10 = entry_1 => entry_1.kind === "model" && requirementOf(entry_1) !== null ? [entry_1.provider.kind] : [];
@@ -173,83 +177,63 @@ export function ModelPicker(t0) {
173
177
  t13 = $[24];
174
178
  }
175
179
  useEffect(t12, t13);
176
- let t14;
177
- if ($[25] !== onChange || $[26] !== requirementOf || $[27] !== setOpen) {
178
- t14 = entry_2 => {
179
- if (!entry_2) {
180
- return;
181
- }
182
- if (entry_2.kind === "legacy") {
183
- setLegacyOpen(true);
184
- inputRef.current?.focus();
185
- return;
186
- }
187
- if (entry_2.kind === "all") {
188
- const kind_0 = entry_2.provider.kind;
189
- setExpanded(now => {
190
- const next_0 = new Set(now);
191
- if (!next_0.delete(kind_0)) {
192
- next_0.add(kind_0);
193
- }
194
- return next_0;
195
- });
196
- inputRef.current?.focus();
197
- return;
198
- }
199
- if (requirementOf(entry_2) !== null) {
200
- return;
201
- }
202
- onChange(entry_2.provider.kind, entry_2.model.slug);
203
- setOpen(false);
204
- };
205
- $[25] = onChange;
206
- $[26] = requirementOf;
207
- $[27] = setOpen;
208
- $[28] = t14;
209
- } else {
210
- t14 = $[28];
211
- }
212
- const choose = t14;
213
- let t15;
214
- if ($[29] !== choose || $[30] !== entries || $[31] !== index) {
215
- t15 = e => {
216
- if (e.key === "ArrowDown") {
180
+ const choose = entry_2 => {
181
+ if (!entry_2) {
182
+ return;
183
+ }
184
+ if (entry_2.kind === "legacy") {
185
+ setLegacyOpen(true);
186
+ inputRef.current?.focus();
187
+ return;
188
+ }
189
+ if (entry_2.kind === "all") {
190
+ const kind_0 = entry_2.provider.kind;
191
+ setExpanded(now => {
192
+ const next_0 = new Set(now);
193
+ if (!next_0.delete(kind_0)) {
194
+ next_0.add(kind_0);
195
+ }
196
+ return next_0;
197
+ });
198
+ inputRef.current?.focus();
199
+ return;
200
+ }
201
+ if (requirementOf(entry_2) !== null) {
202
+ return;
203
+ }
204
+ onChange(entry_2.provider.kind, entry_2.model.slug);
205
+ setOpen(false);
206
+ };
207
+ const onKeyDown = e => {
208
+ if (e.key === "ArrowDown") {
209
+ e.preventDefault();
210
+ setIndex(i_0 => entries.length === 0 ? 0 : (i_0 + 1) % entries.length);
211
+ } else {
212
+ if (e.key === "ArrowUp") {
217
213
  e.preventDefault();
218
- setIndex(i => entries.length === 0 ? 0 : (i + 1) % entries.length);
214
+ setIndex(i_1 => entries.length === 0 ? 0 : (i_1 - 1 + entries.length) % entries.length);
219
215
  } else {
220
- if (e.key === "ArrowUp") {
216
+ if (e.key === "Enter") {
221
217
  e.preventDefault();
222
- setIndex(i_0 => entries.length === 0 ? 0 : (i_0 - 1 + entries.length) % entries.length);
223
- } else {
224
- if (e.key === "Enter") {
225
- e.preventDefault();
226
- choose(entries[index]);
227
- }
218
+ choose(entries[index]);
228
219
  }
229
220
  }
230
- };
231
- $[29] = choose;
232
- $[30] = entries;
233
- $[31] = index;
234
- $[32] = t15;
221
+ }
222
+ };
223
+ let t14;
224
+ if ($[25] !== current || $[26] !== owner || $[27] !== t) {
225
+ t14 = owner ? `${owner.name} · ${current}` : t("pickers.model.choose");
226
+ $[25] = current;
227
+ $[26] = owner;
228
+ $[27] = t;
229
+ $[28] = t14;
235
230
  } else {
236
- t15 = $[32];
231
+ t14 = $[28];
237
232
  }
238
- const onKeyDown = t15;
233
+ const t15 = kbd ?? undefined;
239
234
  let t16;
240
- if ($[33] !== current || $[34] !== owner || $[35] !== t) {
241
- t16 = owner ? `${owner.name} · ${current}` : t("pickers.model.choose");
242
- $[33] = current;
243
- $[34] = owner;
244
- $[35] = t;
245
- $[36] = t16;
246
- } else {
247
- t16 = $[36];
248
- }
249
- const t17 = kbd ?? undefined;
250
- let t18;
251
- if ($[37] !== chip || $[38] !== current || $[39] !== provider || $[40] !== trigger) {
252
- t18 = trigger === "chip" ? /*#__PURE__*/_jsxs(Popover.Trigger, {
235
+ if ($[29] !== chip || $[30] !== current || $[31] !== provider || $[32] !== trigger) {
236
+ t16 = trigger === "chip" ? /*#__PURE__*/_jsxs(Popover.Trigger, {
253
237
  render: /*#__PURE__*/_jsx(Button, {
254
238
  variant: "secondary",
255
239
  size: "sm",
@@ -286,117 +270,89 @@ export function ModelPicker(t0) {
286
270
  className: "text-text-faint"
287
271
  })]
288
272
  });
289
- $[37] = chip;
290
- $[38] = current;
291
- $[39] = provider;
292
- $[40] = trigger;
293
- $[41] = t18;
294
- } else {
295
- t18 = $[41];
296
- }
297
- let t19;
298
- if ($[42] !== t16 || $[43] !== t17 || $[44] !== t18) {
299
- t19 = /*#__PURE__*/_jsx(Tooltip, {
300
- label: t16,
301
- kbd: t17,
302
- children: t18
273
+ $[29] = chip;
274
+ $[30] = current;
275
+ $[31] = provider;
276
+ $[32] = trigger;
277
+ $[33] = t16;
278
+ } else {
279
+ t16 = $[33];
280
+ }
281
+ let t17;
282
+ if ($[34] !== t14 || $[35] !== t15 || $[36] !== t16) {
283
+ t17 = /*#__PURE__*/_jsx(Tooltip, {
284
+ label: t14,
285
+ kbd: t15,
286
+ children: t16
303
287
  });
304
- $[42] = t16;
305
- $[43] = t17;
306
- $[44] = t18;
307
- $[45] = t19;
288
+ $[34] = t14;
289
+ $[35] = t15;
290
+ $[36] = t16;
291
+ $[37] = t17;
308
292
  } else {
309
- t19 = $[45];
293
+ t17 = $[37];
310
294
  }
311
- let t20;
312
- if ($[46] !== finalFocus) {
313
- t20 = finalFocus === undefined ? {} : {
295
+ let t18;
296
+ if ($[38] !== finalFocus) {
297
+ t18 = finalFocus === undefined ? {} : {
314
298
  finalFocus
315
299
  };
316
- $[46] = finalFocus;
317
- $[47] = t20;
300
+ $[38] = finalFocus;
301
+ $[39] = t18;
318
302
  } else {
319
- t20 = $[47];
303
+ t18 = $[39];
320
304
  }
321
- let t21;
322
- if ($[48] === Symbol.for("react.memo_cache_sentinel")) {
323
- t21 = /*#__PURE__*/_jsx(Icon, {
305
+ let t19;
306
+ if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
307
+ t19 = /*#__PURE__*/_jsx(Icon, {
324
308
  icon: Search,
325
309
  size: 14,
326
310
  className: "shrink-0 text-text-faint"
327
311
  });
328
- $[48] = t21;
312
+ $[40] = t19;
329
313
  } else {
330
- t21 = $[48];
314
+ t19 = $[40];
331
315
  }
332
- let t22;
333
- if ($[49] !== t) {
334
- t22 = t("pickers.model.search");
335
- $[49] = t;
336
- $[50] = t22;
316
+ let t20;
317
+ if ($[41] !== t) {
318
+ t20 = t("pickers.model.search");
319
+ $[41] = t;
320
+ $[42] = t20;
337
321
  } else {
338
- t22 = $[50];
322
+ t20 = $[42];
339
323
  }
340
- let t23;
341
- if ($[51] === Symbol.for("react.memo_cache_sentinel")) {
342
- t23 = e_0 => {
324
+ let t21;
325
+ if ($[43] === Symbol.for("react.memo_cache_sentinel")) {
326
+ t21 = e_0 => {
343
327
  setQuery(e_0.target.value);
344
328
  setIndex(0);
345
329
  };
346
- $[51] = t23;
330
+ $[43] = t21;
347
331
  } else {
348
- t23 = $[51];
332
+ t21 = $[43];
349
333
  }
350
- let t24;
351
- if ($[52] !== onKeyDown || $[53] !== query || $[54] !== t22) {
352
- t24 = /*#__PURE__*/_jsxs("div", {
353
- className: "flex items-center gap-2 border-b border-border px-2.5",
354
- children: [t21, /*#__PURE__*/_jsx("input", {
355
- ref: inputRef,
356
- className: "h-8 w-full bg-transparent text-sm text-text outline-none placeholder:text-text-faint",
357
- placeholder: t22,
358
- spellCheck: false,
359
- value: query,
360
- onChange: t23,
361
- onKeyDown: onKeyDown
362
- })]
363
- });
364
- $[52] = onKeyDown;
365
- $[53] = query;
366
- $[54] = t22;
367
- $[55] = t24;
368
- } else {
369
- t24 = $[55];
370
- }
371
- let t25;
372
- if ($[56] !== entries.length || $[57] !== providers.length || $[58] !== t) {
373
- t25 = entries.length === 0 && /*#__PURE__*/_jsx("div", {
374
- className: "px-3 py-6 text-center text-xs text-text-faint",
375
- children: providers.length === 0 ? t("pickers.model.noProvider") : t("pickers.model.noMatch")
376
- });
377
- $[56] = entries.length;
378
- $[57] = providers.length;
379
- $[58] = t;
380
- $[59] = t25;
381
- } else {
382
- t25 = $[59];
383
- }
384
- const t26 = entries.map((entry_3, i_1) => {
334
+ const t22 = "max-h-72 overflow-auto p-1";
335
+ const t23 = "listbox";
336
+ const t24 = entries.length === 0 && /*#__PURE__*/_jsx("div", {
337
+ className: "px-3 py-6 text-center text-xs text-text-faint",
338
+ children: providers.length === 0 ? t("pickers.model.noProvider") : t("pickers.model.noMatch")
339
+ });
340
+ const t25 = entries.map((entry_3, i_2) => {
385
341
  if (entry_3.kind === "all") {
386
- const before = entries[i_1 - 1];
387
- const header = grouped && (before === undefined || before.kind === "legacy" || before.provider.kind !== entry_3.provider.kind);
342
+ const before_0 = entries[i_2 - 1];
343
+ const header = grouped && (before_0 === undefined || before_0.kind === "legacy" || before_0.provider.kind !== entry_3.provider.kind);
388
344
  return /*#__PURE__*/_jsxs("div", {
389
- children: [header && /*#__PURE__*/_jsxs(Menu.Label, {
345
+ children: [separatesGroups(kindBefore(i_2), entry_3.provider.kind) && /*#__PURE__*/_jsx(Menu.Separator, {}), header && /*#__PURE__*/_jsxs(Menu.Label, {
390
346
  className: "flex items-center gap-1.5",
391
347
  children: [/*#__PURE__*/_jsx(AgentIcon, {
392
348
  kind: entry_3.provider.kind,
393
349
  size: 12
394
350
  }), entry_3.provider.name]
395
351
  }), /*#__PURE__*/_jsxs("button", {
396
- "data-active": i_1 === index,
352
+ "data-active": i_2 === index,
397
353
  "aria-expanded": entry_3.open,
398
354
  className: "cursor-row flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-xs text-text-muted",
399
- onMouseEnter: () => setIndex(i_1),
355
+ onMouseEnter: () => setIndex(i_2),
400
356
  onClick: () => choose(entry_3),
401
357
  children: [/*#__PURE__*/_jsx(Icon, {
402
358
  icon: entry_3.open ? ChevronDown : ChevronRight,
@@ -409,9 +365,9 @@ export function ModelPicker(t0) {
409
365
  }
410
366
  if (entry_3.kind === "legacy") {
411
367
  return /*#__PURE__*/_jsxs("button", {
412
- "data-active": i_1 === index,
368
+ "data-active": i_2 === index,
413
369
  className: "cursor-row flex w-full items-center gap-2 rounded-md px-2.5 py-1.5 text-left text-xs text-text-faint",
414
- onMouseEnter: () => setIndex(i_1),
370
+ onMouseEnter: () => setIndex(i_2),
415
371
  onClick: () => choose(entry_3),
416
372
  children: [/*#__PURE__*/_jsx(Icon, {
417
373
  icon: legacyOpen ? ChevronDown : ChevronRight,
@@ -421,17 +377,17 @@ export function ModelPicker(t0) {
421
377
  })]
422
378
  }, "legacy");
423
379
  }
424
- const previous = entries[i_1 - 1];
380
+ const previous = entries[i_2 - 1];
425
381
  const first = grouped && (previous === undefined || previous.kind === "legacy" || previous.provider.kind !== entry_3.provider.kind);
426
382
  const vendorOpen = trimmed !== "" || expanded.has(entry_3.provider.kind);
427
383
  const vendorFirst = vendorOpen && entry_3.model.vendor !== undefined && !starredHere(entry_3) && (previous === undefined || previous.kind !== "model" || previous.provider.kind !== entry_3.provider.kind || starredHere(previous) || previous.model.vendor !== entry_3.model.vendor);
428
384
  const requirement = requirementOf(entry_3);
429
- const next_1 = entries[i_1 + 1];
385
+ const next_1 = entries[i_2 + 1];
430
386
  const lastOfGroup = next_1 === undefined || next_1.kind !== "model" || next_1.provider.kind !== entry_3.provider.kind;
431
387
  const chosen = entry_3.provider.kind === provider && entry_3.model.slug === selection.model;
432
388
  const starred = starredHere(entry_3);
433
389
  return /*#__PURE__*/_jsxs("div", {
434
- children: [first && /*#__PURE__*/_jsxs(Menu.Label, {
390
+ children: [separatesGroups(kindBefore(i_2), entry_3.provider.kind) && /*#__PURE__*/_jsx(Menu.Separator, {}), first && /*#__PURE__*/_jsxs(Menu.Label, {
435
391
  className: "flex items-center gap-1.5",
436
392
  children: [/*#__PURE__*/_jsx(AgentIcon, {
437
393
  kind: entry_3.provider.kind,
@@ -445,9 +401,9 @@ export function ModelPicker(t0) {
445
401
  role: "option",
446
402
  "aria-selected": chosen,
447
403
  "aria-disabled": requirement !== null || undefined,
448
- "data-active": i_1 === index,
404
+ "data-active": i_2 === index,
449
405
  className: `cursor-row flex min-w-0 grow flex-col items-stretch gap-0.5 rounded-md px-2.5 py-1.5 text-left text-sm ${requirement === null ? "text-text-muted" : "text-text-faint"}`,
450
- onMouseEnter: () => setIndex(i_1),
406
+ onMouseEnter: () => setIndex(i_2),
451
407
  onClick: () => choose(entry_3),
452
408
  children: [/*#__PURE__*/_jsxs("span", {
453
409
  className: "flex w-full items-center gap-2",
@@ -496,65 +452,53 @@ export function ModelPicker(t0) {
496
452
  })]
497
453
  }, `${entry_3.provider.kind}/${entry_3.model.slug}`);
498
454
  });
499
- let t27;
500
- if ($[60] !== t25 || $[61] !== t26) {
501
- t27 = /*#__PURE__*/_jsxs("div", {
455
+ let t26;
456
+ if ($[44] !== t24 || $[45] !== t25) {
457
+ t26 = /*#__PURE__*/_jsxs("div", {
502
458
  ref: listRef,
503
- className: "max-h-72 overflow-auto p-1",
504
- role: "listbox",
505
- children: [t25, t26]
459
+ className: t22,
460
+ role: t23,
461
+ children: [t24, t25]
506
462
  });
507
- $[60] = t25;
508
- $[61] = t26;
509
- $[62] = t27;
463
+ $[44] = t24;
464
+ $[45] = t25;
465
+ $[46] = t26;
510
466
  } else {
511
- t27 = $[62];
467
+ t26 = $[46];
512
468
  }
513
- let t28;
514
- if ($[63] !== footer) {
515
- t28 = footer !== undefined && /*#__PURE__*/_jsx("div", {
469
+ let t27;
470
+ if ($[47] !== footer) {
471
+ t27 = footer !== undefined && /*#__PURE__*/_jsx("div", {
516
472
  className: "border-t border-border-soft px-3 py-2 text-xs text-text-faint",
517
473
  children: footer
518
474
  });
519
- $[63] = footer;
520
- $[64] = t28;
475
+ $[47] = footer;
476
+ $[48] = t27;
521
477
  } else {
522
- t28 = $[64];
478
+ t27 = $[48];
523
479
  }
524
- let t29;
525
- if ($[65] !== side || $[66] !== t20 || $[67] !== t24 || $[68] !== t27 || $[69] !== t28) {
526
- t29 = /*#__PURE__*/_jsxs(Popover.Popup, {
480
+ return /*#__PURE__*/_jsxs(Popover.Root, {
481
+ open: open,
482
+ onOpenChange: setOpen,
483
+ children: [t17, /*#__PURE__*/_jsxs(Popover.Popup, {
527
484
  variant: "picker",
528
485
  side: side,
529
486
  initialFocus: inputRef,
530
- ...t20,
531
- children: [t24, t27, t28]
532
- });
533
- $[65] = side;
534
- $[66] = t20;
535
- $[67] = t24;
536
- $[68] = t27;
537
- $[69] = t28;
538
- $[70] = t29;
539
- } else {
540
- t29 = $[70];
541
- }
542
- let t30;
543
- if ($[71] !== open || $[72] !== setOpen || $[73] !== t19 || $[74] !== t29) {
544
- t30 = /*#__PURE__*/_jsxs(Popover.Root, {
545
- open: open,
546
- onOpenChange: setOpen,
547
- children: [t19, t29]
548
- });
549
- $[71] = open;
550
- $[72] = setOpen;
551
- $[73] = t19;
552
- $[74] = t29;
553
- $[75] = t30;
554
- } else {
555
- t30 = $[75];
556
- }
557
- return t30;
487
+ ...t18,
488
+ children: [/*#__PURE__*/_jsxs("div", {
489
+ className: "flex items-center gap-2 border-b border-border px-2.5",
490
+ children: [t19, /*#__PURE__*/_jsx("input", {
491
+ ref: inputRef,
492
+ className: "h-8 w-full bg-transparent text-sm text-text outline-none placeholder:text-text-faint",
493
+ placeholder: t20,
494
+ spellCheck: false,
495
+ value: query,
496
+ onChange: t21,
497
+ onKeyDown: onKeyDown
498
+ })]
499
+ }), t26, t27]
500
+ })]
501
+ });
558
502
  }
559
503
  /*
560
504
  * Prompts put aside with Cmd+S, on a shelf the whole app shares, since a stashed prompt usually moves
@@ -10,7 +10,7 @@ import { AgentIcon } from '../../agents/AgentIcon.js';
10
10
  import { modelName, shortModelName } from '../../agents/model-name.js';
11
11
  import { ModelMarks } from './ModelMarks.js';
12
12
  import { SessionLine } from './SessionLine.js';
13
- import { favoritesFirst, searchModels, shortlistOf, wantsModelSearch } from '../logic/picker-entries.js';
13
+ import { favoritesFirst, inPickerOrder, searchModels, separatesGroups, shortlistOf, wantsModelSearch } from '../logic/picker-entries.js';
14
14
  import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences.js';
15
15
  import { CONTEXT_OPTION, contextFraction, contextSegments, orderOptions } from '../logic/context-usage.js';
16
16
  import { RUNTIME_MODES } from '../runtime-modes.js';
@@ -56,7 +56,7 @@ function modelValue(provider, slug) {
56
56
  }
57
57
  /* Each provider's current or legacy models; a provider with none of them is left out. */
58
58
  function modelGroupsOf(providers, legacy) {
59
- return providers.map(entry => ({
59
+ return inPickerOrder(providers).map(entry => ({
60
60
  entry,
61
61
  models: entry.models.filter(row => row.legacy === true === legacy)
62
62
  })).filter(group => group.models.length > 0);
@@ -196,7 +196,7 @@ export function RunSettings(t0) {
196
196
  const favoritesOf = t8;
197
197
  let t9;
198
198
  if ($[19] !== favoritesOf || $[20] !== providers || $[21] !== query) {
199
- t9 = query.trim() === "" ? null : searchModels(providers, query, favoritesOf);
199
+ t9 = query.trim() === "" ? null : searchModels(inPickerOrder(providers), query, favoritesOf);
200
200
  $[19] = favoritesOf;
201
201
  $[20] = providers;
202
202
  $[21] = query;
@@ -391,19 +391,21 @@ export function RunSettings(t0) {
391
391
  const providerModels = t17;
392
392
  let t18;
393
393
  if ($[51] !== grouped || $[52] !== providerModels) {
394
- t18 = groups => groups.map(t19 => {
394
+ t18 = groups => groups.map((t19, i_0) => {
395
395
  const {
396
396
  entry: entry_3,
397
397
  models: models_1
398
398
  } = t19;
399
- return /*#__PURE__*/_jsxs(Menu.Group, {
400
- children: [grouped && /*#__PURE__*/_jsxs(Menu.GroupLabel, {
401
- className: "flex items-center gap-1.5",
402
- children: [/*#__PURE__*/_jsx(AgentIcon, {
403
- kind: entry_3.kind,
404
- size: 12
405
- }), entry_3.name]
406
- }), providerModels(entry_3, models_1)]
399
+ return /*#__PURE__*/_jsxs(Fragment, {
400
+ children: [separatesGroups(groups[i_0 - 1]?.entry.kind, entry_3.kind) && /*#__PURE__*/_jsx(Menu.Separator, {}), /*#__PURE__*/_jsxs(Menu.Group, {
401
+ children: [grouped && /*#__PURE__*/_jsxs(Menu.GroupLabel, {
402
+ className: "flex items-center gap-1.5",
403
+ children: [/*#__PURE__*/_jsx(AgentIcon, {
404
+ kind: entry_3.kind,
405
+ size: 12
406
+ }), entry_3.name]
407
+ }), providerModels(entry_3, models_1)]
408
+ })]
407
409
  }, entry_3.kind);
408
410
  });
409
411
  $[51] = grouped;
@@ -657,19 +659,21 @@ export function RunSettings(t0) {
657
659
  t47 = found !== null && found.length > 0 && /*#__PURE__*/_jsx(Menu.RadioGroup, {
658
660
  value: chosenModel,
659
661
  onValueChange: chooseModel,
660
- children: found.map(t48 => {
662
+ children: found.map((t48, i_1) => {
661
663
  const {
662
664
  entry: entry_5,
663
665
  models: models_2
664
666
  } = t48;
665
- return /*#__PURE__*/_jsxs(Menu.Group, {
666
- children: [grouped && /*#__PURE__*/_jsxs(Menu.GroupLabel, {
667
- className: "flex items-center gap-1.5",
668
- children: [/*#__PURE__*/_jsx(AgentIcon, {
669
- kind: entry_5.kind,
670
- size: 12
671
- }), entry_5.name]
672
- }), byMaker(entry_5, models_2)]
667
+ return /*#__PURE__*/_jsxs(Fragment, {
668
+ children: [separatesGroups(found[i_1 - 1]?.entry.kind, entry_5.kind) && /*#__PURE__*/_jsx(Menu.Separator, {}), /*#__PURE__*/_jsxs(Menu.Group, {
669
+ children: [grouped && /*#__PURE__*/_jsxs(Menu.GroupLabel, {
670
+ className: "flex items-center gap-1.5",
671
+ children: [/*#__PURE__*/_jsx(AgentIcon, {
672
+ kind: entry_5.kind,
673
+ size: 12
674
+ }), entry_5.name]
675
+ }), byMaker(entry_5, models_2)]
676
+ })]
673
677
  }, entry_5.kind);
674
678
  })
675
679
  });
@@ -10,7 +10,8 @@ const CLI_COLORS = {
10
10
  gemini: 'text-chart-gemini',
11
11
  copilot: 'text-chart-copilot',
12
12
  nanogpt: 'text-chart-nanogpt',
13
- grok: 'text-chart-grok'
13
+ grok: 'text-chart-grok',
14
+ 'anthropic-claude-api': 'text-chart-claude-api'
14
15
  };
15
16
  export function CliMark(t0) {
16
17
  const $ = _c(7);
package/dist/theme.css CHANGED
@@ -8,8 +8,10 @@
8
8
  --skill: #9333ea;
9
9
  --skill-soft: #f7eeff;
10
10
  /* One color per agent CLI, so a bar, a dot and a legend row all name the same provider. Apple's
11
- system orange and teal; Gemini and Copilot are reserved for the day their transcripts are read. */
11
+ system orange and teal, and purple for Claude through an API key so it reads apart from Claude Code;
12
+ Gemini and Copilot are reserved for the day their transcripts are read. */
12
13
  --chart-claude: #ff9500;
14
+ --chart-claude-api: #af52de;
13
15
  --chart-codex: #30b0c7;
14
16
  --chart-gemini: #3b82f6;
15
17
  --chart-copilot: #64748b;
@@ -37,6 +39,7 @@
37
39
  --skill: #c084fc;
38
40
  --skill-soft: #2f2350;
39
41
  --chart-claude: #ff9f0a;
42
+ --chart-claude-api: #bf5af2;
40
43
  --chart-codex: #40c8e0;
41
44
  --chart-gemini: #60a5fa;
42
45
  --chart-copilot: #94a3b8;
@@ -70,6 +73,7 @@
70
73
  --color-skill: var(--skill);
71
74
  --color-skill-soft: var(--skill-soft);
72
75
  --color-chart-claude: var(--chart-claude);
76
+ --color-chart-claude-api: var(--chart-claude-api);
73
77
  --color-chart-codex: var(--chart-codex);
74
78
  --color-chart-gemini: var(--chart-gemini);
75
79
  --color-chart-copilot: var(--chart-copilot);
@@ -25,14 +25,16 @@ export const PROVIDER_LABELS = {
25
25
  codex: 'Codex',
26
26
  nanogpt: 'NanoGPT',
27
27
  grok: 'Grok',
28
- copilot: 'GitHub Copilot'
28
+ copilot: 'GitHub Copilot',
29
+ 'anthropic-claude-api': 'Claude API'
29
30
  };
30
31
  export const PROVIDER_COLORS = {
31
32
  claude: 'var(--chart-claude)',
32
33
  codex: 'var(--chart-codex)',
33
34
  nanogpt: 'var(--chart-nanogpt)',
34
35
  grok: 'var(--chart-grok)',
35
- copilot: 'var(--chart-copilot)'
36
+ copilot: 'var(--chart-copilot)',
37
+ 'anthropic-claude-api': 'var(--chart-claude-api)'
36
38
  };
37
39
  /*
38
40
  * A slot is `2026-09-10` or `2026-09-10T14`, already in the viewer's own zone, so it is read as plain
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/agents-react",
3
- "version": "0.37.0",
3
+ "version": "0.37.2",
4
4
  "description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -67,11 +67,11 @@
67
67
  "test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
68
68
  },
69
69
  "dependencies": {
70
- "@adecore/agent-contracts": "0.37.0",
71
- "@adecore/editor": "0.37.0",
72
- "@adecore/editor-react": "0.37.0",
73
- "@adecore/intelligent-ui": "0.37.0",
74
- "@adecore/merge": "0.37.0",
70
+ "@adecore/agent-contracts": "0.37.2",
71
+ "@adecore/editor": "0.37.2",
72
+ "@adecore/editor-react": "0.37.2",
73
+ "@adecore/intelligent-ui": "0.37.2",
74
+ "@adecore/merge": "0.37.2",
75
75
  "@codemirror/commands": "^6.11.1",
76
76
  "@codemirror/language": "^6.12.4",
77
77
  "@codemirror/state": "^6.7.6",
@@ -94,14 +94,14 @@
94
94
  "zustand": "^5.0.15"
95
95
  },
96
96
  "peerDependencies": {
97
- "@adecore/ui": "^0.37.0",
97
+ "@adecore/ui": "^0.37.2",
98
98
  "i18next": "^26.4.2",
99
99
  "react": "^19.3.0",
100
100
  "react-dom": "^19.3.0",
101
101
  "react-i18next": "^17.0.15"
102
102
  },
103
103
  "devDependencies": {
104
- "@adecore/ui": "0.37.0",
104
+ "@adecore/ui": "0.37.2",
105
105
  "@types/bun": "^1.4.2",
106
106
  "@types/react": "^19.3.0",
107
107
  "@types/react-dom": "^19.3.0",
@@ -5,8 +5,12 @@ import { siGithubcopilot } from 'simple-icons';
5
5
  * The marks of Anthropic, OpenAI, xAI and GitHub Copilot, and a monogram for a provider with no mark of its own. Lucide has no brand marks, so these are the icon rule's
6
6
  * exception for provider marks. One path each, in `currentColor`, so a row decides how loud the logo is.
7
7
  */
8
+ const CLAUDE_PATH =
9
+ 'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z';
10
+
8
11
  export const PROVIDER_PATHS: Record<UsageProvider, string> = {
9
- claude: 'M4.709 15.955l4.72-2.647.079-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.377-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.048.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.607.212-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.914-1.316-.012.01z',
12
+ claude: CLAUDE_PATH,
13
+ 'anthropic-claude-api': CLAUDE_PATH,
10
14
  codex: 'M21.55 10.004a5.416 5.416 0 00-.478-4.501c-1.217-2.09-3.662-3.166-6.05-2.66A5.59 5.59 0 0010.831 1C8.39.995 6.224 2.546 5.473 4.838A5.553 5.553 0 001.76 7.496a5.487 5.487 0 00.691 6.5 5.416 5.416 0 00.477 4.502c1.217 2.09 3.662 3.165 6.05 2.66A5.586 5.586 0 0013.168 23c2.443.006 4.61-1.546 5.361-3.84a5.554 5.554 0 003.715-2.66 5.488 5.488 0 00-.693-6.497v.001zm-8.381 11.558a4.199 4.199 0 01-2.675-.954c.034-.018.093-.05.132-.074l4.44-2.53a.71.71 0 00.364-.623v-6.176l1.877 1.069c.02.01.033.029.036.05v5.115c-.003 2.274-1.87 4.118-4.174 4.123zM4.192 17.78a4.059 4.059 0 01-.498-2.763c.032.019.09.055.131.078l4.44 2.53c.225.13.504.13.73 0l5.42-3.088v2.138a.068.068 0 01-.027.057L9.9 19.288c-1.999 1.136-4.552.46-5.707-1.51h-.001zM3.023 8.216A4.15 4.15 0 015.198 6.41l-.002.151v5.06a.711.711 0 00.364.624l5.42 3.087-1.876 1.07a.067.067 0 01-.063.005l-4.489-2.559c-1.995-1.14-2.679-3.658-1.53-5.63h.001zm15.417 3.54l-5.42-3.088L14.896 7.6a.067.067 0 01.063-.006l4.489 2.557c1.998 1.14 2.683 3.662 1.529 5.633a4.163 4.163 0 01-2.174 1.807V12.38a.71.71 0 00-.363-.623zm1.867-2.773a6.04 6.04 0 00-.132-.078l-4.44-2.53a.731.731 0 00-.729 0l-5.42 3.088V7.325a.068.068 0 01.027-.057L14.1 4.713c2-1.137 4.555-.46 5.707 1.513.487.833.664 1.809.499 2.757h.001zm-11.741 3.81l-1.877-1.068a.065.065 0 01-.036-.051V6.559c.001-2.277 1.873-4.122 4.181-4.12.976 0 1.92.338 2.671.954-.034.018-.092.05-.131.073l-4.44 2.53a.71.71 0 00-.365.623l-.003 6.173v.001zm1.02-2.168L12 9.25l2.414 1.375v2.75L12 14.75l-2.415-1.375v-2.75z',
11
15
  nanogpt: 'M5 20V4h2.6l8.8 11.2V4H19v16h-2.6L7.6 8.8V20z',
12
16
  grok: 'M6.469 8.776 16.512 23h-4.464L2.005 8.776H6.47Zm-.004 7.9 2.233 3.164L6.467 23H2l4.465-6.324ZM22 2.582V23h-3.659V7.764L22 2.582ZM22 1l-9.952 14.095-2.233-3.163L17.533 1H22Z',
@@ -1,6 +1,17 @@
1
- import type { AgentKind, ModelInfo, ProviderInfo } from '@adecore/agent-contracts';
1
+ import { pickerGroupsOf, providerGroupOf, type AgentKind, type ModelInfo, type ProviderInfo } from '@adecore/agent-contracts';
2
2
  import { nameCollator } from '@adecore/ui/format';
3
3
 
4
+ /* The providers in the order a picker lists them: the frontier labs, the rest, then the API providers, each by name. */
5
+ export function inPickerOrder<T extends { kind: AgentKind; name: string }>(providers: readonly T[]): T[] {
6
+ const groups = pickerGroupsOf(providers);
7
+ return [...groups.frontier, ...groups.others, ...groups.api];
8
+ }
9
+
10
+ /* Whether a separator goes between two providers next to each other in that order: where one group ends. */
11
+ export function separatesGroups(previous: AgentKind | undefined, kind: AgentKind): boolean {
12
+ return previous !== undefined && providerGroupOf(previous) !== providerGroupOf(kind);
13
+ }
14
+
4
15
  /*
5
16
  * A provider with more live models than this keeps the list short: the model that is picked and the default,
6
17
  * with the rest behind one row that opens all of them. A list of three hundred models is not one to scroll.
@@ -10,7 +10,7 @@ import { useChatHost } from '../../host';
10
10
  import { ChatScopeContext } from '../../scope';
11
11
  import { useReportedModels } from '../../state/chats';
12
12
  import { ModelMarks } from './ModelMarks';
13
- import { pickerEntriesOf, type PickerEntry } from '../logic/picker-entries';
13
+ import { inPickerOrder, pickerEntriesOf, separatesGroups, type PickerEntry } from '../logic/picker-entries';
14
14
  import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences';
15
15
  import { forgetStashed, STASH_SHORTCUT, useStash, type StashedPrompt } from '../stash';
16
16
  import { Button, Icon, IconButton, Menu, Popover, Tooltip } from '@adecore/ui';
@@ -75,7 +75,7 @@ export function ModelPicker({
75
75
 
76
76
  const favorites = useChatPreferences((preferences) => preferences.favoriteModels);
77
77
  const starredHere = (row: PickerEntry | undefined): boolean => row?.kind === 'model' && favorites[row.provider.kind]?.includes(row.model.slug) === true;
78
- const entries = pickerEntriesOf(providers, trimmed, legacyOpen, expanded, { provider, model: selection.model }, (kind) =>
78
+ const entries = pickerEntriesOf(inPickerOrder(providers), trimmed, legacyOpen, expanded, { provider, model: selection.model }, (kind) =>
79
79
  favoriteModelsOf(favorites, kind)
80
80
  );
81
81
  const scope = useContext(ChatScopeContext);
@@ -83,6 +83,10 @@ export function ModelPicker({
83
83
  const reported = useReportedModels(provider);
84
84
  const requirementOf = (entry: PickerEntry & { kind: 'model' }) => modelRequirementOf(entry.model, entry.provider, reported);
85
85
  // A CLI that is too old blocks all its models; one way to its settings under the group says it once.
86
+ const kindBefore = (i: number): AgentKind | undefined => {
87
+ const before = entries[i - 1];
88
+ return before === undefined || before.kind === 'legacy' ? undefined : before.provider.kind;
89
+ };
86
90
  const blockedKinds = new Set(entries.flatMap((entry) => (entry.kind === 'model' && requirementOf(entry) !== null ? [entry.provider.kind] : [])));
87
91
 
88
92
  /* Closing forgets the search, so the next open (a click or `/model`) starts on the whole list. */
@@ -188,6 +192,7 @@ export function ModelPicker({
188
192
  const header = grouped && (before === undefined || before.kind === 'legacy' || before.provider.kind !== entry.provider.kind);
189
193
  return (
190
194
  <div key={`all-${entry.provider.kind}`}>
195
+ {separatesGroups(kindBefore(i), entry.provider.kind) && <Menu.Separator />}
191
196
  {header && (
192
197
  <Menu.Label className="flex items-center gap-1.5">
193
198
  <AgentIcon kind={entry.provider.kind} size={12} />
@@ -243,6 +248,7 @@ export function ModelPicker({
243
248
  const starred = starredHere(entry);
244
249
  return (
245
250
  <div key={`${entry.provider.kind}/${entry.model.slug}`}>
251
+ {separatesGroups(kindBefore(i), entry.provider.kind) && <Menu.Separator />}
246
252
  {first && (
247
253
  <Menu.Label className="flex items-center gap-1.5">
248
254
  <AgentIcon kind={entry.provider.kind} size={12} />
@@ -27,7 +27,7 @@ import type { AccountChoice } from '../account-choice';
27
27
  import { modelName, shortModelName } from '../../agents/model-name';
28
28
  import { ModelMarks } from './ModelMarks';
29
29
  import { SessionLine } from './SessionLine';
30
- import { favoritesFirst, searchModels, shortlistOf, wantsModelSearch } from '../logic/picker-entries';
30
+ import { favoritesFirst, inPickerOrder, searchModels, separatesGroups, shortlistOf, wantsModelSearch } from '../logic/picker-entries';
31
31
  import { favoriteModelsOf, toggleFavoriteModel, useChatPreferences } from '../preferences';
32
32
  import { CONTEXT_OPTION, contextFraction, contextSegments, orderOptions, type ContextPart } from '../logic/context-usage';
33
33
  import { RUNTIME_MODES } from '../runtime-modes';
@@ -113,7 +113,7 @@ function modelValue(provider: AgentKind, slug: string): string {
113
113
 
114
114
  /* Each provider's current or legacy models; a provider with none of them is left out. */
115
115
  function modelGroupsOf(providers: ProviderInfo[], legacy: boolean): Array<{ entry: ProviderInfo; models: ModelInfo[] }> {
116
- return providers
116
+ return inPickerOrder(providers)
117
117
  .map((entry) => ({ entry, models: entry.models.filter((row) => (row.legacy === true) === legacy) }))
118
118
  .filter((group) => group.models.length > 0);
119
119
  }
@@ -177,7 +177,7 @@ export function RunSettings({
177
177
  const searchable = wantsModelSearch(providers);
178
178
  const favorites = useChatPreferences((preferences) => preferences.favoriteModels);
179
179
  const favoritesOf = (kind: AgentKind): ReadonlySet<string> => favoriteModelsOf(favorites, kind);
180
- const found = query.trim() === '' ? null : searchModels(providers, query, favoritesOf);
180
+ const found = query.trim() === '' ? null : searchModels(inPickerOrder(providers), query, favoritesOf);
181
181
 
182
182
  // The menu moves the focus to its popup as it opens, so the search field takes it a frame later.
183
183
  useEffect(() => {
@@ -292,16 +292,19 @@ export function RunSettings({
292
292
  };
293
293
 
294
294
  const modelGroups = (groups: typeof live) =>
295
- groups.map(({ entry, models }) => (
296
- <Menu.Group key={entry.kind}>
297
- {grouped && (
298
- <Menu.GroupLabel className="flex items-center gap-1.5">
299
- <AgentIcon kind={entry.kind} size={12} />
300
- {entry.name}
301
- </Menu.GroupLabel>
302
- )}
303
- {providerModels(entry, models)}
304
- </Menu.Group>
295
+ groups.map(({ entry, models }, i) => (
296
+ <Fragment key={entry.kind}>
297
+ {separatesGroups(groups[i - 1]?.entry.kind, entry.kind) && <Menu.Separator />}
298
+ <Menu.Group>
299
+ {grouped && (
300
+ <Menu.GroupLabel className="flex items-center gap-1.5">
301
+ <AgentIcon kind={entry.kind} size={12} />
302
+ {entry.name}
303
+ </Menu.GroupLabel>
304
+ )}
305
+ {providerModels(entry, models)}
306
+ </Menu.Group>
307
+ </Fragment>
305
308
  ));
306
309
 
307
310
  return (
@@ -370,16 +373,19 @@ export function RunSettings({
370
373
  {found !== null && found.length === 0 && <div className="px-2.5 py-3 text-xs text-text-faint">{t('pickers.model.noMatch')}</div>}
371
374
  {found !== null && found.length > 0 && (
372
375
  <Menu.RadioGroup value={chosenModel} onValueChange={chooseModel}>
373
- {found.map(({ entry, models }) => (
374
- <Menu.Group key={entry.kind}>
375
- {grouped && (
376
- <Menu.GroupLabel className="flex items-center gap-1.5">
377
- <AgentIcon kind={entry.kind} size={12} />
378
- {entry.name}
379
- </Menu.GroupLabel>
380
- )}
381
- {byMaker(entry, models)}
382
- </Menu.Group>
376
+ {found.map(({ entry, models }, i) => (
377
+ <Fragment key={entry.kind}>
378
+ {separatesGroups(found[i - 1]?.entry.kind, entry.kind) && <Menu.Separator />}
379
+ <Menu.Group>
380
+ {grouped && (
381
+ <Menu.GroupLabel className="flex items-center gap-1.5">
382
+ <AgentIcon kind={entry.kind} size={12} />
383
+ {entry.name}
384
+ </Menu.GroupLabel>
385
+ )}
386
+ {byMaker(entry, models)}
387
+ </Menu.Group>
388
+ </Fragment>
383
389
  ))}
384
390
  </Menu.RadioGroup>
385
391
  )}
@@ -10,7 +10,8 @@ const CLI_COLORS: Partial<Record<AgentKind, string>> = {
10
10
  gemini: 'text-chart-gemini',
11
11
  copilot: 'text-chart-copilot',
12
12
  nanogpt: 'text-chart-nanogpt',
13
- grok: 'text-chart-grok'
13
+ grok: 'text-chart-grok',
14
+ 'anthropic-claude-api': 'text-chart-claude-api'
14
15
  };
15
16
 
16
17
  export function CliMark({ kind, size = 14, className }: { kind: AgentKind; size?: number; className?: string }) {
package/src/theme.css CHANGED
@@ -8,8 +8,10 @@
8
8
  --skill: #9333ea;
9
9
  --skill-soft: #f7eeff;
10
10
  /* One color per agent CLI, so a bar, a dot and a legend row all name the same provider. Apple's
11
- system orange and teal; Gemini and Copilot are reserved for the day their transcripts are read. */
11
+ system orange and teal, and purple for Claude through an API key so it reads apart from Claude Code;
12
+ Gemini and Copilot are reserved for the day their transcripts are read. */
12
13
  --chart-claude: #ff9500;
14
+ --chart-claude-api: #af52de;
13
15
  --chart-codex: #30b0c7;
14
16
  --chart-gemini: #3b82f6;
15
17
  --chart-copilot: #64748b;
@@ -37,6 +39,7 @@
37
39
  --skill: #c084fc;
38
40
  --skill-soft: #2f2350;
39
41
  --chart-claude: #ff9f0a;
42
+ --chart-claude-api: #bf5af2;
40
43
  --chart-codex: #40c8e0;
41
44
  --chart-gemini: #60a5fa;
42
45
  --chart-copilot: #94a3b8;
@@ -70,6 +73,7 @@
70
73
  --color-skill: var(--skill);
71
74
  --color-skill-soft: var(--skill-soft);
72
75
  --color-chart-claude: var(--chart-claude);
76
+ --color-chart-claude-api: var(--chart-claude-api);
73
77
  --color-chart-codex: var(--chart-codex);
74
78
  --color-chart-gemini: var(--chart-gemini);
75
79
  --color-chart-copilot: var(--chart-copilot);
@@ -33,7 +33,8 @@ export const PROVIDER_LABELS: Record<UsageProvider, string> = {
33
33
  codex: 'Codex',
34
34
  nanogpt: 'NanoGPT',
35
35
  grok: 'Grok',
36
- copilot: 'GitHub Copilot'
36
+ copilot: 'GitHub Copilot',
37
+ 'anthropic-claude-api': 'Claude API'
37
38
  };
38
39
 
39
40
  export const PROVIDER_COLORS: Record<UsageProvider, string> = {
@@ -41,7 +42,8 @@ export const PROVIDER_COLORS: Record<UsageProvider, string> = {
41
42
  codex: 'var(--chart-codex)',
42
43
  nanogpt: 'var(--chart-nanogpt)',
43
44
  grok: 'var(--chart-grok)',
44
- copilot: 'var(--chart-copilot)'
45
+ copilot: 'var(--chart-copilot)',
46
+ 'anthropic-claude-api': 'var(--chart-claude-api)'
45
47
  };
46
48
 
47
49
  /*