diff --git a/src/bun/index.js b/src/bun/index.js index d0b4ad3..215d209 100644 --- a/src/bun/index.js +++ b/src/bun/index.js @@ -1,7 +1,6 @@ import { closeSingleInstanceServer, - ensureSingleInstanceLock, - setSingleInstanceHandlers + ensureSingleInstanceLock } from '../desktop/single-instance.js' const gotSingleInstanceLock = await ensureSingleInstanceLock() @@ -17,21 +16,16 @@ const { } = await import('../desktop/spotlight.js') const { createMainWindow, - handleDeepLink, handleDeepLinkFromArgv, setupDevAuthServer, setupNavigationGestures, - showMainWindow + setupWindowsDeepLinkHandling } = await import('../desktop/window.js') -setSingleInstanceHandlers({ - onDeepLink: handleDeepLink, - onFocus: showMainWindow -}) - const rpc = createAppRpc() const mainWindow = await createMainWindow(rpc) +setupWindowsDeepLinkHandling() setupNavigationGestures(mainWindow) registerGlobalShortcuts(rpc) setupDevAuthServer() diff --git a/src/desktop/single-instance.js b/src/desktop/single-instance.js index fedbaee..e890314 100644 --- a/src/desktop/single-instance.js +++ b/src/desktop/single-instance.js @@ -114,10 +114,11 @@ export function findProtocolUrl(args) { const trimmed = arg.trim().replace(/^['"]+|['"]+$/g, '') const match = trimmed.match(/farmcontrol:\/\/\S+/i) if (match) { + const rawUrl = match[0].replace(/['"]+$/g, '') try { - return decodeURI(match[0]) + return decodeURI(rawUrl) } catch { - return match[0] + return rawUrl } } } @@ -125,10 +126,11 @@ export function findProtocolUrl(args) { const combined = sources.join(' ') const combinedMatch = combined.match(/farmcontrol:\/\/\S+/i) if (combinedMatch) { + const rawUrl = combinedMatch[0].replace(/['"]+$/g, '') try { - return decodeURI(combinedMatch[0]) + return decodeURI(rawUrl) } catch { - return combinedMatch[0] + return rawUrl } } @@ -212,7 +214,6 @@ function dispatchMessage(message) { if (resolved.type === 'deeplink' && resolved.url) { handlers.onDeepLink?.(resolved.url) - handlers.onFocus?.() return } @@ -243,7 +244,7 @@ function startInstanceSignalWatcher() { // Polling below covers filesystems without reliable watch support. } - pollInterval = setInterval(processInstanceSignal, 250) + pollInterval = setInterval(processInstanceSignal, 100) } export async function ensureSingleInstanceLock() { @@ -252,6 +253,7 @@ export async function ensureSingleInstanceLock() { } if (tryAcquirePrimaryLock()) { + startInstanceSignalWatcher() return true } @@ -261,7 +263,6 @@ export async function ensureSingleInstanceLock() { export function setSingleInstanceHandlers({ onDeepLink, onFocus }) { handlers = { onDeepLink, onFocus } - startInstanceSignalWatcher() while (pendingMessages.length > 0) { dispatchMessage(pendingMessages.shift()) diff --git a/src/desktop/window.js b/src/desktop/window.js index 7af88b4..fed758b 100644 --- a/src/desktop/window.js +++ b/src/desktop/window.js @@ -10,7 +10,7 @@ const isMacOS = process.platform === 'darwin' const DEV_SERVER_PORT = 5780 const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}` -import { findProtocolUrl } from './single-instance.js' +import { findProtocolUrl, setSingleInstanceHandlers } from './single-instance.js' import { getWindowsLaunchSources } from './windows-launch-args.js' let mainWindow = null @@ -86,14 +86,46 @@ function sendNavigateToRenderer(redirectPath) { setTimeout(() => deliverNavigation(redirectPath), 100) } -export function handleDeepLink(url) { - if (!url || typeof url !== 'string') return +export function openInternalUrl(url) { + sendNavigateToRenderer(url) + return true +} + +function parseDeepLinkPath(url) { + if (!url || typeof url !== 'string') { + return null + } + + if (url.startsWith('/')) { + return url + } const match = url.match(/^farmcontrol:\/\/app(.*)$/i) - if (!match) return + if (!match) { + return null + } const redirectPath = match[1] || '/' - sendNavigateToRenderer(redirectPath.startsWith('/') ? redirectPath : `/${redirectPath}`) + const normalizedPath = redirectPath.startsWith('/') + ? redirectPath + : `/${redirectPath}` + + try { + return decodeURI(normalizedPath) + } catch { + return normalizedPath + } +} + +export function handleDeepLink(url) { + const path = parseDeepLinkPath(url) + if (!path) { + showMainWindow() + return false + } + + openInternalUrl(path) + return true } export function handleDeepLinkFromArgv() { @@ -262,9 +294,15 @@ export async function setupDevAuthServer() { app.listen(port, () => {}) } -export function openInternalUrl(url) { - sendNavigateToRenderer(url) - return true +export function setupWindowsDeepLinkHandling() { + if (process.platform === 'darwin') { + return + } + + setSingleInstanceHandlers({ + onDeepLink: handleDeepLink, + onFocus: showMainWindow + }) } export function getWindowState() {