Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 148 additions & 84 deletions SnipIT.ps1

Large diffs are not rendered by default.

172 changes: 164 additions & 8 deletions Test-SnipIT-Interactive.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,14 @@ Describe 'Per-monitor Smart overlay set' {
$overlay.DragRect.Stroke, $fixture.Services.Resources['SnipMintBrush']))
Should-BeTrue ($overlay.HintBorder.CornerRadius.TopLeft -gt 0)
Should-BeTrue ($overlay.LoupeBorder.CornerRadius.TopLeft -gt 0)
Should-Be $overlay.LoupeBorder.Child.CornerRadius.TopLeft 15
Should-Be $overlay.HintBorder.Child.CornerRadius.TopLeft 15
foreach ($highlightName in 'LoupeInnerHighlight','HintInnerHighlight') {
$highlight = $overlay.Window.FindName($highlightName)
Should-BeTrue ($highlight -is [System.Windows.Controls.Border])
Should-Be $highlight.Height 1
Should-Be $highlight.Background $fixture.Services.Resources['SnipInnerHighlightBrush']
}
$body = (Get-Command New-SnipOverlayWindow).ScriptBlock.ToString()
Should-BeFalse ($body -match '(?i)#(?:00)?78D4|cyan')
}
Expand Down Expand Up @@ -1881,18 +1889,81 @@ Describe 'Shared Optical Bench theme resources' {
Should-Be $resources['SnipCornerHeight'] 42
Should-Be $resources['SnipHairlineThickness'] 1
Should-Be $resources['SnipAccentBrush'].Color.ToString() '#FFD8C8A5'
Should-Be $resources['SnipGlassScrimBrush'].Color.ToString() '#CC000000'
Should-Be $resources['SnipGlassGradientBrush'].GradientStops[0].Color.ToString() '#260E1012'
Should-Be $resources['SnipGlassGradientBrush'].GradientStops[1].Color.ToString() '#14050608'
Should-Be $resources['SnipGlassScrimBrush'].Color.ToString() '#E60A0C0E'
Should-Be $resources['SnipHairlineBrush'].Color.ToString() '#80D8C8A5'
Should-Be $resources['SnipQuietBorderBrush'].Color.ToString() '#00FFFFFF'
Should-Be $resources['SnipInnerHighlightBrush'].Color.ToString() '#33F3EAD7'
Should-BeFalse ($resources['SnipInnerHighlightBrush'].Color.ToString() -eq
$resources['SnipHairlineBrush'].Color.ToString())
Should-Be $resources['SnipGlassGradientBrush'].GradientStops[0].Color.ToString() '#4050463B'
Should-Be $resources['SnipGlassGradientBrush'].GradientStops[1].Color.ToString() '#26000000'
}

It 'keeps secondary buttons quiet at rest and reserves champagne chrome for primary actions' {
$resources = New-SnipThemeResources -HighContrast:$false -AnimationsEnabled:$false
$secondary = [System.Windows.Controls.Button]::new()
$primary = [System.Windows.Controls.Button]::new()
Add-SnipThemeResources -Root $secondary -Resources $resources | Out-Null
Add-SnipThemeResources -Root $primary -Resources $resources | Out-Null
$secondary.Style = $resources['SnipButtonStyle']
$primary.Style = $resources['SnipPrimaryButtonStyle']

Should-Be $secondary.Background.Color.ToString() '#00FFFFFF'
Should-Be $secondary.BorderBrush.Color.ToString() '#00FFFFFF'
Should-Be $secondary.BorderThickness.Left 1
Should-Be $primary.Background.Color.ToString() '#2ED8C8A5'
Should-Be $primary.BorderBrush.Color.ToString() '#FFD8C8A5'
}

