import type { ContextMenuItem } from "@t3tools/contracts"; export type ExternalLinkContextMenuAction = "open-in-preview" | "open-external" | "copy-link"; export type ExternalLinkContextMenuFailureOperation = | "show-link-context-menu" | "open-link-in-preview" | "open-link-external" | "copy-link"; const FAILURE_OPERATION_BY_ACTION = { "open-in-preview": "open-link-in-preview", "open-external": "open-link-external", "copy-link": "copy-link", } as const satisfies Record; const EXTERNAL_LINK_CONTEXT_MENU_ITEMS = [ { id: "open-in-preview", label: "Open in integrated browser" }, { id: "open-external", label: "Open in system browser" }, { id: "copy-link", label: "Copy Link" }, ] as const satisfies readonly ContextMenuItem[]; interface ShowExternalLinkContextMenuOptions { readonly href: string; readonly position: { readonly x: number; readonly y: number }; readonly showContextMenu: ( items: readonly ContextMenuItem[], position: { readonly x: number; readonly y: number }, ) => Promise; readonly openInPreview: (href: string) => Promise; readonly openExternal: (href: string) => Promise; readonly copyLink: (href: string) => Promise; readonly reportFailure: ( operation: ExternalLinkContextMenuFailureOperation, cause: unknown, ) => void; } export function resolveExternalWebLinkHost(href: string | undefined): string | null { if (!href) return null; try { const url = new URL(href); if (url.protocol !== "http:" && url.protocol !== "https:") return null; return url.hostname || null; } catch { return null; } } export async function showExternalLinkContextMenu({ href, position, showContextMenu, openInPreview, openExternal, copyLink, reportFailure, }: ShowExternalLinkContextMenuOptions): Promise { let action: ExternalLinkContextMenuAction | null; try { action = await showContextMenu(EXTERNAL_LINK_CONTEXT_MENU_ITEMS, position); } catch (cause) { reportFailure("show-link-context-menu", cause); return; } try { if (action === "open-in-preview") { await openInPreview(href); } else if (action === "open-external") { await openExternal(href); } else if (action === "copy-link") { await copyLink(href); } } catch (cause) { if (action) reportFailure(FAILURE_OPERATION_BY_ACTION[action], cause); } }