Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
import { BrowserWindow, screen } from 'electron'
|
||||
|
||||
import { attachRendererConsoleCapture } from './renderer-log'
|
||||
import {
|
||||
normalizeWakeIndicatorState,
|
||||
selectWakeIndicatorDisplay,
|
||||
WAKE_INDICATOR_FADE_MS,
|
||||
type WakeIndicatorState,
|
||||
wakeIndicatorWindowBounds
|
||||
} from './wake-indicator'
|
||||
import { installWindowRendererLifecycle } from './window-renderer-lifecycle'
|
||||
|
||||
interface WakeIndicatorWindowOptions {
|
||||
devServer?: string
|
||||
isMac: boolean
|
||||
loadWindowUrl: (window: BrowserWindow, url: string, label: string) => void
|
||||
log: (message: string) => void
|
||||
preloadPath: string
|
||||
rendererIndex: () => string
|
||||
wireWindow: (window: BrowserWindow) => void
|
||||
}
|
||||
|
||||
export function createWakeIndicatorWindowController({
|
||||
devServer,
|
||||
isMac,
|
||||
loadWindowUrl,
|
||||
log,
|
||||
preloadPath,
|
||||
rendererIndex,
|
||||
wireWindow
|
||||
}: WakeIndicatorWindowOptions) {
|
||||
let hideTimer: NodeJS.Timeout | null = null
|
||||
let state: WakeIndicatorState = 'hidden'
|
||||
let window: BrowserWindow | null = null
|
||||
|
||||
const url = () => {
|
||||
if (devServer) {
|
||||
return `${devServer.endsWith('/') ? devServer.slice(0, -1) : devServer}/?win=wake#/`
|
||||
}
|
||||
|
||||
return `${pathToFileURL(rendererIndex()).toString()}?win=wake#/`
|
||||
}
|
||||
|
||||
const selectedDisplay = () => selectWakeIndicatorDisplay(screen.getAllDisplays(), screen.getPrimaryDisplay())
|
||||
|
||||
const reposition = () => {
|
||||
if (!window || window.isDestroyed()) {
|
||||
return
|
||||
}
|
||||
|
||||
window.setBounds(wakeIndicatorWindowBounds(selectedDisplay()))
|
||||
}
|
||||
|
||||
const sendState = () => {
|
||||
if (!window || window.isDestroyed()) {
|
||||
return
|
||||
}
|
||||
|
||||
window.webContents.send('hermes:wake-indicator:state', state)
|
||||
}
|
||||
|
||||
const spawn = () => {
|
||||
const next = new BrowserWindow({
|
||||
...wakeIndicatorWindowBounds(selectedDisplay()),
|
||||
alwaysOnTop: true,
|
||||
backgroundColor: '#00000000',
|
||||
focusable: false,
|
||||
frame: false,
|
||||
fullscreenable: false,
|
||||
hasShadow: false,
|
||||
hiddenInMissionControl: true,
|
||||
maximizable: false,
|
||||
minimizable: false,
|
||||
movable: false,
|
||||
resizable: false,
|
||||
show: false,
|
||||
skipTaskbar: false,
|
||||
transparent: true,
|
||||
type: 'panel',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
contextIsolation: true,
|
||||
devTools: true,
|
||||
nodeIntegration: false,
|
||||
preload: preloadPath,
|
||||
sandbox: true
|
||||
}
|
||||
})
|
||||
|
||||
next.setAlwaysOnTop(true, 'floating')
|
||||
next.setHiddenInMissionControl?.(true)
|
||||
next.setIgnoreMouseEvents(true, { forward: true })
|
||||
|
||||
try {
|
||||
next.setVisibleOnAllWorkspaces(true, {
|
||||
skipTransformProcessType: true,
|
||||
visibleOnFullScreen: true
|
||||
})
|
||||
} catch {
|
||||
// Best effort on older Electron/macOS combinations.
|
||||
}
|
||||
|
||||
wireWindow(next)
|
||||
|
||||
// Log-only renderer lifecycle (#81290): the wake cue is ambient and
|
||||
// macOS-only; its loss belongs in desktop.log, never resurrected.
|
||||
installWindowRendererLifecycle(next, { kind: 'wake', callbacks: { log } })
|
||||
// Console errors go through the shared capture (renderer-log.ts owns
|
||||
// console-message; the lifecycle helper deliberately does not).
|
||||
attachRendererConsoleCapture(next, 'wake', log)
|
||||
|
||||
next.webContents.on('did-finish-load', sendState)
|
||||
next.once('ready-to-show', () => {
|
||||
if (!next.isDestroyed() && state !== 'hidden') {
|
||||
next.showInactive()
|
||||
}
|
||||
})
|
||||
next.on('closed', () => {
|
||||
if (window === next) {
|
||||
window = null
|
||||
}
|
||||
})
|
||||
|
||||
loadWindowUrl(next, url(), 'Wake indicator')
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
const setState = (value: unknown) => {
|
||||
if (!isMac) {
|
||||
return
|
||||
}
|
||||
|
||||
state = normalizeWakeIndicatorState(value)
|
||||
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer)
|
||||
hideTimer = null
|
||||
}
|
||||
|
||||
if (state === 'hidden') {
|
||||
sendState()
|
||||
hideTimer = setTimeout(() => {
|
||||
hideTimer = null
|
||||
|
||||
if (state === 'hidden' && window && !window.isDestroyed()) {
|
||||
window.hide()
|
||||
}
|
||||
}, WAKE_INDICATOR_FADE_MS)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (!window || window.isDestroyed()) {
|
||||
window = spawn()
|
||||
} else {
|
||||
reposition()
|
||||
sendState()
|
||||
window.showInactive()
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
if (hideTimer) {
|
||||
clearTimeout(hideTimer)
|
||||
hideTimer = null
|
||||
}
|
||||
|
||||
if (window && !window.isDestroyed()) {
|
||||
window.close()
|
||||
}
|
||||
|
||||
window = null
|
||||
state = 'hidden'
|
||||
}
|
||||
|
||||
return {
|
||||
close,
|
||||
getState: () => state,
|
||||
reposition,
|
||||
setState
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user