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).
This commit is contained in:
Zac Gaetano 2026-05-12 23:52:35 -04:00
parent 94b0a71edc
commit cb1402ec8d
15 changed files with 573 additions and 14 deletions

View file

@ -2,13 +2,14 @@
"solution": {
"path": "TeamsISO.sln",
"projects": [
"src/TeamsISO.Engine/TeamsISO.Engine.csproj",
"src/TeamsISO.Engine.NdiInterop/TeamsISO.Engine.NdiInterop.csproj",
"src/TeamsISO.Console/TeamsISO.Console.csproj",
"src/TeamsISO.App/TeamsISO.App.csproj",
"src/tests/TeamsISO.Engine.Tests/TeamsISO.Engine.Tests.csproj",
"src/tests/TeamsISO.Engine.IntegrationTests/TeamsISO.Engine.IntegrationTests.csproj",
"src/tests/TeamsISO.App.Tests/TeamsISO.App.Tests.csproj"
"src\\TeamsISO.Engine\\TeamsISO.Engine.csproj",
"src\\TeamsISO.Engine.NdiInterop\\TeamsISO.Engine.NdiInterop.csproj",
"src\\TeamsISO.Console\\TeamsISO.Console.csproj",
"src\\TeamsISO.App\\TeamsISO.App.csproj",
"src\\TeamsISO.App.WinUI\\TeamsISO.App.WinUI.csproj",
"src\\tests\\TeamsISO.Engine.Tests\\TeamsISO.Engine.Tests.csproj",
"src\\tests\\TeamsISO.Engine.IntegrationTests\\TeamsISO.Engine.IntegrationTests.csproj",
"src\\tests\\TeamsISO.App.Tests\\TeamsISO.App.Tests.csproj"
]
}
}

View file

