6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
64 lines
2.8 KiB
TypeScript
64 lines
2.8 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* Reports which renderer-owned panel currently owns the user's interaction
|
|
* context, so the desktop shell can route global menu accelerators — a Cmd-W
|
|
* typed into a shell should close that shell, not the window.
|
|
*
|
|
* Tracking belongs to the PANEL, not to the widgets inside it. A panel that
|
|
* renders N children (terminal tabs) must not let each child report on its own
|
|
* unmount: the shell holds a single flag per panel, so one child tearing down
|
|
* would erase a sibling's live claim, and the accelerator would fall through to
|
|
* closing the window.
|
|
*
|
|
* The claim follows real interaction, and only that: a document-level
|
|
* `pointerdown` or `focusin` landing inside the panel. Appearing on screen is
|
|
* NOT a claim — the agent opens these panels on the user's behalf, and a panel
|
|
* that claimed on appearance would answer a Cmd-W aimed at the chat by closing
|
|
* something the user never touched.
|
|
*
|
|
* This covers only what the shell cannot see for itself — renderer-owned
|
|
* chrome. Content rendered in a native view above the renderer (the browser's
|
|
* page) emits no DOM events here, and needs none: the shell reads that focus
|
|
* directly from the view's own `webContents`.
|
|
*/
|
|
export function trackPanelFocus(
|
|
panel: HTMLElement,
|
|
reportFocus: (focused: boolean) => void
|
|
): () => void {
|
|
// Deduped: these listeners see every pointerdown and focus move in the whole
|
|
// app, and each report is an IPC hop whose handler re-registers listeners on
|
|
// the main thread. Only transitions are worth sending.
|
|
let reported: boolean | null = null
|
|
const updateFocusOwner = (target: EventTarget | null) => {
|
|
const owns = target instanceof Node && panel.contains(target)
|
|
if (owns === reported) return
|
|
reported = owns
|
|
reportFocus(owns)
|
|
}
|
|
const handlePointerDown = (event: PointerEvent) => updateFocusOwner(event.target)
|
|
const handleFocusIn = (event: FocusEvent) => updateFocusOwner(event.target)
|
|
const handleWindowBlur = () => {
|
|
if (reported !== true) {
|
|
reported = false
|
|
return
|
|
}
|
|
reported = false
|
|
reportFocus(false)
|
|
}
|
|
const handleWindowFocus = () => updateFocusOwner(document.activeElement)
|
|
document.addEventListener('pointerdown', handlePointerDown, true)
|
|
document.addEventListener('focusin', handleFocusIn, true)
|
|
window.addEventListener('blur', handleWindowBlur)
|
|
window.addEventListener('focus', handleWindowFocus)
|
|
return () => {
|
|
document.removeEventListener('pointerdown', handlePointerDown, true)
|
|
document.removeEventListener('focusin', handleFocusIn, true)
|
|
window.removeEventListener('blur', handleWindowBlur)
|
|
window.removeEventListener('focus', handleWindowFocus)
|
|
// Unconditional: the panel is going away, so the shell must not be left
|
|
// holding a claim for it even if nothing inside it was ever focused.
|
|
reportFocus(false)
|
|
}
|
|
}
|