import type { ToolWidgetStatus } from './toolWidgetUtils';
/**
* Pure helpers for the native artifact WebView preview — mirrors the web's
* inject-editor-theme.ts / artifact-theme-*.ts posture (frontend/apps/builder/
* src/pages/agent-editor/components/): the model markup is ALWAYS nested inside
* a host-controlled document whose
starts with the CSP, so the policy is
* parsed before any model byte can run.
*/
// Same directives as the web's CSP_META: resource loading stays permissive
// (https/inline/eval for Tailwind Play + CDNs) while programmatic egress is
// blocked — `connect-src 'none'` kills fetch/XHR/beacon/WebSocket,
// `form-action 'none'` kills form posts, `base-uri 'none'` blocks
// hijacking.
const CSP_META =
'';
const VIEWPORT_META = '';
// Google Fonts copy of the editor typeface — the WebView document can't reach
// the app's bundled fonts, same as the web's null-origin iframe.
const FONT_LINK = ''
+ ''
+ '';
// shadcn HSL palettes — verbatim from the web's artifact-theme-palette.ts so
// artifacts written against the editor's `bg-background text-foreground`
// conventions render with the same colors on native.
const LIGHT_VARS = `--background: 38 27% 94%; --foreground: 0 0% 4%; --card: 0 0% 100%;
--card-foreground: 0 0% 4%; --popover: 0 0% 100%; --popover-foreground: 0 0% 4%;
--primary: 21 42% 55%; --primary-foreground: 0 0% 100%; --secondary: 0 0% 96%;
--secondary-foreground: 0 0% 9%; --muted: 39 17% 93%; --muted-foreground: 0 0% 45%;
--accent: 0 0% 96%; --accent-foreground: 0 0% 9%; --destructive: 0 84% 60%;
--destructive-foreground: 0 0% 98%; --border: 0 0% 98%; --input: 0 0% 98%; --ring: 21 42% 55%;`;
const DARK_VARS = `--background: 220 5% 11%; --foreground: 0 0% 99%; --card: 0 1% 14%;
--card-foreground: 0 0% 99%; --popover: 0 1% 14%; --popover-foreground: 0 0% 99%;
--primary: 21 42% 55%; --primary-foreground: 0 0% 99%; --secondary: 0 0% 18%;
--secondary-foreground: 0 0% 99%; --muted: 0 0% 18%; --muted-foreground: 0 0% 65%;
--accent: 0 0% 18%; --accent-foreground: 0 0% 99%; --destructive: 0 62% 30%;
--destructive-foreground: 0 0% 99%; --border: 0 0% 16%; --input: 0 0% 16%; --ring: 21 42% 55%;`;
const FONT_SANS = "'Wix Madefor Text', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
// Base style: theme variables + the web styleBlock's opaque-document variant
// (the WebView is its own surface, not transparent chat inline like the web
// card). Bare `border` defaults to the theme token, as in the web base layer.
const STYLE_BLOCK = ``;
// Tailwind Play CDN + the same shadcn variable mapping as the web's
// TAILWIND_BLOCK, so the model's `bg-card rounded-md border` just works.
const TAILWIND_BLOCK = `
`;
// Popups are inert at the WebView layer (no `onOpenWindow` handler, multiple
// windows kept ON so Android's `target="_blank"`-bypasses-the-nav-guard bug
// can't apply) — this neutralizes them inside the document too, as defense in
// depth: `window.open` returns null and `target="_blank"` clicks are cancelled.
const POPUP_BLOCK = ``;
// Emitted in AND after the model markup: a model doc shipping
// `` is parsed as a duplicate token whose attributes
// get adopted onto our root AFTER the head copy ran — the post-body
// copy re-syncs so the host scheme wins (same trick as the web).
function darkClassScript(isDark: boolean): string {
return ``;
}
// Honor an explicit model ``, else `auto` so RTL content lays out
// via the browser's bidi auto-detect (web resolveDocDir parity).
function resolveDocDir(html: string): string {
const htmlTag = html.match(/]*>/i)?.[0];
return htmlTag?.match(/\bdir\s*=\s*["']?(rtl|ltr|auto)\b/i)?.[1]?.toLowerCase() ?? 'auto';
}
/**
* SVG artifacts need an HTML shell. Trust an explicit `type` arg; sniff the
* markup only when the type is missing.
*/
export function isSvgArtifact(type: string | undefined, source: string): boolean {
if (type) return type === 'image/svg+xml';
return /^\s*(?:<\?xml[^>]*>\s*)?(?:\s*)*