dsh-layered-memory 0.6.1 → 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 +88 -80
- 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 +1100 -402
- 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 +107 -44
- package/dist/pipeline/rebuild.d.ts +2 -0
- package/dist/pipeline/rebuild.js +7 -0
- package/dist/pipeline/runner.d.ts +7 -1
- package/dist/pipeline/runner.js +22 -7
- package/dist/settings.js +79 -20
- package/dist/stats.d.ts +7 -1
- package/dist/stats.js +131 -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/runtime-installer.d.ts +60 -0
- package/dist/store/runtime-installer.js +181 -0
- package/dist/store/sqlite.d.ts +53 -7
- package/dist/store/sqlite.js +339 -72
- 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",
|
|
166
|
+
marginBottom: 14,
|
|
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,
|
|
161
178
|
marginBottom: 14,
|
|
162
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,149 +593,305 @@ 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
|
-
//
|
|
497
|
-
//
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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;
|
|
501
691
|
var el = document.createElement("style");
|
|
502
|
-
el.id =
|
|
692
|
+
el.id = THEME_STYLE_ID;
|
|
503
693
|
el.textContent = [
|
|
504
|
-
// conic 角度动画需要 @property
|
|
694
|
+
// conic 角度动画需要 @property 注册才能插值;不支持 @property 的浏览器里
|
|
695
|
+
// var(--dsh-mem-angle) 无定义 → conic 层失效 → 整条 background 退化为无背景
|
|
696
|
+
// (光带边框与内底一并消失,仅剩文字色)。2026 常青浏览器均已支持,仅作记录。
|
|
505
697
|
"@property --dsh-mem-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }",
|
|
506
698
|
"@keyframes dshMemFlow { to { --dsh-mem-angle: 360deg; } }",
|
|
507
|
-
//
|
|
508
|
-
//
|
|
509
|
-
//
|
|
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)
|
|
510
820
|
".dsh-mem-flow {",
|
|
511
821
|
" border: 1px solid transparent;",
|
|
512
822
|
" background:",
|
|
513
823
|
" linear-gradient(",
|
|
514
|
-
" color-mix(in srgb, var(--
|
|
515
|
-
" 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))",
|
|
516
826
|
" ) padding-box,",
|
|
517
827
|
" conic-gradient(from var(--dsh-mem-angle),",
|
|
518
|
-
" rgba(
|
|
519
|
-
" 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;",
|
|
520
830
|
" animation: dshMemFlow 3s linear infinite;",
|
|
521
|
-
" color: #0284c7;",
|
|
522
831
|
"}",
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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 {",
|
|
529
841
|
" border-radius: 12px;",
|
|
530
|
-
"
|
|
531
|
-
"
|
|
532
|
-
"
|
|
533
|
-
" -
|
|
534
|
-
" backdrop-filter: blur(32px) saturate(180%);",
|
|
535
|
-
" border: 1px solid rgba(255,255,255,0.55);",
|
|
536
|
-
" box-shadow:",
|
|
537
|
-
" 0 12px 40px rgba(0,0,0,0.16), 0 2px 8px rgba(0,0,0,0.07),",
|
|
538
|
-
" 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);",
|
|
539
846
|
"}",
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
"
|
|
546
|
-
"
|
|
547
|
-
"
|
|
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);",
|
|
548
858
|
"}",
|
|
549
|
-
|
|
550
|
-
|
|
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);",
|
|
551
866
|
"}",
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
"
|
|
555
|
-
"
|
|
556
|
-
"
|
|
557
|
-
" background: var(--dsw-alias-bg-layer-2, #ffffff);",
|
|
558
|
-
" padding: 12px 14px;",
|
|
559
|
-
" margin-bottom: 14px;",
|
|
560
|
-
" font-size: 13px;",
|
|
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);",
|
|
561
872
|
"}",
|
|
562
|
-
|
|
563
|
-
"
|
|
564
|
-
|
|
565
|
-
"
|
|
566
|
-
"
|
|
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;",
|
|
567
879
|
"}",
|
|
568
|
-
".dsh-mem-rb-
|
|
569
|
-
".dsh-mem-rb-
|
|
570
|
-
"body[data-ds-dark-theme] .dsh-mem-rb-danger { color: #f4707b; border-color: rgba(244,112,123,0.5); }",
|
|
571
|
-
".dsh-mem-rb-primary { background: #cf222e; border-color: #cf222e; color: #fff; }",
|
|
572
|
-
"body[data-ds-dark-theme] .dsh-mem-rb-primary { background: #b62324; border-color: #b62324; }",
|
|
573
|
-
".dsh-mem-rb-bar { height: 8px; border-radius: 4px; overflow: hidden; flex: 1;",
|
|
574
|
-
" background: var(--dsw-alias-border-l2, #e8ebef); }",
|
|
575
|
-
"body[data-ds-dark-theme] .dsh-mem-rb-bar { background: rgba(255,255,255,0.12); }",
|
|
576
|
-
".dsh-mem-rb-fill { height: 100%; border-radius: 4px;",
|
|
577
|
-
" background: linear-gradient(90deg, #0969da, #8250df); transition: width .4s ease; }",
|
|
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; }",
|
|
578
882
|
".dsh-mem-rb-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35);",
|
|
579
883
|
" display: flex; align-items: center; justify-content: center; z-index: 2000; }",
|
|
580
884
|
".dsh-mem-rb-modal { width: 440px; max-width: calc(100vw - 48px); border-radius: 12px;",
|
|
581
885
|
" padding: 18px 20px; font-size: 13px; line-height: 1.6;",
|
|
582
|
-
" border: 1px solid var(--
|
|
583
|
-
" background: var(--dsw-alias-bg-layer-2, #ffffff);",
|
|
886
|
+
" border: 1px solid var(--dsh-mem-border); background: var(--dsh-mem-bg-card); color: var(--dsh-mem-text-1);",
|
|
584
887
|
" box-shadow: 0 16px 48px rgba(0,0,0,0.24); }",
|
|
585
888
|
"body[data-ds-dark-theme] .dsh-mem-rb-modal { box-shadow: 0 16px 48px rgba(0,0,0,0.6); }",
|
|
586
|
-
".dsh-mem-rb-muted { font-size: 12px; color: var(--
|
|
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; }",
|
|
894
|
+
"}",
|
|
587
895
|
].join("\n");
|
|
588
896
|
document.head.appendChild(el);
|
|
589
897
|
}
|
|
@@ -602,16 +910,31 @@ window.__ModuleLoader__.load({
|
|
|
602
910
|
var open = openState[0];
|
|
603
911
|
var setOpen = openState[1];
|
|
604
912
|
var wrapRef = react.useRef(null);
|
|
913
|
+
// 请求序列号:快速切换会话时丢弃旧会话的过期响应(慢响应不得覆盖新会话档位)
|
|
914
|
+
var seqRef = react.useRef(0);
|
|
605
915
|
|
|
606
|
-
react.
|
|
916
|
+
var load = react.useCallback(function () {
|
|
607
917
|
if (!sessionId || !rpc) return;
|
|
918
|
+
var token = ++seqRef.current;
|
|
919
|
+
setError(null);
|
|
608
920
|
rpc("dsh-memory/session-mode-get", { sessionId: sessionId })
|
|
609
921
|
.then(function (r) {
|
|
922
|
+
if (token !== seqRef.current) return;
|
|
610
923
|
if (r && r.ok && r.value) setMode(r.value.mode);
|
|
924
|
+
else setError(r && r.error ? r.error.message : "RPC error");
|
|
611
925
|
})
|
|
612
|
-
.catch(function () {
|
|
926
|
+
.catch(function (e) {
|
|
927
|
+
if (token !== seqRef.current) return;
|
|
928
|
+
setError(String((e && e.message) || e));
|
|
929
|
+
});
|
|
613
930
|
}, [sessionId, rpc]);
|
|
614
931
|
|
|
932
|
+
react.useEffect(function () { load(); }, [load]);
|
|
933
|
+
|
|
934
|
+
// 侧边栏书本 icon 补丁由常驻 pill 驱动(MemoryPanel 只在记忆分节激活时挂载,
|
|
935
|
+
// 覆盖不了"打开设置第一眼"的场景);body 级观察器全局单例,多实例幂等
|
|
936
|
+
react.useEffect(function () { watchSidebarIcon(); }, []);
|
|
937
|
+
|
|
615
938
|
react.useEffect(function () {
|
|
616
939
|
if (!open) return;
|
|
617
940
|
var onDown = function (e) {
|
|
@@ -650,12 +973,13 @@ window.__ModuleLoader__.load({
|
|
|
650
973
|
if (!sessionId || !rpc) return null;
|
|
651
974
|
var info = modeInfo(mode);
|
|
652
975
|
var loaded = mode !== null;
|
|
653
|
-
|
|
976
|
+
// 关闭档与其余三档二分:关闭 = dsh 透明按钮(无边框无底无光晕);
|
|
977
|
+
// 日常/工作/智能 = 同款流光 + 光晕,档位区分靠蓝阶文字色与流光内底混色深度
|
|
978
|
+
var isOff = loaded && mode === "off";
|
|
979
|
+
var isFlow = loaded && !isOff;
|
|
654
980
|
|
|
655
|
-
|
|
981
|
+
ensureThemeStyle();
|
|
656
982
|
|
|
657
|
-
// auto 档的边框/背景/文字色均由 .dsh-mem-flow 提供(双层背景画流光边 + 分主题文字),
|
|
658
|
-
// inline 不能设置这些——inline 优先级会盖掉 class 里的流光
|
|
659
983
|
var pillStyle = {
|
|
660
984
|
display: "inline-flex",
|
|
661
985
|
alignItems: "center",
|
|
@@ -667,13 +991,13 @@ window.__ModuleLoader__.load({
|
|
|
667
991
|
fontWeight: 500,
|
|
668
992
|
lineHeight: "20px",
|
|
669
993
|
cursor: "pointer",
|
|
994
|
+
// 流光档的边框/背景由 .dsh-mem-flow 的双层背景提供(流光边 + 不透明内底),
|
|
995
|
+
// inline 只给文字色 / 光晕 / 流光内底混色通道(--dsh-mem-pill-tint)
|
|
996
|
+
color: isFlow ? info.color : "var(--dsh-mem-text-2)",
|
|
670
997
|
};
|
|
671
|
-
if (
|
|
672
|
-
pillStyle.boxShadow = "0 0 12px
|
|
673
|
-
|
|
674
|
-
pillStyle.border = "1px solid " + (loaded ? info.color + "66" : "var(--dsw-alias-border-secondary, #ccc)");
|
|
675
|
-
pillStyle.background = loaded ? info.color + "14" : "var(--dsw-alias-bg-secondary, #f6f8fa)";
|
|
676
|
-
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;
|
|
677
1001
|
}
|
|
678
1002
|
|
|
679
1003
|
return react.createElement(
|
|
@@ -683,13 +1007,16 @@ window.__ModuleLoader__.load({
|
|
|
683
1007
|
"button",
|
|
684
1008
|
{
|
|
685
1009
|
type: "button",
|
|
686
|
-
title: "本会话记忆档位(点击切换)",
|
|
687
|
-
onClick: function () {
|
|
688
|
-
|
|
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",
|
|
689
1016
|
style: pillStyle,
|
|
690
1017
|
},
|
|
691
|
-
"
|
|
692
|
-
react.createElement("span", null, loaded ? info.label : "…"),
|
|
1018
|
+
"记忆 · ",
|
|
1019
|
+
react.createElement("span", null, loaded ? info.label : error ? "⚠" : "…"),
|
|
693
1020
|
),
|
|
694
1021
|
open
|
|
695
1022
|
? react.createElement(ModeSlider, {
|
|
@@ -742,7 +1069,7 @@ window.__ModuleLoader__.load({
|
|
|
742
1069
|
}, [running, refresh]);
|
|
743
1070
|
|
|
744
1071
|
if (!rb || rb.supported === false) return null;
|
|
745
|
-
|
|
1072
|
+
ensureThemeStyle();
|
|
746
1073
|
|
|
747
1074
|
var start = function () {
|
|
748
1075
|
setBusy(true);
|
|
@@ -785,7 +1112,7 @@ window.__ModuleLoader__.load({
|
|
|
785
1112
|
} else if (!running && rb.phase === "cancelled") {
|
|
786
1113
|
lastNote = "上次重建:已取消(完成 " + rb.done + "/" + rb.total + " 会话,已重建部分保留)";
|
|
787
1114
|
} else if (!running && rb.phase === "failed") {
|
|
788
|
-
lastNote = "
|
|
1115
|
+
lastNote = "上次重建:失败:" + (rb.error || "未知错误");
|
|
789
1116
|
}
|
|
790
1117
|
|
|
791
1118
|
return react.createElement(
|
|
@@ -800,17 +1127,14 @@ window.__ModuleLoader__.load({
|
|
|
800
1127
|
: "从 L0 原始对话重新蒸馏 L1/L2/L3;旧数据先归档(不删除)",
|
|
801
1128
|
),
|
|
802
1129
|
running
|
|
803
|
-
? react.createElement(
|
|
804
|
-
type: "button",
|
|
805
|
-
className: "dsh-mem-rb-btn",
|
|
1130
|
+
? react.createElement(NButton, {
|
|
806
1131
|
disabled: busy || rb.cancelRequested,
|
|
807
1132
|
onClick: cancel,
|
|
808
1133
|
}, rb.cancelRequested ? "取消中…" : "取消重建")
|
|
809
|
-
: react.createElement(
|
|
810
|
-
type: "button",
|
|
811
|
-
className: "dsh-mem-rb-btn dsh-mem-rb-danger",
|
|
1134
|
+
: react.createElement(NButton, {
|
|
812
1135
|
disabled: busy || empty,
|
|
813
1136
|
title: empty ? "L0 无消息,无可重建内容" : "重新蒸馏全部记忆",
|
|
1137
|
+
style: { color: "var(--dsh-mem-danger)" },
|
|
814
1138
|
onClick: function () { setConfirmOpen(true); },
|
|
815
1139
|
}, busy ? "…" : "开始重建"),
|
|
816
1140
|
),
|
|
@@ -831,58 +1155,314 @@ window.__ModuleLoader__.load({
|
|
|
831
1155
|
? react.createElement("div", { className: "dsh-mem-rb-muted", style: { marginTop: 8 } }, lastNote)
|
|
832
1156
|
: null,
|
|
833
1157
|
rbError
|
|
834
|
-
? react.createElement("div", { style: { marginTop: 8, fontSize: 12, color: "
|
|
1158
|
+
? react.createElement("div", { style: { marginTop: 8, fontSize: 12, color: "var(--dsh-mem-danger)" } }, rbError)
|
|
835
1159
|
: null,
|
|
836
1160
|
confirmOpen
|
|
837
1161
|
? react.createElement(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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
|
+
],
|
|
843
1179
|
},
|
|
844
1180
|
react.createElement(
|
|
845
|
-
"div",
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
react.createElement("b", null, rb.sessionCount + " 个会话 · " + rb.messageCount + " 条消息"),
|
|
854
|
-
",预计 ≥" + rb.estCalls + " 次蒸馏调用。",
|
|
855
|
-
),
|
|
856
|
-
react.createElement(
|
|
857
|
-
"div", { style: { marginTop: 8 } },
|
|
858
|
-
"现有 L1 记忆 / L2 场景 / L3 画像会整体归档(*.bak.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。",
|
|
859
|
-
),
|
|
860
|
-
react.createElement(
|
|
861
|
-
"div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 } },
|
|
862
|
-
react.createElement(
|
|
863
|
-
"button", {
|
|
864
|
-
type: "button",
|
|
865
|
-
className: "dsh-mem-rb-btn",
|
|
866
|
-
onClick: function () { setConfirmOpen(false); },
|
|
867
|
-
},
|
|
868
|
-
"取消",
|
|
869
|
-
),
|
|
870
|
-
react.createElement(
|
|
871
|
-
"button", {
|
|
872
|
-
type: "button",
|
|
873
|
-
className: "dsh-mem-rb-btn dsh-mem-rb-primary",
|
|
874
|
-
disabled: busy,
|
|
875
|
-
onClick: start,
|
|
876
|
-
},
|
|
877
|
-
busy ? "启动中…" : "开始重建",
|
|
878
|
-
),
|
|
879
|
-
),
|
|
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.时间戳,可手工找回),随后清空重建;重建期间可正常对话,新对话的蒸馏优先进行;中途可取消,已重建部分保留。",
|
|
880
1189
|
),
|
|
881
1190
|
)
|
|
882
1191
|
: null,
|
|
883
1192
|
);
|
|
884
1193
|
}
|
|
885
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
|
+
|
|
886
1466
|
// ── Tab:概览(开关面板 + 计数) ──
|
|
887
1467
|
function OverviewTab(props) {
|
|
888
1468
|
var rpc = props.rpc;
|
|
@@ -939,22 +1519,27 @@ window.__ModuleLoader__.load({
|
|
|
939
1519
|
});
|
|
940
1520
|
};
|
|
941
1521
|
|
|
942
|
-
|
|
1522
|
+
// 统计分两段:计数瓦片(一眼可读)+ 明细行(目录/版本/时间线/队列)
|
|
1523
|
+
var tiles = [];
|
|
1524
|
+
var infos = [];
|
|
943
1525
|
if (stats) {
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
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)]);
|
|
957
1541
|
}
|
|
1542
|
+
var degraded = stats && stats.message && stats.message !== "running";
|
|
958
1543
|
|
|
959
1544
|
var master = settingsData && settingsData.settings ? settingsData.settings.enabled : true;
|
|
960
1545
|
var ceilingNote = "";
|
|
@@ -1030,24 +1615,41 @@ window.__ModuleLoader__.load({
|
|
|
1030
1615
|
: null,
|
|
1031
1616
|
)
|
|
1032
1617
|
: null,
|
|
1618
|
+
react.createElement(EmbeddingSection, { rpc: rpc }),
|
|
1033
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,
|
|
1034
1627
|
error
|
|
1035
1628
|
? react.createElement("div", { style: S.error }, "获取状态失败:" + error)
|
|
1036
1629
|
: !stats
|
|
1037
1630
|
? react.createElement("p", { style: S.intro }, "正在读取记忆状态…")
|
|
1038
1631
|
: react.createElement(
|
|
1039
|
-
"
|
|
1040
|
-
{ style: S.
|
|
1632
|
+
"div", null,
|
|
1633
|
+
react.createElement("div", { style: S.panelLabel }, "记忆概况"),
|
|
1041
1634
|
react.createElement(
|
|
1042
|
-
"
|
|
1043
|
-
|
|
1635
|
+
"div", { style: S.statGrid },
|
|
1636
|
+
tiles.map(function (t) {
|
|
1044
1637
|
return react.createElement(
|
|
1045
|
-
"
|
|
1046
|
-
|
|
1047
|
-
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),
|
|
1048
1642
|
);
|
|
1049
1643
|
}),
|
|
1050
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
|
+
}),
|
|
1051
1653
|
),
|
|
1052
1654
|
react.createElement(
|
|
1053
1655
|
"p", { style: S.hint },
|
|
@@ -1076,6 +1678,9 @@ window.__ModuleLoader__.load({
|
|
|
1076
1678
|
var loadingState = react.useState(false);
|
|
1077
1679
|
var loading = loadingState[0];
|
|
1078
1680
|
var setLoading = loadingState[1];
|
|
1681
|
+
var truncState = react.useState(false);
|
|
1682
|
+
var truncated = truncState[0];
|
|
1683
|
+
var setTruncated = truncState[1];
|
|
1079
1684
|
var errState = react.useState(null);
|
|
1080
1685
|
var error = errState[0];
|
|
1081
1686
|
var setError = errState[1];
|
|
@@ -1098,15 +1703,20 @@ window.__ModuleLoader__.load({
|
|
|
1098
1703
|
var last = lastState[0];
|
|
1099
1704
|
var setLast = lastState[1];
|
|
1100
1705
|
|
|
1706
|
+
// 请求序列号:快速连续搜索/翻页时丢弃过期响应(慢的旧响应不得覆盖新结果)
|
|
1707
|
+
var seqRef = react.useRef(0);
|
|
1708
|
+
|
|
1101
1709
|
var fetchPage = react.useCallback(function (conds, offset, append) {
|
|
1102
1710
|
setLoading(true);
|
|
1103
1711
|
setError(null);
|
|
1712
|
+
var token = ++seqRef.current;
|
|
1104
1713
|
var payload = { limit: limit, offset: offset };
|
|
1105
1714
|
if (conds.query) payload.query = conds.query;
|
|
1106
1715
|
if (conds.type) payload.type = conds.type;
|
|
1107
1716
|
if (conds.scene) payload.scene = conds.scene;
|
|
1108
1717
|
rpc("dsh-memory/list-records", payload)
|
|
1109
1718
|
.then(function (r) {
|
|
1719
|
+
if (token !== seqRef.current) return;
|
|
1110
1720
|
setLoading(false);
|
|
1111
1721
|
if (!r || !r.ok) {
|
|
1112
1722
|
setError(r && r.error ? r.error.message : "RPC error");
|
|
@@ -1116,9 +1726,11 @@ window.__ModuleLoader__.load({
|
|
|
1116
1726
|
setItems(function (prev) { return append ? prev.concat(v.items) : v.items; });
|
|
1117
1727
|
setHasMore(!!v.hasMore);
|
|
1118
1728
|
setTotal(v.total === undefined || v.total === null ? null : v.total);
|
|
1729
|
+
setTruncated(!!v.truncated);
|
|
1119
1730
|
if (v.scenes) setSceneOptions(v.scenes);
|
|
1120
1731
|
})
|
|
1121
1732
|
.catch(function (e) {
|
|
1733
|
+
if (token !== seqRef.current) return;
|
|
1122
1734
|
setLoading(false);
|
|
1123
1735
|
setError(String((e && e.message) || e));
|
|
1124
1736
|
});
|
|
@@ -1146,8 +1758,8 @@ window.__ModuleLoader__.load({
|
|
|
1146
1758
|
"div", null,
|
|
1147
1759
|
react.createElement(
|
|
1148
1760
|
"div", { style: S.toolbar },
|
|
1149
|
-
react.createElement(
|
|
1150
|
-
style:
|
|
1761
|
+
react.createElement(NInput, {
|
|
1762
|
+
style: { flex: 1, minWidth: 160 },
|
|
1151
1763
|
placeholder: "搜索记忆内容(BM25 关键词)…",
|
|
1152
1764
|
value: query,
|
|
1153
1765
|
onChange: function (e) { setQuery(e.target.value); },
|
|
@@ -1156,7 +1768,7 @@ window.__ModuleLoader__.load({
|
|
|
1156
1768
|
react.createElement(
|
|
1157
1769
|
"select",
|
|
1158
1770
|
{
|
|
1159
|
-
|
|
1771
|
+
className: "dsh-mem-select",
|
|
1160
1772
|
value: typeFilter,
|
|
1161
1773
|
onChange: function (e) { setTypeFilter(e.target.value); },
|
|
1162
1774
|
},
|
|
@@ -1168,7 +1780,7 @@ window.__ModuleLoader__.load({
|
|
|
1168
1780
|
react.createElement(
|
|
1169
1781
|
"select",
|
|
1170
1782
|
{
|
|
1171
|
-
|
|
1783
|
+
className: "dsh-mem-select",
|
|
1172
1784
|
value: sceneFilter,
|
|
1173
1785
|
onChange: function (e) { setSceneFilter(e.target.value); },
|
|
1174
1786
|
},
|
|
@@ -1177,18 +1789,24 @@ window.__ModuleLoader__.load({
|
|
|
1177
1789
|
return react.createElement("option", { key: s, value: s }, s.length > 24 ? s.slice(0, 24) + "…" : s);
|
|
1178
1790
|
}),
|
|
1179
1791
|
),
|
|
1180
|
-
react.createElement(
|
|
1792
|
+
react.createElement(NButton, { onClick: search }, "搜索"),
|
|
1181
1793
|
),
|
|
1182
1794
|
react.createElement(
|
|
1183
1795
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1184
1796
|
react.createElement("span", { style: S.muted }, loading ? "加载中…" : countText),
|
|
1185
1797
|
react.createElement("div", { style: S.grow }),
|
|
1186
|
-
react.createElement(
|
|
1187
|
-
style: S.button,
|
|
1798
|
+
react.createElement(NButton, {
|
|
1188
1799
|
onClick: function () { fetchPage(last, 0, false); },
|
|
1189
1800
|
}, "刷新"),
|
|
1190
1801
|
),
|
|
1191
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,
|
|
1192
1810
|
items.length === 0 && !loading && !error
|
|
1193
1811
|
? react.createElement("p", { style: S.intro }, "暂无记忆。对话几轮后,蒸馏管线会自动抽取记忆。")
|
|
1194
1812
|
: items.map(function (m) {
|
|
@@ -1197,18 +1815,15 @@ window.__ModuleLoader__.load({
|
|
|
1197
1815
|
"div",
|
|
1198
1816
|
{
|
|
1199
1817
|
key: m.id,
|
|
1200
|
-
|
|
1818
|
+
className: "dsh-mem-card dsh-mem-card-hover",
|
|
1819
|
+
style: Object.assign({}, S.card, { cursor: "pointer" }),
|
|
1201
1820
|
onClick: function () { setExpandedId(open ? null : m.id); },
|
|
1202
1821
|
},
|
|
1203
1822
|
react.createElement(
|
|
1204
1823
|
"div", { style: S.cardHead },
|
|
1205
1824
|
react.createElement(
|
|
1206
1825
|
"span",
|
|
1207
|
-
{
|
|
1208
|
-
style: Object.assign({}, S.tag, {
|
|
1209
|
-
background: TYPE_COLORS[m.type] || "#666",
|
|
1210
|
-
}),
|
|
1211
|
-
},
|
|
1826
|
+
{ className: "dsh-mem-tag dsh-mem-tag-" + m.type },
|
|
1212
1827
|
TYPE_LABELS[m.type] || m.type,
|
|
1213
1828
|
),
|
|
1214
1829
|
react.createElement("span", { style: S.muted }, "优先级 " + m.priority),
|
|
@@ -1223,13 +1838,13 @@ window.__ModuleLoader__.load({
|
|
|
1223
1838
|
? react.createElement(
|
|
1224
1839
|
"div", { style: S.detail },
|
|
1225
1840
|
"id: " + m.id + "\n" +
|
|
1226
|
-
"情境: " + (m.scene || "
|
|
1841
|
+
"情境: " + (m.scene || "-") + "\n" +
|
|
1227
1842
|
"版本: v" + m.version + "(去重合并次数 " + m.version + ")\n" +
|
|
1228
1843
|
"创建: " + fmtTime(m.createdAt) + "\n" +
|
|
1229
|
-
"活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "
|
|
1844
|
+
"活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" +
|
|
1230
1845
|
(m.sourceMessageIds && m.sourceMessageIds.length > 0
|
|
1231
1846
|
? "来源消息: " + m.sourceMessageIds.join(", ")
|
|
1232
|
-
: "来源消息:
|
|
1847
|
+
: "来源消息: -"),
|
|
1233
1848
|
)
|
|
1234
1849
|
: null,
|
|
1235
1850
|
);
|
|
@@ -1238,9 +1853,11 @@ window.__ModuleLoader__.load({
|
|
|
1238
1853
|
? react.createElement(
|
|
1239
1854
|
"div", { style: S.flexRow },
|
|
1240
1855
|
react.createElement("div", { style: S.grow }),
|
|
1241
|
-
react.createElement(
|
|
1242
|
-
|
|
1243
|
-
onClick: function () {
|
|
1856
|
+
react.createElement(NButton, {
|
|
1857
|
+
disabled: loading,
|
|
1858
|
+
onClick: function () {
|
|
1859
|
+
if (!loading) fetchPage(last, items.length, true);
|
|
1860
|
+
},
|
|
1244
1861
|
}, loading ? "加载中…" : "加载更多"),
|
|
1245
1862
|
)
|
|
1246
1863
|
: null,
|
|
@@ -1274,7 +1891,7 @@ window.__ModuleLoader__.load({
|
|
|
1274
1891
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1275
1892
|
react.createElement("span", { style: S.muted }, items ? items.length + " 个场景块" : "加载中…"),
|
|
1276
1893
|
react.createElement("div", { style: S.grow }),
|
|
1277
|
-
react.createElement(
|
|
1894
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1278
1895
|
),
|
|
1279
1896
|
error ? react.createElement("div", { style: S.error }, error) : null,
|
|
1280
1897
|
items && items.length === 0
|
|
@@ -1282,7 +1899,7 @@ window.__ModuleLoader__.load({
|
|
|
1282
1899
|
: null,
|
|
1283
1900
|
(items || []).map(function (s) {
|
|
1284
1901
|
return react.createElement(
|
|
1285
|
-
"div", { key: s.path, style: S.card },
|
|
1902
|
+
"div", { key: s.path, className: "dsh-mem-card", style: S.card },
|
|
1286
1903
|
react.createElement(
|
|
1287
1904
|
"div", { style: S.sceneHead },
|
|
1288
1905
|
react.createElement("span", { style: S.sceneTitle }, s.path),
|
|
@@ -1309,11 +1926,18 @@ window.__ModuleLoader__.load({
|
|
|
1309
1926
|
var contentState = react.useState(null);
|
|
1310
1927
|
var content = contentState[0];
|
|
1311
1928
|
var setContent = contentState[1];
|
|
1929
|
+
var errState = react.useState(null);
|
|
1930
|
+
var error = errState[0];
|
|
1931
|
+
var setError = errState[1];
|
|
1312
1932
|
|
|
1313
1933
|
var load = react.useCallback(function () {
|
|
1934
|
+
setError(null);
|
|
1314
1935
|
rpc("dsh-memory/persona", {})
|
|
1315
|
-
.then(function (r) {
|
|
1316
|
-
|
|
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)); });
|
|
1317
1941
|
}, [rpc]);
|
|
1318
1942
|
|
|
1319
1943
|
react.useEffect(function () { load(); }, [load]);
|
|
@@ -1323,10 +1947,17 @@ window.__ModuleLoader__.load({
|
|
|
1323
1947
|
react.createElement(
|
|
1324
1948
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1325
1949
|
react.createElement("span", { style: S.muted },
|
|
1326
|
-
content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像"),
|
|
1950
|
+
error ? "加载失败" : content === null ? "加载中…" : content ? content.length + " 字符" : "未生成画像"),
|
|
1327
1951
|
react.createElement("div", { style: S.grow }),
|
|
1328
|
-
react.createElement(
|
|
1952
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1329
1953
|
),
|
|
1954
|
+
error
|
|
1955
|
+
? react.createElement(
|
|
1956
|
+
"div",
|
|
1957
|
+
{ style: Object.assign({}, S.error, { marginBottom: 10 }) },
|
|
1958
|
+
"画像读取失败:" + error + "(点右上“刷新”重试)",
|
|
1959
|
+
)
|
|
1960
|
+
: null,
|
|
1330
1961
|
content
|
|
1331
1962
|
? react.createElement("pre", { style: S.pre }, content)
|
|
1332
1963
|
: content === null
|
|
@@ -1341,11 +1972,18 @@ window.__ModuleLoader__.load({
|
|
|
1341
1972
|
var linesState = react.useState(null);
|
|
1342
1973
|
var lines = linesState[0];
|
|
1343
1974
|
var setLines = linesState[1];
|
|
1975
|
+
var errState = react.useState(null);
|
|
1976
|
+
var error = errState[0];
|
|
1977
|
+
var setError = errState[1];
|
|
1344
1978
|
|
|
1345
1979
|
var load = react.useCallback(function () {
|
|
1980
|
+
setError(null);
|
|
1346
1981
|
rpc("dsh-memory/log-tail", { lines: 200 })
|
|
1347
|
-
.then(function (r) {
|
|
1348
|
-
|
|
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)); });
|
|
1349
1987
|
}, [rpc]);
|
|
1350
1988
|
|
|
1351
1989
|
react.useEffect(function () { load(); }, [load]);
|
|
@@ -1354,11 +1992,17 @@ window.__ModuleLoader__.load({
|
|
|
1354
1992
|
"div", null,
|
|
1355
1993
|
react.createElement(
|
|
1356
1994
|
"div", { style: Object.assign({}, S.flexRow, { marginBottom: 10 }) },
|
|
1357
|
-
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)"),
|
|
1358
1996
|
react.createElement("div", { style: S.grow }),
|
|
1359
|
-
react.createElement(
|
|
1997
|
+
react.createElement(NButton, { onClick: load }, "刷新"),
|
|
1360
1998
|
),
|
|
1361
|
-
|
|
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") || "(暂无日志)"),
|
|
1362
2006
|
);
|
|
1363
2007
|
}
|
|
1364
2008
|
|
|
@@ -1371,12 +2015,66 @@ window.__ModuleLoader__.load({
|
|
|
1371
2015
|
["log", "日志"],
|
|
1372
2016
|
];
|
|
1373
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
|
+
|
|
1374
2069
|
function MemoryPanel(props) {
|
|
1375
2070
|
var rpc = props.rpc;
|
|
1376
2071
|
var tabState = react.useState("overview");
|
|
1377
2072
|
var tab = tabState[0];
|
|
1378
2073
|
var setTab = tabState[1];
|
|
1379
2074
|
|
|
2075
|
+
ensureThemeStyle(); // 设置页挂载即注入主题令牌与组件样式
|
|
2076
|
+
react.useEffect(function () { watchSidebarIcon(); }, []);
|
|
2077
|
+
|
|
1380
2078
|
var body;
|
|
1381
2079
|
if (tab === "overview") body = react.createElement(OverviewTab, { rpc: rpc });
|
|
1382
2080
|
else if (tab === "records") body = react.createElement(RecordsTab, { rpc: rpc });
|
|
@@ -1386,7 +2084,7 @@ window.__ModuleLoader__.load({
|
|
|
1386
2084
|
|
|
1387
2085
|
return react.createElement(
|
|
1388
2086
|
"div",
|
|
1389
|
-
{ style: S.section },
|
|
2087
|
+
{ className: "dsh-mem-root", style: S.section },
|
|
1390
2088
|
react.createElement("h2", { style: S.heading }, "记忆 (Memory)"),
|
|
1391
2089
|
react.createElement(
|
|
1392
2090
|
"p", { style: S.intro },
|
|
@@ -1399,7 +2097,7 @@ window.__ModuleLoader__.load({
|
|
|
1399
2097
|
"button",
|
|
1400
2098
|
{
|
|
1401
2099
|
key: t[0],
|
|
1402
|
-
|
|
2100
|
+
className: tab === t[0] ? "dsh-mem-tab dsh-mem-tab-on" : "dsh-mem-tab",
|
|
1403
2101
|
onClick: function () { setTab(t[0]); },
|
|
1404
2102
|
},
|
|
1405
2103
|
t[1],
|