1import type { ConfirmDialogOptions, ContextMenuItem, LocalApi } from "@t3tools/contracts";
2
3import { requestConfirmDialog } from "./confirmDialog";
4import { dismissContextMenu, showContextMenuFallback } from "./contextMenuFallback";
5import { readBrowserClientSettings, writeBrowserClientSettings } from "./clientPersistenceStorage";
6
7let cachedApi: LocalApi | undefined;
8
9export function createLocalApi(): LocalApi {
10 return {
11 dialogs: {
12 pickFolder: async (options) => {
13 if (!window.desktopBridge) return null;
14 return window.desktopBridge.pickFolder(options);
15 },
16 confirm: async (message, options?: ConfirmDialogOptions) => {
17 return requestConfirmDialog(message, options) ?? false;
18 },
19 },
20 shell: {
21 openExternal: async (url) => {
22 if (window.desktopBridge) {
23 const opened = await window.desktopBridge.openExternal(url);
24 if (!opened) {
25 throw new Error("Unable to open link.");
26 }
27 return;
28 }
29
30 window.open(url, "_blank", "noopener,noreferrer");
31 },
32 openSystemSettings: async (pane) => {
33 if (!window.desktopBridge) {
34 throw new Error("Unable to open System Settings.");
35 }
36 const opened = await window.desktopBridge.openSystemSettings(pane);
37 if (!opened) {
38 throw new Error("Unable to open System Settings.");
39 }
40 },
41 },
42 contextMenu: {
43 show: async <T extends string>(
44 items: readonly ContextMenuItem<T>[],
45 position?: { x: number; y: number },
46 ): Promise<T | null> => {
47 if (window.desktopBridge) {
48 return window.desktopBridge.showContextMenu(items, position) as Promise<T | null>;
49 }
50 return showContextMenuFallback(items, position);
51 },
52 // Unlike native menus, the DOM fallback needs dismissal when underlying state disappears.
53 close: async () => {
54 if (!window.desktopBridge) {
55 dismissContextMenu();
56 }
57 },
58 },
59 persistence: {
60 getClientSettings: async () => {
61 if (window.desktopBridge) {
62 return window.desktopBridge.getClientSettings();
63 }
64 return readBrowserClientSettings();
65 },
66 setClientSettings: async (settings) => {
67 if (window.desktopBridge) {
68 return window.desktopBridge.setClientSettings(settings);
69 }
70 writeBrowserClientSettings(settings);
71 },
72 },
73 };
74}
75
76export function readLocalApi(): LocalApi | undefined {
77 if (typeof window === "undefined") return undefined;
78 if (cachedApi) return cachedApi;
79
80 cachedApi = createLocalApi();
81 return cachedApi;
82}
83
84export function ensureLocalApi(): LocalApi {
85 const api = readLocalApi();
86 if (!api) {
87 throw new Error("Local API not found");
88 }
89 return api;
90}