@a9i5k4/dsh-auto-memory 0.1.13 → 0.1.15
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/LICENSE +29 -29
- package/cordis.patch.yml +9 -9
- package/lib/client.js +1559 -1436
- package/lib/index.js +2710 -2691
- package/package.json +46 -46
package/lib/client.js
CHANGED
|
@@ -1,1436 +1,1559 @@
|
|
|
1
|
-
/* dsh-auto-memory — browser half (hand-written __ModuleLoader__ bundle).
|
|
2
|
-
* Registers three additive surfaces:
|
|
3
|
-
* 1. sidebar.footer.action — 「记忆」入口按钮(开关左下角浮层面板)
|
|
4
|
-
* 2. shell.overlay — 记忆面板:概览 / 日志 / 笔记 / 反思 / 检索
|
|
5
|
-
* 液态玻璃视觉(backdrop-filter + --dsw-alias-* 主题令牌),
|
|
6
|
-
* 可拖动 / 右下角缩放 / 开关缩放动画 / 位置大小持久化。
|
|
7
|
-
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
|
-
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
|
-
*/
|
|
10
|
-
console.log('[dsh-auto-memory] client v0.1.13 fingerprint: drawer-summary-ok')
|
|
11
|
-
window.__ModuleLoader__.load({
|
|
12
|
-
id: '@a9i5k4/dsh-auto-memory',
|
|
13
|
-
factory: (require) => {
|
|
14
|
-
var module = { exports: {} }
|
|
15
|
-
var exports = module.exports
|
|
16
|
-
var React = require('react')
|
|
17
|
-
var h = React.createElement
|
|
18
|
-
var useState = React.useState
|
|
19
|
-
var useEffect = React.useEffect
|
|
20
|
-
var useReducer = React.useReducer
|
|
21
|
-
|
|
22
|
-
// ───────────────────────── 控制器 ─────────────────────────
|
|
23
|
-
var listeners = new Set()
|
|
24
|
-
var panelOpen = false
|
|
25
|
-
var panelClosing = false
|
|
26
|
-
var closeTimer = null
|
|
27
|
-
// 几何状态:left/top/width/height,持久化到 localStorage(用户可拖动/缩放)
|
|
28
|
-
var GEOM_KEY = 'dsh-auto-memory.panel.geom'
|
|
29
|
-
var DEFAULT_W = 440
|
|
30
|
-
var DEFAULT_H = 560
|
|
31
|
-
var DEFAULT_GAP = 16
|
|
32
|
-
function defaultGeom() {
|
|
33
|
-
var vh = window.innerHeight || 800
|
|
34
|
-
return {
|
|
35
|
-
left: DEFAULT_GAP,
|
|
36
|
-
top: Math.max(DEFAULT_GAP, vh - DEFAULT_H - DEFAULT_GAP),
|
|
37
|
-
width: DEFAULT_W,
|
|
38
|
-
height: DEFAULT_H,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
var geom = null
|
|
42
|
-
function clampGeom(g) {
|
|
43
|
-
var vw = window.innerWidth || 1280
|
|
44
|
-
var vh = window.innerHeight || 800
|
|
45
|
-
var w = Math.max(300, Math.min(g.width || DEFAULT_W, vw - DEFAULT_GAP * 2))
|
|
46
|
-
var h = Math.max(240, Math.min(g.height || DEFAULT_H, vh - DEFAULT_GAP * 2))
|
|
47
|
-
return {
|
|
48
|
-
left: Math.max(DEFAULT_GAP, Math.min(g.left !== undefined ? g.left : DEFAULT_GAP, vw - w - DEFAULT_GAP)),
|
|
49
|
-
top: Math.max(DEFAULT_GAP, Math.min(g.top !== undefined ? g.top : DEFAULT_GAP, vh - h - DEFAULT_GAP)),
|
|
50
|
-
width: w,
|
|
51
|
-
height: h,
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
function loadGeom() {
|
|
55
|
-
try {
|
|
56
|
-
var raw = localStorage.getItem(GEOM_KEY)
|
|
57
|
-
if (raw) return clampGeom(JSON.parse(raw))
|
|
58
|
-
} catch (e) {}
|
|
59
|
-
return clampGeom(defaultGeom())
|
|
60
|
-
}
|
|
61
|
-
function persistGeom() { if (geom) { try { localStorage.setItem(GEOM_KEY, JSON.stringify(geom)) } catch (e) {} } }
|
|
62
|
-
function emit() { listeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
63
|
-
var controller = {
|
|
64
|
-
isOpen: function () { return panelOpen },
|
|
65
|
-
isClosing: function () { return panelClosing },
|
|
66
|
-
geom: function () { if (!geom) geom = loadGeom(); return geom },
|
|
67
|
-
setGeom: function (partial) {
|
|
68
|
-
var cur = controller.geom()
|
|
69
|
-
geom = clampGeom({
|
|
70
|
-
left: partial.left !== undefined ? partial.left : cur.left,
|
|
71
|
-
top: partial.top !== undefined ? partial.top : cur.top,
|
|
72
|
-
width: partial.width !== undefined ? partial.width : cur.width,
|
|
73
|
-
height: partial.height !== undefined ? partial.height : cur.height,
|
|
74
|
-
})
|
|
75
|
-
emit()
|
|
76
|
-
},
|
|
77
|
-
flushGeom: function () { persistGeom() },
|
|
78
|
-
resetGeom: function () { geom = clampGeom(defaultGeom()); persistGeom(); emit() },
|
|
79
|
-
toggle: function () { if (panelOpen) controller.close(); else controller.open() },
|
|
80
|
-
open: function () {
|
|
81
|
-
if (closeTimer) { clearTimeout(closeTimer); closeTimer = null }
|
|
82
|
-
panelOpen = true; panelClosing = false; emit()
|
|
83
|
-
},
|
|
84
|
-
close: function () {
|
|
85
|
-
if (!panelOpen || panelClosing) return
|
|
86
|
-
panelClosing = true; emit()
|
|
87
|
-
closeTimer = setTimeout(function () {
|
|
88
|
-
panelOpen = false; panelClosing = false; closeTimer = null; emit()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
var
|
|
281
|
-
var
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
'
|
|
347
|
-
'
|
|
348
|
-
'
|
|
349
|
-
'
|
|
350
|
-
|
|
351
|
-
'
|
|
352
|
-
'
|
|
353
|
-
|
|
354
|
-
'
|
|
355
|
-
'
|
|
356
|
-
'
|
|
357
|
-
'
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
'
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
var
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
var
|
|
434
|
-
|
|
435
|
-
var
|
|
436
|
-
var
|
|
437
|
-
var
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
var
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
//
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
var
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
state.
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
(
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
})(
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
var
|
|
706
|
-
var
|
|
707
|
-
var
|
|
708
|
-
var
|
|
709
|
-
var
|
|
710
|
-
var
|
|
711
|
-
var
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
return h(
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
h(
|
|
729
|
-
h('
|
|
730
|
-
h('
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
var
|
|
746
|
-
var
|
|
747
|
-
var
|
|
748
|
-
var
|
|
749
|
-
var
|
|
750
|
-
var
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
h('button', { 'data-dam-btn': '', onClick:
|
|
778
|
-
h('span', { 'data-dam-hint': '' }, t('
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
var
|
|
793
|
-
var
|
|
794
|
-
var
|
|
795
|
-
var
|
|
796
|
-
var
|
|
797
|
-
var
|
|
798
|
-
var
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
function
|
|
805
|
-
if (!
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
:
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
'
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
var
|
|
881
|
-
var
|
|
882
|
-
var
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
function
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
var
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
rows.push(h('div',
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
})
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
return
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
var
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
var
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
h('
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
var
|
|
1242
|
-
var
|
|
1243
|
-
var
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
var
|
|
1277
|
-
var
|
|
1278
|
-
var
|
|
1279
|
-
var
|
|
1280
|
-
var
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
.
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
if (
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
})
|
|
1
|
+
/* dsh-auto-memory — browser half (hand-written __ModuleLoader__ bundle).
|
|
2
|
+
* Registers three additive surfaces:
|
|
3
|
+
* 1. sidebar.footer.action — 「记忆」入口按钮(开关左下角浮层面板)
|
|
4
|
+
* 2. shell.overlay — 记忆面板:概览 / 日志 / 笔记 / 反思 / 检索
|
|
5
|
+
* 液态玻璃视觉(backdrop-filter + --dsw-alias-* 主题令牌),
|
|
6
|
+
* 可拖动 / 右下角缩放 / 开关缩放动画 / 位置大小持久化。
|
|
7
|
+
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
|
+
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
|
+
*/
|
|
10
|
+
console.log('[dsh-auto-memory] client v0.1.13 fingerprint: drawer-summary-ok')
|
|
11
|
+
window.__ModuleLoader__.load({
|
|
12
|
+
id: '@a9i5k4/dsh-auto-memory',
|
|
13
|
+
factory: (require) => {
|
|
14
|
+
var module = { exports: {} }
|
|
15
|
+
var exports = module.exports
|
|
16
|
+
var React = require('react')
|
|
17
|
+
var h = React.createElement
|
|
18
|
+
var useState = React.useState
|
|
19
|
+
var useEffect = React.useEffect
|
|
20
|
+
var useReducer = React.useReducer
|
|
21
|
+
|
|
22
|
+
// ───────────────────────── 控制器 ─────────────────────────
|
|
23
|
+
var listeners = new Set()
|
|
24
|
+
var panelOpen = false
|
|
25
|
+
var panelClosing = false
|
|
26
|
+
var closeTimer = null
|
|
27
|
+
// 几何状态:left/top/width/height,持久化到 localStorage(用户可拖动/缩放)
|
|
28
|
+
var GEOM_KEY = 'dsh-auto-memory.panel.geom'
|
|
29
|
+
var DEFAULT_W = 440
|
|
30
|
+
var DEFAULT_H = 560
|
|
31
|
+
var DEFAULT_GAP = 16
|
|
32
|
+
function defaultGeom() {
|
|
33
|
+
var vh = window.innerHeight || 800
|
|
34
|
+
return {
|
|
35
|
+
left: DEFAULT_GAP,
|
|
36
|
+
top: Math.max(DEFAULT_GAP, vh - DEFAULT_H - DEFAULT_GAP),
|
|
37
|
+
width: DEFAULT_W,
|
|
38
|
+
height: DEFAULT_H,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
var geom = null
|
|
42
|
+
function clampGeom(g) {
|
|
43
|
+
var vw = window.innerWidth || 1280
|
|
44
|
+
var vh = window.innerHeight || 800
|
|
45
|
+
var w = Math.max(300, Math.min(g.width || DEFAULT_W, vw - DEFAULT_GAP * 2))
|
|
46
|
+
var h = Math.max(240, Math.min(g.height || DEFAULT_H, vh - DEFAULT_GAP * 2))
|
|
47
|
+
return {
|
|
48
|
+
left: Math.max(DEFAULT_GAP, Math.min(g.left !== undefined ? g.left : DEFAULT_GAP, vw - w - DEFAULT_GAP)),
|
|
49
|
+
top: Math.max(DEFAULT_GAP, Math.min(g.top !== undefined ? g.top : DEFAULT_GAP, vh - h - DEFAULT_GAP)),
|
|
50
|
+
width: w,
|
|
51
|
+
height: h,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function loadGeom() {
|
|
55
|
+
try {
|
|
56
|
+
var raw = localStorage.getItem(GEOM_KEY)
|
|
57
|
+
if (raw) return clampGeom(JSON.parse(raw))
|
|
58
|
+
} catch (e) {}
|
|
59
|
+
return clampGeom(defaultGeom())
|
|
60
|
+
}
|
|
61
|
+
function persistGeom() { if (geom) { try { localStorage.setItem(GEOM_KEY, JSON.stringify(geom)) } catch (e) {} } }
|
|
62
|
+
function emit() { listeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
63
|
+
var controller = {
|
|
64
|
+
isOpen: function () { return panelOpen },
|
|
65
|
+
isClosing: function () { return panelClosing },
|
|
66
|
+
geom: function () { if (!geom) geom = loadGeom(); return geom },
|
|
67
|
+
setGeom: function (partial) {
|
|
68
|
+
var cur = controller.geom()
|
|
69
|
+
geom = clampGeom({
|
|
70
|
+
left: partial.left !== undefined ? partial.left : cur.left,
|
|
71
|
+
top: partial.top !== undefined ? partial.top : cur.top,
|
|
72
|
+
width: partial.width !== undefined ? partial.width : cur.width,
|
|
73
|
+
height: partial.height !== undefined ? partial.height : cur.height,
|
|
74
|
+
})
|
|
75
|
+
emit()
|
|
76
|
+
},
|
|
77
|
+
flushGeom: function () { persistGeom() },
|
|
78
|
+
resetGeom: function () { geom = clampGeom(defaultGeom()); persistGeom(); emit() },
|
|
79
|
+
toggle: function () { if (panelOpen) controller.close(); else controller.open() },
|
|
80
|
+
open: function () {
|
|
81
|
+
if (closeTimer) { clearTimeout(closeTimer); closeTimer = null }
|
|
82
|
+
panelOpen = true; panelClosing = false; emit()
|
|
83
|
+
},
|
|
84
|
+
close: function () {
|
|
85
|
+
if (!panelOpen || panelClosing) return
|
|
86
|
+
panelClosing = true; emit()
|
|
87
|
+
closeTimer = setTimeout(function () {
|
|
88
|
+
panelOpen = false; panelClosing = false; closeTimer = null; emit()
|
|
89
|
+
// 面板关闭时记录本次活动时间:离开>1小时再回来时自动弹开+欢迎语
|
|
90
|
+
try { localStorage.setItem('dsh-auto-memory.lastActive', String(Date.now())) } catch (e) {}
|
|
91
|
+
}, 170)
|
|
92
|
+
},
|
|
93
|
+
subscribe: function (fn) { listeners.add(fn); return function () { listeners.delete(fn) } },
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ───────────────────────── 国际化 ─────────────────────────
|
|
97
|
+
var I18N = {
|
|
98
|
+
zh: {
|
|
99
|
+
loading: '加载中…',
|
|
100
|
+
memoryPanel: '记忆面板',
|
|
101
|
+
memory: '记忆',
|
|
102
|
+
autoMemory: '自动记忆',
|
|
103
|
+
overview: '概览', logs: '日志', notes: '笔记', reflections: '反思', connect: '接续', calendar: '日历', search: '检索', workspaces: '工作区',
|
|
104
|
+
wsGenerating: '正在生成各工作区总结(每个工作区一次 AI 调用,可能稍慢)…', wsNone: '未发现带记忆的工作区', wsNoSummary: '(无总结)', wsOverview: '工作区总结(跨工作区)',
|
|
105
|
+
debugCenter: '调试中心', dbgLoading: '加载诊断信息…', dbgRefresh: '刷新', dbgRefreshing: '刷新中…',
|
|
106
|
+
fMemoryRoot: '记忆根目录', fMemoryRootHint: '集中式存储:所有工作区记忆统一放在此目录下(每工作区一个子目录),旧版分散的记忆会自动迁移。',
|
|
107
|
+
fBrowse: '浏览…', browseTitle: '选择记忆根目录', fUp: '上级', fSelectDir: '选择此目录',
|
|
108
|
+
pickedDir: '已选择目录:', rememberSave: '(点击「保存设置」后生效)', pickerUnavailable: '系统文件夹选择器不可用,改用内嵌浏览。',
|
|
109
|
+
fDayBoundary: '日界(分钟)', fDayBoundaryHint: '从 0 点起算:凌晨在此之前的活儿归前一天。默认 450=早上 7:30 进入新一天;改 480=8:00;0=按午夜切日。',
|
|
110
|
+
fVersion: '插件版本', checkUpdate: '检查更新', checking: '检查中…', upToDate: '(已是最新)', hasUpdate: '(有新版本)', versionError: '版本检查失败: ', versionCmdHint: '更新命令: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory,然后重启 dsh web 生效。',
|
|
111
|
+
updateNow: '一键更新', updating: '更新中…', updateDone: '更新完成,请重启 dsh web 生效。', updateFailed: '更新失败: ', devLinkHint: '(当前为本地开发链接,请直接同步开发源码)', noProfileHint: '(未找到 dsh profile,无法自动更新)',
|
|
112
|
+
kvVersion: '插件版本', kvUnreadable: '(读不到)', kvPid: 'PID / 启动', kvRestart: 'Host 需重启?', kvRestartYes: '是(代码比进程新)', kvRestartNo: '否',
|
|
113
|
+
kvHeartbeat: '轮询心跳', hbAliveAgo: '存活(上次 ', hbSecAgo: ' 秒前)', hbAlive: '存活', hbNone: '无心跳文件',
|
|
114
|
+
kvQueue: '沉淀重试队列', kvCountSuffix: ' 条', kvToday: '今日沉淀', kvRecentPrefix: ' 条 / 最近 ', kvBusy: '正在沉淀中', kvYes: '是', kvNo: '否',
|
|
115
|
+
kvAvail: '可用', kvUnavail: '不可用', kvDup: '项目笔记重复标题', kvDupCount: ' 个',
|
|
116
|
+
kvMem: '记忆文件', kvMemUser: '用户', kvMemNotes: '笔记', kvMemLog: '日志', kvMemMissing: '缺失',
|
|
117
|
+
kvWs: '当前工作区', kvWsUnknown: '(未取到)', kvApi: 'API 连通', unknown: '(未知)',
|
|
118
|
+
guideTitle: '欢迎使用 dsh-auto-memory', guideSub: '这个插件能干什么:',
|
|
119
|
+
gFeat1: '三层记忆自动注入与检索:用户级 / 项目笔记 / 每日日志,每轮对话自动读、自动写',
|
|
120
|
+
gFeat2: '每轮对话结束自动沉淀:修了什么 bug、做了哪些决策,自动按主题写进今日日志',
|
|
121
|
+
gFeat3: 'AI 时段问候与三级抽屉:打开「记忆」面板,AI 按时段向你汇报当天进展',
|
|
122
|
+
gFeat4: '智能检索与日历:自然语言提问找记忆;AI 自动把 deadline 记进日历并提醒',
|
|
123
|
+
gFeat5: '接续其他 AI 工具的记忆:CodeBuddy / Claude Code / Codex 等的历史记忆一键接入',
|
|
124
|
+
gFeat6: '自动检查更新:启动时检测新版本,设置页可一键更新',
|
|
125
|
+
guideTip: '打开侧边栏「记忆」按钮即可使用;所有记忆都是本地明文文件,可随时查看修改。',
|
|
126
|
+
updateTitle: 'dsh-auto-memory 已更新', updateSub: '本次更新内容:', gotIt: '知道了',
|
|
127
|
+
refresh: '刷新', refreshing: '刷新中…', close: '关闭', dragMove: '拖动移动', dragResize: '拖动调整大小', resetPos: '恢复默认位置',
|
|
128
|
+
generated: '已生成', failed: '失败: ',
|
|
129
|
+
generatedAt: '生成于 ',
|
|
130
|
+
pointsCount: ' 项', summarizing: 'AI 总结生成中…', summaryFailed: 'AI 总结生成失败,点 ⟳ 重试',
|
|
131
|
+
pendingReflection: '待生成反思: ',
|
|
132
|
+
pendingReflectionHint: ' —— 可点下方「一键反思」立即生成。',
|
|
133
|
+
todayWork: '今日工作', logEntries: ' 条日志',
|
|
134
|
+
autoSettledToday: '今日已自动沉淀 ', autoSettledSuffix: ' 条要点',
|
|
135
|
+
autoSettledRecent: '最近: ', autoSettledNone: '本轮对话暂无自动沉淀',
|
|
136
|
+
dailyReflection: '每日反思', notYet: '(还没有)',
|
|
137
|
+
workspace: '工作区',
|
|
138
|
+
reflecting: '反思生成中…', oneClickReflect: '一键反思',
|
|
139
|
+
reflectHint: '用最近日志自动生成反思',
|
|
140
|
+
quickLinks: '快捷入口:日志页签看每日记录 · 笔记页签追加项目笔记 · 接续页签接入其他 AI 记忆 · 检索页签全文搜索。',
|
|
141
|
+
collapseTech: '收起技术信息 ▴', expandTech: '技术信息 ▾',
|
|
142
|
+
userMemory: '用户级记忆', projectNotes: '项目笔记', todayLog: '今日日志', configFile: '配置文件',
|
|
143
|
+
readFailed: '读取失败: ', ok: '正常', refreshTime: '刷新时间', notYetShort: '尚未',
|
|
144
|
+
empty: '(空)', back: '← 返回', view: '查看',
|
|
145
|
+
clickDateViewLog: '点击日期查看当日日志(append-only):',
|
|
146
|
+
appended: '已追加', notesPathLabel: '项目笔记: ',
|
|
147
|
+
notesPlaceholder: '想追加到项目笔记的内容…(保存时自动带日期标题)',
|
|
148
|
+
saving: '保存中…', append: '追加',
|
|
149
|
+
notesHint: '建议直接用对话让 agent 调 memory_note;此处为手动追加。',
|
|
150
|
+
reflectionTitle: '反思 ', generating: '生成中…',
|
|
151
|
+
reflectAutoHint: '自动用最近日志生成反思草稿(便于测试)',
|
|
152
|
+
noReflection: '还没有反思。每天第一次会话时,agent 会主动呈现前一天的工作反思。',
|
|
153
|
+
searchFailed: '检索失败: ', searchPlaceholder: '搜记忆:关键词或自包含描述…', searchBtn: '检索', resultTitle: '结果',
|
|
154
|
+
smartSearch: '智能检索', smartAnswer: '智能回答', keywordsLabel: '关键词: ',
|
|
155
|
+
projectFiles: '项目文件', aiAssistant: 'AI 助手',
|
|
156
|
+
imported: '接入完成', importing: '正在接入 ', importingSuffix: ' 个源…', allImported: '全部接入完成',
|
|
157
|
+
noExternal: '未检测到其他 AI 工具的记忆文件(CodeBuddy/Claude Code/Codex 等)。',
|
|
158
|
+
sessionSource: '会话源:共 ', sessionSourceSuffix: ' 个会话文件,用 memory_recall 按需检索',
|
|
159
|
+
importingOne: '接入中…', importToNotes: '接入项目笔记', importToUser: '接入用户记忆',
|
|
160
|
+
disabled: ' · 已停用', filesCount: ' 个文件 · ',
|
|
161
|
+
connectHint: '把其他 AI 工具(CodeBuddy / Claude Code / Codex / 项目约定文件等)积累的记忆接入当前 DSH 工作。接入后内容写入本地记忆并自动标注来源,后续会随会话自动注入。',
|
|
162
|
+
importAll: '一键接入全部', rescan: '重新扫描',
|
|
163
|
+
styleAuto: '由内容决定', styleLife: '生活化', styleProfessional: '专业性',
|
|
164
|
+
todayGreetingTitle: '问候',
|
|
165
|
+
yesterdayTimeline: '昨天(', pendingReflectionShort: '昨天的工作还没复盘(',
|
|
166
|
+
saved: '已保存',
|
|
167
|
+
settingsHeader: '记忆存储与行为设置(保存到 DSH 主目录 dsh-auto-memory.json):',
|
|
168
|
+
fFontSize: '界面字号', fFontSizeHint: '记忆面板文字大小(立即生效,仅本机)', fsSm: '小', fsMd: '标准', fsLg: '大', fsXl: '特大',
|
|
169
|
+
fUserDir: '用户记忆目录', fUserDirHint: '跨项目规则存放处,支持 ~ 开头;需有文件写权限。',
|
|
170
|
+
fProjectDir: '项目记忆目录', fProjectDirHint: '相对各工作区的目录名(默认 .dsh-memory)。',
|
|
171
|
+
fInject: '注入记忆上下文', fInjectHint: '每次组装提示词时自动注入 <memory_system> 块。',
|
|
172
|
+
fBudget: '注入预算(字符)', fBudgetHint: '记忆块总预算,超出部分截断。',
|
|
173
|
+
fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N 天的工作日志尾部。',
|
|
174
|
+
fReflect: '每日反思', fReflectHint: '昨天有工作日志时,会话首轮主动呈现昨日反思。',
|
|
175
|
+
fStyle: '反思风格', fStyleHint: '生活化 / 专业性 / 由内容决定。',
|
|
176
|
+
fLocale: '界面语言', fLocaleHint: '默认跟随 DSH 系统语言;也可手动指定中文 / English。', followSystem: '跟随系统语言',
|
|
177
|
+
saveSettings: '保存设置',
|
|
178
|
+
zh: '中文', en: 'English',
|
|
179
|
+
calendar: '日历', addItem: '添加', save: '保存', cancel: '取消', needTitle: '请填写事项标题', itemTitle: '事项标题…',
|
|
180
|
+
segMorning: '早晨', segForenoon: '上午', segNoon: '中午', segAfternoon: '下午', segEvening: '晚上',
|
|
181
|
+
segPrefix: '今日', segMorningHint: '(昨日摘要)',
|
|
182
|
+
welcomeBack: '欢迎回来!这段时间你完成了这些工作:',
|
|
183
|
+
yesterdayDrawer: '昨天',
|
|
184
|
+
greetMorning: '新的一天开始啦,先看看昨天做了什么。', greetForenoon: '上午好,今天也在稳步推进。', greetNoon: '中午好,歇口气再继续。', greetAfternoon: '下午好,下午也要元气满满。', greetEvening: '晚上好,辛苦一天了。',
|
|
185
|
+
greetSummary: '今天已经完成了 ', greetThings: ' 件工作,点开看看:',
|
|
186
|
+
qUrgentImportant: '重要紧急', qImportant: '重要不紧急', qUrgent: '紧急不重要', qNone: '不重要不紧急', qUncategorized: '未分类',
|
|
187
|
+
},
|
|
188
|
+
en: {
|
|
189
|
+
loading: 'Loading…',
|
|
190
|
+
memoryPanel: 'Memory Panel',
|
|
191
|
+
memory: 'Memory',
|
|
192
|
+
autoMemory: 'Auto Memory',
|
|
193
|
+
overview: 'Overview', logs: 'Logs', notes: 'Notes', reflections: 'Reflections', connect: 'Connect', calendar: 'Calendar', search: 'Search', workspaces: 'Workspaces',
|
|
194
|
+
wsGenerating: 'Generating per-workspace summaries (one AI call each, may take a while)…', wsNone: 'No workspace with memory found', wsNoSummary: '(no summary)', wsOverview: 'Workspace summaries (all workspaces)',
|
|
195
|
+
debugCenter: 'Debug Center', dbgLoading: 'Loading diagnostics…', dbgRefresh: 'Refresh', dbgRefreshing: 'Refreshing…',
|
|
196
|
+
fMemoryRoot: 'Memory root', fMemoryRootHint: 'Centralized storage: all workspace memories live here (one subdir per workspace); legacy memories are auto-migrated.',
|
|
197
|
+
fBrowse: 'Browse…', browseTitle: 'Choose memory root', fUp: 'Up', fSelectDir: 'Select this folder',
|
|
198
|
+
pickedDir: 'Selected folder:', rememberSave: '(click Save to apply)', pickerUnavailable: 'Native folder picker unavailable, falling back to in-app browser.',
|
|
199
|
+
fDayBoundary: 'Day boundary (minutes)', fDayBoundaryHint: 'Minutes from midnight: work before it counts as the previous day. Default 450 = 7:30 AM starts the new day; 480 = 8:00; 0 = midnight cut.',
|
|
200
|
+
fVersion: 'Plugin version', checkUpdate: 'Check for updates', checking: 'Checking…', upToDate: '(up to date)', hasUpdate: '(update available)', versionError: 'Version check failed: ', versionCmdHint: 'Update: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory, then restart dsh web.',
|
|
201
|
+
updateNow: 'Update now', updating: 'Updating…', updateDone: 'Update finished, restart dsh web to apply.', updateFailed: 'Update failed: ', devLinkHint: '(local dev link, sync source instead)', noProfileHint: '(no dsh profile found, cannot auto-update)',
|
|
202
|
+
kvVersion: 'Plugin version', kvUnreadable: '(unreadable)', kvPid: 'PID / Started', kvRestart: 'Host restart needed?', kvRestartYes: 'yes (code newer than process)', kvRestartNo: 'no',
|
|
203
|
+
kvHeartbeat: 'Heartbeat', hbAliveAgo: 'alive (last ', hbSecAgo: ' s ago)', hbAlive: 'alive', hbNone: 'no heartbeat file',
|
|
204
|
+
kvQueue: 'Consolidation retry queue', kvCountSuffix: ' item(s)', kvToday: 'Consolidated today', kvRecentPrefix: ' item(s) / latest ', kvBusy: 'Consolidating now', kvYes: 'yes', kvNo: 'no',
|
|
205
|
+
kvAvail: 'available', kvUnavail: 'unavailable', kvDup: 'Duplicate note headings', kvDupCount: '',
|
|
206
|
+
kvMem: 'Memory files', kvMemUser: 'user', kvMemNotes: 'notes', kvMemLog: 'log', kvMemMissing: 'missing',
|
|
207
|
+
kvWs: 'Current workspace', kvWsUnknown: '(unknown)', kvApi: 'API probes', unknown: '(unknown)',
|
|
208
|
+
guideTitle: 'Welcome to dsh-auto-memory', guideSub: 'What this plugin does:',
|
|
209
|
+
gFeat1: 'Three-layer memory with automatic injection & retrieval: user / project notes / daily logs',
|
|
210
|
+
gFeat2: 'Auto-consolidation after every turn: bugs fixed and decisions made are written into today\'s log automatically',
|
|
211
|
+
gFeat3: 'AI period greetings with three-level drawers: open the Memory panel for a period-by-period digest',
|
|
212
|
+
gFeat4: 'Smart search & calendar: ask in natural language; AI files deadlines into the calendar and reminds you',
|
|
213
|
+
gFeat5: 'Inherit memories from other AI tools: CodeBuddy / Claude Code / Codex etc. in one click',
|
|
214
|
+
gFeat6: 'Auto update check at boot, one-click update in settings',
|
|
215
|
+
guideTip: 'Click the Memory button in the sidebar to start; all memories are plain local Markdown files you can inspect anytime.',
|
|
216
|
+
updateTitle: 'dsh-auto-memory Updated', updateSub: 'What\'s new in this update:', gotIt: 'Got it',
|
|
217
|
+
refresh: 'Refresh', refreshing: 'Refreshing…', close: 'Close', dragMove: 'Drag to move', dragResize: 'Drag to resize', resetPos: 'Reset position',
|
|
218
|
+
generated: 'Generated', failed: 'Failed: ',
|
|
219
|
+
generatedAt: 'Generated ',
|
|
220
|
+
pointsCount: ' items', summarizing: 'AI summary generating…', summaryFailed: 'Summary failed, press refresh to retry',
|
|
221
|
+
pendingReflection: 'Pending reflection: ',
|
|
222
|
+
pendingReflectionHint: ' — click "One-click Reflect" below to generate now.',
|
|
223
|
+
todayWork: 'Today', logEntries: ' log entries',
|
|
224
|
+
autoSettledToday: 'Auto-consolidated ', autoSettledSuffix: ' points today',
|
|
225
|
+
autoSettledRecent: 'Latest: ', autoSettledNone: 'Nothing auto-consolidated this turn',
|
|
226
|
+
dailyReflection: 'Daily reflection', notYet: '(none yet)',
|
|
227
|
+
workspace: 'Workspace',
|
|
228
|
+
reflecting: 'Generating…', oneClickReflect: 'One-click Reflect',
|
|
229
|
+
reflectHint: 'Auto-generate reflection from recent logs',
|
|
230
|
+
quickLinks: 'Quick access: Logs tab for daily records · Notes tab to append project notes · Connect tab to import AI memories · Search tab for full-text search.',
|
|
231
|
+
collapseTech: 'Collapse details ▴', expandTech: 'Details ▾',
|
|
232
|
+
userMemory: 'User memory', projectNotes: 'Project notes', todayLog: 'Today log', configFile: 'Config file',
|
|
233
|
+
readFailed: 'Read failed: ', ok: 'OK', refreshTime: 'Refreshed', notYetShort: 'never',
|
|
234
|
+
empty: '(empty)', back: '← Back', view: 'View',
|
|
235
|
+
clickDateViewLog: 'Click a date to view the daily log (append-only):',
|
|
236
|
+
appended: 'Appended', notesPathLabel: 'Project notes: ',
|
|
237
|
+
notesPlaceholder: 'Content to append to project notes…(date heading added on save)',
|
|
238
|
+
saving: 'Saving…', append: 'Append',
|
|
239
|
+
notesHint: 'Tip: ask the agent to call memory_note in chat; this is a manual fallback.',
|
|
240
|
+
reflectionTitle: 'Reflection ', generating: 'Generating…',
|
|
241
|
+
reflectAutoHint: 'Auto-generate a reflection draft from recent logs (for testing)',
|
|
242
|
+
noReflection: "No reflections yet. On the first session each day, the agent presents the previous day's reflection.",
|
|
243
|
+
searchFailed: 'Search failed: ', searchPlaceholder: 'Search memory: keyword or self-contained description…', searchBtn: 'Search', resultTitle: 'Results',
|
|
244
|
+
smartSearch: 'Smart search', smartAnswer: 'AI Answer', keywordsLabel: 'Keywords: ',
|
|
245
|
+
projectFiles: 'Project files', aiAssistant: 'AI Assistant',
|
|
246
|
+
imported: 'Imported', importing: 'Importing ', importingSuffix: ' sources…', allImported: 'All imported',
|
|
247
|
+
noExternal: 'No memory files found from other AI tools (CodeBuddy/Claude Code/Codex etc.).',
|
|
248
|
+
sessionSource: 'Session source: ', sessionSourceSuffix: ' session files, search on demand with memory_recall',
|
|
249
|
+
importingOne: 'Importing…', importToNotes: 'Import to notes', importToUser: 'Import to user memory',
|
|
250
|
+
disabled: ' · disabled', filesCount: ' files · ',
|
|
251
|
+
connectHint: 'Import memories accumulated by other AI tools (CodeBuddy / Claude Code / Codex / project convention files) into the current DSH workspace. Imported content is written to local memory with its source noted, and is auto-injected in future sessions.',
|
|
252
|
+
importAll: 'Import all', rescan: 'Rescan',
|
|
253
|
+
styleAuto: 'Auto', styleLife: 'Life-style', styleProfessional: 'Professional',
|
|
254
|
+
todayGreetingTitle: 'Greeting',
|
|
255
|
+
yesterdayTimeline: 'Yesterday (', pendingReflectionShort: 'Yesterday\'s work not reviewed (',
|
|
256
|
+
saved: 'Saved',
|
|
257
|
+
settingsHeader: 'Memory storage & behavior (saved to ~/.dsh/dsh-auto-memory.json):',
|
|
258
|
+
fFontSize: 'Panel font size', fFontSizeHint: 'Memory panel text size (applies immediately, this device only)', fsSm: 'Small', fsMd: 'Normal', fsLg: 'Large', fsXl: 'Extra large',
|
|
259
|
+
fUserDir: 'User memory dir', fUserDirHint: 'Cross-project rules; supports ~ prefix; needs write permission.',
|
|
260
|
+
fProjectDir: 'Project memory dir', fProjectDirHint: 'Directory name relative to each workspace (default .dsh-memory).',
|
|
261
|
+
fInject: 'Inject memory context', fInjectHint: 'Auto-inject <memory_system> block into every prompt.',
|
|
262
|
+
fBudget: 'Injection budget (chars)', fBudgetHint: 'Total budget for the memory block; excess is truncated.',
|
|
263
|
+
fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start.',
|
|
264
|
+
fReflect: 'Daily reflection', fReflectHint: 'When yesterday has logs, the agent presents the reflection at session start.',
|
|
265
|
+
fStyle: 'Reflection style', fStyleHint: 'Life-style / Professional / Auto.',
|
|
266
|
+
fLocale: 'UI language', fLocaleHint: 'Follows the DSH system language by default; you can also pin Chinese / English.', followSystem: 'Follow system language',
|
|
267
|
+
saveSettings: 'Save settings',
|
|
268
|
+
zh: '中文', en: 'English',
|
|
269
|
+
calendar: 'Calendar', addItem: 'Add', save: 'Save', cancel: 'Cancel', needTitle: 'Title is required', itemTitle: 'Item title…',
|
|
270
|
+
segMorning: 'Morning', segForenoon: 'Forenoon', segNoon: 'Noon', segAfternoon: 'Afternoon', segEvening: 'Evening',
|
|
271
|
+
segPrefix: 'Today ', segMorningHint: ' (yesterday summary)',
|
|
272
|
+
welcomeBack: 'Welcome back! While you were away, you finished:',
|
|
273
|
+
yesterdayDrawer: 'Yesterday',
|
|
274
|
+
greetMorning: 'A fresh day! Let us look back at yesterday first.', greetForenoon: 'Good morning, steady progress today.', greetNoon: 'Good noon, take a break and keep going.', greetAfternoon: 'Good afternoon, keep up the good energy.', greetEvening: 'Good evening, well done today.',
|
|
275
|
+
greetSummary: 'You have completed ', greetThings: ' things today. Tap to expand:',
|
|
276
|
+
qUrgentImportant: 'Urgent & Important', qImportant: 'Important', qUrgent: 'Urgent', qNone: 'Neither', qUncategorized: 'Uncategorized',
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
var locale = 'zh'
|
|
280
|
+
var localeMode = 'system' // 'system'=跟随 DSH 系统语言 | 'zh' | 'en'
|
|
281
|
+
var sysLocale = 'zh' // DSH 系统语言(跟随模式使用)
|
|
282
|
+
function applyLocalePref(m) {
|
|
283
|
+
if (m !== 'zh' && m !== 'en' && m !== 'system') m = 'system'
|
|
284
|
+
localeMode = m
|
|
285
|
+
var target = m === 'system' ? sysLocale : m
|
|
286
|
+
if (locale !== target) { locale = target; localeListeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
287
|
+
}
|
|
288
|
+
var sessions = null
|
|
289
|
+
// 当前会话工作区(跟随 GUI 切换工作区):从 sessions 服务拿,供 state/summarize/greet 请求使用
|
|
290
|
+
function currentWs() {
|
|
291
|
+
try {
|
|
292
|
+
var snap = sessions && sessions.list && sessions.list.getSnapshot()
|
|
293
|
+
if (!snap) return ''
|
|
294
|
+
var id = snap.current
|
|
295
|
+
if (id === undefined || id === null) return ''
|
|
296
|
+
var s = snap.byId && snap.byId[id]
|
|
297
|
+
return (s && typeof s.cwd === 'string') ? s.cwd : ''
|
|
298
|
+
} catch (e) { return '' }
|
|
299
|
+
}
|
|
300
|
+
var fontScale = 'lg'
|
|
301
|
+
var FONT_SCALES = { sm: '小', md: '标准', lg: '大', xl: '特大' }
|
|
302
|
+
var FONT_SCALE_VALUES = { sm: '0.9', md: '1', lg: '1.15', xl: '1.3' }
|
|
303
|
+
var localeListeners = new Set()
|
|
304
|
+
function t(key) { return (I18N[locale] && I18N[locale][key]) || I18N.zh[key] || key }
|
|
305
|
+
function setLocale(l) { if (l !== 'zh' && l !== 'en') return; if (l === locale) return; locale = l; localeListeners.forEach(function (fn) { try { fn(l) } catch (e) {} }) }
|
|
306
|
+
function onLocale(fn) { localeListeners.add(fn); return function () { localeListeners.delete(fn) } }
|
|
307
|
+
// 暂离检测:距离上次关闭面板超过 1 小时视为暂离回来
|
|
308
|
+
function isAway() {
|
|
309
|
+
var lastSeen = 0
|
|
310
|
+
try { lastSeen = Number(localStorage.getItem('dsh-auto-memory.lastActive') || 0) } catch (e) {}
|
|
311
|
+
return lastSeen > 0 && (Date.now() - lastSeen) > 3600000
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
315
|
+
var CHANGELOG = {
|
|
316
|
+
'0.1.15': { zh: ['优化:记忆注入迁至运行时上下文快照(systemPrompt.context),system prompt 保持字节级稳定 → DeepSeek 前缀缓存全程命中,自动沉淀/跨天/切换模型都不再击穿缓存,进一步降低 token 消耗', '优化:动态记忆内容不变时不重复注入,会话上下文更精简'], en: ['Optimize: memory injection moved to runtime-context snapshot (systemPrompt.context), system prompt stays byte-stable → DeepSeek prefix cache hits throughout, auto-consolidation/day-crossing/model-switch no longer break the cache, lower token usage', 'Optimize: unchanged dynamic memory is not re-injected, leaner session context'] },
|
|
317
|
+
'0.1.14': { zh: ['新增:更新后自动弹出更新说明窗口,首次安装有功能指导窗口', '新增:暂离超过 1 小时回来,记忆窗口自动打开并显示 AI 总结', '优化:弹窗改为左下角毛玻璃小卡片,更透明更轻量'], en: ['New: update dialog shows what changed after an upgrade; first-run guide dialog for new installs', 'New: the memory panel auto-opens with an AI summary after returning from >1h away', 'Polish: dialogs are now compact frosted-glass cards in the bottom-left corner'] },
|
|
318
|
+
'0.1.13': { zh: ['修复 npm 安装后客户端面板无法加载(bundle 注册名拼写)', '修复秒级时间戳击穿 DeepSeek 前缀缓存(改日期级,大幅节省 token)', '修复记忆接续导入报错'], en: ['Fix client panel not loading after npm install (bundle registration id)', 'Fix second-level timestamp breaking DeepSeek prefix cache (date-level now, big token savings)', 'Fix memory import error (inherit external memory)'] },
|
|
319
|
+
'0.1.12': { zh: ['声明 @deepseek-ai/cordis 为 peerDependency(插件市场收录规范)'], en: ['Declare @deepseek-ai/cordis as peerDependency (plugin market checklist)'] },
|
|
320
|
+
'0.1.11': { zh: ['界面语言跟随 DSH 系统语言,实时切换', 'README 重构:宣传图/真实截图/快速安装', '修复设置页加载崩溃'], en: ['UI language follows the DSH system language', 'README overhaul: banner, real screenshots, quick install', 'Fix settings page crash'] },
|
|
321
|
+
'0.1.10': { zh: ['自动检查更新 + 设置页一键更新', '日界:凌晨的活儿归前一天(默认 7:30)', '记忆根目录系统文件夹选择器,换位置自动迁移', '每日写入预算,超限自动压缩旧内容', '30 天 AI 蒸馏'], en: ['Auto update check + one-click update in settings', 'Day boundary: late-night work logs to yesterday (default 07:30)', 'Native OS folder picker for memory root, auto-migration', 'Daily write budget with auto-compaction', '30-day AI distillation'] },
|
|
322
|
+
}
|
|
323
|
+
var dialogListeners = new Set()
|
|
324
|
+
var dialogState = null // {kind:'update', versions:[...]} | {kind:'first'}
|
|
325
|
+
function openDialog(d) { dialogState = d; dialogListeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
326
|
+
function closeDialog() { dialogState = null; dialogListeners.forEach(function (fn) { try { fn() } catch (e) {} }) }
|
|
327
|
+
function onDialog(fn) { dialogListeners.add(fn); return function () { dialogListeners.delete(fn) } }
|
|
328
|
+
function cmpVersion(a, b) {
|
|
329
|
+
var pa = String(a).split('.').map(Number), pb = String(b).split('.').map(Number)
|
|
330
|
+
for (var i = 0; i < 3; i++) {
|
|
331
|
+
var x = pa[i] || 0, y = pb[i] || 0
|
|
332
|
+
if (x > y) return 1
|
|
333
|
+
if (x < y) return -1
|
|
334
|
+
}
|
|
335
|
+
return 0
|
|
336
|
+
}
|
|
337
|
+
function changelogBetween(fromVer, toVer) {
|
|
338
|
+
var keys = Object.keys(CHANGELOG).filter(function (v) { return cmpVersion(v, fromVer) > 0 && cmpVersion(v, toVer) <= 0 })
|
|
339
|
+
keys.sort(function (a, b) { return cmpVersion(a, b) })
|
|
340
|
+
return keys.map(function (v) { return { version: v, items: CHANGELOG[v] } })
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ───────────────────────── API ─────────────────────────
|
|
344
|
+
var API = {
|
|
345
|
+
state: '/api/dsh-auto-memory/state',
|
|
346
|
+
list: '/api/dsh-auto-memory/list',
|
|
347
|
+
file: '/api/dsh-auto-memory/file',
|
|
348
|
+
recall: '/api/dsh-auto-memory/recall',
|
|
349
|
+
smartRecall: '/api/dsh-auto-memory/smart-recall',
|
|
350
|
+
workspaces: '/api/dsh-auto-memory/workspaces',
|
|
351
|
+
debug: '/api/dsh-auto-memory/debug',
|
|
352
|
+
browseDir: '/api/dsh-auto-memory/browse-dir',
|
|
353
|
+
pickDir: '/api/dsh-auto-memory/pick-dir',
|
|
354
|
+
updateCheck: '/api/dsh-auto-memory/update-check',
|
|
355
|
+
update: '/api/dsh-auto-memory/update',
|
|
356
|
+
config: '/api/dsh-auto-memory/config',
|
|
357
|
+
reflect: '/api/dsh-auto-memory/reflect',
|
|
358
|
+
reflectAuto: '/api/dsh-auto-memory/reflect-auto',
|
|
359
|
+
note: '/api/dsh-auto-memory/note',
|
|
360
|
+
external: '/api/dsh-auto-memory/external',
|
|
361
|
+
externalImport: '/api/dsh-auto-memory/external-import',
|
|
362
|
+
summarize: '/api/dsh-auto-memory/summarize',
|
|
363
|
+
greet: '/api/dsh-auto-memory/greet',
|
|
364
|
+
}
|
|
365
|
+
function query(params) {
|
|
366
|
+
var search = new URLSearchParams()
|
|
367
|
+
for (var key in params) if (params[key] !== undefined && params[key] !== '') search.set(key, String(params[key]))
|
|
368
|
+
var text = search.toString()
|
|
369
|
+
return text ? '?' + text : ''
|
|
370
|
+
}
|
|
371
|
+
async function apiGet(path, params) {
|
|
372
|
+
var res = await fetch(path + query(params))
|
|
373
|
+
var body = await res.json().catch(function () { return {} })
|
|
374
|
+
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
375
|
+
return body
|
|
376
|
+
}
|
|
377
|
+
async function apiPost(path, payload) {
|
|
378
|
+
var res = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
|
|
379
|
+
var body = await res.json().catch(function () { return {} })
|
|
380
|
+
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
381
|
+
return body
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ───────────────────────── 样式 ─────────────────────────
|
|
385
|
+
// 视觉:液态玻璃(毛玻璃)—— backdrop-filter + DSH 主题令牌(--dsw-alias-*),
|
|
386
|
+
// 跟随亮/暗主题与页面背景自适应;位置/尺寸由 JS 几何状态驱动(可拖动、可缩放)。
|
|
387
|
+
var CSS = [
|
|
388
|
+
'[data-dam-panel] { position: fixed; left: 16px; width: 440px; height: 560px; --dam-scale: 1;',
|
|
389
|
+
' max-width: calc(100vw - 32px); max-height: calc(100vh - 32px);',
|
|
390
|
+
' display: flex; flex-direction: column; overflow: hidden; z-index: 3000; pointer-events: auto;',
|
|
391
|
+
' border-radius: 16px; font: 13px/1.55 system-ui, "Segoe UI", sans-serif;',
|
|
392
|
+
' background: color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.86)) 58%, transparent);',
|
|
393
|
+
' -webkit-backdrop-filter: blur(28px) saturate(1.55); backdrop-filter: blur(28px) saturate(1.55);',
|
|
394
|
+
' border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.35)) 65%, transparent);',
|
|
395
|
+
' box-shadow: 0 24px 64px rgba(0,0,0,.22), 0 4px 16px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.22);',
|
|
396
|
+
' color: var(--dsw-alias-label-primary, #1f2328);',
|
|
397
|
+
' animation: dam-in .2s cubic-bezier(.2,.9,.3,1.15) both; transform-origin: left bottom; }',
|
|
398
|
+
'@keyframes dam-in { from { opacity: 0; transform: scale(.92) translateY(10px); } to { opacity: 1; transform: none; } }',
|
|
399
|
+
'@keyframes dam-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(.95) translateY(6px); } }',
|
|
400
|
+
'[data-dam-panel][data-closing="true"] { animation: dam-out .16s ease-in both; }',
|
|
401
|
+
'[data-dam-panel][data-scale="sm"] { --dam-scale: .9; }',
|
|
402
|
+
'[data-dam-panel][data-scale="lg"] { --dam-scale: 1.15; }',
|
|
403
|
+
'[data-dam-panel][data-scale="xl"] { --dam-scale: 1.3; }',
|
|
404
|
+
'[data-dam-panel]::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 64%; pointer-events: none;',
|
|
405
|
+
' background: linear-gradient(180deg, rgba(255,255,255,.13), rgba(255,255,255,0) 70%); border-radius: 16px 16px 0 0; }',
|
|
406
|
+
'[data-dam-panel][data-dragging="true"] { user-select: none; }',
|
|
407
|
+
'[data-dam-panel] header { display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: grab;',
|
|
408
|
+
' border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 55%, transparent); }',
|
|
409
|
+
'[data-dam-panel][data-dragging="true"] header { cursor: grabbing; }',
|
|
410
|
+
'[data-dam-panel] header strong { font-size: calc(14px * var(--dam-scale)); }',
|
|
411
|
+
'[data-dam-panel] header .dam-spacer { flex: 1; }',
|
|
412
|
+
'[data-dam-resize] { position: absolute; right: 0; bottom: 0; width: 22px; height: 22px; cursor: nwse-resize;',
|
|
413
|
+
' opacity: .55; z-index: 2; border-radius: 0 0 16px 0;',
|
|
414
|
+
' background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 45%, transparent) 50%); }',
|
|
415
|
+
'[data-dam-resize]:hover { opacity: 1; }',
|
|
416
|
+
'[data-dam-btn] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .75; font-size: calc(13px * var(--dam-scale)); padding: 4px 8px; border-radius: 6px; }',
|
|
417
|
+
'[data-dam-btn]:hover { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 16%, transparent); }',
|
|
418
|
+
'[data-dam-tabs] { display: flex; gap: 2px; padding: 6px 10px; border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.2)) 55%, transparent); }',
|
|
419
|
+
'[data-dam-tab] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .6; padding: 5px 10px; border-radius: 7px; font-size: calc(12.5px * var(--dam-scale)); }',
|
|
420
|
+
'[data-dam-tab][data-active="true"] { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 18%, transparent); font-weight: 600; }',
|
|
421
|
+
'[data-dam-body] { flex: 1; overflow: auto; padding: 12px 14px; }',
|
|
422
|
+
'[data-dam-kv] { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: calc(12.5px * var(--dam-scale)); }',
|
|
423
|
+
'[data-dam-kv] b { opacity: .55; font-weight: 500; }',
|
|
424
|
+
'[data-dam-card] { border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.22)) 60%, transparent); border-radius: 9px; padding: 8px 10px; margin-bottom: 8px; font-size: calc(12.5px * var(--dam-scale));',
|
|
425
|
+
' background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent); }',
|
|
426
|
+
'[data-dam-card] .dam-date { font-weight: 600; margin-bottom: 4px; }',
|
|
427
|
+
'[data-dam-card] .dam-content { white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }',
|
|
428
|
+
'[data-dam-banner] { border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 55%, transparent);',
|
|
429
|
+
' background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 13%, transparent); border-radius: 9px; padding: 8px 10px; margin-bottom: 10px; font-size: calc(12.5px * var(--dam-scale)); }',
|
|
430
|
+
'[data-dam-input], [data-dam-select] { width: 100%; box-sizing: border-box; background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.08)) 45%, transparent); color: inherit; border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 60%, transparent); border-radius: 7px; padding: 6px 8px; font: inherit; }',
|
|
431
|
+
'[data-dam-row] { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }',
|
|
432
|
+
'[data-dam-row] label { flex: 0 0 110px; opacity: .8; font-size: calc(12.5px * var(--dam-scale)); }',
|
|
433
|
+
'[data-dam-hint] { opacity: .5; font-size: calc(11.5px * var(--dam-scale)); margin-top: 2px; }',
|
|
434
|
+
'[data-dam-sidebar-btn] { display: flex; align-items: center; gap: 6px; width: 100%; border: none; background: transparent; color: inherit; cursor: pointer; padding: 6px 10px; border-radius: 8px; font: inherit; font-size: 13px; opacity: .8; }',
|
|
435
|
+
'[data-dam-sidebar-btn]:hover { background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 14%, transparent); opacity: 1; }',
|
|
436
|
+
'[data-dam-sidebar-btn][data-active="true"] { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 16%, transparent); color: var(--dsw-alias-brand-primary, #4f7cff); }',
|
|
437
|
+
'[data-dam-error] { color: var(--dsw-alias-state-error-primary, #d64545); font-size: 12px; margin-top: 6px; white-space: pre-wrap; }',
|
|
438
|
+
'[data-dam-muted] { opacity: .55; }',
|
|
439
|
+
'@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {',
|
|
440
|
+
' [data-dam-panel] { background: var(--dsw-alias-bg-overlay, #ffffff); } }',
|
|
441
|
+
].join('\n')
|
|
442
|
+
var STYLE_ID = 'dsh-auto-memory-css'
|
|
443
|
+
function ensureStyle() {
|
|
444
|
+
if (document.getElementById(STYLE_ID)) return
|
|
445
|
+
var tag = document.createElement('style')
|
|
446
|
+
tag.id = STYLE_ID
|
|
447
|
+
tag.dataset.plugin = '@a9i5k4/dsh-auto-memory'
|
|
448
|
+
tag.textContent = CSS
|
|
449
|
+
document.head.appendChild(tag)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ───────────────────────── 通用小组件 ─────────────────────────
|
|
453
|
+
function useTick() { return useReducer(function (x) { return x + 1 }, 0) }
|
|
454
|
+
|
|
455
|
+
function Banner(props) {
|
|
456
|
+
return h('div', { 'data-dam-banner': '' }, props.children)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function Card(props) {
|
|
460
|
+
return h('div', { 'data-dam-card': '' },
|
|
461
|
+
h('div', { className: 'dam-date' }, props.title),
|
|
462
|
+
h('div', { className: 'dam-content' }, props.children))
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function Loading() { return h('div', { 'data-dam-muted': '' }, t('loading')) }
|
|
466
|
+
|
|
467
|
+
// ───────────────────────── 侧边栏入口 ─────────────────────────
|
|
468
|
+
function SidebarButton() {
|
|
469
|
+
var tick = useTick()
|
|
470
|
+
useEffect(function () { return controller.subscribe(tick[1]) }, [])
|
|
471
|
+
return h('button', {
|
|
472
|
+
'data-dam-sidebar-btn': '',
|
|
473
|
+
title: t('memoryPanel'),
|
|
474
|
+
'data-active': (panelOpen || panelClosing) ? 'true' : undefined,
|
|
475
|
+
onClick: function () { controller.toggle() },
|
|
476
|
+
}, h('span', null, t('memory')))
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ───────────────────────── 记忆面板 ─────────────────────────
|
|
480
|
+
function fmtSize(n) {
|
|
481
|
+
if (n >= 1024 * 1024) return (n / 1024 / 1024).toFixed(1) + ' MB'
|
|
482
|
+
if (n >= 1024) return (n / 1024).toFixed(1) + ' KB'
|
|
483
|
+
return n + ' B'
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function GreetingCard(props) {
|
|
487
|
+
var g = props.greeting
|
|
488
|
+
var ps = props.periodSummary
|
|
489
|
+
if (!g) return null
|
|
490
|
+
var openPair = useState({})
|
|
491
|
+
var openMap = openPair[0]
|
|
492
|
+
var setOpenMap = openPair[1]
|
|
493
|
+
var subOpenPair = useState({})
|
|
494
|
+
var subOpenMap = subOpenPair[0]
|
|
495
|
+
var setSubOpenMap = subOpenPair[1]
|
|
496
|
+
// 智能时段判定
|
|
497
|
+
var hour = new Date().getHours()
|
|
498
|
+
var seg = hour < 9 ? 'morning' : hour < 12 ? 'forenoon' : hour < 14 ? 'noon' : hour < 18 ? 'afternoon' : 'evening'
|
|
499
|
+
var segLabel = { morning: t('segMorning'), forenoon: t('segForenoon'), noon: t('segNoon'), afternoon: t('segAfternoon'), evening: t('segEvening') }[seg]
|
|
500
|
+
// 收集抽屉: {key, label, hint, items, withTime}
|
|
501
|
+
var drawers = []
|
|
502
|
+
var curSegName = { morning: '早晨', forenoon: '上午', noon: '中午', afternoon: '下午', evening: '晚上' }[seg]
|
|
503
|
+
// 昨天抽屉(早晨显示)
|
|
504
|
+
if (seg === 'morning' && g.entries.length) {
|
|
505
|
+
drawers.push({ key: 'yesterday', label: t('yesterdayDrawer') + (g.yesterdayDate || ''), hint: '', items: g.entries, withTime: true, defaultOpen: true, period: '昨天' })
|
|
506
|
+
}
|
|
507
|
+
// 今天各时段抽屉(已过的时段)
|
|
508
|
+
var seenSeg = { '早晨': seg !== 'morning', '上午': (seg === 'forenoon' || seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '中午': (seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '下午': (seg === 'afternoon' || seg === 'evening'), '晚上': seg === 'evening' }
|
|
509
|
+
var segOrder = ['早晨', '上午', '中午', '下午', '晚上']
|
|
510
|
+
for (var si = 0; si < segOrder.length; si++) {
|
|
511
|
+
var sname = segOrder[si]
|
|
512
|
+
if (!seenSeg[sname]) continue
|
|
513
|
+
var items = (ps && ps.groups && ps.groups[sname]) || []
|
|
514
|
+
if (!items.length) continue
|
|
515
|
+
var segEn = { '早晨': 'morning', '上午': 'forenoon', '中午': 'noon', '下午': 'afternoon', '晚上': 'evening' }[sname] || sname
|
|
516
|
+
drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + t('seg' + segEn.charAt(0).toUpperCase() + segEn.slice(1)), hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName, period: sname })
|
|
517
|
+
}
|
|
518
|
+
// 外层:生活化问候
|
|
519
|
+
var totalCount = 0
|
|
520
|
+
for (var d0 = 0; d0 < drawers.length; d0++) totalCount += drawers[d0].items.length
|
|
521
|
+
var greetLine = g.greeting && String(g.greeting).trim()
|
|
522
|
+
? String(g.greeting).trim()
|
|
523
|
+
: (function () {
|
|
524
|
+
if (away()) return t('welcomeBack')
|
|
525
|
+
var base = { morning: t('greetMorning'), forenoon: t('greetForenoon'), noon: t('greetNoon'), afternoon: t('greetAfternoon'), evening: t('greetEvening') }[seg]
|
|
526
|
+
if (totalCount > 0) base += t('greetSummary') + totalCount + t('greetThings')
|
|
527
|
+
return base
|
|
528
|
+
})()
|
|
529
|
+
// 离开>1小时后回来
|
|
530
|
+
function away() { return isAway() }
|
|
531
|
+
var rows = []
|
|
532
|
+
rows.push(h('div', { 'data-dam-content': '' }, greetLine))
|
|
533
|
+
// 抽屉列表(AI 生活化总结缓存)
|
|
534
|
+
var sumCachePair = useState({})
|
|
535
|
+
var sumCache = sumCachePair[0]
|
|
536
|
+
var setSumCache = sumCachePair[1]
|
|
537
|
+
function requestSummary(key, period, force) {
|
|
538
|
+
if (!force && sumCache[key] !== undefined) return
|
|
539
|
+
apiPost(API.summarize, { period: period, force: !!force, ws: currentWs() }).then(function (d) {
|
|
540
|
+
if (d && (d.summary || (d.works && d.works.length))) {
|
|
541
|
+
var n = Object.assign({}, sumCache)
|
|
542
|
+
n[key] = { summary: d.summary || d.result || '', works: d.works || [], at: d.generatedAt || Date.now(), failed: false }
|
|
543
|
+
setSumCache(n)
|
|
544
|
+
}
|
|
545
|
+
}).catch(function () {
|
|
546
|
+
var n = Object.assign({}, sumCache)
|
|
547
|
+
n[key] = { summary: '', works: [], at: 0, failed: true }
|
|
548
|
+
setSumCache(n)
|
|
549
|
+
})
|
|
550
|
+
}
|
|
551
|
+
// 面板刷新键(nonce 变化):已缓存的展开抽屉 force 重新生成
|
|
552
|
+
useEffect(function () {
|
|
553
|
+
if (!props.nonce) return
|
|
554
|
+
for (var d = 0; d < drawers.length; d++) {
|
|
555
|
+
var dr = drawers[d]
|
|
556
|
+
var isOpen = openMap[dr.key] !== undefined ? openMap[dr.key] : dr.defaultOpen
|
|
557
|
+
if (isOpen && sumCache[dr.key] !== undefined) requestSummary(dr.key, dr.period, true)
|
|
558
|
+
}
|
|
559
|
+
}, [props.nonce])
|
|
560
|
+
for (var d = 0; d < drawers.length; d++) {
|
|
561
|
+
(function (dr) {
|
|
562
|
+
var isOpen = openMap[dr.key] !== undefined ? openMap[dr.key] : dr.defaultOpen
|
|
563
|
+
if (isOpen && sumCache[dr.key] === undefined) requestSummary(dr.key, dr.period, false)
|
|
564
|
+
var cached = sumCache[dr.key]
|
|
565
|
+
// 大抽屉标题:有 AI 总结时用总结内容(用户要求),否则用时段名
|
|
566
|
+
var bigTitle = cached && cached.summary ? cached.summary : dr.label
|
|
567
|
+
rows.push(h('div', { key: dr.key, style: { marginTop: '8px' } },
|
|
568
|
+
h('button', {
|
|
569
|
+
'data-dam-btn': '',
|
|
570
|
+
onClick: function () { var n = Object.assign({}, openMap); n[dr.key] = !isOpen; setOpenMap(n) },
|
|
571
|
+
style: { width: '100%', textAlign: 'left', padding: '7px 9px', borderRadius: '8px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.08)) 45%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 50%, transparent)' },
|
|
572
|
+
},
|
|
573
|
+
h('div', { style: { fontWeight: 600, fontSize: 'calc(12.5px * var(--dam-scale))', lineHeight: 1.5 } }, (isOpen ? '▼ ' : '▶ ') + bigTitle),
|
|
574
|
+
h('div', { 'data-dam-muted': '', style: { fontSize: 'calc(11px * var(--dam-scale))', marginTop: '2px' } },
|
|
575
|
+
dr.label + ' · ' + dr.items.length + t('logEntries') + (cached && cached.at ? ' · ' + t('generatedAt') + new Date(cached.at).toLocaleTimeString() : ''))),
|
|
576
|
+
isOpen ? h('div', { style: { padding: '4px 2px 2px 8px' } },
|
|
577
|
+
cached && cached.works && cached.works.length ? cached.works.map(function (w, wi) {
|
|
578
|
+
var sk = dr.key + ':w' + wi
|
|
579
|
+
var sOpen = subOpenMap[sk] !== undefined ? subOpenMap[sk] : false
|
|
580
|
+
return h('div', { key: sk, style: { marginTop: '5px' } },
|
|
581
|
+
h('button', {
|
|
582
|
+
'data-dam-btn': '',
|
|
583
|
+
onClick: function () { var n = Object.assign({}, subOpenMap); n[sk] = !sOpen; setSubOpenMap(n) },
|
|
584
|
+
style: { width: '100%', textAlign: 'left', padding: '4px 8px', borderRadius: '6px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent)', border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.22)) 45%, transparent)' },
|
|
585
|
+
},
|
|
586
|
+
h('span', { style: { fontWeight: 600, fontSize: 'calc(12px * var(--dam-scale))' } }, (sOpen ? '▾ ' : '▸ ') + w.title),
|
|
587
|
+
h('span', { 'data-dam-muted': '', style: { fontSize: 'calc(11px * var(--dam-scale))', marginLeft: '6px' } }, w.points.length + t('pointsCount'))),
|
|
588
|
+
sOpen ? h('div', { style: { padding: '4px 4px 2px 14px' } },
|
|
589
|
+
w.points.map(function (pt, pi) {
|
|
590
|
+
return h('div', { key: pi, style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.5, marginBottom: '2px' } }, '· ' + pt)
|
|
591
|
+
})) : null)
|
|
592
|
+
}) : cached && cached.failed ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summaryFailed'))
|
|
593
|
+
: cached && cached.summary ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, cached.summary)
|
|
594
|
+
: h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summarizing'))) : null))
|
|
595
|
+
})(drawers[d])
|
|
596
|
+
}
|
|
597
|
+
// 待反思提醒
|
|
598
|
+
if (g.pendingReflectionDate) {
|
|
599
|
+
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '8px' } }, t('pendingReflectionShort') + g.pendingReflectionDate))
|
|
600
|
+
}
|
|
601
|
+
return h(Card, { title: (g.period || '') + ' · ' + segLabel }, rows)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function OverviewTab(props) {
|
|
605
|
+
var statePair = useState(null)
|
|
606
|
+
var state = statePair[0]
|
|
607
|
+
var setState = statePair[1]
|
|
608
|
+
var detailPair = useState(false)
|
|
609
|
+
var showDetail = detailPair[0]
|
|
610
|
+
var setShowDetail = detailPair[1]
|
|
611
|
+
var reflectBusyPair = useState(false)
|
|
612
|
+
var reflectBusy = reflectBusyPair[0]
|
|
613
|
+
var setReflectBusy = reflectBusyPair[1]
|
|
614
|
+
var actMsgPair = useState('')
|
|
615
|
+
var actMsg = actMsgPair[0]
|
|
616
|
+
var setActMsg = actMsgPair[1]
|
|
617
|
+
var wsPair = useState(null)
|
|
618
|
+
var wsData = wsPair[0]
|
|
619
|
+
var setWsData = wsPair[1]
|
|
620
|
+
// 每次进入面板 / 点刷新(nonce 变化)/ 面板打开期间每 30s 自动重拉,保证自动沉淀等数据新鲜
|
|
621
|
+
useEffect(function () {
|
|
622
|
+
var alive = true
|
|
623
|
+
// 跨工作区总结(概览页集成:所有工作区的小总结+细分;读缓存)
|
|
624
|
+
apiPost(API.workspaces, { force: false }).then(function (d) { if (d && alive) setWsData(d) }).catch(function () {})
|
|
625
|
+
apiGet(API.state, { ws: currentWs() }).then(function (s) {
|
|
626
|
+
if (alive) setState(s)
|
|
627
|
+
// 无 AI 问候 → 自动生成一次(host 按时段缓存,不会重复生成)
|
|
628
|
+
if (s && s.greeting && !s.greeting.greeting) {
|
|
629
|
+
apiPost(API.greet, { ws: currentWs() }).then(function (d) {
|
|
630
|
+
if (d && d.greeting && alive) {
|
|
631
|
+
setState(function (prev) {
|
|
632
|
+
var g0 = (prev && prev.greeting) || {}
|
|
633
|
+
return Object.assign({}, prev, { greeting: Object.assign({}, g0, { greeting: d.greeting, hasGreeting: true }) })
|
|
634
|
+
})
|
|
635
|
+
}
|
|
636
|
+
}).catch(function () {})
|
|
637
|
+
}
|
|
638
|
+
}).catch(function () {})
|
|
639
|
+
return function () { alive = false }
|
|
640
|
+
}, [props && props.nonce])
|
|
641
|
+
useEffect(function () {
|
|
642
|
+
var timer = setInterval(function () {
|
|
643
|
+
apiGet(API.state, { ws: currentWs() }).then(function (s) { setState(s) }).catch(function () {})
|
|
644
|
+
}, 30000)
|
|
645
|
+
return function () { clearInterval(timer) }
|
|
646
|
+
}, [])
|
|
647
|
+
|
|
648
|
+
if (!state) return h(Loading)
|
|
649
|
+
function oneClickReflect() {
|
|
650
|
+
if (reflectBusy) return
|
|
651
|
+
setReflectBusy(true); setActMsg('')
|
|
652
|
+
apiPost(API.reflectAuto, {}).then(function (d) {
|
|
653
|
+
setActMsg(d.result || t('generated')); setReflectBusy(false)
|
|
654
|
+
apiGet(API.state, { ws: currentWs() }).then(function (s) { if (s) setState(s) }).catch(function () {})
|
|
655
|
+
}).catch(function (e) { setActMsg(t('failed') + e.message); setReflectBusy(false) })
|
|
656
|
+
}
|
|
657
|
+
return h('div', null,
|
|
658
|
+
// 今日问候卡:问候语 + 昨天时间轴 + 提醒(纯 GUI 渲染,不干扰对话流)
|
|
659
|
+
h(GreetingCard, { greeting: state.greeting, periodSummary: state.periodSummary, t: t, nonce: props && props.nonce }),
|
|
660
|
+
// 跨工作区总结(所有工作区的小总结 + 区内细分)
|
|
661
|
+
h('div', { style: { marginTop: '10px' } },
|
|
662
|
+
h('div', { style: { fontWeight: 700, fontSize: 'calc(12.5px * var(--dam-scale))', opacity: 0.75, marginBottom: '6px' } }, t('wsOverview')),
|
|
663
|
+
wsData && wsData.workspaces && wsData.workspaces.length
|
|
664
|
+
? wsData.workspaces.map(function (ws) {
|
|
665
|
+
return h('div', { key: ws.path, style: { border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.22)) 60%, transparent)', borderRadius: '8px', padding: '7px 9px', marginBottom: '6px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.05)) 40%, transparent)' } },
|
|
666
|
+
h('div', { style: { fontWeight: 600, fontSize: 'calc(12px * var(--dam-scale))' } }, ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '')),
|
|
667
|
+
ws.summary ? h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.5, marginTop: '3px' } }, ws.summary) : h('div', { 'data-dam-hint': '' }, t('wsNoSummary')),
|
|
668
|
+
(ws.items && ws.items.length) ? ws.items.map(function (it, i) {
|
|
669
|
+
return h('div', { key: i, style: { fontSize: 'calc(11.5px * var(--dam-scale))', marginTop: '2px', opacity: 0.75 } }, '· ' + it)
|
|
670
|
+
}) : null)
|
|
671
|
+
})
|
|
672
|
+
: h('div', { 'data-dam-hint': '' }, t('wsGenerating'))),
|
|
673
|
+
state.pendingReflection
|
|
674
|
+
? h(Banner, null, t('pendingReflection') + state.pendingReflection + t('pendingReflectionHint'))
|
|
675
|
+
: null,
|
|
676
|
+
// 状态行:今日工作 / 反思 / 笔记
|
|
677
|
+
h('div', { 'data-dam-kv': '' },
|
|
678
|
+
h('b', null, t('todayWork')), h('span', null, state.todayEntries + t('logEntries')),
|
|
679
|
+
h('b', null, t('dailyReflection')), h('span', null, state.latestReflectionDate || t('notYet')),
|
|
680
|
+
h('b', null, t('workspace')), h('span', null, state.ws)),
|
|
681
|
+
// 自动沉淀即时反馈(本轮/今日沉淀了多少条、最近一次时间)
|
|
682
|
+
state.autoStats && state.autoStats.count > 0
|
|
683
|
+
? h('div', { 'data-dam-hint': '', style: { marginTop: '2px' } },
|
|
684
|
+
t('autoSettledToday') + state.autoStats.count + t('autoSettledSuffix') +
|
|
685
|
+
(state.autoStats.lastAt ? ' (' + t('autoSettledRecent') + new Date(state.autoStats.lastAt).toLocaleTimeString() + ')' : ''))
|
|
686
|
+
: h('div', { 'data-dam-hint': '', style: { marginTop: '2px' } }, t('autoSettledNone')),
|
|
687
|
+
// 快捷操作
|
|
688
|
+
h('div', { 'data-dam-row': '' },
|
|
689
|
+
h('button', { 'data-dam-btn': '', onClick: oneClickReflect, disabled: reflectBusy }, reflectBusy ? t('reflecting') : t('oneClickReflect')),
|
|
690
|
+
h('span', { 'data-dam-hint': '' }, t('reflectHint'))),
|
|
691
|
+
actMsg ? h('div', { 'data-dam-hint': '' }, actMsg) : null,
|
|
692
|
+
h('div', { 'data-dam-hint': '' }, t('quickLinks')),
|
|
693
|
+
// 技术细节(折叠)
|
|
694
|
+
h('div', null,
|
|
695
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setShowDetail(!showDetail) } }, showDetail ? t('collapseTech') : t('expandTech'))),
|
|
696
|
+
showDetail ? h('div', { 'data-dam-kv': '' },
|
|
697
|
+
h('b', null, t('userMemory')), h('span', null, state.userFile + ' (' + fmtSize(state.sizes.user) + ')'),
|
|
698
|
+
h('b', null, t('projectNotes')), h('span', null, state.notesPath + ' (' + fmtSize(state.sizes.notes) + ')'),
|
|
699
|
+
h('b', null, t('todayLog')), h('span', null, state.logPath + ' (' + fmtSize(state.sizes.log) + ')'),
|
|
700
|
+
h('b', null, t('configFile')), h('span', null, state.configReadError ? (t('readFailed') + state.configReadError) : t('ok')),
|
|
701
|
+
h('b', null, t('refreshTime')), h('span', null, state.refreshedAt ? new Date(state.refreshedAt).toLocaleString() : t('notYetShort'))) : null)
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function LogsTab() {
|
|
705
|
+
var dataPair = useState(null)
|
|
706
|
+
var data = dataPair[0]
|
|
707
|
+
var setData = dataPair[1]
|
|
708
|
+
var openPair = useState(null)
|
|
709
|
+
var open = openPair[0]
|
|
710
|
+
var setOpen = openPair[1]
|
|
711
|
+
var contentPair = useState('')
|
|
712
|
+
var content = contentPair[0]
|
|
713
|
+
var setContent = contentPair[1]
|
|
714
|
+
useEffect(function () {
|
|
715
|
+
var alive = true
|
|
716
|
+
apiGet(API.list).then(function (d) { if (alive) setData(d) }).catch(function () {})
|
|
717
|
+
return function () { alive = false }
|
|
718
|
+
}, [])
|
|
719
|
+
useEffect(function () {
|
|
720
|
+
if (!open) return
|
|
721
|
+
var alive = true
|
|
722
|
+
apiGet(API.file, { path: open }).then(function (d) { if (alive) setContent(d.content) }).catch(function () {})
|
|
723
|
+
return function () { alive = false }
|
|
724
|
+
}, [open])
|
|
725
|
+
if (!data) return h(Loading)
|
|
726
|
+
var rows = []
|
|
727
|
+
if (open) {
|
|
728
|
+
rows.push(h(Card, { title: pathName(open) },
|
|
729
|
+
h('div', { 'data-dam-content': '' }, content || t('empty')),
|
|
730
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setOpen(null); setContent('') } }, t('back'))))
|
|
731
|
+
} else {
|
|
732
|
+
rows.push(h('div', { 'data-dam-hint': '' }, t('clickDateViewLog')))
|
|
733
|
+
for (var i = 0; i < data.logs.length; i++) {
|
|
734
|
+
(function (log) {
|
|
735
|
+
rows.push(h(Card, { title: log.date + ' · ' + fmtSize(log.size) },
|
|
736
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setOpen(log.date + '.md') } }, t('view'))))
|
|
737
|
+
})(data.logs[i])
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
return h('div', null, rows)
|
|
741
|
+
}
|
|
742
|
+
function pathName(p) { var parts = String(p).split(/[\\/]/); return parts[parts.length - 1] }
|
|
743
|
+
|
|
744
|
+
function NotesTab() {
|
|
745
|
+
var dataPair = useState(null)
|
|
746
|
+
var data = dataPair[0]
|
|
747
|
+
var setData = dataPair[1]
|
|
748
|
+
var draftPair = useState('')
|
|
749
|
+
var draft = draftPair[0]
|
|
750
|
+
var setDraft = draftPair[1]
|
|
751
|
+
var savingPair = useState(false)
|
|
752
|
+
var saving = savingPair[0]
|
|
753
|
+
var setSaving = savingPair[1]
|
|
754
|
+
var msgPair = useState('')
|
|
755
|
+
var msg = msgPair[0]
|
|
756
|
+
var setMsg = msgPair[1]
|
|
757
|
+
var errPair = useState('')
|
|
758
|
+
var err = errPair[0]
|
|
759
|
+
var setErr = errPair[1]
|
|
760
|
+
useEffect(function () {
|
|
761
|
+
var alive = true
|
|
762
|
+
apiGet(API.state).then(function (s) { if (alive) setData(s) }).catch(function () {})
|
|
763
|
+
return function () { alive = false }
|
|
764
|
+
}, [])
|
|
765
|
+
if (!data) return h(Loading)
|
|
766
|
+
function save() {
|
|
767
|
+
if (!draft.trim() || saving) return
|
|
768
|
+
setSaving(true); setMsg(''); setErr('')
|
|
769
|
+
apiPost(API.note, { content: draft.trim() }).then(function (d) {
|
|
770
|
+
setMsg(d.result || t('appended')); setDraft(''); setSaving(false)
|
|
771
|
+
}).catch(function (e) { setErr(e.message); setSaving(false) })
|
|
772
|
+
}
|
|
773
|
+
return h('div', null,
|
|
774
|
+
h('div', { 'data-dam-hint': '' }, t('notesPathLabel') + data.notesPath),
|
|
775
|
+
h('textarea', { 'data-dam-input': '', rows: 6, placeholder: t('notesPlaceholder'), value: draft, onChange: function (e) { setDraft(e.target.value) } }),
|
|
776
|
+
h('div', { 'data-dam-row': '' },
|
|
777
|
+
h('button', { 'data-dam-btn': '', onClick: save, disabled: saving }, saving ? t('saving') : t('append')),
|
|
778
|
+
h('span', { 'data-dam-hint': '' }, t('notesHint'))),
|
|
779
|
+
msg ? h('div', null, msg) : null,
|
|
780
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null)
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function ReflectionsTab() {
|
|
784
|
+
var dataPair = useState(null)
|
|
785
|
+
var data = dataPair[0]
|
|
786
|
+
var setData = dataPair[1]
|
|
787
|
+
var openPair = useState(null)
|
|
788
|
+
var open = openPair[0]
|
|
789
|
+
var setOpen = openPair[1]
|
|
790
|
+
var contentPair = useState('')
|
|
791
|
+
var content = contentPair[0]
|
|
792
|
+
var setContent = contentPair[1]
|
|
793
|
+
var busyPair = useState(false)
|
|
794
|
+
var busy = busyPair[0]
|
|
795
|
+
var setBusy = busyPair[1]
|
|
796
|
+
var msgPair = useState('')
|
|
797
|
+
var msg = msgPair[0]
|
|
798
|
+
var setMsg = msgPair[1]
|
|
799
|
+
useEffect(function () {
|
|
800
|
+
var alive = true
|
|
801
|
+
apiGet(API.list).then(function (d) { if (alive) setData(d) }).catch(function () {})
|
|
802
|
+
return function () { alive = false }
|
|
803
|
+
}, [])
|
|
804
|
+
useEffect(function () {
|
|
805
|
+
if (!open) return
|
|
806
|
+
var alive = true
|
|
807
|
+
apiGet(API.file, { path: open }).then(function (d) { if (alive) setContent(d.content) }).catch(function () {})
|
|
808
|
+
return function () { alive = false }
|
|
809
|
+
}, [open])
|
|
810
|
+
if (!data) return h(Loading)
|
|
811
|
+
function oneClickReflect() {
|
|
812
|
+
if (busy) return
|
|
813
|
+
setBusy(true); setMsg('')
|
|
814
|
+
apiPost(API.reflectAuto, {}).then(function (d) {
|
|
815
|
+
setMsg(d.result || t('generated')); setBusy(false)
|
|
816
|
+
apiGet(API.list).then(function (dd) { if (dd) setData(dd) }).catch(function () {})
|
|
817
|
+
}).catch(function (e) { setMsg(t('failed') + e.message); setBusy(false) })
|
|
818
|
+
}
|
|
819
|
+
var rows = []
|
|
820
|
+
if (open) {
|
|
821
|
+
rows.push(h(Card, { title: t('reflectionTitle') + pathName(open) }, h('div', { 'data-dam-content': '' }, content || t('empty'))))
|
|
822
|
+
rows.push(h('button', { 'data-dam-btn': '', onClick: function () { setOpen(null); setContent('') } }, t('back')))
|
|
823
|
+
} else {
|
|
824
|
+
rows.push(h('div', { 'data-dam-row': '' },
|
|
825
|
+
h('button', { 'data-dam-btn': '', onClick: oneClickReflect, disabled: busy }, busy ? t('generating') : t('oneClickReflect')),
|
|
826
|
+
h('span', { 'data-dam-hint': '' }, t('reflectAutoHint'))))
|
|
827
|
+
if (msg) rows.push(h('div', null, msg))
|
|
828
|
+
if (!data.reflections.length) rows.push(h('div', { 'data-dam-muted': '' }, t('noReflection')))
|
|
829
|
+
for (var i = 0; i < data.reflections.length; i++) {
|
|
830
|
+
(function (r) {
|
|
831
|
+
rows.push(h(Card, { title: r.date + ' · ' + fmtSize(r.size) },
|
|
832
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setOpen('reflections/' + r.name) } }, t('view'))))
|
|
833
|
+
})(data.reflections[i])
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return h('div', null, rows)
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function SearchTab() {
|
|
840
|
+
var qPair = useState('')
|
|
841
|
+
var q = qPair[0]
|
|
842
|
+
var setQ = qPair[1]
|
|
843
|
+
var resultPair = useState(null)
|
|
844
|
+
var result = resultPair[0]
|
|
845
|
+
var setResult = resultPair[1]
|
|
846
|
+
var smartPair = useState(null)
|
|
847
|
+
var smart = smartPair[0]
|
|
848
|
+
var setSmart = smartPair[1]
|
|
849
|
+
var busyPair = useState(false)
|
|
850
|
+
var busy = busyPair[0]
|
|
851
|
+
var setBusy = busyPair[1]
|
|
852
|
+
function search() {
|
|
853
|
+
if (!q.trim() || busy) return
|
|
854
|
+
setBusy(true); setResult(null); setSmart(null)
|
|
855
|
+
apiPost(API.recall, { query: q.trim() }).then(function (d) { setResult(d.result); setBusy(false) })
|
|
856
|
+
.catch(function (e) { setResult(t('searchFailed') + e.message); setBusy(false) })
|
|
857
|
+
}
|
|
858
|
+
function smartSearch() {
|
|
859
|
+
if (!q.trim() || busy) return
|
|
860
|
+
setBusy(true); setResult(null); setSmart(null)
|
|
861
|
+
apiPost(API.smartRecall, { query: q.trim() }).then(function (d) { setSmart(d); setBusy(false) })
|
|
862
|
+
.catch(function (e) { setSmart({ answer: t('searchFailed') + e.message, keywords: [], hits: [] }); setBusy(false) })
|
|
863
|
+
}
|
|
864
|
+
return h('div', null,
|
|
865
|
+
h('div', { 'data-dam-row': '' },
|
|
866
|
+
h('input', { 'data-dam-input': '', placeholder: t('searchPlaceholder'), value: q, onChange: function (e) { setQ(e.target.value) }, onKeyDown: function (e) { if (e.key === 'Enter') search() } }),
|
|
867
|
+
h('button', { 'data-dam-btn': '', onClick: search, disabled: busy }, t('searchBtn')),
|
|
868
|
+
h('button', { 'data-dam-btn': '', onClick: smartSearch, disabled: busy }, t('smartSearch'))),
|
|
869
|
+
smart ? h(Card, { title: t('smartAnswer') },
|
|
870
|
+
h('div', { 'data-dam-content': '' }, smart.answer || t('empty')),
|
|
871
|
+
smart.keywords && smart.keywords.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } }, t('keywordsLabel') + smart.keywords.join(' / ')) : null,
|
|
872
|
+
smart.hits && smart.hits.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } },
|
|
873
|
+
smart.hits.map(function (h, i) { return h('div', { key: i, style: { marginTop: '3px' } }, '· [' + h.where + '] ' + h.line) })) : null)
|
|
874
|
+
: null,
|
|
875
|
+
result ? h(Card, { title: t('resultTitle') }, h('div', { 'data-dam-content': '' }, result)) : null)
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// ───────────────────────── 工作区总览页签(跨工作区全局总结) ─────────────────────────
|
|
879
|
+
function WorkspaceTab() {
|
|
880
|
+
var dataPair = useState(null)
|
|
881
|
+
var data = dataPair[0]
|
|
882
|
+
var setData = dataPair[1]
|
|
883
|
+
var busyPair = useState(false)
|
|
884
|
+
var busy = busyPair[0]
|
|
885
|
+
var setBusy = busyPair[1]
|
|
886
|
+
function load(force) {
|
|
887
|
+
if (busy) return
|
|
888
|
+
setBusy(true)
|
|
889
|
+
apiPost(API.workspaces, { force: !!force }).then(function (d) { if (d) setData(d); setBusy(false) })
|
|
890
|
+
.catch(function () { setBusy(false) })
|
|
891
|
+
}
|
|
892
|
+
useEffect(function () { load(false) }, [])
|
|
893
|
+
if (!data) return h('div', null, busy ? h('div', { 'data-dam-hint': '' }, t('wsGenerating')) : h(Loading))
|
|
894
|
+
return h('div', null,
|
|
895
|
+
h('div', { 'data-dam-row': '' },
|
|
896
|
+
h('button', { 'data-dam-btn': '', onClick: function () { load(true) }, disabled: busy }, busy ? t('refreshing') : t('refresh')),
|
|
897
|
+
h('span', { 'data-dam-hint': '' }, data.cached ? (t('generatedAt') + new Date(data.generatedAt).toLocaleTimeString()) : '')),
|
|
898
|
+
(data.workspaces && data.workspaces.length) ? data.workspaces.map(function (ws) {
|
|
899
|
+
return h(Card, { key: ws.path, title: ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '') },
|
|
900
|
+
ws.summary ? h('div', { 'data-dam-content': '' }, ws.summary) : h('div', { 'data-dam-hint': '' }, t('wsNoSummary')),
|
|
901
|
+
(ws.items && ws.items.length) ? h('div', { style: { marginTop: '6px' } },
|
|
902
|
+
ws.items.map(function (it, i) {
|
|
903
|
+
return h('div', { key: i, style: { fontSize: 'calc(12px * var(--dam-scale))', marginTop: '3px', lineHeight: 1.5 } }, '· ' + it)
|
|
904
|
+
})) : null,
|
|
905
|
+
h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } }, ws.path + ' · ' + (ws.logCount || 0) + t('logEntries')))
|
|
906
|
+
}) : h('div', { 'data-dam-hint': '' }, t('wsNone')))
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// ───────────────────────── 日历页签 ─────────────────────────
|
|
910
|
+
var QUADRANT_I18N = { '重要紧急': 'qUrgentImportant', '重要不紧急': 'qImportant', '紧急不重要': 'qUrgent', '不重要不紧急': 'qNone', '未分类': 'qUncategorized' }
|
|
911
|
+
var QUADRANT_STYLE = {
|
|
912
|
+
'重要紧急': { color: 'var(--dsw-alias-state-error-primary, #d64545)' },
|
|
913
|
+
'重要不紧急': { color: 'var(--dsw-alias-brand-primary, #4f7cff)' },
|
|
914
|
+
'紧急不重要': { color: 'var(--dsw-alias-state-warn-primary, #e6a23c)' },
|
|
915
|
+
'不重要不紧急': { color: 'var(--dsw-alias-label-secondary, #8a94a6)' },
|
|
916
|
+
'未分类': { color: 'var(--dsw-alias-label-secondary, #8a94a6)' },
|
|
917
|
+
}
|
|
918
|
+
function CalendarTab() {
|
|
919
|
+
var dataPair = useState(null)
|
|
920
|
+
var data = dataPair[0]
|
|
921
|
+
var setData = dataPair[1]
|
|
922
|
+
var monthPair = useState(null)
|
|
923
|
+
var month = monthPair[0] // {year, mon} 1-12
|
|
924
|
+
var setMonth = monthPair[1]
|
|
925
|
+
var draftPair = useState(null)
|
|
926
|
+
var draft = draftPair[0] // {date, time, quadrant, title}
|
|
927
|
+
var setDraft = draftPair[1]
|
|
928
|
+
var msgPair = useState('')
|
|
929
|
+
var msg = msgPair[0]
|
|
930
|
+
var setMsg = msgPair[1]
|
|
931
|
+
function load() {
|
|
932
|
+
apiGet(API.calendar).then(function (d) { if (d) setData(d) }).catch(function () {})
|
|
933
|
+
}
|
|
934
|
+
useEffect(function () {
|
|
935
|
+
load()
|
|
936
|
+
var now = new Date()
|
|
937
|
+
setMonth({ year: now.getFullYear(), mon: now.getMonth() + 1 })
|
|
938
|
+
}, [])
|
|
939
|
+
if (!data || !month) return h(Loading)
|
|
940
|
+
function dayEntries(date) {
|
|
941
|
+
return (data.entries || []).filter(function (en) { return en.date === date })
|
|
942
|
+
}
|
|
943
|
+
function moveMonth(delta) {
|
|
944
|
+
var y = month.year, m = month.mon + delta
|
|
945
|
+
if (m < 1) { m = 12; y-- }
|
|
946
|
+
if (m > 12) { m = 1; y++ }
|
|
947
|
+
setMonth({ year: y, mon: m })
|
|
948
|
+
}
|
|
949
|
+
function fmtDate(y, m, d) { return y + '-' + String(m).padStart(2, '0') + '-' + String(d).padStart(2, '0') }
|
|
950
|
+
// 月网格
|
|
951
|
+
var firstDay = new Date(month.year, month.mon - 1, 1)
|
|
952
|
+
var startDow = firstDay.getDay()
|
|
953
|
+
var daysInMonth = new Date(month.year, month.mon, 0).getDate()
|
|
954
|
+
var cells = []
|
|
955
|
+
var today = fmtDate(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate())
|
|
956
|
+
for (var i = 0; i < startDow; i++) cells.push(null)
|
|
957
|
+
for (var d = 1; d <= daysInMonth; d++) cells.push(fmtDate(month.year, month.mon, d))
|
|
958
|
+
var dowLabels = locale === 'zh' ? ['日', '一', '二', '三', '四', '五', '六'] : ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']
|
|
959
|
+
var rows = []
|
|
960
|
+
// 顶部:月份切换 + 添加按钮
|
|
961
|
+
rows.push(h('div', { 'data-dam-row': '' },
|
|
962
|
+
h('button', { 'data-dam-btn': '', onClick: function () { moveMonth(-1) } }, '◀'),
|
|
963
|
+
h('span', { style: { fontWeight: 700, flex: 1, textAlign: 'center' } }, month.year + ' / ' + String(month.mon).padStart(2, '0')),
|
|
964
|
+
h('button', { 'data-dam-btn': '', onClick: function () { moveMonth(1) } }, '▶'),
|
|
965
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setDraft({ date: today, time: '09:00', quadrant: '重要不紧急', title: '' }) } }, '+ ' + t('addItem'))))
|
|
966
|
+
// 图例
|
|
967
|
+
rows.push(h('div', { 'data-dam-row': '', style: { flexWrap: 'wrap' } },
|
|
968
|
+
Object.keys(QUADRANT_STYLE).map(function (q) {
|
|
969
|
+
return h('span', { key: q, style: { fontSize: 'calc(11px * var(--dam-scale))', opacity: 0.8, marginRight: '10px' } },
|
|
970
|
+
h('span', { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: QUADRANT_STYLE[q].color, marginRight: 4 } }), t(QUADRANT_I18N[q]))
|
|
971
|
+
})))
|
|
972
|
+
// 星期表头
|
|
973
|
+
rows.push(h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '3px', marginBottom: '4px' } },
|
|
974
|
+
dowLabels.map(function (dl) { return h('div', { key: dl, style: { textAlign: 'center', fontSize: 'calc(11px * var(--dam-scale))', opacity: 0.55 } }, dl) })))
|
|
975
|
+
// 日历格子
|
|
976
|
+
var gridRows = []
|
|
977
|
+
for (var ci = 0; ci < cells.length; ci += 7) {
|
|
978
|
+
var week = []
|
|
979
|
+
for (var cj = 0; cj < 7; cj++) {
|
|
980
|
+
(function (cell) {
|
|
981
|
+
if (!cell) { week.push(h('div', { key: 'empty' + cj })); return }
|
|
982
|
+
var es = dayEntries(cell)
|
|
983
|
+
var isToday = cell === today
|
|
984
|
+
var dayNum = Number(cell.slice(8))
|
|
985
|
+
week.push(h('div', {
|
|
986
|
+
key: cell,
|
|
987
|
+
onClick: function () { setDraft({ date: cell, time: '09:00', quadrant: '重要不紧急', title: '' }) },
|
|
988
|
+
style: {
|
|
989
|
+
minHeight: '64px', padding: '4px', cursor: 'pointer', borderRadius: '8px',
|
|
990
|
+
border: '1px solid ' + (isToday ? 'var(--dsw-alias-brand-primary, #4f7cff)' : 'color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 55%, transparent)'),
|
|
991
|
+
background: isToday ? 'color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 10%, transparent)' : 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent)',
|
|
992
|
+
overflow: 'hidden',
|
|
993
|
+
},
|
|
994
|
+
},
|
|
995
|
+
h('div', { style: { fontSize: 'calc(11.5px * var(--dam-scale))', fontWeight: isToday ? 700 : 500, opacity: isToday ? 1 : 0.7, marginBottom: '3px' } }, dayNum),
|
|
996
|
+
es.slice(0, 3).map(function (en) {
|
|
997
|
+
var qs = QUADRANT_STYLE[en.quadrant] || QUADRANT_STYLE['未分类']
|
|
998
|
+
return h('div', {
|
|
999
|
+
key: en.time + en.title,
|
|
1000
|
+
title: en.time + ' ' + en.title,
|
|
1001
|
+
onClick: function (ev) { ev.stopPropagation(); toggleDone(en) },
|
|
1002
|
+
style: {
|
|
1003
|
+
fontSize: 'calc(10.5px * var(--dam-scale))', lineHeight: 1.35, padding: '1px 4px', borderRadius: 4, marginBottom: 2,
|
|
1004
|
+
background: 'color-mix(in srgb, ' + qs.color + ' 18%, transparent)',
|
|
1005
|
+
color: qs.color, textDecoration: en.done ? 'line-through' : 'none',
|
|
1006
|
+
opacity: en.done ? 0.5 : 1, cursor: 'pointer', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
|
|
1007
|
+
},
|
|
1008
|
+
}, (en.time && en.time !== '--:--' ? en.time + ' ' : '') + en.title)
|
|
1009
|
+
}),
|
|
1010
|
+
es.length > 3 ? h('div', { style: { fontSize: 'calc(10px * var(--dam-scale))', opacity: 0.5 } }, '+' + (es.length - 3)) : null))
|
|
1011
|
+
})(cells[ci + cj])
|
|
1012
|
+
}
|
|
1013
|
+
gridRows.push(h('div', { key: 'w' + ci, style: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '3px' } }, week))
|
|
1014
|
+
}
|
|
1015
|
+
rows.push(h('div', null, gridRows))
|
|
1016
|
+
function toggleDone(en) {
|
|
1017
|
+
apiPost(API.calendar, { action: en.done ? 'remove' : 'done', date: en.date, time: en.time, title: en.title }).then(function (d) {
|
|
1018
|
+
setMsg(d.result || ''); load()
|
|
1019
|
+
}).catch(function (e) { setMsg(t('failed') + e.message) })
|
|
1020
|
+
}
|
|
1021
|
+
function saveDraft() {
|
|
1022
|
+
if (!draft || !draft.title.trim()) { setMsg(t('needTitle')); return }
|
|
1023
|
+
apiPost(API.calendar, { date: draft.date, time: draft.time, quadrant: draft.quadrant, title: draft.title.trim(), note: draft.note || '' }).then(function (d) {
|
|
1024
|
+
setMsg(d.result || t('saved')); setDraft(null); load()
|
|
1025
|
+
}).catch(function (e) { setMsg(t('failed') + e.message) })
|
|
1026
|
+
}
|
|
1027
|
+
// 添加/编辑浮层(液态玻璃)
|
|
1028
|
+
if (draft) {
|
|
1029
|
+
rows.push(h('div', {
|
|
1030
|
+
style: {
|
|
1031
|
+
position: 'absolute', inset: 0, zIndex: 10, borderRadius: '16px',
|
|
1032
|
+
background: 'color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.9)) 70%, transparent)',
|
|
1033
|
+
backdropFilter: 'blur(20px) saturate(1.4)', WebkitBackdropFilter: 'blur(20px) saturate(1.4)',
|
|
1034
|
+
display: 'flex', flexDirection: 'column', padding: '18px', gap: '10px',
|
|
1035
|
+
border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.35)) 60%, transparent)',
|
|
1036
|
+
},
|
|
1037
|
+
},
|
|
1038
|
+
h('div', { style: { fontWeight: 700 } }, t('addItem') + ' · ' + draft.date),
|
|
1039
|
+
h('input', { 'data-dam-input': '', placeholder: t('itemTitle'), value: draft.title, autoFocus: true, onChange: function (e) { var n = Object.assign({}, draft); n.title = e.target.value; setDraft(n) } }),
|
|
1040
|
+
h('div', { 'data-dam-row': '' },
|
|
1041
|
+
h('input', { 'data-dam-input': '', type: 'time', value: draft.time, onChange: function (e) { var n = Object.assign({}, draft); n.time = e.target.value || '09:00'; setDraft(n) } }),
|
|
1042
|
+
h('select', { 'data-dam-select': '', value: draft.quadrant, onChange: function (e) { var n = Object.assign({}, draft); n.quadrant = e.target.value; setDraft(n) } },
|
|
1043
|
+
['重要紧急', '重要不紧急', '紧急不重要', '不重要不紧急'].map(function (q) { return h('option', { key: q, value: q }, t(QUADRANT_I18N[q])) }))),
|
|
1044
|
+
h('div', { 'data-dam-row': '' },
|
|
1045
|
+
h('button', { 'data-dam-btn': '', onClick: saveDraft }, t('save')),
|
|
1046
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setDraft(null) } }, t('cancel')))))
|
|
1047
|
+
}
|
|
1048
|
+
msg ? h('div', { 'data-dam-hint': '' }, msg) : null
|
|
1049
|
+
return h('div', { style: { position: 'relative' } }, rows)
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
var TOOL_LABEL = { workbuddy: t('aiAssistant'), codebuddy: 'CodeBuddy', claude: 'Claude Code', codex: 'Codex', 'project-files': t('projectFiles') }
|
|
1053
|
+
function ConnectTab() {
|
|
1054
|
+
var sourcesPair = useState(null)
|
|
1055
|
+
var sources = sourcesPair[0]
|
|
1056
|
+
var setSources = sourcesPair[1]
|
|
1057
|
+
var busyPair = useState({})
|
|
1058
|
+
var busy = busyPair[0]
|
|
1059
|
+
var setBusy = busyPair[1]
|
|
1060
|
+
var msgPair = useState('')
|
|
1061
|
+
var msg = msgPair[0]
|
|
1062
|
+
var setMsg = msgPair[1]
|
|
1063
|
+
var errPair = useState('')
|
|
1064
|
+
var err = errPair[0]
|
|
1065
|
+
var setErr = errPair[1]
|
|
1066
|
+
function load() {
|
|
1067
|
+
apiGet(API.external).then(function (d) { setSources(d.sources || []) }).catch(function (e) { setErr(e.message) })
|
|
1068
|
+
}
|
|
1069
|
+
useEffect(function () { load(); var t = setInterval(load, 60000); return function () { clearInterval(t) } }, [])
|
|
1070
|
+
if (!sources) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
1071
|
+
function doImport(source, target) {
|
|
1072
|
+
var next = Object.assign({}, busy); next[source] = true
|
|
1073
|
+
setBusy(next); setMsg(''); setErr('')
|
|
1074
|
+
apiPost(API.externalImport, { source: source, target: target }).then(function (d) {
|
|
1075
|
+
var done = Object.assign({}, busy); done[source] = false
|
|
1076
|
+
setBusy(done); setMsg(d.result || t('imported'))
|
|
1077
|
+
}).catch(function (e) { var done = Object.assign({}, busy); done[source] = false; setBusy(done); setErr(e.message) })
|
|
1078
|
+
}
|
|
1079
|
+
function importAll() {
|
|
1080
|
+
var md = sources.filter(function (s) { return s.kind !== 'sessions' && s.enabled !== false })
|
|
1081
|
+
setMsg(t('importing') + md.length + t('importingSuffix')); setErr('')
|
|
1082
|
+
var chain = Promise.resolve()
|
|
1083
|
+
md.forEach(function (s) { chain = chain.then(function () { return apiPost(API.externalImport, { source: s.id, target: 'project' }) }) })
|
|
1084
|
+
chain.then(function () { setMsg(t('allImported')) }).catch(function (e) { setErr(e.message) })
|
|
1085
|
+
}
|
|
1086
|
+
var cards = []
|
|
1087
|
+
if (!sources.length) {
|
|
1088
|
+
cards.push(h('div', { 'data-dam-muted': '' }, t('noExternal')))
|
|
1089
|
+
}
|
|
1090
|
+
for (var i = 0; i < sources.length; i++) {
|
|
1091
|
+
(function (s) {
|
|
1092
|
+
var actions = []
|
|
1093
|
+
if (s.kind === 'sessions') {
|
|
1094
|
+
actions.push(h('span', { 'data-dam-hint': '' }, t('sessionSource') + s.fileCount + t('sessionSourceSuffix')))
|
|
1095
|
+
} else {
|
|
1096
|
+
actions.push(h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'project') } }, busy[s.id] ? t('importingOne') : t('importToNotes')))
|
|
1097
|
+
actions.push(h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'user') } }, t('importToUser')))
|
|
1098
|
+
}
|
|
1099
|
+
cards.push(h(Card, { title: s.name + ' · ' + s.tool + (s.enabled === false ? t('disabled') : '') },
|
|
1100
|
+
h('div', { 'data-dam-hint': '' }, s.kind + ' · ' + s.fileCount + t('filesCount') + fmtSize(s.size)),
|
|
1101
|
+
s.preview ? h('div', { 'data-dam-content': '' }, s.preview) : null,
|
|
1102
|
+
h('div', { 'data-dam-row': '' }, actions)))
|
|
1103
|
+
})(sources[i])
|
|
1104
|
+
}
|
|
1105
|
+
return h('div', null,
|
|
1106
|
+
h('div', { 'data-dam-hint': '' }, t('connectHint')),
|
|
1107
|
+
h('div', { 'data-dam-row': '' },
|
|
1108
|
+
h('button', { 'data-dam-btn': '', onClick: importAll }, t('importAll')),
|
|
1109
|
+
h('button', { 'data-dam-btn': '', onClick: load }, t('rescan'))),
|
|
1110
|
+
msg ? h('div', null, msg) : null,
|
|
1111
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null,
|
|
1112
|
+
cards)
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// ───────────────────────── 拖动/缩放辅助 ─────────────────────────
|
|
1116
|
+
var dragActive = false
|
|
1117
|
+
function startPointerDrag(onMove, interactiveSel) {
|
|
1118
|
+
return function (e) {
|
|
1119
|
+
// 命中交互控件(按钮/输入框等)时不启动拖动
|
|
1120
|
+
if (interactiveSel && e.target && e.target.closest && e.target.closest(interactiveSel)) return
|
|
1121
|
+
e.preventDefault()
|
|
1122
|
+
e.stopPropagation()
|
|
1123
|
+
var startX = e.clientX
|
|
1124
|
+
var startY = e.clientY
|
|
1125
|
+
var moved = false
|
|
1126
|
+
function move(ev) {
|
|
1127
|
+
var dx = ev.clientX - startX
|
|
1128
|
+
var dy = ev.clientY - startY
|
|
1129
|
+
if (!moved && Math.abs(dx) < 2 && Math.abs(dy) < 2) return
|
|
1130
|
+
moved = true
|
|
1131
|
+
if (!dragActive) { dragActive = true; emit() }
|
|
1132
|
+
onMove(dx, dy)
|
|
1133
|
+
}
|
|
1134
|
+
function up() {
|
|
1135
|
+
window.removeEventListener('pointermove', move)
|
|
1136
|
+
window.removeEventListener('pointerup', up)
|
|
1137
|
+
if (dragActive) { dragActive = false; emit() }
|
|
1138
|
+
controller.flushGeom()
|
|
1139
|
+
}
|
|
1140
|
+
window.addEventListener('pointermove', move)
|
|
1141
|
+
window.addEventListener('pointerup', up)
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
function MemoryPanel() {
|
|
1146
|
+
var tick = useTick()
|
|
1147
|
+
var tabPair = useState('overview')
|
|
1148
|
+
var tab = tabPair[0]
|
|
1149
|
+
var setTab = tabPair[1]
|
|
1150
|
+
// 刷新 nonce:每次打开面板 / 点 ⟳ 时递增,驱动各页签重拉数据
|
|
1151
|
+
var noncePair = useState(0)
|
|
1152
|
+
var nonce = noncePair[0]
|
|
1153
|
+
var setNonce = noncePair[1]
|
|
1154
|
+
var g = controller.geom()
|
|
1155
|
+
useEffect(function () { return controller.subscribe(tick[1]) }, [])
|
|
1156
|
+
if (!panelOpen && !panelClosing) return null
|
|
1157
|
+
var body
|
|
1158
|
+
if (tab === 'overview') body = h(OverviewTab, { nonce: nonce })
|
|
1159
|
+
else if (tab === 'logs') body = h(LogsTab)
|
|
1160
|
+
else if (tab === 'notes') body = h(NotesTab)
|
|
1161
|
+
else if (tab === 'reflections') body = h(ReflectionsTab)
|
|
1162
|
+
else if (tab === 'connect') body = h(ConnectTab)
|
|
1163
|
+
else if (tab === 'calendar') body = h(CalendarTab)
|
|
1164
|
+
else if (tab === 'workspaces') body = h(WorkspaceTab)
|
|
1165
|
+
else body = h(SearchTab)
|
|
1166
|
+
var tabs = [['overview', t('overview')], ['logs', t('logs')], ['notes', t('notes')], ['reflections', t('reflections')], ['connect', t('connect')], ['calendar', t('calendar')], ['search', t('search')], ['workspaces', t('workspaces')]]
|
|
1167
|
+
var style = {
|
|
1168
|
+
left: g.left + 'px',
|
|
1169
|
+
top: g.top + 'px',
|
|
1170
|
+
width: g.width + 'px',
|
|
1171
|
+
height: g.height + 'px',
|
|
1172
|
+
'--dam-scale': FONT_SCALE_VALUES[fontScale] || '1',
|
|
1173
|
+
}
|
|
1174
|
+
var dragMove = startPointerDrag(function (dx, dy) {
|
|
1175
|
+
controller.setGeom({ left: g.left + dx, top: g.top + dy })
|
|
1176
|
+
}, '[data-dam-btn], [data-dam-tab], [data-dam-input], [data-dam-select], textarea')
|
|
1177
|
+
var resizeMove = startPointerDrag(function (dx, dy) {
|
|
1178
|
+
controller.setGeom({ width: g.width + dx, height: g.height + dy })
|
|
1179
|
+
})
|
|
1180
|
+
return h('div', {
|
|
1181
|
+
'data-dam-panel': '',
|
|
1182
|
+
'data-scale': fontScale in FONT_SCALES ? fontScale : 'md',
|
|
1183
|
+
style: style,
|
|
1184
|
+
'data-closing': panelClosing ? 'true' : undefined,
|
|
1185
|
+
'data-dragging': dragActive ? 'true' : undefined,
|
|
1186
|
+
},
|
|
1187
|
+
h('header', {
|
|
1188
|
+
title: t('dragMove'),
|
|
1189
|
+
onPointerDown: dragMove,
|
|
1190
|
+
},
|
|
1191
|
+
h('strong', null, t('autoMemory')),
|
|
1192
|
+
h('span', { className: 'dam-spacer' }),
|
|
1193
|
+
h('button', { 'data-dam-btn': '', title: t('resetPos'), onClick: function () { controller.resetGeom() } }, '⤾'),
|
|
1194
|
+
h('button', { 'data-dam-btn': '', title: t('refresh'), onClick: function () { setNonce(nonce + 1) } }, '⟳'),
|
|
1195
|
+
h('button', { 'data-dam-btn': '', title: t('close'), onClick: function () { controller.close() } }, '✕')),
|
|
1196
|
+
h('div', { 'data-dam-tabs': '' }, tabs.map(function (t) {
|
|
1197
|
+
return h('button', { key: t[0], 'data-dam-tab': '', 'data-active': tab === t[0] ? 'true' : undefined, onClick: function () { setTab(t[0]) } }, t[1])
|
|
1198
|
+
})),
|
|
1199
|
+
h('div', { 'data-dam-body': '' }, body),
|
|
1200
|
+
h('div', { 'data-dam-resize': '', title: t('dragResize'), onPointerDown: resizeMove }))
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// ───────────────────────── 调试中心(为提 issue 提供诊断) ─────────────────────────
|
|
1204
|
+
function DebugCenter() {
|
|
1205
|
+
var dataPair = useState(null)
|
|
1206
|
+
var data = dataPair[0]
|
|
1207
|
+
var setData = dataPair[1]
|
|
1208
|
+
var probesPair = useState(null)
|
|
1209
|
+
var probes = probesPair[0]
|
|
1210
|
+
var setProbes = probesPair[1]
|
|
1211
|
+
var busyPair = useState(false)
|
|
1212
|
+
var busy = busyPair[0]
|
|
1213
|
+
var setBusy = busyPair[1]
|
|
1214
|
+
function refresh() {
|
|
1215
|
+
if (busy) return
|
|
1216
|
+
setBusy(true)
|
|
1217
|
+
var probesDone = {}
|
|
1218
|
+
var probeList = [
|
|
1219
|
+
['state', API.state, 'GET'], ['config', API.config, 'GET'], ['list', API.list, 'GET'],
|
|
1220
|
+
['file', API.file, 'GET'], ['calendar', API.calendar, 'GET'], ['debug', API.debug, 'GET'],
|
|
1221
|
+
['recall', API.recall, 'POST'], ['summarize', API.summarize, 'POST'], ['greet', API.greet, 'POST'],
|
|
1222
|
+
['workspaces', API.workspaces, 'POST'], ['reflectAuto', API.reflectAuto, 'POST'],
|
|
1223
|
+
]
|
|
1224
|
+
Promise.all(probeList.map(function (pr) {
|
|
1225
|
+
var t0 = Date.now()
|
|
1226
|
+
return fetch(pr[1], pr[2] === 'POST' ? { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' } : undefined)
|
|
1227
|
+
.then(function (r) { probesDone[pr[0]] = { status: r.status, ms: Date.now() - t0 } })
|
|
1228
|
+
.catch(function (e) { probesDone[pr[0]] = { error: e.message } })
|
|
1229
|
+
})).then(function () {
|
|
1230
|
+
setProbes(probesDone)
|
|
1231
|
+
return apiGet(API.debug)
|
|
1232
|
+
}).then(function (d) { setData(d); setBusy(false) }).catch(function () { setBusy(false) })
|
|
1233
|
+
}
|
|
1234
|
+
useEffect(function () { refresh() }, [])
|
|
1235
|
+
if (!data) return h('div', { 'data-dam-hint': '' }, t('dbgLoading'))
|
|
1236
|
+
function kv(label, value, warn) {
|
|
1237
|
+
return h('div', { 'data-dam-row': '', style: { marginBottom: '3px' } },
|
|
1238
|
+
h('span', { style: { flex: '0 0 140px', opacity: .7, fontSize: 'calc(11.5px * var(--dam-scale))' } }, label),
|
|
1239
|
+
h('span', { style: { fontSize: 'calc(11.5px * var(--dam-scale))', wordBreak: 'break-all', color: warn ? 'var(--dsw-alias-state-warn-primary, #e6a23c)' : undefined } }, String(value)))
|
|
1240
|
+
}
|
|
1241
|
+
var hb = data.heartbeat || {}
|
|
1242
|
+
var hbAge = hb.exists && hb.heartbeatAt ? Math.round((Date.now() - hb.heartbeatAt) / 1000) : -1
|
|
1243
|
+
var apiBad = probes && Object.keys(probes).some(function (k) { return probes[k].status === 404 || probes[k].error })
|
|
1244
|
+
return h('div', null,
|
|
1245
|
+
h('div', { 'data-dam-row': '' },
|
|
1246
|
+
h('button', { 'data-dam-btn': '', onClick: refresh, disabled: busy }, busy ? t('dbgRefreshing') : t('dbgRefresh')),
|
|
1247
|
+
h('span', { 'data-dam-hint': '' }, data.host.indexPath || '')),
|
|
1248
|
+
kv(t('kvVersion'), data.host.version || t('kvUnreadable'), false),
|
|
1249
|
+
kv(t('kvPid'), data.host.pid + ' / ' + (data.host.startTime ? new Date(data.host.startTime).toLocaleTimeString() : ''), false),
|
|
1250
|
+
kv(t('kvRestart'), data.host.needsRestart ? t('kvRestartYes') : t('kvRestartNo'), data.host.needsRestart),
|
|
1251
|
+
kv(t('kvHeartbeat'), hb.exists ? (hbAge >= 0 ? t('hbAliveAgo') + hbAge + t('hbSecAgo') : t('hbAlive')) : t('hbNone'), !hb.exists || hbAge > 360),
|
|
1252
|
+
kv(t('kvQueue'), data.autoConsolidate.pendingQueue + t('kvCountSuffix'), data.autoConsolidate.pendingQueue > 0),
|
|
1253
|
+
kv(t('kvToday'), data.autoConsolidate.stats.count + t('kvRecentPrefix') + (data.autoConsolidate.stats.lastAt ? new Date(data.autoConsolidate.stats.lastAt).toLocaleTimeString() : '—'), false),
|
|
1254
|
+
kv(t('kvBusy'), data.autoConsolidate.consolidating ? t('kvYes') : t('kvNo'), data.autoConsolidate.consolidating),
|
|
1255
|
+
kv('subagents', data.subagents.available ? (data.subagents.providers.join(', ') || t('kvAvail')) : t('kvUnavail'), !data.subagents.available),
|
|
1256
|
+
kv(t('kvDup'), data.duplicateHeadings + t('kvDupCount'), data.duplicateHeadings > 0),
|
|
1257
|
+
kv(t('kvMem'), t('kvMemUser') + ' ' + (data.memoryFiles.user.exists ? fmtSize(data.memoryFiles.user.size) : t('kvMemMissing')) + ' · ' + t('kvMemNotes') + ' ' + (data.memoryFiles.notes.exists ? fmtSize(data.memoryFiles.notes.size) : t('kvMemMissing')) + ' · ' + t('kvMemLog') + ' ' + (data.memoryFiles.log.exists ? fmtSize(data.memoryFiles.log.size) : t('kvMemMissing')), !data.memoryFiles.log.exists),
|
|
1258
|
+
kv(t('kvWs'), currentWs() || t('kvWsUnknown'), false),
|
|
1259
|
+
kv(t('kvApi'), probes ? Object.keys(probes).map(function (k) { return k + '=' + (probes[k].status !== undefined ? probes[k].status : probes[k].error) }).join(' ') : '…', apiBad))
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// ───────────────────────── 更新弹窗 / 首次指导(毛玻璃) ─────────────────────────
|
|
1263
|
+
function DialogHost() {
|
|
1264
|
+
var tickPair = useTick()
|
|
1265
|
+
useEffect(function () { return onDialog(tickPair[1]) }, [])
|
|
1266
|
+
if (!dialogState) return null
|
|
1267
|
+
// 左下角小卡片(记忆按钮上方),高透明毛玻璃,不遮全屏
|
|
1268
|
+
var overlay = { position: 'fixed', left: '10px', bottom: '64px', zIndex: 2147483000, width: 'min(360px, calc(100vw - 20px))', maxHeight: 'min(46vh, 430px)', display: 'flex', flexDirection: 'column' }
|
|
1269
|
+
var box = {
|
|
1270
|
+
overflow: 'auto', borderRadius: '14px', padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: '6px',
|
|
1271
|
+
background: 'color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(24,26,32,.9)) 58%, transparent)',
|
|
1272
|
+
backdropFilter: 'blur(14px) saturate(1.3)', WebkitBackdropFilter: 'blur(14px) saturate(1.3)',
|
|
1273
|
+
border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.35)) 45%, transparent)',
|
|
1274
|
+
boxShadow: '0 8px 28px rgba(0,0,0,.18)',
|
|
1275
|
+
}
|
|
1276
|
+
var head = { fontSize: 'calc(13px * var(--dam-scale))', fontWeight: 700, color: 'var(--dsw-alias-text-primary, inherit)' }
|
|
1277
|
+
var sub = { fontSize: 'calc(11px * var(--dam-scale))', opacity: .7 }
|
|
1278
|
+
var item = { fontSize: 'calc(11px * var(--dam-scale))', lineHeight: 1.55, padding: '1px 0 1px 16px', position: 'relative' }
|
|
1279
|
+
var close = { alignSelf: 'flex-end', padding: '4px 16px', borderRadius: '8px', border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.35)) 45%, transparent)', background: 'color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 12%, transparent)', color: 'var(--dsw-alias-text-primary, inherit)', cursor: 'pointer', fontSize: 'calc(11.5px * var(--dam-scale))' }
|
|
1280
|
+
var dot = { position: 'absolute', left: '0', top: '10px', width: '7px', height: '7px', borderRadius: '50%', background: 'var(--dsw-alias-brand-primary, #4f7cff)' }
|
|
1281
|
+
if (dialogState.kind === 'first') {
|
|
1282
|
+
var feats = [t('gFeat1'), t('gFeat2'), t('gFeat3'), t('gFeat4'), t('gFeat5'), t('gFeat6')]
|
|
1283
|
+
return h('div', { style: overlay },
|
|
1284
|
+
h('div', { style: box },
|
|
1285
|
+
h('div', { style: head }, t('guideTitle')),
|
|
1286
|
+
h('div', { style: sub }, t('guideSub')),
|
|
1287
|
+
feats.map(function (f) { return h('div', { style: item }, h('span', { style: dot }), f) }),
|
|
1288
|
+
h('div', { style: { fontSize: 'calc(12px * var(--dam-scale))', opacity: .8, marginTop: '4px' } }, t('guideTip')),
|
|
1289
|
+
h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt'))))
|
|
1290
|
+
}
|
|
1291
|
+
var versions = dialogState.versions || []
|
|
1292
|
+
var lastV = versions.length ? versions[versions.length - 1].version : ''
|
|
1293
|
+
return h('div', { style: overlay },
|
|
1294
|
+
h('div', { style: box },
|
|
1295
|
+
h('div', { style: head }, t('updateTitle') + ' v' + lastV),
|
|
1296
|
+
h('div', { style: sub }, t('updateSub')),
|
|
1297
|
+
versions.map(function (v) {
|
|
1298
|
+
var items = (v.items && (v.items[locale] || v.items.zh)) || []
|
|
1299
|
+
return h('div', null,
|
|
1300
|
+
h('div', { style: { fontSize: 'calc(13px * var(--dam-scale))', fontWeight: 700, margin: '6px 0 2px', opacity: .9 } }, 'v' + v.version),
|
|
1301
|
+
items.map(function (it) { return h('div', { style: item }, h('span', { style: dot }), it) }))
|
|
1302
|
+
}),
|
|
1303
|
+
h('button', { 'data-dam-btn': '', style: close, onClick: closeDialog }, t('gotIt'))))
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// ───────────────────────── 设置页 ─────────────────────────
|
|
1307
|
+
var STYLE_IDS = ['auto', 'life', 'professional']
|
|
1308
|
+
var LOCALE_IDS_LIST = ['system', 'zh', 'en']
|
|
1309
|
+
function SettingsPage() {
|
|
1310
|
+
var tickPair = useTick()
|
|
1311
|
+
var cfgPair = useState(null)
|
|
1312
|
+
var cfg = cfgPair[0]
|
|
1313
|
+
var setCfg = cfgPair[1]
|
|
1314
|
+
var busyPair = useState(false)
|
|
1315
|
+
var busy = busyPair[0]
|
|
1316
|
+
var setBusy = busyPair[1]
|
|
1317
|
+
var msgPair = useState('')
|
|
1318
|
+
var msg = msgPair[0]
|
|
1319
|
+
var setMsg = msgPair[1]
|
|
1320
|
+
var errPair = useState('')
|
|
1321
|
+
var err = errPair[0]
|
|
1322
|
+
var setErr = errPair[1]
|
|
1323
|
+
var dbgOpenPair = useState(false)
|
|
1324
|
+
var dbgOpen = dbgOpenPair[0]
|
|
1325
|
+
var setDbgOpen = dbgOpenPair[1]
|
|
1326
|
+
var browseOpenPair = useState(false)
|
|
1327
|
+
var browseOpen = browseOpenPair[0]
|
|
1328
|
+
var setBrowseOpen = browseOpenPair[1]
|
|
1329
|
+
var browsePathPair = useState('')
|
|
1330
|
+
var browsePath = browsePathPair[0]
|
|
1331
|
+
var setBrowsePath = browsePathPair[1]
|
|
1332
|
+
var browseParentPair = useState('')
|
|
1333
|
+
var browseParent = browseParentPair[0]
|
|
1334
|
+
var setBrowseParent = browseParentPair[1]
|
|
1335
|
+
var browseDirsPair = useState(null)
|
|
1336
|
+
var browseDirs = browseDirsPair[0]
|
|
1337
|
+
var setBrowseDirs = browseDirsPair[1]
|
|
1338
|
+
function browseTo(p) {
|
|
1339
|
+
setBrowsePath(p)
|
|
1340
|
+
apiPost(API.browseDir, { path: p }).then(function (d) {
|
|
1341
|
+
if (d) { setBrowsePath(d.path); setBrowseParent(d.parent); setBrowseDirs(d.dirs || []) }
|
|
1342
|
+
}).catch(function () { setBrowseDirs([]) })
|
|
1343
|
+
}
|
|
1344
|
+
function openBrowser() {
|
|
1345
|
+
// 优先弹系统原生文件夹选择器(native 后端);不可用(远程/无显示)回退内嵌浏览
|
|
1346
|
+
apiPost(API.pickDir, {}).then(function (d) {
|
|
1347
|
+
if (d && d.native && d.dir) {
|
|
1348
|
+
set('memoryRoot', d.dir)
|
|
1349
|
+
setMsg(t('pickedDir') + ' ' + d.dir + ' ' + t('rememberSave'))
|
|
1350
|
+
} else if (d && d.native) {
|
|
1351
|
+
// 用户在系统对话框点了取消:保持原值,不动作
|
|
1352
|
+
} else {
|
|
1353
|
+
setMsg(t('pickerUnavailable'))
|
|
1354
|
+
setBrowseOpen(true)
|
|
1355
|
+
browseTo(cfg.memoryRoot || '')
|
|
1356
|
+
}
|
|
1357
|
+
}).catch(function () {
|
|
1358
|
+
setMsg(t('pickerUnavailable'))
|
|
1359
|
+
setBrowseOpen(true)
|
|
1360
|
+
browseTo(cfg.memoryRoot || '')
|
|
1361
|
+
})
|
|
1362
|
+
}
|
|
1363
|
+
var verPair = useState(null)
|
|
1364
|
+
var verInfo = verPair[0]
|
|
1365
|
+
var setVerInfo = verPair[1]
|
|
1366
|
+
var checkingPair = useState(false)
|
|
1367
|
+
var checkingUpdate = checkingPair[0]
|
|
1368
|
+
var setCheckingUpdate = checkingPair[1]
|
|
1369
|
+
var upBusyPair = useState(false)
|
|
1370
|
+
var upBusy = upBusyPair[0]
|
|
1371
|
+
var setUpBusy = upBusyPair[1]
|
|
1372
|
+
var upMsgPair = useState('')
|
|
1373
|
+
var upMsg = upMsgPair[0]
|
|
1374
|
+
var setUpMsg = upMsgPair[1]
|
|
1375
|
+
useEffect(function () { return controller.subscribe(tickPair[1]) }, [])
|
|
1376
|
+
useEffect(function () {
|
|
1377
|
+
var alive = true
|
|
1378
|
+
apiGet(API.config).then(function (d) { if (alive) setCfg(d.config) }).catch(function (e) { setErr(e.message) })
|
|
1379
|
+
// 打开设置页自动检查更新(host 有 12h 缓存,不重复查网)
|
|
1380
|
+
apiGet(API.updateCheck).then(function (d) { if (alive) setVerInfo(d) }).catch(function () {})
|
|
1381
|
+
return function () { alive = false }
|
|
1382
|
+
}, [])
|
|
1383
|
+
if (!cfg) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
1384
|
+
function set(key, value) { var next = Object.assign({}, cfg); next[key] = value; setCfg(next) }
|
|
1385
|
+
function checkUpdate() {
|
|
1386
|
+
if (checkingUpdate) return
|
|
1387
|
+
setCheckingUpdate(true)
|
|
1388
|
+
apiGet(API.updateCheck + '?force=1').then(function (d) { setVerInfo(d); setCheckingUpdate(false) })
|
|
1389
|
+
.catch(function (e) { setVerInfo({ error: e.message }); setCheckingUpdate(false) })
|
|
1390
|
+
}
|
|
1391
|
+
function doUpdate() {
|
|
1392
|
+
if (upBusy) return
|
|
1393
|
+
setUpBusy(true); setUpMsg('')
|
|
1394
|
+
apiPost(API.update, {}).then(function (d) {
|
|
1395
|
+
setUpBusy(false)
|
|
1396
|
+
if (d && d.ok) { setUpMsg(t('updateDone')); setVerInfo(null); checkUpdate() }
|
|
1397
|
+
else setUpMsg(t('updateFailed') + (d && d.message ? d.message : t('unknown')))
|
|
1398
|
+
}).catch(function (e) { setUpBusy(false); setUpMsg(t('updateFailed') + e.message) })
|
|
1399
|
+
}
|
|
1400
|
+
function save() {
|
|
1401
|
+
if (busy) return
|
|
1402
|
+
setBusy(true); setMsg(''); setErr('')
|
|
1403
|
+
apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false); if (d && d.config && d.config.locale) applyLocalePref(d.config.locale) })
|
|
1404
|
+
.catch(function (e) { setErr(e.message); setBusy(false) })
|
|
1405
|
+
}
|
|
1406
|
+
function field(label, control, hint) {
|
|
1407
|
+
return h('div', null,
|
|
1408
|
+
h('div', { 'data-dam-row': '' }, h('label', null, label), control),
|
|
1409
|
+
hint ? h('div', { 'data-dam-hint': '' }, hint) : null)
|
|
1410
|
+
}
|
|
1411
|
+
return h('div', null,
|
|
1412
|
+
h('div', { 'data-dam-hint': '' }, t('settingsHeader')),
|
|
1413
|
+
field(t('fLocale'), h('select', { 'data-dam-select': '', value: cfg.locale || 'system', onChange: function (e) { set('locale', e.target.value) } },
|
|
1414
|
+
LOCALE_IDS_LIST.map(function (id) { return h('option', { key: id, value: id }, id === 'system' ? t('followSystem') : t(id)) })), t('fLocaleHint')),
|
|
1415
|
+
field(t('fFontSize'), h('select', { 'data-dam-select': '', value: fontScale, onChange: function (e) { fontScale = e.target.value; try { localStorage.setItem('dsh-auto-memory.fontScale.v2', fontScale) } catch (ee) {}; try { var pp = document.querySelector('[data-dam-panel]'); if (pp) pp.style.setProperty('--dam-scale', FONT_SCALE_VALUES[fontScale] || '1') } catch (ee2) {}; emit() } },
|
|
1416
|
+
Object.keys(FONT_SCALES).map(function (k) { return h('option', { key: k, value: k }, t('fs' + k.charAt(0).toUpperCase() + k.slice(1))) })), t('fFontSizeHint')),
|
|
1417
|
+
field(t('fUserDir'), h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), t('fUserDirHint')),
|
|
1418
|
+
field(t('fProjectDir'), h('input', { 'data-dam-input': '', value: cfg.projectMemoryDir, onChange: function (e) { set('projectMemoryDir', e.target.value) } }), t('fProjectDirHint')),
|
|
1419
|
+
field(t('fMemoryRoot'), h('div', { 'data-dam-row': '', style: { flex: 1 } },
|
|
1420
|
+
h('input', { 'data-dam-input': '', style: { flex: 1 }, value: cfg.memoryRoot || '', onChange: function (e) { set('memoryRoot', e.target.value) } }),
|
|
1421
|
+
h('button', { 'data-dam-btn': '', onClick: function () { openBrowser() } }, t('fBrowse'))), t('fMemoryRootHint')),
|
|
1422
|
+
browseOpen ? h('div', { style: { border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 60%, transparent)', borderRadius: '8px', padding: '8px', marginBottom: '8px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent)' } },
|
|
1423
|
+
h('div', { 'data-dam-row': '' },
|
|
1424
|
+
h('b', { style: { fontSize: 'calc(12px * var(--dam-scale))', wordBreak: 'break-all', flex: 1 } }, browsePath || '…'),
|
|
1425
|
+
h('button', { 'data-dam-btn': '', onClick: function () { browseTo(browseParent) }, disabled: browseParent === browsePath }, t('fUp'))),
|
|
1426
|
+
h('div', { style: { maxHeight: '160px', overflow: 'auto', marginTop: '4px' } },
|
|
1427
|
+
(browseDirs || []).map(function (d) {
|
|
1428
|
+
return h('button', { key: d.path, 'data-dam-btn': '', style: { display: 'block', width: '100%', textAlign: 'left', padding: '3px 6px' }, onClick: function () { browseTo(d.path) } }, '📁 ' + d.name)
|
|
1429
|
+
}).concat(browseDirs && !browseDirs.length ? [h('div', { key: 'e', 'data-dam-hint': '' }, t('empty'))] : [])),
|
|
1430
|
+
h('div', { 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
1431
|
+
h('button', { 'data-dam-btn': '', onClick: function () { set('memoryRoot', browsePath); setBrowseOpen(false) } }, t('fSelectDir')),
|
|
1432
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setBrowseOpen(false) } }, t('close'))))
|
|
1433
|
+
: null,
|
|
1434
|
+
field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
|
|
1435
|
+
field(t('fBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 400, value: cfg.injectBudgetChars, onChange: function (e) { set('injectBudgetChars', Number(e.target.value) || 2400) } }), t('fBudgetHint')),
|
|
1436
|
+
field(t('fDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 14, value: cfg.recentDaysInjected, onChange: function (e) { set('recentDaysInjected', Number(e.target.value) || 3) } }), t('fDaysHint')),
|
|
1437
|
+
field(t('fDayBoundary'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 1439, value: (cfg.dayBoundaryMinutes === undefined ? 450 : cfg.dayBoundaryMinutes), onChange: function (e) { set('dayBoundaryMinutes', parseInt(e.target.value || '0', 10)) } }), t('fDayBoundaryHint')),
|
|
1438
|
+
field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
|
|
1439
|
+
field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
|
|
1440
|
+
STYLE_IDS.map(function (id) { return h('option', { key: id, value: id }, t('style' + id.charAt(0).toUpperCase() + id.slice(1))) })), t('fStyleHint')),
|
|
1441
|
+
field(t('fVersion'), h('div', { 'data-dam-row': '' },
|
|
1442
|
+
h('span', { style: { flex: 1 } }, verInfo ? (verInfo.current || '?') + (verInfo.latest ? ' → ' + verInfo.latest + (verInfo.upToDate ? ' ' + t('upToDate') : ' ' + t('hasUpdate')) : '') : (checkingUpdate ? t('checking') : '—')),
|
|
1443
|
+
h('button', { 'data-dam-btn': '', onClick: checkUpdate, disabled: checkingUpdate }, checkingUpdate ? t('checking') : t('checkUpdate')),
|
|
1444
|
+
(verInfo && verInfo.latest && !verInfo.upToDate && verInfo.installKind === 'registry') ? h('button', { 'data-dam-btn': '', onClick: doUpdate, disabled: upBusy, style: { marginLeft: '4px' } }, upBusy ? t('updating') : t('updateNow')) : null,
|
|
1445
|
+
upMsg ? h('span', { 'data-dam-hint': '' }, upMsg) : null),
|
|
1446
|
+
t('versionCmdHint') + (verInfo && verInfo.error ? ' ' + t('versionError') + verInfo.error : '') + (verInfo && verInfo.installKind === 'dev-link' ? ' ' + t('devLinkHint') : '') + (verInfo && !verInfo.installKind ? ' ' + t('noProfileHint') : '')),
|
|
1447
|
+
h('div', { 'data-dam-row': '' },
|
|
1448
|
+
h('button', { 'data-dam-btn': '', onClick: save, disabled: busy }, busy ? t('saving') : t('saveSettings')),
|
|
1449
|
+
msg ? h('span', null, msg) : null),
|
|
1450
|
+
// 调试中心(折叠):模块状态一览,方便排查问题/提 issue
|
|
1451
|
+
h('div', { style: { marginTop: '14px', borderTop: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.2)) 55%, transparent)', paddingTop: '10px' } },
|
|
1452
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setDbgOpen(!dbgOpen) } }, (dbgOpen ? '▴ ' : '▾ ') + t('debugCenter')),
|
|
1453
|
+
dbgOpen ? h('div', { style: { marginTop: '8px' } }, h(DebugCenter)) : null),
|
|
1454
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null)
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
// ───────────────────────── 插件挂载 ─────────────────────────
|
|
1458
|
+
function apply(ctx) {
|
|
1459
|
+
try { ensureStyle() } catch (e) { console.warn('[dsh-auto-memory] style inject failed', e) }
|
|
1460
|
+
ctx.effect(function () {
|
|
1461
|
+
return function () {
|
|
1462
|
+
var tag = document.getElementById(STYLE_ID)
|
|
1463
|
+
if (tag) tag.remove()
|
|
1464
|
+
if (closeTimer) { clearTimeout(closeTimer); closeTimer = null }
|
|
1465
|
+
}
|
|
1466
|
+
}, 'dsh-auto-memory: styles')
|
|
1467
|
+
|
|
1468
|
+
var slots = ctx.slots
|
|
1469
|
+
if (!slots) { console.warn('[dsh-auto-memory] slots service unavailable'); return }
|
|
1470
|
+
sessions = ctx.sessions
|
|
1471
|
+
|
|
1472
|
+
// 初始化界面字号(本地偏好,localStorage 持久化)
|
|
1473
|
+
try {
|
|
1474
|
+
var savedScale = localStorage.getItem('dsh-auto-memory.fontScale.v2')
|
|
1475
|
+
if (savedScale in FONT_SCALES) fontScale = savedScale
|
|
1476
|
+
} catch (e3) {}
|
|
1477
|
+
// 界面语言:默认跟随 DSH 系统语言(config.locale 可手动指定 zh / en / system)
|
|
1478
|
+
try {
|
|
1479
|
+
var sl0 = ctx.locale && ctx.locale.getLocale ? ctx.locale.getLocale() : null
|
|
1480
|
+
if (sl0 && sl0.active) sysLocale = sl0.active
|
|
1481
|
+
} catch (e) {}
|
|
1482
|
+
applyLocalePref('system')
|
|
1483
|
+
apiGet(API.config).then(function (d) {
|
|
1484
|
+
if (d && d.config && d.config.locale) applyLocalePref(d.config.locale)
|
|
1485
|
+
}).catch(function () {})
|
|
1486
|
+
// DSH 系统语言变化:跟随更新 + 重渲染 + 重注册入口 label
|
|
1487
|
+
ctx.on('locale/change', function (snap) {
|
|
1488
|
+
try {
|
|
1489
|
+
if (snap && snap.active && snap.active !== sysLocale) {
|
|
1490
|
+
sysLocale = snap.active
|
|
1491
|
+
if (localeMode !== 'zh' && localeMode !== 'en') { locale = sysLocale; emit() }
|
|
1492
|
+
refreshSurfaces()
|
|
1493
|
+
}
|
|
1494
|
+
} catch (e) {}
|
|
1495
|
+
})
|
|
1496
|
+
// 语言切换时通知所有订阅者重渲染
|
|
1497
|
+
onLocale(function () { emit() })
|
|
1498
|
+
// 暂离回来自动弹开记忆窗口:距离上次关闭面板超过 1 小时,页面加载或切回标签页时自动打开(欢迎语在 GreetingCard 展示)
|
|
1499
|
+
function autoOpenOnReturn() {
|
|
1500
|
+
try {
|
|
1501
|
+
if (isAway() && !controller.isOpen()) controller.open()
|
|
1502
|
+
} catch (e) {}
|
|
1503
|
+
}
|
|
1504
|
+
autoOpenOnReturn()
|
|
1505
|
+
try {
|
|
1506
|
+
document.addEventListener('visibilitychange', function () {
|
|
1507
|
+
if (!document.hidden) autoOpenOnReturn()
|
|
1508
|
+
})
|
|
1509
|
+
} catch (e) {}
|
|
1510
|
+
// 更新弹窗 / 首次指导:对比本地记录的已见版本,有更新或首次安装时弹窗(host 12h 缓存,不重复查网)
|
|
1511
|
+
apiGet(API.updateCheck).then(function (d) {
|
|
1512
|
+
if (d && d.current) {
|
|
1513
|
+
try {
|
|
1514
|
+
var seen = localStorage.getItem('dsh-auto-memory.seenVersion')
|
|
1515
|
+
if (!seen && !localStorage.getItem('dsh-auto-memory.firstRunDone')) {
|
|
1516
|
+
try { localStorage.setItem('dsh-auto-memory.firstRunDone', '1') } catch (e2) {}
|
|
1517
|
+
openDialog({ kind: 'first' })
|
|
1518
|
+
} else if (seen && seen !== d.current) {
|
|
1519
|
+
var versions = changelogBetween(seen, d.current)
|
|
1520
|
+
if (versions.length) openDialog({ kind: 'update', versions: versions })
|
|
1521
|
+
}
|
|
1522
|
+
try { localStorage.setItem('dsh-auto-memory.seenVersion', d.current) } catch (e3) {}
|
|
1523
|
+
} catch (e) {}
|
|
1524
|
+
}
|
|
1525
|
+
}).catch(function () {})
|
|
1526
|
+
|
|
1527
|
+
var surfaceDisposers = []
|
|
1528
|
+
function registerSurfaces() {
|
|
1529
|
+
try {
|
|
1530
|
+
surfaceDisposers.push(slots.inject('sidebar.footer.action', function () {
|
|
1531
|
+
return slots.register({ name: 'sidebar.footer.action', id: 'auto-memory', order: 5, label: t('memory') }, function () { return h(SidebarButton) })
|
|
1532
|
+
}))
|
|
1533
|
+
surfaceDisposers.push(slots.inject('shell.overlay', function () {
|
|
1534
|
+
return slots.register({ name: 'shell.overlay', id: 'auto-memory', order: 5 }, function () { return h(MemoryPanel) })
|
|
1535
|
+
}))
|
|
1536
|
+
surfaceDisposers.push(slots.inject('shell.overlay', function () {
|
|
1537
|
+
return slots.register({ name: 'shell.overlay', id: 'auto-memory-dialogs', order: 6 }, function () { return h(DialogHost) })
|
|
1538
|
+
}))
|
|
1539
|
+
surfaceDisposers.push(slots.inject('settings.section', function () {
|
|
1540
|
+
return slots.register({ name: 'settings.section', id: 'auto-memory', order: 25, label: t('autoMemory') }, function (props) { return h(SettingsPage, { close: props && props.close }) })
|
|
1541
|
+
}))
|
|
1542
|
+
} catch (e) {
|
|
1543
|
+
console.warn('[dsh-auto-memory] slot registration failed', e)
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
function refreshSurfaces() {
|
|
1547
|
+
for (var i = 0; i < surfaceDisposers.length; i++) { try { surfaceDisposers[i]() } catch (e) {} }
|
|
1548
|
+
surfaceDisposers = []
|
|
1549
|
+
registerSurfaces()
|
|
1550
|
+
}
|
|
1551
|
+
registerSurfaces()
|
|
1552
|
+
console.log('[dsh-auto-memory] client ready: sidebar entry + panel + settings page')
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
exports.inject = ['slots', 'sessions']
|
|
1556
|
+
exports.apply = apply
|
|
1557
|
+
return module.exports
|
|
1558
|
+
},
|
|
1559
|
+
})
|