@ -5,21 +5,23 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{46E05E34-8A87-4986-87D3-FE0DE4E05F44}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine", "src/TeamsISO.Engine/TeamsISO.Engine.csproj", "{F0D24EAE-9225-4DC4-B3D2-6966077287A0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine", "src\TeamsISO.Engine\TeamsISO.Engine.csproj", "{F0D24EAE-9225-4DC4-B3D2-6966077287A0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.NdiInterop", "src/TeamsISO.Engine.NdiInterop/TeamsISO.Engine.NdiInterop.csproj", "{E737E54B-73DE-4F74-909C-1F0F5CF82AC6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.NdiInterop", "src\TeamsISO.Engine.NdiInterop\TeamsISO.Engine.NdiInterop.csproj", "{E737E54B-73DE-4F74-909C-1F0F5CF82AC6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DBDF4A1D-4215-42D5-B456-2CE7159DF848}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.Tests", "src/tests/TeamsISO.Engine.Tests/TeamsISO.Engine.Tests.csproj", "{F8DBD7AB-E160-4B75-88FC-BAECDD4D44E8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.Tests", "src\tests\TeamsISO.Engine.Tests\TeamsISO.Engine.Tests.csproj", "{F8DBD7AB-E160-4B75-88FC-BAECDD4D44E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.App", "src/TeamsISO.App/TeamsISO.App.csproj", "{80DCE039-3BBC-4D3F-B44B-51F324591C29}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.App", "src\TeamsISO.App\TeamsISO.App.csproj", "{80DCE039-3BBC-4D3F-B44B-51F324591C29}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.IntegrationTests", "src/tests/TeamsISO.Engine.IntegrationTests/TeamsISO.Engine.IntegrationTests.csproj", "{A85E331D-026E-4BDE-B89C-0CC4C95001CE}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Engine.IntegrationTests", "src\tests\TeamsISO.Engine.IntegrationTests\TeamsISO.Engine.IntegrationTests.csproj", "{A85E331D-026E-4BDE-B89C-0CC4C95001CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Console", "src/TeamsISO.Console/TeamsISO.Console.csproj", "{C3254998-9428-4264-A8FB-EAC9E1F9F432}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.Console", "src\TeamsISO.Console\TeamsISO.Console.csproj", "{C3254998-9428-4264-A8FB-EAC9E1F9F432}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.App.Tests", "src/tests/TeamsISO.App.Tests/TeamsISO.App.Tests.csproj", "{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.App.Tests", "src\tests\TeamsISO.App.Tests\TeamsISO.App.Tests.csproj", "{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamsISO.App.WinUI", "src\TeamsISO.App.WinUI\TeamsISO.App.WinUI.csproj", "{14928B5A-E45C-4265-A5D7-D13B5ED18F84}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -58,6 +60,10 @@ Global
{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F}.Release|Any CPU.Build.0 = Release|Any CPU
{14928B5A-E45C-4265-A5D7-D13B5ED18F84}.Debug|Any CPU.ActiveCfg = Debug|x64
{14928B5A-E45C-4265-A5D7-D13B5ED18F84}.Debug|Any CPU.Build.0 = Debug|x64
{14928B5A-E45C-4265-A5D7-D13B5ED18F84}.Release|Any CPU.ActiveCfg = Release|x64
{14928B5A-E45C-4265-A5D7-D13B5ED18F84}.Release|Any CPU.Build.0 = Release|x64
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F0D24EAE-9225-4DC4-B3D2-6966077287A0} = {46E05E34-8A87-4986-87D3-FE0DE4E05F44}
@ -68,5 +74,6 @@ Global
{A85E331D-026E-4BDE-B89C-0CC4C95001CE} = {DBDF4A1D-4215-42D5-B456-2CE7159DF848}
{C3254998-9428-4264-A8FB-EAC9E1F9F432} = {46E05E34-8A87-4986-87D3-FE0DE4E05F44}
{B5A6F1E7-3D2C-4F89-9A55-7E1B2A4C8D6F} = {DBDF4A1D-4215-42D5-B456-2CE7159DF848}
{14928B5A-E45C-4265-A5D7-D13B5ED18F84} = {46E05E34-8A87-4986-87D3-FE0DE4E05F44}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="TeamsISO.App.WinUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--
Tokens.xaml owns the color/typography/spacing primitives in
a ThemeDictionary so {ThemeResource} on the consumer side
auto-swaps when RequestedTheme flips. Controls.xaml owns
the actual Style targets (Button, TextBlock, etc.) and
references the tokens via {ThemeResource}.
-->
<ResourceDictionary Source="ms-appx:///Themes/Tokens.xaml"/>
<ResourceDictionary Source="ms-appx:///Themes/Controls.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View file

@ -0,0 +1,38 @@
using Microsoft.UI.Xaml;
using TeamsISO.App.WinUI.Views;
namespace TeamsISO.App.WinUI;
/// <summary>
/// WinUI 3 application entry. The full startup pipeline from the WPF host
/// (NDI runtime preflight, IsoController wiring, single-instance mutex, REST
/// + OSC bridge, tray icon, crash diagnostics, auto-update banner, onboarding)
/// will migrate over in subsequent commits — this initial scaffold just brings
/// up MainWindow so the redesigned shell can be developed and previewed.
///
/// The engine layer (TeamsISO.Engine) is unchanged; the WinUI 3 host's
/// responsibility is binding its view-models to the same controller surface
/// the WPF host already uses.
/// </summary>
public partial class App : Application
{
private Window? _mainWindow;
public App()
{
InitializeComponent();
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
_mainWindow = new MainWindow();
_mainWindow.Activate();
}
/// <summary>
/// Exposes the active main window so settings can swap RequestedTheme on
/// the root element without having to thread the Window reference through
/// every consumer.
/// </summary>
internal Window? MainWindow => _mainWindow;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -0,0 +1,72 @@
<Project Sdk="Microsoft.NET.Sdk">
<!--
TeamsISO WinUI 3 host. Coexists with the WPF project (src/TeamsISO.App)
during the redesign migration. Shares the engine (TeamsISO.Engine) and
the NDI interop assembly via ProjectReference. Once the WinUI 3 build is
feature-complete and tested against a real Teams meeting, the WPF
project is retired and this becomes the only shipping host.
Target framework choice: net8.0-windows10.0.19041.0 is the minimum the
Windows App SDK supports cleanly. Going higher (e.g. 22621) would lock
out Win10 1809+ operators, which is undesirable for a broadcast tool
that still has to run on hardware in working broadcast suites.
Packaging mode: WindowsPackageType=None for "unpackaged" — the .exe
drops directly into Program Files via the existing MSI rather than
going through MSIX. The Windows App Runtime install becomes a prereq
of the MSI (or bootstrapped at startup), which matches how operators
install NDI Runtime today.
-->
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<RootNamespace>TeamsISO.App.WinUI</RootNamespace>
<AssemblyName>TeamsISO</AssemblyName>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<WindowsPackageType>None</WindowsPackageType>
<EnableMsixTooling>true</EnableMsixTooling>
<!--
Pinning the Windows SDK projection package: WindowsAppSDK 1.6 requires
Microsoft.Windows.SDK.NET.Ref >= 10.0.19041.38, but the .NET 8.0.301
SDK installed here ships an older Ref. Setting this explicitly avoids
having to upgrade the .NET SDK on the build host.
-->
<WindowsSdkPackageVersion>10.0.19041.38</WindowsSdkPackageVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>Assets\teamsiso.ico</ApplicationIcon>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<!--
WindowsAppSDK 1.6 is the current LTS branch (Win10 1809-compatible at
a 10.0.17763 floor, which matches our SupportedOSPlatformVersion).
DataGrid lives in the older 7.x Community Toolkit because the 8.x line
dropped it; 7.1.2 still works on WinUI 3 / WindowsAppSDK 1.6 and is the
only currently-maintained free DataGrid for this stack.
-->
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250602001" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TeamsISO.Engine\TeamsISO.Engine.csproj" />
<ProjectReference Include="..\TeamsISO.Engine.NdiInterop\TeamsISO.Engine.NdiInterop.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\teamsiso.ico" />
<Content Include="Assets\dragon-mark.png" />
<Content Include="Assets\wild-dragon-wordmark.png" />
<Content Include="Assets\Fonts\Inter.ttf" />
<Content Include="Assets\Fonts\JetBrainsMono.ttf" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--
Control styles. Where WPF needed full ControlTemplate overrides to
achieve the Wild Dragon look, WinUI 3's built-in VisualStateManager
handles hover / pressed / focus states cleanly, so most styles here
just set surface properties (background, foreground, padding, corner
radius) and the framework handles state.
The few exceptions — rail icon button, ISO toggle pill — re-template
because their interaction model (Teams-style hover wash; status-coded
pill that preserves background on hover) doesn't fit the default
Button template.
-->
<!-- ════════════ TextBlock (typographic ramp) ════════════ -->
<Style x:Key="TextDisplay" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextDisplaySize}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
<Setter Property="LineHeight" Value="26"/>
</Style>
<Style x:Key="TextTitle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextTitleSize}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
</Style>
<Style x:Key="TextHeading" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextHeadingSize}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
</Style>
<Style x:Key="TextBody" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextBodySize}"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
</Style>
<Style x:Key="TextSubtle" TargetType="TextBlock" BasedOn="{StaticResource TextBody}">
<Setter Property="Foreground" Value="{ThemeResource FgSecondary}"/>
</Style>
<Style x:Key="TextCaption" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextCaptionSize}"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Foreground" Value="{ThemeResource FgTertiary}"/>
<Setter Property="CharacterSpacing" Value="80"/>
</Style>
<Style x:Key="TextMono" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{ThemeResource FontMono}"/>
<Setter Property="FontSize" Value="{ThemeResource TextMonoSize}"/>
<Setter Property="Foreground" Value="{ThemeResource FgSecondary}"/>
</Style>
<!-- ════════════ Button hierarchy ════════════
Primary — single per surface, the brand action. Cyan fill, near-black text.
Secondary — common operator actions. Transparent + bordered.
Tertiary — inline dismissals, low-frequency. Text-only.
Destructive — Stop / Leave / Delete. Coral border + text.
-->
<Style x:Key="ButtonPrimary" TargetType="Button">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextBodySize}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Background" Value="{ThemeResource AccentCyanSurface}"/>
<Setter Property="BorderBrush" Value="{ThemeResource AccentCyanSurface}"/>
<Setter Property="Foreground" Value="{ThemeResource FgOnAccent}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="16,8"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusM}"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<Style x:Key="ButtonSecondary" TargetType="Button">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextBodySize}"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{ThemeResource BorderStrong}"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="14,7"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusM}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<Style x:Key="ButtonTertiary" TargetType="Button">
<Setter Property="FontFamily" Value="{ThemeResource FontSans}"/>
<Setter Property="FontSize" Value="{ThemeResource TextBodySize}"/>
<Setter Property="FontWeight" Value="Medium"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="{ThemeResource FgSecondary}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="10,6"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusM}"/>
</Style>
<Style x:Key="ButtonDestructive" TargetType="Button" BasedOn="{StaticResource ButtonSecondary}">
<Setter Property="BorderBrush" Value="{ThemeResource AccentCoral}"/>
<Setter Property="Foreground" Value="{ThemeResource AccentCoral}"/>
</Style>
<!-- Title-bar caption buttons. 46x32 to match Windows 11 standard. The
close button gets a coral-red hover treatment via VSM override
(handled per-button inline since WinUI 3's default Close-button
red is the wrong red for our palette). -->
<Style x:Key="ButtonCaption" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{ThemeResource FgPrimary}"/>
<Setter Property="Width" Value="46"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<!-- Rail icon button. Vertical-square 48x48 with rounded fill on hover.
Used in the left rail; the icon (FontIcon) sits inside as Content. -->
<Style x:Key="ButtonRailIcon" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="{ThemeResource FgSecondary}"/>
<Setter Property="Width" Value="48"/>
<Setter Property="Height" Value="48"/>
<Setter Property="Margin" Value="0,4"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusM}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<!-- ════════════ Card / Pill / Status containers ════════════ -->
<Style x:Key="CardBorder" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource BgSurface}"/>
<Setter Property="BorderBrush" Value="{ThemeResource BorderSubtle}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusL}"/>
<Setter Property="Padding" Value="16"/>
</Style>
<Style x:Key="PillBorder" TargetType="Border">
<Setter Property="Background" Value="{ThemeResource BgElevated}"/>
<Setter Property="CornerRadius" Value="{ThemeResource RadiusPill}"/>
<Setter Property="Padding" Value="10,4"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
</ResourceDictionary>

