Commit graph

10 commits

Author SHA1 Message Date
7ac56c2661 feat(winui3): wire Teams orchestration into the in-call bar + rail buttons
In-call control bar now drives the live Teams app via UIAutomation:

  * Mute button → TeamsControlBridge.ToggleMute()
  * Camera button → TeamsControlBridge.ToggleCamera()
  * Share button → TeamsControlBridge.OpenShareTray()
  * Leave button → TeamsControlBridge.LeaveCall()

Each button reports the result through the status bar (Invoked /
Teams-not-running / Control-not-visible / Invoke-failed).

Rail buttons also wired:

  * Launch / surface Teams → TeamsLauncher.IsRunning()/TryLaunch()/ShowWindows()
  * Hide / show Teams windows → TeamsLauncher.HideWindows()/ShowWindows()
    with a _teamsHidden flag tracking the toggle state

The Marker button was already command-bound to MainViewModel.DropRecording
MarkerCommand (which fans out to IIsoController.AddRecordingMarker), so
the only thing that wasn't covered before is the Teams-side stuff.

Implementation notes:

  * Services/TeamsControlBridge.cs and Services/TeamsLauncher.cs are
    copied verbatim from src/TeamsISO.App/Services/ with only the
    namespace adjusted (TeamsISO.App.Services → TeamsISO.App.WinUI.
    Services). Neither file has WPF-specific dependencies — they use
    System.Windows.Automation (UIAutomationClient) which works
    identically across WPF and WinUI 3 builds. Duplication is
    acceptable migration debt; the long-term plan is to lift these
    into a shared TeamsISO.App.Shared library once both hosts
    stabilize.
  * DescribeBridgeResult maps the InvokeResult enum to operator-tone
    status text so a failing mute reads "Mute failed — control not
    visible (not in a call?)" instead of an opaque "ControlNotFound".

The in-call bar now does what the WPF host's in-call bar does, minus
the MUTED / CAM OFF state pills (those would need a 1Hz UIA poll of
the Teams call state — wire-up to come).
2026-05-13 21:31:04 -04:00
83c954d80d feat(winui3): engine wired — discovers Teams participants live
Some checks failed
CI / build-and-test (push) Failing after 27s
The WinUI 3 host now stands up the full engine pipeline on launch and
discovers participants from the operator's live Teams meeting. Verified
end-to-end against a real call: window opened, NDI runtime preflight
passed, IsoController spun up, and Participants observable yielded
three live entries ((Local), Active Speaker, Brendon Power) with their
TEAMSISO_<name> output names in the redesigned shell.

What this commit lands:

ViewModels/ (slim ports of the WPF host's view-models — engine layer
shared verbatim via ProjectReference):

  * ObservableObject.cs — INPC base, mirrors the WPF version
  * RelayCommand.cs — sync + typed + async variants; ICommand is the
    same shared type across both hosts (System.ObjectModel.dll)
  * ParticipantViewModel.cs — DisplayName / Initials / SourceCodec /
    IsoStateLabel / DisplayedAudioLevel / IsActiveSpeaker / IsOnline /
    OutputName + ToggleIsoCommand. Drops the WPF-specific thumbnail
    WriteableBitmap path, clipboard, PreviewWindow, snapshot encoder —
    those come back when the WinUI imaging pipeline is wired (Phase 5
    of the migration plan).
  * MainViewModel.cs — subscribes to IIsoController.Participants on a
    DispatcherQueueSynchronizationContext, owns the ObservableCollection,
    runs a 1Hz DispatcherQueueTimer for stats + active-speaker
    highlight + session-elapsed text. Commands: EnableAllOnline,
    StopAllIsos, RefreshDiscovery, DropRecordingMarker, ToggleByIndex.

