dsh-layered-memory 0.6.0 → 0.7.0
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.en.md +101 -81
- package/README.md +91 -71
- package/assets/img/Hero.png +0 -0
- package/assets/img/Layers.png +0 -0
- package/assets/img/Modes.png +0 -0
- package/assets/img/ui-dark.jpg +0 -0
- package/assets/img/ui-light.jpg +0 -0
- package/assets/readme/flow.svg +189 -0
- package/assets/readme/storage.svg +115 -0
- package/dist/client.js +1242 -325
- package/dist/config.d.ts +12 -0
- package/dist/config.js +20 -17
- package/dist/hooks/capture.d.ts +17 -1
- package/dist/hooks/capture.js +45 -11
- package/dist/hooks/recall.d.ts +7 -0
- package/dist/hooks/recall.js +20 -4
- package/dist/index.d.ts +8 -0
- package/dist/index.js +112 -44
- package/dist/pipeline/rebuild.d.ts +80 -0
- package/dist/pipeline/rebuild.js +307 -0
- package/dist/pipeline/runner.d.ts +42 -4
- package/dist/pipeline/runner.js +125 -21
- package/dist/settings.js +79 -20
- package/dist/stats.d.ts +8 -1
- package/dist/stats.js +156 -14
- package/dist/store/download-queue.d.ts +71 -0
- package/dist/store/download-queue.js +313 -0
- package/dist/store/embedding-source.d.ts +160 -0
- package/dist/store/embedding-source.js +421 -0
- package/dist/store/embedding.d.ts +3 -1
- package/dist/store/embedding.js +5 -0
- package/dist/store/l0.d.ts +13 -5
- package/dist/store/l0.js +33 -6
- package/dist/store/l1.d.ts +15 -5
- package/dist/store/l1.js +40 -15
- package/dist/store/local-embedding.d.ts +64 -0
- package/dist/store/local-embedding.js +120 -0
- package/dist/store/model-catalog.d.ts +45 -0
- package/dist/store/model-catalog.js +78 -0
- package/dist/store/pending.d.ts +15 -0
- package/dist/store/pending.js +56 -0
- package/dist/store/runtime-installer.d.ts +60 -0
- package/dist/store/runtime-installer.js +181 -0
- package/dist/store/sqlite.d.ts +68 -7
- package/dist/store/sqlite.js +414 -71
- package/dist/store/state.d.ts +6 -0
- package/dist/store/state.js +11 -0
- package/dist/tools/index.js +6 -4
- package/dist/util/filelog.d.ts +2 -0
- package/dist/util/filelog.js +20 -3
- package/package.json +1 -1
- package/assets/readme/hero.svg +0 -58
package/dist/client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* dsh-layered-memory — browser half(手写 plain-JS bundle,无打包器)。
|
|
3
3
|
*
|
|
4
4
|
* 1. 输入栏(conversation.input.left,模式选择器右侧):会话记忆档位 pill
|
|
5
|
-
*
|
|
5
|
+
* (关闭/日常/工作/智能 四态,配置键 off/chat/work/auto),点击展开滑动选择器;
|
|
6
6
|
* 2. 设置 → 记忆:多 Tab 记忆浏览器(概览+开关 / L1 记忆 / L2 场景 / L3 画像 / 运行日志,
|
|
7
7
|
* 两族混合视图)。
|
|
8
8
|
* 数据通道:ctx.connection.rpc.call('/rpc', 'dsh-memory/*', payload) → Host 侧 RPC 端点。
|
|
@@ -19,108 +19,111 @@ window.__ModuleLoader__.load({
|
|
|
19
19
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
20
20
|
var react = require("react");
|
|
21
21
|
|
|
22
|
+
// ── dsh 原生 UI 组件(官方 seed 模块 @deepseek-ai/dsh-client-ui-primitives,
|
|
23
|
+
// 与宿主视觉完全一致)。宿主未注册该模块(老版本 dsh)时静默回退到
|
|
24
|
+
// 本 bundle 的等效实现——degrade-don't-crash,与插件整体降级哲学一致。 ──
|
|
25
|
+
var P = null;
|
|
26
|
+
try {
|
|
27
|
+
P = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
28
|
+
} catch (e) {
|
|
29
|
+
P = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 原生 Button(size sm)优先;回退 .dsh-mem-btn 类按钮(剥离原生专属 props)。 */
|
|
33
|
+
function NButton(props) {
|
|
34
|
+
if (P && P.Button) return react.createElement(P.Button, Object.assign({ size: "sm" }, props));
|
|
35
|
+
var rest = Object.assign({}, props);
|
|
36
|
+
delete rest.variant;
|
|
37
|
+
delete rest.icon;
|
|
38
|
+
rest.className = "dsh-mem-btn" + (rest.className ? " " + rest.className : "");
|
|
39
|
+
return react.createElement("button", rest);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 原生 Input 优先;回退 .dsh-mem-input 类输入框。
|
|
43
|
+
* 原生 Input 是 span>input 结构且 rest 摊给内层 input——布局属性(flex/minWidth
|
|
44
|
+
* 等)必须路由到外层,否则搜索框在 flex 工具栏里不再撑满。 */
|
|
45
|
+
function NInput(props) {
|
|
46
|
+
if (P && P.Input) {
|
|
47
|
+
var inner = Object.assign({}, props);
|
|
48
|
+
var layoutStyle = inner.style;
|
|
49
|
+
delete inner.style;
|
|
50
|
+
return react.createElement("span", { style: layoutStyle }, react.createElement(P.Input, inner));
|
|
51
|
+
}
|
|
52
|
+
var rest = Object.assign({}, props);
|
|
53
|
+
rest.className = "dsh-mem-input" + (rest.className ? " " + rest.className : "");
|
|
54
|
+
return react.createElement("input", rest);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 原生 Modal 优先;回退 .dsh-mem-rb-overlay/.dsh-mem-rb-modal 模态。 */
|
|
58
|
+
function NModal(props) {
|
|
59
|
+
if (props.open === false) return null;
|
|
60
|
+
if (P && P.Modal) return react.createElement(P.Modal, Object.assign({ closeLabel: "关闭" }, props));
|
|
61
|
+
return react.createElement(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
className: "dsh-mem-rb-overlay",
|
|
65
|
+
onClick: function (e) {
|
|
66
|
+
if (e.target === e.currentTarget && props.onClose) props.onClose();
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
react.createElement(
|
|
70
|
+
"div",
|
|
71
|
+
{ className: "dsh-mem-rb-modal" },
|
|
72
|
+
props.title
|
|
73
|
+
? react.createElement("div", { style: { fontSize: 15, fontWeight: 600, marginBottom: 10 } }, props.title)
|
|
74
|
+
: null,
|
|
75
|
+
props.children,
|
|
76
|
+
props.footer
|
|
77
|
+
? react.createElement(
|
|
78
|
+
"div",
|
|
79
|
+
{ style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 } },
|
|
80
|
+
props.footer,
|
|
81
|
+
)
|
|
82
|
+
: null,
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
22
87
|
var inject = ["slots", "connection"];
|
|
23
88
|
|
|
24
|
-
// ──
|
|
89
|
+
// ── 通用样式:颜色一律走 --dsh-mem-* 令牌(随 Light/Dark 主题自动切换);
|
|
90
|
+
// 视觉与交互态(hover/active/focus)在注入样式表的类里,这里只留布局 ──
|
|
25
91
|
var S = {
|
|
26
92
|
section: { padding: "0 4px" },
|
|
27
|
-
heading: { fontSize:
|
|
28
|
-
intro: { fontSize: 13, color: "var(--
|
|
29
|
-
|
|
30
|
-
display: "inline-block",
|
|
31
|
-
padding: "2px 10px",
|
|
32
|
-
borderRadius: 10,
|
|
33
|
-
fontSize: 12,
|
|
34
|
-
fontWeight: 600,
|
|
35
|
-
marginLeft: 8,
|
|
36
|
-
verticalAlign: "middle",
|
|
37
|
-
},
|
|
38
|
-
badgeOk: { background: "var(--dsw-alias-interactive-bg-primary, #1a7f37)", color: "#fff" },
|
|
39
|
-
badgeErr: { background: "var(--dsw-alias-state-error-primary, #cf222e)", color: "#fff" },
|
|
40
|
-
tabbar: { display: "flex", gap: 4, borderBottom: "1px solid var(--dsw-alias-border-secondary, #e0e0e0)", marginBottom: 12 },
|
|
41
|
-
tab: {
|
|
42
|
-
padding: "6px 14px",
|
|
43
|
-
fontSize: 13,
|
|
44
|
-
border: "none",
|
|
45
|
-
background: "none",
|
|
46
|
-
cursor: "pointer",
|
|
47
|
-
color: "var(--dsw-alias-label-secondary, #666)",
|
|
48
|
-
borderBottom: "2px solid transparent",
|
|
49
|
-
marginBottom: -1,
|
|
50
|
-
},
|
|
51
|
-
tabActive: {
|
|
52
|
-
fontWeight: 600,
|
|
53
|
-
color: "var(--dsw-alias-label-primary, #111)",
|
|
54
|
-
borderBottom: "2px solid var(--dsw-alias-interactive-bg-primary, #1a7f37)",
|
|
55
|
-
},
|
|
56
|
-
table: {
|
|
57
|
-
width: "100%",
|
|
58
|
-
borderCollapse: "collapse",
|
|
59
|
-
fontSize: 13,
|
|
60
|
-
fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace",
|
|
61
|
-
},
|
|
62
|
-
td: { padding: "6px 10px", borderBottom: "1px solid var(--dsw-alias-border-secondary, #eee)" },
|
|
63
|
-
tdKey: { width: 180, color: "var(--dsw-alias-label-tertiary, #888)", whiteSpace: "nowrap" },
|
|
93
|
+
heading: { fontSize: 16, fontWeight: 600, margin: "0 0 4px", color: "var(--dsh-mem-text-1)" },
|
|
94
|
+
intro: { fontSize: 13, color: "var(--dsh-mem-text-3)", margin: "0 0 12px" },
|
|
95
|
+
tabbar: { display: "flex", gap: 2, borderBottom: "1px solid var(--dsh-mem-border)", marginBottom: 14 },
|
|
64
96
|
error: {
|
|
65
97
|
marginTop: 10,
|
|
66
98
|
fontSize: 13,
|
|
67
|
-
color: "var(--dsh-
|
|
99
|
+
color: "var(--dsh-mem-danger)",
|
|
68
100
|
whiteSpace: "pre-wrap",
|
|
69
101
|
},
|
|
70
|
-
hint: { marginTop: 12, fontSize: 12, color: "var(--
|
|
102
|
+
hint: { marginTop: 12, fontSize: 12, color: "var(--dsh-mem-text-3)" },
|
|
71
103
|
toolbar: { display: "flex", gap: 8, marginBottom: 12, flexWrap: "wrap", alignItems: "center" },
|
|
72
|
-
input: {
|
|
73
|
-
flex: 1,
|
|
74
|
-
minWidth: 160,
|
|
75
|
-
padding: "5px 10px",
|
|
76
|
-
fontSize: 13,
|
|
77
|
-
borderRadius: 6,
|
|
78
|
-
border: "1px solid var(--dsw-alias-border-secondary, #ccc)",
|
|
79
|
-
background: "var(--dsw-alias-bg-primary, #fff)",
|
|
80
|
-
color: "var(--dsw-alias-label-primary, #111)",
|
|
81
|
-
},
|
|
82
|
-
select: { padding: "5px 6px", fontSize: 13, borderRadius: 6, border: "1px solid var(--dsw-alias-border-secondary, #ccc)" },
|
|
83
|
-
button: {
|
|
84
|
-
padding: "5px 14px",
|
|
85
|
-
fontSize: 13,
|
|
86
|
-
borderRadius: 6,
|
|
87
|
-
border: "1px solid var(--dsw-alias-border-secondary, #ccc)",
|
|
88
|
-
background: "var(--dsw-alias-bg-primary, #fff)",
|
|
89
|
-
cursor: "pointer",
|
|
90
|
-
},
|
|
91
104
|
card: {
|
|
92
|
-
border: "1px solid var(--dsw-alias-border-secondary, #e5e5e5)",
|
|
93
|
-
borderRadius: 8,
|
|
94
105
|
padding: "10px 12px",
|
|
95
106
|
marginBottom: 8,
|
|
96
107
|
fontSize: 13,
|
|
97
108
|
},
|
|
98
109
|
cardHead: { display: "flex", alignItems: "center", gap: 8, marginBottom: 4 },
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
padding: "1px 8px",
|
|
102
|
-
borderRadius: 8,
|
|
103
|
-
fontSize: 11,
|
|
104
|
-
fontWeight: 600,
|
|
105
|
-
color: "#fff",
|
|
106
|
-
whiteSpace: "nowrap",
|
|
107
|
-
},
|
|
108
|
-
muted: { color: "var(--dsw-alias-label-tertiary, #999)", fontSize: 12 },
|
|
109
|
-
content: { lineHeight: 1.5, wordBreak: "break-word" },
|
|
110
|
+
muted: { color: "var(--dsh-mem-text-3)", fontSize: 12 },
|
|
111
|
+
content: { lineHeight: 1.5, wordBreak: "break-word", color: "var(--dsh-mem-text-1)" },
|
|
110
112
|
detail: {
|
|
111
113
|
marginTop: 8,
|
|
112
114
|
paddingTop: 8,
|
|
113
|
-
borderTop: "1px dashed var(--
|
|
115
|
+
borderTop: "1px dashed var(--dsh-mem-border)",
|
|
114
116
|
fontSize: 12,
|
|
115
117
|
fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace",
|
|
116
|
-
color: "var(--
|
|
118
|
+
color: "var(--dsh-mem-text-2)",
|
|
117
119
|
whiteSpace: "pre-wrap",
|
|
118
120
|
},
|
|
119
121
|
pre: {
|
|
120
122
|
margin: 0,
|
|
121
123
|
padding: "10px 12px",
|
|
122
|
-
background: "var(--
|
|
123
|
-
|
|
124
|
+
background: "var(--dsh-mem-bg-inset)",
|
|
125
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
126
|
+
borderRadius: 10,
|
|
124
127
|
fontSize: 12,
|
|
125
128
|
fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace",
|
|
126
129
|
whiteSpace: "pre-wrap",
|
|
@@ -130,19 +133,19 @@ window.__ModuleLoader__.load({
|
|
|
130
133
|
overflow: "auto",
|
|
131
134
|
},
|
|
132
135
|
switchRow: { display: "flex", alignItems: "center", gap: 10, padding: "8px 0" },
|
|
133
|
-
switchLabel: { fontSize: 13, fontWeight: 600, minWidth: 72 },
|
|
134
|
-
switchDesc: { fontSize: 12, color: "var(--
|
|
136
|
+
switchLabel: { fontSize: 13, fontWeight: 600, minWidth: 72, color: "var(--dsh-mem-text-1)" },
|
|
137
|
+
switchDesc: { fontSize: 12, color: "var(--dsh-mem-text-3)" },
|
|
135
138
|
switch: {
|
|
136
139
|
width: 36,
|
|
137
140
|
height: 20,
|
|
138
|
-
borderRadius:
|
|
141
|
+
borderRadius: 999,
|
|
139
142
|
position: "relative",
|
|
140
143
|
cursor: "pointer",
|
|
141
144
|
transition: "background .15s",
|
|
142
145
|
flexShrink: 0,
|
|
143
146
|
},
|
|
144
|
-
switchOn: { background: "var(--
|
|
145
|
-
switchOff: { background: "var(--
|
|
147
|
+
switchOn: { background: "var(--dsh-mem-accent-fill)" },
|
|
148
|
+
switchOff: { background: "var(--dsh-mem-border-strong)" },
|
|
146
149
|
switchDisabled: { opacity: 0.4, cursor: "not-allowed" },
|
|
147
150
|
knob: {
|
|
148
151
|
position: "absolute",
|
|
@@ -150,22 +153,56 @@ window.__ModuleLoader__.load({
|
|
|
150
153
|
width: 16,
|
|
151
154
|
height: 16,
|
|
152
155
|
borderRadius: "50%",
|
|
153
|
-
background: "
|
|
156
|
+
background: "var(--dsh-mem-thumb)",
|
|
154
157
|
transition: "left .15s",
|
|
155
|
-
boxShadow: "0 1px 2px rgba(0,0,0,.
|
|
158
|
+
boxShadow: "0 1px 2px rgba(0,0,0,.25)",
|
|
156
159
|
},
|
|
157
160
|
switchPanel: {
|
|
158
|
-
border: "1px solid var(--
|
|
159
|
-
borderRadius:
|
|
160
|
-
|
|
161
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
162
|
+
borderRadius: 10,
|
|
163
|
+
background: "var(--dsh-mem-bg-card)",
|
|
164
|
+
boxShadow: "var(--dsh-mem-shadow-card)",
|
|
165
|
+
padding: "4px 14px",
|
|
161
166
|
marginBottom: 14,
|
|
162
167
|
},
|
|
168
|
+
panelLabel: {
|
|
169
|
+
fontSize: 12,
|
|
170
|
+
fontWeight: 600,
|
|
171
|
+
color: "var(--dsh-mem-text-3)",
|
|
172
|
+
margin: "12px 0 2px",
|
|
173
|
+
},
|
|
174
|
+
statGrid: {
|
|
175
|
+
display: "grid",
|
|
176
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(140px, 1fr))",
|
|
177
|
+
gap: 8,
|
|
178
|
+
marginBottom: 14,
|
|
179
|
+
},
|
|
180
|
+
statTile: { padding: "10px 12px" },
|
|
181
|
+
statNum: { fontSize: 20, fontWeight: 650, lineHeight: "28px", color: "var(--dsh-mem-text-1)" },
|
|
182
|
+
statLabel: { fontSize: 12, color: "var(--dsh-mem-text-3)", marginTop: 2 },
|
|
183
|
+
infoRow: {
|
|
184
|
+
display: "flex",
|
|
185
|
+
alignItems: "baseline",
|
|
186
|
+
gap: 12,
|
|
187
|
+
padding: "5px 0",
|
|
188
|
+
borderBottom: "1px solid var(--dsh-mem-border)",
|
|
189
|
+
},
|
|
190
|
+
infoKey: { fontSize: 12.5, color: "var(--dsh-mem-text-3)", whiteSpace: "nowrap", minWidth: 96 },
|
|
191
|
+
infoVal: {
|
|
192
|
+
fontSize: 12.5,
|
|
193
|
+
color: "var(--dsh-mem-text-1)",
|
|
194
|
+
fontFamily: "ui-monospace, SFMono-Regular, Consolas, monospace",
|
|
195
|
+
wordBreak: "break-all",
|
|
196
|
+
textAlign: "right",
|
|
197
|
+
flex: 1,
|
|
198
|
+
},
|
|
163
199
|
seg: {
|
|
164
200
|
display: "inline-flex",
|
|
165
|
-
border: "1px solid var(--
|
|
201
|
+
border: "1px solid var(--dsh-mem-border)",
|
|
166
202
|
borderRadius: 8,
|
|
167
203
|
overflow: "hidden",
|
|
168
204
|
flexShrink: 0,
|
|
205
|
+
background: "var(--dsh-mem-bg-inset)",
|
|
169
206
|
},
|
|
170
207
|
segBtn: {
|
|
171
208
|
padding: "4px 12px",
|
|
@@ -173,33 +210,21 @@ window.__ModuleLoader__.load({
|
|
|
173
210
|
lineHeight: "16px",
|
|
174
211
|
cursor: "pointer",
|
|
175
212
|
background: "transparent",
|
|
176
|
-
color: "
|
|
213
|
+
color: "var(--dsh-mem-text-2)",
|
|
177
214
|
border: "none",
|
|
178
|
-
borderRight: "1px solid var(--
|
|
215
|
+
borderRight: "1px solid var(--dsh-mem-border)",
|
|
179
216
|
},
|
|
180
217
|
segBtnOn: {
|
|
181
|
-
background: "var(--
|
|
218
|
+
background: "var(--dsh-mem-accent-fill)",
|
|
182
219
|
color: "#fff",
|
|
183
220
|
fontWeight: 600,
|
|
184
221
|
},
|
|
185
|
-
segBtnOff: {
|
|
186
|
-
background: "var(--dsw-alias-bg-secondary, #f6f8fa)",
|
|
187
|
-
},
|
|
188
222
|
flexRow: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" },
|
|
189
223
|
grow: { flex: 1 },
|
|
190
224
|
sceneHead: { display: "flex", alignItems: "baseline", gap: 10, marginBottom: 6, flexWrap: "wrap" },
|
|
191
|
-
sceneTitle: { fontSize: 13, fontWeight: 600, fontFamily: "ui-monospace, Consolas, monospace" },
|
|
225
|
+
sceneTitle: { fontSize: 13, fontWeight: 600, fontFamily: "ui-monospace, Consolas, monospace", color: "var(--dsh-mem-text-1)" },
|
|
192
226
|
};
|
|
193
227
|
|
|
194
|
-
var TYPE_COLORS = {
|
|
195
|
-
persona: "#8250df",
|
|
196
|
-
episodic: "#0969da",
|
|
197
|
-
instruction: "#bf8700",
|
|
198
|
-
work_fact: "#0969da",
|
|
199
|
-
work_task: "#1a7f37",
|
|
200
|
-
work_method: "#8250df",
|
|
201
|
-
work_artifact: "#bf8700",
|
|
202
|
-
};
|
|
203
228
|
var TYPE_LABELS = {
|
|
204
229
|
persona: "画像偏好",
|
|
205
230
|
episodic: "客观事件",
|
|
@@ -211,7 +236,7 @@ window.__ModuleLoader__.load({
|
|
|
211
236
|
};
|
|
212
237
|
|
|
213
238
|
function fmtTime(iso) {
|
|
214
|
-
if (!iso) return "
|
|
239
|
+
if (!iso) return "-";
|
|
215
240
|
try {
|
|
216
241
|
return new Date(iso).toLocaleString();
|
|
217
242
|
} catch (e) {
|
|
@@ -253,7 +278,7 @@ window.__ModuleLoader__.load({
|
|
|
253
278
|
);
|
|
254
279
|
}
|
|
255
280
|
|
|
256
|
-
// ── Segmented
|
|
281
|
+
// ── Segmented 组件:分段选择器(支持逐项禁用:远程档未配四件套时置灰) ──
|
|
257
282
|
function Segmented(props) {
|
|
258
283
|
var value = props.value;
|
|
259
284
|
var disabled = !!props.disabled;
|
|
@@ -262,19 +287,21 @@ window.__ModuleLoader__.load({
|
|
|
262
287
|
{ style: Object.assign({}, S.seg, disabled ? S.switchDisabled : null) },
|
|
263
288
|
props.options.map(function (opt, i) {
|
|
264
289
|
var on = opt.key === value;
|
|
290
|
+
var optDisabled = disabled || !!opt.disabled;
|
|
265
291
|
return react.createElement(
|
|
266
292
|
"span",
|
|
267
293
|
{
|
|
268
294
|
key: opt.key,
|
|
295
|
+
title: opt.disabledTitle || "",
|
|
269
296
|
style: Object.assign(
|
|
270
297
|
{},
|
|
271
298
|
S.segBtn,
|
|
272
|
-
on ? S.segBtnOn :
|
|
299
|
+
on ? S.segBtnOn : null,
|
|
273
300
|
i === props.options.length - 1 ? { borderRight: "none" } : null,
|
|
274
|
-
|
|
301
|
+
optDisabled ? { cursor: "not-allowed", opacity: 0.45 } : null,
|
|
275
302
|
),
|
|
276
303
|
onClick: function () {
|
|
277
|
-
if (!
|
|
304
|
+
if (!optDisabled && !on && props.onChange) props.onChange(opt.key);
|
|
278
305
|
},
|
|
279
306
|
},
|
|
280
307
|
opt.label,
|
|
@@ -283,17 +310,36 @@ window.__ModuleLoader__.load({
|
|
|
283
310
|
);
|
|
284
311
|
}
|
|
285
312
|
|
|
286
|
-
// ── 会话记忆档位控件(输入栏 pill +
|
|
287
|
-
// 档位顺序即滑轨顺序:关闭 →
|
|
313
|
+
// ── 会话记忆档位控件(输入栏 pill + 滑动选择器) ──
|
|
314
|
+
// 档位顺序即滑轨顺序:关闭 → 日常 → 工作 → 智能(默认档"智能"居右)。
|
|
315
|
+
// 显示名中文;配置键与英文层保持 off/chat/work/auto(session-modes.json 不变)。
|
|
316
|
+
// 档位色是 CSS 变量引用(--dsh-mem-mode-*,Light/Dark 各一组值,注入样式表定义),
|
|
317
|
+
// 取值 = 灰 → 品牌蓝 的渐变阶:off 灰、chat/work 过渡蓝、auto 品牌蓝。
|
|
288
318
|
var MODES = [
|
|
289
|
-
{ key: "off", label: "关闭", color: "
|
|
290
|
-
{ key: "chat", label: "
|
|
291
|
-
{ key: "work", label: "
|
|
292
|
-
{ key: "auto", label: "
|
|
319
|
+
{ key: "off", label: "关闭", color: "var(--dsh-mem-text-2)" },
|
|
320
|
+
{ key: "chat", label: "日常", color: "var(--dsh-mem-mode-chat)" },
|
|
321
|
+
{ key: "work", label: "工作", color: "var(--dsh-mem-mode-work)" },
|
|
322
|
+
{ key: "auto", label: "智能", color: "var(--dsh-mem-mode-auto)" },
|
|
293
323
|
];
|
|
294
324
|
var TRACK_W = 200;
|
|
295
325
|
var THUMB = 16;
|
|
326
|
+
var RAIL_H = 22; // 粗滑轨高度 > 圆球直径(圆球被滑轨包裹)
|
|
296
327
|
var INNER_W = TRACK_W - THUMB;
|
|
328
|
+
// 点阵粒子场档位参数(参考 DSH-Claude-Style-Reasoning-Slider 的分档场强,
|
|
329
|
+
// 配色锁品牌蓝单色系):density 越大点阵越密、alpha 亮度系数、wave 明暗水波纹、
|
|
330
|
+
// tempo 闪烁节拍倍率。tier0(关闭)不参与——show=false 整层不画
|
|
331
|
+
var FIELD_TIERS = [
|
|
332
|
+
{ density: 0, alpha: 0, wave: 0, tempo: 1 },
|
|
333
|
+
{ density: 0.34, alpha: 0.5, wave: 0, tempo: 1 }, // 日常:稀疏微光
|
|
334
|
+
{ density: 0.55, alpha: 0.78, wave: 1, tempo: 1.15 }, // 工作:中强 + 水波纹
|
|
335
|
+
{ density: 0.72, alpha: 1, wave: 1, tempo: 1.3 }, // 智能:满场最活跃
|
|
336
|
+
];
|
|
337
|
+
|
|
338
|
+
/** smoothstep(粒子场展开/揭示用 ease) */
|
|
339
|
+
function smStep(a, b, x) {
|
|
340
|
+
var t = Math.min(1, Math.max(0, (x - a) / (b - a)));
|
|
341
|
+
return t * t * (3 - 2 * t);
|
|
342
|
+
}
|
|
297
343
|
|
|
298
344
|
function modeInfo(key) {
|
|
299
345
|
for (var i = 0; i < MODES.length; i++) if (MODES[i].key === key) return MODES[i];
|
|
@@ -301,10 +347,10 @@ window.__ModuleLoader__.load({
|
|
|
301
347
|
}
|
|
302
348
|
|
|
303
349
|
function modeLabel(key) {
|
|
304
|
-
if (key === "auto") return "
|
|
305
|
-
if (key === "chat") return "
|
|
306
|
-
if (key === "work") return "
|
|
307
|
-
return "
|
|
350
|
+
if (key === "auto") return "智能(双族)";
|
|
351
|
+
if (key === "chat") return "日常(个人)";
|
|
352
|
+
if (key === "work") return "工作(团队)";
|
|
353
|
+
return "关闭";
|
|
308
354
|
}
|
|
309
355
|
|
|
310
356
|
function modeIndex(key) {
|
|
@@ -314,12 +360,15 @@ window.__ModuleLoader__.load({
|
|
|
314
360
|
|
|
315
361
|
/** 滑动选择器浮层(参考 macOS 滑动器:拖拽圆头 1:1 连续跟手,松手按动量投影吸附最近档)。 */
|
|
316
362
|
function ModeSlider(props) {
|
|
317
|
-
|
|
363
|
+
ensureThemeStyle(); // 主题令牌与浮层/气泡 class 共用同一张注入样式表
|
|
318
364
|
var trackRef = react.useRef(null);
|
|
319
365
|
// 拖拽状态:{ x: 圆头连续位置 px, lastX: 上次指针 clientX, t: 时间戳, v: 速度 px/ms(EMA 平滑) }
|
|
320
366
|
var dragState = react.useState(null);
|
|
321
367
|
var drag = dragState[0];
|
|
322
368
|
var setDrag = dragState[1];
|
|
369
|
+
// 粒子层 canvas ref + 几何快照 ref(rAF 循环跨帧读最新值,不重建 effect)
|
|
370
|
+
var canvasRef = react.useRef(null);
|
|
371
|
+
var geoRef = react.useRef(null);
|
|
323
372
|
|
|
324
373
|
var clampX = function (x) {
|
|
325
374
|
if (x < 0) return 0;
|
|
@@ -362,77 +411,180 @@ window.__ModuleLoader__.load({
|
|
|
362
411
|
var activeIdx = Math.min(MODES.length - 1, Math.max(0, Math.round((thumbLeft / INNER_W) * (MODES.length - 1))));
|
|
363
412
|
var info = MODES[activeIdx];
|
|
364
413
|
|
|
414
|
+
// 粒子层几何快照:每帧渲染时更新,rAF 循环跨帧读取(避免按帧重建 effect)
|
|
415
|
+
geoRef.current = {
|
|
416
|
+
origin: thumbLeft + THUMB / 2, // 密度/亮度中心 = 圆球中心
|
|
417
|
+
rightEdge: thumbLeft + THUMB, // 粒子活动区右界 = 填充右缘(不越过圆球)
|
|
418
|
+
tier: activeIdx, // 场强档位(与填充/气泡同源;拖拽预览即时升降级)
|
|
419
|
+
show: activeIdx > 0 || drag !== null, // 与填充显隐同源
|
|
420
|
+
dragging: drag !== null,
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// 粒子层动画循环:DPR 适配 + ResizeObserver + 主题观察;reduced-motion 只画静帧。
|
|
424
|
+
// 依赖数组为空——几何/拖拽态经 geoRef 传递,effect 全生命周期只建一次
|
|
425
|
+
react.useEffect(function () {
|
|
426
|
+
var canvas = canvasRef.current;
|
|
427
|
+
if (!canvas) return undefined;
|
|
428
|
+
var ctx = canvas.getContext && canvas.getContext("2d");
|
|
429
|
+
if (!ctx) return undefined;
|
|
430
|
+
var reduced = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
431
|
+
var width = 1;
|
|
432
|
+
var height = 1;
|
|
433
|
+
var frame = 0;
|
|
434
|
+
var grid = []; // 点阵网格(resize 时预计算静态哈希,逐帧只做时间维运算)
|
|
435
|
+
var cell = 5;
|
|
436
|
+
var gap = 1.1;
|
|
437
|
+
var fieldOn = false; // show 翻转沿:入场展开动画的计时原点
|
|
438
|
+
var fieldStart = 0;
|
|
439
|
+
var lastDrawn = 0;
|
|
440
|
+
|
|
441
|
+
var resize = function () {
|
|
442
|
+
var b = canvas.getBoundingClientRect();
|
|
443
|
+
var ratio = Math.min(window.devicePixelRatio || 1, 2);
|
|
444
|
+
width = Math.max(1, b.width);
|
|
445
|
+
height = Math.max(1, b.height);
|
|
446
|
+
canvas.width = Math.max(1, Math.round(width * ratio));
|
|
447
|
+
canvas.height = Math.max(1, Math.round(height * ratio));
|
|
448
|
+
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
|
|
449
|
+
// 网格:200×22 轨道 → 40 列 × 5 行 ≈ 200 格(哈希来自仓库B 的预计算方案)
|
|
450
|
+
cell = width < 280 ? 5 : 6;
|
|
451
|
+
grid = [];
|
|
452
|
+
for (var row = 0; row * cell < height; row++) {
|
|
453
|
+
for (var column = 0; column * cell < width; column++) {
|
|
454
|
+
grid.push({
|
|
455
|
+
x: column * cell,
|
|
456
|
+
y: row * cell,
|
|
457
|
+
base: Math.abs(Math.sin(column * 12.9898 + row * 78.233) * 43758.5453) % 1,
|
|
458
|
+
tempo: Math.abs(Math.sin(column * 7.13 + row * 19.41) * 19341.731) % 1,
|
|
459
|
+
phase: Math.abs(Math.sin(column * 31.17 + row * 11.93) * 28437.123) % 1,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
var draw = function (time) {
|
|
466
|
+
var st = geoRef.current || { origin: 0, rightEdge: 0, tier: 0, show: false, dragging: false };
|
|
467
|
+
ctx.clearRect(0, 0, width, height);
|
|
468
|
+
if (!st.show || st.rightEdge <= 0) {
|
|
469
|
+
fieldOn = false;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (!fieldOn) {
|
|
473
|
+
fieldOn = true;
|
|
474
|
+
fieldStart = time; // 入场展开从这一刻起算(900ms 从圆球向外揭示)
|
|
475
|
+
}
|
|
476
|
+
var dark = document.body.hasAttribute("data-ds-dark-theme");
|
|
477
|
+
var tier = FIELD_TIERS[st.tier] || FIELD_TIERS[1];
|
|
478
|
+
var elapsed = Math.max(0, time - fieldStart);
|
|
479
|
+
var reveal = reduced.matches ? 1 : smStep(0, 1, elapsed / 900);
|
|
480
|
+
var ripplePhase = (elapsed % 1200) / 1200; // 明暗水波纹(1200ms 一轮,从球向外)
|
|
481
|
+
var tempo = tier.tempo * (st.dragging ? 2 : 1); // 拖拽全档提速
|
|
482
|
+
// 基色→高亮色:浅色用深蓝(multiply 混合下沉显色)/ 暗色用亮蓝
|
|
483
|
+
var dim = dark ? [124, 144, 250] : [61, 91, 224];
|
|
484
|
+
var hot = dark ? [214, 224, 255] : [126, 148, 250];
|
|
485
|
+
|
|
486
|
+
ctx.save();
|
|
487
|
+
ctx.beginPath();
|
|
488
|
+
// 裁剪到填充区(胶囊形,与滑轨同圆角——矩形裁剪会在圆角末端溢出)
|
|
489
|
+
if (ctx.roundRect) ctx.roundRect(0, 0, st.rightEdge, height, height / 2);
|
|
490
|
+
else ctx.rect(0, 0, st.rightEdge, height);
|
|
491
|
+
ctx.clip();
|
|
492
|
+
|
|
493
|
+
for (var i = 0; i < grid.length; i++) {
|
|
494
|
+
var c = grid[i];
|
|
495
|
+
var dx = Math.abs(c.x + cell * 0.5 - st.origin) / Math.max(1, st.rightEdge * 0.5);
|
|
496
|
+
if (dx > 1) continue;
|
|
497
|
+
var near = Math.min(1, Math.max(0, 1 - dx * 1.1)); // 近球更密更亮
|
|
498
|
+
if (c.base > tier.density - near * 0.3) continue; // 密度门(近球放行更多格)
|
|
499
|
+
// 独立随机闪烁:每格按自身 tempo/phase 起伏
|
|
500
|
+
var flicker = 0.5 + 0.5 * Math.sin(elapsed * 0.012 * tempo + c.tempo * 6.283 + c.phase * 6.283);
|
|
501
|
+
// 明暗水波纹:从球心向外传播的亮带(tier 未开波纹时给常量底)
|
|
502
|
+
var wave = tier.wave ? 0.5 + 0.5 * Math.sin((dx * 2 - ripplePhase) * 6.283) : 0.62;
|
|
503
|
+
// 展开:越靠近球越早亮,向外渐显
|
|
504
|
+
var revealA = smStep(0, 1, reveal * (1 - dx * 0.85) + dx * 0.15);
|
|
505
|
+
var alpha = Math.min(1, (0.26 + 0.44 * flicker + near * 0.28) * (0.28 + 0.72 * wave) * revealA * tier.alpha);
|
|
506
|
+
if (alpha < 0.02) continue;
|
|
507
|
+
// 亮闪格向高亮色靠(flicker×wave 双高才发白)
|
|
508
|
+
var glowMix = Math.max(0, flicker * wave - 0.45) * 1.6;
|
|
509
|
+
ctx.fillStyle = "rgba(" +
|
|
510
|
+
Math.round(dim[0] + (hot[0] - dim[0]) * glowMix) + "," +
|
|
511
|
+
Math.round(dim[1] + (hot[1] - dim[1]) * glowMix) + "," +
|
|
512
|
+
Math.round(dim[2] + (hot[2] - dim[2]) * glowMix) + "," +
|
|
513
|
+
alpha.toFixed(3) + ")";
|
|
514
|
+
ctx.fillRect(c.x + gap * 0.5, c.y + gap * 0.5, cell - gap, cell - gap);
|
|
515
|
+
}
|
|
516
|
+
ctx.restore();
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
var loop = function (time) {
|
|
520
|
+
// 33ms 节流(≈30fps):闪烁/波纹尺度下无可感差异,省电
|
|
521
|
+
if (time - lastDrawn >= 33) {
|
|
522
|
+
lastDrawn = time;
|
|
523
|
+
draw(time);
|
|
524
|
+
}
|
|
525
|
+
frame = window.requestAnimationFrame(loop);
|
|
526
|
+
};
|
|
527
|
+
var redrawStatic = function () {
|
|
528
|
+
if (reduced.matches) draw(performance.now());
|
|
529
|
+
};
|
|
530
|
+
var ro = new ResizeObserver(function () {
|
|
531
|
+
resize();
|
|
532
|
+
redrawStatic();
|
|
533
|
+
});
|
|
534
|
+
var themeObs = new MutationObserver(function () {
|
|
535
|
+
redrawStatic(); // 静帧模式下主题翻转要重画(动画循环每帧自读主题)
|
|
536
|
+
});
|
|
537
|
+
ro.observe(canvas);
|
|
538
|
+
themeObs.observe(document.body, { attributes: true, attributeFilter: ["data-ds-dark-theme"] });
|
|
539
|
+
resize();
|
|
540
|
+
draw(performance.now());
|
|
541
|
+
if (!reduced.matches) frame = window.requestAnimationFrame(loop);
|
|
542
|
+
return function () {
|
|
543
|
+
window.cancelAnimationFrame(frame);
|
|
544
|
+
ro.disconnect();
|
|
545
|
+
themeObs.disconnect();
|
|
546
|
+
};
|
|
547
|
+
}, []);
|
|
548
|
+
|
|
549
|
+
// 停点刻度:轨道上的 4 个小点提示可吸附位置;档位名改由拖动气泡显示
|
|
365
550
|
var stops = [];
|
|
366
551
|
for (var i = 0; i < MODES.length; i++) {
|
|
367
|
-
(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
"
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
),
|
|
387
|
-
react.createElement(
|
|
388
|
-
"button",
|
|
389
|
-
{
|
|
390
|
-
key: "label" + i,
|
|
391
|
-
style: {
|
|
392
|
-
position: "absolute",
|
|
393
|
-
left: stopLeft,
|
|
394
|
-
top: 26,
|
|
395
|
-
transform: "translateX(-50%)",
|
|
396
|
-
fontSize: 11,
|
|
397
|
-
lineHeight: "16px",
|
|
398
|
-
padding: "0 4px",
|
|
399
|
-
border: "none",
|
|
400
|
-
background: "none",
|
|
401
|
-
cursor: "pointer",
|
|
402
|
-
whiteSpace: "nowrap",
|
|
403
|
-
fontWeight: active ? 600 : 400,
|
|
404
|
-
// 未激活标签走主题 caption 令牌(浅色 #400 灰蓝 / 暗色 #600),玻璃面上保持可读
|
|
405
|
-
color: active ? MODES[i].color : "var(--dsw-alias-label-caption, #888)",
|
|
406
|
-
},
|
|
407
|
-
onClick: function () { if (drag === null) props.onCommit(MODES[i].key); },
|
|
408
|
-
},
|
|
409
|
-
MODES[i].label,
|
|
410
|
-
),
|
|
411
|
-
);
|
|
412
|
-
})(i);
|
|
552
|
+
var stopLeft = (i / (MODES.length - 1)) * INNER_W + THUMB / 2;
|
|
553
|
+
stops.push(
|
|
554
|
+
react.createElement("div", {
|
|
555
|
+
key: "stop" + i,
|
|
556
|
+
style: {
|
|
557
|
+
position: "absolute",
|
|
558
|
+
left: stopLeft - 3,
|
|
559
|
+
top: (RAIL_H - 6) / 2,
|
|
560
|
+
width: 6,
|
|
561
|
+
height: 6,
|
|
562
|
+
borderRadius: "50%",
|
|
563
|
+
background: "var(--dsh-mem-dot)",
|
|
564
|
+
zIndex: 2,
|
|
565
|
+
pointerEvents: "none",
|
|
566
|
+
},
|
|
567
|
+
}),
|
|
568
|
+
);
|
|
413
569
|
}
|
|
414
570
|
|
|
415
571
|
return react.createElement(
|
|
416
572
|
"div",
|
|
417
573
|
{
|
|
418
|
-
// 外壳只负责定位(带 transform
|
|
419
|
-
// Chromium 中 transform 元素上的 backdrop-filter 采样异常,玻璃会失效
|
|
574
|
+
// 外壳只负责定位(带 transform 居中悬浮在按钮上方,水平中轴对齐 pill 中心)
|
|
420
575
|
style: {
|
|
421
576
|
position: "absolute",
|
|
422
577
|
bottom: "calc(100% + 8px)",
|
|
423
|
-
// 居中悬浮在按钮上方(水平中轴对齐 pill 中心)
|
|
424
578
|
left: "50%",
|
|
425
579
|
transform: "translateX(-50%)",
|
|
426
580
|
zIndex: 1000,
|
|
427
581
|
},
|
|
428
582
|
},
|
|
429
|
-
react.createElement("div", {
|
|
430
|
-
className: "dsh-mem-glass",
|
|
431
|
-
style: { position: "absolute", top: 0, left: 0, right: 0, bottom: 0, pointerEvents: "none" },
|
|
432
|
-
}),
|
|
433
583
|
react.createElement(
|
|
434
584
|
"div",
|
|
435
|
-
|
|
585
|
+
// dsh 原生菜单同配方浮层:不透明实底(dsw-specific-menu)+ inverted 描边 + lv3 阴影;
|
|
586
|
+
// 上下内边距对称(滑轨垂直居中、浮层紧凑),拖动气泡经 overflow: visible 溢出到浮层上方
|
|
587
|
+
{ className: "dsh-mem-popover", style: { position: "relative", padding: "14px 16px" } },
|
|
436
588
|
react.createElement(
|
|
437
589
|
"div",
|
|
438
590
|
{
|
|
@@ -441,112 +593,304 @@ window.__ModuleLoader__.load({
|
|
|
441
593
|
position: "relative",
|
|
442
594
|
// 容器宽 = thumb 活动范围(0..INNER_W + THUMB),点击映射与视觉两端严格对齐
|
|
443
595
|
width: TRACK_W,
|
|
444
|
-
height:
|
|
596
|
+
height: RAIL_H,
|
|
597
|
+
borderRadius: 999,
|
|
598
|
+
background: "var(--dsh-mem-track)",
|
|
445
599
|
touchAction: "none",
|
|
446
600
|
cursor: drag === null ? "pointer" : "grabbing",
|
|
601
|
+
},
|
|
602
|
+
onPointerDown: onPointerDown,
|
|
603
|
+
onPointerMove: onPointerMove,
|
|
604
|
+
onPointerUp: onPointerUp,
|
|
605
|
+
onPointerCancel: onPointerUp,
|
|
447
606
|
},
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
},
|
|
486
|
-
|
|
607
|
+
// 填充:从滑轨左端铺到圆球右缘(width = thumbLeft + THUMB,整球落在
|
|
608
|
+
// 填充末端上与其重合,无空隙不割裂;auto 档恰好全轨蓝、不超出轨道);
|
|
609
|
+
// 颜色从左往右渐变:左侧浅(fill-1)到球侧深(fill-2);
|
|
610
|
+
// 显隐分两支:静态关闭档(off 且未拖拽)不渲染;拖拽中无论预览到哪档
|
|
611
|
+
// 恒显示(松手落 off 才随提交消失);
|
|
612
|
+
// 松手吸附时 width 与圆球 left 同走 120ms ease(防球与填充瞬时分家)
|
|
613
|
+
activeIdx > 0 || drag !== null
|
|
614
|
+
? react.createElement("div", {
|
|
615
|
+
style: {
|
|
616
|
+
position: "absolute",
|
|
617
|
+
left: 0,
|
|
618
|
+
top: 0,
|
|
619
|
+
bottom: 0,
|
|
620
|
+
width: thumbLeft + THUMB,
|
|
621
|
+
borderRadius: 999,
|
|
622
|
+
background: "linear-gradient(90deg, var(--dsh-mem-fill-1), var(--dsh-mem-fill-2))",
|
|
623
|
+
pointerEvents: "none",
|
|
624
|
+
zIndex: 1,
|
|
625
|
+
transition: drag === null ? "width 120ms ease" : "none",
|
|
626
|
+
},
|
|
627
|
+
})
|
|
628
|
+
: null,
|
|
629
|
+
stops,
|
|
630
|
+
// 粒子层:点阵粒子场(pointerEvents none 不挡拖拽;拖拽时滤镜增饱和提亮)
|
|
631
|
+
react.createElement("canvas", {
|
|
632
|
+
ref: canvasRef,
|
|
633
|
+
className: "dsh-mem-particles",
|
|
634
|
+
style: {
|
|
635
|
+
position: "absolute",
|
|
636
|
+
left: 0,
|
|
637
|
+
top: 0,
|
|
638
|
+
width: "100%",
|
|
639
|
+
height: "100%",
|
|
640
|
+
pointerEvents: "none",
|
|
641
|
+
zIndex: 2,
|
|
642
|
+
filter: drag !== null ? "saturate(1.45) brightness(1.28) contrast(1.06)" : "none",
|
|
643
|
+
},
|
|
644
|
+
}),
|
|
645
|
+
// 圆球:被粗滑轨包裹(RAIL_H > THUMB),品牌蓝描边,拖拽时阴影加重
|
|
646
|
+
react.createElement("div", {
|
|
647
|
+
style: {
|
|
648
|
+
position: "absolute",
|
|
649
|
+
left: thumbLeft,
|
|
650
|
+
top: (RAIL_H - THUMB) / 2,
|
|
651
|
+
width: THUMB,
|
|
652
|
+
height: THUMB,
|
|
653
|
+
borderRadius: "50%",
|
|
654
|
+
background: "var(--dsh-mem-thumb)",
|
|
655
|
+
border: "1px solid var(--dsh-mem-accent)",
|
|
656
|
+
boxShadow: drag !== null
|
|
657
|
+
? "0 2px 8px rgba(0,0,0,0.35)"
|
|
658
|
+
: "0 1px 4px rgba(0,0,0,0.25)",
|
|
659
|
+
pointerEvents: "none",
|
|
660
|
+
transition: drag === null ? "left 120ms ease" : "none",
|
|
661
|
+
zIndex: 3,
|
|
662
|
+
},
|
|
663
|
+
}),
|
|
664
|
+
// 拖动气泡:仅拖拽期间显示当前档位名,下尖角指向圆球,松手即消失
|
|
665
|
+
drag !== null
|
|
666
|
+
? react.createElement(
|
|
667
|
+
"div",
|
|
668
|
+
{ className: "dsh-mem-bubble", style: { left: thumbLeft + THUMB / 2, zIndex: 4 } },
|
|
669
|
+
info.label,
|
|
670
|
+
)
|
|
671
|
+
: null,
|
|
487
672
|
),
|
|
488
673
|
props.error
|
|
489
|
-
? react.createElement("div", { style: { fontSize: 11, color: "
|
|
674
|
+
? react.createElement("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 10, whiteSpace: "nowrap" } }, props.error)
|
|
490
675
|
: null,
|
|
491
676
|
),
|
|
492
677
|
);
|
|
493
678
|
}
|
|
494
679
|
|
|
495
|
-
// ──
|
|
496
|
-
// 惰性注入一次性样式表(id 防重复)。
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
680
|
+
// ── 主题令牌层 + 组件样式:inline style 放不了 @keyframes/@property/伪类/媒体查询,
|
|
681
|
+
// 惰性注入一次性样式表(id 防重复)。
|
|
682
|
+
// 主题机制:dsh 前端在 body[data-ds-dark-theme] 切暗色并重定义 --dsw-alias-* 令牌;
|
|
683
|
+
// 我们的中性色链真实 dsw 令牌(缺省时用自带 fallback),强调色是 DeepSeek 品牌蓝体系:
|
|
684
|
+
// accent(图形:下划线/边框/光晕,非文字,3:1 即可)
|
|
685
|
+
// accent-text(表面上的强调文字,双主题 ≥4.5:1)
|
|
686
|
+
// accent-fill(实底填充 + 白字,双主题 ≥4.5:1)
|
|
687
|
+
// 设置页挂载即注入(ensureThemeStyle),输入栏 pill / 浮层 / 重建面板共用。 ──
|
|
688
|
+
var THEME_STYLE_ID = "dsh-mem-theme-style";
|
|
689
|
+
function ensureThemeStyle() {
|
|
690
|
+
if (document.getElementById(THEME_STYLE_ID)) return;
|
|
500
691
|
var el = document.createElement("style");
|
|
501
|
-
el.id =
|
|
692
|
+
el.id = THEME_STYLE_ID;
|
|
502
693
|
el.textContent = [
|
|
503
|
-
// conic 角度动画需要 @property
|
|
694
|
+
// conic 角度动画需要 @property 注册才能插值;不支持 @property 的浏览器里
|
|
695
|
+
// var(--dsh-mem-angle) 无定义 → conic 层失效 → 整条 background 退化为无背景
|
|
696
|
+
// (光带边框与内底一并消失,仅剩文字色)。2026 常青浏览器均已支持,仅作记录。
|
|
504
697
|
"@property --dsh-mem-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }",
|
|
505
698
|
"@keyframes dshMemFlow { to { --dsh-mem-angle: 360deg; } }",
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
//
|
|
699
|
+
// ── 令牌(浅色)。中性色链【真实存在的】dsw 令牌(design-platform.css 校对):
|
|
700
|
+
// bg-layer-2/3、bg-overlay、border-l1/l2/l3、border-inverted、label-*、
|
|
701
|
+
// interactive-bg-hover、state-error-primary、tooltip-bg、dsw-shadow-lv1/lv3 ──
|
|
702
|
+
":root {",
|
|
703
|
+
" --dsh-mem-accent: #4d6bfe;",
|
|
704
|
+
" --dsh-mem-accent-text: #3d5be0;",
|
|
705
|
+
" --dsh-mem-accent-fill: #3d5be0;",
|
|
706
|
+
" --dsh-mem-accent-weak: rgba(77,107,254,0.10);",
|
|
707
|
+
" --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #ffffff);",
|
|
708
|
+
" --dsh-mem-bg-inset: var(--dsw-alias-bg-overlay, #e9ecf2);",
|
|
709
|
+
" --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(38,49,72,0.06));",
|
|
710
|
+
" --dsh-mem-bg-pop: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3, #ffffff));",
|
|
711
|
+
" --dsh-mem-border: var(--dsw-alias-border-l2, rgba(0,0,0,0.10));",
|
|
712
|
+
" --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(0,0,0,0.12));",
|
|
713
|
+
" --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(0,0,0,0));",
|
|
714
|
+
" --dsh-mem-text-1: var(--dsw-alias-label-primary, #0f1115);",
|
|
715
|
+
" --dsh-mem-text-2: var(--dsw-alias-label-secondary, #61666b);",
|
|
716
|
+
" --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #6e7781);",
|
|
717
|
+
" --dsh-mem-danger: var(--dsw-alias-state-error-primary, #d0403f);",
|
|
718
|
+
// 档位色 = 灰 → 品牌蓝 的渐变阶(chat/work 过渡蓝 / auto 品牌蓝);
|
|
719
|
+
// 文字对比度按 pill 真实底色(流光内底 = bg-card 97% + 档位色 3%)复算 AA 达标
|
|
720
|
+
" --dsh-mem-mode-chat: #5a69b0;",
|
|
721
|
+
" --dsh-mem-mode-work: #5263ca;",
|
|
722
|
+
" --dsh-mem-mode-auto: #3d5be0;",
|
|
723
|
+
// 滑轨填充渐变(左浅右深)
|
|
724
|
+
" --dsh-mem-fill-1: #7b93ff;",
|
|
725
|
+
" --dsh-mem-fill-2: #3d5be0;",
|
|
726
|
+
" --dsh-mem-thumb: #ffffff;",
|
|
727
|
+
" --dsh-mem-track: rgba(128,140,150,0.32);",
|
|
728
|
+
" --dsh-mem-dot: rgba(128,140,150,0.55);",
|
|
729
|
+
" --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.05));",
|
|
730
|
+
" --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,.08));",
|
|
731
|
+
"}",
|
|
732
|
+
// ── 令牌(暗色:body[data-ds-dark-theme] 是 dsh 前端的暗色开关) ──
|
|
733
|
+
"body[data-ds-dark-theme] {",
|
|
734
|
+
" --dsh-mem-accent: #6e85ff;",
|
|
735
|
+
" --dsh-mem-accent-text: #7b90ff;",
|
|
736
|
+
" --dsh-mem-accent-fill: #465ce8;",
|
|
737
|
+
" --dsh-mem-accent-weak: rgba(110,133,255,0.14);",
|
|
738
|
+
" --dsh-mem-bg-card: var(--dsw-alias-bg-layer-2, #2c2c2e);",
|
|
739
|
+
" --dsh-mem-bg-inset: var(--dsw-alias-bg-layer-1, #232324);",
|
|
740
|
+
" --dsh-mem-bg-hover: var(--dsw-alias-interactive-bg-hover, rgba(255,255,255,0.08));",
|
|
741
|
+
" --dsh-mem-bg-pop: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3, #353638));",
|
|
742
|
+
" --dsh-mem-border: var(--dsw-alias-border-l2, rgba(255,255,255,0.12));",
|
|
743
|
+
" --dsh-mem-border-strong: var(--dsw-alias-border-l3, rgba(255,255,255,0.16));",
|
|
744
|
+
" --dsh-mem-border-pop: var(--dsw-alias-border-inverted, rgba(255,255,255,0.06));",
|
|
745
|
+
" --dsh-mem-text-1: var(--dsw-alias-label-primary, #f9fafb);",
|
|
746
|
+
" --dsh-mem-text-2: var(--dsw-alias-label-secondary, #cfd3d6);",
|
|
747
|
+
" --dsh-mem-text-3: var(--dsw-alias-label-tertiary, #8892a6);",
|
|
748
|
+
" --dsh-mem-danger: var(--dsw-alias-state-error-primary, #f4707b);",
|
|
749
|
+
" --dsh-mem-mode-chat: #97a4ff;",
|
|
750
|
+
" --dsh-mem-mode-work: #8295ff;",
|
|
751
|
+
" --dsh-mem-mode-auto: #7b90ff;",
|
|
752
|
+
" --dsh-mem-fill-1: #8fa0ff;",
|
|
753
|
+
" --dsh-mem-fill-2: #465ce8;",
|
|
754
|
+
" --dsh-mem-thumb: #e8ebf5;",
|
|
755
|
+
" --dsh-mem-track: rgba(148,160,180,0.30);",
|
|
756
|
+
" --dsh-mem-dot: rgba(148,160,180,0.5);",
|
|
757
|
+
" --dsh-mem-shadow-card: var(--dsw-shadow-lv1, 0 2px 4px 0 rgba(0,0,0,0.3));",
|
|
758
|
+
" --dsh-mem-shadow-pop: var(--dsw-shadow-lv3, 0 0 1px 0 rgba(0,0,0,.2), 0 0 4px 0 rgba(0,0,0,.02), 0 12px 32px 0 rgba(0,0,0,.08));",
|
|
759
|
+
"}",
|
|
760
|
+
// ── 主题切换过渡:只挂颜色/阴影(不碰 transform),让明暗翻转不生硬 ──
|
|
761
|
+
".dsh-mem-root, .dsh-mem-root * { transition: background-color .18s ease, border-color .18s ease, color .18s ease, box-shadow .18s ease; }",
|
|
762
|
+
// ── 控件:按钮(次级)── 圆角体系:控件 8 / 卡片 10 / 浮层 12 / 胶囊 999
|
|
763
|
+
".dsh-mem-btn {",
|
|
764
|
+
" padding: 5px 14px; font-size: 13px; line-height: 20px; border-radius: 8px; cursor: pointer;",
|
|
765
|
+
" border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
|
|
766
|
+
" transition: background-color .15s ease, border-color .15s ease, transform .08s ease;",
|
|
767
|
+
"}",
|
|
768
|
+
".dsh-mem-btn:hover:not(:disabled) { border-color: var(--dsh-mem-border-strong); background: var(--dsh-mem-bg-hover); }",
|
|
769
|
+
".dsh-mem-btn:active:not(:disabled) { transform: scale(0.98); }",
|
|
770
|
+
".dsh-mem-btn:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
|
|
771
|
+
".dsh-mem-btn:disabled { opacity: 0.45; cursor: not-allowed; }",
|
|
772
|
+
// ── 控件:输入框 / 下拉 ──
|
|
773
|
+
".dsh-mem-input, .dsh-mem-select {",
|
|
774
|
+
" padding: 5px 10px; font-size: 13px; border-radius: 8px; color: var(--dsh-mem-text-1);",
|
|
775
|
+
" border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card);",
|
|
776
|
+
" transition: border-color .15s ease, box-shadow .15s ease;",
|
|
777
|
+
"}",
|
|
778
|
+
".dsh-mem-input:focus, .dsh-mem-select:focus {",
|
|
779
|
+
" outline: none; border-color: var(--dsh-mem-accent);",
|
|
780
|
+
" box-shadow: 0 0 0 3px var(--dsh-mem-accent-weak);",
|
|
781
|
+
"}",
|
|
782
|
+
// ── Tab(下划线式):active 品牌蓝下划线 + 主文字色 ──
|
|
783
|
+
".dsh-mem-tab {",
|
|
784
|
+
" padding: 6px 12px; font-size: 13px; cursor: pointer; background: none; border: none;",
|
|
785
|
+
" color: var(--dsh-mem-text-2); border-bottom: 2px solid transparent; margin-bottom: -1px;",
|
|
786
|
+
"}",
|
|
787
|
+
".dsh-mem-tab:hover { color: var(--dsh-mem-text-1); }",
|
|
788
|
+
".dsh-mem-tab-on { font-weight: 600; color: var(--dsh-mem-text-1); border-bottom-color: var(--dsh-mem-accent); }",
|
|
789
|
+
".dsh-mem-tab:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: -2px; }",
|
|
790
|
+
// ── 卡片:悬浮微抬 + 边框加深(只在可交互卡片上用 hover) ──
|
|
791
|
+
".dsh-mem-card {",
|
|
792
|
+
" border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
|
|
793
|
+
" box-shadow: var(--dsh-mem-shadow-card);",
|
|
794
|
+
"}",
|
|
795
|
+
".dsh-mem-card-hover:hover { border-color: var(--dsh-mem-border-strong); }",
|
|
796
|
+
// ── 记忆类型标签:tint 风格(彩底淡色 + 彩字),--dsh-mem-tag-c 由类型类给定 ──
|
|
797
|
+
".dsh-mem-tag {",
|
|
798
|
+
" display: inline-block; padding: 1px 8px; border-radius: 999px;",
|
|
799
|
+
" font-size: 11px; font-weight: 600; line-height: 18px; white-space: nowrap;",
|
|
800
|
+
" color: var(--dsh-mem-tag-c, var(--dsh-mem-text-2));",
|
|
801
|
+
" background: color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 12%, transparent);",
|
|
802
|
+
" border: 1px solid color-mix(in srgb, var(--dsh-mem-tag-c, #8a93a1) 28%, transparent);",
|
|
803
|
+
"}",
|
|
804
|
+
".dsh-mem-tag-persona { --dsh-mem-tag-c: #6f42c1; }",
|
|
805
|
+
".dsh-mem-tag-episodic { --dsh-mem-tag-c: #0757b4; }",
|
|
806
|
+
".dsh-mem-tag-instruction, .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #8a5a00; }",
|
|
807
|
+
".dsh-mem-tag-work-fact { --dsh-mem-tag-c: #0757b4; }",
|
|
808
|
+
".dsh-mem-tag-work-task { --dsh-mem-tag-c: #116629; }",
|
|
809
|
+
".dsh-mem-tag-work-method { --dsh-mem-tag-c: #6f42c1; }",
|
|
810
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-persona { --dsh-mem-tag-c: #c297ff; }",
|
|
811
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-episodic { --dsh-mem-tag-c: #6cb2ff; }",
|
|
812
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-instruction, body[data-ds-dark-theme] .dsh-mem-tag-work-artifact { --dsh-mem-tag-c: #e3b341; }",
|
|
813
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-work-fact { --dsh-mem-tag-c: #6cb2ff; }",
|
|
814
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-work-task { --dsh-mem-tag-c: #6fca74; }",
|
|
815
|
+
"body[data-ds-dark-theme] .dsh-mem-tag-work-method { --dsh-mem-tag-c: #c297ff; }",
|
|
816
|
+
// ── pill 边缘流光(品牌蓝族,chat/work/auto 三档共用;off 无):border 区画旋转
|
|
817
|
+
// conic 光带;内部必须是【不透明】底色盖住光带(半透明内层会让 conic 透进按钮
|
|
818
|
+
// 内部,文字被光斑干扰——实测事故)。不透明底 = 主题底混 3% 档位色
|
|
819
|
+
// (--dsh-mem-pill-tint 由 pill inline 给定;3% 保证档位色文字 AA,暗色智能档余量 4.63:1)
|
|
509
820
|
".dsh-mem-flow {",
|
|
510
821
|
" border: 1px solid transparent;",
|
|
511
822
|
" background:",
|
|
512
823
|
" linear-gradient(",
|
|
513
|
-
" color-mix(in srgb, var(--
|
|
514
|
-
" color-mix(in srgb, var(--
|
|
824
|
+
" color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe)),",
|
|
825
|
+
" color-mix(in srgb, var(--dsh-mem-bg-card, #ffffff) 97%, var(--dsh-mem-pill-tint, #4d6bfe))",
|
|
515
826
|
" ) padding-box,",
|
|
516
827
|
" conic-gradient(from var(--dsh-mem-angle),",
|
|
517
|
-
" rgba(
|
|
518
|
-
" rgba(
|
|
828
|
+
" rgba(61,91,224,0.9), rgba(77,107,254,0.95), rgba(147,168,255,1),",
|
|
829
|
+
" rgba(110,133,255,0.9), rgba(61,91,224,0.9)) border-box;",
|
|
519
830
|
" animation: dshMemFlow 3s linear infinite;",
|
|
520
|
-
" color: #0284c7;",
|
|
521
831
|
"}",
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
832
|
+
// ── off 档 pill:dsh 透明按钮——无底无边框只留文字,hover 才出 interactive 淡底 ──
|
|
833
|
+
//(button 裸元素会露出 UA 默认灰底+描边,必须显式压掉)
|
|
834
|
+
".dsh-mem-pill-off { border: none; background: transparent; }",
|
|
835
|
+
".dsh-mem-pill-off:hover { background: var(--dsh-mem-bg-hover); }",
|
|
836
|
+
".dsh-mem-pill-off:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
|
|
837
|
+
// 流光态焦点环(同一物理按钮的两态焦点反馈对称,配方同 .dsh-mem-btn)
|
|
838
|
+
".dsh-mem-flow:focus-visible { outline: 2px solid var(--dsh-mem-accent); outline-offset: 1px; }",
|
|
839
|
+
// ── 浮层(dsh 原生菜单同配方:不透明实底 + inverted 描边(浅色不可见)+ lv3 阴影) ──
|
|
840
|
+
".dsh-mem-popover {",
|
|
528
841
|
" border-radius: 12px;",
|
|
529
|
-
"
|
|
530
|
-
"
|
|
531
|
-
"
|
|
532
|
-
" -
|
|
533
|
-
" backdrop-filter: blur(32px) saturate(180%);",
|
|
534
|
-
" border: 1px solid rgba(255,255,255,0.55);",
|
|
535
|
-
" box-shadow:",
|
|
536
|
-
" 0 12px 40px rgba(0,0,0,0.16), 0 2px 8px rgba(0,0,0,0.07),",
|
|
537
|
-
" inset 0 1px 0 rgba(255,255,255,0.55), inset 0 -1px 0 rgba(0,0,0,0.03);",
|
|
842
|
+
" border: 1px solid var(--dsh-mem-border-pop);",
|
|
843
|
+
" background: var(--dsh-mem-bg-pop);",
|
|
844
|
+
" box-shadow: var(--dsh-mem-shadow-pop);",
|
|
845
|
+
" color: var(--dsh-mem-text-1);",
|
|
538
846
|
"}",
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
"
|
|
545
|
-
"
|
|
546
|
-
"
|
|
847
|
+
// ── 拖动气泡:拖拽时显示当前档位名,随圆球移动,下倒三角尖角贴近圆球 ──
|
|
848
|
+
// 底色走浮层同材质令牌(浅色白底深字 / 暗色深底浅字,随主题翻转;
|
|
849
|
+
// tooltip-bg 令牌在浅色下仍是深色,不随材质走,已弃用)。
|
|
850
|
+
// 悬停 8px(尖角尖端距圆球顶约 5px);气泡 zIndex 4 高于浮层(同层叠上下文内
|
|
851
|
+
// 数值比较),跨过浮层上缘时盖在其上;描边 + 投影让同材质不融合
|
|
852
|
+
".dsh-mem-bubble {",
|
|
853
|
+
" position: absolute; bottom: calc(100% + 8px); transform: translateX(-50%);",
|
|
854
|
+
" padding: 3px 10px; border-radius: 8px; font-size: 12px; font-weight: 600; line-height: 18px;",
|
|
855
|
+
" border: 1px solid var(--dsh-mem-border);",
|
|
856
|
+
" background: var(--dsh-mem-bg-pop); color: var(--dsh-mem-text-1); white-space: nowrap;",
|
|
857
|
+
" box-shadow: 0 2px 8px rgba(0,0,0,0.18);",
|
|
858
|
+
"}",
|
|
859
|
+
// 尖角:clip-path 倒三角(旋转方块会露出上半截成菱形,实测视觉缺陷)。
|
|
860
|
+
// 双三角叠画:外层描边色大一圈、内层填充色,压在浮层上缘也有轮廓可读
|
|
861
|
+
".dsh-mem-bubble::before {",
|
|
862
|
+
" content: ''; position: absolute; top: 100%; left: 50%; margin-left: -6px;",
|
|
863
|
+
" width: 12px; height: 7px;",
|
|
864
|
+
" clip-path: polygon(0 0, 100% 0, 50% 100%);",
|
|
865
|
+
" background: var(--dsh-mem-border);",
|
|
866
|
+
"}",
|
|
867
|
+
".dsh-mem-bubble::after {",
|
|
868
|
+
" content: ''; position: absolute; top: 100%; left: 50%; margin-left: -5px;",
|
|
869
|
+
" width: 10px; height: 6px;",
|
|
870
|
+
" clip-path: polygon(0 0, 100% 0, 50% 100%);",
|
|
871
|
+
" background: var(--dsh-mem-bg-pop);",
|
|
547
872
|
"}",
|
|
548
|
-
|
|
549
|
-
"
|
|
873
|
+
// ── 粒子层(点阵场):浅色 multiply 混合——深蓝点乘在浅蓝填充上沉显对比 ──
|
|
874
|
+
"body:not([data-ds-dark-theme]) .dsh-mem-particles { mix-blend-mode: multiply; opacity: 0.82; }",
|
|
875
|
+
// ── 重建面板 ──(模态本体走 NModal:原生 Modal 优先,回退 rb-overlay/rb-modal)
|
|
876
|
+
".dsh-mem-rb-card {",
|
|
877
|
+
" border: 1px solid var(--dsh-mem-border); border-radius: 10px; background: var(--dsh-mem-bg-card);",
|
|
878
|
+
" box-shadow: var(--dsh-mem-shadow-card); padding: 12px 14px; margin-bottom: 14px; font-size: 13px;",
|
|
879
|
+
"}",
|
|
880
|
+
".dsh-mem-rb-bar { height: 8px; border-radius: 4px; overflow: hidden; flex: 1; background: var(--dsh-mem-track); }",
|
|
881
|
+
".dsh-mem-rb-fill { height: 100%; border-radius: 4px; background: var(--dsh-mem-accent-fill); transition: width .4s ease; }",
|
|
882
|
+
".dsh-mem-rb-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);",
|
|
883
|
+
" display: flex; align-items: center; justify-content: center; z-index: 2000; }",
|
|
884
|
+
".dsh-mem-rb-modal { width: 440px; max-width: calc(100vw - 48px); border-radius: 12px;",
|
|
885
|
+
" padding: 18px 20px; font-size: 13px; line-height: 1.6;",
|
|
886
|
+
" border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
|
|
887
|
+
" box-shadow: 0 16px 48px rgba(0,0,0,0.24); }",
|
|
888
|
+
"body[data-ds-dark-theme] .dsh-mem-rb-modal { box-shadow: 0 16px 48px rgba(0,0,0,0.6); }",
|
|
889
|
+
".dsh-mem-rb-muted { font-size: 12px; color: var(--dsh-mem-text-3); }",
|
|
890
|
+
// reduced-motion 兜底放样式表末尾:同特异性下后置声明才能压过上面的组件类
|
|
891
|
+
"@media (prefers-reduced-motion: reduce) {",
|
|
892
|
+
" .dsh-mem-root, .dsh-mem-root *, .dsh-mem-btn, .dsh-mem-input, .dsh-mem-select, .dsh-mem-rb-fill { transition: none; }",
|
|
893
|
+
" .dsh-mem-flow { animation: none; }",
|
|
550
894
|
"}",
|
|
551
895
|
].join("\n");
|
|
552
896
|
document.head.appendChild(el);
|
|
@@ -566,16 +910,31 @@ window.__ModuleLoader__.load({
|
|
|
566
910
|
var open = openState[0];
|
|
567
911
|
var setOpen = openState[1];
|
|
568
912
|
var wrapRef = react.useRef(null);
|
|
913
|
+
// 请求序列号:快速切换会话时丢弃旧会话的过期响应(慢响应不得覆盖新会话档位)
|
|
914
|
+
var seqRef = react.useRef(0);
|
|
569
915
|
|
|
570
|
-
react.
|
|
916
|
+
var load = react.useCallback(function () {
|
|
571
917
|
if (!sessionId || !rpc) return;
|
|
918
|
+
var token = ++seqRef.current;
|
|
919
|
+
setError(null);
|
|
572
920
|
rpc("dsh-memory/session-mode-get", { sessionId: sessionId })
|
|
573
921
|
.then(function (r) {
|
|
922
|
+
if (token !== seqRef.current) return;
|
|
574
923
|
if (r && r.ok && r.value) setMode(r.value.mode);
|
|
924
|
+
else setError(r && r.error ? r.error.message : "RPC error");
|
|
575
925
|
})
|
|
576
|
-
.catch(function () {
|
|
926
|
+
.catch(function (e) {
|
|
927
|
+
if (token !== seqRef.current) return;
|
|
928
|
+
setError(String((e && e.message) || e));
|
|
929
|
+
});
|
|
577
930
|
}, [sessionId, rpc]);
|
|
578
931
|
|
|
932
|
+
react.useEffect(function () { load(); }, [load]);
|
|
933
|
+
|
|
934
|
+
// 侧边栏书本 icon 补丁由常驻 pill 驱动(MemoryPanel 只在记忆分节激活时挂载,
|
|
935
|
+
// 覆盖不了"打开设置第一眼"的场景);body 级观察器全局单例,多实例幂等
|
|
936
|
+
react.useEffect(function () { watchSidebarIcon(); }, []);
|
|
937
|
+
|
|
579
938
|
react.useEffect(function () {
|
|
580
939
|
if (!open) return;
|
|
581
940
|
var onDown = function (e) {
|
|
@@ -614,12 +973,13 @@ window.__ModuleLoader__.load({
|
|
|
614
973
|
if (!sessionId || !rpc) return null;
|
|
615
974
|
var info = modeInfo(mode);
|
|
616
975
|
var loaded = mode !== null;
|
|
617
|
-
|
|
976
|
+
// 关闭档与其余三档二分:关闭 = dsh 透明按钮(无边框无底无光晕);
|
|
977
|
+
// 日常/工作/智能 = 同款流光 + 光晕,档位区分靠蓝阶文字色与流光内底混色深度
|
|
978
|
+
var isOff = loaded && mode === "off";
|
|
979
|
+
var isFlow = loaded && !isOff;
|
|
618
980
|
|
|
619
|
-
|
|
981
|
+
ensureThemeStyle();
|
|
620
982
|
|
|
621
|
-
// auto 档的边框/背景/文字色均由 .dsh-mem-flow 提供(双层背景画流光边 + 分主题文字),
|
|
622
|
-
// inline 不能设置这些——inline 优先级会盖掉 class 里的流光
|
|
623
983
|
var pillStyle = {
|
|
624
984
|
display: "inline-flex",
|
|
625
985
|
alignItems: "center",
|
|
@@ -631,13 +991,13 @@ window.__ModuleLoader__.load({
|
|
|
631
991
|
fontWeight: 500,
|
|
632
992
|
lineHeight: "20px",
|
|
633
993
|
cursor: "pointer",
|
|
994
|
+
// 流光档的边框/背景由 .dsh-mem-flow 的双层背景提供(流光边 + 不透明内底),
|
|
995
|
+
// inline 只给文字色 / 光晕 / 流光内底混色通道(--dsh-mem-pill-tint)
|
|
996
|
+
color: isFlow ? info.color : "var(--dsh-mem-text-2)",
|
|
634
997
|
};
|
|
635
|
-
if (
|
|
636
|
-
pillStyle.boxShadow = "0 0 12px
|
|
637
|
-
|
|
638
|
-
pillStyle.border = "1px solid " + (loaded ? info.color + "66" : "var(--dsw-alias-border-secondary, #ccc)");
|
|
639
|
-
pillStyle.background = loaded ? info.color + "14" : "var(--dsw-alias-bg-secondary, #f6f8fa)";
|
|
640
|
-
pillStyle.color = loaded ? info.color : "var(--dsh-alias-label-tertiary, #888)";
|
|
998
|
+
if (isFlow) {
|
|
999
|
+
pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
|
|
1000
|
+
pillStyle["--dsh-mem-pill-tint"] = info.color;
|
|
641
1001
|
}
|
|
642
1002
|
|
|
643
1003
|
return react.createElement(
|
|
@@ -647,13 +1007,16 @@ window.__ModuleLoader__.load({
|
|
|
647
1007
|
"button",
|
|
648
1008
|
{
|
|
649
1009
|
type: "button",
|
|
650
|
-
title: "本会话记忆档位(点击切换)",
|
|
651
|
-
onClick: function () {
|
|
652
|
-
|
|
1010
|
+
title: error ? "档位读取失败:" + error + "(点击重试)" : "本会话记忆档位(点击切换)",
|
|
1011
|
+
onClick: function () {
|
|
1012
|
+
if (error) load();
|
|
1013
|
+
setOpen(!open);
|
|
1014
|
+
},
|
|
1015
|
+
className: isFlow ? "dsh-mem-flow" : "dsh-mem-pill-off",
|
|
653
1016
|
style: pillStyle,
|
|
654
1017
|
},
|
|
655
|
-
"
|
|
656
|
-
react.createElement("span", null, loaded ? info.label : "…"),
|
|
1018
|
+
"记忆 · ",
|
|
1019
|
+
react.createElement("span", null, loaded ? info.label : error ? "⚠" : "…"),
|
|
657
1020
|
),
|
|
658
1021
|
open
|
|
659
1022
|
? react.createElement(ModeSlider, {
|
|
@@ -665,6 +1028,441 @@ window.__ModuleLoader__.load({
|
|
|
665
1028
|
);
|
|
666
1029
|
}
|
|
667
1030
|
|
|
1031
|
+
// ── 重建面板:从 L0 重导 L1/L2/L3(确认弹窗 + 进度 + 取消;Light/Dark 双主题走 class) ──
|
|
1032
|
+
var RB_PHASE_LABEL = {
|
|
1033
|
+
preparing: "准备中(归档旧数据 · 清空检索库)",
|
|
1034
|
+
distilling: "分块蒸馏中",
|
|
1035
|
+
finalizing: "收尾(强制 L2 场景 + L3 画像)",
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
function RebuildPanel(props) {
|
|
1039
|
+
var rpc = props.rpc;
|
|
1040
|
+
var rbState = react.useState(null);
|
|
1041
|
+
var rb = rbState[0];
|
|
1042
|
+
var setRb = rbState[1];
|
|
1043
|
+
var confirmState = react.useState(false);
|
|
1044
|
+
var confirmOpen = confirmState[0];
|
|
1045
|
+
var setConfirmOpen = confirmState[1];
|
|
1046
|
+
var busyState = react.useState(false);
|
|
1047
|
+
var busy = busyState[0];
|
|
1048
|
+
var setBusy = busyState[1];
|
|
1049
|
+
var errState = react.useState(null);
|
|
1050
|
+
var rbError = errState[0];
|
|
1051
|
+
var setRbError = errState[1];
|
|
1052
|
+
|
|
1053
|
+
var refresh = react.useCallback(function () {
|
|
1054
|
+
rpc("dsh-memory/rebuild-status", {})
|
|
1055
|
+
.then(function (r) {
|
|
1056
|
+
if (r && r.ok) setRb(r.value);
|
|
1057
|
+
})
|
|
1058
|
+
.catch(function () {});
|
|
1059
|
+
}, [rpc]);
|
|
1060
|
+
|
|
1061
|
+
react.useEffect(function () { refresh(); }, [refresh]);
|
|
1062
|
+
|
|
1063
|
+
// 运行中 1.5s 高频轮询进度;空闲不轮询(重新打开 Tab 时挂载刷新一次)
|
|
1064
|
+
var running = !!(rb && rb.running);
|
|
1065
|
+
react.useEffect(function () {
|
|
1066
|
+
if (!running) return;
|
|
1067
|
+
var timer = setInterval(refresh, 1500);
|
|
1068
|
+
return function () { clearInterval(timer); };
|
|
1069
|
+
}, [running, refresh]);
|
|
1070
|
+
|
|
1071
|
+
if (!rb || rb.supported === false) return null;
|
|
1072
|
+
ensureThemeStyle();
|
|
1073
|
+
|
|
1074
|
+
var start = function () {
|
|
1075
|
+
setBusy(true);
|
|
1076
|
+
setRbError(null);
|
|
1077
|
+
rpc("dsh-memory/rebuild-start", {})
|
|
1078
|
+
.then(function (r) {
|
|
1079
|
+
setBusy(false);
|
|
1080
|
+
if (r && r.ok) {
|
|
1081
|
+
setConfirmOpen(false);
|
|
1082
|
+
setRb(r.value);
|
|
1083
|
+
} else {
|
|
1084
|
+
setRbError(r && r.error ? r.error.message : "启动失败");
|
|
1085
|
+
}
|
|
1086
|
+
})
|
|
1087
|
+
.catch(function (e) {
|
|
1088
|
+
setBusy(false);
|
|
1089
|
+
setRbError(String((e && e.message) || e));
|
|
1090
|
+
});
|
|
1091
|
+
};
|
|
1092
|
+
var cancel = function () {
|
|
1093
|
+
setBusy(true);
|
|
1094
|
+
rpc("dsh-memory/rebuild-cancel", {})
|
|
1095
|
+
.then(function (r) {
|
|
1096
|
+
setBusy(false);
|
|
1097
|
+
if (r && r.ok) setRb(r.value);
|
|
1098
|
+
})
|
|
1099
|
+
.catch(function () {
|
|
1100
|
+
setBusy(false);
|
|
1101
|
+
});
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
var empty = !running && (rb.messageCount === 0 || rb.estCalls === 0);
|
|
1105
|
+
var pct = rb.total > 0 ? Math.round((rb.done / rb.total) * 100) : 0;
|
|
1106
|
+
|
|
1107
|
+
var lastNote = null;
|
|
1108
|
+
if (!running && rb.phase === "done") {
|
|
1109
|
+
lastNote =
|
|
1110
|
+
"上次重建:完成(" + rb.done + "/" + rb.total + " 会话,产出 " + rb.recordsBuilt + " 条记录)" +
|
|
1111
|
+
(rb.finishedAt ? " · " + fmtTime(new Date(rb.finishedAt).toISOString()) : "");
|
|
1112
|
+
} else if (!running && rb.phase === "cancelled") {
|
|
1113
|
+
lastNote = "上次重建:已取消(完成 " + rb.done + "/" + rb.total + " 会话,已重建部分保留)";
|
|
1114
|
+
} else if (!running && rb.phase === "failed") {
|
|
1115
|
+
lastNote = "上次重建:失败:" + (rb.error || "未知错误");
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return react.createElement(
|
|
1119
|
+
"div", { className: "dsh-mem-rb-card" },
|
|
1120
|
+
react.createElement(
|
|
1121
|
+
"div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" } },
|
|
1122
|
+
react.createElement("div", { style: { fontWeight: 600, whiteSpace: "nowrap" } }, "重建记忆"),
|
|
1123
|
+
react.createElement(
|
|
1124
|
+
"div", { className: "dsh-mem-rb-muted", style: { flex: 1, minWidth: 180 } },
|
|
1125
|
+
running
|
|
1126
|
+
? (RB_PHASE_LABEL[rb.phase] || rb.phase) + " · " + rb.done + "/" + rb.total + " 会话(" + pct + "%)"
|
|
1127
|
+
: "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)",
|
|
1128
|
+
),
|
|
1129
|
+
running
|
|
1130
|
+
? react.createElement(NButton, {
|
|
1131
|
+
disabled: busy || rb.cancelRequested,
|
|
1132
|
+
onClick: cancel,
|
|
1133
|
+
}, rb.cancelRequested ? "取消中…" : "取消重建")
|
|
1134
|
+
: react.createElement(NButton, {
|
|
1135
|
+
disabled: busy || empty,
|
|
1136
|
+
title: empty ? "L0 无消息,无可重建内容" : "重新蒸馏全部记忆",
|
|
1137
|
+
style: { color: "var(--dsh-mem-danger)" },
|
|
1138
|
+
onClick: function () { setConfirmOpen(true); },
|
|
1139
|
+
}, busy ? "…" : "开始重建"),
|
|
1140
|
+
),
|
|
1141
|
+
running
|
|
1142
|
+
? react.createElement(
|
|
1143
|
+
"div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 } },
|
|
1144
|
+
react.createElement(
|
|
1145
|
+
"div", { className: "dsh-mem-rb-bar" },
|
|
1146
|
+
react.createElement("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } }),
|
|
1147
|
+
),
|
|
1148
|
+
react.createElement(
|
|
1149
|
+
"span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" } },
|
|
1150
|
+
"产出 " + rb.recordsBuilt + " 条",
|
|
1151
|
+
),
|
|
1152
|
+
)
|
|
1153
|
+
: null,
|
|
1154
|
+
lastNote
|
|
1155
|
+
? react.createElement("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } }, lastNote)
|
|
1156
|
+
: null,
|
|
1157
|
+
rbError
|
|
1158
|
+
? react.createElement("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" } }, rbError)
|
|
1159
|
+
: null,
|
|
1160
|
+
confirmOpen
|
|
1161
|
+
? react.createElement(
|
|
1162
|
+
NModal,
|
|
1163
|
+
{
|
|
1164
|
+
open: true,
|
|
1165
|
+
onClose: function () { setConfirmOpen(false); },
|
|
1166
|
+
title: "确认重建全部记忆?",
|
|
1167
|
+
footer: [
|
|
1168
|
+
react.createElement(NButton, {
|
|
1169
|
+
key: "cancel",
|
|
1170
|
+
onClick: function () { setConfirmOpen(false); },
|
|
1171
|
+
}, "取消"),
|
|
1172
|
+
react.createElement(NButton, {
|
|
1173
|
+
key: "confirm",
|
|
1174
|
+
variant: "primary",
|
|
1175
|
+
disabled: busy,
|
|
1176
|
+
onClick: start,
|
|
1177
|
+
}, busy ? "启动中…" : "开始重建"),
|
|
1178
|
+
],
|
|
1179
|
+
},
|
|
1180
|
+
react.createElement(
|
|
1181
|
+
"div", null,
|
|
1182
|
+
"将以 L0 原始对话为事实源重新蒸馏:",
|
|
1183
|
+
react.createElement("b", null, rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息"),
|
|
1184
|
+
",预计 ≥" + rb.estCalls + " 次蒸馏调用。",
|
|
1185
|
+
),
|
|
1186
|
+
react.createElement(
|
|
1187
|
+
"div", { style: { marginTop: 8 } },
|
|
1188
|
+
"现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。",
|
|
1189
|
+
),
|
|
1190
|
+
)
|
|
1191
|
+
: null,
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// ── Tab:概览(开关面板 + 计数) ──
|
|
1196
|
+
function fmtMB(bytes) {
|
|
1197
|
+
if (!bytes || bytes <= 0) return "0MB";
|
|
1198
|
+
if (bytes < 1024 * 1024) return Math.round(bytes / 1024) + "KB";
|
|
1199
|
+
return (bytes / (1024 * 1024)).toFixed(bytes < 100 * 1024 * 1024 ? 1 : 0) + "MB";
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/** 嵌入源区块(D4 三态单选 + D7 下载进度 + D5 重嵌进度;1.2s 轮询,不能让用户傻等)。 */
|
|
1203
|
+
function EmbeddingSection(props) {
|
|
1204
|
+
var rpc = props.rpc;
|
|
1205
|
+
var stState = react.useState(null);
|
|
1206
|
+
var st = stState[0];
|
|
1207
|
+
var setSt = stState[1];
|
|
1208
|
+
var errState = react.useState(null);
|
|
1209
|
+
var err = errState[0];
|
|
1210
|
+
var setErr = errState[1];
|
|
1211
|
+
// 轮询退避标志(load 维护):snapshot 每次几十次 fs.stat,空闲常开 1.2s 是白烧
|
|
1212
|
+
var busyPollRef = react.useRef(null);
|
|
1213
|
+
|
|
1214
|
+
var load = react.useCallback(function () {
|
|
1215
|
+
rpc("dsh-memory/embedding-state-get", {})
|
|
1216
|
+
.then(function (r) {
|
|
1217
|
+
if (r && r.ok && r.value && r.value.supported !== false) {
|
|
1218
|
+
var v = r.value;
|
|
1219
|
+
if (busyPollRef.current) {
|
|
1220
|
+
busyPollRef.current.v = !!(
|
|
1221
|
+
(v.download && (v.download.phase === "downloading" || v.download.phase === "verifying")) ||
|
|
1222
|
+
v.apply.busy ||
|
|
1223
|
+
(v.reindex && v.reindex.running) ||
|
|
1224
|
+
(v.runtime && v.runtime.phase === "installing")
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
setSt(v);
|
|
1228
|
+
setErr(null);
|
|
1229
|
+
} else if (r && r.ok && r.value && r.value.supported === false) {
|
|
1230
|
+
setSt(null);
|
|
1231
|
+
setErr("__unsupported__");
|
|
1232
|
+
} else {
|
|
1233
|
+
setErr(r && r.error ? r.error.message : "RPC error");
|
|
1234
|
+
}
|
|
1235
|
+
})
|
|
1236
|
+
.catch(function (e) {
|
|
1237
|
+
setErr(String((e && e.message) || e));
|
|
1238
|
+
});
|
|
1239
|
+
}, [rpc]);
|
|
1240
|
+
|
|
1241
|
+
react.useEffect(function () { load(); }, [load]);
|
|
1242
|
+
react.useEffect(function () {
|
|
1243
|
+
var stopped = false;
|
|
1244
|
+
var busyFlag = { v: false };
|
|
1245
|
+
busyPollRef.current = busyFlag;
|
|
1246
|
+
var tick = function () {
|
|
1247
|
+
if (stopped) return;
|
|
1248
|
+
load();
|
|
1249
|
+
timer = setTimeout(tick, busyFlag.v ? 1200 : 5000);
|
|
1250
|
+
};
|
|
1251
|
+
var timer = setTimeout(tick, 1200);
|
|
1252
|
+
return function () {
|
|
1253
|
+
stopped = true;
|
|
1254
|
+
clearTimeout(timer);
|
|
1255
|
+
busyPollRef.current = null;
|
|
1256
|
+
};
|
|
1257
|
+
}, [load]);
|
|
1258
|
+
|
|
1259
|
+
var call = function (endpoint, payload, confirmText) {
|
|
1260
|
+
if (confirmText && !window.confirm(confirmText)) return;
|
|
1261
|
+
rpc(endpoint, payload)
|
|
1262
|
+
.then(function (r) {
|
|
1263
|
+
if (!r || !r.ok) setErr(r && r.error ? r.error.message : "操作失败");
|
|
1264
|
+
else { setErr(null); load(); }
|
|
1265
|
+
})
|
|
1266
|
+
.catch(function (e) { setErr(String((e && e.message) || e)); });
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
if (err === "__unsupported__") {
|
|
1270
|
+
return react.createElement(
|
|
1271
|
+
"div", { className: "dsh-mem-rb-card" },
|
|
1272
|
+
react.createElement("div", { style: { fontWeight: 600, marginBottom: 4 } }, "语义检索(嵌入)"),
|
|
1273
|
+
react.createElement("div", { className: "dsh-mem-rb-muted" }, "存储处于降级状态,嵌入管理不可用。"),
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
1276
|
+
if (!st) {
|
|
1277
|
+
return react.createElement(
|
|
1278
|
+
"div", { className: "dsh-mem-rb-card" },
|
|
1279
|
+
react.createElement("div", { className: "dsh-mem-rb-muted" }, err ? "嵌入状态读取失败:" + err : "嵌入状态读取中…"),
|
|
1280
|
+
err ? react.createElement(NButton, { style: { marginTop: 8 }, onClick: load }, "重试") : null,
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
var switchConfirm = "切换嵌入源后将按新模型重建向量索引(期间语义检索暂退化为关键词匹配,不影响对话)。确定切换?";
|
|
1285
|
+
var onSource = function (key) {
|
|
1286
|
+
if (key === "local") {
|
|
1287
|
+
// 本地档需要具体模型:有已下载模型直接用最新的启用;否则提示先下载
|
|
1288
|
+
var ready = [];
|
|
1289
|
+
for (var i = 0; i < st.models.length; i++) if (st.models[i].state === "downloaded") ready.push(st.models[i].id);
|
|
1290
|
+
if (ready.length === 0) {
|
|
1291
|
+
setErr("请先在下方下载一个本地嵌入模型,再切换到本地档。");
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
// 默认取目录序首个已下载模型(目录按轻→重排序,避免默认选中最重最慢的)
|
|
1295
|
+
var current = st.activeModel && ready.indexOf(st.activeModel) >= 0 ? st.activeModel : ready[0];
|
|
1296
|
+
call("dsh-memory/embedding-source-set", { source: "local", activeModel: current }, switchConfirm);
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
call("dsh-memory/embedding-source-set", { source: key }, key === "remote" ? switchConfirm : null);
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
var dl = st.download;
|
|
1303
|
+
var dlActive = dl && (dl.phase === "downloading" || dl.phase === "verifying");
|
|
1304
|
+
var ap = st.apply;
|
|
1305
|
+
var localInfo = st.local;
|
|
1306
|
+
var rt = st.runtime;
|
|
1307
|
+
|
|
1308
|
+
var runtimeRow = null;
|
|
1309
|
+
if (rt.phase === "installing") {
|
|
1310
|
+
runtimeRow = react.createElement(
|
|
1311
|
+
"div", { style: { marginTop: 8, fontSize: 12 } },
|
|
1312
|
+
react.createElement("div", { style: S.flexRow },
|
|
1313
|
+
react.createElement("span", null, "安装推理运行时中… 已耗时 " + Math.round(rt.elapsedMs / 1000) + "s(约 100~200MB,视网络)"),
|
|
1314
|
+
react.createElement("div", { style: S.grow }),
|
|
1315
|
+
react.createElement(NButton, { onClick: function () { call("dsh-memory/embedding-runtime-cancel", {}); } }, "取消"),
|
|
1316
|
+
),
|
|
1317
|
+
react.createElement(
|
|
1318
|
+
"pre",
|
|
1319
|
+
{ style: Object.assign({}, S.pre, { maxHeight: 68, marginTop: 6, fontSize: 11, opacity: 0.85 }) },
|
|
1320
|
+
(rt.lastLines || []).join("\n") || "等待 npm 输出…",
|
|
1321
|
+
),
|
|
1322
|
+
);
|
|
1323
|
+
} else if (rt.phase === "error") {
|
|
1324
|
+
runtimeRow = react.createElement(
|
|
1325
|
+
"div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" } },
|
|
1326
|
+
"运行时安装失败:" + (rt.error || "未知") + "(重新切换嵌入源可重试)",
|
|
1327
|
+
);
|
|
1328
|
+
} else if (rt.phase === "ready") {
|
|
1329
|
+
runtimeRow = react.createElement(
|
|
1330
|
+
"div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } },
|
|
1331
|
+
"推理运行时就绪(transformers.js v" + rt.installedVersion + ")",
|
|
1332
|
+
);
|
|
1333
|
+
} else if (st.source === "local") {
|
|
1334
|
+
runtimeRow = react.createElement(
|
|
1335
|
+
"div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } },
|
|
1336
|
+
"首次启用本地嵌入时会自动安装推理运行时(约 100~200MB)。",
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
var applyRow = null;
|
|
1341
|
+
if (ap.phase === "warming") {
|
|
1342
|
+
applyRow = react.createElement("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } }, "加载嵌入模型中…(首次需数秒)");
|
|
1343
|
+
} else if (ap.phase === "switching") {
|
|
1344
|
+
applyRow = react.createElement("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } }, "切换嵌入源中…");
|
|
1345
|
+
} else if (ap.phase === "error") {
|
|
1346
|
+
applyRow = react.createElement("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" } }, "切换失败:" + ap.message + "(已保存的嵌入源不变,重启后仍按原源运行)");
|
|
1347
|
+
} else if (st.reindex && st.reindex.running) {
|
|
1348
|
+
var rj = st.reindex;
|
|
1349
|
+
var rDone = rj.l1Done + rj.l0Done;
|
|
1350
|
+
var rTotal = rj.l1Total + rj.l0Total;
|
|
1351
|
+
var rPct = rTotal > 0 ? Math.round((rDone / rTotal) * 100) : 0;
|
|
1352
|
+
applyRow = react.createElement(
|
|
1353
|
+
"div", { style: { marginTop: 8 } },
|
|
1354
|
+
react.createElement("div", { style: S.flexRow },
|
|
1355
|
+
react.createElement("span", { className: "dsh-mem-rb-muted" },
|
|
1356
|
+
"重嵌入中 L1 " + rj.l1Done + "/" + rj.l1Total + " · L0 " + rj.l0Done + "/" + rj.l0Total + "(" + rPct + "%)"),
|
|
1357
|
+
react.createElement("div", { style: S.grow }),
|
|
1358
|
+
react.createElement(NButton, { onClick: function () { call("dsh-memory/embedding-reindex-cancel", {}); } }, "取消"),
|
|
1359
|
+
),
|
|
1360
|
+
react.createElement(
|
|
1361
|
+
"div", { style: Object.assign({}, S.flexRow, { marginTop: 6 }) },
|
|
1362
|
+
react.createElement("div", { className: "dsh-mem-rb-bar" }, react.createElement("div", { className: "dsh-mem-rb-fill", style: { width: rPct + "%" } })),
|
|
1363
|
+
),
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
var modelCards = st.models.map(function (m) {
|
|
1368
|
+
var isActive = st.source === "local" && st.activeModel === m.id;
|
|
1369
|
+
var mDl = dlActive && dl.modelId === m.id;
|
|
1370
|
+
var pct = mDl && dl.overallTotal > 0 ? Math.round((dl.overallReceived / dl.overallTotal) * 100) : 0;
|
|
1371
|
+
var action = null;
|
|
1372
|
+
if (mDl) {
|
|
1373
|
+
action = react.createElement(
|
|
1374
|
+
"div", { style: { flex: 1, minWidth: 200 } },
|
|
1375
|
+
react.createElement("div", { style: S.flexRow },
|
|
1376
|
+
react.createElement("span", { className: "dsh-mem-rb-muted", style: { whiteSpace: "nowrap" } },
|
|
1377
|
+
(dl.phase === "verifying" ? "校验中 " : "") +
|
|
1378
|
+
fmtMB(dl.overallReceived) + " / " + fmtMB(dl.overallTotal) + "(文件 " + dl.fileIndex + "/" + dl.fileCount + "," + pct + "%" +
|
|
1379
|
+
(dl.speedBps > 0 && dl.phase === "downloading" ? "," + fmtMB(dl.speedBps) + "/s" : "") + ")"),
|
|
1380
|
+
react.createElement("div", { style: S.grow }),
|
|
1381
|
+
react.createElement(NButton, { onClick: function () { call("dsh-memory/embedding-download-cancel", {}); } }, "取消"),
|
|
1382
|
+
),
|
|
1383
|
+
react.createElement(
|
|
1384
|
+
"div", { style: Object.assign({}, S.flexRow, { marginTop: 6 }) },
|
|
1385
|
+
react.createElement("div", { className: "dsh-mem-rb-bar" }, react.createElement("div", { className: "dsh-mem-rb-fill", style: { width: pct + "%" } })),
|
|
1386
|
+
),
|
|
1387
|
+
);
|
|
1388
|
+
} else if (isActive) {
|
|
1389
|
+
action = react.createElement(
|
|
1390
|
+
"div", { style: S.flexRow },
|
|
1391
|
+
react.createElement("span", { className: "dsh-mem-tag dsh-mem-tag-work-task" }, "使用中"),
|
|
1392
|
+
localInfo && localInfo.state === "loading" ? react.createElement("span", { className: "dsh-mem-rb-muted" }, "模型加载中…") : null,
|
|
1393
|
+
localInfo && localInfo.state === "failed"
|
|
1394
|
+
? react.createElement("span", { style: { fontSize: 12, color: "var(--dsh-mem-danger)" } }, "加载失败:" + (localInfo.error || ""))
|
|
1395
|
+
: null,
|
|
1396
|
+
localInfo && localInfo.state === "ready" ? react.createElement("span", { className: "dsh-mem-rb-muted" }, "已就绪") : null,
|
|
1397
|
+
);
|
|
1398
|
+
} else if (m.state === "downloaded") {
|
|
1399
|
+
action = react.createElement(
|
|
1400
|
+
"div", { style: S.flexRow },
|
|
1401
|
+
react.createElement(NButton, { disabled: ap.busy, onClick: function () {
|
|
1402
|
+
call("dsh-memory/embedding-source-set", { source: "local", activeModel: m.id }, switchConfirm);
|
|
1403
|
+
} }, "启用"),
|
|
1404
|
+
react.createElement(NButton, { disabled: dlActive, onClick: function () {
|
|
1405
|
+
call("dsh-memory/embedding-model-delete", { modelId: m.id }, "删除已下载的 " + m.name + "(" + fmtMB(m.totalBytes) + ")?");
|
|
1406
|
+
} }, "删除"),
|
|
1407
|
+
);
|
|
1408
|
+
} else {
|
|
1409
|
+
action = react.createElement(NButton, {
|
|
1410
|
+
disabled: dlActive || !st.ceilings.local,
|
|
1411
|
+
title: !st.ceilings.local ? "部署已禁用本地嵌入模型" : "",
|
|
1412
|
+
onClick: function () { call("dsh-memory/embedding-download-start", { modelId: m.id }); },
|
|
1413
|
+
}, (m.state === "partial" ? "继续下载 " : "下载 ") + fmtMB(m.totalBytes));
|
|
1414
|
+
}
|
|
1415
|
+
return react.createElement(
|
|
1416
|
+
"div",
|
|
1417
|
+
{ key: m.id, style: Object.assign({}, S.flexRow, { padding: "8px 0", borderBottom: "1px solid var(--dsh-mem-border)", flexWrap: "wrap" }) },
|
|
1418
|
+
react.createElement(
|
|
1419
|
+
"div", { style: { minWidth: 150 } },
|
|
1420
|
+
react.createElement("div", { style: { fontWeight: 600 } }, m.name),
|
|
1421
|
+
react.createElement("div", { className: "dsh-mem-rb-muted" }, m.tags.join(" · ") + " · " + m.dims + " 维 · 上下文 " + m.contextTokens),
|
|
1422
|
+
),
|
|
1423
|
+
react.createElement("div", { style: { flex: 1, minWidth: 180, fontSize: 12, color: "var(--dsh-mem-text-2)" } }, m.description),
|
|
1424
|
+
action,
|
|
1425
|
+
);
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
return react.createElement(
|
|
1429
|
+
"div", { className: "dsh-mem-rb-card" },
|
|
1430
|
+
react.createElement(
|
|
1431
|
+
"div", { style: S.flexRow },
|
|
1432
|
+
react.createElement("div", { style: { fontWeight: 600, whiteSpace: "nowrap" } }, "语义检索(嵌入源)"),
|
|
1433
|
+
react.createElement("div", { style: S.grow }),
|
|
1434
|
+
react.createElement(Segmented, {
|
|
1435
|
+
value: st.source,
|
|
1436
|
+
options: [
|
|
1437
|
+
{ key: "off", label: "关闭" },
|
|
1438
|
+
{ key: "local", label: "本地", disabled: !st.ceilings.local, disabledTitle: "部署已禁用本地嵌入模型" },
|
|
1439
|
+
{ key: "remote", label: "远程", disabled: !st.ceilings.remote, disabledTitle: "部署未配置远程嵌入(baseUrl/apiKey/model/dimensions)" },
|
|
1440
|
+
],
|
|
1441
|
+
onChange: onSource,
|
|
1442
|
+
}),
|
|
1443
|
+
),
|
|
1444
|
+
react.createElement(
|
|
1445
|
+
"div", { className: "dsh-mem-rb-muted", style: { marginTop: 4 } },
|
|
1446
|
+
st.source === "off" ? "当前:关键词(BM25)检索,不做向量嵌入"
|
|
1447
|
+
: st.source === "remote" ? "当前:远程嵌入(" + (rt ? "" : "") + "模型由部署配置给定)"
|
|
1448
|
+
: "当前:本地嵌入" + (st.activeModel ? "(" + st.activeModel + ")" : ""),
|
|
1449
|
+
),
|
|
1450
|
+
st.activeNote
|
|
1451
|
+
? react.createElement("div", { style: { marginTop: 4, fontSize: 12, color: "var(--dsh-mem-danger)" } }, st.activeNote)
|
|
1452
|
+
: null,
|
|
1453
|
+
err && err !== "__unsupported__"
|
|
1454
|
+
? react.createElement("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" } }, err)
|
|
1455
|
+
: null,
|
|
1456
|
+
dl && dl.phase === "error"
|
|
1457
|
+
? react.createElement("div", { style: { marginTop: 6, fontSize: 12, color: "var(--dsh-mem-danger)" } }, "下载失败:" + (dl.error || ""))
|
|
1458
|
+
: null,
|
|
1459
|
+
runtimeRow,
|
|
1460
|
+
applyRow,
|
|
1461
|
+
react.createElement("div", { style: S.panelLabel }, "本地模型目录(下载后离线可用,不随插件分发)"),
|
|
1462
|
+
modelCards,
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
668
1466
|
// ── Tab:概览(开关面板 + 计数) ──
|
|
669
1467
|
function OverviewTab(props) {
|
|
670
1468
|
var rpc = props.rpc;
|
|
@@ -721,22 +1519,27 @@ window.__ModuleLoader__.load({
|
|
|
721
1519
|
});
|
|
722
1520
|
};
|
|
723
1521
|
|
|
724
|
-
|
|
1522
|
+
// 统计分两段:计数瓦片(一眼可读)+ 明细行(目录/版本/时间线/队列)
|
|
1523
|
+
var tiles = [];
|
|
1524
|
+
var infos = [];
|
|
725
1525
|
if (stats) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
1526
|
+
var th = stats.thresholds || {};
|
|
1527
|
+
tiles.push({ num: String(stats.l0Today), label: "L0 今日消息" });
|
|
1528
|
+
tiles.push({ num: String(stats.l1Count), label: "L1 原子记忆" });
|
|
1529
|
+
tiles.push({ num: String(stats.sceneCount), label: "L2 场景块" });
|
|
1530
|
+
tiles.push({ num: String(stats.pendingExtract), label: "待重试消息" });
|
|
1531
|
+
infos.push(["数据目录", stats.dataDir]);
|
|
1532
|
+
infos.push(["插件版本", "v" + stats.version]);
|
|
1533
|
+
infos.push(["默认档", modeLabel(stats.family)]);
|
|
1534
|
+
infos.push(["L1 累计抽取", String(stats.l1TotalExtracted)]);
|
|
1535
|
+
infos.push(["L3 画像", stats.personaChars > 0 ? stats.personaChars + " 字符" : "未生成"]);
|
|
1536
|
+
infos.push(["上次 L1 抽取", fmtTime(stats.lastExtractAt)]);
|
|
1537
|
+
infos.push(["上次 L2 整合", fmtTime(stats.lastL2At)]);
|
|
1538
|
+
infos.push(["上次 L3 蒸馏", fmtTime(stats.lastL3At)]);
|
|
1539
|
+
infos.push(["待 L2 新记忆", stats.memoriesSinceL2 + " / " + (th.l2MinNewMemories != null ? th.l2MinNewMemories : 5)]);
|
|
1540
|
+
infos.push(["待 L3 新记忆", stats.memoriesSinceL3 + " / " + (th.l3Interval != null ? th.l3Interval : 20)]);
|
|
739
1541
|
}
|
|
1542
|
+
var degraded = stats && stats.message && stats.message !== "running";
|
|
740
1543
|
|
|
741
1544
|
var master = settingsData && settingsData.settings ? settingsData.settings.enabled : true;
|
|
742
1545
|
var ceilingNote = "";
|
|
@@ -812,23 +1615,41 @@ window.__ModuleLoader__.load({
|
|
|
812
1615
|
: null,
|
|
813
1616
|
)
|
|
814
1617
|
: null,
|
|
1618
|
+
react.createElement(EmbeddingSection, { rpc: rpc }),
|
|
1619
|
+
react.createElement(RebuildPanel, { rpc: rpc }),
|
|
1620
|
+
degraded
|
|
1621
|
+
? react.createElement(
|
|
1622
|
+
"div",
|
|
1623
|
+
{ style: Object.assign({}, S.error, { marginBottom: 10 }) },
|
|
1624
|
+
"⚠ " + stats.message + "。上方数据为最后一次成功读取的值,记忆功能当前未工作。",
|
|
1625
|
+
)
|
|
1626
|
+
: null,
|
|
815
1627
|
error
|
|
816
1628
|
? react.createElement("div", { style: S.error }, "获取状态失败:" + error)
|
|
817
1629
|
: !stats
|
|
818
1630
|
? react.createElement("p", { style: S.intro }, "正在读取记忆状态…")
|
|
819
1631
|
: react.createElement(
|
|
820
|
-
"
|
|
821
|
-
{ style: S.
|
|
1632
|
+
"div", null,
|
|
1633
|
+
react.createElement("div", { style: S.panelLabel }, "记忆概况"),
|
|
822
1634
|
react.createElement(
|
|
823
|
-
"
|
|
824
|
-
|
|
1635
|
+
"div", { style: S.statGrid },
|
|
1636
|
+
tiles.map(function (t) {
|
|
825
1637
|
return react.createElement(
|
|
826
|
-
"
|
|
827
|
-
|
|
828
|
-
react.createElement("
|
|
1638
|
+
"div",
|
|
1639
|
+
{ key: t.label, className: "dsh-mem-card", style: S.statTile },
|
|
1640
|
+
react.createElement("div", { style: S.statNum }, t.num),
|
|
1641
|
+
react.createElement("div", { style: S.statLabel }, t.label),
|
|
829
1642
|
);
|
|
830
1643
|
}),
|
|
831
1644
|
),
|
|
1645
|
+
react.createElement("div", { style: S.panelLabel }, "运行状态"),
|
|
1646
|
+
infos.map(function (row) {
|
|
1647
|
+
return react.createElement(
|
|
1648
|
+
"div", { key: row[0], style: S.infoRow },
|
|
1649
|
+
react.createElement("span", { style: S.infoKey }, row[0]),
|
|
1650
|
+
react.createElement("span", { style: S.infoVal }, row[1]),
|
|
1651
|
+
);
|
|
1652
|
+
}),
|
|
832
1653
|
),
|
|
833
1654
|
react.createElement(
|
|
834
1655
|
"p", { style: S.hint },
|
|
@@ -857,6 +1678,9 @@ window.__ModuleLoader__.load({
|
|
|
857
1678
|
var loadingState = react.useState(false);
|
|
858
1679
|
var loading = loadingState[0];
|
|
859
1680
|
var setLoading = loadingState[1];
|
|
1681
|
+
var truncState = react.useState(false);
|
|
1682
|
+
var truncated = truncState[0];
|
|
1683
|
+
var setTruncated = truncState[1];
|
|
860
1684
|
var errState = react.useState(null);
|
|
861
1685
|
var error = errState[0];
|
|
862
1686
|
var setError = errState[1];
|
|
@@ -879,15 +1703,20 @@ window.__ModuleLoader__.load({
|
|
|
879
1703
|
var last = lastState[0];
|
|
880
1704
|
var setLast = lastState[1];
|
|
881
1705
|
|
|
1706
|
+
// 请求序列号:快速连续搜索/翻页时丢弃过期响应(慢的旧响应不得覆盖新结果)
|
|
1707
|
+
var seqRef = react.useRef(0);
|
|
1708
|
+
|
|
882
1709
|
var fetchPage = react.useCallback(function (conds, offset, append) {
|
|
883
1710
|
setLoading(true);
|
|
884
1711
|
setError(null);
|
|
1712
|
+
var token = ++seqRef.current;
|
|
885
1713
|
var payload = { limit: limit, offset: offset };
|
|
886
1714
|
if (conds.query) payload.query = conds.query;
|
|
887
1715
|
if (conds.type) payload.type = conds.type;
|
|
888
1716
|
if (conds.scene) payload.scene = conds.scene;
|
|
889
1717
|
rpc("dsh-memory/list-records", payload)
|
|
890
1718
|
.then(function (r) {
|
|
1719
|
+
if (token !== seqRef.current) return;
|
|
891
1720
|
setLoading(false);
|
|
892
1721
|
if (!r || !r.ok) {
|
|
893
1722
|
setError(r && r.error ? r.error.message : "RPC error");
|
|
@@ -897,9 +1726,11 @@ window.__ModuleLoader__.load({
|
|
|
897
1726
|
setItems(function (prev) { return append ? prev.concat(v.items) : v.items; });
|
|
898
1727
|
setHasMore(!!v.hasMore);
|
|
899
1728
|
setTotal(v.total === undefined || v.total === null ? null : v.total);
|
|
1729
|
+
setTruncated(!!v.truncated);
|
|
900
1730
|
if (v.scenes) setSceneOptions(v.scenes);
|
|
901
1731
|
})
|
|
902
1732
|
.catch(function (e) {
|
|
1733
|
+
if (token !== seqRef.current) return;
|
|
903
1734
|
setLoading(false);
|
|
904
1735
|
setError(String((e && e.message) || e));
|
|
905
1736
|
});
|
|
@@ -927,8 +1758,8 @@ window.__ModuleLoader__.load({
|
|
|
927
1758
|
"div", null,
|
|
928
1759
|
react.createElement(
|
|
929
1760
|
"div", { style: S.toolbar },
|
|
930
|
-
react.createElement(
|
|
931
|
-
style:
|
|
1761
|
+
react.createElement(NInput, {
|
|
1762
|
+
style: { flex: 1, minWidth: 160 },
|
|
932
1763
|
placeholder: "搜索记忆内容(BM25 关键词)…",
|
|
933
1764
|
value: query,
|
|
934
1765
|
onChange: function (e) { setQuery(e.target.value); },
|
|
@@ -937,7 +1768,7 @@ window.__ModuleLoader__.load({
|
|
|
937
1768
|
react.createElement(
|
|
938
1769
|
"select",
|
|
939
1770
|
{
|
|
940
|
-
|
|
1771
|
+
className: "dsh-mem-select",
|
|
941
1772
|
value: typeFilter,
|
|
942
1773
|
onChange: function (e) { setTypeFilter(e.target.value); },
|
|
943
1774
|
},
|
|
@@ -949,7 +1780,7 @@ window.__ModuleLoader__.load({
|
|
|
949
1780
|
react.createElement(
|
|
950
1781
|
"select",
|
|
951
1782
|
{
|
|
952
|
-
|
|
1783
|
+
className: "dsh-mem-select",
|
|
953
1784
|
value: sceneFilter,
|
|
954
1785
|
onChange: function (e) { setSceneFilter(e.target.value); },
|
|
955
1786
|
},
|
|
@@ -958,18 +1789,24 @@ window.__ModuleLoader__.load({
|
|
|
958
1789
|
return react.createElement("option", { key: s, value: s }, s.length > 24 ? s.slice(0, 24) + "…" : s);
|
|
959
1790
|
}),
|
|
960
1791
|
),
|
|
961
|
-
react.createElement(
|
|
1792
|
+
react.createElement(NButton, { onClick: search }, "搜索"),
|
|
962
1793
|
),
|
|
963
1794
|
react.createElement(
|
|
964
1795
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
965
1796
|
react.createElement("span", { style: S.muted }, loading ? "加载中…" : countText),
|
|
966
1797
|
react.createElement("div", { style: S.grow }),
|
|
967
|
-
react.createElement(
|
|
968
|
-
style: S.button,
|
|
1798
|
+
react.createElement(NButton, {
|
|
969
1799
|
onClick: function () { fetchPage(last, 0, false); },
|
|
970
1800
|
}, "刷新"),
|
|
971
1801
|
),
|
|
972
1802
|
error ? react.createElement("div", { style: S.error }, error) : null,
|
|
1803
|
+
truncated
|
|
1804
|
+
? react.createElement(
|
|
1805
|
+
"div",
|
|
1806
|
+
{ style: S.hint },
|
|
1807
|
+
"搜索分页已达检索上限(200 条),更早的结果未显示。请用更精确的关键词或类型/情境过滤。",
|
|
1808
|
+
)
|
|
1809
|
+
: null,
|
|
973
1810
|
items.length === 0 && !loading && !error
|
|
974
1811
|
? react.createElement("p", { style: S.intro }, "暂无记忆。对话几轮后,蒸馏管线会自动抽取记忆。")
|
|
975
1812
|
: items.map(function (m) {
|
|
@@ -978,18 +1815,15 @@ window.__ModuleLoader__.load({
|
|
|
978
1815
|
"div",
|
|
979
1816
|
{
|
|
980
1817
|
key: m.id,
|
|
981
|
-
|
|
1818
|
+
className: "dsh-mem-card dsh-mem-card-hover",
|
|
1819
|
+
style: Object.assign({}, S.card, { cursor: "pointer" }),
|
|
982
1820
|
onClick: function () { setExpandedId(open ? null : m.id); },
|
|
983
1821
|
},
|
|
984
1822
|
react.createElement(
|
|
985
1823
|
"div", { style: S.cardHead },
|
|
986
1824
|
react.createElement(
|
|
987
1825
|
"span",
|
|
988
|
-
{
|
|
989
|
-
style: Object.assign({}, S.tag, {
|
|
990
|
-
background: TYPE_COLORS[m.type] || "#666",
|
|
991
|
-
}),
|
|
992
|
-
},
|
|
1826
|
+
{ className: "dsh-mem-tag dsh-mem-tag-" + m.type },
|
|
993
1827
|
TYPE_LABELS[m.type] || m.type,
|
|
994
1828
|
),
|
|
995
1829
|
react.createElement("span", { style: S.muted }, "优先级 " + m.priority),
|
|
@@ -1004,13 +1838,13 @@ window.__ModuleLoader__.load({
|
|
|
1004
1838
|
? react.createElement(
|
|
1005
1839
|
"div", { style: S.detail },
|
|
1006
1840
|
"id: " + m.id + "\n" +
|
|
1007
|
-
"情境: " + (m.scene || "
|
|
1841
|
+
"情境: " + (m.scene || "-") + "\n" +
|
|
1008
1842
|
"版本: v" + m.version + "(去重合并次数 " + m.version + ")\n" +
|
|
1009
1843
|
"创建: " + fmtTime(m.createdAt) + "\n" +
|
|
1010
|
-
"活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "
|
|
1844
|
+
"活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" +
|
|
1011
1845
|
(m.sourceMessageIds && m.sourceMessageIds.length > 0
|
|
1012
1846
|
? "来源消息: " + m.sourceMessageIds.join(", ")
|
|
1013
|
-
: "来源消息:
|
|
1847
|
+
: "来源消息: -"),
|
|
1014
1848
|
)
|
|
1015
1849
|
: null,
|
|
1016
1850
|
);
|
|
@@ -1019,9 +1853,11 @@ window.__ModuleLoader__.load({
|
|
|
1019
1853
|
? react.createElement(
|
|
1020
1854
|
"div", { style: S.flexRow },
|
|
1021
1855
|
react.createElement("div", { style: S.grow }),
|
|
1022
|
-
react.createElement(
|
|
1023
|
-
|
|
1024
|
-
onClick: function () {
|
|
1856
|
+
react.createElement(NButton, {
|
|
1857
|
+
disabled: loading,
|
|
1858
|
+
onClick: function () {
|
|
1859
|
+
if (!loading) fetchPage(last, items.length, true);
|
|
1860
|
+
},
|
|
1025
1861
|
}, loading ? "加载中…" : "加载更多"),
|
|
1026
1862
|
)
|
|
1027
1863
|
: null,
|
|
@@ -1055,7 +1891,7 @@ window.__ModuleLoader__.load({
|
|
|
1055
1891
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1056
1892
|
react.createElement("span", { style: S.muted }, items ? items.length + " 个场景块" : "加载中…"),
|
|
1057
1893
|
react.createElement("div", { style: S.grow }),
|
|
1058
|
-
react.createElement(
|
|
1894
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1059
1895
|
),
|
|
1060
1896
|
error ? react.createElement("div", { style: S.error }, error) : null,
|
|
1061
1897
|
items && items.length === 0
|
|
@@ -1063,7 +1899,7 @@ window.__ModuleLoader__.load({
|
|
|
1063
1899
|
: null,
|
|
1064
1900
|
(items || []).map(function (s) {
|
|
1065
1901
|
return react.createElement(
|
|
1066
|
-
"div", { key: s.path, style: S.card },
|
|
1902
|
+
"div", { key: s.path, className: "dsh-mem-card", style: S.card },
|
|
1067
1903
|
react.createElement(
|
|
1068
1904
|
"div", { style: S.sceneHead },
|
|
1069
1905
|
react.createElement("span", { style: S.sceneTitle }, s.path),
|
|
@@ -1090,11 +1926,18 @@ window.__ModuleLoader__.load({
|
|
|
1090
1926
|
var contentState = react.useState(null);
|
|
1091
1927
|
var content = contentState[0];
|
|
1092
1928
|
var setContent = contentState[1];
|
|
1929
|
+
var errState = react.useState(null);
|
|
1930
|
+
var error = errState[0];
|
|
1931
|
+
var setError = errState[1];
|
|
1093
1932
|
|
|
1094
1933
|
var load = react.useCallback(function () {
|
|
1934
|
+
setError(null);
|
|
1095
1935
|
rpc("dsh-memory/persona", {})
|
|
1096
|
-
.then(function (r) {
|
|
1097
|
-
|
|
1936
|
+
.then(function (r) {
|
|
1937
|
+
if (r && r.ok) setContent(r.value.content);
|
|
1938
|
+
else setError(r && r.error ? r.error.message : "RPC error");
|
|
1939
|
+
})
|
|
1940
|
+
.catch(function (e) { setError(String((e && e.message) || e)); });
|
|
1098
1941
|
}, [rpc]);
|
|
1099
1942
|
|
|
1100
1943
|
react.useEffect(function () { load(); }, [load]);
|
|
@@ -1104,10 +1947,17 @@ window.__ModuleLoader__.load({
|
|
|
1104
1947
|
react.createElement(
|
|
1105
1948
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1106
1949
|
react.createElement("span", { style: S.muted },
|
|
1107
|
-
content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像"),
|
|
1950
|
+
error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像"),
|
|
1108
1951
|
react.createElement("div", { style: S.grow }),
|
|
1109
|
-
react.createElement(
|
|
1952
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1110
1953
|
),
|
|
1954
|
+
error
|
|
1955
|
+
? react.createElement(
|
|
1956
|
+
"div",
|
|
1957
|
+
{ style: Object.assign({}, S.error, { marginBottom: 10 }) },
|
|
1958
|
+
"画像读取失败:" + error + "(点右上“刷新”重试)",
|
|
1959
|
+
)
|
|
1960
|
+
: null,
|
|
1111
1961
|
content
|
|
1112
1962
|
? react.createElement("pre", { style: S.pre }, content)
|
|
1113
1963
|
: content === null
|
|
@@ -1122,11 +1972,18 @@ window.__ModuleLoader__.load({
|
|
|
1122
1972
|
var linesState = react.useState(null);
|
|
1123
1973
|
var lines = linesState[0];
|
|
1124
1974
|
var setLines = linesState[1];
|
|
1975
|
+
var errState = react.useState(null);
|
|
1976
|
+
var error = errState[0];
|
|
1977
|
+
var setError = errState[1];
|
|
1125
1978
|
|
|
1126
1979
|
var load = react.useCallback(function () {
|
|
1980
|
+
setError(null);
|
|
1127
1981
|
rpc("dsh-memory/log-tail", { lines: 200 })
|
|
1128
|
-
.then(function (r) {
|
|
1129
|
-
|
|
1982
|
+
.then(function (r) {
|
|
1983
|
+
if (r && r.ok) setLines(r.value.lines);
|
|
1984
|
+
else setError(r && r.error ? r.error.message : "RPC error");
|
|
1985
|
+
})
|
|
1986
|
+
.catch(function (e) { setError(String((e && e.message) || e)); });
|
|
1130
1987
|
}, [rpc]);
|
|
1131
1988
|
|
|
1132
1989
|
react.useEffect(function () { load(); }, [load]);
|
|
@@ -1135,11 +1992,17 @@ window.__ModuleLoader__.load({
|
|
|
1135
1992
|
"div", null,
|
|
1136
1993
|
react.createElement(
|
|
1137
1994
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1138
|
-
react.createElement("span", { style: S.muted }, lines === null ? "加载中…" : "最近 " + lines.length + " 行(memory.log)"),
|
|
1995
|
+
react.createElement("span", { style: S.muted }, error ? "加载失败" : lines === null ? "加载中…" : "最近 " + lines.length + " 行(memory.log)"),
|
|
1139
1996
|
react.createElement("div", { style: S.grow }),
|
|
1140
|
-
react.createElement(
|
|
1997
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1141
1998
|
),
|
|
1142
|
-
|
|
1999
|
+
error
|
|
2000
|
+
? react.createElement(
|
|
2001
|
+
"div",
|
|
2002
|
+
{ style: Object.assign({}, S.error, { marginBottom: 10 }) },
|
|
2003
|
+
"日志读取失败:" + error + "(点右上“刷新”重试)",
|
|
2004
|
+
)
|
|
2005
|
+
: react.createElement("pre", { style: S.pre }, (lines || []).join("\n") || "(暂无日志)"),
|
|
1143
2006
|
);
|
|
1144
2007
|
}
|
|
1145
2008
|
|
|
@@ -1152,12 +2015,66 @@ window.__ModuleLoader__.load({
|
|
|
1152
2015
|
["log", "日志"],
|
|
1153
2016
|
];
|
|
1154
2017
|
|
|
2018
|
+
// ── 设置页侧边栏 icon:宿主 navIcon() 按 section id 硬编码白名单(slot 注册对象
|
|
2019
|
+
// 无 icon 字段),"dsh-memory" 落齿轮兜底。受控 DOM 补丁:把侧边栏"记忆"按钮的
|
|
2020
|
+
// 齿轮换成书本 icon。宿主只在分节激活时渲染我们的面板,所以补丁不能依赖
|
|
2021
|
+
// MemoryPanel 挂载——由常驻的输入栏 pill 驱动一个 body 级防抖观察器(全局单例,
|
|
2022
|
+
// 应用生命周期存续,观察 body 子树 childList):设置页随时打开、侧边栏随时
|
|
2023
|
+
// 重渲染都能打上/重打补丁。找不到或 DOM 结构变化时静默保持原生齿轮。 ──
|
|
2024
|
+
var BOOK_ICON_SVG =
|
|
2025
|
+
'<svg data-mem-icon="1" viewBox="0 0 16 16" width="16" height="16" fill="none" ' +
|
|
2026
|
+
'xmlns="http://www.w3.org/2000/svg" style="flex-shrink:0">' +
|
|
2027
|
+
'<path d="M8 3.4C6.6 2.5 4.6 2.4 2.9 3.1v9.3c1.7-.7 3.7-.6 5.1.3 1.4-.9 3.4-1 5.1-.3V3.1C11.4 2.4 9.4 2.5 8 3.4Z" ' +
|
|
2028
|
+
'stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>' +
|
|
2029
|
+
'<path d="M8 3.4v9.3" stroke="currentColor" stroke-width="1.2"/></svg>';
|
|
2030
|
+
|
|
2031
|
+
function patchSidebarIcon() {
|
|
2032
|
+
try {
|
|
2033
|
+
var buttons = document.querySelectorAll("button");
|
|
2034
|
+
for (var i = 0; i < buttons.length; i++) {
|
|
2035
|
+
var b = buttons[i];
|
|
2036
|
+
var span = b.querySelector("span");
|
|
2037
|
+
var svg = b.querySelector("svg");
|
|
2038
|
+
// 侧边栏导航项 = [svg 图标, span 标签文本];输入栏 pill 的文本形态不同("记忆 · X")
|
|
2039
|
+
if (span && svg && span.textContent.trim() === "记忆" && svg.getAttribute("data-mem-icon") !== "1") {
|
|
2040
|
+
svg.outerHTML = BOOK_ICON_SVG;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
} catch (e) {
|
|
2044
|
+
/* best-effort:失败保持原生齿轮 */
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// body 子树变更频繁(对话流式渲染),补丁扫描走 250ms 尾随防抖
|
|
2049
|
+
var sidebarIconTimer = null;
|
|
2050
|
+
function scheduleSidebarPatch() {
|
|
2051
|
+
if (sidebarIconTimer !== null) return;
|
|
2052
|
+
sidebarIconTimer = setTimeout(function () {
|
|
2053
|
+
sidebarIconTimer = null;
|
|
2054
|
+
patchSidebarIcon();
|
|
2055
|
+
}, 250);
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
function watchSidebarIcon() {
|
|
2059
|
+
patchSidebarIcon();
|
|
2060
|
+
try {
|
|
2061
|
+
if (document.body.getAttribute("data-mem-icon-bodywatch") === "1") return;
|
|
2062
|
+
document.body.setAttribute("data-mem-icon-bodywatch", "1");
|
|
2063
|
+
new MutationObserver(scheduleSidebarPatch).observe(document.body, { childList: true, subtree: true });
|
|
2064
|
+
} catch (e) {
|
|
2065
|
+
/* ignore */
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
|
|
1155
2069
|
function MemoryPanel(props) {
|
|
1156
2070
|
var rpc = props.rpc;
|
|
1157
2071
|
var tabState = react.useState("overview");
|
|
1158
2072
|
var tab = tabState[0];
|
|
1159
2073
|
var setTab = tabState[1];
|
|
1160
2074
|
|
|
2075
|
+
ensureThemeStyle(); // 设置页挂载即注入主题令牌与组件样式
|
|
2076
|
+
react.useEffect(function () { watchSidebarIcon(); }, []);
|
|
2077
|
+
|
|
1161
2078
|
var body;
|
|
1162
2079
|
if (tab === "overview") body = react.createElement(OverviewTab, { rpc: rpc });
|
|
1163
2080
|
else if (tab === "records") body = react.createElement(RecordsTab, { rpc: rpc });
|
|
@@ -1167,7 +2084,7 @@ window.__ModuleLoader__.load({
|
|
|
1167
2084
|
|
|
1168
2085
|
return react.createElement(
|
|
1169
2086
|
"div",
|
|
1170
|
-
{ style: S.section },
|
|
2087
|
+
{ className: "dsh-mem-root", style: S.section },
|
|
1171
2088
|
react.createElement("h2", { style: S.heading }, "记忆 (Memory)"),
|
|
1172
2089
|
react.createElement(
|
|
1173
2090
|
"p", { style: S.intro },
|
|
@@ -1180,7 +2097,7 @@ window.__ModuleLoader__.load({
|
|
|
1180
2097
|
"button",
|
|
1181
2098
|
{
|
|
1182
2099
|
key: t[0],
|
|
1183
|
-
|
|
2100
|
+
className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
|
|
1184
2101
|
onClick: function () { setTab(t[0]); },
|
|
1185
2102
|
},
|
|
1186
2103
|
t[1],
|