2026-05-07 11:09:56 -04:00
|
|
|
|
<Project Sdk="Microsoft.NET.Sdk">
|
|
|
|
|
|
|
|
|
|
|
|
<PropertyGroup>
|
|
|
|
|
|
<OutputType>WinExe</OutputType>
|
|
|
|
|
|
<TargetFramework>net8.0-windows</TargetFramework>
|
|
|
|
|
|
<UseWPF>true</UseWPF>
|
2026-05-10 09:41:30 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
WinForms in addition to WPF for the system-tray NotifyIcon — there's no
|
|
|
|
|
|
WPF equivalent. The two frameworks coexist cleanly in .NET 8; UseWindowsForms
|
|
|
|
|
|
adds System.Windows.Forms.dll without changing the application model.
|
|
|
|
|
|
-->
|
|
|
|
|
|
<UseWindowsForms>true</UseWindowsForms>
|
2026-05-07 11:09:56 -04:00
|
|
|
|
<RootNamespace>TeamsISO.App</RootNamespace>
|
2026-05-07 11:39:46 -04:00
|
|
|
|
<AssemblyName>TeamsISO</AssemblyName>
|
|
|
|
|
|
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
|
<ApplicationIcon>Assets\teamsiso.ico</ApplicationIcon>
|
2026-05-10 09:41:30 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
Required by ParticipantViewModel.ScaleNearestNeighborBgra, which writes
|
|
|
|
|
|
directly into a WriteableBitmap's pinned BackBuffer (IntPtr) for 10×
|
|
|
|
|
|
better thumbnail update perf than going through Span<byte>.
|
|
|
|
|
|
-->
|
|
|
|
|
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
2026-05-07 11:09:56 -04:00
|
|
|
|
</PropertyGroup>
|
|
|
|
|
|
|
|
|
|
|
|
<ItemGroup>
|
|
|
|
|
|
<ProjectReference Include="..\TeamsISO.Engine\TeamsISO.Engine.csproj" />
|
|
|
|
|
|
<ProjectReference Include="..\TeamsISO.Engine.NdiInterop\TeamsISO.Engine.NdiInterop.csproj" />
|
2026-05-16 11:36:52 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
System.Management gives us Win32_Process via ManagementObjectSearcher,
|
|
|
|
|
|
used in App.xaml.cs's ShouldDeElevate() to look up the parent process
|
|
|
|
|
|
name (we re-spawn ourselves via runas /trustlevel:0x20000 when the
|
|
|
|
|
|
parent is explorer.exe AND we're elevated — that combo triggers an
|
|
|
|
|
|
NDI mDNS-isolation bug that returns zero discovered sources).
|
|
|
|
|
|
-->
|
|
|
|
|
|
<PackageReference Include="System.Management" Version="8.0.0" />
|
2026-05-07 11:09:56 -04:00
|
|
|
|
</ItemGroup>
|
|
|
|
|
|
|
2026-05-10 09:41:30 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
Grant the test assembly access to internal types — specifically the
|
|
|
|
|
|
OperatorPresetStore.PathOverride hook used to redirect file IO away from
|
|
|
|
|
|
%LOCALAPPDATA% during tests. We use AssemblyAttribute rather than
|
|
|
|
|
|
AssemblyInfo.cs so it co-locates with the project's other config.
|
|
|
|
|
|
-->
|
|
|
|
|
|
<ItemGroup>
|
|
|
|
|
|
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
|
|
|
|
|
<_Parameter1>TeamsISO.App.Tests</_Parameter1>
|
|
|
|
|
|
</AssemblyAttribute>
|
|
|
|
|
|
</ItemGroup>
|
|
|
|
|
|
|
polish(mainwindow): empty state, table widths, strings, theme tooltip
Walks the v2 polish punch list against MainWindow.
- Theme button tooltip is now "Theme (System / Dark / Light)" per the
v2 shape brief, replacing the previous "Toggle theme (Ctrl+T)".
- Participants table column widths match spec: Output 130px (was 150),
ISO pill 100px (was 110). The 24px state LED, 110px audio meter, and
52px row height already matched. The 106px Preview thumbnail column
and 32px gear-button column are intentional deviations (live thumbs
were restored at 4944de5; per-ISO override gear added at the same
time) and are now called out in the column-spec comment so a future
reader doesn't try to "fix" them.
- Empty-state placeholder finally renders when ParticipantCount == 0:
mono sentence "no ndi sources yet — open teams and start a meeting"
+ a tertiary Refresh discovery button — exactly the copy specified
by the shape brief's empty-states section. CountToVisibilityConverter
is now declared in MainWindow.Resources (it shipped as a class but
was never registered).
- OnClosing wraps WindowStateStore.Save in a try/catch so a serialization
or filesystem fault on shutdown can never block the window from
closing. Save itself already swallows its own IO errors; this is
defense-in-depth for anything that escapes.
- MessageBox copy in MainWindow.xaml.cs (Hide/show Teams, Launch Teams,
Stop Teams) moves to Properties/Strings.resx + a hand-written
Properties/Strings.Designer.cs accessor. ResourceManager reads it by
basename "TeamsISO.App.Properties.Strings"; LogicalName is set on the
EmbeddedResource so the manifest name is predictable regardless of
how MSBuild would otherwise compute it. Future-localization seam.
OnLaunchTeamsRightClick's confirmation dialog is intentional — it
guards a destructive mid-show action — and the code-behind comment
now says so; the palette also offers Stop Teams as the keyboard
surface, so the right-click affordance isn't the only one.
Build clean (0 warnings, 0 errors); 160 tests still pass (56 App +
104 Engine, Category!=ndi&requires!=ndi filter).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 19:26:23 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
Strings.resx — user-facing English MessageBox copy. Embedded as a
|
|
|
|
|
|
.NET resource so ResourceManager can resolve TeamsISO.App.Properties.Strings
|
|
|
|
|
|
by basename. Strings.Designer.cs is hand-written (see file comment).
|
|
|
|
|
|
-->
|
|
|
|
|
|
<ItemGroup>
|
|
|
|
|
|
<EmbeddedResource Update="Properties\Strings.resx">
|
|
|
|
|
|
<LogicalName>TeamsISO.App.Properties.Strings.resources</LogicalName>
|
|
|
|
|
|
</EmbeddedResource>
|
|
|
|
|
|
</ItemGroup>
|
|
|
|
|
|
|
2026-05-08 07:19:31 -04:00
|
|
|
|
<!-- Wild Dragon brand assets — embedded as resources so the published binary is self-contained. -->
|
|
|
|
|
|
<ItemGroup>
|
|
|
|
|
|
<Resource Include="Assets\dragon-mark.png" />
|
|
|
|
|
|
<Resource Include="Assets\wild-dragon-wordmark.png" />
|
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle
Six related polish items, all building on tonight's groundwork.
1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now.
2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart.
3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'.
4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets.
5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo.
6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition.
Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
|
|
|
|
<Resource Include="Assets\teamsiso.ico" />
|
2026-05-08 13:59:14 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
Inter Variable from rsms/inter v3.19 (OFL). Single .ttf covers every weight
|
|
|
|
|
|
from 100 (Thin) to 900 (Black) so we don't ship a directory of duplicates.
|
|
|
|
|
|
-->
|
|
|
|
|
|
<Resource Include="Assets\Fonts\Inter.ttf" />
|
feat(ui): empty-state, pipeline error/no-signal indicators, JetBrains Mono, tooltips
Four polish improvements aimed at production-floor usability.
1. Empty-state placeholder for the participants card. When Participants.Count == 0, the DataGrid is hidden in favor of a friendly 'Waiting for Teams' panel: faded dragon mark, headline, explainer, and a four-item checklist (Teams running? NDI broadcast on? Discovery group correct? Firewall clear?). New CountToVisibilityConverter (with optional 'empty' parameter to invert) drives both the placeholder and the DataGrid visibility from the same Participants.Count source.
2. Per-pipeline error / no-signal surfacing. IsoHealthStats grows an init-only State property populated from IsoPipeline.State. ParticipantViewModel.UpdateStats maps that to a StateLabel ('LIVE' / 'NO SIGNAL' / 'ERROR' / 'STARTING' / '—'). The ISO toggle button gains DataTriggers on StateLabel — coral-tinted '● ERROR' when the supervisor gives up, amber-tinted '● NO SIGNAL' when the slate threshold trips. Operators can see at a glance which pipelines are broken.
3. JetBrains Mono Variable v2.304 (OFL) bundled at Assets/Fonts/JetBrainsMono.ttf. Wd.Font.Mono now points at the embedded font so machine names, timecodes, and stat counters render in JetBrains Mono regardless of system fonts. Falls back to Cascadia Mono / Consolas if the resource is missing.
4. Tooltip pass over every interactive control in the settings panel (framerate / resolution / aspect / audio / discovery group / output group / hide-local checkbox / Apply button / per-row Output Name textbox / per-row ISO toggle). Operators learn affordances on hover instead of by trial and error.
Tests: 76/76 unit + 9/9 NDI integration green.
2026-05-08 19:32:19 -04:00
|
|
|
|
<!--
|
|
|
|
|
|
JetBrains Mono Variable v2.304 (OFL). Used for machine names, source IDs,
|
|
|
|
|
|
and stat counters where a fixed-width font reads better than Inter.
|
|
|
|
|
|
-->
|
|
|
|
|
|
<Resource Include="Assets\Fonts\JetBrainsMono.ttf" />
|
2026-05-08 07:19:31 -04:00
|
|
|
|
</ItemGroup>
|
|
|
|
|
|
|
2026-05-07 11:09:56 -04:00
|
|
|
|
</Project>
|