// — content rendered inside for the inbox // flyout. "mark all read" POSTs to the *dashboard's* origin (not this // agent's own backend) via a plain `fetch()`, same as app.js's // `buildInboxMarkAllRow` — unlike pause/resume this one's fine as a // fetch even when the two are genuinely cross-origin, because // `hive-c0re`'s `/api/agent/{name}/mark-all-read` route replies with // JSON, not a redirect; in the common gateway-fronted deployment // `dashboardBase` already resolves to `location.origin` anyway (see // dashboardBase.ts), so it's same-origin in practice. import { useState } from "preact/hooks"; import { useConfirmClick } from "../lib/useConfirmClick.js"; import type { InboxRow } from "../types.js"; export interface InboxPanelProps { rows: InboxRow[]; label: string; dashboardBase: string; onCleared: () => void; } function fmtTs(unixSeconds: number): string { return new Date(unixSeconds * 1000) .toISOString() .replace("T", " ") .slice(5, 19); } export function InboxPanel({ rows, label, dashboardBase, onCleared, }: InboxPanelProps) { const [status, setStatus] = useState(""); const [busy, setBusy] = useState(false); async function markAllRead() { if (!dashboardBase || !label) { setStatus("dashboard url / label unknown"); return; } setBusy(true); setStatus("clearing…"); try { const resp = await fetch( `${dashboardBase}api/agent/${encodeURIComponent(label)}/mark-all-read`, { method: "POST", }, ); if (resp.ok) { const data = await resp.json().catch(() => ({})); const n = Number((data as { marked?: number }).marked) || 0; setStatus(`✓ marked ${n} as read`); onCleared(); } else { setStatus(`failed: http ${resp.status}`); } } catch (err) { setStatus(`failed: ${err instanceof Error ? err.message : String(err)}`); } finally { setBusy(false); } } const { armed, trigger } = useConfirmClick(markAllRead); if (!rows.length) { return

inbox empty.

; } return (
{status}
    {rows.map((m) => (
  • {m.in_reply_to != null && ( ↳ reply · )} {fmtTs(m.at)}{" "} {m.from}{" "} →{" "} {m.body}
  • ))}
); }