App.xaml.cs:

  * OnLaunched brings up MainWindow first, then fires WireEngineAsync
    so the user sees the shell immediately while NDI preflight + engine
    setup proceed.
  * Full pipeline: EngineLogging → NdiInteropPInvoke (with friendly
    fallback message if the NDI runtime isn't installed) → ConfigStore
    at %APPDATA%\TeamsISO\config.json → NdiRuntimeProbe + scaler →
    IsoPipeline factory → IsoController → MainViewModel →
    MainWindow.AttachViewModel → IsoController.StartAsync.
  * Logger writes to the same %LOCALAPPDATA%\TeamsISO\Logs as the WPF
    host so a mixed-host operator sees a single timeline.

MainWindow.xaml + .xaml.cs:

  * x:Name on the section header buttons (RefreshButton, StopAllButton,
    EnableAllButton, MarkerButton) and on the status bar text + the
    ParticipantsHost grid.
  * AttachViewModel wires those buttons to view-model commands; pushes
    StatusText + ParticipantCountText through PropertyChanged.
  * BuildSimpleRow imperatively constructs each row (Grid with name +
    codec + output + ISO toggle pill) instead of going through a
    DataTemplate. Rationale: declaring a DataTemplate in
    Grid.Resources OR loading one via XamlReader.Load both crash WinUI
    3's XAML parser at runtime on this build host (same HR=0x802b000a
    we saw with the SettingsDrawer NavigationView). Imperative
    construction sidesteps the parser. The rich row template (avatar
    circle, audio meter, active-speaker accent) returns in Phase 5
    alongside the CommunityToolkit DataGrid swap.
  * Per-row PropertyChanged subscriptions refresh DisplayName, codec,
    output name, and ISO pill text as the engine pushes updates.

Verified live (PID 4824, 2026-05-13 08:02): three real Teams
participants appeared in the redesigned shell within seconds of launch,
status bar populated with "3 participants · 0 routing", section
header showed "Participants 3", and the title-bar live pills rendered
in their proper places (placeholder values for now; binding to
SessionElapsed / IsRecording lands in the next commit).
2026-05-13 08:03:32 -04:00
a05c0a75d2 feat(winui3): SettingsDrawer hosts successfully — NavigationView swap
Some checks failed
CI / build-and-test (push) Failing after 29s
Replaces the NavigationView in SettingsDrawer with a simpler
StackPanel of tab buttons built imperatively at runtime. The
NavigationView's resource-dictionary expansion (or its default
template loading) was crashing the XAML parser at SettingsDrawer's
InitializeComponent on WinUI 3 1.8.

New shape:

- `TabStrip` StackPanel populated in BuildTabStrip() with five
  Tertiary-styled Button instances. Selection updates the foreground
  to AccentCyanText for the active tab and FgSecondary for the rest.
- `TabContent` ScrollViewer remains; RebuildTabContent(key) clears
  and rebuilds via the same helpers as before (SettingHeader,
  SettingRow, SettingNote, AccentSwatch).
- Each tab's content moved into its own helper method
  (BuildAppearanceTab / Routing / Display / Control / Advanced) so
  the switch in the old OnTabSelectionChanged disappears.

MainWindow re-hosts the drawer at Grid.Row=0, RowSpan=4, right-
aligned, 400px wide, Visibility=Collapsed. OnSettingsClick toggles
visibility. Verified: dotnet build + run launches cleanly, and the
window stays alive (PID confirmed via Get-Process).

This closes Phase 6 (secondary windows) for the drawer specifically.
The Help, About, and Onboarding dialogs are ContentDialogs that
don't host inline so they should be straightforward to wire to
their respective triggers (F1 / About button / first launch) in
Phase 7.
2026-05-13 00:50:54 -04:00
27f47401d9 build(winui3): keep SettingsDrawer host deferred + narrow the suspect
Some checks failed
CI / build-and-test (push) Failing after 28s
Tried re-hosting SettingsDrawer with `Visibility="Collapsed"` (no
RenderTransform / Storyboard this time). Still crashes the XAML parser
at startup with the same HR 0x802b000a.

Narrows the suspect: the crash is inside SettingsDrawer.xaml's
InitializeComponent, not in MainWindow.xaml's hosting of it. Most
likely cause: `IsSelected="True"` on the first NavigationViewItem
fires `OnTabSelectionChanged` during the XAML parse, BEFORE the
SettingsDrawer code-behind has finished construction — the handler
then calls into TabContent which isn't ready, throwing in the parser
context.

Two fixes to try next session:

