@opengeni/react 3.3.2 → 3.7.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +241 -18
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-MKKFRRRV.js → browser-viewer-E5DDEFQ4.js} +3 -3
- package/dist/{chunk-2DZVYHVO.js → chunk-4JFUT22W.js} +39 -13
- package/dist/chunk-4JFUT22W.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-3AGBGTRT.js → chunk-C6WDLHBA.js} +148 -26
- package/dist/chunk-C6WDLHBA.js.map +1 -0
- package/dist/{chunk-YG4QEQF5.js → chunk-DOK4KZF2.js} +7863 -7736
- package/dist/chunk-DOK4KZF2.js.map +1 -0
- package/dist/{chunk-JAQ5K7W6.js → chunk-FJBOU2TH.js} +167 -58
- package/dist/chunk-FJBOU2TH.js.map +1 -0
- package/dist/{chunk-XYRDDSXF.js → chunk-R55HFTT3.js} +80 -34
- package/dist/chunk-R55HFTT3.js.map +1 -0
- package/dist/{chunk-ZPBTI3TE.js → chunk-W2RGYBCV.js} +4 -4
- package/dist/{chunk-WSK7G5LE.js → chunk-W3OUAH6M.js} +80 -80
- package/dist/chunk-W3OUAH6M.js.map +1 -0
- package/dist/{chunk-X4A66EWZ.js → chunk-WMGF4Z7X.js} +41 -43
- package/dist/chunk-WMGF4Z7X.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/components/artifacts/index.d.ts +1 -1
- package/dist/components/artifacts/published-html-artifact-frame.d.ts +45 -3
- package/dist/components/composer.d.ts +2 -0
- package/dist/components/session-chrome.d.ts +1 -1
- package/dist/composer.js +4 -4
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
- package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
- package/dist/hooks/use-file-attachments.d.ts +8 -0
- package/dist/hooks/use-sandbox-files.d.ts +4 -1
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/hooks/use-workspace-edit.d.ts +4 -0
- package/dist/index.js +195 -141
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/model-policy.d.ts +2 -1
- package/dist/model-policy.js +1 -1
- package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js} +2 -2
- package/dist/realtime.js +2 -2
- package/dist/session-ui.js +6 -6
- package/dist/session.js +4 -4
- package/dist/timeline/registry.d.ts +2 -0
- package/dist/timeline/types.d.ts +3 -1
- package/package.json +2 -2
- package/src/artifacts.ts +3 -0
- package/src/client.ts +2 -1
- package/src/components/artifacts/index.ts +3 -0
- package/src/components/artifacts/published-html-artifact-frame.tsx +284 -4
- package/src/components/composer.tsx +103 -29
- package/src/components/message-timeline.tsx +48 -10
- package/src/components/model-policy-picker.tsx +37 -10
- package/src/components/sandbox-workspace.tsx +18 -7
- package/src/components/session-chrome.tsx +3 -3
- package/src/hooks/use-file-attachments.ts +62 -2
- package/src/hooks/use-sandbox-files.ts +68 -20
- package/src/hooks/use-session-events.ts +176 -21
- package/src/hooks/use-workspace-edit.ts +23 -5
- package/src/model-policy.ts +48 -53
- package/src/timeline/fleet-decision-projection.ts +2 -1
- package/src/timeline/fleet-decision-row.tsx +1 -0
- package/src/timeline/projection.ts +157 -8
- package/src/timeline/registry.ts +15 -3
- package/src/timeline/tool-renderers.tsx +117 -1
- package/src/timeline/types.ts +10 -1
- package/styles/compiled.css +5 -0
- package/dist/chunk-2DZVYHVO.js.map +0 -1
- package/dist/chunk-3AGBGTRT.js.map +0 -1
- package/dist/chunk-JAQ5K7W6.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js.map +0 -1
- package/dist/chunk-XYRDDSXF.js.map +0 -1
- package/dist/chunk-YG4QEQF5.js.map +0 -1
- package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
- /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-E5DDEFQ4.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{chunk-ZPBTI3TE.js.map → chunk-W2RGYBCV.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
- /package/dist/{queue-surface-implementation-BSKUWHRI.js.map → queue-surface-implementation-XL2A6PBM.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/model-policy-picker.tsx"],"sourcesContent":["import type { ClientModel, LatencyMode, ReasoningEffort } from \"@opengeni/sdk\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n Globe2Icon,\n KeyRoundIcon,\n ZapIcon,\n} from \"lucide-react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { DropdownMenu } from \"radix-ui\";\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type SVGProps,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenSource, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n effortOptionsForModel,\n findPickerRow,\n groupPickerRowsByBillingClass,\n labelReasoningEffort,\n payerSummaryForModel,\n projectClientModelRows,\n runnableLatencyModesForModel,\n type PickerBillingClass,\n type PickerModelRow,\n} from \"../model-policy\";\n\ntype ClientPickerModelRow = PickerModelRow<ClientModel>;\n\ntype NavLevel = \"providers\" | \"models\" | \"thinking\";\n\ntype PickerNavState = {\n level: NavLevel;\n rail: PickerBillingClass | null;\n modelId: string | null;\n};\n\ntype NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);\n\nexport type ModelPolicyPickerMessages = {\n label: string;\n loading: string;\n noModels: string;\n thinking: string;\n fast: string;\n fastRateHint: string;\n codexOnly: string;\n billingHints: Record<PickerBillingClass, string>;\n};\n\nexport const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {\n label: \"Model and effort\",\n loading: \"Loading model catalog…\",\n noModels: \"No models available.\",\n thinking: \"Thinking\",\n fast: \"Fast\",\n fastRateHint: \"2× rate\",\n codexOnly: \"Codex-only session\",\n billingHints: {\n opengeni_credits: \"Will use credits\",\n external: \"Provider terms and limits apply\",\n codex_subscription: \"ChatGPT / Codex plan\",\n supergrok_subscription: \"SuperGrok / xAI plan\",\n byok: \"Billed to the workspace provider account\",\n organization_byok: \"Billed to the organization provider account\",\n },\n};\n\nexport type ModelPolicyPickerProps = {\n /** Lightweight deployment models. Catalog rows take precedence when supplied. */\n models?: ClientModel[] | undefined;\n /** Catalog-backed rows with availability and billing-class truth. */\n rows?: PickerModelRow[] | undefined;\n model: string;\n effort: ReasoningEffort;\n latencyMode: LatencyMode;\n disabled?: boolean | undefined;\n loading?: boolean | undefined;\n error?: string | null | undefined;\n /** Controlled menu state. Omit for the built-in uncontrolled behavior. */\n open?: boolean | undefined;\n /** Initial menu state when `open` is uncontrolled. */\n defaultOpen?: boolean | undefined;\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Non-Codex models stay visible but cannot be selected. */\n codexOnly?: boolean | undefined;\n /** Resets drill-down navigation when the session scope changes. */\n sessionKey?: string | undefined;\n /** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /** Hide latency controls on surfaces whose saved policy does not include latency. */\n allowLatencyMode?: boolean | undefined;\n /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */\n contentClassName?: string | undefined;\n /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */\n contentStyle?: CSSProperties | undefined;\n className?: string | undefined;\n messages?: Partial<ModelPolicyPickerMessages> | undefined;\n onModelChange: (modelId: string) => void;\n onEffortChange: (effort: ReasoningEffort) => void;\n onLatencyModeChange: (latencyMode: LatencyMode) => void;\n};\n\nconst SLIDE_EASE = [0.22, 1, 0.36, 1] as const;\n\nfunction OpenGeniMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg\n viewBox=\"0 0 140 133\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.2}\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <g transform=\"translate(-17.5,-20.999893188476562) scale(1.75)\">\n <g transform=\"translate(0,-952.36218)\">\n <path d=\"m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z\" />\n </g>\n </g>\n </svg>\n );\n}\n\nfunction ChatGptMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n </svg>\n );\n}\n\nfunction XaiMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 466.04 516.93\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <polygon points=\"0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71\" />\n <polygon points=\"0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92\" />\n <polygon points=\"466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0\" />\n <polygon points=\"380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92\" />\n </svg>\n );\n}\n\nexport function BillingClassMark(props: {\n billingClass: PickerBillingClass;\n className?: string | undefined;\n \"aria-label\"?: string | undefined;\n}) {\n const labels: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n external: \"External provider\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n byok: \"Workspace provider account\",\n organization_byok: \"Organization provider account\",\n };\n const label = props[\"aria-label\"] ?? labels[props.billingClass];\n const accessibility =\n label.length === 0\n ? { \"aria-hidden\": true as const }\n : { role: \"img\" as const, \"aria-label\": label };\n const shell = cn(\n \"inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle\",\n props.className,\n );\n const mark = \"size-3.5\";\n return (\n <span\n className={shell}\n data-testid={`billing-class-icon-${props.billingClass}`}\n {...accessibility}\n >\n {props.billingClass === \"opengeni_credits\" ? (\n <OpenGeniMark className={mark} />\n ) : props.billingClass === \"external\" ? (\n <Globe2Icon className={mark} aria-hidden />\n ) : props.billingClass === \"codex_subscription\" ? (\n <ChatGptMark className={mark} />\n ) : props.billingClass === \"supergrok_subscription\" ? (\n <XaiMark className={mark} />\n ) : (\n <KeyRoundIcon className={mark} aria-hidden />\n )}\n </span>\n );\n}\n\nfunction isCodexModel(model: ClientModel): boolean {\n return model.id.startsWith(\"codex/\") || model.source === \"codex\";\n}\n\nfunction billingClassForMissingSelection(modelId: string): PickerBillingClass {\n if (modelId.startsWith(\"workspace-gateway/\")) return \"byok\";\n if (modelId.startsWith(\"workspace-openrouter/\")) return \"byok\";\n // A deployment OpenRouter ID does not encode its workspace-facing cost.\n // Missing rows therefore use the credits-safe rail instead of falsely\n // claiming that an unknown former selection was externally funded.\n if (modelId.startsWith(\"openrouter/\")) return \"opengeni_credits\";\n if (modelId.startsWith(\"codex/\")) return \"codex_subscription\";\n if (modelId.startsWith(\"supergrok/\")) return \"supergrok_subscription\";\n return \"opengeni_credits\";\n}\n\nfunction applyCodexOnly(\n rows: ClientPickerModelRow[],\n codexOnly: boolean,\n unavailableReason: string,\n): ClientPickerModelRow[] {\n if (!codexOnly) return rows;\n return rows.map((row) =>\n isCodexModel(row.catalog)\n ? row\n : {\n ...row,\n selectable: false,\n unavailableReason: row.unavailableReason ?? unavailableReason,\n },\n );\n}\n\nfunction effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {\n const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);\n}\n\nfunction defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {\n const selected = findPickerRow(rows, modelId);\n if (!selected) {\n const rails = new Set(rows.map((row) => row.billingClass));\n const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;\n return onlyRail\n ? { level: \"models\", rail: onlyRail, modelId: null }\n : { level: \"providers\", rail: null, modelId: null };\n }\n return {\n level: \"thinking\",\n rail: selected.billingClass,\n modelId: selected.id,\n };\n}\n\nfunction usePickerNavState(\n sessionKey: string | undefined,\n rows: ClientPickerModelRow[],\n model: string,\n): [PickerNavState, (next: NavUpdater) => void] {\n const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));\n useEffect(() => {\n setState(defaultNavState(rows, model));\n // The menu deliberately preserves its drill-down while the same session is mounted.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessionKey]);\n return [state, setState];\n}\n\nexport function PickerNavRow(props: {\n label: string;\n hint?: string | undefined;\n icon?: ReactNode;\n trailing?: ReactNode;\n showChevron?: boolean | undefined;\n disabled?: boolean | undefined;\n title?: string | undefined;\n active?: boolean | undefined;\n testId?: string | undefined;\n onClick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n disabled={props.disabled}\n title={props.title}\n onClick={props.onClick}\n data-testid={props.testId}\n className={cn(\n \"flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\",\n props.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n {props.icon}\n <span className=\"min-w-0 flex-1\">\n <span className={cn(\"block truncate text-og-menu\", props.active && \"font-medium\")}>\n {props.label}\n </span>\n {props.hint ? (\n <span className=\"mt-0.5 block truncate text-og-control text-og-fg-subtle\">\n {props.hint}\n </span>\n ) : null}\n </span>\n {props.trailing ? <span className=\"ml-auto shrink-0\">{props.trailing}</span> : null}\n {props.showChevron === false ? null : (\n <ChevronRightIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n )}\n </button>\n );\n}\n\nexport function PickerBackHeader(props: {\n label: string;\n icon?: ReactNode;\n onBack: () => void;\n trailing?: ReactNode;\n}) {\n return (\n <div className=\"mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5\">\n <button\n type=\"button\"\n onClick={props.onBack}\n data-testid=\"model-picker-back\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\"\n >\n <ChevronLeftIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n {props.icon}\n <span className=\"min-w-0 flex-1 truncate text-og-menu font-medium\">{props.label}</span>\n </button>\n {props.trailing ? <div className=\"relative z-10 shrink-0\">{props.trailing}</div> : null}\n </div>\n );\n}\n\nexport function PickerAnimatedPage(props: {\n pageKey: string;\n direction: 1 | -1;\n children: ReactNode;\n}) {\n const reduceMotion = useReducedMotion();\n return (\n <motion.div\n key={props.pageKey}\n initial={reduceMotion ? false : { x: props.direction * 14, opacity: 0 }}\n animate={{ x: 0, opacity: 1 }}\n transition={{ duration: reduceMotion ? 0 : 0.18, ease: SLIDE_EASE }}\n className=\"w-full\"\n >\n {props.children}\n </motion.div>\n );\n}\n\nexport function ModelPolicyPickerMenu(\n props: ModelPolicyPickerProps & {\n nav?: PickerNavState | undefined;\n onNavChange?: ((next: NavUpdater) => void) | undefined;\n },\n) {\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const rows = effectiveRows(props);\n const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);\n const [localNav, setLocalNav] = usePickerNavState(\n props.nav === undefined ? props.sessionKey : undefined,\n rows,\n props.model,\n );\n const nav = props.nav ?? localNav;\n const setNav = props.onNavChange ?? setLocalNav;\n const [direction, setDirection] = useState<1 | -1>(1);\n\n const effectiveNav =\n nav.level === \"providers\" && groups.length === 1\n ? {\n level: \"models\" as const,\n rail: groups[0]!.billingClass,\n modelId: null,\n }\n : nav;\n const activeGroup =\n effectiveNav.rail === null\n ? undefined\n : groups.find((group) => group.billingClass === effectiveNav.rail);\n const focusModel =\n effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);\n const selectedRow = findPickerRow(rows, props.model);\n\n const go = (next: PickerNavState, nextDirection: 1 | -1) => {\n setDirection(nextDirection);\n setNav(next);\n };\n\n const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {\n if (!row.selectable) return;\n if (row.id !== props.model) props.onModelChange(row.id);\n props.onEffortChange(effort);\n };\n\n const pageKey =\n effectiveNav.level === \"providers\"\n ? \"providers\"\n : effectiveNav.level === \"models\"\n ? `models:${effectiveNav.rail ?? \"\"}`\n : `thinking:${effectiveNav.modelId ?? \"\"}`;\n\n let body: ReactNode;\n if (props.loading) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.loading}</p>;\n } else if (groups.length === 0) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.noModels}</p>;\n } else if (effectiveNav.level === \"providers\") {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n active={selectedRow?.billingClass === group.billingClass}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n } else if (effectiveNav.level === \"models\" && activeGroup) {\n body = (\n <div data-testid=\"model-picker-models\">\n {groups.length > 1 ? (\n <PickerBackHeader\n label={activeGroup.label}\n icon={<BillingClassMark billingClass={activeGroup.billingClass} />}\n onBack={() => go({ level: \"providers\", rail: null, modelId: null }, -1)}\n />\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {activeGroup.rows.map((row) => (\n <PickerNavRow\n key={`${row.billingClass}:${row.id}`}\n label={row.label}\n hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}\n disabled={!row.selectable}\n title={row.unavailableReason ?? undefined}\n active={row.id === props.model}\n testId={`model-picker-choice-${row.id}`}\n onClick={() => {\n if (row.selectable) {\n go(\n {\n level: \"thinking\",\n rail: row.billingClass,\n modelId: row.id,\n },\n 1,\n );\n }\n }}\n />\n ))}\n </div>\n </div>\n );\n } else if (effectiveNav.level === \"thinking\" && focusModel) {\n const activeModel = focusModel.id === props.model;\n const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes(\"fast\");\n body = (\n <div data-testid=\"model-picker-reasoning\">\n <PickerBackHeader\n label={focusModel.label}\n icon={<BillingClassMark billingClass={focusModel.billingClass} />}\n onBack={() => go({ level: \"models\", rail: focusModel.billingClass, modelId: null }, -1)}\n trailing={\n supportsFast && props.allowLatencyMode !== false ? (\n <button\n type=\"button\"\n disabled={!focusModel.selectable}\n aria-pressed={activeModel && props.latencyMode === \"fast\"}\n aria-label={\n activeModel && props.latencyMode === \"fast\"\n ? `Disable ${messages.fast}`\n : `Enable ${messages.fast}`\n }\n title={`${messages.fast} · ${messages.fastRateHint}`}\n data-testid=\"model-picker-fast\"\n onClick={() => {\n if (!activeModel) {\n props.onModelChange(focusModel.id);\n }\n props.onLatencyModeChange(\n activeModel && props.latencyMode === \"fast\" ? \"standard\" : \"fast\",\n );\n }}\n className={cn(\n \"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n activeModel && props.latencyMode === \"fast\" && \"text-og-fg\",\n )}\n >\n <ZapIcon\n className={cn(\n \"size-3.5\",\n activeModel && props.latencyMode === \"fast\" && \"fill-current\",\n )}\n />\n </button>\n ) : null\n }\n />\n <p className=\"px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase\">\n {messages.thinking}\n </p>\n {focusModel.catalog.capabilities?.inputModalities.includes(\"image\") === false ? (\n <p className=\"px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle\">\n Unsupported attachments stay in the session but are hidden from this model.\n </p>\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {effortOptionsForModel(focusModel.catalog).map((effort) => {\n const selected = activeModel && effort === props.effort;\n return (\n <button\n key={effort}\n type=\"button\"\n disabled={!focusModel.selectable}\n onClick={() => selectEffort(focusModel, effort)}\n className={cn(\n \"flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n selected && \"bg-og-surface-2\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{labelReasoningEffort(effort)}</span>\n {selected ? (\n <CheckIcon\n className=\"ml-auto size-3.5 shrink-0\"\n data-testid=\"model-picker-effort-check\"\n />\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n } else {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div data-testid=\"model-picker-menu\" className=\"relative overflow-hidden\">\n {props.error ? (\n <p className=\"px-2 py-1 text-og-control text-og-status-failed\" role=\"alert\">\n {props.error}\n </p>\n ) : null}\n <PickerAnimatedPage pageKey={pageKey} direction={direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\n/** @internal Shared state seam kept outside the Radix portal for deterministic testing. */\nexport function useModelPolicyPickerState(props: ModelPolicyPickerProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);\n const open = props.open ?? uncontrolledOpen;\n const rows = effectiveRows(props);\n const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);\n const previousControlledOpen = useRef(props.open);\n useEffect(() => {\n const wasOpen = previousControlledOpen.current;\n previousControlledOpen.current = props.open;\n if (props.open === true && wasOpen !== true) {\n setNav(defaultNavState(rows, props.model));\n }\n // Reset only on the controlled closed-to-open edge; ordinary rerenders\n // deliberately preserve the current drill-down page.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.open]);\n const setOpen = (next: boolean) => {\n if (next) {\n setNav(defaultNavState(rows, props.model));\n }\n if (props.open === undefined) setUncontrolledOpen(next);\n props.onOpenChange?.(next);\n };\n return { open, setOpen, rows, nav, setNav };\n}\n\nexport function ModelPolicyPicker(props: ModelPolicyPickerProps) {\n const trigger = usePortalTokenSource<HTMLButtonElement>();\n const portalStyle = usePortalTokenStyle(trigger.source);\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const { open, setOpen, rows, nav, setNav } = useModelPolicyPickerState(props);\n const selected = findPickerRow(rows, props.model);\n\n if (props.loading) {\n return (\n <span\n className={cn(\n \"og-root inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2\",\n props.className,\n )}\n aria-label={messages.loading}\n data-testid=\"model-picker-loading\"\n />\n );\n }\n return (\n <DropdownMenu.Root open={open} onOpenChange={setOpen}>\n <DropdownMenu.Trigger asChild>\n <button\n ref={trigger.ref}\n type=\"button\"\n disabled={props.disabled}\n aria-label={messages.label}\n className={cn(\n \"og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2\",\n props.className,\n )}\n >\n <BillingClassMark\n billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}\n className=\"text-og-fg\"\n />\n <span className=\"og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden\">\n {selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden\">\n {selected?.shortLabel ?? selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-effort max-sm:hidden\">\n {labelReasoningEffort(props.effort)}\n </span>\n {props.latencyMode === \"fast\" ? (\n <ZapIcon\n className=\"size-3.5 shrink-0 fill-current stroke-current text-og-fg\"\n aria-label={messages.fast}\n data-testid=\"model-picker-fast-icon\"\n />\n ) : null}\n <ChevronDownIcon className=\"size-3 shrink-0\" />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n align=\"start\"\n side={props.menuSide ?? \"bottom\"}\n sideOffset={8}\n collisionPadding={12}\n onCloseAutoFocus={(event) => event.preventDefault()}\n className={cn(\n \"og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg\",\n props.contentClassName,\n )}\n style={{ ...portalStyle, ...props.contentStyle }}\n data-testid=\"model-picker-content\"\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />\n </div>\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -17,15 +17,15 @@ import {
|
|
|
17
17
|
encodeStreamClose,
|
|
18
18
|
encodeStreamOpen
|
|
19
19
|
} from "./chunk-5YK2ZDIG.js";
|
|
20
|
+
import {
|
|
21
|
+
copyTextToClipboard
|
|
22
|
+
} from "./chunk-JVYQRRC4.js";
|
|
20
23
|
import {
|
|
21
24
|
usePageLiveActivity
|
|
22
25
|
} from "./chunk-KZ73RL76.js";
|
|
23
26
|
import {
|
|
24
27
|
useEmbeddedComputerInteraction
|
|
25
28
|
} from "./chunk-NEXLLTP4.js";
|
|
26
|
-
import {
|
|
27
|
-
copyTextToClipboard
|
|
28
|
-
} from "./chunk-JVYQRRC4.js";
|
|
29
29
|
import {
|
|
30
30
|
cn
|
|
31
31
|
} from "./chunk-22KP6LR5.js";
|
|
@@ -2297,4 +2297,4 @@ export {
|
|
|
2297
2297
|
ComputerViewer,
|
|
2298
2298
|
computerKey
|
|
2299
2299
|
};
|
|
2300
|
-
//# sourceMappingURL=chunk-
|
|
2300
|
+
//# sourceMappingURL=chunk-BV5A4OTE.js.map
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildTimeline,
|
|
3
|
-
createOlderHistoryLoadReceipt,
|
|
4
|
-
groupTimeline,
|
|
5
|
-
isActionableHumanInputRequest,
|
|
6
|
-
sessionStatusFromEvents
|
|
7
|
-
} from "./chunk-JAQ5K7W6.js";
|
|
8
1
|
import {
|
|
9
2
|
useDebouncedCallback,
|
|
10
3
|
useMutationRunner,
|
|
@@ -21,6 +14,13 @@ import {
|
|
|
21
14
|
useEmbeddedSessionMcpApprovalPolicy,
|
|
22
15
|
useEmbeddedSessionRead
|
|
23
16
|
} from "./chunk-NEXLLTP4.js";
|
|
17
|
+
import {
|
|
18
|
+
buildTimeline,
|
|
19
|
+
createOlderHistoryLoadReceipt,
|
|
20
|
+
groupTimeline,
|
|
21
|
+
isActionableHumanInputRequest,
|
|
22
|
+
sessionStatusFromEvents
|
|
23
|
+
} from "./chunk-FJBOU2TH.js";
|
|
24
24
|
|
|
25
25
|
// src/embedded-session-client.ts
|
|
26
26
|
var requiredMethods = [
|
|
@@ -192,11 +192,11 @@ function useSession(sessionId, options = {}) {
|
|
|
192
192
|
}
|
|
193
193
|
|
|
194
194
|
// src/hooks/use-session-events.ts
|
|
195
|
+
import {
|
|
196
|
+
sessionEventStreamCoveredThrough
|
|
197
|
+
} from "@opengeni/sdk";
|
|
195
198
|
import { startTransition, useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
196
|
-
var
|
|
197
|
-
var OLDER_PAGE_SIZE = 5e3;
|
|
198
|
-
var NEWER_PAGE_SIZE = 5e3;
|
|
199
|
-
var OLDEST_PAGE_SIZE = 1e3;
|
|
199
|
+
var SESSION_HISTORY_PAGE_SIZE = 255;
|
|
200
200
|
var INITIAL_FETCH_CAP = 1;
|
|
201
201
|
var OLDER_GROUP_TARGET = 32;
|
|
202
202
|
var OLDER_FETCH_CAP = 2;
|
|
@@ -204,7 +204,12 @@ var NEWER_GROUP_TARGET = 32;
|
|
|
204
204
|
var NEWER_FETCH_CAP = 2;
|
|
205
205
|
var OLDEST_GROUP_TARGET = 32;
|
|
206
206
|
var OLDEST_FETCH_CAP = 2;
|
|
207
|
-
var BOUNDARY_PAGE_CAP =
|
|
207
|
+
var BOUNDARY_PAGE_CAP = 1;
|
|
208
|
+
var FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
|
|
209
|
+
var FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = SESSION_HISTORY_PAGE_SIZE;
|
|
210
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
|
|
211
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
|
|
212
|
+
var FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
|
|
208
213
|
var EMPTY_EVENTS = [];
|
|
209
214
|
var encoder = new TextEncoder();
|
|
210
215
|
var decoder = new TextDecoder();
|
|
@@ -276,6 +281,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
276
281
|
const loadingLatestRef = useRef2(false);
|
|
277
282
|
const viewModeRef = useRef2("live");
|
|
278
283
|
const initialWindowLoadedRef = useRef2(false);
|
|
284
|
+
const reconcileAfterPageResumeRef = useRef2(false);
|
|
279
285
|
const streamAbortRef = useRef2(null);
|
|
280
286
|
const streamKeyRef = useRef2(null);
|
|
281
287
|
const generationRef = useRef2(0);
|
|
@@ -314,6 +320,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
314
320
|
viewModeRef.current = "live";
|
|
315
321
|
setViewMode("live");
|
|
316
322
|
initialWindowLoadedRef.current = false;
|
|
323
|
+
reconcileAfterPageResumeRef.current = false;
|
|
317
324
|
}
|
|
318
325
|
const generation = generationRef.current + 1;
|
|
319
326
|
generationRef.current = generation;
|
|
@@ -322,14 +329,20 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
322
329
|
setLoadingOlder(false);
|
|
323
330
|
}
|
|
324
331
|
if (!sessionId || !streamEnabled) {
|
|
332
|
+
if (sessionId && enabled && !pageLive && !fullReplay && viewModeRef.current === "live" && initialWindowLoadedRef.current) {
|
|
333
|
+
reconcileAfterPageResumeRef.current = true;
|
|
334
|
+
}
|
|
325
335
|
setConnectionState("idle");
|
|
326
336
|
return;
|
|
327
337
|
}
|
|
328
338
|
if (viewMode === "history") {
|
|
339
|
+
reconcileAfterPageResumeRef.current = false;
|
|
329
340
|
setConnectionState("idle");
|
|
330
341
|
setInitialLoading(false);
|
|
331
342
|
return;
|
|
332
343
|
}
|
|
344
|
+
const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
|
|
345
|
+
reconcileAfterPageResumeRef.current = false;
|
|
333
346
|
const controller = new AbortController();
|
|
334
347
|
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
|
|
335
348
|
streamAbortRef.current = controller;
|
|
@@ -386,13 +399,57 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
386
399
|
};
|
|
387
400
|
void (async () => {
|
|
388
401
|
try {
|
|
402
|
+
if (reconcileForegroundResume) {
|
|
403
|
+
setConnectionState("connecting");
|
|
404
|
+
const cursor = streamResumeSequenceRef.current;
|
|
405
|
+
let plan = { kind: "resume" };
|
|
406
|
+
try {
|
|
407
|
+
plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
|
|
408
|
+
signal: controller.signal
|
|
409
|
+
});
|
|
410
|
+
} catch {
|
|
411
|
+
if (controller.signal.aborted) return;
|
|
412
|
+
plan = { kind: "resume" };
|
|
413
|
+
}
|
|
414
|
+
if (!isCurrent()) return;
|
|
415
|
+
if (plan.kind === "append") {
|
|
416
|
+
const current = eventWindowRef.current;
|
|
417
|
+
assertAppendOrder(current.events, plan.events);
|
|
418
|
+
const status = observeSessionStatus(plan.events, sessionStatusRef);
|
|
419
|
+
const next = boundBrowserSessionEventWindow([...current.events, ...plan.events]);
|
|
420
|
+
const retained = {
|
|
421
|
+
...next,
|
|
422
|
+
truncated: current.truncated || next.truncated
|
|
423
|
+
};
|
|
424
|
+
eventWindowRef.current = retained;
|
|
425
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
426
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
427
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
|
|
428
|
+
streamResumeSequenceRef.current = Math.max(
|
|
429
|
+
streamResumeSequenceRef.current,
|
|
430
|
+
plan.resumeSequence
|
|
431
|
+
);
|
|
432
|
+
if (status !== void 0) {
|
|
433
|
+
setSessionStatusProjection(status);
|
|
434
|
+
}
|
|
435
|
+
setEventWindow(retained);
|
|
436
|
+
if (retained.truncated) {
|
|
437
|
+
hasOlderRef.current = true;
|
|
438
|
+
setHasOlder(true);
|
|
439
|
+
}
|
|
440
|
+
} else if (plan.kind === "reload") {
|
|
441
|
+
initialWindowLoadedRef.current = false;
|
|
442
|
+
setError(null);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
389
445
|
if (!fullReplay && !initialWindowLoadedRef.current) {
|
|
390
446
|
setConnectionState("connecting");
|
|
391
447
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
392
448
|
before: Number.MAX_SAFE_INTEGER,
|
|
393
|
-
pageSize:
|
|
449
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
394
450
|
targetGroups: Number.POSITIVE_INFINITY,
|
|
395
451
|
maxFetches: INITIAL_FETCH_CAP,
|
|
452
|
+
boundaryPageCap: BOUNDARY_PAGE_CAP,
|
|
396
453
|
signal: controller.signal
|
|
397
454
|
});
|
|
398
455
|
if (!isCurrent()) {
|
|
@@ -481,6 +538,8 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
481
538
|
workspaceId,
|
|
482
539
|
sessionId,
|
|
483
540
|
after,
|
|
541
|
+
enabled,
|
|
542
|
+
pageLive,
|
|
484
543
|
streamEnabled,
|
|
485
544
|
fullReplay,
|
|
486
545
|
streamKey,
|
|
@@ -508,7 +567,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
508
567
|
try {
|
|
509
568
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
510
569
|
before,
|
|
511
|
-
pageSize:
|
|
570
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
512
571
|
targetGroups: OLDER_GROUP_TARGET,
|
|
513
572
|
maxFetches: OLDER_FETCH_CAP
|
|
514
573
|
});
|
|
@@ -599,7 +658,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
599
658
|
try {
|
|
600
659
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
601
660
|
after: 0,
|
|
602
|
-
pageSize:
|
|
661
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
603
662
|
targetGroups: OLDEST_GROUP_TARGET,
|
|
604
663
|
maxFetches: OLDEST_FETCH_CAP
|
|
605
664
|
});
|
|
@@ -663,7 +722,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
663
722
|
try {
|
|
664
723
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
665
724
|
after: afterSequence,
|
|
666
|
-
pageSize:
|
|
725
|
+
pageSize: SESSION_HISTORY_PAGE_SIZE,
|
|
667
726
|
targetGroups: NEWER_GROUP_TARGET,
|
|
668
727
|
maxFetches: NEWER_FETCH_CAP
|
|
669
728
|
});
|
|
@@ -1010,6 +1069,39 @@ function observeSessionStatus(events, ref) {
|
|
|
1010
1069
|
ref.current = latest;
|
|
1011
1070
|
return latest.status;
|
|
1012
1071
|
}
|
|
1072
|
+
async function planForegroundCatchup(client, workspaceId, sessionId, cursor, options = {}) {
|
|
1073
|
+
if (options.signal?.aborted) throw abortError();
|
|
1074
|
+
const session = await client.getSession(workspaceId, sessionId, {
|
|
1075
|
+
fresh: true,
|
|
1076
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1077
|
+
});
|
|
1078
|
+
if (options.signal?.aborted) throw abortError();
|
|
1079
|
+
const durableHead = Math.max(cursor, session.lastSequence);
|
|
1080
|
+
const rawGap = durableHead - cursor;
|
|
1081
|
+
if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
|
|
1082
|
+
return { kind: "resume" };
|
|
1083
|
+
}
|
|
1084
|
+
if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
|
|
1085
|
+
return { kind: "reload" };
|
|
1086
|
+
}
|
|
1087
|
+
const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
|
|
1088
|
+
pageSize: rawGap,
|
|
1089
|
+
...options.signal ? { signal: options.signal } : {}
|
|
1090
|
+
});
|
|
1091
|
+
const events = page.filter((event) => eventResumeSequence(event) > cursor).map(boundBrowserLegacyEvent);
|
|
1092
|
+
if (events.length === 0) {
|
|
1093
|
+
return { kind: "reload" };
|
|
1094
|
+
}
|
|
1095
|
+
assertAscending(events);
|
|
1096
|
+
const resumeSequence = maxResumeSequence(events);
|
|
1097
|
+
if (resumeSequence < durableHead) {
|
|
1098
|
+
return { kind: "reload" };
|
|
1099
|
+
}
|
|
1100
|
+
if (events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS || groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS || browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES) {
|
|
1101
|
+
return { kind: "reload" };
|
|
1102
|
+
}
|
|
1103
|
+
return { kind: "append", events, resumeSequence };
|
|
1104
|
+
}
|
|
1013
1105
|
async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
1014
1106
|
let cursor = options.before;
|
|
1015
1107
|
let buffer = [];
|
|
@@ -1037,7 +1129,7 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
1037
1129
|
}
|
|
1038
1130
|
}
|
|
1039
1131
|
let snapPages = 0;
|
|
1040
|
-
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages <
|
|
1132
|
+
while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < (options.boundaryPageCap ?? 0)) {
|
|
1041
1133
|
const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
|
|
1042
1134
|
pageSize: options.pageSize,
|
|
1043
1135
|
...options.signal ? { signal: options.signal } : {}
|
|
@@ -1202,12 +1294,9 @@ function maxResumeSequenceOrNull(events) {
|
|
|
1202
1294
|
return events.length > 0 ? maxResumeSequence(events) : null;
|
|
1203
1295
|
}
|
|
1204
1296
|
function eventResumeSequence(event) {
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
return
|
|
1208
|
-
}
|
|
1209
|
-
function asRecord(value) {
|
|
1210
|
-
return value && typeof value === "object" ? value : {};
|
|
1297
|
+
const streamedCoverage = sessionEventStreamCoveredThrough(event);
|
|
1298
|
+
if (streamedCoverage !== null) return streamedCoverage;
|
|
1299
|
+
return typeof event.coveredThrough === "number" && Number.isSafeInteger(event.coveredThrough) && event.coveredThrough >= event.sequence ? event.coveredThrough : event.sequence;
|
|
1211
1300
|
}
|
|
1212
1301
|
function assertAscending(events) {
|
|
1213
1302
|
for (let index = 1; index < events.length; index += 1) {
|
|
@@ -1245,10 +1334,22 @@ function abortError() {
|
|
|
1245
1334
|
// src/hooks/use-file-attachments.ts
|
|
1246
1335
|
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
1247
1336
|
var isImage = (file) => file.type.startsWith("image/");
|
|
1337
|
+
var fallbackAttachmentId = 0;
|
|
1338
|
+
function createAttachmentId() {
|
|
1339
|
+
const cryptoSource = globalThis.crypto;
|
|
1340
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
1341
|
+
fallbackAttachmentId += 1;
|
|
1342
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
1343
|
+
}
|
|
1344
|
+
function secureContextRequiredErrorCode(error) {
|
|
1345
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
|
|
1346
|
+
}
|
|
1248
1347
|
function useFileAttachments(options = {}) {
|
|
1249
1348
|
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
1250
1349
|
const pasteFilter = options.pasteFilter ?? isImage;
|
|
1251
1350
|
const [attachments, setAttachments] = useState3([]);
|
|
1351
|
+
const attachmentsRef = useRef3(attachments);
|
|
1352
|
+
attachmentsRef.current = attachments;
|
|
1252
1353
|
const sources = useRef3(/* @__PURE__ */ new Map());
|
|
1253
1354
|
const previewUrls = useRef3(/* @__PURE__ */ new Map());
|
|
1254
1355
|
const previewRetainers = useRef3(/* @__PURE__ */ new Map());
|
|
@@ -1332,6 +1433,7 @@ function useFileAttachments(options = {}) {
|
|
|
1332
1433
|
name: asset.filename,
|
|
1333
1434
|
contentType: asset.contentType,
|
|
1334
1435
|
sizeBytes: asset.sizeBytes,
|
|
1436
|
+
errorCode: void 0,
|
|
1335
1437
|
error: void 0
|
|
1336
1438
|
} : attachment
|
|
1337
1439
|
)
|
|
@@ -1343,6 +1445,7 @@ function useFileAttachments(options = {}) {
|
|
|
1343
1445
|
(attachment) => attachment.id === id ? {
|
|
1344
1446
|
...attachment,
|
|
1345
1447
|
status: "failed",
|
|
1448
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
1346
1449
|
error: error instanceof Error ? error.message : String(error)
|
|
1347
1450
|
} : attachment
|
|
1348
1451
|
)
|
|
@@ -1354,7 +1457,7 @@ function useFileAttachments(options = {}) {
|
|
|
1354
1457
|
const addFiles = useCallback3(
|
|
1355
1458
|
(files) => {
|
|
1356
1459
|
for (const file of files) {
|
|
1357
|
-
const id =
|
|
1460
|
+
const id = createAttachmentId();
|
|
1358
1461
|
sources.current.set(id, file);
|
|
1359
1462
|
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
1360
1463
|
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
@@ -1382,7 +1485,7 @@ function useFileAttachments(options = {}) {
|
|
|
1382
1485
|
}
|
|
1383
1486
|
setAttachments(
|
|
1384
1487
|
(current) => current.map(
|
|
1385
|
-
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
|
|
1488
|
+
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", errorCode: void 0, error: void 0 } : attachment
|
|
1386
1489
|
)
|
|
1387
1490
|
);
|
|
1388
1491
|
startUpload(id, file);
|
|
@@ -1426,6 +1529,7 @@ function useFileAttachments(options = {}) {
|
|
|
1426
1529
|
sizeBytes: file.sizeBytes,
|
|
1427
1530
|
status: "ready",
|
|
1428
1531
|
file,
|
|
1532
|
+
errorCode: void 0,
|
|
1429
1533
|
error: void 0
|
|
1430
1534
|
} : {
|
|
1431
1535
|
id: `restored:${file.id}`,
|
|
@@ -1443,6 +1547,23 @@ function useFileAttachments(options = {}) {
|
|
|
1443
1547
|
},
|
|
1444
1548
|
[workspaceId]
|
|
1445
1549
|
);
|
|
1550
|
+
const loadPreview = useCallback3(
|
|
1551
|
+
async (id, signal) => {
|
|
1552
|
+
const generation = scopeGeneration.current;
|
|
1553
|
+
const attachment = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
1554
|
+
const createDownloadUrl = client.createFileDownloadUrl;
|
|
1555
|
+
if (!attachment || attachment.status !== "ready" || !attachment.file || !attachment.contentType.startsWith("image/") || typeof createDownloadUrl !== "function" || signal?.aborted) {
|
|
1556
|
+
return void 0;
|
|
1557
|
+
}
|
|
1558
|
+
const fileId = attachment.file.id;
|
|
1559
|
+
const signed = await createDownloadUrl.call(client, workspaceId, fileId, { signal });
|
|
1560
|
+
if (scopeGeneration.current !== generation || signal?.aborted) return void 0;
|
|
1561
|
+
const current = attachmentsRef.current.find((candidate) => candidate.id === id);
|
|
1562
|
+
if (current?.status !== "ready" || current.file?.id !== fileId) return void 0;
|
|
1563
|
+
return signed.url;
|
|
1564
|
+
},
|
|
1565
|
+
[client, workspaceId]
|
|
1566
|
+
);
|
|
1446
1567
|
const remove = useCallback3(
|
|
1447
1568
|
(id) => {
|
|
1448
1569
|
sources.current.delete(id);
|
|
@@ -1475,6 +1596,7 @@ function useFileAttachments(options = {}) {
|
|
|
1475
1596
|
addFiles,
|
|
1476
1597
|
addFromPaste,
|
|
1477
1598
|
restoreReadyFiles,
|
|
1599
|
+
...typeof client.createFileDownloadUrl === "function" ? { loadPreview } : {},
|
|
1478
1600
|
retry,
|
|
1479
1601
|
retainPreview,
|
|
1480
1602
|
remove,
|
|
@@ -2465,4 +2587,4 @@ export {
|
|
|
2465
2587
|
isHumanInputEvent,
|
|
2466
2588
|
useHumanInputRequests
|
|
2467
2589
|
};
|
|
2468
|
-
//# sourceMappingURL=chunk-
|
|
2590
|
+
//# sourceMappingURL=chunk-C6WDLHBA.js.map
|