coding-tool-x 3.4.10 → 3.4.12

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.
@@ -238,7 +238,191 @@ function escapeForXml(value) {
238
238
  }
239
239
 
240
240
  function buildWindowsPopupCommand(title, message) {
241
- return `powershell -NoProfile -Command "$wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('${escapeForPowerShellSingleQuote(message)}', 5, '${escapeForPowerShellSingleQuote(title)}', 0x40)"`;
241
+ const script = [
242
+ "$ErrorActionPreference = 'Stop'",
243
+ `$titleText = '${escapeForPowerShellSingleQuote(title)}'`,
244
+ `$messageText = '${escapeForPowerShellSingleQuote(message)}'`,
245
+ "$segments = $messageText -split '\\s*\\|\\s*', 2",
246
+ "$headlineText = if ($segments.Length -ge 1) { $segments[0] } else { $messageText }",
247
+ "$detailText = if ($segments.Length -ge 2) { $segments[1] } else { '' }",
248
+ 'try {',
249
+ 'Add-Type -AssemblyName PresentationFramework',
250
+ 'Add-Type -AssemblyName PresentationCore',
251
+ 'Add-Type -AssemblyName WindowsBase',
252
+ '$brushConverter = New-Object System.Windows.Media.BrushConverter',
253
+ '$window = New-Object System.Windows.Window',
254
+ '$window.Width = 372',
255
+ '$window.Height = 118',
256
+ '$window.WindowStyle = [System.Windows.WindowStyle]::None',
257
+ '$window.ResizeMode = [System.Windows.ResizeMode]::NoResize',
258
+ '$window.AllowsTransparency = $true',
259
+ '$window.Background = [System.Windows.Media.Brushes]::Transparent',
260
+ '$window.ShowInTaskbar = $false',
261
+ '$window.Topmost = $true',
262
+ '$window.ShowActivated = $false',
263
+ '$window.Opacity = 0',
264
+ '$window.WindowStartupLocation = [System.Windows.WindowStartupLocation]::Manual',
265
+ '$root = New-Object System.Windows.Controls.Border',
266
+ '$root.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 20',
267
+ '$root.Padding = New-Object System.Windows.Thickness -ArgumentList 16',
268
+ "$root.Background = $brushConverter.ConvertFromString('#F3F4F7')",
269
+ "$root.BorderBrush = $brushConverter.ConvertFromString('#D9DBE3')",
270
+ '$root.BorderThickness = New-Object System.Windows.Thickness -ArgumentList 1',
271
+ '$shadow = New-Object System.Windows.Media.Effects.DropShadowEffect',
272
+ "$shadow.Color = [System.Windows.Media.ColorConverter]::ConvertFromString('#22000000')",
273
+ '$shadow.BlurRadius = 26',
274
+ '$shadow.ShadowDepth = 0',
275
+ '$shadow.Opacity = 0.85',
276
+ '$root.Effect = $shadow',
277
+ '$grid = New-Object System.Windows.Controls.Grid',
278
+ '$iconColumn = New-Object System.Windows.Controls.ColumnDefinition',
279
+ '$iconColumn.Width = New-Object System.Windows.GridLength -ArgumentList 44',
280
+ '$contentColumn = New-Object System.Windows.Controls.ColumnDefinition',
281
+ '$grid.ColumnDefinitions.Add($iconColumn) | Out-Null',
282
+ '$grid.ColumnDefinitions.Add($contentColumn) | Out-Null',
283
+ '$iconHolder = New-Object System.Windows.Controls.Border',
284
+ '$iconHolder.Width = 34',
285
+ '$iconHolder.Height = 34',
286
+ '$iconHolder.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left',
287
+ '$iconHolder.VerticalAlignment = [System.Windows.VerticalAlignment]::Top',
288
+ '$iconHolder.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 17',
289
+ "$iconHolder.Background = $brushConverter.ConvertFromString('#DDEBFF')",
290
+ '$iconGlyph = New-Object System.Windows.Controls.TextBlock',
291
+ '$iconGlyph.Text = [char]0x2713',
292
+ '$iconGlyph.FontSize = 18',
293
+ '$iconGlyph.FontWeight = [System.Windows.FontWeights]::Bold',
294
+ "$iconGlyph.Foreground = $brushConverter.ConvertFromString('#0A84FF')",
295
+ '$iconGlyph.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Center',
296
+ '$iconGlyph.VerticalAlignment = [System.Windows.VerticalAlignment]::Center',
297
+ '$iconHolder.Child = $iconGlyph',
298
+ '$contentStack = New-Object System.Windows.Controls.StackPanel',
299
+ '$contentStack.Orientation = [System.Windows.Controls.Orientation]::Vertical',
300
+ '$contentStack.Margin = New-Object System.Windows.Thickness -ArgumentList 4,0,0,0',
301
+ '$metaGrid = New-Object System.Windows.Controls.Grid',
302
+ '$metaGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition)) | Out-Null',
303
+ '$timeColumn = New-Object System.Windows.Controls.ColumnDefinition',
304
+ '$timeColumn.Width = [System.Windows.GridLength]::Auto',
305
+ '$metaGrid.ColumnDefinitions.Add($timeColumn) | Out-Null',
306
+ '$appLabel = New-Object System.Windows.Controls.TextBlock',
307
+ '$appLabel.Text = $titleText',
308
+ '$appLabel.FontSize = 11.5',
309
+ '$appLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
310
+ "$appLabel.Foreground = $brushConverter.ConvertFromString('#6B6C73')",
311
+ '$appLabel.TextTrimming = [System.Windows.TextTrimming]::CharacterEllipsis',
312
+ '$stampLabel = New-Object System.Windows.Controls.TextBlock',
313
+ "$stampLabel.Text = '刚刚'",
314
+ '$stampLabel.FontSize = 11',
315
+ "$stampLabel.Foreground = $brushConverter.ConvertFromString('#8D8E95')",
316
+ '$stampLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 12,0,0,0',
317
+ '[System.Windows.Controls.Grid]::SetColumn($stampLabel, 1)',
318
+ '$metaGrid.Children.Add($appLabel) | Out-Null',
319
+ '$metaGrid.Children.Add($stampLabel) | Out-Null',
320
+ '$headlineLabel = New-Object System.Windows.Controls.TextBlock',
321
+ '$headlineLabel.Text = $headlineText',
322
+ '$headlineLabel.FontSize = 14',
323
+ '$headlineLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
324
+ "$headlineLabel.Foreground = $brushConverter.ConvertFromString('#1F2024')",
325
+ '$headlineLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
326
+ '$headlineLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,6,0,0',
327
+ '$detailLabel = New-Object System.Windows.Controls.TextBlock',
328
+ '$detailLabel.Text = $detailText',
329
+ '$detailLabel.FontSize = 12',
330
+ "$detailLabel.Foreground = $brushConverter.ConvertFromString('#5F6168')",
331
+ '$detailLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
332
+ '$detailLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,4,0,0',
333
+ '$detailLabel.Visibility = if ([string]::IsNullOrWhiteSpace($detailText)) { [System.Windows.Visibility]::Collapsed } else { [System.Windows.Visibility]::Visible }',
334
+ '$contentStack.Children.Add($metaGrid) | Out-Null',
335
+ '$contentStack.Children.Add($headlineLabel) | Out-Null',
336
+ '$contentStack.Children.Add($detailLabel) | Out-Null',
337
+ '[System.Windows.Controls.Grid]::SetColumn($contentStack, 1)',
338
+ '$grid.Children.Add($iconHolder) | Out-Null',
339
+ '$grid.Children.Add($contentStack) | Out-Null',
340
+ '$root.Child = $grid',
341
+ '$window.Content = $root',
342
+ '$workArea = [System.Windows.SystemParameters]::WorkArea',
343
+ '$window.Left = $workArea.Right - $window.Width - 18',
344
+ '$window.Top = $workArea.Top + 18',
345
+ '$frame = New-Object System.Windows.Threading.DispatcherFrame',
346
+ '$window.Add_Closed({ $frame.Continue = $false })',
347
+ '$closeTimer = New-Object System.Windows.Threading.DispatcherTimer',
348
+ '$closeTimer.Interval = [TimeSpan]::FromMilliseconds(4600)',
349
+ '$closeTimer.Add_Tick({',
350
+ ' $closeTimer.Stop()',
351
+ ' $fadeOut = New-Object System.Windows.Media.Animation.DoubleAnimation',
352
+ ' $fadeOut.From = $window.Opacity',
353
+ ' $fadeOut.To = 0',
354
+ ' $fadeOut.Duration = [TimeSpan]::FromMilliseconds(220)',
355
+ ' $fadeOut.Add_Completed({ $window.Close() })',
356
+ ' $window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeOut)',
357
+ '})',
358
+ '$window.Show()',
359
+ '$fadeIn = New-Object System.Windows.Media.Animation.DoubleAnimation',
360
+ '$fadeIn.From = 0',
361
+ '$fadeIn.To = 1',
362
+ '$fadeIn.Duration = [TimeSpan]::FromMilliseconds(180)',
363
+ '$window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeIn)',
364
+ '$closeTimer.Start()',
365
+ '[System.Windows.Threading.Dispatcher]::PushFrame($frame)',
366
+ '} catch {',
367
+ 'Add-Type -AssemblyName System.Windows.Forms',
368
+ 'Add-Type -AssemblyName System.Drawing',
369
+ '$form = New-Object System.Windows.Forms.Form',
370
+ '$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
371
+ '$form.BackColor = [System.Drawing.Color]::FromArgb(247, 247, 250)',
372
+ '$form.Width = 360',
373
+ '$form.Height = 118',
374
+ '$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual',
375
+ '$form.ShowInTaskbar = $false',
376
+ '$form.TopMost = $true',
377
+ '$form.MaximizeBox = $false',
378
+ '$form.MinimizeBox = $false',
379
+ '$workingArea = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea',
380
+ '$form.Location = New-Object System.Drawing.Point(($workingArea.Right - $form.Width - 18), ($workingArea.Top + 18))',
381
+ '$iconPanel = New-Object System.Windows.Forms.Panel',
382
+ '$iconPanel.Width = 34',
383
+ '$iconPanel.Height = 34',
384
+ '$iconPanel.BackColor = [System.Drawing.Color]::FromArgb(221, 235, 255)',
385
+ '$iconPanel.Location = New-Object System.Drawing.Point(16, 18)',
386
+ '$iconLabel = New-Object System.Windows.Forms.Label',
387
+ '$iconLabel.Text = [char]0x2713',
388
+ "$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', 14, [System.Drawing.FontStyle]::Bold)",
389
+ '$iconLabel.ForeColor = [System.Drawing.Color]::FromArgb(10, 132, 255)',
390
+ '$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter',
391
+ '$iconLabel.Dock = [System.Windows.Forms.DockStyle]::Fill',
392
+ '$iconPanel.Controls.Add($iconLabel)',
393
+ '$titleLabel = New-Object System.Windows.Forms.Label',
394
+ '$titleLabel.Text = $titleText',
395
+ "$titleLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9, [System.Drawing.FontStyle]::Bold)",
396
+ '$titleLabel.ForeColor = [System.Drawing.Color]::FromArgb(109, 110, 115)',
397
+ '$titleLabel.AutoSize = $true',
398
+ '$titleLabel.Location = New-Object System.Drawing.Point(60, 16)',
399
+ '$headlineLabel = New-Object System.Windows.Forms.Label',
400
+ '$headlineLabel.Text = $headlineText',
401
+ "$headlineLabel.Font = New-Object System.Drawing.Font('Segoe UI', 10.5, [System.Drawing.FontStyle]::Bold)",
402
+ '$headlineLabel.ForeColor = [System.Drawing.Color]::FromArgb(31, 32, 36)',
403
+ '$headlineLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
404
+ '$headlineLabel.AutoSize = $true',
405
+ '$headlineLabel.Location = New-Object System.Drawing.Point(60, 36)',
406
+ '$detailLabel = New-Object System.Windows.Forms.Label',
407
+ '$detailLabel.Text = $detailText',
408
+ "$detailLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9)",
409
+ '$detailLabel.ForeColor = [System.Drawing.Color]::FromArgb(95, 97, 104)',
410
+ '$detailLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
411
+ '$detailLabel.AutoSize = $true',
412
+ '$detailLabel.Location = New-Object System.Drawing.Point(60, 60)',
413
+ '$detailLabel.Visible = -not [string]::IsNullOrWhiteSpace($detailText)',
414
+ '$form.Controls.Add($iconPanel)',
415
+ '$form.Controls.Add($titleLabel)',
416
+ '$form.Controls.Add($headlineLabel)',
417
+ '$form.Controls.Add($detailLabel)',
418
+ '$timer = New-Object System.Windows.Forms.Timer',
419
+ '$timer.Interval = 4800',
420
+ '$timer.Add_Tick({ $timer.Stop(); $form.Close() })',
421
+ '$timer.Start()',
422
+ '[void]$form.ShowDialog()',
423
+ '}'
424
+ ].join('; ');
425
+ return `powershell -NoProfile -STA -Command ${JSON.stringify(script)}`;
242
426
  }
