@av-pi-studio/web-client 0.0.45 → 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/web/assets/{BinaryFallbackViewer-DN2oT6N7.js → BinaryFallbackViewer-C8VjuRB5.js} +2 -2
- package/dist/web/assets/{BinaryFallbackViewer-DN2oT6N7.js.map → BinaryFallbackViewer-C8VjuRB5.js.map} +1 -1
- package/dist/web/assets/ChatPanel-EEteI2qS.js +3 -0
- package/dist/web/assets/ChatPanel-EEteI2qS.js.map +1 -0
- package/dist/web/assets/{CodeView-DwUKpXwU.js → CodeView-CKUKYQ4A.js} +2 -2
- package/dist/web/assets/{CodeView-DwUKpXwU.js.map → CodeView-CKUKYQ4A.js.map} +1 -1
- package/dist/web/assets/FilePanel-Dg4iw-OH.js +2 -0
- package/dist/web/assets/FilePanel-Dg4iw-OH.js.map +1 -0
- package/dist/web/assets/{ImageViewer-BKflylHn.js → ImageViewer-CiKV0OZI.js} +2 -2
- package/dist/web/assets/{ImageViewer-BKflylHn.js.map → ImageViewer-CiKV0OZI.js.map} +1 -1
- package/dist/web/assets/MarkdownFileViewer-DHWrkv5i.js +2 -0
- package/dist/web/assets/{MarkdownFileViewer-BDmo3la-.js.map → MarkdownFileViewer-DHWrkv5i.js.map} +1 -1
- package/dist/web/assets/{MoleculeViewerPanel-BZA00vSx.js → MoleculeViewerPanel-DS2USK0p.js} +2 -2
- package/dist/web/assets/{MoleculeViewerPanel-BZA00vSx.js.map → MoleculeViewerPanel-DS2USK0p.js.map} +1 -1
- package/dist/web/assets/{TerminalPanel-qx_HdM7y.js → TerminalPanel-qCs6c5HO.js} +2 -2
- package/dist/web/assets/{TerminalPanel-qx_HdM7y.js.map → TerminalPanel-qCs6c5HO.js.map} +1 -1
- package/dist/web/assets/{TextViewer-CDXNfUrU.js → TextViewer-DwAXmSeZ.js} +2 -2
- package/dist/web/assets/{TextViewer-CDXNfUrU.js.map → TextViewer-DwAXmSeZ.js.map} +1 -1
- package/dist/web/assets/{VideoViewer-D4XJC0lT.js → VideoViewer-CIgPlKOT.js} +2 -2
- package/dist/web/assets/{VideoViewer-D4XJC0lT.js.map → VideoViewer-CIgPlKOT.js.map} +1 -1
- package/dist/web/assets/index-CL9Q4aRP.js +3 -0
- package/dist/web/assets/index-CL9Q4aRP.js.map +1 -0
- package/dist/web/assets/markdown-CbV5mRfd.js +2 -0
- package/dist/web/assets/markdown-CbV5mRfd.js.map +1 -0
- package/dist/web/assets/markdown-JJHL50c6.css +1 -0
- package/dist/web/assets/paths--dEDMUm1.js +2 -0
- package/dist/web/assets/paths--dEDMUm1.js.map +1 -0
- package/dist/web/assets/{use-file-download-DsIqcxhB.js → use-file-download-DqpI1O5G.js} +2 -2
- package/dist/web/assets/{use-file-download-DsIqcxhB.js.map → use-file-download-DqpI1O5G.js.map} +1 -1
- package/dist/web/assets/{use-file-watch-ld_2lbTl.js → use-file-watch-ChUc4oqE.js} +2 -2
- package/dist/web/assets/{use-file-watch-ld_2lbTl.js.map → use-file-watch-ChUc4oqE.js.map} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +3 -3
- package/dist/web/assets/ChatPanel-YUfYdTdZ.js +0 -3
- package/dist/web/assets/ChatPanel-YUfYdTdZ.js.map +0 -1
- package/dist/web/assets/FilePanel-DCZ6Vntr.js +0 -2
- package/dist/web/assets/FilePanel-DCZ6Vntr.js.map +0 -1
- package/dist/web/assets/MarkdownFileViewer-BDmo3la-.js +0 -2
- package/dist/web/assets/index-DneBC_ta.js +0 -3
- package/dist/web/assets/index-DneBC_ta.js.map +0 -1
- package/dist/web/assets/markdown-BQwXm0MF.css +0 -1
- package/dist/web/assets/markdown-BVrzq29V.js +0 -2
- package/dist/web/assets/markdown-BVrzq29V.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TerminalPanel-qx_HdM7y.js","sources":["../../../../client/src/terminal-stream-router.ts","../../../src/features/terminal/TerminalPanel.tsx"],"sourcesContent":["import { encodeTerminalFrame, type TerminalFrame } from \"@av-pi-studio/protocol\";\n\nimport type { DaemonClient } from \"./daemon-client.js\";\n\n/**\n * Client-side demux of binary terminal frames to per-slot subscribers, plus outbound input/resize\n * encoding (architecture/client-app-runtime.md § Router; features/terminals.md § Binary stream\n * protocol).\n *\n * Inbound `Output`/`Snapshot`/`Restore` frames are dispatched to the subscriber registered for that\n * `slot`. Outbound `Input`/`Resize` are encoded with the right opcode + slot and sent on the data\n * path.\n */\n\nexport interface TerminalSlotSubscriber {\n /** Live terminal output bytes. */\n onOutput?: (data: Uint8Array) => void;\n /** Full-screen snapshot bytes (sent on (re)subscribe). */\n onSnapshot?: (data: Uint8Array) => void;\n /** Restore snapshot bytes (reflowable/mode-gated). */\n onRestore?: (data: Uint8Array) => void;\n}\n\nexport class TerminalStreamRouter {\n private readonly subscribers = new Map<number, TerminalSlotSubscriber>();\n private detach: (() => void) | null = null;\n\n constructor(private readonly daemon: DaemonClient) {}\n\n /** Begin routing inbound terminal frames. Idempotent. */\n start(): void {\n if (this.detach) return;\n this.detach = this.daemon.onTerminalFrame((frame) => this.dispatch(frame));\n }\n\n /** Stop routing inbound frames (subscribers retained). */\n stop(): void {\n this.detach?.();\n this.detach = null;\n }\n\n /** Register (or replace) the subscriber for a slot. Returns an unsubscribe fn. */\n subscribeSlot(slot: number, subscriber: TerminalSlotSubscriber): () => void {\n this.subscribers.set(slot, subscriber);\n return () => {\n if (this.subscribers.get(slot) === subscriber) this.subscribers.delete(slot);\n };\n }\n\n /** True iff a subscriber is registered for the slot. */\n hasSlot(slot: number): boolean {\n return this.subscribers.has(slot);\n }\n\n // ─── Outbound ─────────────────────────────────────────────────────────────\n\n /** Send raw input bytes to a slot's PTY (opcode `Input = 0x02`). */\n sendInput(slot: number, data: Uint8Array): void {\n this.daemon.sendBinary(encodeTerminalFrame({ opcode: \"Input\", slot, data }));\n }\n\n /** Send a resize (opcode `Resize = 0x03`, JSON `{ rows, cols }` payload). */\n sendResize(slot: number, rows: number, cols: number): void {\n this.daemon.sendBinary(encodeTerminalFrame({ opcode: \"Resize\", slot, rows, cols }));\n }\n\n // ─── Inbound dispatch ───────────────────────────────────────────────────────\n\n private dispatch(frame: TerminalFrame): void {\n const subscriber = this.subscribers.get(frame.slot);\n if (!subscriber) return; // no subscriber for this slot — drop\n switch (frame.opcode) {\n case \"Output\":\n subscriber.onOutput?.(frame.data);\n return;\n case \"Snapshot\":\n subscriber.onSnapshot?.(frame.data);\n return;\n case \"Restore\":\n subscriber.onRestore?.(frame.data);\n return;\n default:\n // Input/Resize are outbound-only; ignore if echoed back.\n return;\n }\n }\n}\n","/**\n * TerminalPanel — @xterm/xterm mount + binary-frame streaming via `TerminalStreamRouter`\n * (POC `initTerminalPanel`, POC_TO_APP_PLAN_UI.md §4.6). Strict upgrade over the POC's 800ms\n * `capture_terminal_request` poll: the daemon pushes `Output`/`Snapshot`/`Restore` binary frames\n * directly over the one shared `DaemonClient` connection, demuxed by slot.\n *\n * Slot lifecycle: created once via `create_terminal_request`, then persisted onto the tab's\n * `TerminalTabData.slot` via `useTabStore.getState().updateData` so switching away and back to\n * this tab (kept mounted-but-hidden by `TabPanelHost`) never recreates the terminal. `TabPanelHost`\n * only unmounts a tab's panel when the tab leaves the store's `tabs[]` (i.e. real tab close, never\n * a tab switch) — this component's true-unmount effect below relies on exactly that invariant to\n * send `kill_terminal_request`, terminating the PTY server-side instead of leaking it forever.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Terminal } from \"@xterm/xterm\";\nimport { FitAddon } from \"@xterm/addon-fit\";\nimport \"@xterm/xterm/css/xterm.css\";\nimport type { DaemonClient } from \"@av-pi-studio/client\";\nimport { TerminalStreamRouter } from \"@av-pi-studio/client\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useTabStore } from \"@pi-studio-ui/stores/tab-store.js\";\nimport type { Tab, TerminalTabData } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport styles from \"./TerminalPanel.module.css\";\n\nexport interface TerminalPanelProps {\n tab: Tab;\n}\n\ninterface CreateTerminalResponse {\n terminal: { slot: number };\n}\n\n// One TerminalStreamRouter per daemon connection — multiple terminal tabs share it rather than\n// each opening its own frame demuxer over the same socket.\nconst routerByDaemon = new WeakMap<DaemonClient, TerminalStreamRouter>();\n\nfunction routerFor(daemon: DaemonClient): TerminalStreamRouter {\n let router = routerByDaemon.get(daemon);\n if (!router) {\n router = new TerminalStreamRouter(daemon);\n router.start();\n routerByDaemon.set(daemon, router);\n }\n return router;\n}\n\n/** Dark palette matching the app's github-dark-ish default theme (theme/variants.ts \"dark\"). */\nconst TERMINAL_THEME = {\n background: \"#181b1a\",\n foreground: \"#fafafa\",\n cursor: \"#a2b4d7\",\n cursorAccent: \"#181b1a\",\n selectionBackground: \"rgba(255,255,255,0.18)\",\n black: \"#18181b\",\n red: \"#ef4444\",\n green: \"#22c55e\",\n yellow: \"#f59e0b\",\n blue: \"#3b82f6\",\n magenta: \"#a855f7\",\n cyan: \"#14b8a6\",\n white: \"#d4d4d8\",\n brightBlack: \"#52525b\",\n brightRed: \"#f87171\",\n brightGreen: \"#4ade80\",\n brightYellow: \"#fbbf24\",\n brightBlue: \"#60a5fa\",\n brightMagenta: \"#c084fc\",\n brightCyan: \"#2dd4bf\",\n brightWhite: \"#fafafa\",\n};\n\nexport function TerminalPanel({ tab }: TerminalPanelProps) {\n const data = tab.data as TerminalTabData;\n const client = useConnectionStore((s) => s.client);\n const activeTabId = useTabStore((s) => s.activeTabId);\n const isActive = activeTabId === tab.id;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const terminalRef = useRef<Terminal | null>(null);\n const fitAddonRef = useRef<FitAddon | null>(null);\n const slotRef = useRef<number | null>(data.slot);\n // Mirrors `use-checkout-status.ts`'s convention: kept in sync every render so the unmount-only\n // kill effect below always sends the CURRENT client, never a stale mount-time closure (e.g.\n // after a reconnect swaps in a new `PiStudioClient` instance).\n const clientRef = useRef(client);\n clientRef.current = client;\n\n const [slot, setSlot] = useState<number | null>(data.slot);\n const [error, setError] = useState<string | null>(null);\n\n // ─── Slot lifecycle: create once, persist onto the tab so re-opening reuses it ─────────────\n // React StrictMode double-invokes effects in dev: mount → cleanup → remount, synchronously,\n // on the SAME component instance (refs/state persist across all three phases — this is not\n // three separate mounts). Two things must hold across that: (1) the request fires exactly\n // once, and (2) whether to APPLY the eventual response is decided by whether the component is\n // mounted at RESPONSE time, not by a flag captured at REQUEST time.\n //\n // `requestStartedRef` gives (1): set the instant the request fires and never reset, so the\n // phantom-mount's cleanup-then-remount sees it's already in flight and never fires a second\n // `create_terminal_request` (this is what previously spawned two real PTYs from one Ctrl+T).\n //\n // `isMountedRef` gives (2): flipped true at the START of every effect invocation and false in\n // every cleanup, so it always reflects the LATEST phase. StrictMode's remount happens\n // synchronously, before the request's promise can possibly settle, so by response time\n // `isMountedRef.current` is back to `true` for a StrictMode phantom (correctly applies the\n // slot) — but stays `false` for a genuine fast real close (correctly kills the orphaned PTY\n // instead of leaking it or, as the previous buggy version did, killing a terminal that was\n // never actually torn down).\n const isMountedRef = useRef(false);\n const requestStartedRef = useRef(false);\n useEffect(() => {\n isMountedRef.current = true;\n if (!client || slotRef.current !== null || requestStartedRef.current) {\n return () => {\n isMountedRef.current = false;\n };\n }\n requestStartedRef.current = true;\n\n const cwd = data.cwd || \"~\";\n\n void client.connection\n .request<CreateTerminalResponse>(\"create_terminal_request\", { workspaceId: \"\", cwd })\n .then((res) => {\n const created = res.terminal.slot;\n if (!isMountedRef.current) {\n // A real close happened with no remount after it — kill the PTY that finished\n // spawning after the tab was already gone, instead of leaking it.\n void client.connection.request(\"kill_terminal_request\", { slot: created }).catch(() => {});\n return;\n }\n slotRef.current = created;\n setSlot(created);\n useTabStore.getState().updateData(tab.id, { slot: created });\n })\n .catch((err: unknown) => {\n if (!isMountedRef.current) return;\n setError(err instanceof Error ? err.message : String(err));\n })\n .finally(() => {\n // Reset only after the promise settles — by then StrictMode's synchronous\n // mount→cleanup→remount window has long passed, so this can never reopen the\n // double-fire race. It DOES allow a legitimate retry (e.g. `client` changed because of\n // a reconnect after the first attempt failed) instead of leaving the tab stuck forever.\n requestStartedRef.current = false;\n });\n\n return () => {\n isMountedRef.current = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, tab.id]);\n\n // ─── True unmount only: kill the PTY server-side ───────────────────────────────────────────\n // Runs its cleanup exactly once, when this component itself unmounts (real tab close, per\n // `TabPanelHost`'s \"hidden but alive\" model — a tab switch never unmounts). An empty deps array\n // means React never re-runs the effect body itself; only the cleanup fires, and only on\n // unmount, so this never races the slot-creation effect above or double-kills on a client\n // change. Without this, every closed terminal tab leaked its PTY process forever.\n //\n // For a REATTACH (this tab opened with a non-null `data.slot` from the very first render —\n // `use-terminal-restore.ts`, or the create-effect above once it has resolved) `slotRef.current`\n // is non-null from the start, so StrictMode's synchronous mount→cleanup→remount phantom cycle\n // would fire this cleanup and kill the PTY immediately on mount — unlike a freshly created\n // terminal, where `slotRef.current` is still `null` during that same phantom window (see the\n // create-effect's own comment) and so never hits this path. Deferred via `setTimeout`, exactly\n // like the create-effect's response handler: by the time it fires, StrictMode's remount has\n // already flipped `isMountedRef` back to `true` if this was a phantom unmount, so the kill is\n // skipped; a genuine close never remounts, so `isMountedRef` stays `false` and the kill proceeds.\n useEffect(() => {\n return () => {\n const currentSlot = slotRef.current;\n if (currentSlot === null) return;\n setTimeout(() => {\n if (isMountedRef.current) return; // StrictMode remounted synchronously — not a real close\n void clientRef.current?.connection\n .request(\"kill_terminal_request\", { slot: currentSlot })\n .catch(() => {});\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── xterm mount (once slot + container are ready) ─────────────────────────────────────────\n useEffect(() => {\n if (!client || slot === null || !containerRef.current) return;\n\n const terminal = new Terminal({\n cursorBlink: true,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: 13,\n scrollback: 5000,\n theme: TERMINAL_THEME,\n allowProposedApi: true,\n });\n const fitAddon = new FitAddon();\n terminal.loadAddon(fitAddon);\n terminal.open(containerRef.current);\n fitAddon.fit();\n\n terminalRef.current = terminal;\n fitAddonRef.current = fitAddon;\n\n const router = routerFor(client.connection);\n const unsubscribeSlot = router.subscribeSlot(slot, {\n onOutput: (chunk) => terminal.write(chunk),\n onSnapshot: (chunk) => {\n terminal.clear();\n terminal.write(chunk);\n },\n onRestore: (chunk) => terminal.write(chunk),\n });\n\n void client.connection.request(\"subscribe_terminal_request\", { slot }).catch((err: unknown) => {\n setError(err instanceof Error ? err.message : String(err));\n });\n\n const dataDisposable = terminal.onData((chunk) => {\n router.sendInput(slot, new TextEncoder().encode(chunk));\n });\n const resizeDisposable = terminal.onResize(({ cols, rows }) => {\n router.sendResize(slot, rows, cols);\n });\n\n const resizeObserver = new ResizeObserver(() => {\n fitAddon.fit();\n });\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n dataDisposable.dispose();\n resizeDisposable.dispose();\n unsubscribeSlot();\n void client.connection.request(\"unsubscribe_terminal_request\", { slot }).catch(() => {});\n terminal.dispose();\n terminalRef.current = null;\n fitAddonRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, slot]);\n\n // ─── Re-fit whenever this tab becomes the active (visible) one ────────────────────────────\n useEffect(() => {\n if (!isActive) return;\n fitAddonRef.current?.fit();\n }, [isActive]);\n\n if (error) {\n return (\n <div className={styles.wrap}>\n <div className={styles.status}>Terminal error: {error}</div>\n </div>\n );\n }\n\n if (slot === null) {\n return (\n <div className={styles.wrap}>\n <div className={styles.status}>\n <Spinner size=\"sm\" /> Starting terminal…\n </div>\n </div>\n );\n }\n\n return (\n <div className={styles.wrap}>\n <div ref={containerRef} className={styles.terminal} />\n </div>\n );\n}\n"],"names":["TerminalStreamRouter","daemon","__publicField","frame","_a","slot","subscriber","data","encodeTerminalFrame","rows","cols","_b","_c","routerByDaemon","routerFor","router","TERMINAL_THEME","TerminalPanel","tab","client","useConnectionStore","s","isActive","useTabStore","containerRef","useRef","terminalRef","fitAddonRef","slotRef","clientRef","setSlot","useState","error","setError","isMountedRef","requestStartedRef","useEffect","cwd","res","created","err","currentSlot","terminal","Terminal","fitAddon","FitAddon","unsubscribeSlot","chunk","dataDisposable","resizeDisposable","resizeObserver","jsx","styles","jsxs","Spinner"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TerminalPanel-qCs6c5HO.js","sources":["../../../../client/src/terminal-stream-router.ts","../../../src/features/terminal/TerminalPanel.tsx"],"sourcesContent":["import { encodeTerminalFrame, type TerminalFrame } from \"@av-pi-studio/protocol\";\n\nimport type { DaemonClient } from \"./daemon-client.js\";\n\n/**\n * Client-side demux of binary terminal frames to per-slot subscribers, plus outbound input/resize\n * encoding (architecture/client-app-runtime.md § Router; features/terminals.md § Binary stream\n * protocol).\n *\n * Inbound `Output`/`Snapshot`/`Restore` frames are dispatched to the subscriber registered for that\n * `slot`. Outbound `Input`/`Resize` are encoded with the right opcode + slot and sent on the data\n * path.\n */\n\nexport interface TerminalSlotSubscriber {\n /** Live terminal output bytes. */\n onOutput?: (data: Uint8Array) => void;\n /** Full-screen snapshot bytes (sent on (re)subscribe). */\n onSnapshot?: (data: Uint8Array) => void;\n /** Restore snapshot bytes (reflowable/mode-gated). */\n onRestore?: (data: Uint8Array) => void;\n}\n\nexport class TerminalStreamRouter {\n private readonly subscribers = new Map<number, TerminalSlotSubscriber>();\n private detach: (() => void) | null = null;\n\n constructor(private readonly daemon: DaemonClient) {}\n\n /** Begin routing inbound terminal frames. Idempotent. */\n start(): void {\n if (this.detach) return;\n this.detach = this.daemon.onTerminalFrame((frame) => this.dispatch(frame));\n }\n\n /** Stop routing inbound frames (subscribers retained). */\n stop(): void {\n this.detach?.();\n this.detach = null;\n }\n\n /** Register (or replace) the subscriber for a slot. Returns an unsubscribe fn. */\n subscribeSlot(slot: number, subscriber: TerminalSlotSubscriber): () => void {\n this.subscribers.set(slot, subscriber);\n return () => {\n if (this.subscribers.get(slot) === subscriber) this.subscribers.delete(slot);\n };\n }\n\n /** True iff a subscriber is registered for the slot. */\n hasSlot(slot: number): boolean {\n return this.subscribers.has(slot);\n }\n\n // ─── Outbound ─────────────────────────────────────────────────────────────\n\n /** Send raw input bytes to a slot's PTY (opcode `Input = 0x02`). */\n sendInput(slot: number, data: Uint8Array): void {\n this.daemon.sendBinary(encodeTerminalFrame({ opcode: \"Input\", slot, data }));\n }\n\n /** Send a resize (opcode `Resize = 0x03`, JSON `{ rows, cols }` payload). */\n sendResize(slot: number, rows: number, cols: number): void {\n this.daemon.sendBinary(encodeTerminalFrame({ opcode: \"Resize\", slot, rows, cols }));\n }\n\n // ─── Inbound dispatch ───────────────────────────────────────────────────────\n\n private dispatch(frame: TerminalFrame): void {\n const subscriber = this.subscribers.get(frame.slot);\n if (!subscriber) return; // no subscriber for this slot — drop\n switch (frame.opcode) {\n case \"Output\":\n subscriber.onOutput?.(frame.data);\n return;\n case \"Snapshot\":\n subscriber.onSnapshot?.(frame.data);\n return;\n case \"Restore\":\n subscriber.onRestore?.(frame.data);\n return;\n default:\n // Input/Resize are outbound-only; ignore if echoed back.\n return;\n }\n }\n}\n","/**\n * TerminalPanel — @xterm/xterm mount + binary-frame streaming via `TerminalStreamRouter`\n * (POC `initTerminalPanel`, POC_TO_APP_PLAN_UI.md §4.6). Strict upgrade over the POC's 800ms\n * `capture_terminal_request` poll: the daemon pushes `Output`/`Snapshot`/`Restore` binary frames\n * directly over the one shared `DaemonClient` connection, demuxed by slot.\n *\n * Slot lifecycle: created once via `create_terminal_request`, then persisted onto the tab's\n * `TerminalTabData.slot` via `useTabStore.getState().updateData` so switching away and back to\n * this tab (kept mounted-but-hidden by `TabPanelHost`) never recreates the terminal. `TabPanelHost`\n * only unmounts a tab's panel when the tab leaves the store's `tabs[]` (i.e. real tab close, never\n * a tab switch) — this component's true-unmount effect below relies on exactly that invariant to\n * send `kill_terminal_request`, terminating the PTY server-side instead of leaking it forever.\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Terminal } from \"@xterm/xterm\";\nimport { FitAddon } from \"@xterm/addon-fit\";\nimport \"@xterm/xterm/css/xterm.css\";\nimport type { DaemonClient } from \"@av-pi-studio/client\";\nimport { TerminalStreamRouter } from \"@av-pi-studio/client\";\nimport { useConnectionStore } from \"@pi-studio-ui/lib/connection/connection-store.js\";\nimport { useTabStore } from \"@pi-studio-ui/stores/tab-store.js\";\nimport type { Tab, TerminalTabData } from \"@pi-studio-ui/stores/tab-store.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport styles from \"./TerminalPanel.module.css\";\n\nexport interface TerminalPanelProps {\n tab: Tab;\n}\n\ninterface CreateTerminalResponse {\n terminal: { slot: number };\n}\n\n// One TerminalStreamRouter per daemon connection — multiple terminal tabs share it rather than\n// each opening its own frame demuxer over the same socket.\nconst routerByDaemon = new WeakMap<DaemonClient, TerminalStreamRouter>();\n\nfunction routerFor(daemon: DaemonClient): TerminalStreamRouter {\n let router = routerByDaemon.get(daemon);\n if (!router) {\n router = new TerminalStreamRouter(daemon);\n router.start();\n routerByDaemon.set(daemon, router);\n }\n return router;\n}\n\n/** Dark palette matching the app's github-dark-ish default theme (theme/variants.ts \"dark\"). */\nconst TERMINAL_THEME = {\n background: \"#181b1a\",\n foreground: \"#fafafa\",\n cursor: \"#a2b4d7\",\n cursorAccent: \"#181b1a\",\n selectionBackground: \"rgba(255,255,255,0.18)\",\n black: \"#18181b\",\n red: \"#ef4444\",\n green: \"#22c55e\",\n yellow: \"#f59e0b\",\n blue: \"#3b82f6\",\n magenta: \"#a855f7\",\n cyan: \"#14b8a6\",\n white: \"#d4d4d8\",\n brightBlack: \"#52525b\",\n brightRed: \"#f87171\",\n brightGreen: \"#4ade80\",\n brightYellow: \"#fbbf24\",\n brightBlue: \"#60a5fa\",\n brightMagenta: \"#c084fc\",\n brightCyan: \"#2dd4bf\",\n brightWhite: \"#fafafa\",\n};\n\nexport function TerminalPanel({ tab }: TerminalPanelProps) {\n const data = tab.data as TerminalTabData;\n const client = useConnectionStore((s) => s.client);\n const activeTabId = useTabStore((s) => s.activeTabId);\n const isActive = activeTabId === tab.id;\n\n const containerRef = useRef<HTMLDivElement | null>(null);\n const terminalRef = useRef<Terminal | null>(null);\n const fitAddonRef = useRef<FitAddon | null>(null);\n const slotRef = useRef<number | null>(data.slot);\n // Mirrors `use-checkout-status.ts`'s convention: kept in sync every render so the unmount-only\n // kill effect below always sends the CURRENT client, never a stale mount-time closure (e.g.\n // after a reconnect swaps in a new `PiStudioClient` instance).\n const clientRef = useRef(client);\n clientRef.current = client;\n\n const [slot, setSlot] = useState<number | null>(data.slot);\n const [error, setError] = useState<string | null>(null);\n\n // ─── Slot lifecycle: create once, persist onto the tab so re-opening reuses it ─────────────\n // React StrictMode double-invokes effects in dev: mount → cleanup → remount, synchronously,\n // on the SAME component instance (refs/state persist across all three phases — this is not\n // three separate mounts). Two things must hold across that: (1) the request fires exactly\n // once, and (2) whether to APPLY the eventual response is decided by whether the component is\n // mounted at RESPONSE time, not by a flag captured at REQUEST time.\n //\n // `requestStartedRef` gives (1): set the instant the request fires and never reset, so the\n // phantom-mount's cleanup-then-remount sees it's already in flight and never fires a second\n // `create_terminal_request` (this is what previously spawned two real PTYs from one Ctrl+T).\n //\n // `isMountedRef` gives (2): flipped true at the START of every effect invocation and false in\n // every cleanup, so it always reflects the LATEST phase. StrictMode's remount happens\n // synchronously, before the request's promise can possibly settle, so by response time\n // `isMountedRef.current` is back to `true` for a StrictMode phantom (correctly applies the\n // slot) — but stays `false` for a genuine fast real close (correctly kills the orphaned PTY\n // instead of leaking it or, as the previous buggy version did, killing a terminal that was\n // never actually torn down).\n const isMountedRef = useRef(false);\n const requestStartedRef = useRef(false);\n useEffect(() => {\n isMountedRef.current = true;\n if (!client || slotRef.current !== null || requestStartedRef.current) {\n return () => {\n isMountedRef.current = false;\n };\n }\n requestStartedRef.current = true;\n\n const cwd = data.cwd || \"~\";\n\n void client.connection\n .request<CreateTerminalResponse>(\"create_terminal_request\", { workspaceId: \"\", cwd })\n .then((res) => {\n const created = res.terminal.slot;\n if (!isMountedRef.current) {\n // A real close happened with no remount after it — kill the PTY that finished\n // spawning after the tab was already gone, instead of leaking it.\n void client.connection.request(\"kill_terminal_request\", { slot: created }).catch(() => {});\n return;\n }\n slotRef.current = created;\n setSlot(created);\n useTabStore.getState().updateData(tab.id, { slot: created });\n })\n .catch((err: unknown) => {\n if (!isMountedRef.current) return;\n setError(err instanceof Error ? err.message : String(err));\n })\n .finally(() => {\n // Reset only after the promise settles — by then StrictMode's synchronous\n // mount→cleanup→remount window has long passed, so this can never reopen the\n // double-fire race. It DOES allow a legitimate retry (e.g. `client` changed because of\n // a reconnect after the first attempt failed) instead of leaving the tab stuck forever.\n requestStartedRef.current = false;\n });\n\n return () => {\n isMountedRef.current = false;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, tab.id]);\n\n // ─── True unmount only: kill the PTY server-side ───────────────────────────────────────────\n // Runs its cleanup exactly once, when this component itself unmounts (real tab close, per\n // `TabPanelHost`'s \"hidden but alive\" model — a tab switch never unmounts). An empty deps array\n // means React never re-runs the effect body itself; only the cleanup fires, and only on\n // unmount, so this never races the slot-creation effect above or double-kills on a client\n // change. Without this, every closed terminal tab leaked its PTY process forever.\n //\n // For a REATTACH (this tab opened with a non-null `data.slot` from the very first render —\n // `use-terminal-restore.ts`, or the create-effect above once it has resolved) `slotRef.current`\n // is non-null from the start, so StrictMode's synchronous mount→cleanup→remount phantom cycle\n // would fire this cleanup and kill the PTY immediately on mount — unlike a freshly created\n // terminal, where `slotRef.current` is still `null` during that same phantom window (see the\n // create-effect's own comment) and so never hits this path. Deferred via `setTimeout`, exactly\n // like the create-effect's response handler: by the time it fires, StrictMode's remount has\n // already flipped `isMountedRef` back to `true` if this was a phantom unmount, so the kill is\n // skipped; a genuine close never remounts, so `isMountedRef` stays `false` and the kill proceeds.\n useEffect(() => {\n return () => {\n const currentSlot = slotRef.current;\n if (currentSlot === null) return;\n setTimeout(() => {\n if (isMountedRef.current) return; // StrictMode remounted synchronously — not a real close\n void clientRef.current?.connection\n .request(\"kill_terminal_request\", { slot: currentSlot })\n .catch(() => {});\n }, 0);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // ─── xterm mount (once slot + container are ready) ─────────────────────────────────────────\n useEffect(() => {\n if (!client || slot === null || !containerRef.current) return;\n\n const terminal = new Terminal({\n cursorBlink: true,\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: 13,\n scrollback: 5000,\n theme: TERMINAL_THEME,\n allowProposedApi: true,\n });\n const fitAddon = new FitAddon();\n terminal.loadAddon(fitAddon);\n terminal.open(containerRef.current);\n fitAddon.fit();\n\n terminalRef.current = terminal;\n fitAddonRef.current = fitAddon;\n\n const router = routerFor(client.connection);\n const unsubscribeSlot = router.subscribeSlot(slot, {\n onOutput: (chunk) => terminal.write(chunk),\n onSnapshot: (chunk) => {\n terminal.clear();\n terminal.write(chunk);\n },\n onRestore: (chunk) => terminal.write(chunk),\n });\n\n void client.connection.request(\"subscribe_terminal_request\", { slot }).catch((err: unknown) => {\n setError(err instanceof Error ? err.message : String(err));\n });\n\n const dataDisposable = terminal.onData((chunk) => {\n router.sendInput(slot, new TextEncoder().encode(chunk));\n });\n const resizeDisposable = terminal.onResize(({ cols, rows }) => {\n router.sendResize(slot, rows, cols);\n });\n\n const resizeObserver = new ResizeObserver(() => {\n fitAddon.fit();\n });\n resizeObserver.observe(containerRef.current);\n\n return () => {\n resizeObserver.disconnect();\n dataDisposable.dispose();\n resizeDisposable.dispose();\n unsubscribeSlot();\n void client.connection.request(\"unsubscribe_terminal_request\", { slot }).catch(() => {});\n terminal.dispose();\n terminalRef.current = null;\n fitAddonRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, slot]);\n\n // ─── Re-fit whenever this tab becomes the active (visible) one ────────────────────────────\n useEffect(() => {\n if (!isActive) return;\n fitAddonRef.current?.fit();\n }, [isActive]);\n\n if (error) {\n return (\n <div className={styles.wrap}>\n <div className={styles.status}>Terminal error: {error}</div>\n </div>\n );\n }\n\n if (slot === null) {\n return (\n <div className={styles.wrap}>\n <div className={styles.status}>\n <Spinner size=\"sm\" /> Starting terminal…\n </div>\n </div>\n );\n }\n\n return (\n <div className={styles.wrap}>\n <div ref={containerRef} className={styles.terminal} />\n </div>\n );\n}\n"],"names":["TerminalStreamRouter","daemon","__publicField","frame","_a","slot","subscriber","data","encodeTerminalFrame","rows","cols","_b","_c","routerByDaemon","routerFor","router","TERMINAL_THEME","TerminalPanel","tab","client","useConnectionStore","s","isActive","useTabStore","containerRef","useRef","terminalRef","fitAddonRef","slotRef","clientRef","setSlot","useState","error","setError","isMountedRef","requestStartedRef","useEffect","cwd","res","created","err","currentSlot","terminal","Terminal","fitAddon","FitAddon","unsubscribeSlot","chunk","dataDisposable","resizeDisposable","resizeObserver","jsx","styles","jsxs","Spinner"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as N,j as r}from"./vendor-react-p-LLqNcx.js";import{r as _,S as u,B as j}from"./index-
|
|
2
|
-
//# sourceMappingURL=TextViewer-
|
|
1
|
+
import{r as N,j as r}from"./vendor-react-p-LLqNcx.js";import{r as _,S as u,B as j}from"./index-CL9Q4aRP.js";import{u as b,F as k,C as y}from"./CodeView-CKUKYQ4A.js";import{u as z}from"./vendor-query-chxBrUuj.js";import{u as E}from"./use-file-download-DqpI1O5G.js";import{p as d}from"./FilePanel.module-DoDNR5C2.js";import"./vendor-B2Vnrid8.js";import"./vendor-markdown-ePgMI45n.js";import"./vendor-highlight-Cry4x-mr.js";import"./vendor-icons-CAhpOZSZ.js";import"./vendor-overlays-DWi-xJna.js";import"./vendor-dnd-CjSU1Kqk.js";function h(s){const{enabled:n,download:e,decode:o}=s;return{isLoading:n&&(e.isLoading||e.hasObjectUrl&&o.isLoading),isError:e.isError||o.isError,error:e.isError?e.error:o.error,data:o.data}}function v(s,n=!0){var a;const e=E(s,n),o=(a=e.data)==null?void 0:a.objectUrl,t=z({queryKey:_.fileText(s,o??null),queryFn:async()=>({content:await(await fetch(o)).text()}),enabled:n&&!!o});return h({enabled:n,download:{isLoading:e.isLoading,isError:e.isError,error:e.error,hasObjectUrl:!!o},decode:{isLoading:t.isLoading,isError:t.isError,error:t.error,data:t.data}})}function U(s){const{maxDisplayBytes:n,inline:e,streamed:o,download:t}=s;if(e.isLoading)return{kind:"loading"};if(e.tooLarge){const{size:a}=e.tooLarge;return a>n?{kind:"too-large",size:a,maxDisplayBytes:n,downloading:t.requested&&t.isLoading,downloadUrl:t.objectUrl,downloadName:t.fileName}:o.isLoading?{kind:"streaming",size:a}:o.isError?{kind:"stream-error",size:a,message:o.errorMessage??"stream failed"}:o.content!==null?{kind:"streamed",size:a,content:o.content}:{kind:"loading"}}return e.isError?{kind:"error",message:e.errorMessage??"unknown error"}:e.content!==null?{kind:"inline",content:e.content}:{kind:"loading"}}const B="_wrap_150m3_1",F="_note_150m3_7",D="_body_150m3_15",M="_tooLarge_150m3_19",l={wrap:B,note:F,body:D,tooLarge:M},L=30*1024*1024;function c(s){return`${(s/(1024*1024)).toFixed(1)} MB`}function X({path:s}){var f,x,p,w;const n=b(s),e=n.error instanceof k?n.error:null,o=e!==null&&e.size<=L,t=v(s,o),[a,g]=N.useState(!1),m=E(s,a),i=U({maxDisplayBytes:L,inline:{isLoading:n.isLoading,isError:n.isError,tooLarge:e?{size:e.size}:null,errorMessage:n.error instanceof Error?n.error.message:null,content:((f=n.data)==null?void 0:f.content)??null},streamed:{isLoading:t.isLoading,isError:t.isError,errorMessage:t.error instanceof Error?t.error.message:null,content:((x=t.data)==null?void 0:x.content)??null},download:{requested:a,isLoading:m.isLoading,objectUrl:((p=m.data)==null?void 0:p.objectUrl)??null,fileName:((w=m.data)==null?void 0:w.fileName)??null}});switch(i.kind){case"loading":return r.jsxs("div",{className:d.emptyState,children:[r.jsx(u,{size:"sm"})," Loading..."]});case"inline":return r.jsx(y,{path:s,content:i.content});case"streaming":return r.jsxs("div",{className:d.emptyState,children:[r.jsx(u,{size:"sm"})," Streaming ",c(i.size)," file..."]});case"stream-error":return r.jsxs("div",{className:d.emptyState,children:["Error: ",i.message]});case"streamed":return r.jsxs("div",{className:l.wrap,children:[r.jsxs("div",{className:l.note,children:[c(i.size)," file streamed"]}),r.jsx("div",{className:l.body,children:r.jsx(y,{path:s,content:i.content})})]});case"too-large":{const S=s.split("/").pop()||s;return r.jsx("div",{className:d.emptyState,children:r.jsxs("div",{className:l.tooLarge,children:[r.jsxs("div",{children:[c(i.size)," — too large to display (display ceiling is"," ",c(i.maxDisplayBytes),")."]}),i.downloading?r.jsx(u,{size:"sm"}):i.downloadUrl?r.jsx("a",{href:i.downloadUrl,download:i.downloadName||S,children:r.jsx(j,{size:"sm",children:"Save file"})}):r.jsx(j,{size:"sm",onClick:()=>g(!0),children:"Download"})]})})}case"error":return r.jsxs("div",{className:d.emptyState,children:["Error: ",i.message]})}}export{L as MAX_DISPLAY_BYTES,X as TextViewer};
|
|
2
|
+
//# sourceMappingURL=TextViewer-DwAXmSeZ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextViewer-CDXNfUrU.js","sources":["../../../src/hooks/file-text-state.ts","../../../src/hooks/use-file-text.ts","../../../src/features/files/text-viewer-state.ts","../../../src/features/files/TextViewer.tsx"],"sourcesContent":["/**\n * Pure query-state merge for `use-file-text` (task-009), kept DOM/React-free so the\n * loading/error/data composition across the two dependent queries it wraps (download → decode)\n * is unit-testable directly, without `renderHook` (no jsdom test environment in this repo).\n */\n\nexport interface FileTextResult {\n content: string;\n}\n\nexport interface FileTextMergeInputs {\n enabled: boolean;\n download: { isLoading: boolean; isError: boolean; error: unknown; hasObjectUrl: boolean };\n decode: {\n isLoading: boolean;\n isError: boolean;\n error: unknown;\n data: FileTextResult | undefined;\n };\n}\n\nexport interface FileTextQueryState {\n isLoading: boolean;\n isError: boolean;\n error: unknown;\n data: FileTextResult | undefined;\n}\n\nexport function mergeFileTextState(input: FileTextMergeInputs): FileTextQueryState {\n const { enabled, download, decode } = input;\n return {\n isLoading: enabled && (download.isLoading || (download.hasObjectUrl && decode.isLoading)),\n isError: download.isError || decode.isError,\n error: download.isError ? download.error : decode.error,\n data: decode.data,\n };\n}\n","/**\n * `use-file-text` — tier-2 fallback for `TextViewer` (task-009): decodes a file's bytes to UTF-8\n * text via the already-uncapped chunked binary download path (`useFileDownload` →\n * `file_download_token_request`/`file_download_request`), for files over `file_read_request`'s\n * inline cap (`MAX_INLINE_FILE_READ_BYTES`, `packages/server/src/files/limits.ts`). Same\n * transport the molecule viewer uses for its own (binary-safe) source — no new RPC, no new\n * transport, just a text decode over the resulting blob.\n *\n * Note the daemon asymmetry documented on `useFileDownload`'s own RPCs: the download path is\n * registered only in the production bootstrap (`bootstrap.ts`), not `dev-bootstrap.ts`. Under\n * `npm run dev:daemon` a file above the inline cap fails this fetch rather than rendering — that\n * surfaces as the normal error state below, which is the intended behavior (see task-009 notes).\n *\n * The loading/error/data merge across the two dependent queries (download → decode) is the pure,\n * DOM-free `mergeFileTextState` (`file-text-state.ts`) — this hook is a thin wrapper feeding it\n * live query state.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { rpcKeys } from \"@pi-studio-ui/lib/connection/rpc-keys.js\";\nimport { useFileDownload } from \"./use-file-download.js\";\nimport { mergeFileTextState, type FileTextResult } from \"./file-text-state.js\";\n\nexport type { FileTextResult };\n\nexport function useFileText(path: string, enabled = true) {\n const download = useFileDownload(path, enabled);\n const objectUrl = download.data?.objectUrl;\n\n const decode = useQuery({\n queryKey: rpcKeys.fileText(path, objectUrl ?? null),\n queryFn: async (): Promise<FileTextResult> => {\n const content = await (await fetch(objectUrl as string)).text();\n return { content };\n },\n enabled: enabled && Boolean(objectUrl),\n });\n\n return mergeFileTextState({\n enabled,\n download: {\n isLoading: download.isLoading,\n isError: download.isError,\n error: download.error,\n hasObjectUrl: Boolean(objectUrl),\n },\n decode: {\n isLoading: decode.isLoading,\n isError: decode.isError,\n error: decode.error,\n data: decode.data,\n },\n });\n}\n","/**\n * Pure tier-selection logic for `TextViewer` (task-009), kept in its own DOM-free module so the\n * three-state branch (inline / streamed / download-only) is unit-testable without mounting\n * CodeMirror or React — mirrors `molecule-reload.ts`'s `shouldApplyRefresh` extraction for the\n * same reason: this repo has no jsdom test environment configured anywhere.\n */\n\nexport type TextViewerState =\n | { kind: \"loading\" }\n | { kind: \"inline\"; content: string }\n | { kind: \"streaming\"; size: number }\n | { kind: \"streamed\"; size: number; content: string }\n | { kind: \"stream-error\"; size: number; message: string }\n | {\n kind: \"too-large\";\n size: number;\n maxDisplayBytes: number;\n downloading: boolean;\n downloadUrl: string | null;\n downloadName: string | null;\n }\n | { kind: \"error\"; message: string };\n\nexport interface TextViewerInputs {\n maxDisplayBytes: number;\n inline: {\n isLoading: boolean;\n isError: boolean;\n /** `{ size }` when the failure was specifically `FileTooLargeError`; `null` for a generic\n * read failure (network/permission/etc). */\n tooLarge: { size: number } | null;\n errorMessage: string | null;\n content: string | null;\n };\n streamed: {\n isLoading: boolean;\n isError: boolean;\n errorMessage: string | null;\n content: string | null;\n };\n download: {\n requested: boolean;\n isLoading: boolean;\n objectUrl: string | null;\n fileName: string | null;\n };\n}\n\n/** Selects exactly one `TextViewerState` from the current query states. Pure — no hooks, no I/O. */\nexport function selectTextViewerState(input: TextViewerInputs): TextViewerState {\n const { maxDisplayBytes, inline, streamed, download } = input;\n\n if (inline.isLoading) return { kind: \"loading\" };\n\n if (inline.tooLarge) {\n const { size } = inline.tooLarge;\n if (size > maxDisplayBytes) {\n return {\n kind: \"too-large\",\n size,\n maxDisplayBytes,\n downloading: download.requested && download.isLoading,\n downloadUrl: download.objectUrl,\n downloadName: download.fileName,\n };\n }\n if (streamed.isLoading) return { kind: \"streaming\", size };\n if (streamed.isError) {\n return { kind: \"stream-error\", size, message: streamed.errorMessage ?? \"stream failed\" };\n }\n if (streamed.content !== null) return { kind: \"streamed\", size, content: streamed.content };\n return { kind: \"loading\" };\n }\n\n if (inline.isError) {\n return { kind: \"error\", message: inline.errorMessage ?? \"unknown error\" };\n }\n\n if (inline.content !== null) return { kind: \"inline\", content: inline.content };\n return { kind: \"loading\" };\n}\n","/**\n * TextViewer — the default viewer for source/text files: fetches the UTF-8 preview via\n * `useFileRead` and renders it through `CodeView` (line gutter + Shiki highlighting). Registered\n * in `viewer-registry.ts` as the fallback for any file not claimed by a more specific viewer.\n *\n * Three size tiers (task-009 — raising `file_read_request`'s old 512 KiB ceiling):\n * 1. `size <= MAX_INLINE_FILE_READ_BYTES` (server-side, 5 MiB) — the `useFileRead` JSON round\n * trip above, unchanged.\n * 2. `MAX_INLINE_FILE_READ_BYTES < size <= MAX_DISPLAY_BYTES` (30 MiB) — transparently refetch via\n * the uncapped chunked binary download path (`use-file-text.ts`) and render the same\n * `CodeView`, with a muted note that the file was streamed rather than read inline.\n * 3. `size > MAX_DISPLAY_BYTES` — a terminal state: no render attempt, just the size, why, and a\n * download action (`BinaryFallbackViewer`'s existing on-demand download pattern).\n *\n * Which tier applies is the pure, DOM-free `selectTextViewerState` (`text-viewer-state.ts`) — this\n * component is a thin switch-render over it.\n */\n\nimport { useState } from \"react\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport { useFileRead, FileTooLargeError } from \"@pi-studio-ui/hooks/use-file-read.js\";\nimport { useFileText } from \"@pi-studio-ui/hooks/use-file-text.js\";\nimport { useFileDownload } from \"@pi-studio-ui/hooks/use-file-download.js\";\nimport { CodeView } from \"./CodeView.js\";\nimport { selectTextViewerState } from \"./text-viewer-state.js\";\nimport type { ViewerProps } from \"./viewer-registry.js\";\nimport styles from \"./FilePanel.module.css\";\nimport textStyles from \"./TextViewer.module.css\";\n\n/** Display ceiling above the inline cap. `CodeView`'s CodeMirror instance enables\n * `EditorView.lineWrapping`, which makes it measure line heights across the whole document —\n * well past interactive above this size, so files this large get a terminal download-only state\n * instead of an attempted render. Deliberately not configurable (task-009 notes). */\nexport const MAX_DISPLAY_BYTES = 30 * 1024 * 1024;\n\nfunction formatMegabytes(bytes: number): string {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function TextViewer({ path }: ViewerProps) {\n const inline = useFileRead(path);\n const tooLarge = inline.error instanceof FileTooLargeError ? inline.error : null;\n const displayable = tooLarge !== null && tooLarge.size <= MAX_DISPLAY_BYTES;\n\n const streamed = useFileText(path, displayable);\n const [downloadRequested, setDownloadRequested] = useState(false);\n const download = useFileDownload(path, downloadRequested);\n\n const state = selectTextViewerState({\n maxDisplayBytes: MAX_DISPLAY_BYTES,\n inline: {\n isLoading: inline.isLoading,\n isError: inline.isError,\n tooLarge: tooLarge ? { size: tooLarge.size } : null,\n errorMessage: inline.error instanceof Error ? inline.error.message : null,\n content: inline.data?.content ?? null,\n },\n streamed: {\n isLoading: streamed.isLoading,\n isError: streamed.isError,\n errorMessage: streamed.error instanceof Error ? streamed.error.message : null,\n content: streamed.data?.content ?? null,\n },\n download: {\n requested: downloadRequested,\n isLoading: download.isLoading,\n objectUrl: download.data?.objectUrl ?? null,\n fileName: download.data?.fileName ?? null,\n },\n });\n\n switch (state.kind) {\n case \"loading\":\n return (\n <div className={styles.emptyState}>\n <Spinner size=\"sm\" /> Loading...\n </div>\n );\n case \"inline\":\n return <CodeView path={path} content={state.content} />;\n case \"streaming\":\n return (\n <div className={styles.emptyState}>\n <Spinner size=\"sm\" /> Streaming {formatMegabytes(state.size)} file...\n </div>\n );\n case \"stream-error\":\n return <div className={styles.emptyState}>Error: {state.message}</div>;\n case \"streamed\":\n return (\n <div className={textStyles.wrap}>\n <div className={textStyles.note}>{formatMegabytes(state.size)} file streamed</div>\n <div className={textStyles.body}>\n <CodeView path={path} content={state.content} />\n </div>\n </div>\n );\n case \"too-large\": {\n const name = path.split(\"/\").pop() || path;\n return (\n <div className={styles.emptyState}>\n <div className={textStyles.tooLarge}>\n <div>\n {formatMegabytes(state.size)} — too large to display (display ceiling is{\" \"}\n {formatMegabytes(state.maxDisplayBytes)}).\n </div>\n {state.downloading ? (\n <Spinner size=\"sm\" />\n ) : state.downloadUrl ? (\n <a href={state.downloadUrl} download={state.downloadName || name}>\n <Button size=\"sm\">Save file</Button>\n </a>\n ) : (\n <Button size=\"sm\" onClick={() => setDownloadRequested(true)}>\n Download\n </Button>\n )}\n </div>\n </div>\n );\n }\n case \"error\":\n return <div className={styles.emptyState}>Error: {state.message}</div>;\n }\n}\n"],"names":["mergeFileTextState","input","enabled","download","decode","useFileText","path","useFileDownload","objectUrl","_a","useQuery","rpcKeys","selectTextViewerState","maxDisplayBytes","inline","streamed","size","MAX_DISPLAY_BYTES","formatMegabytes","bytes","TextViewer","useFileRead","tooLarge","FileTooLargeError","displayable","downloadRequested","setDownloadRequested","useState","state","_b","_c","_d","jsxs","styles","jsx","Spinner","CodeView","textStyles","name","Button"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TextViewer-DwAXmSeZ.js","sources":["../../../src/hooks/file-text-state.ts","../../../src/hooks/use-file-text.ts","../../../src/features/files/text-viewer-state.ts","../../../src/features/files/TextViewer.tsx"],"sourcesContent":["/**\n * Pure query-state merge for `use-file-text` (task-009), kept DOM/React-free so the\n * loading/error/data composition across the two dependent queries it wraps (download → decode)\n * is unit-testable directly, without `renderHook` (no jsdom test environment in this repo).\n */\n\nexport interface FileTextResult {\n content: string;\n}\n\nexport interface FileTextMergeInputs {\n enabled: boolean;\n download: { isLoading: boolean; isError: boolean; error: unknown; hasObjectUrl: boolean };\n decode: {\n isLoading: boolean;\n isError: boolean;\n error: unknown;\n data: FileTextResult | undefined;\n };\n}\n\nexport interface FileTextQueryState {\n isLoading: boolean;\n isError: boolean;\n error: unknown;\n data: FileTextResult | undefined;\n}\n\nexport function mergeFileTextState(input: FileTextMergeInputs): FileTextQueryState {\n const { enabled, download, decode } = input;\n return {\n isLoading: enabled && (download.isLoading || (download.hasObjectUrl && decode.isLoading)),\n isError: download.isError || decode.isError,\n error: download.isError ? download.error : decode.error,\n data: decode.data,\n };\n}\n","/**\n * `use-file-text` — tier-2 fallback for `TextViewer` (task-009): decodes a file's bytes to UTF-8\n * text via the already-uncapped chunked binary download path (`useFileDownload` →\n * `file_download_token_request`/`file_download_request`), for files over `file_read_request`'s\n * inline cap (`MAX_INLINE_FILE_READ_BYTES`, `packages/server/src/files/limits.ts`). Same\n * transport the molecule viewer uses for its own (binary-safe) source — no new RPC, no new\n * transport, just a text decode over the resulting blob.\n *\n * Note the daemon asymmetry documented on `useFileDownload`'s own RPCs: the download path is\n * registered only in the production bootstrap (`bootstrap.ts`), not `dev-bootstrap.ts`. Under\n * `npm run dev:daemon` a file above the inline cap fails this fetch rather than rendering — that\n * surfaces as the normal error state below, which is the intended behavior (see task-009 notes).\n *\n * The loading/error/data merge across the two dependent queries (download → decode) is the pure,\n * DOM-free `mergeFileTextState` (`file-text-state.ts`) — this hook is a thin wrapper feeding it\n * live query state.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\nimport { rpcKeys } from \"@pi-studio-ui/lib/connection/rpc-keys.js\";\nimport { useFileDownload } from \"./use-file-download.js\";\nimport { mergeFileTextState, type FileTextResult } from \"./file-text-state.js\";\n\nexport type { FileTextResult };\n\nexport function useFileText(path: string, enabled = true) {\n const download = useFileDownload(path, enabled);\n const objectUrl = download.data?.objectUrl;\n\n const decode = useQuery({\n queryKey: rpcKeys.fileText(path, objectUrl ?? null),\n queryFn: async (): Promise<FileTextResult> => {\n const content = await (await fetch(objectUrl as string)).text();\n return { content };\n },\n enabled: enabled && Boolean(objectUrl),\n });\n\n return mergeFileTextState({\n enabled,\n download: {\n isLoading: download.isLoading,\n isError: download.isError,\n error: download.error,\n hasObjectUrl: Boolean(objectUrl),\n },\n decode: {\n isLoading: decode.isLoading,\n isError: decode.isError,\n error: decode.error,\n data: decode.data,\n },\n });\n}\n","/**\n * Pure tier-selection logic for `TextViewer` (task-009), kept in its own DOM-free module so the\n * three-state branch (inline / streamed / download-only) is unit-testable without mounting\n * CodeMirror or React — mirrors `molecule-reload.ts`'s `shouldApplyRefresh` extraction for the\n * same reason: this repo has no jsdom test environment configured anywhere.\n */\n\nexport type TextViewerState =\n | { kind: \"loading\" }\n | { kind: \"inline\"; content: string }\n | { kind: \"streaming\"; size: number }\n | { kind: \"streamed\"; size: number; content: string }\n | { kind: \"stream-error\"; size: number; message: string }\n | {\n kind: \"too-large\";\n size: number;\n maxDisplayBytes: number;\n downloading: boolean;\n downloadUrl: string | null;\n downloadName: string | null;\n }\n | { kind: \"error\"; message: string };\n\nexport interface TextViewerInputs {\n maxDisplayBytes: number;\n inline: {\n isLoading: boolean;\n isError: boolean;\n /** `{ size }` when the failure was specifically `FileTooLargeError`; `null` for a generic\n * read failure (network/permission/etc). */\n tooLarge: { size: number } | null;\n errorMessage: string | null;\n content: string | null;\n };\n streamed: {\n isLoading: boolean;\n isError: boolean;\n errorMessage: string | null;\n content: string | null;\n };\n download: {\n requested: boolean;\n isLoading: boolean;\n objectUrl: string | null;\n fileName: string | null;\n };\n}\n\n/** Selects exactly one `TextViewerState` from the current query states. Pure — no hooks, no I/O. */\nexport function selectTextViewerState(input: TextViewerInputs): TextViewerState {\n const { maxDisplayBytes, inline, streamed, download } = input;\n\n if (inline.isLoading) return { kind: \"loading\" };\n\n if (inline.tooLarge) {\n const { size } = inline.tooLarge;\n if (size > maxDisplayBytes) {\n return {\n kind: \"too-large\",\n size,\n maxDisplayBytes,\n downloading: download.requested && download.isLoading,\n downloadUrl: download.objectUrl,\n downloadName: download.fileName,\n };\n }\n if (streamed.isLoading) return { kind: \"streaming\", size };\n if (streamed.isError) {\n return { kind: \"stream-error\", size, message: streamed.errorMessage ?? \"stream failed\" };\n }\n if (streamed.content !== null) return { kind: \"streamed\", size, content: streamed.content };\n return { kind: \"loading\" };\n }\n\n if (inline.isError) {\n return { kind: \"error\", message: inline.errorMessage ?? \"unknown error\" };\n }\n\n if (inline.content !== null) return { kind: \"inline\", content: inline.content };\n return { kind: \"loading\" };\n}\n","/**\n * TextViewer — the default viewer for source/text files: fetches the UTF-8 preview via\n * `useFileRead` and renders it through `CodeView` (line gutter + Shiki highlighting). Registered\n * in `viewer-registry.ts` as the fallback for any file not claimed by a more specific viewer.\n *\n * Three size tiers (task-009 — raising `file_read_request`'s old 512 KiB ceiling):\n * 1. `size <= MAX_INLINE_FILE_READ_BYTES` (server-side, 5 MiB) — the `useFileRead` JSON round\n * trip above, unchanged.\n * 2. `MAX_INLINE_FILE_READ_BYTES < size <= MAX_DISPLAY_BYTES` (30 MiB) — transparently refetch via\n * the uncapped chunked binary download path (`use-file-text.ts`) and render the same\n * `CodeView`, with a muted note that the file was streamed rather than read inline.\n * 3. `size > MAX_DISPLAY_BYTES` — a terminal state: no render attempt, just the size, why, and a\n * download action (`BinaryFallbackViewer`'s existing on-demand download pattern).\n *\n * Which tier applies is the pure, DOM-free `selectTextViewerState` (`text-viewer-state.ts`) — this\n * component is a thin switch-render over it.\n */\n\nimport { useState } from \"react\";\nimport { Button } from \"@pi-studio-ui/components/primitives/Button.js\";\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport { useFileRead, FileTooLargeError } from \"@pi-studio-ui/hooks/use-file-read.js\";\nimport { useFileText } from \"@pi-studio-ui/hooks/use-file-text.js\";\nimport { useFileDownload } from \"@pi-studio-ui/hooks/use-file-download.js\";\nimport { CodeView } from \"./CodeView.js\";\nimport { selectTextViewerState } from \"./text-viewer-state.js\";\nimport type { ViewerProps } from \"./viewer-registry.js\";\nimport styles from \"./FilePanel.module.css\";\nimport textStyles from \"./TextViewer.module.css\";\n\n/** Display ceiling above the inline cap. `CodeView`'s CodeMirror instance enables\n * `EditorView.lineWrapping`, which makes it measure line heights across the whole document —\n * well past interactive above this size, so files this large get a terminal download-only state\n * instead of an attempted render. Deliberately not configurable (task-009 notes). */\nexport const MAX_DISPLAY_BYTES = 30 * 1024 * 1024;\n\nfunction formatMegabytes(bytes: number): string {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport function TextViewer({ path }: ViewerProps) {\n const inline = useFileRead(path);\n const tooLarge = inline.error instanceof FileTooLargeError ? inline.error : null;\n const displayable = tooLarge !== null && tooLarge.size <= MAX_DISPLAY_BYTES;\n\n const streamed = useFileText(path, displayable);\n const [downloadRequested, setDownloadRequested] = useState(false);\n const download = useFileDownload(path, downloadRequested);\n\n const state = selectTextViewerState({\n maxDisplayBytes: MAX_DISPLAY_BYTES,\n inline: {\n isLoading: inline.isLoading,\n isError: inline.isError,\n tooLarge: tooLarge ? { size: tooLarge.size } : null,\n errorMessage: inline.error instanceof Error ? inline.error.message : null,\n content: inline.data?.content ?? null,\n },\n streamed: {\n isLoading: streamed.isLoading,\n isError: streamed.isError,\n errorMessage: streamed.error instanceof Error ? streamed.error.message : null,\n content: streamed.data?.content ?? null,\n },\n download: {\n requested: downloadRequested,\n isLoading: download.isLoading,\n objectUrl: download.data?.objectUrl ?? null,\n fileName: download.data?.fileName ?? null,\n },\n });\n\n switch (state.kind) {\n case \"loading\":\n return (\n <div className={styles.emptyState}>\n <Spinner size=\"sm\" /> Loading...\n </div>\n );\n case \"inline\":\n return <CodeView path={path} content={state.content} />;\n case \"streaming\":\n return (\n <div className={styles.emptyState}>\n <Spinner size=\"sm\" /> Streaming {formatMegabytes(state.size)} file...\n </div>\n );\n case \"stream-error\":\n return <div className={styles.emptyState}>Error: {state.message}</div>;\n case \"streamed\":\n return (\n <div className={textStyles.wrap}>\n <div className={textStyles.note}>{formatMegabytes(state.size)} file streamed</div>\n <div className={textStyles.body}>\n <CodeView path={path} content={state.content} />\n </div>\n </div>\n );\n case \"too-large\": {\n const name = path.split(\"/\").pop() || path;\n return (\n <div className={styles.emptyState}>\n <div className={textStyles.tooLarge}>\n <div>\n {formatMegabytes(state.size)} — too large to display (display ceiling is{\" \"}\n {formatMegabytes(state.maxDisplayBytes)}).\n </div>\n {state.downloading ? (\n <Spinner size=\"sm\" />\n ) : state.downloadUrl ? (\n <a href={state.downloadUrl} download={state.downloadName || name}>\n <Button size=\"sm\">Save file</Button>\n </a>\n ) : (\n <Button size=\"sm\" onClick={() => setDownloadRequested(true)}>\n Download\n </Button>\n )}\n </div>\n </div>\n );\n }\n case \"error\":\n return <div className={styles.emptyState}>Error: {state.message}</div>;\n }\n}\n"],"names":["mergeFileTextState","input","enabled","download","decode","useFileText","path","useFileDownload","objectUrl","_a","useQuery","rpcKeys","selectTextViewerState","maxDisplayBytes","inline","streamed","size","MAX_DISPLAY_BYTES","formatMegabytes","bytes","TextViewer","useFileRead","tooLarge","FileTooLargeError","displayable","downloadRequested","setDownloadRequested","useState","state","_b","_c","_d","jsxs","styles","jsx","Spinner","CodeView","textStyles","name","Button"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as o}from"./vendor-react-p-LLqNcx.js";import{S as t}from"./index-
|
|
2
|
-
//# sourceMappingURL=VideoViewer-
|
|
1
|
+
import{j as o}from"./vendor-react-p-LLqNcx.js";import{S as t}from"./index-CL9Q4aRP.js";import{u as a}from"./use-file-download-DqpI1O5G.js";import{p as e}from"./FilePanel.module-DoDNR5C2.js";import"./vendor-query-chxBrUuj.js";import"./vendor-B2Vnrid8.js";import"./vendor-markdown-ePgMI45n.js";import"./vendor-highlight-Cry4x-mr.js";import"./vendor-icons-CAhpOZSZ.js";import"./vendor-overlays-DWi-xJna.js";import"./vendor-dnd-CjSU1Kqk.js";const n="_wrap_1c9y4_1",m="_video_1c9y4_10",i={wrap:n,video:m};function _({path:s}){const r=a(s);return r.isLoading?o.jsxs("div",{className:e.emptyState,children:[o.jsx(t,{size:"sm"})," Loading video..."]}):r.isError?o.jsxs("div",{className:e.emptyState,children:["Error: ",r.error instanceof Error?r.error.message:"unknown error"]}):r.data?o.jsx("div",{className:i.wrap,children:o.jsx("video",{className:i.video,src:r.data.objectUrl,controls:!0})}):null}export{_ as VideoViewer};
|
|
2
|
+
//# sourceMappingURL=VideoViewer-CIgPlKOT.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoViewer-
|
|
1
|
+
{"version":3,"file":"VideoViewer-CIgPlKOT.js","sources":["../../../src/features/files/VideoViewer.tsx"],"sourcesContent":["/**\n * VideoViewer — renders a binary video file via the file-transfer binary download\n * (`useFileDownload`) into a native `<video>` element (POC_TO_APP_PLAN_UI.md §4.5 follow-up:\n * modular file preview).\n */\n\nimport { Spinner } from \"@pi-studio-ui/components/primitives/Spinner.js\";\nimport { useFileDownload } from \"@pi-studio-ui/hooks/use-file-download.js\";\nimport type { ViewerProps } from \"./viewer-registry.js\";\nimport panelStyles from \"./FilePanel.module.css\";\nimport styles from \"./VideoViewer.module.css\";\n\nexport function VideoViewer({ path }: ViewerProps) {\n const query = useFileDownload(path);\n\n if (query.isLoading) {\n return (\n <div className={panelStyles.emptyState}>\n <Spinner size=\"sm\" /> Loading video...\n </div>\n );\n }\n if (query.isError) {\n return (\n <div className={panelStyles.emptyState}>\n Error: {query.error instanceof Error ? query.error.message : \"unknown error\"}\n </div>\n );\n }\n if (!query.data) return null;\n\n return (\n <div className={styles.wrap}>\n {/* eslint-disable-next-line jsx-a11y/media-has-caption -- arbitrary local file, no caption track available */}\n <video className={styles.video} src={query.data.objectUrl} controls />\n </div>\n );\n}\n"],"names":["VideoViewer","path","query","useFileDownload","jsxs","panelStyles","jsx","Spinner","styles"],"mappings":"ofAYO,SAASA,EAAY,CAAE,KAAAC,GAAqB,CACjD,MAAMC,EAAQC,EAAgBF,CAAI,EAElC,OAAIC,EAAM,UAENE,EAAAA,KAAC,MAAA,CAAI,UAAWC,EAAY,WAC1B,SAAA,CAAAC,EAAAA,IAACC,EAAA,CAAQ,KAAK,IAAA,CAAK,EAAE,mBAAA,EACvB,EAGAL,EAAM,QAENE,EAAAA,KAAC,MAAA,CAAI,UAAWC,EAAY,WAAY,SAAA,CAAA,UAC9BH,EAAM,iBAAiB,MAAQA,EAAM,MAAM,QAAU,eAAA,EAC/D,EAGCA,EAAM,WAGR,MAAA,CAAI,UAAWM,EAAO,KAErB,eAAC,QAAA,CAAM,UAAWA,EAAO,MAAO,IAAKN,EAAM,KAAK,UAAW,SAAQ,GAAC,EACtE,EANsB,IAQ1B"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/ChatPanel-EEteI2qS.js","assets/vendor-react-p-LLqNcx.js","assets/vendor-query-chxBrUuj.js","assets/vendor-icons-CAhpOZSZ.js","assets/markdown-CbV5mRfd.js","assets/vendor-highlight-Cry4x-mr.js","assets/vendor-markdown-ePgMI45n.js","assets/vendor-B2Vnrid8.js","assets/paths--dEDMUm1.js","assets/markdown-JJHL50c6.css","assets/StatusBadge-D5M084rB.js","assets/StatusBadge-BgIink5J.css","assets/DiffView-CUxhzW_O.js","assets/DiffView-CRbeRztO.css","assets/vendor-overlays-DWi-xJna.js","assets/vendor-dnd-CjSU1Kqk.js","assets/ChatPanel-CG6o6nFc.css","assets/FilePanel-Dg4iw-OH.js","assets/use-file-watch-ChUc4oqE.js","assets/FilePanel.module-DoDNR5C2.js","assets/FilePanel-DIInFr3h.css","assets/TerminalPanel-qCs6c5HO.js","assets/vendor-terminal-BhzX2_ts.js","assets/vendor-terminal-DYP7pi_n.css","assets/TerminalPanel-BQj62G6N.css","assets/MoleculeViewerPanel-DS2USK0p.js","assets/vendor-molviewer-PwwY6jQU.js","assets/vendor-molviewer-CtvG6b9Z.css","assets/use-file-download-DqpI1O5G.js","assets/MoleculeViewerPanel-CTZU0_lX.css","assets/TextViewer-DwAXmSeZ.js","assets/CodeView-CKUKYQ4A.js","assets/CodeView-CLRODhB8.css","assets/TextViewer-B9IV5fXQ.css","assets/MarkdownFileViewer-DHWrkv5i.js","assets/MarkdownFileViewer-BD5Ri_Fb.css","assets/ImageViewer-CiKV0OZI.js","assets/ImageViewer-ijzMsq8H.css","assets/VideoViewer-CIgPlKOT.js","assets/VideoViewer-BBuZ4wou.css","assets/BinaryFallbackViewer-C8VjuRB5.js","assets/BinaryFallbackViewer-CLiNWU_S.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
var Bn=Object.defineProperty;var An=(e,t,n)=>t in e?Bn(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var I=(e,t,n)=>An(e,typeof t!="symbol"?t+"":t,n);import{r as w,j as i,c as $n}from"./vendor-react-p-LLqNcx.js";import{Q as Dn,a as On,u as Ht,b as Pn,c as xe,d as Fn}from"./vendor-query-chxBrUuj.js";import{A as l,B as u,C as $,D as Wt,E as M,F as he,G as se,H as B,I as b,J as D,K as P,L as Ge,M as we,N as me}from"./vendor-B2Vnrid8.js";import{L as zn,P as Ln,a as Hn,b as Wn,c as Un,E as Ut,C as _t,d as Kt,F as Kn,e as Vt,f as Gt,A as Xt,S as Jt,G as Vn,g as Gn,M as Yt,X as ct,h as Ae,i as Qt,j as Xn,k as Zt,U as Jn,R as Yn,l as en,m as Qn,n as Zn,o as es,D as ts,p as ns,q as ss}from"./vendor-icons-CAhpOZSZ.js";import{R as Xe,T as Je,P as Ye,C as Qe,I as W,S as lt,D as os,a as rs,b as as,c as is,d as vt,e as dt}from"./vendor-overlays-DWi-xJna.js";import{u as cs,a as ls,D as ds,c as us,S as fs,h as ps,P as hs,b as ms,C as gs}from"./vendor-dnd-CjSU1Kqk.js";import{_ as oe}from"./vendor-highlight-Cry4x-mr.js";import"./vendor-markdown-ePgMI45n.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))s(o);new MutationObserver(o=>{for(const r of o)if(r.type==="childList")for(const a of r.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&s(a)}).observe(document,{childList:!0,subtree:!0});function n(o){const r={};return o.integrity&&(r.integrity=o.integrity),o.referrerPolicy&&(r.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?r.credentials="include":o.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function s(o){if(o.ep)return;o.ep=!0;const r=n(o);fetch(o.href,r)}})();const _s=new Dn;function tn(e){const t=e.trim().replace(/^#/,""),n=t.length===3?t.split("").map(s=>s+s).join(""):t;if(n.length!==6||/[^0-9a-fA-F]/.test(n))throw new Error(`Invalid hex color: ${e}`);return{r:parseInt(n.slice(0,2),16),g:parseInt(n.slice(2,4),16),b:parseInt(n.slice(4,6),16)}}function ys(e){return Math.max(0,Math.min(255,Math.round(e)))}function bs({r:e,g:t,b:n}){const s=o=>ys(o).toString(16).padStart(2,"0");return`#${s(e)}${s(t)}${s(n)}`}function ws(e){const{r:t,g:n,b:s}=tn(e),o=r=>{const a=r/255;return a<=.03928?a/12.92:((a+.055)/1.055)**2.4};return .2126*o(t)+.7152*o(n)+.0722*o(s)}function yt(e,t="#18181b",n="#ffffff"){return ws(e)>.45?t:n}function Ss(e,t){const{r:n,g:s,b:o}=tn(e),r=Math.max(0,Math.min(1,t));return bs({r:n+(255-n)*r,g:s+(255-s)*r,b:o+(255-o)*r})}function Pe(e){const t=e.trim().replace(/^#/,"");return(t.length===3||t.length===6)&&!/[^0-9a-fA-F]/.test(t)}const V=(e,t,n,s,o,r,a,c,d,m,p)=>({50:e,100:t,200:n,300:s,400:o,500:r,600:a,700:c,800:d,900:m,950:p}),v={zinc:V("#fafafa","#f4f4f5","#e4e4e7","#d4d4d8","#a1a1aa","#71717a","#52525b","#3f3f46","#27272a","#18181b","#09090b"),gray:V("#f9fafb","#f3f4f6","#e5e7eb","#d1d5db","#9ca3af","#6b7280","#4b5563","#374151","#1f2937","#111827","#030712"),slate:V("#f8fafc","#f1f5f9","#e2e8f0","#cbd5e1","#94a3b8","#64748b","#475569","#334155","#1e293b","#0f172a","#020617"),blue:V("#eff6ff","#dbeafe","#bfdbfe","#93c5fd","#60a5fa","#3b82f6","#2563eb","#1d4ed8","#1e40af","#1e3a8a","#172554"),green:V("#f0fdf4","#dcfce7","#bbf7d0","#86efac","#4ade80","#22c55e","#16a34a","#15803d","#166534","#14532d","#052e16"),red:V("#fef2f2","#fee2e2","#fecaca","#fca5a5","#f87171","#ef4444","#dc2626","#b91c1c","#991b1b","#7f1d1d","#450a0a"),teal:V("#f0fdfa","#ccfbf1","#99f6e4","#5eead4","#2dd4bf","#14b8a6","#0d9488","#0f766e","#115e59","#134e4a","#042f2e"),amber:V("#fffbeb","#fef3c7","#fde68a","#fcd34d","#fbbf24","#f59e0b","#d97706","#b45309","#92400e","#78350f","#451a03"),yellow:V("#fefce8","#fef9c3","#fef08a","#fde047","#facc15","#eab308","#ca8a04","#a16207","#854d0e","#713f12","#422006"),purple:V("#faf5ff","#f3e8ff","#e9d5ff","#d8b4fe","#c084fc","#a855f7","#9333ea","#7e22ce","#6b21a8","#581c87","#3b0764"),orange:V("#fff7ed","#ffedd5","#fed7aa","#fdba74","#fb923c","#f97316","#ea580c","#c2410c","#9a3412","#7c2d12","#431407"),white:"#ffffff",black:"#000000"},xs={statusSuccess:"#16a34a",statusDanger:"#dc2626",statusWarning:"#f59e0b",statusMerged:"#9333ea"},vs={statusSuccess:"#15803d",statusDanger:"#b91c1c",statusWarning:"#d97706",statusMerged:"#7c3aed"};function ks(){return{keyword:v.purple[300],string:v.green[300],number:v.orange[300],boolean:v.orange[300],comment:v.zinc[500],function:v.blue[300],variable:v.zinc[200],type:v.teal[300],class:v.amber[300],constant:v.orange[300],operator:v.zinc[400],punctuation:v.zinc[400],tag:v.red[300],attribute:v.amber[300],property:v.blue[200],regexp:v.teal[300],escape:v.orange[200],heading:v.blue[300],link:v.blue[400],deleted:v.red[400],inserted:v.green[400]}}function Is(){return{keyword:v.purple[700],string:v.green[700],number:v.orange[700],boolean:v.orange[700],comment:v.zinc[500],function:v.blue[700],variable:v.zinc[800],type:v.teal[700],class:v.amber[700],constant:v.orange[700],operator:v.zinc[600],punctuation:v.zinc[600],tag:v.red[700],attribute:v.amber[700],property:v.blue[700],regexp:v.teal[700],escape:v.orange[700],heading:v.blue[700],link:v.blue[600],deleted:v.red[700],inserted:v.green[700]}}function nn(e,t,n){return{background:e,foreground:t,cursor:n,cursorAccent:e,selectionBackground:"rgba(255,255,255,0.18)",black:v.zinc[900],red:v.red[500],green:v.green[500],yellow:v.amber[500],blue:v.blue[500],magenta:v.purple[500],cyan:v.teal[500],white:v.zinc[300],brightBlack:v.zinc[600],brightRed:v.red[400],brightGreen:v.green[400],brightYellow:v.amber[400],brightBlue:v.blue[400],brightMagenta:v.purple[400],brightCyan:v.teal[400],brightWhite:v.zinc[50]}}function Ce(e){const t=v.zinc[50],n=yt(e.accent);return{surface0:e.surface0,surface1:e.surface1,surface2:e.surface2,surface3:e.surface3,surface4:e.surface4,surfaceDiffEmpty:e.surfaceDiffEmpty,surfaceSidebar:e.surfaceSidebar,surfaceSidebarHover:e.surfaceSidebarHover,surfaceWorkspace:e.surface1,foreground:t,foregroundMuted:e.mutedForeground,accent:e.accent,accentBright:e.accentBright,accentForeground:n,destructive:e.destructive,destructiveForeground:v.white,success:e.accent,successForeground:v.white,border:e.border,borderAccent:e.borderAccent,...xs,diffAddition:v.green[400],diffDeletion:v.red[500],scrollbarHandle:e.scrollbarHandle,background:e.surface0,popover:e.surface2,popoverForeground:t,primary:e.accent,secondary:e.surface2,muted:e.surface2,mutedForeground:e.mutedForeground,input:e.surface2,ring:e.accent,palette:v,syntax:ks(),terminal:nn(e.surface0,t,e.accent)}}function js(){const e="#1a1a1e",t="#253e6f";return{surface0:"#ffffff",surface1:"#fafafa",surface2:"#f4f4f5",surface3:"#e4e4e7",surface4:"#d4d4d8",surfaceDiffEmpty:"#f6f6f6",surfaceSidebar:"#f4f4f5",surfaceSidebarHover:"#e9e9ec",surfaceWorkspace:"#ffffff",foreground:e,foregroundMuted:"#71717a",accent:t,accentBright:"#2f4f8e",accentForeground:yt(t),destructive:"#b04138",destructiveForeground:"#ffffff",success:t,successForeground:"#ffffff",border:"#e4e4e7",borderAccent:"#ececf1",...vs,diffAddition:"#15803d",diffDeletion:"#b91c1c",scrollbarHandle:"#3f3f46",background:"#ffffff",popover:"#ffffff",popoverForeground:e,primary:t,secondary:"#f4f4f5",muted:"#f4f4f5",mutedForeground:"#71717a",input:"#f4f4f5",ring:t,palette:v,syntax:Is(),terminal:{...nn("#ffffff",e,t),selectionBackground:"rgba(0,0,0,0.15)",white:"#ffffff",brightWhite:"#fafafa"}}}const Cs={light:"#ffffff",dark:"#3b62b0",zinc:"#808080",midnight:"#4A6BA8",claude:"#D97757",ghostty:"#8caaee"},sn=["light","dark","zinc","midnight","claude","ghostty"];function Ts(e){if(e==="light")return js();switch(e){case"dark":return Ce({surface0:"#181B1A",surface1:"#1E2120",surface2:"#272A29",surface3:"#434645",surface4:"#595B5B",surfaceDiffEmpty:"#252827",surfaceSidebar:"#141716",surfaceSidebarHover:"#1c1f1e",mutedForeground:"#A1A5A4",scrollbarHandle:"#717574",border:"#252B2A",borderAccent:"#2f3135",accent:"#2e5cb8",accentBright:"#a2b4d7",destructive:"#c64f43"});case"zinc":return Ce({surface0:"#18181b",surface1:"#1f1f22",surface2:"#27272a",surface3:"#3f3f46",surface4:"#52525b",surfaceDiffEmpty:"#242427",surfaceSidebar:"#131316",surfaceSidebarHover:"#1b1b1e",mutedForeground:"#a1a1aa",scrollbarHandle:"#71717a",border:"#27272a",borderAccent:"#303036",accent:"#e4e4e7",accentBright:"#fafafa",destructive:"#c44a4a"});case"midnight":return Ce({surface0:"#161820",surface1:"#1c1e27",surface2:"#252731",surface3:"#3c3e4c",surface4:"#535564",surfaceDiffEmpty:"#222430",surfaceSidebar:"#121420",surfaceSidebarHover:"#1a1c28",mutedForeground:"#9a9db0",scrollbarHandle:"#6b6e82",border:"#242636",borderAccent:"#2e3040",accent:"#3b6fcf",accentBright:"#7eaaeb",destructive:"#c44a52"});case"claude":return Ce({surface0:"#1f1f1e",surface1:"#262523",surface2:"#2f2d2b",surface3:"#4a4745",surface4:"#605d5b",surfaceDiffEmpty:"#2a2826",surfaceSidebar:"#1a1918",surfaceSidebarHover:"#222120",mutedForeground:"#ada9a5",scrollbarHandle:"#78746f",border:"#2c2a27",borderAccent:"#36332f",accent:"#d97757",accentBright:"#e89a7f",destructive:"#cf513e"});case"ghostty":return Ce({surface0:"#282c34",surface1:"#2f333d",surface2:"#383c48",surface3:"#4a4f5e",surface4:"#5b6175",surfaceDiffEmpty:"#323643",surfaceSidebar:"#21252d",surfaceSidebarHover:"#292d36",mutedForeground:"#c8ccd8",scrollbarHandle:"#a0a4b2",border:"#353a47",borderAccent:"#3f4454",accent:"#89b4fa",accentBright:"#b4d0fc",destructive:"#c44a55"})}}const on={};for(const e of sn){const t=e==="light"?"light":"dark";on[e]={name:e,colorScheme:t,colors:Ts(e),swatch:Cs[e]}}const Es=on,Ns={0:0,1:4,"1.5":6,2:8,3:12,4:16,6:24,8:32,12:48,16:64,20:80,24:96,32:128},rn={xs:12,code:12,sm:14,base:16,lg:18,xl:20,"2xl":22,"3xl":26,"4xl":34},Rs={normal:"400",medium:"500",semibold:"600",bold:"700"},Ms={none:0,sm:2,base:4,md:6,lg:8,xl:12,"2xl":16,full:9999},qs={0:0,1:1,2:2},Bs={0:0,50:.5,100:1},As={xs:12,sm:14,md:16,lg:20},$s={diff:22},an='system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',cn='ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace';function Ds(e){const t=e==="dark"?"rgba(0,0,0,0.55)":"rgba(24,24,27,0.12)";return{sm:{color:t,offsetX:0,offsetY:1,radius:2,elevation:1},md:{color:t,offsetX:0,offsetY:3,radius:8,elevation:4},lg:{color:t,offsetX:0,offsetY:10,radius:24,elevation:12}}}function ln(e){const t=Es[e];return{name:e,colorScheme:t.colorScheme,colors:t.colors,spacing:Ns,fontSize:{...rn},fontFamily:{ui:an,mono:cn},lineHeight:{...$s},iconSize:As,fontWeight:Rs,borderRadius:Ms,borderWidth:qs,opacity:Bs,shadow:Ds(t.colorScheme),swatch:t.swatch}}const Os=16,Ps=10,Fs=24;function zs(e,t){const n=t.themeName??e.name,s=ln(n),o=t.uiFont&&t.uiFont.trim()!==""?t.uiFont:an,r=t.monoFont&&t.monoFont.trim()!==""?t.monoFont:cn;let a={...s.fontSize};if(t.fontSize!=null){const d=Math.max(Ps,Math.min(Fs,t.fontSize))/Os,m=["xs","code","sm","base","lg","xl","2xl","3xl","4xl"];for(const p of m){const S=rn[p];a[p]=Math.round(S*d)}}return{...s,fontSize:a,fontFamily:{ui:o,mono:r}}}function Ls(e){return ln(e)}const Hs="dark";function Ws(e){const t={};Us(e.colors,t);for(const[n,s]of Object.entries(e.spacing))t[`--pi-spacing-${n}`]=`${s}px`;for(const[n,s]of Object.entries(e.fontSize))t[`--pi-font-size-${n}`]=`${s}px`;t["--pi-font-ui"]=e.fontFamily.ui,t["--pi-font-mono"]=e.fontFamily.mono,t["--pi-line-height-diff"]=`${e.lineHeight.diff}px`;for(const[n,s]of Object.entries(e.iconSize))t[`--pi-icon-size-${n}`]=`${s}px`;for(const[n,s]of Object.entries(e.fontWeight))t[`--pi-font-weight-${n}`]=String(s);for(const[n,s]of Object.entries(e.borderRadius))t[`--pi-radius-${n}`]=s===9999?"9999px":`${s}px`;for(const[n,s]of Object.entries(e.borderWidth))t[`--pi-border-width-${n}`]=`${s}px`;for(const[n,s]of Object.entries(e.opacity))t[`--pi-opacity-${n}`]=String(s);for(const[n,s]of Object.entries(e.shadow))if(typeof s=="string")t[`--pi-shadow-${n}`]=s;else if(s&&typeof s=="object"){const o=s;t[`--pi-shadow-${n}`]=`${o.offsetX}px ${o.offsetY}px ${o.radius}px ${o.color}`}return t["--pi-color-scheme"]=e.colorScheme,t}function Us(e,t){const{palette:n,syntax:s,terminal:o,...r}=e;for(const[a,c]of Object.entries(r))typeof c=="string"&&(t[`--pi-color-${a}`]=c);s&&Ks(s,t),o&&(t["--pi-terminal-bg"]=o.background,t["--pi-terminal-fg"]=o.foreground)}function Ks(e,t){for(const[n,s]of Object.entries(e))typeof s=="string"&&(t[`--syntax-${Vs(n)}`]=s)}function Vs(e){return e.replace(/[A-Z]/g,t=>`-${t.toLowerCase()}`)}function Gs(e){if(typeof document>"u")return;const t=document.documentElement.style;for(const[s,o]of Object.entries(e))t.setProperty(s,o);const n=e["--pi-color-scheme"];n&&document.documentElement.style.setProperty("color-scheme",n)}function Xs(e){const t=Ws(e);Gs(t)}const Te=l().min(1,"Asset path must not be empty"),Js=u({accent:l().refine(Pe,{message:"accent must be a valid #rrggbb hex color"}),accentBright:l().refine(Pe,{message:"accentBright must be a valid #rrggbb hex color"}).optional(),accentForeground:l().refine(Pe,{message:"accentForeground must be a valid #rrggbb hex color"}).optional(),swatch:l().refine(Pe,{message:"swatch must be a valid #rrggbb hex color"}).optional()}),Ys=u({logoLight:Te,logoDark:Te,logoMark:Te.optional(),icon:Te,splash:Te.optional()}),Qs=u({docs:l().url().optional(),support:l().url().optional(),issues:l().url().optional(),website:l().url().optional()}).optional(),Zs=u({copyright:l().optional(),companyName:l().optional()}).optional();u({productName:l().min(1,"productName is required"),shortName:l().optional(),tagline:l().optional(),colors:Js,assets:Ys,links:Qs,legal:Zs});function eo(e){const t=e.accent,n=e.accentBright??Ss(t,.35),s=e.accentForeground??yt(t),o=e.swatch??t;return{accent:t,accentBright:n,accentForeground:s,swatch:o}}function to(e,t){return{...e,colors:{...e.colors,accent:t.accent,accentBright:t.accentBright,accentForeground:t.accentForeground,primary:t.accent,ring:t.accent},swatch:t.swatch}}const kt="pi-studio-appearance";function no(e,t){let n=c(e),s=null;function o(p){return p==="system"?typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":p}function r(p,S){const h=o(p),x=Ls(h);let g=zs(x,{...S,themeName:h});if(t!=null&&t.colors){const _=eo(t.colors);g=to(g,_)}return g}function a(){const p=JSON.stringify({mode:n.mode,settings:n.settings});e.set(kt,p)}function c(p){const S=p.get(kt);let h=Hs,x={};if(S)try{const _=JSON.parse(S);_.mode&&(sn.includes(_.mode)||_.mode==="system")&&(h=_.mode),_.settings&&typeof _.settings=="object"&&(x=_.settings)}catch{}const g=r(h,x);return{mode:h,settings:x,resolvedTheme:g}}function d(){n={...n,resolvedTheme:r(n.mode,n.settings)},a()}const m={getState(){return n},setMode(p){n={...n,mode:p},d(),m.apply()},updateSettings(p){n={...n,settings:{...n.settings,...p}},d(),m.apply()},apply(){Xs(n.resolvedTheme)},listen(){if(typeof window>"u")return()=>{};const p=window.matchMedia("(prefers-color-scheme: dark)"),S=()=>{n.mode==="system"&&(d(),m.apply())};return p.addEventListener("change",S),s=()=>p.removeEventListener("change",S),s}};return m}function so({store:e,brandConfig:t,children:n}){const s=w.useRef(null);return s.current||(s.current=no(e,t),s.current.apply()),w.useEffect(()=>s.current.listen(),[]),i.jsx(i.Fragment,{children:n})}const oo={get(e){var t;try{return((t=globalThis.localStorage)==null?void 0:t.getItem(e))??null}catch{return null}},set(e,t){var n;try{(n=globalThis.localStorage)==null||n.setItem(e,t)}catch{}}};function ro({children:e}){return i.jsx(so,{store:oo,children:i.jsx(On,{client:_s,children:e})})}const ao="_btn_1chbg_4",io="_secondary_1chbg_44",co="_outline_1chbg_53",lo="_ghost_1chbg_62",uo="_destructive_1chbg_72",fo="_iconOnly_1chbg_82",po="_loadingSpinner_1chbg_89",ho="_piSpin_1chbg_1",Fe={btn:ao,default:"_default_1chbg_35",secondary:io,outline:co,ghost:lo,destructive:uo,iconOnly:fo,loadingSpinner:po,piSpin:ho},It={xs:28,sm:32,md:36,lg:40},mo={xs:12,sm:14,md:16,lg:20},jt={xs:8,sm:10,md:12,lg:16},go={xs:12,sm:13,md:14,lg:15};function _o(e){const{disabled:t,loading:n}=e;return t||n?{opacity:.5,isGhostHover:!1}:{opacity:1,isGhostHover:!1}}function O({variant:e="default",size:t="md",loading:n=!1,disabled:s=!1,leftIcon:o,trailing:r,iconOnly:a=!1,children:c,style:d,className:m,...p}){const{opacity:S}=_o({disabled:s??!1,loading:n}),h=s||n,x=mo[t];return i.jsxs("button",{className:$(Fe.btn,Fe[e],a&&Fe.iconOnly,m),disabled:h,"aria-busy":n||void 0,style:{minHeight:It[t],paddingLeft:a?void 0:jt[t],paddingRight:a?void 0:jt[t],fontSize:go[t],opacity:S,...a?{width:It[t]}:{},...d},...p,children:[n?i.jsx(zn,{className:Fe.loadingSpinner,size:x,"aria-hidden":!0}):o?i.jsx("span",{"aria-hidden":!0,style:{display:"flex",alignItems:"center",flexShrink:0},children:o}):null,c,r&&!n&&i.jsx("span",{"aria-hidden":!0,style:{display:"flex",alignItems:"center",flexShrink:0},children:r})]})}const yo="_input_a7qyq_3",bo="_textarea_a7qyq_32",ut={input:yo,textarea:bo},Ve=w.forwardRef(function({className:t,...n},s){return i.jsx("input",{ref:s,className:$(ut.input,t),...n})}),Iu=w.forwardRef(function({className:t,...n},s){return i.jsx("textarea",{ref:s,className:$(ut.input,ut.textarea,t),...n})}),dn=l().datetime({offset:!0});l().uuid();l().regex(/^[A-Za-z0-9_-]+$/,"expected base64url");const Be=Wt([M(),dn]),wo=he(["mobile","browser","cli","mcp"]),So=u({type:b("hello"),clientId:l(),clientType:wo,protocolVersion:M(),appVersion:l().optional(),capabilities:se(l(),B()).optional()}),xo=u({status:b("server_info"),serverId:l(),hostname:l().optional(),version:l().optional(),capabilities:se(l(),D()),features:se(l(),D())}),vo=u({type:b("status"),payload:xo}),ko=u({type:b("ping"),requestId:l(),clientSentAt:Be.optional()}),Io=u({type:b("pong"),requestId:l(),clientSentAt:Be.optional(),serverReceivedAt:Be,serverSentAt:Be}),un=he(["initializing","idle","running","error","closed"]),Ze=u({mimeType:l().optional(),data:l().optional()}).passthrough(),jo=u({prs:P(D()).optional(),issues:P(D()).optional()}).passthrough(),Co=u({provider:l(),cwd:l(),modeId:l().optional(),model:l().optional(),modelProvider:l().optional(),thinkingOptionId:l().optional(),featureValues:se(l(),D()).optional(),title:l().nullable().optional(),approvalPolicy:l().optional(),sandboxMode:l().optional(),networkAccess:B().optional(),webSearch:B().optional(),extra:u({pi:D().optional()}).passthrough().optional(),systemPrompt:l().optional(),mcpServers:se(l(),D()).optional()}),To=u({type:b("create_agent_request"),requestId:l(),config:Co,env:se(l(),l()).optional(),workspaceId:l().optional(),worktreeName:l().optional(),initialPrompt:l().optional(),clientMessageId:l().optional(),outputSchema:se(l(),D()).optional(),images:P(Ze).optional(),attachments:jo.optional(),git:D().optional(),worktree:D().optional(),autoArchive:B().optional(),labels:se(l(),l()).default({})}),Eo=u({type:b("create_agent_response"),requestId:l(),payload:u({agentId:l()}).passthrough()}),No=u({type:b("agent_update"),agentId:l(),status:un.optional(),title:l().nullable().optional(),labels:se(l(),l()).optional()}),Ro=u({type:b("agent_status"),agentId:l(),status:un}),Mo=u({type:b("agent_list"),agents:P(u({id:l()}).passthrough())}),qo=u({type:b("agent_deleted"),agentId:l()}),Bo=u({type:b("agent_archived"),agentId:l(),archivedAt:dn.optional()}),fn=Ge("kind",[u({kind:b("shell"),command:l().optional(),output:l().optional()}),u({kind:b("read"),path:l().optional(),output:l().optional()}),u({kind:b("edit"),path:l().optional(),diff:l().optional(),output:l().optional()}),u({kind:b("write"),path:l().optional(),output:l().optional()}),u({kind:b("search"),query:l().optional(),output:l().optional()}),u({kind:b("fetch"),url:l().optional(),output:l().optional()}),u({kind:b("task"),description:l().optional(),output:l().optional()})]),Ao=Ge("kind",[u({kind:b("user_message"),messageId:l().optional(),text:l().optional(),images:P(Ze).optional()}),u({kind:b("assistant_message"),messageId:l().optional(),text:l().optional()}),u({kind:b("reasoning"),text:l().optional()}),u({kind:b("tool_call"),callId:l().optional(),tool:fn,status:l().optional()}),u({kind:b("turn_started"),turnId:l().optional()}),u({kind:b("turn_completed"),turnId:l().optional()}),u({kind:b("turn_failed"),turnId:l().optional(),error:l().optional()}),u({kind:b("turn_canceled"),turnId:l().optional()}),u({kind:b("error"),message:l().optional()}),u({kind:b("queue_update"),steering:P(l()).optional(),followUp:P(l()).optional()})]),$o=u({type:b("agent_stream"),agentId:l(),seq:M().optional(),timestamp:Be.optional(),event:Ao}),Do=he(["before","after"]),Oo=u({start:M(),end:M()}),Po=u({type:b("fetch_agent_timeline_request"),requestId:l(),agentId:l(),cursor:l().nullable().optional(),direction:Do,limit:M().optional()}),Fo=u({type:b("fetch_agent_timeline_response"),requestId:l(),agentId:l().optional(),items:P(D()),seqStart:M(),seqEnd:M(),sourceSeqRanges:P(Oo),collapsed:B(),hasNewer:B(),startCursor:l().nullable().optional(),endCursor:l().nullable().optional()}),zo=u({type:b("agent_permission_request"),requestId:l(),agentId:l(),toolName:l().optional(),tool:fn.optional(),action:D().optional(),responses:P(l()).optional()}),Lo=u({type:b("agent_permission_resolved"),requestId:l(),agentId:l(),decision:l().optional()}),Ho=u({type:b("agent.permission.respond.request"),requestId:l(),permissionRequestId:l(),response:D()}),Wo=u({type:b("agent.permission.respond.response"),requestId:l(),payload:u({resolved:B()}).passthrough()}),Uo=u({type:b("respond_to_permission"),requestId:l(),permissionRequestId:l(),response:D()}),pn=he(["conversation","files","both"]),Ko=u({type:b("agent.rewind.request"),requestId:l(),agentId:l(),messageId:l(),mode:pn}).passthrough(),Vo=u({type:b("agent.rewind.response"),requestId:l(),payload:u({agentId:l(),messageId:l(),mode:pn,truncatedAt:l().optional()}).passthrough()}).passthrough(),Go=u({input:M().optional(),output:M().optional(),cacheRead:M().optional(),cacheWrite:M().optional(),total:M().optional()}).passthrough(),Xo=u({tokens:M().nullable().optional(),contextWindow:M().optional(),percent:M().nullable().optional()}).passthrough(),Jo=u({type:b("agent_session_stats_request"),requestId:l(),agentId:l()}).passthrough(),Yo=u({type:b("agent_session_stats_response"),requestId:l(),payload:u({sessionId:l().optional(),sessionFile:l().optional(),userMessages:M().optional(),assistantMessages:M().optional(),toolCalls:M().optional(),toolResults:M().optional(),totalMessages:M().optional(),tokens:Go.optional(),cost:M().optional(),contextUsage:Xo.optional(),model:l().optional()}).passthrough()}).passthrough(),Qo=u({type:b("agent_compact_request"),requestId:l(),agentId:l(),customInstructions:l().optional()}).passthrough(),Zo=u({type:b("agent_compact_response"),requestId:l(),payload:u({summary:l().optional(),firstKeptEntryId:l().optional(),tokensBefore:M().optional(),details:D().optional()}).passthrough()}).passthrough(),er=u({type:b("agent_new_session_request"),requestId:l(),agentId:l()}).passthrough(),tr=u({type:b("agent_new_session_response"),requestId:l(),payload:u({cancelled:B()}).passthrough()}).passthrough(),nr=u({type:b("agent_switch_session_request"),requestId:l(),agentId:l(),sessionPath:l()}).passthrough(),sr=u({type:b("agent_switch_session_response"),requestId:l(),payload:u({cancelled:B()}).passthrough()}).passthrough(),or=u({type:b("agent_fork_request"),requestId:l(),agentId:l(),entryId:l()}).passthrough(),rr=u({type:b("agent_fork_response"),requestId:l(),payload:u({text:l(),cancelled:B()}).passthrough()}).passthrough(),ar=u({type:b("agent_fork_messages_request"),requestId:l(),agentId:l()}).passthrough(),ir=u({type:b("agent_fork_messages_response"),requestId:l(),payload:u({messages:P(u({entryId:l(),text:l()}).passthrough())}).passthrough()}).passthrough(),cr=u({type:b("agent_clone_request"),requestId:l(),agentId:l()}).passthrough(),lr=u({type:b("agent_clone_response"),requestId:l(),payload:u({cancelled:B()}).passthrough()}).passthrough(),dr=u({type:b("agent_set_session_name_request"),requestId:l(),agentId:l(),name:l()}).passthrough(),ur=u({type:b("agent_set_session_name_response"),requestId:l(),payload:u({}).passthrough().optional()}).passthrough(),fr=u({type:b("agent_export_html_request"),requestId:l(),agentId:l(),outputPath:l().optional()}).passthrough(),pr=u({type:b("agent_export_html_response"),requestId:l(),payload:u({path:l()}).passthrough()}).passthrough(),hr=u({type:b("agent_set_model_request"),requestId:l(),agentId:l(),provider:l(),modelId:l()}).passthrough(),mr=u({type:b("agent_set_model_response"),requestId:l(),payload:u({}).passthrough()}).passthrough(),gr=u({type:b("agent_cycle_model_request"),requestId:l(),agentId:l()}).passthrough(),_r=u({type:b("agent_cycle_model_response"),requestId:l(),payload:u({model:D().nullable().optional(),thinkingLevel:l().optional(),isScoped:B().optional()}).passthrough()}).passthrough(),yr=u({type:b("agent_last_assistant_text_request"),requestId:l(),agentId:l()}).passthrough(),br=u({type:b("agent_last_assistant_text_response"),requestId:l(),payload:u({text:l().nullable()}).passthrough()}).passthrough(),wr=u({type:b("steer_agent_request"),requestId:l(),agentId:l(),message:l(),images:P(Ze).optional(),clientMessageId:l().optional()}).passthrough(),Sr=u({type:b("steer_agent_response"),requestId:l(),agentId:l(),ok:B()}).passthrough(),xr=u({type:b("follow_up_agent_request"),requestId:l(),agentId:l(),message:l(),images:P(Ze).optional(),clientMessageId:l().optional()}).passthrough(),vr=u({type:b("follow_up_agent_response"),requestId:l(),agentId:l(),ok:B()}).passthrough(),kr=u({name:l(),id:l().optional(),description:l().optional(),source:he(["extension","prompt","skill"]).optional(),scope:he(["user","project","temporary"]).optional(),path:l().optional()}).passthrough(),Ir=u({type:b("agent_list_commands_request"),requestId:l(),agentId:l()}).passthrough(),jr=u({type:b("agent_list_commands_response"),requestId:l(),payload:u({commands:P(kr)}).passthrough()}).passthrough(),Cr=u({type:b("rpc_error"),requestId:l(),code:l().optional(),message:l().optional(),error:l().optional()}),Tr=Ge("type",[To,Eo,No,Ro,Mo,qo,Bo,$o,Po,Fo,zo,Lo,Ho,Wo,Uo,Ko,Vo,Jo,Yo,Qo,Zo,er,tr,nr,sr,or,rr,ar,ir,cr,lr,dr,ur,fr,pr,hr,mr,gr,_r,yr,br,wr,Sr,xr,vr,Ir,jr,Cr]),Er=u({type:l()}).passthrough(),Nr=u({type:b("session"),message:Wt([Tr,Er])});Ge("type",[So,vo,ko,Io,Nr]);const Rr={inline_image_markdown:"inline_image_markdown"},_e={Output:1,Input:2,Resize:3,Snapshot:4,Restore:5},Mr=Object.fromEntries(Object.entries(_e).map(([e,t])=>[t,e])),hn=u({rows:M().int().nonnegative(),cols:M().int().nonnegative()});class Re extends Error{constructor(t){super(t),this.name="TerminalFrameError"}}const qr=new TextEncoder,Br=new TextDecoder;function Ar(e){if(!Number.isInteger(e)||e<0||e>255)throw new Re(`slot must be an integer 0–255, got ${e}`)}function Ee(e,t,n){const s=new Uint8Array(2+n.length);return s[0]=e,s[1]=t,s.set(n,2),s}function ju(e){switch(Ar(e.slot),e.opcode){case"Output":return Ee(_e.Output,e.slot,e.data);case"Input":return Ee(_e.Input,e.slot,e.data);case"Snapshot":return Ee(_e.Snapshot,e.slot,e.data);case"Restore":return Ee(_e.Restore,e.slot,e.data);case"Resize":{const t=JSON.stringify(hn.parse({rows:e.rows,cols:e.cols}));return Ee(_e.Resize,e.slot,qr.encode(t))}}}function $r(e){if(e.length<2)throw new Re(`frame too short: ${e.length} bytes (need ≥ 2)`);const t=e[0],n=e[1],s=Mr[t];if(!s)throw new Re(`unknown terminal opcode 0x${t.toString(16)}`);const o=e.subarray(2);if(s==="Resize"){let r;try{r=JSON.parse(Br.decode(o))}catch{throw new Re("resize payload is not valid JSON")}const a=hn.safeParse(r);if(!a.success)throw new Re("resize payload is not { rows, cols }");return{opcode:"Resize",slot:n,rows:a.data.rows,cols:a.data.cols}}return{opcode:s,slot:n,data:o.slice()}}const be={Begin:16,Chunk:17,End:18,Error:19},Dr=Object.fromEntries(Object.entries(be).map(([e,t])=>[t,e])),mn=u({transferId:l(),fileName:l().optional(),mimeType:l().optional(),totalBytes:M().int().nonnegative().optional()});class Me extends Error{constructor(t){super(t),this.name="FileTransferFrameError"}}const Ct=new TextEncoder,Tt=new TextDecoder;function Or(e){if(!Number.isInteger(e)||e<0||e>255)throw new Me(`stream must be an integer 0–255, got ${e}`)}function ze(e,t,n){const s=new Uint8Array(2+n.length);return s[0]=e,s[1]=t,s.set(n,2),s}function st(e){switch(Or(e.stream),e.opcode){case"Begin":return ze(be.Begin,e.stream,Ct.encode(JSON.stringify(mn.parse(e.meta))));case"Chunk":return ze(be.Chunk,e.stream,e.data);case"End":return ze(be.End,e.stream,new Uint8Array([e.ok?1:0]));case"Error":return ze(be.Error,e.stream,Ct.encode(e.message))}}function Pr(e){if(e.length<2)throw new Me(`frame too short: ${e.length} bytes (need ≥ 2)`);const t=e[0],n=e[1],s=Dr[t];if(!s)throw new Me(`unknown file-transfer opcode 0x${t.toString(16)}`);const o=e.subarray(2);switch(s){case"Begin":{let r;try{r=JSON.parse(Tt.decode(o))}catch{throw new Me("begin payload is not valid JSON")}const a=mn.safeParse(r);if(!a.success)throw new Me("begin payload is not a valid transfer header");return{opcode:"Begin",stream:n,meta:a.data}}case"Chunk":return{opcode:"Chunk",stream:n,data:o.slice()};case"End":return{opcode:"End",stream:n,ok:(o[0]??1)!==0};case"Error":return{opcode:"Error",stream:n,message:Tt.decode(o)}}}function Fr(e){try{return Pr(e)}catch{return null}}const zr=l().regex(/^[a-z][a-z0-9-]*$/,"invalid provider id"),Lr=he(["safe","moderate","dangerous","planning"]),Hr=u({supportsStreaming:B(),supportsSessionPersistence:B(),supportsDynamicModes:B(),supportsMcpServers:B(),supportsReasoningStream:B(),supportsToolInvocations:B(),supportsRewindConversation:B().optional(),supportsRewindFiles:B().optional(),supportsRewindBoth:B().optional(),supportsSteering:B().optional()}).passthrough(),Wr=u({id:l(),label:l().optional(),description:l().optional(),icon:l().optional(),colorTier:Lr});u({id:zr,label:l(),description:l().optional(),extends:l().optional(),modes:P(Wr),capabilities:Hr});function ft(e){return typeof e=="string"?e:e instanceof Uint8Array||Buffer.isBuffer(e)?e.toString("utf8"):String(e??"")}function gn(e){let t=null;const n={onMessage:null,onClose:null,onError:null,get isOpen(){return t!==null&&t.readyState===1},connect(s){return new Promise((o,r)=>{const a=e??(()=>new WebSocket(s));try{t=e?a(s):new WebSocket(s),t.binaryType="arraybuffer"}catch(c){r(c);return}t.onopen=()=>o(),t.onerror=c=>{var d;(t==null?void 0:t.readyState)!==1&&r(c),(d=n.onError)==null||d.call(n,c)},t.onclose=c=>{var d;(d=n.onClose)==null||d.call(n,c.code,ft(c.reason))},t.onmessage=c=>{var m,p,S,h;const{data:d}=c;d instanceof ArrayBuffer||d instanceof Uint8Array?(m=n.onMessage)==null||m.call(n,d):typeof d=="string"?(p=n.onMessage)==null||p.call(n,d):typeof Blob<"u"&&d instanceof Blob?(S=n.onMessage)==null||S.call(n,d):Buffer.isBuffer(d)&&((h=n.onMessage)==null||h.call(n,d.buffer.slice(d.byteOffset,d.byteOffset+d.byteLength)))}})},sendText(s){t==null||t.send(s)},sendBinary(s){t==null||t.send(s)},close(s=1e3,o=""){t==null||t.close(s,o),t=null}};return n}const qe="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function _n(e){let t="";for(let n=0;n<e.length;n+=3){const s=e[n],o=n+1<e.length,r=n+2<e.length,a=o?e[n+1]:0,c=r?e[n+2]:0;t+=qe[s>>2],t+=qe[(s&3)<<4|a>>4],t+=o?qe[(a&15)<<2|c>>6]:"=",t+=r?qe[c&63]:"="}return t}function yn(e){const t=e.replace(/=+$/,""),n=[];let s=0,o=0;for(const r of t){const a=qe.indexOf(r);if(a===-1)throw new Error(`invalid base64 character: ${JSON.stringify(r)}`);s=s<<6|a,o+=6,o>=8&&(o-=8,n.push(s>>o&255))}return new Uint8Array(n)}function Ur(e){try{const t=JSON.parse(e);return!t||typeof t.type!="string"?null:t}catch{return null}}const Ue=24;function bn(e,t){const n=we.randomBytes(Ue),s=we.box.after(e,n,t),o=new Uint8Array(n.length+s.length);return o.set(n,0),o.set(s,n.length),_n(o)}function wn(e,t){let n;try{n=yn(e)}catch{return null}if(n.length<Ue)return null;const s=n.slice(0,Ue),o=n.slice(Ue);return we.box.open.after(o,s,t)}function Kr(e,t){return bn(new TextEncoder().encode(e),t)}function Vr(e,t){const n=wn(e,t);return n?new TextDecoder().decode(n):null}function Gr(e,t,n,s,o,r){let a=!1;return n.onClose(c=>{var d;a=!0,(d=s.onClose)==null||d.call(s,c)}),{handleAppFrame(c){var p,S;if(a)return;const d=o();if(!r()||!d)return;const m=Vr(c.frame,d);if(m===null){(p=s.onAuthError)==null||p.call(s,new Error(`relay ${e} channel: message failed authentication`));return}(S=s.onMessage)==null||S.call(s,m)},handleBinaryFrame(c){var p,S;if(a)return;const d=o();if(!r()||!d)return;const m=wn(c.frame,d);if(m===null){(p=s.onAuthError)==null||p.call(s,new Error(`relay ${e} channel: binary message failed authentication`));return}(S=s.onBinaryMessage)==null||S.call(s,m)},send(c){if(a)throw new Error(`relay ${e} channel: cannot send after close()`);const d=o();if(!r()||!d)throw new Error(`relay ${e} channel: cannot send before the E2EE handshake completes`);n.send(JSON.stringify({type:"e2ee_app",frame:Kr(c,d)}))},sendBinary(c){if(a)throw new Error(`relay ${e} channel: cannot send after close()`);const d=o();if(!r()||!d)throw new Error(`relay ${e} channel: cannot send before the E2EE handshake completes`);n.send(JSON.stringify({type:"e2ee_bin",frame:bn(c,d)}))},close(){a||(a=!0,n.close())}}}function Xr(e){const{transport:t,attachment:n,daemonPublicKey:s}=e,o=e.events??{},r=we.box.keyPair(),a=we.box.before(s,r.secretKey);let c=!1;const d=Gr("client",n,t,o,()=>a,()=>c);return t.onMessage(m=>{var S;const p=Ur(m);if(p){if(p.type==="e2ee_ready"){if(c)return;c=!0,(S=o.onReady)==null||S.call(o);return}if(p.type==="e2ee_app"){d.handleAppFrame(p);return}p.type==="e2ee_bin"&&d.handleBinaryFrame(p)}}),t.send(JSON.stringify({type:"e2ee_hello",ephemeralPublicKey:_n(r.publicKey)})),{role:"client",attachment:n,get ready(){return c},send:d.send,sendBinary:d.sendBinary,close:d.close}}const Jr=16;function Yr(e){const t=we.hash(e);let n="";for(let s=0;s<Jr;s++)n+=t[s].toString(16).padStart(2,"0");return n}function Qr(e){const t=e.useTls?"wss":"ws",n=e.endpoint.replace(/^(?:https?|wss?):\/\//,"").replace(/\/+$/,"");return`${t}://${n}`}function Zr(e){let t=null,n=null;const s=e.sessionId??Yr(e.daemonPublicKey),o={onMessage:null,onClose:null,onError:null,get isOpen(){return t!==null&&t.readyState===1&&n!==null&&n.ready},connect(r){return new Promise((a,c)=>{const d=e.factory??(()=>new WebSocket(r));try{t=e.factory?d(r):new WebSocket(r)}catch(h){c(h);return}const m=t,p=[],S=[];m.onopen=()=>{m.send(JSON.stringify({type:"relay_register",sessionId:s})),n=Xr({transport:{send:h=>m.send(h),onMessage:h=>p.push(h),onClose:h=>S.push(h),close:()=>m.close()},attachment:{sessionId:s},daemonPublicKey:e.daemonPublicKey,events:{onReady:()=>a(),onMessage:h=>{var x;return(x=o.onMessage)==null?void 0:x.call(o,h)},onBinaryMessage:h=>{var x;return(x=o.onMessage)==null?void 0:x.call(o,h.slice().buffer)},onAuthError:h=>{var x;return(x=o.onError)==null?void 0:x.call(o,h)}}})},m.onerror=h=>{var x;m.readyState!==1&&c(h),(x=o.onError)==null||x.call(o,h)},m.onclose=h=>{var x;for(const g of S)g(ft(h.reason));(x=o.onClose)==null||x.call(o,h.code,ft(h.reason))},m.onmessage=h=>{const{data:x}=h;if(typeof x=="string")for(const g of p)g(x)}})},sendText(r){if(!n||!n.ready)throw new Error("relay transport: cannot send before the E2EE handshake completes");n.send(r)},sendBinary(r){if(!n||!n.ready)throw new Error("relay transport: cannot send before the E2EE handshake completes");n.sendBinary(r)},close(r=1e3,a=""){n==null||n.close(),n=null,t==null||t.close(r,a),t=null}};return o}function ea(e){const t=e.indexOf("#"),n=t===-1?e:e.slice(t+1),s=new URLSearchParams(n),o=s.get("offer");if(!o)return null;let r;try{r=yn(o)}catch{return null}const a=s.get("relay");if(a){const d={endpoint:a,useTls:s.get("relayTls")==="1"};return{publicKeyB64:o,publicKey:r,relay:d}}const c=s.get("host")??void 0;return{publicKeyB64:o,publicKey:r,...c?{host:c}:{}}}const ta=1;function pt(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`id-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,10)}`}class na extends Error{constructor(t,n,s){super(t),this.requestId=n,this.code=s,this.name="RpcError"}}class Et extends Error{constructor(t){super(`RPC ${t} timed out`),this.requestId=t,this.name="RpcTimeoutError"}}class sa{constructor(t){I(this,"transport");I(this,"rpcTimeoutMs");I(this,"now");I(this,"_state","idle");I(this,"_serverId",null);I(this,"_features",{});I(this,"_serverCapabilities",{});I(this,"pending",new Map);I(this,"sessionHandlers",new Set);I(this,"terminalHandlers",new Set);I(this,"fileTransferHandlers",new Set);I(this,"stateHandlers",new Set);I(this,"helloResolve",null);I(this,"helloReject",null);this.options=t,this.transport=t.transport??gn(),this.rpcTimeoutMs=t.rpcTimeoutMs??3e4,this.now=t.now??(()=>Date.now()),this.transport.onMessage=n=>this.handleIncoming(n),this.transport.onClose=(n,s)=>this.handleClose(n,s),this.transport.onError=()=>{}}get state(){return this._state}get serverId(){return this._serverId}get features(){return this._features}get serverCapabilities(){return this._serverCapabilities}hasFeature(t){return!!this._features[t]}async connect(){this.setState("connecting"),await this.transport.connect(this.options.url);const t=new Promise((s,o)=>{this.helloResolve=s,this.helloReject=o});this.transport.sendText(JSON.stringify({type:"hello",clientId:this.options.clientId,clientType:this.options.clientType,protocolVersion:this.options.protocolVersion??ta,appVersion:this.options.appVersion,capabilities:this.options.capabilities}));const n=await t;return this._serverId=n.serverId,this._features=n.features,this._serverCapabilities=n.capabilities,this.setState("open"),n}close(t=1e3,n=""){this.setState("closing"),this.transport.close(t,n)}request(t,n={},s){const o=n.requestId??pt();return new Promise((r,a)=>{const c=s??this.rpcTimeoutMs,d=c>0?setTimeout(()=>{this.pending.delete(o),a(new Et(o))},c):null;this.pending.set(o,{resolve:m=>r(m),reject:a,timer:d}),this.sendSession({type:t,...n,requestId:o})})}sendSession(t){this.transport.sendText(JSON.stringify({type:"session",message:t}))}sendBinary(t){this.transport.sendBinary(t)}ping(t=1e4){const n=pt();return new Promise((s,o)=>{const r=t>0?setTimeout(()=>{this.pending.delete(n),o(new Et(n))},t):null;this.pending.set(n,{resolve:()=>s(),reject:o,timer:r}),this.transport.sendText(JSON.stringify({type:"ping",requestId:n,clientSentAt:this.now()}))})}onSessionMessage(t){return this.sessionHandlers.add(t),()=>this.sessionHandlers.delete(t)}onTerminalFrame(t){return this.terminalHandlers.add(t),()=>this.terminalHandlers.delete(t)}onFileTransferFrame(t){return this.fileTransferHandlers.add(t),()=>this.fileTransferHandlers.delete(t)}onStateChange(t){return this.stateHandlers.add(t),()=>this.stateHandlers.delete(t)}setState(t){if(this._state!==t){this._state=t;for(const n of this.stateHandlers)n(t)}}handleClose(t,n){this.helloReject&&(this.helloReject(new Error(`socket closed during handshake: ${n}`)),this.helloResolve=null,this.helloReject=null);for(const[,s]of this.pending)s.timer&&clearTimeout(s.timer),s.reject(new Error(`socket closed: ${n}`));this.pending.clear(),this.setState("closed")}handleIncoming(t){if(typeof t=="string"){this.handleTextFrame(t);return}if(t instanceof ArrayBuffer){this.handleBinaryFrame(new Uint8Array(t));return}typeof t.arrayBuffer=="function"&&t.arrayBuffer().then(n=>this.handleBinaryFrame(new Uint8Array(n)))}handleBinaryFrame(t){const n=t[0];if(n!==void 0&&Object.values(be).includes(n)){const r=Fr(t);if(!r)return;for(const a of this.fileTransferHandlers)a(r);return}const o=$r(t);for(const r of this.terminalHandlers)r(o)}handleTextFrame(t){let n;try{n=JSON.parse(t)}catch{return}const s=n;switch(s.type){case"status":this.handleStatus(s);return;case"pong":this.resolvePending(s.requestId,void 0);return;case"ping":this.transport.sendText(JSON.stringify({type:"pong",requestId:s.requestId,serverReceivedAt:this.now(),serverSentAt:this.now()}));return;case"session":this.handleSession(s.message);return;default:return}}handleStatus(t){const n=t.payload;(n==null?void 0:n.status)==="server_info"&&this.helloResolve&&(this.helloResolve(n),this.helloResolve=null,this.helloReject=null)}handleSession(t){if(!t||typeof t!="object")return;const n=t;if(n.type==="rpc_error"&&typeof n.requestId=="string"){const s=this.pending.get(n.requestId);s&&(s.timer&&clearTimeout(s.timer),this.pending.delete(n.requestId),s.reject(new na(n.message??n.error??"RPC failed",n.requestId,n.code)));return}if(typeof n.requestId=="string"&&this.pending.has(n.requestId)){const s="payload"in n?n.payload:n;this.resolvePending(n.requestId,s)}for(const s of this.sessionHandlers)s(t)}resolvePending(t,n){const s=this.pending.get(t);s&&(s.timer&&clearTimeout(s.timer),this.pending.delete(t),s.resolve(n))}}class oa{constructor(t){this.daemon=t}get connection(){return this.daemon}async createAgent(t){return await this.daemon.request("create_agent_request",{...t})}agent(t){return new aa(this.daemon,t)}workspace(t){return new ia(this.daemon,t)}get providers(){return new ca(this.daemon)}onAgentUpdate(t){return this.daemon.onSessionMessage(n=>{n.type==="agent_update"&&t(n)})}onWorkspaceUpdate(t){return this.daemon.onSessionMessage(n=>{n.type==="workspace_update"&&t(n)})}}class ra{constructor(t,n){this.daemon=t,this.agentId=n}fetch(t={}){return this.daemon.request("fetch_agent_timeline_request",{agentId:this.agentId,cursor:t.cursor,direction:t.direction,limit:t.limit})}subscribe(t){return this.daemon.onSessionMessage(n=>{const s=n;s.type==="agent_stream"&&s.agentId===this.agentId&&s.event&&t(s.event)})}}class aa{constructor(t,n){I(this,"timeline");this.daemon=t,this.agentId=n,this.timeline=new ra(t,n)}send(t,n={}){return this.daemon.request("send_agent_prompt",{agentId:this.agentId,prompt:t,clientMessageId:n.clientMessageId,images:n.images})}run(t,n){return this.send(t,n)}interrupt(){return this.daemon.request("interrupt_agent",{agentId:this.agentId})}steer(t,n={}){return this.daemon.request("steer_agent_request",{agentId:this.agentId,message:t,clientMessageId:n.clientMessageId,images:n.images})}followUp(t,n={}){return this.daemon.request("follow_up_agent_request",{agentId:this.agentId,message:t,clientMessageId:n.clientMessageId,images:n.images})}update(t){return this.daemon.request("update_agent",{agentId:this.agentId,...t})}resume(){return this.daemon.request("resume_agent",{agentId:this.agentId})}archive(){return this.daemon.request("archive_agent",{agentId:this.agentId})}delete(){return this.daemon.request("delete_agent",{agentId:this.agentId})}onUpdate(t){return this.daemon.onSessionMessage(n=>{const s=n;s.type==="agent_update"&&s.agentId===this.agentId&&t(s)})}sessionStats(){return this.daemon.request("agent_session_stats_request",{agentId:this.agentId})}compact(t){return this.daemon.request("agent_compact_request",{agentId:this.agentId,customInstructions:t})}newSession(){return this.daemon.request("agent_new_session_request",{agentId:this.agentId})}switchSession(t){return this.daemon.request("agent_switch_session_request",{agentId:this.agentId,sessionPath:t})}fork(t){return this.daemon.request("agent_fork_request",{agentId:this.agentId,entryId:t})}forkMessages(){return this.daemon.request("agent_fork_messages_request",{agentId:this.agentId})}clone(){return this.daemon.request("agent_clone_request",{agentId:this.agentId})}setSessionName(t){return this.daemon.request("agent_set_session_name_request",{agentId:this.agentId,name:t})}exportHtml(t){return this.daemon.request("agent_export_html_request",{agentId:this.agentId,outputPath:t})}setModel(t,n){return this.daemon.request("agent_set_model_request",{agentId:this.agentId,provider:t,modelId:n})}cycleModel(){return this.daemon.request("agent_cycle_model_request",{agentId:this.agentId})}lastAssistantText(){return this.daemon.request("agent_last_assistant_text_request",{agentId:this.agentId})}listCommands(){return this.daemon.request("agent_list_commands_request",{agentId:this.agentId})}}class ia{constructor(t,n){this.daemon=t,this.workspaceId=n}onUpdate(t){return this.daemon.onSessionMessage(n=>{const s=n;s.type==="workspace_update"&&s.workspaceId===this.workspaceId&&t(s)})}}class ca{constructor(t){this.daemon=t}listProviders(){return this.daemon.request("list_providers",{})}listModels(t){return this.daemon.request("list_provider_models",{provider:t})}listModes(t){return this.daemon.request("list_provider_modes",{provider:t})}resolveDefaultModel(t,n){return this.daemon.request("resolve_default_model",{provider:t,...n?{cwd:n}:{}})}refreshSnapshot(){return this.daemon.request("providers.snapshot.refresh.request",{})}}const Nt=32*1024;function la(e){const t=e.reduce((o,r)=>o+r.length,0),n=new Uint8Array(t);let s=0;for(const o of e)n.set(o,s),s+=o.length;return n}class da{constructor(t){I(this,"pending",new Map);I(this,"nextStream",1);I(this,"detach",null);this.daemon=t}start(){this.detach||(this.detach=this.daemon.onFileTransferFrame(t=>this.dispatch(t)))}stop(){var t;(t=this.detach)==null||t.call(this),this.detach=null;for(const[n,s]of this.pending)s.reject(new Error("file transfer router stopped")),this.pending.delete(n)}async download(t){const n=await this.daemon.request("file_download_token_request",{path:t});if(!n.ok||!n.token)throw new Error(n.error??"failed to issue download token");const s=this.nextStream++,o=new Promise((a,c)=>{this.pending.set(s,{chunks:[],meta:{},resolve:a,reject:c})}),r=await this.daemon.request("file_download_request",{token:n.token,stream:s});if(!r.ok)throw this.pending.delete(s),new Error(r.error??"download request failed");return o}async upload(t,n){const s=pt(),o=await this.daemon.request("file_upload_request",{path:t,transferId:s});if(!o.ok||o.stream===void 0)throw new Error(o.error??"upload request failed");const r=o.stream;this.daemon.sendBinary(st({opcode:"Begin",stream:r,meta:{transferId:s}}));for(let a=0;a<n.length;a+=Nt){const c=n.subarray(a,a+Nt);this.daemon.sendBinary(st({opcode:"Chunk",stream:r,data:c}))}this.daemon.sendBinary(st({opcode:"End",stream:r,ok:!0}))}dispatch(t){const n=this.pending.get(t.stream);if(n)switch(t.opcode){case"Begin":n.meta.fileName=t.meta.fileName,n.meta.mimeType=t.meta.mimeType;return;case"Chunk":n.chunks.push(t.data);return;case"End":this.pending.delete(t.stream),t.ok?n.resolve({bytes:la(n.chunks),...n.meta}):n.reject(new Error("download failed"));return;case"Error":this.pending.delete(t.stream),n.reject(new Error(t.message));return}}}class ua{constructor(t,n={}){I(this,"initialDelayMs");I(this,"maxDelayMs");I(this,"factor");I(this,"jitter");I(this,"maxAttempts");I(this,"setTimer");I(this,"clearTimer");I(this,"random");I(this,"attempt",0);I(this,"timer",null);I(this,"active",!1);I(this,"detachState",null);I(this,"reconnectedHandlers",new Set);I(this,"failedHandlers",new Set);this.daemon=t,this.initialDelayMs=n.initialDelayMs??500,this.maxDelayMs=n.maxDelayMs??3e4,this.factor=n.factor??2,this.jitter=n.jitter??.2,this.maxAttempts=n.maxAttempts??Number.POSITIVE_INFINITY,this.setTimer=n.setTimer??((s,o)=>setTimeout(s,o)),this.clearTimer=n.clearTimer??(s=>clearTimeout(s)),this.random=n.random??Math.random}delayForAttempt(t){const n=Math.min(this.maxDelayMs,this.initialDelayMs*this.factor**(t-1)),s=n*this.jitter*(this.random()*2-1);return Math.max(0,Math.round(n+s))}onReconnected(t){return this.reconnectedHandlers.add(t),()=>this.reconnectedHandlers.delete(t)}onReconnectFailed(t){return this.failedHandlers.add(t),()=>this.failedHandlers.delete(t)}start(){this.active||(this.active=!0,this.detachState=this.daemon.onStateChange(t=>{t==="closed"&&this.active&&this.scheduleReconnect(),t==="open"&&(this.attempt=0)}))}stop(){var t;this.active=!1,this.timer&&this.clearTimer(this.timer),this.timer=null,(t=this.detachState)==null||t.call(this),this.detachState=null}get attemptCount(){return this.attempt}scheduleReconnect(){if(!this.active||this.attempt>=this.maxAttempts)return;this.attempt+=1;const t=this.delayForAttempt(this.attempt);this.timer=this.setTimer(()=>{this.timer=null,this.tryReconnect()},t)}async tryReconnect(){if(!this.active)return;const t=this.attempt;try{await this.daemon.connect();for(const n of this.reconnectedHandlers)n({attempt:t,serverId:this.daemon.serverId})}catch(n){for(const s of this.failedHandlers)s(n,t);this.scheduleReconnect()}}}function Rt(e){const t=e.trim();if(t==="")return t;const n=t.match(/^([a-z][a-z0-9+.-]*):\/\//i);if(!n)return`ws://${t.replace(/\/+$/,"")}`;const s=n[1].toLowerCase(),o=t.slice(n[0].length).replace(/\/+$/,"");switch(s){case"http":return`ws://${o}`;case"https":return`wss://${o}`;case"ws":case"wss":return`${s}://${o}`;default:return t}}function fa(e){const t=ea(e);return t!=null&&t.relay?{mode:"relay",url:Qr(t.relay),daemonPublicKey:t.publicKey}:t!=null&&t.host?{mode:"direct",url:Rt(t.host)}:{mode:"direct",url:Rt(e)}}const pa=32,z=new Map;function ha(){for(;z.size>pa;){let e=null;for(const[t,n]of z)if(n.refs===0&&n.entry){URL.revokeObjectURL(n.entry.objectUrl),e=t;break}if(e===null)return;z.delete(e)}}function Cu(e,t){const n=z.get(e);if(n)return n.refs+=1,z.delete(e),z.set(e,n),n.entry?Promise.resolve(n.entry):n.pending;const s={refs:1,entry:null,pending:null};return z.set(e,s),ha(),s.pending=t(e).then(o=>{const r=new Blob([Uint8Array.from(o.bytes)],{type:o.mimeType||"application/octet-stream"}),a={objectUrl:URL.createObjectURL(r),mimeType:o.mimeType};return z.get(e)===s?(s.entry=a,s.pending=null):URL.revokeObjectURL(a.objectUrl),a}).catch(o=>{throw z.get(e)===s&&z.delete(e),o}),s.pending}function Tu(e){const t=z.get(e);t&&(t.refs=Math.max(0,t.refs-1))}function ma(){for(const e of z.values())e.entry&&URL.revokeObjectURL(e.entry.objectUrl);z.clear()}function ga(){return"web-"+Math.random().toString(36).slice(2,10)}const j=me()((e,t)=>({status:"idle",serverInfo:null,error:null,daemon:null,client:null,reconnection:null,async connect(n){t().disconnect();const s=fa(n.url);let o;s.mode==="relay"?o=Zr({daemonPublicKey:s.daemonPublicKey}):n.password&&(o=gn(d=>new WebSocket(d,[`pi-studio.bearer.${n.password}`])));const r=new sa({url:s.url,clientId:n.clientId??ga(),clientType:"browser",capabilities:{[Rr.inline_image_markdown]:!0},transport:o,rpcTimeoutMs:1800*1e3}),a=new oa(r),c=new ua(r);r.onStateChange(d=>e({status:d})),c.onReconnected(()=>e({error:null})),c.onReconnectFailed(d=>{e({error:d instanceof Error?d.message:String(d)})}),e({daemon:r,client:a,reconnection:c,error:null,status:"connecting"});try{const d=await r.connect();e({serverInfo:d}),c.start()}catch(d){throw e({error:d instanceof Error?d.message:String(d)}),d}},disconnect(){const{daemon:n,reconnection:s}=t();s==null||s.stop(),n==null||n.close(),ma(),e({status:"idle",serverInfo:null,daemon:null,client:null,reconnection:null})}})),_a=100,ya=480,ba=220,wa=280;function Le(e){return Math.min(ya,Math.max(_a,e))}const k=me()(e=>({host:"ws://127.0.0.1:6767",password:"",cwd:"~",cwdPickerOpen:!1,sessionMenu:null,fileMenu:null,rightSidebarTab:"files",collapsedWorkspaces:new Set,leftSidebarCollapsed:!1,rightSidebarCollapsed:!1,leftSidebarWidth:ba,rightSidebarWidth:wa,setHost:t=>e({host:t}),setPassword:t=>e({password:t}),setCwd:t=>e({cwd:t}),openCwdPicker:()=>e({cwdPickerOpen:!0}),closeCwdPicker:()=>e({cwdPickerOpen:!1}),openSessionMenu:(t,n,s)=>e({sessionMenu:{sessionId:t,x:n,y:s}}),closeSessionMenu:()=>e({sessionMenu:null}),openFileMenu:(t,n,s,o)=>e({fileMenu:{path:t,isDirectory:n,x:s,y:o}}),closeFileMenu:()=>e({fileMenu:null}),setRightSidebarTab:t=>e({rightSidebarTab:t}),toggleWorkspaceCollapsed:t=>e(n=>{const s=new Set(n.collapsedWorkspaces);return s.has(t)?s.delete(t):s.add(t),{collapsedWorkspaces:s}}),toggleLeftSidebar:()=>e(t=>({leftSidebarCollapsed:!t.leftSidebarCollapsed})),toggleRightSidebar:()=>e(t=>({rightSidebarCollapsed:!t.rightSidebarCollapsed})),setLeftSidebarWidth:t=>e({leftSidebarWidth:Le(t)}),setRightSidebarWidth:t=>e({rightSidebarWidth:Le(t)}),resizeLeftSidebar:t=>e(n=>({leftSidebarWidth:Le(n.leftSidebarWidth+t)})),resizeRightSidebar:t=>e(n=>({rightSidebarWidth:Le(n.rightSidebarWidth+t)}))})),Sa="_wrap_emmus_1",xa="_dot_emmus_8",va="_open_emmus_15",ka="_connecting_emmus_18",Ia="_closed_emmus_21",ja="_idle_emmus_22",Ca="_closing_emmus_23",ot={wrap:Sa,dot:xa,open:va,connecting:ka,closed:Ia,idle:ja,closing:Ca},Ta={idle:"disconnected",connecting:"connecting…",open:"connected",closing:"disconnecting…",closed:"disconnected"};function Ea(){const e=j(n=>n.status),t=j(n=>n.error);return i.jsxs("span",{className:ot.wrap,title:t??void 0,children:[i.jsx("span",{className:$(ot.dot,ot[e])}),Ta[e]??e]})}const Na="_toolbar_i9hmh_1",Ra="_brand_i9hmh_10",Ma="_version_i9hmh_16",qa="_sep_i9hmh_22",Ba="_host_i9hmh_28",Aa="_password_i9hmh_31",$a="_sidebarToggles_i9hmh_34",ue={toolbar:Na,brand:Ra,version:Ma,sep:qa,host:Ba,password:Aa,sidebarToggles:$a};function Da(){const e=j(h=>h.status),t=j(h=>h.connect),n=j(h=>h.disconnect),s=k(h=>h.host),o=k(h=>h.setHost),r=k(h=>h.password),a=k(h=>h.setPassword),c=k(h=>h.leftSidebarCollapsed),d=k(h=>h.toggleLeftSidebar),m=k(h=>h.rightSidebarCollapsed),p=k(h=>h.toggleRightSidebar),S=e==="open"||e==="connecting";return i.jsxs("div",{className:ue.toolbar,children:[i.jsx("span",{className:ue.brand,children:"Pi-Studio"}),i.jsxs("span",{className:ue.version,children:["v","0.0.46"]}),i.jsx(Ea,{}),i.jsx("div",{className:ue.sep}),i.jsx(Ve,{className:ue.host,value:s,onChange:h=>o(h.target.value),placeholder:"host:port, ws://…/http://…, or a pasted pairing link",disabled:S}),i.jsx(Ve,{className:ue.password,type:"password",value:r,onChange:h=>a(h.target.value),placeholder:"password",disabled:S}),e==="open"||e==="connecting"||e==="closing"?i.jsx(O,{size:"sm",onClick:()=>n(),children:"Disconnect"}):i.jsx(O,{size:"sm",variant:"default",onClick:()=>void t({url:s,password:r||void 0}),children:"Connect"}),i.jsxs("div",{className:ue.sidebarToggles,children:[i.jsx(O,{size:"sm",variant:"ghost",iconOnly:!0,title:c?"Show sessions sidebar":"Hide sessions sidebar",onClick:()=>d(),children:c?i.jsx(Ln,{size:16}):i.jsx(Hn,{size:16})}),i.jsx(O,{size:"sm",variant:"ghost",iconOnly:!0,title:m?"Show files/changes sidebar":"Hide files/changes sidebar",onClick:()=>p(),children:m?i.jsx(Wn,{size:16}):i.jsx(Un,{size:16})})]})]})}const ht={rows:[],streamingAssistantIndex:null,streamingReasoningIndex:null,toolIndexByCallId:{}};let Mt=0;function ie(){return Mt+=1,`row-${Mt}`}function Oa(e){switch(e.kind){case"shell":return e.command??"";case"read":case"write":return e.path??"";case"edit":return e.path??"";case"search":return e.query??"";case"fetch":return e.url??"";case"task":return e.description??"";default:return""}}function Pa(e,t){return e??`${t.kind}:${Oa(t)}`}function Fa(e){return{...e,streamingAssistantIndex:null,streamingReasoningIndex:null}}function za(e,t){var r;const n=e.rows.slice();let s=e.streamingAssistantIndex;if(s===null||((r=n[s])==null?void 0:r.kind)!=="assistant"){const a={kind:"assistant",id:ie(),text:"",streaming:!0};n.push(a),s=n.length-1}const o=n[s];return!o||o.kind!=="assistant"?e:(n[s]={...o,text:o.text+t},{...e,rows:n,streamingAssistantIndex:s,streamingReasoningIndex:null})}function La(e,t){var r;const n=e.rows.slice();let s=e.streamingReasoningIndex;if(s===null||((r=n[s])==null?void 0:r.kind)!=="reasoning"){const a={kind:"reasoning",id:ie(),text:"",streaming:!0};n.push(a),s=n.length-1}const o=n[s];return!o||o.kind!=="reasoning"?e:(n[s]={...o,text:o.text+t},{...e,rows:n,streamingReasoningIndex:s,streamingAssistantIndex:null})}function Ha(e,t){if(e.kind!==t.kind)return t;const n={...e};for(const[s,o]of Object.entries(t))o!==void 0&&o!==""&&(n[s]=o);return n}function Wa(e,t){var c;const n=Pa(t.callId,t.tool),s=t.status==="completed"||t.status==="error"?t.status:"running",o=e.toolIndexByCallId[n],r=e.rows.slice();if(o!==void 0&&((c=r[o])==null?void 0:c.kind)==="tool"){const d=r[o];return d.kind!=="tool"?e:(r[o]={...d,tool:Ha(d.tool,t.tool),status:s},{...e,rows:r,streamingAssistantIndex:null,streamingReasoningIndex:null})}const a={kind:"tool",id:ie(),callId:n,tool:t.tool,status:s};return r.push(a),{...e,rows:r,toolIndexByCallId:{...e.toolIndexByCallId,[n]:r.length-1},streamingAssistantIndex:null,streamingReasoningIndex:null}}function bt(e){const t=e.rows.slice();if(e.streamingAssistantIndex!==null){const n=t[e.streamingAssistantIndex];(n==null?void 0:n.kind)==="assistant"&&(t[e.streamingAssistantIndex]={...n,streaming:!1})}if(e.streamingReasoningIndex!==null){const n=t[e.streamingReasoningIndex];(n==null?void 0:n.kind)==="reasoning"&&(t[e.streamingReasoningIndex]={...n,streaming:!1})}return{...e,rows:t,streamingAssistantIndex:null,streamingReasoningIndex:null}}function Ua(e,t){const n=bt(e),s={kind:"error",id:ie(),text:t||"turn failed"};return{...n,rows:[...n.rows,s]}}function Ka(e){const t=bt(e),n={kind:"system",id:ie(),text:"(canceled)"};return{...t,rows:[...t.rows,n]}}function Va(e,t){const n={kind:"error",id:ie(),text:t||"error"};return{...e,rows:[...e.rows,n]}}function Ga(e,t,n,s){if(s!==void 0){const r=e.rows.findIndex(a=>a.kind==="user"&&a.pending&&a.clientMessageId===s);if(r!==-1){const a=e.rows.slice(),c=a[r];if((c==null?void 0:c.kind)==="user")return a[r]={...c,text:t,images:n,pending:!1},{...e,rows:a}}}const o={kind:"user",id:ie(),text:t,images:n,clientMessageId:s};return{...e,rows:[...e.rows,o]}}function Xa(e,t){let n=!1;const s=e.rows.map(o=>o.kind!=="user"||!o.queued||t.includes(o.text)?o:(n=!0,{...o,queued:!1}));return n?{...e,rows:s}:e}function Sn(e,t){switch(t.kind){case"turn_started":return Fa(e);case"user_message":return Ga(e,t.text??"",t.images,t.messageId);case"assistant_message":return za(e,t.text??"");case"reasoning":return La(e,t.text??"");case"tool_call":return Wa(e,t);case"turn_completed":return bt(e);case"turn_failed":return Ua(e,t.error);case"turn_canceled":return Ka(e);case"error":return Va(e,t.message);case"queue_update":return Xa(e,t.steering??[]);default:return e}}function Ja(e,t,n,s,o){const r={kind:"user",id:ie(),text:n,images:s,clientMessageId:t,pending:!0,queued:o};return{...e,rows:[...e.rows,r]}}function Ya(e,t){const n=e.rows.findIndex(r=>r.kind==="user"&&r.pending&&r.clientMessageId===t);if(n===-1)return e;const s=e.rows.slice(),o=s[n];return(o==null?void 0:o.kind)!=="user"?e:(s[n]={...o,pending:!1,failed:!0},{...e,rows:s})}let qt=0;function Qa(){return qt+=1,`s-${Date.now().toString(36)}-${qt}`}const T=me()((e,t)=>({sessions:{},order:[],activeSessionId:null,createSession(n){const s=Qa(),o={id:s,agentId:null,title:"New chat",status:"idle",cwd:n,timeline:ht,userMessageCount:0};return e(r=>({sessions:{...r.sessions,[s]:o},order:[s,...r.order],activeSessionId:s})),s},hydrate(n){e(s=>s.sessions[n.id]?s:{sessions:{...s.sessions,[n.id]:n},order:[...s.order,n.id]})},bindAgent(n,s){e(o=>{const r=o.sessions[n];return r?{sessions:{...o.sessions,[n]:{...r,agentId:s}}}:o})},setStatus(n,s){e(o=>{const r=o.sessions[n];return r?{sessions:{...o.sessions,[n]:{...r,status:s}}}:o})},setStatusByAgentId(n,s){const o=t().findByAgentId(n);o&&t().setStatus(o.id,s)},setModel(n,s,o){e(r=>{const a=r.sessions[n];return a?{sessions:{...r.sessions,[n]:{...a,model:s,modelProvider:o}}}:r})},setModelByAgentId(n,s,o){const r=t().findByAgentId(n);r&&t().setModel(r.id,s,o)},setTitle(n,s){e(o=>{const r=o.sessions[n];return r?{sessions:{...o.sessions,[n]:{...r,title:s}}}:o})},setCwd(n,s){e(o=>{const r=o.sessions[n];return r?{sessions:{...o.sessions,[n]:{...r,cwd:s}}}:o})},applyStreamEvent(n,s){e(o=>{const r=o.sessions[n];if(!r)return o;const a=Sn(r.timeline,s);let c=r.title;if(s.kind==="turn_completed"&&c==="New chat"){const d=a.rows.slice().reverse().find(m=>m.kind==="assistant");d&&d.kind==="assistant"&&d.text&&(c=d.text.slice(0,40)+(d.text.length>40?"…":""))}return{sessions:{...o.sessions,[n]:{...r,timeline:a,title:c}}}})},addOptimisticUserMessage(n,s,o,r,a){e(c=>{const d=c.sessions[n];if(!d)return c;const m=Ja(d.timeline,s,o,r,a);return{sessions:{...c.sessions,[n]:{...d,timeline:m,userMessageCount:d.userMessageCount+1}}}})},markUserMessageFailed(n,s){e(o=>{const r=o.sessions[n];if(!r)return o;const a=Ya(r.timeline,s);return{sessions:{...o.sessions,[n]:{...r,timeline:a}}}})},activate(n){e({activeSessionId:n})},remove(n){e(s=>{const{[n]:o,...r}=s.sessions,a=s.order.filter(d=>d!==n),c=s.activeSessionId===n?a[0]??null:s.activeSessionId;return{sessions:r,order:a,activeSessionId:c}})},findByAgentId(n){return Object.values(t().sessions).find(s=>s.agentId===n)}})),Bt=new WeakMap;function Za(e,t="pi"){const n=Bt.get(e);if(n)return n;const s=e.providers.resolveDefaultModel(t).then(o=>({model:o.model,modelProvider:o.modelProvider})).catch(()=>({}));return Bt.set(e,s),s}const Ke=new Map;function xn(e,t){const n=T.getState().sessions[t];if(!n)return Promise.reject(new Error(`unknown session: ${t}`));if(n.agentId)return Promise.resolve(n.agentId);const s=Ke.get(t);if(s)return s;const o=(async()=>{if(!n.model){const d=await Za(e),m=T.getState().sessions[t];d.model&&m&&!m.model&&T.getState().setModel(t,d.model,d.modelProvider)}const r=T.getState().sessions[t];if(!r)throw new Error(`unknown session: ${t}`);const a={provider:"pi",cwd:r.cwd||"~"};r.model&&(a.model=r.model),r.modelProvider&&(a.modelProvider=r.modelProvider);const c=await e.createAgent({config:a,labels:{}});return T.getState().bindAgent(t,c.agentId),c.agentId})();return Ke.set(t,o),o.finally(()=>Ke.delete(t)),o}async function ei(e,t){var o;const n=T.getState().sessions[t];if(!n||n.userMessageCount>0||n.timeline.rows.length>0)return!1;await((o=Ke.get(t))==null?void 0:o.catch(()=>{}));const s=T.getState().sessions[t];return s?(s.agentId&&e&&await e.agent(s.agentId).delete().catch(()=>{}),T.getState().remove(t),!0):!1}function rt(e,t){return e.filter(n=>n.workspaceCwd===t)}function He(e){(e==null?void 0:e.kind)==="chat"&&T.getState().activate(e.data.sessionId)}const E=me()((e,t)=>({tabs:[],activeTabId:null,activeWorkspaceCwd:null,lastActiveTabByWorkspace:{},open(n){const s=t().tabs.find(o=>o.id===n.id);if(s){t().activate(s.id);return}e(o=>({tabs:[...o.tabs,n],activeTabId:n.id,activeWorkspaceCwd:n.workspaceCwd,lastActiveTabByWorkspace:{...o.lastActiveTabByWorkspace,[n.workspaceCwd]:n.id}})),He(n)},close(n){let s;e(o=>{const r=o.tabs.find(p=>p.id===n);if(!r)return o;const a=o.tabs.findIndex(p=>p.id===n),c=o.tabs.filter(p=>p.id!==n);let d=o.activeTabId;const m={...o.lastActiveTabByWorkspace};if(d===n){const p=rt(c,r.workspaceCwd),S=Math.min(a,p.length-1),h=p[S];d=(h==null?void 0:h.id)??null,s=h}if(m[r.workspaceCwd]===n){const p=rt(c,r.workspaceCwd)[0];p?m[r.workspaceCwd]=p.id:delete m[r.workspaceCwd]}return{tabs:c,activeTabId:d,lastActiveTabByWorkspace:m}}),He(s)},activate(n){let s;e(o=>{const r=o.tabs.find(a=>a.id===n);return r?(s=r,{activeTabId:n,activeWorkspaceCwd:r.workspaceCwd,lastActiveTabByWorkspace:{...o.lastActiveTabByWorkspace,[r.workspaceCwd]:n}}):o}),He(s)},switchWorkspace(n){let s;e(o=>{var m;const r=rt(o.tabs,n),a=o.lastActiveTabByWorkspace[n],d=a&&r.some(p=>p.id===a)?a:((m=r[0])==null?void 0:m.id)??null;return s=r.find(p=>p.id===d),{activeWorkspaceCwd:n,activeTabId:d,lastActiveTabByWorkspace:d?{...o.lastActiveTabByWorkspace,[n]:d}:o.lastActiveTabByWorkspace}}),He(s)},reorder(n,s){e(o=>{const r=o.tabs.findIndex(m=>m.id===n),a=o.tabs.findIndex(m=>m.id===s);if(r===-1||a===-1||r===a)return o;const c=o.tabs.slice(),[d]=c.splice(r,1);return d?(c.splice(a,0,d),{tabs:c}):o})},updateLabel(n,s){e(o=>({tabs:o.tabs.map(r=>r.id===n?{...r,label:s}:r)}))},updateData(n,s){e(o=>({tabs:o.tabs.map(r=>r.id===n?{...r,data:{...r.data,...s}}:r)}))}})),U={chat:e=>`chat-${e}`,file:e=>`file-${e}`,diff:(e,t)=>`diff-${e}${t?"-staged":""}`,terminal:e=>`term-${e}`,molecule:e=>`mol-${e}`};let at=0;function vn(e){at+=1,E.getState().open({id:U.terminal(`new-${at}`),kind:"terminal",label:`Terminal ${at}`,closable:!0,data:{slot:null,cwd:e},workspaceCwd:e})}let it=0;function ti(e){it+=1,E.getState().open({id:U.molecule(`new-${it}`),kind:"molecule",label:`Molecule ${it}`,closable:!0,data:{path:null},workspaceCwd:e})}function wt(e){const t=T.getState().createSession(e);E.getState().open({id:U.chat(t),kind:"chat",label:"New chat",closable:!0,data:{sessionId:t},workspaceCwd:e});const n=j.getState().client;n&&xn(n,t).catch(()=>{})}function mt(e){const t=E.getState().tabs.find(s=>s.id===e);if(E.getState().close(e),(t==null?void 0:t.kind)!=="chat")return;const{sessionId:n}=t.data;ei(j.getState().client,n)}const ae=me()(e=>({rootPath:"",expanded:new Set,expandedByRoot:new Map,draft:null,setRoot:t=>e(n=>{const s=new Map(n.expandedByRoot);n.rootPath&&s.set(n.rootPath,n.expanded);const o=s.get(t),r=new Set(o);return r.add(t),{rootPath:t,expanded:r,expandedByRoot:s,draft:null}}),toggle:t=>e(n=>{if(t===n.rootPath)return n;const s=new Set(n.expanded);s.has(t)?s.delete(t):s.add(t);const o=new Map(n.expandedByRoot);return o.set(n.rootPath,s),{expanded:s,expandedByRoot:o}}),startDraft:(t,n)=>e(s=>{const o=new Set(s.expanded);o.add(t);const r=new Map(s.expandedByRoot);return s.rootPath&&r.set(s.rootPath,o),{draft:{parentPath:t,kind:n},expanded:o,expandedByRoot:r}}),cancelDraft:()=>e({draft:null})}));let ye=null;const gt=new Set;function ni(e){return gt.add(e),()=>gt.delete(e)}async function et(e){var t;if(ye)return ye;try{const n=await e.connection.request("file_explorer_request",{path:"/home"}),o=(((t=n.result)==null?void 0:t.entries)??n.entries??[]).find(r=>r.kind==="directory"||r.type==="directory");ye="/home/"+((o==null?void 0:o.name)||"user")}catch{return"/tmp"}for(const n of gt)n(ye);return ye}function si(){return ye}async function oi(e,t){return t==="~"||t.startsWith("~/")?await et(e)+t.slice(1):t}function St(){const e=j(o=>o.client),t=j(o=>o.status),[n,s]=w.useState(si());return w.useEffect(()=>ni(s),[]),w.useEffect(()=>{!e||t!=="open"||n||et(e)},[e,t,n]),n}const ri="_dot_gfh8u_3",ai="_spinner_gfh8u_16",At={dot:ri,spinner:ai};function ii(e){const{status:t,requiresAttention:n,attentionReason:s,showInactive:o}=e;if(!t)return null;if(n)switch(s){case"permission":return"statusWarning";case"error":return"statusDanger";case"finished":default:return"statusSuccess"}switch(t){case"running":case"queued":return"accent";case"waiting":return"statusWarning";case"finished":return"statusSuccess";case"error":return"statusDanger";case"idle":case"archived":return o?"foregroundMuted":null}}const $t=8,ci={accent:"var(--pi-color-accent)",statusSuccess:"var(--pi-color-statusSuccess)",statusDanger:"var(--pi-color-statusDanger)",statusWarning:"var(--pi-color-statusWarning)",foregroundMuted:"var(--pi-color-foregroundMuted)"};function li({className:e,...t}){const n=ii(t);if(!n)return null;const s=t.status==="running"&&!t.requiresAttention,o=s?"var(--pi-color-accentBright, #a2b4d7)":ci[n]??"currentColor";return i.jsx("span",{className:`${s?At.spinner:At.dot}${e?` ${e}`:""}`,style:s?{borderTopColor:o,borderRightColor:o}:{width:$t,height:$t,backgroundColor:o},role:"presentation"})}const di={initializing:"queued",idle:"idle",running:"running",error:"error",closed:"finished"};function ui(e){return di[e]??"idle"}const fi="_wrap_14der_1",pi="_header_14der_7",hi="_list_14der_23",mi="_item_14der_28",gi="_active_14der_37",_i="_title_14der_41",yi="_meta_14der_50",bi="_menuBtn_14der_58",wi="_workspaceGroup_14der_76",Si="_workspaceHeader_14der_79",xi="_workspaceChevron_14der_92",vi="_workspaceIcon_14der_103",ki="_workspaceLabel_14der_107",Ii="_workspaceCount_14der_113",ji="_newSessionBtn_14der_118",Ci="_workspaceSessions_14der_130",Ti="_emptyState_14der_133",A={wrap:fi,header:pi,list:hi,item:mi,active:gi,title:_i,meta:yi,menuBtn:bi,workspaceGroup:wi,workspaceHeader:Si,workspaceChevron:xi,workspaceIcon:vi,workspaceLabel:ki,workspaceCount:Ii,newSessionBtn:ji,workspaceSessions:Ci,emptyState:Ti};function Ei({session:e,active:t,onSelect:n,onOpenMenu:s}){return i.jsxs("div",{className:$(A.item,t&&A.active),onClick:n,onContextMenu:o=>{o.preventDefault(),s(o.clientX,o.clientY)},children:[i.jsxs("div",{className:A.title,children:[i.jsx(li,{status:ui(e.status),showInactive:!0}),i.jsx("span",{children:e.title})]}),i.jsxs("div",{className:A.meta,children:[e.cwd||"~"," · ",e.agentId?e.agentId.slice(0,8):"new"," ·"," ",e.userMessageCount," msgs"]}),i.jsx("button",{type:"button",className:A.menuBtn,title:"Actions",onClick:o=>{o.stopPropagation();const r=o.currentTarget.getBoundingClientRect();s(r.left,r.bottom)},children:i.jsx(Ut,{size:14})})]})}const Ni="_trigger_42hju_1",Ri="_content_42hju_12",Mi="_item_42hju_21",qi="_danger_42hju_38",Bi="_sep_42hju_41",ee={trigger:Ni,content:Ri,item:Mi,danger:qi,sep:Bi};function Ai(){const e=k(g=>g.sessionMenu),t=k(g=>g.closeSessionMenu),n=j(g=>g.client),s=w.useRef(null),[o,r]=w.useState(!1);w.useEffect(()=>{r(e!==null)},[e]);const a=T(g=>e?g.sessions[e.sessionId]:void 0);if(!e)return null;const c=!!(a!=null&&a.agentId)&&(a==null?void 0:a.status)==="running";function d(g){r(g),g||t()}function m(){if(!a)return;const g=window.prompt("Rename:",a.title);if(g!=null&&g.trim()){const _=g.trim();T.getState().setTitle(a.id,_),E.getState().updateLabel(U.chat(a.id),_),a.agentId&&(n==null||n.agent(a.agentId).update({title:_}).catch(()=>{}))}t()}function p(){a!=null&&a.agentId&&(n==null||n.agent(a.agentId).interrupt().catch(()=>{})),t()}function S(g){T.getState().remove(g),E.getState().close(U.chat(g))}async function h(){if(a){if(!window.confirm("Archive this agent? It can be resumed later."))return t();if(t(),!a.agentId){S(a.id);return}if(!n){window.alert("Not connected — cannot archive remotely.");return}try{await n.agent(a.agentId).archive(),S(a.id)}catch(g){window.alert(`Failed to archive: ${g instanceof Error?g.message:String(g)}`)}}}async function x(){if(a){if(!window.confirm("Permanently delete this agent? This cannot be undone."))return t();if(t(),!a.agentId){S(a.id);return}if(!n){window.alert("Not connected — cannot delete remotely.");return}try{await n.agent(a.agentId).delete(),S(a.id)}catch(g){window.alert(`Failed to delete: ${g instanceof Error?g.message:String(g)}`)}}}return i.jsxs(Xe,{open:o,onOpenChange:d,modal:!1,children:[i.jsx(Je,{asChild:!0,children:i.jsx("button",{ref:s,type:"button",className:ee.trigger,style:{left:e.x,top:e.y},"aria-hidden":!0,tabIndex:-1})}),i.jsx(Ye,{children:i.jsxs(Qe,{className:ee.content,align:"start",sideOffset:2,children:[i.jsx(W,{className:ee.item,onSelect:m,children:"Rename"}),i.jsx(W,{className:ee.item,disabled:!c,onSelect:p,children:"Stop agent"}),i.jsx(lt,{className:ee.sep}),i.jsx(W,{className:`${ee.item} ${ee.danger}`,onSelect:h,children:"Archive"}),i.jsx(W,{className:`${ee.item} ${ee.danger}`,onSelect:x,children:"Delete permanently"})]})})]})}function $i({label:e,cwd:t,sessionCount:n,collapsed:s,onToggleCollapsed:o,onNewSession:r}){return i.jsxs("div",{className:A.workspaceHeader,onClick:o,title:t,children:[i.jsx("button",{type:"button",className:A.workspaceChevron,"aria-label":s?"Expand workspace":"Collapse workspace",onClick:a=>{a.stopPropagation(),o()},children:s?i.jsx(_t,{size:13}):i.jsx(Kt,{size:13})}),i.jsx(Kn,{size:13,className:$(A.workspaceIcon)}),i.jsx("span",{className:A.workspaceLabel,children:e}),i.jsx("span",{className:A.workspaceCount,children:n}),i.jsx(O,{size:"xs",variant:"ghost",iconOnly:!0,title:"New conversation",className:A.newSessionBtn,onClick:a=>{a.stopPropagation(),r()},children:i.jsx(Vt,{size:13})})]})}function Se(e,t){return t&&(e==="~"||e.startsWith("~/"))?t+e.slice(1):e}function kn(e,t,n){const s=new Map;for(const o of e){const r=t[o];if(!r)continue;const a=Se(r.cwd||"~",n),c=s.get(a);c?c.push(r):s.set(a,[r])}return Array.from(s,([o,r])=>({cwd:o,sessions:r}))}function Di(e){return!e||e==="~"?"~":e.split("/").filter(Boolean).pop()||e}function Oi(){const e=T(_=>_.order),t=T(_=>_.sessions),n=T(_=>_.activeSessionId),s=T(_=>_.activate),o=E(_=>_.open),r=j(_=>_.status),a=k(_=>_.openSessionMenu),c=k(_=>_.setCwd),d=k(_=>_.openCwdPicker),m=k(_=>_.collapsedWorkspaces),p=k(_=>_.toggleWorkspaceCollapsed),S=St();function h(_){const y=Se(_||"~",S);wt(y)}function x(_){s(_);const y=t[_],q=Se((y==null?void 0:y.cwd)||"~",S);y!=null&&y.cwd&&c(y.cwd),o({id:U.chat(_),kind:"chat",label:(y==null?void 0:y.title)??"Chat",closable:!0,data:{sessionId:_},workspaceCwd:q})}const g=kn(e,t,S);return i.jsxs("div",{className:A.wrap,children:[i.jsxs("div",{className:A.header,children:[i.jsx("h3",{children:"Workspaces"}),i.jsx(O,{size:"xs",variant:"ghost",iconOnly:!0,title:r!=="open"?"Connect to open a workspace":"Open a workspace folder",disabled:r!=="open",onClick:()=>d(),children:i.jsx(Gt,{size:14})})]}),i.jsxs("div",{className:A.list,children:[g.length===0&&i.jsx("div",{className:A.emptyState,children:r!=="open"?"Not connected":"No workspaces — open a folder to start"}),g.map(_=>{const y=m.has(_.cwd);return i.jsxs("div",{className:A.workspaceGroup,children:[i.jsx($i,{label:Di(_.cwd),cwd:_.cwd,sessionCount:_.sessions.length,collapsed:y,onToggleCollapsed:()=>p(_.cwd),onNewSession:()=>h(_.cwd)}),!y&&i.jsx("div",{className:A.workspaceSessions,children:_.sessions.map(q=>i.jsx(Ei,{session:q,active:q.id===n,onSelect:()=>x(q.id),onOpenMenu:(J,C)=>a(q.id,J,C)},q.id))})]},_.cwd)})]}),i.jsx(Ai,{})]})}const Pi="_strip_8jveh_1",Fi="_tab_8jveh_10",zi="_active_8jveh_26",Li="_icon_8jveh_30",Hi="_label_8jveh_35",Wi="_close_8jveh_40",Ui="_newTab_8jveh_50",Ki="_content_8jveh_73",Vi="_item_8jveh_82",Gi="_itemIcon_8jveh_96",F={strip:Pi,tab:Fi,active:zi,icon:Li,label:Hi,close:Wi,newTab:Ui,content:Ki,item:Vi,itemIcon:Gi},Xi={chat:Yt,file:Gn,diff:Vn,terminal:Jt,molecule:Xt};function Ji({tab:e}){const t=E(p=>p.activeTabId),n=E(p=>p.activate),s=Xi[e.kind],{attributes:o,listeners:r,setNodeRef:a,transform:c,transition:d,isDragging:m}=ms({id:e.id});return i.jsxs("div",{ref:a,className:$(F.tab,e.id===t&&F.active),style:{transform:gs.Transform.toString(c),transition:d,opacity:m?.5:1},onClick:()=>n(e.id),onAuxClick:p=>{p.button===1&&e.closable&&mt(e.id)},title:e.label,...o,...r,children:[i.jsx("span",{className:F.icon,children:i.jsx(s,{size:13})}),i.jsx("span",{className:F.label,children:e.label}),e.closable&&i.jsx("span",{className:F.close,onClick:p=>{p.stopPropagation(),mt(e.id)},children:i.jsx(ct,{size:12})})]})}function Yi({workspaceCwd:e}){const t=e??"~";return i.jsxs(Xe,{children:[i.jsx(Je,{asChild:!0,children:i.jsx("button",{type:"button",className:F.newTab,title:"New tab",disabled:!e,children:i.jsx(Vt,{size:14})})}),i.jsx(Ye,{children:i.jsxs(Qe,{className:F.content,align:"start",sideOffset:4,children:[i.jsxs(W,{className:F.item,onSelect:()=>wt(t),children:[i.jsx(Yt,{size:13,className:F.itemIcon}),"New chat"]}),i.jsxs(W,{className:F.item,onSelect:()=>vn(t),children:[i.jsx(Jt,{size:13,className:F.itemIcon}),"New terminal"]}),i.jsxs(W,{className:F.item,onSelect:()=>ti(t),children:[i.jsx(Xt,{size:13,className:F.itemIcon}),"New molecule view"]})]})})]})}function Qi(){const e=E(a=>a.tabs),t=E(a=>a.activeWorkspaceCwd),n=E(a=>a.reorder),s=e.filter(a=>a.workspaceCwd===t),o=cs(ls(hs,{activationConstraint:{distance:4}}));function r(a){const{active:c,over:d}=a;!d||c.id===d.id||n(String(c.id),String(d.id))}return i.jsxs("div",{className:F.strip,children:[i.jsx(ds,{sensors:o,collisionDetection:us,onDragEnd:r,children:i.jsx(fs,{items:s.map(a=>a.id),strategy:ps,children:s.map(a=>i.jsx(Ji,{tab:a},a.id))})}),i.jsx(Yi,{workspaceCwd:t})]})}const Zi="_spinner_1tgzp_3",ec={spinner:Zi},tc={xs:12,sm:16,md:20,lg:24},nc={xs:1,sm:2,md:2,lg:3};function In({size:e="md",color:t="currentColor",className:n,"aria-label":s="Loading"}){const o=typeof e=="number"?e:tc[e],r=typeof e=="number"?2:nc[e];return i.jsx("span",{className:`${ec.spinner}${n?` ${n}`:""}`,role:"status","aria-label":s,style:{width:o,height:o,borderWidth:r,color:t}})}const sc=w.lazy(()=>oe(()=>import("./ChatPanel-EEteI2qS.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])).then(e=>({default:e.ChatPanel}))),Dt=w.lazy(()=>oe(()=>import("./FilePanel-Dg4iw-OH.js"),__vite__mapDeps([17,1,7,6,5,2,8,18,12,13,19,20,3,14,15])).then(e=>({default:e.FilePanel}))),oc=w.lazy(()=>oe(()=>import("./TerminalPanel-qCs6c5HO.js"),__vite__mapDeps([21,1,22,23,7,6,5,2,3,14,15,24])).then(e=>({default:e.TerminalPanel}))),rc=w.lazy(()=>oe(()=>import("./MoleculeViewerPanel-DS2USK0p.js"),__vite__mapDeps([25,1,26,27,10,11,28,2,18,7,6,5,3,14,15,29])).then(e=>({default:e.MoleculeViewerPanel}))),ac={chat:sc,file:Dt,diff:Dt,terminal:oc,molecule:rc},ic="_area_z7u01_1",cc="_panel_z7u01_7",lc="_active_z7u01_14",dc="_empty_z7u01_17",uc="_emptyStack_z7u01_25",fc="_emptyTitle_z7u01_35",pc="_emptyHint_z7u01_39",te={area:ic,panel:cc,active:lc,empty:dc,emptyStack:uc,emptyTitle:fc,emptyHint:pc};function hc(){const e=E(a=>a.tabs),t=E(a=>a.activeTabId),n=E(a=>a.activeWorkspaceCwd),s=j(a=>a.status),o=k(a=>a.openCwdPicker),r=e.some(a=>a.workspaceCwd===n);return n===null?i.jsx("div",{className:te.area,children:i.jsxs("div",{className:te.emptyStack,children:[i.jsx("div",{className:te.emptyTitle,children:"No workspace open"}),i.jsx("div",{className:te.emptyHint,children:"Open a project folder to start a chat."}),i.jsx(O,{size:"sm",variant:"secondary",leftIcon:i.jsx(Gt,{size:14}),disabled:s!=="open",title:s!=="open"?"Connect to open a workspace":"Open a workspace folder",onClick:()=>o(),children:"Open Workspace"})]})}):r?i.jsx("div",{className:te.area,children:e.map(a=>{const c=ac[a.kind];return i.jsx("div",{className:$(te.panel,a.id===t&&te.active),children:i.jsx(w.Suspense,{fallback:i.jsx(In,{size:"md"}),children:i.jsx(c,{tab:a})})},a.id)})}):i.jsx("div",{className:te.area,children:i.jsx("div",{className:te.empty,children:"No open tabs in this workspace"})})}const ve={fileRead:e=>["file","read",e],fileDownload:e=>["file","download",e],fileText:(e,t)=>["file","text",e,t],fileDiff:(e,t,n)=>["file","diff",e,t,n],fileDiffByPath:e=>["file","diff",e],explorer:e=>["explorer",e],agentList:()=>["agents","list"],providerModels:e=>["providers","models",e],agentTimeline:e=>["agents","timeline",e],agentCommands:e=>["agents","commands",e]};function mc(e){const t=e.kind==="directory"||e.type==="directory"?"directory":"file";return{name:e.name??"",kind:t,size:e.size,mtimeMs:e.mtimeMs}}function gc(e){return e.slice().sort((t,n)=>t.kind!==n.kind?t.kind==="directory"?-1:1:t.name.localeCompare(n.name))}async function jn(e,t){const s=(await e.connection.request("file_explorer_request",{path:t})).result;if(!s||s.ok===!1)throw new Error(s&&"error"in s?s.error:"explorer request failed");const o="entries"in s?s.entries??[]:[];return{path:s.resolvedPath??t,entries:gc(o.map(mc))}}function _c(e,t=!0){const n=j(s=>s.client);return Ht({queryKey:ve.explorer(e),queryFn:()=>{if(!n)throw new Error("not connected");return jn(n,e)},enabled:!!n&&!!e&&t})}function yc(e){const t=j(o=>o.client),n=w.useMemo(()=>Array.from(e),[e]),s=Pn({queries:n.map(o=>({queryKey:ve.explorer(o),queryFn:()=>{if(!t)throw new Error("not connected");return jn(t,o)},enabled:!!t&&!!o}))});return w.useMemo(()=>{const o=new Map;return n.forEach((r,a)=>{const c=s[a];o.set(r,{listing:c==null?void 0:c.data,isLoading:!!(c!=null&&c.isLoading),isError:!!(c!=null&&c.isError),error:c==null?void 0:c.error})}),o},[n,s])}function bc(e){return typeof e=="object"&&e!==null&&"type"in e&&e.type==="file_changed"&&"path"in e&&typeof e.path=="string"}function wc(e,t){const n=new Set,s=e.connection.onSessionMessage(o=>{const r=o;bc(r)&&n.has(r.path)&&t(r.path)});return{sync(o){for(const r of n)o.has(r)||(n.delete(r),e.connection.request("file_watch_unsubscribe",{path:r}).catch(()=>{}));for(const r of o)n.has(r)||(n.add(r),e.connection.request("file_watch_subscribe",{path:r}).then(a=>{const c=a;(c==null?void 0:c.ok)===!1&&console.warn(`useExplorerWatch: ${c.error??"subscribe failed"} for "${r}"`)}).catch(()=>{}))},dispose(){s();for(const o of n)e.connection.request("file_watch_unsubscribe",{path:o}).catch(()=>{});n.clear()}}}function Sc(e){const t=j(a=>a.client),n=xe(),s=w.useRef(null),o=w.useRef(null),r=Array.from(e).toSorted().join("\0");w.useEffect(()=>{var a,c;t&&(o.current!==t&&((a=s.current)==null||a.dispose(),s.current=wc(t,d=>{n.invalidateQueries({queryKey:ve.explorer(d)})}),o.current=t),(c=s.current)==null||c.sync(new Set(r?r.split("\0"):[])))},[t,r,n]),w.useEffect(()=>()=>{var a;(a=s.current)==null||a.dispose(),s.current=null,o.current=null},[])}const Ot=new WeakMap;function Pt(e){let t=Ot.get(e);return t||(t=new da(e),t.start(),Ot.set(e,t)),t}function xc(e,t,n){const s=new Blob([Uint8Array.from(e)],{type:n||"application/octet-stream"}),o=URL.createObjectURL(s),r=document.createElement("a");r.href=o,r.download=t,r.rel="noopener",document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(o),0)}function Cn(){const e=j(o=>o.daemon),t=xe(),n=w.useCallback(async(o,r)=>{if(!e)throw new Error("not connected");const a=o?`${o}/${r.name}`:r.name,c=new Uint8Array(await r.arrayBuffer());await Pt(e).upload(a,c),await t.invalidateQueries({queryKey:["explorer"]})},[e,t]),s=w.useCallback(async o=>{if(!e)throw new Error("not connected");const r=await Pt(e).download(o);xc(r.bytes,r.fileName||o.split("/").pop()||"download",r.mimeType)},[e]);return{upload:n,saveToDisk:s}}const vc=w.lazy(()=>oe(()=>import("./TextViewer-DwAXmSeZ.js"),__vite__mapDeps([30,1,31,2,7,6,5,32,28,19,20,3,14,15,33])).then(e=>({default:e.TextViewer}))),kc=w.lazy(()=>oe(()=>import("./MarkdownFileViewer-DHWrkv5i.js"),__vite__mapDeps([34,1,31,2,7,6,5,32,4,8,9,19,20,3,14,15,35])).then(e=>({default:e.MarkdownFileViewer}))),Ic=w.lazy(()=>oe(()=>import("./ImageViewer-CiKV0OZI.js"),__vite__mapDeps([36,1,28,2,19,20,7,6,5,3,14,15,37])).then(e=>({default:e.ImageViewer}))),jc=w.lazy(()=>oe(()=>import("./VideoViewer-CIgPlKOT.js"),__vite__mapDeps([38,1,28,2,19,20,7,6,5,3,14,15,39])).then(e=>({default:e.VideoViewer}))),Cc=w.lazy(()=>oe(()=>import("./BinaryFallbackViewer-C8VjuRB5.js"),__vite__mapDeps([40,1,28,2,3,7,6,5,14,15,41])).then(e=>({default:e.BinaryFallbackViewer}))),Eu={text:vc,markdown:kc,image:Ic,video:jc,binary:Cc},Tc={md:"markdown",markdown:"markdown",mdx:"markdown",png:"image",jpg:"image",jpeg:"image",gif:"image",webp:"image",svg:"image",bmp:"image",ico:"image",avif:"image",mp4:"video",webm:"video",mov:"video",m4v:"video",ogv:"video",pdf:"binary",zip:"binary",gz:"binary",tar:"binary",exe:"binary",wasm:"binary",woff:"binary",woff2:"binary",ttf:"binary",otf:"binary"},Ec=[["image/","image"],["video/","video"]];function Tn(e){const t=e.split("/").pop()??e,n=t.lastIndexOf(".");return n>0?t.slice(n+1).toLowerCase():""}const Nc={pdb:!0,cif:!0,mmcif:!0,mol:!0,mol2:!0,xyz:!0,extxyz:!0,gro:!0,lammpstrj:!0,xsf:!0},Rc={poscar:!0,contcar:!0};function Mc(e){if(Nc[Tn(e)])return!0;const t=e.split("/").pop()??e;return!!Rc[t.toLowerCase()]}function Nu(e,t={}){const n=Tc[Tn(e)];if(n)return n;const s=t.mimeHint??"";for(const[o,r]of Ec)if(s.startsWith(o))return r;return t.isBinary?"binary":"text"}function qc(e,t){return e.endsWith("/")?`${e}${t}`:`${e}/${t}`}function En(e,t,n,s,o,r){var c;(r==null?void 0:r.parentPath)===t&&e.push({kind:"draft",path:`${t}::draft`,depth:n,draftKind:r.kind,parentPath:t});const a=o.get(t);if(!a||a.isLoading){e.push({kind:"loading",path:t,depth:n});return}if(a.isError){const d=a.error instanceof Error?a.error.message:"failed to load";e.push({kind:"error",path:t,depth:n,message:d});return}for(const d of((c=a.listing)==null?void 0:c.entries)??[])Bc(e,t,d,n,s,o,r)}function Bc(e,t,n,s,o,r,a){const c=qc(t,n.name);if(n.kind!=="directory"){e.push({kind:"file",path:c,name:n.name,depth:s});return}const d=o.has(c);e.push({kind:"directory",path:c,name:n.name,depth:s,expanded:d}),d&&En(e,c,s+1,o,r,a)}function Ac(e,t,n,s){if(!e)return[];const o=[];return En(o,e,0,t,n,s),o}const $c="_container_1098n_1",Dc="_header_1098n_7",Oc="_uploadButton_1098n_14",Pc="_hiddenInput_1098n_33",Fc="_status_1098n_36",zc="_statusError_1098n_37",Lc="_rowAction_1098n_50",Hc="_item_1098n_65",Wc="_dropOverlay_1098n_68",Uc="_list_1098n_83",Kc="_chevronSlot_1098n_102",Vc="_chevron_1098n_102",Gc="_chevronOpen_1098n_113",Xc="_icon_1098n_116",Jc="_name_1098n_123",Yc="_dirName_1098n_128",Qc="_statusRow_1098n_134",Zc="_statusRowError_1098n_145",el="_emptyState_1098n_148",tl="_draftInput_1098n_154",N={container:$c,header:Dc,uploadButton:Oc,hiddenInput:Pc,status:Fc,statusError:zc,rowAction:Lc,item:Hc,dropOverlay:Wc,list:Uc,chevronSlot:Kc,chevron:Vc,chevronOpen:Gc,icon:Xc,name:Jc,dirName:Yc,statusRow:Qc,statusRowError:Zc,emptyState:el,draftInput:tl};function nl({draftKind:e,indentStyle:t,onSubmit:n,onCancel:s}){const[o,r]=w.useState("");return i.jsxs("div",{className:N.item,style:t,onClick:a=>a.stopPropagation(),children:[i.jsx("span",{className:N.chevronSlot}),i.jsx("span",{className:N.icon,children:e==="directory"?i.jsx(Ae,{size:14}):i.jsx(Qt,{size:14})}),i.jsx("input",{className:N.draftInput,autoFocus:!0,spellCheck:!1,placeholder:e==="directory"?"New folder":"New file",value:o,onChange:a=>r(a.target.value),onKeyDown:a=>{if(a.key==="Enter"){a.preventDefault();const c=o.trim();c?n(c):s()}else a.key==="Escape"&&(a.preventDefault(),s())},onBlur:s})]})}const sl=14;function ol({row:e,onToggle:t,onOpenFile:n,onContextMenu:s,onSubmitDraft:o,onCancelDraft:r}){const a={paddingLeft:8+e.depth*sl};if(e.kind==="loading")return i.jsx("div",{className:N.statusRow,style:a,children:"Loading…"});if(e.kind==="error")return i.jsx("div",{className:$(N.statusRow,N.statusRowError),style:a,children:e.message??"failed to load"});if(e.kind==="draft")return i.jsx(nl,{draftKind:e.draftKind,indentStyle:a,onSubmit:d=>o(e.parentPath,d),onCancel:r});const c=e.kind==="directory";return i.jsxs("div",{className:N.item,style:a,onClick:()=>c?t(e.path):n(e.path),onContextMenu:d=>{d.preventDefault(),s(e.path,c,d.clientX,d.clientY)},children:[i.jsx("span",{className:N.chevronSlot,children:c&&i.jsx(_t,{size:12,className:$(N.chevron,e.expanded&&N.chevronOpen)})}),i.jsx("span",{className:N.icon,children:c?i.jsx(Ae,{size:14}):i.jsx(Qt,{size:14})}),i.jsx("span",{className:c?N.dirName:N.name,children:e.name}),i.jsx("button",{type:"button",className:N.rowAction,title:"Actions",onClick:d=>{d.stopPropagation();const m=d.currentTarget.getBoundingClientRect();s(e.path,c,m.left,m.bottom)},children:i.jsx(Ut,{size:12})})]})}const rl="_trigger_ngv1a_1",al="_content_ngv1a_12",il="_item_ngv1a_21",cl="_danger_ngv1a_34",ll="_sep_ngv1a_37",ne={trigger:rl,content:al,item:il,danger:cl,sep:ll};function dl(){const e=k(g=>g.fileMenu),t=k(g=>g.closeFileMenu),n=j(g=>g.client),s=ae(g=>g.startDraft),{saveToDisk:o}=Cn(),r=xe(),a=w.useRef(null),[c,d]=w.useState(!1);if(w.useEffect(()=>{d(e!==null)},[e]),!e)return null;const m=e.path.split("/").pop()||e.path;function p(g){d(g),g||t()}async function S(){if(e){t();try{await o(e.path)}catch(g){window.alert(`Failed to download: ${g instanceof Error?g.message:String(g)}`)}}}async function h(){if(!e)return;const g=e.isDirectory?"folder (and everything inside it)":"file";if(!window.confirm(`Permanently delete this ${g}? "${m}" — this cannot be undone.`))return t();if(t(),!n){window.alert("Not connected — cannot delete remotely.");return}try{const _=await n.connection.request("file_delete_request",{path:e.path});if(!_.ok)throw new Error(_.error??"delete failed");await r.invalidateQueries({queryKey:["explorer"]})}catch(_){window.alert(`Failed to delete: ${_ instanceof Error?_.message:String(_)}`)}}function x(g){if(!e)return;const _=e.isDirectory?e.path:e.path.split("/").slice(0,-1).join("/")||"/";t(),s(_,g)}return i.jsxs(Xe,{open:c,onOpenChange:p,modal:!1,children:[i.jsx(Je,{asChild:!0,children:i.jsx("button",{ref:a,type:"button",className:ne.trigger,style:{left:e.x,top:e.y},"aria-hidden":!0,tabIndex:-1})}),i.jsx(Ye,{children:i.jsxs(Qe,{className:ne.content,align:"start",sideOffset:2,children:[i.jsx(W,{className:ne.item,onSelect:()=>x("file"),children:"New File"}),i.jsx(W,{className:ne.item,onSelect:()=>x("directory"),children:"New Folder"}),i.jsx(lt,{className:ne.sep}),!e.isDirectory&&i.jsxs(i.Fragment,{children:[i.jsx(W,{className:ne.item,onSelect:S,children:"Download"}),i.jsx(lt,{className:ne.sep})]}),i.jsx(W,{className:`${ne.item} ${ne.danger}`,onSelect:h,children:"Delete"})]})})]})}const ul={empty_path:"Choose a folder first.",invalid_name:'Invalid name — cannot be empty or contain "/".',exists:"An item with that name already exists here.",not_found:"That folder no longer exists.",not_a_directory:"That path is not a folder.",unreadable:"That folder is not readable."};async function Nn(e,t,n,s){const o=await e.connection.request("file_create_request",{path:t,name:n,kind:s});if(!o.ok){const r=o.error??"";throw new Error(ul[r]??r??"Failed to create")}return o.path??(t.endsWith("/")?`${t}${n}`:`${t}/${n}`)}const fl=24;function pl(){const e=j(f=>f.client),t=ae(f=>f.rootPath),n=ae(f=>f.expanded),s=ae(f=>f.setRoot),o=ae(f=>f.toggle),r=ae(f=>f.draft),a=ae(f=>f.startDraft),c=ae(f=>f.cancelDraft),d=E(f=>f.activeWorkspaceCwd),m=E(f=>f.open),{upload:p}=Cn(),S=k(f=>f.openFileMenu),h=xe(),[x,g]=w.useState(null),[_,y]=w.useState(!1),[q,J]=w.useState(null),C=w.useRef(null),$e=w.useRef(null),ke=w.useRef(null);w.useEffect(()=>{if(!e)return;const f=d||"~";ke.current!==f&&(ke.current=f,oi(e,f).then(R=>s(R)))},[e,d,s]);const ge=yc(n);Sc(n);const ce=w.useMemo(()=>Ac(t,n,ge,r),[t,n,ge,r]),le=Fn({count:ce.length,getScrollElement:()=>$e.current,estimateSize:()=>fl,overscan:12,getItemKey:f=>{var R;return((R=ce[f])==null?void 0:R.path)??f}});function de(f){if(Mc(f)){m({id:U.molecule(f),kind:"molecule",label:f.split("/").pop()||f,closable:!0,data:{path:f},workspaceCwd:d||"~"});return}m({id:U.file(f),kind:"file",label:f.split("/").pop()||f,closable:!0,data:{path:f},workspaceCwd:d||"~"})}async function tt(f,R){const L=(r==null?void 0:r.kind)??"file";if(c(),!!e)try{const K=await Nn(e,f,R,L);await h.invalidateQueries({queryKey:ve.explorer(f)}),g({text:`Created ${K}`,error:!1}),L==="file"&&de(K)}catch(K){g({text:K instanceof Error?K.message:"Failed to create",error:!0})}}async function Ie(f,R){var Oe;if(!R.length||!f)return;const L=(Oe=ge.get(f))==null?void 0:Oe.listing,K=new Set((L==null?void 0:L.entries.map(Y=>Y.name))??[]),je=R.filter(Y=>K.has(Y.name));if(je.length>0){const Y=je.map(qn=>qn.name).join(", ");if(!window.confirm(`Overwrite ${je.length>1?"these files":"this file"}? ${Y}`))return}g({text:`Uploading ${R.length} file${R.length>1?"s":""}…`,error:!1});try{for(const Y of R)await p(f,Y);g({text:`Uploaded ${R.length} file${R.length>1?"s":""}`,error:!1})}catch(Y){g({text:`Upload failed: ${Y instanceof Error?Y.message:"unknown"}`,error:!0})}}function De(f){f.preventDefault(),y(!1);const R=q??t;J(null);const L=Array.from(f.dataTransfer.files);Ie(R,L)}function nt(f){f.dataTransfer.types.includes("Files")&&(f.preventDefault(),y(!0))}return e?i.jsxs("div",{className:N.container,onDragOver:nt,onDragLeave:()=>y(!1),onDrop:De,children:[i.jsxs("div",{className:N.header,children:[i.jsx("button",{type:"button",className:N.uploadButton,onClick:()=>a(t,"file"),disabled:!t,title:"New file in workspace root",children:i.jsx(Xn,{size:12})}),i.jsx("button",{type:"button",className:N.uploadButton,onClick:()=>a(t,"directory"),disabled:!t,title:"New folder in workspace root",children:i.jsx(Zt,{size:12})}),i.jsxs("button",{type:"button",className:N.uploadButton,onClick:()=>{var f;return(f=C.current)==null?void 0:f.click()},disabled:!t,title:"Upload files to the workspace root",children:[i.jsx(Jn,{size:12}),i.jsx("span",{children:"Upload"})]}),i.jsx("input",{ref:C,type:"file",multiple:!0,className:N.hiddenInput,onChange:f=>{Ie(t,Array.from(f.target.files??[])),f.target.value=""}})]}),x&&i.jsx("div",{className:x.error?N.statusError:N.status,children:x.text}),i.jsxs("div",{className:N.list,ref:$e,children:[ce.length===0&&i.jsx("div",{className:N.emptyState,children:"No files loaded"}),i.jsx("div",{style:{position:"relative",height:le.getTotalSize()},children:le.getVirtualItems().map(f=>{const R=ce[f.index];return R?i.jsx("div",{"data-index":f.index,style:{position:"absolute",top:0,left:0,width:"100%",height:f.size,transform:`translateY(${f.start}px)`},onDragEnter:()=>{R.kind==="directory"&&J(R.path)},children:i.jsx(ol,{row:R,onToggle:o,onOpenFile:de,onContextMenu:(L,K,je,Oe)=>S(L,K,je,Oe),onSubmitDraft:(L,K)=>void tt(L,K),onCancelDraft:c})},f.key):null})})]}),_&&i.jsx("div",{className:N.dropOverlay,children:"Drop to upload"}),i.jsx(dl,{})]}):i.jsx("div",{className:N.emptyState,children:"Connect to browse files"})}const Ft={available:!1,branch:null,upstream:null,ahead:0,behind:0,detached:!1,conflictCount:0},Z=me()(e=>({subscribedCwd:null,changes:[],...Ft,setSubscribedCwd:t=>e({subscribedCwd:t}),applyProjection:t=>{if(!t||!t.available){e({changes:[],...Ft});return}const n=[];for(const s of t.staged)n.push({path:s.path,status:s.indexStatus==="A"?"added":s.indexStatus==="D"?"deleted":"modified",staged:!0});for(const s of t.unstaged)n.push({path:s.path,status:s.worktreeStatus==="D"?"deleted":"modified",staged:!1});for(const s of t.untracked)n.push({path:s,status:"added",staged:!1});e({changes:n,available:!0,branch:t.branch,upstream:t.upstream,ahead:t.ahead,behind:t.behind,detached:t.detached,conflictCount:t.conflicted.length})}})),hl="_list_1pgby_1",ml="_summary_1pgby_4",gl="_item_1pgby_11",_l="_badge_1pgby_23",yl="_added_1pgby_30",bl="_modified_1pgby_34",wl="_deleted_1pgby_38",Sl="_name_1pgby_42",xl="_emptyState_1pgby_47",fe={list:hl,summary:ml,item:gl,badge:_l,added:yl,modified:bl,deleted:wl,name:Sl,emptyState:xl},vl={added:"A",modified:"M",deleted:"D"};function kl(){const t=E(r=>r.activeWorkspaceCwd)||"~",n=Z(r=>r.changes),s=E(r=>r.open);function o(r,a){s({id:U.diff(r,a),kind:"diff",label:r.split("/").pop()||r,closable:!0,data:{path:r,staged:a},workspaceCwd:t})}return n.length===0?i.jsx("div",{className:fe.emptyState,children:"No changes detected"}):i.jsxs("div",{className:fe.list,children:[i.jsxs("div",{className:fe.summary,children:[n.length," changed file",n.length===1?"":"s"]}),n.map((r,a)=>i.jsxs("div",{className:fe.item,onClick:()=>o(r.path,r.staged),children:[i.jsx("span",{className:$(fe.badge,fe[r.status]),children:vl[r.status]}),i.jsx("span",{className:fe.name,children:r.path})]},`${r.path}-${r.staged}-${a}`))]})}const Il="_wrap_r37ok_1",jl="_tabBar_r37ok_7",Cl="_tabBtn_r37ok_13",Tl="_active_r37ok_26",El="_refreshBtn_r37ok_30",Nl="_content_r37ok_34",re={wrap:Il,tabBar:jl,tabBtn:Cl,active:Tl,refreshBtn:El,content:Nl};function Rl(){const e=k(c=>c.rightSidebarTab),t=k(c=>c.setRightSidebarTab),s=E(c=>c.activeWorkspaceCwd)||"~",o=j(c=>c.client),r=xe();function a(){r.invalidateQueries({queryKey:["explorer"]}),o&&s&&o.connection.request("checkout_refresh_request",{cwd:s}).catch(()=>{})}return i.jsxs("div",{className:re.wrap,children:[i.jsxs("div",{className:re.tabBar,children:[i.jsx("button",{type:"button",className:$(re.tabBtn,e==="files"&&re.active),onClick:()=>t("files"),children:"Files"}),i.jsx("button",{type:"button",className:$(re.tabBtn,e==="changes"&&re.active),onClick:()=>t("changes"),children:"Changes"}),i.jsx(O,{size:"xs",variant:"ghost",iconOnly:!0,title:"Refresh",className:re.refreshBtn,onClick:a,children:i.jsx(Yn,{size:13})})]}),i.jsx("div",{className:re.content,children:e==="files"?i.jsx(pl,{}):i.jsx(kl,{})})]})}const Rn=me()(e=>({bySession:{},setStats(t,n){e(s=>({bySession:{...s.bySession,[t]:{...s.bySession[t],...n}}}))},clear(t){e(n=>{const{[t]:s,...o}=n.bySession;return{bySession:o}})}})),Ml=12e3;function ql(e,t){return e==="running"&&t!=="running"}function zt(e,t){var s,o,r,a,c,d;const n={};((s=t.contextUsage)==null?void 0:s.tokens)!=null&&(n.contextTokens=t.contextUsage.tokens),((o=t.contextUsage)==null?void 0:o.contextWindow)!==void 0&&(n.contextWindow=t.contextUsage.contextWindow),((r=t.contextUsage)==null?void 0:r.percent)!=null&&(n.contextPercent=t.contextUsage.percent),((a=t.tokens)==null?void 0:a.total)!==void 0&&(n.totalTokens=t.tokens.total),((c=t.tokens)==null?void 0:c.input)!==void 0&&(n.inputTokens=t.tokens.input),((d=t.tokens)==null?void 0:d.output)!==void 0&&(n.outputTokens=t.tokens.output),t.cost!==void 0&&(n.cost=t.cost),t.model!==void 0&&(n.model=t.model,T.getState().setModel(e,t.model)),Rn.getState().setStats(e,n)}function Bl(e){const t=j(c=>c.client),n=j(c=>c.status),s=T(c=>{var d;return e?((d=c.sessions[e])==null?void 0:d.agentId)??null:null}),o=T(c=>{var d;return e?(d=c.sessions[e])==null?void 0:d.status:void 0}),r=w.useRef(!1),a=w.useRef(void 0);w.useEffect(()=>{if(n!=="open"||!t||!e||!s)return;let c=!1;const d=async()=>{if(!r.current){r.current=!0;try{const p=await t.agent(s).sessionStats();c||zt(e,p)}catch{}finally{r.current=!1}}};d();const m=setInterval(()=>void d(),Ml);return()=>{c=!0,clearInterval(m)}},[n,t,e,s]),w.useEffect(()=>{const c=a.current;a.current=o,ql(c,o)&&(n!=="open"||!t||!e||!s||(r.current=!1,t.agent(s).sessionStats().then(d=>zt(e,d)).catch(()=>{})))},[o,n,t,e,s])}function Al(e){return typeof e=="object"&&e!==null&&"type"in e&&e.type==="checkout_status_update"&&"cwd"in e&&typeof e.cwd=="string"&&"projection"in e&&typeof e.projection=="object"&&e.projection!==null}function $l(e){const t=j(r=>r.client),n=Z(r=>r.applyProjection),s=Z(r=>r.setSubscribedCwd),o=w.useRef(t);return o.current=t,w.useEffect(()=>{if(!t||!e)return;let r=!1;t.connection.request("checkout_status_subscribe",{cwd:e}).catch(()=>{}),s(e);const a=t.connection.onSessionMessage(c=>{if(r)return;const d=c;Al(d)&&d.cwd===e&&n(d.projection)});return t.connection.request("checkout_refresh_request",{cwd:e}).catch(()=>{}),()=>{r=!0,a(),t.connection.request("checkout_status_unsubscribe",{cwd:e}).catch(()=>{}),s(null)}},[t,e,n,s]),{refresh(){const r=o.current;!r||!e||r.connection.request("checkout_refresh_request",{cwd:e}).catch(()=>{})}}}const xt="--";function Ne(e){return e===void 0?xt:e<1e3?String(e):e<1e6?`${(e/1e3).toFixed(1)}k`:`${(e/1e6).toFixed(1)}M`}function Dl(e){if(e==null)return xt;const t=e<=1?e*100:e;return`${Math.round(t)}%`}function Ol(e){if(e===void 0)return xt;const t=e<1?4:2;return`$${e.toFixed(t)}`}function Pl(e,t){return t?e===t?"~":e.startsWith(`${t}/`)?`~${e.slice(t.length)}`:e:e}function Fl(e,t){const n=[];return e>0&&n.push(`↑${e}`),t>0&&n.push(`↓${t}`),n.join(" ")}function zl(e,t=!0){const n=j(s=>s.client);return Ht({queryKey:ve.providerModels(e),queryFn:async()=>{if(!n)throw new Error("not connected");return(await n.providers.listModels(e)).models},enabled:!!n&&!!e&&t})}function Ll(e,t){if(t.trim()==="")return e;const n=t.toLowerCase();return e.filter(s=>s.label.toLowerCase().includes(n)||s.description!==void 0&&s.description.toLowerCase().includes(n))}function Hl(e,t){if(!t)return e;const n=e.find(s=>s.id===t);return n?[n,...e.filter(s=>s.id!==t)]:e}function Wl(e){const t=new Set;return e.filter(n=>t.has(n.id)?!1:(t.add(n.id),!0))}const Ul="_content_1imfv_4",Kl="_search_1imfv_17",Vl="_list_1imfv_35",Gl="_item_1imfv_40",Xl="_itemActive_1imfv_54",Jl="_checkSlot_1imfv_58",Yl="_label_1imfv_67",Ql="_modelId_1imfv_73",Zl="_state_1imfv_80",ed="_stateError_1imfv_89",td="_commandContent_1imfv_99",nd="_commandList_1imfv_102",sd="_commandItem_1imfv_107",od="_commandItemHeader_1imfv_140",rd="_commandKindBadge_1imfv_154",ad="_commandKindExtension_1imfv_173",id="_commandKindPrompt_1imfv_178",cd="_commandKindSkill_1imfv_183",ld="_commandDescription_1imfv_188",Q={content:Ul,search:Kl,list:Vl,item:Gl,itemActive:Xl,checkSlot:Jl,label:Yl,modelId:Ql,state:Zl,stateError:ed,commandContent:td,commandList:nd,commandItem:sd,commandItemHeader:od,commandKindBadge:rd,commandKindExtension:ad,commandKindPrompt:id,commandKindSkill:cd,commandDescription:ld};function dd({currentModel:e,provider:t,onSelect:n,renderTrigger:s}){const[o,r]=w.useState(!1),[a,c]=w.useState(""),d=w.useRef(null),{data:m=[],isLoading:p,isError:S,error:h}=zl(t,o),x=Wl(Hl(m,e));w.useEffect(()=>{var C;o&&(c(""),(C=d.current)==null||C.focus())},[o]);const g=new Map(x.map(C=>[C.id,C.provider])),_=x.map(C=>({value:C.id,label:C.label??C.id,description:C.id})),y=Ll(_,a),q=h instanceof Error?h.message:"Failed to load models";function J(C){C.stopPropagation(),C.key==="Escape"&&r(!1)}return i.jsxs(Xe,{open:o,onOpenChange:r,modal:!1,children:[i.jsx(Je,{asChild:!0,children:s(e)}),i.jsx(Ye,{children:i.jsxs(Qe,{className:Q.content,align:"start",sideOffset:4,children:[i.jsx("input",{ref:d,className:Q.search,placeholder:"Search models…",value:a,onChange:C=>c(C.target.value),onKeyDown:J}),p&&i.jsx("div",{className:Q.state,children:i.jsx(In,{size:"sm"})}),!p&&S&&i.jsx("div",{className:Q.stateError,children:q}),!p&&!S&&y.length===0&&i.jsx("div",{className:Q.state,children:"No models found"}),!p&&!S&&y.length>0&&i.jsx("div",{className:Q.list,children:y.map(C=>i.jsxs(W,{className:Q.item,onSelect:()=>n(C.value,g.get(C.value)),children:[i.jsx("span",{className:Q.checkSlot,"aria-hidden":!0,children:C.value===e&&i.jsx(en,{size:14})}),i.jsx("span",{className:Q.label,children:C.label}),i.jsxs("span",{className:Q.modelId,children:["(",C.value,")"]})]},C.value))})]})})]})}const ud="_statusBar_tm1j6_1",fd="_segmentGroup_tm1j6_16",pd="_chevron_tm1j6_20",hd="_segment_tm1j6_16",md="_icon_tm1j6_33",gd="_modelSegment_tm1j6_39",_d="_modelChevron_tm1j6_63",yd="_text_tm1j6_74",G={statusBar:ud,segmentGroup:fd,chevron:pd,segment:hd,icon:md,modelSegment:gd,modelChevron:_d,text:yd};function bd(){const e=T(y=>y.activeSessionId),t=T(y=>e?y.sessions[e]:void 0),n=j(y=>y.client),s=T(y=>y.setModel),o=St(),r=E(y=>y.activeWorkspaceCwd);$l(r||"");const a=Z(y=>y.available),c=Z(y=>y.branch),d=Z(y=>y.ahead),m=Z(y=>y.behind),p=Z(y=>y.detached),S=Z(y=>y.conflictCount),h=Z(y=>y.changes.length),x=Rn(y=>e?y.bySession[e]:void 0);Bl(e);const g=[];if(t){const y=Pl(t.cwd,o);g.push({key:"cwd",icon:i.jsx(Ae,{size:13}),text:y,title:t.cwd})}if(a){const y=Fl(d,m),q=[p?"(detached)":c??"?"];y&&q.push(y),h>0&&q.push(`●${h}`),S>0&&q.push(`⚠${S}`),g.push({key:"branch",icon:i.jsx(Qn,{size:13}),text:q.join(" ")})}if(t){const y=`${Dl(x==null?void 0:x.contextPercent)} (${Ne(x==null?void 0:x.contextTokens)}/${Ne(x==null?void 0:x.contextWindow)})`;g.push({key:"context",icon:i.jsx(Zn,{size:13}),text:y}),g.push({key:"tokens",icon:i.jsx(es,{size:13}),text:Ne(x==null?void 0:x.totalTokens),title:`in ${Ne(x==null?void 0:x.inputTokens)} / out ${Ne(x==null?void 0:x.outputTokens)}`}),g.push({key:"cost",icon:i.jsx(ts,{size:13}),text:Ol(x==null?void 0:x.cost)})}function _(y,q){e&&(s(e,y,q),!(!n||!q)&&(async()=>{const J=await xn(n,e);await n.agent(J).setModel(q,y)})().catch(()=>{}))}return i.jsxs("div",{className:G.statusBar,children:[t&&i.jsx("span",{className:G.segmentGroup,children:i.jsx(dd,{currentModel:t.model,provider:"pi",onSelect:_,renderTrigger:y=>i.jsxs("button",{type:"button",className:G.modelSegment,disabled:!n,title:y?`Model: ${y}`:"Select model",children:[i.jsx("span",{className:G.icon,children:i.jsx(ns,{size:13})}),i.jsx("span",{className:G.text,children:y??"Model"}),i.jsx(Kt,{size:12,className:G.modelChevron,"aria-hidden":"true"})]})})}),g.map((y,q)=>i.jsxs("span",{className:G.segmentGroup,children:[(!!t||q>0)&&i.jsx(_t,{size:12,className:G.chevron,"aria-hidden":"true"}),i.jsxs("span",{className:G.segment,title:y.title,children:[i.jsx("span",{className:G.icon,children:y.icon}),i.jsx("span",{className:G.text,children:y.text})]})]},y.key))]})}const wd="_wrapper_131x2_3",Sd="_box_131x2_18",xd="_checkmark_131x2_36",vd="_nativeInput_131x2_41",We={wrapper:wd,box:Sd,checkmark:xd,nativeInput:vd};w.forwardRef(function({label:t,checked:n=!1,disabled:s,className:o,onChange:r,...a},c){return i.jsxs("label",{className:We.wrapper,"data-disabled":s||void 0,children:[i.jsx("input",{ref:c,type:"checkbox",className:We.nativeInput,checked:n,disabled:s,onChange:r,...a}),i.jsx("span",{className:We.box,"data-checked":n,"aria-hidden":!0,children:n&&i.jsx(en,{size:12,className:We.checkmark})}),t&&i.jsx("span",{children:t})]})});const kd="_select_vj45v_3",Id={select:kd};w.forwardRef(function({options:t,placeholder:n,className:s,...o},r){return i.jsxs("select",{ref:r,className:$(Id.select,s),...o,children:[n&&i.jsx("option",{value:"",disabled:!0,children:n}),t.map(a=>i.jsx("option",{value:a.value,children:a.label},a.value))]})});const jd="_overlay_1yaef_1",Cd="_contentWrapper_1yaef_7",Td="_dialog_1yaef_16",Ed="_header_1yaef_28",Nd="_title_1yaef_36",Rd="_closeBtn_1yaef_43",Md="_body_1yaef_57",qd="_footer_1yaef_63",Bd="_bare_1yaef_72",Ad="_bareBody_1yaef_78",$d="_bareCloseBtn_1yaef_82",Dd="_visuallyHidden_1yaef_101",H={overlay:jd,contentWrapper:Cd,dialog:Td,header:Ed,title:Nd,closeBtn:Rd,body:Md,footer:qd,bare:Bd,bareBody:Ad,bareCloseBtn:$d,visuallyHidden:Dd},Od=dt;function Pd({open:e,onOpenChange:t,title:n,children:s,footer:o,width:r,className:a,bare:c=!1}){return i.jsx(os,{open:e,onOpenChange:t,children:i.jsxs(rs,{children:[i.jsx(as,{className:H.overlay}),i.jsx("div",{className:H.contentWrapper,children:i.jsxs(is,{className:$(H.dialog,c&&H.bare,a),style:r!==void 0?{width:r}:void 0,children:[c?i.jsx(vt,{className:H.visuallyHidden,children:n}):i.jsxs("div",{className:H.header,children:[i.jsx(vt,{className:H.title,children:n}),i.jsx(dt,{asChild:!0,children:i.jsx("button",{type:"button",className:H.closeBtn,"aria-label":"Close",children:i.jsx(ct,{size:16})})})]}),i.jsx("div",{className:$(H.body,c&&H.bareBody),children:s}),c&&i.jsx(dt,{asChild:!0,children:i.jsx("button",{type:"button",className:H.bareCloseBtn,"aria-label":"Close",children:i.jsx(ct,{size:16})})}),o&&i.jsx("div",{className:H.footer,children:o})]})})]})})}const Fd="_handle_1ocv1_1",zd={handle:Fd};function Lt({side:e,onResize:t,className:n}){const s=w.useRef(0),o=w.useCallback(c=>{const d=c.clientX-s.current;s.current=c.clientX,t(e==="left"?d:-d)},[t,e]),r=w.useCallback(c=>{var d,m;window.removeEventListener("pointermove",o),window.removeEventListener("pointerup",r),document.body.style.cursor="",document.body.style.userSelect="",(m=(d=c.target)==null?void 0:d.releasePointerCapture)==null||m.call(d,c.pointerId)},[o]);function a(c){c.preventDefault(),s.current=c.clientX,document.body.style.cursor="col-resize",document.body.style.userSelect="none",window.addEventListener("pointermove",o),window.addEventListener("pointerup",r)}return i.jsx("div",{className:$(zd.handle,n),onPointerDown:a,role:"separator","aria-orientation":"vertical"})}function Ld(e,t){const{order:n,sessions:s}=T.getState(),r=kn(n,s,t).find(a=>a.cwd===e);if(r!=null&&r.sessions[0]){const a=r.sessions[0].id;T.getState().activate(a);const c=T.getState().sessions[a];E.getState().open({id:U.chat(a),kind:"chat",label:(c==null?void 0:c.title)??"New chat",closable:!0,data:{sessionId:a},workspaceCwd:e})}else wt(e);k.getState().setCwd(e)}const Hd="_pathRow_trimn_1",Wd="_pathInput_trimn_9",Ud="_createRow_trimn_13",Kd="_createInput_trimn_21",Vd="_createError_trimn_25",Gd="_list_trimn_30",Xd="_item_trimn_37",Jd="_name_trimn_49",Yd="_emptyState_trimn_54",X={pathRow:Hd,pathInput:Wd,createRow:Ud,createInput:Kd,createError:Vd,list:Gd,item:Xd,name:Jd,emptyState:Yd};function Qd(){const e=k(f=>f.cwdPickerOpen),t=k(f=>f.closeCwdPicker),n=E(f=>f.activeWorkspaceCwd),s=k(f=>f.cwd),o=j(f=>f.client),r=St(),[a,c]=w.useState("/"),[d,m]=w.useState("/"),[p,S]=w.useState(!1),[h,x]=w.useState(""),[g,_]=w.useState(null),[y,q]=w.useState(!1),J=xe();w.useEffect(()=>{if(!e)return;const f=Se(n||s||"~",r);c(f),m(f),le()},[e,o,n,s]);const{data:C,isLoading:$e,isError:ke,error:ge}=_c(a,e&&!!o),ce=((C==null?void 0:C.entries)??[]).filter(f=>f.kind==="directory");function le(){S(!1),x(""),_(null)}function de(f){c(f),m(f),le()}function tt(){de(a.split("/").slice(0,-1).join("/")||"/")}function Ie(){de(d.trim())}async function De(){const f=h.trim();if(!(!f||!o||y)){q(!0),_(null);try{const R=await Nn(o,a,f,"directory");await J.invalidateQueries({queryKey:ve.explorer(a)}),de(R)}catch(R){_(R instanceof Error?R.message:"Failed to create folder")}finally{q(!1)}}}function nt(){Ld(a,r),t()}return i.jsxs(Pd,{open:e,onOpenChange:f=>!f&&t(),title:"Open Workspace",footer:i.jsxs(i.Fragment,{children:[i.jsx(Od,{asChild:!0,children:i.jsx(O,{size:"sm",variant:"secondary",children:"Cancel"})}),i.jsx(O,{size:"sm",onClick:nt,children:"Open this folder"})]}),children:[i.jsxs("div",{className:X.pathRow,children:[i.jsx(O,{size:"xs",variant:"ghost",iconOnly:!0,title:"Up",onClick:tt,children:i.jsx(ss,{size:13})}),i.jsx(Ve,{className:X.pathInput,value:d,onChange:f=>m(f.target.value),onKeyDown:f=>{f.key==="Enter"&&(f.preventDefault(),Ie())}}),i.jsx(O,{size:"xs",onClick:Ie,children:"Go"}),i.jsx(O,{size:"xs",variant:"ghost",iconOnly:!0,title:"New folder",disabled:!o||ke||p,onClick:()=>{S(!0),x(""),_(null)},children:i.jsx(Zt,{size:13})})]}),p&&i.jsxs(i.Fragment,{children:[i.jsxs("div",{className:X.createRow,children:[i.jsx(Ae,{size:14}),i.jsx(Ve,{className:X.createInput,autoFocus:!0,placeholder:"New folder name",value:h,onChange:f=>x(f.target.value),onKeyDown:f=>{f.key==="Enter"?(f.preventDefault(),De()):f.key==="Escape"&&(f.preventDefault(),f.stopPropagation(),le())}}),i.jsx(O,{size:"xs",loading:y,disabled:!h.trim()||y,onClick:()=>void De(),children:"Create"}),i.jsx(O,{size:"xs",variant:"secondary",onClick:le,children:"Cancel"})]}),g&&i.jsx("div",{className:X.createError,children:g})]}),i.jsx("div",{className:X.list,children:$e?i.jsx("div",{className:X.emptyState,children:"Loading..."}):ke?i.jsxs("div",{className:X.emptyState,children:["Error: ",ge instanceof Error?ge.message:"unknown error"]}):ce.length===0?i.jsx("div",{className:X.emptyState,children:"No subdirectories"}):ce.map(f=>i.jsxs("div",{className:X.item,onClick:()=>de(a==="/"?`/${f.name}`:`${a}/${f.name}`),children:[i.jsx(Ae,{size:14}),i.jsx("span",{className:X.name,children:f.name})]},f.name))})]})}const Zd="_shell_1m1nv_1",eu="_main_1m1nv_7",tu="_sidebarLeft_1m1nv_13",nu="_leftHandle_1m1nv_21",su="_center_1m1nv_24",ou="_sidebarRight_1m1nv_31",ru="_rightHandle_1m1nv_39",pe={shell:Zd,main:eu,sidebarLeft:tu,leftHandle:nu,center:su,sidebarRight:ou,rightHandle:ru};function au(){const e=k(a=>a.leftSidebarCollapsed),t=k(a=>a.rightSidebarCollapsed),n=k(a=>a.leftSidebarWidth),s=k(a=>a.rightSidebarWidth),o=k(a=>a.resizeLeftSidebar),r=k(a=>a.resizeRightSidebar);return i.jsxs("div",{className:pe.shell,children:[i.jsx(Da,{}),i.jsxs("div",{className:pe.main,children:[!e&&i.jsxs("aside",{className:pe.sidebarLeft,style:{width:n},children:[i.jsx(Oi,{}),i.jsx(Lt,{side:"left",className:pe.leftHandle,onResize:o})]}),i.jsxs("div",{className:pe.center,children:[i.jsx(Qi,{}),i.jsx(hc,{})]}),!t&&i.jsxs("aside",{className:pe.sidebarRight,style:{width:s},children:[i.jsx(Lt,{side:"right",className:pe.rightHandle,onResize:r}),i.jsx(Rl,{})]})]}),i.jsx(bd,{}),i.jsx(Qd,{})]})}function iu(){const e=w.useRef(!1);w.useEffect(()=>{if(e.current)return;if(e.current=!0,window.location.hash.includes("offer=")){const d=window.location.href;k.getState().setHost(d),j.getState().connect({url:d});return}const t=new URLSearchParams(window.location.search),n=t.get("host"),s=t.get("pair"),o=t.get("password"),r=t.get("cwd"),a=t.get("connect")==="1",c=k.getState();if(s?c.setHost(s):n&&c.setHost(n),o&&c.setPassword(o),r&&c.setCwd(r),s)j.getState().connect({url:s});else if(a){const{host:d,password:m}=k.getState();j.getState().connect({url:d,password:m||void 0})}},[])}function cu(e){return typeof e=="object"&&e!==null&&"kind"in e}function lu(e){const t=[];for(const n of e)if(cu(n))if(n.kind==="other")n.event&&t.push(n.event);else for(const s of n.events??[])t.push(s);return t}function du(e){return typeof e.model=="string"}function uu(){const e=j(s=>s.client),t=j(s=>s.status),n=w.useRef(!1);w.useEffect(()=>{t!=="open"||!e||n.current||(n.current=!0,(async()=>{var p;let s=[];try{s=(await e.connection.request("list_agents_request",{all:!1})).agents??[]}catch{s=[]}s.sort((S,h)=>(h.lastActivity??0)-(S.lastActivity??0));const o=T.getState();for(const S of s){if(o.findByAgentId(S.agentId))continue;let h=ht;try{const g=await e.agent(S.agentId).timeline.fetch({direction:"after",limit:200});h=lu(g.items).reduce(Sn,ht)}catch{}const x=h.rows.length===0;o.hydrate({id:`s-${S.agentId.slice(0,12)}`,agentId:S.agentId,title:S.title||(x?"New chat":(p=S.cwd)==null?void 0:p.split("/").pop())||"Restored",status:S.status??"idle",cwd:S.cwd||"~",timeline:h,userMessageCount:h.rows.filter(g=>g.kind==="user").length,model:S.model,modelProvider:S.modelProvider})}const{order:r,sessions:a}=T.getState(),c=r.length>0?a[r[0]??""]:void 0;if(!c)return;const d=await et(e).catch(()=>null),m=Se(c.cwd||"~",d);T.getState().activate(c.id),k.getState().setCwd(c.cwd),E.getState().open({id:U.chat(c.id),kind:"chat",label:c.title,closable:!0,data:{sessionId:c.id},workspaceCwd:m})})())},[t,e]),w.useEffect(()=>{if(!(t!=="open"||!e))return e.onAgentUpdate(s=>{if(du(s)){const o=typeof s.modelProvider=="string"?s.modelProvider:void 0;T.getState().setModelByAgentId(s.agentId,s.model,o)}})},[t,e])}function fu(){const e=j(s=>s.client),t=j(s=>s.status),n=w.useRef(!1);w.useEffect(()=>{t!=="open"||!e||n.current||(n.current=!0,(async()=>{let s=[];try{s=(await e.connection.request("list_terminals_request",{})).terminals??[]}catch{s=[]}if(s.length===0)return;const o=await et(e).catch(()=>null),r=E.getState().open;for(const a of s){if(a.closed)continue;const c=Se(a.cwd||"~",o);r({id:U.terminal(a.slot),kind:"terminal",label:a.name,closable:!0,data:{slot:a.slot,cwd:c},workspaceCwd:c})}})())},[t,e])}function pu(){w.useEffect(()=>{function e(t){const n=k.getState();if(t.key==="Escape"){n.closeCwdPicker(),n.closeSessionMenu();return}const s=t.ctrlKey||t.metaKey;if(s&&t.key.toLowerCase()==="t"){t.preventDefault(),vn(E.getState().activeWorkspaceCwd||"~");return}if(s&&t.key.toLowerCase()==="w"){const{activeTabId:o,tabs:r}=E.getState();if(!o)return;const a=r.find(c=>c.id===o);(a==null?void 0:a.closable)!==!1&&(t.preventDefault(),mt(o))}}return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[])}function hu(){return iu(),uu(),fu(),pu(),null}function mu(){return i.jsxs(ro,{children:[i.jsx(hu,{}),i.jsx(au,{})]})}const Mn=document.getElementById("root");if(!Mn)throw new Error("Missing #root element");$n.createRoot(Mn).render(i.jsx(w.StrictMode,{children:i.jsx(mu,{})}));export{O as B,Pd as D,In as S,Iu as T,Eu as V,j as a,St as b,ju as c,Nu as d,xn as e,Ll as f,E as g,Cu as h,Tu as i,Pt as j,U as k,Se as n,ve as r,Q as s,Oa as t,T as u};
|
|
3
|
+
//# sourceMappingURL=index-CL9Q4aRP.js.map
|