diff --git a/src/TeamsISO.App.WinUI/Models/MockParticipant.cs b/src/TeamsISO.App.WinUI/Models/MockParticipant.cs
new file mode 100644
index 0000000..88c8e06
--- /dev/null
+++ b/src/TeamsISO.App.WinUI/Models/MockParticipant.cs
@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+
+namespace TeamsISO.App.WinUI.Models;
+
+///
+/// Stand-in for the real ParticipantViewModel until the view-model bindings
+/// migrate over from the WPF host. Lets the redesigned MainWindow render with
+/// representative data so the visual design can be validated independently
+/// of the engine layer. Removed in the view-model wiring commit.
+///
+public sealed class MockParticipant
+{
+ public string DisplayName { get; init; } = "";
+ public string Initials { get; init; } = "";
+ public string SourceCodec { get; init; } = "MS Teams · 1920x1080 · 30fps";
+ public string SignalState { get; init; } = "locked"; // locked | degraded | offline
+ public string OutputName { get; init; } = "";
+ public string IsoState { get; init; } = "OFF"; // LIVE | OFF | ERROR
+ public double AudioLevel { get; init; } = 0.0; // 0..1
+ public bool IsActiveSpeaker { get; init; } = false;
+
+ public static List Sample()
+ {
+ return new List
+ {
+ new()
+ {
+ DisplayName = "Maya Rodriguez", Initials = "MA",
+ SourceCodec = "MS Teams · 1920x1080 · 30fps",
+ SignalState = "locked", OutputName = "TEAMSISO_maya",
+ IsoState = "LIVE", AudioLevel = 0.82, IsActiveSpeaker = true,
+ },
+ new()
+ {
+ DisplayName = "Daniel Chen", Initials = "DC",
+ SourceCodec = "MS Teams · 1280x720 · 30fps",
+ SignalState = "locked", OutputName = "TEAMSISO_daniel",
+ IsoState = "LIVE", AudioLevel = 0.35,
+ },
+ new()
+ {
+ DisplayName = "Aicha Kone", Initials = "AK",
+ SourceCodec = "MS Teams · 1920x1080 · 30fps",
+ SignalState = "degraded", OutputName = "TEAMSISO_aicha",
+ IsoState = "OFF", AudioLevel = 0.12,
+ },
+ new()
+ {
+ DisplayName = "Sam Park", Initials = "SP",
+ SourceCodec = "MS Teams · 1920x1080 · 30fps",
+ SignalState = "locked", OutputName = "TEAMSISO_sam",
+ IsoState = "LIVE", AudioLevel = 0.48,
+ },
+ };
+ }
+}
diff --git a/src/TeamsISO.App.WinUI/Program.cs b/src/TeamsISO.App.WinUI/Program.cs
new file mode 100644
index 0000000..03d4e1c
--- /dev/null
+++ b/src/TeamsISO.App.WinUI/Program.cs
@@ -0,0 +1,63 @@
+using System;
+using Microsoft.UI.Dispatching;
+using Microsoft.UI.Xaml;
+using Microsoft.Windows.ApplicationModel.DynamicDependency;
+
+namespace TeamsISO.App.WinUI;
+
+///
+/// Custom Main for the unpackaged WinUI 3 host.
+///
+/// The default XAML-compiler-generated Main (disabled here via the
+/// DISABLE_XAML_GENERATED_MAIN compile constant) calls Application.Start
+/// directly. That's fine for MSIX-packaged WinUI 3 apps where the OS
+/// activates the package and the runtime is found via the package
+/// dependency, but for an unpackaged .exe the Windows App Runtime has to
+/// be bootstrapped explicitly before any WinUI 3 type is touched.
+///
+/// Bootstrap.TryInitialize(0x00010006) targets WindowsAppSDK 1.6 (the LTS
+/// branch we ship against). The major nibble 0x0001 is the runtime major;
+/// the minor 0x0006 is the runtime minor. If the user's machine has a
+/// compatible 1.6.x framework installed (the broadcast-tool installer
+/// will eventually ensure that as a prereq), Bootstrap connects and the
+/// rest of the WinUI 3 surface comes alive. If not, the call returns a
+/// negative HResult that we surface via Environment.Exit so the .exe
+/// dies cleanly rather than throwing an opaque "this application could
+/// not be started" dialog.
+///
+public static class Program
+{
+ /// WindowsAppSDK 1.6 major/minor packed as 0x00010006.
+ private const uint WindowsAppSdkMajorMinor = 0x00010006;
+
+ [STAThread]
+ public static int Main(string[] args)
+ {
+ if (!Bootstrap.TryInitialize(WindowsAppSdkMajorMinor, out int hr))
+ {
+ // The runtime isn't installed (or this build's bootstrap can't
+ // find a compatible package version). Surface a Win32 error code
+ // so postmortem inspection of the launch failure has more than
+ // "application could not be started" to go on.
+ return hr;
+ }
+
+ try
+ {
+ WinRT.ComWrappersSupport.InitializeComWrappers();
+ Application.Start(p =>
+ {
+ var context = new DispatcherQueueSynchronizationContext(
+ DispatcherQueue.GetForCurrentThread());
+ System.Threading.SynchronizationContext.SetSynchronizationContext(context);
+ _ = new App();
+ });
+ }
+ finally
+ {
+ Bootstrap.Shutdown();
+ }
+
+ return 0;
+ }
+}
diff --git a/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj b/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
index 03de33e..2aaff00 100644
--- a/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
+++ b/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
@@ -25,7 +25,14 @@
10.0.17763.0
TeamsISO.App.WinUI
TeamsISO
- app.manifest
+
x64;ARM64
win-x64;win-arm64
true
@@ -38,6 +45,16 @@
having to upgrade the .NET SDK on the build host.
-->
10.0.19041.38
+
+ $(DefineConstants);DISABLE_XAML_GENERATED_MAIN
enable
enable
Assets\teamsiso.ico
diff --git a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml
index 72289b5..788fc85 100644
--- a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml
+++ b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml
@@ -2,11 +2,481 @@
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:models="using:TeamsISO.App.WinUI.Models">
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs
index ee66b88..7df0918 100644
--- a/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs
+++ b/src/TeamsISO.App.WinUI/Views/MainWindow.xaml.cs
@@ -1,4 +1,9 @@
+using Microsoft.UI;
+using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
+using TeamsISO.App.WinUI.Models;
+using Windows.Graphics;
+using Windows.UI;
namespace TeamsISO.App.WinUI.Views;
@@ -7,6 +12,78 @@ public sealed partial class MainWindow : Window
public MainWindow()
{
InitializeComponent();
+
Title = "TeamsISO";
+
+ // ── Custom title bar wiring ───────────────────────────────────────
+ // ExtendsContentIntoTitleBar=true tells WindowsAppSDK to draw the
+ // window chrome over our content instead of reserving a Windows-default
+ // caption strip. SetTitleBar marks AppTitleBar as the drag region —
+ // clicks on it route to the system drag handler, everything else stays
+ // hit-testable as a normal XAML element. The system min/max/close
+ // buttons render on top of the right edge regardless; we just provide
+ // their colors so they match our palette.
+ ExtendsContentIntoTitleBar = true;
+ SetTitleBar(AppTitleBar);
+
+ AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
+ AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+ AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0x33, 0x34, 0x3A);
+ AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0x3F, 0x3F, 0x47);
+ AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF6);
+ AppWindow.TitleBar.ButtonHoverForegroundColor = Colors.White;
+
+ // ── Initial size & position ───────────────────────────────────────
+ // 1280x780 matches the WPF host's default — fits comfortably on a
+ // 14-inch laptop while giving the participants table 600+ pixels
+ // of vertical breathing room.
+ AppWindow.Resize(new SizeInt32(1280, 780));
+
+ // ── Mock data wiring (interim) ────────────────────────────────────
+ // Until ParticipantViewModel binds in the engine wiring commit, the
+ // table is populated from a static sample list so the visual design
+ // can be validated end-to-end against representative data.
+ ParticipantsRepeater.ItemsSource = MockParticipant.Sample();
+ }
+
+ ///
+ /// Cycle the active theme between Dark and Light. The actual swap target
+ /// is the window's root visual element — RequestedTheme on that element
+ /// propagates the {ThemeResource} swap across the whole tree, with no
+ /// flicker. The system title-bar buttons aren't part of the visual tree,
+ /// so their colors are recomputed inline.
+ ///
+ /// Persistence to UIPreferences.Theme happens in the view-model wiring
+ /// commit (the WinUI host doesn't yet share UIPreferences with the WPF
+ /// host; both will once the WinUI host owns the .NET startup).
+ ///
+ private void OnThemeToggleClick(object sender, RoutedEventArgs e)
+ {
+ if (Content is FrameworkElement root)
+ {
+ var next = root.ActualTheme == ElementTheme.Dark
+ ? ElementTheme.Light
+ : ElementTheme.Dark;
+ root.RequestedTheme = next;
+
+ // Title-bar system buttons read from AppWindow.TitleBar directly,
+ // not from the XAML resource dictionary, so we set them inline
+ // for both themes. Dark-mode chrome lands on near-white glyphs;
+ // light-mode chrome lands on near-black glyphs.
+ if (next == ElementTheme.Dark)
+ {
+ AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF6);
+ AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0x33, 0x34, 0x3A);
+ AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0x3F, 0x3F, 0x47);
+ ThemeToggleIcon.Glyph = ""; // sun
+ }
+ else
+ {
+ AppWindow.TitleBar.ButtonForegroundColor = Color.FromArgb(0xFF, 0x0A, 0x0A, 0x0A);
+ AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0xFF, 0xEC, 0xEE, 0xF1);
+ AppWindow.TitleBar.ButtonPressedBackgroundColor = Color.FromArgb(0xFF, 0xE0, 0xE3, 0xE7);
+ ThemeToggleIcon.Glyph = ""; // moon
+ }
+ }
}
}