Compare commits
No commits in common. "611cd4aad523666d6c5f0cecae5f1050e93ee69c" and "d0345fd7f31a7b2be78456abc89c4c81394dff55" have entirely different histories.
611cd4aad5
...
d0345fd7f3
@ -1,30 +1,15 @@
|
|||||||
import { createAppRpc } from '../desktop/rpc.js'
|
import { createAppRpc } from '../desktop/rpc.js'
|
||||||
import {
|
|
||||||
closeSingleInstanceServer,
|
|
||||||
ensureSingleInstanceLock
|
|
||||||
} from '../desktop/single-instance.js'
|
|
||||||
import {
|
import {
|
||||||
registerGlobalShortcuts,
|
registerGlobalShortcuts,
|
||||||
unregisterGlobalShortcuts
|
unregisterGlobalShortcuts
|
||||||
} from '../desktop/spotlight.js'
|
} from '../desktop/spotlight.js'
|
||||||
import {
|
import {
|
||||||
createMainWindow,
|
createMainWindow,
|
||||||
handleDeepLink,
|
|
||||||
handleDeepLinkFromArgv,
|
handleDeepLinkFromArgv,
|
||||||
setupDevAuthServer,
|
setupDevAuthServer,
|
||||||
setupNavigationGestures,
|
setupNavigationGestures
|
||||||
showMainWindow
|
|
||||||
} from '../desktop/window.js'
|
} from '../desktop/window.js'
|
||||||
|
|
||||||
const gotSingleInstanceLock = await ensureSingleInstanceLock({
|
|
||||||
onDeepLink: handleDeepLink,
|
|
||||||
onFocus: showMainWindow
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!gotSingleInstanceLock) {
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rpc = createAppRpc()
|
const rpc = createAppRpc()
|
||||||
const mainWindow = await createMainWindow(rpc)
|
const mainWindow = await createMainWindow(rpc)
|
||||||
|
|
||||||
@ -35,5 +20,4 @@ handleDeepLinkFromArgv()
|
|||||||
|
|
||||||
process.on('exit', () => {
|
process.on('exit', () => {
|
||||||
unregisterGlobalShortcuts()
|
unregisterGlobalShortcuts()
|
||||||
closeSingleInstanceServer()
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -54,8 +54,8 @@ const DashboardWindowButtons = () => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{minimizeButton}
|
|
||||||
{maximizeButton}
|
{maximizeButton}
|
||||||
|
{minimizeButton}
|
||||||
{closeButton}
|
{closeButton}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,111 +0,0 @@
|
|||||||
const PROTOCOL_PREFIX = 'farmcontrol://'
|
|
||||||
const SINGLE_INSTANCE_HOST = '127.0.0.1'
|
|
||||||
|
|
||||||
// Stable port derived from the app identifier so multiple apps don't collide.
|
|
||||||
function getSingleInstancePort() {
|
|
||||||
const identifier = 'com.tombutcher.farmcontrol'
|
|
||||||
let hash = 0
|
|
||||||
|
|
||||||
for (const char of identifier) {
|
|
||||||
hash = (hash * 31 + char.charCodeAt(0)) | 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return 49152 + (Math.abs(hash) % 16383)
|
|
||||||
}
|
|
||||||
|
|
||||||
function findProtocolUrl(args) {
|
|
||||||
return args.find(
|
|
||||||
(arg) => typeof arg === 'string' && arg.startsWith(PROTOCOL_PREFIX)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildSecondInstanceMessage() {
|
|
||||||
const url = findProtocolUrl(process.argv)
|
|
||||||
return url ? { type: 'deeplink', url } : { type: 'focus' }
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseIncomingMessage(data) {
|
|
||||||
const text = data.toString().trim()
|
|
||||||
if (!text) return null
|
|
||||||
|
|
||||||
try {
|
|
||||||
return JSON.parse(text)
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function forwardToPrimaryInstance(message) {
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
Bun.connect({
|
|
||||||
hostname: SINGLE_INSTANCE_HOST,
|
|
||||||
port: getSingleInstancePort(),
|
|
||||||
socket: {
|
|
||||||
open(socket) {
|
|
||||||
socket.write(`${JSON.stringify(message)}\n`)
|
|
||||||
socket.end()
|
|
||||||
resolve()
|
|
||||||
},
|
|
||||||
data() {},
|
|
||||||
error(_socket, error) {
|
|
||||||
reject(error)
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(reject)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let server = null
|
|
||||||
|
|
||||||
function startPrimaryInstanceServer({ onDeepLink, onFocus }) {
|
|
||||||
server = Bun.listen({
|
|
||||||
hostname: SINGLE_INSTANCE_HOST,
|
|
||||||
port: getSingleInstancePort(),
|
|
||||||
socket: {
|
|
||||||
data(socket, data) {
|
|
||||||
const message = parseIncomingMessage(data)
|
|
||||||
socket.end()
|
|
||||||
|
|
||||||
if (!message) return
|
|
||||||
|
|
||||||
if (message.type === 'deeplink' && message.url) {
|
|
||||||
onDeepLink?.(message.url)
|
|
||||||
} else if (message.type === 'focus') {
|
|
||||||
onFocus?.()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ensureSingleInstanceLock({ onDeepLink, onFocus }) {
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
startPrimaryInstanceServer({ onDeepLink, onFocus })
|
|
||||||
return true
|
|
||||||
} catch (error) {
|
|
||||||
if (error?.code !== 'EADDRINUSE') {
|
|
||||||
console.warn('Single instance lock failed:', error)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await forwardToPrimaryInstance(buildSecondInstanceMessage())
|
|
||||||
} catch (forwardError) {
|
|
||||||
console.warn('Failed to forward to running instance:', forwardError)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function closeSingleInstanceServer() {
|
|
||||||
server?.stop?.()
|
|
||||||
server = null
|
|
||||||
}
|
|
||||||
@ -20,11 +20,6 @@ export function getMainWindow() {
|
|||||||
return mainWindow
|
return mainWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showMainWindow() {
|
|
||||||
mainWindow?.show?.()
|
|
||||||
mainWindow?.activate?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMainViewUrl() {
|
export async function getMainViewUrl() {
|
||||||
const channel = await Updater.localInfo.channel()
|
const channel = await Updater.localInfo.channel()
|
||||||
if (channel === 'dev' || process.env.NODE_ENV === 'development') {
|
if (channel === 'dev' || process.env.NODE_ENV === 'development') {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user