It 'keeps quiet secondary and split boundaries visible in High Contrast' {
$resources = New-SnipThemeResources -HighContrast:$true -AnimationsEnabled:$false
Should-Be $resources['SnipQuietBorderBrush'].Color.ToString() `
([System.Windows.SystemColors]::WindowTextColor.ToString())

$window = [System.Windows.Window]::new()
$window.ShowInTaskbar = $false
$window.WindowStartupLocation = 'Manual'
$window.Left = -10000
$window.Top = -10000
Add-SnipThemeResources -Root $window -Resources $resources | Out-Null
$button = [System.Windows.Controls.Button]::new()
$button.Style = $resources['SnipButtonStyle']
$context = [pscustomobject]@{
Resources = $resources
Window = $window
RegisterWindow = { param($hwnd) }
UnregisterWindow = { param($hwnd) }
GetKeyboardModifiers = { [System.Windows.Input.ModifierKeys]::None }
TransientMenus = [System.Collections.ArrayList]::new()
}
$split = New-SnipSplitControl -Name ReviewProbe -DefaultCommand Probe `
-PrimaryText 'Probe' -PrimaryAutomationName 'Probe action' `
-OptionsAutomationName 'Probe options' -Options ([ordered]@{ Probe = {} }) `
-Context $context -PrimaryWidth 56 -OptionsWidth 24
$panel = [System.Windows.Controls.StackPanel]::new()
$panel.Children.Add($button) | Out-Null
$panel.Children.Add($split.Root) | Out-Null
$window.Content = $panel
try {
$window.Show()
$window.UpdateLayout()
Should-Be $button.BorderBrush.Color.ToString() `
([System.Windows.SystemColors]::WindowTextColor.ToString())
Should-Be $split.Root.BorderBrush.Color.ToString() `
([System.Windows.SystemColors]::WindowTextColor.ToString())
} finally {
$window.Close()
}
}

