coding-tool-x 3.4.11 → 3.5.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/dist/web/assets/{Analytics-BT-pLYj8.js → Analytics-Q_QFMM2p.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BGH9N-xf.js → ConfigTemplates-Ca8C7VtV.js} +1 -1
- package/dist/web/assets/{Home-C_YwC-4M.js → Home-ChIIT4Ew.js} +1 -1
- package/dist/web/assets/{PluginManager-BTb28q0R.js → PluginManager-C3w7p-sj.js} +1 -1
- package/dist/web/assets/{ProjectList-BYm3jQ3S.js → ProjectList-BfJTDXDw.js} +1 -1
- package/dist/web/assets/{SessionList-C6EIsN9j.js → SessionList-a3EoL0hZ.js} +1 -1
- package/dist/web/assets/{SkillManager-B2VKu6_J.js → SkillManager-CohW5iqS.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-LHBQcZIV.js → WorkspaceManager-DxaKsaJK.js} +1 -1
- package/dist/web/assets/index-B02wDWNC.css +1 -0
- package/dist/web/assets/index-EMrm1wk-.js +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/server/api/claude-hooks.js +23 -570
- package/src/server/index.js +3 -1
- package/src/server/services/notification-hooks.js +634 -42
- package/dist/web/assets/index-D547X48u.js +0 -2
- package/dist/web/assets/index-NC-fbfg8.css +0 -1
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const http = require('http');
|
|
5
5
|
const https = require('https');
|
|
6
|
-
const { execSync } = require('child_process');
|
|
6
|
+
const { execSync, execFileSync } = require('child_process');
|
|
7
7
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
8
8
|
const { loadUIConfig, saveUIConfig } = require('./ui-config');
|
|
9
9
|
const codexSettingsManager = require('./codex-settings-manager');
|
|
@@ -239,40 +239,198 @@ function escapeForXml(value) {
|
|
|
239
239
|
|
|
240
240
|
function buildWindowsPopupCommand(title, message) {
|
|
241
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 {',
|
|
242
367
|
'Add-Type -AssemblyName System.Windows.Forms',
|
|
243
368
|
'Add-Type -AssemblyName System.Drawing',
|
|
244
369
|
'$form = New-Object System.Windows.Forms.Form',
|
|
245
|
-
|
|
370
|
+
'$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
|
|
371
|
+
'$form.BackColor = [System.Drawing.Color]::FromArgb(247, 247, 250)',
|
|
246
372
|
'$form.Width = 360',
|
|
247
|
-
'$form.Height =
|
|
373
|
+
'$form.Height = 118',
|
|
248
374
|
'$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual',
|
|
249
|
-
'$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow',
|
|
250
375
|
'$form.ShowInTaskbar = $false',
|
|
251
376
|
'$form.TopMost = $true',
|
|
252
377
|
'$form.MaximizeBox = $false',
|
|
253
378
|
'$form.MinimizeBox = $false',
|
|
254
379
|
'$workingArea = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea',
|
|
255
|
-
'$form.Location = New-Object System.Drawing.Point(($workingArea.Right - $form.Width -
|
|
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)',
|
|
256
393
|
'$titleLabel = New-Object System.Windows.Forms.Label',
|
|
257
|
-
|
|
258
|
-
"$titleLabel.Font = New-Object System.Drawing.Font('Segoe UI',
|
|
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)',
|
|
259
397
|
'$titleLabel.AutoSize = $true',
|
|
260
|
-
'$titleLabel.Location = New-Object System.Drawing.Point(
|
|
261
|
-
'$
|
|
262
|
-
|
|
263
|
-
"$
|
|
264
|
-
'$
|
|
265
|
-
'$
|
|
266
|
-
'$
|
|
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)',
|
|
267
415
|
'$form.Controls.Add($titleLabel)',
|
|
268
|
-
'$form.Controls.Add($
|
|
416
|
+
'$form.Controls.Add($headlineLabel)',
|
|
417
|
+
'$form.Controls.Add($detailLabel)',
|
|
269
418
|
'$timer = New-Object System.Windows.Forms.Timer',
|
|
270
|
-
'$timer.Interval =
|
|
419
|
+
'$timer.Interval = 4800',
|
|
271
420
|
'$timer.Add_Tick({ $timer.Stop(); $form.Close() })',
|
|
272
421
|
'$timer.Start()',
|
|
273
|
-
'[void]$form.ShowDialog()'
|
|
422
|
+
'[void]$form.ShowDialog()',
|
|
423
|
+
'}'
|
|
274
424
|
].join('; ');
|
|
275
|
-
return
|
|
425
|
+
return script;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function runWindowsPowerShellCommand(command) {
|
|
429
|
+
// Invoke PowerShell directly so the styled popup script doesn't hit cmd.exe's 8191-char limit.
|
|
430
|
+
execFileSync('powershell', ['-NoProfile', '-STA', '-Command', command], {
|
|
431
|
+
stdio: 'ignore',
|
|
432
|
+
windowsHide: true
|
|
433
|
+
});
|
|
276
434
|
}
|
|
277
435
|
|
|
278
436
|
function generateNotifyScript(feishu = {}) {
|
|
@@ -284,7 +442,7 @@ const fs = require('fs')
|
|
|
284
442
|
const os = require('os')
|
|
285
443
|
const http = require('http')
|
|
286
444
|
const https = require('https')
|
|
287
|
-
const { execSync } = require('child_process')
|
|
445
|
+
const { execSync, execFileSync } = require('child_process')
|
|
288
446
|
|
|
289
447
|
const FEISHU_ENABLED = ${feishuEnabled ? 'true' : 'false'}
|
|
290
448
|
const FEISHU_WEBHOOK_URL = ${JSON.stringify(feishuEnabled ? feishu.webhookUrl : '')}
|
|
@@ -379,10 +537,13 @@ function notify(mode, message) {
|
|
|
379
537
|
const ps = "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('" +
|
|
380
538
|
escapeForPowerShellSingleQuote(message) + "', '" +
|
|
381
539
|
escapeForPowerShellSingleQuote(title) + "', 'OK', 'Information')"
|
|
382
|
-
|
|
383
|
-
|
|
540
|
+
try {
|
|
541
|
+
runWindowsPowerShellCommand(ps)
|
|
542
|
+
} catch (dialogError) {
|
|
543
|
+
runWindowsPowerShellCommand(popupCommand)
|
|
544
|
+
}
|
|
384
545
|
} else {
|
|
385
|
-
|
|
546
|
+
runWindowsPowerShellCommand(popupCommand)
|
|
386
547
|
}
|
|
387
548
|
return
|
|
388
549
|
}
|
|
@@ -499,40 +660,198 @@ function escapeForXml(value) {
|
|
|
499
660
|
|
|
500
661
|
function buildWindowsPopupCommand(title, message) {
|
|
501
662
|
const script = [
|
|
663
|
+
"$ErrorActionPreference = 'Stop'",
|
|
664
|
+
\`$titleText = '\${escapeForPowerShellSingleQuote(title)}'\`,
|
|
665
|
+
\`$messageText = '\${escapeForPowerShellSingleQuote(message)}'\`,
|
|
666
|
+
"$segments = $messageText -split '\\\\s*\\\\|\\\\s*', 2",
|
|
667
|
+
"$headlineText = if ($segments.Length -ge 1) { $segments[0] } else { $messageText }",
|
|
668
|
+
"$detailText = if ($segments.Length -ge 2) { $segments[1] } else { '' }",
|
|
669
|
+
'try {',
|
|
670
|
+
'Add-Type -AssemblyName PresentationFramework',
|
|
671
|
+
'Add-Type -AssemblyName PresentationCore',
|
|
672
|
+
'Add-Type -AssemblyName WindowsBase',
|
|
673
|
+
'$brushConverter = New-Object System.Windows.Media.BrushConverter',
|
|
674
|
+
'$window = New-Object System.Windows.Window',
|
|
675
|
+
'$window.Width = 372',
|
|
676
|
+
'$window.Height = 118',
|
|
677
|
+
'$window.WindowStyle = [System.Windows.WindowStyle]::None',
|
|
678
|
+
'$window.ResizeMode = [System.Windows.ResizeMode]::NoResize',
|
|
679
|
+
'$window.AllowsTransparency = $true',
|
|
680
|
+
'$window.Background = [System.Windows.Media.Brushes]::Transparent',
|
|
681
|
+
'$window.ShowInTaskbar = $false',
|
|
682
|
+
'$window.Topmost = $true',
|
|
683
|
+
'$window.ShowActivated = $false',
|
|
684
|
+
'$window.Opacity = 0',
|
|
685
|
+
'$window.WindowStartupLocation = [System.Windows.WindowStartupLocation]::Manual',
|
|
686
|
+
'$root = New-Object System.Windows.Controls.Border',
|
|
687
|
+
'$root.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 20',
|
|
688
|
+
'$root.Padding = New-Object System.Windows.Thickness -ArgumentList 16',
|
|
689
|
+
"$root.Background = $brushConverter.ConvertFromString('#F3F4F7')",
|
|
690
|
+
"$root.BorderBrush = $brushConverter.ConvertFromString('#D9DBE3')",
|
|
691
|
+
'$root.BorderThickness = New-Object System.Windows.Thickness -ArgumentList 1',
|
|
692
|
+
'$shadow = New-Object System.Windows.Media.Effects.DropShadowEffect',
|
|
693
|
+
"$shadow.Color = [System.Windows.Media.ColorConverter]::ConvertFromString('#22000000')",
|
|
694
|
+
'$shadow.BlurRadius = 26',
|
|
695
|
+
'$shadow.ShadowDepth = 0',
|
|
696
|
+
'$shadow.Opacity = 0.85',
|
|
697
|
+
'$root.Effect = $shadow',
|
|
698
|
+
'$grid = New-Object System.Windows.Controls.Grid',
|
|
699
|
+
'$iconColumn = New-Object System.Windows.Controls.ColumnDefinition',
|
|
700
|
+
'$iconColumn.Width = New-Object System.Windows.GridLength -ArgumentList 44',
|
|
701
|
+
'$contentColumn = New-Object System.Windows.Controls.ColumnDefinition',
|
|
702
|
+
'$grid.ColumnDefinitions.Add($iconColumn) | Out-Null',
|
|
703
|
+
'$grid.ColumnDefinitions.Add($contentColumn) | Out-Null',
|
|
704
|
+
'$iconHolder = New-Object System.Windows.Controls.Border',
|
|
705
|
+
'$iconHolder.Width = 34',
|
|
706
|
+
'$iconHolder.Height = 34',
|
|
707
|
+
'$iconHolder.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left',
|
|
708
|
+
'$iconHolder.VerticalAlignment = [System.Windows.VerticalAlignment]::Top',
|
|
709
|
+
'$iconHolder.CornerRadius = New-Object System.Windows.CornerRadius -ArgumentList 17',
|
|
710
|
+
"$iconHolder.Background = $brushConverter.ConvertFromString('#DDEBFF')",
|
|
711
|
+
'$iconGlyph = New-Object System.Windows.Controls.TextBlock',
|
|
712
|
+
'$iconGlyph.Text = [char]0x2713',
|
|
713
|
+
'$iconGlyph.FontSize = 18',
|
|
714
|
+
'$iconGlyph.FontWeight = [System.Windows.FontWeights]::Bold',
|
|
715
|
+
"$iconGlyph.Foreground = $brushConverter.ConvertFromString('#0A84FF')",
|
|
716
|
+
'$iconGlyph.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Center',
|
|
717
|
+
'$iconGlyph.VerticalAlignment = [System.Windows.VerticalAlignment]::Center',
|
|
718
|
+
'$iconHolder.Child = $iconGlyph',
|
|
719
|
+
'$contentStack = New-Object System.Windows.Controls.StackPanel',
|
|
720
|
+
'$contentStack.Orientation = [System.Windows.Controls.Orientation]::Vertical',
|
|
721
|
+
'$contentStack.Margin = New-Object System.Windows.Thickness -ArgumentList 4,0,0,0',
|
|
722
|
+
'$metaGrid = New-Object System.Windows.Controls.Grid',
|
|
723
|
+
'$metaGrid.ColumnDefinitions.Add((New-Object System.Windows.Controls.ColumnDefinition)) | Out-Null',
|
|
724
|
+
'$timeColumn = New-Object System.Windows.Controls.ColumnDefinition',
|
|
725
|
+
'$timeColumn.Width = [System.Windows.GridLength]::Auto',
|
|
726
|
+
'$metaGrid.ColumnDefinitions.Add($timeColumn) | Out-Null',
|
|
727
|
+
'$appLabel = New-Object System.Windows.Controls.TextBlock',
|
|
728
|
+
'$appLabel.Text = $titleText',
|
|
729
|
+
'$appLabel.FontSize = 11.5',
|
|
730
|
+
'$appLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
|
|
731
|
+
"$appLabel.Foreground = $brushConverter.ConvertFromString('#6B6C73')",
|
|
732
|
+
'$appLabel.TextTrimming = [System.Windows.TextTrimming]::CharacterEllipsis',
|
|
733
|
+
'$stampLabel = New-Object System.Windows.Controls.TextBlock',
|
|
734
|
+
"$stampLabel.Text = '刚刚'",
|
|
735
|
+
'$stampLabel.FontSize = 11',
|
|
736
|
+
"$stampLabel.Foreground = $brushConverter.ConvertFromString('#8D8E95')",
|
|
737
|
+
'$stampLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 12,0,0,0',
|
|
738
|
+
'[System.Windows.Controls.Grid]::SetColumn($stampLabel, 1)',
|
|
739
|
+
'$metaGrid.Children.Add($appLabel) | Out-Null',
|
|
740
|
+
'$metaGrid.Children.Add($stampLabel) | Out-Null',
|
|
741
|
+
'$headlineLabel = New-Object System.Windows.Controls.TextBlock',
|
|
742
|
+
'$headlineLabel.Text = $headlineText',
|
|
743
|
+
'$headlineLabel.FontSize = 14',
|
|
744
|
+
'$headlineLabel.FontWeight = [System.Windows.FontWeights]::SemiBold',
|
|
745
|
+
"$headlineLabel.Foreground = $brushConverter.ConvertFromString('#1F2024')",
|
|
746
|
+
'$headlineLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
|
|
747
|
+
'$headlineLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,6,0,0',
|
|
748
|
+
'$detailLabel = New-Object System.Windows.Controls.TextBlock',
|
|
749
|
+
'$detailLabel.Text = $detailText',
|
|
750
|
+
'$detailLabel.FontSize = 12',
|
|
751
|
+
"$detailLabel.Foreground = $brushConverter.ConvertFromString('#5F6168')",
|
|
752
|
+
'$detailLabel.TextWrapping = [System.Windows.TextWrapping]::Wrap',
|
|
753
|
+
'$detailLabel.Margin = New-Object System.Windows.Thickness -ArgumentList 0,4,0,0',
|
|
754
|
+
'$detailLabel.Visibility = if ([string]::IsNullOrWhiteSpace($detailText)) { [System.Windows.Visibility]::Collapsed } else { [System.Windows.Visibility]::Visible }',
|
|
755
|
+
'$contentStack.Children.Add($metaGrid) | Out-Null',
|
|
756
|
+
'$contentStack.Children.Add($headlineLabel) | Out-Null',
|
|
757
|
+
'$contentStack.Children.Add($detailLabel) | Out-Null',
|
|
758
|
+
'[System.Windows.Controls.Grid]::SetColumn($contentStack, 1)',
|
|
759
|
+
'$grid.Children.Add($iconHolder) | Out-Null',
|
|
760
|
+
'$grid.Children.Add($contentStack) | Out-Null',
|
|
761
|
+
'$root.Child = $grid',
|
|
762
|
+
'$window.Content = $root',
|
|
763
|
+
'$workArea = [System.Windows.SystemParameters]::WorkArea',
|
|
764
|
+
'$window.Left = $workArea.Right - $window.Width - 18',
|
|
765
|
+
'$window.Top = $workArea.Top + 18',
|
|
766
|
+
'$frame = New-Object System.Windows.Threading.DispatcherFrame',
|
|
767
|
+
'$window.Add_Closed({ $frame.Continue = $false })',
|
|
768
|
+
'$closeTimer = New-Object System.Windows.Threading.DispatcherTimer',
|
|
769
|
+
'$closeTimer.Interval = [TimeSpan]::FromMilliseconds(4600)',
|
|
770
|
+
'$closeTimer.Add_Tick({',
|
|
771
|
+
' $closeTimer.Stop()',
|
|
772
|
+
' $fadeOut = New-Object System.Windows.Media.Animation.DoubleAnimation',
|
|
773
|
+
' $fadeOut.From = $window.Opacity',
|
|
774
|
+
' $fadeOut.To = 0',
|
|
775
|
+
' $fadeOut.Duration = [TimeSpan]::FromMilliseconds(220)',
|
|
776
|
+
' $fadeOut.Add_Completed({ $window.Close() })',
|
|
777
|
+
' $window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeOut)',
|
|
778
|
+
'})',
|
|
779
|
+
'$window.Show()',
|
|
780
|
+
'$fadeIn = New-Object System.Windows.Media.Animation.DoubleAnimation',
|
|
781
|
+
'$fadeIn.From = 0',
|
|
782
|
+
'$fadeIn.To = 1',
|
|
783
|
+
'$fadeIn.Duration = [TimeSpan]::FromMilliseconds(180)',
|
|
784
|
+
'$window.BeginAnimation([System.Windows.Window]::OpacityProperty, $fadeIn)',
|
|
785
|
+
'$closeTimer.Start()',
|
|
786
|
+
'[System.Windows.Threading.Dispatcher]::PushFrame($frame)',
|
|
787
|
+
'} catch {',
|
|
502
788
|
'Add-Type -AssemblyName System.Windows.Forms',
|
|
503
789
|
'Add-Type -AssemblyName System.Drawing',
|
|
504
790
|
'$form = New-Object System.Windows.Forms.Form',
|
|
505
|
-
|
|
791
|
+
'$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::None',
|
|
792
|
+
'$form.BackColor = [System.Drawing.Color]::FromArgb(247, 247, 250)',
|
|
506
793
|
'$form.Width = 360',
|
|
507
|
-
'$form.Height =
|
|
794
|
+
'$form.Height = 118',
|
|
508
795
|
'$form.StartPosition = [System.Windows.Forms.FormStartPosition]::Manual',
|
|
509
|
-
'$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedToolWindow',
|
|
510
796
|
'$form.ShowInTaskbar = $false',
|
|
511
797
|
'$form.TopMost = $true',
|
|
512
798
|
'$form.MaximizeBox = $false',
|
|
513
799
|
'$form.MinimizeBox = $false',
|
|
514
800
|
'$workingArea = [System.Windows.Forms.Screen]::PrimaryScreen.WorkingArea',
|
|
515
|
-
'$form.Location = New-Object System.Drawing.Point(($workingArea.Right - $form.Width -
|
|
801
|
+
'$form.Location = New-Object System.Drawing.Point(($workingArea.Right - $form.Width - 18), ($workingArea.Top + 18))',
|
|
802
|
+
'$iconPanel = New-Object System.Windows.Forms.Panel',
|
|
803
|
+
'$iconPanel.Width = 34',
|
|
804
|
+
'$iconPanel.Height = 34',
|
|
805
|
+
'$iconPanel.BackColor = [System.Drawing.Color]::FromArgb(221, 235, 255)',
|
|
806
|
+
'$iconPanel.Location = New-Object System.Drawing.Point(16, 18)',
|
|
807
|
+
'$iconLabel = New-Object System.Windows.Forms.Label',
|
|
808
|
+
'$iconLabel.Text = [char]0x2713',
|
|
809
|
+
"$iconLabel.Font = New-Object System.Drawing.Font('Segoe UI', 14, [System.Drawing.FontStyle]::Bold)",
|
|
810
|
+
'$iconLabel.ForeColor = [System.Drawing.Color]::FromArgb(10, 132, 255)',
|
|
811
|
+
'$iconLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter',
|
|
812
|
+
'$iconLabel.Dock = [System.Windows.Forms.DockStyle]::Fill',
|
|
813
|
+
'$iconPanel.Controls.Add($iconLabel)',
|
|
516
814
|
'$titleLabel = New-Object System.Windows.Forms.Label',
|
|
517
|
-
|
|
518
|
-
"
|
|
815
|
+
'$titleLabel.Text = $titleText',
|
|
816
|
+
"$titleLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9, [System.Drawing.FontStyle]::Bold)",
|
|
817
|
+
'$titleLabel.ForeColor = [System.Drawing.Color]::FromArgb(109, 110, 115)',
|
|
519
818
|
'$titleLabel.AutoSize = $true',
|
|
520
|
-
'$titleLabel.Location = New-Object System.Drawing.Point(
|
|
521
|
-
'$
|
|
522
|
-
|
|
523
|
-
"
|
|
524
|
-
'$
|
|
525
|
-
'$
|
|
526
|
-
'$
|
|
819
|
+
'$titleLabel.Location = New-Object System.Drawing.Point(60, 16)',
|
|
820
|
+
'$headlineLabel = New-Object System.Windows.Forms.Label',
|
|
821
|
+
'$headlineLabel.Text = $headlineText',
|
|
822
|
+
"$headlineLabel.Font = New-Object System.Drawing.Font('Segoe UI', 10.5, [System.Drawing.FontStyle]::Bold)",
|
|
823
|
+
'$headlineLabel.ForeColor = [System.Drawing.Color]::FromArgb(31, 32, 36)',
|
|
824
|
+
'$headlineLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
|
|
825
|
+
'$headlineLabel.AutoSize = $true',
|
|
826
|
+
'$headlineLabel.Location = New-Object System.Drawing.Point(60, 36)',
|
|
827
|
+
'$detailLabel = New-Object System.Windows.Forms.Label',
|
|
828
|
+
'$detailLabel.Text = $detailText',
|
|
829
|
+
"$detailLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9)",
|
|
830
|
+
'$detailLabel.ForeColor = [System.Drawing.Color]::FromArgb(95, 97, 104)',
|
|
831
|
+
'$detailLabel.MaximumSize = New-Object System.Drawing.Size(272, 0)',
|
|
832
|
+
'$detailLabel.AutoSize = $true',
|
|
833
|
+
'$detailLabel.Location = New-Object System.Drawing.Point(60, 60)',
|
|
834
|
+
'$detailLabel.Visible = -not [string]::IsNullOrWhiteSpace($detailText)',
|
|
835
|
+
'$form.Controls.Add($iconPanel)',
|
|
527
836
|
'$form.Controls.Add($titleLabel)',
|
|
528
|
-
'$form.Controls.Add($
|
|
837
|
+
'$form.Controls.Add($headlineLabel)',
|
|
838
|
+
'$form.Controls.Add($detailLabel)',
|
|
529
839
|
'$timer = New-Object System.Windows.Forms.Timer',
|
|
530
|
-
'$timer.Interval =
|
|
840
|
+
'$timer.Interval = 4800',
|
|
531
841
|
'$timer.Add_Tick({ $timer.Stop(); $form.Close() })',
|
|
532
842
|
'$timer.Start()',
|
|
533
|
-
'[void]$form.ShowDialog()'
|
|
843
|
+
'[void]$form.ShowDialog()',
|
|
844
|
+
'}'
|
|
534
845
|
].join('; ')
|
|
535
|
-
return
|
|
846
|
+
return script
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function runWindowsPowerShellCommand(command) {
|
|
850
|
+
// Invoke PowerShell directly so the styled popup script doesn't hit cmd.exe's 8191-char limit.
|
|
851
|
+
execFileSync('powershell', ['-NoProfile', '-STA', '-Command', command], {
|
|
852
|
+
stdio: 'ignore',
|
|
853
|
+
windowsHide: true
|
|
854
|
+
})
|
|
536
855
|
}
|
|
537
856
|
`;
|
|
538
857
|
}
|
|
@@ -900,6 +1219,205 @@ function saveNotificationSettings(input = {}) {
|
|
|
900
1219
|
return getNotificationSettings();
|
|
901
1220
|
}
|
|
902
1221
|
|
|
1222
|
+
function parseNotifyTypeMarker(command) {
|
|
1223
|
+
const marker = String(command || '').match(/--cc-notify-type=(['"])?(dialog|notification)\1/i);
|
|
1224
|
+
return marker?.[2] ? normalizeType(marker[2].toLowerCase()) : null;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
function getStopHookCommand(settings = {}) {
|
|
1228
|
+
const hooks = settings?.hooks?.Stop;
|
|
1229
|
+
if (!Array.isArray(hooks) || hooks.length === 0) {
|
|
1230
|
+
return '';
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
let fallbackCommand = '';
|
|
1234
|
+
for (const group of hooks) {
|
|
1235
|
+
const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1236
|
+
for (const hook of groupHooks) {
|
|
1237
|
+
const command = String(hook?.command || '');
|
|
1238
|
+
if (!command) continue;
|
|
1239
|
+
if (!fallbackCommand) {
|
|
1240
|
+
fallbackCommand = command;
|
|
1241
|
+
}
|
|
1242
|
+
if (command.includes('notify-hook.js')) {
|
|
1243
|
+
return command;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
return fallbackCommand;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
function normalizePathForCompare(rawPath) {
|
|
1252
|
+
return String(rawPath || '').replace(/\\/g, '/');
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
function shouldRepairStopHook(settings, expectedScriptPath = PATHS.notifyHook, fileExists = fs.existsSync) {
|
|
1256
|
+
const command = getStopHookCommand(settings);
|
|
1257
|
+
if (!command || !command.includes('notify-hook.js')) {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const normalizedCommand = normalizePathForCompare(command);
|
|
1262
|
+
const normalizedExpected = normalizePathForCompare(expectedScriptPath);
|
|
1263
|
+
if (!normalizedCommand.includes(normalizedExpected)) {
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
const markerType = parseNotifyTypeMarker(command);
|
|
1268
|
+
if (!markerType) {
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
return !fileExists(expectedScriptPath);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
function buildStopHookCommand(type) {
|
|
1276
|
+
return buildClaudeCommand(type);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
function parseStopHookStatus(settings = {}) {
|
|
1280
|
+
const stopGroups = Array.isArray(settings?.hooks?.Stop) ? settings.hooks.Stop : [];
|
|
1281
|
+
if (stopGroups.length === 0) {
|
|
1282
|
+
return { enabled: false, type: 'notification' };
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
let sawNotificationLikeCommand = false;
|
|
1286
|
+
for (const group of stopGroups) {
|
|
1287
|
+
const groupHooks = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
1288
|
+
for (const hook of groupHooks) {
|
|
1289
|
+
const command = String(hook?.command || '');
|
|
1290
|
+
if (!command) continue;
|
|
1291
|
+
|
|
1292
|
+
const markerType = parseNotifyTypeMarker(command) || parseManagedType(command);
|
|
1293
|
+
if (markerType) {
|
|
1294
|
+
return { enabled: true, type: markerType };
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
const isDialog = command.includes('display dialog') ||
|
|
1298
|
+
command.includes('MessageBox') ||
|
|
1299
|
+
command.includes('zenity --info');
|
|
1300
|
+
if (isDialog) {
|
|
1301
|
+
return { enabled: true, type: 'dialog' };
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
const isNotification = command.includes('display notification') ||
|
|
1305
|
+
command.includes('Popup') ||
|
|
1306
|
+
command.includes('GraphicsPath') ||
|
|
1307
|
+
command.includes('TransparencyKey') ||
|
|
1308
|
+
command.includes('FormBorderStyle]::None') ||
|
|
1309
|
+
command.includes('AllowsTransparency') ||
|
|
1310
|
+
command.includes('notify-send') ||
|
|
1311
|
+
command.includes('ToastNotificationManager') ||
|
|
1312
|
+
command.includes('CreateToastNotifier') ||
|
|
1313
|
+
command.includes('notify-hook.js');
|
|
1314
|
+
if (isNotification) {
|
|
1315
|
+
sawNotificationLikeCommand = true;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
return sawNotificationLikeCommand
|
|
1321
|
+
? { enabled: true, type: 'notification' }
|
|
1322
|
+
: { enabled: false, type: 'notification' };
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function normalizeSavedPlatformStatus(platform = {}) {
|
|
1326
|
+
return {
|
|
1327
|
+
enabled: platform?.enabled === true,
|
|
1328
|
+
type: normalizeType(platform?.type)
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
function buildLegacyClaudeSaveInput(input = {}, currentSettings = getNotificationSettings()) {
|
|
1333
|
+
return {
|
|
1334
|
+
platforms: {
|
|
1335
|
+
claude: input.stopHook !== undefined
|
|
1336
|
+
? normalizePlatformInput(input.stopHook)
|
|
1337
|
+
: { enabled: false, type: 'notification' },
|
|
1338
|
+
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
1339
|
+
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
1340
|
+
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
|
|
1341
|
+
},
|
|
1342
|
+
feishu: input.feishu !== undefined
|
|
1343
|
+
? {
|
|
1344
|
+
enabled: input.feishu?.enabled === true,
|
|
1345
|
+
webhookUrl: input.feishu?.webhookUrl || ''
|
|
1346
|
+
}
|
|
1347
|
+
: {
|
|
1348
|
+
enabled: currentSettings?.feishu?.enabled === true,
|
|
1349
|
+
webhookUrl: currentSettings?.feishu?.webhookUrl || ''
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
function getLegacyClaudeHookSettings() {
|
|
1355
|
+
return {
|
|
1356
|
+
success: true,
|
|
1357
|
+
stopHook: parseStopHookStatus(readClaudeSettings()),
|
|
1358
|
+
feishu: getFeishuConfig(),
|
|
1359
|
+
platform: os.platform()
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
function saveLegacyClaudeHookSettings(input = {}) {
|
|
1364
|
+
const currentSettings = getNotificationSettings();
|
|
1365
|
+
saveNotificationSettings(buildLegacyClaudeSaveInput(input, currentSettings));
|
|
1366
|
+
return getLegacyClaudeHookSettings();
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function initDefaultHooks() {
|
|
1370
|
+
try {
|
|
1371
|
+
const uiConfig = loadUIConfig();
|
|
1372
|
+
if (uiConfig.claudeNotificationDisabledByUser === true) {
|
|
1373
|
+
console.log('[Claude Hooks] 用户已主动关闭通知,跳过自动初始化');
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
const currentClaudeSettings = readClaudeSettings();
|
|
1378
|
+
const currentStatus = parseStopHookStatus(currentClaudeSettings);
|
|
1379
|
+
|
|
1380
|
+
if (currentStatus.enabled) {
|
|
1381
|
+
if (shouldRepairStopHook(currentClaudeSettings)) {
|
|
1382
|
+
const currentSettings = getNotificationSettings();
|
|
1383
|
+
saveNotificationSettings({
|
|
1384
|
+
platforms: {
|
|
1385
|
+
claude: { enabled: true, type: currentStatus.type || 'notification' },
|
|
1386
|
+
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
1387
|
+
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
1388
|
+
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
|
|
1389
|
+
},
|
|
1390
|
+
feishu: {
|
|
1391
|
+
enabled: currentSettings?.feishu?.enabled === true,
|
|
1392
|
+
webhookUrl: currentSettings?.feishu?.webhookUrl || ''
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
console.log('[Claude Hooks] 检测到旧版 Stop hook 路径,已自动修复');
|
|
1396
|
+
} else {
|
|
1397
|
+
console.log('[Claude Hooks] 已存在 Stop hook 配置,跳过初始化');
|
|
1398
|
+
}
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
const currentSettings = getNotificationSettings();
|
|
1403
|
+
saveNotificationSettings({
|
|
1404
|
+
platforms: {
|
|
1405
|
+
claude: { enabled: true, type: 'notification' },
|
|
1406
|
+
codex: normalizeSavedPlatformStatus(currentSettings?.platforms?.codex),
|
|
1407
|
+
gemini: normalizeSavedPlatformStatus(currentSettings?.platforms?.gemini),
|
|
1408
|
+
opencode: normalizeSavedPlatformStatus(currentSettings?.platforms?.opencode)
|
|
1409
|
+
},
|
|
1410
|
+
feishu: {
|
|
1411
|
+
enabled: currentSettings?.feishu?.enabled === true,
|
|
1412
|
+
webhookUrl: currentSettings?.feishu?.webhookUrl || ''
|
|
1413
|
+
}
|
|
1414
|
+
});
|
|
1415
|
+
console.log('[Claude Hooks] 已自动开启任务完成通知(右上角卡片)');
|
|
1416
|
+
} catch (error) {
|
|
1417
|
+
console.error('[Claude Hooks] 初始化默认配置失败:', error);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
903
1421
|
function sendFeishuTest(webhookUrl) {
|
|
904
1422
|
return new Promise((resolve, reject) => {
|
|
905
1423
|
try {
|
|
@@ -965,7 +1483,7 @@ function generateSystemNotificationCommand(type, message, platformOverride = os.
|
|
|
965
1483
|
if (platform === 'win32') {
|
|
966
1484
|
const popupCommand = buildWindowsPopupCommand(title, message);
|
|
967
1485
|
if (normalizedType === 'dialog') {
|
|
968
|
-
return `powershell -NoProfile -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('${escapeForPowerShellSingleQuote(message)}', '${escapeForPowerShellSingleQuote(title)}', 'OK', 'Information')" || ${popupCommand}`;
|
|
1486
|
+
return `powershell -NoProfile -STA -Command "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('${escapeForPowerShellSingleQuote(message)}', '${escapeForPowerShellSingleQuote(title)}', 'OK', 'Information')" || ${popupCommand}`;
|
|
969
1487
|
}
|
|
970
1488
|
return popupCommand;
|
|
971
1489
|
}
|
|
@@ -977,19 +1495,89 @@ function generateSystemNotificationCommand(type, message, platformOverride = os.
|
|
|
977
1495
|
return `notify-send "Coding Tool" "${String(message || '').replace(/"/g, '\\"')}"`;
|
|
978
1496
|
}
|
|
979
1497
|
|
|
1498
|
+
function runSystemNotification(type, message, platformOverride = os.platform()) {
|
|
1499
|
+
const normalizedType = normalizeType(type);
|
|
1500
|
+
const title = 'Coding Tool';
|
|
1501
|
+
const platform = platformOverride;
|
|
1502
|
+
|
|
1503
|
+
if (platform === 'darwin') {
|
|
1504
|
+
if (normalizedType === 'dialog') {
|
|
1505
|
+
const appleScript = 'display dialog "' + escapeForAppleScript(message) +
|
|
1506
|
+
'" with title "' + escapeForAppleScript(title) +
|
|
1507
|
+
'" buttons {"好的"} default button 1 with icon note';
|
|
1508
|
+
execSync('osascript -e ' + JSON.stringify(appleScript), { stdio: 'ignore', windowsHide: true });
|
|
1509
|
+
} else {
|
|
1510
|
+
const fallbackScript = 'display notification "' + escapeForAppleScript(message) +
|
|
1511
|
+
'" with title "' + escapeForAppleScript(title) + '" sound name "Glass"';
|
|
1512
|
+
const command = 'if command -v terminal-notifier >/dev/null 2>&1; then ' +
|
|
1513
|
+
'terminal-notifier -title ' + JSON.stringify(title) +
|
|
1514
|
+
' -message ' + JSON.stringify(message) +
|
|
1515
|
+
' -sound Glass -activate com.apple.Terminal; ' +
|
|
1516
|
+
'else osascript -e ' + JSON.stringify(fallbackScript) + '; fi';
|
|
1517
|
+
execSync(command, { stdio: 'ignore', windowsHide: true });
|
|
1518
|
+
}
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
if (platform === 'win32') {
|
|
1523
|
+
const popupCommand = buildWindowsPopupCommand(title, message);
|
|
1524
|
+
if (normalizedType === 'dialog') {
|
|
1525
|
+
const dialogScript = "Add-Type -AssemblyName PresentationFramework; [System.Windows.MessageBox]::Show('" +
|
|
1526
|
+
escapeForPowerShellSingleQuote(message) + "', '" +
|
|
1527
|
+
escapeForPowerShellSingleQuote(title) + "', 'OK', 'Information')";
|
|
1528
|
+
try {
|
|
1529
|
+
runWindowsPowerShellCommand(dialogScript);
|
|
1530
|
+
} catch (dialogError) {
|
|
1531
|
+
runWindowsPowerShellCommand(popupCommand);
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
runWindowsPowerShellCommand(popupCommand);
|
|
1535
|
+
}
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
const escapedMessage = String(message || '').replace(/"/g, '\\"');
|
|
1540
|
+
if (normalizedType === 'dialog') {
|
|
1541
|
+
execSync(
|
|
1542
|
+
`zenity --info --title="Coding Tool" --text="${escapedMessage}" 2>/dev/null || notify-send "Coding Tool" "${escapedMessage}"`,
|
|
1543
|
+
{ stdio: 'ignore', windowsHide: true }
|
|
1544
|
+
);
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
execSync(`notify-send "Coding Tool" "${escapedMessage}"`, { stdio: 'ignore', windowsHide: true });
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
function syncManagedNotificationAssets() {
|
|
1552
|
+
const settings = getNotificationSettings();
|
|
1553
|
+
const hasManagedPlatform = Object.values(settings?.platforms || {}).some((platform) => platform?.enabled === true);
|
|
1554
|
+
|
|
1555
|
+
if (hasManagedPlatform) {
|
|
1556
|
+
writeNotifyScript(settings.feishu || {});
|
|
1557
|
+
} else {
|
|
1558
|
+
removeNotifyScript();
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
return settings;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
980
1564
|
function testNotification({ type, testFeishu, webhookUrl } = {}) {
|
|
981
1565
|
if (testFeishu && webhookUrl) {
|
|
982
1566
|
return sendFeishuTest(webhookUrl);
|
|
983
1567
|
}
|
|
984
1568
|
|
|
985
|
-
|
|
1569
|
+
runSystemNotification(type || 'notification', '这是一条测试通知');
|
|
986
1570
|
}
|
|
987
1571
|
|
|
988
1572
|
module.exports = {
|
|
989
1573
|
MANAGED_HOOK_NAME,
|
|
990
1574
|
getNotificationSettings,
|
|
1575
|
+
getLegacyClaudeHookSettings,
|
|
991
1576
|
saveNotificationSettings,
|
|
1577
|
+
saveLegacyClaudeHookSettings,
|
|
992
1578
|
testNotification,
|
|
1579
|
+
initDefaultHooks,
|
|
1580
|
+
syncManagedNotificationAssets,
|
|
993
1581
|
getOpenCodeManagedPluginPath,
|
|
994
1582
|
buildOpenCodePluginContent,
|
|
995
1583
|
buildCodexNotifyCommand,
|
|
@@ -999,6 +1587,7 @@ module.exports = {
|
|
|
999
1587
|
applyClaudeDisablePreference,
|
|
1000
1588
|
getManagedCommandType,
|
|
1001
1589
|
parseManagedType,
|
|
1590
|
+
parseNotifyTypeMarker,
|
|
1002
1591
|
getClaudeHookStatus,
|
|
1003
1592
|
getCodexHookStatus,
|
|
1004
1593
|
getGeminiHookStatus,
|
|
@@ -1009,10 +1598,13 @@ module.exports = {
|
|
|
1009
1598
|
validateFeishuWebhookUrl,
|
|
1010
1599
|
buildCodexNotifyCommand,
|
|
1011
1600
|
buildGeminiCommand,
|
|
1601
|
+
buildStopHookCommand,
|
|
1012
1602
|
buildClaudeCommand,
|
|
1013
1603
|
buildOpenCodePluginContent,
|
|
1014
1604
|
getOpenCodeManagedPluginPath,
|
|
1015
1605
|
generateNotifyScript,
|
|
1016
|
-
generateSystemNotificationCommand
|
|
1606
|
+
generateSystemNotificationCommand,
|
|
1607
|
+
parseStopHookStatus,
|
|
1608
|
+
shouldRepairStopHook
|
|
1017
1609
|
}
|
|
1018
1610
|
};
|