@a9i5k4/dsh-auto-memory 0.1.23 → 0.1.24
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/README.md +12 -4
- package/README.zh-CN.md +12 -4
- package/lib/client.js +481 -79
- package/lib/index.js +283 -109
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -18,6 +18,7 @@ window.__ModuleLoader__.load({
|
|
|
18
18
|
var useState = React.useState
|
|
19
19
|
var useEffect = React.useEffect
|
|
20
20
|
var useReducer = React.useReducer
|
|
21
|
+
var useRef = React.useRef
|
|
21
22
|
|
|
22
23
|
// ───────────────────────── 控制器 ─────────────────────────
|
|
23
24
|
var listeners = new Set()
|
|
@@ -143,6 +144,7 @@ window.__ModuleLoader__.load({
|
|
|
143
144
|
readFailed: '读取失败: ', ok: '正常', refreshTime: '刷新时间', notYetShort: '尚未',
|
|
144
145
|
empty: '(空)', back: '← 返回', view: '查看',
|
|
145
146
|
clickDateViewLog: '点击日期查看当日日志(append-only):',
|
|
147
|
+
userMemoryBlock: '用户记忆(用户画像 · 跨项目):', userMemoryEmpty: '(空)', userMemoryTruncated: '(内容过长,已截断显示)',
|
|
146
148
|
appended: '已追加', notesPathLabel: '项目笔记: ',
|
|
147
149
|
notesPlaceholder: '想追加到项目笔记的内容…(保存时自动带日期标题)',
|
|
148
150
|
saving: '保存中…', append: '追加',
|
|
@@ -170,8 +172,13 @@ window.__ModuleLoader__.load({
|
|
|
170
172
|
fProjectDir: '项目记忆目录', fProjectDirHint: '相对各工作区的目录名(默认 .dsh-memory)。',
|
|
171
173
|
fInject: '注入记忆上下文', fInjectHint: '每次组装提示词时自动注入 <memory_system> 块。',
|
|
172
174
|
fBudget: '注入预算(字符)', fBudgetHint: '记忆块总预算,超出部分截断。',
|
|
173
|
-
fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N
|
|
175
|
+
fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N 天的工作日志尾部。默认 1。',
|
|
176
|
+
fExtBudget: '外部记忆注入预算(字符)', fExtBudgetHint: '外部记忆来源在上下文中的注入预算。默认 1400(路径模式下影响有限)。',
|
|
177
|
+
fConsolidateMin: '自动沉淀内容门槛(字符)', fConsolidateMinHint: '本轮 user+assistant 总字符低于此值视为寒暄跳过。默认 240。',
|
|
174
178
|
fAway: '暂离阈值(分钟)', fAwayHint: '距上次活动超过该值视为暂离,回归时自动弹出记忆窗口。默认 60。',
|
|
179
|
+
fAutoConsolidate: '自动沉淀(每轮对话结束AI评估)', fAutoConsolidateHint: '关闭后每轮对话结束不再自动调用 AI 评估与写入今日日志。',
|
|
180
|
+
fConsolidate: '自动沉淀间隔(分钟)', fConsolidateHint: '两轮自动沉淀之间的最短间隔。默认 30;非工作时间(22:00-08:00)自动翻倍,避免短时间耗尽每日额度。',
|
|
181
|
+
fConsolidateMax: '自动沉淀每日额度(次)', fConsolidateMaxHint: '每天最多触发自动沉淀的次数,到点后当天不再调用。默认 8。',
|
|
175
182
|
fAutoSum: '自动总结时间点(HH:MM,逗号分隔)', fAutoSumHint: '到点自动生成本时段总结并弹窗展示,如 12:00,18:00,22:00。空=关闭。',
|
|
176
183
|
awayTitle: '欢迎回来', awayMsg: '你离开的这段时间,我已经帮你把日志整理好了。记忆窗口已打开,可以看看这段时间的状况。',
|
|
177
184
|
sumTitle: '时段总结',
|
|
@@ -237,6 +244,7 @@ window.__ModuleLoader__.load({
|
|
|
237
244
|
readFailed: 'Read failed: ', ok: 'OK', refreshTime: 'Refreshed', notYetShort: 'never',
|
|
238
245
|
empty: '(empty)', back: '← Back', view: 'View',
|
|
239
246
|
clickDateViewLog: 'Click a date to view the daily log (append-only):',
|
|
247
|
+
userMemoryBlock: 'User memory (profile · cross-project):', userMemoryEmpty: '(empty)', userMemoryTruncated: '(content truncated for display)',
|
|
240
248
|
appended: 'Appended', notesPathLabel: 'Project notes: ',
|
|
241
249
|
notesPlaceholder: 'Content to append to project notes…(date heading added on save)',
|
|
242
250
|
saving: 'Saving…', append: 'Append',
|
|
@@ -264,8 +272,13 @@ window.__ModuleLoader__.load({
|
|
|
264
272
|
fProjectDir: 'Project memory dir', fProjectDirHint: 'Directory name relative to each workspace (default .dsh-memory).',
|
|
265
273
|
fInject: 'Inject memory context', fInjectHint: 'Auto-inject <memory_system> block into every prompt.',
|
|
266
274
|
fBudget: 'Injection budget (chars)', fBudgetHint: 'Total budget for the memory block; excess is truncated.',
|
|
267
|
-
fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start.',
|
|
275
|
+
fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start. Default 1.',
|
|
276
|
+
fExtBudget: 'External memory injection budget (chars)', fExtBudgetHint: 'Budget for external memory sources in context. Default 1400 (limited effect with path mode).',
|
|
277
|
+
fConsolidateMin: 'Auto-consolidation content threshold (chars)', fConsolidateMinHint: 'Turns with fewer combined user+assistant chars are treated as chit-chat and skipped. Default 240.',
|
|
268
278
|
fAway: 'Away threshold (minutes)', fAwayHint: 'Marked away when inactive longer than this; the memory panel auto-opens on return. Default 60.',
|
|
279
|
+
fAutoConsolidate: 'Auto-consolidation (AI review each turn end)', fAutoConsolidateHint: 'When off, no automatic AI review or daily-log writes at turn end.',
|
|
280
|
+
fConsolidate: 'Auto-consolidate interval (minutes)', fConsolidateHint: 'Minimum gap between auto-consolidations. Default 30; doubled automatically outside work hours (22:00-08:00).',
|
|
281
|
+
fConsolidateMax: 'Daily auto-consolidate quota', fConsolidateMaxHint: 'Max auto-consolidation runs per day; no more runs after the quota is reached. Default 8.',
|
|
269
282
|
fAutoSum: 'Auto summary times (HH:MM, comma-separated)', fAutoSumHint: 'Auto-generate and show a period summary at these times, e.g. 12:00,18:00,22:00. Empty = off.',
|
|
270
283
|
awayTitle: 'Welcome back', awayMsg: 'While you were away, I tidied up your logs. The memory panel is open — take a look at what happened.',
|
|
271
284
|
sumTitle: 'Period summary',
|
|
@@ -308,6 +321,15 @@ window.__ModuleLoader__.load({
|
|
|
308
321
|
var fontScale = 'lg'
|
|
309
322
|
var FONT_SCALES = { sm: '小', md: '标准', lg: '大', xl: '特大' }
|
|
310
323
|
var FONT_SCALE_VALUES = { sm: '0.9', md: '1', lg: '1.15', xl: '1.3' }
|
|
324
|
+
var accentTheme = 'deepseek'
|
|
325
|
+
var graphDensity = 'relaxed'
|
|
326
|
+
var ACCENT_VALUES = { deepseek: '#1d4ed8', graphite: '#8b949e', violet: '#9b8cff' }
|
|
327
|
+
try {
|
|
328
|
+
var savedAccent = localStorage.getItem('dsh-auto-memory.accentTheme.v1')
|
|
329
|
+
var savedDensity = localStorage.getItem('dsh-auto-memory.graphDensity.v1')
|
|
330
|
+
if (ACCENT_VALUES[savedAccent]) accentTheme = savedAccent
|
|
331
|
+
if (savedDensity === 'relaxed' || savedDensity === 'compact') graphDensity = savedDensity
|
|
332
|
+
} catch (e) {}
|
|
311
333
|
var localeListeners = new Set()
|
|
312
334
|
function t(key) { return (I18N[locale] && I18N[locale][key]) || I18N.zh[key] || key }
|
|
313
335
|
function setLocale(l) { if (l !== 'zh' && l !== 'en') return; if (l === locale) return; locale = l; localeListeners.forEach(function (fn) { try { fn(l) } catch (e) {} }) }
|
|
@@ -324,6 +346,19 @@ window.__ModuleLoader__.load({
|
|
|
324
346
|
|
|
325
347
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
326
348
|
var CHANGELOG = {
|
|
349
|
+
'0.1.24': { zh: [
|
|
350
|
+
'记忆面板:全新液态玻璃 UI——模块箭头滚动、抽屉丝滑展开/收起、工作区 AI 思维导图(拖动画布+缩放)、日历当天时间轴(07:00-22:00)+地点/提醒字段。',
|
|
351
|
+
'注入精简:只注入最近 1 天日志与反思精华,外部记忆改为绝对路径按需读取,新增 memory_read 工具;敏感段落(令牌/密钥)不再注入 prompt。',
|
|
352
|
+
'接续页:按来源查看内容/接入/移除(笔记与用户级独立),已接入来源显示 ✓;不再整段注入外部记忆。',
|
|
353
|
+
'设置页:全部参数可调(注入预算/天数/外部预算、自动沉淀开关/门槛/间隔/额度),浮动保存栏未保存时高亮。',
|
|
354
|
+
'稳定性:修复对话结束时 host 卡死/崩溃(惰性投影移除、超时兜底、延迟启动),自动沉淀恢复正常并每日限频。',
|
|
355
|
+
], en: [
|
|
356
|
+
'Memory panel: brand-new liquid-glass UI — arrow tab scrolling, smooth drawer transitions, AI workspace mind map (pan + zoom), day timeline (07:00-22:00) with location/reminder fields.',
|
|
357
|
+
'Lean injection: only the last day of logs and a reflection digest; external memory is read by absolute path on demand via the new memory_read tool; credentials/tokens are filtered out of the prompt.',
|
|
358
|
+
'Connect tab: per-source view/import/remove (notes vs user-level independently), imported sources show ✓; external memory is no longer injected in bulk.',
|
|
359
|
+
'Settings: every parameter is now adjustable (injection budget/days/external budget, auto-consolidation toggle/threshold/interval/quota) with a floating save bar that highlights unsaved changes.',
|
|
360
|
+
'Stability: fixed host freezes/crashes at turn end (lazy projection removed, timeout fallbacks, delayed start); auto-consolidation recovered with daily rate limiting.',
|
|
361
|
+
] },
|
|
327
362
|
'0.1.23': { zh: [
|
|
328
363
|
'修复:正式发布包首次欢迎文案仍显示“开发版”;发布转换与残留校验已加强,确保开发版和正式版身份完全隔离。',
|
|
329
364
|
], en: [
|
|
@@ -468,7 +503,10 @@ window.__ModuleLoader__.load({
|
|
|
468
503
|
reflectAuto: '/api/dsh-auto-memory/reflect-auto',
|
|
469
504
|
note: '/api/dsh-auto-memory/note',
|
|
470
505
|
external: '/api/dsh-auto-memory/external',
|
|
506
|
+
externalView: '/api/dsh-auto-memory/external-view',
|
|
507
|
+
externalRemove: '/api/dsh-auto-memory/external-remove',
|
|
471
508
|
externalImport: '/api/dsh-auto-memory/external-import',
|
|
509
|
+
calendar: '/api/dsh-auto-memory/calendar',
|
|
472
510
|
summarize: '/api/dsh-auto-memory/summarize',
|
|
473
511
|
greet: '/api/dsh-auto-memory/greet',
|
|
474
512
|
notices: '/api/dsh-auto-memory/notices',
|
|
@@ -482,13 +520,13 @@ window.__ModuleLoader__.load({
|
|
|
482
520
|
async function apiGet(path, params) {
|
|
483
521
|
var res = await fetch(path + query(params))
|
|
484
522
|
var body = await res.json().catch(function () { return {} })
|
|
485
|
-
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
523
|
+
if (!res.ok) throw new Error(body.error || ('GET ' + path + ' → HTTP ' + res.status))
|
|
486
524
|
return body
|
|
487
525
|
}
|
|
488
526
|
async function apiPost(path, payload) {
|
|
489
527
|
var res = await fetch(path, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(payload) })
|
|
490
528
|
var body = await res.json().catch(function () { return {} })
|
|
491
|
-
if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status))
|
|
529
|
+
if (!res.ok) throw new Error(body.error || ('POST ' + path + ' → HTTP ' + res.status))
|
|
492
530
|
return body
|
|
493
531
|
}
|
|
494
532
|
|
|
@@ -526,16 +564,61 @@ window.__ModuleLoader__.load({
|
|
|
526
564
|
'[data-dam-resize]:hover { opacity: 1; }',
|
|
527
565
|
'[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; }',
|
|
528
566
|
'[data-dam-btn]:hover { opacity: 1; background: color-mix(in srgb, var(--dsw-alias-label-secondary, #666) 16%, transparent); }',
|
|
529
|
-
'[data-dam-tabs] { display: flex;
|
|
530
|
-
'[data-dam-
|
|
531
|
-
'[data-dam-
|
|
532
|
-
'[data-dam-
|
|
567
|
+
'[data-dam-tabs-wrap] { display: flex; align-items: center; min-width: 0; border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.2)) 55%, transparent); }',
|
|
568
|
+
'[data-dam-tabs] { flex: 1; min-width: 0; overflow: hidden; padding: 5px 4px; scrollbar-width: none; -ms-overflow-style: none; }',
|
|
569
|
+
'[data-dam-tabs]::-webkit-scrollbar { display: none; }',
|
|
570
|
+
'[data-dam-tab-strip] { display: flex; width: max-content; transition: transform .42s cubic-bezier(.22,.8,.2,1); will-change: transform; }',
|
|
571
|
+
'[data-dam-tab] { flex: 0 0 auto; min-width: max-content; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; border: none; background: transparent; cursor: pointer; color: inherit; opacity: .68; padding: 6px 9px; border-radius: 7px; font-size: calc(12.5px * var(--dam-scale)); transition: color .25s ease, background .25s ease, opacity .25s ease, transform .25s ease; }',
|
|
572
|
+
'[data-dam-tabs-arrow] { flex: 0 0 26px; height: 30px; border: 0; background: transparent; color: inherit; cursor: pointer; opacity: .55; font-size: 18px; }',
|
|
573
|
+
'[data-dam-tabs-arrow]:hover:not(:disabled) { opacity: 1; background: color-mix(in srgb, var(--dam-accent, #2456c4) 16%, transparent); transform: scale(1.12); }',
|
|
574
|
+
'[data-dam-tabs-arrow]:active:not(:disabled) { transform: scale(.92); }',
|
|
575
|
+
'[data-dam-tab][data-active="true"] { position: relative; opacity: 1; background: var(--dam-accent, #2456c4); color: #fff; font-weight: 700; box-shadow: 0 3px 10px color-mix(in srgb, var(--dam-accent, #2456c4) 30%, transparent); transform: translateY(-1px); }',
|
|
576
|
+
'[data-dam-tab][data-active="true"]::before { content: ""; position: absolute; left: 3px; top: 7px; bottom: 7px; width: 3px; border-radius: 3px; background: rgba(255,255,255,.9); }',
|
|
577
|
+
'[data-dam-body] { flex: 1 1 auto; min-width: 0; min-height: 0; overflow: auto; padding: 16px; }',
|
|
578
|
+
'[data-dam-settings] { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 18px; min-height: 100%; }',
|
|
579
|
+
'[data-dam-settings-nav] { position: sticky; top: 0; align-self: start; display: flex; flex-direction: column; gap: 4px; padding-top: 2px; }',
|
|
580
|
+
'[data-dam-settings-nav] button { border: 0; background: transparent; color: inherit; text-align: left; padding: 8px 9px; border-radius: 8px; cursor: pointer; opacity: .58; font: inherit; font-size: calc(11.5px * var(--dam-scale)); }',
|
|
581
|
+
'[data-dam-settings-nav] button[data-active="true"] { opacity: 1; color: var(--dam-accent, var(--dsw-alias-brand-primary, #4f7cff)); background: color-mix(in srgb, var(--dam-accent, #4f7cff) 11%, transparent); font-weight: 650; }',
|
|
582
|
+
'[data-dam-settings-content] { min-width: 0; padding-bottom: 24px; }',
|
|
583
|
+
'[data-dam-settings-group] { scroll-margin-top: 12px; padding-bottom: 22px; margin-bottom: 24px; border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, #c5cbd3) 38%, transparent); }',
|
|
584
|
+
'[data-dam-settings-group] h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2328); font-size: calc(14px * var(--dam-scale)); }',
|
|
585
|
+
'[data-dam-settings-group] p { margin: 4px 0 14px; color: var(--dsw-alias-label-secondary, #68717d); font-size: calc(11.5px * var(--dam-scale)); line-height: 1.5; }',
|
|
586
|
+
'[data-dam-settings-row] { padding: 10px 0; }',
|
|
587
|
+
'[data-dam-savebar] { position: sticky; bottom: 0; z-index: 3; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 14px; margin: 6px -16px -16px; background: color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.92)) 74%, transparent); backdrop-filter: blur(16px) saturate(1.4); -webkit-backdrop-filter: blur(16px) saturate(1.4); border-top: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, #c5cbd3) 38%, transparent); border-radius: 0 0 14px 14px; transition: background .25s ease; }',
|
|
588
|
+
'[data-dam-savebar] button { opacity: .5; transition: opacity .2s ease, background .2s ease, color .2s ease; }',
|
|
589
|
+
'[data-dam-savebar] button[data-dirty="true"] { opacity: 1; background: var(--dam-accent, #1d4ed8); color: #fff; font-weight: 650; box-shadow: 0 3px 12px color-mix(in srgb, var(--dam-accent, #1d4ed8) 35%, transparent); }',
|
|
590
|
+
'[data-dam-settings-row] > [data-dam-row] { margin-bottom: 3px; }',
|
|
591
|
+
'[data-dam-graph-toolbar] { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, #c5cbd3) 38%, transparent); }',
|
|
592
|
+
'[data-dam-graph-toolbar] input[type="range"] { flex: 1; min-width: 90px; accent-color: var(--dam-accent, #6b98ff); }',
|
|
593
|
+
'[data-dam-graph] { position: relative; min-height: 420px; overflow: hidden; border: 1px solid color-mix(in srgb, var(--dsw-alias-border-l1, #c5cbd3) 42%, transparent); border-radius: 12px; background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, #f5f6f7) 42%, transparent); }',
|
|
594
|
+
'[data-dam-graph] { cursor: grab; }',
|
|
595
|
+
'[data-dam-graph][data-panning="true"] { cursor: grabbing; user-select: none; }',
|
|
596
|
+
'[data-dam-graph] svg { display: block; width: auto; max-width: none; height: auto; min-height: 520px; transform-origin: 0 0; }',
|
|
597
|
+
'[data-dam-graph-node] { cursor: pointer; }',
|
|
598
|
+
'[data-dam-graph-legend] { display: flex; gap: 12px; flex-wrap: wrap; margin: 10px 0 14px; color: var(--dsw-alias-label-secondary, #68717d); font-size: calc(11px * var(--dam-scale)); }',
|
|
599
|
+
'[data-dam-legend-dot] { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 4px; background: var(--dam-accent, #4f7cff); }',
|
|
600
|
+
'[data-dam-legend-dot="branch"] { background: #7d8793; }',
|
|
601
|
+
'[data-dam-legend-dot="leaf"] { background: #b0b7c0; }',
|
|
533
602
|
'[data-dam-kv] { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: calc(12.5px * var(--dam-scale)); }',
|
|
534
603
|
'[data-dam-kv] b { opacity: .55; font-weight: 500; }',
|
|
535
604
|
'[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));',
|
|
536
605
|
' background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 40%, transparent); }',
|
|
537
606
|
'[data-dam-card] .dam-date { font-weight: 600; margin-bottom: 4px; }',
|
|
538
607
|
'[data-dam-card] .dam-content { white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }',
|
|
608
|
+
'[data-dam-disclosure] { overflow: hidden; opacity: 0; max-height: 0; transform: translateY(-5px) scale(.985); transition: max-height .32s cubic-bezier(.22,.8,.2,1), opacity .2s ease, transform .28s cubic-bezier(.22,.8,.2,1); }',
|
|
609
|
+
'[data-dam-disclosure][data-phase="open"] { opacity: 1; max-height: 1800px; transform: translateY(0) scale(1); }',
|
|
610
|
+
'[data-dam-disclosure][data-phase="closing"] { opacity: 0; max-height: 0; transform: translateY(-5px) scale(.985); }',
|
|
611
|
+
'[data-dam-card], [data-dam-banner] { animation: dam-rise .34s cubic-bezier(.22,.8,.2,1) both; }',
|
|
612
|
+
'@keyframes dam-rise { from { opacity: 0; transform: translateY(7px) scale(.988); } to { opacity: 1; transform: translateY(0) scale(1); } }',
|
|
613
|
+
'@media (prefers-reduced-motion: reduce) { [data-dam-tab-strip], [data-dam-tab], [data-dam-disclosure], [data-dam-card], [data-dam-banner] { transition: none !important; animation: none !important; } }',
|
|
614
|
+
'[data-dam-calendar] { animation: dam-rise .34s cubic-bezier(.22,.8,.2,1) both; }',
|
|
615
|
+
'[data-dam-calendar] [data-dam-calendar-day] { transition: transform .18s ease, border-color .2s ease, background .2s ease, box-shadow .2s ease; }',
|
|
616
|
+
'[data-dam-calendar] [data-dam-calendar-day]:hover { transform: translateY(-1px); border-color: var(--dam-accent, #1d4ed8) !important; box-shadow: 0 4px 12px rgba(0,0,0,.08); }',
|
|
617
|
+
'[data-dam-calendar-event] { animation: dam-event-in .25s cubic-bezier(.22,.8,.2,1) both; transition: transform .18s ease, filter .18s ease; }',
|
|
618
|
+
'[data-dam-calendar-event]:hover { transform: translateX(2px); filter: brightness(1.06); }',
|
|
619
|
+
'[data-dam-calendar-modal] { animation: dam-modal-in .26s cubic-bezier(.22,.8,.2,1) both; }',
|
|
620
|
+
'@keyframes dam-event-in { from { opacity: 0; transform: translateX(-5px); } to { opacity: 1; transform: none; } }',
|
|
621
|
+
'@keyframes dam-modal-in { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }',
|
|
539
622
|
'[data-dam-banner] { border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #e6a23c) 55%, transparent);',
|
|
540
623
|
' 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)); }',
|
|
541
624
|
'[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; }',
|
|
@@ -547,6 +630,9 @@ window.__ModuleLoader__.load({
|
|
|
547
630
|
'[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); }',
|
|
548
631
|
'[data-dam-error] { color: var(--dsw-alias-state-error-primary, #d64545); font-size: 12px; margin-top: 6px; white-space: pre-wrap; }',
|
|
549
632
|
'[data-dam-muted] { opacity: .55; }',
|
|
633
|
+
'[data-dam-loading] { display: flex; align-items: center; justify-content: center; gap: 9px; min-height: 96px; color: var(--dsw-alias-label-secondary, #7d8793); font-size: calc(12px * var(--dam-scale)); }',
|
|
634
|
+
'[data-dam-spinner] { width: 18px; height: 18px; border: 2px solid color-mix(in srgb, var(--dam-accent, #4f7cff) 22%, transparent); border-top-color: var(--dam-accent, #4f7cff); border-radius: 50%; animation: dam-spin .8s linear infinite; }',
|
|
635
|
+
'@keyframes dam-spin { to { transform: rotate(360deg); } }',
|
|
550
636
|
'@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {',
|
|
551
637
|
' [data-dam-panel] { background: var(--dsw-alias-bg-overlay, #ffffff); } }',
|
|
552
638
|
].join('\n')
|
|
@@ -573,7 +659,32 @@ window.__ModuleLoader__.load({
|
|
|
573
659
|
h('div', { className: 'dam-content' }, props.children))
|
|
574
660
|
}
|
|
575
661
|
|
|
576
|
-
function Loading() {
|
|
662
|
+
function Loading(props) {
|
|
663
|
+
var label = props && props.label ? props.label : t('loading')
|
|
664
|
+
return h('div', { 'data-dam-loading': '' }, h('span', { 'data-dam-spinner': '', 'aria-hidden': 'true' }), h('span', null, label))
|
|
665
|
+
}
|
|
666
|
+
function AnimatedDisclosure(props) {
|
|
667
|
+
var open = !!props.open
|
|
668
|
+
var shownPair = useState(open)
|
|
669
|
+
var shown = shownPair[0]
|
|
670
|
+
var setShown = shownPair[1]
|
|
671
|
+
var phasePair = useState(open ? 'open' : 'closed')
|
|
672
|
+
var phase = phasePair[0]
|
|
673
|
+
var setPhase = phasePair[1]
|
|
674
|
+
useEffect(function () {
|
|
675
|
+
var timer
|
|
676
|
+
if (open) {
|
|
677
|
+
setShown(true)
|
|
678
|
+
timer = setTimeout(function () { setPhase('open') }, 16)
|
|
679
|
+
} else if (shown) {
|
|
680
|
+
setPhase('closing')
|
|
681
|
+
timer = setTimeout(function () { setShown(false); setPhase('closed') }, 260)
|
|
682
|
+
}
|
|
683
|
+
return function () { if (timer) clearTimeout(timer) }
|
|
684
|
+
}, [open])
|
|
685
|
+
if (!shown) return null
|
|
686
|
+
return h('div', { 'data-dam-disclosure': '', 'data-phase': phase }, props.children)
|
|
687
|
+
}
|
|
577
688
|
|
|
578
689
|
// ───────────────────────── 侧边栏入口 ─────────────────────────
|
|
579
690
|
function SidebarButton() {
|
|
@@ -684,7 +795,7 @@ window.__ModuleLoader__.load({
|
|
|
684
795
|
h('div', { style: { fontWeight: 600, fontSize: 'calc(12.5px * var(--dam-scale))', lineHeight: 1.5 } }, (isOpen ? '▼ ' : '▶ ') + bigTitle),
|
|
685
796
|
h('div', { 'data-dam-muted': '', style: { fontSize: 'calc(11px * var(--dam-scale))', marginTop: '2px' } },
|
|
686
797
|
dr.label + ' · ' + dr.items.length + t('logEntries') + (cached && cached.at ? ' · ' + t('generatedAt') + new Date(cached.at).toLocaleTimeString() : ''))),
|
|
687
|
-
isOpen
|
|
798
|
+
h(AnimatedDisclosure, { open: isOpen }, h('div', { style: { padding: '4px 2px 2px 8px' } },
|
|
688
799
|
cached && cached.works && cached.works.length ? cached.works.map(function (w, wi) {
|
|
689
800
|
var sk = dr.key + ':w' + wi
|
|
690
801
|
var sOpen = subOpenMap[sk] !== undefined ? subOpenMap[sk] : false
|
|
@@ -702,7 +813,7 @@ window.__ModuleLoader__.load({
|
|
|
702
813
|
})) : null)
|
|
703
814
|
}) : cached && cached.failed ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summaryFailed'))
|
|
704
815
|
: cached && cached.summary ? h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, cached.summary)
|
|
705
|
-
: h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summarizing')))
|
|
816
|
+
: h('div', { 'data-dam-hint': '', style: { padding: '4px 6px' } }, t('summarizing'))))))
|
|
706
817
|
})(drawers[d])
|
|
707
818
|
}
|
|
708
819
|
// 待反思提醒
|
|
@@ -728,6 +839,9 @@ window.__ModuleLoader__.load({
|
|
|
728
839
|
var wsPair = useState(null)
|
|
729
840
|
var wsData = wsPair[0]
|
|
730
841
|
var setWsData = wsPair[1]
|
|
842
|
+
var wsOpenPair = useState(false)
|
|
843
|
+
var wsOpen = wsOpenPair[0]
|
|
844
|
+
var setWsOpen = wsOpenPair[1]
|
|
731
845
|
// 每次进入面板 / 点刷新(nonce 变化)/ 面板打开期间每 30s 自动重拉,保证自动沉淀等数据新鲜
|
|
732
846
|
useEffect(function () {
|
|
733
847
|
var alive = true
|
|
@@ -768,19 +882,12 @@ window.__ModuleLoader__.load({
|
|
|
768
882
|
return h('div', null,
|
|
769
883
|
// 今日问候卡:问候语 + 昨天时间轴 + 提醒(纯 GUI 渲染,不干扰对话流)
|
|
770
884
|
h(GreetingCard, { greeting: state.greeting, periodSummary: state.periodSummary, t: t, nonce: props && props.nonce }),
|
|
771
|
-
//
|
|
772
|
-
h('div', { style: { marginTop: '
|
|
773
|
-
h('
|
|
774
|
-
wsData && wsData.workspaces && wsData.workspaces.length
|
|
775
|
-
? wsData.workspaces.
|
|
776
|
-
|
|
777
|
-
h('div', { style: { fontWeight: 600, fontSize: 'calc(12px * var(--dam-scale))' } }, ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '')),
|
|
778
|
-
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')),
|
|
779
|
-
(ws.items && ws.items.length) ? ws.items.map(function (it, i) {
|
|
780
|
-
return h('div', { key: i, style: { fontSize: 'calc(11.5px * var(--dam-scale))', marginTop: '2px', opacity: 0.75 } }, '· ' + it)
|
|
781
|
-
}) : null)
|
|
782
|
-
})
|
|
783
|
-
: h('div', { 'data-dam-hint': '' }, t('wsGenerating'))),
|
|
885
|
+
// 跨工作区总结默认折叠,首页把空间留给问候与今日状态
|
|
886
|
+
h('div', { 'data-dam-collapsible': '', style: { marginTop: '16px' } },
|
|
887
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setWsOpen(!wsOpen) }, style: { width: '100%', textAlign: 'left', padding: '11px 12px', fontWeight: 650, borderBottom: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, #c5cbd3) 38%, transparent)' } }, (wsOpen ? '▾ ' : '▸ ') + t('wsOverview')),
|
|
888
|
+
h(AnimatedDisclosure, { open: wsOpen }, wsData && wsData.workspaces && wsData.workspaces.length
|
|
889
|
+
? h('div', { style: { paddingTop: '10px' } }, h(WorkspaceGraph, { workspaces: wsData.workspaces, graph: wsData.graph, onSelect: function () {} }))
|
|
890
|
+
: h(Loading, { label: locale === 'zh' ? '正在生成跨工作区总结…' : 'Generating workspace overview…' }))),
|
|
784
891
|
state.pendingReflection
|
|
785
892
|
? h(Banner, null, t('pendingReflection') + state.pendingReflection + t('pendingReflectionHint'))
|
|
786
893
|
: null,
|
|
@@ -822,9 +929,16 @@ window.__ModuleLoader__.load({
|
|
|
822
929
|
var contentPair = useState('')
|
|
823
930
|
var content = contentPair[0]
|
|
824
931
|
var setContent = contentPair[1]
|
|
932
|
+
var userPair = useState('')
|
|
933
|
+
var userText = userPair[0]
|
|
934
|
+
var setUserText = userPair[1]
|
|
935
|
+
var userTruncPair = useState(false)
|
|
936
|
+
var userTruncated = userTruncPair[0]
|
|
937
|
+
var setUserTruncated = userTruncPair[1]
|
|
825
938
|
useEffect(function () {
|
|
826
939
|
var alive = true
|
|
827
940
|
apiGet(API.list).then(function (d) { if (alive) setData(d) }).catch(function () {})
|
|
941
|
+
apiGet(API.state).then(function (s) { if (alive && s) { if (typeof s.userText === 'string') setUserText(s.userText); setUserTruncated(!!s.userTextTruncated) } }).catch(function () {})
|
|
828
942
|
return function () { alive = false }
|
|
829
943
|
}, [])
|
|
830
944
|
useEffect(function () {
|
|
@@ -847,6 +961,11 @@ window.__ModuleLoader__.load({
|
|
|
847
961
|
h('button', { 'data-dam-btn': '', onClick: function () { setOpen(log.date + '.md') } }, t('view'))))
|
|
848
962
|
})(data.logs[i])
|
|
849
963
|
}
|
|
964
|
+
// 三级记忆之用户级:当日日志下方展示用户画像(跨项目规则/偏好)
|
|
965
|
+
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '12px' } }, t('userMemoryBlock')))
|
|
966
|
+
rows.push(h(Card, { title: 'MEMORY.md (' + (locale === 'zh' ? '用户级' : 'user-level') + ')' },
|
|
967
|
+
h('div', { 'data-dam-content': '' }, userText || t('userMemoryEmpty')),
|
|
968
|
+
userTruncated ? h('div', { 'data-dam-hint': '' }, t('userMemoryTruncated')) : null))
|
|
850
969
|
}
|
|
851
970
|
return h('div', null, rows)
|
|
852
971
|
}
|
|
@@ -977,16 +1096,99 @@ window.__ModuleLoader__.load({
|
|
|
977
1096
|
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() } }),
|
|
978
1097
|
h('button', { 'data-dam-btn': '', onClick: search, disabled: busy }, t('searchBtn')),
|
|
979
1098
|
h('button', { 'data-dam-btn': '', onClick: smartSearch, disabled: busy }, t('smartSearch'))),
|
|
1099
|
+
busy ? h(Loading, { label: locale === 'zh' ? 'AI 正在分析记忆…' : 'AI is analyzing memory…' }) : null,
|
|
980
1100
|
smart ? h(Card, { title: t('smartAnswer') },
|
|
981
1101
|
h('div', { 'data-dam-content': '' }, smart.answer || t('empty')),
|
|
982
1102
|
smart.keywords && smart.keywords.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } }, t('keywordsLabel') + smart.keywords.join(' / ')) : null,
|
|
983
|
-
smart.hits && smart.hits.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } },
|
|
984
|
-
smart.hits.map(function (
|
|
1103
|
+
Array.isArray(smart.hits) && smart.hits.length ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } },
|
|
1104
|
+
smart.hits.map(function (hit, i) { return hit && typeof hit === 'object' ? h('div', { key: i, style: { marginTop: '3px', overflowWrap: 'anywhere' } }, '· [' + String(hit.where || '') + '] ' + String(hit.line || '')) : null })) : null)
|
|
985
1105
|
: null,
|
|
986
1106
|
result ? h(Card, { title: t('resultTitle') }, h('div', { 'data-dam-content': '' }, result)) : null)
|
|
987
1107
|
}
|
|
988
1108
|
|
|
989
1109
|
// ───────────────────────── 工作区总览页签(跨工作区全局总结) ─────────────────────────
|
|
1110
|
+
function graphKeywords(text) {
|
|
1111
|
+
return String(text || '').toLowerCase().split(/[\s,,。::;;/|()[\]{}]+/).filter(function (x) { return x.length >= 3 }).slice(0, 18)
|
|
1112
|
+
}
|
|
1113
|
+
function WorkspaceGraph(props) {
|
|
1114
|
+
var workspaces = props.workspaces || []
|
|
1115
|
+
var panPair = useState({ x: 0, y: 0 })
|
|
1116
|
+
var pan = panPair[0]
|
|
1117
|
+
var setPan = panPair[1]
|
|
1118
|
+
var dragPair = useState(false)
|
|
1119
|
+
var panning = dragPair[0]
|
|
1120
|
+
var setPanning = dragPair[1]
|
|
1121
|
+
var startPair = useState(null)
|
|
1122
|
+
var dragStart = startPair[0]
|
|
1123
|
+
var setDragStart = startPair[1]
|
|
1124
|
+
function pointerDown(ev) {
|
|
1125
|
+
if (ev.button !== 0 || (ev.target && ev.target.closest && ev.target.closest('[data-dam-graph-node]'))) return
|
|
1126
|
+
ev.preventDefault()
|
|
1127
|
+
try { if (ev.currentTarget.setPointerCapture) ev.currentTarget.setPointerCapture(ev.pointerId) } catch (e) {}
|
|
1128
|
+
setPanning(true)
|
|
1129
|
+
setDragStart({ x: ev.clientX, y: ev.clientY, px: pan.x, py: pan.y, moved: false, pointerId: ev.pointerId })
|
|
1130
|
+
}
|
|
1131
|
+
function pointerMove(ev) {
|
|
1132
|
+
if (!panning || !dragStart || ev.pointerId !== dragStart.pointerId) return
|
|
1133
|
+
var dx = ev.clientX - dragStart.x, dy = ev.clientY - dragStart.y
|
|
1134
|
+
if (!dragStart.moved && Math.abs(dx) + Math.abs(dy) < 3) return
|
|
1135
|
+
if (!dragStart.moved) { dragStart = Object.assign({}, dragStart, { moved: true }); setDragStart(dragStart) }
|
|
1136
|
+
setPan({ x: dragStart.px + dx, y: dragStart.py + dy })
|
|
1137
|
+
}
|
|
1138
|
+
function pointerUp(ev) {
|
|
1139
|
+
if (!panning || !dragStart || (ev && ev.pointerId !== dragStart.pointerId)) return
|
|
1140
|
+
try { if (ev.currentTarget.releasePointerCapture) ev.currentTarget.releasePointerCapture(dragStart.pointerId) } catch (e) {}
|
|
1141
|
+
setPanning(false); setDragStart(null)
|
|
1142
|
+
}
|
|
1143
|
+
var onSelect = props.onSelect || function () {}
|
|
1144
|
+
var graph = props.graph || {}
|
|
1145
|
+
var scale = Number(props.scale) || 1.2
|
|
1146
|
+
var compact = graphDensity === 'compact'
|
|
1147
|
+
var columns = Math.min(2, Math.max(1, workspaces.length))
|
|
1148
|
+
var rows = Math.ceil(workspaces.length / columns)
|
|
1149
|
+
var width = 920
|
|
1150
|
+
var colGap = 420
|
|
1151
|
+
var rowGap = compact ? 220 : 270
|
|
1152
|
+
var height = Math.max(420, rows * rowGap + 80)
|
|
1153
|
+
var centers = workspaces.map(function (ws, i) {
|
|
1154
|
+
var col = i % columns, row = Math.floor(i / columns)
|
|
1155
|
+
return { ws: ws, x: 160 + col * colGap, y: 72 + row * rowGap, i: i }
|
|
1156
|
+
})
|
|
1157
|
+
var edges = []
|
|
1158
|
+
;(graph.links || []).forEach(function (link) {
|
|
1159
|
+
var from = centers.find(function (c) { return c.ws.name === link.from })
|
|
1160
|
+
var to = centers.find(function (c) { return c.ws.name === link.to })
|
|
1161
|
+
if (from && to) edges.push([from, to, link.label || ''])
|
|
1162
|
+
})
|
|
1163
|
+
var children = []
|
|
1164
|
+
centers.forEach(function (c) {
|
|
1165
|
+
var topics = c.ws.graphTopics && c.ws.graphTopics.length ? c.ws.graphTopics : (c.ws.items || []).map(function (x) { return { label: x, detail: '' } })
|
|
1166
|
+
topics.slice(0, compact ? 3 : 4).forEach(function (topic, j) {
|
|
1167
|
+
var col = j % 2, row = Math.floor(j / 2)
|
|
1168
|
+
children.push({ center: c, item: topic, x: c.x - 105 + col * 210, y: c.y + 82 + row * 56, j: j })
|
|
1169
|
+
})
|
|
1170
|
+
})
|
|
1171
|
+
return h('div', { 'data-dam-graph': '', 'data-panning': panning ? 'true' : undefined, onPointerDown: pointerDown, onPointerMove: pointerMove, onPointerUp: pointerUp, onPointerCancel: pointerUp, onLostPointerCapture: pointerUp },
|
|
1172
|
+
h('svg', { viewBox: '0 0 ' + width + ' ' + height, width: Math.round(width * scale), height: Math.round(height * scale), role: 'img', 'aria-label': t('wsOverview'), style: { touchAction: 'none', userSelect: 'none' } },
|
|
1173
|
+
h('g', { transform: 'translate(' + (pan.x / scale) + ' ' + (pan.y / scale) + ')' },
|
|
1174
|
+
h('g', null, edges.map(function (e, i) {
|
|
1175
|
+
return h('g', { key: 'cross-' + i }, h('line', { x1: e[0].x, y1: e[0].y, x2: e[1].x, y2: e[1].y, stroke: 'var(--dam-accent, #4f7cff)', strokeOpacity: .35, strokeWidth: 1.5, strokeDasharray: '5 6' }), e[2] ? h('text', { x: (e[0].x + e[1].x) / 2, y: (e[0].y + e[1].y) / 2 - 4, textAnchor: 'middle', fontSize: 8, fill: 'var(--dam-accent, #4f7cff)' }, e[2].slice(0, 16)) : null)
|
|
1176
|
+
})),
|
|
1177
|
+
h('g', null, children.map(function (n, i) {
|
|
1178
|
+
return h('g', { key: 'branch-' + i },
|
|
1179
|
+
h('line', { x1: n.center.x, y1: n.center.y + 38, x2: n.x, y2: n.y - 22, stroke: '#8b949e', strokeOpacity: .48, strokeWidth: 1.2 }),
|
|
1180
|
+
h('rect', { x: n.x - (compact ? 82 : 92), y: n.y - 22, width: compact ? 164 : 184, height: 44, rx: 10, fill: 'color-mix(in srgb, var(--dsw-alias-bg-overlay, #fff) 92%, transparent)', stroke: 'color-mix(in srgb, var(--dam-accent, #1d4ed8) 42%, #8b949e)', strokeOpacity: .8 }),
|
|
1181
|
+
h('text', { x: n.x, y: n.y - 5, textAnchor: 'middle', fontSize: compact ? 9.5 : 10.5, fontWeight: 650, fill: 'currentColor' }, String(n.item && n.item.label || n.item || '').slice(0, 22)),
|
|
1182
|
+
h('text', { x: n.x, y: n.y + 10, textAnchor: 'middle', fontSize: 8, fill: '#7d8793' }, String(n.item && n.item.detail || '').slice(0, 26)))
|
|
1183
|
+
})),
|
|
1184
|
+
h('g', null, centers.map(function (c) {
|
|
1185
|
+
return h('g', { key: c.ws.path, 'data-dam-graph-node': '', onPointerDown: function (ev) { ev.stopPropagation() }, onClick: function () { onSelect(c.ws) } },
|
|
1186
|
+
h('rect', { x: c.x - 100, y: c.y - 38, width: 200, height: 76, rx: 16, fill: 'color-mix(in srgb, var(--dam-accent, #1d4ed8) 22%, var(--dsw-alias-bg-overlay, #fff))', stroke: 'var(--dam-accent, #1d4ed8)', strokeWidth: 2 }),
|
|
1187
|
+
h('text', { x: c.x, y: c.y - 5, textAnchor: 'middle', fontSize: compact ? 10.5 : 12, fontWeight: 650, fill: 'currentColor' }, h('tspan', { x: c.x, dy: 0 }, String(c.ws.name || '').slice(0, 12)), h('tspan', { x: c.x, dy: 13 }, String(c.ws.name || '').slice(12, 24))),
|
|
1188
|
+
h('text', { x: c.x, y: c.y + 15, textAnchor: 'middle', fontSize: 8, fill: '#7d8793' }, (c.ws.logCount || 0) + ' ' + t('logEntries')))
|
|
1189
|
+
}))))
|
|
1190
|
+
)
|
|
1191
|
+
}
|
|
990
1192
|
function WorkspaceTab() {
|
|
991
1193
|
var dataPair = useState(null)
|
|
992
1194
|
var data = dataPair[0]
|
|
@@ -1001,20 +1203,32 @@ window.__ModuleLoader__.load({
|
|
|
1001
1203
|
.catch(function () { setBusy(false) })
|
|
1002
1204
|
}
|
|
1003
1205
|
useEffect(function () { load(false) }, [])
|
|
1004
|
-
|
|
1206
|
+
var selectedPair = useState(null)
|
|
1207
|
+
var selected = selectedPair[0]
|
|
1208
|
+
var setSelected = selectedPair[1]
|
|
1209
|
+
var collapsedPair = useState({})
|
|
1210
|
+
var collapsedByPath = collapsedPair[0]
|
|
1211
|
+
var setCollapsedByPath = collapsedPair[1]
|
|
1212
|
+
var graphScalePair = useState(1.0)
|
|
1213
|
+
var graphScale = graphScalePair[0]
|
|
1214
|
+
var setGraphScale = graphScalePair[1]
|
|
1215
|
+
if (!data) return h('div', null, busy ? h(Loading, { label: locale === 'zh' ? 'AI 正在整理工作区…' : 'AI is mapping workspaces…' }) : h(Loading))
|
|
1216
|
+
var workspaces = data.workspaces || []
|
|
1005
1217
|
return h('div', null,
|
|
1006
1218
|
h('div', { 'data-dam-row': '' },
|
|
1007
1219
|
h('button', { 'data-dam-btn': '', onClick: function () { load(true) }, disabled: busy }, busy ? t('refreshing') : t('refresh')),
|
|
1008
1220
|
h('span', { 'data-dam-hint': '' }, data.cached ? (t('generatedAt') + new Date(data.generatedAt).toLocaleTimeString()) : '')),
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
h('div', { 'data-dam-
|
|
1017
|
-
|
|
1221
|
+
workspaces.length ? h('div', null,
|
|
1222
|
+
h('div', { 'data-dam-hint': '', style: { margin: '5px 0 8px' } }, locale === 'zh' ? '中心节点代表工作区;分支为记忆主题;虚线表示共享主题。' : 'Centers are workspaces, branches are topics, and dashed links show shared themes.'),
|
|
1223
|
+
h('div', { 'data-dam-graph-toolbar': '' }, h('span', { 'data-dam-hint': '' }, locale === 'zh' ? '思维导图大小' : 'Map size'), h('input', { type: 'range', min: '0.55', max: '1.75', step: '0.05', value: graphScale, 'aria-label': locale === 'zh' ? '调整思维导图大小' : 'Adjust map size', onChange: function (e) { setGraphScale(Number(e.target.value) || 1.0) } }), h('span', { 'data-dam-hint': '' }, Math.round(graphScale * 100) + '%'), h('button', { 'data-dam-btn': '', title: locale === 'zh' ? '重置视图' : 'Reset view', onClick: function () { setGraphScale(1.0) } }, '⌂')),
|
|
1224
|
+
h('div', { 'data-dam-hint': '' }, locale === 'zh' ? '点击工作区卡片查看摘要;拖动滑块调整图的大小。' : 'Select a workspace card for details; use the slider to resize the map.'),
|
|
1225
|
+
h(WorkspaceGraph, { workspaces: workspaces, graph: data.graph, scale: graphScale, onSelect: function (ws) { setSelected(ws); var n = Object.assign({}, collapsedByPath); n[ws.path] = false; setCollapsedByPath(n) } }),
|
|
1226
|
+
h('div', { style: { marginTop: '12px' } }, workspaces.map(function (ws) {
|
|
1227
|
+
var open = collapsedByPath[ws.path] === false
|
|
1228
|
+
return h('div', { key: ws.path, 'data-dam-card': '', style: { marginBottom: '8px', padding: '0' } },
|
|
1229
|
+
h('button', { 'data-dam-btn': '', 'aria-expanded': open ? 'true' : 'false', onClick: function () { var n = Object.assign({}, collapsedByPath); n[ws.path] = !open; setCollapsedByPath(n); setSelected(ws) }, style: { width: '100%', textAlign: 'left', padding: '10px 12px', fontWeight: 650 } }, (open ? '▾ ' : '▸ ') + ws.name + (ws.dateRange ? ' · ' + ws.dateRange : '')),
|
|
1230
|
+
h(AnimatedDisclosure, { open: open }, h('div', { style: { padding: '4px 12px 12px' } }, ws.summary ? h('div', { 'data-dam-content': '' }, ws.summary) : null, (ws.items || []).map(function (it, i) { return h('div', { key: i, style: { fontSize: 'calc(12px * var(--dam-scale))', marginTop: '4px', lineHeight: 1.55 } }, '· ' + it) }), h('div', { 'data-dam-hint': '' }, ws.path + ' · ' + (ws.logCount || 0) + t('logEntries')))))
|
|
1231
|
+
})) ) : h('div', { 'data-dam-hint': '' }, t('wsNone')))
|
|
1018
1232
|
}
|
|
1019
1233
|
|
|
1020
1234
|
// ───────────────────────── 日历页签 ─────────────────────────
|
|
@@ -1036,11 +1250,20 @@ window.__ModuleLoader__.load({
|
|
|
1036
1250
|
var draftPair = useState(null)
|
|
1037
1251
|
var draft = draftPair[0] // {date, time, quadrant, title}
|
|
1038
1252
|
var setDraft = draftPair[1]
|
|
1253
|
+
var dayViewPair = useState(null)
|
|
1254
|
+
var dayView = dayViewPair[0]
|
|
1255
|
+
var setDayView = dayViewPair[1]
|
|
1039
1256
|
var msgPair = useState('')
|
|
1040
1257
|
var msg = msgPair[0]
|
|
1041
1258
|
var setMsg = msgPair[1]
|
|
1259
|
+
var savingPair = useState(false)
|
|
1260
|
+
var saving = savingPair[0]
|
|
1261
|
+
var setSaving = savingPair[1]
|
|
1262
|
+
var calErrorPair = useState('')
|
|
1263
|
+
var calError = calErrorPair[0]
|
|
1264
|
+
var setCalError = calErrorPair[1]
|
|
1042
1265
|
function load() {
|
|
1043
|
-
apiGet(API.calendar).then(function (d) { if (d) setData(d) }).catch(function () {})
|
|
1266
|
+
apiGet(API.calendar).then(function (d) { if (d) { setData(d); setCalError('') } }).catch(function (e) { setCalError(t('failed') + e.message) })
|
|
1044
1267
|
}
|
|
1045
1268
|
useEffect(function () {
|
|
1046
1269
|
load()
|
|
@@ -1073,7 +1296,7 @@ window.__ModuleLoader__.load({
|
|
|
1073
1296
|
h('button', { 'data-dam-btn': '', onClick: function () { moveMonth(-1) } }, '◀'),
|
|
1074
1297
|
h('span', { style: { fontWeight: 700, flex: 1, textAlign: 'center' } }, month.year + ' / ' + String(month.mon).padStart(2, '0')),
|
|
1075
1298
|
h('button', { 'data-dam-btn': '', onClick: function () { moveMonth(1) } }, '▶'),
|
|
1076
|
-
h('button', { 'data-dam-btn': '', onClick: function () { setDraft({ date: today, time: '09:00', quadrant: '重要不紧急', title: '' }) } }, '+ ' + t('addItem'))))
|
|
1299
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setDraft({ date: today, time: '09:00', quadrant: '重要不紧急', title: '', location: '', reminder: '', note: '' }) } }, '+ ' + t('addItem'))))
|
|
1077
1300
|
// 图例
|
|
1078
1301
|
rows.push(h('div', { 'data-dam-row': '', style: { flexWrap: 'wrap' } },
|
|
1079
1302
|
Object.keys(QUADRANT_STYLE).map(function (q) {
|
|
@@ -1095,7 +1318,8 @@ window.__ModuleLoader__.load({
|
|
|
1095
1318
|
var dayNum = Number(cell.slice(8))
|
|
1096
1319
|
week.push(h('div', {
|
|
1097
1320
|
key: cell,
|
|
1098
|
-
|
|
1321
|
+
'data-dam-calendar-day': '',
|
|
1322
|
+
onClick: function () { setDayView(cell) },
|
|
1099
1323
|
style: {
|
|
1100
1324
|
minHeight: '64px', padding: '4px', cursor: 'pointer', borderRadius: '8px',
|
|
1101
1325
|
border: '1px solid ' + (isToday ? 'var(--dsw-alias-brand-primary, #4f7cff)' : 'color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.3)) 55%, transparent)'),
|
|
@@ -1108,6 +1332,7 @@ window.__ModuleLoader__.load({
|
|
|
1108
1332
|
var qs = QUADRANT_STYLE[en.quadrant] || QUADRANT_STYLE['未分类']
|
|
1109
1333
|
return h('div', {
|
|
1110
1334
|
key: en.time + en.title,
|
|
1335
|
+
'data-dam-calendar-event': '',
|
|
1111
1336
|
title: en.time + ' ' + en.title,
|
|
1112
1337
|
onClick: function (ev) { ev.stopPropagation(); toggleDone(en) },
|
|
1113
1338
|
style: {
|
|
@@ -1125,19 +1350,38 @@ window.__ModuleLoader__.load({
|
|
|
1125
1350
|
}
|
|
1126
1351
|
rows.push(h('div', null, gridRows))
|
|
1127
1352
|
function toggleDone(en) {
|
|
1353
|
+
if (saving) return
|
|
1354
|
+
setSaving(true); setCalError('')
|
|
1128
1355
|
apiPost(API.calendar, { action: en.done ? 'remove' : 'done', date: en.date, time: en.time, title: en.title }).then(function (d) {
|
|
1129
|
-
setMsg(d.result || ''); load()
|
|
1130
|
-
}).catch(function (e) {
|
|
1356
|
+
setMsg(d.result || ''); setSaving(false); load()
|
|
1357
|
+
}).catch(function (e) { setSaving(false); setCalError(t('failed') + e.message) })
|
|
1131
1358
|
}
|
|
1132
1359
|
function saveDraft() {
|
|
1133
|
-
if (!draft || !draft.title.trim()) {
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1360
|
+
if (!draft || !draft.title.trim()) { setCalError(t('needTitle')); return }
|
|
1361
|
+
if (saving) return
|
|
1362
|
+
setSaving(true); setCalError('')
|
|
1363
|
+
apiPost(API.calendar, { date: draft.date, time: draft.time, quadrant: draft.quadrant, title: draft.title.trim(), note: [draft.location ? (locale === 'zh' ? '地点: ' : 'Location: ') + draft.location : '', draft.reminder ? (locale === 'zh' ? '提醒: ' : 'Reminder: ') + draft.reminder : '', draft.note || ''].filter(Boolean).join(' | ') }).then(function (d) {
|
|
1364
|
+
setMsg(d.result || t('saved')); setSaving(false); setDraft(null); load()
|
|
1365
|
+
}).catch(function (e) { setSaving(false); setCalError(t('failed') + e.message) })
|
|
1366
|
+
}
|
|
1367
|
+
// 日期格先打开当天时间轴;点击某个时间槽才创建事件。
|
|
1368
|
+
if (dayView) {
|
|
1369
|
+
var timeline = []
|
|
1370
|
+
for (var hourSlot = 7; hourSlot <= 22; hourSlot++) {
|
|
1371
|
+
(function (hourValue) {
|
|
1372
|
+
var hourText = String(hourValue).padStart(2, '0') + ':00'
|
|
1373
|
+
var hourEvents = dayEntries(dayView).filter(function (en) { return String(en.time || '').slice(0, 2) === String(hourValue).padStart(2, '0') })
|
|
1374
|
+
timeline.push(h('div', { key: hourText, 'data-dam-calendar-slot': '', onClick: function () { setDayView(null); setDraft({ date: dayView, time: hourText, quadrant: '重要不紧急', title: '', location: '', reminder: '', note: '' }) } },
|
|
1375
|
+
h('div', { style: { flex: '0 0 50px', fontSize: 'calc(11px * var(--dam-scale))', opacity: .65 } }, hourText),
|
|
1376
|
+
h('div', { style: { minHeight: '34px', flex: 1, borderLeft: '2px solid color-mix(in srgb, var(--dam-accent, #1d4ed8) 24%, transparent)', paddingLeft: '9px' } }, hourEvents.length ? hourEvents.map(function (en) { var qs = QUADRANT_STYLE[en.quadrant] || QUADRANT_STYLE['未分类']; return h('div', { key: en.time + en.title, 'data-dam-calendar-event': '', onClick: function (ev) { ev.stopPropagation(); toggleDone(en) }, style: { marginBottom: '4px', padding: '4px 7px', borderRadius: 6, background: 'color-mix(in srgb, ' + qs.color + ' 18%, transparent)', color: qs.color, textDecoration: en.done ? 'line-through' : 'none' } }, (en.time || '') + ' ' + en.title + (en.note ? ' · ' + en.note : '')) }) : h('span', { 'data-dam-hint': '' }, locale === 'zh' ? '点击添加事件' : 'Click to add event'))))
|
|
1377
|
+
})(hourSlot)
|
|
1378
|
+
}
|
|
1379
|
+
rows.push(h('div', { 'data-dam-calendar-modal': '', style: { position: 'absolute', inset: 0, zIndex: 10, overflow: 'auto', borderRadius: '16px', background: 'color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.9)) 78%, transparent)', backdropFilter: 'blur(20px) saturate(1.4)', WebkitBackdropFilter: 'blur(20px) saturate(1.4)', padding: '16px' } }, h('div', { 'data-dam-row': '' }, h('div', { style: { fontWeight: 700, flex: 1 } }, dayView + ' · ' + (locale === 'zh' ? '当天安排' : 'Day schedule')), h('button', { 'data-dam-btn': '', onClick: function () { setDayView(null) } }, '✕')), timeline))
|
|
1137
1380
|
}
|
|
1138
1381
|
// 添加/编辑浮层(液态玻璃)
|
|
1139
1382
|
if (draft) {
|
|
1140
1383
|
rows.push(h('div', {
|
|
1384
|
+
'data-dam-calendar-modal': '',
|
|
1141
1385
|
style: {
|
|
1142
1386
|
position: 'absolute', inset: 0, zIndex: 10, borderRadius: '16px',
|
|
1143
1387
|
background: 'color-mix(in srgb, var(--dsw-alias-bg-overlay, rgba(255,255,255,.9)) 70%, transparent)',
|
|
@@ -1152,12 +1396,17 @@ window.__ModuleLoader__.load({
|
|
|
1152
1396
|
h('input', { 'data-dam-input': '', type: 'time', value: draft.time, onChange: function (e) { var n = Object.assign({}, draft); n.time = e.target.value || '09:00'; setDraft(n) } }),
|
|
1153
1397
|
h('select', { 'data-dam-select': '', value: draft.quadrant, onChange: function (e) { var n = Object.assign({}, draft); n.quadrant = e.target.value; setDraft(n) } },
|
|
1154
1398
|
['重要紧急', '重要不紧急', '紧急不重要', '不重要不紧急'].map(function (q) { return h('option', { key: q, value: q }, t(QUADRANT_I18N[q])) }))),
|
|
1399
|
+
h('input', { 'data-dam-input': '', placeholder: locale === 'zh' ? '地点(可选)' : 'Location (optional)', value: draft.location || '', onChange: function (e) { var n = Object.assign({}, draft); n.location = e.target.value; setDraft(n) } }),
|
|
1400
|
+
h('input', { 'data-dam-input': '', placeholder: locale === 'zh' ? '提醒内容(可选)' : 'Reminder note (optional)', value: draft.reminder || '', onChange: function (e) { var n = Object.assign({}, draft); n.reminder = e.target.value; setDraft(n) } }),
|
|
1401
|
+
h('textarea', { 'data-dam-input': '', rows: 2, placeholder: locale === 'zh' ? '补充说明(可选)' : 'Details (optional)', value: draft.note || '', onChange: function (e) { var n = Object.assign({}, draft); n.note = e.target.value; setDraft(n) } }),
|
|
1155
1402
|
h('div', { 'data-dam-row': '' },
|
|
1156
|
-
h('button', { 'data-dam-btn': '', onClick: saveDraft }, t('save')),
|
|
1157
|
-
h('button', { 'data-dam-btn': '', onClick: function () { setDraft(null) } }, t('cancel'))
|
|
1403
|
+
h('button', { 'data-dam-btn': '', onClick: saveDraft, disabled: saving }, saving ? (locale === 'zh' ? '保存中…' : 'Saving…') : t('save')),
|
|
1404
|
+
h('button', { 'data-dam-btn': '', onClick: function () { if (!saving) setDraft(null) }, disabled: saving }, t('cancel')),
|
|
1405
|
+
calError ? h('div', { 'data-dam-error': '' }, calError) : null)))
|
|
1158
1406
|
}
|
|
1159
|
-
msg
|
|
1160
|
-
|
|
1407
|
+
if (msg) rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '8px' } }, msg))
|
|
1408
|
+
if (calError && !draft) rows.push(h('div', { 'data-dam-error': '' }, calError))
|
|
1409
|
+
return h('div', { 'data-dam-calendar': '', style: { position: 'relative' } }, rows)
|
|
1161
1410
|
}
|
|
1162
1411
|
|
|
1163
1412
|
var TOOL_LABEL = { workbuddy: t('aiAssistant'), codebuddy: 'CodeBuddy', claude: 'Claude Code', codex: 'Codex', 'project-files': t('projectFiles') }
|
|
@@ -1174,10 +1423,25 @@ window.__ModuleLoader__.load({
|
|
|
1174
1423
|
var errPair = useState('')
|
|
1175
1424
|
var err = errPair[0]
|
|
1176
1425
|
var setErr = errPair[1]
|
|
1426
|
+
var expandedPair = useState(null)
|
|
1427
|
+
var expanded = expandedPair[0]
|
|
1428
|
+
var setExpanded = expandedPair[1]
|
|
1429
|
+
var viewCachePair = useState({})
|
|
1430
|
+
var viewCache = viewCachePair[0]
|
|
1431
|
+
var setViewCache = viewCachePair[1]
|
|
1432
|
+
var removeArmPair = useState('')
|
|
1433
|
+
var removeArm = removeArmPair[0]
|
|
1434
|
+
var setRemoveArm = removeArmPair[1]
|
|
1435
|
+
var hitsCachePair = useState({})
|
|
1436
|
+
var hitsCache = hitsCachePair[0]
|
|
1437
|
+
var setHitsCache = hitsCachePair[1]
|
|
1438
|
+
var hitsBusyForPair = useState(null)
|
|
1439
|
+
var hitsBusyFor = hitsBusyForPair[0]
|
|
1440
|
+
var setHitsBusyFor = hitsBusyForPair[1]
|
|
1177
1441
|
function load() {
|
|
1178
1442
|
apiGet(API.external).then(function (d) { setSources(d.sources || []) }).catch(function (e) { setErr(e.message) })
|
|
1179
1443
|
}
|
|
1180
|
-
useEffect(function () { load()
|
|
1444
|
+
useEffect(function () { load() }, [])
|
|
1181
1445
|
if (!sources) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
1182
1446
|
function doImport(source, target) {
|
|
1183
1447
|
var next = Object.assign({}, busy); next[source] = true
|
|
@@ -1185,8 +1449,44 @@ window.__ModuleLoader__.load({
|
|
|
1185
1449
|
apiPost(API.externalImport, { source: source, target: target }).then(function (d) {
|
|
1186
1450
|
var done = Object.assign({}, busy); done[source] = false
|
|
1187
1451
|
setBusy(done); setMsg(d.result || t('imported'))
|
|
1452
|
+
setSources(function (prev) { return (prev || []).map(function (x) { return x.id === source ? Object.assign({}, x, target === 'user' ? { importedUser: true } : { importedNotes: true }) : x }) })
|
|
1453
|
+
load()
|
|
1188
1454
|
}).catch(function (e) { var done = Object.assign({}, busy); done[source] = false; setBusy(done); setErr(e.message) })
|
|
1189
1455
|
}
|
|
1456
|
+
function toggleView(source) {
|
|
1457
|
+
if (expanded === source.id) { setExpanded(null); return }
|
|
1458
|
+
setExpanded(source.id); setRemoveArm('')
|
|
1459
|
+
if (viewCache[source.id] !== undefined) return
|
|
1460
|
+
var n = Object.assign({}, viewCache); n[source.id] = { source: source, loading: true }
|
|
1461
|
+
setViewCache(n)
|
|
1462
|
+
apiGet(API.externalView, { source: source.id }).then(function (d) {
|
|
1463
|
+
var m = Object.assign({}, viewCache); m[source.id] = Object.assign({ source: source }, d, { loading: false })
|
|
1464
|
+
setViewCache(m)
|
|
1465
|
+
}).catch(function (e) {
|
|
1466
|
+
var m = Object.assign({}, viewCache); m[source.id] = { source: source, loading: false, error: e.message }
|
|
1467
|
+
setViewCache(m)
|
|
1468
|
+
})
|
|
1469
|
+
}
|
|
1470
|
+
function removeImported(source, target) {
|
|
1471
|
+
var key = source.id + ':' + (target || '')
|
|
1472
|
+
if (removeArm !== key) { setRemoveArm(key); setMsg(''); return }
|
|
1473
|
+
setRemoveArm('')
|
|
1474
|
+
apiPost(API.externalRemove, { source: source.id, target: target }).then(function (d) {
|
|
1475
|
+
setMsg(d.result || ''); setExpanded(null); load()
|
|
1476
|
+
setSources(function (prev) { return (prev || []).map(function (x) { return x.id === source.id ? Object.assign({}, x, target === 'user' ? { importedUser: false } : target === 'project' ? { importedNotes: false } : { importedUser: false, importedNotes: false }) : x }) })
|
|
1477
|
+
}).catch(function (e) { setErr(t('failed') + e.message) })
|
|
1478
|
+
}
|
|
1479
|
+
function findImported(source) {
|
|
1480
|
+
if (!source) return
|
|
1481
|
+
setHitsBusyFor(source.id)
|
|
1482
|
+
apiPost(API.recall, { query: source.name || source.tool || '' }).then(function (d) {
|
|
1483
|
+
var n = Object.assign({}, hitsCache); n[source.id] = d && d.result ? d.result : (locale === 'zh' ? '未找到相关片段' : 'No matches')
|
|
1484
|
+
setHitsCache(n); setHitsBusyFor(null)
|
|
1485
|
+
}).catch(function (e) {
|
|
1486
|
+
var n = Object.assign({}, hitsCache); n[source.id] = t('failed') + e.message
|
|
1487
|
+
setHitsCache(n); setHitsBusyFor(null)
|
|
1488
|
+
})
|
|
1489
|
+
}
|
|
1190
1490
|
function importAll() {
|
|
1191
1491
|
var md = sources.filter(function (s) { return s.kind !== 'sessions' && s.enabled !== false })
|
|
1192
1492
|
setMsg(t('importing') + md.length + t('importingSuffix')); setErr('')
|
|
@@ -1200,17 +1500,31 @@ window.__ModuleLoader__.load({
|
|
|
1200
1500
|
}
|
|
1201
1501
|
for (var i = 0; i < sources.length; i++) {
|
|
1202
1502
|
(function (s) {
|
|
1503
|
+
var isOpen = expanded === s.id
|
|
1504
|
+
var v = viewCache[s.id]
|
|
1203
1505
|
var actions = []
|
|
1506
|
+
actions.push(h('button', { 'data-dam-btn': '', title: locale === 'zh' ? '只读查看该来源的内容' : 'Read-only view of this source', onClick: function () { toggleView(s) } }, isOpen ? (locale === 'zh' ? '收起' : 'Collapse') : (locale === 'zh' ? '查看内容' : 'View content')))
|
|
1204
1507
|
if (s.kind === 'sessions') {
|
|
1205
1508
|
actions.push(h('span', { 'data-dam-hint': '' }, t('sessionSource') + s.fileCount + t('sessionSourceSuffix')))
|
|
1206
1509
|
} else {
|
|
1207
|
-
actions.push(h('button', { 'data-dam-btn': '',
|
|
1208
|
-
actions.push(h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, '
|
|
1510
|
+
actions.push(s.importedUser ? h('button', { 'data-dam-btn': '', title: locale === 'zh' ? '从用户级记忆删除已导入段落' : 'Delete the imported section from user-level memory', onClick: function () { removeImported(s, 'user') }, style: removeArm === s.id + ':user' ? { background: 'color-mix(in srgb, var(--dsw-alias-state-error-primary, #d64545) 22%, transparent)', color: 'var(--dsw-alias-state-error-primary, #d64545)' } : undefined }, removeArm === s.id + ':user' ? (locale === 'zh' ? '确认删除?' : 'Confirm?') : (locale === 'zh' ? '从 prompt 删除' : 'Delete')) : h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'user') } }, t('importToUser')))
|
|
1511
|
+
actions.push(s.importedNotes ? h('button', { 'data-dam-btn': '', title: locale === 'zh' ? '从项目笔记删除已导入段落' : 'Delete the imported section from project notes', onClick: function () { removeImported(s, 'project') }, style: removeArm === s.id + ':project' ? { background: 'color-mix(in srgb, var(--dsw-alias-state-error-primary, #d64545) 22%, transparent)', color: 'var(--dsw-alias-state-error-primary, #d64545)' } : undefined }, removeArm === s.id + ':project' ? (locale === 'zh' ? '确认删除?' : 'Confirm?') : (locale === 'zh' ? '从 prompt 删除' : 'Delete')) : h('button', { 'data-dam-btn': '', disabled: !!busy[s.id], onClick: function () { doImport(s.id, 'project') } }, busy[s.id] ? t('importingOne') : t('importToNotes')))
|
|
1512
|
+
|
|
1209
1513
|
}
|
|
1210
|
-
cards.push(h(Card, { title: s.name + ' · ' + s.tool + (s.enabled === false ? t('disabled') : '') },
|
|
1211
|
-
h('div', { 'data-dam-
|
|
1212
|
-
|
|
1213
|
-
|
|
1514
|
+
cards.push(h(Card, { key: s.id, title: s.name + ' · ' + s.tool + (s.enabled === false ? t('disabled') : '') },
|
|
1515
|
+
h('div', { 'data-dam-row': '', style: { marginBottom: '2px' } }, actions),
|
|
1516
|
+
h(AnimatedDisclosure, { open: isOpen },
|
|
1517
|
+
v && v.loading ? h(Loading, { label: locale === 'zh' ? '正在读取来源…' : 'Reading source…' })
|
|
1518
|
+
: v && v.error ? h('div', { 'data-dam-error': '' }, v.error)
|
|
1519
|
+
: v ? h('div', null,
|
|
1520
|
+
h('div', { 'data-dam-content': '' }, v.content || t('empty')),
|
|
1521
|
+
v.imported !== undefined ? h('div', { 'data-dam-hint': '', style: { marginTop: '6px' } },
|
|
1522
|
+
(locale === 'zh' ? '已接入位置:' : 'Imported at: ') + (v.imported ? (v.locations || []).join(' ; ') : (locale === 'zh' ? '尚未接入' : 'Not imported'))) : null,
|
|
1523
|
+
h('div', { 'data-dam-row': '', style: { marginTop: '8px' } },
|
|
1524
|
+
h('button', { 'data-dam-btn': '', onClick: function () { findImported(s) } }, hitsBusyFor === s.id ? (locale === 'zh' ? '检索中…' : 'Searching…') : (locale === 'zh' ? '在记忆中查找' : 'Find in memory'))),
|
|
1525
|
+
hitsCache[s.id] ? h('div', { 'data-dam-content': '', style: { marginTop: '6px' } }, String(hitsCache[s.id])) : null,
|
|
1526
|
+
h('div', { 'data-dam-hint': '', style: { marginTop: '8px' } }, locale === 'zh' ? '提示:接入后按钮会变成「从 prompt 删除」,可单独删除用户级或项目笔记里的已导入段落;不会删除来源文件。' : 'Note: after import the button becomes "Delete" for that target; source files are never touched.'))
|
|
1527
|
+
: null)))
|
|
1214
1528
|
})(sources[i])
|
|
1215
1529
|
}
|
|
1216
1530
|
return h('div', null,
|
|
@@ -1253,6 +1567,52 @@ window.__ModuleLoader__.load({
|
|
|
1253
1567
|
}
|
|
1254
1568
|
}
|
|
1255
1569
|
|
|
1570
|
+
function TabScroller(props) {
|
|
1571
|
+
var tabs = props.tabs || []
|
|
1572
|
+
var tab = props.tab
|
|
1573
|
+
var setTab = props.setTab
|
|
1574
|
+
var viewportRef = useRef(null)
|
|
1575
|
+
var canLeftPair = useState(false)
|
|
1576
|
+
var canLeft = canLeftPair[0]
|
|
1577
|
+
var setCanLeft = canLeftPair[1]
|
|
1578
|
+
var canRightPair = useState(false)
|
|
1579
|
+
var canRight = canRightPair[0]
|
|
1580
|
+
var setCanRight = canRightPair[1]
|
|
1581
|
+
function refreshArrows() {
|
|
1582
|
+
var vp = viewportRef.current
|
|
1583
|
+
if (!vp) return
|
|
1584
|
+
setCanLeft(vp.scrollLeft > 2)
|
|
1585
|
+
setCanRight(vp.scrollLeft + vp.clientWidth < vp.scrollWidth - 2)
|
|
1586
|
+
}
|
|
1587
|
+
useEffect(function () {
|
|
1588
|
+
var vp = viewportRef.current
|
|
1589
|
+
if (!vp) return
|
|
1590
|
+
refreshArrows()
|
|
1591
|
+
vp.addEventListener('scroll', refreshArrows)
|
|
1592
|
+
var ro = null
|
|
1593
|
+
if (typeof ResizeObserver !== 'undefined') { try { ro = new ResizeObserver(refreshArrows); ro.observe(vp) } catch (e) {} }
|
|
1594
|
+
return function () { vp.removeEventListener('scroll', refreshArrows); if (ro) ro.disconnect() }
|
|
1595
|
+
}, [tabs.length])
|
|
1596
|
+
useEffect(function () {
|
|
1597
|
+
var vp = viewportRef.current
|
|
1598
|
+
if (!vp) return
|
|
1599
|
+
var activeEl = vp.querySelector('[data-active="true"]')
|
|
1600
|
+
if (!activeEl) return
|
|
1601
|
+
var l = activeEl.offsetLeft, w = activeEl.offsetWidth
|
|
1602
|
+
if (l < vp.scrollLeft) vp.scrollTo({ left: Math.max(0, l - 8), behavior: 'smooth' })
|
|
1603
|
+
else if (l + w > vp.scrollLeft + vp.clientWidth) vp.scrollTo({ left: l + w - vp.clientWidth + 8, behavior: 'smooth' })
|
|
1604
|
+
}, [tab])
|
|
1605
|
+
function step(dir) {
|
|
1606
|
+
var vp = viewportRef.current
|
|
1607
|
+
if (!vp) return
|
|
1608
|
+
try { vp.scrollBy({ left: dir * Math.max(90, vp.clientWidth * 0.6), behavior: 'smooth' }) } catch (e) { vp.scrollLeft += dir * 90 }
|
|
1609
|
+
}
|
|
1610
|
+
return h('div', { 'data-dam-tabs-wrap': '' },
|
|
1611
|
+
h('button', { 'data-dam-tabs-arrow': '', title: locale === 'zh' ? '向左滚动模块' : 'Scroll modules left', 'aria-label': locale === 'zh' ? '向左滚动模块' : 'Scroll modules left', disabled: !canLeft, onClick: function () { step(-1) } }, '‹'),
|
|
1612
|
+
h('div', { 'data-dam-tabs': '', ref: viewportRef }, h('div', { 'data-dam-tab-strip': '' }, tabs.map(function (item) { return h('button', { key: item[0], 'data-dam-tab': '', 'data-active': tab === item[0] ? 'true' : undefined, onClick: function () { setTab(item[0]) } }, item[1]) }))),
|
|
1613
|
+
h('button', { 'data-dam-tabs-arrow': '', title: locale === 'zh' ? '向右滚动模块' : 'Scroll modules right', 'aria-label': locale === 'zh' ? '向右滚动模块' : 'Scroll modules right', disabled: !canRight, onClick: function () { step(1) } }, '›'))
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1256
1616
|
function MemoryPanel() {
|
|
1257
1617
|
var tick = useTick()
|
|
1258
1618
|
var tabPair = useState('overview')
|
|
@@ -1281,6 +1641,7 @@ window.__ModuleLoader__.load({
|
|
|
1281
1641
|
width: g.width + 'px',
|
|
1282
1642
|
height: g.height + 'px',
|
|
1283
1643
|
'--dam-scale': FONT_SCALE_VALUES[fontScale] || '1',
|
|
1644
|
+
'--dam-accent': ACCENT_VALUES[accentTheme] || ACCENT_VALUES.deepseek,
|
|
1284
1645
|
}
|
|
1285
1646
|
var dragMove = startPointerDrag(function (dx, dy) {
|
|
1286
1647
|
controller.setGeom({ left: g.left + dx, top: g.top + dy })
|
|
@@ -1300,13 +1661,12 @@ window.__ModuleLoader__.load({
|
|
|
1300
1661
|
onPointerDown: dragMove,
|
|
1301
1662
|
},
|
|
1302
1663
|
h('strong', null, t('autoMemory')),
|
|
1664
|
+
h('span', { 'data-dam-hint': '', style: { fontSize: '10px', opacity: .55 } }, 'v0.1.23-ui'),
|
|
1303
1665
|
h('span', { className: 'dam-spacer' }),
|
|
1304
1666
|
h('button', { 'data-dam-btn': '', title: t('resetPos'), onClick: function () { controller.resetGeom() } }, '⤾'),
|
|
1305
1667
|
h('button', { 'data-dam-btn': '', title: t('refresh'), onClick: function () { setNonce(nonce + 1) } }, '⟳'),
|
|
1306
1668
|
h('button', { 'data-dam-btn': '', title: t('close'), onClick: function () { controller.close() } }, '✕')),
|
|
1307
|
-
h(
|
|
1308
|
-
return h('button', { key: t[0], 'data-dam-tab': '', 'data-active': tab === t[0] ? 'true' : undefined, onClick: function () { setTab(t[0]) } }, t[1])
|
|
1309
|
-
})),
|
|
1669
|
+
h(TabScroller, { tabs: tabs, tab: tab, setTab: setTab }),
|
|
1310
1670
|
h('div', { 'data-dam-body': '' }, body),
|
|
1311
1671
|
h('div', { 'data-dam-resize': '', title: t('dragResize'), onPointerDown: resizeMove }))
|
|
1312
1672
|
}
|
|
@@ -1483,9 +1843,15 @@ window.__ModuleLoader__.load({
|
|
|
1483
1843
|
var errPair = useState('')
|
|
1484
1844
|
var err = errPair[0]
|
|
1485
1845
|
var setErr = errPair[1]
|
|
1846
|
+
var dirtyPair = useState(false)
|
|
1847
|
+
var dirty = dirtyPair[0]
|
|
1848
|
+
var setDirty = dirtyPair[1]
|
|
1486
1849
|
var dbgOpenPair = useState(false)
|
|
1487
1850
|
var dbgOpen = dbgOpenPair[0]
|
|
1488
1851
|
var setDbgOpen = dbgOpenPair[1]
|
|
1852
|
+
var settingsSectionPair = useState('appearance')
|
|
1853
|
+
var settingsSection = settingsSectionPair[0]
|
|
1854
|
+
var setSettingsSection = settingsSectionPair[1]
|
|
1489
1855
|
var browseOpenPair = useState(false)
|
|
1490
1856
|
var browseOpen = browseOpenPair[0]
|
|
1491
1857
|
var setBrowseOpen = browseOpenPair[1]
|
|
@@ -1544,7 +1910,7 @@ window.__ModuleLoader__.load({
|
|
|
1544
1910
|
return function () { alive = false }
|
|
1545
1911
|
}, [])
|
|
1546
1912
|
if (!cfg) return err ? h('div', { 'data-dam-error': '' }, err) : h(Loading)
|
|
1547
|
-
function set(key, value) { var next = Object.assign({}, cfg); next[key] = value; setCfg(next) }
|
|
1913
|
+
function set(key, value) { var next = Object.assign({}, cfg); next[key] = value; setCfg(next); setDirty(true) }
|
|
1548
1914
|
function checkUpdate() {
|
|
1549
1915
|
if (checkingUpdate) return
|
|
1550
1916
|
setCheckingUpdate(true)
|
|
@@ -1563,21 +1929,51 @@ window.__ModuleLoader__.load({
|
|
|
1563
1929
|
function save() {
|
|
1564
1930
|
if (busy) return
|
|
1565
1931
|
setBusy(true); setMsg(''); setErr('')
|
|
1566
|
-
apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false); if (d && d.config && d.config.locale) applyLocalePref(d.config.locale) })
|
|
1932
|
+
apiPost(API.config, cfg).then(function (d) { setCfg(d.config); setDirty(false); setMsg(t('saved') + (d.migrated ? ' ' + d.migrated : '')); setBusy(false); if (d && d.config && d.config.locale) applyLocalePref(d.config.locale) })
|
|
1567
1933
|
.catch(function (e) { setErr(e.message); setBusy(false) })
|
|
1568
1934
|
}
|
|
1569
1935
|
function field(label, control, hint) {
|
|
1570
|
-
return h('div',
|
|
1936
|
+
return h('div', { 'data-dam-settings-row': '' },
|
|
1571
1937
|
h('div', { 'data-dam-row': '' }, h('label', null, label), control),
|
|
1572
1938
|
hint ? h('div', { 'data-dam-hint': '' }, hint) : null)
|
|
1573
1939
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1940
|
+
function setAccent(value) {
|
|
1941
|
+
accentTheme = ACCENT_VALUES[value] ? value : 'deepseek'
|
|
1942
|
+
try { localStorage.setItem('dsh-auto-memory.accentTheme.v1', accentTheme) } catch (e) {}
|
|
1943
|
+
emit()
|
|
1944
|
+
}
|
|
1945
|
+
function setDensity(value) {
|
|
1946
|
+
graphDensity = value === 'compact' ? 'compact' : 'relaxed'
|
|
1947
|
+
try { localStorage.setItem('dsh-auto-memory.graphDensity.v1', graphDensity) } catch (e) {}
|
|
1948
|
+
emit()
|
|
1949
|
+
}
|
|
1950
|
+
var sectionLabels = {
|
|
1951
|
+
appearance: locale === 'zh' ? '外观' : 'Appearance', storage: locale === 'zh' ? '存储' : 'Storage',
|
|
1952
|
+
injection: locale === 'zh' ? '记忆窗口' : 'Memory window', automation: locale === 'zh' ? '自动化' : 'Automation',
|
|
1953
|
+
maintenance: locale === 'zh' ? '维护' : 'Maintenance'
|
|
1954
|
+
}
|
|
1955
|
+
function section(key, title, content) { return h('section', { id: 'dam-settings-' + key, 'data-dam-settings-group': '' }, h('h3', null, title), content) }
|
|
1956
|
+
function jumpToSection(key) {
|
|
1957
|
+
setSettingsSection(key)
|
|
1958
|
+
try { var el = document.getElementById('dam-settings-' + key); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' }) } catch (e) {}
|
|
1959
|
+
}
|
|
1960
|
+
return h('div', { 'data-dam-settings': '' },
|
|
1961
|
+
h('nav', { 'data-dam-settings-nav': '', 'aria-label': locale === 'zh' ? '设置分组' : 'Settings sections' }, Object.keys(sectionLabels).map(function (key) {
|
|
1962
|
+
return h('button', { key: key, 'data-dam-btn': '', 'data-active': settingsSection === key ? 'true' : undefined, onClick: function () { jumpToSection(key) } }, sectionLabels[key])
|
|
1963
|
+
})),
|
|
1964
|
+
h('div', { 'data-dam-settings-content': '' },
|
|
1965
|
+
section('appearance', sectionLabels.appearance, [
|
|
1966
|
+
h('div', { 'data-dam-hint': '' }, t('settingsHeader')),
|
|
1967
|
+
field(t('fLocale'), h('select', { 'data-dam-select': '', value: cfg.locale || 'system', onChange: function (e) { set('locale', e.target.value) } },
|
|
1968
|
+
LOCALE_IDS_LIST.map(function (id) { return h('option', { key: id, value: id }, id === 'system' ? t('followSystem') : t(id)) })), t('fLocaleHint')),
|
|
1969
|
+
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() } },
|
|
1970
|
+
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')),
|
|
1971
|
+
field(locale === 'zh' ? '强调色' : 'Accent color', h('select', { 'data-dam-select': '', value: accentTheme, onChange: function (e) { setAccent(e.target.value) } },
|
|
1972
|
+
h('option', { value: 'deepseek' }, locale === 'zh' ? 'DeepSeek 蓝' : 'DeepSeek blue'), h('option', { value: 'graphite' }, locale === 'zh' ? '石墨灰' : 'Graphite'), h('option', { value: 'violet' }, locale === 'zh' ? '雾紫' : 'Violet')), locale === 'zh' ? '默认使用 DeepSeek 蓝;日历与状态颜色保持语义色。' : 'DeepSeek blue by default; calendar and status colors stay semantic.'),
|
|
1973
|
+
field(locale === 'zh' ? '关系图密度' : 'Graph density', h('select', { 'data-dam-select': '', value: graphDensity, onChange: function (e) { setDensity(e.target.value) } },
|
|
1974
|
+
h('option', { value: 'relaxed' }, locale === 'zh' ? '舒展' : 'Relaxed'), h('option', { value: 'compact' }, locale === 'zh' ? '紧凑' : 'Compact')), locale === 'zh' ? '影响工作区关系图的节点间距和显示数量。' : 'Controls node spacing and detail in the workspace graph.')
|
|
1975
|
+
]),
|
|
1976
|
+
section('storage', sectionLabels.storage, [field(t('fUserDir'), h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), t('fUserDirHint')),
|
|
1581
1977
|
field(t('fProjectDir'), h('input', { 'data-dam-input': '', value: cfg.projectMemoryDir, onChange: function (e) { set('projectMemoryDir', e.target.value) } }), t('fProjectDirHint')),
|
|
1582
1978
|
field(t('fMemoryRoot'), h('div', { 'data-dam-row': '', style: { flex: 1 } },
|
|
1583
1979
|
h('input', { 'data-dam-input': '', style: { flex: 1 }, value: cfg.memoryRoot || '', onChange: function (e) { set('memoryRoot', e.target.value) } }),
|
|
@@ -1593,30 +1989,36 @@ window.__ModuleLoader__.load({
|
|
|
1593
1989
|
h('div', { 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
1594
1990
|
h('button', { 'data-dam-btn': '', onClick: function () { set('memoryRoot', browsePath); setBrowseOpen(false) } }, t('fSelectDir')),
|
|
1595
1991
|
h('button', { 'data-dam-btn': '', onClick: function () { setBrowseOpen(false) } }, t('close'))))
|
|
1596
|
-
: null,
|
|
1597
|
-
field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
|
|
1992
|
+
: null]),
|
|
1993
|
+
section('injection', sectionLabels.injection, [field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
|
|
1598
1994
|
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')),
|
|
1599
|
-
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) ||
|
|
1995
|
+
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) || 1) } }), t('fDaysHint')),
|
|
1996
|
+
field(t('fExtBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.externalInjectionChars === undefined ? 1400 : cfg.externalInjectionChars, onChange: function (e) { set('externalInjectionChars', Number(e.target.value) || 1400) } }), t('fExtBudgetHint'))]),
|
|
1997
|
+
section('automation', sectionLabels.automation, [field(t('fConsolidateMin'), h('input', { 'data-dam-input': '', type: 'number', min: 80, value: cfg.autoConsolidateMinChars === undefined ? 240 : cfg.autoConsolidateMinChars, onChange: function (e) { set('autoConsolidateMinChars', Number(e.target.value) || 240) } }), t('fConsolidateMinHint')),
|
|
1998
|
+
field(t('fAutoConsolidate'), h('input', { type: 'checkbox', checked: cfg.autoConsolidate !== false, onChange: function (e) { set('autoConsolidate', e.target.checked) } }), t('fAutoConsolidateHint')),
|
|
1999
|
+
field(t('fConsolidate'), h('input', { 'data-dam-input': '', type: 'number', min: 5, value: cfg.autoConsolidateCooldownMinutes === undefined ? 30 : cfg.autoConsolidateCooldownMinutes, onChange: function (e) { set('autoConsolidateCooldownMinutes', Number(e.target.value) || 30) } }), t('fConsolidateHint')),
|
|
2000
|
+
field(t('fConsolidateMax'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 50, value: cfg.autoConsolidateDailyMax === undefined ? 8 : cfg.autoConsolidateDailyMax, onChange: function (e) { set('autoConsolidateDailyMax', Number(e.target.value) || 8) } }), t('fConsolidateMaxHint')),
|
|
1600
2001
|
field(t('fAway'), h('input', { 'data-dam-input': '', type: 'number', min: 1, value: cfg.awayMinutes || 60, onChange: function (e) { set('awayMinutes', Number(e.target.value) || 60) } }), t('fAwayHint')),
|
|
1601
2002
|
field(t('fAutoSum'), h('input', { 'data-dam-input': '', value: (cfg.autoSummaryTimes || []).join(','), onChange: function (e) { set('autoSummaryTimes', String(e.target.value || '').split(',').map(function (s) { return s.trim() }).filter(Boolean)) } }), t('fAutoSumHint')),
|
|
1602
2003
|
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')),
|
|
1603
2004
|
field(t('fReflect'), h('input', { type: 'checkbox', checked: !!cfg.reflectEnabled, onChange: function (e) { set('reflectEnabled', e.target.checked) } }), t('fReflectHint')),
|
|
1604
2005
|
field(t('fStyle'), h('select', { 'data-dam-select': '', value: cfg.reflectStyle, onChange: function (e) { set('reflectStyle', e.target.value) } },
|
|
1605
|
-
STYLE_IDS.map(function (id) { return h('option', { key: id, value: id }, t('style' + id.charAt(0).toUpperCase() + id.slice(1))) })), t('fStyleHint')),
|
|
1606
|
-
field(t('fVersion'), h('div', { 'data-dam-row': '' },
|
|
2006
|
+
STYLE_IDS.map(function (id) { return h('option', { key: id, value: id }, t('style' + id.charAt(0).toUpperCase() + id.slice(1))) })), t('fStyleHint'))]),
|
|
2007
|
+
section('maintenance', sectionLabels.maintenance, [field(t('fVersion'), h('div', { 'data-dam-row': '' },
|
|
1607
2008
|
h('span', { style: { flex: 1 } }, verInfo ? (verInfo.current || '?') + (verInfo.latest ? ' → ' + verInfo.latest + (verInfo.upToDate ? ' ' + t('upToDate') : ' ' + t('hasUpdate')) : '') : (checkingUpdate ? t('checking') : '—')),
|
|
1608
2009
|
h('button', { 'data-dam-btn': '', onClick: checkUpdate, disabled: checkingUpdate }, checkingUpdate ? t('checking') : t('checkUpdate')),
|
|
1609
2010
|
(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,
|
|
1610
2011
|
upMsg ? h('span', { 'data-dam-hint': '' }, upMsg) : null),
|
|
1611
|
-
t('versionCmdHint') + (verInfo && verInfo.error ? ' ' + t('versionError') + verInfo.error : '') + (verInfo && verInfo.installKind === 'dev-link' ? ' ' + t('devLinkHint') : '') + (verInfo && !verInfo.installKind ? ' ' + t('noProfileHint') : '')),
|
|
1612
|
-
h('div', { 'data-dam-
|
|
1613
|
-
h('button', { 'data-dam-btn': '', onClick: save, disabled: busy }, busy ? t('saving') : t('saveSettings')),
|
|
1614
|
-
|
|
2012
|
+
t('versionCmdHint') + (verInfo && verInfo.error ? ' ' + t('versionError') + verInfo.error : '') + (verInfo && verInfo.installKind === 'dev-link' ? ' ' + t('devLinkHint') : '') + (verInfo && !verInfo.installKind ? ' ' + t('noProfileHint') : ''))]),
|
|
2013
|
+
h('div', { 'data-dam-savebar': '' },
|
|
2014
|
+
h('button', { 'data-dam-btn': '', 'data-dirty': dirty ? 'true' : undefined, onClick: save, disabled: busy }, busy ? t('saving') : (dirty ? (locale === 'zh' ? '保存更改' : 'Save changes') : t('saveSettings'))),
|
|
2015
|
+
dirty ? h('span', { 'data-dam-hint': '' }, locale === 'zh' ? '有未保存的更改' : 'Unsaved changes') : null,
|
|
2016
|
+
msg ? h('span', { 'data-dam-hint': '' }, msg) : null),
|
|
1615
2017
|
// 调试中心(折叠):模块状态一览,方便排查问题/提 issue
|
|
1616
2018
|
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' } },
|
|
1617
2019
|
h('button', { 'data-dam-btn': '', onClick: function () { setDbgOpen(!dbgOpen) } }, (dbgOpen ? '▴ ' : '▾ ') + t('debugCenter')),
|
|
1618
2020
|
dbgOpen ? h('div', { style: { marginTop: '8px' } }, h(DebugCenter)) : null),
|
|
1619
|
-
err ? h('div', { 'data-dam-error': '' }, err) : null)
|
|
2021
|
+
err ? h('div', { 'data-dam-error': '' }, err) : null))
|
|
1620
2022
|
}
|
|
1621
2023
|
|
|
1622
2024
|
// ───────────────────────── 插件挂载 ─────────────────────────
|