teamsiso/docs/shapes/2026-05-13-teamsiso-v2-studio-terminal.md
Zac Gaetano c27130302f
Some checks failed
CI / build-and-test (push) Failing after 31s
feat(wpf): v2 'Studio Terminal' shell - theme system, header, transport strip, drawer
- 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
2026-05-14 12:46:24 -04:00

12 KiB
Raw Blame History

TeamsISO v2 — Studio Terminal (approved shape brief)

Date approved: 2026-05-13 Approver: Zac (operator + product owner) Host: WPF .NET 8 (src/TeamsISO.App/). WinUI 3 rebuild is abandoned. Predecessor: the WPF rollback at 1d1ce6a (recording axed, settings pane tab fix, settings button wired).

Why this redesign

The v1 GUI failed the "AI made that" test. Quote from the operator: "its cluttered, screams that AI made it - and relatively inefficient to navigate." The PRODUCT.md anti-references — card-grid-of-icons, always-visible side panel, footer-as-theatre — all describe the current build. v2 commits to a different aesthetic register entirely.

Aesthetic register

Broadcast-engineering instrument. Not a SaaS dashboard. Not Material. Not Fluent default.

Reference proximity: Linear's keyboard-first density × Avid S6 console legibility × Blackmagic ATEM's information hierarchy. The operator mental model is "I'm sitting at an audio mixer; every region has a job, no region is theatre."

