CVR Manager 1.0.0

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Exterrata
2026-10-25 02:46:35 -04:00
commit 9bb4f85802
59 changed files with 7913 additions and 0 deletions
+627
View File
@@ -0,0 +1,627 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:CVRManager.ViewModels"
xmlns:m="using:CVRManager.Models"
xmlns:v="using:CVRManager.Views"
x:Class="CVRManager.Views.MainWindow"
Icon="/Assets/app.ico" Title="CVR Manager" Width="1240" Height="820" MinWidth="1000" MinHeight="620">
<Grid>
<DockPanel>
<Border DockPanel.Dock="Top" Background="{DynamicResource SidebarBg}" BorderBrush="{DynamicResource CardBorder}" BorderThickness="0,0,0,1" Padding="24,0">
<StackPanel Orientation="Horizontal" Spacing="4">
<Path Data="{StaticResource CvrMark}" Fill="{DynamicResource AccentBrush}" Stretch="Uniform" Height="17" VerticalAlignment="Center" Margin="0,0,12,0"/>
<TextBlock Text="CVR Manager" FontSize="16" FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,28,0"/>
<RadioButton Classes="tab" GroupName="top" Content="Installs" IsChecked="{Binding IsInstallsPage}"/>
<RadioButton Classes="tab" GroupName="top" Content="Sources" IsChecked="{Binding IsModsPage}"/>
<RadioButton Classes="tab" GroupName="top" Content="Gallery" IsChecked="{Binding IsGalleryPage}"/>
<RadioButton Classes="tab" GroupName="top" Content="Settings" IsChecked="{Binding IsSettingsPage}"/>
</StackPanel>
</Border>
<Panel>
<Grid ColumnDefinitions="300,*" Margin="24,20,24,20" IsVisible="{Binding IsInstallsPage}">
<DockPanel Margin="0,0,20,0">
<StackPanel DockPanel.Dock="Bottom" Spacing="8" Margin="0,8,0,0">
<Button Content="+ New install" Command="{Binding ShowNewCommand}" Classes="accent" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
<Button Content="Refresh branches" Command="{Binding RefreshBranchesCommand}" Classes="ghost" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"/>
</StackPanel>
<ListBox x:Name="InstallList" Classes="cards" ItemsSource="{Binding Installs}" SelectedItem="{Binding SelectedInstall}">
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:InstallVM">
<Grid ColumnDefinitions="*,Auto" Margin="16,12,12,12" ColumnSpacing="8">
<StackPanel Spacing="3">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="15" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding Branch}" Classes="dim" FontSize="12" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding CardStatus}" Classes="dim" FontSize="12" IsVisible="{Binding !UpdateAvailable}"/>
<TextBlock Text="{Binding CardStatus}" Foreground="{DynamicResource WarnFg}" FontWeight="SemiBold" FontSize="12" IsVisible="{Binding UpdateAvailable}"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="6" VerticalAlignment="Stretch">
<StackPanel Spacing="4" VerticalAlignment="Center">
<Button Content="▶ Desktop" Command="{Binding LaunchDesktopCommand}" IsEnabled="{Binding HasGame}" Classes="accent" Padding="10,6" FontSize="12" HorizontalAlignment="Stretch" ToolTip.Tip="Launch in desktop mode (again, if it is already running)"/>
<Button Content="▶ VR" Command="{Binding LaunchVrCommand}" IsEnabled="{Binding HasGame}" Padding="10,6" FontSize="12" HorizontalAlignment="Stretch" ToolTip.Tip="Launch in VR mode (again, if it is already running)"/>
</StackPanel>
<Button Content="{Binding StopShortText}" Command="{Binding StopCommand}" IsVisible="{Binding IsRunning}" Classes="stop" Padding="9,6" FontSize="12" VerticalAlignment="Stretch" VerticalContentAlignment="Center" ToolTip.Tip="{Binding StopTip}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<Panel Grid.Column="1">
<StackPanel IsVisible="{Binding NoInstalls}" HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="10">
<TextBlock Text="No installs yet" Classes="h1" HorizontalAlignment="Center"/>
</StackPanel>
<ContentControl Content="{Binding SelectedInstall}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="vm:InstallVM">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Spacing="12" Margin="0,0,0,12">
<Grid ColumnDefinitions="*,Auto">
<StackPanel Spacing="8">
<TextBlock Text="{Binding Name}" Classes="h1"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Border Classes="chip"><TextBlock Text="{Binding Branch}"/></Border>
<Border Classes="chip"><TextBlock Text="{Binding BuildText}"/></Border>
<Border Classes="chip good" IsVisible="{Binding UpToDate}"><TextBlock Text="Up to date"/></Border>
<Border Classes="chip warn" IsVisible="{Binding UpdateAvailable}"><TextBlock Text="{Binding UpdateChipText}"/></Border>
<Border Classes="chip" IsVisible="{Binding UpdateUnknown}"><TextBlock Text="Latest build unknown"/></Border>
<Border Classes="chip good" IsVisible="{Binding IsRunning}"><TextBlock Text="{Binding RunningText}"/></Border>
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8" VerticalAlignment="Top">
<ComboBox ItemsSource="{Binding Main.AccountChoices}" SelectedItem="{Binding AccountChoice}" IsVisible="{Binding Main.HasAccounts}" MinWidth="150" MinHeight="40" Padding="12,8" ToolTip.Tip="Account">
<ComboBox.ItemTemplate>
<DataTemplate DataType="m:AccountChoice"><TextBlock Text="{Binding Name}"/></DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding LaunchModes}" SelectedItem="{Binding LaunchModeChoice}" MinWidth="130" MinHeight="40" Padding="12,8">
<ComboBox.ItemTemplate>
<DataTemplate DataType="m:LaunchModeOption"><TextBlock Text="{Binding Label}"/></DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="▶ Launch" Command="{Binding LaunchCommand}" IsEnabled="{Binding HasGame}" Classes="accent" MinWidth="132" HorizontalContentAlignment="Center" ToolTip.Tip="{Binding LaunchTip}"/>
<Button Content="{Binding StopText}" Command="{Binding StopCommand}" IsVisible="{Binding IsRunning}" Classes="stop" HorizontalContentAlignment="Center" ToolTip.Tip="{Binding StopTip}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="6">
<Button Content="Update" Command="{Binding UpdateCommand}"/>
<Button Content="Verify files" Command="{Binding VerifyCommand}"/>
<Button Content="Open folder" Command="{Binding OpenFolderCommand}"/>
<Button Content="Duplicate" Command="{Binding DuplicateCommand}"/>
<Button Content="Delete install" Command="{Binding DeleteCommand}" Classes="ghost danger"/>
</StackPanel>
<Border BorderBrush="{DynamicResource CardBorder}" BorderThickness="0,0,0,1">
<StackPanel Orientation="Horizontal">
<RadioButton Classes="tab" GroupName="sub" Content="Overview" IsChecked="{Binding ShowOverview}"/>
<RadioButton Classes="tab" GroupName="sub" Content="Mods" IsChecked="{Binding ShowModsTab}"/>
<RadioButton Classes="tab" GroupName="sub" Content="Logs" IsChecked="{Binding ShowLogs}"/>
</StackPanel>
</Border>
</StackPanel>
<Panel>
<ScrollViewer IsVisible="{Binding ShowOverview}">
<StackPanel Spacing="14" Margin="0,0,10,16" MaxWidth="900" HorizontalAlignment="Stretch">
<Border Classes="card">
<StackPanel Spacing="8">
<TextBlock Text="Branch and launch" Classes="h2"/>
<TextBlock Text="Name" Classes="label"/>
<TextBox Text="{Binding Name}"/>
<TextBlock Text="Branch" Classes="label"/>
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="8">
<ComboBox ItemsSource="{Binding Main.BranchChoices}" SelectedItem="{Binding BranchHint}" HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate DataType="m:BranchChoice"><TextBlock Text="{Binding Display}"/></DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1" Content="+ Beta code" Command="{Binding EnterCodeCommand}" VerticalAlignment="Stretch"/>
</Grid>
<TextBlock Text="Launch arguments" Classes="label"/>
<Grid ColumnDefinitions="*,260" ColumnSpacing="8">
<TextBox Text="{Binding LaunchArgs}" PlaceholderText="Extra arguments"/>
<ComboBox Grid.Column="1" ItemsSource="{x:Static m:LaunchArgOption.All}" SelectedItem="{Binding ArgChoice}" PlaceholderText="Add an argument..." HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate DataType="m:LaunchArgOption">
<StackPanel ToolTip.Tip="{Binding Description}">
<TextBlock Text="{Binding Flag}" FontWeight="Medium"/>
<TextBlock Text="{Binding Description}" Classes="dim" FontSize="11" MaxWidth="360" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="MelonLoader (version.dll)" Classes="h2"/>
<TextBlock Text="{Binding MlText}" Classes="dim"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<ComboBox ItemsSource="{Binding MlVersions}" SelectedItem="{Binding MlVersionChoice}" PlaceholderText="Version" MinWidth="150"/>
<Button Content="Install this version" Command="{Binding SetMlVersionCommand}"/>
<Button Content="{Binding MlToggleText}" Command="{Binding ToggleMlCommand}"/>
</StackPanel>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="8">
<TextBlock Text="Hooks" Classes="h2"/>
<ItemsControl ItemsSource="{Binding Hooks}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:InstallHookVM">
<Grid ColumnDefinitions="Auto,*,Auto" ColumnSpacing="12" Margin="0,3">
<CheckBox IsChecked="{Binding Enabled}" MinWidth="0"/>
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" FontWeight="Medium"/>
<TextBlock Text="{Binding When}" Classes="dim" FontSize="12"/>
</StackPanel>
<Button Grid.Column="2" Content="Run now" Command="{Binding RunCommand}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<Border Classes="card">
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="12">
<StackPanel Spacing="4">
<TextBlock Text="Shared cache" Classes="h2"/>
<TextBlock Text="{Binding CacheText}" Classes="dim"/>
</StackPanel>
<Button Grid.Column="1" Content="Link to shared cache" Command="{Binding LinkCacheCommand}" VerticalAlignment="Center"/>
</Grid>
</Border>
</StackPanel>
</ScrollViewer>
<Grid IsVisible="{Binding ShowModsTab}" RowDefinitions="Auto,Auto,*,Auto" Margin="0,0,0,4">
<Grid ColumnDefinitions="*,200" ColumnSpacing="8" Margin="0,0,0,8">
<TextBox Text="{Binding Main.ModSearch}" PlaceholderText="Search all available mods"/>
<ComboBox Grid.Column="1" ItemsSource="{x:Static vm:MainViewModel.ModFilters}" SelectedItem="{Binding Main.ModFilter}" HorizontalAlignment="Stretch"/>
</Grid>
<Grid Grid.Row="1" ColumnDefinitions="34,250,90,90,*,96" Margin="12,0,12,4">
<TextBlock Grid.Column="1" Text="Name" Classes="dim" FontSize="12"/>
<TextBlock Grid.Column="2" Text="Installed" Classes="dim" FontSize="12"/>
<TextBlock Grid.Column="3" Text="Latest" Classes="dim" FontSize="12"/>
<TextBlock Grid.Column="4" Text="{Binding Main.AvailableStatus}" Classes="dim" FontSize="12"/>
</Grid>
<ListBox Grid.Row="2" Classes="table" ItemsSource="{Binding Main.ModRows}" SelectedItem="{Binding Main.SelectedRow}" SelectionChanged="OnTableSelection">
<ListBox.DataTemplates>
<DataTemplate DataType="vm:GroupHeader">
<Border Background="{DynamicResource GroupHeaderBg}" Padding="12,9" Margin="0,6,0,0" CornerRadius="6" Cursor="Hand">
<TextBlock Text="{Binding Text}" FontWeight="SemiBold"/>
</Border>
</DataTemplate>
<DataTemplate DataType="vm:ModVM">
<Grid ColumnDefinitions="34,250,90,90,*,96" Margin="12,6" ToolTip.Tip="{Binding Description}">
<CheckBox IsChecked="{Binding InstallUsed}" IsEnabled="{Binding HasInstall}" MinWidth="0" VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" VerticalAlignment="Center" Margin="0,0,10,0">
<TextBlock Text="{Binding Name}" FontWeight="Medium" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding AuthorLine}" Classes="dim" FontSize="11" TextTrimming="CharacterEllipsis"/>
</StackPanel>
<StackPanel Grid.Column="2" VerticalAlignment="Center" Margin="0,0,8,0">
<TextBlock Text="{Binding InstalledText}" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="update" FontSize="11" Foreground="{DynamicResource WarnFg}" IsVisible="{Binding UpdateAvailable}"/>
</StackPanel>
<TextBlock Grid.Column="3" Text="{Binding LatestText}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" Margin="0,0,8,0"/>
<TextBlock Grid.Column="4" Text="{Binding ShortDescription}" Classes="dim" VerticalAlignment="Center" TextWrapping="Wrap" MaxHeight="34" TextTrimming="CharacterEllipsis" Margin="0,0,10,0"/>
<Button Grid.Column="5" Content="Manage" Command="{Binding ManageCommand}" Padding="10,5" HorizontalAlignment="Right" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.DataTemplates>
</ListBox>
<Grid Grid.Row="3" ColumnDefinitions="Auto,Auto,*,Auto,Auto" ColumnSpacing="8" Margin="0,12,0,0">
<StackPanel Grid.ColumnSpan="2" Orientation="Horizontal" Spacing="8">
<Button Content="Scan Steam install" Command="{Binding Main.ScanSteamInstallCommand}" IsVisible="{Binding Main.HasDetected}"/>
<Button Content="Scan this install's folder" Command="{Binding ImportModsCommand}"/>
<Button Content="Refresh sources" Command="{Binding Main.RefreshAllModsCommand}" Classes="ghost"/>
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding PendingText}" Foreground="{DynamicResource WarnFg}" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,12,0"/>
<Button Grid.Column="3" Content="Add local file(s)..." Command="{Binding Main.AddLocalNewCommand}"/>
<Button Grid.Column="4" Content="Install / Update" Command="{Binding ApplyCommand}" Classes="accent"/>
</Grid>
</Grid>
<Grid IsVisible="{Binding ShowLogs}" RowDefinitions="Auto,*" Margin="0,0,0,4">
<StackPanel Orientation="Horizontal" Margin="0,0,0,8" Spacing="4">
<RadioButton Classes="tab" GroupName="logtab" Content="MelonLoader" IsChecked="{Binding ShowMlLog}" Padding="14,8"/>
<RadioButton Classes="tab" GroupName="logtab" Content="Unity" IsChecked="{Binding ShowUnityLog}" Padding="14,8"/>
<TextBlock Text="Shared by all installs" Classes="dim" FontSize="12" VerticalAlignment="Center" Margin="12,0,0,0" IsVisible="{Binding ShowUnityLog}"/>
</StackPanel>
<v:LogView Grid.Row="1" Text="{Binding MlLogText}" IsVisible="{Binding ShowMlLog}"/>
<v:LogView Grid.Row="1" Text="{Binding UnityLogText}" IsVisible="{Binding ShowUnityLog}"/>
</Grid>
</Panel>
</DockPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Panel>
</Grid>
<Grid ColumnDefinitions="320,*" Margin="24,20,24,20" IsVisible="{Binding IsModsPage}">
<DockPanel Margin="0,0,20,0">
<TextBlock DockPanel.Dock="Top" Text="Sources" Classes="h1" Margin="0,0,0,14"/>
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto" ColumnSpacing="8" Margin="0,8,0,0">
<TextBox Text="{Binding AddSourceRepo}" PlaceholderText="owner/repo or URL"/>
<Button Grid.Column="1" Content="Add" Command="{Binding AddSourceCommand}" Classes="accent" Padding="16,8"/>
</Grid>
<ListBox Classes="cards" ItemsSource="{Binding Sources}" SelectedItem="{Binding SelectedSource}">
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:SourceVM">
<StackPanel Spacing="2" Margin="16,12">
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding Detail}" Classes="dim" FontSize="12" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding CountText}" Classes="dim" FontSize="12"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
<ContentControl Grid.Column="1" Content="{Binding SelectedSource}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="vm:SourceVM">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Spacing="10" Margin="0,0,0,12">
<Grid ColumnDefinitions="*,Auto,Auto" ColumnSpacing="8">
<TextBlock Text="{Binding Name}" Classes="h1" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Refresh" Command="{Binding Main.RefreshAllModsCommand}" VerticalAlignment="Center"/>
<Button Grid.Column="2" Content="Remove source" Command="{Binding RemoveCommand}" IsVisible="{Binding CanRemove}" Classes="ghost danger" VerticalAlignment="Center"/>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="8">
<Border Classes="chip"><TextBlock Text="{Binding Kind}"/></Border>
<Border Classes="chip"><TextBlock Text="{Binding CountText}"/></Border>
</StackPanel>
<TextBlock Text="{Binding Link}" Classes="dim"/>
<Grid ColumnDefinitions="*,Auto,260" ColumnSpacing="8">
<TextBox Text="{Binding Filter}" PlaceholderText="Search this source"/>
<TextBlock Grid.Column="1" Text="Add to" VerticalAlignment="Center" Classes="dim"/>
<ComboBox Grid.Column="2" ItemsSource="{Binding Main.Installs}" SelectedItem="{Binding Main.SelectedInstall}" HorizontalAlignment="Stretch" PlaceholderText="Pick an install">
<ComboBox.ItemTemplate>
<DataTemplate DataType="vm:InstallVM"><TextBlock Text="{Binding Name}"/></DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
<Grid DockPanel.Dock="Bottom" ColumnDefinitions="*,Auto" Margin="0,12,0,0" IsVisible="{Binding Main.HasSelectedInstall}">
<TextBlock Text="{Binding Main.SelectedInstall.PendingText}" Foreground="{DynamicResource WarnFg}" VerticalAlignment="Center"/>
<Button Grid.Column="1" Content="Install / Update" Command="{Binding Main.SelectedInstall.ApplyCommand}" Classes="accent"/>
</Grid>
<ListBox Classes="plain" ItemsSource="{Binding VisibleMods}">
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:ModVM">
<Grid ColumnDefinitions="*,110,110,Auto,Auto" ColumnSpacing="10" Margin="8,6" ToolTip.Tip="{Binding Description}">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" FontWeight="Medium" TextTrimming="CharacterEllipsis"/>
<TextBlock Text="{Binding ShortDescription}" Classes="dim" FontSize="11" TextTrimming="CharacterEllipsis"/>
</StackPanel>
<TextBlock Grid.Column="1" Text="{Binding InstalledText}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Column="2" Text="{Binding LatestText}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<Button Grid.Column="3" Content="{Binding ToggleInstallText}" Command="{Binding ToggleInstallCommand}" IsEnabled="{Binding HasInstall}" Padding="10,5" VerticalAlignment="Center"/>
<Button Grid.Column="4" Content="Manage" Command="{Binding ManageCommand}" Classes="ghost" Padding="10,5" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
<DockPanel Margin="24,20,24,20" IsVisible="{Binding IsGalleryPage}">
<Grid DockPanel.Dock="Top" ColumnDefinitions="*,Auto,Auto" ColumnSpacing="8" Margin="0,0,0,14">
<StackPanel>
<TextBlock Text="Gallery" Classes="h1"/>
<TextBlock Text="{Binding Gallery.FolderText}" Classes="dim" FontSize="12"/>
</StackPanel>
<Button Grid.Column="1" Content="Refresh" Command="{Binding Gallery.RefreshCommand}" VerticalAlignment="Center"/>
<Button Grid.Column="2" Content="Open folder" Command="{Binding Gallery.OpenFolderCommand}" VerticalAlignment="Center"/>
</Grid>
<TextBlock DockPanel.Dock="Bottom" Text="{Binding Gallery.Status}" Classes="dim" Margin="0,8,0,0"/>
<ScrollViewer>
<StackPanel>
<TextBlock Text="No screenshots yet." Classes="dim" IsVisible="{Binding Gallery.IsEmpty}" Margin="0,20"/>
<ItemsControl ItemsSource="{Binding Gallery.Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:GalleryItemVM">
<Border Classes="card" Width="276" Margin="0,0,12,12" Padding="10">
<StackPanel Spacing="8">
<Border Background="{DynamicResource AppBg}" CornerRadius="6" ClipToBounds="True" Height="150">
<Image Source="{Binding Thumb}" Stretch="UniformToFill"/>
</Border>
<TextBlock Text="{Binding DateText}" FontWeight="Medium"/>
<TextBlock Text="{Binding Name}" Classes="dim" FontSize="11" TextTrimming="CharacterEllipsis"/>
<Grid ColumnDefinitions="*,*,Auto,Auto" ColumnSpacing="6">
<Button Content="Copy" Command="{Binding CopyCommand}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Padding="6,5"/>
<Button Grid.Column="1" Content="Open" Command="{Binding OpenCommand}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Padding="6,5"/>
<Button Grid.Column="2" Content="Show" Command="{Binding ShowCommand}" Classes="ghost" Padding="8,5"/>
<Button Grid.Column="3" Content="Delete" Command="{Binding DeleteCommand}" Classes="ghost danger" Padding="8,5"/>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Load more" Command="{Binding Gallery.MoreCommand}" IsVisible="{Binding Gallery.HasMore}" HorizontalAlignment="Center" Margin="0,8,0,16"/>
</StackPanel>
</ScrollViewer>
</DockPanel>
<ScrollViewer IsVisible="{Binding IsSettingsPage}">
<StackPanel Spacing="14" Margin="24,20,24,20" MaxWidth="780" HorizontalAlignment="Stretch">
<TextBlock Text="Settings" Classes="h1"/>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="{Binding VersionText}" Classes="h2"/>
<TextBlock Text="{Binding UpdateStatus}" Classes="dim" FontSize="12" IsVisible="{Binding UpdateStatus, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Check for updates" Command="{Binding CheckUpdateCommand}"/>
<Button Content="{Binding InstallUpdateText}" Command="{Binding InstallUpdateCommand}" IsVisible="{Binding UpdateReady}" Classes="accent"/>
</StackPanel>
<CheckBox IsChecked="{Binding CheckUpdates}" Content="Check on startup"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Steam account" Classes="h2"/>
<TextBlock Text="{Binding SteamAccountText}" Classes="dim" FontSize="12" TextWrapping="Wrap"/>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Sign in..." Command="{Binding SteamSignInCommand}"/>
<Button Content="Sign out" Command="{Binding SteamSignOutCommand}" IsVisible="{Binding SteamSignedIn}" Classes="ghost"/>
</StackPanel>
<TextBlock Text="{Binding SteamClientText}" Classes="dim" FontSize="12" TextWrapping="Wrap"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Game accounts" Classes="h2"/>
<ItemsControl ItemsSource="{Binding Accounts}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:AccountVM">
<Grid ColumnDefinitions="220,*,Auto" ColumnSpacing="10" Margin="0,0,0,6">
<TextBox Text="{Binding Name}"/>
<TextBlock Grid.Column="1" Text="{Binding Username}" Classes="dim" VerticalAlignment="Center" TextTrimming="CharacterEllipsis"/>
<Button Grid.Column="2" Content="✕" Command="{Binding RemoveCommand}" Classes="ghost" ToolTip.Tip="Remove"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Grid ColumnDefinitions="220,*,Auto,Auto" ColumnSpacing="10">
<TextBox Text="{Binding NewAccountUser}" PlaceholderText="Username"/>
<TextBox Grid.Column="1" Text="{Binding NewAccountKey}" PlaceholderText="Access key" PasswordChar="•"/>
<Button Grid.Column="2" Content="Add" Command="{Binding AddAccountCommand}"/>
<Button Grid.Column="3" Content="Import from installs" Command="{Binding ImportAccountsCommand}"/>
</Grid>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Appearance" Classes="h2"/>
<TextBlock Text="{Binding ThemeNote}" IsVisible="{Binding ThemeNote, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" Classes="dim" TextWrapping="Wrap"/>
<ComboBox ItemsSource="{Binding ThemeNames}" SelectedItem="{Binding Theme}" MinWidth="260" HorizontalAlignment="Left"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Library location" Classes="h2"/>
<Grid ColumnDefinitions="*,Auto,Auto" ColumnSpacing="8">
<TextBox Text="{Binding LibraryRoot}" IsEnabled="{Binding CanChangeRoot}"/>
<Button Grid.Column="1" Content="Browse..." Command="{Binding BrowseLibraryRootCommand}" IsEnabled="{Binding CanChangeRoot}"/>
<Button Grid.Column="2" Content="Apply" Command="{Binding ApplyLibraryRootCommand}" IsEnabled="{Binding CanChangeRoot}"/>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Open library" Command="{Binding OpenLibraryCommand}"/>
<Button Content="Open shared cache" Command="{Binding OpenCacheCommand}"/>
<Button Content="Open mod store" Command="{Binding OpenModStoreCommand}"/>
</StackPanel>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Cache" Classes="h2"/>
<StackPanel Orientation="Horizontal" Spacing="12">
<Button Content="Deduplicate and relink all caches" Command="{Binding DedupeCommand}"/>
<TextBlock Text="{Binding CacheSize}" Classes="dim" VerticalAlignment="Center"/>
</StackPanel>
<CheckBox IsChecked="{Binding CacheGuard}" Content="Multi-instance cache guard"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Hooks" Classes="h2"/>
<TextBlock Text="{}{game} {install} {branch} {build} work in arguments." TextWrapping="Wrap" Classes="dim"/>
<ItemsControl ItemsSource="{Binding Hooks}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="vm:HookVM">
<Border BorderBrush="{DynamicResource CardBorder}" BorderThickness="1" CornerRadius="8" Padding="14" Margin="0,0,0,10">
<StackPanel Spacing="8">
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="8">
<TextBox Text="{Binding Name}" FontWeight="SemiBold"/>
<Button Grid.Column="1" Content="Remove" Command="{Binding RemoveCommand}" Classes="ghost danger"/>
</Grid>
<WrapPanel>
<CheckBox IsChecked="{Binding BeforeUpdate}" Content="Before update" Margin="0,0,18,0"/>
<CheckBox IsChecked="{Binding AfterUpdate}" Content="After install / update" Margin="0,0,18,0"/>
<CheckBox IsChecked="{Binding BeforeLaunch}" Content="Before launch"/>
</WrapPanel>
<StackPanel Spacing="8">
<TextBox Text="{Binding Command}" PlaceholderText="Script or program"/>
<TextBox Text="{Binding Arguments}" PlaceholderText="Arguments, e.g. --target &quot;{game}&quot;"/>
</StackPanel>
<CheckBox IsChecked="{Binding DefaultOn}" Content="Enable on new installs"/>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="+ Add command hook" Command="{Binding AddHookCommand}" HorizontalAlignment="Left"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="Activity log" Classes="h2"/>
<v:LogView Text="{Binding LogText}" Height="260"/>
</StackPanel>
</Border>
<Border Classes="card">
<StackPanel Spacing="10">
<TextBlock Text="GitHub" Classes="h2"/>
<TextBox Text="{Binding GitHubToken}" PasswordChar="●" PlaceholderText="Personal access token (optional)"/>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
</Panel>
</DockPanel>
<Border IsVisible="{Binding ShowNew}" Background="#B0000000">
<Border Classes="card" Width="540" VerticalAlignment="Center" HorizontalAlignment="Center" Background="{DynamicResource OverlayBg}">
<StackPanel Spacing="10">
<TextBlock Text="New install" Classes="h1"/>
<TextBlock Text="Name" Classes="label"/>
<TextBox Text="{Binding NewName}" PlaceholderText="Name"/>
<TextBlock Text="Branch" Classes="label"/>
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="8">
<ComboBox ItemsSource="{Binding BranchChoices}" SelectedItem="{Binding NewChoice}" HorizontalAlignment="Stretch">
<ComboBox.ItemTemplate>
<DataTemplate DataType="m:BranchChoice"><TextBlock Text="{Binding Display}"/></DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1" Content="+ Beta code" Command="{Binding EnterCodeNewCommand}" VerticalAlignment="Stretch"/>
</Grid>
<CheckBox IsChecked="{Binding NewImport}" Content="Mirror an existing install" Margin="0,8,0,0"/>
<StackPanel Spacing="8" IsVisible="{Binding NewImport}">
<Grid ColumnDefinitions="*,Auto" ColumnSpacing="8">
<ComboBox ItemsSource="{Binding DetectedInstalls}" SelectedItem="{Binding NewDetected}" PlaceholderText="Detected Steam installs" HorizontalAlignment="Stretch"/>
<Button Grid.Column="1" Content="Browse..." Command="{Binding BrowseImportCommand}"/>
</Grid>
<TextBox Text="{Binding NewImportPath}" PlaceholderText="Folder with ChilloutVR.exe"/>
<CheckBox IsChecked="{Binding NewMoveCache}" Content="Copy its cache too"/>
<TextBlock Text="The original is not modified." Classes="dim" FontSize="12" TextWrapping="Wrap"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right" Margin="0,12,0,0">
<Button Content="Cancel" Command="{Binding CancelNewCommand}" Classes="ghost"/>
<Button Content="Create install" Command="{Binding CreateInstallCommand}" Classes="accent"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
<Border IsVisible="{Binding ShowManage}" Background="#B0000000">
<Border Classes="card" Width="500" MaxHeight="680" VerticalAlignment="Center" HorizontalAlignment="Center" Background="{DynamicResource OverlayBg}" DataContext="{Binding ManagedMod}">
<ScrollViewer>
<StackPanel Spacing="12">
<StackPanel Spacing="10">
<TextBlock Text="{Binding Name}" FontSize="16" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Details}" Classes="dim" FontSize="12" TextWrapping="Wrap"/>
<TextBlock Text="Version" Classes="label"/>
<ComboBox ItemsSource="{Binding VersionChoices}" SelectedItem="{Binding InstallVersion}" IsEnabled="{Binding HasInstall}" HorizontalAlignment="Stretch"/>
<StackPanel Orientation="Horizontal" Spacing="10" IsVisible="{Binding HasSourceChoice}">
<TextBlock Text="Source" VerticalAlignment="Center" Classes="dim"/>
<ComboBox ItemsSource="{Binding SourceOptions}" SelectedItem="{Binding SelectedSource}" MinHeight="30" Padding="10,4"/>
</StackPanel>
<Grid ColumnDefinitions="150,*" ColumnSpacing="10">
<ComboBox ItemsSource="{x:Static vm:ModVM.Types}" SelectedItem="{Binding Type}" HorizontalAlignment="Stretch"/>
<TextBox Grid.Column="1" Text="{Binding AssetFilter}" PlaceholderText="Asset filter (optional)" IsVisible="{Binding !IsLocal}"/>
</Grid>
<TextBlock Text="Available versions" Classes="label"/>
<ListBox Classes="plain" ItemsSource="{Binding Releases}" SelectedItem="{Binding SelectedRelease}" MaxHeight="170">
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:ReleaseRow"><TextBlock Text="{Binding Text}" FontSize="12"/></DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Text="{Binding UsedBy}" Classes="dim" FontSize="12"/>
<WrapPanel>
<Button Content="Download selected" Command="{Binding DownloadCommand}" IsVisible="{Binding !IsLocal}" Margin="0,0,6,6"/>
<Button Content="Replace local files..." Command="{Binding AddLocalCommand}" IsVisible="{Binding IsLocal}" Margin="0,0,6,6"/>
<Button Content="Delete download" Command="{Binding DeleteDownloadCommand}" Margin="0,0,6,6"/>
<Button Content="Remove from catalog" Command="{Binding RemoveCommand}" IsEnabled="{Binding InCatalog}" Classes="ghost danger" Margin="0,0,6,6"/>
</WrapPanel>
</StackPanel>
<Button Content="Close" Command="{Binding Main.CloseManageCommand}" Classes="accent" HorizontalAlignment="Right"/>
</StackPanel>
</ScrollViewer>
</Border>
</Border>
<Border IsVisible="{Binding ShowSignIn}" Background="#B0000000">
<Border Classes="card" Width="440" VerticalAlignment="Center" HorizontalAlignment="Center" Background="{DynamicResource OverlayBg}">
<StackPanel Spacing="10">
<TextBlock Text="Sign in to Steam" Classes="h1" FontSize="20"/>
<TextBlock Text="{Binding SignInPrompt}" Classes="dim" TextWrapping="Wrap"/>
<TextBox Text="{Binding SignInUser}" PlaceholderText="Steam username" IsVisible="{Binding SignInNeedsPassword}"/>
<TextBox Text="{Binding SignInPassword}" PasswordChar="●" PlaceholderText="Password" IsVisible="{Binding SignInNeedsPassword}">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding SubmitSignInCommand}"/>
</TextBox.KeyBindings>
</TextBox>
<TextBox Text="{Binding SignInCode}" PlaceholderText="Steam Guard code" IsVisible="{Binding SignInNeedsCode}">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding SubmitSignInCommand}"/>
</TextBox.KeyBindings>
</TextBox>
<TextBlock Text="The password is not saved." Classes="dim" FontSize="12" TextWrapping="Wrap"/>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Content="Cancel" Command="{Binding CancelSignInCommand}" Classes="ghost"/>
<Button Content="Continue" Command="{Binding SubmitSignInCommand}" Classes="accent" IsVisible="{Binding SignInCanContinue}"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
<Border IsVisible="{Binding ShowCodeEntry}" Background="#B0000000">
<Border Classes="card" Width="440" VerticalAlignment="Center" HorizontalAlignment="Center" Background="{DynamicResource OverlayBg}">
<StackPanel Spacing="10">
<TextBlock Text="Enter beta code" Classes="h1" FontSize="20"/>
<TextBlock Text="{Binding CodeIntro}" Classes="dim" TextWrapping="Wrap"/>
<TextBox Text="{Binding CodeText}" PlaceholderText="Beta access code" IsEnabled="{Binding CodeIdle}">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding SubmitCodeCommand}"/>
</TextBox.KeyBindings>
</TextBox>
<TextBlock Text="{Binding CodeError}" IsVisible="{Binding HasCodeError}" Foreground="{DynamicResource DangerFg}" TextWrapping="Wrap"/>
<TextBlock Text="Checking..." IsVisible="{Binding CodeBusy}" Classes="dim"/>
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Content="Cancel" Command="{Binding CancelCodeCommand}" IsEnabled="{Binding CodeIdle}" Classes="ghost"/>
<Button Content="Check code" Command="{Binding SubmitCodeCommand}" IsEnabled="{Binding CodeIdle}" Classes="accent"/>
</StackPanel>
</StackPanel>
</Border>
</Border>
<Border IsVisible="{Binding ToastVisible}" Classes="card" Background="{DynamicResource ToastBg}" Padding="0" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="24" MinWidth="340" MaxWidth="480">
<Grid ColumnDefinitions="Auto,*,Auto">
<Border Width="4" CornerRadius="10,0,0,10" Background="{DynamicResource AccentBrush}" IsVisible="{Binding !ToastError}"/>
<Border Width="4" CornerRadius="10,0,0,10" Background="{DynamicResource DangerFg}" IsVisible="{Binding ToastError}"/>
<StackPanel Grid.Column="1" Spacing="8" Margin="14,12">
<TextBlock Text="{Binding Status}" TextWrapping="Wrap"/>
<ProgressBar Value="{Binding Progress}" Maximum="100" IsIndeterminate="{Binding Indeterminate}" Height="4" IsVisible="{Binding IsBusy}"/>
</StackPanel>
<StackPanel Grid.Column="2" VerticalAlignment="Center" Margin="0,0,8,0">
<Button Content="Cancel" Command="{Binding CancelCommand}" Classes="ghost" IsVisible="{Binding IsBusy}"/>
<Button Content="✕" Command="{Binding DismissToastCommand}" Classes="ghost" IsVisible="{Binding IsIdle}"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</Window>