1. Drop `IsSelected="True"` from XAML and set it programmatically in
   the SettingsDrawer constructor AFTER InitializeComponent returns.
2. Verify the OnTabSelectionChanged signature for WinUI 3 1.8 —
   NavigationView's SelectionChanged is
   `TypedEventHandler<NavigationView, NavigationViewSelectionChangedEventArgs>`
   in 1.8 (might be different from the 1.6 SDK signature I wrote
   against).

For now, the MainWindow's OnSettingsClick is a no-op stub. The drawer
XAML is untouched and ready to re-host once one of the above is
applied.

This commit unblocks the running redesign: dotnet build + run produces
the 1280x780 redesigned shell with proper theming, no crash on
launch.
2026-05-13 00:48:03 -04:00
a33f80d345 feat(winui3): WinUI 3 host LAUNCHES — verified rendering on Windows
Some checks failed
CI / build-and-test (push) Failing after 26s
Removing the inline-hosted SettingsDrawer (and its accompanying
Storyboard resources targeting TranslateTransform.X) unblocks the
launch. The WinUI 3 host now opens, paints, and stays alive. Verified
via screenshot:

  * 64px left rail with Wild Dragon "W" brand mark + participants /
    Teams / hide-Teams / settings / engine-status puck buttons (Segoe
    Fluent Icons throughout, uniform stroke)
  * 44px custom title bar with the live pills inline (live · session
    timer · REC count · disk free) and a theme toggle to the left of
    the system min/max/close
  * Section header: "Participants 4" + filter input + Refresh +
    Presets + the single cyan Primary CTA "Enable all online"
  * Participants list placeholder ("View-model wiring queued for the
    next session") in the hero row — real DataGrid + bindings land in
    Phase 4/5 of the migration plan
  * Conditional in-call control bar: Muted (destructive coral) +
    Camera/Share/Marker (Secondary) + Leave (destructive coral) +
    overflow kebab
  * Slim status bar: control-surface URL + keyboard shortcut hints
  * Rendered in LIGHT THEME on first run (matched the OS app-mode
    setting via ThemeManager.ResolveTheme), confirming the
    ThemeDictionary swap works end-to-end

Two open suspects causing the SettingsDrawer host to crash WinUI 3's
XAML parser with HR=0x802b000a (XAML_E_PARSER_GENERAL_ERROR):

  * RenderTransform with a x:Name'd TranslateTransform — WinUI 3
    might not allow naming transforms inside RenderTransform the way
    WPF does
  * Storyboard.TargetName pointing at the named transform — WinUI 3
    Storyboards have stricter resolution

The drawer XAML itself (Views/SettingsDrawer.xaml + .cs) is unchanged
and ships alongside this commit. Re-host it in MainWindow.xaml once
the parse error is triaged (likely fix: replace TranslateTransform.X
animation with the AppWindow composition API or use the
CompositionTarget approach instead of a Storyboard).

The migration plan's Phase 3 is now substantially CLOSED — the
WindowsAppSDK activation blocker is resolved (1.8 DDLM swap). Next
session opens with Phase 4 (view-model wiring) plus the SettingsDrawer
re-host triage.
2026-05-13 00:41:49 -04:00
166e7d6e6a build(winui3): switch to WindowsAppSDK 1.8 + add diagnostic probe
Some checks failed
CI / build-and-test (push) Has been cancelled
Two big findings from a custom MddBootstrapInitialize2 P/Invoke probe
this session:

1. The original WinUI 3 activation failure ("this application could not
   be started") was MDD_E_BOOTSTRAP_INITIALIZE_DDLM_NOT_FOUND (HR
   0x80670016). The framework package Microsoft.WindowsAppRuntime.1.6
   was installed, but the Dynamic Dependency Lifetime Manager sibling
   (MicrosoftCorporationII.WinAppRuntime.Main.1.6) wasn't. This machine
   has Main.1.5 and Main.1.8 packages but no Main.1.6, so bootstrap for
   1.6 fails.

2. Switching the WindowsAppSDK NuGet to 1.8.250916003 + the bootstrap
   major.minor to 0x00010008 in Program.cs gets past activation. The
   .exe now launches and Bootstrap.TryInitialize returns S_OK. The 1.8
   DDLM is present and the runtime spins up.

Also lands `src/TeamsISO.App.WinUI.Probe/`, a tiny console diagnostic
that calls MddBootstrapInitialize2 directly via P/Invoke (bypassing the
full WindowsAppSDK NuGet to avoid the MRT/PRI MSBuild tasks that need
VS's AppxPackage tooling installed). The probe prints the HResult and a
human-readable description; use it to triage WindowsAppSDK activation
on any deployment target:

  dotnet run --project src/TeamsISO.App.WinUI.Probe

A SECOND ISSUE surfaces after activation: the .exe crashes 1 second
after launch with 0xC000027B inside Microsoft.UI.Xaml.dll, sub-code
0x802b000a (XAML_E_PARSER_GENERAL_ERROR). The participants ItemsRepeater
with {Binding ...} markup is suspect (WinUI 3 prefers x:Bind with
x:DataType, and Visibility="{Binding bool}" needs a converter). The
ItemsRepeater is stubbed out to a plain "Participants list renders here"
TextBlock placeholder for now; same crash recurs, so the XAML issue is
elsewhere — likely in Controls.xaml (one of CharacterSpacing /
TextCaption / etc. unsupported), in App.xaml's MergedDictionary chain,
or in MainWindow.xaml's Storyboard target.

Triaging the XAML parse error is the next session's first action. The
sub-code 0x802b000a will help (search WindowsAppSDK source for the
matching XAML parser error). The migration plan in
docs/superpowers/plans/2026-05-12-winui3-migration.md is updated.

Build remains clean.
2026-05-13 00:39:43 -04:00
2909d8b1d7 feat(winui3): wire Settings drawer slide-in animation into MainWindow
Hosts SettingsDrawer in the main content grid as a fixed-width 400px
panel positioned at the right edge, with a TranslateTransform pre-set
to X=400 so it starts off-screen. The rail's settings icon triggers a
220ms ease-out-quart slide-in storyboard; CloseRequested (or the
drawer's Esc/close button) triggers a 180ms ease-in slide-out.
IsHitTestVisible toggles in sync so the off-screen drawer doesn't
intercept clicks on the participants list.

This is the structural commitment from DESIGN.md: settings live in a
right-drawer (not a permanent 380px panel), the participants table
reclaims full width when settings aren't being edited.

Builds clean.

Followup: tab content currently rebuilds imperatively in code-behind;
wire it to a SettingsViewModel mirror of the WPF host's
GlobalSettingsViewModel once the view-model migration starts (Phase 4
of the migration plan).
2026-05-13 00:20:23 -04:00
48ca16bc5e feat(winui3): ThemeManager service + Settings drawer + Help/About/Onboarding
Builds out the secondary surfaces of the redesigned WinUI 3 host.

ThemeManager (Services/ThemeManager.cs)
  Single-source-of-truth for the active theme. Holds the user preference
  (System / Dark / Light), resolves it to ElementTheme at request, and
  raises a Themed event when it changes so the MainWindow can push the
  AppWindow title-bar button colors. Uses Windows.UI.ViewManagement
  UISettings to follow the OS app-mode when preference is System.
  Persistence to UIPreferences lands in the engine-wiring commit.

MainWindow theme wiring
  Replaces the per-handler theme toggle with a ThemeManager subscription:
  click the title-bar sun/moon -> Toggle() -> Themed event ->
  ApplyResolvedTheme on the visual tree + the title-bar buttons. Glyph
  cue: sun = "current is Light, click to Dark"; moon = "current is Dark,
  click to Light." Initial state applied at construction so the first
  frame matches the preference.

SettingsDrawer (Views/SettingsDrawer.xaml + .cs)
  UserControl that slides in from the right over the participants table.
  56px header, NavigationView with five tabs (Appearance, Routing,
  Display, Control, Advanced), footer with Reset-to-defaults +
  Apply/Close. Appearance tab has the theme tri-state picker (System /
  Dark / Light radio group) and an "Accent peek" row showing the four
  brand accents (cyan / coral / live / warn) as swatches so the
  operator can verify Wild Dragon brand is respected on a light desk.
  CloseRequested event signals the host to collapse the drawer.

HelpDialog (Views/HelpDialog.xaml + .cs)
  ContentDialog with the keyboard shortcut cheat sheet, grouped by
  category (Global / Participants / Look / Control surface). 540px max
  height with scroll, mono-spaced shortcut labels at left, body text at
  right. Replaces the WPF host's HelpWindow at parity.

AboutDialog (Views/AboutDialog.xaml + .cs)
  ContentDialog with the Wild Dragon mark, version + host + engine +
  brand info as label/value rows, and three quick action buttons
  (open logs folder, open recordings, check for updates). Mirrors the
  WPF host's AboutWindow.

OnboardingDialog (Views/OnboardingDialog.xaml + .cs)
  Three numbered steps (Install NDI Runtime / Enable Teams NDI / Pick
  transcoder topology), no carousel, operator-tone copy ("Don't show
  this again" defaults checked). PrimaryButtonText "Get started",
  SecondaryButtonText "Skip" so the dialog is skippable from the first
  frame as the PRODUCT.md anti-references demand.

Build clean: dotnet build TeamsISO.App.WinUI -c Debug -> 0 / 0.

Next: wire the drawer's CloseRequested into MainWindow (so the settings
icon actually opens / collapses the drawer), then attack the runtime
activation blocker (Phase 3 of the migration plan).
2026-05-13 00:13:58 -04:00
9e176d8f10 feat(winui3): redesigned MainWindow + custom title bar + theme toggle
Lands the approved shape brief as the WinUI 3 MainWindow:

* 64px left rail with brand mark, primary nav (participants), Teams
  launch / hide / settings buttons, and the engine-status puck at the
  bottom. All five rail buttons use Segoe Fluent Icons glyphs at a
  uniform 20px optical size; no more bespoke <Path Data> shapes with
  inconsistent stroke weights.

* 44px custom title bar via ExtendsContentIntoTitleBar +
  SetTitleBar(AppTitleBar). The drag region absorbs the three live-state
  pills inline (session timer 'live * 00:14:32', REC count + elapsed,
  disk free) and a slim sun/moon theme-toggle button to the left of the
  system Min/Max/Close controls. System buttons inherit ButtonForeground
  Color etc. from AppWindow.TitleBar so they match palette in both
  themes.

* Section header with 'Participants * count' display, filter input,
  Refresh + Presets (Secondary buttons), and 'Enable all online' as
  the single cyan Primary button - finally a real button hierarchy
  instead of seven indistinguishable ghost buttons.

* Participants list rendered as ItemsRepeater + DataTemplate for now;
  the CommunityToolkit DataGrid migration follows in a separate commit.
  Row template at 64px height with: 3px cyan left border for active
  speaker, avatar with initials in cyan-muted circle, name + codec line,
  signal lock state with dot, audio meter via ProgressBar, output name
  in JetBrains Mono, ISO state pill (LIVE/OFF/ERROR) at right.

* Conditional in-call control bar below the table: Mute / Camera /
  Share / Marker / Leave + overflow kebab. Muted state binds the
  destructive coral treatment to the Mute button; Leave is also
  destructive (coral border + text); everything else is Secondary.
  Tight 8px spacing keeps the bar dense without crowding.

* Slim 32px status bar at the bottom: control-surface URL on the left
  (cyan dot indicator), keyboard-shortcut hints on the right in
  tertiary mono. Replaces the WPF host's six-column footer.

Implementation notes:

* MockParticipant model populates the table with representative data
  (Maya / Daniel / Aicha / Sam, one as active speaker) until the
  ParticipantViewModel binding migrates over from the WPF host.

* Custom Program.cs takes ownership of Main from the XAML compiler
  (DISABLE_XAML_GENERATED_MAIN). Calls Bootstrap.TryInitialize(0x00010006)
  before Application.Start so the unpackaged .exe can locate the
  WindowsAppSDK 1.6 framework MSIX at launch. Shutdown is paired in
  a finally block.

* Theme toggle in code-behind flips Window.Content.RequestedTheme
  between Dark and Light. {ThemeResource} bindings auto-swap across
  the visual tree; system title-bar buttons (outside the XAML tree)
  get color updates inline so they stay readable in both modes.

* app.manifest deferred from build - the framework-emitted manifest
  covers DPI awareness and supportedOS GUIDs; reintroducing our own
  goes in the next commit alongside the bootstrapper hardening.

Known issue: the unpackaged .exe currently fails to activate on this
build host with 'this application could not be started' before Main
runs. Build is clean; published output runs the same way. Diagnosing
the activation failure is the next session's first task (likely the
runtimeconfig.json including Microsoft.WindowsDesktop.App which WinUI 3
doesn't want, or a missing CRT redistributable). The WPF host remains
the running build until that's resolved.

dotnet build TeamsISO.Windows.slnf -c Debug: 0 warnings, 0 errors.
2026-05-13 00:03:12 -04:00
cb1402ec8d feat(winui3): scaffold TeamsISO.App.WinUI alongside the WPF host
First step of the WinUI 3 replatform per the approved redesign brief.
The new project coexists with the existing src/TeamsISO.App (WPF) so the
WPF host keeps building and shipping while the WinUI 3 redesign lands
incrementally. Once the WinUI 3 build is feature-complete and tested
against a real Teams meeting, the WPF project is retired.

Scaffold contents:

* src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
  Windows App SDK 1.6 LTS (250602001), unpackaged mode
  (WindowsPackageType=None) so the existing MSI installer keeps working.
  Target framework net8.0-windows10.0.19041.0, min platform 10.0.17763.0
  to preserve Win10 1809+ compatibility for working broadcast hardware.
  Pins WindowsSdkPackageVersion=10.0.19041.38 so .NET SDK 8.0.301 builds
  cleanly without an SDK upgrade on the build host.

* src/TeamsISO.App.WinUI/app.manifest
  PerMonitorV2 DPI awareness + gdiScaling for crisp text on high-DPI
  broadcast monitors. asInvoker trust level (control surface :9755 and
  OSC :9000 bind to 127.0.0.1, no admin needed).

* App.xaml + App.xaml.cs
  Minimal startup: brings up MainWindow. The full pipeline (NDI runtime
  preflight, IsoController wiring, single-instance mutex, REST + OSC
  bridge, tray icon, crash diagnostics, auto-update banner, onboarding)
  migrates in subsequent commits.

* Themes/Tokens.xaml
  Wild Dragon design tokens as ThemeDictionary entries (Default = Dark,
  Light). Colors as Color resources, Brushes paired per theme so
  {ThemeResource} auto-swaps when RequestedTheme flips — no app restart,
  no flicker. Spacing/radii/typography tokens are theme-agnostic at the
  outer level. Light palette maintains brand recognition via cyan-tinted
  off-whites (#FAFAFB canvas, #F0F1F3 rail) rather than pure white, and
  splits cyan into accent.cyan.surface (#97EDF0, works in both modes
  because text on top is near-black) and accent.cyan.text (#97EDF0 dark
  / #0E7C82 light) so captions and inline labels keep AA contrast.

* Themes/Controls.xaml
  Button hierarchy with real commitments: Primary (cyan fill, one per
  surface), Secondary (transparent bordered), Tertiary (text only),
  Destructive (coral border + text), Caption (titlebar), RailIcon.
  Typographic ramp (Display / Title / Heading / Body / Subtle / Caption
  / Mono) at the DESIGN.md 1.25 ratio.

* CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2 referenced for the
  participants table migration. (Toolkit 8.x dropped DataGrid; 7.x is
  the only currently-maintained free option for WinUI 3.)

* Inter.ttf + JetBrainsMono.ttf + dragon-mark.png + teamsiso.ico copied
  from the WPF project's Assets/ so the WinUI 3 host is self-contained.

* TeamsISO.sln + TeamsISO.Windows.slnf updated to include the new
  project. The .slnf paths switch to backslash form so MSBuild can match
  them against the .sln's canonical path representation.

Verified: dotnet build TeamsISO.Windows.slnf -c Debug succeeds with 0
warnings and 0 errors for all 8 projects (WPF host, WinUI 3 host, engine,
NDI interop, console, three test projects).
2026-05-12 23:52:35 -04:00