@a9i5k4/dsh-auto-memory 0.1.8 → 0.1.10

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