farmcontrol-ui/src/desktop/macos-window-effects.js
Tom Butcher a8339aa9f8
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
Enhance macOS window effects validation and build process
- Introduced functions to validate the macOS dynamic library, ensuring it meets size and architecture requirements.
- Updated build scripts to resolve target architecture dynamically and validate the generated dylib.
- Enhanced the macOS window effects implementation to include traffic light positioning and improved error handling.
- Refactored the dylib path resolution logic to support multiple potential locations for the library.
2026-08-02 13:05:28 +01:00

149 lines
3.9 KiB
JavaScript

import { dlopen, FFIType } from 'bun:ffi'
import { existsSync, statSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
export const MAC_WINDOW_CORNER_RADIUS = 15
export const MAC_TRAFFIC_LIGHT_OFFSET = { x: 14, y: 12 }
const DYLIB_NAME = 'libMacWindowEffects.dylib'
const MIN_DYLIB_BYTES = 10_000
function resolveDylibPath() {
const candidates = new Set()
const moduleDir = path.dirname(fileURLToPath(import.meta.url))
candidates.add(path.join(moduleDir, DYLIB_NAME))
candidates.add(path.join(moduleDir, '../bun', DYLIB_NAME))
candidates.add(path.join(moduleDir, '../../bun', DYLIB_NAME))
candidates.add(path.join(process.cwd(), 'src/bun', DYLIB_NAME))
for (const execPath of [process.execPath, process.argv0].filter(Boolean)) {
const execDir = path.dirname(execPath)
candidates.add(
path.join(execDir, '../Resources/app/bun', DYLIB_NAME)
)
candidates.add(path.join(execDir, 'Resources/app/bun', DYLIB_NAME))
candidates.add(path.join(execDir, DYLIB_NAME))
}
for (const candidate of candidates) {
const resolved = path.resolve(candidate)
if (!existsSync(resolved)) {
continue
}
try {
if (statSync(resolved).size >= MIN_DYLIB_BYTES) {
return resolved
}
} catch {
// Ignore unreadable paths and keep searching.
}
}
return null
}
function loadMacWindowEffectsLibrary() {
const dylibPath = resolveDylibPath()
if (!dylibPath) {
console.warn(
'macOS vibrancy: native effects library not found; using transparent window only.'
)
return null
}
try {
return {
path: dylibPath,
lib: dlopen(dylibPath, {
enableWindowVibrancy: {
args: [FFIType.ptr, FFIType.f64],
returns: FFIType.bool
},
ensureWindowShadow: {
args: [FFIType.ptr],
returns: FFIType.bool
},
setWindowCornerRadius: {
args: [FFIType.ptr, FFIType.f64],
returns: FFIType.bool
},
setWindowTrafficLightsPosition: {
args: [FFIType.ptr, FFIType.f64, FFIType.f64],
returns: FFIType.bool
},
setNativeWindowDragRegion: {
args: [FFIType.ptr, FFIType.f64, FFIType.f64],
returns: FFIType.bool
}
})
}
} catch (error) {
console.warn(
`macOS vibrancy: failed to load native effects library (${dylibPath}):`,
error
)
return null
}
}
function applyTrafficLightsPosition(lib, mainWindow, attempt = 0) {
const applied = lib.symbols.setWindowTrafficLightsPosition(
mainWindow.ptr,
MAC_TRAFFIC_LIGHT_OFFSET.x,
MAC_TRAFFIC_LIGHT_OFFSET.y
)
if (applied || attempt >= 10) {
return applied
}
setTimeout(() => applyTrafficLightsPosition(lib, mainWindow, attempt + 1), 50)
return false
}
export function applyMacOSWindowEffects(mainWindow) {
if (process.platform !== 'darwin' || !mainWindow?.ptr) {
return
}
const loaded = loadMacWindowEffectsLibrary()
if (!loaded) {
return
}
const { path: dylibPath, lib } = loaded
try {
const vibrancyEnabled = lib.symbols.enableWindowVibrancy(
mainWindow.ptr,
MAC_WINDOW_CORNER_RADIUS
)
const shadowEnabled = lib.symbols.ensureWindowShadow(mainWindow.ptr)
const trafficLightsScheduled = applyTrafficLightsPosition(lib, mainWindow)
mainWindow.on?.('resize', () => {
lib.symbols.setWindowCornerRadius(
mainWindow.ptr,
MAC_WINDOW_CORNER_RADIUS
)
lib.symbols.setWindowTrafficLightsPosition(
mainWindow.ptr,
MAC_TRAFFIC_LIGHT_OFFSET.x,
MAC_TRAFFIC_LIGHT_OFFSET.y
)
})
console.log(
`macOS vibrancy applied (dylib=${dylibPath}, vibrancy=${vibrancyEnabled}, shadow=${shadowEnabled}, trafficLights=${trafficLightsScheduled}, cornerRadius=${MAC_WINDOW_CORNER_RADIUS})`
)
} catch (error) {
console.warn(
'macOS vibrancy: failed to apply native window effects:',
error
)
}
}