@agent-native/core 0.7.20 → 0.7.21

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.
@@ -1 +1 @@
1
- {"version":3,"file":"NewWorkspaceAppFlow.d.ts","sourceRoot":"","sources":["../../src/client/NewWorkspaceAppFlow.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AA0DD,wBAAgB,mBAAmB,CAAC,EAClC,SAAqB,EACrB,SAAc,EACd,gBAAgB,GACjB,EAAE,wBAAwB,2CA+S1B"}
1
+ {"version":3,"file":"NewWorkspaceAppFlow.d.ts","sourceRoot":"","sources":["../../src/client/NewWorkspaceAppFlow.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AA0DD,wBAAgB,mBAAmB,CAAC,EAClC,SAAqB,EACrB,SAAc,EACd,gBAAgB,GACjB,EAAE,wBAAwB,2CA0S1B"}
@@ -188,13 +188,11 @@ export function NewWorkspaceAppFlow({ sourceApp = "starter", className = "", dis
188
188
  }
189
189
  return (_jsx("section", { className: `mx-auto flex w-full max-w-5xl flex-col gap-5 px-4 py-6 ${className}`, children: _jsxs("div", { className: "grid gap-5 lg:grid-cols-[minmax(0,1fr)_320px]", children: [_jsxs("div", { className: "rounded-lg border border-border bg-card", children: [_jsx("div", { className: "border-b border-border px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(IconPlus, { className: "h-4 w-4" }), "New app"] }) }), _jsxs("div", { className: "space-y-4 p-4", children: [_jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Prompt" }), _jsx("textarea", { value: prompt, onChange: (e) => setPrompt(e.target.value), placeholder: "Describe the app your teammate should be able to use...", rows: 6, className: "min-h-36 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring" })] }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-2", children: [_jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "App path" }), _jsx("input", { value: appName, onChange: (e) => setAppName(slugify(e.target.value)), placeholder: "customer-health", className: "h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring" })] }), _jsxs("label", { className: "block space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Starter template" }), _jsx("select", { value: template, onChange: (e) => setTemplate(e.target.value), className: "h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring", children: TEMPLATE_OPTIONS.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value))) })] })] }), _jsxs("button", { type: "button", onClick: submit, disabled: !canSubmit || isSubmitting, className: "inline-flex h-10 items-center gap-2 rounded-md bg-foreground px-4 text-sm font-medium text-background disabled:cursor-not-allowed disabled:opacity-40", children: [isSubmitting ? (_jsx(IconLoader2, { className: "h-4 w-4 animate-spin" })) : (_jsx(IconCode, { className: "h-4 w-4" })), "Create app"] }), statusMessage ? (_jsxs("div", { className: "rounded-md border border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground", children: [statusMessage, branchUrl ? (_jsxs("a", { href: branchUrl, target: "_blank", rel: "noreferrer", className: "ml-2 inline-flex items-center gap-1 font-medium text-foreground underline", children: ["Open branch ", _jsx(IconArrowUpRight, { className: "h-3 w-3" })] })) : null] })) : null] })] }), _jsxs("aside", { className: "rounded-lg border border-border bg-card", children: [_jsx("div", { className: "border-b border-border px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2 text-sm font-medium", children: [_jsx(IconKey, { className: "h-4 w-4" }), "Dispatch keys"] }) }), _jsx("div", { className: "max-h-[440px] space-y-2 overflow-y-auto p-3", children: secretsError ? (_jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground", children: secretsError })) : secrets.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground", children: "No Dispatch vault keys found yet." })) : (secrets.map((secret) => {
190
190
  const selected = selectedSecretIds.includes(secret.id);
191
- return (_jsxs("button", { type: "button", onClick: () => toggleSecret(secret.id), className: `flex w-full items-start gap-3 rounded-md border px-3 py-2 text-left text-sm transition ${selected
192
- ? "border-foreground bg-foreground text-background"
193
- : "border-border hover:bg-accent"}`, children: [_jsx("span", { className: `mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${selected
194
- ? "border-background"
195
- : "border-muted-foreground/40"}`, children: selected ? _jsx(IconCheck, { className: "h-3 w-3" }) : null }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate font-medium", children: secret.credentialKey }), _jsx("span", { className: `block truncate text-xs ${selected
196
- ? "text-background/70"
197
- : "text-muted-foreground"}`, children: secret.provider || secret.name })] })] }, secret.id));
191
+ return (_jsxs("button", { type: "button", "aria-pressed": selected, onClick: () => toggleSecret(secret.id), className: `group flex w-full items-start gap-3 rounded-md border px-3 py-2 text-left text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 ${selected
192
+ ? "border-ring/60 bg-muted/70 text-foreground"
193
+ : "border-border bg-background/30 text-foreground hover:border-muted-foreground/40 hover:bg-accent/45"}`, children: [_jsx("span", { className: `mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition ${selected
194
+ ? "border-ring bg-background text-foreground"
195
+ : "border-muted-foreground/35 text-transparent group-hover:border-muted-foreground/60"}`, children: selected ? _jsx(IconCheck, { className: "h-3 w-3" }) : null }), _jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "block truncate font-medium", children: secret.credentialKey }), _jsx("span", { className: "block truncate text-xs text-muted-foreground", children: secret.provider || secret.name })] })] }, secret.id));
198
196
  })) })] })] }) }));
199
197
  }
200
198
  //# sourceMappingURL=NewWorkspaceAppFlow.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NewWorkspaceAppFlow.js","sourceRoot":"","sources":["../../src/client/NewWorkspaceAppFlow.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport {\n IconArrowUpRight,\n IconCheck,\n IconCode,\n IconKey,\n IconLoader2,\n IconPlus,\n} from \"@tabler/icons-react\";\nimport { agentNativePath, appBasePath } from \"./api-path.js\";\nimport { sendToAgentChat } from \"./agent-chat.js\";\nimport { isInBuilderFrame } from \"./builder-frame.js\";\nimport { useDevMode } from \"./use-dev-mode.js\";\n\nexport interface VaultSecretOption {\n id: string;\n name: string;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\nexport interface NewWorkspaceAppFlowProps {\n sourceApp?: string;\n className?: string;\n dispatchBasePath?: string | null;\n}\n\nconst TEMPLATE_OPTIONS = [\n { value: \"starter\", label: \"Starter\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"calendar\", label: \"Calendar\" },\n { value: \"content\", label: \"Content\" },\n { value: \"design\", label: \"Design\" },\n { value: \"dispatch\", label: \"Dispatch\" },\n { value: \"forms\", label: \"Forms\" },\n { value: \"mail\", label: \"Mail\" },\n { value: \"slides\", label: \"Slides\" },\n { value: \"videos\", label: \"Videos\" },\n { value: \"clips\", label: \"Clips\" },\n] as const;\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 48);\n}\n\nfunction titleFromPrompt(prompt: string): string {\n const cleaned = prompt\n .replace(/\\b(build|create|make|an?|the|app|tool|dashboard)\\b/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n return slugify(cleaned || \"new-app\") || \"new-app\";\n}\n\nfunction actionUrl(basePath: string | null, action: string): string {\n const path = `/_agent-native/actions/${action}`;\n if (basePath === null) return agentNativePath(path);\n const normalized = basePath.replace(/\\/+$/, \"\");\n return `${normalized}${path}`;\n}\n\nfunction defaultDispatchBasePath(sourceApp?: string): string | null {\n if (sourceApp === \"dispatch\") return null;\n const base = appBasePath();\n if (base === \"/dispatch\") return null;\n return \"/dispatch\";\n}\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<any> {\n const res = await fetch(url, init);\n const data = await res.json().catch(() => null);\n if (!res.ok) {\n throw new Error(\n data?.error || data?.message || `Request failed ${res.status}`,\n );\n }\n return data;\n}\n\nexport function NewWorkspaceAppFlow({\n sourceApp = \"starter\",\n className = \"\",\n dispatchBasePath,\n}: NewWorkspaceAppFlowProps) {\n const [prompt, setPrompt] = useState(\"\");\n const [appName, setAppName] = useState(\"\");\n const [template, setTemplate] =\n useState<(typeof TEMPLATE_OPTIONS)[number][\"value\"]>(\"starter\");\n const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);\n const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);\n const [secretsError, setSecretsError] = useState<string | null>(null);\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [branchUrl, setBranchUrl] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const { isDevMode } = useDevMode();\n\n const effectiveDispatchBasePath =\n dispatchBasePath === undefined\n ? defaultDispatchBasePath(sourceApp)\n : dispatchBasePath;\n\n useEffect(() => {\n let cancelled = false;\n const url = actionUrl(\n effectiveDispatchBasePath,\n \"list-vault-secret-options\",\n );\n fetchJson(url)\n .then((data) => {\n if (cancelled) return;\n setSecrets(Array.isArray(data) ? data : []);\n setSecretsError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setSecrets([]);\n setSecretsError(err?.message || \"Could not load Dispatch keys\");\n });\n return () => {\n cancelled = true;\n };\n }, [effectiveDispatchBasePath]);\n\n useEffect(() => {\n if (appName || !prompt.trim()) return;\n setAppName(titleFromPrompt(prompt));\n }, [prompt, appName]);\n\n const selectedSecrets = useMemo(\n () => secrets.filter((secret) => selectedSecretIds.includes(secret.id)),\n [secrets, selectedSecretIds],\n );\n\n const canSubmit = prompt.trim().length > 0 && slugify(appName).length > 0;\n\n function buildMessage(): string {\n const safeAppName = slugify(appName) || titleFromPrompt(prompt);\n const keyList = selectedSecrets.map((s) => s.credentialKey).join(\", \");\n return [\n `Create a new agent-native app in this workspace.`,\n ``,\n `App name: ${safeAppName}`,\n `Template to start from: ${template}`,\n `User prompt: ${prompt.trim()}`,\n keyList\n ? `Dispatch vault keys selected for this app: ${keyList}`\n : `Dispatch vault keys selected for this app: none`,\n ``,\n `Use the workspace app layout: create it under apps/${safeAppName}, mount it at /${safeAppName}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,\n keyList\n ? `Grant the selected Dispatch vault keys to appId \"${safeAppName}\" and sync them once the app server is available.`\n : `Do not grant any Dispatch vault keys unless the user asks later.`,\n `When it is ready, start or update the dev server and navigate the user to /${safeAppName}.`,\n ].join(\"\\n\");\n }\n\n async function grantSelectedSecrets(safeAppName: string) {\n if (selectedSecretIds.length === 0) return;\n try {\n await fetchJson(\n actionUrl(effectiveDispatchBasePath, \"grant-vault-secrets-to-app\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n appId: safeAppName,\n secretIds: selectedSecretIds,\n }),\n },\n );\n } catch (err: any) {\n setStatusMessage(\n `The app request was prepared, but Dispatch grants could not be saved yet: ${err?.message || \"unknown error\"}`,\n );\n }\n }\n\n async function submit() {\n if (!canSubmit || isSubmitting) return;\n const safeAppName = slugify(appName) || titleFromPrompt(prompt);\n const message = buildMessage();\n setIsSubmitting(true);\n setStatusMessage(null);\n setBranchUrl(null);\n\n try {\n await grantSelectedSecrets(safeAppName);\n\n if (isInBuilderFrame()) {\n sendToAgentChat({ message, submit: true, type: \"code\" });\n setStatusMessage(\"Sent to Builder chat.\");\n } else if (isDevMode) {\n sendToAgentChat({ message, submit: true, type: \"code\", newTab: true });\n setStatusMessage(\"Sent to the local agent.\");\n } else {\n const result = await fetchJson(\n actionUrl(effectiveDispatchBasePath, \"start-workspace-app-creation\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n prompt: prompt.trim(),\n appId: safeAppName,\n template,\n secretIds: selectedSecretIds,\n preparedPrompt: message,\n }),\n },\n );\n if (result?.mode === \"builder\") {\n setBranchUrl(result?.url || null);\n setStatusMessage(\"Builder branch created.\");\n } else {\n setStatusMessage(\n result?.message ||\n \"Builder app creation is coming soon here. Open this workspace in Builder to create an app from this prompt.\",\n );\n }\n }\n } catch (err: any) {\n setStatusMessage(err?.message || \"Could not start the new app flow.\");\n } finally {\n setIsSubmitting(false);\n }\n }\n\n function toggleSecret(id: string) {\n setSelectedSecretIds((current) =>\n current.includes(id)\n ? current.filter((existing) => existing !== id)\n : [...current, id],\n );\n }\n\n return (\n <section\n className={`mx-auto flex w-full max-w-5xl flex-col gap-5 px-4 py-6 ${className}`}\n >\n <div className=\"grid gap-5 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <div className=\"rounded-lg border border-border bg-card\">\n <div className=\"border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n <IconPlus className=\"h-4 w-4\" />\n New app\n </div>\n </div>\n <div className=\"space-y-4 p-4\">\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Prompt\n </span>\n <textarea\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n placeholder=\"Describe the app your teammate should be able to use...\"\n rows={6}\n className=\"min-h-36 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring\"\n />\n </label>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n App path\n </span>\n <input\n value={appName}\n onChange={(e) => setAppName(slugify(e.target.value))}\n placeholder=\"customer-health\"\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring\"\n />\n </label>\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Starter template\n </span>\n <select\n value={template}\n onChange={(e) =>\n setTemplate(e.target.value as typeof template)\n }\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring\"\n >\n {TEMPLATE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n\n <button\n type=\"button\"\n onClick={submit}\n disabled={!canSubmit || isSubmitting}\n className=\"inline-flex h-10 items-center gap-2 rounded-md bg-foreground px-4 text-sm font-medium text-background disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {isSubmitting ? (\n <IconLoader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <IconCode className=\"h-4 w-4\" />\n )}\n Create app\n </button>\n\n {statusMessage ? (\n <div className=\"rounded-md border border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n {statusMessage}\n {branchUrl ? (\n <a\n href={branchUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"ml-2 inline-flex items-center gap-1 font-medium text-foreground underline\"\n >\n Open branch <IconArrowUpRight className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n ) : null}\n </div>\n </div>\n\n <aside className=\"rounded-lg border border-border bg-card\">\n <div className=\"border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n <IconKey className=\"h-4 w-4\" />\n Dispatch keys\n </div>\n </div>\n <div className=\"max-h-[440px] space-y-2 overflow-y-auto p-3\">\n {secretsError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {secretsError}\n </p>\n ) : secrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch vault keys found yet.\n </p>\n ) : (\n secrets.map((secret) => {\n const selected = selectedSecretIds.includes(secret.id);\n return (\n <button\n key={secret.id}\n type=\"button\"\n onClick={() => toggleSecret(secret.id)}\n className={`flex w-full items-start gap-3 rounded-md border px-3 py-2 text-left text-sm transition ${\n selected\n ? \"border-foreground bg-foreground text-background\"\n : \"border-border hover:bg-accent\"\n }`}\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border ${\n selected\n ? \"border-background\"\n : \"border-muted-foreground/40\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span\n className={`block truncate text-xs ${\n selected\n ? \"text-background/70\"\n : \"text-muted-foreground\"\n }`}\n >\n {secret.provider || secret.name}\n </span>\n </span>\n </button>\n );\n })\n )}\n </div>\n </aside>\n </div>\n </section>\n );\n}\n"]}
1
+ {"version":3,"file":"NewWorkspaceAppFlow.js","sourceRoot":"","sources":["../../src/client/NewWorkspaceAppFlow.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport {\n IconArrowUpRight,\n IconCheck,\n IconCode,\n IconKey,\n IconLoader2,\n IconPlus,\n} from \"@tabler/icons-react\";\nimport { agentNativePath, appBasePath } from \"./api-path.js\";\nimport { sendToAgentChat } from \"./agent-chat.js\";\nimport { isInBuilderFrame } from \"./builder-frame.js\";\nimport { useDevMode } from \"./use-dev-mode.js\";\n\nexport interface VaultSecretOption {\n id: string;\n name: string;\n credentialKey: string;\n provider?: string | null;\n description?: string | null;\n}\n\nexport interface NewWorkspaceAppFlowProps {\n sourceApp?: string;\n className?: string;\n dispatchBasePath?: string | null;\n}\n\nconst TEMPLATE_OPTIONS = [\n { value: \"starter\", label: \"Starter\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"calendar\", label: \"Calendar\" },\n { value: \"content\", label: \"Content\" },\n { value: \"design\", label: \"Design\" },\n { value: \"dispatch\", label: \"Dispatch\" },\n { value: \"forms\", label: \"Forms\" },\n { value: \"mail\", label: \"Mail\" },\n { value: \"slides\", label: \"Slides\" },\n { value: \"videos\", label: \"Videos\" },\n { value: \"clips\", label: \"Clips\" },\n] as const;\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 48);\n}\n\nfunction titleFromPrompt(prompt: string): string {\n const cleaned = prompt\n .replace(/\\b(build|create|make|an?|the|app|tool|dashboard)\\b/gi, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n return slugify(cleaned || \"new-app\") || \"new-app\";\n}\n\nfunction actionUrl(basePath: string | null, action: string): string {\n const path = `/_agent-native/actions/${action}`;\n if (basePath === null) return agentNativePath(path);\n const normalized = basePath.replace(/\\/+$/, \"\");\n return `${normalized}${path}`;\n}\n\nfunction defaultDispatchBasePath(sourceApp?: string): string | null {\n if (sourceApp === \"dispatch\") return null;\n const base = appBasePath();\n if (base === \"/dispatch\") return null;\n return \"/dispatch\";\n}\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<any> {\n const res = await fetch(url, init);\n const data = await res.json().catch(() => null);\n if (!res.ok) {\n throw new Error(\n data?.error || data?.message || `Request failed ${res.status}`,\n );\n }\n return data;\n}\n\nexport function NewWorkspaceAppFlow({\n sourceApp = \"starter\",\n className = \"\",\n dispatchBasePath,\n}: NewWorkspaceAppFlowProps) {\n const [prompt, setPrompt] = useState(\"\");\n const [appName, setAppName] = useState(\"\");\n const [template, setTemplate] =\n useState<(typeof TEMPLATE_OPTIONS)[number][\"value\"]>(\"starter\");\n const [selectedSecretIds, setSelectedSecretIds] = useState<string[]>([]);\n const [secrets, setSecrets] = useState<VaultSecretOption[]>([]);\n const [secretsError, setSecretsError] = useState<string | null>(null);\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n const [branchUrl, setBranchUrl] = useState<string | null>(null);\n const [isSubmitting, setIsSubmitting] = useState(false);\n const { isDevMode } = useDevMode();\n\n const effectiveDispatchBasePath =\n dispatchBasePath === undefined\n ? defaultDispatchBasePath(sourceApp)\n : dispatchBasePath;\n\n useEffect(() => {\n let cancelled = false;\n const url = actionUrl(\n effectiveDispatchBasePath,\n \"list-vault-secret-options\",\n );\n fetchJson(url)\n .then((data) => {\n if (cancelled) return;\n setSecrets(Array.isArray(data) ? data : []);\n setSecretsError(null);\n })\n .catch((err) => {\n if (cancelled) return;\n setSecrets([]);\n setSecretsError(err?.message || \"Could not load Dispatch keys\");\n });\n return () => {\n cancelled = true;\n };\n }, [effectiveDispatchBasePath]);\n\n useEffect(() => {\n if (appName || !prompt.trim()) return;\n setAppName(titleFromPrompt(prompt));\n }, [prompt, appName]);\n\n const selectedSecrets = useMemo(\n () => secrets.filter((secret) => selectedSecretIds.includes(secret.id)),\n [secrets, selectedSecretIds],\n );\n\n const canSubmit = prompt.trim().length > 0 && slugify(appName).length > 0;\n\n function buildMessage(): string {\n const safeAppName = slugify(appName) || titleFromPrompt(prompt);\n const keyList = selectedSecrets.map((s) => s.credentialKey).join(\", \");\n return [\n `Create a new agent-native app in this workspace.`,\n ``,\n `App name: ${safeAppName}`,\n `Template to start from: ${template}`,\n `User prompt: ${prompt.trim()}`,\n keyList\n ? `Dispatch vault keys selected for this app: ${keyList}`\n : `Dispatch vault keys selected for this app: none`,\n ``,\n `Use the workspace app layout: create it under apps/${safeAppName}, mount it at /${safeAppName}, keep it on the shared workspace database/hosting model, and avoid table-name collisions by namespacing any new domain tables to the app.`,\n keyList\n ? `Grant the selected Dispatch vault keys to appId \"${safeAppName}\" and sync them once the app server is available.`\n : `Do not grant any Dispatch vault keys unless the user asks later.`,\n `When it is ready, start or update the dev server and navigate the user to /${safeAppName}.`,\n ].join(\"\\n\");\n }\n\n async function grantSelectedSecrets(safeAppName: string) {\n if (selectedSecretIds.length === 0) return;\n try {\n await fetchJson(\n actionUrl(effectiveDispatchBasePath, \"grant-vault-secrets-to-app\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n appId: safeAppName,\n secretIds: selectedSecretIds,\n }),\n },\n );\n } catch (err: any) {\n setStatusMessage(\n `The app request was prepared, but Dispatch grants could not be saved yet: ${err?.message || \"unknown error\"}`,\n );\n }\n }\n\n async function submit() {\n if (!canSubmit || isSubmitting) return;\n const safeAppName = slugify(appName) || titleFromPrompt(prompt);\n const message = buildMessage();\n setIsSubmitting(true);\n setStatusMessage(null);\n setBranchUrl(null);\n\n try {\n await grantSelectedSecrets(safeAppName);\n\n if (isInBuilderFrame()) {\n sendToAgentChat({ message, submit: true, type: \"code\" });\n setStatusMessage(\"Sent to Builder chat.\");\n } else if (isDevMode) {\n sendToAgentChat({ message, submit: true, type: \"code\", newTab: true });\n setStatusMessage(\"Sent to the local agent.\");\n } else {\n const result = await fetchJson(\n actionUrl(effectiveDispatchBasePath, \"start-workspace-app-creation\"),\n {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n prompt: prompt.trim(),\n appId: safeAppName,\n template,\n secretIds: selectedSecretIds,\n preparedPrompt: message,\n }),\n },\n );\n if (result?.mode === \"builder\") {\n setBranchUrl(result?.url || null);\n setStatusMessage(\"Builder branch created.\");\n } else {\n setStatusMessage(\n result?.message ||\n \"Builder app creation is coming soon here. Open this workspace in Builder to create an app from this prompt.\",\n );\n }\n }\n } catch (err: any) {\n setStatusMessage(err?.message || \"Could not start the new app flow.\");\n } finally {\n setIsSubmitting(false);\n }\n }\n\n function toggleSecret(id: string) {\n setSelectedSecretIds((current) =>\n current.includes(id)\n ? current.filter((existing) => existing !== id)\n : [...current, id],\n );\n }\n\n return (\n <section\n className={`mx-auto flex w-full max-w-5xl flex-col gap-5 px-4 py-6 ${className}`}\n >\n <div className=\"grid gap-5 lg:grid-cols-[minmax(0,1fr)_320px]\">\n <div className=\"rounded-lg border border-border bg-card\">\n <div className=\"border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n <IconPlus className=\"h-4 w-4\" />\n New app\n </div>\n </div>\n <div className=\"space-y-4 p-4\">\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Prompt\n </span>\n <textarea\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n placeholder=\"Describe the app your teammate should be able to use...\"\n rows={6}\n className=\"min-h-36 w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:border-ring\"\n />\n </label>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n App path\n </span>\n <input\n value={appName}\n onChange={(e) => setAppName(slugify(e.target.value))}\n placeholder=\"customer-health\"\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring\"\n />\n </label>\n <label className=\"block space-y-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Starter template\n </span>\n <select\n value={template}\n onChange={(e) =>\n setTemplate(e.target.value as typeof template)\n }\n className=\"h-10 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring\"\n >\n {TEMPLATE_OPTIONS.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n </label>\n </div>\n\n <button\n type=\"button\"\n onClick={submit}\n disabled={!canSubmit || isSubmitting}\n className=\"inline-flex h-10 items-center gap-2 rounded-md bg-foreground px-4 text-sm font-medium text-background disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {isSubmitting ? (\n <IconLoader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <IconCode className=\"h-4 w-4\" />\n )}\n Create app\n </button>\n\n {statusMessage ? (\n <div className=\"rounded-md border border-border bg-muted/40 px-3 py-2 text-sm text-muted-foreground\">\n {statusMessage}\n {branchUrl ? (\n <a\n href={branchUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"ml-2 inline-flex items-center gap-1 font-medium text-foreground underline\"\n >\n Open branch <IconArrowUpRight className=\"h-3 w-3\" />\n </a>\n ) : null}\n </div>\n ) : null}\n </div>\n </div>\n\n <aside className=\"rounded-lg border border-border bg-card\">\n <div className=\"border-b border-border px-4 py-3\">\n <div className=\"flex items-center gap-2 text-sm font-medium\">\n <IconKey className=\"h-4 w-4\" />\n Dispatch keys\n </div>\n </div>\n <div className=\"max-h-[440px] space-y-2 overflow-y-auto p-3\">\n {secretsError ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n {secretsError}\n </p>\n ) : secrets.length === 0 ? (\n <p className=\"rounded-md border border-dashed border-border px-3 py-3 text-xs text-muted-foreground\">\n No Dispatch vault keys found yet.\n </p>\n ) : (\n secrets.map((secret) => {\n const selected = selectedSecretIds.includes(secret.id);\n return (\n <button\n key={secret.id}\n type=\"button\"\n aria-pressed={selected}\n onClick={() => toggleSecret(secret.id)}\n className={`group flex w-full items-start gap-3 rounded-md border px-3 py-2 text-left text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30 ${\n selected\n ? \"border-ring/60 bg-muted/70 text-foreground\"\n : \"border-border bg-background/30 text-foreground hover:border-muted-foreground/40 hover:bg-accent/45\"\n }`}\n >\n <span\n className={`mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded border transition ${\n selected\n ? \"border-ring bg-background text-foreground\"\n : \"border-muted-foreground/35 text-transparent group-hover:border-muted-foreground/60\"\n }`}\n >\n {selected ? <IconCheck className=\"h-3 w-3\" /> : null}\n </span>\n <span className=\"min-w-0\">\n <span className=\"block truncate font-medium\">\n {secret.credentialKey}\n </span>\n <span className=\"block truncate text-xs text-muted-foreground\">\n {secret.provider || secret.name}\n </span>\n </span>\n </button>\n );\n })\n )}\n </div>\n </aside>\n </div>\n </section>\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationsBell.d.ts","sourceRoot":"","sources":["../../../src/client/notifications/NotificationsBell.tsx"],"names":[],"mappings":"AAcA,UAAU,sBAAsB;IAC9B,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAwB,EACxB,SAAS,EACT,oBAA4B,GAC7B,EAAE,sBAAsB,2CAkRxB"}
1
+ {"version":3,"file":"NotificationsBell.d.ts","sourceRoot":"","sources":["../../../src/client/notifications/NotificationsBell.tsx"],"names":[],"mappings":"AAcA,UAAU,sBAAsB;IAC9B,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAMD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAwB,EACxB,SAAS,EACT,oBAA4B,GAC7B,EAAE,sBAAsB,2CAoTxB"}
@@ -16,6 +16,7 @@ export function NotificationsBell({ pollMs = POLL_MS_DEFAULT, className, browser
16
16
  const [unreadCount, setUnreadCount] = useState(0);
17
17
  const [open, setOpen] = useState(false);
18
18
  const [items, setItems] = useState(null);
19
+ const [menuPosition, setMenuPosition] = useState(null);
19
20
  // Init to "default" unconditionally so server and client render the same
20
21
  // HTML — reading Notification.permission at init would diverge between SSR
21
22
  // ("denied", no API) and hydration ("default"/"granted"), causing a mismatch
@@ -27,6 +28,7 @@ export function NotificationsBell({ pollMs = POLL_MS_DEFAULT, className, browser
27
28
  setPermission(Notification.permission);
28
29
  }, []);
29
30
  const menuRef = useRef(null);
31
+ const triggerRef = useRef(null);
30
32
  // Ids already popped as browser notifications. Seeded on first run so
31
33
  // existing unread don't pop retroactively on page load.
32
34
  const seenIdsRef = useRef(null);
@@ -103,6 +105,28 @@ export function NotificationsBell({ pollMs = POLL_MS_DEFAULT, className, browser
103
105
  return;
104
106
  loadItems();
105
107
  }, [open, loadItems]);
108
+ useEffect(() => {
109
+ if (!open)
110
+ return;
111
+ const updatePosition = () => {
112
+ const rect = triggerRef.current?.getBoundingClientRect();
113
+ if (!rect)
114
+ return;
115
+ const width = 320;
116
+ const margin = 12;
117
+ setMenuPosition({
118
+ top: rect.bottom + 8,
119
+ left: Math.min(Math.max(rect.right - width, margin), window.innerWidth - width - margin),
120
+ });
121
+ };
122
+ updatePosition();
123
+ window.addEventListener("resize", updatePosition);
124
+ window.addEventListener("scroll", updatePosition, true);
125
+ return () => {
126
+ window.removeEventListener("resize", updatePosition);
127
+ window.removeEventListener("scroll", updatePosition, true);
128
+ };
129
+ }, [open]);
106
130
  useEffect(() => {
107
131
  if (!open)
108
132
  return;
@@ -157,7 +181,10 @@ export function NotificationsBell({ pollMs = POLL_MS_DEFAULT, className, browser
157
181
  const hasUnread = unreadCount > 0;
158
182
  const Icon = hasUnread ? IconBellRinging : IconBell;
159
183
  return (_jsxs("div", { ref: menuRef, className: "an-notifications-bell relative inline-flex" +
160
- (className ? ` ${className}` : ""), children: [_jsxs("button", { type: "button", "aria-label": hasUnread ? `${unreadCount} unread notifications` : "Notifications", onClick: () => setOpen((v) => !v), className: "an-notifications-bell__trigger relative inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: [_jsx(Icon, { size: 18, "aria-hidden": true }), hasUnread ? (_jsx("span", { "aria-hidden": true, className: "an-notifications-bell__badge absolute -right-0.5 -top-0.5 rounded-full bg-destructive px-1 text-[10px] leading-[14px] font-medium text-destructive-foreground", children: unreadCount > 99 ? "99+" : unreadCount })) : null] }), open ? (_jsxs("div", { role: "menu", className: "an-notifications-bell__menu absolute right-0 top-full z-50 mt-2 w-80 rounded-md border border-border bg-popover text-popover-foreground shadow-lg", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2 text-sm font-medium", children: [_jsx("span", { children: "Notifications" }), hasUnread ? (_jsx("button", { type: "button", onClick: markAllRead, className: "text-xs text-primary hover:underline", children: "Mark all read" })) : null] }), browserNotifications &&
184
+ (className ? ` ${className}` : ""), children: [_jsxs("button", { ref: triggerRef, type: "button", "aria-label": hasUnread ? `${unreadCount} unread notifications` : "Notifications", onClick: () => setOpen((v) => !v), className: "an-notifications-bell__trigger relative inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground", children: [_jsx(Icon, { size: 18, "aria-hidden": true }), hasUnread ? (_jsx("span", { "aria-hidden": true, className: "an-notifications-bell__badge absolute -right-0.5 -top-0.5 rounded-full bg-destructive px-1 text-[10px] leading-[14px] font-medium text-destructive-foreground", children: unreadCount > 99 ? "99+" : unreadCount })) : null] }), open ? (_jsxs("div", { role: "menu", className: "an-notifications-bell__menu fixed z-[2100] w-80 rounded-md border border-border bg-popover text-popover-foreground shadow-lg", style: {
185
+ top: menuPosition?.top ?? 48,
186
+ left: menuPosition?.left ?? 12,
187
+ }, children: [_jsxs("div", { className: "flex items-center justify-between border-b border-border px-3 py-2 text-sm font-medium", children: [_jsx("span", { children: "Notifications" }), hasUnread ? (_jsx("button", { type: "button", onClick: markAllRead, className: "text-xs text-primary hover:underline", children: "Mark all read" })) : null] }), browserNotifications &&
161
188
  SUPPORTS_NOTIFICATION &&
162
189
  permission === "default" ? (_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border bg-accent/40 px-3 py-2 text-xs text-foreground", children: [_jsx("span", { children: "Get a system popup for new notifications." }), _jsx("button", { type: "button", onClick: async () => {
163
190
  const result = await Notification.requestPermission();
@@ -1 +1 @@
1
- {"version":3,"file":"NotificationsBell.js","sourceRoot":"","sources":["../../../src/client/notifications/NotificationsBell.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,EACL,QAAQ,EACR,eAAe,EACf,WAAW,EACX,KAAK,GACN,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAoBhE,MAAM,eAAe,GAAG,MAAM,CAAC;AAC/B,MAAM,qBAAqB,GACzB,OAAO,MAAM,KAAK,WAAW,IAAI,cAAc,IAAI,MAAM,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,MAAM,GAAG,eAAe,EACxB,SAAS,EACT,oBAAoB,GAAG,KAAK,GACL;IACvB,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAA2B,IAAI,CAAC,CAAC;IACnE,yEAAyE;IACzE,2EAA2E;IAC3E,6EAA6E;IAC7E,0EAA0E;IAC1E,0CAA0C;IAC1C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAC/B,QAAQ,CAAyB,SAAS,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,qBAAqB;YAAE,aAAa,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IACpE,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,MAAM,OAAO,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;IACpD,sEAAsE;IACtE,wDAAwD;IACxD,MAAM,UAAU,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IAEpD,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACvC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,eAAe,CAAC,uCAAuC,CAAC,CACzD,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO;YACpB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;YACrD,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,2EAA2E;IAC3E,yEAAyE;IACzE,yEAAyE;IACzE,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,oBAAoB,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,eAAe,CAAC,mDAAmD,CAAC,CACrE,CAAC;gBACF,IAAI,CAAC,GAAG,CAAC,EAAE;oBAAE,OAAO;gBACpB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;gBACrD,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC5B,8DAA8D;gBAC9D,mEAAmE;gBACnE,iEAAiE;gBACjE,+CAA+C;gBAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC;gBAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;oBACrB,MAAM,WAAW,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;oBAC5C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACf,IAAI,WAAW;wBAAE,SAAS;oBAC1B,IAAI,CAAC,qBAAqB;wBAAE,SAAS;oBACrC,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS;wBAAE,SAAS;oBACpD,IAAI,CAAC;wBACH,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACzD,CAAC;oBAAC,MAAM,CAAC;wBACP,8DAA8D;wBAC9D,uCAAuC;oBACzC,CAAC;gBACH,CAAC;gBACD,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,cAAc;YAChB,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,eAAe,CAAC,oCAAoC,CAAC,CACtD,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO;YACpB,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAsB,CAAC;YACrD,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;IAE3B,kBAAkB,CAChB,OAAO,EACP,MAAM;IACN,qBAAqB,CAAC,CAAC,oBAAoB,CAC5C,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,SAAS,EAAE,CAAC;IACd,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAEtB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,UAAU,GAAG,CAAC,CAAa,EAAE,EAAE;YACnC,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAc,CAAC,EAAE,CAAC;gBACnE,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACnD,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACrE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,QAAQ,GAAG,KAAK,EAAE,EAAU,EAAE,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,eAAe,CAAC,gCAAgC,EAAE,OAAO,CAAC,EAAE;gBACtE,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YACH,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAChB,IAAI;gBACF,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAC7D;gBACH,CAAC,CAAC,IAAI,CACT,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC7B,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,eAAe,CAAC,uCAAuC,CAAC,EAAE;gBACpE,MAAM,EAAE,MAAM;aACf,CAAC,CAAC;YACH,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAChB,IAAI;gBACF,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAC1D;gBACH,CAAC,CAAC,IAAI,CACT,CAAC;YACF,cAAc,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,KAAK,EAAE,EAAU,EAAE,EAAE;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,eAAe,CAAC,gCAAgC,EAAE,EAAE,CAAC,EAAE;gBACjE,MAAM,EAAE,QAAQ;aACjB,CAAC,CAAC;YACH,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACpE,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,GAAG,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC;IAEpD,OAAO,CACL,eACE,GAAG,EAAE,OAAO,EACZ,SAAS,EACP,4CAA4C;YAC5C,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAGpC,kBACE,IAAI,EAAC,QAAQ,gBAEX,SAAS,CAAC,CAAC,CAAC,GAAG,WAAW,uBAAuB,CAAC,CAAC,CAAC,eAAe,EAErE,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EACjC,SAAS,EAAC,mKAAmK,aAE7K,KAAC,IAAI,IAAC,IAAI,EAAE,EAAE,wBAAgB,EAC7B,SAAS,CAAC,CAAC,CAAC,CACX,oCAEE,SAAS,EAAC,+JAA+J,YAExK,WAAW,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,GAClC,CACR,CAAC,CAAC,CAAC,IAAI,IACD,EACR,IAAI,CAAC,CAAC,CAAC,CACN,eACE,IAAI,EAAC,MAAM,EACX,SAAS,EAAC,mJAAmJ,aAE7J,eAAK,SAAS,EAAC,wFAAwF,aACrG,2CAA0B,EACzB,SAAS,CAAC,CAAC,CAAC,CACX,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,WAAW,EACpB,SAAS,EAAC,sCAAsC,8BAGzC,CACV,CAAC,CAAC,CAAC,IAAI,IACJ,EACL,oBAAoB;wBACrB,qBAAqB;wBACrB,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,CACzB,eAAK,SAAS,EAAC,+GAA+G,aAC5H,uEAAsD,EACtD,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,KAAK,IAAI,EAAE;oCAClB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,iBAAiB,EAAE,CAAC;oCACtD,aAAa,CAAC,MAAM,CAAC,CAAC;gCACxB,CAAC,EACD,SAAS,EAAC,iGAAiG,uBAGpG,IACL,CACP,CAAC,CAAC,CAAC,IAAI,EACR,cAAK,SAAS,EAAC,0BAA0B,YACtC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAChB,eAAK,SAAS,EAAC,2DAA2D,aACxE,KAAC,WAAW,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,cAAc,GAAG,sBAC9C,CACP,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACrB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACf,eAEE,SAAS,EACP,2EAA2E;gCAC3E,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,aAGhC,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EACtD,SAAS,EAAC,mEAAmE,aAE7E,eAAK,SAAS,EAAC,gDAAgD,aAC7D,eAAM,SAAS,EAAC,8CAA8C,YAC3D,CAAC,CAAC,KAAK,GACH,EACP,KAAC,aAAa,IAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAI,IACnC,EACL,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACR,eAAM,SAAS,EAAC,4CAA4C,YACzD,CAAC,CAAC,IAAI,GACF,CACR,CAAC,CAAC,CAAC,IAAI,EACR,eAAM,SAAS,EAAC,sCAAsC,YACnD,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,cAAc,EAAE,GAClC,IACA,EACT,iBACE,IAAI,EAAC,QAAQ,gBACF,sBAAsB,EACjC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;wCACb,CAAC,CAAC,eAAe,EAAE,CAAC;wCACpB,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oCACrB,CAAC,EACD,SAAS,EAAC,0HAA0H,YAEpI,KAAC,KAAK,IAAC,IAAI,EAAE,EAAE,GAAI,GACZ,KApCJ,CAAC,CAAC,EAAE,CAqCL,CACP,CAAC,CACH,CAAC,CAAC,CAAC,CACF,cAAK,SAAS,EAAC,mCAAmC,kCAE5C,CACP,GACG,IACF,CACP,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,2EAA2E;AAC3E,2EAA2E;AAC3E,4EAA4E;AAC5E,SAAS,aAAa,CAAC,EAAE,QAAQ,EAAsC;IACrE,MAAM,KAAK,GACT,QAAQ,KAAK,UAAU;QACrB,CAAC,CAAC,8CAA8C;QAChD,CAAC,CAAC,QAAQ,KAAK,SAAS;YACtB,CAAC,CAAC,oDAAoD;YACtD,CAAC,CAAC,gCAAgC,CAAC;IACzC,OAAO,CACL,eAAM,SAAS,EAAE,iDAAiD,KAAK,EAAE,YACtE,QAAQ,GACJ,CACR,CAAC;AACJ,CAAC","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n IconBell,\n IconBellRinging,\n IconLoader2,\n IconX,\n} from \"@tabler/icons-react\";\nimport { usePausingInterval } from \"../use-pausing-interval.js\";\nimport type {\n Notification as NotificationDto,\n NotificationSeverity,\n} from \"../../notifications/types.js\";\n\ninterface NotificationsBellProps {\n /** Poll interval in ms. Set to 0 to disable polling. Default: 10000. */\n pollMs?: number;\n /** Optional className for the outer container. */\n className?: string;\n /**\n * When true, fires a system-level `new Notification(...)` popup for each\n * new unread notification — handy when the tab is in the background.\n * Renders an \"Enable browser notifications\" prompt in the dropdown until\n * the user grants permission. Silently no-ops on denied or unsupported.\n */\n browserNotifications?: boolean;\n}\n\nconst POLL_MS_DEFAULT = 10_000;\nconst SUPPORTS_NOTIFICATION =\n typeof window !== \"undefined\" && \"Notification\" in window;\n\n/**\n * Header-bar bell that shows the unread-notification count and a dropdown of\n * recent entries. Polling keeps it in sync (the framework poll loop already\n * bumps a version counter so notifications ride on that signal, but we poll\n * the count endpoint directly so the bell updates even outside an app-state\n * change).\n */\nexport function NotificationsBell({\n pollMs = POLL_MS_DEFAULT,\n className,\n browserNotifications = false,\n}: NotificationsBellProps) {\n const [unreadCount, setUnreadCount] = useState(0);\n const [open, setOpen] = useState(false);\n const [items, setItems] = useState<NotificationDto[] | null>(null);\n // Init to \"default\" unconditionally so server and client render the same\n // HTML — reading Notification.permission at init would diverge between SSR\n // (\"denied\", no API) and hydration (\"default\"/\"granted\"), causing a mismatch\n // in templates that mount the bell outside a ClientOnly boundary. We sync\n // to the real value in a useEffect below.\n const [permission, setPermission] =\n useState<NotificationPermission>(\"default\");\n\n useEffect(() => {\n if (SUPPORTS_NOTIFICATION) setPermission(Notification.permission);\n }, []);\n const menuRef = useRef<HTMLDivElement | null>(null);\n // Ids already popped as browser notifications. Seeded on first run so\n // existing unread don't pop retroactively on page load.\n const seenIdsRef = useRef<Set<string> | null>(null);\n\n const loadItems = useCallback(async () => {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications?limit=20\"),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as NotificationDto[];\n setItems(rows);\n } catch {\n // best-effort\n }\n }, []);\n\n // One polling callback used by both paths. When browserNotifications is on\n // we fetch the unread list (source of truth for both the badge count AND\n // the popup loop — no second /count request), and pop Notification() for\n // any new ids. When off, we fetch just /count. The unread-list branch also\n // opts out of visibility pause so popups still fire for backgrounded tabs.\n const refresh = useCallback(async () => {\n if (browserNotifications) {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications?unread=true&limit=20\"),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as NotificationDto[];\n setUnreadCount(rows.length);\n // First run: treat everything as already seen so we don't pop\n // retroactively on page load. After that, rebuild from the current\n // unread list so ids for read/archived rows drop out — keeps the\n // set bounded to the unread fetch limit (~20).\n const prev = seenIdsRef.current;\n const seen = new Set<string>();\n for (const n of rows) {\n const alreadySeen = prev?.has(n.id) ?? true;\n seen.add(n.id);\n if (alreadySeen) continue;\n if (!SUPPORTS_NOTIFICATION) continue;\n if (Notification.permission !== \"granted\") continue;\n try {\n new Notification(n.title, { body: n.body, tag: n.id });\n } catch {\n // Safari / restricted contexts may throw even when permission\n // claims to be granted — silent no-op.\n }\n }\n seenIdsRef.current = seen;\n } catch {\n // best-effort\n }\n return;\n }\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications/count\"),\n );\n if (!res.ok) return;\n const data = (await res.json()) as { count: number };\n setUnreadCount(data.count);\n } catch {\n // best-effort\n }\n }, [browserNotifications]);\n\n usePausingInterval(\n refresh,\n pollMs,\n /* pauseWhenHidden */ !browserNotifications,\n );\n\n useEffect(() => {\n if (!open) return;\n loadItems();\n }, [open, loadItems]);\n\n useEffect(() => {\n if (!open) return;\n const onDocClick = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onDocClick);\n return () => document.removeEventListener(\"mousedown\", onDocClick);\n }, [open]);\n\n const markRead = async (id: string) => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/${id}/read`), {\n method: \"POST\",\n });\n setItems((prev) =>\n prev\n ? prev.map((n) =>\n n.id === id ? { ...n, readAt: new Date().toISOString() } : n,\n )\n : prev,\n );\n refresh();\n } catch {\n // best-effort\n }\n };\n\n const markAllRead = async () => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/read-all`), {\n method: \"POST\",\n });\n setItems((prev) =>\n prev\n ? prev.map((n) =>\n n.readAt ? n : { ...n, readAt: new Date().toISOString() },\n )\n : prev,\n );\n setUnreadCount(0);\n } catch {\n // best-effort\n }\n };\n\n const dismiss = async (id: string) => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/${id}`), {\n method: \"DELETE\",\n });\n setItems((prev) => (prev ? prev.filter((n) => n.id !== id) : prev));\n refresh();\n } catch {\n // best-effort\n }\n };\n\n const hasUnread = unreadCount > 0;\n const Icon = hasUnread ? IconBellRinging : IconBell;\n\n return (\n <div\n ref={menuRef}\n className={\n \"an-notifications-bell relative inline-flex\" +\n (className ? ` ${className}` : \"\")\n }\n >\n <button\n type=\"button\"\n aria-label={\n hasUnread ? `${unreadCount} unread notifications` : \"Notifications\"\n }\n onClick={() => setOpen((v) => !v)}\n className=\"an-notifications-bell__trigger relative inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n <Icon size={18} aria-hidden />\n {hasUnread ? (\n <span\n aria-hidden\n className=\"an-notifications-bell__badge absolute -right-0.5 -top-0.5 rounded-full bg-destructive px-1 text-[10px] leading-[14px] font-medium text-destructive-foreground\"\n >\n {unreadCount > 99 ? \"99+\" : unreadCount}\n </span>\n ) : null}\n </button>\n {open ? (\n <div\n role=\"menu\"\n className=\"an-notifications-bell__menu absolute right-0 top-full z-50 mt-2 w-80 rounded-md border border-border bg-popover text-popover-foreground shadow-lg\"\n >\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 text-sm font-medium\">\n <span>Notifications</span>\n {hasUnread ? (\n <button\n type=\"button\"\n onClick={markAllRead}\n className=\"text-xs text-primary hover:underline\"\n >\n Mark all read\n </button>\n ) : null}\n </div>\n {browserNotifications &&\n SUPPORTS_NOTIFICATION &&\n permission === \"default\" ? (\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-accent/40 px-3 py-2 text-xs text-foreground\">\n <span>Get a system popup for new notifications.</span>\n <button\n type=\"button\"\n onClick={async () => {\n const result = await Notification.requestPermission();\n setPermission(result);\n }}\n className=\"shrink-0 rounded bg-primary px-2 py-0.5 font-medium text-primary-foreground hover:bg-primary/90\"\n >\n Enable\n </button>\n </div>\n ) : null}\n <div className=\"max-h-96 overflow-y-auto\">\n {items === null ? (\n <div className=\"flex items-center gap-2 p-4 text-sm text-muted-foreground\">\n <IconLoader2 size={14} className=\"animate-spin\" /> Loading…\n </div>\n ) : items.length > 0 ? (\n items.map((n) => (\n <div\n key={n.id}\n className={\n \"group relative border-b border-border last:border-b-0 hover:bg-accent/40 \" +\n (n.readAt ? \"opacity-60\" : \"\")\n }\n >\n <button\n type=\"button\"\n onClick={() => (n.readAt ? undefined : markRead(n.id))}\n className=\"flex w-full flex-col items-start gap-0.5 px-3 py-2 pr-8 text-left\"\n >\n <div className=\"flex w-full items-center justify-between gap-2\">\n <span className=\"truncate text-sm font-medium text-foreground\">\n {n.title}\n </span>\n <SeverityBadge severity={n.severity} />\n </div>\n {n.body ? (\n <span className=\"line-clamp-2 text-xs text-muted-foreground\">\n {n.body}\n </span>\n ) : null}\n <span className=\"text-[10px] text-muted-foreground/70\">\n {new Date(n.createdAt).toLocaleString()}\n </span>\n </button>\n <button\n type=\"button\"\n aria-label=\"Dismiss notification\"\n onClick={(e) => {\n e.stopPropagation();\n void dismiss(n.id);\n }}\n className=\"absolute right-2 top-2 hidden rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground group-hover:flex\"\n >\n <IconX size={12} />\n </button>\n </div>\n ))\n ) : (\n <div className=\"p-4 text-sm text-muted-foreground\">\n No notifications.\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n// Severity color pairs — use /20 opacity backdrops that work against both\n// light and dark theme backgrounds; text uses 700/300 so it stays readable\n// in each mode (the `dark:` prefix is one of the few places where explicit\n// variants are necessary since these are brand-color tokens, not semantic).\nfunction SeverityBadge({ severity }: { severity: NotificationSeverity }) {\n const color =\n severity === \"critical\"\n ? \"bg-red-500/20 text-red-700 dark:text-red-300\"\n : severity === \"warning\"\n ? \"bg-amber-500/20 text-amber-700 dark:text-amber-300\"\n : \"bg-muted text-muted-foreground\";\n return (\n <span className={`rounded px-1.5 py-0.5 text-[10px] font-medium ${color}`}>\n {severity}\n </span>\n );\n}\n"]}
1
+ {"version":3,"file":"NotificationsBell.js","sourceRoot":"","sources":["../../../src/client/notifications/NotificationsBell.tsx"],"names":[],"mappings":"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","sourcesContent":["import { agentNativePath } from \"../api-path.js\";\nimport React, { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n IconBell,\n IconBellRinging,\n IconLoader2,\n IconX,\n} from \"@tabler/icons-react\";\nimport { usePausingInterval } from \"../use-pausing-interval.js\";\nimport type {\n Notification as NotificationDto,\n NotificationSeverity,\n} from \"../../notifications/types.js\";\n\ninterface NotificationsBellProps {\n /** Poll interval in ms. Set to 0 to disable polling. Default: 10000. */\n pollMs?: number;\n /** Optional className for the outer container. */\n className?: string;\n /**\n * When true, fires a system-level `new Notification(...)` popup for each\n * new unread notification — handy when the tab is in the background.\n * Renders an \"Enable browser notifications\" prompt in the dropdown until\n * the user grants permission. Silently no-ops on denied or unsupported.\n */\n browserNotifications?: boolean;\n}\n\nconst POLL_MS_DEFAULT = 10_000;\nconst SUPPORTS_NOTIFICATION =\n typeof window !== \"undefined\" && \"Notification\" in window;\n\n/**\n * Header-bar bell that shows the unread-notification count and a dropdown of\n * recent entries. Polling keeps it in sync (the framework poll loop already\n * bumps a version counter so notifications ride on that signal, but we poll\n * the count endpoint directly so the bell updates even outside an app-state\n * change).\n */\nexport function NotificationsBell({\n pollMs = POLL_MS_DEFAULT,\n className,\n browserNotifications = false,\n}: NotificationsBellProps) {\n const [unreadCount, setUnreadCount] = useState(0);\n const [open, setOpen] = useState(false);\n const [items, setItems] = useState<NotificationDto[] | null>(null);\n const [menuPosition, setMenuPosition] = useState<{\n top: number;\n left: number;\n } | null>(null);\n // Init to \"default\" unconditionally so server and client render the same\n // HTML — reading Notification.permission at init would diverge between SSR\n // (\"denied\", no API) and hydration (\"default\"/\"granted\"), causing a mismatch\n // in templates that mount the bell outside a ClientOnly boundary. We sync\n // to the real value in a useEffect below.\n const [permission, setPermission] =\n useState<NotificationPermission>(\"default\");\n\n useEffect(() => {\n if (SUPPORTS_NOTIFICATION) setPermission(Notification.permission);\n }, []);\n const menuRef = useRef<HTMLDivElement | null>(null);\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n // Ids already popped as browser notifications. Seeded on first run so\n // existing unread don't pop retroactively on page load.\n const seenIdsRef = useRef<Set<string> | null>(null);\n\n const loadItems = useCallback(async () => {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications?limit=20\"),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as NotificationDto[];\n setItems(rows);\n } catch {\n // best-effort\n }\n }, []);\n\n // One polling callback used by both paths. When browserNotifications is on\n // we fetch the unread list (source of truth for both the badge count AND\n // the popup loop — no second /count request), and pop Notification() for\n // any new ids. When off, we fetch just /count. The unread-list branch also\n // opts out of visibility pause so popups still fire for backgrounded tabs.\n const refresh = useCallback(async () => {\n if (browserNotifications) {\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications?unread=true&limit=20\"),\n );\n if (!res.ok) return;\n const rows = (await res.json()) as NotificationDto[];\n setUnreadCount(rows.length);\n // First run: treat everything as already seen so we don't pop\n // retroactively on page load. After that, rebuild from the current\n // unread list so ids for read/archived rows drop out — keeps the\n // set bounded to the unread fetch limit (~20).\n const prev = seenIdsRef.current;\n const seen = new Set<string>();\n for (const n of rows) {\n const alreadySeen = prev?.has(n.id) ?? true;\n seen.add(n.id);\n if (alreadySeen) continue;\n if (!SUPPORTS_NOTIFICATION) continue;\n if (Notification.permission !== \"granted\") continue;\n try {\n new Notification(n.title, { body: n.body, tag: n.id });\n } catch {\n // Safari / restricted contexts may throw even when permission\n // claims to be granted — silent no-op.\n }\n }\n seenIdsRef.current = seen;\n } catch {\n // best-effort\n }\n return;\n }\n try {\n const res = await fetch(\n agentNativePath(\"/_agent-native/notifications/count\"),\n );\n if (!res.ok) return;\n const data = (await res.json()) as { count: number };\n setUnreadCount(data.count);\n } catch {\n // best-effort\n }\n }, [browserNotifications]);\n\n usePausingInterval(\n refresh,\n pollMs,\n /* pauseWhenHidden */ !browserNotifications,\n );\n\n useEffect(() => {\n if (!open) return;\n loadItems();\n }, [open, loadItems]);\n\n useEffect(() => {\n if (!open) return;\n const updatePosition = () => {\n const rect = triggerRef.current?.getBoundingClientRect();\n if (!rect) return;\n const width = 320;\n const margin = 12;\n setMenuPosition({\n top: rect.bottom + 8,\n left: Math.min(\n Math.max(rect.right - width, margin),\n window.innerWidth - width - margin,\n ),\n });\n };\n updatePosition();\n window.addEventListener(\"resize\", updatePosition);\n window.addEventListener(\"scroll\", updatePosition, true);\n return () => {\n window.removeEventListener(\"resize\", updatePosition);\n window.removeEventListener(\"scroll\", updatePosition, true);\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n const onDocClick = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", onDocClick);\n return () => document.removeEventListener(\"mousedown\", onDocClick);\n }, [open]);\n\n const markRead = async (id: string) => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/${id}/read`), {\n method: \"POST\",\n });\n setItems((prev) =>\n prev\n ? prev.map((n) =>\n n.id === id ? { ...n, readAt: new Date().toISOString() } : n,\n )\n : prev,\n );\n refresh();\n } catch {\n // best-effort\n }\n };\n\n const markAllRead = async () => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/read-all`), {\n method: \"POST\",\n });\n setItems((prev) =>\n prev\n ? prev.map((n) =>\n n.readAt ? n : { ...n, readAt: new Date().toISOString() },\n )\n : prev,\n );\n setUnreadCount(0);\n } catch {\n // best-effort\n }\n };\n\n const dismiss = async (id: string) => {\n try {\n await fetch(agentNativePath(`/_agent-native/notifications/${id}`), {\n method: \"DELETE\",\n });\n setItems((prev) => (prev ? prev.filter((n) => n.id !== id) : prev));\n refresh();\n } catch {\n // best-effort\n }\n };\n\n const hasUnread = unreadCount > 0;\n const Icon = hasUnread ? IconBellRinging : IconBell;\n\n return (\n <div\n ref={menuRef}\n className={\n \"an-notifications-bell relative inline-flex\" +\n (className ? ` ${className}` : \"\")\n }\n >\n <button\n ref={triggerRef}\n type=\"button\"\n aria-label={\n hasUnread ? `${unreadCount} unread notifications` : \"Notifications\"\n }\n onClick={() => setOpen((v) => !v)}\n className=\"an-notifications-bell__trigger relative inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent/40 hover:text-foreground\"\n >\n <Icon size={18} aria-hidden />\n {hasUnread ? (\n <span\n aria-hidden\n className=\"an-notifications-bell__badge absolute -right-0.5 -top-0.5 rounded-full bg-destructive px-1 text-[10px] leading-[14px] font-medium text-destructive-foreground\"\n >\n {unreadCount > 99 ? \"99+\" : unreadCount}\n </span>\n ) : null}\n </button>\n {open ? (\n <div\n role=\"menu\"\n className=\"an-notifications-bell__menu fixed z-[2100] w-80 rounded-md border border-border bg-popover text-popover-foreground shadow-lg\"\n style={{\n top: menuPosition?.top ?? 48,\n left: menuPosition?.left ?? 12,\n }}\n >\n <div className=\"flex items-center justify-between border-b border-border px-3 py-2 text-sm font-medium\">\n <span>Notifications</span>\n {hasUnread ? (\n <button\n type=\"button\"\n onClick={markAllRead}\n className=\"text-xs text-primary hover:underline\"\n >\n Mark all read\n </button>\n ) : null}\n </div>\n {browserNotifications &&\n SUPPORTS_NOTIFICATION &&\n permission === \"default\" ? (\n <div className=\"flex items-center justify-between gap-2 border-b border-border bg-accent/40 px-3 py-2 text-xs text-foreground\">\n <span>Get a system popup for new notifications.</span>\n <button\n type=\"button\"\n onClick={async () => {\n const result = await Notification.requestPermission();\n setPermission(result);\n }}\n className=\"shrink-0 rounded bg-primary px-2 py-0.5 font-medium text-primary-foreground hover:bg-primary/90\"\n >\n Enable\n </button>\n </div>\n ) : null}\n <div className=\"max-h-96 overflow-y-auto\">\n {items === null ? (\n <div className=\"flex items-center gap-2 p-4 text-sm text-muted-foreground\">\n <IconLoader2 size={14} className=\"animate-spin\" /> Loading…\n </div>\n ) : items.length > 0 ? (\n items.map((n) => (\n <div\n key={n.id}\n className={\n \"group relative border-b border-border last:border-b-0 hover:bg-accent/40 \" +\n (n.readAt ? \"opacity-60\" : \"\")\n }\n >\n <button\n type=\"button\"\n onClick={() => (n.readAt ? undefined : markRead(n.id))}\n className=\"flex w-full flex-col items-start gap-0.5 px-3 py-2 pr-8 text-left\"\n >\n <div className=\"flex w-full items-center justify-between gap-2\">\n <span className=\"truncate text-sm font-medium text-foreground\">\n {n.title}\n </span>\n <SeverityBadge severity={n.severity} />\n </div>\n {n.body ? (\n <span className=\"line-clamp-2 text-xs text-muted-foreground\">\n {n.body}\n </span>\n ) : null}\n <span className=\"text-[10px] text-muted-foreground/70\">\n {new Date(n.createdAt).toLocaleString()}\n </span>\n </button>\n <button\n type=\"button\"\n aria-label=\"Dismiss notification\"\n onClick={(e) => {\n e.stopPropagation();\n void dismiss(n.id);\n }}\n className=\"absolute right-2 top-2 hidden rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground group-hover:flex\"\n >\n <IconX size={12} />\n </button>\n </div>\n ))\n ) : (\n <div className=\"p-4 text-sm text-muted-foreground\">\n No notifications.\n </div>\n )}\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n\n// Severity color pairs — use /20 opacity backdrops that work against both\n// light and dark theme backgrounds; text uses 700/300 so it stays readable\n// in each mode (the `dark:` prefix is one of the few places where explicit\n// variants are necessary since these are brand-color tokens, not semantic).\nfunction SeverityBadge({ severity }: { severity: NotificationSeverity }) {\n const color =\n severity === \"critical\"\n ? \"bg-red-500/20 text-red-700 dark:text-red-300\"\n : severity === \"warning\"\n ? \"bg-amber-500/20 text-amber-700 dark:text-amber-300\"\n : \"bg-muted text-muted-foreground\";\n return (\n <span className={`rounded px-1.5 py-0.5 text-[10px] font-medium ${color}`}>\n {severity}\n </span>\n );\n}\n"]}
@@ -128,7 +128,7 @@ export function getOnboardingHtml(opts = {}) {
128
128
  letter-spacing: -0.02em;
129
129
  }
130
130
  .app-name img.brand-mark {
131
- height: 1.75rem;
131
+ height: 2.0125rem;
132
132
  width: auto;
133
133
  display: block;
134
134
  flex-shrink: 0;
@@ -194,7 +194,7 @@ export function getOnboardingHtml(opts = {}) {
194
194
  .split { flex-direction: column; min-height: auto; }
195
195
  .marketing-panel { padding: 2rem 1.5rem 1.5rem; }
196
196
  .app-name { font-size: 1.375rem; }
197
- .app-name img.brand-mark { height: 1.25rem; }
197
+ .app-name img.brand-mark { height: 1.4375rem; }
198
198
  .app-tagline { font-size: 1rem; margin-bottom: 1rem; }
199
199
  .app-desc { margin-bottom: 1rem; }
200
200
  .feature-list { gap: 0.5rem; }