@a9i5k4/dsh-auto-memory 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -7,8 +7,9 @@
7
7
  * 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
8
8
  * Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
9
9
  */
10
+ console.log('[dsh-auto-memory] client v0.1.9 fingerprint: drawer-summary-ok')
10
11
  window.__ModuleLoader__.load({
11
- id: '@a9i5k4/dsh-auto-memory',
12
+ id: '@deepseek-ai/dsh-auto-memory',
12
13
  factory: (require) => {
13
14
  var module = { exports: {} }
14
15
  var exports = module.exports
@@ -97,12 +98,22 @@ window.__ModuleLoader__.load({
97
98
  memoryPanel: '记忆面板',
98
99
  memory: '记忆',
99
100
  autoMemory: '自动记忆',
100
- overview: '概览', logs: '日志', notes: '笔记', reflections: '反思', connect: '接续', search: '检索',
101
- refresh: '刷新', close: '关闭', dragMove: '拖动移动', dragResize: '拖动调整大小', resetPos: '恢复默认位置',
101
+ overview: '概览', logs: '日志', notes: '笔记', reflections: '反思', connect: '接续', calendar: '日历', search: '检索', workspaces: '工作区',
102
+ wsGenerating: '正在生成各工作区总结(每个工作区一次 AI 调用,可能稍慢)…', wsNone: '未发现带记忆的工作区', wsNoSummary: '(无总结)', wsOverview: '工作区总结(跨工作区)',
103
+ debugCenter: '调试中心', dbgLoading: '加载诊断信息…', dbgRefresh: '刷新', dbgRefreshing: '刷新中…',
104
+ fMemoryRoot: '记忆根目录', fMemoryRootHint: '集中式存储:所有工作区记忆统一放在此目录下(每工作区一个子目录),旧版分散的记忆会自动迁移。',
105
+ fBrowse: '浏览…', browseTitle: '选择记忆根目录', fUp: '上级', fSelectDir: '选择此目录',
106
+ pickedDir: '已选择目录:', rememberSave: '(点击「保存设置」后生效)', pickerUnavailable: '系统文件夹选择器不可用,改用内嵌浏览。',
107
+ fDayBoundary: '日界(分钟)', fDayBoundaryHint: '从 0 点起算:凌晨在此之前的活儿归前一天。默认 450=早上 7:30 进入新一天;改 480=8:00;0=按午夜切日。',
108
+ refresh: '刷新', refreshing: '刷新中…', close: '关闭', dragMove: '拖动移动', dragResize: '拖动调整大小', resetPos: '恢复默认位置',
102
109
  generated: '已生成', failed: '失败: ',
110
+ generatedAt: '生成于 ',
111
+ pointsCount: ' 项', summarizing: 'AI 总结生成中…', summaryFailed: 'AI 总结生成失败,点 ⟳ 重试',
103
112
  pendingReflection: '待生成反思: ',
104
113
  pendingReflectionHint: ' —— 可点下方「一键反思」立即生成。',
105
114
  todayWork: '今日工作', logEntries: ' 条日志',
115
+ autoSettledToday: '今日已自动沉淀 ', autoSettledSuffix: ' 条要点',
116
+ autoSettledRecent: '最近: ', autoSettledNone: '本轮对话暂无自动沉淀',
106
117
  dailyReflection: '每日反思', notYet: '(还没有)',
107
118
  workspace: '工作区',
108
119
  reflecting: '反思生成中…', oneClickReflect: '一键反思',
@@ -121,6 +132,7 @@ window.__ModuleLoader__.load({
121
132
  reflectAutoHint: '自动用最近日志生成反思草稿(便于测试)',
122
133
  noReflection: '还没有反思。每天第一次会话时,agent 会主动呈现前一天的工作反思。',
123
134
  searchFailed: '检索失败: ', searchPlaceholder: '搜记忆:关键词或自包含描述…', searchBtn: '检索', resultTitle: '结果',
135
+ smartSearch: '智能检索', smartAnswer: '智能回答', keywordsLabel: '关键词: ',
124
136
  projectFiles: '项目文件', aiAssistant: 'AI 助手',
125
137
  imported: '接入完成', importing: '正在接入 ', importingSuffix: ' 个源…', allImported: '全部接入完成',
126
138
  noExternal: '未检测到其他 AI 工具的记忆文件(CodeBuddy/Claude Code/Codex 等)。',
@@ -134,6 +146,7 @@ window.__ModuleLoader__.load({
134
146
  yesterdayTimeline: '昨天(', pendingReflectionShort: '昨天的工作还没复盘(',
135
147
  saved: '已保存',
136
148
  settingsHeader: '记忆存储与行为设置(保存到 DSH 主目录 dsh-auto-memory.json):',
149
+ fFontSize: '界面字号', fFontSizeHint: '记忆面板文字大小(立即生效,仅本机)', fsSm: '小', fsMd: '标准', fsLg: '大', fsXl: '特大',
137
150
  fUserDir: '用户记忆目录', fUserDirHint: '跨项目规则存放处,支持 ~ 开头;需有文件写权限。',
138
151
  fProjectDir: '项目记忆目录', fProjectDirHint: '相对各工作区的目录名(默认 .dsh-memory)。',
139
152
  fInject: '注入记忆上下文', fInjectHint: '每次组装提示词时自动注入 <memory_system> 块。',
@@ -158,12 +171,22 @@ window.__ModuleLoader__.load({
158
171
  memoryPanel: 'Memory Panel',
159
172
  memory: 'Memory',
160
173
  autoMemory: 'Auto Memory',
161
- overview: 'Overview', logs: 'Logs', notes: 'Notes', reflections: 'Reflections', connect: 'Connect', search: 'Search',
162
- refresh: 'Refresh', close: 'Close', dragMove: 'Drag to move', dragResize: 'Drag to resize', resetPos: 'Reset position',
174
+ overview: 'Overview', logs: 'Logs', notes: 'Notes', reflections: 'Reflections', connect: 'Connect', calendar: 'Calendar', search: 'Search', workspaces: 'Workspaces',
175
+ 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)',
176
+ debugCenter: 'Debug Center', dbgLoading: 'Loading diagnostics…', dbgRefresh: 'Refresh', dbgRefreshing: 'Refreshing…',
177
+ fMemoryRoot: 'Memory root', fMemoryRootHint: 'Centralized storage: all workspace memories live here (one subdir per workspace); legacy memories are auto-migrated.',
178
+ fBrowse: 'Browse…', browseTitle: 'Choose memory root', fUp: 'Up', fSelectDir: 'Select this folder',
179
+ pickedDir: 'Selected folder:', rememberSave: '(click Save to apply)', pickerUnavailable: 'Native folder picker unavailable, falling back to in-app browser.',
180
+ 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.',
181
+ refresh: 'Refresh', refreshing: 'Refreshing…', close: 'Close', dragMove: 'Drag to move', dragResize: 'Drag to resize', resetPos: 'Reset position',
163
182
  generated: 'Generated', failed: 'Failed: ',
183
+ generatedAt: 'Generated ',
184
+ pointsCount: ' items', summarizing: 'AI summary generating…', summaryFailed: 'Summary failed, press refresh to retry',
164
185
  pendingReflection: 'Pending reflection: ',
165
186
  pendingReflectionHint: ' — click "One-click Reflect" below to generate now.',
166
187
  todayWork: 'Today', logEntries: ' log entries',
188
+ autoSettledToday: 'Auto-consolidated ', autoSettledSuffix: ' points today',
189
+ autoSettledRecent: 'Latest: ', autoSettledNone: 'Nothing auto-consolidated this turn',
167
190
  dailyReflection: 'Daily reflection', notYet: '(none yet)',
168
191
  workspace: 'Workspace',
169
192
  reflecting: 'Generating…', oneClickReflect: 'One-click Reflect',
@@ -182,6 +205,7 @@ window.__ModuleLoader__.load({
182
205
  reflectAutoHint: 'Auto-generate a reflection draft from recent logs (for testing)',
183
206
  noReflection: "No reflections yet. On the first session each day, the agent presents the previous day's reflection.",
184
207
  searchFailed: 'Search failed: ', searchPlaceholder: 'Search memory: keyword or self-contained description…', searchBtn: 'Search', resultTitle: 'Results',
208
+ smartSearch: 'Smart search', smartAnswer: 'AI Answer', keywordsLabel: 'Keywords: ',
185
209
  projectFiles: 'Project files', aiAssistant: 'AI Assistant',
186
210
  imported: 'Imported', importing: 'Importing ', importingSuffix: ' sources…', allImported: 'All imported',
187
211
  noExternal: 'No memory files found from other AI tools (CodeBuddy/Claude Code/Codex etc.).',
@@ -195,6 +219,7 @@ window.__ModuleLoader__.load({
195
219
  yesterdayTimeline: 'Yesterday (', pendingReflectionShort: 'Yesterday\'s work not reviewed (',
196
220
  saved: 'Saved',
197
221
  settingsHeader: 'Memory storage & behavior (saved to ~/.dsh/dsh-auto-memory.json):',
222
+ fFontSize: 'Panel font size', fFontSizeHint: 'Memory panel text size (applies immediately, this device only)', fsSm: 'Small', fsMd: 'Normal', fsLg: 'Large', fsXl: 'Extra large',
198
223
  fUserDir: 'User memory dir', fUserDirHint: 'Cross-project rules; supports ~ prefix; needs write permission.',
199
224
  fProjectDir: 'Project memory dir', fProjectDirHint: 'Directory name relative to each workspace (default .dsh-memory).',
200
225
  fInject: 'Inject memory context', fInjectHint: 'Auto-inject <memory_system> block into every prompt.',
@@ -216,6 +241,21 @@ window.__ModuleLoader__.load({
216
241
  }
217
242
  }
218
243
  var locale = 'zh'
244
+ var sessions = null
245
+ // 当前会话工作区(跟随 GUI 切换工作区):从 sessions 服务拿,供 state/summarize/greet 请求使用
246
+ function currentWs() {
247
+ try {
248
+ var snap = sessions && sessions.list && sessions.list.getSnapshot()
249
+ if (!snap) return ''
250
+ var id = snap.current
251
+ if (id === undefined || id === null) return ''
252
+ var s = snap.byId && snap.byId[id]
253
+ return (s && typeof s.cwd === 'string') ? s.cwd : ''
254
+ } catch (e) { return '' }
255
+ }
256
+ var fontScale = 'lg'
257
+ var FONT_SCALES = { sm: '小', md: '标准', lg: '大', xl: '特大' }
258
+ var FONT_SCALE_VALUES = { sm: '0.9', md: '1', lg: '1.15', xl: '1.3' }
219
259
  var localeListeners = new Set()
220
260
  function t(key) { return (I18N[locale] && I18N[locale][key]) || I18N.zh[key] || key }
221
261
  function setLocale(l) { if (l !== 'zh' && l !== 'en') return; if (l === locale) return; locale = l; localeListeners.forEach(function (fn) { try { fn(l) } catch (e) {} }) }
@@ -227,6 +267,11 @@ window.__ModuleLoader__.load({
227
267
  list: '/api/dsh-auto-memory/list',
228
268
  file: '/api/dsh-auto-memory/file',
229
269
  recall: '/api/dsh-auto-memory/recall',
270
+ smartRecall: '/api/dsh-auto-memory/smart-recall',
271
+ workspaces: '/api/dsh-auto-memory/workspaces',
272
+ debug: '/api/dsh-auto-memory/debug',
273
+ browseDir: '/api/dsh-auto-memory/browse-dir',
274
+ pickDir: '/api/dsh-auto-memory/pick-dir',
230
275
  config: '/api/dsh-auto-memory/config',
231
276
  reflect: '/api/dsh-auto-memory/reflect',
232
277
  reflectAuto: '/api/dsh-auto-memory/reflect-auto',
@@ -234,6 +279,7 @@ window.__ModuleLoader__.load({
234
279
  external: '/api/dsh-auto-memory/external',
235
280
  externalImport: '/api/dsh-auto-memory/external-import',
236
281
  summarize: '/api/dsh-auto-memory/summarize',
282
+ greet: '/api/dsh-auto-memory/greet',
237
283
  }
238
284
  function query(params) {
239
285
  var search = new URLSearchParams()
@@ -258,7 +304,7 @@ window.__ModuleLoader__.load({
258
304
  // 视觉:液态玻璃(毛玻璃)—— backdrop-filter + DSH 主题令牌(--dsw-alias-*),
259
305
  // 跟随亮/暗主题与页面背景自适应;位置/尺寸由 JS 几何状态驱动(可拖动、可缩放)。
260
306
  var CSS = [
261
- '[data-dam-panel] { position: fixed; left: 16px; width: 440px; height: 560px;',
307
+ '[data-dam-panel] { position: fixed; left: 16px; width: 440px; height: 560px; --dam-scale: 1;',
262
308
  ' max-width: calc(100vw - 32px); max-height: calc(100vh - 32px);',
263
309
  ' display: flex; flex-direction: column; overflow: hidden; z-index: 3000; pointer-events: auto;',
264
310
  ' border-radius: 16px; font: 13px/1.55 system-ui, "Segoe UI", sans-serif;',
@@ -271,36 +317,39 @@ window.__ModuleLoader__.load({
271
317
  '@keyframes dam-in { from { opacity: 0; transform: scale(.92) translateY(10px); } to { opacity: 1; transform: none; } }',
272
318
  '@keyframes dam-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: scale(.95) translateY(6px); } }',
273
319
  '[data-dam-panel][data-closing="true"] { animation: dam-out .16s ease-in both; }',
320
+ '[data-dam-panel][data-scale="sm"] { --dam-scale: .9; }',
321
+ '[data-dam-panel][data-scale="lg"] { --dam-scale: 1.15; }',
322
+ '[data-dam-panel][data-scale="xl"] { --dam-scale: 1.3; }',
274
323
  '[data-dam-panel]::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 64%; pointer-events: none;',
275
324
  ' background: linear-gradient(180deg, rgba(255,255,255,.13), rgba(255,255,255,0) 70%); border-radius: 16px 16px 0 0; }',
276
325
  '[data-dam-panel][data-dragging="true"] { user-select: none; }',
277
326
  '[data-dam-panel] header { display: flex; align-items: center; gap: 8px; padding: 10px 14px; cursor: grab;',
278
327
  ' border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 55%, transparent); }',
279
328
  '[data-dam-panel][data-dragging="true"] header { cursor: grabbing; }',
280
- '[data-dam-panel] header strong { font-size: 14px; }',
329
+ '[data-dam-panel] header strong { font-size: calc(14px * var(--dam-scale)); }',
281
330
  '[data-dam-panel] header .dam-spacer { flex: 1; }',
282
331
  '[data-dam-resize] { position: absolute; right: 0; bottom: 0; width: 22px; height: 22px; cursor: nwse-resize;',
283
332
  ' opacity: .55; z-index: 2; border-radius: 0 0 16px 0;',
284
333
  ' background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 45%, transparent) 50%); }',
285
334
  '[data-dam-resize]:hover { opacity: 1; }',
286
- '[data-dam-btn] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .75; font-size: 13px; padding: 4px 8px; border-radius: 6px; }',
335
+ '[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; }',
287
336
  '[data-dam-btn]:hover { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 16%, transparent); }',
288
337
  '[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); }',
289
- '[data-dam-tab] { border: none; background: transparent; cursor: pointer; color: inherit; opacity: .6; padding: 5px 10px; border-radius: 7px; font-size: 12.5px; }',
338
+ '[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)); }',
290
339
  '[data-dam-tab][data-active="true"] { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 18%, transparent); font-weight: 600; }',
291
340
  '[data-dam-body] { flex: 1; overflow: auto; padding: 12px 14px; }',
292
- '[data-dam-kv] { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 12.5px; }',
341
+ '[data-dam-kv] { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: calc(12.5px * var(--dam-scale)); }',
293
342
  '[data-dam-kv] b { opacity: .55; font-weight: 500; }',
294
- '[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: 12.5px;',
343
+ '[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));',
295
344
  ' background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent); }',
296
345
  '[data-dam-card] .dam-date { font-weight: 600; margin-bottom: 4px; }',
297
346
  '[data-dam-card] .dam-content { white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }',
298
347
  '[data-dam-banner] { border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 55%, transparent);',
299
- ' 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: 12.5px; }',
348
+ ' 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)); }',
300
349
  '[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; }',
301
350
  '[data-dam-row] { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }',
302
- '[data-dam-row] label { flex: 0 0 110px; opacity: .8; font-size: 12.5px; }',
303
- '[data-dam-hint] { opacity: .5; font-size: 11.5px; margin-top: 2px; }',
351
+ '[data-dam-row] label { flex: 0 0 110px; opacity: .8; font-size: calc(12.5px * var(--dam-scale)); }',
352
+ '[data-dam-hint] { opacity: .5; font-size: calc(11.5px * var(--dam-scale)); margin-top: 2px; }',
304
353
  '[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; }',
305
354
  '[data-dam-sidebar-btn]:hover { background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 14%, transparent); opacity: 1; }',
306
355
  '[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); }',
@@ -314,7 +363,7 @@ window.__ModuleLoader__.load({
314
363
  if (document.getElementById(STYLE_ID)) return
315
364
  var tag = document.createElement('style')
316
365
  tag.id = STYLE_ID
317
- tag.dataset.plugin = '@a9i5k4/dsh-auto-memory'
366
+ tag.dataset.plugin = '@deepseek-ai/dsh-auto-memory'
318
367
  tag.textContent = CSS
319
368
  document.head.appendChild(tag)
320
369
  }
@@ -360,6 +409,9 @@ window.__ModuleLoader__.load({
360
409
  var openPair = useState({})
361
410
  var openMap = openPair[0]
362
411
  var setOpenMap = openPair[1]
412
+ var subOpenPair = useState({})
413
+ var subOpenMap = subOpenPair[0]
414
+ var setSubOpenMap = subOpenPair[1]
363
415
  // 智能时段判定
364
416
  var hour = new Date().getHours()
365
417
  var seg = hour < 9 ? 'morning' : hour < 12 ? 'forenoon' : hour < 14 ? 'noon' : hour < 18 ? 'afternoon' : 'evening'
@@ -369,7 +421,7 @@ window.__ModuleLoader__.load({
369
421
  var curSegName = { morning: '早晨', forenoon: '上午', noon: '中午', afternoon: '下午', evening: '晚上' }[seg]
370
422
  // 昨天抽屉(早晨显示)
371
423
  if (seg === 'morning' && g.entries.length) {
372
- drawers.push({ key: 'yesterday', label: t('yesterdayDrawer') + (g.yesterdayDate || ''), hint: '', items: g.entries, withTime: true, defaultOpen: true })
424
+ drawers.push({ key: 'yesterday', label: t('yesterdayDrawer') + (g.yesterdayDate || ''), hint: '', items: g.entries, withTime: true, defaultOpen: true, period: '昨天' })
373
425
  }
374
426
  // 今天各时段抽屉(已过的时段)
375
427
  var seenSeg = { '早晨': seg !== 'morning', '上午': (seg === 'forenoon' || seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '中午': (seg === 'noon' || seg === 'afternoon' || seg === 'evening'), '下午': (seg === 'afternoon' || seg === 'evening'), '晚上': seg === 'evening' }
@@ -379,7 +431,7 @@ window.__ModuleLoader__.load({
379
431
  if (!seenSeg[sname]) continue
380
432
  var items = (ps && ps.groups && ps.groups[sname]) || []
381
433
  if (!items.length) continue
382
- drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + sname, hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName })
434
+ drawers.push({ key: 'seg-' + sname, label: t('segPrefix') + sname, hint: '', items: items.map(function (x) { return { time: '', text: x } }), withTime: false, defaultOpen: sname === curSegName, period: sname })
383
435
  }
384
436
  // 外层:生活化问候
385
437
  var totalCount = 0
@@ -404,33 +456,64 @@ window.__ModuleLoader__.load({
404
456
  var sumCachePair = useState({})
405
457
  var sumCache = sumCachePair[0]
406
458
  var setSumCache = sumCachePair[1]
407
- function requestSummary(key, period) {
408
- if (sumCache[key] !== undefined) return
409
- apiPost(API.summarize, { period: period }).then(function (d) {
410
- if (d && d.result) { var n = Object.assign({}, sumCache); n[key] = d.result; setSumCache(n) }
411
- }).catch(function () {})
459
+ function requestSummary(key, period, force) {
460
+ if (!force && sumCache[key] !== undefined) return
461
+ apiPost(API.summarize, { period: period, force: !!force, ws: currentWs() }).then(function (d) {
462
+ if (d && (d.summary || (d.works && d.works.length))) {
463
+ var n = Object.assign({}, sumCache)
464
+ n[key] = { summary: d.summary || d.result || '', works: d.works || [], at: d.generatedAt || Date.now(), failed: false }
465
+ setSumCache(n)
466
+ }
467
+ }).catch(function () {
468
+ var n = Object.assign({}, sumCache)
469
+ n[key] = { summary: '', works: [], at: 0, failed: true }
470
+ setSumCache(n)
471
+ })
412
472
  }
473
+ // 面板刷新键(nonce 变化):已缓存的展开抽屉 force 重新生成
474
+ useEffect(function () {
475
+ if (!props.nonce) return
476
+ for (var d = 0; d < drawers.length; d++) {
477
+ var dr = drawers[d]
478
+ var isOpen = openMap[dr.key] !== undefined ? openMap[dr.key] : dr.defaultOpen
479
+ if (isOpen && sumCache[dr.key] !== undefined) requestSummary(dr.key, dr.period, true)
480
+ }
481
+ }, [props.nonce])
413
482
  for (var d = 0; d < drawers.length; d++) {
414
483
  (function (dr) {
415
484
  var isOpen = openMap[dr.key] !== undefined ? openMap[dr.key] : dr.defaultOpen
416
- if (isOpen && sumCache[dr.key] === undefined) {
417
- requestSummary(dr.key, dr.key === 'yesterday' ? '昨天' : dr.key.replace('seg-', ''))
418
- }
485
+ if (isOpen && sumCache[dr.key] === undefined) requestSummary(dr.key, dr.period, false)
486
+ var cached = sumCache[dr.key]
487
+ // 大抽屉标题:有 AI 总结时用总结内容(用户要求),否则用时段名
488
+ var bigTitle = cached && cached.summary ? cached.summary : dr.label
419
489
  rows.push(h('div', { key: dr.key, style: { marginTop: '8px' } },
420
490
  h('button', {
421
491
  'data-dam-btn': '',
422
492
  onClick: function () { var n = Object.assign({}, openMap); n[dr.key] = !isOpen; setOpenMap(n) },
423
- style: { width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '5px 8px', borderRadius: '7px', 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)' },
493
+ 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)' },
424
494
  },
425
- h('span', { style: { fontWeight: 600, fontSize: '12.5px' } }, (isOpen ? '▼ ' : '▶ ') + dr.label),
426
- h('span', { 'data-dam-muted': '', style: { fontSize: '11.5px' } }, dr.items.length + t('logEntries'))),
427
- isOpen ? h('div', { style: { padding: '6px 4px 2px 10px' } },
428
- sumCache[dr.key] ? h('div', { style: { marginBottom: '8px', padding: '8px 10px', borderRadius: '8px', fontSize: '12.5px', lineHeight: 1.65, background: 'color-mix(in srgb, var(--dsw-alias-brand-primary, #4f7cff) 8%, transparent)', borderLeft: '3px solid var(--dsw-alias-brand-primary, #4f7cff)' } }, sumCache[dr.key]) : null,
429
- dr.items.map(function (en) {
430
- return h('div', { key: (en.time || '') + en.text, style: { marginBottom: '3px', fontSize: '12.5px', lineHeight: 1.5 } },
431
- dr.withTime && en.time ? h('span', { 'data-dam-muted': '', style: { fontFamily: 'monospace', fontSize: '11px', marginRight: '6px' } }, en.time) : null,
432
- h('span', { style: { wordBreak: 'break-word' } }, en.text))
433
- })) : null))
495
+ h('div', { style: { fontWeight: 600, fontSize: 'calc(12.5px * var(--dam-scale))', lineHeight: 1.5 } }, (isOpen ? '▼ ' : '▶ ') + bigTitle),
496
+ h('div', { 'data-dam-muted': '', style: { fontSize: 'calc(11px * var(--dam-scale))', marginTop: '2px' } },
497
+ dr.label + ' · ' + dr.items.length + t('logEntries') + (cached && cached.at ? ' · ' + t('generatedAt') + new Date(cached.at).toLocaleTimeString() : ''))),
498
+ isOpen ? h('div', { style: { padding: '4px 2px 2px 8px' } },
499
+ cached && cached.works && cached.works.length ? cached.works.map(function (w, wi) {
500
+ var sk = dr.key + ':w' + wi
501
+ var sOpen = subOpenMap[sk] !== undefined ? subOpenMap[sk] : false
502
+ return h('div', { key: sk, style: { marginTop: '5px' } },
503
+ h('button', {
504
+ 'data-dam-btn': '',
505
+ onClick: function () { var n = Object.assign({}, subOpenMap); n[sk] = !sOpen; setSubOpenMap(n) },
506
+ 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)' },
507
+ },
508
+ h('span', { style: { fontWeight: 600, fontSize: 'calc(12px * var(--dam-scale))' } }, (sOpen ? '▾ ' : '▸ ') + w.title),
509
+ h('span', { 'data-dam-muted': '', style: { fontSize: 'calc(11px * var(--dam-scale))', marginLeft: '6px' } }, w.points.length + t('pointsCount'))),
510
+ sOpen ? h('div', { style: { padding: '4px 4px 2px 14px' } },
511
+ w.points.map(function (pt, pi) {
512
+ return h('div', { key: pi, style: { fontSize: 'calc(12px * var(--dam-scale))', lineHeight: 1.5, marginBottom: '2px' } }, '· ' + pt)
513
+ })) : null)
514
+ }) : cached && cached.failed ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summaryFailed'))
515
+ : cached && cached.summary ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, cached.summary)
516
+ : h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summarizing'))) : null))
434
517
  })(drawers[d])
435
518
  }
436
519
  // 待反思提醒
@@ -440,7 +523,7 @@ window.__ModuleLoader__.load({
440
523
  return h(Card, { title: (g.period || '') + ' · ' + segLabel }, rows)
441
524
  }
442
525
 
443
- function OverviewTab() {
526
+ function OverviewTab(props) {
444
527
  var statePair = useState(null)
445
528
  var state = statePair[0]
446
529
  var setState = statePair[1]
@@ -453,10 +536,35 @@ window.__ModuleLoader__.load({
453
536
  var actMsgPair = useState('')
454
537
  var actMsg = actMsgPair[0]
455
538
  var setActMsg = actMsgPair[1]
539
+ var wsPair = useState(null)
540
+ var wsData = wsPair[0]
541
+ var setWsData = wsPair[1]
542
+ // 每次进入面板 / 点刷新(nonce 变化)/ 面板打开期间每 30s 自动重拉,保证自动沉淀等数据新鲜
456
543
  useEffect(function () {
457
544
  var alive = true
458
- apiGet(API.state).then(function (s) { if (alive) setState(s) }).catch(function () {})
545
+ // 跨工作区总结(概览页集成:所有工作区的小总结+细分;读缓存)
546
+ apiPost(API.workspaces, { force: false }).then(function (d) { if (d && alive) setWsData(d) }).catch(function () {})
547
+ apiGet(API.state, { ws: currentWs() }).then(function (s) {
548
+ if (alive) setState(s)
549
+ // 无 AI 问候 → 自动生成一次(host 按时段缓存,不会重复生成)
550
+ if (s && s.greeting && !s.greeting.greeting) {
551
+ apiPost(API.greet, { ws: currentWs() }).then(function (d) {
552
+ if (d && d.greeting && alive) {
553
+ setState(function (prev) {
554
+ var g0 = (prev && prev.greeting) || {}
555
+ return Object.assign({}, prev, { greeting: Object.assign({}, g0, { greeting: d.greeting, hasGreeting: true }) })
556
+ })
557
+ }
558
+ }).catch(function () {})
559
+ }
560
+ }).catch(function () {})
459
561
  return function () { alive = false }
562
+ }, [props && props.nonce])
563
+ useEffect(function () {
564
+ var timer = setInterval(function () {
565
+ apiGet(API.state, { ws: currentWs() }).then(function (s) { setState(s) }).catch(function () {})
566
+ }, 30000)
567
+ return function () { clearInterval(timer) }
460
568
  }, [])
461
569
  useEffect(function () {
462
570
  // 记录本次活动时间;下次打开若相隔>1小时,GreetingCard 显示"欢迎回来"
@@ -468,12 +576,25 @@ window.__ModuleLoader__.load({
468
576
  setReflectBusy(true); setActMsg('')
469
577
  apiPost(API.reflectAuto, {}).then(function (d) {
470
578
  setActMsg(d.result || t('generated')); setReflectBusy(false)
471
- apiGet(API.state).then(function (s) { if (s) setState(s) }).catch(function () {})
579
+ apiGet(API.state, { ws: currentWs() }).then(function (s) { if (s) setState(s) }).catch(function () {})
472
580
  }).catch(function (e) { setActMsg(t('failed') + e.message); setReflectBusy(false) })
473
581
  }
474
582
  return h('div', null,
475
583
  // 今日问候卡:问候语 + 昨天时间轴 + 提醒(纯 GUI 渲染,不干扰对话流)
476
- h(GreetingCard, { greeting: state.greeting, periodSummary: state.periodSummary, t: t }),
584
+ h(GreetingCard, { greeting: state.greeting, periodSummary: state.periodSummary, t: t, nonce: props && props.nonce }),
585
+ // 跨工作区总结(所有工作区的小总结 + 区内细分)
586
+ h('div', { style: { marginTop: '10px' } },
587
+ h('div', { style: { fontWeight: 700, fontSize: 'calc(12.5px * var(--dam-scale))', opacity: 0.75, marginBottom: '6px' } }, t('wsOverview')),
588
+ wsData && wsData.workspaces && wsData.workspaces.length
589
+ ? wsData.workspaces.map(function (ws) {
590
+ 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)' } },
591
+ h('div', { style: { fontWeight: 600, fontSize: 'calc(12px * var(--dam-scale))' } }, ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '')),
592
+ 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')),
593
+ (ws.items && ws.items.length) ? ws.items.map(function (it, i) {
594
+ return h('div', { key: i, style: { fontSize: 'calc(11.5px * var(--dam-scale))', marginTop: '2px', opacity: 0.75 } }, '· ' + it)
595
+ }) : null)
596
+ })
597
+ : h('div', { 'data-dam-hint': '' }, t('wsGenerating'))),
477
598
  state.pendingReflection
478
599
  ? h(Banner, null, t('pendingReflection') + state.pendingReflection + t('pendingReflectionHint'))
479
600
  : null,
@@ -482,6 +603,12 @@ window.__ModuleLoader__.load({
482
603
  h('b', null, t('todayWork')), h('span', null, state.todayEntries + t('logEntries')),
483
604
  h('b', null, t('dailyReflection')), h('span', null, state.latestReflectionDate || t('notYet')),
484
605
  h('b', null, t('workspace')), h('span', null, state.ws)),
606
+ // 自动沉淀即时反馈(本轮/今日沉淀了多少条、最近一次时间)
607
+ state.autoStats && state.autoStats.count > 0
608
+ ? h('div', { 'data-dam-hint': '', style: { marginTop: '2px' } },
609
+ t('autoSettledToday') + state.autoStats.count + t('autoSettledSuffix') +
610
+ (state.autoStats.lastAt ? ' (' + t('autoSettledRecent') + new Date(state.autoStats.lastAt).toLocaleTimeString() + ')' : ''))
611
+ : h('div', { 'data-dam-hint': '', style: { marginTop: '2px' } }, t('autoSettledNone')),
485
612
  // 快捷操作
486
613
  h('div', { 'data-dam-row': '' },
487
614
  h('button', { 'data-dam-btn': '', onClick: oneClickReflect, disabled: reflectBusy }, reflectBusy ? t('reflecting') : t('oneClickReflect')),
@@ -641,22 +768,69 @@ window.__ModuleLoader__.load({
641
768
  var resultPair = useState(null)
642
769
  var result = resultPair[0]
643
770
  var setResult = resultPair[1]
771
+ var smartPair = useState(null)
772
+ var smart = smartPair[0]
773
+ var setSmart = smartPair[1]
644
774
  var busyPair = useState(false)
645
775
  var busy = busyPair[0]
646
776
  var setBusy = busyPair[1]
647
777
  function search() {
648
778
  if (!q.trim() || busy) return
649
- setBusy(true); setResult(null)
779
+ setBusy(true); setResult(null); setSmart(null)
650
780
  apiPost(API.recall, { query: q.trim() }).then(function (d) { setResult(d.result); setBusy(false) })
651
781
  .catch(function (e) { setResult(t('searchFailed') + e.message); setBusy(false) })
652
782
  }
783
+ function smartSearch() {
784
+ if (!q.trim() || busy) return
785
+ setBusy(true); setResult(null); setSmart(null)
786
+ apiPost(API.smartRecall, { query: q.trim() }).then(function (d) { setSmart(d); setBusy(false) })
787
+ .catch(function (e) { setSmart({ answer: t('searchFailed') + e.message, keywords: [], hits: [] }); setBusy(false) })
788
+ }
653
789
  return h('div', null,
654
790
  h('div', { 'data-dam-row': '' },
655
791
  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() } }),
656
- h('button', { 'data-dam-btn': '', onClick: search, disabled: busy }, t('searchBtn'))),
792
+ h('button', { 'data-dam-btn': '', onClick: search, disabled: busy }, t('searchBtn')),
793
+ h('button', { 'data-dam-btn': '', onClick: smartSearch, disabled: busy }, t('smartSearch'))),
794
+ smart ? h(Card, { title: t('smartAnswer') },
795
+ h('div', { 'data-dam-content': '' }, smart.answer || t('empty')),
796
+ smart.keywords && smart.keywords.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } }, t('keywordsLabel') + smart.keywords.join(' / ')) : null,
797
+ smart.hits && smart.hits.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } },
798
+ smart.hits.map(function (h, i) { return h('div', { key: i, style: { marginTop: '3px' } }, '· [' + h.where + '] ' + h.line) })) : null)
799
+ : null,
657
800
  result ? h(Card, { title: t('resultTitle') }, h('div', { 'data-dam-content': '' }, result)) : null)
658
801
  }
659
802
 
803
+ // ───────────────────────── 工作区总览页签(跨工作区全局总结) ─────────────────────────
804
+ function WorkspaceTab() {
805
+ var dataPair = useState(null)
806
+ var data = dataPair[0]
807
+ var setData = dataPair[1]
808
+ var busyPair = useState(false)
809
+ var busy = busyPair[0]
810
+ var setBusy = busyPair[1]
811
+ function load(force) {
812
+ if (busy) return
813
+ setBusy(true)
814
+ apiPost(API.workspaces, { force: !!force }).then(function (d) { if (d) setData(d); setBusy(false) })
815
+ .catch(function () { setBusy(false) })
816
+ }
817
+ useEffect(function () { load(false) }, [])
818
+ if (!data) return h('div', null, busy ? h('div', { 'data-dam-hint': '' }, t('wsGenerating')) : h(Loading))
819
+ return h('div', null,
820
+ h('div', { 'data-dam-row': '' },
821
+ h('button', { 'data-dam-btn': '', onClick: function () { load(true) }, disabled: busy }, busy ? t('refreshing') : t('refresh')),
822
+ h('span', { 'data-dam-hint': '' }, data.cached ? (t('generatedAt') + new Date(data.generatedAt).toLocaleTimeString()) : '')),
823
+ (data.workspaces && data.workspaces.length) ? data.workspaces.map(function (ws) {
824
+ return h(Card, { key: ws.path, title: ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '') },
825
+ ws.summary ? h('div', { 'data-dam-content': '' }, ws.summary) : h('div', { 'data-dam-hint': '' }, t('wsNoSummary')),
826
+ (ws.items && ws.items.length) ? h('div', { style: { marginTop: '6px' } },
827
+ ws.items.map(function (it, i) {
828
+ return h('div', { key: i, style: { fontSize: 'calc(12px * var(--dam-scale))', marginTop: '3px', lineHeight: 1.5 } }, '· ' + it)
829
+ })) : null,
830
+ h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } }, ws.path + ' · ' + (ws.logCount || 0) + t('logEntries')))
831
+ }) : h('div', { 'data-dam-hint': '' }, t('wsNone')))
832
+ }
833
+
660
834
  // ───────────────────────── 日历页签 ─────────────────────────
661
835
  var QUADRANT_STYLE = {
662
836
  '重要紧急': { color: 'var(--dsw-alias-state-error-primary, #d64545)', label: t('qUrgentImportant') },
@@ -716,12 +890,12 @@ window.__ModuleLoader__.load({
716
890
  // 图例
717
891
  rows.push(h('div', { 'data-dam-row': '', style: { flexWrap: 'wrap' } },
718
892
  Object.keys(QUADRANT_STYLE).map(function (q) {
719
- return h('span', { key: q, style: { fontSize: '11px', opacity: 0.8, marginRight: '10px' } },
893
+ return h('span', { key: q, style: { fontSize: 'calc(11px * var(--dam-scale))', opacity: 0.8, marginRight: '10px' } },
720
894
  h('span', { style: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: QUADRANT_STYLE[q].color, marginRight: 4 } }), QUADRANT_STYLE[q].label)
721
895
  })))
722
896
  // 星期表头
723
897
  rows.push(h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '3px', marginBottom: '4px' } },
724
- dowLabels.map(function (dl) { return h('div', { key: dl, style: { textAlign: 'center', fontSize: '11px', opacity: 0.55 } }, dl) })))
898
+ dowLabels.map(function (dl) { return h('div', { key: dl, style: { textAlign: 'center', fontSize: 'calc(11px * var(--dam-scale))', opacity: 0.55 } }, dl) })))
725
899
  // 日历格子
726
900
  var gridRows = []
727
901
  for (var ci = 0; ci < cells.length; ci += 7) {
@@ -742,7 +916,7 @@ window.__ModuleLoader__.load({
742
916
  overflow: 'hidden',
743
917
  },
744
918
  },
745
- h('div', { style: { fontSize: '11.5px', fontWeight: isToday ? 700 : 500, opacity: isToday ? 1 : 0.7, marginBottom: '3px' } }, dayNum),
919
+ h('div', { style: { fontSize: 'calc(11.5px * var(--dam-scale))', fontWeight: isToday ? 700 : 500, opacity: isToday ? 1 : 0.7, marginBottom: '3px' } }, dayNum),
746
920
  es.slice(0, 3).map(function (en) {
747
921
  var qs = QUADRANT_STYLE[en.quadrant] || QUADRANT_STYLE['未分类']
748
922
  return h('div', {
@@ -750,14 +924,14 @@ window.__ModuleLoader__.load({
750
924
  title: en.time + ' ' + en.title,
751
925
  onClick: function (ev) { ev.stopPropagation(); toggleDone(en) },
752
926
  style: {
753
- fontSize: '10.5px', lineHeight: 1.35, padding: '1px 4px', borderRadius: 4, marginBottom: 2,
927
+ fontSize: 'calc(10.5px * var(--dam-scale))', lineHeight: 1.35, padding: '1px 4px', borderRadius: 4, marginBottom: 2,
754
928
  background: 'color-mix(in srgb, ' + qs.color + ' 18%, transparent)',
755
929
  color: qs.color, textDecoration: en.done ? 'line-through' : 'none',
756
930
  opacity: en.done ? 0.5 : 1, cursor: 'pointer', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
757
931
  },
758
932
  }, (en.time && en.time !== '--:--' ? en.time + ' ' : '') + en.title)
759
933
  }),
760
- es.length > 3 ? h('div', { style: { fontSize: '10px', opacity: 0.5 } }, '+' + (es.length - 3)) : null))
934
+ es.length > 3 ? h('div', { style: { fontSize: 'calc(10px * var(--dam-scale))', opacity: 0.5 } }, '+' + (es.length - 3)) : null))
761
935
  })(cells[ci + cj])
762
936
  }
763
937
  gridRows.push(h('div', { key: 'w' + ci, style: { display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '3px' } }, week))
@@ -897,23 +1071,29 @@ window.__ModuleLoader__.load({
897
1071
  var tabPair = useState('overview')
898
1072
  var tab = tabPair[0]
899
1073
  var setTab = tabPair[1]
1074
+ // 刷新 nonce:每次打开面板 / 点 ⟳ 时递增,驱动各页签重拉数据
1075
+ var noncePair = useState(0)
1076
+ var nonce = noncePair[0]
1077
+ var setNonce = noncePair[1]
900
1078
  var g = controller.geom()
901
1079
  useEffect(function () { return controller.subscribe(tick[1]) }, [])
902
1080
  if (!panelOpen && !panelClosing) return null
903
1081
  var body
904
- if (tab === 'overview') body = h(OverviewTab)
1082
+ if (tab === 'overview') body = h(OverviewTab, { nonce: nonce })
905
1083
  else if (tab === 'logs') body = h(LogsTab)
906
1084
  else if (tab === 'notes') body = h(NotesTab)
907
1085
  else if (tab === 'reflections') body = h(ReflectionsTab)
908
1086
  else if (tab === 'connect') body = h(ConnectTab)
909
1087
  else if (tab === 'calendar') body = h(CalendarTab)
1088
+ else if (tab === 'workspaces') body = h(WorkspaceTab)
910
1089
  else body = h(SearchTab)
911
- var tabs = [['overview', t('overview')], ['logs', t('logs')], ['notes', t('notes')], ['reflections', t('reflections')], ['connect', t('connect')], ['calendar', t('calendar')], ['search', t('search')]]
1090
+ 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')]]
912
1091
  var style = {
913
1092
  left: g.left + 'px',
914
1093
  top: g.top + 'px',
915
1094
  width: g.width + 'px',
916
1095
  height: g.height + 'px',
1096
+ '--dam-scale': FONT_SCALE_VALUES[fontScale] || '1',
917
1097
  }
918
1098
  var dragMove = startPointerDrag(function (dx, dy) {
919
1099
  controller.setGeom({ left: g.left + dx, top: g.top + dy })
@@ -923,6 +1103,7 @@ window.__ModuleLoader__.load({
923
1103
  })
924
1104
  return h('div', {
925
1105
  'data-dam-panel': '',
1106
+ 'data-scale': fontScale in FONT_SCALES ? fontScale : 'md',
926
1107
  style: style,
927
1108
  'data-closing': panelClosing ? 'true' : undefined,
928
1109
  'data-dragging': dragActive ? 'true' : undefined,
@@ -934,7 +1115,7 @@ window.__ModuleLoader__.load({
934
1115
  h('strong', null, t('autoMemory')),
935
1116
  h('span', { className: 'dam-spacer' }),
936
1117
  h('button', { 'data-dam-btn': '', title: t('resetPos'), onClick: function () { controller.resetGeom() } }, '⤾'),
937
- h('button', { 'data-dam-btn': '', title: t('refresh'), onClick: function () { tick[1]() } }, '⟳'),
1118
+ h('button', { 'data-dam-btn': '', title: t('refresh'), onClick: function () { setNonce(nonce + 1) } }, '⟳'),
938
1119
  h('button', { 'data-dam-btn': '', title: t('close'), onClick: function () { controller.close() } }, '✕')),
939
1120
  h('div', { 'data-dam-tabs': '' }, tabs.map(function (t) {
940
1121
  return h('button', { key: t[0], 'data-dam-tab': '', 'data-active': tab === t[0] ? 'true' : undefined, onClick: function () { setTab(t[0]) } }, t[1])
@@ -943,10 +1124,70 @@ window.__ModuleLoader__.load({
943
1124
  h('div', { 'data-dam-resize': '', title: t('dragResize'), onPointerDown: resizeMove }))
944
1125
  }
945
1126
 
1127
+ // ───────────────────────── 调试中心(为提 issue 提供诊断) ─────────────────────────
1128
+ function DebugCenter() {
1129
+ var dataPair = useState(null)
1130
+ var data = dataPair[0]
1131
+ var setData = dataPair[1]
1132
+ var probesPair = useState(null)
1133
+ var probes = probesPair[0]
1134
+ var setProbes = probesPair[1]
1135
+ var busyPair = useState(false)
1136
+ var busy = busyPair[0]
1137
+ var setBusy = busyPair[1]
1138
+ function refresh() {
1139
+ if (busy) return
1140
+ setBusy(true)
1141
+ var probesDone = {}
1142
+ var probeList = [
1143
+ ['state', API.state, 'GET'], ['config', API.config, 'GET'], ['list', API.list, 'GET'],
1144
+ ['file', API.file, 'GET'], ['calendar', API.calendar, 'GET'], ['debug', API.debug, 'GET'],
1145
+ ['recall', API.recall, 'POST'], ['summarize', API.summarize, 'POST'], ['greet', API.greet, 'POST'],
1146
+ ['workspaces', API.workspaces, 'POST'], ['reflectAuto', API.reflectAuto, 'POST'],
1147
+ ]
1148
+ Promise.all(probeList.map(function (pr) {
1149
+ var t0 = Date.now()
1150
+ return fetch(pr[1], pr[2] === 'POST' ? { method: 'POST', headers: { 'content-type': 'application/json' }, body: '{}' } : undefined)
1151
+ .then(function (r) { probesDone[pr[0]] = { status: r.status, ms: Date.now() - t0 } })
1152
+ .catch(function (e) { probesDone[pr[0]] = { error: e.message } })
1153
+ })).then(function () {
1154
+ setProbes(probesDone)
1155
+ return apiGet(API.debug)
1156
+ }).then(function (d) { setData(d); setBusy(false) }).catch(function () { setBusy(false) })
1157
+ }
1158
+ useEffect(function () { refresh() }, [])
1159
+ if (!data) return h('div', { 'data-dam-hint': '' }, t('dbgLoading'))
1160
+ function kv(label, value, warn) {
1161
+ return h('div', { 'data-dam-row': '', style: { marginBottom: '3px' } },
1162
+ h('span', { style: { flex: '0 0 140px', opacity: .7, fontSize: 'calc(11.5px * var(--dam-scale))' } }, label),
1163
+ 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)))
1164
+ }
1165
+ var hb = data.heartbeat || {}
1166
+ var hbAge = hb.exists && hb.heartbeatAt ? Math.round((Date.now() - hb.heartbeatAt) / 1000) : -1
1167
+ var apiBad = probes && Object.keys(probes).some(function (k) { return probes[k].status === 404 || probes[k].error })
1168
+ return h('div', null,
1169
+ h('div', { 'data-dam-row': '' },
1170
+ h('button', { 'data-dam-btn': '', onClick: refresh, disabled: busy }, busy ? t('dbgRefreshing') : t('dbgRefresh')),
1171
+ h('span', { 'data-dam-hint': '' }, data.host.indexPath || '')),
1172
+ kv('插件版本', data.host.version || '(读不到)', false),
1173
+ kv('PID / 启动', data.host.pid + ' / ' + (data.host.startTime ? new Date(data.host.startTime).toLocaleTimeString() : ''), false),
1174
+ kv('Host 需重启?', data.host.needsRestart ? '是(代码比进程新)' : '否', data.host.needsRestart),
1175
+ kv('轮询心跳', hb.exists ? (hbAge >= 0 ? '存活(上次 ' + hbAge + ' 秒前)' : '存活') : '无心跳文件', !hb.exists || hbAge > 360),
1176
+ kv('沉淀重试队列', data.autoConsolidate.pendingQueue + ' 条', data.autoConsolidate.pendingQueue > 0),
1177
+ kv('今日沉淀', data.autoConsolidate.stats.count + ' 条 / 最近 ' + (data.autoConsolidate.stats.lastAt ? new Date(data.autoConsolidate.stats.lastAt).toLocaleTimeString() : '无'), false),
1178
+ kv('正在沉淀中', data.autoConsolidate.consolidating ? '是' : '否', data.autoConsolidate.consolidating),
1179
+ kv('subagents', data.subagents.available ? (data.subagents.providers.join(', ') || '可用') : '不可用', !data.subagents.available),
1180
+ kv('项目笔记重复标题', data.duplicateHeadings + ' 个', data.duplicateHeadings > 0),
1181
+ kv('记忆文件', '用户 ' + (data.memoryFiles.user.exists ? fmtSize(data.memoryFiles.user.size) : '缺失') + ' · 笔记 ' + (data.memoryFiles.notes.exists ? fmtSize(data.memoryFiles.notes.size) : '缺失') + ' · 日志 ' + (data.memoryFiles.log.exists ? fmtSize(data.memoryFiles.log.size) : '缺失'), !data.memoryFiles.log.exists),
1182
+ kv('当前工作区', currentWs() || '(未取到)', false),
1183
+ kv('API 连通', probes ? Object.keys(probes).map(function (k) { return k + '=' + (probes[k].status !== undefined ? probes[k].status : probes[k].error) }).join(' ') : '…', apiBad))
1184
+ }
1185
+
946
1186
  // ───────────────────────── 设置页 ─────────────────────────
947
1187
  var STYLE_OPTIONS = [['auto', t('styleAuto')], ['life', t('styleLife')], ['professional', t('styleProfessional')]]
948
1188
  var LOCALE_OPTIONS = [['zh', t('zh')], ['en', t('en')]]
949
1189
  function SettingsPage() {
1190
+ var tickPair = useTick()
950
1191
  var cfgPair = useState(null)
951
1192
  var cfg = cfgPair[0]
952
1193
  var setCfg = cfgPair[1]
@@ -959,6 +1200,47 @@ window.__ModuleLoader__.load({
959
1200
  var errPair = useState('')
960
1201
  var err = errPair[0]
961
1202
  var setErr = errPair[1]
1203
+ var dbgOpenPair = useState(false)
1204
+ var dbgOpen = dbgOpenPair[0]
1205
+ var setDbgOpen = dbgOpenPair[1]
1206
+ var browseOpenPair = useState(false)
1207
+ var browseOpen = browseOpenPair[0]
1208
+ var setBrowseOpen = browseOpenPair[1]
1209
+ var browsePathPair = useState('')
1210
+ var browsePath = browsePathPair[0]
1211
+ var setBrowsePath = browsePathPair[1]
1212
+ var browseParentPair = useState('')
1213
+ var browseParent = browseParentPair[0]
1214
+ var setBrowseParent = browseParentPair[1]
1215
+ var browseDirsPair = useState(null)
1216
+ var browseDirs = browseDirsPair[0]
1217
+ var setBrowseDirs = browseDirsPair[1]
1218
+ function browseTo(p) {
1219
+ setBrowsePath(p)
1220
+ apiPost(API.browseDir, { path: p }).then(function (d) {
1221
+ if (d) { setBrowsePath(d.path); setBrowseParent(d.parent); setBrowseDirs(d.dirs || []) }
1222
+ }).catch(function () { setBrowseDirs([]) })
1223
+ }
1224
+ function openBrowser() {
1225
+ // 优先弹系统原生文件夹选择器(native 后端);不可用(远程/无显示)回退内嵌浏览
1226
+ apiPost(API.pickDir, {}).then(function (d) {
1227
+ if (d && d.native && d.dir) {
1228
+ set('memoryRoot', d.dir)
1229
+ setMsg(t('pickedDir') + ' ' + d.dir + ' ' + t('rememberSave'))
1230
+ } else if (d && d.native) {
1231
+ // 用户在系统对话框点了取消:保持原值,不动作
1232
+ } else {
1233
+ setMsg(t('pickerUnavailable'))
1234
+ setBrowseOpen(true)
1235
+ browseTo(cfg.memoryRoot || '')
1236
+ }
1237
+ }).catch(function () {
1238
+ setMsg(t('pickerUnavailable'))
1239
+ setBrowseOpen(true)
1240
+ browseTo(cfg.memoryRoot || '')
1241
+ })
1242
+ }
1243
+ useEffect(function () { return controller.subscribe(tickPair[1]) }, [])
962
1244
  useEffect(function () {
963
1245
  var alive = true
964
1246
  apiGet(API.config).then(function (d) { if (alive) setCfg(d.config) }).catch(function (e) { setErr(e.message) })
@@ -969,7 +1251,7 @@ window.__ModuleLoader__.load({
969
1251
  function save() {
970
1252
  if (busy) return
971
1253
  setBusy(true); setMsg(''); setErr('')
972
- apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved')); setBusy(false) })
1254
+ apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false) })
973
1255
  .catch(function (e) { setErr(e.message); setBusy(false) })
974
1256
  }
975
1257
  function field(label, control, hint) {
@@ -981,17 +1263,39 @@ window.__ModuleLoader__.load({
981
1263
  h('div', { 'data-dam-hint': '' }, t('settingsHeader')),
982
1264
  field(t('fLocale'), h('select', { 'data-dam-select': '', value: cfg.locale || 'zh', onChange: function (e) { set('locale', e.target.value); setLocale(e.target.value) } },
983
1265
  LOCALE_OPTIONS.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]) })), t('fLocaleHint')),
1266
+ 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() } },
1267
+ 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')),
984
1268
  field(t('fUserDir'), h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), t('fUserDirHint')),
985
1269
  field(t('fProjectDir'), h('input', { 'data-dam-input': '', value: cfg.projectMemoryDir, onChange: function (e) { set('projectMemoryDir', e.target.value) } }), t('fProjectDirHint')),
1270
+ field(t('fMemoryRoot'), h('div', { 'data-dam-row': '', style: { flex: 1 } },
1271
+ h('input', { 'data-dam-input': '', style: { flex: 1 }, value: cfg.memoryRoot || '', onChange: function (e) { set('memoryRoot', e.target.value) } }),
1272
+ h('button', { 'data-dam-btn': '', onClick: function () { openBrowser() } }, t('fBrowse'))), t('fMemoryRootHint')),
1273
+ 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)' } },
1274
+ h('div', { 'data-dam-row': '' },
1275
+ h('b', { style: { fontSize: 'calc(12px * var(--dam-scale))', wordBreak: 'break-all', flex: 1 } }, browsePath || '…'),
1276
+ h('button', { 'data-dam-btn': '', onClick: function () { browseTo(browseParent) }, disabled: browseParent === browsePath }, t('fUp'))),
1277
+ h('div', { style: { maxHeight: '160px', overflow: 'auto', marginTop: '4px' } },
1278
+ (browseDirs || []).map(function (d) {
1279
+ 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)
1280
+ }).concat(browseDirs && !browseDirs.length ? [h('div', { key: 'e', 'data-dam-hint': '' }, t('empty'))] : [])),
1281
+ h('div', { 'data-dam-row': '', style: { marginTop: '6px' } },
1282
+ h('button', { 'data-dam-btn': '', onClick: function () { set('memoryRoot', browsePath); setBrowseOpen(false) } }, t('fSelectDir')),
1283
+ h('button', { 'data-dam-btn': '', onClick: function () { setBrowseOpen(false) } }, t('close'))))
1284
+ : null,
986
1285
  field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
987
1286
  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')),
988
1287
  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')),
1288
+ 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')),
989
1289
  field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
990
1290
  field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
991
1291
  STYLE_OPTIONS.map(function (o) { return h('option', { key: o[0], value: o[0] }, o[1]) })), t('fStyleHint')),
992
1292
  h('div', { 'data-dam-row': '' },
993
1293
  h('button', { 'data-dam-btn': '', onClick: save, disabled: busy }, busy ? t('saving') : t('saveSettings')),
994
1294
  msg ? h('span', null, msg) : null),
1295
+ // 调试中心(折叠):模块状态一览,方便排查问题/提 issue
1296
+ 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' } },
1297
+ h('button', { 'data-dam-btn': '', onClick: function () { setDbgOpen(!dbgOpen) } }, (dbgOpen ? '▴ ' : '▾ ') + t('debugCenter')),
1298
+ dbgOpen ? h('div', { style: { marginTop: '8px' } }, h(DebugCenter)) : null),
995
1299
  err ? h('div', { 'data-dam-error': '' }, err) : null)
996
1300
  }
997
1301
 
@@ -1008,7 +1312,13 @@ window.__ModuleLoader__.load({
1008
1312
 
1009
1313
  var slots = ctx.slots
1010
1314
  if (!slots) { console.warn('[dsh-auto-memory] slots service unavailable'); return }
1315
+ sessions = ctx.sessions
1011
1316
 
1317
+ // 初始化界面字号(本地偏好,localStorage 持久化)
1318
+ try {
1319
+ var savedScale = localStorage.getItem('dsh-auto-memory.fontScale.v2')
1320
+ if (savedScale in FONT_SCALES) fontScale = savedScale
1321
+ } catch (e3) {}
1012
1322
  // 初始化界面语言(从 host 配置读取,持久化到 localStorage 以便即时生效)
1013
1323
  try {
1014
1324
  var savedLocale = localStorage.getItem('dsh-auto-memory.locale')
@@ -1039,7 +1349,7 @@ window.__ModuleLoader__.load({
1039
1349
  console.log('[dsh-auto-memory] client ready: sidebar entry + panel + settings page')
1040
1350
  }
1041
1351
 
1042
- exports.inject = ['slots']
1352
+ exports.inject = ['slots', 'sessions']
1043
1353
  exports.apply = apply
1044
1354
  return module.exports
1045
1355
  },