@ottocode/web-sdk 0.1.288 → 0.1.289

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.
@@ -31392,7 +31392,7 @@ var BrowserPanelToggle = memo49(function BrowserPanelToggle2() {
31392
31392
  });
31393
31393
  });
31394
31394
  // src/components/browser/BrowserViewerPanel.tsx
31395
- import { useEffect as useEffect56, useState as useState52 } from "react";
31395
+ import { useCallback as useCallback37, useEffect as useEffect56, useRef as useRef38, useState as useState52 } from "react";
31396
31396
  import {
31397
31397
  ChevronLeft as ChevronLeft2,
31398
31398
  ChevronRight as ChevronRight18,
@@ -31400,7 +31400,8 @@ import {
31400
31400
  Globe2 as Globe23,
31401
31401
  Plus as Plus9,
31402
31402
  RefreshCw as RefreshCw12,
31403
- Smartphone
31403
+ Smartphone,
31404
+ X as X23
31404
31405
  } from "lucide-react";
31405
31406
 
31406
31407
  // src/hooks/useSimulator.ts
@@ -31432,13 +31433,17 @@ function useSimulatorStatus() {
31432
31433
  import { jsx as jsx122, jsxs as jsxs106 } from "react/jsx-runtime";
31433
31434
  var DEFAULT_BROWSER_URL = "http://localhost:3000";
31434
31435
  var SIMULATOR_URL = "http://localhost:3200";
31436
+ var IFRAME_EMBED_TIMEOUT_MS = 6000;
31435
31437
  function normalizeBrowserUrl(value) {
31436
31438
  const trimmed = value.trim();
31437
31439
  if (!trimmed)
31438
31440
  return "";
31439
31441
  if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed))
31440
31442
  return trimmed;
31441
- return `http://${trimmed}`;
31443
+ if (/^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::\d+)?(?:\/|$)/i.test(trimmed)) {
31444
+ return `http://${trimmed}`;
31445
+ }
31446
+ return `https://${trimmed}`;
31442
31447
  }
