dsh-notify-yimit 0.1.4 → 0.1.5
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/lib/client.js +5 -13
- package/lib/index.js +104 -30
- package/lib/toast-host.ps1 +355 -133
- package/lib/typert.host.js +2 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -40,8 +40,6 @@ window.__ModuleLoader__.load({
|
|
|
40
40
|
'.dn-seg{display:flex;gap:2px;padding:2px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:520px}',
|
|
41
41
|
'.dn-seg-btn{flex:1;font:inherit;font-size:13px;color:var(--dsw-alias-label-secondary);border:0;border-radius:8px;padding:6px 10px;cursor:pointer;background:transparent;transition:background .15s ease,color .15s ease;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
42
42
|
'.dn-seg-btn:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}',
|
|
43
|
-
// 选中态用主题主色 + 反色文字:浅色模式下与背景对比也足够清晰(此前浅色下
|
|
44
|
-
// 白底+深灰与容器太接近,深色正常)。
|
|
45
43
|
'.dn-seg-btn[data-on="true"]{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-inverted);box-shadow:var(--dsw-shadow-lv1)}',
|
|
46
44
|
'.dn-seg-btn[data-on="true"]:hover{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-inverted)}',
|
|
47
45
|
'.dn-seg-btn:disabled{opacity:.45;cursor:not-allowed}',
|
|
@@ -52,17 +50,13 @@ window.__ModuleLoader__.load({
|
|
|
52
50
|
'.dn-collapse>.dn-collapse-body>.dn-block{min-width:0}',
|
|
53
51
|
'.dn-kind-list{display:flex;flex-direction:column;gap:8px}',
|
|
54
52
|
'.dn-kind-row{display:flex;align-items:center;gap:12px;padding:8px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1)}',
|
|
55
|
-
// 每类型的背景/文字/开关项右对齐(预览块留在左侧)
|
|
56
53
|
'.dn-kind-row>.dn-kind-field:first-of-type{margin-left:auto}',
|
|
57
|
-
// 样式预览:纯色块内嵌类型文字,始终用配置的背景/文字色(内联样式,不受主题影响)
|
|
58
54
|
'.dn-kind-preview{flex:none;min-width:72px;text-align:center;font-size:12px;font-weight:600;line-height:22px;padding:2px 10px;border-radius:6px;white-space:nowrap;box-shadow:inset 0 0 0 1px rgba(0,0,0,.08)}',
|
|
59
55
|
'.dn-kind-field{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--dsw-alias-label-tertiary);flex:none}',
|
|
60
56
|
'.dn-input{font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:5px 10px;outline:none;max-width:120px}',
|
|
61
57
|
'.dn-input:focus{border-color:var(--dsw-alias-state-business-primary)}',
|
|
62
58
|
'.dn-input[type=color]{padding:2px 4px;width:48px;height:28px;cursor:pointer;max-width:none}',
|
|
63
59
|
'.dn-input:disabled{opacity:.45;cursor:not-allowed}',
|
|
64
|
-
// 浏览器选择器:原生 select 的展开列表为系统绘制、无法贴合主题,改为自绘下拉,
|
|
65
|
-
// 触发器与弹层都走 --dsw-* 变量,深浅色模式下与设置页风格一致。
|
|
66
60
|
'.dn-bselect{position:relative;width:280px;max-width:100%;flex:none}',
|
|
67
61
|
'.dn-bselect-trigger{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:5px 10px;cursor:pointer;outline:none}',
|
|
68
62
|
'.dn-bselect-trigger:hover:not(:disabled){border-color:var(--dsw-alias-border-l2)}',
|
|
@@ -577,11 +571,8 @@ window.__ModuleLoader__.load({
|
|
|
577
571
|
}
|
|
578
572
|
|
|
579
573
|
if (config.mode === 'custom') {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
ack();
|
|
583
|
-
return
|
|
584
|
-
}
|
|
574
|
+
ack();
|
|
575
|
+
return
|
|
585
576
|
} else if (config.mode === 'system') {
|
|
586
577
|
if (typeof Notification === 'undefined' || Notification.permission ===
|
|
587
578
|
'denied') {
|
|
@@ -801,7 +792,8 @@ window.__ModuleLoader__.load({
|
|
|
801
792
|
const controlsDisabled = !config.enabled
|
|
802
793
|
|
|
803
794
|
const kindRows = KIND_ORDER.map((kind) => {
|
|
804
|
-
|
|
795
|
+
// 修正:去掉 `?.` 和 `[kind]` 之间的空格
|
|
796
|
+
const entry = config.colors?.[kind] ?? {}
|
|
805
797
|
const bg = entry.bg || config.bgColor || '#20272f'
|
|
806
798
|
const fg = entry.fg || config.textColor || '#e6edf3'
|
|
807
799
|
const enabled = entry.enabled !== false
|
|
@@ -1366,4 +1358,4 @@ window.__ModuleLoader__.load({
|
|
|
1366
1358
|
exports.inject = inject
|
|
1367
1359
|
return module.exports
|
|
1368
1360
|
},
|
|
1369
|
-
})
|
|
1361
|
+
})
|
package/lib/index.js
CHANGED
|
@@ -30,39 +30,42 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
30
30
|
bgColor: '#20272f',
|
|
31
31
|
textColor: '#e6edf3',
|
|
32
32
|
locale: 'en',
|
|
33
|
-
// 通知窗口样式:width 窗口宽度(编辑模式拖拽边缘调整);gap 通知间距(px);
|
|
34
|
-
// position = null 表示默认右下贴边,{left,top} 为任意屏幕位置(工作区相对坐标)。
|
|
35
33
|
toast: {
|
|
36
34
|
width: 340,
|
|
37
35
|
gap: 12,
|
|
38
|
-
position: null
|
|
36
|
+
position: null
|
|
39
37
|
},
|
|
40
38
|
colors: {
|
|
41
39
|
completed: {
|
|
42
40
|
bg: '#1f5138',
|
|
43
41
|
fg: '#e6f4ec',
|
|
42
|
+
accent: '#34d399',
|
|
44
43
|
enabled: true
|
|
45
|
-
},
|
|
44
|
+
}, // 翠绿
|
|
46
45
|
error: {
|
|
47
46
|
bg: '#5c1f24',
|
|
48
47
|
fg: '#fbe9ea',
|
|
48
|
+
accent: '#f87171',
|
|
49
49
|
enabled: true
|
|
50
|
-
},
|
|
50
|
+
}, // 珊瑚红
|
|
51
51
|
running: {
|
|
52
52
|
bg: '#203a5c',
|
|
53
53
|
fg: '#e8f0fb',
|
|
54
|
+
accent: '#60a5fa',
|
|
54
55
|
enabled: true
|
|
55
|
-
},
|
|
56
|
+
}, // 天蓝
|
|
56
57
|
approval: {
|
|
57
58
|
bg: '#4a3a12',
|
|
58
59
|
fg: '#f7efd8',
|
|
60
|
+
accent: '#fbbf24',
|
|
59
61
|
enabled: true
|
|
60
|
-
},
|
|
62
|
+
}, // 琥珀
|
|
61
63
|
question: {
|
|
62
64
|
bg: '#3d2a55',
|
|
63
65
|
fg: '#f0e8fa',
|
|
66
|
+
accent: '#a78bfa',
|
|
64
67
|
enabled: true
|
|
65
|
-
},
|
|
68
|
+
}, // 雾紫
|
|
66
69
|
},
|
|
67
70
|
})
|
|
68
71
|
|
|
@@ -137,7 +140,11 @@ function normalizeConfig(input) {
|
|
|
137
140
|
if (src.locale === 'zh' || src.locale === 'en') out.locale = src.locale
|
|
138
141
|
|
|
139
142
|
// 通知窗口样式:width 260–520;gap 0–60;position 缺省 null(默认右下贴边),合法 {left,top} 才采纳。
|
|
140
|
-
out.toast = {
|
|
143
|
+
out.toast = {
|
|
144
|
+
width: 340,
|
|
145
|
+
gap: 12,
|
|
146
|
+
position: null
|
|
147
|
+
}
|
|
141
148
|
const srcToast = src.toast !== null && typeof src.toast === 'object' && !Array.isArray(src.toast) ? src.toast : {}
|
|
142
149
|
if (typeof srcToast.width === 'number' && Number.isFinite(srcToast.width)) {
|
|
143
150
|
out.toast.width = Math.max(260, Math.min(520, Math.round(srcToast.width)))
|
|
@@ -149,7 +156,10 @@ function normalizeConfig(input) {
|
|
|
149
156
|
if (pos !== null && typeof pos === 'object' && !Array.isArray(pos) &&
|
|
150
157
|
typeof pos.left === 'number' && Number.isFinite(pos.left) &&
|
|
151
158
|
typeof pos.top === 'number' && Number.isFinite(pos.top)) {
|
|
152
|
-
out.toast.position = {
|
|
159
|
+
out.toast.position = {
|
|
160
|
+
left: Math.round(pos.left),
|
|
161
|
+
top: Math.round(pos.top)
|
|
162
|
+
}
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
const srcColors = src.colors !== null && typeof src.colors === 'object' && !Array.isArray(src.colors) ? src.colors :
|
|
@@ -161,6 +171,7 @@ function normalizeConfig(input) {
|
|
|
161
171
|
out.colors[kind] = {
|
|
162
172
|
bg: isHex(entry?.bg) ? entry.bg : def.bg,
|
|
163
173
|
fg: isHex(entry?.fg) ? entry.fg : def.fg,
|
|
174
|
+
accent: isHex(entry?.accent) ? entry.accent : def.accent, // 新增
|
|
164
175
|
enabled: entry?.enabled !== false,
|
|
165
176
|
}
|
|
166
177
|
}
|
|
@@ -201,7 +212,8 @@ function mergeConfigPatch(base, patch) {
|
|
|
201
212
|
if (patch !== null && typeof patch === 'object' && patch.toast !== null && typeof patch.toast === 'object' && !
|
|
202
213
|
Array.isArray(patch.toast)) {
|
|
203
214
|
out.toast = {
|
|
204
|
-
...(base.toast !== null && typeof base.toast === 'object' && !Array.isArray(base.toast) ? base.toast :
|
|
215
|
+
...(base.toast !== null && typeof base.toast === 'object' && !Array.isArray(base.toast) ? base.toast :
|
|
216
|
+
{}),
|
|
205
217
|
...patch.toast
|
|
206
218
|
}
|
|
207
219
|
}
|
|
@@ -258,7 +270,8 @@ function openConfig() {
|
|
|
258
270
|
fs.writeFileSync(tmp, JSON.stringify(config, null, 2), 'utf8')
|
|
259
271
|
fs.renameSync(tmp, file)
|
|
260
272
|
} catch {
|
|
261
|
-
/* 静默 */
|
|
273
|
+
/* 静默 */
|
|
274
|
+
}
|
|
262
275
|
},
|
|
263
276
|
}
|
|
264
277
|
}
|
|
@@ -489,7 +502,8 @@ export function apply(ctx) {
|
|
|
489
502
|
if (idx !== -1) toastOrder.splice(idx, 1)
|
|
490
503
|
reflowToasts()
|
|
491
504
|
}
|
|
492
|
-
} else if (msg.type === 'geometry' && typeof msg.left === 'number' && typeof msg.top ===
|
|
505
|
+
} else if (msg.type === 'geometry' && typeof msg.left === 'number' && typeof msg.top ===
|
|
506
|
+
'number') {
|
|
493
507
|
// 编辑模式:样板窗拖拽/缩放结束 → 写回配置,并应用到在屏浮窗。
|
|
494
508
|
const width = Math.max(260, Math.min(520, Math.round(Number(msg.width) || 340)))
|
|
495
509
|
const left = Math.round(Number(msg.left))
|
|
@@ -497,11 +511,18 @@ export function apply(ctx) {
|
|
|
497
511
|
store.set({
|
|
498
512
|
toast: {
|
|
499
513
|
width,
|
|
500
|
-
position: {
|
|
514
|
+
position: {
|
|
515
|
+
left,
|
|
516
|
+
top
|
|
517
|
+
}
|
|
501
518
|
}
|
|
502
519
|
})
|
|
503
520
|
for (const rec of activeToasts.values()) {
|
|
504
|
-
sendCmd({
|
|
521
|
+
sendCmd({
|
|
522
|
+
cmd: 'size',
|
|
523
|
+
key: rec.key,
|
|
524
|
+
width
|
|
525
|
+
})
|
|
505
526
|
}
|
|
506
527
|
reflowToasts()
|
|
507
528
|
}
|
|
@@ -585,9 +606,16 @@ export function apply(ctx) {
|
|
|
585
606
|
const needsTop = rec.meta !== null && newTop !== rec.meta.top
|
|
586
607
|
const needsLeft = rec.meta !== null && rec.meta.left !== pos.left
|
|
587
608
|
if (needsTop || needsLeft) {
|
|
588
|
-
const move = {
|
|
609
|
+
const move = {
|
|
610
|
+
cmd: 'move',
|
|
611
|
+
key: rec.key,
|
|
612
|
+
top: newTop
|
|
613
|
+
}
|
|
589
614
|
if (needsTop) rec.meta.top = newTop
|
|
590
|
-
if (needsLeft) {
|
|
615
|
+
if (needsLeft) {
|
|
616
|
+
move.left = pos.left;
|
|
617
|
+
rec.meta.left = pos.left
|
|
618
|
+
}
|
|
591
619
|
sendCmd(move)
|
|
592
620
|
}
|
|
593
621
|
prevTop = newTop
|
|
@@ -626,6 +654,23 @@ export function apply(ctx) {
|
|
|
626
654
|
return `http://127.0.0.1:${ws !== null && ws !== undefined && typeof ws.port === 'number' ? ws.port : 3080}`
|
|
627
655
|
}
|
|
628
656
|
|
|
657
|
+
const KIND_BADGES = {
|
|
658
|
+
zh: {
|
|
659
|
+
completed: '完成',
|
|
660
|
+
error: '出错',
|
|
661
|
+
running: '运行中',
|
|
662
|
+
approval: '待审批',
|
|
663
|
+
question: '待回答'
|
|
664
|
+
},
|
|
665
|
+
en: {
|
|
666
|
+
completed: 'Done',
|
|
667
|
+
error: 'Failed',
|
|
668
|
+
running: 'Running',
|
|
669
|
+
approval: 'Approval',
|
|
670
|
+
question: 'Question'
|
|
671
|
+
},
|
|
672
|
+
}
|
|
673
|
+
|
|
629
674
|
function showDesktopToast(cfg, ev) {
|
|
630
675
|
const maxVisible = Math.max(1, Math.min(20, Math.floor(Number(cfg.maxVisible) || 3)))
|
|
631
676
|
const stickyKind = ev.kind === 'running' || ev.kind === 'approval' || ev.kind === 'question'
|
|
@@ -691,6 +736,8 @@ export function apply(ctx) {
|
|
|
691
736
|
bg: colors?.bg ?? cfg.bgColor ?? '#20272f',
|
|
692
737
|
fg: colors?.fg ?? cfg.textColor ?? '#e6edf3',
|
|
693
738
|
durationSec: Math.max(1, Math.round((Number(cfg.durationMs) || 8000) / 1000)),
|
|
739
|
+
accent: colors?.accent ?? '#60a5fa', // 强调色
|
|
740
|
+
durationSec: Math.max(1, Math.round((Number(cfg.durationMs) || 8000) / 1000)),
|
|
694
741
|
sticky: stickyKind,
|
|
695
742
|
offsetY,
|
|
696
743
|
sessionId: String(ev.sessionId),
|
|
@@ -736,7 +783,11 @@ export function apply(ctx) {
|
|
|
736
783
|
if (typeof title !== 'string' || title.length === 0) return
|
|
737
784
|
for (const rec of activeToasts.values()) {
|
|
738
785
|
if (rec.sessionId === sessionId) {
|
|
739
|
-
sendCmd({
|
|
786
|
+
sendCmd({
|
|
787
|
+
cmd: 'title',
|
|
788
|
+
key: rec.key,
|
|
789
|
+
title
|
|
790
|
+
})
|
|
740
791
|
}
|
|
741
792
|
}
|
|
742
793
|
}
|
|
@@ -748,11 +799,13 @@ export function apply(ctx) {
|
|
|
748
799
|
cmd: 'shutdown'
|
|
749
800
|
})
|
|
750
801
|
} catch {
|
|
751
|
-
/* 忽略 */
|
|
802
|
+
/* 忽略 */
|
|
803
|
+
}
|
|
752
804
|
try {
|
|
753
805
|
host.kill()
|
|
754
806
|
} catch {
|
|
755
|
-
/* 已退出 */
|
|
807
|
+
/* 已退出 */
|
|
808
|
+
}
|
|
756
809
|
}
|
|
757
810
|
activeToasts.clear()
|
|
758
811
|
toastOrder.length = 0
|
|
@@ -768,7 +821,7 @@ export function apply(ctx) {
|
|
|
768
821
|
const at = Date.now()
|
|
769
822
|
const cfg = store.get()
|
|
770
823
|
// 每类型开关:该类型被关闭时完全不产生通知(桌面浮窗与系统通知都不发)。
|
|
771
|
-
if (cfg.colors?.[kind]?.enabled === false) return
|
|
824
|
+
if (cfg.colors?. [kind]?.enabled === false) return
|
|
772
825
|
const desktop = cfg.enabled === true && cfg.mode === 'custom'
|
|
773
826
|
const lang = cfg.locale === 'zh' ? 'zh' : 'en'
|
|
774
827
|
const l = TOAST_LABELS[lang]
|
|
@@ -878,7 +931,8 @@ export function apply(ctx) {
|
|
|
878
931
|
if (typeof event.data?.title === 'string' && event.data.title.length > 0) {
|
|
879
932
|
st.title = event.data.title
|
|
880
933
|
for (let i = queueHead; i < queue.length; i++) {
|
|
881
|
-
if (queue[i].sessionId === id && (queue[i].title.length === 0 || UNTITLED_NAMES.has(
|
|
934
|
+
if (queue[i].sessionId === id && (queue[i].title.length === 0 || UNTITLED_NAMES.has(
|
|
935
|
+
queue[i].title)))
|
|
882
936
|
queue[i].title = st.title
|
|
883
937
|
}
|
|
884
938
|
// 已弹浮窗(运行中/待审批/待回答等)标题原地更新,不再一直顶着占位/旧标题。
|
|
@@ -928,7 +982,8 @@ export function apply(ctx) {
|
|
|
928
982
|
push(id, 'question', first.question)
|
|
929
983
|
}
|
|
930
984
|
} catch {
|
|
931
|
-
/* arguments 非 JSON */
|
|
985
|
+
/* arguments 非 JSON */
|
|
986
|
+
}
|
|
932
987
|
}
|
|
933
988
|
return
|
|
934
989
|
}
|
|
@@ -1015,7 +1070,8 @@ export function apply(ctx) {
|
|
|
1015
1070
|
`${L.approvalPrefix} ${toolName}` : L.approvalFallback))
|
|
1016
1071
|
}
|
|
1017
1072
|
} catch {
|
|
1018
|
-
/* 观察失败不影响审批链路 */
|
|
1073
|
+
/* 观察失败不影响审批链路 */
|
|
1074
|
+
}
|
|
1019
1075
|
return next()
|
|
1020
1076
|
})
|
|
1021
1077
|
|
|
@@ -1032,11 +1088,16 @@ export function apply(ctx) {
|
|
|
1032
1088
|
const prev = store.get()
|
|
1033
1089
|
const next = store.set(patch)
|
|
1034
1090
|
// toast 几何(位置/宽度/间距)变更:立即应用到在屏浮窗,无需等待下一个浮窗。
|
|
1035
|
-
const pToast = patch !== null && typeof patch === 'object' && !Array.isArray(patch) ? patch.toast :
|
|
1091
|
+
const pToast = patch !== null && typeof patch === 'object' && !Array.isArray(patch) ? patch.toast :
|
|
1092
|
+
undefined
|
|
1036
1093
|
if (pToast !== null && typeof pToast === 'object' && !Array.isArray(pToast)) {
|
|
1037
1094
|
if (typeof pToast.width === 'number' && pToast.width !== prev.toast?.width) {
|
|
1038
1095
|
for (const rec of activeToasts.values()) {
|
|
1039
|
-
sendCmd({
|
|
1096
|
+
sendCmd({
|
|
1097
|
+
cmd: 'size',
|
|
1098
|
+
key: rec.key,
|
|
1099
|
+
width: next.toast.width
|
|
1100
|
+
})
|
|
1040
1101
|
}
|
|
1041
1102
|
}
|
|
1042
1103
|
reflowToasts()
|
|
@@ -1072,13 +1133,26 @@ export function apply(ctx) {
|
|
|
1072
1133
|
title: EDIT_TITLES[lang],
|
|
1073
1134
|
width: cfg.toast?.width ?? 340,
|
|
1074
1135
|
position: cfg.toast?.position ?? null,
|
|
1136
|
+
// 新增:类型色卡数组,样板窗内做预览
|
|
1137
|
+
chips: KIND_KEYS.map((k) => ({
|
|
1138
|
+
label: (KIND_BADGES[lang] ?? KIND_BADGES.en)[k],
|
|
1139
|
+
accent: cfg.colors[k].accent,
|
|
1140
|
+
bg: cfg.colors[k].bg,
|
|
1141
|
+
fg: cfg.colors[k].fg,
|
|
1142
|
+
})),
|
|
1075
1143
|
})
|
|
1076
|
-
return {
|
|
1144
|
+
return {
|
|
1145
|
+
ok: true
|
|
1146
|
+
}
|
|
1077
1147
|
},
|
|
1078
1148
|
/** 退出编辑模式:样板窗淡出消失。 */
|
|
1079
1149
|
endEditMode() {
|
|
1080
|
-
sendCmd({
|
|
1081
|
-
|
|
1150
|
+
sendCmd({
|
|
1151
|
+
cmd: 'edit-end'
|
|
1152
|
+
})
|
|
1153
|
+
return {
|
|
1154
|
+
ok: true
|
|
1155
|
+
}
|
|
1082
1156
|
},
|
|
1083
1157
|
}
|
|
1084
1158
|
|
|
@@ -1095,4 +1169,4 @@ export function apply(ctx) {
|
|
|
1095
1169
|
|
|
1096
1170
|
ctx.provide('notify', service)
|
|
1097
1171
|
console.log('[dsh-notify-yimit] Loaded (disabled by default; please enable in Settings -> Notifications)')
|
|
1098
|
-
}
|
|
1172
|
+
}
|
package/lib/toast-host.ps1
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
<# dsh-notify-yimit 常驻浮窗宿主(Windows PowerShell + WPF)。
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
倒计时即进度(本版核心变化):
|
|
4
|
+
- 非粘性通知整卡抽象为倒计时:强调色 @12% "液面层"锚定左缘,ScaleX 1→0,
|
|
5
|
+
右缘向左退去 —— 时间从右向左流走,剩余时间 = 剩余液面面积;
|
|
6
|
+
- 独立 2.5px 强调色"进度指针"亮线贴液面右缘同步左移(不被 ScaleX 压扁,
|
|
7
|
+
在 bg≈accent 相近配色下仍 100% 饱和可辨,是可见性兜底信号);
|
|
8
|
+
- 动画严格线性(无缓动),保证视觉时长与真实剩余时间一致;
|
|
9
|
+
- 粘性通知(运行中/待审批/待回答)无倒计时,左侧强调色条改为呼吸脉动,
|
|
10
|
+
与"会消失的卡"一眼区分。
|
|
11
|
+
|
|
12
|
+
其余特性:
|
|
13
|
+
- 强调色(accent)体系:左侧类型色条 + 强调色描边 + 强调色主按钮 + 辉光阴影;
|
|
14
|
+
- 编辑模式样板窗:5 类型实时色卡预览 + 边缘圆点缩放把手 + 多语言提示;
|
|
15
|
+
- JSON 序列化深度增加、Tag 闭包变量传递、接收外部 Label 参数实现多语言。 #>
|
|
16
|
+
|
|
3
17
|
Add-Type -AssemblyName PresentationFramework
|
|
4
18
|
Add-Type -AssemblyName PresentationCore
|
|
5
19
|
Add-Type -AssemblyName WindowsBase
|
|
@@ -14,9 +28,34 @@ $script:stdout = New-Object System.IO.StreamWriter([System.Console]::OpenStandar
|
|
|
14
28
|
$script:stdout.AutoFlush = $true
|
|
15
29
|
|
|
16
30
|
function Send-Report([object]$obj) {
|
|
17
|
-
try {
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
try { $script:stdout.WriteLine(($obj | ConvertTo-Json -Compress -Depth 10)) } catch { }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# ───────────────────────── 颜色 / 效果工具 ─────────────────────────
|
|
35
|
+
|
|
36
|
+
function Convert-Hsl([System.Windows.Media.Color]$c, [double]$lightFactor) {
|
|
37
|
+
# 简易亮度调整:lightFactor > 1 提亮, < 1 压暗(用于主按钮 hover/按压变体)。
|
|
38
|
+
$f = [Math]::Max(0.0, $lightFactor)
|
|
39
|
+
return [System.Windows.Media.Color]::FromRgb(
|
|
40
|
+
[byte][Math]::Min(255, [int]($c.R * $f)),
|
|
41
|
+
[byte][Math]::Min(255, [int]($c.G * $f)),
|
|
42
|
+
[byte]([Math]::Min(255, [int]($c.B * $f))))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function New-GlowEffect([System.Windows.Media.Color]$c, [double]$opacity) {
|
|
46
|
+
# 强调色辉光阴影:无位移、大模糊,营造"类型色悬浮"感。
|
|
47
|
+
$effect = New-Object System.Windows.Media.Effects.DropShadowEffect
|
|
48
|
+
$effect.Color = $c
|
|
49
|
+
$effect.BlurRadius = 24
|
|
50
|
+
$effect.ShadowDepth = 0
|
|
51
|
+
$effect.Opacity = $opacity
|
|
52
|
+
return $effect
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function Add-GridRow($grid) {
|
|
56
|
+
$row = New-Object System.Windows.Controls.RowDefinition
|
|
57
|
+
$row.Height = [System.Windows.GridLength]::Auto
|
|
58
|
+
$grid.RowDefinitions.Add($row) | Out-Null
|
|
20
59
|
}
|
|
21
60
|
|
|
22
61
|
# 编辑模式几何回传:样板窗当前 left/top/width/height(工作区相对坐标)。
|
|
@@ -24,22 +63,26 @@ function Send-Geometry($w) {
|
|
|
24
63
|
try {
|
|
25
64
|
$work = [System.Windows.SystemParameters]::WorkArea
|
|
26
65
|
Send-Report @{
|
|
27
|
-
type
|
|
28
|
-
left
|
|
29
|
-
top
|
|
30
|
-
width
|
|
66
|
+
type = 'geometry'
|
|
67
|
+
left = [Math]::Round($w.Left - $work.Left)
|
|
68
|
+
top = [Math]::Round($w.Top - $work.Top)
|
|
69
|
+
width = [Math]::Round($w.ActualWidth)
|
|
31
70
|
height = [Math]::Round($w.ActualHeight)
|
|
32
71
|
}
|
|
33
72
|
} catch { }
|
|
34
73
|
}
|
|
35
74
|
|
|
36
|
-
#
|
|
37
|
-
|
|
75
|
+
# ───────────────────────── 编辑模式样板窗 ─────────────────────────
|
|
76
|
+
|
|
77
|
+
# 编辑模式样板窗:拖拽移动 + 左右边缘圆点把手缩放;拖拽/缩放结束后回传 geometry。
|
|
78
|
+
# 强调色描边 + 辉光;内嵌 5 类型实时色卡预览(chips);多语言提示。
|
|
38
79
|
function New-EditSample($cmd) {
|
|
39
80
|
$bg = Parse-Color ([string]$cmd.bg) "#203a5c"
|
|
40
81
|
$fg = Parse-Color ([string]$cmd.fg) "#e8f0fb"
|
|
82
|
+
$accent = Parse-Color ([string]$cmd.accent) "#60a5fa"
|
|
41
83
|
$bgBrush = New-Object System.Windows.Media.SolidColorBrush($bg)
|
|
42
84
|
$fgBrush = New-Object System.Windows.Media.SolidColorBrush($fg)
|
|
85
|
+
$accentBrush = New-Object System.Windows.Media.SolidColorBrush($accent)
|
|
43
86
|
|
|
44
87
|
$win = New-Object System.Windows.Window
|
|
45
88
|
$win.WindowStyle = [System.Windows.WindowStyle]::None
|
|
@@ -49,55 +92,121 @@ function New-EditSample($cmd) {
|
|
|
49
92
|
$win.ShowInTaskbar = $false
|
|
50
93
|
if ($null -ne $cmd.width) { $win.Width = [double]$cmd.width } else { $win.Width = 340 }
|
|
51
94
|
$win.SizeToContent = [System.Windows.SizeToContent]::Height
|
|
95
|
+
$win.Effect = New-GlowEffect $accent 0.25
|
|
52
96
|
|
|
53
97
|
$border = New-Object System.Windows.Controls.Border
|
|
54
98
|
$border.Background = $bgBrush
|
|
55
|
-
$border.BorderBrush = $
|
|
99
|
+
$border.BorderBrush = $accentBrush
|
|
56
100
|
$border.BorderThickness = New-Object System.Windows.Thickness(1)
|
|
57
|
-
$border.CornerRadius = New-Object System.Windows.CornerRadius(
|
|
101
|
+
$border.CornerRadius = New-Object System.Windows.CornerRadius(14)
|
|
58
102
|
$border.Margin = New-Object System.Windows.Thickness(12)
|
|
59
103
|
|
|
60
|
-
$root = New-Object System.Windows.Controls.
|
|
61
|
-
$root.Margin = New-Object System.Windows.Thickness(14,
|
|
104
|
+
$root = New-Object System.Windows.Controls.StackPanel
|
|
105
|
+
$root.Margin = New-Object System.Windows.Thickness(16, 14, 16, 14)
|
|
62
106
|
$border.Child = $root
|
|
63
107
|
|
|
64
|
-
|
|
108
|
+
# 标题(带强调色小色块前缀)
|
|
109
|
+
$titleRow = New-Object System.Windows.Controls.StackPanel
|
|
110
|
+
$titleRow.Orientation = [System.Windows.Controls.Orientation]::Horizontal
|
|
111
|
+
$titleRow.Margin = New-Object System.Windows.Thickness(0, 0, 0, 8)
|
|
112
|
+
$titleDot = New-Object System.Windows.Controls.Border
|
|
113
|
+
$titleDot.Width = 4
|
|
114
|
+
$titleDot.Height = 14
|
|
115
|
+
$titleDot.CornerRadius = New-Object System.Windows.CornerRadius(2)
|
|
116
|
+
$titleDot.Background = $accentBrush
|
|
117
|
+
$titleDot.Margin = New-Object System.Windows.Thickness(0, 0, 8, 0)
|
|
118
|
+
$titleDot.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
|
|
119
|
+
$titleRow.Children.Add($titleDot) | Out-Null
|
|
65
120
|
$titleText = New-Object System.Windows.Controls.TextBlock
|
|
66
121
|
$titleText.Text = if ([string]::IsNullOrEmpty([string]$cmd.title)) { "Notification preview" } else { [string]$cmd.title }
|
|
67
122
|
$titleText.Foreground = $fgBrush
|
|
68
123
|
$titleText.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
69
124
|
$titleText.FontSize = 13
|
|
70
125
|
$titleText.FontWeight = [System.Windows.FontWeights]::SemiBold
|
|
71
|
-
$titleText.
|
|
72
|
-
$
|
|
126
|
+
$titleText.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
|
|
127
|
+
$titleRow.Children.Add($titleText) | Out-Null
|
|
128
|
+
$root.Children.Add($titleRow) | Out-Null
|
|
129
|
+
|
|
130
|
+
# 5 类型实时色卡预览:浮窗色条/描边/主按钮所见即所得
|
|
131
|
+
$chips = @($cmd.chips)
|
|
132
|
+
if ($chips.Count -gt 0) {
|
|
133
|
+
$chipRow = New-Object System.Windows.Controls.WrapPanel
|
|
134
|
+
$chipRow.Margin = New-Object System.Windows.Thickness(0, 0, 0, 8)
|
|
135
|
+
foreach ($c in $chips) {
|
|
136
|
+
if ($c -eq $null) { continue }
|
|
137
|
+
$chipAccent = Parse-Color ([string]$c.accent) "#60a5fa"
|
|
138
|
+
$chipBg = Parse-Color ([string]$c.bg) "#20272f"
|
|
139
|
+
$chip = New-Object System.Windows.Controls.Border
|
|
140
|
+
$chip.CornerRadius = New-Object System.Windows.CornerRadius(8)
|
|
141
|
+
$chip.Padding = New-Object System.Windows.Thickness(8, 3, 8, 4)
|
|
142
|
+
$chip.Margin = New-Object System.Windows.Thickness(0, 0, 6, 4)
|
|
143
|
+
$chip.Background = New-Object System.Windows.Media.SolidColorBrush($chipBg)
|
|
144
|
+
$chip.BorderBrush = New-Object System.Windows.Media.SolidColorBrush($chipAccent)
|
|
145
|
+
$chip.BorderThickness = New-Object System.Windows.Thickness(1)
|
|
146
|
+
$chipText = New-Object System.Windows.Controls.TextBlock
|
|
147
|
+
$chipText.Text = [string]$c.label
|
|
148
|
+
$chipText.Foreground = New-Object System.Windows.Media.SolidColorBrush($chipAccent)
|
|
149
|
+
$chipText.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
150
|
+
$chipText.FontSize = 11
|
|
151
|
+
$chipText.FontWeight = [System.Windows.FontWeights]::SemiBold
|
|
152
|
+
$chip.Child = $chipText
|
|
153
|
+
$chipRow.Children.Add($chip) | Out-Null
|
|
154
|
+
}
|
|
155
|
+
$root.Children.Add($chipRow) | Out-Null
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# 提示文案(多语言,由 startEditMode 传入 hint)
|
|
73
159
|
$bodyText = New-Object System.Windows.Controls.TextBlock
|
|
74
|
-
$bodyText.Text =
|
|
160
|
+
$bodyText.Text = if ([string]::IsNullOrEmpty([string]$cmd.hint)) {
|
|
161
|
+
"Drag to move · Drag window edges to resize width · Click 'Finish editing' when done"
|
|
162
|
+
} else { [string]$cmd.hint }
|
|
75
163
|
$bodyText.Foreground = $fgBrush
|
|
76
164
|
$bodyText.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
77
165
|
$bodyText.FontSize = 12
|
|
78
166
|
$bodyText.TextWrapping = [System.Windows.TextWrapping]::Wrap
|
|
79
|
-
$
|
|
80
|
-
$root.Children.Add($stack) | Out-Null
|
|
167
|
+
$root.Children.Add($bodyText) | Out-Null
|
|
81
168
|
|
|
82
|
-
#
|
|
83
|
-
#
|
|
84
|
-
$edgeBrush = New-Object System.Windows.Media.SolidColorBrush(
|
|
169
|
+
# 左右边缘缩放把手:8px 通高,强调色半透明填充 + 三圆点把手,
|
|
170
|
+
# 悬停时完全不透明提示可拖拽区域,光标为左右双向箭头。
|
|
171
|
+
$edgeBrush = New-Object System.Windows.Media.SolidColorBrush(
|
|
172
|
+
[System.Windows.Media.Color]::FromArgb(115, $accent.R, $accent.G, $accent.B))
|
|
85
173
|
$leftStrip = New-Object System.Windows.Controls.Border
|
|
86
174
|
$leftStrip.Width = 8
|
|
87
175
|
$leftStrip.Cursor = [System.Windows.Input.Cursors]::SizeWE
|
|
88
176
|
$leftStrip.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left
|
|
89
177
|
$leftStrip.VerticalAlignment = [System.Windows.VerticalAlignment]::Stretch
|
|
90
178
|
$leftStrip.Background = $edgeBrush
|
|
91
|
-
$leftStrip.CornerRadius = New-Object System.Windows.CornerRadius(
|
|
179
|
+
$leftStrip.CornerRadius = New-Object System.Windows.CornerRadius(14, 0, 0, 14)
|
|
92
180
|
$rightStrip = New-Object System.Windows.Controls.Border
|
|
93
181
|
$rightStrip.Width = 8
|
|
94
182
|
$rightStrip.Cursor = [System.Windows.Input.Cursors]::SizeWE
|
|
95
183
|
$rightStrip.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Right
|
|
96
184
|
$rightStrip.VerticalAlignment = [System.Windows.VerticalAlignment]::Stretch
|
|
97
185
|
$rightStrip.Background = $edgeBrush
|
|
98
|
-
$rightStrip.CornerRadius = New-Object System.Windows.CornerRadius(0,
|
|
186
|
+
$rightStrip.CornerRadius = New-Object System.Windows.CornerRadius(0, 14, 14, 0)
|
|
187
|
+
|
|
188
|
+
function Add-GripDots($strip, $dotColor) {
|
|
189
|
+
$dots = New-Object System.Windows.Controls.StackPanel
|
|
190
|
+
$dots.Orientation = [System.Windows.Controls.Orientation]::Horizontal
|
|
191
|
+
$dots.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
|
|
192
|
+
$dots.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Center
|
|
193
|
+
foreach ($i in 1..3) {
|
|
194
|
+
$dot = New-Object System.Windows.Shapes.Ellipse
|
|
195
|
+
$dot.Width = 3; $dot.Height = 3
|
|
196
|
+
$dot.Margin = New-Object System.Windows.Thickness(1, 0, 1, 0)
|
|
197
|
+
$dot.Fill = New-Object System.Windows.Media.SolidColorBrush(
|
|
198
|
+
[System.Windows.Media.Color]::FromArgb(170, $dotColor.R, $dotColor.G, $dotColor.B))
|
|
199
|
+
$dots.Children.Add($dot) | Out-Null
|
|
200
|
+
}
|
|
201
|
+
$strip.Child = $dots
|
|
202
|
+
$strip.Opacity = 0.75
|
|
203
|
+
$strip.Add_MouseEnter({ $this.Opacity = 1.0 }) | Out-Null
|
|
204
|
+
$strip.Add_MouseLeave({ $this.Opacity = 0.75 }) | Out-Null
|
|
205
|
+
}
|
|
206
|
+
Add-GripDots $leftStrip $fg
|
|
207
|
+
Add-GripDots $rightStrip $fg
|
|
99
208
|
|
|
100
|
-
# 外层 Grid:边框 +
|
|
209
|
+
# 外层 Grid:边框 + 左右缩放把手叠放(把手覆盖窗口左右边缘)
|
|
101
210
|
$outer = New-Object System.Windows.Controls.Grid
|
|
102
211
|
$outer.Children.Add($border) | Out-Null
|
|
103
212
|
$outer.Children.Add($leftStrip) | Out-Null
|
|
@@ -119,12 +228,10 @@ function New-EditSample($cmd) {
|
|
|
119
228
|
Send-Geometry $win
|
|
120
229
|
}.GetNewClosure()) | Out-Null
|
|
121
230
|
|
|
122
|
-
# 拖拽移动(
|
|
231
|
+
# 拖拽移动(缩放把手区域除外):DragMove 阻塞至松开,结束后收敛到工作区内并回传几何。
|
|
123
232
|
$win.Add_MouseLeftButtonDown({
|
|
124
233
|
if ($win.Tag.resizing) { $win.Tag.resizing = $false; return }
|
|
125
|
-
try {
|
|
126
|
-
$win.DragMove()
|
|
127
|
-
} catch { }
|
|
234
|
+
try { $win.DragMove() } catch { }
|
|
128
235
|
try {
|
|
129
236
|
$work = [System.Windows.SystemParameters]::WorkArea
|
|
130
237
|
$win.Left = [Math]::Max($work.Left, [Math]::Min($win.Left, $work.Right - $win.ActualWidth))
|
|
@@ -189,39 +296,40 @@ function Parse-Color([string]$hex, [string]$fallback) {
|
|
|
189
296
|
if ($h.Length -eq 3) { $h = ($h.ToCharArray() | ForEach-Object { "$_$_" }) -join '' }
|
|
190
297
|
if ($h.Length -eq 6) { $h = "FF$h" }
|
|
191
298
|
if ($h.Length -ne 8 -or $h -notmatch '^[0-9a-fA-F]{8}$') {
|
|
192
|
-
# 非法/缺失输入:改用回退色并同样做 3/6 位展开 + 补 FF
|
|
193
|
-
# 回退为 6 位时未补前缀,Substring 越界抛异常)。
|
|
299
|
+
# 非法/缺失输入:改用回退色并同样做 3/6 位展开 + 补 FF。
|
|
194
300
|
$h = $fallback.TrimStart('#')
|
|
195
301
|
if ($h.Length -eq 3) { $h = ($h.ToCharArray() | ForEach-Object { "$_$_" }) -join '' }
|
|
196
302
|
if ($h.Length -eq 6) { $h = "FF$h" }
|
|
197
303
|
}
|
|
198
304
|
return [System.Windows.Media.Color]::FromArgb(
|
|
199
|
-
[Convert]::ToInt32($h.Substring(0,2), 16),
|
|
200
|
-
[Convert]::ToInt32($h.Substring(2,2), 16),
|
|
201
|
-
[Convert]::ToInt32($h.Substring(4,2), 16),
|
|
202
|
-
[Convert]::ToInt32($h.Substring(6,2), 16))
|
|
305
|
+
[Convert]::ToInt32($h.Substring(0, 2), 16),
|
|
306
|
+
[Convert]::ToInt32($h.Substring(2, 2), 16),
|
|
307
|
+
[Convert]::ToInt32($h.Substring(4, 2), 16),
|
|
308
|
+
[Convert]::ToInt32($h.Substring(6, 2), 16))
|
|
203
309
|
}
|
|
204
310
|
|
|
205
|
-
$downP = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(158,164,170))
|
|
206
|
-
$downI = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(30,36,44))
|
|
207
|
-
$hoverP = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(196,202,208))
|
|
208
|
-
$hoverI = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(44,52,62))
|
|
311
|
+
$downP = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(158, 164, 170))
|
|
312
|
+
$downI = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(30, 36, 44))
|
|
313
|
+
$hoverP = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(196, 202, 208))
|
|
314
|
+
$hoverI = New-Object System.Windows.Media.SolidColorBrush([System.Windows.Media.Color]::FromRgb(44, 52, 62))
|
|
209
315
|
|
|
316
|
+
# 浮窗按钮:primary 时传入强调色画刷,hover/按压基于该色自动提亮/压暗;
|
|
317
|
+
# secondary 保持描边透明底样式。
|
|
210
318
|
function New-ToastButton([string]$label, [scriptblock]$onClick, [bool]$primary, [System.Windows.Media.SolidColorBrush]$fg, [System.Windows.Media.SolidColorBrush]$bg) {
|
|
211
319
|
$bd = New-Object System.Windows.Controls.Border
|
|
212
320
|
$bd.CornerRadius = New-Object System.Windows.CornerRadius(10)
|
|
213
|
-
$bd.Padding = New-Object System.Windows.Thickness(18,7,18,7)
|
|
214
|
-
$bd.Margin = New-Object System.Windows.Thickness(0,0,8,0)
|
|
321
|
+
$bd.Padding = New-Object System.Windows.Thickness(18, 7, 18, 7)
|
|
322
|
+
$bd.Margin = New-Object System.Windows.Thickness(0, 0, 8, 0)
|
|
215
323
|
$bd.Cursor = [System.Windows.Input.Cursors]::Hand
|
|
216
324
|
$bd.Focusable = $false
|
|
217
|
-
|
|
325
|
+
|
|
218
326
|
$txt = New-Object System.Windows.Controls.TextBlock
|
|
219
327
|
$txt.Text = $label
|
|
220
328
|
$txt.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
221
329
|
$txt.FontSize = 12
|
|
222
330
|
$txt.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
|
|
223
331
|
$txt.IsHitTestVisible = $false
|
|
224
|
-
|
|
332
|
+
|
|
225
333
|
if ($primary) {
|
|
226
334
|
$bd.Background = $fg
|
|
227
335
|
$txt.Foreground = $bg
|
|
@@ -233,31 +341,32 @@ function New-ToastButton([string]$label, [scriptblock]$onClick, [bool]$primary,
|
|
|
233
341
|
$bd.BorderBrush = $fg
|
|
234
342
|
$bd.BorderThickness = New-Object System.Windows.Thickness(1)
|
|
235
343
|
}
|
|
344
|
+
|
|
236
345
|
$bd.Child = $txt
|
|
237
|
-
|
|
238
|
-
$bd.Tag = @{
|
|
239
|
-
|
|
346
|
+
# hover/down 变体:primary 基于传入的强调色提亮/压暗;secondary 用全局灰蓝变体。
|
|
347
|
+
$bd.Tag = @{
|
|
348
|
+
primary = $primary; onClick = $onClick
|
|
349
|
+
hoverP = (Convert-Hsl $fg.Color 1.15); downP = (Convert-Hsl $fg.Color 0.85)
|
|
350
|
+
hoverI = $hoverI; downI = $downI
|
|
351
|
+
fg = $fg; bg = $bg
|
|
352
|
+
}
|
|
240
353
|
$bd.Add_MouseEnter({
|
|
241
354
|
$info = $this.Tag
|
|
242
355
|
$this.Background = if ($info.primary) { $info.hoverP } else { $info.hoverI }
|
|
243
356
|
}) | Out-Null
|
|
244
|
-
|
|
245
357
|
$bd.Add_MouseLeave({
|
|
246
358
|
$info = $this.Tag
|
|
247
359
|
$this.Background = if ($info.primary) { $info.fg } else { [System.Windows.Media.Brushes]::Transparent }
|
|
248
360
|
}) | Out-Null
|
|
249
|
-
|
|
250
361
|
$bd.Add_MouseLeftButtonDown({
|
|
251
362
|
$info = $this.Tag
|
|
252
363
|
$this.Background = if ($info.primary) { $info.downP } else { $info.downI }
|
|
253
364
|
}) | Out-Null
|
|
254
|
-
|
|
255
365
|
$bd.Add_MouseLeftButtonUp({
|
|
256
366
|
$info = $this.Tag
|
|
257
367
|
try { if ($info.onClick -ne $null) { & $info.onClick } } catch { }
|
|
258
368
|
$this.Background = if ($info.primary) { $info.fg } else { [System.Windows.Media.Brushes]::Transparent }
|
|
259
369
|
}) | Out-Null
|
|
260
|
-
|
|
261
370
|
return $bd
|
|
262
371
|
}
|
|
263
372
|
|
|
@@ -268,23 +377,41 @@ $easeExit.EasingMode = [System.Windows.Media.Animation.EasingMode]::EaseIn
|
|
|
268
377
|
|
|
269
378
|
function Close-WithFade($w) {
|
|
270
379
|
try {
|
|
271
|
-
|
|
272
|
-
$w.Tag
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
$
|
|
281
|
-
|
|
380
|
+
# closing 标记改为 Tag 哈希表(同时携带卡片缩放变换供退场动画用)。
|
|
381
|
+
if ($w.Tag -is [hashtable]) {
|
|
382
|
+
if ($w.Tag.closing) { return }
|
|
383
|
+
$w.Tag.closing =$true
|
|
384
|
+
} else {
|
|
385
|
+
if ($w.Tag -eq 'closing') { return }
|
|
386
|
+
$w.Tag = @{ closing =$true; scale = $null }
|
|
387
|
+
}
|
|
388
|
+
# 淡出 + 下坠 + 轻微收缩(0.98):三者同步 200ms,"沉落"退场。
|
|
389
|
+
$fadeOut = New-Object System.Windows.Media.Animation.DoubleAnimation(1, 0, [TimeSpan]::FromMilliseconds(200))
|
|
390
|
+
$fadeOut.EasingFunction =$easeExit
|
|
391
|
+
$fadeOut.Add_Completed({
|
|
392
|
+
try { $w.Close() } catch { }
|
|
393
|
+
}.GetNewClosure()) | Out-Null
|
|
394
|
+
$w.BeginAnimation([System.Windows.Window]::OpacityProperty,$fadeOut)
|
|
395
|
+
$dropAn = New-Object System.Windows.Media.Animation.DoubleAnimation($w.Top, ($w.Top + 16), [TimeSpan]::FromMilliseconds(200))
|
|
396
|
+
$dropAn.EasingFunction =$easeExit
|
|
397
|
+
$w.BeginAnimation([System.Windows.Window]::TopProperty,$dropAn)
|
|
398
|
+
try {
|
|
399
|
+
$sc =$w.Tag.scale
|
|
400
|
+
if ($sc -ne$null) {
|
|
401
|
+
$sx = New-Object System.Windows.Media.Animation.DoubleAnimation(1, 0.98, [TimeSpan]::FromMilliseconds(200))
|
|
402
|
+
$sx.EasingFunction =$easeExit
|
|
403
|
+
$sy = New-Object System.Windows.Media.Animation.DoubleAnimation(1, 0.98, [TimeSpan]::FromMilliseconds(200))
|
|
404
|
+
$sy.EasingFunction =$easeExit
|
|
405
|
+
$sc.BeginAnimation([System.Windows.Media.ScaleTransform]::ScaleXProperty,$sx)
|
|
406
|
+
$sc.BeginAnimation([System.Windows.Media.ScaleTransform]::ScaleYProperty,$sy)
|
|
407
|
+
}
|
|
408
|
+
} catch { }
|
|
282
409
|
$backstop = New-Object System.Windows.Threading.DispatcherTimer
|
|
283
410
|
$backstop.Interval = [TimeSpan]::FromMilliseconds(600)
|
|
284
|
-
$backstop.Tag
|
|
411
|
+
$backstop.Tag =$w
|
|
285
412
|
$backstop.Add_Tick({
|
|
286
413
|
$this.Stop()
|
|
287
|
-
try { if ($this.Tag.IsVisible) {
|
|
414
|
+
try { if ($this.Tag.IsVisible) {$this.Tag.Close() } } catch { }
|
|
288
415
|
}) | Out-Null
|
|
289
416
|
$backstop.Start()
|
|
290
417
|
} catch {
|
|
@@ -292,64 +419,87 @@ function Close-WithFade($w) {
|
|
|
292
419
|
}
|
|
293
420
|
}
|
|
294
421
|
|
|
422
|
+
# ───────────────────────── 通知浮窗 ─────────────────────────
|
|
423
|
+
|
|
295
424
|
function New-ToastWindow($cmd) {
|
|
296
425
|
$bg = Parse-Color ([string]$cmd.bg) "#20272f"
|
|
297
426
|
$fg = Parse-Color ([string]$cmd.fg) "#e6edf3"
|
|
427
|
+
$accent = Parse-Color ([string]$cmd.accent) "#60a5fa"
|
|
298
428
|
$bgBrush = New-Object System.Windows.Media.SolidColorBrush($bg)
|
|
299
429
|
$fgBrush = New-Object System.Windows.Media.SolidColorBrush($fg)
|
|
300
|
-
|
|
430
|
+
$accentBrush = New-Object System.Windows.Media.SolidColorBrush($accent)
|
|
431
|
+
|
|
432
|
+
# 提前计算:粘性判定与倒计时时长(Loaded 闭包与计时器共用)。
|
|
433
|
+
$isSticky = ($cmd.sticky -eq $true)
|
|
434
|
+
$dur = [Math]::Max(1, [int]$cmd.durationSec)
|
|
435
|
+
|
|
301
436
|
$win = New-Object System.Windows.Window
|
|
302
437
|
$win.WindowStyle = [System.Windows.WindowStyle]::None
|
|
303
|
-
$win.AllowsTransparency
|
|
438
|
+
$win.AllowsTransparency =$true
|
|
304
439
|
$win.Background = [System.Windows.Media.Brushes]::Transparent
|
|
305
|
-
$win.Topmost
|
|
306
|
-
$win.ShowInTaskbar
|
|
307
|
-
|
|
308
|
-
if ($null -ne $cmd.width) { $win.Width = [double]$cmd.width } else { $win.Width = 340 }
|
|
440
|
+
$win.Topmost =$true
|
|
441
|
+
$win.ShowInTaskbar =$false
|
|
442
|
+
if ($null -ne$cmd.width) { $win.Width = [double]$cmd.width } else { $win.Width = 340 }
|
|
309
443
|
$win.SizeToContent = [System.Windows.SizeToContent]::Height
|
|
310
|
-
if (-not [string]::IsNullOrEmpty([string]$cmd.winTitle)) {
|
|
311
|
-
|
|
444
|
+
if (-not [string]::IsNullOrEmpty([string]$cmd.winTitle)) {$win.Title = [string]$cmd.winTitle }
|
|
445
|
+
$win.Effect = New-GlowEffect $accent 0.28
|
|
446
|
+
|
|
312
447
|
$border = New-Object System.Windows.Controls.Border
|
|
313
|
-
$border.Background
|
|
314
|
-
$border.BorderBrush
|
|
448
|
+
$border.Background =$bgBrush
|
|
449
|
+
$border.BorderBrush =$accentBrush
|
|
315
450
|
$border.BorderThickness = New-Object System.Windows.Thickness(1)
|
|
316
|
-
$border.CornerRadius = New-Object System.Windows.CornerRadius(
|
|
451
|
+
$border.CornerRadius = New-Object System.Windows.CornerRadius(14)
|
|
317
452
|
$border.Margin = New-Object System.Windows.Thickness(12)
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
$
|
|
321
|
-
$
|
|
322
|
-
|
|
323
|
-
|
|
453
|
+
|
|
454
|
+
# 卡片缩放变换:入场 0.96→1(底部锚点,"浮现"),退场 1→0.98("沉落")。
|
|
455
|
+
# 经 $win.Tag 传给 Close-WithFade,同时 Tag.closing 作防重入标记。
|
|
456
|
+
$cardT = New-Object System.Windows.Media.ScaleTransform(1, 1)
|
|
457
|
+
$border.RenderTransformOrigin = New-Object System.Windows.Point(0.5, 1)
|
|
458
|
+
$border.RenderTransform =$cardT
|
|
459
|
+
|
|
460
|
+
# 内容 Grid:3 行 = 头部(标题) / 正文 / 按钮行
|
|
461
|
+
$root = New-Object System.Windows.Controls.Grid
|
|
462
|
+
$root.Margin = New-Object System.Windows.Thickness(26, 12, 14, 12)
|
|
463
|
+
Add-GridRow $root
|
|
464
|
+
Add-GridRow $root
|
|
465
|
+
Add-GridRow $root
|
|
466
|
+
|
|
467
|
+
# ── 头部:仅标题 ──
|
|
468
|
+
$head = New-Object System.Windows.Controls.StackPanel
|
|
469
|
+
$head.Orientation = [System.Windows.Controls.Orientation]::Horizontal
|
|
470
|
+
$head.Margin = New-Object System.Windows.Thickness(0, 0, 0, 6)
|
|
324
471
|
$unnamedLabel = if ([string]::IsNullOrEmpty([string]$cmd.unnamedLabel)) { "(Unnamed session)" } else { [string]$cmd.unnamedLabel }
|
|
325
|
-
|
|
326
472
|
$titleText = New-Object System.Windows.Controls.TextBlock
|
|
327
473
|
$titleText.Text = if ([string]::IsNullOrEmpty([string]$cmd.title)) { $unnamedLabel } else { [string]$cmd.title }
|
|
328
|
-
$titleText.Foreground
|
|
474
|
+
$titleText.Foreground =$fgBrush
|
|
329
475
|
$titleText.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
330
476
|
$titleText.FontSize = 13
|
|
331
477
|
$titleText.FontWeight = [System.Windows.FontWeights]::SemiBold
|
|
332
478
|
$titleText.TextTrimming = [System.Windows.TextTrimming]::CharacterEllipsis
|
|
333
|
-
$titleText.
|
|
334
|
-
$
|
|
335
|
-
|
|
479
|
+
$titleText.VerticalAlignment = [System.Windows.VerticalAlignment]::Center
|
|
480
|
+
$head.Children.Add($titleText) | Out-Null
|
|
481
|
+
[System.Windows.Controls.Grid]::SetRow($head, 0)
|
|
482
|
+
$root.Children.Add($head) | Out-Null
|
|
483
|
+
|
|
484
|
+
# ── 正文 ──
|
|
336
485
|
$bodyText = New-Object System.Windows.Controls.TextBlock
|
|
337
486
|
$bodyText.Text = [string]$cmd.text
|
|
338
|
-
$bodyText.Foreground
|
|
487
|
+
$bodyText.Foreground =$fgBrush
|
|
339
488
|
$bodyText.FontFamily = New-Object System.Windows.Media.FontFamily("Segoe UI, Microsoft YaHei UI")
|
|
340
489
|
$bodyText.FontSize = 13
|
|
341
490
|
$bodyText.TextWrapping = [System.Windows.TextWrapping]::Wrap
|
|
342
|
-
$bodyText.Margin = New-Object System.Windows.Thickness(0,0,0,10)
|
|
491
|
+
$bodyText.Margin = New-Object System.Windows.Thickness(0, 0, 0, 10)
|
|
492
|
+
[System.Windows.Controls.Grid]::SetRow($bodyText, 1)
|
|
343
493
|
$root.Children.Add($bodyText) | Out-Null
|
|
344
|
-
|
|
494
|
+
|
|
495
|
+
# ── 按钮行 ──
|
|
345
496
|
$btnRow = New-Object System.Windows.Controls.StackPanel
|
|
346
497
|
$btnRow.Orientation = [System.Windows.Controls.Orientation]::Horizontal
|
|
347
498
|
$btnRow.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Right
|
|
348
|
-
|
|
499
|
+
$btnRow.Margin = New-Object System.Windows.Thickness(0, 0, 0, 2)
|
|
349
500
|
$ignoreLabel = if ([string]::IsNullOrEmpty([string]$cmd.ignoreLabel)) { "Ignore" } else { [string]$cmd.ignoreLabel }
|
|
350
501
|
$ignoreBtn = New-ToastButton $ignoreLabel ({ Close-WithFade $win }.GetNewClosure()) $false $fgBrush $bgBrush
|
|
351
502
|
$btnRow.Children.Add($ignoreBtn) | Out-Null
|
|
352
|
-
|
|
353
503
|
if (-not [string]::IsNullOrEmpty([string]$cmd.sessionId)) {
|
|
354
504
|
$jumpLabel = if ([string]::IsNullOrEmpty([string]$cmd.jumpLabel)) { "Open" } else { [string]$cmd.jumpLabel }
|
|
355
505
|
$jumpBtn = New-ToastButton $jumpLabel ({
|
|
@@ -360,93 +510,155 @@ function New-ToastWindow($cmd) {
|
|
|
360
510
|
} else { Start-Process $url }
|
|
361
511
|
} catch { }
|
|
362
512
|
Close-WithFade $win
|
|
363
|
-
}.GetNewClosure()) $true $
|
|
513
|
+
}.GetNewClosure()) $true $accentBrush $bgBrush
|
|
364
514
|
$btnRow.Children.Add($jumpBtn) | Out-Null
|
|
365
515
|
}
|
|
516
|
+
[System.Windows.Controls.Grid]::SetRow($btnRow, 2)
|
|
366
517
|
$root.Children.Add($btnRow) | Out-Null
|
|
367
|
-
|
|
368
|
-
|
|
518
|
+
|
|
519
|
+
# ── 倒计时液面层(唯一的倒计时视觉,无任何线条元素) ──
|
|
520
|
+
$inner = New-Object System.Windows.Controls.Grid
|
|
521
|
+
$tintLayer = New-Object System.Windows.Controls.Border
|
|
522
|
+
$tintLayer.CornerRadius = New-Object System.Windows.CornerRadius(13, 0, 0, 13)
|
|
523
|
+
$tintLayer.VerticalAlignment = [System.Windows.VerticalAlignment]::Stretch
|
|
524
|
+
$tintLayer.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Stretch
|
|
525
|
+
$tintBrush = New-Object System.Windows.Media.SolidColorBrush($accent)
|
|
526
|
+
$tintBrush.Opacity = 0.12
|
|
527
|
+
$tintLayer.Background =$tintBrush
|
|
528
|
+
$tintT = New-Object System.Windows.Media.ScaleTransform(1, 1)
|
|
529
|
+
$tintLayer.RenderTransformOrigin = New-Object System.Windows.Point(0, 0.5)
|
|
530
|
+
$tintLayer.RenderTransform =$tintT
|
|
531
|
+
$inner.Children.Add($tintLayer) | Out-Null
|
|
532
|
+
$inner.Children.Add($root) | Out-Null
|
|
533
|
+
$border.Child =$inner
|
|
534
|
+
|
|
535
|
+
# ── 左侧强调色条(粘性通知做呼吸脉动) ──
|
|
536
|
+
$accentStrip = New-Object System.Windows.Controls.Border
|
|
537
|
+
$accentStrip.Width = 4
|
|
538
|
+
$accentStrip.CornerRadius = New-Object System.Windows.CornerRadius(2)
|
|
539
|
+
$accentStrip.VerticalAlignment = [System.Windows.VerticalAlignment]::Stretch
|
|
540
|
+
$accentStrip.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Left
|
|
541
|
+
$accentStrip.Margin = New-Object System.Windows.Thickness(22, 26, 0, 26)
|
|
542
|
+
$accentStrip.Background =$accentBrush
|
|
543
|
+
|
|
544
|
+
# 外层只有:卡片 + 色条。进度指针已彻底移除 —— 任何通知都不再出现那根线。
|
|
545
|
+
$outer = New-Object System.Windows.Controls.Grid
|
|
546
|
+
$outer.Children.Add($border) | Out-Null
|
|
547
|
+
$outer.Children.Add($accentStrip) | Out-Null
|
|
548
|
+
$win.Content =$outer
|
|
549
|
+
|
|
550
|
+
$win.Tag = @{ closing =$false; scale = $cardT }
|
|
551
|
+
|
|
369
552
|
$win.Add_Loaded({
|
|
370
553
|
$work = [System.Windows.SystemParameters]::WorkArea
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
$pos = $cmd.position
|
|
374
|
-
if ($null -ne $pos -and $null -ne $pos.left -and $null -ne $pos.top) {
|
|
554
|
+
$pos =$cmd.position
|
|
555
|
+
if ($null -ne$pos -and $null -ne$pos.left -and $null -ne$pos.top) {
|
|
375
556
|
$targetLeft = [double]$pos.left + $work.Left
|
|
376
557
|
$targetTop = [double]$pos.top + $work.Top - [int]$cmd.offsetY
|
|
377
558
|
} else {
|
|
378
|
-
$targetLeft
|
|
379
|
-
$targetTop
|
|
559
|
+
$targetLeft =$work.Right - $win.ActualWidth - 20
|
|
560
|
+
$targetTop =$work.Bottom - $win.ActualHeight - 20 - [int]$cmd.offsetY
|
|
380
561
|
}
|
|
381
|
-
$win.Left
|
|
382
|
-
$win.Top
|
|
562
|
+
$win.Left =$targetLeft
|
|
563
|
+
$win.Top =$targetTop + 36
|
|
383
564
|
$win.Opacity = 0
|
|
384
565
|
try {
|
|
385
566
|
$easingIn = New-Object System.Windows.Media.Animation.QuadraticEase
|
|
386
567
|
$easingIn.EasingMode = [System.Windows.Media.Animation.EasingMode]::EaseOut
|
|
387
|
-
|
|
388
|
-
$slideIn
|
|
389
|
-
$
|
|
390
|
-
$
|
|
391
|
-
$fadeIn
|
|
392
|
-
$
|
|
393
|
-
|
|
568
|
+
# 入场三连:上滑 36px + 淡入 + 卡片 0.96→1 缩放(底部锚点),260ms 同步完成。
|
|
569
|
+
$slideIn = New-Object System.Windows.Media.Animation.DoubleAnimation($targetTop + 36, $targetTop, [TimeSpan]::FromMilliseconds(260))
|
|
570
|
+
$slideIn.EasingFunction =$easingIn
|
|
571
|
+
$win.BeginAnimation([System.Windows.Window]::TopProperty,$slideIn)
|
|
572
|
+
$fadeIn = New-Object System.Windows.Media.Animation.DoubleAnimation(0, 1, [TimeSpan]::FromMilliseconds(200))
|
|
573
|
+
$fadeIn.EasingFunction =$easingIn
|
|
574
|
+
$win.BeginAnimation([System.Windows.Window]::OpacityProperty,$fadeIn)
|
|
575
|
+
$scInX = New-Object System.Windows.Media.Animation.DoubleAnimation(0.96, 1, [TimeSpan]::FromMilliseconds(260))
|
|
576
|
+
$scInX.EasingFunction =$easingIn
|
|
577
|
+
$scInY = New-Object System.Windows.Media.Animation.DoubleAnimation(0.96, 1, [TimeSpan]::FromMilliseconds(260))
|
|
578
|
+
$scInY.EasingFunction =$easingIn
|
|
579
|
+
$cardT.BeginAnimation([System.Windows.Media.ScaleTransform]::ScaleXProperty,$scInX)
|
|
580
|
+
$cardT.BeginAnimation([System.Windows.Media.ScaleTransform]::ScaleYProperty,$scInY)
|
|
394
581
|
$guard = New-Object System.Windows.Threading.DispatcherTimer
|
|
395
582
|
$guard.Interval = [TimeSpan]::FromMilliseconds(400)
|
|
396
|
-
$guard.Tag = @{ w
|
|
583
|
+
$guard.Tag = @{ w =$win; top = $targetTop }
|
|
397
584
|
$guard.Add_Tick({
|
|
398
585
|
$this.Stop()
|
|
399
|
-
$g
|
|
586
|
+
$g =$this.Tag
|
|
400
587
|
try {
|
|
401
|
-
if ($g.w.Opacity -lt 0.9) {
|
|
402
|
-
if ($g.w.Top -ne
|
|
588
|
+
if ($g.w.Opacity -lt 0.9) {$g.w.Opacity = 1 }
|
|
589
|
+
if ($g.w.Top -ne$g.top) { $g.w.Top =$g.top }
|
|
403
590
|
} catch { }
|
|
404
591
|
}) | Out-Null
|
|
405
592
|
$guard.Start()
|
|
406
593
|
} catch {
|
|
407
594
|
$win.Opacity = 1
|
|
408
|
-
$win.Top
|
|
595
|
+
$win.Top =$targetTop
|
|
409
596
|
}
|
|
410
597
|
try {
|
|
411
598
|
$hwnd = [System.Windows.Interop.WindowInteropHelper]::new($win).Handle
|
|
412
|
-
Send-Report @{
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
599
|
+
Send-Report @{
|
|
600
|
+
type = 'pos'; key = [string]$cmd.key; instance = [int]$cmd.instance
|
|
601
|
+
left = [Math]::Round($targetLeft); top = [Math]::Round($targetTop)
|
|
602
|
+
height = [Math]::Round($win.ActualHeight); hwnd =$hwnd.ToInt64()
|
|
603
|
+
workBottom = [Math]::Round($work.Bottom)
|
|
604
|
+
}
|
|
416
605
|
} catch { }
|
|
606
|
+
|
|
607
|
+
# ── 停留阶段:非粘性 = 液面匀速右→左;粘性 = 色条呼吸 ──
|
|
608
|
+
if (-not $isSticky) {
|
|
609
|
+
try {
|
|
610
|
+
$shrink = New-Object System.Windows.Media.Animation.DoubleAnimation(
|
|
611
|
+
1, 0, [TimeSpan]::FromSeconds($dur))
|
|
612
|
+
# 无缓动:视觉时长 = 真实剩余时间。
|
|
613
|
+
$tintT.BeginAnimation([System.Windows.Media.ScaleTransform]::ScaleXProperty,$shrink)
|
|
614
|
+
} catch { }
|
|
615
|
+
} else {
|
|
616
|
+
try {
|
|
617
|
+
$pulse = New-Object System.Windows.Media.Animation.DoubleAnimation(0.55, 1.0,
|
|
618
|
+
[TimeSpan]::FromMilliseconds(1100))
|
|
619
|
+
$pulse.AutoReverse =$true
|
|
620
|
+
$pulse.RepeatBehavior = [System.Windows.Media.Animation.RepeatBehavior]::Forever
|
|
621
|
+
$pulse.EasingFunction = New-Object System.Windows.Media.Animation.QuadraticEase
|
|
622
|
+
$accentStrip.BeginAnimation([System.Windows.UIElement]::OpacityProperty,$pulse)
|
|
623
|
+
} catch { }
|
|
624
|
+
}
|
|
417
625
|
}.GetNewClosure()) | Out-Null
|
|
418
|
-
|
|
419
|
-
$toastsRef
|
|
626
|
+
|
|
627
|
+
$toastsRef =$script:toasts
|
|
420
628
|
$win.Add_Closed({
|
|
421
629
|
if ($toastsRef.ContainsKey([string]$cmd.key)) {
|
|
422
|
-
$rec
|
|
423
|
-
if ($rec.Timer -ne
|
|
630
|
+
$rec =$toastsRef[[string]$cmd.key]
|
|
631
|
+
if ($rec.Timer -ne$null) {
|
|
632
|
+
try { $rec.Timer.Stop() } catch { }
|
|
633
|
+
}
|
|
424
634
|
$toastsRef.Remove([string]$cmd.key) | Out-Null
|
|
425
635
|
}
|
|
426
636
|
Send-Report @{ type = 'exit'; key = [string]$cmd.key; instance = [int]$cmd.instance }
|
|
427
637
|
}.GetNewClosure()) | Out-Null
|
|
428
|
-
|
|
429
|
-
$rec = @{ Win
|
|
638
|
+
|
|
639
|
+
$rec = @{ Win =$win; Body = $bodyText; Title =$titleText; Timer = $null; Instance = [int]$cmd.instance }
|
|
430
640
|
$script:toasts[[string]$cmd.key] = $rec
|
|
431
|
-
|
|
432
|
-
if (-not
|
|
433
|
-
$dur = [Math]::Max(1, [int]$cmd.durationSec)
|
|
641
|
+
|
|
642
|
+
if (-not $isSticky) {
|
|
434
643
|
$timer = New-Object System.Windows.Threading.DispatcherTimer
|
|
435
644
|
$timer.Interval = [TimeSpan]::FromSeconds($dur)
|
|
436
|
-
$timer.Add_Tick({
|
|
645
|
+
$timer.Add_Tick({$this.Stop(); Close-WithFade $win }.GetNewClosure()) | Out-Null
|
|
437
646
|
$timer.Start()
|
|
438
|
-
$rec.Timer
|
|
647
|
+
$rec.Timer =$timer
|
|
439
648
|
}
|
|
649
|
+
|
|
440
650
|
return $win
|
|
441
651
|
}
|
|
442
652
|
|
|
653
|
+
# ───────────────────────── 命令处理 ─────────────────────────
|
|
654
|
+
|
|
443
655
|
function Invoke-Command([string]$line) {
|
|
444
656
|
$line = $line.Trim()
|
|
445
657
|
if ([string]::IsNullOrEmpty($line)) { return }
|
|
446
658
|
$cmd = $null
|
|
447
659
|
try { $cmd = $line | ConvertFrom-Json } catch { return }
|
|
448
660
|
if ($cmd -eq $null -or [string]::IsNullOrEmpty([string]$cmd.cmd)) { return }
|
|
449
|
-
|
|
661
|
+
|
|
450
662
|
switch ([string]$cmd.cmd) {
|
|
451
663
|
'show' {
|
|
452
664
|
if ($script:toasts.ContainsKey([string]$cmd.key)) {
|
|
@@ -497,19 +709,28 @@ function Invoke-Command([string]$line) {
|
|
|
497
709
|
if ($rec -ne $null -and $null -ne $cmd.width) { $rec.Win.Width = [double]$cmd.width }
|
|
498
710
|
}
|
|
499
711
|
'edit' {
|
|
500
|
-
# 编辑模式:关闭旧样板(如有),弹新样板窗(
|
|
501
|
-
if ($script:editSample -ne $null) {
|
|
712
|
+
# 编辑模式:关闭旧样板(如有),弹新样板窗(可拖拽/缩放,内嵌类型色卡预览)。
|
|
713
|
+
if ($script:editSample -ne $null) {
|
|
714
|
+
try { $script:editSample.Close() } catch { }
|
|
715
|
+
$script:editSample = $null
|
|
716
|
+
}
|
|
502
717
|
$w = New-EditSample $cmd
|
|
503
718
|
if ($w -ne $null) { $w.Show(); $script:editSample = $w }
|
|
504
719
|
}
|
|
505
720
|
'edit-end' {
|
|
506
|
-
if ($script:editSample -ne $null) {
|
|
721
|
+
if ($script:editSample -ne $null) {
|
|
722
|
+
try { $script:editSample.Close() } catch { }
|
|
723
|
+
$script:editSample = $null
|
|
724
|
+
}
|
|
507
725
|
}
|
|
508
726
|
'shutdown' { $script:exitRequested = $true }
|
|
509
727
|
}
|
|
510
728
|
}
|
|
511
729
|
|
|
730
|
+
# ───────────────────────── 主循环 ─────────────────────────
|
|
731
|
+
|
|
512
732
|
$script:cmdQueue = New-Object 'System.Collections.Concurrent.ConcurrentQueue[string]'
|
|
733
|
+
|
|
513
734
|
$readerScript = {
|
|
514
735
|
param($q)
|
|
515
736
|
try {
|
|
@@ -553,6 +774,7 @@ $cmdTimer.Add_Tick({
|
|
|
553
774
|
} catch { }
|
|
554
775
|
}) | Out-Null
|
|
555
776
|
$cmdTimer.Start()
|
|
777
|
+
|
|
556
778
|
[System.Windows.Threading.Dispatcher]::Run()
|
|
557
779
|
|
|
558
780
|
try { if ($readerAsyncResult -ne $null) { $readerPs.EndInvoke($readerAsyncResult) } } catch { }
|
package/lib/typert.host.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
const kindColorSchema = z.object({
|
|
11
11
|
bg: z.string(),
|
|
12
12
|
fg: z.string(),
|
|
13
|
+
accent: z.string().optional(),
|
|
13
14
|
enabled: z.boolean(),
|
|
14
15
|
})
|
|
15
16
|
|
|
@@ -224,4 +225,4 @@ export const TYPERT = {
|
|
|
224
225
|
},
|
|
225
226
|
}
|
|
226
227
|
|
|
227
|
-
export default TYPERT
|
|
228
|
+
export default TYPERT
|
package/package.json
CHANGED