View file

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--
TeamsISO design tokens — Wild Dragon brand × redesigned IA.
Color tokens live inside ResourceDictionary.ThemeDictionaries so
{ThemeResource} consumers swap automatically when RequestedTheme
flips (no app restart, no flicker). Brushes are paired per theme
because Color resolution is theme-scoped.
Spacing, radii, and typography tokens are theme-agnostic and live
at the top level.
Token naming mirrors DESIGN.md. The dark palette is the canonical
reference; the light palette inverts the value scale while
preserving brand recognition (cyan-tinted off-whites, not pure
white surfaces).
-->
<ResourceDictionary.ThemeDictionaries>
<!-- ════════════ DARK ════════════ -->
<ResourceDictionary x:Key="Default">
<!-- Surfaces -->
<Color x:Key="BgCanvasColor">#FF0A0A0A</Color>
<Color x:Key="BgRailColor">#FF080808</Color>
<Color x:Key="BgSurfaceColor">#FF141416</Color>
<Color x:Key="BgElevatedColor">#FF1C1C1F</Color>
<Color x:Key="BgHoverColor">#FF26272B</Color>
<Color x:Key="BgActiveColor">#FF33343A</Color>
<!-- Borders -->
<Color x:Key="BorderSubtleColor">#FF26272B</Color>
<Color x:Key="BorderStrongColor">#FF3A3B40</Color>
<!-- Text -->
<Color x:Key="FgPrimaryColor">#FFF4F4F6</Color>
<Color x:Key="FgSecondaryColor">#FFA3A4AA</Color>
<Color x:Key="FgTertiaryColor">#FF6B6C72</Color>
<Color x:Key="FgDisabledColor">#FF404145</Color>
<Color x:Key="FgOnAccentColor">#FF0A0A0A</Color>
<!-- Accents (context-aware) -->
<Color x:Key="AccentCyanSurfaceColor">#FF97EDF0</Color>
<Color x:Key="AccentCyanTextColor">#FF97EDF0</Color>
<Color x:Key="AccentCyanHoverColor">#FFB5F2F4</Color>
<Color x:Key="AccentCyanMutedColor">#FF1B3537</Color>
<Color x:Key="AccentCoralColor">#FFFB819C</Color>
<Color x:Key="AccentCoralBgColor">#FF3A1922</Color>
<Color x:Key="StatusLiveColor">#FF4ADE80</Color>
<Color x:Key="StatusLiveBgColor">#FF13261A</Color>
<Color x:Key="StatusWarnColor">#FFFBBF24</Color>
<Color x:Key="StatusWarnBgColor">#FF3A2E12</Color>
<!-- Brushes — one per Color, used by consumers via {ThemeResource} -->
<SolidColorBrush x:Key="BgCanvas" Color="{ThemeResource BgCanvasColor}"/>
<SolidColorBrush x:Key="BgRail" Color="{ThemeResource BgRailColor}"/>
<SolidColorBrush x:Key="BgSurface" Color="{ThemeResource BgSurfaceColor}"/>
<SolidColorBrush x:Key="BgElevated" Color="{ThemeResource BgElevatedColor}"/>
<SolidColorBrush x:Key="BgHover" Color="{ThemeResource BgHoverColor}"/>
<SolidColorBrush x:Key="BgActive" Color="{ThemeResource BgActiveColor}"/>
<SolidColorBrush x:Key="BorderSubtle" Color="{ThemeResource BorderSubtleColor}"/>
<SolidColorBrush x:Key="BorderStrong" Color="{ThemeResource BorderStrongColor}"/>
<SolidColorBrush x:Key="FgPrimary" Color="{ThemeResource FgPrimaryColor}"/>
<SolidColorBrush x:Key="FgSecondary" Color="{ThemeResource FgSecondaryColor}"/>
<SolidColorBrush x:Key="FgTertiary" Color="{ThemeResource FgTertiaryColor}"/>
<SolidColorBrush x:Key="FgDisabled" Color="{ThemeResource FgDisabledColor}"/>
<SolidColorBrush x:Key="FgOnAccent" Color="{ThemeResource FgOnAccentColor}"/>
<SolidColorBrush x:Key="AccentCyanSurface" Color="{ThemeResource AccentCyanSurfaceColor}"/>
<SolidColorBrush x:Key="AccentCyanText" Color="{ThemeResource AccentCyanTextColor}"/>
<SolidColorBrush x:Key="AccentCyanHover" Color="{ThemeResource AccentCyanHoverColor}"/>
<SolidColorBrush x:Key="AccentCyanMuted" Color="{ThemeResource AccentCyanMutedColor}"/>
<SolidColorBrush x:Key="AccentCoral" Color="{ThemeResource AccentCoralColor}"/>
<SolidColorBrush x:Key="AccentCoralBg" Color="{ThemeResource AccentCoralBgColor}"/>
<SolidColorBrush x:Key="StatusLive" Color="{ThemeResource StatusLiveColor}"/>
<SolidColorBrush x:Key="StatusLiveBg" Color="{ThemeResource StatusLiveBgColor}"/>
<SolidColorBrush x:Key="StatusWarn" Color="{ThemeResource StatusWarnColor}"/>
<SolidColorBrush x:Key="StatusWarnBg" Color="{ThemeResource StatusWarnBgColor}"/>
</ResourceDictionary>
<!-- ════════════ LIGHT ════════════ -->
<ResourceDictionary x:Key="Light">
<!-- Surfaces — cyan-tinted off-whites; not pure white -->
<Color x:Key="BgCanvasColor">#FFFAFAFB</Color>
<Color x:Key="BgRailColor">#FFF0F1F3</Color>
<Color x:Key="BgSurfaceColor">#FFFFFFFF</Color>
<Color x:Key="BgElevatedColor">#FFFFFFFF</Color>
<Color x:Key="BgHoverColor">#FFECEEF1</Color>
<Color x:Key="BgActiveColor">#FFE0E3E7</Color>
<!-- Borders -->
<Color x:Key="BorderSubtleColor">#FFE5E7EB</Color>
<Color x:Key="BorderStrongColor">#FFD1D5DA</Color>
<!-- Text -->
<Color x:Key="FgPrimaryColor">#FF0A0A0A</Color>
<Color x:Key="FgSecondaryColor">#FF4A4B50</Color>
<Color x:Key="FgTertiaryColor">#FF71747A</Color>
<Color x:Key="FgDisabledColor">#FFB3B6BC</Color>
<Color x:Key="FgOnAccentColor">#FF0A0A0A</Color>
<!-- Accents — surface fill stays bright cyan; text variant darkens for AA -->
<Color x:Key="AccentCyanSurfaceColor">#FF97EDF0</Color>
<Color x:Key="AccentCyanTextColor">#FF0E7C82</Color>
<Color x:Key="AccentCyanHoverColor">#FF0890A0</Color>
<Color x:Key="AccentCyanMutedColor">#FFE6F8F9</Color>
<Color x:Key="AccentCoralColor">#FFD43E5C</Color>
<Color x:Key="AccentCoralBgColor">#FFFDECF0</Color>
<Color x:Key="StatusLiveColor">#FF15803D</Color>
<Color x:Key="StatusLiveBgColor">#FFDCFCE7</Color>
<Color x:Key="StatusWarnColor">#FFB45309</Color>
<Color x:Key="StatusWarnBgColor">#FFFEF3C7</Color>
<SolidColorBrush x:Key="BgCanvas" Color="{ThemeResource BgCanvasColor}"/>
<SolidColorBrush x:Key="BgRail" Color="{ThemeResource BgRailColor}"/>
<SolidColorBrush x:Key="BgSurface" Color="{ThemeResource BgSurfaceColor}"/>
<SolidColorBrush x:Key="BgElevated" Color="{ThemeResource BgElevatedColor}"/>
<SolidColorBrush x:Key="BgHover" Color="{ThemeResource BgHoverColor}"/>
<SolidColorBrush x:Key="BgActive" Color="{ThemeResource BgActiveColor}"/>
<SolidColorBrush x:Key="BorderSubtle" Color="{ThemeResource BorderSubtleColor}"/>
<SolidColorBrush x:Key="BorderStrong" Color="{ThemeResource BorderStrongColor}"/>
<SolidColorBrush x:Key="FgPrimary" Color="{ThemeResource FgPrimaryColor}"/>
<SolidColorBrush x:Key="FgSecondary" Color="{ThemeResource FgSecondaryColor}"/>
<SolidColorBrush x:Key="FgTertiary" Color="{ThemeResource FgTertiaryColor}"/>
<SolidColorBrush x:Key="FgDisabled" Color="{ThemeResource FgDisabledColor}"/>
<SolidColorBrush x:Key="FgOnAccent" Color="{ThemeResource FgOnAccentColor}"/>
<SolidColorBrush x:Key="AccentCyanSurface" Color="{ThemeResource AccentCyanSurfaceColor}"/>
<SolidColorBrush x:Key="AccentCyanText" Color="{ThemeResource AccentCyanTextColor}"/>
<SolidColorBrush x:Key="AccentCyanHover" Color="{ThemeResource AccentCyanHoverColor}"/>
<SolidColorBrush x:Key="AccentCyanMuted" Color="{ThemeResource AccentCyanMutedColor}"/>
<SolidColorBrush x:Key="AccentCoral" Color="{ThemeResource AccentCoralColor}"/>
<SolidColorBrush x:Key="AccentCoralBg" Color="{ThemeResource AccentCoralBgColor}"/>
<SolidColorBrush x:Key="StatusLive" Color="{ThemeResource StatusLiveColor}"/>
<SolidColorBrush x:Key="StatusLiveBg" Color="{ThemeResource StatusLiveBgColor}"/>
<SolidColorBrush x:Key="StatusWarn" Color="{ThemeResource StatusWarnColor}"/>
<SolidColorBrush x:Key="StatusWarnBg" Color="{ThemeResource StatusWarnBgColor}"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<!-- ════════════ SPACING (8px grid) — theme-agnostic ════════════ -->
<x:Double x:Key="SpaceXS">4</x:Double>
<x:Double x:Key="SpaceS">8</x:Double>
<x:Double x:Key="SpaceM">12</x:Double>
<x:Double x:Key="SpaceL">16</x:Double>
<x:Double x:Key="SpaceXL">24</x:Double>
<x:Double x:Key="SpaceXXL">32</x:Double>
<x:Double x:Key="SpaceXXXL">48</x:Double>
<!-- ════════════ RADII ════════════ -->
<CornerRadius x:Key="RadiusS">6</CornerRadius>
<CornerRadius x:Key="RadiusM">8</CornerRadius>
<CornerRadius x:Key="RadiusL">12</CornerRadius>
<CornerRadius x:Key="RadiusPill">999</CornerRadius>
<!-- ════════════ TYPOGRAPHY ════════════ -->
<!--
WinUI 3 font URIs use ms-appx, not WPF's pack://. The "#Inter" suffix
is the font's family name as declared in the .ttf's name table —
without it, the font loads as a generic font and falls back to the
system default.
-->
<FontFamily x:Key="FontSans">ms-appx:///Assets/Fonts/Inter.ttf#Inter</FontFamily>
<FontFamily x:Key="FontMono">ms-appx:///Assets/Fonts/JetBrainsMono.ttf#JetBrains Mono</FontFamily>
<x:Double x:Key="TextDisplaySize">22</x:Double>
<x:Double x:Key="TextTitleSize">18</x:Double>
<x:Double x:Key="TextHeadingSize">14</x:Double>
<x:Double x:Key="TextBodySize">13</x:Double>
<x:Double x:Key="TextCaptionSize">11</x:Double>
<x:Double x:Key="TextMonoSize">12</x:Double>
</ResourceDictionary>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="TeamsISO.App.WinUI.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="{ThemeResource BgCanvas}">
<TextBlock Style="{StaticResource TextTitle}"
Text="TeamsISO — WinUI 3 host scaffold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Window>

View file

@ -0,0 +1,12 @@
using Microsoft.UI.Xaml;
namespace TeamsISO.App.WinUI.Views;
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Title = "TeamsISO";
}
}

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="TeamsISO.App.WinUI"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- TeamsISO is a normal-trust desktop app; no UAC elevation needed.
Network listens (control surface :9755 and OSC :9000) bind to
127.0.0.1 only, which doesn't require admin. -->
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Win10 1809 (17763) is the floor — same as TargetPlatformMinVersion. -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<!-- Crisp text on high-DPI broadcast monitors. -->
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling>
</windowsSettings>
</application>
</assembly>