/**
 * API client — all operations go through the IPC bridge (mailxapi).
 * mailxapi is injected by the launcher (msger on desktop, MAUI on Android).
 */
function getIpc() {
    if (typeof mailxapi !== "undefined" && mailxapi?.isApp)
        return mailxapi;
    if (window.opener?.mailxapi?.isApp)
        return window.opener.mailxapi;
    // Compose iframe — check parent
    if (window.parent?.mailxapi?.isApp)
        return window.parent.mailxapi;
    return null;
}
/** Build a proxy bridge that forwards every method call to the parent window
 *  via postMessage. Used when the compose iframe's own attempt to reach
 *  msger's IPC silently drops messages — empirically, `sendMessage` and
 *  `saveDraft` both hit this (user-visible: "Sending…" spinner forever;
 *  "Draft save failed: mailxapi timeout"). The main window's bridge is
 *  provably fine, so the iframe routes through it. */
function buildRelayBridge() {
    const pending = new Map();
    window.addEventListener("message", (ev) => {
        if (!ev.data || ev.data.type !== "mailx-ipc-result" || !ev.data.id)
            return;
        const entry = pending.get(ev.data.id);
        if (!entry)
            return;
        pending.delete(ev.data.id);
        clearTimeout(entry.timer);
        if (ev.data.ok)
            entry.resolve(ev.data.result);
        else
            entry.reject(new Error(ev.data.error || "parent-relay ipc error"));
    });
    const call = (method, args) => {
        const id = `ipc-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        return new Promise((resolve, reject) => {
            const timer = setTimeout(() => {
                pending.delete(id);
                reject(new Error(`parent-relay timeout: ${method}`));
            }, 120000);
            pending.set(id, { resolve, reject, timer });
            try {
                window.parent.postMessage({ type: "mailx-ipc", id, method, args }, "*");
            }
            catch (e) {
                clearTimeout(timer);
                pending.delete(id);
                reject(e);
            }
        });
    };
    // Proxy: any property access returns a function that forwards to parent.
    // `isApp` / `platform` / other non-function reads return sensible defaults
    // so existing getIpc-style checks still work.
    return new Proxy({}, {
        get(_t, prop) {
            if (prop === "isApp")
                return true;
            if (prop === "platform")
                return window.parent?.mailxapi?.platform || "webview2";
            if (prop === "onEvent") {
                // Event subscription can't be relayed simply — iframes that need
                // events are rare. Fall back to direct parent bridge for onEvent
                // since the subscription path doesn't hit the broken send path.
                return (handler) => window.parent?.mailxapi?.onEvent?.(handler);
            }
            return (...args) => call(prop, args);
        }
    });
}
let cachedRelayBridge = null;
function ipc() {
    // Direct bridge is fine for the top window (main mailx app). The iframe
    // (compose) can't trust its own bridge resolution because msger-routed
    // sendMessage / saveDraft IPCs disappear without trace. So when we're in
    // a child frame with a parent bridge, go through the parent.
    const inIframe = window.parent && window.parent !== window;
    if (inIframe && window.parent?.mailxapi?.isApp) {
        if (!cachedRelayBridge)
            cachedRelayBridge = buildRelayBridge();
        return cachedRelayBridge;
    }
    const bridge = getIpc();
    if (!bridge)
        throw new Error("IPC bridge not available");
    return bridge;
}
// ── Abort controller for message-list requests ──
let messageListAbort = null;
export function abortMessageListRequests() {
    if (messageListAbort) {
        messageListAbort.abort();
        messageListAbort = null;
    }
}
// ── API Methods ──
export function getAccounts() {
    return ipc().getAccounts();
}
export function getFolders(accountId) {
    return ipc().getFolders(accountId);
}
export function getMessages(accountId, folderId, page = 1, pageSize = 50, flaggedOnly = false, sort, sortDir, dateBasis) {
    abortMessageListRequests();
    return ipc().getMessages(accountId, folderId, page, pageSize, sort, sortDir, undefined, flaggedOnly, dateBasis);
}
export function getUnifiedInbox(page = 1, pageSize = 50, flaggedOnly = false, dateBasis) {
    abortMessageListRequests();
    return ipc().getUnifiedInbox(page, pageSize, flaggedOnly, dateBasis);
}
export function searchMessages(query, page = 1, pageSize = 50, scope = "all", accountId = "", folderId = 0, includeTrashSpam = false) {
    return ipc().searchMessages(query, page, pageSize, scope, accountId, folderId, includeTrashSpam);
}
/** Abort any in-flight server (IMAP) search. Fire-and-forget — the daemon
 *  bumps a generation counter its per-folder loop checks between batches. */
export function cancelServerSearch() {
    return ipc().cancelServerSearch?.();
}
export function getMessage(accountId, uid, allowRemote = false, folderId) {
    return ipc().getMessage(accountId, uid, allowRemote, folderId);
}
export function updateFlags(accountId, uid, flags, folderId) {
    return ipc().updateFlags(accountId, uid, flags, folderId);
}
export function triggerSync() {
    return ipc().syncAll();
}
export function syncAccount(accountId) {
    return ipc().syncAccount(accountId);
}
/** Sync one folder on demand (lazy-folder-sync: fired when the user opens a
 *  folder so it's fresh without the app sweeping all folders on a tight timer). */
export function syncFolderNow(accountId, folderId) {
    return ipc().syncFolderNow?.(accountId, folderId);
}
export function reauthenticate(accountId) {
    return ipc().reauthenticate(accountId);
}
export function reauthGoogleScopes() {
    return ipc().reauthGoogleScopes();
}
export function getSyncPending() {
    return ipc().getSyncPending();
}
export function getDiagnostics() {
    return ipc().getDiagnostics?.() ?? Promise.resolve([]);
}
/** Account that supplies `feature` data (calendar / tasks / contacts).
 *  Resolution: per-feature primary flag → catch-all `primary` → first account.
 *  Pass e.g. "calendar" to honor `primaryCalendar:true` overrides; omit for
 *  back-compat single-flag behavior. */
export function getPrimaryAccount(feature) {
    return ipc().getPrimaryAccount?.(feature) ?? Promise.resolve(null);
}
// Calendar / Tasks: two-way cache. Reads return local-cached rows; writes
// commit locally and queue a push to Google. Service layer handles drain.
export function getCalendarEvents(fromMs, toMs) {
    return ipc().getCalendarEvents?.(fromMs, toMs) ?? Promise.resolve([]);
}
/** List the user's selected Google calendars (id, name, color, primary) so
 *  the sidebar can render a checkbox + icon per calendar. */
export function getCalendars() {
    return ipc().getCalendars?.() ?? Promise.resolve([]);
}
export function createCalendarEvent(ev) {
    return ipc().createCalendarEvent?.(ev);
}
export function updateCalendarEvent(uuid, patch) {
    return ipc().updateCalendarEvent?.(uuid, patch);
}
export function deleteCalendarEvent(uuid) {
    return ipc().deleteCalendarEvent?.(uuid);
}
export function getTasks(includeCompleted = false) {
    return ipc().getTasks?.(includeCompleted) ?? Promise.resolve([]);
}
export function createTask(t) {
    return ipc().createTask?.(t);
}
export function updateTask(uuid, patch) {
    return ipc().updateTask?.(uuid, patch);
}
export function deleteTask(uuid) {
    return ipc().deleteTask?.(uuid);
}
export function drainStoreSync() {
    return ipc().drainStoreSync?.();
}
/** Report the currently-viewed message as spam → appends a row to
 *  `~/.mailx/spam.csv`. Placeholder: no folder move, no flag change, no
 *  auto-delete. Training data for a smarter pass later. */
export function recordSpamReport(accountId, uid, folderId) {
    return ipc().recordSpamReport?.(accountId, uid, folderId);
}
export function getOutboxStatus() {
    return ipc().getOutboxStatus();
}
export function listQueuedOutgoing() {
    return ipc().listQueuedOutgoing();
}
export function cancelQueuedOutgoing(p) {
    return ipc().cancelQueuedOutgoing(p);
}
export function searchContacts(query) {
    return ipc().searchContacts(query);
}
export function hasCcHistoryTo(email) {
    return ipc().hasCcHistoryTo(email);
}
export function hasBccHistoryTo(email) {
    return ipc().hasBccHistoryTo(email);
}
export function listContacts(query, page = 1, pageSize = 100) {
    return ipc().listContacts(query, page, pageSize);
}
export function upsertContact(name, email) {
    return ipc().upsertContact(name, email);
}
export function deleteContact(email) {
    return ipc().deleteContact(email);
}
export function addPreferredContact(entry) {
    return ipc().addPreferredContact(entry.name, entry.email, entry.source, entry.organization);
}
export function getPriorityLists() {
    return ipc().getPriorityLists();
}
export function setPrioritySender(email, value, name) {
    return ipc().setPrioritySender(email, value, name);
}
export function setPriorityDomain(domain, value) {
    return ipc().setPriorityDomain(domain, value);
}
export function addToDenylist(email) {
    return ipc().addToDenylist(email);
}
export function openLocalPath(which) {
    return ipc().openLocalPath(which);
}
/** Open an absolute file path (under ~/.rmfmail) in the OS default
 *  *text* editor — Notepad on Windows, TextEdit on Mac, $EDITOR or
 *  xdg-open(.txt) on Linux. Distinct from the file's default app,
 *  which for .eml is usually Outlook / a mail client. */
export function openInTextEditor(path) {
    return ipc().openInTextEditor?.(path) ?? Promise.resolve({ ok: false, opener: "none", reason: "no host" });
}
export function allowRemoteContent(type, value) {
    return ipc().allowRemoteContent(type, value);
}
export function getUserDict() {
    return ipc().getUserDict?.() ?? Promise.resolve([]);
}
export function addUserDictWord(word) {
    return ipc().addUserDictWord?.(word) ?? Promise.resolve([]);
}
export function addUserDictWords(words) {
    return ipc().addUserDictWords?.(words) ?? Promise.resolve([]);
}
export function removeUserDictWord(word) {
    return ipc().removeUserDictWord?.(word) ?? Promise.resolve([]);
}
export function flagSenderOrDomain(type, value) {
    return ipc().flagSenderOrDomain?.(type, value) ?? Promise.resolve({ flagged: false });
}
export function deleteMessage(accountId, uid, folderId) {
    return ipc().deleteMessage?.(accountId, uid, folderId);
}
export function deleteMessages(accountId, uids, folderIds) {
    if (uids.length === 1)
        return deleteMessage(accountId, uids[0], folderIds?.[0]);
    return ipc().deleteMessages?.(accountId, uids, folderIds);
}
export function moveMessages(accountId, uids, targetFolderId, folderIds, targetAccountId) {
    if (uids.length === 1)
        return moveMessage(accountId, uids[0], targetFolderId, targetAccountId);
    // folderIds[] disambiguates each uid to its source folder — IMAP UIDs are
    // per-folder, so a bare (account, uid) lookup can move the wrong message.
    return ipc().moveMessages?.(accountId, uids, targetFolderId, folderIds);
}
export function markAsSpamMessages(accountId, uids, folderIds) {
    return ipc().markAsSpamMessages?.(accountId, uids, folderIds);
}
/** Copy messages to a target folder (they stay in the source too). Carries
 *  per-message source folderIds index-aligned with uids, same as
 *  deleteMessages, so the daemon copies from the exact (folder, uid). */
export function copyMessages(accountId, uids, folderIds, targetFolderId) {
    return ipc().copyMessages?.(accountId, uids, folderIds, targetFolderId);
}
export function undeleteMessage(accountId, uid, folderId) {
    return ipc().undeleteMessage?.(accountId, uid, folderId);
}
export function moveMessage(accountId, uid, targetFolderId, targetAccountId) {
    return ipc().moveMessage?.(accountId, uid, targetFolderId, targetAccountId);
}
export function restartServer() {
    return ipc().restart?.();
}
export function markFolderRead(accountId, folderId) {
    return ipc().markFolderRead?.(accountId, folderId);
}
export function createFolder(accountId, parentPath, name) {
    return ipc().createFolder?.(accountId, parentPath, name);
}
/** Rename a folder in place, or reparent it when `newParentId` is given (drag a
 *  folder onto another). Both are a single server-side mailbox rename. */
export function renameFolder(accountId, folderId, newName, newParentId) {
    return ipc().renameFolder?.(accountId, folderId, newName, newParentId);
}
export function deleteFolder(accountId, folderId) {
    return ipc().deleteFolder?.(accountId, folderId);
}
export function moveFolderToTrash(accountId, folderId) {
    return ipc().moveFolderToTrash?.(accountId, folderId);
}
export function emptyFolder(accountId, folderId) {
    return ipc().emptyFolder?.(accountId, folderId);
}
/** Ship a named event to the Node log as `[client] <tag> <data>`. Fire and
 *  forget — never awaits, never throws, never blocks the caller. Tries two
 *  paths so a broken primary channel can't swallow the trace:
 *    1. Direct bridge call (self / opener / parent mailxapi).
 *    2. parent.postMessage fallback — the main window listens and relays.
 *  The fallback matters because the whole point of tracing is to diagnose
 *  a broken iframe bridge; a single-path tracer that goes through that same
 *  bridge is useless in exactly the case we need it. */
/** Capture every console.log / console.warn / console.error / console.info
 *  / console.debug call AND every uncaught error + unhandledrejection, and
 *  route them through logClientEvent to the daemon log.
 *
 *  Why: debugging "the list is empty" or "preview but no summary" required
 *  asking the user to open devtools and read the console. That's slow and
 *  often the user has already navigated away by the time they look. With
 *  this hook everything the WebView console would show is also in
 *  rmfmail-YYYY-MM-DD.log — searchable, persistent, no devtools required.
 *
 *  Idempotent. Original console methods preserved on _origConsole so any
 *  internal logger (including logClientEvent's own bridge fallback) can
 *  still write to the real console without recursing through the hook. */
export function installConsoleCapture() {
    const g = globalThis;
    if (g.__mailxConsoleCaptureInstalled)
        return;
    g.__mailxConsoleCaptureInstalled = true;
    const orig = {
        log: console.log.bind(console),
        info: console.info.bind(console),
        warn: console.warn.bind(console),
        error: console.error.bind(console),
        debug: console.debug.bind(console),
    };
    g._origConsole = orig;
    const serialize = (v) => {
        if (v == null)
            return v;
        const t = typeof v;
        if (t === "string" || t === "number" || t === "boolean")
            return v;
        if (v instanceof Error)
            return { __err: true, message: v.message, stack: v.stack };
        try {
            // Cap large objects so a circular ref or a giant DOM dump
            // doesn't OOM the IPC channel.
            const s = JSON.stringify(v);
            return s.length > 4096 ? s.slice(0, 4096) + "…[truncated]" : JSON.parse(s);
        }
        catch {
            try {
                return String(v);
            }
            catch {
                return "[unserializable]";
            }
        }
    };
    const forward = (level, args) => {
        try {
            // First arg often a format string; rest are values. Pass as array.
            logClientEvent(`console.${level}`, { args: args.map(serialize) });
        }
        catch { /* never throw from log capture */ }
    };
    // CRITICAL: only forward warn + error to the daemon log. The first cut
    // of this (1.1.178) forwarded console.log/info/debug too — but mailx logs
    // console.log on a hot path (per-message preview timing, sync ticks),
    // turning EVERY one into an IPC round-trip. That ~100x'd IPC traffic,
    // saturated the channel, and the getUnifiedInbox RESPONSE (sent by the
    // daemon in ~180ms) couldn't get processed client-side — the list hung on
    // "Loading…" forever (Bob 2026-05-28, "gave up"). warn/error are rare and
    // high-value; log/info/debug stay console-only. Explicit logClientEvent()
    // calls elsewhere are unaffected.
    console.log = (...args) => { orig.log(...args); };
    console.info = (...args) => { orig.info(...args); };
    console.debug = (...args) => { orig.debug(...args); };
    console.warn = (...args) => { forward("warn", args); orig.warn(...args); };
    console.error = (...args) => { forward("error", args); orig.error(...args); };
    // Uncaught errors — what stops renderMessages mid-execution and leaves
    // a blank list. Without this we'd see nothing in the daemon log.
    try {
        window.addEventListener("error", (e) => {
            try {
                logClientEvent("window.error", {
                    message: e.message,
                    filename: e.filename,
                    lineno: e.lineno,
                    colno: e.colno,
                    stack: e.error?.stack || null,
                });
            }
            catch { /* */ }
        });
        window.addEventListener("unhandledrejection", (e) => {
            try {
                const r = e.reason;
                const msg = r?.message || String(r);
                // Break the cascade: a `logClientEvent` IPC timeout is itself a
                // rejection; forwarding it spawns ANOTHER logClientEvent which
                // can also time out → a self-amplifying flood (Bob 2026-05-28
                // saw dozens/sec). Never forward tracing-channel rejections.
                if (/logClientEvent|mailxapi timeout/i.test(msg))
                    return;
                logClientEvent("window.unhandledrejection", {
                    message: msg,
                    stack: r?.stack || null,
                });
            }
            catch { /* */ }
        });
    }
    catch { /* */ }
}
export function logClientEvent(tag, data) {
    let delivered = false;
    try {
        const bridge = typeof globalThis.mailxapi !== "undefined" && globalThis.mailxapi?.isApp ? globalThis.mailxapi
            : window.opener?.mailxapi?.isApp ? window.opener.mailxapi
                : window.parent?.mailxapi?.isApp ? window.parent.mailxapi
                    : null;
        if (bridge?.logClientEvent) {
            // bridge.logClientEvent returns a callNode promise that REJECTS on
            // IPC timeout. Swallow it — an unhandled rejection here cascades
            // (the rejection gets logged → spawns another logClientEvent →
            // …). Fire-and-forget with a no-op catch.
            const p = bridge.logClientEvent(tag, data);
            if (p && typeof p.catch === "function")
                p.catch(() => { });
            delivered = true;
        }
    }
    catch { /* never throw from tracing */ }
    try {
        if (window.parent && window.parent !== window) {
            window.parent.postMessage({ type: "mailx-trace", tag, data, bridged: delivered }, "*");
        }
    }
    catch { /* */ }
}
export function sendMessage(body) {
    return ipc().sendMessage?.(body);
}
/** Answer leg of the --debug-server /api/eval round trip (see the
 *  `debugEval` event handler in app.ts). */
export function debugEvalResult(id, result, error) {
    return ipc().debugEvalResult?.(id, result, error);
}
export function saveDraft(body) {
    return ipc().saveDraft?.(body);
}
const eventHandlers = [];
export function onEvent(handler) {
    eventHandlers.push(handler);
    return () => {
        const i = eventHandlers.indexOf(handler);
        if (i >= 0)
            eventHandlers.splice(i, 1);
    };
}
const storeSubs = new Map();
export function subscribeStore(topic, handler) {
    let set = storeSubs.get(topic);
    if (!set) {
        set = new Set();
        storeSubs.set(topic, set);
    }
    set.add(handler);
    return () => {
        const s = storeSubs.get(topic);
        if (s) {
            s.delete(handler);
            if (s.size === 0)
                storeSubs.delete(topic);
        }
    };
}
function deliverStore(event) {
    const exact = storeSubs.get(event.topic);
    if (exact)
        for (const h of exact) {
            try {
                h(event);
            }
            catch (e) {
                console.error("[store-bus]", e);
            }
        }
    const wild = storeSubs.get("*");
    if (wild)
        for (const h of wild) {
            try {
                h(event);
            }
            catch (e) {
                console.error("[store-bus]", e);
            }
        }
}
export function connectEvents() {
    ipc().onEvent((event) => {
        if (event && event._event === "store")
            deliverStore(event);
        for (const h of eventHandlers)
            h(event);
    });
}
// ── Autocomplete ──
export function autocomplete(body, signal) {
    return ipc().autocomplete?.(body);
}
export function getAutocompleteSettings() {
    return ipc().getAutocompleteSettings?.();
}
export function saveAutocompleteSettings(settings) {
    return ipc().saveAutocompleteSettings?.(settings);
}
export function getVersion() {
    return ipc().getVersion();
}
export function getSettings() {
    return ipc().getSettings();
}
export function saveSettings(settings) {
    return ipc().saveSettingsData?.(settings);
}
export function repairAccounts() {
    return ipc().repairAccounts?.();
}
export function deleteDraft(accountId, draftUid, draftId) {
    return ipc().deleteDraft?.(accountId, draftUid, draftId);
}
export function addContact(name, email) {
    return ipc().addContact?.(name, email);
}
export function getThreadMessages(accountId, threadId) {
    return ipc().getThreadMessages?.(accountId, threadId);
}
export function readJsoncFile(name) {
    return ipc().readJsoncFile?.(name);
}
export function getReminderSound() {
    return ipc().getReminderSound?.();
}
export function writeJsoncFile(name, content) {
    return ipc().writeJsoncFile?.(name, content);
}
export function formatJsonc(content) {
    return ipc().formatJsonc?.(content);
}
export function readConfigHelp(name) {
    return ipc().readConfigHelp?.(name) ?? Promise.resolve({ content: "" });
}
export function unsubscribeOneClick(url) {
    return ipc().unsubscribeOneClick?.(url);
}
export function openInWord(editId, html) {
    return ipc().openInWord?.(editId, html) ?? Promise.resolve({ ok: false, path: "", opener: "none" });
}
export function closeWordEdit(editId) {
    return ipc().closeWordEdit?.(editId) ?? Promise.resolve();
}
/** Show an OS-level always-on-top reminder popup via msger. Returns the
 *  label of the button the user clicked. Empty string when the host
 *  isn't available (browser fallback) — caller should fall back to an
 *  in-WebView popup in that case. */
export function showReminderPopup(opts) {
    return ipc().showReminderPopup?.(opts) ?? Promise.resolve({ button: "", reason: "no host" });
}
/** Read + delete a pending mailto: drop file, if any (P115). Used at app
 *  startup so a `mailx --mailto <url>` that just spawned us doesn't lose
 *  its compose payload to the daemon-fires-before-app-registers race
 *  window. Subsequent live clicks arrive via the `openMailto` event. */
export function consumePendingMailto() {
    return ipc().consumePendingMailto?.() ?? Promise.resolve(null);
}
/** Run an AI text transform (translate / proofread / summarize). Returns
 *  empty `text` with a `reason` when the feature is disabled or the provider
 *  errors — caller should surface `reason` in a status bar, not throw. */
export function aiTransform(req) {
    return ipc().aiTransform?.(req) ?? Promise.resolve({ text: "", reason: "AI not available in this host" });
}
export function setupAccount(name, email, password) {
    return ipc().setupAccount?.(name, email, password);
}
export async function getAttachment(accountId, uid, attachmentId, folderId) {
    return ipc().getAttachment(accountId, uid, attachmentId, folderId);
}
/** Desktop: have the Node service save the attachment and open it with the
 *  OS default app. Returns undefined when the host has no such method (real
 *  browser / --server mode) so the caller can fall back to a blob download. */
export async function openAttachment(accountId, uid, attachmentId, folderId, filename) {
    const fn = ipc().openAttachment;
    return fn ? fn(accountId, uid, attachmentId, folderId, filename) : undefined;
}
/** Raw RFC 822 source (base64) of a message — for "Save message as .eml". */
export async function getMessageSource(accountId, uid, folderId) {
    return ipc().getMessageSource(accountId, uid, folderId);
}
/** Open a message in its own native OS window (msger WebView spawned by the
 *  daemon). Resolves { ok:false, reason } when no popout host is available —
 *  caller falls back to the in-window overlay. */
/** Offline grammar lint via the daemon's Harper engine. Throws when the
 *  host has no grammarLint (Android web-service today) — caller disables. */
export async function grammarLint(blocks) {
    const fn = ipc().grammarLint;
    if (!fn)
        throw new Error("no grammarLint bridge");
    return fn(blocks);
}
/** Register rmfmail as the OS-level mailto: handler (Settings menu). */
export async function registerMailto() {
    const fn = ipc().registerMailto;
    if (!fn)
        return { ok: false, message: "Not available in this host — run `rmfmail -register-mailto` from a terminal." };
    return fn();
}
export async function popoutWindow(accountId, uid, folderId, subject) {
    const fn = ipc().popoutWindow;
    if (!fn)
        return { ok: false, reason: "no popoutWindow bridge" };
    return fn(accountId, uid, folderId, subject);
}
/** Take a live compose into its own native OS window. init is the full
 *  compose state; the daemon stashes it and spawns a popout-server-backed
 *  msger window that consumes it on boot. */
export async function popoutCompose(init) {
    const fn = ipc().popoutCompose;
    if (!fn)
        return { ok: false, reason: "no popoutCompose bridge" };
    return fn(init);
}
/** Popout compose page boot: one-shot fetch of the init stashed by popoutCompose. */
export async function consumePopoutComposeInit(id) {
    const fn = ipc().consumePopoutComposeInit;
    if (!fn)
        return { init: null };
    return fn(id);
}
/** Ask the daemon to close this compose-popout window (after send/discard). */
export function closeComposePopout(id) {
    const fn = ipc().closeComposePopout;
    return fn ? fn(id) : Promise.resolve({ ok: false });
}
/** Compose paste: daemon-side fetch of a remote image, returned as a data:
 *  URI (or { error }) — beats hotlink protection and editor CSP. */
export function fetchImageAsDataUri(url) {
    const fn = ipc().fetchImageAsDataUri;
    return fn ? fn(url) : Promise.resolve({ error: "no bridge" });
}
/** View → New window: open another full mailx window as its own OS window
 *  (e.g. Drafts in one, Sent in another, side by side). */
export async function popoutMainWindow(accountId, folderId, name) {
    const fn = ipc().popoutMainWindow;
    if (!fn)
        return { ok: false, reason: "no popoutMainWindow bridge" };
    return fn(accountId, folderId, name);
}
export async function getDeviceAccounts() {
    return ipc().getDeviceAccounts?.() ?? [];
}
// Legacy exports for backward compatibility
export const connectWebSocket = connectEvents;
export const onWsEvent = onEvent;
//# sourceMappingURL=api-client.js.map