@opengeni/react 0.36.1 → 0.37.0

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.
Files changed (42) hide show
  1. package/README.md +4 -0
  2. package/dist/{chunk-J2RVJ6JX.js → chunk-4IJCL7YO.js} +13 -2
  3. package/dist/chunk-4IJCL7YO.js.map +1 -0
  4. package/dist/{chunk-OKKMZDQF.js → chunk-FSPDND3P.js} +2 -2
  5. package/dist/{chunk-FT2TXNGZ.js → chunk-HJ4OQVGW.js} +174 -14
  6. package/dist/chunk-HJ4OQVGW.js.map +1 -0
  7. package/dist/{chunk-N4XEBE23.js → chunk-IP22SLO6.js} +432 -10
  8. package/dist/chunk-IP22SLO6.js.map +1 -0
  9. package/dist/{chunk-22RM4GMO.js → chunk-SJKT4TKW.js} +26 -2
  10. package/dist/chunk-SJKT4TKW.js.map +1 -0
  11. package/dist/{chunk-Z5FV355Z.js → chunk-UCZPNXV3.js} +91 -14
  12. package/dist/chunk-UCZPNXV3.js.map +1 -0
  13. package/dist/components/chat-composer.d.ts +3 -1
  14. package/dist/components/model-policy-picker.d.ts +78 -0
  15. package/dist/components/session-chrome.d.ts +1 -1
  16. package/dist/composer.d.ts +2 -0
  17. package/dist/composer.js +17 -3
  18. package/dist/hooks/use-composer.d.ts +9 -0
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.js +30 -9
  21. package/dist/index.js.map +1 -1
  22. package/dist/model-policy.d.ts +12 -4
  23. package/dist/model-policy.js +5 -1
  24. package/dist/session-ui.js +2 -2
  25. package/dist/session.js +3 -3
  26. package/dist/timeline/types.d.ts +5 -0
  27. package/package.json +2 -2
  28. package/src/components/chat-composer.tsx +8 -3
  29. package/src/components/model-policy-picker.tsx +582 -0
  30. package/src/components/session-chrome.tsx +138 -14
  31. package/src/composer.ts +13 -0
  32. package/src/hooks/use-composer.ts +213 -16
  33. package/src/index.ts +15 -0
  34. package/src/model-policy.ts +51 -7
  35. package/src/timeline/projection.ts +21 -1
  36. package/src/timeline/types.ts +5 -0
  37. package/dist/chunk-22RM4GMO.js.map +0 -1
  38. package/dist/chunk-FT2TXNGZ.js.map +0 -1
  39. package/dist/chunk-J2RVJ6JX.js.map +0 -1
  40. package/dist/chunk-N4XEBE23.js.map +0 -1
  41. package/dist/chunk-Z5FV355Z.js.map +0 -1
  42. /package/dist/{chunk-OKKMZDQF.js.map → chunk-FSPDND3P.js.map} +0 -0
