The custom Path gear with Stroke=Wd.Text.Secondary + StrokeThickness=1.4 rendered as a near-invisible thin grey shape against the dark row background — users couldn't tell the column was clickable. Replace with TextBlock rendering U+2699 GEAR from Segoe UI Symbol at 16px and Wd.Text.Primary foreground. Universally recognized as 'settings', renders crisply at any DPI, and stands out against the row. Header bumped from empty to 'CFG' so the affordance is discoverable, column widened from 32px to 56px so 'CFG' fits cleanly.
1032 lines
64 KiB
XML
1032 lines
64 KiB
XML
<Window x:Class="TeamsISO.App.MainWindow"
|
||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||
xmlns:vm="clr-namespace:TeamsISO.App.ViewModels"
|
||
xmlns:conv="clr-namespace:TeamsISO.App.Converters"
|
||
mc:Ignorable="d"
|
||
Title="TeamsISO"
|
||
Width="1280" Height="780"
|
||
MinWidth="980" MinHeight="640"
|
||
WindowStartupLocation="CenterScreen"
|
||
Background="{DynamicResource Wd.Canvas}"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
UseLayoutRounding="True"
|
||
SnapsToDevicePixels="True"
|
||
TextOptions.TextRenderingMode="ClearType"
|
||
TextOptions.TextFormattingMode="Display"
|
||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}">
|
||
<!--
|
||
v2 SHELL — "Studio Terminal"
|
||
(Approved 2026-05-13. Shape brief at docs/shapes/2026-05-13-teamsiso-v2-studio-terminal.md)
|
||
|
||
Default Windows title bar (no chromeless WindowChrome). The 32px header
|
||
below it carries the brand mark, wordmark, and three icon buttons:
|
||
⌘K (command palette), theme toggle, settings drawer. Below that, a
|
||
single transport strip carries the operator's at-a-glance status.
|
||
The participants area is the canvas — no rail, no permanent side
|
||
panel, no footer. The meeting bar at the bottom renders ONLY when
|
||
Teams is in a call.
|
||
-->
|
||
|
||
<Window.Resources>
|
||
<conv:BoolToVisibilityConverter x:Key="BoolToVis"
|
||
TrueValue="Visible"
|
||
FalseValue="Collapsed"/>
|
||
<conv:BoolToVisibilityConverter x:Key="BoolToVisInverse"
|
||
TrueValue="Collapsed"
|
||
FalseValue="Visible"/>
|
||
<conv:EnumDescriptionConverter x:Key="EnumDesc"/>
|
||
<conv:LevelThresholdConverter x:Key="LevelGate"/>
|
||
<conv:CountToVisibilityConverter x:Key="CountToVis"/>
|
||
</Window.Resources>
|
||
|
||
<Window.InputBindings>
|
||
<KeyBinding Key="F1" Command="{Binding ShowHelpCommand}"/>
|
||
<KeyBinding Key="S" Modifiers="Ctrl+Shift" Command="{Binding StopAllIsosCommand}"/>
|
||
<KeyBinding Key="R" Modifiers="Ctrl" Command="{Binding RefreshDiscoveryCommand}"/>
|
||
<KeyBinding Key="T" Modifiers="Ctrl" Command="{Binding ToggleThemeCommand}"/>
|
||
<!-- Ctrl+K (and Ctrl+P alias) — open the v2 command palette. The
|
||
KeyBinding fires on the window; the handler is wired via the
|
||
palette button's Click in the header. -->
|
||
<KeyBinding Key="K" Modifiers="Ctrl" Command="{Binding OpenCommandPaletteCommand}"/>
|
||
<KeyBinding Key="P" Modifiers="Ctrl" Command="{Binding OpenCommandPaletteCommand}"/>
|
||
<KeyBinding Key="NumPad1" Command="{Binding ToggleByIndexCommand}" CommandParameter="1"/>
|
||
<KeyBinding Key="NumPad2" Command="{Binding ToggleByIndexCommand}" CommandParameter="2"/>
|
||
<KeyBinding Key="NumPad3" Command="{Binding ToggleByIndexCommand}" CommandParameter="3"/>
|
||
<KeyBinding Key="NumPad4" Command="{Binding ToggleByIndexCommand}" CommandParameter="4"/>
|
||
<KeyBinding Key="NumPad5" Command="{Binding ToggleByIndexCommand}" CommandParameter="5"/>
|
||
<KeyBinding Key="NumPad6" Command="{Binding ToggleByIndexCommand}" CommandParameter="6"/>
|
||
<KeyBinding Key="NumPad7" Command="{Binding ToggleByIndexCommand}" CommandParameter="7"/>
|
||
<KeyBinding Key="NumPad8" Command="{Binding ToggleByIndexCommand}" CommandParameter="8"/>
|
||
<KeyBinding Key="NumPad9" Command="{Binding ToggleByIndexCommand}" CommandParameter="9"/>
|
||
<KeyBinding Key="D1" Command="{Binding ToggleByIndexCommand}" CommandParameter="1"/>
|
||
<KeyBinding Key="D2" Command="{Binding ToggleByIndexCommand}" CommandParameter="2"/>
|
||
<KeyBinding Key="D3" Command="{Binding ToggleByIndexCommand}" CommandParameter="3"/>
|
||
<KeyBinding Key="D4" Command="{Binding ToggleByIndexCommand}" CommandParameter="4"/>
|
||
<KeyBinding Key="D5" Command="{Binding ToggleByIndexCommand}" CommandParameter="5"/>
|
||
<KeyBinding Key="D6" Command="{Binding ToggleByIndexCommand}" CommandParameter="6"/>
|
||
<KeyBinding Key="D7" Command="{Binding ToggleByIndexCommand}" CommandParameter="7"/>
|
||
<KeyBinding Key="D8" Command="{Binding ToggleByIndexCommand}" CommandParameter="8"/>
|
||
<KeyBinding Key="D9" Command="{Binding ToggleByIndexCommand}" CommandParameter="9"/>
|
||
</Window.InputBindings>
|
||
|
||
<Grid>
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="32"/>
|
||
<RowDefinition Height="40"/>
|
||
<RowDefinition Height="*"/>
|
||
<RowDefinition Height="Auto"/>
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- ═════════════════════ HEADER (32px) ═════════════════════ -->
|
||
<Border Grid.Row="0"
|
||
Background="{DynamicResource Wd.Canvas}"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="0,0,0,1">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<!-- Brand: Wild Dragon mark + wordmark. Click on the mark opens About.
|
||
Mark is small (20px) and sits as a quality cue — not as
|
||
marketing chrome. -->
|
||
<StackPanel Grid.Column="0"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Margin="12,0,0,0">
|
||
<Button Click="OnAboutClick"
|
||
Padding="2"
|
||
Background="Transparent"
|
||
BorderThickness="0"
|
||
Cursor="Hand"
|
||
ToolTip="About TeamsISO">
|
||
<Image Source="/Assets/dragon-mark.png"
|
||
Width="20" Height="20"
|
||
RenderOptions.BitmapScalingMode="HighQuality"/>
|
||
</Button>
|
||
<TextBlock Text="TeamsISO"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="13"
|
||
FontWeight="Medium"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
VerticalAlignment="Center"
|
||
Margin="8,0,0,0"/>
|
||
</StackPanel>
|
||
|
||
<!-- Right cluster: three icon buttons. ⌘K opens the command
|
||
palette (Ctrl+K shortcut). The theme button cycles
|
||
dark ↔ light (Ctrl+T). The gear opens the settings
|
||
drawer. That's the entire chrome. -->
|
||
<StackPanel Grid.Column="2"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,10,0">
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnCommandPaletteClick"
|
||
Padding="8,4"
|
||
Margin="0,0,2,0"
|
||
ToolTip="Command palette (Ctrl+K)"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Secondary}"
|
||
Content="⌘K"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding ToggleThemeCommand}"
|
||
Padding="6,4"
|
||
Margin="0,0,2,0"
|
||
ToolTip="Theme (System / Dark / Light)">
|
||
<Path Data="M 8,2 C 8,2 4,4 4,8 C 4,12 8,14 8,14 C 5,14 2,11 2,8 C 2,5 5,2 8,2 Z"
|
||
Stroke="{DynamicResource Wd.Text.Secondary}"
|
||
StrokeThickness="1.4"
|
||
Fill="{DynamicResource Wd.Text.Secondary}"
|
||
Width="14" Height="14"
|
||
Stretch="None"/>
|
||
</Button>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnSettingsToggleClick"
|
||
Padding="6,4"
|
||
ToolTip="Settings">
|
||
<Path Data="M 8,1 L 8,3 M 8,13 L 8,15 M 1,8 L 3,8 M 13,8 L 15,8 M 3,3 L 4.5,4.5 M 11.5,11.5 L 13,13 M 3,13 L 4.5,11.5 M 11.5,4.5 L 13,3 M 8,5.5 C 9.4,5.5 10.5,6.6 10.5,8 C 10.5,9.4 9.4,10.5 8,10.5 C 6.6,10.5 5.5,9.4 5.5,8 C 5.5,6.6 6.6,5.5 8,5.5"
|
||
Stroke="{DynamicResource Wd.Text.Secondary}"
|
||
StrokeThickness="1.4"
|
||
Fill="Transparent"
|
||
Width="14" Height="14"
|
||
Stretch="None"/>
|
||
</Button>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- ═════════════════════ TRANSPORT STRIP (40px) ═════════════════════ -->
|
||
<Border Grid.Row="1"
|
||
Background="{DynamicResource Wd.Canvas}"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="0,0,0,1">
|
||
<Grid Margin="20,0,20,0" VerticalAlignment="Center">
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<StackPanel Grid.Column="0"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center">
|
||
<!-- Session timer — green dot + elapsed when at least one ISO is live -->
|
||
<StackPanel Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,20,0"
|
||
Visibility="{Binding IsSessionActive, Converter={StaticResource BoolToVis}}">
|
||
<Ellipse Width="7" Height="7"
|
||
Fill="{DynamicResource Wd.Accent.Cyan}"
|
||
VerticalAlignment="Center"/>
|
||
<TextBlock Text="{Binding SessionElapsed}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
FontWeight="Medium"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
VerticalAlignment="Center"
|
||
Margin="8,0,0,0"/>
|
||
</StackPanel>
|
||
|
||
<!-- Participant counts — labels in tracked smallcaps, values in mono.
|
||
The space between value and next label uses a middle dot. -->
|
||
<StackPanel Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,20,0">
|
||
<TextBlock Text="PART"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="10"
|
||
FontWeight="SemiBold"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,6,0"/>
|
||
<TextBlock Text="{Binding ParticipantCount, Mode=OneWay, FallbackValue=0}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
VerticalAlignment="Center"/>
|
||
<TextBlock Text=" · "
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Text.Disabled}"
|
||
VerticalAlignment="Center"/>
|
||
<TextBlock Text="LIVE"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="10"
|
||
FontWeight="SemiBold"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,6,0"/>
|
||
<TextBlock Text="{Binding LiveCount, Mode=OneWay, FallbackValue=0}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Accent.CyanText}"
|
||
VerticalAlignment="Center"/>
|
||
</StackPanel>
|
||
|
||
<!-- Teams meeting pill — only present in-call -->
|
||
<StackPanel Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,20,0"
|
||
Visibility="{Binding IsTeamsInCall, Converter={StaticResource BoolToVis}}">
|
||
<TextBlock Text="CALL"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="10"
|
||
FontWeight="SemiBold"
|
||
Foreground="{DynamicResource Wd.Accent.CyanText}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,6,0"/>
|
||
<TextBlock Text="{Binding TeamsMeetingState}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Secondary}"
|
||
VerticalAlignment="Center"
|
||
MaxWidth="240"
|
||
TextTrimming="CharacterEllipsis"/>
|
||
</StackPanel>
|
||
|
||
</StackPanel>
|
||
|
||
<!-- Control surface — only present when listening, right-aligned -->
|
||
<StackPanel Grid.Column="2"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
Visibility="{Binding IsControlSurfaceRunning, Converter={StaticResource BoolToVis}}">
|
||
<TextBlock Text="CTRL"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="10"
|
||
FontWeight="SemiBold"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,6,0"/>
|
||
<TextBlock Text="{Binding ControlSurfaceText}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Accent.CyanText}"
|
||
VerticalAlignment="Center"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- ═════════════════════ BODY ═════════════════════ -->
|
||
<Grid Grid.Row="2">
|
||
<Grid.RowDefinitions>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="Auto"/>
|
||
<RowDefinition Height="*"/>
|
||
</Grid.RowDefinitions>
|
||
|
||
<!-- Alert banner — appears when AlertBanner.IsVisible flips on -->
|
||
<Border Grid.Row="0"
|
||
Background="{DynamicResource Wd.Accent.CoralBg}"
|
||
BorderBrush="{DynamicResource Wd.Accent.Coral}"
|
||
BorderThickness="0,0,0,1"
|
||
Padding="24,10"
|
||
Visibility="{Binding AlertBanner.IsVisible, Converter={StaticResource BoolToVis}}">
|
||
<DockPanel>
|
||
<Button DockPanel.Dock="Right"
|
||
Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Dismiss"
|
||
Command="{Binding AlertBanner.DismissCommand}"
|
||
Margin="12,0,0,0"/>
|
||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||
<Ellipse Width="7" Height="7"
|
||
Fill="{DynamicResource Wd.Accent.Coral}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,10,0"/>
|
||
<TextBlock Text="{Binding AlertBanner.Message}"
|
||
Style="{StaticResource Wd.Text.Body}"
|
||
FontWeight="SemiBold"
|
||
VerticalAlignment="Center"/>
|
||
</StackPanel>
|
||
</DockPanel>
|
||
</Border>
|
||
|
||
<!-- Update banner — Forgejo release available -->
|
||
<Border Grid.Row="1"
|
||
Margin="20,12,20,0"
|
||
Padding="14,10"
|
||
Background="{DynamicResource Wd.Accent.CyanMuted}"
|
||
BorderBrush="{DynamicResource Wd.Accent.Cyan}"
|
||
BorderThickness="1"
|
||
CornerRadius="{StaticResource Radius.M}"
|
||
Visibility="{Binding UpdateBanner.IsVisible, Converter={StaticResource BoolToVis}}">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<Ellipse Grid.Column="0"
|
||
Width="7" Height="7"
|
||
Fill="{DynamicResource Wd.Accent.Cyan}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,12,0"/>
|
||
<TextBlock Grid.Column="1"
|
||
Text="{Binding UpdateBanner.Message}"
|
||
Style="{StaticResource Wd.Text.Body}"
|
||
FontSize="12"
|
||
VerticalAlignment="Center"/>
|
||
<Button Grid.Column="2"
|
||
Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Get update"
|
||
Command="{Binding UpdateBanner.OpenReleasePageCommand}"
|
||
Padding="14,4"
|
||
Margin="0,0,8,0"/>
|
||
<Button Grid.Column="3"
|
||
Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Dismiss"
|
||
Command="{Binding UpdateBanner.DismissCommand}"
|
||
Padding="14,4"/>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- Action toolbar — primary verbs above the participants table -->
|
||
<Border Grid.Row="2"
|
||
Padding="20,14,20,12">
|
||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||
<Button Style="{StaticResource Wd.Button.Primary}"
|
||
Command="{Binding EnableAllOnlineCommand}"
|
||
Content="Enable all"
|
||
Padding="14,7"
|
||
Margin="0,0,8,0"
|
||
ToolTip="Enable ISO routing for every online participant"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding RefreshDiscoveryCommand}"
|
||
Content="Refresh"
|
||
Padding="14,7"
|
||
Margin="0,0,8,0"
|
||
ToolTip="Refresh NDI discovery (Ctrl+R)"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnPresetsClick"
|
||
Content="Presets"
|
||
Padding="14,7"
|
||
Margin="0,0,8,0"
|
||
ToolTip="Open operator presets"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding StopAllIsosCommand}"
|
||
Content="Stop all"
|
||
Padding="14,7"
|
||
ToolTip="Disable every running ISO (Ctrl+Shift+S)"/>
|
||
|
||
<!-- Teams launch / hide — small icon buttons, after a separator -->
|
||
<Border Width="1"
|
||
Background="{DynamicResource Wd.Border}"
|
||
Margin="14,4,14,4"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnLaunchTeamsClick"
|
||
MouseRightButtonUp="OnLaunchTeamsRightClick"
|
||
Padding="10,7"
|
||
Margin="0,0,4,0"
|
||
ToolTip="Launch Microsoft Teams (right-click to stop)">
|
||
<Path Data="M 4,8 L 16,8 L 16,16 L 4,16 Z M 16,11 L 22,8 L 22,16 L 16,13 Z"
|
||
Stroke="{DynamicResource Wd.Text.Secondary}"
|
||
StrokeThickness="1.5"
|
||
Fill="Transparent"
|
||
StrokeLineJoin="Round"
|
||
Width="18" Height="14"
|
||
Stretch="Uniform"/>
|
||
</Button>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnToggleTeamsWindowClick"
|
||
Padding="10,7"
|
||
ToolTip="Hide / show Teams windows">
|
||
<Path Data="M 1,11 C 5,4 17,4 21,11 C 17,18 5,18 1,11 Z M 11,8 A 3,3 0 1 1 11,14 A 3,3 0 1 1 11,8 Z"
|
||
Stroke="{DynamicResource Wd.Text.Secondary}"
|
||
StrokeThickness="1.4"
|
||
Fill="Transparent"
|
||
Width="18" Height="14"
|
||
Stretch="Uniform"/>
|
||
</Button>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<!--
|
||
Participants table — v2 "Studio Terminal" layout.
|
||
|
||
Spec columns (from docs/shapes/2026-05-13-…studio-terminal.md):
|
||
1. State LED 24px — 8×8 hard-edged square. Filled cyan
|
||
when LIVE; filled coral on ERROR;
|
||
filled amber on NO SIGNAL / STARTING;
|
||
hollow neutral when OFF.
|
||
2. Name + caption * — DisplayName (Inter 13/Medium) plus
|
||
source machine + state label below
|
||
in JetBrains Mono 11/Tertiary.
|
||
3. Audio meter 110px — five vertical hard-edged bars,
|
||
each lit when DisplayedAudioLevel
|
||
crosses its threshold (0.2, 0.4,
|
||
0.6, 0.8, 1.0). No averaging.
|
||
4. Output name 130px — JetBrains Mono 12 — the NDI source
|
||
name TeamsISO broadcasts as.
|
||
5. ISO toggle pill 100px — LIVE = cyan-muted fill with cyan
|
||
text; OFF = hollow neutral; ERROR
|
||
gets the existing trigger swap.
|
||
|
||
Deliberate deviations from the spec (operator preference, see
|
||
4944de5 — "restore live thumbnail preview column"):
|
||
• A 106px live thumbnail column sits between State LED and
|
||
Name. Replaces the table's previous role as the only place
|
||
to see what the operator is broadcasting; the pop-out
|
||
preview window is the secondary view.
|
||
• A 32px ghost-button cell on the right edge of Name opens
|
||
the per-ISO override dialog (framerate / resolution /
|
||
aspect / audio). Hidden on hover-out.
|
||
|
||
Row height 52 (down from 56). Active speaker = full-row
|
||
bg.active-speaker tint set by the global DataGridRow style
|
||
(avoids the impeccable side-stripe-border ban).
|
||
-->
|
||
<Border Grid.Row="3"
|
||
Margin="20,0,20,12"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="1"
|
||
CornerRadius="{StaticResource Radius.M}"
|
||
Background="{DynamicResource Wd.Surface}">
|
||
<Grid>
|
||
<DataGrid x:Name="ParticipantsGrid"
|
||
ItemsSource="{Binding ParticipantsView}"
|
||
AutoGenerateColumns="False"
|
||
HeadersVisibility="Column"
|
||
GridLinesVisibility="None"
|
||
RowBackground="Transparent"
|
||
AlternatingRowBackground="Transparent"
|
||
BorderThickness="0"
|
||
CanUserAddRows="False"
|
||
CanUserDeleteRows="False"
|
||
CanUserResizeRows="False"
|
||
SelectionMode="Single"
|
||
SelectionUnit="FullRow"
|
||
RowHeight="52"
|
||
Visibility="{Binding ParticipantCount, Converter={StaticResource CountToVis}}">
|
||
<DataGrid.Columns>
|
||
<!-- Col 1 — State LED. 8×8 hard-edged Rectangle.
|
||
Default fill is hollow (transparent with stroke). DataTriggers
|
||
swap the fill based on StateLabel: LIVE → cyan filled,
|
||
NO SIGNAL / STARTING → amber filled, ERROR → coral filled.
|
||
No rounding — broadcast vocabulary uses sharp LEDs. -->
|
||
<DataGridTemplateColumn Header="" Width="24" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<Rectangle Width="8" Height="8"
|
||
HorizontalAlignment="Center"
|
||
VerticalAlignment="Center"
|
||
StrokeThickness="1.5">
|
||
<Rectangle.Style>
|
||
<Style TargetType="Rectangle">
|
||
<Setter Property="Fill" Value="Transparent"/>
|
||
<Setter Property="Stroke" Value="{DynamicResource Wd.BorderStrong}"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding StateLabel}" Value="LIVE">
|
||
<Setter Property="Fill" Value="{DynamicResource Wd.Accent.Cyan}"/>
|
||
<Setter Property="Stroke" Value="{DynamicResource Wd.Accent.Cyan}"/>
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding StateLabel}" Value="ERROR">
|
||
<Setter Property="Fill" Value="{DynamicResource Wd.Accent.Coral}"/>
|
||
<Setter Property="Stroke" Value="{DynamicResource Wd.Accent.Coral}"/>
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding StateLabel}" Value="NO SIGNAL">
|
||
<Setter Property="Fill" Value="{DynamicResource Wd.Status.Warn}"/>
|
||
<Setter Property="Stroke" Value="{DynamicResource Wd.Status.Warn}"/>
|
||
</DataTrigger>
|
||
<DataTrigger Binding="{Binding StateLabel}" Value="STARTING">
|
||
<Setter Property="Fill" Value="{DynamicResource Wd.Status.Warn}"/>
|
||
<Setter Property="Stroke" Value="{DynamicResource Wd.Status.Warn}"/>
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Rectangle.Style>
|
||
</Rectangle>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 2 — Live preview thumbnail. 96×54 (16:9). WriteableBitmap
|
||
fed from the engine's most recent ProcessedFrame at the 1Hz
|
||
stats tick. Em-dash placeholder when no pipeline is running. -->
|
||
<DataGridTemplateColumn Header="Preview" Width="106" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<Grid VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,8,0">
|
||
<Border Width="96" Height="54"
|
||
Background="{DynamicResource Wd.SurfaceElevated}"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="1"
|
||
CornerRadius="4"
|
||
Visibility="{Binding HasThumbnail, Converter={StaticResource BoolToVisInverse}}">
|
||
<TextBlock Text="—"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
HorizontalAlignment="Center"
|
||
VerticalAlignment="Center"/>
|
||
</Border>
|
||
<Border Width="96" Height="54"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="1"
|
||
CornerRadius="4"
|
||
ClipToBounds="True"
|
||
Visibility="{Binding HasThumbnail, Converter={StaticResource BoolToVis}}">
|
||
<Image Source="{Binding Thumbnail}"
|
||
Stretch="UniformToFill"
|
||
RenderOptions.BitmapScalingMode="LowQuality"/>
|
||
</Border>
|
||
</Grid>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 3 — Name + caption. Display name (Inter 13/Medium) above,
|
||
source machine + state caption (Mono 11/Tertiary) below. -->
|
||
<DataGridTemplateColumn Header="Participant" Width="*" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<StackPanel VerticalAlignment="Center">
|
||
<TextBlock Text="{Binding DisplayName}"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="13"
|
||
FontWeight="Medium"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
TextTrimming="CharacterEllipsis"/>
|
||
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
|
||
<TextBlock Text="{Binding SourceMachine}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
TextTrimming="CharacterEllipsis"
|
||
MaxWidth="180"/>
|
||
<TextBlock Text=" · "
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Disabled}"/>
|
||
<TextBlock Text="{Binding StateLabel}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"/>
|
||
</StackPanel>
|
||
</StackPanel>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 3 — Audio meter. 5 vertical bars; each lit when
|
||
DisplayedAudioLevel crosses its threshold. -->
|
||
<DataGridTemplateColumn Header="Audio" Width="110" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<StackPanel Orientation="Horizontal"
|
||
VerticalAlignment="Center"
|
||
HorizontalAlignment="Left">
|
||
<Rectangle Width="6" Height="6"
|
||
Margin="0,0,3,0"
|
||
Fill="{DynamicResource Wd.Accent.Cyan}"
|
||
Opacity="{Binding DisplayedAudioLevel, Converter={StaticResource LevelGate}, ConverterParameter=0.2}"
|
||
VerticalAlignment="Bottom"/>
|
||
<Rectangle Width="6" Height="10"
|
||
Margin="0,0,3,0"
|
||
Fill="{DynamicResource Wd.Accent.Cyan}"
|
||
Opacity="{Binding DisplayedAudioLevel, Converter={StaticResource LevelGate}, ConverterParameter=0.4}"
|
||
VerticalAlignment="Bottom"/>
|
||
<Rectangle Width="6" Height="14"
|
||
Margin="0,0,3,0"
|
||
Fill="{DynamicResource Wd.Accent.Cyan}"
|
||
Opacity="{Binding DisplayedAudioLevel, Converter={StaticResource LevelGate}, ConverterParameter=0.6}"
|
||
VerticalAlignment="Bottom"/>
|
||
<Rectangle Width="6" Height="18"
|
||
Margin="0,0,3,0"
|
||
Fill="{DynamicResource Wd.Status.Warn}"
|
||
Opacity="{Binding DisplayedAudioLevel, Converter={StaticResource LevelGate}, ConverterParameter=0.8}"
|
||
VerticalAlignment="Bottom"/>
|
||
<Rectangle Width="6" Height="22"
|
||
Fill="{DynamicResource Wd.Accent.Coral}"
|
||
Opacity="{Binding DisplayedAudioLevel, Converter={StaticResource LevelGate}, ConverterParameter=0.95}"
|
||
VerticalAlignment="Bottom"/>
|
||
</StackPanel>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 4 — Output name (mono). The NDI source name TeamsISO
|
||
will broadcast this participant as. -->
|
||
<DataGridTemplateColumn Header="Output" Width="130" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding OutputName}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Text.Secondary}"
|
||
VerticalAlignment="Center"
|
||
TextTrimming="CharacterEllipsis"/>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 5a — Per-row gear: opens the ISO override editor for this
|
||
participant. We use the Unicode gear glyph (U+2699) instead
|
||
of a custom Path — it renders cleanly at any size, doesn't
|
||
disappear against dark rows the way 1.4px strokes do, and
|
||
reads as "settings" at a glance. Header is "CFG" so the
|
||
affordance is discoverable even when the row hover state
|
||
isn't active. -->
|
||
<DataGridTemplateColumn Header="CFG" Width="56" IsReadOnly="True">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnIsoOverrideClick"
|
||
Padding="6,2"
|
||
VerticalAlignment="Center"
|
||
HorizontalAlignment="Center"
|
||
ToolTip="Override output settings for this participant (framerate, resolution, audio)">
|
||
<TextBlock Text="⚙"
|
||
FontSize="16"
|
||
FontFamily="Segoe UI Symbol"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
VerticalAlignment="Center"
|
||
HorizontalAlignment="Center"/>
|
||
</Button>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
|
||
<!-- Col 5 — ISO toggle pill. LIVE = cyan-muted fill + cyan border + cyan text.
|
||
OFF = hollow neutral. Error states use the existing IsoToggle style. -->
|
||
<DataGridTemplateColumn Header="ISO" Width="100">
|
||
<DataGridTemplateColumn.CellTemplate>
|
||
<DataTemplate>
|
||
<Button Command="{Binding ToggleIsoCommand}"
|
||
Margin="0,0,12,0"
|
||
Padding="14,6"
|
||
VerticalAlignment="Center">
|
||
<Button.Style>
|
||
<Style TargetType="Button" BasedOn="{StaticResource Wd.Button.IsoToggle}">
|
||
<Setter Property="Content" Value="Enable"/>
|
||
<Style.Triggers>
|
||
<DataTrigger Binding="{Binding IsEnabled}" Value="True">
|
||
<Setter Property="Content" Value="● LIVE"/>
|
||
<Setter Property="Background" Value="{DynamicResource Wd.Accent.CyanMuted}"/>
|
||
<Setter Property="BorderBrush" Value="{DynamicResource Wd.Accent.Cyan}"/>
|
||
<Setter Property="Foreground" Value="{DynamicResource Wd.Accent.CyanText}"/>
|
||
</DataTrigger>
|
||
</Style.Triggers>
|
||
</Style>
|
||
</Button.Style>
|
||
</Button>
|
||
</DataTemplate>
|
||
</DataGridTemplateColumn.CellTemplate>
|
||
</DataGridTemplateColumn>
|
||
</DataGrid.Columns>
|
||
</DataGrid>
|
||
|
||
<!-- Empty-state placeholder. Renders when no NDI participants
|
||
have been discovered yet. Mono sentence + one tertiary
|
||
Refresh button — no illustration, no mascot, per the v2
|
||
shape brief's empty-states section. -->
|
||
<StackPanel HorizontalAlignment="Center"
|
||
VerticalAlignment="Center"
|
||
Visibility="{Binding ParticipantCount, Converter={StaticResource CountToVis}, ConverterParameter=empty}">
|
||
<TextBlock Text="no ndi sources yet — open teams and start a meeting"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
HorizontalAlignment="Center"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding RefreshDiscoveryCommand}"
|
||
Content="Refresh discovery (Ctrl+R)"
|
||
Padding="14,7"
|
||
Margin="0,14,0,0"
|
||
HorizontalAlignment="Center"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"
|
||
ToolTip="Rebuild the NDI finder"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
</Grid>
|
||
|
||
<!-- ═════════════════════ MEETING BAR (conditional) ═════════════════════ -->
|
||
<Border Grid.Row="3"
|
||
Background="{DynamicResource Wd.SurfaceElevated}"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="0,1,0,0"
|
||
Padding="20,10,20,10"
|
||
Visibility="{Binding IsTeamsInCall, Converter={StaticResource BoolToVis}}">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="Auto"/>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
|
||
<StackPanel Grid.Column="0"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center">
|
||
<TextBlock Text="IN CALL"
|
||
FontFamily="{StaticResource Wd.Font.Sans}"
|
||
FontSize="10"
|
||
FontWeight="SemiBold"
|
||
Foreground="{DynamicResource Wd.Accent.CyanText}"
|
||
VerticalAlignment="Center"
|
||
Margin="0,0,10,0"/>
|
||
<TextBlock Text="{Binding TeamsMeetingState}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="12"
|
||
Foreground="{DynamicResource Wd.Text.Primary}"
|
||
VerticalAlignment="Center"
|
||
MaxWidth="500"
|
||
TextTrimming="CharacterEllipsis"/>
|
||
</StackPanel>
|
||
|
||
<!-- Quick-join field — paste a meeting URL, click Join -->
|
||
<StackPanel Grid.Column="1"
|
||
Orientation="Horizontal"
|
||
HorizontalAlignment="Center"
|
||
VerticalAlignment="Center"
|
||
Visibility="{Binding IsTeamsInCall, Converter={StaticResource BoolToVisInverse}}">
|
||
<!-- Hidden when in call to keep the bar uncluttered -->
|
||
</StackPanel>
|
||
|
||
<StackPanel Grid.Column="2"
|
||
Orientation="Horizontal"
|
||
VerticalAlignment="Center">
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding ToggleMuteCommand}"
|
||
Content="Mute"
|
||
Padding="14,6"
|
||
Margin="0,0,6,0"
|
||
MinWidth="64"
|
||
ToolTip="Toggle microphone mute in Teams"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding ToggleCameraCommand}"
|
||
Content="Cam"
|
||
Padding="14,6"
|
||
Margin="0,0,6,0"
|
||
MinWidth="64"
|
||
ToolTip="Toggle camera in Teams"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Command="{Binding LeaveCallCommand}"
|
||
Content="Leave"
|
||
Padding="14,6"
|
||
MinWidth="64"
|
||
Foreground="{DynamicResource Wd.Accent.Coral}"
|
||
BorderBrush="{DynamicResource Wd.Accent.Coral}"
|
||
ToolTip="Leave the Teams call"/>
|
||
</StackPanel>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- ═════════════════════ SETTINGS DRAWER (overlay) ═════════════════════
|
||
Slides in over the main content from the right when the header gear
|
||
is clicked. Backdrop scrim is a semi-transparent overlay over the
|
||
whole window; clicking it dismisses the drawer. Esc also dismisses
|
||
(handled in code-behind). The drawer carries the same settings
|
||
surface as v1 — OUTPUT / NETWORK / DISPLAY tabs with the existing
|
||
bindings — so the engine wiring is preserved through the shell
|
||
rebuild. -->
|
||
<Grid Grid.Row="0"
|
||
Grid.RowSpan="4"
|
||
x:Name="SettingsDrawerOverlay"
|
||
Visibility="Collapsed">
|
||
<!-- Scrim — click to dismiss -->
|
||
<Border Background="#80000000"
|
||
MouseLeftButtonDown="OnSettingsScrimClick"/>
|
||
<!-- Drawer panel -->
|
||
<Border HorizontalAlignment="Right"
|
||
Width="420"
|
||
Background="{DynamicResource Wd.Surface}"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="1,0,0,0">
|
||
<DockPanel LastChildFill="True">
|
||
<!-- Drawer header -->
|
||
<Border DockPanel.Dock="Top"
|
||
Padding="20,18,16,16"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="0,0,0,1">
|
||
<Grid>
|
||
<Grid.ColumnDefinitions>
|
||
<ColumnDefinition Width="*"/>
|
||
<ColumnDefinition Width="Auto"/>
|
||
</Grid.ColumnDefinitions>
|
||
<StackPanel Grid.Column="0">
|
||
<TextBlock Text="Settings"
|
||
Style="{StaticResource Wd.Text.Title}"
|
||
FontSize="18"/>
|
||
<TextBlock Text="Per-session global processing"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
Margin="0,2,0,0"/>
|
||
</StackPanel>
|
||
<Button Grid.Column="1"
|
||
Style="{StaticResource Wd.Button.Ghost}"
|
||
Click="OnSettingsToggleClick"
|
||
Padding="8,4"
|
||
ToolTip="Close (Esc)">
|
||
<Path Data="M 0,0 L 10,10 M 10,0 L 0,10"
|
||
Stroke="{DynamicResource Wd.Text.Secondary}"
|
||
StrokeThickness="1.4"
|
||
Width="10" Height="10"
|
||
Stretch="None"/>
|
||
</Button>
|
||
</Grid>
|
||
</Border>
|
||
|
||
<!-- Drawer footer (Apply) -->
|
||
<Border DockPanel.Dock="Bottom"
|
||
Padding="20,12,20,16"
|
||
BorderBrush="{DynamicResource Wd.Border}"
|
||
BorderThickness="0,1,0,0">
|
||
<StackPanel Orientation="Horizontal"
|
||
HorizontalAlignment="Right">
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Close"
|
||
Click="OnSettingsToggleClick"
|
||
Padding="14,6"
|
||
Margin="0,0,8,0"/>
|
||
<Button Style="{StaticResource Wd.Button.Primary}"
|
||
Content="Apply changes"
|
||
Command="{Binding Settings.ApplyCommand}"
|
||
Padding="14,6"/>
|
||
</StackPanel>
|
||
</Border>
|
||
|
||
<!-- Drawer scrollable body — three tabs -->
|
||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||
HorizontalScrollBarVisibility="Disabled">
|
||
<Border Padding="20,16,20,16">
|
||
<TabControl Style="{StaticResource Wd.TabControl}">
|
||
<!-- ─────── OUTPUT ─────── -->
|
||
<TabItem Header="OUTPUT" Style="{StaticResource Wd.TabItem}">
|
||
<StackPanel>
|
||
<TextBlock Text="Target framerate"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,0,0,4"/>
|
||
<ComboBox ItemsSource="{Binding Settings.AvailableFramerates}"
|
||
SelectedItem="{Binding Settings.Framerate}">
|
||
<ComboBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
||
</DataTemplate>
|
||
</ComboBox.ItemTemplate>
|
||
</ComboBox>
|
||
|
||
<TextBlock Text="Target resolution"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,12,0,4"/>
|
||
<ComboBox ItemsSource="{Binding Settings.AvailableResolutions}"
|
||
SelectedItem="{Binding Settings.Resolution}">
|
||
<ComboBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
||
</DataTemplate>
|
||
</ComboBox.ItemTemplate>
|
||
</ComboBox>
|
||
|
||
<TextBlock Text="Aspect mode"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,12,0,4"/>
|
||
<ComboBox ItemsSource="{Binding Settings.AvailableAspectModes}"
|
||
SelectedItem="{Binding Settings.Aspect}">
|
||
<ComboBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
||
</DataTemplate>
|
||
</ComboBox.ItemTemplate>
|
||
</ComboBox>
|
||
|
||
<TextBlock Text="Audio mode"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,12,0,4"/>
|
||
<ComboBox ItemsSource="{Binding Settings.AvailableAudioModes}"
|
||
SelectedItem="{Binding Settings.Audio}">
|
||
<ComboBox.ItemTemplate>
|
||
<DataTemplate>
|
||
<TextBlock Text="{Binding Converter={StaticResource EnumDesc}}"/>
|
||
</DataTemplate>
|
||
</ComboBox.ItemTemplate>
|
||
</ComboBox>
|
||
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Reset to defaults"
|
||
Command="{Binding Settings.ResetOutputDefaultsCommand}"
|
||
HorizontalAlignment="Stretch"
|
||
Margin="0,16,0,0"
|
||
Padding="0,8"/>
|
||
</StackPanel>
|
||
</TabItem>
|
||
|
||
<!-- ─────── NETWORK ─────── -->
|
||
<TabItem Header="NETWORK" Style="{StaticResource Wd.TabItem}">
|
||
<StackPanel>
|
||
<TextBlock Text="Discovery group"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,0,0,4"/>
|
||
<TextBox Text="{Binding Settings.DiscoveryGroups, UpdateSourceTrigger=PropertyChanged}"/>
|
||
|
||
<TextBlock Text="Output group"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,12,0,4"/>
|
||
<TextBox Text="{Binding Settings.OutputGroups, UpdateSourceTrigger=PropertyChanged}"/>
|
||
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Apply transcoder topology"
|
||
Command="{Binding Settings.ApplyTranscoderTopologyCommand}"
|
||
HorizontalAlignment="Stretch"
|
||
Margin="0,16,0,0"
|
||
Padding="0,9"/>
|
||
<TextBlock Text="Pins Teams' raw NDI broadcasts to a private 'teamsiso-input' group so they don't pollute the Public network. Restart Teams after applying."
|
||
Style="{StaticResource Wd.Text.Body}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
TextWrapping="Wrap"
|
||
Margin="0,6,0,0"/>
|
||
|
||
<TextBlock Text="Output name template"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,16,0,4"/>
|
||
<TextBox Text="{Binding Settings.OutputNameTemplate, UpdateSourceTrigger=LostFocus}"
|
||
FontFamily="{StaticResource Wd.Font.Mono}"
|
||
FontSize="11"/>
|
||
<TextBlock Text="Tokens: {name}, {guid}, {machine}, {timestamp}. Default: TEAMSISO_{guid}."
|
||
Style="{StaticResource Wd.Text.Body}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Text.Tertiary}"
|
||
TextWrapping="Wrap"
|
||
Margin="0,4,0,0"/>
|
||
</StackPanel>
|
||
</TabItem>
|
||
|
||
<!-- ─────── APP ─────── -->
|
||
<TabItem Header="APP" Style="{StaticResource Wd.TabItem}">
|
||
<StackPanel>
|
||
<CheckBox Content="Hide my self-preview from participants"
|
||
IsChecked="{Binding Settings.HideLocalSelf}"/>
|
||
<CheckBox Content="Auto-disable ISOs when a participant leaves"
|
||
IsChecked="{Binding Settings.AutoDisableOnDeparture}"
|
||
Margin="0,8,0,0"/>
|
||
<CheckBox Content="Auto-apply last preset on launch"
|
||
IsChecked="{Binding Settings.AutoApplyLastPreset}"
|
||
Margin="0,8,0,0"/>
|
||
|
||
<TextBlock Text="Sort participants by"
|
||
Style="{StaticResource Wd.Text.Subtle}"
|
||
Margin="0,14,0,4"/>
|
||
<ComboBox ItemsSource="{Binding Settings.AvailableSortModes}"
|
||
SelectedItem="{Binding Settings.ParticipantSort}"/>
|
||
|
||
<CheckBox Content="Minimize to system tray"
|
||
IsChecked="{Binding Settings.MinimizeToTray}"
|
||
Margin="0,14,0,0"/>
|
||
|
||
<Border Margin="0,16,0,8"
|
||
Height="1"
|
||
Background="{DynamicResource Wd.Border}"/>
|
||
|
||
<CheckBox Content="Launch Microsoft Teams on TeamsISO startup"
|
||
IsChecked="{Binding Settings.LaunchTeamsOnStartup}"/>
|
||
<CheckBox Content="Auto-hide Teams windows when launched"
|
||
IsChecked="{Binding Settings.AutoHideTeamsWindows}"
|
||
Margin="0,8,0,0"/>
|
||
|
||
<Border Margin="0,16,0,8"
|
||
Height="1"
|
||
Background="{DynamicResource Wd.Border}"/>
|
||
|
||
<CheckBox Content="Embed Teams window (experimental)"
|
||
IsChecked="{Binding Settings.EmbedTeamsWindow}"/>
|
||
<Button Style="{StaticResource Wd.Button.Ghost}"
|
||
Content="Open embed window"
|
||
Click="OnOpenEmbedWindowClick"
|
||
HorizontalAlignment="Stretch"
|
||
Margin="0,8,0,0"
|
||
Padding="0,8"
|
||
IsEnabled="{Binding Settings.EmbedTeamsWindow}"/>
|
||
|
||
<Border Margin="0,16,0,8"
|
||
Height="1"
|
||
Background="{DynamicResource Wd.Border}"/>
|
||
|
||
<CheckBox Content="Control surface (Stream Deck / Companion / web)"
|
||
IsChecked="{Binding Settings.ControlSurfaceEnabled}"/>
|
||
<CheckBox Content="LAN-reachable"
|
||
IsChecked="{Binding Settings.ControlSurfaceLanReachable}"
|
||
Margin="20,4,0,0"/>
|
||
<TextBlock Text="{Binding Settings.ControlSurfaceUrl}"
|
||
Style="{StaticResource Wd.Text.Mono}"
|
||
FontSize="11"
|
||
Foreground="{DynamicResource Wd.Accent.CyanText}"
|
||
Margin="20,4,0,0"/>
|
||
</StackPanel>
|
||
</TabItem>
|
||
</TabControl>
|
||
</Border>
|
||
</ScrollViewer>
|
||
</DockPanel>
|
||
</Border>
|
||
</Grid>
|
||
</Grid>
|
||
</Window>
|