31443
31448
  function isEmbeddableUrl(value) {
31444
31449
  if (!value)
@@ -31454,11 +31459,100 @@ function BrowserViewerPanel({ tab }) {
31454
31459
  const updateBrowserTabUrl = useViewerTabsStore((state) => state.updateBrowserTabUrl);
31455
31460
  const reloadBrowserTab = useViewerTabsStore((state) => state.reloadBrowserTab);
31456
31461
  const openBrowserTab = useViewerTabsStore((state) => state.openBrowserTab);
31462
+ const contentRef = useRef38(null);
31463
+ const iframeRef = useRef38(null);
31464
+ const loadingDoneTimeoutRef = useRef38(null);
31465
+ const iframeEmbedTimeoutRef = useRef38(null);
31466
+ const isLoadingRef = useRef38(false);
31457
31467
  const [draftUrl, setDraftUrl] = useState52(tab.url);
31468
+ const [historyEntries, setHistoryEntries] = useState52(() => tab.url ? [normalizeBrowserUrl(tab.url)] : []);
31469
+ const [historyIndex, setHistoryIndex] = useState52(tab.url ? 0 : -1);
31470
+ const [isLoading, setIsLoading] = useState52(() => isEmbeddableUrl(normalizeBrowserUrl(tab.url)));
31471
+ const [loadingProgress, setLoadingProgress] = useState52(() => isEmbeddableUrl(normalizeBrowserUrl(tab.url)) ? 12 : 0);
31472
+ const [embedError, setEmbedError] = useState52(null);
31458
31473
  const simulatorStatus = useSimulatorStatus();
31474
+ const nativeBrowser = typeof window !== "undefined" ? window.OTTO_NATIVE_BROWSER : undefined;
31475
+ const normalizedUrl = normalizeBrowserUrl(tab.url);
31476
+ const canRenderUrl = isEmbeddableUrl(normalizedUrl);
31477
+ const useNativeBrowser = Boolean(nativeBrowser?.isAvailable && canRenderUrl);
31478
+ const canGoBack = historyIndex > 0;
31479
+ const canGoForward = historyIndex >= 0 && historyIndex < historyEntries.length - 1;
31480
+ const clearIframeEmbedTimeout = useCallback37(() => {
31481
+ if (iframeEmbedTimeoutRef.current) {
31482
+ clearTimeout(iframeEmbedTimeoutRef.current);
31483
+ iframeEmbedTimeoutRef.current = null;
31484
+ }
31485
+ }, []);
31486
+ const completeLoading = useCallback37(() => {
31487
+ clearIframeEmbedTimeout();
31488
+ setEmbedError(null);
31489
+ setLoadingProgress(100);
31490
+ if (loadingDoneTimeoutRef.current) {
31491
+ clearTimeout(loadingDoneTimeoutRef.current);
31492
+ }
31493
+ loadingDoneTimeoutRef.current = setTimeout(() => {
31494
+ setIsLoading(false);
31495
+ setLoadingProgress(0);
31496
+ loadingDoneTimeoutRef.current = null;
31497
+ }, 180);
31498
+ }, [clearIframeEmbedTimeout]);
31459
31499
  useEffect56(() => {
31460
31500
  setDraftUrl(tab.url);
31461
31501
  }, [tab.url]);
31502
+ useEffect56(() => {
31503
+ isLoadingRef.current = isLoading;
31504
+ }, [isLoading]);
31505
+ useEffect56(() => {
31506
+ if (!isLoading)
31507
+ return;
31508
+ setLoadingProgress((progress) => progress <= 0 || progress >= 100 ? 12 : progress);
31509
+ const interval = setInterval(() => {
31510
+ setLoadingProgress((progress) => {
31511
+ if (progress >= 88)
31512
+ return progress;
31513
+ return Math.min(88, progress + Math.max(2, (90 - progress) * 0.12));
31514
+ });
31515
+ }, 250);
31516
+ return () => clearInterval(interval);
31517
+ }, [isLoading]);
31518
+ useEffect56(() => () => {
31519
+ if (loadingDoneTimeoutRef.current) {
31520
+ clearTimeout(loadingDoneTimeoutRef.current);
31521
+ }
31522
+ clearIframeEmbedTimeout();
31523
+ }, [clearIframeEmbedTimeout]);
31524
+ useEffect56(() => {
31525
+ if (!isLoading || !canRenderUrl || useNativeBrowser) {
31526
+ clearIframeEmbedTimeout();
31527
+ return;
31528
+ }
31529
+ clearIframeEmbedTimeout();
31530
+ iframeEmbedTimeoutRef.current = setTimeout(() => {
31531
+ setEmbedError("This site may block embedding in Otto, or it took too long to load.");
31532
+ setIsLoading(false);
31533
+ setLoadingProgress(0);
31534
+ }, IFRAME_EMBED_TIMEOUT_MS);
31535
+ return clearIframeEmbedTimeout;
31536
+ }, [canRenderUrl, clearIframeEmbedTimeout, isLoading, useNativeBrowser]);
31537
+ useEffect56(() => () => {
31538
+ if (nativeBrowser?.isAvailable) {
31539
+ nativeBrowser.unmount(tab.id);
31540
+ }
31541
+ }, [nativeBrowser, tab.id]);
31542
+ useEffect56(() => {
31543
+ const nextUrl = normalizeBrowserUrl(tab.url);
31544
+ if (!nextUrl)
31545
+ return;
31546
+ setHistoryEntries((entries) => {
31547
+ if (entries[historyIndex] === nextUrl)
31548
+ return entries;
31549
+ const nextEntries = entries.slice(0, historyIndex + 1);
31550
+ nextEntries.push(nextUrl);
31551
+ setHistoryIndex(nextEntries.length - 1);
31552
+ setIsLoading(isEmbeddableUrl(nextUrl));
31553
+ return nextEntries;
31554
+ });
31555
+ }, [tab.url, historyIndex]);
31462
31556
  useEffect56(() => {
31463
31557
  const url = simulatorStatus.data?.url ?? SIMULATOR_URL;
31464
31558
  if (tab.kind === "simulator" && simulatorStatus.data?.status === "connected" && url !== tab.url) {
@@ -31468,25 +31562,113 @@ function BrowserViewerPanel({ tab }) {
31468
31562
  });
31469
31563
  }
31470
31564
  }, [openBrowserTab, simulatorStatus.data, tab.kind, tab.url]);
31471
- const normalizedUrl = normalizeBrowserUrl(tab.url);
31472
- const canRenderUrl = isEmbeddableUrl(normalizedUrl);
31565
+ useEffect56(() => {
31566
+ if (!useNativeBrowser || !nativeBrowser?.isAvailable)
31567
+ return;
31568
+ const content = contentRef.current;
31569
+ if (!content)
31570
+ return;
31571
+ let cancelled = false;
31572
+ const mountNativeBrowser = () => {
31573
+ const rect = content.getBoundingClientRect();
31574
+ const visible = rect.width > 1 && rect.height > 1;
31575
+ if (!visible) {
31576
+ nativeBrowser.setVisible(tab.id, false);
31577
+ return;
31578
+ }
31579
+ nativeBrowser.mount({
31580
+ id: tab.id,
31581
+ url: normalizedUrl,
31582
+ reloadKey: tab.reloadKey,
31583
+ bounds: {
31584
+ x: rect.x,
31585
+ y: rect.y,
31586
+ width: rect.width,
31587
+ height: rect.height
31588
+ },
31589
+ visible
31590
+ }).then(() => {
31591
+ if (!cancelled && isLoadingRef.current)
31592
+ completeLoading();
31593
+ }).catch((error) => {
31594
+ if (cancelled)
31595
+ return;
31596
+ const message = error instanceof Error ? error.message : "Unable to open the native desktop webview.";
31597
+ setEmbedError(message);
31598
+ setIsLoading(false);
31599
+ setLoadingProgress(0);
31600
+ });
31601
+ };
31602
+ mountNativeBrowser();
31603
+ const resizeObserver = new ResizeObserver(mountNativeBrowser);
31604
+ resizeObserver.observe(content);
31605
+ window.addEventListener("resize", mountNativeBrowser);
31606
+ return () => {
31607
+ cancelled = true;
31608
+ resizeObserver.disconnect();
31609
+ window.removeEventListener("resize", mountNativeBrowser);
31610
+ };
31611
+ }, [
31612
+ completeLoading,
31613
+ nativeBrowser,
31614
+ normalizedUrl,
31615
+ tab.id,
31616
+ tab.reloadKey,
31617
+ useNativeBrowser
31618
+ ]);
31473
31619
  const navigate = (value) => {
31474
31620
  const nextUrl = normalizeBrowserUrl(value);
31621
+ if (!nextUrl)
31622
+ return;
31623
+ setEmbedError(null);
31624
+ setIsLoading(isEmbeddableUrl(nextUrl));
31625
+ setLoadingProgress(isEmbeddableUrl(nextUrl) ? 12 : 0);
31626
+ setHistoryEntries((entries) => {
31627
+ const currentUrl = entries[historyIndex];
31628
+ if (currentUrl === nextUrl)
31629
+ return entries;
31630
+ const nextEntries = entries.slice(0, historyIndex + 1);
31631
+ nextEntries.push(nextUrl);
31632
+ setHistoryIndex(nextEntries.length - 1);
31633
+ return nextEntries;
31634
+ });
31635
+ updateBrowserTabUrl(tab.id, nextUrl);
31636
+ };
31637
+ const goToHistoryIndex = (index) => {
31638
+ const nextUrl = historyEntries[index];
31639
+ if (!nextUrl)
31640
+ return;
31641
+ setHistoryIndex(index);
31642
+ setEmbedError(null);
31643
+ setIsLoading(isEmbeddableUrl(nextUrl));
31644
+ setLoadingProgress(isEmbeddableUrl(nextUrl) ? 12 : 0);
31475
31645
  updateBrowserTabUrl(tab.id, nextUrl);
31476
31646
  };
31647
+ const stopLoading = () => {
31648
+ try {
31649
+ iframeRef.current?.contentWindow?.stop();
31650
+ } catch {}
31651
+ setIsLoading(false);
31652
+ setLoadingProgress(0);
31653
+ clearIframeEmbedTimeout();
31654
+ if (nativeBrowser?.isAvailable) {
31655
+ nativeBrowser.setVisible(tab.id, false);
31656
+ }
31657
+ };
31477
31658
  const simulatorError = tab.kind === "simulator" && simulatorStatus.data?.status === "error" ? simulatorStatus.data.error : null;
31478
31659
  return /* @__PURE__ */ jsxs106("div", {
31479
31660
  className: "h-full w-full min-w-0 bg-background flex flex-col",
31480
31661
  children: [
31481
31662
  /* @__PURE__ */ jsx122("div", {
31482
- className: "shrink-0 border-b border-border bg-[#0b0b0d] text-zinc-400",
31663
+ className: "shrink-0 border-b border-border bg-sidebar text-muted-foreground",
31483
31664
  children: /* @__PURE__ */ jsxs106("div", {
31484
- className: "flex h-11 items-center gap-1 px-3",
31665
+ className: "relative flex h-11 items-center gap-1 px-3",
31485
31666
  children: [
31486
31667
  /* @__PURE__ */ jsx122("button", {
31487
31668
  type: "button",
31488
- disabled: true,
31489
- className: "flex h-8 w-8 items-center justify-center rounded-md text-zinc-600",
31669
+ onClick: () => goToHistoryIndex(historyIndex - 1),
31670
+ disabled: !canGoBack,
31671
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground/40 disabled:hover:bg-transparent",
31490
31672
  title: "Back",
31491
31673
  children: /* @__PURE__ */ jsx122(ChevronLeft2, {
31492
31674
  className: "h-4 w-4"
@@ -31494,8 +31676,9 @@ function BrowserViewerPanel({ tab }) {
31494
31676
  }),
31495
31677
  /* @__PURE__ */ jsx122("button", {
31496
31678
  type: "button",
31497
- disabled: true,
31498
- className: "flex h-8 w-8 items-center justify-center rounded-md text-zinc-600",
31679
+ onClick: () => goToHistoryIndex(historyIndex + 1),
31680
+ disabled: !canGoForward,
31681
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground/40 disabled:hover:bg-transparent",
31499
31682
  title: "Forward",
31500
31683
  children: /* @__PURE__ */ jsx122(ChevronRight18, {
31501
31684
  className: "h-4 w-4"
@@ -31503,25 +31686,45 @@ function BrowserViewerPanel({ tab }) {
31503
31686
  }),
31504
31687
  /* @__PURE__ */ jsx122("button", {
31505
31688
  type: "button",
31506
- onClick: () => reloadBrowserTab(tab.id),
31507
- disabled: !canRenderUrl,
31508
- title: "Reload",
31509
- className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-white/5 hover:text-zinc-200 disabled:text-zinc-600 disabled:hover:bg-transparent",
31510
- children: /* @__PURE__ */ jsx122(RefreshCw12, {
31689
+ onClick: () => {
31690
+ if (isLoading) {
31691
+ stopLoading();
31692
+ return;
31693
+ }
31694
+ setEmbedError(null);
31695
+ setIsLoading(true);
31696
+ setLoadingProgress(12);
31697
+ reloadBrowserTab(tab.id);
31698
+ },
31699
+ disabled: !isLoading && !canRenderUrl,
31700
+ title: isLoading ? "Stop loading" : "Reload",
31701
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground/40 disabled:hover:bg-transparent",
31702
+ children: isLoading ? /* @__PURE__ */ jsx122(X23, {
31703
+ className: "h-4 w-4"
31704
+ }) : /* @__PURE__ */ jsx122(RefreshCw12, {
31511
31705
  className: "h-4 w-4"
31512
31706
  })
31513
31707
  }),
31514
31708
  /* @__PURE__ */ jsx122("form", {
31515
- className: "mx-2 min-w-48 flex-1",
31709
+ className: "relative mx-2 min-w-48 flex-1",
31516
31710
  onSubmit: (event) => {
31517
31711
  event.preventDefault();
31518
31712
  navigate(draftUrl);
31519
31713
  },
31520
- children: /* @__PURE__ */ jsx122("input", {
31521
- value: draftUrl,
31522
- onChange: (event) => setDraftUrl(event.target.value),
31523
- placeholder: "localhost:3000 or https://example.com",
31524
- className: "h-8 w-full rounded-md border border-zinc-800 bg-black/30 px-3 font-mono text-xs text-zinc-300 outline-none placeholder:text-zinc-600 focus:border-zinc-600"
31714
+ children: /* @__PURE__ */ jsxs106("div", {
31715
+ className: "relative h-8 overflow-hidden rounded-md border border-input bg-muted/30 focus-within:border-ring",
31716
+ children: [
31717
+ loadingProgress > 0 && /* @__PURE__ */ jsx122("div", {
31718
+ className: "absolute inset-y-0 left-0 bg-primary/10 transition-[width] duration-200 ease-out",
31719
+ style: { width: `${loadingProgress}%` }
31720
+ }),
31721
+ /* @__PURE__ */ jsx122("input", {
31722
+ value: draftUrl,
31723
+ onChange: (event) => setDraftUrl(event.target.value),
31724
+ placeholder: "localhost:3000 or https://example.com",
31725
+ className: "relative z-10 h-full w-full bg-transparent px-3 font-mono text-xs text-foreground outline-none placeholder:text-muted-foreground/60"
31726
+ })
31727
+ ]
31525
31728
  })
31526
31729
  }),
31527
31730
  /* @__PURE__ */ jsx122("button", {
@@ -31529,7 +31732,7 @@ function BrowserViewerPanel({ tab }) {
31529
31732
  onClick: () => window.open(normalizedUrl, "_blank", "noopener,noreferrer"),
31530
31733
  disabled: !canRenderUrl,
31531
31734
  title: "Open externally",
31532
- className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-white/5 hover:text-zinc-200 disabled:text-zinc-600 disabled:hover:bg-transparent",
31735
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground disabled:text-muted-foreground/40 disabled:hover:bg-transparent",
31533
31736
  children: /* @__PURE__ */ jsx122(ExternalLink11, {
31534
31737
  className: "h-4 w-4"
31535
31738
  })
@@ -31542,7 +31745,7 @@ function BrowserViewerPanel({ tab }) {
31542
31745
  newTab: true
31543
31746
  }),
31544
31747
  title: "New browser preview",
31545
- className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-white/5 hover:text-zinc-200",
31748
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground",
31546
31749
  children: /* @__PURE__ */ jsx122(Plus9, {
31547
31750
  className: "h-4 w-4"
31548
31751
  })
@@ -31554,7 +31757,7 @@ function BrowserViewerPanel({ tab }) {
31554
31757
  title: "Simulator"
31555
31758
  }),
31556
31759
  title: "Simulator preview",
31557
- className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-white/5 hover:text-zinc-200",
31760
+ className: "flex h-8 w-8 items-center justify-center rounded-md transition-colors hover:bg-accent hover:text-accent-foreground",
31558
31761
  children: /* @__PURE__ */ jsx122(Smartphone, {
31559
31762
  className: "h-4 w-4"
31560
31763
  })
@@ -31567,13 +31770,67 @@ function BrowserViewerPanel({ tab }) {
31567
31770
  children: simulatorError
31568
31771
  }),
31569
31772
  /* @__PURE__ */ jsx122("div", {
31773
+ ref: contentRef,
31570
31774
  className: "min-h-0 flex-1 bg-muted/20",
31571
- children: canRenderUrl ? /* @__PURE__ */ jsx122("iframe", {
31775
+ children: useNativeBrowser && canRenderUrl ? /* @__PURE__ */ jsx122("div", {
31776
+ className: "h-full w-full bg-background"
31777
+ }) : canRenderUrl && !embedError ? /* @__PURE__ */ jsx122("iframe", {
31778
+ ref: iframeRef,
31572
31779
  title: tab.title,
31573
31780
  src: normalizedUrl,
31781
+ onLoad: completeLoading,
31574
31782
  className: "h-full w-full border-0 bg-background",
31575
31783
  allow: "clipboard-read; clipboard-write; fullscreen; microphone; camera; geolocation; autoplay"
31576
- }, `${tab.id}:${tab.reloadKey}`) : /* @__PURE__ */ jsx122("div", {
31784
+ }, `${tab.id}:${tab.reloadKey}`) : embedError ? /* @__PURE__ */ jsx122("div", {
31785
+ className: "h-full w-full flex items-center justify-center p-6 text-center",
31786
+ children: /* @__PURE__ */ jsxs106("div", {
31787
+ className: "max-w-md rounded-lg border border-border bg-background p-6 shadow-sm",
31788
+ children: [
31789
+ /* @__PURE__ */ jsx122(Globe23, {
31790
+ className: "mx-auto mb-3 h-8 w-8 text-muted-foreground"
31791
+ }),
31792
+ /* @__PURE__ */ jsx122("h2", {
31793
+ className: "mb-2 text-sm font-semibold text-foreground",
31794
+ children: "This site can't be embedded"
31795
+ }),
31796
+ /* @__PURE__ */ jsx122("p", {
31797
+ className: "mb-4 text-xs leading-relaxed text-muted-foreground",
31798
+ children: embedError
31799
+ }),
31800
+ /* @__PURE__ */ jsxs106("div", {
31801
+ className: "flex flex-wrap justify-center gap-2",
31802
+ children: [
31803
+ /* @__PURE__ */ jsx122(Button, {
31804
+ type: "button",
31805
+ variant: "secondary",
31806
+ size: "sm",
31807
+ onClick: () => {
31808
+ setEmbedError(null);
31809
+ setIsLoading(true);
31810
+ setLoadingProgress(12);
31811
+ reloadBrowserTab(tab.id);
31812
+ },
31813
+ children: "Try again"
31814
+ }),
31815
+ nativeBrowser?.openWindow && /* @__PURE__ */ jsx122(Button, {
31816
+ type: "button",
31817
+ variant: "secondary",
31818
+ size: "sm",
31819
+ onClick: () => nativeBrowser.openWindow?.(normalizedUrl),
31820
+ children: "Open in desktop window"
31821
+ }),
31822
+ /* @__PURE__ */ jsx122(Button, {
31823
+ type: "button",
31824
+ variant: "secondary",
31825
+ size: "sm",
31826
+ onClick: () => window.open(normalizedUrl, "_blank", "noopener,noreferrer"),
31827
+ children: "Open externally"
31828
+ })
31829
+ ]
31830
+ })
31831
+ ]
31832
+ })
31833
+ }) : /* @__PURE__ */ jsx122("div", {
31577
31834
  className: "h-full w-full flex items-center justify-center p-6 text-center",
31578
31835
  children: /* @__PURE__ */ jsxs106("div", {
31579
31836
  className: "max-w-md rounded-lg border border-border bg-background p-6 shadow-sm",
@@ -31608,7 +31865,7 @@ function BrowserViewerPanel({ tab }) {
31608
31865
  }
31609
31866
  // src/components/workspace/ViewerTabs.tsx
31610
31867
  import { memo as memo50, useEffect as useEffect57 } from "react";
31611
- import { Code2, GitCommit as GitCommit5, Globe2 as Globe24, Smartphone as Smartphone2, X as X23 } from "lucide-react";
31868
+ import { Code2, GitCommit as GitCommit5, Globe2 as Globe24, Smartphone as Smartphone2, X as X24 } from "lucide-react";
31612
31869
  import { jsx as jsx123, jsxs as jsxs107 } from "react/jsx-runtime";
31613
31870
  function tabKindLabel(tab) {
31614
31871
  switch (tab.type) {
@@ -31778,6 +32035,8 @@ function renderTabContent(tab, closeTab, updateSessionFileOperationIndex) {
31778
32035
  });
31779
32036
  }
31780
32037
  }
32038
+ var VIEWER_MODE_TAB_BUTTON_BASE = "absolute top-0.5 z-20 h-7 w-9 rounded-full p-0 leading-none transform-none select-none bg-transparent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring active:transform-none";
32039
+ var VIEWER_MODE_ICON_BASE_CLASS = "pointer-events-none absolute top-1/2 z-10 block h-4 w-4 -translate-x-1/2 -translate-y-1/2 shrink-0 transition-colors";
31781
32040
  var ViewerTabs = memo50(function ViewerTabs2() {
31782
32041
  const tabs = useViewerTabsStore((state) => state.tabs);
31783
32042
  const activeTabId = useViewerTabsStore((state) => state.activeTabId);
@@ -31830,28 +32089,31 @@ var ViewerTabs = memo50(function ViewerTabs2() {
31830
32089
  children: /* @__PURE__ */ jsxs107("div", {
31831
32090
  role: "tablist",
31832
32091
  "aria-label": "Viewer mode",
31833
- className: "relative h-8 inline-flex items-center rounded-full ring-1 ring-inset ring-sidebar-border bg-muted/40 p-0.5",
32092
+ className: "relative h-8 w-[4.75rem] shrink-0 rounded-full ring-1 ring-inset ring-sidebar-border bg-muted/40 p-0.5",
31834
32093
  children: [
31835
32094
  /* @__PURE__ */ jsx123("span", {
31836
32095
  "aria-hidden": "true",
31837
32096
  className: `absolute left-0.5 inset-y-0.5 w-9 rounded-full bg-background shadow-sm ring-1 ring-sidebar-border transition-transform duration-200 ease-out pointer-events-none ${activeMode === "preview" ? "translate-x-9" : "translate-x-0"}`
31838
32097
  }),
31839
- /* @__PURE__ */ jsxs107("button", {
32098
+ /* @__PURE__ */ jsx123(Code2, {
32099
+ "aria-hidden": "true",
32100
+ className: `${VIEWER_MODE_ICON_BASE_CLASS} left-5 ${activeMode === "work" ? "text-foreground" : "text-muted-foreground/70"}`
32101
+ }),
32102
+ /* @__PURE__ */ jsx123(Globe24, {
32103
+ "aria-hidden": "true",
32104
+ className: `${VIEWER_MODE_ICON_BASE_CLASS} left-14 ${activeMode === "preview" ? "text-foreground" : "text-muted-foreground/70"}`
32105
+ }),
32106
+ showWorkActivityDot && /* @__PURE__ */ jsx123("span", {
32107
+ className: "pointer-events-none absolute left-[2rem] top-1 z-10 h-1.5 w-1.5 rounded-full bg-primary"
32108
+ }),
32109
+ /* @__PURE__ */ jsx123("button", {
31840
32110
  type: "button",
31841
32111
  role: "tab",
31842
32112
  "aria-selected": activeMode === "work",
31843
32113
  onClick: () => setViewerMode("work"),
31844
32114
  title: "Work tabs",
31845
32115
  "aria-label": "Work tabs",
31846
- className: `relative z-10 h-7 w-9 inline-flex items-center justify-center rounded-full p-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors ${activeMode === "work" ? "text-foreground" : "text-muted-foreground/70 hover:text-foreground"}`,
31847
- children: [
31848
- /* @__PURE__ */ jsx123(Code2, {
31849
- className: "h-4 w-4"
31850
- }),
31851
- showWorkActivityDot && /* @__PURE__ */ jsx123("span", {
31852
- className: "absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-primary"
31853
- })
31854
- ]
32116
+ className: `${VIEWER_MODE_TAB_BUTTON_BASE} left-0.5`
31855
32117
  }),
31856
32118
  /* @__PURE__ */ jsx123("button", {
31857
32119
  type: "button",
@@ -31860,10 +32122,7 @@ var ViewerTabs = memo50(function ViewerTabs2() {
31860
32122
  onClick: handlePreviewMode,
31861
32123
  title: "Preview tabs",
31862
32124
  "aria-label": "Preview tabs",
31863
- className: `relative z-10 h-7 w-9 inline-flex items-center justify-center rounded-full p-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors ${activeMode === "preview" ? "text-foreground" : "text-muted-foreground/70 hover:text-foreground"}`,
31864
- children: /* @__PURE__ */ jsx123(Globe24, {
31865
- className: "h-4 w-4"
31866
- })
32125
+ className: `${VIEWER_MODE_TAB_BUTTON_BASE} left-[2.375rem]`
31867
32126
  })
31868
32127
  ]
31869
32128
  })
@@ -31875,7 +32134,7 @@ var ViewerTabs = memo50(function ViewerTabs2() {
31875
32134
  const isActive = tab.id === activeTab.id;
31876
32135
  const activityKind = getTabActivityKind(tab);
31877
32136
  return /* @__PURE__ */ jsxs107("div", {
31878
- className: `group h-12 w-44 max-w-56 shrink-0 px-3 border-r border-sidebar-border flex items-center gap-2 text-left transition-colors ${isActive ? "bg-sidebar text-sidebar-foreground" : "border-b bg-background text-muted-foreground/70 hover:text-foreground hover:bg-sidebar-accent/40"}`,
32137
+ className: `group h-12 w-44 max-w-56 shrink-0 px-3 border-r border-b border-sidebar-border flex items-center gap-2 text-left transition-colors ${isActive ? "border-b-transparent bg-sidebar text-sidebar-foreground" : "bg-background text-muted-foreground/70 hover:text-foreground hover:bg-sidebar-accent/40"}`,
31879
32138
  title: `${tab.title}
31880
32139
  ${tabKindLabel(tab)}`,
31881
32140
  children: [
@@ -31900,7 +32159,7 @@ ${tabKindLabel(tab)}`,
31900
32159
  },
31901
32160
  title: "Close tab",
31902
32161
  className: "h-6 w-6 opacity-60 group-hover:opacity-100 shrink-0",
31903
- children: /* @__PURE__ */ jsx123(X23, {
32162
+ children: /* @__PURE__ */ jsx123(X24, {
31904
32163
  className: "h-3.5 w-3.5"
31905
32164
  })
31906
32165
  })
@@ -31918,7 +32177,7 @@ ${tabKindLabel(tab)}`,
31918
32177
  title: "Close all tabs and collapse viewer",
31919
32178
  "aria-label": "Close all tabs and collapse viewer",
31920
32179
  className: "h-8 w-8 inline-flex items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-destructive/10 hover:text-destructive",
31921
- children: /* @__PURE__ */ jsx123(X23, {
32180
+ children: /* @__PURE__ */ jsx123(X24, {
31922
32181
  className: "h-3.5 w-3.5"
31923
32182
  })
31924
32183
  })
@@ -31927,12 +32186,28 @@ ${tabKindLabel(tab)}`,
31927
32186
  })
31928
32187
  ]
31929
32188
  }),
31930
- /* @__PURE__ */ jsx123("div", {
31931
- className: "flex-1 min-h-0 overflow-hidden",
31932
- children: activeTab ? renderTabContent(activeTab, closeTab, updateSessionFileOperationIndex) : /* @__PURE__ */ jsx123("div", {
31933
- className: "flex h-full items-center justify-center bg-sidebar text-muted-foreground/60 text-sm",
31934
- children: activeMode === "work" ? "No work tabs open" : "No preview tabs open"
31935
- })
32189
+ /* @__PURE__ */ jsxs107("div", {
32190
+ className: "relative flex-1 min-h-0 overflow-hidden",
32191
+ children: [
32192
+ previewTabs.map((tab) => {
32193
+ const isActive = activeMode === "preview" && tab.id === activeTab?.id;
32194
+ return /* @__PURE__ */ jsx123("div", {
32195
+ "aria-hidden": !isActive,
32196
+ className: `absolute inset-0 ${isActive ? "block" : "hidden"}`,
32197
+ children: /* @__PURE__ */ jsx123(BrowserViewerPanel, {
32198
+ tab
32199
+ })
32200
+ }, tab.id);
32201
+ }),
32202
+ activeMode === "work" && activeTab && /* @__PURE__ */ jsx123("div", {
32203
+ className: "absolute inset-0",
32204
+ children: renderTabContent(activeTab, closeTab, updateSessionFileOperationIndex)
32205
+ }),
32206
+ !activeTab && /* @__PURE__ */ jsx123("div", {
32207
+ className: "absolute inset-0 flex items-center justify-center bg-sidebar text-muted-foreground/60 text-sm",
32208
+ children: activeMode === "work" ? "No work tabs open" : "No preview tabs open"
32209
+ })
32210
+ ]
31936
32211
  })
31937
32212
  ]
31938
32213
  });
@@ -31941,12 +32216,12 @@ ${tabKindLabel(tab)}`,
31941
32216
  import { memo as memo53, useEffect as useEffect60 } from "react";
31942
32217
 
31943
32218
  // src/components/onboarding/steps/ProviderSetupStep.tsx
31944
- import { memo as memo51, useEffect as useEffect58, useState as useState53, useRef as useRef38 } from "react";
32219
+ import { memo as memo51, useEffect as useEffect58, useState as useState53, useRef as useRef39 } from "react";
31945
32220
  import {
31946
32221
  Copy as Copy5,
31947
32222
  Check as Check16,
31948
32223
  CreditCard as CreditCard4,
31949
- X as X24,
32224
+ X as X25,
31950
32225
  Key as Key2,
31951
32226
  ExternalLink as ExternalLink12,
31952
32227
  ArrowRight as ArrowRight3,
@@ -32031,20 +32306,20 @@ var ProviderSetupStep = memo51(function ProviderSetupStep2({
32031
32306
  const [copilotCodeCopied, setCopilotCodeCopied] = useState53(false);
32032
32307
  const [copilotModalOpen, setCopilotModalOpen] = useState53(false);
32033
32308
  const [copilotLoading, setCopilotLoading] = useState53(false);
32034
- const copilotPollRef = useRef38(undefined);
32035
- const copilotCancelledRef = useRef38(false);
32036
- const copilotPollFnRef = useRef38(onPollCopilotDeviceFlow);
32309
+ const copilotPollRef = useRef39(undefined);
32310
+ const copilotCancelledRef = useRef39(false);
32311
+ const copilotPollFnRef = useRef39(onPollCopilotDeviceFlow);
32037
32312
  copilotPollFnRef.current = onPollCopilotDeviceFlow;
32038
32313
  const balance = useOttoRouterStore((s) => s.balance);
32039
32314
  const usdcBalance = useOttoRouterStore((s) => s.usdcBalance);
32040
32315
  const payg = useOttoRouterStore((s) => s.payg);
32041
32316
  const subscription = useOttoRouterStore((s) => s.subscription);
32042
32317
  const isBalanceLoading = useOttoRouterStore((s) => s.isLoading);
32043
- const apiKeyInputRef = useRef38(null);
32044
- const oauthCodeInputRef = useRef38(null);
32045
- const importPrivateKeyRef = useRef38(null);
32318
+ const apiKeyInputRef = useRef39(null);
32319
+ const oauthCodeInputRef = useRef39(null);
32320
+ const importPrivateKeyRef = useRef39(null);
32046
32321
  const isTopupModalOpen = useOttoRouterStore((s) => s.isTopupModalOpen);
32047
- const prevTopupModalOpen = useRef38(false);
32322
+ const prevTopupModalOpen = useRef39(false);
32048
32323
  const { fetchBalance } = useOttoRouterBalance("ottorouter");
32049
32324
  const effectivePayg = payg?.effectiveSpendableUsd ?? balance ?? 0;
32050
32325
  const setuStatusLabel = subscription?.active ? `GO ${(subscription.creditsRemaining ?? 0).toFixed(1)} credits` : `$${effectivePayg.toFixed(2)}`;
@@ -32743,7 +33018,7 @@ var ProviderSetupStep = memo51(function ProviderSetupStep2({
32743
33018
  type: "button",
32744
33019
  onClick: () => handleRemoveProvider(id),
32745
33020
  className: "ml-1 p-1 text-green-600/40 dark:text-green-500/40 hover:text-green-600/80 dark:hover:text-green-500/80 opacity-0 group-hover:opacity-100 transition-opacity",
32746
- children: /* @__PURE__ */ jsx124(X24, {
33021
+ children: /* @__PURE__ */ jsx124(X25, {
32747
33022
  className: "w-3 h-3"
32748
33023
  })
32749
33024
  })
@@ -32802,7 +33077,7 @@ var ProviderSetupStep = memo51(function ProviderSetupStep2({
32802
33077
  setApiKeyInput("");
32803
33078
  },
32804
33079
  className: "shrink-0 p-1.5 text-muted-foreground hover:text-foreground",
32805
- children: /* @__PURE__ */ jsx124(X24, {
33080
+ children: /* @__PURE__ */ jsx124(X25, {
32806
33081
  className: "w-4 h-4"
32807
33082
  })
32808
33083
  })
@@ -33563,7 +33838,7 @@ var ProviderSetupStep = memo51(function ProviderSetupStep2({
33563
33838
  });
33564
33839
 
33565
33840
  // src/components/onboarding/steps/DefaultsStep.tsx
33566
- import { memo as memo52, useState as useState54, useEffect as useEffect59, useId as useId5, useRef as useRef39 } from "react";
33841
+ import { memo as memo52, useState as useState54, useEffect as useEffect59, useId as useId5, useRef as useRef40 } from "react";
33567
33842
  import { ArrowLeft, Sparkles as Sparkles9, ChevronDown as ChevronDown15 } from "lucide-react";
33568
33843
  import { jsx as jsx125, jsxs as jsxs109, Fragment as Fragment48 } from "react/jsx-runtime";
33569
33844
  var DefaultsStep = memo52(function DefaultsStep2({
@@ -33581,7 +33856,7 @@ var DefaultsStep = memo52(function DefaultsStep2({
33581
33856
  const [selectedAgent, setSelectedAgent] = useState54(authStatus.defaults.agent || "build");
33582
33857
  const [selectedApproval, setSelectedApproval] = useState54(authStatus.defaults.toolApproval || "dangerous");
33583
33858
  const [guidedMode, setGuidedMode] = useState54(false);
33584
- const hasUserChangedProvider = useRef39(false);
33859
+ const hasUserChangedProvider = useRef40(false);
33585
33860
  const providerId = useId5();
33586
33861
  const modelId = useId5();
33587
33862
  const agentId = useId5();
@@ -34040,7 +34315,7 @@ var OnboardingModal = memo53(function OnboardingModal2({
34040
34315
  });
34041
34316
  });
34042
34317
  // src/components/dashboard/UsageDashboard.tsx
34043
- import { useCallback as useCallback37, useEffect as useEffect61, useMemo as useMemo33, useState as useState55 } from "react";
34318
+ import { useCallback as useCallback38, useEffect as useEffect61, useMemo as useMemo33, useState as useState55 } from "react";
34044
34319
  import { AlertTriangle as AlertTriangle3, ArrowLeft as ArrowLeft2, Globe2 as Globe25, RefreshCw as RefreshCw14 } from "lucide-react";
34045
34320
  import { jsx as jsx127, jsxs as jsxs111, Fragment as Fragment49 } from "react/jsx-runtime";
34046
34321
  function formatNumber(n) {
@@ -34618,7 +34893,7 @@ function UsageDashboard({ onBack }) {
34618
34893
  const [loading, setLoading] = useState55(false);
34619
34894
  const [error, setError] = useState55(null);
34620
34895
  const [scope, setScope] = useState55("project");
34621
- const fetchStats = useCallback37(async () => {
34896
+ const fetchStats = useCallback38(async () => {
34622
34897
  setLoading(true);
34623
34898
  setError(null);
34624
34899
  try {
@@ -34633,7 +34908,7 @@ function UsageDashboard({ onBack }) {
34633
34908
  useEffect61(() => {
34634
34909
  fetchStats();
34635
34910
  }, [fetchStats]);
34636
- const handleBack = useCallback37(() => {
34911
+ const handleBack = useCallback38(() => {
34637
34912
  if (onBack)
34638
34913
  return onBack();
34639
34914
  if (typeof window === "undefined")
@@ -35081,4 +35356,4 @@ export {
35081
35356
  AssistantMessageGroup
35082
35357
  };
35083
35358
 
35084
- //# debugId=3DAA67F0EB745CD264756E2164756E21
35359
+ //# debugId=857870757EF1E6EC64756E2164756E21