@opengeni/react 0.45.0 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-L3EWO3UK.js → chunk-3PB7MIT6.js} +243 -22
- package/dist/chunk-3PB7MIT6.js.map +1 -0
- package/dist/{chunk-EHSYZ4KP.js → chunk-FGZUCXZF.js} +2 -2
- package/dist/{chunk-EHSYZ4KP.js.map → chunk-FGZUCXZF.js.map} +1 -1
- package/dist/{chunk-FD72GJMK.js → chunk-LGVMUIRV.js} +11 -11
- package/dist/chunk-LGVMUIRV.js.map +1 -0
- package/dist/composer.js +2 -2
- package/dist/hooks/use-voice-input.d.ts +8 -1
- package/dist/index.js +3 -3
- package/dist/realtime.js +4 -4
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.js +1 -1
- package/package.json +2 -2
- package/src/components/composer.tsx +4 -4
- package/src/components/copy-button.tsx +1 -1
- package/src/components/message-timeline.tsx +5 -5
- package/src/components/model-picker.tsx +1 -1
- package/src/components/model-policy-picker.tsx +1 -1
- package/src/hooks/use-voice-input.ts +320 -19
- package/src/realtime/realtime-control.tsx +3 -3
- package/src/timeline/activity-rail.tsx +1 -1
- package/src/timeline/shared.tsx +3 -3
- package/src/timeline/turn-summary.tsx +4 -4
- package/dist/chunk-FD72GJMK.js.map +0 -1
- package/dist/chunk-L3EWO3UK.js.map +0 -1
package/dist/composer.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
useChatComposer,
|
|
38
38
|
useChatComposerController,
|
|
39
39
|
useVoiceInput
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-3PB7MIT6.js";
|
|
41
41
|
import "./chunk-HFO4ERGQ.js";
|
|
42
42
|
import "./chunk-EB67J347.js";
|
|
43
43
|
import {
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
PickerBackHeader,
|
|
49
49
|
PickerNavRow,
|
|
50
50
|
defaultModelPolicyPickerMessages
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-FGZUCXZF.js";
|
|
52
52
|
import "./chunk-EKZH6IDG.js";
|
|
53
53
|
import "./chunk-23EJ676W.js";
|
|
54
54
|
import "./chunk-U3CSCMWA.js";
|
|
@@ -2,7 +2,7 @@ import type { ClientVoiceInputConfig, OpenGeniClient } from "@opengeni/sdk";
|
|
|
2
2
|
import { type VoiceRecordingStore } from "../voice-recording-store";
|
|
3
3
|
export type VoiceInputStatus = "idle" | "requesting-permission" | "recording" | "saving" | "transcribing" | "recovered" | "transcript-ready" | "error";
|
|
4
4
|
export type UseVoiceInputOptions = {
|
|
5
|
-
client:
|
|
5
|
+
client: VoiceInputClient | null;
|
|
6
6
|
workspaceId: string;
|
|
7
7
|
capability: ClientVoiceInputConfig | null;
|
|
8
8
|
enabled: boolean;
|
|
@@ -16,6 +16,8 @@ export type UseVoiceInputOptions = {
|
|
|
16
16
|
createOwnerId?: (() => string) | undefined;
|
|
17
17
|
now?: (() => Date) | undefined;
|
|
18
18
|
};
|
|
19
|
+
type ResumableVoiceInputClient = Pick<OpenGeniClient, "createTranscriptionRecording" | "getTranscriptionRecording" | "uploadTranscriptionRecordingChunk" | "finalizeTranscriptionRecording" | "processNextTranscriptionRecordingSegment" | "discardTranscriptionRecording">;
|
|
20
|
+
type VoiceInputClient = Pick<OpenGeniClient, "transcribeAudio"> & Partial<ResumableVoiceInputClient>;
|
|
19
21
|
export type UseVoiceInputResult = {
|
|
20
22
|
status: VoiceInputStatus;
|
|
21
23
|
error: string | null;
|
|
@@ -43,4 +45,9 @@ export declare const VOICE_RECORDING_TIMESLICE_MILLISECONDS = 5000;
|
|
|
43
45
|
export declare const VOICE_RECORDING_OWNER_HEARTBEAT_MILLISECONDS = 5000;
|
|
44
46
|
export declare const VOICE_RECORDING_OWNER_STALE_MILLISECONDS = 30000;
|
|
45
47
|
export declare const VOICE_RECORDING_CLIENT_MAX_DURATION_SECONDS = 600;
|
|
48
|
+
export declare const VOICE_RECORDING_RESUMABLE_CLIENT_MAX_DURATION_SECONDS: number;
|
|
49
|
+
export declare const VOICE_RECORDING_RECOVERY_STATUS_POLL_MILLISECONDS = 2000;
|
|
50
|
+
export declare const VOICE_RECORDING_RECOVERY_MAX_MUTATION_DELAY_MILLISECONDS = 30000;
|
|
51
|
+
export declare function transcriptionRecoveryMutationDelayMilliseconds(retryAfterMilliseconds: number | undefined, attempt: number, random?: () => number): number;
|
|
46
52
|
export declare function useVoiceInput({ client, workspaceId, capability, enabled, value, setValue, focusInput, disabled, createRecordingStore, createRecordingId, createOwnerId, now, }: UseVoiceInputOptions): UseVoiceInputResult;
|
|
53
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -111,7 +111,7 @@ import {
|
|
|
111
111
|
useThemeType,
|
|
112
112
|
userMessageLikelyNeedsDisclosure,
|
|
113
113
|
v4aToGitFileDiff
|
|
114
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-LGVMUIRV.js";
|
|
115
115
|
import "./chunk-VCBFUSUZ.js";
|
|
116
116
|
import {
|
|
117
117
|
buildTimeline,
|
|
@@ -173,7 +173,7 @@ import {
|
|
|
173
173
|
useSlashCommands,
|
|
174
174
|
useTranscription,
|
|
175
175
|
useVoiceInput
|
|
176
|
-
} from "./chunk-
|
|
176
|
+
} from "./chunk-3PB7MIT6.js";
|
|
177
177
|
import {
|
|
178
178
|
FILE_ONLY_MESSAGE_TEXT,
|
|
179
179
|
composeSendInput,
|
|
@@ -194,7 +194,7 @@ import {
|
|
|
194
194
|
PickerBackHeader,
|
|
195
195
|
PickerNavRow,
|
|
196
196
|
defaultModelPolicyPickerMessages
|
|
197
|
-
} from "./chunk-
|
|
197
|
+
} from "./chunk-FGZUCXZF.js";
|
|
198
198
|
import {
|
|
199
199
|
useOpenGeni,
|
|
200
200
|
useOpenGeniClient
|
package/dist/realtime.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
PickerAnimatedPage,
|
|
4
4
|
PickerBackHeader,
|
|
5
5
|
PickerNavRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FGZUCXZF.js";
|
|
7
7
|
import {
|
|
8
8
|
useEmbeddedRealtimeSession
|
|
9
9
|
} from "./chunk-EKZH6IDG.js";
|
|
@@ -698,11 +698,11 @@ function RealtimeVoiceControl(props) {
|
|
|
698
698
|
title: `Voice model: ${selectedModel.label}`,
|
|
699
699
|
disabled: props.selectionDisabled,
|
|
700
700
|
className: cn(
|
|
701
|
-
//
|
|
702
|
-
|
|
701
|
+
// Keep the split trigger compact on desktop while preserving a
|
|
702
|
+
// full 44px mobile target independent of pointer-media support.
|
|
703
|
+
"inline-flex h-11 w-11 items-center justify-center rounded-r-og-md outline-none sm:h-8 sm:w-6",
|
|
703
704
|
"transition-[background-color,border-color,color] duration-200 ease-og-out",
|
|
704
705
|
"focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-og-accent/45",
|
|
705
|
-
"pointer-coarse:h-11 pointer-coarse:w-7",
|
|
706
706
|
desktopOnlyModelMenu && "hidden sm:inline-flex",
|
|
707
707
|
voiceChevronTone(status.phase)
|
|
708
708
|
),
|
package/dist/realtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/realtime/realtime-control.tsx","../src/realtime/dropdown-menu.tsx"],"sourcesContent":["import { type EffectiveSessionControl, type SessionEvent, type SessionStatus } from \"@opengeni/sdk\";\nimport {\n hasStoredSessionRealtimeOwnerProof,\n projectSessionRealtimeLifecycle,\n type SessionRealtimeClientLike,\n type SessionRealtimeController,\n type SessionRealtimeControllerSnapshot,\n type CreateSessionRealtimeControllerOptions,\n type SessionRealtimeModel,\n type WorkspaceRealtimeModelCatalogItem,\n} from \"@opengeni/sdk/realtime\";\nimport {\n AudioLinesIcon,\n CheckIcon,\n ChevronDownIcon,\n CircleAlertIcon,\n LoaderCircleIcon,\n MicIcon,\n MicOffIcon,\n RotateCcwIcon,\n SquareIcon,\n Volume2Icon,\n VolumeXIcon,\n} from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport {\n type ReactNode,\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n BillingClassMark,\n PickerAnimatedPage,\n PickerBackHeader,\n PickerNavRow,\n} from \"../components/model-policy-picker\";\nimport type { EmbeddedRealtimeSessionClientLike } from \"../client\";\nimport { cn } from \"../lib/cn\";\nimport type { PickerBillingClass as BillingClass } from \"../model-policy\";\nimport { useEmbeddedRealtimeSession } from \"../session-context\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\";\n\nexport type RealtimeModelOption = {\n id: SessionRealtimeModel;\n label: string;\n provider: \"OpenGeni\" | \"Connected Codex\" | \"Your Gateway\";\n description: string;\n available: boolean;\n unavailableReason: string | null;\n recommended: boolean;\n};\n\nconst CODEX_LIVE_MODEL: RealtimeModelOption = {\n id: \"gpt-live-1-boulder-alpha\",\n label: \"Codex Live\",\n provider: \"Connected Codex\",\n description: \"Deep session integration\",\n available: false,\n unavailableReason: \"Connect Codex to use this voice model\",\n recommended: false,\n};\n\nconst REALTIME_MODEL_PROVIDERS = [\"OpenGeni\", \"Connected Codex\", \"Your Gateway\"] as const;\ntype RealtimeModelProvider = (typeof REALTIME_MODEL_PROVIDERS)[number];\n\nconst REALTIME_PROVIDER_META: Record<\n RealtimeModelProvider,\n { billingClass: BillingClass; hint: string }\n> = {\n OpenGeni: { billingClass: \"opengeni_credits\", hint: \"Will use credits\" },\n \"Connected Codex\": {\n billingClass: \"codex_subscription\",\n hint: \"ChatGPT / Codex plan\",\n },\n \"Your Gateway\": { billingClass: \"byok\", hint: \"Billed to your AI Gateway\" },\n};\nconst REALTIME_MODEL_STORAGE_PREFIX = \"opengeni:realtime-model\";\n\ntype RealtimeControllerClient = EmbeddedRealtimeSessionClientLike & SessionRealtimeClientLike;\n\nexport type SessionRealtimeControllerFactory = (\n options: CreateSessionRealtimeControllerOptions,\n) => SessionRealtimeController;\n\ntype AdmissionInput = {\n sessionStatus: SessionStatus;\n controlState: \"active\" | \"paused\";\n settlement: { state: string } | null;\n codexConnected: boolean;\n lifecycleActive: boolean;\n};\n\nexport function codexRealtimeAdmissionAllowed(input: AdmissionInput): boolean {\n return codexRealtimeAdmissionBlocker(input) === null;\n}\n\nexport function codexRealtimeAdmissionBlocker(input: AdmissionInput): string | null {\n if (input.sessionStatus === \"cancelled\") return \"This session was cancelled.\";\n if (input.controlState !== \"active\") return \"Resume this session before starting voice.\";\n if (input.settlement !== null) return \"Wait for the current session transition to finish.\";\n if (!input.codexConnected) return \"Connect Codex to use this voice model.\";\n if (input.lifecycleActive) return \"Voice is already active for this session.\";\n return null;\n}\n\nexport function useSessionRealtime(options: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n sessionId: string;\n sessionStatus: SessionStatus;\n effectiveControl: EffectiveSessionControl;\n events: SessionEvent[];\n eventsReady: boolean;\n codexConnected: boolean;\n model?: SessionRealtimeModel | undefined;\n modelAvailable?: boolean | undefined;\n modelUnavailableReason?: string | null | undefined;\n /** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */\n controllerFactory?: SessionRealtimeControllerFactory | undefined;\n}) {\n const { client, workspaceId } = useEmbeddedRealtimeSession({\n client: options.client,\n workspaceId: options.workspaceId,\n });\n const model = options.model ?? CODEX_LIVE_MODEL.id;\n const audioRef = useRef<HTMLAudioElement>(null);\n const controllerRef = useRef<SessionRealtimeController | null>(null);\n const controllerModelRef = useRef<SessionRealtimeModel | null>(null);\n const [snapshot, setSnapshot] = useState<SessionRealtimeControllerSnapshot>(() => ({\n status: hasStoredSessionRealtimeOwnerProof({ workspaceId, sessionId: options.sessionId, model })\n ? \"recovering\"\n : \"idle\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: null,\n error: null,\n }));\n const lifecycle = useMemo(\n () => projectSessionRealtimeLifecycle(options.events),\n [options.events],\n );\n const lifecycleRef = useRef(lifecycle);\n const eventsReadyRef = useRef(options.eventsReady);\n lifecycleRef.current = lifecycle;\n eventsReadyRef.current = options.eventsReady;\n const lifecycleActive = lifecycle?.state === \"active\";\n\n useEffect(() => {\n let disposed = false;\n let controller: SessionRealtimeController | null = null;\n let unsubscribe: (() => void) | null = null;\n void import(\"@opengeni/sdk/realtime\")\n .then(({ createSessionRealtimeController }) => {\n if (disposed) return;\n const controllerFactory = options.controllerFactory ?? createSessionRealtimeController;\n controller = controllerFactory({\n client,\n workspaceId,\n sessionId: options.sessionId,\n ...(audioRef.current ? { remoteAudio: audioRef.current } : {}),\n model,\n });\n controllerRef.current = controller;\n controllerModelRef.current = model;\n unsubscribe = controller.subscribe(setSnapshot);\n if (eventsReadyRef.current) {\n void controller.observeLifecycle(lifecycleRef.current).catch(() => undefined);\n }\n })\n .catch((error: unknown) => {\n if (disposed) return;\n setSnapshot({\n status: \"error\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: {\n kind: \"negotiation_failure\",\n message:\n error instanceof Error ? error.message : \"Codex realtime controller failed to load\",\n recoverable: false,\n connectionGeneration: 0,\n attempt: 0,\n },\n error:\n error instanceof Error ? error.message : \"Codex realtime controller failed to load\",\n });\n });\n return () => {\n disposed = true;\n unsubscribe?.();\n controller?.close();\n if (controllerRef.current === controller) {\n controllerRef.current = null;\n controllerModelRef.current = null;\n }\n };\n }, [client, model, options.controllerFactory, options.sessionId, workspaceId]);\n\n useEffect(() => {\n if (!options.eventsReady) return;\n void controllerRef.current?.observeLifecycle(lifecycle).catch(() => undefined);\n }, [lifecycle, options.eventsReady]);\n\n const start = useCallback(async () => {\n const controller = controllerRef.current;\n if (!controller || controllerModelRef.current !== model) {\n throw new Error(\"The selected voice model is still preparing\");\n }\n await controller.start();\n }, [model]);\n const stop = useCallback(async () => {\n await controllerRef.current?.stop();\n }, []);\n const retry = useCallback(async () => {\n await controllerRef.current?.retry();\n }, []);\n const retryAudibleOutput = useCallback(async () => {\n await controllerRef.current?.retryAudibleOutput();\n }, []);\n const setInputMuted = useCallback((muted: boolean) => {\n controllerRef.current?.setInputMuted(muted);\n }, []);\n const setOutputMuted = useCallback((muted: boolean) => {\n controllerRef.current?.setOutputMuted(muted);\n }, []);\n const admissionInput = {\n sessionStatus: options.sessionStatus,\n controlState: options.effectiveControl.state,\n settlement: options.effectiveControl.settlement,\n codexConnected: options.modelAvailable ?? options.codexConnected,\n lifecycleActive,\n } satisfies AdmissionInput;\n const admissionBlocker =\n options.modelAvailable === false\n ? (options.modelUnavailableReason ?? \"This voice model is unavailable.\")\n : codexRealtimeAdmissionBlocker(admissionInput);\n const canStart =\n controllerRef.current !== null &&\n controllerModelRef.current === model &&\n [\"idle\", \"error\"].includes(snapshot.status) &&\n admissionBlocker === null;\n\n return {\n snapshot,\n lifecycleActive,\n canStart,\n admissionBlocker,\n codexConnected: options.codexConnected,\n audioRef,\n start,\n stop,\n retry,\n retryAudibleOutput,\n setInputMuted,\n setOutputMuted,\n };\n}\n\nexport function SessionRealtimeControl(props: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n sessionId: string;\n sessionStatus: SessionStatus;\n effectiveControl: EffectiveSessionControl;\n events: SessionEvent[];\n eventsReady: boolean;\n codexConnected: boolean;\n realtimeAutostartModel?: SessionRealtimeModel | undefined;\n onRealtimeAutostartConsumed?: (() => void) | undefined;\n /** Host hides dictate (and can choreograph layout) while realtime voice is live. */\n onVoiceActiveChange?: ((active: boolean) => void) | undefined;\n /** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */\n controllerFactory?: SessionRealtimeControllerFactory | undefined;\n}) {\n const lifecycle = useMemo(() => projectSessionRealtimeLifecycle(props.events), [props.events]);\n const selection = useRealtimeModelSelection({\n client: props.client,\n workspaceId: props.workspaceId,\n codexConnected: props.codexConnected,\n activeModel: lifecycle?.state === \"active\" ? lifecycle.model : null,\n });\n const selectedModel = selection.selectedModel;\n const realtime = useSessionRealtime({\n ...props,\n model: selectedModel.id,\n modelAvailable: selectedModel.available,\n modelUnavailableReason: selectedModel.unavailableReason,\n });\n const autostartModelRef = useRef(props.realtimeAutostartModel ?? null);\n const autostartStartedRef = useRef(false);\n\n const { models, selectedModel: selectedRealtimeModel, selectModel } = selection;\n const { canStart, start } = realtime;\n const onRealtimeAutostartConsumed = props.onRealtimeAutostartConsumed;\n const onVoiceActiveChange = props.onVoiceActiveChange;\n const voiceActive = realtime.snapshot.mode?.state === \"active\";\n\n useEffect(() => {\n onVoiceActiveChange?.(voiceActive);\n }, [onVoiceActiveChange, voiceActive]);\n\n useEffect(() => {\n const pending = autostartModelRef.current;\n if (!pending || autostartStartedRef.current || lifecycle?.state === \"active\") return;\n const available = models.some((model) => model.id === pending && model.available);\n if (!available) return;\n if (selectedRealtimeModel.id !== pending) {\n selectModel(pending);\n return;\n }\n if (!canStart) return;\n autostartStartedRef.current = true;\n void start()\n .then(() => {\n autostartModelRef.current = null;\n onRealtimeAutostartConsumed?.();\n })\n .catch(() => {\n autostartStartedRef.current = false;\n });\n }, [\n canStart,\n lifecycle?.state,\n models,\n props.sessionId,\n props.workspaceId,\n onRealtimeAutostartConsumed,\n selectModel,\n selectedRealtimeModel.id,\n start,\n ]);\n\n return (\n <RealtimeVoiceControl\n snapshot={realtime.snapshot}\n canStart={realtime.canStart}\n admissionBlocker={realtime.admissionBlocker}\n modelAvailable={selectedModel.available}\n menuSide=\"top\"\n audioRef={realtime.audioRef}\n selectedModel={selectedModel}\n models={selection.models}\n onSelectModel={realtime.lifecycleActive ? undefined : selection.selectModel}\n onStart={realtime.start}\n onStop={realtime.stop}\n onRetry={realtime.retry}\n onRetryAudibleOutput={realtime.retryAudibleOutput}\n onSetInputMuted={realtime.setInputMuted}\n onSetOutputMuted={realtime.setOutputMuted}\n />\n );\n}\n\nexport function useRealtimeModelSelection(options: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n codexConnected: boolean;\n activeModel?: SessionRealtimeModel | null | undefined;\n}) {\n const { client, workspaceId } = useEmbeddedRealtimeSession({\n client: options.client,\n workspaceId: options.workspaceId,\n });\n const activeModel = options.activeModel;\n const [catalog, setCatalog] = useState<RealtimeModelOption[]>(() => [\n {\n ...CODEX_LIVE_MODEL,\n available: options.codexConnected,\n unavailableReason: options.codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,\n },\n ]);\n const [selectedModelId, setSelectedModelId] = useState<SessionRealtimeModel>(\n () => readRealtimeModelPreference(workspaceId) ?? CODEX_LIVE_MODEL.id,\n );\n\n useEffect(() => {\n let disposed = false;\n const load = client.getWorkspaceRealtimeModelCatalog;\n if (!load) return;\n void load\n .call(client, workspaceId)\n .then((response) => {\n if (disposed) return;\n const models = response.models.map(toRealtimeModelOption);\n setCatalog(models);\n setSelectedModelId((current) => {\n if (models.some((model) => model.id === current && model.available)) return current;\n return models.find((model) => model.available)?.id ?? CODEX_LIVE_MODEL.id;\n });\n })\n .catch(() => undefined);\n return () => {\n disposed = true;\n };\n }, [client, workspaceId]);\n\n useEffect(() => {\n if (activeModel) setSelectedModelId(activeModel);\n }, [activeModel]);\n\n const selectedModel =\n catalog.find((model) => model.id === selectedModelId) ??\n ({\n ...CODEX_LIVE_MODEL,\n available: options.codexConnected,\n unavailableReason: options.codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,\n } satisfies RealtimeModelOption);\n const selectModel = useCallback(\n (value: string) => {\n const model = catalog.find((candidate) => candidate.id === value);\n if (!model || !model.available || activeModel) return;\n setSelectedModelId(model.id);\n writeRealtimeModelPreference(workspaceId, model.id);\n },\n [activeModel, catalog, workspaceId],\n );\n\n return { models: catalog, selectedModel, selectModel };\n}\n\nconst IDLE_REALTIME_SNAPSHOT: SessionRealtimeControllerSnapshot = {\n status: \"idle\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: null,\n error: null,\n};\n\nexport function NewSessionRealtimeControl(props: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n codexConnected: boolean;\n disabled?: boolean | undefined;\n disabledReason?: string | null | undefined;\n onStart: (model: SessionRealtimeModel) => Promise<boolean>;\n /** Parent-owned selection (e.g. shared with the mobile “+” voice-model panel). */\n models?: readonly RealtimeModelOption[] | undefined;\n selectedModel?: RealtimeModelOption | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n /**\n * `split` attaches the model chevron at every breakpoint (public default).\n * `split-desktop` hides it below `sm` when the host owns mobile selection (e.g. “+”).\n * `none` never attaches a model menu to the bar button.\n */\n modelMenu?: \"split\" | \"split-desktop\" | \"none\" | undefined;\n}) {\n const internalSelection = useRealtimeModelSelection({\n client: props.client,\n workspaceId: props.workspaceId,\n codexConnected: props.codexConnected,\n });\n const selection =\n props.models && props.selectedModel && props.onSelectModel\n ? {\n models: props.models,\n selectedModel: props.selectedModel,\n selectModel: props.onSelectModel,\n }\n : internalSelection;\n const audioRef = useRef<HTMLAudioElement>(null);\n const [starting, setStarting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const snapshot = useMemo<SessionRealtimeControllerSnapshot>(\n () =>\n starting\n ? { ...IDLE_REALTIME_SNAPSHOT, status: \"starting\" }\n : error\n ? { ...IDLE_REALTIME_SNAPSHOT, status: \"error\", error }\n : IDLE_REALTIME_SNAPSHOT,\n [error, starting],\n );\n const canStart = selection.selectedModel.available && props.disabled !== true && !starting;\n const onStart = props.onStart;\n const selectedModelId = selection.selectedModel.id;\n const start = useCallback(async () => {\n if (!canStart) return;\n setStarting(true);\n setError(null);\n try {\n const started = await onStart(selectedModelId);\n if (!started) setError(\"The voice session could not be created.\");\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n setStarting(false);\n }\n }, [canStart, onStart, selectedModelId]);\n\n return (\n <RealtimeVoiceControl\n snapshot={snapshot}\n canStart={canStart}\n admissionBlocker={selection.selectedModel.unavailableReason ?? props.disabledReason ?? null}\n modelAvailable={selection.selectedModel.available}\n selectionDisabled={starting}\n menuSide=\"bottom\"\n modelMenu={props.modelMenu ?? \"split\"}\n showDiagnostics={false}\n audioRef={audioRef}\n selectedModel={selection.selectedModel}\n models={selection.models}\n onSelectModel={selection.selectModel}\n onStart={start}\n onStop={async () => undefined}\n onRetry={start}\n onRetryAudibleOutput={async () => undefined}\n onSetInputMuted={() => undefined}\n onSetOutputMuted={() => undefined}\n />\n );\n}\n\nexport function RealtimeVoiceControl(props: {\n snapshot: SessionRealtimeControllerSnapshot;\n canStart: boolean;\n admissionBlocker?: string | null | undefined;\n modelAvailable: boolean;\n selectionDisabled?: boolean | undefined;\n /** Prefer `bottom` on home/new-session; `top` for the docked session composer. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /**\n * `split` = start + model chevron at every breakpoint (public SDK default).\n * `split-desktop` = chevron from `sm` up; below that the host owns selection.\n * `none` = start button only.\n */\n modelMenu?: \"split\" | \"split-desktop\" | \"none\" | undefined;\n showDiagnostics?: boolean;\n /** Extra classes on the in-bar mute cluster (e.g. `max-sm:hidden` when mutes live in “+”). */\n muteControlsClassName?: string | undefined;\n audioRef: RefObject<HTMLAudioElement | null>;\n selectedModel?: RealtimeModelOption | undefined;\n models?: readonly RealtimeModelOption[] | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n onStart: () => Promise<void>;\n onStop: () => Promise<void>;\n onRetry: () => Promise<void>;\n onRetryAudibleOutput: () => Promise<void>;\n onSetInputMuted: (muted: boolean) => void;\n onSetOutputMuted: (muted: boolean) => void;\n}) {\n const reduceMotion = useReducedMotion();\n const selectedModel = props.selectedModel ?? {\n ...CODEX_LIVE_MODEL,\n available: props.modelAvailable,\n unavailableReason: props.modelAvailable ? null : CODEX_LIVE_MODEL.unavailableReason,\n };\n const models = props.models ?? [selectedModel];\n const status = statusContent(\n props.snapshot,\n props.modelAvailable,\n props.canStart,\n props.admissionBlocker ?? null,\n selectedModel.label,\n );\n const modeOwned = props.snapshot.mode?.state === \"active\";\n const retryConnection =\n modeOwned &&\n props.snapshot.status !== \"stopping\" &&\n props.snapshot.status !== \"active\" &&\n props.snapshot.diagnostic?.recoverable === true;\n const audioBlocked = props.snapshot.audibleOutput === \"blocked\" && !props.snapshot.outputMuted;\n const mainDisabled =\n props.snapshot.status === \"stopping\" ||\n props.snapshot.status === \"lost_owner\" ||\n (!modeOwned && !props.canStart) ||\n (props.snapshot.status === \"starting\" && !modeOwned);\n const mainLabel = audioBlocked\n ? \"Resume voice audio\"\n : retryConnection\n ? \"Retry voice connection\"\n : modeOwned\n ? \"End voice conversation\"\n : `Start voice with ${selectedModel.label}`;\n const runMainAction = audioBlocked\n ? props.onRetryAudibleOutput\n : retryConnection\n ? props.onRetry\n : modeOwned\n ? props.onStop\n : props.onStart;\n const diagnosticsVisible = props.showDiagnostics ?? import.meta.env.DEV;\n const modelMenu = props.modelMenu ?? \"split\";\n const showAttachedModelMenu = modelMenu === \"split\" || modelMenu === \"split-desktop\";\n const desktopOnlyModelMenu = modelMenu === \"split-desktop\";\n const [pickerOpen, setPickerOpen] = useState(false);\n const [pickerProvider, setPickerProvider] = useState<RealtimeModelProvider | null>(\n selectedModel.provider,\n );\n const [pickerDirection, setPickerDirection] = useState<1 | -1>(1);\n\n useEffect(() => {\n if (!pickerOpen) setPickerProvider(selectedModel.provider);\n }, [pickerOpen, selectedModel.provider]);\n\n return (\n <div\n role=\"group\"\n aria-label=\"Realtime voice\"\n data-picker-side={props.menuSide ?? \"top\"}\n className=\"inline-flex shrink-0 items-center\"\n >\n <audio ref={props.audioRef} autoPlay className=\"hidden\" aria-hidden=\"true\" />\n <RealtimeComposerGlow phase={status.phase} reduceMotion={reduceMotion === true} />\n <AnimatePresence initial={false}>\n {modeOwned ? (\n <motion.div\n key=\"realtime-mute-controls\"\n data-testid=\"realtime-mute-controls\"\n // After dictate collapses and the model slides left, bloom mutes\n // out from the primary voice control. Exit is immediate (no delay).\n initial={reduceMotion ? false : { opacity: 0, width: 0 }}\n animate={{\n opacity: 1,\n width: \"auto\",\n transition: reduceMotion\n ? { duration: 0 }\n : { delay: 0.3, duration: 0.48, ease: [0.22, 1, 0.36, 1] },\n }}\n {...(reduceMotion\n ? {}\n : {\n exit: {\n opacity: 0,\n width: 0,\n transition: { duration: 0.28, ease: [0.4, 0, 1, 1] },\n },\n })}\n className={cn(\n \"inline-flex shrink-0 items-center overflow-hidden\",\n props.muteControlsClassName,\n )}\n >\n <motion.div\n className=\"inline-flex items-center gap-0.5 pr-1\"\n initial={reduceMotion ? false : { x: 18, opacity: 0 }}\n animate={{\n x: 0,\n opacity: 1,\n transition: reduceMotion\n ? { duration: 0 }\n : { delay: 0.32, type: \"spring\", stiffness: 320, damping: 30, mass: 0.8 },\n }}\n {...(reduceMotion\n ? {}\n : {\n exit: {\n x: 14,\n opacity: 0,\n transition: { duration: 0.22, ease: [0.4, 0, 1, 1] },\n },\n })}\n >\n <RealtimeMuteButton\n muted={props.snapshot.inputMuted}\n kind=\"microphone\"\n reduceMotion={reduceMotion === true}\n enterDelay={reduceMotion ? 0 : 0.34}\n onToggle={() => props.onSetInputMuted(!props.snapshot.inputMuted)}\n />\n <RealtimeMuteButton\n muted={props.snapshot.outputMuted}\n kind=\"output\"\n reduceMotion={reduceMotion === true}\n enterDelay={reduceMotion ? 0 : 0.4}\n onToggle={() => props.onSetOutputMuted(!props.snapshot.outputMuted)}\n />\n </motion.div>\n </motion.div>\n ) : null}\n </AnimatePresence>\n <div className=\"inline-flex shrink-0 items-center\">\n <motion.button\n type=\"button\"\n data-testid=\"realtime-primary-action\"\n data-phase={status.phase}\n aria-label={mainLabel}\n aria-pressed={modeOwned}\n title={mainLabel}\n disabled={mainDisabled}\n {...(reduceMotion ? {} : { whileTap: { scale: 0.92 } })}\n transition={{ type: \"spring\", stiffness: 520, damping: 30 }}\n onClick={() => void runMainAction().catch(() => undefined)}\n className={cn(\n // Match transcription mic: ghost icon when idle; filled only when live.\n // Public contract: coarse pointers keep a 44px target; split menu uses\n // left-only rounding at `sm+` where the chevron attaches.\n \"relative inline-flex size-8 items-center justify-center outline-none\",\n \"rounded-og-md transition-[background-color,border-color,color,box-shadow] duration-200 ease-og-out\",\n \"focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-og-accent/45\",\n \"disabled:cursor-not-allowed disabled:opacity-45 pointer-coarse:size-11\",\n showAttachedModelMenu &&\n (desktopOnlyModelMenu\n ? \"sm:rounded-l-og-md sm:rounded-r-none\"\n : \"rounded-l-og-md rounded-r-none\"),\n voiceButtonTone(status.phase),\n )}\n >\n <RealtimeActionGlyph\n phase={status.phase}\n audioBlocked={audioBlocked}\n reduceMotion={reduceMotion === true}\n />\n </motion.button>\n\n {showAttachedModelMenu ? (\n <DropdownMenu open={pickerOpen} onOpenChange={setPickerOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Choose voice model and options\"\n title={`Voice model: ${selectedModel.label}`}\n disabled={props.selectionDisabled}\n className={cn(\n // ≥24px wide so WCAG 2.2 target-size passes when the chevron is shown.\n \"inline-flex h-8 w-6 items-center justify-center rounded-r-og-md outline-none\",\n \"transition-[background-color,border-color,color] duration-200 ease-og-out\",\n \"focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-og-accent/45\",\n \"pointer-coarse:h-11 pointer-coarse:w-7\",\n desktopOnlyModelMenu && \"hidden sm:inline-flex\",\n voiceChevronTone(status.phase),\n )}\n >\n <ChevronDownIcon className=\"size-3\" aria-hidden />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n side={props.menuSide ?? \"top\"}\n align=\"end\"\n sideOffset={8}\n collisionPadding={12}\n className=\"flex w-72 max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] flex-col overflow-hidden rounded-xl border-border bg-surface p-1.5 shadow-xl\"\n onCloseAutoFocus={(event) => event.preventDefault()}\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <RealtimeModelPickerMenu\n models={models}\n selectedModel={selectedModel}\n provider={pickerProvider}\n direction={pickerDirection}\n disabled={\n props.selectionDisabled === true || props.snapshot.mode?.state === \"active\"\n }\n onProviderChange={(provider, direction) => {\n setPickerDirection(direction);\n setPickerProvider(provider);\n }}\n onSelect={(modelId) => {\n props.onSelectModel?.(modelId);\n setPickerOpen(false);\n }}\n />\n </div>\n\n {status.phase !== \"idle\" ? (\n <>\n <DropdownMenuSeparator className=\"mx-1 bg-og-border\" />\n <div className=\"px-2.5 py-2\" role=\"status\" aria-live=\"polite\">\n <div className=\"flex items-center gap-2 text-og-sm font-medium text-og-fg\">\n <RealtimeStatusDot\n phase={status.phase}\n reduceMotion={reduceMotion === true}\n />\n {status.label}\n </div>\n <p className=\"mt-1 text-og-xs leading-5 text-og-fg-subtle\">{status.detail}</p>\n </div>\n </>\n ) : null}\n\n {audioBlocked ? (\n <DropdownMenuItem\n className=\"rounded-og-md\"\n onSelect={() => void props.onRetryAudibleOutput().catch(() => undefined)}\n >\n <Volume2Icon />\n Resume audio\n </DropdownMenuItem>\n ) : null}\n {retryConnection ? (\n <DropdownMenuItem\n className=\"rounded-og-md\"\n onSelect={() => void props.onRetry().catch(() => undefined)}\n >\n <RotateCcwIcon />\n Retry connection\n </DropdownMenuItem>\n ) : null}\n {modeOwned && props.snapshot.status !== \"lost_owner\" ? (\n <DropdownMenuItem\n variant=\"destructive\"\n className=\"rounded-og-md\"\n disabled={props.snapshot.status === \"stopping\"}\n onSelect={() => void props.onStop().catch(() => undefined)}\n >\n <SquareIcon />\n End voice conversation\n </DropdownMenuItem>\n ) : null}\n\n {diagnosticsVisible ? <RealtimeDiagnosticsMenu snapshot={props.snapshot} /> : null}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {status.label}. {status.detail}\n </span>\n {props.snapshot.error ? (\n <span className=\"sr-only\" role=\"alert\">\n {props.snapshot.error}\n </span>\n ) : null}\n </div>\n );\n}\n\n/** Drill-in body for the mobile composer “+” menu — same catalog as the desktop chevron. */\nexport function RealtimeVoiceModelPanel(props: {\n models: readonly RealtimeModelOption[];\n selectedModel: RealtimeModelOption;\n disabled?: boolean | undefined;\n leading?: ReactNode | undefined;\n onSelect: (modelId: SessionRealtimeModel) => void;\n}) {\n const [provider, setProvider] = useState<RealtimeModelProvider | null>(null);\n const [direction, setDirection] = useState<1 | -1>(1);\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\" data-testid=\"realtime-voice-model-panel\">\n <div className=\"flex shrink-0 items-start gap-1 px-2 pt-1 pb-1.5\">\n {props.leading}\n <div className=\"min-w-0\">\n <div className=\"text-og-menu font-medium text-og-fg\">Voice model</div>\n <p className=\"mt-0.5 text-og-control text-og-fg-subtle\">\n Used when you start a voice conversation.\n </p>\n </div>\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-1.5 pb-1.5\">\n <RealtimeModelPickerMenu\n models={props.models}\n selectedModel={props.selectedModel}\n provider={provider}\n direction={direction}\n disabled={props.disabled === true}\n onProviderChange={(next, nextDirection) => {\n setDirection(nextDirection);\n setProvider(next);\n }}\n onSelect={props.onSelect}\n />\n </div>\n </div>\n );\n}\n\nfunction RealtimeMuteButton(props: {\n muted: boolean;\n kind: \"microphone\" | \"output\";\n reduceMotion: boolean;\n enterDelay?: number | undefined;\n onToggle(): void;\n}) {\n const microphone = props.kind === \"microphone\";\n const label = microphone\n ? props.muted\n ? \"Unmute microphone\"\n : \"Mute microphone\"\n : props.muted\n ? \"Unmute voice audio\"\n : \"Mute voice audio\";\n const Icon = microphone\n ? props.muted\n ? MicOffIcon\n : MicIcon\n : props.muted\n ? VolumeXIcon\n : Volume2Icon;\n\n return (\n <motion.button\n type=\"button\"\n aria-label={label}\n aria-pressed={props.muted}\n title={label}\n initial={props.reduceMotion ? false : { scale: 0.72, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n transition={{\n delay: props.enterDelay ?? 0,\n type: \"spring\",\n stiffness: 420,\n damping: 26,\n mass: 0.7,\n }}\n {...(props.reduceMotion ? {} : { whileTap: { scale: 0.92 } })}\n onClick={props.onToggle}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-og-md outline-none\",\n \"transition-[background-color,border-color,color] duration-150 ease-og-out\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent/45 pointer-coarse:size-11\",\n props.muted\n ? \"border border-og-accent/35 bg-og-accent-soft text-og-accent\"\n : \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\",\n )}\n >\n <Icon className=\"size-3.5\" aria-hidden />\n </motion.button>\n );\n}\n\nexport function RealtimeModelPickerMenu(props: {\n models: readonly RealtimeModelOption[];\n selectedModel: RealtimeModelOption;\n provider: RealtimeModelProvider | null;\n direction: 1 | -1;\n disabled: boolean;\n onProviderChange: (provider: RealtimeModelProvider | null, direction: 1 | -1) => void;\n onSelect: (modelId: SessionRealtimeModel) => void;\n}) {\n const providers = REALTIME_MODEL_PROVIDERS.filter((provider) =>\n props.models.some((model) => model.provider === provider),\n );\n const pageKey = props.provider ? `voice-models:${props.provider}` : \"voice-providers\";\n const body = props.provider ? (\n <div data-testid=\"realtime-model-picker-models\">\n <PickerBackHeader\n label={props.provider}\n icon={\n <BillingClassMark\n billingClass={REALTIME_PROVIDER_META[props.provider].billingClass}\n aria-label=\"\"\n />\n }\n onBack={() => props.onProviderChange(null, -1)}\n />\n <div className=\"flex flex-col gap-0.5\">\n {props.models\n .filter((model) => model.provider === props.provider)\n .map((model) => {\n const selected = model.id === props.selectedModel.id;\n return (\n <PickerNavRow\n key={model.id}\n label={model.label}\n hint={\n model.unavailableReason ??\n (model.recommended ? `Recommended · ${model.description}` : model.description)\n }\n disabled={props.disabled || !model.available}\n title={model.unavailableReason ?? model.description}\n active={selected}\n showChevron={false}\n trailing={selected ? <CheckIcon className=\"size-3.5\" aria-hidden /> : undefined}\n testId={`realtime-model-choice-${model.id}`}\n onClick={() => props.onSelect(model.id)}\n />\n );\n })}\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"realtime-model-picker-providers\">\n {providers.map((provider) => {\n const meta = REALTIME_PROVIDER_META[provider];\n return (\n <PickerNavRow\n key={provider}\n label={provider}\n hint={meta.hint}\n icon={<BillingClassMark billingClass={meta.billingClass} aria-label=\"\" />}\n active={props.selectedModel.provider === provider}\n testId={`realtime-model-provider-${meta.billingClass}`}\n onClick={() => props.onProviderChange(provider, 1)}\n />\n );\n })}\n </div>\n );\n\n return (\n <div className=\"relative overflow-hidden\" data-testid=\"realtime-model-picker-menu\">\n <PickerAnimatedPage pageKey={pageKey} direction={props.direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\nfunction RealtimeDiagnosticsMenu({ snapshot }: { snapshot: SessionRealtimeControllerSnapshot }) {\n const bridge = snapshot.bridge;\n const rows = [\n [\"controller\", snapshot.status],\n [\"microphone\", snapshot.microphone],\n [\"microphone muted\", String(snapshot.inputMuted)],\n [\"audio\", snapshot.audibleOutput],\n [\"audio muted\", String(snapshot.outputMuted)],\n [\"generation\", String(snapshot.connectionGeneration)],\n [\"reconnect attempt\", String(snapshot.reconnectAttempt)],\n [\"mode\", snapshot.mode?.state ?? \"none\"],\n [\"mode version\", snapshot.mode ? String(snapshot.mode.version) : \"—\"],\n [\"connection epoch\", snapshot.mode ? String(snapshot.mode.connectionEpoch) : \"—\"],\n [\"provider started\", bridge ? String(bridge.providerStarted) : \"false\"],\n [\"provider speaking\", bridge ? String(bridge.speaking) : \"false\"],\n [\"delegation\", bridge?.activeDelegationId ? \"active\" : \"none\"],\n [\"pending durable events\", bridge ? String(bridge.pendingInbound) : \"0\"],\n [\"pending bytes\", bridge ? String(bridge.pendingInboundBytes) : \"0\"],\n [\"client ack\", bridge?.clientAckThroughSequence?.toString() ?? \"—\"],\n [\"ignored provider events\", bridge ? String(bridge.ignoredEventCount) : \"0\"],\n [\"last ignored event\", bridge?.lastIgnoredEventType ?? \"none\"],\n [\"bridge error\", bridge?.lastError ?? \"none\"],\n [\"diagnostic\", snapshot.diagnostic?.kind ?? \"none\"],\n ] as const;\n\n return (\n <>\n <DropdownMenuSeparator className=\"mx-1 bg-og-border\" />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className=\"rounded-og-md text-og-fg-muted\">\n Realtime diagnostics\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-80 w-72 overflow-y-auto rounded-og-lg border-og-border bg-og-surface-1 p-1.5 text-og-fg shadow-og-lg\">\n {rows.map(([label, value]) => (\n <DropdownMenuItem\n key={label}\n disabled\n className=\"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-3 rounded-og-md font-mono text-[11px] opacity-100\"\n >\n <span className=\"truncate text-og-fg-subtle\">{label}</span>\n <span className=\"truncate text-right text-og-fg\">{value}</span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n </>\n );\n}\n\ntype RealtimeVisualPhase =\n | \"idle\"\n | \"connecting\"\n | \"listening\"\n | \"speaking\"\n | \"reconnecting\"\n | \"blocked\"\n | \"unavailable\"\n | \"error\"\n | \"elsewhere\"\n | \"stopping\";\n\nfunction statusContent(\n snapshot: SessionRealtimeControllerSnapshot,\n modelAvailable: boolean,\n canStart: boolean,\n admissionBlocker: string | null,\n modelLabel: string,\n): { phase: RealtimeVisualPhase; label: string; detail: string } {\n if (snapshot.audibleOutput === \"blocked\" && !snapshot.outputMuted) {\n return {\n phase: \"blocked\",\n label: \"Audio output blocked\",\n detail: \"Resume audio to hear the existing voice connection.\",\n };\n }\n switch (snapshot.status) {\n case \"starting\":\n return {\n phase: \"connecting\",\n label: \"Connecting…\",\n detail: \"Preparing your microphone and live voice.\",\n };\n case \"active\":\n return snapshot.bridge?.speaking\n ? {\n phase: \"speaking\",\n label: \"Speaking\",\n detail: \"Interrupt naturally whenever you want.\",\n }\n : {\n phase: \"listening\",\n label: \"Listening\",\n detail: \"Voice and typed messages share this session.\",\n };\n case \"stopping\":\n return {\n phase: \"stopping\",\n label: \"Ending voice…\",\n detail: \"Saving the remaining conversation context.\",\n };\n case \"recovering\":\n return {\n phase: \"reconnecting\",\n label: \"Reconnecting…\",\n detail: snapshot.error ?? \"Keeping this voice conversation connected.\",\n };\n case \"lost_owner\":\n return {\n phase: \"elsewhere\",\n label: \"Voice active elsewhere\",\n detail: \"Return to the browser that started it, or wait for that connection to expire.\",\n };\n case \"error\":\n return {\n phase: \"error\",\n label: \"Voice unavailable\",\n detail: snapshot.error ?? \"The connection could not be started.\",\n };\n case \"idle\":\n if (!modelAvailable) {\n return {\n phase: \"unavailable\",\n label: \"Voice model unavailable\",\n detail: admissionBlocker ?? \"Choose another voice model.\",\n };\n }\n if (!canStart && admissionBlocker) {\n return { phase: \"unavailable\", label: \"Voice unavailable\", detail: admissionBlocker };\n }\n return {\n phase: \"idle\",\n label: \"Start voice\",\n detail: `Talk live with ${modelLabel}.`,\n };\n }\n}\n\nfunction RealtimeComposerGlow(props: { phase: RealtimeVisualPhase; reduceMotion: boolean }) {\n const visible = [\n \"connecting\",\n \"listening\",\n \"speaking\",\n \"reconnecting\",\n \"blocked\",\n \"error\",\n \"stopping\",\n ].includes(props.phase);\n return (\n <AnimatePresence initial={false}>\n {visible ? (\n <motion.span\n aria-hidden\n key={props.phase}\n initial={{ opacity: 0 }}\n animate={\n props.reduceMotion\n ? { opacity: 0.48 }\n : props.phase === \"speaking\"\n ? { opacity: [0.42, 0.82, 0.52] }\n : props.phase === \"listening\"\n ? { opacity: [0.34, 0.62, 0.42] }\n : { opacity: [0.28, 0.58, 0.28] }\n }\n exit={{ opacity: 0 }}\n transition={\n props.reduceMotion\n ? { duration: 0.12 }\n : {\n duration: props.phase === \"speaking\" ? 1.15 : 2.1,\n ease: \"easeInOut\",\n repeat: Number.POSITIVE_INFINITY,\n }\n }\n className={cn(\n \"pointer-events-none absolute -inset-px z-[1] rounded-og-lg ring-1 ring-inset shadow-og-glow\",\n props.phase === \"speaking\"\n ? \"ring-og-status-running/50\"\n : props.phase === \"blocked\" || props.phase === \"error\"\n ? \"ring-og-status-waiting/45\"\n : \"ring-og-accent/45\",\n )}\n />\n ) : null}\n </AnimatePresence>\n );\n}\n\nfunction RealtimeActionGlyph(props: {\n phase: RealtimeVisualPhase;\n audioBlocked: boolean;\n reduceMotion: boolean;\n}) {\n if ([\"connecting\", \"reconnecting\", \"stopping\"].includes(props.phase)) {\n return <LoaderCircleIcon className=\"size-3.5 animate-og-spin\" aria-hidden />;\n }\n if (props.phase === \"blocked\") {\n return props.audioBlocked ? (\n <Volume2Icon className=\"size-3.5\" aria-hidden />\n ) : (\n <CircleAlertIcon className=\"size-3.5\" aria-hidden />\n );\n }\n if (props.phase === \"error\" || props.phase === \"elsewhere\" || props.phase === \"unavailable\") {\n return <CircleAlertIcon className=\"size-3.5\" aria-hidden />;\n }\n if (props.phase === \"listening\" || props.phase === \"speaking\") {\n return (\n <span className=\"relative inline-flex size-4 items-center justify-center\">\n {!props.reduceMotion ? (\n <motion.span\n aria-hidden\n className=\"absolute inset-0 rounded-full border border-current\"\n animate={{ opacity: [0.65, 0], scale: [0.65, 1.45] }}\n transition={{\n duration: props.phase === \"speaking\" ? 0.9 : 1.5,\n ease: \"easeOut\",\n repeat: Number.POSITIVE_INFINITY,\n }}\n />\n ) : null}\n <SquareIcon className=\"relative size-2.5 fill-current\" aria-hidden />\n </span>\n );\n }\n return <AudioLinesIcon className=\"size-4\" aria-hidden />;\n}\n\nfunction RealtimeStatusDot(props: { phase: RealtimeVisualPhase; reduceMotion: boolean }) {\n const live = props.phase === \"listening\" || props.phase === \"speaking\";\n return (\n <span className=\"relative inline-flex size-2 shrink-0\" aria-hidden>\n {live && !props.reduceMotion ? (\n <motion.span\n className=\"absolute inset-0 rounded-full bg-og-status-running\"\n animate={{ opacity: [0.45, 0], scale: [1, 2.2] }}\n transition={{ duration: 1.4, ease: \"easeOut\", repeat: Number.POSITIVE_INFINITY }}\n />\n ) : null}\n <span\n className={cn(\n \"relative size-2 rounded-full\",\n live\n ? \"bg-og-status-running\"\n : props.phase === \"blocked\" || props.phase === \"error\"\n ? \"bg-og-status-waiting\"\n : props.phase === \"elsewhere\" || props.phase === \"unavailable\"\n ? \"bg-og-fg-subtle\"\n : \"bg-og-accent\",\n )}\n />\n </span>\n );\n}\n\nfunction voiceButtonTone(phase: RealtimeVisualPhase): string {\n if (phase === \"listening\" || phase === \"speaking\") {\n return \"border border-og-accent/45 bg-og-accent text-og-accent-fg shadow-og-sm hover:bg-og-accent-strong\";\n }\n if (phase === \"blocked\" || phase === \"error\") {\n return \"border border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting hover:bg-og-status-waiting/15\";\n }\n if (phase === \"connecting\" || phase === \"reconnecting\" || phase === \"stopping\") {\n return \"border border-og-accent/35 bg-og-accent-soft text-og-accent\";\n }\n // Idle: same ghost treatment as the composer transcription mic — no box.\n return \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\";\n}\n\nfunction voiceChevronTone(phase: RealtimeVisualPhase): string {\n if (phase === \"listening\" || phase === \"speaking\") {\n return \"border border-l-0 border-og-accent/45 bg-og-accent text-og-accent-fg hover:bg-og-accent-strong\";\n }\n if (phase === \"blocked\" || phase === \"error\") {\n return \"border border-l-0 border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting hover:bg-og-status-waiting/15\";\n }\n if (phase === \"connecting\" || phase === \"reconnecting\" || phase === \"stopping\") {\n return \"border border-l-0 border-og-accent/35 bg-og-accent-soft text-og-accent\";\n }\n return \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\";\n}\n\nfunction toRealtimeModelOption(model: WorkspaceRealtimeModelCatalogItem): RealtimeModelOption {\n return { ...model };\n}\n\nfunction readRealtimeModelPreference(workspaceId: string): SessionRealtimeModel | null {\n if (typeof localStorage === \"undefined\") return null;\n try {\n const value = localStorage.getItem(`${REALTIME_MODEL_STORAGE_PREFIX}:${workspaceId}`);\n return isRealtimeModel(value) ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction writeRealtimeModelPreference(workspaceId: string, model: SessionRealtimeModel): void {\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.setItem(`${REALTIME_MODEL_STORAGE_PREFIX}:${workspaceId}`, model);\n } catch {\n // Voice selection still works when browser storage is unavailable.\n }\n}\n\nfunction isRealtimeModel(value: string | null): value is SessionRealtimeModel {\n return (\n value === \"gpt-live-1-boulder-alpha\" ||\n value === \"opengeni-gateway/openai/gpt-realtime-2.1\" ||\n value === \"opengeni-gateway/openai/gpt-realtime-mini\" ||\n value === \"opengeni-gateway/xai/grok-voice-think-fast-2.0\" ||\n value === \"workspace-gateway/openai/gpt-realtime-2.1\" ||\n value === \"workspace-gateway/openai/gpt-realtime-mini\" ||\n value === \"workspace-gateway/xai/grok-voice-think-fast-2.0\"\n );\n}\n\n/** @deprecated Use the provider-neutral realtime names. */\nexport const useSessionCodexRealtime = useSessionRealtime;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const useWorkspaceRealtimeModelSelection = useRealtimeModelSelection;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const SessionCodexRealtimeControl = SessionRealtimeControl;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const CodexRealtimeControl = RealtimeVoiceControl;\n","\"use client\";\n\nimport { ChevronRightIcon } from \"lucide-react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport {\n createContext,\n useContext,\n useRef,\n type ComponentProps,\n type Ref,\n type RefObject,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\n\nconst DropdownMenuSourceContext = createContext<RefObject<HTMLElement | null> | null>(null);\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === \"function\") ref(value);\n else if (ref) ref.current = value;\n}\n\nfunction DropdownMenu(props: ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n const sourceRef = useRef<HTMLElement | null>(null);\n return (\n <DropdownMenuSourceContext.Provider value={sourceRef}>\n <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />\n </DropdownMenuSourceContext.Provider>\n );\n}\n\nfunction DropdownMenuTrigger({\n ref,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n ref={(node) => {\n if (sourceRef) sourceRef.current = node;\n assignRef(ref, node);\n }}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n style,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n const fallbackRef = useRef<HTMLElement | null>(null);\n const portalStyle = usePortalTokenStyle(sourceRef ?? fallbackRef);\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"og-root z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className,\n )}\n style={{ ...portalStyle, ...style }}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: \"default\" | \"destructive\";\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-og-menu outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSub(props: ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-og-menu outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n style,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n const fallbackRef = useRef<HTMLElement | null>(null);\n const portalStyle = usePortalTokenStyle(sourceRef ?? fallbackRef);\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"og-root z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className,\n )}\n style={{ ...portalStyle, ...style }}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA;AAAA,EACE;AAAA,EACA;AAAA,OAOK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB,QAAQ,wBAAwB;AAC1D;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAAA;AAAA,EACA;AAAA,OACK;;;AC/BP,SAAS,wBAAwB;AACjC,SAAS,gBAAgB,6BAA6B;AACtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAeD,cA8FF,YA9FE;AAXN,IAAM,4BAA4B,cAAoD,IAAI;AAE1F,SAAS,UAAa,KAAyB,OAAiB;AAC9D,MAAI,OAAO,QAAQ,WAAY,KAAI,KAAK;AAAA,WAC/B,IAAK,KAAI,UAAU;AAC9B;AAEA,SAAS,aAAa,OAA0D;AAC9E,QAAM,YAAY,OAA2B,IAAI;AACjD,SACE,oBAAC,0BAA0B,UAA1B,EAAmC,OAAO,WACzC,8BAAC,sBAAsB,MAAtB,EAA2B,aAAU,iBAAiB,GAAG,OAAO,GACnE;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA,GAAG;AACL,GAAyD;AACvD,QAAM,YAAY,WAAW,yBAAyB;AACtD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,KAAK,CAAC,SAAS;AACb,YAAI,UAAW,WAAU,UAAU;AACnC,kBAAU,KAAK,IAAI;AAAA,MACrB;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAAyD;AACvD,QAAM,YAAY,WAAW,yBAAyB;AACtD,QAAM,cAAc,OAA2B,IAAI;AACnD,QAAM,cAAc,oBAAoB,aAAa,WAAW;AAChE,SACE,oBAAC,sBAAsB,QAAtB,EACC;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM;AAAA,MACjC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,GAAG;AACL,GAGG;AACD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA,GAAG;AACL,GAA2D;AACzD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,6BAA6B,SAAS;AAAA,MACnD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,OAAyD;AAChF,SAAO,oBAAC,sBAAsB,KAAtB,EAA0B,aAAU,qBAAqB,GAAG,OAAO;AAC7E;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkF;AAChF,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD,oBAAC,oBAAiB,WAAU,kBAAiB;AAAA;AAAA;AAAA,EAC/C;AAEJ;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4D;AAC1D,QAAM,YAAY,WAAW,yBAAyB;AACtD,QAAM,cAAc,OAA2B,IAAI;AACnD,QAAM,cAAc,oBAAoB,aAAa,WAAW;AAChE,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM;AAAA,MACjC,GAAG;AAAA;AAAA,EACN;AAEJ;;;AD+MI,SA4aY,UA5aZ,OAAAC,MAkTQ,QAAAC,aAlTR;AAvSJ,IAAM,mBAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,aAAa;AACf;AAEA,IAAM,2BAA2B,CAAC,YAAY,mBAAmB,cAAc;AAG/E,IAAM,yBAGF;AAAA,EACF,UAAU,EAAE,cAAc,oBAAoB,MAAM,mBAAmB;AAAA,EACvE,mBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,MAAM;AAAA,EACR;AAAA,EACA,gBAAgB,EAAE,cAAc,QAAQ,MAAM,4BAA4B;AAC5E;AACA,IAAM,gCAAgC;AAgB/B,SAAS,8BAA8B,OAAgC;AAC5E,SAAO,8BAA8B,KAAK,MAAM;AAClD;AAEO,SAAS,8BAA8B,OAAsC;AAClF,MAAI,MAAM,kBAAkB,YAAa,QAAO;AAChD,MAAI,MAAM,iBAAiB,SAAU,QAAO;AAC5C,MAAI,MAAM,eAAe,KAAM,QAAO;AACtC,MAAI,CAAC,MAAM,eAAgB,QAAO;AAClC,MAAI,MAAM,gBAAiB,QAAO;AAClC,SAAO;AACT;AAEO,SAAS,mBAAmB,SAchC;AACD,QAAM,EAAE,QAAQ,YAAY,IAAI,2BAA2B;AAAA,IACzD,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,QAAQ,QAAQ,SAAS,iBAAiB;AAChD,QAAM,WAAWC,QAAyB,IAAI;AAC9C,QAAM,gBAAgBA,QAAyC,IAAI;AACnE,QAAM,qBAAqBA,QAAoC,IAAI;AACnE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA4C,OAAO;AAAA,IACjF,QAAQ,mCAAmC,EAAE,aAAa,WAAW,QAAQ,WAAW,MAAM,CAAC,IAC3F,eACA;AAAA,IACJ,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,OAAO;AAAA,EACT,EAAE;AACF,QAAM,YAAY;AAAA,IAChB,MAAM,gCAAgC,QAAQ,MAAM;AAAA,IACpD,CAAC,QAAQ,MAAM;AAAA,EACjB;AACA,QAAM,eAAeA,QAAO,SAAS;AACrC,QAAM,iBAAiBA,QAAO,QAAQ,WAAW;AACjD,eAAa,UAAU;AACvB,iBAAe,UAAU,QAAQ;AACjC,QAAM,kBAAkB,WAAW,UAAU;AAE7C,YAAU,MAAM;AACd,QAAI,WAAW;AACf,QAAI,aAA+C;AACnD,QAAI,cAAmC;AACvC,SAAK,OAAO,wBAAwB,EACjC,KAAK,CAAC,EAAE,gCAAgC,MAAM;AAC7C,UAAI,SAAU;AACd,YAAM,oBAAoB,QAAQ,qBAAqB;AACvD,mBAAa,kBAAkB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,GAAI,SAAS,UAAU,EAAE,aAAa,SAAS,QAAQ,IAAI,CAAC;AAAA,QAC5D;AAAA,MACF,CAAC;AACD,oBAAc,UAAU;AACxB,yBAAmB,UAAU;AAC7B,oBAAc,WAAW,UAAU,WAAW;AAC9C,UAAI,eAAe,SAAS;AAC1B,aAAK,WAAW,iBAAiB,aAAa,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,MAC9E;AAAA,IACF,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI,SAAU;AACd,kBAAY;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,aAAa;AAAA,QACb,sBAAsB;AAAA,QACtB,kBAAkB;AAAA,QAClB,YAAY;AAAA,UACV,MAAM;AAAA,UACN,SACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAC3C,aAAa;AAAA,UACb,sBAAsB;AAAA,UACtB,SAAS;AAAA,QACX;AAAA,QACA,OACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAC7C,CAAC;AAAA,IACH,CAAC;AACH,WAAO,MAAM;AACX,iBAAW;AACX,oBAAc;AACd,kBAAY,MAAM;AAClB,UAAI,cAAc,YAAY,YAAY;AACxC,sBAAc,UAAU;AACxB,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,QAAQ,mBAAmB,QAAQ,WAAW,WAAW,CAAC;AAE7E,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,YAAa;AAC1B,SAAK,cAAc,SAAS,iBAAiB,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,EAC/E,GAAG,CAAC,WAAW,QAAQ,WAAW,CAAC;AAEnC,QAAM,QAAQ,YAAY,YAAY;AACpC,UAAM,aAAa,cAAc;AACjC,QAAI,CAAC,cAAc,mBAAmB,YAAY,OAAO;AACvD,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AACA,UAAM,WAAW,MAAM;AAAA,EACzB,GAAG,CAAC,KAAK,CAAC;AACV,QAAM,OAAO,YAAY,YAAY;AACnC,UAAM,cAAc,SAAS,KAAK;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,QAAM,QAAQ,YAAY,YAAY;AACpC,UAAM,cAAc,SAAS,MAAM;AAAA,EACrC,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqB,YAAY,YAAY;AACjD,UAAM,cAAc,SAAS,mBAAmB;AAAA,EAClD,GAAG,CAAC,CAAC;AACL,QAAM,gBAAgB,YAAY,CAAC,UAAmB;AACpD,kBAAc,SAAS,cAAc,KAAK;AAAA,EAC5C,GAAG,CAAC,CAAC;AACL,QAAM,iBAAiB,YAAY,CAAC,UAAmB;AACrD,kBAAc,SAAS,eAAe,KAAK;AAAA,EAC7C,GAAG,CAAC,CAAC;AACL,QAAM,iBAAiB;AAAA,IACrB,eAAe,QAAQ;AAAA,IACvB,cAAc,QAAQ,iBAAiB;AAAA,IACvC,YAAY,QAAQ,iBAAiB;AAAA,IACrC,gBAAgB,QAAQ,kBAAkB,QAAQ;AAAA,IAClD;AAAA,EACF;AACA,QAAM,mBACJ,QAAQ,mBAAmB,QACtB,QAAQ,0BAA0B,qCACnC,8BAA8B,cAAc;AAClD,QAAM,WACJ,cAAc,YAAY,QAC1B,mBAAmB,YAAY,SAC/B,CAAC,QAAQ,OAAO,EAAE,SAAS,SAAS,MAAM,KAC1C,qBAAqB;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,uBAAuB,OAepC;AACD,QAAM,YAAY,QAAQ,MAAM,gCAAgC,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC;AAC7F,QAAM,YAAY,0BAA0B;AAAA,IAC1C,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,gBAAgB,MAAM;AAAA,IACtB,aAAa,WAAW,UAAU,WAAW,UAAU,QAAQ;AAAA,EACjE,CAAC;AACD,QAAM,gBAAgB,UAAU;AAChC,QAAM,WAAW,mBAAmB;AAAA,IAClC,GAAG;AAAA,IACH,OAAO,cAAc;AAAA,IACrB,gBAAgB,cAAc;AAAA,IAC9B,wBAAwB,cAAc;AAAA,EACxC,CAAC;AACD,QAAM,oBAAoBA,QAAO,MAAM,0BAA0B,IAAI;AACrE,QAAM,sBAAsBA,QAAO,KAAK;AAExC,QAAM,EAAE,QAAQ,eAAe,uBAAuB,YAAY,IAAI;AACtE,QAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,QAAM,8BAA8B,MAAM;AAC1C,QAAM,sBAAsB,MAAM;AAClC,QAAM,cAAc,SAAS,SAAS,MAAM,UAAU;AAEtD,YAAU,MAAM;AACd,0BAAsB,WAAW;AAAA,EACnC,GAAG,CAAC,qBAAqB,WAAW,CAAC;AAErC,YAAU,MAAM;AACd,UAAM,UAAU,kBAAkB;AAClC,QAAI,CAAC,WAAW,oBAAoB,WAAW,WAAW,UAAU,SAAU;AAC9E,UAAM,YAAY,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,WAAW,MAAM,SAAS;AAChF,QAAI,CAAC,UAAW;AAChB,QAAI,sBAAsB,OAAO,SAAS;AACxC,kBAAY,OAAO;AACnB;AAAA,IACF;AACA,QAAI,CAAC,SAAU;AACf,wBAAoB,UAAU;AAC9B,SAAK,MAAM,EACR,KAAK,MAAM;AACV,wBAAkB,UAAU;AAC5B,oCAA8B;AAAA,IAChC,CAAC,EACA,MAAM,MAAM;AACX,0BAAoB,UAAU;AAAA,IAChC,CAAC;AAAA,EACL,GAAG;AAAA,IACD;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB;AAAA,EACF,CAAC;AAED,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,SAAS;AAAA,MACnB,UAAU,SAAS;AAAA,MACnB,kBAAkB,SAAS;AAAA,MAC3B,gBAAgB,cAAc;AAAA,MAC9B,UAAS;AAAA,MACT,UAAU,SAAS;AAAA,MACnB;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB,eAAe,SAAS,kBAAkB,SAAY,UAAU;AAAA,MAChE,SAAS,SAAS;AAAA,MAClB,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,MAClB,sBAAsB,SAAS;AAAA,MAC/B,iBAAiB,SAAS;AAAA,MAC1B,kBAAkB,SAAS;AAAA;AAAA,EAC7B;AAEJ;AAEO,SAAS,0BAA0B,SAKvC;AACD,QAAM,EAAE,QAAQ,YAAY,IAAI,2BAA2B;AAAA,IACzD,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,cAAc,QAAQ;AAC5B,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,MAAM;AAAA,IAClE;AAAA,MACE,GAAG;AAAA,MACH,WAAW,QAAQ;AAAA,MACnB,mBAAmB,QAAQ,iBAAiB,OAAO,iBAAiB;AAAA,IACtE;AAAA,EACF,CAAC;AACD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C,MAAM,4BAA4B,WAAW,KAAK,iBAAiB;AAAA,EACrE;AAEA,YAAU,MAAM;AACd,QAAI,WAAW;AACf,UAAM,OAAO,OAAO;AACpB,QAAI,CAAC,KAAM;AACX,SAAK,KACF,KAAK,QAAQ,WAAW,EACxB,KAAK,CAAC,aAAa;AAClB,UAAI,SAAU;AACd,YAAM,SAAS,SAAS,OAAO,IAAI,qBAAqB;AACxD,iBAAW,MAAM;AACjB,yBAAmB,CAAC,YAAY;AAC9B,YAAI,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,WAAW,MAAM,SAAS,EAAG,QAAO;AAC5E,eAAO,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,GAAG,MAAM,iBAAiB;AAAA,MACzE,CAAC;AAAA,IACH,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,WAAO,MAAM;AACX,iBAAW;AAAA,IACb;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,YAAU,MAAM;AACd,QAAI,YAAa,oBAAmB,WAAW;AAAA,EACjD,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,gBACJ,QAAQ,KAAK,CAAC,UAAU,MAAM,OAAO,eAAe,KACnD;AAAA,IACC,GAAG;AAAA,IACH,WAAW,QAAQ;AAAA,IACnB,mBAAmB,QAAQ,iBAAiB,OAAO,iBAAiB;AAAA,EACtE;AACF,QAAM,cAAc;AAAA,IAClB,CAAC,UAAkB;AACjB,YAAM,QAAQ,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,KAAK;AAChE,UAAI,CAAC,SAAS,CAAC,MAAM,aAAa,YAAa;AAC/C,yBAAmB,MAAM,EAAE;AAC3B,mCAA6B,aAAa,MAAM,EAAE;AAAA,IACpD;AAAA,IACA,CAAC,aAAa,SAAS,WAAW;AAAA,EACpC;AAEA,SAAO,EAAE,QAAQ,SAAS,eAAe,YAAY;AACvD;AAEA,IAAM,yBAA4D;AAAA,EAChE,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,OAAO;AACT;AAEO,SAAS,0BAA0B,OAiBvC;AACD,QAAM,oBAAoB,0BAA0B;AAAA,IAClD,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,gBAAgB,MAAM;AAAA,EACxB,CAAC;AACD,QAAM,YACJ,MAAM,UAAU,MAAM,iBAAiB,MAAM,gBACzC;AAAA,IACE,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,aAAa,MAAM;AAAA,EACrB,IACA;AACN,QAAM,WAAWE,QAAyB,IAAI;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,WAAW;AAAA,IACf,MACE,WACI,EAAE,GAAG,wBAAwB,QAAQ,WAAW,IAChD,QACE,EAAE,GAAG,wBAAwB,QAAQ,SAAS,MAAM,IACpD;AAAA,IACR,CAAC,OAAO,QAAQ;AAAA,EAClB;AACA,QAAM,WAAW,UAAU,cAAc,aAAa,MAAM,aAAa,QAAQ,CAAC;AAClF,QAAM,UAAU,MAAM;AACtB,QAAM,kBAAkB,UAAU,cAAc;AAChD,QAAM,QAAQ,YAAY,YAAY;AACpC,QAAI,CAAC,SAAU;AACf,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,eAAe;AAC7C,UAAI,CAAC,QAAS,UAAS,yCAAyC;AAAA,IAClE,SAAS,OAAO;AACd,eAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACjE,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,UAAU,SAAS,eAAe,CAAC;AAEvC,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,kBAAkB,UAAU,cAAc,qBAAqB,MAAM,kBAAkB;AAAA,MACvF,gBAAgB,UAAU,cAAc;AAAA,MACxC,mBAAmB;AAAA,MACnB,UAAS;AAAA,MACT,WAAW,MAAM,aAAa;AAAA,MAC9B,iBAAiB;AAAA,MACjB;AAAA,MACA,eAAe,UAAU;AAAA,MACzB,QAAQ,UAAU;AAAA,MAClB,eAAe,UAAU;AAAA,MACzB,SAAS;AAAA,MACT,QAAQ,YAAY;AAAA,MACpB,SAAS;AAAA,MACT,sBAAsB,YAAY;AAAA,MAClC,iBAAiB,MAAM;AAAA,MACvB,kBAAkB,MAAM;AAAA;AAAA,EAC1B;AAEJ;AAEO,SAAS,qBAAqB,OA2BlC;AACD,QAAM,eAAe,iBAAiB;AACtC,QAAM,gBAAgB,MAAM,iBAAiB;AAAA,IAC3C,GAAG;AAAA,IACH,WAAW,MAAM;AAAA,IACjB,mBAAmB,MAAM,iBAAiB,OAAO,iBAAiB;AAAA,EACpE;AACA,QAAM,SAAS,MAAM,UAAU,CAAC,aAAa;AAC7C,QAAM,SAAS;AAAA,IACb,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM,oBAAoB;AAAA,IAC1B,cAAc;AAAA,EAChB;AACA,QAAM,YAAY,MAAM,SAAS,MAAM,UAAU;AACjD,QAAM,kBACJ,aACA,MAAM,SAAS,WAAW,cAC1B,MAAM,SAAS,WAAW,YAC1B,MAAM,SAAS,YAAY,gBAAgB;AAC7C,QAAM,eAAe,MAAM,SAAS,kBAAkB,aAAa,CAAC,MAAM,SAAS;AACnF,QAAM,eACJ,MAAM,SAAS,WAAW,cAC1B,MAAM,SAAS,WAAW,gBACzB,CAAC,aAAa,CAAC,MAAM,YACrB,MAAM,SAAS,WAAW,cAAc,CAAC;AAC5C,QAAM,YAAY,eACd,uBACA,kBACE,2BACA,YACE,2BACA,oBAAoB,cAAc,KAAK;AAC/C,QAAM,gBAAgB,eAClB,MAAM,uBACN,kBACE,MAAM,UACN,YACE,MAAM,SACN,MAAM;AACd,QAAM,qBAAqB,MAAM,mBAAmB,YAAY,IAAI;AACpE,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,wBAAwB,cAAc,WAAW,cAAc;AACrE,QAAM,uBAAuB,cAAc;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI;AAAA,IAC1C,cAAc;AAAA,EAChB;AACA,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAiB,CAAC;AAEhE,YAAU,MAAM;AACd,QAAI,CAAC,WAAY,mBAAkB,cAAc,QAAQ;AAAA,EAC3D,GAAG,CAAC,YAAY,cAAc,QAAQ,CAAC;AAEvC,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,oBAAkB,MAAM,YAAY;AAAA,MACpC,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,WAAM,KAAK,MAAM,UAAU,UAAQ,MAAC,WAAU,UAAS,eAAY,QAAO;AAAA,QAC3E,gBAAAA,KAAC,wBAAqB,OAAO,OAAO,OAAO,cAAc,iBAAiB,MAAM;AAAA,QAChF,gBAAAA,KAAC,mBAAgB,SAAS,OACvB,sBACC,gBAAAA;AAAA,UAAC,OAAO;AAAA,UAAP;AAAA,YAEC,eAAY;AAAA,YAGZ,SAAS,eAAe,QAAQ,EAAE,SAAS,GAAG,OAAO,EAAE;AAAA,YACvD,SAAS;AAAA,cACP,SAAS;AAAA,cACT,OAAO;AAAA,cACP,YAAY,eACR,EAAE,UAAU,EAAE,IACd,EAAE,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE;AAAA,YAC7D;AAAA,YACC,GAAI,eACD,CAAC,IACD;AAAA,cACE,MAAM;AAAA,gBACJ,SAAS;AAAA,gBACT,OAAO;AAAA,gBACP,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE;AAAA,cACrD;AAAA,YACF;AAAA,YACJ,WAAW;AAAA,cACT;AAAA,cACA,MAAM;AAAA,YACR;AAAA,YAEA,0BAAAC;AAAA,cAAC,OAAO;AAAA,cAAP;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,eAAe,QAAQ,EAAE,GAAG,IAAI,SAAS,EAAE;AAAA,gBACpD,SAAS;AAAA,kBACP,GAAG;AAAA,kBACH,SAAS;AAAA,kBACT,YAAY,eACR,EAAE,UAAU,EAAE,IACd,EAAE,OAAO,MAAM,MAAM,UAAU,WAAW,KAAK,SAAS,IAAI,MAAM,IAAI;AAAA,gBAC5E;AAAA,gBACC,GAAI,eACD,CAAC,IACD;AAAA,kBACE,MAAM;AAAA,oBACJ,GAAG;AAAA,oBACH,SAAS;AAAA,oBACT,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE;AAAA,kBACrD;AAAA,gBACF;AAAA,gBAEJ;AAAA,kCAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,MAAM,SAAS;AAAA,sBACtB,MAAK;AAAA,sBACL,cAAc,iBAAiB;AAAA,sBAC/B,YAAY,eAAe,IAAI;AAAA,sBAC/B,UAAU,MAAM,MAAM,gBAAgB,CAAC,MAAM,SAAS,UAAU;AAAA;AAAA,kBAClE;AAAA,kBACA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,MAAM,SAAS;AAAA,sBACtB,MAAK;AAAA,sBACL,cAAc,iBAAiB;AAAA,sBAC/B,YAAY,eAAe,IAAI;AAAA,sBAC/B,UAAU,MAAM,MAAM,iBAAiB,CAAC,MAAM,SAAS,WAAW;AAAA;AAAA,kBACpE;AAAA;AAAA;AAAA,YACF;AAAA;AAAA,UA5DI;AAAA,QA6DN,IACE,MACN;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,qCACb;AAAA,0BAAAD;AAAA,YAAC,OAAO;AAAA,YAAP;AAAA,cACC,MAAK;AAAA,cACL,eAAY;AAAA,cACZ,cAAY,OAAO;AAAA,cACnB,cAAY;AAAA,cACZ,gBAAc;AAAA,cACd,OAAO;AAAA,cACP,UAAU;AAAA,cACT,GAAI,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,KAAK,EAAE;AAAA,cACrD,YAAY,EAAE,MAAM,UAAU,WAAW,KAAK,SAAS,GAAG;AAAA,cAC1D,SAAS,MAAM,KAAK,cAAc,EAAE,MAAM,MAAM,MAAS;AAAA,cACzD,WAAW;AAAA;AAAA;AAAA;AAAA,gBAIT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,0BACG,uBACG,yCACA;AAAA,gBACN,gBAAgB,OAAO,KAAK;AAAA,cAC9B;AAAA,cAEA,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,OAAO;AAAA,kBACd;AAAA,kBACA,cAAc,iBAAiB;AAAA;AAAA,cACjC;AAAA;AAAA,UACF;AAAA,UAEC,wBACC,gBAAAC,MAAC,gBAAa,MAAM,YAAY,cAAc,eAC5C;AAAA,4BAAAD,KAAC,uBAAoB,SAAO,MAC1B,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,OAAO,gBAAgB,cAAc,KAAK;AAAA,gBAC1C,UAAU,MAAM;AAAA,gBAChB,WAAW;AAAA;AAAA,kBAET;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,wBAAwB;AAAA,kBACxB,iBAAiB,OAAO,KAAK;AAAA,gBAC/B;AAAA,gBAEA,0BAAAA,KAAC,mBAAgB,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,YAClD,GACF;AAAA,YACA,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,MAAM,YAAY;AAAA,gBACxB,OAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,WAAU;AAAA,gBACV,kBAAkB,CAAC,UAAU,MAAM,eAAe;AAAA,gBAElD;AAAA,kCAAAD,KAAC,SAAI,WAAU,qDACb,0BAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC;AAAA,sBACA;AAAA,sBACA,UAAU;AAAA,sBACV,WAAW;AAAA,sBACX,UACE,MAAM,sBAAsB,QAAQ,MAAM,SAAS,MAAM,UAAU;AAAA,sBAErE,kBAAkB,CAAC,UAAU,cAAc;AACzC,2CAAmB,SAAS;AAC5B,0CAAkB,QAAQ;AAAA,sBAC5B;AAAA,sBACA,UAAU,CAAC,YAAY;AACrB,8BAAM,gBAAgB,OAAO;AAC7B,sCAAc,KAAK;AAAA,sBACrB;AAAA;AAAA,kBACF,GACF;AAAA,kBAEC,OAAO,UAAU,SAChB,gBAAAC,MAAA,YACE;AAAA,oCAAAD,KAAC,yBAAsB,WAAU,qBAAoB;AAAA,oBACrD,gBAAAC,MAAC,SAAI,WAAU,eAAc,MAAK,UAAS,aAAU,UACnD;AAAA,sCAAAA,MAAC,SAAI,WAAU,6DACb;AAAA,wCAAAD;AAAA,0BAAC;AAAA;AAAA,4BACC,OAAO,OAAO;AAAA,4BACd,cAAc,iBAAiB;AAAA;AAAA,wBACjC;AAAA,wBACC,OAAO;AAAA,yBACV;AAAA,sBACA,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,iBAAO,QAAO;AAAA,uBAC5E;AAAA,qBACF,IACE;AAAA,kBAEH,eACC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,UAAU,MAAM,KAAK,MAAM,qBAAqB,EAAE,MAAM,MAAM,MAAS;AAAA,sBAEvE;AAAA,wCAAAD,KAAC,eAAY;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEjB,IACE;AAAA,kBACH,kBACC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,UAAU,MAAM,KAAK,MAAM,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,sBAE1D;AAAA,wCAAAD,KAAC,iBAAc;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEnB,IACE;AAAA,kBACH,aAAa,MAAM,SAAS,WAAW,eACtC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,WAAU;AAAA,sBACV,UAAU,MAAM,SAAS,WAAW;AAAA,sBACpC,UAAU,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,sBAEzD;AAAA,wCAAAD,KAAC,cAAW;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEhB,IACE;AAAA,kBAEH,qBAAqB,gBAAAA,KAAC,2BAAwB,UAAU,MAAM,UAAU,IAAK;AAAA;AAAA;AAAA,YAChF;AAAA,aACF,IACE;AAAA,WACN;AAAA,QAEA,gBAAAC,MAAC,UAAK,WAAU,WAAU,MAAK,UAAS,aAAU,UAC/C;AAAA,iBAAO;AAAA,UAAM;AAAA,UAAG,OAAO;AAAA,WAC1B;AAAA,QACC,MAAM,SAAS,QACd,gBAAAD,KAAC,UAAK,WAAU,WAAU,MAAK,SAC5B,gBAAM,SAAS,OAClB,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAGO,SAAS,wBAAwB,OAMrC;AACD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAuC,IAAI;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAI,SAAiB,CAAC;AAEpD,SACE,gBAAAC,MAAC,SAAI,WAAU,gCAA+B,eAAY,8BACxD;AAAA,oBAAAA,MAAC,SAAI,WAAU,oDACZ;AAAA,YAAM;AAAA,MACP,gBAAAA,MAAC,SAAI,WAAU,WACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,uCAAsC,yBAAW;AAAA,QAChE,gBAAAA,KAAC,OAAE,WAAU,4CAA2C,uDAExD;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAU,mEACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,MAAM;AAAA,QACd,eAAe,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA,UAAU,MAAM,aAAa;AAAA,QAC7B,kBAAkB,CAAC,MAAM,kBAAkB;AACzC,uBAAa,aAAa;AAC1B,sBAAY,IAAI;AAAA,QAClB;AAAA,QACA,UAAU,MAAM;AAAA;AAAA,IAClB,GACF;AAAA,KACF;AAEJ;AAEA,SAAS,mBAAmB,OAMzB;AACD,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,QAAQ,aACV,MAAM,QACJ,sBACA,oBACF,MAAM,QACJ,uBACA;AACN,QAAM,OAAO,aACT,MAAM,QACJ,aACA,UACF,MAAM,QACJ,cACA;AAEN,SACE,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,gBAAc,MAAM;AAAA,MACpB,OAAO;AAAA,MACP,SAAS,MAAM,eAAe,QAAQ,EAAE,OAAO,MAAM,SAAS,EAAE;AAAA,MAChE,SAAS,EAAE,OAAO,GAAG,SAAS,EAAE;AAAA,MAChC,YAAY;AAAA,QACV,OAAO,MAAM,cAAc;AAAA,QAC3B,MAAM;AAAA,QACN,WAAW;AAAA,QACX,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,MACC,GAAI,MAAM,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,KAAK,EAAE;AAAA,MAC3D,SAAS,MAAM;AAAA,MACf,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,QACF,gEACA;AAAA,MACN;AAAA,MAEA,0BAAAA,KAAC,QAAK,WAAU,YAAW,eAAW,MAAC;AAAA;AAAA,EACzC;AAEJ;AAEO,SAAS,wBAAwB,OAQrC;AACD,QAAM,YAAY,yBAAyB;AAAA,IAAO,CAAC,aACjD,MAAM,OAAO,KAAK,CAAC,UAAU,MAAM,aAAa,QAAQ;AAAA,EAC1D;AACA,QAAM,UAAU,MAAM,WAAW,gBAAgB,MAAM,QAAQ,KAAK;AACpE,QAAM,OAAO,MAAM,WACjB,gBAAAC,MAAC,SAAI,eAAY,gCACf;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,MACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,cAAc,uBAAuB,MAAM,QAAQ,EAAE;AAAA,YACrD,cAAW;AAAA;AAAA,QACb;AAAA,QAEF,QAAQ,MAAM,MAAM,iBAAiB,MAAM,EAAE;AAAA;AAAA,IAC/C;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAU,yBACZ,gBAAM,OACJ,OAAO,CAAC,UAAU,MAAM,aAAa,MAAM,QAAQ,EACnD,IAAI,CAAC,UAAU;AACd,YAAM,WAAW,MAAM,OAAO,MAAM,cAAc;AAClD,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO,MAAM;AAAA,UACb,MACE,MAAM,sBACL,MAAM,cAAc,oBAAiB,MAAM,WAAW,KAAK,MAAM;AAAA,UAEpE,UAAU,MAAM,YAAY,CAAC,MAAM;AAAA,UACnC,OAAO,MAAM,qBAAqB,MAAM;AAAA,UACxC,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,UAAU,WAAW,gBAAAA,KAAC,aAAU,WAAU,YAAW,eAAW,MAAC,IAAK;AAAA,UACtE,QAAQ,yBAAyB,MAAM,EAAE;AAAA,UACzC,SAAS,MAAM,MAAM,SAAS,MAAM,EAAE;AAAA;AAAA,QAZjC,MAAM;AAAA,MAab;AAAA,IAEJ,CAAC,GACL;AAAA,KACF,IAEA,gBAAAA,KAAC,SAAI,WAAU,yBAAwB,eAAY,mCAChD,oBAAU,IAAI,CAAC,aAAa;AAC3B,UAAM,OAAO,uBAAuB,QAAQ;AAC5C,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO;AAAA,QACP,MAAM,KAAK;AAAA,QACX,MAAM,gBAAAA,KAAC,oBAAiB,cAAc,KAAK,cAAc,cAAW,IAAG;AAAA,QACvE,QAAQ,MAAM,cAAc,aAAa;AAAA,QACzC,QAAQ,2BAA2B,KAAK,YAAY;AAAA,QACpD,SAAS,MAAM,MAAM,iBAAiB,UAAU,CAAC;AAAA;AAAA,MAN5C;AAAA,IAOP;AAAA,EAEJ,CAAC,GACH;AAGF,SACE,gBAAAA,KAAC,SAAI,WAAU,4BAA2B,eAAY,8BACpD,0BAAAA,KAAC,sBAAmB,SAAkB,WAAW,MAAM,WACpD,gBACH,GACF;AAEJ;AAEA,SAAS,wBAAwB,EAAE,SAAS,GAAoD;AAC9F,QAAM,SAAS,SAAS;AACxB,QAAM,OAAO;AAAA,IACX,CAAC,cAAc,SAAS,MAAM;AAAA,IAC9B,CAAC,cAAc,SAAS,UAAU;AAAA,IAClC,CAAC,oBAAoB,OAAO,SAAS,UAAU,CAAC;AAAA,IAChD,CAAC,SAAS,SAAS,aAAa;AAAA,IAChC,CAAC,eAAe,OAAO,SAAS,WAAW,CAAC;AAAA,IAC5C,CAAC,cAAc,OAAO,SAAS,oBAAoB,CAAC;AAAA,IACpD,CAAC,qBAAqB,OAAO,SAAS,gBAAgB,CAAC;AAAA,IACvD,CAAC,QAAQ,SAAS,MAAM,SAAS,MAAM;AAAA,IACvC,CAAC,gBAAgB,SAAS,OAAO,OAAO,SAAS,KAAK,OAAO,IAAI,QAAG;AAAA,IACpE,CAAC,oBAAoB,SAAS,OAAO,OAAO,SAAS,KAAK,eAAe,IAAI,QAAG;AAAA,IAChF,CAAC,oBAAoB,SAAS,OAAO,OAAO,eAAe,IAAI,OAAO;AAAA,IACtE,CAAC,qBAAqB,SAAS,OAAO,OAAO,QAAQ,IAAI,OAAO;AAAA,IAChE,CAAC,cAAc,QAAQ,qBAAqB,WAAW,MAAM;AAAA,IAC7D,CAAC,0BAA0B,SAAS,OAAO,OAAO,cAAc,IAAI,GAAG;AAAA,IACvE,CAAC,iBAAiB,SAAS,OAAO,OAAO,mBAAmB,IAAI,GAAG;AAAA,IACnE,CAAC,cAAc,QAAQ,0BAA0B,SAAS,KAAK,QAAG;AAAA,IAClE,CAAC,2BAA2B,SAAS,OAAO,OAAO,iBAAiB,IAAI,GAAG;AAAA,IAC3E,CAAC,sBAAsB,QAAQ,wBAAwB,MAAM;AAAA,IAC7D,CAAC,gBAAgB,QAAQ,aAAa,MAAM;AAAA,IAC5C,CAAC,cAAc,SAAS,YAAY,QAAQ,MAAM;AAAA,EACpD;AAEA,SACE,gBAAAC,MAAA,YACE;AAAA,oBAAAD,KAAC,yBAAsB,WAAU,qBAAoB;AAAA,IACrD,gBAAAC,MAAC,mBACC;AAAA,sBAAAD,KAAC,0BAAuB,WAAU,kCAAiC,kCAEnE;AAAA,MACA,gBAAAA,KAAC,0BAAuB,WAAU,8GAC/B,eAAK,IAAI,CAAC,CAAC,OAAO,KAAK,MACtB,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,UAAQ;AAAA,UACR,WAAU;AAAA,UAEV;AAAA,4BAAAD,KAAC,UAAK,WAAU,8BAA8B,iBAAM;AAAA,YACpD,gBAAAA,KAAC,UAAK,WAAU,kCAAkC,iBAAM;AAAA;AAAA;AAAA,QALnD;AAAA,MAMP,CACD,GACH;AAAA,OACF;AAAA,KACF;AAEJ;AAcA,SAAS,cACP,UACA,gBACA,UACA,kBACA,YAC+D;AAC/D,MAAI,SAAS,kBAAkB,aAAa,CAAC,SAAS,aAAa;AACjE,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AACA,UAAQ,SAAS,QAAQ;AAAA,IACvB,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO,SAAS,QAAQ,WACpB;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV,IACA;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACN,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,SAAS,SAAS;AAAA,MAC5B;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,SAAS,SAAS;AAAA,MAC5B;AAAA,IACF,KAAK;AACH,UAAI,CAAC,gBAAgB;AACnB,eAAO;AAAA,UACL,OAAO;AAAA,UACP,OAAO;AAAA,UACP,QAAQ,oBAAoB;AAAA,QAC9B;AAAA,MACF;AACA,UAAI,CAAC,YAAY,kBAAkB;AACjC,eAAO,EAAE,OAAO,eAAe,OAAO,qBAAqB,QAAQ,iBAAiB;AAAA,MACtF;AACA,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,kBAAkB,UAAU;AAAA,MACtC;AAAA,EACJ;AACF;AAEA,SAAS,qBAAqB,OAA8D;AAC1F,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,SAAS,MAAM,KAAK;AACtB,SACE,gBAAAA,KAAC,mBAAgB,SAAS,OACvB,oBACC,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,eAAW;AAAA,MAEX,SAAS,EAAE,SAAS,EAAE;AAAA,MACtB,SACE,MAAM,eACF,EAAE,SAAS,KAAK,IAChB,MAAM,UAAU,aACd,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,IAC9B,MAAM,UAAU,cACd,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,IAC9B,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE;AAAA,MAExC,MAAM,EAAE,SAAS,EAAE;AAAA,MACnB,YACE,MAAM,eACF,EAAE,UAAU,KAAK,IACjB;AAAA,QACE,UAAU,MAAM,UAAU,aAAa,OAAO;AAAA,QAC9C,MAAM;AAAA,QACN,QAAQ,OAAO;AAAA,MACjB;AAAA,MAEN,WAAW;AAAA,QACT;AAAA,QACA,MAAM,UAAU,aACZ,8BACA,MAAM,UAAU,aAAa,MAAM,UAAU,UAC3C,8BACA;AAAA,MACR;AAAA;AAAA,IA5BK,MAAM;AAAA,EA6Bb,IACE,MACN;AAEJ;AAEA,SAAS,oBAAoB,OAI1B;AACD,MAAI,CAAC,cAAc,gBAAgB,UAAU,EAAE,SAAS,MAAM,KAAK,GAAG;AACpE,WAAO,gBAAAA,KAAC,oBAAiB,WAAU,4BAA2B,eAAW,MAAC;AAAA,EAC5E;AACA,MAAI,MAAM,UAAU,WAAW;AAC7B,WAAO,MAAM,eACX,gBAAAA,KAAC,eAAY,WAAU,YAAW,eAAW,MAAC,IAE9C,gBAAAA,KAAC,mBAAgB,WAAU,YAAW,eAAW,MAAC;AAAA,EAEtD;AACA,MAAI,MAAM,UAAU,WAAW,MAAM,UAAU,eAAe,MAAM,UAAU,eAAe;AAC3F,WAAO,gBAAAA,KAAC,mBAAgB,WAAU,YAAW,eAAW,MAAC;AAAA,EAC3D;AACA,MAAI,MAAM,UAAU,eAAe,MAAM,UAAU,YAAY;AAC7D,WACE,gBAAAC,MAAC,UAAK,WAAU,2DACb;AAAA,OAAC,MAAM,eACN,gBAAAD;AAAA,QAAC,OAAO;AAAA,QAAP;AAAA,UACC,eAAW;AAAA,UACX,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE;AAAA,UACnD,YAAY;AAAA,YACV,UAAU,MAAM,UAAU,aAAa,MAAM;AAAA,YAC7C,MAAM;AAAA,YACN,QAAQ,OAAO;AAAA,UACjB;AAAA;AAAA,MACF,IACE;AAAA,MACJ,gBAAAA,KAAC,cAAW,WAAU,kCAAiC,eAAW,MAAC;AAAA,OACrE;AAAA,EAEJ;AACA,SAAO,gBAAAA,KAAC,kBAAe,WAAU,UAAS,eAAW,MAAC;AACxD;AAEA,SAAS,kBAAkB,OAA8D;AACvF,QAAM,OAAO,MAAM,UAAU,eAAe,MAAM,UAAU;AAC5D,SACE,gBAAAC,MAAC,UAAK,WAAU,wCAAuC,eAAW,MAC/D;AAAA,YAAQ,CAAC,MAAM,eACd,gBAAAD;AAAA,MAAC,OAAO;AAAA,MAAP;AAAA,QACC,WAAU;AAAA,QACV,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,EAAE;AAAA,QAC/C,YAAY,EAAE,UAAU,KAAK,MAAM,WAAW,QAAQ,OAAO,kBAAkB;AAAA;AAAA,IACjF,IACE;AAAA,IACJ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,OACI,yBACA,MAAM,UAAU,aAAa,MAAM,UAAU,UAC3C,yBACA,MAAM,UAAU,eAAe,MAAM,UAAU,gBAC7C,oBACA;AAAA,QACV;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,SAAS,gBAAgB,OAAoC;AAC3D,MAAI,UAAU,eAAe,UAAU,YAAY;AACjD,WAAO;AAAA,EACT;AACA,MAAI,UAAU,aAAa,UAAU,SAAS;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,gBAAgB,UAAU,kBAAkB,UAAU,YAAY;AAC9E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAoC;AAC5D,MAAI,UAAU,eAAe,UAAU,YAAY;AACjD,WAAO;AAAA,EACT;AACA,MAAI,UAAU,aAAa,UAAU,SAAS;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,gBAAgB,UAAU,kBAAkB,UAAU,YAAY;AAC9E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAA+D;AAC5F,SAAO,EAAE,GAAG,MAAM;AACpB;AAEA,SAAS,4BAA4B,aAAkD;AACrF,MAAI,OAAO,iBAAiB,YAAa,QAAO;AAChD,MAAI;AACF,UAAM,QAAQ,aAAa,QAAQ,GAAG,6BAA6B,IAAI,WAAW,EAAE;AACpF,WAAO,gBAAgB,KAAK,IAAI,QAAQ;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,6BAA6B,aAAqB,OAAmC;AAC5F,MAAI,OAAO,iBAAiB,YAAa;AACzC,MAAI;AACF,iBAAa,QAAQ,GAAG,6BAA6B,IAAI,WAAW,IAAI,KAAK;AAAA,EAC/E,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,gBAAgB,OAAqD;AAC5E,SACE,UAAU,8BACV,UAAU,8CACV,UAAU,+CACV,UAAU,oDACV,UAAU,+CACV,UAAU,gDACV,UAAU;AAEd;AAGO,IAAM,0BAA0B;AAEhC,IAAM,qCAAqC;AAE3C,IAAM,8BAA8B;AAEpC,IAAM,uBAAuB;","names":["useRef","jsx","jsxs","useRef"]}
|
|
1
|
+
{"version":3,"sources":["../src/realtime/realtime-control.tsx","../src/realtime/dropdown-menu.tsx"],"sourcesContent":["import { type EffectiveSessionControl, type SessionEvent, type SessionStatus } from \"@opengeni/sdk\";\nimport {\n hasStoredSessionRealtimeOwnerProof,\n projectSessionRealtimeLifecycle,\n type SessionRealtimeClientLike,\n type SessionRealtimeController,\n type SessionRealtimeControllerSnapshot,\n type CreateSessionRealtimeControllerOptions,\n type SessionRealtimeModel,\n type WorkspaceRealtimeModelCatalogItem,\n} from \"@opengeni/sdk/realtime\";\nimport {\n AudioLinesIcon,\n CheckIcon,\n ChevronDownIcon,\n CircleAlertIcon,\n LoaderCircleIcon,\n MicIcon,\n MicOffIcon,\n RotateCcwIcon,\n SquareIcon,\n Volume2Icon,\n VolumeXIcon,\n} from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport {\n type ReactNode,\n type RefObject,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport {\n BillingClassMark,\n PickerAnimatedPage,\n PickerBackHeader,\n PickerNavRow,\n} from \"../components/model-policy-picker\";\nimport type { EmbeddedRealtimeSessionClientLike } from \"../client\";\nimport { cn } from \"../lib/cn\";\nimport type { PickerBillingClass as BillingClass } from \"../model-policy\";\nimport { useEmbeddedRealtimeSession } from \"../session-context\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\";\n\nexport type RealtimeModelOption = {\n id: SessionRealtimeModel;\n label: string;\n provider: \"OpenGeni\" | \"Connected Codex\" | \"Your Gateway\";\n description: string;\n available: boolean;\n unavailableReason: string | null;\n recommended: boolean;\n};\n\nconst CODEX_LIVE_MODEL: RealtimeModelOption = {\n id: \"gpt-live-1-boulder-alpha\",\n label: \"Codex Live\",\n provider: \"Connected Codex\",\n description: \"Deep session integration\",\n available: false,\n unavailableReason: \"Connect Codex to use this voice model\",\n recommended: false,\n};\n\nconst REALTIME_MODEL_PROVIDERS = [\"OpenGeni\", \"Connected Codex\", \"Your Gateway\"] as const;\ntype RealtimeModelProvider = (typeof REALTIME_MODEL_PROVIDERS)[number];\n\nconst REALTIME_PROVIDER_META: Record<\n RealtimeModelProvider,\n { billingClass: BillingClass; hint: string }\n> = {\n OpenGeni: { billingClass: \"opengeni_credits\", hint: \"Will use credits\" },\n \"Connected Codex\": {\n billingClass: \"codex_subscription\",\n hint: \"ChatGPT / Codex plan\",\n },\n \"Your Gateway\": { billingClass: \"byok\", hint: \"Billed to your AI Gateway\" },\n};\nconst REALTIME_MODEL_STORAGE_PREFIX = \"opengeni:realtime-model\";\n\ntype RealtimeControllerClient = EmbeddedRealtimeSessionClientLike & SessionRealtimeClientLike;\n\nexport type SessionRealtimeControllerFactory = (\n options: CreateSessionRealtimeControllerOptions,\n) => SessionRealtimeController;\n\ntype AdmissionInput = {\n sessionStatus: SessionStatus;\n controlState: \"active\" | \"paused\";\n settlement: { state: string } | null;\n codexConnected: boolean;\n lifecycleActive: boolean;\n};\n\nexport function codexRealtimeAdmissionAllowed(input: AdmissionInput): boolean {\n return codexRealtimeAdmissionBlocker(input) === null;\n}\n\nexport function codexRealtimeAdmissionBlocker(input: AdmissionInput): string | null {\n if (input.sessionStatus === \"cancelled\") return \"This session was cancelled.\";\n if (input.controlState !== \"active\") return \"Resume this session before starting voice.\";\n if (input.settlement !== null) return \"Wait for the current session transition to finish.\";\n if (!input.codexConnected) return \"Connect Codex to use this voice model.\";\n if (input.lifecycleActive) return \"Voice is already active for this session.\";\n return null;\n}\n\nexport function useSessionRealtime(options: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n sessionId: string;\n sessionStatus: SessionStatus;\n effectiveControl: EffectiveSessionControl;\n events: SessionEvent[];\n eventsReady: boolean;\n codexConnected: boolean;\n model?: SessionRealtimeModel | undefined;\n modelAvailable?: boolean | undefined;\n modelUnavailableReason?: string | null | undefined;\n /** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */\n controllerFactory?: SessionRealtimeControllerFactory | undefined;\n}) {\n const { client, workspaceId } = useEmbeddedRealtimeSession({\n client: options.client,\n workspaceId: options.workspaceId,\n });\n const model = options.model ?? CODEX_LIVE_MODEL.id;\n const audioRef = useRef<HTMLAudioElement>(null);\n const controllerRef = useRef<SessionRealtimeController | null>(null);\n const controllerModelRef = useRef<SessionRealtimeModel | null>(null);\n const [snapshot, setSnapshot] = useState<SessionRealtimeControllerSnapshot>(() => ({\n status: hasStoredSessionRealtimeOwnerProof({ workspaceId, sessionId: options.sessionId, model })\n ? \"recovering\"\n : \"idle\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: null,\n error: null,\n }));\n const lifecycle = useMemo(\n () => projectSessionRealtimeLifecycle(options.events),\n [options.events],\n );\n const lifecycleRef = useRef(lifecycle);\n const eventsReadyRef = useRef(options.eventsReady);\n lifecycleRef.current = lifecycle;\n eventsReadyRef.current = options.eventsReady;\n const lifecycleActive = lifecycle?.state === \"active\";\n\n useEffect(() => {\n let disposed = false;\n let controller: SessionRealtimeController | null = null;\n let unsubscribe: (() => void) | null = null;\n void import(\"@opengeni/sdk/realtime\")\n .then(({ createSessionRealtimeController }) => {\n if (disposed) return;\n const controllerFactory = options.controllerFactory ?? createSessionRealtimeController;\n controller = controllerFactory({\n client,\n workspaceId,\n sessionId: options.sessionId,\n ...(audioRef.current ? { remoteAudio: audioRef.current } : {}),\n model,\n });\n controllerRef.current = controller;\n controllerModelRef.current = model;\n unsubscribe = controller.subscribe(setSnapshot);\n if (eventsReadyRef.current) {\n void controller.observeLifecycle(lifecycleRef.current).catch(() => undefined);\n }\n })\n .catch((error: unknown) => {\n if (disposed) return;\n setSnapshot({\n status: \"error\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: {\n kind: \"negotiation_failure\",\n message:\n error instanceof Error ? error.message : \"Codex realtime controller failed to load\",\n recoverable: false,\n connectionGeneration: 0,\n attempt: 0,\n },\n error:\n error instanceof Error ? error.message : \"Codex realtime controller failed to load\",\n });\n });\n return () => {\n disposed = true;\n unsubscribe?.();\n controller?.close();\n if (controllerRef.current === controller) {\n controllerRef.current = null;\n controllerModelRef.current = null;\n }\n };\n }, [client, model, options.controllerFactory, options.sessionId, workspaceId]);\n\n useEffect(() => {\n if (!options.eventsReady) return;\n void controllerRef.current?.observeLifecycle(lifecycle).catch(() => undefined);\n }, [lifecycle, options.eventsReady]);\n\n const start = useCallback(async () => {\n const controller = controllerRef.current;\n if (!controller || controllerModelRef.current !== model) {\n throw new Error(\"The selected voice model is still preparing\");\n }\n await controller.start();\n }, [model]);\n const stop = useCallback(async () => {\n await controllerRef.current?.stop();\n }, []);\n const retry = useCallback(async () => {\n await controllerRef.current?.retry();\n }, []);\n const retryAudibleOutput = useCallback(async () => {\n await controllerRef.current?.retryAudibleOutput();\n }, []);\n const setInputMuted = useCallback((muted: boolean) => {\n controllerRef.current?.setInputMuted(muted);\n }, []);\n const setOutputMuted = useCallback((muted: boolean) => {\n controllerRef.current?.setOutputMuted(muted);\n }, []);\n const admissionInput = {\n sessionStatus: options.sessionStatus,\n controlState: options.effectiveControl.state,\n settlement: options.effectiveControl.settlement,\n codexConnected: options.modelAvailable ?? options.codexConnected,\n lifecycleActive,\n } satisfies AdmissionInput;\n const admissionBlocker =\n options.modelAvailable === false\n ? (options.modelUnavailableReason ?? \"This voice model is unavailable.\")\n : codexRealtimeAdmissionBlocker(admissionInput);\n const canStart =\n controllerRef.current !== null &&\n controllerModelRef.current === model &&\n [\"idle\", \"error\"].includes(snapshot.status) &&\n admissionBlocker === null;\n\n return {\n snapshot,\n lifecycleActive,\n canStart,\n admissionBlocker,\n codexConnected: options.codexConnected,\n audioRef,\n start,\n stop,\n retry,\n retryAudibleOutput,\n setInputMuted,\n setOutputMuted,\n };\n}\n\nexport function SessionRealtimeControl(props: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n sessionId: string;\n sessionStatus: SessionStatus;\n effectiveControl: EffectiveSessionControl;\n events: SessionEvent[];\n eventsReady: boolean;\n codexConnected: boolean;\n realtimeAutostartModel?: SessionRealtimeModel | undefined;\n onRealtimeAutostartConsumed?: (() => void) | undefined;\n /** Host hides dictate (and can choreograph layout) while realtime voice is live. */\n onVoiceActiveChange?: ((active: boolean) => void) | undefined;\n /** Deterministic browser-test/demo seam. Production hosts should use the SDK default. */\n controllerFactory?: SessionRealtimeControllerFactory | undefined;\n}) {\n const lifecycle = useMemo(() => projectSessionRealtimeLifecycle(props.events), [props.events]);\n const selection = useRealtimeModelSelection({\n client: props.client,\n workspaceId: props.workspaceId,\n codexConnected: props.codexConnected,\n activeModel: lifecycle?.state === \"active\" ? lifecycle.model : null,\n });\n const selectedModel = selection.selectedModel;\n const realtime = useSessionRealtime({\n ...props,\n model: selectedModel.id,\n modelAvailable: selectedModel.available,\n modelUnavailableReason: selectedModel.unavailableReason,\n });\n const autostartModelRef = useRef(props.realtimeAutostartModel ?? null);\n const autostartStartedRef = useRef(false);\n\n const { models, selectedModel: selectedRealtimeModel, selectModel } = selection;\n const { canStart, start } = realtime;\n const onRealtimeAutostartConsumed = props.onRealtimeAutostartConsumed;\n const onVoiceActiveChange = props.onVoiceActiveChange;\n const voiceActive = realtime.snapshot.mode?.state === \"active\";\n\n useEffect(() => {\n onVoiceActiveChange?.(voiceActive);\n }, [onVoiceActiveChange, voiceActive]);\n\n useEffect(() => {\n const pending = autostartModelRef.current;\n if (!pending || autostartStartedRef.current || lifecycle?.state === \"active\") return;\n const available = models.some((model) => model.id === pending && model.available);\n if (!available) return;\n if (selectedRealtimeModel.id !== pending) {\n selectModel(pending);\n return;\n }\n if (!canStart) return;\n autostartStartedRef.current = true;\n void start()\n .then(() => {\n autostartModelRef.current = null;\n onRealtimeAutostartConsumed?.();\n })\n .catch(() => {\n autostartStartedRef.current = false;\n });\n }, [\n canStart,\n lifecycle?.state,\n models,\n props.sessionId,\n props.workspaceId,\n onRealtimeAutostartConsumed,\n selectModel,\n selectedRealtimeModel.id,\n start,\n ]);\n\n return (\n <RealtimeVoiceControl\n snapshot={realtime.snapshot}\n canStart={realtime.canStart}\n admissionBlocker={realtime.admissionBlocker}\n modelAvailable={selectedModel.available}\n menuSide=\"top\"\n audioRef={realtime.audioRef}\n selectedModel={selectedModel}\n models={selection.models}\n onSelectModel={realtime.lifecycleActive ? undefined : selection.selectModel}\n onStart={realtime.start}\n onStop={realtime.stop}\n onRetry={realtime.retry}\n onRetryAudibleOutput={realtime.retryAudibleOutput}\n onSetInputMuted={realtime.setInputMuted}\n onSetOutputMuted={realtime.setOutputMuted}\n />\n );\n}\n\nexport function useRealtimeModelSelection(options: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n codexConnected: boolean;\n activeModel?: SessionRealtimeModel | null | undefined;\n}) {\n const { client, workspaceId } = useEmbeddedRealtimeSession({\n client: options.client,\n workspaceId: options.workspaceId,\n });\n const activeModel = options.activeModel;\n const [catalog, setCatalog] = useState<RealtimeModelOption[]>(() => [\n {\n ...CODEX_LIVE_MODEL,\n available: options.codexConnected,\n unavailableReason: options.codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,\n },\n ]);\n const [selectedModelId, setSelectedModelId] = useState<SessionRealtimeModel>(\n () => readRealtimeModelPreference(workspaceId) ?? CODEX_LIVE_MODEL.id,\n );\n\n useEffect(() => {\n let disposed = false;\n const load = client.getWorkspaceRealtimeModelCatalog;\n if (!load) return;\n void load\n .call(client, workspaceId)\n .then((response) => {\n if (disposed) return;\n const models = response.models.map(toRealtimeModelOption);\n setCatalog(models);\n setSelectedModelId((current) => {\n if (models.some((model) => model.id === current && model.available)) return current;\n return models.find((model) => model.available)?.id ?? CODEX_LIVE_MODEL.id;\n });\n })\n .catch(() => undefined);\n return () => {\n disposed = true;\n };\n }, [client, workspaceId]);\n\n useEffect(() => {\n if (activeModel) setSelectedModelId(activeModel);\n }, [activeModel]);\n\n const selectedModel =\n catalog.find((model) => model.id === selectedModelId) ??\n ({\n ...CODEX_LIVE_MODEL,\n available: options.codexConnected,\n unavailableReason: options.codexConnected ? null : CODEX_LIVE_MODEL.unavailableReason,\n } satisfies RealtimeModelOption);\n const selectModel = useCallback(\n (value: string) => {\n const model = catalog.find((candidate) => candidate.id === value);\n if (!model || !model.available || activeModel) return;\n setSelectedModelId(model.id);\n writeRealtimeModelPreference(workspaceId, model.id);\n },\n [activeModel, catalog, workspaceId],\n );\n\n return { models: catalog, selectedModel, selectModel };\n}\n\nconst IDLE_REALTIME_SNAPSHOT: SessionRealtimeControllerSnapshot = {\n status: \"idle\",\n realtimeId: null,\n mode: null,\n bridge: null,\n microphone: \"inactive\",\n inputMuted: false,\n audibleOutput: \"inactive\",\n outputMuted: false,\n connectionGeneration: 0,\n reconnectAttempt: 0,\n diagnostic: null,\n error: null,\n};\n\nexport function NewSessionRealtimeControl(props: {\n client?: RealtimeControllerClient | undefined;\n workspaceId?: string | undefined;\n codexConnected: boolean;\n disabled?: boolean | undefined;\n disabledReason?: string | null | undefined;\n onStart: (model: SessionRealtimeModel) => Promise<boolean>;\n /** Parent-owned selection (e.g. shared with the mobile “+” voice-model panel). */\n models?: readonly RealtimeModelOption[] | undefined;\n selectedModel?: RealtimeModelOption | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n /**\n * `split` attaches the model chevron at every breakpoint (public default).\n * `split-desktop` hides it below `sm` when the host owns mobile selection (e.g. “+”).\n * `none` never attaches a model menu to the bar button.\n */\n modelMenu?: \"split\" | \"split-desktop\" | \"none\" | undefined;\n}) {\n const internalSelection = useRealtimeModelSelection({\n client: props.client,\n workspaceId: props.workspaceId,\n codexConnected: props.codexConnected,\n });\n const selection =\n props.models && props.selectedModel && props.onSelectModel\n ? {\n models: props.models,\n selectedModel: props.selectedModel,\n selectModel: props.onSelectModel,\n }\n : internalSelection;\n const audioRef = useRef<HTMLAudioElement>(null);\n const [starting, setStarting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const snapshot = useMemo<SessionRealtimeControllerSnapshot>(\n () =>\n starting\n ? { ...IDLE_REALTIME_SNAPSHOT, status: \"starting\" }\n : error\n ? { ...IDLE_REALTIME_SNAPSHOT, status: \"error\", error }\n : IDLE_REALTIME_SNAPSHOT,\n [error, starting],\n );\n const canStart = selection.selectedModel.available && props.disabled !== true && !starting;\n const onStart = props.onStart;\n const selectedModelId = selection.selectedModel.id;\n const start = useCallback(async () => {\n if (!canStart) return;\n setStarting(true);\n setError(null);\n try {\n const started = await onStart(selectedModelId);\n if (!started) setError(\"The voice session could not be created.\");\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause));\n } finally {\n setStarting(false);\n }\n }, [canStart, onStart, selectedModelId]);\n\n return (\n <RealtimeVoiceControl\n snapshot={snapshot}\n canStart={canStart}\n admissionBlocker={selection.selectedModel.unavailableReason ?? props.disabledReason ?? null}\n modelAvailable={selection.selectedModel.available}\n selectionDisabled={starting}\n menuSide=\"bottom\"\n modelMenu={props.modelMenu ?? \"split\"}\n showDiagnostics={false}\n audioRef={audioRef}\n selectedModel={selection.selectedModel}\n models={selection.models}\n onSelectModel={selection.selectModel}\n onStart={start}\n onStop={async () => undefined}\n onRetry={start}\n onRetryAudibleOutput={async () => undefined}\n onSetInputMuted={() => undefined}\n onSetOutputMuted={() => undefined}\n />\n );\n}\n\nexport function RealtimeVoiceControl(props: {\n snapshot: SessionRealtimeControllerSnapshot;\n canStart: boolean;\n admissionBlocker?: string | null | undefined;\n modelAvailable: boolean;\n selectionDisabled?: boolean | undefined;\n /** Prefer `bottom` on home/new-session; `top` for the docked session composer. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /**\n * `split` = start + model chevron at every breakpoint (public SDK default).\n * `split-desktop` = chevron from `sm` up; below that the host owns selection.\n * `none` = start button only.\n */\n modelMenu?: \"split\" | \"split-desktop\" | \"none\" | undefined;\n showDiagnostics?: boolean;\n /** Extra classes on the in-bar mute cluster (e.g. `max-sm:hidden` when mutes live in “+”). */\n muteControlsClassName?: string | undefined;\n audioRef: RefObject<HTMLAudioElement | null>;\n selectedModel?: RealtimeModelOption | undefined;\n models?: readonly RealtimeModelOption[] | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n onStart: () => Promise<void>;\n onStop: () => Promise<void>;\n onRetry: () => Promise<void>;\n onRetryAudibleOutput: () => Promise<void>;\n onSetInputMuted: (muted: boolean) => void;\n onSetOutputMuted: (muted: boolean) => void;\n}) {\n const reduceMotion = useReducedMotion();\n const selectedModel = props.selectedModel ?? {\n ...CODEX_LIVE_MODEL,\n available: props.modelAvailable,\n unavailableReason: props.modelAvailable ? null : CODEX_LIVE_MODEL.unavailableReason,\n };\n const models = props.models ?? [selectedModel];\n const status = statusContent(\n props.snapshot,\n props.modelAvailable,\n props.canStart,\n props.admissionBlocker ?? null,\n selectedModel.label,\n );\n const modeOwned = props.snapshot.mode?.state === \"active\";\n const retryConnection =\n modeOwned &&\n props.snapshot.status !== \"stopping\" &&\n props.snapshot.status !== \"active\" &&\n props.snapshot.diagnostic?.recoverable === true;\n const audioBlocked = props.snapshot.audibleOutput === \"blocked\" && !props.snapshot.outputMuted;\n const mainDisabled =\n props.snapshot.status === \"stopping\" ||\n props.snapshot.status === \"lost_owner\" ||\n (!modeOwned && !props.canStart) ||\n (props.snapshot.status === \"starting\" && !modeOwned);\n const mainLabel = audioBlocked\n ? \"Resume voice audio\"\n : retryConnection\n ? \"Retry voice connection\"\n : modeOwned\n ? \"End voice conversation\"\n : `Start voice with ${selectedModel.label}`;\n const runMainAction = audioBlocked\n ? props.onRetryAudibleOutput\n : retryConnection\n ? props.onRetry\n : modeOwned\n ? props.onStop\n : props.onStart;\n const diagnosticsVisible = props.showDiagnostics ?? import.meta.env.DEV;\n const modelMenu = props.modelMenu ?? \"split\";\n const showAttachedModelMenu = modelMenu === \"split\" || modelMenu === \"split-desktop\";\n const desktopOnlyModelMenu = modelMenu === \"split-desktop\";\n const [pickerOpen, setPickerOpen] = useState(false);\n const [pickerProvider, setPickerProvider] = useState<RealtimeModelProvider | null>(\n selectedModel.provider,\n );\n const [pickerDirection, setPickerDirection] = useState<1 | -1>(1);\n\n useEffect(() => {\n if (!pickerOpen) setPickerProvider(selectedModel.provider);\n }, [pickerOpen, selectedModel.provider]);\n\n return (\n <div\n role=\"group\"\n aria-label=\"Realtime voice\"\n data-picker-side={props.menuSide ?? \"top\"}\n className=\"inline-flex shrink-0 items-center\"\n >\n <audio ref={props.audioRef} autoPlay className=\"hidden\" aria-hidden=\"true\" />\n <RealtimeComposerGlow phase={status.phase} reduceMotion={reduceMotion === true} />\n <AnimatePresence initial={false}>\n {modeOwned ? (\n <motion.div\n key=\"realtime-mute-controls\"\n data-testid=\"realtime-mute-controls\"\n // After dictate collapses and the model slides left, bloom mutes\n // out from the primary voice control. Exit is immediate (no delay).\n initial={reduceMotion ? false : { opacity: 0, width: 0 }}\n animate={{\n opacity: 1,\n width: \"auto\",\n transition: reduceMotion\n ? { duration: 0 }\n : { delay: 0.3, duration: 0.48, ease: [0.22, 1, 0.36, 1] },\n }}\n {...(reduceMotion\n ? {}\n : {\n exit: {\n opacity: 0,\n width: 0,\n transition: { duration: 0.28, ease: [0.4, 0, 1, 1] },\n },\n })}\n className={cn(\n \"inline-flex shrink-0 items-center overflow-hidden\",\n props.muteControlsClassName,\n )}\n >\n <motion.div\n className=\"inline-flex items-center gap-0.5 pr-1\"\n initial={reduceMotion ? false : { x: 18, opacity: 0 }}\n animate={{\n x: 0,\n opacity: 1,\n transition: reduceMotion\n ? { duration: 0 }\n : { delay: 0.32, type: \"spring\", stiffness: 320, damping: 30, mass: 0.8 },\n }}\n {...(reduceMotion\n ? {}\n : {\n exit: {\n x: 14,\n opacity: 0,\n transition: { duration: 0.22, ease: [0.4, 0, 1, 1] },\n },\n })}\n >\n <RealtimeMuteButton\n muted={props.snapshot.inputMuted}\n kind=\"microphone\"\n reduceMotion={reduceMotion === true}\n enterDelay={reduceMotion ? 0 : 0.34}\n onToggle={() => props.onSetInputMuted(!props.snapshot.inputMuted)}\n />\n <RealtimeMuteButton\n muted={props.snapshot.outputMuted}\n kind=\"output\"\n reduceMotion={reduceMotion === true}\n enterDelay={reduceMotion ? 0 : 0.4}\n onToggle={() => props.onSetOutputMuted(!props.snapshot.outputMuted)}\n />\n </motion.div>\n </motion.div>\n ) : null}\n </AnimatePresence>\n <div className=\"inline-flex shrink-0 items-center\">\n <motion.button\n type=\"button\"\n data-testid=\"realtime-primary-action\"\n data-phase={status.phase}\n aria-label={mainLabel}\n aria-pressed={modeOwned}\n title={mainLabel}\n disabled={mainDisabled}\n {...(reduceMotion ? {} : { whileTap: { scale: 0.92 } })}\n transition={{ type: \"spring\", stiffness: 520, damping: 30 }}\n onClick={() => void runMainAction().catch(() => undefined)}\n className={cn(\n // Match transcription mic: ghost icon when idle; filled only when live.\n // Public contract: coarse pointers keep a 44px target; split menu uses\n // left-only rounding at `sm+` where the chevron attaches.\n \"relative inline-flex size-8 items-center justify-center outline-none\",\n \"rounded-og-md transition-[background-color,border-color,color,box-shadow] duration-200 ease-og-out\",\n \"focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-og-accent/45\",\n \"disabled:cursor-not-allowed disabled:opacity-45 pointer-coarse:size-11\",\n showAttachedModelMenu &&\n (desktopOnlyModelMenu\n ? \"sm:rounded-l-og-md sm:rounded-r-none\"\n : \"rounded-l-og-md rounded-r-none\"),\n voiceButtonTone(status.phase),\n )}\n >\n <RealtimeActionGlyph\n phase={status.phase}\n audioBlocked={audioBlocked}\n reduceMotion={reduceMotion === true}\n />\n </motion.button>\n\n {showAttachedModelMenu ? (\n <DropdownMenu open={pickerOpen} onOpenChange={setPickerOpen}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n aria-label=\"Choose voice model and options\"\n title={`Voice model: ${selectedModel.label}`}\n disabled={props.selectionDisabled}\n className={cn(\n // Keep the split trigger compact on desktop while preserving a\n // full 44px mobile target independent of pointer-media support.\n \"inline-flex h-11 w-11 items-center justify-center rounded-r-og-md outline-none sm:h-8 sm:w-6\",\n \"transition-[background-color,border-color,color] duration-200 ease-og-out\",\n \"focus-visible:z-10 focus-visible:ring-2 focus-visible:ring-og-accent/45\",\n desktopOnlyModelMenu && \"hidden sm:inline-flex\",\n voiceChevronTone(status.phase),\n )}\n >\n <ChevronDownIcon className=\"size-3\" aria-hidden />\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent\n side={props.menuSide ?? \"top\"}\n align=\"end\"\n sideOffset={8}\n collisionPadding={12}\n className=\"flex w-72 max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] flex-col overflow-hidden rounded-xl border-border bg-surface p-1.5 shadow-xl\"\n onCloseAutoFocus={(event) => event.preventDefault()}\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <RealtimeModelPickerMenu\n models={models}\n selectedModel={selectedModel}\n provider={pickerProvider}\n direction={pickerDirection}\n disabled={\n props.selectionDisabled === true || props.snapshot.mode?.state === \"active\"\n }\n onProviderChange={(provider, direction) => {\n setPickerDirection(direction);\n setPickerProvider(provider);\n }}\n onSelect={(modelId) => {\n props.onSelectModel?.(modelId);\n setPickerOpen(false);\n }}\n />\n </div>\n\n {status.phase !== \"idle\" ? (\n <>\n <DropdownMenuSeparator className=\"mx-1 bg-og-border\" />\n <div className=\"px-2.5 py-2\" role=\"status\" aria-live=\"polite\">\n <div className=\"flex items-center gap-2 text-og-sm font-medium text-og-fg\">\n <RealtimeStatusDot\n phase={status.phase}\n reduceMotion={reduceMotion === true}\n />\n {status.label}\n </div>\n <p className=\"mt-1 text-og-xs leading-5 text-og-fg-subtle\">{status.detail}</p>\n </div>\n </>\n ) : null}\n\n {audioBlocked ? (\n <DropdownMenuItem\n className=\"rounded-og-md\"\n onSelect={() => void props.onRetryAudibleOutput().catch(() => undefined)}\n >\n <Volume2Icon />\n Resume audio\n </DropdownMenuItem>\n ) : null}\n {retryConnection ? (\n <DropdownMenuItem\n className=\"rounded-og-md\"\n onSelect={() => void props.onRetry().catch(() => undefined)}\n >\n <RotateCcwIcon />\n Retry connection\n </DropdownMenuItem>\n ) : null}\n {modeOwned && props.snapshot.status !== \"lost_owner\" ? (\n <DropdownMenuItem\n variant=\"destructive\"\n className=\"rounded-og-md\"\n disabled={props.snapshot.status === \"stopping\"}\n onSelect={() => void props.onStop().catch(() => undefined)}\n >\n <SquareIcon />\n End voice conversation\n </DropdownMenuItem>\n ) : null}\n\n {diagnosticsVisible ? <RealtimeDiagnosticsMenu snapshot={props.snapshot} /> : null}\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {status.label}. {status.detail}\n </span>\n {props.snapshot.error ? (\n <span className=\"sr-only\" role=\"alert\">\n {props.snapshot.error}\n </span>\n ) : null}\n </div>\n );\n}\n\n/** Drill-in body for the mobile composer “+” menu — same catalog as the desktop chevron. */\nexport function RealtimeVoiceModelPanel(props: {\n models: readonly RealtimeModelOption[];\n selectedModel: RealtimeModelOption;\n disabled?: boolean | undefined;\n leading?: ReactNode | undefined;\n onSelect: (modelId: SessionRealtimeModel) => void;\n}) {\n const [provider, setProvider] = useState<RealtimeModelProvider | null>(null);\n const [direction, setDirection] = useState<1 | -1>(1);\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col\" data-testid=\"realtime-voice-model-panel\">\n <div className=\"flex shrink-0 items-start gap-1 px-2 pt-1 pb-1.5\">\n {props.leading}\n <div className=\"min-w-0\">\n <div className=\"text-og-menu font-medium text-og-fg\">Voice model</div>\n <p className=\"mt-0.5 text-og-control text-og-fg-subtle\">\n Used when you start a voice conversation.\n </p>\n </div>\n </div>\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-1.5 pb-1.5\">\n <RealtimeModelPickerMenu\n models={props.models}\n selectedModel={props.selectedModel}\n provider={provider}\n direction={direction}\n disabled={props.disabled === true}\n onProviderChange={(next, nextDirection) => {\n setDirection(nextDirection);\n setProvider(next);\n }}\n onSelect={props.onSelect}\n />\n </div>\n </div>\n );\n}\n\nfunction RealtimeMuteButton(props: {\n muted: boolean;\n kind: \"microphone\" | \"output\";\n reduceMotion: boolean;\n enterDelay?: number | undefined;\n onToggle(): void;\n}) {\n const microphone = props.kind === \"microphone\";\n const label = microphone\n ? props.muted\n ? \"Unmute microphone\"\n : \"Mute microphone\"\n : props.muted\n ? \"Unmute voice audio\"\n : \"Mute voice audio\";\n const Icon = microphone\n ? props.muted\n ? MicOffIcon\n : MicIcon\n : props.muted\n ? VolumeXIcon\n : Volume2Icon;\n\n return (\n <motion.button\n type=\"button\"\n aria-label={label}\n aria-pressed={props.muted}\n title={label}\n initial={props.reduceMotion ? false : { scale: 0.72, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n transition={{\n delay: props.enterDelay ?? 0,\n type: \"spring\",\n stiffness: 420,\n damping: 26,\n mass: 0.7,\n }}\n {...(props.reduceMotion ? {} : { whileTap: { scale: 0.92 } })}\n onClick={props.onToggle}\n className={cn(\n \"inline-flex size-8 shrink-0 items-center justify-center rounded-og-md outline-none\",\n \"transition-[background-color,border-color,color] duration-150 ease-og-out\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent/45 pointer-coarse:size-11\",\n props.muted\n ? \"border border-og-accent/35 bg-og-accent-soft text-og-accent\"\n : \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\",\n )}\n >\n <Icon className=\"size-3.5\" aria-hidden />\n </motion.button>\n );\n}\n\nexport function RealtimeModelPickerMenu(props: {\n models: readonly RealtimeModelOption[];\n selectedModel: RealtimeModelOption;\n provider: RealtimeModelProvider | null;\n direction: 1 | -1;\n disabled: boolean;\n onProviderChange: (provider: RealtimeModelProvider | null, direction: 1 | -1) => void;\n onSelect: (modelId: SessionRealtimeModel) => void;\n}) {\n const providers = REALTIME_MODEL_PROVIDERS.filter((provider) =>\n props.models.some((model) => model.provider === provider),\n );\n const pageKey = props.provider ? `voice-models:${props.provider}` : \"voice-providers\";\n const body = props.provider ? (\n <div data-testid=\"realtime-model-picker-models\">\n <PickerBackHeader\n label={props.provider}\n icon={\n <BillingClassMark\n billingClass={REALTIME_PROVIDER_META[props.provider].billingClass}\n aria-label=\"\"\n />\n }\n onBack={() => props.onProviderChange(null, -1)}\n />\n <div className=\"flex flex-col gap-0.5\">\n {props.models\n .filter((model) => model.provider === props.provider)\n .map((model) => {\n const selected = model.id === props.selectedModel.id;\n return (\n <PickerNavRow\n key={model.id}\n label={model.label}\n hint={\n model.unavailableReason ??\n (model.recommended ? `Recommended · ${model.description}` : model.description)\n }\n disabled={props.disabled || !model.available}\n title={model.unavailableReason ?? model.description}\n active={selected}\n showChevron={false}\n trailing={selected ? <CheckIcon className=\"size-3.5\" aria-hidden /> : undefined}\n testId={`realtime-model-choice-${model.id}`}\n onClick={() => props.onSelect(model.id)}\n />\n );\n })}\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"realtime-model-picker-providers\">\n {providers.map((provider) => {\n const meta = REALTIME_PROVIDER_META[provider];\n return (\n <PickerNavRow\n key={provider}\n label={provider}\n hint={meta.hint}\n icon={<BillingClassMark billingClass={meta.billingClass} aria-label=\"\" />}\n active={props.selectedModel.provider === provider}\n testId={`realtime-model-provider-${meta.billingClass}`}\n onClick={() => props.onProviderChange(provider, 1)}\n />\n );\n })}\n </div>\n );\n\n return (\n <div className=\"relative overflow-hidden\" data-testid=\"realtime-model-picker-menu\">\n <PickerAnimatedPage pageKey={pageKey} direction={props.direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\nfunction RealtimeDiagnosticsMenu({ snapshot }: { snapshot: SessionRealtimeControllerSnapshot }) {\n const bridge = snapshot.bridge;\n const rows = [\n [\"controller\", snapshot.status],\n [\"microphone\", snapshot.microphone],\n [\"microphone muted\", String(snapshot.inputMuted)],\n [\"audio\", snapshot.audibleOutput],\n [\"audio muted\", String(snapshot.outputMuted)],\n [\"generation\", String(snapshot.connectionGeneration)],\n [\"reconnect attempt\", String(snapshot.reconnectAttempt)],\n [\"mode\", snapshot.mode?.state ?? \"none\"],\n [\"mode version\", snapshot.mode ? String(snapshot.mode.version) : \"—\"],\n [\"connection epoch\", snapshot.mode ? String(snapshot.mode.connectionEpoch) : \"—\"],\n [\"provider started\", bridge ? String(bridge.providerStarted) : \"false\"],\n [\"provider speaking\", bridge ? String(bridge.speaking) : \"false\"],\n [\"delegation\", bridge?.activeDelegationId ? \"active\" : \"none\"],\n [\"pending durable events\", bridge ? String(bridge.pendingInbound) : \"0\"],\n [\"pending bytes\", bridge ? String(bridge.pendingInboundBytes) : \"0\"],\n [\"client ack\", bridge?.clientAckThroughSequence?.toString() ?? \"—\"],\n [\"ignored provider events\", bridge ? String(bridge.ignoredEventCount) : \"0\"],\n [\"last ignored event\", bridge?.lastIgnoredEventType ?? \"none\"],\n [\"bridge error\", bridge?.lastError ?? \"none\"],\n [\"diagnostic\", snapshot.diagnostic?.kind ?? \"none\"],\n ] as const;\n\n return (\n <>\n <DropdownMenuSeparator className=\"mx-1 bg-og-border\" />\n <DropdownMenuSub>\n <DropdownMenuSubTrigger className=\"rounded-og-md text-og-fg-muted\">\n Realtime diagnostics\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-80 w-72 overflow-y-auto rounded-og-lg border-og-border bg-og-surface-1 p-1.5 text-og-fg shadow-og-lg\">\n {rows.map(([label, value]) => (\n <DropdownMenuItem\n key={label}\n disabled\n className=\"grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] gap-3 rounded-og-md font-mono text-[11px] opacity-100\"\n >\n <span className=\"truncate text-og-fg-subtle\">{label}</span>\n <span className=\"truncate text-right text-og-fg\">{value}</span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n </>\n );\n}\n\ntype RealtimeVisualPhase =\n | \"idle\"\n | \"connecting\"\n | \"listening\"\n | \"speaking\"\n | \"reconnecting\"\n | \"blocked\"\n | \"unavailable\"\n | \"error\"\n | \"elsewhere\"\n | \"stopping\";\n\nfunction statusContent(\n snapshot: SessionRealtimeControllerSnapshot,\n modelAvailable: boolean,\n canStart: boolean,\n admissionBlocker: string | null,\n modelLabel: string,\n): { phase: RealtimeVisualPhase; label: string; detail: string } {\n if (snapshot.audibleOutput === \"blocked\" && !snapshot.outputMuted) {\n return {\n phase: \"blocked\",\n label: \"Audio output blocked\",\n detail: \"Resume audio to hear the existing voice connection.\",\n };\n }\n switch (snapshot.status) {\n case \"starting\":\n return {\n phase: \"connecting\",\n label: \"Connecting…\",\n detail: \"Preparing your microphone and live voice.\",\n };\n case \"active\":\n return snapshot.bridge?.speaking\n ? {\n phase: \"speaking\",\n label: \"Speaking\",\n detail: \"Interrupt naturally whenever you want.\",\n }\n : {\n phase: \"listening\",\n label: \"Listening\",\n detail: \"Voice and typed messages share this session.\",\n };\n case \"stopping\":\n return {\n phase: \"stopping\",\n label: \"Ending voice…\",\n detail: \"Saving the remaining conversation context.\",\n };\n case \"recovering\":\n return {\n phase: \"reconnecting\",\n label: \"Reconnecting…\",\n detail: snapshot.error ?? \"Keeping this voice conversation connected.\",\n };\n case \"lost_owner\":\n return {\n phase: \"elsewhere\",\n label: \"Voice active elsewhere\",\n detail: \"Return to the browser that started it, or wait for that connection to expire.\",\n };\n case \"error\":\n return {\n phase: \"error\",\n label: \"Voice unavailable\",\n detail: snapshot.error ?? \"The connection could not be started.\",\n };\n case \"idle\":\n if (!modelAvailable) {\n return {\n phase: \"unavailable\",\n label: \"Voice model unavailable\",\n detail: admissionBlocker ?? \"Choose another voice model.\",\n };\n }\n if (!canStart && admissionBlocker) {\n return { phase: \"unavailable\", label: \"Voice unavailable\", detail: admissionBlocker };\n }\n return {\n phase: \"idle\",\n label: \"Start voice\",\n detail: `Talk live with ${modelLabel}.`,\n };\n }\n}\n\nfunction RealtimeComposerGlow(props: { phase: RealtimeVisualPhase; reduceMotion: boolean }) {\n const visible = [\n \"connecting\",\n \"listening\",\n \"speaking\",\n \"reconnecting\",\n \"blocked\",\n \"error\",\n \"stopping\",\n ].includes(props.phase);\n return (\n <AnimatePresence initial={false}>\n {visible ? (\n <motion.span\n aria-hidden\n key={props.phase}\n initial={{ opacity: 0 }}\n animate={\n props.reduceMotion\n ? { opacity: 0.48 }\n : props.phase === \"speaking\"\n ? { opacity: [0.42, 0.82, 0.52] }\n : props.phase === \"listening\"\n ? { opacity: [0.34, 0.62, 0.42] }\n : { opacity: [0.28, 0.58, 0.28] }\n }\n exit={{ opacity: 0 }}\n transition={\n props.reduceMotion\n ? { duration: 0.12 }\n : {\n duration: props.phase === \"speaking\" ? 1.15 : 2.1,\n ease: \"easeInOut\",\n repeat: Number.POSITIVE_INFINITY,\n }\n }\n className={cn(\n \"pointer-events-none absolute -inset-px z-[1] rounded-og-lg ring-1 ring-inset shadow-og-glow\",\n props.phase === \"speaking\"\n ? \"ring-og-status-running/50\"\n : props.phase === \"blocked\" || props.phase === \"error\"\n ? \"ring-og-status-waiting/45\"\n : \"ring-og-accent/45\",\n )}\n />\n ) : null}\n </AnimatePresence>\n );\n}\n\nfunction RealtimeActionGlyph(props: {\n phase: RealtimeVisualPhase;\n audioBlocked: boolean;\n reduceMotion: boolean;\n}) {\n if ([\"connecting\", \"reconnecting\", \"stopping\"].includes(props.phase)) {\n return <LoaderCircleIcon className=\"size-3.5 animate-og-spin\" aria-hidden />;\n }\n if (props.phase === \"blocked\") {\n return props.audioBlocked ? (\n <Volume2Icon className=\"size-3.5\" aria-hidden />\n ) : (\n <CircleAlertIcon className=\"size-3.5\" aria-hidden />\n );\n }\n if (props.phase === \"error\" || props.phase === \"elsewhere\" || props.phase === \"unavailable\") {\n return <CircleAlertIcon className=\"size-3.5\" aria-hidden />;\n }\n if (props.phase === \"listening\" || props.phase === \"speaking\") {\n return (\n <span className=\"relative inline-flex size-4 items-center justify-center\">\n {!props.reduceMotion ? (\n <motion.span\n aria-hidden\n className=\"absolute inset-0 rounded-full border border-current\"\n animate={{ opacity: [0.65, 0], scale: [0.65, 1.45] }}\n transition={{\n duration: props.phase === \"speaking\" ? 0.9 : 1.5,\n ease: \"easeOut\",\n repeat: Number.POSITIVE_INFINITY,\n }}\n />\n ) : null}\n <SquareIcon className=\"relative size-2.5 fill-current\" aria-hidden />\n </span>\n );\n }\n return <AudioLinesIcon className=\"size-4\" aria-hidden />;\n}\n\nfunction RealtimeStatusDot(props: { phase: RealtimeVisualPhase; reduceMotion: boolean }) {\n const live = props.phase === \"listening\" || props.phase === \"speaking\";\n return (\n <span className=\"relative inline-flex size-2 shrink-0\" aria-hidden>\n {live && !props.reduceMotion ? (\n <motion.span\n className=\"absolute inset-0 rounded-full bg-og-status-running\"\n animate={{ opacity: [0.45, 0], scale: [1, 2.2] }}\n transition={{ duration: 1.4, ease: \"easeOut\", repeat: Number.POSITIVE_INFINITY }}\n />\n ) : null}\n <span\n className={cn(\n \"relative size-2 rounded-full\",\n live\n ? \"bg-og-status-running\"\n : props.phase === \"blocked\" || props.phase === \"error\"\n ? \"bg-og-status-waiting\"\n : props.phase === \"elsewhere\" || props.phase === \"unavailable\"\n ? \"bg-og-fg-subtle\"\n : \"bg-og-accent\",\n )}\n />\n </span>\n );\n}\n\nfunction voiceButtonTone(phase: RealtimeVisualPhase): string {\n if (phase === \"listening\" || phase === \"speaking\") {\n return \"border border-og-accent/45 bg-og-accent text-og-accent-fg shadow-og-sm hover:bg-og-accent-strong\";\n }\n if (phase === \"blocked\" || phase === \"error\") {\n return \"border border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting hover:bg-og-status-waiting/15\";\n }\n if (phase === \"connecting\" || phase === \"reconnecting\" || phase === \"stopping\") {\n return \"border border-og-accent/35 bg-og-accent-soft text-og-accent\";\n }\n // Idle: same ghost treatment as the composer transcription mic — no box.\n return \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\";\n}\n\nfunction voiceChevronTone(phase: RealtimeVisualPhase): string {\n if (phase === \"listening\" || phase === \"speaking\") {\n return \"border border-l-0 border-og-accent/45 bg-og-accent text-og-accent-fg hover:bg-og-accent-strong\";\n }\n if (phase === \"blocked\" || phase === \"error\") {\n return \"border border-l-0 border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting hover:bg-og-status-waiting/15\";\n }\n if (phase === \"connecting\" || phase === \"reconnecting\" || phase === \"stopping\") {\n return \"border border-l-0 border-og-accent/35 bg-og-accent-soft text-og-accent\";\n }\n return \"text-og-fg-muted hover:bg-og-surface-2 hover:text-og-fg\";\n}\n\nfunction toRealtimeModelOption(model: WorkspaceRealtimeModelCatalogItem): RealtimeModelOption {\n return { ...model };\n}\n\nfunction readRealtimeModelPreference(workspaceId: string): SessionRealtimeModel | null {\n if (typeof localStorage === \"undefined\") return null;\n try {\n const value = localStorage.getItem(`${REALTIME_MODEL_STORAGE_PREFIX}:${workspaceId}`);\n return isRealtimeModel(value) ? value : null;\n } catch {\n return null;\n }\n}\n\nfunction writeRealtimeModelPreference(workspaceId: string, model: SessionRealtimeModel): void {\n if (typeof localStorage === \"undefined\") return;\n try {\n localStorage.setItem(`${REALTIME_MODEL_STORAGE_PREFIX}:${workspaceId}`, model);\n } catch {\n // Voice selection still works when browser storage is unavailable.\n }\n}\n\nfunction isRealtimeModel(value: string | null): value is SessionRealtimeModel {\n return (\n value === \"gpt-live-1-boulder-alpha\" ||\n value === \"opengeni-gateway/openai/gpt-realtime-2.1\" ||\n value === \"opengeni-gateway/openai/gpt-realtime-mini\" ||\n value === \"opengeni-gateway/xai/grok-voice-think-fast-2.0\" ||\n value === \"workspace-gateway/openai/gpt-realtime-2.1\" ||\n value === \"workspace-gateway/openai/gpt-realtime-mini\" ||\n value === \"workspace-gateway/xai/grok-voice-think-fast-2.0\"\n );\n}\n\n/** @deprecated Use the provider-neutral realtime names. */\nexport const useSessionCodexRealtime = useSessionRealtime;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const useWorkspaceRealtimeModelSelection = useRealtimeModelSelection;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const SessionCodexRealtimeControl = SessionRealtimeControl;\n/** @deprecated Use the provider-neutral realtime names. */\nexport const CodexRealtimeControl = RealtimeVoiceControl;\n","\"use client\";\n\nimport { ChevronRightIcon } from \"lucide-react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport {\n createContext,\n useContext,\n useRef,\n type ComponentProps,\n type Ref,\n type RefObject,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\n\nconst DropdownMenuSourceContext = createContext<RefObject<HTMLElement | null> | null>(null);\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === \"function\") ref(value);\n else if (ref) ref.current = value;\n}\n\nfunction DropdownMenu(props: ComponentProps<typeof DropdownMenuPrimitive.Root>) {\n const sourceRef = useRef<HTMLElement | null>(null);\n return (\n <DropdownMenuSourceContext.Provider value={sourceRef}>\n <DropdownMenuPrimitive.Root data-slot=\"dropdown-menu\" {...props} />\n </DropdownMenuSourceContext.Provider>\n );\n}\n\nfunction DropdownMenuTrigger({\n ref,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n return (\n <DropdownMenuPrimitive.Trigger\n data-slot=\"dropdown-menu-trigger\"\n ref={(node) => {\n if (sourceRef) sourceRef.current = node;\n assignRef(ref, node);\n }}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuContent({\n className,\n sideOffset = 4,\n style,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Content>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n const fallbackRef = useRef<HTMLElement | null>(null);\n const portalStyle = usePortalTokenStyle(sourceRef ?? fallbackRef);\n return (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n data-slot=\"dropdown-menu-content\"\n sideOffset={sideOffset}\n className={cn(\n \"og-root z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className,\n )}\n style={{ ...portalStyle, ...style }}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuItem({\n className,\n inset,\n variant = \"default\",\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: \"default\" | \"destructive\";\n}) {\n return (\n <DropdownMenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset}\n data-variant={variant}\n className={cn(\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-og-menu outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSeparator({\n className,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {\n return (\n <DropdownMenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSub(props: ComponentProps<typeof DropdownMenuPrimitive.Sub>) {\n return <DropdownMenuPrimitive.Sub data-slot=\"dropdown-menu-sub\" {...props} />;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }) {\n return (\n <DropdownMenuPrimitive.SubTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset}\n className={cn(\n \"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-og-menu outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[inset]:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon className=\"ml-auto size-4\" />\n </DropdownMenuPrimitive.SubTrigger>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n style,\n ...props\n}: ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {\n const sourceRef = useContext(DropdownMenuSourceContext);\n const fallbackRef = useRef<HTMLElement | null>(null);\n const portalStyle = usePortalTokenStyle(sourceRef ?? fallbackRef);\n return (\n <DropdownMenuPrimitive.SubContent\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(\n \"og-root z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95\",\n className,\n )}\n style={{ ...portalStyle, ...style }}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA;AAAA,EACE;AAAA,EACA;AAAA,OAOK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB,QAAQ,wBAAwB;AAC1D;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAAA;AAAA,EACA;AAAA,OACK;;;AC/BP,SAAS,wBAAwB;AACjC,SAAS,gBAAgB,6BAA6B;AACtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAeD,cA8FF,YA9FE;AAXN,IAAM,4BAA4B,cAAoD,IAAI;AAE1F,SAAS,UAAa,KAAyB,OAAiB;AAC9D,MAAI,OAAO,QAAQ,WAAY,KAAI,KAAK;AAAA,WAC/B,IAAK,KAAI,UAAU;AAC9B;AAEA,SAAS,aAAa,OAA0D;AAC9E,QAAM,YAAY,OAA2B,IAAI;AACjD,SACE,oBAAC,0BAA0B,UAA1B,EAAmC,OAAO,WACzC,8BAAC,sBAAsB,MAAtB,EAA2B,aAAU,iBAAiB,GAAG,OAAO,GACnE;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA,GAAG;AACL,GAAyD;AACvD,QAAM,YAAY,WAAW,yBAAyB;AACtD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,KAAK,CAAC,SAAS;AACb,YAAI,UAAW,WAAU,UAAU;AACnC,kBAAU,KAAK,IAAI;AAAA,MACrB;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAAyD;AACvD,QAAM,YAAY,WAAW,yBAAyB;AACtD,QAAM,cAAc,OAA2B,IAAI;AACnD,QAAM,cAAc,oBAAoB,aAAa,WAAW;AAChE,SACE,oBAAC,sBAAsB,QAAtB,EACC;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM;AAAA,MACjC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAEA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,GAAG;AACL,GAGG;AACD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,gBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,sBAAsB;AAAA,EAC7B;AAAA,EACA,GAAG;AACL,GAA2D;AACzD,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,6BAA6B,SAAS;AAAA,MACnD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,SAAS,gBAAgB,OAAyD;AAChF,SAAO,oBAAC,sBAAsB,KAAtB,EAA0B,aAAU,qBAAqB,GAAG,OAAO;AAC7E;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAkF;AAChF,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,cAAY;AAAA,MACZ,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD,oBAAC,oBAAiB,WAAU,kBAAiB;AAAA;AAAA;AAAA,EAC/C;AAEJ;AAEA,SAAS,uBAAuB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAA4D;AAC1D,QAAM,YAAY,WAAW,yBAAyB;AACtD,QAAM,cAAc,OAA2B,IAAI;AACnD,QAAM,cAAc,oBAAoB,aAAa,WAAW;AAChE,SACE;AAAA,IAAC,sBAAsB;AAAA,IAAtB;AAAA,MACC,aAAU;AAAA,MACV,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,OAAO,EAAE,GAAG,aAAa,GAAG,MAAM;AAAA,MACjC,GAAG;AAAA;AAAA,EACN;AAEJ;;;AD+MI,SA4aY,UA5aZ,OAAAC,MAkTQ,QAAAC,aAlTR;AAvSJ,IAAM,mBAAwC;AAAA,EAC5C,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB,aAAa;AACf;AAEA,IAAM,2BAA2B,CAAC,YAAY,mBAAmB,cAAc;AAG/E,IAAM,yBAGF;AAAA,EACF,UAAU,EAAE,cAAc,oBAAoB,MAAM,mBAAmB;AAAA,EACvE,mBAAmB;AAAA,IACjB,cAAc;AAAA,IACd,MAAM;AAAA,EACR;AAAA,EACA,gBAAgB,EAAE,cAAc,QAAQ,MAAM,4BAA4B;AAC5E;AACA,IAAM,gCAAgC;AAgB/B,SAAS,8BAA8B,OAAgC;AAC5E,SAAO,8BAA8B,KAAK,MAAM;AAClD;AAEO,SAAS,8BAA8B,OAAsC;AAClF,MAAI,MAAM,kBAAkB,YAAa,QAAO;AAChD,MAAI,MAAM,iBAAiB,SAAU,QAAO;AAC5C,MAAI,MAAM,eAAe,KAAM,QAAO;AACtC,MAAI,CAAC,MAAM,eAAgB,QAAO;AAClC,MAAI,MAAM,gBAAiB,QAAO;AAClC,SAAO;AACT;AAEO,SAAS,mBAAmB,SAchC;AACD,QAAM,EAAE,QAAQ,YAAY,IAAI,2BAA2B;AAAA,IACzD,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,QAAQ,QAAQ,SAAS,iBAAiB;AAChD,QAAM,WAAWC,QAAyB,IAAI;AAC9C,QAAM,gBAAgBA,QAAyC,IAAI;AACnE,QAAM,qBAAqBA,QAAoC,IAAI;AACnE,QAAM,CAAC,UAAU,WAAW,IAAI,SAA4C,OAAO;AAAA,IACjF,QAAQ,mCAAmC,EAAE,aAAa,WAAW,QAAQ,WAAW,MAAM,CAAC,IAC3F,eACA;AAAA,IACJ,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,kBAAkB;AAAA,IAClB,YAAY;AAAA,IACZ,OAAO;AAAA,EACT,EAAE;AACF,QAAM,YAAY;AAAA,IAChB,MAAM,gCAAgC,QAAQ,MAAM;AAAA,IACpD,CAAC,QAAQ,MAAM;AAAA,EACjB;AACA,QAAM,eAAeA,QAAO,SAAS;AACrC,QAAM,iBAAiBA,QAAO,QAAQ,WAAW;AACjD,eAAa,UAAU;AACvB,iBAAe,UAAU,QAAQ;AACjC,QAAM,kBAAkB,WAAW,UAAU;AAE7C,YAAU,MAAM;AACd,QAAI,WAAW;AACf,QAAI,aAA+C;AACnD,QAAI,cAAmC;AACvC,SAAK,OAAO,wBAAwB,EACjC,KAAK,CAAC,EAAE,gCAAgC,MAAM;AAC7C,UAAI,SAAU;AACd,YAAM,oBAAoB,QAAQ,qBAAqB;AACvD,mBAAa,kBAAkB;AAAA,QAC7B;AAAA,QACA;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,GAAI,SAAS,UAAU,EAAE,aAAa,SAAS,QAAQ,IAAI,CAAC;AAAA,QAC5D;AAAA,MACF,CAAC;AACD,oBAAc,UAAU;AACxB,yBAAmB,UAAU;AAC7B,oBAAc,WAAW,UAAU,WAAW;AAC9C,UAAI,eAAe,SAAS;AAC1B,aAAK,WAAW,iBAAiB,aAAa,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,MAC9E;AAAA,IACF,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,UAAI,SAAU;AACd,kBAAY;AAAA,QACV,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ,YAAY;AAAA,QACZ,eAAe;AAAA,QACf,aAAa;AAAA,QACb,sBAAsB;AAAA,QACtB,kBAAkB;AAAA,QAClB,YAAY;AAAA,UACV,MAAM;AAAA,UACN,SACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,UAC3C,aAAa;AAAA,UACb,sBAAsB;AAAA,UACtB,SAAS;AAAA,QACX;AAAA,QACA,OACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAC7C,CAAC;AAAA,IACH,CAAC;AACH,WAAO,MAAM;AACX,iBAAW;AACX,oBAAc;AACd,kBAAY,MAAM;AAClB,UAAI,cAAc,YAAY,YAAY;AACxC,sBAAc,UAAU;AACxB,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,QAAQ,mBAAmB,QAAQ,WAAW,WAAW,CAAC;AAE7E,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,YAAa;AAC1B,SAAK,cAAc,SAAS,iBAAiB,SAAS,EAAE,MAAM,MAAM,MAAS;AAAA,EAC/E,GAAG,CAAC,WAAW,QAAQ,WAAW,CAAC;AAEnC,QAAM,QAAQ,YAAY,YAAY;AACpC,UAAM,aAAa,cAAc;AACjC,QAAI,CAAC,cAAc,mBAAmB,YAAY,OAAO;AACvD,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AACA,UAAM,WAAW,MAAM;AAAA,EACzB,GAAG,CAAC,KAAK,CAAC;AACV,QAAM,OAAO,YAAY,YAAY;AACnC,UAAM,cAAc,SAAS,KAAK;AAAA,EACpC,GAAG,CAAC,CAAC;AACL,QAAM,QAAQ,YAAY,YAAY;AACpC,UAAM,cAAc,SAAS,MAAM;AAAA,EACrC,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqB,YAAY,YAAY;AACjD,UAAM,cAAc,SAAS,mBAAmB;AAAA,EAClD,GAAG,CAAC,CAAC;AACL,QAAM,gBAAgB,YAAY,CAAC,UAAmB;AACpD,kBAAc,SAAS,cAAc,KAAK;AAAA,EAC5C,GAAG,CAAC,CAAC;AACL,QAAM,iBAAiB,YAAY,CAAC,UAAmB;AACrD,kBAAc,SAAS,eAAe,KAAK;AAAA,EAC7C,GAAG,CAAC,CAAC;AACL,QAAM,iBAAiB;AAAA,IACrB,eAAe,QAAQ;AAAA,IACvB,cAAc,QAAQ,iBAAiB;AAAA,IACvC,YAAY,QAAQ,iBAAiB;AAAA,IACrC,gBAAgB,QAAQ,kBAAkB,QAAQ;AAAA,IAClD;AAAA,EACF;AACA,QAAM,mBACJ,QAAQ,mBAAmB,QACtB,QAAQ,0BAA0B,qCACnC,8BAA8B,cAAc;AAClD,QAAM,WACJ,cAAc,YAAY,QAC1B,mBAAmB,YAAY,SAC/B,CAAC,QAAQ,OAAO,EAAE,SAAS,SAAS,MAAM,KAC1C,qBAAqB;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,QAAQ;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,uBAAuB,OAepC;AACD,QAAM,YAAY,QAAQ,MAAM,gCAAgC,MAAM,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC;AAC7F,QAAM,YAAY,0BAA0B;AAAA,IAC1C,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,gBAAgB,MAAM;AAAA,IACtB,aAAa,WAAW,UAAU,WAAW,UAAU,QAAQ;AAAA,EACjE,CAAC;AACD,QAAM,gBAAgB,UAAU;AAChC,QAAM,WAAW,mBAAmB;AAAA,IAClC,GAAG;AAAA,IACH,OAAO,cAAc;AAAA,IACrB,gBAAgB,cAAc;AAAA,IAC9B,wBAAwB,cAAc;AAAA,EACxC,CAAC;AACD,QAAM,oBAAoBA,QAAO,MAAM,0BAA0B,IAAI;AACrE,QAAM,sBAAsBA,QAAO,KAAK;AAExC,QAAM,EAAE,QAAQ,eAAe,uBAAuB,YAAY,IAAI;AACtE,QAAM,EAAE,UAAU,MAAM,IAAI;AAC5B,QAAM,8BAA8B,MAAM;AAC1C,QAAM,sBAAsB,MAAM;AAClC,QAAM,cAAc,SAAS,SAAS,MAAM,UAAU;AAEtD,YAAU,MAAM;AACd,0BAAsB,WAAW;AAAA,EACnC,GAAG,CAAC,qBAAqB,WAAW,CAAC;AAErC,YAAU,MAAM;AACd,UAAM,UAAU,kBAAkB;AAClC,QAAI,CAAC,WAAW,oBAAoB,WAAW,WAAW,UAAU,SAAU;AAC9E,UAAM,YAAY,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,WAAW,MAAM,SAAS;AAChF,QAAI,CAAC,UAAW;AAChB,QAAI,sBAAsB,OAAO,SAAS;AACxC,kBAAY,OAAO;AACnB;AAAA,IACF;AACA,QAAI,CAAC,SAAU;AACf,wBAAoB,UAAU;AAC9B,SAAK,MAAM,EACR,KAAK,MAAM;AACV,wBAAkB,UAAU;AAC5B,oCAA8B;AAAA,IAChC,CAAC,EACA,MAAM,MAAM;AACX,0BAAoB,UAAU;AAAA,IAChC,CAAC;AAAA,EACL,GAAG;AAAA,IACD;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,sBAAsB;AAAA,IACtB;AAAA,EACF,CAAC;AAED,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,SAAS;AAAA,MACnB,UAAU,SAAS;AAAA,MACnB,kBAAkB,SAAS;AAAA,MAC3B,gBAAgB,cAAc;AAAA,MAC9B,UAAS;AAAA,MACT,UAAU,SAAS;AAAA,MACnB;AAAA,MACA,QAAQ,UAAU;AAAA,MAClB,eAAe,SAAS,kBAAkB,SAAY,UAAU;AAAA,MAChE,SAAS,SAAS;AAAA,MAClB,QAAQ,SAAS;AAAA,MACjB,SAAS,SAAS;AAAA,MAClB,sBAAsB,SAAS;AAAA,MAC/B,iBAAiB,SAAS;AAAA,MAC1B,kBAAkB,SAAS;AAAA;AAAA,EAC7B;AAEJ;AAEO,SAAS,0BAA0B,SAKvC;AACD,QAAM,EAAE,QAAQ,YAAY,IAAI,2BAA2B;AAAA,IACzD,QAAQ,QAAQ;AAAA,IAChB,aAAa,QAAQ;AAAA,EACvB,CAAC;AACD,QAAM,cAAc,QAAQ;AAC5B,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,MAAM;AAAA,IAClE;AAAA,MACE,GAAG;AAAA,MACH,WAAW,QAAQ;AAAA,MACnB,mBAAmB,QAAQ,iBAAiB,OAAO,iBAAiB;AAAA,IACtE;AAAA,EACF,CAAC;AACD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C,MAAM,4BAA4B,WAAW,KAAK,iBAAiB;AAAA,EACrE;AAEA,YAAU,MAAM;AACd,QAAI,WAAW;AACf,UAAM,OAAO,OAAO;AACpB,QAAI,CAAC,KAAM;AACX,SAAK,KACF,KAAK,QAAQ,WAAW,EACxB,KAAK,CAAC,aAAa;AAClB,UAAI,SAAU;AACd,YAAM,SAAS,SAAS,OAAO,IAAI,qBAAqB;AACxD,iBAAW,MAAM;AACjB,yBAAmB,CAAC,YAAY;AAC9B,YAAI,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,WAAW,MAAM,SAAS,EAAG,QAAO;AAC5E,eAAO,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,GAAG,MAAM,iBAAiB;AAAA,MACzE,CAAC;AAAA,IACH,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,WAAO,MAAM;AACX,iBAAW;AAAA,IACb;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,YAAU,MAAM;AACd,QAAI,YAAa,oBAAmB,WAAW;AAAA,EACjD,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,gBACJ,QAAQ,KAAK,CAAC,UAAU,MAAM,OAAO,eAAe,KACnD;AAAA,IACC,GAAG;AAAA,IACH,WAAW,QAAQ;AAAA,IACnB,mBAAmB,QAAQ,iBAAiB,OAAO,iBAAiB;AAAA,EACtE;AACF,QAAM,cAAc;AAAA,IAClB,CAAC,UAAkB;AACjB,YAAM,QAAQ,QAAQ,KAAK,CAAC,cAAc,UAAU,OAAO,KAAK;AAChE,UAAI,CAAC,SAAS,CAAC,MAAM,aAAa,YAAa;AAC/C,yBAAmB,MAAM,EAAE;AAC3B,mCAA6B,aAAa,MAAM,EAAE;AAAA,IACpD;AAAA,IACA,CAAC,aAAa,SAAS,WAAW;AAAA,EACpC;AAEA,SAAO,EAAE,QAAQ,SAAS,eAAe,YAAY;AACvD;AAEA,IAAM,yBAA4D;AAAA,EAChE,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,OAAO;AACT;AAEO,SAAS,0BAA0B,OAiBvC;AACD,QAAM,oBAAoB,0BAA0B;AAAA,IAClD,QAAQ,MAAM;AAAA,IACd,aAAa,MAAM;AAAA,IACnB,gBAAgB,MAAM;AAAA,EACxB,CAAC;AACD,QAAM,YACJ,MAAM,UAAU,MAAM,iBAAiB,MAAM,gBACzC;AAAA,IACE,QAAQ,MAAM;AAAA,IACd,eAAe,MAAM;AAAA,IACrB,aAAa,MAAM;AAAA,EACrB,IACA;AACN,QAAM,WAAWE,QAAyB,IAAI;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,WAAW;AAAA,IACf,MACE,WACI,EAAE,GAAG,wBAAwB,QAAQ,WAAW,IAChD,QACE,EAAE,GAAG,wBAAwB,QAAQ,SAAS,MAAM,IACpD;AAAA,IACR,CAAC,OAAO,QAAQ;AAAA,EAClB;AACA,QAAM,WAAW,UAAU,cAAc,aAAa,MAAM,aAAa,QAAQ,CAAC;AAClF,QAAM,UAAU,MAAM;AACtB,QAAM,kBAAkB,UAAU,cAAc;AAChD,QAAM,QAAQ,YAAY,YAAY;AACpC,QAAI,CAAC,SAAU;AACf,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,eAAe;AAC7C,UAAI,CAAC,QAAS,UAAS,yCAAyC;AAAA,IAClE,SAAS,OAAO;AACd,eAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACjE,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,UAAU,SAAS,eAAe,CAAC;AAEvC,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,kBAAkB,UAAU,cAAc,qBAAqB,MAAM,kBAAkB;AAAA,MACvF,gBAAgB,UAAU,cAAc;AAAA,MACxC,mBAAmB;AAAA,MACnB,UAAS;AAAA,MACT,WAAW,MAAM,aAAa;AAAA,MAC9B,iBAAiB;AAAA,MACjB;AAAA,MACA,eAAe,UAAU;AAAA,MACzB,QAAQ,UAAU;AAAA,MAClB,eAAe,UAAU;AAAA,MACzB,SAAS;AAAA,MACT,QAAQ,YAAY;AAAA,MACpB,SAAS;AAAA,MACT,sBAAsB,YAAY;AAAA,MAClC,iBAAiB,MAAM;AAAA,MACvB,kBAAkB,MAAM;AAAA;AAAA,EAC1B;AAEJ;AAEO,SAAS,qBAAqB,OA2BlC;AACD,QAAM,eAAe,iBAAiB;AACtC,QAAM,gBAAgB,MAAM,iBAAiB;AAAA,IAC3C,GAAG;AAAA,IACH,WAAW,MAAM;AAAA,IACjB,mBAAmB,MAAM,iBAAiB,OAAO,iBAAiB;AAAA,EACpE;AACA,QAAM,SAAS,MAAM,UAAU,CAAC,aAAa;AAC7C,QAAM,SAAS;AAAA,IACb,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM,oBAAoB;AAAA,IAC1B,cAAc;AAAA,EAChB;AACA,QAAM,YAAY,MAAM,SAAS,MAAM,UAAU;AACjD,QAAM,kBACJ,aACA,MAAM,SAAS,WAAW,cAC1B,MAAM,SAAS,WAAW,YAC1B,MAAM,SAAS,YAAY,gBAAgB;AAC7C,QAAM,eAAe,MAAM,SAAS,kBAAkB,aAAa,CAAC,MAAM,SAAS;AACnF,QAAM,eACJ,MAAM,SAAS,WAAW,cAC1B,MAAM,SAAS,WAAW,gBACzB,CAAC,aAAa,CAAC,MAAM,YACrB,MAAM,SAAS,WAAW,cAAc,CAAC;AAC5C,QAAM,YAAY,eACd,uBACA,kBACE,2BACA,YACE,2BACA,oBAAoB,cAAc,KAAK;AAC/C,QAAM,gBAAgB,eAClB,MAAM,uBACN,kBACE,MAAM,UACN,YACE,MAAM,SACN,MAAM;AACd,QAAM,qBAAqB,MAAM,mBAAmB,YAAY,IAAI;AACpE,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,wBAAwB,cAAc,WAAW,cAAc;AACrE,QAAM,uBAAuB,cAAc;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,gBAAgB,iBAAiB,IAAI;AAAA,IAC1C,cAAc;AAAA,EAChB;AACA,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAiB,CAAC;AAEhE,YAAU,MAAM;AACd,QAAI,CAAC,WAAY,mBAAkB,cAAc,QAAQ;AAAA,EAC3D,GAAG,CAAC,YAAY,cAAc,QAAQ,CAAC;AAEvC,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,oBAAkB,MAAM,YAAY;AAAA,MACpC,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,WAAM,KAAK,MAAM,UAAU,UAAQ,MAAC,WAAU,UAAS,eAAY,QAAO;AAAA,QAC3E,gBAAAA,KAAC,wBAAqB,OAAO,OAAO,OAAO,cAAc,iBAAiB,MAAM;AAAA,QAChF,gBAAAA,KAAC,mBAAgB,SAAS,OACvB,sBACC,gBAAAA;AAAA,UAAC,OAAO;AAAA,UAAP;AAAA,YAEC,eAAY;AAAA,YAGZ,SAAS,eAAe,QAAQ,EAAE,SAAS,GAAG,OAAO,EAAE;AAAA,YACvD,SAAS;AAAA,cACP,SAAS;AAAA,cACT,OAAO;AAAA,cACP,YAAY,eACR,EAAE,UAAU,EAAE,IACd,EAAE,OAAO,KAAK,UAAU,MAAM,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE;AAAA,YAC7D;AAAA,YACC,GAAI,eACD,CAAC,IACD;AAAA,cACE,MAAM;AAAA,gBACJ,SAAS;AAAA,gBACT,OAAO;AAAA,gBACP,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE;AAAA,cACrD;AAAA,YACF;AAAA,YACJ,WAAW;AAAA,cACT;AAAA,cACA,MAAM;AAAA,YACR;AAAA,YAEA,0BAAAC;AAAA,cAAC,OAAO;AAAA,cAAP;AAAA,gBACC,WAAU;AAAA,gBACV,SAAS,eAAe,QAAQ,EAAE,GAAG,IAAI,SAAS,EAAE;AAAA,gBACpD,SAAS;AAAA,kBACP,GAAG;AAAA,kBACH,SAAS;AAAA,kBACT,YAAY,eACR,EAAE,UAAU,EAAE,IACd,EAAE,OAAO,MAAM,MAAM,UAAU,WAAW,KAAK,SAAS,IAAI,MAAM,IAAI;AAAA,gBAC5E;AAAA,gBACC,GAAI,eACD,CAAC,IACD;AAAA,kBACE,MAAM;AAAA,oBACJ,GAAG;AAAA,oBACH,SAAS;AAAA,oBACT,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE;AAAA,kBACrD;AAAA,gBACF;AAAA,gBAEJ;AAAA,kCAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,MAAM,SAAS;AAAA,sBACtB,MAAK;AAAA,sBACL,cAAc,iBAAiB;AAAA,sBAC/B,YAAY,eAAe,IAAI;AAAA,sBAC/B,UAAU,MAAM,MAAM,gBAAgB,CAAC,MAAM,SAAS,UAAU;AAAA;AAAA,kBAClE;AAAA,kBACA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,OAAO,MAAM,SAAS;AAAA,sBACtB,MAAK;AAAA,sBACL,cAAc,iBAAiB;AAAA,sBAC/B,YAAY,eAAe,IAAI;AAAA,sBAC/B,UAAU,MAAM,MAAM,iBAAiB,CAAC,MAAM,SAAS,WAAW;AAAA;AAAA,kBACpE;AAAA;AAAA;AAAA,YACF;AAAA;AAAA,UA5DI;AAAA,QA6DN,IACE,MACN;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,qCACb;AAAA,0BAAAD;AAAA,YAAC,OAAO;AAAA,YAAP;AAAA,cACC,MAAK;AAAA,cACL,eAAY;AAAA,cACZ,cAAY,OAAO;AAAA,cACnB,cAAY;AAAA,cACZ,gBAAc;AAAA,cACd,OAAO;AAAA,cACP,UAAU;AAAA,cACT,GAAI,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,KAAK,EAAE;AAAA,cACrD,YAAY,EAAE,MAAM,UAAU,WAAW,KAAK,SAAS,GAAG;AAAA,cAC1D,SAAS,MAAM,KAAK,cAAc,EAAE,MAAM,MAAM,MAAS;AAAA,cACzD,WAAW;AAAA;AAAA;AAAA;AAAA,gBAIT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,0BACG,uBACG,yCACA;AAAA,gBACN,gBAAgB,OAAO,KAAK;AAAA,cAC9B;AAAA,cAEA,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,OAAO,OAAO;AAAA,kBACd;AAAA,kBACA,cAAc,iBAAiB;AAAA;AAAA,cACjC;AAAA;AAAA,UACF;AAAA,UAEC,wBACC,gBAAAC,MAAC,gBAAa,MAAM,YAAY,cAAc,eAC5C;AAAA,4BAAAD,KAAC,uBAAoB,SAAO,MAC1B,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,OAAO,gBAAgB,cAAc,KAAK;AAAA,gBAC1C,UAAU,MAAM;AAAA,gBAChB,WAAW;AAAA;AAAA;AAAA,kBAGT;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,wBAAwB;AAAA,kBACxB,iBAAiB,OAAO,KAAK;AAAA,gBAC/B;AAAA,gBAEA,0BAAAA,KAAC,mBAAgB,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,YAClD,GACF;AAAA,YACA,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,MAAM,YAAY;AAAA,gBACxB,OAAM;AAAA,gBACN,YAAY;AAAA,gBACZ,kBAAkB;AAAA,gBAClB,WAAU;AAAA,gBACV,kBAAkB,CAAC,UAAU,MAAM,eAAe;AAAA,gBAElD;AAAA,kCAAAD,KAAC,SAAI,WAAU,qDACb,0BAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC;AAAA,sBACA;AAAA,sBACA,UAAU;AAAA,sBACV,WAAW;AAAA,sBACX,UACE,MAAM,sBAAsB,QAAQ,MAAM,SAAS,MAAM,UAAU;AAAA,sBAErE,kBAAkB,CAAC,UAAU,cAAc;AACzC,2CAAmB,SAAS;AAC5B,0CAAkB,QAAQ;AAAA,sBAC5B;AAAA,sBACA,UAAU,CAAC,YAAY;AACrB,8BAAM,gBAAgB,OAAO;AAC7B,sCAAc,KAAK;AAAA,sBACrB;AAAA;AAAA,kBACF,GACF;AAAA,kBAEC,OAAO,UAAU,SAChB,gBAAAC,MAAA,YACE;AAAA,oCAAAD,KAAC,yBAAsB,WAAU,qBAAoB;AAAA,oBACrD,gBAAAC,MAAC,SAAI,WAAU,eAAc,MAAK,UAAS,aAAU,UACnD;AAAA,sCAAAA,MAAC,SAAI,WAAU,6DACb;AAAA,wCAAAD;AAAA,0BAAC;AAAA;AAAA,4BACC,OAAO,OAAO;AAAA,4BACd,cAAc,iBAAiB;AAAA;AAAA,wBACjC;AAAA,wBACC,OAAO;AAAA,yBACV;AAAA,sBACA,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,iBAAO,QAAO;AAAA,uBAC5E;AAAA,qBACF,IACE;AAAA,kBAEH,eACC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,UAAU,MAAM,KAAK,MAAM,qBAAqB,EAAE,MAAM,MAAM,MAAS;AAAA,sBAEvE;AAAA,wCAAAD,KAAC,eAAY;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEjB,IACE;AAAA,kBACH,kBACC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,UAAU,MAAM,KAAK,MAAM,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,sBAE1D;AAAA,wCAAAD,KAAC,iBAAc;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEnB,IACE;AAAA,kBACH,aAAa,MAAM,SAAS,WAAW,eACtC,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,WAAU;AAAA,sBACV,UAAU,MAAM,SAAS,WAAW;AAAA,sBACpC,UAAU,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,MAAM,MAAS;AAAA,sBAEzD;AAAA,wCAAAD,KAAC,cAAW;AAAA,wBAAE;AAAA;AAAA;AAAA,kBAEhB,IACE;AAAA,kBAEH,qBAAqB,gBAAAA,KAAC,2BAAwB,UAAU,MAAM,UAAU,IAAK;AAAA;AAAA;AAAA,YAChF;AAAA,aACF,IACE;AAAA,WACN;AAAA,QAEA,gBAAAC,MAAC,UAAK,WAAU,WAAU,MAAK,UAAS,aAAU,UAC/C;AAAA,iBAAO;AAAA,UAAM;AAAA,UAAG,OAAO;AAAA,WAC1B;AAAA,QACC,MAAM,SAAS,QACd,gBAAAD,KAAC,UAAK,WAAU,WAAU,MAAK,SAC5B,gBAAM,SAAS,OAClB,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAGO,SAAS,wBAAwB,OAMrC;AACD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAuC,IAAI;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAI,SAAiB,CAAC;AAEpD,SACE,gBAAAC,MAAC,SAAI,WAAU,gCAA+B,eAAY,8BACxD;AAAA,oBAAAA,MAAC,SAAI,WAAU,oDACZ;AAAA,YAAM;AAAA,MACP,gBAAAA,MAAC,SAAI,WAAU,WACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,uCAAsC,yBAAW;AAAA,QAChE,gBAAAA,KAAC,OAAE,WAAU,4CAA2C,uDAExD;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAU,mEACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,MAAM;AAAA,QACd,eAAe,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA,UAAU,MAAM,aAAa;AAAA,QAC7B,kBAAkB,CAAC,MAAM,kBAAkB;AACzC,uBAAa,aAAa;AAC1B,sBAAY,IAAI;AAAA,QAClB;AAAA,QACA,UAAU,MAAM;AAAA;AAAA,IAClB,GACF;AAAA,KACF;AAEJ;AAEA,SAAS,mBAAmB,OAMzB;AACD,QAAM,aAAa,MAAM,SAAS;AAClC,QAAM,QAAQ,aACV,MAAM,QACJ,sBACA,oBACF,MAAM,QACJ,uBACA;AACN,QAAM,OAAO,aACT,MAAM,QACJ,aACA,UACF,MAAM,QACJ,cACA;AAEN,SACE,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,gBAAc,MAAM;AAAA,MACpB,OAAO;AAAA,MACP,SAAS,MAAM,eAAe,QAAQ,EAAE,OAAO,MAAM,SAAS,EAAE;AAAA,MAChE,SAAS,EAAE,OAAO,GAAG,SAAS,EAAE;AAAA,MAChC,YAAY;AAAA,QACV,OAAO,MAAM,cAAc;AAAA,QAC3B,MAAM;AAAA,QACN,WAAW;AAAA,QACX,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,MACC,GAAI,MAAM,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,KAAK,EAAE;AAAA,MAC3D,SAAS,MAAM;AAAA,MACf,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,MAAM,QACF,gEACA;AAAA,MACN;AAAA,MAEA,0BAAAA,KAAC,QAAK,WAAU,YAAW,eAAW,MAAC;AAAA;AAAA,EACzC;AAEJ;AAEO,SAAS,wBAAwB,OAQrC;AACD,QAAM,YAAY,yBAAyB;AAAA,IAAO,CAAC,aACjD,MAAM,OAAO,KAAK,CAAC,UAAU,MAAM,aAAa,QAAQ;AAAA,EAC1D;AACA,QAAM,UAAU,MAAM,WAAW,gBAAgB,MAAM,QAAQ,KAAK;AACpE,QAAM,OAAO,MAAM,WACjB,gBAAAC,MAAC,SAAI,eAAY,gCACf;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,MAAM;AAAA,QACb,MACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,cAAc,uBAAuB,MAAM,QAAQ,EAAE;AAAA,YACrD,cAAW;AAAA;AAAA,QACb;AAAA,QAEF,QAAQ,MAAM,MAAM,iBAAiB,MAAM,EAAE;AAAA;AAAA,IAC/C;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAU,yBACZ,gBAAM,OACJ,OAAO,CAAC,UAAU,MAAM,aAAa,MAAM,QAAQ,EACnD,IAAI,CAAC,UAAU;AACd,YAAM,WAAW,MAAM,OAAO,MAAM,cAAc;AAClD,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,OAAO,MAAM;AAAA,UACb,MACE,MAAM,sBACL,MAAM,cAAc,oBAAiB,MAAM,WAAW,KAAK,MAAM;AAAA,UAEpE,UAAU,MAAM,YAAY,CAAC,MAAM;AAAA,UACnC,OAAO,MAAM,qBAAqB,MAAM;AAAA,UACxC,QAAQ;AAAA,UACR,aAAa;AAAA,UACb,UAAU,WAAW,gBAAAA,KAAC,aAAU,WAAU,YAAW,eAAW,MAAC,IAAK;AAAA,UACtE,QAAQ,yBAAyB,MAAM,EAAE;AAAA,UACzC,SAAS,MAAM,MAAM,SAAS,MAAM,EAAE;AAAA;AAAA,QAZjC,MAAM;AAAA,MAab;AAAA,IAEJ,CAAC,GACL;AAAA,KACF,IAEA,gBAAAA,KAAC,SAAI,WAAU,yBAAwB,eAAY,mCAChD,oBAAU,IAAI,CAAC,aAAa;AAC3B,UAAM,OAAO,uBAAuB,QAAQ;AAC5C,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,OAAO;AAAA,QACP,MAAM,KAAK;AAAA,QACX,MAAM,gBAAAA,KAAC,oBAAiB,cAAc,KAAK,cAAc,cAAW,IAAG;AAAA,QACvE,QAAQ,MAAM,cAAc,aAAa;AAAA,QACzC,QAAQ,2BAA2B,KAAK,YAAY;AAAA,QACpD,SAAS,MAAM,MAAM,iBAAiB,UAAU,CAAC;AAAA;AAAA,MAN5C;AAAA,IAOP;AAAA,EAEJ,CAAC,GACH;AAGF,SACE,gBAAAA,KAAC,SAAI,WAAU,4BAA2B,eAAY,8BACpD,0BAAAA,KAAC,sBAAmB,SAAkB,WAAW,MAAM,WACpD,gBACH,GACF;AAEJ;AAEA,SAAS,wBAAwB,EAAE,SAAS,GAAoD;AAC9F,QAAM,SAAS,SAAS;AACxB,QAAM,OAAO;AAAA,IACX,CAAC,cAAc,SAAS,MAAM;AAAA,IAC9B,CAAC,cAAc,SAAS,UAAU;AAAA,IAClC,CAAC,oBAAoB,OAAO,SAAS,UAAU,CAAC;AAAA,IAChD,CAAC,SAAS,SAAS,aAAa;AAAA,IAChC,CAAC,eAAe,OAAO,SAAS,WAAW,CAAC;AAAA,IAC5C,CAAC,cAAc,OAAO,SAAS,oBAAoB,CAAC;AAAA,IACpD,CAAC,qBAAqB,OAAO,SAAS,gBAAgB,CAAC;AAAA,IACvD,CAAC,QAAQ,SAAS,MAAM,SAAS,MAAM;AAAA,IACvC,CAAC,gBAAgB,SAAS,OAAO,OAAO,SAAS,KAAK,OAAO,IAAI,QAAG;AAAA,IACpE,CAAC,oBAAoB,SAAS,OAAO,OAAO,SAAS,KAAK,eAAe,IAAI,QAAG;AAAA,IAChF,CAAC,oBAAoB,SAAS,OAAO,OAAO,eAAe,IAAI,OAAO;AAAA,IACtE,CAAC,qBAAqB,SAAS,OAAO,OAAO,QAAQ,IAAI,OAAO;AAAA,IAChE,CAAC,cAAc,QAAQ,qBAAqB,WAAW,MAAM;AAAA,IAC7D,CAAC,0BAA0B,SAAS,OAAO,OAAO,cAAc,IAAI,GAAG;AAAA,IACvE,CAAC,iBAAiB,SAAS,OAAO,OAAO,mBAAmB,IAAI,GAAG;AAAA,IACnE,CAAC,cAAc,QAAQ,0BAA0B,SAAS,KAAK,QAAG;AAAA,IAClE,CAAC,2BAA2B,SAAS,OAAO,OAAO,iBAAiB,IAAI,GAAG;AAAA,IAC3E,CAAC,sBAAsB,QAAQ,wBAAwB,MAAM;AAAA,IAC7D,CAAC,gBAAgB,QAAQ,aAAa,MAAM;AAAA,IAC5C,CAAC,cAAc,SAAS,YAAY,QAAQ,MAAM;AAAA,EACpD;AAEA,SACE,gBAAAC,MAAA,YACE;AAAA,oBAAAD,KAAC,yBAAsB,WAAU,qBAAoB;AAAA,IACrD,gBAAAC,MAAC,mBACC;AAAA,sBAAAD,KAAC,0BAAuB,WAAU,kCAAiC,kCAEnE;AAAA,MACA,gBAAAA,KAAC,0BAAuB,WAAU,8GAC/B,eAAK,IAAI,CAAC,CAAC,OAAO,KAAK,MACtB,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,UAAQ;AAAA,UACR,WAAU;AAAA,UAEV;AAAA,4BAAAD,KAAC,UAAK,WAAU,8BAA8B,iBAAM;AAAA,YACpD,gBAAAA,KAAC,UAAK,WAAU,kCAAkC,iBAAM;AAAA;AAAA;AAAA,QALnD;AAAA,MAMP,CACD,GACH;AAAA,OACF;AAAA,KACF;AAEJ;AAcA,SAAS,cACP,UACA,gBACA,UACA,kBACA,YAC+D;AAC/D,MAAI,SAAS,kBAAkB,aAAa,CAAC,SAAS,aAAa;AACjE,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF;AACA,UAAQ,SAAS,QAAQ;AAAA,IACvB,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO,SAAS,QAAQ,WACpB;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV,IACA;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACN,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,SAAS,SAAS;AAAA,MAC5B;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,SAAS,SAAS;AAAA,MAC5B;AAAA,IACF,KAAK;AACH,UAAI,CAAC,gBAAgB;AACnB,eAAO;AAAA,UACL,OAAO;AAAA,UACP,OAAO;AAAA,UACP,QAAQ,oBAAoB;AAAA,QAC9B;AAAA,MACF;AACA,UAAI,CAAC,YAAY,kBAAkB;AACjC,eAAO,EAAE,OAAO,eAAe,OAAO,qBAAqB,QAAQ,iBAAiB;AAAA,MACtF;AACA,aAAO;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,kBAAkB,UAAU;AAAA,MACtC;AAAA,EACJ;AACF;AAEA,SAAS,qBAAqB,OAA8D;AAC1F,QAAM,UAAU;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,SAAS,MAAM,KAAK;AACtB,SACE,gBAAAA,KAAC,mBAAgB,SAAS,OACvB,oBACC,gBAAAA;AAAA,IAAC,OAAO;AAAA,IAAP;AAAA,MACC,eAAW;AAAA,MAEX,SAAS,EAAE,SAAS,EAAE;AAAA,MACtB,SACE,MAAM,eACF,EAAE,SAAS,KAAK,IAChB,MAAM,UAAU,aACd,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,IAC9B,MAAM,UAAU,cACd,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE,IAC9B,EAAE,SAAS,CAAC,MAAM,MAAM,IAAI,EAAE;AAAA,MAExC,MAAM,EAAE,SAAS,EAAE;AAAA,MACnB,YACE,MAAM,eACF,EAAE,UAAU,KAAK,IACjB;AAAA,QACE,UAAU,MAAM,UAAU,aAAa,OAAO;AAAA,QAC9C,MAAM;AAAA,QACN,QAAQ,OAAO;AAAA,MACjB;AAAA,MAEN,WAAW;AAAA,QACT;AAAA,QACA,MAAM,UAAU,aACZ,8BACA,MAAM,UAAU,aAAa,MAAM,UAAU,UAC3C,8BACA;AAAA,MACR;AAAA;AAAA,IA5BK,MAAM;AAAA,EA6Bb,IACE,MACN;AAEJ;AAEA,SAAS,oBAAoB,OAI1B;AACD,MAAI,CAAC,cAAc,gBAAgB,UAAU,EAAE,SAAS,MAAM,KAAK,GAAG;AACpE,WAAO,gBAAAA,KAAC,oBAAiB,WAAU,4BAA2B,eAAW,MAAC;AAAA,EAC5E;AACA,MAAI,MAAM,UAAU,WAAW;AAC7B,WAAO,MAAM,eACX,gBAAAA,KAAC,eAAY,WAAU,YAAW,eAAW,MAAC,IAE9C,gBAAAA,KAAC,mBAAgB,WAAU,YAAW,eAAW,MAAC;AAAA,EAEtD;AACA,MAAI,MAAM,UAAU,WAAW,MAAM,UAAU,eAAe,MAAM,UAAU,eAAe;AAC3F,WAAO,gBAAAA,KAAC,mBAAgB,WAAU,YAAW,eAAW,MAAC;AAAA,EAC3D;AACA,MAAI,MAAM,UAAU,eAAe,MAAM,UAAU,YAAY;AAC7D,WACE,gBAAAC,MAAC,UAAK,WAAU,2DACb;AAAA,OAAC,MAAM,eACN,gBAAAD;AAAA,QAAC,OAAO;AAAA,QAAP;AAAA,UACC,eAAW;AAAA,UACX,WAAU;AAAA,UACV,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE;AAAA,UACnD,YAAY;AAAA,YACV,UAAU,MAAM,UAAU,aAAa,MAAM;AAAA,YAC7C,MAAM;AAAA,YACN,QAAQ,OAAO;AAAA,UACjB;AAAA;AAAA,MACF,IACE;AAAA,MACJ,gBAAAA,KAAC,cAAW,WAAU,kCAAiC,eAAW,MAAC;AAAA,OACrE;AAAA,EAEJ;AACA,SAAO,gBAAAA,KAAC,kBAAe,WAAU,UAAS,eAAW,MAAC;AACxD;AAEA,SAAS,kBAAkB,OAA8D;AACvF,QAAM,OAAO,MAAM,UAAU,eAAe,MAAM,UAAU;AAC5D,SACE,gBAAAC,MAAC,UAAK,WAAU,wCAAuC,eAAW,MAC/D;AAAA,YAAQ,CAAC,MAAM,eACd,gBAAAD;AAAA,MAAC,OAAO;AAAA,MAAP;AAAA,QACC,WAAU;AAAA,QACV,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,EAAE;AAAA,QAC/C,YAAY,EAAE,UAAU,KAAK,MAAM,WAAW,QAAQ,OAAO,kBAAkB;AAAA;AAAA,IACjF,IACE;AAAA,IACJ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,OACI,yBACA,MAAM,UAAU,aAAa,MAAM,UAAU,UAC3C,yBACA,MAAM,UAAU,eAAe,MAAM,UAAU,gBAC7C,oBACA;AAAA,QACV;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AAEA,SAAS,gBAAgB,OAAoC;AAC3D,MAAI,UAAU,eAAe,UAAU,YAAY;AACjD,WAAO;AAAA,EACT;AACA,MAAI,UAAU,aAAa,UAAU,SAAS;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,gBAAgB,UAAU,kBAAkB,UAAU,YAAY;AAC9E,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAoC;AAC5D,MAAI,UAAU,eAAe,UAAU,YAAY;AACjD,WAAO;AAAA,EACT;AACA,MAAI,UAAU,aAAa,UAAU,SAAS;AAC5C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,gBAAgB,UAAU,kBAAkB,UAAU,YAAY;AAC9E,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,sBAAsB,OAA+D;AAC5F,SAAO,EAAE,GAAG,MAAM;AACpB;AAEA,SAAS,4BAA4B,aAAkD;AACrF,MAAI,OAAO,iBAAiB,YAAa,QAAO;AAChD,MAAI;AACF,UAAM,QAAQ,aAAa,QAAQ,GAAG,6BAA6B,IAAI,WAAW,EAAE;AACpF,WAAO,gBAAgB,KAAK,IAAI,QAAQ;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,6BAA6B,aAAqB,OAAmC;AAC5F,MAAI,OAAO,iBAAiB,YAAa;AACzC,MAAI;AACF,iBAAa,QAAQ,GAAG,6BAA6B,IAAI,WAAW,IAAI,KAAK;AAAA,EAC/E,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,gBAAgB,OAAqD;AAC5E,SACE,UAAU,8BACV,UAAU,8CACV,UAAU,+CACV,UAAU,oDACV,UAAU,+CACV,UAAU,gDACV,UAAU;AAEd;AAGO,IAAM,0BAA0B;AAEhC,IAAM,qCAAqC;AAE3C,IAAM,8BAA8B;AAEpC,IAAM,uBAAuB;","names":["useRef","jsx","jsxs","useRef"]}
|
package/dist/session-ui.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.1",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@dnd-kit/utilities": "3.2.2",
|
|
76
76
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
77
77
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
78
|
-
"@opengeni/sdk": "^0.
|
|
78
|
+
"@opengeni/sdk": "^0.46.0",
|
|
79
79
|
"clsx": "^2.1.1",
|
|
80
80
|
"lucide-react": "^1.8.0",
|
|
81
81
|
"motion": "^12.0.0",
|
|
@@ -957,7 +957,7 @@ export const Input = forwardRef<HTMLTextAreaElement, ComposerInputProps>(functio
|
|
|
957
957
|
paletteOpen ? `${controller.listboxId}-option-${controller.palette.highlight}` : undefined
|
|
958
958
|
}
|
|
959
959
|
className={cn(
|
|
960
|
-
"block w-full resize-none bg-transparent px-3.5 pt-3 pb-1 text-og-composer md:px-4 md:text-og-composer-wide",
|
|
960
|
+
"block w-full resize-none bg-transparent px-3.5 pt-3 pb-1 text-og-composer md:px-4 md:text-og-composer-wide pointer-coarse:min-h-11",
|
|
961
961
|
"text-og-fg placeholder:text-og-fg-subtle focus:outline-none focus-visible:outline-none",
|
|
962
962
|
"disabled:cursor-not-allowed disabled:opacity-60",
|
|
963
963
|
className,
|
|
@@ -1095,7 +1095,7 @@ export const AttachButton = forwardRef<HTMLButtonElement, ComposerAttachButtonPr
|
|
|
1095
1095
|
className={cn(
|
|
1096
1096
|
"inline-flex size-8 items-center justify-center rounded-og-md",
|
|
1097
1097
|
"text-og-fg-muted transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
|
|
1098
|
-
"disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-
|
|
1098
|
+
"disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:size-11",
|
|
1099
1099
|
className,
|
|
1100
1100
|
)}
|
|
1101
1101
|
>
|
|
@@ -1167,7 +1167,7 @@ export const PauseButton = forwardRef<HTMLButtonElement, ComposerPauseButtonProp
|
|
|
1167
1167
|
disabled={busy}
|
|
1168
1168
|
aria-label={ariaLabel ?? controller.messages.pauseAriaLabel}
|
|
1169
1169
|
className={cn(
|
|
1170
|
-
"inline-flex size-8 items-center justify-center rounded-og-md border border-og-border pointer-coarse:size-
|
|
1170
|
+
"inline-flex size-8 items-center justify-center rounded-og-md border border-og-border pointer-coarse:size-11",
|
|
1171
1171
|
"bg-og-surface-2 text-og-fg-muted transition-colors duration-150",
|
|
1172
1172
|
"hover:border-og-status-waiting/50 hover:text-og-status-waiting",
|
|
1173
1173
|
"disabled:opacity-50",
|
|
@@ -1216,7 +1216,7 @@ export const SendButton = forwardRef<HTMLButtonElement, ComposerSendButtonProps>
|
|
|
1216
1216
|
: controller.messages.sendMessageAriaLabel)
|
|
1217
1217
|
}
|
|
1218
1218
|
className={cn(
|
|
1219
|
-
"inline-flex size-8 items-center justify-center rounded-og-md pointer-coarse:size-
|
|
1219
|
+
"inline-flex size-8 items-center justify-center rounded-og-md pointer-coarse:size-11",
|
|
1220
1220
|
"bg-og-accent text-og-accent-fg shadow-og-sm",
|
|
1221
1221
|
"transition-[background-color,transform,opacity] duration-150 ease-og-spring",
|
|
1222
1222
|
"hover:bg-og-accent-strong active:scale-95",
|
|
@@ -93,7 +93,7 @@ export function CopyButton({
|
|
|
93
93
|
aria-label={tip}
|
|
94
94
|
onClick={onClick}
|
|
95
95
|
className={cn(
|
|
96
|
-
"inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm",
|
|
96
|
+
"inline-flex size-7 shrink-0 items-center justify-center rounded-og-sm pointer-coarse:size-11",
|
|
97
97
|
"text-og-fg-subtle transition-[opacity,color,background-color] duration-150",
|
|
98
98
|
"hover:bg-og-surface-2/80 hover:text-og-fg",
|
|
99
99
|
"focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent/40",
|
|
@@ -2184,12 +2184,12 @@ function CompactionRow({ item }: { item: ContextCompactionItem }) {
|
|
|
2184
2184
|
: null;
|
|
2185
2185
|
const title =
|
|
2186
2186
|
item.phase === "started"
|
|
2187
|
-
? "Compacting conversation
|
|
2187
|
+
? "Compacting conversation history…"
|
|
2188
2188
|
: item.phase === "compacted"
|
|
2189
2189
|
? before && after
|
|
2190
|
-
? `Conversation
|
|
2191
|
-
: "Conversation
|
|
2192
|
-
: "Couldn’t compact conversation
|
|
2190
|
+
? `Conversation history compacted · ~${before} → ~${after} estimated history tokens`
|
|
2191
|
+
: "Conversation history compacted"
|
|
2192
|
+
: "Couldn’t compact conversation history";
|
|
2193
2193
|
const subtitle =
|
|
2194
2194
|
item.phase === "compacted"
|
|
2195
2195
|
? "Chat history above is unchanged"
|
|
@@ -2247,7 +2247,7 @@ function MessageFooterTime({ occurredAt }: { occurredAt: string }) {
|
|
|
2247
2247
|
className={cn(
|
|
2248
2248
|
"shrink-0 tabular-nums text-og-xs text-og-fg-subtle",
|
|
2249
2249
|
"opacity-0 transition-opacity duration-150",
|
|
2250
|
-
"group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 pointer-coarse:opacity-
|
|
2250
|
+
"group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 pointer-coarse:opacity-100",
|
|
2251
2251
|
)}
|
|
2252
2252
|
>
|
|
2253
2253
|
{formatClockTime(occurredAt)}
|