- Theme split: Theme.Dark.xaml + Theme.Light.xaml + ThemeManager - New shell: 32px header (mark + wordmark + 3 icons), 40px transport strip, conditional meeting bar, slide-over settings drawer - Removed: 72px rail, 380px permanent settings panel, 6-column footer, custom chromeless title bar buttons - Ctrl+T toggles theme; follows Windows app-mode by default - Shape doc at docs/shapes/2026-05-13-teamsiso-v2-studio-terminal.md
7.9 KiB
PRODUCT.md — TeamsISO
Register
Product. This is a tool, not a destination. The design serves the operator running a live broadcast. The UI is judged by how invisible it gets once the show is rolling.
Product purpose
TeamsISO is a per-participant NDI ISO controller for Microsoft Teams. It sits between Teams' raw NDI broadcast output and a live-production switcher (vMix, OBS, Resolve, Ross, hardware capture), and does three things:
- Routes each guest as a clean, individually-addressable, normalized NDI source (consistent framerate, resolution, aspect, audio routing — regardless of what each participant's webcam is doing).
- Orchestrates Teams itself — launch/hide Teams windows, drive in-call controls (mute, camera, share, leave, raise hand, quick-join) via UIAutomation, so the operator never has to alt-tab away from the routing table while the show is live.
(Recording — previously the second pillar — was removed in the WPF rollback on 2026-05-13. The engine plumbing is intact for a future re-introduction, but no UI surface, view-model command, REST route, or OSC route exposes it.)
External control surface (REST + WebSocket + OSC on localhost) lets a Companion / Stream Deck / TouchOSC controller drive routing remotely.
Users — the primary persona
Solo operator. One person, one Windows laptop or desk machine, often running the show alone from a hotel room, conference green room, or home studio. Picture them at 1:50am, twenty minutes before a live international broadcast, ambient room lights down, the Teams call already started, four guests joining staggered over the next ten minutes. They need to:
- See which participants are present, online, and producing NDI signal.
- Toggle each one's ISO on as they join.
- Confirm at a glance that recording is live, the disk has room, and the control surface is reachable.
- Drop a marker if the host says something quotable.
- Mute themselves without alt-tabbing.
If the UI demands more than a glance for any of those, the show suffers.
Secondary personas (informed, not designed-for)
- TD at a broadcast desk — multi-monitor, may use the OSC bridge to a hardware control surface. Can tolerate a denser layout because their eyes aren't the only thing on the surface.
- Producer monitoring — glances occasionally, mostly hands-off. Will see this app over someone's shoulder; first read matters.
- IT/AV admin — installs it once, tunes config, walks away. Needs settings to be findable, not present-at-all-times.
The design optimizes for the solo operator. Everyone else is downstream.
Brand
Wild Dragon LLC. Reference: wilddragon.net.
Palette anchors:
- Canvas: near-black (
#0A0A0A) - Primary accent: cyan (
#97EDF0) - Secondary blue: (
#9AE0FD) - Coral (error / destructive): (
#FB819C) - Earth (warning): (
#423825)
Typography:
- Sans: Inter (variable, bundled as a resource — not assumed installed).
- Mono: JetBrains Mono (also bundled).
The brand carries the surface but doesn't shout. Wild Dragon's authority is in the restraint, not the saturation.
Voice and tone
Operator-first, terse, broadcaster-native. The UI talks like a confident peer, not a Slack bot.
- "Stop all" not "Are you sure you want to stop all ISOs?"
- "Disk low — 8.3 GB" not "Heads up! Your disk space is running low."
- "Joined call · 4 guests" not "You have successfully joined a Teams meeting!"
- Numbers carry their unit, no sentence wraps them.
- Never apologetic. Never bubbly. Never "Let's get started!"
When something goes wrong, name it: "NDI receiver dropped — restarting" beats "Something went wrong, please try again."
Strategic principles
These are the design's load-bearing commitments. Any choice that contradicts one of these is wrong, even if it would otherwise be pretty.
1. One operator, one screen, one show.
The design is for someone running a live broadcast alone. Their attention budget for chrome is roughly zero. Anything that's not the participants table should fade until it's needed.
2. The participants table IS the product.
Everything else is support staff. Routing toggles, ISO state, and per-guest signal health get the real estate, the contrast, and the typographic hierarchy.
3. Progressive disclosure, not progressive density.
The current GUI's failure mode is "every feature gets its own visible button." The redesign's failure mode would be the opposite — burying important things in menus. The discipline: surface the half-dozen actions an operator needs mid-show; hide setup, presets, control-surface config, and exotic options behind purposeful entry points.
4. At-a-glance status is sacred.
Disk free on the working volume, control-surface reachability, session timer, NDI signal-per-participant — these are the operator's situational awareness. They must be readable in peripheral vision, in one place, without scanning. (Recording state was a historical fifth field; it's removed.)
5. Confident neutrality over decorative warmth.
This is a broadcast tool. It looks like one. No empty-state mascots, no illustrated onboarding cards, no celebratory toasts. Restraint is the brand.
Anti-references — what this is NOT
The "vibe-coded GUI" failure mode is the enemy. The redesign should never read as AI-generated. Concretely, this means none of:
- Generic SaaS dashboard. No "hero metric + supporting stats + gradient accent" cards. No "icon + heading + body text" card grids.
- Cards-in-a-grid template. Same-sized cards repeated endlessly is the defining LLM-design tell. If a layout would benefit from cards-in-a-grid, it benefits more from a table.
- Card-with-icon-and-text rows. The in-call control bar's current "icon + label" buttons (Mute / Camera / Share / Marker / Notes / Leave) read AI-generated. The redesign uses iconography differently.
- Zoom pastel. Soft purples, friendly mint greens, rounded everything, Inter-at-low-weight.
- Skeuomorphic broadcast hardware. No woodgrain, no chrome bezels, no fake LCD readouts, no metallic gradients. Wild Dragon's confidence is in flat surfaces with real typography.
- Tour-everything onboarding. No "Let's get started!" wizards with cute copy. The OnboardingWindow exists for first-launch config, not pageantry.
- Modal-as-first-thought. Settings, presets, help all currently live in modals; some should be drawers or inline-progressive. Modal is a last resort.
Technical constraints (informing design)
- Windows-only (Teams' NDI is Windows-only anyway).
- WPF .NET 8 is the supported frontend host. (A WinUI 3 rebuild was
attempted in May 2026; it proved fragile — XAML parser crashes on
DataTemplate, theme-glyph rendering issues — and was abandoned. The
rollback commit
1d1ce6ais the canonical baseline.) - Engine layer (.NET 8) is preserved verbatim — view-model surface is the swap boundary.
- Fonts are bundled via WPF's
pack://application:,,,/Assets/Fonts/#Interresource URI so the operator's machine doesn't have to have Inter or JetBrains Mono installed. - MSIX-signed installer is on the v1.0 path; the new shell needs to package cleanly through that pipeline.
- The external control surface (REST/WebSocket on
:9755, OSC on:9000) must not regress — its HTML control panel at/uiis a separate design surface but shares brand tokens.
What "done" looks like
The redesign is finished when:
- A first-time operator can launch TeamsISO, join a Teams meeting, and route their first ISO without reading documentation.
- A returning operator at 1:50am can find the four things they need (participant signal · ISO toggle · recording state · disk free) in under half a second of glance.
- Nothing on the surface reads as AI-generated. Show this to a working broadcast engineer and they say "someone who knows the job built this."
- The design system is documented in DESIGN.md tightly enough that a future contributor can add a new view that looks like it belongs.