oc-tweaks 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/index.js +156 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,6 +89,11 @@ Customize the appearance of the WPF notification window.
|
|
|
89
89
|
| `shadow` | `true` | Enable or disable the drop shadow effect. |
|
|
90
90
|
| `idleColor` | `"#4ADE80"` | Accent color for idle (success) notifications. |
|
|
91
91
|
| `errorColor` | `"#EF4444"` | Accent color for error notifications. |
|
|
92
|
+
| `fadeOut` | `true` | Animate opacity from `initialOpacity` to `finalOpacity` over `duration`. |
|
|
93
|
+
| `initialOpacity` | `0.85` | Window opacity at start (0–1). |
|
|
94
|
+
| `finalOpacity` | `0.05` | Window opacity at end before auto-close (0–1). |
|
|
95
|
+
| `clickThrough` | `true` | Mouse clicks pass through the window (WS_EX_TRANSPARENT). Hover over the ✕ zone to dismiss. |
|
|
96
|
+
| `hoverDismissMs` | `400` | Milliseconds the cursor must dwell on the ✕ zone to dismiss the notification. |
|
|
92
97
|
|
|
93
98
|
#### `notify.toolCall` (Windows WPF)
|
|
94
99
|
|
|
@@ -305,6 +310,11 @@ bunx oc-tweaks init
|
|
|
305
310
|
| `shadow` | `true` | 启用或禁用下拉阴影效果。 |
|
|
306
311
|
| `idleColor` | `"#4ADE80"` | 空闲 (成功) 通知的强调色。 |
|
|
307
312
|
| `errorColor` | `"#EF4444"` | 错误通知的强调色。 |
|
|
313
|
+
| `fadeOut` | `true` | 在 `duration` 时间内从 `initialOpacity` 渐隐到 `finalOpacity`。 |
|
|
314
|
+
| `initialOpacity` | `0.85` | 窗口初始不透明度 (0–1)。 |
|
|
315
|
+
| `finalOpacity` | `0.05` | 自动关闭前的最终不透明度 (0–1)。 |
|
|
316
|
+
| `clickThrough` | `true` | 鼠标点击穿透窗口 (WS_EX_TRANSPARENT)。将鼠标悬停在 ✕ 区域即可关闭。 |
|
|
317
|
+
| `hoverDismissMs` | `400` | 光标在 ✕ 区域停留多少毫秒后关闭通知。 |
|
|
308
318
|
|
|
309
319
|
#### `notify.toolCall` (Windows WPF)
|
|
310
320
|
|
package/dist/index.js
CHANGED
|
@@ -662,6 +662,11 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
662
662
|
const shadow = style?.shadow !== false;
|
|
663
663
|
const idleColor = style?.idleColor ?? "#4ADE80";
|
|
664
664
|
const errorColor = style?.errorColor ?? "#EF4444";
|
|
665
|
+
const fadeOut = style?.fadeOut !== false;
|
|
666
|
+
const initialOpacity = style?.initialOpacity ?? 0.85;
|
|
667
|
+
const finalOpacity = style?.finalOpacity ?? 0.05;
|
|
668
|
+
const clickThrough = style?.clickThrough !== false;
|
|
669
|
+
const hoverDismissMs = style?.hoverDismissMs ?? 400;
|
|
665
670
|
const contentMaxWidth = Math.max(160, width - 120);
|
|
666
671
|
const accentColor = renderOptions?.accentColor ?? (tag === "Error" ? errorColor : idleColor);
|
|
667
672
|
const icon = renderOptions?.icon ?? (tag === "Error" ? "❌" : "✅");
|
|
@@ -677,8 +682,19 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
677
682
|
const messageLimit = typeof renderOptions?.maxMessageLength === "number" && renderOptions.maxMessageLength > 0 ? renderOptions.maxMessageLength : 400;
|
|
678
683
|
const normalizedMessage = renderOptions?.preserveMessageFormatting ? message : cleanMarkdown(message);
|
|
679
684
|
const textB64 = escapeForPowerShell(truncateText(normalizedMessage, messageLimit));
|
|
680
|
-
const shadowXaml = shadow ? '<Border.Effect><DropShadowEffect BlurRadius="
|
|
681
|
-
const
|
|
685
|
+
const shadowXaml = shadow ? '<Border.Effect><DropShadowEffect BlurRadius="16" ShadowDepth="0" Opacity="0.4" Color="Black"/></Border.Effect>' : "";
|
|
686
|
+
const closeBtnXaml = clickThrough ? [
|
|
687
|
+
` <Border Name="CloseBg" CornerRadius="4" Width="22" Height="22"`,
|
|
688
|
+
` HorizontalAlignment="Right" VerticalAlignment="Top"`,
|
|
689
|
+
` Margin="0,6,6,0" Background="Transparent">`,
|
|
690
|
+
` <TextBlock Name="CloseBtn" Text="✕" FontSize="13"`,
|
|
691
|
+
` Foreground="#2A2A3A"`,
|
|
692
|
+
` HorizontalAlignment="Center" VerticalAlignment="Center"`,
|
|
693
|
+
` Margin="0,-1,0,0"/>`,
|
|
694
|
+
` </Border>`
|
|
695
|
+
].join(`
|
|
696
|
+
`) : "";
|
|
697
|
+
const dismissHintXaml = showDismissHint && !clickThrough ? ` <TextBlock Text="Click to dismiss" Foreground="#555555" FontSize="9" Margin="0,4,0,0"/>` : "";
|
|
682
698
|
const xaml = [
|
|
683
699
|
`<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"`,
|
|
684
700
|
` xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"`,
|
|
@@ -693,7 +709,7 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
693
709
|
` ${shadowXaml}`,
|
|
694
710
|
` <Grid>`,
|
|
695
711
|
` <Border CornerRadius="${borderRadius},0,0,${borderRadius}" Width="${colorBarWidth}" HorizontalAlignment="Left" Name="ColorBar"/>`,
|
|
696
|
-
` <StackPanel Orientation="Horizontal" Margin="22,12
|
|
712
|
+
` <StackPanel Orientation="Horizontal" Margin="22,12,${clickThrough ? 50 : 16},12" VerticalAlignment="Center">`,
|
|
697
713
|
` <TextBlock Name="IconText" FontSize="${iconFontSize}" VerticalAlignment="Center" Margin="0,0,15,0" Foreground="White"/>`,
|
|
698
714
|
` <StackPanel VerticalAlignment="Center" MaxWidth="${contentMaxWidth}">`,
|
|
699
715
|
` <TextBlock Name="TitleText" FontSize="${titleFontSize}" FontWeight="SemiBold" TextWrapping="Wrap"/>`,
|
|
@@ -701,6 +717,7 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
701
717
|
dismissHintXaml,
|
|
702
718
|
` </StackPanel>`,
|
|
703
719
|
` </StackPanel>`,
|
|
720
|
+
closeBtnXaml,
|
|
704
721
|
` </Grid>`,
|
|
705
722
|
` </Border>`,
|
|
706
723
|
`</Window>`
|
|
@@ -711,13 +728,55 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
711
728
|
`$window.Left = ${resolvedPosition.leftExpr}`,
|
|
712
729
|
`$window.Top = ${resolvedPosition.topExpr}`
|
|
713
730
|
] : [];
|
|
714
|
-
const
|
|
715
|
-
"Add-Type -
|
|
716
|
-
"
|
|
717
|
-
"
|
|
718
|
-
"
|
|
719
|
-
"
|
|
731
|
+
const win32TypeDef = clickThrough ? [
|
|
732
|
+
"Add-Type -TypeDefinition @'",
|
|
733
|
+
"using System;",
|
|
734
|
+
"using System.Runtime.InteropServices;",
|
|
735
|
+
"",
|
|
736
|
+
"[StructLayout(LayoutKind.Sequential)]",
|
|
737
|
+
"public struct RECT { public int Left, Top, Right, Bottom; }",
|
|
738
|
+
"",
|
|
739
|
+
"[StructLayout(LayoutKind.Sequential)]",
|
|
740
|
+
"public struct POINT { public int X, Y; }",
|
|
741
|
+
"",
|
|
742
|
+
"public static class VDesktop {",
|
|
743
|
+
' [DllImport("user32.dll", SetLastError = true)]',
|
|
744
|
+
" public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);",
|
|
745
|
+
"",
|
|
746
|
+
' [DllImport("user32.dll", SetLastError = true)]',
|
|
747
|
+
" public static extern int GetWindowLong(IntPtr hWnd, int nIndex);",
|
|
720
748
|
"",
|
|
749
|
+
' [DllImport("user32.dll")]',
|
|
750
|
+
" public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);",
|
|
751
|
+
"",
|
|
752
|
+
' [DllImport("user32.dll")]',
|
|
753
|
+
" public static extern bool GetCursorPos(out POINT lpPoint);",
|
|
754
|
+
"",
|
|
755
|
+
" public const int GWL_EXSTYLE = -20;",
|
|
756
|
+
" public const int WS_EX_TOOLWINDOW = 0x00000080;",
|
|
757
|
+
" public const int WS_EX_NOACTIVATE = 0x08000000;",
|
|
758
|
+
" public const int WS_EX_APPWINDOW = 0x00040000;",
|
|
759
|
+
" public const int WS_EX_TRANSPARENT = 0x00000020;",
|
|
760
|
+
"",
|
|
761
|
+
" public static void MakeGlobalWindow(IntPtr hwnd, bool transparent) {",
|
|
762
|
+
" int style = GetWindowLong(hwnd, GWL_EXSTYLE);",
|
|
763
|
+
" style = style | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE;",
|
|
764
|
+
" style = style & ~WS_EX_APPWINDOW;",
|
|
765
|
+
" if (transparent) { style = style | WS_EX_TRANSPARENT; }",
|
|
766
|
+
" else { style = style & ~WS_EX_TRANSPARENT; }",
|
|
767
|
+
" SetWindowLong(hwnd, GWL_EXSTYLE, style);",
|
|
768
|
+
" }",
|
|
769
|
+
"",
|
|
770
|
+
" public static bool IsCursorInTopRight(IntPtr hwnd, int zoneW, int zoneH) {",
|
|
771
|
+
" RECT r; POINT p;",
|
|
772
|
+
" if (!GetWindowRect(hwnd, out r)) return false;",
|
|
773
|
+
" if (!GetCursorPos(out p)) return false;",
|
|
774
|
+
" return (p.X >= r.Right - zoneW && p.X <= r.Right &&",
|
|
775
|
+
" p.Y >= r.Top && p.Y <= r.Top + zoneH);",
|
|
776
|
+
" }",
|
|
777
|
+
"}",
|
|
778
|
+
"'@ -ErrorAction SilentlyContinue"
|
|
779
|
+
] : [
|
|
721
780
|
"Add-Type -TypeDefinition @'",
|
|
722
781
|
"using System;",
|
|
723
782
|
"using System.Runtime.InteropServices;",
|
|
@@ -734,14 +793,95 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
734
793
|
" public const int WS_EX_NOACTIVATE = 0x08000000;",
|
|
735
794
|
" public const int WS_EX_APPWINDOW = 0x00040000;",
|
|
736
795
|
"",
|
|
737
|
-
" public static void MakeGlobalWindow(IntPtr hwnd) {",
|
|
796
|
+
" public static void MakeGlobalWindow(IntPtr hwnd, bool transparent) {",
|
|
738
797
|
" int style = GetWindowLong(hwnd, GWL_EXSTYLE);",
|
|
739
798
|
" style = style | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE;",
|
|
740
799
|
" style = style & ~WS_EX_APPWINDOW;",
|
|
741
800
|
" SetWindowLong(hwnd, GWL_EXSTYLE, style);",
|
|
742
801
|
" }",
|
|
743
802
|
"}",
|
|
744
|
-
"'@ -ErrorAction SilentlyContinue"
|
|
803
|
+
"'@ -ErrorAction SilentlyContinue"
|
|
804
|
+
];
|
|
805
|
+
const hoverThreshold = Math.max(1, Math.round(hoverDismissMs / 100));
|
|
806
|
+
const hoverDismissLines = clickThrough ? [
|
|
807
|
+
"",
|
|
808
|
+
"$script:hoverTicks = 0",
|
|
809
|
+
`$hoverThreshold = ${hoverThreshold}`,
|
|
810
|
+
"$brushConv = [System.Windows.Media.BrushConverter]::new()",
|
|
811
|
+
"$closeBtnEl = $window.FindName('CloseBtn')",
|
|
812
|
+
"$closeBgEl = $window.FindName('CloseBg')",
|
|
813
|
+
"",
|
|
814
|
+
"$hitTimer = New-Object System.Windows.Threading.DispatcherTimer",
|
|
815
|
+
"$hitTimer.Interval = [TimeSpan]::FromMilliseconds(100)",
|
|
816
|
+
"$hitTimer.Add_Tick({",
|
|
817
|
+
" if ($script:hwnd -eq [IntPtr]::Zero) { return }",
|
|
818
|
+
" $inZone = [VDesktop]::IsCursorInTopRight($script:hwnd, 80, 50)",
|
|
819
|
+
" if ($inZone) {",
|
|
820
|
+
" $script:hoverTicks++",
|
|
821
|
+
" $closeBtnEl.Foreground = $brushConv.ConvertFromString('#FFFFFF')",
|
|
822
|
+
" $closeBgEl.Background = $brushConv.ConvertFromString('#CC3344')",
|
|
823
|
+
" if ($script:hoverTicks -ge $hoverThreshold) {",
|
|
824
|
+
" $hitTimer.Stop()",
|
|
825
|
+
" $window.Close()",
|
|
826
|
+
" }",
|
|
827
|
+
" } else {",
|
|
828
|
+
" $script:hoverTicks = 0",
|
|
829
|
+
" $closeBtnEl.Foreground = $brushConv.ConvertFromString('#2A2A3A')",
|
|
830
|
+
" $closeBgEl.Background = $brushConv.ConvertFromString('Transparent')",
|
|
831
|
+
" }",
|
|
832
|
+
"})"
|
|
833
|
+
] : [];
|
|
834
|
+
const loadedLines = clickThrough ? [
|
|
835
|
+
"$window.Add_Loaded({",
|
|
836
|
+
" $script:hwnd = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle",
|
|
837
|
+
" [VDesktop]::MakeGlobalWindow($script:hwnd, $true)",
|
|
838
|
+
...fadeOut ? [
|
|
839
|
+
` $fadeAnim = New-Object System.Windows.Media.Animation.DoubleAnimation`,
|
|
840
|
+
` $fadeAnim.From = ${initialOpacity}`,
|
|
841
|
+
` $fadeAnim.To = ${finalOpacity}`,
|
|
842
|
+
` $fadeAnim.Duration = [System.Windows.Duration]::new([TimeSpan]::FromMilliseconds($duration))`,
|
|
843
|
+
` $fadeAnim.Add_Completed({ $window.Close() })`,
|
|
844
|
+
` $window.BeginAnimation([System.Windows.UIElement]::OpacityProperty, $fadeAnim)`
|
|
845
|
+
] : [],
|
|
846
|
+
" $hitTimer.Start()",
|
|
847
|
+
"})",
|
|
848
|
+
"",
|
|
849
|
+
"$window.Add_Closed({ $hitTimer.Stop() })"
|
|
850
|
+
] : [
|
|
851
|
+
"$window.Add_Loaded({",
|
|
852
|
+
" $script:hwnd = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle",
|
|
853
|
+
" [VDesktop]::MakeGlobalWindow($script:hwnd, $false)",
|
|
854
|
+
...fadeOut ? [
|
|
855
|
+
` $fadeAnim = New-Object System.Windows.Media.Animation.DoubleAnimation`,
|
|
856
|
+
` $fadeAnim.From = ${initialOpacity}`,
|
|
857
|
+
` $fadeAnim.To = ${finalOpacity}`,
|
|
858
|
+
` $fadeAnim.Duration = [System.Windows.Duration]::new([TimeSpan]::FromMilliseconds($duration))`,
|
|
859
|
+
` $fadeAnim.Add_Completed({ $window.Close() })`,
|
|
860
|
+
` $window.BeginAnimation([System.Windows.UIElement]::OpacityProperty, $fadeAnim)`
|
|
861
|
+
] : [],
|
|
862
|
+
"})"
|
|
863
|
+
];
|
|
864
|
+
const timerLines = !fadeOut ? [
|
|
865
|
+
"",
|
|
866
|
+
"if ($duration -gt 0) {",
|
|
867
|
+
" $timer = New-Object System.Windows.Threading.DispatcherTimer",
|
|
868
|
+
" $timer.Interval = [TimeSpan]::FromMilliseconds($duration)",
|
|
869
|
+
" $timer.Add_Tick({",
|
|
870
|
+
" $window.Close()",
|
|
871
|
+
" $timer.Stop()",
|
|
872
|
+
" })",
|
|
873
|
+
" $timer.Start()",
|
|
874
|
+
"}"
|
|
875
|
+
] : [];
|
|
876
|
+
const clickDismissLine = !clickThrough ? "$window.Add_MouseLeftButtonDown({ $window.Close() })" : "";
|
|
877
|
+
const psScript = [
|
|
878
|
+
"Add-Type -AssemblyName PresentationFramework",
|
|
879
|
+
"Add-Type -AssemblyName PresentationCore",
|
|
880
|
+
"Add-Type -AssemblyName WindowsBase",
|
|
881
|
+
"Add-Type -AssemblyName System.Windows.Forms",
|
|
882
|
+
"$targetScreen = [System.Windows.Forms.Screen]::FromPoint([System.Windows.Forms.Cursor]::Position).WorkingArea",
|
|
883
|
+
"",
|
|
884
|
+
...win32TypeDef,
|
|
745
885
|
"",
|
|
746
886
|
`$title = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${titleB64}'))`,
|
|
747
887
|
`$text = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('${textB64}'))`,
|
|
@@ -756,6 +896,7 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
756
896
|
"$reader = New-Object System.Xml.XmlNodeReader $xaml",
|
|
757
897
|
"$window = [Windows.Markup.XamlReader]::Load($reader)",
|
|
758
898
|
...manualPositionLines,
|
|
899
|
+
...fadeOut ? [`$window.Opacity = ${initialOpacity}`] : [],
|
|
759
900
|
"",
|
|
760
901
|
"$colorBar = $window.FindName('ColorBar')",
|
|
761
902
|
"$iconText = $window.FindName('IconText')",
|
|
@@ -768,22 +909,11 @@ async function runWpfNotification($, shellCommand, title, message, tag, style, p
|
|
|
768
909
|
"$titleText.Foreground = [System.Windows.Media.BrushConverter]::new().ConvertFromString($accentColor)",
|
|
769
910
|
"$contentText.Text = $text",
|
|
770
911
|
"",
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
"$window.Add_Loaded({",
|
|
774
|
-
" $hwnd = (New-Object System.Windows.Interop.WindowInteropHelper($window)).Handle",
|
|
775
|
-
" [VDesktop]::MakeGlobalWindow($hwnd)",
|
|
776
|
-
"})",
|
|
912
|
+
clickDismissLine,
|
|
913
|
+
...hoverDismissLines,
|
|
777
914
|
"",
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
" $timer.Interval = [TimeSpan]::FromMilliseconds($duration)",
|
|
781
|
-
" $timer.Add_Tick({",
|
|
782
|
-
" $window.Close()",
|
|
783
|
-
" $timer.Stop()",
|
|
784
|
-
" })",
|
|
785
|
-
" $timer.Start()",
|
|
786
|
-
"}",
|
|
915
|
+
...loadedLines,
|
|
916
|
+
...timerLines,
|
|
787
917
|
"",
|
|
788
918
|
"$window.ShowActivated = $false",
|
|
789
919
|
"$window.Show()",
|