Files
WeHub Mirror 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
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

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)
}
}