What goes away

  • The 72px left rail (no actual navigation — there's only one screen)
  • The 380px always-visible settings pane (settings change rarely, shouldn't claim permanent real estate)
  • The 6-column footer status row (theatre, not information)
  • The custom chromeless title-bar caption buttons (look worse than system chrome, break on DPI scaling)
  • The "by Wild Dragon" pill and the always-visible "TeamsISO" wordmark as decorative chrome
  • The in-call control bar as a permanent strip (only relevant in-call; should appear conditionally)
  • The seven identical ghost buttons in the in-call bar (textbook card-grid anti-pattern)

What replaces it

┌─ system Windows title bar [_ □ ✕] ─────────────────────┐
│ 🐉 TeamsISO                          [⌘K]  [☾]  [⚙]  │   32px header — mark + wordmark left, 3 icons right
├────────────────────────────────────────────────────────┤
│ ● 02:14:32   PART 4 · LIVE 2   DISK 482g  CTRL :9755  │   transport strip — single mono line, replaces footer
├────────────────────────────────────────────────────────┤
│                                                        │
│   ▮ alice          ▮▮▮▮  t:5ms   alice         [LIVE] │
│   ▯ bob            ▮▮    t:8ms   bob           [— OFF]│   participants table = the canvas
│   ▮ carlos         ▮▮▮▮▮ t:9ms   carlos        [LIVE] │   (cyan-tinted row bg = active speaker)
│   ▮ guest 4        --    NO SIG  guest_4      [ERROR]│
│                                                        │
├────────────────────────────────────────────────────────┤
│ IN CALL · Daily standup         [mute] [cam] [leave]  │   conditional — only renders when in call
└────────────────────────────────────────────────────────┘

Header (32px)

Left: Wild Dragon mark (~20px) + "TeamsISO" wordmark in Inter 13 Medium. Click on mark opens About. Right: three icon buttons.

  • ⌘K (Tabler ti-command) — opens command palette (also Ctrl+K, Ctrl+P shortcut)
  • / (Tabler ti-moon / ti-sun) — cycles theme dark ↔ light. Tooltip "Theme (System / Dark / Light)" — long-press could open the tri-state, but for v2 just a one-click cycle.
  • (Tabler ti-settings) — opens settings drawer

That's all the chrome. No nav rail because there's nothing to navigate to.

Transport strip

Single horizontal line. Mono type (JetBrains Mono 12). Replaces the entire footer.

Fields:

  • ● 02:14:32 — green dot + session timer when at least one ISO is live; both hidden otherwise
  • PART 4 · LIVE 2 — participant count and live-ISO count; "PART" / "LIVE" in Inter 11 SemiBold UPPER tracking 0.06em, numbers in mono
  • DISK 482g — free disk space on the working volume; coral text if <10GB, hidden if no relevant volume is configured
  • CTRL :9755 — control surface bind; cyan text when active, hidden when off

No icons. No badges. No backgrounds. Just typed status — a console heads-up display.

Participants table — the canvas

Five columns:

# Width Content Type
1 24px State LED — 8×8 filled cyan/coral or hollow neutral hard-edged square, no rounding
2 * Name (Inter 13/Medium) + codec/latency caption (Mono 11/Regular, tertiary fg) "Alice Wong" / "NDIV5 · t:5ms"
3 110px Audio meter — 5 vertical bars, instantaneous level hard-edged, cyan when LIVE, neutral when OFF
4 130px Output name Mono 12
5 100px ISO toggle pill LIVE = cyan fill / OFF = hollow neutral / ERROR = coral outline

Row height: 52px (was 56).

Active speaker: full-row background tint bg.active-speaker (cyan-tinted muted neutral). NOT a left-edge stripe — that trips the impeccable "side-stripe border" ban.

Each row reacts to:

  • Click anywhere → focuses the row, keyboard-actions apply
  • Click the pill → toggle ISO
  • Right-click → context menu (preview, custom name, copy NDI source name, save snapshot)
  • Hover → reveals a kebab affordance in column 5 right edge for less-frequent actions

Conditional meeting bar

Renders below the table only when TeamsControlBridge.DetectCallState().IsInCall == true. Slides up from below on transition (~120ms ease-out-quart on RenderTransform.Y + Opacity).

Content: IN CALL label (Inter 11 SemiBold UPPER, cyan accent) + meeting title (Mono 12, truncated with ellipsis) + three buttons right-aligned (Mute / Cam / Leave). Share and Notes do NOT live here — they move to ⌘K, where they're invocable any time without the bar fighting for attention.

Width matches the table — not full-bleed; respects the page padding.

Ctrl+K command palette

The redesign's navigation move. Replaces ~80% of what's in the v1 rail + tabbed settings.

Behavior:

  • Ctrl+K (also Ctrl+P) opens a centered floating window over the main shell, 560×360px
  • Search input at the top, results list below
  • Empty input → frequent + recent commands
  • Typing → fuzzy-matches across command label + category + keywords
  • ↑/↓ navigates, Enter invokes, Esc closes

Command categories (each command has icon, label, optional value preview, optional shortcut hint):

  • Quick — Enable all online, Stop all ISOs, Refresh discovery, Drop snapshot of all
  • Teams — Launch Teams, Hide / show Teams windows, Mute, Toggle camera, Open share, Leave call
  • Presets — Apply … (one row per saved preset), Save current as preset, Manage presets
  • Output — Framerate 24 / 30 / 60, Resolution 1080p / 720p, Aspect Pillarbox / Letterbox / Stretch
  • Network — Apply transcoder topology, Restore default NDI groups, Edit output name template
  • App — Theme dark / light / system, Open settings, About TeamsISO, Help (F1)

This is the keyboard-first surface broadcasters with Stream Decks already mentally use.

Settings — slide-over drawer

Triggered from the header gear icon, or from Open settings in the palette, or hotkey , (comma).

  • 420px wide, slides in from the right
  • 40% canvas scrim behind
  • Three tabs: OUTPUT (framerate / resolution / aspect / audio + Reset to defaults), NETWORK (discovery / output groups + Apply transcoder topology + Restore defaults + output name template), APP (theme tri-state, minimize to tray, sort order, Launch Teams on startup, Auto-hide Teams windows)
  • Apply Changes button pinned to drawer footer; Esc dismisses; click outside the drawer dismisses

DISPLAY tab from v1 gets renamed APP and absorbs the theme tri-state.

Empty states

  • No participants yet: a single centered mono sentence, "no ndi sources yet — open teams and start a meeting", and one tertiary button "Refresh discovery (Ctrl+R)". No illustration, no mascot.
  • Not in a call: meeting bar simply doesn't render. No placeholder.
  • Discovery degraded: amber dot in transport strip's session timer position, mono text "NDI discovery — restarting". No banner.

Color, theme, motion

Color strategy: Restrained (impeccable product default). Cyan accent earns its place — reserved for LIVE state, focus ring, active speaker tint. Coral reserved for destructive + error. Status amber for warnings. Green NOT used (would compete with cyan for "ok / live" semantics).

Theme default: Follow Windows. Theme persists per-operator via UIPreferences.Theme. Implementation: split WildDragonTheme.xaml into a single style + token-shape file plus two color-only ResourceDictionary files (Theme.Dark.xaml, Theme.Light.xaml). At runtime ThemeManager swaps the merged dictionary entry. WPF analog of WinUI's ThemeDictionary.

Motion:

  • 120ms cubic-bezier(0.16, 1, 0.3, 1) (ease-out-quart) on the meeting bar slide-in/out
  • 200ms ease-out on the drawer slide
  • 180ms cross-fade on theme swap
  • 90ms on focus + hover transitions
  • No bounce, no elastic, no spring overshoots. Animate RenderTransform and Opacity only — never layout properties.

Typography commitments

Token Family Size Weight Used for
text.timer JetBrains Mono 14 Medium Session timer in transport strip — instrument-grade
text.caption Inter 11 SemiBold (600) UPPER + tracking 0.06em — transport-strip labels, "IN CALL", "SPEAKING"
text.display Inter 22 SemiBold Settings drawer headings only
text.title Inter 13 Medium Wordmark, table column headers
text.body Inter 13 Regular Participant display names
text.mono.code JetBrains Mono 12 Regular Output names, NDI IDs, meeting title
text.mono.tech JetBrains Mono 11 Regular Latency readouts, codec captions, transport-strip values

What this is NOT

  • Not Fluent-styled. Default Fluent accent integration is generic Windows; TeamsISO is a broadcaster's tool.
  • Not minimalism for its own sake. The participants table is dense. Density is the broadcaster's virtue.
  • Not chromeless. Default system title bar stays. Chromeless windows break embarrassingly at 4K + DPI scaling.
  • Not vanity-branded. The Wild Dragon mark sits small in the header as a quality cue, never as decoration.

Migration path

The view-model surface in src/TeamsISO.App/ViewModels/ is the contract. The redesign rewrites MainWindow.xaml and Themes/* but leaves view-models, the engine, the control surface server, and the OSC bridge untouched.

Order of operations (each step builds clean before the next):

  1. Theme split — Refactor WildDragonTheme.xamlThemes/Theme.Tokens.xaml (styles + key shape) + Themes/Theme.Dark.xaml + Themes/Theme.Light.xaml (color resources only). Port ThemeManager from the deleted WinUI project; wire system app-mode detection via registry (HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme).
  2. Main window shell — Replace MainWindow.xaml's outer Grid. Add 32px header, transport strip, full-width content area, conditional meeting bar. Delete the 72px rail, the 380px right pane, the footer.
  3. Participants table redesign — 5 columns, LED state, instantaneous audio meter, ISO pill.
  4. Settings drawer — Slide-over from right, dismissable; reuses existing settings view-model.
  5. Command paletteCtrl+K floating window with fuzzy command list.

Each step is a self-contained commit so the v1 build remains shippable at any rollback point.

Anti-references — explicit on the "AI made that" failure

These are the failure modes the redesign defends against:

  • Card-grid-of-icons (the v1 in-call bar's seven identical ghost buttons)
  • Always-visible side panel (the v1 380px settings sidebar)
  • Decorative chrome (the v1 "by Wild Dragon" pill, the 72px nav rail, the six-column footer)
  • Generic Inter at 13 for everything
  • Default WPF DataGrid (Excel)
  • Custom chromeless title bars that look generic
  • Gradient text, glassmorphism, side-stripe borders (impeccable absolute bans)
  • "Hero metric + supporting stats + gradient" SaaS dashboards
  • Mascots, "Welcome!" copy, illustrated onboarding cards