@opengeni/react 0.44.3 → 0.44.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/dist/chunk-22KP6LR5.js +31 -0
- package/dist/{chunk-TK7G6XLT.js.map → chunk-22KP6LR5.js.map} +1 -1
- package/dist/{chunk-SXIQK54Z.js → chunk-6EDV6YFE.js} +8 -8
- package/dist/{chunk-SXIQK54Z.js.map → chunk-6EDV6YFE.js.map} +1 -1
- package/dist/{chunk-U6K24XQD.js → chunk-EHSYZ4KP.js} +37 -16
- package/dist/chunk-EHSYZ4KP.js.map +1 -0
- package/dist/chunk-JB5725BI.js +48 -0
- package/dist/chunk-JB5725BI.js.map +1 -0
- package/dist/{chunk-QQRM3DO3.js → chunk-L3EWO3UK.js} +6 -6
- package/dist/{chunk-QQRM3DO3.js.map → chunk-L3EWO3UK.js.map} +1 -1
- package/dist/{chunk-YNYIAYXQ.js → chunk-LYRJUU5V.js} +23 -23
- package/dist/chunk-LYRJUU5V.js.map +1 -0
- package/dist/{chunk-ATSTR5P3.js → chunk-U3CSCMWA.js} +5 -49
- package/dist/chunk-U3CSCMWA.js.map +1 -0
- package/dist/{chunk-YDNWMKXO.js → chunk-VCBFUSUZ.js} +4 -4
- package/dist/chunk-VCBFUSUZ.js.map +1 -0
- package/dist/components/model-policy-picker.d.ts +10 -1
- package/dist/composer.js +5 -4
- package/dist/index.js +11 -9
- package/dist/index.js.map +1 -1
- package/dist/machines.js +2 -2
- package/dist/{queue-surface-implementation-THPAKGMD.js → queue-surface-implementation-J4XJFKRW.js} +41 -26
- package/dist/queue-surface-implementation-J4XJFKRW.js.map +1 -0
- package/dist/realtime/dropdown-menu.d.ts +4 -4
- package/dist/realtime.js +58 -20
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.js +5 -4
- package/package.json +1 -1
- package/src/components/composer.tsx +3 -3
- package/src/components/enrollment-device-flow.tsx +2 -2
- package/src/components/message-timeline.tsx +15 -15
- package/src/components/model-policy-picker.tsx +47 -14
- package/src/components/queue-surface-implementation.tsx +28 -18
- package/src/components/queue-surface-state.tsx +3 -3
- package/src/components/queue-surface.tsx +2 -2
- package/src/components/session-status.tsx +1 -1
- package/src/lib/cn.ts +14 -1
- package/src/lib/use-portal-token-style.ts +1 -1
- package/src/provider.tsx +4 -4
- package/src/realtime/dropdown-menu.tsx +51 -8
- package/src/realtime/realtime-control.tsx +2 -2
- package/styles/index.css +91 -11
- package/styles/tokens.css +61 -0
- package/dist/chunk-ATSTR5P3.js.map +0 -1
- package/dist/chunk-TK7G6XLT.js +0 -18
- package/dist/chunk-U6K24XQD.js.map +0 -1
- package/dist/chunk-YDNWMKXO.js.map +0 -1
- package/dist/chunk-YNYIAYXQ.js.map +0 -1
- package/dist/queue-surface-implementation-THPAKGMD.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/use-last-started-turn-policy.ts","../src/hooks/use-scheduled-tasks.ts","../src/hooks/use-workspace-sessions.ts","../src/hooks/use-environments.ts","../src/hooks/use-rigs.ts","../src/hooks/use-packs.ts","../src/hooks/use-workspaces.ts","../src/hooks/use-billing-usage.ts","../src/hooks/use-available-models.ts","../src/hooks/use-session-capabilities.ts","../src/hooks/use-desktop-stream.ts","../src/lib/relay-wire.ts","../src/hooks/use-relay-frame-stream.ts","../src/hooks/use-terminal-stream.ts","../src/hooks/use-sandbox-terminal.ts","../src/hooks/use-sandbox-files.ts","../src/hooks/use-sandbox-git.ts","../src/hooks/use-workspace-capture.ts","../src/hooks/use-workspace-edit.ts","../src/hooks/use-machine-chip.ts","../src/components/approval-surface.tsx","../src/components/chat-composer.tsx","../src/components/fleet-tile.tsx","../src/components/sandbox-terminal.tsx","../src/lib/xterm-theme.ts","../src/lib/xterm-renderer.ts","../src/components/file-browser.tsx","../src/lib/use-unicode-fonts.ts","../src/components/workbench-changes.tsx","../src/hooks/use-windowed-sections.ts","../src/components/diff-view.tsx","../src/components/pierre-file.tsx","../src/components/code-editor.tsx","../src/components/sandbox-files.tsx","../src/components/desktop-viewer.tsx","../src/components/workspace-dock.tsx","../src/components/sandbox-workspace.tsx","../src/hooks/use-codex-accounts.ts"],"sourcesContent":["import type { LatencyMode, ReasoningEffort, SessionEvent, SessionTurn } from \"@opengeni/sdk\";\nimport { useCallback, useEffect } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../session-context\";\nimport { usePolledValue, useSessionEventTrigger, type SessionEventFeedOptions } from \"./internal\";\n\n/** Refresh when a turn actually admits — not on composer picker / queue-only changes. */\nexport function isLastStartedTurnPolicyEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type === \"turn.started\";\n}\n\nexport type LastStartedTurnPolicy = {\n model: string;\n reasoningEffort: ReasoningEffort;\n latencyMode: LatencyMode;\n turnId: string;\n};\n\nexport type UseLastStartedTurnPolicyOptions = ClientOverride &\n SessionEventFeedOptions & {\n pollIntervalMs?: number | undefined;\n };\n\nexport type UseLastStartedTurnPolicyResult = {\n /** Null until some turn has durably emitted `turn.started`. */\n policy: LastStartedTurnPolicy | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nfunction policyFromTurn(turn: SessionTurn | null | undefined): LastStartedTurnPolicy | null {\n if (!turn) return null;\n return {\n model: turn.model,\n reasoningEffort: turn.reasoningEffort,\n latencyMode: turn.latencyMode ?? \"standard\",\n turnId: turn.id,\n };\n}\n\n/**\n * Model·effort·latency of the newest admitted turn (`turn.started`). This is\n * historical runtime truth for the session header — not the composer next-turn\n * picker and not the frozen session creation defaults.\n */\nexport function useLastStartedTurnPolicy(\n sessionId: string | null | undefined,\n options: UseLastStartedTurnPolicyOptions = {},\n): UseLastStartedTurnPolicyResult {\n const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } =\n useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n\n const load = useCallback(async (): Promise<LastStartedTurnPolicy | null> => {\n if (!sessionId) return null;\n const turns = await client.listTurns(workspaceId, sessionId, { latestStarted: true });\n return policyFromTurn(turns[0]);\n }, [client, workspaceId, sessionId]);\n\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled,\n });\n\n useEffect(() => {\n if (enabled && workspaceControlEvent) void refresh();\n }, [enabled, refresh, workspaceControlEvent]);\n\n useEffect(() => {\n if (!sessionId || !enabled) return;\n return registerSessionReconciler(sessionId, \"last-started-turn-policy\", refresh);\n }, [enabled, refresh, registerSessionReconciler, sessionId]);\n\n // Shared-feed only: the session header lives outside the route event log and\n // must not open a second SSE. Without `events`, rely on the initial fetch +\n // optional poll / workspace-control / reconciler.\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isLastStartedTurnPolicyEvent,\n () => void refresh(),\n {\n events: options.events,\n enabled: enabled && options.events !== undefined,\n },\n );\n\n return {\n policy: enabled ? data : null,\n loading,\n error,\n refresh,\n };\n}\n","import type { ScheduledTask } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseScheduledTasksOptions = ClientOverride & {\n limit?: number | undefined;\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseScheduledTasksResult = {\n tasks: ScheduledTask[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** List the workspace's scheduled tasks (drift checks, sentinels, reapers, ...). */\nexport function useScheduledTasks(options: UseScheduledTasksOptions = {}): UseScheduledTasksResult {\n const { client, workspaceId } = useOpenGeni(options);\n const limit = options.limit;\n const load = useCallback(\n async () => await client.listScheduledTasks(workspaceId, limit !== undefined ? { limit } : {}),\n [client, workspaceId, limit],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n tasks: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type { Session } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseWorkspaceSessionsOptions = ClientOverride & {\n limit?: number | undefined;\n parentSessionId?: string | null | undefined;\n cursor?: string | undefined;\n search?: string | undefined;\n /** Return only the complete personal pinned projection. */\n pinsOnly?: boolean | undefined;\n /** Refresh interval (ms) for fleet/manager views. Off by default. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceSessionsResult = {\n /**\n * All visible rows, with pins first. This preserves the pre-pinning hook\n * contract for consumers that only read `sessions`.\n */\n sessions: Session[];\n /** The complete personal pinned section, also present in `sessions`. */\n pinned: Session[];\n /** True when the server omitted older pins from its bounded pinned section. */\n pinnedTruncated: boolean;\n nextCursor: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** List the workspace's sessions — the data behind fleet and manager views. */\nexport function useWorkspaceSessions(\n options: UseWorkspaceSessionsOptions = {},\n): UseWorkspaceSessionsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const limit = options.limit;\n const parentSessionId = options.parentSessionId;\n const cursor = options.cursor;\n const search = options.search;\n const pinsOnly = options.pinsOnly;\n const enabled = options.enabled ?? true;\n const queryKey = [\n workspaceId,\n limit ?? \"\",\n parentSessionId === null ? \"null\" : (parentSessionId ?? \"\"),\n cursor ?? \"\",\n search ?? \"\",\n pinsOnly ? \"1\" : \"\",\n ].join(\"\\u0000\");\n const previousQueryKey = useRef(queryKey);\n const queryKeyTransition = previousQueryKey.current !== queryKey;\n useEffect(() => {\n previousQueryKey.current = queryKey;\n }, [queryKey]);\n const load = useCallback(\n async () => ({\n queryKey,\n page: await client.listSessionPage(workspaceId, {\n ...(limit !== undefined ? { limit } : {}),\n ...(parentSessionId !== undefined ? { parentSessionId } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n ...(search !== undefined ? { search } : {}),\n ...(pinsOnly ? { pinsOnly: true } : {}),\n }),\n }),\n [client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, queryKey],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled,\n });\n // usePolledValue drops stale async completions, while the explicit query key\n // also prevents the previous query's cached value from painting for the one\n // render before its loader-change effect clears state.\n const page = state.data?.queryKey === queryKey ? state.data.page : null;\n const pinned = page?.pinned ?? [];\n const ordinary = page?.sessions ?? [];\n return {\n // The old hook returned every visible session. Keep that public behavior\n // while exposing `pinned` separately for the compact section. The API page\n // itself continues to paginate only `ordinary` rows via nextCursor.\n sessions: [...pinned, ...ordinary],\n pinned,\n pinnedTruncated: page?.pinnedTruncated ?? false,\n nextCursor: page?.nextCursor ?? null,\n // `usePolledValue` clears the old data and starts the new request in an\n // effect. During that query-key transition render, its old loading flag\n // can still be false; expose loading immediately so consumers do not\n // announce a transient false zero-match result.\n loading:\n enabled &&\n (state.loading ||\n queryKeyTransition ||\n (state.data !== null && state.data.queryKey !== queryKey)),\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type {\n CreateVariableSetRequest,\n UpdateVariableSetRequest,\n VariableSet,\n VariableSetVariableMetadata,\n} from \"@opengeni/sdk\";\nimport { useCallback, useMemo } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseVariableSetsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseVariableSetsResult = {\n variableSets: VariableSet[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateVariableSetRequest) => Promise<VariableSet | null>;\n update: (variableSetId: string, request: UpdateVariableSetRequest) => Promise<VariableSet | null>;\n remove: (variableSetId: string) => Promise<boolean>;\n /** Set/rotate a variable. Values are write-only — reads expose metadata only. */\n setVariable: (\n variableSetId: string,\n name: string,\n value: string,\n ) => Promise<VariableSetVariableMetadata | null>;\n deleteVariable: (variableSetId: string, name: string) => Promise<boolean>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * Variable sets (named, encrypted variable sets attached to sessions\n * and scheduled tasks). Variable values are write-only end to end: this hook\n * never sees a value after it is sent.\n */\nexport function useVariableSets(options: UseVariableSetsOptions = {}): UseVariableSetsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listVariableSets(workspaceId),\n [client, workspaceId],\n );\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateVariableSetRequest): Promise<VariableSet | null> => {\n const result = await run(() => client.createVariableSet(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const update = useCallback(\n async (\n variableSetId: string,\n request: UpdateVariableSetRequest,\n ): Promise<VariableSet | null> => {\n const result = await run(() => client.updateVariableSet(workspaceId, variableSetId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (variableSetId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteVariableSet(workspaceId, variableSetId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const setVariable = useCallback(\n async (\n variableSetId: string,\n name: string,\n value: string,\n ): Promise<VariableSetVariableMetadata | null> => {\n const result = await run(() =>\n client.setVariableSetVariable(workspaceId, variableSetId, name, value),\n );\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const deleteVariable = useCallback(\n async (variableSetId: string, name: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteVariableSetVariable(workspaceId, variableSetId, name);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n return {\n variableSets: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n remove,\n setVariable,\n deleteVariable,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\n/** @deprecated use UseVariableSetsOptions */\nexport type UseEnvironmentsOptions = UseVariableSetsOptions;\n/** @deprecated use UseVariableSetsResult */\nexport type UseEnvironmentsResult = UseVariableSetsResult & { environments: VariableSet[] };\n/** @deprecated use useVariableSets */\nexport function useEnvironments(options: UseEnvironmentsOptions = {}): UseEnvironmentsResult {\n const legacyClient = useMemo(() => {\n if (!options.client) {\n return undefined;\n }\n return {\n ...options.client,\n listVariableSets: options.client.listEnvironments ?? options.client.listVariableSets,\n createVariableSet: options.client.createEnvironment ?? options.client.createVariableSet,\n updateVariableSet: options.client.updateEnvironment ?? options.client.updateVariableSet,\n deleteVariableSet: options.client.deleteEnvironment ?? options.client.deleteVariableSet,\n setVariableSetVariable:\n options.client.setEnvironmentVariable ?? options.client.setVariableSetVariable,\n deleteVariableSetVariable:\n options.client.deleteEnvironmentVariable ?? options.client.deleteVariableSetVariable,\n };\n }, [options.client]);\n const result = useVariableSets({ ...options, ...(legacyClient ? { client: legacyClient } : {}) });\n return { ...result, environments: result.variableSets };\n}\n","import type {\n CreateRigRequest,\n ProposeRigChangeRequest,\n Rig,\n RigChange,\n RigVersion,\n UpdateRigRequest,\n} from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseRigsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigsResult = {\n rigs: Rig[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateRigRequest) => Promise<Rig | null>;\n update: (rigId: string, request: UpdateRigRequest) => Promise<Rig | null>;\n remove: (rigId: string) => Promise<boolean>;\n listVersions: (rigId: string) => Promise<RigVersion[] | null>;\n activateVersion: (rigId: string, versionId: string) => Promise<RigVersion | null>;\n listChanges: (rigId: string) => Promise<RigChange[] | null>;\n proposeChange: (rigId: string, request: ProposeRigChangeRequest) => Promise<RigChange | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * Rigs (workspace-scoped, versioned sandbox machine definitions). The list\n * polls; version/change reads are on-demand (they are per-rig detail, not part\n * of the list surface).\n */\nexport function useRigs(options: UseRigsOptions = {}): UseRigsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(async () => await client.listRigs(workspaceId), [client, workspaceId]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.createRig(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const update = useCallback(\n async (rigId: string, request: UpdateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.updateRig(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (rigId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteRig(workspaceId, rigId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const listVersions = useCallback(\n async (rigId: string): Promise<RigVersion[] | null> => {\n return await run(() => client.listRigVersions(workspaceId, rigId));\n },\n [client, workspaceId, run],\n );\n\n const activateVersion = useCallback(\n async (rigId: string, versionId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const listChanges = useCallback(\n async (rigId: string): Promise<RigChange[] | null> => {\n return await run(() => client.listRigChanges(workspaceId, rigId));\n },\n [client, workspaceId, run],\n );\n\n const proposeChange = useCallback(\n async (rigId: string, request: ProposeRigChangeRequest): Promise<RigChange | null> => {\n const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n return {\n rigs: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n remove,\n listVersions,\n activateVersion,\n listChanges,\n proposeChange,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\nexport type UseRigOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigResult = {\n rig: Rig | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n update: (request: UpdateRigRequest) => Promise<Rig | null>;\n remove: () => Promise<boolean>;\n activateVersion: (versionId: string) => Promise<RigVersion | null>;\n proposeChange: (request: ProposeRigChangeRequest) => Promise<RigChange | null>;\n /** Re-run verification for a change (asynchronous — poll for the outcome). */\n verifyChange: (changeId: string) => Promise<RigChange | null>;\n /** Promote a verified definition_edit into a new active version (rigs:manage). */\n promoteChange: (changeId: string) => Promise<RigVersion | null>;\n /** Re-verify the active version's checks in a clean throwaway sandbox. */\n verify: () => Promise<{ ok: boolean; versionId: string } | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * A single rig (its active version + counts), polled so verification and\n * promotion state stay live. Owns the rig's write actions; the versions and\n * changes lists are separate polled reads (`useRigVersions`/`useRigChanges`).\n */\nexport function useRig(rigId: string, options: UseRigOptions = {}): UseRigResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.getRig(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const update = useCallback(\n async (request: UpdateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.updateRig(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const remove = useCallback(async (): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteRig(workspaceId, rigId);\n return true;\n });\n return result === true;\n }, [client, workspaceId, rigId, run]);\n\n const activateVersion = useCallback(\n async (versionId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const proposeChange = useCallback(\n async (request: ProposeRigChangeRequest): Promise<RigChange | null> => {\n const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const verifyChange = useCallback(\n async (changeId: string): Promise<RigChange | null> => {\n const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const promoteChange = useCallback(\n async (changeId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const verify = useCallback(async (): Promise<{ ok: boolean; versionId: string } | null> => {\n return await run(() => client.verifyRig(workspaceId, rigId));\n }, [client, workspaceId, rigId, run]);\n\n return {\n rig: data,\n loading,\n error,\n refresh,\n update,\n remove,\n activateVersion,\n proposeChange,\n verifyChange,\n promoteChange,\n verify,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\nexport type UseRigVersionsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigVersionsResult = {\n versions: RigVersion[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** A rig's append-only version history, newest-first (polled). */\nexport function useRigVersions(\n rigId: string,\n options: UseRigVersionsOptions = {},\n): UseRigVersionsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listRigVersions(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n versions: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n\nexport type UseRigChangesOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigChangesResult = {\n changes: RigChange[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * A rig's change queue (polled). The default poll cadence lets a change move\n * through verifying → merged/rejected without a manual refresh.\n */\nexport function useRigChanges(\n rigId: string,\n options: UseRigChangesOptions = {},\n): UseRigChangesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listRigChanges(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n changes: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type {\n CapabilityPack,\n EnablePackRequest,\n PackInstallation,\n RegisterCapabilityPackRequest,\n WorkspaceRegisteredPack,\n} from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nconst EMPTY_INSTALLATIONS: PackInstallation[] = [];\n\nexport type UsePacksOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UsePacksResult = {\n /** Built-in + registered packs available to the workspace. */\n packs: CapabilityPack[];\n /** Enable/disable state per pack. */\n installations: PackInstallation[];\n /** The installation for a pack id, if any. */\n installationFor: (packId: string) => PackInstallation | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n /** Register (or replace) a workspace-scoped pack manifest. */\n register: (manifest: RegisterCapabilityPackRequest) => Promise<WorkspaceRegisteredPack | null>;\n enable: (packId: string, request?: EnablePackRequest) => Promise<PackInstallation | null>;\n /** Unregister a workspace-scoped pack (built-ins cannot be removed). */\n remove: (packId: string) => Promise<boolean>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/** Capability packs: catalog + installations + register/enable/unregister. */\nexport function usePacks(options: UsePacksOptions = {}): UsePacksResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(async () => await client.listPacks(workspaceId), [client, workspaceId]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const register = useCallback(\n async (manifest: RegisterCapabilityPackRequest): Promise<WorkspaceRegisteredPack | null> => {\n const result = await run(() => client.registerPack(workspaceId, manifest));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const enable = useCallback(\n async (packId: string, request: EnablePackRequest = {}): Promise<PackInstallation | null> => {\n const result = await run(() => client.enablePack(workspaceId, packId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (packId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deletePack(workspaceId, packId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const installations = data?.installations ?? EMPTY_INSTALLATIONS;\n const installationFor = useCallback(\n (packId: string): PackInstallation | null =>\n installations.find((installation) => installation.packId === packId) ?? null,\n [installations],\n );\n\n return {\n packs: data?.packs ?? [],\n installations,\n installationFor,\n loading,\n error,\n refresh,\n register,\n enable,\n remove,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n","import type { CreateWorkspaceRequest, UpdateWorkspaceRequest, Workspace } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseWorkspacesOptions = Pick<ClientOverride, \"client\"> & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspacesResult = {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateWorkspaceRequest) => Promise<Workspace | null>;\n update: (workspaceId: string, request: UpdateWorkspaceRequest) => Promise<Workspace | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * The caller's workspaces (workspace switchers, onboarding). Not scoped to\n * the provider's workspace, so it only needs the client.\n */\nexport function useWorkspaces(options: UseWorkspacesOptions = {}): UseWorkspacesResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => await client.listWorkspaces(), [client]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateWorkspaceRequest): Promise<Workspace | null> => {\n const result = await run(() => client.createWorkspace(request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, run, refresh],\n );\n\n const update = useCallback(\n async (workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace | null> => {\n const result = await run(() => client.updateWorkspace(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, run, refresh],\n );\n\n return {\n workspaces: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n","import type { BillingBalance, UsageEvent } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseBillingUsageOptions = Pick<ClientOverride, \"client\"> & {\n /** Account to read. Defaults to the caller's default account server-side. */\n accountId?: string | undefined;\n /** Filter usage to one workspace. */\n workspaceId?: string | undefined;\n /** Refresh interval (ms) for live billing meters. Off by default. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseBillingUsageResult = {\n /** Prepaid credit balance (micro-USD), null until loaded. */\n balance: BillingBalance | null;\n /** Recent usage events (runs, tokens, cost, uploads, ...). */\n usage: UsageEvent[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * Account billing usage: credit balance + recent usage events — the data\n * behind per-call billing meters. Account-scoped, so it only needs the\n * client; pass `workspaceId` to narrow usage to one workspace.\n */\nexport function useBillingUsage(options: UseBillingUsageOptions = {}): UseBillingUsageResult {\n const client = useOpenGeniClient(options);\n const accountId = options.accountId;\n const workspaceId = options.workspaceId;\n const load = useCallback(\n async () =>\n await client.getBillingUsage({\n ...(accountId !== undefined ? { accountId } : {}),\n ...(workspaceId !== undefined ? { workspaceId } : {}),\n }),\n [client, accountId, workspaceId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n balance: state.data?.balance ?? null,\n usage: state.data?.usage ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type { WorkspaceModelCatalogModel } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { projectPickerRows, sortPickerRows, type PickerModelRow } from \"../model-policy\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseAvailableModelsOptions = Pick<ClientOverride, \"client\"> & {\n /** Refresh interval (ms). Off by default — the host model list rarely moves. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseAvailableModelsResult = {\n models: import(\"@opengeni/sdk\").ClientModel[];\n defaultModel: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nexport type UseWorkspaceModelCatalogOptions = Pick<ClientOverride, \"client\"> & {\n workspaceId: string | null;\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceModelCatalogResult = {\n models: WorkspaceModelCatalogModel[];\n rows: PickerModelRow[];\n defaultModel: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * The host-exposed model list for a <ModelPicker>: fetches the deployment's\n * public client config (`GET /v1/config/client`) and surfaces the richer\n * provider-grouped `models` plus the `defaultModel` the picker should preselect.\n * Deployment-scoped, so it only needs the client (no workspace).\n */\nexport function useAvailableModels(\n options: UseAvailableModelsOptions = {},\n): UseAvailableModelsResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => await client.getClientConfig(), [client]);\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n models: state.data?.models ?? [],\n defaultModel: state.data?.defaultModel ?? null,\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n\n/** Workspace-scoped catalog with selectability and billing-class picker rows. */\nexport function useWorkspaceModelCatalog(\n options: UseWorkspaceModelCatalogOptions,\n): UseWorkspaceModelCatalogResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => {\n if (!options.workspaceId) {\n return { models: [], defaultModel: null };\n }\n const [catalog, config] = await Promise.all([\n client.getWorkspaceModelCatalog(options.workspaceId),\n client.getClientConfig(),\n ]);\n return {\n models: catalog.models,\n defaultModel: config.defaultModel,\n };\n }, [client, options.workspaceId]);\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled !== false && Boolean(options.workspaceId),\n });\n const rows = sortPickerRows(projectPickerRows(state.data?.models ?? []));\n return {\n models: state.data?.models ?? [],\n rows,\n defaultModel: state.data?.defaultModel ?? null,\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import {\n OpenGeniApiError,\n applyUrlRotation,\n type SessionCapabilities,\n type SessionEvent,\n type StreamUrlRotatedPayload,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n// \"on-demand\" is the boxless-benign resting state: the lease is cold and NOTHING\n// asked to warm a box (no viewer/consent action, no files-tab warm). Under lazy\n// provisioning a chat-only turn never creates a box, so this is the correct calm\n// terminal state — NOT an error. It is distinct from \"cold\", which now means \"a\n// warm-up is genuinely in flight\" (a viewer attach was requested) and therefore\n// still polls with a patience deadline. A box appears on demand when the agent\n// first runs a sandbox tool; the workbench observes `sandbox.provision` and\n// renegotiates to pick up the freshly-warm box.\nexport type SessionCapabilitiesState =\n | \"idle\"\n | \"negotiating\"\n | \"ready\"\n | \"cold\"\n | \"on-demand\"\n | \"error\";\n\nexport type UseSessionCapabilitiesOptions = ClientOverride & {\n /**\n * Live event log to fold `stream.url.rotated` from (usually\n * `useSessionEvents().events`). When present the desktop socket stays fresh on\n * a box rollover without a round-trip; stale-epoch rotations are dropped.\n */\n events?: SessionEvent[] | undefined;\n /**\n * Whether to acquire a viewer holder for the desktop pixel plane. Requires the\n * un-redacted acknowledgment to have been recorded (else the attach 409s and\n * the hook surfaces the consent requirement). Default false: read-only\n * negotiation (no holder, no warm) — terminal/files/git work without it.\n */\n attachDesktop?: boolean | undefined;\n /**\n * Whether to acquire a viewer holder to warm the box for the REAL interactive\n * terminal (the ttyd pty-ws plane). Symmetric with `attachDesktop` and shares\n * the SAME viewer attach (one warm box serves both planes), but needs NO\n * un-redacted acknowledgment — a shell is interactive by nature, and the gate\n * is the scoped tunnel URL + stream token. Default false: the terminal stays on\n * the read-only Channel-A firehose until the user opens/focuses it. The attach\n * folds the minted `pty-ws` url+token into the `Terminal` cell.\n */\n attachTerminal?: boolean | undefined;\n /**\n * Whether to acquire a viewer holder to warm the box for a FILE WRITE — the\n * wake-on-edit INTENT (a first keystroke in the editor), NOT passive browsing.\n * Shares the SAME viewer attach as the desktop/terminal (one warm box, one\n * holder) and — like the terminal — needs NO un-redacted acknowledgment:\n * reading/writing files is the ordinary Channel-A control plane, not the pixel\n * plane. It folds NO live URL (files ride the stateless HTTP plane) — it only\n * refcounts liveness. Unlike desktop/terminal it warms even a COLD box: an edit\n * legitimately cold-creates (that IS the wake), so the write lands ~100ms warm\n * instead of paying the ~5s cold resume. Default false. IMPORTANT: merely\n * opening/reading the Files tab must NOT set this — browsing capture-served\n * trees/diffs needs no box, and warming one on a cold glance burns box-hours to\n * serve reads the capture already answers for free.\n */\n attachFiles?: boolean | undefined;\n /** Hold off negotiating (e.g. the workbench panel is collapsed). Default true. */\n enabled?: boolean | undefined;\n /** Poll cadence (ms) while the lease is cold/warming. Default 1500. */\n warmingPollMs?: number | undefined;\n /**\n * Give up waiting for `warm` after this long while polling (ms) and surface a\n * stalled error with a manual `renegotiate`. Default 30000. This is a UI\n * patience deadline, independent from the worker's sandbox warming timeout.\n * 0 disables the deadline.\n */\n warmingDeadlineMs?: number | undefined;\n};\n\nexport type UseSessionCapabilitiesResult = {\n /** The negotiated capability doc — the single source of UI truth. */\n capabilities: SessionCapabilities | null;\n state: SessionCapabilitiesState;\n error: Error | null;\n /**\n * 409 from the desktop attach: the un-redacted (or shared) plane needs explicit\n * acknowledgment before a viewer holder is granted. Drives the consent prompt.\n */\n acknowledgmentRequired: \"unredacted\" | \"shared\" | null;\n /** 429 from the desktop attach: the per-session viewer cap is reached. */\n viewerCapReached: boolean;\n /** The viewer holder id minted on a desktop attach (for detach/heartbeat). */\n viewerId: string | null;\n /** Force a re-negotiation (after acknowledging, a resolution change, etc.). */\n renegotiate: () => void;\n};\n\n/**\n * Whether a desktop attach (POST /viewers) is worth attempting for this cell.\n * The desktop is FEASIBLE — and thus worth warming — when it already has a live\n * transport, OR when the only thing missing is a warm box (a cold/un-provisioned\n * lease). The handshake never warms a box, so a feasible-but-cold cell reports\n * transport:null with a transient reason; the viewer attach is exactly what warms\n * it. A genuinely-unsupported desktop (backend/os/policy/headless) is never\n * attachable — attaching would 409/403/no-op without ever producing a stream.\n */\nfunction desktopAttachable(cell: SessionCapabilities[\"DesktopStream\"]): boolean {\n if (cell.transport !== null) return true;\n // transport === null: attach only when the reason is a transient cold state.\n return cell.reason === \"lease_cold\" || cell.reason === \"not_provisioned\" || cell.reason === null;\n}\n\n/**\n * Whether warming the box for the interactive terminal (pty-ws) is worth it.\n * The Terminal cell is ALWAYS feasible when the backend advertises a transport at\n * all (`sse-events` on a cold box, `pty-ws` once warm) — only a genuinely\n * terminal-less backend reports transport:null with a hard reason. The attach is\n * what flips `sse-events` → `pty-ws` by warming the box and minting the ttyd\n * tunnel URL, so we attach whenever the terminal is not hard-unavailable.\n */\nfunction terminalAttachable(cell: SessionCapabilities[\"Terminal\"]): boolean {\n if (cell.transport === null) {\n // No terminal at all unless the only blocker is a transient cold state.\n return (\n cell.reason === \"lease_cold\" || cell.reason === \"not_provisioned\" || cell.reason === null\n );\n }\n // Already pty-ws (warm) is fine to (re)attach; sse-events is the cold state the\n // attach upgrades. Either way it's attachable.\n return true;\n}\n\n/** Read `stream.url.rotated` payloads off the live event log, newest last. */\nfunction rotationsFrom(events: SessionEvent[]): StreamUrlRotatedPayload[] {\n const out: StreamUrlRotatedPayload[] = [];\n for (const event of events) {\n if (event.type === \"stream.url.rotated\" && event.payload && typeof event.payload === \"object\") {\n out.push(event.payload as StreamUrlRotatedPayload);\n }\n }\n return out;\n}\n\n/**\n * The capability-negotiation hook. Discovers what THIS session+backend+OS\n * supports (FileSystem/Terminal/Git always-ish; DesktopStream/Recording\n * sometimes), drives capability-gated rendering, and — when `attachDesktop` —\n * holds a viewer lease + heartbeats it so the box stays warm while watched.\n *\n * Degradation is a value, never a crash: an unsupported surface comes back\n * `available:false`/`transport:null` + a `reason`; the components render the\n * reason-aware empty state. 409 (consent) and 429 (viewer cap) are surfaced as\n * typed signals, not thrown.\n */\nexport function useSessionCapabilities(\n sessionId: string | null | undefined,\n options: UseSessionCapabilitiesOptions = {},\n): UseSessionCapabilitiesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const attachDesktop = options.attachDesktop ?? false;\n const attachTerminal = options.attachTerminal ?? false;\n const attachFiles = options.attachFiles ?? false;\n const warmingPollMs = options.warmingPollMs ?? 1500;\n const warmingDeadlineMs = options.warmingDeadlineMs ?? 30_000;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${enabled}`;\n\n const [capabilities, setCapabilities] = useState<SessionCapabilities | null>(null);\n const [state, setState] = useState<SessionCapabilitiesState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [acknowledgmentRequired, setAcknowledgmentRequired] = useState<\n \"unredacted\" | \"shared\" | null\n >(null);\n const [viewerCapReached, setViewerCapReached] = useState(false);\n const [viewerId, setViewerId] = useState<string | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Bumped to force a fresh negotiation cycle.\n const [nonce, setNonce] = useState(0);\n\n // The epoch the client has settled on — used to fence stale rotations folded\n // from the event log, and echoed on heartbeats.\n const epochRef = useRef(0);\n const viewerIdRef = useRef<string | null>(null);\n const previousIdentityRef = useRef(identityKey);\n\n const renegotiate = useCallback(() => {\n setNonce((n) => n + 1);\n }, []);\n\n // ── Negotiation + viewer lifecycle ──────────────────────────────────────────\n useEffect(() => {\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged) {\n setStateIdentity(identityKey);\n setCapabilities(null);\n setState(\"idle\");\n setError(null);\n setAcknowledgmentRequired(null);\n setViewerCapReached(false);\n setViewerId(null);\n epochRef.current = 0;\n viewerIdRef.current = null;\n }\n if (!enabled || !sessionId) {\n setState(\"idle\");\n setCapabilities(null);\n return;\n }\n let cancelled = false;\n let pollTimer: ReturnType<typeof setTimeout> | null = null;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n let localViewerId: string | null = null;\n const requestAbort = new AbortController();\n const startedAt = Date.now();\n\n const clearTimers = () => {\n if (pollTimer !== null) clearTimeout(pollTimer);\n if (heartbeatTimer !== null) clearInterval(heartbeatTimer);\n pollTimer = null;\n heartbeatTimer = null;\n };\n\n const settle = (caps: SessionCapabilities) => {\n if (cancelled) return;\n epochRef.current = caps.leaseEpoch;\n setCapabilities(caps);\n setError(null);\n };\n\n const startHeartbeat = (caps: SessionCapabilities) => {\n if (!localViewerId || heartbeatTimer !== null) return;\n const intervalMs =\n caps.viewerHeartbeatIntervalMs > 0 ? caps.viewerHeartbeatIntervalMs : 30_000;\n heartbeatTimer = setInterval(() => {\n if (cancelled || !localViewerId) return;\n void client\n .heartbeatViewer(workspaceId, sessionId, localViewerId, { leaseEpoch: epochRef.current })\n .then((res) => {\n // alive:false ⇒ the holder was reaped or the epoch moved under us;\n // re-negotiate to re-acquire against the new owner.\n if (!res.alive && !cancelled) {\n renegotiate();\n }\n })\n .catch((cause) => {\n if (cancelled) return;\n if (\n cause instanceof OpenGeniApiError &&\n (cause.status === 409 || cause.status === 410)\n ) {\n renegotiate();\n }\n });\n }, intervalMs);\n };\n\n const pollUntilWarm = () => {\n pollTimer = setTimeout(() => {\n if (cancelled) return;\n void client\n .getStreamCapabilities(workspaceId, sessionId, { signal: requestAbort.signal })\n .then((caps) => {\n if (cancelled) return;\n settle(caps);\n if (caps.liveness === \"warm\" || caps.liveness === \"draining\") {\n setState(\"ready\");\n return;\n }\n if (warmingDeadlineMs > 0 && Date.now() - startedAt > warmingDeadlineMs) {\n setState(\"error\");\n setError(new Error(\"sandbox did not warm in time — retry to re-negotiate\"));\n return;\n }\n pollUntilWarm();\n })\n .catch((cause) => {\n if (!cancelled) {\n setState(\"error\");\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n });\n }, warmingPollMs);\n };\n\n void (async () => {\n setState(\"negotiating\");\n setAcknowledgmentRequired(null);\n setViewerCapReached(false);\n try {\n const caps = await client.getStreamCapabilities(workspaceId, sessionId, {\n signal: requestAbort.signal,\n });\n if (cancelled) return;\n settle(caps);\n\n // Optional viewer attach: acquire a viewer holder (warms a cold box). ONE\n // attach serves BOTH live planes — the desktop pixel stream AND the\n // interactive terminal (ttyd pty-ws) ride the same warm box and the same\n // holder; the response folds the minted address for each requested plane.\n // The consent gate (409) and the viewer cap (429) surface as typed signals\n // rather than throwing — the tabs degrade gracefully.\n //\n // KEY: the handshake (getStreamCapabilities) NEVER spins up a cold box, so\n // on a cold/warming lease the desktop cell comes back transport:null and\n // the terminal cell comes back `sse-events` (read-only firehose) with a\n // transient reason (`lease_cold`/`not_provisioned`). Gating the attach on\n // `transport` therefore dead-ends both surfaces (forever \"Starting…\"): they\n // never warm because they never attach, and never attach because they\n // aren't warm. We attach whenever a REQUESTED plane is FEASIBLE — transport\n // already live OR cold-but-feasible — and let POST /viewers warm the box and\n // mint the URLs. Only a genuinely-unsupported reason suppresses the attach.\n const wantDesktopAttach = attachDesktop && desktopAttachable(caps.DesktopStream);\n const wantTerminalAttach = attachTerminal && terminalAttachable(caps.Terminal);\n // The Files attach warms the box for a file WRITE (wake-on-edit intent). It\n // folds no live URL (files are stateless HTTP) — the attach is purely a\n // liveness refcount. Unlike the previous \"keep the box warm while the Files\n // tab is on screen\" wiring, this fires on genuine EDIT intent only, so it\n // legitimately cold-CREATES: an edit is the wake, and the caller (the dock)\n // never sets it for passive browsing. Gate solely on the FileSystem cap\n // being advertised; liveness (cold included) does not suppress it — POST\n // /viewers warms a cold box exactly as it does for a desktop/terminal engage.\n const wantFilesAttach = attachFiles && caps.FileSystem.available;\n if (wantDesktopAttach || wantTerminalAttach || wantFilesAttach) {\n try {\n // Declare WHICH plane we're attaching for. `desktop:true` opts into the\n // un-redacted pixel plane (which carries the consent gate); a\n // terminal-only attach (`desktop:false`) warms the box + mints the\n // pty-ws terminal cell WITHOUT tripping the desktop consent 409.\n const holder = await client.attachViewer(workspaceId, sessionId, {\n desktop: wantDesktopAttach,\n });\n if (cancelled) {\n // The attach crossed an identity/unmount boundary after the server\n // had already minted a holder. It is too late to publish the result,\n // but abandoning it would keep the old sandbox warm until the\n // reaper runs. Release that exact old-session holder immediately.\n void client.detachViewer(workspaceId, sessionId, holder.viewerId).catch(() => {});\n return;\n }\n localViewerId = holder.viewerId;\n viewerIdRef.current = holder.viewerId;\n setViewerId(holder.viewerId);\n epochRef.current = holder.leaseEpoch;\n // Fold the freshly-minted live address(es) into the doc the components\n // read. Desktop fields fold only when a desktop attach was wanted;\n // terminal fields fold the minted ttyd pty-ws url+token when present.\n setCapabilities((prev) =>\n prev\n ? {\n ...prev,\n liveness: holder.liveness,\n leaseEpoch: holder.leaseEpoch,\n viewerHeartbeatIntervalMs: holder.viewerHeartbeatIntervalMs,\n DesktopStream: wantDesktopAttach\n ? {\n ...prev.DesktopStream,\n transport: holder.transport ?? prev.DesktopStream.transport,\n client: holder.client ?? prev.DesktopStream.client,\n url: holder.dataPlaneUrl ?? prev.DesktopStream.url,\n token: holder.streamToken ?? prev.DesktopStream.token,\n expiresAt: holder.streamExpiresAt ?? prev.DesktopStream.expiresAt,\n resolution: holder.resolution ?? prev.DesktopStream.resolution,\n }\n : prev.DesktopStream,\n Terminal:\n holder.terminalUrl && holder.terminalTransport\n ? {\n ...prev.Terminal,\n transport: holder.terminalTransport,\n url: holder.terminalUrl,\n token: holder.terminalToken ?? prev.Terminal.token,\n // A live pty-ws means the box is pty-capable for real.\n ptyCapable: true,\n reason: null,\n }\n : prev.Terminal,\n }\n : prev,\n );\n } catch (cause) {\n if (cancelled) return;\n if (cause instanceof OpenGeniApiError) {\n if (cause.status === 409) {\n // The un-redacted/shared consent gate is a DESKTOP requirement. A\n // terminal-only warm attach needs no consent, so a 409 there is not\n // a consent prompt — the terminal just stays on the read-only\n // firehose. Only raise the consent requirement when the desktop was\n // the (or a) reason we attached.\n if (wantDesktopAttach) {\n setAcknowledgmentRequired(\n cause.message.includes(\"shared_acknowledgment\") ? \"shared\" : \"unredacted\",\n );\n }\n } else if (cause.status === 429) {\n setViewerCapReached(true);\n } else if (cause.status === 403) {\n setState(\"error\");\n setError(cause);\n return;\n }\n // 409/429 are recoverable: structured surfaces still negotiated;\n // keep going to set ready/cold below.\n } else {\n throw cause;\n }\n }\n }\n\n // Whether ANYTHING asked to warm a box this negotiation. Only then is a\n // cold lease \"warming in flight\" — worth polling, and worth surfacing a\n // stall as an error. With no warm-up requested a cold lease is the benign\n // boxless resting state (below), never an error.\n const warmUpRequested = wantDesktopAttach || wantTerminalAttach || wantFilesAttach;\n if (caps.liveness === \"warm\" || caps.liveness === \"draining\" || localViewerId) {\n setState(\"ready\");\n startHeartbeat(caps);\n } else if (warmUpRequested) {\n // A warm-up is genuinely in flight (a viewer attach was requested) but the\n // box isn't warm yet — poll until it warms, with the patience deadline so a\n // real stall surfaces as an error.\n setState(\"cold\");\n pollUntilWarm();\n } else {\n // Boxless and nobody asked for a box: rest in the benign on-demand state.\n // NO poll, NO deadline, NO false \"sandbox offline\". The box is created on\n // demand when the agent first runs a sandbox tool; the workbench watches\n // for `sandbox.provision` and renegotiates to pick the warm box back up.\n setState(\"on-demand\");\n }\n } catch (cause) {\n if (cancelled) return;\n if (cause instanceof OpenGeniApiError && cause.status === 403) {\n setState(\"error\");\n setError(new Error(\"not permitted to view this session's sandbox\"));\n return;\n }\n setState(\"error\");\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n })();\n\n return () => {\n cancelled = true;\n requestAbort.abort();\n clearTimers();\n // Fire-and-forget detach (idempotent delete-my-row). Capture the id so a\n // re-render/unmount race still releases the right holder.\n const releaseId = localViewerId ?? viewerIdRef.current;\n if (releaseId) {\n void client.detachViewer(workspaceId, sessionId, releaseId).catch(() => {});\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n client,\n workspaceId,\n sessionId,\n enabled,\n attachDesktop,\n attachTerminal,\n attachFiles,\n warmingPollMs,\n warmingDeadlineMs,\n nonce,\n identityKey,\n ]);\n\n // ── Fold stream.url.rotated from the live event log (no round trip) ──────────\n const events = options.events;\n useEffect(() => {\n if (!events || events.length === 0) return;\n setCapabilities((prev) => {\n if (!prev || !prev.DesktopStream.url) return prev;\n let next = prev.DesktopStream;\n let changed = false;\n for (const rotation of rotationsFrom(events)) {\n // Only this viewer's rotations (others' are filtered by viewerId when set).\n if (rotation.viewerId && viewerIdRef.current && rotation.viewerId !== viewerIdRef.current) {\n continue;\n }\n const applied = applyUrlRotation(next, rotation, epochRef.current);\n if (applied) {\n next = { ...next, url: applied.url, token: applied.token, expiresAt: applied.expiresAt };\n epochRef.current = Math.max(epochRef.current, rotation.leaseEpoch);\n changed = true;\n }\n }\n return changed ? { ...prev, DesktopStream: next, leaseEpoch: epochRef.current } : prev;\n });\n }, [events]);\n\n const identityMatches = enabled && stateIdentity === identityKey;\n return {\n capabilities: identityMatches ? capabilities : null,\n state: identityMatches ? state : \"idle\",\n error: identityMatches ? error : null,\n acknowledgmentRequired: identityMatches ? acknowledgmentRequired : null,\n viewerCapReached: identityMatches && viewerCapReached,\n viewerId: identityMatches ? viewerId : null,\n renegotiate,\n };\n}\n","import {\n desktopSocketUrl,\n nextDesktopState,\n type DesktopConnectionState,\n type DesktopRfbFactory,\n type DesktopRfbLike,\n type DesktopStreamCapability,\n} from \"@opengeni/sdk\";\nimport { type RefObject, useEffect, useRef, useState } from \"react\";\nimport { type DesktopWebSocketFactory, useRelayFrameStream } from \"./use-relay-frame-stream\";\n\nexport type UseDesktopStreamOptions = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /** The mount target. RFB (or the frame canvas) attaches here on connect. */\n containerRef: RefObject<HTMLDivElement | null>;\n /** Read-only by default (v1 ruling H). interactive only when cap.mode allows. */\n interactive?: boolean | undefined;\n scaleViewport?: boolean | undefined;\n /** Custom RFB factory (tests / a WebRTC swap). Defaults to a lazy @novnc/novnc. */\n rfbFactory?: DesktopRfbFactory | undefined;\n /** Custom socket factory for the `relay-frames` transport (tests). Defaults to\n * `new WebSocket(url)`. Mirrors `rfbFactory` for the frame renderer. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n};\n\nexport type UseDesktopStreamResult = {\n state: DesktopConnectionState;\n error: Error | null;\n /** Manual reconnect (e.g. after a securityfailure once a fresh URL arrives). */\n reconnect: () => void;\n};\n\n/** Lazy-load @novnc/novnc's RFB as the default factory. Imported inside the\n * connect effect so SSR / non-desktop bundles never pull the DOM-only lib.\n * @novnc/novnc ships no types and a single `export default class RFB`. The\n * specifier is STATIC (`import(\"@novnc/novnc\")`) so Vite can pre-bundle and\n * resolve it — a runtime-string indirection with `@vite-ignore` (the previous\n * approach) hands the browser a bare specifier and throws\n * \"Failed to resolve module specifier '@novnc/novnc'\". The dynamic form keeps\n * it out of the SSR / non-desktop critical path while staying resolvable. */\nasync function defaultRfbFactory(): Promise<DesktopRfbFactory> {\n const mod = (await import(\"@novnc/novnc\")) as unknown as {\n default: new (\n t: HTMLElement,\n u: string,\n o: { credentials?: { password?: string | undefined } | undefined },\n ) => DesktopRfbLike;\n };\n const RFB = mod.default;\n return (target, url, opts) => new RFB(target, url, opts);\n}\n\n/**\n * Drive the noVNC RFB lifecycle from a `DesktopStreamCapability`, using the\n * SDK's `desktop.ts` reducer + `desktopSocketUrl`. SSR-safe: the RFB import and\n * the DOM attach happen inside `useEffect`, so a server render is a no-op and\n * the component shows its placeholder until hydration.\n *\n * Read-only is enforced at three layers: `capability.mode` (server) →\n * `interactive` prop → `RFB.viewOnly`. v1 always resolves to read-only. On a\n * capability `url` change (a rotation), the old RFB disconnects and a fresh one\n * connects to the new URL — a brief \"desktop blink\", acceptable on rollover.\n *\n * Transport dispatch: a Modal box negotiates `transport: \"vnc-ws\"` and drives the\n * noVNC RFB below. A SELF-HOSTED machine negotiates `transport: \"relay-frames\"`\n * (PNG-per-frame over the relay, view-only) — that path is delegated to\n * `useRelayFrameStream`, which paints a `<canvas>`. Both hooks are ALWAYS called\n * (rules of hooks); each is dormant for the other's transport, and we return the\n * result of whichever owns the surface. The public interface is identical either\n * way, so `DesktopViewer` is transport-agnostic.\n */\nexport function useDesktopStream(options: UseDesktopStreamOptions): UseDesktopStreamResult {\n const { capability, containerRef, interactive, scaleViewport, rfbFactory, webSocketFactory } =\n options;\n\n // The self-hosted PNG-frame path. Always invoked (rules of hooks); dormant\n // unless `transport === \"relay-frames\"`, in which case it owns the surface.\n const frameStream = useRelayFrameStream({\n capability,\n containerRef,\n ...(webSocketFactory ? { webSocketFactory } : {}),\n });\n const [state, setState] = useState<DesktopConnectionState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [nonce, setNonce] = useState(0);\n const stateRef = useRef<DesktopConnectionState>(\"idle\");\n const setBoth = (next: DesktopConnectionState) => {\n stateRef.current = next;\n setState(next);\n };\n\n const reconnect = () => setNonce((n) => n + 1);\n\n const url = capability?.url ?? null;\n // The credential is keyed into the connect effect so a token rotation (new\n // password) reconnects, but NOT folded into the whole-`capability` identity —\n // a benign re-negotiation that re-mints the same cell must not churn the socket.\n const token = capability?.token ?? null;\n const transport = capability?.transport ?? null;\n const mode = capability?.mode ?? \"read-only\";\n\n // The live RFB handle. Holding it in a ref lets us flip view-only (take\n // control / return control) and re-scale on the OPEN connection instead of\n // tearing the socket down — the old behaviour reconnected on every\n // `interactive` change, which read as a constant \"refresh\" and (combined with\n // the canvas churn) bounced control back to watch.\n const rfbRef = useRef<DesktopRfbLike | null>(null);\n // Latest input/scale/factory the connect effect reads via refs so they are NOT\n // connect-effect dependencies: changing them must update the live RFB in place,\n // never reconnect it.\n const interactiveRef = useRef(interactive);\n const scaleViewportRef = useRef(scaleViewport);\n const modeRef = useRef(mode);\n const rfbFactoryRef = useRef(rfbFactory);\n interactiveRef.current = interactive;\n scaleViewportRef.current = scaleViewport;\n modeRef.current = mode;\n rfbFactoryRef.current = rfbFactory;\n\n // read-only is forced when the server says so OR the caller didn't opt in.\n const viewOnlyFor = (m: string, want: boolean | undefined) => m === \"read-only\" || !want;\n\n useEffect(() => {\n // SSR / no DOM / no usable transport: stay idle and show the placeholder.\n if (typeof window === \"undefined\") return;\n if (transport !== \"vnc-ws\" || !url) {\n setBoth(\"idle\");\n return;\n }\n const container = containerRef.current;\n if (!container) {\n // The mount target isn't in the DOM yet (e.g. the tab is still hidden).\n // Stay idle (not a stale negotiating/connecting) so a re-attach nudge —\n // fired by the viewer on becoming visible / on mount — can re-run this\n // effect once the container exists. Without this the surface can stick\n // forever on the idle scrim and never open a socket.\n setBoth(\"idle\");\n return;\n }\n\n let rfb: DesktopRfbLike | null = null;\n let disposed = false;\n setError(null);\n setBoth(\"negotiating\");\n\n const onConnect = () => {\n if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: \"connected\" }));\n };\n const onDisconnect = () => {\n if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: \"disconnected\" }));\n };\n const onSecurityFailure = () => {\n if (disposed) return;\n setError(new Error(\"desktop authentication failed (token expired or revoked)\"));\n setBoth(nextDesktopState(stateRef.current, { type: \"fail\" }));\n };\n\n void (async () => {\n try {\n const factory = rfbFactoryRef.current ?? (await defaultRfbFactory());\n if (disposed) return;\n const socketUrl = desktopSocketUrl({ url });\n setBoth(nextDesktopState(stateRef.current, { type: \"negotiated\" }));\n rfb = factory(container, socketUrl, {\n credentials: token ? { password: token } : undefined,\n });\n rfb.viewOnly = viewOnlyFor(modeRef.current, interactiveRef.current);\n // Fit-to-panel: SCALE the 1280x800 framebuffer down to the container\n // (aspect-preserved) and never 1:1-clip. `clipViewport=false` is pinned\n // explicitly — noVNC forces it off while scaling, but a fresh RFB on a\n // url rotation could otherwise start from a stale clip and read as\n // \"zoomed in\". Order matters: clip off, then scale on.\n rfb.clipViewport = false;\n rfb.scaleViewport = scaleViewportRef.current ?? true;\n rfb.addEventListener(\"connect\", onConnect);\n rfb.addEventListener(\"disconnect\", onDisconnect);\n rfb.addEventListener(\"securityfailure\", onSecurityFailure);\n rfbRef.current = rfb;\n } catch (cause) {\n if (!disposed) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n }\n }\n })();\n\n return () => {\n disposed = true;\n if (rfbRef.current === rfb) rfbRef.current = null;\n if (rfb) {\n rfb.removeEventListener?.(\"connect\", onConnect);\n rfb.removeEventListener?.(\"disconnect\", onDisconnect);\n rfb.removeEventListener?.(\"securityfailure\", onSecurityFailure);\n try {\n rfb.disconnect();\n } catch {\n // ignore teardown errors\n }\n }\n };\n // ONLY a real transport change reconnects: a fresh url (rotation), a new\n // credential, the transport flipping, or an explicit manual reconnect\n // (`nonce`). `interactive`/`scaleViewport`/`mode`/`rfbFactory` are read via\n // refs and applied live below — they must never re-open the socket.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, token, transport, nonce]);\n\n // Apply take-control / return-control to the OPEN connection in place. noVNC\n // honours `viewOnly` live, so flipping it neither blinks the surface nor drops\n // the framebuffer — the cure for the take-control \"refresh + auto-release\" loop.\n useEffect(() => {\n const rfb = rfbRef.current;\n if (rfb) rfb.viewOnly = viewOnlyFor(mode, interactive);\n // viewOnlyFor is pure; re-run only when the inputs change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [interactive, mode, state]);\n\n // Re-scale the live RFB in place (no reconnect) when the scale preference flips\n // OR the connection state advances (e.g. the first framebuffer paints after a\n // take-control flip / a re-attach). Re-asserting clip-off + scale-on here is the\n // belt-and-suspenders against a \"zoomed\" surface: it re-fits the canvas to the\n // panel on every meaningful transition, so a transient mis-measure at connect\n // time can never stick.\n useEffect(() => {\n const rfb = rfbRef.current;\n if (rfb) {\n rfb.clipViewport = false;\n rfb.scaleViewport = scaleViewport ?? true;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scaleViewport, state]);\n\n // Delegate to whichever transport owns the surface. For `relay-frames` the\n // noVNC effect above is dormant (it bailed to idle) and the frame renderer\n // drives; for `vnc-ws` (and everything else) the noVNC path drives while the\n // frame renderer stays idle. Same public shape either way.\n if (transport === \"relay-frames\") return frameStream;\n return { state, error, reconnect };\n}\n","// Minimal HAND-MIRROR of the relay stream wire messages the desktop frame client\n// needs (`StreamOpen` encode; `StreamOpenAck` + `StreamFrame` decode). It exists\n// because the publish-closure guard (scripts/publish-closure-guard.ts) forbids\n// `@opengeni/react` from depending on `@opengeni/agent-proto` — the published SDK\n// closure may only reach `@opengeni/sdk` among `@opengeni/*` packages. So, exactly\n// as the guard prescribes (\"hand-mirror it\"), we re-encode just these three\n// messages against the third-party `@bufbuild/protobuf/wire` runtime (the same\n// runtime `@opengeni/agent-proto` generates against), byte-compatible with the\n// generated code. Field numbers copied verbatim from\n// `packages/agent-proto/src/gen/opengeni_agent.ts`:\n// StreamChannel: channelId=1, workspaceId=2, agentId=3, kind=4(int32), port=5(uint32)\n// StreamOpen: channel=1(msg), token=2, role=3(int32), resumeFromSeq=4(uint64, omitted when \"0\")\n// StreamOpenAck: accepted=1(bool), error=2(AgentError msg), resumeFromSeq=3\n// AgentError: code=1, message=2, retryable=3, detail=4 (we read only message)\n// StreamFrame: channelId=1, seq=2, data=3(bytes), producedAtMs=4 (we read only data)\nimport { BinaryReader, BinaryWriter } from \"@bufbuild/protobuf/wire\";\n\n/** `StreamKind.STREAM_KIND_DESKTOP` (agent-proto enum value). */\nexport const STREAM_KIND_DESKTOP = 2;\n/** `StreamRole.STREAM_ROLE_CLIENT` (agent-proto enum value). */\nexport const STREAM_ROLE_CLIENT = 2;\n\nexport interface RelayChannel {\n channelId: string;\n workspaceId: string;\n agentId: string;\n kind: number;\n port: number;\n}\n\nexport interface RelayStreamOpen {\n channel: RelayChannel;\n token: string;\n role: number;\n resumeFromSeq: string;\n}\n\nfunction encodeChannel(message: RelayChannel, writer: BinaryWriter): BinaryWriter {\n if (message.channelId !== \"\") writer.uint32(10).string(message.channelId);\n if (message.workspaceId !== \"\") writer.uint32(18).string(message.workspaceId);\n if (message.agentId !== \"\") writer.uint32(26).string(message.agentId);\n if (message.kind !== 0) writer.uint32(32).int32(message.kind);\n if (message.port !== 0) writer.uint32(40).uint32(message.port);\n return writer;\n}\n\n/** Encode a `StreamOpen`, byte-compatible with `StreamOpen.encode(...).finish()`. */\nexport function encodeStreamOpen(message: RelayStreamOpen): Uint8Array {\n const writer = new BinaryWriter();\n encodeChannel(message.channel, writer.uint32(10).fork()).join();\n if (message.token !== \"\") writer.uint32(18).string(message.token);\n if (message.role !== 0) writer.uint32(24).int32(message.role);\n if (message.resumeFromSeq !== \"0\") writer.uint32(32).uint64(message.resumeFromSeq);\n return writer.finish();\n}\n\n/** The only `AgentError` field we surface from a rejected ack. */\nfunction decodeAgentErrorMessage(reader: BinaryReader, length: number): { message?: string } {\n const end = reader.pos + length;\n let message: string | undefined;\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 2 && tag === 18) {\n message = reader.string();\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return message !== undefined ? { message } : {};\n}\n\n/** Decode a `StreamOpenAck` (we need `accepted` + the error message). */\nexport function decodeStreamOpenAck(bytes: Uint8Array): {\n accepted: boolean;\n error?: { message?: string };\n} {\n const reader = new BinaryReader(bytes);\n const end = reader.len;\n let accepted = false;\n let error: { message?: string } | undefined;\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 1 && tag === 8) {\n accepted = reader.bool();\n continue;\n }\n if (tag >>> 3 === 2 && tag === 18) {\n error = decodeAgentErrorMessage(reader, reader.uint32());\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return error !== undefined ? { accepted, error } : { accepted };\n}\n\n/** Decode a `StreamFrame`, extracting only the `data` (framebuffer PNG) bytes. */\nexport function decodeStreamFrame(bytes: Uint8Array): { data: Uint8Array } {\n const reader = new BinaryReader(bytes);\n const end = reader.len;\n let data = new Uint8Array(0);\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 3 && tag === 26) {\n // Copy into a fresh ArrayBuffer-backed view: BinaryReader.bytes() returns\n // `Uint8Array<ArrayBufferLike>` (a view into the message buffer), which both\n // narrows the type to `Uint8Array<ArrayBuffer>` and isolates just this frame.\n data = new Uint8Array(reader.bytes());\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return { data };\n}\n","import {\n decodeStreamFrame,\n decodeStreamOpenAck,\n encodeStreamOpen,\n STREAM_KIND_DESKTOP,\n STREAM_ROLE_CLIENT,\n} from \"../lib/relay-wire\";\nimport type { DesktopConnectionState, DesktopStreamCapability } from \"@opengeni/sdk\";\nimport { type RefObject, useEffect, useRef, useState } from \"react\";\n\n/**\n * The minimal WebSocket surface the frame renderer drives. Lets tests (and a\n * future transport swap) inject a fake without a live socket — the exact\n * analogue of `DesktopRfbFactory` for the noVNC path. Structurally a subset of\n * the browser `WebSocket`.\n */\nexport interface DesktopWebSocketLike {\n binaryType: string;\n send(data: ArrayBuffer): void;\n close(): void;\n addEventListener(\n type: \"open\" | \"message\" | \"error\" | \"close\",\n listener: (ev: { data?: unknown } & Record<string, unknown>) => void,\n ): void;\n removeEventListener(\n type: \"open\" | \"message\" | \"error\" | \"close\",\n listener: (ev: { data?: unknown } & Record<string, unknown>) => void,\n ): void;\n}\n\nexport type DesktopWebSocketFactory = (url: string) => DesktopWebSocketLike;\n\nexport type UseRelayFrameStreamOptions = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /** The mount target. A `<canvas>` is appended here on connect. */\n containerRef: RefObject<HTMLDivElement | null>;\n /** Custom socket factory (tests / a transport swap). Defaults to `new WebSocket(url)`. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n};\n\nexport type UseRelayFrameStreamResult = {\n state: DesktopConnectionState;\n error: Error | null;\n /** Tear down + reopen the socket (e.g. after a drop, once a fresh url arrives). */\n reconnect: () => void;\n};\n\n// Relay datagram tags: byte[0] of every binary message. Mirrors the proven wire\n// protocol in `apps/api/scripts/diagnose-mac-desktop-stream.ts`.\nconst TAG_OPEN = 1;\nconst TAG_OPENACK = 2;\nconst TAG_FRAME = 3;\n\n/** Build a relay datagram: a fresh Uint8Array of `body.length + 1` with the tag\n * at [0] and the protobuf body copied at offset 1 (so `.buffer` is exact). */\nfunction datagram(tag: number, body: Uint8Array): Uint8Array {\n const out = new Uint8Array(body.length + 1);\n out[0] = tag;\n out.set(body, 1);\n return out;\n}\n\nfunction defaultWebSocketFactory(url: string): DesktopWebSocketLike {\n // The browser `WebSocket` is structurally a superset of `DesktopWebSocketLike`\n // (its event maps are stricter); cast so the seam stays loosely typed for fakes.\n return new WebSocket(url) as unknown as DesktopWebSocketLike;\n}\n\n/**\n * VIEW-ONLY PNG-frame renderer for a SELF-HOSTED desktop stream — the relay\n * transport (`transport: \"relay-frames\"`, `client: \"frames\"`). The self-hosted\n * agent produces one PNG per frame as a protobuf datagram over the relay; there\n * is no RFB/noVNC here. This hook opens the relay DESKTOP channel as a CLIENT\n * (mirroring the proven diagnostic wire protocol), decodes each PNG onto a\n * `<canvas>` mounted into `containerRef`, and drives the connection state\n * machine: idle → connecting (ws opening) → connected (first painted frame) →\n * error (ws error/close/ack rejection).\n *\n * It is DORMANT for any other transport (or no url): it stays idle and touches\n * nothing, so the noVNC path owns the surface. SSR-safe: the socket + DOM attach\n * live inside `useEffect`.\n *\n * Backpressure (critical — frames are ~1.8MB at ~10fps): at most one frame is\n * decoded at a time; a frame arriving mid-decode replaces the pending one\n * (latest-wins), so a slow decode can never build an unbounded queue.\n */\nexport function useRelayFrameStream(\n options: UseRelayFrameStreamOptions,\n): UseRelayFrameStreamResult {\n const { capability, containerRef, webSocketFactory } = options;\n const [state, setState] = useState<DesktopConnectionState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [nonce, setNonce] = useState(0);\n const stateRef = useRef<DesktopConnectionState>(\"idle\");\n const setBoth = (next: DesktopConnectionState) => {\n stateRef.current = next;\n setState(next);\n };\n\n const reconnect = () => setNonce((n) => n + 1);\n\n const transport = capability?.transport ?? null;\n const url = capability?.url ?? null;\n const token = capability?.token ?? null;\n\n // The factory is read via a ref so swapping it never re-opens the socket (it is\n // not a connect-effect dependency), symmetric with the noVNC `rfbFactory`.\n const factoryRef = useRef(webSocketFactory);\n factoryRef.current = webSocketFactory;\n\n useEffect(() => {\n // SSR / no DOM: stay idle and show the placeholder.\n if (typeof window === \"undefined\") return;\n // This hook OWNS the surface ONLY for the frame transport; anything else (or\n // no live url) → dormant idle so the noVNC path can drive.\n if (transport !== \"relay-frames\" || !url) {\n setBoth(\"idle\");\n return;\n }\n const container = containerRef.current;\n if (!container) {\n // Mount target not in the DOM yet (tab hidden) — stay idle so a re-attach\n // nudge can re-run this effect once the container exists.\n setBoth(\"idle\");\n return;\n }\n\n // Parse the 4 relay query params the channel is keyed on.\n let channel: {\n channelId: string;\n workspaceId: string;\n agentId: string;\n kind: number;\n port: number;\n };\n try {\n const u = new URL(url);\n channel = {\n channelId: u.searchParams.get(\"channel\") ?? \"\",\n workspaceId: u.searchParams.get(\"ws\") ?? \"\",\n agentId: u.searchParams.get(\"agent\") ?? \"\",\n kind: STREAM_KIND_DESKTOP,\n port: Number(u.searchParams.get(\"port\") ?? \"0\"),\n };\n } catch (cause) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n return;\n }\n\n setError(null);\n setBoth(\"connecting\");\n\n // Canvas mount — mirror the noVNC surface conventions: absolute-fill the\n // panel and CONTAIN the framebuffer (aspect-preserved, centered, letterboxed)\n // — never distorted, never overflowing. `max-*: 100%` + `margin: auto` +\n // intrinsic size gives fit-to-panel without a resize observer.\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"data-opengeni-desktop-frames\", \"\");\n const style = canvas.style;\n style.position = \"absolute\";\n style.top = \"0\";\n style.left = \"0\";\n style.right = \"0\";\n style.bottom = \"0\";\n style.margin = \"auto\";\n style.maxWidth = \"100%\";\n style.maxHeight = \"100%\";\n style.width = \"auto\";\n style.height = \"auto\";\n style.display = \"block\";\n style.imageRendering = \"auto\";\n container.appendChild(canvas);\n const ctx = canvas.getContext(\"2d\");\n\n let disposed = false;\n let acked = false;\n // Backpressure: decode at most ONE frame at a time; a frame arriving mid-\n // decode replaces `pending` (keep only the LATEST) — never a queue.\n let decoding = false;\n let pending: Uint8Array | null = null;\n\n const drainLatest = async () => {\n if (decoding) return;\n decoding = true;\n try {\n while (pending) {\n if (disposed) break;\n const data = pending;\n pending = null;\n let bmp: ImageBitmap;\n try {\n // Copy the exact frame bytes into a fresh ArrayBuffer-backed view:\n // ts-proto's `bytes()` hands back a subarray VIEW into the larger\n // message buffer, so slicing isolates just this PNG (and satisfies the\n // `BlobPart` typing, which rejects the generic `ArrayBufferLike`).\n bmp = await createImageBitmap(new Blob([new Uint8Array(data)], { type: \"image/png\" }));\n } catch {\n // A corrupt/partial frame is skipped; latest-wins keeps us live.\n continue;\n }\n if (disposed) {\n bmp.close();\n break;\n }\n // Size the backing store to the frame's natural WxH on the first frame\n // (or when the resolution changes); CSS then scales the element to fit.\n if (canvas.width !== bmp.width || canvas.height !== bmp.height) {\n canvas.width = bmp.width;\n canvas.height = bmp.height;\n }\n ctx?.drawImage(bmp, 0, 0, canvas.width, canvas.height);\n bmp.close();\n // First painted frame → connected.\n if (!disposed && stateRef.current !== \"connected\") setBoth(\"connected\");\n }\n } finally {\n decoding = false;\n }\n };\n\n let ws: DesktopWebSocketLike;\n try {\n ws = (factoryRef.current ?? defaultWebSocketFactory)(url);\n } catch (cause) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n canvas.remove();\n return;\n }\n ws.binaryType = \"arraybuffer\";\n\n const onOpen = () => {\n if (disposed) return;\n const body = encodeStreamOpen({\n channel,\n token: token ?? \"\",\n role: STREAM_ROLE_CLIENT,\n resumeFromSeq: \"0\",\n });\n try {\n ws.send(datagram(TAG_OPEN, body).buffer as ArrayBuffer);\n } catch {\n // Socket already closing; the close/error handler surfaces it.\n }\n };\n\n const onMessage = (ev: { data?: unknown }) => {\n if (disposed) return;\n const data = ev.data;\n if (!(data instanceof ArrayBuffer)) return;\n const buf = new Uint8Array(data);\n if (buf.length === 0) return;\n const tag = buf[0];\n const rest = buf.subarray(1);\n if (tag === TAG_OPENACK) {\n let ack: ReturnType<typeof decodeStreamOpenAck>;\n try {\n ack = decodeStreamOpenAck(rest);\n } catch {\n return;\n }\n if (!ack.accepted) {\n setError(\n new Error(\n ack.error?.message\n ? `desktop stream rejected: ${ack.error.message}`\n : \"desktop stream rejected by the relay\",\n ),\n );\n setBoth(\"error\");\n try {\n ws.close();\n } catch {\n // already closed\n }\n return;\n }\n // Accepted — stay \"connecting\" until the first frame paints.\n acked = true;\n } else if (tag === TAG_FRAME) {\n let fr: ReturnType<typeof decodeStreamFrame>;\n try {\n fr = decodeStreamFrame(rest);\n } catch {\n return;\n }\n if (fr.data && fr.data.length > 0) {\n pending = fr.data; // latest-wins backpressure\n void drainLatest();\n }\n }\n };\n\n const onError = () => {\n if (disposed) return;\n setError((prev) => prev ?? new Error(\"desktop stream connection error\"));\n setBoth(\"error\");\n };\n\n const onClose = () => {\n if (disposed) return;\n setError(\n (prev) =>\n prev ??\n new Error(acked ? \"desktop stream closed\" : \"desktop stream closed before it opened\"),\n );\n setBoth(\"error\");\n };\n\n ws.addEventListener(\"open\", onOpen);\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n return () => {\n disposed = true;\n pending = null;\n ws.removeEventListener(\"open\", onOpen);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close();\n } catch {\n // ignore teardown errors\n }\n canvas.remove();\n };\n // ONLY a real transport change reopens: a fresh url (rotation), a new token,\n // the transport flipping, or a manual reconnect (`nonce`). The factory is read\n // via a ref and must never re-open the socket.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, token, transport, nonce]);\n\n return { state, error, reconnect };\n}\n","import {\n TTYD_SUBPROTOCOL,\n TerminalCapability,\n terminalSocketUrl,\n ttydAuthFrame,\n ttydInputFrame,\n ttydResizeFrame,\n TtydServerCommand,\n} from \"@opengeni/sdk\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\n/** The ttyd connection lifecycle as surfaced to the component. */\nexport type TerminalStreamStatus = \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport type UseTerminalStreamOptions = {\n /** The Terminal cell of the negotiated capabilities (`capabilities.Terminal`).\n * The stream connects ONLY when `transport === \"pty-ws\"` and `url` is set; on a\n * cold box (`transport === \"sse-events\"` / no url) it stays idle and the caller\n * falls back to the Channel-A read-only firehose. */\n capability: Pick<TerminalCapability, \"transport\" | \"url\" | \"token\"> | null;\n /** Called for each OUTPUT payload from ttyd (write verbatim into xterm). */\n onOutput?: ((data: string) => void) | undefined;\n /** Called when ttyd sends a SET_WINDOW_TITLE frame. */\n onTitle?: ((title: string) => void) | undefined;\n /** Initial PTY size to seed the ttyd auth frame + first resize. */\n initialCols?: number | undefined;\n initialRows?: number | undefined;\n};\n\nexport type UseTerminalStreamResult = {\n /** True once the ttyd socket is open (and the auth frame has been sent). */\n connected: boolean;\n status: TerminalStreamStatus;\n /** Pipe a keystroke/paste to the PTY stdin. No-op until the socket is open. */\n write: (data: string) => void;\n /** Tell ttyd the PTY window changed size (on xterm fit/resize). */\n resize: (cols: number, rows: number) => void;\n /** Tear the socket down (the effect also tears down on unmount / url change). */\n disconnect: () => void;\n};\n\n/** Decode an inbound ttyd frame's payload (everything after the 1-char command).\n * ttyd may send either a text frame (string) or a binary frame (ArrayBuffer);\n * for binary we slice off the first byte (the command) and utf-8 decode the rest. */\nfunction decodeFrame(data: string | ArrayBuffer): { command: string; payload: string } {\n if (typeof data === \"string\") {\n return { command: data.charAt(0), payload: data.slice(1) };\n }\n const bytes = new Uint8Array(data);\n const command = bytes.length > 0 ? String.fromCharCode(bytes[0]!) : \"\";\n const payload = bytes.length > 1 ? new TextDecoder().decode(bytes.subarray(1)) : \"\";\n return { command, payload };\n}\n\n/**\n * Drive a ttyd PTY-over-websocket connection from a `pty-ws` Terminal capability,\n * symmetric with `use-desktop-stream` (the noVNC-over-tunnel hook). The scoped\n * stream token is already embedded in the minted tunnel `url`; the WebSocket is\n * opened with the REQUIRED ttyd subprotocol \"tty\".\n *\n * ttyd wire protocol (see `@opengeni/sdk/terminal`):\n * - first frame: `JSON.stringify({ AuthToken: \"\" })` (+ optional columns/rows).\n * - client→server: INPUT = \"0\"+data ; RESIZE = \"1\"+JSON({columns,rows}).\n * - server→client: \"0\" = OUTPUT (→ xterm) ; \"1\" = SET_WINDOW_TITLE ;\n * \"2\" = SET_PREFERENCES (ignored). Binary frames are decoded the same way.\n *\n * On a `url`/`token` rotation (a box rollover folds a fresh address into the cell)\n * the effect re-runs: the old socket closes and a fresh one connects — a brief\n * terminal blink, acceptable on rollover (mirrors the desktop's RFB hot-swap).\n * SSR-safe: the socket open lives in `useEffect`, so a server render is a no-op.\n */\nexport function useTerminalStream(options: UseTerminalStreamOptions): UseTerminalStreamResult {\n const { capability, onOutput, onTitle, initialCols, initialRows } = options;\n const [status, setStatus] = useState<TerminalStreamStatus>(\"closed\");\n const wsRef = useRef<WebSocket | null>(null);\n // Latest size, so a resize() before the socket opens is replayed on open, and a\n // reconnect seeds the right geometry.\n const sizeRef = useRef<{ cols: number; rows: number }>({\n cols: initialCols ?? 80,\n rows: initialRows ?? 24,\n });\n // Keep the callbacks current without re-running the connect effect on every\n // render (the parent passes fresh closures each time).\n const onOutputRef = useRef(onOutput);\n const onTitleRef = useRef(onTitle);\n onOutputRef.current = onOutput;\n onTitleRef.current = onTitle;\n\n const transport = capability?.transport ?? null;\n const url = capability?.url ?? null;\n const token = capability?.token ?? null;\n\n useEffect(() => {\n // SSR / no WebSocket / not a live pty-ws cell: stay closed; the caller falls\n // back to the Channel-A read-only firehose.\n if (typeof window === \"undefined\" || typeof WebSocket === \"undefined\") return;\n if (transport !== \"pty-ws\" || !url) {\n setStatus(\"closed\");\n return;\n }\n\n let disposed = false;\n let socket: WebSocket;\n setStatus(\"connecting\");\n try {\n // The ttyd \"tty\" subprotocol is REQUIRED — ttyd rejects a handshake without\n // it. The scoped token is already in the tunnel `url`.\n socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);\n } catch {\n setStatus(\"error\");\n return;\n }\n socket.binaryType = \"arraybuffer\";\n wsRef.current = socket;\n\n socket.onopen = () => {\n if (disposed) return;\n // ttyd's required first frame: the auth message (empty token — the gate is\n // the tunnel url + scoped stream token, not a ttyd -c credential), seeded\n // with the current PTY geometry. Then an explicit resize to be safe.\n try {\n socket.send(ttydAuthFrame({ columns: sizeRef.current.cols, rows: sizeRef.current.rows }));\n socket.send(ttydResizeFrame(sizeRef.current.cols, sizeRef.current.rows));\n } catch {\n // a closed socket between open and send — onclose handles state.\n }\n setStatus(\"open\");\n };\n\n socket.onmessage = (ev: MessageEvent) => {\n if (disposed) return;\n const { command, payload } = decodeFrame(ev.data as string | ArrayBuffer);\n switch (command) {\n case TtydServerCommand.OUTPUT:\n onOutputRef.current?.(payload);\n break;\n case TtydServerCommand.SET_WINDOW_TITLE:\n onTitleRef.current?.(payload);\n break;\n // SET_PREFERENCES (\"2\") and anything else: ignored.\n default:\n break;\n }\n };\n\n socket.onerror = () => {\n if (!disposed) setStatus(\"error\");\n };\n socket.onclose = () => {\n if (!disposed) setStatus(\"closed\");\n };\n\n return () => {\n disposed = true;\n wsRef.current = null;\n // Drop handlers so an in-flight close/error doesn't mutate state post-unmount.\n socket.onopen = null;\n socket.onmessage = null;\n socket.onerror = null;\n socket.onclose = null;\n try {\n socket.close();\n } catch {\n // ignore teardown errors\n }\n };\n // A url/token change (rotation) re-runs this effect → close old, open new.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [transport, url, token]);\n\n return useMemo<UseTerminalStreamResult>(() => {\n const write = (data: string) => {\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(ttydInputFrame(data));\n } catch {\n // socket raced closed — the reconnect effect will re-establish.\n }\n }\n };\n const resize = (cols: number, rows: number) => {\n if (cols <= 0 || rows <= 0) return;\n sizeRef.current = { cols, rows };\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(ttydResizeFrame(cols, rows));\n } catch {\n // socket raced closed — geometry is replayed on the next open.\n }\n }\n };\n const disconnect = () => {\n const ws = wsRef.current;\n wsRef.current = null;\n if (ws) {\n try {\n ws.close();\n } catch {\n // ignore\n }\n }\n };\n return { connected: status === \"open\", status, write, resize, disconnect };\n }, [status]);\n}\n","import type {\n SandboxCommandOutputDeltaPayload,\n SessionEvent,\n TerminalPtyExitedPayload,\n TerminalPtyOutputDeltaPayload,\n} from \"@opengeni/sdk\";\nimport { OpenGeniApiError } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\nexport type TerminalChunk = {\n /** Stable key (the source event id) so the xterm writer tracks a written-cursor. */\n id: string;\n /** Raw output bytes (utf-8 lossy) — written verbatim into xterm. */\n text: string;\n /** stdout vs stderr (drives optional tinting). */\n stream: \"stdout\" | \"stderr\";\n /** Global ordering: the source event sequence. */\n seq: number;\n};\n\nexport type UseSandboxTerminalOptions = ClientOverride & {\n /** The live session event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /** Restrict to one PTY (by ptyId). Omit to interleave the agent firehose +\n * every PTY. */\n ptyId?: string | undefined;\n /** Include the agent's command-output firehose (sandbox.command.output.delta).\n * Default true — the read-only \"terminal-as-events\" the data path settled on. */\n includeAgentFirehose?: boolean | undefined;\n /**\n * OPEN an interactive PTY against the box so the user can type, not just watch.\n * When true (and the session is live) the hook calls `terminalPtyOpen` once,\n * tracks the returned ptyId, exposes `write` immediately (bound to that ptyId),\n * and closes the PTY on unmount. The PTY's banner + every output delta ride the\n * SSE spine (`terminal.pty.*`) back into `events`, so xterm fills in. Default\n * false — a caller that only wants the read-only firehose stays projection-only.\n */\n interactive?: boolean | undefined;\n /** Lease liveness (\"cold\" | \"warm\" | \"draining\"). The interactive PTY is only\n * opened once the box is warm — opening on a cold box (ptyCapable is advertised\n * cold too) races the box and leaves a dead read-only terminal. */\n liveness?: string | undefined;\n};\n\nexport type UseSandboxTerminalResult = {\n /** Ordered, deduped output chunks to write() into xterm.js. */\n chunks: TerminalChunk[];\n /** Whether a PTY is currently open (drives the prompt/cursor affordance). */\n running: boolean;\n /**\n * Interactive write fn when a PTY is open and the backend supports stdin\n * (`terminal.transport === \"pty-ws\"` / `PtyOpenResponse.supportsInput`). Null\n * in the read-only event-projection case (v1 default).\n */\n write: ((data: string) => void) | null;\n /** The active PTY id, if one is open. */\n activePtyId: string | null;\n /** Close the active PTY (no-op when none is open). */\n close: () => void;\n /** A PTY-open failure (interactive mode), if any. */\n error: Error | null;\n};\n\n/**\n * Project the Channel-A event log into an xterm-writable byte stream. The\n * terminal is \"terminal-as-events\": there is NO new socket in v1 — the agent's\n * command output (`sandbox.command.output.delta`) and any interactive PTY\n * (`terminal.pty.output.delta`) ride the existing SSE spine. When a PTY is open\n * and the backend accepts stdin, `write` pipes keystrokes via the SDK\n * `terminalPtyWrite` (the synchronous Channel-A control path).\n */\nexport function useSandboxTerminal(\n sessionId: string | null | undefined,\n options: UseSandboxTerminalOptions,\n): UseSandboxTerminalResult {\n const { client, workspaceId } = useOpenGeni(options);\n const includeAgentFirehose = options.includeAgentFirehose ?? true;\n const interactive = options.interactive ?? false;\n // When the caller controls a fixed ptyId we project that one; otherwise (the\n // interactive default) the hook opens its OWN PTY and tracks it here so `write`\n // is live before the `terminal.pty.started` event round-trips through SSE.\n const ptyFilter = options.ptyId;\n const liveness = options.liveness;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${interactive}\\u0000${ptyFilter ?? \"\"}`;\n const [openedPtyId, setOpenedPtyId] = useState<string | null>(null);\n const [openError, setOpenError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Bumped to force a fresh PTY open after a write reveals the old PTY was lost\n // (a 409 \"pty session lost\" — the box rolled over since the open). This makes\n // the interactive terminal self-heal instead of dead-ending on a stale session.\n const [reopenNonce, setReopenNonce] = useState(0);\n const currentIdentityRef = useRef(identityKey);\n currentIdentityRef.current = identityKey;\n\n // Async PTY state belongs to one workspace/session/mode identity. Hide it\n // synchronously on the switching render, then clear it durably in the effect.\n useEffect(() => {\n setStateIdentity(identityKey);\n setOpenedPtyId(null);\n setOpenError(null);\n }, [identityKey]);\n\n // Open ONE interactive PTY against the box, close it on unmount/identity\n // change. The open's banner + subsequent output deltas ride A1, so xterm fills\n // from `events` — we don't thread the open's body output here (the SSE\n // projection is the single source of truth for what's written). The open is\n // gated on a WARM box: opening on a cold/warming lease races the box (the PTY\n // exec-session is created on a box that the next op may not resume), which is\n // exactly the \"session not found\" terminal failure; we wait for warm/draining.\n const openInFlight = useRef(false);\n const boxWarm = liveness === undefined || liveness === \"warm\" || liveness === \"draining\";\n useEffect(() => {\n if (!interactive || !sessionId || ptyFilter || !boxWarm) {\n setOpenError(null);\n return;\n }\n let cancelled = false;\n setOpenError(null);\n openInFlight.current = true;\n let openedId: string | null = null;\n void client\n .terminalPtyOpen(workspaceId, sessionId, {})\n .then((res) => {\n if (cancelled) {\n // Raced past unmount — close the orphan we just opened.\n void client\n .terminalPtyClose(workspaceId, sessionId, { ptyId: res.ptyId })\n .catch(() => {});\n return;\n }\n openedId = res.ptyId;\n setOpenedPtyId(res.ptyId);\n })\n .catch((cause) => {\n if (!cancelled) setOpenError(cause instanceof Error ? cause : new Error(String(cause)));\n })\n .finally(() => {\n openInFlight.current = false;\n });\n return () => {\n cancelled = true;\n setOpenedPtyId(null);\n const id = openedId;\n if (id) void client.terminalPtyClose(workspaceId, sessionId, { ptyId: id }).catch(() => {});\n };\n }, [interactive, client, workspaceId, sessionId, ptyFilter, boxWarm, reopenNonce]);\n\n const { chunks, openPty, supportsInput } = useMemo(() => {\n const out: TerminalChunk[] = [];\n // Track PTY lifecycle so `running`/`write` reflect the latest state.\n const open = new Map<string, { supportsInput: boolean }>();\n let lastOpened: string | null = null;\n let lastSupportsInput = false;\n\n for (const event of options.events) {\n // The hook is session-scoped even if a host accidentally retains the old\n // event array for one render during navigation.\n if (event.workspaceId !== workspaceId || event.sessionId !== sessionId) continue;\n if (event.type === \"terminal.pty.started\") {\n const payload = event.payload as { ptyId?: string } | null;\n if (payload?.ptyId) {\n open.set(payload.ptyId, { supportsInput: true });\n lastOpened = payload.ptyId;\n }\n continue;\n }\n if (event.type === \"terminal.pty.exited\") {\n const payload = event.payload as TerminalPtyExitedPayload | null;\n if (payload?.ptyId) {\n open.delete(payload.ptyId);\n if (lastOpened === payload.ptyId) lastOpened = null;\n }\n continue;\n }\n if (event.type === \"terminal.pty.output.delta\") {\n const payload = event.payload as TerminalPtyOutputDeltaPayload | null;\n if (!payload || (ptyFilter && payload.ptyId !== ptyFilter)) continue;\n out.push({\n id: event.id,\n text: payload.chunk,\n stream: payload.stream === \"stderr\" ? \"stderr\" : \"stdout\",\n seq: event.sequence,\n });\n continue;\n }\n if (includeAgentFirehose && !ptyFilter && event.type === \"sandbox.command.output.delta\") {\n const payload = event.payload as SandboxCommandOutputDeltaPayload | null;\n if (!payload?.chunk) continue;\n out.push({\n id: event.id,\n text: payload.chunk,\n stream: payload.stream === \"stderr\" ? \"stderr\" : \"stdout\",\n seq: event.sequence,\n });\n }\n }\n\n // Stable order: by sequence (the SSE spine guarantees per-session ordering).\n out.sort((a, b) => a.seq - b.seq);\n const activePty = ptyFilter && open.has(ptyFilter) ? ptyFilter : lastOpened;\n lastSupportsInput = activePty ? (open.get(activePty)?.supportsInput ?? false) : false;\n return { chunks: out, openPty: activePty, supportsInput: lastSupportsInput };\n }, [options.events, ptyFilter, includeAgentFirehose, workspaceId, sessionId]);\n\n // The PTY this hook actively drives: the one it opened (interactive) wins so\n // `write` is live the instant the open resolves — even before the\n // `terminal.pty.started` event arrives through SSE. Fall back to whatever the\n // event projection found (a PTY opened elsewhere, or a caller-pinned ptyId).\n const identityMatches = stateIdentity === identityKey;\n const activePtyId = identityMatches ? (openedPtyId ?? openPty) : null;\n // An interactively-opened PTY accepts stdin by construction (we only open it on\n // a pty-capable backend); a projected PTY uses its advertised supportsInput.\n const canWrite = openedPtyId !== null || supportsInput;\n\n const write = useMemo(() => {\n if (!activePtyId || !canWrite || !sessionId) return null;\n const writeIdentity = identityKey;\n return (data: string) => {\n void client\n .terminalPtyWrite(workspaceId, sessionId, { ptyId: activePtyId, data })\n .catch((cause) => {\n // The PTY exec-session was lost on the live box (409/404 — the box rolled\n // over since the open). Self-heal: drop the stale id and re-open a fresh\n // PTY against the current box rather than silently swallowing keystrokes.\n if (\n cause instanceof OpenGeniApiError &&\n (cause.status === 409 || cause.status === 404) &&\n activePtyId === openedPtyId &&\n currentIdentityRef.current === writeIdentity\n ) {\n setOpenedPtyId(null);\n setReopenNonce((n) => n + 1);\n }\n });\n };\n }, [client, workspaceId, sessionId, activePtyId, canWrite, openedPtyId, identityKey]);\n\n const close = useCallback(() => {\n if (!activePtyId || !sessionId) return;\n void client.terminalPtyClose(workspaceId, sessionId, { ptyId: activePtyId }).catch(() => {});\n }, [client, workspaceId, sessionId, activePtyId]);\n\n return {\n chunks,\n running: activePtyId !== null,\n write,\n activePtyId,\n close,\n error: identityMatches ? openError : null,\n };\n}\n","import type {\n FsChangedPayload,\n FsReadResponse,\n FsTreeNode,\n FsWriteResponse,\n GitChangedPayload,\n GitFileStatusCode,\n OpenGeniRequestOptions,\n SessionEvent,\n WorkspaceCaptureManifest,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/** The git-status overlay a file row may carry (tints modified files in the tree). */\nexport type FileTreeStatus = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\";\n\nexport type CapturedFileUnavailableReason = \"not-captured\" | \"too-large\" | \"content-missing\";\n\n/** A cold capture can index a path without retaining its contents. Components\n * use this typed result to offer an explicit wake action instead of silently\n * turning passive review into a live sandbox read. */\nexport class CapturedFileUnavailableError extends Error {\n readonly code = \"captured_file_unavailable\";\n\n constructor(\n readonly path: string,\n readonly reason: CapturedFileUnavailableReason,\n ) {\n super(\n reason === \"too-large\"\n ? `${path} exceeds the captured preview limit.`\n : reason === \"content-missing\"\n ? `${path} is no longer available in this capture.`\n : `${path} was not changed in this captured turn.`,\n );\n this.name = \"CapturedFileUnavailableError\";\n }\n}\n\nexport class FileWriteConflictError extends Error {\n readonly code = \"file_write_conflict\";\n\n constructor(\n readonly path: string,\n readonly expectedContent: string,\n readonly liveContent: string,\n ) {\n super(`${path} changed on the machine. Reload it or explicitly overwrite the live version.`);\n this.name = \"FileWriteConflictError\";\n }\n}\n\nexport type SandboxWriteFileOptions = {\n /** Exact text loaded into the editor. The live file is re-read before write;\n * divergence fails closed with FileWriteConflictError. */\n expectedContent?: string | undefined;\n /** Deliberately bypass the expected-content guard after a visible conflict. */\n force?: boolean | undefined;\n};\n\n/** A node in the Pierre file tree. `children === undefined` ⇒ an unexpanded dir\n * (lazy treeMode); `children: []` ⇒ an expanded-but-empty dir. */\nexport type FileTreeNode = {\n path: string; // workspace-relative POSIX\n name: string;\n kind: \"file\" | \"dir\";\n children?: FileTreeNode[] | undefined;\n size?: number | null | undefined;\n status?: FileTreeStatus | undefined;\n /** A residue dir the capture COLLAPSED (node_modules, .git, dist, …): its\n * contents were never indexed. Cold, expanding it can't list children — the\n * UI shows an inline \"contents on machine\" row until the box is warm. */\n truncated?: boolean | undefined;\n};\n\nexport type UseSandboxFilesOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh\n * on `fs.changed` / `git.changed`. */\n events?: SessionEvent[] | undefined;\n /** Initial path to list (workspace root by default). */\n rootPath?: string | undefined;\n /** Hold off the initial list (e.g. panel collapsed). Default true. */\n enabled?: boolean | undefined;\n /** The lease liveness (\"cold\" | \"warm\" | \"draining\"). The structured FileSystem\n * capability is advertised even on a COLD box, so the mount-time list can race\n * the box: it lists before the box is warm, gets an empty/errored result, and\n * (with no `fs.changed` event) never re-lists. Passing liveness re-lists when\n * the box first becomes warm, so the tree populates as soon as the box is up. */\n liveness?: string | undefined;\n /** The latest turn-end workspace capture (from `useWorkspaceCapture`). The tree\n * paints immediately from this durable index, then a warm box reconciles live\n * in place. If that live read fails, the capture remains a truthful read-only\n * fallback instead of turning into an empty error surface. */\n capture?: WorkspaceCaptureManifest | null | undefined;\n /** Called when an OPTIMISTIC mutation is reverted because its background\n * Channel-A op failed (e.g. a 409 rename collision). The host wires this to a\n * toast — the tree silently rolls the node back, the user sees why. */\n onMutationError?: ((error: Error, op: string) => void) | undefined;\n};\n\nexport type UseSandboxFilesResult = {\n /** The tree roots (the listed root's children). */\n tree: FileTreeNode[];\n /** Lazy-expand a directory node in place (lists its immediate children). */\n expand: (path: string) => Promise<void>;\n /** Paths whose lazy `fs.list` is currently in flight — the FileBrowser shows a\n * spinner on these nodes so a 2-3s Channel-A list never looks frozen. */\n expandingPaths: Set<string>;\n /** Read a file for the preview pane (text or base64-for-binary, size-capped). */\n readFile: (path: string, options?: OpenGeniRequestOptions) => Promise<FsReadResponse>;\n /** Write a file (overwrite, last-writer-wins) — the editor save path.\n * Optimistic: a brand-new file is spliced into the tree immediately and the\n * Channel-A write runs in the background; on failure the splice is reverted. */\n writeFile: (\n path: string,\n content: string,\n options?: SandboxWriteFileOptions,\n ) => Promise<FsWriteResponse>;\n /** Create a new empty file (refuses to clobber an existing path: overwrite=false). */\n createFile: (path: string) => Promise<void>;\n /** Create a directory (recursive by default). */\n createDir: (path: string) => Promise<void>;\n /** Delete a path (pass recursive=true for a non-empty directory). */\n deleteEntry: (path: string, recursive?: boolean) => Promise<void>;\n /** Move / rename a path (rename == move). Refuses to clobber unless overwrite=true. */\n moveEntry: (path: string, newPath: string, opts?: { overwrite?: boolean }) => Promise<void>;\n /** Re-list the whole tree from the root. */\n refresh: () => Promise<void>;\n /** Which source the tree is currently served from: the live box, the turn-end\n * capture (cold/offline), or neither yet. M5's source badge reads this. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n loading: boolean;\n error: Error | null;\n};\n\n/** The workspace-relative parent directory of a POSIX path (\"\" for a root entry). */\nfunction parentOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i <= 0 ? \"\" : path.slice(0, i);\n}\n\n/** The leaf name of a POSIX path. */\nfunction leafOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i < 0 ? path : path.slice(i + 1);\n}\n\n/** Join a parent dir with a leaf name (handles the root \"\" parent). */\n/** Stable sibling ordering: dirs before files, then case-insensitive by name —\n * matches a typical depth-1 list so an optimistic insert lands where a real\n * re-list would put it (no jump when the server reconciles). */\nfunction sortNodes(nodes: FileTreeNode[]): FileTreeNode[] {\n return [...nodes].sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === \"dir\" ? -1 : 1;\n return a.name.localeCompare(b.name, undefined, { sensitivity: \"base\" });\n });\n}\n\nfunction fsNodeToTree(node: FsTreeNode, captureComplete = false): FileTreeNode {\n const kind = node.type === \"dir\" ? \"dir\" : \"file\";\n // Lazy-tree contract: a depth-bounded `fsList` returns each directory at the\n // depth boundary with `children: []` (the dir is listed, but its grandchildren\n // are NOT). An empty array therefore means \"not yet expanded\", NOT \"empty\n // directory\" — so we must map it to `undefined` (the unexpanded marker the\n // FileBrowser keys lazy-expand on). If we kept `[]`, `toggle()`'s\n // `node.children === undefined` guard would never fire and clicking a folder\n // would do nothing (the reported bug). A directory we actually expand has its\n // children spliced in by `replaceChildren` (bypassing this mapper), so a\n // genuinely-empty dir correctly ends up as `[]` AFTER expansion.\n const mappedChildren =\n node.children && node.children.length > 0\n ? node.children.map((child) => fsNodeToTree(child, captureComplete))\n : captureComplete && kind === \"dir\"\n ? []\n : undefined;\n return {\n path: node.path,\n name: node.name,\n kind,\n size: node.sizeBytes,\n ...(kind === \"dir\" ? { children: mappedChildren } : {}),\n // A collapsed residue dir from the capture index — carried through so the\n // tree can show an inline \"contents on machine\" row cold instead of nothing.\n ...(kind === \"dir\" && node.truncated ? { truncated: true } : {}),\n };\n}\n\nconst GIT_STATUS_TO_TREE: Partial<Record<GitFileStatusCode, FileTreeStatus>> = {\n added: \"added\",\n modified: \"modified\",\n deleted: \"deleted\",\n renamed: \"renamed\",\n copied: \"added\",\n untracked: \"untracked\",\n typechange: \"modified\",\n};\n\n/** Replace the children of `targetPath` within the tree (immutably). */\nfunction replaceChildren(\n nodes: FileTreeNode[],\n targetPath: string,\n children: FileTreeNode[],\n): FileTreeNode[] {\n return nodes.map((node) => {\n if (node.path === targetPath) {\n return { ...node, children };\n }\n if (node.kind === \"dir\" && node.children && targetPath.startsWith(`${node.path}/`)) {\n return { ...node, children: replaceChildren(node.children, targetPath, children) };\n }\n return node;\n });\n}\n\n/** Find a node by exact path (depth-first). */\nfunction findNodeByPath(nodes: FileTreeNode[], path: string): FileTreeNode | undefined {\n for (const node of nodes) {\n if (node.path === path) return node;\n if (node.kind === \"dir\" && node.children && path.startsWith(`${node.path}/`)) {\n const hit = findNodeByPath(node.children, path);\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n// ── In-place (optimistic) tree mutations ────────────────────────────────────\n// The Pierre mutation-handle analogue. These splice a single node in/out/across\n// the immutable tree, PRESERVING every other node's `children` (and therefore the\n// FileBrowser's expansion + selection). The old data flow re-listed the whole\n// root and `setTree(rootChildren)` — that dropped every expanded dir back to the\n// unexpanded marker, which is exactly the \"everything refreshes / collapses to\n// .config\" the user saw. Mutating in place is the fix.\n//\n// `parent === \"\"` targets the root list directly. A parent that isn't present in\n// the (lazily-loaded) tree means it's collapsed/unloaded — the helpers return the\n// tree UNCHANGED in that case (caller treats it as \"nothing visible to update\",\n// which is correct: a collapsed dir re-lists fresh when the user expands it).\n\n/** True when `parent` is the root (\"\") or a dir node that is actually present in\n * the loaded tree (so an insert/remove there would be visible). */\nfunction parentIsLoaded(nodes: FileTreeNode[], parent: string): boolean {\n if (parent === \"\") return true;\n const node = findNodeByPath(nodes, parent);\n return Boolean(node && node.kind === \"dir\" && node.children !== undefined);\n}\n\n/** Insert `child` under `parent` (immutably), keeping siblings sorted. A no-op\n * (returns the same array) when the parent isn't a loaded dir or the child\n * already exists. */\nfunction insertNode(nodes: FileTreeNode[], parent: string, child: FileTreeNode): FileTreeNode[] {\n if (parent === \"\") {\n if (nodes.some((n) => n.path === child.path)) return nodes;\n return sortNodes([...nodes, child]);\n }\n return nodes.map((node) => {\n if (node.path === parent) {\n if (node.kind !== \"dir\" || node.children === undefined) return node;\n if (node.children.some((n) => n.path === child.path)) return node;\n return { ...node, children: sortNodes([...node.children, child]) };\n }\n if (node.kind === \"dir\" && node.children && parent.startsWith(`${node.path}/`)) {\n return { ...node, children: insertNode(node.children, parent, child) };\n }\n return node;\n });\n}\n\n/** Remove the node at `path` (immutably). No-op when it isn't in the tree. */\nfunction removeNode(nodes: FileTreeNode[], path: string): FileTreeNode[] {\n const parent = parentOf(path);\n if (parent === \"\") {\n if (!nodes.some((n) => n.path === path)) return nodes;\n return nodes.filter((n) => n.path !== path);\n }\n return nodes.map((node) => {\n if (node.path === parent) {\n if (node.kind !== \"dir\" || node.children === undefined) return node;\n return { ...node, children: node.children.filter((n) => n.path !== path) };\n }\n if (node.kind === \"dir\" && node.children && parent.startsWith(`${node.path}/`)) {\n return { ...node, children: removeNode(node.children, path) };\n }\n return node;\n });\n}\n\n/** Reconcile a freshly-listed depth-1 set of children against the CURRENT nodes\n * at the same level, PRESERVING expansion: an existing dir keeps its already-\n * loaded `children` (so its expanded subtree survives), new entries are added,\n * and entries the server no longer returns are dropped. This is the in-place\n * merge a root (\"\") reconcile needs — a blind replace would collapse every\n * expanded top-level dir back to the unexpanded marker (the reported bug). */\n/** Shallow node equality for identity preservation across a reconcile — same\n * path/kind/name/size/status (children handled separately by the caller). */\nfunction sameNodeShallow(a: FileTreeNode, b: FileTreeNode): boolean {\n return (\n a.path === b.path &&\n a.kind === b.kind &&\n a.name === b.name &&\n (a.size ?? null) === (b.size ?? null) &&\n (a.status ?? undefined) === (b.status ?? undefined) &&\n (a.truncated ?? false) === (b.truncated ?? false)\n );\n}\n\nfunction mergeChildren(current: FileTreeNode[], listed: FileTreeNode[]): FileTreeNode[] {\n const byPath = new Map(current.map((n) => [n.path, n] as const));\n const merged = listed.map((next) => {\n const existing = byPath.get(next.path);\n if (!existing) return next; // brand-new entry\n // Keep an already-expanded dir's loaded children; otherwise take the listing's\n // marker (undefined = unexpanded). Carry forward size/status from the listing.\n if (existing.kind === \"dir\" && next.kind === \"dir\" && existing.children !== undefined) {\n const candidate: FileTreeNode = { ...next, children: existing.children };\n // Preserve identity when nothing observable changed — no remount (§12-D1).\n return sameNodeShallow(existing, candidate) ? existing : candidate;\n }\n // A file or an unexpanded dir: preserve identity when the node is unchanged.\n if (existing.children === next.children && sameNodeShallow(existing, next)) {\n return existing;\n }\n return next;\n });\n return sortNodes(merged);\n}\n\n/** Root-level (\"\") reconcile: merge a fresh depth-1 listing into the root list\n * without collapsing expanded top-level dirs. */\nfunction mergeRootChildren(nodes: FileTreeNode[], listed: FileTreeNode[]): FileTreeNode[] {\n return mergeChildren(nodes, listed);\n}\n\n/** Re-path a subtree rooted at `node` from `fromPrefix` to `toPrefix` (so a moved\n * dir's descendants keep correct paths without a re-list). */\nfunction repathNode(node: FileTreeNode, fromPrefix: string, toPrefix: string): FileTreeNode {\n const newPath = toPrefix + node.path.slice(fromPrefix.length);\n const next: FileTreeNode = { ...node, path: newPath, name: leafOf(newPath) };\n if (node.children) next.children = node.children.map((c) => repathNode(c, fromPrefix, toPrefix));\n return next;\n}\n\nfunction decodeBase64Bytes(value: string): Uint8Array {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n\nfunction decodeBase64Text(value: string): string {\n return new TextDecoder().decode(decodeBase64Bytes(value));\n}\n\nfunction encodeBase64Bytes(bytes: Uint8Array): string {\n let binary = \"\";\n const chunkSize = 0x8000;\n for (let offset = 0; offset < bytes.length; offset += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));\n }\n return btoa(binary);\n}\n\nasync function assertCapturedHash(bytes: Uint8Array, expected: string | null): Promise<void> {\n if (!expected) throw new Error(\"Captured file is missing its integrity hash.\");\n if (!globalThis.crypto?.subtle) {\n throw new Error(\"Captured file integrity verification is unavailable in this browser.\");\n }\n const buffer = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(buffer).set(bytes);\n const digest = new Uint8Array(await globalThis.crypto.subtle.digest(\"SHA-256\", buffer));\n const actual = [...digest].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n if (actual !== expected) throw new Error(\"Captured file content failed its integrity check.\");\n}\n\n/**\n * Project the FileSystem service into a lazy-loaded Pierre tree. The initial\n * list pulls one level (depth 1); `expand(path)` lists a directory's immediate\n * children on demand (the fast lazy-tree UX). A git-status overlay tints\n * modified files. Auto-refreshes when an `fs.changed` / `git.changed` event\n * arrives on the live log.\n */\nexport function useSandboxFiles(\n sessionId: string | null | undefined,\n options: UseSandboxFilesOptions = {},\n): UseSandboxFilesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const rootPath = options.rootPath ?? \"\";\n const capture = options.capture ?? null;\n const isLive = options.liveness === \"warm\" || options.liveness === \"draining\";\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${rootPath}`;\n\n const [tree, setTree] = useState<FileTreeNode[]>([]);\n // A ref mirror of the current tree — lets the optimistic path snapshot the\n // pre-op tree WITHOUT relying on a `setTree` updater (which StrictMode invokes\n // twice, corrupting an in-closure snapshot). Kept in sync on every set below\n // and in a layout effect for any path that sets `tree` directly.\n const treeRef = useRef<FileTreeNode[]>([]);\n const [loading, setLoading] = useState(false);\n const [expandingPaths, setExpandingPaths] = useState<Set<string>>(new Set());\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Which source the tree currently reflects — \"capture\" (cold paint) or \"live\".\n const [source, setSource] = useState<\"live\" | \"capture\" | null>(null);\n const sourceRef = useRef<\"live\" | \"capture\" | null>(null);\n const statusRef = useRef<Map<string, FileTreeStatus>>(new Map());\n // Async reads, event cursors, and debounced work are scoped to the hook's\n // workspace/session/root identity. These refs are reset/fenced at that boundary.\n const refreshGenerationRef = useRef(0);\n const refreshAbortRef = useRef<AbortController | null>(null);\n const identityAbortRef = useRef(new AbortController());\n const identityGenerationRef = useRef(0);\n const lastSeqRef = useRef(0);\n const pendingParentsRef = useRef<Set<string>>(new Set());\n const pendingGitRef = useRef(false);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // ── Self-emitted fs.changed de-dupe ───────────────────────────────────────\n // Every one of OUR mutations emits an `fs.changed` event back on the live log\n // (source:\"write\"). The old flow auto-refreshed on EVERY fs.changed, so each\n // edit self-triggered a full root collapse-reload (~5s, lost expansion). We now\n // (a) ignore fs.changed whose `source === \"write\"` (our own control-plane ops),\n // and (b) belt-and-braces, track the revisions WE caused so even a watch-sourced\n // echo of our own write is suppressed. Only EXTERNAL changes (the agent writing,\n // source:\"agent\"/\"watch\" we didn't cause) drive a targeted reconcile.\n const ownRevisionsRef = useRef<Set<number>>(new Set());\n const onMutationError = options.onMutationError;\n\n // Keep the ref mirror current for the optimistic snapshot path.\n treeRef.current = tree;\n\n // Overlay the git-status tint onto the tree. IDENTITY-PRESERVING: a node whose\n // tint and children are unchanged is returned by reference (not rebuilt), so a\n // re-tint or a cold→warm reconcile does NOT remount unchanged rows — the\n // no-flicker constraint. An empty overlay is still\n // meaningful: it strips stale tints after the repository becomes clean.\n const applyStatus = useCallback((nodes: FileTreeNode[]): FileTreeNode[] => {\n const overlay = statusRef.current;\n const walk = (list: FileTreeNode[]): FileTreeNode[] => {\n let changed = false;\n const out = list.map((node) => {\n const nextChildren = node.children ? walk(node.children) : node.children;\n const wantStatus = node.kind === \"file\" ? overlay.get(node.path) : undefined;\n const childrenChanged = nextChildren !== node.children;\n const statusChanged = (node.status ?? undefined) !== (wantStatus ?? undefined);\n if (!childrenChanged && !statusChanged) return node;\n changed = true;\n const next: FileTreeNode = {\n ...node,\n ...(node.children ? { children: nextChildren } : {}),\n };\n if (wantStatus) next.status = wantStatus;\n else delete next.status;\n return next;\n });\n return changed ? out : list;\n };\n return walk(nodes);\n }, []);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n refreshAbortRef.current?.abort();\n const refreshAbort = new AbortController();\n refreshAbortRef.current = refreshAbort;\n const generation = (refreshGenerationRef.current += 1);\n setLoading(true);\n setError(null);\n try {\n // Pull the git-status overlay first (best-effort — a non-repo box just\n // returns isRepo:false), then the tree, so the first paint is tinted.\n try {\n const status = await client.gitStatus(\n workspaceId,\n sessionId,\n { path: rootPath },\n { signal: refreshAbort.signal },\n );\n if (refreshGenerationRef.current !== generation) return;\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of status.files) {\n const code = file.worktree ?? file.index;\n const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n } catch {\n if (refreshGenerationRef.current !== generation) return;\n statusRef.current = new Map();\n }\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path: rootPath, depth: 1 },\n { signal: refreshAbort.signal },\n );\n if (refreshGenerationRef.current !== generation) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n // Merge rather than replace so an explicit refresh / cold→warm re-list folds\n // in new entries WITHOUT collapsing the dirs the user already expanded (and,\n // via the identity-preserving merge, WITHOUT remounting unchanged rows — the\n // no-flicker cold→warm reconcile). On a first (empty) load this is a plain set.\n setTree((prev) =>\n applyStatus(prev.length === 0 ? children : mergeRootChildren(prev, children)),\n );\n // Live data is now serving — flip the source off the capture snapshot.\n sourceRef.current = \"live\";\n setSource(\"live\");\n } catch (cause) {\n if (refreshGenerationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (refreshGenerationRef.current === generation) setLoading(false);\n if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, rootPath, applyStatus]);\n\n // Seed (or re-seed) the tree from a turn-end capture — the COLD/offline paint,\n // zero Channel-A calls. The tree index is workspace-relative (`treeIndex`); the\n // tint overlay comes from the capture's changed `files`. Uses the SAME merge as\n // the live reconcile so a re-seed (a newer capture arriving cold) patches deltas\n // in place instead of remounting the tree (§12-D1).\n const seedFromCapture = useCallback(\n (manifest: WorkspaceCaptureManifest) => {\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of manifest.files) {\n if (file.deleted) continue;\n const mapped = GIT_STATUS_TO_TREE[file.status];\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n // The capture tree is a complete bounded index. Preserve an explicit empty\n // `children: []` as an actually empty directory; the live depth-1 mapper\n // intentionally treats that shape as an unexpanded boundary.\n const children = (manifest.treeIndex.children ?? []).map((node) => fsNodeToTree(node, true));\n setTree((prev) =>\n applyStatus(prev.length === 0 ? children : mergeRootChildren(prev, children)),\n );\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n setError(null);\n },\n [applyStatus],\n );\n\n const expand = useCallback(\n async (path: string) => {\n if (!sessionId) return;\n // Capture browsing is a durable server-side read surface. Never turn a\n // folder click into a Channel-A list while cold; truncated residue already\n // renders a truthful \"contents on machine\" row.\n if (source === \"capture\" && capture) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n // Mark this node as expanding so the FileBrowser can render a spinner while\n // the (often 2-3s) Channel-A fs/list is in flight — the tree never looks\n // frozen on a click.\n setExpandingPaths((prev) => {\n const next = new Set(prev);\n next.add(path);\n return next;\n });\n try {\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path, depth: 1 },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n setTree((prev) => applyStatus(replaceChildren(prev, path, children)));\n } catch (cause) {\n if (identityGenerationRef.current !== identityGeneration) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (identityGenerationRef.current === identityGeneration) {\n setExpandingPaths((prev) => {\n if (!prev.has(path)) return prev;\n const next = new Set(prev);\n next.delete(path);\n return next;\n });\n }\n }\n },\n [client, workspaceId, sessionId, capture, source, applyStatus],\n );\n\n const readFile = useCallback(\n async (path: string, requestOptions: OpenGeniRequestOptions = {}) => {\n if (!sessionId) throw new Error(\"no session\");\n const identitySignal = identityAbortRef.current.signal;\n const signal = requestOptions.signal\n ? AbortSignal.any([identitySignal, requestOptions.signal])\n : identitySignal;\n const captured = capture?.files.find((file) => file.path === path && !file.deleted);\n if (source === \"capture\" && capture) {\n if (!captured) {\n throw new CapturedFileUnavailableError(path, \"not-captured\");\n }\n // A passive review of a turn-touched file must stay entirely server-side:\n // mint/read its captured after-image, verify the content hash, and never\n // wake the sandbox. A URL may expire between mint and GET, so retry once\n // through the authenticated API to obtain a fresh scoped URL.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const result = await client.getWorkspaceCaptureFile(\n workspaceId,\n sessionId,\n path,\n capture.revision,\n { signal },\n );\n if (result.path !== path || result.revision !== capture.revision) {\n throw new Error(\n \"Captured file identity did not match the requested workspace revision.\",\n );\n }\n if (\n result.hash !== captured.hash ||\n result.sizeBytes !== captured.sizeBytes ||\n result.isBinary !== captured.isBinary ||\n result.tooLarge !== captured.tooLarge\n ) {\n throw new Error(\"Captured file metadata did not match its manifest.\");\n }\n if (result.tooLarge) {\n throw new CapturedFileUnavailableError(path, \"too-large\");\n }\n\n let bytes: Uint8Array;\n let encoding: \"utf8\" | \"base64\";\n let content: string;\n if (result.content !== null && result.encoding !== null) {\n encoding = result.encoding;\n content = result.content;\n bytes =\n encoding === \"base64\"\n ? decodeBase64Bytes(content)\n : new TextEncoder().encode(content);\n } else if (result.contentUrl) {\n const response = await fetch(result.contentUrl.url, {\n credentials: \"omit\",\n cache: \"no-store\",\n referrerPolicy: \"no-referrer\",\n signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),\n }).catch(() => null);\n if (!response?.ok) {\n if (\n attempt === 0 &&\n (response === null || response.status === 401 || response.status === 403)\n ) {\n continue;\n }\n throw new Error(\"Captured file download failed.\");\n }\n bytes = new Uint8Array(await response.arrayBuffer());\n encoding = result.isBinary ? \"base64\" : \"utf8\";\n content = result.isBinary ? encodeBase64Bytes(bytes) : new TextDecoder().decode(bytes);\n } else {\n throw new CapturedFileUnavailableError(path, \"content-missing\");\n }\n\n if (bytes.byteLength !== result.sizeBytes) {\n throw new Error(\"Captured file size did not match its manifest.\");\n }\n await assertCapturedHash(bytes, result.hash);\n return {\n path,\n encoding,\n content,\n sizeBytes: result.sizeBytes,\n truncated: false,\n isBinary: result.isBinary,\n revision: result.revision,\n };\n }\n throw new Error(\"Captured file download failed after refreshing its URL.\");\n }\n return await client.fsRead(workspaceId, sessionId, { path }, { signal });\n },\n [client, workspaceId, sessionId, capture, source],\n );\n\n // TARGETED reconcile of a single directory — re-list ONE parent at depth 1 and\n // splice its children in place via `replaceChildren`, preserving the rest of the\n // tree's expansion. NEVER falls back to a root refresh (that's the collapse).\n // Used to (a) reconcile an optimistic insert against the server's real\n // size/mtime, and (b) fold an EXTERNAL (agent) change into the tree. A reconcile\n // of a parent that isn't loaded (collapsed/unmounted) is a no-op — there's\n // nothing visible to update, and it re-lists fresh when the user expands it.\n const reconcilePath = useCallback(\n async (path: string) => {\n if (!sessionId) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n // Skip parents that aren't currently loaded/expanded in the tree (nothing\n // visible to update; they re-list fresh on the next expand).\n if (!parentIsLoaded(treeRef.current, path)) return;\n try {\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path, depth: 1 },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n if (path === \"\") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));\n else\n setTree((prev) => {\n const existing = findNodeByPath(prev, path);\n const merged = existing?.children\n ? mergeChildren(existing.children, children)\n : children;\n return applyStatus(replaceChildren(prev, path, merged));\n });\n } catch {\n // A failed reconcile is non-fatal: the optimistic state stands. We never\n // root-refresh here (that would collapse the tree the user is working in).\n }\n },\n [client, workspaceId, sessionId, applyStatus],\n );\n\n // Run a Channel-A op behind an OPTIMISTIC tree edit. `apply` splices the change\n // in immediately (preserving expansion/selection); the op runs in the\n // background; on failure we revert to the pre-op snapshot and surface a toast.\n // On success we keep the optimistic state and (optionally) reconcile the\n // affected parent(s) to pick up the server's real size/revision — NEVER a full\n // refresh. The returned promise resolves/rejects with the op so callers (the\n // editor save, inline rename) can still await it.\n const runOptimistic = useCallback(\n async <T>(\n opName: string,\n apply: (nodes: FileTreeNode[]) => FileTreeNode[],\n op: () => Promise<T>,\n reconcileParents: string[],\n ): Promise<T> => {\n const identityGeneration = identityGenerationRef.current;\n // Snapshot the pre-op tree from the ref (StrictMode-safe — see treeRef).\n const snapshot = treeRef.current;\n setError(null);\n setTree(applyStatus(apply(snapshot)));\n try {\n const res = await op();\n if (identityGenerationRef.current !== identityGeneration) return res;\n // Remember the revision WE caused so the matching fs.changed echo is\n // ignored by the event effect (no self-triggered refresh).\n if (res && typeof res === \"object\" && \"revision\" in res) {\n const rev = (res as { revision?: unknown }).revision;\n if (typeof rev === \"number\") ownRevisionsRef.current.add(rev);\n }\n // Reconcile loaded parents to fold the server's real metadata. Sequential\n // and best-effort — purely cosmetic over the already-correct optimistic UI.\n for (const parent of reconcileParents) {\n if (identityGenerationRef.current !== identityGeneration) return res;\n await reconcilePath(parent);\n }\n return res;\n } catch (cause) {\n const err = cause instanceof Error ? cause : new Error(String(cause));\n if (identityGenerationRef.current !== identityGeneration) throw err;\n // Revert the optimistic edit to the exact pre-op tree.\n setTree(applyStatus(snapshot));\n // An expected-content conflict belongs to the editor's explicit\n // Reload/Overwrite decision surface. Treating it as a broken file tree\n // would also raise a duplicate host toast and leave a stale global error\n // after the user successfully resolves the conflict.\n if (err instanceof FileWriteConflictError) throw err;\n setError(err);\n onMutationError?.(err, opName);\n throw err;\n }\n },\n [applyStatus, reconcilePath, onMutationError],\n );\n\n const writeFile = useCallback(\n async (\n path: string,\n content: string,\n writeOptions: SandboxWriteFileOptions = {},\n ): Promise<FsWriteResponse> => {\n if (!sessionId) throw new Error(\"no session\");\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n const parent = parentOf(path);\n // Splice a new file node in immediately ONLY when the path doesn't already\n // exist in a loaded parent (an editor SAVE to an existing file mutates no\n // tree shape — just content — so it needs no optimistic node, no reconcile).\n const exists = Boolean(findNodeByPath(tree, path));\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"file\", size: content.length };\n return await runOptimistic(\n \"write\",\n (nodes) => (exists ? nodes : insertNode(nodes, parent, node)),\n async () => {\n if (!writeOptions.force && writeOptions.expectedContent !== undefined) {\n const live = await client.fsRead(\n workspaceId,\n sessionId,\n { path },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) {\n throw new Error(\"File save cancelled because the workspace changed.\");\n }\n const liveContent =\n live.encoding === \"base64\" ? decodeBase64Text(live.content) : live.content;\n if (live.truncated || live.isBinary || liveContent !== writeOptions.expectedContent) {\n throw new FileWriteConflictError(path, writeOptions.expectedContent, liveContent);\n }\n }\n if (identityGenerationRef.current !== identityGeneration) {\n throw new Error(\"File save cancelled because the workspace changed.\");\n }\n return await client.fsWrite(workspaceId, sessionId, {\n path,\n content,\n overwrite: true,\n });\n },\n exists ? [] : [parent],\n );\n },\n [client, workspaceId, sessionId, tree, runOptimistic],\n );\n\n const createFile = useCallback(\n async (path: string): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const parent = parentOf(path);\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"file\", size: 0 };\n await runOptimistic(\n \"create file\",\n (nodes) => insertNode(nodes, parent, node),\n () => client.fsWrite(workspaceId, sessionId, { path, content: \"\", overwrite: false }),\n [parent],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const createDir = useCallback(\n async (path: string): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const parent = parentOf(path);\n // A freshly-created dir is empty + expanded: children:[] (not undefined, so\n // it doesn't show the lazy-expand marker over a dir we KNOW is empty).\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"dir\", children: [] };\n await runOptimistic(\n \"create folder\",\n (nodes) => insertNode(nodes, parent, node),\n () => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),\n [parent],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const deleteEntry = useCallback(\n async (path: string, recursive = false): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n await runOptimistic(\n \"delete\",\n (nodes) => removeNode(nodes, path),\n () => client.fsDelete(workspaceId, sessionId, { path, recursive }),\n [parentOf(path)],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const moveEntry = useCallback(\n async (path: string, newPath: string, opts?: { overwrite?: boolean }): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const from = parentOf(path);\n const to = parentOf(newPath);\n await runOptimistic(\n \"move\",\n (nodes) => {\n const moving = findNodeByPath(nodes, path);\n if (!moving) return nodes; // not loaded — let the reconcile pick it up\n // Re-path the moved subtree, drop it from its old parent, splice into new.\n const moved = repathNode(moving, path, newPath);\n return insertNode(removeNode(nodes, path), to, moved);\n },\n () =>\n client.fsMove(workspaceId, sessionId, {\n path,\n newPath,\n overwrite: opts?.overwrite ?? false,\n }),\n to === from ? [from] : [from, to],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n // Initial paint + reset on identity change. Source selection:\n // • any box WITH a capture → paint instantly from the durable capture index;\n // a warm/draining box then reconciles live in place.\n // • if that live reconciliation fails, keep the capture visible and read-only.\n // • cold box with NO capture → best-effort live list (status quo — never worse).\n // Key the seed on the capture's REVISION (a primitive), not the manifest object\n // — a consumer passing a fresh object each render (or a new revision) must not\n // spin the effect. The latest manifest is read from a ref at run time.\n const captureRef = useRef<WorkspaceCaptureManifest | null>(capture);\n captureRef.current = capture;\n const captureRevision = capture?.revision ?? null;\n const previousIdentityRef = useRef(identityKey);\n useEffect(() => {\n // Any source-selection change supersedes an older root refresh. A true data\n // identity change additionally fences all other async tree work and resets\n // event/debounce state before the event-folding effect runs.\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged || !enabled) {\n identityAbortRef.current.abort();\n identityAbortRef.current = new AbortController();\n identityGenerationRef.current += 1;\n lastSeqRef.current = 0;\n pendingParentsRef.current = new Set();\n pendingGitRef.current = false;\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n ownRevisionsRef.current = new Set();\n statusRef.current = new Map();\n treeRef.current = [];\n setTree([]);\n setStateIdentity(identityKey);\n setExpandingPaths(new Set());\n sourceRef.current = null;\n setSource(null);\n setLoading(false);\n setError(null);\n }\n if (!enabled) {\n return;\n }\n const currentCapture = captureRevision !== null ? captureRef.current : null;\n if (currentCapture && (identityChanged || !isLive || sourceRef.current !== \"live\")) {\n seedFromCapture(currentCapture);\n }\n if (isLive || !currentCapture) {\n void refresh();\n }\n return () => {\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n };\n }, [enabled, isLive, captureRevision, identityKey, refresh, seedFromCapture]);\n\n // Re-pull JUST the git-status overlay and re-tint the existing tree in place —\n // no fs re-list, no collapse. This is all a `git.changed` (commit/stage/checkout)\n // needs: the tree SHAPE is unchanged, only the tints move.\n const refreshGitOverlay = useCallback(async () => {\n if (!sessionId) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n try {\n const status = await client.gitStatus(\n workspaceId,\n sessionId,\n { path: rootPath },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of status.files) {\n const code = file.worktree ?? file.index;\n const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n setTree((prev) => applyStatus(prev));\n } catch {\n /* a non-repo box has no overlay — leave the tree untinted */\n }\n }, [client, workspaceId, sessionId, rootPath, applyStatus]);\n\n // Auto-reconcile on fs/git change notifications — TARGETED, never a root\n // collapse-reload, and de-duped against our OWN mutations.\n //\n // • Our own ops (`source:\"write\"`, or a revision WE caused) are IGNORED —\n // the optimistic edit already reflects them, so a refresh here would be the\n // pointless 5s collapse the user reported.\n // • An EXTERNAL fs.changed (the agent writing files: `source:\"agent\"`/`watch`)\n // reconciles ONLY the affected parent directories (in place, expansion\n // preserved). Bursts are debounced into a single reconcile pass.\n // • A git.changed just re-tints (refreshes the status overlay) — no fs re-list.\n const events = options.events;\n\n useEffect(() => {\n if (!enabled || !events) return;\n let sawNew = false;\n for (const event of events) {\n if (event.sequence <= lastSeqRef.current) continue;\n if (event.type === \"fs.changed\") {\n sawNew = true;\n const payload = event.payload as FsChangedPayload | null;\n if (!payload || typeof payload !== \"object\") continue;\n // Suppress our own writes: the optimistic tree already shows them.\n if (payload.source === \"write\") continue;\n if (typeof payload.revision === \"number\" && ownRevisionsRef.current.has(payload.revision))\n continue;\n for (const change of payload.changes ?? []) {\n pendingParentsRef.current.add(parentOf(change.path));\n if (change.oldPath) pendingParentsRef.current.add(parentOf(change.oldPath));\n }\n } else if (event.type === \"git.changed\") {\n sawNew = true;\n const payload = event.payload as GitChangedPayload | null;\n // A git.changed our own write triggered (commit/stage from the agent is\n // external; a checkout we caused isn't — but we don't cause git ops here,\n // so any git.changed is external) → re-tint.\n if (\n payload &&\n typeof payload === \"object\" &&\n typeof payload.revision === \"number\" &&\n ownRevisionsRef.current.has(payload.revision)\n )\n continue;\n pendingGitRef.current = true;\n }\n }\n // Advance the high-water mark past everything we've folded.\n for (const event of events)\n if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;\n if (!sawNew) return;\n\n if (debounceRef.current) clearTimeout(debounceRef.current);\n const identityGeneration = identityGenerationRef.current;\n debounceRef.current = setTimeout(() => {\n debounceRef.current = null;\n if (identityGenerationRef.current !== identityGeneration) return;\n const parents = pendingParentsRef.current;\n pendingParentsRef.current = new Set();\n const wantGit = pendingGitRef.current;\n pendingGitRef.current = false;\n // Reconcile the changed directories (in place). A git change re-tints first\n // so the freshly-listed nodes get the correct overlay.\n void (async () => {\n if (wantGit) await refreshGitOverlay();\n if (identityGenerationRef.current !== identityGeneration) return;\n for (const parent of parents) {\n if (identityGenerationRef.current !== identityGeneration) return;\n await reconcilePath(parent);\n }\n })();\n }, 150);\n }, [enabled, events, reconcilePath, refreshGitOverlay]);\n\n useEffect(\n () => () => {\n refreshAbortRef.current?.abort();\n identityAbortRef.current.abort();\n identityGenerationRef.current += 1;\n if (debounceRef.current) clearTimeout(debounceRef.current);\n },\n [],\n );\n\n const identityMatches = enabled && stateIdentity === identityKey;\n return {\n tree: identityMatches ? tree : [],\n expand,\n expandingPaths: identityMatches ? expandingPaths : new Set<string>(),\n readFile,\n writeFile,\n createFile,\n createDir,\n deleteEntry,\n moveEntry,\n refresh,\n source: identityMatches ? source : null,\n capturedAt: identityMatches && source === \"capture\" ? (capture?.capturedAt ?? null) : null,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n };\n}\n","import type {\n GitFileDiff,\n SessionEvent,\n WorkspaceCaptureManifest,\n WorkspaceCaptureRepo,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\nexport type UseSandboxGitOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh\n * on `git.changed`. */\n events?: SessionEvent[] | undefined;\n /** Repo root within the workspace (multi-repo). Default: workspace root. */\n repoPath?: string | undefined;\n /** All repository roots in the workspace. When supplied, status and diff are\n * aggregated into one workspace-wide result and paths are workspace-relative. */\n repoPaths?: readonly string[] | undefined;\n /** Diff the staged index vs HEAD (`--cached`) instead of the working tree. */\n staged?: boolean | undefined;\n /** Hold off the initial fetch. Default true. */\n enabled?: boolean | undefined;\n /** Lease liveness (\"warm\" | \"draining\" | \"cold\"). When NOT warm, the diff is\n * served from the capture (cold/offline) instead of a live `gitDiff` RPC. */\n liveness?: string | undefined;\n /** The latest turn-end capture (from `useWorkspaceCapture`). Seeds the diff\n * immediately; a warm box reconciles live and leaves this durable review\n * surface in place if the live request is temporarily unavailable. */\n capture?: WorkspaceCaptureManifest | null | undefined;\n};\n\n/** A Git diff qualified into workspace scope. `repoRoot` is present for the\n * workspace-wide path and lets review UI group real repositories without\n * guessing from the first path segment. */\nexport type SandboxGitFileDiff = GitFileDiff & { repoRoot?: string | undefined };\n\nexport type UseSandboxGitResult = {\n /** Working-tree (or staged) diff vs HEAD — the structured hunks the Pierre\n * diff view renders. */\n diff: SandboxGitFileDiff[];\n branch: string | null;\n /** Whether a repo is actually mounted (drives \"no repository\" vs \"no changes\"). */\n isRepo: boolean;\n ahead: number;\n behind: number;\n /** Number and workspace-relative roots of repositories represented here. */\n repoCount: number;\n repoRoots: string[];\n refresh: () => Promise<void>;\n /** Which source the diff is served from: the live box or the turn-end capture. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n loading: boolean;\n error: Error | null;\n};\n\n/** Normalize a repo root for matching (\"\" and \".\" both mean the workspace root). */\nfunction normalizeRoot(root: string): string {\n const normalized = root\n .replaceAll(\"\\\\\", \"/\")\n .replace(/^\\.\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return normalized === \".\" ? \"\" : normalized;\n}\n\nfunction qualifyPath(repoRoot: string, path: string): string {\n const root = normalizeRoot(repoRoot);\n return root ? `${root}/${path}` : path;\n}\n\nfunction qualifyDiff(repoRoot: string, file: GitFileDiff): SandboxGitFileDiff {\n const root = normalizeRoot(repoRoot);\n return {\n ...file,\n path: qualifyPath(root, file.path),\n oldPath: file.oldPath === null ? null : qualifyPath(root, file.oldPath),\n repoRoot: root,\n };\n}\n\nfunction uniqueRoots(roots: readonly string[]): string[] {\n return [...new Set(roots.map(normalizeRoot))].sort((a, b) => a.localeCompare(b));\n}\n\n/** Find the capture repo matching `repoPath` (default workspace root). */\nfunction repoForPath(\n manifest: WorkspaceCaptureManifest,\n repoPath: string,\n): WorkspaceCaptureRepo | null {\n const want = normalizeRoot(repoPath);\n const exact = manifest.repos.find((r) => normalizeRoot(r.root) === want);\n if (exact) return exact;\n // Root-scoped hook against a single-repo capture whose root isn't literally \"\":\n // fall back to the sole repo so the common single-repo case just works.\n if (want === \"\" && manifest.repos.length === 1) return manifest.repos[0] ?? null;\n return null;\n}\n\n/** Full diff equality for identity preservation. Hunk count/count summaries are\n * not sufficient: an edit can replace text without changing either shape. */\nfunction sameFileDiff(a: SandboxGitFileDiff, b: SandboxGitFileDiff): boolean {\n if (\n a.path !== b.path ||\n a.oldPath !== b.oldPath ||\n a.repoRoot !== b.repoRoot ||\n a.status !== b.status ||\n a.isBinary !== b.isBinary ||\n a.isImage !== b.isImage ||\n a.additions !== b.additions ||\n a.deletions !== b.deletions ||\n a.truncated !== b.truncated ||\n a.hunks.length !== b.hunks.length\n )\n return false;\n\n return a.hunks.every((hunk, hunkIndex) => {\n const other = b.hunks[hunkIndex];\n if (\n !other ||\n hunk.oldStart !== other.oldStart ||\n hunk.oldLines !== other.oldLines ||\n hunk.newStart !== other.newStart ||\n hunk.newLines !== other.newLines ||\n hunk.header !== other.header ||\n hunk.lines.length !== other.lines.length\n )\n return false;\n return hunk.lines.every((line, lineIndex) => {\n const otherLine = other.lines[lineIndex];\n return (\n otherLine !== undefined &&\n line.type === otherLine.type &&\n line.oldNo === otherLine.oldNo &&\n line.newNo === otherLine.newNo &&\n line.text === otherLine.text\n );\n });\n });\n}\n\n/** Merge a freshly-served diff over the current one, preserving the identity of\n * genuinely unchanged per-file entries so a cold→warm reconcile does NOT\n * remount unchanged file sections — no-flicker (§12-D1). */\nfunction mergeDiffs(\n current: SandboxGitFileDiff[],\n next: SandboxGitFileDiff[],\n): SandboxGitFileDiff[] {\n if (current.length === 0) return next;\n const byPath = new Map(current.map((d) => [d.path, d] as const));\n let changed = current.length !== next.length;\n const merged = next.map((file, index) => {\n const existing = byPath.get(file.path);\n if (existing && sameFileDiff(existing, file)) {\n if (current[index] !== existing) changed = true;\n return existing;\n }\n changed = true;\n return file;\n });\n return changed ? merged : current;\n}\n\n/**\n * Project the Git service into the Pierre diff data contract: structured\n * `GitFileDiff[]` (per-file hunks with per-line old/new numbers, rename\n * detection, binary flag, add/del counts) plus branch + ahead/behind. When the\n * box is warm the `git diff` runs in-box (API-direct); when it is cold/offline the\n * diff is served from the turn-end capture instead. Refreshes on\n * `git.changed`; reconciles live in place on the cold→warm transition.\n */\nexport function useSandboxGit(\n sessionId: string | null | undefined,\n options: UseSandboxGitOptions = {},\n): UseSandboxGitResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const repoPath = options.repoPath ?? \"\";\n const workspaceWide = options.repoPaths !== undefined;\n // Serialize the caller's array so inline values do not retrigger effects. An\n // explicitly empty advertised list falls back to the legacy repoPath probe.\n const repoPathsKey = uniqueRoots(\n options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths : [repoPath],\n ).join(\"\\u0000\");\n const repoPaths = useMemo(() => repoPathsKey.split(\"\\u0000\"), [repoPathsKey]);\n const staged = options.staged ?? false;\n const capture = options.capture ?? null;\n const isLive = options.liveness === \"warm\" || options.liveness === \"draining\";\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${repoPathsKey}\\u0000${workspaceWide}\\u0000${staged}`;\n\n const [diff, setDiff] = useState<SandboxGitFileDiff[]>([]);\n const [branch, setBranch] = useState<string | null>(null);\n const [isRepo, setIsRepo] = useState(false);\n const [ahead, setAhead] = useState(0);\n const [behind, setBehind] = useState(0);\n const [repoRoots, setRepoRoots] = useState<string[]>([]);\n const [source, setSource] = useState<\"live\" | \"capture\" | null>(null);\n const sourceRef = useRef<\"live\" | \"capture\" | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Live requests and event cursors are identity-scoped. The refs deliberately\n // survive renders, so they must be fenced/reset when that identity changes.\n const refreshGenerationRef = useRef(0);\n const refreshAbortRef = useRef<AbortController | null>(null);\n const lastChangeRef = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n refreshAbortRef.current?.abort();\n const refreshAbort = new AbortController();\n refreshAbortRef.current = refreshAbort;\n const generation = (refreshGenerationRef.current += 1);\n setLoading(true);\n setError(null);\n try {\n const statuses = await Promise.all(\n repoPaths.map(async (root) => ({\n root,\n status: await client.gitStatus(\n workspaceId,\n sessionId,\n { path: root },\n { signal: refreshAbort.signal },\n ),\n })),\n );\n if (refreshGenerationRef.current !== generation) return;\n const repositories = statuses.filter(({ status }) => status.isRepo);\n const roots = repositories.map(({ root }) => root);\n setRepoRoots(roots);\n setIsRepo(repositories.length > 0);\n setBranch(repositories.length === 1 ? (repositories[0]?.status.head ?? null) : null);\n setAhead(repositories.reduce((sum, { status }) => sum + status.ahead, 0));\n setBehind(repositories.reduce((sum, { status }) => sum + status.behind, 0));\n if (repositories.length === 0) {\n setDiff([]);\n sourceRef.current = \"live\";\n setSource(\"live\");\n return;\n }\n const results = await Promise.all(\n repositories.map(async ({ root }) => ({\n root,\n result: await client.gitDiff(\n workspaceId,\n sessionId,\n {\n path: root,\n staged,\n includeUntracked: !staged,\n },\n { signal: refreshAbort.signal },\n ),\n })),\n );\n if (refreshGenerationRef.current !== generation) return;\n // Merge in place so the cold→warm swap keeps unchanged file sections mounted.\n setDiff((prev) =>\n mergeDiffs(\n prev,\n results.flatMap(({ root, result }) =>\n workspaceWide ? result.files.map((file) => qualifyDiff(root, file)) : result.files,\n ),\n ),\n );\n sourceRef.current = \"live\";\n setSource(\"live\");\n } catch (cause) {\n if (refreshGenerationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (refreshGenerationRef.current === generation) setLoading(false);\n if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, repoPaths, staged, workspaceWide]);\n\n // Serve the diff from a capture (the cold/offline source). `staged` has no\n // meaning here: the capture records the combined turn-end change surface vs HEAD.\n const seedFromCapture = useCallback(\n (manifest: WorkspaceCaptureManifest) => {\n const repositories = workspaceWide\n ? manifest.repos\n : ([repoForPath(manifest, repoPath)].filter(Boolean) as WorkspaceCaptureRepo[]);\n if (repositories.length === 0) {\n setIsRepo(false);\n setBranch(null);\n setAhead(0);\n setBehind(0);\n setRepoRoots([]);\n setDiff((prev) => (prev.length === 0 ? prev : []));\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n return;\n }\n setIsRepo(true);\n setBranch(repositories.length === 1 ? (repositories[0]?.head ?? null) : null);\n setAhead(repositories.reduce((sum, repo) => sum + repo.ahead, 0));\n setBehind(repositories.reduce((sum, repo) => sum + repo.behind, 0));\n setRepoRoots(repositories.map((repo) => normalizeRoot(repo.root)));\n setDiff((prev) =>\n mergeDiffs(\n prev,\n repositories.flatMap((repo) =>\n workspaceWide ? repo.diff.map((file) => qualifyDiff(repo.root, file)) : repo.diff,\n ),\n ),\n );\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n setError(null);\n },\n [repoPath, workspaceWide],\n );\n\n // Source selection on mount / liveness / capture-revision change. Key on the\n // capture REVISION (a primitive), not the manifest object, so a fresh object per\n // render can't spin the effect; the latest manifest is read from a ref.\n const captureRef = useRef<WorkspaceCaptureManifest | null>(capture);\n captureRef.current = capture;\n const captureRevision = capture?.revision ?? null;\n const previousIdentityRef = useRef(identityKey);\n useEffect(() => {\n // A liveness/capture source change supersedes any older live request. Only a\n // true data-identity change clears rendered state and the event high-water mark.\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged || !enabled) {\n lastChangeRef.current = 0;\n setDiff([]);\n setStateIdentity(identityKey);\n setIsRepo(false);\n setBranch(null);\n setAhead(0);\n setBehind(0);\n setRepoRoots([]);\n sourceRef.current = null;\n setSource(null);\n setLoading(false);\n setError(null);\n }\n if (!enabled) {\n return;\n }\n const currentCapture = captureRevision !== null ? captureRef.current : null;\n if (currentCapture && (identityChanged || !isLive || sourceRef.current !== \"live\")) {\n seedFromCapture(currentCapture);\n }\n if (isLive || !currentCapture) {\n void refresh();\n }\n return () => {\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n };\n }, [enabled, isLive, captureRevision, identityKey, refresh, seedFromCapture]);\n\n // git.changed → re-fetch the LIVE diff. A git.changed only originates from a\n // live box, so this both keeps warm sessions fresh and folds a cold box that\n // just came up (unchanged from the pre-capture behavior).\n const events = options.events;\n useEffect(() => {\n if (!enabled || !events) return;\n let latest = lastChangeRef.current;\n for (const event of events) {\n if (event.type === \"git.changed\" && event.sequence > latest) {\n latest = event.sequence;\n }\n }\n if (latest > lastChangeRef.current) {\n lastChangeRef.current = latest;\n void refresh();\n }\n }, [enabled, events, refresh]);\n\n const identityMatches = enabled && stateIdentity === identityKey;\n const visibleRepoRoots = identityMatches ? repoRoots : [];\n return {\n diff: identityMatches ? diff : [],\n branch: identityMatches ? branch : null,\n isRepo: identityMatches && isRepo,\n ahead: identityMatches ? ahead : 0,\n behind: identityMatches ? behind : 0,\n repoCount: visibleRepoRoots.length,\n repoRoots: visibleRepoRoots,\n refresh,\n source: identityMatches ? source : null,\n capturedAt: identityMatches && source === \"capture\" ? (capture?.capturedAt ?? null) : null,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n };\n}\n","import type {\n GetWorkspaceCaptureResponse,\n SessionEvent,\n WorkspaceCaptureDegradedReason,\n WorkspaceCaptureManifest,\n WorkspaceRevisionCapturedPayload,\n WorkspaceRevisionDegradedPayload,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/** The announce event M1 emits at turn end (metadata only, never content). */\nconst REVISION_CAPTURED = \"workspace.revision.captured\";\nconst REVISION_DEGRADED = \"workspace.revision.degraded\";\nconst MANIFEST_FETCH_TIMEOUT_MS = 15_000;\n\ntype AvailableCaptureResponse = Extract<GetWorkspaceCaptureResponse, { available: true }>;\n\nconst CAPTURE_STATS_NUMBER_FIELDS = [\n \"repoCount\",\n \"fileCount\",\n \"additions\",\n \"deletions\",\n \"totalBytes\",\n \"tooLargeCount\",\n \"binaryCount\",\n \"treeEntryCount\",\n \"durationMs\",\n] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Signed manifests bypass the authenticated JSON client's wire decoder. Treat\n * the blob as untrusted until its outer schema and exact revision identity match\n * the metadata response that minted the URL. The API performs the full contract\n * parse; this client-side boundary prevents a corrupt/mis-keyed response from\n * being presented as a different authoritative capture.\n */\nfunction captureManifestForResponse(\n value: unknown,\n response: AvailableCaptureResponse,\n): WorkspaceCaptureManifest {\n if (\n !isRecord(value) ||\n value.version !== 1 ||\n !Number.isSafeInteger(value.revision) ||\n typeof value.capturedAt !== \"string\" ||\n !(value.turnId === null || typeof value.turnId === \"string\") ||\n !Number.isSafeInteger(value.leaseEpoch) ||\n !isRecord(value.treeIndex) ||\n typeof value.treeTruncated !== \"boolean\" ||\n !Array.isArray(value.repos) ||\n !Array.isArray(value.files) ||\n !isRecord(value.stats)\n ) {\n throw new Error(\"Workspace capture manifest failed validation.\");\n }\n\n if (\n value.revision !== response.revision ||\n value.capturedAt !== response.capturedAt ||\n value.turnId !== response.turnId ||\n value.leaseEpoch !== response.leaseEpoch\n ) {\n throw new Error(\"Workspace capture manifest identity did not match its response.\");\n }\n\n for (const field of CAPTURE_STATS_NUMBER_FIELDS) {\n if (!Number.isSafeInteger(value.stats[field]) || value.stats[field] !== response.stats[field]) {\n throw new Error(\"Workspace capture manifest statistics did not match its response.\");\n }\n }\n if (\n typeof value.stats.treeTruncated !== \"boolean\" ||\n value.stats.treeTruncated !== response.stats.treeTruncated ||\n (value.stats.fingerprint ?? null) !== (response.stats.fingerprint ?? null) ||\n value.repos.length !== response.stats.repoCount ||\n value.files.length !== response.stats.fileCount ||\n value.treeTruncated !== response.stats.treeTruncated\n ) {\n throw new Error(\"Workspace capture manifest statistics did not match its response.\");\n }\n\n return value as WorkspaceCaptureManifest;\n}\n\nexport type UseWorkspaceCaptureOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — a\n * `workspace.revision.captured` for a NEWER revision refreshes the manifest. */\n events?: SessionEvent[] | undefined;\n /** Hold off the mount fetch (e.g. the workbench panel is collapsed). Default true. */\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceCaptureResult = {\n /** The resolved manifest (tree index + per-repo diffs + file refs), or null when\n * no capture exists (falls back to the live/wake path — status quo). */\n capture: WorkspaceCaptureManifest | null;\n /** The loaded capture's monotonic revision, or null when unavailable. */\n revision: number | null;\n /** When the loaded capture was taken (ISO), for the \"as of <time>\" source badge. */\n capturedAt: string | null;\n /** Whether a capture is available at all (the `{available:false}` discriminator). */\n available: boolean;\n /** Why the newest durable revision is unavailable. null means no capture has\n * been attempted yet; a value means capture explicitly failed closed and\n * consumers must use the live box rather than trust an incomplete snapshot. */\n degradedReason: WorkspaceCaptureDegradedReason | null;\n /** The changed-file count from the capture's stats, resolved on the FIRST GET\n * (from the response's top-level `stats`, before any manifest-URL hop). null\n * until that first resolve; 0 when no capture exists. This is the pre-paint\n * \"changes exist?\" signal the dock uses to pick its default tab with no\n * embedder events-at-mount contract. */\n fileCount: number | null;\n /** A newer revision has been ANNOUNCED than the one currently loaded (a refresh\n * is in flight or pending). M5's source badge can show a subtle \"updating…\". */\n isStale: boolean;\n loading: boolean;\n error: Error | null;\n /** Force a re-fetch of the latest capture. */\n refresh: () => Promise<void>;\n};\n\n/**\n * The cold-paint data source: fetch the latest turn-end workspace capture with a\n * SINGLE api round-trip on mount (no machine, no Channel-A — this is the <200ms\n * first paint). The manifest is served inline in the common\n * case; a rare >2MB manifest comes back as a short-TTL signed URL we follow.\n *\n * Subscribes to the live event log: a `workspace.revision.captured` for a revision\n * newer than the one loaded triggers a background refresh (stale-while-revalidate).\n * `{available:false}` is a value, never a crash — consumers fall back to the\n * live/wake path exactly as before the capture feature existed.\n */\nexport function useWorkspaceCapture(\n sessionId: string | null | undefined,\n options: UseWorkspaceCaptureOptions = {},\n): UseWorkspaceCaptureResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}`;\n\n const [capture, setCapture] = useState<WorkspaceCaptureManifest | null>(null);\n const [available, setAvailable] = useState(false);\n const [degradedReason, setDegradedReason] = useState<WorkspaceCaptureDegradedReason | null>(null);\n // Resolved from the FIRST GET's top-level stats (before any manifest-URL hop),\n // so the dock can pick its default tab as early as possible. null until then.\n const [fileCount, setFileCount] = useState<number | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n // The highest revision ANNOUNCED on the event log — compared against the loaded\n // manifest's revision to compute `isStale` and to gate refreshes.\n const [announcedRevision, setAnnouncedRevision] = useState<number | null>(null);\n // State updates are asynchronous, so the previous session's capture still\n // exists during the first render after an identity switch. Tag it and hide it\n // synchronously until the new identity's request owns the state.\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n\n // A generation counter fences a slow in-flight fetch against a newer one (or an\n // identity change) so a stale response can never overwrite fresher state.\n const generationRef = useRef(0);\n const requestAbortRef = useRef<AbortController | null>(null);\n // Event sequence numbers are scoped to a session. Keeping this cursor across an\n // identity switch would discard the new session's low-numbered announcements.\n const lastSeqRef = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n requestAbortRef.current?.abort();\n const requestAbort = new AbortController();\n requestAbortRef.current = requestAbort;\n const generation = (generationRef.current += 1);\n setStateIdentity(identityKey);\n setLoading(true);\n setError(null);\n try {\n // A signed URL can expire between mint and GET. Re-enter the authenticated\n // endpoint once to mint a fresh URL; never replay or log the old credential.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const res: GetWorkspaceCaptureResponse = await client.getWorkspaceCapture(\n workspaceId,\n sessionId,\n { signal: requestAbort.signal },\n );\n if (generationRef.current !== generation) return;\n if (!res.available) {\n setCapture(null);\n setAvailable(false);\n setFileCount(0);\n setDegradedReason(res.degradedReason ?? null);\n return;\n }\n setDegradedReason(null);\n // Resolve the changed-file count immediately from the response's stats — the\n // default-tab signal must not wait on a >2MB manifest-URL hop.\n setFileCount(res.stats.fileCount);\n // Exactly one of manifest / manifestUrl is non-null (M2 contract). The inline\n // manifest is the <200ms common case; a >2MB manifest is a signed URL hop.\n let manifestValue: unknown = res.manifest;\n if (!manifestValue && res.manifestUrl) {\n const response = await fetch(res.manifestUrl.url, {\n signal: AbortSignal.any([\n requestAbort.signal,\n AbortSignal.timeout(MANIFEST_FETCH_TIMEOUT_MS),\n ]),\n credentials: \"omit\",\n cache: \"no-store\",\n referrerPolicy: \"no-referrer\",\n }).catch(() => null);\n if (generationRef.current !== generation) return;\n if (!response?.ok) {\n if (\n attempt === 0 &&\n (response === null || response.status === 401 || response.status === 403)\n ) {\n continue;\n }\n throw new Error(\n `Workspace capture manifest download failed${response ? ` (${response.status})` : \"\"}.`,\n );\n }\n try {\n manifestValue = await response.json();\n } catch {\n throw new Error(\"Workspace capture manifest was not valid JSON.\");\n }\n if (generationRef.current !== generation) return;\n }\n if (!manifestValue) {\n throw new Error(\"Workspace capture response did not include a manifest.\");\n }\n const manifest = captureManifestForResponse(manifestValue, res);\n setCapture(manifest);\n setAvailable(true);\n // Fold the served revision into the announced high-water mark so a capture we\n // JUST loaded is never reported stale against an older announce.\n setAnnouncedRevision((prev) =>\n prev === null || manifest.revision > prev ? manifest.revision : prev,\n );\n return;\n }\n throw new Error(\"Workspace capture manifest download failed after refreshing its URL.\");\n } catch (cause) {\n if (generationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (generationRef.current === generation) setLoading(false);\n if (requestAbortRef.current === requestAbort) requestAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, identityKey]);\n\n // Mount fetch + reset on identity change.\n useEffect(() => {\n // Invalidate a request started for the previous identity before clearing its\n // state. The cleanup also fences a response that arrives after unmount.\n requestAbortRef.current?.abort();\n requestAbortRef.current = null;\n generationRef.current += 1;\n lastSeqRef.current = 0;\n setCapture(null);\n setAvailable(false);\n setDegradedReason(null);\n setFileCount(null);\n setAnnouncedRevision(null);\n setError(null);\n if (!enabled) {\n setLoading(false);\n return;\n }\n void refresh();\n return () => {\n requestAbortRef.current?.abort();\n requestAbortRef.current = null;\n generationRef.current += 1;\n };\n }, [enabled, refresh]);\n\n // Fold `workspace.revision.captured` announcements. A revision NEWER than the one\n // loaded bumps the announced high-water mark (→ isStale) and refreshes in the\n // background. The event is announce-only (metadata) — we still re-fetch the\n // manifest, since the payload carries no content.\n const events = options.events;\n useEffect(() => {\n if (!enabled || !events) return;\n let newest: number | null = null;\n for (const event of events) {\n if (event.sequence <= lastSeqRef.current) continue;\n if (event.type === REVISION_CAPTURED) {\n const payload = event.payload as WorkspaceRevisionCapturedPayload | null;\n if (payload && typeof payload === \"object\" && typeof payload.revision === \"number\") {\n newest = newest === null ? payload.revision : Math.max(newest, payload.revision);\n }\n } else if (event.type === REVISION_DEGRADED) {\n const payload = event.payload as WorkspaceRevisionDegradedPayload | null;\n if (payload && typeof payload === \"object\" && typeof payload.revision === \"number\") {\n newest = newest === null ? payload.revision : Math.max(newest, payload.revision);\n }\n }\n }\n for (const event of events)\n if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;\n if (newest === null) return;\n setAnnouncedRevision((prev) => (prev === null || newest > prev ? newest : prev));\n }, [enabled, events]);\n\n // A newer announced revision than the loaded manifest → refresh in the background.\n const identityMatches = enabled && stateIdentity === identityKey;\n const visibleCapture = identityMatches ? capture : null;\n const visibleAnnouncedRevision = identityMatches ? announcedRevision : null;\n const loadedRevision = visibleCapture?.revision ?? null;\n useEffect(() => {\n if (!enabled) return;\n if (visibleAnnouncedRevision === null) return;\n if (loadedRevision !== null && visibleAnnouncedRevision <= loadedRevision) return;\n // The mount request is already resolving the latest revision. Do not abort\n // and replace that exact identity-equivalent read merely because the event\n // tail announced a revision before the response arrived. When the request\n // settles, its state update re-runs this effect and a genuinely newer\n // announcement still refreshes immediately.\n if (requestAbortRef.current) return;\n // A newer capture exists than the one we hold (or we hold none) — pull it.\n void refresh();\n }, [enabled, visibleAnnouncedRevision, loadedRevision, refresh]);\n\n const isStale =\n loadedRevision !== null &&\n visibleAnnouncedRevision !== null &&\n visibleAnnouncedRevision > loadedRevision;\n\n return {\n capture: visibleCapture,\n revision: loadedRevision,\n capturedAt: visibleCapture?.capturedAt ?? null,\n available: identityMatches && available,\n degradedReason: identityMatches ? degradedReason : null,\n fileCount: identityMatches ? fileCount : null,\n isStale,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n refresh,\n };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/**\n * The wake-on-edit state machine. This hook owns the logic and the UI renders\n * its result. States are explicit and exhaustive:\n *\n * viewing-cold — cold snapshot open, no local edits (editable, cloud).\n * buffering — local edits buffered; the box is not warm and no warm is\n * yet in flight (the host has not attached).\n * warming — edits buffered; the box is actively coming up (the host\n * attached a viewer in response to `wantsWarm`).\n * flushing — the box is warm; the guarded write is in flight.\n * flushed — the buffer was written to the live box.\n * conflict — the live file diverged from the captured base between\n * capture and flush; NO write was issued (C2).\n * readonly-offline — a self-hosted machine that is offline; read-only, no wake.\n */\nexport type WorkspaceEditState =\n | \"viewing-cold\"\n | \"buffering\"\n | \"warming\"\n | \"flushing\"\n | \"flushed\"\n | \"conflict\"\n | \"readonly-offline\";\n\nexport type WorkspaceEditConflict = {\n path: string;\n /** The capture-served content the edit was based on. */\n base: string;\n /** The live content found on the box at flush time (what would be overwritten). */\n live: string;\n};\n\nexport type UseWorkspaceEditOptions = ClientOverride & {\n /** The file being edited (workspace-relative). null disables the machine. */\n path?: string | null | undefined;\n /** The capture-served content loaded into the editor — the flush base (C2). */\n baseContent?: string | null | undefined;\n /** `capabilities.liveness` — \"warm\" | \"draining\" | \"cold\". Drives the flush. */\n liveness?: string | undefined;\n /** True while the host is actively warming the box (attach in flight, not warm\n * yet) — lifts `buffering` → `warming`. The host sets this after acting on\n * `wantsWarm` (e.g. flipping `attachFiles` on `useSessionCapabilities`). */\n warming?: boolean | undefined;\n /** A self-hosted machine that is OFFLINE: read-only, no remote wake possible. */\n offline?: boolean | undefined;\n /** Called once when the first cold edit needs the box warmed — the host wires\n * this to its liveness primitive (flip `attachFiles`/`attachViewer`). */\n onWarmRequested?: (() => void) | undefined;\n};\n\nexport type UseWorkspaceEditResult = {\n state: WorkspaceEditState;\n /** The editor should be read-only (self-hosted offline). */\n readOnly: boolean;\n /** The current buffered content (null = no local edit yet). */\n buffer: string | null;\n /** The host should warm the box (flip its attach primitive). Latches true from\n * the first cold edit until the buffer flushes or is discarded. */\n wantsWarm: boolean;\n /** Record an edit from the editor (idempotent per identical content). */\n edit: (content: string) => void;\n /** The conflict detail when `state === \"conflict\"`. */\n conflict: WorkspaceEditConflict | null;\n /** Flush the buffer regardless of the base guard (the user chose \"overwrite\"\n * from the conflict bar). Writes last-writer-wins. */\n overwrite: () => Promise<void>;\n /** Drop the local buffer and return to viewing the snapshot. */\n discard: () => void;\n error: Error | null;\n};\n\nfunction isLive(liveness: string | undefined): boolean {\n return liveness === \"warm\" || liveness === \"draining\";\n}\n\n/**\n * Cold-editing without a spinner: the editor opens instantly from the capture,\n * the first keystroke buffers locally AND signals the host to warm the box, and on\n * warm the buffer flushes IF the live file still matches the captured base — else a\n * non-blocking conflict is surfaced and nothing is overwritten.\n *\n * The base guard compares the live content at flush time against `baseContent`\n * (the exact bytes the editor loaded). This is equivalent to the sha256 compare\n * the design specifies — M1 leaves `baseHash` null, so the loaded content IS the\n * authoritative base — and avoids async Web Crypto in the render path.\n */\nexport function useWorkspaceEdit(\n sessionId: string | null | undefined,\n options: UseWorkspaceEditOptions = {},\n): UseWorkspaceEditResult {\n const { client, workspaceId } = useOpenGeni(options);\n const path = options.path ?? null;\n const baseContent = options.baseContent ?? null;\n const live = isLive(options.liveness);\n const offline = options.offline === true;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${path ?? \"\"}`;\n\n const [buffer, setBuffer] = useState<string | null>(null);\n const [wantsWarm, setWantsWarm] = useState(false);\n const [flush, setFlush] = useState<\"idle\" | \"flushing\" | \"flushed\" | \"conflict\">(\"idle\");\n const [conflict, setConflict] = useState<WorkspaceEditConflict | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n\n const onWarmRequested = options.onWarmRequested;\n const bufferRef = useRef<string | null>(null);\n bufferRef.current = buffer;\n // Guards a single in-flight flush against re-entry (liveness can re-render).\n const flushingRef = useRef(false);\n const identityGenerationRef = useRef(0);\n const flushAttemptRef = useRef(0);\n const readAbortRef = useRef<AbortController | null>(null);\n\n // Reset the machine when the edited file (or session) changes.\n useEffect(() => {\n identityGenerationRef.current += 1;\n flushAttemptRef.current += 1;\n readAbortRef.current?.abort();\n readAbortRef.current = null;\n setStateIdentity(identityKey);\n setBuffer(null);\n setWantsWarm(false);\n setFlush(\"idle\");\n setConflict(null);\n setError(null);\n flushingRef.current = false;\n return () => {\n identityGenerationRef.current += 1;\n flushAttemptRef.current += 1;\n readAbortRef.current?.abort();\n readAbortRef.current = null;\n };\n }, [identityKey]);\n\n const edit = useCallback(\n (content: string) => {\n if (stateIdentity !== identityKey) return;\n if (offline) return; // read-only — no buffering, no wake.\n setBuffer((prev) => (prev === content ? prev : content));\n // Any new edit invalidates a prior flushed/conflict resolution.\n setFlush((prev) => (prev === \"flushing\" ? prev : \"idle\"));\n setConflict(null);\n if (!live) {\n // First cold edit signals the host to warm the box (once).\n setWantsWarm((prev) => {\n if (!prev) onWarmRequested?.();\n return true;\n });\n }\n },\n [offline, live, onWarmRequested, stateIdentity, identityKey],\n );\n\n const doFlush = useCallback(\n async (force: boolean) => {\n if (stateIdentity !== identityKey) return;\n if (!sessionId || !path) return;\n const content = bufferRef.current;\n if (content === null) return;\n if (flushingRef.current) return;\n const identityGeneration = identityGenerationRef.current;\n const flushAttempt = (flushAttemptRef.current += 1);\n flushingRef.current = true;\n setFlush(\"flushing\");\n setError(null);\n try {\n if (!force) {\n // Base guard: re-read the live file and compare to the captured base. A\n // divergence means the box changed under us — surface a conflict, write\n // NOTHING (C2).\n const readAbort = new AbortController();\n readAbortRef.current?.abort();\n readAbortRef.current = readAbort;\n const liveRead = await client.fsRead(\n workspaceId,\n sessionId,\n { path },\n { signal: readAbort.signal },\n );\n if (readAbortRef.current === readAbort) readAbortRef.current = null;\n if (identityGenerationRef.current !== identityGeneration) return;\n const liveContent = liveRead.content;\n if (baseContent !== null && liveContent !== baseContent) {\n setConflict({ path, base: baseContent, live: liveContent });\n setFlush(\"conflict\");\n return;\n }\n }\n await client.fsWrite(workspaceId, sessionId, { path, content, overwrite: true });\n if (identityGenerationRef.current !== identityGeneration) return;\n setConflict(null);\n setFlush(\"flushed\");\n setWantsWarm(false);\n } catch (cause) {\n if (identityGenerationRef.current !== identityGeneration) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n // Leave the buffer intact so the user can retry; fall back to buffering.\n setFlush(\"idle\");\n } finally {\n if (flushAttemptRef.current === flushAttempt) {\n flushingRef.current = false;\n readAbortRef.current = null;\n }\n }\n },\n [client, workspaceId, sessionId, path, baseContent, stateIdentity, identityKey],\n );\n\n // Auto-flush once the box is warm and a buffer is pending (the wake completed).\n useEffect(() => {\n if (offline) return;\n if (!live) return;\n if (buffer === null) return;\n if (flush === \"flushed\" || flush === \"conflict\" || flush === \"flushing\") return;\n void doFlush(false);\n }, [offline, live, buffer, flush, doFlush]);\n\n const overwrite = useCallback(async () => {\n await doFlush(true);\n }, [doFlush]);\n\n const discard = useCallback(() => {\n setBuffer(null);\n setWantsWarm(false);\n setFlush(\"idle\");\n setConflict(null);\n setError(null);\n }, []);\n\n // Derive the exhaustive public state.\n let state: WorkspaceEditState;\n if (offline) {\n state = \"readonly-offline\";\n } else if (flush === \"flushing\") {\n state = \"flushing\";\n } else if (flush === \"flushed\") {\n state = \"flushed\";\n } else if (flush === \"conflict\") {\n state = \"conflict\";\n } else if (buffer === null) {\n state = \"viewing-cold\";\n } else if (live) {\n // Warm with a pending buffer that hasn't entered flush yet — a transient tick\n // before the auto-flush effect runs.\n state = \"flushing\";\n } else if (options.warming === true) {\n state = \"warming\";\n } else {\n state = \"buffering\";\n }\n\n const identityMatches = stateIdentity === identityKey;\n return {\n state: identityMatches ? state : offline ? \"readonly-offline\" : \"viewing-cold\",\n readOnly: offline,\n buffer: identityMatches ? buffer : null,\n wantsWarm: identityMatches && wantsWarm,\n edit,\n conflict: identityMatches ? conflict : null,\n overwrite,\n discard,\n error: identityMatches ? error : null,\n };\n}\n","import { useMemo } from \"react\";\nimport type { MachineState } from \"@opengeni/sdk\";\nimport type { SessionCapabilitiesState } from \"./use-session-capabilities\";\n\n/** The one truthful machine indicator. Honest staleness beats\n * fake liveness: a cold/asleep box reads \"offline — as of <time>\", never \"live\". */\nexport type MachineChipState = \"live\" | \"waking\" | \"offline\";\n\nexport type MachineChip = {\n state: MachineChipState;\n /** A ready-to-render label (\"Live\" / \"Waking…\" / \"Offline — as of 3m ago\"). */\n label: string;\n /** The capture time backing an offline/stale label (ISO), or null. M4 may\n * reformat this; `label` already embeds a relative form. */\n asOf: string | null;\n};\n\nexport type DeriveMachineChipInput = {\n /** `capabilities.liveness` — \"warm\" | \"draining\" | \"cold\" (or null pre-negotiation). */\n liveness?: string | null | undefined;\n /** `useSessionCapabilities().state` — drives the \"waking\" (negotiating) read. */\n capabilitiesState?: SessionCapabilitiesState | null | undefined;\n /** The ACTIVE machine's connection state (from `useMachines`), when known. A\n * self-hosted machine that is `offline` cannot be remotely woken. */\n activeMachineState?: MachineState | null | undefined;\n /** Whether the active sandbox is a user-owned self-hosted machine (no remote wake). */\n activeIsSelfhosted?: boolean | undefined;\n /** An interaction (wake-on-edit, terminal focus) is actively warming the box. */\n wantsWarm?: boolean | undefined;\n /** The latest capture's `capturedAt` (ISO) — the \"as of <time>\" backing. */\n capturedAt?: string | null | undefined;\n /** Injectable clock for deterministic relative labels (defaults to Date.now). */\n now?: number | undefined;\n};\n\n/** Compact relative-time for the \"as of\" label. Pure, no Intl dependency. */\nexport function formatAsOf(capturedAt: string, now: number): string {\n const then = Date.parse(capturedAt);\n if (Number.isNaN(then)) return \"recently\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 45) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.round(hours / 24);\n return `${days}d ago`;\n}\n\n/**\n * Derive the machine-state chip from the live capability/liveness surface, the\n * active machine's connection state, and the latest capture time. PURE (M4 renders\n * it; the whole point is a deterministic, testable projection).\n *\n * Precedence:\n * 1. warm/draining lease → **live** (a warm box always wins).\n * 2. actively warming (negotiating, wake-on-edit, or a reconnecting/enrolling\n * machine) → **waking**.\n * 3. everything else (a cold/asleep box, a self-hosted machine that's offline,\n * an error) → **offline**, labelled \"as of <capture time>\".\n */\nexport function deriveMachineChip(input: DeriveMachineChipInput): MachineChip {\n const now = input.now ?? Date.now();\n const asOf = input.capturedAt ?? null;\n const offlineLabel = asOf ? `Offline — as of ${formatAsOf(asOf, now)}` : \"Offline\";\n\n // 1. A warm (or draining) lease is live regardless of anything else.\n if (input.liveness === \"warm\" || input.liveness === \"draining\") {\n return { state: \"live\", label: \"Live\", asOf: null };\n }\n\n // A self-hosted machine that reports offline cannot be woken remotely — honest\n // \"offline\", never \"waking\".\n const selfhostedOffline =\n input.activeIsSelfhosted === true && input.activeMachineState === \"offline\";\n\n // 2. Actively coming up: negotiation in flight, an edit/terminal is warming, or\n // the active machine is (re)connecting. Never when self-hosted is hard-offline.\n const machineWarming =\n input.activeMachineState === \"reconnecting\" || input.activeMachineState === \"enrolling\";\n if (\n !selfhostedOffline &&\n (input.capabilitiesState === \"negotiating\" || input.wantsWarm === true || machineWarming)\n ) {\n return { state: \"waking\", label: \"Waking…\", asOf };\n }\n\n // 3. Cold / asleep / offline / error → honest stale label.\n return { state: \"offline\", label: offlineLabel, asOf };\n}\n\nexport type UseMachineChipOptions = DeriveMachineChipInput;\n\n/**\n * Thin memoized wrapper over `deriveMachineChip` for the dock header (M4). The\n * dock already runs `useSessionCapabilities` + `useMachines` + `useWorkspace\n * capture`; it feeds their `liveness` / `state` / active-machine state /\n * `capturedAt` here. Pass a periodically-updated `now` if you want the relative\n * \"as of\" label to tick.\n */\nexport function useMachineChip(input: UseMachineChipOptions): MachineChip {\n const {\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n } = input;\n return useMemo(\n () =>\n deriveMachineChip({\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n }),\n // Depend on the primitive fields (not the object identity) so a caller passing\n // a fresh object each render doesn't recompute needlessly.\n [\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n ],\n );\n}\n","import { CheckIcon, ShieldCheckIcon, XIcon } from \"lucide-react\";\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport type { PendingApproval } from \"../approvals\";\nimport { cn } from \"../lib/cn\";\n\nexport type ApprovalSurfaceMessages = {\n title: string;\n description: string;\n approve: string;\n reject: string;\n approving: string;\n rejecting: string;\n formatToolName: (name: string) => string;\n};\n\nexport const defaultApprovalSurfaceMessages: ApprovalSurfaceMessages = {\n title: \"Approval required\",\n description: \"Review the requested action before the agent continues.\",\n approve: \"Approve\",\n reject: \"Reject\",\n approving: \"Approving…\",\n rejecting: \"Rejecting…\",\n formatToolName: (name) => name.replaceAll(\"_\", \" \").replaceAll(\".\", \" › \"),\n};\n\nexport type ApprovalSurfaceProps = {\n approvals: PendingApproval[];\n onApprove: (approval: PendingApproval) => void | Promise<void>;\n onReject: (approval: PendingApproval) => void | Promise<void>;\n responding?: boolean | undefined;\n error?: string | Error | null | undefined;\n messages?: Partial<ApprovalSurfaceMessages> | undefined;\n renderApproval?: ((approval: PendingApproval) => ReactNode) | undefined;\n className?: string | undefined;\n};\n\nconst APPROVAL_ARGUMENT_PREVIEW_CHARACTERS = 4_000;\n\nfunction approvalArgumentsPreview(value: unknown): string | null {\n if (value === undefined) return null;\n let serialized: string;\n try {\n serialized =\n typeof value === \"string\" ? value : (JSON.stringify(value, null, 2) ?? String(value));\n } catch {\n serialized = \"[Arguments unavailable]\";\n }\n const characters = Array.from(serialized);\n if (characters.length <= APPROVAL_ARGUMENT_PREVIEW_CHARACTERS) return serialized;\n return `${characters.slice(0, APPROVAL_ARGUMENT_PREVIEW_CHARACTERS).join(\"\")}\\n… ${characters.length - APPROVAL_ARGUMENT_PREVIEW_CHARACTERS} characters omitted`;\n}\n\nfunction approvalOwnershipKey(approval: PendingApproval): string {\n return `${approval.id}\\u0000${approval.name}\\u0000${approvalArgumentsPreview(approval.arguments) ?? \"\"}`;\n}\n\n/**\n * Host-neutral approval presentation backed by the native pending-approval\n * projection and control callbacks. It owns only presentation and duplicate\n * click fencing; OpenGeni remains the authority for approval state.\n */\nexport function ApprovalSurface({\n approvals,\n onApprove,\n onReject,\n responding = false,\n error,\n messages: overrides,\n renderApproval,\n className,\n}: ApprovalSurfaceProps) {\n const titleId = useId();\n const messages = { ...defaultApprovalSurfaceMessages, ...overrides };\n const [pending, setPending] = useState<{\n approvalKey: string;\n decision: \"approve\" | \"reject\";\n token: symbol;\n } | null>(null);\n const pendingRef = useRef<{ approvalKey: string; token: symbol } | null>(null);\n const [decisionError, setDecisionError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (\n pending &&\n !approvals.some((approval) => approvalOwnershipKey(approval) === pending.approvalKey)\n ) {\n if (pendingRef.current?.token === pending.token) {\n pendingRef.current = null;\n }\n setPending((current) => (current?.token === pending.token ? null : current));\n }\n }, [approvals, pending]);\n\n if (approvals.length === 0) return null;\n const busy = responding || pendingRef.current !== null;\n const decide = async (\n approval: PendingApproval,\n decision: \"approve\" | \"reject\",\n ): Promise<void> => {\n if (responding || pendingRef.current !== null) return;\n const approvalKey = approvalOwnershipKey(approval);\n const token = Symbol(approvalKey);\n pendingRef.current = { approvalKey, token };\n setPending({ approvalKey, decision, token });\n setDecisionError(null);\n try {\n await (decision === \"approve\" ? onApprove(approval) : onReject(approval));\n // Keep the decision fenced until the authoritative projection removes or\n // replaces this exact approval. A successful callback is transport\n // acceptance, not settlement.\n } catch (cause) {\n if (pendingRef.current?.token === token) {\n pendingRef.current = null;\n setPending((current) => (current?.token === token ? null : current));\n setDecisionError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n }\n };\n const errorMessage = decisionError?.message ?? (error instanceof Error ? error.message : error);\n\n return (\n <section\n className={cn(\n \"og-root flex w-full flex-col gap-3 rounded-og-lg border border-og-status-waiting/35 bg-og-status-waiting/5 p-4 shadow-og-sm\",\n className,\n )}\n aria-labelledby={titleId}\n >\n <header className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-status-waiting/12 text-og-status-waiting\">\n <ShieldCheckIcon aria-hidden=\"true\" className=\"size-4\" />\n </span>\n <div className=\"min-w-0\">\n <h2 id={titleId} className=\"text-og-md font-semibold text-og-fg\">\n {messages.title}\n </h2>\n <p className=\"mt-0.5 text-og-sm text-og-fg-muted\">{messages.description}</p>\n </div>\n </header>\n\n <div className=\"flex flex-col gap-2\">\n {approvals.map((approval) => {\n const approvalKey = approvalOwnershipKey(approval);\n const active = pending?.approvalKey === approvalKey ? pending.decision : null;\n const argumentsPreview = approvalArgumentsPreview(approval.arguments);\n return (\n <article\n key={approval.id}\n data-approval-id={approval.id}\n className=\"rounded-og-md border border-og-border bg-og-surface-1 p-3\"\n >\n {renderApproval ? (\n renderApproval(approval)\n ) : (\n <>\n <p className=\"text-og-sm font-medium text-og-fg\">\n {messages.formatToolName(approval.name)}\n </p>\n {argumentsPreview !== null ? (\n <pre className=\"mt-2 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-og-sm bg-og-surface-2 p-2 font-mono text-og-xs text-og-fg-muted\">\n {argumentsPreview}\n </pre>\n ) : null}\n </>\n )}\n <div className=\"mt-3 flex flex-wrap justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void decide(approval, \"reject\")}\n className=\"inline-flex min-h-9 items-center gap-1.5 rounded-og-md border border-og-border px-3 py-1.5 text-og-sm font-medium text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50\"\n >\n <XIcon aria-hidden=\"true\" className=\"size-3.5\" />\n {active === \"reject\" ? messages.rejecting : messages.reject}\n </button>\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void decide(approval, \"approve\")}\n className=\"inline-flex min-h-9 items-center gap-1.5 rounded-og-md bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg transition-colors hover:bg-og-accent-strong disabled:opacity-50\"\n >\n <CheckIcon aria-hidden=\"true\" className=\"size-3.5\" />\n {active === \"approve\" ? messages.approving : messages.approve}\n </button>\n </div>\n </article>\n );\n })}\n </div>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-og-sm text-og-status-failed\">\n {errorMessage}\n </p>\n ) : null}\n </section>\n );\n}\n","import type { ClientModel, EffectiveSessionControl } from \"@opengeni/sdk\";\nimport { LayoutGroup, motion } from \"motion/react\";\nimport type { ClipboardEvent, ReactNode } from \"react\";\nimport type { SlashCommand } from \"../commands/types\";\nimport type { ComposerState } from \"../hooks/use-composer\";\nimport type { UseFileAttachmentsResult } from \"../hooks/use-file-attachments\";\nimport type { SlashCommandContext } from \"../hooks/use-slash-commands\";\nimport { OPEN_WORKSTREAM_CONTROL_EVENT } from \"../workstream-control-event\";\nimport {\n Actions,\n AttachButton,\n Attachments,\n CommandPalette,\n Confirmation,\n Controls,\n Footer,\n Frame,\n Help,\n Hint,\n Input,\n ModelPicker,\n PauseButton,\n PausedState,\n RestoredResources,\n Root,\n SendButton,\n Status,\n Surface,\n useChatComposerController,\n type ChatComposerMessages,\n type ComposerControlLinks,\n} from \"./composer\";\nimport {\n ComposerTranscriptionControl,\n type ComposerTranscriptionControlProps,\n} from \"./composer-transcription-control\";\n\nexport { OPEN_WORKSTREAM_CONTROL_EVENT };\n\nexport type ChatComposerProps = {\n composer: ComposerState;\n /** Canonical workstream control, separate from lifecycle status. */\n effectiveControl?: EffectiveSessionControl | null | undefined;\n /** Waiting prompts already ahead of a normal Send. */\n queuedAheadCount?: number | undefined;\n /** Whether broader Workspace Resume is authorized for this viewer. */\n canControlWorkspace?: boolean | undefined;\n /** Optional host routes used to navigate from effective Pause blockers. */\n controlLinks?: ComposerControlLinks | undefined;\n placeholder?: string | undefined;\n disabled?: boolean | undefined;\n autoFocus?: boolean | undefined;\n /** Replaces the default keyboard hint under the field. */\n hint?: string | undefined;\n /** App controls ahead of attach (e.g. mobile “+” overflow). */\n controlsLeading?: ReactNode | undefined;\n /** App controls in the footer row, replacing the hint. */\n controlsStart?: ReactNode | undefined;\n /** App actions beside Pause/Send, ordered before the built-in actions. */\n actionsStart?: ReactNode | undefined;\n /** Extra classes for the built-in attach control (e.g. `max-sm:hidden`). */\n attachButtonClassName?: string | undefined;\n /** Provider-neutral speech capability. Provider configuration stays in workspace settings. */\n transcription?: ComposerTranscriptionControlProps | undefined;\n /** Extra classes on the transcription control. */\n transcriptionClassName?: string | undefined;\n /** Soft-hide dictate while realtime voice is active (animated collapse). */\n transcriptionSuppressed?: boolean | undefined;\n /** Content rendered above the textarea, inside the field chrome. */\n header?: ReactNode | undefined;\n /** Paste hook composed with the attachment paste path. */\n onPaste?: ((event: ClipboardEvent<HTMLTextAreaElement>) => void) | undefined;\n /** Opt-in file attachment state, typically from `useFileAttachments`. */\n attachments?: UseFileAttachmentsResult | undefined;\n /** Opt-in model picker choices. */\n models?: ClientModel[] | undefined;\n selectedModel?: string | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n className?: string | undefined;\n commands?: readonly SlashCommand[] | undefined;\n commandContext?: SlashCommandContext | undefined;\n onClearView?: (() => void) | undefined;\n /** Partial overrides for all composer-owned visible and accessible copy. */\n messages?: Partial<ChatComposerMessages> | undefined;\n};\n\n/**\n * Batteries-included chat composer. This preset is assembled exclusively from\n * the public controller and compound primitives exported by the composer\n * subpath, so custom and default layouts share one behavioral implementation.\n */\nexport function ChatComposer({\n composer,\n effectiveControl,\n queuedAheadCount,\n canControlWorkspace,\n controlLinks,\n placeholder,\n disabled,\n autoFocus,\n hint,\n controlsLeading,\n controlsStart,\n actionsStart,\n attachButtonClassName,\n transcription,\n transcriptionClassName,\n transcriptionSuppressed = false,\n header,\n onPaste,\n attachments,\n models,\n selectedModel,\n onSelectModel,\n className,\n commands,\n commandContext,\n onClearView,\n messages,\n}: ChatComposerProps) {\n const controller = useChatComposerController({\n delivery: composer,\n draft: composer,\n control: composer,\n effectiveControl,\n queuedAheadCount,\n canControlWorkspace,\n controlLinks,\n disabled,\n attachments,\n commands,\n commandContext,\n onClearView,\n onPaste,\n messages,\n });\n const hasControls = Boolean(\n attachments || models || controlsLeading || controlsStart || transcription,\n );\n const stackActions = hasControls && Boolean(actionsStart);\n\n return (\n <Root controller={controller} className={className}>\n <Frame>\n <CommandPalette />\n <Surface>\n <PausedState />\n <RestoredResources />\n <Attachments />\n {header}\n <Input placeholder={placeholder} autoFocus={autoFocus} />\n {controller.confirmState ? (\n <Confirmation />\n ) : (\n <Footer className={stackActions ? \"max-sm:flex-nowrap sm:flex-wrap\" : undefined}>\n <LayoutGroup id=\"og-composer-footer\">\n {hasControls ? (\n <Controls\n className={stackActions ? \"min-w-0 max-sm:flex-1 sm:w-auto\" : undefined}\n >\n {controlsLeading}\n <AttachButton className={attachButtonClassName} />\n {transcription ? (\n <ComposerTranscriptionControl\n {...transcription}\n suppressed={transcriptionSuppressed}\n className={[transcription.className, transcriptionClassName]\n .filter(Boolean)\n .join(\" \")}\n />\n ) : null}\n {models ? (\n <motion.span\n layout\n transition={{ duration: 0.36, ease: [0.22, 1, 0.36, 1] }}\n className=\"inline-flex min-w-0\"\n >\n <ModelPicker\n models={models}\n value={selectedModel}\n onChange={onSelectModel}\n />\n </motion.span>\n ) : null}\n {controlsStart ? (\n <motion.span\n layout\n transition={{ duration: 0.36, ease: [0.22, 1, 0.36, 1] }}\n className=\"inline-flex min-w-0 items-center gap-1.5\"\n >\n {controlsStart}\n </motion.span>\n ) : null}\n </Controls>\n ) : (\n <Hint>{hint}</Hint>\n )}\n <Actions\n className={stackActions ? \"max-sm:shrink-0 sm:w-auto sm:justify-end\" : undefined}\n >\n {actionsStart}\n <PauseButton />\n <SendButton />\n </Actions>\n </LayoutGroup>\n </Footer>\n )}\n </Surface>\n </Frame>\n <Help />\n <Status />\n </Root>\n );\n}\n","import type { Session } from \"@opengeni/sdk\";\nimport { cn } from \"../lib/cn\";\nimport { formatRelativeTime, truncate } from \"../lib/format\";\nimport { SessionStatus } from \"./session-status\";\n\nexport type FleetTileProps = {\n session: Session;\n /** Overrides the derived title (metadata.title/name, else the initial message). */\n title?: string | undefined;\n /** Extra line under the title — e.g. \"drift check\" or the worker's task. */\n subtitle?: string | undefined;\n onOpen?: ((session: Session) => void) | undefined;\n className?: string | undefined;\n};\n\n/** Best-effort display title for a session. */\nexport function sessionDisplayTitle(session: Session): string {\n // A set title (agent-generated or user-renamed) wins over metadata + the\n // initial-message fallback.\n if (typeof session.title === \"string\" && session.title.trim().length > 0) {\n return session.title;\n }\n for (const key of [\"title\", \"name\"] as const) {\n const value = session.metadata[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n }\n return truncate(session.initialMessage, 100) || session.id.slice(0, 8);\n}\n\n/**\n * One session in the fleet/manager view: title, live status, model, and\n * recency — dense but calm. Running sessions carry a breathing status dot\n * and a faint accent edge so the live ones read at a glance.\n */\nexport function FleetTile({ session, title, subtitle, onOpen, className }: FleetTileProps) {\n const running = session.status === \"running\" || session.status === \"queued\";\n const needsYou = session.status === \"requires_action\";\n return (\n <button\n type=\"button\"\n data-status={session.status}\n onClick={onOpen ? () => onOpen(session) : undefined}\n disabled={!onOpen}\n className={cn(\n \"og-root group relative flex w-full flex-col gap-2.5 overflow-hidden rounded-og-lg border border-og-border\",\n \"bg-og-surface-1 p-4 text-left shadow-og-sm\",\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 ease-og-out\",\n onOpen &&\n \"hover:-translate-y-px hover:border-og-border-strong hover:bg-og-surface-2 hover:shadow-og-md\",\n \"disabled:cursor-default\",\n className,\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"absolute inset-y-0 left-0 w-0.5 transition-opacity duration-300\",\n running\n ? \"bg-og-accent opacity-100\"\n : needsYou\n ? \"bg-og-status-waiting opacity-100\"\n : \"opacity-0\",\n )}\n />\n <span className=\"flex w-full items-start justify-between gap-3\">\n <span className=\"line-clamp-2 min-w-0 text-og-base font-medium leading-snug text-og-fg\">\n {title ?? sessionDisplayTitle(session)}\n </span>\n <SessionStatus status={session.status} size=\"sm\" className=\"mt-px\" />\n </span>\n {subtitle ? (\n <span className=\"line-clamp-1 text-og-sm text-og-fg-muted\">{subtitle}</span>\n ) : null}\n <span className=\"mt-auto flex w-full items-center gap-2 text-og-xs text-og-fg-subtle\">\n <span className=\"font-og-mono\">{session.id.slice(0, 8)}</span>\n <span aria-hidden>·</span>\n <span className=\"truncate\">{session.model}</span>\n <span className=\"ml-auto shrink-0\" title={session.updatedAt}>\n {formatRelativeTime(session.updatedAt)}\n </span>\n </span>\n </button>\n );\n}\n","import type { TerminalCapability } from \"@opengeni/sdk\";\nimport { type ReactNode, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useTerminalStream } from \"../hooks/use-terminal-stream\";\nimport type { UseSandboxTerminalResult } from \"../hooks/use-sandbox-terminal\";\nimport { resolveTerminalFont, xtermThemeFromTokens } from \"../lib/xterm-theme\";\nimport { attachRenderer, type RendererLoaders, type RendererTier } from \"../lib/xterm-renderer\";\n\n/**\n * A COMPLETE xterm `ITheme` (subset by intent, but every field xterm colors a\n * cell with is present so it never falls back to its stock VGA palette). Built\n * from the og tokens via `xtermThemeFromTokens`.\n */\nexport type XtermTheme = {\n background?: string;\n foreground?: string;\n cursor?: string;\n cursorAccent?: string;\n selectionBackground?: string;\n selectionForeground?: string;\n selectionInactiveBackground?: string;\n black?: string;\n red?: string;\n green?: string;\n yellow?: string;\n blue?: string;\n magenta?: string;\n cyan?: string;\n white?: string;\n brightBlack?: string;\n brightRed?: string;\n brightGreen?: string;\n brightYellow?: string;\n brightBlue?: string;\n brightMagenta?: string;\n brightCyan?: string;\n brightWhite?: string;\n};\n\nexport type SandboxTerminalProps = {\n /** From `useSandboxTerminal(...)`. */\n result: UseSandboxTerminalResult;\n /**\n * The negotiated Terminal capability cell. When it advertises `transport:\n * \"pty-ws\"` + a live `url` (a warm box with a viewer attached), the terminal is\n * driven by a REAL bidirectional PTY over the Modal tunnel (ttyd-over-websocket)\n * INSTEAD of the broken ptyWrite-over-HTTP path: xterm input → the socket, the\n * socket's output → xterm, xterm resize → the socket. On a cold box / no url\n * (`transport: \"sse-events\"`) it stays on the read-only Channel-A firehose\n * (`result.chunks`). Omit to force the legacy firehose-only behavior.\n */\n terminalCapability?: TerminalCapability | null | undefined;\n /**\n * Full xterm theme. When omitted the terminal self-derives it from the `--og-*`\n * tokens of its own container (correct even inside a nested `data-og-theme`\n * subtree). Passing it lets the host drive re-theme on a theme flip.\n */\n theme?: XtermTheme | undefined;\n fontFamily?: string | undefined;\n fontSize?: number | undefined;\n /**\n * Lease liveness (`cold` | `warming` | `warm` | `draining`). Drives the\n * boot-in-terminal status lines: once the user has focused the terminal and\n * the box is not yet warm, a styled \"● waking machine — Ns…\" line is rendered\n * INSIDE xterm (no overlay/spinner) until the live PTY connects.\n */\n liveness?: string | undefined;\n /**\n * Force read-only even when the PTY accepts stdin. Default: interactive\n * whenever a live pty-ws stream is connected OR `result.write !== null` (the box\n * advertises an interactive PTY); otherwise the read-only agent firehose.\n */\n readOnly?: boolean | undefined;\n /** Shown on the server / before xterm hydrates (SSR-safe placeholder). */\n placeholder?: ReactNode | undefined;\n /** Render the small status header (pty/shell + running dot + read-only pill). */\n showHeader?: boolean | undefined;\n /** Shell label for the header (e.g. `/bin/bash`). */\n shell?: string | undefined;\n /**\n * Fired the first time the user engages the terminal surface (focus or click).\n * The host wires this to warm the box for the REAL pty-ws terminal (the viewer\n * attach), so a cold box upgrades from the read-only firehose to a live PTY ON\n * INTERACT — never on mere mount (which would force a box spin-up and regress\n * the firehose-only default).\n */\n onActivate?: (() => void) | undefined;\n className?: string | undefined;\n};\n\n// The xterm.js handle the effect owns. `any`-free structural shape so we can\n// drive it without a hard type dep on the lazily-imported lib.\ntype XtermLike = {\n open: (el: HTMLElement) => void;\n write: (data: string) => void;\n clear: () => void;\n onData: (cb: (data: string) => void) => { dispose: () => void };\n onResize: (cb: (size: { cols: number; rows: number }) => void) => { dispose: () => void };\n loadAddon: (addon: unknown) => void;\n dispose: () => void;\n cols: number;\n rows: number;\n options: {\n theme?: XtermTheme;\n disableStdin?: boolean;\n cursorBlink?: boolean;\n fontFamily?: string;\n fontSize?: number;\n };\n};\ntype FitAddonLike = { fit: () => void };\n\n/** Debug seam (dev/evidence only, never a typed public API): a global hook the\n * screenshot harness reads to probe the settled renderer tier + resolved font\n * without mounting a real WebGL context in unit tests. */\ntype TerminalDebugInfo = {\n renderer: RendererTier | null;\n fontFamily: string | undefined;\n fontSize: number | undefined;\n hasVarInFont: boolean;\n theme: XtermTheme | undefined;\n /** The live xterm instance (opaque) — the evidence harness reads its buffer to\n * prove screen preservation (E5) and drive the burst-responsiveness probe. */\n term: unknown;\n};\ndeclare global {\n // eslint-disable-next-line no-var\n var __OG_TERMINAL_DEBUG__: ((info: TerminalDebugInfo) => void) | undefined;\n // A space/comma list of renderer tiers to force-fail, so the demo can prove the\n // WebGL→canvas→DOM fallback ladder without a real GPU context loss.\n // eslint-disable-next-line no-var\n var __OG_FORCE_RENDERER_FAIL__: string | undefined;\n}\n\n/** Which renderer tiers are force-failed (demo/evidence only). */\nfunction forcedRendererFailures(): Set<RendererTier> {\n const raw = typeof globalThis !== \"undefined\" ? globalThis.__OG_FORCE_RENDERER_FAIL__ : undefined;\n const set = new Set<RendererTier>();\n if (typeof raw === \"string\") {\n for (const tier of raw.split(/[\\s,]+/)) {\n if (tier === \"webgl\" || tier === \"canvas\") set.add(tier);\n }\n }\n return set;\n}\n\n/**\n * An xterm.js terminal fed by the Channel-A event projection\n * (`useSandboxTerminal`) or a live ttyd PTY. xterm + its addons are lazy-imported\n * inside an effect, so SSR renders the placeholder and the terminal mounts on\n * hydration.\n *\n * Renderer: WebGL (GPU-composited cells) with a canvas → DOM fallback ladder\n * (`attachRenderer`), so bursty output stays smooth but a lost/blocklisted\n * context degrades gracefully. Font + theme are resolved to CONCRETE values\n * from the og tokens BEFORE construction (xterm measures glyphs in a canvas\n * where `var()` never resolves, and colors every cell from a full ANSI palette).\n *\n * The terminal is mounted ONCE and its data source is swapped in place: the\n * read-only firehose (`result.chunks`) → the live PTY. Stdin is toggled via\n * `term.options.disableStdin` at runtime (not a remount), so scrollback and the\n * visible screen survive the projection→PTY handoff.\n *\n * Resizes are tracked with a `ResizeObserver` on the container (not just\n * `window.resize`) so dragging the dock handle refits the grid.\n */\nexport function SandboxTerminal({\n result,\n terminalCapability,\n theme,\n fontFamily,\n fontSize,\n liveness,\n readOnly,\n placeholder,\n showHeader,\n shell,\n onActivate,\n className,\n}: SandboxTerminalProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const termRef = useRef<XtermLike | null>(null);\n const fitRef = useRef<FitAddonLike | null>(null);\n const rendererRef = useRef<RendererTier | null>(null);\n const writtenRef = useRef<Set<string>>(new Set());\n const wroteFirehoseRef = useRef(false);\n const bootActiveRef = useRef(false);\n const [ready, setReady] = useState(false);\n const [activated, setActivated] = useState(false);\n\n // ── Interactive transport switch ─────────────────────────────────────────────\n const ptyWs = terminalCapability?.transport === \"pty-ws\" && Boolean(terminalCapability?.url);\n // Output frames buffer here until xterm has mounted; the write effect drains it.\n const ptyOutputQueueRef = useRef<string[]>([]);\n const {\n status: ptyStatus,\n write: ptyWrite,\n resize: resizePty,\n connected: ptyConnected,\n } = useTerminalStream({\n capability: ptyWs\n ? {\n transport: terminalCapability!.transport,\n url: terminalCapability!.url,\n token: terminalCapability!.token,\n }\n : null,\n onOutput: (data) => {\n const term = termRef.current;\n if (term) term.write(data);\n else ptyOutputQueueRef.current.push(data);\n },\n });\n\n // PTY mode = a live ttyd socket drives the screen. Firehose mode = the\n // read-only projection (or the legacy HTTP-write fallback).\n const ptyMode = ptyWs && ptyStatus !== \"closed\";\n const interactive = !readOnly && (ptyMode || result.write !== null);\n\n // Boot-in-terminal: after the user focuses a not-yet-warm box, show styled\n // status lines INSIDE xterm instead of an overlay — but only when there is no\n // firehose transcript to show yet (otherwise the projected output is shown).\n const warm = liveness === \"warm\" || liveness === \"draining\";\n const booting =\n activated &&\n !ptyMode &&\n !warm &&\n liveness !== null &&\n liveness !== undefined &&\n result.chunks.length === 0;\n\n function handleActivate() {\n if (!activated) setActivated(true);\n onActivate?.();\n }\n\n // Mount xterm once (client-only). Never re-mounts on interactive/theme flips —\n // stdin + theme are runtime options synced by the effects below, so the visible\n // screen survives the projection→PTY handoff (E5).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = containerRef.current;\n if (!el) return;\n let disposed = false;\n\n // Wait for a real measured size before opening xterm so the first fit is at\n // the true width (belt-and-braces with the visibility gate below).\n const waitForSize = (node: HTMLElement) =>\n new Promise<void>((resolve) => {\n if (node.clientWidth > 0 && node.clientHeight > 0) return resolve();\n const ro = new ResizeObserver(() => {\n if (node.clientWidth > 0 && node.clientHeight > 0) {\n ro.disconnect();\n resolve();\n }\n });\n ro.observe(node);\n setTimeout(() => {\n ro.disconnect();\n resolve();\n }, 1000);\n });\n\n ensureXtermBaseCss();\n\n void (async () => {\n const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([\n import(\"@xterm/xterm\"),\n import(\"@xterm/addon-fit\"),\n import(\"@xterm/addon-web-links\"),\n ]);\n if (disposed) return;\n await waitForSize(el);\n if (disposed) return;\n\n // Resolve font + theme to CONCRETE values BEFORE construction. `var()` in a\n // font family never resolves in xterm's canvas measurement, so we read the\n // computed `--og-font-mono` here; the theme is a full ANSI palette.\n const font = resolveTerminalFont(el, { fontFamily, fontSize });\n const initialTheme = theme ?? xtermThemeFromTokens(el);\n\n const term = new Terminal({\n convertEol: true,\n disableStdin: !interactive,\n cursorBlink: interactive,\n fontFamily: font.fontFamily,\n fontSize: font.fontSize,\n lineHeight: font.lineHeight,\n ...(initialTheme ? { theme: initialTheme } : {}),\n }) as unknown as XtermLike;\n const fit = new FitAddon() as unknown as FitAddonLike;\n term.loadAddon(fit);\n term.loadAddon(\n new WebLinksAddon((_e: MouseEvent, uri: string) =>\n window.open(uri, \"_blank\", \"noopener,noreferrer\"),\n ) as unknown,\n );\n term.open(el);\n termRef.current = term;\n fitRef.current = fit;\n\n // Attach the fastest available renderer. For xterm-6 the supported ladder\n // is WebGL → DOM (the standalone 2D-canvas addon targets xterm-5 internals,\n // so it is intentionally NOT in the shipped path). Init failure / context\n // loss steps down to xterm's built-in DOM renderer; the settled tier is\n // exposed for evidence.\n const failSet = forcedRendererFailures();\n const loaders: RendererLoaders = {\n webgl: async (onLoss) => {\n if (failSet.has(\"webgl\")) throw new Error(\"forced webgl failure\");\n const { WebglAddon } = await import(\"@xterm/addon-webgl\");\n const addon = new WebglAddon() as unknown as {\n dispose: () => void;\n onContextLoss?: (cb: () => void) => void;\n };\n addon.onContextLoss?.(() => {\n try {\n addon.dispose();\n } catch {\n // already disposed\n }\n onLoss();\n });\n term.loadAddon(addon);\n return addon;\n },\n };\n await attachRenderer(\"webgl\", loaders, (tier) => {\n rendererRef.current = tier;\n el.setAttribute(\"data-og-term-renderer\", tier);\n });\n if (disposed) return;\n\n // Fit BEFORE reveal so the first visible frame is already correctly sized\n // (no 80×24 rasterize-then-reflow flash — the container stays hidden until\n // now via the `ready` visibility gate).\n const settle = () => {\n try {\n fit.fit();\n } catch {\n // ignore fit before layout\n }\n };\n settle();\n requestAnimationFrame(() => {\n if (disposed) return;\n settle();\n if (ptyOutputQueueRef.current.length > 0) {\n for (const data of ptyOutputQueueRef.current) term.write(data);\n ptyOutputQueueRef.current = [];\n }\n el.setAttribute(\"data-og-term-ready\", \"true\");\n if (typeof globalThis.__OG_TERMINAL_DEBUG__ === \"function\") {\n globalThis.__OG_TERMINAL_DEBUG__({\n renderer: rendererRef.current,\n fontFamily: term.options.fontFamily,\n fontSize: term.options.fontSize,\n hasVarInFont: String(term.options.fontFamily ?? \"\").includes(\"var(\"),\n theme: term.options.theme,\n term,\n });\n }\n setReady(true);\n });\n })();\n\n return () => {\n disposed = true;\n termRef.current?.dispose();\n termRef.current = null;\n fitRef.current = null;\n rendererRef.current = null;\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n bootActiveRef.current = false;\n setReady(false);\n };\n // Mount ONCE — see the effects below for the runtime option syncs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync stdin/cursor at runtime (NOT a remount) when interactivity flips, so the\n // projection→PTY handoff keeps the single Terminal instance + its scrollback.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n term.options.disableStdin = !interactive;\n term.options.cursorBlink = interactive;\n }, [ready, interactive]);\n\n // Re-theme live (dark↔light flip) without re-mounting / losing scrollback. Uses\n // the passed theme, or self-derives from the container's tokens.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n const next = theme ?? xtermThemeFromTokens(containerRef.current);\n if (next) term.options.theme = next;\n }, [ready, theme]);\n\n // Boot-in-terminal status lines. While `booting`, paint a styled \"● waking\n // machine — Ns…\" line and tick it. On exit, if those lines were the ONLY\n // content (no firehose transcript), clear them for the incoming live screen —\n // never clear a real transcript (E5 preservation).\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n if (!booting) {\n if (bootActiveRef.current) {\n bootActiveRef.current = false;\n if (!wroteFirehoseRef.current) term.clear();\n }\n return;\n }\n bootActiveRef.current = true;\n const start = Date.now();\n const paint = () => {\n const s = Math.max(0, Math.round((Date.now() - start) / 1000));\n // Bright-blue dot (accent) + dim label; `\\r` + clear-line keeps it on one\n // line so the counter updates in place.\n term.write(`\\r\\x1b[2K\\x1b[94m●\\x1b[0m \\x1b[2mwaking machine — ${s}s…\\x1b[0m`);\n };\n term.write(\"\\r\\n\");\n paint();\n const id = setInterval(paint, 1000);\n return () => clearInterval(id);\n }, [ready, booting]);\n\n // Write new firehose chunks incrementally — ONLY in firehose mode. In PTY mode\n // the ttyd socket owns the screen. Records that a real transcript exists so the\n // boot cleanup never wipes it.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || ptyMode) return;\n for (const chunk of result.chunks) {\n if (writtenRef.current.has(chunk.id)) continue;\n writtenRef.current.add(chunk.id);\n wroteFirehoseRef.current = true;\n term.write(chunk.text);\n }\n }, [ready, result.chunks, ptyMode]);\n\n // Wire interactive input when allowed. PTY mode pipes keystrokes to the ttyd\n // socket; firehose mode uses the legacy ptyWrite-over-HTTP fn.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !interactive) return;\n const sink = ptyMode ? ptyWrite : result.write;\n if (!sink) return;\n const sub = term.onData((data) => sink(data));\n return () => sub.dispose();\n }, [ready, interactive, ptyMode, ptyWrite, result.write]);\n\n // In PTY mode, tell ttyd the window size on the xterm resize event.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !ptyMode) return;\n resizePty(term.cols, term.rows);\n const sub = term.onResize(({ cols, rows }) => resizePty(cols, rows));\n return () => sub.dispose();\n }, [ready, ptyMode, ptyConnected, resizePty]);\n\n // Refit on container resize (dock drag) AND window resize.\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const refit = () => {\n try {\n fitRef.current?.fit();\n } catch {\n // ignore\n }\n };\n window.addEventListener(\"resize\", refit);\n let observer: ResizeObserver | null = null;\n const el = containerRef.current;\n if (el && typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(() => refit());\n observer.observe(el);\n }\n return () => {\n window.removeEventListener(\"resize\", refit);\n observer?.disconnect();\n };\n }, [ready]);\n\n return (\n <div className={cn(\"relative flex h-full w-full flex-col overflow-hidden\", className)}>\n {showHeader && (\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-2 py-1 text-og-xs text-og-fg-subtle\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n result.running ? \"bg-og-status-running\" : \"bg-og-fg-subtle\",\n )}\n />\n <span className=\"truncate font-og-mono\">pty: {shell ?? \"shell\"}</span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {!interactive && (\n <span className=\"rounded-og-sm bg-og-surface-2 px-1.5 py-0.5 text-og-xs uppercase tracking-wide\">\n read-only\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => {\n termRef.current?.clear();\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n }}\n className=\"rounded-og-sm px-1.5 py-0.5 text-og-xs hover:text-og-fg pointer-coarse:min-h-11\"\n >\n Clear\n </button>\n </span>\n </div>\n )}\n {/* `onPointerDownCapture`/`onFocusCapture` fire on the FIRST user engagement\n with the terminal surface (capture so they win even though xterm's own\n listeners stop propagation). The host warms the box for pty-ws here. The\n 12px inset + `--og-color-bg` ground match the dock surface. */}\n <div\n className=\"relative min-h-0 flex-1 bg-og-bg p-3\"\n onPointerDownCapture={handleActivate}\n onFocusCapture={handleActivate}\n >\n {!ready && (placeholder ?? <TerminalPlaceholder />)}\n {/* Kept `visibility:hidden` until the first successful fit so the first\n VISIBLE frame is already correctly sized (no 80×24 flash — E4). */}\n <div\n ref={containerRef}\n className=\"h-full w-full\"\n style={{ visibility: ready ? \"visible\" : \"hidden\" }}\n data-opengeni-terminal\n />\n </div>\n </div>\n );\n}\n\n// xterm.js ships a stylesheet (`@xterm/xterm/css/xterm.css`) that is REQUIRED for\n// correct layout — without it the `.xterm-helper-textarea` (an off-screen input\n// xterm uses for IME/keystrokes) is NOT clipped and renders as a visible 1-line\n// box in the top-left (the \"weird input box\" bug), and the viewport/screen aren't\n// positioned. The host app never imports that CSS (no global stylesheet here), so\n// we inject the structural rules once, idempotently (keyed by id), the first time\n// any terminal mounts. Scoped under `.xterm` so it can't leak into host styles.\nconst XTERM_STYLE_ID = \"opengeni-xterm-base-css\";\nconst XTERM_BASE_CSS = `\n.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}\n.xterm.focus,.xterm:focus{outline:none}\n.xterm .xterm-helpers{position:absolute;top:0;z-index:5}\n.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}\n.xterm .composition-view{background:var(--og-color-bg);color:var(--og-color-fg);display:none;position:absolute;white-space:nowrap;z-index:1}\n.xterm .composition-view.active{display:block}\n.xterm .xterm-viewport{background-color:transparent;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}\n.xterm .xterm-screen{position:relative}\n.xterm .xterm-screen canvas{position:absolute;left:0;top:0}\n.xterm .xterm-scroll-area{visibility:hidden}\n.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}\n.xterm.enable-mouse-events{cursor:default}\n.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}\n.xterm.column-select.focus{cursor:crosshair}\n.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}\n.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}\n.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}\n.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}\n.xterm-dim{opacity:1!important}\n.xterm-underline-1{text-decoration:underline}\n.xterm-underline-2{text-decoration:double underline}\n.xterm-underline-3{text-decoration:wavy underline}\n.xterm-underline-4{text-decoration:dotted underline}\n.xterm-underline-5{text-decoration:dashed underline}\n.xterm-overline{text-decoration:overline}\n.xterm-strikethrough{text-decoration:line-through}\n.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}\n.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}\n.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}\n.xterm-decoration-top{z-index:2;position:relative}\n`;\n\nfunction ensureXtermBaseCss(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(XTERM_STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = XTERM_STYLE_ID;\n style.textContent = XTERM_BASE_CSS;\n document.head.appendChild(style);\n}\n\nfunction TerminalPlaceholder() {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center text-og-sm text-og-fg-subtle\">\n Loading terminal…\n </div>\n );\n}\n","import type { XtermTheme } from \"../components/sandbox-terminal\";\n\n/* ----------------------------------------------------------------------------\n xterm theming + font resolution from the `--og-*` token system.\n\n xterm paints glyphs into a canvas (WebGL / 2D) and builds its font strings by\n hand, so it can consume NEITHER CSS custom properties (`var(--og-font-mono)`\n never resolves in a canvas font string) NOR a partial palette (an unset ANSI\n slot falls back to xterm's stock VGA color). This module resolves both to\n concrete values: a full 16-color ANSI ITheme designed against the og palette\n for BOTH themes, and a concrete monospace family + px size.\n\n The color-building + font-resolution cores are pure over a token `read`\n function so they unit-test without a DOM; thin `*FromTokens` / `resolve*`\n wrappers do the `getComputedStyle` read on the client.\n -------------------------------------------------------------------------- */\n\nexport type ThemeMode = \"dark\" | \"light\";\n\n/** Reads a resolved token value (already `getComputedStyle`-flattened). */\nexport type TokenReader = (name: string) => string | undefined;\n\n/**\n * The 16 ANSI colors + selection/cursor treatment, designed against the og\n * OKLCH palette. Hues are borrowed from the og status ramp so terminal color\n * reads as part of the same system: red≈status-failed(22), green≈status-idle\n * (155), yellow≈status-running(80), blue≈accent(255), magenta≈status-waiting\n * (305), cyan≈200. Dark uses lifted lightness on the dark slate ground; light\n * uses deeper, more saturated inks on the near-white ground. Bright variants\n * add lightness + a touch of chroma. `black`/`white` are neutral slate ramp\n * stops (never pure #000/#fff — that clashes with the calm neutral ground).\n */\nexport type AnsiPalette = {\n black: string;\n red: string;\n green: string;\n yellow: string;\n blue: string;\n magenta: string;\n cyan: string;\n white: string;\n brightBlack: string;\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n /** Selection wash + its foreground; sits over the terminal ground. */\n selectionBackground: string;\n selectionForeground: string;\n selectionInactiveBackground: string;\n};\n\nconst DARK_ANSI: AnsiPalette = {\n black: \"oklch(0.3 0.014 260)\",\n red: \"oklch(0.68 0.17 22)\",\n green: \"oklch(0.75 0.13 155)\",\n yellow: \"oklch(0.8 0.11 80)\",\n blue: \"oklch(0.7 0.14 255)\",\n magenta: \"oklch(0.72 0.13 305)\",\n cyan: \"oklch(0.75 0.1 210)\",\n white: \"oklch(0.8 0.01 260)\",\n brightBlack: \"oklch(0.45 0.014 260)\",\n brightRed: \"oklch(0.76 0.17 22)\",\n brightGreen: \"oklch(0.82 0.14 155)\",\n brightYellow: \"oklch(0.86 0.12 85)\",\n brightBlue: \"oklch(0.78 0.13 255)\",\n brightMagenta: \"oklch(0.8 0.13 305)\",\n brightCyan: \"oklch(0.83 0.1 200)\",\n brightWhite: \"oklch(0.97 0.005 260)\",\n selectionBackground: \"oklch(0.72 0.15 255 / 0.34)\",\n selectionForeground: \"oklch(0.985 0.005 260)\",\n selectionInactiveBackground: \"oklch(0.72 0.02 260 / 0.22)\",\n};\n\nconst LIGHT_ANSI: AnsiPalette = {\n black: \"oklch(0.35 0.015 260)\",\n red: \"oklch(0.52 0.19 22)\",\n green: \"oklch(0.5 0.13 155)\",\n yellow: \"oklch(0.56 0.13 80)\",\n blue: \"oklch(0.52 0.18 255)\",\n magenta: \"oklch(0.52 0.16 305)\",\n cyan: \"oklch(0.52 0.1 210)\",\n white: \"oklch(0.7 0.01 260)\",\n brightBlack: \"oklch(0.5 0.014 260)\",\n brightRed: \"oklch(0.55 0.2 22)\",\n brightGreen: \"oklch(0.54 0.14 155)\",\n brightYellow: \"oklch(0.6 0.14 80)\",\n brightBlue: \"oklch(0.48 0.19 255)\",\n brightMagenta: \"oklch(0.54 0.17 305)\",\n brightCyan: \"oklch(0.54 0.11 210)\",\n brightWhite: \"oklch(0.3 0.015 260)\",\n selectionBackground: \"oklch(0.55 0.18 255 / 0.24)\",\n selectionForeground: \"oklch(0.21 0.015 260)\",\n selectionInactiveBackground: \"oklch(0.6 0.02 260 / 0.16)\",\n};\n\n/** The designed ANSI-16 palette for a theme mode. Pure. */\nexport function ogAnsiPalette(mode: ThemeMode): AnsiPalette {\n return mode === \"light\" ? LIGHT_ANSI : DARK_ANSI;\n}\n\n/**\n * Build a COMPLETE xterm ITheme from token values. Background/foreground/cursor\n * come from the live `--og-*` tokens (so an embedder's rebrand flows through);\n * the ANSI-16 + selection come from the designed palette for `mode`. Every ANSI\n * slot is always set — xterm never falls back to its stock VGA colors. Pure.\n */\nexport function buildXtermTheme(read: TokenReader, mode: ThemeMode): XtermTheme {\n const ansi = ogAnsiPalette(mode);\n const bgFallback = mode === \"light\" ? \"oklch(0.978 0.003 260)\" : \"oklch(0.155 0.012 260)\";\n const fgFallback = mode === \"light\" ? \"oklch(0.21 0.015 260)\" : \"oklch(0.955 0.005 260)\";\n const bg = read(\"--og-color-bg\") ?? bgFallback;\n const fg = read(\"--og-color-fg\") ?? fgFallback;\n const accent = read(\"--og-color-accent\") ?? ansi.brightBlue;\n return {\n // Match the dock surface the terminal paints on (`--og-color-bg`).\n background: bg,\n foreground: fg,\n cursor: accent,\n cursorAccent: bg,\n selectionBackground: ansi.selectionBackground,\n selectionForeground: ansi.selectionForeground,\n selectionInactiveBackground: ansi.selectionInactiveBackground,\n black: ansi.black,\n red: ansi.red,\n green: ansi.green,\n yellow: ansi.yellow,\n blue: ansi.blue,\n magenta: ansi.magenta,\n cyan: ansi.cyan,\n white: ansi.white,\n brightBlack: ansi.brightBlack,\n brightRed: ansi.brightRed,\n brightGreen: ansi.brightGreen,\n brightYellow: ansi.brightYellow,\n brightBlue: ansi.brightBlue,\n brightMagenta: ansi.brightMagenta,\n brightCyan: ansi.brightCyan,\n brightWhite: ansi.brightWhite,\n };\n}\n\n/** Which og theme is active for `el`, matching the `use-theme-type` convention\n * (nearest `data-og-theme` / `.og-light` ancestor; else the computed\n * `color-scheme`; else dark, the first-class default). */\nfunction detectMode(el: Element, style: CSSStyleDeclaration): ThemeMode {\n const closest = el.closest?.bind(el);\n if (closest) {\n if (closest('[data-og-theme=\"light\"]') || closest(\".og-light\")) return \"light\";\n const explicit = closest(\"[data-og-theme]\");\n if (explicit) return explicit.getAttribute(\"data-og-theme\") === \"light\" ? \"light\" : \"dark\";\n }\n const scheme = (\n style.getPropertyValue(\"color-scheme\").trim() ||\n style.colorScheme ||\n \"\"\n ).toLowerCase();\n if (scheme.includes(\"light\") && !scheme.includes(\"dark\")) return \"light\";\n return \"dark\";\n}\n\n/**\n * Derive a COMPLETE xterm `ITheme` from the live `--og-*` token system on the\n * DOM. Reads the COMPUTED values so xterm gets concrete colors, detects the\n * active theme mode, and fills every ANSI slot. Call on mount + re-derive on a\n * `data-og-theme` flip. SSR-safe: returns undefined off the DOM.\n *\n * `root` should be an element INSIDE the themed subtree (e.g. the terminal\n * container) so nested `[data-og-theme]` themes resolve correctly; when omitted\n * it falls back to the nearest `[data-og-theme]` element, then documentElement.\n */\nexport function xtermThemeFromTokens(root?: HTMLElement | null): XtermTheme | undefined {\n if (typeof window === \"undefined\" || typeof getComputedStyle === \"undefined\") return undefined;\n const el =\n root ??\n (typeof document !== \"undefined\"\n ? ((document.querySelector(\"[data-og-theme]\") as HTMLElement | null) ??\n document.documentElement)\n : null);\n if (!el) return undefined;\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return buildXtermTheme(read, detectMode(el, style));\n}\n\n// ── Font resolution ──────────────────────────────────────────────────────────\n\nexport type ResolvedTerminalFont = { fontFamily: string; fontSize: number; lineHeight: number };\n\n/** Concrete fallback if the token is unset or (defensively) still a `var()`. */\nconst FALLBACK_MONO = \"ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace\";\n\nexport type FontOverrides = { fontFamily?: string | undefined; fontSize?: number | undefined };\n\n/**\n * Resolve xterm's font from token values. The critical bug this fixes:\n * `fontFamily: \"var(--og-font-mono)\"` is measured by xterm via a canvas font\n * string where `var()` does NOT resolve, so xterm silently mis-measures every\n * glyph. Here the family is a CONCRETE stack; a `var(` in the input is treated\n * as unresolved and replaced with the fallback. Pure over a token `read`.\n */\nexport function resolveTerminalFontFromReader(\n read: TokenReader,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const rawFamily = overrides?.fontFamily ?? read(\"--og-font-mono\");\n const fontFamily =\n rawFamily && rawFamily.trim() && !rawFamily.includes(\"var(\") ? rawFamily.trim() : FALLBACK_MONO;\n\n let fontSize = overrides?.fontSize;\n if (fontSize == null) {\n const raw = read(\"--og-code-font-size\");\n const parsed = raw ? Number.parseFloat(raw) : Number.NaN;\n fontSize = Number.isFinite(parsed) && parsed > 0 ? parsed : 13;\n }\n\n return { fontFamily, fontSize, lineHeight: 1.3 };\n}\n\n/** DOM wrapper: resolve the terminal font from an element's computed tokens. */\nexport function resolveTerminalFont(\n el: HTMLElement,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return resolveTerminalFontFromReader(read, overrides);\n}\n","/* ----------------------------------------------------------------------------\n xterm renderer selection ladder: WebGL → 2D canvas → DOM.\n\n WebGL is the fast path (GPU-composited cells — the whole point of the burst\n responsiveness win). But a WebGL context can fail to init (headless / no GPU /\n blocklisted driver) or be LOST at runtime (GPU reset, tab backgrounded). On\n either, we dispose the addon and step down one tier so the terminal keeps\n painting via the 2D canvas addon, and failing that xterm's built-in DOM\n renderer (no addon).\n\n `attachRenderer` is pure over its `loaders` (each closes over the xterm\n instance and constructs+loads its addon, throwing on failure), so the ladder\n + context-loss downgrade unit-test without a real WebGL context.\n -------------------------------------------------------------------------- */\n\nexport type RendererTier = \"webgl\" | \"canvas\" | \"dom\";\n\nexport type RendererAddon = { dispose: () => void };\n\n/** A loader constructs + `loadAddon`s its renderer, wiring `onLoss` to the\n * addon's context-loss event, and throws if the renderer can't initialize. */\nexport type RendererLoaders = {\n webgl?: ((onLoss: () => void) => Promise<RendererAddon>) | undefined;\n canvas?: (() => Promise<RendererAddon>) | undefined;\n};\n\nconst ORDER: RendererTier[] = [\"webgl\", \"canvas\", \"dom\"];\n\n/** The next lower renderer tier, or null at the bottom (DOM). Pure. */\nexport function nextRendererTier(tier: RendererTier): RendererTier | null {\n const i = ORDER.indexOf(tier);\n return i >= 0 && i < ORDER.length - 1 ? ORDER[i + 1]! : null;\n}\n\n/**\n * Attach the highest renderer tier at or below `prefer` that initializes,\n * reporting the settled tier via `onTier`. On a WebGL/canvas context loss the\n * loader's `onLoss` re-enters at the next tier down, so `onTier` is called again\n * with the downgraded tier. Returns the tier chosen on this pass.\n */\nexport async function attachRenderer(\n prefer: RendererTier,\n loaders: RendererLoaders,\n onTier: (tier: RendererTier) => void,\n): Promise<RendererTier> {\n const start = Math.max(0, ORDER.indexOf(prefer));\n for (let i = start; i < ORDER.length; i++) {\n const tier = ORDER[i]!;\n if (tier === \"dom\") break;\n const loader = tier === \"webgl\" ? loaders.webgl : loaders.canvas;\n if (!loader) continue;\n try {\n const downgrade = nextRendererTier(tier) ?? \"dom\";\n await loader(() => {\n void attachRenderer(downgrade, loaders, onTier);\n });\n onTier(tier);\n return tier;\n } catch {\n // Initialization failed — fall through to the next tier down.\n }\n }\n onTier(\"dom\");\n return \"dom\";\n}\n","import {\n ChevronRightIcon,\n FileIcon,\n FilePlusIcon,\n FolderIcon,\n FolderPlusIcon,\n PencilIcon,\n RefreshCwIcon,\n TriangleAlertIcon,\n Trash2Icon,\n} from \"lucide-react\";\nimport {\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { AlertDialog } from \"radix-ui\";\nimport { VList, type VListHandle } from \"virtua\";\nimport { cn } from \"../lib/cn\";\nimport { type PortalTokenStyle, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport { useUnicodeFallbackFonts } from \"../lib/use-unicode-fonts\";\nimport type { FileTreeNode, UseSandboxFilesResult } from \"../hooks/use-sandbox-files\";\n\nexport type FileBrowserProps = {\n /** From `useSandboxFiles(...)`. */\n result: UseSandboxFilesResult;\n /**\n * Rendered instead of the built-in tree when the file surface is unavailable\n * (e.g. a `FileSystem.available === false` capability). Default: a quiet notice.\n */\n fallback?: ReactNode | undefined;\n /** Selection callback for the preview pane. */\n onSelectFile?: ((path: string) => void) | undefined;\n selectedPath?: string | undefined;\n /** Render-prop to theme/replace a row entirely (the Pierre-swap escape hatch). */\n renderNode?: ((node: FileTreeNode, depth: number, expanded: boolean) => ReactNode) | undefined;\n /** Shown when the tree is empty (no files / not loaded yet). */\n emptyState?: ReactNode | undefined;\n /**\n * Enable the file-manager affordances (toolbar, context menu, drag-drop move,\n * inline rename, delete, new file/folder). Defaults to `true` when the hook\n * exposes the mutation methods; pass `false` for a strictly read-only tree.\n */\n editable?: boolean | undefined;\n /**\n * Confirm a (recursive) delete before it runs. Return `false` to cancel.\n * The default is an accessible, non-blocking confirmation dialog. Supply this\n * callback to delegate confirmation to a host-owned dialog.\n */\n confirmDelete?: ((node: FileTreeNode) => boolean | Promise<boolean>) | undefined;\n className?: string | undefined;\n};\n\nconst STATUS_TINT: Record<NonNullable<FileTreeNode[\"status\"]>, string> = {\n added: \"text-og-status-idle\",\n modified: \"text-og-status-running\",\n deleted: \"text-og-status-failed line-through\",\n renamed: \"text-og-accent\",\n untracked: \"text-og-fg-subtle\",\n};\nconst useBrowserLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Parent dir of a workspace-relative POSIX path (\"\" for a root entry). */\nfunction parentOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i <= 0 ? \"\" : path.slice(0, i);\n}\n\n/** Join a parent dir with a leaf name (handles the root \"\" parent). */\nfunction joinPath(parent: string, name: string): string {\n return parent ? `${parent}/${name}` : name;\n}\n\n/** A stable React key for a flat render item. */\nfunction itemKey(item: RenderItem): string {\n switch (item.type) {\n case \"node\":\n return item.node.path;\n case \"create\":\n return `create:${item.parent}`;\n case \"skeleton\":\n return `skeleton:${item.path}`;\n case \"residue\":\n return `residue:${item.path}`;\n }\n}\n\n/** Walk the tree to find a node by path. */\nfunction findNode(nodes: FileTreeNode[], path: string): FileTreeNode | undefined {\n for (const node of nodes) {\n if (node.path === path) return node;\n if (node.kind === \"dir\" && node.children && path.startsWith(`${node.path}/`)) {\n const hit = findNode(node.children, path);\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** A pending inline-create: a phantom input row under `parent`. */\ntype DraftCreate = { parent: string; kind: \"file\" | \"dir\" };\n/** A pending inline-rename of an existing node. */\ntype DraftRename = { path: string };\ntype ContextMenuState = { node: FileTreeNode | null; x: number; y: number };\n\n/** One virtualized row. Nodes plus the synthetic rows the recursive renderer\n * used to nest inline. */\ntype RenderItem =\n | { type: \"node\"; node: FileTreeNode; depth: number }\n | { type: \"create\"; parent: string; kind: \"file\" | \"dir\"; depth: number }\n | { type: \"skeleton\"; path: string; depth: number }\n | { type: \"residue\"; path: string; depth: number };\n\n/**\n * The file MANAGER, fed by the FileSystem service via `useSandboxFiles`. This is\n * a first-class editable tree (not a render-only view): lazy-expand with a\n * spinner on the in-flight node, git-status tinting, full keyboard navigation,\n * selection, and the mutating affordances wired straight to the hook —\n *\n * • drag-and-drop MOVE → `moveEntry(from, to)`\n * • inline RENAME → `moveEntry(path, newPath)` (F2 / double-click / menu)\n * • DELETE → `deleteEntry(path, recursive)` (Del / menu, confirmed)\n * • NEW FILE / FOLDER → `createFile` / `createDir` (toolbar + menu), then open\n * • right-click CONTEXT MENU\n *\n * `renderNode` is still honoured as the Pierre-swap escape hatch for the row\n * chrome; the manager scaffolding (toolbar, dnd, menu, inline inputs) wraps it.\n */\nexport function FileBrowser({\n result,\n fallback,\n onSelectFile,\n selectedPath,\n renderNode,\n emptyState,\n editable = true,\n confirmDelete,\n className,\n}: FileBrowserProps) {\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n const [loadingPaths, setLoadingPaths] = useState<Set<string>>(new Set());\n const [active, setActive] = useState<string | null>(null);\n const [draftCreate, setDraftCreate] = useState<DraftCreate | null>(null);\n const [draftRename, setDraftRename] = useState<DraftRename | null>(null);\n const [dragOver, setDragOver] = useState<string | null>(null);\n const [menu, setMenu] = useState<ContextMenuState | null>(null);\n const [pendingDelete, setPendingDelete] = useState<FileTreeNode | null>(null);\n const [busy, setBusy] = useState(false);\n const [coarsePointer, setCoarsePointer] = useState(false);\n const treeId = useId();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const deleteReturnFocusRef = useRef<HTMLElement | null>(null);\n const vlistRef = useRef<VListHandle>(null);\n const portalTokenStyle = usePortalTokenStyle(containerRef);\n const getTreeElement = useCallback(\n () => (typeof document === \"undefined\" ? null : document.getElementById(treeId)),\n [treeId],\n );\n\n useBrowserLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const query = window.matchMedia(\"(pointer: coarse)\");\n const update = () => setCoarsePointer(query.matches);\n update();\n if (typeof query.addEventListener === \"function\") {\n query.addEventListener(\"change\", update);\n return () => query.removeEventListener(\"change\", update);\n }\n if (typeof query.addListener === \"function\") {\n query.addListener(update);\n return () => query.removeListener(update);\n }\n }, []);\n\n // The keyboard cursor: an explicitly-navigated node falls back to the\n // externally-selected file so arrow keys pick up where the preview pane is.\n const cursor = active ?? selectedPath ?? null;\n\n const expand = useCallback(\n async (path: string) => {\n const node = findNode(result.tree, path);\n if (node && node.kind === \"dir\" && node.children === undefined) {\n setLoadingPaths((prev) => new Set(prev).add(path));\n try {\n await result.expand(path);\n } finally {\n setLoadingPaths((prev) => {\n const next = new Set(prev);\n next.delete(path);\n return next;\n });\n }\n }\n },\n [result],\n );\n\n const open = useCallback(\n (path: string) => {\n setExpanded((prev) => new Set(prev).add(path));\n void expand(path);\n },\n [expand],\n );\n\n const toggle = useCallback(\n async (node: FileTreeNode) => {\n if (node.kind !== \"dir\") return;\n const isOpen = expanded.has(node.path);\n setExpanded((prev) => {\n const next = new Set(prev);\n if (isOpen) next.delete(node.path);\n else next.add(node.path);\n return next;\n });\n if (!isOpen) await expand(node.path);\n },\n [expanded, expand],\n );\n\n // Flatten the VISIBLE (expanded) tree into a single ordered render list — the\n // input to the virtualizer. Interleaves the synthetic rows the recursive\n // renderer used to nest (inline create input, lazy-expand skeleton, and the\n // cold residue \"contents on machine\" row) so every visual row is one list item.\n const cold = result.source !== \"live\";\n const renderItems = useMemo(() => {\n const items: RenderItem[] = [];\n if (draftCreate && draftCreate.parent === \"\") {\n items.push({ type: \"create\", parent: \"\", kind: draftCreate.kind, depth: 0 });\n }\n const walk = (nodes: FileTreeNode[], depth: number) => {\n for (const node of nodes) {\n items.push({ type: \"node\", node, depth });\n if (node.kind !== \"dir\" || !expanded.has(node.path)) continue;\n if (draftCreate && draftCreate.parent === node.path) {\n items.push({\n type: \"create\",\n parent: node.path,\n kind: draftCreate.kind,\n depth: depth + 1,\n });\n }\n const childless = node.children === undefined || node.children.length === 0;\n const isLoading = loadingPaths.has(node.path) || result.expandingPaths.has(node.path);\n // A collapsed residue dir (node_modules, .git, …) can't list children from\n // the cold capture — offer to open it live rather than a dead skeleton.\n if (node.truncated && childless && cold && !isLoading) {\n items.push({ type: \"residue\", path: node.path, depth: depth + 1 });\n } else if (node.children && node.children.length > 0) {\n walk(node.children, depth + 1);\n } else if (isLoading && childless) {\n items.push({ type: \"skeleton\", path: node.path, depth: depth + 1 });\n }\n }\n };\n walk(result.tree, 0);\n return items;\n }, [result.tree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);\n const unicodeFontPaths = useMemo(\n () => renderItems.flatMap((item) => (item.type === \"node\" ? [item.node.path] : [])),\n [renderItems],\n );\n useUnicodeFallbackFonts(unicodeFontPaths);\n\n // The visible NODE rows in order — drives keyboard up/down navigation.\n const flatRows = useMemo(\n () =>\n renderItems.flatMap((item) =>\n item.type === \"node\" ? [{ node: item.node, depth: item.depth }] : [],\n ),\n [renderItems],\n );\n const rowIndexByPath = useMemo(\n () => new Map(flatRows.map((row, index) => [row.node.path, index] as const)),\n [flatRows],\n );\n const treePositionByPath = useMemo(() => {\n const positions = new Map<string, { position: number; setSize: number }>();\n const visit = (nodes: FileTreeNode[]) => {\n nodes.forEach((node, index) => {\n positions.set(node.path, { position: index + 1, setSize: nodes.length });\n if (node.children) visit(node.children);\n });\n };\n visit(result.tree);\n return positions;\n }, [result.tree]);\n const cursorIndex = cursor ? (rowIndexByPath.get(cursor) ?? -1) : -1;\n const activeDescendantId = cursorIndex >= 0 ? `${treeId}-item-${cursorIndex}` : undefined;\n\n // A capture is immutable review data even if a stale capability document still\n // says the machine is writable. Mutations become available only after a live\n // reconciliation has actually succeeded.\n const supportsMutation =\n editable && result.source === \"live\" && result.error === null && !result.loading;\n\n const performDelete = useCallback(\n async (node: FileTreeNode) => {\n if (!supportsMutation) return;\n const recursive = node.kind === \"dir\";\n setBusy(true);\n try {\n await result.deleteEntry(node.path, recursive);\n } catch {\n // The hook surfaces the error on `result.error`; the panel renders it.\n } finally {\n setBusy(false);\n }\n },\n [supportsMutation, result],\n );\n\n const runDelete = useCallback(\n async (node: FileTreeNode, returnFocus?: HTMLElement) => {\n if (!supportsMutation) return;\n setMenu(null);\n if (!confirmDelete) {\n deleteReturnFocusRef.current =\n returnFocus ??\n (typeof document !== \"undefined\" && document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null);\n setPendingDelete(node);\n return;\n }\n if (await confirmDelete(node)) await performDelete(node);\n },\n [supportsMutation, confirmDelete, performDelete],\n );\n\n const closePendingDelete = useCallback(\n (restoreTrigger: boolean) => {\n const returnTarget = restoreTrigger ? deleteReturnFocusRef.current : getTreeElement();\n deleteReturnFocusRef.current = null;\n setPendingDelete(null);\n if (typeof window !== \"undefined\") {\n window.requestAnimationFrame(() => {\n const target = returnTarget?.isConnected ? returnTarget : getTreeElement();\n target?.focus();\n });\n }\n },\n [getTreeElement],\n );\n\n const commitCreate = useCallback(\n async (name: string) => {\n const draft = draftCreate;\n setDraftCreate(null);\n if (!draft || !supportsMutation) return;\n const trimmed = name.trim().replace(/\\/+$/, \"\");\n if (!trimmed) return;\n const path = joinPath(draft.parent, trimmed);\n setBusy(true);\n try {\n if (draft.kind === \"dir\") {\n await result.createDir(path);\n open(path);\n } else {\n await result.createFile(path);\n // Open the freshly-created file in the preview/editor pane.\n setActive(path);\n onSelectFile?.(path);\n }\n } catch {\n /* error surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [draftCreate, supportsMutation, result, open, onSelectFile],\n );\n\n const commitRename = useCallback(\n async (name: string) => {\n const draft = draftRename;\n setDraftRename(null);\n if (!draft || !supportsMutation) return;\n const node = findNode(result.tree, draft.path);\n const trimmed = name.trim().replace(/\\/+$/, \"\");\n if (!node || !trimmed || trimmed === node.name) return;\n const newPath = joinPath(parentOf(draft.path), trimmed);\n setBusy(true);\n try {\n await result.moveEntry(draft.path, newPath);\n if (node.kind === \"file\" && (selectedPath === draft.path || active === draft.path)) {\n setActive(newPath);\n onSelectFile?.(newPath);\n }\n } catch {\n /* error surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [draftRename, supportsMutation, result, selectedPath, active, onSelectFile],\n );\n\n // Begin a new file/folder under the active node's directory (or root).\n const startCreate = useCallback(\n (kind: \"file\" | \"dir\") => {\n if (!supportsMutation) return;\n const anchor = cursor ? findNode(result.tree, cursor) : undefined;\n const parent = anchor ? (anchor.kind === \"dir\" ? anchor.path : parentOf(anchor.path)) : \"\";\n if (parent) open(parent);\n setDraftRename(null);\n setMenu(null);\n setDraftCreate({ parent, kind });\n },\n [supportsMutation, cursor, result.tree, open],\n );\n\n const startRename = useCallback(\n (node: FileTreeNode) => {\n if (!supportsMutation) return;\n setDraftCreate(null);\n setMenu(null);\n setDraftRename({ path: node.path });\n },\n [supportsMutation],\n );\n\n // Drag-drop move: drop a node onto a directory (or the root) → moveEntry.\n const onDropOnto = useCallback(\n async (targetDir: string, sourcePath: string) => {\n setDragOver(null);\n if (!supportsMutation || !sourcePath) return;\n const src = findNode(result.tree, sourcePath);\n if (!src) return;\n // No-op drops: onto its own current parent, onto itself, or into its own subtree.\n if (parentOf(sourcePath) === targetDir) return;\n if (targetDir === sourcePath || targetDir.startsWith(`${sourcePath}/`)) return;\n const newPath = joinPath(targetDir, src.name);\n if (newPath === sourcePath) return;\n setBusy(true);\n try {\n await result.moveEntry(sourcePath, newPath);\n if (src.kind === \"file\" && (selectedPath === sourcePath || active === sourcePath)) {\n setActive(newPath);\n onSelectFile?.(newPath);\n }\n } catch {\n /* 409/collision etc. surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [supportsMutation, result, selectedPath, active, onSelectFile],\n );\n\n const onKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (draftCreate || draftRename) return; // inline input owns the keyboard\n if (flatRows.length === 0) return;\n const idx = flatRows.findIndex((r) => r.node.path === cursor);\n const move = (next: number) => {\n const clamped = Math.max(0, Math.min(flatRows.length - 1, next));\n const row = flatRows[clamped];\n if (row) setActive(row.node.path);\n e.preventDefault();\n };\n const cur = idx >= 0 ? flatRows[idx] : undefined;\n switch (e.key) {\n case \"ArrowDown\":\n move(idx < 0 ? 0 : idx + 1);\n break;\n case \"ArrowUp\":\n move(idx < 0 ? 0 : idx - 1);\n break;\n case \"Home\":\n move(0);\n break;\n case \"End\":\n move(flatRows.length - 1);\n break;\n case \"PageDown\": {\n const pageSize = Math.max(\n 1,\n Math.floor((containerRef.current?.clientHeight || 280) / (coarsePointer ? 44 : 28)) - 1,\n );\n move((idx < 0 ? 0 : idx) + pageSize);\n break;\n }\n case \"PageUp\": {\n const pageSize = Math.max(\n 1,\n Math.floor((containerRef.current?.clientHeight || 280) / (coarsePointer ? 44 : 28)) - 1,\n );\n move((idx < 0 ? 0 : idx) - pageSize);\n break;\n }\n case \"ArrowRight\":\n if (cur?.node.kind === \"dir\") {\n if (!expanded.has(cur.node.path)) open(cur.node.path);\n else move(idx + 1);\n e.preventDefault();\n }\n break;\n case \"ArrowLeft\":\n if (cur) {\n if (cur.node.kind === \"dir\" && expanded.has(cur.node.path)) {\n setExpanded((prev) => {\n const n = new Set(prev);\n n.delete(cur.node.path);\n return n;\n });\n } else {\n const parent = parentOf(cur.node.path);\n if (parent) setActive(parent);\n }\n e.preventDefault();\n }\n break;\n case \"Enter\":\n if (cur) {\n if (cur.node.kind === \"dir\") void toggle(cur.node);\n else onSelectFile?.(cur.node.path);\n e.preventDefault();\n }\n break;\n case \"ContextMenu\":\n case \"F10\":\n if (cur && supportsMutation && (e.key === \"ContextMenu\" || e.shiftKey)) {\n const rowIndex = rowIndexByPath.get(cur.node.path) ?? 0;\n const row = document.getElementById(`${treeId}-item-${rowIndex}`);\n const rect = row?.getBoundingClientRect();\n setMenu({\n node: cur.node,\n x: rect ? rect.left + Math.min(32, rect.width / 2) : 8,\n y: rect ? rect.bottom : 8,\n });\n e.preventDefault();\n } else if (e.key === \"F10\") {\n break;\n }\n break;\n case \"F2\":\n if (cur) {\n startRename(cur.node);\n e.preventDefault();\n }\n break;\n case \"Delete\":\n case \"Backspace\":\n if (cur && supportsMutation) {\n void runDelete(cur.node);\n e.preventDefault();\n }\n break;\n }\n },\n [\n draftCreate,\n draftRename,\n flatRows,\n cursor,\n expanded,\n open,\n toggle,\n onSelectFile,\n startRename,\n runDelete,\n supportsMutation,\n coarsePointer,\n rowIndexByPath,\n treeId,\n ],\n );\n\n // Close the context menu on any outside interaction.\n useEffect(() => {\n if (!menu) return;\n const close = () => setMenu(null);\n window.addEventListener(\"click\", close);\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return () => {\n window.removeEventListener(\"click\", close);\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n }, [menu]);\n\n const showErrorEmpty = result.error && result.tree.length === 0 && !draftCreate;\n const showDegradedTree = result.error && result.tree.length > 0;\n const showEmpty = !result.loading && result.tree.length === 0 && !draftCreate;\n // Retry lives next to the state it explains (the degraded/empty notice), so do\n // not render a second toolbar retry for the same failure.\n const showToolbar = supportsMutation || result.loading;\n const usesVirtualTree = !showErrorEmpty && !showEmpty;\n\n // Render a SINGLE node row (no recursion — children are separate flat items the\n // virtualizer renders). Returns the row element; the caller assigns the key.\n const renderNodeRow = (node: FileTreeNode, depth: number): ReactNode => {\n const isOpen = expanded.has(node.path);\n if (renderNode) {\n return <div>{renderNode(node, depth, isOpen)}</div>;\n }\n const isDir = node.kind === \"dir\";\n const isSelected = node.path === selectedPath || node.path === active;\n const isCursor = node.path === cursor;\n const isLoading = loadingPaths.has(node.path) || result.expandingPaths.has(node.path);\n const isRenaming = draftRename?.path === node.path;\n const isDropTarget = dragOver === (isDir ? node.path : parentOf(node.path));\n const dropDir = isDir ? node.path : parentOf(node.path);\n const treePosition = treePositionByPath.get(node.path);\n\n return (\n <div\n id={`${treeId}-item-${rowIndexByPath.get(node.path) ?? 0}`}\n role=\"treeitem\"\n aria-level={depth + 1}\n aria-posinset={treePosition?.position}\n aria-setsize={treePosition?.setSize}\n aria-expanded={isDir ? isOpen : undefined}\n aria-selected={isCursor || undefined}\n aria-busy={isLoading || undefined}\n onDragOver={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(dropDir);\n }\n : undefined\n }\n onDragLeave={\n supportsMutation\n ? (e) => {\n e.stopPropagation();\n setDragOver((cur) => (cur === dropDir ? null : cur));\n }\n : undefined\n }\n onDrop={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.stopPropagation();\n const src = e.dataTransfer.getData(\"text/og-path\");\n void onDropOnto(dropDir, src);\n }\n : undefined\n }\n >\n {isRenaming ? (\n <InlineInput\n depth={depth}\n kind={node.kind}\n initialValue={node.name}\n onCommit={(name) => void commitRename(name)}\n onCancel={() => setDraftRename(null)}\n />\n ) : (\n <button\n type=\"button\"\n tabIndex={-1}\n draggable={supportsMutation || undefined}\n onDragStart={\n supportsMutation\n ? (e) => {\n e.dataTransfer.setData(\"text/og-path\", node.path);\n e.dataTransfer.effectAllowed = \"move\";\n }\n : undefined\n }\n onClick={() => {\n setActive(node.path);\n if (isDir) void toggle(node);\n else onSelectFile?.(node.path);\n }}\n onDoubleClick={() => supportsMutation && startRename(node)}\n onContextMenu={(e) => {\n if (!supportsMutation) return;\n e.preventDefault();\n setActive(node.path);\n setMenu({ node, x: e.clientX, y: e.clientY });\n }}\n className={cn(\n \"group relative flex min-h-7 w-full items-center gap-1 truncate rounded-og-sm px-1 py-0.5 text-left text-og-sm pointer-coarse:min-h-11\",\n \"hover:bg-og-surface-2\",\n isSelected && \"bg-og-surface-2\",\n isCursor && \"outline outline-1 -outline-offset-1 outline-og-accent\",\n isDropTarget && \"bg-og-accent-soft ring-1 ring-inset ring-og-accent\",\n // A thin progress shimmer on the expanding row — no per-node spinner.\n isLoading &&\n \"after:pointer-events-none after:absolute after:inset-x-1 after:bottom-0 after:h-px after:animate-pulse after:rounded-full after:bg-og-accent-soft\",\n node.status ? STATUS_TINT[node.status] : undefined,\n )}\n style={{ paddingLeft: `${depth * 12 + 4}px` }}\n >\n {isDir ? (\n <ChevronRightIcon\n className={cn(\n \"size-3 shrink-0 transition-transform\",\n isOpen && \"rotate-90\",\n isLoading && \"text-og-accent\",\n )}\n />\n ) : (\n <span className=\"inline-block w-3 shrink-0\" />\n )}\n {isDir ? (\n <FolderIcon className=\"size-3.5 shrink-0\" />\n ) : (\n <FileIcon className=\"size-3.5 shrink-0\" />\n )}\n <span className=\"truncate\">{node.name}</span>\n {supportsMutation && (\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label=\"More actions\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setActive(node.path);\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n setMenu({ node, x: rect.right, y: rect.bottom });\n }}\n className=\"ml-auto hidden shrink-0 px-1 text-og-fg-subtle hover:text-og-fg group-hover:inline\"\n >\n ⋯\n </span>\n )}\n </button>\n )}\n </div>\n );\n };\n\n // Render one flat list item (node row or a synthetic create/skeleton/residue row).\n const renderItem = (item: RenderItem): ReactNode => {\n switch (item.type) {\n case \"node\":\n return renderNodeRow(item.node, item.depth);\n case \"create\":\n return (\n <InlineInput\n depth={item.depth}\n kind={item.kind}\n initialValue=\"\"\n onCommit={(name) => void commitCreate(name)}\n onCancel={() => setDraftCreate(null)}\n />\n );\n case \"skeleton\":\n return (\n <div\n role=\"group\"\n aria-hidden\n style={{ paddingLeft: `${item.depth * 12 + 4}px` }}\n className=\"space-y-1 py-1\"\n >\n {[0, 1, 2].map((i) => (\n <div\n key={i}\n className=\"h-2.5 animate-pulse rounded-og-sm bg-og-surface-2\"\n style={{ width: `${70 - i * 12}%` }}\n />\n ))}\n </div>\n );\n case \"residue\":\n return (\n <div\n style={{ paddingLeft: `${item.depth * 12 + 4}px` }}\n className=\"flex items-center gap-1.5 py-0.5 pr-1 text-og-xs italic text-og-fg-subtle\"\n >\n <span className=\"inline-block w-3 shrink-0\" />\n <span className=\"min-w-0 truncate\">contents on machine — open when live</span>\n </div>\n );\n }\n };\n\n // Keep the keyboard cursor's row on screen even though off-screen rows unmount.\n useEffect(() => {\n if (!cursor) return;\n const index = renderItems.findIndex((it) => it.type === \"node\" && it.node.path === cursor);\n if (index >= 0) vlistRef.current?.scrollToIndex(index);\n }, [cursor, renderItems]);\n\n return (\n <div className={cn(\"flex min-h-0 min-w-0 flex-col\", className)}>\n {showToolbar && (\n <div\n role=\"toolbar\"\n aria-label=\"File actions\"\n className=\"flex shrink-0 flex-wrap items-center gap-0.5 border-b border-og-border px-1 py-1\"\n >\n {supportsMutation ? (\n <>\n <ToolbarButton label=\"New file\" onClick={() => startCreate(\"file\")} disabled={busy}>\n <FilePlusIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton label=\"New folder\" onClick={() => startCreate(\"dir\")} disabled={busy}>\n <FolderPlusIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n label=\"Rename\"\n onClick={() => {\n const node = cursor ? findNode(result.tree, cursor) : undefined;\n if (node) startRename(node);\n }}\n disabled={busy || !cursor}\n >\n <PencilIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n label=\"Delete\"\n onClick={(event) => {\n const node = cursor ? findNode(result.tree, cursor) : undefined;\n if (node) void runDelete(node, event.currentTarget);\n }}\n disabled={busy || !cursor}\n >\n <Trash2Icon className=\"size-3.5\" />\n </ToolbarButton>\n </>\n ) : null}\n <span className=\"ml-auto\" />\n <ToolbarButton\n label=\"Refresh\"\n onClick={() => void result.refresh()}\n disabled={busy || result.loading}\n >\n <RefreshCwIcon className={cn(\"size-3.5\", result.loading && \"animate-spin\")} />\n </ToolbarButton>\n </div>\n )}\n\n {showDegradedTree ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-opengeni-files-degraded\n className=\"flex shrink-0 items-center gap-2 border-b border-og-status-running/30 bg-og-status-running/10 px-2 py-1.5 text-og-xs text-og-fg-muted\"\n >\n <TriangleAlertIcon className=\"size-3.5 shrink-0 text-og-status-running\" aria-hidden />\n <span className=\"min-w-0 flex-1\">\n {result.source === \"capture\"\n ? \"Live files are temporarily unavailable. Showing the latest captured revision.\"\n : \"Live refresh failed. Showing the last loaded files.\"}\n </span>\n <button\n type=\"button\"\n onClick={() => void result.refresh()}\n disabled={result.loading}\n className=\"inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3\", result.loading && \"animate-spin motion-reduce:animate-none\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : null}\n\n {/* The tree itself. The root is a drop target so a node can be moved to \"\". */}\n {/* biome-ignore lint/a11y/noNoninteractiveTabindex: the tree owns keyboard nav */}\n <div\n ref={containerRef}\n id={usesVirtualTree ? undefined : treeId}\n role={usesVirtualTree ? undefined : \"tree\"}\n aria-label={usesVirtualTree ? undefined : \"Workspace files\"}\n aria-activedescendant={usesVirtualTree ? undefined : activeDescendantId}\n tabIndex={usesVirtualTree ? undefined : 0}\n aria-multiselectable={usesVirtualTree ? undefined : false}\n onFocus={(event) => {\n if ((event.target as HTMLElement).id === treeId && cursorIndex < 0) {\n const first = flatRows[0];\n if (first) setActive(first.node.path);\n }\n }}\n onKeyDown={onKeyDown}\n onDragOver={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(\"\");\n }\n : undefined\n }\n onDrop={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n const src = e.dataTransfer.getData(\"text/og-path\");\n void onDropOnto(\"\", src);\n }\n : undefined\n }\n className={cn(\n \"flex min-h-0 min-w-0 flex-1 flex-col p-1 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent\",\n dragOver === \"\" && \"ring-1 ring-inset ring-og-accent\",\n )}\n data-opengeni-file-tree\n >\n {showErrorEmpty ? (\n <div className=\"flex flex-col items-start gap-2 p-2 text-og-sm text-og-fg-subtle\">\n <div>\n {fallback ??\n `Could not load files: ${result.error?.message ?? \"refresh the file list to try again\"}`}\n </div>\n <button\n type=\"button\"\n onClick={() => void result.refresh()}\n disabled={result.loading}\n className=\"inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3.5\", result.loading && \"animate-spin\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : showEmpty ? (\n <div className=\"p-2 text-og-sm text-og-fg-subtle\">\n {emptyState ?? \"This directory is empty\"}\n </div>\n ) : (\n // Virtualized: only the rows in/near the viewport mount, so a 3k-node\n // tree stays responsive. The flat item list already carries the\n // expanded subtree + synthetic (create/skeleton/residue) rows.\n <VList\n ref={vlistRef}\n id={treeId}\n role=\"tree\"\n aria-label=\"Workspace files\"\n aria-activedescendant={activeDescendantId}\n aria-multiselectable={false}\n tabIndex={0}\n className=\"min-h-0 flex-1 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent\"\n itemSize={coarsePointer ? 44 : 28}\n ssrCount={Math.min(32, renderItems.length)}\n >\n {renderItems.map((item) => (\n <div key={itemKey(item)} className=\"min-w-0\">\n {renderItem(item)}\n </div>\n ))}\n </VList>\n )}\n </div>\n\n {menu && menu.node && (\n <ContextMenu\n node={menu.node}\n x={menu.x}\n y={menu.y}\n onRename={() => menu.node && startRename(menu.node)}\n onDelete={() => menu.node && void runDelete(menu.node)}\n onNewFile={() => startCreate(\"file\")}\n onNewFolder={() => startCreate(\"dir\")}\n onClose={() => {\n setMenu(null);\n window.requestAnimationFrame(() => getTreeElement()?.focus());\n }}\n />\n )}\n {pendingDelete ? (\n <DeleteDialog\n node={pendingDelete}\n busy={busy}\n portalTokenStyle={portalTokenStyle}\n onCancel={() => closePendingDelete(true)}\n onConfirm={() => {\n const node = pendingDelete;\n closePendingDelete(false);\n void performDelete(node);\n }}\n />\n ) : null}\n </div>\n );\n}\n\nfunction DeleteDialog({\n node,\n busy,\n portalTokenStyle,\n onCancel,\n onConfirm,\n}: {\n node: FileTreeNode;\n busy: boolean;\n portalTokenStyle: PortalTokenStyle;\n onCancel: () => void;\n onConfirm: () => void;\n}) {\n const cancelRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(true);\n const closeIntentRef = useRef<\"cancel\" | \"confirm\">(\"cancel\");\n const settledRef = useRef(false);\n const settleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const settle = () => {\n if (settledRef.current) return;\n settledRef.current = true;\n if (closeIntentRef.current === \"confirm\") {\n onConfirm();\n } else {\n onCancel();\n }\n };\n\n const scheduleSettle = () => {\n if (settleTimerRef.current !== null) return;\n settleTimerRef.current = setTimeout(() => {\n settleTimerRef.current = null;\n settle();\n }, 0);\n };\n\n useEffect(\n () => () => {\n if (settleTimerRef.current !== null) clearTimeout(settleTimerRef.current);\n },\n [],\n );\n\n return (\n <AlertDialog.Root\n open={open}\n onOpenChange={(nextOpen) => {\n if (!nextOpen && !busy) {\n setOpen(false);\n scheduleSettle();\n }\n }}\n >\n <AlertDialog.Portal container={typeof document === \"undefined\" ? null : document.body}>\n <AlertDialog.Overlay className=\"fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]\" />\n <AlertDialog.Content\n className=\"og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-none\"\n style={portalTokenStyle}\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n cancelRef.current?.focus();\n }}\n onCloseAutoFocus={(event) => {\n event.preventDefault();\n scheduleSettle();\n }}\n onEscapeKeyDown={(event) => {\n event.stopPropagation();\n if (busy) {\n event.preventDefault();\n } else {\n closeIntentRef.current = \"cancel\";\n }\n }}\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-full bg-og-status-failed/12 text-og-status-failed\">\n <Trash2Icon className=\"size-4\" aria-hidden />\n </span>\n <div className=\"min-w-0\">\n <AlertDialog.Title className=\"text-og-sm font-semibold\">\n Delete {node.kind === \"dir\" ? \"folder\" : \"file\"}?\n </AlertDialog.Title>\n <AlertDialog.Description className=\"mt-1 text-og-sm leading-relaxed text-og-fg-muted\">\n <span className=\"break-all font-og-mono text-og-fg\">{node.path}</span>\n {node.kind === \"dir\"\n ? \" and everything inside it will be permanently removed.\"\n : \" will be permanently removed.\"}\n </AlertDialog.Description>\n </div>\n </div>\n <div className=\"mt-4 flex justify-end gap-2\">\n <AlertDialog.Cancel asChild>\n <button\n ref={cancelRef}\n type=\"button\"\n onClick={() => {\n closeIntentRef.current = \"cancel\";\n }}\n disabled={busy}\n className=\"min-h-9 rounded-og-md border border-og-border px-3 text-og-sm font-medium text-og-fg hover:bg-og-surface-2 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Cancel\n </button>\n </AlertDialog.Cancel>\n <AlertDialog.Action asChild>\n <button\n type=\"button\"\n onClick={() => {\n closeIntentRef.current = \"confirm\";\n }}\n disabled={busy}\n className=\"min-h-9 rounded-og-md bg-og-status-failed px-3 text-og-sm font-semibold text-white shadow-sm hover:brightness-110 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Delete permanently\n </button>\n </AlertDialog.Action>\n </div>\n </AlertDialog.Content>\n </AlertDialog.Portal>\n </AlertDialog.Root>\n );\n}\n\nfunction ToolbarButton({\n label,\n onClick,\n disabled,\n children,\n}: {\n label: string;\n onClick: (event: ReactMouseEvent<HTMLButtonElement>) => void;\n disabled?: boolean;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n onClick={onClick}\n disabled={disabled}\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-og-sm p-1 text-og-fg-muted max-[1023px]:size-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n \"hover:bg-og-surface-2 hover:text-og-fg\",\n \"disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n >\n {children}\n </button>\n );\n}\n\n/** An inline text input for create / rename, indented to match its row depth. */\nfunction InlineInput({\n depth,\n kind,\n initialValue,\n onCommit,\n onCancel,\n}: {\n depth: number;\n kind: \"file\" | \"dir\";\n initialValue: string;\n onCommit: (value: string) => void;\n onCancel: () => void;\n}) {\n const [value, setValue] = useState(initialValue);\n const ref = useRef<HTMLInputElement | null>(null);\n // Track whether we've already resolved so a blur after Enter/Escape is a no-op.\n const doneRef = useRef(false);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.focus();\n // Select the basename (excluding any extension) for a rename, like an IDE.\n const dot = initialValue.lastIndexOf(\".\");\n if (initialValue && dot > 0) el.setSelectionRange(0, dot);\n else el.select();\n }, [initialValue]);\n\n const finish = (commit: boolean) => {\n if (doneRef.current) return;\n doneRef.current = true;\n if (commit) onCommit(value);\n else onCancel();\n };\n\n return (\n <div\n className=\"flex items-center gap-1 px-1 py-0.5\"\n style={{ paddingLeft: `${depth * 12 + 4}px` }}\n >\n <span className=\"inline-block w-3 shrink-0\" />\n {kind === \"dir\" ? (\n <FolderIcon className=\"size-3.5 shrink-0\" />\n ) : (\n <FileIcon className=\"size-3.5 shrink-0\" />\n )}\n <input\n ref={ref}\n value={value}\n spellCheck={false}\n autoComplete=\"off\"\n aria-label={kind === \"dir\" ? \"Folder name\" : \"File name\"}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => finish(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n finish(true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n finish(false);\n }\n e.stopPropagation();\n }}\n className={cn(\n \"min-w-0 flex-1 rounded-og-sm border bg-og-bg px-1 py-0 text-og-sm\",\n \"border-og-accent text-og-fg\",\n \"outline-none\",\n )}\n />\n </div>\n );\n}\n\n/** The right-click / kebab context menu, positioned at the click point. */\nfunction ContextMenu({\n node,\n x,\n y,\n onRename,\n onDelete,\n onNewFile,\n onNewFolder,\n onClose,\n}: {\n node: FileTreeNode;\n x: number;\n y: number;\n onRename: () => void;\n onDelete: () => void;\n onNewFile: () => void;\n onNewFolder: () => void;\n onClose: () => void;\n}) {\n const isDir = node.kind === \"dir\";\n const menuRef = useRef<HTMLDivElement | null>(null);\n // Keep the menu on-screen when opened near the viewport edge.\n const vw = typeof window !== \"undefined\" ? window.innerWidth : 9999;\n const vh = typeof window !== \"undefined\" ? window.innerHeight : 9999;\n const left = Math.max(4, Math.min(x, vw - 180));\n const top = Math.max(4, Math.min(y, vh - 160));\n\n useEffect(() => {\n menuRef.current?.querySelector<HTMLButtonElement>('[role=\"menuitem\"]')?.focus();\n }, []);\n\n const onKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const items = Array.from(\n menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]') ?? [],\n );\n if (items.length === 0) return;\n const current = items.findIndex((item) => item === document.activeElement);\n let next: number | null = null;\n if (event.key === \"ArrowDown\") next = (current + 1 + items.length) % items.length;\n else if (event.key === \"ArrowUp\") next = (current - 1 + items.length) % items.length;\n else if (event.key === \"Home\") next = 0;\n else if (event.key === \"End\") next = items.length - 1;\n else if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n onClose();\n return;\n }\n if (next === null) return;\n event.preventDefault();\n event.stopPropagation();\n items[next]?.focus();\n };\n\n const item = (label: string, icon: ReactNode, onClick: () => void, danger?: boolean) => (\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={(e) => {\n e.stopPropagation();\n onClick();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-2.5 py-1 text-left text-og-sm pointer-coarse:min-h-11\",\n \"hover:bg-og-surface-2\",\n danger ? \"text-og-status-failed\" : \"text-og-fg\",\n )}\n >\n {icon}\n {label}\n </button>\n );\n\n return (\n <div\n ref={menuRef}\n role=\"menu\"\n aria-label={`Actions for ${node.name}`}\n onKeyDown={onKeyDown}\n onClick={(e) => e.stopPropagation()}\n style={{ left, top }}\n className={cn(\n \"fixed z-50 min-w-[160px] overflow-hidden rounded-md border py-1 shadow-lg\",\n \"border-og-border\",\n \"bg-og-surface-1\",\n )}\n >\n {isDir && (\n <>\n {item(\"New file\", <FilePlusIcon className=\"size-3.5\" />, onNewFile)}\n {item(\"New folder\", <FolderPlusIcon className=\"size-3.5\" />, onNewFolder)}\n <div role=\"separator\" className=\"my-1 h-px bg-og-border\" />\n </>\n )}\n {item(\"Rename\", <PencilIcon className=\"size-3.5\" />, onRename)}\n {item(\"Delete\", <Trash2Icon className=\"size-3.5\" />, onDelete, true)}\n </div>\n );\n}\n","import { useEffect, useMemo } from \"react\";\n\nexport type UnicodeFontNeeds = {\n japanese: boolean;\n arabic: boolean;\n};\n\nconst japaneseScript = /[\\u3040-\\u30ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff]/u;\nconst arabicScript = /[\\u0600-\\u06ff\\u0750-\\u077f\\u0870-\\u08ff\\ufb50-\\ufdff\\ufe70-\\ufefc]/u;\n\nexport function detectUnicodeFontNeeds(values: Iterable<string>): UnicodeFontNeeds {\n let japanese = false;\n let arabic = false;\n for (const value of values) {\n japanese ||= japaneseScript.test(value);\n arabic ||= arabicScript.test(value);\n if (japanese && arabic) break;\n }\n return { japanese, arabic };\n}\n\n/**\n * Load large script-specific font manifests only when visible workspace content\n * needs them. Fontsource splits the actual glyphs by unicode-range, so Chromium\n * then fetches only the small shards used by the path instead of charging every\n * Latin-only session for the complete font.\n */\nexport function useUnicodeFallbackFonts(values: readonly string[]): void {\n const content = useMemo(() => collectUnicodeContent(values), [values]);\n useEffect(() => {\n if (content.japanese) {\n void import(\"./noto-sans-jp-loader.generated\")\n .then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese))\n .catch(() => undefined);\n }\n if (content.arabic) {\n void import(\"./noto-sans-arabic-loader\")\n .then(({ loadNotoSansArabic }) => loadNotoSansArabic())\n .catch(() => undefined);\n }\n }, [content.arabic, content.japanese]);\n}\n\nfunction collectUnicodeContent(values: Iterable<string>): {\n japanese: string;\n arabic: string;\n} {\n const japanese = new Set<string>();\n const arabic = new Set<string>();\n for (const value of values) {\n for (const character of value) {\n if (japaneseScript.test(character)) japanese.add(character);\n if (arabicScript.test(character)) arabic.add(character);\n }\n }\n return { japanese: [...japanese].join(\"\"), arabic: [...arabic].join(\"\") };\n}\n","import type { GitFileDiff } from \"@opengeni/sdk\";\nimport { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from \"lucide-react\";\nimport {\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { VList } from \"virtua\";\nimport { cn } from \"../lib/cn\";\nimport { useUnicodeFallbackFonts } from \"../lib/use-unicode-fonts\";\nimport { useThemeType } from \"../lib/use-theme-type\";\nimport { formatAsOf } from \"../hooks/use-machine-chip\";\nimport type { SandboxGitFileDiff } from \"../hooks/use-sandbox-git\";\nimport { useWindowedSections } from \"../hooks/use-windowed-sections\";\nimport { PierreDiff } from \"./pierre-diff\";\n\n/* ----------------------------------------------------------------------------\n Changes tab — a PR-review surface.\n\n A file rail (status glyph, ±counts, grouped by repository when metadata is\n available, otherwise by top-level directory for a large legacy input) on the\n left; a stacked, WINDOWED diff pane on the right. Only\n the file sections inside the visible window ± overscan mount a Pierre/Shiki\n highlighter (the one renderer), so a 40- or 400-file change set never mounts N\n highlighters at once (D2). A per-file size guard (binary / diff too\n large) degrades to an \"open live\" affordance instead of a diff body.\n\n Workspace-wide Git results carry an explicit `repoRoot`, so grouping never\n guesses repository ownership from a directory name. Multi-repo results group\n at every size; legacy unscoped inputs group by top-level directory only past\n the threshold.\n -------------------------------------------------------------------------- */\n\n/** Group the rail (and reorder the pane) once the change set is larger than this. */\nconst GROUP_THRESHOLD = 20;\n/** Mount this many sections beyond the visible window on each side. */\nconst OVERSCAN = 2;\nconst HEADER_PX = 30;\nconst LINE_PX = 18;\nconst GUARD_BODY_PX = 52;\nconst COMPACT_SURFACE_PX = 560;\nconst useChangesLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nconst STATUS_TINT: Record<GitFileDiff[\"status\"], string> = {\n added: \"text-og-status-idle\",\n modified: \"text-og-status-running\",\n deleted: \"text-og-status-failed\",\n renamed: \"text-og-accent\",\n copied: \"text-og-accent\",\n untracked: \"text-og-fg-subtle\",\n ignored: \"text-og-fg-subtle\",\n conflicted: \"text-og-status-failed\",\n typechange: \"text-og-status-running\",\n};\n\nconst STATUS_LETTER: Record<GitFileDiff[\"status\"], string> = {\n added: \"A\",\n modified: \"M\",\n deleted: \"D\",\n renamed: \"R\",\n copied: \"C\",\n untracked: \"U\",\n ignored: \"I\",\n conflicted: \"!\",\n typechange: \"T\",\n};\n\nfunction compactFileLabel(file: SandboxGitFileDiff): string {\n const slash = file.path.lastIndexOf(\"/\");\n const basename = slash >= 0 ? file.path.slice(slash + 1) : file.path;\n const parent = slash >= 0 ? file.path.slice(0, slash + 1) : \"\";\n return `${STATUS_LETTER[file.status]} · ${basename}${parent ? ` — ${parent}` : \"\"}`;\n}\n\nexport type WorkbenchChangesProps = {\n /** The changed files (`useSandboxGit().diff`). Assumed non-empty by the caller. */\n diff: SandboxGitFileDiff[];\n /** Which source the diff came from — drives the \"live\" vs \"as of turn\" badge. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n /** The capture's turn revision, for the \"as of turn N\" badge. */\n captureRevision?: number | null | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Route a guarded binary/oversize file into the live Files surface. */\n onOpenFile?: ((path: string) => void) | undefined;\n className?: string | undefined;\n};\n\ntype RailRow =\n | { kind: \"group\"; label: string; count: number }\n | { kind: \"file\"; file: SandboxGitFileDiff; index: number };\n\n/** Order the files and build the rail rows (grouped past the threshold). The\n * returned `orderedFiles` drives BOTH the rail and the diff pane so a rail row's\n * `index` addresses the matching pane section. Exported for tests. */\nexport function buildRail(files: SandboxGitFileDiff[]): {\n orderedFiles: SandboxGitFileDiff[];\n rows: RailRow[];\n grouped: boolean;\n} {\n const roots = new Set(\n files.flatMap((file) => (file.repoRoot === undefined ? [] : [file.repoRoot])),\n );\n const groupByRepo = roots.size > 1;\n if (!groupByRepo && files.length <= GROUP_THRESHOLD) {\n return {\n orderedFiles: files,\n rows: files.map((file, index) => ({ kind: \"file\", file, index })),\n grouped: false,\n };\n }\n const groups = new Map<string, SandboxGitFileDiff[]>();\n for (const file of files) {\n const slash = file.path.indexOf(\"/\");\n const key = groupByRepo\n ? file.repoRoot || \"(workspace)\"\n : slash > 0\n ? file.path.slice(0, slash)\n : \"(root)\";\n const bucket = groups.get(key);\n if (bucket) bucket.push(file);\n else groups.set(key, [file]);\n }\n const labels = [...groups.keys()].sort((a, b) => a.localeCompare(b));\n const orderedFiles: SandboxGitFileDiff[] = [];\n const rows: RailRow[] = [];\n for (const label of labels) {\n const bucket = (groups.get(label) ?? []).slice().sort((a, b) => a.path.localeCompare(b.path));\n rows.push({ kind: \"group\", label, count: bucket.length });\n for (const file of bucket) {\n rows.push({ kind: \"file\", file, index: orderedFiles.length });\n orderedFiles.push(file);\n }\n }\n return { orderedFiles, rows, grouped: true };\n}\n\n/** A file's diff body is \"too large to inline\" when it's binary or the diff guard\n * tripped (the backend omits hunks past its size cap) — open it live instead. */\nfunction isGuarded(file: GitFileDiff): boolean {\n return file.isBinary || file.truncated;\n}\n\n/** Estimate a section's rendered height for the windowing math. Line-count based,\n * so it is close enough that the first paint barely shifts as real heights land. */\nfunction estimateSectionHeight(file: GitFileDiff): number {\n if (isGuarded(file)) return HEADER_PX + GUARD_BODY_PX;\n let lines = 0;\n for (const hunk of file.hunks) lines += hunk.lines.length + 1;\n return HEADER_PX + Math.max(lines, 1) * LINE_PX + 8;\n}\n\nexport function WorkbenchChanges({\n diff,\n source,\n capturedAt,\n captureRevision,\n themeType,\n onOpenFile,\n className,\n}: WorkbenchChangesProps) {\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [compact, setCompact] = useState(false);\n const [coarsePointer, setCoarsePointer] = useState(false);\n const resolvedTheme = useThemeType(themeType);\n const [layout, setLayout] = useState<\"unified\" | \"split\">(\"unified\");\n const [activePath, setActivePath] = useState<string | null>(null);\n const pickerId = useId();\n\n const unicodeFontPaths = useMemo(\n () => diff.flatMap((file) => (file.oldPath ? [file.path, file.oldPath] : [file.path])),\n [diff],\n );\n useUnicodeFallbackFonts(unicodeFontPaths);\n\n const { orderedFiles, rows, grouped } = useMemo(() => buildRail(diff), [diff]);\n const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);\n const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;\n\n useChangesLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || typeof ResizeObserver === \"undefined\") return;\n const update = (width: number) => {\n // DOM test environments report a zero geometry. Keep the deterministic\n // desktop/SSR branch until the observer has a real measurement.\n if (width > 0) setCompact(width < COMPACT_SURFACE_PX);\n };\n update(root.getBoundingClientRect().width);\n const observer = new ResizeObserver((entries) => {\n update(entries[0]?.contentRect.width ?? root.getBoundingClientRect().width);\n });\n observer.observe(root);\n return () => observer.disconnect();\n }, []);\n\n useChangesLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const query = window.matchMedia(\"(pointer: coarse)\");\n const update = () => setCoarsePointer(query.matches);\n update();\n if (typeof query.addEventListener === \"function\") {\n query.addEventListener(\"change\", update);\n return () => query.removeEventListener(\"change\", update);\n }\n if (typeof query.addListener === \"function\") {\n query.addListener(update);\n return () => query.removeListener(update);\n }\n }, []);\n\n const additions = useMemo(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);\n const deletions = useMemo(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);\n\n const estimateHeight = useCallback(\n (index: number) => {\n const file = orderedFiles[index];\n return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;\n },\n [orderedFiles],\n );\n\n const windowed = useWindowedSections({\n count: orderedFiles.length,\n estimateHeight,\n overscan: OVERSCAN,\n });\n\n const jumpTo = useCallback(\n (index: number) => {\n setActivePath(orderedFiles[index]?.path ?? null);\n windowed.scrollToIndex(index);\n },\n [orderedFiles, windowed],\n );\n\n // Track which section is at the top of the pane so the rail highlights it.\n const onPaneScroll = useCallback(() => {\n const el = windowed.scrollRef.current;\n if (!el) {\n return;\n }\n const top = el.scrollTop;\n const offsets = windowed.offsets;\n let idx = 0;\n for (let i = 0; i < orderedFiles.length; i++) {\n if ((offsets[i] ?? 0) <= top + 4) idx = i;\n else break;\n }\n const nextPath = orderedFiles[idx]?.path ?? null;\n setActivePath((previous) => (previous === nextPath ? previous : nextPath));\n }, [windowed, orderedFiles]);\n\n useEffect(() => {\n const el = windowed.scrollRef.current;\n if (!el) return;\n el.addEventListener(\"scroll\", onPaneScroll, { passive: true });\n return () => el.removeEventListener(\"scroll\", onPaneScroll);\n }, [windowed.scrollRef, onPaneScroll]);\n\n const sourceBadge = describeSource(source, capturedAt, captureRevision ?? null);\n const activeFile = orderedFiles[activeIndex] ?? orderedFiles[0];\n\n return (\n <div\n ref={rootRef}\n className={cn(\"flex h-full min-h-0 min-w-0 flex-col\", className)}\n data-workbench-changes-layout={compact ? \"compact\" : \"rail\"}\n >\n {/* Summary + source badge + layout toggle. */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-3 py-1.5\",\n compact && source === \"capture\" && \"flex-col items-stretch gap-1.5 py-2\",\n )}\n >\n <span\n role=\"status\"\n aria-atomic=\"true\"\n aria-live=\"polite\"\n data-contrast-audited\n className=\"min-w-0 text-og-xs text-og-fg-muted\"\n >\n {diff.length} {diff.length === 1 ? \"file\" : \"files\"} changed\n <span data-contrast-audited className=\"ml-2 text-og-status-idle\">\n +{additions}\n </span>\n <span data-contrast-audited className=\"ml-1 text-og-status-failed\">\n −{deletions}\n </span>\n </span>\n <div\n className={cn(\n \"flex shrink-0 items-center gap-2\",\n compact && source === \"capture\" && \"self-start\",\n )}\n >\n {compact ? null : <LayoutToggle layout={layout} onChange={setLayout} />}\n <SourceBadge source={source} capturedAt={capturedAt} label={sourceBadge} />\n </div>\n </div>\n\n {compact ? (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"shrink-0 border-b border-og-border bg-og-surface-1 p-2\">\n <label className=\"sr-only\" htmlFor={pickerId}>\n Changed file\n </label>\n <select\n id={pickerId}\n aria-label=\"Changed file\"\n value={activeIndex}\n onChange={(event) => {\n const index = Number(event.currentTarget.value);\n setActivePath(orderedFiles[index]?.path ?? null);\n }}\n title={activeFile?.path}\n data-compact-file-picker\n className=\"h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-none transition-colors focus:border-og-accent focus:ring-2 focus:ring-og-accent-soft\"\n >\n {orderedFiles.map((file, index) => (\n <option key={file.path} value={index}>\n {compactFileLabel(file)}\n </option>\n ))}\n </select>\n </div>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto\" data-opengeni-changes-pane>\n {activeFile ? (\n <DiffSection\n file={activeFile}\n layout=\"unified\"\n themeType={resolvedTheme}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n ) : null}\n </div>\n </div>\n ) : (\n <div className=\"flex min-h-0 flex-1\">\n {/* File rail — virtualized (virtua): a dense change set is fine. */}\n <div className=\"w-[clamp(12rem,28%,15rem)] shrink-0 border-r border-og-border bg-og-surface-1/35\">\n <VList\n className=\"h-full\"\n itemSize={coarsePointer ? 44 : 28}\n ssrCount={Math.min(30, rows.length)}\n >\n {rows.map((row) =>\n row.kind === \"group\" ? (\n <div\n key={`g:${row.label}`}\n data-rail-group\n className=\"flex items-center gap-1.5 px-2 pb-0.5 pt-2 text-og-xs font-medium uppercase tracking-wide text-og-fg-subtle\"\n >\n <span className=\"min-w-0 truncate\">{row.label}</span>\n <span className=\"shrink-0\">{row.count}</span>\n </div>\n ) : (\n <RailFileRow\n key={row.file.path}\n file={row.file}\n grouped={grouped}\n active={row.index === activeIndex}\n onClick={() => jumpTo(row.index)}\n />\n ),\n )}\n </VList>\n </div>\n\n {/* Diff pane — windowed file sections. Only the sections inside the\n visible window ± overscan are in the DOM; the container reserves the\n full scroll height so scrolling + the rail-jump stay accurate. */}\n <div\n ref={windowed.scrollRef}\n className=\"min-h-0 min-w-0 flex-1 overflow-auto\"\n data-opengeni-changes-pane\n >\n <div style={{ position: \"relative\", height: windowed.totalHeight }}>\n {orderedFiles.map((file, index) => {\n if (index < windowed.range.start || index >= windowed.range.end) return null;\n return (\n <MeasuredSection\n key={file.path}\n index={index}\n top={windowed.offsets[index] ?? 0}\n onMeasure={windowed.measure}\n >\n <DiffSection\n file={file}\n layout={layout}\n themeType={resolvedTheme}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n </MeasuredSection>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n/**\n * The data-source badge. \"live\" stays understated (the header chip already\n * carries machine liveness) — but a capture-served diff is honestly labelled\n * historical: a muted clock + the \"as of turn N · <time>\" so the reviewer always\n * knows they are looking at a turn-end snapshot, not the live tree.\n */\nfunction SourceBadge({\n source,\n capturedAt,\n label,\n}: {\n source: \"live\" | \"capture\" | null;\n capturedAt: string | null;\n label: string;\n}) {\n if (source === \"capture\" && capturedAt) {\n return (\n <span\n className=\"inline-flex items-center gap-1 rounded-og-xs border border-og-border px-1.5 py-px text-og-xs text-og-fg-muted\"\n title={new Date(capturedAt).toLocaleString()}\n >\n <HistoryIcon className=\"size-3 shrink-0 text-og-status-running\" aria-hidden />\n {label}\n </span>\n );\n }\n return (\n <span className=\"rounded-og-xs bg-og-surface-2 px-1.5 py-px text-og-xs text-og-fg-subtle\">\n {label}\n </span>\n );\n}\n\n/** \"Live diff\" · \"as of turn 7 · 14:32\" · \"as of 14:32\" (no revision). */\nfunction describeSource(\n source: \"live\" | \"capture\" | null,\n capturedAt: string | null,\n revision: number | null,\n): string {\n if (source !== \"capture\" || !capturedAt) return \"Live diff\";\n const time = formatAsOf(capturedAt, Date.now());\n return revision !== null ? `as of turn ${revision} · ${time}` : `as of ${time}`;\n}\n\nfunction RailFileRow({\n file,\n grouped,\n active,\n onClick,\n}: {\n file: SandboxGitFileDiff;\n grouped: boolean;\n active: boolean;\n onClick: () => void;\n}) {\n // A real repository group strips the exact repo root. Legacy top-directory\n // grouping strips only that first segment.\n const repoPrefix = file.repoRoot ? `${file.repoRoot}/` : null;\n const shown = grouped\n ? file.repoRoot !== undefined\n ? repoPrefix && file.path.startsWith(repoPrefix)\n ? file.path.slice(repoPrefix.length)\n : file.path\n : file.path.slice(file.path.indexOf(\"/\") + 1) || file.path\n : file.path;\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={file.path}\n data-rail-file\n className={cn(\n \"relative flex min-h-7 w-full items-center gap-1.5 truncate px-2 py-0.5 text-left text-og-sm transition-colors hover:bg-og-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent pointer-coarse:min-h-11\",\n grouped && \"pl-3\",\n active &&\n \"bg-og-accent-soft text-og-fg before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-r-full before:bg-og-accent\",\n )}\n >\n <span\n data-contrast-audited\n className={cn(\"w-3 shrink-0 text-center font-og-mono text-og-xs\", STATUS_TINT[file.status])}\n >\n {STATUS_LETTER[file.status]}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{shown}</span>\n <span className=\"ml-auto flex shrink-0 items-center gap-1 pl-1 font-og-mono text-og-xs\">\n <span data-contrast-audited className=\"text-og-status-idle\">\n +{file.additions}\n </span>\n <span data-contrast-audited className=\"text-og-status-failed\">\n −{file.deletions}\n </span>\n </span>\n </button>\n );\n}\n\n/** A windowed section wrapper. Absolutely positioned at `top`; a ResizeObserver\n * reports its real height back so the layout refines as Pierre's async Shiki\n * render grows (else short estimates would overlap sections). */\nfunction MeasuredSection({\n index,\n top,\n onMeasure,\n children,\n}: {\n index: number;\n top: number;\n onMeasure: (index: number, height: number) => void;\n children: ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const report = () => onMeasure(index, el.offsetHeight);\n report();\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(report) : null;\n ro?.observe(el);\n return () => ro?.disconnect();\n }, [index, onMeasure]);\n return (\n <div\n ref={ref}\n data-diff-section\n data-diff-index={index}\n style={{ position: \"absolute\", top, left: 0, right: 0 }}\n >\n {children}\n </div>\n );\n}\n\nfunction DiffSection({\n file,\n layout,\n themeType,\n onOpenFile,\n}: {\n file: GitFileDiff;\n layout: \"unified\" | \"split\";\n themeType: \"dark\" | \"light\";\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n // The guard files (binary / over-cap) get a minimal header + \"open live\" body\n // since Pierre has nothing to render; real diffs use Pierre's own sticky file\n // header (one header — no redundant chrome).\n if (isGuarded(file)) {\n const renamed = file.oldPath && file.oldPath !== file.path;\n return (\n <section className=\"border-b border-og-border pb-1\">\n <header className=\"flex items-center justify-between gap-2 bg-og-surface-1 px-3 py-1.5 text-og-sm\">\n <span className=\"min-w-0 truncate font-og-mono text-og-xs\">\n {renamed ? `${file.oldPath} → ${file.path}` : file.path}\n </span>\n <span className=\"flex shrink-0 items-center gap-2 font-og-mono text-og-xs\">\n <span className=\"text-og-status-idle\">+{file.additions}</span>\n <span className=\"text-og-status-failed\">−{file.deletions}</span>\n </span>\n </header>\n <GuardBody file={file} {...(onOpenFile ? { onOpenFile } : {})} />\n </section>\n );\n }\n return (\n <section data-pierre-section className=\"border-b border-og-border pb-2\">\n <PierreDiff diff={[file]} layout={layout} themeType={themeType} className=\"px-1\" />\n </section>\n );\n}\n\n/** The per-file guard: a binary file or an over-cap diff opens live rather than\n * inlining a body we don't (fully) have. */\nfunction GuardBody({\n file,\n onOpenFile,\n}: {\n file: GitFileDiff;\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n const reason = file.isBinary ? \"Binary file\" : \"Diff too large to show here\";\n return (\n <div className=\"flex flex-wrap items-center gap-2 px-3 py-3 text-og-sm text-og-fg-subtle\">\n <FileWarningIcon className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"min-w-0 flex-1\">{reason}.</span>\n {onOpenFile ? (\n <button\n type=\"button\"\n onClick={() => onOpenFile(file.path)}\n className=\"inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11\"\n >\n Open in Files\n <ArrowUpRightIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction LayoutToggle({\n layout,\n onChange,\n}: {\n layout: \"unified\" | \"split\";\n onChange: (next: \"unified\" | \"split\") => void;\n}) {\n return (\n <div className=\"inline-flex items-center rounded-og-sm border border-og-border p-0.5\">\n {([\"unified\", \"split\"] as const).map((value) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => onChange(value)}\n className={cn(\n \"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n layout === value\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n {value}\n </button>\n ))}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\n/* ----------------------------------------------------------------------------\n Windowed sections — variable-height file-section virtualization.\n\n The Changes tab stacks N file diffs in one scroll pane. Mounting N Shiki\n highlighters (Pierre's `PatchDiff`) at once is the exact wart this replaces,\n so we mount only the sections inside the visible window ± overscan and reserve\n the rest as empty space (D2).\n\n Why NOT `virtua` here (unlike the file tree/rail): Pierre renders each diff in\n a shadow DOM with its own internal overflow/virtualization, so an outer\n measuring virtualizer can read a collapsed or fixed height and lay sections\n out wrong (known layout risk). This hook windows by mounting/unmounting whole\n file sections off a scroll offset and a per-section height ESTIMATE (refined\n by measurement when a real browser provides it), which never depends on\n Pierre reporting its own height — and the math is pure + deterministic, so\n D2 is provable in both happy-dom (drive scrollTop/clientHeight) and a real\n browser (Playwright, the verification-matrix path).\n -------------------------------------------------------------------------- */\n\nexport type WindowRange = {\n /** First mounted index (inclusive). */\n start: number;\n /** One past the last mounted index (exclusive). */\n end: number;\n};\n\n/**\n * Pure windowing math: given each section's height (px), the scroll offset, the\n * viewport height, and an overscan count, return the `[start, end)` index window\n * to mount. Robust to a zero viewport (a headless/pre-layout pass) — it then\n * mounts a small overscan window anchored at the item under `scrollTop` rather\n * than the whole list.\n */\nexport function computeWindowRange(\n heights: readonly number[],\n scrollTop: number,\n viewport: number,\n overscan: number,\n): WindowRange {\n const n = heights.length;\n if (n === 0) return { start: 0, end: 0 };\n const top = Math.max(scrollTop, 0);\n const bottom = top + Math.max(viewport, 0);\n\n let firstVisible = -1;\n let lastVisible = -1;\n let acc = 0;\n let anchor = 0; // the item containing `scrollTop` — the zero-viewport fallback\n for (let i = 0; i < n; i++) {\n const secTop = acc;\n const secBottom = secTop + Math.max(heights[i] ?? 0, 0);\n if (secTop <= top) anchor = i;\n // A section is visible when it overlaps [top, bottom); with viewport 0 the\n // half-open interval is empty, so nothing matches and we fall back to anchor.\n if (secBottom > top && secTop < bottom) {\n if (firstVisible === -1) firstVisible = i;\n lastVisible = i;\n }\n acc = secBottom;\n }\n\n if (firstVisible === -1) {\n firstVisible = anchor;\n lastVisible = anchor;\n }\n\n return {\n start: Math.max(0, firstVisible - overscan),\n end: Math.min(n, lastVisible + 1 + overscan),\n };\n}\n\nexport type WindowedSections = {\n /** Attach to the scroll container. */\n scrollRef: React.RefObject<HTMLDivElement | null>;\n /** The mounted window `[start, end)`. */\n range: WindowRange;\n /** Prefix offsets: `offsets[i]` is the top px of section `i`; `offsets[count]`\n * is the total content height. */\n offsets: number[];\n /** Total content height (px) — set on the spacer so scroll length is stable. */\n totalHeight: number;\n /** Report a MOUNTED section's real height (px) so the layout refines as async\n * content — e.g. Pierre's Shiki render — grows. Drive it from a ResizeObserver;\n * a zero/idle value is ignored so a headless pass keeps the estimate. */\n measure: (index: number, height: number) => void;\n /** Scroll section `index` to the top of the pane (the rail-jump). */\n scrollToIndex: (index: number) => void;\n};\n\n/**\n * Variable-height section windowing over a scroll container. Seeds every section\n * to `estimateHeight(i)`, mounts only `range`, and refines heights from real\n * measurements (a no-op in a zero-layout headless pass, which keeps the estimate\n * — deterministic for tests). Pass a STABLE `estimateHeight` (wrap in useCallback).\n */\nexport function useWindowedSections(opts: {\n count: number;\n estimateHeight: (index: number) => number;\n overscan?: number | undefined;\n}): WindowedSections {\n const { count, estimateHeight, overscan = 3 } = opts;\n const scrollRef = useRef<HTMLDivElement | null>(null);\n\n const [heights, setHeights] = useState<number[]>(() =>\n Array.from({ length: count }, (_, i) => estimateHeight(i)),\n );\n\n // Re-seed heights when the section count changes (a new diff arrives), keeping\n // any already-measured heights for surviving indices.\n useEffect(() => {\n setHeights((prev) => {\n if (prev.length === count) return prev;\n return Array.from({ length: count }, (_, i) => prev[i] ?? estimateHeight(i));\n });\n // estimateHeight is expected stable; count is the real trigger.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [count]);\n\n const heightsRef = useRef(heights);\n heightsRef.current = heights;\n\n const [range, setRange] = useState<WindowRange>(() => ({\n start: 0,\n end: Math.min(count, overscan + 1),\n }));\n\n const recompute = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);\n setRange((prev) => (prev.start === next.start && prev.end === next.end ? prev : next));\n }, [overscan]);\n\n // Recompute on scroll (rAF-coalesced) and on container resize.\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n let raf = 0;\n const onScroll = () => {\n if (raf) return;\n raf =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(() => {\n raf = 0;\n recompute();\n })\n : (recompute(), 0);\n };\n el.addEventListener(\"scroll\", onScroll, { passive: true });\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(() => recompute()) : null;\n ro?.observe(el);\n recompute();\n return () => {\n el.removeEventListener(\"scroll\", onScroll);\n ro?.disconnect();\n if (raf && typeof cancelAnimationFrame === \"function\") cancelAnimationFrame(raf);\n };\n }, [recompute, count]);\n\n const measure = useCallback((index: number, height: number) => {\n if (height <= 0) return; // zero-layout pass — keep the estimate\n setHeights((prev) => {\n if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;\n const next = prev.slice();\n next[index] = height;\n return next;\n });\n }, []);\n\n const offsets = useMemo(() => {\n const out = new Array<number>(count + 1);\n out[0] = 0;\n for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);\n return out;\n }, [heights, count]);\n\n // A measured height can shift the window (e.g. estimates were short) — keep the\n // mounted set in sync without waiting for the next scroll.\n useEffect(() => {\n recompute();\n }, [offsets, recompute]);\n\n const scrollToIndex = useCallback(\n (index: number) => {\n const el = scrollRef.current;\n if (!el) return;\n const clamped = Math.max(0, Math.min(count - 1, index));\n el.scrollTop = offsets[clamped] ?? 0;\n },\n [offsets, count],\n );\n\n return {\n scrollRef,\n range,\n offsets,\n totalHeight: offsets[count] ?? 0,\n measure,\n scrollToIndex,\n };\n}\n","import type { GitFileDiff } from \"@opengeni/sdk\";\nimport { type ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { PierreDiff } from \"./pierre-diff\";\n\n/**\n * @deprecated Use {@link PierreDiff} directly. Retained only as a thin,\n * back-compat alias that renders the Pierre (Shiki) renderer with a plain-text\n * degrade. The old hand-rolled hunk renderer was removed in Workbench v2 — the\n * workbench keeps exactly ONE diff renderer. This alias is removed next major.\n */\nexport type DiffTheme = {\n addBackground?: string;\n delBackground?: string;\n contextBackground?: string;\n metaForeground?: string;\n};\n\n/**\n * @deprecated Use {@link PierreDiff}. `theme`/`onSelectFile`/`emptyState` from the\n * removed hand-rolled renderer are accepted for source compatibility and ignored;\n * `fallback` still overrides the plain-text degrade.\n */\nexport type DiffViewProps = {\n diff: GitFileDiff[];\n fallback?: ReactNode | undefined;\n layout?: \"unified\" | \"split\" | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** @deprecated line-background theming was part of the removed renderer. */\n theme?: DiffTheme | undefined;\n /** @deprecated the alias no longer renders a clickable file header. */\n onSelectFile?: ((path: string) => void) | undefined;\n isRepo?: boolean | undefined;\n emptyState?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n/**\n * @deprecated Use {@link PierreDiff} directly. Thin alias: renders `PierreDiff`\n * (Shiki-highlighted, unified/split) with a plain-text fallback for a host\n * without `@pierre/diffs`. Removed next major.\n */\nexport function DiffView({\n diff,\n fallback,\n layout = \"unified\",\n themeType,\n isRepo = true,\n emptyState,\n className,\n}: DiffViewProps) {\n if (diff.length === 0) {\n return (\n <div className={cn(\"p-3 text-og-sm text-og-fg-subtle\", className)}>\n {emptyState ?? (isRepo ? \"No changes\" : \"No repository mounted\")}\n </div>\n );\n }\n return (\n <PierreDiff\n diff={diff}\n layout={layout}\n themeType={themeType}\n {...(fallback !== undefined ? { fallback } : {})}\n className={className}\n />\n );\n}\n","import {\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n lazy,\n Suspense,\n useEffect,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** Pierre `File` props subset we drive — the single-file, syntax-highlighted view\n * (Shiki), the read counterpart of `PatchDiff`. */\ntype FileComponent = ComponentType<{\n file: { name: string; contents: string; lang?: string };\n options?: {\n theme?: string | { dark: string; light: string };\n themeType?: \"dark\" | \"light\";\n overflow?: \"scroll\" | \"wrap\";\n stickyHeader?: boolean;\n showLineNumbers?: boolean;\n };\n disableWorkerPool?: boolean;\n className?: string;\n}>;\n\nexport type PierreFileProps = {\n /** Workspace-relative path (used for the header + language inference). */\n path: string;\n /** The decoded text contents. */\n contents: string;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Shiki bundled theme names (dark/light) — derived from the host palette. */\n theme?: { dark: string; light: string } | undefined;\n /** Disable Pierre's worker pool if its worker bundling fights the host bundler. */\n disableWorkerPool?: boolean | undefined;\n /** Rendered while the (lazy) Pierre bundle loads. */\n loading?: ReactNode | undefined;\n /** Rendered if `@pierre/diffs/react` is not installed / fails to import. */\n fallback?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n// Lazy-load `@pierre/diffs/react`'s `File` so Shiki + the worker pool stay off the\n// critical path (and out of an SSR bundle) until a file is actually viewed.\nconst LazyFile = lazy(async () => {\n const mod = (await import(\"@pierre/diffs/react\")) as unknown as { File: FileComponent };\n return { default: mod.File };\n});\n\n/**\n * The Pierre-backed single-file VIEWER: Shiki-highlighted, language inferred from\n * the filename — the read complement of `PierreDiff`. Wired to `fs.read` so\n * clicking any file in the tree shows its contents (NOT a diff; no repo needed).\n * Falls back to a plain `<pre>` when `@pierre/diffs` is absent / fails to import.\n */\nexport function PierreFile({\n path,\n contents,\n themeType,\n theme,\n disableWorkerPool,\n loading,\n fallback,\n className,\n}: PierreFileProps) {\n const [failed, setFailed] = useState(false);\n\n // Probe the import once so a hard failure (peer missing) shows `fallback`\n // rather than a Suspense boundary that never resolves.\n useEffect(() => {\n let cancelled = false;\n void import(\"@pierre/diffs/react\").catch(() => {\n if (!cancelled) setFailed(true);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const name = path.split(\"/\").filter(Boolean).pop() ?? path;\n\n if (failed) {\n return (\n <div className={className}>{fallback ?? <PlainFile name={name} contents={contents} />}</div>\n );\n }\n\n const options = {\n overflow: \"scroll\" as const,\n stickyHeader: true,\n showLineNumbers: true,\n ...(theme\n ? { theme }\n : {\n theme: {\n dark: \"github-dark-high-contrast\",\n light: \"github-light-high-contrast\",\n },\n }),\n themeType: themeType ?? \"dark\",\n };\n\n // Same shadow-DOM override vars as PierreDiff: pin the base background to the\n // dock surface so the viewer reads as part of the panel, not a black seam.\n const pierreVars = {\n \"--diffs-dark-bg\": \"var(--og-color-bg)\",\n \"--diffs-light-bg\": \"var(--og-color-bg)\",\n \"--diffs-bg-buffer-override\": \"var(--og-color-surface-1)\",\n \"--diffs-bg-separator-override\": \"var(--og-color-surface-1)\",\n \"--diffs-font-size\": \"var(--og-code-font-size)\",\n \"--diffs-line-height\": \"var(--og-code-line-height)\",\n } as CSSProperties;\n\n return (\n <div className={cn(\"min-w-0\", className)} data-opengeni-pierre-file style={pierreVars}>\n <Suspense fallback={loading ?? <FileSkeleton />}>\n {/* `cacheKey` keys Pierre's worker-pool highlight cache on path+size so a\n re-select of the same file is instant but an edited file re-highlights. */}\n <LazyFile\n file={{ name, contents }}\n options={options}\n {...(disableWorkerPool !== undefined ? { disableWorkerPool } : {})}\n />\n </Suspense>\n </div>\n );\n}\n\nfunction PlainFile({ name, contents }: { name: string; contents: string }) {\n return (\n <pre\n className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\"\n data-file={name}\n >\n {contents}\n </pre>\n );\n}\n\nfunction FileSkeleton() {\n return <div className=\"p-3 text-og-sm text-og-fg-subtle\">Loading file…</div>;\n}\n","import { Loader2Icon, SaveIcon } from \"lucide-react\";\nimport {\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\n\n// --- Lazy CodeMirror surface ----------------------------------------------\n//\n// CodeMirror 6 (via @uiw/react-codemirror) plus the per-language grammars are a\n// heavy bundle (lezer parsers, the view layer). We keep them OFF the critical\n// path and out of any SSR bundle by lazy-importing only when a file is actually\n// EDITED. The read-only viewer stays Pierre's Shiki `File`; this is the\n// editable complement, mounted on demand.\n\n/** The subset of `@uiw/react-codemirror`'s props we drive. */\ntype ReactCodeMirrorComponent = ComponentType<{\n value?: string;\n height?: string;\n theme?: \"light\" | \"dark\" | \"none\" | unknown;\n editable?: boolean;\n readOnly?: boolean;\n basicSetup?: boolean | Record<string, boolean>;\n extensions?: unknown[];\n onChange?: ((value: string) => void) | undefined;\n className?: string;\n style?: CSSProperties;\n}>;\n\n// Resolved lazily on first edit: the editor component + the keymap/Prec helpers\n// (both re-exported from @uiw/react-codemirror, which re-exports @codemirror/view\n// and @codemirror/state) + the chosen language extension.\ntype EditorBundle = {\n Editor: ReactCodeMirrorComponent;\n saveKeymapExtension: (onSave: () => void) => unknown;\n languageExtension: unknown | null;\n};\n\n/** Language grammar loaders, keyed by the extension class we infer from the path. */\nconst LANGUAGE_LOADERS: Record<string, () => Promise<unknown>> = {\n javascript: async () =>\n (await import(\"@codemirror/lang-javascript\")).javascript({ jsx: true, typescript: true }),\n json: async () => (await import(\"@codemirror/lang-json\")).json(),\n python: async () => (await import(\"@codemirror/lang-python\")).python(),\n markdown: async () => (await import(\"@codemirror/lang-markdown\")).markdown(),\n css: async () => (await import(\"@codemirror/lang-css\")).css(),\n html: async () => (await import(\"@codemirror/lang-html\")).html(),\n};\n\n/** Map a filename to a grammar key (or null for plain text — still fully editable). */\nexport function languageForPath(path: string): keyof typeof LANGUAGE_LOADERS | null {\n const name = path.split(\"/\").pop() ?? path;\n const ext = name.includes(\".\") ? (name.split(\".\").pop() ?? \"\").toLowerCase() : \"\";\n switch (ext) {\n case \"js\":\n case \"jsx\":\n case \"ts\":\n case \"tsx\":\n case \"mjs\":\n case \"cjs\":\n return \"javascript\";\n case \"json\":\n case \"jsonc\":\n return \"json\";\n case \"py\":\n case \"pyi\":\n return \"python\";\n case \"md\":\n case \"markdown\":\n case \"mdx\":\n return \"markdown\";\n case \"css\":\n case \"scss\":\n case \"less\":\n return \"css\";\n case \"html\":\n case \"htm\":\n case \"xml\":\n case \"svg\":\n case \"vue\":\n return \"html\";\n default:\n return null;\n }\n}\n\nexport type CodeEditorProps = {\n /** Workspace-relative path — drives language inference (and the save target upstream). */\n path: string;\n /** The decoded text contents to seed the editor with. */\n initialContents: string;\n /** Persist the current buffer. Resolves when the write lands; rejects to surface an error. */\n onSave: (contents: string) => Promise<unknown>;\n /** Explicitly overwrite after a visible expected-content conflict. */\n onOverwrite?: ((contents: string) => Promise<unknown>) | undefined;\n /** Re-read the selected file after a visible save conflict. */\n onReload?: (() => void) | undefined;\n /** Fired once when the buffer FIRST diverges from its baseline (the first real\n * keystroke) — the wake-on-edit intent. The host warms the box on this so the\n * eventual save lands fast; merely opening the file for reading never fires it. */\n onEditIntent?: (() => void) | undefined;\n /** Read-only mode (e.g. a truncated/too-large file shown for reference only). */\n readOnly?: boolean | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Rendered while the (lazy) CodeMirror bundle loads. */\n loading?: ReactNode | undefined;\n /** Rendered if `@uiw/react-codemirror` is not installed / fails to import. */\n fallback?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n/**\n * The EDITABLE single-file pane: CodeMirror 6 with a per-language grammar chosen\n * from the filename, og-* themed, with dirty tracking and a save path wired to\n * `Cmd/Ctrl+S` *and* an explicit Save button. The viewer (Pierre `File`) stays\n * the read-only complement — this is only mounted when the user opts to edit.\n *\n * Save semantics: the buffer is \"dirty\" the moment it diverges from the last\n * saved baseline; a successful `onSave` clears dirty and re-baselines. A failed\n * save keeps the buffer dirty (nothing is lost) and surfaces the error inline.\n * `readOnly` suppresses every mutation path so a truncated/binary file can never\n * be saved back (which would corrupt it by writing the truncated prefix).\n */\nexport function CodeEditor({\n path,\n initialContents,\n onSave,\n onOverwrite,\n onReload,\n onEditIntent,\n readOnly = false,\n themeType = \"dark\",\n loading,\n fallback,\n className,\n}: CodeEditorProps) {\n const [failed, setFailed] = useState(false);\n const [bundle, setBundle] = useState<EditorBundle | null>(null);\n\n // The live buffer + the baseline it's compared against for dirtiness. The\n // baseline resets whenever a *new* file is loaded (path/initialContents change)\n // or after a successful save.\n const [value, setValue] = useState(initialContents);\n const [baseline, setBaseline] = useState(initialContents);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<Error | null>(null);\n const [savedTick, setSavedTick] = useState(false);\n\n const dirty = value !== baseline;\n const saveConflict =\n saveError !== null && (saveError as Error & { code?: unknown }).code === \"file_write_conflict\";\n\n // Reload the buffer when the file identity changes. Guard on initialContents\n // too so an external refresh of the SAME path (e.g. fs.changed re-read) reseeds\n // a clean buffer — but only when the user hasn't got unsaved edits in flight.\n // Fire the wake-on-edit intent at most once per opened file — reset on re-seed.\n const editIntentFiredRef = useRef(false);\n const lastSeed = useRef<{ path: string; contents: string }>({ path, contents: initialContents });\n useEffect(() => {\n const seedChanged =\n lastSeed.current.path !== path || lastSeed.current.contents !== initialContents;\n if (!seedChanged) return;\n lastSeed.current = { path, contents: initialContents };\n editIntentFiredRef.current = false;\n setValue(initialContents);\n setBaseline(initialContents);\n setSaveError(null);\n setSavedTick(false);\n }, [path, initialContents]);\n\n // Record a buffer change: the FIRST divergence from the baseline is the edit\n // intent that warms the box (idempotent — latched per opened file).\n const noteEdit = useCallback(\n (next: string) => {\n setValue(next);\n setSavedTick(false);\n if (!editIntentFiredRef.current && next !== baseline) {\n editIntentFiredRef.current = true;\n onEditIntent?.();\n }\n },\n [baseline, onEditIntent],\n );\n\n // Resolve the lazy bundle (editor + keymap/Prec helpers + language grammar)\n // once, re-resolving the grammar when the file's language class changes.\n const langKey = useMemo(() => languageForPath(path), [path]);\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const cmMod = (await import(\"@uiw/react-codemirror\")) as unknown as {\n default: ReactCodeMirrorComponent;\n keymap: { of: (binds: unknown[]) => unknown };\n Prec: { highest: (ext: unknown) => unknown };\n };\n // Language peers are optional. A missing grammar must degrade to a plain\n // CodeMirror document, not throw the entire editor down to textarea.\n let languageExtension: unknown | null = null;\n if (langKey) {\n try {\n languageExtension = (await LANGUAGE_LOADERS[langKey]?.()) ?? null;\n } catch {\n languageExtension = null;\n }\n }\n if (cancelled) return;\n // A high-precedence Cmd/Ctrl-S keymap that calls back into the latest\n // save handler (kept fresh via a ref) and swallows the browser's\n // \"save page\" default.\n const saveKeymapExtension = (run: () => void) =>\n cmMod.Prec.highest(\n cmMod.keymap.of([\n {\n key: \"Mod-s\",\n preventDefault: true,\n run: () => {\n run();\n return true;\n },\n },\n ]),\n );\n setBundle({\n Editor: cmMod.default,\n saveKeymapExtension,\n languageExtension,\n });\n setFailed(false);\n } catch {\n if (!cancelled) setFailed(true);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [langKey]);\n\n // Keep the freshest save closure reachable from the (stable) keymap binding.\n const saveRef = useRef<() => void>(() => {});\n\n const save = useCallback(async () => {\n if (readOnly) return;\n // Snapshot the buffer at call time so an in-flight edit can't race the write.\n const snapshot = value;\n if (snapshot === baseline) return; // nothing to persist\n setSaving(true);\n setSaveError(null);\n setSavedTick(false);\n try {\n await onSave(snapshot);\n setBaseline(snapshot);\n lastSeed.current = { path, contents: snapshot };\n setSavedTick(true);\n } catch (cause) {\n setSaveError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n setSaving(false);\n }\n }, [readOnly, value, baseline, onSave, path]);\n\n const overwrite = useCallback(async () => {\n if (readOnly || !onOverwrite) return;\n const snapshot = value;\n if (snapshot === baseline) return;\n setSaving(true);\n setSaveError(null);\n setSavedTick(false);\n try {\n await onOverwrite(snapshot);\n setBaseline(snapshot);\n lastSeed.current = { path, contents: snapshot };\n setSavedTick(true);\n } catch (cause) {\n setSaveError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n setSaving(false);\n }\n }, [readOnly, onOverwrite, value, baseline, path]);\n\n saveRef.current = () => {\n void save();\n };\n\n // A brief \"Saved\" affordance after a successful write, cleared on the next edit.\n useEffect(() => {\n if (!savedTick) return;\n const t = setTimeout(() => setSavedTick(false), 1800);\n return () => clearTimeout(t);\n }, [savedTick]);\n\n const fileName = path.split(\"/\").filter(Boolean).pop() ?? path;\n\n const cmExtensions = useMemo(() => {\n if (!bundle) return [] as unknown[];\n const exts: unknown[] = [bundle.saveKeymapExtension(() => saveRef.current())];\n if (bundle.languageExtension) exts.push(bundle.languageExtension);\n return exts;\n }, [bundle]);\n\n const Editor = bundle?.Editor;\n\n return (\n <div\n className={cn(\"flex h-full min-h-0 flex-col\", className)}\n data-opengeni-code-editor\n style={editorVars}\n >\n {/* Save bar: dirty indicator + status + the explicit Save button. */}\n <div className=\"flex min-h-9 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1\">\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full transition-colors\",\n readOnly ? \"bg-transparent\" : dirty ? \"bg-og-status-running\" : \"bg-og-status-idle\",\n )}\n title={readOnly ? \"read-only\" : dirty ? \"unsaved changes\" : \"saved\"}\n />\n <span className=\"truncate font-og-mono text-og-xs text-og-fg-muted\">\n {fileName}\n {dirty && !readOnly ? \" •\" : \"\"}\n </span>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {saveError && !saveConflict && (\n <span\n className=\"max-w-[220px] truncate text-og-xs text-og-status-failed\"\n title={saveError.message}\n >\n {saveError.message}\n </span>\n )}\n {!saveError && savedTick && <span className=\"text-og-xs text-og-status-idle\">Saved</span>}\n {readOnly ? (\n <span className=\"text-og-xs uppercase tracking-wide text-og-fg-subtle\">Read-only</span>\n ) : !saveConflict ? (\n <button\n type=\"button\"\n onClick={() => void save()}\n disabled={saving || !dirty}\n className={cn(\n \"flex items-center gap-1 rounded-og-sm border border-og-border px-1.5 py-0.5 text-og-xs pointer-coarse:min-h-11\",\n saving || !dirty\n ? \"cursor-default text-og-fg-subtle opacity-60\"\n : \"text-og-fg hover:bg-og-accent-soft\",\n )}\n title=\"Save (⌘/Ctrl+S)\"\n >\n {saving ? (\n <Loader2Icon className=\"size-3 animate-spin\" />\n ) : (\n <SaveIcon className=\"size-3\" />\n )}\n Save\n </button>\n ) : null}\n </div>\n </div>\n\n {saveConflict ? (\n <div\n className=\"flex shrink-0 flex-wrap items-center gap-x-3 gap-y-2 border-b border-og-status-failed/25 bg-og-status-failed/8 px-2 py-2\"\n role=\"alert\"\n >\n <div className=\"min-w-[12rem] flex-1 text-og-xs text-og-fg-muted\">\n <span className=\"font-medium text-og-status-failed\">File changed on machine.</span>{\" \"}\n Reloading discards your edits; overwriting replaces the live version.\n </div>\n <div className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {onReload ? (\n <button\n type=\"button\"\n onClick={() => {\n setSaveError(null);\n onReload();\n }}\n className=\"min-h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11\"\n >\n Reload live\n </button>\n ) : null}\n {onOverwrite ? (\n <button\n type=\"button\"\n onClick={() => void overwrite()}\n disabled={saving}\n className=\"min-h-8 rounded-og-sm border border-og-status-failed/40 px-2 text-og-xs font-medium text-og-status-failed hover:bg-og-status-failed/10 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Overwrite\n </button>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {/* The editor surface. */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {failed ? (\n (fallback ?? (\n <PlainTextarea\n value={value}\n readOnly={readOnly}\n onChange={noteEdit}\n onSave={() => void save()}\n />\n ))\n ) : Editor ? (\n <Editor\n value={value}\n theme={themeType === \"light\" ? \"light\" : \"dark\"}\n editable={!readOnly}\n readOnly={readOnly}\n basicSetup={true}\n extensions={cmExtensions}\n height=\"100%\"\n className=\"og-cm-editor min-h-full text-og-sm\"\n onChange={readOnly ? undefined : noteEdit}\n />\n ) : (\n (loading ?? <EditorSkeleton />)\n )}\n </div>\n </div>\n );\n}\n\n/** og-* themed CSS-var overrides handed to CodeMirror's container. */\nconst editorVars = {\n \"--og-cm-bg\": \"var(--og-color-bg)\",\n fontFamily: \"var(--og-font-mono)\",\n} as CSSProperties;\n\nfunction PlainTextarea({\n value,\n readOnly,\n onChange,\n onSave,\n}: {\n value: string;\n readOnly: boolean;\n onChange: (value: string) => void;\n onSave: () => void;\n}) {\n return (\n <textarea\n value={value}\n readOnly={readOnly}\n spellCheck={false}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"s\") {\n event.preventDefault();\n onSave();\n }\n }}\n aria-label=\"Plain text file editor\"\n className=\"h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-none\"\n />\n );\n}\n\nfunction EditorSkeleton() {\n return <div className=\"p-3 text-og-sm text-og-fg-subtle\">Loading editor…</div>;\n}\n","import { FileCode2Icon, FileWarningIcon, LoaderCircleIcon } from \"lucide-react\";\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useThemeType } from \"../lib/use-theme-type\";\nimport {\n CapturedFileUnavailableError,\n type UseSandboxFilesResult,\n} from \"../hooks/use-sandbox-files\";\nimport type { UseSandboxGitResult } from \"../hooks/use-sandbox-git\";\nimport { CodeEditor } from \"./code-editor\";\nimport { FileBrowser } from \"./file-browser\";\nimport { PierreFile } from \"./pierre-file\";\n\nexport type SandboxFilesProps = {\n /** From `useSandboxFiles(...)`. */\n files: UseSandboxFilesResult;\n /** From `useSandboxGit(...)` — drives the branch/dirty header only. */\n git: UseSandboxGitResult;\n /** @deprecated Diffs live in the dedicated Changes tab now; accepted for\n * source-compat but unused (the Files surface is a browser + viewer). */\n stagedGit?: UseSandboxGitResult | undefined;\n /** Whether a FileSystem surface is advertised (drives the unavailable notice). */\n fileSystemAvailable?: boolean | undefined;\n /** Use Pierre's Shiki highlighter for the viewer (default true; plain fallback). */\n usePierre?: boolean | undefined;\n /** Allow in-place editing of tree files (CodeMirror). Default true. When false\n * the surface is review-only: every text file opens in the read-only viewer. */\n editable?: boolean | undefined;\n /** Fired once when the user first edits an open file (wake-on-edit intent). The\n * dock warms the box on this so the save lands fast; opening/reading never fires\n * it. Browsing the tree/diff must not warm a box. */\n onEditIntent?: (() => void) | undefined;\n /** A guarded diff path routed here by the parent workspace. */\n requestedPath?: string | undefined;\n /** Identity for one guarded-file request. Increment this when the same path is\n * deliberately requested again; it also lets a pending request be consumed\n * without overriding later manual tree navigation. Defaults to the path. */\n requestedPathRequestId?: string | number | undefined;\n /** False while the parent is waking a cold sandbox for `requestedPath`. */\n requestedPathReady?: boolean | undefined;\n /** The machine is cold and no durable capture exists yet. Render an explicit\n * wake gate instead of an empty-tree lie or an implicit Channel-A read. */\n workspaceResting?: boolean | undefined;\n /** A deliberate wake has started but the live file surface is not ready yet. */\n workspaceWaking?: boolean | undefined;\n /** Whether live reads are currently authoritative. */\n liveWorkspaceReady?: boolean | undefined;\n /** Deliberately wake the machine to read content absent from the capture. */\n onWakeWorkspace?: (() => void) | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n className?: string | undefined;\n};\n\n/**\n * The Files surface: a branch/dirty header, the full lazy file tree, and a\n * viewer/editor pane for the selected file. This is the workspace BROWSER — pick\n * a file to read or edit it. Diff review lives in the dedicated Changes tab (this\n * surface deliberately does NOT replicate the changed-files list, and does not\n * diff here — one job per tab). The agent commits; the human reviews.\n */\nexport function SandboxFiles({\n files,\n git,\n fileSystemAvailable = true,\n usePierre = true,\n editable = true,\n onEditIntent,\n requestedPath,\n requestedPathRequestId,\n requestedPathReady = true,\n workspaceResting = false,\n workspaceWaking = false,\n liveWorkspaceReady = true,\n onWakeWorkspace,\n themeType,\n className,\n}: SandboxFilesProps) {\n const [selected, setSelected] = useState<string | null>(null);\n // View vs Edit for the selected file. Resets to View on every new selection so\n // opening a file never lands you in a stale dirty editor for a different path.\n const [editMode, setEditMode] = useState(false);\n const [liveRequestedPath, setLiveRequestedPath] = useState<string | null>(null);\n const [viewReloadRevision, setViewReloadRevision] = useState(0);\n const pendingRequestRef = useRef<string | number | null>(null);\n const handledRequestRef = useRef<string | number | null>(null);\n const requestKey = requestedPath ? (requestedPathRequestId ?? requestedPath) : null;\n\n useEffect(() => {\n if (!requestedPath || requestKey === null) {\n pendingRequestRef.current = null;\n handledRequestRef.current = null;\n return;\n }\n if (handledRequestRef.current === requestKey) {\n return;\n }\n if (!requestedPathReady) {\n pendingRequestRef.current = requestKey;\n return;\n }\n handledRequestRef.current = requestKey;\n pendingRequestRef.current = null;\n setSelected(requestedPath);\n setEditMode(false);\n }, [requestKey, requestedPath, requestedPathReady]);\n\n // Side-by-side (tree left, viewer right) once the surface is wide enough;\n // stacked (tree over viewer) on a narrow dock. Tracked off the container so it\n // reacts to the dock resize, not just the viewport.\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [wide, setWide] = useState(false);\n useEffect(() => {\n const el = rootRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const w = entries[0]?.contentRect.width ?? 0;\n setWide(w >= 720);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n // Resolve the effective viewer theme from the host palette (the `data-og-theme`\n // attribute the demo/app sets), defaulting to dark, unless the caller forced one.\n const resolvedTheme = useThemeType(themeType);\n\n // The selected tree file opens in the viewer (read-only) or the editor (when\n // writable). Nothing is auto-selected — the pane waits for a tree click, so the\n // Files tab opens as a calm browser, not a diff.\n const viewPath = selected;\n const fileView = useFileView(viewPath, files.readFile, viewReloadRevision);\n\n // Selecting a (different) file always returns to View — never drop the user into\n // an editor whose buffer belongs to the previously-selected path. Manual\n // navigation also consumes a pending guarded-file request: a late cold→warm\n // transition must never pull the user away from the file they chose meanwhile.\n const selectFile = useCallback((path: string) => {\n if (pendingRequestRef.current !== null) {\n handledRequestRef.current = pendingRequestRef.current;\n pendingRequestRef.current = null;\n }\n setSelected(path);\n setEditMode(false);\n setLiveRequestedPath(null);\n setViewReloadRevision(0);\n }, []);\n\n // A tree file is editable only when it is a real, fully-loaded text file: not\n // binary (would corrupt on save) and not truncated (we only hold a PREFIX). The\n // editor is then additionally gated on the `editable` prop; anything failing this\n // opens read-only in the viewer.\n const canEdit =\n editable &&\n viewPath !== null &&\n !fileView.loading &&\n fileView.error === null &&\n !fileView.isBinary &&\n !fileView.truncated &&\n fileView.content !== null;\n const showEditor = canEdit && editMode;\n const captureFileUnavailable =\n fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;\n const waitingForSelectedFile =\n liveRequestedPath === viewPath &&\n (!liveWorkspaceReady || files.loading) &&\n captureFileUnavailable !== null;\n\n if (!fileSystemAvailable) {\n return (\n <Notice\n className={className}\n icon={<FileWarningIcon className=\"size-5\" aria-hidden />}\n title=\"Files unavailable\"\n announce=\"alert\"\n >\n This sandbox does not expose a file system.\n </Notice>\n );\n }\n\n if (workspaceResting || workspaceWaking) {\n return (\n <div className={cn(\"h-full\", className)} data-opengeni-workspace-resting>\n <Notice\n icon={\n workspaceWaking ? (\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n ) : (\n <FileCode2Icon className=\"size-5\" aria-hidden />\n )\n }\n title={workspaceWaking ? \"Waking workspace\" : \"Workspace is resting\"}\n announce=\"status\"\n >\n <p>\n {workspaceWaking\n ? \"Connecting to the live file system…\"\n : \"No captured revision is available yet. Wake the sandbox to browse its current files.\"}\n </p>\n {!workspaceWaking && onWakeWorkspace ? (\n <WakeButton onClick={onWakeWorkspace}>Open live workspace</WakeButton>\n ) : null}\n </Notice>\n </div>\n );\n }\n\n return (\n <div ref={rootRef} className={cn(\"flex h-full min-h-0 min-w-0 flex-col\", className)}>\n {/* Branch + dirty header (context; NOT the changed-files list). */}\n <GitHeader git={git} dirtyCount={git.diff.length} />\n\n <div className={cn(\"flex min-h-0 flex-1\", wide ? \"flex-row\" : \"flex-col\")}>\n {/* Tree pane: the full lazy file tree. A fixed left column when wide, a top\n band when narrow. */}\n <div\n className={cn(\n \"flex min-h-0 flex-col\",\n wide ? \"w-[280px] shrink-0 border-r border-og-border\" : \"flex-1\",\n )}\n >\n <FileBrowser\n result={files}\n selectedPath={selected ?? undefined}\n onSelectFile={selectFile}\n editable={editable}\n emptyState=\"This directory is empty\"\n className=\"min-w-0 flex-1\"\n />\n </div>\n\n {/* Viewer pane: the selected file's contents (read-only) or the editor.\n Fills the remaining width when side-by-side, sits below the tree when\n stacked. */}\n <div\n className={cn(\n \"flex min-h-0 min-w-0 flex-col\",\n wide ? \"flex-1\" : \"flex-[1.4] border-t border-og-border\",\n )}\n >\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1\">\n <span\n data-opengeni-selected-file\n className=\"min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted\"\n >\n {selected ?? \"No file selected\"}\n </span>\n {/* View/Edit toggle — only for a real, fully-loaded text file the editor\n can safely round-trip. Binary/truncated/read-only files never get an\n Edit affordance (they'd corrupt on save or can't be written). */}\n {canEdit && (\n <Segmented\n options={[\n { value: \"view\", label: \"View\" },\n { value: \"edit\", label: \"Edit\" },\n ]}\n value={editMode ? \"edit\" : \"view\"}\n onChange={(v) => setEditMode(v === \"edit\")}\n />\n )}\n </div>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {viewPath ? (\n showEditor && fileView.content !== null ? (\n <CodeEditor\n key={viewPath}\n path={viewPath}\n initialContents={fileView.content}\n themeType={resolvedTheme}\n onSave={(contents) =>\n files.writeFile(viewPath, contents, { expectedContent: fileView.content! })\n }\n onOverwrite={(contents) => files.writeFile(viewPath, contents, { force: true })}\n onReload={() => setViewReloadRevision((revision) => revision + 1)}\n {...(onEditIntent ? { onEditIntent } : {})}\n className=\"h-full\"\n />\n ) : waitingForSelectedFile ? (\n <Notice\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n title=\"Waking workspace\"\n announce=\"status\"\n >\n Opening {viewPath} when the live file system is ready…\n </Notice>\n ) : captureFileUnavailable ? (\n <Notice icon={<FileCode2Icon className=\"size-5\" aria-hidden />} title=\"On machine\">\n <p>\n {captureFileUnavailable.reason === \"too-large\"\n ? \"This file is larger than the captured preview limit.\"\n : captureFileUnavailable.reason === \"content-missing\"\n ? \"The captured copy is no longer available.\"\n : \"This file was indexed, but it was not changed in the captured turn.\"}\n </p>\n {onWakeWorkspace ? (\n <WakeButton\n onClick={() => {\n setLiveRequestedPath(viewPath);\n if (liveWorkspaceReady) void files.refresh();\n else onWakeWorkspace();\n }}\n >\n {liveWorkspaceReady ? \"Retry live file\" : \"Open live file\"}\n </WakeButton>\n ) : null}\n </Notice>\n ) : fileView.error ? (\n <Notice announce=\"alert\">\n Could not open {viewPath}: {fileView.error.message}\n </Notice>\n ) : fileView.loading ? (\n <Notice announce=\"status\">Loading {viewPath}…</Notice>\n ) : fileView.isBinary ? (\n <Notice>\n {viewPath} is a binary file ({fileView.sizeBytes ?? 0} bytes).\n </Notice>\n ) : fileView.content !== null ? (\n <>\n {fileView.truncated && (\n <div className=\"border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running\">\n Large file — showing a truncated preview ({fileView.sizeBytes ?? 0} bytes\n loaded). Editing is disabled to avoid corrupting the file.\n </div>\n )}\n {usePierre ? (\n <PierreFile\n path={viewPath}\n contents={fileView.content}\n themeType={resolvedTheme}\n fallback={\n <pre className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\">\n {fileView.content}\n </pre>\n }\n className=\"p-1\"\n />\n ) : (\n <pre className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\">\n {fileView.content}\n </pre>\n )}\n </>\n ) : (\n <Notice announce=\"status\">Loading {viewPath}…</Notice>\n )\n ) : // Nothing selected — the tree shows the whole workspace; pick a file.\n requestedPath && !requestedPathReady ? (\n <Notice\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n title=\"Waking sandbox\"\n announce=\"status\"\n >\n Opening {requestedPath} when the live workspace is ready…\n </Notice>\n ) : (\n <Notice icon={<FileCode2Icon className=\"size-5\" aria-hidden />} title=\"Choose a file\">\n Select a file in the tree to preview it.\n </Notice>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction WakeButton({ children, onClick }: { children: ReactNode; onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent-deep px-3 py-2 text-og-sm font-medium text-og-accent-fg shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg\"\n >\n {children}\n </button>\n );\n}\n\nfunction GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount: number }) {\n const dirty = dirtyCount > 0;\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm\">\n <span\n aria-hidden=\"true\"\n className={cn(\n \"size-2 shrink-0 rounded-full\",\n dirty ? \"bg-og-status-running\" : \"bg-og-status-idle\",\n )}\n />\n <span className=\"sr-only\">\n {dirty ? `Working tree has ${dirtyCount} changed files` : \"Working tree clean\"}\n </span>\n <span className=\"truncate font-og-mono text-og-fg\">\n {git.repoCount > 1\n ? `${git.repoCount} repositories`\n : (git.branch ?? (git.isRepo ? \"(detached)\" : \"no repo\"))}\n </span>\n {(git.ahead > 0 || git.behind > 0) && (\n <span className=\"flex shrink-0 items-center gap-1.5 text-og-xs text-og-fg-subtle\">\n {git.ahead > 0 && <span>↑{git.ahead}</span>}\n {git.behind > 0 && <span>↓{git.behind}</span>}\n </span>\n )}\n {dirty && (\n <span\n aria-hidden=\"true\"\n data-contrast-audited\n className=\"ml-auto shrink-0 text-og-xs text-og-fg-subtle\"\n >\n {dirtyCount} changed\n </span>\n )}\n </div>\n );\n}\n\nfunction Segmented({\n options,\n value,\n onChange,\n}: {\n options: { value: string; label: string }[];\n value: string;\n onChange: (value: string) => void;\n}) {\n return (\n <div className=\"flex flex-wrap items-center rounded-og-sm border border-og-border p-0.5\">\n {options.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => onChange(opt.value)}\n className={cn(\n \"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n opt.value === value\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n );\n}\n\ntype FileViewState = {\n content: string | null;\n isBinary: boolean;\n /** The backend truncated the read (size cap hit) — content is a PREFIX only.\n * Editing+saving such a file would write the prefix back and corrupt it, so\n * the editor must stay read-only for a truncated read. */\n truncated: boolean;\n sizeBytes: number | null;\n loading: boolean;\n error: Error | null;\n};\n\n/**\n * Read a file's contents for the viewer pane. Calls `fs.read` (text by default;\n * the backend flags binary), decodes a base64 payload if one comes back, and\n * exposes loading/error/binary state. Re-fetches when the path changes; ignores\n * a stale resolve after the selection moves on.\n */\nfunction useFileView(\n path: string | null,\n readFile: UseSandboxFilesResult[\"readFile\"],\n reloadRevision = 0,\n): FileViewState {\n const [state, setState] = useState<FileViewState>({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: null,\n });\n useEffect(() => {\n if (!path) {\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: null,\n });\n return;\n }\n let cancelled = false;\n const abort = new AbortController();\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: true,\n error: null,\n });\n void readFile(path, { signal: abort.signal })\n .then((res) => {\n if (cancelled) return;\n const content = res.isBinary\n ? null\n : res.encoding === \"base64\"\n ? decodeBase64Utf8(res.content)\n : res.content;\n setState({\n content,\n isBinary: res.isBinary,\n truncated: res.truncated,\n sizeBytes: res.sizeBytes,\n loading: false,\n error: null,\n });\n })\n .catch((cause) => {\n if (cancelled) return;\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: cause instanceof Error ? cause : new Error(String(cause)),\n });\n });\n return () => {\n cancelled = true;\n abort.abort();\n };\n }, [path, readFile, reloadRevision]);\n return state;\n}\n\n/** Decode a base64 payload to a UTF-8 string (browser `atob` + TextDecoder). */\nfunction decodeBase64Utf8(b64: string): string {\n try {\n if (typeof atob === \"function\") {\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n } catch {\n /* fall through to returning the raw payload */\n }\n return b64;\n}\n\nfunction Notice({\n children,\n className,\n icon,\n title,\n announce,\n}: {\n children: ReactNode;\n className?: string | undefined;\n icon?: ReactNode | undefined;\n title?: string | undefined;\n announce?: \"status\" | \"alert\" | undefined;\n}) {\n return (\n <div\n role={announce}\n aria-atomic={announce ? \"true\" : undefined}\n aria-live={announce === \"alert\" ? \"assertive\" : announce === \"status\" ? \"polite\" : undefined}\n className={cn(\n \"flex h-full items-center justify-center p-4 text-center text-og-sm text-og-fg-subtle\",\n className,\n )}\n >\n <div className=\"flex max-w-sm flex-col items-center gap-2.5\">\n {icon ? (\n <span className=\"grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted shadow-sm\">\n {icon}\n </span>\n ) : null}\n {title ? <p className=\"font-medium text-og-fg\">{title}</p> : null}\n <div className=\"leading-5\">{children}</div>\n </div>\n </div>\n );\n}\n","import type {\n CapabilityUnavailableReason,\n DesktopRfbFactory,\n DesktopStreamCapability,\n} from \"@opengeni/sdk\";\nimport { LoaderCircleIcon, MonitorIcon, MousePointerClickIcon, WifiOffIcon } from \"lucide-react\";\nimport { type ReactNode, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useDesktopStream } from \"../hooks/use-desktop-stream\";\nimport type { DesktopWebSocketFactory } from \"../hooks/use-relay-frame-stream\";\n\nexport type DesktopViewerProps = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /**\n * Initial control mode. Default false (watch). When the user flips\n * \"Take control\" the viewer drives input — but only if `capability.mode`\n * permits it (server-gated; a read-only deployment disables the toggle).\n * Pass a value to control it externally; omit to let the viewer own the state.\n */\n interactive?: boolean | undefined;\n /** Render the built-in Watching ⇄ Take control toggle (default true). */\n showControlToggle?: boolean | undefined;\n scaleViewport?: boolean | undefined;\n /** Custom RFB factory (tests / a WebRTC swap). Defaults to lazy @novnc/novnc. */\n rfbFactory?: DesktopRfbFactory | undefined;\n /** Custom socket factory for the self-hosted `relay-frames` transport (tests).\n * Defaults to `new WebSocket(url)`. Mirrors `rfbFactory`. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n /**\n * Consent gate for the un-redacted (and possibly shared) pixel plane. Rendered\n * BEFORE connecting whenever the desktop requires acknowledgment that hasn't\n * been given. Call `onAccept` to record consent (the host wires it to\n * `client.acknowledgeStream` + a re-negotiate). When omitted, a default\n * banner is shown.\n */\n renderConsentGate?: ((onAccept: () => void, shared: boolean) => ReactNode) | undefined;\n /** Called when the default consent gate's accept button is pressed. */\n onAcknowledge?: (() => void) | undefined;\n /**\n * Whether the host has the viewer attach engaged (i.e. the user has opted into\n * watching — the parent's `watchDesktop`/`attachDesktop`). Drives the\n * cold-state behaviour: when watching, a cold-but-warmable lease AUTO-WARMS\n * (and re-warms when the box drains) instead of dead-ending. When omitted we\n * infer it from a recorded consent (the default gate's accept), so the\n * component still self-heals after the first acknowledgment.\n */\n watching?: boolean | undefined;\n /**\n * Request a (re)warm of the sandbox WITHOUT re-acknowledging (the consent has\n * already been recorded — only the box drained). The host wires this to\n * \"engage the viewer attach + re-negotiate\". Called automatically when a\n * watched desktop is found cold-but-warmable, and behind the manual retry on\n * the warming notice. Distinct from `onAcknowledge`, which is the FIRST,\n * consent-bearing warm.\n */\n onWarm?: (() => void) | undefined;\n /** Shown when transport is null (headless backend / degraded / disabled). */\n renderUnavailable?: ((reason: CapabilityUnavailableReason | null) => ReactNode) | undefined;\n /** Shown while the box is cold/warming (no live address yet). */\n renderWarming?: (() => ReactNode) | undefined;\n /** Shown when the per-session viewer cap (429) was hit. */\n renderViewerCap?: (() => ReactNode) | undefined;\n /** Surface the 429 cap state from `useSessionCapabilities().viewerCapReached`. */\n viewerCapReached?: boolean | undefined;\n /**\n * Connect watchdog (ms): if a live url is present but the RFB hasn't connected\n * within this window, surface a \"Couldn't connect\" + Reconnect instead of an\n * eternal idle scrim. Default 13000. 0 disables the watchdog.\n */\n connectTimeoutMs?: number | undefined;\n className?: string | undefined;\n};\n\n/**\n * The derived desktop surface state. A single source of truth so the overlay,\n * the scrim, the auto-warm effect, and the watchdog all agree. Priority order\n * (highest first): viewer-cap → unavailable → consent → warming → connect-failed\n * → error → connecting → connected.\n */\ntype DesktopUiState =\n | \"viewer_cap\" // 429 — the per-session live-viewer limit is reached.\n | \"unavailable\" // genuinely unsupported (headless/policy/os/backend/not-provisioned).\n | \"consent\" // un-redacted/shared plane needs acknowledgment first.\n | \"warming\" // cold-but-warmable: box not running yet (we auto-warm + spin).\n | \"connecting\" // url is live, RFB negotiating/handshaking (we spin, with a watchdog).\n | \"connect_failed\" // watchdog fired: url present but never connected.\n | \"error\" // RFB error / securityfailure after a connect.\n | \"connected\"; // live framebuffer painting.\n\n// Media-inspection exception: the framebuffer sits on true black so screenshots\n// and desktop pixels are not tinted by the product chrome palette.\nconst DESKTOP_MEDIA_BACKGROUND = \"#000\";\n\n/** Reasons that are genuinely-unavailable (never warmable from the viewer). A\n * `lease_cold` is deliberately EXCLUDED — that's the warmable cold state. */\nfunction isHardUnavailable(reason: CapabilityUnavailableReason | null): boolean {\n switch (reason) {\n case \"backend_unsupported\":\n case \"os_unsupported\":\n case \"not_provisioned\":\n case \"disabled_by_policy\":\n case \"tier_headless\":\n return true;\n default:\n // null or \"lease_cold\" → not a hard reason (cold-but-warmable / live).\n return false;\n }\n}\n\n/**\n * The desktop surface: a noVNC client connecting to the Channel-B scoped tunnel\n * URL from the capability doc. Owns the mount `<div ref>`, drives\n * `useDesktopStream` (SSR-safe lazy RFB), and renders a real\n * cold → warming → connecting → connected → error state machine with live\n * feedback (spinners, transitions) — never a dead black box with stale text.\n *\n * The read-only vs interactive decision is enforced server-first\n * (`capability.mode`): when the deployment advertises mode \"interactive\" the\n * viewer can TAKE CONTROL and drive the mouse & keyboard into the box's :0; a\n * \"read-only\" deployment disables the take-control affordance (graceful, with a\n * reason).\n *\n * Warming: a cold-but-warmable lease (`reason: \"lease_cold\"`) is NOT a dead end.\n * When the user is watching (consented), the viewer asks the host to (re)warm\n * the box (`onWarm`) and shows a \"Warming…\" spinner; if the box later drains to\n * cold it re-warms. Genuinely-unavailable surfaces (headless/policy/os/backend)\n * keep a clear, static unavailable notice.\n */\nexport function DesktopViewer({\n capability,\n interactive,\n showControlToggle = true,\n scaleViewport,\n rfbFactory,\n webSocketFactory,\n renderConsentGate,\n onAcknowledge,\n watching,\n onWarm,\n renderUnavailable,\n renderWarming,\n renderViewerCap,\n viewerCapReached,\n connectTimeoutMs = 13_000,\n className,\n}: DesktopViewerProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [consented, setConsented] = useState(false);\n // Local control state when not externally controlled. The server gate\n // (`capability.mode`) is the hard ceiling — a read-only deployment can never\n // be flipped to interactive regardless of this toggle.\n const [takeControl, setTakeControl] = useState(interactive ?? false);\n const externallyControlled = interactive !== undefined;\n const serverAllowsControl = capability?.mode !== \"read-only\";\n const wantControl = externallyControlled ? interactive : takeControl;\n const inControl = Boolean(wantControl) && serverAllowsControl;\n\n // Whether the host has the viewer attach engaged. Prefer the explicit prop;\n // fall back to a locally-recorded consent so the component still self-heals\n // (auto-warms / re-warms) once the user has accepted the gate at least once.\n const isWatching = watching ?? consented;\n\n // ── Decide the rendered state (before touching the stream hook) ─────────────\n const transportNull = !capability || capability.transport === null;\n const reason = capability?.reason ?? null;\n const needsAck =\n capability?.requiresAcknowledgment === true && capability.acknowledged !== true && !consented;\n // No live address yet on an otherwise-live transport (post-ack, mid-warm).\n const noLiveAddress = Boolean(capability) && !transportNull && !capability!.url;\n\n // Cold-but-warmable: the lease is cold (`lease_cold`) OR the transport is up\n // but no url has been minted yet — either way warming, not unavailable.\n const coldWarmable =\n (transportNull && reason === \"lease_cold\") || (!transportNull && noLiveAddress);\n // Genuinely-unavailable: transport null for a HARD reason (not lease_cold).\n const hardUnavailable = transportNull && isHardUnavailable(reason);\n\n const accept = () => {\n setConsented(true);\n onAcknowledge?.();\n };\n\n // Release control WITHOUT ever swallowing a key the desktop needs. Esc, and\n // every other key, pass straight through to noVNC/:0 — vital for vim, menus,\n // and dialogs inside the box. Exactly ONE non-trapping keyboard exit:\n // • A single non-conflicting chord — Ctrl+Alt+Shift pressed on its own (no\n // other key) — which no app binds, so it never eats a real keystroke.\n // (Plus the always-visible \"Return control\" button in the in-control bar.)\n //\n // We deliberately DO NOT release on pointer-leave (or window blur): those fire\n // as a SIDE EFFECT of connecting — the noVNC canvas re-laying-out, the surface\n // grabbing focus, the connecting scrim swapping in — which bounced control\n // straight back to \"watch\" the instant the user took it. Control is given up\n // only on an explicit, intentional gesture (the button or the chord).\n useEffect(() => {\n if (!inControl || externallyControlled) return;\n const onKey = (event: KeyboardEvent) => {\n // Only the bare modifier chord releases; if any non-modifier key is also\n // down (event.key is a real key like \"a\"/\"Escape\"), let it pass through.\n if (\n event.ctrlKey &&\n event.altKey &&\n event.shiftKey &&\n (event.key === \"Control\" || event.key === \"Alt\" || event.key === \"Shift\")\n ) {\n event.preventDefault();\n setTakeControl(false);\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n };\n }, [inControl, externallyControlled]);\n\n // The hook is always called (rules of hooks); it stays idle until `url` is set.\n // Do NOT open a socket while the viewer-cap (429) notice is showing — the slot\n // is already exhausted, so connecting would only burn a doomed attempt (and in\n // tests leak an unhandled ws error from the never-resolving tunnel URL).\n const connectCapability = !transportNull && !needsAck && !viewerCapReached ? capability : null;\n const stream = useDesktopStream({\n capability: connectCapability,\n containerRef,\n interactive: inControl,\n ...(scaleViewport !== undefined ? { scaleViewport } : {}),\n ...(rfbFactory ? { rfbFactory } : {}),\n ...(webSocketFactory ? { webSocketFactory } : {}),\n });\n\n const connected = stream.state === \"connected\";\n const hasLiveUrl = Boolean(connectCapability?.url);\n // The latest stream state, read without making it an effect dependency — so the\n // re-attach below can consult it on a visibility change WITHOUT re-subscribing\n // (and re-firing) every time the state walks idle→negotiating→connected.\n const streamStateRef = useRef(stream.state);\n streamStateRef.current = stream.state;\n\n // ── AUTO-WARM ───────────────────────────────────────────────────────────────\n // When the user is watching and the desktop is cold-but-warmable, ask the host\n // to (re)warm the box. This covers BOTH (a) the user just accepted consent and\n // (b) a previously-warm box that drained back to cold under a live viewer. We\n // fire once per distinct cold episode (keyed on the lease epoch + url) so we\n // don't spam the attach while a single warm is in flight. `onWarm` is the\n // no-re-ack path; if the host only wired `onAcknowledge` (no `onWarm`), the\n // first warm still rides consent and subsequent drains fall back to it.\n const warmKeyRef = useRef<string | null>(null);\n useEffect(() => {\n if (!isWatching || !coldWarmable || needsAck || viewerCapReached) {\n // Reset the de-dupe once we leave the cold episode so a future drain warms.\n if (!coldWarmable) warmKeyRef.current = null;\n return;\n }\n const warm = onWarm ?? onAcknowledge;\n if (!warm) return;\n // Key the de-dupe on the cell fields available to the viewer: while cold,\n // `reason`/`url`/`expiresAt` are stable, so we warm exactly once; once the box\n // warms the cell changes (url+expiresAt minted) and `coldWarmable` flips false,\n // resetting the ref so a later drain re-warms.\n const key = `${capability?.reason ?? \"\"}:${capability?.url ?? \"\"}:${capability?.expiresAt ?? \"\"}`;\n if (warmKeyRef.current === key) return; // already kicked this episode.\n warmKeyRef.current = key;\n warm();\n // capability identity is the trigger; leaseEpoch/expiresAt key the episode.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isWatching, coldWarmable, needsAck, viewerCapReached, capability, onWarm, onAcknowledge]);\n\n // ── TAB RE-ATTACH ───────────────────────────────────────────────────────────\n // The \"stuck Watching, never connects\" bug: the RFB socket never (re)fires when\n // the Desktop tab is shown without a refresh. When a live url exists but we are\n // not connected/connecting, nudge the stream hook to (re)attach — on mount, on\n // the document becoming visible, and on a fresh capability. Re-attach is cheap\n // (disconnect-old/connect-new) and idempotent: the hook ignores it once live.\n useEffect(() => {\n if (!hasLiveUrl || typeof document === \"undefined\") return;\n const maybeReattach = () => {\n if (document.visibilityState !== \"visible\") return;\n // Only kick a socket that never OPENED (idle — e.g. the tab was hidden when\n // the url arrived, so the connect effect bailed on a missing container).\n // Deliberately NOT on \"error\": that surfaces an overlay with an explicit\n // Reconnect, and auto-retrying here would hammer (reconnect → error →\n // reconnect…). We read the live state via a ref so this effect does NOT\n // depend on `stream.state` — depending on it re-ran the effect on every\n // transition and re-fired the kick, which is the reconnect loop.\n if (streamStateRef.current === \"idle\") stream.reconnect();\n };\n // The connect effect already opens the socket on mount / a fresh url. The\n // ONLY gap it can't self-heal is a tab that was hidden when the url arrived\n // (container not in the DOM → connect effect bailed to idle): the socket then\n // never (re)fires on its own. So we revive it on the tab becoming visible —\n // NOT on mount (that double-opens the socket the connect effect just opened).\n document.addEventListener(\"visibilitychange\", maybeReattach);\n return () => document.removeEventListener(\"visibilitychange\", maybeReattach);\n // Re-run ONLY on a fresh live url, never on a state transition.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasLiveUrl, connectCapability?.url]);\n\n // ── CONNECT WATCHDOG ─────────────────────────────────────────────────────────\n // If a live url is present but the RFB hasn't reached \"connected\" within the\n // window, stop spinning forever and surface \"Couldn't connect\" + Reconnect.\n // Cleared whenever we connect, the url rotates, or the user reconnects.\n const [connectTimedOut, setConnectTimedOut] = useState(false);\n // Bumped on every manual reconnect so the watchdog re-arms its window (the\n // stream hook's url/error don't change on a reconnect, so we need our own key).\n const [reconnectNonce, setReconnectNonce] = useState(0);\n useEffect(() => {\n // Re-arm on a fresh url / a reconnect and clear the moment we connect or hit a\n // real RFB error. We deliberately do NOT key on `stream.state`, so the benign\n // idle→connecting walk doesn't keep resetting the window — the full timeout\n // runs from the url becoming live.\n setConnectTimedOut(false);\n if (!hasLiveUrl || connected || stream.error || connectTimeoutMs <= 0) return;\n const timer = setTimeout(() => setConnectTimedOut(true), connectTimeoutMs);\n return () => clearTimeout(timer);\n }, [\n hasLiveUrl,\n connected,\n connectTimeoutMs,\n connectCapability?.url,\n stream.error,\n reconnectNonce,\n ]);\n\n const reconnect = () => {\n setConnectTimedOut(false);\n setReconnectNonce((n) => n + 1);\n stream.reconnect();\n };\n\n // ── Resolve the single UI state (priority order) ────────────────────────────\n let uiState: DesktopUiState;\n if (viewerCapReached) uiState = \"viewer_cap\";\n else if (hardUnavailable) uiState = \"unavailable\";\n else if (needsAck && !isWatching) uiState = \"consent\";\n else if (coldWarmable) uiState = \"warming\";\n else if (connected) uiState = \"connected\";\n else if (stream.error) uiState = \"error\";\n else if (connectTimedOut) uiState = \"connect_failed\";\n else uiState = \"connecting\"; // hasLiveUrl && not yet connected (idle/negotiating/connecting).\n\n // An overlay blocks the surface for every state except connected; the toggle\n // and the live scrim are suppressed under an overlay.\n const overlayShown = uiState !== \"connected\" && uiState !== \"connecting\";\n const showToggle = showControlToggle && !overlayShown;\n\n let overlay: ReactNode = null;\n switch (uiState) {\n case \"viewer_cap\":\n overlay =\n renderViewerCap?.() ??\n defaultNotice(\n \"Too many viewers\",\n \"This session has reached its live-viewer limit. Try again shortly.\",\n );\n break;\n case \"unavailable\":\n overlay =\n renderUnavailable?.(reason) ??\n defaultNotice(\"Desktop unavailable\", unavailableCopy(reason));\n break;\n case \"consent\":\n overlay = renderConsentGate ? (\n renderConsentGate(accept, capability?.shared ?? false)\n ) : (\n <DefaultConsentGate shared={capability?.shared ?? false} onAccept={accept} />\n );\n break;\n case \"warming\":\n overlay = renderWarming?.() ?? <WarmingNotice />;\n break;\n case \"connect_failed\":\n overlay = defaultNotice(\n \"Couldn’t connect\",\n \"The desktop is warm but the live stream didn’t come up. This usually clears on a retry.\",\n reconnect,\n );\n break;\n case \"error\":\n overlay = defaultNotice(\n \"Desktop disconnected\",\n stream.error?.message ?? \"The stream dropped.\",\n reconnect,\n );\n break;\n case \"connecting\":\n case \"connected\":\n overlay = null;\n break;\n }\n\n return (\n <div\n className={cn(\n \"relative h-full w-full overflow-hidden\",\n inControl && \"ring-2 ring-inset ring-og-accent\",\n className,\n )}\n style={{ backgroundColor: DESKTOP_MEDIA_BACKGROUND }}\n data-opengeni-desktop\n data-state={stream.state}\n data-ui-state={uiState}\n data-in-control={inControl ? \"true\" : undefined}\n >\n {/* noVNC mount target. It appends a `width:100%;height:100%` `_screen` div\n and AUTOSCALES the 1280x800 framebuffer to fit THIS box. We pin it to\n the bounded `relative` wrapper with `absolute inset-0` (not just\n `h-full w-full`) so its measured size is ALWAYS the panel — never the\n canvas content. A content-sized mount is exactly what makes noVNC\n measure a huge screen and paint the desktop \"zoomed in\". `overflow-hidden`\n keeps the centered (margin:auto) canvas from ever spilling the panel. */}\n <div\n ref={containerRef}\n className=\"absolute inset-0 overflow-hidden\"\n data-opengeni-desktop-canvas\n data-state={stream.state}\n />\n\n {/* Idle / connecting scrim: a quiet, ALIVE \"connecting to the desktop\"\n state behind the canvas so the surface never reads as a dead black\n rectangle before the first framebuffer paints. Only shown in the\n `connecting` UI-state (every other non-connected state has an explicit\n overlay). A spinner + transitional copy makes it feel live. */}\n {uiState === \"connecting\" && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle\">\n <span className=\"relative flex items-center justify-center\">\n <MonitorIcon className=\"size-8 opacity-30\" strokeWidth={1.5} />\n <LoaderCircleIcon\n className=\"absolute size-12 animate-og-spin text-og-accent opacity-70\"\n strokeWidth={1.25}\n />\n </span>\n <span className=\"text-og-sm\">Connecting to the desktop…</span>\n </div>\n )}\n\n {/* Take-control affordance. Two distinct states:\n - WATCHING → a prominent, centered call-to-action button overlaid on\n the desktop (the primary CTA; tasteful so the screen stays visible).\n - IN CONTROL → a small top bar (\"You're in control · click Return\n control (or Ctrl+Alt+Shift)\") with a clearly-visible Return-control\n button; the desktop stays fully usable and EVERY key — Esc included —\n passes through to the box. Server-gated: when the deployment is\n read-only the CTA renders disabled with a reason so it degrades\n gracefully. */}\n {showToggle && !externallyControlled && inControl && (\n <InControlBar\n shared={capability?.shared ?? false}\n onRelease={() => setTakeControl(false)}\n />\n )}\n {/* The big CTA only appears once the framebuffer is live + the viewer is\n watching: before connect the scrim already communicates state, so we\n don't double up. A read-only deployment (serverAllowsControl=false) still\n surfaces the CTA disabled-with-reason once connected, so it degrades\n gracefully and stays discoverable. */}\n {showToggle && !externallyControlled && !inControl && connected && (\n <TakeControlCallToAction\n disabled={!serverAllowsControl}\n disabledReason={\n !serverAllowsControl\n ? capability?.client === \"frames\"\n ? \"View-only — live control isn't available for this machine yet.\"\n : \"This deployment streams the desktop read-only.\"\n : undefined\n }\n onTakeControl={() => setTakeControl(true)}\n />\n )}\n\n {overlay && (\n <div className=\"absolute inset-0 flex items-center justify-center p-4\">{overlay}</div>\n )}\n </div>\n );\n}\n\n/**\n * The primary WATCHING-state call-to-action: a large, centered pill button\n * overlaid on the desktop inviting the viewer to drive the mouse & keyboard.\n * Tasteful by default — it sits in the LOWER-center with a soft scrim only behind\n * the button (not the whole screen) and lifts on hover, so a watcher can still see\n * the agent work. Server-gated: when the deployment is read-only (or the desktop\n * hasn't connected yet) it renders disabled with a reason. Accessible: a real\n * <button> with a title, focus ring, and Enter/Space activation.\n */\nfunction TakeControlCallToAction({\n disabled,\n disabledReason,\n onTakeControl,\n}: {\n disabled: boolean;\n disabledReason?: string | undefined;\n onTakeControl: () => void;\n}) {\n return (\n // The wrapper spans the surface but is click-through (pointer-events-none); only\n // the button itself is interactive, so watchers can still see the desktop.\n <div className=\"pointer-events-none absolute inset-0 flex items-end justify-center pb-[8%]\">\n <button\n type=\"button\"\n disabled={disabled}\n aria-label=\"Take control of the desktop\"\n title={disabled ? disabledReason : \"Take control of the desktop\"}\n onClick={onTakeControl}\n className={cn(\n \"group pointer-events-auto flex items-center gap-3 rounded-og-lg border px-5 py-3\",\n \"border-og-border\",\n \"bg-og-bg/85 backdrop-blur-md\",\n \"shadow-og-lg\",\n \"outline-none transition-all duration-150 ease-out\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg\",\n disabled\n ? \"cursor-not-allowed opacity-60\"\n : cn(\n \"cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100\",\n \"hover:border-og-accent\",\n \"hover:bg-og-accent/10\",\n ),\n )}\n >\n <span\n className={cn(\n \"flex size-9 shrink-0 items-center justify-center rounded-full transition-colors\",\n \"bg-og-accent\",\n \"text-og-accent-fg\",\n disabled ? \"\" : \"group-hover:scale-105\",\n )}\n >\n <MousePointerClickIcon className=\"size-5\" strokeWidth={2} />\n </span>\n <span className=\"flex flex-col items-start leading-tight\">\n <span className=\"text-og-base font-semibold text-og-fg\">Take control</span>\n <span className=\"text-og-xs text-og-fg-subtle\">\n {disabled && disabledReason ? disabledReason : \"Drive the mouse & keyboard\"}\n </span>\n </span>\n </button>\n </div>\n );\n}\n\n/**\n * The IN-CONTROL state: a small top bar so the desktop stays fully usable while\n * driving (the accent ring around the viewport carries the primary \"you're\n * driving\" signal). Every keystroke — including Esc — passes through to the box,\n * so release lives only on explicit, non-trapping affordances: a clearly-visible\n * \"Return control\" button and the Ctrl+Alt+Shift chord. The button is solid\n * (not a faint ghost) so it's always discoverable as the way out.\n */\nfunction InControlBar({ shared, onRelease }: { shared: boolean; onRelease: () => void }) {\n return (\n <div className=\"pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between gap-2 p-2\">\n <span className=\"pointer-events-auto inline-flex items-center gap-2 rounded-og-sm bg-og-accent px-2.5 py-1 text-og-xs font-medium text-og-accent-fg shadow-og-md\">\n <span className=\"size-1.5 animate-pulse rounded-full bg-current\" aria-hidden />\n You're in control\n <span className=\"opacity-75\">· click Return control (or Ctrl+Alt+Shift)</span>\n </span>\n <div className=\"pointer-events-auto flex items-center gap-1.5\">\n {shared && (\n <span className=\"rounded-og-sm bg-og-status-failed/85 px-2 py-0.5 text-og-xs text-og-accent-fg\">\n Shared sandbox — others are watching\n </span>\n )}\n <button\n type=\"button\"\n onClick={onRelease}\n title=\"Return control (or press Ctrl+Alt+Shift)\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors\",\n \"border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm\",\n \"outline-none hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent\",\n )}\n >\n <MonitorIcon className=\"size-3.5\" strokeWidth={2} aria-hidden />\n Return control\n </button>\n </div>\n </div>\n );\n}\n\nfunction unavailableCopy(reason: CapabilityUnavailableReason | null): string {\n switch (reason) {\n case \"backend_unsupported\":\n return \"This sandbox backend cannot stream a desktop.\";\n case \"tier_headless\":\n return \"This deployment is headless — terminal, files, and diff only.\";\n case \"os_unsupported\":\n return \"The sandbox OS does not support a desktop stream.\";\n case \"not_provisioned\":\n return \"This sandbox doesn't have a desktop yet.\";\n case \"disabled_by_policy\":\n return \"Desktop streaming is disabled on this deployment.\";\n case \"lease_cold\":\n // Should not reach the unavailable notice (lease_cold warms), but keep\n // friendly copy as a fallback rather than the old dead-end wording.\n return \"Waiting for the sandbox to start…\";\n default:\n return \"The desktop isn’t available for this sandbox.\";\n }\n}\n\n/**\n * The WARMING state: a cold-but-warmable box that is being spun up. A spinner +\n * clear, accurate copy — and (when the host wired a manual warm) an explicit\n * retry so a slow warm never feels stuck. This is the cure for the old\n * \"Desktop unavailable — Start a turn or attach to warm it\" dead-end: the box IS\n * being warmed automatically; we just tell the user it's happening.\n */\nfunction WarmingNotice({ onRetry }: { onRetry?: (() => void) | undefined }) {\n return (\n <div className=\"flex max-w-sm flex-col items-center gap-3 rounded-og-lg border border-og-border bg-og-bg/90 p-5 text-center text-og-base text-og-fg backdrop-blur-sm\">\n <LoaderCircleIcon className=\"size-7 animate-og-spin text-og-accent\" strokeWidth={1.5} />\n <div className=\"space-y-1\">\n <div className=\"font-medium\">Warming the sandbox…</div>\n <p className=\"text-og-sm text-og-fg-subtle\">\n Spinning up the desktop — this takes a few seconds.\n </p>\n </div>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm text-og-fg-muted transition-colors hover:text-og-fg pointer-coarse:min-h-11\"\n >\n Taking too long? Retry\n </button>\n )}\n </div>\n );\n}\n\nfunction DefaultConsentGate({ shared, onAccept }: { shared: boolean; onAccept: () => void }) {\n return (\n <div className=\"max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg\">\n <div className=\"mb-1 font-medium\">Watch the live desktop?</div>\n <p className=\"mb-3 text-og-sm text-og-fg-subtle\">\n The desktop pixel stream is <strong>un-redacted</strong> — it can show secrets the agent\n prints on screen.\n {shared\n ? \" This sandbox is shared: you'll also see other sessions' agents on the same screen.\"\n : \"\"}\n </p>\n <button\n type=\"button\"\n onClick={onAccept}\n className=\"rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg pointer-coarse:min-h-11\"\n >\n I understand — show the desktop\n </button>\n </div>\n );\n}\n\nfunction defaultNotice(title: string, body: string, onRetry?: () => void): ReactNode {\n return (\n <div className=\"max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg\">\n <div className=\"mb-1 flex items-center justify-center gap-1.5 font-medium\">\n {onRetry && <WifiOffIcon className=\"size-4 opacity-70\" strokeWidth={1.75} />}\n {title}\n </div>\n <p className=\"text-og-sm text-og-fg-subtle\">{body}</p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-3 rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm transition-colors hover:border-og-accent pointer-coarse:min-h-11\"\n >\n Reconnect\n </button>\n )}\n </div>\n );\n}\n","import {\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n ChevronsLeftRightIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n XIcon,\n} from \"lucide-react\";\nimport {\n Group,\n Panel,\n Separator,\n useDefaultLayout,\n usePanelRef,\n type LayoutStorage,\n} from \"react-resizable-panels\";\nimport { cn } from \"../lib/cn\";\n\nexport type WorkspaceTab = {\n id: string;\n label: ReactNode;\n /** Rendered as the active surface. */\n content: ReactNode;\n /** A small badge after the label (e.g. dirty count, live pill). */\n badge?: ReactNode | undefined;\n};\n\nexport type WorkspaceDockProps = {\n /** The chat / primary pane shown beside the dock. */\n primary: ReactNode;\n tabs: WorkspaceTab[];\n /** Controlled active tab. Falls back to the first tab. */\n activeTab?: string | undefined;\n onActiveTabChange?: ((id: string) => void) | undefined;\n /** Controlled collapsed state for hosts that expose their own dock toggle. */\n collapsed?: boolean | undefined;\n onCollapsedChange?: ((collapsed: boolean) => void) | undefined;\n /** A status accessory pinned to the right of the tab strip, left of the\n * maximize/collapse controls (e.g. the machine-state chip). Renders in both\n * the docked header and the full-screen overlay header. */\n headerAccessory?: ReactNode | undefined;\n /** Optional host navigation shown at the start of the phone overlay header.\n * It is intentionally absent from desktop dock chrome. */\n mobileLeadingControl?: ReactNode | undefined;\n /** Persisted layout id (localStorage key) for react-resizable-panels. */\n autoSaveId?: string | undefined;\n /** Default dock width as a percent of the session area. */\n defaultSize?: number | undefined;\n minSize?: number | undefined;\n maxSize?: number | undefined;\n className?: string | undefined;\n};\n\nconst SERVER_LAYOUT_STORAGE: LayoutStorage = {\n getItem: () => null,\n setItem: () => {},\n};\n\n/** `useDefaultLayout` defaults to the ambient `localStorage` identifier when\n * storage is undefined, which throws during SSR. Access can also throw in a\n * browser that blocks storage (sandboxed frames/private policy), so both cases\n * use a stable, non-persisting implementation instead. */\nfunction getLayoutStorage(): LayoutStorage {\n if (typeof window === \"undefined\") return SERVER_LAYOUT_STORAGE;\n try {\n return window.localStorage;\n } catch {\n return SERVER_LAYOUT_STORAGE;\n }\n}\n\n/**\n * The resizable / collapsible / maximizable right-hand Workspace dock. Replaces\n * a fixed grid column: drag the separator to set width, collapse to a thin rail\n * that re-opens on click, and maximize to a full-workspace overlay (Esc /\n * restore button returns). Layout persists via `useDefaultLayout` keyed on\n * `autoSaveId`. Maximize is a mode ABOVE the Group (a `fixed inset-0` overlay) —\n * pushing a Panel to ~100% still fights min sizes and leaves a chat sliver.\n *\n * Below {@link DOCK_OVERLAY_BREAKPOINT} the side-by-side split can't work on a\n * phone-width viewport, so the resizable panels are dropped entirely: the\n * primary pane goes full-width and the dock becomes a full-screen overlay driven\n * by the same `collapsed` / `onCollapsedChange` contract (collapsed → hidden).\n * No drag splitter renders below the breakpoint.\n */\nconst useDockLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * The dock stops being a side column at this width and becomes a full-screen\n * overlay — matches the app's rail-drawer breakpoint (the single `isMobile`\n * source). The package can't read app context, so it detects the width itself.\n */\nconst DOCK_OVERLAY_BREAKPOINT = 1024;\n\n/** SSR-safe `(max-width: …)` match; false until mounted, then live. */\nfunction useIsNarrow(maxWidth: number): boolean {\n const [narrow, setNarrow] = useState(false);\n useDockLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n const mql = window.matchMedia(`(max-width: ${maxWidth - 1}px)`);\n const update = () => setNarrow(mql.matches);\n update();\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", update);\n return () => mql.removeEventListener(\"change\", update);\n }\n // Legacy Safari.\n mql.addListener(update);\n return () => mql.removeListener(update);\n }, [maxWidth]);\n return narrow;\n}\n\nexport function WorkspaceDock({\n primary,\n tabs,\n activeTab,\n onActiveTabChange,\n collapsed: collapsedProp,\n onCollapsedChange,\n headerAccessory,\n mobileLeadingControl,\n autoSaveId = \"og.session.dock\",\n defaultSize = 34,\n minSize = 22,\n maxSize = 70,\n className,\n}: WorkspaceDockProps) {\n const narrow = useIsNarrow(DOCK_OVERLAY_BREAKPOINT);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const reopenRef = useRef<HTMLButtonElement | null>(null);\n const returnFocusRef = useRef<HTMLElement | null>(null);\n const initialNarrowFocusRef = useRef(false);\n const dockPanelRef = usePanelRef();\n const [internalCollapsed, setInternalCollapsed] = useState(false);\n const [maximized, setMaximized] = useState(false);\n const [internalTab, setInternalTab] = useState(tabs[0]?.id ?? \"\");\n const collapsed = collapsedProp ?? internalCollapsed;\n // When the host supplies `collapsed` it owns the open/close affordance (e.g.\n // the app header's panel toggle) — the dock must not offer a SECOND\n // open/close control (duplicate buttons with near-identical icons read as a\n // bug). Standalone (uncontrolled) usage keeps the built-in collapse button\n // and the thin re-open rail as its only affordances.\n const hostControlled = collapsedProp !== undefined;\n const previousCollapsedRef = useRef(collapsed);\n\n // Persisted layout (width split) keyed by autoSaveId.\n const { defaultLayout, onLayoutChanged } = useDefaultLayout({\n panelIds: [\"primary\", \"dock\"],\n storage: getLayoutStorage(),\n id: autoSaveId,\n });\n\n const requestedTab = activeTab ?? internalTab;\n const tabIds = tabs.map((tab) => tab.id);\n const firstTabId = tabs[0]?.id ?? \"\";\n const requestedTabIsValid = tabIds.includes(requestedTab);\n const current = requestedTabIsValid ? requestedTab : firstTabId;\n const setTab = useCallback(\n (id: string) => {\n setInternalTab(id);\n onActiveTabChange?.(id);\n },\n [onActiveTabChange],\n );\n const setCollapsed = useCallback(\n (next: boolean) => {\n setInternalCollapsed((previous) => (previous === next ? previous : next));\n onCollapsedChange?.(next);\n },\n [onCollapsedChange],\n );\n\n useDockLayoutEffect(() => {\n if (collapsedProp === undefined) {\n return;\n }\n if (collapsedProp) {\n dockPanelRef.current?.collapse();\n setMaximized(false);\n } else {\n dockPanelRef.current?.expand();\n }\n }, [collapsedProp, dockPanelRef]);\n\n // Treat the narrow overlay like a real modal: remember the external opener,\n // move focus into the selected tab, and restore it on close. The standalone\n // desktop dock instead focuses its newly-visible reopen rail after collapse.\n useEffect(() => {\n const previous = previousCollapsedRef.current;\n previousCollapsedRef.current = collapsed;\n if (previous === collapsed) return;\n\n if (!collapsed) {\n const active = document.activeElement;\n const workspaceSurface = rootRef.current?.querySelector<HTMLElement>(\n narrow ? '[role=\"dialog\"]' : \"[data-workspace-surface]\",\n );\n if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {\n returnFocusRef.current = active;\n }\n if (narrow) {\n const frame = requestAnimationFrame(() => {\n rootRef.current\n ?.querySelector<HTMLElement>('[role=\"dialog\"] [role=\"tab\"][aria-selected=\"true\"]')\n ?.focus();\n });\n return () => cancelAnimationFrame(frame);\n }\n return;\n }\n\n const frame = requestAnimationFrame(() => {\n if (!hostControlled) {\n reopenRef.current?.focus();\n return;\n }\n const target = returnFocusRef.current;\n if (target?.isConnected) target.focus();\n });\n return () => cancelAnimationFrame(frame);\n }, [collapsed, hostControlled, narrow]);\n\n // On the first narrow render there is no collapsed-state transition to drive\n // focus into the modal: the overlay can already be open at mount. Place focus\n // on its selected tab once, just as we do when an existing overlay is opened.\n useEffect(() => {\n if (!narrow || collapsed || initialNarrowFocusRef.current) return;\n initialNarrowFocusRef.current = true;\n const active = document.activeElement;\n const workspaceSurface = rootRef.current?.querySelector<HTMLElement>('[role=\"dialog\"]');\n if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {\n returnFocusRef.current = active;\n }\n const frame = requestAnimationFrame(() => {\n workspaceSurface?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')?.focus();\n });\n return () => cancelAnimationFrame(frame);\n }, [collapsed, narrow]);\n\n // Keep the active tab valid if the available tabs change.\n useEffect(() => {\n if (firstTabId && !requestedTabIsValid) {\n setTab(firstTabId);\n }\n // Depend on tab identity, not the tab content objects. Session live events\n // rebuild tab JSX frequently; only id changes can invalidate the active tab.\n }, [firstTabId, requestedTabIsValid, setTab]);\n\n const collapse = useCallback(() => {\n dockPanelRef.current?.collapse();\n setCollapsed(true);\n }, [dockPanelRef, setCollapsed]);\n const expand = useCallback(() => {\n dockPanelRef.current?.expand();\n setCollapsed(false);\n }, [dockPanelRef, setCollapsed]);\n\n // Esc restores from maximize (desktop) and closes the mobile overlay. Tab is\n // fenced inside either full-screen surface: both visually cover the host, so\n // keyboard focus must never move into invisible content behind them.\n useEffect(() => {\n const overlayOpen = maximized || (narrow && !collapsed);\n if (!overlayOpen) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (maximized) setMaximized(false);\n else collapse();\n return;\n }\n if (event.key !== \"Tab\") return;\n const surface = rootRef.current?.querySelector<HTMLElement>(\n narrow ? '[role=\"dialog\"]' : \"[data-workspace-surface]\",\n );\n if (!surface) return;\n const focusable = Array.from(surface.querySelectorAll<HTMLElement>(\"*\")).filter(\n (element) =>\n element.tabIndex >= 0 &&\n !element.hasAttribute(\"disabled\") &&\n element.getClientRects().length > 0 &&\n getComputedStyle(element).visibility !== \"hidden\",\n );\n const first = focusable[0];\n const last = focusable.at(-1);\n const active = document.activeElement;\n if (!first || !last) {\n event.preventDefault();\n surface.focus();\n return;\n }\n if (!surface.contains(active)) {\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n return;\n }\n if ((event.shiftKey && active === first) || (!event.shiftKey && active === last)) {\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n return;\n }\n // Focus can emerge from a shadow-DOM editor/highlighter host at a point\n // the light-DOM order cannot predict. Repair any escape after the browser\n // performs its default Tab move.\n requestAnimationFrame(() => {\n const next = document.activeElement;\n if (!surface.contains(next)) (event.shiftKey ? last : first).focus();\n });\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [maximized, narrow, collapsed, collapse]);\n\n // Below the breakpoint the dock is a full-screen overlay, not a resizable\n // column: primary goes full-width and no splitter ever mounts. The overlay is\n // driven by the same `collapsed` contract (collapsed → hidden).\n if (narrow) {\n return (\n <div ref={rootRef} className={cn(\"relative flex h-full min-h-0 w-full min-w-0\", className)}>\n <div\n data-workspace-primary\n aria-hidden={!collapsed ? true : undefined}\n className=\"min-h-0 min-w-0 flex-1\"\n inert={!collapsed ? true : undefined}\n >\n {primary}\n </div>\n <div\n data-workspace-surface\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Workspace\"\n className=\"fixed inset-0 z-40 flex flex-col bg-og-bg\"\n hidden={collapsed}\n style={{\n paddingTop: \"env(safe-area-inset-top)\",\n paddingBottom: \"env(safe-area-inset-bottom)\",\n }}\n >\n <DockChrome\n tabs={tabs}\n current={current}\n onTab={setTab}\n leading={mobileLeadingControl}\n accessory={headerAccessory}\n controls={\n <ChromeButton onClick={collapse} title=\"Close workspace\" label=\"Close workspace\">\n <XIcon className=\"size-4\" />\n </ChromeButton>\n }\n />\n </div>\n {collapsed && !hostControlled ? (\n <button\n ref={reopenRef}\n type=\"button\"\n onClick={expand}\n title=\"Open workspace\"\n aria-label=\"Open workspace\"\n className=\"absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent\"\n style={{ marginTop: \"env(safe-area-inset-top)\" }}\n >\n <ChevronsLeftRightIcon className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n );\n }\n\n const dockChrome = (\n <DockChrome\n tabs={tabs}\n current={current}\n onTab={setTab}\n accessory={headerAccessory}\n controls={\n <>\n <ChromeButton\n onClick={() => setMaximized((m) => !m)}\n title={maximized ? \"Restore (Esc)\" : \"Maximize\"}\n label={maximized ? \"Restore dock\" : \"Maximize dock\"}\n >\n {maximized ? (\n <Minimize2Icon className=\"size-3.5\" />\n ) : (\n <Maximize2Icon className=\"size-3.5\" />\n )}\n </ChromeButton>\n {hostControlled ? null : (\n <ChromeButton onClick={collapse} title=\"Collapse\" label=\"Collapse dock\">\n <PanelRightCloseIcon className=\"size-3.5\" />\n </ChromeButton>\n )}\n </>\n }\n />\n );\n\n return (\n <div ref={rootRef} className={cn(\"relative flex h-full min-h-0 w-full min-w-0\", className)}>\n <Group\n orientation=\"horizontal\"\n className=\"min-h-0 flex-1\"\n {...(defaultLayout ? { defaultLayout } : {})}\n onLayoutChanged={onLayoutChanged}\n >\n <Panel id=\"primary\" minSize=\"30%\" className=\"min-h-0 min-w-0\">\n <div\n data-workspace-primary\n aria-hidden={maximized ? true : undefined}\n className=\"h-full min-h-0 min-w-0\"\n inert={maximized ? true : undefined}\n >\n {primary}\n </div>\n </Panel>\n\n {!collapsed && (\n <Separator className=\"group relative z-10 w-1.5 shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11\">\n <span className=\"absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent\" />\n </Separator>\n )}\n\n <Panel\n id=\"dock\"\n panelRef={dockPanelRef}\n collapsible\n collapsedSize=\"0%\"\n defaultSize={`${defaultSize}%`}\n minSize={`${minSize}%`}\n maxSize={`${maxSize}%`}\n onResize={(size, _id, previousSize) => {\n // `asPercentage` is 0..100; treat a near-zero panel as collapsed.\n const isCollapsed = size.asPercentage <= 1;\n const canInferCollapse = collapsedProp === undefined || previousSize !== undefined;\n if (canInferCollapse && isCollapsed !== collapsed) {\n setCollapsed(isCollapsed);\n }\n }}\n className=\"min-h-0 min-w-0\"\n >\n {/* One persistent mount across normal, collapsed, and maximized modes:\n layout changes must never destroy an editor buffer or terminal view. */}\n <div\n data-workspace-surface\n role={maximized ? \"dialog\" : undefined}\n aria-modal={maximized ? true : undefined}\n aria-label={maximized ? \"Workspace\" : undefined}\n aria-hidden={collapsed ? true : undefined}\n className={cn(\n \"flex h-full min-h-0 min-w-0 flex-col bg-og-bg\",\n maximized ? \"fixed inset-0 z-40\" : \"border-l border-og-border\",\n collapsed && \"invisible pointer-events-none\",\n )}\n >\n {dockChrome}\n </div>\n </Panel>\n </Group>\n\n {/* Collapsed rail: the standalone fallback re-open affordance. Hidden\n when the host controls collapse — its own toggle is the one way in. */}\n {collapsed && !maximized && !hostControlled && (\n <button\n ref={reopenRef}\n type=\"button\"\n onClick={expand}\n title=\"Open workspace\"\n className=\"absolute inset-y-0 right-0 flex w-6 shrink-0 items-center justify-center border-l border-og-border bg-og-surface-1 text-og-fg-subtle hover:text-og-fg\"\n >\n <ChevronsLeftRightIcon className=\"size-3.5\" />\n </button>\n )}\n </div>\n );\n}\n\n/** A dock-chrome control button — compact on fine pointers, ≥40px on coarse. */\nfunction ChromeButton({\n onClick,\n title,\n label,\n children,\n}: {\n onClick: () => void;\n title: string;\n label: string;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n aria-label={label}\n className=\"inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:size-11 pointer-coarse:size-11\"\n >\n {children}\n </button>\n );\n}\n\nfunction DockChrome({\n tabs,\n current,\n onTab,\n leading,\n accessory,\n controls,\n}: {\n tabs: WorkspaceTab[];\n current: string;\n onTab: (id: string) => void;\n /** Host navigation at the start of mobile overlay chrome. */\n leading?: ReactNode | undefined;\n /** A status accessory (machine chip) between the tab strip and the controls. */\n accessory?: ReactNode | undefined;\n /** Right-aligned chrome controls (maximize / collapse, or the overlay close). */\n controls: ReactNode;\n}) {\n const active = tabs.find((t) => t.id === current) ?? tabs[0];\n const tabsetId = useId();\n const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const tabListRef = useRef<HTMLDivElement | null>(null);\n const [visitedTabs, setVisitedTabs] = useState<Set<string>>(() => new Set());\n const [tabOverflow, setTabOverflow] = useState({ start: false, end: false });\n const activeId = active?.id ?? \"\";\n const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);\n const keepActiveTabVisible = useCallback(() => {\n const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;\n selected?.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n }, [activeTabIndex]);\n\n useEffect(() => {\n if (!activeId) return;\n setVisitedTabs((previous) => {\n if (previous.has(activeId)) return previous;\n const next = new Set(previous);\n next.add(activeId);\n return next;\n });\n }, [activeId]);\n\n // A controlled/initial tab can become active without receiving focus (for\n // example, a host routing a guarded diff into Files). Native scrolling only\n // follows focus, so keep the selected tab inside the horizontally scrollable\n // strip even when state — rather than a click or arrow key — selected it.\n useDockLayoutEffect(() => {\n keepActiveTabVisible();\n }, [activeId, keepActiveTabVisible]);\n\n // Hidden scrollbars keep the chrome quiet, but without an edge cue a clipped\n // host label reads like corrupted copy. Fade only the edge(s) with more tabs,\n // and recompute after scrolling, resizing, or late font metrics.\n useDockLayoutEffect(() => {\n const list = tabListRef.current;\n if (!list) return;\n const update = () => {\n const max = Math.max(0, list.scrollWidth - list.clientWidth);\n const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };\n setTabOverflow((previous) =>\n previous.start === next.start && previous.end === next.end ? previous : next,\n );\n };\n update();\n list.addEventListener(\"scroll\", update, { passive: true });\n const observer =\n typeof ResizeObserver === \"undefined\"\n ? null\n : new ResizeObserver(() => {\n keepActiveTabVisible();\n update();\n });\n observer?.observe(list);\n for (const tab of tabRefs.current) {\n if (tab) observer?.observe(tab);\n }\n return () => {\n list.removeEventListener(\"scroll\", update);\n observer?.disconnect();\n };\n }, [activeId, keepActiveTabVisible, tabs.length]);\n\n const tabMask =\n tabOverflow.start && tabOverflow.end\n ? \"linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%)\"\n : tabOverflow.start\n ? \"linear-gradient(to right, transparent 0, black 14px, black 100%)\"\n : tabOverflow.end\n ? \"linear-gradient(to right, black 0, black calc(100% - 14px), transparent 100%)\"\n : undefined;\n\n const activateTab = (index: number) => {\n const tab = tabs[index];\n if (!tab) {\n return;\n }\n onTab(tab.id);\n tabRefs.current[index]?.focus();\n };\n\n const onTabKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>, index: number) => {\n if (tabs.length === 0) return;\n let nextIndex: number | null = null;\n if (event.key === \"ArrowRight\") nextIndex = (index + 1) % tabs.length;\n else if (event.key === \"ArrowLeft\") nextIndex = (index - 1 + tabs.length) % tabs.length;\n else if (event.key === \"Home\") nextIndex = 0;\n else if (event.key === \"End\") nextIndex = tabs.length - 1;\n if (nextIndex === null) return;\n event.preventDefault();\n activateTab(nextIndex);\n };\n\n return (\n <>\n <div className=\"grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]\">\n <div className=\"flex min-w-0 shrink-0 items-center max-[1023px]:min-h-11 min-[640px]:max-[1023px]:col-start-1 min-[640px]:max-[1023px]:row-start-1\">\n {leading ?? (\n <span className=\"hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline\">\n Workspace\n </span>\n )}\n </div>\n {/* The tab list scrolls horizontally when it can't fit — it must never\n grow into or overlap the chrome controls (they stay shrink-0). The\n scrollbar is hidden to keep the strip calm. On the narrow overlay it\n owns a full second row, so status/close chrome can never squeeze the\n canonical workspace navigation out of view. Tablets have enough\n width to retain the denser single-row chrome, with this same strip\n absorbing any unusually long host-injected labels. */}\n <div\n ref={tabListRef}\n className=\"flex min-w-0 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-[1023px]:col-span-3 max-[1023px]:row-start-2 max-[1023px]:w-full min-[640px]:max-[1023px]:col-span-1 min-[640px]:max-[1023px]:col-start-2 min-[640px]:max-[1023px]:row-start-1\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n style={{\n maskImage: tabMask,\n WebkitMaskImage: tabMask,\n scrollPaddingInline: \"16px\",\n }}\n >\n {tabs.map((tab, index) => (\n <button\n key={tab.id}\n ref={(node) => {\n tabRefs.current[index] = node;\n }}\n id={`${tabsetId}-tab-${index}`}\n type=\"button\"\n role=\"tab\"\n aria-selected={tab.id === current}\n aria-controls={`${tabsetId}-panel-${index}`}\n tabIndex={tab.id === current ? 0 : -1}\n onClick={() => onTab(tab.id)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className={cn(\n \"flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n tab.id === current\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n <span data-contrast-audited>{tab.label}</span>\n {tab.badge}\n </button>\n ))}\n </div>\n <div className=\"flex min-w-0 shrink-0 items-center justify-self-end min-[640px]:max-[1023px]:col-start-3 min-[640px]:max-[1023px]:row-start-1\">\n {accessory}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5 text-og-fg-subtle min-[640px]:max-[1023px]:col-start-4 min-[640px]:max-[1023px]:row-start-1\">\n {controls}\n </div>\n </div>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-hidden\">\n {tabs.length > 0 ? (\n tabs.map((tab, index) => {\n const selected = tab.id === activeId;\n const shouldMount = selected || visitedTabs.has(tab.id);\n return (\n <div\n key={tab.id}\n id={`${tabsetId}-panel-${index}`}\n aria-labelledby={`${tabsetId}-tab-${index}`}\n className=\"h-full min-h-0 min-w-0 overflow-hidden\"\n hidden={!selected}\n role=\"tabpanel\"\n >\n {shouldMount ? tab.content : null}\n </div>\n );\n })\n ) : (\n <div className=\"h-full min-h-0 min-w-0\" aria-label=\"Workspace\" role=\"tabpanel\" />\n )}\n </div>\n </>\n );\n}\n","// The sandbox workbench — the SDK-embeddable dock \"brain\" (Workbench v2, M4).\n//\n// This module owns the whole session workspace surface an embedder mounts:\n// Changes — review-first git: turn-end capture (cold) or live diff (warm)\n// Files — tree + inline editor (capture-backed cold, live warm)\n// Terminal — interactive xterm wired to the box PTY (Channel-A projection)\n// Desktop — noVNC (Channel-B), watch by default + a server-gated take-control\n// plus a machine-state chip in the dock header (the one truthful live/waking/\n// offline indicator) and any host-injected extra tabs (Run/Debug in apps/web).\n//\n// It is decoupled from the host app: the client comes from <OpenGeniProvider>\n// (never an app context), notifications flow through an optional `onNotify` prop\n// (no `sonner` import), and every surface renders with package primitives + og\n// tokens only. `apps/web` consumes this through the exact public surface an\n// external embedder uses — that is criterion F1.\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Popover } from \"radix-ui\";\nimport type { SessionEvent } from \"@opengeni/sdk\";\nimport {\n CircleCheckIcon,\n CpuIcon,\n LaptopIcon,\n LoaderCircleIcon,\n RefreshCwIcon,\n TriangleAlertIcon,\n} from \"lucide-react\";\n\nimport { type ClientOverride, useOpenGeni } from \"../provider\";\nimport { cn } from \"../lib/cn\";\nimport { xtermThemeFromTokens } from \"../lib/xterm-theme\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport { useSessionCapabilities } from \"../hooks/use-session-capabilities\";\nimport { useSandboxFiles } from \"../hooks/use-sandbox-files\";\nimport { useSandboxGit, type UseSandboxGitResult } from \"../hooks/use-sandbox-git\";\nimport { useSandboxTerminal } from \"../hooks/use-sandbox-terminal\";\nimport { useWorkspaceCapture } from \"../hooks/use-workspace-capture\";\nimport { formatAsOf, useMachineChip, type MachineChip } from \"../hooks/use-machine-chip\";\nimport { useMachines } from \"../hooks/use-machines\";\nimport type { MachineView } from \"../types/machines\";\nimport { connectionStatusForState } from \"../types/machines\";\nimport { ConnectionStatusPill } from \"./machine-status-pill\";\nimport { SharedMachineDisclosure } from \"./machine-dock-bar\";\nimport { SandboxFiles } from \"./sandbox-files\";\nimport { WorkbenchChanges } from \"./workbench-changes\";\nimport { SandboxTerminal, type XtermTheme } from \"./sandbox-terminal\";\nimport { DesktopViewer } from \"./desktop-viewer\";\nimport { WorkspaceDock, type WorkspaceDockProps, type WorkspaceTab } from \"./workspace-dock\";\n\n/** A host-routed notification (replaces the app-only `sonner` toast coupling). */\nexport type WorkspaceNotification = { kind: \"error\" | \"info\"; message: string };\n\n/** The workbench's canonical tab ids (a host injects extras around these). */\nexport const WORKBENCH_TAB_CHANGES = \"changes\";\nexport const WORKBENCH_TAB_FILES = \"files\";\nexport const WORKBENCH_TAB_TERMINAL = \"terminal\";\nexport const WORKBENCH_TAB_DESKTOP = \"desktop\";\n\n/**\n * The built-in workbench surfaces in their canonical display order. Hosts can\n * pass a subset through `surfaces`; omission preserves the complete standalone\n * workbench.\n */\nexport const WORKBENCH_SURFACES = [\n WORKBENCH_TAB_CHANGES,\n WORKBENCH_TAB_FILES,\n WORKBENCH_TAB_TERMINAL,\n WORKBENCH_TAB_DESKTOP,\n] as const;\n\nexport type SandboxWorkspaceSurface = (typeof WORKBENCH_SURFACES)[number];\n\nfunction isWorkbenchSurface(value: string): value is SandboxWorkspaceSurface {\n return (WORKBENCH_SURFACES as readonly string[]).includes(value);\n}\n\nfunction sourceDrivenDefaultTab(\n hasChanges: boolean,\n changesEnabled: boolean,\n filesEnabled: boolean,\n): string | null {\n if (hasChanges && changesEnabled) return WORKBENCH_TAB_CHANGES;\n if (filesEnabled) return WORKBENCH_TAB_FILES;\n if (changesEnabled) return WORKBENCH_TAB_CHANGES;\n return null;\n}\n\nfunction captureDegradedMessage(reason: string): string {\n switch (reason) {\n case \"repository_discovery_timed_out\":\n return \"Workspace capture is incomplete because repository discovery timed out. Live files remain authoritative.\";\n case \"repository_discovery_result_limit_exceeded\":\n return \"Workspace capture is incomplete because the repository limit was exceeded. Live files remain authoritative.\";\n case \"repository_read_unavailable\":\n return \"Workspace capture is incomplete because repository changes could not be read. Live files remain authoritative.\";\n default:\n return \"Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.\";\n }\n}\n\n/**\n * Decide a workspace tab from an already-local event log: the newest\n * `workspace.revision.captured` announce carries the change surface stats, so\n * \"changes exist → Changes, else Files\" needs zero machine round-trips. A host\n * `override` (e.g. a landing \"run\" tab) wins when supplied.\n *\n * `<SandboxWorkspace>` uses this only as an optional early hint after its own\n * capture GET reports no durable capture. A pure embedder needs no events-at-mount\n * contract: it falls through to authoritative live Git instead.\n */\nexport function initialWorkspaceTab(\n events: SessionEvent[] | undefined,\n override?: string | null,\n): string {\n if (override) return override;\n let bestSeq = -1;\n let bestFileCount = 0;\n for (const event of events ?? []) {\n if (event.type !== \"workspace.revision.captured\") continue;\n if (event.sequence <= bestSeq) continue;\n const stats = (event.payload as { stats?: { fileCount?: number } } | null)?.stats;\n bestSeq = event.sequence;\n bestFileCount = typeof stats?.fileCount === \"number\" ? stats.fileCount : 0;\n }\n return bestFileCount > 0 ? WORKBENCH_TAB_CHANGES : WORKBENCH_TAB_FILES;\n}\n\n/**\n * Whether a lazy sandbox provision is in flight on this event stream: the latest\n * `sandbox.provision` operation event is a `.started` not yet closed by a\n * `.completed`/`.failed`. Package-local twin of apps/web's events helper so the\n * dock brain can wake its on-demand capability negotiation when the box warms.\n */\nfunction sandboxProvisionInFlight(events: SessionEvent[]): boolean {\n let inFlight = false;\n for (const event of events) {\n if (\n event.type !== \"sandbox.operation.started\" &&\n event.type !== \"sandbox.operation.completed\" &&\n event.type !== \"sandbox.operation.failed\"\n ) {\n continue;\n }\n const payload = event.payload;\n const name =\n payload && typeof payload === \"object\" && !Array.isArray(payload)\n ? (payload as Record<string, unknown>).name\n : null;\n if (name !== \"sandbox.provision\") continue;\n if (event.type === \"sandbox.operation.started\") {\n inFlight = true;\n } else {\n inFlight = false;\n }\n }\n return inFlight;\n}\n\nexport type WorkspaceMachine = {\n /** Whether at least one built-in workbench surface is enabled. */\n enabled: boolean;\n /** The derived live/waking/offline chip model. */\n chip: MachineChip;\n /** The machine these surfaces are bound to (the Modal group box or a\n * self-hosted machine), or null while the fleet is still resolving. */\n activeMachine: MachineView | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nexport type UseSandboxWorkspaceTabsOptions = ClientOverride & {\n sessionId: string;\n /** Live event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /**\n * Built-in surfaces this host wants to expose. Omit for all four. A disabled\n * surface is behaviorally dormant: its data hooks, warm intents, stream\n * attachment, and navigation callbacks are not activated.\n */\n surfaces?: readonly SandboxWorkspaceSurface[] | undefined;\n /** Override the source-driven default tab (e.g. a host landing tab id). When\n * omitted the workbench picks Changes-vs-Files from capture or live Git. */\n initialTab?: string | null | undefined;\n /** Host-routed notifications (mutation errors, desktop-consent failures). The\n * package never imports a toast library — the host decides how to surface. */\n onNotify?: ((notification: WorkspaceNotification) => void) | undefined;\n /** File requested by a Changes guard. The Files surface defers the read until\n * the sandbox is live, then reveals this path. */\n requestedFilePath?: string | null | undefined;\n /** Unique identity for `requestedFilePath`, including repeated requests for the\n * same path. */\n requestedFileRequestId?: string | number | null | undefined;\n /** Route a guarded diff into the host's Files tab. */\n onOpenFile?: ((path: string) => void) | undefined;\n};\n\nexport type UseSandboxWorkspaceTabsResult = {\n /** Changes | Files | Terminal | Desktop (capability-gated where noted). */\n tabs: WorkspaceTab[];\n /** The source-driven default tab: Changes when the first authoritative capture\n * or live Git result has changes, else Files (a host `initialTab` overrides).\n * null while that source resolves, or permanently when neither Changes nor\n * Files is enabled; the choice latches once. */\n defaultTab: string | null;\n /** The machine-state model for the dock-header chip. */\n machine: WorkspaceMachine;\n};\n\ntype SessionWarmIntents = {\n sessionId: string;\n watchDesktop: boolean;\n warmTerminal: boolean;\n warmFiles: boolean;\n};\n\nfunction emptyWarmIntents(sessionId: string): SessionWarmIntents {\n return {\n sessionId,\n watchDesktop: false,\n warmTerminal: false,\n warmFiles: false,\n };\n}\n\n/**\n * Build the workbench tabs + the machine-chip model for one session. This is the\n * dock \"brain\": capability negotiation, capture-backed cold reads, prewarm\n * flags, desktop consent, and the xterm theme observer — all package-local.\n */\nexport function useSandboxWorkspaceTabs(\n options: UseSandboxWorkspaceTabsOptions,\n): UseSandboxWorkspaceTabsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const { sessionId, events, onNotify, requestedFilePath, requestedFileRequestId, onOpenFile } =\n options;\n const initialTab = options.initialTab ?? null;\n const requestedSurfaces = options.surfaces ?? WORKBENCH_SURFACES;\n const surfaceSet = new Set<SandboxWorkspaceSurface>(requestedSurfaces);\n const changesEnabled = surfaceSet.has(WORKBENCH_TAB_CHANGES);\n const filesEnabled = surfaceSet.has(WORKBENCH_TAB_FILES);\n const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);\n const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);\n const workspaceDataEnabled = changesEnabled || filesEnabled;\n const anySurfaceEnabled = workspaceDataEnabled || terminalEnabled || desktopEnabled;\n const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(\",\");\n\n // The three box-warming INTENTS, each off by default and each\n // flipped true by a genuine user action (never on mount, never on a passive\n // capture glance): desktop watch consent, terminal engagement (`onActivate`), and\n // a deliberate live-file open/edit in Files. Browsing capture-served\n // Changes/Files warms nothing — that is the whole point of Refinement 1.\n const [storedWarmIntents, setStoredWarmIntents] = useState<SessionWarmIntents>(() =>\n emptyWarmIntents(sessionId),\n );\n const warmIntents =\n storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);\n const { watchDesktop, warmTerminal, warmFiles } = warmIntents;\n const requestWarmIntent = useCallback(\n (intent: Exclude<keyof SessionWarmIntents, \"sessionId\">) => {\n setStoredWarmIntents((previous) => {\n const current = previous.sessionId === sessionId ? previous : emptyWarmIntents(sessionId);\n return current[intent] ? current : { ...current, [intent]: true };\n });\n },\n [sessionId],\n );\n\n // The session's machine fleet + the active-sandbox pointer. Drives the header\n // chip (which machine + its connection state). Polls slowly — ambient context.\n const machines = useMachines({\n workspaceId,\n sessionId,\n pollIntervalMs: 8000,\n enabled: anySurfaceEnabled,\n });\n const activeMachine: MachineView | null =\n machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ??\n machines.machines.find((m) => m.active) ??\n null;\n\n const caps = useSessionCapabilities(sessionId, {\n events,\n enabled: anySurfaceEnabled,\n attachDesktop: desktopEnabled && watchDesktop,\n attachTerminal: terminalEnabled && warmTerminal,\n // Explicit live-file intent only — NOT \"the Files tab is open\". A cold edit\n // or guarded-file open wakes the box; a glance at the tree/diff does not.\n attachFiles: workspaceDataEnabled && warmFiles,\n });\n const capabilities = caps.capabilities;\n const liveness = capabilities?.liveness;\n const fileSystemOn = capabilities?.FileSystem.available ?? false;\n // The FS is writable only when it's live AND not read-only. A self-hosted box\n // that's offline (or any read-only advertisement) or a capture-served cold tree\n // must not offer create/rename/delete/edit affordances — you cannot mutate a\n // machine you can't reach (C3). Tree-structure ops need a warm\n // writable box; content editing on a cold CLOUD box is the wake-on-edit path in\n // the editor, not tree mutation.\n const fsReadOnly = capabilities?.FileSystem.readOnly ?? false;\n // Mutations are live-only. A capture is a review artifact, never a writable\n // surrogate for a sleeping machine; the user must deliberately open the live\n // workspace before create/rename/delete/edit controls appear.\n const filesEditable =\n fileSystemOn && !fsReadOnly && (liveness === \"warm\" || liveness === \"draining\");\n const gitOn = capabilities?.Git.available ?? false;\n const terminalOn = terminalEnabled && (capabilities?.Terminal.transport ?? null) !== null;\n // The REAL interactive terminal is the ttyd pty-ws stream. When it's live the\n // legacy HTTP PTY must NOT run; `useSandboxTerminal` opens its HTTP PTY only as\n // the firehose-mode fallback (a backend without ttyd).\n const ptyWsLive =\n capabilities?.Terminal.transport === \"pty-ws\" && Boolean(capabilities?.Terminal.url);\n const ptyCapable = (capabilities?.Terminal.ptyCapable ?? false) && !ptyWsLive;\n const terminal = useSandboxTerminal(sessionId, {\n events: terminalEnabled ? events : [],\n interactive: terminalEnabled && ptyCapable,\n liveness,\n });\n const desktopAdvertised =\n desktopEnabled &&\n ((capabilities?.DesktopStream.transport ?? null) !== null ||\n capabilities?.DesktopStream.reason === \"lease_cold\");\n\n // Lazy provisioning (#315) creates the box mid-turn on the first sandbox tool\n // call, emitting sandbox.provision started→completed/failed on the live stream.\n // The on-demand resting hook rests without polling, so when the box warms the\n // cold capability doc never refreshes on its own — Terminal/Desktop would stay\n // hidden. Watch the provision edge and renegotiate when it settles so the\n // freshly-warm box's surfaces fill in.\n const provisioning = sandboxProvisionInFlight(events);\n const renegotiate = caps.renegotiate;\n const provisioningRef = useRef({ sessionId, provisioning });\n useEffect(() => {\n const previous = provisioningRef.current;\n if (previous.sessionId === sessionId && previous.provisioning && !provisioning) {\n renegotiate();\n }\n provisioningRef.current = { sessionId, provisioning };\n }, [sessionId, provisioning, renegotiate]);\n\n // The cold-paint data source: the latest turn-end capture, fetched with a single\n // api round-trip on mount (no machine). Feeds the Files tree + the Changes/Git\n // diff immediately; a warm box reconciles live without discarding the capture\n // when that provider read is temporarily unavailable.\n const captureState = useWorkspaceCapture(sessionId, {\n events,\n enabled: workspaceDataEnabled,\n });\n const captureAvailable = captureState.available;\n const liveWorkspaceExpected = liveness === \"warm\" || liveness === \"draining\";\n // Capability negotiation and capture resolution are independent requests. A\n // cold capability document can win that race by a few milliseconds; enabling\n // the leaf hooks in that window would make them interpret `capture: null` as a\n // real miss and issue Channel-A reads, waking the very box the capture exists\n // to avoid. Wait until the capture request has conclusively resolved. A warm\n // workspace never waits on this server-side snapshot.\n const capturePending =\n !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);\n const repoPaths = useMemo(() => {\n const advertised = capabilities?.Git.repos ?? [];\n if (advertised.length > 0) return advertised;\n return captureState.capture?.repos.map((repo) => repo.root) ?? [];\n }, [capabilities?.Git.repos, captureState.capture]);\n const notifiedCaptureDegradedReason = useRef<{\n sessionId: string;\n reason: string | null;\n }>({\n sessionId,\n reason: null,\n });\n if (notifiedCaptureDegradedReason.current.sessionId !== sessionId) {\n notifiedCaptureDegradedReason.current = { sessionId, reason: null };\n }\n useEffect(() => {\n const reason = captureState.degradedReason;\n if (!reason || notifiedCaptureDegradedReason.current.reason === reason) return;\n notifiedCaptureDegradedReason.current = { sessionId, reason };\n onNotify?.({ kind: \"error\", message: captureDegradedMessage(reason) });\n }, [sessionId, captureState.degradedReason, onNotify]);\n\n const files = useSandboxFiles(sessionId, {\n events,\n // No passive Channel-A reads while cold, even after a conclusive capture\n // miss. A missing capture gets an explicit \"Open live workspace\" gate.\n enabled: filesEnabled && (captureAvailable || (fileSystemOn && liveWorkspaceExpected)),\n liveness,\n capture: captureState.capture,\n // A reverted optimistic mutation (e.g. a 409 rename collision) surfaces as a\n // host notification — the tree silently rolls back, the user sees why.\n onMutationError: (error, op) =>\n onNotify?.({\n kind: \"error\",\n message: `Could not ${op}: ${error.message}`,\n }),\n });\n const git = useSandboxGit(sessionId, {\n events,\n enabled: workspaceDataEnabled && (captureAvailable || (gitOn && liveWorkspaceExpected)),\n repoPaths,\n liveness,\n capture: captureState.capture,\n });\n const stagedGit = useSandboxGit(sessionId, {\n events,\n // Captures intentionally expose one combined review diff, not a staged\n // index. Never probe the live index while the lease is cold.\n enabled: filesEnabled && gitOn && liveWorkspaceExpected,\n staged: true,\n repoPaths,\n liveness,\n capture: captureState.capture,\n });\n\n // Token-derived xterm theme; re-derive on a `data-og-theme` flip. Generic — it\n // belongs in the package (an embedder's theme toggle drives it too).\n const [xtermTheme, setXtermTheme] = useState<XtermTheme | undefined>(undefined);\n useEffect(() => {\n if (!terminalEnabled || typeof document === \"undefined\") return;\n const derive = () => setXtermTheme(xtermThemeFromTokens());\n derive();\n const observer = new MutationObserver(derive);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-og-theme\", \"class\"],\n });\n return () => observer.disconnect();\n }, [terminalEnabled]);\n\n async function acknowledgeAndWatch() {\n try {\n const shared = capabilities?.DesktopStream.shared ?? false;\n await client.acknowledgeStream(workspaceId, sessionId, {\n acknowledgeUnredacted: true,\n acknowledgeShared: shared,\n });\n requestWarmIntent(\"watchDesktop\");\n caps.renegotiate();\n } catch (error) {\n onNotify?.({\n kind: \"error\",\n message: `Could not start the desktop stream: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n\n // Re-warm WITHOUT re-acknowledging: the consent was already recorded, only the\n // box drained back to cold. Idempotent — the viewer auto-warm de-dupes.\n function rewarmDesktop() {\n if (!watchDesktop) requestWarmIntent(\"watchDesktop\");\n caps.renegotiate();\n }\n\n const dirtyCount = git.diff.length;\n\n // The one truthful machine indicator, derived from the live capability/liveness\n // surface + the active machine's connection state + the latest capture time.\n const chip = useMachineChip({\n liveness,\n capabilitiesState: caps.state,\n activeMachineState: activeMachine?.state ?? null,\n activeIsSelfhosted: activeMachine?.kind === \"selfhosted\",\n wantsWarm:\n (terminalEnabled && warmTerminal) ||\n (desktopEnabled && watchDesktop) ||\n (workspaceDataEnabled && warmFiles),\n capturedAt: captureState.capturedAt,\n });\n const workspaceWaking = chip.state === \"waking\";\n\n // The pre-paint default tab is decided from the first AUTHORITATIVE workspace\n // source. A capture wins immediately on the cold/offline fast path; a warm box\n // waits for live Git so a prior snapshot can never outrank the current tree.\n // When the GET says no capture exists, `fileCount: 0` does NOT itself mean the\n // working tree is clean. A captured-revision announce can\n // resolve Changes earlier, but a pure embedder with no event preload still gets\n // the correct live default. The choice latches once so later edits never steal\n // the user's current tab.\n const defaultIdentity = `${sessionId}\\u0000${surfaceIdentity}`;\n const defaultTabRef = useRef<{ identity: string; value: string | null }>({\n identity: defaultIdentity,\n value: null,\n });\n if (defaultTabRef.current.identity !== defaultIdentity) {\n defaultTabRef.current = { identity: defaultIdentity, value: null };\n }\n const captureIsAuthoritative =\n !liveWorkspaceExpected && (liveness !== undefined || caps.error !== null);\n const captureUnavailable = captureState.fileCount === 0 || captureState.error !== null;\n if (defaultTabRef.current.value === null) {\n if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {\n defaultTabRef.current.value = initialTab;\n } else if (git.source === \"live\") {\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n git.diff.length > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (\n captureIsAuthoritative &&\n captureState.fileCount !== null &&\n captureState.available\n ) {\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n captureState.fileCount > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (\n captureIsAuthoritative &&\n captureUnavailable &&\n initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES\n ) {\n defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);\n } else if (captureUnavailable && caps.error) {\n // The Changes surface owns the truthful sandbox-unavailable state + retry.\n // Files would misleadingly describe this as a missing filesystem.\n defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);\n } else if (captureIsAuthoritative && captureState.fileCount !== null && captureUnavailable) {\n // No durable review surface exists and the machine is resting. Files owns\n // the explicit wake gate; do not issue a live Git probe merely to choose a\n // tab.\n defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);\n } else if (captureState.available && git.error && captureState.fileCount !== null) {\n // A warm live read failed but the durable capture is intact: retain an\n // immediate, deterministic review surface instead of hanging unresolved.\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n captureState.fileCount > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (captureUnavailable && git.error) {\n // No capture and live Git failed: Files is the least surprising fallback\n // and preserves the established no-capture degraded behavior.\n defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);\n }\n }\n // null only during the brief pre-first-resolve window (no host override yet).\n const defaultTab = defaultTabRef.current.value;\n\n const tabs = useMemo(() => {\n const list: WorkspaceTab[] = [];\n\n // Changes — capture-backed and usable cold/offline when the host enables it.\n if (changesEnabled)\n list.push({\n id: WORKBENCH_TAB_CHANGES,\n label: \"Changes\",\n badge: dirtyCount > 0 ? <DirtyBadge count={dirtyCount} /> : undefined,\n content: (\n <ChangesTabBody\n git={git}\n captureAvailable={captureAvailable}\n captureRevision={captureState.revision}\n capturePending={capturePending}\n liveWorkspaceExpected={liveWorkspaceExpected}\n workspaceWaking={workspaceWaking}\n capabilitiesState={caps.state}\n capabilitiesError={caps.error}\n onRetry={caps.renegotiate}\n onWake={() => requestWarmIntent(\"warmFiles\")}\n {...(filesEnabled && onOpenFile\n ? {\n onOpenFile: (path: string) => {\n requestWarmIntent(\"warmFiles\");\n onOpenFile(path);\n },\n }\n : {})}\n />\n ),\n });\n\n // Files — capture-backed cold tree; live warm when the host enables it.\n if (filesEnabled)\n list.push({\n id: WORKBENCH_TAB_FILES,\n label: \"Files\",\n content: (\n <SandboxFiles\n key={sessionId}\n files={files}\n git={git}\n stagedGit={stagedGit}\n fileSystemAvailable={fileSystemOn || captureAvailable}\n editable={\n filesEditable && files.source === \"live\" && files.error === null && !files.loading\n }\n workspaceResting={\n !liveWorkspaceExpected &&\n liveness !== undefined &&\n !capturePending &&\n !captureAvailable &&\n !workspaceWaking\n }\n workspaceWaking={!liveWorkspaceExpected && !captureAvailable && workspaceWaking}\n liveWorkspaceReady={liveWorkspaceExpected}\n onWakeWorkspace={() => requestWarmIntent(\"warmFiles\")}\n {...(requestedFilePath\n ? {\n requestedPath: requestedFilePath,\n ...(requestedFileRequestId !== null && requestedFileRequestId !== undefined\n ? { requestedPathRequestId: requestedFileRequestId }\n : {}),\n requestedPathReady: liveness === \"warm\" || liveness === \"draining\",\n }\n : {})}\n // Ordinary capture browsing does not warm a box. The first edit — or an\n // explicit guarded-file open from Changes — is deliberate live-file intent.\n onEditIntent={() => requestWarmIntent(\"warmFiles\")}\n className=\"h-full\"\n />\n ),\n });\n\n // Terminal — capability-gated (appears after negotiation; never the default).\n if (terminalOn) {\n list.push({\n id: WORKBENCH_TAB_TERMINAL,\n label: \"Terminal\",\n content: (\n <div className=\"h-full bg-og-bg p-1\">\n <SandboxTerminal\n result={terminal}\n terminalCapability={capabilities?.Terminal ?? null}\n onActivate={() => requestWarmIntent(\"warmTerminal\")}\n showHeader\n shell={capabilities?.Terminal.shell ?? undefined}\n liveness={liveness}\n {...(xtermTheme ? { theme: xtermTheme } : {})}\n />\n </div>\n ),\n });\n }\n\n // Desktop — capability-gated + consent-gated.\n if (desktopAdvertised) {\n list.push({\n id: WORKBENCH_TAB_DESKTOP,\n label: \"Desktop\",\n badge: watchDesktop ? (\n <span className=\"rounded-og-xs bg-og-status-running/20 px-1 text-og-xs text-og-status-running\">\n Live\n </span>\n ) : undefined,\n content: (\n <DesktopViewer\n capability={capabilities?.DesktopStream ?? null}\n viewerCapReached={caps.viewerCapReached}\n watching={watchDesktop}\n onAcknowledge={() => void acknowledgeAndWatch()}\n onWarm={rewarmDesktop}\n className=\"h-full\"\n />\n ),\n });\n }\n\n return list;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n fileSystemOn,\n changesEnabled,\n filesEnabled,\n terminalOn,\n desktopAdvertised,\n captureAvailable,\n dirtyCount,\n watchDesktop,\n warmTerminal,\n requestedFilePath,\n requestedFileRequestId,\n onOpenFile,\n liveness,\n sessionId,\n files,\n git,\n stagedGit,\n terminal,\n xtermTheme,\n capabilities,\n workspaceWaking,\n caps.state,\n caps.error,\n caps.viewerCapReached,\n requestWarmIntent,\n ]);\n\n return {\n tabs,\n defaultTab,\n machine: {\n enabled: anySurfaceEnabled,\n chip,\n activeMachine,\n loading: machines.loading,\n error: machines.error,\n refresh: machines.refresh,\n },\n };\n}\n\nexport type SandboxWorkspaceProps = ClientOverride & {\n sessionId: string;\n /** Live event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /** The chat / primary pane shown beside the dock. */\n primary: ReactNode;\n /**\n * Built-in surfaces this host wants to expose. Omit for Changes, Files,\n * Terminal, and Desktop. Disabled surfaces remain behaviorally dormant.\n */\n surfaces?: readonly SandboxWorkspaceSurface[] | undefined;\n /** Host tabs injected BEFORE the workbench tabs (e.g. a \"Run\" landing tab). */\n leadingTabs?: WorkspaceTab[] | undefined;\n /** Host tabs injected AFTER the workbench tabs (e.g. a \"Debug\" tab). */\n trailingTabs?: WorkspaceTab[] | undefined;\n /** Override the pre-paint default tab (e.g. a host landing tab id). When\n * omitted the workbench decides from capture, then live Git when no capture exists. */\n initialTab?: string | undefined;\n /** Host-routed notifications (no toast dependency in the package). */\n onNotify?: ((notification: WorkspaceNotification) => void) | undefined;\n /** Controlled collapsed state for hosts with their own dock toggle. */\n collapsed?: boolean | undefined;\n onCollapsedChange?: ((collapsed: boolean) => void) | undefined;\n /** Host navigation shown only in the phone workspace overlay header. */\n mobileLeadingControl?: ReactNode | undefined;\n autoSaveId?: string | undefined;\n defaultSize?: number | undefined;\n minSize?: number | undefined;\n maxSize?: number | undefined;\n className?: string | undefined;\n};\n\n/**\n * The whole session workspace, ready to mount: `<SandboxWorkspace>` assembles the\n * capability-gated tabs, pins the machine-state chip in the dock header, and\n * renders the resizable `<WorkspaceDock>`. Wrap the tree in `<OpenGeniProvider>`\n * (client + workspaceId) and import `@opengeni/react/styles.css`; that is the\n * entire integration (see `docs/embedding-workbench.md`).\n */\nexport function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {\n const {\n sessionId,\n events,\n primary,\n surfaces,\n leadingTabs,\n trailingTabs,\n initialTab,\n onNotify,\n collapsed,\n onCollapsedChange,\n mobileLeadingControl,\n autoSaveId,\n defaultSize,\n minSize,\n maxSize,\n className,\n } = props;\n\n const [storedSelection, setStoredSelection] = useState<{\n sessionId: string;\n tab: string;\n } | null>(null);\n const [requestedFile, setRequestedFile] = useState<{\n sessionId: string;\n path: string;\n requestId: number;\n } | null>(null);\n const nextFileRequestId = useRef(0);\n const openFile = useCallback(\n (path: string) => {\n nextFileRequestId.current += 1;\n setRequestedFile({ sessionId, path, requestId: nextFileRequestId.current });\n setStoredSelection({ sessionId, tab: WORKBENCH_TAB_FILES });\n },\n [sessionId],\n );\n\n const {\n tabs: workbenchTabs,\n machine,\n defaultTab,\n } = useSandboxWorkspaceTabs({\n ...(props.client ? { client: props.client } : {}),\n ...(props.workspaceId ? { workspaceId: props.workspaceId } : {}),\n sessionId,\n events,\n ...(surfaces ? { surfaces } : {}),\n ...(initialTab ? { initialTab } : {}),\n ...(onNotify ? { onNotify } : {}),\n requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,\n requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,\n onOpenFile: openFile,\n });\n\n // A user's tab click wins forever; before that we follow the source-driven\n // default. While it is still resolving (null, pure-embedder pre-first-resolve)\n // we pass no controlled tab, so the dock renders its own first-tab fallback\n // (Changes) — whose body is a connecting/loading state until the capture lands,\n // so committing the real default at first-resolve produces no CONTENT switch.\n const tabs: WorkspaceTab[] = [...(leadingTabs ?? []), ...workbenchTabs, ...(trailingTabs ?? [])];\n const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;\n const preferredTab = selectedTab ?? defaultTab;\n const activeTab =\n preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;\n const selectTab = useCallback(\n (tab: string) => setStoredSelection({ sessionId, tab }),\n [sessionId],\n );\n\n return (\n <WorkspaceDock\n primary={primary}\n tabs={tabs}\n {...(activeTab !== undefined ? { activeTab } : {})}\n onActiveTabChange={selectTab}\n {...(machine.enabled\n ? {\n headerAccessory: (\n <MachineStateChip\n chip={machine.chip}\n activeMachine={machine.activeMachine}\n error={machine.error}\n onRetry={() => void machine.refresh()}\n />\n ),\n }\n : {})}\n {...(mobileLeadingControl !== undefined ? { mobileLeadingControl } : {})}\n {...(collapsed !== undefined ? { collapsed } : {})}\n {...(onCollapsedChange ? { onCollapsedChange } : {})}\n {...(autoSaveId !== undefined ? { autoSaveId } : {})}\n {...(defaultSize !== undefined ? { defaultSize } : {})}\n {...(minSize !== undefined ? { minSize } : {})}\n {...(maxSize !== undefined ? { maxSize } : {})}\n {...(className !== undefined ? { className } : {})}\n />\n );\n}\n\n// Re-export the dock so an embedder can grab the shell type off one import.\nexport type { WorkspaceDockProps };\n\nfunction DirtyBadge({ count }: { count: number }) {\n return (\n <span className=\"rounded-og-xs bg-og-accent-soft px-1 text-og-xs text-og-fg-muted\">\n {count}\n </span>\n );\n}\n\n/** A machine-kind glyph for the chip popover. */\nfunction MachineKindIcon({\n kind,\n className,\n}: {\n kind: MachineView[\"kind\"] | undefined;\n className?: string;\n}) {\n const Icon = kind === \"selfhosted\" ? LaptopIcon : CpuIcon;\n return <Icon className={cn(\"size-3.5 shrink-0 text-og-fg-subtle\", className)} aria-hidden />;\n}\n\nfunction chipDotClass(state: MachineChip[\"state\"]): string {\n if (state === \"live\") return \"bg-og-status-running\";\n if (state === \"waking\") return \"bg-og-status-idle animate-pulse motion-reduce:animate-none\";\n return \"bg-og-fg-subtle\";\n}\n\n/**\n * The dock-header machine chip: the one truthful live/waking/offline indicator,\n * with a popover carrying the machine identity, connection state, the \"shown as\n * of <time>\" staleness note, the shared-session disclosure, and a retry when the\n * fleet failed to resolve (the old per-surface machine bar + Sandbox info tab,\n * folded into one header affordance — recommendation).\n */\nfunction MachineStateChip({\n chip,\n activeMachine,\n error,\n onRetry,\n}: {\n chip: MachineChip;\n activeMachine: MachineView | null;\n error: Error | null;\n onRetry: () => void;\n}) {\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const portalStyle = usePortalTokenStyle(triggerRef);\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <button\n ref={triggerRef}\n type=\"button\"\n aria-label={`Machine: ${chip.label}`}\n className=\"inline-flex min-h-7 items-center gap-1.5 rounded-og-sm px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11\"\n >\n <span\n className={cn(\"size-1.5 shrink-0 rounded-full\", chipDotClass(chip.state))}\n aria-hidden\n />\n <span className=\"max-w-[11rem] truncate\">{chip.label}</span>\n </button>\n </Popover.Trigger>\n <Popover.Portal forceMount>\n <Popover.Content\n forceMount\n data-machine-state-popover\n align=\"end\"\n sideOffset={6}\n collisionPadding={8}\n className=\"og-root z-50 w-64 max-w-[calc(100vw-1rem)] rounded-og-md border border-og-border bg-og-surface-1 p-3 text-og-sm text-og-fg shadow-lg outline-none\"\n style={portalStyle}\n >\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <MachineKindIcon kind={activeMachine?.kind} />\n <span className=\"min-w-0 truncate font-medium\">{activeMachine?.name ?? \"Sandbox\"}</span>\n </div>\n <div className=\"mt-2.5 flex items-center justify-between gap-2\">\n <span className=\"text-og-xs text-og-fg-subtle\">Connection</span>\n {activeMachine ? (\n <ConnectionStatusPill\n status={connectionStatusForState(activeMachine.state)}\n size=\"sm\"\n />\n ) : (\n <span className=\"text-og-xs text-og-fg-muted\">{chip.label}</span>\n )}\n </div>\n {chip.asOf ? (\n <p className=\"mt-2.5 text-og-xs leading-4 text-og-fg-subtle\">\n Workspace shown as of {formatAsOf(chip.asOf, Date.now())} — the machine is not live.\n </p>\n ) : null}\n {activeMachine && activeMachine.sharedSessionCount > 1 ? (\n <div className=\"mt-2.5\">\n <SharedMachineDisclosure\n sharedSessionCount={activeMachine.sharedSessionCount}\n density=\"full\"\n />\n </div>\n ) : null}\n {error ? (\n <div className=\"mt-2.5 space-y-1.5 border-t border-og-border pt-2.5\">\n <p className=\"text-og-xs leading-4 text-og-danger\">\n Couldn't reach the sandbox for this session.\n </p>\n <DockActionButton onClick={onRetry}>\n <RefreshCwIcon className=\"size-3\" />\n Retry\n </DockActionButton>\n </div>\n ) : null}\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** A minimal token-styled action button (the package has no app Button import). */\nfunction DockActionButton({ onClick, children }: { onClick: () => void; children: ReactNode }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11\"\n >\n {children}\n </button>\n );\n}\n\n/**\n * The Changes tab body: the real PR-review surface (`WorkbenchChanges` — file\n * rail + windowed Pierre diff pane) when there are changes, wrapped in the honest\n * connecting/offline/empty states so the default tab is never a blank surface.\n * The dock frame is untouched; this is the M5 seam.\n */\nfunction ChangesTabBody({\n git,\n captureAvailable,\n captureRevision,\n capturePending,\n liveWorkspaceExpected,\n workspaceWaking,\n capabilitiesState,\n capabilitiesError,\n onRetry,\n onWake,\n onOpenFile,\n}: {\n git: UseSandboxGitResult;\n captureAvailable: boolean;\n captureRevision: number | null;\n capturePending: boolean;\n liveWorkspaceExpected: boolean;\n workspaceWaking: boolean;\n capabilitiesState: string;\n capabilitiesError: Error | null;\n onRetry: () => void;\n onWake: () => void;\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n const diff = git.diff;\n\n if (diff.length > 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {git.error ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-opengeni-changes-degraded\n className=\"flex shrink-0 items-center gap-2 border-b border-og-status-running/30 bg-og-status-running/10 px-2 py-1.5 text-og-xs text-og-fg-muted\"\n >\n <TriangleAlertIcon className=\"size-3.5 shrink-0 text-og-status-running\" aria-hidden />\n <span className=\"min-w-0 flex-1\">\n {git.source === \"capture\"\n ? \"Live changes are temporarily unavailable. Showing the latest captured revision.\"\n : \"Live refresh failed. Showing the last loaded changes.\"}\n </span>\n <button\n type=\"button\"\n onClick={() => void git.refresh()}\n disabled={git.loading}\n className=\"inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3\", git.loading && \"animate-spin motion-reduce:animate-none\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : null}\n <div className=\"min-h-0 flex-1\">\n <WorkbenchChanges\n diff={diff}\n source={git.source}\n capturedAt={git.capturedAt}\n captureRevision={captureRevision}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n </div>\n </div>\n );\n }\n\n if (capabilitiesError && !captureAvailable) {\n return (\n <CenteredState icon={<TriangleAlertIcon className=\"size-5\" aria-hidden />} tone=\"danger\">\n <p className=\"text-og-sm font-medium text-og-fg\">Sandbox unavailable</p>\n <p data-contrast-audited className=\"text-og-sm leading-5 text-og-fg-muted\">\n {capabilitiesError.message || \"Couldn't reach the sandbox for this session.\"}\n </p>\n <DockActionButton onClick={onRetry}>\n <RefreshCwIcon className=\"size-3\" />\n Retry\n </DockActionButton>\n </CenteredState>\n );\n }\n\n if (\n (capturePending || capabilitiesState === \"negotiating\") &&\n !captureAvailable &&\n !workspaceWaking\n ) {\n return (\n <CenteredState\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">Connecting workspace</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n Looking for the latest files and changes…\n </p>\n </CenteredState>\n );\n }\n\n if (!liveWorkspaceExpected && !captureAvailable) {\n return (\n <CenteredState\n icon={\n workspaceWaking ? (\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n ) : (\n <CpuIcon className=\"size-5\" aria-hidden />\n )\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">\n {workspaceWaking ? \"Waking workspace\" : \"Workspace is resting\"}\n </p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n {workspaceWaking\n ? \"Connecting to the live working tree…\"\n : \"No captured revision is available yet. Wake the sandbox to inspect current changes.\"}\n </p>\n {!workspaceWaking ? (\n <DockActionButton onClick={onWake}>\n <CpuIcon className=\"size-3\" />\n Open live workspace\n </DockActionButton>\n ) : null}\n </CenteredState>\n );\n }\n\n if (git.loading && git.source === null) {\n return (\n <CenteredState\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">Loading workspace</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">Reading the current working tree…</p>\n </CenteredState>\n );\n }\n\n return (\n <CenteredState icon={<CircleCheckIcon className=\"size-5\" aria-hidden />} tone=\"success\">\n <p className=\"text-og-sm font-medium text-og-fg\">Working tree is clean</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n File edits from future turns will appear here.\n </p>\n </CenteredState>\n );\n}\n\nfunction CenteredState({\n children,\n icon,\n tone = \"neutral\",\n}: {\n children: ReactNode;\n icon?: ReactNode | undefined;\n tone?: \"neutral\" | \"success\" | \"danger\";\n}) {\n return (\n <div\n role={tone === \"danger\" ? \"alert\" : \"status\"}\n aria-atomic=\"true\"\n aria-live={tone === \"danger\" ? \"assertive\" : \"polite\"}\n className=\"grid h-full place-items-center p-6 text-center\"\n >\n <div className=\"flex max-w-sm flex-col items-center gap-2.5\">\n {icon ? (\n <span\n className={cn(\n \"grid size-10 place-items-center rounded-og-lg border bg-og-surface-1 shadow-sm\",\n tone === \"success\" && \"border-og-status-idle/30 text-og-status-idle\",\n tone === \"danger\" && \"border-og-status-failed/30 text-og-status-failed\",\n tone === \"neutral\" && \"border-og-border text-og-fg-muted\",\n )}\n >\n {icon}\n </span>\n ) : null}\n {children}\n </div>\n </div>\n );\n}\n","import { useCallback, useState } from \"react\";\nimport type {\n CodexAccount,\n CodexAccountsResponse,\n CodexRotationSettings,\n SessionEvent,\n} from \"@opengeni/sdk\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport {\n useMutationRunner,\n usePolledValue,\n useSessionEventTrigger,\n type SessionEventFeedOptions,\n} from \"./internal\";\n\n/** Events that change which Codex account a session runs on (or just ran). */\nexport function isCodexAccountEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type === \"codex.account.switched\" || event.type === \"turn.started\";\n}\n\n/**\n * The structural slice of the SDK client the Codex-accounts surface needs. Method\n * NAMES + SIGNATURES match `OpenGeniClient` so the real client satisfies it\n * directly; declared structurally (not a hard Pick) so a test/Geni client can\n * stand in. `getSession` reads the session's pin/last; `pinSessionCodexAccount`\n * is the optional mutation (absent ⇒ the indicator hides the switch affordance).\n */\nexport type CodexAccountsClientLike = {\n listCodexAccounts: (workspaceId: string) => Promise<CodexAccountsResponse>;\n getSession?: (\n workspaceId: string,\n sessionId: string,\n ) => Promise<{ codexPinnedCredentialId?: string | null; codexLastCredentialId?: string | null }>;\n pinSessionCodexAccount?: (\n workspaceId: string,\n sessionId: string,\n target: string,\n ) => Promise<{ pinned: string }>;\n /** Optional (absent ⇒ the card hides live refresh): batched live /wham/usage refresh. */\n refreshCodexUsage?: (workspaceId: string) => Promise<{ usage: Record<string, unknown> }>;\n};\n\nexport type UseCodexAccountsOptions = ClientOverride &\n SessionEventFeedOptions & {\n pollIntervalMs?: number | undefined;\n /** Scope to a session so the hook resolves the pin + the effective account. */\n sessionId?: string | undefined;\n /** Override the client with one implementing `CodexAccountsClientLike`. */\n codexClient?: CodexAccountsClientLike | undefined;\n };\n\nexport type UseCodexAccountsResult = {\n accounts: CodexAccount[];\n /** The workspace ACTIVE account (used when a session is unpinned). */\n activeAccountId: string | null;\n /** The session's PINNED account (null ⇒ following workspace active). */\n pinnedAccountId: string | null;\n /** The account the next turn will run on: pin > workspace active. */\n effectiveAccountId: string | null;\n /** The account the session's last turn ACTUALLY ran on (the \"Running on:\" source). */\n lastAccountId: string | null;\n settings: CodexRotationSettings;\n loading: boolean;\n refresh: () => Promise<void>;\n /**\n * Trigger a LIVE batched /wham/usage refresh across all accounts, then re-read\n * the cached metadata so the new windows land on `accounts`. Modeled on `pin`.\n * No-op (resolves false) when the client can't refresh usage.\n */\n refreshUsage: () => Promise<boolean>;\n /** True while a live usage refresh is in flight (drives the bar skeleton). */\n refreshingUsage: boolean;\n /** Pin (or unpin via \"auto\") the session's account; returns true on success. */\n pin: (target: string) => Promise<boolean>;\n pinning: boolean;\n /** The target of the in-flight pin (for per-row spinner gating). */\n pinningTarget: string | null;\n mutationError: Error | null;\n};\n\nconst EMPTY_SETTINGS: CodexRotationSettings = {\n rotationEnabled: false,\n rotationStrategy: \"most_remaining\",\n activeCredentialId: null,\n};\n\ntype CodexAccountsState = {\n accounts: CodexAccount[];\n activeAccountId: string | null;\n settings: CodexRotationSettings;\n pinnedAccountId: string | null;\n lastAccountId: string | null;\n};\n\nconst EMPTY_STATE: CodexAccountsState = {\n accounts: [],\n activeAccountId: null,\n settings: EMPTY_SETTINGS,\n pinnedAccountId: null,\n lastAccountId: null,\n};\n\n/**\n * The workspace's Codex accounts + the per-workspace active pointer + (when\n * session-scoped) the session pin and last-ran-on account. Composed like\n * `useMachines`: slow polling (the realtime work is done by the\n * `codex.account.switched` / `turn.started` event trigger) + a `pin` mutation.\n * Dual-consumer safe via the structural `CodexAccountsClientLike` surface.\n */\nexport function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCodexAccountsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const codexClient = options.codexClient ?? (client as unknown as CodexAccountsClientLike);\n const sessionId = options.sessionId;\n const sharedEvents = options.events;\n\n const load = useCallback(async (): Promise<CodexAccountsState> => {\n const accountsP = codexClient.listCodexAccounts(workspaceId);\n const sessionP =\n sessionId && codexClient.getSession\n ? codexClient.getSession(workspaceId, sessionId).catch(() => null)\n : Promise.resolve(null);\n const [acc, session] = await Promise.all([accountsP, sessionP]);\n return {\n accounts: acc.accounts,\n activeAccountId: acc.activeAccountId,\n settings: acc.settings,\n pinnedAccountId: session?.codexPinnedCredentialId ?? null,\n lastAccountId: session?.codexLastCredentialId ?? null,\n };\n }, [codexClient, workspaceId, sessionId]);\n\n const {\n data: loadedData,\n loading,\n refresh,\n } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run: runMutation, mutating: pinning, mutationError } = useMutationRunner();\n const { run: runUsageMutation, mutating: refreshingUsage } = useMutationRunner();\n const [pinningTarget, setPinningTarget] = useState<string | null>(null);\n\n // Live flip: a manual switch (P1) or a failover (P3) emits codex.account.switched;\n // turn.started covers the case where the worker recorded the actual account.\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isCodexAccountEvent,\n () => void refresh(),\n {\n enabled: options.enabled ?? true,\n ...(sharedEvents !== undefined ? { events: sharedEvents } : {}),\n },\n );\n\n const pin = useCallback(\n async (target: string): Promise<boolean> => {\n if (!sessionId || !codexClient.pinSessionCodexAccount) {\n return false;\n }\n setPinningTarget(target);\n const result = await runMutation(async () => {\n await codexClient.pinSessionCodexAccount!(workspaceId, sessionId, target);\n return true;\n });\n setPinningTarget(null);\n if (result) await refresh();\n return result === true;\n },\n [codexClient, workspaceId, sessionId, runMutation, refresh],\n );\n\n // Live usage refresh: hit the batched provider read, then re-read cached\n // metadata so the fresh windows land on `accounts`. The provider read writes the\n // cache columns server-side; state.refresh() pulls them back.\n const refreshUsage = useCallback(async (): Promise<boolean> => {\n if (!codexClient.refreshCodexUsage) {\n return false;\n }\n const result = await runUsageMutation(async () => {\n await codexClient.refreshCodexUsage!(workspaceId);\n return true;\n });\n if (result) await refresh();\n return result === true;\n }, [codexClient, workspaceId, runUsageMutation, refresh]);\n\n const data = loadedData ?? EMPTY_STATE;\n const effectiveAccountId = data.pinnedAccountId ?? data.activeAccountId;\n\n return {\n accounts: data.accounts,\n activeAccountId: data.activeAccountId,\n pinnedAccountId: data.pinnedAccountId,\n effectiveAccountId,\n lastAccountId: data.lastAccountId,\n settings: data.settings,\n loading,\n refresh,\n refreshUsage,\n refreshingUsage,\n pin,\n pinning,\n pinningTarget,\n mutationError,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,SAAS,aAAa,iBAAiB;AAKhC,SAAS,6BAA6B,OAA4C;AACvF,SAAO,MAAM,SAAS;AACxB;AAsBA,SAAS,eAAe,MAAoE;AAC1F,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,iBAAiB,KAAK;AAAA,IACtB,aAAa,KAAK,eAAe;AAAA,IACjC,QAAQ,KAAK;AAAA,EACf;AACF;AAOO,SAAS,yBACd,WACA,UAA2C,CAAC,GACZ;AAChC,QAAM,EAAE,QAAQ,aAAa,uBAAuB,0BAA0B,IAC5E,YAAY,OAAO;AACrB,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAE9D,QAAM,OAAO,YAAY,YAAmD;AAC1E,QAAI,CAAC,UAAW,QAAO;AACvB,UAAM,QAAQ,MAAM,OAAO,UAAU,aAAa,WAAW,EAAE,eAAe,KAAK,CAAC;AACpF,WAAO,eAAe,MAAM,CAAC,CAAC;AAAA,EAChC,GAAG,CAAC,QAAQ,aAAa,SAAS,CAAC;AAEnC,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,QAAI,WAAW,sBAAuB,MAAK,QAAQ;AAAA,EACrD,GAAG,CAAC,SAAS,SAAS,qBAAqB,CAAC;AAE5C,YAAU,MAAM;AACd,QAAI,CAAC,aAAa,CAAC,QAAS;AAC5B,WAAO,0BAA0B,WAAW,4BAA4B,OAAO;AAAA,EACjF,GAAG,CAAC,SAAS,SAAS,2BAA2B,SAAS,CAAC;AAK3D;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,KAAK,QAAQ;AAAA,IACnB;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,SAAS,WAAW,QAAQ,WAAW;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,UAAU,OAAO;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AC7FA,SAAS,eAAAA,oBAAmB;AAkBrB,SAAS,kBAAkB,UAAoC,CAAC,GAA4B;AACjG,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,QAAQ,QAAQ;AACtB,QAAM,OAAOC;AAAA,IACX,YAAY,MAAM,OAAO,mBAAmB,aAAa,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,CAAC;AAAA,IAC7F,CAAC,QAAQ,aAAa,KAAK;AAAA,EAC7B;AACA,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO;AAAA,IACL,OAAO,MAAM,QAAQ,CAAC;AAAA,IACtB,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;;;ACnCA,SAAS,eAAAC,cAAa,aAAAC,YAAW,cAAc;AAiCxC,SAAS,qBACd,UAAuC,CAAC,GACZ;AAC5B,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,QAAQ,QAAQ;AACtB,QAAM,kBAAkB,QAAQ;AAChC,QAAM,SAAS,QAAQ;AACvB,QAAM,SAAS,QAAQ;AACvB,QAAM,WAAW,QAAQ;AACzB,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW;AAAA,IACf;AAAA,IACA,SAAS;AAAA,IACT,oBAAoB,OAAO,SAAU,mBAAmB;AAAA,IACxD,UAAU;AAAA,IACV,UAAU;AAAA,IACV,WAAW,MAAM;AAAA,EACnB,EAAE,KAAK,IAAQ;AACf,QAAM,mBAAmB,OAAO,QAAQ;AACxC,QAAM,qBAAqB,iBAAiB,YAAY;AACxD,EAAAC,WAAU,MAAM;AACd,qBAAiB,UAAU;AAAA,EAC7B,GAAG,CAAC,QAAQ,CAAC;AACb,QAAM,OAAOC;AAAA,IACX,aAAa;AAAA,MACX;AAAA,MACA,MAAM,MAAM,OAAO,gBAAgB,aAAa;AAAA,QAC9C,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,QACvC,GAAI,oBAAoB,SAAY,EAAE,gBAAgB,IAAI,CAAC;AAAA,QAC3D,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,QACzC,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,QACzC,GAAI,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;AAAA,MACvC,CAAC;AAAA,IACH;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,iBAAiB,QAAQ,QAAQ,UAAU,QAAQ;AAAA,EAClF;AACA,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB;AAAA,EACF,CAAC;AAID,QAAM,OAAO,MAAM,MAAM,aAAa,WAAW,MAAM,KAAK,OAAO;AACnE,QAAM,SAAS,MAAM,UAAU,CAAC;AAChC,QAAM,WAAW,MAAM,YAAY,CAAC;AACpC,SAAO;AAAA;AAAA;AAAA;AAAA,IAIL,UAAU,CAAC,GAAG,QAAQ,GAAG,QAAQ;AAAA,IACjC;AAAA,IACA,iBAAiB,MAAM,mBAAmB;AAAA,IAC1C,YAAY,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhC,SACE,YACC,MAAM,WACL,sBACC,MAAM,SAAS,QAAQ,MAAM,KAAK,aAAa;AAAA,IACpD,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;;;AC9FA,SAAS,eAAAC,cAAa,eAAe;AAkC9B,SAAS,gBAAgB,UAAkC,CAAC,GAA0B;AAC3F,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOC;AAAA,IACX,YAAY,MAAM,OAAO,iBAAiB,WAAW;AAAA,IACrD,CAAC,QAAQ,WAAW;AAAA,EACtB;AACA,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB,IAAI,kBAAkB;AAE/E,QAAM,SAASA;AAAA,IACb,OAAO,YAAmE;AACxE,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,kBAAkB,aAAa,OAAO,CAAC;AAC7E,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OACE,eACA,YACgC;AAChC,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,kBAAkB,aAAa,eAAe,OAAO,CAAC;AAC5F,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,kBAA4C;AACjD,YAAM,SAAS,MAAM,IAAI,YAAY;AACnC,cAAM,OAAO,kBAAkB,aAAa,aAAa;AACzD,eAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO,WAAW;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,cAAcA;AAAA,IAClB,OACE,eACA,MACA,UACgD;AAChD,YAAM,SAAS,MAAM;AAAA,QAAI,MACvB,OAAO,uBAAuB,aAAa,eAAe,MAAM,KAAK;AAAA,MACvE;AACA,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,iBAAiBA;AAAA,IACrB,OAAO,eAAuB,SAAmC;AAC/D,YAAM,SAAS,MAAM,IAAI,YAAY;AACnC,cAAM,OAAO,0BAA0B,aAAa,eAAe,IAAI;AACvE,eAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO,WAAW;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,cAAc,QAAQ,CAAC;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAOO,SAAS,gBAAgB,UAAkC,CAAC,GAA0B;AAC3F,QAAM,eAAe,QAAQ,MAAM;AACjC,QAAI,CAAC,QAAQ,QAAQ;AACnB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,MACL,GAAG,QAAQ;AAAA,MACX,kBAAkB,QAAQ,OAAO,oBAAoB,QAAQ,OAAO;AAAA,MACpE,mBAAmB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO;AAAA,MACtE,mBAAmB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO;AAAA,MACtE,mBAAmB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO;AAAA,MACtE,wBACE,QAAQ,OAAO,0BAA0B,QAAQ,OAAO;AAAA,MAC1D,2BACE,QAAQ,OAAO,6BAA6B,QAAQ,OAAO;AAAA,IAC/D;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,CAAC;AACnB,QAAM,SAAS,gBAAgB,EAAE,GAAG,SAAS,GAAI,eAAe,EAAE,QAAQ,aAAa,IAAI,CAAC,EAAG,CAAC;AAChG,SAAO,EAAE,GAAG,QAAQ,cAAc,OAAO,aAAa;AACxD;;;AC1JA,SAAS,eAAAC,oBAAmB;AA+BrB,SAAS,QAAQ,UAA0B,CAAC,GAAkB;AACnE,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOC,aAAY,YAAY,MAAM,OAAO,SAAS,WAAW,GAAG,CAAC,QAAQ,WAAW,CAAC;AAC9F,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB,IAAI,kBAAkB;AAE/E,QAAM,SAASA;AAAA,IACb,OAAO,YAAmD;AACxD,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,UAAU,aAAa,OAAO,CAAC;AACrE,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,OAAe,YAAmD;AACvE,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,UAAU,aAAa,OAAO,OAAO,CAAC;AAC5E,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,UAAoC;AACzC,YAAM,SAAS,MAAM,IAAI,YAAY;AACnC,cAAM,OAAO,UAAU,aAAa,KAAK;AACzC,eAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO,WAAW;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,eAAeA;AAAA,IACnB,OAAO,UAAgD;AACrD,aAAO,MAAM,IAAI,MAAM,OAAO,gBAAgB,aAAa,KAAK,CAAC;AAAA,IACnE;AAAA,IACA,CAAC,QAAQ,aAAa,GAAG;AAAA,EAC3B;AAEA,QAAM,kBAAkBA;AAAA,IACtB,OAAO,OAAe,cAAkD;AACtE,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,mBAAmB,aAAa,OAAO,SAAS,CAAC;AACvF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,cAAcA;AAAA,IAClB,OAAO,UAA+C;AACpD,aAAO,MAAM,IAAI,MAAM,OAAO,eAAe,aAAa,KAAK,CAAC;AAAA,IAClE;AAAA,IACA,CAAC,QAAQ,aAAa,GAAG;AAAA,EAC3B;AAEA,QAAM,gBAAgBA;AAAA,IACpB,OAAO,OAAe,YAAgE;AACpF,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,iBAAiB,aAAa,OAAO,OAAO,CAAC;AACnF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,CAAC;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAgCO,SAAS,OAAO,OAAe,UAAyB,CAAC,GAAiB;AAC/E,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOA;AAAA,IACX,YAAY,MAAM,OAAO,OAAO,aAAa,KAAK;AAAA,IAClD,CAAC,QAAQ,aAAa,KAAK;AAAA,EAC7B;AACA,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB,IAAI,kBAAkB;AAE/E,QAAM,SAASA;AAAA,IACb,OAAO,YAAmD;AACxD,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,UAAU,aAAa,OAAO,OAAO,CAAC;AAC5E,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO;AAAA,EAC3C;AAEA,QAAM,SAASA,aAAY,YAA8B;AACvD,UAAM,SAAS,MAAM,IAAI,YAAY;AACnC,YAAM,OAAO,UAAU,aAAa,KAAK;AACzC,aAAO;AAAA,IACT,CAAC;AACD,WAAO,WAAW;AAAA,EACpB,GAAG,CAAC,QAAQ,aAAa,OAAO,GAAG,CAAC;AAEpC,QAAM,kBAAkBA;AAAA,IACtB,OAAO,cAAkD;AACvD,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,mBAAmB,aAAa,OAAO,SAAS,CAAC;AACvF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO;AAAA,EAC3C;AAEA,QAAM,gBAAgBA;AAAA,IACpB,OAAO,YAAgE;AACrE,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,iBAAiB,aAAa,OAAO,OAAO,CAAC;AACnF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO;AAAA,EAC3C;AAEA,QAAM,eAAeA;AAAA,IACnB,OAAO,aAAgD;AACrD,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,gBAAgB,aAAa,OAAO,QAAQ,CAAC;AACnF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO;AAAA,EAC3C;AAEA,QAAM,gBAAgBA;AAAA,IACpB,OAAO,aAAiD;AACtD,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,iBAAiB,aAAa,OAAO,QAAQ,CAAC;AACpF,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,OAAO,KAAK,OAAO;AAAA,EAC3C;AAEA,QAAM,SAASA,aAAY,YAAgE;AACzF,WAAO,MAAM,IAAI,MAAM,OAAO,UAAU,aAAa,KAAK,CAAC;AAAA,EAC7D,GAAG,CAAC,QAAQ,aAAa,OAAO,GAAG,CAAC;AAEpC,SAAO;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAeO,SAAS,eACd,OACA,UAAiC,CAAC,GACZ;AACtB,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOA;AAAA,IACX,YAAY,MAAM,OAAO,gBAAgB,aAAa,KAAK;AAAA,IAC3D,CAAC,QAAQ,aAAa,KAAK;AAAA,EAC7B;AACA,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO;AAAA,IACL,UAAU,MAAM,QAAQ,CAAC;AAAA,IACzB,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;AAkBO,SAAS,cACd,OACA,UAAgC,CAAC,GACZ;AACrB,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOA;AAAA,IACX,YAAY,MAAM,OAAO,eAAe,aAAa,KAAK;AAAA,IAC1D,CAAC,QAAQ,aAAa,KAAK;AAAA,EAC7B;AACA,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO;AAAA,IACL,SAAS,MAAM,QAAQ,CAAC;AAAA,IACxB,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;;;ACvUA,SAAS,eAAAC,oBAAmB;AAI5B,IAAM,sBAA0C,CAAC;AA4B1C,SAAS,SAAS,UAA2B,CAAC,GAAmB;AACtE,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAOC,aAAY,YAAY,MAAM,OAAO,UAAU,WAAW,GAAG,CAAC,QAAQ,WAAW,CAAC;AAC/F,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB,IAAI,kBAAkB;AAE/E,QAAM,WAAWA;AAAA,IACf,OAAO,aAAqF;AAC1F,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,aAAa,aAAa,QAAQ,CAAC;AACzE,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,QAAgB,UAA6B,CAAC,MAAwC;AAC3F,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,WAAW,aAAa,QAAQ,OAAO,CAAC;AAC9E,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,WAAqC;AAC1C,YAAM,SAAS,MAAM,IAAI,YAAY;AACnC,cAAM,OAAO,WAAW,aAAa,MAAM;AAC3C,eAAO;AAAA,MACT,CAAC;AACD,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO,WAAW;AAAA,IACpB;AAAA,IACA,CAAC,QAAQ,aAAa,KAAK,OAAO;AAAA,EACpC;AAEA,QAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAM,kBAAkBA;AAAA,IACtB,CAAC,WACC,cAAc,KAAK,CAAC,iBAAiB,aAAa,WAAW,MAAM,KAAK;AAAA,IAC1E,CAAC,aAAa;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,OAAO,MAAM,SAAS,CAAC;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxGA,SAAS,eAAAC,oBAAmB;AAyBrB,SAAS,cAAc,UAAgC,CAAC,GAAwB;AACrF,QAAM,SAAS,kBAAkB,OAAO;AACxC,QAAM,OAAOC,aAAY,YAAY,MAAM,OAAO,eAAe,GAAG,CAAC,MAAM,CAAC;AAC5E,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,eAAe,MAAM;AAAA,IAC7D,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,UAAU,eAAe,mBAAmB,IAAI,kBAAkB;AAE/E,QAAM,SAASA;AAAA,IACb,OAAO,YAA+D;AACpE,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,gBAAgB,OAAO,CAAC;AAC9D,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,KAAK,OAAO;AAAA,EACvB;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,aAAqB,YAA+D;AACzF,YAAM,SAAS,MAAM,IAAI,MAAM,OAAO,gBAAgB,aAAa,OAAO,CAAC;AAC3E,UAAI,QAAQ;AACV,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,QAAQ,KAAK,OAAO;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,YAAY,QAAQ,CAAC;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACnEA,SAAS,eAAAC,oBAAmB;AA6BrB,SAAS,gBAAgB,UAAkC,CAAC,GAA0B;AAC3F,QAAM,SAAS,kBAAkB,OAAO;AACxC,QAAM,YAAY,QAAQ;AAC1B,QAAM,cAAc,QAAQ;AAC5B,QAAM,OAAOC;AAAA,IACX,YACE,MAAM,OAAO,gBAAgB;AAAA,MAC3B,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MAC/C,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,IACrD,CAAC;AAAA,IACH,CAAC,QAAQ,WAAW,WAAW;AAAA,EACjC;AACA,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO;AAAA,IACL,SAAS,MAAM,MAAM,WAAW;AAAA,IAChC,OAAO,MAAM,MAAM,SAAS,CAAC;AAAA,IAC7B,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;;;ACpDA,SAAS,eAAAC,oBAAmB;AAwCrB,SAAS,mBACd,UAAqC,CAAC,GACZ;AAC1B,QAAM,SAAS,kBAAkB,OAAO;AACxC,QAAM,OAAOC,aAAY,YAAY,MAAM,OAAO,gBAAgB,GAAG,CAAC,MAAM,CAAC;AAC7E,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,SAAO;AAAA,IACL,QAAQ,MAAM,MAAM,UAAU,CAAC;AAAA,IAC/B,cAAc,MAAM,MAAM,gBAAgB;AAAA,IAC1C,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;AAGO,SAAS,yBACd,SACgC;AAChC,QAAM,SAAS,kBAAkB,OAAO;AACxC,QAAM,OAAOA,aAAY,YAAY;AACnC,QAAI,CAAC,QAAQ,aAAa;AACxB,aAAO,EAAE,QAAQ,CAAC,GAAG,cAAc,KAAK;AAAA,IAC1C;AACA,UAAM,CAAC,SAAS,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC1C,OAAO,yBAAyB,QAAQ,WAAW;AAAA,MACnD,OAAO,gBAAgB;AAAA,IACzB,CAAC;AACD,WAAO;AAAA,MACL,QAAQ,QAAQ;AAAA,MAChB,cAAc,OAAO;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,WAAW,CAAC;AAChC,QAAM,QAAQ,eAAe,MAAM;AAAA,IACjC,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ,YAAY,SAAS,QAAQ,QAAQ,WAAW;AAAA,EACnE,CAAC;AACD,QAAM,OAAO,eAAe,kBAAkB,MAAM,MAAM,UAAU,CAAC,CAAC,CAAC;AACvE,SAAO;AAAA,IACL,QAAQ,MAAM,MAAM,UAAU,CAAC;AAAA,IAC/B;AAAA,IACA,cAAc,MAAM,MAAM,gBAAgB;AAAA,IAC1C,SAAS,MAAM;AAAA,IACf,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,EACjB;AACF;;;AC1FA;AAAA,EACE;AAAA,EACA;AAAA,OAIK;AACP,SAAS,eAAAC,eAAa,aAAAC,YAAW,UAAAC,SAAQ,gBAAgB;AAkGzD,SAAS,kBAAkB,MAAqD;AAC9E,MAAI,KAAK,cAAc,KAAM,QAAO;AAEpC,SAAO,KAAK,WAAW,gBAAgB,KAAK,WAAW,qBAAqB,KAAK,WAAW;AAC9F;AAUA,SAAS,mBAAmB,MAAgD;AAC1E,MAAI,KAAK,cAAc,MAAM;AAE3B,WACE,KAAK,WAAW,gBAAgB,KAAK,WAAW,qBAAqB,KAAK,WAAW;AAAA,EAEzF;AAGA,SAAO;AACT;AAGA,SAAS,cAAc,QAAmD;AACxE,QAAM,MAAiC,CAAC;AACxC,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,SAAS,wBAAwB,MAAM,WAAW,OAAO,MAAM,YAAY,UAAU;AAC7F,UAAI,KAAK,MAAM,OAAkC;AAAA,IACnD;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,uBACd,WACA,UAAyC,CAAC,GACZ;AAC9B,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC9D,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,iBAAiB,QAAQ,kBAAkB;AACjD,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,oBAAoB,QAAQ,qBAAqB;AACvD,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE,KAAS,OAAO;AAE1E,QAAM,CAAC,cAAc,eAAe,IAAI,SAAqC,IAAI;AACjF,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAmC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AACrD,QAAM,CAAC,wBAAwB,yBAAyB,IAAI,SAE1D,IAAI;AACN,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,KAAK;AAC9D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAwB,IAAI;AAC5D,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAS,WAAW;AAE9D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,CAAC;AAIpC,QAAM,WAAWC,QAAO,CAAC;AACzB,QAAM,cAAcA,QAAsB,IAAI;AAC9C,QAAM,sBAAsBA,QAAO,WAAW;AAE9C,QAAM,cAAcC,cAAY,MAAM;AACpC,aAAS,CAAC,MAAM,IAAI,CAAC;AAAA,EACvB,GAAG,CAAC,CAAC;AAGL,EAAAC,WAAU,MAAM;AACd,UAAM,kBAAkB,oBAAoB,YAAY;AACxD,wBAAoB,UAAU;AAC9B,QAAI,iBAAiB;AACnB,uBAAiB,WAAW;AAC5B,sBAAgB,IAAI;AACpB,eAAS,MAAM;AACf,eAAS,IAAI;AACb,gCAA0B,IAAI;AAC9B,0BAAoB,KAAK;AACzB,kBAAY,IAAI;AAChB,eAAS,UAAU;AACnB,kBAAY,UAAU;AAAA,IACxB;AACA,QAAI,CAAC,WAAW,CAAC,WAAW;AAC1B,eAAS,MAAM;AACf,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,QAAI,YAAkD;AACtD,QAAI,iBAAwD;AAC5D,QAAI,gBAA+B;AACnC,UAAM,eAAe,IAAI,gBAAgB;AACzC,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,cAAc,MAAM;AACxB,UAAI,cAAc,KAAM,cAAa,SAAS;AAC9C,UAAI,mBAAmB,KAAM,eAAc,cAAc;AACzD,kBAAY;AACZ,uBAAiB;AAAA,IACnB;AAEA,UAAM,SAAS,CAAC,SAA8B;AAC5C,UAAI,UAAW;AACf,eAAS,UAAU,KAAK;AACxB,sBAAgB,IAAI;AACpB,eAAS,IAAI;AAAA,IACf;AAEA,UAAM,iBAAiB,CAAC,SAA8B;AACpD,UAAI,CAAC,iBAAiB,mBAAmB,KAAM;AAC/C,YAAM,aACJ,KAAK,4BAA4B,IAAI,KAAK,4BAA4B;AACxE,uBAAiB,YAAY,MAAM;AACjC,YAAI,aAAa,CAAC,cAAe;AACjC,aAAK,OACF,gBAAgB,aAAa,WAAW,eAAe,EAAE,YAAY,SAAS,QAAQ,CAAC,EACvF,KAAK,CAAC,QAAQ;AAGb,cAAI,CAAC,IAAI,SAAS,CAAC,WAAW;AAC5B,wBAAY;AAAA,UACd;AAAA,QACF,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,cAAI,UAAW;AACf,cACE,iBAAiB,qBAChB,MAAM,WAAW,OAAO,MAAM,WAAW,MAC1C;AACA,wBAAY;AAAA,UACd;AAAA,QACF,CAAC;AAAA,MACL,GAAG,UAAU;AAAA,IACf;AAEA,UAAM,gBAAgB,MAAM;AAC1B,kBAAY,WAAW,MAAM;AAC3B,YAAI,UAAW;AACf,aAAK,OACF,sBAAsB,aAAa,WAAW,EAAE,QAAQ,aAAa,OAAO,CAAC,EAC7E,KAAK,CAAC,SAAS;AACd,cAAI,UAAW;AACf,iBAAO,IAAI;AACX,cAAI,KAAK,aAAa,UAAU,KAAK,aAAa,YAAY;AAC5D,qBAAS,OAAO;AAChB;AAAA,UACF;AACA,cAAI,oBAAoB,KAAK,KAAK,IAAI,IAAI,YAAY,mBAAmB;AACvE,qBAAS,OAAO;AAChB,qBAAS,IAAI,MAAM,2DAAsD,CAAC;AAC1E;AAAA,UACF;AACA,wBAAc;AAAA,QAChB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,cAAI,CAAC,WAAW;AACd,qBAAS,OAAO;AAChB,qBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,UACpE;AAAA,QACF,CAAC;AAAA,MACL,GAAG,aAAa;AAAA,IAClB;AAEA,UAAM,YAAY;AAChB,eAAS,aAAa;AACtB,gCAA0B,IAAI;AAC9B,0BAAoB,KAAK;AACzB,UAAI;AACF,cAAM,OAAO,MAAM,OAAO,sBAAsB,aAAa,WAAW;AAAA,UACtE,QAAQ,aAAa;AAAA,QACvB,CAAC;AACD,YAAI,UAAW;AACf,eAAO,IAAI;AAkBX,cAAM,oBAAoB,iBAAiB,kBAAkB,KAAK,aAAa;AAC/E,cAAM,qBAAqB,kBAAkB,mBAAmB,KAAK,QAAQ;AAS7E,cAAM,kBAAkB,eAAe,KAAK,WAAW;AACvD,YAAI,qBAAqB,sBAAsB,iBAAiB;AAC9D,cAAI;AAKF,kBAAM,SAAS,MAAM,OAAO,aAAa,aAAa,WAAW;AAAA,cAC/D,SAAS;AAAA,YACX,CAAC;AACD,gBAAI,WAAW;AAKb,mBAAK,OAAO,aAAa,aAAa,WAAW,OAAO,QAAQ,EAAE,MAAM,MAAM;AAAA,cAAC,CAAC;AAChF;AAAA,YACF;AACA,4BAAgB,OAAO;AACvB,wBAAY,UAAU,OAAO;AAC7B,wBAAY,OAAO,QAAQ;AAC3B,qBAAS,UAAU,OAAO;AAI1B;AAAA,cAAgB,CAAC,SACf,OACI;AAAA,gBACE,GAAG;AAAA,gBACH,UAAU,OAAO;AAAA,gBACjB,YAAY,OAAO;AAAA,gBACnB,2BAA2B,OAAO;AAAA,gBAClC,eAAe,oBACX;AAAA,kBACE,GAAG,KAAK;AAAA,kBACR,WAAW,OAAO,aAAa,KAAK,cAAc;AAAA,kBAClD,QAAQ,OAAO,UAAU,KAAK,cAAc;AAAA,kBAC5C,KAAK,OAAO,gBAAgB,KAAK,cAAc;AAAA,kBAC/C,OAAO,OAAO,eAAe,KAAK,cAAc;AAAA,kBAChD,WAAW,OAAO,mBAAmB,KAAK,cAAc;AAAA,kBACxD,YAAY,OAAO,cAAc,KAAK,cAAc;AAAA,gBACtD,IACA,KAAK;AAAA,gBACT,UACE,OAAO,eAAe,OAAO,oBACzB;AAAA,kBACE,GAAG,KAAK;AAAA,kBACR,WAAW,OAAO;AAAA,kBAClB,KAAK,OAAO;AAAA,kBACZ,OAAO,OAAO,iBAAiB,KAAK,SAAS;AAAA;AAAA,kBAE7C,YAAY;AAAA,kBACZ,QAAQ;AAAA,gBACV,IACA,KAAK;AAAA,cACb,IACA;AAAA,YACN;AAAA,UACF,SAAS,OAAO;AACd,gBAAI,UAAW;AACf,gBAAI,iBAAiB,kBAAkB;AACrC,kBAAI,MAAM,WAAW,KAAK;AAMxB,oBAAI,mBAAmB;AACrB;AAAA,oBACE,MAAM,QAAQ,SAAS,uBAAuB,IAAI,WAAW;AAAA,kBAC/D;AAAA,gBACF;AAAA,cACF,WAAW,MAAM,WAAW,KAAK;AAC/B,oCAAoB,IAAI;AAAA,cAC1B,WAAW,MAAM,WAAW,KAAK;AAC/B,yBAAS,OAAO;AAChB,yBAAS,KAAK;AACd;AAAA,cACF;AAAA,YAGF,OAAO;AACL,oBAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAMA,cAAM,kBAAkB,qBAAqB,sBAAsB;AACnE,YAAI,KAAK,aAAa,UAAU,KAAK,aAAa,cAAc,eAAe;AAC7E,mBAAS,OAAO;AAChB,yBAAe,IAAI;AAAA,QACrB,WAAW,iBAAiB;AAI1B,mBAAS,MAAM;AACf,wBAAc;AAAA,QAChB,OAAO;AAKL,mBAAS,WAAW;AAAA,QACtB;AAAA,MACF,SAAS,OAAO;AACd,YAAI,UAAW;AACf,YAAI,iBAAiB,oBAAoB,MAAM,WAAW,KAAK;AAC7D,mBAAS,OAAO;AAChB,mBAAS,IAAI,MAAM,8CAA8C,CAAC;AAClE;AAAA,QACF;AACA,iBAAS,OAAO;AAChB,iBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MACpE;AAAA,IACF,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AACZ,mBAAa,MAAM;AACnB,kBAAY;AAGZ,YAAM,YAAY,iBAAiB,YAAY;AAC/C,UAAI,WAAW;AACb,aAAK,OAAO,aAAa,aAAa,WAAW,SAAS,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAAA,MAC5E;AAAA,IACF;AAAA,EAEF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,QAAM,SAAS,QAAQ;AACvB,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,WAAW,EAAG;AACpC,oBAAgB,CAAC,SAAS;AACxB,UAAI,CAAC,QAAQ,CAAC,KAAK,cAAc,IAAK,QAAO;AAC7C,UAAI,OAAO,KAAK;AAChB,UAAI,UAAU;AACd,iBAAW,YAAY,cAAc,MAAM,GAAG;AAE5C,YAAI,SAAS,YAAY,YAAY,WAAW,SAAS,aAAa,YAAY,SAAS;AACzF;AAAA,QACF;AACA,cAAM,UAAU,iBAAiB,MAAM,UAAU,SAAS,OAAO;AACjE,YAAI,SAAS;AACX,iBAAO,EAAE,GAAG,MAAM,KAAK,QAAQ,KAAK,OAAO,QAAQ,OAAO,WAAW,QAAQ,UAAU;AACvF,mBAAS,UAAU,KAAK,IAAI,SAAS,SAAS,SAAS,UAAU;AACjE,oBAAU;AAAA,QACZ;AAAA,MACF;AACA,aAAO,UAAU,EAAE,GAAG,MAAM,eAAe,MAAM,YAAY,SAAS,QAAQ,IAAI;AAAA,IACpF,CAAC;AAAA,EACH,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,kBAAkB,WAAW,kBAAkB;AACrD,SAAO;AAAA,IACL,cAAc,kBAAkB,eAAe;AAAA,IAC/C,OAAO,kBAAkB,QAAQ;AAAA,IACjC,OAAO,kBAAkB,QAAQ;AAAA,IACjC,wBAAwB,kBAAkB,yBAAyB;AAAA,IACnE,kBAAkB,mBAAmB;AAAA,IACrC,UAAU,kBAAkB,WAAW;AAAA,IACvC;AAAA,EACF;AACF;;;ACrfA;AAAA,EACE;AAAA,EACA;AAAA,OAKK;AACP,SAAyB,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACO5D,SAAS,cAAc,oBAAoB;AAGpC,IAAM,sBAAsB;AAE5B,IAAM,qBAAqB;AAiBlC,SAAS,cAAc,SAAuB,QAAoC;AAChF,MAAI,QAAQ,cAAc,GAAI,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,SAAS;AACxE,MAAI,QAAQ,gBAAgB,GAAI,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,WAAW;AAC5E,MAAI,QAAQ,YAAY,GAAI,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,OAAO;AACpE,MAAI,QAAQ,SAAS,EAAG,QAAO,OAAO,EAAE,EAAE,MAAM,QAAQ,IAAI;AAC5D,MAAI,QAAQ,SAAS,EAAG,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,IAAI;AAC7D,SAAO;AACT;AAGO,SAAS,iBAAiB,SAAsC;AACrE,QAAM,SAAS,IAAI,aAAa;AAChC,gBAAc,QAAQ,SAAS,OAAO,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK;AAC9D,MAAI,QAAQ,UAAU,GAAI,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,KAAK;AAChE,MAAI,QAAQ,SAAS,EAAG,QAAO,OAAO,EAAE,EAAE,MAAM,QAAQ,IAAI;AAC5D,MAAI,QAAQ,kBAAkB,IAAK,QAAO,OAAO,EAAE,EAAE,OAAO,QAAQ,aAAa;AACjF,SAAO,OAAO,OAAO;AACvB;AAGA,SAAS,wBAAwB,QAAsB,QAAsC;AAC3F,QAAM,MAAM,OAAO,MAAM;AACzB,MAAI;AACJ,SAAO,OAAO,MAAM,KAAK;AACvB,UAAM,MAAM,OAAO,OAAO;AAC1B,QAAI,QAAQ,MAAM,KAAK,QAAQ,IAAI;AACjC,gBAAU,OAAO,OAAO;AACxB;AAAA,IACF;AACA,SAAK,MAAM,OAAO,KAAK,QAAQ,EAAG;AAClC,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB;AACA,SAAO,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAChD;AAGO,SAAS,oBAAoB,OAGlC;AACA,QAAM,SAAS,IAAI,aAAa,KAAK;AACrC,QAAM,MAAM,OAAO;AACnB,MAAI,WAAW;AACf,MAAI;AACJ,SAAO,OAAO,MAAM,KAAK;AACvB,UAAM,MAAM,OAAO,OAAO;AAC1B,QAAI,QAAQ,MAAM,KAAK,QAAQ,GAAG;AAChC,iBAAW,OAAO,KAAK;AACvB;AAAA,IACF;AACA,QAAI,QAAQ,MAAM,KAAK,QAAQ,IAAI;AACjC,cAAQ,wBAAwB,QAAQ,OAAO,OAAO,CAAC;AACvD;AAAA,IACF;AACA,SAAK,MAAM,OAAO,KAAK,QAAQ,EAAG;AAClC,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB;AACA,SAAO,UAAU,SAAY,EAAE,UAAU,MAAM,IAAI,EAAE,SAAS;AAChE;AAGO,SAAS,kBAAkB,OAAyC;AACzE,QAAM,SAAS,IAAI,aAAa,KAAK;AACrC,QAAM,MAAM,OAAO;AACnB,MAAI,OAAO,IAAI,WAAW,CAAC;AAC3B,SAAO,OAAO,MAAM,KAAK;AACvB,UAAM,MAAM,OAAO,OAAO;AAC1B,QAAI,QAAQ,MAAM,KAAK,QAAQ,IAAI;AAIjC,aAAO,IAAI,WAAW,OAAO,MAAM,CAAC;AACpC;AAAA,IACF;AACA,SAAK,MAAM,OAAO,KAAK,QAAQ,EAAG;AAClC,WAAO,KAAK,MAAM,CAAC;AAAA,EACrB;AACA,SAAO,EAAE,KAAK;AAChB;;;AC3GA,SAAyB,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AA0C5D,IAAM,WAAW;AACjB,IAAM,cAAc;AACpB,IAAM,YAAY;AAIlB,SAAS,SAAS,KAAa,MAA8B;AAC3D,QAAM,MAAM,IAAI,WAAW,KAAK,SAAS,CAAC;AAC1C,MAAI,CAAC,IAAI;AACT,MAAI,IAAI,MAAM,CAAC;AACf,SAAO;AACT;AAEA,SAAS,wBAAwB,KAAmC;AAGlE,SAAO,IAAI,UAAU,GAAG;AAC1B;AAoBO,SAAS,oBACd,SAC2B;AAC3B,QAAM,EAAE,YAAY,cAAc,iBAAiB,IAAI;AACvD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAiC,MAAM;AACjE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,CAAC;AACpC,QAAM,WAAWD,QAA+B,MAAM;AACtD,QAAM,UAAU,CAAC,SAAiC;AAChD,aAAS,UAAU;AACnB,aAAS,IAAI;AAAA,EACf;AAEA,QAAM,YAAY,MAAM,SAAS,CAAC,MAAM,IAAI,CAAC;AAE7C,QAAM,YAAY,YAAY,aAAa;AAC3C,QAAM,MAAM,YAAY,OAAO;AAC/B,QAAM,QAAQ,YAAY,SAAS;AAInC,QAAM,aAAaA,QAAO,gBAAgB;AAC1C,aAAW,UAAU;AAErB,EAAAD,WAAU,MAAM;AAEd,QAAI,OAAO,WAAW,YAAa;AAGnC,QAAI,cAAc,kBAAkB,CAAC,KAAK;AACxC,cAAQ,MAAM;AACd;AAAA,IACF;AACA,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,WAAW;AAGd,cAAQ,MAAM;AACd;AAAA,IACF;AAGA,QAAI;AAOJ,QAAI;AACF,YAAM,IAAI,IAAI,IAAI,GAAG;AACrB,gBAAU;AAAA,QACR,WAAW,EAAE,aAAa,IAAI,SAAS,KAAK;AAAA,QAC5C,aAAa,EAAE,aAAa,IAAI,IAAI,KAAK;AAAA,QACzC,SAAS,EAAE,aAAa,IAAI,OAAO,KAAK;AAAA,QACxC,MAAM;AAAA,QACN,MAAM,OAAO,EAAE,aAAa,IAAI,MAAM,KAAK,GAAG;AAAA,MAChD;AAAA,IACF,SAAS,OAAO;AACd,eAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAClE,cAAQ,OAAO;AACf;AAAA,IACF;AAEA,aAAS,IAAI;AACb,YAAQ,YAAY;AAMpB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,aAAa,gCAAgC,EAAE;AACtD,UAAM,QAAQ,OAAO;AACrB,UAAM,WAAW;AACjB,UAAM,MAAM;AACZ,UAAM,OAAO;AACb,UAAM,QAAQ;AACd,UAAM,SAAS;AACf,UAAM,SAAS;AACf,UAAM,WAAW;AACjB,UAAM,YAAY;AAClB,UAAM,QAAQ;AACd,UAAM,SAAS;AACf,UAAM,UAAU;AAChB,UAAM,iBAAiB;AACvB,cAAU,YAAY,MAAM;AAC5B,UAAM,MAAM,OAAO,WAAW,IAAI;AAElC,QAAI,WAAW;AACf,QAAI,QAAQ;AAGZ,QAAI,WAAW;AACf,QAAI,UAA6B;AAEjC,UAAM,cAAc,YAAY;AAC9B,UAAI,SAAU;AACd,iBAAW;AACX,UAAI;AACF,eAAO,SAAS;AACd,cAAI,SAAU;AACd,gBAAM,OAAO;AACb,oBAAU;AACV,cAAI;AACJ,cAAI;AAKF,kBAAM,MAAM,kBAAkB,IAAI,KAAK,CAAC,IAAI,WAAW,IAAI,CAAC,GAAG,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA,UACvF,QAAQ;AAEN;AAAA,UACF;AACA,cAAI,UAAU;AACZ,gBAAI,MAAM;AACV;AAAA,UACF;AAGA,cAAI,OAAO,UAAU,IAAI,SAAS,OAAO,WAAW,IAAI,QAAQ;AAC9D,mBAAO,QAAQ,IAAI;AACnB,mBAAO,SAAS,IAAI;AAAA,UACtB;AACA,eAAK,UAAU,KAAK,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AACrD,cAAI,MAAM;AAEV,cAAI,CAAC,YAAY,SAAS,YAAY,YAAa,SAAQ,WAAW;AAAA,QACxE;AAAA,MACF,UAAE;AACA,mBAAW;AAAA,MACb;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,YAAM,WAAW,WAAW,yBAAyB,GAAG;AAAA,IAC1D,SAAS,OAAO;AACd,eAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAClE,cAAQ,OAAO;AACf,aAAO,OAAO;AACd;AAAA,IACF;AACA,OAAG,aAAa;AAEhB,UAAM,SAAS,MAAM;AACnB,UAAI,SAAU;AACd,YAAM,OAAO,iBAAiB;AAAA,QAC5B;AAAA,QACA,OAAO,SAAS;AAAA,QAChB,MAAM;AAAA,QACN,eAAe;AAAA,MACjB,CAAC;AACD,UAAI;AACF,WAAG,KAAK,SAAS,UAAU,IAAI,EAAE,MAAqB;AAAA,MACxD,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,YAAY,CAAC,OAA2B;AAC5C,UAAI,SAAU;AACd,YAAM,OAAO,GAAG;AAChB,UAAI,EAAE,gBAAgB,aAAc;AACpC,YAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,UAAI,IAAI,WAAW,EAAG;AACtB,YAAM,MAAM,IAAI,CAAC;AACjB,YAAM,OAAO,IAAI,SAAS,CAAC;AAC3B,UAAI,QAAQ,aAAa;AACvB,YAAI;AACJ,YAAI;AACF,gBAAM,oBAAoB,IAAI;AAAA,QAChC,QAAQ;AACN;AAAA,QACF;AACA,YAAI,CAAC,IAAI,UAAU;AACjB;AAAA,YACE,IAAI;AAAA,cACF,IAAI,OAAO,UACP,4BAA4B,IAAI,MAAM,OAAO,KAC7C;AAAA,YACN;AAAA,UACF;AACA,kBAAQ,OAAO;AACf,cAAI;AACF,eAAG,MAAM;AAAA,UACX,QAAQ;AAAA,UAER;AACA;AAAA,QACF;AAEA,gBAAQ;AAAA,MACV,WAAW,QAAQ,WAAW;AAC5B,YAAI;AACJ,YAAI;AACF,eAAK,kBAAkB,IAAI;AAAA,QAC7B,QAAQ;AACN;AAAA,QACF;AACA,YAAI,GAAG,QAAQ,GAAG,KAAK,SAAS,GAAG;AACjC,oBAAU,GAAG;AACb,eAAK,YAAY;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd,eAAS,CAAC,SAAS,QAAQ,IAAI,MAAM,iCAAiC,CAAC;AACvE,cAAQ,OAAO;AAAA,IACjB;AAEA,UAAM,UAAU,MAAM;AACpB,UAAI,SAAU;AACd;AAAA,QACE,CAAC,SACC,QACA,IAAI,MAAM,QAAQ,0BAA0B,wCAAwC;AAAA,MACxF;AACA,cAAQ,OAAO;AAAA,IACjB;AAEA,OAAG,iBAAiB,QAAQ,MAAM;AAClC,OAAG,iBAAiB,WAAW,SAAS;AACxC,OAAG,iBAAiB,SAAS,OAAO;AACpC,OAAG,iBAAiB,SAAS,OAAO;AAEpC,WAAO,MAAM;AACX,iBAAW;AACX,gBAAU;AACV,SAAG,oBAAoB,QAAQ,MAAM;AACrC,SAAG,oBAAoB,WAAW,SAAS;AAC3C,SAAG,oBAAoB,SAAS,OAAO;AACvC,SAAG,oBAAoB,SAAS,OAAO;AACvC,UAAI;AACF,WAAG,MAAM;AAAA,MACX,QAAQ;AAAA,MAER;AACA,aAAO,OAAO;AAAA,IAChB;AAAA,EAKF,GAAG,CAAC,KAAK,OAAO,WAAW,KAAK,CAAC;AAEjC,SAAO,EAAE,OAAO,OAAO,UAAU;AACnC;;;AFxSA,eAAe,oBAAgD;AAC7D,QAAM,MAAO,MAAM,OAAO,cAAc;AAOxC,QAAM,MAAM,IAAI;AAChB,SAAO,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,QAAQ,KAAK,IAAI;AACzD;AAqBO,SAAS,iBAAiB,SAA0D;AACzF,QAAM,EAAE,YAAY,cAAc,aAAa,eAAe,YAAY,iBAAiB,IACzF;AAIF,QAAM,cAAc,oBAAoB;AAAA,IACtC;AAAA,IACA;AAAA,IACA,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,EACjD,CAAC;AACD,QAAM,CAAC,OAAO,QAAQ,IAAIG,UAAiC,MAAM;AACjE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,CAAC;AACpC,QAAM,WAAWC,QAA+B,MAAM;AACtD,QAAM,UAAU,CAAC,SAAiC;AAChD,aAAS,UAAU;AACnB,aAAS,IAAI;AAAA,EACf;AAEA,QAAM,YAAY,MAAM,SAAS,CAAC,MAAM,IAAI,CAAC;AAE7C,QAAM,MAAM,YAAY,OAAO;AAI/B,QAAM,QAAQ,YAAY,SAAS;AACnC,QAAM,YAAY,YAAY,aAAa;AAC3C,QAAM,OAAO,YAAY,QAAQ;AAOjC,QAAM,SAASA,QAA8B,IAAI;AAIjD,QAAM,iBAAiBA,QAAO,WAAW;AACzC,QAAM,mBAAmBA,QAAO,aAAa;AAC7C,QAAM,UAAUA,QAAO,IAAI;AAC3B,QAAM,gBAAgBA,QAAO,UAAU;AACvC,iBAAe,UAAU;AACzB,mBAAiB,UAAU;AAC3B,UAAQ,UAAU;AAClB,gBAAc,UAAU;AAGxB,QAAM,cAAc,CAAC,GAAW,SAA8B,MAAM,eAAe,CAAC;AAEpF,EAAAC,WAAU,MAAM;AAEd,QAAI,OAAO,WAAW,YAAa;AACnC,QAAI,cAAc,YAAY,CAAC,KAAK;AAClC,cAAQ,MAAM;AACd;AAAA,IACF;AACA,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,WAAW;AAMd,cAAQ,MAAM;AACd;AAAA,IACF;AAEA,QAAI,MAA6B;AACjC,QAAI,WAAW;AACf,aAAS,IAAI;AACb,YAAQ,aAAa;AAErB,UAAM,YAAY,MAAM;AACtB,UAAI,CAAC,SAAU,SAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,YAAY,CAAC,CAAC;AAAA,IAClF;AACA,UAAM,eAAe,MAAM;AACzB,UAAI,CAAC,SAAU,SAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,eAAe,CAAC,CAAC;AAAA,IACrF;AACA,UAAM,oBAAoB,MAAM;AAC9B,UAAI,SAAU;AACd,eAAS,IAAI,MAAM,0DAA0D,CAAC;AAC9E,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,IAC9D;AAEA,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,UAAU,cAAc,WAAY,MAAM,kBAAkB;AAClE,YAAI,SAAU;AACd,cAAM,YAAY,iBAAiB,EAAE,IAAI,CAAC;AAC1C,gBAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,aAAa,CAAC,CAAC;AAClE,cAAM,QAAQ,WAAW,WAAW;AAAA,UAClC,aAAa,QAAQ,EAAE,UAAU,MAAM,IAAI;AAAA,QAC7C,CAAC;AACD,YAAI,WAAW,YAAY,QAAQ,SAAS,eAAe,OAAO;AAMlE,YAAI,eAAe;AACnB,YAAI,gBAAgB,iBAAiB,WAAW;AAChD,YAAI,iBAAiB,WAAW,SAAS;AACzC,YAAI,iBAAiB,cAAc,YAAY;AAC/C,YAAI,iBAAiB,mBAAmB,iBAAiB;AACzD,eAAO,UAAU;AAAA,MACnB,SAAS,OAAO;AACd,YAAI,CAAC,UAAU;AACb,mBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAClE,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAAA,IACF,GAAG;AAEH,WAAO,MAAM;AACX,iBAAW;AACX,UAAI,OAAO,YAAY,IAAK,QAAO,UAAU;AAC7C,UAAI,KAAK;AACP,YAAI,sBAAsB,WAAW,SAAS;AAC9C,YAAI,sBAAsB,cAAc,YAAY;AACpD,YAAI,sBAAsB,mBAAmB,iBAAiB;AAC9D,YAAI;AACF,cAAI,WAAW;AAAA,QACjB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EAMF,GAAG,CAAC,KAAK,OAAO,WAAW,KAAK,CAAC;AAKjC,EAAAA,WAAU,MAAM;AACd,UAAM,MAAM,OAAO;AACnB,QAAI,IAAK,KAAI,WAAW,YAAY,MAAM,WAAW;AAAA,EAGvD,GAAG,CAAC,aAAa,MAAM,KAAK,CAAC;AAQ7B,EAAAA,WAAU,MAAM;AACd,UAAM,MAAM,OAAO;AACnB,QAAI,KAAK;AACP,UAAI,eAAe;AACnB,UAAI,gBAAgB,iBAAiB;AAAA,IACvC;AAAA,EAEF,GAAG,CAAC,eAAe,KAAK,CAAC;AAMzB,MAAI,cAAc,eAAgB,QAAO;AACzC,SAAO,EAAE,OAAO,OAAO,UAAU;AACnC;;;AG/OA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAmCrD,SAAS,YAAY,MAAkE;AACrF,MAAI,OAAO,SAAS,UAAU;AAC5B,WAAO,EAAE,SAAS,KAAK,OAAO,CAAC,GAAG,SAAS,KAAK,MAAM,CAAC,EAAE;AAAA,EAC3D;AACA,QAAM,QAAQ,IAAI,WAAW,IAAI;AACjC,QAAM,UAAU,MAAM,SAAS,IAAI,OAAO,aAAa,MAAM,CAAC,CAAE,IAAI;AACpE,QAAM,UAAU,MAAM,SAAS,IAAI,IAAI,YAAY,EAAE,OAAO,MAAM,SAAS,CAAC,CAAC,IAAI;AACjF,SAAO,EAAE,SAAS,QAAQ;AAC5B;AAmBO,SAAS,kBAAkB,SAA4D;AAC5F,QAAM,EAAE,YAAY,UAAU,SAAS,aAAa,YAAY,IAAI;AACpE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAA+B,QAAQ;AACnE,QAAM,QAAQD,QAAyB,IAAI;AAG3C,QAAM,UAAUA,QAAuC;AAAA,IACrD,MAAM,eAAe;AAAA,IACrB,MAAM,eAAe;AAAA,EACvB,CAAC;AAGD,QAAM,cAAcA,QAAO,QAAQ;AACnC,QAAM,aAAaA,QAAO,OAAO;AACjC,cAAY,UAAU;AACtB,aAAW,UAAU;AAErB,QAAM,YAAY,YAAY,aAAa;AAC3C,QAAM,MAAM,YAAY,OAAO;AAC/B,QAAM,QAAQ,YAAY,SAAS;AAEnC,EAAAF,WAAU,MAAM;AAGd,QAAI,OAAO,WAAW,eAAe,OAAO,cAAc,YAAa;AACvE,QAAI,cAAc,YAAY,CAAC,KAAK;AAClC,gBAAU,QAAQ;AAClB;AAAA,IACF;AAEA,QAAI,WAAW;AACf,QAAI;AACJ,cAAU,YAAY;AACtB,QAAI;AAGF,eAAS,IAAI,UAAU,kBAAkB,EAAE,IAAI,CAAC,GAAG,gBAAgB;AAAA,IACrE,QAAQ;AACN,gBAAU,OAAO;AACjB;AAAA,IACF;AACA,WAAO,aAAa;AACpB,UAAM,UAAU;AAEhB,WAAO,SAAS,MAAM;AACpB,UAAI,SAAU;AAId,UAAI;AACF,eAAO,KAAK,cAAc,EAAE,SAAS,QAAQ,QAAQ,MAAM,MAAM,QAAQ,QAAQ,KAAK,CAAC,CAAC;AACxF,eAAO,KAAK,gBAAgB,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,IAAI,CAAC;AAAA,MACzE,QAAQ;AAAA,MAER;AACA,gBAAU,MAAM;AAAA,IAClB;AAEA,WAAO,YAAY,CAAC,OAAqB;AACvC,UAAI,SAAU;AACd,YAAM,EAAE,SAAS,QAAQ,IAAI,YAAY,GAAG,IAA4B;AACxE,cAAQ,SAAS;AAAA,QACf,KAAK,kBAAkB;AACrB,sBAAY,UAAU,OAAO;AAC7B;AAAA,QACF,KAAK,kBAAkB;AACrB,qBAAW,UAAU,OAAO;AAC5B;AAAA;AAAA,QAEF;AACE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,UAAU,MAAM;AACrB,UAAI,CAAC,SAAU,WAAU,OAAO;AAAA,IAClC;AACA,WAAO,UAAU,MAAM;AACrB,UAAI,CAAC,SAAU,WAAU,QAAQ;AAAA,IACnC;AAEA,WAAO,MAAM;AACX,iBAAW;AACX,YAAM,UAAU;AAEhB,aAAO,SAAS;AAChB,aAAO,YAAY;AACnB,aAAO,UAAU;AACjB,aAAO,UAAU;AACjB,UAAI;AACF,eAAO,MAAM;AAAA,MACf,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EAGF,GAAG,CAAC,WAAW,KAAK,KAAK,CAAC;AAE1B,SAAOC,SAAiC,MAAM;AAC5C,UAAM,QAAQ,CAAC,SAAiB;AAC9B,YAAM,KAAK,MAAM;AACjB,UAAI,MAAM,GAAG,eAAe,UAAU,MAAM;AAC1C,YAAI;AACF,aAAG,KAAK,eAAe,IAAI,CAAC;AAAA,QAC9B,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,UAAM,SAAS,CAAC,MAAc,SAAiB;AAC7C,UAAI,QAAQ,KAAK,QAAQ,EAAG;AAC5B,cAAQ,UAAU,EAAE,MAAM,KAAK;AAC/B,YAAM,KAAK,MAAM;AACjB,UAAI,MAAM,GAAG,eAAe,UAAU,MAAM;AAC1C,YAAI;AACF,aAAG,KAAK,gBAAgB,MAAM,IAAI,CAAC;AAAA,QACrC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,UAAM,aAAa,MAAM;AACvB,YAAM,KAAK,MAAM;AACjB,YAAM,UAAU;AAChB,UAAI,IAAI;AACN,YAAI;AACF,aAAG,MAAM;AAAA,QACX,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AACA,WAAO,EAAE,WAAW,WAAW,QAAQ,QAAQ,OAAO,QAAQ,WAAW;AAAA,EAC3E,GAAG,CAAC,MAAM,CAAC;AACb;;;ACxMA,SAAS,oBAAAG,yBAAwB;AACjC,SAAS,eAAAC,eAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAiE3D,SAAS,mBACd,WACA,SAC0B;AAC1B,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,uBAAuB,QAAQ,wBAAwB;AAC7D,QAAM,cAAc,QAAQ,eAAe;AAI3C,QAAM,YAAY,QAAQ;AAC1B,QAAM,WAAW,QAAQ;AACzB,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE,KAAS,WAAW,KAAS,aAAa,EAAE;AACtG,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAwB,IAAI;AAClE,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAuB,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,WAAW;AAI9D,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,CAAC;AAChD,QAAM,qBAAqBC,QAAO,WAAW;AAC7C,qBAAmB,UAAU;AAI7B,EAAAC,WAAU,MAAM;AACd,qBAAiB,WAAW;AAC5B,mBAAe,IAAI;AACnB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,WAAW,CAAC;AAShB,QAAM,eAAeD,QAAO,KAAK;AACjC,QAAM,UAAU,aAAa,UAAa,aAAa,UAAU,aAAa;AAC9E,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,aAAa,aAAa,CAAC,SAAS;AACvD,mBAAa,IAAI;AACjB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,iBAAa,IAAI;AACjB,iBAAa,UAAU;AACvB,QAAI,WAA0B;AAC9B,SAAK,OACF,gBAAgB,aAAa,WAAW,CAAC,CAAC,EAC1C,KAAK,CAAC,QAAQ;AACb,UAAI,WAAW;AAEb,aAAK,OACF,iBAAiB,aAAa,WAAW,EAAE,OAAO,IAAI,MAAM,CAAC,EAC7D,MAAM,MAAM;AAAA,QAAC,CAAC;AACjB;AAAA,MACF;AACA,iBAAW,IAAI;AACf,qBAAe,IAAI,KAAK;AAAA,IAC1B,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,CAAC,UAAW,cAAa,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACxF,CAAC,EACA,QAAQ,MAAM;AACb,mBAAa,UAAU;AAAA,IACzB,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AACZ,qBAAe,IAAI;AACnB,YAAM,KAAK;AACX,UAAI,GAAI,MAAK,OAAO,iBAAiB,aAAa,WAAW,EAAE,OAAO,GAAG,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAC5F;AAAA,EACF,GAAG,CAAC,aAAa,QAAQ,aAAa,WAAW,WAAW,SAAS,WAAW,CAAC;AAEjF,QAAM,EAAE,QAAQ,SAAS,cAAc,IAAIC,SAAQ,MAAM;AACvD,UAAM,MAAuB,CAAC;AAE9B,UAAM,OAAO,oBAAI,IAAwC;AACzD,QAAI,aAA4B;AAChC,QAAI,oBAAoB;AAExB,eAAW,SAAS,QAAQ,QAAQ;AAGlC,UAAI,MAAM,gBAAgB,eAAe,MAAM,cAAc,UAAW;AACxE,UAAI,MAAM,SAAS,wBAAwB;AACzC,cAAM,UAAU,MAAM;AACtB,YAAI,SAAS,OAAO;AAClB,eAAK,IAAI,QAAQ,OAAO,EAAE,eAAe,KAAK,CAAC;AAC/C,uBAAa,QAAQ;AAAA,QACvB;AACA;AAAA,MACF;AACA,UAAI,MAAM,SAAS,uBAAuB;AACxC,cAAM,UAAU,MAAM;AACtB,YAAI,SAAS,OAAO;AAClB,eAAK,OAAO,QAAQ,KAAK;AACzB,cAAI,eAAe,QAAQ,MAAO,cAAa;AAAA,QACjD;AACA;AAAA,MACF;AACA,UAAI,MAAM,SAAS,6BAA6B;AAC9C,cAAM,UAAU,MAAM;AACtB,YAAI,CAAC,WAAY,aAAa,QAAQ,UAAU,UAAY;AAC5D,YAAI,KAAK;AAAA,UACP,IAAI,MAAM;AAAA,UACV,MAAM,QAAQ;AAAA,UACd,QAAQ,QAAQ,WAAW,WAAW,WAAW;AAAA,UACjD,KAAK,MAAM;AAAA,QACb,CAAC;AACD;AAAA,MACF;AACA,UAAI,wBAAwB,CAAC,aAAa,MAAM,SAAS,gCAAgC;AACvF,cAAM,UAAU,MAAM;AACtB,YAAI,CAAC,SAAS,MAAO;AACrB,YAAI,KAAK;AAAA,UACP,IAAI,MAAM;AAAA,UACV,MAAM,QAAQ;AAAA,UACd,QAAQ,QAAQ,WAAW,WAAW,WAAW;AAAA,UACjD,KAAK,MAAM;AAAA,QACb,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG;AAChC,UAAM,YAAY,aAAa,KAAK,IAAI,SAAS,IAAI,YAAY;AACjE,wBAAoB,YAAa,KAAK,IAAI,SAAS,GAAG,iBAAiB,QAAS;AAChF,WAAO,EAAE,QAAQ,KAAK,SAAS,WAAW,eAAe,kBAAkB;AAAA,EAC7E,GAAG,CAAC,QAAQ,QAAQ,WAAW,sBAAsB,aAAa,SAAS,CAAC;AAM5E,QAAM,kBAAkB,kBAAkB;AAC1C,QAAM,cAAc,kBAAmB,eAAe,UAAW;AAGjE,QAAM,WAAW,gBAAgB,QAAQ;AAEzC,QAAM,QAAQA,SAAQ,MAAM;AAC1B,QAAI,CAAC,eAAe,CAAC,YAAY,CAAC,UAAW,QAAO;AACpD,UAAM,gBAAgB;AACtB,WAAO,CAAC,SAAiB;AACvB,WAAK,OACF,iBAAiB,aAAa,WAAW,EAAE,OAAO,aAAa,KAAK,CAAC,EACrE,MAAM,CAAC,UAAU;AAIhB,YACE,iBAAiBC,sBAChB,MAAM,WAAW,OAAO,MAAM,WAAW,QAC1C,gBAAgB,eAChB,mBAAmB,YAAY,eAC/B;AACA,yBAAe,IAAI;AACnB,yBAAe,CAAC,MAAM,IAAI,CAAC;AAAA,QAC7B;AAAA,MACF,CAAC;AAAA,IACL;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,aAAa,UAAU,aAAa,WAAW,CAAC;AAEpF,QAAM,QAAQC,cAAY,MAAM;AAC9B,QAAI,CAAC,eAAe,CAAC,UAAW;AAChC,SAAK,OAAO,iBAAiB,aAAa,WAAW,EAAE,OAAO,YAAY,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EAC7F,GAAG,CAAC,QAAQ,aAAa,WAAW,WAAW,CAAC;AAEhD,SAAO;AAAA,IACL;AAAA,IACA,SAAS,gBAAgB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO,kBAAkB,YAAY;AAAA,EACvC;AACF;;;AChPA,SAAS,eAAAC,eAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAWlD,IAAM,+BAAN,cAA2C,MAAM;AAAA,EAGtD,YACW,MACA,QACT;AACA;AAAA,MACE,WAAW,cACP,GAAG,IAAI,yCACP,WAAW,oBACT,GAAG,IAAI,6CACP,GAAG,IAAI;AAAA,IACf;AATS;AACA;AAST,SAAK,OAAO;AAAA,EACd;AAAA,EAdS,OAAO;AAelB;AAEO,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAGhD,YACW,MACA,iBACA,aACT;AACA,UAAM,GAAG,IAAI,8EAA8E;AAJlF;AACA;AACA;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EATS,OAAO;AAUlB;AAwFA,SAAS,SAAS,MAAsB;AACtC,QAAM,IAAI,KAAK,YAAY,GAAG;AAC9B,SAAO,KAAK,IAAI,KAAK,KAAK,MAAM,GAAG,CAAC;AACtC;AAGA,SAAS,OAAO,MAAsB;AACpC,QAAM,IAAI,KAAK,YAAY,GAAG;AAC9B,SAAO,IAAI,IAAI,OAAO,KAAK,MAAM,IAAI,CAAC;AACxC;AAMA,SAAS,UAAU,OAAuC;AACxD,SAAO,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM;AAC/B,QAAI,EAAE,SAAS,EAAE,KAAM,QAAO,EAAE,SAAS,QAAQ,KAAK;AACtD,WAAO,EAAE,KAAK,cAAc,EAAE,MAAM,QAAW,EAAE,aAAa,OAAO,CAAC;AAAA,EACxE,CAAC;AACH;AAEA,SAAS,aAAa,MAAkB,kBAAkB,OAAqB;AAC7E,QAAM,OAAO,KAAK,SAAS,QAAQ,QAAQ;AAU3C,QAAM,iBACJ,KAAK,YAAY,KAAK,SAAS,SAAS,IACpC,KAAK,SAAS,IAAI,CAAC,UAAU,aAAa,OAAO,eAAe,CAAC,IACjE,mBAAmB,SAAS,QAC1B,CAAC,IACD;AACR,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,MAAM,KAAK;AAAA,IACX;AAAA,IACA,MAAM,KAAK;AAAA,IACX,GAAI,SAAS,QAAQ,EAAE,UAAU,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,IAGrD,GAAI,SAAS,SAAS,KAAK,YAAY,EAAE,WAAW,KAAK,IAAI,CAAC;AAAA,EAChE;AACF;AAEA,IAAM,qBAAyE;AAAA,EAC7E,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,YAAY;AACd;AAGA,SAAS,gBACP,OACA,YACA,UACgB;AAChB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,YAAY;AAC5B,aAAO,EAAE,GAAG,MAAM,SAAS;AAAA,IAC7B;AACA,QAAI,KAAK,SAAS,SAAS,KAAK,YAAY,WAAW,WAAW,GAAG,KAAK,IAAI,GAAG,GAAG;AAClF,aAAO,EAAE,GAAG,MAAM,UAAU,gBAAgB,KAAK,UAAU,YAAY,QAAQ,EAAE;AAAA,IACnF;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAGA,SAAS,eAAe,OAAuB,MAAwC;AACrF,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,KAAM,QAAO;AAC/B,QAAI,KAAK,SAAS,SAAS,KAAK,YAAY,KAAK,WAAW,GAAG,KAAK,IAAI,GAAG,GAAG;AAC5E,YAAM,MAAM,eAAe,KAAK,UAAU,IAAI;AAC9C,UAAI,IAAK,QAAO;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AAiBA,SAAS,eAAe,OAAuB,QAAyB;AACtE,MAAI,WAAW,GAAI,QAAO;AAC1B,QAAM,OAAO,eAAe,OAAO,MAAM;AACzC,SAAO,QAAQ,QAAQ,KAAK,SAAS,SAAS,KAAK,aAAa,MAAS;AAC3E;AAKA,SAAS,WAAW,OAAuB,QAAgB,OAAqC;AAC9F,MAAI,WAAW,IAAI;AACjB,QAAI,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI,EAAG,QAAO;AACrD,WAAO,UAAU,CAAC,GAAG,OAAO,KAAK,CAAC;AAAA,EACpC;AACA,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,QAAQ;AACxB,UAAI,KAAK,SAAS,SAAS,KAAK,aAAa,OAAW,QAAO;AAC/D,UAAI,KAAK,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM,IAAI,EAAG,QAAO;AAC7D,aAAO,EAAE,GAAG,MAAM,UAAU,UAAU,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC,EAAE;AAAA,IACnE;AACA,QAAI,KAAK,SAAS,SAAS,KAAK,YAAY,OAAO,WAAW,GAAG,KAAK,IAAI,GAAG,GAAG;AAC9E,aAAO,EAAE,GAAG,MAAM,UAAU,WAAW,KAAK,UAAU,QAAQ,KAAK,EAAE;AAAA,IACvE;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAGA,SAAS,WAAW,OAAuB,MAA8B;AACvE,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,WAAW,IAAI;AACjB,QAAI,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,EAAG,QAAO;AAChD,WAAO,MAAM,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI;AAAA,EAC5C;AACA,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,QAAI,KAAK,SAAS,QAAQ;AACxB,UAAI,KAAK,SAAS,SAAS,KAAK,aAAa,OAAW,QAAO;AAC/D,aAAO,EAAE,GAAG,MAAM,UAAU,KAAK,SAAS,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE;AAAA,IAC3E;AACA,QAAI,KAAK,SAAS,SAAS,KAAK,YAAY,OAAO,WAAW,GAAG,KAAK,IAAI,GAAG,GAAG;AAC9E,aAAO,EAAE,GAAG,MAAM,UAAU,WAAW,KAAK,UAAU,IAAI,EAAE;AAAA,IAC9D;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAUA,SAAS,gBAAgB,GAAiB,GAA0B;AAClE,SACE,EAAE,SAAS,EAAE,QACb,EAAE,SAAS,EAAE,QACb,EAAE,SAAS,EAAE,SACZ,EAAE,QAAQ,WAAW,EAAE,QAAQ,UAC/B,EAAE,UAAU,aAAgB,EAAE,UAAU,YACxC,EAAE,aAAa,YAAY,EAAE,aAAa;AAE/C;AAEA,SAAS,cAAc,SAAyB,QAAwC;AACtF,QAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAU,CAAC;AAC/D,QAAM,SAAS,OAAO,IAAI,CAAC,SAAS;AAClC,UAAM,WAAW,OAAO,IAAI,KAAK,IAAI;AACrC,QAAI,CAAC,SAAU,QAAO;AAGtB,QAAI,SAAS,SAAS,SAAS,KAAK,SAAS,SAAS,SAAS,aAAa,QAAW;AACrF,YAAM,YAA0B,EAAE,GAAG,MAAM,UAAU,SAAS,SAAS;AAEvE,aAAO,gBAAgB,UAAU,SAAS,IAAI,WAAW;AAAA,IAC3D;AAEA,QAAI,SAAS,aAAa,KAAK,YAAY,gBAAgB,UAAU,IAAI,GAAG;AAC1E,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,CAAC;AACD,SAAO,UAAU,MAAM;AACzB;AAIA,SAAS,kBAAkB,OAAuB,QAAwC;AACxF,SAAO,cAAc,OAAO,MAAM;AACpC;AAIA,SAAS,WAAW,MAAoB,YAAoB,UAAgC;AAC1F,QAAM,UAAU,WAAW,KAAK,KAAK,MAAM,WAAW,MAAM;AAC5D,QAAM,OAAqB,EAAE,GAAG,MAAM,MAAM,SAAS,MAAM,OAAO,OAAO,EAAE;AAC3E,MAAI,KAAK,SAAU,MAAK,WAAW,KAAK,SAAS,IAAI,CAAC,MAAM,WAAW,GAAG,YAAY,QAAQ,CAAC;AAC/F,SAAO;AACT;AAEA,SAAS,kBAAkB,OAA2B;AACpD,QAAM,SAAS,KAAK,KAAK;AACzB,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;AACrD,UAAM,KAAK,IAAI,OAAO,WAAW,KAAK;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,OAAuB;AAC/C,SAAO,IAAI,YAAY,EAAE,OAAO,kBAAkB,KAAK,CAAC;AAC1D;AAEA,SAAS,kBAAkB,OAA2B;AACpD,MAAI,SAAS;AACb,QAAM,YAAY;AAClB,WAAS,SAAS,GAAG,SAAS,MAAM,QAAQ,UAAU,WAAW;AAC/D,cAAU,OAAO,aAAa,GAAG,MAAM,SAAS,QAAQ,SAAS,SAAS,CAAC;AAAA,EAC7E;AACA,SAAO,KAAK,MAAM;AACpB;AAEA,eAAe,mBAAmB,OAAmB,UAAwC;AAC3F,MAAI,CAAC,SAAU,OAAM,IAAI,MAAM,8CAA8C;AAC7E,MAAI,CAAC,WAAW,QAAQ,QAAQ;AAC9B,UAAM,IAAI,MAAM,sEAAsE;AAAA,EACxF;AACA,QAAM,SAAS,IAAI,YAAY,MAAM,UAAU;AAC/C,MAAI,WAAW,MAAM,EAAE,IAAI,KAAK;AAChC,QAAM,SAAS,IAAI,WAAW,MAAM,WAAW,OAAO,OAAO,OAAO,WAAW,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACpF,MAAI,WAAW,SAAU,OAAM,IAAI,MAAM,mDAAmD;AAC9F;AASO,SAAS,gBACd,WACA,UAAkC,CAAC,GACZ;AACvB,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC9D,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAMC,UAAS,QAAQ,aAAa,UAAU,QAAQ,aAAa;AACnE,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE,KAAS,QAAQ;AAE3E,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAyB,CAAC,CAAC;AAKnD,QAAM,UAAUC,QAAuB,CAAC,CAAC;AACzC,QAAM,CAAC,SAAS,UAAU,IAAID,UAAS,KAAK;AAC5C,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAsB,oBAAI,IAAI,CAAC;AAC3E,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,WAAW;AAE9D,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAoC,IAAI;AACpE,QAAM,YAAYC,QAAkC,IAAI;AACxD,QAAM,YAAYA,QAAoC,oBAAI,IAAI,CAAC;AAG/D,QAAM,uBAAuBA,QAAO,CAAC;AACrC,QAAM,kBAAkBA,QAA+B,IAAI;AAC3D,QAAM,mBAAmBA,QAAO,IAAI,gBAAgB,CAAC;AACrD,QAAM,wBAAwBA,QAAO,CAAC;AACtC,QAAM,aAAaA,QAAO,CAAC;AAC3B,QAAM,oBAAoBA,QAAoB,oBAAI,IAAI,CAAC;AACvD,QAAM,gBAAgBA,QAAO,KAAK;AAClC,QAAM,cAAcA,QAA6C,IAAI;AAUrE,QAAM,kBAAkBA,QAAoB,oBAAI,IAAI,CAAC;AACrD,QAAM,kBAAkB,QAAQ;AAGhC,UAAQ,UAAU;AAOlB,QAAM,cAAcC,cAAY,CAAC,UAA0C;AACzE,UAAM,UAAU,UAAU;AAC1B,UAAM,OAAO,CAAC,SAAyC;AACrD,UAAI,UAAU;AACd,YAAM,MAAM,KAAK,IAAI,CAAC,SAAS;AAC7B,cAAM,eAAe,KAAK,WAAW,KAAK,KAAK,QAAQ,IAAI,KAAK;AAChE,cAAM,aAAa,KAAK,SAAS,SAAS,QAAQ,IAAI,KAAK,IAAI,IAAI;AACnE,cAAM,kBAAkB,iBAAiB,KAAK;AAC9C,cAAM,iBAAiB,KAAK,UAAU,aAAgB,cAAc;AACpE,YAAI,CAAC,mBAAmB,CAAC,cAAe,QAAO;AAC/C,kBAAU;AACV,cAAM,OAAqB;AAAA,UACzB,GAAG;AAAA,UACH,GAAI,KAAK,WAAW,EAAE,UAAU,aAAa,IAAI,CAAC;AAAA,QACpD;AACA,YAAI,WAAY,MAAK,SAAS;AAAA,YACzB,QAAO,KAAK;AACjB,eAAO;AAAA,MACT,CAAC;AACD,aAAO,UAAU,MAAM;AAAA,IACzB;AACA,WAAO,KAAK,KAAK;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,QAAM,UAAUA,cAAY,YAAY;AACtC,QAAI,CAAC,UAAW;AAChB,oBAAgB,SAAS,MAAM;AAC/B,UAAM,eAAe,IAAI,gBAAgB;AACzC,oBAAgB,UAAU;AAC1B,UAAM,aAAc,qBAAqB,WAAW;AACpD,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AAGF,UAAI;AACF,cAAM,SAAS,MAAM,OAAO;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,EAAE,MAAM,SAAS;AAAA,UACjB,EAAE,QAAQ,aAAa,OAAO;AAAA,QAChC;AACA,YAAI,qBAAqB,YAAY,WAAY;AACjD,cAAM,UAAU,oBAAI,IAA4B;AAChD,mBAAW,QAAQ,OAAO,OAAO;AAC/B,gBAAM,OAAO,KAAK,YAAY,KAAK;AACnC,gBAAM,SAAS,OAAO,mBAAmB,IAAI,IAAI;AACjD,cAAI,OAAQ,SAAQ,IAAI,KAAK,MAAM,MAAM;AAAA,QAC3C;AACA,kBAAU,UAAU;AAAA,MACtB,QAAQ;AACN,YAAI,qBAAqB,YAAY,WAAY;AACjD,kBAAU,UAAU,oBAAI,IAAI;AAAA,MAC9B;AACA,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,EAAE,MAAM,UAAU,OAAO,EAAE;AAAA,QAC3B,EAAE,QAAQ,aAAa,OAAO;AAAA,MAChC;AACA,UAAI,qBAAqB,YAAY,WAAY;AACjD,YAAM,YAAY,OAAO,KAAK,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,aAAa,IAAI,CAAC;AAK9E;AAAA,QAAQ,CAAC,SACP,YAAY,KAAK,WAAW,IAAI,WAAW,kBAAkB,MAAM,QAAQ,CAAC;AAAA,MAC9E;AAEA,gBAAU,UAAU;AACpB,gBAAU,MAAM;AAAA,IAClB,SAAS,OAAO;AACd,UAAI,qBAAqB,YAAY,WAAY;AACjD,eAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACpE,UAAE;AACA,UAAI,qBAAqB,YAAY,WAAY,YAAW,KAAK;AACjE,UAAI,gBAAgB,YAAY,aAAc,iBAAgB,UAAU;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,UAAU,WAAW,CAAC;AAO1D,QAAM,kBAAkBA;AAAA,IACtB,CAAC,aAAuC;AACtC,YAAM,UAAU,oBAAI,IAA4B;AAChD,iBAAW,QAAQ,SAAS,OAAO;AACjC,YAAI,KAAK,QAAS;AAClB,cAAM,SAAS,mBAAmB,KAAK,MAAM;AAC7C,YAAI,OAAQ,SAAQ,IAAI,KAAK,MAAM,MAAM;AAAA,MAC3C;AACA,gBAAU,UAAU;AAIpB,YAAM,YAAY,SAAS,UAAU,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,aAAa,MAAM,IAAI,CAAC;AAC3F;AAAA,QAAQ,CAAC,SACP,YAAY,KAAK,WAAW,IAAI,WAAW,kBAAkB,MAAM,QAAQ,CAAC;AAAA,MAC9E;AACA,gBAAU,UAAU;AACpB,gBAAU,SAAS;AACnB,eAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,SAAiB;AACtB,UAAI,CAAC,UAAW;AAIhB,UAAI,WAAW,aAAa,QAAS;AACrC,YAAM,qBAAqB,sBAAsB;AACjD,YAAM,iBAAiB,iBAAiB,QAAQ;AAIhD,wBAAkB,CAAC,SAAS;AAC1B,cAAM,OAAO,IAAI,IAAI,IAAI;AACzB,aAAK,IAAI,IAAI;AACb,eAAO;AAAA,MACT,CAAC;AACD,UAAI;AACF,cAAM,SAAS,MAAM,OAAO;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,EAAE,MAAM,OAAO,EAAE;AAAA,UACjB,EAAE,QAAQ,eAAe;AAAA,QAC3B;AACA,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,cAAM,YAAY,OAAO,KAAK,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,aAAa,IAAI,CAAC;AAC9E,gBAAQ,CAAC,SAAS,YAAY,gBAAgB,MAAM,MAAM,QAAQ,CAAC,CAAC;AAAA,MACtE,SAAS,OAAO;AACd,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,iBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MACpE,UAAE;AACA,YAAI,sBAAsB,YAAY,oBAAoB;AACxD,4BAAkB,CAAC,SAAS;AAC1B,gBAAI,CAAC,KAAK,IAAI,IAAI,EAAG,QAAO;AAC5B,kBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,iBAAK,OAAO,IAAI;AAChB,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,SAAS,QAAQ,WAAW;AAAA,EAC/D;AAEA,QAAM,WAAWA;AAAA,IACf,OAAO,MAAc,iBAAyC,CAAC,MAAM;AACnE,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM,iBAAiB,iBAAiB,QAAQ;AAChD,YAAM,SAAS,eAAe,SAC1B,YAAY,IAAI,CAAC,gBAAgB,eAAe,MAAM,CAAC,IACvD;AACJ,YAAM,WAAW,SAAS,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,QAAQ,CAAC,KAAK,OAAO;AAClF,UAAI,WAAW,aAAa,SAAS;AACnC,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI,6BAA6B,MAAM,cAAc;AAAA,QAC7D;AAKA,iBAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,gBAAM,SAAS,MAAM,OAAO;AAAA,YAC1B;AAAA,YACA;AAAA,YACA;AAAA,YACA,QAAQ;AAAA,YACR,EAAE,OAAO;AAAA,UACX;AACA,cAAI,OAAO,SAAS,QAAQ,OAAO,aAAa,QAAQ,UAAU;AAChE,kBAAM,IAAI;AAAA,cACR;AAAA,YACF;AAAA,UACF;AACA,cACE,OAAO,SAAS,SAAS,QACzB,OAAO,cAAc,SAAS,aAC9B,OAAO,aAAa,SAAS,YAC7B,OAAO,aAAa,SAAS,UAC7B;AACA,kBAAM,IAAI,MAAM,oDAAoD;AAAA,UACtE;AACA,cAAI,OAAO,UAAU;AACnB,kBAAM,IAAI,6BAA6B,MAAM,WAAW;AAAA,UAC1D;AAEA,cAAI;AACJ,cAAI;AACJ,cAAI;AACJ,cAAI,OAAO,YAAY,QAAQ,OAAO,aAAa,MAAM;AACvD,uBAAW,OAAO;AAClB,sBAAU,OAAO;AACjB,oBACE,aAAa,WACT,kBAAkB,OAAO,IACzB,IAAI,YAAY,EAAE,OAAO,OAAO;AAAA,UACxC,WAAW,OAAO,YAAY;AAC5B,kBAAM,WAAW,MAAM,MAAM,OAAO,WAAW,KAAK;AAAA,cAClD,aAAa;AAAA,cACb,OAAO;AAAA,cACP,gBAAgB;AAAA,cAChB,QAAQ,YAAY,IAAI,CAAC,QAAQ,YAAY,QAAQ,GAAM,CAAC,CAAC;AAAA,YAC/D,CAAC,EAAE,MAAM,MAAM,IAAI;AACnB,gBAAI,CAAC,UAAU,IAAI;AACjB,kBACE,YAAY,MACX,aAAa,QAAQ,SAAS,WAAW,OAAO,SAAS,WAAW,MACrE;AACA;AAAA,cACF;AACA,oBAAM,IAAI,MAAM,gCAAgC;AAAA,YAClD;AACA,oBAAQ,IAAI,WAAW,MAAM,SAAS,YAAY,CAAC;AACnD,uBAAW,OAAO,WAAW,WAAW;AACxC,sBAAU,OAAO,WAAW,kBAAkB,KAAK,IAAI,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,UACvF,OAAO;AACL,kBAAM,IAAI,6BAA6B,MAAM,iBAAiB;AAAA,UAChE;AAEA,cAAI,MAAM,eAAe,OAAO,WAAW;AACzC,kBAAM,IAAI,MAAM,gDAAgD;AAAA,UAClE;AACA,gBAAM,mBAAmB,OAAO,OAAO,IAAI;AAC3C,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA,WAAW,OAAO;AAAA,YAClB,WAAW;AAAA,YACX,UAAU,OAAO;AAAA,YACjB,UAAU,OAAO;AAAA,UACnB;AAAA,QACF;AACA,cAAM,IAAI,MAAM,yDAAyD;AAAA,MAC3E;AACA,aAAO,MAAM,OAAO,OAAO,aAAa,WAAW,EAAE,KAAK,GAAG,EAAE,OAAO,CAAC;AAAA,IACzE;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,SAAS,MAAM;AAAA,EAClD;AASA,QAAM,gBAAgBA;AAAA,IACpB,OAAO,SAAiB;AACtB,UAAI,CAAC,UAAW;AAChB,YAAM,qBAAqB,sBAAsB;AACjD,YAAM,iBAAiB,iBAAiB,QAAQ;AAGhD,UAAI,CAAC,eAAe,QAAQ,SAAS,IAAI,EAAG;AAC5C,UAAI;AACF,cAAM,SAAS,MAAM,OAAO;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,EAAE,MAAM,OAAO,EAAE;AAAA,UACjB,EAAE,QAAQ,eAAe;AAAA,QAC3B;AACA,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,cAAM,YAAY,OAAO,KAAK,YAAY,CAAC,GAAG,IAAI,CAAC,SAAS,aAAa,IAAI,CAAC;AAC9E,YAAI,SAAS,GAAI,SAAQ,CAAC,SAAS,YAAY,kBAAkB,MAAM,QAAQ,CAAC,CAAC;AAAA;AAE/E,kBAAQ,CAAC,SAAS;AAChB,kBAAM,WAAW,eAAe,MAAM,IAAI;AAC1C,kBAAM,SAAS,UAAU,WACrB,cAAc,SAAS,UAAU,QAAQ,IACzC;AACJ,mBAAO,YAAY,gBAAgB,MAAM,MAAM,MAAM,CAAC;AAAA,UACxD,CAAC;AAAA,MACL,QAAQ;AAAA,MAGR;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,WAAW;AAAA,EAC9C;AASA,QAAM,gBAAgBA;AAAA,IACpB,OACE,QACA,OACA,IACA,qBACe;AACf,YAAM,qBAAqB,sBAAsB;AAEjD,YAAM,WAAW,QAAQ;AACzB,eAAS,IAAI;AACb,cAAQ,YAAY,MAAM,QAAQ,CAAC,CAAC;AACpC,UAAI;AACF,cAAM,MAAM,MAAM,GAAG;AACrB,YAAI,sBAAsB,YAAY,mBAAoB,QAAO;AAGjE,YAAI,OAAO,OAAO,QAAQ,YAAY,cAAc,KAAK;AACvD,gBAAM,MAAO,IAA+B;AAC5C,cAAI,OAAO,QAAQ,SAAU,iBAAgB,QAAQ,IAAI,GAAG;AAAA,QAC9D;AAGA,mBAAW,UAAU,kBAAkB;AACrC,cAAI,sBAAsB,YAAY,mBAAoB,QAAO;AACjE,gBAAM,cAAc,MAAM;AAAA,QAC5B;AACA,eAAO;AAAA,MACT,SAAS,OAAO;AACd,cAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,YAAI,sBAAsB,YAAY,mBAAoB,OAAM;AAEhE,gBAAQ,YAAY,QAAQ,CAAC;AAK7B,YAAI,eAAe,uBAAwB,OAAM;AACjD,iBAAS,GAAG;AACZ,0BAAkB,KAAK,MAAM;AAC7B,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,aAAa,eAAe,eAAe;AAAA,EAC9C;AAEA,QAAM,YAAYA;AAAA,IAChB,OACE,MACA,SACA,eAAwC,CAAC,MACZ;AAC7B,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM,qBAAqB,sBAAsB;AACjD,YAAM,iBAAiB,iBAAiB,QAAQ;AAChD,YAAM,SAAS,SAAS,IAAI;AAI5B,YAAM,SAAS,QAAQ,eAAe,MAAM,IAAI,CAAC;AACjD,YAAM,OAAqB,EAAE,MAAM,MAAM,OAAO,IAAI,GAAG,MAAM,QAAQ,MAAM,QAAQ,OAAO;AAC1F,aAAO,MAAM;AAAA,QACX;AAAA,QACA,CAAC,UAAW,SAAS,QAAQ,WAAW,OAAO,QAAQ,IAAI;AAAA,QAC3D,YAAY;AACV,cAAI,CAAC,aAAa,SAAS,aAAa,oBAAoB,QAAW;AACrE,kBAAM,OAAO,MAAM,OAAO;AAAA,cACxB;AAAA,cACA;AAAA,cACA,EAAE,KAAK;AAAA,cACP,EAAE,QAAQ,eAAe;AAAA,YAC3B;AACA,gBAAI,sBAAsB,YAAY,oBAAoB;AACxD,oBAAM,IAAI,MAAM,oDAAoD;AAAA,YACtE;AACA,kBAAM,cACJ,KAAK,aAAa,WAAW,iBAAiB,KAAK,OAAO,IAAI,KAAK;AACrE,gBAAI,KAAK,aAAa,KAAK,YAAY,gBAAgB,aAAa,iBAAiB;AACnF,oBAAM,IAAI,uBAAuB,MAAM,aAAa,iBAAiB,WAAW;AAAA,YAClF;AAAA,UACF;AACA,cAAI,sBAAsB,YAAY,oBAAoB;AACxD,kBAAM,IAAI,MAAM,oDAAoD;AAAA,UACtE;AACA,iBAAO,MAAM,OAAO,QAAQ,aAAa,WAAW;AAAA,YAClD;AAAA,YACA;AAAA,YACA,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AAAA,QACA,SAAS,CAAC,IAAI,CAAC,MAAM;AAAA,MACvB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,MAAM,aAAa;AAAA,EACtD;AAEA,QAAM,aAAaA;AAAA,IACjB,OAAO,SAAgC;AACrC,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM,SAAS,SAAS,IAAI;AAC5B,YAAM,OAAqB,EAAE,MAAM,MAAM,OAAO,IAAI,GAAG,MAAM,QAAQ,MAAM,EAAE;AAC7E,YAAM;AAAA,QACJ;AAAA,QACA,CAAC,UAAU,WAAW,OAAO,QAAQ,IAAI;AAAA,QACzC,MAAM,OAAO,QAAQ,aAAa,WAAW,EAAE,MAAM,SAAS,IAAI,WAAW,MAAM,CAAC;AAAA,QACpF,CAAC,MAAM;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,aAAa;AAAA,EAChD;AAEA,QAAM,YAAYA;AAAA,IAChB,OAAO,SAAgC;AACrC,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM,SAAS,SAAS,IAAI;AAG5B,YAAM,OAAqB,EAAE,MAAM,MAAM,OAAO,IAAI,GAAG,MAAM,OAAO,UAAU,CAAC,EAAE;AACjF,YAAM;AAAA,QACJ;AAAA,QACA,CAAC,UAAU,WAAW,OAAO,QAAQ,IAAI;AAAA,QACzC,MAAM,OAAO,QAAQ,aAAa,WAAW,EAAE,MAAM,WAAW,KAAK,CAAC;AAAA,QACtE,CAAC,MAAM;AAAA,MACT;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,aAAa;AAAA,EAChD;AAEA,QAAM,cAAcA;AAAA,IAClB,OAAO,MAAc,YAAY,UAAyB;AACxD,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM;AAAA,QACJ;AAAA,QACA,CAAC,UAAU,WAAW,OAAO,IAAI;AAAA,QACjC,MAAM,OAAO,SAAS,aAAa,WAAW,EAAE,MAAM,UAAU,CAAC;AAAA,QACjE,CAAC,SAAS,IAAI,CAAC;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,aAAa;AAAA,EAChD;AAEA,QAAM,YAAYA;AAAA,IAChB,OAAO,MAAc,SAAiB,SAAkD;AACtF,UAAI,CAAC,UAAW,OAAM,IAAI,MAAM,YAAY;AAC5C,YAAM,OAAO,SAAS,IAAI;AAC1B,YAAM,KAAK,SAAS,OAAO;AAC3B,YAAM;AAAA,QACJ;AAAA,QACA,CAAC,UAAU;AACT,gBAAM,SAAS,eAAe,OAAO,IAAI;AACzC,cAAI,CAAC,OAAQ,QAAO;AAEpB,gBAAM,QAAQ,WAAW,QAAQ,MAAM,OAAO;AAC9C,iBAAO,WAAW,WAAW,OAAO,IAAI,GAAG,IAAI,KAAK;AAAA,QACtD;AAAA,QACA,MACE,OAAO,OAAO,aAAa,WAAW;AAAA,UACpC;AAAA,UACA;AAAA,UACA,WAAW,MAAM,aAAa;AAAA,QAChC,CAAC;AAAA,QACH,OAAO,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE;AAAA,MAClC;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,aAAa;AAAA,EAChD;AAUA,QAAM,aAAaD,QAAwC,OAAO;AAClE,aAAW,UAAU;AACrB,QAAM,kBAAkB,SAAS,YAAY;AAC7C,QAAM,sBAAsBA,QAAO,WAAW;AAC9C,EAAAE,WAAU,MAAM;AAId,oBAAgB,SAAS,MAAM;AAC/B,oBAAgB,UAAU;AAC1B,yBAAqB,WAAW;AAChC,UAAM,kBAAkB,oBAAoB,YAAY;AACxD,wBAAoB,UAAU;AAC9B,QAAI,mBAAmB,CAAC,SAAS;AAC/B,uBAAiB,QAAQ,MAAM;AAC/B,uBAAiB,UAAU,IAAI,gBAAgB;AAC/C,4BAAsB,WAAW;AACjC,iBAAW,UAAU;AACrB,wBAAkB,UAAU,oBAAI,IAAI;AACpC,oBAAc,UAAU;AACxB,UAAI,YAAY,SAAS;AACvB,qBAAa,YAAY,OAAO;AAChC,oBAAY,UAAU;AAAA,MACxB;AACA,sBAAgB,UAAU,oBAAI,IAAI;AAClC,gBAAU,UAAU,oBAAI,IAAI;AAC5B,cAAQ,UAAU,CAAC;AACnB,cAAQ,CAAC,CAAC;AACV,uBAAiB,WAAW;AAC5B,wBAAkB,oBAAI,IAAI,CAAC;AAC3B,gBAAU,UAAU;AACpB,gBAAU,IAAI;AACd,iBAAW,KAAK;AAChB,eAAS,IAAI;AAAA,IACf;AACA,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,UAAM,iBAAiB,oBAAoB,OAAO,WAAW,UAAU;AACvE,QAAI,mBAAmB,mBAAmB,CAACJ,WAAU,UAAU,YAAY,SAAS;AAClF,sBAAgB,cAAc;AAAA,IAChC;AACA,QAAIA,WAAU,CAAC,gBAAgB;AAC7B,WAAK,QAAQ;AAAA,IACf;AACA,WAAO,MAAM;AACX,sBAAgB,SAAS,MAAM;AAC/B,sBAAgB,UAAU;AAC1B,2BAAqB,WAAW;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,SAASA,SAAQ,iBAAiB,aAAa,SAAS,eAAe,CAAC;AAK5E,QAAM,oBAAoBG,cAAY,YAAY;AAChD,QAAI,CAAC,UAAW;AAChB,UAAM,qBAAqB,sBAAsB;AACjD,UAAM,iBAAiB,iBAAiB,QAAQ;AAChD,QAAI;AACF,YAAM,SAAS,MAAM,OAAO;AAAA,QAC1B;AAAA,QACA;AAAA,QACA,EAAE,MAAM,SAAS;AAAA,QACjB,EAAE,QAAQ,eAAe;AAAA,MAC3B;AACA,UAAI,sBAAsB,YAAY,mBAAoB;AAC1D,YAAM,UAAU,oBAAI,IAA4B;AAChD,iBAAW,QAAQ,OAAO,OAAO;AAC/B,cAAM,OAAO,KAAK,YAAY,KAAK;AACnC,cAAM,SAAS,OAAO,mBAAmB,IAAI,IAAI;AACjD,YAAI,OAAQ,SAAQ,IAAI,KAAK,MAAM,MAAM;AAAA,MAC3C;AACA,gBAAU,UAAU;AACpB,cAAQ,CAAC,SAAS,YAAY,IAAI,CAAC;AAAA,IACrC,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,UAAU,WAAW,CAAC;AAY1D,QAAM,SAAS,QAAQ;AAEvB,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,OAAQ;AACzB,QAAI,SAAS;AACb,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,YAAY,WAAW,QAAS;AAC1C,UAAI,MAAM,SAAS,cAAc;AAC/B,iBAAS;AACT,cAAM,UAAU,MAAM;AACtB,YAAI,CAAC,WAAW,OAAO,YAAY,SAAU;AAE7C,YAAI,QAAQ,WAAW,QAAS;AAChC,YAAI,OAAO,QAAQ,aAAa,YAAY,gBAAgB,QAAQ,IAAI,QAAQ,QAAQ;AACtF;AACF,mBAAW,UAAU,QAAQ,WAAW,CAAC,GAAG;AAC1C,4BAAkB,QAAQ,IAAI,SAAS,OAAO,IAAI,CAAC;AACnD,cAAI,OAAO,QAAS,mBAAkB,QAAQ,IAAI,SAAS,OAAO,OAAO,CAAC;AAAA,QAC5E;AAAA,MACF,WAAW,MAAM,SAAS,eAAe;AACvC,iBAAS;AACT,cAAM,UAAU,MAAM;AAItB,YACE,WACA,OAAO,YAAY,YACnB,OAAO,QAAQ,aAAa,YAC5B,gBAAgB,QAAQ,IAAI,QAAQ,QAAQ;AAE5C;AACF,sBAAc,UAAU;AAAA,MAC1B;AAAA,IACF;AAEA,eAAW,SAAS;AAClB,UAAI,MAAM,WAAW,WAAW,QAAS,YAAW,UAAU,MAAM;AACtE,QAAI,CAAC,OAAQ;AAEb,QAAI,YAAY,QAAS,cAAa,YAAY,OAAO;AACzD,UAAM,qBAAqB,sBAAsB;AACjD,gBAAY,UAAU,WAAW,MAAM;AACrC,kBAAY,UAAU;AACtB,UAAI,sBAAsB,YAAY,mBAAoB;AAC1D,YAAM,UAAU,kBAAkB;AAClC,wBAAkB,UAAU,oBAAI,IAAI;AACpC,YAAM,UAAU,cAAc;AAC9B,oBAAc,UAAU;AAGxB,YAAM,YAAY;AAChB,YAAI,QAAS,OAAM,kBAAkB;AACrC,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,mBAAW,UAAU,SAAS;AAC5B,cAAI,sBAAsB,YAAY,mBAAoB;AAC1D,gBAAM,cAAc,MAAM;AAAA,QAC5B;AAAA,MACF,GAAG;AAAA,IACL,GAAG,GAAG;AAAA,EACR,GAAG,CAAC,SAAS,QAAQ,eAAe,iBAAiB,CAAC;AAEtD,EAAAA;AAAA,IACE,MAAM,MAAM;AACV,sBAAgB,SAAS,MAAM;AAC/B,uBAAiB,QAAQ,MAAM;AAC/B,4BAAsB,WAAW;AACjC,UAAI,YAAY,QAAS,cAAa,YAAY,OAAO;AAAA,IAC3D;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,kBAAkB,WAAW,kBAAkB;AACrD,SAAO;AAAA,IACL,MAAM,kBAAkB,OAAO,CAAC;AAAA,IAChC;AAAA,IACA,gBAAgB,kBAAkB,iBAAiB,oBAAI,IAAY;AAAA,IACnE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,kBAAkB,SAAS;AAAA,IACnC,YAAY,mBAAmB,WAAW,YAAa,SAAS,cAAc,OAAQ;AAAA,IACtF,SAAS,mBAAmB;AAAA,IAC5B,OAAO,kBAAkB,QAAQ;AAAA,EACnC;AACF;;;AC7jCA,SAAS,eAAAC,eAAa,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgB;AAoDlE,SAAS,cAAc,MAAsB;AAC3C,QAAM,aAAa,KAChB,WAAW,MAAM,GAAG,EACpB,QAAQ,UAAU,EAAE,EACpB,QAAQ,QAAQ,EAAE;AACrB,SAAO,eAAe,MAAM,KAAK;AACnC;AAEA,SAAS,YAAY,UAAkB,MAAsB;AAC3D,QAAM,OAAO,cAAc,QAAQ;AACnC,SAAO,OAAO,GAAG,IAAI,IAAI,IAAI,KAAK;AACpC;AAEA,SAAS,YAAY,UAAkB,MAAuC;AAC5E,QAAM,OAAO,cAAc,QAAQ;AACnC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,YAAY,MAAM,KAAK,IAAI;AAAA,IACjC,SAAS,KAAK,YAAY,OAAO,OAAO,YAAY,MAAM,KAAK,OAAO;AAAA,IACtE,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,YAAY,OAAoC;AACvD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACjF;AAGA,SAAS,YACP,UACA,UAC6B;AAC7B,QAAM,OAAO,cAAc,QAAQ;AACnC,QAAM,QAAQ,SAAS,MAAM,KAAK,CAAC,MAAM,cAAc,EAAE,IAAI,MAAM,IAAI;AACvE,MAAI,MAAO,QAAO;AAGlB,MAAI,SAAS,MAAM,SAAS,MAAM,WAAW,EAAG,QAAO,SAAS,MAAM,CAAC,KAAK;AAC5E,SAAO;AACT;AAIA,SAAS,aAAa,GAAuB,GAAgC;AAC3E,MACE,EAAE,SAAS,EAAE,QACb,EAAE,YAAY,EAAE,WAChB,EAAE,aAAa,EAAE,YACjB,EAAE,WAAW,EAAE,UACf,EAAE,aAAa,EAAE,YACjB,EAAE,YAAY,EAAE,WAChB,EAAE,cAAc,EAAE,aAClB,EAAE,cAAc,EAAE,aAClB,EAAE,cAAc,EAAE,aAClB,EAAE,MAAM,WAAW,EAAE,MAAM;AAE3B,WAAO;AAET,SAAO,EAAE,MAAM,MAAM,CAAC,MAAM,cAAc;AACxC,UAAM,QAAQ,EAAE,MAAM,SAAS;AAC/B,QACE,CAAC,SACD,KAAK,aAAa,MAAM,YACxB,KAAK,aAAa,MAAM,YACxB,KAAK,aAAa,MAAM,YACxB,KAAK,aAAa,MAAM,YACxB,KAAK,WAAW,MAAM,UACtB,KAAK,MAAM,WAAW,MAAM,MAAM;AAElC,aAAO;AACT,WAAO,KAAK,MAAM,MAAM,CAAC,MAAM,cAAc;AAC3C,YAAM,YAAY,MAAM,MAAM,SAAS;AACvC,aACE,cAAc,UACd,KAAK,SAAS,UAAU,QACxB,KAAK,UAAU,UAAU,SACzB,KAAK,UAAU,UAAU,SACzB,KAAK,SAAS,UAAU;AAAA,IAE5B,CAAC;AAAA,EACH,CAAC;AACH;AAKA,SAAS,WACP,SACA,MACsB;AACtB,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAU,CAAC;AAC/D,MAAI,UAAU,QAAQ,WAAW,KAAK;AACtC,QAAM,SAAS,KAAK,IAAI,CAAC,MAAM,UAAU;AACvC,UAAM,WAAW,OAAO,IAAI,KAAK,IAAI;AACrC,QAAI,YAAY,aAAa,UAAU,IAAI,GAAG;AAC5C,UAAI,QAAQ,KAAK,MAAM,SAAU,WAAU;AAC3C,aAAO;AAAA,IACT;AACA,cAAU;AACV,WAAO;AAAA,EACT,CAAC;AACD,SAAO,UAAU,SAAS;AAC5B;AAUO,SAAS,cACd,WACA,UAAgC,CAAC,GACZ;AACrB,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC9D,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,gBAAgB,QAAQ,cAAc;AAG5C,QAAM,eAAe;AAAA,IACnB,QAAQ,aAAa,QAAQ,UAAU,SAAS,IAAI,QAAQ,YAAY,CAAC,QAAQ;AAAA,EACnF,EAAE,KAAK,IAAQ;AACf,QAAM,YAAYC,SAAQ,MAAM,aAAa,MAAM,IAAQ,GAAG,CAAC,YAAY,CAAC;AAC5E,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAMC,UAAS,QAAQ,aAAa,UAAU,QAAQ,aAAa;AACnE,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE,KAAS,YAAY,KAAS,aAAa,KAAS,MAAM;AAEpH,QAAM,CAAC,MAAM,OAAO,IAAIC,UAA+B,CAAC,CAAC;AACzD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAwB,IAAI;AACxD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,KAAK;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAS,CAAC;AACpC,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAS,CAAC;AACtC,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAmB,CAAC,CAAC;AACvD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAoC,IAAI;AACpE,QAAM,YAAYC,QAAkC,IAAI;AACxD,QAAM,CAAC,SAAS,UAAU,IAAID,UAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,WAAW;AAG9D,QAAM,uBAAuBC,QAAO,CAAC;AACrC,QAAM,kBAAkBA,QAA+B,IAAI;AAC3D,QAAM,gBAAgBA,QAAO,CAAC;AAE9B,QAAM,UAAUC,cAAY,YAAY;AACtC,QAAI,CAAC,UAAW;AAChB,oBAAgB,SAAS,MAAM;AAC/B,UAAM,eAAe,IAAI,gBAAgB;AACzC,oBAAgB,UAAU;AAC1B,UAAM,aAAc,qBAAqB,WAAW;AACpD,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AACF,YAAM,WAAW,MAAM,QAAQ;AAAA,QAC7B,UAAU,IAAI,OAAO,UAAU;AAAA,UAC7B;AAAA,UACA,QAAQ,MAAM,OAAO;AAAA,YACnB;AAAA,YACA;AAAA,YACA,EAAE,MAAM,KAAK;AAAA,YACb,EAAE,QAAQ,aAAa,OAAO;AAAA,UAChC;AAAA,QACF,EAAE;AAAA,MACJ;AACA,UAAI,qBAAqB,YAAY,WAAY;AACjD,YAAM,eAAe,SAAS,OAAO,CAAC,EAAE,OAAO,MAAM,OAAO,MAAM;AAClE,YAAM,QAAQ,aAAa,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI;AACjD,mBAAa,KAAK;AAClB,gBAAU,aAAa,SAAS,CAAC;AACjC,gBAAU,aAAa,WAAW,IAAK,aAAa,CAAC,GAAG,OAAO,QAAQ,OAAQ,IAAI;AACnF,eAAS,aAAa,OAAO,CAAC,KAAK,EAAE,OAAO,MAAM,MAAM,OAAO,OAAO,CAAC,CAAC;AACxE,gBAAU,aAAa,OAAO,CAAC,KAAK,EAAE,OAAO,MAAM,MAAM,OAAO,QAAQ,CAAC,CAAC;AAC1E,UAAI,aAAa,WAAW,GAAG;AAC7B,gBAAQ,CAAC,CAAC;AACV,kBAAU,UAAU;AACpB,kBAAU,MAAM;AAChB;AAAA,MACF;AACA,YAAM,UAAU,MAAM,QAAQ;AAAA,QAC5B,aAAa,IAAI,OAAO,EAAE,KAAK,OAAO;AAAA,UACpC;AAAA,UACA,QAAQ,MAAM,OAAO;AAAA,YACnB;AAAA,YACA;AAAA,YACA;AAAA,cACE,MAAM;AAAA,cACN;AAAA,cACA,kBAAkB,CAAC;AAAA,YACrB;AAAA,YACA,EAAE,QAAQ,aAAa,OAAO;AAAA,UAChC;AAAA,QACF,EAAE;AAAA,MACJ;AACA,UAAI,qBAAqB,YAAY,WAAY;AAEjD;AAAA,QAAQ,CAAC,SACP;AAAA,UACE;AAAA,UACA,QAAQ;AAAA,YAAQ,CAAC,EAAE,MAAM,OAAO,MAC9B,gBAAgB,OAAO,MAAM,IAAI,CAAC,SAAS,YAAY,MAAM,IAAI,CAAC,IAAI,OAAO;AAAA,UAC/E;AAAA,QACF;AAAA,MACF;AACA,gBAAU,UAAU;AACpB,gBAAU,MAAM;AAAA,IAClB,SAAS,OAAO;AACd,UAAI,qBAAqB,YAAY,WAAY;AACjD,eAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACpE,UAAE;AACA,UAAI,qBAAqB,YAAY,WAAY,YAAW,KAAK;AACjE,UAAI,gBAAgB,YAAY,aAAc,iBAAgB,UAAU;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,WAAW,QAAQ,aAAa,CAAC;AAIrE,QAAM,kBAAkBA;AAAA,IACtB,CAAC,aAAuC;AACtC,YAAM,eAAe,gBACjB,SAAS,QACR,CAAC,YAAY,UAAU,QAAQ,CAAC,EAAE,OAAO,OAAO;AACrD,UAAI,aAAa,WAAW,GAAG;AAC7B,kBAAU,KAAK;AACf,kBAAU,IAAI;AACd,iBAAS,CAAC;AACV,kBAAU,CAAC;AACX,qBAAa,CAAC,CAAC;AACf,gBAAQ,CAAC,SAAU,KAAK,WAAW,IAAI,OAAO,CAAC,CAAE;AACjD,kBAAU,UAAU;AACpB,kBAAU,SAAS;AACnB;AAAA,MACF;AACA,gBAAU,IAAI;AACd,gBAAU,aAAa,WAAW,IAAK,aAAa,CAAC,GAAG,QAAQ,OAAQ,IAAI;AAC5E,eAAS,aAAa,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,OAAO,CAAC,CAAC;AAChE,gBAAU,aAAa,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,QAAQ,CAAC,CAAC;AAClE,mBAAa,aAAa,IAAI,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC,CAAC;AACjE;AAAA,QAAQ,CAAC,SACP;AAAA,UACE;AAAA,UACA,aAAa;AAAA,YAAQ,CAAC,SACpB,gBAAgB,KAAK,KAAK,IAAI,CAAC,SAAS,YAAY,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK;AAAA,UAC/E;AAAA,QACF;AAAA,MACF;AACA,gBAAU,UAAU;AACpB,gBAAU,SAAS;AACnB,eAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,UAAU,aAAa;AAAA,EAC1B;AAKA,QAAM,aAAaD,QAAwC,OAAO;AAClE,aAAW,UAAU;AACrB,QAAM,kBAAkB,SAAS,YAAY;AAC7C,QAAM,sBAAsBA,QAAO,WAAW;AAC9C,EAAAE,WAAU,MAAM;AAGd,oBAAgB,SAAS,MAAM;AAC/B,oBAAgB,UAAU;AAC1B,yBAAqB,WAAW;AAChC,UAAM,kBAAkB,oBAAoB,YAAY;AACxD,wBAAoB,UAAU;AAC9B,QAAI,mBAAmB,CAAC,SAAS;AAC/B,oBAAc,UAAU;AACxB,cAAQ,CAAC,CAAC;AACV,uBAAiB,WAAW;AAC5B,gBAAU,KAAK;AACf,gBAAU,IAAI;AACd,eAAS,CAAC;AACV,gBAAU,CAAC;AACX,mBAAa,CAAC,CAAC;AACf,gBAAU,UAAU;AACpB,gBAAU,IAAI;AACd,iBAAW,KAAK;AAChB,eAAS,IAAI;AAAA,IACf;AACA,QAAI,CAAC,SAAS;AACZ;AAAA,IACF;AACA,UAAM,iBAAiB,oBAAoB,OAAO,WAAW,UAAU;AACvE,QAAI,mBAAmB,mBAAmB,CAACJ,WAAU,UAAU,YAAY,SAAS;AAClF,sBAAgB,cAAc;AAAA,IAChC;AACA,QAAIA,WAAU,CAAC,gBAAgB;AAC7B,WAAK,QAAQ;AAAA,IACf;AACA,WAAO,MAAM;AACX,sBAAgB,SAAS,MAAM;AAC/B,sBAAgB,UAAU;AAC1B,2BAAqB,WAAW;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,SAASA,SAAQ,iBAAiB,aAAa,SAAS,eAAe,CAAC;AAK5E,QAAM,SAAS,QAAQ;AACvB,EAAAI,WAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,OAAQ;AACzB,QAAI,SAAS,cAAc;AAC3B,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,SAAS,iBAAiB,MAAM,WAAW,QAAQ;AAC3D,iBAAS,MAAM;AAAA,MACjB;AAAA,IACF;AACA,QAAI,SAAS,cAAc,SAAS;AAClC,oBAAc,UAAU;AACxB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF,GAAG,CAAC,SAAS,QAAQ,OAAO,CAAC;AAE7B,QAAM,kBAAkB,WAAW,kBAAkB;AACrD,QAAM,mBAAmB,kBAAkB,YAAY,CAAC;AACxD,SAAO;AAAA,IACL,MAAM,kBAAkB,OAAO,CAAC;AAAA,IAChC,QAAQ,kBAAkB,SAAS;AAAA,IACnC,QAAQ,mBAAmB;AAAA,IAC3B,OAAO,kBAAkB,QAAQ;AAAA,IACjC,QAAQ,kBAAkB,SAAS;AAAA,IACnC,WAAW,iBAAiB;AAAA,IAC5B,WAAW;AAAA,IACX;AAAA,IACA,QAAQ,kBAAkB,SAAS;AAAA,IACnC,YAAY,mBAAmB,WAAW,YAAa,SAAS,cAAc,OAAQ;AAAA,IACtF,SAAS,mBAAmB;AAAA,IAC5B,OAAO,kBAAkB,QAAQ;AAAA,EACnC;AACF;;;ACnYA,SAAS,eAAAC,eAAa,aAAAC,aAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAIzD,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAC1B,IAAM,4BAA4B;AAIlC,IAAM,8BAA8B;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AASA,SAAS,2BACP,OACA,UAC0B;AAC1B,MACE,CAAC,SAAS,KAAK,KACf,MAAM,YAAY,KAClB,CAAC,OAAO,cAAc,MAAM,QAAQ,KACpC,OAAO,MAAM,eAAe,YAC5B,EAAE,MAAM,WAAW,QAAQ,OAAO,MAAM,WAAW,aACnD,CAAC,OAAO,cAAc,MAAM,UAAU,KACtC,CAAC,SAAS,MAAM,SAAS,KACzB,OAAO,MAAM,kBAAkB,aAC/B,CAAC,MAAM,QAAQ,MAAM,KAAK,KAC1B,CAAC,MAAM,QAAQ,MAAM,KAAK,KAC1B,CAAC,SAAS,MAAM,KAAK,GACrB;AACA,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AAEA,MACE,MAAM,aAAa,SAAS,YAC5B,MAAM,eAAe,SAAS,cAC9B,MAAM,WAAW,SAAS,UAC1B,MAAM,eAAe,SAAS,YAC9B;AACA,UAAM,IAAI,MAAM,iEAAiE;AAAA,EACnF;AAEA,aAAW,SAAS,6BAA6B;AAC/C,QAAI,CAAC,OAAO,cAAc,MAAM,MAAM,KAAK,CAAC,KAAK,MAAM,MAAM,KAAK,MAAM,SAAS,MAAM,KAAK,GAAG;AAC7F,YAAM,IAAI,MAAM,mEAAmE;AAAA,IACrF;AAAA,EACF;AACA,MACE,OAAO,MAAM,MAAM,kBAAkB,aACrC,MAAM,MAAM,kBAAkB,SAAS,MAAM,kBAC5C,MAAM,MAAM,eAAe,WAAW,SAAS,MAAM,eAAe,SACrE,MAAM,MAAM,WAAW,SAAS,MAAM,aACtC,MAAM,MAAM,WAAW,SAAS,MAAM,aACtC,MAAM,kBAAkB,SAAS,MAAM,eACvC;AACA,UAAM,IAAI,MAAM,mEAAmE;AAAA,EACrF;AAEA,SAAO;AACT;AAkDO,SAAS,oBACd,WACA,UAAsC,CAAC,GACZ;AAC3B,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,WAAW,QAAQ,WAAW,SAAS,QAAQ,SAAS;AAC9D,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE;AAE1D,QAAM,CAAC,SAAS,UAAU,IAAIC,UAA0C,IAAI;AAC5E,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAgD,IAAI;AAGhG,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAwB,IAAI;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAGrD,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,UAAwB,IAAI;AAI9E,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,WAAW;AAI9D,QAAM,gBAAgBC,QAAO,CAAC;AAC9B,QAAM,kBAAkBA,QAA+B,IAAI;AAG3D,QAAM,aAAaA,QAAO,CAAC;AAE3B,QAAM,UAAUC,cAAY,YAAY;AACtC,QAAI,CAAC,UAAW;AAChB,oBAAgB,SAAS,MAAM;AAC/B,UAAM,eAAe,IAAI,gBAAgB;AACzC,oBAAgB,UAAU;AAC1B,UAAM,aAAc,cAAc,WAAW;AAC7C,qBAAiB,WAAW;AAC5B,eAAW,IAAI;AACf,aAAS,IAAI;AACb,QAAI;AAGF,eAAS,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG;AAC/C,cAAM,MAAmC,MAAM,OAAO;AAAA,UACpD;AAAA,UACA;AAAA,UACA,EAAE,QAAQ,aAAa,OAAO;AAAA,QAChC;AACA,YAAI,cAAc,YAAY,WAAY;AAC1C,YAAI,CAAC,IAAI,WAAW;AAClB,qBAAW,IAAI;AACf,uBAAa,KAAK;AAClB,uBAAa,CAAC;AACd,4BAAkB,IAAI,kBAAkB,IAAI;AAC5C;AAAA,QACF;AACA,0BAAkB,IAAI;AAGtB,qBAAa,IAAI,MAAM,SAAS;AAGhC,YAAI,gBAAyB,IAAI;AACjC,YAAI,CAAC,iBAAiB,IAAI,aAAa;AACrC,gBAAM,WAAW,MAAM,MAAM,IAAI,YAAY,KAAK;AAAA,YAChD,QAAQ,YAAY,IAAI;AAAA,cACtB,aAAa;AAAA,cACb,YAAY,QAAQ,yBAAyB;AAAA,YAC/C,CAAC;AAAA,YACD,aAAa;AAAA,YACb,OAAO;AAAA,YACP,gBAAgB;AAAA,UAClB,CAAC,EAAE,MAAM,MAAM,IAAI;AACnB,cAAI,cAAc,YAAY,WAAY;AAC1C,cAAI,CAAC,UAAU,IAAI;AACjB,gBACE,YAAY,MACX,aAAa,QAAQ,SAAS,WAAW,OAAO,SAAS,WAAW,MACrE;AACA;AAAA,YACF;AACA,kBAAM,IAAI;AAAA,cACR,6CAA6C,WAAW,KAAK,SAAS,MAAM,MAAM,EAAE;AAAA,YACtF;AAAA,UACF;AACA,cAAI;AACF,4BAAgB,MAAM,SAAS,KAAK;AAAA,UACtC,QAAQ;AACN,kBAAM,IAAI,MAAM,gDAAgD;AAAA,UAClE;AACA,cAAI,cAAc,YAAY,WAAY;AAAA,QAC5C;AACA,YAAI,CAAC,eAAe;AAClB,gBAAM,IAAI,MAAM,wDAAwD;AAAA,QAC1E;AACA,cAAM,WAAW,2BAA2B,eAAe,GAAG;AAC9D,mBAAW,QAAQ;AACnB,qBAAa,IAAI;AAGjB;AAAA,UAAqB,CAAC,SACpB,SAAS,QAAQ,SAAS,WAAW,OAAO,SAAS,WAAW;AAAA,QAClE;AACA;AAAA,MACF;AACA,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF,SAAS,OAAO;AACd,UAAI,cAAc,YAAY,WAAY;AAC1C,eAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACpE,UAAE;AACA,UAAI,cAAc,YAAY,WAAY,YAAW,KAAK;AAC1D,UAAI,gBAAgB,YAAY,aAAc,iBAAgB,UAAU;AAAA,IAC1E;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,WAAW,CAAC;AAGhD,EAAAC,YAAU,MAAM;AAGd,oBAAgB,SAAS,MAAM;AAC/B,oBAAgB,UAAU;AAC1B,kBAAc,WAAW;AACzB,eAAW,UAAU;AACrB,eAAW,IAAI;AACf,iBAAa,KAAK;AAClB,sBAAkB,IAAI;AACtB,iBAAa,IAAI;AACjB,yBAAqB,IAAI;AACzB,aAAS,IAAI;AACb,QAAI,CAAC,SAAS;AACZ,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,SAAK,QAAQ;AACb,WAAO,MAAM;AACX,sBAAgB,SAAS,MAAM;AAC/B,sBAAgB,UAAU;AAC1B,oBAAc,WAAW;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAMrB,QAAM,SAAS,QAAQ;AACvB,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,OAAQ;AACzB,QAAI,SAAwB;AAC5B,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,YAAY,WAAW,QAAS;AAC1C,UAAI,MAAM,SAAS,mBAAmB;AACpC,cAAM,UAAU,MAAM;AACtB,YAAI,WAAW,OAAO,YAAY,YAAY,OAAO,QAAQ,aAAa,UAAU;AAClF,mBAAS,WAAW,OAAO,QAAQ,WAAW,KAAK,IAAI,QAAQ,QAAQ,QAAQ;AAAA,QACjF;AAAA,MACF,WAAW,MAAM,SAAS,mBAAmB;AAC3C,cAAM,UAAU,MAAM;AACtB,YAAI,WAAW,OAAO,YAAY,YAAY,OAAO,QAAQ,aAAa,UAAU;AAClF,mBAAS,WAAW,OAAO,QAAQ,WAAW,KAAK,IAAI,QAAQ,QAAQ,QAAQ;AAAA,QACjF;AAAA,MACF;AAAA,IACF;AACA,eAAW,SAAS;AAClB,UAAI,MAAM,WAAW,WAAW,QAAS,YAAW,UAAU,MAAM;AACtE,QAAI,WAAW,KAAM;AACrB,yBAAqB,CAAC,SAAU,SAAS,QAAQ,SAAS,OAAO,SAAS,IAAK;AAAA,EACjF,GAAG,CAAC,SAAS,MAAM,CAAC;AAGpB,QAAM,kBAAkB,WAAW,kBAAkB;AACrD,QAAM,iBAAiB,kBAAkB,UAAU;AACnD,QAAM,2BAA2B,kBAAkB,oBAAoB;AACvE,QAAM,iBAAiB,gBAAgB,YAAY;AACnD,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,QAAI,6BAA6B,KAAM;AACvC,QAAI,mBAAmB,QAAQ,4BAA4B,eAAgB;AAM3E,QAAI,gBAAgB,QAAS;AAE7B,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,SAAS,0BAA0B,gBAAgB,OAAO,CAAC;AAE/D,QAAM,UACJ,mBAAmB,QACnB,6BAA6B,QAC7B,2BAA2B;AAE7B,SAAO;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY,gBAAgB,cAAc;AAAA,IAC1C,WAAW,mBAAmB;AAAA,IAC9B,gBAAgB,kBAAkB,iBAAiB;AAAA,IACnD,WAAW,kBAAkB,YAAY;AAAA,IACzC;AAAA,IACA,SAAS,mBAAmB;AAAA,IAC5B,OAAO,kBAAkB,QAAQ;AAAA,IACjC;AAAA,EACF;AACF;;;ACxVA,SAAS,eAAAC,eAAa,aAAAC,aAAW,UAAAC,UAAQ,YAAAC,iBAAgB;AA0EzD,SAAS,OAAO,UAAuC;AACrD,SAAO,aAAa,UAAU,aAAa;AAC7C;AAaO,SAAS,iBACd,WACA,UAAmC,CAAC,GACZ;AACxB,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,OAAO,OAAO,QAAQ,QAAQ;AACpC,QAAM,UAAU,QAAQ,YAAY;AACpC,QAAM,cAAc,GAAG,WAAW,KAAS,aAAa,EAAE,KAAS,QAAQ,EAAE;AAE7E,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAwB,IAAI;AACxD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuD,MAAM;AACvF,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAuC,IAAI;AAC3E,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AACrD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,WAAW;AAE9D,QAAM,kBAAkB,QAAQ;AAChC,QAAM,YAAYC,SAAsB,IAAI;AAC5C,YAAU,UAAU;AAEpB,QAAM,cAAcA,SAAO,KAAK;AAChC,QAAM,wBAAwBA,SAAO,CAAC;AACtC,QAAM,kBAAkBA,SAAO,CAAC;AAChC,QAAM,eAAeA,SAA+B,IAAI;AAGxD,EAAAC,YAAU,MAAM;AACd,0BAAsB,WAAW;AACjC,oBAAgB,WAAW;AAC3B,iBAAa,SAAS,MAAM;AAC5B,iBAAa,UAAU;AACvB,qBAAiB,WAAW;AAC5B,cAAU,IAAI;AACd,iBAAa,KAAK;AAClB,aAAS,MAAM;AACf,gBAAY,IAAI;AAChB,aAAS,IAAI;AACb,gBAAY,UAAU;AACtB,WAAO,MAAM;AACX,4BAAsB,WAAW;AACjC,sBAAgB,WAAW;AAC3B,mBAAa,SAAS,MAAM;AAC5B,mBAAa,UAAU;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,OAAOC;AAAA,IACX,CAAC,YAAoB;AACnB,UAAI,kBAAkB,YAAa;AACnC,UAAI,QAAS;AACb,gBAAU,CAAC,SAAU,SAAS,UAAU,OAAO,OAAQ;AAEvD,eAAS,CAAC,SAAU,SAAS,aAAa,OAAO,MAAO;AACxD,kBAAY,IAAI;AAChB,UAAI,CAAC,MAAM;AAET,qBAAa,CAAC,SAAS;AACrB,cAAI,CAAC,KAAM,mBAAkB;AAC7B,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,SAAS,MAAM,iBAAiB,eAAe,WAAW;AAAA,EAC7D;AAEA,QAAM,UAAUA;AAAA,IACd,OAAO,UAAmB;AACxB,UAAI,kBAAkB,YAAa;AACnC,UAAI,CAAC,aAAa,CAAC,KAAM;AACzB,YAAM,UAAU,UAAU;AAC1B,UAAI,YAAY,KAAM;AACtB,UAAI,YAAY,QAAS;AACzB,YAAM,qBAAqB,sBAAsB;AACjD,YAAM,eAAgB,gBAAgB,WAAW;AACjD,kBAAY,UAAU;AACtB,eAAS,UAAU;AACnB,eAAS,IAAI;AACb,UAAI;AACF,YAAI,CAAC,OAAO;AAIV,gBAAM,YAAY,IAAI,gBAAgB;AACtC,uBAAa,SAAS,MAAM;AAC5B,uBAAa,UAAU;AACvB,gBAAM,WAAW,MAAM,OAAO;AAAA,YAC5B;AAAA,YACA;AAAA,YACA,EAAE,KAAK;AAAA,YACP,EAAE,QAAQ,UAAU,OAAO;AAAA,UAC7B;AACA,cAAI,aAAa,YAAY,UAAW,cAAa,UAAU;AAC/D,cAAI,sBAAsB,YAAY,mBAAoB;AAC1D,gBAAM,cAAc,SAAS;AAC7B,cAAI,gBAAgB,QAAQ,gBAAgB,aAAa;AACvD,wBAAY,EAAE,MAAM,MAAM,aAAa,MAAM,YAAY,CAAC;AAC1D,qBAAS,UAAU;AACnB;AAAA,UACF;AAAA,QACF;AACA,cAAM,OAAO,QAAQ,aAAa,WAAW,EAAE,MAAM,SAAS,WAAW,KAAK,CAAC;AAC/E,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,oBAAY,IAAI;AAChB,iBAAS,SAAS;AAClB,qBAAa,KAAK;AAAA,MACpB,SAAS,OAAO;AACd,YAAI,sBAAsB,YAAY,mBAAoB;AAC1D,iBAAS,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAElE,iBAAS,MAAM;AAAA,MACjB,UAAE;AACA,YAAI,gBAAgB,YAAY,cAAc;AAC5C,sBAAY,UAAU;AACtB,uBAAa,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,aAAa,WAAW,MAAM,aAAa,eAAe,WAAW;AAAA,EAChF;AAGA,EAAAD,YAAU,MAAM;AACd,QAAI,QAAS;AACb,QAAI,CAAC,KAAM;AACX,QAAI,WAAW,KAAM;AACrB,QAAI,UAAU,aAAa,UAAU,cAAc,UAAU,WAAY;AACzE,SAAK,QAAQ,KAAK;AAAA,EACpB,GAAG,CAAC,SAAS,MAAM,QAAQ,OAAO,OAAO,CAAC;AAE1C,QAAM,YAAYC,cAAY,YAAY;AACxC,UAAM,QAAQ,IAAI;AAAA,EACpB,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,UAAUA,cAAY,MAAM;AAChC,cAAU,IAAI;AACd,iBAAa,KAAK;AAClB,aAAS,MAAM;AACf,gBAAY,IAAI;AAChB,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,CAAC;AAGL,MAAI;AACJ,MAAI,SAAS;AACX,YAAQ;AAAA,EACV,WAAW,UAAU,YAAY;AAC/B,YAAQ;AAAA,EACV,WAAW,UAAU,WAAW;AAC9B,YAAQ;AAAA,EACV,WAAW,UAAU,YAAY;AAC/B,YAAQ;AAAA,EACV,WAAW,WAAW,MAAM;AAC1B,YAAQ;AAAA,EACV,WAAW,MAAM;AAGf,YAAQ;AAAA,EACV,WAAW,QAAQ,YAAY,MAAM;AACnC,YAAQ;AAAA,EACV,OAAO;AACL,YAAQ;AAAA,EACV;AAEA,QAAM,kBAAkB,kBAAkB;AAC1C,SAAO;AAAA,IACL,OAAO,kBAAkB,QAAQ,UAAU,qBAAqB;AAAA,IAChE,UAAU;AAAA,IACV,QAAQ,kBAAkB,SAAS;AAAA,IACnC,WAAW,mBAAmB;AAAA,IAC9B;AAAA,IACA,UAAU,kBAAkB,WAAW;AAAA,IACvC;AAAA,IACA;AAAA,IACA,OAAO,kBAAkB,QAAQ;AAAA,EACnC;AACF;;;AC1QA,SAAS,WAAAC,gBAAe;AAoCjB,SAAS,WAAW,YAAoB,KAAqB;AAClE,QAAM,OAAO,KAAK,MAAM,UAAU;AAClC,MAAI,OAAO,MAAM,IAAI,EAAG,QAAO;AAC/B,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,QAAQ,GAAI,CAAC;AAC3D,MAAI,UAAU,GAAI,QAAO;AACzB,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,QAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,SAAO,GAAG,IAAI;AAChB;AAcO,SAAS,kBAAkB,OAA4C;AAC5E,QAAM,MAAM,MAAM,OAAO,KAAK,IAAI;AAClC,QAAM,OAAO,MAAM,cAAc;AACjC,QAAM,eAAe,OAAO,wBAAmB,WAAW,MAAM,GAAG,CAAC,KAAK;AAGzE,MAAI,MAAM,aAAa,UAAU,MAAM,aAAa,YAAY;AAC9D,WAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,MAAM,KAAK;AAAA,EACpD;AAIA,QAAM,oBACJ,MAAM,uBAAuB,QAAQ,MAAM,uBAAuB;AAIpE,QAAM,iBACJ,MAAM,uBAAuB,kBAAkB,MAAM,uBAAuB;AAC9E,MACE,CAAC,sBACA,MAAM,sBAAsB,iBAAiB,MAAM,cAAc,QAAQ,iBAC1E;AACA,WAAO,EAAE,OAAO,UAAU,OAAO,gBAAW,KAAK;AAAA,EACnD;AAGA,SAAO,EAAE,OAAO,WAAW,OAAO,cAAc,KAAK;AACvD;AAWO,SAAS,eAAe,OAA2C;AACxE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AACJ,SAAOA;AAAA,IACL,MACE,kBAAkB;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA;AAAA;AAAA,IAGH;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;ACrIA,SAAS,WAAW,iBAAiB,aAAa;AAClD,SAAS,aAAAC,aAAW,OAAO,UAAAC,UAAQ,YAAAC,kBAAgC;AAiIzD,SAwBM,UAxBN,KAEF,YAFE;AAnHH,IAAM,iCAA0D;AAAA,EACrE,OAAO;AAAA,EACP,aAAa;AAAA,EACb,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB,CAAC,SAAS,KAAK,WAAW,KAAK,GAAG,EAAE,WAAW,KAAK,UAAK;AAC3E;AAaA,IAAM,uCAAuC;AAE7C,SAAS,yBAAyB,OAA+B;AAC/D,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI;AACJ,MAAI;AACF,iBACE,OAAO,UAAU,WAAW,QAAS,KAAK,UAAU,OAAO,MAAM,CAAC,KAAK,OAAO,KAAK;AAAA,EACvF,QAAQ;AACN,iBAAa;AAAA,EACf;AACA,QAAM,aAAa,MAAM,KAAK,UAAU;AACxC,MAAI,WAAW,UAAU,qCAAsC,QAAO;AACtE,SAAO,GAAG,WAAW,MAAM,GAAG,oCAAoC,EAAE,KAAK,EAAE,CAAC;AAAA,SAAO,WAAW,SAAS,oCAAoC;AAC7I;AAEA,SAAS,qBAAqB,UAAmC;AAC/D,SAAO,GAAG,SAAS,EAAE,KAAS,SAAS,IAAI,KAAS,yBAAyB,SAAS,SAAS,KAAK,EAAE;AACxG;AAOO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,UAAU,MAAM;AACtB,QAAM,WAAW,EAAE,GAAG,gCAAgC,GAAG,UAAU;AACnE,QAAM,CAAC,SAAS,UAAU,IAAIC,WAIpB,IAAI;AACd,QAAM,aAAaC,SAAsD,IAAI;AAC7E,QAAM,CAAC,eAAe,gBAAgB,IAAID,WAAuB,IAAI;AAErE,EAAAE,YAAU,MAAM;AACd,QACE,WACA,CAAC,UAAU,KAAK,CAAC,aAAa,qBAAqB,QAAQ,MAAM,QAAQ,WAAW,GACpF;AACA,UAAI,WAAW,SAAS,UAAU,QAAQ,OAAO;AAC/C,mBAAW,UAAU;AAAA,MACvB;AACA,iBAAW,CAAC,YAAa,SAAS,UAAU,QAAQ,QAAQ,OAAO,OAAQ;AAAA,IAC7E;AAAA,EACF,GAAG,CAAC,WAAW,OAAO,CAAC;AAEvB,MAAI,UAAU,WAAW,EAAG,QAAO;AACnC,QAAM,OAAO,cAAc,WAAW,YAAY;AAClD,QAAM,SAAS,OACb,UACA,aACkB;AAClB,QAAI,cAAc,WAAW,YAAY,KAAM;AAC/C,UAAM,cAAc,qBAAqB,QAAQ;AACjD,UAAM,QAAQ,OAAO,WAAW;AAChC,eAAW,UAAU,EAAE,aAAa,MAAM;AAC1C,eAAW,EAAE,aAAa,UAAU,MAAM,CAAC;AAC3C,qBAAiB,IAAI;AACrB,QAAI;AACF,aAAO,aAAa,YAAY,UAAU,QAAQ,IAAI,SAAS,QAAQ;AAAA,IAIzE,SAAS,OAAO;AACd,UAAI,WAAW,SAAS,UAAU,OAAO;AACvC,mBAAW,UAAU;AACrB,mBAAW,CAAC,YAAa,SAAS,UAAU,QAAQ,OAAO,OAAQ;AACnE,yBAAiB,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,MAC5E;AAAA,IACF;AAAA,EACF;AACA,QAAM,eAAe,eAAe,YAAY,iBAAiB,QAAQ,MAAM,UAAU;AAEzF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,mBAAiB;AAAA,MAEjB;AAAA,6BAAC,YAAO,WAAU,0BAChB;AAAA,8BAAC,UAAK,WAAU,+HACd,8BAAC,mBAAgB,eAAY,QAAO,WAAU,UAAS,GACzD;AAAA,UACA,qBAAC,SAAI,WAAU,WACb;AAAA,gCAAC,QAAG,IAAI,SAAS,WAAU,uCACxB,mBAAS,OACZ;AAAA,YACA,oBAAC,OAAE,WAAU,sCAAsC,mBAAS,aAAY;AAAA,aAC1E;AAAA,WACF;AAAA,QAEA,oBAAC,SAAI,WAAU,uBACZ,oBAAU,IAAI,CAAC,aAAa;AAC3B,gBAAM,cAAc,qBAAqB,QAAQ;AACjD,gBAAM,SAAS,SAAS,gBAAgB,cAAc,QAAQ,WAAW;AACzE,gBAAM,mBAAmB,yBAAyB,SAAS,SAAS;AACpE,iBACE;AAAA,YAAC;AAAA;AAAA,cAEC,oBAAkB,SAAS;AAAA,cAC3B,WAAU;AAAA,cAET;AAAA,iCACC,eAAe,QAAQ,IAEvB,iCACE;AAAA,sCAAC,OAAE,WAAU,qCACV,mBAAS,eAAe,SAAS,IAAI,GACxC;AAAA,kBACC,qBAAqB,OACpB,oBAAC,SAAI,WAAU,uIACZ,4BACH,IACE;AAAA,mBACN;AAAA,gBAEF,qBAAC,SAAI,WAAU,yCACb;AAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,UAAU;AAAA,sBACV,SAAS,MAAM,KAAK,OAAO,UAAU,QAAQ;AAAA,sBAC7C,WAAU;AAAA,sBAEV;AAAA,4CAAC,SAAM,eAAY,QAAO,WAAU,YAAW;AAAA,wBAC9C,WAAW,WAAW,SAAS,YAAY,SAAS;AAAA;AAAA;AAAA,kBACvD;AAAA,kBACA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,UAAU;AAAA,sBACV,SAAS,MAAM,KAAK,OAAO,UAAU,SAAS;AAAA,sBAC9C,WAAU;AAAA,sBAEV;AAAA,4CAAC,aAAU,eAAY,QAAO,WAAU,YAAW;AAAA,wBAClD,WAAW,YAAY,SAAS,YAAY,SAAS;AAAA;AAAA;AAAA,kBACxD;AAAA,mBACF;AAAA;AAAA;AAAA,YArCK,SAAS;AAAA,UAsChB;AAAA,QAEJ,CAAC,GACH;AAAA,QAEC,eACC,oBAAC,OAAE,MAAK,SAAQ,WAAU,oCACvB,wBACH,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;;;ACpMA,SAAS,aAAa,cAAc;AA+I5B,gBAAAC,MAaU,QAAAC,aAbV;AArDD,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,0BAA0B;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,aAAa,0BAA0B;AAAA,IAC3C,UAAU;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,QAAM,cAAc;AAAA,IAClB,eAAe,UAAU,mBAAmB,iBAAiB;AAAA,EAC/D;AACA,QAAM,eAAe,eAAe,QAAQ,YAAY;AAExD,SACE,gBAAAC,MAAC,QAAK,YAAwB,WAC5B;AAAA,oBAAAA,MAAC,SACC;AAAA,sBAAAC,KAACC,iBAAA,EAAe;AAAA,MAChB,gBAAAF,MAAC,WACC;AAAA,wBAAAC,KAAC,eAAY;AAAA,QACb,gBAAAA,KAAC,qBAAkB;AAAA,QACnB,gBAAAA,KAAC,eAAY;AAAA,QACZ;AAAA,QACD,gBAAAA,KAAC,SAAM,aAA0B,WAAsB;AAAA,QACtD,WAAW,eACV,gBAAAA,KAAC,gBAAa,IAEd,gBAAAA,KAAC,UAAO,WAAW,eAAe,oCAAoC,QACpE,0BAAAD,MAAC,eAAY,IAAG,sBACb;AAAA,wBACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,eAAe,oCAAoC;AAAA,cAE7D;AAAA;AAAA,gBACD,gBAAAC,KAAC,gBAAa,WAAW,uBAAuB;AAAA,gBAC/C,gBACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACE,GAAG;AAAA,oBACJ,YAAY;AAAA,oBACZ,WAAW,CAAC,cAAc,WAAW,sBAAsB,EACxD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA;AAAA,gBACb,IACE;AAAA,gBACH,SACC,gBAAAA;AAAA,kBAAC,OAAO;AAAA,kBAAP;AAAA,oBACC,QAAM;AAAA,oBACN,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE;AAAA,oBACvD,WAAU;AAAA,oBAEV,0BAAAA;AAAA,sBAACE;AAAA,sBAAA;AAAA,wBACC;AAAA,wBACA,OAAO;AAAA,wBACP,UAAU;AAAA;AAAA,oBACZ;AAAA;AAAA,gBACF,IACE;AAAA,gBACH,gBACC,gBAAAF;AAAA,kBAAC,OAAO;AAAA,kBAAP;AAAA,oBACC,QAAM;AAAA,oBACN,YAAY,EAAE,UAAU,MAAM,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE;AAAA,oBACvD,WAAU;AAAA,oBAET;AAAA;AAAA,gBACH,IACE;AAAA;AAAA;AAAA,UACN,IAEA,gBAAAA,KAAC,QAAM,gBAAK;AAAA,UAEd,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,eAAe,6CAA6C;AAAA,cAEtE;AAAA;AAAA,gBACD,gBAAAC,KAAC,eAAY;AAAA,gBACb,gBAAAA,KAAC,cAAW;AAAA;AAAA;AAAA,UACd;AAAA,WACF,GACF;AAAA,SAEJ;AAAA,OACF;AAAA,IACA,gBAAAA,KAAC,QAAK;AAAA,IACN,gBAAAA,KAAC,UAAO;AAAA,KACV;AAEJ;;;AC9JM,gBAAAG,MAWA,QAAAC,aAXA;AAvCC,SAAS,oBAAoB,SAA0B;AAG5D,MAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAAM,KAAK,EAAE,SAAS,GAAG;AACxE,WAAO,QAAQ;AAAA,EACjB;AACA,aAAW,OAAO,CAAC,SAAS,MAAM,GAAY;AAC5C,UAAM,QAAQ,QAAQ,SAAS,GAAG;AAClC,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,GAAG;AACxD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,SAAS,QAAQ,gBAAgB,GAAG,KAAK,QAAQ,GAAG,MAAM,GAAG,CAAC;AACvE;AAOO,SAAS,UAAU,EAAE,SAAS,OAAO,UAAU,QAAQ,UAAU,GAAmB;AACzF,QAAM,UAAU,QAAQ,WAAW,aAAa,QAAQ,WAAW;AACnE,QAAM,WAAW,QAAQ,WAAW;AACpC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,eAAa,QAAQ;AAAA,MACrB,SAAS,SAAS,MAAM,OAAO,OAAO,IAAI;AAAA,MAC1C,UAAU,CAAC;AAAA,MACX,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,UACE;AAAA,QACF;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,eAAW;AAAA,YACX,WAAW;AAAA,cACT;AAAA,cACA,UACI,6BACA,WACE,qCACA;AAAA,YACR;AAAA;AAAA,QACF;AAAA,QACA,gBAAAC,MAAC,UAAK,WAAU,iDACd;AAAA,0BAAAD,KAAC,UAAK,WAAU,yEACb,mBAAS,oBAAoB,OAAO,GACvC;AAAA,UACA,gBAAAA,KAAC,iBAAc,QAAQ,QAAQ,QAAQ,MAAK,MAAK,WAAU,SAAQ;AAAA,WACrE;AAAA,QACC,WACC,gBAAAA,KAAC,UAAK,WAAU,4CAA4C,oBAAS,IACnE;AAAA,QACJ,gBAAAC,MAAC,UAAK,WAAU,uEACd;AAAA,0BAAAD,KAAC,UAAK,WAAU,gBAAgB,kBAAQ,GAAG,MAAM,GAAG,CAAC,GAAE;AAAA,UACvD,gBAAAA,KAAC,UAAK,eAAW,MAAC,kBAAC;AAAA,UACnB,gBAAAA,KAAC,UAAK,WAAU,YAAY,kBAAQ,OAAM;AAAA,UAC1C,gBAAAA,KAAC,UAAK,WAAU,oBAAmB,OAAO,QAAQ,WAC/C,6BAAmB,QAAQ,SAAS,GACvC;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACpFA,SAAyB,aAAAE,aAAW,UAAAC,UAAQ,YAAAC,kBAAgB;;;ACsD5D,IAAM,YAAyB;AAAA,EAC7B,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,6BAA6B;AAC/B;AAEA,IAAM,aAA0B;AAAA,EAC9B,OAAO;AAAA,EACP,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,aAAa;AAAA,EACb,WAAW;AAAA,EACX,aAAa;AAAA,EACb,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,6BAA6B;AAC/B;AAGO,SAAS,cAAc,MAA8B;AAC1D,SAAO,SAAS,UAAU,aAAa;AACzC;AAQO,SAAS,gBAAgB,MAAmB,MAA6B;AAC9E,QAAM,OAAO,cAAc,IAAI;AAC/B,QAAM,aAAa,SAAS,UAAU,2BAA2B;AACjE,QAAM,aAAa,SAAS,UAAU,0BAA0B;AAChE,QAAM,KAAK,KAAK,eAAe,KAAK;AACpC,QAAM,KAAK,KAAK,eAAe,KAAK;AACpC,QAAM,SAAS,KAAK,mBAAmB,KAAK,KAAK;AACjD,SAAO;AAAA;AAAA,IAEL,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,qBAAqB,KAAK;AAAA,IAC1B,qBAAqB,KAAK;AAAA,IAC1B,6BAA6B,KAAK;AAAA,IAClC,OAAO,KAAK;AAAA,IACZ,KAAK,KAAK;AAAA,IACV,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,IACX,OAAO,KAAK;AAAA,IACZ,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,aAAa,KAAK;AAAA,IAClB,cAAc,KAAK;AAAA,IACnB,YAAY,KAAK;AAAA,IACjB,eAAe,KAAK;AAAA,IACpB,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,EACpB;AACF;AAKA,SAAS,WAAW,IAAa,OAAuC;AACtE,QAAM,UAAU,GAAG,SAAS,KAAK,EAAE;AACnC,MAAI,SAAS;AACX,QAAI,QAAQ,yBAAyB,KAAK,QAAQ,WAAW,EAAG,QAAO;AACvE,UAAM,WAAW,QAAQ,iBAAiB;AAC1C,QAAI,SAAU,QAAO,SAAS,aAAa,eAAe,MAAM,UAAU,UAAU;AAAA,EACtF;AACA,QAAM,UACJ,MAAM,iBAAiB,cAAc,EAAE,KAAK,KAC5C,MAAM,eACN,IACA,YAAY;AACd,MAAI,OAAO,SAAS,OAAO,KAAK,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACjE,SAAO;AACT;AAYO,SAAS,qBAAqB,MAAmD;AACtF,MAAI,OAAO,WAAW,eAAe,OAAO,qBAAqB,YAAa,QAAO;AACrF,QAAM,KACJ,SACC,OAAO,aAAa,cACf,SAAS,cAAc,iBAAiB,KAC1C,SAAS,kBACT;AACN,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,QAAQ,iBAAiB,EAAE;AACjC,QAAM,OAAoB,CAAC,SAAS;AAClC,UAAM,QAAQ,MAAM,iBAAiB,IAAI,EAAE,KAAK;AAChD,WAAO,SAAS;AAAA,EAClB;AACA,SAAO,gBAAgB,MAAM,WAAW,IAAI,KAAK,CAAC;AACpD;AAOA,IAAM,gBAAgB;AAWf,SAAS,8BACd,MACA,WACsB;AACtB,QAAM,YAAY,WAAW,cAAc,KAAK,gBAAgB;AAChE,QAAM,aACJ,aAAa,UAAU,KAAK,KAAK,CAAC,UAAU,SAAS,MAAM,IAAI,UAAU,KAAK,IAAI;AAEpF,MAAI,WAAW,WAAW;AAC1B,MAAI,YAAY,MAAM;AACpB,UAAM,MAAM,KAAK,qBAAqB;AACtC,UAAM,SAAS,MAAM,OAAO,WAAW,GAAG,IAAI,OAAO;AACrD,eAAW,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAAA,EAC9D;AAEA,SAAO,EAAE,YAAY,UAAU,YAAY,IAAI;AACjD;AAGO,SAAS,oBACd,IACA,WACsB;AACtB,QAAM,QAAQ,iBAAiB,EAAE;AACjC,QAAM,OAAoB,CAAC,SAAS;AAClC,UAAM,QAAQ,MAAM,iBAAiB,IAAI,EAAE,KAAK;AAChD,WAAO,SAAS;AAAA,EAClB;AACA,SAAO,8BAA8B,MAAM,SAAS;AACtD;;;AClNA,IAAM,QAAwB,CAAC,SAAS,UAAU,KAAK;AAGhD,SAAS,iBAAiB,MAAyC;AACxE,QAAM,IAAI,MAAM,QAAQ,IAAI;AAC5B,SAAO,KAAK,KAAK,IAAI,MAAM,SAAS,IAAI,MAAM,IAAI,CAAC,IAAK;AAC1D;AAQA,eAAsB,eACpB,QACA,SACA,QACuB;AACvB,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,QAAQ,MAAM,CAAC;AAC/C,WAAS,IAAI,OAAO,IAAI,MAAM,QAAQ,KAAK;AACzC,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,SAAS,MAAO;AACpB,UAAM,SAAS,SAAS,UAAU,QAAQ,QAAQ,QAAQ;AAC1D,QAAI,CAAC,OAAQ;AACb,QAAI;AACF,YAAM,YAAY,iBAAiB,IAAI,KAAK;AAC5C,YAAM,OAAO,MAAM;AACjB,aAAK,eAAe,WAAW,SAAS,MAAM;AAAA,MAChD,CAAC;AACD,aAAO,IAAI;AACX,aAAO;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,KAAK;AACZ,SAAO;AACT;;;AFyaY,gBAAAC,MAMA,QAAAC,aANA;AAlWZ,SAAS,yBAA4C;AACnD,QAAM,MAAM,OAAO,eAAe,cAAc,WAAW,6BAA6B;AACxF,QAAM,MAAM,oBAAI,IAAkB;AAClC,MAAI,OAAO,QAAQ,UAAU;AAC3B,eAAW,QAAQ,IAAI,MAAM,QAAQ,GAAG;AACtC,UAAI,SAAS,WAAW,SAAS,SAAU,KAAI,IAAI,IAAI;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAsBO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,eAAeC,SAA8B,IAAI;AACvD,QAAM,UAAUA,SAAyB,IAAI;AAC7C,QAAM,SAASA,SAA4B,IAAI;AAC/C,QAAM,cAAcA,SAA4B,IAAI;AACpD,QAAM,aAAaA,SAAoB,oBAAI,IAAI,CAAC;AAChD,QAAM,mBAAmBA,SAAO,KAAK;AACrC,QAAM,gBAAgBA,SAAO,KAAK;AAClC,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAS,KAAK;AACxC,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAGhD,QAAM,QAAQ,oBAAoB,cAAc,YAAY,QAAQ,oBAAoB,GAAG;AAE3F,QAAM,oBAAoBD,SAAiB,CAAC,CAAC;AAC7C,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,IAAI,kBAAkB;AAAA,IACpB,YAAY,QACR;AAAA,MACE,WAAW,mBAAoB;AAAA,MAC/B,KAAK,mBAAoB;AAAA,MACzB,OAAO,mBAAoB;AAAA,IAC7B,IACA;AAAA,IACJ,UAAU,CAAC,SAAS;AAClB,YAAM,OAAO,QAAQ;AACrB,UAAI,KAAM,MAAK,MAAM,IAAI;AAAA,UACpB,mBAAkB,QAAQ,KAAK,IAAI;AAAA,IAC1C;AAAA,EACF,CAAC;AAID,QAAM,UAAU,SAAS,cAAc;AACvC,QAAM,cAAc,CAAC,aAAa,WAAW,OAAO,UAAU;AAK9D,QAAM,OAAO,aAAa,UAAU,aAAa;AACjD,QAAM,UACJ,aACA,CAAC,WACD,CAAC,QACD,aAAa,QACb,aAAa,UACb,OAAO,OAAO,WAAW;AAE3B,WAAS,iBAAiB;AACxB,QAAI,CAAC,UAAW,cAAa,IAAI;AACjC,iBAAa;AAAA,EACf;AAKA,EAAAE,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,YAAa;AACnC,UAAM,KAAK,aAAa;AACxB,QAAI,CAAC,GAAI;AACT,QAAI,WAAW;AAIf,UAAM,cAAc,CAAC,SACnB,IAAI,QAAc,CAAC,YAAY;AAC7B,UAAI,KAAK,cAAc,KAAK,KAAK,eAAe,EAAG,QAAO,QAAQ;AAClE,YAAM,KAAK,IAAI,eAAe,MAAM;AAClC,YAAI,KAAK,cAAc,KAAK,KAAK,eAAe,GAAG;AACjD,aAAG,WAAW;AACd,kBAAQ;AAAA,QACV;AAAA,MACF,CAAC;AACD,SAAG,QAAQ,IAAI;AACf,iBAAW,MAAM;AACf,WAAG,WAAW;AACd,gBAAQ;AAAA,MACV,GAAG,GAAI;AAAA,IACT,CAAC;AAEH,uBAAmB;AAEnB,UAAM,YAAY;AAChB,YAAM,CAAC,EAAE,SAAS,GAAG,EAAE,SAAS,GAAG,EAAE,cAAc,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,QACxE,OAAO,cAAc;AAAA,QACrB,OAAO,kBAAkB;AAAA,QACzB,OAAO,wBAAwB;AAAA,MACjC,CAAC;AACD,UAAI,SAAU;AACd,YAAM,YAAY,EAAE;AACpB,UAAI,SAAU;AAKd,YAAM,OAAO,oBAAoB,IAAI,EAAE,YAAY,SAAS,CAAC;AAC7D,YAAM,eAAe,SAAS,qBAAqB,EAAE;AAErD,YAAM,OAAO,IAAI,SAAS;AAAA,QACxB,YAAY;AAAA,QACZ,cAAc,CAAC;AAAA,QACf,aAAa;AAAA,QACb,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,YAAY,KAAK;AAAA,QACjB,GAAI,eAAe,EAAE,OAAO,aAAa,IAAI,CAAC;AAAA,MAChD,CAAC;AACD,YAAM,MAAM,IAAI,SAAS;AACzB,WAAK,UAAU,GAAG;AAClB,WAAK;AAAA,QACH,IAAI;AAAA,UAAc,CAAC,IAAgB,QACjC,OAAO,KAAK,KAAK,UAAU,qBAAqB;AAAA,QAClD;AAAA,MACF;AACA,WAAK,KAAK,EAAE;AACZ,cAAQ,UAAU;AAClB,aAAO,UAAU;AAOjB,YAAM,UAAU,uBAAuB;AACvC,YAAM,UAA2B;AAAA,QAC/B,OAAO,OAAO,WAAW;AACvB,cAAI,QAAQ,IAAI,OAAO,EAAG,OAAM,IAAI,MAAM,sBAAsB;AAChE,gBAAM,EAAE,WAAW,IAAI,MAAM,OAAO,oBAAoB;AACxD,gBAAM,QAAQ,IAAI,WAAW;AAI7B,gBAAM,gBAAgB,MAAM;AAC1B,gBAAI;AACF,oBAAM,QAAQ;AAAA,YAChB,QAAQ;AAAA,YAER;AACA,mBAAO;AAAA,UACT,CAAC;AACD,eAAK,UAAU,KAAK;AACpB,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,eAAe,SAAS,SAAS,CAAC,SAAS;AAC/C,oBAAY,UAAU;AACtB,WAAG,aAAa,yBAAyB,IAAI;AAAA,MAC/C,CAAC;AACD,UAAI,SAAU;AAKd,YAAM,SAAS,MAAM;AACnB,YAAI;AACF,cAAI,IAAI;AAAA,QACV,QAAQ;AAAA,QAER;AAAA,MACF;AACA,aAAO;AACP,4BAAsB,MAAM;AAC1B,YAAI,SAAU;AACd,eAAO;AACP,YAAI,kBAAkB,QAAQ,SAAS,GAAG;AACxC,qBAAW,QAAQ,kBAAkB,QAAS,MAAK,MAAM,IAAI;AAC7D,4BAAkB,UAAU,CAAC;AAAA,QAC/B;AACA,WAAG,aAAa,sBAAsB,MAAM;AAC5C,YAAI,OAAO,WAAW,0BAA0B,YAAY;AAC1D,qBAAW,sBAAsB;AAAA,YAC/B,UAAU,YAAY;AAAA,YACtB,YAAY,KAAK,QAAQ;AAAA,YACzB,UAAU,KAAK,QAAQ;AAAA,YACvB,cAAc,OAAO,KAAK,QAAQ,cAAc,EAAE,EAAE,SAAS,MAAM;AAAA,YACnE,OAAO,KAAK,QAAQ;AAAA,YACpB;AAAA,UACF,CAAC;AAAA,QACH;AACA,iBAAS,IAAI;AAAA,MACf,CAAC;AAAA,IACH,GAAG;AAEH,WAAO,MAAM;AACX,iBAAW;AACX,cAAQ,SAAS,QAAQ;AACzB,cAAQ,UAAU;AAClB,aAAO,UAAU;AACjB,kBAAY,UAAU;AACtB,iBAAW,UAAU,oBAAI,IAAI;AAC7B,uBAAiB,UAAU;AAC3B,oBAAc,UAAU;AACxB,eAAS,KAAK;AAAA,IAChB;AAAA,EAGF,GAAG,CAAC,CAAC;AAIL,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,KAAM;AACrB,SAAK,QAAQ,eAAe,CAAC;AAC7B,SAAK,QAAQ,cAAc;AAAA,EAC7B,GAAG,CAAC,OAAO,WAAW,CAAC;AAIvB,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,KAAM;AACrB,UAAM,OAAO,SAAS,qBAAqB,aAAa,OAAO;AAC/D,QAAI,KAAM,MAAK,QAAQ,QAAQ;AAAA,EACjC,GAAG,CAAC,OAAO,KAAK,CAAC;AAMjB,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,KAAM;AACrB,QAAI,CAAC,SAAS;AACZ,UAAI,cAAc,SAAS;AACzB,sBAAc,UAAU;AACxB,YAAI,CAAC,iBAAiB,QAAS,MAAK,MAAM;AAAA,MAC5C;AACA;AAAA,IACF;AACA,kBAAc,UAAU;AACxB,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,QAAQ,MAAM;AAClB,YAAM,IAAI,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI,IAAI,SAAS,GAAI,CAAC;AAG7D,WAAK,MAAM,+DAAqD,CAAC,gBAAW;AAAA,IAC9E;AACA,SAAK,MAAM,MAAM;AACjB,UAAM;AACN,UAAM,KAAK,YAAY,OAAO,GAAI;AAClC,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,OAAO,OAAO,CAAC;AAKnB,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,QAAQ,QAAS;AAChC,eAAW,SAAS,OAAO,QAAQ;AACjC,UAAI,WAAW,QAAQ,IAAI,MAAM,EAAE,EAAG;AACtC,iBAAW,QAAQ,IAAI,MAAM,EAAE;AAC/B,uBAAiB,UAAU;AAC3B,WAAK,MAAM,MAAM,IAAI;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,OAAO,OAAO,QAAQ,OAAO,CAAC;AAIlC,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,YAAa;AACrC,UAAM,OAAO,UAAU,WAAW,OAAO;AACzC,QAAI,CAAC,KAAM;AACX,UAAM,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC;AAC5C,WAAO,MAAM,IAAI,QAAQ;AAAA,EAC3B,GAAG,CAAC,OAAO,aAAa,SAAS,UAAU,OAAO,KAAK,CAAC;AAGxD,EAAAA,YAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAS;AACjC,cAAU,KAAK,MAAM,KAAK,IAAI;AAC9B,UAAM,MAAM,KAAK,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,UAAU,MAAM,IAAI,CAAC;AACnE,WAAO,MAAM,IAAI,QAAQ;AAAA,EAC3B,GAAG,CAAC,OAAO,SAAS,cAAc,SAAS,CAAC;AAG5C,EAAAA,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,YAAa;AACnC,UAAM,QAAQ,MAAM;AAClB,UAAI;AACF,eAAO,SAAS,IAAI;AAAA,MACtB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,WAAO,iBAAiB,UAAU,KAAK;AACvC,QAAI,WAAkC;AACtC,UAAM,KAAK,aAAa;AACxB,QAAI,MAAM,OAAO,mBAAmB,aAAa;AAC/C,iBAAW,IAAI,eAAe,MAAM,MAAM,CAAC;AAC3C,eAAS,QAAQ,EAAE;AAAA,IACrB;AACA,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,KAAK;AAC1C,gBAAU,WAAW;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,SACE,gBAAAH,MAAC,SAAI,WAAW,GAAG,wDAAwD,SAAS,GACjF;AAAA,kBACC,gBAAAA,MAAC,SAAI,WAAU,qHACb;AAAA,sBAAAA,MAAC,UAAK,WAAU,qCACd;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,OAAO,UAAU,yBAAyB;AAAA,YAC5C;AAAA;AAAA,QACF;AAAA,QACA,gBAAAC,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,UAAM,SAAS;AAAA,WAAQ;AAAA,SACjE;AAAA,MACA,gBAAAA,MAAC,UAAK,WAAU,oCACb;AAAA,SAAC,eACA,gBAAAD,KAAC,UAAK,WAAU,kFAAiF,uBAEjG;AAAA,QAEF,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AACb,sBAAQ,SAAS,MAAM;AACvB,yBAAW,UAAU,oBAAI,IAAI;AAC7B,+BAAiB,UAAU;AAAA,YAC7B;AAAA,YACA,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,IAMF,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,sBAAsB;AAAA,QACtB,gBAAgB;AAAA,QAEf;AAAA,WAAC,UAAU,eAAe,gBAAAD,KAAC,uBAAoB;AAAA,UAGhD,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,WAAU;AAAA,cACV,OAAO,EAAE,YAAY,QAAQ,YAAY,SAAS;AAAA,cAClD,0BAAsB;AAAA;AAAA,UACxB;AAAA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;AASA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCvB,SAAS,qBAA2B;AAClC,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,eAAe,cAAc,EAAG;AAC7C,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cAAc;AACpB,WAAS,KAAK,YAAY,KAAK;AACjC;AAEA,SAAS,sBAAsB;AAC7B,SACE,gBAAAA,KAAC,SAAI,WAAU,kFAAiF,oCAEhG;AAEJ;;;AGplBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAIE,eAAAK;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AACP,SAAS,mBAAmB;AAC5B,SAAS,aAA+B;;;ACxBxC,SAAS,aAAAC,aAAW,WAAAC,gBAAe;AAOnC,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAmBd,SAAS,wBAAwB,QAAiC;AACvE,QAAM,UAAUC,SAAQ,MAAM,sBAAsB,MAAM,GAAG,CAAC,MAAM,CAAC;AACrE,EAAAC,YAAU,MAAM;AACd,QAAI,QAAQ,UAAU;AACpB,WAAK,OAAO,6CAAiC,EAC1C,KAAK,CAAC,EAAE,eAAe,MAAM,eAAe,QAAQ,QAAQ,CAAC,EAC7D,MAAM,MAAM,MAAS;AAAA,IAC1B;AACA,QAAI,QAAQ,QAAQ;AAClB,WAAK,OAAO,uCAA2B,EACpC,KAAK,CAAC,EAAE,mBAAmB,MAAM,mBAAmB,CAAC,EACrD,MAAM,MAAM,MAAS;AAAA,IAC1B;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,CAAC;AACvC;AAEA,SAAS,sBAAsB,QAG7B;AACA,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,SAAS,oBAAI,IAAY;AAC/B,aAAW,SAAS,QAAQ;AAC1B,eAAW,aAAa,OAAO;AAC7B,UAAI,eAAe,KAAK,SAAS,EAAG,UAAS,IAAI,SAAS;AAC1D,UAAI,aAAa,KAAK,SAAS,EAAG,QAAO,IAAI,SAAS;AAAA,IACxD;AAAA,EACF;AACA,SAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE;AAC1E;;;ADmiBa,SAoMD,YAAAC,WApMC,OAAAC,MA0DH,QAAAC,aA1DG;AA/hBb,IAAM,cAAmE;AAAA,EACvE,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AACb;AACA,IAAM,yBAAyB,OAAO,WAAW,cAAcC,cAAY;AAG3E,SAASC,UAAS,MAAsB;AACtC,QAAM,IAAI,KAAK,YAAY,GAAG;AAC9B,SAAO,KAAK,IAAI,KAAK,KAAK,MAAM,GAAG,CAAC;AACtC;AAGA,SAAS,SAAS,QAAgB,MAAsB;AACtD,SAAO,SAAS,GAAG,MAAM,IAAI,IAAI,KAAK;AACxC;AAGA,SAAS,QAAQ,MAA0B;AACzC,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,KAAK,KAAK;AAAA,IACnB,KAAK;AACH,aAAO,UAAU,KAAK,MAAM;AAAA,IAC9B,KAAK;AACH,aAAO,YAAY,KAAK,IAAI;AAAA,IAC9B,KAAK;AACH,aAAO,WAAW,KAAK,IAAI;AAAA,EAC/B;AACF;AAGA,SAAS,SAAS,OAAuB,MAAwC;AAC/E,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,KAAM,QAAO;AAC/B,QAAI,KAAK,SAAS,SAAS,KAAK,YAAY,KAAK,WAAW,GAAG,KAAK,IAAI,GAAG,GAAG;AAC5E,YAAM,MAAM,SAAS,KAAK,UAAU,IAAI;AACxC,UAAI,IAAK,QAAO;AAAA,IAClB;AAAA,EACF;AACA,SAAO;AACT;AA+BO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAsB,oBAAI,IAAI,CAAC;AAC/D,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAsB,oBAAI,IAAI,CAAC;AACvE,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAwB,IAAI;AACxD,QAAM,CAAC,aAAa,cAAc,IAAIA,WAA6B,IAAI;AACvE,QAAM,CAAC,aAAa,cAAc,IAAIA,WAA6B,IAAI;AACvE,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAwB,IAAI;AAC5D,QAAM,CAAC,MAAM,OAAO,IAAIA,WAAkC,IAAI;AAC9D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAA8B,IAAI;AAC5E,QAAM,CAAC,MAAM,OAAO,IAAIA,WAAS,KAAK;AACtC,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAS,KAAK;AACxD,QAAM,SAASC,OAAM;AACrB,QAAM,eAAeC,SAA8B,IAAI;AACvD,QAAM,uBAAuBA,SAA2B,IAAI;AAC5D,QAAM,WAAWA,SAAoB,IAAI;AACzC,QAAM,mBAAmB,oBAAoB,YAAY;AACzD,QAAM,iBAAiBC;AAAA,IACrB,MAAO,OAAO,aAAa,cAAc,OAAO,SAAS,eAAe,MAAM;AAAA,IAC9E,CAAC,MAAM;AAAA,EACT;AAEA,yBAAuB,MAAM;AAC3B,QAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,WAAY;AAC9E,UAAM,QAAQ,OAAO,WAAW,mBAAmB;AACnD,UAAM,SAAS,MAAM,iBAAiB,MAAM,OAAO;AACnD,WAAO;AACP,QAAI,OAAO,MAAM,qBAAqB,YAAY;AAChD,YAAM,iBAAiB,UAAU,MAAM;AACvC,aAAO,MAAM,MAAM,oBAAoB,UAAU,MAAM;AAAA,IACzD;AACA,QAAI,OAAO,MAAM,gBAAgB,YAAY;AAC3C,YAAM,YAAY,MAAM;AACxB,aAAO,MAAM,MAAM,eAAe,MAAM;AAAA,IAC1C;AAAA,EACF,GAAG,CAAC,CAAC;AAIL,QAAM,SAAS,UAAU,gBAAgB;AAEzC,QAAM,SAASA;AAAA,IACb,OAAO,SAAiB;AACtB,YAAM,OAAO,SAAS,OAAO,MAAM,IAAI;AACvC,UAAI,QAAQ,KAAK,SAAS,SAAS,KAAK,aAAa,QAAW;AAC9D,wBAAgB,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC;AACjD,YAAI;AACF,gBAAM,OAAO,OAAO,IAAI;AAAA,QAC1B,UAAE;AACA,0BAAgB,CAAC,SAAS;AACxB,kBAAM,OAAO,IAAI,IAAI,IAAI;AACzB,iBAAK,OAAO,IAAI;AAChB,mBAAO;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,OAAOA;AAAA,IACX,CAAC,SAAiB;AAChB,kBAAY,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI,CAAC;AAC7C,WAAK,OAAO,IAAI;AAAA,IAClB;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,SAAuB;AAC5B,UAAI,KAAK,SAAS,MAAO;AACzB,YAAM,SAAS,SAAS,IAAI,KAAK,IAAI;AACrC,kBAAY,CAAC,SAAS;AACpB,cAAM,OAAO,IAAI,IAAI,IAAI;AACzB,YAAI,OAAQ,MAAK,OAAO,KAAK,IAAI;AAAA,YAC5B,MAAK,IAAI,KAAK,IAAI;AACvB,eAAO;AAAA,MACT,CAAC;AACD,UAAI,CAAC,OAAQ,OAAM,OAAO,KAAK,IAAI;AAAA,IACrC;AAAA,IACA,CAAC,UAAU,MAAM;AAAA,EACnB;AAMA,QAAM,OAAO,OAAO,WAAW;AAC/B,QAAM,cAAcC,SAAQ,MAAM;AAChC,UAAM,QAAsB,CAAC;AAC7B,QAAI,eAAe,YAAY,WAAW,IAAI;AAC5C,YAAM,KAAK,EAAE,MAAM,UAAU,QAAQ,IAAI,MAAM,YAAY,MAAM,OAAO,EAAE,CAAC;AAAA,IAC7E;AACA,UAAM,OAAO,CAAC,OAAuB,UAAkB;AACrD,iBAAW,QAAQ,OAAO;AACxB,cAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,MAAM,CAAC;AACxC,YAAI,KAAK,SAAS,SAAS,CAAC,SAAS,IAAI,KAAK,IAAI,EAAG;AACrD,YAAI,eAAe,YAAY,WAAW,KAAK,MAAM;AACnD,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,QAAQ,KAAK;AAAA,YACb,MAAM,YAAY;AAAA,YAClB,OAAO,QAAQ;AAAA,UACjB,CAAC;AAAA,QACH;AACA,cAAM,YAAY,KAAK,aAAa,UAAa,KAAK,SAAS,WAAW;AAC1E,cAAM,YAAY,aAAa,IAAI,KAAK,IAAI,KAAK,OAAO,eAAe,IAAI,KAAK,IAAI;AAGpF,YAAI,KAAK,aAAa,aAAa,QAAQ,CAAC,WAAW;AACrD,gBAAM,KAAK,EAAE,MAAM,WAAW,MAAM,KAAK,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,QACnE,WAAW,KAAK,YAAY,KAAK,SAAS,SAAS,GAAG;AACpD,eAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,QAC/B,WAAW,aAAa,WAAW;AACjC,gBAAM,KAAK,EAAE,MAAM,YAAY,MAAM,KAAK,MAAM,OAAO,QAAQ,EAAE,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AACA,SAAK,OAAO,MAAM,CAAC;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,MAAM,UAAU,aAAa,cAAc,OAAO,gBAAgB,IAAI,CAAC;AAClF,QAAM,mBAAmBA;AAAA,IACvB,MAAM,YAAY,QAAQ,CAAC,SAAU,KAAK,SAAS,SAAS,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAE;AAAA,IAClF,CAAC,WAAW;AAAA,EACd;AACA,0BAAwB,gBAAgB;AAGxC,QAAM,WAAWA;AAAA,IACf,MACE,YAAY;AAAA,MAAQ,CAAC,SACnB,KAAK,SAAS,SAAS,CAAC,EAAE,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC;AAAA,IACrE;AAAA,IACF,CAAC,WAAW;AAAA,EACd;AACA,QAAM,iBAAiBA;AAAA,IACrB,MAAM,IAAI,IAAI,SAAS,IAAI,CAAC,KAAK,UAAU,CAAC,IAAI,KAAK,MAAM,KAAK,CAAU,CAAC;AAAA,IAC3E,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,qBAAqBA,SAAQ,MAAM;AACvC,UAAM,YAAY,oBAAI,IAAmD;AACzE,UAAM,QAAQ,CAAC,UAA0B;AACvC,YAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,kBAAU,IAAI,KAAK,MAAM,EAAE,UAAU,QAAQ,GAAG,SAAS,MAAM,OAAO,CAAC;AACvE,YAAI,KAAK,SAAU,OAAM,KAAK,QAAQ;AAAA,MACxC,CAAC;AAAA,IACH;AACA,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,IAAI,CAAC;AAChB,QAAM,cAAc,SAAU,eAAe,IAAI,MAAM,KAAK,KAAM;AAClE,QAAM,qBAAqB,eAAe,IAAI,GAAG,MAAM,SAAS,WAAW,KAAK;AAKhF,QAAM,mBACJ,YAAY,OAAO,WAAW,UAAU,OAAO,UAAU,QAAQ,CAAC,OAAO;AAE3E,QAAM,gBAAgBD;AAAA,IACpB,OAAO,SAAuB;AAC5B,UAAI,CAAC,iBAAkB;AACvB,YAAM,YAAY,KAAK,SAAS;AAChC,cAAQ,IAAI;AACZ,UAAI;AACF,cAAM,OAAO,YAAY,KAAK,MAAM,SAAS;AAAA,MAC/C,QAAQ;AAAA,MAER,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,kBAAkB,MAAM;AAAA,EAC3B;AAEA,QAAM,YAAYA;AAAA,IAChB,OAAO,MAAoB,gBAA8B;AACvD,UAAI,CAAC,iBAAkB;AACvB,cAAQ,IAAI;AACZ,UAAI,CAAC,eAAe;AAClB,6BAAqB,UACnB,gBACC,OAAO,aAAa,eAAe,SAAS,yBAAyB,cAClE,SAAS,gBACT;AACN,yBAAiB,IAAI;AACrB;AAAA,MACF;AACA,UAAI,MAAM,cAAc,IAAI,EAAG,OAAM,cAAc,IAAI;AAAA,IACzD;AAAA,IACA,CAAC,kBAAkB,eAAe,aAAa;AAAA,EACjD;AAEA,QAAM,qBAAqBA;AAAA,IACzB,CAAC,mBAA4B;AAC3B,YAAM,eAAe,iBAAiB,qBAAqB,UAAU,eAAe;AACpF,2BAAqB,UAAU;AAC/B,uBAAiB,IAAI;AACrB,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,sBAAsB,MAAM;AACjC,gBAAM,SAAS,cAAc,cAAc,eAAe,eAAe;AACzE,kBAAQ,MAAM;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,CAAC,cAAc;AAAA,EACjB;AAEA,QAAM,eAAeA;AAAA,IACnB,OAAO,SAAiB;AACtB,YAAM,QAAQ;AACd,qBAAe,IAAI;AACnB,UAAI,CAAC,SAAS,CAAC,iBAAkB;AACjC,YAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAC9C,UAAI,CAAC,QAAS;AACd,YAAM,OAAO,SAAS,MAAM,QAAQ,OAAO;AAC3C,cAAQ,IAAI;AACZ,UAAI;AACF,YAAI,MAAM,SAAS,OAAO;AACxB,gBAAM,OAAO,UAAU,IAAI;AAC3B,eAAK,IAAI;AAAA,QACX,OAAO;AACL,gBAAM,OAAO,WAAW,IAAI;AAE5B,oBAAU,IAAI;AACd,yBAAe,IAAI;AAAA,QACrB;AAAA,MACF,QAAQ;AAAA,MAER,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,aAAa,kBAAkB,QAAQ,MAAM,YAAY;AAAA,EAC5D;AAEA,QAAM,eAAeA;AAAA,IACnB,OAAO,SAAiB;AACtB,YAAM,QAAQ;AACd,qBAAe,IAAI;AACnB,UAAI,CAAC,SAAS,CAAC,iBAAkB;AACjC,YAAM,OAAO,SAAS,OAAO,MAAM,MAAM,IAAI;AAC7C,YAAM,UAAU,KAAK,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAC9C,UAAI,CAAC,QAAQ,CAAC,WAAW,YAAY,KAAK,KAAM;AAChD,YAAM,UAAU,SAASJ,UAAS,MAAM,IAAI,GAAG,OAAO;AACtD,cAAQ,IAAI;AACZ,UAAI;AACF,cAAM,OAAO,UAAU,MAAM,MAAM,OAAO;AAC1C,YAAI,KAAK,SAAS,WAAW,iBAAiB,MAAM,QAAQ,WAAW,MAAM,OAAO;AAClF,oBAAU,OAAO;AACjB,yBAAe,OAAO;AAAA,QACxB;AAAA,MACF,QAAQ;AAAA,MAER,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,aAAa,kBAAkB,QAAQ,cAAc,QAAQ,YAAY;AAAA,EAC5E;AAGA,QAAM,cAAcI;AAAA,IAClB,CAAC,SAAyB;AACxB,UAAI,CAAC,iBAAkB;AACvB,YAAM,SAAS,SAAS,SAAS,OAAO,MAAM,MAAM,IAAI;AACxD,YAAM,SAAS,SAAU,OAAO,SAAS,QAAQ,OAAO,OAAOJ,UAAS,OAAO,IAAI,IAAK;AACxF,UAAI,OAAQ,MAAK,MAAM;AACvB,qBAAe,IAAI;AACnB,cAAQ,IAAI;AACZ,qBAAe,EAAE,QAAQ,KAAK,CAAC;AAAA,IACjC;AAAA,IACA,CAAC,kBAAkB,QAAQ,OAAO,MAAM,IAAI;AAAA,EAC9C;AAEA,QAAM,cAAcI;AAAA,IAClB,CAAC,SAAuB;AACtB,UAAI,CAAC,iBAAkB;AACvB,qBAAe,IAAI;AACnB,cAAQ,IAAI;AACZ,qBAAe,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,IACpC;AAAA,IACA,CAAC,gBAAgB;AAAA,EACnB;AAGA,QAAM,aAAaA;AAAA,IACjB,OAAO,WAAmB,eAAuB;AAC/C,kBAAY,IAAI;AAChB,UAAI,CAAC,oBAAoB,CAAC,WAAY;AACtC,YAAM,MAAM,SAAS,OAAO,MAAM,UAAU;AAC5C,UAAI,CAAC,IAAK;AAEV,UAAIJ,UAAS,UAAU,MAAM,UAAW;AACxC,UAAI,cAAc,cAAc,UAAU,WAAW,GAAG,UAAU,GAAG,EAAG;AACxE,YAAM,UAAU,SAAS,WAAW,IAAI,IAAI;AAC5C,UAAI,YAAY,WAAY;AAC5B,cAAQ,IAAI;AACZ,UAAI;AACF,cAAM,OAAO,UAAU,YAAY,OAAO;AAC1C,YAAI,IAAI,SAAS,WAAW,iBAAiB,cAAc,WAAW,aAAa;AACjF,oBAAU,OAAO;AACjB,yBAAe,OAAO;AAAA,QACxB;AAAA,MACF,QAAQ;AAAA,MAER,UAAE;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAAA,IACA,CAAC,kBAAkB,QAAQ,cAAc,QAAQ,YAAY;AAAA,EAC/D;AAEA,QAAM,YAAYI;AAAA,IAChB,CAAC,MAA0C;AACzC,UAAI,eAAe,YAAa;AAChC,UAAI,SAAS,WAAW,EAAG;AAC3B,YAAM,MAAM,SAAS,UAAU,CAAC,MAAM,EAAE,KAAK,SAAS,MAAM;AAC5D,YAAM,OAAO,CAAC,SAAiB;AAC7B,cAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,SAAS,GAAG,IAAI,CAAC;AAC/D,cAAM,MAAM,SAAS,OAAO;AAC5B,YAAI,IAAK,WAAU,IAAI,KAAK,IAAI;AAChC,UAAE,eAAe;AAAA,MACnB;AACA,YAAM,MAAM,OAAO,IAAI,SAAS,GAAG,IAAI;AACvC,cAAQ,EAAE,KAAK;AAAA,QACb,KAAK;AACH,eAAK,MAAM,IAAI,IAAI,MAAM,CAAC;AAC1B;AAAA,QACF,KAAK;AACH,eAAK,MAAM,IAAI,IAAI,MAAM,CAAC;AAC1B;AAAA,QACF,KAAK;AACH,eAAK,CAAC;AACN;AAAA,QACF,KAAK;AACH,eAAK,SAAS,SAAS,CAAC;AACxB;AAAA,QACF,KAAK,YAAY;AACf,gBAAM,WAAW,KAAK;AAAA,YACpB;AAAA,YACA,KAAK,OAAO,aAAa,SAAS,gBAAgB,QAAQ,gBAAgB,KAAK,GAAG,IAAI;AAAA,UACxF;AACA,gBAAM,MAAM,IAAI,IAAI,OAAO,QAAQ;AACnC;AAAA,QACF;AAAA,QACA,KAAK,UAAU;AACb,gBAAM,WAAW,KAAK;AAAA,YACpB;AAAA,YACA,KAAK,OAAO,aAAa,SAAS,gBAAgB,QAAQ,gBAAgB,KAAK,GAAG,IAAI;AAAA,UACxF;AACA,gBAAM,MAAM,IAAI,IAAI,OAAO,QAAQ;AACnC;AAAA,QACF;AAAA,QACA,KAAK;AACH,cAAI,KAAK,KAAK,SAAS,OAAO;AAC5B,gBAAI,CAAC,SAAS,IAAI,IAAI,KAAK,IAAI,EAAG,MAAK,IAAI,KAAK,IAAI;AAAA,gBAC/C,MAAK,MAAM,CAAC;AACjB,cAAE,eAAe;AAAA,UACnB;AACA;AAAA,QACF,KAAK;AACH,cAAI,KAAK;AACP,gBAAI,IAAI,KAAK,SAAS,SAAS,SAAS,IAAI,IAAI,KAAK,IAAI,GAAG;AAC1D,0BAAY,CAAC,SAAS;AACpB,sBAAM,IAAI,IAAI,IAAI,IAAI;AACtB,kBAAE,OAAO,IAAI,KAAK,IAAI;AACtB,uBAAO;AAAA,cACT,CAAC;AAAA,YACH,OAAO;AACL,oBAAM,SAASJ,UAAS,IAAI,KAAK,IAAI;AACrC,kBAAI,OAAQ,WAAU,MAAM;AAAA,YAC9B;AACA,cAAE,eAAe;AAAA,UACnB;AACA;AAAA,QACF,KAAK;AACH,cAAI,KAAK;AACP,gBAAI,IAAI,KAAK,SAAS,MAAO,MAAK,OAAO,IAAI,IAAI;AAAA,gBAC5C,gBAAe,IAAI,KAAK,IAAI;AACjC,cAAE,eAAe;AAAA,UACnB;AACA;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,cAAI,OAAO,qBAAqB,EAAE,QAAQ,iBAAiB,EAAE,WAAW;AACtE,kBAAM,WAAW,eAAe,IAAI,IAAI,KAAK,IAAI,KAAK;AACtD,kBAAM,MAAM,SAAS,eAAe,GAAG,MAAM,SAAS,QAAQ,EAAE;AAChE,kBAAM,OAAO,KAAK,sBAAsB;AACxC,oBAAQ;AAAA,cACN,MAAM,IAAI;AAAA,cACV,GAAG,OAAO,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,QAAQ,CAAC,IAAI;AAAA,cACrD,GAAG,OAAO,KAAK,SAAS;AAAA,YAC1B,CAAC;AACD,cAAE,eAAe;AAAA,UACnB,WAAW,EAAE,QAAQ,OAAO;AAC1B;AAAA,UACF;AACA;AAAA,QACF,KAAK;AACH,cAAI,KAAK;AACP,wBAAY,IAAI,IAAI;AACpB,cAAE,eAAe;AAAA,UACnB;AACA;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,cAAI,OAAO,kBAAkB;AAC3B,iBAAK,UAAU,IAAI,IAAI;AACvB,cAAE,eAAe;AAAA,UACnB;AACA;AAAA,MACJ;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,EAAAD,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,UAAM,QAAQ,MAAM,QAAQ,IAAI;AAChC,WAAO,iBAAiB,SAAS,KAAK;AACtC,WAAO,iBAAiB,UAAU,OAAO,IAAI;AAC7C,WAAO,iBAAiB,UAAU,KAAK;AACvC,WAAO,MAAM;AACX,aAAO,oBAAoB,SAAS,KAAK;AACzC,aAAO,oBAAoB,UAAU,OAAO,IAAI;AAChD,aAAO,oBAAoB,UAAU,KAAK;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,iBAAiB,OAAO,SAAS,OAAO,KAAK,WAAW,KAAK,CAAC;AACpE,QAAM,mBAAmB,OAAO,SAAS,OAAO,KAAK,SAAS;AAC9D,QAAM,YAAY,CAAC,OAAO,WAAW,OAAO,KAAK,WAAW,KAAK,CAAC;AAGlE,QAAM,cAAc,oBAAoB,OAAO;AAC/C,QAAM,kBAAkB,CAAC,kBAAkB,CAAC;AAI5C,QAAM,gBAAgB,CAAC,MAAoB,UAA6B;AACtE,UAAM,SAAS,SAAS,IAAI,KAAK,IAAI;AACrC,QAAI,YAAY;AACd,aAAO,gBAAAF,KAAC,SAAK,qBAAW,MAAM,OAAO,MAAM,GAAE;AAAA,IAC/C;AACA,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,aAAa,KAAK,SAAS,gBAAgB,KAAK,SAAS;AAC/D,UAAM,WAAW,KAAK,SAAS;AAC/B,UAAM,YAAY,aAAa,IAAI,KAAK,IAAI,KAAK,OAAO,eAAe,IAAI,KAAK,IAAI;AACpF,UAAM,aAAa,aAAa,SAAS,KAAK;AAC9C,UAAM,eAAe,cAAc,QAAQ,KAAK,OAAOG,UAAS,KAAK,IAAI;AACzE,UAAM,UAAU,QAAQ,KAAK,OAAOA,UAAS,KAAK,IAAI;AACtD,UAAM,eAAe,mBAAmB,IAAI,KAAK,IAAI;AAErD,WACE,gBAAAH;AAAA,MAAC;AAAA;AAAA,QACC,IAAI,GAAG,MAAM,SAAS,eAAe,IAAI,KAAK,IAAI,KAAK,CAAC;AAAA,QACxD,MAAK;AAAA,QACL,cAAY,QAAQ;AAAA,QACpB,iBAAe,cAAc;AAAA,QAC7B,gBAAc,cAAc;AAAA,QAC5B,iBAAe,QAAQ,SAAS;AAAA,QAChC,iBAAe,YAAY;AAAA,QAC3B,aAAW,aAAa;AAAA,QACxB,YACE,mBACI,CAAC,MAAM;AACL,YAAE,eAAe;AACjB,YAAE,aAAa,aAAa;AAC5B,sBAAY,OAAO;AAAA,QACrB,IACA;AAAA,QAEN,aACE,mBACI,CAAC,MAAM;AACL,YAAE,gBAAgB;AAClB,sBAAY,CAAC,QAAS,QAAQ,UAAU,OAAO,GAAI;AAAA,QACrD,IACA;AAAA,QAEN,QACE,mBACI,CAAC,MAAM;AACL,YAAE,eAAe;AACjB,YAAE,gBAAgB;AAClB,gBAAM,MAAM,EAAE,aAAa,QAAQ,cAAc;AACjD,eAAK,WAAW,SAAS,GAAG;AAAA,QAC9B,IACA;AAAA,QAGL,uBACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,MAAM,KAAK;AAAA,YACX,cAAc,KAAK;AAAA,YACnB,UAAU,CAAC,SAAS,KAAK,aAAa,IAAI;AAAA,YAC1C,UAAU,MAAM,eAAe,IAAI;AAAA;AAAA,QACrC,IAEA,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAU;AAAA,YACV,WAAW,oBAAoB;AAAA,YAC/B,aACE,mBACI,CAAC,MAAM;AACL,gBAAE,aAAa,QAAQ,gBAAgB,KAAK,IAAI;AAChD,gBAAE,aAAa,gBAAgB;AAAA,YACjC,IACA;AAAA,YAEN,SAAS,MAAM;AACb,wBAAU,KAAK,IAAI;AACnB,kBAAI,MAAO,MAAK,OAAO,IAAI;AAAA,kBACtB,gBAAe,KAAK,IAAI;AAAA,YAC/B;AAAA,YACA,eAAe,MAAM,oBAAoB,YAAY,IAAI;AAAA,YACzD,eAAe,CAAC,MAAM;AACpB,kBAAI,CAAC,iBAAkB;AACvB,gBAAE,eAAe;AACjB,wBAAU,KAAK,IAAI;AACnB,sBAAQ,EAAE,MAAM,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAAA,YAC9C;AAAA,YACA,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,cAAc;AAAA,cACd,YAAY;AAAA,cACZ,gBAAgB;AAAA;AAAA,cAEhB,aACE;AAAA,cACF,KAAK,SAAS,YAAY,KAAK,MAAM,IAAI;AAAA,YAC3C;AAAA,YACA,OAAO,EAAE,aAAa,GAAG,QAAQ,KAAK,CAAC,KAAK;AAAA,YAE3C;AAAA,sBACC,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,UAAU;AAAA,oBACV,aAAa;AAAA,kBACf;AAAA;AAAA,cACF,IAEA,gBAAAA,KAAC,UAAK,WAAU,6BAA4B;AAAA,cAE7C,QACC,gBAAAA,KAAC,cAAW,WAAU,qBAAoB,IAE1C,gBAAAA,KAAC,YAAS,WAAU,qBAAoB;AAAA,cAE1C,gBAAAA,KAAC,UAAK,WAAU,YAAY,eAAK,MAAK;AAAA,cACrC,oBACC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,UAAU;AAAA,kBACV,cAAW;AAAA,kBACX,SAAS,CAAC,MAAM;AACd,sBAAE,eAAe;AACjB,sBAAE,gBAAgB;AAClB,8BAAU,KAAK,IAAI;AACnB,0BAAM,OAAQ,EAAE,cAA8B,sBAAsB;AACpE,4BAAQ,EAAE,MAAM,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO,CAAC;AAAA,kBACjD;AAAA,kBACA,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA;AAAA;AAAA,QAEJ;AAAA;AAAA,IAEJ;AAAA,EAEJ;AAGA,QAAM,aAAa,CAAC,SAAgC;AAClD,YAAQ,KAAK,MAAM;AAAA,MACjB,KAAK;AACH,eAAO,cAAc,KAAK,MAAM,KAAK,KAAK;AAAA,MAC5C,KAAK;AACH,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,KAAK;AAAA,YACZ,MAAM,KAAK;AAAA,YACX,cAAa;AAAA,YACb,UAAU,CAAC,SAAS,KAAK,aAAa,IAAI;AAAA,YAC1C,UAAU,MAAM,eAAe,IAAI;AAAA;AAAA,QACrC;AAAA,MAEJ,KAAK;AACH,eACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,eAAW;AAAA,YACX,OAAO,EAAE,aAAa,GAAG,KAAK,QAAQ,KAAK,CAAC,KAAK;AAAA,YACjD,WAAU;AAAA,YAET,WAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MACd,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAU;AAAA,gBACV,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,EAAE,IAAI;AAAA;AAAA,cAF7B;AAAA,YAGP,CACD;AAAA;AAAA,QACH;AAAA,MAEJ,KAAK;AACH,eACE,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,EAAE,aAAa,GAAG,KAAK,QAAQ,KAAK,CAAC,KAAK;AAAA,YACjD,WAAU;AAAA,YAEV;AAAA,8BAAAD,KAAC,UAAK,WAAU,6BAA4B;AAAA,cAC5C,gBAAAA,KAAC,UAAK,WAAU,oBAAmB,uDAAoC;AAAA;AAAA;AAAA,QACzE;AAAA,IAEN;AAAA,EACF;AAGA,EAAAE,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,YAAY,UAAU,CAAC,OAAO,GAAG,SAAS,UAAU,GAAG,KAAK,SAAS,MAAM;AACzF,QAAI,SAAS,EAAG,UAAS,SAAS,cAAc,KAAK;AAAA,EACvD,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,SACE,gBAAAD,MAAC,SAAI,WAAW,GAAG,iCAAiC,SAAS,GAC1D;AAAA,mBACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAW;AAAA,QACX,WAAU;AAAA,QAET;AAAA,6BACC,gBAAAA,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,iBAAc,OAAM,YAAW,SAAS,MAAM,YAAY,MAAM,GAAG,UAAU,MAC5E,0BAAAA,KAAC,gBAAa,WAAU,YAAW,GACrC;AAAA,YACA,gBAAAA,KAAC,iBAAc,OAAM,cAAa,SAAS,MAAM,YAAY,KAAK,GAAG,UAAU,MAC7E,0BAAAA,KAAC,kBAAe,WAAU,YAAW,GACvC;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAM;AAAA,gBACN,SAAS,MAAM;AACb,wBAAM,OAAO,SAAS,SAAS,OAAO,MAAM,MAAM,IAAI;AACtD,sBAAI,KAAM,aAAY,IAAI;AAAA,gBAC5B;AAAA,gBACA,UAAU,QAAQ,CAAC;AAAA,gBAEnB,0BAAAA,KAAC,cAAW,WAAU,YAAW;AAAA;AAAA,YACnC;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAM;AAAA,gBACN,SAAS,CAAC,UAAU;AAClB,wBAAM,OAAO,SAAS,SAAS,OAAO,MAAM,MAAM,IAAI;AACtD,sBAAI,KAAM,MAAK,UAAU,MAAM,MAAM,aAAa;AAAA,gBACpD;AAAA,gBACA,UAAU,QAAQ,CAAC;AAAA,gBAEnB,0BAAAA,KAAC,cAAW,WAAU,YAAW;AAAA;AAAA,YACnC;AAAA,aACF,IACE;AAAA,UACJ,gBAAAA,KAAC,UAAK,WAAU,WAAU;AAAA,UAC1B,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,SAAS,MAAM,KAAK,OAAO,QAAQ;AAAA,cACnC,UAAU,QAAQ,OAAO;AAAA,cAEzB,0BAAAA,KAAC,iBAAc,WAAW,GAAG,YAAY,OAAO,WAAW,cAAc,GAAG;AAAA;AAAA,UAC9E;AAAA;AAAA;AAAA,IACF;AAAA,IAGD,mBACC,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,aAAU;AAAA,QACV,gCAA4B;AAAA,QAC5B,WAAU;AAAA,QAEV;AAAA,0BAAAD,KAAC,qBAAkB,WAAU,4CAA2C,eAAW,MAAC;AAAA,UACpF,gBAAAA,KAAC,UAAK,WAAU,kBACb,iBAAO,WAAW,YACf,kFACA,uDACN;AAAA,UACA,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,KAAK,OAAO,QAAQ;AAAA,cACnC,UAAU,OAAO;AAAA,cACjB,WAAU;AAAA,cAEV;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW,GAAG,UAAU,OAAO,WAAW,yCAAyC;AAAA,oBACnF,eAAW;AAAA;AAAA,gBACb;AAAA,gBAAE;AAAA;AAAA;AAAA,UAEJ;AAAA;AAAA;AAAA,IACF,IACE;AAAA,IAIJ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,IAAI,kBAAkB,SAAY;AAAA,QAClC,MAAM,kBAAkB,SAAY;AAAA,QACpC,cAAY,kBAAkB,SAAY;AAAA,QAC1C,yBAAuB,kBAAkB,SAAY;AAAA,QACrD,UAAU,kBAAkB,SAAY;AAAA,QACxC,wBAAsB,kBAAkB,SAAY;AAAA,QACpD,SAAS,CAAC,UAAU;AAClB,cAAK,MAAM,OAAuB,OAAO,UAAU,cAAc,GAAG;AAClE,kBAAM,QAAQ,SAAS,CAAC;AACxB,gBAAI,MAAO,WAAU,MAAM,KAAK,IAAI;AAAA,UACtC;AAAA,QACF;AAAA,QACA;AAAA,QACA,YACE,mBACI,CAAC,MAAM;AACL,YAAE,eAAe;AACjB,YAAE,aAAa,aAAa;AAC5B,sBAAY,EAAE;AAAA,QAChB,IACA;AAAA,QAEN,QACE,mBACI,CAAC,MAAM;AACL,YAAE,eAAe;AACjB,gBAAM,MAAM,EAAE,aAAa,QAAQ,cAAc;AACjD,eAAK,WAAW,IAAI,GAAG;AAAA,QACzB,IACA;AAAA,QAEN,WAAW;AAAA,UACT;AAAA,UACA,aAAa,MAAM;AAAA,QACrB;AAAA,QACA,2BAAuB;AAAA,QAEtB,2BACC,gBAAAC,MAAC,SAAI,WAAU,oEACb;AAAA,0BAAAD,KAAC,SACE,sBACC,yBAAyB,OAAO,OAAO,WAAW,oCAAoC,IAC1F;AAAA,UACA,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,KAAK,OAAO,QAAQ;AAAA,cACnC,UAAU,OAAO;AAAA,cACjB,WAAU;AAAA,cAEV;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAW,GAAG,YAAY,OAAO,WAAW,cAAc;AAAA,oBAC1D,eAAW;AAAA;AAAA,gBACb;AAAA,gBAAE;AAAA;AAAA;AAAA,UAEJ;AAAA,WACF,IACE,YACF,gBAAAA,KAAC,SAAI,WAAU,oCACZ,wBAAc,2BACjB;AAAA;AAAA;AAAA;AAAA,UAKA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,IAAI;AAAA,cACJ,MAAK;AAAA,cACL,cAAW;AAAA,cACX,yBAAuB;AAAA,cACvB,wBAAsB;AAAA,cACtB,UAAU;AAAA,cACV,WAAU;AAAA,cACV,UAAU,gBAAgB,KAAK;AAAA,cAC/B,UAAU,KAAK,IAAI,IAAI,YAAY,MAAM;AAAA,cAExC,sBAAY,IAAI,CAAC,SAChB,gBAAAA,KAAC,SAAwB,WAAU,WAChC,qBAAW,IAAI,KADR,QAAQ,IAAI,CAEtB,CACD;AAAA;AAAA,UACH;AAAA;AAAA;AAAA,IAEJ;AAAA,IAEC,QAAQ,KAAK,QACZ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,KAAK;AAAA,QACX,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR,UAAU,MAAM,KAAK,QAAQ,YAAY,KAAK,IAAI;AAAA,QAClD,UAAU,MAAM,KAAK,QAAQ,KAAK,UAAU,KAAK,IAAI;AAAA,QACrD,WAAW,MAAM,YAAY,MAAM;AAAA,QACnC,aAAa,MAAM,YAAY,KAAK;AAAA,QACpC,SAAS,MAAM;AACb,kBAAQ,IAAI;AACZ,iBAAO,sBAAsB,MAAM,eAAe,GAAG,MAAM,CAAC;AAAA,QAC9D;AAAA;AAAA,IACF;AAAA,IAED,gBACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,UAAU,MAAM,mBAAmB,IAAI;AAAA,QACvC,WAAW,MAAM;AACf,gBAAM,OAAO;AACb,6BAAmB,KAAK;AACxB,eAAK,cAAc,IAAI;AAAA,QACzB;AAAA;AAAA,IACF,IACE;AAAA,KACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,YAAYM,SAAiC,IAAI;AACvD,QAAM,CAAC,MAAM,OAAO,IAAIF,WAAS,IAAI;AACrC,QAAM,iBAAiBE,SAA6B,QAAQ;AAC5D,QAAM,aAAaA,SAAO,KAAK;AAC/B,QAAM,iBAAiBA,SAA6C,IAAI;AAExE,QAAM,SAAS,MAAM;AACnB,QAAI,WAAW,QAAS;AACxB,eAAW,UAAU;AACrB,QAAI,eAAe,YAAY,WAAW;AACxC,gBAAU;AAAA,IACZ,OAAO;AACL,eAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM;AAC3B,QAAI,eAAe,YAAY,KAAM;AACrC,mBAAe,UAAU,WAAW,MAAM;AACxC,qBAAe,UAAU;AACzB,aAAO;AAAA,IACT,GAAG,CAAC;AAAA,EACN;AAEA,EAAAJ;AAAA,IACE,MAAM,MAAM;AACV,UAAI,eAAe,YAAY,KAAM,cAAa,eAAe,OAAO;AAAA,IAC1E;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SACE,gBAAAF;AAAA,IAAC,YAAY;AAAA,IAAZ;AAAA,MACC;AAAA,MACA,cAAc,CAAC,aAAa;AAC1B,YAAI,CAAC,YAAY,CAAC,MAAM;AACtB,kBAAQ,KAAK;AACb,yBAAe;AAAA,QACjB;AAAA,MACF;AAAA,MAEA,0BAAAC,MAAC,YAAY,QAAZ,EAAmB,WAAW,OAAO,aAAa,cAAc,OAAO,SAAS,MAC/E;AAAA,wBAAAD,KAAC,YAAY,SAAZ,EAAoB,WAAU,yDAAwD;AAAA,QACvF,gBAAAC;AAAA,UAAC,YAAY;AAAA,UAAZ;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,YACP,iBAAiB,CAAC,UAAU;AAC1B,oBAAM,eAAe;AACrB,wBAAU,SAAS,MAAM;AAAA,YAC3B;AAAA,YACA,kBAAkB,CAAC,UAAU;AAC3B,oBAAM,eAAe;AACrB,6BAAe;AAAA,YACjB;AAAA,YACA,iBAAiB,CAAC,UAAU;AAC1B,oBAAM,gBAAgB;AACtB,kBAAI,MAAM;AACR,sBAAM,eAAe;AAAA,cACvB,OAAO;AACL,+BAAe,UAAU;AAAA,cAC3B;AAAA,YACF;AAAA,YAEA;AAAA,8BAAAA,MAAC,SAAI,WAAU,0BACb;AAAA,gCAAAD,KAAC,UAAK,WAAU,8GACd,0BAAAA,KAAC,cAAW,WAAU,UAAS,eAAW,MAAC,GAC7C;AAAA,gBACA,gBAAAC,MAAC,SAAI,WAAU,WACb;AAAA,kCAAAA,MAAC,YAAY,OAAZ,EAAkB,WAAU,4BAA2B;AAAA;AAAA,oBAC9C,KAAK,SAAS,QAAQ,WAAW;AAAA,oBAAO;AAAA,qBAClD;AAAA,kBACA,gBAAAA,MAAC,YAAY,aAAZ,EAAwB,WAAU,oDACjC;AAAA,oCAAAD,KAAC,UAAK,WAAU,qCAAqC,eAAK,MAAK;AAAA,oBAC9D,KAAK,SAAS,QACX,2DACA;AAAA,qBACN;AAAA,mBACF;AAAA,iBACF;AAAA,cACA,gBAAAC,MAAC,SAAI,WAAU,+BACb;AAAA,gCAAAD,KAAC,YAAY,QAAZ,EAAmB,SAAO,MACzB,0BAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,KAAK;AAAA,oBACL,MAAK;AAAA,oBACL,SAAS,MAAM;AACb,qCAAe,UAAU;AAAA,oBAC3B;AAAA,oBACA,UAAU;AAAA,oBACV,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED,GACF;AAAA,gBACA,gBAAAA,KAAC,YAAY,QAAZ,EAAmB,SAAO,MACzB,0BAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM;AACb,qCAAe,UAAU;AAAA,oBAC3B;AAAA,oBACA,UAAU;AAAA,oBACV,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED,GACF;AAAA,iBACF;AAAA;AAAA;AAAA,QACF;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,OAAO;AAAA,MACP,cAAY;AAAA,MACZ;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAGA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,CAAC,OAAO,QAAQ,IAAII,WAAS,YAAY;AAC/C,QAAM,MAAME,SAAgC,IAAI;AAEhD,QAAM,UAAUA,SAAO,KAAK;AAE5B,EAAAJ,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,OAAG,MAAM;AAET,UAAM,MAAM,aAAa,YAAY,GAAG;AACxC,QAAI,gBAAgB,MAAM,EAAG,IAAG,kBAAkB,GAAG,GAAG;AAAA,QACnD,IAAG,OAAO;AAAA,EACjB,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,SAAS,CAAC,WAAoB;AAClC,QAAI,QAAQ,QAAS;AACrB,YAAQ,UAAU;AAClB,QAAI,OAAQ,UAAS,KAAK;AAAA,QACrB,UAAS;AAAA,EAChB;AAEA,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,aAAa,GAAG,QAAQ,KAAK,CAAC,KAAK;AAAA,MAE5C;AAAA,wBAAAD,KAAC,UAAK,WAAU,6BAA4B;AAAA,QAC3C,SAAS,QACR,gBAAAA,KAAC,cAAW,WAAU,qBAAoB,IAE1C,gBAAAA,KAAC,YAAS,WAAU,qBAAoB;AAAA,QAE1C,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,YAAY;AAAA,YACZ,cAAa;AAAA,YACb,cAAY,SAAS,QAAQ,gBAAgB;AAAA,YAC7C,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YACxC,QAAQ,MAAM,OAAO,IAAI;AAAA,YACzB,WAAW,CAAC,MAAM;AAChB,kBAAI,EAAE,QAAQ,SAAS;AACrB,kBAAE,eAAe;AACjB,uBAAO,IAAI;AAAA,cACb,WAAW,EAAE,QAAQ,UAAU;AAC7B,kBAAE,eAAe;AACjB,uBAAO,KAAK;AAAA,cACd;AACA,gBAAE,gBAAgB;AAAA,YACpB;AAAA,YACA,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAGA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,QAAQ,KAAK,SAAS;AAC5B,QAAM,UAAUM,SAA8B,IAAI;AAElD,QAAM,KAAK,OAAO,WAAW,cAAc,OAAO,aAAa;AAC/D,QAAM,KAAK,OAAO,WAAW,cAAc,OAAO,cAAc;AAChE,QAAM,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG,CAAC;AAC9C,QAAM,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG,CAAC;AAE7C,EAAAJ,YAAU,MAAM;AACd,YAAQ,SAAS,cAAiC,mBAAmB,GAAG,MAAM;AAAA,EAChF,GAAG,CAAC,CAAC;AAEL,QAAM,YAAY,CAAC,UAA8C;AAC/D,UAAM,QAAQ,MAAM;AAAA,MAClB,QAAQ,SAAS,iBAAoC,mBAAmB,KAAK,CAAC;AAAA,IAChF;AACA,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,UAAU,MAAM,UAAU,CAACO,UAASA,UAAS,SAAS,aAAa;AACzE,QAAI,OAAsB;AAC1B,QAAI,MAAM,QAAQ,YAAa,SAAQ,UAAU,IAAI,MAAM,UAAU,MAAM;AAAA,aAClE,MAAM,QAAQ,UAAW,SAAQ,UAAU,IAAI,MAAM,UAAU,MAAM;AAAA,aACrE,MAAM,QAAQ,OAAQ,QAAO;AAAA,aAC7B,MAAM,QAAQ,MAAO,QAAO,MAAM,SAAS;AAAA,aAC3C,MAAM,QAAQ,UAAU;AAC/B,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,SAAS,KAAM;AACnB,UAAM,eAAe;AACrB,UAAM,gBAAgB;AACtB,UAAM,IAAI,GAAG,MAAM;AAAA,EACrB;AAEA,QAAM,OAAO,CAAC,OAAe,MAAiB,SAAqB,WACjE,gBAAAR;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,MAAK;AAAA,MACL,SAAS,CAAC,MAAM;AACd,UAAE,gBAAgB;AAClB,gBAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,SAAS,0BAA0B;AAAA,MACrC;AAAA,MAEC;AAAA;AAAA,QACA;AAAA;AAAA;AAAA,EACH;AAGF,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,MAAK;AAAA,MACL,cAAY,eAAe,KAAK,IAAI;AAAA,MACpC;AAAA,MACA,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,MAClC,OAAO,EAAE,MAAM,IAAI;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA,iBACC,gBAAAA,MAAAF,WAAA,EACG;AAAA,eAAK,YAAY,gBAAAC,KAAC,gBAAa,WAAU,YAAW,GAAI,SAAS;AAAA,UACjE,KAAK,cAAc,gBAAAA,KAAC,kBAAe,WAAU,YAAW,GAAI,WAAW;AAAA,UACxE,gBAAAA,KAAC,SAAI,MAAK,aAAY,WAAU,0BAAyB;AAAA,WAC3D;AAAA,QAED,KAAK,UAAU,gBAAAA,KAAC,cAAW,WAAU,YAAW,GAAI,QAAQ;AAAA,QAC5D,KAAK,UAAU,gBAAAA,KAAC,cAAW,WAAU,YAAW,GAAI,UAAU,IAAI;AAAA;AAAA;AAAA,EACrE;AAEJ;;;AEnyCA,SAAS,kBAAkB,iBAAiB,mBAAmB;AAC/D;AAAA,EAEE,eAAAU;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AACP,SAAS,SAAAC,cAAa;;;ACZtB,SAAS,eAAAC,eAAa,aAAAC,aAAW,WAAAC,UAAS,UAAAC,UAAQ,YAAAC,kBAAgB;AAmC3D,SAAS,mBACd,SACA,WACA,UACA,UACa;AACb,QAAM,IAAI,QAAQ;AAClB,MAAI,MAAM,EAAG,QAAO,EAAE,OAAO,GAAG,KAAK,EAAE;AACvC,QAAM,MAAM,KAAK,IAAI,WAAW,CAAC;AACjC,QAAM,SAAS,MAAM,KAAK,IAAI,UAAU,CAAC;AAEzC,MAAI,eAAe;AACnB,MAAI,cAAc;AAClB,MAAI,MAAM;AACV,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,SAAS;AACf,UAAM,YAAY,SAAS,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,CAAC;AACtD,QAAI,UAAU,IAAK,UAAS;AAG5B,QAAI,YAAY,OAAO,SAAS,QAAQ;AACtC,UAAI,iBAAiB,GAAI,gBAAe;AACxC,oBAAc;AAAA,IAChB;AACA,UAAM;AAAA,EACR;AAEA,MAAI,iBAAiB,IAAI;AACvB,mBAAe;AACf,kBAAc;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,OAAO,KAAK,IAAI,GAAG,eAAe,QAAQ;AAAA,IAC1C,KAAK,KAAK,IAAI,GAAG,cAAc,IAAI,QAAQ;AAAA,EAC7C;AACF;AA0BO,SAAS,oBAAoB,MAIf;AACnB,QAAM,EAAE,OAAO,gBAAgB,WAAW,EAAE,IAAI;AAChD,QAAM,YAAYD,SAA8B,IAAI;AAEpD,QAAM,CAAC,SAAS,UAAU,IAAIC;AAAA,IAAmB,MAC/C,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,MAAM,eAAe,CAAC,CAAC;AAAA,EAC3D;AAIA,EAAAH,YAAU,MAAM;AACd,eAAW,CAAC,SAAS;AACnB,UAAI,KAAK,WAAW,MAAO,QAAO;AAClC,aAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,MAAM,KAAK,CAAC,KAAK,eAAe,CAAC,CAAC;AAAA,IAC7E,CAAC;AAAA,EAGH,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,aAAaE,SAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAsB,OAAO;AAAA,IACrD,OAAO;AAAA,IACP,KAAK,KAAK,IAAI,OAAO,WAAW,CAAC;AAAA,EACnC,EAAE;AAEF,QAAM,YAAYJ,cAAY,MAAM;AAClC,UAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AACT,UAAM,OAAO,mBAAmB,WAAW,SAAS,GAAG,WAAW,GAAG,cAAc,QAAQ;AAC3F,aAAS,CAAC,SAAU,KAAK,UAAU,KAAK,SAAS,KAAK,QAAQ,KAAK,MAAM,OAAO,IAAK;AAAA,EACvF,GAAG,CAAC,QAAQ,CAAC;AAGb,EAAAC,YAAU,MAAM;AACd,UAAM,KAAK,UAAU;AACrB,QAAI,CAAC,GAAI;AACT,QAAI,MAAM;AACV,UAAM,WAAW,MAAM;AACrB,UAAI,IAAK;AACT,YACE,OAAO,0BAA0B,aAC7B,sBAAsB,MAAM;AAC1B,cAAM;AACN,kBAAU;AAAA,MACZ,CAAC,KACA,UAAU,GAAG;AAAA,IACtB;AACA,OAAG,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC;AACzD,UAAM,KAAK,OAAO,mBAAmB,cAAc,IAAI,eAAe,MAAM,UAAU,CAAC,IAAI;AAC3F,QAAI,QAAQ,EAAE;AACd,cAAU;AACV,WAAO,MAAM;AACX,SAAG,oBAAoB,UAAU,QAAQ;AACzC,UAAI,WAAW;AACf,UAAI,OAAO,OAAO,yBAAyB,WAAY,sBAAqB,GAAG;AAAA,IACjF;AAAA,EACF,GAAG,CAAC,WAAW,KAAK,CAAC;AAErB,QAAM,UAAUD,cAAY,CAAC,OAAe,WAAmB;AAC7D,QAAI,UAAU,EAAG;AACjB,eAAW,CAAC,SAAS;AACnB,UAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,IAAI,EAAG,QAAO;AACtD,YAAM,OAAO,KAAK,MAAM;AACxB,WAAK,KAAK,IAAI;AACd,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,UAAUE,SAAQ,MAAM;AAC5B,UAAM,MAAM,IAAI,MAAc,QAAQ,CAAC;AACvC,QAAI,CAAC,IAAI;AACT,aAAS,IAAI,GAAG,IAAI,OAAO,IAAK,KAAI,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG,CAAC;AACxF,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,KAAK,CAAC;AAInB,EAAAD,YAAU,MAAM;AACd,cAAU;AAAA,EACZ,GAAG,CAAC,SAAS,SAAS,CAAC;AAEvB,QAAM,gBAAgBD;AAAA,IACpB,CAAC,UAAkB;AACjB,YAAM,KAAK,UAAU;AACrB,UAAI,CAAC,GAAI;AACT,YAAM,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,GAAG,KAAK,CAAC;AACtD,SAAG,YAAY,QAAQ,OAAO,KAAK;AAAA,IACrC;AAAA,IACA,CAAC,SAAS,KAAK;AAAA,EACjB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,QAAQ,KAAK,KAAK;AAAA,IAC/B;AAAA,IACA;AAAA,EACF;AACF;;;ADsFU,SAakB,OAAAK,MAblB,QAAAC,aAAA;AA1PV,IAAM,kBAAkB;AAExB,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,UAAU;AAChB,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB,OAAO,WAAW,cAAcC,cAAYC;AAE3E,IAAMC,eAAqD;AAAA,EACzD,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AACd;AAEA,IAAM,gBAAuD;AAAA,EAC3D,OAAO;AAAA,EACP,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,YAAY;AACd;AAEA,SAAS,iBAAiB,MAAkC;AAC1D,QAAM,QAAQ,KAAK,KAAK,YAAY,GAAG;AACvC,QAAM,WAAW,SAAS,IAAI,KAAK,KAAK,MAAM,QAAQ,CAAC,IAAI,KAAK;AAChE,QAAM,SAAS,SAAS,IAAI,KAAK,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI;AAC5D,SAAO,GAAG,cAAc,KAAK,MAAM,CAAC,SAAM,QAAQ,GAAG,SAAS,WAAM,MAAM,KAAK,EAAE;AACnF;AAwBO,SAAS,UAAU,OAIxB;AACA,QAAM,QAAQ,IAAI;AAAA,IAChB,MAAM,QAAQ,CAAC,SAAU,KAAK,aAAa,SAAY,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAE;AAAA,EAC9E;AACA,QAAM,cAAc,MAAM,OAAO;AACjC,MAAI,CAAC,eAAe,MAAM,UAAU,iBAAiB;AACnD,WAAO;AAAA,MACL,cAAc;AAAA,MACd,MAAM,MAAM,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE;AAAA,MAChE,SAAS;AAAA,IACX;AAAA,EACF;AACA,QAAM,SAAS,oBAAI,IAAkC;AACrD,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,KAAK,QAAQ,GAAG;AACnC,UAAM,MAAM,cACR,KAAK,YAAY,gBACjB,QAAQ,IACN,KAAK,KAAK,MAAM,GAAG,KAAK,IACxB;AACN,UAAM,SAAS,OAAO,IAAI,GAAG;AAC7B,QAAI,OAAQ,QAAO,KAAK,IAAI;AAAA,QACvB,QAAO,IAAI,KAAK,CAAC,IAAI,CAAC;AAAA,EAC7B;AACA,QAAM,SAAS,CAAC,GAAG,OAAO,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AACnE,QAAM,eAAqC,CAAC;AAC5C,QAAM,OAAkB,CAAC;AACzB,aAAW,SAAS,QAAQ;AAC1B,UAAM,UAAU,OAAO,IAAI,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC5F,SAAK,KAAK,EAAE,MAAM,SAAS,OAAO,OAAO,OAAO,OAAO,CAAC;AACxD,eAAW,QAAQ,QAAQ;AACzB,WAAK,KAAK,EAAE,MAAM,QAAQ,MAAM,OAAO,aAAa,OAAO,CAAC;AAC5D,mBAAa,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AACA,SAAO,EAAE,cAAc,MAAM,SAAS,KAAK;AAC7C;AAIA,SAAS,UAAU,MAA4B;AAC7C,SAAO,KAAK,YAAY,KAAK;AAC/B;AAIA,SAAS,sBAAsB,MAA2B;AACxD,MAAI,UAAU,IAAI,EAAG,QAAO,YAAY;AACxC,MAAI,QAAQ;AACZ,aAAW,QAAQ,KAAK,MAAO,UAAS,KAAK,MAAM,SAAS;AAC5D,SAAO,YAAY,KAAK,IAAI,OAAO,CAAC,IAAI,UAAU;AACpD;AAEO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,UAAUC,SAA8B,IAAI;AAClD,QAAM,CAAC,SAAS,UAAU,IAAIC,WAAS,KAAK;AAC5C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAS,KAAK;AACxD,QAAM,gBAAgB,aAAa,SAAS;AAC5C,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA8B,SAAS;AACnE,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAChE,QAAM,WAAWC,OAAM;AAEvB,QAAM,mBAAmBC;AAAA,IACvB,MAAM,KAAK,QAAQ,CAAC,SAAU,KAAK,UAAU,CAAC,KAAK,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,IAAI,CAAE;AAAA,IACrF,CAAC,IAAI;AAAA,EACP;AACA,0BAAwB,gBAAgB;AAExC,QAAM,EAAE,cAAc,MAAM,QAAQ,IAAIA,SAAQ,MAAM,UAAU,IAAI,GAAG,CAAC,IAAI,CAAC;AAC7E,QAAM,gBAAgB,aAAa,UAAU,CAAC,SAAS,KAAK,SAAS,UAAU;AAC/E,QAAM,cAAc,iBAAiB,IAAI,gBAAgB;AAEzD,yBAAuB,MAAM;AAC3B,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,QAAQ,OAAO,mBAAmB,YAAa;AACpD,UAAM,SAAS,CAAC,UAAkB;AAGhC,UAAI,QAAQ,EAAG,YAAW,QAAQ,kBAAkB;AAAA,IACtD;AACA,WAAO,KAAK,sBAAsB,EAAE,KAAK;AACzC,UAAM,WAAW,IAAI,eAAe,CAAC,YAAY;AAC/C,aAAO,QAAQ,CAAC,GAAG,YAAY,SAAS,KAAK,sBAAsB,EAAE,KAAK;AAAA,IAC5E,CAAC;AACD,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,CAAC;AAEL,yBAAuB,MAAM;AAC3B,QAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,WAAY;AAC9E,UAAM,QAAQ,OAAO,WAAW,mBAAmB;AACnD,UAAM,SAAS,MAAM,iBAAiB,MAAM,OAAO;AACnD,WAAO;AACP,QAAI,OAAO,MAAM,qBAAqB,YAAY;AAChD,YAAM,iBAAiB,UAAU,MAAM;AACvC,aAAO,MAAM,MAAM,oBAAoB,UAAU,MAAM;AAAA,IACzD;AACA,QAAI,OAAO,MAAM,gBAAgB,YAAY;AAC3C,YAAM,YAAY,MAAM;AACxB,aAAO,MAAM,MAAM,eAAe,MAAM;AAAA,IAC1C;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,YAAYA,SAAQ,MAAM,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AACrF,QAAM,YAAYA,SAAQ,MAAM,KAAK,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC;AAErF,QAAM,iBAAiBC;AAAA,IACrB,CAAC,UAAkB;AACjB,YAAM,OAAO,aAAa,KAAK;AAC/B,aAAO,OAAO,sBAAsB,IAAI,IAAI,YAAY;AAAA,IAC1D;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,WAAW,oBAAoB;AAAA,IACnC,OAAO,aAAa;AAAA,IACpB;AAAA,IACA,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,SAASA;AAAA,IACb,CAAC,UAAkB;AACjB,oBAAc,aAAa,KAAK,GAAG,QAAQ,IAAI;AAC/C,eAAS,cAAc,KAAK;AAAA,IAC9B;AAAA,IACA,CAAC,cAAc,QAAQ;AAAA,EACzB;AAGA,QAAM,eAAeA,cAAY,MAAM;AACrC,UAAM,KAAK,SAAS,UAAU;AAC9B,QAAI,CAAC,IAAI;AACP;AAAA,IACF;AACA,UAAM,MAAM,GAAG;AACf,UAAM,UAAU,SAAS;AACzB,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC5C,WAAK,QAAQ,CAAC,KAAK,MAAM,MAAM,EAAG,OAAM;AAAA,UACnC;AAAA,IACP;AACA,UAAM,WAAW,aAAa,GAAG,GAAG,QAAQ;AAC5C,kBAAc,CAAC,aAAc,aAAa,WAAW,WAAW,QAAS;AAAA,EAC3E,GAAG,CAAC,UAAU,YAAY,CAAC;AAE3B,EAAAP,YAAU,MAAM;AACd,UAAM,KAAK,SAAS,UAAU;AAC9B,QAAI,CAAC,GAAI;AACT,OAAG,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAC7D,WAAO,MAAM,GAAG,oBAAoB,UAAU,YAAY;AAAA,EAC5D,GAAG,CAAC,SAAS,WAAW,YAAY,CAAC;AAErC,QAAM,cAAc,eAAe,QAAQ,YAAY,mBAAmB,IAAI;AAC9E,QAAM,aAAa,aAAa,WAAW,KAAK,aAAa,CAAC;AAE9D,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,GAAG,wCAAwC,SAAS;AAAA,MAC/D,iCAA+B,UAAU,YAAY;AAAA,MAGrD;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,WAAW,WAAW,aAAa;AAAA,YACrC;AAAA,YAEA;AAAA,8BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,eAAY;AAAA,kBACZ,aAAU;AAAA,kBACV,yBAAqB;AAAA,kBACrB,WAAU;AAAA,kBAET;AAAA,yBAAK;AAAA,oBAAO;AAAA,oBAAE,KAAK,WAAW,IAAI,SAAS;AAAA,oBAAQ;AAAA,oBACpD,gBAAAA,MAAC,UAAK,yBAAqB,MAAC,WAAU,4BAA2B;AAAA;AAAA,sBAC7D;AAAA,uBACJ;AAAA,oBACA,gBAAAA,MAAC,UAAK,yBAAqB,MAAC,WAAU,8BAA6B;AAAA;AAAA,sBAC/D;AAAA,uBACJ;AAAA;AAAA;AAAA,cACF;AAAA,cACA,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAW;AAAA,oBACT;AAAA,oBACA,WAAW,WAAW,aAAa;AAAA,kBACrC;AAAA,kBAEC;AAAA,8BAAU,OAAO,gBAAAD,KAAC,gBAAa,QAAgB,UAAU,WAAW;AAAA,oBACrE,gBAAAA,KAAC,eAAY,QAAgB,YAAwB,OAAO,aAAa;AAAA;AAAA;AAAA,cAC3E;AAAA;AAAA;AAAA,QACF;AAAA,QAEC,UACC,gBAAAC,MAAC,SAAI,WAAU,gCACb;AAAA,0BAAAA,MAAC,SAAI,WAAU,0DACb;AAAA,4BAAAD,KAAC,WAAM,WAAU,WAAU,SAAS,UAAU,0BAE9C;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI;AAAA,gBACJ,cAAW;AAAA,gBACX,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU;AACnB,wBAAM,QAAQ,OAAO,MAAM,cAAc,KAAK;AAC9C,gCAAc,aAAa,KAAK,GAAG,QAAQ,IAAI;AAAA,gBACjD;AAAA,gBACA,OAAO,YAAY;AAAA,gBACnB,4BAAwB;AAAA,gBACxB,WAAU;AAAA,gBAET,uBAAa,IAAI,CAAC,MAAM,UACvB,gBAAAA,KAAC,YAAuB,OAAO,OAC5B,2BAAiB,IAAI,KADX,KAAK,IAElB,CACD;AAAA;AAAA,YACH;AAAA,aACF;AAAA,UACA,gBAAAA,KAAC,SAAI,WAAU,wCAAuC,8BAA0B,MAC7E,uBACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,QAAO;AAAA,cACP,WAAW;AAAA,cACV,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,UACtC,IACE,MACN;AAAA,WACF,IAEA,gBAAAC,MAAC,SAAI,WAAU,uBAEb;AAAA,0BAAAD,KAAC,SAAI,WAAU,oFACb,0BAAAA;AAAA,YAACU;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,UAAU,gBAAgB,KAAK;AAAA,cAC/B,UAAU,KAAK,IAAI,IAAI,KAAK,MAAM;AAAA,cAEjC,eAAK;AAAA,gBAAI,CAAC,QACT,IAAI,SAAS,UACX,gBAAAT;AAAA,kBAAC;AAAA;AAAA,oBAEC,mBAAe;AAAA,oBACf,WAAU;AAAA,oBAEV;AAAA,sCAAAD,KAAC,UAAK,WAAU,oBAAoB,cAAI,OAAM;AAAA,sBAC9C,gBAAAA,KAAC,UAAK,WAAU,YAAY,cAAI,OAAM;AAAA;AAAA;AAAA,kBALjC,KAAK,IAAI,KAAK;AAAA,gBAMrB,IAEA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBAEC,MAAM,IAAI;AAAA,oBACV;AAAA,oBACA,QAAQ,IAAI,UAAU;AAAA,oBACtB,SAAS,MAAM,OAAO,IAAI,KAAK;AAAA;AAAA,kBAJ1B,IAAI,KAAK;AAAA,gBAKhB;AAAA,cAEJ;AAAA;AAAA,UACF,GACF;AAAA,UAKA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK,SAAS;AAAA,cACd,WAAU;AAAA,cACV,8BAA0B;AAAA,cAE1B,0BAAAA,KAAC,SAAI,OAAO,EAAE,UAAU,YAAY,QAAQ,SAAS,YAAY,GAC9D,uBAAa,IAAI,CAAC,MAAM,UAAU;AACjC,oBAAI,QAAQ,SAAS,MAAM,SAAS,SAAS,SAAS,MAAM,IAAK,QAAO;AACxE,uBACE,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBAEC;AAAA,oBACA,KAAK,SAAS,QAAQ,KAAK,KAAK;AAAA,oBAChC,WAAW,SAAS;AAAA,oBAEpB,0BAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC;AAAA,wBACA;AAAA,wBACA,WAAW;AAAA,wBACV,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,oBACtC;AAAA;AAAA,kBAVK,KAAK;AAAA,gBAWZ;AAAA,cAEJ,CAAC,GACH;AAAA;AAAA,UACF;AAAA,WACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAQA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,WAAW,aAAa,YAAY;AACtC,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,IAAI,KAAK,UAAU,EAAE,eAAe;AAAA,QAE3C;AAAA,0BAAAD,KAAC,eAAY,WAAU,0CAAyC,eAAW,MAAC;AAAA,UAC3E;AAAA;AAAA;AAAA,IACH;AAAA,EAEJ;AACA,SACE,gBAAAA,KAAC,UAAK,WAAU,2EACb,iBACH;AAEJ;AAGA,SAAS,eACP,QACA,YACA,UACQ;AACR,MAAI,WAAW,aAAa,CAAC,WAAY,QAAO;AAChD,QAAM,OAAO,WAAW,YAAY,KAAK,IAAI,CAAC;AAC9C,SAAO,aAAa,OAAO,cAAc,QAAQ,SAAM,IAAI,KAAK,SAAS,IAAI;AAC/E;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AAGD,QAAM,aAAa,KAAK,WAAW,GAAG,KAAK,QAAQ,MAAM;AACzD,QAAM,QAAQ,UACV,KAAK,aAAa,SAChB,cAAc,KAAK,KAAK,WAAW,UAAU,IAC3C,KAAK,KAAK,MAAM,WAAW,MAAM,IACjC,KAAK,OACP,KAAK,KAAK,MAAM,KAAK,KAAK,QAAQ,GAAG,IAAI,CAAC,KAAK,KAAK,OACtD,KAAK;AACT,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,OAAO,KAAK;AAAA,MACZ,kBAAc;AAAA,MACd,WAAW;AAAA,QACT;AAAA,QACA,WAAW;AAAA,QACX,UACE;AAAA,MACJ;AAAA,MAEA;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,yBAAqB;AAAA,YACrB,WAAW,GAAG,oDAAoDI,aAAY,KAAK,MAAM,CAAC;AAAA,YAEzF,wBAAc,KAAK,MAAM;AAAA;AAAA,QAC5B;AAAA,QACA,gBAAAJ,KAAC,UAAK,WAAU,2BAA2B,iBAAM;AAAA,QACjD,gBAAAC,MAAC,UAAK,WAAU,yEACd;AAAA,0BAAAA,MAAC,UAAK,yBAAqB,MAAC,WAAU,uBAAsB;AAAA;AAAA,YACxD,KAAK;AAAA,aACT;AAAA,UACA,gBAAAA,MAAC,UAAK,yBAAqB,MAAC,WAAU,yBAAwB;AAAA;AAAA,YAC1D,KAAK;AAAA,aACT;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAKA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,MAAMI,SAAuB,IAAI;AACvC,EAAAH,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,UAAM,SAAS,MAAM,UAAU,OAAO,GAAG,YAAY;AACrD,WAAO;AACP,UAAM,KAAK,OAAO,mBAAmB,cAAc,IAAI,eAAe,MAAM,IAAI;AAChF,QAAI,QAAQ,EAAE;AACd,WAAO,MAAM,IAAI,WAAW;AAAA,EAC9B,GAAG,CAAC,OAAO,SAAS,CAAC;AACrB,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,qBAAiB;AAAA,MACjB,mBAAiB;AAAA,MACjB,OAAO,EAAE,UAAU,YAAY,KAAK,MAAM,GAAG,OAAO,EAAE;AAAA,MAErD;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AAID,MAAI,UAAU,IAAI,GAAG;AACnB,UAAM,UAAU,KAAK,WAAW,KAAK,YAAY,KAAK;AACtD,WACE,gBAAAC,MAAC,aAAQ,WAAU,kCACjB;AAAA,sBAAAA,MAAC,YAAO,WAAU,kFAChB;AAAA,wBAAAD,KAAC,UAAK,WAAU,4CACb,oBAAU,GAAG,KAAK,OAAO,WAAM,KAAK,IAAI,KAAK,KAAK,MACrD;AAAA,QACA,gBAAAC,MAAC,UAAK,WAAU,4DACd;AAAA,0BAAAA,MAAC,UAAK,WAAU,uBAAsB;AAAA;AAAA,YAAE,KAAK;AAAA,aAAU;AAAA,UACvD,gBAAAA,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,YAAE,KAAK;AAAA,aAAU;AAAA,WAC3D;AAAA,SACF;AAAA,MACA,gBAAAD,KAAC,aAAU,MAAa,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC,GAAI;AAAA,OACjE;AAAA,EAEJ;AACA,SACE,gBAAAA,KAAC,aAAQ,uBAAmB,MAAC,WAAU,kCACrC,0BAAAA,KAAC,cAAW,MAAM,CAAC,IAAI,GAAG,QAAgB,WAAsB,WAAU,QAAO,GACnF;AAEJ;AAIA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AACF,GAGG;AACD,QAAM,SAAS,KAAK,WAAW,gBAAgB;AAC/C,SACE,gBAAAC,MAAC,SAAI,WAAU,4EACb;AAAA,oBAAAD,KAAC,mBAAgB,WAAU,qBAAoB,eAAW,MAAC;AAAA,IAC3D,gBAAAC,MAAC,UAAK,WAAU,kBAAkB;AAAA;AAAA,MAAO;AAAA,OAAC;AAAA,IACzC,aACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,WAAW,KAAK,IAAI;AAAA,QACnC,WAAU;AAAA,QACX;AAAA;AAAA,UAEC,gBAAAD,KAAC,oBAAiB,WAAU,YAAW,eAAW,MAAC;AAAA;AAAA;AAAA,IACrD,IACE;AAAA,KACN;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AACF,GAGG;AACD,SACE,gBAAAA,KAAC,SAAI,WAAU,wEACX,WAAC,WAAW,OAAO,EAAY,IAAI,CAAC,UACpC,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAEC,MAAK;AAAA,MACL,SAAS,MAAM,SAAS,KAAK;AAAA,MAC7B,WAAW;AAAA,QACT;AAAA,QACA,WAAW,QACP,iCACA;AAAA,MACN;AAAA,MAEC;AAAA;AAAA,IAVI;AAAA,EAWP,CACD,GACH;AAEJ;;;AEtkBM,gBAAAW,YAAA;AAXC,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AACF,GAAkB;AAChB,MAAI,KAAK,WAAW,GAAG;AACrB,WACE,gBAAAA,KAAC,SAAI,WAAW,GAAG,oCAAoC,SAAS,GAC7D,yBAAe,SAAS,eAAe,0BAC1C;AAAA,EAEJ;AACA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACC,GAAI,aAAa,SAAY,EAAE,SAAS,IAAI,CAAC;AAAA,MAC9C;AAAA;AAAA,EACF;AAEJ;;;ACnEA;AAAA,EAIE;AAAA,EACA;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AA4EuC,gBAAAC,YAAA;AAvC9C,IAAM,WAAW,KAAK,YAAY;AAChC,QAAM,MAAO,MAAM,OAAO,qBAAqB;AAC/C,SAAO,EAAE,SAAS,IAAI,KAAK;AAC7B,CAAC;AAQM,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAAS,KAAK;AAI1C,EAAAC,YAAU,MAAM;AACd,QAAI,YAAY;AAChB,SAAK,OAAO,qBAAqB,EAAE,MAAM,MAAM;AAC7C,UAAI,CAAC,UAAW,WAAU,IAAI;AAAA,IAChC,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,OAAO,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AAEtD,MAAI,QAAQ;AACV,WACE,gBAAAF,KAAC,SAAI,WAAuB,sBAAY,gBAAAA,KAAC,aAAU,MAAY,UAAoB,GAAG;AAAA,EAE1F;AAEA,QAAM,UAAU;AAAA,IACd,UAAU;AAAA,IACV,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,GAAI,QACA,EAAE,MAAM,IACR;AAAA,MACE,OAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACJ,WAAW,aAAa;AAAA,EAC1B;AAIA,QAAM,aAAa;AAAA,IACjB,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,8BAA8B;AAAA,IAC9B,iCAAiC;AAAA,IACjC,qBAAqB;AAAA,IACrB,uBAAuB;AAAA,EACzB;AAEA,SACE,gBAAAA,KAAC,SAAI,WAAW,GAAG,WAAW,SAAS,GAAG,6BAAyB,MAAC,OAAO,YACzE,0BAAAA,KAAC,YAAS,UAAU,WAAW,gBAAAA,KAAC,gBAAa,GAG3C,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,EAAE,MAAM,SAAS;AAAA,MACvB;AAAA,MACC,GAAI,sBAAsB,SAAY,EAAE,kBAAkB,IAAI,CAAC;AAAA;AAAA,EAClE,GACF,GACF;AAEJ;AAEA,SAAS,UAAU,EAAE,MAAM,SAAS,GAAuC;AACzE,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,aAAW;AAAA,MAEV;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,eAAe;AACtB,SAAO,gBAAAA,KAAC,SAAI,WAAU,oCAAmC,gCAAa;AACxE;;;AC9IA,SAAS,aAAa,gBAAgB;AACtC;AAAA,EAIE,eAAAG;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AAkTC,gBAAAC,MAOA,QAAAC,aAPA;AA/QR,IAAM,mBAA2D;AAAA,EAC/D,YAAY,aACT,MAAM,OAAO,6BAA6B,GAAG,WAAW,EAAE,KAAK,MAAM,YAAY,KAAK,CAAC;AAAA,EAC1F,MAAM,aAAa,MAAM,OAAO,uBAAuB,GAAG,KAAK;AAAA,EAC/D,QAAQ,aAAa,MAAM,OAAO,yBAAyB,GAAG,OAAO;AAAA,EACrE,UAAU,aAAa,MAAM,OAAO,2BAA2B,GAAG,SAAS;AAAA,EAC3E,KAAK,aAAa,MAAM,OAAO,sBAAsB,GAAG,IAAI;AAAA,EAC5D,MAAM,aAAa,MAAM,OAAO,uBAAuB,GAAG,KAAK;AACjE;AAGO,SAAS,gBAAgB,MAAoD;AAClF,QAAM,OAAO,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK;AACtC,QAAM,MAAM,KAAK,SAAS,GAAG,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK,IAAI,YAAY,IAAI;AAC/E,UAAQ,KAAK;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAuCO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAAS,KAAK;AAC1C,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA8B,IAAI;AAK9D,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAS,eAAe;AAClD,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,eAAe;AACxD,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAuB,IAAI;AAC7D,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAEhD,QAAM,QAAQ,UAAU;AACxB,QAAM,eACJ,cAAc,QAAS,UAAyC,SAAS;AAM3E,QAAM,qBAAqBC,SAAO,KAAK;AACvC,QAAM,WAAWA,SAA2C,EAAE,MAAM,UAAU,gBAAgB,CAAC;AAC/F,EAAAC,YAAU,MAAM;AACd,UAAM,cACJ,SAAS,QAAQ,SAAS,QAAQ,SAAS,QAAQ,aAAa;AAClE,QAAI,CAAC,YAAa;AAClB,aAAS,UAAU,EAAE,MAAM,UAAU,gBAAgB;AACrD,uBAAmB,UAAU;AAC7B,aAAS,eAAe;AACxB,gBAAY,eAAe;AAC3B,iBAAa,IAAI;AACjB,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,MAAM,eAAe,CAAC;AAI1B,QAAM,WAAWC;AAAA,IACf,CAAC,SAAiB;AAChB,eAAS,IAAI;AACb,mBAAa,KAAK;AAClB,UAAI,CAAC,mBAAmB,WAAW,SAAS,UAAU;AACpD,2BAAmB,UAAU;AAC7B,uBAAe;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,UAAU,YAAY;AAAA,EACzB;AAIA,QAAM,UAAUC,UAAQ,MAAM,gBAAgB,IAAI,GAAG,CAAC,IAAI,CAAC;AAC3D,EAAAF,YAAU,MAAM;AACd,QAAI,YAAY;AAChB,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,QAAS,MAAM,OAAO,uBAAuB;AAOnD,YAAI,oBAAoC;AACxC,YAAI,SAAS;AACX,cAAI;AACF,gCAAqB,MAAM,iBAAiB,OAAO,IAAI,KAAM;AAAA,UAC/D,QAAQ;AACN,gCAAoB;AAAA,UACtB;AAAA,QACF;AACA,YAAI,UAAW;AAIf,cAAM,sBAAsB,CAAC,QAC3B,MAAM,KAAK;AAAA,UACT,MAAM,OAAO,GAAG;AAAA,YACd;AAAA,cACE,KAAK;AAAA,cACL,gBAAgB;AAAA,cAChB,KAAK,MAAM;AACT,oBAAI;AACJ,uBAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH;AACF,kBAAU;AAAA,UACR,QAAQ,MAAM;AAAA,UACd;AAAA,UACA;AAAA,QACF,CAAC;AACD,kBAAU,KAAK;AAAA,MACjB,QAAQ;AACN,YAAI,CAAC,UAAW,WAAU,IAAI;AAAA,MAChC;AAAA,IACF,GAAG;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAGZ,QAAM,UAAUD,SAAmB,MAAM;AAAA,EAAC,CAAC;AAE3C,QAAM,OAAOE,cAAY,YAAY;AACnC,QAAI,SAAU;AAEd,UAAM,WAAW;AACjB,QAAI,aAAa,SAAU;AAC3B,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,iBAAa,KAAK;AAClB,QAAI;AACF,YAAM,OAAO,QAAQ;AACrB,kBAAY,QAAQ;AACpB,eAAS,UAAU,EAAE,MAAM,UAAU,SAAS;AAC9C,mBAAa,IAAI;AAAA,IACnB,SAAS,OAAO;AACd,mBAAa,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACxE,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,UAAU,OAAO,UAAU,QAAQ,IAAI,CAAC;AAE5C,QAAM,YAAYA,cAAY,YAAY;AACxC,QAAI,YAAY,CAAC,YAAa;AAC9B,UAAM,WAAW;AACjB,QAAI,aAAa,SAAU;AAC3B,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,iBAAa,KAAK;AAClB,QAAI;AACF,YAAM,YAAY,QAAQ;AAC1B,kBAAY,QAAQ;AACpB,eAAS,UAAU,EAAE,MAAM,UAAU,SAAS;AAC9C,mBAAa,IAAI;AAAA,IACnB,SAAS,OAAO;AACd,mBAAa,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACxE,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,UAAU,aAAa,OAAO,UAAU,IAAI,CAAC;AAEjD,UAAQ,UAAU,MAAM;AACtB,SAAK,KAAK;AAAA,EACZ;AAGA,EAAAD,YAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,UAAM,IAAI,WAAW,MAAM,aAAa,KAAK,GAAG,IAAI;AACpD,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,WAAW,KAAK,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AAE1D,QAAM,eAAeE,UAAQ,MAAM;AACjC,QAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,UAAM,OAAkB,CAAC,OAAO,oBAAoB,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAC5E,QAAI,OAAO,kBAAmB,MAAK,KAAK,OAAO,iBAAiB;AAChE,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,SAAS,QAAQ;AAEvB,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,gCAAgC,SAAS;AAAA,MACvD,6BAAyB;AAAA,MACzB,OAAO;AAAA,MAGP;AAAA,wBAAAA,MAAC,SAAI,WAAU,gGACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,WAAW,mBAAmB,QAAQ,yBAAyB;AAAA,cACjE;AAAA,cACA,OAAO,WAAW,cAAc,QAAQ,oBAAoB;AAAA;AAAA,UAC9D;AAAA,UACA,gBAAAC,MAAC,UAAK,WAAU,qDACb;AAAA;AAAA,YACA,SAAS,CAAC,WAAW,YAAO;AAAA,aAC/B;AAAA,UACA,gBAAAA,MAAC,SAAI,WAAU,4CACZ;AAAA,yBAAa,CAAC,gBACb,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,UAAU;AAAA,gBAEhB,oBAAU;AAAA;AAAA,YACb;AAAA,YAED,CAAC,aAAa,aAAa,gBAAAA,KAAC,UAAK,WAAU,kCAAiC,mBAAK;AAAA,YACjF,WACC,gBAAAA,KAAC,UAAK,WAAU,wDAAuD,uBAAS,IAC9E,CAAC,eACH,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,KAAK,KAAK;AAAA,gBACzB,UAAU,UAAU,CAAC;AAAA,gBACrB,WAAW;AAAA,kBACT;AAAA,kBACA,UAAU,CAAC,QACP,gDACA;AAAA,gBACN;AAAA,gBACA,OAAM;AAAA,gBAEL;AAAA,2BACC,gBAAAD,KAAC,eAAY,WAAU,uBAAsB,IAE7C,gBAAAA,KAAC,YAAS,WAAU,UAAS;AAAA,kBAC7B;AAAA;AAAA;AAAA,YAEJ,IACE;AAAA,aACN;AAAA,WACF;AAAA,QAEC,eACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YAEL;AAAA,8BAAAA,MAAC,SAAI,WAAU,oDACb;AAAA,gCAAAD,KAAC,UAAK,WAAU,qCAAoC,sCAAwB;AAAA,gBAAQ;AAAA,gBAAI;AAAA,iBAE1F;AAAA,cACA,gBAAAC,MAAC,SAAI,WAAU,8CACZ;AAAA,2BACC,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM;AACb,mCAAa,IAAI;AACjB,+BAAS;AAAA,oBACX;AAAA,oBACA,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED,IACE;AAAA,gBACH,cACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM,KAAK,UAAU;AAAA,oBAC9B,UAAU;AAAA,oBACV,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED,IACE;AAAA,iBACN;AAAA;AAAA;AAAA,QACF,IACE;AAAA,QAGJ,gBAAAA,KAAC,SAAI,WAAU,gCACZ,mBACE,YACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,QAAQ,MAAM,KAAK,KAAK;AAAA;AAAA,QAC1B,IAEA,SACF,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,OAAO,cAAc,UAAU,UAAU;AAAA,YACzC,UAAU,CAAC;AAAA,YACX;AAAA,YACA,YAAY;AAAA,YACZ,YAAY;AAAA,YACZ,QAAO;AAAA,YACP,WAAU;AAAA,YACV,UAAU,WAAW,SAAY;AAAA;AAAA,QACnC,IAEC,WAAW,gBAAAA,KAAC,kBAAe,GAEhC;AAAA;AAAA;AAAA,EACF;AAEJ;AAGA,IAAM,aAAa;AAAA,EACjB,cAAc;AAAA,EACd,YAAY;AACd;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC,WAAW,CAAC,UAAU;AACpB,aAAK,MAAM,WAAW,MAAM,YAAY,MAAM,IAAI,YAAY,MAAM,KAAK;AACvE,gBAAM,eAAe;AACrB,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MACA,cAAW;AAAA,MACX,WAAU;AAAA;AAAA,EACZ;AAEJ;AAEA,SAAS,iBAAiB;AACxB,SAAO,gBAAAA,KAAC,SAAI,WAAU,oCAAmC,kCAAe;AAC1E;;;ACndA,SAAS,eAAe,mBAAAO,kBAAiB,wBAAwB;AACjE,SAAyB,eAAAC,eAAa,aAAAC,aAAW,UAAAC,UAAQ,YAAAC,kBAAgB;AA0K3D,SA0JE,YAAAC,WA1JF,OAAAC,OAYN,QAAAC,aAZM;AA/GP,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAwB,IAAI;AAG5D,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,WAAwB,IAAI;AAC9E,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,WAAS,CAAC;AAC9D,QAAM,oBAAoBC,SAA+B,IAAI;AAC7D,QAAM,oBAAoBA,SAA+B,IAAI;AAC7D,QAAM,aAAa,gBAAiB,0BAA0B,gBAAiB;AAE/E,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,iBAAiB,eAAe,MAAM;AACzC,wBAAkB,UAAU;AAC5B,wBAAkB,UAAU;AAC5B;AAAA,IACF;AACA,QAAI,kBAAkB,YAAY,YAAY;AAC5C;AAAA,IACF;AACA,QAAI,CAAC,oBAAoB;AACvB,wBAAkB,UAAU;AAC5B;AAAA,IACF;AACA,sBAAkB,UAAU;AAC5B,sBAAkB,UAAU;AAC5B,gBAAY,aAAa;AACzB,gBAAY,KAAK;AAAA,EACnB,GAAG,CAAC,YAAY,eAAe,kBAAkB,CAAC;AAKlD,QAAM,UAAUD,SAA8B,IAAI;AAClD,QAAM,CAAC,MAAM,OAAO,IAAID,WAAS,KAAK;AACtC,EAAAE,YAAU,MAAM;AACd,UAAM,KAAK,QAAQ;AACnB,QAAI,CAAC,MAAM,OAAO,mBAAmB,YAAa;AAClD,UAAM,WAAW,IAAI,eAAe,CAAC,YAAY;AAC/C,YAAM,IAAI,QAAQ,CAAC,GAAG,YAAY,SAAS;AAC3C,cAAQ,KAAK,GAAG;AAAA,IAClB,CAAC;AACD,aAAS,QAAQ,EAAE;AACnB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,CAAC;AAIL,QAAM,gBAAgB,aAAa,SAAS;AAK5C,QAAM,WAAW;AACjB,QAAM,WAAW,YAAY,UAAU,MAAM,UAAU,kBAAkB;AAMzE,QAAM,aAAaC,cAAY,CAAC,SAAiB;AAC/C,QAAI,kBAAkB,YAAY,MAAM;AACtC,wBAAkB,UAAU,kBAAkB;AAC9C,wBAAkB,UAAU;AAAA,IAC9B;AACA,gBAAY,IAAI;AAChB,gBAAY,KAAK;AACjB,yBAAqB,IAAI;AACzB,0BAAsB,CAAC;AAAA,EACzB,GAAG,CAAC,CAAC;AAML,QAAM,UACJ,YACA,aAAa,QACb,CAAC,SAAS,WACV,SAAS,UAAU,QACnB,CAAC,SAAS,YACV,CAAC,SAAS,aACV,SAAS,YAAY;AACvB,QAAM,aAAa,WAAW;AAC9B,QAAM,yBACJ,SAAS,iBAAiB,+BAA+B,SAAS,QAAQ;AAC5E,QAAM,yBACJ,sBAAsB,aACrB,CAAC,sBAAsB,MAAM,YAC9B,2BAA2B;AAE7B,MAAI,CAAC,qBAAqB;AACxB,WACE,gBAAAL;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,MAAM,gBAAAA,MAACM,kBAAA,EAAgB,WAAU,UAAS,eAAW,MAAC;AAAA,QACtD,OAAM;AAAA,QACN,UAAS;AAAA,QACV;AAAA;AAAA,IAED;AAAA,EAEJ;AAEA,MAAI,oBAAoB,iBAAiB;AACvC,WACE,gBAAAN,MAAC,SAAI,WAAW,GAAG,UAAU,SAAS,GAAG,mCAA+B,MACtE,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MACE,kBACE,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA;AAAA,QACb,IAEA,gBAAAA,MAAC,iBAAc,WAAU,UAAS,eAAW,MAAC;AAAA,QAGlD,OAAO,kBAAkB,qBAAqB;AAAA,QAC9C,UAAS;AAAA,QAET;AAAA,0BAAAA,MAAC,OACE,4BACG,6CACA,wFACN;AAAA,UACC,CAAC,mBAAmB,kBACnB,gBAAAA,MAAC,cAAW,SAAS,iBAAiB,iCAAmB,IACvD;AAAA;AAAA;AAAA,IACN,GACF;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,KAAK,SAAS,WAAW,GAAG,wCAAwC,SAAS,GAEhF;AAAA,oBAAAD,MAAC,aAAU,KAAU,YAAY,IAAI,KAAK,QAAQ;AAAA,IAElD,gBAAAC,MAAC,SAAI,WAAW,GAAG,uBAAuB,OAAO,aAAa,UAAU,GAGtE;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,OAAO,iDAAiD;AAAA,UAC1D;AAAA,UAEA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,QAAQ;AAAA,cACR,cAAc,YAAY;AAAA,cAC1B,cAAc;AAAA,cACd;AAAA,cACA,YAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA;AAAA,MACF;AAAA,MAKA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,OAAO,WAAW;AAAA,UACpB;AAAA,UAEA;AAAA,4BAAAA,MAAC,SAAI,WAAU,wGACb;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,+BAA2B;AAAA,kBAC3B,WAAU;AAAA,kBAET,sBAAY;AAAA;AAAA,cACf;AAAA,cAIC,WACC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS;AAAA,oBACP,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,oBAC/B,EAAE,OAAO,QAAQ,OAAO,OAAO;AAAA,kBACjC;AAAA,kBACA,OAAO,WAAW,SAAS;AAAA,kBAC3B,UAAU,CAAC,MAAM,YAAY,MAAM,MAAM;AAAA;AAAA,cAC3C;AAAA,eAEJ;AAAA,YACA,gBAAAA,MAAC,SAAI,WAAU,gCACZ,qBACC,cAAc,SAAS,YAAY,OACjC,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAM;AAAA,gBACN,iBAAiB,SAAS;AAAA,gBAC1B,WAAW;AAAA,gBACX,QAAQ,CAAC,aACP,MAAM,UAAU,UAAU,UAAU,EAAE,iBAAiB,SAAS,QAAS,CAAC;AAAA,gBAE5E,aAAa,CAAC,aAAa,MAAM,UAAU,UAAU,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,gBAC9E,UAAU,MAAM,sBAAsB,CAAC,aAAa,WAAW,CAAC;AAAA,gBAC/D,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,gBACxC,WAAU;AAAA;AAAA,cAVL;AAAA,YAWP,IACE,yBACF,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MACE,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,eAAW;AAAA;AAAA,gBACb;AAAA,gBAEF,OAAM;AAAA,gBACN,UAAS;AAAA,gBACV;AAAA;AAAA,kBACU;AAAA,kBAAS;AAAA;AAAA;AAAA,YACpB,IACE,yBACF,gBAAAC,MAAC,UAAO,MAAM,gBAAAD,MAAC,iBAAc,WAAU,UAAS,eAAW,MAAC,GAAI,OAAM,cACpE;AAAA,8BAAAA,MAAC,OACE,iCAAuB,WAAW,cAC/B,yDACA,uBAAuB,WAAW,oBAChC,8CACA,uEACR;AAAA,cACC,kBACC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM;AACb,yCAAqB,QAAQ;AAC7B,wBAAI,mBAAoB,MAAK,MAAM,QAAQ;AAAA,wBACtC,iBAAgB;AAAA,kBACvB;AAAA,kBAEC,+BAAqB,oBAAoB;AAAA;AAAA,cAC5C,IACE;AAAA,eACN,IACE,SAAS,QACX,gBAAAC,MAAC,UAAO,UAAS,SAAQ;AAAA;AAAA,cACP;AAAA,cAAS;AAAA,cAAG,SAAS,MAAM;AAAA,eAC7C,IACE,SAAS,UACX,gBAAAA,MAAC,UAAO,UAAS,UAAS;AAAA;AAAA,cAAS;AAAA,cAAS;AAAA,eAAC,IAC3C,SAAS,WACX,gBAAAA,MAAC,UACE;AAAA;AAAA,cAAS;AAAA,cAAoB,SAAS,aAAa;AAAA,cAAE;AAAA,eACxD,IACE,SAAS,YAAY,OACvB,gBAAAA,MAAAF,WAAA,EACG;AAAA,uBAAS,aACR,gBAAAE,MAAC,SAAI,WAAU,yFAAwF;AAAA;AAAA,gBAC1D,SAAS,aAAa;AAAA,gBAAE;AAAA,iBAErE;AAAA,cAED,YACC,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM;AAAA,kBACN,UAAU,SAAS;AAAA,kBACnB,WAAW;AAAA,kBACX,UACE,gBAAAA,MAAC,SAAI,WAAU,uEACZ,mBAAS,SACZ;AAAA,kBAEF,WAAU;AAAA;AAAA,cACZ,IAEA,gBAAAA,MAAC,SAAI,WAAU,uEACZ,mBAAS,SACZ;AAAA,eAEJ,IAEA,gBAAAC,MAAC,UAAO,UAAS,UAAS;AAAA;AAAA,cAAS;AAAA,cAAS;AAAA,eAAC;AAAA;AAAA,cAGjD,iBAAiB,CAAC,qBAChB,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MACE,gBAAAD;AAAA,oBAAC;AAAA;AAAA,sBACC,WAAU;AAAA,sBACV,eAAW;AAAA;AAAA,kBACb;AAAA,kBAEF,OAAM;AAAA,kBACN,UAAS;AAAA,kBACV;AAAA;AAAA,oBACU;AAAA,oBAAc;AAAA;AAAA;AAAA,cACzB,IAEA,gBAAAA,MAAC,UAAO,MAAM,gBAAAA,MAAC,iBAAc,WAAU,UAAS,eAAW,MAAC,GAAI,OAAM,iBAAgB,sDAEtF;AAAA,eAEJ;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,SAAS,WAAW,EAAE,UAAU,QAAQ,GAAiD;AACvF,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,UAAU,EAAE,KAAK,WAAW,GAAqD;AACxF,QAAM,QAAQ,aAAa;AAC3B,SACE,gBAAAC,MAAC,SAAI,WAAU,mGACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,QAAQ,yBAAyB;AAAA,QACnC;AAAA;AAAA,IACF;AAAA,IACA,gBAAAA,MAAC,UAAK,WAAU,WACb,kBAAQ,oBAAoB,UAAU,mBAAmB,sBAC5D;AAAA,IACA,gBAAAA,MAAC,UAAK,WAAU,oCACb,cAAI,YAAY,IACb,GAAG,IAAI,SAAS,kBACf,IAAI,WAAW,IAAI,SAAS,eAAe,YAClD;AAAA,KACE,IAAI,QAAQ,KAAK,IAAI,SAAS,MAC9B,gBAAAC,MAAC,UAAK,WAAU,mEACb;AAAA,UAAI,QAAQ,KAAK,gBAAAA,MAAC,UAAK;AAAA;AAAA,QAAE,IAAI;AAAA,SAAM;AAAA,MACnC,IAAI,SAAS,KAAK,gBAAAA,MAAC,UAAK;AAAA;AAAA,QAAE,IAAI;AAAA,SAAO;AAAA,OACxC;AAAA,IAED,SACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,eAAY;AAAA,QACZ,yBAAqB;AAAA,QACrB,WAAU;AAAA,QAET;AAAA;AAAA,UAAW;AAAA;AAAA;AAAA,IACd;AAAA,KAEJ;AAEJ;AAEA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,gBAAAD,MAAC,SAAI,WAAU,2EACZ,kBAAQ,IAAI,CAAC,QACZ,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAEC,MAAK;AAAA,MACL,SAAS,MAAM,SAAS,IAAI,KAAK;AAAA,MACjC,WAAW;AAAA,QACT;AAAA,QACA,IAAI,UAAU,QACV,iCACA;AAAA,MACN;AAAA,MAEC,cAAI;AAAA;AAAA,IAVA,IAAI;AAAA,EAWX,CACD,GACH;AAEJ;AAoBA,SAAS,YACP,MACA,UACA,iBAAiB,GACF;AACf,QAAM,CAAC,OAAO,QAAQ,IAAIE,WAAwB;AAAA,IAChD,SAAS;AAAA,IACT,UAAU;AAAA,IACV,WAAW;AAAA,IACX,WAAW;AAAA,IACX,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AACD,EAAAE,YAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT,eAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AACD;AAAA,IACF;AACA,QAAI,YAAY;AAChB,UAAM,QAAQ,IAAI,gBAAgB;AAClC,aAAS;AAAA,MACP,SAAS;AAAA,MACT,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AACD,SAAK,SAAS,MAAM,EAAE,QAAQ,MAAM,OAAO,CAAC,EACzC,KAAK,CAAC,QAAQ;AACb,UAAI,UAAW;AACf,YAAM,UAAU,IAAI,WAChB,OACA,IAAI,aAAa,WACf,iBAAiB,IAAI,OAAO,IAC5B,IAAI;AACV,eAAS;AAAA,QACP;AAAA,QACA,UAAU,IAAI;AAAA,QACd,WAAW,IAAI;AAAA,QACf,WAAW,IAAI;AAAA,QACf,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AAAA,IACH,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,UAAW;AACf,eAAS;AAAA,QACP,SAAS;AAAA,QACT,UAAU;AAAA,QACV,WAAW;AAAA,QACX,WAAW;AAAA,QACX,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE,CAAC;AAAA,IACH,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AACZ,YAAM,MAAM;AAAA,IACd;AAAA,EACF,GAAG,CAAC,MAAM,UAAU,cAAc,CAAC;AACnC,SAAO;AACT;AAGA,SAAS,iBAAiB,KAAqB;AAC7C,MAAI;AACF,QAAI,OAAO,SAAS,YAAY;AAC9B,YAAM,SAAS,KAAK,GAAG;AACvB,YAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,OAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AACtE,aAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,IACvC;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN,eAAa,WAAW,SAAS;AAAA,MACjC,aAAW,aAAa,UAAU,cAAc,aAAa,WAAW,WAAW;AAAA,MACnF,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA,0BAAAC,MAAC,SAAI,WAAU,+CACZ;AAAA,eACC,gBAAAD,MAAC,UAAK,WAAU,oHACb,gBACH,IACE;AAAA,QACH,QAAQ,gBAAAA,MAAC,OAAE,WAAU,0BAA0B,iBAAM,IAAO;AAAA,QAC7D,gBAAAA,MAAC,SAAI,WAAU,aAAa,UAAS;AAAA,SACvC;AAAA;AAAA,EACF;AAEJ;;;AChlBA,SAAS,oBAAAO,mBAAkB,aAAa,uBAAuB,mBAAmB;AAClF,SAAyB,aAAAC,aAAW,UAAAC,UAAQ,YAAAC,kBAAgB;AAsWpD,gBAAAC,OA4DE,QAAAC,aA5DF;AAhRR,IAAM,2BAA2B;AAIjC,SAAS,kBAAkB,QAAqD;AAC9E,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT;AAEE,aAAO;AAAA,EACX;AACF;AAqBO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AAAA,EACnB;AACF,GAAuB;AACrB,QAAM,eAAeC,SAA8B,IAAI;AACvD,QAAM,CAAC,WAAW,YAAY,IAAIC,WAAS,KAAK;AAIhD,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,eAAe,KAAK;AACnE,QAAM,uBAAuB,gBAAgB;AAC7C,QAAM,sBAAsB,YAAY,SAAS;AACjD,QAAM,cAAc,uBAAuB,cAAc;AACzD,QAAM,YAAY,QAAQ,WAAW,KAAK;AAK1C,QAAM,aAAa,YAAY;AAG/B,QAAM,gBAAgB,CAAC,cAAc,WAAW,cAAc;AAC9D,QAAM,SAAS,YAAY,UAAU;AACrC,QAAM,WACJ,YAAY,2BAA2B,QAAQ,WAAW,iBAAiB,QAAQ,CAAC;AAEtF,QAAM,gBAAgB,QAAQ,UAAU,KAAK,CAAC,iBAAiB,CAAC,WAAY;AAI5E,QAAM,eACH,iBAAiB,WAAW,gBAAkB,CAAC,iBAAiB;AAEnE,QAAM,kBAAkB,iBAAiB,kBAAkB,MAAM;AAEjE,QAAM,SAAS,MAAM;AACnB,iBAAa,IAAI;AACjB,oBAAgB;AAAA,EAClB;AAcA,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,aAAa,qBAAsB;AACxC,UAAM,QAAQ,CAAC,UAAyB;AAGtC,UACE,MAAM,WACN,MAAM,UACN,MAAM,aACL,MAAM,QAAQ,aAAa,MAAM,QAAQ,SAAS,MAAM,QAAQ,UACjE;AACA,cAAM,eAAe;AACrB,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AACA,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM;AACX,aAAO,oBAAoB,WAAW,KAAK;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,WAAW,oBAAoB,CAAC;AAMpC,QAAM,oBAAoB,CAAC,iBAAiB,CAAC,YAAY,CAAC,mBAAmB,aAAa;AAC1F,QAAM,SAAS,iBAAiB;AAAA,IAC9B,YAAY;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,IACb,GAAI,kBAAkB,SAAY,EAAE,cAAc,IAAI,CAAC;AAAA,IACvD,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IACnC,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,EACjD,CAAC;AAED,QAAM,YAAY,OAAO,UAAU;AACnC,QAAM,aAAa,QAAQ,mBAAmB,GAAG;AAIjD,QAAM,iBAAiBF,SAAO,OAAO,KAAK;AAC1C,iBAAe,UAAU,OAAO;AAUhC,QAAM,aAAaA,SAAsB,IAAI;AAC7C,EAAAE,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,CAAC,gBAAgB,YAAY,kBAAkB;AAEhE,UAAI,CAAC,aAAc,YAAW,UAAU;AACxC;AAAA,IACF;AACA,UAAM,OAAO,UAAU;AACvB,QAAI,CAAC,KAAM;AAKX,UAAM,MAAM,GAAG,YAAY,UAAU,EAAE,IAAI,YAAY,OAAO,EAAE,IAAI,YAAY,aAAa,EAAE;AAC/F,QAAI,WAAW,YAAY,IAAK;AAChC,eAAW,UAAU;AACrB,SAAK;AAAA,EAGP,GAAG,CAAC,YAAY,cAAc,UAAU,kBAAkB,YAAY,QAAQ,aAAa,CAAC;AAQ5F,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,OAAO,aAAa,YAAa;AACpD,UAAM,gBAAgB,MAAM;AAC1B,UAAI,SAAS,oBAAoB,UAAW;AAQ5C,UAAI,eAAe,YAAY,OAAQ,QAAO,UAAU;AAAA,IAC1D;AAMA,aAAS,iBAAiB,oBAAoB,aAAa;AAC3D,WAAO,MAAM,SAAS,oBAAoB,oBAAoB,aAAa;AAAA,EAG7E,GAAG,CAAC,YAAY,mBAAmB,GAAG,CAAC;AAMvC,QAAM,CAAC,iBAAiB,kBAAkB,IAAID,WAAS,KAAK;AAG5D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,WAAS,CAAC;AACtD,EAAAC,YAAU,MAAM;AAKd,uBAAmB,KAAK;AACxB,QAAI,CAAC,cAAc,aAAa,OAAO,SAAS,oBAAoB,EAAG;AACvE,UAAM,QAAQ,WAAW,MAAM,mBAAmB,IAAI,GAAG,gBAAgB;AACzE,WAAO,MAAM,aAAa,KAAK;AAAA,EACjC,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,OAAO;AAAA,IACP;AAAA,EACF,CAAC;AAED,QAAM,YAAY,MAAM;AACtB,uBAAmB,KAAK;AACxB,sBAAkB,CAAC,MAAM,IAAI,CAAC;AAC9B,WAAO,UAAU;AAAA,EACnB;AAGA,MAAI;AACJ,MAAI,iBAAkB,WAAU;AAAA,WACvB,gBAAiB,WAAU;AAAA,WAC3B,YAAY,CAAC,WAAY,WAAU;AAAA,WACnC,aAAc,WAAU;AAAA,WACxB,UAAW,WAAU;AAAA,WACrB,OAAO,MAAO,WAAU;AAAA,WACxB,gBAAiB,WAAU;AAAA,MAC/B,WAAU;AAIf,QAAM,eAAe,YAAY,eAAe,YAAY;AAC5D,QAAM,aAAa,qBAAqB,CAAC;AAEzC,MAAI,UAAqB;AACzB,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,gBACE,kBAAkB,KAClB;AAAA,QACE;AAAA,QACA;AAAA,MACF;AACF;AAAA,IACF,KAAK;AACH,gBACE,oBAAoB,MAAM,KAC1B,cAAc,uBAAuB,gBAAgB,MAAM,CAAC;AAC9D;AAAA,IACF,KAAK;AACH,gBAAU,oBACR,kBAAkB,QAAQ,YAAY,UAAU,KAAK,IAErD,gBAAAJ,MAAC,sBAAmB,QAAQ,YAAY,UAAU,OAAO,UAAU,QAAQ;AAE7E;AAAA,IACF,KAAK;AACH,gBAAU,gBAAgB,KAAK,gBAAAA,MAAC,iBAAc;AAC9C;AAAA,IACF,KAAK;AACH,gBAAU;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA;AAAA,IACF,KAAK;AACH,gBAAU;AAAA,QACR;AAAA,QACA,OAAO,OAAO,WAAW;AAAA,QACzB;AAAA,MACF;AACA;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,gBAAU;AACV;AAAA,EACJ;AAEA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,aAAa;AAAA,QACb;AAAA,MACF;AAAA,MACA,OAAO,EAAE,iBAAiB,yBAAyB;AAAA,MACnD,yBAAqB;AAAA,MACrB,cAAY,OAAO;AAAA,MACnB,iBAAe;AAAA,MACf,mBAAiB,YAAY,SAAS;AAAA,MAStC;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAU;AAAA,YACV,gCAA4B;AAAA,YAC5B,cAAY,OAAO;AAAA;AAAA,QACrB;AAAA,QAOC,YAAY,gBACX,gBAAAC,MAAC,SAAI,WAAU,0GACb;AAAA,0BAAAA,MAAC,UAAK,WAAU,6CACd;AAAA,4BAAAD,MAAC,eAAY,WAAU,qBAAoB,aAAa,KAAK;AAAA,YAC7D,gBAAAA;AAAA,cAACK;AAAA,cAAA;AAAA,gBACC,WAAU;AAAA,gBACV,aAAa;AAAA;AAAA,YACf;AAAA,aACF;AAAA,UACA,gBAAAL,MAAC,UAAK,WAAU,cAAa,6CAA0B;AAAA,WACzD;AAAA,QAYD,cAAc,CAAC,wBAAwB,aACtC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ,YAAY,UAAU;AAAA,YAC9B,WAAW,MAAM,eAAe,KAAK;AAAA;AAAA,QACvC;AAAA,QAOD,cAAc,CAAC,wBAAwB,CAAC,aAAa,aACpD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,CAAC;AAAA,YACX,gBACE,CAAC,sBACG,YAAY,WAAW,WACrB,wEACA,mDACF;AAAA,YAEN,eAAe,MAAM,eAAe,IAAI;AAAA;AAAA,QAC1C;AAAA,QAGD,WACC,gBAAAA,MAAC,SAAI,WAAU,yDAAyD,mBAAQ;AAAA;AAAA;AAAA,EAEpF;AAEJ;AAWA,SAAS,wBAAwB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD;AAAA;AAAA;AAAA,IAGE,gBAAAA,MAAC,SAAI,WAAU,8EACb,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA,cAAW;AAAA,QACX,OAAO,WAAW,iBAAiB;AAAA,QACnC,SAAS;AAAA,QACT,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,WACI,kCACA;AAAA,YACE;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QACN;AAAA,QAEA;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA,WAAW,KAAK;AAAA,cAClB;AAAA,cAEA,0BAAAA,MAAC,yBAAsB,WAAU,UAAS,aAAa,GAAG;AAAA;AAAA,UAC5D;AAAA,UACA,gBAAAC,MAAC,UAAK,WAAU,2CACd;AAAA,4BAAAD,MAAC,UAAK,WAAU,yCAAwC,0BAAY;AAAA,YACpE,gBAAAA,MAAC,UAAK,WAAU,gCACb,sBAAY,iBAAiB,iBAAiB,8BACjD;AAAA,aACF;AAAA;AAAA;AAAA,IACF,GACF;AAAA;AAEJ;AAUA,SAAS,aAAa,EAAE,QAAQ,UAAU,GAA+C;AACvF,SACE,gBAAAC,MAAC,SAAI,WAAU,4FACb;AAAA,oBAAAA,MAAC,UAAK,WAAU,mJACd;AAAA,sBAAAD,MAAC,UAAK,WAAU,kDAAiD,eAAW,MAAC;AAAA,MAAE;AAAA,MAE/E,gBAAAA,MAAC,UAAK,WAAU,cAAa,2DAA0C;AAAA,OACzE;AAAA,IACA,gBAAAC,MAAC,SAAI,WAAU,iDACZ;AAAA,gBACC,gBAAAD,MAAC,UAAK,WAAU,iFAAgF,uDAEhG;AAAA,MAEF,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,OAAM;AAAA,UACN,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UAEA;AAAA,4BAAAD,MAAC,eAAY,WAAU,YAAW,aAAa,GAAG,eAAW,MAAC;AAAA,YAAE;AAAA;AAAA;AAAA,MAElE;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,SAAS,gBAAgB,QAAoD;AAC3E,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAGH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AASA,SAAS,cAAc,EAAE,QAAQ,GAA2C;AAC1E,SACE,gBAAAC,MAAC,SAAI,WAAU,wJACb;AAAA,oBAAAD,MAACK,mBAAA,EAAiB,WAAU,yCAAwC,aAAa,KAAK;AAAA,IACtF,gBAAAJ,MAAC,SAAI,WAAU,aACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,eAAc,uCAAoB;AAAA,MACjD,gBAAAA,MAAC,OAAE,WAAU,gCAA+B,sEAE5C;AAAA,OACF;AAAA,IACC,WACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;AAEA,SAAS,mBAAmB,EAAE,QAAQ,SAAS,GAA8C;AAC3F,SACE,gBAAAC,MAAC,SAAI,WAAU,mGACb;AAAA,oBAAAD,MAAC,SAAI,WAAU,oBAAmB,qCAAuB;AAAA,IACzD,gBAAAC,MAAC,OAAE,WAAU,qCAAoC;AAAA;AAAA,MACnB,gBAAAD,MAAC,YAAO,yBAAW;AAAA,MAAS;AAAA,MAEvD,SACG,wFACA;AAAA,OACN;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEA,SAAS,cAAc,OAAe,MAAc,SAAiC;AACnF,SACE,gBAAAC,MAAC,SAAI,WAAU,mGACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,6DACZ;AAAA,iBAAW,gBAAAD,MAAC,eAAY,WAAU,qBAAoB,aAAa,MAAM;AAAA,MACzE;AAAA,OACH;AAAA,IACA,gBAAAA,MAAC,OAAE,WAAU,gCAAgC,gBAAK;AAAA,IACjD,WACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;;;ACjqBA;AAAA,EAGE,eAAAM;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAAC;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAgTD,SA0DE,YAAAC,WAzDA,OAAAC,OADF,QAAAC,cAAA;AA1QN,IAAM,wBAAuC;AAAA,EAC3C,SAAS,MAAM;AAAA,EACf,SAAS,MAAM;AAAA,EAAC;AAClB;AAMA,SAAS,mBAAkC;AACzC,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAgBA,IAAM,sBAAsB,OAAO,WAAW,cAAcC,cAAYC;AAOxE,IAAM,0BAA0B;AAGhC,SAAS,YAAY,UAA2B;AAC9C,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAAS,KAAK;AAC1C,sBAAoB,MAAM;AACxB,QAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E;AAAA,IACF;AACA,UAAM,MAAM,OAAO,WAAW,eAAe,WAAW,CAAC,KAAK;AAC9D,UAAM,SAAS,MAAM,UAAU,IAAI,OAAO;AAC1C,WAAO;AACP,QAAI,OAAO,IAAI,qBAAqB,YAAY;AAC9C,UAAI,iBAAiB,UAAU,MAAM;AACrC,aAAO,MAAM,IAAI,oBAAoB,UAAU,MAAM;AAAA,IACvD;AAEA,QAAI,YAAY,MAAM;AACtB,WAAO,MAAM,IAAI,eAAe,MAAM;AAAA,EACxC,GAAG,CAAC,QAAQ,CAAC;AACb,SAAO;AACT;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,cAAc;AAAA,EACd,UAAU;AAAA,EACV,UAAU;AAAA,EACV;AACF,GAAuB;AACrB,QAAM,SAAS,YAAY,uBAAuB;AAClD,QAAM,UAAUC,SAA8B,IAAI;AAClD,QAAM,YAAYA,SAAiC,IAAI;AACvD,QAAM,iBAAiBA,SAA2B,IAAI;AACtD,QAAM,wBAAwBA,SAAO,KAAK;AAC1C,QAAM,eAAe,YAAY;AACjC,QAAM,CAAC,mBAAmB,oBAAoB,IAAID,WAAS,KAAK;AAChE,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,KAAK,CAAC,GAAG,MAAM,EAAE;AAChE,QAAM,YAAY,iBAAiB;AAMnC,QAAM,iBAAiB,kBAAkB;AACzC,QAAM,uBAAuBC,SAAO,SAAS;AAG7C,QAAM,EAAE,eAAe,gBAAgB,IAAI,iBAAiB;AAAA,IAC1D,UAAU,CAAC,WAAW,MAAM;AAAA,IAC5B,SAAS,iBAAiB;AAAA,IAC1B,IAAI;AAAA,EACN,CAAC;AAED,QAAM,eAAe,aAAa;AAClC,QAAM,SAAS,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE;AACvC,QAAM,aAAa,KAAK,CAAC,GAAG,MAAM;AAClC,QAAM,sBAAsB,OAAO,SAAS,YAAY;AACxD,QAAM,UAAU,sBAAsB,eAAe;AACrD,QAAM,SAASC;AAAA,IACb,CAAC,OAAe;AACd,qBAAe,EAAE;AACjB,0BAAoB,EAAE;AAAA,IACxB;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AACA,QAAM,eAAeA;AAAA,IACnB,CAAC,SAAkB;AACjB,2BAAqB,CAAC,aAAc,aAAa,OAAO,WAAW,IAAK;AACxE,0BAAoB,IAAI;AAAA,IAC1B;AAAA,IACA,CAAC,iBAAiB;AAAA,EACpB;AAEA,sBAAoB,MAAM;AACxB,QAAI,kBAAkB,QAAW;AAC/B;AAAA,IACF;AACA,QAAI,eAAe;AACjB,mBAAa,SAAS,SAAS;AAC/B,mBAAa,KAAK;AAAA,IACpB,OAAO;AACL,mBAAa,SAAS,OAAO;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,eAAe,YAAY,CAAC;AAKhC,EAAAJ,YAAU,MAAM;AACd,UAAM,WAAW,qBAAqB;AACtC,yBAAqB,UAAU;AAC/B,QAAI,aAAa,UAAW;AAE5B,QAAI,CAAC,WAAW;AACd,YAAM,SAAS,SAAS;AACxB,YAAM,mBAAmB,QAAQ,SAAS;AAAA,QACxC,SAAS,oBAAoB;AAAA,MAC/B;AACA,UAAI,kBAAkB,eAAe,CAAC,kBAAkB,SAAS,MAAM,GAAG;AACxE,uBAAe,UAAU;AAAA,MAC3B;AACA,UAAI,QAAQ;AACV,cAAMK,SAAQ,sBAAsB,MAAM;AACxC,kBAAQ,SACJ,cAA2B,oDAAoD,GAC/E,MAAM;AAAA,QACZ,CAAC;AACD,eAAO,MAAM,qBAAqBA,MAAK;AAAA,MACzC;AACA;AAAA,IACF;AAEA,UAAM,QAAQ,sBAAsB,MAAM;AACxC,UAAI,CAAC,gBAAgB;AACnB,kBAAU,SAAS,MAAM;AACzB;AAAA,MACF;AACA,YAAM,SAAS,eAAe;AAC9B,UAAI,QAAQ,YAAa,QAAO,MAAM;AAAA,IACxC,CAAC;AACD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,WAAW,gBAAgB,MAAM,CAAC;AAKtC,EAAAL,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,aAAa,sBAAsB,QAAS;AAC3D,0BAAsB,UAAU;AAChC,UAAM,SAAS,SAAS;AACxB,UAAM,mBAAmB,QAAQ,SAAS,cAA2B,iBAAiB;AACtF,QAAI,kBAAkB,eAAe,CAAC,kBAAkB,SAAS,MAAM,GAAG;AACxE,qBAAe,UAAU;AAAA,IAC3B;AACA,UAAM,QAAQ,sBAAsB,MAAM;AACxC,wBAAkB,cAA2B,oCAAoC,GAAG,MAAM;AAAA,IAC5F,CAAC;AACD,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACzC,GAAG,CAAC,WAAW,MAAM,CAAC;AAGtB,EAAAA,YAAU,MAAM;AACd,QAAI,cAAc,CAAC,qBAAqB;AACtC,aAAO,UAAU;AAAA,IACnB;AAAA,EAGF,GAAG,CAAC,YAAY,qBAAqB,MAAM,CAAC;AAE5C,QAAM,WAAWI,cAAY,MAAM;AACjC,iBAAa,SAAS,SAAS;AAC/B,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,cAAc,YAAY,CAAC;AAC/B,QAAM,SAASA,cAAY,MAAM;AAC/B,iBAAa,SAAS,OAAO;AAC7B,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,cAAc,YAAY,CAAC;AAK/B,EAAAJ,YAAU,MAAM;AACd,UAAM,cAAc,aAAc,UAAU,CAAC;AAC7C,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,CAAC,UAAyB;AACtC,UAAI,MAAM,QAAQ,UAAU;AAC1B,YAAI,UAAW,cAAa,KAAK;AAAA,YAC5B,UAAS;AACd;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,MAAO;AACzB,YAAM,UAAU,QAAQ,SAAS;AAAA,QAC/B,SAAS,oBAAoB;AAAA,MAC/B;AACA,UAAI,CAAC,QAAS;AACd,YAAM,YAAY,MAAM,KAAK,QAAQ,iBAA8B,GAAG,CAAC,EAAE;AAAA,QACvE,CAAC,YACC,QAAQ,YAAY,KACpB,CAAC,QAAQ,aAAa,UAAU,KAChC,QAAQ,eAAe,EAAE,SAAS,KAClC,iBAAiB,OAAO,EAAE,eAAe;AAAA,MAC7C;AACA,YAAM,QAAQ,UAAU,CAAC;AACzB,YAAM,OAAO,UAAU,GAAG,EAAE;AAC5B,YAAM,SAAS,SAAS;AACxB,UAAI,CAAC,SAAS,CAAC,MAAM;AACnB,cAAM,eAAe;AACrB,gBAAQ,MAAM;AACd;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,SAAS,MAAM,GAAG;AAC7B,cAAM,eAAe;AACrB,SAAC,MAAM,WAAW,OAAO,OAAO,MAAM;AACtC;AAAA,MACF;AACA,UAAK,MAAM,YAAY,WAAW,SAAW,CAAC,MAAM,YAAY,WAAW,MAAO;AAChF,cAAM,eAAe;AACrB,SAAC,MAAM,WAAW,OAAO,OAAO,MAAM;AACtC;AAAA,MACF;AAIA,4BAAsB,MAAM;AAC1B,cAAM,OAAO,SAAS;AACtB,YAAI,CAAC,QAAQ,SAAS,IAAI,EAAG,EAAC,MAAM,WAAW,OAAO,OAAO,MAAM;AAAA,MACrE,CAAC;AAAA,IACH;AACA,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,WAAW,QAAQ,WAAW,QAAQ,CAAC;AAK3C,MAAI,QAAQ;AACV,WACE,gBAAAD,OAAC,SAAI,KAAK,SAAS,WAAW,GAAG,+CAA+C,SAAS,GACvF;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,0BAAsB;AAAA,UACtB,eAAa,CAAC,YAAY,OAAO;AAAA,UACjC,WAAU;AAAA,UACV,OAAO,CAAC,YAAY,OAAO;AAAA,UAE1B;AAAA;AAAA,MACH;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,0BAAsB;AAAA,UACtB,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAW;AAAA,UACX,WAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO;AAAA,YACL,YAAY;AAAA,YACZ,eAAe;AAAA,UACjB;AAAA,UAEA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA;AAAA,cACA,OAAO;AAAA,cACP,SAAS;AAAA,cACT,WAAW;AAAA,cACX,UACE,gBAAAA,MAAC,gBAAa,SAAS,UAAU,OAAM,mBAAkB,OAAM,mBAC7D,0BAAAA,MAACQ,QAAA,EAAM,WAAU,UAAS,GAC5B;AAAA;AAAA,UAEJ;AAAA;AAAA,MACF;AAAA,MACC,aAAa,CAAC,iBACb,gBAAAR;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS;AAAA,UACT,OAAM;AAAA,UACN,cAAW;AAAA,UACX,WAAU;AAAA,UACV,OAAO,EAAE,WAAW,2BAA2B;AAAA,UAE/C,0BAAAA,MAAC,yBAAsB,WAAU,UAAS,eAAW,MAAC;AAAA;AAAA,MACxD,IACE;AAAA,OACN;AAAA,EAEJ;AAEA,QAAM,aACJ,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,OAAO;AAAA,MACP,WAAW;AAAA,MACX,UACE,gBAAAC,OAAAF,WAAA,EACE;AAAA,wBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;AAAA,YACrC,OAAO,YAAY,kBAAkB;AAAA,YACrC,OAAO,YAAY,iBAAiB;AAAA,YAEnC,sBACC,gBAAAA,MAAC,iBAAc,WAAU,YAAW,IAEpC,gBAAAA,MAAC,iBAAc,WAAU,YAAW;AAAA;AAAA,QAExC;AAAA,QACC,iBAAiB,OAChB,gBAAAA,MAAC,gBAAa,SAAS,UAAU,OAAM,YAAW,OAAM,iBACtD,0BAAAA,MAAC,uBAAoB,WAAU,YAAW,GAC5C;AAAA,SAEJ;AAAA;AAAA,EAEJ;AAGF,SACE,gBAAAC,OAAC,SAAI,KAAK,SAAS,WAAW,GAAG,+CAA+C,SAAS,GACvF;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,aAAY;AAAA,QACZ,WAAU;AAAA,QACT,GAAI,gBAAgB,EAAE,cAAc,IAAI,CAAC;AAAA,QAC1C;AAAA,QAEA;AAAA,0BAAAD,MAAC,SAAM,IAAG,WAAU,SAAQ,OAAM,WAAU,mBAC1C,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,0BAAsB;AAAA,cACtB,eAAa,YAAY,OAAO;AAAA,cAChC,WAAU;AAAA,cACV,OAAO,YAAY,OAAO;AAAA,cAEzB;AAAA;AAAA,UACH,GACF;AAAA,UAEC,CAAC,aACA,gBAAAA,MAAC,aAAU,WAAU,sNACnB,0BAAAA,MAAC,UAAK,WAAU,iMAAgM,GAClN;AAAA,UAGF,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,UAAU;AAAA,cACV,aAAW;AAAA,cACX,eAAc;AAAA,cACd,aAAa,GAAG,WAAW;AAAA,cAC3B,SAAS,GAAG,OAAO;AAAA,cACnB,SAAS,GAAG,OAAO;AAAA,cACnB,UAAU,CAAC,MAAM,KAAK,iBAAiB;AAErC,sBAAM,cAAc,KAAK,gBAAgB;AACzC,sBAAM,mBAAmB,kBAAkB,UAAa,iBAAiB;AACzE,oBAAI,oBAAoB,gBAAgB,WAAW;AACjD,+BAAa,WAAW;AAAA,gBAC1B;AAAA,cACF;AAAA,cACA,WAAU;AAAA,cAIV,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,0BAAsB;AAAA,kBACtB,MAAM,YAAY,WAAW;AAAA,kBAC7B,cAAY,YAAY,OAAO;AAAA,kBAC/B,cAAY,YAAY,cAAc;AAAA,kBACtC,eAAa,YAAY,OAAO;AAAA,kBAChC,WAAW;AAAA,oBACT;AAAA,oBACA,YAAY,uBAAuB;AAAA,oBACnC,aAAa;AAAA,kBACf;AAAA,kBAEC;AAAA;AAAA,cACH;AAAA;AAAA,UACF;AAAA;AAAA;AAAA,IACF;AAAA,IAIC,aAAa,CAAC,aAAa,CAAC,kBAC3B,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,SAAS;AAAA,QACT,OAAM;AAAA,QACN,WAAU;AAAA,QAEV,0BAAAA,MAAC,yBAAsB,WAAU,YAAW;AAAA;AAAA,IAC9C;AAAA,KAEJ;AAEJ;AAGA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,cAAY;AAAA,MACZ,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAUG;AACD,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,KAAK,CAAC;AAC3D,QAAM,WAAWS,OAAM;AACvB,QAAM,UAAUJ,SAAwC,CAAC,CAAC;AAC1D,QAAM,aAAaA,SAA8B,IAAI;AACrD,QAAM,CAAC,aAAa,cAAc,IAAID,WAAsB,MAAM,oBAAI,IAAI,CAAC;AAC3E,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,EAAE,OAAO,OAAO,KAAK,MAAM,CAAC;AAC3E,QAAM,WAAW,QAAQ,MAAM;AAC/B,QAAM,iBAAiB,KAAK,UAAU,CAAC,QAAQ,IAAI,OAAO,QAAQ;AAClE,QAAM,uBAAuBE,cAAY,MAAM;AAC7C,UAAM,WAAW,kBAAkB,IAAI,QAAQ,QAAQ,cAAc,IAAI;AACzE,cAAU,eAAe,EAAE,OAAO,WAAW,QAAQ,UAAU,CAAC;AAAA,EAClE,GAAG,CAAC,cAAc,CAAC;AAEnB,EAAAJ,YAAU,MAAM;AACd,QAAI,CAAC,SAAU;AACf,mBAAe,CAAC,aAAa;AAC3B,UAAI,SAAS,IAAI,QAAQ,EAAG,QAAO;AACnC,YAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,WAAK,IAAI,QAAQ;AACjB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAMb,sBAAoB,MAAM;AACxB,yBAAqB;AAAA,EACvB,GAAG,CAAC,UAAU,oBAAoB,CAAC;AAKnC,sBAAoB,MAAM;AACxB,UAAM,OAAO,WAAW;AACxB,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,MAAM;AACnB,YAAM,MAAM,KAAK,IAAI,GAAG,KAAK,cAAc,KAAK,WAAW;AAC3D,YAAM,OAAO,EAAE,OAAO,KAAK,aAAa,GAAG,KAAK,KAAK,aAAa,MAAM,EAAE;AAC1E;AAAA,QAAe,CAAC,aACd,SAAS,UAAU,KAAK,SAAS,SAAS,QAAQ,KAAK,MAAM,WAAW;AAAA,MAC1E;AAAA,IACF;AACA,WAAO;AACP,SAAK,iBAAiB,UAAU,QAAQ,EAAE,SAAS,KAAK,CAAC;AACzD,UAAM,WACJ,OAAO,mBAAmB,cACtB,OACA,IAAI,eAAe,MAAM;AACvB,2BAAqB;AACrB,aAAO;AAAA,IACT,CAAC;AACP,cAAU,QAAQ,IAAI;AACtB,eAAW,OAAO,QAAQ,SAAS;AACjC,UAAI,IAAK,WAAU,QAAQ,GAAG;AAAA,IAChC;AACA,WAAO,MAAM;AACX,WAAK,oBAAoB,UAAU,MAAM;AACzC,gBAAU,WAAW;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,UAAU,sBAAsB,KAAK,MAAM,CAAC;AAEhD,QAAM,UACJ,YAAY,SAAS,YAAY,MAC7B,oGACA,YAAY,QACV,qEACA,YAAY,MACV,kFACA;AAEV,QAAM,cAAc,CAAC,UAAkB;AACrC,UAAM,MAAM,KAAK,KAAK;AACtB,QAAI,CAAC,KAAK;AACR;AAAA,IACF;AACA,UAAM,IAAI,EAAE;AACZ,YAAQ,QAAQ,KAAK,GAAG,MAAM;AAAA,EAChC;AAEA,QAAM,eAAe,CAAC,OAA8C,UAAkB;AACpF,QAAI,KAAK,WAAW,EAAG;AACvB,QAAI,YAA2B;AAC/B,QAAI,MAAM,QAAQ,aAAc,cAAa,QAAQ,KAAK,KAAK;AAAA,aACtD,MAAM,QAAQ,YAAa,cAAa,QAAQ,IAAI,KAAK,UAAU,KAAK;AAAA,aACxE,MAAM,QAAQ,OAAQ,aAAY;AAAA,aAClC,MAAM,QAAQ,MAAO,aAAY,KAAK,SAAS;AACxD,QAAI,cAAc,KAAM;AACxB,UAAM,eAAe;AACrB,gBAAY,SAAS;AAAA,EACvB;AAEA,SACE,gBAAAD,OAAAF,WAAA,EACE;AAAA,oBAAAE,OAAC,SAAI,WAAU,mTACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,sIACZ,qBACC,gBAAAA,MAAC,UAAK,WAAU,gFAA+E,uBAE/F,GAEJ;AAAA,MAQA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,WAAU;AAAA,UACV,MAAK;AAAA,UACL,oBAAiB;AAAA,UACjB,OAAO;AAAA,YACL,WAAW;AAAA,YACX,iBAAiB;AAAA,YACjB,qBAAqB;AAAA,UACvB;AAAA,UAEC,eAAK,IAAI,CAAC,KAAK,UACd,gBAAAC;AAAA,YAAC;AAAA;AAAA,cAEC,KAAK,CAAC,SAAS;AACb,wBAAQ,QAAQ,KAAK,IAAI;AAAA,cAC3B;AAAA,cACA,IAAI,GAAG,QAAQ,QAAQ,KAAK;AAAA,cAC5B,MAAK;AAAA,cACL,MAAK;AAAA,cACL,iBAAe,IAAI,OAAO;AAAA,cAC1B,iBAAe,GAAG,QAAQ,UAAU,KAAK;AAAA,cACzC,UAAU,IAAI,OAAO,UAAU,IAAI;AAAA,cACnC,SAAS,MAAM,MAAM,IAAI,EAAE;AAAA,cAC3B,WAAW,CAAC,UAAU,aAAa,OAAO,KAAK;AAAA,cAC/C,WAAW;AAAA,gBACT;AAAA,gBACA,IAAI,OAAO,UACP,iCACA;AAAA,cACN;AAAA,cAEA;AAAA,gCAAAD,MAAC,UAAK,yBAAqB,MAAE,cAAI,OAAM;AAAA,gBACtC,IAAI;AAAA;AAAA;AAAA,YApBA,IAAI;AAAA,UAqBX,CACD;AAAA;AAAA,MACH;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,iIACZ,qBACH;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,kIACZ,oBACH;AAAA,OACF;AAAA,IACA,gBAAAA,MAAC,SAAI,WAAU,0CACZ,eAAK,SAAS,IACb,KAAK,IAAI,CAAC,KAAK,UAAU;AACvB,YAAM,WAAW,IAAI,OAAO;AAC5B,YAAM,cAAc,YAAY,YAAY,IAAI,IAAI,EAAE;AACtD,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,IAAI,GAAG,QAAQ,UAAU,KAAK;AAAA,UAC9B,mBAAiB,GAAG,QAAQ,QAAQ,KAAK;AAAA,UACzC,WAAU;AAAA,UACV,QAAQ,CAAC;AAAA,UACT,MAAK;AAAA,UAEJ,wBAAc,IAAI,UAAU;AAAA;AAAA,QAPxB,IAAI;AAAA,MAQX;AAAA,IAEJ,CAAC,IAED,gBAAAA,MAAC,SAAI,WAAU,0BAAyB,cAAW,aAAY,MAAK,YAAW,GAEnF;AAAA,KACF;AAEJ;;;ACrrBA,SAAyB,eAAAU,eAAa,aAAAC,aAAW,WAAAC,WAAS,UAAAC,UAAQ,YAAAC,kBAAgB;AAClF,SAAS,eAAe;AAExB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,qBAAAC;AAAA,OACK;AAygByB,gBAAAC,OA2VxB,QAAAC,cA3VwB;AA9ezB,IAAM,wBAAwB;AAC9B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,wBAAwB;AAO9B,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,SAAS,mBAAmB,OAAiD;AAC3E,SAAQ,mBAAyC,SAAS,KAAK;AACjE;AAEA,SAAS,uBACP,YACA,gBACA,cACe;AACf,MAAI,cAAc,eAAgB,QAAO;AACzC,MAAI,aAAc,QAAO;AACzB,MAAI,eAAgB,QAAO;AAC3B,SAAO;AACT;AAEA,SAAS,uBAAuB,QAAwB;AACtD,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAYO,SAAS,oBACd,QACA,UACQ;AACR,MAAI,SAAU,QAAO;AACrB,MAAI,UAAU;AACd,MAAI,gBAAgB;AACpB,aAAW,SAAS,UAAU,CAAC,GAAG;AAChC,QAAI,MAAM,SAAS,8BAA+B;AAClD,QAAI,MAAM,YAAY,QAAS;AAC/B,UAAM,QAAS,MAAM,SAAuD;AAC5E,cAAU,MAAM;AAChB,oBAAgB,OAAO,OAAO,cAAc,WAAW,MAAM,YAAY;AAAA,EAC3E;AACA,SAAO,gBAAgB,IAAI,wBAAwB;AACrD;AAQA,SAAS,yBAAyB,QAAiC;AACjE,MAAI,WAAW;AACf,aAAW,SAAS,QAAQ;AAC1B,QACE,MAAM,SAAS,+BACf,MAAM,SAAS,iCACf,MAAM,SAAS,4BACf;AACA;AAAA,IACF;AACA,UAAM,UAAU,MAAM;AACtB,UAAM,OACJ,WAAW,OAAO,YAAY,YAAY,CAAC,MAAM,QAAQ,OAAO,IAC3D,QAAoC,OACrC;AACN,QAAI,SAAS,oBAAqB;AAClC,QAAI,MAAM,SAAS,6BAA6B;AAC9C,iBAAW;AAAA,IACb,OAAO;AACL,iBAAW;AAAA,IACb;AAAA,EACF;AACA,SAAO;AACT;AA4DA,SAAS,iBAAiB,WAAuC;AAC/D,SAAO;AAAA,IACL;AAAA,IACA,cAAc;AAAA,IACd,cAAc;AAAA,IACd,WAAW;AAAA,EACb;AACF;AAOO,SAAS,wBACd,SAC+B;AAC/B,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,EAAE,WAAW,QAAQ,UAAU,mBAAmB,wBAAwB,WAAW,IACzF;AACF,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,oBAAoB,QAAQ,YAAY;AAC9C,QAAM,aAAa,IAAI,IAA6B,iBAAiB;AACrE,QAAM,iBAAiB,WAAW,IAAI,qBAAqB;AAC3D,QAAM,eAAe,WAAW,IAAI,mBAAmB;AACvD,QAAM,kBAAkB,WAAW,IAAI,sBAAsB;AAC7D,QAAM,iBAAiB,WAAW,IAAI,qBAAqB;AAC3D,QAAM,uBAAuB,kBAAkB;AAC/C,QAAM,oBAAoB,wBAAwB,mBAAmB;AACrE,QAAM,kBAAkB,mBAAmB,OAAO,CAAC,YAAY,WAAW,IAAI,OAAO,CAAC,EAAE,KAAK,GAAG;AAOhG,QAAM,CAAC,mBAAmB,oBAAoB,IAAIC;AAAA,IAA6B,MAC7E,iBAAiB,SAAS;AAAA,EAC5B;AACA,QAAM,cACJ,kBAAkB,cAAc,YAAY,oBAAoB,iBAAiB,SAAS;AAC5F,QAAM,EAAE,cAAc,cAAc,UAAU,IAAI;AAClD,QAAM,oBAAoBC;AAAA,IACxB,CAAC,WAA2D;AAC1D,2BAAqB,CAAC,aAAa;AACjC,cAAM,UAAU,SAAS,cAAc,YAAY,WAAW,iBAAiB,SAAS;AACxF,eAAO,QAAQ,MAAM,IAAI,UAAU,EAAE,GAAG,SAAS,CAAC,MAAM,GAAG,KAAK;AAAA,MAClE,CAAC;AAAA,IACH;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAIA,QAAM,WAAW,YAAY;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,SAAS;AAAA,EACX,CAAC;AACD,QAAM,gBACJ,SAAS,SAAS,KAAK,CAAC,MAAM,EAAE,cAAc,SAAS,eAAe,KACtE,SAAS,SAAS,KAAK,CAAC,MAAM,EAAE,MAAM,KACtC;AAEF,QAAM,OAAO,uBAAuB,WAAW;AAAA,IAC7C;AAAA,IACA,SAAS;AAAA,IACT,eAAe,kBAAkB;AAAA,IACjC,gBAAgB,mBAAmB;AAAA;AAAA;AAAA,IAGnC,aAAa,wBAAwB;AAAA,EACvC,CAAC;AACD,QAAM,eAAe,KAAK;AAC1B,QAAM,WAAW,cAAc;AAC/B,QAAM,eAAe,cAAc,WAAW,aAAa;AAO3D,QAAM,aAAa,cAAc,WAAW,YAAY;AAIxD,QAAM,gBACJ,gBAAgB,CAAC,eAAe,aAAa,UAAU,aAAa;AACtE,QAAM,QAAQ,cAAc,IAAI,aAAa;AAC7C,QAAM,aAAa,oBAAoB,cAAc,SAAS,aAAa,UAAU;AAIrF,QAAM,YACJ,cAAc,SAAS,cAAc,YAAY,QAAQ,cAAc,SAAS,GAAG;AACrF,QAAM,cAAc,cAAc,SAAS,cAAc,UAAU,CAAC;AACpE,QAAM,WAAW,mBAAmB,WAAW;AAAA,IAC7C,QAAQ,kBAAkB,SAAS,CAAC;AAAA,IACpC,aAAa,mBAAmB;AAAA,IAChC;AAAA,EACF,CAAC;AACD,QAAM,oBACJ,oBACE,cAAc,cAAc,aAAa,UAAU,QACnD,cAAc,cAAc,WAAW;AAQ3C,QAAM,eAAe,yBAAyB,MAAM;AACpD,QAAM,cAAc,KAAK;AACzB,QAAM,kBAAkBC,SAAO,EAAE,WAAW,aAAa,CAAC;AAC1D,EAAAC,YAAU,MAAM;AACd,UAAM,WAAW,gBAAgB;AACjC,QAAI,SAAS,cAAc,aAAa,SAAS,gBAAgB,CAAC,cAAc;AAC9E,kBAAY;AAAA,IACd;AACA,oBAAgB,UAAU,EAAE,WAAW,aAAa;AAAA,EACtD,GAAG,CAAC,WAAW,cAAc,WAAW,CAAC;AAMzC,QAAM,eAAe,oBAAoB,WAAW;AAAA,IAClD;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACD,QAAM,mBAAmB,aAAa;AACtC,QAAM,wBAAwB,aAAa,UAAU,aAAa;AAOlE,QAAM,iBACJ,CAAC,0BAA0B,aAAa,cAAc,QAAQ,aAAa;AAC7E,QAAM,YAAYC,UAAQ,MAAM;AAC9B,UAAM,aAAa,cAAc,IAAI,SAAS,CAAC;AAC/C,QAAI,WAAW,SAAS,EAAG,QAAO;AAClC,WAAO,aAAa,SAAS,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI,KAAK,CAAC;AAAA,EAClE,GAAG,CAAC,cAAc,IAAI,OAAO,aAAa,OAAO,CAAC;AAClD,QAAM,gCAAgCF,SAGnC;AAAA,IACD;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AACD,MAAI,8BAA8B,QAAQ,cAAc,WAAW;AACjE,kCAA8B,UAAU,EAAE,WAAW,QAAQ,KAAK;AAAA,EACpE;AACA,EAAAC,YAAU,MAAM;AACd,UAAM,SAAS,aAAa;AAC5B,QAAI,CAAC,UAAU,8BAA8B,QAAQ,WAAW,OAAQ;AACxE,kCAA8B,UAAU,EAAE,WAAW,OAAO;AAC5D,eAAW,EAAE,MAAM,SAAS,SAAS,uBAAuB,MAAM,EAAE,CAAC;AAAA,EACvE,GAAG,CAAC,WAAW,aAAa,gBAAgB,QAAQ,CAAC;AAErD,QAAM,QAAQ,gBAAgB,WAAW;AAAA,IACvC;AAAA;AAAA;AAAA,IAGA,SAAS,iBAAiB,oBAAqB,gBAAgB;AAAA,IAC/D;AAAA,IACA,SAAS,aAAa;AAAA;AAAA;AAAA,IAGtB,iBAAiB,CAAC,OAAO,OACvB,WAAW;AAAA,MACT,MAAM;AAAA,MACN,SAAS,aAAa,EAAE,KAAK,MAAM,OAAO;AAAA,IAC5C,CAAC;AAAA,EACL,CAAC;AACD,QAAM,MAAM,cAAc,WAAW;AAAA,IACnC;AAAA,IACA,SAAS,yBAAyB,oBAAqB,SAAS;AAAA,IAChE;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,EACxB,CAAC;AACD,QAAM,YAAY,cAAc,WAAW;AAAA,IACzC;AAAA;AAAA;AAAA,IAGA,SAAS,gBAAgB,SAAS;AAAA,IAClC,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,EACxB,CAAC;AAID,QAAM,CAAC,YAAY,aAAa,IAAIH,WAAiC,MAAS;AAC9E,EAAAG,YAAU,MAAM;AACd,QAAI,CAAC,mBAAmB,OAAO,aAAa,YAAa;AACzD,UAAM,SAAS,MAAM,cAAc,qBAAqB,CAAC;AACzD,WAAO;AACP,UAAM,WAAW,IAAI,iBAAiB,MAAM;AAC5C,aAAS,QAAQ,SAAS,iBAAiB;AAAA,MACzC,YAAY;AAAA,MACZ,iBAAiB,CAAC,iBAAiB,OAAO;AAAA,IAC5C,CAAC;AACD,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,eAAe,CAAC;AAEpB,iBAAe,sBAAsB;AACnC,QAAI;AACF,YAAM,SAAS,cAAc,cAAc,UAAU;AACrD,YAAM,OAAO,kBAAkB,aAAa,WAAW;AAAA,QACrD,uBAAuB;AAAA,QACvB,mBAAmB;AAAA,MACrB,CAAC;AACD,wBAAkB,cAAc;AAChC,WAAK,YAAY;AAAA,IACnB,SAAS,OAAO;AACd,iBAAW;AAAA,QACT,MAAM;AAAA,QACN,SAAS,uCAAuC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MACxG,CAAC;AAAA,IACH;AAAA,EACF;AAIA,WAAS,gBAAgB;AACvB,QAAI,CAAC,aAAc,mBAAkB,cAAc;AACnD,SAAK,YAAY;AAAA,EACnB;AAEA,QAAM,aAAa,IAAI,KAAK;AAI5B,QAAM,OAAO,eAAe;AAAA,IAC1B;AAAA,IACA,mBAAmB,KAAK;AAAA,IACxB,oBAAoB,eAAe,SAAS;AAAA,IAC5C,oBAAoB,eAAe,SAAS;AAAA,IAC5C,WACG,mBAAmB,gBACnB,kBAAkB,gBAClB,wBAAwB;AAAA,IAC3B,YAAY,aAAa;AAAA,EAC3B,CAAC;AACD,QAAM,kBAAkB,KAAK,UAAU;AAUvC,QAAM,kBAAkB,GAAG,SAAS,KAAS,eAAe;AAC5D,QAAM,gBAAgBD,SAAmD;AAAA,IACvE,UAAU;AAAA,IACV,OAAO;AAAA,EACT,CAAC;AACD,MAAI,cAAc,QAAQ,aAAa,iBAAiB;AACtD,kBAAc,UAAU,EAAE,UAAU,iBAAiB,OAAO,KAAK;AAAA,EACnE;AACA,QAAM,yBACJ,CAAC,0BAA0B,aAAa,UAAa,KAAK,UAAU;AACtE,QAAM,qBAAqB,aAAa,cAAc,KAAK,aAAa,UAAU;AAClF,MAAI,cAAc,QAAQ,UAAU,MAAM;AACxC,QAAI,eAAe,CAAC,mBAAmB,UAAU,KAAK,WAAW,IAAI,UAAU,IAAI;AACjF,oBAAc,QAAQ,QAAQ;AAAA,IAChC,WAAW,IAAI,WAAW,QAAQ;AAChC,oBAAc,QAAQ,QAAQ;AAAA,QAC5B,IAAI,KAAK,SAAS;AAAA,QAClB;AAAA,QACA;AAAA,MACF;AAAA,IACF,WACE,0BACA,aAAa,cAAc,QAC3B,aAAa,WACb;AACA,oBAAc,QAAQ,QAAQ;AAAA,QAC5B,aAAa,YAAY;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAAA,IACF,WACE,0BACA,sBACA,oBAAoB,MAAM,MAAM,uBAChC;AACA,oBAAc,QAAQ,QAAQ,uBAAuB,MAAM,gBAAgB,YAAY;AAAA,IACzF,WAAW,sBAAsB,KAAK,OAAO;AAG3C,oBAAc,QAAQ,QAAQ,uBAAuB,MAAM,gBAAgB,YAAY;AAAA,IACzF,WAAW,0BAA0B,aAAa,cAAc,QAAQ,oBAAoB;AAI1F,oBAAc,QAAQ,QAAQ,uBAAuB,OAAO,gBAAgB,YAAY;AAAA,IAC1F,WAAW,aAAa,aAAa,IAAI,SAAS,aAAa,cAAc,MAAM;AAGjF,oBAAc,QAAQ,QAAQ;AAAA,QAC5B,aAAa,YAAY;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,sBAAsB,IAAI,OAAO;AAG1C,oBAAc,QAAQ,QAAQ,uBAAuB,OAAO,gBAAgB,YAAY;AAAA,IAC1F;AAAA,EACF;AAEA,QAAM,aAAa,cAAc,QAAQ;AAEzC,QAAM,OAAOE,UAAQ,MAAM;AACzB,UAAM,OAAuB,CAAC;AAG9B,QAAI;AACF,WAAK,KAAK;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO,aAAa,IAAI,gBAAAN,MAAC,cAAW,OAAO,YAAY,IAAK;AAAA,QAC5D,SACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA,iBAAiB,aAAa;AAAA,YAC9B;AAAA,YACA;AAAA,YACA;AAAA,YACA,mBAAmB,KAAK;AAAA,YACxB,mBAAmB,KAAK;AAAA,YACxB,SAAS,KAAK;AAAA,YACd,QAAQ,MAAM,kBAAkB,WAAW;AAAA,YAC1C,GAAI,gBAAgB,aACjB;AAAA,cACE,YAAY,CAAC,SAAiB;AAC5B,kCAAkB,WAAW;AAC7B,2BAAW,IAAI;AAAA,cACjB;AAAA,YACF,IACA,CAAC;AAAA;AAAA,QACP;AAAA,MAEJ,CAAC;AAGH,QAAI;AACF,WAAK,KAAK;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,SACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA;AAAA,YACA;AAAA,YACA,qBAAqB,gBAAgB;AAAA,YACrC,UACE,iBAAiB,MAAM,WAAW,UAAU,MAAM,UAAU,QAAQ,CAAC,MAAM;AAAA,YAE7E,kBACE,CAAC,yBACD,aAAa,UACb,CAAC,kBACD,CAAC,oBACD,CAAC;AAAA,YAEH,iBAAiB,CAAC,yBAAyB,CAAC,oBAAoB;AAAA,YAChE,oBAAoB;AAAA,YACpB,iBAAiB,MAAM,kBAAkB,WAAW;AAAA,YACnD,GAAI,oBACD;AAAA,cACE,eAAe;AAAA,cACf,GAAI,2BAA2B,QAAQ,2BAA2B,SAC9D,EAAE,wBAAwB,uBAAuB,IACjD,CAAC;AAAA,cACL,oBAAoB,aAAa,UAAU,aAAa;AAAA,YAC1D,IACA,CAAC;AAAA,YAGL,cAAc,MAAM,kBAAkB,WAAW;AAAA,YACjD,WAAU;AAAA;AAAA,UA9BL;AAAA,QA+BP;AAAA,MAEJ,CAAC;AAGH,QAAI,YAAY;AACd,WAAK,KAAK;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,SACE,gBAAAA,MAAC,SAAI,WAAU,uBACb,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ;AAAA,YACR,oBAAoB,cAAc,YAAY;AAAA,YAC9C,YAAY,MAAM,kBAAkB,cAAc;AAAA,YAClD,YAAU;AAAA,YACV,OAAO,cAAc,SAAS,SAAS;AAAA,YACvC;AAAA,YACC,GAAI,aAAa,EAAE,OAAO,WAAW,IAAI,CAAC;AAAA;AAAA,QAC7C,GACF;AAAA,MAEJ,CAAC;AAAA,IACH;AAGA,QAAI,mBAAmB;AACrB,WAAK,KAAK;AAAA,QACR,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,OAAO,eACL,gBAAAA,MAAC,UAAK,WAAU,gFAA+E,kBAE/F,IACE;AAAA,QACJ,SACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,YAAY,cAAc,iBAAiB;AAAA,YAC3C,kBAAkB,KAAK;AAAA,YACvB,UAAU;AAAA,YACV,eAAe,MAAM,KAAK,oBAAoB;AAAA,YAC9C,QAAQ;AAAA,YACR,WAAU;AAAA;AAAA,QACZ;AAAA,MAEJ,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EAET,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS;AAAA,MACP,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA,SAAS,SAAS;AAAA,MAClB,OAAO,SAAS;AAAA,MAChB,SAAS,SAAS;AAAA,IACpB;AAAA,EACF;AACF;AAyCO,SAAS,iBAAiB,OAAyC;AACxE,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,CAAC,iBAAiB,kBAAkB,IAAIE,WAGpC,IAAI;AACd,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAIhC,IAAI;AACd,QAAM,oBAAoBE,SAAO,CAAC;AAClC,QAAM,WAAWD;AAAA,IACf,CAAC,SAAiB;AAChB,wBAAkB,WAAW;AAC7B,uBAAiB,EAAE,WAAW,MAAM,WAAW,kBAAkB,QAAQ,CAAC;AAC1E,yBAAmB,EAAE,WAAW,KAAK,oBAAoB,CAAC;AAAA,IAC5D;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF,IAAI,wBAAwB;AAAA,IAC1B,GAAI,MAAM,SAAS,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC/C,GAAI,MAAM,cAAc,EAAE,aAAa,MAAM,YAAY,IAAI,CAAC;AAAA,IAC9D;AAAA,IACA;AAAA,IACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IACnC,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,mBAAmB,eAAe,cAAc,YAAY,cAAc,OAAO;AAAA,IACjF,wBAAwB,eAAe,cAAc,YAAY,cAAc,YAAY;AAAA,IAC3F,YAAY;AAAA,EACd,CAAC;AAOD,QAAM,OAAuB,CAAC,GAAI,eAAe,CAAC,GAAI,GAAG,eAAe,GAAI,gBAAgB,CAAC,CAAE;AAC/F,QAAM,cAAc,iBAAiB,cAAc,YAAY,gBAAgB,MAAM;AACrF,QAAM,eAAe,eAAe;AACpC,QAAM,YACJ,gBAAgB,KAAK,KAAK,CAAC,QAAQ,IAAI,OAAO,YAAY,IAAI,eAAe,KAAK,CAAC,GAAG;AACxF,QAAM,YAAYA;AAAA,IAChB,CAAC,QAAgB,mBAAmB,EAAE,WAAW,IAAI,CAAC;AAAA,IACtD,CAAC,SAAS;AAAA,EACZ;AAEA,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACC,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MAChD,mBAAmB;AAAA,MAClB,GAAI,QAAQ,UACT;AAAA,QACE,iBACE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,QAAQ;AAAA,YACd,eAAe,QAAQ;AAAA,YACvB,OAAO,QAAQ;AAAA,YACf,SAAS,MAAM,KAAK,QAAQ,QAAQ;AAAA;AAAA,QACtC;AAAA,MAEJ,IACA,CAAC;AAAA,MACJ,GAAI,yBAAyB,SAAY,EAAE,qBAAqB,IAAI,CAAC;AAAA,MACrE,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MAC/C,GAAI,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;AAAA,MACjD,GAAI,eAAe,SAAY,EAAE,WAAW,IAAI,CAAC;AAAA,MACjD,GAAI,gBAAgB,SAAY,EAAE,YAAY,IAAI,CAAC;AAAA,MACnD,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA;AAAA,EAClD;AAEJ;AAKA,SAAS,WAAW,EAAE,MAAM,GAAsB;AAChD,SACE,gBAAAA,MAAC,UAAK,WAAU,oEACb,iBACH;AAEJ;AAGA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AACF,GAGG;AACD,QAAM,OAAO,SAAS,eAAe,aAAa;AAClD,SAAO,gBAAAA,MAAC,QAAK,WAAW,GAAG,uCAAuC,SAAS,GAAG,eAAW,MAAC;AAC5F;AAEA,SAAS,aAAa,OAAqC;AACzD,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,SAAU,QAAO;AAC/B,SAAO;AACT;AASA,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,aAAaI,SAAiC,IAAI;AACxD,QAAM,cAAc,oBAAoB,UAAU;AAClD,SACE,gBAAAH,OAAC,QAAQ,MAAR,EACC;AAAA,oBAAAD,MAAC,QAAQ,SAAR,EAAgB,SAAO,MACtB,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,cAAY,YAAY,KAAK,KAAK;AAAA,QAClC,WAAU;AAAA,QAEV;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,GAAG,kCAAkC,aAAa,KAAK,KAAK,CAAC;AAAA,cACxE,eAAW;AAAA;AAAA,UACb;AAAA,UACA,gBAAAA,MAAC,UAAK,WAAU,0BAA0B,eAAK,OAAM;AAAA;AAAA;AAAA,IACvD,GACF;AAAA,IACA,gBAAAA,MAAC,QAAQ,QAAR,EAAe,YAAU,MACxB,0BAAAC;AAAA,MAAC,QAAQ;AAAA,MAAR;AAAA,QACC,YAAU;AAAA,QACV,8BAA0B;AAAA,QAC1B,OAAM;AAAA,QACN,YAAY;AAAA,QACZ,kBAAkB;AAAA,QAClB,WAAU;AAAA,QACV,OAAO;AAAA,QAEP;AAAA,0BAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,4BAAAD,MAAC,mBAAgB,MAAM,eAAe,MAAM;AAAA,YAC5C,gBAAAA,MAAC,UAAK,WAAU,gCAAgC,yBAAe,QAAQ,WAAU;AAAA,aACnF;AAAA,UACA,gBAAAC,OAAC,SAAI,WAAU,kDACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,gCAA+B,wBAAU;AAAA,YACxD,gBACC,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,QAAQ,yBAAyB,cAAc,KAAK;AAAA,gBACpD,MAAK;AAAA;AAAA,YACP,IAEA,gBAAAA,MAAC,UAAK,WAAU,+BAA+B,eAAK,OAAM;AAAA,aAE9D;AAAA,UACC,KAAK,OACJ,gBAAAC,OAAC,OAAE,WAAU,iDAAgD;AAAA;AAAA,YACpC,WAAW,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,YAAE;AAAA,aAC3D,IACE;AAAA,UACH,iBAAiB,cAAc,qBAAqB,IACnD,gBAAAD,MAAC,SAAI,WAAU,UACb,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,oBAAoB,cAAc;AAAA,cAClC,SAAQ;AAAA;AAAA,UACV,GACF,IACE;AAAA,UACH,QACC,gBAAAC,OAAC,SAAI,WAAU,uDACb;AAAA,4BAAAD,MAAC,OAAE,WAAU,uCAAsC,0DAEnD;AAAA,YACA,gBAAAC,OAAC,oBAAiB,SAAS,SACzB;AAAA,8BAAAD,MAACO,gBAAA,EAAc,WAAU,UAAS;AAAA,cAAE;AAAA,eAEtC;AAAA,aACF,IACE;AAAA;AAAA;AAAA,IACN,GACF;AAAA,KACF;AAEJ;AAGA,SAAS,iBAAiB,EAAE,SAAS,SAAS,GAAiD;AAC7F,SACE,gBAAAP;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;AAQA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAYG;AACD,QAAM,OAAO,IAAI;AAEjB,MAAI,KAAK,SAAS,GAAG;AACnB,WACE,gBAAAC,OAAC,SAAI,WAAU,gCACZ;AAAA,UAAI,QACH,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,aAAU;AAAA,UACV,kCAA8B;AAAA,UAC9B,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAACQ,oBAAA,EAAkB,WAAU,4CAA2C,eAAW,MAAC;AAAA,YACpF,gBAAAR,MAAC,UAAK,WAAU,kBACb,cAAI,WAAW,YACZ,oFACA,yDACN;AAAA,YACA,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,KAAK,IAAI,QAAQ;AAAA,gBAChC,UAAU,IAAI;AAAA,gBACd,WAAU;AAAA,gBAEV;AAAA,kCAAAD;AAAA,oBAACO;AAAA,oBAAA;AAAA,sBACC,WAAW,GAAG,UAAU,IAAI,WAAW,yCAAyC;AAAA,sBAChF,eAAW;AAAA;AAAA,kBACb;AAAA,kBAAE;AAAA;AAAA;AAAA,YAEJ;AAAA;AAAA;AAAA,MACF,IACE;AAAA,MACJ,gBAAAP,MAAC,SAAI,WAAU,kBACb,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,QAAQ,IAAI;AAAA,UACZ,YAAY,IAAI;AAAA,UAChB;AAAA,UACC,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA;AAAA,MACtC,GACF;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,qBAAqB,CAAC,kBAAkB;AAC1C,WACE,gBAAAC,OAAC,iBAAc,MAAM,gBAAAD,MAACQ,oBAAA,EAAkB,WAAU,UAAS,eAAW,MAAC,GAAI,MAAK,UAC9E;AAAA,sBAAAR,MAAC,OAAE,WAAU,qCAAoC,iCAAmB;AAAA,MACpE,gBAAAA,MAAC,OAAE,yBAAqB,MAAC,WAAU,yCAChC,4BAAkB,WAAW,gDAChC;AAAA,MACA,gBAAAC,OAAC,oBAAiB,SAAS,SACzB;AAAA,wBAAAD,MAACO,gBAAA,EAAc,WAAU,UAAS;AAAA,QAAE;AAAA,SAEtC;AAAA,OACF;AAAA,EAEJ;AAEA,OACG,kBAAkB,sBAAsB,kBACzC,CAAC,oBACD,CAAC,iBACD;AACA,WACE,gBAAAN;AAAA,MAAC;AAAA;AAAA,QACC,MACE,gBAAAD;AAAA,UAACS;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA;AAAA,QACb;AAAA,QAGF;AAAA,0BAAAT,MAAC,OAAE,WAAU,qCAAoC,kCAAoB;AAAA,UACrE,gBAAAA,MAAC,OAAE,WAAU,0CAAyC,4DAEtD;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,CAAC,yBAAyB,CAAC,kBAAkB;AAC/C,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MACE,kBACE,gBAAAD;AAAA,UAACS;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA;AAAA,QACb,IAEA,gBAAAT,MAAC,WAAQ,WAAU,UAAS,eAAW,MAAC;AAAA,QAI5C;AAAA,0BAAAA,MAAC,OAAE,WAAU,qCACV,4BAAkB,qBAAqB,wBAC1C;AAAA,UACA,gBAAAA,MAAC,OAAE,WAAU,0CACV,4BACG,8CACA,uFACN;AAAA,UACC,CAAC,kBACA,gBAAAC,OAAC,oBAAiB,SAAS,QACzB;AAAA,4BAAAD,MAAC,WAAQ,WAAU,UAAS;AAAA,YAAE;AAAA,aAEhC,IACE;AAAA;AAAA;AAAA,IACN;AAAA,EAEJ;AAEA,MAAI,IAAI,WAAW,IAAI,WAAW,MAAM;AACtC,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MACE,gBAAAD;AAAA,UAACS;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,eAAW;AAAA;AAAA,QACb;AAAA,QAGF;AAAA,0BAAAT,MAAC,OAAE,WAAU,qCAAoC,+BAAiB;AAAA,UAClE,gBAAAA,MAAC,OAAE,WAAU,0CAAyC,oDAAiC;AAAA;AAAA;AAAA,IACzF;AAAA,EAEJ;AAEA,SACE,gBAAAC,OAAC,iBAAc,MAAM,gBAAAD,MAAC,mBAAgB,WAAU,UAAS,eAAW,MAAC,GAAI,MAAK,WAC5E;AAAA,oBAAAA,MAAC,OAAE,WAAU,qCAAoC,mCAAqB;AAAA,IACtE,gBAAAA,MAAC,OAAE,WAAU,0CAAyC,4DAEtD;AAAA,KACF;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA,OAAO;AACT,GAIG;AACD,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,SAAS,WAAW,UAAU;AAAA,MACpC,eAAY;AAAA,MACZ,aAAW,SAAS,WAAW,cAAc;AAAA,MAC7C,WAAU;AAAA,MAEV,0BAAAC,OAAC,SAAI,WAAU,+CACZ;AAAA,eACC,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,SAAS,aAAa;AAAA,cACtB,SAAS,YAAY;AAAA,cACrB,SAAS,aAAa;AAAA,YACxB;AAAA,YAEC;AAAA;AAAA,QACH,IACE;AAAA,QACH;AAAA,SACH;AAAA;AAAA,EACF;AAEJ;;;AC1pCA,SAAS,eAAAU,eAAa,YAAAC,kBAAgB;AAgB/B,SAAS,oBAAoB,OAA4C;AAC9E,SAAO,MAAM,SAAS,4BAA4B,MAAM,SAAS;AACnE;AA8DA,IAAM,iBAAwC;AAAA,EAC5C,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,oBAAoB;AACtB;AAUA,IAAM,cAAkC;AAAA,EACtC,UAAU,CAAC;AAAA,EACX,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,eAAe;AACjB;AASO,SAAS,iBAAiB,UAAmC,CAAC,GAA2B;AAC9F,QAAM,EAAE,QAAQ,YAAY,IAAI,YAAY,OAAO;AACnD,QAAM,cAAc,QAAQ,eAAgB;AAC5C,QAAM,YAAY,QAAQ;AAC1B,QAAM,eAAe,QAAQ;AAE7B,QAAM,OAAOC,cAAY,YAAyC;AAChE,UAAM,YAAY,YAAY,kBAAkB,WAAW;AAC3D,UAAM,WACJ,aAAa,YAAY,aACrB,YAAY,WAAW,aAAa,SAAS,EAAE,MAAM,MAAM,IAAI,IAC/D,QAAQ,QAAQ,IAAI;AAC1B,UAAM,CAAC,KAAK,OAAO,IAAI,MAAM,QAAQ,IAAI,CAAC,WAAW,QAAQ,CAAC;AAC9D,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,iBAAiB,IAAI;AAAA,MACrB,UAAU,IAAI;AAAA,MACd,iBAAiB,SAAS,2BAA2B;AAAA,MACrD,eAAe,SAAS,yBAAyB;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,aAAa,aAAa,SAAS,CAAC;AAExC,QAAM;AAAA,IACJ,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF,IAAI,eAAe,MAAM;AAAA,IACvB,gBAAgB,QAAQ;AAAA,IACxB,SAAS,QAAQ;AAAA,EACnB,CAAC;AACD,QAAM,EAAE,KAAK,aAAa,UAAU,SAAS,cAAc,IAAI,kBAAkB;AACjF,QAAM,EAAE,KAAK,kBAAkB,UAAU,gBAAgB,IAAI,kBAAkB;AAC/E,QAAM,CAAC,eAAe,gBAAgB,IAAIC,WAAwB,IAAI;AAItE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,KAAK,QAAQ;AAAA,IACnB;AAAA,MACE,SAAS,QAAQ,WAAW;AAAA,MAC5B,GAAI,iBAAiB,SAAY,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,MAAMD;AAAA,IACV,OAAO,WAAqC;AAC1C,UAAI,CAAC,aAAa,CAAC,YAAY,wBAAwB;AACrD,eAAO;AAAA,MACT;AACA,uBAAiB,MAAM;AACvB,YAAM,SAAS,MAAM,YAAY,YAAY;AAC3C,cAAM,YAAY,uBAAwB,aAAa,WAAW,MAAM;AACxE,eAAO;AAAA,MACT,CAAC;AACD,uBAAiB,IAAI;AACrB,UAAI,OAAQ,OAAM,QAAQ;AAC1B,aAAO,WAAW;AAAA,IACpB;AAAA,IACA,CAAC,aAAa,aAAa,WAAW,aAAa,OAAO;AAAA,EAC5D;AAKA,QAAM,eAAeA,cAAY,YAA8B;AAC7D,QAAI,CAAC,YAAY,mBAAmB;AAClC,aAAO;AAAA,IACT;AACA,UAAM,SAAS,MAAM,iBAAiB,YAAY;AAChD,YAAM,YAAY,kBAAmB,WAAW;AAChD,aAAO;AAAA,IACT,CAAC;AACD,QAAI,OAAQ,OAAM,QAAQ;AAC1B,WAAO,WAAW;AAAA,EACpB,GAAG,CAAC,aAAa,aAAa,kBAAkB,OAAO,CAAC;AAExD,QAAM,OAAO,cAAc;AAC3B,QAAM,qBAAqB,KAAK,mBAAmB,KAAK;AAExD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,iBAAiB,KAAK;AAAA,IACtB,iBAAiB,KAAK;AAAA,IACtB;AAAA,IACA,eAAe,KAAK;AAAA,IACpB,UAAU,KAAK;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["useCallback","useCallback","useCallback","useEffect","useEffect","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useEffect","useRef","useRef","useCallback","useEffect","useEffect","useRef","useState","useEffect","useRef","useState","useState","useRef","useEffect","useEffect","useMemo","useRef","useState","OpenGeniApiError","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","useEffect","useMemo","OpenGeniApiError","useCallback","useCallback","useEffect","useRef","useState","isLive","useState","useRef","useCallback","useEffect","useCallback","useEffect","useMemo","useRef","useState","useMemo","isLive","useState","useRef","useCallback","useEffect","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useMemo","useEffect","useRef","useState","useState","useRef","useEffect","jsx","jsxs","jsxs","jsx","CommandPalette","ModelPicker","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useEffect","useCallback","useEffect","useId","useMemo","useRef","useState","useEffect","useMemo","useMemo","useEffect","Fragment","jsx","jsxs","useEffect","parentOf","useState","useId","useRef","useCallback","useMemo","item","useCallback","useEffect","useId","useLayoutEffect","useMemo","useRef","useState","VList","useCallback","useEffect","useMemo","useRef","useState","jsx","jsxs","useEffect","useLayoutEffect","STATUS_TINT","useRef","useState","useId","useMemo","useCallback","VList","jsx","useEffect","useState","jsx","useState","useEffect","useCallback","useEffect","useMemo","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","useMemo","FileWarningIcon","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","FileWarningIcon","LoaderCircleIcon","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useEffect","LoaderCircleIcon","useCallback","useEffect","useId","useLayoutEffect","useRef","useState","XIcon","Fragment","jsx","jsxs","useEffect","useLayoutEffect","useState","useRef","useCallback","frame","XIcon","useId","useCallback","useEffect","useMemo","useRef","useState","LoaderCircleIcon","RefreshCwIcon","TriangleAlertIcon","jsx","jsxs","useState","useCallback","useRef","useEffect","useMemo","RefreshCwIcon","TriangleAlertIcon","LoaderCircleIcon","useCallback","useState","useCallback","useState"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-last-started-turn-policy.ts","../src/hooks/use-scheduled-tasks.ts","../src/hooks/use-workspace-sessions.ts","../src/hooks/use-environments.ts","../src/hooks/use-rigs.ts","../src/hooks/use-packs.ts","../src/hooks/use-workspaces.ts","../src/hooks/use-billing-usage.ts","../src/hooks/use-available-models.ts","../src/hooks/use-session-capabilities.ts","../src/hooks/use-desktop-stream.ts","../src/lib/relay-wire.ts","../src/hooks/use-relay-frame-stream.ts","../src/hooks/use-terminal-stream.ts","../src/hooks/use-sandbox-terminal.ts","../src/hooks/use-sandbox-files.ts","../src/hooks/use-sandbox-git.ts","../src/hooks/use-workspace-capture.ts","../src/hooks/use-workspace-edit.ts","../src/hooks/use-machine-chip.ts","../src/components/approval-surface.tsx","../src/components/chat-composer.tsx","../src/components/fleet-tile.tsx","../src/components/sandbox-terminal.tsx","../src/lib/xterm-theme.ts","../src/lib/xterm-renderer.ts","../src/components/file-browser.tsx","../src/lib/use-unicode-fonts.ts","../src/components/workbench-changes.tsx","../src/hooks/use-windowed-sections.ts","../src/components/diff-view.tsx","../src/components/pierre-file.tsx","../src/components/code-editor.tsx","../src/components/sandbox-files.tsx","../src/components/desktop-viewer.tsx","../src/components/workspace-dock.tsx","../src/components/sandbox-workspace.tsx","../src/hooks/use-codex-accounts.ts"],"sourcesContent":["import type { LatencyMode, ReasoningEffort, SessionEvent, SessionTurn } from \"@opengeni/sdk\";\nimport { useCallback, useEffect } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../session-context\";\nimport { usePolledValue, useSessionEventTrigger, type SessionEventFeedOptions } from \"./internal\";\n\n/** Refresh when a turn actually admits — not on composer picker / queue-only changes. */\nexport function isLastStartedTurnPolicyEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type === \"turn.started\";\n}\n\nexport type LastStartedTurnPolicy = {\n model: string;\n reasoningEffort: ReasoningEffort;\n latencyMode: LatencyMode;\n turnId: string;\n};\n\nexport type UseLastStartedTurnPolicyOptions = ClientOverride &\n SessionEventFeedOptions & {\n pollIntervalMs?: number | undefined;\n };\n\nexport type UseLastStartedTurnPolicyResult = {\n /** Null until some turn has durably emitted `turn.started`. */\n policy: LastStartedTurnPolicy | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nfunction policyFromTurn(turn: SessionTurn | null | undefined): LastStartedTurnPolicy | null {\n if (!turn) return null;\n return {\n model: turn.model,\n reasoningEffort: turn.reasoningEffort,\n latencyMode: turn.latencyMode ?? \"standard\",\n turnId: turn.id,\n };\n}\n\n/**\n * Model·effort·latency of the newest admitted turn (`turn.started`). This is\n * historical runtime truth for the session header — not the composer next-turn\n * picker and not the frozen session creation defaults.\n */\nexport function useLastStartedTurnPolicy(\n sessionId: string | null | undefined,\n options: UseLastStartedTurnPolicyOptions = {},\n): UseLastStartedTurnPolicyResult {\n const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } =\n useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n\n const load = useCallback(async (): Promise<LastStartedTurnPolicy | null> => {\n if (!sessionId) return null;\n const turns = await client.listTurns(workspaceId, sessionId, { latestStarted: true });\n return policyFromTurn(turns[0]);\n }, [client, workspaceId, sessionId]);\n\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled,\n });\n\n useEffect(() => {\n if (enabled && workspaceControlEvent) void refresh();\n }, [enabled, refresh, workspaceControlEvent]);\n\n useEffect(() => {\n if (!sessionId || !enabled) return;\n return registerSessionReconciler(sessionId, \"last-started-turn-policy\", refresh);\n }, [enabled, refresh, registerSessionReconciler, sessionId]);\n\n // Shared-feed only: the session header lives outside the route event log and\n // must not open a second SSE. Without `events`, rely on the initial fetch +\n // optional poll / workspace-control / reconciler.\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isLastStartedTurnPolicyEvent,\n () => void refresh(),\n {\n events: options.events,\n enabled: enabled && options.events !== undefined,\n },\n );\n\n return {\n policy: enabled ? data : null,\n loading,\n error,\n refresh,\n };\n}\n","import type { ScheduledTask } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseScheduledTasksOptions = ClientOverride & {\n limit?: number | undefined;\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseScheduledTasksResult = {\n tasks: ScheduledTask[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** List the workspace's scheduled tasks (drift checks, sentinels, reapers, ...). */\nexport function useScheduledTasks(options: UseScheduledTasksOptions = {}): UseScheduledTasksResult {\n const { client, workspaceId } = useOpenGeni(options);\n const limit = options.limit;\n const load = useCallback(\n async () => await client.listScheduledTasks(workspaceId, limit !== undefined ? { limit } : {}),\n [client, workspaceId, limit],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n tasks: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type { Session } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseWorkspaceSessionsOptions = ClientOverride & {\n limit?: number | undefined;\n parentSessionId?: string | null | undefined;\n cursor?: string | undefined;\n search?: string | undefined;\n /** Return only the complete personal pinned projection. */\n pinsOnly?: boolean | undefined;\n /** Refresh interval (ms) for fleet/manager views. Off by default. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceSessionsResult = {\n /**\n * All visible rows, with pins first. This preserves the pre-pinning hook\n * contract for consumers that only read `sessions`.\n */\n sessions: Session[];\n /** The complete personal pinned section, also present in `sessions`. */\n pinned: Session[];\n /** True when the server omitted older pins from its bounded pinned section. */\n pinnedTruncated: boolean;\n nextCursor: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** List the workspace's sessions — the data behind fleet and manager views. */\nexport function useWorkspaceSessions(\n options: UseWorkspaceSessionsOptions = {},\n): UseWorkspaceSessionsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const limit = options.limit;\n const parentSessionId = options.parentSessionId;\n const cursor = options.cursor;\n const search = options.search;\n const pinsOnly = options.pinsOnly;\n const enabled = options.enabled ?? true;\n const queryKey = [\n workspaceId,\n limit ?? \"\",\n parentSessionId === null ? \"null\" : (parentSessionId ?? \"\"),\n cursor ?? \"\",\n search ?? \"\",\n pinsOnly ? \"1\" : \"\",\n ].join(\"\\u0000\");\n const previousQueryKey = useRef(queryKey);\n const queryKeyTransition = previousQueryKey.current !== queryKey;\n useEffect(() => {\n previousQueryKey.current = queryKey;\n }, [queryKey]);\n const load = useCallback(\n async () => ({\n queryKey,\n page: await client.listSessionPage(workspaceId, {\n ...(limit !== undefined ? { limit } : {}),\n ...(parentSessionId !== undefined ? { parentSessionId } : {}),\n ...(cursor !== undefined ? { cursor } : {}),\n ...(search !== undefined ? { search } : {}),\n ...(pinsOnly ? { pinsOnly: true } : {}),\n }),\n }),\n [client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, queryKey],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled,\n });\n // usePolledValue drops stale async completions, while the explicit query key\n // also prevents the previous query's cached value from painting for the one\n // render before its loader-change effect clears state.\n const page = state.data?.queryKey === queryKey ? state.data.page : null;\n const pinned = page?.pinned ?? [];\n const ordinary = page?.sessions ?? [];\n return {\n // The old hook returned every visible session. Keep that public behavior\n // while exposing `pinned` separately for the compact section. The API page\n // itself continues to paginate only `ordinary` rows via nextCursor.\n sessions: [...pinned, ...ordinary],\n pinned,\n pinnedTruncated: page?.pinnedTruncated ?? false,\n nextCursor: page?.nextCursor ?? null,\n // `usePolledValue` clears the old data and starts the new request in an\n // effect. During that query-key transition render, its old loading flag\n // can still be false; expose loading immediately so consumers do not\n // announce a transient false zero-match result.\n loading:\n enabled &&\n (state.loading ||\n queryKeyTransition ||\n (state.data !== null && state.data.queryKey !== queryKey)),\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type {\n CreateVariableSetRequest,\n UpdateVariableSetRequest,\n VariableSet,\n VariableSetVariableMetadata,\n} from \"@opengeni/sdk\";\nimport { useCallback, useMemo } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseVariableSetsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseVariableSetsResult = {\n variableSets: VariableSet[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateVariableSetRequest) => Promise<VariableSet | null>;\n update: (variableSetId: string, request: UpdateVariableSetRequest) => Promise<VariableSet | null>;\n remove: (variableSetId: string) => Promise<boolean>;\n /** Set/rotate a variable. Values are write-only — reads expose metadata only. */\n setVariable: (\n variableSetId: string,\n name: string,\n value: string,\n ) => Promise<VariableSetVariableMetadata | null>;\n deleteVariable: (variableSetId: string, name: string) => Promise<boolean>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * Variable sets (named, encrypted variable sets attached to sessions\n * and scheduled tasks). Variable values are write-only end to end: this hook\n * never sees a value after it is sent.\n */\nexport function useVariableSets(options: UseVariableSetsOptions = {}): UseVariableSetsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listVariableSets(workspaceId),\n [client, workspaceId],\n );\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateVariableSetRequest): Promise<VariableSet | null> => {\n const result = await run(() => client.createVariableSet(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const update = useCallback(\n async (\n variableSetId: string,\n request: UpdateVariableSetRequest,\n ): Promise<VariableSet | null> => {\n const result = await run(() => client.updateVariableSet(workspaceId, variableSetId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (variableSetId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteVariableSet(workspaceId, variableSetId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const setVariable = useCallback(\n async (\n variableSetId: string,\n name: string,\n value: string,\n ): Promise<VariableSetVariableMetadata | null> => {\n const result = await run(() =>\n client.setVariableSetVariable(workspaceId, variableSetId, name, value),\n );\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const deleteVariable = useCallback(\n async (variableSetId: string, name: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteVariableSetVariable(workspaceId, variableSetId, name);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n return {\n variableSets: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n remove,\n setVariable,\n deleteVariable,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\n/** @deprecated use UseVariableSetsOptions */\nexport type UseEnvironmentsOptions = UseVariableSetsOptions;\n/** @deprecated use UseVariableSetsResult */\nexport type UseEnvironmentsResult = UseVariableSetsResult & { environments: VariableSet[] };\n/** @deprecated use useVariableSets */\nexport function useEnvironments(options: UseEnvironmentsOptions = {}): UseEnvironmentsResult {\n const legacyClient = useMemo(() => {\n if (!options.client) {\n return undefined;\n }\n return {\n ...options.client,\n listVariableSets: options.client.listEnvironments ?? options.client.listVariableSets,\n createVariableSet: options.client.createEnvironment ?? options.client.createVariableSet,\n updateVariableSet: options.client.updateEnvironment ?? options.client.updateVariableSet,\n deleteVariableSet: options.client.deleteEnvironment ?? options.client.deleteVariableSet,\n setVariableSetVariable:\n options.client.setEnvironmentVariable ?? options.client.setVariableSetVariable,\n deleteVariableSetVariable:\n options.client.deleteEnvironmentVariable ?? options.client.deleteVariableSetVariable,\n };\n }, [options.client]);\n const result = useVariableSets({ ...options, ...(legacyClient ? { client: legacyClient } : {}) });\n return { ...result, environments: result.variableSets };\n}\n","import type {\n CreateRigRequest,\n ProposeRigChangeRequest,\n Rig,\n RigChange,\n RigVersion,\n UpdateRigRequest,\n} from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseRigsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigsResult = {\n rigs: Rig[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateRigRequest) => Promise<Rig | null>;\n update: (rigId: string, request: UpdateRigRequest) => Promise<Rig | null>;\n remove: (rigId: string) => Promise<boolean>;\n listVersions: (rigId: string) => Promise<RigVersion[] | null>;\n activateVersion: (rigId: string, versionId: string) => Promise<RigVersion | null>;\n listChanges: (rigId: string) => Promise<RigChange[] | null>;\n proposeChange: (rigId: string, request: ProposeRigChangeRequest) => Promise<RigChange | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * Rigs (workspace-scoped, versioned sandbox machine definitions). The list\n * polls; version/change reads are on-demand (they are per-rig detail, not part\n * of the list surface).\n */\nexport function useRigs(options: UseRigsOptions = {}): UseRigsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(async () => await client.listRigs(workspaceId), [client, workspaceId]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.createRig(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const update = useCallback(\n async (rigId: string, request: UpdateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.updateRig(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (rigId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteRig(workspaceId, rigId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const listVersions = useCallback(\n async (rigId: string): Promise<RigVersion[] | null> => {\n return await run(() => client.listRigVersions(workspaceId, rigId));\n },\n [client, workspaceId, run],\n );\n\n const activateVersion = useCallback(\n async (rigId: string, versionId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const listChanges = useCallback(\n async (rigId: string): Promise<RigChange[] | null> => {\n return await run(() => client.listRigChanges(workspaceId, rigId));\n },\n [client, workspaceId, run],\n );\n\n const proposeChange = useCallback(\n async (rigId: string, request: ProposeRigChangeRequest): Promise<RigChange | null> => {\n const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n return {\n rigs: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n remove,\n listVersions,\n activateVersion,\n listChanges,\n proposeChange,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\nexport type UseRigOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigResult = {\n rig: Rig | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n update: (request: UpdateRigRequest) => Promise<Rig | null>;\n remove: () => Promise<boolean>;\n activateVersion: (versionId: string) => Promise<RigVersion | null>;\n proposeChange: (request: ProposeRigChangeRequest) => Promise<RigChange | null>;\n /** Re-run verification for a change (asynchronous — poll for the outcome). */\n verifyChange: (changeId: string) => Promise<RigChange | null>;\n /** Promote a verified definition_edit into a new active version (rigs:manage). */\n promoteChange: (changeId: string) => Promise<RigVersion | null>;\n /** Re-verify the active version's checks in a clean throwaway sandbox. */\n verify: () => Promise<{ ok: boolean; versionId: string } | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * A single rig (its active version + counts), polled so verification and\n * promotion state stay live. Owns the rig's write actions; the versions and\n * changes lists are separate polled reads (`useRigVersions`/`useRigChanges`).\n */\nexport function useRig(rigId: string, options: UseRigOptions = {}): UseRigResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.getRig(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const update = useCallback(\n async (request: UpdateRigRequest): Promise<Rig | null> => {\n const result = await run(() => client.updateRig(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const remove = useCallback(async (): Promise<boolean> => {\n const result = await run(async () => {\n await client.deleteRig(workspaceId, rigId);\n return true;\n });\n return result === true;\n }, [client, workspaceId, rigId, run]);\n\n const activateVersion = useCallback(\n async (versionId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const proposeChange = useCallback(\n async (request: ProposeRigChangeRequest): Promise<RigChange | null> => {\n const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const verifyChange = useCallback(\n async (changeId: string): Promise<RigChange | null> => {\n const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const promoteChange = useCallback(\n async (changeId: string): Promise<RigVersion | null> => {\n const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, rigId, run, refresh],\n );\n\n const verify = useCallback(async (): Promise<{ ok: boolean; versionId: string } | null> => {\n return await run(() => client.verifyRig(workspaceId, rigId));\n }, [client, workspaceId, rigId, run]);\n\n return {\n rig: data,\n loading,\n error,\n refresh,\n update,\n remove,\n activateVersion,\n proposeChange,\n verifyChange,\n promoteChange,\n verify,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n\nexport type UseRigVersionsOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigVersionsResult = {\n versions: RigVersion[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/** A rig's append-only version history, newest-first (polled). */\nexport function useRigVersions(\n rigId: string,\n options: UseRigVersionsOptions = {},\n): UseRigVersionsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listRigVersions(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n versions: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n\nexport type UseRigChangesOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseRigChangesResult = {\n changes: RigChange[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * A rig's change queue (polled). The default poll cadence lets a change move\n * through verifying → merged/rejected without a manual refresh.\n */\nexport function useRigChanges(\n rigId: string,\n options: UseRigChangesOptions = {},\n): UseRigChangesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(\n async () => await client.listRigChanges(workspaceId, rigId),\n [client, workspaceId, rigId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n changes: state.data ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type {\n CapabilityPack,\n EnablePackRequest,\n PackInstallation,\n RegisterCapabilityPackRequest,\n WorkspaceRegisteredPack,\n} from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nconst EMPTY_INSTALLATIONS: PackInstallation[] = [];\n\nexport type UsePacksOptions = ClientOverride & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UsePacksResult = {\n /** Built-in + registered packs available to the workspace. */\n packs: CapabilityPack[];\n /** Enable/disable state per pack. */\n installations: PackInstallation[];\n /** The installation for a pack id, if any. */\n installationFor: (packId: string) => PackInstallation | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n /** Register (or replace) a workspace-scoped pack manifest. */\n register: (manifest: RegisterCapabilityPackRequest) => Promise<WorkspaceRegisteredPack | null>;\n enable: (packId: string, request?: EnablePackRequest) => Promise<PackInstallation | null>;\n /** Unregister a workspace-scoped pack (built-ins cannot be removed). */\n remove: (packId: string) => Promise<boolean>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/** Capability packs: catalog + installations + register/enable/unregister. */\nexport function usePacks(options: UsePacksOptions = {}): UsePacksResult {\n const { client, workspaceId } = useOpenGeni(options);\n const load = useCallback(async () => await client.listPacks(workspaceId), [client, workspaceId]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const register = useCallback(\n async (manifest: RegisterCapabilityPackRequest): Promise<WorkspaceRegisteredPack | null> => {\n const result = await run(() => client.registerPack(workspaceId, manifest));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const enable = useCallback(\n async (packId: string, request: EnablePackRequest = {}): Promise<PackInstallation | null> => {\n const result = await run(() => client.enablePack(workspaceId, packId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, workspaceId, run, refresh],\n );\n\n const remove = useCallback(\n async (packId: string): Promise<boolean> => {\n const result = await run(async () => {\n await client.deletePack(workspaceId, packId);\n return true;\n });\n if (result) {\n await refresh();\n }\n return result === true;\n },\n [client, workspaceId, run, refresh],\n );\n\n const installations = data?.installations ?? EMPTY_INSTALLATIONS;\n const installationFor = useCallback(\n (packId: string): PackInstallation | null =>\n installations.find((installation) => installation.packId === packId) ?? null,\n [installations],\n );\n\n return {\n packs: data?.packs ?? [],\n installations,\n installationFor,\n loading,\n error,\n refresh,\n register,\n enable,\n remove,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n","import type { CreateWorkspaceRequest, UpdateWorkspaceRequest, Workspace } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { useMutationRunner, usePolledValue } from \"./internal\";\n\nexport type UseWorkspacesOptions = Pick<ClientOverride, \"client\"> & {\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspacesResult = {\n workspaces: Workspace[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n create: (request: CreateWorkspaceRequest) => Promise<Workspace | null>;\n update: (workspaceId: string, request: UpdateWorkspaceRequest) => Promise<Workspace | null>;\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * The caller's workspaces (workspace switchers, onboarding). Not scoped to\n * the provider's workspace, so it only needs the client.\n */\nexport function useWorkspaces(options: UseWorkspacesOptions = {}): UseWorkspacesResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => await client.listWorkspaces(), [client]);\n const { data, loading, error, refresh } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run, mutating, mutationError, clearMutationError } = useMutationRunner();\n\n const create = useCallback(\n async (request: CreateWorkspaceRequest): Promise<Workspace | null> => {\n const result = await run(() => client.createWorkspace(request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, run, refresh],\n );\n\n const update = useCallback(\n async (workspaceId: string, request: UpdateWorkspaceRequest): Promise<Workspace | null> => {\n const result = await run(() => client.updateWorkspace(workspaceId, request));\n if (result) {\n await refresh();\n }\n return result;\n },\n [client, run, refresh],\n );\n\n return {\n workspaces: data ?? [],\n loading,\n error,\n refresh,\n create,\n update,\n mutating,\n mutationError,\n clearMutationError,\n };\n}\n","import type { BillingBalance, UsageEvent } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseBillingUsageOptions = Pick<ClientOverride, \"client\"> & {\n /** Account to read. Defaults to the caller's default account server-side. */\n accountId?: string | undefined;\n /** Filter usage to one workspace. */\n workspaceId?: string | undefined;\n /** Refresh interval (ms) for live billing meters. Off by default. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseBillingUsageResult = {\n /** Prepaid credit balance (micro-USD), null until loaded. */\n balance: BillingBalance | null;\n /** Recent usage events (runs, tokens, cost, uploads, ...). */\n usage: UsageEvent[];\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * Account billing usage: credit balance + recent usage events — the data\n * behind per-call billing meters. Account-scoped, so it only needs the\n * client; pass `workspaceId` to narrow usage to one workspace.\n */\nexport function useBillingUsage(options: UseBillingUsageOptions = {}): UseBillingUsageResult {\n const client = useOpenGeniClient(options);\n const accountId = options.accountId;\n const workspaceId = options.workspaceId;\n const load = useCallback(\n async () =>\n await client.getBillingUsage({\n ...(accountId !== undefined ? { accountId } : {}),\n ...(workspaceId !== undefined ? { workspaceId } : {}),\n }),\n [client, accountId, workspaceId],\n );\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n balance: state.data?.balance ?? null,\n usage: state.data?.usage ?? [],\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import type { WorkspaceModelCatalogModel } from \"@opengeni/sdk\";\nimport { useCallback } from \"react\";\nimport { projectPickerRows, sortPickerRows, type PickerModelRow } from \"../model-policy\";\nimport { useOpenGeniClient, type ClientOverride } from \"../provider\";\nimport { usePolledValue } from \"./internal\";\n\nexport type UseAvailableModelsOptions = Pick<ClientOverride, \"client\"> & {\n /** Refresh interval (ms). Off by default — the host model list rarely moves. */\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseAvailableModelsResult = {\n models: import(\"@opengeni/sdk\").ClientModel[];\n defaultModel: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nexport type UseWorkspaceModelCatalogOptions = Pick<ClientOverride, \"client\"> & {\n workspaceId: string | null;\n pollIntervalMs?: number | undefined;\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceModelCatalogResult = {\n models: WorkspaceModelCatalogModel[];\n rows: PickerModelRow[];\n defaultModel: string | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * The host-exposed model list for a <ModelPicker>: fetches the deployment's\n * public client config (`GET /v1/config/client`) and surfaces the richer\n * provider-grouped `models` plus the `defaultModel` the picker should preselect.\n * Deployment-scoped, so it only needs the client (no workspace).\n */\nexport function useAvailableModels(\n options: UseAvailableModelsOptions = {},\n): UseAvailableModelsResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => await client.getClientConfig(), [client]);\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n return {\n models: state.data?.models ?? [],\n defaultModel: state.data?.defaultModel ?? null,\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n\n/** Workspace-scoped catalog with selectability and billing-class picker rows. */\nexport function useWorkspaceModelCatalog(\n options: UseWorkspaceModelCatalogOptions,\n): UseWorkspaceModelCatalogResult {\n const client = useOpenGeniClient(options);\n const load = useCallback(async () => {\n if (!options.workspaceId) {\n return { models: [], defaultModel: null };\n }\n const [catalog, config] = await Promise.all([\n client.getWorkspaceModelCatalog(options.workspaceId),\n client.getClientConfig(),\n ]);\n return {\n models: catalog.models,\n defaultModel: config.defaultModel,\n };\n }, [client, options.workspaceId]);\n const state = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled !== false && Boolean(options.workspaceId),\n });\n const rows = sortPickerRows(projectPickerRows(state.data?.models ?? []));\n return {\n models: state.data?.models ?? [],\n rows,\n defaultModel: state.data?.defaultModel ?? null,\n loading: state.loading,\n error: state.error,\n refresh: state.refresh,\n };\n}\n","import {\n OpenGeniApiError,\n applyUrlRotation,\n type SessionCapabilities,\n type SessionEvent,\n type StreamUrlRotatedPayload,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n// \"on-demand\" is the boxless-benign resting state: the lease is cold and NOTHING\n// asked to warm a box (no viewer/consent action, no files-tab warm). Under lazy\n// provisioning a chat-only turn never creates a box, so this is the correct calm\n// terminal state — NOT an error. It is distinct from \"cold\", which now means \"a\n// warm-up is genuinely in flight\" (a viewer attach was requested) and therefore\n// still polls with a patience deadline. A box appears on demand when the agent\n// first runs a sandbox tool; the workbench observes `sandbox.provision` and\n// renegotiates to pick up the freshly-warm box.\nexport type SessionCapabilitiesState =\n | \"idle\"\n | \"negotiating\"\n | \"ready\"\n | \"cold\"\n | \"on-demand\"\n | \"error\";\n\nexport type UseSessionCapabilitiesOptions = ClientOverride & {\n /**\n * Live event log to fold `stream.url.rotated` from (usually\n * `useSessionEvents().events`). When present the desktop socket stays fresh on\n * a box rollover without a round-trip; stale-epoch rotations are dropped.\n */\n events?: SessionEvent[] | undefined;\n /**\n * Whether to acquire a viewer holder for the desktop pixel plane. Requires the\n * un-redacted acknowledgment to have been recorded (else the attach 409s and\n * the hook surfaces the consent requirement). Default false: read-only\n * negotiation (no holder, no warm) — terminal/files/git work without it.\n */\n attachDesktop?: boolean | undefined;\n /**\n * Whether to acquire a viewer holder to warm the box for the REAL interactive\n * terminal (the ttyd pty-ws plane). Symmetric with `attachDesktop` and shares\n * the SAME viewer attach (one warm box serves both planes), but needs NO\n * un-redacted acknowledgment — a shell is interactive by nature, and the gate\n * is the scoped tunnel URL + stream token. Default false: the terminal stays on\n * the read-only Channel-A firehose until the user opens/focuses it. The attach\n * folds the minted `pty-ws` url+token into the `Terminal` cell.\n */\n attachTerminal?: boolean | undefined;\n /**\n * Whether to acquire a viewer holder to warm the box for a FILE WRITE — the\n * wake-on-edit INTENT (a first keystroke in the editor), NOT passive browsing.\n * Shares the SAME viewer attach as the desktop/terminal (one warm box, one\n * holder) and — like the terminal — needs NO un-redacted acknowledgment:\n * reading/writing files is the ordinary Channel-A control plane, not the pixel\n * plane. It folds NO live URL (files ride the stateless HTTP plane) — it only\n * refcounts liveness. Unlike desktop/terminal it warms even a COLD box: an edit\n * legitimately cold-creates (that IS the wake), so the write lands ~100ms warm\n * instead of paying the ~5s cold resume. Default false. IMPORTANT: merely\n * opening/reading the Files tab must NOT set this — browsing capture-served\n * trees/diffs needs no box, and warming one on a cold glance burns box-hours to\n * serve reads the capture already answers for free.\n */\n attachFiles?: boolean | undefined;\n /** Hold off negotiating (e.g. the workbench panel is collapsed). Default true. */\n enabled?: boolean | undefined;\n /** Poll cadence (ms) while the lease is cold/warming. Default 1500. */\n warmingPollMs?: number | undefined;\n /**\n * Give up waiting for `warm` after this long while polling (ms) and surface a\n * stalled error with a manual `renegotiate`. Default 30000. This is a UI\n * patience deadline, independent from the worker's sandbox warming timeout.\n * 0 disables the deadline.\n */\n warmingDeadlineMs?: number | undefined;\n};\n\nexport type UseSessionCapabilitiesResult = {\n /** The negotiated capability doc — the single source of UI truth. */\n capabilities: SessionCapabilities | null;\n state: SessionCapabilitiesState;\n error: Error | null;\n /**\n * 409 from the desktop attach: the un-redacted (or shared) plane needs explicit\n * acknowledgment before a viewer holder is granted. Drives the consent prompt.\n */\n acknowledgmentRequired: \"unredacted\" | \"shared\" | null;\n /** 429 from the desktop attach: the per-session viewer cap is reached. */\n viewerCapReached: boolean;\n /** The viewer holder id minted on a desktop attach (for detach/heartbeat). */\n viewerId: string | null;\n /** Force a re-negotiation (after acknowledging, a resolution change, etc.). */\n renegotiate: () => void;\n};\n\n/**\n * Whether a desktop attach (POST /viewers) is worth attempting for this cell.\n * The desktop is FEASIBLE — and thus worth warming — when it already has a live\n * transport, OR when the only thing missing is a warm box (a cold/un-provisioned\n * lease). The handshake never warms a box, so a feasible-but-cold cell reports\n * transport:null with a transient reason; the viewer attach is exactly what warms\n * it. A genuinely-unsupported desktop (backend/os/policy/headless) is never\n * attachable — attaching would 409/403/no-op without ever producing a stream.\n */\nfunction desktopAttachable(cell: SessionCapabilities[\"DesktopStream\"]): boolean {\n if (cell.transport !== null) return true;\n // transport === null: attach only when the reason is a transient cold state.\n return cell.reason === \"lease_cold\" || cell.reason === \"not_provisioned\" || cell.reason === null;\n}\n\n/**\n * Whether warming the box for the interactive terminal (pty-ws) is worth it.\n * The Terminal cell is ALWAYS feasible when the backend advertises a transport at\n * all (`sse-events` on a cold box, `pty-ws` once warm) — only a genuinely\n * terminal-less backend reports transport:null with a hard reason. The attach is\n * what flips `sse-events` → `pty-ws` by warming the box and minting the ttyd\n * tunnel URL, so we attach whenever the terminal is not hard-unavailable.\n */\nfunction terminalAttachable(cell: SessionCapabilities[\"Terminal\"]): boolean {\n if (cell.transport === null) {\n // No terminal at all unless the only blocker is a transient cold state.\n return (\n cell.reason === \"lease_cold\" || cell.reason === \"not_provisioned\" || cell.reason === null\n );\n }\n // Already pty-ws (warm) is fine to (re)attach; sse-events is the cold state the\n // attach upgrades. Either way it's attachable.\n return true;\n}\n\n/** Read `stream.url.rotated` payloads off the live event log, newest last. */\nfunction rotationsFrom(events: SessionEvent[]): StreamUrlRotatedPayload[] {\n const out: StreamUrlRotatedPayload[] = [];\n for (const event of events) {\n if (event.type === \"stream.url.rotated\" && event.payload && typeof event.payload === \"object\") {\n out.push(event.payload as StreamUrlRotatedPayload);\n }\n }\n return out;\n}\n\n/**\n * The capability-negotiation hook. Discovers what THIS session+backend+OS\n * supports (FileSystem/Terminal/Git always-ish; DesktopStream/Recording\n * sometimes), drives capability-gated rendering, and — when `attachDesktop` —\n * holds a viewer lease + heartbeats it so the box stays warm while watched.\n *\n * Degradation is a value, never a crash: an unsupported surface comes back\n * `available:false`/`transport:null` + a `reason`; the components render the\n * reason-aware empty state. 409 (consent) and 429 (viewer cap) are surfaced as\n * typed signals, not thrown.\n */\nexport function useSessionCapabilities(\n sessionId: string | null | undefined,\n options: UseSessionCapabilitiesOptions = {},\n): UseSessionCapabilitiesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const attachDesktop = options.attachDesktop ?? false;\n const attachTerminal = options.attachTerminal ?? false;\n const attachFiles = options.attachFiles ?? false;\n const warmingPollMs = options.warmingPollMs ?? 1500;\n const warmingDeadlineMs = options.warmingDeadlineMs ?? 30_000;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${enabled}`;\n\n const [capabilities, setCapabilities] = useState<SessionCapabilities | null>(null);\n const [state, setState] = useState<SessionCapabilitiesState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [acknowledgmentRequired, setAcknowledgmentRequired] = useState<\n \"unredacted\" | \"shared\" | null\n >(null);\n const [viewerCapReached, setViewerCapReached] = useState(false);\n const [viewerId, setViewerId] = useState<string | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Bumped to force a fresh negotiation cycle.\n const [nonce, setNonce] = useState(0);\n\n // The epoch the client has settled on — used to fence stale rotations folded\n // from the event log, and echoed on heartbeats.\n const epochRef = useRef(0);\n const viewerIdRef = useRef<string | null>(null);\n const previousIdentityRef = useRef(identityKey);\n\n const renegotiate = useCallback(() => {\n setNonce((n) => n + 1);\n }, []);\n\n // ── Negotiation + viewer lifecycle ──────────────────────────────────────────\n useEffect(() => {\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged) {\n setStateIdentity(identityKey);\n setCapabilities(null);\n setState(\"idle\");\n setError(null);\n setAcknowledgmentRequired(null);\n setViewerCapReached(false);\n setViewerId(null);\n epochRef.current = 0;\n viewerIdRef.current = null;\n }\n if (!enabled || !sessionId) {\n setState(\"idle\");\n setCapabilities(null);\n return;\n }\n let cancelled = false;\n let pollTimer: ReturnType<typeof setTimeout> | null = null;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n let localViewerId: string | null = null;\n const requestAbort = new AbortController();\n const startedAt = Date.now();\n\n const clearTimers = () => {\n if (pollTimer !== null) clearTimeout(pollTimer);\n if (heartbeatTimer !== null) clearInterval(heartbeatTimer);\n pollTimer = null;\n heartbeatTimer = null;\n };\n\n const settle = (caps: SessionCapabilities) => {\n if (cancelled) return;\n epochRef.current = caps.leaseEpoch;\n setCapabilities(caps);\n setError(null);\n };\n\n const startHeartbeat = (caps: SessionCapabilities) => {\n if (!localViewerId || heartbeatTimer !== null) return;\n const intervalMs =\n caps.viewerHeartbeatIntervalMs > 0 ? caps.viewerHeartbeatIntervalMs : 30_000;\n heartbeatTimer = setInterval(() => {\n if (cancelled || !localViewerId) return;\n void client\n .heartbeatViewer(workspaceId, sessionId, localViewerId, { leaseEpoch: epochRef.current })\n .then((res) => {\n // alive:false ⇒ the holder was reaped or the epoch moved under us;\n // re-negotiate to re-acquire against the new owner.\n if (!res.alive && !cancelled) {\n renegotiate();\n }\n })\n .catch((cause) => {\n if (cancelled) return;\n if (\n cause instanceof OpenGeniApiError &&\n (cause.status === 409 || cause.status === 410)\n ) {\n renegotiate();\n }\n });\n }, intervalMs);\n };\n\n const pollUntilWarm = () => {\n pollTimer = setTimeout(() => {\n if (cancelled) return;\n void client\n .getStreamCapabilities(workspaceId, sessionId, { signal: requestAbort.signal })\n .then((caps) => {\n if (cancelled) return;\n settle(caps);\n if (caps.liveness === \"warm\" || caps.liveness === \"draining\") {\n setState(\"ready\");\n return;\n }\n if (warmingDeadlineMs > 0 && Date.now() - startedAt > warmingDeadlineMs) {\n setState(\"error\");\n setError(new Error(\"sandbox did not warm in time — retry to re-negotiate\"));\n return;\n }\n pollUntilWarm();\n })\n .catch((cause) => {\n if (!cancelled) {\n setState(\"error\");\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n });\n }, warmingPollMs);\n };\n\n void (async () => {\n setState(\"negotiating\");\n setAcknowledgmentRequired(null);\n setViewerCapReached(false);\n try {\n const caps = await client.getStreamCapabilities(workspaceId, sessionId, {\n signal: requestAbort.signal,\n });\n if (cancelled) return;\n settle(caps);\n\n // Optional viewer attach: acquire a viewer holder (warms a cold box). ONE\n // attach serves BOTH live planes — the desktop pixel stream AND the\n // interactive terminal (ttyd pty-ws) ride the same warm box and the same\n // holder; the response folds the minted address for each requested plane.\n // The consent gate (409) and the viewer cap (429) surface as typed signals\n // rather than throwing — the tabs degrade gracefully.\n //\n // KEY: the handshake (getStreamCapabilities) NEVER spins up a cold box, so\n // on a cold/warming lease the desktop cell comes back transport:null and\n // the terminal cell comes back `sse-events` (read-only firehose) with a\n // transient reason (`lease_cold`/`not_provisioned`). Gating the attach on\n // `transport` therefore dead-ends both surfaces (forever \"Starting…\"): they\n // never warm because they never attach, and never attach because they\n // aren't warm. We attach whenever a REQUESTED plane is FEASIBLE — transport\n // already live OR cold-but-feasible — and let POST /viewers warm the box and\n // mint the URLs. Only a genuinely-unsupported reason suppresses the attach.\n const wantDesktopAttach = attachDesktop && desktopAttachable(caps.DesktopStream);\n const wantTerminalAttach = attachTerminal && terminalAttachable(caps.Terminal);\n // The Files attach warms the box for a file WRITE (wake-on-edit intent). It\n // folds no live URL (files are stateless HTTP) — the attach is purely a\n // liveness refcount. Unlike the previous \"keep the box warm while the Files\n // tab is on screen\" wiring, this fires on genuine EDIT intent only, so it\n // legitimately cold-CREATES: an edit is the wake, and the caller (the dock)\n // never sets it for passive browsing. Gate solely on the FileSystem cap\n // being advertised; liveness (cold included) does not suppress it — POST\n // /viewers warms a cold box exactly as it does for a desktop/terminal engage.\n const wantFilesAttach = attachFiles && caps.FileSystem.available;\n if (wantDesktopAttach || wantTerminalAttach || wantFilesAttach) {\n try {\n // Declare WHICH plane we're attaching for. `desktop:true` opts into the\n // un-redacted pixel plane (which carries the consent gate); a\n // terminal-only attach (`desktop:false`) warms the box + mints the\n // pty-ws terminal cell WITHOUT tripping the desktop consent 409.\n const holder = await client.attachViewer(workspaceId, sessionId, {\n desktop: wantDesktopAttach,\n });\n if (cancelled) {\n // The attach crossed an identity/unmount boundary after the server\n // had already minted a holder. It is too late to publish the result,\n // but abandoning it would keep the old sandbox warm until the\n // reaper runs. Release that exact old-session holder immediately.\n void client.detachViewer(workspaceId, sessionId, holder.viewerId).catch(() => {});\n return;\n }\n localViewerId = holder.viewerId;\n viewerIdRef.current = holder.viewerId;\n setViewerId(holder.viewerId);\n epochRef.current = holder.leaseEpoch;\n // Fold the freshly-minted live address(es) into the doc the components\n // read. Desktop fields fold only when a desktop attach was wanted;\n // terminal fields fold the minted ttyd pty-ws url+token when present.\n setCapabilities((prev) =>\n prev\n ? {\n ...prev,\n liveness: holder.liveness,\n leaseEpoch: holder.leaseEpoch,\n viewerHeartbeatIntervalMs: holder.viewerHeartbeatIntervalMs,\n DesktopStream: wantDesktopAttach\n ? {\n ...prev.DesktopStream,\n transport: holder.transport ?? prev.DesktopStream.transport,\n client: holder.client ?? prev.DesktopStream.client,\n url: holder.dataPlaneUrl ?? prev.DesktopStream.url,\n token: holder.streamToken ?? prev.DesktopStream.token,\n expiresAt: holder.streamExpiresAt ?? prev.DesktopStream.expiresAt,\n resolution: holder.resolution ?? prev.DesktopStream.resolution,\n }\n : prev.DesktopStream,\n Terminal:\n holder.terminalUrl && holder.terminalTransport\n ? {\n ...prev.Terminal,\n transport: holder.terminalTransport,\n url: holder.terminalUrl,\n token: holder.terminalToken ?? prev.Terminal.token,\n // A live pty-ws means the box is pty-capable for real.\n ptyCapable: true,\n reason: null,\n }\n : prev.Terminal,\n }\n : prev,\n );\n } catch (cause) {\n if (cancelled) return;\n if (cause instanceof OpenGeniApiError) {\n if (cause.status === 409) {\n // The un-redacted/shared consent gate is a DESKTOP requirement. A\n // terminal-only warm attach needs no consent, so a 409 there is not\n // a consent prompt — the terminal just stays on the read-only\n // firehose. Only raise the consent requirement when the desktop was\n // the (or a) reason we attached.\n if (wantDesktopAttach) {\n setAcknowledgmentRequired(\n cause.message.includes(\"shared_acknowledgment\") ? \"shared\" : \"unredacted\",\n );\n }\n } else if (cause.status === 429) {\n setViewerCapReached(true);\n } else if (cause.status === 403) {\n setState(\"error\");\n setError(cause);\n return;\n }\n // 409/429 are recoverable: structured surfaces still negotiated;\n // keep going to set ready/cold below.\n } else {\n throw cause;\n }\n }\n }\n\n // Whether ANYTHING asked to warm a box this negotiation. Only then is a\n // cold lease \"warming in flight\" — worth polling, and worth surfacing a\n // stall as an error. With no warm-up requested a cold lease is the benign\n // boxless resting state (below), never an error.\n const warmUpRequested = wantDesktopAttach || wantTerminalAttach || wantFilesAttach;\n if (caps.liveness === \"warm\" || caps.liveness === \"draining\" || localViewerId) {\n setState(\"ready\");\n startHeartbeat(caps);\n } else if (warmUpRequested) {\n // A warm-up is genuinely in flight (a viewer attach was requested) but the\n // box isn't warm yet — poll until it warms, with the patience deadline so a\n // real stall surfaces as an error.\n setState(\"cold\");\n pollUntilWarm();\n } else {\n // Boxless and nobody asked for a box: rest in the benign on-demand state.\n // NO poll, NO deadline, NO false \"sandbox offline\". The box is created on\n // demand when the agent first runs a sandbox tool; the workbench watches\n // for `sandbox.provision` and renegotiates to pick the warm box back up.\n setState(\"on-demand\");\n }\n } catch (cause) {\n if (cancelled) return;\n if (cause instanceof OpenGeniApiError && cause.status === 403) {\n setState(\"error\");\n setError(new Error(\"not permitted to view this session's sandbox\"));\n return;\n }\n setState(\"error\");\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n })();\n\n return () => {\n cancelled = true;\n requestAbort.abort();\n clearTimers();\n // Fire-and-forget detach (idempotent delete-my-row). Capture the id so a\n // re-render/unmount race still releases the right holder.\n const releaseId = localViewerId ?? viewerIdRef.current;\n if (releaseId) {\n void client.detachViewer(workspaceId, sessionId, releaseId).catch(() => {});\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n client,\n workspaceId,\n sessionId,\n enabled,\n attachDesktop,\n attachTerminal,\n attachFiles,\n warmingPollMs,\n warmingDeadlineMs,\n nonce,\n identityKey,\n ]);\n\n // ── Fold stream.url.rotated from the live event log (no round trip) ──────────\n const events = options.events;\n useEffect(() => {\n if (!events || events.length === 0) return;\n setCapabilities((prev) => {\n if (!prev || !prev.DesktopStream.url) return prev;\n let next = prev.DesktopStream;\n let changed = false;\n for (const rotation of rotationsFrom(events)) {\n // Only this viewer's rotations (others' are filtered by viewerId when set).\n if (rotation.viewerId && viewerIdRef.current && rotation.viewerId !== viewerIdRef.current) {\n continue;\n }\n const applied = applyUrlRotation(next, rotation, epochRef.current);\n if (applied) {\n next = { ...next, url: applied.url, token: applied.token, expiresAt: applied.expiresAt };\n epochRef.current = Math.max(epochRef.current, rotation.leaseEpoch);\n changed = true;\n }\n }\n return changed ? { ...prev, DesktopStream: next, leaseEpoch: epochRef.current } : prev;\n });\n }, [events]);\n\n const identityMatches = enabled && stateIdentity === identityKey;\n return {\n capabilities: identityMatches ? capabilities : null,\n state: identityMatches ? state : \"idle\",\n error: identityMatches ? error : null,\n acknowledgmentRequired: identityMatches ? acknowledgmentRequired : null,\n viewerCapReached: identityMatches && viewerCapReached,\n viewerId: identityMatches ? viewerId : null,\n renegotiate,\n };\n}\n","import {\n desktopSocketUrl,\n nextDesktopState,\n type DesktopConnectionState,\n type DesktopRfbFactory,\n type DesktopRfbLike,\n type DesktopStreamCapability,\n} from \"@opengeni/sdk\";\nimport { type RefObject, useEffect, useRef, useState } from \"react\";\nimport { type DesktopWebSocketFactory, useRelayFrameStream } from \"./use-relay-frame-stream\";\n\nexport type UseDesktopStreamOptions = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /** The mount target. RFB (or the frame canvas) attaches here on connect. */\n containerRef: RefObject<HTMLDivElement | null>;\n /** Read-only by default (v1 ruling H). interactive only when cap.mode allows. */\n interactive?: boolean | undefined;\n scaleViewport?: boolean | undefined;\n /** Custom RFB factory (tests / a WebRTC swap). Defaults to a lazy @novnc/novnc. */\n rfbFactory?: DesktopRfbFactory | undefined;\n /** Custom socket factory for the `relay-frames` transport (tests). Defaults to\n * `new WebSocket(url)`. Mirrors `rfbFactory` for the frame renderer. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n};\n\nexport type UseDesktopStreamResult = {\n state: DesktopConnectionState;\n error: Error | null;\n /** Manual reconnect (e.g. after a securityfailure once a fresh URL arrives). */\n reconnect: () => void;\n};\n\n/** Lazy-load @novnc/novnc's RFB as the default factory. Imported inside the\n * connect effect so SSR / non-desktop bundles never pull the DOM-only lib.\n * @novnc/novnc ships no types and a single `export default class RFB`. The\n * specifier is STATIC (`import(\"@novnc/novnc\")`) so Vite can pre-bundle and\n * resolve it — a runtime-string indirection with `@vite-ignore` (the previous\n * approach) hands the browser a bare specifier and throws\n * \"Failed to resolve module specifier '@novnc/novnc'\". The dynamic form keeps\n * it out of the SSR / non-desktop critical path while staying resolvable. */\nasync function defaultRfbFactory(): Promise<DesktopRfbFactory> {\n const mod = (await import(\"@novnc/novnc\")) as unknown as {\n default: new (\n t: HTMLElement,\n u: string,\n o: { credentials?: { password?: string | undefined } | undefined },\n ) => DesktopRfbLike;\n };\n const RFB = mod.default;\n return (target, url, opts) => new RFB(target, url, opts);\n}\n\n/**\n * Drive the noVNC RFB lifecycle from a `DesktopStreamCapability`, using the\n * SDK's `desktop.ts` reducer + `desktopSocketUrl`. SSR-safe: the RFB import and\n * the DOM attach happen inside `useEffect`, so a server render is a no-op and\n * the component shows its placeholder until hydration.\n *\n * Read-only is enforced at three layers: `capability.mode` (server) →\n * `interactive` prop → `RFB.viewOnly`. v1 always resolves to read-only. On a\n * capability `url` change (a rotation), the old RFB disconnects and a fresh one\n * connects to the new URL — a brief \"desktop blink\", acceptable on rollover.\n *\n * Transport dispatch: a Modal box negotiates `transport: \"vnc-ws\"` and drives the\n * noVNC RFB below. A SELF-HOSTED machine negotiates `transport: \"relay-frames\"`\n * (PNG-per-frame over the relay, view-only) — that path is delegated to\n * `useRelayFrameStream`, which paints a `<canvas>`. Both hooks are ALWAYS called\n * (rules of hooks); each is dormant for the other's transport, and we return the\n * result of whichever owns the surface. The public interface is identical either\n * way, so `DesktopViewer` is transport-agnostic.\n */\nexport function useDesktopStream(options: UseDesktopStreamOptions): UseDesktopStreamResult {\n const { capability, containerRef, interactive, scaleViewport, rfbFactory, webSocketFactory } =\n options;\n\n // The self-hosted PNG-frame path. Always invoked (rules of hooks); dormant\n // unless `transport === \"relay-frames\"`, in which case it owns the surface.\n const frameStream = useRelayFrameStream({\n capability,\n containerRef,\n ...(webSocketFactory ? { webSocketFactory } : {}),\n });\n const [state, setState] = useState<DesktopConnectionState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [nonce, setNonce] = useState(0);\n const stateRef = useRef<DesktopConnectionState>(\"idle\");\n const setBoth = (next: DesktopConnectionState) => {\n stateRef.current = next;\n setState(next);\n };\n\n const reconnect = () => setNonce((n) => n + 1);\n\n const url = capability?.url ?? null;\n // The credential is keyed into the connect effect so a token rotation (new\n // password) reconnects, but NOT folded into the whole-`capability` identity —\n // a benign re-negotiation that re-mints the same cell must not churn the socket.\n const token = capability?.token ?? null;\n const transport = capability?.transport ?? null;\n const mode = capability?.mode ?? \"read-only\";\n\n // The live RFB handle. Holding it in a ref lets us flip view-only (take\n // control / return control) and re-scale on the OPEN connection instead of\n // tearing the socket down — the old behaviour reconnected on every\n // `interactive` change, which read as a constant \"refresh\" and (combined with\n // the canvas churn) bounced control back to watch.\n const rfbRef = useRef<DesktopRfbLike | null>(null);\n // Latest input/scale/factory the connect effect reads via refs so they are NOT\n // connect-effect dependencies: changing them must update the live RFB in place,\n // never reconnect it.\n const interactiveRef = useRef(interactive);\n const scaleViewportRef = useRef(scaleViewport);\n const modeRef = useRef(mode);\n const rfbFactoryRef = useRef(rfbFactory);\n interactiveRef.current = interactive;\n scaleViewportRef.current = scaleViewport;\n modeRef.current = mode;\n rfbFactoryRef.current = rfbFactory;\n\n // read-only is forced when the server says so OR the caller didn't opt in.\n const viewOnlyFor = (m: string, want: boolean | undefined) => m === \"read-only\" || !want;\n\n useEffect(() => {\n // SSR / no DOM / no usable transport: stay idle and show the placeholder.\n if (typeof window === \"undefined\") return;\n if (transport !== \"vnc-ws\" || !url) {\n setBoth(\"idle\");\n return;\n }\n const container = containerRef.current;\n if (!container) {\n // The mount target isn't in the DOM yet (e.g. the tab is still hidden).\n // Stay idle (not a stale negotiating/connecting) so a re-attach nudge —\n // fired by the viewer on becoming visible / on mount — can re-run this\n // effect once the container exists. Without this the surface can stick\n // forever on the idle scrim and never open a socket.\n setBoth(\"idle\");\n return;\n }\n\n let rfb: DesktopRfbLike | null = null;\n let disposed = false;\n setError(null);\n setBoth(\"negotiating\");\n\n const onConnect = () => {\n if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: \"connected\" }));\n };\n const onDisconnect = () => {\n if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: \"disconnected\" }));\n };\n const onSecurityFailure = () => {\n if (disposed) return;\n setError(new Error(\"desktop authentication failed (token expired or revoked)\"));\n setBoth(nextDesktopState(stateRef.current, { type: \"fail\" }));\n };\n\n void (async () => {\n try {\n const factory = rfbFactoryRef.current ?? (await defaultRfbFactory());\n if (disposed) return;\n const socketUrl = desktopSocketUrl({ url });\n setBoth(nextDesktopState(stateRef.current, { type: \"negotiated\" }));\n rfb = factory(container, socketUrl, {\n credentials: token ? { password: token } : undefined,\n });\n rfb.viewOnly = viewOnlyFor(modeRef.current, interactiveRef.current);\n // Fit-to-panel: SCALE the 1280x800 framebuffer down to the container\n // (aspect-preserved) and never 1:1-clip. `clipViewport=false` is pinned\n // explicitly — noVNC forces it off while scaling, but a fresh RFB on a\n // url rotation could otherwise start from a stale clip and read as\n // \"zoomed in\". Order matters: clip off, then scale on.\n rfb.clipViewport = false;\n rfb.scaleViewport = scaleViewportRef.current ?? true;\n rfb.addEventListener(\"connect\", onConnect);\n rfb.addEventListener(\"disconnect\", onDisconnect);\n rfb.addEventListener(\"securityfailure\", onSecurityFailure);\n rfbRef.current = rfb;\n } catch (cause) {\n if (!disposed) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n }\n }\n })();\n\n return () => {\n disposed = true;\n if (rfbRef.current === rfb) rfbRef.current = null;\n if (rfb) {\n rfb.removeEventListener?.(\"connect\", onConnect);\n rfb.removeEventListener?.(\"disconnect\", onDisconnect);\n rfb.removeEventListener?.(\"securityfailure\", onSecurityFailure);\n try {\n rfb.disconnect();\n } catch {\n // ignore teardown errors\n }\n }\n };\n // ONLY a real transport change reconnects: a fresh url (rotation), a new\n // credential, the transport flipping, or an explicit manual reconnect\n // (`nonce`). `interactive`/`scaleViewport`/`mode`/`rfbFactory` are read via\n // refs and applied live below — they must never re-open the socket.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, token, transport, nonce]);\n\n // Apply take-control / return-control to the OPEN connection in place. noVNC\n // honours `viewOnly` live, so flipping it neither blinks the surface nor drops\n // the framebuffer — the cure for the take-control \"refresh + auto-release\" loop.\n useEffect(() => {\n const rfb = rfbRef.current;\n if (rfb) rfb.viewOnly = viewOnlyFor(mode, interactive);\n // viewOnlyFor is pure; re-run only when the inputs change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [interactive, mode, state]);\n\n // Re-scale the live RFB in place (no reconnect) when the scale preference flips\n // OR the connection state advances (e.g. the first framebuffer paints after a\n // take-control flip / a re-attach). Re-asserting clip-off + scale-on here is the\n // belt-and-suspenders against a \"zoomed\" surface: it re-fits the canvas to the\n // panel on every meaningful transition, so a transient mis-measure at connect\n // time can never stick.\n useEffect(() => {\n const rfb = rfbRef.current;\n if (rfb) {\n rfb.clipViewport = false;\n rfb.scaleViewport = scaleViewport ?? true;\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [scaleViewport, state]);\n\n // Delegate to whichever transport owns the surface. For `relay-frames` the\n // noVNC effect above is dormant (it bailed to idle) and the frame renderer\n // drives; for `vnc-ws` (and everything else) the noVNC path drives while the\n // frame renderer stays idle. Same public shape either way.\n if (transport === \"relay-frames\") return frameStream;\n return { state, error, reconnect };\n}\n","// Minimal HAND-MIRROR of the relay stream wire messages the desktop frame client\n// needs (`StreamOpen` encode; `StreamOpenAck` + `StreamFrame` decode). It exists\n// because the publish-closure guard (scripts/publish-closure-guard.ts) forbids\n// `@opengeni/react` from depending on `@opengeni/agent-proto` — the published SDK\n// closure may only reach `@opengeni/sdk` among `@opengeni/*` packages. So, exactly\n// as the guard prescribes (\"hand-mirror it\"), we re-encode just these three\n// messages against the third-party `@bufbuild/protobuf/wire` runtime (the same\n// runtime `@opengeni/agent-proto` generates against), byte-compatible with the\n// generated code. Field numbers copied verbatim from\n// `packages/agent-proto/src/gen/opengeni_agent.ts`:\n// StreamChannel: channelId=1, workspaceId=2, agentId=3, kind=4(int32), port=5(uint32)\n// StreamOpen: channel=1(msg), token=2, role=3(int32), resumeFromSeq=4(uint64, omitted when \"0\")\n// StreamOpenAck: accepted=1(bool), error=2(AgentError msg), resumeFromSeq=3\n// AgentError: code=1, message=2, retryable=3, detail=4 (we read only message)\n// StreamFrame: channelId=1, seq=2, data=3(bytes), producedAtMs=4 (we read only data)\nimport { BinaryReader, BinaryWriter } from \"@bufbuild/protobuf/wire\";\n\n/** `StreamKind.STREAM_KIND_DESKTOP` (agent-proto enum value). */\nexport const STREAM_KIND_DESKTOP = 2;\n/** `StreamRole.STREAM_ROLE_CLIENT` (agent-proto enum value). */\nexport const STREAM_ROLE_CLIENT = 2;\n\nexport interface RelayChannel {\n channelId: string;\n workspaceId: string;\n agentId: string;\n kind: number;\n port: number;\n}\n\nexport interface RelayStreamOpen {\n channel: RelayChannel;\n token: string;\n role: number;\n resumeFromSeq: string;\n}\n\nfunction encodeChannel(message: RelayChannel, writer: BinaryWriter): BinaryWriter {\n if (message.channelId !== \"\") writer.uint32(10).string(message.channelId);\n if (message.workspaceId !== \"\") writer.uint32(18).string(message.workspaceId);\n if (message.agentId !== \"\") writer.uint32(26).string(message.agentId);\n if (message.kind !== 0) writer.uint32(32).int32(message.kind);\n if (message.port !== 0) writer.uint32(40).uint32(message.port);\n return writer;\n}\n\n/** Encode a `StreamOpen`, byte-compatible with `StreamOpen.encode(...).finish()`. */\nexport function encodeStreamOpen(message: RelayStreamOpen): Uint8Array {\n const writer = new BinaryWriter();\n encodeChannel(message.channel, writer.uint32(10).fork()).join();\n if (message.token !== \"\") writer.uint32(18).string(message.token);\n if (message.role !== 0) writer.uint32(24).int32(message.role);\n if (message.resumeFromSeq !== \"0\") writer.uint32(32).uint64(message.resumeFromSeq);\n return writer.finish();\n}\n\n/** The only `AgentError` field we surface from a rejected ack. */\nfunction decodeAgentErrorMessage(reader: BinaryReader, length: number): { message?: string } {\n const end = reader.pos + length;\n let message: string | undefined;\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 2 && tag === 18) {\n message = reader.string();\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return message !== undefined ? { message } : {};\n}\n\n/** Decode a `StreamOpenAck` (we need `accepted` + the error message). */\nexport function decodeStreamOpenAck(bytes: Uint8Array): {\n accepted: boolean;\n error?: { message?: string };\n} {\n const reader = new BinaryReader(bytes);\n const end = reader.len;\n let accepted = false;\n let error: { message?: string } | undefined;\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 1 && tag === 8) {\n accepted = reader.bool();\n continue;\n }\n if (tag >>> 3 === 2 && tag === 18) {\n error = decodeAgentErrorMessage(reader, reader.uint32());\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return error !== undefined ? { accepted, error } : { accepted };\n}\n\n/** Decode a `StreamFrame`, extracting only the `data` (framebuffer PNG) bytes. */\nexport function decodeStreamFrame(bytes: Uint8Array): { data: Uint8Array } {\n const reader = new BinaryReader(bytes);\n const end = reader.len;\n let data = new Uint8Array(0);\n while (reader.pos < end) {\n const tag = reader.uint32();\n if (tag >>> 3 === 3 && tag === 26) {\n // Copy into a fresh ArrayBuffer-backed view: BinaryReader.bytes() returns\n // `Uint8Array<ArrayBufferLike>` (a view into the message buffer), which both\n // narrows the type to `Uint8Array<ArrayBuffer>` and isolates just this frame.\n data = new Uint8Array(reader.bytes());\n continue;\n }\n if ((tag & 7) === 4 || tag === 0) break;\n reader.skip(tag & 7);\n }\n return { data };\n}\n","import {\n decodeStreamFrame,\n decodeStreamOpenAck,\n encodeStreamOpen,\n STREAM_KIND_DESKTOP,\n STREAM_ROLE_CLIENT,\n} from \"../lib/relay-wire\";\nimport type { DesktopConnectionState, DesktopStreamCapability } from \"@opengeni/sdk\";\nimport { type RefObject, useEffect, useRef, useState } from \"react\";\n\n/**\n * The minimal WebSocket surface the frame renderer drives. Lets tests (and a\n * future transport swap) inject a fake without a live socket — the exact\n * analogue of `DesktopRfbFactory` for the noVNC path. Structurally a subset of\n * the browser `WebSocket`.\n */\nexport interface DesktopWebSocketLike {\n binaryType: string;\n send(data: ArrayBuffer): void;\n close(): void;\n addEventListener(\n type: \"open\" | \"message\" | \"error\" | \"close\",\n listener: (ev: { data?: unknown } & Record<string, unknown>) => void,\n ): void;\n removeEventListener(\n type: \"open\" | \"message\" | \"error\" | \"close\",\n listener: (ev: { data?: unknown } & Record<string, unknown>) => void,\n ): void;\n}\n\nexport type DesktopWebSocketFactory = (url: string) => DesktopWebSocketLike;\n\nexport type UseRelayFrameStreamOptions = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /** The mount target. A `<canvas>` is appended here on connect. */\n containerRef: RefObject<HTMLDivElement | null>;\n /** Custom socket factory (tests / a transport swap). Defaults to `new WebSocket(url)`. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n};\n\nexport type UseRelayFrameStreamResult = {\n state: DesktopConnectionState;\n error: Error | null;\n /** Tear down + reopen the socket (e.g. after a drop, once a fresh url arrives). */\n reconnect: () => void;\n};\n\n// Relay datagram tags: byte[0] of every binary message. Mirrors the proven wire\n// protocol in `apps/api/scripts/diagnose-mac-desktop-stream.ts`.\nconst TAG_OPEN = 1;\nconst TAG_OPENACK = 2;\nconst TAG_FRAME = 3;\n\n/** Build a relay datagram: a fresh Uint8Array of `body.length + 1` with the tag\n * at [0] and the protobuf body copied at offset 1 (so `.buffer` is exact). */\nfunction datagram(tag: number, body: Uint8Array): Uint8Array {\n const out = new Uint8Array(body.length + 1);\n out[0] = tag;\n out.set(body, 1);\n return out;\n}\n\nfunction defaultWebSocketFactory(url: string): DesktopWebSocketLike {\n // The browser `WebSocket` is structurally a superset of `DesktopWebSocketLike`\n // (its event maps are stricter); cast so the seam stays loosely typed for fakes.\n return new WebSocket(url) as unknown as DesktopWebSocketLike;\n}\n\n/**\n * VIEW-ONLY PNG-frame renderer for a SELF-HOSTED desktop stream — the relay\n * transport (`transport: \"relay-frames\"`, `client: \"frames\"`). The self-hosted\n * agent produces one PNG per frame as a protobuf datagram over the relay; there\n * is no RFB/noVNC here. This hook opens the relay DESKTOP channel as a CLIENT\n * (mirroring the proven diagnostic wire protocol), decodes each PNG onto a\n * `<canvas>` mounted into `containerRef`, and drives the connection state\n * machine: idle → connecting (ws opening) → connected (first painted frame) →\n * error (ws error/close/ack rejection).\n *\n * It is DORMANT for any other transport (or no url): it stays idle and touches\n * nothing, so the noVNC path owns the surface. SSR-safe: the socket + DOM attach\n * live inside `useEffect`.\n *\n * Backpressure (critical — frames are ~1.8MB at ~10fps): at most one frame is\n * decoded at a time; a frame arriving mid-decode replaces the pending one\n * (latest-wins), so a slow decode can never build an unbounded queue.\n */\nexport function useRelayFrameStream(\n options: UseRelayFrameStreamOptions,\n): UseRelayFrameStreamResult {\n const { capability, containerRef, webSocketFactory } = options;\n const [state, setState] = useState<DesktopConnectionState>(\"idle\");\n const [error, setError] = useState<Error | null>(null);\n const [nonce, setNonce] = useState(0);\n const stateRef = useRef<DesktopConnectionState>(\"idle\");\n const setBoth = (next: DesktopConnectionState) => {\n stateRef.current = next;\n setState(next);\n };\n\n const reconnect = () => setNonce((n) => n + 1);\n\n const transport = capability?.transport ?? null;\n const url = capability?.url ?? null;\n const token = capability?.token ?? null;\n\n // The factory is read via a ref so swapping it never re-opens the socket (it is\n // not a connect-effect dependency), symmetric with the noVNC `rfbFactory`.\n const factoryRef = useRef(webSocketFactory);\n factoryRef.current = webSocketFactory;\n\n useEffect(() => {\n // SSR / no DOM: stay idle and show the placeholder.\n if (typeof window === \"undefined\") return;\n // This hook OWNS the surface ONLY for the frame transport; anything else (or\n // no live url) → dormant idle so the noVNC path can drive.\n if (transport !== \"relay-frames\" || !url) {\n setBoth(\"idle\");\n return;\n }\n const container = containerRef.current;\n if (!container) {\n // Mount target not in the DOM yet (tab hidden) — stay idle so a re-attach\n // nudge can re-run this effect once the container exists.\n setBoth(\"idle\");\n return;\n }\n\n // Parse the 4 relay query params the channel is keyed on.\n let channel: {\n channelId: string;\n workspaceId: string;\n agentId: string;\n kind: number;\n port: number;\n };\n try {\n const u = new URL(url);\n channel = {\n channelId: u.searchParams.get(\"channel\") ?? \"\",\n workspaceId: u.searchParams.get(\"ws\") ?? \"\",\n agentId: u.searchParams.get(\"agent\") ?? \"\",\n kind: STREAM_KIND_DESKTOP,\n port: Number(u.searchParams.get(\"port\") ?? \"0\"),\n };\n } catch (cause) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n return;\n }\n\n setError(null);\n setBoth(\"connecting\");\n\n // Canvas mount — mirror the noVNC surface conventions: absolute-fill the\n // panel and CONTAIN the framebuffer (aspect-preserved, centered, letterboxed)\n // — never distorted, never overflowing. `max-*: 100%` + `margin: auto` +\n // intrinsic size gives fit-to-panel without a resize observer.\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"data-opengeni-desktop-frames\", \"\");\n const style = canvas.style;\n style.position = \"absolute\";\n style.top = \"0\";\n style.left = \"0\";\n style.right = \"0\";\n style.bottom = \"0\";\n style.margin = \"auto\";\n style.maxWidth = \"100%\";\n style.maxHeight = \"100%\";\n style.width = \"auto\";\n style.height = \"auto\";\n style.display = \"block\";\n style.imageRendering = \"auto\";\n container.appendChild(canvas);\n const ctx = canvas.getContext(\"2d\");\n\n let disposed = false;\n let acked = false;\n // Backpressure: decode at most ONE frame at a time; a frame arriving mid-\n // decode replaces `pending` (keep only the LATEST) — never a queue.\n let decoding = false;\n let pending: Uint8Array | null = null;\n\n const drainLatest = async () => {\n if (decoding) return;\n decoding = true;\n try {\n while (pending) {\n if (disposed) break;\n const data = pending;\n pending = null;\n let bmp: ImageBitmap;\n try {\n // Copy the exact frame bytes into a fresh ArrayBuffer-backed view:\n // ts-proto's `bytes()` hands back a subarray VIEW into the larger\n // message buffer, so slicing isolates just this PNG (and satisfies the\n // `BlobPart` typing, which rejects the generic `ArrayBufferLike`).\n bmp = await createImageBitmap(new Blob([new Uint8Array(data)], { type: \"image/png\" }));\n } catch {\n // A corrupt/partial frame is skipped; latest-wins keeps us live.\n continue;\n }\n if (disposed) {\n bmp.close();\n break;\n }\n // Size the backing store to the frame's natural WxH on the first frame\n // (or when the resolution changes); CSS then scales the element to fit.\n if (canvas.width !== bmp.width || canvas.height !== bmp.height) {\n canvas.width = bmp.width;\n canvas.height = bmp.height;\n }\n ctx?.drawImage(bmp, 0, 0, canvas.width, canvas.height);\n bmp.close();\n // First painted frame → connected.\n if (!disposed && stateRef.current !== \"connected\") setBoth(\"connected\");\n }\n } finally {\n decoding = false;\n }\n };\n\n let ws: DesktopWebSocketLike;\n try {\n ws = (factoryRef.current ?? defaultWebSocketFactory)(url);\n } catch (cause) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setBoth(\"error\");\n canvas.remove();\n return;\n }\n ws.binaryType = \"arraybuffer\";\n\n const onOpen = () => {\n if (disposed) return;\n const body = encodeStreamOpen({\n channel,\n token: token ?? \"\",\n role: STREAM_ROLE_CLIENT,\n resumeFromSeq: \"0\",\n });\n try {\n ws.send(datagram(TAG_OPEN, body).buffer as ArrayBuffer);\n } catch {\n // Socket already closing; the close/error handler surfaces it.\n }\n };\n\n const onMessage = (ev: { data?: unknown }) => {\n if (disposed) return;\n const data = ev.data;\n if (!(data instanceof ArrayBuffer)) return;\n const buf = new Uint8Array(data);\n if (buf.length === 0) return;\n const tag = buf[0];\n const rest = buf.subarray(1);\n if (tag === TAG_OPENACK) {\n let ack: ReturnType<typeof decodeStreamOpenAck>;\n try {\n ack = decodeStreamOpenAck(rest);\n } catch {\n return;\n }\n if (!ack.accepted) {\n setError(\n new Error(\n ack.error?.message\n ? `desktop stream rejected: ${ack.error.message}`\n : \"desktop stream rejected by the relay\",\n ),\n );\n setBoth(\"error\");\n try {\n ws.close();\n } catch {\n // already closed\n }\n return;\n }\n // Accepted — stay \"connecting\" until the first frame paints.\n acked = true;\n } else if (tag === TAG_FRAME) {\n let fr: ReturnType<typeof decodeStreamFrame>;\n try {\n fr = decodeStreamFrame(rest);\n } catch {\n return;\n }\n if (fr.data && fr.data.length > 0) {\n pending = fr.data; // latest-wins backpressure\n void drainLatest();\n }\n }\n };\n\n const onError = () => {\n if (disposed) return;\n setError((prev) => prev ?? new Error(\"desktop stream connection error\"));\n setBoth(\"error\");\n };\n\n const onClose = () => {\n if (disposed) return;\n setError(\n (prev) =>\n prev ??\n new Error(acked ? \"desktop stream closed\" : \"desktop stream closed before it opened\"),\n );\n setBoth(\"error\");\n };\n\n ws.addEventListener(\"open\", onOpen);\n ws.addEventListener(\"message\", onMessage);\n ws.addEventListener(\"error\", onError);\n ws.addEventListener(\"close\", onClose);\n\n return () => {\n disposed = true;\n pending = null;\n ws.removeEventListener(\"open\", onOpen);\n ws.removeEventListener(\"message\", onMessage);\n ws.removeEventListener(\"error\", onError);\n ws.removeEventListener(\"close\", onClose);\n try {\n ws.close();\n } catch {\n // ignore teardown errors\n }\n canvas.remove();\n };\n // ONLY a real transport change reopens: a fresh url (rotation), a new token,\n // the transport flipping, or a manual reconnect (`nonce`). The factory is read\n // via a ref and must never re-open the socket.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [url, token, transport, nonce]);\n\n return { state, error, reconnect };\n}\n","import {\n TTYD_SUBPROTOCOL,\n TerminalCapability,\n terminalSocketUrl,\n ttydAuthFrame,\n ttydInputFrame,\n ttydResizeFrame,\n TtydServerCommand,\n} from \"@opengeni/sdk\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\n/** The ttyd connection lifecycle as surfaced to the component. */\nexport type TerminalStreamStatus = \"connecting\" | \"open\" | \"closed\" | \"error\";\n\nexport type UseTerminalStreamOptions = {\n /** The Terminal cell of the negotiated capabilities (`capabilities.Terminal`).\n * The stream connects ONLY when `transport === \"pty-ws\"` and `url` is set; on a\n * cold box (`transport === \"sse-events\"` / no url) it stays idle and the caller\n * falls back to the Channel-A read-only firehose. */\n capability: Pick<TerminalCapability, \"transport\" | \"url\" | \"token\"> | null;\n /** Called for each OUTPUT payload from ttyd (write verbatim into xterm). */\n onOutput?: ((data: string) => void) | undefined;\n /** Called when ttyd sends a SET_WINDOW_TITLE frame. */\n onTitle?: ((title: string) => void) | undefined;\n /** Initial PTY size to seed the ttyd auth frame + first resize. */\n initialCols?: number | undefined;\n initialRows?: number | undefined;\n};\n\nexport type UseTerminalStreamResult = {\n /** True once the ttyd socket is open (and the auth frame has been sent). */\n connected: boolean;\n status: TerminalStreamStatus;\n /** Pipe a keystroke/paste to the PTY stdin. No-op until the socket is open. */\n write: (data: string) => void;\n /** Tell ttyd the PTY window changed size (on xterm fit/resize). */\n resize: (cols: number, rows: number) => void;\n /** Tear the socket down (the effect also tears down on unmount / url change). */\n disconnect: () => void;\n};\n\n/** Decode an inbound ttyd frame's payload (everything after the 1-char command).\n * ttyd may send either a text frame (string) or a binary frame (ArrayBuffer);\n * for binary we slice off the first byte (the command) and utf-8 decode the rest. */\nfunction decodeFrame(data: string | ArrayBuffer): { command: string; payload: string } {\n if (typeof data === \"string\") {\n return { command: data.charAt(0), payload: data.slice(1) };\n }\n const bytes = new Uint8Array(data);\n const command = bytes.length > 0 ? String.fromCharCode(bytes[0]!) : \"\";\n const payload = bytes.length > 1 ? new TextDecoder().decode(bytes.subarray(1)) : \"\";\n return { command, payload };\n}\n\n/**\n * Drive a ttyd PTY-over-websocket connection from a `pty-ws` Terminal capability,\n * symmetric with `use-desktop-stream` (the noVNC-over-tunnel hook). The scoped\n * stream token is already embedded in the minted tunnel `url`; the WebSocket is\n * opened with the REQUIRED ttyd subprotocol \"tty\".\n *\n * ttyd wire protocol (see `@opengeni/sdk/terminal`):\n * - first frame: `JSON.stringify({ AuthToken: \"\" })` (+ optional columns/rows).\n * - client→server: INPUT = \"0\"+data ; RESIZE = \"1\"+JSON({columns,rows}).\n * - server→client: \"0\" = OUTPUT (→ xterm) ; \"1\" = SET_WINDOW_TITLE ;\n * \"2\" = SET_PREFERENCES (ignored). Binary frames are decoded the same way.\n *\n * On a `url`/`token` rotation (a box rollover folds a fresh address into the cell)\n * the effect re-runs: the old socket closes and a fresh one connects — a brief\n * terminal blink, acceptable on rollover (mirrors the desktop's RFB hot-swap).\n * SSR-safe: the socket open lives in `useEffect`, so a server render is a no-op.\n */\nexport function useTerminalStream(options: UseTerminalStreamOptions): UseTerminalStreamResult {\n const { capability, onOutput, onTitle, initialCols, initialRows } = options;\n const [status, setStatus] = useState<TerminalStreamStatus>(\"closed\");\n const wsRef = useRef<WebSocket | null>(null);\n // Latest size, so a resize() before the socket opens is replayed on open, and a\n // reconnect seeds the right geometry.\n const sizeRef = useRef<{ cols: number; rows: number }>({\n cols: initialCols ?? 80,\n rows: initialRows ?? 24,\n });\n // Keep the callbacks current without re-running the connect effect on every\n // render (the parent passes fresh closures each time).\n const onOutputRef = useRef(onOutput);\n const onTitleRef = useRef(onTitle);\n onOutputRef.current = onOutput;\n onTitleRef.current = onTitle;\n\n const transport = capability?.transport ?? null;\n const url = capability?.url ?? null;\n const token = capability?.token ?? null;\n\n useEffect(() => {\n // SSR / no WebSocket / not a live pty-ws cell: stay closed; the caller falls\n // back to the Channel-A read-only firehose.\n if (typeof window === \"undefined\" || typeof WebSocket === \"undefined\") return;\n if (transport !== \"pty-ws\" || !url) {\n setStatus(\"closed\");\n return;\n }\n\n let disposed = false;\n let socket: WebSocket;\n setStatus(\"connecting\");\n try {\n // The ttyd \"tty\" subprotocol is REQUIRED — ttyd rejects a handshake without\n // it. The scoped token is already in the tunnel `url`.\n socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);\n } catch {\n setStatus(\"error\");\n return;\n }\n socket.binaryType = \"arraybuffer\";\n wsRef.current = socket;\n\n socket.onopen = () => {\n if (disposed) return;\n // ttyd's required first frame: the auth message (empty token — the gate is\n // the tunnel url + scoped stream token, not a ttyd -c credential), seeded\n // with the current PTY geometry. Then an explicit resize to be safe.\n try {\n socket.send(ttydAuthFrame({ columns: sizeRef.current.cols, rows: sizeRef.current.rows }));\n socket.send(ttydResizeFrame(sizeRef.current.cols, sizeRef.current.rows));\n } catch {\n // a closed socket between open and send — onclose handles state.\n }\n setStatus(\"open\");\n };\n\n socket.onmessage = (ev: MessageEvent) => {\n if (disposed) return;\n const { command, payload } = decodeFrame(ev.data as string | ArrayBuffer);\n switch (command) {\n case TtydServerCommand.OUTPUT:\n onOutputRef.current?.(payload);\n break;\n case TtydServerCommand.SET_WINDOW_TITLE:\n onTitleRef.current?.(payload);\n break;\n // SET_PREFERENCES (\"2\") and anything else: ignored.\n default:\n break;\n }\n };\n\n socket.onerror = () => {\n if (!disposed) setStatus(\"error\");\n };\n socket.onclose = () => {\n if (!disposed) setStatus(\"closed\");\n };\n\n return () => {\n disposed = true;\n wsRef.current = null;\n // Drop handlers so an in-flight close/error doesn't mutate state post-unmount.\n socket.onopen = null;\n socket.onmessage = null;\n socket.onerror = null;\n socket.onclose = null;\n try {\n socket.close();\n } catch {\n // ignore teardown errors\n }\n };\n // A url/token change (rotation) re-runs this effect → close old, open new.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [transport, url, token]);\n\n return useMemo<UseTerminalStreamResult>(() => {\n const write = (data: string) => {\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(ttydInputFrame(data));\n } catch {\n // socket raced closed — the reconnect effect will re-establish.\n }\n }\n };\n const resize = (cols: number, rows: number) => {\n if (cols <= 0 || rows <= 0) return;\n sizeRef.current = { cols, rows };\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN) {\n try {\n ws.send(ttydResizeFrame(cols, rows));\n } catch {\n // socket raced closed — geometry is replayed on the next open.\n }\n }\n };\n const disconnect = () => {\n const ws = wsRef.current;\n wsRef.current = null;\n if (ws) {\n try {\n ws.close();\n } catch {\n // ignore\n }\n }\n };\n return { connected: status === \"open\", status, write, resize, disconnect };\n }, [status]);\n}\n","import type {\n SandboxCommandOutputDeltaPayload,\n SessionEvent,\n TerminalPtyExitedPayload,\n TerminalPtyOutputDeltaPayload,\n} from \"@opengeni/sdk\";\nimport { OpenGeniApiError } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\nexport type TerminalChunk = {\n /** Stable key (the source event id) so the xterm writer tracks a written-cursor. */\n id: string;\n /** Raw output bytes (utf-8 lossy) — written verbatim into xterm. */\n text: string;\n /** stdout vs stderr (drives optional tinting). */\n stream: \"stdout\" | \"stderr\";\n /** Global ordering: the source event sequence. */\n seq: number;\n};\n\nexport type UseSandboxTerminalOptions = ClientOverride & {\n /** The live session event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /** Restrict to one PTY (by ptyId). Omit to interleave the agent firehose +\n * every PTY. */\n ptyId?: string | undefined;\n /** Include the agent's command-output firehose (sandbox.command.output.delta).\n * Default true — the read-only \"terminal-as-events\" the data path settled on. */\n includeAgentFirehose?: boolean | undefined;\n /**\n * OPEN an interactive PTY against the box so the user can type, not just watch.\n * When true (and the session is live) the hook calls `terminalPtyOpen` once,\n * tracks the returned ptyId, exposes `write` immediately (bound to that ptyId),\n * and closes the PTY on unmount. The PTY's banner + every output delta ride the\n * SSE spine (`terminal.pty.*`) back into `events`, so xterm fills in. Default\n * false — a caller that only wants the read-only firehose stays projection-only.\n */\n interactive?: boolean | undefined;\n /** Lease liveness (\"cold\" | \"warm\" | \"draining\"). The interactive PTY is only\n * opened once the box is warm — opening on a cold box (ptyCapable is advertised\n * cold too) races the box and leaves a dead read-only terminal. */\n liveness?: string | undefined;\n};\n\nexport type UseSandboxTerminalResult = {\n /** Ordered, deduped output chunks to write() into xterm.js. */\n chunks: TerminalChunk[];\n /** Whether a PTY is currently open (drives the prompt/cursor affordance). */\n running: boolean;\n /**\n * Interactive write fn when a PTY is open and the backend supports stdin\n * (`terminal.transport === \"pty-ws\"` / `PtyOpenResponse.supportsInput`). Null\n * in the read-only event-projection case (v1 default).\n */\n write: ((data: string) => void) | null;\n /** The active PTY id, if one is open. */\n activePtyId: string | null;\n /** Close the active PTY (no-op when none is open). */\n close: () => void;\n /** A PTY-open failure (interactive mode), if any. */\n error: Error | null;\n};\n\n/**\n * Project the Channel-A event log into an xterm-writable byte stream. The\n * terminal is \"terminal-as-events\": there is NO new socket in v1 — the agent's\n * command output (`sandbox.command.output.delta`) and any interactive PTY\n * (`terminal.pty.output.delta`) ride the existing SSE spine. When a PTY is open\n * and the backend accepts stdin, `write` pipes keystrokes via the SDK\n * `terminalPtyWrite` (the synchronous Channel-A control path).\n */\nexport function useSandboxTerminal(\n sessionId: string | null | undefined,\n options: UseSandboxTerminalOptions,\n): UseSandboxTerminalResult {\n const { client, workspaceId } = useOpenGeni(options);\n const includeAgentFirehose = options.includeAgentFirehose ?? true;\n const interactive = options.interactive ?? false;\n // When the caller controls a fixed ptyId we project that one; otherwise (the\n // interactive default) the hook opens its OWN PTY and tracks it here so `write`\n // is live before the `terminal.pty.started` event round-trips through SSE.\n const ptyFilter = options.ptyId;\n const liveness = options.liveness;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${interactive}\\u0000${ptyFilter ?? \"\"}`;\n const [openedPtyId, setOpenedPtyId] = useState<string | null>(null);\n const [openError, setOpenError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Bumped to force a fresh PTY open after a write reveals the old PTY was lost\n // (a 409 \"pty session lost\" — the box rolled over since the open). This makes\n // the interactive terminal self-heal instead of dead-ending on a stale session.\n const [reopenNonce, setReopenNonce] = useState(0);\n const currentIdentityRef = useRef(identityKey);\n currentIdentityRef.current = identityKey;\n\n // Async PTY state belongs to one workspace/session/mode identity. Hide it\n // synchronously on the switching render, then clear it durably in the effect.\n useEffect(() => {\n setStateIdentity(identityKey);\n setOpenedPtyId(null);\n setOpenError(null);\n }, [identityKey]);\n\n // Open ONE interactive PTY against the box, close it on unmount/identity\n // change. The open's banner + subsequent output deltas ride A1, so xterm fills\n // from `events` — we don't thread the open's body output here (the SSE\n // projection is the single source of truth for what's written). The open is\n // gated on a WARM box: opening on a cold/warming lease races the box (the PTY\n // exec-session is created on a box that the next op may not resume), which is\n // exactly the \"session not found\" terminal failure; we wait for warm/draining.\n const openInFlight = useRef(false);\n const boxWarm = liveness === undefined || liveness === \"warm\" || liveness === \"draining\";\n useEffect(() => {\n if (!interactive || !sessionId || ptyFilter || !boxWarm) {\n setOpenError(null);\n return;\n }\n let cancelled = false;\n setOpenError(null);\n openInFlight.current = true;\n let openedId: string | null = null;\n void client\n .terminalPtyOpen(workspaceId, sessionId, {})\n .then((res) => {\n if (cancelled) {\n // Raced past unmount — close the orphan we just opened.\n void client\n .terminalPtyClose(workspaceId, sessionId, { ptyId: res.ptyId })\n .catch(() => {});\n return;\n }\n openedId = res.ptyId;\n setOpenedPtyId(res.ptyId);\n })\n .catch((cause) => {\n if (!cancelled) setOpenError(cause instanceof Error ? cause : new Error(String(cause)));\n })\n .finally(() => {\n openInFlight.current = false;\n });\n return () => {\n cancelled = true;\n setOpenedPtyId(null);\n const id = openedId;\n if (id) void client.terminalPtyClose(workspaceId, sessionId, { ptyId: id }).catch(() => {});\n };\n }, [interactive, client, workspaceId, sessionId, ptyFilter, boxWarm, reopenNonce]);\n\n const { chunks, openPty, supportsInput } = useMemo(() => {\n const out: TerminalChunk[] = [];\n // Track PTY lifecycle so `running`/`write` reflect the latest state.\n const open = new Map<string, { supportsInput: boolean }>();\n let lastOpened: string | null = null;\n let lastSupportsInput = false;\n\n for (const event of options.events) {\n // The hook is session-scoped even if a host accidentally retains the old\n // event array for one render during navigation.\n if (event.workspaceId !== workspaceId || event.sessionId !== sessionId) continue;\n if (event.type === \"terminal.pty.started\") {\n const payload = event.payload as { ptyId?: string } | null;\n if (payload?.ptyId) {\n open.set(payload.ptyId, { supportsInput: true });\n lastOpened = payload.ptyId;\n }\n continue;\n }\n if (event.type === \"terminal.pty.exited\") {\n const payload = event.payload as TerminalPtyExitedPayload | null;\n if (payload?.ptyId) {\n open.delete(payload.ptyId);\n if (lastOpened === payload.ptyId) lastOpened = null;\n }\n continue;\n }\n if (event.type === \"terminal.pty.output.delta\") {\n const payload = event.payload as TerminalPtyOutputDeltaPayload | null;\n if (!payload || (ptyFilter && payload.ptyId !== ptyFilter)) continue;\n out.push({\n id: event.id,\n text: payload.chunk,\n stream: payload.stream === \"stderr\" ? \"stderr\" : \"stdout\",\n seq: event.sequence,\n });\n continue;\n }\n if (includeAgentFirehose && !ptyFilter && event.type === \"sandbox.command.output.delta\") {\n const payload = event.payload as SandboxCommandOutputDeltaPayload | null;\n if (!payload?.chunk) continue;\n out.push({\n id: event.id,\n text: payload.chunk,\n stream: payload.stream === \"stderr\" ? \"stderr\" : \"stdout\",\n seq: event.sequence,\n });\n }\n }\n\n // Stable order: by sequence (the SSE spine guarantees per-session ordering).\n out.sort((a, b) => a.seq - b.seq);\n const activePty = ptyFilter && open.has(ptyFilter) ? ptyFilter : lastOpened;\n lastSupportsInput = activePty ? (open.get(activePty)?.supportsInput ?? false) : false;\n return { chunks: out, openPty: activePty, supportsInput: lastSupportsInput };\n }, [options.events, ptyFilter, includeAgentFirehose, workspaceId, sessionId]);\n\n // The PTY this hook actively drives: the one it opened (interactive) wins so\n // `write` is live the instant the open resolves — even before the\n // `terminal.pty.started` event arrives through SSE. Fall back to whatever the\n // event projection found (a PTY opened elsewhere, or a caller-pinned ptyId).\n const identityMatches = stateIdentity === identityKey;\n const activePtyId = identityMatches ? (openedPtyId ?? openPty) : null;\n // An interactively-opened PTY accepts stdin by construction (we only open it on\n // a pty-capable backend); a projected PTY uses its advertised supportsInput.\n const canWrite = openedPtyId !== null || supportsInput;\n\n const write = useMemo(() => {\n if (!activePtyId || !canWrite || !sessionId) return null;\n const writeIdentity = identityKey;\n return (data: string) => {\n void client\n .terminalPtyWrite(workspaceId, sessionId, { ptyId: activePtyId, data })\n .catch((cause) => {\n // The PTY exec-session was lost on the live box (409/404 — the box rolled\n // over since the open). Self-heal: drop the stale id and re-open a fresh\n // PTY against the current box rather than silently swallowing keystrokes.\n if (\n cause instanceof OpenGeniApiError &&\n (cause.status === 409 || cause.status === 404) &&\n activePtyId === openedPtyId &&\n currentIdentityRef.current === writeIdentity\n ) {\n setOpenedPtyId(null);\n setReopenNonce((n) => n + 1);\n }\n });\n };\n }, [client, workspaceId, sessionId, activePtyId, canWrite, openedPtyId, identityKey]);\n\n const close = useCallback(() => {\n if (!activePtyId || !sessionId) return;\n void client.terminalPtyClose(workspaceId, sessionId, { ptyId: activePtyId }).catch(() => {});\n }, [client, workspaceId, sessionId, activePtyId]);\n\n return {\n chunks,\n running: activePtyId !== null,\n write,\n activePtyId,\n close,\n error: identityMatches ? openError : null,\n };\n}\n","import type {\n FsChangedPayload,\n FsReadResponse,\n FsTreeNode,\n FsWriteResponse,\n GitChangedPayload,\n GitFileStatusCode,\n OpenGeniRequestOptions,\n SessionEvent,\n WorkspaceCaptureManifest,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/** The git-status overlay a file row may carry (tints modified files in the tree). */\nexport type FileTreeStatus = \"added\" | \"modified\" | \"deleted\" | \"renamed\" | \"untracked\";\n\nexport type CapturedFileUnavailableReason = \"not-captured\" | \"too-large\" | \"content-missing\";\n\n/** A cold capture can index a path without retaining its contents. Components\n * use this typed result to offer an explicit wake action instead of silently\n * turning passive review into a live sandbox read. */\nexport class CapturedFileUnavailableError extends Error {\n readonly code = \"captured_file_unavailable\";\n\n constructor(\n readonly path: string,\n readonly reason: CapturedFileUnavailableReason,\n ) {\n super(\n reason === \"too-large\"\n ? `${path} exceeds the captured preview limit.`\n : reason === \"content-missing\"\n ? `${path} is no longer available in this capture.`\n : `${path} was not changed in this captured turn.`,\n );\n this.name = \"CapturedFileUnavailableError\";\n }\n}\n\nexport class FileWriteConflictError extends Error {\n readonly code = \"file_write_conflict\";\n\n constructor(\n readonly path: string,\n readonly expectedContent: string,\n readonly liveContent: string,\n ) {\n super(`${path} changed on the machine. Reload it or explicitly overwrite the live version.`);\n this.name = \"FileWriteConflictError\";\n }\n}\n\nexport type SandboxWriteFileOptions = {\n /** Exact text loaded into the editor. The live file is re-read before write;\n * divergence fails closed with FileWriteConflictError. */\n expectedContent?: string | undefined;\n /** Deliberately bypass the expected-content guard after a visible conflict. */\n force?: boolean | undefined;\n};\n\n/** A node in the Pierre file tree. `children === undefined` ⇒ an unexpanded dir\n * (lazy treeMode); `children: []` ⇒ an expanded-but-empty dir. */\nexport type FileTreeNode = {\n path: string; // workspace-relative POSIX\n name: string;\n kind: \"file\" | \"dir\";\n children?: FileTreeNode[] | undefined;\n size?: number | null | undefined;\n status?: FileTreeStatus | undefined;\n /** A residue dir the capture COLLAPSED (node_modules, .git, dist, …): its\n * contents were never indexed. Cold, expanding it can't list children — the\n * UI shows an inline \"contents on machine\" row until the box is warm. */\n truncated?: boolean | undefined;\n};\n\nexport type UseSandboxFilesOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh\n * on `fs.changed` / `git.changed`. */\n events?: SessionEvent[] | undefined;\n /** Initial path to list (workspace root by default). */\n rootPath?: string | undefined;\n /** Hold off the initial list (e.g. panel collapsed). Default true. */\n enabled?: boolean | undefined;\n /** The lease liveness (\"cold\" | \"warm\" | \"draining\"). The structured FileSystem\n * capability is advertised even on a COLD box, so the mount-time list can race\n * the box: it lists before the box is warm, gets an empty/errored result, and\n * (with no `fs.changed` event) never re-lists. Passing liveness re-lists when\n * the box first becomes warm, so the tree populates as soon as the box is up. */\n liveness?: string | undefined;\n /** The latest turn-end workspace capture (from `useWorkspaceCapture`). The tree\n * paints immediately from this durable index, then a warm box reconciles live\n * in place. If that live read fails, the capture remains a truthful read-only\n * fallback instead of turning into an empty error surface. */\n capture?: WorkspaceCaptureManifest | null | undefined;\n /** Called when an OPTIMISTIC mutation is reverted because its background\n * Channel-A op failed (e.g. a 409 rename collision). The host wires this to a\n * toast — the tree silently rolls the node back, the user sees why. */\n onMutationError?: ((error: Error, op: string) => void) | undefined;\n};\n\nexport type UseSandboxFilesResult = {\n /** The tree roots (the listed root's children). */\n tree: FileTreeNode[];\n /** Lazy-expand a directory node in place (lists its immediate children). */\n expand: (path: string) => Promise<void>;\n /** Paths whose lazy `fs.list` is currently in flight — the FileBrowser shows a\n * spinner on these nodes so a 2-3s Channel-A list never looks frozen. */\n expandingPaths: Set<string>;\n /** Read a file for the preview pane (text or base64-for-binary, size-capped). */\n readFile: (path: string, options?: OpenGeniRequestOptions) => Promise<FsReadResponse>;\n /** Write a file (overwrite, last-writer-wins) — the editor save path.\n * Optimistic: a brand-new file is spliced into the tree immediately and the\n * Channel-A write runs in the background; on failure the splice is reverted. */\n writeFile: (\n path: string,\n content: string,\n options?: SandboxWriteFileOptions,\n ) => Promise<FsWriteResponse>;\n /** Create a new empty file (refuses to clobber an existing path: overwrite=false). */\n createFile: (path: string) => Promise<void>;\n /** Create a directory (recursive by default). */\n createDir: (path: string) => Promise<void>;\n /** Delete a path (pass recursive=true for a non-empty directory). */\n deleteEntry: (path: string, recursive?: boolean) => Promise<void>;\n /** Move / rename a path (rename == move). Refuses to clobber unless overwrite=true. */\n moveEntry: (path: string, newPath: string, opts?: { overwrite?: boolean }) => Promise<void>;\n /** Re-list the whole tree from the root. */\n refresh: () => Promise<void>;\n /** Which source the tree is currently served from: the live box, the turn-end\n * capture (cold/offline), or neither yet. M5's source badge reads this. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n loading: boolean;\n error: Error | null;\n};\n\n/** The workspace-relative parent directory of a POSIX path (\"\" for a root entry). */\nfunction parentOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i <= 0 ? \"\" : path.slice(0, i);\n}\n\n/** The leaf name of a POSIX path. */\nfunction leafOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i < 0 ? path : path.slice(i + 1);\n}\n\n/** Join a parent dir with a leaf name (handles the root \"\" parent). */\n/** Stable sibling ordering: dirs before files, then case-insensitive by name —\n * matches a typical depth-1 list so an optimistic insert lands where a real\n * re-list would put it (no jump when the server reconciles). */\nfunction sortNodes(nodes: FileTreeNode[]): FileTreeNode[] {\n return [...nodes].sort((a, b) => {\n if (a.kind !== b.kind) return a.kind === \"dir\" ? -1 : 1;\n return a.name.localeCompare(b.name, undefined, { sensitivity: \"base\" });\n });\n}\n\nfunction fsNodeToTree(node: FsTreeNode, captureComplete = false): FileTreeNode {\n const kind = node.type === \"dir\" ? \"dir\" : \"file\";\n // Lazy-tree contract: a depth-bounded `fsList` returns each directory at the\n // depth boundary with `children: []` (the dir is listed, but its grandchildren\n // are NOT). An empty array therefore means \"not yet expanded\", NOT \"empty\n // directory\" — so we must map it to `undefined` (the unexpanded marker the\n // FileBrowser keys lazy-expand on). If we kept `[]`, `toggle()`'s\n // `node.children === undefined` guard would never fire and clicking a folder\n // would do nothing (the reported bug). A directory we actually expand has its\n // children spliced in by `replaceChildren` (bypassing this mapper), so a\n // genuinely-empty dir correctly ends up as `[]` AFTER expansion.\n const mappedChildren =\n node.children && node.children.length > 0\n ? node.children.map((child) => fsNodeToTree(child, captureComplete))\n : captureComplete && kind === \"dir\"\n ? []\n : undefined;\n return {\n path: node.path,\n name: node.name,\n kind,\n size: node.sizeBytes,\n ...(kind === \"dir\" ? { children: mappedChildren } : {}),\n // A collapsed residue dir from the capture index — carried through so the\n // tree can show an inline \"contents on machine\" row cold instead of nothing.\n ...(kind === \"dir\" && node.truncated ? { truncated: true } : {}),\n };\n}\n\nconst GIT_STATUS_TO_TREE: Partial<Record<GitFileStatusCode, FileTreeStatus>> = {\n added: \"added\",\n modified: \"modified\",\n deleted: \"deleted\",\n renamed: \"renamed\",\n copied: \"added\",\n untracked: \"untracked\",\n typechange: \"modified\",\n};\n\n/** Replace the children of `targetPath` within the tree (immutably). */\nfunction replaceChildren(\n nodes: FileTreeNode[],\n targetPath: string,\n children: FileTreeNode[],\n): FileTreeNode[] {\n return nodes.map((node) => {\n if (node.path === targetPath) {\n return { ...node, children };\n }\n if (node.kind === \"dir\" && node.children && targetPath.startsWith(`${node.path}/`)) {\n return { ...node, children: replaceChildren(node.children, targetPath, children) };\n }\n return node;\n });\n}\n\n/** Find a node by exact path (depth-first). */\nfunction findNodeByPath(nodes: FileTreeNode[], path: string): FileTreeNode | undefined {\n for (const node of nodes) {\n if (node.path === path) return node;\n if (node.kind === \"dir\" && node.children && path.startsWith(`${node.path}/`)) {\n const hit = findNodeByPath(node.children, path);\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n// ── In-place (optimistic) tree mutations ────────────────────────────────────\n// The Pierre mutation-handle analogue. These splice a single node in/out/across\n// the immutable tree, PRESERVING every other node's `children` (and therefore the\n// FileBrowser's expansion + selection). The old data flow re-listed the whole\n// root and `setTree(rootChildren)` — that dropped every expanded dir back to the\n// unexpanded marker, which is exactly the \"everything refreshes / collapses to\n// .config\" the user saw. Mutating in place is the fix.\n//\n// `parent === \"\"` targets the root list directly. A parent that isn't present in\n// the (lazily-loaded) tree means it's collapsed/unloaded — the helpers return the\n// tree UNCHANGED in that case (caller treats it as \"nothing visible to update\",\n// which is correct: a collapsed dir re-lists fresh when the user expands it).\n\n/** True when `parent` is the root (\"\") or a dir node that is actually present in\n * the loaded tree (so an insert/remove there would be visible). */\nfunction parentIsLoaded(nodes: FileTreeNode[], parent: string): boolean {\n if (parent === \"\") return true;\n const node = findNodeByPath(nodes, parent);\n return Boolean(node && node.kind === \"dir\" && node.children !== undefined);\n}\n\n/** Insert `child` under `parent` (immutably), keeping siblings sorted. A no-op\n * (returns the same array) when the parent isn't a loaded dir or the child\n * already exists. */\nfunction insertNode(nodes: FileTreeNode[], parent: string, child: FileTreeNode): FileTreeNode[] {\n if (parent === \"\") {\n if (nodes.some((n) => n.path === child.path)) return nodes;\n return sortNodes([...nodes, child]);\n }\n return nodes.map((node) => {\n if (node.path === parent) {\n if (node.kind !== \"dir\" || node.children === undefined) return node;\n if (node.children.some((n) => n.path === child.path)) return node;\n return { ...node, children: sortNodes([...node.children, child]) };\n }\n if (node.kind === \"dir\" && node.children && parent.startsWith(`${node.path}/`)) {\n return { ...node, children: insertNode(node.children, parent, child) };\n }\n return node;\n });\n}\n\n/** Remove the node at `path` (immutably). No-op when it isn't in the tree. */\nfunction removeNode(nodes: FileTreeNode[], path: string): FileTreeNode[] {\n const parent = parentOf(path);\n if (parent === \"\") {\n if (!nodes.some((n) => n.path === path)) return nodes;\n return nodes.filter((n) => n.path !== path);\n }\n return nodes.map((node) => {\n if (node.path === parent) {\n if (node.kind !== \"dir\" || node.children === undefined) return node;\n return { ...node, children: node.children.filter((n) => n.path !== path) };\n }\n if (node.kind === \"dir\" && node.children && parent.startsWith(`${node.path}/`)) {\n return { ...node, children: removeNode(node.children, path) };\n }\n return node;\n });\n}\n\n/** Reconcile a freshly-listed depth-1 set of children against the CURRENT nodes\n * at the same level, PRESERVING expansion: an existing dir keeps its already-\n * loaded `children` (so its expanded subtree survives), new entries are added,\n * and entries the server no longer returns are dropped. This is the in-place\n * merge a root (\"\") reconcile needs — a blind replace would collapse every\n * expanded top-level dir back to the unexpanded marker (the reported bug). */\n/** Shallow node equality for identity preservation across a reconcile — same\n * path/kind/name/size/status (children handled separately by the caller). */\nfunction sameNodeShallow(a: FileTreeNode, b: FileTreeNode): boolean {\n return (\n a.path === b.path &&\n a.kind === b.kind &&\n a.name === b.name &&\n (a.size ?? null) === (b.size ?? null) &&\n (a.status ?? undefined) === (b.status ?? undefined) &&\n (a.truncated ?? false) === (b.truncated ?? false)\n );\n}\n\nfunction mergeChildren(current: FileTreeNode[], listed: FileTreeNode[]): FileTreeNode[] {\n const byPath = new Map(current.map((n) => [n.path, n] as const));\n const merged = listed.map((next) => {\n const existing = byPath.get(next.path);\n if (!existing) return next; // brand-new entry\n // Keep an already-expanded dir's loaded children; otherwise take the listing's\n // marker (undefined = unexpanded). Carry forward size/status from the listing.\n if (existing.kind === \"dir\" && next.kind === \"dir\" && existing.children !== undefined) {\n const candidate: FileTreeNode = { ...next, children: existing.children };\n // Preserve identity when nothing observable changed — no remount (§12-D1).\n return sameNodeShallow(existing, candidate) ? existing : candidate;\n }\n // A file or an unexpanded dir: preserve identity when the node is unchanged.\n if (existing.children === next.children && sameNodeShallow(existing, next)) {\n return existing;\n }\n return next;\n });\n return sortNodes(merged);\n}\n\n/** Root-level (\"\") reconcile: merge a fresh depth-1 listing into the root list\n * without collapsing expanded top-level dirs. */\nfunction mergeRootChildren(nodes: FileTreeNode[], listed: FileTreeNode[]): FileTreeNode[] {\n return mergeChildren(nodes, listed);\n}\n\n/** Re-path a subtree rooted at `node` from `fromPrefix` to `toPrefix` (so a moved\n * dir's descendants keep correct paths without a re-list). */\nfunction repathNode(node: FileTreeNode, fromPrefix: string, toPrefix: string): FileTreeNode {\n const newPath = toPrefix + node.path.slice(fromPrefix.length);\n const next: FileTreeNode = { ...node, path: newPath, name: leafOf(newPath) };\n if (node.children) next.children = node.children.map((c) => repathNode(c, fromPrefix, toPrefix));\n return next;\n}\n\nfunction decodeBase64Bytes(value: string): Uint8Array {\n const binary = atob(value);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n return bytes;\n}\n\nfunction decodeBase64Text(value: string): string {\n return new TextDecoder().decode(decodeBase64Bytes(value));\n}\n\nfunction encodeBase64Bytes(bytes: Uint8Array): string {\n let binary = \"\";\n const chunkSize = 0x8000;\n for (let offset = 0; offset < bytes.length; offset += chunkSize) {\n binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));\n }\n return btoa(binary);\n}\n\nasync function assertCapturedHash(bytes: Uint8Array, expected: string | null): Promise<void> {\n if (!expected) throw new Error(\"Captured file is missing its integrity hash.\");\n if (!globalThis.crypto?.subtle) {\n throw new Error(\"Captured file integrity verification is unavailable in this browser.\");\n }\n const buffer = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(buffer).set(bytes);\n const digest = new Uint8Array(await globalThis.crypto.subtle.digest(\"SHA-256\", buffer));\n const actual = [...digest].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n if (actual !== expected) throw new Error(\"Captured file content failed its integrity check.\");\n}\n\n/**\n * Project the FileSystem service into a lazy-loaded Pierre tree. The initial\n * list pulls one level (depth 1); `expand(path)` lists a directory's immediate\n * children on demand (the fast lazy-tree UX). A git-status overlay tints\n * modified files. Auto-refreshes when an `fs.changed` / `git.changed` event\n * arrives on the live log.\n */\nexport function useSandboxFiles(\n sessionId: string | null | undefined,\n options: UseSandboxFilesOptions = {},\n): UseSandboxFilesResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const rootPath = options.rootPath ?? \"\";\n const capture = options.capture ?? null;\n const isLive = options.liveness === \"warm\" || options.liveness === \"draining\";\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${rootPath}`;\n\n const [tree, setTree] = useState<FileTreeNode[]>([]);\n // A ref mirror of the current tree — lets the optimistic path snapshot the\n // pre-op tree WITHOUT relying on a `setTree` updater (which StrictMode invokes\n // twice, corrupting an in-closure snapshot). Kept in sync on every set below\n // and in a layout effect for any path that sets `tree` directly.\n const treeRef = useRef<FileTreeNode[]>([]);\n const [loading, setLoading] = useState(false);\n const [expandingPaths, setExpandingPaths] = useState<Set<string>>(new Set());\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Which source the tree currently reflects — \"capture\" (cold paint) or \"live\".\n const [source, setSource] = useState<\"live\" | \"capture\" | null>(null);\n const sourceRef = useRef<\"live\" | \"capture\" | null>(null);\n const statusRef = useRef<Map<string, FileTreeStatus>>(new Map());\n // Async reads, event cursors, and debounced work are scoped to the hook's\n // workspace/session/root identity. These refs are reset/fenced at that boundary.\n const refreshGenerationRef = useRef(0);\n const refreshAbortRef = useRef<AbortController | null>(null);\n const identityAbortRef = useRef(new AbortController());\n const identityGenerationRef = useRef(0);\n const lastSeqRef = useRef(0);\n const pendingParentsRef = useRef<Set<string>>(new Set());\n const pendingGitRef = useRef(false);\n const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // ── Self-emitted fs.changed de-dupe ───────────────────────────────────────\n // Every one of OUR mutations emits an `fs.changed` event back on the live log\n // (source:\"write\"). The old flow auto-refreshed on EVERY fs.changed, so each\n // edit self-triggered a full root collapse-reload (~5s, lost expansion). We now\n // (a) ignore fs.changed whose `source === \"write\"` (our own control-plane ops),\n // and (b) belt-and-braces, track the revisions WE caused so even a watch-sourced\n // echo of our own write is suppressed. Only EXTERNAL changes (the agent writing,\n // source:\"agent\"/\"watch\" we didn't cause) drive a targeted reconcile.\n const ownRevisionsRef = useRef<Set<number>>(new Set());\n const onMutationError = options.onMutationError;\n\n // Keep the ref mirror current for the optimistic snapshot path.\n treeRef.current = tree;\n\n // Overlay the git-status tint onto the tree. IDENTITY-PRESERVING: a node whose\n // tint and children are unchanged is returned by reference (not rebuilt), so a\n // re-tint or a cold→warm reconcile does NOT remount unchanged rows — the\n // no-flicker constraint. An empty overlay is still\n // meaningful: it strips stale tints after the repository becomes clean.\n const applyStatus = useCallback((nodes: FileTreeNode[]): FileTreeNode[] => {\n const overlay = statusRef.current;\n const walk = (list: FileTreeNode[]): FileTreeNode[] => {\n let changed = false;\n const out = list.map((node) => {\n const nextChildren = node.children ? walk(node.children) : node.children;\n const wantStatus = node.kind === \"file\" ? overlay.get(node.path) : undefined;\n const childrenChanged = nextChildren !== node.children;\n const statusChanged = (node.status ?? undefined) !== (wantStatus ?? undefined);\n if (!childrenChanged && !statusChanged) return node;\n changed = true;\n const next: FileTreeNode = {\n ...node,\n ...(node.children ? { children: nextChildren } : {}),\n };\n if (wantStatus) next.status = wantStatus;\n else delete next.status;\n return next;\n });\n return changed ? out : list;\n };\n return walk(nodes);\n }, []);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n refreshAbortRef.current?.abort();\n const refreshAbort = new AbortController();\n refreshAbortRef.current = refreshAbort;\n const generation = (refreshGenerationRef.current += 1);\n setLoading(true);\n setError(null);\n try {\n // Pull the git-status overlay first (best-effort — a non-repo box just\n // returns isRepo:false), then the tree, so the first paint is tinted.\n try {\n const status = await client.gitStatus(\n workspaceId,\n sessionId,\n { path: rootPath },\n { signal: refreshAbort.signal },\n );\n if (refreshGenerationRef.current !== generation) return;\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of status.files) {\n const code = file.worktree ?? file.index;\n const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n } catch {\n if (refreshGenerationRef.current !== generation) return;\n statusRef.current = new Map();\n }\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path: rootPath, depth: 1 },\n { signal: refreshAbort.signal },\n );\n if (refreshGenerationRef.current !== generation) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n // Merge rather than replace so an explicit refresh / cold→warm re-list folds\n // in new entries WITHOUT collapsing the dirs the user already expanded (and,\n // via the identity-preserving merge, WITHOUT remounting unchanged rows — the\n // no-flicker cold→warm reconcile). On a first (empty) load this is a plain set.\n setTree((prev) =>\n applyStatus(prev.length === 0 ? children : mergeRootChildren(prev, children)),\n );\n // Live data is now serving — flip the source off the capture snapshot.\n sourceRef.current = \"live\";\n setSource(\"live\");\n } catch (cause) {\n if (refreshGenerationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (refreshGenerationRef.current === generation) setLoading(false);\n if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, rootPath, applyStatus]);\n\n // Seed (or re-seed) the tree from a turn-end capture — the COLD/offline paint,\n // zero Channel-A calls. The tree index is workspace-relative (`treeIndex`); the\n // tint overlay comes from the capture's changed `files`. Uses the SAME merge as\n // the live reconcile so a re-seed (a newer capture arriving cold) patches deltas\n // in place instead of remounting the tree (§12-D1).\n const seedFromCapture = useCallback(\n (manifest: WorkspaceCaptureManifest) => {\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of manifest.files) {\n if (file.deleted) continue;\n const mapped = GIT_STATUS_TO_TREE[file.status];\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n // The capture tree is a complete bounded index. Preserve an explicit empty\n // `children: []` as an actually empty directory; the live depth-1 mapper\n // intentionally treats that shape as an unexpanded boundary.\n const children = (manifest.treeIndex.children ?? []).map((node) => fsNodeToTree(node, true));\n setTree((prev) =>\n applyStatus(prev.length === 0 ? children : mergeRootChildren(prev, children)),\n );\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n setError(null);\n },\n [applyStatus],\n );\n\n const expand = useCallback(\n async (path: string) => {\n if (!sessionId) return;\n // Capture browsing is a durable server-side read surface. Never turn a\n // folder click into a Channel-A list while cold; truncated residue already\n // renders a truthful \"contents on machine\" row.\n if (source === \"capture\" && capture) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n // Mark this node as expanding so the FileBrowser can render a spinner while\n // the (often 2-3s) Channel-A fs/list is in flight — the tree never looks\n // frozen on a click.\n setExpandingPaths((prev) => {\n const next = new Set(prev);\n next.add(path);\n return next;\n });\n try {\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path, depth: 1 },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n setTree((prev) => applyStatus(replaceChildren(prev, path, children)));\n } catch (cause) {\n if (identityGenerationRef.current !== identityGeneration) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (identityGenerationRef.current === identityGeneration) {\n setExpandingPaths((prev) => {\n if (!prev.has(path)) return prev;\n const next = new Set(prev);\n next.delete(path);\n return next;\n });\n }\n }\n },\n [client, workspaceId, sessionId, capture, source, applyStatus],\n );\n\n const readFile = useCallback(\n async (path: string, requestOptions: OpenGeniRequestOptions = {}) => {\n if (!sessionId) throw new Error(\"no session\");\n const identitySignal = identityAbortRef.current.signal;\n const signal = requestOptions.signal\n ? AbortSignal.any([identitySignal, requestOptions.signal])\n : identitySignal;\n const captured = capture?.files.find((file) => file.path === path && !file.deleted);\n if (source === \"capture\" && capture) {\n if (!captured) {\n throw new CapturedFileUnavailableError(path, \"not-captured\");\n }\n // A passive review of a turn-touched file must stay entirely server-side:\n // mint/read its captured after-image, verify the content hash, and never\n // wake the sandbox. A URL may expire between mint and GET, so retry once\n // through the authenticated API to obtain a fresh scoped URL.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const result = await client.getWorkspaceCaptureFile(\n workspaceId,\n sessionId,\n path,\n capture.revision,\n { signal },\n );\n if (result.path !== path || result.revision !== capture.revision) {\n throw new Error(\n \"Captured file identity did not match the requested workspace revision.\",\n );\n }\n if (\n result.hash !== captured.hash ||\n result.sizeBytes !== captured.sizeBytes ||\n result.isBinary !== captured.isBinary ||\n result.tooLarge !== captured.tooLarge\n ) {\n throw new Error(\"Captured file metadata did not match its manifest.\");\n }\n if (result.tooLarge) {\n throw new CapturedFileUnavailableError(path, \"too-large\");\n }\n\n let bytes: Uint8Array;\n let encoding: \"utf8\" | \"base64\";\n let content: string;\n if (result.content !== null && result.encoding !== null) {\n encoding = result.encoding;\n content = result.content;\n bytes =\n encoding === \"base64\"\n ? decodeBase64Bytes(content)\n : new TextEncoder().encode(content);\n } else if (result.contentUrl) {\n const response = await fetch(result.contentUrl.url, {\n credentials: \"omit\",\n cache: \"no-store\",\n referrerPolicy: \"no-referrer\",\n signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),\n }).catch(() => null);\n if (!response?.ok) {\n if (\n attempt === 0 &&\n (response === null || response.status === 401 || response.status === 403)\n ) {\n continue;\n }\n throw new Error(\"Captured file download failed.\");\n }\n bytes = new Uint8Array(await response.arrayBuffer());\n encoding = result.isBinary ? \"base64\" : \"utf8\";\n content = result.isBinary ? encodeBase64Bytes(bytes) : new TextDecoder().decode(bytes);\n } else {\n throw new CapturedFileUnavailableError(path, \"content-missing\");\n }\n\n if (bytes.byteLength !== result.sizeBytes) {\n throw new Error(\"Captured file size did not match its manifest.\");\n }\n await assertCapturedHash(bytes, result.hash);\n return {\n path,\n encoding,\n content,\n sizeBytes: result.sizeBytes,\n truncated: false,\n isBinary: result.isBinary,\n revision: result.revision,\n };\n }\n throw new Error(\"Captured file download failed after refreshing its URL.\");\n }\n return await client.fsRead(workspaceId, sessionId, { path }, { signal });\n },\n [client, workspaceId, sessionId, capture, source],\n );\n\n // TARGETED reconcile of a single directory — re-list ONE parent at depth 1 and\n // splice its children in place via `replaceChildren`, preserving the rest of the\n // tree's expansion. NEVER falls back to a root refresh (that's the collapse).\n // Used to (a) reconcile an optimistic insert against the server's real\n // size/mtime, and (b) fold an EXTERNAL (agent) change into the tree. A reconcile\n // of a parent that isn't loaded (collapsed/unmounted) is a no-op — there's\n // nothing visible to update, and it re-lists fresh when the user expands it.\n const reconcilePath = useCallback(\n async (path: string) => {\n if (!sessionId) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n // Skip parents that aren't currently loaded/expanded in the tree (nothing\n // visible to update; they re-list fresh on the next expand).\n if (!parentIsLoaded(treeRef.current, path)) return;\n try {\n const listed = await client.fsList(\n workspaceId,\n sessionId,\n { path, depth: 1 },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));\n if (path === \"\") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));\n else\n setTree((prev) => {\n const existing = findNodeByPath(prev, path);\n const merged = existing?.children\n ? mergeChildren(existing.children, children)\n : children;\n return applyStatus(replaceChildren(prev, path, merged));\n });\n } catch {\n // A failed reconcile is non-fatal: the optimistic state stands. We never\n // root-refresh here (that would collapse the tree the user is working in).\n }\n },\n [client, workspaceId, sessionId, applyStatus],\n );\n\n // Run a Channel-A op behind an OPTIMISTIC tree edit. `apply` splices the change\n // in immediately (preserving expansion/selection); the op runs in the\n // background; on failure we revert to the pre-op snapshot and surface a toast.\n // On success we keep the optimistic state and (optionally) reconcile the\n // affected parent(s) to pick up the server's real size/revision — NEVER a full\n // refresh. The returned promise resolves/rejects with the op so callers (the\n // editor save, inline rename) can still await it.\n const runOptimistic = useCallback(\n async <T>(\n opName: string,\n apply: (nodes: FileTreeNode[]) => FileTreeNode[],\n op: () => Promise<T>,\n reconcileParents: string[],\n ): Promise<T> => {\n const identityGeneration = identityGenerationRef.current;\n // Snapshot the pre-op tree from the ref (StrictMode-safe — see treeRef).\n const snapshot = treeRef.current;\n setError(null);\n setTree(applyStatus(apply(snapshot)));\n try {\n const res = await op();\n if (identityGenerationRef.current !== identityGeneration) return res;\n // Remember the revision WE caused so the matching fs.changed echo is\n // ignored by the event effect (no self-triggered refresh).\n if (res && typeof res === \"object\" && \"revision\" in res) {\n const rev = (res as { revision?: unknown }).revision;\n if (typeof rev === \"number\") ownRevisionsRef.current.add(rev);\n }\n // Reconcile loaded parents to fold the server's real metadata. Sequential\n // and best-effort — purely cosmetic over the already-correct optimistic UI.\n for (const parent of reconcileParents) {\n if (identityGenerationRef.current !== identityGeneration) return res;\n await reconcilePath(parent);\n }\n return res;\n } catch (cause) {\n const err = cause instanceof Error ? cause : new Error(String(cause));\n if (identityGenerationRef.current !== identityGeneration) throw err;\n // Revert the optimistic edit to the exact pre-op tree.\n setTree(applyStatus(snapshot));\n // An expected-content conflict belongs to the editor's explicit\n // Reload/Overwrite decision surface. Treating it as a broken file tree\n // would also raise a duplicate host toast and leave a stale global error\n // after the user successfully resolves the conflict.\n if (err instanceof FileWriteConflictError) throw err;\n setError(err);\n onMutationError?.(err, opName);\n throw err;\n }\n },\n [applyStatus, reconcilePath, onMutationError],\n );\n\n const writeFile = useCallback(\n async (\n path: string,\n content: string,\n writeOptions: SandboxWriteFileOptions = {},\n ): Promise<FsWriteResponse> => {\n if (!sessionId) throw new Error(\"no session\");\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n const parent = parentOf(path);\n // Splice a new file node in immediately ONLY when the path doesn't already\n // exist in a loaded parent (an editor SAVE to an existing file mutates no\n // tree shape — just content — so it needs no optimistic node, no reconcile).\n const exists = Boolean(findNodeByPath(tree, path));\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"file\", size: content.length };\n return await runOptimistic(\n \"write\",\n (nodes) => (exists ? nodes : insertNode(nodes, parent, node)),\n async () => {\n if (!writeOptions.force && writeOptions.expectedContent !== undefined) {\n const live = await client.fsRead(\n workspaceId,\n sessionId,\n { path },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) {\n throw new Error(\"File save cancelled because the workspace changed.\");\n }\n const liveContent =\n live.encoding === \"base64\" ? decodeBase64Text(live.content) : live.content;\n if (live.truncated || live.isBinary || liveContent !== writeOptions.expectedContent) {\n throw new FileWriteConflictError(path, writeOptions.expectedContent, liveContent);\n }\n }\n if (identityGenerationRef.current !== identityGeneration) {\n throw new Error(\"File save cancelled because the workspace changed.\");\n }\n return await client.fsWrite(workspaceId, sessionId, {\n path,\n content,\n overwrite: true,\n });\n },\n exists ? [] : [parent],\n );\n },\n [client, workspaceId, sessionId, tree, runOptimistic],\n );\n\n const createFile = useCallback(\n async (path: string): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const parent = parentOf(path);\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"file\", size: 0 };\n await runOptimistic(\n \"create file\",\n (nodes) => insertNode(nodes, parent, node),\n () => client.fsWrite(workspaceId, sessionId, { path, content: \"\", overwrite: false }),\n [parent],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const createDir = useCallback(\n async (path: string): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const parent = parentOf(path);\n // A freshly-created dir is empty + expanded: children:[] (not undefined, so\n // it doesn't show the lazy-expand marker over a dir we KNOW is empty).\n const node: FileTreeNode = { path, name: leafOf(path), kind: \"dir\", children: [] };\n await runOptimistic(\n \"create folder\",\n (nodes) => insertNode(nodes, parent, node),\n () => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),\n [parent],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const deleteEntry = useCallback(\n async (path: string, recursive = false): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n await runOptimistic(\n \"delete\",\n (nodes) => removeNode(nodes, path),\n () => client.fsDelete(workspaceId, sessionId, { path, recursive }),\n [parentOf(path)],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n const moveEntry = useCallback(\n async (path: string, newPath: string, opts?: { overwrite?: boolean }): Promise<void> => {\n if (!sessionId) throw new Error(\"no session\");\n const from = parentOf(path);\n const to = parentOf(newPath);\n await runOptimistic(\n \"move\",\n (nodes) => {\n const moving = findNodeByPath(nodes, path);\n if (!moving) return nodes; // not loaded — let the reconcile pick it up\n // Re-path the moved subtree, drop it from its old parent, splice into new.\n const moved = repathNode(moving, path, newPath);\n return insertNode(removeNode(nodes, path), to, moved);\n },\n () =>\n client.fsMove(workspaceId, sessionId, {\n path,\n newPath,\n overwrite: opts?.overwrite ?? false,\n }),\n to === from ? [from] : [from, to],\n );\n },\n [client, workspaceId, sessionId, runOptimistic],\n );\n\n // Initial paint + reset on identity change. Source selection:\n // • any box WITH a capture → paint instantly from the durable capture index;\n // a warm/draining box then reconciles live in place.\n // • if that live reconciliation fails, keep the capture visible and read-only.\n // • cold box with NO capture → best-effort live list (status quo — never worse).\n // Key the seed on the capture's REVISION (a primitive), not the manifest object\n // — a consumer passing a fresh object each render (or a new revision) must not\n // spin the effect. The latest manifest is read from a ref at run time.\n const captureRef = useRef<WorkspaceCaptureManifest | null>(capture);\n captureRef.current = capture;\n const captureRevision = capture?.revision ?? null;\n const previousIdentityRef = useRef(identityKey);\n useEffect(() => {\n // Any source-selection change supersedes an older root refresh. A true data\n // identity change additionally fences all other async tree work and resets\n // event/debounce state before the event-folding effect runs.\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged || !enabled) {\n identityAbortRef.current.abort();\n identityAbortRef.current = new AbortController();\n identityGenerationRef.current += 1;\n lastSeqRef.current = 0;\n pendingParentsRef.current = new Set();\n pendingGitRef.current = false;\n if (debounceRef.current) {\n clearTimeout(debounceRef.current);\n debounceRef.current = null;\n }\n ownRevisionsRef.current = new Set();\n statusRef.current = new Map();\n treeRef.current = [];\n setTree([]);\n setStateIdentity(identityKey);\n setExpandingPaths(new Set());\n sourceRef.current = null;\n setSource(null);\n setLoading(false);\n setError(null);\n }\n if (!enabled) {\n return;\n }\n const currentCapture = captureRevision !== null ? captureRef.current : null;\n if (currentCapture && (identityChanged || !isLive || sourceRef.current !== \"live\")) {\n seedFromCapture(currentCapture);\n }\n if (isLive || !currentCapture) {\n void refresh();\n }\n return () => {\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n };\n }, [enabled, isLive, captureRevision, identityKey, refresh, seedFromCapture]);\n\n // Re-pull JUST the git-status overlay and re-tint the existing tree in place —\n // no fs re-list, no collapse. This is all a `git.changed` (commit/stage/checkout)\n // needs: the tree SHAPE is unchanged, only the tints move.\n const refreshGitOverlay = useCallback(async () => {\n if (!sessionId) return;\n const identityGeneration = identityGenerationRef.current;\n const identitySignal = identityAbortRef.current.signal;\n try {\n const status = await client.gitStatus(\n workspaceId,\n sessionId,\n { path: rootPath },\n { signal: identitySignal },\n );\n if (identityGenerationRef.current !== identityGeneration) return;\n const overlay = new Map<string, FileTreeStatus>();\n for (const file of status.files) {\n const code = file.worktree ?? file.index;\n const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;\n if (mapped) overlay.set(file.path, mapped);\n }\n statusRef.current = overlay;\n setTree((prev) => applyStatus(prev));\n } catch {\n /* a non-repo box has no overlay — leave the tree untinted */\n }\n }, [client, workspaceId, sessionId, rootPath, applyStatus]);\n\n // Auto-reconcile on fs/git change notifications — TARGETED, never a root\n // collapse-reload, and de-duped against our OWN mutations.\n //\n // • Our own ops (`source:\"write\"`, or a revision WE caused) are IGNORED —\n // the optimistic edit already reflects them, so a refresh here would be the\n // pointless 5s collapse the user reported.\n // • An EXTERNAL fs.changed (the agent writing files: `source:\"agent\"`/`watch`)\n // reconciles ONLY the affected parent directories (in place, expansion\n // preserved). Bursts are debounced into a single reconcile pass.\n // • A git.changed just re-tints (refreshes the status overlay) — no fs re-list.\n const events = options.events;\n\n useEffect(() => {\n if (!enabled || !events) return;\n let sawNew = false;\n for (const event of events) {\n if (event.sequence <= lastSeqRef.current) continue;\n if (event.type === \"fs.changed\") {\n sawNew = true;\n const payload = event.payload as FsChangedPayload | null;\n if (!payload || typeof payload !== \"object\") continue;\n // Suppress our own writes: the optimistic tree already shows them.\n if (payload.source === \"write\") continue;\n if (typeof payload.revision === \"number\" && ownRevisionsRef.current.has(payload.revision))\n continue;\n for (const change of payload.changes ?? []) {\n pendingParentsRef.current.add(parentOf(change.path));\n if (change.oldPath) pendingParentsRef.current.add(parentOf(change.oldPath));\n }\n } else if (event.type === \"git.changed\") {\n sawNew = true;\n const payload = event.payload as GitChangedPayload | null;\n // A git.changed our own write triggered (commit/stage from the agent is\n // external; a checkout we caused isn't — but we don't cause git ops here,\n // so any git.changed is external) → re-tint.\n if (\n payload &&\n typeof payload === \"object\" &&\n typeof payload.revision === \"number\" &&\n ownRevisionsRef.current.has(payload.revision)\n )\n continue;\n pendingGitRef.current = true;\n }\n }\n // Advance the high-water mark past everything we've folded.\n for (const event of events)\n if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;\n if (!sawNew) return;\n\n if (debounceRef.current) clearTimeout(debounceRef.current);\n const identityGeneration = identityGenerationRef.current;\n debounceRef.current = setTimeout(() => {\n debounceRef.current = null;\n if (identityGenerationRef.current !== identityGeneration) return;\n const parents = pendingParentsRef.current;\n pendingParentsRef.current = new Set();\n const wantGit = pendingGitRef.current;\n pendingGitRef.current = false;\n // Reconcile the changed directories (in place). A git change re-tints first\n // so the freshly-listed nodes get the correct overlay.\n void (async () => {\n if (wantGit) await refreshGitOverlay();\n if (identityGenerationRef.current !== identityGeneration) return;\n for (const parent of parents) {\n if (identityGenerationRef.current !== identityGeneration) return;\n await reconcilePath(parent);\n }\n })();\n }, 150);\n }, [enabled, events, reconcilePath, refreshGitOverlay]);\n\n useEffect(\n () => () => {\n refreshAbortRef.current?.abort();\n identityAbortRef.current.abort();\n identityGenerationRef.current += 1;\n if (debounceRef.current) clearTimeout(debounceRef.current);\n },\n [],\n );\n\n const identityMatches = enabled && stateIdentity === identityKey;\n return {\n tree: identityMatches ? tree : [],\n expand,\n expandingPaths: identityMatches ? expandingPaths : new Set<string>(),\n readFile,\n writeFile,\n createFile,\n createDir,\n deleteEntry,\n moveEntry,\n refresh,\n source: identityMatches ? source : null,\n capturedAt: identityMatches && source === \"capture\" ? (capture?.capturedAt ?? null) : null,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n };\n}\n","import type {\n GitFileDiff,\n SessionEvent,\n WorkspaceCaptureManifest,\n WorkspaceCaptureRepo,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\nexport type UseSandboxGitOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — drives auto-refresh\n * on `git.changed`. */\n events?: SessionEvent[] | undefined;\n /** Repo root within the workspace (multi-repo). Default: workspace root. */\n repoPath?: string | undefined;\n /** All repository roots in the workspace. When supplied, status and diff are\n * aggregated into one workspace-wide result and paths are workspace-relative. */\n repoPaths?: readonly string[] | undefined;\n /** Diff the staged index vs HEAD (`--cached`) instead of the working tree. */\n staged?: boolean | undefined;\n /** Hold off the initial fetch. Default true. */\n enabled?: boolean | undefined;\n /** Lease liveness (\"warm\" | \"draining\" | \"cold\"). When NOT warm, the diff is\n * served from the capture (cold/offline) instead of a live `gitDiff` RPC. */\n liveness?: string | undefined;\n /** The latest turn-end capture (from `useWorkspaceCapture`). Seeds the diff\n * immediately; a warm box reconciles live and leaves this durable review\n * surface in place if the live request is temporarily unavailable. */\n capture?: WorkspaceCaptureManifest | null | undefined;\n};\n\n/** A Git diff qualified into workspace scope. `repoRoot` is present for the\n * workspace-wide path and lets review UI group real repositories without\n * guessing from the first path segment. */\nexport type SandboxGitFileDiff = GitFileDiff & { repoRoot?: string | undefined };\n\nexport type UseSandboxGitResult = {\n /** Working-tree (or staged) diff vs HEAD — the structured hunks the Pierre\n * diff view renders. */\n diff: SandboxGitFileDiff[];\n branch: string | null;\n /** Whether a repo is actually mounted (drives \"no repository\" vs \"no changes\"). */\n isRepo: boolean;\n ahead: number;\n behind: number;\n /** Number and workspace-relative roots of repositories represented here. */\n repoCount: number;\n repoRoots: string[];\n refresh: () => Promise<void>;\n /** Which source the diff is served from: the live box or the turn-end capture. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n loading: boolean;\n error: Error | null;\n};\n\n/** Normalize a repo root for matching (\"\" and \".\" both mean the workspace root). */\nfunction normalizeRoot(root: string): string {\n const normalized = root\n .replaceAll(\"\\\\\", \"/\")\n .replace(/^\\.\\/+/, \"\")\n .replace(/\\/+$/, \"\");\n return normalized === \".\" ? \"\" : normalized;\n}\n\nfunction qualifyPath(repoRoot: string, path: string): string {\n const root = normalizeRoot(repoRoot);\n return root ? `${root}/${path}` : path;\n}\n\nfunction qualifyDiff(repoRoot: string, file: GitFileDiff): SandboxGitFileDiff {\n const root = normalizeRoot(repoRoot);\n return {\n ...file,\n path: qualifyPath(root, file.path),\n oldPath: file.oldPath === null ? null : qualifyPath(root, file.oldPath),\n repoRoot: root,\n };\n}\n\nfunction uniqueRoots(roots: readonly string[]): string[] {\n return [...new Set(roots.map(normalizeRoot))].sort((a, b) => a.localeCompare(b));\n}\n\n/** Find the capture repo matching `repoPath` (default workspace root). */\nfunction repoForPath(\n manifest: WorkspaceCaptureManifest,\n repoPath: string,\n): WorkspaceCaptureRepo | null {\n const want = normalizeRoot(repoPath);\n const exact = manifest.repos.find((r) => normalizeRoot(r.root) === want);\n if (exact) return exact;\n // Root-scoped hook against a single-repo capture whose root isn't literally \"\":\n // fall back to the sole repo so the common single-repo case just works.\n if (want === \"\" && manifest.repos.length === 1) return manifest.repos[0] ?? null;\n return null;\n}\n\n/** Full diff equality for identity preservation. Hunk count/count summaries are\n * not sufficient: an edit can replace text without changing either shape. */\nfunction sameFileDiff(a: SandboxGitFileDiff, b: SandboxGitFileDiff): boolean {\n if (\n a.path !== b.path ||\n a.oldPath !== b.oldPath ||\n a.repoRoot !== b.repoRoot ||\n a.status !== b.status ||\n a.isBinary !== b.isBinary ||\n a.isImage !== b.isImage ||\n a.additions !== b.additions ||\n a.deletions !== b.deletions ||\n a.truncated !== b.truncated ||\n a.hunks.length !== b.hunks.length\n )\n return false;\n\n return a.hunks.every((hunk, hunkIndex) => {\n const other = b.hunks[hunkIndex];\n if (\n !other ||\n hunk.oldStart !== other.oldStart ||\n hunk.oldLines !== other.oldLines ||\n hunk.newStart !== other.newStart ||\n hunk.newLines !== other.newLines ||\n hunk.header !== other.header ||\n hunk.lines.length !== other.lines.length\n )\n return false;\n return hunk.lines.every((line, lineIndex) => {\n const otherLine = other.lines[lineIndex];\n return (\n otherLine !== undefined &&\n line.type === otherLine.type &&\n line.oldNo === otherLine.oldNo &&\n line.newNo === otherLine.newNo &&\n line.text === otherLine.text\n );\n });\n });\n}\n\n/** Merge a freshly-served diff over the current one, preserving the identity of\n * genuinely unchanged per-file entries so a cold→warm reconcile does NOT\n * remount unchanged file sections — no-flicker (§12-D1). */\nfunction mergeDiffs(\n current: SandboxGitFileDiff[],\n next: SandboxGitFileDiff[],\n): SandboxGitFileDiff[] {\n if (current.length === 0) return next;\n const byPath = new Map(current.map((d) => [d.path, d] as const));\n let changed = current.length !== next.length;\n const merged = next.map((file, index) => {\n const existing = byPath.get(file.path);\n if (existing && sameFileDiff(existing, file)) {\n if (current[index] !== existing) changed = true;\n return existing;\n }\n changed = true;\n return file;\n });\n return changed ? merged : current;\n}\n\n/**\n * Project the Git service into the Pierre diff data contract: structured\n * `GitFileDiff[]` (per-file hunks with per-line old/new numbers, rename\n * detection, binary flag, add/del counts) plus branch + ahead/behind. When the\n * box is warm the `git diff` runs in-box (API-direct); when it is cold/offline the\n * diff is served from the turn-end capture instead. Refreshes on\n * `git.changed`; reconciles live in place on the cold→warm transition.\n */\nexport function useSandboxGit(\n sessionId: string | null | undefined,\n options: UseSandboxGitOptions = {},\n): UseSandboxGitResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const repoPath = options.repoPath ?? \"\";\n const workspaceWide = options.repoPaths !== undefined;\n // Serialize the caller's array so inline values do not retrigger effects. An\n // explicitly empty advertised list falls back to the legacy repoPath probe.\n const repoPathsKey = uniqueRoots(\n options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths : [repoPath],\n ).join(\"\\u0000\");\n const repoPaths = useMemo(() => repoPathsKey.split(\"\\u0000\"), [repoPathsKey]);\n const staged = options.staged ?? false;\n const capture = options.capture ?? null;\n const isLive = options.liveness === \"warm\" || options.liveness === \"draining\";\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${repoPathsKey}\\u0000${workspaceWide}\\u0000${staged}`;\n\n const [diff, setDiff] = useState<SandboxGitFileDiff[]>([]);\n const [branch, setBranch] = useState<string | null>(null);\n const [isRepo, setIsRepo] = useState(false);\n const [ahead, setAhead] = useState(0);\n const [behind, setBehind] = useState(0);\n const [repoRoots, setRepoRoots] = useState<string[]>([]);\n const [source, setSource] = useState<\"live\" | \"capture\" | null>(null);\n const sourceRef = useRef<\"live\" | \"capture\" | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n // Live requests and event cursors are identity-scoped. The refs deliberately\n // survive renders, so they must be fenced/reset when that identity changes.\n const refreshGenerationRef = useRef(0);\n const refreshAbortRef = useRef<AbortController | null>(null);\n const lastChangeRef = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n refreshAbortRef.current?.abort();\n const refreshAbort = new AbortController();\n refreshAbortRef.current = refreshAbort;\n const generation = (refreshGenerationRef.current += 1);\n setLoading(true);\n setError(null);\n try {\n const statuses = await Promise.all(\n repoPaths.map(async (root) => ({\n root,\n status: await client.gitStatus(\n workspaceId,\n sessionId,\n { path: root },\n { signal: refreshAbort.signal },\n ),\n })),\n );\n if (refreshGenerationRef.current !== generation) return;\n const repositories = statuses.filter(({ status }) => status.isRepo);\n const roots = repositories.map(({ root }) => root);\n setRepoRoots(roots);\n setIsRepo(repositories.length > 0);\n setBranch(repositories.length === 1 ? (repositories[0]?.status.head ?? null) : null);\n setAhead(repositories.reduce((sum, { status }) => sum + status.ahead, 0));\n setBehind(repositories.reduce((sum, { status }) => sum + status.behind, 0));\n if (repositories.length === 0) {\n setDiff([]);\n sourceRef.current = \"live\";\n setSource(\"live\");\n return;\n }\n const results = await Promise.all(\n repositories.map(async ({ root }) => ({\n root,\n result: await client.gitDiff(\n workspaceId,\n sessionId,\n {\n path: root,\n staged,\n includeUntracked: !staged,\n },\n { signal: refreshAbort.signal },\n ),\n })),\n );\n if (refreshGenerationRef.current !== generation) return;\n // Merge in place so the cold→warm swap keeps unchanged file sections mounted.\n setDiff((prev) =>\n mergeDiffs(\n prev,\n results.flatMap(({ root, result }) =>\n workspaceWide ? result.files.map((file) => qualifyDiff(root, file)) : result.files,\n ),\n ),\n );\n sourceRef.current = \"live\";\n setSource(\"live\");\n } catch (cause) {\n if (refreshGenerationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (refreshGenerationRef.current === generation) setLoading(false);\n if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, repoPaths, staged, workspaceWide]);\n\n // Serve the diff from a capture (the cold/offline source). `staged` has no\n // meaning here: the capture records the combined turn-end change surface vs HEAD.\n const seedFromCapture = useCallback(\n (manifest: WorkspaceCaptureManifest) => {\n const repositories = workspaceWide\n ? manifest.repos\n : ([repoForPath(manifest, repoPath)].filter(Boolean) as WorkspaceCaptureRepo[]);\n if (repositories.length === 0) {\n setIsRepo(false);\n setBranch(null);\n setAhead(0);\n setBehind(0);\n setRepoRoots([]);\n setDiff((prev) => (prev.length === 0 ? prev : []));\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n return;\n }\n setIsRepo(true);\n setBranch(repositories.length === 1 ? (repositories[0]?.head ?? null) : null);\n setAhead(repositories.reduce((sum, repo) => sum + repo.ahead, 0));\n setBehind(repositories.reduce((sum, repo) => sum + repo.behind, 0));\n setRepoRoots(repositories.map((repo) => normalizeRoot(repo.root)));\n setDiff((prev) =>\n mergeDiffs(\n prev,\n repositories.flatMap((repo) =>\n workspaceWide ? repo.diff.map((file) => qualifyDiff(repo.root, file)) : repo.diff,\n ),\n ),\n );\n sourceRef.current = \"capture\";\n setSource(\"capture\");\n setError(null);\n },\n [repoPath, workspaceWide],\n );\n\n // Source selection on mount / liveness / capture-revision change. Key on the\n // capture REVISION (a primitive), not the manifest object, so a fresh object per\n // render can't spin the effect; the latest manifest is read from a ref.\n const captureRef = useRef<WorkspaceCaptureManifest | null>(capture);\n captureRef.current = capture;\n const captureRevision = capture?.revision ?? null;\n const previousIdentityRef = useRef(identityKey);\n useEffect(() => {\n // A liveness/capture source change supersedes any older live request. Only a\n // true data-identity change clears rendered state and the event high-water mark.\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n const identityChanged = previousIdentityRef.current !== identityKey;\n previousIdentityRef.current = identityKey;\n if (identityChanged || !enabled) {\n lastChangeRef.current = 0;\n setDiff([]);\n setStateIdentity(identityKey);\n setIsRepo(false);\n setBranch(null);\n setAhead(0);\n setBehind(0);\n setRepoRoots([]);\n sourceRef.current = null;\n setSource(null);\n setLoading(false);\n setError(null);\n }\n if (!enabled) {\n return;\n }\n const currentCapture = captureRevision !== null ? captureRef.current : null;\n if (currentCapture && (identityChanged || !isLive || sourceRef.current !== \"live\")) {\n seedFromCapture(currentCapture);\n }\n if (isLive || !currentCapture) {\n void refresh();\n }\n return () => {\n refreshAbortRef.current?.abort();\n refreshAbortRef.current = null;\n refreshGenerationRef.current += 1;\n };\n }, [enabled, isLive, captureRevision, identityKey, refresh, seedFromCapture]);\n\n // git.changed → re-fetch the LIVE diff. A git.changed only originates from a\n // live box, so this both keeps warm sessions fresh and folds a cold box that\n // just came up (unchanged from the pre-capture behavior).\n const events = options.events;\n useEffect(() => {\n if (!enabled || !events) return;\n let latest = lastChangeRef.current;\n for (const event of events) {\n if (event.type === \"git.changed\" && event.sequence > latest) {\n latest = event.sequence;\n }\n }\n if (latest > lastChangeRef.current) {\n lastChangeRef.current = latest;\n void refresh();\n }\n }, [enabled, events, refresh]);\n\n const identityMatches = enabled && stateIdentity === identityKey;\n const visibleRepoRoots = identityMatches ? repoRoots : [];\n return {\n diff: identityMatches ? diff : [],\n branch: identityMatches ? branch : null,\n isRepo: identityMatches && isRepo,\n ahead: identityMatches ? ahead : 0,\n behind: identityMatches ? behind : 0,\n repoCount: visibleRepoRoots.length,\n repoRoots: visibleRepoRoots,\n refresh,\n source: identityMatches ? source : null,\n capturedAt: identityMatches && source === \"capture\" ? (capture?.capturedAt ?? null) : null,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n };\n}\n","import type {\n GetWorkspaceCaptureResponse,\n SessionEvent,\n WorkspaceCaptureDegradedReason,\n WorkspaceCaptureManifest,\n WorkspaceRevisionCapturedPayload,\n WorkspaceRevisionDegradedPayload,\n} from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/** The announce event M1 emits at turn end (metadata only, never content). */\nconst REVISION_CAPTURED = \"workspace.revision.captured\";\nconst REVISION_DEGRADED = \"workspace.revision.degraded\";\nconst MANIFEST_FETCH_TIMEOUT_MS = 15_000;\n\ntype AvailableCaptureResponse = Extract<GetWorkspaceCaptureResponse, { available: true }>;\n\nconst CAPTURE_STATS_NUMBER_FIELDS = [\n \"repoCount\",\n \"fileCount\",\n \"additions\",\n \"deletions\",\n \"totalBytes\",\n \"tooLargeCount\",\n \"binaryCount\",\n \"treeEntryCount\",\n \"durationMs\",\n] as const;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Signed manifests bypass the authenticated JSON client's wire decoder. Treat\n * the blob as untrusted until its outer schema and exact revision identity match\n * the metadata response that minted the URL. The API performs the full contract\n * parse; this client-side boundary prevents a corrupt/mis-keyed response from\n * being presented as a different authoritative capture.\n */\nfunction captureManifestForResponse(\n value: unknown,\n response: AvailableCaptureResponse,\n): WorkspaceCaptureManifest {\n if (\n !isRecord(value) ||\n value.version !== 1 ||\n !Number.isSafeInteger(value.revision) ||\n typeof value.capturedAt !== \"string\" ||\n !(value.turnId === null || typeof value.turnId === \"string\") ||\n !Number.isSafeInteger(value.leaseEpoch) ||\n !isRecord(value.treeIndex) ||\n typeof value.treeTruncated !== \"boolean\" ||\n !Array.isArray(value.repos) ||\n !Array.isArray(value.files) ||\n !isRecord(value.stats)\n ) {\n throw new Error(\"Workspace capture manifest failed validation.\");\n }\n\n if (\n value.revision !== response.revision ||\n value.capturedAt !== response.capturedAt ||\n value.turnId !== response.turnId ||\n value.leaseEpoch !== response.leaseEpoch\n ) {\n throw new Error(\"Workspace capture manifest identity did not match its response.\");\n }\n\n for (const field of CAPTURE_STATS_NUMBER_FIELDS) {\n if (!Number.isSafeInteger(value.stats[field]) || value.stats[field] !== response.stats[field]) {\n throw new Error(\"Workspace capture manifest statistics did not match its response.\");\n }\n }\n if (\n typeof value.stats.treeTruncated !== \"boolean\" ||\n value.stats.treeTruncated !== response.stats.treeTruncated ||\n (value.stats.fingerprint ?? null) !== (response.stats.fingerprint ?? null) ||\n value.repos.length !== response.stats.repoCount ||\n value.files.length !== response.stats.fileCount ||\n value.treeTruncated !== response.stats.treeTruncated\n ) {\n throw new Error(\"Workspace capture manifest statistics did not match its response.\");\n }\n\n return value as WorkspaceCaptureManifest;\n}\n\nexport type UseWorkspaceCaptureOptions = ClientOverride & {\n /** Live event log (usually `useSessionEvents().events`) — a\n * `workspace.revision.captured` for a NEWER revision refreshes the manifest. */\n events?: SessionEvent[] | undefined;\n /** Hold off the mount fetch (e.g. the workbench panel is collapsed). Default true. */\n enabled?: boolean | undefined;\n};\n\nexport type UseWorkspaceCaptureResult = {\n /** The resolved manifest (tree index + per-repo diffs + file refs), or null when\n * no capture exists (falls back to the live/wake path — status quo). */\n capture: WorkspaceCaptureManifest | null;\n /** The loaded capture's monotonic revision, or null when unavailable. */\n revision: number | null;\n /** When the loaded capture was taken (ISO), for the \"as of <time>\" source badge. */\n capturedAt: string | null;\n /** Whether a capture is available at all (the `{available:false}` discriminator). */\n available: boolean;\n /** Why the newest durable revision is unavailable. null means no capture has\n * been attempted yet; a value means capture explicitly failed closed and\n * consumers must use the live box rather than trust an incomplete snapshot. */\n degradedReason: WorkspaceCaptureDegradedReason | null;\n /** The changed-file count from the capture's stats, resolved on the FIRST GET\n * (from the response's top-level `stats`, before any manifest-URL hop). null\n * until that first resolve; 0 when no capture exists. This is the pre-paint\n * \"changes exist?\" signal the dock uses to pick its default tab with no\n * embedder events-at-mount contract. */\n fileCount: number | null;\n /** A newer revision has been ANNOUNCED than the one currently loaded (a refresh\n * is in flight or pending). M5's source badge can show a subtle \"updating…\". */\n isStale: boolean;\n loading: boolean;\n error: Error | null;\n /** Force a re-fetch of the latest capture. */\n refresh: () => Promise<void>;\n};\n\n/**\n * The cold-paint data source: fetch the latest turn-end workspace capture with a\n * SINGLE api round-trip on mount (no machine, no Channel-A — this is the <200ms\n * first paint). The manifest is served inline in the common\n * case; a rare >2MB manifest comes back as a short-TTL signed URL we follow.\n *\n * Subscribes to the live event log: a `workspace.revision.captured` for a revision\n * newer than the one loaded triggers a background refresh (stale-while-revalidate).\n * `{available:false}` is a value, never a crash — consumers fall back to the\n * live/wake path exactly as before the capture feature existed.\n */\nexport function useWorkspaceCapture(\n sessionId: string | null | undefined,\n options: UseWorkspaceCaptureOptions = {},\n): UseWorkspaceCaptureResult {\n const { client, workspaceId } = useOpenGeni(options);\n const enabled = (options.enabled ?? true) && Boolean(sessionId);\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}`;\n\n const [capture, setCapture] = useState<WorkspaceCaptureManifest | null>(null);\n const [available, setAvailable] = useState(false);\n const [degradedReason, setDegradedReason] = useState<WorkspaceCaptureDegradedReason | null>(null);\n // Resolved from the FIRST GET's top-level stats (before any manifest-URL hop),\n // so the dock can pick its default tab as early as possible. null until then.\n const [fileCount, setFileCount] = useState<number | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n // The highest revision ANNOUNCED on the event log — compared against the loaded\n // manifest's revision to compute `isStale` and to gate refreshes.\n const [announcedRevision, setAnnouncedRevision] = useState<number | null>(null);\n // State updates are asynchronous, so the previous session's capture still\n // exists during the first render after an identity switch. Tag it and hide it\n // synchronously until the new identity's request owns the state.\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n\n // A generation counter fences a slow in-flight fetch against a newer one (or an\n // identity change) so a stale response can never overwrite fresher state.\n const generationRef = useRef(0);\n const requestAbortRef = useRef<AbortController | null>(null);\n // Event sequence numbers are scoped to a session. Keeping this cursor across an\n // identity switch would discard the new session's low-numbered announcements.\n const lastSeqRef = useRef(0);\n\n const refresh = useCallback(async () => {\n if (!sessionId) return;\n requestAbortRef.current?.abort();\n const requestAbort = new AbortController();\n requestAbortRef.current = requestAbort;\n const generation = (generationRef.current += 1);\n setStateIdentity(identityKey);\n setLoading(true);\n setError(null);\n try {\n // A signed URL can expire between mint and GET. Re-enter the authenticated\n // endpoint once to mint a fresh URL; never replay or log the old credential.\n for (let attempt = 0; attempt < 2; attempt += 1) {\n const res: GetWorkspaceCaptureResponse = await client.getWorkspaceCapture(\n workspaceId,\n sessionId,\n { signal: requestAbort.signal },\n );\n if (generationRef.current !== generation) return;\n if (!res.available) {\n setCapture(null);\n setAvailable(false);\n setFileCount(0);\n setDegradedReason(res.degradedReason ?? null);\n return;\n }\n setDegradedReason(null);\n // Resolve the changed-file count immediately from the response's stats — the\n // default-tab signal must not wait on a >2MB manifest-URL hop.\n setFileCount(res.stats.fileCount);\n // Exactly one of manifest / manifestUrl is non-null (M2 contract). The inline\n // manifest is the <200ms common case; a >2MB manifest is a signed URL hop.\n let manifestValue: unknown = res.manifest;\n if (!manifestValue && res.manifestUrl) {\n const response = await fetch(res.manifestUrl.url, {\n signal: AbortSignal.any([\n requestAbort.signal,\n AbortSignal.timeout(MANIFEST_FETCH_TIMEOUT_MS),\n ]),\n credentials: \"omit\",\n cache: \"no-store\",\n referrerPolicy: \"no-referrer\",\n }).catch(() => null);\n if (generationRef.current !== generation) return;\n if (!response?.ok) {\n if (\n attempt === 0 &&\n (response === null || response.status === 401 || response.status === 403)\n ) {\n continue;\n }\n throw new Error(\n `Workspace capture manifest download failed${response ? ` (${response.status})` : \"\"}.`,\n );\n }\n try {\n manifestValue = await response.json();\n } catch {\n throw new Error(\"Workspace capture manifest was not valid JSON.\");\n }\n if (generationRef.current !== generation) return;\n }\n if (!manifestValue) {\n throw new Error(\"Workspace capture response did not include a manifest.\");\n }\n const manifest = captureManifestForResponse(manifestValue, res);\n setCapture(manifest);\n setAvailable(true);\n // Fold the served revision into the announced high-water mark so a capture we\n // JUST loaded is never reported stale against an older announce.\n setAnnouncedRevision((prev) =>\n prev === null || manifest.revision > prev ? manifest.revision : prev,\n );\n return;\n }\n throw new Error(\"Workspace capture manifest download failed after refreshing its URL.\");\n } catch (cause) {\n if (generationRef.current !== generation) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n if (generationRef.current === generation) setLoading(false);\n if (requestAbortRef.current === requestAbort) requestAbortRef.current = null;\n }\n }, [client, workspaceId, sessionId, identityKey]);\n\n // Mount fetch + reset on identity change.\n useEffect(() => {\n // Invalidate a request started for the previous identity before clearing its\n // state. The cleanup also fences a response that arrives after unmount.\n requestAbortRef.current?.abort();\n requestAbortRef.current = null;\n generationRef.current += 1;\n lastSeqRef.current = 0;\n setCapture(null);\n setAvailable(false);\n setDegradedReason(null);\n setFileCount(null);\n setAnnouncedRevision(null);\n setError(null);\n if (!enabled) {\n setLoading(false);\n return;\n }\n void refresh();\n return () => {\n requestAbortRef.current?.abort();\n requestAbortRef.current = null;\n generationRef.current += 1;\n };\n }, [enabled, refresh]);\n\n // Fold `workspace.revision.captured` announcements. A revision NEWER than the one\n // loaded bumps the announced high-water mark (→ isStale) and refreshes in the\n // background. The event is announce-only (metadata) — we still re-fetch the\n // manifest, since the payload carries no content.\n const events = options.events;\n useEffect(() => {\n if (!enabled || !events) return;\n let newest: number | null = null;\n for (const event of events) {\n if (event.sequence <= lastSeqRef.current) continue;\n if (event.type === REVISION_CAPTURED) {\n const payload = event.payload as WorkspaceRevisionCapturedPayload | null;\n if (payload && typeof payload === \"object\" && typeof payload.revision === \"number\") {\n newest = newest === null ? payload.revision : Math.max(newest, payload.revision);\n }\n } else if (event.type === REVISION_DEGRADED) {\n const payload = event.payload as WorkspaceRevisionDegradedPayload | null;\n if (payload && typeof payload === \"object\" && typeof payload.revision === \"number\") {\n newest = newest === null ? payload.revision : Math.max(newest, payload.revision);\n }\n }\n }\n for (const event of events)\n if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;\n if (newest === null) return;\n setAnnouncedRevision((prev) => (prev === null || newest > prev ? newest : prev));\n }, [enabled, events]);\n\n // A newer announced revision than the loaded manifest → refresh in the background.\n const identityMatches = enabled && stateIdentity === identityKey;\n const visibleCapture = identityMatches ? capture : null;\n const visibleAnnouncedRevision = identityMatches ? announcedRevision : null;\n const loadedRevision = visibleCapture?.revision ?? null;\n useEffect(() => {\n if (!enabled) return;\n if (visibleAnnouncedRevision === null) return;\n if (loadedRevision !== null && visibleAnnouncedRevision <= loadedRevision) return;\n // The mount request is already resolving the latest revision. Do not abort\n // and replace that exact identity-equivalent read merely because the event\n // tail announced a revision before the response arrived. When the request\n // settles, its state update re-runs this effect and a genuinely newer\n // announcement still refreshes immediately.\n if (requestAbortRef.current) return;\n // A newer capture exists than the one we hold (or we hold none) — pull it.\n void refresh();\n }, [enabled, visibleAnnouncedRevision, loadedRevision, refresh]);\n\n const isStale =\n loadedRevision !== null &&\n visibleAnnouncedRevision !== null &&\n visibleAnnouncedRevision > loadedRevision;\n\n return {\n capture: visibleCapture,\n revision: loadedRevision,\n capturedAt: visibleCapture?.capturedAt ?? null,\n available: identityMatches && available,\n degradedReason: identityMatches ? degradedReason : null,\n fileCount: identityMatches ? fileCount : null,\n isStale,\n loading: identityMatches && loading,\n error: identityMatches ? error : null,\n refresh,\n };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\n\n/**\n * The wake-on-edit state machine. This hook owns the logic and the UI renders\n * its result. States are explicit and exhaustive:\n *\n * viewing-cold — cold snapshot open, no local edits (editable, cloud).\n * buffering — local edits buffered; the box is not warm and no warm is\n * yet in flight (the host has not attached).\n * warming — edits buffered; the box is actively coming up (the host\n * attached a viewer in response to `wantsWarm`).\n * flushing — the box is warm; the guarded write is in flight.\n * flushed — the buffer was written to the live box.\n * conflict — the live file diverged from the captured base between\n * capture and flush; NO write was issued (C2).\n * readonly-offline — a self-hosted machine that is offline; read-only, no wake.\n */\nexport type WorkspaceEditState =\n | \"viewing-cold\"\n | \"buffering\"\n | \"warming\"\n | \"flushing\"\n | \"flushed\"\n | \"conflict\"\n | \"readonly-offline\";\n\nexport type WorkspaceEditConflict = {\n path: string;\n /** The capture-served content the edit was based on. */\n base: string;\n /** The live content found on the box at flush time (what would be overwritten). */\n live: string;\n};\n\nexport type UseWorkspaceEditOptions = ClientOverride & {\n /** The file being edited (workspace-relative). null disables the machine. */\n path?: string | null | undefined;\n /** The capture-served content loaded into the editor — the flush base (C2). */\n baseContent?: string | null | undefined;\n /** `capabilities.liveness` — \"warm\" | \"draining\" | \"cold\". Drives the flush. */\n liveness?: string | undefined;\n /** True while the host is actively warming the box (attach in flight, not warm\n * yet) — lifts `buffering` → `warming`. The host sets this after acting on\n * `wantsWarm` (e.g. flipping `attachFiles` on `useSessionCapabilities`). */\n warming?: boolean | undefined;\n /** A self-hosted machine that is OFFLINE: read-only, no remote wake possible. */\n offline?: boolean | undefined;\n /** Called once when the first cold edit needs the box warmed — the host wires\n * this to its liveness primitive (flip `attachFiles`/`attachViewer`). */\n onWarmRequested?: (() => void) | undefined;\n};\n\nexport type UseWorkspaceEditResult = {\n state: WorkspaceEditState;\n /** The editor should be read-only (self-hosted offline). */\n readOnly: boolean;\n /** The current buffered content (null = no local edit yet). */\n buffer: string | null;\n /** The host should warm the box (flip its attach primitive). Latches true from\n * the first cold edit until the buffer flushes or is discarded. */\n wantsWarm: boolean;\n /** Record an edit from the editor (idempotent per identical content). */\n edit: (content: string) => void;\n /** The conflict detail when `state === \"conflict\"`. */\n conflict: WorkspaceEditConflict | null;\n /** Flush the buffer regardless of the base guard (the user chose \"overwrite\"\n * from the conflict bar). Writes last-writer-wins. */\n overwrite: () => Promise<void>;\n /** Drop the local buffer and return to viewing the snapshot. */\n discard: () => void;\n error: Error | null;\n};\n\nfunction isLive(liveness: string | undefined): boolean {\n return liveness === \"warm\" || liveness === \"draining\";\n}\n\n/**\n * Cold-editing without a spinner: the editor opens instantly from the capture,\n * the first keystroke buffers locally AND signals the host to warm the box, and on\n * warm the buffer flushes IF the live file still matches the captured base — else a\n * non-blocking conflict is surfaced and nothing is overwritten.\n *\n * The base guard compares the live content at flush time against `baseContent`\n * (the exact bytes the editor loaded). This is equivalent to the sha256 compare\n * the design specifies — M1 leaves `baseHash` null, so the loaded content IS the\n * authoritative base — and avoids async Web Crypto in the render path.\n */\nexport function useWorkspaceEdit(\n sessionId: string | null | undefined,\n options: UseWorkspaceEditOptions = {},\n): UseWorkspaceEditResult {\n const { client, workspaceId } = useOpenGeni(options);\n const path = options.path ?? null;\n const baseContent = options.baseContent ?? null;\n const live = isLive(options.liveness);\n const offline = options.offline === true;\n const identityKey = `${workspaceId}\\u0000${sessionId ?? \"\"}\\u0000${path ?? \"\"}`;\n\n const [buffer, setBuffer] = useState<string | null>(null);\n const [wantsWarm, setWantsWarm] = useState(false);\n const [flush, setFlush] = useState<\"idle\" | \"flushing\" | \"flushed\" | \"conflict\">(\"idle\");\n const [conflict, setConflict] = useState<WorkspaceEditConflict | null>(null);\n const [error, setError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState(identityKey);\n\n const onWarmRequested = options.onWarmRequested;\n const bufferRef = useRef<string | null>(null);\n bufferRef.current = buffer;\n // Guards a single in-flight flush against re-entry (liveness can re-render).\n const flushingRef = useRef(false);\n const identityGenerationRef = useRef(0);\n const flushAttemptRef = useRef(0);\n const readAbortRef = useRef<AbortController | null>(null);\n\n // Reset the machine when the edited file (or session) changes.\n useEffect(() => {\n identityGenerationRef.current += 1;\n flushAttemptRef.current += 1;\n readAbortRef.current?.abort();\n readAbortRef.current = null;\n setStateIdentity(identityKey);\n setBuffer(null);\n setWantsWarm(false);\n setFlush(\"idle\");\n setConflict(null);\n setError(null);\n flushingRef.current = false;\n return () => {\n identityGenerationRef.current += 1;\n flushAttemptRef.current += 1;\n readAbortRef.current?.abort();\n readAbortRef.current = null;\n };\n }, [identityKey]);\n\n const edit = useCallback(\n (content: string) => {\n if (stateIdentity !== identityKey) return;\n if (offline) return; // read-only — no buffering, no wake.\n setBuffer((prev) => (prev === content ? prev : content));\n // Any new edit invalidates a prior flushed/conflict resolution.\n setFlush((prev) => (prev === \"flushing\" ? prev : \"idle\"));\n setConflict(null);\n if (!live) {\n // First cold edit signals the host to warm the box (once).\n setWantsWarm((prev) => {\n if (!prev) onWarmRequested?.();\n return true;\n });\n }\n },\n [offline, live, onWarmRequested, stateIdentity, identityKey],\n );\n\n const doFlush = useCallback(\n async (force: boolean) => {\n if (stateIdentity !== identityKey) return;\n if (!sessionId || !path) return;\n const content = bufferRef.current;\n if (content === null) return;\n if (flushingRef.current) return;\n const identityGeneration = identityGenerationRef.current;\n const flushAttempt = (flushAttemptRef.current += 1);\n flushingRef.current = true;\n setFlush(\"flushing\");\n setError(null);\n try {\n if (!force) {\n // Base guard: re-read the live file and compare to the captured base. A\n // divergence means the box changed under us — surface a conflict, write\n // NOTHING (C2).\n const readAbort = new AbortController();\n readAbortRef.current?.abort();\n readAbortRef.current = readAbort;\n const liveRead = await client.fsRead(\n workspaceId,\n sessionId,\n { path },\n { signal: readAbort.signal },\n );\n if (readAbortRef.current === readAbort) readAbortRef.current = null;\n if (identityGenerationRef.current !== identityGeneration) return;\n const liveContent = liveRead.content;\n if (baseContent !== null && liveContent !== baseContent) {\n setConflict({ path, base: baseContent, live: liveContent });\n setFlush(\"conflict\");\n return;\n }\n }\n await client.fsWrite(workspaceId, sessionId, { path, content, overwrite: true });\n if (identityGenerationRef.current !== identityGeneration) return;\n setConflict(null);\n setFlush(\"flushed\");\n setWantsWarm(false);\n } catch (cause) {\n if (identityGenerationRef.current !== identityGeneration) return;\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n // Leave the buffer intact so the user can retry; fall back to buffering.\n setFlush(\"idle\");\n } finally {\n if (flushAttemptRef.current === flushAttempt) {\n flushingRef.current = false;\n readAbortRef.current = null;\n }\n }\n },\n [client, workspaceId, sessionId, path, baseContent, stateIdentity, identityKey],\n );\n\n // Auto-flush once the box is warm and a buffer is pending (the wake completed).\n useEffect(() => {\n if (offline) return;\n if (!live) return;\n if (buffer === null) return;\n if (flush === \"flushed\" || flush === \"conflict\" || flush === \"flushing\") return;\n void doFlush(false);\n }, [offline, live, buffer, flush, doFlush]);\n\n const overwrite = useCallback(async () => {\n await doFlush(true);\n }, [doFlush]);\n\n const discard = useCallback(() => {\n setBuffer(null);\n setWantsWarm(false);\n setFlush(\"idle\");\n setConflict(null);\n setError(null);\n }, []);\n\n // Derive the exhaustive public state.\n let state: WorkspaceEditState;\n if (offline) {\n state = \"readonly-offline\";\n } else if (flush === \"flushing\") {\n state = \"flushing\";\n } else if (flush === \"flushed\") {\n state = \"flushed\";\n } else if (flush === \"conflict\") {\n state = \"conflict\";\n } else if (buffer === null) {\n state = \"viewing-cold\";\n } else if (live) {\n // Warm with a pending buffer that hasn't entered flush yet — a transient tick\n // before the auto-flush effect runs.\n state = \"flushing\";\n } else if (options.warming === true) {\n state = \"warming\";\n } else {\n state = \"buffering\";\n }\n\n const identityMatches = stateIdentity === identityKey;\n return {\n state: identityMatches ? state : offline ? \"readonly-offline\" : \"viewing-cold\",\n readOnly: offline,\n buffer: identityMatches ? buffer : null,\n wantsWarm: identityMatches && wantsWarm,\n edit,\n conflict: identityMatches ? conflict : null,\n overwrite,\n discard,\n error: identityMatches ? error : null,\n };\n}\n","import { useMemo } from \"react\";\nimport type { MachineState } from \"@opengeni/sdk\";\nimport type { SessionCapabilitiesState } from \"./use-session-capabilities\";\n\n/** The one truthful machine indicator. Honest staleness beats\n * fake liveness: a cold/asleep box reads \"offline — as of <time>\", never \"live\". */\nexport type MachineChipState = \"live\" | \"waking\" | \"offline\";\n\nexport type MachineChip = {\n state: MachineChipState;\n /** A ready-to-render label (\"Live\" / \"Waking…\" / \"Offline — as of 3m ago\"). */\n label: string;\n /** The capture time backing an offline/stale label (ISO), or null. M4 may\n * reformat this; `label` already embeds a relative form. */\n asOf: string | null;\n};\n\nexport type DeriveMachineChipInput = {\n /** `capabilities.liveness` — \"warm\" | \"draining\" | \"cold\" (or null pre-negotiation). */\n liveness?: string | null | undefined;\n /** `useSessionCapabilities().state` — drives the \"waking\" (negotiating) read. */\n capabilitiesState?: SessionCapabilitiesState | null | undefined;\n /** The ACTIVE machine's connection state (from `useMachines`), when known. A\n * self-hosted machine that is `offline` cannot be remotely woken. */\n activeMachineState?: MachineState | null | undefined;\n /** Whether the active sandbox is a user-owned self-hosted machine (no remote wake). */\n activeIsSelfhosted?: boolean | undefined;\n /** An interaction (wake-on-edit, terminal focus) is actively warming the box. */\n wantsWarm?: boolean | undefined;\n /** The latest capture's `capturedAt` (ISO) — the \"as of <time>\" backing. */\n capturedAt?: string | null | undefined;\n /** Injectable clock for deterministic relative labels (defaults to Date.now). */\n now?: number | undefined;\n};\n\n/** Compact relative-time for the \"as of\" label. Pure, no Intl dependency. */\nexport function formatAsOf(capturedAt: string, now: number): string {\n const then = Date.parse(capturedAt);\n if (Number.isNaN(then)) return \"recently\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 45) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.round(hours / 24);\n return `${days}d ago`;\n}\n\n/**\n * Derive the machine-state chip from the live capability/liveness surface, the\n * active machine's connection state, and the latest capture time. PURE (M4 renders\n * it; the whole point is a deterministic, testable projection).\n *\n * Precedence:\n * 1. warm/draining lease → **live** (a warm box always wins).\n * 2. actively warming (negotiating, wake-on-edit, or a reconnecting/enrolling\n * machine) → **waking**.\n * 3. everything else (a cold/asleep box, a self-hosted machine that's offline,\n * an error) → **offline**, labelled \"as of <capture time>\".\n */\nexport function deriveMachineChip(input: DeriveMachineChipInput): MachineChip {\n const now = input.now ?? Date.now();\n const asOf = input.capturedAt ?? null;\n const offlineLabel = asOf ? `Offline — as of ${formatAsOf(asOf, now)}` : \"Offline\";\n\n // 1. A warm (or draining) lease is live regardless of anything else.\n if (input.liveness === \"warm\" || input.liveness === \"draining\") {\n return { state: \"live\", label: \"Live\", asOf: null };\n }\n\n // A self-hosted machine that reports offline cannot be woken remotely — honest\n // \"offline\", never \"waking\".\n const selfhostedOffline =\n input.activeIsSelfhosted === true && input.activeMachineState === \"offline\";\n\n // 2. Actively coming up: negotiation in flight, an edit/terminal is warming, or\n // the active machine is (re)connecting. Never when self-hosted is hard-offline.\n const machineWarming =\n input.activeMachineState === \"reconnecting\" || input.activeMachineState === \"enrolling\";\n if (\n !selfhostedOffline &&\n (input.capabilitiesState === \"negotiating\" || input.wantsWarm === true || machineWarming)\n ) {\n return { state: \"waking\", label: \"Waking…\", asOf };\n }\n\n // 3. Cold / asleep / offline / error → honest stale label.\n return { state: \"offline\", label: offlineLabel, asOf };\n}\n\nexport type UseMachineChipOptions = DeriveMachineChipInput;\n\n/**\n * Thin memoized wrapper over `deriveMachineChip` for the dock header (M4). The\n * dock already runs `useSessionCapabilities` + `useMachines` + `useWorkspace\n * capture`; it feeds their `liveness` / `state` / active-machine state /\n * `capturedAt` here. Pass a periodically-updated `now` if you want the relative\n * \"as of\" label to tick.\n */\nexport function useMachineChip(input: UseMachineChipOptions): MachineChip {\n const {\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n } = input;\n return useMemo(\n () =>\n deriveMachineChip({\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n }),\n // Depend on the primitive fields (not the object identity) so a caller passing\n // a fresh object each render doesn't recompute needlessly.\n [\n liveness,\n capabilitiesState,\n activeMachineState,\n activeIsSelfhosted,\n wantsWarm,\n capturedAt,\n now,\n ],\n );\n}\n","import { CheckIcon, ShieldCheckIcon, XIcon } from \"lucide-react\";\nimport { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport type { PendingApproval } from \"../approvals\";\nimport { cn } from \"../lib/cn\";\n\nexport type ApprovalSurfaceMessages = {\n title: string;\n description: string;\n approve: string;\n reject: string;\n approving: string;\n rejecting: string;\n formatToolName: (name: string) => string;\n};\n\nexport const defaultApprovalSurfaceMessages: ApprovalSurfaceMessages = {\n title: \"Approval required\",\n description: \"Review the requested action before the agent continues.\",\n approve: \"Approve\",\n reject: \"Reject\",\n approving: \"Approving…\",\n rejecting: \"Rejecting…\",\n formatToolName: (name) => name.replaceAll(\"_\", \" \").replaceAll(\".\", \" › \"),\n};\n\nexport type ApprovalSurfaceProps = {\n approvals: PendingApproval[];\n onApprove: (approval: PendingApproval) => void | Promise<void>;\n onReject: (approval: PendingApproval) => void | Promise<void>;\n responding?: boolean | undefined;\n error?: string | Error | null | undefined;\n messages?: Partial<ApprovalSurfaceMessages> | undefined;\n renderApproval?: ((approval: PendingApproval) => ReactNode) | undefined;\n className?: string | undefined;\n};\n\nconst APPROVAL_ARGUMENT_PREVIEW_CHARACTERS = 4_000;\n\nfunction approvalArgumentsPreview(value: unknown): string | null {\n if (value === undefined) return null;\n let serialized: string;\n try {\n serialized =\n typeof value === \"string\" ? value : (JSON.stringify(value, null, 2) ?? String(value));\n } catch {\n serialized = \"[Arguments unavailable]\";\n }\n const characters = Array.from(serialized);\n if (characters.length <= APPROVAL_ARGUMENT_PREVIEW_CHARACTERS) return serialized;\n return `${characters.slice(0, APPROVAL_ARGUMENT_PREVIEW_CHARACTERS).join(\"\")}\\n… ${characters.length - APPROVAL_ARGUMENT_PREVIEW_CHARACTERS} characters omitted`;\n}\n\nfunction approvalOwnershipKey(approval: PendingApproval): string {\n return `${approval.id}\\u0000${approval.name}\\u0000${approvalArgumentsPreview(approval.arguments) ?? \"\"}`;\n}\n\n/**\n * Host-neutral approval presentation backed by the native pending-approval\n * projection and control callbacks. It owns only presentation and duplicate\n * click fencing; OpenGeni remains the authority for approval state.\n */\nexport function ApprovalSurface({\n approvals,\n onApprove,\n onReject,\n responding = false,\n error,\n messages: overrides,\n renderApproval,\n className,\n}: ApprovalSurfaceProps) {\n const titleId = useId();\n const messages = { ...defaultApprovalSurfaceMessages, ...overrides };\n const [pending, setPending] = useState<{\n approvalKey: string;\n decision: \"approve\" | \"reject\";\n token: symbol;\n } | null>(null);\n const pendingRef = useRef<{ approvalKey: string; token: symbol } | null>(null);\n const [decisionError, setDecisionError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (\n pending &&\n !approvals.some((approval) => approvalOwnershipKey(approval) === pending.approvalKey)\n ) {\n if (pendingRef.current?.token === pending.token) {\n pendingRef.current = null;\n }\n setPending((current) => (current?.token === pending.token ? null : current));\n }\n }, [approvals, pending]);\n\n if (approvals.length === 0) return null;\n const busy = responding || pendingRef.current !== null;\n const decide = async (\n approval: PendingApproval,\n decision: \"approve\" | \"reject\",\n ): Promise<void> => {\n if (responding || pendingRef.current !== null) return;\n const approvalKey = approvalOwnershipKey(approval);\n const token = Symbol(approvalKey);\n pendingRef.current = { approvalKey, token };\n setPending({ approvalKey, decision, token });\n setDecisionError(null);\n try {\n await (decision === \"approve\" ? onApprove(approval) : onReject(approval));\n // Keep the decision fenced until the authoritative projection removes or\n // replaces this exact approval. A successful callback is transport\n // acceptance, not settlement.\n } catch (cause) {\n if (pendingRef.current?.token === token) {\n pendingRef.current = null;\n setPending((current) => (current?.token === token ? null : current));\n setDecisionError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n }\n };\n const errorMessage = decisionError?.message ?? (error instanceof Error ? error.message : error);\n\n return (\n <section\n className={cn(\n \"og-root flex w-full flex-col gap-3 rounded-og-lg border border-og-status-waiting/35 bg-og-status-waiting/5 p-4 shadow-og-sm\",\n className,\n )}\n aria-labelledby={titleId}\n >\n <header className=\"flex items-start gap-3\">\n <span className=\"mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-og-md bg-og-status-waiting/12 text-og-status-waiting\">\n <ShieldCheckIcon aria-hidden=\"true\" className=\"size-4\" />\n </span>\n <div className=\"min-w-0\">\n <h2 id={titleId} className=\"text-og-md font-semibold text-og-fg\">\n {messages.title}\n </h2>\n <p className=\"mt-0.5 text-og-sm text-og-fg-muted\">{messages.description}</p>\n </div>\n </header>\n\n <div className=\"flex flex-col gap-2\">\n {approvals.map((approval) => {\n const approvalKey = approvalOwnershipKey(approval);\n const active = pending?.approvalKey === approvalKey ? pending.decision : null;\n const argumentsPreview = approvalArgumentsPreview(approval.arguments);\n return (\n <article\n key={approval.id}\n data-approval-id={approval.id}\n className=\"rounded-og-md border border-og-border bg-og-surface-1 p-3\"\n >\n {renderApproval ? (\n renderApproval(approval)\n ) : (\n <>\n <p className=\"text-og-sm font-medium text-og-fg\">\n {messages.formatToolName(approval.name)}\n </p>\n {argumentsPreview !== null ? (\n <pre className=\"mt-2 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded-og-sm bg-og-surface-2 p-2 font-mono text-og-xs text-og-fg-muted\">\n {argumentsPreview}\n </pre>\n ) : null}\n </>\n )}\n <div className=\"mt-3 flex flex-wrap justify-end gap-2\">\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void decide(approval, \"reject\")}\n className=\"inline-flex min-h-9 items-center gap-1.5 rounded-og-md border border-og-border px-3 py-1.5 text-og-sm font-medium text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50\"\n >\n <XIcon aria-hidden=\"true\" className=\"size-3.5\" />\n {active === \"reject\" ? messages.rejecting : messages.reject}\n </button>\n <button\n type=\"button\"\n disabled={busy}\n onClick={() => void decide(approval, \"approve\")}\n className=\"inline-flex min-h-9 items-center gap-1.5 rounded-og-md bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg transition-colors hover:bg-og-accent-strong disabled:opacity-50\"\n >\n <CheckIcon aria-hidden=\"true\" className=\"size-3.5\" />\n {active === \"approve\" ? messages.approving : messages.approve}\n </button>\n </div>\n </article>\n );\n })}\n </div>\n\n {errorMessage ? (\n <p role=\"alert\" className=\"text-og-sm text-og-status-failed\">\n {errorMessage}\n </p>\n ) : null}\n </section>\n );\n}\n","import type { ClientModel, EffectiveSessionControl } from \"@opengeni/sdk\";\nimport { LayoutGroup, motion } from \"motion/react\";\nimport type { ClipboardEvent, ReactNode } from \"react\";\nimport type { SlashCommand } from \"../commands/types\";\nimport type { ComposerState } from \"../hooks/use-composer\";\nimport type { UseFileAttachmentsResult } from \"../hooks/use-file-attachments\";\nimport type { SlashCommandContext } from \"../hooks/use-slash-commands\";\nimport { OPEN_WORKSTREAM_CONTROL_EVENT } from \"../workstream-control-event\";\nimport {\n Actions,\n AttachButton,\n Attachments,\n CommandPalette,\n Confirmation,\n Controls,\n Footer,\n Frame,\n Help,\n Hint,\n Input,\n ModelPicker,\n PauseButton,\n PausedState,\n RestoredResources,\n Root,\n SendButton,\n Status,\n Surface,\n useChatComposerController,\n type ChatComposerMessages,\n type ComposerControlLinks,\n} from \"./composer\";\nimport {\n ComposerTranscriptionControl,\n type ComposerTranscriptionControlProps,\n} from \"./composer-transcription-control\";\n\nexport { OPEN_WORKSTREAM_CONTROL_EVENT };\n\nexport type ChatComposerProps = {\n composer: ComposerState;\n /** Canonical workstream control, separate from lifecycle status. */\n effectiveControl?: EffectiveSessionControl | null | undefined;\n /** Waiting prompts already ahead of a normal Send. */\n queuedAheadCount?: number | undefined;\n /** Whether broader Workspace Resume is authorized for this viewer. */\n canControlWorkspace?: boolean | undefined;\n /** Optional host routes used to navigate from effective Pause blockers. */\n controlLinks?: ComposerControlLinks | undefined;\n placeholder?: string | undefined;\n disabled?: boolean | undefined;\n autoFocus?: boolean | undefined;\n /** Replaces the default keyboard hint under the field. */\n hint?: string | undefined;\n /** App controls ahead of attach (e.g. mobile “+” overflow). */\n controlsLeading?: ReactNode | undefined;\n /** App controls in the footer row, replacing the hint. */\n controlsStart?: ReactNode | undefined;\n /** App actions beside Pause/Send, ordered before the built-in actions. */\n actionsStart?: ReactNode | undefined;\n /** Extra classes for the built-in attach control (e.g. `max-sm:hidden`). */\n attachButtonClassName?: string | undefined;\n /** Provider-neutral speech capability. Provider configuration stays in workspace settings. */\n transcription?: ComposerTranscriptionControlProps | undefined;\n /** Extra classes on the transcription control. */\n transcriptionClassName?: string | undefined;\n /** Soft-hide dictate while realtime voice is active (animated collapse). */\n transcriptionSuppressed?: boolean | undefined;\n /** Content rendered above the textarea, inside the field chrome. */\n header?: ReactNode | undefined;\n /** Paste hook composed with the attachment paste path. */\n onPaste?: ((event: ClipboardEvent<HTMLTextAreaElement>) => void) | undefined;\n /** Opt-in file attachment state, typically from `useFileAttachments`. */\n attachments?: UseFileAttachmentsResult | undefined;\n /** Opt-in model picker choices. */\n models?: ClientModel[] | undefined;\n selectedModel?: string | undefined;\n onSelectModel?: ((modelId: string) => void) | undefined;\n className?: string | undefined;\n commands?: readonly SlashCommand[] | undefined;\n commandContext?: SlashCommandContext | undefined;\n onClearView?: (() => void) | undefined;\n /** Partial overrides for all composer-owned visible and accessible copy. */\n messages?: Partial<ChatComposerMessages> | undefined;\n};\n\n/**\n * Batteries-included chat composer. This preset is assembled exclusively from\n * the public controller and compound primitives exported by the composer\n * subpath, so custom and default layouts share one behavioral implementation.\n */\nexport function ChatComposer({\n composer,\n effectiveControl,\n queuedAheadCount,\n canControlWorkspace,\n controlLinks,\n placeholder,\n disabled,\n autoFocus,\n hint,\n controlsLeading,\n controlsStart,\n actionsStart,\n attachButtonClassName,\n transcription,\n transcriptionClassName,\n transcriptionSuppressed = false,\n header,\n onPaste,\n attachments,\n models,\n selectedModel,\n onSelectModel,\n className,\n commands,\n commandContext,\n onClearView,\n messages,\n}: ChatComposerProps) {\n const controller = useChatComposerController({\n delivery: composer,\n draft: composer,\n control: composer,\n effectiveControl,\n queuedAheadCount,\n canControlWorkspace,\n controlLinks,\n disabled,\n attachments,\n commands,\n commandContext,\n onClearView,\n onPaste,\n messages,\n });\n const hasControls = Boolean(\n attachments || models || controlsLeading || controlsStart || transcription,\n );\n const stackActions = hasControls && Boolean(actionsStart);\n\n return (\n <Root controller={controller} className={className}>\n <Frame>\n <CommandPalette />\n <Surface>\n <PausedState />\n <RestoredResources />\n <Attachments />\n {header}\n <Input placeholder={placeholder} autoFocus={autoFocus} />\n {controller.confirmState ? (\n <Confirmation />\n ) : (\n <Footer className={stackActions ? \"max-sm:flex-nowrap sm:flex-wrap\" : undefined}>\n <LayoutGroup id=\"og-composer-footer\">\n {hasControls ? (\n <Controls\n className={stackActions ? \"min-w-0 max-sm:flex-1 sm:w-auto\" : undefined}\n >\n {controlsLeading}\n <AttachButton className={attachButtonClassName} />\n {transcription ? (\n <ComposerTranscriptionControl\n {...transcription}\n suppressed={transcriptionSuppressed}\n className={[transcription.className, transcriptionClassName]\n .filter(Boolean)\n .join(\" \")}\n />\n ) : null}\n {models ? (\n <motion.span\n layout\n transition={{ duration: 0.36, ease: [0.22, 1, 0.36, 1] }}\n className=\"inline-flex min-w-0\"\n >\n <ModelPicker\n models={models}\n value={selectedModel}\n onChange={onSelectModel}\n />\n </motion.span>\n ) : null}\n {controlsStart ? (\n <motion.span\n layout\n transition={{ duration: 0.36, ease: [0.22, 1, 0.36, 1] }}\n className=\"inline-flex min-w-0 items-center gap-1.5\"\n >\n {controlsStart}\n </motion.span>\n ) : null}\n </Controls>\n ) : (\n <Hint>{hint}</Hint>\n )}\n <Actions\n className={stackActions ? \"max-sm:shrink-0 sm:w-auto sm:justify-end\" : undefined}\n >\n {actionsStart}\n <PauseButton />\n <SendButton />\n </Actions>\n </LayoutGroup>\n </Footer>\n )}\n </Surface>\n </Frame>\n <Help />\n <Status />\n </Root>\n );\n}\n","import type { Session } from \"@opengeni/sdk\";\nimport { cn } from \"../lib/cn\";\nimport { formatRelativeTime, truncate } from \"../lib/format\";\nimport { SessionStatus } from \"./session-status\";\n\nexport type FleetTileProps = {\n session: Session;\n /** Overrides the derived title (metadata.title/name, else the initial message). */\n title?: string | undefined;\n /** Extra line under the title — e.g. \"drift check\" or the worker's task. */\n subtitle?: string | undefined;\n onOpen?: ((session: Session) => void) | undefined;\n className?: string | undefined;\n};\n\n/** Best-effort display title for a session. */\nexport function sessionDisplayTitle(session: Session): string {\n // A set title (agent-generated or user-renamed) wins over metadata + the\n // initial-message fallback.\n if (typeof session.title === \"string\" && session.title.trim().length > 0) {\n return session.title;\n }\n for (const key of [\"title\", \"name\"] as const) {\n const value = session.metadata[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n }\n return truncate(session.initialMessage, 100) || session.id.slice(0, 8);\n}\n\n/**\n * One session in the fleet/manager view: title, live status, model, and\n * recency — dense but calm. Running sessions carry a breathing status dot\n * and a faint accent edge so the live ones read at a glance.\n */\nexport function FleetTile({ session, title, subtitle, onOpen, className }: FleetTileProps) {\n const running = session.status === \"running\" || session.status === \"queued\";\n const needsYou = session.status === \"requires_action\";\n return (\n <button\n type=\"button\"\n data-status={session.status}\n onClick={onOpen ? () => onOpen(session) : undefined}\n disabled={!onOpen}\n className={cn(\n \"og-root group relative flex w-full flex-col gap-2.5 overflow-hidden rounded-og-lg border border-og-border\",\n \"bg-og-surface-1 p-4 text-left shadow-og-sm\",\n \"transition-[border-color,background-color,box-shadow,transform] duration-200 ease-og-out\",\n onOpen &&\n \"hover:-translate-y-px hover:border-og-border-strong hover:bg-og-surface-2 hover:shadow-og-md\",\n \"disabled:cursor-default\",\n className,\n )}\n >\n <span\n aria-hidden\n className={cn(\n \"absolute inset-y-0 left-0 w-0.5 transition-opacity duration-300\",\n running\n ? \"bg-og-accent opacity-100\"\n : needsYou\n ? \"bg-og-status-waiting opacity-100\"\n : \"opacity-0\",\n )}\n />\n <span className=\"flex w-full items-start justify-between gap-3\">\n <span className=\"line-clamp-2 min-w-0 text-og-base font-medium leading-snug text-og-fg\">\n {title ?? sessionDisplayTitle(session)}\n </span>\n <SessionStatus status={session.status} size=\"sm\" className=\"mt-px\" />\n </span>\n {subtitle ? (\n <span className=\"line-clamp-1 text-og-sm text-og-fg-muted\">{subtitle}</span>\n ) : null}\n <span className=\"mt-auto flex w-full items-center gap-2 text-og-xs text-og-fg-subtle\">\n <span className=\"font-og-mono\">{session.id.slice(0, 8)}</span>\n <span aria-hidden>·</span>\n <span className=\"truncate\">{session.model}</span>\n <span className=\"ml-auto shrink-0\" title={session.updatedAt}>\n {formatRelativeTime(session.updatedAt)}\n </span>\n </span>\n </button>\n );\n}\n","import type { TerminalCapability } from \"@opengeni/sdk\";\nimport { type ReactNode, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useTerminalStream } from \"../hooks/use-terminal-stream\";\nimport type { UseSandboxTerminalResult } from \"../hooks/use-sandbox-terminal\";\nimport { resolveTerminalFont, xtermThemeFromTokens } from \"../lib/xterm-theme\";\nimport { attachRenderer, type RendererLoaders, type RendererTier } from \"../lib/xterm-renderer\";\n\n/**\n * A COMPLETE xterm `ITheme` (subset by intent, but every field xterm colors a\n * cell with is present so it never falls back to its stock VGA palette). Built\n * from the og tokens via `xtermThemeFromTokens`.\n */\nexport type XtermTheme = {\n background?: string;\n foreground?: string;\n cursor?: string;\n cursorAccent?: string;\n selectionBackground?: string;\n selectionForeground?: string;\n selectionInactiveBackground?: string;\n black?: string;\n red?: string;\n green?: string;\n yellow?: string;\n blue?: string;\n magenta?: string;\n cyan?: string;\n white?: string;\n brightBlack?: string;\n brightRed?: string;\n brightGreen?: string;\n brightYellow?: string;\n brightBlue?: string;\n brightMagenta?: string;\n brightCyan?: string;\n brightWhite?: string;\n};\n\nexport type SandboxTerminalProps = {\n /** From `useSandboxTerminal(...)`. */\n result: UseSandboxTerminalResult;\n /**\n * The negotiated Terminal capability cell. When it advertises `transport:\n * \"pty-ws\"` + a live `url` (a warm box with a viewer attached), the terminal is\n * driven by a REAL bidirectional PTY over the Modal tunnel (ttyd-over-websocket)\n * INSTEAD of the broken ptyWrite-over-HTTP path: xterm input → the socket, the\n * socket's output → xterm, xterm resize → the socket. On a cold box / no url\n * (`transport: \"sse-events\"`) it stays on the read-only Channel-A firehose\n * (`result.chunks`). Omit to force the legacy firehose-only behavior.\n */\n terminalCapability?: TerminalCapability | null | undefined;\n /**\n * Full xterm theme. When omitted the terminal self-derives it from the `--og-*`\n * tokens of its own container (correct even inside a nested `data-og-theme`\n * subtree). Passing it lets the host drive re-theme on a theme flip.\n */\n theme?: XtermTheme | undefined;\n fontFamily?: string | undefined;\n fontSize?: number | undefined;\n /**\n * Lease liveness (`cold` | `warming` | `warm` | `draining`). Drives the\n * boot-in-terminal status lines: once the user has focused the terminal and\n * the box is not yet warm, a styled \"● waking machine — Ns…\" line is rendered\n * INSIDE xterm (no overlay/spinner) until the live PTY connects.\n */\n liveness?: string | undefined;\n /**\n * Force read-only even when the PTY accepts stdin. Default: interactive\n * whenever a live pty-ws stream is connected OR `result.write !== null` (the box\n * advertises an interactive PTY); otherwise the read-only agent firehose.\n */\n readOnly?: boolean | undefined;\n /** Shown on the server / before xterm hydrates (SSR-safe placeholder). */\n placeholder?: ReactNode | undefined;\n /** Render the small status header (pty/shell + running dot + read-only pill). */\n showHeader?: boolean | undefined;\n /** Shell label for the header (e.g. `/bin/bash`). */\n shell?: string | undefined;\n /**\n * Fired the first time the user engages the terminal surface (focus or click).\n * The host wires this to warm the box for the REAL pty-ws terminal (the viewer\n * attach), so a cold box upgrades from the read-only firehose to a live PTY ON\n * INTERACT — never on mere mount (which would force a box spin-up and regress\n * the firehose-only default).\n */\n onActivate?: (() => void) | undefined;\n className?: string | undefined;\n};\n\n// The xterm.js handle the effect owns. `any`-free structural shape so we can\n// drive it without a hard type dep on the lazily-imported lib.\ntype XtermLike = {\n open: (el: HTMLElement) => void;\n write: (data: string) => void;\n clear: () => void;\n onData: (cb: (data: string) => void) => { dispose: () => void };\n onResize: (cb: (size: { cols: number; rows: number }) => void) => { dispose: () => void };\n loadAddon: (addon: unknown) => void;\n dispose: () => void;\n cols: number;\n rows: number;\n options: {\n theme?: XtermTheme;\n disableStdin?: boolean;\n cursorBlink?: boolean;\n fontFamily?: string;\n fontSize?: number;\n };\n};\ntype FitAddonLike = { fit: () => void };\n\n/** Debug seam (dev/evidence only, never a typed public API): a global hook the\n * screenshot harness reads to probe the settled renderer tier + resolved font\n * without mounting a real WebGL context in unit tests. */\ntype TerminalDebugInfo = {\n renderer: RendererTier | null;\n fontFamily: string | undefined;\n fontSize: number | undefined;\n hasVarInFont: boolean;\n theme: XtermTheme | undefined;\n /** The live xterm instance (opaque) — the evidence harness reads its buffer to\n * prove screen preservation (E5) and drive the burst-responsiveness probe. */\n term: unknown;\n};\ndeclare global {\n // eslint-disable-next-line no-var\n var __OG_TERMINAL_DEBUG__: ((info: TerminalDebugInfo) => void) | undefined;\n // A space/comma list of renderer tiers to force-fail, so the demo can prove the\n // WebGL→canvas→DOM fallback ladder without a real GPU context loss.\n // eslint-disable-next-line no-var\n var __OG_FORCE_RENDERER_FAIL__: string | undefined;\n}\n\n/** Which renderer tiers are force-failed (demo/evidence only). */\nfunction forcedRendererFailures(): Set<RendererTier> {\n const raw = typeof globalThis !== \"undefined\" ? globalThis.__OG_FORCE_RENDERER_FAIL__ : undefined;\n const set = new Set<RendererTier>();\n if (typeof raw === \"string\") {\n for (const tier of raw.split(/[\\s,]+/)) {\n if (tier === \"webgl\" || tier === \"canvas\") set.add(tier);\n }\n }\n return set;\n}\n\n/**\n * An xterm.js terminal fed by the Channel-A event projection\n * (`useSandboxTerminal`) or a live ttyd PTY. xterm + its addons are lazy-imported\n * inside an effect, so SSR renders the placeholder and the terminal mounts on\n * hydration.\n *\n * Renderer: WebGL (GPU-composited cells) with a canvas → DOM fallback ladder\n * (`attachRenderer`), so bursty output stays smooth but a lost/blocklisted\n * context degrades gracefully. Font + theme are resolved to CONCRETE values\n * from the og tokens BEFORE construction (xterm measures glyphs in a canvas\n * where `var()` never resolves, and colors every cell from a full ANSI palette).\n *\n * The terminal is mounted ONCE and its data source is swapped in place: the\n * read-only firehose (`result.chunks`) → the live PTY. Stdin is toggled via\n * `term.options.disableStdin` at runtime (not a remount), so scrollback and the\n * visible screen survive the projection→PTY handoff.\n *\n * Resizes are tracked with a `ResizeObserver` on the container (not just\n * `window.resize`) so dragging the dock handle refits the grid.\n */\nexport function SandboxTerminal({\n result,\n terminalCapability,\n theme,\n fontFamily,\n fontSize,\n liveness,\n readOnly,\n placeholder,\n showHeader,\n shell,\n onActivate,\n className,\n}: SandboxTerminalProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const termRef = useRef<XtermLike | null>(null);\n const fitRef = useRef<FitAddonLike | null>(null);\n const rendererRef = useRef<RendererTier | null>(null);\n const writtenRef = useRef<Set<string>>(new Set());\n const wroteFirehoseRef = useRef(false);\n const bootActiveRef = useRef(false);\n const [ready, setReady] = useState(false);\n const [activated, setActivated] = useState(false);\n\n // ── Interactive transport switch ─────────────────────────────────────────────\n const ptyWs = terminalCapability?.transport === \"pty-ws\" && Boolean(terminalCapability?.url);\n // Output frames buffer here until xterm has mounted; the write effect drains it.\n const ptyOutputQueueRef = useRef<string[]>([]);\n const {\n status: ptyStatus,\n write: ptyWrite,\n resize: resizePty,\n connected: ptyConnected,\n } = useTerminalStream({\n capability: ptyWs\n ? {\n transport: terminalCapability!.transport,\n url: terminalCapability!.url,\n token: terminalCapability!.token,\n }\n : null,\n onOutput: (data) => {\n const term = termRef.current;\n if (term) term.write(data);\n else ptyOutputQueueRef.current.push(data);\n },\n });\n\n // PTY mode = a live ttyd socket drives the screen. Firehose mode = the\n // read-only projection (or the legacy HTTP-write fallback).\n const ptyMode = ptyWs && ptyStatus !== \"closed\";\n const interactive = !readOnly && (ptyMode || result.write !== null);\n\n // Boot-in-terminal: after the user focuses a not-yet-warm box, show styled\n // status lines INSIDE xterm instead of an overlay — but only when there is no\n // firehose transcript to show yet (otherwise the projected output is shown).\n const warm = liveness === \"warm\" || liveness === \"draining\";\n const booting =\n activated &&\n !ptyMode &&\n !warm &&\n liveness !== null &&\n liveness !== undefined &&\n result.chunks.length === 0;\n\n function handleActivate() {\n if (!activated) setActivated(true);\n onActivate?.();\n }\n\n // Mount xterm once (client-only). Never re-mounts on interactive/theme flips —\n // stdin + theme are runtime options synced by the effects below, so the visible\n // screen survives the projection→PTY handoff (E5).\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const el = containerRef.current;\n if (!el) return;\n let disposed = false;\n\n // Wait for a real measured size before opening xterm so the first fit is at\n // the true width (belt-and-braces with the visibility gate below).\n const waitForSize = (node: HTMLElement) =>\n new Promise<void>((resolve) => {\n if (node.clientWidth > 0 && node.clientHeight > 0) return resolve();\n const ro = new ResizeObserver(() => {\n if (node.clientWidth > 0 && node.clientHeight > 0) {\n ro.disconnect();\n resolve();\n }\n });\n ro.observe(node);\n setTimeout(() => {\n ro.disconnect();\n resolve();\n }, 1000);\n });\n\n ensureXtermBaseCss();\n\n void (async () => {\n const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([\n import(\"@xterm/xterm\"),\n import(\"@xterm/addon-fit\"),\n import(\"@xterm/addon-web-links\"),\n ]);\n if (disposed) return;\n await waitForSize(el);\n if (disposed) return;\n\n // Resolve font + theme to CONCRETE values BEFORE construction. `var()` in a\n // font family never resolves in xterm's canvas measurement, so we read the\n // computed `--og-font-mono` here; the theme is a full ANSI palette.\n const font = resolveTerminalFont(el, { fontFamily, fontSize });\n const initialTheme = theme ?? xtermThemeFromTokens(el);\n\n const term = new Terminal({\n convertEol: true,\n disableStdin: !interactive,\n cursorBlink: interactive,\n fontFamily: font.fontFamily,\n fontSize: font.fontSize,\n lineHeight: font.lineHeight,\n ...(initialTheme ? { theme: initialTheme } : {}),\n }) as unknown as XtermLike;\n const fit = new FitAddon() as unknown as FitAddonLike;\n term.loadAddon(fit);\n term.loadAddon(\n new WebLinksAddon((_e: MouseEvent, uri: string) =>\n window.open(uri, \"_blank\", \"noopener,noreferrer\"),\n ) as unknown,\n );\n term.open(el);\n termRef.current = term;\n fitRef.current = fit;\n\n // Attach the fastest available renderer. For xterm-6 the supported ladder\n // is WebGL → DOM (the standalone 2D-canvas addon targets xterm-5 internals,\n // so it is intentionally NOT in the shipped path). Init failure / context\n // loss steps down to xterm's built-in DOM renderer; the settled tier is\n // exposed for evidence.\n const failSet = forcedRendererFailures();\n const loaders: RendererLoaders = {\n webgl: async (onLoss) => {\n if (failSet.has(\"webgl\")) throw new Error(\"forced webgl failure\");\n const { WebglAddon } = await import(\"@xterm/addon-webgl\");\n const addon = new WebglAddon() as unknown as {\n dispose: () => void;\n onContextLoss?: (cb: () => void) => void;\n };\n addon.onContextLoss?.(() => {\n try {\n addon.dispose();\n } catch {\n // already disposed\n }\n onLoss();\n });\n term.loadAddon(addon);\n return addon;\n },\n };\n await attachRenderer(\"webgl\", loaders, (tier) => {\n rendererRef.current = tier;\n el.setAttribute(\"data-og-term-renderer\", tier);\n });\n if (disposed) return;\n\n // Fit BEFORE reveal so the first visible frame is already correctly sized\n // (no 80×24 rasterize-then-reflow flash — the container stays hidden until\n // now via the `ready` visibility gate).\n const settle = () => {\n try {\n fit.fit();\n } catch {\n // ignore fit before layout\n }\n };\n settle();\n requestAnimationFrame(() => {\n if (disposed) return;\n settle();\n if (ptyOutputQueueRef.current.length > 0) {\n for (const data of ptyOutputQueueRef.current) term.write(data);\n ptyOutputQueueRef.current = [];\n }\n el.setAttribute(\"data-og-term-ready\", \"true\");\n if (typeof globalThis.__OG_TERMINAL_DEBUG__ === \"function\") {\n globalThis.__OG_TERMINAL_DEBUG__({\n renderer: rendererRef.current,\n fontFamily: term.options.fontFamily,\n fontSize: term.options.fontSize,\n hasVarInFont: String(term.options.fontFamily ?? \"\").includes(\"var(\"),\n theme: term.options.theme,\n term,\n });\n }\n setReady(true);\n });\n })();\n\n return () => {\n disposed = true;\n termRef.current?.dispose();\n termRef.current = null;\n fitRef.current = null;\n rendererRef.current = null;\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n bootActiveRef.current = false;\n setReady(false);\n };\n // Mount ONCE — see the effects below for the runtime option syncs.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Sync stdin/cursor at runtime (NOT a remount) when interactivity flips, so the\n // projection→PTY handoff keeps the single Terminal instance + its scrollback.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n term.options.disableStdin = !interactive;\n term.options.cursorBlink = interactive;\n }, [ready, interactive]);\n\n // Re-theme live (dark↔light flip) without re-mounting / losing scrollback. Uses\n // the passed theme, or self-derives from the container's tokens.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n const next = theme ?? xtermThemeFromTokens(containerRef.current);\n if (next) term.options.theme = next;\n }, [ready, theme]);\n\n // Boot-in-terminal status lines. While `booting`, paint a styled \"● waking\n // machine — Ns…\" line and tick it. On exit, if those lines were the ONLY\n // content (no firehose transcript), clear them for the incoming live screen —\n // never clear a real transcript (E5 preservation).\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term) return;\n if (!booting) {\n if (bootActiveRef.current) {\n bootActiveRef.current = false;\n if (!wroteFirehoseRef.current) term.clear();\n }\n return;\n }\n bootActiveRef.current = true;\n const start = Date.now();\n const paint = () => {\n const s = Math.max(0, Math.round((Date.now() - start) / 1000));\n // Bright-blue dot (accent) + dim label; `\\r` + clear-line keeps it on one\n // line so the counter updates in place.\n term.write(`\\r\\x1b[2K\\x1b[94m●\\x1b[0m \\x1b[2mwaking machine — ${s}s…\\x1b[0m`);\n };\n term.write(\"\\r\\n\");\n paint();\n const id = setInterval(paint, 1000);\n return () => clearInterval(id);\n }, [ready, booting]);\n\n // Write new firehose chunks incrementally — ONLY in firehose mode. In PTY mode\n // the ttyd socket owns the screen. Records that a real transcript exists so the\n // boot cleanup never wipes it.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || ptyMode) return;\n for (const chunk of result.chunks) {\n if (writtenRef.current.has(chunk.id)) continue;\n writtenRef.current.add(chunk.id);\n wroteFirehoseRef.current = true;\n term.write(chunk.text);\n }\n }, [ready, result.chunks, ptyMode]);\n\n // Wire interactive input when allowed. PTY mode pipes keystrokes to the ttyd\n // socket; firehose mode uses the legacy ptyWrite-over-HTTP fn.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !interactive) return;\n const sink = ptyMode ? ptyWrite : result.write;\n if (!sink) return;\n const sub = term.onData((data) => sink(data));\n return () => sub.dispose();\n }, [ready, interactive, ptyMode, ptyWrite, result.write]);\n\n // In PTY mode, tell ttyd the window size on the xterm resize event.\n useEffect(() => {\n const term = termRef.current;\n if (!ready || !term || !ptyMode) return;\n resizePty(term.cols, term.rows);\n const sub = term.onResize(({ cols, rows }) => resizePty(cols, rows));\n return () => sub.dispose();\n }, [ready, ptyMode, ptyConnected, resizePty]);\n\n // Refit on container resize (dock drag) AND window resize.\n useEffect(() => {\n if (typeof window === \"undefined\") return;\n const refit = () => {\n try {\n fitRef.current?.fit();\n } catch {\n // ignore\n }\n };\n window.addEventListener(\"resize\", refit);\n let observer: ResizeObserver | null = null;\n const el = containerRef.current;\n if (el && typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(() => refit());\n observer.observe(el);\n }\n return () => {\n window.removeEventListener(\"resize\", refit);\n observer?.disconnect();\n };\n }, [ready]);\n\n return (\n <div className={cn(\"relative flex h-full w-full flex-col overflow-hidden\", className)}>\n {showHeader && (\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-2 py-1 text-og-xs text-og-fg-subtle\">\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full\",\n result.running ? \"bg-og-status-running\" : \"bg-og-fg-subtle\",\n )}\n />\n <span className=\"truncate font-og-mono\">pty: {shell ?? \"shell\"}</span>\n </span>\n <span className=\"flex shrink-0 items-center gap-2\">\n {!interactive && (\n <span className=\"rounded-og-sm bg-og-surface-2 px-1.5 py-0.5 text-og-xs uppercase tracking-wide\">\n read-only\n </span>\n )}\n <button\n type=\"button\"\n onClick={() => {\n termRef.current?.clear();\n writtenRef.current = new Set();\n wroteFirehoseRef.current = false;\n }}\n className=\"rounded-og-sm px-1.5 py-0.5 text-og-xs hover:text-og-fg pointer-coarse:min-h-11\"\n >\n Clear\n </button>\n </span>\n </div>\n )}\n {/* `onPointerDownCapture`/`onFocusCapture` fire on the FIRST user engagement\n with the terminal surface (capture so they win even though xterm's own\n listeners stop propagation). The host warms the box for pty-ws here. The\n 12px inset + `--og-color-bg` ground match the dock surface. */}\n <div\n className=\"relative min-h-0 flex-1 bg-og-bg p-3\"\n onPointerDownCapture={handleActivate}\n onFocusCapture={handleActivate}\n >\n {!ready && (placeholder ?? <TerminalPlaceholder />)}\n {/* Kept `visibility:hidden` until the first successful fit so the first\n VISIBLE frame is already correctly sized (no 80×24 flash — E4). */}\n <div\n ref={containerRef}\n className=\"h-full w-full\"\n style={{ visibility: ready ? \"visible\" : \"hidden\" }}\n data-opengeni-terminal\n />\n </div>\n </div>\n );\n}\n\n// xterm.js ships a stylesheet (`@xterm/xterm/css/xterm.css`) that is REQUIRED for\n// correct layout — without it the `.xterm-helper-textarea` (an off-screen input\n// xterm uses for IME/keystrokes) is NOT clipped and renders as a visible 1-line\n// box in the top-left (the \"weird input box\" bug), and the viewport/screen aren't\n// positioned. The host app never imports that CSS (no global stylesheet here), so\n// we inject the structural rules once, idempotently (keyed by id), the first time\n// any terminal mounts. Scoped under `.xterm` so it can't leak into host styles.\nconst XTERM_STYLE_ID = \"opengeni-xterm-base-css\";\nconst XTERM_BASE_CSS = `\n.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}\n.xterm.focus,.xterm:focus{outline:none}\n.xterm .xterm-helpers{position:absolute;top:0;z-index:5}\n.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}\n.xterm .composition-view{background:var(--og-color-bg);color:var(--og-color-fg);display:none;position:absolute;white-space:nowrap;z-index:1}\n.xterm .composition-view.active{display:block}\n.xterm .xterm-viewport{background-color:transparent;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}\n.xterm .xterm-screen{position:relative}\n.xterm .xterm-screen canvas{position:absolute;left:0;top:0}\n.xterm .xterm-scroll-area{visibility:hidden}\n.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}\n.xterm.enable-mouse-events{cursor:default}\n.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}\n.xterm.column-select.focus{cursor:crosshair}\n.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}\n.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}\n.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}\n.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}\n.xterm-dim{opacity:1!important}\n.xterm-underline-1{text-decoration:underline}\n.xterm-underline-2{text-decoration:double underline}\n.xterm-underline-3{text-decoration:wavy underline}\n.xterm-underline-4{text-decoration:dotted underline}\n.xterm-underline-5{text-decoration:dashed underline}\n.xterm-overline{text-decoration:overline}\n.xterm-strikethrough{text-decoration:line-through}\n.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}\n.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}\n.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}\n.xterm-decoration-top{z-index:2;position:relative}\n`;\n\nfunction ensureXtermBaseCss(): void {\n if (typeof document === \"undefined\") return;\n if (document.getElementById(XTERM_STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = XTERM_STYLE_ID;\n style.textContent = XTERM_BASE_CSS;\n document.head.appendChild(style);\n}\n\nfunction TerminalPlaceholder() {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center text-og-sm text-og-fg-subtle\">\n Loading terminal…\n </div>\n );\n}\n","import type { XtermTheme } from \"../components/sandbox-terminal\";\n\n/* ----------------------------------------------------------------------------\n xterm theming + font resolution from the `--og-*` token system.\n\n xterm paints glyphs into a canvas (WebGL / 2D) and builds its font strings by\n hand, so it can consume NEITHER CSS custom properties (`var(--og-font-mono)`\n never resolves in a canvas font string) NOR a partial palette (an unset ANSI\n slot falls back to xterm's stock VGA color). This module resolves both to\n concrete values: a full 16-color ANSI ITheme designed against the og palette\n for BOTH themes, and a concrete monospace family + px size.\n\n The color-building + font-resolution cores are pure over a token `read`\n function so they unit-test without a DOM; thin `*FromTokens` / `resolve*`\n wrappers do the `getComputedStyle` read on the client.\n -------------------------------------------------------------------------- */\n\nexport type ThemeMode = \"dark\" | \"light\";\n\n/** Reads a resolved token value (already `getComputedStyle`-flattened). */\nexport type TokenReader = (name: string) => string | undefined;\n\n/**\n * The 16 ANSI colors + selection/cursor treatment, designed against the og\n * OKLCH palette. Hues are borrowed from the og status ramp so terminal color\n * reads as part of the same system: red≈status-failed(22), green≈status-idle\n * (155), yellow≈status-running(80), blue≈accent(255), magenta≈status-waiting\n * (305), cyan≈200. Dark uses lifted lightness on the dark slate ground; light\n * uses deeper, more saturated inks on the near-white ground. Bright variants\n * add lightness + a touch of chroma. `black`/`white` are neutral slate ramp\n * stops (never pure #000/#fff — that clashes with the calm neutral ground).\n */\nexport type AnsiPalette = {\n black: string;\n red: string;\n green: string;\n yellow: string;\n blue: string;\n magenta: string;\n cyan: string;\n white: string;\n brightBlack: string;\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n /** Selection wash + its foreground; sits over the terminal ground. */\n selectionBackground: string;\n selectionForeground: string;\n selectionInactiveBackground: string;\n};\n\nconst DARK_ANSI: AnsiPalette = {\n black: \"oklch(0.3 0.014 260)\",\n red: \"oklch(0.68 0.17 22)\",\n green: \"oklch(0.75 0.13 155)\",\n yellow: \"oklch(0.8 0.11 80)\",\n blue: \"oklch(0.7 0.14 255)\",\n magenta: \"oklch(0.72 0.13 305)\",\n cyan: \"oklch(0.75 0.1 210)\",\n white: \"oklch(0.8 0.01 260)\",\n brightBlack: \"oklch(0.45 0.014 260)\",\n brightRed: \"oklch(0.76 0.17 22)\",\n brightGreen: \"oklch(0.82 0.14 155)\",\n brightYellow: \"oklch(0.86 0.12 85)\",\n brightBlue: \"oklch(0.78 0.13 255)\",\n brightMagenta: \"oklch(0.8 0.13 305)\",\n brightCyan: \"oklch(0.83 0.1 200)\",\n brightWhite: \"oklch(0.97 0.005 260)\",\n selectionBackground: \"oklch(0.72 0.15 255 / 0.34)\",\n selectionForeground: \"oklch(0.985 0.005 260)\",\n selectionInactiveBackground: \"oklch(0.72 0.02 260 / 0.22)\",\n};\n\nconst LIGHT_ANSI: AnsiPalette = {\n black: \"oklch(0.35 0.015 260)\",\n red: \"oklch(0.52 0.19 22)\",\n green: \"oklch(0.5 0.13 155)\",\n yellow: \"oklch(0.56 0.13 80)\",\n blue: \"oklch(0.52 0.18 255)\",\n magenta: \"oklch(0.52 0.16 305)\",\n cyan: \"oklch(0.52 0.1 210)\",\n white: \"oklch(0.7 0.01 260)\",\n brightBlack: \"oklch(0.5 0.014 260)\",\n brightRed: \"oklch(0.55 0.2 22)\",\n brightGreen: \"oklch(0.54 0.14 155)\",\n brightYellow: \"oklch(0.6 0.14 80)\",\n brightBlue: \"oklch(0.48 0.19 255)\",\n brightMagenta: \"oklch(0.54 0.17 305)\",\n brightCyan: \"oklch(0.54 0.11 210)\",\n brightWhite: \"oklch(0.3 0.015 260)\",\n selectionBackground: \"oklch(0.55 0.18 255 / 0.24)\",\n selectionForeground: \"oklch(0.21 0.015 260)\",\n selectionInactiveBackground: \"oklch(0.6 0.02 260 / 0.16)\",\n};\n\n/** The designed ANSI-16 palette for a theme mode. Pure. */\nexport function ogAnsiPalette(mode: ThemeMode): AnsiPalette {\n return mode === \"light\" ? LIGHT_ANSI : DARK_ANSI;\n}\n\n/**\n * Build a COMPLETE xterm ITheme from token values. Background/foreground/cursor\n * come from the live `--og-*` tokens (so an embedder's rebrand flows through);\n * the ANSI-16 + selection come from the designed palette for `mode`. Every ANSI\n * slot is always set — xterm never falls back to its stock VGA colors. Pure.\n */\nexport function buildXtermTheme(read: TokenReader, mode: ThemeMode): XtermTheme {\n const ansi = ogAnsiPalette(mode);\n const bgFallback = mode === \"light\" ? \"oklch(0.978 0.003 260)\" : \"oklch(0.155 0.012 260)\";\n const fgFallback = mode === \"light\" ? \"oklch(0.21 0.015 260)\" : \"oklch(0.955 0.005 260)\";\n const bg = read(\"--og-color-bg\") ?? bgFallback;\n const fg = read(\"--og-color-fg\") ?? fgFallback;\n const accent = read(\"--og-color-accent\") ?? ansi.brightBlue;\n return {\n // Match the dock surface the terminal paints on (`--og-color-bg`).\n background: bg,\n foreground: fg,\n cursor: accent,\n cursorAccent: bg,\n selectionBackground: ansi.selectionBackground,\n selectionForeground: ansi.selectionForeground,\n selectionInactiveBackground: ansi.selectionInactiveBackground,\n black: ansi.black,\n red: ansi.red,\n green: ansi.green,\n yellow: ansi.yellow,\n blue: ansi.blue,\n magenta: ansi.magenta,\n cyan: ansi.cyan,\n white: ansi.white,\n brightBlack: ansi.brightBlack,\n brightRed: ansi.brightRed,\n brightGreen: ansi.brightGreen,\n brightYellow: ansi.brightYellow,\n brightBlue: ansi.brightBlue,\n brightMagenta: ansi.brightMagenta,\n brightCyan: ansi.brightCyan,\n brightWhite: ansi.brightWhite,\n };\n}\n\n/** Which og theme is active for `el`, matching the `use-theme-type` convention\n * (nearest `data-og-theme` / `.og-light` ancestor; else the computed\n * `color-scheme`; else dark, the first-class default). */\nfunction detectMode(el: Element, style: CSSStyleDeclaration): ThemeMode {\n const closest = el.closest?.bind(el);\n if (closest) {\n if (closest('[data-og-theme=\"light\"]') || closest(\".og-light\")) return \"light\";\n const explicit = closest(\"[data-og-theme]\");\n if (explicit) return explicit.getAttribute(\"data-og-theme\") === \"light\" ? \"light\" : \"dark\";\n }\n const scheme = (\n style.getPropertyValue(\"color-scheme\").trim() ||\n style.colorScheme ||\n \"\"\n ).toLowerCase();\n if (scheme.includes(\"light\") && !scheme.includes(\"dark\")) return \"light\";\n return \"dark\";\n}\n\n/**\n * Derive a COMPLETE xterm `ITheme` from the live `--og-*` token system on the\n * DOM. Reads the COMPUTED values so xterm gets concrete colors, detects the\n * active theme mode, and fills every ANSI slot. Call on mount + re-derive on a\n * `data-og-theme` flip. SSR-safe: returns undefined off the DOM.\n *\n * `root` should be an element INSIDE the themed subtree (e.g. the terminal\n * container) so nested `[data-og-theme]` themes resolve correctly; when omitted\n * it falls back to the nearest `[data-og-theme]` element, then documentElement.\n */\nexport function xtermThemeFromTokens(root?: HTMLElement | null): XtermTheme | undefined {\n if (typeof window === \"undefined\" || typeof getComputedStyle === \"undefined\") return undefined;\n const el =\n root ??\n (typeof document !== \"undefined\"\n ? ((document.querySelector(\"[data-og-theme]\") as HTMLElement | null) ??\n document.documentElement)\n : null);\n if (!el) return undefined;\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return buildXtermTheme(read, detectMode(el, style));\n}\n\n// ── Font resolution ──────────────────────────────────────────────────────────\n\nexport type ResolvedTerminalFont = { fontFamily: string; fontSize: number; lineHeight: number };\n\n/** Concrete fallback if the token is unset or (defensively) still a `var()`. */\nconst FALLBACK_MONO = \"ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace\";\n\nexport type FontOverrides = { fontFamily?: string | undefined; fontSize?: number | undefined };\n\n/**\n * Resolve xterm's font from token values. The critical bug this fixes:\n * `fontFamily: \"var(--og-font-mono)\"` is measured by xterm via a canvas font\n * string where `var()` does NOT resolve, so xterm silently mis-measures every\n * glyph. Here the family is a CONCRETE stack; a `var(` in the input is treated\n * as unresolved and replaced with the fallback. Pure over a token `read`.\n */\nexport function resolveTerminalFontFromReader(\n read: TokenReader,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const rawFamily = overrides?.fontFamily ?? read(\"--og-font-mono\");\n const fontFamily =\n rawFamily && rawFamily.trim() && !rawFamily.includes(\"var(\") ? rawFamily.trim() : FALLBACK_MONO;\n\n let fontSize = overrides?.fontSize;\n if (fontSize == null) {\n const raw = read(\"--og-code-font-size\");\n const parsed = raw ? Number.parseFloat(raw) : Number.NaN;\n fontSize = Number.isFinite(parsed) && parsed > 0 ? parsed : 13;\n }\n\n return { fontFamily, fontSize, lineHeight: 1.3 };\n}\n\n/** DOM wrapper: resolve the terminal font from an element's computed tokens. */\nexport function resolveTerminalFont(\n el: HTMLElement,\n overrides?: FontOverrides,\n): ResolvedTerminalFont {\n const style = getComputedStyle(el);\n const read: TokenReader = (name) => {\n const value = style.getPropertyValue(name).trim();\n return value || undefined;\n };\n return resolveTerminalFontFromReader(read, overrides);\n}\n","/* ----------------------------------------------------------------------------\n xterm renderer selection ladder: WebGL → 2D canvas → DOM.\n\n WebGL is the fast path (GPU-composited cells — the whole point of the burst\n responsiveness win). But a WebGL context can fail to init (headless / no GPU /\n blocklisted driver) or be LOST at runtime (GPU reset, tab backgrounded). On\n either, we dispose the addon and step down one tier so the terminal keeps\n painting via the 2D canvas addon, and failing that xterm's built-in DOM\n renderer (no addon).\n\n `attachRenderer` is pure over its `loaders` (each closes over the xterm\n instance and constructs+loads its addon, throwing on failure), so the ladder\n + context-loss downgrade unit-test without a real WebGL context.\n -------------------------------------------------------------------------- */\n\nexport type RendererTier = \"webgl\" | \"canvas\" | \"dom\";\n\nexport type RendererAddon = { dispose: () => void };\n\n/** A loader constructs + `loadAddon`s its renderer, wiring `onLoss` to the\n * addon's context-loss event, and throws if the renderer can't initialize. */\nexport type RendererLoaders = {\n webgl?: ((onLoss: () => void) => Promise<RendererAddon>) | undefined;\n canvas?: (() => Promise<RendererAddon>) | undefined;\n};\n\nconst ORDER: RendererTier[] = [\"webgl\", \"canvas\", \"dom\"];\n\n/** The next lower renderer tier, or null at the bottom (DOM). Pure. */\nexport function nextRendererTier(tier: RendererTier): RendererTier | null {\n const i = ORDER.indexOf(tier);\n return i >= 0 && i < ORDER.length - 1 ? ORDER[i + 1]! : null;\n}\n\n/**\n * Attach the highest renderer tier at or below `prefer` that initializes,\n * reporting the settled tier via `onTier`. On a WebGL/canvas context loss the\n * loader's `onLoss` re-enters at the next tier down, so `onTier` is called again\n * with the downgraded tier. Returns the tier chosen on this pass.\n */\nexport async function attachRenderer(\n prefer: RendererTier,\n loaders: RendererLoaders,\n onTier: (tier: RendererTier) => void,\n): Promise<RendererTier> {\n const start = Math.max(0, ORDER.indexOf(prefer));\n for (let i = start; i < ORDER.length; i++) {\n const tier = ORDER[i]!;\n if (tier === \"dom\") break;\n const loader = tier === \"webgl\" ? loaders.webgl : loaders.canvas;\n if (!loader) continue;\n try {\n const downgrade = nextRendererTier(tier) ?? \"dom\";\n await loader(() => {\n void attachRenderer(downgrade, loaders, onTier);\n });\n onTier(tier);\n return tier;\n } catch {\n // Initialization failed — fall through to the next tier down.\n }\n }\n onTier(\"dom\");\n return \"dom\";\n}\n","import {\n ChevronRightIcon,\n FileIcon,\n FilePlusIcon,\n FolderIcon,\n FolderPlusIcon,\n PencilIcon,\n RefreshCwIcon,\n TriangleAlertIcon,\n Trash2Icon,\n} from \"lucide-react\";\nimport {\n type KeyboardEvent as ReactKeyboardEvent,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { AlertDialog } from \"radix-ui\";\nimport { VList, type VListHandle } from \"virtua\";\nimport { cn } from \"../lib/cn\";\nimport { type PortalTokenStyle, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport { useUnicodeFallbackFonts } from \"../lib/use-unicode-fonts\";\nimport type { FileTreeNode, UseSandboxFilesResult } from \"../hooks/use-sandbox-files\";\n\nexport type FileBrowserProps = {\n /** From `useSandboxFiles(...)`. */\n result: UseSandboxFilesResult;\n /**\n * Rendered instead of the built-in tree when the file surface is unavailable\n * (e.g. a `FileSystem.available === false` capability). Default: a quiet notice.\n */\n fallback?: ReactNode | undefined;\n /** Selection callback for the preview pane. */\n onSelectFile?: ((path: string) => void) | undefined;\n selectedPath?: string | undefined;\n /** Render-prop to theme/replace a row entirely (the Pierre-swap escape hatch). */\n renderNode?: ((node: FileTreeNode, depth: number, expanded: boolean) => ReactNode) | undefined;\n /** Shown when the tree is empty (no files / not loaded yet). */\n emptyState?: ReactNode | undefined;\n /**\n * Enable the file-manager affordances (toolbar, context menu, drag-drop move,\n * inline rename, delete, new file/folder). Defaults to `true` when the hook\n * exposes the mutation methods; pass `false` for a strictly read-only tree.\n */\n editable?: boolean | undefined;\n /**\n * Confirm a (recursive) delete before it runs. Return `false` to cancel.\n * The default is an accessible, non-blocking confirmation dialog. Supply this\n * callback to delegate confirmation to a host-owned dialog.\n */\n confirmDelete?: ((node: FileTreeNode) => boolean | Promise<boolean>) | undefined;\n className?: string | undefined;\n};\n\nconst STATUS_TINT: Record<NonNullable<FileTreeNode[\"status\"]>, string> = {\n added: \"text-og-status-idle\",\n modified: \"text-og-status-running\",\n deleted: \"text-og-status-failed line-through\",\n renamed: \"text-og-accent\",\n untracked: \"text-og-fg-subtle\",\n};\nconst useBrowserLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/** Parent dir of a workspace-relative POSIX path (\"\" for a root entry). */\nfunction parentOf(path: string): string {\n const i = path.lastIndexOf(\"/\");\n return i <= 0 ? \"\" : path.slice(0, i);\n}\n\n/** Join a parent dir with a leaf name (handles the root \"\" parent). */\nfunction joinPath(parent: string, name: string): string {\n return parent ? `${parent}/${name}` : name;\n}\n\n/** A stable React key for a flat render item. */\nfunction itemKey(item: RenderItem): string {\n switch (item.type) {\n case \"node\":\n return item.node.path;\n case \"create\":\n return `create:${item.parent}`;\n case \"skeleton\":\n return `skeleton:${item.path}`;\n case \"residue\":\n return `residue:${item.path}`;\n }\n}\n\n/** Walk the tree to find a node by path. */\nfunction findNode(nodes: FileTreeNode[], path: string): FileTreeNode | undefined {\n for (const node of nodes) {\n if (node.path === path) return node;\n if (node.kind === \"dir\" && node.children && path.startsWith(`${node.path}/`)) {\n const hit = findNode(node.children, path);\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** A pending inline-create: a phantom input row under `parent`. */\ntype DraftCreate = { parent: string; kind: \"file\" | \"dir\" };\n/** A pending inline-rename of an existing node. */\ntype DraftRename = { path: string };\ntype ContextMenuState = { node: FileTreeNode | null; x: number; y: number };\n\n/** One virtualized row. Nodes plus the synthetic rows the recursive renderer\n * used to nest inline. */\ntype RenderItem =\n | { type: \"node\"; node: FileTreeNode; depth: number }\n | { type: \"create\"; parent: string; kind: \"file\" | \"dir\"; depth: number }\n | { type: \"skeleton\"; path: string; depth: number }\n | { type: \"residue\"; path: string; depth: number };\n\n/**\n * The file MANAGER, fed by the FileSystem service via `useSandboxFiles`. This is\n * a first-class editable tree (not a render-only view): lazy-expand with a\n * spinner on the in-flight node, git-status tinting, full keyboard navigation,\n * selection, and the mutating affordances wired straight to the hook —\n *\n * • drag-and-drop MOVE → `moveEntry(from, to)`\n * • inline RENAME → `moveEntry(path, newPath)` (F2 / double-click / menu)\n * • DELETE → `deleteEntry(path, recursive)` (Del / menu, confirmed)\n * • NEW FILE / FOLDER → `createFile` / `createDir` (toolbar + menu), then open\n * • right-click CONTEXT MENU\n *\n * `renderNode` is still honoured as the Pierre-swap escape hatch for the row\n * chrome; the manager scaffolding (toolbar, dnd, menu, inline inputs) wraps it.\n */\nexport function FileBrowser({\n result,\n fallback,\n onSelectFile,\n selectedPath,\n renderNode,\n emptyState,\n editable = true,\n confirmDelete,\n className,\n}: FileBrowserProps) {\n const [expanded, setExpanded] = useState<Set<string>>(new Set());\n const [loadingPaths, setLoadingPaths] = useState<Set<string>>(new Set());\n const [active, setActive] = useState<string | null>(null);\n const [draftCreate, setDraftCreate] = useState<DraftCreate | null>(null);\n const [draftRename, setDraftRename] = useState<DraftRename | null>(null);\n const [dragOver, setDragOver] = useState<string | null>(null);\n const [menu, setMenu] = useState<ContextMenuState | null>(null);\n const [pendingDelete, setPendingDelete] = useState<FileTreeNode | null>(null);\n const [busy, setBusy] = useState(false);\n const [coarsePointer, setCoarsePointer] = useState(false);\n const treeId = useId();\n const containerRef = useRef<HTMLDivElement | null>(null);\n const deleteReturnFocusRef = useRef<HTMLElement | null>(null);\n const vlistRef = useRef<VListHandle>(null);\n const portalTokenStyle = usePortalTokenStyle(containerRef);\n const getTreeElement = useCallback(\n () => (typeof document === \"undefined\" ? null : document.getElementById(treeId)),\n [treeId],\n );\n\n useBrowserLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const query = window.matchMedia(\"(pointer: coarse)\");\n const update = () => setCoarsePointer(query.matches);\n update();\n if (typeof query.addEventListener === \"function\") {\n query.addEventListener(\"change\", update);\n return () => query.removeEventListener(\"change\", update);\n }\n if (typeof query.addListener === \"function\") {\n query.addListener(update);\n return () => query.removeListener(update);\n }\n }, []);\n\n // The keyboard cursor: an explicitly-navigated node falls back to the\n // externally-selected file so arrow keys pick up where the preview pane is.\n const cursor = active ?? selectedPath ?? null;\n\n const expand = useCallback(\n async (path: string) => {\n const node = findNode(result.tree, path);\n if (node && node.kind === \"dir\" && node.children === undefined) {\n setLoadingPaths((prev) => new Set(prev).add(path));\n try {\n await result.expand(path);\n } finally {\n setLoadingPaths((prev) => {\n const next = new Set(prev);\n next.delete(path);\n return next;\n });\n }\n }\n },\n [result],\n );\n\n const open = useCallback(\n (path: string) => {\n setExpanded((prev) => new Set(prev).add(path));\n void expand(path);\n },\n [expand],\n );\n\n const toggle = useCallback(\n async (node: FileTreeNode) => {\n if (node.kind !== \"dir\") return;\n const isOpen = expanded.has(node.path);\n setExpanded((prev) => {\n const next = new Set(prev);\n if (isOpen) next.delete(node.path);\n else next.add(node.path);\n return next;\n });\n if (!isOpen) await expand(node.path);\n },\n [expanded, expand],\n );\n\n // Flatten the VISIBLE (expanded) tree into a single ordered render list — the\n // input to the virtualizer. Interleaves the synthetic rows the recursive\n // renderer used to nest (inline create input, lazy-expand skeleton, and the\n // cold residue \"contents on machine\" row) so every visual row is one list item.\n const cold = result.source !== \"live\";\n const renderItems = useMemo(() => {\n const items: RenderItem[] = [];\n if (draftCreate && draftCreate.parent === \"\") {\n items.push({ type: \"create\", parent: \"\", kind: draftCreate.kind, depth: 0 });\n }\n const walk = (nodes: FileTreeNode[], depth: number) => {\n for (const node of nodes) {\n items.push({ type: \"node\", node, depth });\n if (node.kind !== \"dir\" || !expanded.has(node.path)) continue;\n if (draftCreate && draftCreate.parent === node.path) {\n items.push({\n type: \"create\",\n parent: node.path,\n kind: draftCreate.kind,\n depth: depth + 1,\n });\n }\n const childless = node.children === undefined || node.children.length === 0;\n const isLoading = loadingPaths.has(node.path) || result.expandingPaths.has(node.path);\n // A collapsed residue dir (node_modules, .git, …) can't list children from\n // the cold capture — offer to open it live rather than a dead skeleton.\n if (node.truncated && childless && cold && !isLoading) {\n items.push({ type: \"residue\", path: node.path, depth: depth + 1 });\n } else if (node.children && node.children.length > 0) {\n walk(node.children, depth + 1);\n } else if (isLoading && childless) {\n items.push({ type: \"skeleton\", path: node.path, depth: depth + 1 });\n }\n }\n };\n walk(result.tree, 0);\n return items;\n }, [result.tree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);\n const unicodeFontPaths = useMemo(\n () => renderItems.flatMap((item) => (item.type === \"node\" ? [item.node.path] : [])),\n [renderItems],\n );\n useUnicodeFallbackFonts(unicodeFontPaths);\n\n // The visible NODE rows in order — drives keyboard up/down navigation.\n const flatRows = useMemo(\n () =>\n renderItems.flatMap((item) =>\n item.type === \"node\" ? [{ node: item.node, depth: item.depth }] : [],\n ),\n [renderItems],\n );\n const rowIndexByPath = useMemo(\n () => new Map(flatRows.map((row, index) => [row.node.path, index] as const)),\n [flatRows],\n );\n const treePositionByPath = useMemo(() => {\n const positions = new Map<string, { position: number; setSize: number }>();\n const visit = (nodes: FileTreeNode[]) => {\n nodes.forEach((node, index) => {\n positions.set(node.path, { position: index + 1, setSize: nodes.length });\n if (node.children) visit(node.children);\n });\n };\n visit(result.tree);\n return positions;\n }, [result.tree]);\n const cursorIndex = cursor ? (rowIndexByPath.get(cursor) ?? -1) : -1;\n const activeDescendantId = cursorIndex >= 0 ? `${treeId}-item-${cursorIndex}` : undefined;\n\n // A capture is immutable review data even if a stale capability document still\n // says the machine is writable. Mutations become available only after a live\n // reconciliation has actually succeeded.\n const supportsMutation =\n editable && result.source === \"live\" && result.error === null && !result.loading;\n\n const performDelete = useCallback(\n async (node: FileTreeNode) => {\n if (!supportsMutation) return;\n const recursive = node.kind === \"dir\";\n setBusy(true);\n try {\n await result.deleteEntry(node.path, recursive);\n } catch {\n // The hook surfaces the error on `result.error`; the panel renders it.\n } finally {\n setBusy(false);\n }\n },\n [supportsMutation, result],\n );\n\n const runDelete = useCallback(\n async (node: FileTreeNode, returnFocus?: HTMLElement) => {\n if (!supportsMutation) return;\n setMenu(null);\n if (!confirmDelete) {\n deleteReturnFocusRef.current =\n returnFocus ??\n (typeof document !== \"undefined\" && document.activeElement instanceof HTMLElement\n ? document.activeElement\n : null);\n setPendingDelete(node);\n return;\n }\n if (await confirmDelete(node)) await performDelete(node);\n },\n [supportsMutation, confirmDelete, performDelete],\n );\n\n const closePendingDelete = useCallback(\n (restoreTrigger: boolean) => {\n const returnTarget = restoreTrigger ? deleteReturnFocusRef.current : getTreeElement();\n deleteReturnFocusRef.current = null;\n setPendingDelete(null);\n if (typeof window !== \"undefined\") {\n window.requestAnimationFrame(() => {\n const target = returnTarget?.isConnected ? returnTarget : getTreeElement();\n target?.focus();\n });\n }\n },\n [getTreeElement],\n );\n\n const commitCreate = useCallback(\n async (name: string) => {\n const draft = draftCreate;\n setDraftCreate(null);\n if (!draft || !supportsMutation) return;\n const trimmed = name.trim().replace(/\\/+$/, \"\");\n if (!trimmed) return;\n const path = joinPath(draft.parent, trimmed);\n setBusy(true);\n try {\n if (draft.kind === \"dir\") {\n await result.createDir(path);\n open(path);\n } else {\n await result.createFile(path);\n // Open the freshly-created file in the preview/editor pane.\n setActive(path);\n onSelectFile?.(path);\n }\n } catch {\n /* error surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [draftCreate, supportsMutation, result, open, onSelectFile],\n );\n\n const commitRename = useCallback(\n async (name: string) => {\n const draft = draftRename;\n setDraftRename(null);\n if (!draft || !supportsMutation) return;\n const node = findNode(result.tree, draft.path);\n const trimmed = name.trim().replace(/\\/+$/, \"\");\n if (!node || !trimmed || trimmed === node.name) return;\n const newPath = joinPath(parentOf(draft.path), trimmed);\n setBusy(true);\n try {\n await result.moveEntry(draft.path, newPath);\n if (node.kind === \"file\" && (selectedPath === draft.path || active === draft.path)) {\n setActive(newPath);\n onSelectFile?.(newPath);\n }\n } catch {\n /* error surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [draftRename, supportsMutation, result, selectedPath, active, onSelectFile],\n );\n\n // Begin a new file/folder under the active node's directory (or root).\n const startCreate = useCallback(\n (kind: \"file\" | \"dir\") => {\n if (!supportsMutation) return;\n const anchor = cursor ? findNode(result.tree, cursor) : undefined;\n const parent = anchor ? (anchor.kind === \"dir\" ? anchor.path : parentOf(anchor.path)) : \"\";\n if (parent) open(parent);\n setDraftRename(null);\n setMenu(null);\n setDraftCreate({ parent, kind });\n },\n [supportsMutation, cursor, result.tree, open],\n );\n\n const startRename = useCallback(\n (node: FileTreeNode) => {\n if (!supportsMutation) return;\n setDraftCreate(null);\n setMenu(null);\n setDraftRename({ path: node.path });\n },\n [supportsMutation],\n );\n\n // Drag-drop move: drop a node onto a directory (or the root) → moveEntry.\n const onDropOnto = useCallback(\n async (targetDir: string, sourcePath: string) => {\n setDragOver(null);\n if (!supportsMutation || !sourcePath) return;\n const src = findNode(result.tree, sourcePath);\n if (!src) return;\n // No-op drops: onto its own current parent, onto itself, or into its own subtree.\n if (parentOf(sourcePath) === targetDir) return;\n if (targetDir === sourcePath || targetDir.startsWith(`${sourcePath}/`)) return;\n const newPath = joinPath(targetDir, src.name);\n if (newPath === sourcePath) return;\n setBusy(true);\n try {\n await result.moveEntry(sourcePath, newPath);\n if (src.kind === \"file\" && (selectedPath === sourcePath || active === sourcePath)) {\n setActive(newPath);\n onSelectFile?.(newPath);\n }\n } catch {\n /* 409/collision etc. surfaced via result.error */\n } finally {\n setBusy(false);\n }\n },\n [supportsMutation, result, selectedPath, active, onSelectFile],\n );\n\n const onKeyDown = useCallback(\n (e: ReactKeyboardEvent<HTMLDivElement>) => {\n if (draftCreate || draftRename) return; // inline input owns the keyboard\n if (flatRows.length === 0) return;\n const idx = flatRows.findIndex((r) => r.node.path === cursor);\n const move = (next: number) => {\n const clamped = Math.max(0, Math.min(flatRows.length - 1, next));\n const row = flatRows[clamped];\n if (row) setActive(row.node.path);\n e.preventDefault();\n };\n const cur = idx >= 0 ? flatRows[idx] : undefined;\n switch (e.key) {\n case \"ArrowDown\":\n move(idx < 0 ? 0 : idx + 1);\n break;\n case \"ArrowUp\":\n move(idx < 0 ? 0 : idx - 1);\n break;\n case \"Home\":\n move(0);\n break;\n case \"End\":\n move(flatRows.length - 1);\n break;\n case \"PageDown\": {\n const pageSize = Math.max(\n 1,\n Math.floor((containerRef.current?.clientHeight || 280) / (coarsePointer ? 44 : 28)) - 1,\n );\n move((idx < 0 ? 0 : idx) + pageSize);\n break;\n }\n case \"PageUp\": {\n const pageSize = Math.max(\n 1,\n Math.floor((containerRef.current?.clientHeight || 280) / (coarsePointer ? 44 : 28)) - 1,\n );\n move((idx < 0 ? 0 : idx) - pageSize);\n break;\n }\n case \"ArrowRight\":\n if (cur?.node.kind === \"dir\") {\n if (!expanded.has(cur.node.path)) open(cur.node.path);\n else move(idx + 1);\n e.preventDefault();\n }\n break;\n case \"ArrowLeft\":\n if (cur) {\n if (cur.node.kind === \"dir\" && expanded.has(cur.node.path)) {\n setExpanded((prev) => {\n const n = new Set(prev);\n n.delete(cur.node.path);\n return n;\n });\n } else {\n const parent = parentOf(cur.node.path);\n if (parent) setActive(parent);\n }\n e.preventDefault();\n }\n break;\n case \"Enter\":\n if (cur) {\n if (cur.node.kind === \"dir\") void toggle(cur.node);\n else onSelectFile?.(cur.node.path);\n e.preventDefault();\n }\n break;\n case \"ContextMenu\":\n case \"F10\":\n if (cur && supportsMutation && (e.key === \"ContextMenu\" || e.shiftKey)) {\n const rowIndex = rowIndexByPath.get(cur.node.path) ?? 0;\n const row = document.getElementById(`${treeId}-item-${rowIndex}`);\n const rect = row?.getBoundingClientRect();\n setMenu({\n node: cur.node,\n x: rect ? rect.left + Math.min(32, rect.width / 2) : 8,\n y: rect ? rect.bottom : 8,\n });\n e.preventDefault();\n } else if (e.key === \"F10\") {\n break;\n }\n break;\n case \"F2\":\n if (cur) {\n startRename(cur.node);\n e.preventDefault();\n }\n break;\n case \"Delete\":\n case \"Backspace\":\n if (cur && supportsMutation) {\n void runDelete(cur.node);\n e.preventDefault();\n }\n break;\n }\n },\n [\n draftCreate,\n draftRename,\n flatRows,\n cursor,\n expanded,\n open,\n toggle,\n onSelectFile,\n startRename,\n runDelete,\n supportsMutation,\n coarsePointer,\n rowIndexByPath,\n treeId,\n ],\n );\n\n // Close the context menu on any outside interaction.\n useEffect(() => {\n if (!menu) return;\n const close = () => setMenu(null);\n window.addEventListener(\"click\", close);\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return () => {\n window.removeEventListener(\"click\", close);\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n }, [menu]);\n\n const showErrorEmpty = result.error && result.tree.length === 0 && !draftCreate;\n const showDegradedTree = result.error && result.tree.length > 0;\n const showEmpty = !result.loading && result.tree.length === 0 && !draftCreate;\n // Retry lives next to the state it explains (the degraded/empty notice), so do\n // not render a second toolbar retry for the same failure.\n const showToolbar = supportsMutation || result.loading;\n const usesVirtualTree = !showErrorEmpty && !showEmpty;\n\n // Render a SINGLE node row (no recursion — children are separate flat items the\n // virtualizer renders). Returns the row element; the caller assigns the key.\n const renderNodeRow = (node: FileTreeNode, depth: number): ReactNode => {\n const isOpen = expanded.has(node.path);\n if (renderNode) {\n return <div>{renderNode(node, depth, isOpen)}</div>;\n }\n const isDir = node.kind === \"dir\";\n const isSelected = node.path === selectedPath || node.path === active;\n const isCursor = node.path === cursor;\n const isLoading = loadingPaths.has(node.path) || result.expandingPaths.has(node.path);\n const isRenaming = draftRename?.path === node.path;\n const isDropTarget = dragOver === (isDir ? node.path : parentOf(node.path));\n const dropDir = isDir ? node.path : parentOf(node.path);\n const treePosition = treePositionByPath.get(node.path);\n\n return (\n <div\n id={`${treeId}-item-${rowIndexByPath.get(node.path) ?? 0}`}\n role=\"treeitem\"\n aria-level={depth + 1}\n aria-posinset={treePosition?.position}\n aria-setsize={treePosition?.setSize}\n aria-expanded={isDir ? isOpen : undefined}\n aria-selected={isCursor || undefined}\n aria-busy={isLoading || undefined}\n onDragOver={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(dropDir);\n }\n : undefined\n }\n onDragLeave={\n supportsMutation\n ? (e) => {\n e.stopPropagation();\n setDragOver((cur) => (cur === dropDir ? null : cur));\n }\n : undefined\n }\n onDrop={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.stopPropagation();\n const src = e.dataTransfer.getData(\"text/og-path\");\n void onDropOnto(dropDir, src);\n }\n : undefined\n }\n >\n {isRenaming ? (\n <InlineInput\n depth={depth}\n kind={node.kind}\n initialValue={node.name}\n onCommit={(name) => void commitRename(name)}\n onCancel={() => setDraftRename(null)}\n />\n ) : (\n <button\n type=\"button\"\n tabIndex={-1}\n draggable={supportsMutation || undefined}\n onDragStart={\n supportsMutation\n ? (e) => {\n e.dataTransfer.setData(\"text/og-path\", node.path);\n e.dataTransfer.effectAllowed = \"move\";\n }\n : undefined\n }\n onClick={() => {\n setActive(node.path);\n if (isDir) void toggle(node);\n else onSelectFile?.(node.path);\n }}\n onDoubleClick={() => supportsMutation && startRename(node)}\n onContextMenu={(e) => {\n if (!supportsMutation) return;\n e.preventDefault();\n setActive(node.path);\n setMenu({ node, x: e.clientX, y: e.clientY });\n }}\n className={cn(\n \"group relative flex min-h-7 w-full items-center gap-1 truncate rounded-og-sm px-1 py-0.5 text-left text-og-sm pointer-coarse:min-h-11\",\n \"hover:bg-og-surface-2\",\n isSelected && \"bg-og-surface-2\",\n isCursor && \"outline outline-1 -outline-offset-1 outline-og-accent\",\n isDropTarget && \"bg-og-accent-soft ring-1 ring-inset ring-og-accent\",\n // A thin progress shimmer on the expanding row — no per-node spinner.\n isLoading &&\n \"after:pointer-events-none after:absolute after:inset-x-1 after:bottom-0 after:h-px after:animate-pulse after:rounded-full after:bg-og-accent-soft\",\n node.status ? STATUS_TINT[node.status] : undefined,\n )}\n style={{ paddingLeft: `${depth * 12 + 4}px` }}\n >\n {isDir ? (\n <ChevronRightIcon\n className={cn(\n \"size-3 shrink-0 transition-transform\",\n isOpen && \"rotate-90\",\n isLoading && \"text-og-accent\",\n )}\n />\n ) : (\n <span className=\"inline-block w-3 shrink-0\" />\n )}\n {isDir ? (\n <FolderIcon className=\"size-3.5 shrink-0\" />\n ) : (\n <FileIcon className=\"size-3.5 shrink-0\" />\n )}\n <span className=\"truncate\">{node.name}</span>\n {supportsMutation && (\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label=\"More actions\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n setActive(node.path);\n const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n setMenu({ node, x: rect.right, y: rect.bottom });\n }}\n className=\"ml-auto hidden shrink-0 px-1 text-og-fg-subtle hover:text-og-fg group-hover:inline\"\n >\n ⋯\n </span>\n )}\n </button>\n )}\n </div>\n );\n };\n\n // Render one flat list item (node row or a synthetic create/skeleton/residue row).\n const renderItem = (item: RenderItem): ReactNode => {\n switch (item.type) {\n case \"node\":\n return renderNodeRow(item.node, item.depth);\n case \"create\":\n return (\n <InlineInput\n depth={item.depth}\n kind={item.kind}\n initialValue=\"\"\n onCommit={(name) => void commitCreate(name)}\n onCancel={() => setDraftCreate(null)}\n />\n );\n case \"skeleton\":\n return (\n <div\n role=\"group\"\n aria-hidden\n style={{ paddingLeft: `${item.depth * 12 + 4}px` }}\n className=\"space-y-1 py-1\"\n >\n {[0, 1, 2].map((i) => (\n <div\n key={i}\n className=\"h-2.5 animate-pulse rounded-og-sm bg-og-surface-2\"\n style={{ width: `${70 - i * 12}%` }}\n />\n ))}\n </div>\n );\n case \"residue\":\n return (\n <div\n style={{ paddingLeft: `${item.depth * 12 + 4}px` }}\n className=\"flex items-center gap-1.5 py-0.5 pr-1 text-og-xs italic text-og-fg-subtle\"\n >\n <span className=\"inline-block w-3 shrink-0\" />\n <span className=\"min-w-0 truncate\">contents on machine — open when live</span>\n </div>\n );\n }\n };\n\n // Keep the keyboard cursor's row on screen even though off-screen rows unmount.\n useEffect(() => {\n if (!cursor) return;\n const index = renderItems.findIndex((it) => it.type === \"node\" && it.node.path === cursor);\n if (index >= 0) vlistRef.current?.scrollToIndex(index);\n }, [cursor, renderItems]);\n\n return (\n <div className={cn(\"flex min-h-0 min-w-0 flex-col\", className)}>\n {showToolbar && (\n <div\n role=\"toolbar\"\n aria-label=\"File actions\"\n className=\"flex shrink-0 flex-wrap items-center gap-0.5 border-b border-og-border px-1 py-1\"\n >\n {supportsMutation ? (\n <>\n <ToolbarButton label=\"New file\" onClick={() => startCreate(\"file\")} disabled={busy}>\n <FilePlusIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton label=\"New folder\" onClick={() => startCreate(\"dir\")} disabled={busy}>\n <FolderPlusIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n label=\"Rename\"\n onClick={() => {\n const node = cursor ? findNode(result.tree, cursor) : undefined;\n if (node) startRename(node);\n }}\n disabled={busy || !cursor}\n >\n <PencilIcon className=\"size-3.5\" />\n </ToolbarButton>\n <ToolbarButton\n label=\"Delete\"\n onClick={(event) => {\n const node = cursor ? findNode(result.tree, cursor) : undefined;\n if (node) void runDelete(node, event.currentTarget);\n }}\n disabled={busy || !cursor}\n >\n <Trash2Icon className=\"size-3.5\" />\n </ToolbarButton>\n </>\n ) : null}\n <span className=\"ml-auto\" />\n <ToolbarButton\n label=\"Refresh\"\n onClick={() => void result.refresh()}\n disabled={busy || result.loading}\n >\n <RefreshCwIcon className={cn(\"size-3.5\", result.loading && \"animate-spin\")} />\n </ToolbarButton>\n </div>\n )}\n\n {showDegradedTree ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-opengeni-files-degraded\n className=\"flex shrink-0 items-center gap-2 border-b border-og-status-running/30 bg-og-status-running/10 px-2 py-1.5 text-og-xs text-og-fg-muted\"\n >\n <TriangleAlertIcon className=\"size-3.5 shrink-0 text-og-status-running\" aria-hidden />\n <span className=\"min-w-0 flex-1\">\n {result.source === \"capture\"\n ? \"Live files are temporarily unavailable. Showing the latest captured revision.\"\n : \"Live refresh failed. Showing the last loaded files.\"}\n </span>\n <button\n type=\"button\"\n onClick={() => void result.refresh()}\n disabled={result.loading}\n className=\"inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3\", result.loading && \"animate-spin motion-reduce:animate-none\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : null}\n\n {/* The tree itself. The root is a drop target so a node can be moved to \"\". */}\n {/* biome-ignore lint/a11y/noNoninteractiveTabindex: the tree owns keyboard nav */}\n <div\n ref={containerRef}\n id={usesVirtualTree ? undefined : treeId}\n role={usesVirtualTree ? undefined : \"tree\"}\n aria-label={usesVirtualTree ? undefined : \"Workspace files\"}\n aria-activedescendant={usesVirtualTree ? undefined : activeDescendantId}\n tabIndex={usesVirtualTree ? undefined : 0}\n aria-multiselectable={usesVirtualTree ? undefined : false}\n onFocus={(event) => {\n if ((event.target as HTMLElement).id === treeId && cursorIndex < 0) {\n const first = flatRows[0];\n if (first) setActive(first.node.path);\n }\n }}\n onKeyDown={onKeyDown}\n onDragOver={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDragOver(\"\");\n }\n : undefined\n }\n onDrop={\n supportsMutation\n ? (e) => {\n e.preventDefault();\n const src = e.dataTransfer.getData(\"text/og-path\");\n void onDropOnto(\"\", src);\n }\n : undefined\n }\n className={cn(\n \"flex min-h-0 min-w-0 flex-1 flex-col p-1 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent\",\n dragOver === \"\" && \"ring-1 ring-inset ring-og-accent\",\n )}\n data-opengeni-file-tree\n >\n {showErrorEmpty ? (\n <div className=\"flex flex-col items-start gap-2 p-2 text-og-sm text-og-fg-subtle\">\n <div>\n {fallback ??\n `Could not load files: ${result.error?.message ?? \"refresh the file list to try again\"}`}\n </div>\n <button\n type=\"button\"\n onClick={() => void result.refresh()}\n disabled={result.loading}\n className=\"inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3.5\", result.loading && \"animate-spin\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : showEmpty ? (\n <div className=\"p-2 text-og-sm text-og-fg-subtle\">\n {emptyState ?? \"This directory is empty\"}\n </div>\n ) : (\n // Virtualized: only the rows in/near the viewport mount, so a 3k-node\n // tree stays responsive. The flat item list already carries the\n // expanded subtree + synthetic (create/skeleton/residue) rows.\n <VList\n ref={vlistRef}\n id={treeId}\n role=\"tree\"\n aria-label=\"Workspace files\"\n aria-activedescendant={activeDescendantId}\n aria-multiselectable={false}\n tabIndex={0}\n className=\"min-h-0 flex-1 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent\"\n itemSize={coarsePointer ? 44 : 28}\n ssrCount={Math.min(32, renderItems.length)}\n >\n {renderItems.map((item) => (\n <div key={itemKey(item)} className=\"min-w-0\">\n {renderItem(item)}\n </div>\n ))}\n </VList>\n )}\n </div>\n\n {menu && menu.node && (\n <ContextMenu\n node={menu.node}\n x={menu.x}\n y={menu.y}\n onRename={() => menu.node && startRename(menu.node)}\n onDelete={() => menu.node && void runDelete(menu.node)}\n onNewFile={() => startCreate(\"file\")}\n onNewFolder={() => startCreate(\"dir\")}\n onClose={() => {\n setMenu(null);\n window.requestAnimationFrame(() => getTreeElement()?.focus());\n }}\n />\n )}\n {pendingDelete ? (\n <DeleteDialog\n node={pendingDelete}\n busy={busy}\n portalTokenStyle={portalTokenStyle}\n onCancel={() => closePendingDelete(true)}\n onConfirm={() => {\n const node = pendingDelete;\n closePendingDelete(false);\n void performDelete(node);\n }}\n />\n ) : null}\n </div>\n );\n}\n\nfunction DeleteDialog({\n node,\n busy,\n portalTokenStyle,\n onCancel,\n onConfirm,\n}: {\n node: FileTreeNode;\n busy: boolean;\n portalTokenStyle: PortalTokenStyle;\n onCancel: () => void;\n onConfirm: () => void;\n}) {\n const cancelRef = useRef<HTMLButtonElement | null>(null);\n const [open, setOpen] = useState(true);\n const closeIntentRef = useRef<\"cancel\" | \"confirm\">(\"cancel\");\n const settledRef = useRef(false);\n const settleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const settle = () => {\n if (settledRef.current) return;\n settledRef.current = true;\n if (closeIntentRef.current === \"confirm\") {\n onConfirm();\n } else {\n onCancel();\n }\n };\n\n const scheduleSettle = () => {\n if (settleTimerRef.current !== null) return;\n settleTimerRef.current = setTimeout(() => {\n settleTimerRef.current = null;\n settle();\n }, 0);\n };\n\n useEffect(\n () => () => {\n if (settleTimerRef.current !== null) clearTimeout(settleTimerRef.current);\n },\n [],\n );\n\n return (\n <AlertDialog.Root\n open={open}\n onOpenChange={(nextOpen) => {\n if (!nextOpen && !busy) {\n setOpen(false);\n scheduleSettle();\n }\n }}\n >\n <AlertDialog.Portal container={typeof document === \"undefined\" ? null : document.body}>\n <AlertDialog.Overlay className=\"fixed inset-0 z-[100] bg-black/45 backdrop-blur-[2px]\" />\n <AlertDialog.Content\n className=\"og-root fixed left-1/2 top-1/2 z-[101] max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-sm -translate-x-1/2 -translate-y-1/2 overflow-y-auto rounded-og-lg border border-og-border-strong bg-og-surface-1 p-4 text-og-fg shadow-2xl outline-none\"\n style={portalTokenStyle}\n onOpenAutoFocus={(event) => {\n event.preventDefault();\n cancelRef.current?.focus();\n }}\n onCloseAutoFocus={(event) => {\n event.preventDefault();\n scheduleSettle();\n }}\n onEscapeKeyDown={(event) => {\n event.stopPropagation();\n if (busy) {\n event.preventDefault();\n } else {\n closeIntentRef.current = \"cancel\";\n }\n }}\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-full bg-og-status-failed/12 text-og-status-failed\">\n <Trash2Icon className=\"size-4\" aria-hidden />\n </span>\n <div className=\"min-w-0\">\n <AlertDialog.Title className=\"text-og-sm font-semibold\">\n Delete {node.kind === \"dir\" ? \"folder\" : \"file\"}?\n </AlertDialog.Title>\n <AlertDialog.Description className=\"mt-1 text-og-sm leading-relaxed text-og-fg-muted\">\n <span className=\"break-all font-og-mono text-og-fg\">{node.path}</span>\n {node.kind === \"dir\"\n ? \" and everything inside it will be permanently removed.\"\n : \" will be permanently removed.\"}\n </AlertDialog.Description>\n </div>\n </div>\n <div className=\"mt-4 flex justify-end gap-2\">\n <AlertDialog.Cancel asChild>\n <button\n ref={cancelRef}\n type=\"button\"\n onClick={() => {\n closeIntentRef.current = \"cancel\";\n }}\n disabled={busy}\n className=\"min-h-9 rounded-og-md border border-og-border px-3 text-og-sm font-medium text-og-fg hover:bg-og-surface-2 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Cancel\n </button>\n </AlertDialog.Cancel>\n <AlertDialog.Action asChild>\n <button\n type=\"button\"\n onClick={() => {\n closeIntentRef.current = \"confirm\";\n }}\n disabled={busy}\n className=\"min-h-9 rounded-og-md bg-og-status-failed px-3 text-og-sm font-semibold text-white shadow-sm hover:brightness-110 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Delete permanently\n </button>\n </AlertDialog.Action>\n </div>\n </AlertDialog.Content>\n </AlertDialog.Portal>\n </AlertDialog.Root>\n );\n}\n\nfunction ToolbarButton({\n label,\n onClick,\n disabled,\n children,\n}: {\n label: string;\n onClick: (event: ReactMouseEvent<HTMLButtonElement>) => void;\n disabled?: boolean;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n title={label}\n aria-label={label}\n onClick={onClick}\n disabled={disabled}\n className={cn(\n \"inline-flex size-7 items-center justify-center rounded-og-sm p-1 text-og-fg-muted max-[1023px]:size-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n \"hover:bg-og-surface-2 hover:text-og-fg\",\n \"disabled:cursor-not-allowed disabled:opacity-40\",\n )}\n >\n {children}\n </button>\n );\n}\n\n/** An inline text input for create / rename, indented to match its row depth. */\nfunction InlineInput({\n depth,\n kind,\n initialValue,\n onCommit,\n onCancel,\n}: {\n depth: number;\n kind: \"file\" | \"dir\";\n initialValue: string;\n onCommit: (value: string) => void;\n onCancel: () => void;\n}) {\n const [value, setValue] = useState(initialValue);\n const ref = useRef<HTMLInputElement | null>(null);\n // Track whether we've already resolved so a blur after Enter/Escape is a no-op.\n const doneRef = useRef(false);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n el.focus();\n // Select the basename (excluding any extension) for a rename, like an IDE.\n const dot = initialValue.lastIndexOf(\".\");\n if (initialValue && dot > 0) el.setSelectionRange(0, dot);\n else el.select();\n }, [initialValue]);\n\n const finish = (commit: boolean) => {\n if (doneRef.current) return;\n doneRef.current = true;\n if (commit) onCommit(value);\n else onCancel();\n };\n\n return (\n <div\n className=\"flex items-center gap-1 px-1 py-0.5\"\n style={{ paddingLeft: `${depth * 12 + 4}px` }}\n >\n <span className=\"inline-block w-3 shrink-0\" />\n {kind === \"dir\" ? (\n <FolderIcon className=\"size-3.5 shrink-0\" />\n ) : (\n <FileIcon className=\"size-3.5 shrink-0\" />\n )}\n <input\n ref={ref}\n value={value}\n spellCheck={false}\n autoComplete=\"off\"\n aria-label={kind === \"dir\" ? \"Folder name\" : \"File name\"}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => finish(true)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n finish(true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n finish(false);\n }\n e.stopPropagation();\n }}\n className={cn(\n \"min-w-0 flex-1 rounded-og-sm border bg-og-bg px-1 py-0 text-og-sm\",\n \"border-og-accent text-og-fg\",\n \"outline-none\",\n )}\n />\n </div>\n );\n}\n\n/** The right-click / kebab context menu, positioned at the click point. */\nfunction ContextMenu({\n node,\n x,\n y,\n onRename,\n onDelete,\n onNewFile,\n onNewFolder,\n onClose,\n}: {\n node: FileTreeNode;\n x: number;\n y: number;\n onRename: () => void;\n onDelete: () => void;\n onNewFile: () => void;\n onNewFolder: () => void;\n onClose: () => void;\n}) {\n const isDir = node.kind === \"dir\";\n const menuRef = useRef<HTMLDivElement | null>(null);\n // Keep the menu on-screen when opened near the viewport edge.\n const vw = typeof window !== \"undefined\" ? window.innerWidth : 9999;\n const vh = typeof window !== \"undefined\" ? window.innerHeight : 9999;\n const left = Math.max(4, Math.min(x, vw - 180));\n const top = Math.max(4, Math.min(y, vh - 160));\n\n useEffect(() => {\n menuRef.current?.querySelector<HTMLButtonElement>('[role=\"menuitem\"]')?.focus();\n }, []);\n\n const onKeyDown = (event: ReactKeyboardEvent<HTMLDivElement>) => {\n const items = Array.from(\n menuRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"menuitem\"]') ?? [],\n );\n if (items.length === 0) return;\n const current = items.findIndex((item) => item === document.activeElement);\n let next: number | null = null;\n if (event.key === \"ArrowDown\") next = (current + 1 + items.length) % items.length;\n else if (event.key === \"ArrowUp\") next = (current - 1 + items.length) % items.length;\n else if (event.key === \"Home\") next = 0;\n else if (event.key === \"End\") next = items.length - 1;\n else if (event.key === \"Escape\") {\n event.preventDefault();\n event.stopPropagation();\n onClose();\n return;\n }\n if (next === null) return;\n event.preventDefault();\n event.stopPropagation();\n items[next]?.focus();\n };\n\n const item = (label: string, icon: ReactNode, onClick: () => void, danger?: boolean) => (\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={(e) => {\n e.stopPropagation();\n onClick();\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-2.5 py-1 text-left text-og-sm pointer-coarse:min-h-11\",\n \"hover:bg-og-surface-2\",\n danger ? \"text-og-status-failed\" : \"text-og-fg\",\n )}\n >\n {icon}\n {label}\n </button>\n );\n\n return (\n <div\n ref={menuRef}\n role=\"menu\"\n aria-label={`Actions for ${node.name}`}\n onKeyDown={onKeyDown}\n onClick={(e) => e.stopPropagation()}\n style={{ left, top }}\n className={cn(\n \"fixed z-50 min-w-[160px] overflow-hidden rounded-md border py-1 shadow-lg\",\n \"border-og-border\",\n \"bg-og-surface-1\",\n )}\n >\n {isDir && (\n <>\n {item(\"New file\", <FilePlusIcon className=\"size-3.5\" />, onNewFile)}\n {item(\"New folder\", <FolderPlusIcon className=\"size-3.5\" />, onNewFolder)}\n <div role=\"separator\" className=\"my-1 h-px bg-og-border\" />\n </>\n )}\n {item(\"Rename\", <PencilIcon className=\"size-3.5\" />, onRename)}\n {item(\"Delete\", <Trash2Icon className=\"size-3.5\" />, onDelete, true)}\n </div>\n );\n}\n","import { useEffect, useMemo } from \"react\";\n\nexport type UnicodeFontNeeds = {\n japanese: boolean;\n arabic: boolean;\n};\n\nconst japaneseScript = /[\\u3040-\\u30ff\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff]/u;\nconst arabicScript = /[\\u0600-\\u06ff\\u0750-\\u077f\\u0870-\\u08ff\\ufb50-\\ufdff\\ufe70-\\ufefc]/u;\n\nexport function detectUnicodeFontNeeds(values: Iterable<string>): UnicodeFontNeeds {\n let japanese = false;\n let arabic = false;\n for (const value of values) {\n japanese ||= japaneseScript.test(value);\n arabic ||= arabicScript.test(value);\n if (japanese && arabic) break;\n }\n return { japanese, arabic };\n}\n\n/**\n * Load large script-specific font manifests only when visible workspace content\n * needs them. Fontsource splits the actual glyphs by unicode-range, so Chromium\n * then fetches only the small shards used by the path instead of charging every\n * Latin-only session for the complete font.\n */\nexport function useUnicodeFallbackFonts(values: readonly string[]): void {\n const content = useMemo(() => collectUnicodeContent(values), [values]);\n useEffect(() => {\n if (content.japanese) {\n void import(\"./noto-sans-jp-loader.generated\")\n .then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese))\n .catch(() => undefined);\n }\n if (content.arabic) {\n void import(\"./noto-sans-arabic-loader\")\n .then(({ loadNotoSansArabic }) => loadNotoSansArabic())\n .catch(() => undefined);\n }\n }, [content.arabic, content.japanese]);\n}\n\nfunction collectUnicodeContent(values: Iterable<string>): {\n japanese: string;\n arabic: string;\n} {\n const japanese = new Set<string>();\n const arabic = new Set<string>();\n for (const value of values) {\n for (const character of value) {\n if (japaneseScript.test(character)) japanese.add(character);\n if (arabicScript.test(character)) arabic.add(character);\n }\n }\n return { japanese: [...japanese].join(\"\"), arabic: [...arabic].join(\"\") };\n}\n","import type { GitFileDiff } from \"@opengeni/sdk\";\nimport { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from \"lucide-react\";\nimport {\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { VList } from \"virtua\";\nimport { cn } from \"../lib/cn\";\nimport { useUnicodeFallbackFonts } from \"../lib/use-unicode-fonts\";\nimport { useThemeType } from \"../lib/use-theme-type\";\nimport { formatAsOf } from \"../hooks/use-machine-chip\";\nimport type { SandboxGitFileDiff } from \"../hooks/use-sandbox-git\";\nimport { useWindowedSections } from \"../hooks/use-windowed-sections\";\nimport { PierreDiff } from \"./pierre-diff\";\n\n/* ----------------------------------------------------------------------------\n Changes tab — a PR-review surface.\n\n A file rail (status glyph, ±counts, grouped by repository when metadata is\n available, otherwise by top-level directory for a large legacy input) on the\n left; a stacked, WINDOWED diff pane on the right. Only\n the file sections inside the visible window ± overscan mount a Pierre/Shiki\n highlighter (the one renderer), so a 40- or 400-file change set never mounts N\n highlighters at once (D2). A per-file size guard (binary / diff too\n large) degrades to an \"open live\" affordance instead of a diff body.\n\n Workspace-wide Git results carry an explicit `repoRoot`, so grouping never\n guesses repository ownership from a directory name. Multi-repo results group\n at every size; legacy unscoped inputs group by top-level directory only past\n the threshold.\n -------------------------------------------------------------------------- */\n\n/** Group the rail (and reorder the pane) once the change set is larger than this. */\nconst GROUP_THRESHOLD = 20;\n/** Mount this many sections beyond the visible window on each side. */\nconst OVERSCAN = 2;\nconst HEADER_PX = 30;\nconst LINE_PX = 18;\nconst GUARD_BODY_PX = 52;\nconst COMPACT_SURFACE_PX = 560;\nconst useChangesLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nconst STATUS_TINT: Record<GitFileDiff[\"status\"], string> = {\n added: \"text-og-status-idle\",\n modified: \"text-og-status-running\",\n deleted: \"text-og-status-failed\",\n renamed: \"text-og-accent\",\n copied: \"text-og-accent\",\n untracked: \"text-og-fg-subtle\",\n ignored: \"text-og-fg-subtle\",\n conflicted: \"text-og-status-failed\",\n typechange: \"text-og-status-running\",\n};\n\nconst STATUS_LETTER: Record<GitFileDiff[\"status\"], string> = {\n added: \"A\",\n modified: \"M\",\n deleted: \"D\",\n renamed: \"R\",\n copied: \"C\",\n untracked: \"U\",\n ignored: \"I\",\n conflicted: \"!\",\n typechange: \"T\",\n};\n\nfunction compactFileLabel(file: SandboxGitFileDiff): string {\n const slash = file.path.lastIndexOf(\"/\");\n const basename = slash >= 0 ? file.path.slice(slash + 1) : file.path;\n const parent = slash >= 0 ? file.path.slice(0, slash + 1) : \"\";\n return `${STATUS_LETTER[file.status]} · ${basename}${parent ? ` — ${parent}` : \"\"}`;\n}\n\nexport type WorkbenchChangesProps = {\n /** The changed files (`useSandboxGit().diff`). Assumed non-empty by the caller. */\n diff: SandboxGitFileDiff[];\n /** Which source the diff came from — drives the \"live\" vs \"as of turn\" badge. */\n source: \"live\" | \"capture\" | null;\n /** When the served capture was taken (ISO), when `source === \"capture\"`. */\n capturedAt: string | null;\n /** The capture's turn revision, for the \"as of turn N\" badge. */\n captureRevision?: number | null | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Route a guarded binary/oversize file into the live Files surface. */\n onOpenFile?: ((path: string) => void) | undefined;\n className?: string | undefined;\n};\n\ntype RailRow =\n | { kind: \"group\"; label: string; count: number }\n | { kind: \"file\"; file: SandboxGitFileDiff; index: number };\n\n/** Order the files and build the rail rows (grouped past the threshold). The\n * returned `orderedFiles` drives BOTH the rail and the diff pane so a rail row's\n * `index` addresses the matching pane section. Exported for tests. */\nexport function buildRail(files: SandboxGitFileDiff[]): {\n orderedFiles: SandboxGitFileDiff[];\n rows: RailRow[];\n grouped: boolean;\n} {\n const roots = new Set(\n files.flatMap((file) => (file.repoRoot === undefined ? [] : [file.repoRoot])),\n );\n const groupByRepo = roots.size > 1;\n if (!groupByRepo && files.length <= GROUP_THRESHOLD) {\n return {\n orderedFiles: files,\n rows: files.map((file, index) => ({ kind: \"file\", file, index })),\n grouped: false,\n };\n }\n const groups = new Map<string, SandboxGitFileDiff[]>();\n for (const file of files) {\n const slash = file.path.indexOf(\"/\");\n const key = groupByRepo\n ? file.repoRoot || \"(workspace)\"\n : slash > 0\n ? file.path.slice(0, slash)\n : \"(root)\";\n const bucket = groups.get(key);\n if (bucket) bucket.push(file);\n else groups.set(key, [file]);\n }\n const labels = [...groups.keys()].sort((a, b) => a.localeCompare(b));\n const orderedFiles: SandboxGitFileDiff[] = [];\n const rows: RailRow[] = [];\n for (const label of labels) {\n const bucket = (groups.get(label) ?? []).slice().sort((a, b) => a.path.localeCompare(b.path));\n rows.push({ kind: \"group\", label, count: bucket.length });\n for (const file of bucket) {\n rows.push({ kind: \"file\", file, index: orderedFiles.length });\n orderedFiles.push(file);\n }\n }\n return { orderedFiles, rows, grouped: true };\n}\n\n/** A file's diff body is \"too large to inline\" when it's binary or the diff guard\n * tripped (the backend omits hunks past its size cap) — open it live instead. */\nfunction isGuarded(file: GitFileDiff): boolean {\n return file.isBinary || file.truncated;\n}\n\n/** Estimate a section's rendered height for the windowing math. Line-count based,\n * so it is close enough that the first paint barely shifts as real heights land. */\nfunction estimateSectionHeight(file: GitFileDiff): number {\n if (isGuarded(file)) return HEADER_PX + GUARD_BODY_PX;\n let lines = 0;\n for (const hunk of file.hunks) lines += hunk.lines.length + 1;\n return HEADER_PX + Math.max(lines, 1) * LINE_PX + 8;\n}\n\nexport function WorkbenchChanges({\n diff,\n source,\n capturedAt,\n captureRevision,\n themeType,\n onOpenFile,\n className,\n}: WorkbenchChangesProps) {\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [compact, setCompact] = useState(false);\n const [coarsePointer, setCoarsePointer] = useState(false);\n const resolvedTheme = useThemeType(themeType);\n const [layout, setLayout] = useState<\"unified\" | \"split\">(\"unified\");\n const [activePath, setActivePath] = useState<string | null>(null);\n const pickerId = useId();\n\n const unicodeFontPaths = useMemo(\n () => diff.flatMap((file) => (file.oldPath ? [file.path, file.oldPath] : [file.path])),\n [diff],\n );\n useUnicodeFallbackFonts(unicodeFontPaths);\n\n const { orderedFiles, rows, grouped } = useMemo(() => buildRail(diff), [diff]);\n const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);\n const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;\n\n useChangesLayoutEffect(() => {\n const root = rootRef.current;\n if (!root || typeof ResizeObserver === \"undefined\") return;\n const update = (width: number) => {\n // DOM test environments report a zero geometry. Keep the deterministic\n // desktop/SSR branch until the observer has a real measurement.\n if (width > 0) setCompact(width < COMPACT_SURFACE_PX);\n };\n update(root.getBoundingClientRect().width);\n const observer = new ResizeObserver((entries) => {\n update(entries[0]?.contentRect.width ?? root.getBoundingClientRect().width);\n });\n observer.observe(root);\n return () => observer.disconnect();\n }, []);\n\n useChangesLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return;\n const query = window.matchMedia(\"(pointer: coarse)\");\n const update = () => setCoarsePointer(query.matches);\n update();\n if (typeof query.addEventListener === \"function\") {\n query.addEventListener(\"change\", update);\n return () => query.removeEventListener(\"change\", update);\n }\n if (typeof query.addListener === \"function\") {\n query.addListener(update);\n return () => query.removeListener(update);\n }\n }, []);\n\n const additions = useMemo(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);\n const deletions = useMemo(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);\n\n const estimateHeight = useCallback(\n (index: number) => {\n const file = orderedFiles[index];\n return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;\n },\n [orderedFiles],\n );\n\n const windowed = useWindowedSections({\n count: orderedFiles.length,\n estimateHeight,\n overscan: OVERSCAN,\n });\n\n const jumpTo = useCallback(\n (index: number) => {\n setActivePath(orderedFiles[index]?.path ?? null);\n windowed.scrollToIndex(index);\n },\n [orderedFiles, windowed],\n );\n\n // Track which section is at the top of the pane so the rail highlights it.\n const onPaneScroll = useCallback(() => {\n const el = windowed.scrollRef.current;\n if (!el) {\n return;\n }\n const top = el.scrollTop;\n const offsets = windowed.offsets;\n let idx = 0;\n for (let i = 0; i < orderedFiles.length; i++) {\n if ((offsets[i] ?? 0) <= top + 4) idx = i;\n else break;\n }\n const nextPath = orderedFiles[idx]?.path ?? null;\n setActivePath((previous) => (previous === nextPath ? previous : nextPath));\n }, [windowed, orderedFiles]);\n\n useEffect(() => {\n const el = windowed.scrollRef.current;\n if (!el) return;\n el.addEventListener(\"scroll\", onPaneScroll, { passive: true });\n return () => el.removeEventListener(\"scroll\", onPaneScroll);\n }, [windowed.scrollRef, onPaneScroll]);\n\n const sourceBadge = describeSource(source, capturedAt, captureRevision ?? null);\n const activeFile = orderedFiles[activeIndex] ?? orderedFiles[0];\n\n return (\n <div\n ref={rootRef}\n className={cn(\"flex h-full min-h-0 min-w-0 flex-col\", className)}\n data-workbench-changes-layout={compact ? \"compact\" : \"rail\"}\n >\n {/* Summary + source badge + layout toggle. */}\n <div\n className={cn(\n \"flex shrink-0 items-center justify-between gap-2 border-b border-og-border px-3 py-1.5\",\n compact && source === \"capture\" && \"flex-col items-stretch gap-1.5 py-2\",\n )}\n >\n <span\n role=\"status\"\n aria-atomic=\"true\"\n aria-live=\"polite\"\n data-contrast-audited\n className=\"min-w-0 text-og-xs text-og-fg-muted\"\n >\n {diff.length} {diff.length === 1 ? \"file\" : \"files\"} changed\n <span data-contrast-audited className=\"ml-2 text-og-status-idle\">\n +{additions}\n </span>\n <span data-contrast-audited className=\"ml-1 text-og-status-failed\">\n −{deletions}\n </span>\n </span>\n <div\n className={cn(\n \"flex shrink-0 items-center gap-2\",\n compact && source === \"capture\" && \"self-start\",\n )}\n >\n {compact ? null : <LayoutToggle layout={layout} onChange={setLayout} />}\n <SourceBadge source={source} capturedAt={capturedAt} label={sourceBadge} />\n </div>\n </div>\n\n {compact ? (\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div className=\"shrink-0 border-b border-og-border bg-og-surface-1 p-2\">\n <label className=\"sr-only\" htmlFor={pickerId}>\n Changed file\n </label>\n <select\n id={pickerId}\n aria-label=\"Changed file\"\n value={activeIndex}\n onChange={(event) => {\n const index = Number(event.currentTarget.value);\n setActivePath(orderedFiles[index]?.path ?? null);\n }}\n title={activeFile?.path}\n data-compact-file-picker\n className=\"h-11 w-full rounded-og-md border border-og-border bg-og-bg px-3 font-og-mono text-og-sm text-og-fg outline-none transition-colors focus:border-og-accent focus:ring-2 focus:ring-og-accent-soft\"\n >\n {orderedFiles.map((file, index) => (\n <option key={file.path} value={index}>\n {compactFileLabel(file)}\n </option>\n ))}\n </select>\n </div>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-auto\" data-opengeni-changes-pane>\n {activeFile ? (\n <DiffSection\n file={activeFile}\n layout=\"unified\"\n themeType={resolvedTheme}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n ) : null}\n </div>\n </div>\n ) : (\n <div className=\"flex min-h-0 flex-1\">\n {/* File rail — virtualized (virtua): a dense change set is fine. */}\n <div className=\"w-[clamp(12rem,28%,15rem)] shrink-0 border-r border-og-border bg-og-surface-1/35\">\n <VList\n className=\"h-full\"\n itemSize={coarsePointer ? 44 : 28}\n ssrCount={Math.min(30, rows.length)}\n >\n {rows.map((row) =>\n row.kind === \"group\" ? (\n <div\n key={`g:${row.label}`}\n data-rail-group\n className=\"flex items-center gap-1.5 px-2 pb-0.5 pt-2 text-og-xs font-medium uppercase tracking-wide text-og-fg-subtle\"\n >\n <span className=\"min-w-0 truncate\">{row.label}</span>\n <span className=\"shrink-0\">{row.count}</span>\n </div>\n ) : (\n <RailFileRow\n key={row.file.path}\n file={row.file}\n grouped={grouped}\n active={row.index === activeIndex}\n onClick={() => jumpTo(row.index)}\n />\n ),\n )}\n </VList>\n </div>\n\n {/* Diff pane — windowed file sections. Only the sections inside the\n visible window ± overscan are in the DOM; the container reserves the\n full scroll height so scrolling + the rail-jump stay accurate. */}\n <div\n ref={windowed.scrollRef}\n className=\"min-h-0 min-w-0 flex-1 overflow-auto\"\n data-opengeni-changes-pane\n >\n <div style={{ position: \"relative\", height: windowed.totalHeight }}>\n {orderedFiles.map((file, index) => {\n if (index < windowed.range.start || index >= windowed.range.end) return null;\n return (\n <MeasuredSection\n key={file.path}\n index={index}\n top={windowed.offsets[index] ?? 0}\n onMeasure={windowed.measure}\n >\n <DiffSection\n file={file}\n layout={layout}\n themeType={resolvedTheme}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n </MeasuredSection>\n );\n })}\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n/**\n * The data-source badge. \"live\" stays understated (the header chip already\n * carries machine liveness) — but a capture-served diff is honestly labelled\n * historical: a muted clock + the \"as of turn N · <time>\" so the reviewer always\n * knows they are looking at a turn-end snapshot, not the live tree.\n */\nfunction SourceBadge({\n source,\n capturedAt,\n label,\n}: {\n source: \"live\" | \"capture\" | null;\n capturedAt: string | null;\n label: string;\n}) {\n if (source === \"capture\" && capturedAt) {\n return (\n <span\n className=\"inline-flex items-center gap-1 rounded-og-xs border border-og-border px-1.5 py-px text-og-xs text-og-fg-muted\"\n title={new Date(capturedAt).toLocaleString()}\n >\n <HistoryIcon className=\"size-3 shrink-0 text-og-status-running\" aria-hidden />\n {label}\n </span>\n );\n }\n return (\n <span className=\"rounded-og-xs bg-og-surface-2 px-1.5 py-px text-og-xs text-og-fg-subtle\">\n {label}\n </span>\n );\n}\n\n/** \"Live diff\" · \"as of turn 7 · 14:32\" · \"as of 14:32\" (no revision). */\nfunction describeSource(\n source: \"live\" | \"capture\" | null,\n capturedAt: string | null,\n revision: number | null,\n): string {\n if (source !== \"capture\" || !capturedAt) return \"Live diff\";\n const time = formatAsOf(capturedAt, Date.now());\n return revision !== null ? `as of turn ${revision} · ${time}` : `as of ${time}`;\n}\n\nfunction RailFileRow({\n file,\n grouped,\n active,\n onClick,\n}: {\n file: SandboxGitFileDiff;\n grouped: boolean;\n active: boolean;\n onClick: () => void;\n}) {\n // A real repository group strips the exact repo root. Legacy top-directory\n // grouping strips only that first segment.\n const repoPrefix = file.repoRoot ? `${file.repoRoot}/` : null;\n const shown = grouped\n ? file.repoRoot !== undefined\n ? repoPrefix && file.path.startsWith(repoPrefix)\n ? file.path.slice(repoPrefix.length)\n : file.path\n : file.path.slice(file.path.indexOf(\"/\") + 1) || file.path\n : file.path;\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={file.path}\n data-rail-file\n className={cn(\n \"relative flex min-h-7 w-full items-center gap-1.5 truncate px-2 py-0.5 text-left text-og-sm transition-colors hover:bg-og-surface-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent pointer-coarse:min-h-11\",\n grouped && \"pl-3\",\n active &&\n \"bg-og-accent-soft text-og-fg before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-r-full before:bg-og-accent\",\n )}\n >\n <span\n data-contrast-audited\n className={cn(\"w-3 shrink-0 text-center font-og-mono text-og-xs\", STATUS_TINT[file.status])}\n >\n {STATUS_LETTER[file.status]}\n </span>\n <span className=\"min-w-0 flex-1 truncate\">{shown}</span>\n <span className=\"ml-auto flex shrink-0 items-center gap-1 pl-1 font-og-mono text-og-xs\">\n <span data-contrast-audited className=\"text-og-status-idle\">\n +{file.additions}\n </span>\n <span data-contrast-audited className=\"text-og-status-failed\">\n −{file.deletions}\n </span>\n </span>\n </button>\n );\n}\n\n/** A windowed section wrapper. Absolutely positioned at `top`; a ResizeObserver\n * reports its real height back so the layout refines as Pierre's async Shiki\n * render grows (else short estimates would overlap sections). */\nfunction MeasuredSection({\n index,\n top,\n onMeasure,\n children,\n}: {\n index: number;\n top: number;\n onMeasure: (index: number, height: number) => void;\n children: ReactNode;\n}) {\n const ref = useRef<HTMLDivElement>(null);\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const report = () => onMeasure(index, el.offsetHeight);\n report();\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(report) : null;\n ro?.observe(el);\n return () => ro?.disconnect();\n }, [index, onMeasure]);\n return (\n <div\n ref={ref}\n data-diff-section\n data-diff-index={index}\n style={{ position: \"absolute\", top, left: 0, right: 0 }}\n >\n {children}\n </div>\n );\n}\n\nfunction DiffSection({\n file,\n layout,\n themeType,\n onOpenFile,\n}: {\n file: GitFileDiff;\n layout: \"unified\" | \"split\";\n themeType: \"dark\" | \"light\";\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n // The guard files (binary / over-cap) get a minimal header + \"open live\" body\n // since Pierre has nothing to render; real diffs use Pierre's own sticky file\n // header (one header — no redundant chrome).\n if (isGuarded(file)) {\n const renamed = file.oldPath && file.oldPath !== file.path;\n return (\n <section className=\"border-b border-og-border pb-1\">\n <header className=\"flex items-center justify-between gap-2 bg-og-surface-1 px-3 py-1.5 text-og-sm\">\n <span className=\"min-w-0 truncate font-og-mono text-og-xs\">\n {renamed ? `${file.oldPath} → ${file.path}` : file.path}\n </span>\n <span className=\"flex shrink-0 items-center gap-2 font-og-mono text-og-xs\">\n <span className=\"text-og-status-idle\">+{file.additions}</span>\n <span className=\"text-og-status-failed\">−{file.deletions}</span>\n </span>\n </header>\n <GuardBody file={file} {...(onOpenFile ? { onOpenFile } : {})} />\n </section>\n );\n }\n return (\n <section data-pierre-section className=\"border-b border-og-border pb-2\">\n <PierreDiff diff={[file]} layout={layout} themeType={themeType} className=\"px-1\" />\n </section>\n );\n}\n\n/** The per-file guard: a binary file or an over-cap diff opens live rather than\n * inlining a body we don't (fully) have. */\nfunction GuardBody({\n file,\n onOpenFile,\n}: {\n file: GitFileDiff;\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n const reason = file.isBinary ? \"Binary file\" : \"Diff too large to show here\";\n return (\n <div className=\"flex flex-wrap items-center gap-2 px-3 py-3 text-og-sm text-og-fg-subtle\">\n <FileWarningIcon className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"min-w-0 flex-1\">{reason}.</span>\n {onOpenFile ? (\n <button\n type=\"button\"\n onClick={() => onOpenFile(file.path)}\n className=\"inline-flex min-h-8 shrink-0 items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-11\"\n >\n Open in Files\n <ArrowUpRightIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n </div>\n );\n}\n\nfunction LayoutToggle({\n layout,\n onChange,\n}: {\n layout: \"unified\" | \"split\";\n onChange: (next: \"unified\" | \"split\") => void;\n}) {\n return (\n <div className=\"inline-flex items-center rounded-og-sm border border-og-border p-0.5\">\n {([\"unified\", \"split\"] as const).map((value) => (\n <button\n key={value}\n type=\"button\"\n onClick={() => onChange(value)}\n className={cn(\n \"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs capitalize focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n layout === value\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n {value}\n </button>\n ))}\n </div>\n );\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\n/* ----------------------------------------------------------------------------\n Windowed sections — variable-height file-section virtualization.\n\n The Changes tab stacks N file diffs in one scroll pane. Mounting N Shiki\n highlighters (Pierre's `PatchDiff`) at once is the exact wart this replaces,\n so we mount only the sections inside the visible window ± overscan and reserve\n the rest as empty space (D2).\n\n Why NOT `virtua` here (unlike the file tree/rail): Pierre renders each diff in\n a shadow DOM with its own internal overflow/virtualization, so an outer\n measuring virtualizer can read a collapsed or fixed height and lay sections\n out wrong (known layout risk). This hook windows by mounting/unmounting whole\n file sections off a scroll offset and a per-section height ESTIMATE (refined\n by measurement when a real browser provides it), which never depends on\n Pierre reporting its own height — and the math is pure + deterministic, so\n D2 is provable in both happy-dom (drive scrollTop/clientHeight) and a real\n browser (Playwright, the verification-matrix path).\n -------------------------------------------------------------------------- */\n\nexport type WindowRange = {\n /** First mounted index (inclusive). */\n start: number;\n /** One past the last mounted index (exclusive). */\n end: number;\n};\n\n/**\n * Pure windowing math: given each section's height (px), the scroll offset, the\n * viewport height, and an overscan count, return the `[start, end)` index window\n * to mount. Robust to a zero viewport (a headless/pre-layout pass) — it then\n * mounts a small overscan window anchored at the item under `scrollTop` rather\n * than the whole list.\n */\nexport function computeWindowRange(\n heights: readonly number[],\n scrollTop: number,\n viewport: number,\n overscan: number,\n): WindowRange {\n const n = heights.length;\n if (n === 0) return { start: 0, end: 0 };\n const top = Math.max(scrollTop, 0);\n const bottom = top + Math.max(viewport, 0);\n\n let firstVisible = -1;\n let lastVisible = -1;\n let acc = 0;\n let anchor = 0; // the item containing `scrollTop` — the zero-viewport fallback\n for (let i = 0; i < n; i++) {\n const secTop = acc;\n const secBottom = secTop + Math.max(heights[i] ?? 0, 0);\n if (secTop <= top) anchor = i;\n // A section is visible when it overlaps [top, bottom); with viewport 0 the\n // half-open interval is empty, so nothing matches and we fall back to anchor.\n if (secBottom > top && secTop < bottom) {\n if (firstVisible === -1) firstVisible = i;\n lastVisible = i;\n }\n acc = secBottom;\n }\n\n if (firstVisible === -1) {\n firstVisible = anchor;\n lastVisible = anchor;\n }\n\n return {\n start: Math.max(0, firstVisible - overscan),\n end: Math.min(n, lastVisible + 1 + overscan),\n };\n}\n\nexport type WindowedSections = {\n /** Attach to the scroll container. */\n scrollRef: React.RefObject<HTMLDivElement | null>;\n /** The mounted window `[start, end)`. */\n range: WindowRange;\n /** Prefix offsets: `offsets[i]` is the top px of section `i`; `offsets[count]`\n * is the total content height. */\n offsets: number[];\n /** Total content height (px) — set on the spacer so scroll length is stable. */\n totalHeight: number;\n /** Report a MOUNTED section's real height (px) so the layout refines as async\n * content — e.g. Pierre's Shiki render — grows. Drive it from a ResizeObserver;\n * a zero/idle value is ignored so a headless pass keeps the estimate. */\n measure: (index: number, height: number) => void;\n /** Scroll section `index` to the top of the pane (the rail-jump). */\n scrollToIndex: (index: number) => void;\n};\n\n/**\n * Variable-height section windowing over a scroll container. Seeds every section\n * to `estimateHeight(i)`, mounts only `range`, and refines heights from real\n * measurements (a no-op in a zero-layout headless pass, which keeps the estimate\n * — deterministic for tests). Pass a STABLE `estimateHeight` (wrap in useCallback).\n */\nexport function useWindowedSections(opts: {\n count: number;\n estimateHeight: (index: number) => number;\n overscan?: number | undefined;\n}): WindowedSections {\n const { count, estimateHeight, overscan = 3 } = opts;\n const scrollRef = useRef<HTMLDivElement | null>(null);\n\n const [heights, setHeights] = useState<number[]>(() =>\n Array.from({ length: count }, (_, i) => estimateHeight(i)),\n );\n\n // Re-seed heights when the section count changes (a new diff arrives), keeping\n // any already-measured heights for surviving indices.\n useEffect(() => {\n setHeights((prev) => {\n if (prev.length === count) return prev;\n return Array.from({ length: count }, (_, i) => prev[i] ?? estimateHeight(i));\n });\n // estimateHeight is expected stable; count is the real trigger.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [count]);\n\n const heightsRef = useRef(heights);\n heightsRef.current = heights;\n\n const [range, setRange] = useState<WindowRange>(() => ({\n start: 0,\n end: Math.min(count, overscan + 1),\n }));\n\n const recompute = useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);\n setRange((prev) => (prev.start === next.start && prev.end === next.end ? prev : next));\n }, [overscan]);\n\n // Recompute on scroll (rAF-coalesced) and on container resize.\n useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n let raf = 0;\n const onScroll = () => {\n if (raf) return;\n raf =\n typeof requestAnimationFrame === \"function\"\n ? requestAnimationFrame(() => {\n raf = 0;\n recompute();\n })\n : (recompute(), 0);\n };\n el.addEventListener(\"scroll\", onScroll, { passive: true });\n const ro = typeof ResizeObserver !== \"undefined\" ? new ResizeObserver(() => recompute()) : null;\n ro?.observe(el);\n recompute();\n return () => {\n el.removeEventListener(\"scroll\", onScroll);\n ro?.disconnect();\n if (raf && typeof cancelAnimationFrame === \"function\") cancelAnimationFrame(raf);\n };\n }, [recompute, count]);\n\n const measure = useCallback((index: number, height: number) => {\n if (height <= 0) return; // zero-layout pass — keep the estimate\n setHeights((prev) => {\n if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;\n const next = prev.slice();\n next[index] = height;\n return next;\n });\n }, []);\n\n const offsets = useMemo(() => {\n const out = new Array<number>(count + 1);\n out[0] = 0;\n for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);\n return out;\n }, [heights, count]);\n\n // A measured height can shift the window (e.g. estimates were short) — keep the\n // mounted set in sync without waiting for the next scroll.\n useEffect(() => {\n recompute();\n }, [offsets, recompute]);\n\n const scrollToIndex = useCallback(\n (index: number) => {\n const el = scrollRef.current;\n if (!el) return;\n const clamped = Math.max(0, Math.min(count - 1, index));\n el.scrollTop = offsets[clamped] ?? 0;\n },\n [offsets, count],\n );\n\n return {\n scrollRef,\n range,\n offsets,\n totalHeight: offsets[count] ?? 0,\n measure,\n scrollToIndex,\n };\n}\n","import type { GitFileDiff } from \"@opengeni/sdk\";\nimport { type ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { PierreDiff } from \"./pierre-diff\";\n\n/**\n * @deprecated Use {@link PierreDiff} directly. Retained only as a thin,\n * back-compat alias that renders the Pierre (Shiki) renderer with a plain-text\n * degrade. The old hand-rolled hunk renderer was removed in Workbench v2 — the\n * workbench keeps exactly ONE diff renderer. This alias is removed next major.\n */\nexport type DiffTheme = {\n addBackground?: string;\n delBackground?: string;\n contextBackground?: string;\n metaForeground?: string;\n};\n\n/**\n * @deprecated Use {@link PierreDiff}. `theme`/`onSelectFile`/`emptyState` from the\n * removed hand-rolled renderer are accepted for source compatibility and ignored;\n * `fallback` still overrides the plain-text degrade.\n */\nexport type DiffViewProps = {\n diff: GitFileDiff[];\n fallback?: ReactNode | undefined;\n layout?: \"unified\" | \"split\" | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** @deprecated line-background theming was part of the removed renderer. */\n theme?: DiffTheme | undefined;\n /** @deprecated the alias no longer renders a clickable file header. */\n onSelectFile?: ((path: string) => void) | undefined;\n isRepo?: boolean | undefined;\n emptyState?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n/**\n * @deprecated Use {@link PierreDiff} directly. Thin alias: renders `PierreDiff`\n * (Shiki-highlighted, unified/split) with a plain-text fallback for a host\n * without `@pierre/diffs`. Removed next major.\n */\nexport function DiffView({\n diff,\n fallback,\n layout = \"unified\",\n themeType,\n isRepo = true,\n emptyState,\n className,\n}: DiffViewProps) {\n if (diff.length === 0) {\n return (\n <div className={cn(\"p-3 text-og-sm text-og-fg-subtle\", className)}>\n {emptyState ?? (isRepo ? \"No changes\" : \"No repository mounted\")}\n </div>\n );\n }\n return (\n <PierreDiff\n diff={diff}\n layout={layout}\n themeType={themeType}\n {...(fallback !== undefined ? { fallback } : {})}\n className={className}\n />\n );\n}\n","import {\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n lazy,\n Suspense,\n useEffect,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/** Pierre `File` props subset we drive — the single-file, syntax-highlighted view\n * (Shiki), the read counterpart of `PatchDiff`. */\ntype FileComponent = ComponentType<{\n file: { name: string; contents: string; lang?: string };\n options?: {\n theme?: string | { dark: string; light: string };\n themeType?: \"dark\" | \"light\";\n overflow?: \"scroll\" | \"wrap\";\n stickyHeader?: boolean;\n showLineNumbers?: boolean;\n };\n disableWorkerPool?: boolean;\n className?: string;\n}>;\n\nexport type PierreFileProps = {\n /** Workspace-relative path (used for the header + language inference). */\n path: string;\n /** The decoded text contents. */\n contents: string;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Shiki bundled theme names (dark/light) — derived from the host palette. */\n theme?: { dark: string; light: string } | undefined;\n /** Disable Pierre's worker pool if its worker bundling fights the host bundler. */\n disableWorkerPool?: boolean | undefined;\n /** Rendered while the (lazy) Pierre bundle loads. */\n loading?: ReactNode | undefined;\n /** Rendered if `@pierre/diffs/react` is not installed / fails to import. */\n fallback?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n// Lazy-load `@pierre/diffs/react`'s `File` so Shiki + the worker pool stay off the\n// critical path (and out of an SSR bundle) until a file is actually viewed.\nconst LazyFile = lazy(async () => {\n const mod = (await import(\"@pierre/diffs/react\")) as unknown as { File: FileComponent };\n return { default: mod.File };\n});\n\n/**\n * The Pierre-backed single-file VIEWER: Shiki-highlighted, language inferred from\n * the filename — the read complement of `PierreDiff`. Wired to `fs.read` so\n * clicking any file in the tree shows its contents (NOT a diff; no repo needed).\n * Falls back to a plain `<pre>` when `@pierre/diffs` is absent / fails to import.\n */\nexport function PierreFile({\n path,\n contents,\n themeType,\n theme,\n disableWorkerPool,\n loading,\n fallback,\n className,\n}: PierreFileProps) {\n const [failed, setFailed] = useState(false);\n\n // Probe the import once so a hard failure (peer missing) shows `fallback`\n // rather than a Suspense boundary that never resolves.\n useEffect(() => {\n let cancelled = false;\n void import(\"@pierre/diffs/react\").catch(() => {\n if (!cancelled) setFailed(true);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n const name = path.split(\"/\").filter(Boolean).pop() ?? path;\n\n if (failed) {\n return (\n <div className={className}>{fallback ?? <PlainFile name={name} contents={contents} />}</div>\n );\n }\n\n const options = {\n overflow: \"scroll\" as const,\n stickyHeader: true,\n showLineNumbers: true,\n ...(theme\n ? { theme }\n : {\n theme: {\n dark: \"github-dark-high-contrast\",\n light: \"github-light-high-contrast\",\n },\n }),\n themeType: themeType ?? \"dark\",\n };\n\n // Same shadow-DOM override vars as PierreDiff: pin the base background to the\n // dock surface so the viewer reads as part of the panel, not a black seam.\n const pierreVars = {\n \"--diffs-dark-bg\": \"var(--og-color-bg)\",\n \"--diffs-light-bg\": \"var(--og-color-bg)\",\n \"--diffs-bg-buffer-override\": \"var(--og-color-surface-1)\",\n \"--diffs-bg-separator-override\": \"var(--og-color-surface-1)\",\n \"--diffs-font-size\": \"var(--og-code-font-size)\",\n \"--diffs-line-height\": \"var(--og-code-line-height)\",\n } as CSSProperties;\n\n return (\n <div className={cn(\"min-w-0\", className)} data-opengeni-pierre-file style={pierreVars}>\n <Suspense fallback={loading ?? <FileSkeleton />}>\n {/* `cacheKey` keys Pierre's worker-pool highlight cache on path+size so a\n re-select of the same file is instant but an edited file re-highlights. */}\n <LazyFile\n file={{ name, contents }}\n options={options}\n {...(disableWorkerPool !== undefined ? { disableWorkerPool } : {})}\n />\n </Suspense>\n </div>\n );\n}\n\nfunction PlainFile({ name, contents }: { name: string; contents: string }) {\n return (\n <pre\n className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\"\n data-file={name}\n >\n {contents}\n </pre>\n );\n}\n\nfunction FileSkeleton() {\n return <div className=\"p-3 text-og-sm text-og-fg-subtle\">Loading file…</div>;\n}\n","import { Loader2Icon, SaveIcon } from \"lucide-react\";\nimport {\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\n\n// --- Lazy CodeMirror surface ----------------------------------------------\n//\n// CodeMirror 6 (via @uiw/react-codemirror) plus the per-language grammars are a\n// heavy bundle (lezer parsers, the view layer). We keep them OFF the critical\n// path and out of any SSR bundle by lazy-importing only when a file is actually\n// EDITED. The read-only viewer stays Pierre's Shiki `File`; this is the\n// editable complement, mounted on demand.\n\n/** The subset of `@uiw/react-codemirror`'s props we drive. */\ntype ReactCodeMirrorComponent = ComponentType<{\n value?: string;\n height?: string;\n theme?: \"light\" | \"dark\" | \"none\" | unknown;\n editable?: boolean;\n readOnly?: boolean;\n basicSetup?: boolean | Record<string, boolean>;\n extensions?: unknown[];\n onChange?: ((value: string) => void) | undefined;\n className?: string;\n style?: CSSProperties;\n}>;\n\n// Resolved lazily on first edit: the editor component + the keymap/Prec helpers\n// (both re-exported from @uiw/react-codemirror, which re-exports @codemirror/view\n// and @codemirror/state) + the chosen language extension.\ntype EditorBundle = {\n Editor: ReactCodeMirrorComponent;\n saveKeymapExtension: (onSave: () => void) => unknown;\n languageExtension: unknown | null;\n};\n\n/** Language grammar loaders, keyed by the extension class we infer from the path. */\nconst LANGUAGE_LOADERS: Record<string, () => Promise<unknown>> = {\n javascript: async () =>\n (await import(\"@codemirror/lang-javascript\")).javascript({ jsx: true, typescript: true }),\n json: async () => (await import(\"@codemirror/lang-json\")).json(),\n python: async () => (await import(\"@codemirror/lang-python\")).python(),\n markdown: async () => (await import(\"@codemirror/lang-markdown\")).markdown(),\n css: async () => (await import(\"@codemirror/lang-css\")).css(),\n html: async () => (await import(\"@codemirror/lang-html\")).html(),\n};\n\n/** Map a filename to a grammar key (or null for plain text — still fully editable). */\nexport function languageForPath(path: string): keyof typeof LANGUAGE_LOADERS | null {\n const name = path.split(\"/\").pop() ?? path;\n const ext = name.includes(\".\") ? (name.split(\".\").pop() ?? \"\").toLowerCase() : \"\";\n switch (ext) {\n case \"js\":\n case \"jsx\":\n case \"ts\":\n case \"tsx\":\n case \"mjs\":\n case \"cjs\":\n return \"javascript\";\n case \"json\":\n case \"jsonc\":\n return \"json\";\n case \"py\":\n case \"pyi\":\n return \"python\";\n case \"md\":\n case \"markdown\":\n case \"mdx\":\n return \"markdown\";\n case \"css\":\n case \"scss\":\n case \"less\":\n return \"css\";\n case \"html\":\n case \"htm\":\n case \"xml\":\n case \"svg\":\n case \"vue\":\n return \"html\";\n default:\n return null;\n }\n}\n\nexport type CodeEditorProps = {\n /** Workspace-relative path — drives language inference (and the save target upstream). */\n path: string;\n /** The decoded text contents to seed the editor with. */\n initialContents: string;\n /** Persist the current buffer. Resolves when the write lands; rejects to surface an error. */\n onSave: (contents: string) => Promise<unknown>;\n /** Explicitly overwrite after a visible expected-content conflict. */\n onOverwrite?: ((contents: string) => Promise<unknown>) | undefined;\n /** Re-read the selected file after a visible save conflict. */\n onReload?: (() => void) | undefined;\n /** Fired once when the buffer FIRST diverges from its baseline (the first real\n * keystroke) — the wake-on-edit intent. The host warms the box on this so the\n * eventual save lands fast; merely opening the file for reading never fires it. */\n onEditIntent?: (() => void) | undefined;\n /** Read-only mode (e.g. a truncated/too-large file shown for reference only). */\n readOnly?: boolean | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n /** Rendered while the (lazy) CodeMirror bundle loads. */\n loading?: ReactNode | undefined;\n /** Rendered if `@uiw/react-codemirror` is not installed / fails to import. */\n fallback?: ReactNode | undefined;\n className?: string | undefined;\n};\n\n/**\n * The EDITABLE single-file pane: CodeMirror 6 with a per-language grammar chosen\n * from the filename, og-* themed, with dirty tracking and a save path wired to\n * `Cmd/Ctrl+S` *and* an explicit Save button. The viewer (Pierre `File`) stays\n * the read-only complement — this is only mounted when the user opts to edit.\n *\n * Save semantics: the buffer is \"dirty\" the moment it diverges from the last\n * saved baseline; a successful `onSave` clears dirty and re-baselines. A failed\n * save keeps the buffer dirty (nothing is lost) and surfaces the error inline.\n * `readOnly` suppresses every mutation path so a truncated/binary file can never\n * be saved back (which would corrupt it by writing the truncated prefix).\n */\nexport function CodeEditor({\n path,\n initialContents,\n onSave,\n onOverwrite,\n onReload,\n onEditIntent,\n readOnly = false,\n themeType = \"dark\",\n loading,\n fallback,\n className,\n}: CodeEditorProps) {\n const [failed, setFailed] = useState(false);\n const [bundle, setBundle] = useState<EditorBundle | null>(null);\n\n // The live buffer + the baseline it's compared against for dirtiness. The\n // baseline resets whenever a *new* file is loaded (path/initialContents change)\n // or after a successful save.\n const [value, setValue] = useState(initialContents);\n const [baseline, setBaseline] = useState(initialContents);\n const [saving, setSaving] = useState(false);\n const [saveError, setSaveError] = useState<Error | null>(null);\n const [savedTick, setSavedTick] = useState(false);\n\n const dirty = value !== baseline;\n const saveConflict =\n saveError !== null && (saveError as Error & { code?: unknown }).code === \"file_write_conflict\";\n\n // Reload the buffer when the file identity changes. Guard on initialContents\n // too so an external refresh of the SAME path (e.g. fs.changed re-read) reseeds\n // a clean buffer — but only when the user hasn't got unsaved edits in flight.\n // Fire the wake-on-edit intent at most once per opened file — reset on re-seed.\n const editIntentFiredRef = useRef(false);\n const lastSeed = useRef<{ path: string; contents: string }>({ path, contents: initialContents });\n useEffect(() => {\n const seedChanged =\n lastSeed.current.path !== path || lastSeed.current.contents !== initialContents;\n if (!seedChanged) return;\n lastSeed.current = { path, contents: initialContents };\n editIntentFiredRef.current = false;\n setValue(initialContents);\n setBaseline(initialContents);\n setSaveError(null);\n setSavedTick(false);\n }, [path, initialContents]);\n\n // Record a buffer change: the FIRST divergence from the baseline is the edit\n // intent that warms the box (idempotent — latched per opened file).\n const noteEdit = useCallback(\n (next: string) => {\n setValue(next);\n setSavedTick(false);\n if (!editIntentFiredRef.current && next !== baseline) {\n editIntentFiredRef.current = true;\n onEditIntent?.();\n }\n },\n [baseline, onEditIntent],\n );\n\n // Resolve the lazy bundle (editor + keymap/Prec helpers + language grammar)\n // once, re-resolving the grammar when the file's language class changes.\n const langKey = useMemo(() => languageForPath(path), [path]);\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const cmMod = (await import(\"@uiw/react-codemirror\")) as unknown as {\n default: ReactCodeMirrorComponent;\n keymap: { of: (binds: unknown[]) => unknown };\n Prec: { highest: (ext: unknown) => unknown };\n };\n // Language peers are optional. A missing grammar must degrade to a plain\n // CodeMirror document, not throw the entire editor down to textarea.\n let languageExtension: unknown | null = null;\n if (langKey) {\n try {\n languageExtension = (await LANGUAGE_LOADERS[langKey]?.()) ?? null;\n } catch {\n languageExtension = null;\n }\n }\n if (cancelled) return;\n // A high-precedence Cmd/Ctrl-S keymap that calls back into the latest\n // save handler (kept fresh via a ref) and swallows the browser's\n // \"save page\" default.\n const saveKeymapExtension = (run: () => void) =>\n cmMod.Prec.highest(\n cmMod.keymap.of([\n {\n key: \"Mod-s\",\n preventDefault: true,\n run: () => {\n run();\n return true;\n },\n },\n ]),\n );\n setBundle({\n Editor: cmMod.default,\n saveKeymapExtension,\n languageExtension,\n });\n setFailed(false);\n } catch {\n if (!cancelled) setFailed(true);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [langKey]);\n\n // Keep the freshest save closure reachable from the (stable) keymap binding.\n const saveRef = useRef<() => void>(() => {});\n\n const save = useCallback(async () => {\n if (readOnly) return;\n // Snapshot the buffer at call time so an in-flight edit can't race the write.\n const snapshot = value;\n if (snapshot === baseline) return; // nothing to persist\n setSaving(true);\n setSaveError(null);\n setSavedTick(false);\n try {\n await onSave(snapshot);\n setBaseline(snapshot);\n lastSeed.current = { path, contents: snapshot };\n setSavedTick(true);\n } catch (cause) {\n setSaveError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n setSaving(false);\n }\n }, [readOnly, value, baseline, onSave, path]);\n\n const overwrite = useCallback(async () => {\n if (readOnly || !onOverwrite) return;\n const snapshot = value;\n if (snapshot === baseline) return;\n setSaving(true);\n setSaveError(null);\n setSavedTick(false);\n try {\n await onOverwrite(snapshot);\n setBaseline(snapshot);\n lastSeed.current = { path, contents: snapshot };\n setSavedTick(true);\n } catch (cause) {\n setSaveError(cause instanceof Error ? cause : new Error(String(cause)));\n } finally {\n setSaving(false);\n }\n }, [readOnly, onOverwrite, value, baseline, path]);\n\n saveRef.current = () => {\n void save();\n };\n\n // A brief \"Saved\" affordance after a successful write, cleared on the next edit.\n useEffect(() => {\n if (!savedTick) return;\n const t = setTimeout(() => setSavedTick(false), 1800);\n return () => clearTimeout(t);\n }, [savedTick]);\n\n const fileName = path.split(\"/\").filter(Boolean).pop() ?? path;\n\n const cmExtensions = useMemo(() => {\n if (!bundle) return [] as unknown[];\n const exts: unknown[] = [bundle.saveKeymapExtension(() => saveRef.current())];\n if (bundle.languageExtension) exts.push(bundle.languageExtension);\n return exts;\n }, [bundle]);\n\n const Editor = bundle?.Editor;\n\n return (\n <div\n className={cn(\"flex h-full min-h-0 flex-col\", className)}\n data-opengeni-code-editor\n style={editorVars}\n >\n {/* Save bar: dirty indicator + status + the explicit Save button. */}\n <div className=\"flex min-h-9 shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1\">\n <span\n className={cn(\n \"size-1.5 shrink-0 rounded-full transition-colors\",\n readOnly ? \"bg-transparent\" : dirty ? \"bg-og-status-running\" : \"bg-og-status-idle\",\n )}\n title={readOnly ? \"read-only\" : dirty ? \"unsaved changes\" : \"saved\"}\n />\n <span className=\"truncate font-og-mono text-og-xs text-og-fg-muted\">\n {fileName}\n {dirty && !readOnly ? \" •\" : \"\"}\n </span>\n <div className=\"ml-auto flex shrink-0 items-center gap-2\">\n {saveError && !saveConflict && (\n <span\n className=\"max-w-[220px] truncate text-og-xs text-og-status-failed\"\n title={saveError.message}\n >\n {saveError.message}\n </span>\n )}\n {!saveError && savedTick && <span className=\"text-og-xs text-og-status-idle\">Saved</span>}\n {readOnly ? (\n <span className=\"text-og-xs uppercase tracking-wide text-og-fg-subtle\">Read-only</span>\n ) : !saveConflict ? (\n <button\n type=\"button\"\n onClick={() => void save()}\n disabled={saving || !dirty}\n className={cn(\n \"flex items-center gap-1 rounded-og-sm border border-og-border px-1.5 py-0.5 text-og-xs pointer-coarse:min-h-11\",\n saving || !dirty\n ? \"cursor-default text-og-fg-subtle opacity-60\"\n : \"text-og-fg hover:bg-og-accent-soft\",\n )}\n title=\"Save (⌘/Ctrl+S)\"\n >\n {saving ? (\n <Loader2Icon className=\"size-3 animate-spin\" />\n ) : (\n <SaveIcon className=\"size-3\" />\n )}\n Save\n </button>\n ) : null}\n </div>\n </div>\n\n {saveConflict ? (\n <div\n className=\"flex shrink-0 flex-wrap items-center gap-x-3 gap-y-2 border-b border-og-status-failed/25 bg-og-status-failed/8 px-2 py-2\"\n role=\"alert\"\n >\n <div className=\"min-w-[12rem] flex-1 text-og-xs text-og-fg-muted\">\n <span className=\"font-medium text-og-status-failed\">File changed on machine.</span>{\" \"}\n Reloading discards your edits; overwriting replaces the live version.\n </div>\n <div className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {onReload ? (\n <button\n type=\"button\"\n onClick={() => {\n setSaveError(null);\n onReload();\n }}\n className=\"min-h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-og-xs font-medium text-og-fg hover:bg-og-surface-2 pointer-coarse:min-h-11\"\n >\n Reload live\n </button>\n ) : null}\n {onOverwrite ? (\n <button\n type=\"button\"\n onClick={() => void overwrite()}\n disabled={saving}\n className=\"min-h-8 rounded-og-sm border border-og-status-failed/40 px-2 text-og-xs font-medium text-og-status-failed hover:bg-og-status-failed/10 disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n Overwrite\n </button>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {/* The editor surface. */}\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {failed ? (\n (fallback ?? (\n <PlainTextarea\n value={value}\n readOnly={readOnly}\n onChange={noteEdit}\n onSave={() => void save()}\n />\n ))\n ) : Editor ? (\n <Editor\n value={value}\n theme={themeType === \"light\" ? \"light\" : \"dark\"}\n editable={!readOnly}\n readOnly={readOnly}\n basicSetup={true}\n extensions={cmExtensions}\n height=\"100%\"\n className=\"og-cm-editor min-h-full text-og-sm\"\n onChange={readOnly ? undefined : noteEdit}\n />\n ) : (\n (loading ?? <EditorSkeleton />)\n )}\n </div>\n </div>\n );\n}\n\n/** og-* themed CSS-var overrides handed to CodeMirror's container. */\nconst editorVars = {\n \"--og-cm-bg\": \"var(--og-color-bg)\",\n fontFamily: \"var(--og-font-mono)\",\n} as CSSProperties;\n\nfunction PlainTextarea({\n value,\n readOnly,\n onChange,\n onSave,\n}: {\n value: string;\n readOnly: boolean;\n onChange: (value: string) => void;\n onSave: () => void;\n}) {\n return (\n <textarea\n value={value}\n readOnly={readOnly}\n spellCheck={false}\n onChange={(e) => onChange(e.target.value)}\n onKeyDown={(event) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"s\") {\n event.preventDefault();\n onSave();\n }\n }}\n aria-label=\"Plain text file editor\"\n className=\"h-full w-full resize-none bg-transparent p-2 font-og-mono text-og-sm text-og-fg outline-none\"\n />\n );\n}\n\nfunction EditorSkeleton() {\n return <div className=\"p-3 text-og-sm text-og-fg-subtle\">Loading editor…</div>;\n}\n","import { FileCode2Icon, FileWarningIcon, LoaderCircleIcon } from \"lucide-react\";\nimport { type ReactNode, useCallback, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useThemeType } from \"../lib/use-theme-type\";\nimport {\n CapturedFileUnavailableError,\n type UseSandboxFilesResult,\n} from \"../hooks/use-sandbox-files\";\nimport type { UseSandboxGitResult } from \"../hooks/use-sandbox-git\";\nimport { CodeEditor } from \"./code-editor\";\nimport { FileBrowser } from \"./file-browser\";\nimport { PierreFile } from \"./pierre-file\";\n\nexport type SandboxFilesProps = {\n /** From `useSandboxFiles(...)`. */\n files: UseSandboxFilesResult;\n /** From `useSandboxGit(...)` — drives the branch/dirty header only. */\n git: UseSandboxGitResult;\n /** @deprecated Diffs live in the dedicated Changes tab now; accepted for\n * source-compat but unused (the Files surface is a browser + viewer). */\n stagedGit?: UseSandboxGitResult | undefined;\n /** Whether a FileSystem surface is advertised (drives the unavailable notice). */\n fileSystemAvailable?: boolean | undefined;\n /** Use Pierre's Shiki highlighter for the viewer (default true; plain fallback). */\n usePierre?: boolean | undefined;\n /** Allow in-place editing of tree files (CodeMirror). Default true. When false\n * the surface is review-only: every text file opens in the read-only viewer. */\n editable?: boolean | undefined;\n /** Fired once when the user first edits an open file (wake-on-edit intent). The\n * dock warms the box on this so the save lands fast; opening/reading never fires\n * it. Browsing the tree/diff must not warm a box. */\n onEditIntent?: (() => void) | undefined;\n /** A guarded diff path routed here by the parent workspace. */\n requestedPath?: string | undefined;\n /** Identity for one guarded-file request. Increment this when the same path is\n * deliberately requested again; it also lets a pending request be consumed\n * without overriding later manual tree navigation. Defaults to the path. */\n requestedPathRequestId?: string | number | undefined;\n /** False while the parent is waking a cold sandbox for `requestedPath`. */\n requestedPathReady?: boolean | undefined;\n /** The machine is cold and no durable capture exists yet. Render an explicit\n * wake gate instead of an empty-tree lie or an implicit Channel-A read. */\n workspaceResting?: boolean | undefined;\n /** A deliberate wake has started but the live file surface is not ready yet. */\n workspaceWaking?: boolean | undefined;\n /** Whether live reads are currently authoritative. */\n liveWorkspaceReady?: boolean | undefined;\n /** Deliberately wake the machine to read content absent from the capture. */\n onWakeWorkspace?: (() => void) | undefined;\n themeType?: \"dark\" | \"light\" | undefined;\n className?: string | undefined;\n};\n\n/**\n * The Files surface: a branch/dirty header, the full lazy file tree, and a\n * viewer/editor pane for the selected file. This is the workspace BROWSER — pick\n * a file to read or edit it. Diff review lives in the dedicated Changes tab (this\n * surface deliberately does NOT replicate the changed-files list, and does not\n * diff here — one job per tab). The agent commits; the human reviews.\n */\nexport function SandboxFiles({\n files,\n git,\n fileSystemAvailable = true,\n usePierre = true,\n editable = true,\n onEditIntent,\n requestedPath,\n requestedPathRequestId,\n requestedPathReady = true,\n workspaceResting = false,\n workspaceWaking = false,\n liveWorkspaceReady = true,\n onWakeWorkspace,\n themeType,\n className,\n}: SandboxFilesProps) {\n const [selected, setSelected] = useState<string | null>(null);\n // View vs Edit for the selected file. Resets to View on every new selection so\n // opening a file never lands you in a stale dirty editor for a different path.\n const [editMode, setEditMode] = useState(false);\n const [liveRequestedPath, setLiveRequestedPath] = useState<string | null>(null);\n const [viewReloadRevision, setViewReloadRevision] = useState(0);\n const pendingRequestRef = useRef<string | number | null>(null);\n const handledRequestRef = useRef<string | number | null>(null);\n const requestKey = requestedPath ? (requestedPathRequestId ?? requestedPath) : null;\n\n useEffect(() => {\n if (!requestedPath || requestKey === null) {\n pendingRequestRef.current = null;\n handledRequestRef.current = null;\n return;\n }\n if (handledRequestRef.current === requestKey) {\n return;\n }\n if (!requestedPathReady) {\n pendingRequestRef.current = requestKey;\n return;\n }\n handledRequestRef.current = requestKey;\n pendingRequestRef.current = null;\n setSelected(requestedPath);\n setEditMode(false);\n }, [requestKey, requestedPath, requestedPathReady]);\n\n // Side-by-side (tree left, viewer right) once the surface is wide enough;\n // stacked (tree over viewer) on a narrow dock. Tracked off the container so it\n // reacts to the dock resize, not just the viewport.\n const rootRef = useRef<HTMLDivElement | null>(null);\n const [wide, setWide] = useState(false);\n useEffect(() => {\n const el = rootRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver((entries) => {\n const w = entries[0]?.contentRect.width ?? 0;\n setWide(w >= 720);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n // Resolve the effective viewer theme from the host palette (the `data-og-theme`\n // attribute the demo/app sets), defaulting to dark, unless the caller forced one.\n const resolvedTheme = useThemeType(themeType);\n\n // The selected tree file opens in the viewer (read-only) or the editor (when\n // writable). Nothing is auto-selected — the pane waits for a tree click, so the\n // Files tab opens as a calm browser, not a diff.\n const viewPath = selected;\n const fileView = useFileView(viewPath, files.readFile, viewReloadRevision);\n\n // Selecting a (different) file always returns to View — never drop the user into\n // an editor whose buffer belongs to the previously-selected path. Manual\n // navigation also consumes a pending guarded-file request: a late cold→warm\n // transition must never pull the user away from the file they chose meanwhile.\n const selectFile = useCallback((path: string) => {\n if (pendingRequestRef.current !== null) {\n handledRequestRef.current = pendingRequestRef.current;\n pendingRequestRef.current = null;\n }\n setSelected(path);\n setEditMode(false);\n setLiveRequestedPath(null);\n setViewReloadRevision(0);\n }, []);\n\n // A tree file is editable only when it is a real, fully-loaded text file: not\n // binary (would corrupt on save) and not truncated (we only hold a PREFIX). The\n // editor is then additionally gated on the `editable` prop; anything failing this\n // opens read-only in the viewer.\n const canEdit =\n editable &&\n viewPath !== null &&\n !fileView.loading &&\n fileView.error === null &&\n !fileView.isBinary &&\n !fileView.truncated &&\n fileView.content !== null;\n const showEditor = canEdit && editMode;\n const captureFileUnavailable =\n fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;\n const waitingForSelectedFile =\n liveRequestedPath === viewPath &&\n (!liveWorkspaceReady || files.loading) &&\n captureFileUnavailable !== null;\n\n if (!fileSystemAvailable) {\n return (\n <Notice\n className={className}\n icon={<FileWarningIcon className=\"size-5\" aria-hidden />}\n title=\"Files unavailable\"\n announce=\"alert\"\n >\n This sandbox does not expose a file system.\n </Notice>\n );\n }\n\n if (workspaceResting || workspaceWaking) {\n return (\n <div className={cn(\"h-full\", className)} data-opengeni-workspace-resting>\n <Notice\n icon={\n workspaceWaking ? (\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n ) : (\n <FileCode2Icon className=\"size-5\" aria-hidden />\n )\n }\n title={workspaceWaking ? \"Waking workspace\" : \"Workspace is resting\"}\n announce=\"status\"\n >\n <p>\n {workspaceWaking\n ? \"Connecting to the live file system…\"\n : \"No captured revision is available yet. Wake the sandbox to browse its current files.\"}\n </p>\n {!workspaceWaking && onWakeWorkspace ? (\n <WakeButton onClick={onWakeWorkspace}>Open live workspace</WakeButton>\n ) : null}\n </Notice>\n </div>\n );\n }\n\n return (\n <div ref={rootRef} className={cn(\"flex h-full min-h-0 min-w-0 flex-col\", className)}>\n {/* Branch + dirty header (context; NOT the changed-files list). */}\n <GitHeader git={git} dirtyCount={git.diff.length} />\n\n <div className={cn(\"flex min-h-0 flex-1\", wide ? \"flex-row\" : \"flex-col\")}>\n {/* Tree pane: the full lazy file tree. A fixed left column when wide, a top\n band when narrow. */}\n <div\n className={cn(\n \"flex min-h-0 flex-col\",\n wide ? \"w-[280px] shrink-0 border-r border-og-border\" : \"flex-1\",\n )}\n >\n <FileBrowser\n result={files}\n selectedPath={selected ?? undefined}\n onSelectFile={selectFile}\n editable={editable}\n emptyState=\"This directory is empty\"\n className=\"min-w-0 flex-1\"\n />\n </div>\n\n {/* Viewer pane: the selected file's contents (read-only) or the editor.\n Fills the remaining width when side-by-side, sits below the tree when\n stacked. */}\n <div\n className={cn(\n \"flex min-h-0 min-w-0 flex-col\",\n wide ? \"flex-1\" : \"flex-[1.4] border-t border-og-border\",\n )}\n >\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1\">\n <span\n data-opengeni-selected-file\n className=\"min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted\"\n >\n {selected ?? \"No file selected\"}\n </span>\n {/* View/Edit toggle — only for a real, fully-loaded text file the editor\n can safely round-trip. Binary/truncated/read-only files never get an\n Edit affordance (they'd corrupt on save or can't be written). */}\n {canEdit && (\n <Segmented\n options={[\n { value: \"view\", label: \"View\" },\n { value: \"edit\", label: \"Edit\" },\n ]}\n value={editMode ? \"edit\" : \"view\"}\n onChange={(v) => setEditMode(v === \"edit\")}\n />\n )}\n </div>\n <div className=\"min-h-0 flex-1 overflow-auto\">\n {viewPath ? (\n showEditor && fileView.content !== null ? (\n <CodeEditor\n key={viewPath}\n path={viewPath}\n initialContents={fileView.content}\n themeType={resolvedTheme}\n onSave={(contents) =>\n files.writeFile(viewPath, contents, { expectedContent: fileView.content! })\n }\n onOverwrite={(contents) => files.writeFile(viewPath, contents, { force: true })}\n onReload={() => setViewReloadRevision((revision) => revision + 1)}\n {...(onEditIntent ? { onEditIntent } : {})}\n className=\"h-full\"\n />\n ) : waitingForSelectedFile ? (\n <Notice\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n title=\"Waking workspace\"\n announce=\"status\"\n >\n Opening {viewPath} when the live file system is ready…\n </Notice>\n ) : captureFileUnavailable ? (\n <Notice icon={<FileCode2Icon className=\"size-5\" aria-hidden />} title=\"On machine\">\n <p>\n {captureFileUnavailable.reason === \"too-large\"\n ? \"This file is larger than the captured preview limit.\"\n : captureFileUnavailable.reason === \"content-missing\"\n ? \"The captured copy is no longer available.\"\n : \"This file was indexed, but it was not changed in the captured turn.\"}\n </p>\n {onWakeWorkspace ? (\n <WakeButton\n onClick={() => {\n setLiveRequestedPath(viewPath);\n if (liveWorkspaceReady) void files.refresh();\n else onWakeWorkspace();\n }}\n >\n {liveWorkspaceReady ? \"Retry live file\" : \"Open live file\"}\n </WakeButton>\n ) : null}\n </Notice>\n ) : fileView.error ? (\n <Notice announce=\"alert\">\n Could not open {viewPath}: {fileView.error.message}\n </Notice>\n ) : fileView.loading ? (\n <Notice announce=\"status\">Loading {viewPath}…</Notice>\n ) : fileView.isBinary ? (\n <Notice>\n {viewPath} is a binary file ({fileView.sizeBytes ?? 0} bytes).\n </Notice>\n ) : fileView.content !== null ? (\n <>\n {fileView.truncated && (\n <div className=\"border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running\">\n Large file — showing a truncated preview ({fileView.sizeBytes ?? 0} bytes\n loaded). Editing is disabled to avoid corrupting the file.\n </div>\n )}\n {usePierre ? (\n <PierreFile\n path={viewPath}\n contents={fileView.content}\n themeType={resolvedTheme}\n fallback={\n <pre className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\">\n {fileView.content}\n </pre>\n }\n className=\"p-1\"\n />\n ) : (\n <pre className=\"overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg\">\n {fileView.content}\n </pre>\n )}\n </>\n ) : (\n <Notice announce=\"status\">Loading {viewPath}…</Notice>\n )\n ) : // Nothing selected — the tree shows the whole workspace; pick a file.\n requestedPath && !requestedPathReady ? (\n <Notice\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n title=\"Waking sandbox\"\n announce=\"status\"\n >\n Opening {requestedPath} when the live workspace is ready…\n </Notice>\n ) : (\n <Notice icon={<FileCode2Icon className=\"size-5\" aria-hidden />} title=\"Choose a file\">\n Select a file in the tree to preview it.\n </Notice>\n )}\n </div>\n </div>\n </div>\n </div>\n );\n}\n\nfunction WakeButton({ children, onClick }: { children: ReactNode; onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"mt-1 inline-flex min-h-11 items-center justify-center rounded-og-md bg-og-accent-deep px-3 py-2 text-og-sm font-medium text-og-accent-fg shadow-sm transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg\"\n >\n {children}\n </button>\n );\n}\n\nfunction GitHeader({ git, dirtyCount }: { git: UseSandboxGitResult; dirtyCount: number }) {\n const dirty = dirtyCount > 0;\n return (\n <div className=\"flex shrink-0 items-center gap-2 border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-sm\">\n <span\n aria-hidden=\"true\"\n className={cn(\n \"size-2 shrink-0 rounded-full\",\n dirty ? \"bg-og-status-running\" : \"bg-og-status-idle\",\n )}\n />\n <span className=\"sr-only\">\n {dirty ? `Working tree has ${dirtyCount} changed files` : \"Working tree clean\"}\n </span>\n <span className=\"truncate font-og-mono text-og-fg\">\n {git.repoCount > 1\n ? `${git.repoCount} repositories`\n : (git.branch ?? (git.isRepo ? \"(detached)\" : \"no repo\"))}\n </span>\n {(git.ahead > 0 || git.behind > 0) && (\n <span className=\"flex shrink-0 items-center gap-1.5 text-og-xs text-og-fg-subtle\">\n {git.ahead > 0 && <span>↑{git.ahead}</span>}\n {git.behind > 0 && <span>↓{git.behind}</span>}\n </span>\n )}\n {dirty && (\n <span\n aria-hidden=\"true\"\n data-contrast-audited\n className=\"ml-auto shrink-0 text-og-xs text-og-fg-subtle\"\n >\n {dirtyCount} changed\n </span>\n )}\n </div>\n );\n}\n\nfunction Segmented({\n options,\n value,\n onChange,\n}: {\n options: { value: string; label: string }[];\n value: string;\n onChange: (value: string) => void;\n}) {\n return (\n <div className=\"flex flex-wrap items-center rounded-og-sm border border-og-border p-0.5\">\n {options.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => onChange(opt.value)}\n className={cn(\n \"min-h-7 rounded-og-xs px-1.5 py-0.5 text-og-xs focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n opt.value === value\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n );\n}\n\ntype FileViewState = {\n content: string | null;\n isBinary: boolean;\n /** The backend truncated the read (size cap hit) — content is a PREFIX only.\n * Editing+saving such a file would write the prefix back and corrupt it, so\n * the editor must stay read-only for a truncated read. */\n truncated: boolean;\n sizeBytes: number | null;\n loading: boolean;\n error: Error | null;\n};\n\n/**\n * Read a file's contents for the viewer pane. Calls `fs.read` (text by default;\n * the backend flags binary), decodes a base64 payload if one comes back, and\n * exposes loading/error/binary state. Re-fetches when the path changes; ignores\n * a stale resolve after the selection moves on.\n */\nfunction useFileView(\n path: string | null,\n readFile: UseSandboxFilesResult[\"readFile\"],\n reloadRevision = 0,\n): FileViewState {\n const [state, setState] = useState<FileViewState>({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: null,\n });\n useEffect(() => {\n if (!path) {\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: null,\n });\n return;\n }\n let cancelled = false;\n const abort = new AbortController();\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: true,\n error: null,\n });\n void readFile(path, { signal: abort.signal })\n .then((res) => {\n if (cancelled) return;\n const content = res.isBinary\n ? null\n : res.encoding === \"base64\"\n ? decodeBase64Utf8(res.content)\n : res.content;\n setState({\n content,\n isBinary: res.isBinary,\n truncated: res.truncated,\n sizeBytes: res.sizeBytes,\n loading: false,\n error: null,\n });\n })\n .catch((cause) => {\n if (cancelled) return;\n setState({\n content: null,\n isBinary: false,\n truncated: false,\n sizeBytes: null,\n loading: false,\n error: cause instanceof Error ? cause : new Error(String(cause)),\n });\n });\n return () => {\n cancelled = true;\n abort.abort();\n };\n }, [path, readFile, reloadRevision]);\n return state;\n}\n\n/** Decode a base64 payload to a UTF-8 string (browser `atob` + TextDecoder). */\nfunction decodeBase64Utf8(b64: string): string {\n try {\n if (typeof atob === \"function\") {\n const binary = atob(b64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n } catch {\n /* fall through to returning the raw payload */\n }\n return b64;\n}\n\nfunction Notice({\n children,\n className,\n icon,\n title,\n announce,\n}: {\n children: ReactNode;\n className?: string | undefined;\n icon?: ReactNode | undefined;\n title?: string | undefined;\n announce?: \"status\" | \"alert\" | undefined;\n}) {\n return (\n <div\n role={announce}\n aria-atomic={announce ? \"true\" : undefined}\n aria-live={announce === \"alert\" ? \"assertive\" : announce === \"status\" ? \"polite\" : undefined}\n className={cn(\n \"flex h-full items-center justify-center p-4 text-center text-og-sm text-og-fg-subtle\",\n className,\n )}\n >\n <div className=\"flex max-w-sm flex-col items-center gap-2.5\">\n {icon ? (\n <span className=\"grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted shadow-sm\">\n {icon}\n </span>\n ) : null}\n {title ? <p className=\"font-medium text-og-fg\">{title}</p> : null}\n <div className=\"leading-5\">{children}</div>\n </div>\n </div>\n );\n}\n","import type {\n CapabilityUnavailableReason,\n DesktopRfbFactory,\n DesktopStreamCapability,\n} from \"@opengeni/sdk\";\nimport { LoaderCircleIcon, MonitorIcon, MousePointerClickIcon, WifiOffIcon } from \"lucide-react\";\nimport { type ReactNode, useEffect, useRef, useState } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { useDesktopStream } from \"../hooks/use-desktop-stream\";\nimport type { DesktopWebSocketFactory } from \"../hooks/use-relay-frame-stream\";\n\nexport type DesktopViewerProps = {\n /** The desktop cell of the negotiated capabilities (`capabilities.DesktopStream`). */\n capability: DesktopStreamCapability | null;\n /**\n * Initial control mode. Default false (watch). When the user flips\n * \"Take control\" the viewer drives input — but only if `capability.mode`\n * permits it (server-gated; a read-only deployment disables the toggle).\n * Pass a value to control it externally; omit to let the viewer own the state.\n */\n interactive?: boolean | undefined;\n /** Render the built-in Watching ⇄ Take control toggle (default true). */\n showControlToggle?: boolean | undefined;\n scaleViewport?: boolean | undefined;\n /** Custom RFB factory (tests / a WebRTC swap). Defaults to lazy @novnc/novnc. */\n rfbFactory?: DesktopRfbFactory | undefined;\n /** Custom socket factory for the self-hosted `relay-frames` transport (tests).\n * Defaults to `new WebSocket(url)`. Mirrors `rfbFactory`. */\n webSocketFactory?: DesktopWebSocketFactory | undefined;\n /**\n * Consent gate for the un-redacted (and possibly shared) pixel plane. Rendered\n * BEFORE connecting whenever the desktop requires acknowledgment that hasn't\n * been given. Call `onAccept` to record consent (the host wires it to\n * `client.acknowledgeStream` + a re-negotiate). When omitted, a default\n * banner is shown.\n */\n renderConsentGate?: ((onAccept: () => void, shared: boolean) => ReactNode) | undefined;\n /** Called when the default consent gate's accept button is pressed. */\n onAcknowledge?: (() => void) | undefined;\n /**\n * Whether the host has the viewer attach engaged (i.e. the user has opted into\n * watching — the parent's `watchDesktop`/`attachDesktop`). Drives the\n * cold-state behaviour: when watching, a cold-but-warmable lease AUTO-WARMS\n * (and re-warms when the box drains) instead of dead-ending. When omitted we\n * infer it from a recorded consent (the default gate's accept), so the\n * component still self-heals after the first acknowledgment.\n */\n watching?: boolean | undefined;\n /**\n * Request a (re)warm of the sandbox WITHOUT re-acknowledging (the consent has\n * already been recorded — only the box drained). The host wires this to\n * \"engage the viewer attach + re-negotiate\". Called automatically when a\n * watched desktop is found cold-but-warmable, and behind the manual retry on\n * the warming notice. Distinct from `onAcknowledge`, which is the FIRST,\n * consent-bearing warm.\n */\n onWarm?: (() => void) | undefined;\n /** Shown when transport is null (headless backend / degraded / disabled). */\n renderUnavailable?: ((reason: CapabilityUnavailableReason | null) => ReactNode) | undefined;\n /** Shown while the box is cold/warming (no live address yet). */\n renderWarming?: (() => ReactNode) | undefined;\n /** Shown when the per-session viewer cap (429) was hit. */\n renderViewerCap?: (() => ReactNode) | undefined;\n /** Surface the 429 cap state from `useSessionCapabilities().viewerCapReached`. */\n viewerCapReached?: boolean | undefined;\n /**\n * Connect watchdog (ms): if a live url is present but the RFB hasn't connected\n * within this window, surface a \"Couldn't connect\" + Reconnect instead of an\n * eternal idle scrim. Default 13000. 0 disables the watchdog.\n */\n connectTimeoutMs?: number | undefined;\n className?: string | undefined;\n};\n\n/**\n * The derived desktop surface state. A single source of truth so the overlay,\n * the scrim, the auto-warm effect, and the watchdog all agree. Priority order\n * (highest first): viewer-cap → unavailable → consent → warming → connect-failed\n * → error → connecting → connected.\n */\ntype DesktopUiState =\n | \"viewer_cap\" // 429 — the per-session live-viewer limit is reached.\n | \"unavailable\" // genuinely unsupported (headless/policy/os/backend/not-provisioned).\n | \"consent\" // un-redacted/shared plane needs acknowledgment first.\n | \"warming\" // cold-but-warmable: box not running yet (we auto-warm + spin).\n | \"connecting\" // url is live, RFB negotiating/handshaking (we spin, with a watchdog).\n | \"connect_failed\" // watchdog fired: url present but never connected.\n | \"error\" // RFB error / securityfailure after a connect.\n | \"connected\"; // live framebuffer painting.\n\n// Media-inspection exception: the framebuffer sits on true black so screenshots\n// and desktop pixels are not tinted by the product chrome palette.\nconst DESKTOP_MEDIA_BACKGROUND = \"#000\";\n\n/** Reasons that are genuinely-unavailable (never warmable from the viewer). A\n * `lease_cold` is deliberately EXCLUDED — that's the warmable cold state. */\nfunction isHardUnavailable(reason: CapabilityUnavailableReason | null): boolean {\n switch (reason) {\n case \"backend_unsupported\":\n case \"os_unsupported\":\n case \"not_provisioned\":\n case \"disabled_by_policy\":\n case \"tier_headless\":\n return true;\n default:\n // null or \"lease_cold\" → not a hard reason (cold-but-warmable / live).\n return false;\n }\n}\n\n/**\n * The desktop surface: a noVNC client connecting to the Channel-B scoped tunnel\n * URL from the capability doc. Owns the mount `<div ref>`, drives\n * `useDesktopStream` (SSR-safe lazy RFB), and renders a real\n * cold → warming → connecting → connected → error state machine with live\n * feedback (spinners, transitions) — never a dead black box with stale text.\n *\n * The read-only vs interactive decision is enforced server-first\n * (`capability.mode`): when the deployment advertises mode \"interactive\" the\n * viewer can TAKE CONTROL and drive the mouse & keyboard into the box's :0; a\n * \"read-only\" deployment disables the take-control affordance (graceful, with a\n * reason).\n *\n * Warming: a cold-but-warmable lease (`reason: \"lease_cold\"`) is NOT a dead end.\n * When the user is watching (consented), the viewer asks the host to (re)warm\n * the box (`onWarm`) and shows a \"Warming…\" spinner; if the box later drains to\n * cold it re-warms. Genuinely-unavailable surfaces (headless/policy/os/backend)\n * keep a clear, static unavailable notice.\n */\nexport function DesktopViewer({\n capability,\n interactive,\n showControlToggle = true,\n scaleViewport,\n rfbFactory,\n webSocketFactory,\n renderConsentGate,\n onAcknowledge,\n watching,\n onWarm,\n renderUnavailable,\n renderWarming,\n renderViewerCap,\n viewerCapReached,\n connectTimeoutMs = 13_000,\n className,\n}: DesktopViewerProps) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [consented, setConsented] = useState(false);\n // Local control state when not externally controlled. The server gate\n // (`capability.mode`) is the hard ceiling — a read-only deployment can never\n // be flipped to interactive regardless of this toggle.\n const [takeControl, setTakeControl] = useState(interactive ?? false);\n const externallyControlled = interactive !== undefined;\n const serverAllowsControl = capability?.mode !== \"read-only\";\n const wantControl = externallyControlled ? interactive : takeControl;\n const inControl = Boolean(wantControl) && serverAllowsControl;\n\n // Whether the host has the viewer attach engaged. Prefer the explicit prop;\n // fall back to a locally-recorded consent so the component still self-heals\n // (auto-warms / re-warms) once the user has accepted the gate at least once.\n const isWatching = watching ?? consented;\n\n // ── Decide the rendered state (before touching the stream hook) ─────────────\n const transportNull = !capability || capability.transport === null;\n const reason = capability?.reason ?? null;\n const needsAck =\n capability?.requiresAcknowledgment === true && capability.acknowledged !== true && !consented;\n // No live address yet on an otherwise-live transport (post-ack, mid-warm).\n const noLiveAddress = Boolean(capability) && !transportNull && !capability!.url;\n\n // Cold-but-warmable: the lease is cold (`lease_cold`) OR the transport is up\n // but no url has been minted yet — either way warming, not unavailable.\n const coldWarmable =\n (transportNull && reason === \"lease_cold\") || (!transportNull && noLiveAddress);\n // Genuinely-unavailable: transport null for a HARD reason (not lease_cold).\n const hardUnavailable = transportNull && isHardUnavailable(reason);\n\n const accept = () => {\n setConsented(true);\n onAcknowledge?.();\n };\n\n // Release control WITHOUT ever swallowing a key the desktop needs. Esc, and\n // every other key, pass straight through to noVNC/:0 — vital for vim, menus,\n // and dialogs inside the box. Exactly ONE non-trapping keyboard exit:\n // • A single non-conflicting chord — Ctrl+Alt+Shift pressed on its own (no\n // other key) — which no app binds, so it never eats a real keystroke.\n // (Plus the always-visible \"Return control\" button in the in-control bar.)\n //\n // We deliberately DO NOT release on pointer-leave (or window blur): those fire\n // as a SIDE EFFECT of connecting — the noVNC canvas re-laying-out, the surface\n // grabbing focus, the connecting scrim swapping in — which bounced control\n // straight back to \"watch\" the instant the user took it. Control is given up\n // only on an explicit, intentional gesture (the button or the chord).\n useEffect(() => {\n if (!inControl || externallyControlled) return;\n const onKey = (event: KeyboardEvent) => {\n // Only the bare modifier chord releases; if any non-modifier key is also\n // down (event.key is a real key like \"a\"/\"Escape\"), let it pass through.\n if (\n event.ctrlKey &&\n event.altKey &&\n event.shiftKey &&\n (event.key === \"Control\" || event.key === \"Alt\" || event.key === \"Shift\")\n ) {\n event.preventDefault();\n setTakeControl(false);\n }\n };\n window.addEventListener(\"keydown\", onKey);\n return () => {\n window.removeEventListener(\"keydown\", onKey);\n };\n }, [inControl, externallyControlled]);\n\n // The hook is always called (rules of hooks); it stays idle until `url` is set.\n // Do NOT open a socket while the viewer-cap (429) notice is showing — the slot\n // is already exhausted, so connecting would only burn a doomed attempt (and in\n // tests leak an unhandled ws error from the never-resolving tunnel URL).\n const connectCapability = !transportNull && !needsAck && !viewerCapReached ? capability : null;\n const stream = useDesktopStream({\n capability: connectCapability,\n containerRef,\n interactive: inControl,\n ...(scaleViewport !== undefined ? { scaleViewport } : {}),\n ...(rfbFactory ? { rfbFactory } : {}),\n ...(webSocketFactory ? { webSocketFactory } : {}),\n });\n\n const connected = stream.state === \"connected\";\n const hasLiveUrl = Boolean(connectCapability?.url);\n // The latest stream state, read without making it an effect dependency — so the\n // re-attach below can consult it on a visibility change WITHOUT re-subscribing\n // (and re-firing) every time the state walks idle→negotiating→connected.\n const streamStateRef = useRef(stream.state);\n streamStateRef.current = stream.state;\n\n // ── AUTO-WARM ───────────────────────────────────────────────────────────────\n // When the user is watching and the desktop is cold-but-warmable, ask the host\n // to (re)warm the box. This covers BOTH (a) the user just accepted consent and\n // (b) a previously-warm box that drained back to cold under a live viewer. We\n // fire once per distinct cold episode (keyed on the lease epoch + url) so we\n // don't spam the attach while a single warm is in flight. `onWarm` is the\n // no-re-ack path; if the host only wired `onAcknowledge` (no `onWarm`), the\n // first warm still rides consent and subsequent drains fall back to it.\n const warmKeyRef = useRef<string | null>(null);\n useEffect(() => {\n if (!isWatching || !coldWarmable || needsAck || viewerCapReached) {\n // Reset the de-dupe once we leave the cold episode so a future drain warms.\n if (!coldWarmable) warmKeyRef.current = null;\n return;\n }\n const warm = onWarm ?? onAcknowledge;\n if (!warm) return;\n // Key the de-dupe on the cell fields available to the viewer: while cold,\n // `reason`/`url`/`expiresAt` are stable, so we warm exactly once; once the box\n // warms the cell changes (url+expiresAt minted) and `coldWarmable` flips false,\n // resetting the ref so a later drain re-warms.\n const key = `${capability?.reason ?? \"\"}:${capability?.url ?? \"\"}:${capability?.expiresAt ?? \"\"}`;\n if (warmKeyRef.current === key) return; // already kicked this episode.\n warmKeyRef.current = key;\n warm();\n // capability identity is the trigger; leaseEpoch/expiresAt key the episode.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [isWatching, coldWarmable, needsAck, viewerCapReached, capability, onWarm, onAcknowledge]);\n\n // ── TAB RE-ATTACH ───────────────────────────────────────────────────────────\n // The \"stuck Watching, never connects\" bug: the RFB socket never (re)fires when\n // the Desktop tab is shown without a refresh. When a live url exists but we are\n // not connected/connecting, nudge the stream hook to (re)attach — on mount, on\n // the document becoming visible, and on a fresh capability. Re-attach is cheap\n // (disconnect-old/connect-new) and idempotent: the hook ignores it once live.\n useEffect(() => {\n if (!hasLiveUrl || typeof document === \"undefined\") return;\n const maybeReattach = () => {\n if (document.visibilityState !== \"visible\") return;\n // Only kick a socket that never OPENED (idle — e.g. the tab was hidden when\n // the url arrived, so the connect effect bailed on a missing container).\n // Deliberately NOT on \"error\": that surfaces an overlay with an explicit\n // Reconnect, and auto-retrying here would hammer (reconnect → error →\n // reconnect…). We read the live state via a ref so this effect does NOT\n // depend on `stream.state` — depending on it re-ran the effect on every\n // transition and re-fired the kick, which is the reconnect loop.\n if (streamStateRef.current === \"idle\") stream.reconnect();\n };\n // The connect effect already opens the socket on mount / a fresh url. The\n // ONLY gap it can't self-heal is a tab that was hidden when the url arrived\n // (container not in the DOM → connect effect bailed to idle): the socket then\n // never (re)fires on its own. So we revive it on the tab becoming visible —\n // NOT on mount (that double-opens the socket the connect effect just opened).\n document.addEventListener(\"visibilitychange\", maybeReattach);\n return () => document.removeEventListener(\"visibilitychange\", maybeReattach);\n // Re-run ONLY on a fresh live url, never on a state transition.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [hasLiveUrl, connectCapability?.url]);\n\n // ── CONNECT WATCHDOG ─────────────────────────────────────────────────────────\n // If a live url is present but the RFB hasn't reached \"connected\" within the\n // window, stop spinning forever and surface \"Couldn't connect\" + Reconnect.\n // Cleared whenever we connect, the url rotates, or the user reconnects.\n const [connectTimedOut, setConnectTimedOut] = useState(false);\n // Bumped on every manual reconnect so the watchdog re-arms its window (the\n // stream hook's url/error don't change on a reconnect, so we need our own key).\n const [reconnectNonce, setReconnectNonce] = useState(0);\n useEffect(() => {\n // Re-arm on a fresh url / a reconnect and clear the moment we connect or hit a\n // real RFB error. We deliberately do NOT key on `stream.state`, so the benign\n // idle→connecting walk doesn't keep resetting the window — the full timeout\n // runs from the url becoming live.\n setConnectTimedOut(false);\n if (!hasLiveUrl || connected || stream.error || connectTimeoutMs <= 0) return;\n const timer = setTimeout(() => setConnectTimedOut(true), connectTimeoutMs);\n return () => clearTimeout(timer);\n }, [\n hasLiveUrl,\n connected,\n connectTimeoutMs,\n connectCapability?.url,\n stream.error,\n reconnectNonce,\n ]);\n\n const reconnect = () => {\n setConnectTimedOut(false);\n setReconnectNonce((n) => n + 1);\n stream.reconnect();\n };\n\n // ── Resolve the single UI state (priority order) ────────────────────────────\n let uiState: DesktopUiState;\n if (viewerCapReached) uiState = \"viewer_cap\";\n else if (hardUnavailable) uiState = \"unavailable\";\n else if (needsAck && !isWatching) uiState = \"consent\";\n else if (coldWarmable) uiState = \"warming\";\n else if (connected) uiState = \"connected\";\n else if (stream.error) uiState = \"error\";\n else if (connectTimedOut) uiState = \"connect_failed\";\n else uiState = \"connecting\"; // hasLiveUrl && not yet connected (idle/negotiating/connecting).\n\n // An overlay blocks the surface for every state except connected; the toggle\n // and the live scrim are suppressed under an overlay.\n const overlayShown = uiState !== \"connected\" && uiState !== \"connecting\";\n const showToggle = showControlToggle && !overlayShown;\n\n let overlay: ReactNode = null;\n switch (uiState) {\n case \"viewer_cap\":\n overlay =\n renderViewerCap?.() ??\n defaultNotice(\n \"Too many viewers\",\n \"This session has reached its live-viewer limit. Try again shortly.\",\n );\n break;\n case \"unavailable\":\n overlay =\n renderUnavailable?.(reason) ??\n defaultNotice(\"Desktop unavailable\", unavailableCopy(reason));\n break;\n case \"consent\":\n overlay = renderConsentGate ? (\n renderConsentGate(accept, capability?.shared ?? false)\n ) : (\n <DefaultConsentGate shared={capability?.shared ?? false} onAccept={accept} />\n );\n break;\n case \"warming\":\n overlay = renderWarming?.() ?? <WarmingNotice />;\n break;\n case \"connect_failed\":\n overlay = defaultNotice(\n \"Couldn’t connect\",\n \"The desktop is warm but the live stream didn’t come up. This usually clears on a retry.\",\n reconnect,\n );\n break;\n case \"error\":\n overlay = defaultNotice(\n \"Desktop disconnected\",\n stream.error?.message ?? \"The stream dropped.\",\n reconnect,\n );\n break;\n case \"connecting\":\n case \"connected\":\n overlay = null;\n break;\n }\n\n return (\n <div\n className={cn(\n \"relative h-full w-full overflow-hidden\",\n inControl && \"ring-2 ring-inset ring-og-accent\",\n className,\n )}\n style={{ backgroundColor: DESKTOP_MEDIA_BACKGROUND }}\n data-opengeni-desktop\n data-state={stream.state}\n data-ui-state={uiState}\n data-in-control={inControl ? \"true\" : undefined}\n >\n {/* noVNC mount target. It appends a `width:100%;height:100%` `_screen` div\n and AUTOSCALES the 1280x800 framebuffer to fit THIS box. We pin it to\n the bounded `relative` wrapper with `absolute inset-0` (not just\n `h-full w-full`) so its measured size is ALWAYS the panel — never the\n canvas content. A content-sized mount is exactly what makes noVNC\n measure a huge screen and paint the desktop \"zoomed in\". `overflow-hidden`\n keeps the centered (margin:auto) canvas from ever spilling the panel. */}\n <div\n ref={containerRef}\n className=\"absolute inset-0 overflow-hidden\"\n data-opengeni-desktop-canvas\n data-state={stream.state}\n />\n\n {/* Idle / connecting scrim: a quiet, ALIVE \"connecting to the desktop\"\n state behind the canvas so the surface never reads as a dead black\n rectangle before the first framebuffer paints. Only shown in the\n `connecting` UI-state (every other non-connected state has an explicit\n overlay). A spinner + transitional copy makes it feel live. */}\n {uiState === \"connecting\" && (\n <div className=\"pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle\">\n <span className=\"relative flex items-center justify-center\">\n <MonitorIcon className=\"size-8 opacity-30\" strokeWidth={1.5} />\n <LoaderCircleIcon\n className=\"absolute size-12 animate-og-spin text-og-accent opacity-70\"\n strokeWidth={1.25}\n />\n </span>\n <span className=\"text-og-sm\">Connecting to the desktop…</span>\n </div>\n )}\n\n {/* Take-control affordance. Two distinct states:\n - WATCHING → a prominent, centered call-to-action button overlaid on\n the desktop (the primary CTA; tasteful so the screen stays visible).\n - IN CONTROL → a small top bar (\"You're in control · click Return\n control (or Ctrl+Alt+Shift)\") with a clearly-visible Return-control\n button; the desktop stays fully usable and EVERY key — Esc included —\n passes through to the box. Server-gated: when the deployment is\n read-only the CTA renders disabled with a reason so it degrades\n gracefully. */}\n {showToggle && !externallyControlled && inControl && (\n <InControlBar\n shared={capability?.shared ?? false}\n onRelease={() => setTakeControl(false)}\n />\n )}\n {/* The big CTA only appears once the framebuffer is live + the viewer is\n watching: before connect the scrim already communicates state, so we\n don't double up. A read-only deployment (serverAllowsControl=false) still\n surfaces the CTA disabled-with-reason once connected, so it degrades\n gracefully and stays discoverable. */}\n {showToggle && !externallyControlled && !inControl && connected && (\n <TakeControlCallToAction\n disabled={!serverAllowsControl}\n disabledReason={\n !serverAllowsControl\n ? capability?.client === \"frames\"\n ? \"View-only — live control isn't available for this machine yet.\"\n : \"This deployment streams the desktop read-only.\"\n : undefined\n }\n onTakeControl={() => setTakeControl(true)}\n />\n )}\n\n {overlay && (\n <div className=\"absolute inset-0 flex items-center justify-center p-4\">{overlay}</div>\n )}\n </div>\n );\n}\n\n/**\n * The primary WATCHING-state call-to-action: a large, centered pill button\n * overlaid on the desktop inviting the viewer to drive the mouse & keyboard.\n * Tasteful by default — it sits in the LOWER-center with a soft scrim only behind\n * the button (not the whole screen) and lifts on hover, so a watcher can still see\n * the agent work. Server-gated: when the deployment is read-only (or the desktop\n * hasn't connected yet) it renders disabled with a reason. Accessible: a real\n * <button> with a title, focus ring, and Enter/Space activation.\n */\nfunction TakeControlCallToAction({\n disabled,\n disabledReason,\n onTakeControl,\n}: {\n disabled: boolean;\n disabledReason?: string | undefined;\n onTakeControl: () => void;\n}) {\n return (\n // The wrapper spans the surface but is click-through (pointer-events-none); only\n // the button itself is interactive, so watchers can still see the desktop.\n <div className=\"pointer-events-none absolute inset-0 flex items-end justify-center pb-[8%]\">\n <button\n type=\"button\"\n disabled={disabled}\n aria-label=\"Take control of the desktop\"\n title={disabled ? disabledReason : \"Take control of the desktop\"}\n onClick={onTakeControl}\n className={cn(\n \"group pointer-events-auto flex items-center gap-3 rounded-og-lg border px-5 py-3\",\n \"border-og-border\",\n \"bg-og-bg/85 backdrop-blur-md\",\n \"shadow-og-lg\",\n \"outline-none transition-all duration-150 ease-out\",\n \"focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg\",\n disabled\n ? \"cursor-not-allowed opacity-60\"\n : cn(\n \"cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100\",\n \"hover:border-og-accent\",\n \"hover:bg-og-accent/10\",\n ),\n )}\n >\n <span\n className={cn(\n \"flex size-9 shrink-0 items-center justify-center rounded-full transition-colors\",\n \"bg-og-accent\",\n \"text-og-accent-fg\",\n disabled ? \"\" : \"group-hover:scale-105\",\n )}\n >\n <MousePointerClickIcon className=\"size-5\" strokeWidth={2} />\n </span>\n <span className=\"flex flex-col items-start leading-tight\">\n <span className=\"text-og-base font-semibold text-og-fg\">Take control</span>\n <span className=\"text-og-xs text-og-fg-subtle\">\n {disabled && disabledReason ? disabledReason : \"Drive the mouse & keyboard\"}\n </span>\n </span>\n </button>\n </div>\n );\n}\n\n/**\n * The IN-CONTROL state: a small top bar so the desktop stays fully usable while\n * driving (the accent ring around the viewport carries the primary \"you're\n * driving\" signal). Every keystroke — including Esc — passes through to the box,\n * so release lives only on explicit, non-trapping affordances: a clearly-visible\n * \"Return control\" button and the Ctrl+Alt+Shift chord. The button is solid\n * (not a faint ghost) so it's always discoverable as the way out.\n */\nfunction InControlBar({ shared, onRelease }: { shared: boolean; onRelease: () => void }) {\n return (\n <div className=\"pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between gap-2 p-2\">\n <span className=\"pointer-events-auto inline-flex items-center gap-2 rounded-og-sm bg-og-accent px-2.5 py-1 text-og-xs font-medium text-og-accent-fg shadow-og-md\">\n <span className=\"size-1.5 animate-pulse rounded-full bg-current\" aria-hidden />\n You're in control\n <span className=\"opacity-75\">· click Return control (or Ctrl+Alt+Shift)</span>\n </span>\n <div className=\"pointer-events-auto flex items-center gap-1.5\">\n {shared && (\n <span className=\"rounded-og-sm bg-og-status-failed/85 px-2 py-0.5 text-og-xs text-og-accent-fg\">\n Shared sandbox — others are watching\n </span>\n )}\n <button\n type=\"button\"\n onClick={onRelease}\n title=\"Return control (or press Ctrl+Alt+Shift)\"\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors\",\n \"border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm\",\n \"outline-none hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent\",\n )}\n >\n <MonitorIcon className=\"size-3.5\" strokeWidth={2} aria-hidden />\n Return control\n </button>\n </div>\n </div>\n );\n}\n\nfunction unavailableCopy(reason: CapabilityUnavailableReason | null): string {\n switch (reason) {\n case \"backend_unsupported\":\n return \"This sandbox backend cannot stream a desktop.\";\n case \"tier_headless\":\n return \"This deployment is headless — terminal, files, and diff only.\";\n case \"os_unsupported\":\n return \"The sandbox OS does not support a desktop stream.\";\n case \"not_provisioned\":\n return \"This sandbox doesn't have a desktop yet.\";\n case \"disabled_by_policy\":\n return \"Desktop streaming is disabled on this deployment.\";\n case \"lease_cold\":\n // Should not reach the unavailable notice (lease_cold warms), but keep\n // friendly copy as a fallback rather than the old dead-end wording.\n return \"Waiting for the sandbox to start…\";\n default:\n return \"The desktop isn’t available for this sandbox.\";\n }\n}\n\n/**\n * The WARMING state: a cold-but-warmable box that is being spun up. A spinner +\n * clear, accurate copy — and (when the host wired a manual warm) an explicit\n * retry so a slow warm never feels stuck. This is the cure for the old\n * \"Desktop unavailable — Start a turn or attach to warm it\" dead-end: the box IS\n * being warmed automatically; we just tell the user it's happening.\n */\nfunction WarmingNotice({ onRetry }: { onRetry?: (() => void) | undefined }) {\n return (\n <div className=\"flex max-w-sm flex-col items-center gap-3 rounded-og-lg border border-og-border bg-og-bg/90 p-5 text-center text-og-base text-og-fg backdrop-blur-sm\">\n <LoaderCircleIcon className=\"size-7 animate-og-spin text-og-accent\" strokeWidth={1.5} />\n <div className=\"space-y-1\">\n <div className=\"font-medium\">Warming the sandbox…</div>\n <p className=\"text-og-sm text-og-fg-subtle\">\n Spinning up the desktop — this takes a few seconds.\n </p>\n </div>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm text-og-fg-muted transition-colors hover:text-og-fg pointer-coarse:min-h-11\"\n >\n Taking too long? Retry\n </button>\n )}\n </div>\n );\n}\n\nfunction DefaultConsentGate({ shared, onAccept }: { shared: boolean; onAccept: () => void }) {\n return (\n <div className=\"max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg\">\n <div className=\"mb-1 font-medium\">Watch the live desktop?</div>\n <p className=\"mb-3 text-og-sm text-og-fg-subtle\">\n The desktop pixel stream is <strong>un-redacted</strong> — it can show secrets the agent\n prints on screen.\n {shared\n ? \" This sandbox is shared: you'll also see other sessions' agents on the same screen.\"\n : \"\"}\n </p>\n <button\n type=\"button\"\n onClick={onAccept}\n className=\"rounded-og-sm bg-og-accent px-3 py-1.5 text-og-sm font-medium text-og-accent-fg pointer-coarse:min-h-11\"\n >\n I understand — show the desktop\n </button>\n </div>\n );\n}\n\nfunction defaultNotice(title: string, body: string, onRetry?: () => void): ReactNode {\n return (\n <div className=\"max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg\">\n <div className=\"mb-1 flex items-center justify-center gap-1.5 font-medium\">\n {onRetry && <WifiOffIcon className=\"size-4 opacity-70\" strokeWidth={1.75} />}\n {title}\n </div>\n <p className=\"text-og-sm text-og-fg-subtle\">{body}</p>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"mt-3 rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm transition-colors hover:border-og-accent pointer-coarse:min-h-11\"\n >\n Reconnect\n </button>\n )}\n </div>\n );\n}\n","import {\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\nimport {\n ChevronsLeftRightIcon,\n Maximize2Icon,\n Minimize2Icon,\n PanelRightCloseIcon,\n XIcon,\n} from \"lucide-react\";\nimport {\n Group,\n Panel,\n Separator,\n useDefaultLayout,\n usePanelRef,\n type LayoutStorage,\n} from \"react-resizable-panels\";\nimport { cn } from \"../lib/cn\";\n\nexport type WorkspaceTab = {\n id: string;\n label: ReactNode;\n /** Rendered as the active surface. */\n content: ReactNode;\n /** A small badge after the label (e.g. dirty count, live pill). */\n badge?: ReactNode | undefined;\n};\n\nexport type WorkspaceDockProps = {\n /** The chat / primary pane shown beside the dock. */\n primary: ReactNode;\n tabs: WorkspaceTab[];\n /** Controlled active tab. Falls back to the first tab. */\n activeTab?: string | undefined;\n onActiveTabChange?: ((id: string) => void) | undefined;\n /** Controlled collapsed state for hosts that expose their own dock toggle. */\n collapsed?: boolean | undefined;\n onCollapsedChange?: ((collapsed: boolean) => void) | undefined;\n /** A status accessory pinned to the right of the tab strip, left of the\n * maximize/collapse controls (e.g. the machine-state chip). Renders in both\n * the docked header and the full-screen overlay header. */\n headerAccessory?: ReactNode | undefined;\n /** Optional host navigation shown at the start of the phone overlay header.\n * It is intentionally absent from desktop dock chrome. */\n mobileLeadingControl?: ReactNode | undefined;\n /** Persisted layout id (localStorage key) for react-resizable-panels. */\n autoSaveId?: string | undefined;\n /** Default dock width as a percent of the session area. */\n defaultSize?: number | undefined;\n minSize?: number | undefined;\n maxSize?: number | undefined;\n className?: string | undefined;\n};\n\nconst SERVER_LAYOUT_STORAGE: LayoutStorage = {\n getItem: () => null,\n setItem: () => {},\n};\n\n/** `useDefaultLayout` defaults to the ambient `localStorage` identifier when\n * storage is undefined, which throws during SSR. Access can also throw in a\n * browser that blocks storage (sandboxed frames/private policy), so both cases\n * use a stable, non-persisting implementation instead. */\nfunction getLayoutStorage(): LayoutStorage {\n if (typeof window === \"undefined\") return SERVER_LAYOUT_STORAGE;\n try {\n return window.localStorage;\n } catch {\n return SERVER_LAYOUT_STORAGE;\n }\n}\n\n/**\n * The resizable / collapsible / maximizable right-hand Workspace dock. Replaces\n * a fixed grid column: drag the separator to set width, collapse to a thin rail\n * that re-opens on click, and maximize to a full-workspace overlay (Esc /\n * restore button returns). Layout persists via `useDefaultLayout` keyed on\n * `autoSaveId`. Maximize is a mode ABOVE the Group (a `fixed inset-0` overlay) —\n * pushing a Panel to ~100% still fights min sizes and leaves a chat sliver.\n *\n * Below {@link DOCK_OVERLAY_BREAKPOINT} the side-by-side split can't work on a\n * phone-width viewport, so the resizable panels are dropped entirely: the\n * primary pane goes full-width and the dock becomes a full-screen overlay driven\n * by the same `collapsed` / `onCollapsedChange` contract (collapsed → hidden).\n * No drag splitter renders below the breakpoint.\n */\nconst useDockLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\n/**\n * The dock stops being a side column at this width and becomes a full-screen\n * overlay — matches the app's rail-drawer breakpoint (the single `isMobile`\n * source). The package can't read app context, so it detects the width itself.\n */\nconst DOCK_OVERLAY_BREAKPOINT = 1024;\n\n/** SSR-safe `(max-width: …)` match; false until mounted, then live. */\nfunction useIsNarrow(maxWidth: number): boolean {\n const [narrow, setNarrow] = useState(false);\n useDockLayoutEffect(() => {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") {\n return;\n }\n const mql = window.matchMedia(`(max-width: ${maxWidth - 1}px)`);\n const update = () => setNarrow(mql.matches);\n update();\n if (typeof mql.addEventListener === \"function\") {\n mql.addEventListener(\"change\", update);\n return () => mql.removeEventListener(\"change\", update);\n }\n // Legacy Safari.\n mql.addListener(update);\n return () => mql.removeListener(update);\n }, [maxWidth]);\n return narrow;\n}\n\nexport function WorkspaceDock({\n primary,\n tabs,\n activeTab,\n onActiveTabChange,\n collapsed: collapsedProp,\n onCollapsedChange,\n headerAccessory,\n mobileLeadingControl,\n autoSaveId = \"og.session.dock\",\n defaultSize = 34,\n minSize = 22,\n maxSize = 70,\n className,\n}: WorkspaceDockProps) {\n const narrow = useIsNarrow(DOCK_OVERLAY_BREAKPOINT);\n const rootRef = useRef<HTMLDivElement | null>(null);\n const reopenRef = useRef<HTMLButtonElement | null>(null);\n const returnFocusRef = useRef<HTMLElement | null>(null);\n const initialNarrowFocusRef = useRef(false);\n const dockPanelRef = usePanelRef();\n const [internalCollapsed, setInternalCollapsed] = useState(false);\n const [maximized, setMaximized] = useState(false);\n const [internalTab, setInternalTab] = useState(tabs[0]?.id ?? \"\");\n const collapsed = collapsedProp ?? internalCollapsed;\n // When the host supplies `collapsed` it owns the open/close affordance (e.g.\n // the app header's panel toggle) — the dock must not offer a SECOND\n // open/close control (duplicate buttons with near-identical icons read as a\n // bug). Standalone (uncontrolled) usage keeps the built-in collapse button\n // and the thin re-open rail as its only affordances.\n const hostControlled = collapsedProp !== undefined;\n const previousCollapsedRef = useRef(collapsed);\n\n // Persisted layout (width split) keyed by autoSaveId.\n const { defaultLayout, onLayoutChanged } = useDefaultLayout({\n panelIds: [\"primary\", \"dock\"],\n storage: getLayoutStorage(),\n id: autoSaveId,\n });\n\n const requestedTab = activeTab ?? internalTab;\n const tabIds = tabs.map((tab) => tab.id);\n const firstTabId = tabs[0]?.id ?? \"\";\n const requestedTabIsValid = tabIds.includes(requestedTab);\n const current = requestedTabIsValid ? requestedTab : firstTabId;\n const setTab = useCallback(\n (id: string) => {\n setInternalTab(id);\n onActiveTabChange?.(id);\n },\n [onActiveTabChange],\n );\n const setCollapsed = useCallback(\n (next: boolean) => {\n setInternalCollapsed((previous) => (previous === next ? previous : next));\n onCollapsedChange?.(next);\n },\n [onCollapsedChange],\n );\n\n useDockLayoutEffect(() => {\n if (collapsedProp === undefined) {\n return;\n }\n if (collapsedProp) {\n dockPanelRef.current?.collapse();\n setMaximized(false);\n } else {\n dockPanelRef.current?.expand();\n }\n }, [collapsedProp, dockPanelRef]);\n\n // Treat the narrow overlay like a real modal: remember the external opener,\n // move focus into the selected tab, and restore it on close. The standalone\n // desktop dock instead focuses its newly-visible reopen rail after collapse.\n useEffect(() => {\n const previous = previousCollapsedRef.current;\n previousCollapsedRef.current = collapsed;\n if (previous === collapsed) return;\n\n if (!collapsed) {\n const active = document.activeElement;\n const workspaceSurface = rootRef.current?.querySelector<HTMLElement>(\n narrow ? '[role=\"dialog\"]' : \"[data-workspace-surface]\",\n );\n if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {\n returnFocusRef.current = active;\n }\n if (narrow) {\n const frame = requestAnimationFrame(() => {\n rootRef.current\n ?.querySelector<HTMLElement>('[role=\"dialog\"] [role=\"tab\"][aria-selected=\"true\"]')\n ?.focus();\n });\n return () => cancelAnimationFrame(frame);\n }\n return;\n }\n\n const frame = requestAnimationFrame(() => {\n if (!hostControlled) {\n reopenRef.current?.focus();\n return;\n }\n const target = returnFocusRef.current;\n if (target?.isConnected) target.focus();\n });\n return () => cancelAnimationFrame(frame);\n }, [collapsed, hostControlled, narrow]);\n\n // On the first narrow render there is no collapsed-state transition to drive\n // focus into the modal: the overlay can already be open at mount. Place focus\n // on its selected tab once, just as we do when an existing overlay is opened.\n useEffect(() => {\n if (!narrow || collapsed || initialNarrowFocusRef.current) return;\n initialNarrowFocusRef.current = true;\n const active = document.activeElement;\n const workspaceSurface = rootRef.current?.querySelector<HTMLElement>('[role=\"dialog\"]');\n if (active instanceof HTMLElement && !workspaceSurface?.contains(active)) {\n returnFocusRef.current = active;\n }\n const frame = requestAnimationFrame(() => {\n workspaceSurface?.querySelector<HTMLElement>('[role=\"tab\"][aria-selected=\"true\"]')?.focus();\n });\n return () => cancelAnimationFrame(frame);\n }, [collapsed, narrow]);\n\n // Keep the active tab valid if the available tabs change.\n useEffect(() => {\n if (firstTabId && !requestedTabIsValid) {\n setTab(firstTabId);\n }\n // Depend on tab identity, not the tab content objects. Session live events\n // rebuild tab JSX frequently; only id changes can invalidate the active tab.\n }, [firstTabId, requestedTabIsValid, setTab]);\n\n const collapse = useCallback(() => {\n dockPanelRef.current?.collapse();\n setCollapsed(true);\n }, [dockPanelRef, setCollapsed]);\n const expand = useCallback(() => {\n dockPanelRef.current?.expand();\n setCollapsed(false);\n }, [dockPanelRef, setCollapsed]);\n\n // Esc restores from maximize (desktop) and closes the mobile overlay. Tab is\n // fenced inside either full-screen surface: both visually cover the host, so\n // keyboard focus must never move into invisible content behind them.\n useEffect(() => {\n const overlayOpen = maximized || (narrow && !collapsed);\n if (!overlayOpen) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (maximized) setMaximized(false);\n else collapse();\n return;\n }\n if (event.key !== \"Tab\") return;\n const surface = rootRef.current?.querySelector<HTMLElement>(\n narrow ? '[role=\"dialog\"]' : \"[data-workspace-surface]\",\n );\n if (!surface) return;\n const focusable = Array.from(surface.querySelectorAll<HTMLElement>(\"*\")).filter(\n (element) =>\n element.tabIndex >= 0 &&\n !element.hasAttribute(\"disabled\") &&\n element.getClientRects().length > 0 &&\n getComputedStyle(element).visibility !== \"hidden\",\n );\n const first = focusable[0];\n const last = focusable.at(-1);\n const active = document.activeElement;\n if (!first || !last) {\n event.preventDefault();\n surface.focus();\n return;\n }\n if (!surface.contains(active)) {\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n return;\n }\n if ((event.shiftKey && active === first) || (!event.shiftKey && active === last)) {\n event.preventDefault();\n (event.shiftKey ? last : first).focus();\n return;\n }\n // Focus can emerge from a shadow-DOM editor/highlighter host at a point\n // the light-DOM order cannot predict. Repair any escape after the browser\n // performs its default Tab move.\n requestAnimationFrame(() => {\n const next = document.activeElement;\n if (!surface.contains(next)) (event.shiftKey ? last : first).focus();\n });\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [maximized, narrow, collapsed, collapse]);\n\n // Below the breakpoint the dock is a full-screen overlay, not a resizable\n // column: primary goes full-width and no splitter ever mounts. The overlay is\n // driven by the same `collapsed` contract (collapsed → hidden).\n if (narrow) {\n return (\n <div ref={rootRef} className={cn(\"relative flex h-full min-h-0 w-full min-w-0\", className)}>\n <div\n data-workspace-primary\n aria-hidden={!collapsed ? true : undefined}\n className=\"min-h-0 min-w-0 flex-1\"\n inert={!collapsed ? true : undefined}\n >\n {primary}\n </div>\n <div\n data-workspace-surface\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Workspace\"\n className=\"fixed inset-0 z-40 flex flex-col bg-og-bg\"\n hidden={collapsed}\n style={{\n paddingTop: \"env(safe-area-inset-top)\",\n paddingBottom: \"env(safe-area-inset-bottom)\",\n }}\n >\n <DockChrome\n tabs={tabs}\n current={current}\n onTab={setTab}\n leading={mobileLeadingControl}\n accessory={headerAccessory}\n controls={\n <ChromeButton onClick={collapse} title=\"Close workspace\" label=\"Close workspace\">\n <XIcon className=\"size-4\" />\n </ChromeButton>\n }\n />\n </div>\n {collapsed && !hostControlled ? (\n <button\n ref={reopenRef}\n type=\"button\"\n onClick={expand}\n title=\"Open workspace\"\n aria-label=\"Open workspace\"\n className=\"absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent\"\n style={{ marginTop: \"env(safe-area-inset-top)\" }}\n >\n <ChevronsLeftRightIcon className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n );\n }\n\n const dockChrome = (\n <DockChrome\n tabs={tabs}\n current={current}\n onTab={setTab}\n accessory={headerAccessory}\n controls={\n <>\n <ChromeButton\n onClick={() => setMaximized((m) => !m)}\n title={maximized ? \"Restore (Esc)\" : \"Maximize\"}\n label={maximized ? \"Restore dock\" : \"Maximize dock\"}\n >\n {maximized ? (\n <Minimize2Icon className=\"size-3.5\" />\n ) : (\n <Maximize2Icon className=\"size-3.5\" />\n )}\n </ChromeButton>\n {hostControlled ? null : (\n <ChromeButton onClick={collapse} title=\"Collapse\" label=\"Collapse dock\">\n <PanelRightCloseIcon className=\"size-3.5\" />\n </ChromeButton>\n )}\n </>\n }\n />\n );\n\n return (\n <div ref={rootRef} className={cn(\"relative flex h-full min-h-0 w-full min-w-0\", className)}>\n <Group\n orientation=\"horizontal\"\n className=\"min-h-0 flex-1\"\n {...(defaultLayout ? { defaultLayout } : {})}\n onLayoutChanged={onLayoutChanged}\n >\n <Panel id=\"primary\" minSize=\"30%\" className=\"min-h-0 min-w-0\">\n <div\n data-workspace-primary\n aria-hidden={maximized ? true : undefined}\n className=\"h-full min-h-0 min-w-0\"\n inert={maximized ? true : undefined}\n >\n {primary}\n </div>\n </Panel>\n\n {!collapsed && (\n <Separator className=\"group relative z-10 w-1.5 shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11\">\n <span className=\"absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent\" />\n </Separator>\n )}\n\n <Panel\n id=\"dock\"\n panelRef={dockPanelRef}\n collapsible\n collapsedSize=\"0%\"\n defaultSize={`${defaultSize}%`}\n minSize={`${minSize}%`}\n maxSize={`${maxSize}%`}\n onResize={(size, _id, previousSize) => {\n // `asPercentage` is 0..100; treat a near-zero panel as collapsed.\n const isCollapsed = size.asPercentage <= 1;\n const canInferCollapse = collapsedProp === undefined || previousSize !== undefined;\n if (canInferCollapse && isCollapsed !== collapsed) {\n setCollapsed(isCollapsed);\n }\n }}\n className=\"min-h-0 min-w-0\"\n >\n {/* One persistent mount across normal, collapsed, and maximized modes:\n layout changes must never destroy an editor buffer or terminal view. */}\n <div\n data-workspace-surface\n role={maximized ? \"dialog\" : undefined}\n aria-modal={maximized ? true : undefined}\n aria-label={maximized ? \"Workspace\" : undefined}\n aria-hidden={collapsed ? true : undefined}\n className={cn(\n \"flex h-full min-h-0 min-w-0 flex-col bg-og-bg\",\n maximized ? \"fixed inset-0 z-40\" : \"border-l border-og-border\",\n collapsed && \"invisible pointer-events-none\",\n )}\n >\n {dockChrome}\n </div>\n </Panel>\n </Group>\n\n {/* Collapsed rail: the standalone fallback re-open affordance. Hidden\n when the host controls collapse — its own toggle is the one way in. */}\n {collapsed && !maximized && !hostControlled && (\n <button\n ref={reopenRef}\n type=\"button\"\n onClick={expand}\n title=\"Open workspace\"\n className=\"absolute inset-y-0 right-0 flex w-6 shrink-0 items-center justify-center border-l border-og-border bg-og-surface-1 text-og-fg-subtle hover:text-og-fg\"\n >\n <ChevronsLeftRightIcon className=\"size-3.5\" />\n </button>\n )}\n </div>\n );\n}\n\n/** A dock-chrome control button — compact on fine pointers, ≥40px on coarse. */\nfunction ChromeButton({\n onClick,\n title,\n label,\n children,\n}: {\n onClick: () => void;\n title: string;\n label: string;\n children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n aria-label={label}\n className=\"inline-flex size-7 items-center justify-center rounded-og-sm p-1 transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:size-11 pointer-coarse:size-11\"\n >\n {children}\n </button>\n );\n}\n\nfunction DockChrome({\n tabs,\n current,\n onTab,\n leading,\n accessory,\n controls,\n}: {\n tabs: WorkspaceTab[];\n current: string;\n onTab: (id: string) => void;\n /** Host navigation at the start of mobile overlay chrome. */\n leading?: ReactNode | undefined;\n /** A status accessory (machine chip) between the tab strip and the controls. */\n accessory?: ReactNode | undefined;\n /** Right-aligned chrome controls (maximize / collapse, or the overlay close). */\n controls: ReactNode;\n}) {\n const active = tabs.find((t) => t.id === current) ?? tabs[0];\n const tabsetId = useId();\n const tabRefs = useRef<Array<HTMLButtonElement | null>>([]);\n const tabListRef = useRef<HTMLDivElement | null>(null);\n const [visitedTabs, setVisitedTabs] = useState<Set<string>>(() => new Set());\n const [tabOverflow, setTabOverflow] = useState({ start: false, end: false });\n const activeId = active?.id ?? \"\";\n const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);\n const keepActiveTabVisible = useCallback(() => {\n const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;\n selected?.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n }, [activeTabIndex]);\n\n useEffect(() => {\n if (!activeId) return;\n setVisitedTabs((previous) => {\n if (previous.has(activeId)) return previous;\n const next = new Set(previous);\n next.add(activeId);\n return next;\n });\n }, [activeId]);\n\n // A controlled/initial tab can become active without receiving focus (for\n // example, a host routing a guarded diff into Files). Native scrolling only\n // follows focus, so keep the selected tab inside the horizontally scrollable\n // strip even when state — rather than a click or arrow key — selected it.\n useDockLayoutEffect(() => {\n keepActiveTabVisible();\n }, [activeId, keepActiveTabVisible]);\n\n // Hidden scrollbars keep the chrome quiet, but without an edge cue a clipped\n // host label reads like corrupted copy. Fade only the edge(s) with more tabs,\n // and recompute after scrolling, resizing, or late font metrics.\n useDockLayoutEffect(() => {\n const list = tabListRef.current;\n if (!list) return;\n const update = () => {\n const max = Math.max(0, list.scrollWidth - list.clientWidth);\n const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };\n setTabOverflow((previous) =>\n previous.start === next.start && previous.end === next.end ? previous : next,\n );\n };\n update();\n list.addEventListener(\"scroll\", update, { passive: true });\n const observer =\n typeof ResizeObserver === \"undefined\"\n ? null\n : new ResizeObserver(() => {\n keepActiveTabVisible();\n update();\n });\n observer?.observe(list);\n for (const tab of tabRefs.current) {\n if (tab) observer?.observe(tab);\n }\n return () => {\n list.removeEventListener(\"scroll\", update);\n observer?.disconnect();\n };\n }, [activeId, keepActiveTabVisible, tabs.length]);\n\n const tabMask =\n tabOverflow.start && tabOverflow.end\n ? \"linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%)\"\n : tabOverflow.start\n ? \"linear-gradient(to right, transparent 0, black 14px, black 100%)\"\n : tabOverflow.end\n ? \"linear-gradient(to right, black 0, black calc(100% - 14px), transparent 100%)\"\n : undefined;\n\n const activateTab = (index: number) => {\n const tab = tabs[index];\n if (!tab) {\n return;\n }\n onTab(tab.id);\n tabRefs.current[index]?.focus();\n };\n\n const onTabKeyDown = (event: ReactKeyboardEvent<HTMLButtonElement>, index: number) => {\n if (tabs.length === 0) return;\n let nextIndex: number | null = null;\n if (event.key === \"ArrowRight\") nextIndex = (index + 1) % tabs.length;\n else if (event.key === \"ArrowLeft\") nextIndex = (index - 1 + tabs.length) % tabs.length;\n else if (event.key === \"Home\") nextIndex = 0;\n else if (event.key === \"End\") nextIndex = tabs.length - 1;\n if (nextIndex === null) return;\n event.preventDefault();\n activateTab(nextIndex);\n };\n\n return (\n <>\n <div className=\"grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]\">\n <div className=\"flex min-w-0 shrink-0 items-center max-[1023px]:min-h-11 min-[640px]:max-[1023px]:col-start-1 min-[640px]:max-[1023px]:row-start-1\">\n {leading ?? (\n <span className=\"hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline\">\n Workspace\n </span>\n )}\n </div>\n {/* The tab list scrolls horizontally when it can't fit — it must never\n grow into or overlap the chrome controls (they stay shrink-0). The\n scrollbar is hidden to keep the strip calm. On the narrow overlay it\n owns a full second row, so status/close chrome can never squeeze the\n canonical workspace navigation out of view. Tablets have enough\n width to retain the denser single-row chrome, with this same strip\n absorbing any unusually long host-injected labels. */}\n <div\n ref={tabListRef}\n className=\"flex min-w-0 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-[1023px]:col-span-3 max-[1023px]:row-start-2 max-[1023px]:w-full min-[640px]:max-[1023px]:col-span-1 min-[640px]:max-[1023px]:col-start-2 min-[640px]:max-[1023px]:row-start-1\"\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n style={{\n maskImage: tabMask,\n WebkitMaskImage: tabMask,\n scrollPaddingInline: \"16px\",\n }}\n >\n {tabs.map((tab, index) => (\n <button\n key={tab.id}\n ref={(node) => {\n tabRefs.current[index] = node;\n }}\n id={`${tabsetId}-tab-${index}`}\n type=\"button\"\n role=\"tab\"\n aria-selected={tab.id === current}\n aria-controls={`${tabsetId}-panel-${index}`}\n tabIndex={tab.id === current ? 0 : -1}\n onClick={() => onTab(tab.id)}\n onKeyDown={(event) => onTabKeyDown(event, index)}\n className={cn(\n \"flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11\",\n tab.id === current\n ? \"bg-og-accent-soft text-og-fg\"\n : \"text-og-fg-subtle hover:text-og-fg\",\n )}\n >\n <span data-contrast-audited>{tab.label}</span>\n {tab.badge}\n </button>\n ))}\n </div>\n <div className=\"flex min-w-0 shrink-0 items-center justify-self-end min-[640px]:max-[1023px]:col-start-3 min-[640px]:max-[1023px]:row-start-1\">\n {accessory}\n </div>\n <div className=\"flex shrink-0 items-center gap-0.5 text-og-fg-subtle min-[640px]:max-[1023px]:col-start-4 min-[640px]:max-[1023px]:row-start-1\">\n {controls}\n </div>\n </div>\n <div className=\"min-h-0 min-w-0 flex-1 overflow-hidden\">\n {tabs.length > 0 ? (\n tabs.map((tab, index) => {\n const selected = tab.id === activeId;\n const shouldMount = selected || visitedTabs.has(tab.id);\n return (\n <div\n key={tab.id}\n id={`${tabsetId}-panel-${index}`}\n aria-labelledby={`${tabsetId}-tab-${index}`}\n className=\"h-full min-h-0 min-w-0 overflow-hidden\"\n hidden={!selected}\n role=\"tabpanel\"\n >\n {shouldMount ? tab.content : null}\n </div>\n );\n })\n ) : (\n <div className=\"h-full min-h-0 min-w-0\" aria-label=\"Workspace\" role=\"tabpanel\" />\n )}\n </div>\n </>\n );\n}\n","// The sandbox workbench — the SDK-embeddable dock \"brain\" (Workbench v2, M4).\n//\n// This module owns the whole session workspace surface an embedder mounts:\n// Changes — review-first git: turn-end capture (cold) or live diff (warm)\n// Files — tree + inline editor (capture-backed cold, live warm)\n// Terminal — interactive xterm wired to the box PTY (Channel-A projection)\n// Desktop — noVNC (Channel-B), watch by default + a server-gated take-control\n// plus a machine-state chip in the dock header (the one truthful live/waking/\n// offline indicator) and any host-injected extra tabs (Run/Debug in apps/web).\n//\n// It is decoupled from the host app: the client comes from <OpenGeniProvider>\n// (never an app context), notifications flow through an optional `onNotify` prop\n// (no `sonner` import), and every surface renders with package primitives + og\n// tokens only. `apps/web` consumes this through the exact public surface an\n// external embedder uses — that is criterion F1.\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Popover } from \"radix-ui\";\nimport type { SessionEvent } from \"@opengeni/sdk\";\nimport {\n CircleCheckIcon,\n CpuIcon,\n LaptopIcon,\n LoaderCircleIcon,\n RefreshCwIcon,\n TriangleAlertIcon,\n} from \"lucide-react\";\n\nimport { type ClientOverride, useOpenGeni } from \"../provider\";\nimport { cn } from \"../lib/cn\";\nimport { xtermThemeFromTokens } from \"../lib/xterm-theme\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport { useSessionCapabilities } from \"../hooks/use-session-capabilities\";\nimport { useSandboxFiles } from \"../hooks/use-sandbox-files\";\nimport { useSandboxGit, type UseSandboxGitResult } from \"../hooks/use-sandbox-git\";\nimport { useSandboxTerminal } from \"../hooks/use-sandbox-terminal\";\nimport { useWorkspaceCapture } from \"../hooks/use-workspace-capture\";\nimport { formatAsOf, useMachineChip, type MachineChip } from \"../hooks/use-machine-chip\";\nimport { useMachines } from \"../hooks/use-machines\";\nimport type { MachineView } from \"../types/machines\";\nimport { connectionStatusForState } from \"../types/machines\";\nimport { ConnectionStatusPill } from \"./machine-status-pill\";\nimport { SharedMachineDisclosure } from \"./machine-dock-bar\";\nimport { SandboxFiles } from \"./sandbox-files\";\nimport { WorkbenchChanges } from \"./workbench-changes\";\nimport { SandboxTerminal, type XtermTheme } from \"./sandbox-terminal\";\nimport { DesktopViewer } from \"./desktop-viewer\";\nimport { WorkspaceDock, type WorkspaceDockProps, type WorkspaceTab } from \"./workspace-dock\";\n\n/** A host-routed notification (replaces the app-only `sonner` toast coupling). */\nexport type WorkspaceNotification = { kind: \"error\" | \"info\"; message: string };\n\n/** The workbench's canonical tab ids (a host injects extras around these). */\nexport const WORKBENCH_TAB_CHANGES = \"changes\";\nexport const WORKBENCH_TAB_FILES = \"files\";\nexport const WORKBENCH_TAB_TERMINAL = \"terminal\";\nexport const WORKBENCH_TAB_DESKTOP = \"desktop\";\n\n/**\n * The built-in workbench surfaces in their canonical display order. Hosts can\n * pass a subset through `surfaces`; omission preserves the complete standalone\n * workbench.\n */\nexport const WORKBENCH_SURFACES = [\n WORKBENCH_TAB_CHANGES,\n WORKBENCH_TAB_FILES,\n WORKBENCH_TAB_TERMINAL,\n WORKBENCH_TAB_DESKTOP,\n] as const;\n\nexport type SandboxWorkspaceSurface = (typeof WORKBENCH_SURFACES)[number];\n\nfunction isWorkbenchSurface(value: string): value is SandboxWorkspaceSurface {\n return (WORKBENCH_SURFACES as readonly string[]).includes(value);\n}\n\nfunction sourceDrivenDefaultTab(\n hasChanges: boolean,\n changesEnabled: boolean,\n filesEnabled: boolean,\n): string | null {\n if (hasChanges && changesEnabled) return WORKBENCH_TAB_CHANGES;\n if (filesEnabled) return WORKBENCH_TAB_FILES;\n if (changesEnabled) return WORKBENCH_TAB_CHANGES;\n return null;\n}\n\nfunction captureDegradedMessage(reason: string): string {\n switch (reason) {\n case \"repository_discovery_timed_out\":\n return \"Workspace capture is incomplete because repository discovery timed out. Live files remain authoritative.\";\n case \"repository_discovery_result_limit_exceeded\":\n return \"Workspace capture is incomplete because the repository limit was exceeded. Live files remain authoritative.\";\n case \"repository_read_unavailable\":\n return \"Workspace capture is incomplete because repository changes could not be read. Live files remain authoritative.\";\n default:\n return \"Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.\";\n }\n}\n\n/**\n * Decide a workspace tab from an already-local event log: the newest\n * `workspace.revision.captured` announce carries the change surface stats, so\n * \"changes exist → Changes, else Files\" needs zero machine round-trips. A host\n * `override` (e.g. a landing \"run\" tab) wins when supplied.\n *\n * `<SandboxWorkspace>` uses this only as an optional early hint after its own\n * capture GET reports no durable capture. A pure embedder needs no events-at-mount\n * contract: it falls through to authoritative live Git instead.\n */\nexport function initialWorkspaceTab(\n events: SessionEvent[] | undefined,\n override?: string | null,\n): string {\n if (override) return override;\n let bestSeq = -1;\n let bestFileCount = 0;\n for (const event of events ?? []) {\n if (event.type !== \"workspace.revision.captured\") continue;\n if (event.sequence <= bestSeq) continue;\n const stats = (event.payload as { stats?: { fileCount?: number } } | null)?.stats;\n bestSeq = event.sequence;\n bestFileCount = typeof stats?.fileCount === \"number\" ? stats.fileCount : 0;\n }\n return bestFileCount > 0 ? WORKBENCH_TAB_CHANGES : WORKBENCH_TAB_FILES;\n}\n\n/**\n * Whether a lazy sandbox provision is in flight on this event stream: the latest\n * `sandbox.provision` operation event is a `.started` not yet closed by a\n * `.completed`/`.failed`. Package-local twin of apps/web's events helper so the\n * dock brain can wake its on-demand capability negotiation when the box warms.\n */\nfunction sandboxProvisionInFlight(events: SessionEvent[]): boolean {\n let inFlight = false;\n for (const event of events) {\n if (\n event.type !== \"sandbox.operation.started\" &&\n event.type !== \"sandbox.operation.completed\" &&\n event.type !== \"sandbox.operation.failed\"\n ) {\n continue;\n }\n const payload = event.payload;\n const name =\n payload && typeof payload === \"object\" && !Array.isArray(payload)\n ? (payload as Record<string, unknown>).name\n : null;\n if (name !== \"sandbox.provision\") continue;\n if (event.type === \"sandbox.operation.started\") {\n inFlight = true;\n } else {\n inFlight = false;\n }\n }\n return inFlight;\n}\n\nexport type WorkspaceMachine = {\n /** Whether at least one built-in workbench surface is enabled. */\n enabled: boolean;\n /** The derived live/waking/offline chip model. */\n chip: MachineChip;\n /** The machine these surfaces are bound to (the Modal group box or a\n * self-hosted machine), or null while the fleet is still resolving. */\n activeMachine: MachineView | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\nexport type UseSandboxWorkspaceTabsOptions = ClientOverride & {\n sessionId: string;\n /** Live event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /**\n * Built-in surfaces this host wants to expose. Omit for all four. A disabled\n * surface is behaviorally dormant: its data hooks, warm intents, stream\n * attachment, and navigation callbacks are not activated.\n */\n surfaces?: readonly SandboxWorkspaceSurface[] | undefined;\n /** Override the source-driven default tab (e.g. a host landing tab id). When\n * omitted the workbench picks Changes-vs-Files from capture or live Git. */\n initialTab?: string | null | undefined;\n /** Host-routed notifications (mutation errors, desktop-consent failures). The\n * package never imports a toast library — the host decides how to surface. */\n onNotify?: ((notification: WorkspaceNotification) => void) | undefined;\n /** File requested by a Changes guard. The Files surface defers the read until\n * the sandbox is live, then reveals this path. */\n requestedFilePath?: string | null | undefined;\n /** Unique identity for `requestedFilePath`, including repeated requests for the\n * same path. */\n requestedFileRequestId?: string | number | null | undefined;\n /** Route a guarded diff into the host's Files tab. */\n onOpenFile?: ((path: string) => void) | undefined;\n};\n\nexport type UseSandboxWorkspaceTabsResult = {\n /** Changes | Files | Terminal | Desktop (capability-gated where noted). */\n tabs: WorkspaceTab[];\n /** The source-driven default tab: Changes when the first authoritative capture\n * or live Git result has changes, else Files (a host `initialTab` overrides).\n * null while that source resolves, or permanently when neither Changes nor\n * Files is enabled; the choice latches once. */\n defaultTab: string | null;\n /** The machine-state model for the dock-header chip. */\n machine: WorkspaceMachine;\n};\n\ntype SessionWarmIntents = {\n sessionId: string;\n watchDesktop: boolean;\n warmTerminal: boolean;\n warmFiles: boolean;\n};\n\nfunction emptyWarmIntents(sessionId: string): SessionWarmIntents {\n return {\n sessionId,\n watchDesktop: false,\n warmTerminal: false,\n warmFiles: false,\n };\n}\n\n/**\n * Build the workbench tabs + the machine-chip model for one session. This is the\n * dock \"brain\": capability negotiation, capture-backed cold reads, prewarm\n * flags, desktop consent, and the xterm theme observer — all package-local.\n */\nexport function useSandboxWorkspaceTabs(\n options: UseSandboxWorkspaceTabsOptions,\n): UseSandboxWorkspaceTabsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const { sessionId, events, onNotify, requestedFilePath, requestedFileRequestId, onOpenFile } =\n options;\n const initialTab = options.initialTab ?? null;\n const requestedSurfaces = options.surfaces ?? WORKBENCH_SURFACES;\n const surfaceSet = new Set<SandboxWorkspaceSurface>(requestedSurfaces);\n const changesEnabled = surfaceSet.has(WORKBENCH_TAB_CHANGES);\n const filesEnabled = surfaceSet.has(WORKBENCH_TAB_FILES);\n const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);\n const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);\n const workspaceDataEnabled = changesEnabled || filesEnabled;\n const anySurfaceEnabled = workspaceDataEnabled || terminalEnabled || desktopEnabled;\n const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(\",\");\n\n // The three box-warming INTENTS, each off by default and each\n // flipped true by a genuine user action (never on mount, never on a passive\n // capture glance): desktop watch consent, terminal engagement (`onActivate`), and\n // a deliberate live-file open/edit in Files. Browsing capture-served\n // Changes/Files warms nothing — that is the whole point of Refinement 1.\n const [storedWarmIntents, setStoredWarmIntents] = useState<SessionWarmIntents>(() =>\n emptyWarmIntents(sessionId),\n );\n const warmIntents =\n storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);\n const { watchDesktop, warmTerminal, warmFiles } = warmIntents;\n const requestWarmIntent = useCallback(\n (intent: Exclude<keyof SessionWarmIntents, \"sessionId\">) => {\n setStoredWarmIntents((previous) => {\n const current = previous.sessionId === sessionId ? previous : emptyWarmIntents(sessionId);\n return current[intent] ? current : { ...current, [intent]: true };\n });\n },\n [sessionId],\n );\n\n // The session's machine fleet + the active-sandbox pointer. Drives the header\n // chip (which machine + its connection state). Polls slowly — ambient context.\n const machines = useMachines({\n workspaceId,\n sessionId,\n pollIntervalMs: 8000,\n enabled: anySurfaceEnabled,\n });\n const activeMachine: MachineView | null =\n machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ??\n machines.machines.find((m) => m.active) ??\n null;\n\n const caps = useSessionCapabilities(sessionId, {\n events,\n enabled: anySurfaceEnabled,\n attachDesktop: desktopEnabled && watchDesktop,\n attachTerminal: terminalEnabled && warmTerminal,\n // Explicit live-file intent only — NOT \"the Files tab is open\". A cold edit\n // or guarded-file open wakes the box; a glance at the tree/diff does not.\n attachFiles: workspaceDataEnabled && warmFiles,\n });\n const capabilities = caps.capabilities;\n const liveness = capabilities?.liveness;\n const fileSystemOn = capabilities?.FileSystem.available ?? false;\n // The FS is writable only when it's live AND not read-only. A self-hosted box\n // that's offline (or any read-only advertisement) or a capture-served cold tree\n // must not offer create/rename/delete/edit affordances — you cannot mutate a\n // machine you can't reach (C3). Tree-structure ops need a warm\n // writable box; content editing on a cold CLOUD box is the wake-on-edit path in\n // the editor, not tree mutation.\n const fsReadOnly = capabilities?.FileSystem.readOnly ?? false;\n // Mutations are live-only. A capture is a review artifact, never a writable\n // surrogate for a sleeping machine; the user must deliberately open the live\n // workspace before create/rename/delete/edit controls appear.\n const filesEditable =\n fileSystemOn && !fsReadOnly && (liveness === \"warm\" || liveness === \"draining\");\n const gitOn = capabilities?.Git.available ?? false;\n const terminalOn = terminalEnabled && (capabilities?.Terminal.transport ?? null) !== null;\n // The REAL interactive terminal is the ttyd pty-ws stream. When it's live the\n // legacy HTTP PTY must NOT run; `useSandboxTerminal` opens its HTTP PTY only as\n // the firehose-mode fallback (a backend without ttyd).\n const ptyWsLive =\n capabilities?.Terminal.transport === \"pty-ws\" && Boolean(capabilities?.Terminal.url);\n const ptyCapable = (capabilities?.Terminal.ptyCapable ?? false) && !ptyWsLive;\n const terminal = useSandboxTerminal(sessionId, {\n events: terminalEnabled ? events : [],\n interactive: terminalEnabled && ptyCapable,\n liveness,\n });\n const desktopAdvertised =\n desktopEnabled &&\n ((capabilities?.DesktopStream.transport ?? null) !== null ||\n capabilities?.DesktopStream.reason === \"lease_cold\");\n\n // Lazy provisioning (#315) creates the box mid-turn on the first sandbox tool\n // call, emitting sandbox.provision started→completed/failed on the live stream.\n // The on-demand resting hook rests without polling, so when the box warms the\n // cold capability doc never refreshes on its own — Terminal/Desktop would stay\n // hidden. Watch the provision edge and renegotiate when it settles so the\n // freshly-warm box's surfaces fill in.\n const provisioning = sandboxProvisionInFlight(events);\n const renegotiate = caps.renegotiate;\n const provisioningRef = useRef({ sessionId, provisioning });\n useEffect(() => {\n const previous = provisioningRef.current;\n if (previous.sessionId === sessionId && previous.provisioning && !provisioning) {\n renegotiate();\n }\n provisioningRef.current = { sessionId, provisioning };\n }, [sessionId, provisioning, renegotiate]);\n\n // The cold-paint data source: the latest turn-end capture, fetched with a single\n // api round-trip on mount (no machine). Feeds the Files tree + the Changes/Git\n // diff immediately; a warm box reconciles live without discarding the capture\n // when that provider read is temporarily unavailable.\n const captureState = useWorkspaceCapture(sessionId, {\n events,\n enabled: workspaceDataEnabled,\n });\n const captureAvailable = captureState.available;\n const liveWorkspaceExpected = liveness === \"warm\" || liveness === \"draining\";\n // Capability negotiation and capture resolution are independent requests. A\n // cold capability document can win that race by a few milliseconds; enabling\n // the leaf hooks in that window would make them interpret `capture: null` as a\n // real miss and issue Channel-A reads, waking the very box the capture exists\n // to avoid. Wait until the capture request has conclusively resolved. A warm\n // workspace never waits on this server-side snapshot.\n const capturePending =\n !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);\n const repoPaths = useMemo(() => {\n const advertised = capabilities?.Git.repos ?? [];\n if (advertised.length > 0) return advertised;\n return captureState.capture?.repos.map((repo) => repo.root) ?? [];\n }, [capabilities?.Git.repos, captureState.capture]);\n const notifiedCaptureDegradedReason = useRef<{\n sessionId: string;\n reason: string | null;\n }>({\n sessionId,\n reason: null,\n });\n if (notifiedCaptureDegradedReason.current.sessionId !== sessionId) {\n notifiedCaptureDegradedReason.current = { sessionId, reason: null };\n }\n useEffect(() => {\n const reason = captureState.degradedReason;\n if (!reason || notifiedCaptureDegradedReason.current.reason === reason) return;\n notifiedCaptureDegradedReason.current = { sessionId, reason };\n onNotify?.({ kind: \"error\", message: captureDegradedMessage(reason) });\n }, [sessionId, captureState.degradedReason, onNotify]);\n\n const files = useSandboxFiles(sessionId, {\n events,\n // No passive Channel-A reads while cold, even after a conclusive capture\n // miss. A missing capture gets an explicit \"Open live workspace\" gate.\n enabled: filesEnabled && (captureAvailable || (fileSystemOn && liveWorkspaceExpected)),\n liveness,\n capture: captureState.capture,\n // A reverted optimistic mutation (e.g. a 409 rename collision) surfaces as a\n // host notification — the tree silently rolls back, the user sees why.\n onMutationError: (error, op) =>\n onNotify?.({\n kind: \"error\",\n message: `Could not ${op}: ${error.message}`,\n }),\n });\n const git = useSandboxGit(sessionId, {\n events,\n enabled: workspaceDataEnabled && (captureAvailable || (gitOn && liveWorkspaceExpected)),\n repoPaths,\n liveness,\n capture: captureState.capture,\n });\n const stagedGit = useSandboxGit(sessionId, {\n events,\n // Captures intentionally expose one combined review diff, not a staged\n // index. Never probe the live index while the lease is cold.\n enabled: filesEnabled && gitOn && liveWorkspaceExpected,\n staged: true,\n repoPaths,\n liveness,\n capture: captureState.capture,\n });\n\n // Token-derived xterm theme; re-derive on a `data-og-theme` flip. Generic — it\n // belongs in the package (an embedder's theme toggle drives it too).\n const [xtermTheme, setXtermTheme] = useState<XtermTheme | undefined>(undefined);\n useEffect(() => {\n if (!terminalEnabled || typeof document === \"undefined\") return;\n const derive = () => setXtermTheme(xtermThemeFromTokens());\n derive();\n const observer = new MutationObserver(derive);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-og-theme\", \"class\"],\n });\n return () => observer.disconnect();\n }, [terminalEnabled]);\n\n async function acknowledgeAndWatch() {\n try {\n const shared = capabilities?.DesktopStream.shared ?? false;\n await client.acknowledgeStream(workspaceId, sessionId, {\n acknowledgeUnredacted: true,\n acknowledgeShared: shared,\n });\n requestWarmIntent(\"watchDesktop\");\n caps.renegotiate();\n } catch (error) {\n onNotify?.({\n kind: \"error\",\n message: `Could not start the desktop stream: ${error instanceof Error ? error.message : String(error)}`,\n });\n }\n }\n\n // Re-warm WITHOUT re-acknowledging: the consent was already recorded, only the\n // box drained back to cold. Idempotent — the viewer auto-warm de-dupes.\n function rewarmDesktop() {\n if (!watchDesktop) requestWarmIntent(\"watchDesktop\");\n caps.renegotiate();\n }\n\n const dirtyCount = git.diff.length;\n\n // The one truthful machine indicator, derived from the live capability/liveness\n // surface + the active machine's connection state + the latest capture time.\n const chip = useMachineChip({\n liveness,\n capabilitiesState: caps.state,\n activeMachineState: activeMachine?.state ?? null,\n activeIsSelfhosted: activeMachine?.kind === \"selfhosted\",\n wantsWarm:\n (terminalEnabled && warmTerminal) ||\n (desktopEnabled && watchDesktop) ||\n (workspaceDataEnabled && warmFiles),\n capturedAt: captureState.capturedAt,\n });\n const workspaceWaking = chip.state === \"waking\";\n\n // The pre-paint default tab is decided from the first AUTHORITATIVE workspace\n // source. A capture wins immediately on the cold/offline fast path; a warm box\n // waits for live Git so a prior snapshot can never outrank the current tree.\n // When the GET says no capture exists, `fileCount: 0` does NOT itself mean the\n // working tree is clean. A captured-revision announce can\n // resolve Changes earlier, but a pure embedder with no event preload still gets\n // the correct live default. The choice latches once so later edits never steal\n // the user's current tab.\n const defaultIdentity = `${sessionId}\\u0000${surfaceIdentity}`;\n const defaultTabRef = useRef<{ identity: string; value: string | null }>({\n identity: defaultIdentity,\n value: null,\n });\n if (defaultTabRef.current.identity !== defaultIdentity) {\n defaultTabRef.current = { identity: defaultIdentity, value: null };\n }\n const captureIsAuthoritative =\n !liveWorkspaceExpected && (liveness !== undefined || caps.error !== null);\n const captureUnavailable = captureState.fileCount === 0 || captureState.error !== null;\n if (defaultTabRef.current.value === null) {\n if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {\n defaultTabRef.current.value = initialTab;\n } else if (git.source === \"live\") {\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n git.diff.length > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (\n captureIsAuthoritative &&\n captureState.fileCount !== null &&\n captureState.available\n ) {\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n captureState.fileCount > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (\n captureIsAuthoritative &&\n captureUnavailable &&\n initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES\n ) {\n defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);\n } else if (captureUnavailable && caps.error) {\n // The Changes surface owns the truthful sandbox-unavailable state + retry.\n // Files would misleadingly describe this as a missing filesystem.\n defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);\n } else if (captureIsAuthoritative && captureState.fileCount !== null && captureUnavailable) {\n // No durable review surface exists and the machine is resting. Files owns\n // the explicit wake gate; do not issue a live Git probe merely to choose a\n // tab.\n defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);\n } else if (captureState.available && git.error && captureState.fileCount !== null) {\n // A warm live read failed but the durable capture is intact: retain an\n // immediate, deterministic review surface instead of hanging unresolved.\n defaultTabRef.current.value = sourceDrivenDefaultTab(\n captureState.fileCount > 0,\n changesEnabled,\n filesEnabled,\n );\n } else if (captureUnavailable && git.error) {\n // No capture and live Git failed: Files is the least surprising fallback\n // and preserves the established no-capture degraded behavior.\n defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);\n }\n }\n // null only during the brief pre-first-resolve window (no host override yet).\n const defaultTab = defaultTabRef.current.value;\n\n const tabs = useMemo(() => {\n const list: WorkspaceTab[] = [];\n\n // Changes — capture-backed and usable cold/offline when the host enables it.\n if (changesEnabled)\n list.push({\n id: WORKBENCH_TAB_CHANGES,\n label: \"Changes\",\n badge: dirtyCount > 0 ? <DirtyBadge count={dirtyCount} /> : undefined,\n content: (\n <ChangesTabBody\n git={git}\n captureAvailable={captureAvailable}\n captureRevision={captureState.revision}\n capturePending={capturePending}\n liveWorkspaceExpected={liveWorkspaceExpected}\n workspaceWaking={workspaceWaking}\n capabilitiesState={caps.state}\n capabilitiesError={caps.error}\n onRetry={caps.renegotiate}\n onWake={() => requestWarmIntent(\"warmFiles\")}\n {...(filesEnabled && onOpenFile\n ? {\n onOpenFile: (path: string) => {\n requestWarmIntent(\"warmFiles\");\n onOpenFile(path);\n },\n }\n : {})}\n />\n ),\n });\n\n // Files — capture-backed cold tree; live warm when the host enables it.\n if (filesEnabled)\n list.push({\n id: WORKBENCH_TAB_FILES,\n label: \"Files\",\n content: (\n <SandboxFiles\n key={sessionId}\n files={files}\n git={git}\n stagedGit={stagedGit}\n fileSystemAvailable={fileSystemOn || captureAvailable}\n editable={\n filesEditable && files.source === \"live\" && files.error === null && !files.loading\n }\n workspaceResting={\n !liveWorkspaceExpected &&\n liveness !== undefined &&\n !capturePending &&\n !captureAvailable &&\n !workspaceWaking\n }\n workspaceWaking={!liveWorkspaceExpected && !captureAvailable && workspaceWaking}\n liveWorkspaceReady={liveWorkspaceExpected}\n onWakeWorkspace={() => requestWarmIntent(\"warmFiles\")}\n {...(requestedFilePath\n ? {\n requestedPath: requestedFilePath,\n ...(requestedFileRequestId !== null && requestedFileRequestId !== undefined\n ? { requestedPathRequestId: requestedFileRequestId }\n : {}),\n requestedPathReady: liveness === \"warm\" || liveness === \"draining\",\n }\n : {})}\n // Ordinary capture browsing does not warm a box. The first edit — or an\n // explicit guarded-file open from Changes — is deliberate live-file intent.\n onEditIntent={() => requestWarmIntent(\"warmFiles\")}\n className=\"h-full\"\n />\n ),\n });\n\n // Terminal — capability-gated (appears after negotiation; never the default).\n if (terminalOn) {\n list.push({\n id: WORKBENCH_TAB_TERMINAL,\n label: \"Terminal\",\n content: (\n <div className=\"h-full bg-og-bg p-1\">\n <SandboxTerminal\n result={terminal}\n terminalCapability={capabilities?.Terminal ?? null}\n onActivate={() => requestWarmIntent(\"warmTerminal\")}\n showHeader\n shell={capabilities?.Terminal.shell ?? undefined}\n liveness={liveness}\n {...(xtermTheme ? { theme: xtermTheme } : {})}\n />\n </div>\n ),\n });\n }\n\n // Desktop — capability-gated + consent-gated.\n if (desktopAdvertised) {\n list.push({\n id: WORKBENCH_TAB_DESKTOP,\n label: \"Desktop\",\n badge: watchDesktop ? (\n <span className=\"rounded-og-xs bg-og-status-running/20 px-1 text-og-xs text-og-status-running\">\n Live\n </span>\n ) : undefined,\n content: (\n <DesktopViewer\n capability={capabilities?.DesktopStream ?? null}\n viewerCapReached={caps.viewerCapReached}\n watching={watchDesktop}\n onAcknowledge={() => void acknowledgeAndWatch()}\n onWarm={rewarmDesktop}\n className=\"h-full\"\n />\n ),\n });\n }\n\n return list;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n fileSystemOn,\n changesEnabled,\n filesEnabled,\n terminalOn,\n desktopAdvertised,\n captureAvailable,\n dirtyCount,\n watchDesktop,\n warmTerminal,\n requestedFilePath,\n requestedFileRequestId,\n onOpenFile,\n liveness,\n sessionId,\n files,\n git,\n stagedGit,\n terminal,\n xtermTheme,\n capabilities,\n workspaceWaking,\n caps.state,\n caps.error,\n caps.viewerCapReached,\n requestWarmIntent,\n ]);\n\n return {\n tabs,\n defaultTab,\n machine: {\n enabled: anySurfaceEnabled,\n chip,\n activeMachine,\n loading: machines.loading,\n error: machines.error,\n refresh: machines.refresh,\n },\n };\n}\n\nexport type SandboxWorkspaceProps = ClientOverride & {\n sessionId: string;\n /** Live event log (usually `useSessionEvents().events`). */\n events: SessionEvent[];\n /** The chat / primary pane shown beside the dock. */\n primary: ReactNode;\n /**\n * Built-in surfaces this host wants to expose. Omit for Changes, Files,\n * Terminal, and Desktop. Disabled surfaces remain behaviorally dormant.\n */\n surfaces?: readonly SandboxWorkspaceSurface[] | undefined;\n /** Host tabs injected BEFORE the workbench tabs (e.g. a \"Run\" landing tab). */\n leadingTabs?: WorkspaceTab[] | undefined;\n /** Host tabs injected AFTER the workbench tabs (e.g. a \"Debug\" tab). */\n trailingTabs?: WorkspaceTab[] | undefined;\n /** Override the pre-paint default tab (e.g. a host landing tab id). When\n * omitted the workbench decides from capture, then live Git when no capture exists. */\n initialTab?: string | undefined;\n /** Host-routed notifications (no toast dependency in the package). */\n onNotify?: ((notification: WorkspaceNotification) => void) | undefined;\n /** Controlled collapsed state for hosts with their own dock toggle. */\n collapsed?: boolean | undefined;\n onCollapsedChange?: ((collapsed: boolean) => void) | undefined;\n /** Host navigation shown only in the phone workspace overlay header. */\n mobileLeadingControl?: ReactNode | undefined;\n autoSaveId?: string | undefined;\n defaultSize?: number | undefined;\n minSize?: number | undefined;\n maxSize?: number | undefined;\n className?: string | undefined;\n};\n\n/**\n * The whole session workspace, ready to mount: `<SandboxWorkspace>` assembles the\n * capability-gated tabs, pins the machine-state chip in the dock header, and\n * renders the resizable `<WorkspaceDock>`. Wrap the tree in `<OpenGeniProvider>`\n * (client + workspaceId) and import `@opengeni/react/styles.css`; that is the\n * entire integration (see `docs/embedding-workbench.md`).\n */\nexport function SandboxWorkspace(props: SandboxWorkspaceProps): ReactNode {\n const {\n sessionId,\n events,\n primary,\n surfaces,\n leadingTabs,\n trailingTabs,\n initialTab,\n onNotify,\n collapsed,\n onCollapsedChange,\n mobileLeadingControl,\n autoSaveId,\n defaultSize,\n minSize,\n maxSize,\n className,\n } = props;\n\n const [storedSelection, setStoredSelection] = useState<{\n sessionId: string;\n tab: string;\n } | null>(null);\n const [requestedFile, setRequestedFile] = useState<{\n sessionId: string;\n path: string;\n requestId: number;\n } | null>(null);\n const nextFileRequestId = useRef(0);\n const openFile = useCallback(\n (path: string) => {\n nextFileRequestId.current += 1;\n setRequestedFile({ sessionId, path, requestId: nextFileRequestId.current });\n setStoredSelection({ sessionId, tab: WORKBENCH_TAB_FILES });\n },\n [sessionId],\n );\n\n const {\n tabs: workbenchTabs,\n machine,\n defaultTab,\n } = useSandboxWorkspaceTabs({\n ...(props.client ? { client: props.client } : {}),\n ...(props.workspaceId ? { workspaceId: props.workspaceId } : {}),\n sessionId,\n events,\n ...(surfaces ? { surfaces } : {}),\n ...(initialTab ? { initialTab } : {}),\n ...(onNotify ? { onNotify } : {}),\n requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,\n requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,\n onOpenFile: openFile,\n });\n\n // A user's tab click wins forever; before that we follow the source-driven\n // default. While it is still resolving (null, pure-embedder pre-first-resolve)\n // we pass no controlled tab, so the dock renders its own first-tab fallback\n // (Changes) — whose body is a connecting/loading state until the capture lands,\n // so committing the real default at first-resolve produces no CONTENT switch.\n const tabs: WorkspaceTab[] = [...(leadingTabs ?? []), ...workbenchTabs, ...(trailingTabs ?? [])];\n const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;\n const preferredTab = selectedTab ?? defaultTab;\n const activeTab =\n preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;\n const selectTab = useCallback(\n (tab: string) => setStoredSelection({ sessionId, tab }),\n [sessionId],\n );\n\n return (\n <WorkspaceDock\n primary={primary}\n tabs={tabs}\n {...(activeTab !== undefined ? { activeTab } : {})}\n onActiveTabChange={selectTab}\n {...(machine.enabled\n ? {\n headerAccessory: (\n <MachineStateChip\n chip={machine.chip}\n activeMachine={machine.activeMachine}\n error={machine.error}\n onRetry={() => void machine.refresh()}\n />\n ),\n }\n : {})}\n {...(mobileLeadingControl !== undefined ? { mobileLeadingControl } : {})}\n {...(collapsed !== undefined ? { collapsed } : {})}\n {...(onCollapsedChange ? { onCollapsedChange } : {})}\n {...(autoSaveId !== undefined ? { autoSaveId } : {})}\n {...(defaultSize !== undefined ? { defaultSize } : {})}\n {...(minSize !== undefined ? { minSize } : {})}\n {...(maxSize !== undefined ? { maxSize } : {})}\n {...(className !== undefined ? { className } : {})}\n />\n );\n}\n\n// Re-export the dock so an embedder can grab the shell type off one import.\nexport type { WorkspaceDockProps };\n\nfunction DirtyBadge({ count }: { count: number }) {\n return (\n <span className=\"rounded-og-xs bg-og-accent-soft px-1 text-og-xs text-og-fg-muted\">\n {count}\n </span>\n );\n}\n\n/** A machine-kind glyph for the chip popover. */\nfunction MachineKindIcon({\n kind,\n className,\n}: {\n kind: MachineView[\"kind\"] | undefined;\n className?: string;\n}) {\n const Icon = kind === \"selfhosted\" ? LaptopIcon : CpuIcon;\n return <Icon className={cn(\"size-3.5 shrink-0 text-og-fg-subtle\", className)} aria-hidden />;\n}\n\nfunction chipDotClass(state: MachineChip[\"state\"]): string {\n if (state === \"live\") return \"bg-og-status-running\";\n if (state === \"waking\") return \"bg-og-status-idle animate-pulse motion-reduce:animate-none\";\n return \"bg-og-fg-subtle\";\n}\n\n/**\n * The dock-header machine chip: the one truthful live/waking/offline indicator,\n * with a popover carrying the machine identity, connection state, the \"shown as\n * of <time>\" staleness note, the shared-session disclosure, and a retry when the\n * fleet failed to resolve (the old per-surface machine bar + Sandbox info tab,\n * folded into one header affordance — recommendation).\n */\nfunction MachineStateChip({\n chip,\n activeMachine,\n error,\n onRetry,\n}: {\n chip: MachineChip;\n activeMachine: MachineView | null;\n error: Error | null;\n onRetry: () => void;\n}) {\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const portalStyle = usePortalTokenStyle(triggerRef);\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <button\n ref={triggerRef}\n type=\"button\"\n aria-label={`Machine: ${chip.label}`}\n className=\"inline-flex min-h-7 items-center gap-1.5 rounded-og-sm px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11\"\n >\n <span\n className={cn(\"size-1.5 shrink-0 rounded-full\", chipDotClass(chip.state))}\n aria-hidden\n />\n <span className=\"max-w-[11rem] truncate\">{chip.label}</span>\n </button>\n </Popover.Trigger>\n <Popover.Portal forceMount>\n <Popover.Content\n forceMount\n data-machine-state-popover\n align=\"end\"\n sideOffset={6}\n collisionPadding={8}\n className=\"og-root z-50 w-64 max-w-[calc(100vw-1rem)] rounded-og-md border border-og-border bg-og-surface-1 p-3 text-og-sm text-og-fg shadow-lg outline-none\"\n style={portalStyle}\n >\n <div className=\"flex min-w-0 items-center gap-1.5\">\n <MachineKindIcon kind={activeMachine?.kind} />\n <span className=\"min-w-0 truncate font-medium\">{activeMachine?.name ?? \"Sandbox\"}</span>\n </div>\n <div className=\"mt-2.5 flex items-center justify-between gap-2\">\n <span className=\"text-og-xs text-og-fg-subtle\">Connection</span>\n {activeMachine ? (\n <ConnectionStatusPill\n status={connectionStatusForState(activeMachine.state)}\n size=\"sm\"\n />\n ) : (\n <span className=\"text-og-xs text-og-fg-muted\">{chip.label}</span>\n )}\n </div>\n {chip.asOf ? (\n <p className=\"mt-2.5 text-og-xs leading-4 text-og-fg-subtle\">\n Workspace shown as of {formatAsOf(chip.asOf, Date.now())} — the machine is not live.\n </p>\n ) : null}\n {activeMachine && activeMachine.sharedSessionCount > 1 ? (\n <div className=\"mt-2.5\">\n <SharedMachineDisclosure\n sharedSessionCount={activeMachine.sharedSessionCount}\n density=\"full\"\n />\n </div>\n ) : null}\n {error ? (\n <div className=\"mt-2.5 space-y-1.5 border-t border-og-border pt-2.5\">\n <p className=\"text-og-xs leading-4 text-og-danger\">\n Couldn't reach the sandbox for this session.\n </p>\n <DockActionButton onClick={onRetry}>\n <RefreshCwIcon className=\"size-3\" />\n Retry\n </DockActionButton>\n </div>\n ) : null}\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n}\n\n/** A minimal token-styled action button (the package has no app Button import). */\nfunction DockActionButton({ onClick, children }: { onClick: () => void; children: ReactNode }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className=\"inline-flex items-center gap-1.5 rounded-og-sm border border-og-border px-2 py-1 text-og-xs font-medium text-og-fg-muted transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 pointer-coarse:min-h-11\"\n >\n {children}\n </button>\n );\n}\n\n/**\n * The Changes tab body: the real PR-review surface (`WorkbenchChanges` — file\n * rail + windowed Pierre diff pane) when there are changes, wrapped in the honest\n * connecting/offline/empty states so the default tab is never a blank surface.\n * The dock frame is untouched; this is the M5 seam.\n */\nfunction ChangesTabBody({\n git,\n captureAvailable,\n captureRevision,\n capturePending,\n liveWorkspaceExpected,\n workspaceWaking,\n capabilitiesState,\n capabilitiesError,\n onRetry,\n onWake,\n onOpenFile,\n}: {\n git: UseSandboxGitResult;\n captureAvailable: boolean;\n captureRevision: number | null;\n capturePending: boolean;\n liveWorkspaceExpected: boolean;\n workspaceWaking: boolean;\n capabilitiesState: string;\n capabilitiesError: Error | null;\n onRetry: () => void;\n onWake: () => void;\n onOpenFile?: ((path: string) => void) | undefined;\n}) {\n const diff = git.diff;\n\n if (diff.length > 0) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n {git.error ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n data-opengeni-changes-degraded\n className=\"flex shrink-0 items-center gap-2 border-b border-og-status-running/30 bg-og-status-running/10 px-2 py-1.5 text-og-xs text-og-fg-muted\"\n >\n <TriangleAlertIcon className=\"size-3.5 shrink-0 text-og-status-running\" aria-hidden />\n <span className=\"min-w-0 flex-1\">\n {git.source === \"capture\"\n ? \"Live changes are temporarily unavailable. Showing the latest captured revision.\"\n : \"Live refresh failed. Showing the last loaded changes.\"}\n </span>\n <button\n type=\"button\"\n onClick={() => void git.refresh()}\n disabled={git.loading}\n className=\"inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11\"\n >\n <RefreshCwIcon\n className={cn(\"size-3\", git.loading && \"animate-spin motion-reduce:animate-none\")}\n aria-hidden\n />\n Retry\n </button>\n </div>\n ) : null}\n <div className=\"min-h-0 flex-1\">\n <WorkbenchChanges\n diff={diff}\n source={git.source}\n capturedAt={git.capturedAt}\n captureRevision={captureRevision}\n {...(onOpenFile ? { onOpenFile } : {})}\n />\n </div>\n </div>\n );\n }\n\n if (capabilitiesError && !captureAvailable) {\n return (\n <CenteredState icon={<TriangleAlertIcon className=\"size-5\" aria-hidden />} tone=\"danger\">\n <p className=\"text-og-sm font-medium text-og-fg\">Sandbox unavailable</p>\n <p data-contrast-audited className=\"text-og-sm leading-5 text-og-fg-muted\">\n {capabilitiesError.message || \"Couldn't reach the sandbox for this session.\"}\n </p>\n <DockActionButton onClick={onRetry}>\n <RefreshCwIcon className=\"size-3\" />\n Retry\n </DockActionButton>\n </CenteredState>\n );\n }\n\n if (\n (capturePending || capabilitiesState === \"negotiating\") &&\n !captureAvailable &&\n !workspaceWaking\n ) {\n return (\n <CenteredState\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">Connecting workspace</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n Looking for the latest files and changes…\n </p>\n </CenteredState>\n );\n }\n\n if (!liveWorkspaceExpected && !captureAvailable) {\n return (\n <CenteredState\n icon={\n workspaceWaking ? (\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n ) : (\n <CpuIcon className=\"size-5\" aria-hidden />\n )\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">\n {workspaceWaking ? \"Waking workspace\" : \"Workspace is resting\"}\n </p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n {workspaceWaking\n ? \"Connecting to the live working tree…\"\n : \"No captured revision is available yet. Wake the sandbox to inspect current changes.\"}\n </p>\n {!workspaceWaking ? (\n <DockActionButton onClick={onWake}>\n <CpuIcon className=\"size-3\" />\n Open live workspace\n </DockActionButton>\n ) : null}\n </CenteredState>\n );\n }\n\n if (git.loading && git.source === null) {\n return (\n <CenteredState\n icon={\n <LoaderCircleIcon\n className=\"size-5 animate-spin motion-reduce:animate-none\"\n aria-hidden\n />\n }\n >\n <p className=\"text-og-sm font-medium text-og-fg\">Loading workspace</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">Reading the current working tree…</p>\n </CenteredState>\n );\n }\n\n return (\n <CenteredState icon={<CircleCheckIcon className=\"size-5\" aria-hidden />} tone=\"success\">\n <p className=\"text-og-sm font-medium text-og-fg\">Working tree is clean</p>\n <p className=\"text-og-sm leading-5 text-og-fg-subtle\">\n File edits from future turns will appear here.\n </p>\n </CenteredState>\n );\n}\n\nfunction CenteredState({\n children,\n icon,\n tone = \"neutral\",\n}: {\n children: ReactNode;\n icon?: ReactNode | undefined;\n tone?: \"neutral\" | \"success\" | \"danger\";\n}) {\n return (\n <div\n role={tone === \"danger\" ? \"alert\" : \"status\"}\n aria-atomic=\"true\"\n aria-live={tone === \"danger\" ? \"assertive\" : \"polite\"}\n className=\"grid h-full place-items-center p-6 text-center\"\n >\n <div className=\"flex max-w-sm flex-col items-center gap-2.5\">\n {icon ? (\n <span\n className={cn(\n \"grid size-10 place-items-center rounded-og-lg border bg-og-surface-1 shadow-sm\",\n tone === \"success\" && \"border-og-status-idle/30 text-og-status-idle\",\n tone === \"danger\" && \"border-og-status-failed/30 text-og-status-failed\",\n tone === \"neutral\" && \"border-og-border text-og-fg-muted\",\n )}\n >\n {icon}\n </span>\n ) : null}\n {children}\n </div>\n </div>\n );\n}\n","import { useCallback, useState } from \"react\";\nimport type {\n CodexAccount,\n CodexAccountsResponse,\n CodexRotationSettings,\n SessionEvent,\n} from \"@opengeni/sdk\";\nimport { useOpenGeni, type ClientOverride } from \"../provider\";\nimport {\n useMutationRunner,\n usePolledValue,\n useSessionEventTrigger,\n type SessionEventFeedOptions,\n} from \"./internal\";\n\n/** Events that change which Codex account a session runs on (or just ran). */\nexport function isCodexAccountEvent(event: Pick<SessionEvent, \"type\">): boolean {\n return event.type === \"codex.account.switched\" || event.type === \"turn.started\";\n}\n\n/**\n * The structural slice of the SDK client the Codex-accounts surface needs. Method\n * NAMES + SIGNATURES match `OpenGeniClient` so the real client satisfies it\n * directly; declared structurally (not a hard Pick) so a test/Geni client can\n * stand in. `getSession` reads the session's pin/last; `pinSessionCodexAccount`\n * is the optional mutation (absent ⇒ the indicator hides the switch affordance).\n */\nexport type CodexAccountsClientLike = {\n listCodexAccounts: (workspaceId: string) => Promise<CodexAccountsResponse>;\n getSession?: (\n workspaceId: string,\n sessionId: string,\n ) => Promise<{ codexPinnedCredentialId?: string | null; codexLastCredentialId?: string | null }>;\n pinSessionCodexAccount?: (\n workspaceId: string,\n sessionId: string,\n target: string,\n ) => Promise<{ pinned: string }>;\n /** Optional (absent ⇒ the card hides live refresh): batched live /wham/usage refresh. */\n refreshCodexUsage?: (workspaceId: string) => Promise<{ usage: Record<string, unknown> }>;\n};\n\nexport type UseCodexAccountsOptions = ClientOverride &\n SessionEventFeedOptions & {\n pollIntervalMs?: number | undefined;\n /** Scope to a session so the hook resolves the pin + the effective account. */\n sessionId?: string | undefined;\n /** Override the client with one implementing `CodexAccountsClientLike`. */\n codexClient?: CodexAccountsClientLike | undefined;\n };\n\nexport type UseCodexAccountsResult = {\n accounts: CodexAccount[];\n /** The workspace ACTIVE account (used when a session is unpinned). */\n activeAccountId: string | null;\n /** The session's PINNED account (null ⇒ following workspace active). */\n pinnedAccountId: string | null;\n /** The account the next turn will run on: pin > workspace active. */\n effectiveAccountId: string | null;\n /** The account the session's last turn ACTUALLY ran on (the \"Running on:\" source). */\n lastAccountId: string | null;\n settings: CodexRotationSettings;\n loading: boolean;\n refresh: () => Promise<void>;\n /**\n * Trigger a LIVE batched /wham/usage refresh across all accounts, then re-read\n * the cached metadata so the new windows land on `accounts`. Modeled on `pin`.\n * No-op (resolves false) when the client can't refresh usage.\n */\n refreshUsage: () => Promise<boolean>;\n /** True while a live usage refresh is in flight (drives the bar skeleton). */\n refreshingUsage: boolean;\n /** Pin (or unpin via \"auto\") the session's account; returns true on success. */\n pin: (target: string) => Promise<boolean>;\n pinning: boolean;\n /** The target of the in-flight pin (for per-row spinner gating). */\n pinningTarget: string | null;\n mutationError: Error | null;\n};\n\nconst EMPTY_SETTINGS: CodexRotationSettings = {\n rotationEnabled: false,\n rotationStrategy: \"most_remaining\",\n activeCredentialId: null,\n};\n\ntype CodexAccountsState = {\n accounts: CodexAccount[];\n activeAccountId: string | null;\n settings: CodexRotationSettings;\n pinnedAccountId: string | null;\n lastAccountId: string | null;\n};\n\nconst EMPTY_STATE: CodexAccountsState = {\n accounts: [],\n activeAccountId: null,\n settings: EMPTY_SETTINGS,\n pinnedAccountId: null,\n lastAccountId: null,\n};\n\n/**\n * The workspace's Codex accounts + the per-workspace active pointer + (when\n * session-scoped) the session pin and last-ran-on account. Composed like\n * `useMachines`: slow polling (the realtime work is done by the\n * `codex.account.switched` / `turn.started` event trigger) + a `pin` mutation.\n * Dual-consumer safe via the structural `CodexAccountsClientLike` surface.\n */\nexport function useCodexAccounts(options: UseCodexAccountsOptions = {}): UseCodexAccountsResult {\n const { client, workspaceId } = useOpenGeni(options);\n const codexClient = options.codexClient ?? (client as unknown as CodexAccountsClientLike);\n const sessionId = options.sessionId;\n const sharedEvents = options.events;\n\n const load = useCallback(async (): Promise<CodexAccountsState> => {\n const accountsP = codexClient.listCodexAccounts(workspaceId);\n const sessionP =\n sessionId && codexClient.getSession\n ? codexClient.getSession(workspaceId, sessionId).catch(() => null)\n : Promise.resolve(null);\n const [acc, session] = await Promise.all([accountsP, sessionP]);\n return {\n accounts: acc.accounts,\n activeAccountId: acc.activeAccountId,\n settings: acc.settings,\n pinnedAccountId: session?.codexPinnedCredentialId ?? null,\n lastAccountId: session?.codexLastCredentialId ?? null,\n };\n }, [codexClient, workspaceId, sessionId]);\n\n const {\n data: loadedData,\n loading,\n refresh,\n } = usePolledValue(load, {\n pollIntervalMs: options.pollIntervalMs,\n enabled: options.enabled,\n });\n const { run: runMutation, mutating: pinning, mutationError } = useMutationRunner();\n const { run: runUsageMutation, mutating: refreshingUsage } = useMutationRunner();\n const [pinningTarget, setPinningTarget] = useState<string | null>(null);\n\n // Live flip: a manual switch (P1) or a failover (P3) emits codex.account.switched;\n // turn.started covers the case where the worker recorded the actual account.\n useSessionEventTrigger(\n client,\n workspaceId,\n sessionId,\n isCodexAccountEvent,\n () => void refresh(),\n {\n enabled: options.enabled ?? true,\n ...(sharedEvents !== undefined ? { events: sharedEvents } : {}),\n },\n );\n\n const pin = useCallback(\n async (target: string): Promise<boolean> => {\n if (!sessionId || !codexClient.pinSessionCodexAccount) {\n return false;\n }\n setPinningTarget(target);\n const result = await runMutation(async () => {\n await codexClient.pinSessionCodexAccount!(workspaceId, sessionId, target);\n return true;\n });\n setPinningTarget(null);\n if (result) await refresh();\n return result === true;\n },\n [codexClient, workspaceId, sessionId, runMutation, refresh],\n );\n\n // Live usage refresh: hit the batched provider read, then re-read cached\n // metadata so the fresh windows land on `accounts`. The provider read writes the\n // cache columns server-side; state.refresh() pulls them back.\n const refreshUsage = useCallback(async (): Promise<boolean> => {\n if (!codexClient.refreshCodexUsage) {\n return false;\n }\n const result = await runUsageMutation(async () => {\n await codexClient.refreshCodexUsage!(workspaceId);\n return true;\n });\n if (result) await refresh();\n return result === true;\n }, [codexClient, workspaceId, runUsageMutation, refresh]);\n\n const data = loadedData ?? EMPTY_STATE;\n const effectiveAccountId = data.pinnedAccountId ?? data.activeAccountId;\n\n return {\n accounts: data.accounts,\n activeAccountId: data.activeAccountId,\n pinnedAccountId: data.pinnedAccountId,\n effectiveAccountId,\n lastAccountId: data.lastAccountId,\n settings: data.settings,\n loading,\n refresh,\n refreshUsage,\n refreshingUsage,\n pin,\n pinning,\n pinningTarget,\n mutationError,\n };\n}\n"],"mappings":"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","names":["useCallback","useCallback","useCallback","useEffect","useEffect","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useCallback","useEffect","useRef","useRef","useCallback","useEffect","useEffect","useRef","useState","useEffect","useRef","useState","useState","useRef","useEffect","useEffect","useMemo","useRef","useState","OpenGeniApiError","useCallback","useEffect","useMemo","useRef","useState","useState","useRef","useEffect","useMemo","OpenGeniApiError","useCallback","useCallback","useEffect","useRef","useState","isLive","useState","useRef","useCallback","useEffect","useCallback","useEffect","useMemo","useRef","useState","useMemo","isLive","useState","useRef","useCallback","useEffect","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","useMemo","useEffect","useRef","useState","useState","useRef","useEffect","jsx","jsxs","jsxs","jsx","CommandPalette","ModelPicker","jsx","jsxs","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useEffect","useCallback","useEffect","useId","useMemo","useRef","useState","useEffect","useMemo","useMemo","useEffect","Fragment","jsx","jsxs","useEffect","parentOf","useState","useId","useRef","useCallback","useMemo","item","useCallback","useEffect","useId","useLayoutEffect","useMemo","useRef","useState","VList","useCallback","useEffect","useMemo","useRef","useState","jsx","jsxs","useEffect","useLayoutEffect","STATUS_TINT","useRef","useState","useId","useMemo","useCallback","VList","jsx","useEffect","useState","jsx","useState","useEffect","useCallback","useEffect","useMemo","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","useMemo","FileWarningIcon","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","FileWarningIcon","LoaderCircleIcon","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useEffect","LoaderCircleIcon","useCallback","useEffect","useId","useLayoutEffect","useRef","useState","XIcon","Fragment","jsx","jsxs","useEffect","useLayoutEffect","useState","useRef","useCallback","frame","XIcon","useId","useCallback","useEffect","useMemo","useRef","useState","LoaderCircleIcon","RefreshCwIcon","TriangleAlertIcon","jsx","jsxs","useState","useCallback","useRef","useEffect","useMemo","RefreshCwIcon","TriangleAlertIcon","LoaderCircleIcon","useCallback","useState","useCallback","useState"]}
|