diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 2c9aa9feef0009de69fd06b91d2c0aeccb634539..107371cc16ebe5782a99fad33f13488604fc2504 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -10,13 +10,11 @@ import type { MenuItemConstructorOptions } from "electron"; import { showDesktopConfirmDialog } from "./confirmDialog"; import { fixPath } from "./fixPath"; -import { showDesktopPromptDialog } from "./promptDialog"; fixPath(); const PICK_FOLDER_CHANNEL = "desktop:pick-folder"; const CONFIRM_CHANNEL = "desktop:confirm"; -const PROMPT_CHANNEL = "desktop:prompt"; const CONTEXT_MENU_CHANNEL = "desktop:context-menu"; const OPEN_EXTERNAL_CHANNEL = "desktop:open-external"; const MENU_ACTION_CHANNEL = "desktop:menu-action"; @@ -436,20 +434,6 @@ function registerIpcHandlers(): void { return showDesktopConfirmDialog(message, owner); }); - ipcMain.removeHandler(PROMPT_CHANNEL); - ipcMain.handle(PROMPT_CHANNEL, async (_event, message: unknown, defaultValue: unknown) => { - if (typeof message !== "string") { - return null; - } - - const owner = BrowserWindow.getFocusedWindow() ?? mainWindow; - return showDesktopPromptDialog( - message, - typeof defaultValue === "string" ? defaultValue : "", - owner, - ); - }); - ipcMain.removeHandler(CONTEXT_MENU_CHANNEL); ipcMain.handle( CONTEXT_MENU_CHANNEL, diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index c251075e86844c8f58f4e1e03d25ccb574df4ec6..be8fbbddd82cb5654dd51172215965fbf4c47f40 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -3,7 +3,6 @@ import type { DesktopBridge } from "@t3tools/contracts"; const PICK_FOLDER_CHANNEL = "desktop:pick-folder"; const CONFIRM_CHANNEL = "desktop:confirm"; -const PROMPT_CHANNEL = "desktop:prompt"; const CONTEXT_MENU_CHANNEL = "desktop:context-menu"; const OPEN_EXTERNAL_CHANNEL = "desktop:open-external"; const MENU_ACTION_CHANNEL = "desktop:menu-action"; @@ -13,7 +12,6 @@ contextBridge.exposeInMainWorld("desktopBridge", { getWsUrl: () => wsUrl, pickFolder: () => ipcRenderer.invoke(PICK_FOLDER_CHANNEL), confirm: (message) => ipcRenderer.invoke(CONFIRM_CHANNEL, message), - prompt: (message, defaultValue) => ipcRenderer.invoke(PROMPT_CHANNEL, message, defaultValue), showContextMenu: (items, position) => ipcRenderer.invoke(CONTEXT_MENU_CHANNEL, items, position), openExternal: (url: string) => ipcRenderer.invoke(OPEN_EXTERNAL_CHANNEL, url), onMenuAction: (listener) => { diff --git a/apps/desktop/src/promptDialog.ts b/apps/desktop/src/promptDialog.ts deleted file mode 100644 index 81f14dfcaf39c4a4fb966749a2cf440628aef557..0000000000000000000000000000000000000000 --- a/apps/desktop/src/promptDialog.ts +++ /dev/null @@ -1,231 +0,0 @@ -import { randomBytes } from "node:crypto"; -import { BrowserWindow, ipcMain } from "electron"; - -const PROMPT_WIDTH = 420; -const PROMPT_HEIGHT = 188; - -function createPromptHtml(input: { - message: string; - defaultValue: string; - submitChannel: string; - cancelChannel: string; -}): string { - const scriptConfig = JSON.stringify(input); - return ` - - - - - ${input.message} - - - -

-
- -
- - -
-
- - -`; -} - -export async function showDesktopPromptDialog( - message: string, - defaultValue: string, - ownerWindow: BrowserWindow | null, -): Promise { - const normalizedMessage = message.trim(); - if (normalizedMessage.length === 0) { - return null; - } - - const token = randomBytes(12).toString("hex"); - const submitChannel = `desktop:prompt-dialog:submit:${token}`; - const cancelChannel = `desktop:prompt-dialog:cancel:${token}`; - - const promptWindow = new BrowserWindow({ - width: PROMPT_WIDTH, - height: PROMPT_HEIGHT, - useContentSize: true, - show: false, - resizable: false, - minimizable: false, - maximizable: false, - fullscreenable: false, - autoHideMenuBar: true, - title: normalizedMessage, - ...(ownerWindow ? { parent: ownerWindow } : {}), - modal: ownerWindow !== null, - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - }, - }); - promptWindow.removeMenu(); - - return new Promise((resolve) => { - let settled = false; - - const settle = (value: string | null) => { - if (settled) return; - settled = true; - cleanup(); - resolve(value); - }; - - const cleanup = () => { - ipcMain.removeAllListeners(submitChannel); - ipcMain.removeAllListeners(cancelChannel); - if (!promptWindow.isDestroyed()) { - promptWindow.destroy(); - } - }; - - ipcMain.once(submitChannel, (_event, value: unknown) => { - if (typeof value !== "string") { - settle(null); - return; - } - settle(value); - }); - ipcMain.once(cancelChannel, () => settle(null)); - promptWindow.once("closed", () => settle(null)); - - const html = createPromptHtml({ - message: normalizedMessage, - defaultValue, - submitChannel, - cancelChannel, - }); - void promptWindow - .loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`) - .then(() => { - if (!promptWindow.isDestroyed()) { - promptWindow.show(); - } - }) - .catch(() => settle(null)); - }); -} diff --git a/apps/web/src/wsNativeApi.test.ts b/apps/web/src/wsNativeApi.test.ts index 4e4f61e6d2217e931180120fa8265c61460c337d..80e283ee40fbe6630285ea7238876eb0963426fa 100644 --- a/apps/web/src/wsNativeApi.test.ts +++ b/apps/web/src/wsNativeApi.test.ts @@ -68,7 +68,6 @@ beforeEach(() => { subscribeMock.mockClear(); channelListeners.clear(); Reflect.deleteProperty(getWindowForTest(), "desktopBridge"); - Reflect.deleteProperty(getWindowForTest(), "prompt"); }); afterEach(() => { @@ -377,37 +376,4 @@ describe("wsNativeApi", () => { ); }); - it("forwards prompt calls to desktop bridge", async () => { - const prompt = vi.fn().mockResolvedValue("renamed"); - Object.defineProperty(getWindowForTest(), "desktopBridge", { - configurable: true, - writable: true, - value: { - prompt, - }, - }); - - const { createWsNativeApi } = await import("./wsNativeApi"); - const api = createWsNativeApi(); - const value = await api.dialogs.prompt("Rename thread", "Old title"); - - expect(value).toBe("renamed"); - expect(prompt).toHaveBeenCalledWith("Rename thread", "Old title"); - }); - - it("uses browser prompt fallback when desktop bridge is unavailable", async () => { - const prompt = vi.fn().mockReturnValue("renamed"); - Object.defineProperty(getWindowForTest(), "prompt", { - configurable: true, - writable: true, - value: prompt, - }); - - const { createWsNativeApi } = await import("./wsNativeApi"); - const api = createWsNativeApi(); - const value = await api.dialogs.prompt("Rename thread", "Old title"); - - expect(value).toBe("renamed"); - expect(prompt).toHaveBeenCalledWith("Rename thread", "Old title"); - }); }); diff --git a/apps/web/src/wsNativeApi.ts b/apps/web/src/wsNativeApi.ts index aa6830022b91daa126c6c72091d40708ef7d2cd6..871e01c911126daee8a492a33ec57fff44c42d82 100644 --- a/apps/web/src/wsNativeApi.ts +++ b/apps/web/src/wsNativeApi.ts @@ -124,16 +124,6 @@ export function createWsNativeApi(): NativeApi { } return window.confirm(message); }, - prompt: async (message, defaultValue) => { - if (window.desktopBridge) { - return window.desktopBridge.prompt(message, defaultValue); - } - try { - return window.prompt(message, defaultValue ?? ""); - } catch { - return null; - } - }, }, terminal: { open: (input) => transport.request(WS_METHODS.terminalOpen, input), diff --git a/packages/contracts/src/ipc.ts b/packages/contracts/src/ipc.ts index bf12d8cb5d8be47bb388edeebd7de6af74e538e2..aeec16ca0c32c9bee0d9b1f3c481e0917ac7587f 100644 --- a/packages/contracts/src/ipc.ts +++ b/packages/contracts/src/ipc.ts @@ -47,7 +47,6 @@ export interface DesktopBridge { getWsUrl: () => string | null; pickFolder: () => Promise; confirm: (message: string) => Promise; - prompt: (message: string, defaultValue?: string) => Promise; showContextMenu: ( items: readonly ContextMenuItem[], position?: { x: number; y: number }, @@ -60,7 +59,6 @@ export interface NativeApi { dialogs: { pickFolder: () => Promise; confirm: (message: string) => Promise; - prompt: (message: string, defaultValue?: string) => Promise; }; terminal: { open: (input: TerminalOpenInput) => Promise;