It 'publishes the exact layered glass contract with restrained depth' {
$resources = New-SnipThemeResources -HighContrast:$false -AnimationsEnabled:$true

Should-Be ($resources['SnipGlassLayers'] -join ',') 'ContrastScrim,GlassGradient,Hairline,InnerHighlight,Shadow'
Should-BeTrue ($resources['SnipIslandShadow'] -is [System.Windows.Media.Effects.DropShadowEffect])
Should-Be $resources['SnipIslandShadow'].BlurRadius 18
Should-Be $resources['SnipIslandShadow'].ShadowDepth 5
Should-Be $resources['SnipIslandShadow'].BlurRadius 26
Should-Be $resources['SnipIslandShadow'].ShadowDepth 8
Should-Be $resources['SnipIslandShadow'].Opacity 0.68
Should-Be $resources['SnipDisplayFont'].Source 'Segoe UI Variable Display'
Should-Be $resources['SnipTextFont'].Source 'Segoe UI Variable Text'
Should-Be $resources['SnipCodeFont'].Source 'Cascadia Code'
Expand Down Expand Up @@ -2124,6 +2195,10 @@ Describe 'Settings utility surface' {
Should-Be $kit.ErrorText.Text ''
Should-BeFalse ([string]::IsNullOrWhiteSpace([string]$kit.ShortcutRecorder.ToolTip))
Should-BeFalse ([string]::IsNullOrWhiteSpace([string]$kit.Window.FindName('SaveBtn').ToolTip))
Should-Be $kit.Window.FindName('GlassOuter').CornerRadius.TopLeft 16
Should-Be $kit.Window.FindName('GlassSurface').CornerRadius.TopLeft 15
Should-Be $kit.ShortcutRecorder.BorderBrush $kit.Resources['SnipHairlineBrush']
Should-Be $kit.ShortcutRecorder.Background $kit.Resources['SnipCanvasBrush']
}

Should-Be $context.RegisteredHotkey.VirtualKey 0x52
Expand Down Expand Up @@ -2377,6 +2452,10 @@ Describe 'About utility surface' {
Should-Be $kit.Metadata.ActiveShortcut 'Ctrl+Alt+Shift+Q'
Should-Be $kit.Window.Title 'About SnipIT'
Should-BeFalse ([string]::IsNullOrWhiteSpace([string]$kit.Window.FindName('CloseBtn').ToolTip))
Should-Be $kit.Window.FindName('GlassOuter').CornerRadius.TopLeft 16
Should-Be $kit.Window.FindName('GlassSurface').CornerRadius.TopLeft 15
Should-Be $kit.Window.FindName('HeaderCloseBtn').BorderBrush.Color.ToString() `
([System.Windows.Media.Brushes]::Transparent.Color.ToString())
}
}
}
Expand Down Expand Up @@ -2421,6 +2500,11 @@ Describe 'Optional floating capture widget' {
Show-FloatingWidget -Context $context -TestAction {
param($kit)
Should-Be ($kit.Order -join ',') 'Smart,Full,Window,Settings'
Should-Be $kit.Window.FindName('GlassOuter').CornerRadius.TopLeft 16
Should-Be $kit.Window.FindName('GlassSurface').CornerRadius.TopLeft 15
$innerHighlight = $kit.Window.FindName('WidgetInnerHighlight')
Should-Be $innerHighlight.Height 1
Should-Be $innerHighlight.Background $kit.Resources['SnipInnerHighlightBrush']
foreach ($button in $kit.Buttons.Values) {
Should-BeFalse ([string]::IsNullOrWhiteSpace([string]$button.ToolTip))
}
Expand Down Expand Up @@ -2450,7 +2534,7 @@ Describe 'Owner-drawn tray presentation' {
Should-BeTrue ($menu -is [System.Windows.Forms.ContextMenuStrip])
Should-BeTrue ($menu.Renderer -is [System.Windows.Forms.ToolStripProfessionalRenderer])
Should-Be $menu.Tag.Palette.PageBlack '#030405'
Should-Be $menu.Tag.Palette.PrimaryText '#F2F5F7'
Should-Be $menu.Tag.Palette.PrimaryText '#F5F1E8'
Should-Be $menu.Tag.Palette.Accent '#D8C8A5'
Should-Be ($menu.Tag.PrimaryOrder -join ',') 'Smart,Full,Window,Settings,About,Exit'
Should-Be ($menu.Tag.Order -join ',') 'Smart,Full,Window,Separator1,Delay,Settings,Widget,OpenFolder,Separator2,About,Uninstall,Exit'
Expand Down Expand Up @@ -2982,7 +3066,7 @@ Describe 'Floating Studio copy completion contract' {
[System.Windows.RoutedEventArgs]::new(
[System.Windows.Controls.Primitives.ButtonBase]::ClickEvent))
$wasOpen.Value = $kit.Win.IsVisible
$wasOpen.Status = $kit.StatusIsland.Child.Children[1].Text
$wasOpen.Status = $kit.Win.FindName('StatusText').Text
}.GetNewClosure()
Should-Be $result 'UserCancelled'
Should-BeTrue $wasOpen.Value
Expand Down Expand Up @@ -4066,7 +4150,9 @@ $null = Show-PreviewWindow -Bitmap $bmp -TestAction {
$intersection = [System.Windows.Rect]::Intersect($left, $right)
if (-not $intersection.IsEmpty -and
$intersection.Width -gt 0 -and $intersection.Height -gt 0) {
throw "Floating Studio islands overlap at ${Width}x${Height}: $($islandNames[$leftIndex]) and $($islandNames[$rightIndex])"
throw ("Floating Studio islands overlap at ${Width}x${Height}: " +
"$($islandNames[$leftIndex]) $left and " +
"$($islandNames[$rightIndex]) $right")
}
}
}
Expand Down Expand Up @@ -4106,11 +4192,63 @@ $null = Show-PreviewWindow -Bitmap $bmp -TestAction {
Should-Be $kit.ActionsIsland.Height 42
Should-Be $kit.ViewportIsland.Height 42
Should-Be $kit.StatusIsland.Height 42
Should-Be $kit.BrandIsland.Width 292
Should-Be $kit.ViewportIsland.Width 216
Should-Be $kit.ActionsIsland.CornerRadius.TopLeft 16
Should-Be $kit.ViewportIsland.CornerRadius.TopLeft 16
Should-Be $kit.StatusIsland.CornerRadius.TopLeft 16
Should-Be $kit.PropertyIsland.Height 54
Should-Be $kit.ToolDock.Height 51
foreach ($island in @(
$kit.BrandIsland,$kit.ActionsIsland,$kit.PropertyIsland,
$kit.ToolDock,$kit.ViewportIsland,$kit.StatusIsland)) {
$innerSurface = $island.Child.Children[0]
Should-Be $innerSurface.CornerRadius.TopLeft 15
Should-Be $innerSurface.CornerRadius.BottomRight 15
}
}

It 'uses consistent safe margins and keeps compact viewport controls above the dock' {
& $kit.SetResponsiveMode 1200 700
Should-Be $kit.BrandIsland.Margin.Left 28
Should-Be $kit.BrandIsland.Margin.Top 24
Should-Be $kit.ActionsIsland.Margin.Right 28
Should-Be $kit.ActionsIsland.Margin.Top 24
Should-Be $kit.ToolDock.Margin.Bottom 28
Should-Be $kit.PropertyIsland.Margin.Bottom 90
Should-Be $kit.ViewportIsland.Margin.Left 28
Should-Be $kit.ViewportIsland.Margin.Bottom 28
Should-Be $kit.StatusIsland.Margin.Right 28
Should-Be $kit.StatusIsland.Margin.Bottom 28

& $kit.SetResponsiveMode 900 600
Should-Be $kit.ViewportIsland.Margin.Bottom 92
}

It 'keeps inactive Preview controls and split containers borderless at rest' {
$transparent = [System.Windows.Media.Brushes]::Transparent.Color.ToString()
Should-Be $kit.Win.FindName('SaveBtn').BorderBrush.Color.ToString() $transparent
Should-Be $kit.Win.FindName('HighlightBtn').BorderBrush.Color.ToString() $transparent
Should-Be $kit.Context.Shell.MoreButton.BorderBrush.Color.ToString() $transparent
Should-Be $kit.SplitControls.ArrowLine.Root.BorderBrush.Color.ToString() $transparent
Should-Be $kit.SplitControls.ArrowLine.Root.Background.Color.ToString() $transparent
Should-Be $kit.Context.ToolControls.Select.BorderBrush `
$kit.Resources['SnipAccentBrush']
}

It 'uses a compact brand mark, restrained status indicator, and legible tool glyphs' {
$brandMark = $kit.Win.FindName('BrandMark')
$statusIndicator = $kit.Win.FindName('StatusIndicator')
Should-BeTrue ($brandMark -is [System.Windows.Controls.Border])
Should-Be $brandMark.Width 20
Should-Be $brandMark.CornerRadius.TopLeft 6
Should-Be $brandMark.BorderBrush $kit.Resources['SnipAccentBrush']
Should-BeTrue ($statusIndicator -is [System.Windows.Shapes.Ellipse])
Should-Be $statusIndicator.Fill $kit.Resources['SnipAccentBrush']
Should-Be $kit.Context.ToolControls.Select.FontSize 15
Should-Be $kit.Context.ToolControls.Select.FontFamily.Source 'Segoe UI Symbol'
Should-Be $kit.SplitControls.ArrowLine.PrimaryButton.FontSize 15
Should-Be $kit.SplitControls.ArrowLine.PrimaryButton.FontFamily.Source 'Segoe UI Symbol'
}

It 'orders the top actions correctly and keeps Copy and close primary' {
Expand Down Expand Up @@ -4190,6 +4328,18 @@ $null = Show-PreviewWindow -Bitmap $bmp -TestAction {
& $kit.SetPropertyIsland 'Select'
}

It 'measures synthetic Wide layout independently of the host display width' {
$previousMaxWidth = $kit.Win.MaxWidth
try {
$kit.Win.MaxWidth = 1024
& $assertFloatingStudioLayout 1200 700 'Wide' @(
'Select','Crop','Pen','Highlight','ArrowLine','RectangleEllipse',
'Text','Steps','BlurPixelate','Color','Undo','Redo')
} finally {
$kit.Win.MaxWidth = $previousMaxWidth
}
}

It 'selects Wide at 1200 by 700 without island overlap' {
& $assertFloatingStudioLayout 1200 700 'Wide' @(
'Select','Crop','Pen','Highlight','ArrowLine','RectangleEllipse',
Expand Down Expand Up @@ -4361,6 +4511,9 @@ $null = Show-PreviewWindow -Bitmap $bmp -TestAction {
& $kit.SetResponsiveMode 760 540
& $kit.ClearStatus
Should-Be $kit.StatusIsland.Width 42
Should-Be $kit.Context.Shell.StatusIndicator.Visibility `
([System.Windows.Visibility]::Collapsed)
Should-Be $kit.Context.Shell.StatusText.Text '●'
Should-Be $kit.Context.StatusState.Kind 'Idle'
& $kit.SetStatus 'Saved to disk' 'Success'
Should-BeTrue ($kit.StatusIsland.Width -ge 150)
Expand All @@ -4369,6 +4522,9 @@ $null = Show-PreviewWindow -Bitmap $bmp -TestAction {
& $kit.ClearStatus
Should-Be $kit.StatusIsland.Width 42
Should-Be $kit.Context.StatusState.Kind 'Idle'
& $kit.SetResponsiveMode 1200 700
Should-Be $kit.Context.Shell.StatusIndicator.Visibility `
([System.Windows.Visibility]::Visible)
}

It 'keeps actual Compact clipboard error and success status collision-safe and fully accessible' {
Expand Down
13 changes: 7 additions & 6 deletions Test-SnipIT.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -745,10 +745,11 @@ Describe 'Approved theme and settings contracts'
It 'returns every locked black champagne token' {
$tokens = Get-SnipThemeTokens
$expected = [ordered]@{
PageBlack = '#030405'; CanvasBlack = '#090A0C'; GlassScrim = '#CC000000'
GlassTop = '#260E1012'; GlassBottom = '#14050608'
PrimaryText = '#F2F5F7'; SecondaryText = '#C0C5CA'; MutedText = '#9EA4AA'
Hairline = '#59FFFFFF'; HoverFill = '#1FFFFFFF'; Accent = '#D8C8A5'
PageBlack = '#030405'; CanvasBlack = '#080A0C'; GlassScrim = '#E60A0C0E'
GlassTop = '#4050463B'; GlassBottom = '#26000000'
PrimaryText = '#F5F1E8'; SecondaryText = '#C7C2B8'; MutedText = '#938E84'
Hairline = '#80D8C8A5'; InnerHighlight = '#33F3EAD7'
HoverFill = '#24D8C8A5'; Accent = '#D8C8A5'
AccentText = '#F3EAD7'; Coral = '#FF8069'; Mint = '#A8EFD7'
Amber = '#FFD36B'; Violet = '#BBA1FF'
IslandRadius = 16; ControlRadius = 10; CornerIslandHeight = 42
Expand All @@ -760,8 +761,8 @@ It 'returns every locked black champagne token' {
}
It 'meets the locked worst-case contrast ratios' {
$tokens = Get-SnipThemeTokens
ShouldBe (Get-SnipContrastRatio -Foreground $tokens.SecondaryText -Background '#2F2F30') 7.69
ShouldBe (Get-SnipContrastRatio -Foreground $tokens.Hairline -Background '#2F2F30') 3.01
ShouldBe (Get-SnipContrastRatio -Foreground $tokens.SecondaryText -Background '#2F2F30') 7.54
ShouldBe (Get-SnipContrastRatio -Foreground $tokens.Hairline -Background '#2F2F30') 3.22
}
It 'rejects malformed contrast colors' {
ShouldThrowType -Body {
Expand Down
9 changes: 5 additions & 4 deletions src/00-Core.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1250,10 +1250,11 @@ function Invoke-CaptureLoop {

function Get-SnipThemeTokens {
[pscustomobject][ordered]@{
PageBlack = '#030405'; CanvasBlack = '#090A0C'; GlassScrim = '#CC000000'
GlassTop = '#260E1012'; GlassBottom = '#14050608'
PrimaryText = '#F2F5F7'; SecondaryText = '#C0C5CA'; MutedText = '#9EA4AA'
Hairline = '#59FFFFFF'; HoverFill = '#1FFFFFFF'; Accent = '#D8C8A5'
PageBlack = '#030405'; CanvasBlack = '#080A0C'; GlassScrim = '#E60A0C0E'
GlassTop = '#4050463B'; GlassBottom = '#26000000'
PrimaryText = '#F5F1E8'; SecondaryText = '#C7C2B8'; MutedText = '#938E84'
Hairline = '#80D8C8A5'; InnerHighlight = '#33F3EAD7'
HoverFill = '#24D8C8A5'; Accent = '#D8C8A5'
AccentText = '#F3EAD7'; Coral = '#FF8069'; Mint = '#A8EFD7'
Amber = '#FFD36B'; Violet = '#BBA1FF'
IslandRadius = 16; ControlRadius = 10; CornerIslandHeight = 42
Expand Down
Loading
Loading