Scene strip: move top row over the preview column and scale it; remove CRT bezel art and on-air seed
- PreviewPane.xaml: drop the OBS-style border Rectangle/Ellipse over webcam elements - SceneThumbnailStrip: Viewbox scales the row; height follows width so it fills the preview column; staged+live tile has no border - MainWindow.xaml: strip moved to column 1 only, leaving the layers cell empty; Margin 8,0 matches the preview - MainViewModel.Background: stop seeding the CRT scene tiles and the on-air image - ytLive.csproj: per-build counter (BuildCounter.txt, gitignored) replaces the commit-count stamp Build stamp now increments on every build.
This commit is contained in:
@@ -20,3 +20,4 @@ MARCOM.md
|
||||
CREDENTIALS.md
|
||||
.aider*
|
||||
ffmpeg/
|
||||
BuildCounter.txt
|
||||
|
||||
@@ -180,49 +180,6 @@
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
|
||||
<!-- OBS-style border: stroked rect (Traditional) / centered circle (Round).
|
||||
Animations are driven by code-behind in WebcamMenu_SetBorderAnimation
|
||||
because DataTrigger storyboards in DataTemplates cannot be stopped
|
||||
(no ExitActions namescope support). -->
|
||||
<Rectangle IsHitTestVisible="False"
|
||||
Stroke="{Binding BorderBrush}"
|
||||
StrokeThickness="{Binding BorderWidth}">
|
||||
<Rectangle.Style>
|
||||
<Style TargetType="Rectangle">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
||||
<Condition Binding="{Binding ClipShape}" Value="Traditional"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Rectangle.Style>
|
||||
</Rectangle>
|
||||
<Ellipse IsHitTestVisible="False"
|
||||
Stroke="{Binding BorderBrush}"
|
||||
StrokeThickness="{Binding BorderWidth}"
|
||||
Width="{Binding RoundBorderSize}"
|
||||
Height="{Binding RoundBorderSize}"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Ellipse.Style>
|
||||
<Style TargetType="Ellipse">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding HasBorder}" Value="True"/>
|
||||
<Condition Binding="{Binding ClipShape}" Value="Round"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Ellipse.Style>
|
||||
</Ellipse>
|
||||
|
||||
<Grid.ContextMenu>
|
||||
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<UserControl x:Class="ytLive.Controls.SceneThumbnailStrip"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Border Background="#0d1117" CornerRadius="4" Margin="0,0,0,4" Padding="4">
|
||||
<Viewbox Stretch="Uniform">
|
||||
<Border Width="1000" Height="150" Background="#0d1117" CornerRadius="4" Margin="0,0,0,4" Padding="4">
|
||||
<ItemsControl ItemsSource="{Binding Scenes}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
@@ -25,6 +26,13 @@
|
||||
<DataTrigger Binding="{Binding IsLive}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="#22c55e"/>
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsStaged}" Value="True"/>
|
||||
<Condition Binding="{Binding IsLive}" Value="True"/>
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="BorderBrush" Value="Transparent"/>
|
||||
</MultiDataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
@@ -51,4 +59,5 @@
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</Border>
|
||||
</Viewbox>
|
||||
</UserControl>
|
||||
@@ -8,9 +8,12 @@ namespace ytLive.Controls;
|
||||
|
||||
public partial class SceneThumbnailStrip : UserControl
|
||||
{
|
||||
private const double DesignAspect = 154.0 / 1000.0;
|
||||
|
||||
public SceneThumbnailStrip()
|
||||
{
|
||||
InitializeComponent();
|
||||
SizeChanged += (_, _) => Height = ActualWidth * DesignAspect;
|
||||
}
|
||||
|
||||
private void Thumbnail_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
|
||||
+2
-2
@@ -78,8 +78,8 @@
|
||||
<ColumnDefinition Width="3*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- ═══ THUMBNAIL STRIP ═══ -->
|
||||
<controls:SceneThumbnailStrip Grid.Row="0" Grid.ColumnSpan="2"/>
|
||||
<!-- ═══ THUMBNAIL STRIP (over the preview column; left column stays empty) ═══ -->
|
||||
<controls:SceneThumbnailStrip Grid.Row="0" Grid.Column="1" Margin="8,0"/>
|
||||
|
||||
<!-- LEFT PANEL: one panel, two states.
|
||||
Offline (edit mode): LAYERS + PROPERTIES
|
||||
|
||||
@@ -441,7 +441,6 @@ public partial class MainViewModel
|
||||
if (background != null)
|
||||
StampDefaultBackgroundAsset(background, scene.Name);
|
||||
}
|
||||
SeedSceneThumbnails();
|
||||
SeedOnAirImage();
|
||||
OnPropertyChanged(nameof(ShowSourcesEmptyHint));
|
||||
}
|
||||
@@ -452,45 +451,14 @@ public partial class MainViewModel
|
||||
// renders (each scene's original background seen through a CRT screen). They
|
||||
// are seeded into the layout's Asset table once per scene; the preview pane
|
||||
// keeps rendering each scene's ORIGINAL background — never these tiles.
|
||||
internal static readonly Dictionary<string, string> SceneThumbnailSeedPaths = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[SceneCatalog.Starting] = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_uvq3i3uvq3i3uvq3.jpg",
|
||||
[SceneCatalog.Live] = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_y27sgqy27sgqy27s.jpg",
|
||||
[SceneCatalog.Brb] = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_lxcq0klxcq0klxcq.jpg",
|
||||
[SceneCatalog.Chat] = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_nqosdenqosdenqos.jpg",
|
||||
[SceneCatalog.Ending] = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_8je5n68je5n68je5.jpg",
|
||||
};
|
||||
|
||||
private void SeedSceneThumbnails()
|
||||
{
|
||||
var imported = false;
|
||||
foreach (var scene in Scenes)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(scene.ThumbnailAssetId)) continue;
|
||||
if (!SceneThumbnailSeedPaths.TryGetValue(scene.Name, out var path)) continue;
|
||||
if (!System.IO.File.Exists(path)) continue;
|
||||
try
|
||||
{
|
||||
// UpsertAsset is SHA256-keyed, so a re-seed never duplicates rows
|
||||
// and returns the same id. Only the Scene mapping is new state.
|
||||
var assetId = AddAsset(System.IO.File.ReadAllBytes(path));
|
||||
if (assetId == null) continue;
|
||||
scene.ThumbnailAssetId = assetId;
|
||||
imported = true;
|
||||
}
|
||||
catch { /* the strip keeps the scene's background-art fallback */ }
|
||||
}
|
||||
if (!imported) return;
|
||||
RefreshSnapshotsAsync();
|
||||
ScheduleSave();
|
||||
}
|
||||
internal static readonly Dictionary<string, string> SceneThumbnailSeedPaths = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// ─── On-air strip tile (creator ruling 2026-10-10) ───
|
||||
// A single shared image ("on-air background screen") labels whichever scene is
|
||||
// CURRENTLY PREVIEWED in the top bar — it replaces that scene's tile and jumps
|
||||
// around as the creator swaps scenes. Stored as an Asset row like any image,
|
||||
// tracked by the OnAirImageAssetId setting so the Save pruner keeps it alive.
|
||||
internal static string OnAirSeedPath = @"C:\Users\gramp\Downloads\Gemini_Generated_Image_4zajyl4zajyl4zaj.jpg";
|
||||
internal static string OnAirSeedPath = string.Empty;
|
||||
|
||||
private System.Windows.Media.Imaging.BitmapImage? OnAirImage;
|
||||
|
||||
|
||||
+8
-2
@@ -40,7 +40,7 @@
|
||||
Baseline 241 = count at introduction, first display 13. Missing git or unparsed output
|
||||
falls back to the start (build never breaks). GUID + compile time stay in startup.log,
|
||||
where build-identity forensics actually live. -->
|
||||
<BuildNumberBaseline>241</BuildNumberBaseline>
|
||||
<BuildNumberBaseline>17</BuildNumberBaseline>
|
||||
<BuildNumberStart>13</BuildNumberStart>
|
||||
</PropertyGroup>
|
||||
<Exec Command=""C:\Program Files\Git\cmd\git.exe" rev-list --count HEAD"
|
||||
@@ -49,7 +49,13 @@
|
||||
</Exec>
|
||||
<PropertyGroup>
|
||||
<CommitCount>$(CommitCountRaw.Trim())</CommitCount>
|
||||
<BuildNumber>$([MSBuild]::Add($(BuildNumberStart), $([MSBuild]::Subtract($(CommitCount), $(BuildNumberBaseline)))))</BuildNumber>
|
||||
<BuildCounterFile>$(ProjectDir)BuildCounter.txt</BuildCounterFile>
|
||||
<PriorBuildCount Condition="Exists('$(BuildCounterFile)')">$([System.IO.File]::ReadAllText('$(BuildCounterFile)').Trim())</PriorBuildCount>
|
||||
<PriorBuildCount Condition="'$(PriorBuildCount)' == ''">423</PriorBuildCount>
|
||||
<BuildNumber>$([MSBuild]::Add($(PriorBuildCount), 1))</BuildNumber>
|
||||
</PropertyGroup>
|
||||
<WriteLinesToFile File="$(BuildCounterFile)" Lines="$(BuildNumber)" Overwrite="true" WriteOnlyWhenDifferent="false" />
|
||||
<PropertyGroup>
|
||||
<BuildNumber Condition="! $([System.Text.RegularExpressions.Regex]::IsMatch('$(BuildNumber)', '^[0-9]+$'))">$(BuildNumberStart)</BuildNumber>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user