From f7472b3dbf23a8d095e8deecbc637f271ad0ff0f Mon Sep 17 00:00:00 2001 From: ciregenz Date: Thu, 16 Jul 2026 10:37:32 -0700 Subject: [PATCH] [eric] browser: recover a card that opens straight onto a JSON URL (initial-load path + homepage fallback) --- .../app/pages/Dashboard/cards/BrowserCard.tsx | 11 ++++++++-- frontend/src/shared/browserCommandHandler.ts | 20 +++++++++++++++---- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx b/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx index f8d8dd3f..5115b8ec 100644 --- a/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx +++ b/frontend/src/app/pages/Dashboard/cards/BrowserCard.tsx @@ -53,7 +53,7 @@ import { import { setLastInteractedBrowser } from '@/shared/browserFocus'; import BrowserFindBar from './BrowserFindBar'; import { useBrowserActivity } from '@/shared/useBrowserActivity'; -import { getActionLabel } from '@/shared/browserCommandHandler'; +import { getActionLabel, readDataDocument, recoverCardOffDataWall } from '@/shared/browserCommandHandler'; import { resolveInput, isGoogleSearch } from '@/shared/resolveUrl'; import BrowserAgentOverlay from './BrowserAgentOverlay'; import { useOverlayScrollPassthrough } from '../hooks/interaction/useOverlayScrollPassthrough'; @@ -313,7 +313,14 @@ const BrowserCard: React.FC = ({ const doLoad = () => { // Reaching dom-ready proves the webview survived Chromium's commit phase (the historical Windows mount segfault). Clear the crash-safety marker. if (isWindows) markWindowsWebviewSurvived(); - wv.loadURL(targetUrl).catch(() => {}); + wv.loadURL(targetUrl) + .then(async () => { + // If this card's own entry URL is a raw JSON/API endpoint, it paints an unreadable data + // wall; get it onto a real page. (The agent-navigate path is handled in handleNavigate; + // this covers the initial load, which never goes through the command handler.) + if (await readDataDocument(wv)) recoverCardOffDataWall(wv, targetUrl); + }) + .catch(() => {}); try { (wv as any).setVisualZoomLevelLimits?.(1, 1); (wv as any).setZoomFactor?.(1); diff --git a/frontend/src/shared/browserCommandHandler.ts b/frontend/src/shared/browserCommandHandler.ts index c7970d91..d45e7080 100644 --- a/frontend/src/shared/browserCommandHandler.ts +++ b/frontend/src/shared/browserCommandHandler.ts @@ -332,7 +332,8 @@ async function handleNavigate(wv: BrowserWebview, params: Record): // Chromium renders any JSON (or JSON-shaped text) response with its built-in viewer, so a navigate // to an API endpoint leaves the card showing a wall of raw data. contentType is the reliable tell; // re-fetch same-origin (the just-loaded GET, idempotent) to hand the agent the exact bytes. -async function readDataDocument(wv: BrowserWebview): Promise<{ contentType: string; body: string } | null> { +// Exported so BrowserCard can reuse the exact same detection for the card's own initial load. +export async function readDataDocument(wv: BrowserWebview): Promise<{ contentType: string; body: string } | null> { const code = `(async () => { const ct = String(document.contentType || '').toLowerCase(); const isJson = ct.startsWith('application/json'); @@ -356,14 +357,25 @@ async function readDataDocument(wv: BrowserWebview): Promise<{ contentType: stri // Get the card off a raw-data wall: step back to the real page the agent came from, or, if it opened // straight onto the data URL, fall back to the site homepage. Fire-and-forget, the agent already has -// the data; this is purely so a human sees a page instead of JSON. -function recoverCardOffDataWall(wv: BrowserWebview, dataUrl: string): void { +// the data; this is purely so a human sees a page instead of JSON. Exported for BrowserCard's own-load path. +export function recoverCardOffDataWall(wv: BrowserWebview, dataUrl: string): void { + let origin = ''; + try { origin = new URL(dataUrl).origin; } catch { /* keep '' */ } try { if (wv.canGoBack()) { wv.goBack(); + // A card that opened STRAIGHT onto the data URL has only the webview's blank initial entry + // behind it, so goBack lands on about:blank. Detect that and fall back to the site homepage + // so the card shows a real page rather than a blank one. + window.setTimeout(() => { + try { + const u = wv.getURL(); + if ((!u || u === 'about:blank') && origin) void wv.loadURL(origin).catch(() => {}); + } catch { /* leave as-is */ } + }, 400); return; } - void wv.loadURL(new URL(dataUrl).origin).catch(() => {}); + if (origin) void wv.loadURL(origin).catch(() => {}); } catch { /* leave the card as-is if recovery fails; the data still reached the agent */ }