From c08b90b0b2bac4a9e886cc55066bc3644f238323 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Fri, 8 May 2026 01:05:26 -0400 Subject: [PATCH] feat(ui): Launch Teams rail button + spec for embedded-Teams roadmap First step of Phase E.1 from the new spec at docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md: a third icon in the left rail launches the Microsoft Teams desktop client as a subprocess of TeamsISO so the operator doesn't have to leave the app to start a meeting. Services/TeamsLauncher tries the ms-teams: URI first, falls back to %LOCALAPPDATA%\\Microsoft\\WindowsApps\\ms-teams.exe (new Teams), then the classic Update.exe handoff. On failure surfaces a friendly MessageBox with the install link. The spec doc lays out the full three-phase roadmap (launcher -> window orchestration -> in-app meeting controls via Graph API or UIAutomation) and explicitly calls out what's out of scope (replacing Teams' media stack). _NEXT.md updated to mark Phase D done and queue Phase E + remaining polish items (code-signing, Inter/JetBrains Mono font bundling, real Wild Dragon dragon-mark, drops counter, running-fps display). --- docs/superpowers/plans/_NEXT.md | 35 +++++- ...2026-05-08-embedded-teams-orchestration.md | 106 ++++++++++++++++++ src/TeamsISO.App/MainWindow.xaml | 15 +++ src/TeamsISO.App/MainWindow.xaml.cs | 18 +++ src/TeamsISO.App/Services/TeamsLauncher.cs | 90 +++++++++++++++ 5 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md create mode 100644 src/TeamsISO.App/Services/TeamsLauncher.cs diff --git a/docs/superpowers/plans/_NEXT.md b/docs/superpowers/plans/_NEXT.md index f09d0fe..2b4483f 100644 --- a/docs/superpowers/plans/_NEXT.md +++ b/docs/superpowers/plans/_NEXT.md @@ -6,11 +6,38 @@ - **Phase B-1 — Pipeline Orchestration** (tag: `phase-b-1-complete`) — NdiReceiver, NdiSender, ExponentialBackoff, NdiRuntimeProbe, IsoPipeline supervisor, IsoController. - **Phase B-2 — Real NDI Interop** (tag: `phase-b-2-complete`) — `NdiInteropPInvoke` against NDI 6 SDK, managed BGRA scaler, `TeamsISO.Console` headless smoke runner, `NdiVersion` constants. - **Phase C — WPF UI** (tag: `phase-c-complete`) — MVVM helpers, ParticipantViewModel, GlobalSettingsViewModel, AlertBannerViewModel, MainViewModel, MainWindow XAML with participants DataGrid + settings sidebar + alert banner, App.xaml DI bootstrap. +- **Hardening — May 2026** (no tag) — see "Done in May 2026" below; covers the bug-fix triad that turned this from "won't even start" into "discovers real Teams participants in a real meeting", plus integration coverage and the brand/UX rebuild. +- **Phase D — WiX Installer** — WiX v5 MSI scaffold, Forgejo CI fix (artifact v3 pin), Forgejo release workflow on tag push. -## Next (Windows-only) +## Done in May 2026 -1. **First end-to-end validation on Windows** — install NDI Runtime, clone the repo, build the full solution, run the integration tests against an NDI Test Pattern source, run the WPF app and validate against a real Teams meeting. Fix any issues found. +- Fixed `.sln` path-separator mismatch that broke `.slnf` filters on Windows. +- `NdiNativeLibraryResolver` resolves `Processing.NDI.Lib.x64.dll` via `NDI_RUNTIME_DIR_V6` so the engine starts on installs where the NDI dir isn't on PATH. +- `NdiVersion.ExpectedRuntimeVersionPrefix` updated to match the shipping NDI 6 banner format (`NDI SDK WIN64 ...`). +- `NdiSourceParser` accepts current Teams desktop's `MS Teams - ` brand format (plus legacy `Teams` and defensive `Microsoft Teams`). +- `--list-sources` diagnostic on `TeamsISO.Console`. +- NDI groups end-to-end (discovery + output) so the operator can confine Teams' raw broadcasts to a private group. +- Hide-(Local) toggle so the user's own self-preview doesn't pollute the participants list. +- Single-instance enforcement via per-user named Mutex with broadcast bring-to-front. +- WPF rebuilt around Wild Dragon brand × Microsoft Teams flush layout (left rail + chromeless title bar + caption controls + cyan accent + JetBrains Mono). +- `IsoHealthStats` wired end-to-end: live receiver/sender refs published from the inner pipeline, frame counters and source resolution displayed in a `Live` column on the participants DataGrid (1 Hz polled). +- Rolling daily file logging at `%LOCALAPPDATA%\TeamsISO\Logs\` via Serilog.Sinks.File. +- Real-NDI integration test tier (`requires=ndi`): runtime probe, finder/sender lifecycle on default + custom groups, loopback discovery, and a full pipeline frame round-trip that asserts 1920×1080 normalization. +- Forgejo CI is green (`actions/upload-artifact` pinned to v3 since Forgejo doesn't support v4). +- WiX v5 MSI scaffold + Forgejo release workflow on tag push (windows-latest runner; uploads MSI as both workflow artifact and release asset). +- Code-review pass + fixes (no static `Serilog.Log.Logger` mutation, frame-dimension snapshot instead of holding a `RawFrame` ref, ComponentDispatcher unsubscribe, Mutex-ownership flag). +- "Launch Teams" rail button (Phase E.1 starter, see `docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md`). -2. **Phase D — WiX Installer & Release** (Windows) — WiX v5 MSI installer detecting NDI Runtime, release pipeline triggering on tag push, code-signing decision implemented. +## Next -3. **Optional polish before v1.0** — system health indicators (CPU/GPU/network meters), per-stream framerate display, output thumbnail previews (deferred from v1.5 if useful), MaterialDesignThemes UI polish. +1. **Phase E — Embedded Teams orchestration** — see the spec at `docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md`. Three-phase rollout: launcher → window orchestration → in-app meeting controls. Phase E.1 partially shipped (launcher). + +2. **Code-signing the MSI** — `installer/TeamsISO.Installer.wixproj` has a `SignOutput` hook but no cert. For a v1.0 release, wire `signtool` invocation from a release-only CI secret. Until then SmartScreen will warn on first launch. + +3. **Bundle Inter / JetBrains Mono fonts** so the typography doesn't depend on Windows fallbacks. Today the WPF UI uses Segoe UI Variable Display + Cascadia Mono as fallbacks. + +4. **Wild Dragon dragon-mark** — the rail logo is a stylized "W" placeholder; swap in the real dragon SVG when available. + +5. **Optional polish before v1.0** — running incoming-fps display (today the field on `IsoHealthStats` is 0), per-pipeline output thumbnail previews, MaterialDesignThemes-equivalent transitions, system health (CPU/network) meters in the rail. + +6. **Drops counter on `IsoHealthStats`** — `FrameProcessor` doesn't currently surface drops or duplicates; wire those through so the Live column can show "↓ N (dropped M)". diff --git a/docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md b/docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md new file mode 100644 index 0000000..9fa9635 --- /dev/null +++ b/docs/superpowers/specs/2026-05-08-embedded-teams-orchestration.md @@ -0,0 +1,106 @@ +# Spec: Embedded Teams meeting orchestration + +**Status:** Draft. Authored 2026-05-08. + +## Problem + +Operators currently run two apps side by side: Microsoft Teams (which broadcasts +NDI from its meetings) and TeamsISO (which consumes those NDI sources, normalizes +them, and re-emits clean ISOs). Two issues fall out: + +1. **Two interfaces, one workflow.** Switching between Teams to drive the meeting + and TeamsISO to drive ISO routing is friction during a live show. +2. **Teams' raw NDI bleeds into the production network.** Even with + TeamsISO running, Teams broadcasts its at-source-resolution / at-source-framerate + feeds on the same `Public` NDI group that switchers and recorders subscribe to. + Operators see "garbage" NDI sources alongside the clean TeamsISO outputs unless + they manually configure NDI groups (which most don't). + +The user's stated north star: **let me host the meeting from inside TeamsISO. Run +Teams in the background. Show me one interface; expose only the proper outputs.** + +## Constraints + +- Microsoft Teams' NDI broadcast feature is desktop-only — the web client does not + broadcast NDI. We cannot replace Teams with a WebView2 view of `teams.microsoft.com`. +- We do not have a native Teams SDK. The Microsoft Graph API exposes some meeting + control (create/join/end), but in-call operations (mute, share, react) are largely + out of scope or behind enterprise tenant configuration. +- Win32 window embedding (`SetParent`) of a foreign process's window is technically + possible but produces a fragile UX — Teams will break out, render incorrectly, or + fail to honor parent-window inputs. +- NDI group routing is the standard primitive for hiding noisy producers. We + shipped this in commit `909237f`. It works. + +## Architecture + +A three-phase rollout. Each phase is shippable on its own. + +### Phase E.1 — Teams launcher (launches Teams as a subprocess) + +The minimum viable embed. TeamsISO grows a "Launch Teams" affordance on the rail. +Clicking it: + +1. Reads the global `NdiGroupSettings.DiscoveryGroups` from `EngineConfig`. If + empty, defaults to `teamsiso-input`. +2. Opens **NDI Access Manager** (or programmatically writes its config) so Teams + broadcasts on `teamsiso-input` rather than `Public`. +3. Launches `ms-teams:` URI (or the `MSTeams.exe` directly for the new client) in + the background. +4. Marks Teams as "owned by TeamsISO" — the rail icon flips to "Stop Teams"; on + click, sends WM_CLOSE to the Teams main window. +5. Surfaces meeting health in the existing engine-status pill (e.g. "Teams running + • 2 participants"). + +Implementation effort: **a few hours.** Pure WPF + ProcessStartInfo + a small +NdiAccessManagerHelper that reads/writes Teams' config. + +### Phase E.2 — Window orchestration + +Teams' main window is repositioned + minimized when launched, so the user's +foreground experience is the TeamsISO window. Optional: + +- Pin Teams to a hidden virtual desktop with `IVirtualDesktopManager`. +- Forward keyboard shortcuts (mute, camera, share) from TeamsISO into Teams via + `SendInput` while Teams' window is hidden. + +Implementation effort: **a day.** Mostly Win32 plumbing. + +### Phase E.3 — Meeting controls in TeamsISO's UI + +A "Meeting" panel in the left rail that shows the active call's participant list +(with their mute / video state) and exposes Join/Leave/Mute/Share controls. Two +ways to plumb this: + +- **Microsoft Graph API for the chrome.** Auth as the user via OAuth (interactive + device-code flow), poll `/me/onlineMeetings` for active meetings, render in + TeamsISO's UI. In-call mute/cam state is not exposed via Graph as of writing — + Phase E.3 would surface participant *presence* but not mic/cam controls. +- **Teams' UI Automation tree.** Walk Teams' window with `UIAutomation` to read + call state. Brittle but usable; what other "Teams remote" tools do. + +Implementation effort: **a week per route.** Recommend Graph for read paths, +UIAutomation for write paths. + +## Out of scope (for now) + +- Hosting the actual meeting media stack (audio/video render, mixer, network). + Teams owns this and we don't want to. +- Replacing Teams entirely with our own SIP/WebRTC stack. That's a different + product. + +## Decision required + +User to confirm: + +1. Phase E.1 first (just launcher + group routing). Yes/no. +2. Whether to use `ms-teams:` URI launch or the new MSTeams.exe binary path + (`%LOCALAPPDATA%\Microsoft\WindowsApps\ms-teams.exe`). +3. Whether to ship NDI Access Manager config writes, or just document the manual + steps and trust the user to set them once. + +## Implementation log + +- 2026-05-08: First version of this spec drafted while user is asleep. +- 2026-05-08: Phase E.1 partial — "Launch Teams" rail button shipped (commit + pending). Group-routing automation deferred until user confirms approach. diff --git a/src/TeamsISO.App/MainWindow.xaml b/src/TeamsISO.App/MainWindow.xaml index ac39323..aacc10a 100644 --- a/src/TeamsISO.App/MainWindow.xaml +++ b/src/TeamsISO.App/MainWindow.xaml @@ -103,6 +103,21 @@ + + +