From 99e0ebb7c3c19e0417a4a26639ac19a6478d0e20 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 20 May 2026 18:43:14 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20update=20TODO=5FAGENTS=20=E2=80=94=20cu?= =?UTF-8?q?rrent=20display=5Fcontrol=20status=20+=20future=20ideas=20secti?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- documentation/TODO_AGENTS.md | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/documentation/TODO_AGENTS.md b/documentation/TODO_AGENTS.md index 86d6f62..667bae2 100644 --- a/documentation/TODO_AGENTS.md +++ b/documentation/TODO_AGENTS.md @@ -82,27 +82,35 @@ **Current state (2026-05-20):** -- ✅ Correct `mirror_of_display:` syntax used in displayplacer fallback (was `mirror:` — wrong, now fixed) -- ✅ Failures logged to Electron console (`[Launcher] set_display_layout:`) instead of silently swallowed -- ✅ **Display Mode toggle** added to Launcher config (Native OS section) — Extend/Mirror buttons always visible, no Technical Mode required -- ⏳ `display_control` binary not yet built — must be compiled on a Mac and committed +- ✅ Correct `mirror_of_display:` syntax used in displayplacer fallback +- ✅ Failures logged to Electron console (`[Launcher] set_display_layout:`) +- ✅ Display Mode toggle in Launcher config (Native OS section) — always visible +- ✅ `display_control` binary built (universal x86_64 + arm64), committed to repo +- ✅ **Idempotency** — `mirror` and `extend` both no-op with a clean message if already in the requested state (no display flicker) +- ✅ **`list-modes`** — JSON array of all online displays + every usable `CGDisplayMode` (width, height, refresh, pixel size, HiDPI flag, is_current) +- ✅ **`set-mode`** — sets resolution/refresh via `CGConfigureDisplayWithDisplayMode`; supports `--refresh`, `--hidpi`, `--no-hidpi`; auto-prefers HiDPI on built-in, non-HiDPI on externals +- ✅ IPC handlers `native:list-display-modes` + `native:set-display-mode` wired through full bridge stack (system_handlers → preload → types → electron_relay) +- ✅ Remote build script (`scripts/remote-build-display-control.sh`) — compiles on laptop-01 via SSH from Linux workstation; uses `ssh cat` pipe pattern (avoids scp space-in-username bug) -**To build `display_control` (do this on a Mac):** +**To rebuild `display_control` after source changes:** ```bash -# One-time: install Xcode Command Line Tools if not already installed -xcode-select --install +# From repo root on workstation (laptop-01 must be reachable): +./scripts/remote-build-display-control.sh -# Then: +# Or directly on a Mac: ./scripts/build-display-control.sh -# Test it with a second display connected: +# Test with a second display connected: ./resources/bin/display_control status ./resources/bin/display_control extend ./resources/bin/display_control mirror +./resources/bin/display_control list-modes +./resources/bin/display_control set-mode 0 1920 1080 +./resources/bin/display_control set-mode 1 1920 1080 --refresh 60 --no-hidpi -# Commit the binary: +# Commit: git add resources/bin/display_control -git commit -m "build: add display_control binary (macOS CoreGraphics)" +git commit -m "build: update display_control binary (universal)" ``` **Optional per-device override (displayplacer format, for edge cases):** @@ -114,3 +122,27 @@ For rooms where auto-detection produces the wrong result, store the raw configSt } ``` `configStr` is passed from the Svelte call site and uses the displayplacer fallback path directly. + +--- + +## Future Ideas + +Capabilities worth adding as the Launcher matures. Roughly ordered by venue-day impact. + +### 1. Display reconfiguration events (push IPC) +`CGDisplayRegisterReconfigurationCallback` fires when a display is connected or removed. Wrapping this in a `webContents.send('native:display-changed', payload)` push event would let the Svelte UI auto-mirror the moment a projector cable lands — eliminating the most common operator action during show setup. Currently the UI must poll `status` or the operator presses Mirror manually. + +### 2. Audio output routing +When mirroring to a projector the audio output should follow. CoreAudio (`AudioObjectSetPropertyData` on `kAudioHardwarePropertyDefaultOutputDevice`) can switch the default output device programmatically. Candidate bridge method: `set_audio_output({device_name?, prefer_hdmi?})`. Could be called automatically as part of the mirror flow, or exposed as a standalone control. + +### 3. Battery / power status in telemetry +`get_device_info` returns CPU and RAM but nothing about power. On venue MacBook Airs this matters operationally. IOKit (`IOPSCopyPowerSourcesInfo` / `IOPSGetPowerSourceDescription`) can surface: charge %, is-charging, time-remaining, health. Low-cost addition to the existing telemetry handler. + +### 4. Presentation state feedback +`control_presentation` is fire-and-forget. AppleScript can query the current slide index and total slide count from both PowerPoint (`current slide index of active presentation`) and Keynote (`slide number of current slide of front document`). A `get_presentation_state()` bridge method returning `{ app, slide, total, presenting }` would let the Launcher UI show "Slide 7 of 42" — useful for operators monitoring multiple rooms. + +### 5. Push event channel (IPC renderer notifications) +All bridge calls are currently request-response. Adding a `webContents.send` channel for unsolicited Electron → renderer events would unlock: display plug/unplug (#1 above), file download progress, network state changes, "presentation ended" detection. A thin `ipcMain.on('native:subscribe', ...)` registration pattern on the Electron side and a corresponding `ipcRenderer.on` listener in the preload would cover all use cases without breaking the existing handler structure. + +### 6. Kiosk / accidental-quit hardening +A speaker or operator can accidentally Cmd+Q the launcher mid-presentation. `app.on('before-quit')` with either a confirmation dialog or an API-controlled lock flag (`event_device.data_json.kiosk_locked: true`) would prevent this. Can be toggled remotely — lock before the show, unlock after.