{"version":3,"mappings":"AA+KU;AA/KV,OAAOA,WAAW;AAClB,SAASC,kBAAkB;AAC3B,OAAO;AACP,OAAO;AACP,OAAO;AACP,SAASC,UAAUC,qBAAqB;AACxC,SAASC,uBAAuB;AAChC,SAASC,eAAe;AACxB,SAASC,WAAW;AACpB,SAASC,qBAAqB;AAC9B,SAASC,uBAAuB;AAChC,SAASC,4BAA4B;AACrC,SAASC,mCAAmC;AAC5C,SAASC,sCAAsC;AAC/C,SAASC,6BAA6B;AACtC,SAASC,2BAA2B;AACpC,SAASC,uBAAuB;AAChC,SAASC,0BAA0B;AACnC,SAASC,2BAA2B;AACpC,SAASC,sBAAsBC,0BAA0B;AACzD,SAASC,8BAA8B;AACvC,SAASC,+BAA+B;AAKxCf,QAAQgB,QAAQC,MAAM;AAStBZ,4BAA4B;AAG5BC,+BAA+B;AAK/BC,sBAAsB;AAItBE,gBAAgBD,mBAAmB;AAMnCE,mBAAmB;AAOnBC,oBAAoB;AAapBG,uBAAuB;AAOvBC,wBAAwB;AAgBxB,WAAWG,QAAQ,CAAC,gBAAgB,iBAAiB,YAAY,GAAG;AAClEC,WAASC,iBAAiBF,MAAM,CAACG,MAAMA,EAAEC,eAAe,GAAG,EAAEC,SAAS,MAAM,CAAC;AAC/E;AAaA,MAAMC,KAAKP,OAAOQ;AAClB,IAAID,IAAI;AACN,MAAIE,YAAY;AAChB,MAAIC,MAAM;AACV,QAAMC,QAAQA,MAAM;AAClBD,UAAM;AACN,UAAME,IAAI/B,cAAc0B,GAAGM,QAAQN,GAAGO,WAAWL,WAAWT,OAAOe,WAAW;AAC9EN,gBAAYG,EAAEH;AACd,UAAMO,OAAOd,SAASe,gBAAgBC;AACtCF,SAAKG,YAAY,WAAW,GAAGP,EAAEQ,IAAI,IAAI;AACzCJ,SAAKG,YAAY,WAAW,GAAGP,EAAES,IAAI,IAAI;AACzCL,SAAKG,YAAY,iBAAiBP,EAAEU,eAAe,QAAQ,yBAAyB;AAIpFN,SAAKG,YAAY,cAAcP,EAAEU,eAAe,QAAQ,sBAAsB;AAE9ExC,oBAAgB8B,EAAEU,YAAY;AAAA,EAChC;AACA,QAAMC,WAAWA,MAAM;AACrB,QAAI,CAACb,IAAKA,OAAMc,sBAAsBb,KAAK;AAAA,EAC7C;AACAA,QAAM;AACNJ,KAAGJ,iBAAiB,UAAUoB,QAAQ;AACtChB,KAAGJ,iBAAiB,UAAUoB,QAAQ;AACtCvB,SAAOG,iBAAiB,qBAAqB,MAAM;AACjDM,gBAAY7B,SAAS2B,GAAGM,MAAM;AAC9BU,aAAS;AAAA,EACX,CAAC;AACH;AAKA5B,qBAAqBC,mBAAmB,CAAC;AAmBzCjB,WAAWuB,SAASuB,eAAe,MAAM,CAAE,EAAEC;AAAAA,EAC3C,uBAAC,MAAM,YAAN,EACC,iCAAC,iBAAc,MAAK,QAAO,OAAM,aAC/B,iCAAC,mBAOC,iCAAC,wBACC,iCAAC,SAAD;AAAA;AAAA;AAAA;AAAA,SAAI,KADN;AAAA;AAAA;AAAA;AAAA,SAEA,KATF;AAAA;AAAA;AAAA;AAAA,SAUA,KAXF;AAAA;AAAA;AAAA;AAAA,SAYA,KAbF;AAAA;AAAA;AAAA;AAAA,SAcA;AACF","names":["React","createRoot","resetMax","viewportState","setKeyboardOpen","hotkeys","App","ErrorBoundary","TooltipProvider","VoiceSessionProvider","installGlobalErrorReporting","installRequestFailureReporting","startBuildReloadWatch","handleSecretMessage","onServerMessage","startLivenessSweep","startConnectionWake","applyBoardBackground","getBoardBackground","armBaselinePersistence","armInstrumentLayoutSync","install","window","type","document","addEventListener","e","preventDefault","passive","vv","visualViewport","maxHeight","raf","apply","s","height","offsetTop","innerHeight","root","documentElement","style","setProperty","appH","appY","keyboardOpen","schedule","requestAnimationFrame","getElementById","render"],"ignoreList":[],"sources":["main.tsx"],"sourcesContent":["import React from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport \"@fontsource-variable/ibm-plex-sans\";\nimport \"@fontsource-variable/jetbrains-mono\";\nimport \"./index.css\";\nimport { resetMax, viewportState } from \"./lib/viewport.ts\";\nimport { setKeyboardOpen } from \"./lib/keyboardOpen.ts\";\nimport { hotkeys } from \"./lib/hotkeys/manager.ts\";\nimport { App } from \"./App.tsx\";\nimport { ErrorBoundary } from \"./components/ErrorBoundary.tsx\";\nimport { TooltipProvider } from \"./components/ui/Tooltip.tsx\";\nimport { VoiceSessionProvider } from \"./components/VoiceSession.tsx\";\nimport { installGlobalErrorReporting } from \"./lib/clientErrorReport.ts\";\nimport { installRequestFailureReporting } from \"./lib/actionFailures.ts\";\nimport { startBuildReloadWatch } from \"./lib/buildReload.ts\";\nimport { handleSecretMessage } from \"./lib/gatedSecrets.ts\";\nimport { onServerMessage } from \"./lib/ws.ts\";\nimport { startLivenessSweep } from \"./lib/livenessSweep.ts\";\nimport { startConnectionWake } from \"./lib/connectionWake.ts\";\nimport { applyBoardBackground, getBoardBackground } from \"./lib/boardBackground.ts\";\nimport { armBaselinePersistence } from \"./lib/boardBaseline.ts\";\nimport { armInstrumentLayoutSync } from \"./lib/store.ts\";\n\n// One dispatcher owns every keyboard shortcut (lib/hotkeys) — installed before\n// anything else can attach a window keydown listener, so a consumed key is\n// suppressed for stragglers via stopImmediatePropagation.\nhotkeys.install(window);\n\n// Report the half of the app's failures a React error boundary structurally CANNOT see —\n// event handlers, timers, async code, rejected promises. The boundaries below contain render\n// throws; these two listeners make sure the rest are not silently invisible just because\n// there is no subtree to replace. Reports only (console + server + toast); never\n// `preventDefault`, so the browser's own logging is untouched. Installed here rather than at\n// module load for the same reason as everything else in this file: component tests must not\n// attach window listeners — and must not have their own expected throws absorbed.\ninstallGlobalErrorReporting();\n// …and every state-changing request that failed for want of an answer goes on the\n// failed-action census (`lib/actionFailures.ts`), whichever caller catches it.\ninstallRequestFailureReporting();\n\n// Watch the HMR bus + the server's build id, so a tab left running stale code after a\n// land reloads itself onto the new build (safely, and traced). Installed here rather\n// than at module load so component tests importing the store never spin its timer.\nstartBuildReloadWatch();\n// Answer the server's gated-secret asks from here rather than a component: a run\n// can need one whatever the user has open, and an unmounted pane would simply not\n// answer — which the server would (correctly, but needlessly) report as unreachable.\nonServerMessage(handleSecretMessage);\n\n// Demote a stream still pulsing \"running\" whose runner died without its closing status\n// event. The reconcile is TIME-based, so it needs a clock; it used to borrow the server's\n// redundant board frames, which the delta protocol stopped sending. Same reason as above\n// for installing it here rather than at module load: component tests must not spin a timer.\nstartLivenessSweep();\n\n// Re-check the socket the moment the tab is looked at again. A backgrounded tab's timers are\n// throttled to ~1/min, which puts the ENTIRE reconnect path to sleep — the pending attempt,\n// the liveness deadline and the offline grace alike — so a returning tab sat frozen for up to\n// a minute with no recovery and no signal. Installed here (not at module load) for the same\n// reason as the two above: component tests must not attach window listeners.\nstartConnectionWake();\n\n// Carry the board across a RELOAD. The baseline survives a socket drop by living in module\n// memory — that is the resume mechanism — but a reload wipes it, and a reload is exactly what\n// a web-source land causes (vite's full-reload, or the build reloader above). Without this the\n// resubscribe asks cold and the server answers with the whole board.\n//\n// Hydrate BEFORE the socket can subscribe, so the first `subscribe_project` already carries a\n// `have`; persist on `pagehide`, which fires for every reload path including a manual refresh.\n// A restored board is resume ammunition only — it is never rendered and never counts as a\n// server answer — so the worst a stale entry costs is bytes.\n// Both steps live in `armBaselinePersistence` so the browser test can drive the real arming\n// rather than a copy of these lines — see its doc comment.\narmBaselinePersistence();\n\n// Converge the header's and the rail's arrangement across every open tab — a write\n// in one tab is otherwise invisible to a sibling until it reloads, and the sibling's\n// next write then overwrites the first tab's silently. Installed here, not at\n// `store.ts` module load, for the same reason as every other window listener in\n// this file: component tests import the store with no `window` to attach one to.\narmInstrumentLayoutSync();\n\n// The WebSocket is opened by App AFTER the optional password gate is satisfied (the\n// auth cookie must be set before the /ws upgrade, or the server 1008-closes it) —\n// not here, so an unauthenticated first paint doesn't spin a rejected reconnect loop.\n\n// The DevTools overlay is NOT bundled here — when Dorodango serves this app as a task\n// preview, its proxy injects the overlay IIFE (`/dorodango-devtools.iife.js`) into the\n// page. Proxy injection is the single delivery path (it also strips any app-bundled\n// boot), so the web app never depends on `@dorodango/devtools` directly.\n\n// Disable pinch-zoom on iOS. Safari deliberately ignores `user-scalable=no` /\n// `maximum-scale` in the viewport meta, so the reliable hook is the iOS-only\n// `gesture*` events — preventing them blocks the pinch (no feature relies on it,\n// and a stuck zoom left content jammed against the Dynamic Island / Safari pill).\n// Double-tap zoom is already handled by `touch-action: manipulation` (index.css).\nfor (const type of [\"gesturestart\", \"gesturechange\", \"gestureend\"]) {\n  document.addEventListener(type, (e) => e.preventDefault(), { passive: false });\n}\n\n// Track the VISUAL viewport into `--app-h`/`--app-y`. `100dvh` follows the Safari\n// toolbar but NOT the on-screen keyboard — so a bottom-docked composer inside a\n// `position: fixed` panel (which iOS never scrolls) would hide behind the keyboard.\n// `visualViewport.height` accounts for both; `offsetTop` tracks iOS focus-scrolling\n// the visual viewport down (untracked, the fixed panel stays glued to the layout\n// viewport top and the board shows through below the composer). While the keyboard\n// is up it covers the home indicator, so `--safe-bottom` collapses to 0 — otherwise\n// every pb-safe bar keeps a dead 34px band above the keyboard in the installed PWA.\n// The decisions live (tested) in lib/viewport.ts; writes are rAF-batched so the\n// event burst during the keyboard animation doesn't thrash style recalc.\n// Falls back to `100dvh` when visualViewport is unavailable.\nconst vv = window.visualViewport;\nif (vv) {\n  let maxHeight = 0;\n  let raf = 0;\n  const apply = () => {\n    raf = 0;\n    const s = viewportState(vv.height, vv.offsetTop, maxHeight, window.innerHeight);\n    maxHeight = s.maxHeight;\n    const root = document.documentElement.style;\n    root.setProperty(\"--app-h\", `${s.appH}px`);\n    root.setProperty(\"--app-y\", `${s.appY}px`);\n    root.setProperty(\"--safe-bottom\", s.keyboardOpen ? \"0px\" : \"var(--safe-bottom-base)\");\n    // Top inset: while the keyboard is up iOS scrolls the notch out of view, so the\n    // env() top inset would leave a blank band above every pt-safe header. Zero it\n    // (mirror of --safe-bottom), restore on close.\n    root.setProperty(\"--safe-top\", s.keyboardOpen ? \"0px\" : \"var(--safe-top-base)\");\n    // Publish the open/close edge to React subscribers (mobile chrome collapse).\n    setKeyboardOpen(s.keyboardOpen);\n  };\n  const schedule = () => {\n    if (!raf) raf = requestAnimationFrame(apply);\n  };\n  apply();\n  vv.addEventListener(\"resize\", schedule);\n  vv.addEventListener(\"scroll\", schedule); // offsetTop changes fire scroll, not resize\n  window.addEventListener(\"orientationchange\", () => {\n    maxHeight = resetMax(vv.height); // portrait's max must not read landscape as \"keyboard\"\n    schedule();\n  });\n}\n\n// Paint the chosen backdrop BEFORE the first render: the store seeds the same value,\n// but `body`'s background is applied by the browser the moment the stylesheet lands,\n// so leaving this to React means a frame of the ship background on every load.\napplyBoardBackground(getBoardBackground());\n\n// THE app's one tooltip provider. It is what makes the skip window reach ACROSS\n// neighbouring triggers — Radix keeps `skipDelayDuration` and the pointer-transit\n// flag on the provider, so a provider per tooltip (what `ui/Tooltip.tsx` shipped\n// with) meant every step along a row of header buttons paid the full 300ms open\n// delay again. Measured on the header's own rings: 255ms to the neighbour 44px\n// away, against 21ms re-entering the one just left.\n//\n// Here rather than inside `App` because `App` has four early returns (licence\n// wall, login gate, user gate, loading shell) and a provider covering only the\n// fifth is a coordination hole shaped exactly like the one it replaces. It\n// renders no DOM, so it is not the \"context providers\" `AGENTS.md` says App has\n// none of — it is context and nothing else, and `Tooltip` still works without it.\n\n// The LAST-RESORT boundary. Every meaningful seam has its own (App.tsx: the header, each\n// warm board, each warm panel, the board-cell overlays), so a throw should be contained long\n// before it reaches here — but a throw in App's own shell, above all of them, has nowhere\n// else to go, and the alternative is the empty `#root` this whole feature exists to end.\ncreateRoot(document.getElementById(\"root\")!).render(\n  <React.StrictMode>\n    <ErrorBoundary name=\"root\" label=\"Dorodango\">\n      <TooltipProvider>\n        {/* THE voice session, above every surface that shows it. Here for the same reason\n            as the provider around it: `App`'s four early returns mean a session mounted\n            inside it would come and go with them. What it fixes is that the session used\n            to belong to `VoiceBar`, so hiding the `voice` instrument — a per-browser\n            display preference — dropped the person out of the call. See\n            `components/VoiceSession.tsx`. */}\n        <VoiceSessionProvider>\n          <App />\n        </VoiceSessionProvider>\n      </TooltipProvider>\n    </ErrorBoundary>\n  </React.StrictMode>\n);\n"],"file":"/home/operator/operator/apps/web/src/main.tsx"}