243
427
 
244
428
  function generateNotifyScript(feishu = {}) {
@@ -345,7 +529,7 @@ function notify(mode, message) {
345
529
  const ps = "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('" +
346
530
  escapeForPowerShellSingleQuote(message) + "', '" +
347
531
  escapeForPowerShellSingleQuote(title) + "', 'OK', 'Information')"
348
- const command = 'powershell -NoProfile -Command ' + JSON.stringify(ps) + ' || ' + popupCommand
532
+ const command = 'powershell -NoProfile -STA -Command ' + JSON.stringify(ps) + ' || ' + popupCommand
349
533
  execSync(command, { stdio: 'ignore', windowsHide: true })
350
534
  } else {
351
535
  execSync(popupCommand, { stdio: 'ignore', windowsHide: true })
@@ -464,7 +648,191 @@ function escapeForXml(value) {
464
648
  }
465
649
 
466
650
  function buildWindowsPopupCommand(title, message) {
467
- return \`powershell -NoProfile -Command "$wshell = New-Object -ComObject Wscript.Shell; $wshell.Popup('\${escapeForPowerShellSingleQuote(message)}', 5, '\${escapeForPowerShellSingleQuote(title)}', 0x40)"\`
651
+ const script = [
652
+ "$ErrorActionPreference = 'Stop'",
653
+ \`$titleText = '\${escapeForPowerShellSingleQuote(title)}'\`,
654
+ \`$messageText = '\${escapeForPowerShellSingleQuote(message)}'\`,
655
+ "$segments = $messageText -split '\\\\s*\\\\|\\\\s*', 2",
656
+ "$headlineText = if ($segments.Length -ge 1) { $segments[0] } else { $messageText }",
657
+ "$detailText = if ($segments.Length -ge 2) { $segments[1] } else { '' }",
658
+ 'try {',
659
+ 'Add-Type -AssemblyName PresentationFramework',
660
+ 'Add-Type -AssemblyName PresentationCore',
661
+ 'Add-Type -AssemblyName WindowsBase',
662
+ '$brushConverter = New-Object System.Windows.Media.BrushConverter',
663
+ '$window = New-Object System.Windows.Window',
664
+ '$window.Width = 372',
665
+ '$window.Height = 118',
666
+ '$window.WindowStyle = [System.Windows.WindowStyle]::None',
667
+ '$window.ResizeMode = [System.Windows.ResizeMode]::NoResize',
668
+ '$window.AllowsTransparency = $true',
669
+ '$window.Background = [System.Windows.Media.Brushes]::Transparent',
670
+ '$window.ShowInTaskbar = $false',
671
+ '$window.Topmost = $true',
672
+ '$window.ShowActivated = $false',
673
+ '$window.Opacity = 0',
674
+ '$window.WindowStartupLocation = [System.Windows.WindowStartupLocation]::Manual',
675
+ '$root = New-Object System.Windows.Controls.Border',
676
+ '$root.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 20',
677
+ '$root.Padding = New-Object System.Windows.Thickness -ArgumentList 16',
678
+ "$root.Background = $brushConverter.ConvertFromString('#F3F4F7')",
679
+ "$root.BorderBrush = $brushConverter.ConvertFromString('#D9DBE3')",
680
+ '$root.BorderThickness = New-Object System.Windows.Thickness -ArgumentList 1',
681
+ '$shadow = New-Object System.Windows.Media.Effects.DropShadowEffect',
682
+ "$shadow.Color = [System.Windows.Media.ColorConverter]::ConvertFromString('#22000000')",
683
+ '$shadow.BlurRadius = 26',
684
+ '$shadow.ShadowDepth = 0',
685
+ '$shadow.Opacity = 0.85',
686
+ '$root.Effect = $shadow',
687
+ '$grid = New-Object System.Windows.Controls.Grid',
688
+ '$iconColumn = New-Object System.Windows.Controls.ColumnDefinition',
689
+ '$iconColumn.Width = New-Object System.Windows.GridLength -ArgumentList 44',
690
+ '$contentColumn = New-Object System.Windows.Controls.ColumnDefinition',
691
+ '$grid.ColumnDefinitions.Add($iconColumn) | Out-Null',
692
+ '$grid.ColumnDefinitions.Add($contentColumn) | Out-Null',
693
+ '$iconHolder = New-Object System.Windows.Controls.Border',
694
+ '$iconHolder.Width = 34',
695
+ '$iconHolder.Height = 34',
696
+ '$iconHolder.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left',
697
+ '$iconHolder.VerticalAlignment = [System.Windows.VerticalAlignment]::Top',
698
+ '$iconHolder.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 17',
699
+ "$iconHolder.Background = $brushConverter.ConvertFromString('#DDEBFF')",
700
+ '$iconGlyph = New-Object System.Windows.Controls.TextBlock',
701
+ '$iconGlyph.Text = [char]0x2713',
702
+ '$iconGlyph.FontSize = 18',
703
+ '$iconGlyph.FontWeight = [System.Windows.FontWeights]::Bold',
704
+ "$iconGlyph.Foreground = $brushConverter.ConvertFromString('#0A84FF')",
705
+ '$iconGlyph.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Center',
706
+ '$iconGlyph.VerticalAlignment = [System.Windows.VerticalAlignment]::Center',
707
+ '$iconHolder.Child = $iconGlyph',
708
+ '$contentStack = New-Object System.Windows.Controls.StackPanel',
709
+ '$contentStack.Orientation = [System.Windows.Controls.Orientation]::Vertical',
710
+ '$contentStack.Margin = New-Object System.Windows.Thickness -ArgumentList 4,0,0,0',
711
+ '$metaGrid = New-Object System.Windows.Controls.Grid',
712
+ '$metaGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition)) | Out-Null',
713
+ '$timeColumn = New-Object System.Windows.Controls.ColumnDefinition',
714
+ '$timeColumn.Width = [System.Windows.GridLength]::Auto',
715
+ '$metaGrid.ColumnDefinitions.Add($timeColumn) | Out-Null',
716
+ '$appLabel = New-Object System.Windows.Controls.TextBlock',
717
+ '$appLabel.Text = $titleText',
718
+ '$appLabel.FontSize = 11.5',
719
+ '$appLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
720
+ "$appLabel.Foreground = $brushConverter.ConvertFromString('#6B6C73')",
721
+ '$appLabel.TextTrimming = [System.Windows.TextTrimming]::CharacterEllipsis',
722
+ '$stampLabel = New-Object System.Windows.Controls.TextBlock',
723
+ "$stampLabel.Text = '刚刚'",
724
+ '$stampLabel.FontSize = 11',
725
+ "$stampLabel.Foreground = $brushConverter.ConvertFromString('#8D8E95')",
726
+ '$stampLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 12,0,0,0',
727
+ '[System.Windows.Controls.Grid]::SetColumn($stampLabel, 1)',
728
+ '$metaGrid.Children.Add($appLabel) | Out-Null',
729
+ '$metaGrid.Children.Add($stampLabel) | Out-Null',
730
+ '$headlineLabel = New-Object System.Windows.Controls.TextBlock',
731
+ '$headlineLabel.Text = $headlineText',
732
+ '$headlineLabel.FontSize = 14',
733
+ '$headlineLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
734
+ "$headlineLabel.Foreground = $brushConverter.ConvertFromString('#1F2024')",
735
+ '$headlineLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
736
+ '$headlineLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,6,0,0',
737
+ '$detailLabel = New-Object System.Windows.Controls.TextBlock',
738
+ '$detailLabel.Text = $detailText',
739
+ '$detailLabel.FontSize = 12',
740
+ "$detailLabel.Foreground = $brushConverter.ConvertFromString('#5F6168')",
741
+ '$detailLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
742
+ '$detailLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,4,0,0',
743
+ '$detailLabel.Visibility = if ([string]::IsNullOrWhiteSpace($detailText)) { [System.Windows.Visibility]::Collapsed } else { [System.Windows.Visibility]::Visible }',
744
+ '$contentStack.Children.Add($metaGrid) | Out-Null',
745
+ '$contentStack.Children.Add($headlineLabel) | Out-Null',
746
+ '$contentStack.Children.Add($detailLabel) | Out-Null',
747
+ '[System.Windows.Controls.Grid]::SetColumn($contentStack, 1)',
748
+ '$grid.Children.Add($iconHolder) | Out-Null',
749
+ '$grid.Children.Add($contentStack) | Out-Null',
750
+ '$root.Child = $grid',
751
+ '$window.Content = $root',
752
+ '$workArea = [System.Windows.SystemParameters]::WorkArea',
753
+ '$window.Left = $workArea.Right - $window.Width - 18',
754
+ '$window.Top = $workArea.Top + 18',
755
+ '$frame = New-Object System.Windows.Threading.DispatcherFrame',
756
+ '$window.Add_Closed({ $frame.Continue = $false })',
757
+ '$closeTimer = New-Object System.Windows.Threading.DispatcherTimer',
758
+ '$closeTimer.Interval = [TimeSpan]::FromMilliseconds(4600)',
759
+ '$closeTimer.Add_Tick({',
760
+ ' $closeTimer.Stop()',
761
+ ' $fadeOut = New-Object System.Windows.Media.Animation.DoubleAnimation',
762
+ ' $fadeOut.From = $window.Opacity',
763
+ ' $fadeOut.To = 0',
764
+ ' $fadeOut.Duration = [TimeSpan]::FromMilliseconds(220)',
765
+ ' $fadeOut.Add_Completed({ $window.Close() })',
766
+ ' $window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeOut)',
767
+ '})',
768
+ '$window.Show()',
769
+ '$fadeIn = New-Object System.Windows.Media.Animation.DoubleAnimation',
770
+ '$fadeIn.From = 0',
771
+ '$fadeIn.To = 1',
772
+ '$fadeIn.Duration = [TimeSpan]::FromMilliseconds(180)',
773
+ '$window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeIn)',
774
+ '$closeTimer.Start()',
775
+ '[System.Windows.Threading.Dispatcher]::PushFrame($frame)',
776
+ '} catch {',
777
+ 'Add-Type -AssemblyName System.Windows.Forms',
778
+ 'Add-Type -AssemblyName System.Drawing',
779
+ '$form = New-Object System.Windows.Forms.Form',
780
+ '$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
781
+ '$form.BackColor = [System.Drawing.Color]::FromArgb(247, 247, 250)',
782
+ '$form.Width = 360',
783
+ '$form.Height = 118',
784
+ '$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual',
785
+ '$form.ShowInTaskbar = $false',
786
+ '$form.TopMost = $true',
787
+ '$form.MaximizeBox = $false',
788
+ '$form.MinimizeBox = $false',
789
+ '$workingArea = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea',
790
+ '$form.Location = New-Object System.Drawing.Point(($workingArea.Right - $form.Width - 18), ($workingArea.Top + 18))',
791
+ '$iconPanel = New-Object System.Windows.Forms.Panel',
792
+ '$iconPanel.Width = 34',
793
+ '$iconPanel.Height = 34',
794
+ '$iconPanel.BackColor = [System.Drawing.Color]::FromArgb(221, 235, 255)',
795
+ '$iconPanel.Location = New-Object System.Drawing.Point(16, 18)',
796
+ '$iconLabel = New-Object System.Windows.Forms.Label',
797
+ '$iconLabel.Text = [char]0x2713',
798
+ "$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', 14, [System.Drawing.FontStyle]::Bold)",
799
+ '$iconLabel.ForeColor = [System.Drawing.Color]::FromArgb(10, 132, 255)',
800
+ '$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter',
801
+ '$iconLabel.Dock = [System.Windows.Forms.DockStyle]::Fill',
802
+ '$iconPanel.Controls.Add($iconLabel)',
803
+ '$titleLabel = New-Object System.Windows.Forms.Label',
804
+ '$titleLabel.Text = $titleText',
805
+ "$titleLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9, [System.Drawing.FontStyle]::Bold)",
806
+ '$titleLabel.ForeColor = [System.Drawing.Color]::FromArgb(109, 110, 115)',
807
+ '$titleLabel.AutoSize = $true',
808
+ '$titleLabel.Location = New-Object System.Drawing.Point(60, 16)',
809
+ '$headlineLabel = New-Object System.Windows.Forms.Label',
810
+ '$headlineLabel.Text = $headlineText',
811
+ "$headlineLabel.Font = New-Object System.Drawing.Font('Segoe UI', 10.5, [System.Drawing.FontStyle]::Bold)",
812
+ '$headlineLabel.ForeColor = [System.Drawing.Color]::FromArgb(31, 32, 36)',
813
+ '$headlineLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
814
+ '$headlineLabel.AutoSize = $true',
815
+ '$headlineLabel.Location = New-Object System.Drawing.Point(60, 36)',
816
+ '$detailLabel = New-Object System.Windows.Forms.Label',
817
+ '$detailLabel.Text = $detailText',
818
+ "$detailLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9)",
819
+ '$detailLabel.ForeColor = [System.Drawing.Color]::FromArgb(95, 97, 104)',
820
+ '$detailLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
821
+ '$detailLabel.AutoSize = $true',
822
+ '$detailLabel.Location = New-Object System.Drawing.Point(60, 60)',
823
+ '$detailLabel.Visible = -not [string]::IsNullOrWhiteSpace($detailText)',
824
+ '$form.Controls.Add($iconPanel)',
825
+ '$form.Controls.Add($titleLabel)',
826
+ '$form.Controls.Add($headlineLabel)',
827
+ '$form.Controls.Add($detailLabel)',
828
+ '$timer = New-Object System.Windows.Forms.Timer',
829
+ '$timer.Interval = 4800',
830
+ '$timer.Add_Tick({ $timer.Stop(); $form.Close() })',
831
+ '$timer.Start()',
832
+ '[void]$form.ShowDialog()',
833
+ '}'
834
+ ].join('; ')
835
+ return 'powershell -NoProfile -STA -Command ' + JSON.stringify(script)
468
836
  }
469
837
  `;
470
838
  }
@@ -832,6 +1200,205 @@ function saveNotificationSettings(input = {}) {
832
1200
  return getNotificationSettings();
833
1201
  }
834
1202
 
1203
+ function parseNotifyTypeMarker(command) {
1204
+ const marker = String(command || '').match(/--cc-notify-type=(['"])?(dialog|notification)\1/i);
1205
+ return marker?.[2] ? normalizeType(marker[2].toLowerCase()) : null;
1206
+ }
1207
+
1208
+ function getStopHookCommand(settings = {}) {
1209
+ const hooks = settings?.hooks?.Stop;
1210
+ if (!Array.isArray(hooks) || hooks.length === 0) {
1211
+ return '';
1212
+ }
1213
+
1214
+ let fallbackCommand = '';
1215
+ for (const group of hooks) {
1216
+ const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
1217
+ for (const hook of groupHooks) {
1218
+ const command = String(hook?.command || '');
1219
+ if (!command) continue;
1220
+ if (!fallbackCommand) {
1221
+ fallbackCommand = command;
1222
+ }
1223
+ if (command.includes('notify-hook.js')) {
1224
+ return command;
1225
+ }
1226
+ }
1227
+ }
1228
+
1229
+ return fallbackCommand;
1230
+ }
1231
+
1232
+ function normalizePathForCompare(rawPath) {
1233
+ return String(rawPath || '').replace(/\\/g, '/');
1234
+ }
1235
+
1236
+ function shouldRepairStopHook(settings, expectedScriptPath = PATHS.notifyHook, fileExists = fs.existsSync) {
1237
+ const command = getStopHookCommand(settings);
1238
+ if (!command || !command.includes('notify-hook.js')) {
1239
+ return false;
1240
+ }
1241
+
1242
+ const normalizedCommand = normalizePathForCompare(command);
1243
+ const normalizedExpected = normalizePathForCompare(expectedScriptPath);
1244
+ if (!normalizedCommand.includes(normalizedExpected)) {
1245
+ return true;
1246
+ }
1247
+
1248
+ const markerType = parseNotifyTypeMarker(command);
1249
+ if (!markerType) {
1250
+ return true;
1251
+ }
1252
+
1253
+ return !fileExists(expectedScriptPath);
1254
+ }
1255
+
1256
+ function buildStopHookCommand(type) {
1257
+ return buildClaudeCommand(type);
1258
+ }
1259
+
1260
+ function parseStopHookStatus(settings = {}) {
1261
+ const stopGroups = Array.isArray(settings?.hooks?.Stop) ? settings.hooks.Stop : [];
1262
+ if (stopGroups.length === 0) {
1263
+ return { enabled: false, type: 'notification' };
1264
+ }
1265
+
1266
+ let sawNotificationLikeCommand = false;
1267
+ for (const group of stopGroups) {
1268
+ const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
1269
+ for (const hook of groupHooks) {
1270
+ const command = String(hook?.command || '');
1271
+ if (!command) continue;
1272
+
1273
+ const markerType = parseNotifyTypeMarker(command) || parseManagedType(command);
1274
+ if (markerType) {
1275
+ return { enabled: true, type: markerType };
1276
+ }
1277
+
1278
+ const isDialog = command.includes('display dialog') ||
1279
+ command.includes('MessageBox') ||
1280
+ command.includes('zenity --info');
1281
+ if (isDialog) {
1282
+ return { enabled: true, type: 'dialog' };
1283
+ }
1284
+
1285
+ const isNotification = command.includes('display notification') ||
1286
+ command.includes('Popup') ||
1287
+ command.includes('GraphicsPath') ||
1288
+ command.includes('TransparencyKey') ||
1289
+ command.includes('FormBorderStyle]::None') ||
1290
+ command.includes('AllowsTransparency') ||
1291
+ command.includes('notify-send') ||
1292
+ command.includes('ToastNotificationManager') ||
1293
+ command.includes('CreateToastNotifier') ||
1294
+ command.includes('notify-hook.js');
1295
+ if (isNotification) {
1296
+ sawNotificationLikeCommand = true;
1297
+ }
1298
+ }
1299
+ }
1300
+
1301
+ return sawNotificationLikeCommand
1302
+ ? { enabled: true, type: 'notification' }
1303
+ : { enabled: false, type: 'notification' };
1304
+ }
1305
+
1306
+ function normalizeSavedPlatformStatus(platform = {}) {
1307
+ return {
1308
+ enabled: platform?.enabled === true,
1309
+ type: normalizeType(platform?.type)
1310
+ };
1311
+ }
1312
+
1313
+ function buildLegacyClaudeSaveInput(input = {}, currentSettings = getNotificationSettings()) {
1314
+ return {
1315
+ platforms: {
1316
+ claude: input.stopHook !== undefined
1317
+ ? normalizePlatformInput(input.stopHook)
1318
+ : { enabled: false, type: 'notification' },
1319
+ codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1320
+ gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1321
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
1322
+ },
1323
+ feishu: input.feishu !== undefined
1324
+ ? {
1325
+ enabled: input.feishu?.enabled === true,
1326
+ webhookUrl: input.feishu?.webhookUrl || ''
1327
+ }
1328
+ : {
1329
+ enabled: currentSettings?.feishu?.enabled === true,
1330
+ webhookUrl: currentSettings?.feishu?.webhookUrl || ''
1331
+ }
1332
+ };
1333
+ }
1334
+
1335
+ function getLegacyClaudeHookSettings() {
1336
+ return {
1337
+ success: true,
1338
+ stopHook: parseStopHookStatus(readClaudeSettings()),
1339
+ feishu: getFeishuConfig(),
1340
+ platform: os.platform()
1341
+ };
1342
+ }
1343
+
1344
+ function saveLegacyClaudeHookSettings(input = {}) {
1345
+ const currentSettings = getNotificationSettings();
1346
+ saveNotificationSettings(buildLegacyClaudeSaveInput(input, currentSettings));
1347
+ return getLegacyClaudeHookSettings();
1348
+ }
1349
+
1350
+ function initDefaultHooks() {
1351
+ try {
1352
+ const uiConfig = loadUIConfig();
1353
+ if (uiConfig.claudeNotificationDisabledByUser === true) {
1354
+ console.log('[Claude Hooks] 用户已主动关闭通知,跳过自动初始化');
1355
+ return;
1356
+ }
1357
+
1358
+ const currentClaudeSettings = readClaudeSettings();
1359
+ const currentStatus = parseStopHookStatus(currentClaudeSettings);
1360
+
1361
+ if (currentStatus.enabled) {
1362
+ if (shouldRepairStopHook(currentClaudeSettings)) {
1363
+ const currentSettings = getNotificationSettings();
1364
+ saveNotificationSettings({
1365
+ platforms: {
1366
+ claude: { enabled: true, type: currentStatus.type || 'notification' },
1367
+ codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1368
+ gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1369
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
1370
+ },
1371
+ feishu: {
1372
+ enabled: currentSettings?.feishu?.enabled === true,
1373
+ webhookUrl: currentSettings?.feishu?.webhookUrl || ''
1374
+ }
1375
+ });
1376
+ console.log('[Claude Hooks] 检测到旧版 Stop hook 路径,已自动修复');
1377
+ } else {
1378
+ console.log('[Claude Hooks] 已存在 Stop hook 配置,跳过初始化');
1379
+ }
1380
+ return;
1381
+ }
1382
+
1383
+ const currentSettings = getNotificationSettings();
1384
+ saveNotificationSettings({
1385
+ platforms: {
1386
+ claude: { enabled: true, type: 'notification' },
1387
+ codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
1388
+ gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
1389
+ opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
1390
+ },
1391
+ feishu: {
1392
+ enabled: currentSettings?.feishu?.enabled === true,
1393
+ webhookUrl: currentSettings?.feishu?.webhookUrl || ''
1394
+ }
1395
+ });
1396
+ console.log('[Claude Hooks] 已自动开启任务完成通知(右上角卡片)');
1397
+ } catch (error) {
1398
+ console.error('[Claude Hooks] 初始化默认配置失败:', error);
1399
+ }
1400
+ }
1401
+
835
1402
  function sendFeishuTest(webhookUrl) {
836
1403
  return new Promise((resolve, reject) => {
837
1404
  try {
@@ -897,7 +1464,7 @@ function generateSystemNotificationCommand(type, message, platformOverride = os.
897
1464
  if (platform === 'win32') {
898
1465
  const popupCommand = buildWindowsPopupCommand(title, message);
899
1466
  if (normalizedType === 'dialog') {
900
- return `powershell -NoProfile -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('${escapeForPowerShellSingleQuote(message)}', '${escapeForPowerShellSingleQuote(title)}', 'OK', 'Information')" || ${popupCommand}`;
1467
+ return `powershell -NoProfile -STA -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('${escapeForPowerShellSingleQuote(message)}', '${escapeForPowerShellSingleQuote(title)}', 'OK', 'Information')" || ${popupCommand}`;
901
1468
  }
902
1469
  return popupCommand;
903
1470
  }
@@ -920,8 +1487,11 @@ function testNotification({ type, testFeishu, webhookUrl } = {}) {
920
1487
  module.exports = {
921
1488
  MANAGED_HOOK_NAME,
922
1489
  getNotificationSettings,
1490
+ getLegacyClaudeHookSettings,
923
1491
  saveNotificationSettings,
1492
+ saveLegacyClaudeHookSettings,
924
1493
  testNotification,
1494
+ initDefaultHooks,
925
1495
  getOpenCodeManagedPluginPath,
926
1496
  buildOpenCodePluginContent,
927
1497
  buildCodexNotifyCommand,
@@ -931,6 +1501,7 @@ module.exports = {
931
1501
  applyClaudeDisablePreference,
932
1502
  getManagedCommandType,
933
1503
  parseManagedType,
1504
+ parseNotifyTypeMarker,
934
1505
  getClaudeHookStatus,
935
1506
  getCodexHookStatus,
936
1507
  getGeminiHookStatus,
@@ -941,10 +1512,13 @@ module.exports = {
941
1512
  validateFeishuWebhookUrl,
942
1513
  buildCodexNotifyCommand,
943
1514
  buildGeminiCommand,
1515
+ buildStopHookCommand,
944
1516
  buildClaudeCommand,
945
1517
  buildOpenCodePluginContent,
946
1518
  getOpenCodeManagedPluginPath,
947
1519
  generateNotifyScript,
948
- generateSystemNotificationCommand
1520
+ generateSystemNotificationCommand,
1521
+ parseStopHookStatus,
1522
+ shouldRepairStopHook
949
1523
  }
950
1524
  };
@@ -117,7 +117,7 @@ function syncManagedChannelConfig(channels = [], preferredChannel = null) {
117
117
  : resolveCurrentManagedChannel(channels);
118
118
 
119
119
  if (targetChannel) {
120
- setChannelConfig(targetChannel);
120
+ setChannelConfig(buildNativeConfigChannel(targetChannel));
121
121
  return targetChannel;
122
122
  }
123
123
 
@@ -305,11 +305,20 @@ function applyChannelToSettings(channelId) {
305
305
  });
306
306
  saveChannels(data);
307
307
 
308
- setChannelConfig(channel);
308
+ setChannelConfig(buildNativeConfigChannel(channel));
309
309
 
310
310
  return channel;
311
311
  }
312
312
 
313
+ function buildNativeConfigChannel(channel = {}) {
314
+ const candidates = getEffectiveApiKeyCandidates(channel);
315
+ const effectiveApiKey = candidates[0] || normalizeApiKey(channel.apiKey || channel.key || '');
316
+ return {
317
+ ...channel,
318
+ apiKey: effectiveApiKey
319
+ };
320
+ }
321
+
313
322
  function loadCodexChannels() {
314
323
  const filePath = getCodexChannelsFilePath();
315
324
  if (!fs.existsSync(filePath)) {