'use client'; // Host-rendered modal for plugin-requested confirm/alert dialogs. // Subscribes to the host-dialog queue and renders the head request, one at // a time. Closing the modal advances the queue. import React, { useEffect, useSyncExternalStore } from 'react'; import { head, resolveHead, subscribe } from '@/lib/plugin-sandbox/host-dialog'; // Lightweight **bold** support in plugin dialog messages. Everything else is // rendered literally (newlines come from the parent's white-space: pre-wrap). // Splitting on the ** delimiter yields alternating plain/bold segments (odd // indices are bold). Plugins control these strings, so the delimiters balance. function renderMessage(message?: string): React.ReactNode { if (!message) return null; return message.split('**').map((seg, i) => i % 2 === 1 ? {seg} : {seg}, ); } export function PluginDialogHost(): React.JSX.Element | null { const current = useSyncExternalStore(subscribe, head, () => null); useEffect(() => { if (!current) return; function onKey(e: KeyboardEvent) { if (e.key === 'Escape') { e.preventDefault(); resolveHead(false); } else if (e.key === 'Enter') { e.preventDefault(); resolveHead(true); } } document.addEventListener('keydown', onKey, true); return () => document.removeEventListener('keydown', onKey, true); }, [current]); if (!current) return null; const confirmLabel = current.confirmLabel ?? (current.kind === 'alert' ? 'OK' : 'Confirm'); const cancelLabel = current.cancelLabel ?? 'Cancel'; return (
{ if (e.target === e.currentTarget) resolveHead(false); }} >

{current.title}

{renderMessage(current.message)}

{current.kind === 'confirm' && ( )}
From plugin: {current.pluginId}
); }