@opengeni/react 3.7.0-canary.2 → 3.7.0-canary.4
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/README.md +15 -0
- package/dist/{chunk-4JFUT22W.js → chunk-JT33KZZN.js} +48 -245
- package/dist/chunk-JT33KZZN.js.map +1 -0
- package/dist/chunk-KWEZ4MPF.js +220 -0
- package/dist/chunk-KWEZ4MPF.js.map +1 -0
- package/dist/{chunk-WZIMM3GX.js → chunk-LUYB4SRW.js} +7 -4
- package/dist/{chunk-WZIMM3GX.js.map → chunk-LUYB4SRW.js.map} +1 -1
- package/dist/components/model-policy-picker-menu.d.ts +9 -0
- package/dist/components/model-policy-picker.d.ts +10 -15
- package/dist/composer.d.ts +2 -1
- package/dist/composer.js +4 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/model-policy-picker-menu-R6WXNWJL.js +13 -0
- package/dist/model-policy-picker-menu-R6WXNWJL.js.map +1 -0
- package/dist/realtime.js +1 -1
- package/dist/session-ui.js +2 -2
- package/package.json +2 -2
- package/src/components/message-timeline.tsx +1 -1
- package/src/components/model-policy-picker-menu.tsx +261 -0
- package/src/components/model-policy-picker.tsx +84 -346
- package/src/components/session-conversation.tsx +4 -1
- package/src/composer.ts +2 -1
- package/src/index.ts +1 -1
- package/styles/compiled.css +30 -19
- package/styles/responsive.css +1 -1
- package/dist/chunk-4JFUT22W.js.map +0 -1
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { ClientModel, LatencyMode, ReasoningEffort } from "@opengeni/sdk";
|
|
2
2
|
import {
|
|
3
|
-
CheckIcon,
|
|
4
3
|
ChevronDownIcon,
|
|
5
4
|
ChevronLeftIcon,
|
|
6
5
|
ChevronRightIcon,
|
|
@@ -9,11 +8,11 @@ import {
|
|
|
9
8
|
ZapIcon,
|
|
10
9
|
} from "lucide-react";
|
|
11
10
|
import { motion, useReducedMotion } from "motion/react";
|
|
12
|
-
import { DropdownMenu } from "radix-ui";
|
|
13
11
|
import {
|
|
14
|
-
|
|
12
|
+
Suspense,
|
|
13
|
+
lazy,
|
|
15
14
|
useMemo,
|
|
16
|
-
|
|
15
|
+
useId,
|
|
17
16
|
useState,
|
|
18
17
|
type CSSProperties,
|
|
19
18
|
type ReactNode,
|
|
@@ -22,28 +21,20 @@ import {
|
|
|
22
21
|
import { cn } from "../lib/cn";
|
|
23
22
|
import { usePortalTokenSource, usePortalTokenStyle } from "../lib/use-portal-token-style";
|
|
24
23
|
import {
|
|
25
|
-
effortOptionsForModel,
|
|
26
24
|
findPickerRow,
|
|
27
|
-
groupPickerRowsByBillingClass,
|
|
28
25
|
labelReasoningEffort,
|
|
29
|
-
payerSummaryForModel,
|
|
30
26
|
projectClientModelRows,
|
|
31
|
-
runnableLatencyModesForModel,
|
|
32
27
|
type PickerBillingClass,
|
|
33
28
|
type PickerModelRow,
|
|
34
29
|
} from "../model-policy";
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
const LazyModelPolicyPickerMenu = lazy(() =>
|
|
32
|
+
import("./model-policy-picker-menu").then(({ ModelPolicyPickerPopover }) => ({
|
|
33
|
+
default: ModelPolicyPickerPopover,
|
|
34
|
+
})),
|
|
35
|
+
);
|
|
39
36
|
|
|
40
|
-
type
|
|
41
|
-
level: NavLevel;
|
|
42
|
-
rail: PickerBillingClass | null;
|
|
43
|
-
modelId: string | null;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
type NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);
|
|
37
|
+
type ClientPickerModelRow = PickerModelRow<ClientModel>;
|
|
47
38
|
|
|
48
39
|
export type ModelPolicyPickerMessages = {
|
|
49
40
|
label: string;
|
|
@@ -53,6 +44,14 @@ export type ModelPolicyPickerMessages = {
|
|
|
53
44
|
fast: string;
|
|
54
45
|
fastRateHint: string;
|
|
55
46
|
codexOnly: string;
|
|
47
|
+
searchLabel?: string;
|
|
48
|
+
searchPlaceholder?: string;
|
|
49
|
+
currentModel?: string;
|
|
50
|
+
noMatches?: string;
|
|
51
|
+
unsupportedAttachments?: string;
|
|
52
|
+
thinkingEffort?: string;
|
|
53
|
+
selected?: string;
|
|
54
|
+
|
|
56
55
|
billingHints: Record<PickerBillingClass, string>;
|
|
57
56
|
};
|
|
58
57
|
|
|
@@ -64,6 +63,15 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
|
|
|
64
63
|
fast: "Fast",
|
|
65
64
|
fastRateHint: "2× rate",
|
|
66
65
|
codexOnly: "Codex-only session",
|
|
66
|
+
searchLabel: "Search models or providers",
|
|
67
|
+
searchPlaceholder: "Search models or providers…",
|
|
68
|
+
currentModel: "Current model",
|
|
69
|
+
noMatches: "No matching models. Try a model or provider name.",
|
|
70
|
+
unsupportedAttachments:
|
|
71
|
+
"Unsupported attachments stay in the session but are hidden from this model.",
|
|
72
|
+
thinkingEffort: "Thinking effort",
|
|
73
|
+
selected: "Selected",
|
|
74
|
+
|
|
67
75
|
billingHints: {
|
|
68
76
|
opengeni_credits: "Will use credits",
|
|
69
77
|
external: "Provider terms and limits apply",
|
|
@@ -92,7 +100,7 @@ export type ModelPolicyPickerProps = {
|
|
|
92
100
|
onOpenChange?: ((open: boolean) => void) | undefined;
|
|
93
101
|
/** Non-Codex models stay visible but cannot be selected. */
|
|
94
102
|
codexOnly?: boolean | undefined;
|
|
95
|
-
/**
|
|
103
|
+
/** Identity of the current session scope. */
|
|
96
104
|
sessionKey?: string | undefined;
|
|
97
105
|
/** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */
|
|
98
106
|
menuSide?: "top" | "bottom" | undefined;
|
|
@@ -227,42 +235,12 @@ function applyCodexOnly(
|
|
|
227
235
|
);
|
|
228
236
|
}
|
|
229
237
|
|
|
230
|
-
function effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {
|
|
238
|
+
export function effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {
|
|
231
239
|
const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);
|
|
232
240
|
const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };
|
|
233
241
|
return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);
|
|
234
242
|
}
|
|
235
243
|
|
|
236
|
-
function defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {
|
|
237
|
-
const selected = findPickerRow(rows, modelId);
|
|
238
|
-
if (!selected) {
|
|
239
|
-
const rails = new Set(rows.map((row) => row.billingClass));
|
|
240
|
-
const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;
|
|
241
|
-
return onlyRail
|
|
242
|
-
? { level: "models", rail: onlyRail, modelId: null }
|
|
243
|
-
: { level: "providers", rail: null, modelId: null };
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
level: "thinking",
|
|
247
|
-
rail: selected.billingClass,
|
|
248
|
-
modelId: selected.id,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function usePickerNavState(
|
|
253
|
-
sessionKey: string | undefined,
|
|
254
|
-
rows: ClientPickerModelRow[],
|
|
255
|
-
model: string,
|
|
256
|
-
): [PickerNavState, (next: NavUpdater) => void] {
|
|
257
|
-
const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));
|
|
258
|
-
useEffect(() => {
|
|
259
|
-
setState(defaultNavState(rows, model));
|
|
260
|
-
// The menu deliberately preserves its drill-down while the same session is mounted.
|
|
261
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
262
|
-
}, [sessionKey]);
|
|
263
|
-
return [state, setState];
|
|
264
|
-
}
|
|
265
|
-
|
|
266
244
|
export function PickerNavRow(props: {
|
|
267
245
|
label: string;
|
|
268
246
|
hint?: string | undefined;
|
|
@@ -348,265 +326,26 @@ export function PickerAnimatedPage(props: {
|
|
|
348
326
|
);
|
|
349
327
|
}
|
|
350
328
|
|
|
351
|
-
|
|
352
|
-
props: ModelPolicyPickerProps & {
|
|
353
|
-
nav?: PickerNavState | undefined;
|
|
354
|
-
onNavChange?: ((next: NavUpdater) => void) | undefined;
|
|
355
|
-
},
|
|
356
|
-
) {
|
|
357
|
-
const messages = useMemo(
|
|
358
|
-
() => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
|
|
359
|
-
[props.messages],
|
|
360
|
-
);
|
|
361
|
-
const rows = effectiveRows(props);
|
|
362
|
-
const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);
|
|
363
|
-
const [localNav, setLocalNav] = usePickerNavState(
|
|
364
|
-
props.nav === undefined ? props.sessionKey : undefined,
|
|
365
|
-
rows,
|
|
366
|
-
props.model,
|
|
367
|
-
);
|
|
368
|
-
const nav = props.nav ?? localNav;
|
|
369
|
-
const setNav = props.onNavChange ?? setLocalNav;
|
|
370
|
-
const [direction, setDirection] = useState<1 | -1>(1);
|
|
371
|
-
|
|
372
|
-
const effectiveNav =
|
|
373
|
-
nav.level === "providers" && groups.length === 1
|
|
374
|
-
? {
|
|
375
|
-
level: "models" as const,
|
|
376
|
-
rail: groups[0]!.billingClass,
|
|
377
|
-
modelId: null,
|
|
378
|
-
}
|
|
379
|
-
: nav;
|
|
380
|
-
const activeGroup =
|
|
381
|
-
effectiveNav.rail === null
|
|
382
|
-
? undefined
|
|
383
|
-
: groups.find((group) => group.billingClass === effectiveNav.rail);
|
|
384
|
-
const focusModel =
|
|
385
|
-
effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);
|
|
386
|
-
const selectedRow = findPickerRow(rows, props.model);
|
|
387
|
-
|
|
388
|
-
const go = (next: PickerNavState, nextDirection: 1 | -1) => {
|
|
389
|
-
setDirection(nextDirection);
|
|
390
|
-
setNav(next);
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {
|
|
394
|
-
if (!row.selectable) return;
|
|
395
|
-
if (row.id !== props.model) props.onModelChange(row.id);
|
|
396
|
-
props.onEffortChange(effort);
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
const pageKey =
|
|
400
|
-
effectiveNav.level === "providers"
|
|
401
|
-
? "providers"
|
|
402
|
-
: effectiveNav.level === "models"
|
|
403
|
-
? `models:${effectiveNav.rail ?? ""}`
|
|
404
|
-
: `thinking:${effectiveNav.modelId ?? ""}`;
|
|
405
|
-
|
|
406
|
-
let body: ReactNode;
|
|
407
|
-
if (props.loading) {
|
|
408
|
-
body = <p className="px-2 py-3 text-og-control text-og-fg-subtle">{messages.loading}</p>;
|
|
409
|
-
} else if (groups.length === 0) {
|
|
410
|
-
body = <p className="px-2 py-3 text-og-control text-og-fg-subtle">{messages.noModels}</p>;
|
|
411
|
-
} else if (effectiveNav.level === "providers") {
|
|
412
|
-
body = (
|
|
413
|
-
<div className="flex flex-col gap-0.5" data-testid="model-picker-providers">
|
|
414
|
-
{groups.map((group) => (
|
|
415
|
-
<PickerNavRow
|
|
416
|
-
key={group.billingClass}
|
|
417
|
-
label={group.label}
|
|
418
|
-
hint={messages.billingHints[group.billingClass]}
|
|
419
|
-
icon={<BillingClassMark billingClass={group.billingClass} />}
|
|
420
|
-
active={selectedRow?.billingClass === group.billingClass}
|
|
421
|
-
testId={`model-picker-rail-${group.billingClass}`}
|
|
422
|
-
onClick={() => go({ level: "models", rail: group.billingClass, modelId: null }, 1)}
|
|
423
|
-
/>
|
|
424
|
-
))}
|
|
425
|
-
</div>
|
|
426
|
-
);
|
|
427
|
-
} else if (effectiveNav.level === "models" && activeGroup) {
|
|
428
|
-
body = (
|
|
429
|
-
<div data-testid="model-picker-models">
|
|
430
|
-
{groups.length > 1 ? (
|
|
431
|
-
<PickerBackHeader
|
|
432
|
-
label={activeGroup.label}
|
|
433
|
-
icon={<BillingClassMark billingClass={activeGroup.billingClass} />}
|
|
434
|
-
onBack={() => go({ level: "providers", rail: null, modelId: null }, -1)}
|
|
435
|
-
/>
|
|
436
|
-
) : null}
|
|
437
|
-
<div className="flex flex-col gap-0.5">
|
|
438
|
-
{activeGroup.rows.map((row) => (
|
|
439
|
-
<PickerNavRow
|
|
440
|
-
key={`${row.billingClass}:${row.id}`}
|
|
441
|
-
label={row.label}
|
|
442
|
-
hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}
|
|
443
|
-
disabled={!row.selectable}
|
|
444
|
-
title={row.unavailableReason ?? undefined}
|
|
445
|
-
active={row.id === props.model}
|
|
446
|
-
testId={`model-picker-choice-${row.id}`}
|
|
447
|
-
onClick={() => {
|
|
448
|
-
if (row.selectable) {
|
|
449
|
-
go(
|
|
450
|
-
{
|
|
451
|
-
level: "thinking",
|
|
452
|
-
rail: row.billingClass,
|
|
453
|
-
modelId: row.id,
|
|
454
|
-
},
|
|
455
|
-
1,
|
|
456
|
-
);
|
|
457
|
-
}
|
|
458
|
-
}}
|
|
459
|
-
/>
|
|
460
|
-
))}
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
463
|
-
);
|
|
464
|
-
} else if (effectiveNav.level === "thinking" && focusModel) {
|
|
465
|
-
const activeModel = focusModel.id === props.model;
|
|
466
|
-
const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes("fast");
|
|
467
|
-
body = (
|
|
468
|
-
<div data-testid="model-picker-reasoning">
|
|
469
|
-
<PickerBackHeader
|
|
470
|
-
label={focusModel.label}
|
|
471
|
-
icon={<BillingClassMark billingClass={focusModel.billingClass} />}
|
|
472
|
-
onBack={() => go({ level: "models", rail: focusModel.billingClass, modelId: null }, -1)}
|
|
473
|
-
trailing={
|
|
474
|
-
supportsFast && props.allowLatencyMode !== false ? (
|
|
475
|
-
<button
|
|
476
|
-
type="button"
|
|
477
|
-
disabled={!focusModel.selectable}
|
|
478
|
-
aria-pressed={activeModel && props.latencyMode === "fast"}
|
|
479
|
-
aria-label={
|
|
480
|
-
activeModel && props.latencyMode === "fast"
|
|
481
|
-
? `Disable ${messages.fast}`
|
|
482
|
-
: `Enable ${messages.fast}`
|
|
483
|
-
}
|
|
484
|
-
title={`${messages.fast} · ${messages.fastRateHint}`}
|
|
485
|
-
data-testid="model-picker-fast"
|
|
486
|
-
onClick={() => {
|
|
487
|
-
if (!activeModel) {
|
|
488
|
-
props.onModelChange(focusModel.id);
|
|
489
|
-
}
|
|
490
|
-
props.onLatencyModeChange(
|
|
491
|
-
activeModel && props.latencyMode === "fast" ? "standard" : "fast",
|
|
492
|
-
);
|
|
493
|
-
}}
|
|
494
|
-
className={cn(
|
|
495
|
-
"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
|
|
496
|
-
activeModel && props.latencyMode === "fast" && "text-og-fg",
|
|
497
|
-
)}
|
|
498
|
-
>
|
|
499
|
-
<ZapIcon
|
|
500
|
-
className={cn(
|
|
501
|
-
"size-3.5",
|
|
502
|
-
activeModel && props.latencyMode === "fast" && "fill-current",
|
|
503
|
-
)}
|
|
504
|
-
/>
|
|
505
|
-
</button>
|
|
506
|
-
) : null
|
|
507
|
-
}
|
|
508
|
-
/>
|
|
509
|
-
<p className="px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase">
|
|
510
|
-
{messages.thinking}
|
|
511
|
-
</p>
|
|
512
|
-
{focusModel.catalog.capabilities?.inputModalities.includes("image") === false ? (
|
|
513
|
-
<p className="px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle">
|
|
514
|
-
Unsupported attachments stay in the session but are hidden from this model.
|
|
515
|
-
</p>
|
|
516
|
-
) : null}
|
|
517
|
-
<div className="flex flex-col gap-0.5">
|
|
518
|
-
{effortOptionsForModel(focusModel.catalog).map((effort) => {
|
|
519
|
-
const selected = activeModel && effort === props.effort;
|
|
520
|
-
return (
|
|
521
|
-
<button
|
|
522
|
-
key={effort}
|
|
523
|
-
type="button"
|
|
524
|
-
disabled={!focusModel.selectable}
|
|
525
|
-
onClick={() => selectEffort(focusModel, effort)}
|
|
526
|
-
className={cn(
|
|
527
|
-
"flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
|
|
528
|
-
selected && "bg-og-surface-2",
|
|
529
|
-
)}
|
|
530
|
-
>
|
|
531
|
-
<span className="min-w-0 flex-1 truncate">{labelReasoningEffort(effort)}</span>
|
|
532
|
-
{selected ? (
|
|
533
|
-
<CheckIcon
|
|
534
|
-
className="ml-auto size-3.5 shrink-0"
|
|
535
|
-
data-testid="model-picker-effort-check"
|
|
536
|
-
/>
|
|
537
|
-
) : null}
|
|
538
|
-
</button>
|
|
539
|
-
);
|
|
540
|
-
})}
|
|
541
|
-
</div>
|
|
542
|
-
</div>
|
|
543
|
-
);
|
|
544
|
-
} else {
|
|
545
|
-
body = (
|
|
546
|
-
<div className="flex flex-col gap-0.5" data-testid="model-picker-providers">
|
|
547
|
-
{groups.map((group) => (
|
|
548
|
-
<PickerNavRow
|
|
549
|
-
key={group.billingClass}
|
|
550
|
-
label={group.label}
|
|
551
|
-
hint={messages.billingHints[group.billingClass]}
|
|
552
|
-
icon={<BillingClassMark billingClass={group.billingClass} />}
|
|
553
|
-
testId={`model-picker-rail-${group.billingClass}`}
|
|
554
|
-
onClick={() => go({ level: "models", rail: group.billingClass, modelId: null }, 1)}
|
|
555
|
-
/>
|
|
556
|
-
))}
|
|
557
|
-
</div>
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
return (
|
|
562
|
-
<div data-testid="model-picker-menu" className="relative overflow-hidden">
|
|
563
|
-
{props.error ? (
|
|
564
|
-
<p className="px-2 py-1 text-og-control text-og-status-failed" role="alert">
|
|
565
|
-
{props.error}
|
|
566
|
-
</p>
|
|
567
|
-
) : null}
|
|
568
|
-
<PickerAnimatedPage pageKey={pageKey} direction={direction}>
|
|
569
|
-
{body}
|
|
570
|
-
</PickerAnimatedPage>
|
|
571
|
-
</div>
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
/** @internal Shared state seam kept outside the Radix portal for deterministic testing. */
|
|
329
|
+
/** @internal Shared controlled/uncontrolled state seam. */
|
|
576
330
|
export function useModelPolicyPickerState(props: ModelPolicyPickerProps) {
|
|
577
331
|
const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);
|
|
578
332
|
const open = props.open ?? uncontrolledOpen;
|
|
579
|
-
const rows = effectiveRows(props);
|
|
580
|
-
const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);
|
|
581
|
-
const previousControlledOpen = useRef(props.open);
|
|
582
|
-
useEffect(() => {
|
|
583
|
-
const wasOpen = previousControlledOpen.current;
|
|
584
|
-
previousControlledOpen.current = props.open;
|
|
585
|
-
if (props.open === true && wasOpen !== true) {
|
|
586
|
-
setNav(defaultNavState(rows, props.model));
|
|
587
|
-
}
|
|
588
|
-
// Reset only on the controlled closed-to-open edge; ordinary rerenders
|
|
589
|
-
// deliberately preserve the current drill-down page.
|
|
590
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
591
|
-
}, [props.open]);
|
|
592
333
|
const setOpen = (next: boolean) => {
|
|
593
|
-
if (next) {
|
|
594
|
-
setNav(defaultNavState(rows, props.model));
|
|
595
|
-
}
|
|
596
334
|
if (props.open === undefined) setUncontrolledOpen(next);
|
|
597
335
|
props.onOpenChange?.(next);
|
|
598
336
|
};
|
|
599
|
-
return { open, setOpen, rows
|
|
337
|
+
return { open, setOpen, rows: effectiveRows(props) };
|
|
600
338
|
}
|
|
601
339
|
|
|
602
340
|
export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
|
|
341
|
+
const contentId = useId();
|
|
603
342
|
const trigger = usePortalTokenSource<HTMLButtonElement>();
|
|
604
343
|
const portalStyle = usePortalTokenStyle(trigger.source);
|
|
605
344
|
const messages = useMemo(
|
|
606
345
|
() => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
|
|
607
346
|
[props.messages],
|
|
608
347
|
);
|
|
609
|
-
const { open, setOpen, rows
|
|
348
|
+
const { open, setOpen, rows } = useModelPolicyPickerState(props);
|
|
610
349
|
const selected = findPickerRow(rows, props.model);
|
|
611
350
|
|
|
612
351
|
if (props.loading) {
|
|
@@ -622,60 +361,59 @@ export function ModelPolicyPicker(props: ModelPolicyPickerProps) {
|
|
|
622
361
|
);
|
|
623
362
|
}
|
|
624
363
|
return (
|
|
625
|
-
|
|
626
|
-
<
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
364
|
+
<>
|
|
365
|
+
<button
|
|
366
|
+
ref={trigger.ref}
|
|
367
|
+
type="button"
|
|
368
|
+
aria-haspopup="dialog"
|
|
369
|
+
aria-expanded={open}
|
|
370
|
+
aria-controls={open ? contentId : undefined}
|
|
371
|
+
data-state={open ? "open" : "closed"}
|
|
372
|
+
onClick={() => setOpen(!open)}
|
|
373
|
+
disabled={props.disabled}
|
|
374
|
+
aria-label={messages.label}
|
|
375
|
+
className={cn(
|
|
376
|
+
"og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2",
|
|
377
|
+
props.className,
|
|
378
|
+
)}
|
|
379
|
+
>
|
|
380
|
+
<BillingClassMark
|
|
381
|
+
billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}
|
|
382
|
+
className="text-og-fg"
|
|
383
|
+
/>
|
|
384
|
+
<span className="og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden @max-[20rem]/model-controls:hidden">
|
|
385
|
+
{selected?.label ?? props.model}
|
|
386
|
+
</span>
|
|
387
|
+
<span className="og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden @max-[20rem]/model-controls:block">
|
|
388
|
+
{selected?.shortLabel ?? selected?.label ?? props.model}
|
|
389
|
+
</span>
|
|
390
|
+
<span
|
|
391
|
+
className="og-model-policy-effort min-w-0 shrink-[9999] truncate"
|
|
392
|
+
title={labelReasoningEffort(props.effort)}
|
|
636
393
|
>
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
394
|
+
{labelReasoningEffort(props.effort)}
|
|
395
|
+
</span>
|
|
396
|
+
{props.latencyMode === "fast" ? (
|
|
397
|
+
<ZapIcon
|
|
398
|
+
className="size-3.5 shrink-0 fill-current stroke-current text-og-fg"
|
|
399
|
+
aria-label={messages.fast}
|
|
400
|
+
data-testid="model-picker-fast-icon"
|
|
640
401
|
/>
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
<
|
|
648
|
-
{
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
<ChevronDownIcon className="size-3 shrink-0" />
|
|
658
|
-
</button>
|
|
659
|
-
</DropdownMenu.Trigger>
|
|
660
|
-
<DropdownMenu.Portal>
|
|
661
|
-
<DropdownMenu.Content
|
|
662
|
-
align="start"
|
|
663
|
-
side={props.menuSide ?? "bottom"}
|
|
664
|
-
sideOffset={8}
|
|
665
|
-
collisionPadding={12}
|
|
666
|
-
onCloseAutoFocus={(event) => event.preventDefault()}
|
|
667
|
-
className={cn(
|
|
668
|
-
"og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg",
|
|
669
|
-
props.contentClassName,
|
|
670
|
-
)}
|
|
671
|
-
style={{ ...portalStyle, ...props.contentStyle }}
|
|
672
|
-
data-testid="model-picker-content"
|
|
673
|
-
>
|
|
674
|
-
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain">
|
|
675
|
-
<ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />
|
|
676
|
-
</div>
|
|
677
|
-
</DropdownMenu.Content>
|
|
678
|
-
</DropdownMenu.Portal>
|
|
679
|
-
</DropdownMenu.Root>
|
|
402
|
+
) : null}
|
|
403
|
+
<ChevronDownIcon className="size-3 shrink-0" />
|
|
404
|
+
</button>
|
|
405
|
+
|
|
406
|
+
{open ? (
|
|
407
|
+
<Suspense fallback={null}>
|
|
408
|
+
<LazyModelPolicyPickerMenu
|
|
409
|
+
{...props}
|
|
410
|
+
anchor={trigger.currentRef}
|
|
411
|
+
contentId={contentId}
|
|
412
|
+
portalStyle={portalStyle}
|
|
413
|
+
onOpenChange={setOpen}
|
|
414
|
+
/>
|
|
415
|
+
</Suspense>
|
|
416
|
+
) : null}
|
|
417
|
+
</>
|
|
680
418
|
);
|
|
681
419
|
}
|
|
@@ -59,7 +59,10 @@ function Conversation({
|
|
|
59
59
|
const error = detail.error ?? feed.error ?? human.error;
|
|
60
60
|
return (
|
|
61
61
|
<div
|
|
62
|
-
className={cn(
|
|
62
|
+
className={cn(
|
|
63
|
+
"og-root flex min-h-0 min-w-0 flex-col gap-2 overflow-hidden bg-og-bg text-og-fg",
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
63
66
|
ref={region}
|
|
64
67
|
style={{ height }}
|
|
65
68
|
data-og-conversation=""
|
package/src/composer.ts
CHANGED
|
@@ -7,7 +7,6 @@ export { OPEN_WORKSTREAM_CONTROL_EVENT } from "./workstream-control-event";
|
|
|
7
7
|
export {
|
|
8
8
|
BillingClassMark,
|
|
9
9
|
ModelPolicyPicker,
|
|
10
|
-
ModelPolicyPickerMenu,
|
|
11
10
|
PickerAnimatedPage,
|
|
12
11
|
PickerBackHeader,
|
|
13
12
|
PickerNavRow,
|
|
@@ -109,3 +108,5 @@ export type {
|
|
|
109
108
|
VoiceRecordingTranscriptionState,
|
|
110
109
|
VoiceRecordingUploadState,
|
|
111
110
|
} from "./voice-recording-store";
|
|
111
|
+
|
|
112
|
+
export { ModelPolicyPickerMenu } from "./components/model-policy-picker-menu";
|
package/src/index.ts
CHANGED
|
@@ -477,12 +477,12 @@ export type { ModelPickerProps } from "./components/model-picker";
|
|
|
477
477
|
export {
|
|
478
478
|
BillingClassMark,
|
|
479
479
|
ModelPolicyPicker,
|
|
480
|
-
ModelPolicyPickerMenu,
|
|
481
480
|
PickerAnimatedPage,
|
|
482
481
|
PickerBackHeader,
|
|
483
482
|
PickerNavRow,
|
|
484
483
|
defaultModelPolicyPickerMessages,
|
|
485
484
|
} from "./components/model-policy-picker";
|
|
485
|
+
export { ModelPolicyPickerMenu } from "./components/model-policy-picker-menu";
|
|
486
486
|
export type {
|
|
487
487
|
ModelPolicyPickerMessages,
|
|
488
488
|
ModelPolicyPickerProps,
|
package/styles/compiled.css
CHANGED
|
@@ -1346,9 +1346,6 @@
|
|
|
1346
1346
|
:where(.og-root).h-\[min\(28rem\,50dvh\)\],:where(.og-root) .h-\[min\(28rem\,50dvh\)\] {
|
|
1347
1347
|
height: min(28rem, 50dvh);
|
|
1348
1348
|
}
|
|
1349
|
-
:where(.og-root).h-\[var\(--og-model-picker-effort-height\)\],:where(.og-root) .h-\[var\(--og-model-picker-effort-height\)\] {
|
|
1350
|
-
height: var(--og-model-picker-effort-height);
|
|
1351
|
-
}
|
|
1352
1349
|
:where(.og-root).h-\[var\(--og-model-picker-trigger-height\)\],:where(.og-root) .h-\[var\(--og-model-picker-trigger-height\)\] {
|
|
1353
1350
|
height: var(--og-model-picker-trigger-height);
|
|
1354
1351
|
}
|
|
@@ -1412,6 +1409,9 @@
|
|
|
1412
1409
|
:where(.og-root).max-h-\[min\(32rem\,70vh\)\],:where(.og-root) .max-h-\[min\(32rem\,70vh\)\] {
|
|
1413
1410
|
max-height: min(32rem, 70vh);
|
|
1414
1411
|
}
|
|
1412
|
+
:where(.og-root).max-h-\[min\(32rem\,var\(--radix-popover-content-available-height\)\)\],:where(.og-root) .max-h-\[min\(32rem\,var\(--radix-popover-content-available-height\)\)\] {
|
|
1413
|
+
max-height: min(32rem, var(--radix-popover-content-available-height));
|
|
1414
|
+
}
|
|
1415
1415
|
:where(.og-root).max-h-full,:where(.og-root) .max-h-full {
|
|
1416
1416
|
max-height: 100%;
|
|
1417
1417
|
}
|
|
@@ -1502,6 +1502,9 @@
|
|
|
1502
1502
|
:where(.og-root).w-\[3\.75rem\],:where(.og-root) .w-\[3\.75rem\] {
|
|
1503
1503
|
width: 3.75rem;
|
|
1504
1504
|
}
|
|
1505
|
+
:where(.og-root).w-\[22rem\],:where(.og-root) .w-\[22rem\] {
|
|
1506
|
+
width: 22rem;
|
|
1507
|
+
}
|
|
1505
1508
|
:where(.og-root).w-\[52px\],:where(.og-root) .w-\[52px\] {
|
|
1506
1509
|
width: 52px;
|
|
1507
1510
|
}
|
|
@@ -1517,9 +1520,6 @@
|
|
|
1517
1520
|
:where(.og-root).w-\[min\(36rem\,calc\(100vw-2rem\)\)\],:where(.og-root) .w-\[min\(36rem\,calc\(100vw-2rem\)\)\] {
|
|
1518
1521
|
width: min(36rem, calc(100vw - 2rem));
|
|
1519
1522
|
}
|
|
1520
|
-
:where(.og-root).w-\[var\(--og-model-picker-menu-width\)\],:where(.og-root) .w-\[var\(--og-model-picker-menu-width\)\] {
|
|
1521
|
-
width: var(--og-model-picker-menu-width);
|
|
1522
|
-
}
|
|
1523
1523
|
:where(.og-root).w-auto,:where(.og-root) .w-auto {
|
|
1524
1524
|
width: auto;
|
|
1525
1525
|
}
|
|
@@ -1640,6 +1640,9 @@
|
|
|
1640
1640
|
:where(.og-root).shrink-0,:where(.og-root) .shrink-0 {
|
|
1641
1641
|
flex-shrink: 0;
|
|
1642
1642
|
}
|
|
1643
|
+
:where(.og-root).shrink-\[9999\],:where(.og-root) .shrink-\[9999\] {
|
|
1644
|
+
flex-shrink: 9999;
|
|
1645
|
+
}
|
|
1643
1646
|
:where(.og-root).grow,:where(.og-root) .grow {
|
|
1644
1647
|
flex-grow: 1;
|
|
1645
1648
|
}
|
|
@@ -2157,6 +2160,12 @@
|
|
|
2157
2160
|
border-color: color-mix(in oklab, var(--og-color-border) 50%, transparent);
|
|
2158
2161
|
}
|
|
2159
2162
|
}
|
|
2163
|
+
:where(.og-root).border-og-border\/60,:where(.og-root) .border-og-border\/60 {
|
|
2164
|
+
border-color: var(--og-color-border);
|
|
2165
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2166
|
+
border-color: color-mix(in oklab, var(--og-color-border) 60%, transparent);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2160
2169
|
:where(.og-root).border-og-border\/70,:where(.og-root) .border-og-border\/70 {
|
|
2161
2170
|
border-color: var(--og-color-border);
|
|
2162
2171
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -2307,12 +2316,6 @@
|
|
|
2307
2316
|
:where(.og-root).bg-black,:where(.og-root) .bg-black {
|
|
2308
2317
|
background-color: var(--color-black);
|
|
2309
2318
|
}
|
|
2310
|
-
:where(.og-root).bg-black\/5,:where(.og-root) .bg-black\/5 {
|
|
2311
|
-
background-color: color-mix(in srgb, #000 5%, transparent);
|
|
2312
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
2313
|
-
background-color: color-mix(in oklab, var(--color-black) 5%, transparent);
|
|
2314
|
-
}
|
|
2315
|
-
}
|
|
2316
2319
|
:where(.og-root).bg-black\/45,:where(.og-root) .bg-black\/45 {
|
|
2317
2320
|
background-color: color-mix(in srgb, #000 45%, transparent);
|
|
2318
2321
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -2430,6 +2433,12 @@
|
|
|
2430
2433
|
:where(.og-root).bg-og-fg-subtle,:where(.og-root) .bg-og-fg-subtle {
|
|
2431
2434
|
background-color: var(--og-color-fg-subtle);
|
|
2432
2435
|
}
|
|
2436
|
+
:where(.og-root).bg-og-fg\/5,:where(.og-root) .bg-og-fg\/5 {
|
|
2437
|
+
background-color: var(--og-color-fg);
|
|
2438
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
2439
|
+
background-color: color-mix(in oklab, var(--og-color-fg) 5%, transparent);
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2433
2442
|
:where(.og-root).bg-og-status-cancelled,:where(.og-root) .bg-og-status-cancelled {
|
|
2434
2443
|
background-color: var(--og-color-status-cancelled);
|
|
2435
2444
|
}
|
|
@@ -5096,12 +5105,14 @@
|
|
|
5096
5105
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
5097
5106
|
}
|
|
5098
5107
|
}
|
|
5099
|
-
:where(.og-root)
|
|
5100
|
-
@
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5108
|
+
:where(.og-root).\@max-\[20rem\]\/model-controls\:block,:where(.og-root) .\@max-\[20rem\]\/model-controls\:block {
|
|
5109
|
+
@container model-controls (width < 20rem) {
|
|
5110
|
+
display: block;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
:where(.og-root).\@max-\[20rem\]\/model-controls\:hidden,:where(.og-root) .\@max-\[20rem\]\/model-controls\:hidden {
|
|
5114
|
+
@container model-controls (width < 20rem) {
|
|
5115
|
+
display: none;
|
|
5105
5116
|
}
|
|
5106
5117
|
}
|
|
5107
5118
|
:where(.og-root).dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20,:where(.og-root) .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20 {
|
|
@@ -5475,7 +5486,7 @@
|
|
|
5475
5486
|
}
|
|
5476
5487
|
:where(.og-root).og-model-policy-menu,:where(.og-root) .og-model-policy-menu {
|
|
5477
5488
|
box-sizing: border-box;
|
|
5478
|
-
width: min( var(--og-model-picker-menu-width), calc(var(--og-portal-source-inline-size, 100vw) - 1rem), var(--radix-
|
|
5489
|
+
width: min( var(--og-model-picker-menu-width), calc(var(--og-portal-source-inline-size, 100vw) - 1rem), var(--radix-popover-content-available-width, 100vw) );
|
|
5479
5490
|
max-width: calc(100vw - 1rem);
|
|
5480
5491
|
}
|
|
5481
5492
|
:where(.og-root).og-realtime-menu,:where(.og-root) .og-realtime-menu {
|