@@ -1,10 +1,16 @@
1
1
  import {
2
2
  shouldSteerOnKey,
3
3
  shouldSubmitOnKey
4
- } from "./chunk-FT2TXNGZ.js";
4
+ } from "./chunk-HJ4OQVGW.js";
5
5
  import {
6
- groupPickerRowsByBillingClass
7
- } from "./chunk-22RM4GMO.js";
6
+ coerceReasoningEffortForModel,
7
+ effortOptionsForModel,
8
+ findPickerRow,
9
+ groupPickerRowsByBillingClass,
10
+ labelReasoningEffort,
11
+ projectClientModelRows,
12
+ runnableLatencyModesForModel
13
+ } from "./chunk-SJKT4TKW.js";
8
14
  import {
9
15
  Tooltip,
10
16
  TooltipContent,
@@ -1291,17 +1297,17 @@ function ModelPicker({
1291
1297
  codexOnly = false
1292
1298
  }) {
1293
1299
  const selectId = useId();
1294
- const effectiveRows = useMemo3(
1300
+ const effectiveRows2 = useMemo3(
1295
1301
  () => rows && rows.length > 0 ? applyCodexOnlyRows(rows, codexOnly) : [],
1296
1302
  [rows, codexOnly]
1297
1303
  );
1298
1304
  const billingGroups = useMemo3(
1299
- () => effectiveRows.length > 0 ? groupPickerRowsByBillingClass(effectiveRows) : [],
1300
- [effectiveRows]
1305
+ () => effectiveRows2.length > 0 ? groupPickerRowsByBillingClass(effectiveRows2) : [],
1306
+ [effectiveRows2]
1301
1307
  );
1302
1308
  const providerGroupList = useMemo3(() => providerGroups(models, codexOnly), [models, codexOnly]);
1303
- const useBillingGroups = Boolean(effectiveRows.length);
1304
- if (models.length === 0 && effectiveRows.length === 0) {
1309
+ const useBillingGroups = Boolean(effectiveRows2.length);
1310
+ if (models.length === 0 && effectiveRows2.length === 0) {
1305
1311
  return null;
1306
1312
  }
1307
1313
  return /* @__PURE__ */ jsxs2("span", { className: cn("relative inline-flex items-center", className), children: [
@@ -2770,6 +2776,415 @@ function transcriptionErrorMessage(code, messages) {
2770
2776
  }
2771
2777
  }
2772
2778
 
2779
+ // src/components/model-policy-picker.tsx
2780
+ import {
2781
+ CheckIcon,
2782
+ ChevronDownIcon as ChevronDownIcon3,
2783
+ ChevronLeftIcon,
2784
+ ChevronRightIcon,
2785
+ KeyRoundIcon,
2786
+ ZapIcon
2787
+ } from "lucide-react";
2788
+ import { motion as motion4, useReducedMotion } from "motion/react";
2789
+ import { DropdownMenu } from "radix-ui";
2790
+ import { useEffect as useEffect5, useMemo as useMemo5, useState as useState6 } from "react";
2791
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2792
+ var defaultModelPolicyPickerMessages = {
2793
+ label: "Model and effort",
2794
+ loading: "Loading model catalog\u2026",
2795
+ noModels: "No models available.",
2796
+ thinking: "Thinking",
2797
+ fast: "Fast",
2798
+ fastRateHint: "2\xD7 rate",
2799
+ codexOnly: "Codex-only session",
2800
+ billingHints: {
2801
+ opengeni_credits: "Will use credits",
2802
+ codex_subscription: "ChatGPT / Codex plan",
2803
+ byok: "Billed to your AI Gateway"
2804
+ }
2805
+ };
2806
+ var SLIDE_EASE = [0.22, 1, 0.36, 1];
2807
+ function OpenGeniMark(props) {
2808
+ return /* @__PURE__ */ jsx5(
2809
+ "svg",
2810
+ {
2811
+ viewBox: "0 0 140 133",
2812
+ fill: "none",
2813
+ stroke: "currentColor",
2814
+ strokeWidth: 1.2,
2815
+ strokeLinejoin: "round",
2816
+ "aria-hidden": "true",
2817
+ ...props,
2818
+ children: /* @__PURE__ */ jsx5("g", { transform: "translate(-17.5,-20.999893188476562) scale(1.75)", children: /* @__PURE__ */ jsx5("g", { transform: "translate(0,-952.36218)", children: /* @__PURE__ */ jsx5("path", { d: "m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z" }) }) })
2819
+ }
2820
+ );
2821
+ }
2822
+ function ChatGptMark(props) {
2823
+ return /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", ...props, children: /* @__PURE__ */ jsx5("path", { d: "M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" }) });
2824
+ }
2825
+ function BillingClassMark(props) {
2826
+ const labels = {
2827
+ opengeni_credits: "OpenGeni",
2828
+ codex_subscription: "Codex",
2829
+ byok: "Bring your own key"
2830
+ };
2831
+ const label = props["aria-label"] ?? labels[props.billingClass];
2832
+ const accessibility = label.length === 0 ? { "aria-hidden": true } : { role: "img", "aria-label": label };
2833
+ const shell = cn(
2834
+ "inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle",
2835
+ props.className
2836
+ );
2837
+ const mark = "size-3.5";
2838
+ return /* @__PURE__ */ jsx5(
2839
+ "span",
2840
+ {
2841
+ className: shell,
2842
+ "data-testid": `billing-class-icon-${props.billingClass}`,
2843
+ ...accessibility,
2844
+ children: props.billingClass === "opengeni_credits" ? /* @__PURE__ */ jsx5(OpenGeniMark, { className: mark }) : props.billingClass === "codex_subscription" ? /* @__PURE__ */ jsx5(ChatGptMark, { className: mark }) : /* @__PURE__ */ jsx5(KeyRoundIcon, { className: mark, "aria-hidden": true })
2845
+ }
2846
+ );
2847
+ }
2848
+ function isCodexModel(model) {
2849
+ return model.id.startsWith("codex/") || model.source === "codex";
2850
+ }
2851
+ function applyCodexOnly(rows, codexOnly, unavailableReason) {
2852
+ if (!codexOnly) return rows;
2853
+ return rows.map(
2854
+ (row) => isCodexModel(row.catalog) ? row : {
2855
+ ...row,
2856
+ selectable: false,
2857
+ unavailableReason: row.unavailableReason ?? unavailableReason
2858
+ }
2859
+ );
2860
+ }
2861
+ function effectiveRows(props) {
2862
+ const rows = props.rows !== void 0 ? props.rows : projectClientModelRows(props.models ?? []);
2863
+ const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };
2864
+ return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);
2865
+ }
2866
+ function defaultNavState(rows, modelId) {
2867
+ const selected = findPickerRow(rows, modelId);
2868
+ if (!selected) {
2869
+ const rails = new Set(rows.map((row) => row.billingClass));
2870
+ const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;
2871
+ return onlyRail ? { level: "models", rail: onlyRail, modelId: null } : { level: "providers", rail: null, modelId: null };
2872
+ }
2873
+ return { level: "thinking", rail: selected.billingClass, modelId: selected.id };
2874
+ }
2875
+ function usePickerNavState(sessionKey, rows, model) {
2876
+ const [state, setState] = useState6(() => defaultNavState(rows, model));
2877
+ useEffect5(() => {
2878
+ setState(defaultNavState(rows, model));
2879
+ }, [sessionKey]);
2880
+ return [state, setState];
2881
+ }
2882
+ function PickerNavRow(props) {
2883
+ return /* @__PURE__ */ jsxs5(
2884
+ "button",
2885
+ {
2886
+ type: "button",
2887
+ disabled: props.disabled,
2888
+ title: props.title,
2889
+ onClick: props.onClick,
2890
+ "data-testid": props.testId,
2891
+ className: cn(
2892
+ "flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-2.5 py-2 text-left text-og-fg outline-none transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40",
2893
+ props.disabled && "cursor-not-allowed opacity-50"
2894
+ ),
2895
+ children: [
2896
+ props.icon,
2897
+ /* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1", children: [
2898
+ /* @__PURE__ */ jsx5("span", { className: cn("block truncate text-sm", props.active && "font-medium"), children: props.label }),
2899
+ props.hint ? /* @__PURE__ */ jsx5("span", { className: "mt-0.5 block truncate text-xs text-og-fg-subtle", children: props.hint }) : null
2900
+ ] }),
2901
+ props.trailing ? /* @__PURE__ */ jsx5("span", { className: "ml-auto shrink-0", children: props.trailing }) : null,
2902
+ props.showChevron === false ? null : /* @__PURE__ */ jsx5(ChevronRightIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle" })
2903
+ ]
2904
+ }
2905
+ );
2906
+ }
2907
+ function PickerBackHeader(props) {
2908
+ return /* @__PURE__ */ jsxs5("div", { className: "mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5", children: [
2909
+ /* @__PURE__ */ jsxs5(
2910
+ "button",
2911
+ {
2912
+ type: "button",
2913
+ onClick: props.onBack,
2914
+ "data-testid": "model-picker-back",
2915
+ className: "flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-none transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40",
2916
+ children: [
2917
+ /* @__PURE__ */ jsx5(ChevronLeftIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle" }),
2918
+ props.icon,
2919
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: props.label })
2920
+ ]
2921
+ }
2922
+ ),
2923
+ props.trailing ? /* @__PURE__ */ jsx5("div", { className: "relative z-10 shrink-0", children: props.trailing }) : null
2924
+ ] });
2925
+ }
2926
+ function PickerAnimatedPage(props) {
2927
+ const reduceMotion = useReducedMotion();
2928
+ return /* @__PURE__ */ jsx5(
2929
+ motion4.div,
2930
+ {
2931
+ initial: reduceMotion ? false : { x: props.direction * 14, opacity: 0 },
2932
+ animate: { x: 0, opacity: 1 },
2933
+ transition: { duration: reduceMotion ? 0 : 0.18, ease: SLIDE_EASE },
2934
+ className: "w-full",
2935
+ children: props.children
2936
+ },
2937
+ props.pageKey
2938
+ );
2939
+ }
2940
+ function ModelPolicyPickerMenu(props) {
2941
+ const messages = useMemo5(
2942
+ () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
2943
+ [props.messages]
2944
+ );
2945
+ const rows = effectiveRows(props);
2946
+ const groups = useMemo5(() => groupPickerRowsByBillingClass(rows), [rows]);
2947
+ const [localNav, setLocalNav] = usePickerNavState(
2948
+ props.nav === void 0 ? props.sessionKey : void 0,
2949
+ rows,
2950
+ props.model
2951
+ );
2952
+ const nav = props.nav ?? localNav;
2953
+ const setNav = props.onNavChange ?? setLocalNav;
2954
+ const [direction, setDirection] = useState6(1);
2955
+ const effectiveNav = nav.level === "providers" && groups.length === 1 ? { level: "models", rail: groups[0].billingClass, modelId: null } : nav;
2956
+ const activeGroup = effectiveNav.rail === null ? void 0 : groups.find((group) => group.billingClass === effectiveNav.rail);
2957
+ const focusModel = effectiveNav.modelId === null ? void 0 : findPickerRow(rows, effectiveNav.modelId);
2958
+ const selectedRow = findPickerRow(rows, props.model);
2959
+ const { latencyMode, loading, onLatencyModeChange } = props;
2960
+ useEffect5(() => {
2961
+ if (loading || !selectedRow?.selectable) return;
2962
+ const supportsCurrentMode = runnableLatencyModesForModel(selectedRow.catalog).includes(
2963
+ latencyMode
2964
+ );
2965
+ if (!supportsCurrentMode && latencyMode !== "standard") {
2966
+ onLatencyModeChange("standard");
2967
+ }
2968
+ }, [latencyMode, loading, onLatencyModeChange, selectedRow]);
2969
+ const go = (next, nextDirection) => {
2970
+ setDirection(nextDirection);
2971
+ setNav(next);
2972
+ };
2973
+ const selectEffort = (row, effort) => {
2974
+ if (!row.selectable) return;
2975
+ if (row.id !== props.model) props.onModelChange(row.id);
2976
+ props.onEffortChange(coerceReasoningEffortForModel(row.catalog, effort));
2977
+ };
2978
+ const pageKey = effectiveNav.level === "providers" ? "providers" : effectiveNav.level === "models" ? `models:${effectiveNav.rail ?? ""}` : `thinking:${effectiveNav.modelId ?? ""}`;
2979
+ let body;
2980
+ if (props.loading) {
2981
+ body = /* @__PURE__ */ jsx5("p", { className: "px-2 py-3 text-xs text-og-fg-subtle", children: messages.loading });
2982
+ } else if (groups.length === 0) {
2983
+ body = /* @__PURE__ */ jsx5("p", { className: "px-2 py-3 text-xs text-og-fg-subtle", children: messages.noModels });
2984
+ } else if (effectiveNav.level === "providers") {
2985
+ body = /* @__PURE__ */ jsx5("div", { className: "flex flex-col gap-0.5", "data-testid": "model-picker-providers", children: groups.map((group) => /* @__PURE__ */ jsx5(
2986
+ PickerNavRow,
2987
+ {
2988
+ label: group.label,
2989
+ hint: messages.billingHints[group.billingClass],
2990
+ icon: /* @__PURE__ */ jsx5(BillingClassMark, { billingClass: group.billingClass }),
2991
+ active: selectedRow?.billingClass === group.billingClass,
2992
+ testId: `model-picker-rail-${group.billingClass}`,
2993
+ onClick: () => go({ level: "models", rail: group.billingClass, modelId: null }, 1)
2994
+ },
2995
+ group.billingClass
2996
+ )) });
2997
+ } else if (effectiveNav.level === "models" && activeGroup) {
2998
+ body = /* @__PURE__ */ jsxs5("div", { "data-testid": "model-picker-models", children: [
2999
+ groups.length > 1 ? /* @__PURE__ */ jsx5(
3000
+ PickerBackHeader,
3001
+ {
3002
+ label: activeGroup.label,
3003
+ icon: /* @__PURE__ */ jsx5(BillingClassMark, { billingClass: activeGroup.billingClass }),
3004
+ onBack: () => go({ level: "providers", rail: null, modelId: null }, -1)
3005
+ }
3006
+ ) : null,
3007
+ /* @__PURE__ */ jsx5("div", { className: "flex flex-col gap-0.5", children: activeGroup.rows.map((row) => /* @__PURE__ */ jsx5(
3008
+ PickerNavRow,
3009
+ {
3010
+ label: row.label,
3011
+ hint: row.unavailableReason ?? void 0,
3012
+ disabled: !row.selectable,
3013
+ title: row.unavailableReason ?? void 0,
3014
+ active: row.id === props.model,
3015
+ testId: `model-picker-choice-${row.id}`,
3016
+ onClick: () => {
3017
+ if (row.selectable) {
3018
+ go({ level: "thinking", rail: row.billingClass, modelId: row.id }, 1);
3019
+ }
3020
+ }
3021
+ },
3022
+ `${row.billingClass}:${row.id}`
3023
+ )) })
3024
+ ] });
3025
+ } else if (effectiveNav.level === "thinking" && focusModel) {
3026
+ const activeModel = focusModel.id === props.model;
3027
+ const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes("fast");
3028
+ body = /* @__PURE__ */ jsxs5("div", { "data-testid": "model-picker-reasoning", children: [
3029
+ /* @__PURE__ */ jsx5(
3030
+ PickerBackHeader,
3031
+ {
3032
+ label: focusModel.label,
3033
+ icon: /* @__PURE__ */ jsx5(BillingClassMark, { billingClass: focusModel.billingClass }),
3034
+ onBack: () => go({ level: "models", rail: focusModel.billingClass, modelId: null }, -1),
3035
+ trailing: supportsFast ? /* @__PURE__ */ jsx5(
3036
+ "button",
3037
+ {
3038
+ type: "button",
3039
+ disabled: !focusModel.selectable,
3040
+ "aria-pressed": activeModel && props.latencyMode === "fast",
3041
+ "aria-label": activeModel && props.latencyMode === "fast" ? `Disable ${messages.fast}` : `Enable ${messages.fast}`,
3042
+ title: `${messages.fast} \xB7 ${messages.fastRateHint}`,
3043
+ "data-testid": "model-picker-fast",
3044
+ onClick: () => {
3045
+ if (!activeModel) {
3046
+ props.onModelChange(focusModel.id);
3047
+ props.onEffortChange(
3048
+ coerceReasoningEffortForModel(focusModel.catalog, props.effort)
3049
+ );
3050
+ }
3051
+ props.onLatencyModeChange(
3052
+ activeModel && props.latencyMode === "fast" ? "standard" : "fast"
3053
+ );
3054
+ },
3055
+ className: cn(
3056
+ "flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-none transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
3057
+ activeModel && props.latencyMode === "fast" && "text-og-fg"
3058
+ ),
3059
+ children: /* @__PURE__ */ jsx5(
3060
+ ZapIcon,
3061
+ {
3062
+ className: cn(
3063
+ "size-3.5",
3064
+ activeModel && props.latencyMode === "fast" && "fill-current"
3065
+ )
3066
+ }
3067
+ )
3068
+ }
3069
+ ) : null
3070
+ }
3071
+ ),
3072
+ /* @__PURE__ */ jsx5("p", { className: "px-2.5 pt-1 pb-1 text-xs font-medium tracking-wide text-og-fg-subtle uppercase", children: messages.thinking }),
3073
+ /* @__PURE__ */ jsx5("div", { className: "flex flex-col gap-0.5", children: effortOptionsForModel(focusModel.catalog).map((effort) => {
3074
+ const selected = activeModel && effort === props.effort;
3075
+ return /* @__PURE__ */ jsxs5(
3076
+ "button",
3077
+ {
3078
+ type: "button",
3079
+ disabled: !focusModel.selectable,
3080
+ onClick: () => selectEffort(focusModel, effort),
3081
+ className: cn(
3082
+ "flex h-8 w-full cursor-pointer items-center rounded-og-sm px-2.5 text-left text-sm text-og-fg outline-none transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50",
3083
+ selected && "bg-og-surface-2"
3084
+ ),
3085
+ children: [
3086
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate", children: labelReasoningEffort(effort) }),
3087
+ selected ? /* @__PURE__ */ jsx5(
3088
+ CheckIcon,
3089
+ {
3090
+ className: "ml-auto size-3.5 shrink-0",
3091
+ "data-testid": "model-picker-effort-check"
3092
+ }
3093
+ ) : null
3094
+ ]
3095
+ },
3096
+ effort
3097
+ );
3098
+ }) })
3099
+ ] });
3100
+ } else {
3101
+ body = /* @__PURE__ */ jsx5("div", { className: "flex flex-col gap-0.5", "data-testid": "model-picker-providers", children: groups.map((group) => /* @__PURE__ */ jsx5(
3102
+ PickerNavRow,
3103
+ {
3104
+ label: group.label,
3105
+ hint: messages.billingHints[group.billingClass],
3106
+ icon: /* @__PURE__ */ jsx5(BillingClassMark, { billingClass: group.billingClass }),
3107
+ testId: `model-picker-rail-${group.billingClass}`,
3108
+ onClick: () => go({ level: "models", rail: group.billingClass, modelId: null }, 1)
3109
+ },
3110
+ group.billingClass
3111
+ )) });
3112
+ }
3113
+ return /* @__PURE__ */ jsxs5("div", { "data-testid": "model-picker-menu", className: "relative overflow-hidden", children: [
3114
+ props.error ? /* @__PURE__ */ jsx5("p", { className: "px-2 py-1 text-xs text-og-status-failed", role: "alert", children: props.error }) : null,
3115
+ /* @__PURE__ */ jsx5(PickerAnimatedPage, { pageKey, direction, children: body })
3116
+ ] });
3117
+ }
3118
+ function ModelPolicyPicker(props) {
3119
+ const [open, setOpen] = useState6(false);
3120
+ const messages = useMemo5(
3121
+ () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),
3122
+ [props.messages]
3123
+ );
3124
+ const rows = effectiveRows(props);
3125
+ const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);
3126
+ const selected = findPickerRow(rows, props.model);
3127
+ if (props.loading) {
3128
+ return /* @__PURE__ */ jsx5(
3129
+ "span",
3130
+ {
3131
+ className: cn(
3132
+ "inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2",
3133
+ props.className
3134
+ ),
3135
+ "aria-label": messages.loading,
3136
+ "data-testid": "model-picker-loading"
3137
+ }
3138
+ );
3139
+ }
3140
+ return /* @__PURE__ */ jsxs5(DropdownMenu.Root, { open, onOpenChange: setOpen, children: [
3141
+ /* @__PURE__ */ jsx5(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs5(
3142
+ "button",
3143
+ {
3144
+ type: "button",
3145
+ disabled: props.disabled,
3146
+ "aria-label": messages.label,
3147
+ className: cn(
3148
+ "inline-flex h-8 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-xs text-og-fg-muted outline-none 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",
3149
+ props.className
3150
+ ),
3151
+ children: [
3152
+ /* @__PURE__ */ jsx5(
3153
+ BillingClassMark,
3154
+ {
3155
+ billingClass: selected?.billingClass ?? (props.model.startsWith("codex/") ? "codex_subscription" : "opengeni_credits"),
3156
+ className: "text-og-fg"
3157
+ }
3158
+ ),
3159
+ /* @__PURE__ */ jsx5("span", { className: "truncate font-medium text-og-fg", children: selected?.label ?? props.model }),
3160
+ /* @__PURE__ */ jsx5("span", { children: labelReasoningEffort(props.effort) }),
3161
+ props.latencyMode === "fast" ? /* @__PURE__ */ jsx5(
3162
+ ZapIcon,
3163
+ {
3164
+ className: "size-3.5 shrink-0 text-og-fg",
3165
+ "aria-label": messages.fast,
3166
+ "data-testid": "model-picker-fast-icon"
3167
+ }
3168
+ ) : null,
3169
+ /* @__PURE__ */ jsx5(ChevronDownIcon3, { className: "size-3 shrink-0" })
3170
+ ]
3171
+ }
3172
+ ) }),
3173
+ /* @__PURE__ */ jsx5(DropdownMenu.Portal, { children: /* @__PURE__ */ jsx5(
3174
+ DropdownMenu.Content,
3175
+ {
3176
+ align: "start",
3177
+ side: props.menuSide ?? "bottom",
3178
+ sideOffset: 8,
3179
+ collisionPadding: 12,
3180
+ onCloseAutoFocus: (event) => event.preventDefault(),
3181
+ className: "z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-72 max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-1.5 text-og-fg shadow-og-lg",
3182
+ children: /* @__PURE__ */ jsx5("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain", children: /* @__PURE__ */ jsx5(ModelPolicyPickerMenu, { ...props, nav, onNavChange: setNav }) })
3183
+ }
3184
+ ) })
3185
+ ] });
3186
+ }
3187
+
2773
3188
  export {
2774
3189
  INITIAL_TRANSCRIPTION_CONTROL_STATE,
2775
3190
  transitionTranscriptionControl,
@@ -2809,6 +3224,13 @@ export {
2809
3224
  SendButton,
2810
3225
  Help,
2811
3226
  Status,
2812
- ComposerTranscriptionControl
3227
+ ComposerTranscriptionControl,
3228
+ defaultModelPolicyPickerMessages,
3229
+ BillingClassMark,
3230
+ PickerNavRow,
3231
+ PickerBackHeader,
3232
+ PickerAnimatedPage,
3233
+ ModelPolicyPickerMenu,
3234
+ ModelPolicyPicker
2813
3235
  };
2814
- //# sourceMappingURL=chunk-N4XEBE23.js.map
3236
+ //# sourceMappingURL=chunk-IP22SLO6.js.map