teamsiso/src/TeamsISO.App/OnboardingWindow.xaml
Zac Gaetano acc569dd24
Some checks failed
CI / build-and-test (push) Failing after 30s
Onboarding step + Open /ui button + recording duration in footer
Three small UX wins:

1. Onboarding gained step 5 ('Run Teams headless') and step 6 ('Drive from another machine') so new operators discover the auto-launch/auto-hide + LAN-reachable workflows. Existing 'where things live' step renumbered to 7.

2. Settings → DISPLAY → Control surface URL row gains an Open button next to Copy that fires the URL into the default browser via Process.Start with UseShellExecute. Operators previewing how the embedded /ui control panel looks on a phone/tablet no longer need to copy-paste manually.

3. Recording badge in footer now shows 'REC 3 · 12:45' instead of just 'REC 3'. RecordingElapsed VM property maintains a separate timer from the session timer because recording can start AFTER the meeting begins; operators tracking 'how long has the archive copy been rolling' need that distinct duration.
2026-05-10 21:05:30 -04:00

294 lines
18 KiB
XML

<Window x:Class="TeamsISO.App.OnboardingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
Title="Welcome to TeamsISO"
Icon="/Assets/teamsiso.ico"
Width="560" Height="600"
WindowStartupLocation="CenterOwner"
WindowStyle="None"
ResizeMode="NoResize"
Background="{DynamicResource Wd.Canvas}"
UseLayoutRounding="True"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="ClearType">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome
CaptionHeight="32"
ResizeBorderThickness="0"
CornerRadius="0"
GlassFrameThickness="0"
UseAeroCaptionButtons="False"/>
</shell:WindowChrome.WindowChrome>
<Border BorderBrush="{DynamicResource Wd.Border}" BorderThickness="1">
<Grid Margin="32,20,32,24">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Caption -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="WELCOME"
Style="{StaticResource Wd.Text.Caption}"
VerticalAlignment="Center"/>
<Button Grid.Column="1"
Style="{StaticResource Wd.Button.CaptionClose}"
Click="OnDismiss"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<Path Data="M 0,0 L 10,10 M 10,0 L 0,10"
Stroke="{DynamicResource Wd.Text.Primary}"
StrokeThickness="1.2"
Width="10" Height="10"
Stretch="None"/>
</Button>
</Grid>
<!-- Hero -->
<StackPanel Grid.Row="1" Margin="0,16,0,20">
<Image Source="/Assets/dragon-mark.png"
Width="56" Height="56"
HorizontalAlignment="Left"
Margin="0,0,0,12"
RenderOptions.BitmapScalingMode="HighQuality"/>
<TextBlock Text="TeamsISO routes Microsoft Teams participants as isolated NDI feeds."
Style="{StaticResource Wd.Text.Title}"
TextWrapping="Wrap"/>
<TextBlock Text="A few one-time setup notes before you start."
Style="{StaticResource Wd.Text.Subtle}"
Foreground="{DynamicResource Wd.Text.Tertiary}"
Margin="0,6,0,0"/>
</StackPanel>
<!-- Body: numbered checklist -->
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<StackPanel>
<!-- Step 1 — NDI runtime -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="1"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Install the NDI 6 runtime"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="TeamsISO depends on NDI 6 from NewTek/Vizrt. Get it at ndi.video/tools — pick the Tools bundle for free, or use the Runtime-only installer if you have an NDI subscription."/>
</StackPanel>
</Border>
<!-- Step 2 — Teams NDI permission -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="2"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Enable broadcast in Teams"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="In Microsoft Teams: Settings → Devices → 'Broadcast over NDI / SDI'. Your Teams admin may need to enable this at the tenant level (Teams admin center → Meetings → Meeting policies → 'Allow NDI broadcasting')."/>
</StackPanel>
</Border>
<!-- Step 3 — Transcoder topology -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="3"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Click 'Apply transcoder topology'"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="Settings → NETWORK → Apply transcoder topology. This pins Teams' raw NDI broadcasts to a private 'teamsiso-input' group so they don't pollute the Public network. Restart Teams once after applying."/>
</StackPanel>
</Border>
<!-- Step 4 — Save a preset -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="4"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Save a preset for recurring shows"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="Once you've toggled the right ISOs, click Presets in the participants header to save them by display name. Turn on 'Auto-apply last preset on launch' under DISPLAY settings and TeamsISO will restore that routing on every subsequent launch."/>
</StackPanel>
</Border>
<!-- Step 5 — Headless Teams ("I only see TeamsISO") -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="5"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Run Teams headless (optional)"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="To use TeamsISO as your only window: tick both 'Launch Microsoft Teams on TeamsISO startup' and 'Auto-hide Teams windows when launched' under Settings → DISPLAY. Teams runs in the background; the IN-CALL bar shows the meeting state (READY / IN CALL · meeting title), the Mute/Camera/Share/Leave buttons drive Teams via UIAutomation, and the URL paste-box joins meetings directly. Use the eye-icon button in the left rail to manually restore Teams' windows when you need them."/>
</StackPanel>
</Border>
<!-- Step 6 — Where things live -->
<Border Style="{StaticResource Wd.Card}" Padding="16" Margin="0,0,0,12">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="6"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Drive from another machine (optional)"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="For headless host PC + thin-client setups: tick 'Control surface' then 'LAN-reachable' under DISPLAY. TeamsISO listens on http://&lt;your-lan-ip&gt;:9755/ui — open that URL from any browser on the LAN. First-time use needs a one-shot 'netsh http add urlacl url=http://+:9755/ user=Everyone' in an Administrator PowerShell."/>
</StackPanel>
</Border>
<!-- Step 7 — Where things live -->
<Border Style="{StaticResource Wd.Card}" Padding="16">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="22" Height="22"
CornerRadius="11"
Background="{DynamicResource Wd.Accent.CyanMuted}"
VerticalAlignment="Center"
Margin="0,0,12,0">
<TextBlock Text="7"
Foreground="{DynamicResource Wd.Accent.Cyan}"
FontFamily="{StaticResource Wd.Font.Mono}"
FontSize="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Text="If something breaks…"
Style="{StaticResource Wd.Text.Heading}"
VerticalAlignment="Center"/>
</StackPanel>
<TextBlock TextWrapping="Wrap"
Style="{StaticResource Wd.Text.Body}"
FontSize="12"
Foreground="{DynamicResource Wd.Text.Secondary}"
Text="Diagnostic logs roll daily under %LOCALAPPDATA%\TeamsISO\Logs. Settings live at %APPDATA%\TeamsISO\config.json; presets at %LOCALAPPDATA%\TeamsISO\presets.json. Attach the most recent log when filing an issue at forge.wilddragon.net/zgaetano/teamsiso."/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
<!-- Footer -->
<Grid Grid.Row="3" Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0"
x:Name="SuppressBox"
Content="Don't show this again"
IsChecked="True"
VerticalAlignment="Center"/>
<Button Grid.Column="2"
Style="{StaticResource Wd.Button.Primary}"
Content="Get started"
Click="OnDismiss"
Padding="22,8"/>
</Grid>
</Grid>
</Border>
</Window>