98 lines · 3.1 KB · TypeScript
RawDownload
import type { ConfirmDialogOptions, ContextMenuItem, LocalApi } from "@t3tools/contracts"; import { requestConfirmDialog } from "./confirmDialog";import { dismissContextMenu, showContextMenuFallback } from "./contextMenuFallback";import { readBrowserClientSettings, writeBrowserClientSettings } from "./clientPersistenceStorage"; let cachedApi: LocalApi | undefined; function createBrowserLocalApi(): LocalApi {  return {    dialogs: {      pickFolder: async (options) => {        if (!window.desktopBridge) return null;        return window.desktopBridge.pickFolder(options);      },      confirm: async (message, options?: ConfirmDialogOptions) => {        return requestConfirmDialog(message, options) ?? false;      },    },    shell: {      openExternal: async (url) => {        if (window.desktopBridge) {          const opened = await window.desktopBridge.openExternal(url);          if (!opened) {            throw new Error("Unable to open link.");          }          return;        }         window.open(url, "_blank", "noopener,noreferrer");      },      // Only the desktop shell can reach the OS; the web build (and older      // desktop shells that predate this method) have nothing to open.      openSystemSettings: async (pane) => {        if (!window.desktopBridge?.openSystemSettings) {          throw new Error("Unable to open System Settings.");        }        const opened = await window.desktopBridge.openSystemSettings(pane);        if (!opened) {          throw new Error("Unable to open System Settings.");        }      },    },    contextMenu: {      show: async <T extends string>(        items: readonly ContextMenuItem<T>[],        position?: { x: number; y: number },      ): Promise<T | null> => {        if (window.desktopBridge) {          return window.desktopBridge.showContextMenu(items, position) as Promise<T | null>;        }        return showContextMenuFallback(items, position);      },      // A native desktop menu blocks keyboard input and closes on outside      // interaction, so nothing to do there; the DOM fallback needs an explicit      // dismiss when the state behind it goes away.      close: async () => {        if (!window.desktopBridge) {          dismissContextMenu();        }      },    },    persistence: {      getClientSettings: async () => {        if (window.desktopBridge) {          return window.desktopBridge.getClientSettings();        }        return readBrowserClientSettings();      },      setClientSettings: async (settings) => {        if (window.desktopBridge) {          return window.desktopBridge.setClientSettings(settings);        }        writeBrowserClientSettings(settings);      },    },  };} export function createLocalApi(): LocalApi {  return createBrowserLocalApi();} export function readLocalApi(): LocalApi | undefined {  if (typeof window === "undefined") return undefined;  if (cachedApi) return cachedApi;   cachedApi = createLocalApi();  return cachedApi;} export function ensureLocalApi(): LocalApi {  const api = readLocalApi();  if (!api) {    throw new Error("Local API not found");  }  return api;}