@stardustlc/dsh-dream 0.7.0 → 0.7.1
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/CHANGELOG.md +33 -16
- package/README.en.md +23 -113
- package/README.md +22 -160
- package/docs/CHANGELOG.en.md +43 -0
- package/docs/USAGE.en.md +84 -0
- package/docs/USAGE.md +133 -0
- package/docs/VALIDATION.md +29 -0
- package/docs/validation/0.5.1.md +68 -0
- package/docs/validation/0.5.2.md +39 -0
- package/docs/validation/0.6.0.md +73 -0
- package/docs/validation/0.7.0.md +11 -0
- package/docs/validation-language-2026-10-03.md +16 -0
- package/lib/client.js +536 -232
- package/package.json +4 -3
- package/skills/dream-protocol/SKILL.md +6 -2
package/lib/client.js
CHANGED
|
@@ -2,15 +2,9 @@
|
|
|
2
2
|
// 仓库里没有 src/client.ts,pnpm run build 也不会重写它。改面板请直接改这里,
|
|
3
3
|
// 改完用 node --check lib/client.js 自检(CI 里也会跑这一步)。
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//(<journalDir>/bridge/ 的桥接应用记录)。
|
|
9
|
-
// 数据来自插件自己的同源只读路由 GET /_dsh/dsh-dream/journal、/_dsh/dsh-dream/knowledge
|
|
10
|
-
// 与 /_dsh/dsh-dream/bridge(由 src/index.ts 挂载);前端不缓存业务数据,刷新即见。
|
|
11
|
-
// 经验区块只展示状态 / 条件 / 范围 / 适用与例外 / 证据摘要 / 最近核验;日志截断或坏行会明确提示,
|
|
12
|
-
// 不展示权重、向量或原始 JSON。经验行只提供「复制审阅 / 预览命令」,写操作仍在对话工具里执行。
|
|
13
|
-
// 隐私纪律:面板只读,不写文件、不上传;「隐私模式」只做显示层模糊,DOM 仍是明文。
|
|
5
|
+
// 日记、来源、审阅、反馈与规则变更共用同一份数据,界面语言跟随 DSH locale。
|
|
6
|
+
// 读取走同源只读路由,写操作走认证 Connection 通道;规则必须预览后应用。
|
|
7
|
+
// 日记、来源原文和人工输入保留原文;「隐私模式」仅模糊显示,DOM 仍是明文。
|
|
14
8
|
window.__ModuleLoader__.load({ id: "@stardustlc/dsh-dream", factory: (require) => {
|
|
15
9
|
var module = { exports: {} }; var exports = module.exports;
|
|
16
10
|
"use strict";
|
|
@@ -19,6 +13,313 @@ const React = require("react");
|
|
|
19
13
|
const { useState, useEffect } = React;
|
|
20
14
|
const h = React.createElement;
|
|
21
15
|
|
|
16
|
+
// Interface language follows DSH; user content and stored records keep their original text.
|
|
17
|
+
let localeService;
|
|
18
|
+
const EN = Object.freeze({
|
|
19
|
+
"请求失败(HTTP ": "Request failed (HTTP ",
|
|
20
|
+
"服务器返回的不是 JSON(HTTP ": "The server did not return JSON (HTTP ",
|
|
21
|
+
"登录状态已失效,请重新打开 DSH 页面后重试。": "Your session has expired. Reopen DSH and try again.",
|
|
22
|
+
"当前宿主尚未提供此操作,请升级 DSH,或使用下方对话命令。": "This host does not support this action. Update DSH or use the chat command below.",
|
|
23
|
+
"候选(未采纳)": "Candidate (not accepted)",
|
|
24
|
+
"可用": "Usable",
|
|
25
|
+
"有冲突": "Disputed",
|
|
26
|
+
"待复核": "Needs review",
|
|
27
|
+
"已驳回": "Rejected",
|
|
28
|
+
"未知状态": "Unknown state",
|
|
29
|
+
"平台 ": "Platform ",
|
|
30
|
+
";等 ": "; and ",
|
|
31
|
+
" 条适用范围": " more conditions",
|
|
32
|
+
"范围未标注": "Scope not specified",
|
|
33
|
+
"暂无可展示的证据摘要": "No evidence summary available",
|
|
34
|
+
"尚未核验": "Not verified yet",
|
|
35
|
+
"没有 lastValidatedAt(从未核验)": "No lastValidatedAt (never verified)",
|
|
36
|
+
"未加载的经验(": "Lesson not loaded (",
|
|
37
|
+
"共 ": "Total: ",
|
|
38
|
+
" 条": " records",
|
|
39
|
+
"还没有经验": "No lessons yet",
|
|
40
|
+
"日志过大,仅回放最近一段事件": "The log is large; only recent events were replayed",
|
|
41
|
+
"有 ": "",
|
|
42
|
+
" 行损坏已跳过": " damaged lines were skipped",
|
|
43
|
+
";events.jsonl 未改动。": "; events.jsonl was preserved.",
|
|
44
|
+
"还没有经验记录": "No lesson records yet",
|
|
45
|
+
"经验是带证据、有适用条件的技术结论:收尾时用 dream_learn 提交候选,证据足够、范围明确的会出现在这里。": "Lessons are technical conclusions with evidence and conditions. Submit a candidate with dream_learn; records with sufficient evidence and a clear scope appear here.",
|
|
46
|
+
"旧日记里的教训不会自动升级为经验;采纳、驳回或标记冲突请用 dream_review。": "Journal observations do not become lessons automatically. Use dream_review to accept, reject or flag conflicts.",
|
|
47
|
+
"已驳回:不参与默认检索与注入": "Rejected: excluded from default retrieval and injection",
|
|
48
|
+
"待复核:条件或依赖可能已过期,默认不注入": "Needs review: conditions or dependencies may be outdated; not injected by default",
|
|
49
|
+
"冲突未解决:默认不注入,先 resolve-conflict": "Unresolved conflict: not injected by default; use resolve-conflict first",
|
|
50
|
+
"候选:可被检索到,但默认不作为任务建议注入": "Candidate: retrievable, but not injected as task advice by default",
|
|
51
|
+
"证据不足:没有独立证据,默认不注入": "Insufficient independent evidence: not injected by default",
|
|
52
|
+
"版本不匹配:当前依赖版本不满足适用范围": "Version mismatch: the dependency version is outside the applicable range",
|
|
53
|
+
"超出条数预算,本次未取回": "Not retrieved: record limit reached",
|
|
54
|
+
"超出字符预算:按排名整条装入,遇到第一条放不下即停": "Character limit reached: complete records are added by rank until the next one cannot fit",
|
|
55
|
+
"超出字符预算,本次未取回": "Not retrieved: character limit reached",
|
|
56
|
+
"新建": "Created",
|
|
57
|
+
"替换管理块": "Replaced managed block",
|
|
58
|
+
"追加管理块": "Appended managed block",
|
|
59
|
+
"未改动": "Unchanged",
|
|
60
|
+
"回滚": "Rolled back",
|
|
61
|
+
"未知动作": "Unknown action",
|
|
62
|
+
"时间未知": "Unknown time",
|
|
63
|
+
"没有记录时间": "No timestamp recorded",
|
|
64
|
+
"目标未记录": "Target not recorded",
|
|
65
|
+
"没有经验引用": "No lesson references",
|
|
66
|
+
"可回滚": "Rollback available",
|
|
67
|
+
"不可回滚": "Rollback unavailable",
|
|
68
|
+
"还没有写入记录": "No rule changes yet",
|
|
69
|
+
"写入记录来自 dream_bridge 的 preview → apply;面板只读展示目标、时间、经验 revision 与前后哈希,不会写文件。": "Changes are recorded by dream_bridge preview → apply. This log shows the target, time, lesson revisions and before/after hashes.",
|
|
70
|
+
"在可桥接经验行点「复制预览命令」,回到对话里执行;写操作只在工具中完成。": "Preview an eligible project lesson, review the changes and apply them. Chat commands are also available.",
|
|
71
|
+
"没有待核实的经验 —— 候选、有冲突、待复核的经验会出现在这里,并写明被扣下的原因。": "Candidates, disputed lessons and lessons needing review appear here, with an explanation of why they are held.",
|
|
72
|
+
"刚刚": "Just now",
|
|
73
|
+
" 分钟前": " min ago",
|
|
74
|
+
" 小时前": " h ago",
|
|
75
|
+
" 天前": " days ago",
|
|
76
|
+
"今天": "Today",
|
|
77
|
+
"昨天": "Yesterday",
|
|
78
|
+
"筛选后 ": "Filtered: ",
|
|
79
|
+
" 场": " dreams",
|
|
80
|
+
"」命中 ": "” matches ",
|
|
81
|
+
" 场梦": " dreams",
|
|
82
|
+
"筛选当前已加载的 ": "Filtering the ",
|
|
83
|
+
" 场 · 命中 ": " loaded dreams · Matches: ",
|
|
84
|
+
"新梦在前": "Newest first",
|
|
85
|
+
"(已达路由上限)": " (route limit reached)",
|
|
86
|
+
"最近 ": "Recent: ",
|
|
87
|
+
" 场 / 共 ": " dreams / Total: ",
|
|
88
|
+
"新梦在前 · 最多显示 ": "Newest first · Up to ",
|
|
89
|
+
"搜索结果 · 最多显示 ": "Search results · Up to ",
|
|
90
|
+
"本页内最近 ": "Most recent on this page: ",
|
|
91
|
+
"(统计未给 lastAt,按本页已加载的 ": "(lastAt is missing; calculated from the ",
|
|
92
|
+
" 场现算)": " dreams loaded on this page)",
|
|
93
|
+
"同一天": "Same day",
|
|
94
|
+
"约 ": "About ",
|
|
95
|
+
" 天": " days",
|
|
96
|
+
"第 1 场梦 · ": "First dream · ",
|
|
97
|
+
" · 心境「": " · Mood: “",
|
|
98
|
+
" 场梦 · 本页最近 ": " dreams · Loaded: ",
|
|
99
|
+
"最近:": "Latest: ",
|
|
100
|
+
"心境": "Mood",
|
|
101
|
+
"1 场梦,先不谈分布 —— 再做梦会累积。": "One dream so far. More dreams will build a mood history.",
|
|
102
|
+
"左 → 右:从早到晚": "Left → right: oldest to newest",
|
|
103
|
+
"教训榜": "Recurring observations",
|
|
104
|
+
"还没有教训 —— 记梦时带上 lessons,出现次数会在这里累积。": "No observations yet. Include lessons when recording dreams to track recurring observations.",
|
|
105
|
+
"常见教训": "Recurring observation",
|
|
106
|
+
"次数仅表示日记里的出现频率;出现 ≥3 次标为「常见教训」。写入项目规则前仍需核验来源并审阅经验。最近时间取自 stats.lastAt;统计缺失时按本页已加载的梦现算。": "Counts show journal frequency only; ≥3 occurrences marks a recurring observation. Verify sources and review lessons before writing project rules. Recency uses stats.lastAt, or the dreams loaded on this page when missing.",
|
|
107
|
+
"复制命令": "Copy command",
|
|
108
|
+
"已复制": "Copied",
|
|
109
|
+
"剪贴板不可用:请手动选中上面的命令复制。": "Clipboard unavailable. Select and copy the command above manually.",
|
|
110
|
+
"重新送审": "Reopen for review",
|
|
111
|
+
"处理冲突": "Resolve conflict",
|
|
112
|
+
"驳回这条": "Reject this lesson",
|
|
113
|
+
"标记待复核": "Mark for review",
|
|
114
|
+
"驳回": "Reject",
|
|
115
|
+
"采纳": "Accept",
|
|
116
|
+
"管理块内容已一致,无需写入。": "The managed block already matches; no write was needed.",
|
|
117
|
+
"请求超时,请先刷新确认结果后再重试。": "Request timed out. Refresh to check the result before retrying.",
|
|
118
|
+
"已更新「": "Updated the review state of “",
|
|
119
|
+
"」的审阅状态。": "”.",
|
|
120
|
+
"已记录使用反馈;「": "Feedback recorded; the review state of “",
|
|
121
|
+
"」的审阅状态保持原样。": "” is unchanged.",
|
|
122
|
+
"的操作": " — actions",
|
|
123
|
+
"收起审阅": "Hide review",
|
|
124
|
+
"审阅详情": "Review details",
|
|
125
|
+
"预览规则变更": "Preview rule changes",
|
|
126
|
+
"复制项目预览命令": "Copy project preview command",
|
|
127
|
+
"正在处理…": "Working…",
|
|
128
|
+
"已刷新经验列表。": "Lesson list refreshed.",
|
|
129
|
+
"刷新列表": "Refresh list",
|
|
130
|
+
"审阅动作": "Review action",
|
|
131
|
+
"冲突经验": "Conflicting lesson",
|
|
132
|
+
"处理方式": "Resolution",
|
|
133
|
+
"采用这条,驳回对方": "Accept this lesson and reject the other",
|
|
134
|
+
"放弃这条,保留对方": "Reject this lesson and keep the other",
|
|
135
|
+
"合并到这条,对方留作历史": "Merge into this lesson and retain the other as history",
|
|
136
|
+
"处理依据": "Reason for resolution",
|
|
137
|
+
"说明哪个来源支持这次决定…": "Explain which source supports this decision…",
|
|
138
|
+
"确认审阅": "Confirm review",
|
|
139
|
+
"在对话里处理": "Handle in chat",
|
|
140
|
+
"选择冲突经验并填写处理依据后即可处理。": "Select the conflicting lesson and provide a reason first.",
|
|
141
|
+
"复制审阅命令": "Copy review command",
|
|
142
|
+
"规则变更预览": "Rule change preview",
|
|
143
|
+
"目标:": "Target: ",
|
|
144
|
+
"管理块内容已经一致,无需更改。": "The managed block already matches; no changes are needed.",
|
|
145
|
+
"将按此预览重建 Dream 管理块,请核对新增和删除内容。块外内容保留,写入前创建回滚备份。": "Check the additions and deletions before rebuilding the Dream managed block. Content outside the block is preserved; a rollback backup is created before writing.",
|
|
146
|
+
"规则已应用,可在「写入记录」中回滚。": "Rules applied. You can roll back from the Rule changes tab.",
|
|
147
|
+
"应用此变更": "Apply these changes",
|
|
148
|
+
"取消预览": "Cancel preview",
|
|
149
|
+
"使用反馈 · 有用 ": "Usage feedback · Useful: ",
|
|
150
|
+
" · 不适用 ": " · Not applicable: ",
|
|
151
|
+
"场景备注(可选)": "Context note (optional)",
|
|
152
|
+
"反馈场景备注": "Feedback context note",
|
|
153
|
+
"这次在哪个场景使用…": "Where did you use this lesson…",
|
|
154
|
+
"这次有用": "Useful this time",
|
|
155
|
+
"这次不适用": "Not applicable this time",
|
|
156
|
+
"记录使用情况,供之后复核;采纳与驳回由审阅决定。": "Feedback records usage for future review. Acceptance and rejection remain review decisions.",
|
|
157
|
+
"有用": "Useful",
|
|
158
|
+
"不适用": "Not applicable",
|
|
159
|
+
"已恢复上一次的 Dream 管理块,块外内容保留。": "The previous Dream managed block was restored; content outside the block was preserved.",
|
|
160
|
+
"取消回滚": "Cancel rollback",
|
|
161
|
+
"回滚这次变更": "Roll back this change",
|
|
162
|
+
"将恢复「": "Restore the previous Dream managed block in “",
|
|
163
|
+
"」的前一个 Dream 管理块。块外内容保留;管理块已被修改时会拒绝回滚。": "”. Content outside the block is preserved. Rollback is refused if the managed block has changed.",
|
|
164
|
+
"正在回滚…": "Rolling back…",
|
|
165
|
+
"确认回滚管理块": "Confirm block rollback",
|
|
166
|
+
"隐私模式:点击此条临时查看": "Privacy mode: click to reveal temporarily",
|
|
167
|
+
"最近核验:": "Last verified: ",
|
|
168
|
+
"何时用:": "When to use: ",
|
|
169
|
+
"未标注适用条件": "No conditions specified",
|
|
170
|
+
"怎么做:": "What to do: ",
|
|
171
|
+
"未标注具体动作": "No action specified",
|
|
172
|
+
"适用范围与证据": "Scope and evidence",
|
|
173
|
+
"范围:": "Scope: ",
|
|
174
|
+
"适用:": "Applies to: ",
|
|
175
|
+
"例外:": "Exceptions: ",
|
|
176
|
+
"证据:": "Evidence: ",
|
|
177
|
+
"没有关联来源记录,采纳前请核对依据。": "No linked sources. Check the evidence before accepting.",
|
|
178
|
+
"来源记录 · ": "Source record · ",
|
|
179
|
+
"已读到来源": "Source read",
|
|
180
|
+
"待核验": "Unverified",
|
|
181
|
+
"会话": "Session",
|
|
182
|
+
"用户纠正": "User correction",
|
|
183
|
+
"本地资料": "Local artifact",
|
|
184
|
+
"未提供摘要": "No summary provided",
|
|
185
|
+
"会话:": "Session: ",
|
|
186
|
+
" · 记录 ": " · Record ",
|
|
187
|
+
"记录时间:": "Recorded: ",
|
|
188
|
+
"核验说明:": "Verification: ",
|
|
189
|
+
"来源校验值:": "Source hash: ",
|
|
190
|
+
"为什么被扣下:": "Why held: ",
|
|
191
|
+
"已审阅经验": "Reviewed lessons",
|
|
192
|
+
"重试": "Retry",
|
|
193
|
+
"读取经验失败:": "Could not load lessons: ",
|
|
194
|
+
"旧日记不受影响;修好后点「重试」。": "Your journal is preserved. Retry after resolving the error.",
|
|
195
|
+
"经验": "Lessons",
|
|
196
|
+
"加载中…": "Loading…",
|
|
197
|
+
"查看 ": "Show ",
|
|
198
|
+
"经验状态": "Lesson state",
|
|
199
|
+
"可用经验": "Usable lessons",
|
|
200
|
+
"已驳回历史": "Rejected history",
|
|
201
|
+
"暂无可用经验;待核实的记录在下方。": "No usable lessons yet. Records needing review are below.",
|
|
202
|
+
"没有已驳回记录。": "No rejected records.",
|
|
203
|
+
"显示更多经验": "Show more lessons",
|
|
204
|
+
"只显示最近 ": "Showing the most recent ",
|
|
205
|
+
" 条经验。": " lessons.",
|
|
206
|
+
"采纳记录仍需满足当前任务的平台、版本与项目条件;来源已读不代表结论永远适用。": "Accepted lessons must still match the platform, version and project of the current task. A source being read does not mean its conclusions always apply.",
|
|
207
|
+
"待核实": "Needs verification",
|
|
208
|
+
" 条待核实": " need verification",
|
|
209
|
+
"没有待核实的经验": "No lessons awaiting verification",
|
|
210
|
+
"显示更多待核实经验": "Show more pending lessons",
|
|
211
|
+
"这些经验默认不会作为任务建议注入。先核对来源与适用范围,再决定采纳、驳回或继续复核。": "These lessons are not injected as task advice by default. Check sources and scope before accepting, rejecting or reviewing further.",
|
|
212
|
+
"写入记录": "Rule changes",
|
|
213
|
+
"读取写入记录失败:": "Could not load rule changes: ",
|
|
214
|
+
" 次写入 · ": " changes · ",
|
|
215
|
+
" 次可回滚": " can be rolled back",
|
|
216
|
+
"经验:": "Lessons: ",
|
|
217
|
+
"前哈希:": "Before hash: ",
|
|
218
|
+
"后哈希:": "After hash: ",
|
|
219
|
+
"备份标识与回滚命令": "Backup ID and rollback command",
|
|
220
|
+
" 条记录。": " records.",
|
|
221
|
+
"回滚恢复对应备份的 Dream 管理块;块外内容保留,外部修改的管理块会停止回滚。": "Rollback restores the backed-up Dream managed block and preserves content outside it. External changes to the block prevent rollback.",
|
|
222
|
+
"隐私模式:点击卡片临时查看": "Privacy mode: click the card to reveal temporarily",
|
|
223
|
+
"心境:": "Mood: ",
|
|
224
|
+
"收起": "Collapse",
|
|
225
|
+
"展开全文": "Read more",
|
|
226
|
+
"没有找到「": "No dreams matching “",
|
|
227
|
+
"」相关的梦": "”",
|
|
228
|
+
"换个关键词试试,或者清空搜索看全部梦境。": "Try another keyword, or clear the search to see all dreams.",
|
|
229
|
+
"清空搜索": "Clear search",
|
|
230
|
+
"还没有梦境日记": "No dreams yet",
|
|
231
|
+
"对 agent 说「做个梦」,它会回放最近的会话、反思之后把第一场梦写进这里。": "Ask your agent to “have a dream”. It will replay recent sessions, reflect and record its first dream here.",
|
|
232
|
+
"对 agent 说「做个梦」": "Ask your agent to “have a dream”",
|
|
233
|
+
"搜索反思与教训…": "Search reflections and observations…",
|
|
234
|
+
"搜索梦境": "Search dreams",
|
|
235
|
+
"筛选作用于已加载窗口,不影响全量统计": "Filters apply to loaded dreams; overall statistics are unchanged",
|
|
236
|
+
"筛选": "Filter",
|
|
237
|
+
"筛选梦境": "Filter dreams",
|
|
238
|
+
" 场,不影响全量统计": " loaded dreams; overall statistics are unchanged",
|
|
239
|
+
"只看有教训的梦": "Only dreams with observations",
|
|
240
|
+
"清空筛选": "Clear filters",
|
|
241
|
+
"完成": "Done",
|
|
242
|
+
"显示层模糊;DOM 仍为明文,不改文件、不加密": "Display blur only; the DOM remains readable. Files are unchanged and not encrypted",
|
|
243
|
+
"隐私模式:开": "Privacy mode: on",
|
|
244
|
+
"隐私模式": "Privacy mode",
|
|
245
|
+
"重新读取梦境日记": "Reload the dream journal",
|
|
246
|
+
"刷新": "Refresh",
|
|
247
|
+
"读取梦境日记失败:": "Could not load the dream journal: ",
|
|
248
|
+
"暂时读不到梦境日记": "The dream journal is unavailable",
|
|
249
|
+
"等宿主路由就绪后点上面的「重试」;如果一直失败,看一眼 DSH 日志里有没有 [dsh-dream] 告警。": "Retry after the host is ready. If the problem persists, check DSH logs for [dsh-dream] warnings.",
|
|
250
|
+
"梦境总数": "Total dreams",
|
|
251
|
+
"本页显示最近 ": "Most recent loaded: ",
|
|
252
|
+
"全部梦境日记": "All dreams",
|
|
253
|
+
"时间跨度": "Time span",
|
|
254
|
+
"最近一场": "Latest dream",
|
|
255
|
+
" 种": " moods",
|
|
256
|
+
"最常见:": "Most common: ",
|
|
257
|
+
"梦境时间线": "Dream timeline",
|
|
258
|
+
"筛选结果": "Filtered results",
|
|
259
|
+
"搜索结果": "Search results",
|
|
260
|
+
"没有符合筛选的梦(筛选作用于已加载的 ": "No matching dreams (filters apply to the ",
|
|
261
|
+
" 场)。": " loaded dreams).",
|
|
262
|
+
"梦境面板视图": "Dream views",
|
|
263
|
+
"日记": "Journal",
|
|
264
|
+
"隐私模式:显示层模糊;DOM 仍为明文,不改文件、不加密。点击卡片可临时查看。": "Privacy mode blurs the display only. The DOM remains readable; files are unchanged and not encrypted. Click a card to reveal it temporarily.",
|
|
265
|
+
"显示更早的梦": "Show older dreams",
|
|
266
|
+
"数据来自梦境日记(dreams.jsonl)": "Data from the dream journal (dreams.jsonl)",
|
|
267
|
+
"梦境日记": "Dream journal",
|
|
268
|
+
"会话回放 → 反思 → 记忆巩固。这里按时间倒序陈列 agent 做过的梦,附心境方块与教训榜。": "Session replay → reflection → memory consolidation. Browse your agent’s dreams, mood history and recurring observations, newest first."
|
|
269
|
+
});
|
|
270
|
+
function languageOf(locale = localeService) {
|
|
271
|
+
try { return String(locale?.getSnapshot?.().active || '').startsWith('en') ? 'en' : 'zh'; } catch { return 'zh'; }
|
|
272
|
+
}
|
|
273
|
+
function t(zh) { return languageOf() === 'en' ? (EN[zh] ?? zh) : zh; }
|
|
274
|
+
function displayError(error) {
|
|
275
|
+
const message = error?.message || String(error || '');
|
|
276
|
+
if (languageOf() !== 'en') return message;
|
|
277
|
+
if (EN[message]) return EN[message];
|
|
278
|
+
if (!/[\u3400-\u9fff]/.test(message)) return message;
|
|
279
|
+
const hints = {
|
|
280
|
+
revision: 'This lesson or project changed. Refresh and review again; your input is preserved.',
|
|
281
|
+
conflict: 'The target changed. Preview the current rules again before applying.',
|
|
282
|
+
'preview-mismatch': 'The preview no longer matches. Create a new preview.',
|
|
283
|
+
'preview-expired': 'The preview expired. Create a new preview.',
|
|
284
|
+
'not-rollbackable': 'Rollback is unavailable because the managed block changed or its backup is missing.',
|
|
285
|
+
'ineligible': 'This lesson is not eligible for rule writing. Check its scope and review state.',
|
|
286
|
+
'read-only': 'The target is read-only. Check its permissions before retrying.',
|
|
287
|
+
'marker': 'The managed block markers are invalid. Check the file before retrying.',
|
|
288
|
+
'path-outside-project': 'The target must stay inside the project directory.',
|
|
289
|
+
'unsupported-encoding': 'The target file encoding is unsupported. Use UTF-8.',
|
|
290
|
+
'target-is-directory': 'The selected target is a directory; select a file.',
|
|
291
|
+
'not-found': 'The lesson, target or backup was not found. Refresh and check the project.',
|
|
292
|
+
forbidden: 'This operation must be performed from the authenticated Dream panel.',
|
|
293
|
+
'too-large': 'The request is too large. Shorten the input and try again.',
|
|
294
|
+
cancelled: 'The operation was cancelled. Refresh to check the result before retrying.',
|
|
295
|
+
io: 'The operation could not access its files. Check the project location and file permissions, then refresh before retrying.',
|
|
296
|
+
internal: 'Could not load the records. Check the DSH logs and retry.',
|
|
297
|
+
invalid: 'The input or selected record is no longer valid. Check the fields and refresh the list.',
|
|
298
|
+
};
|
|
299
|
+
return (hints[error?.code] || 'The operation failed. Check the DSH logs, then refresh before retrying.')
|
|
300
|
+
+ (error?.status ? ' (HTTP ' + error.status + (error.code ? ', ' + error.code : '') + ')' : '');
|
|
301
|
+
}
|
|
302
|
+
function rollbackReasonText(reason) {
|
|
303
|
+
if (languageOf() !== 'en') return reason;
|
|
304
|
+
return ({ '目标文件不可读': 'Target file is unreadable', '文件不存在': 'File does not exist',
|
|
305
|
+
'管理块标记异常': 'Invalid managed block markers', '管理块已被移除': 'Managed block was removed',
|
|
306
|
+
'管理块已被外部修改': 'Managed block changed externally' })[reason]
|
|
307
|
+
|| (/[\u3400-\u9fff]/.test(reason) ? 'Check the target file and backup' : reason);
|
|
308
|
+
}
|
|
309
|
+
function scopeText(label) {
|
|
310
|
+
if (languageOf() !== 'en') return label;
|
|
311
|
+
return label.replace(/^全局$/, 'Global').replace(/^工作区 /, 'Workspace ').replace(/^项目 /, 'Project ');
|
|
312
|
+
}
|
|
313
|
+
function evidenceText(summary) {
|
|
314
|
+
if (languageOf() !== 'en') return summary;
|
|
315
|
+
return summary.replace(/^独立证据 (\d+) 条(已读 (\d+) \/ 仅声明 (\d+)):/, 'Independent sources: $1 (read: $2 / claimed: $3): ');
|
|
316
|
+
}
|
|
317
|
+
function useLanguage(locale) {
|
|
318
|
+
const [, rerender] = useState(0);
|
|
319
|
+
useEffect(() => { const update = () => rerender(n => n + 1); update(); return locale?.subscribe?.(update); }, [locale]);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
22
323
|
// 相对路径按 document.baseURI 解析:反代挂在 /tools/dsh/ 这类前缀下也不会打偏。
|
|
23
324
|
const ROUTE = "_dsh/dsh-dream/journal";
|
|
24
325
|
const ACTION_ROUTE = "api/dsh-dream/actions";
|
|
@@ -74,7 +375,7 @@ function errorMessageOf(body, status) {
|
|
|
74
375
|
if (body.error !== null && typeof body.error === "object" && typeof body.error.message === "string" && body.error.message !== "") return body.error.message;
|
|
75
376
|
if (typeof body.message === "string" && body.message !== "") return body.message;
|
|
76
377
|
}
|
|
77
|
-
return "请求失败(HTTP " + String(status) + ")";
|
|
378
|
+
return t("请求失败(HTTP ") + String(status) + ")";
|
|
78
379
|
}
|
|
79
380
|
|
|
80
381
|
/** GET /_dsh/dsh-dream/journal:契约体 { dreams, stats, query, limit }。 */
|
|
@@ -87,7 +388,7 @@ async function fetchJournal(query, limit) {
|
|
|
87
388
|
const res = await fetch(routeUrl(params.join("&")), init);
|
|
88
389
|
let body = null;
|
|
89
390
|
try { body = await res.json(); } catch (error) { body = null; }
|
|
90
|
-
if (body === null || typeof body !== "object") throw new Error("服务器返回的不是 JSON(HTTP " + String(res.status) + ")");
|
|
391
|
+
if (body === null || typeof body !== "object") throw new Error(t("服务器返回的不是 JSON(HTTP ") + String(res.status) + ")");
|
|
91
392
|
if (res.ok !== true || body.ok === false) {
|
|
92
393
|
const failure = new Error(errorMessageOf(body, res.status));
|
|
93
394
|
failure.status = res.status;
|
|
@@ -111,7 +412,7 @@ async function fetchKnowledge() {
|
|
|
111
412
|
const res = await fetch(routeUrlFor(KNOWLEDGE_ROUTE, ""), init);
|
|
112
413
|
let body = null;
|
|
113
414
|
try { body = await res.json(); } catch (error) { body = null; }
|
|
114
|
-
if (body === null || typeof body !== "object") throw new Error("服务器返回的不是 JSON(HTTP " + String(res.status) + ")");
|
|
415
|
+
if (body === null || typeof body !== "object") throw new Error(t("服务器返回的不是 JSON(HTTP ") + String(res.status) + ")");
|
|
115
416
|
if (res.ok !== true || body.ok === false) {
|
|
116
417
|
const failure = new Error(errorMessageOf(body, res.status));
|
|
117
418
|
failure.status = res.status;
|
|
@@ -134,7 +435,7 @@ async function fetchBridgeRecords() {
|
|
|
134
435
|
const res = await fetch(routeUrlFor(BRIDGE_ROUTE, ""), init);
|
|
135
436
|
let body = null;
|
|
136
437
|
try { body = await res.json(); } catch (error) { body = null; }
|
|
137
|
-
if (body === null || typeof body !== "object") throw new Error("服务器返回的不是 JSON(HTTP " + String(res.status) + ")");
|
|
438
|
+
if (body === null || typeof body !== "object") throw new Error(t("服务器返回的不是 JSON(HTTP ") + String(res.status) + ")");
|
|
138
439
|
if (res.ok !== true || body.ok === false) {
|
|
139
440
|
const failure = new Error(errorMessageOf(body, res.status));
|
|
140
441
|
failure.status = res.status;
|
|
@@ -155,8 +456,8 @@ async function postAction(body) {
|
|
|
155
456
|
let value = null;
|
|
156
457
|
try { value = await res.json(); } catch (error) { /* Preserve HTTP status for useful recovery hints. */ }
|
|
157
458
|
if (res.ok !== true || value === null || value.ok === false) {
|
|
158
|
-
const hint = res.status === 401 ? "登录状态已失效,请重新打开 DSH 页面后重试。"
|
|
159
|
-
: res.status === 404 ? "当前宿主尚未提供此操作,请升级 DSH,或使用下方对话命令。"
|
|
459
|
+
const hint = res.status === 401 ? t("登录状态已失效,请重新打开 DSH 页面后重试。")
|
|
460
|
+
: res.status === 404 ? t("当前宿主尚未提供此操作,请升级 DSH,或使用下方对话命令。")
|
|
160
461
|
: errorMessageOf(value, res.status);
|
|
161
462
|
const failure = new Error(hint);
|
|
162
463
|
failure.code = value && value.error ? value.error.code : undefined;
|
|
@@ -257,12 +558,12 @@ function normalizeLessonState(value) {
|
|
|
257
558
|
/** 状态 → 中文标签(面板文案集中在这里)。 */
|
|
258
559
|
function lessonStateLabel(state) {
|
|
259
560
|
switch (state) {
|
|
260
|
-
case "candidate": return "候选(未采纳)";
|
|
261
|
-
case "usable": return "可用";
|
|
262
|
-
case "disputed": return "有冲突";
|
|
263
|
-
case "stale": return "待复核";
|
|
264
|
-
case "rejected": return "已驳回";
|
|
265
|
-
default: return "未知状态";
|
|
561
|
+
case "candidate": return t("候选(未采纳)");
|
|
562
|
+
case "usable": return t("可用");
|
|
563
|
+
case "disputed": return t("有冲突");
|
|
564
|
+
case "stale": return t("待复核");
|
|
565
|
+
case "rejected": return t("已驳回");
|
|
566
|
+
default: return t("未知状态");
|
|
266
567
|
}
|
|
267
568
|
}
|
|
268
569
|
|
|
@@ -295,11 +596,11 @@ function applicabilityTextOf(value) {
|
|
|
295
596
|
const main = pkg !== "" ? (versions !== "" ? pkg + " " + versions : pkg) : versions;
|
|
296
597
|
const bits = [];
|
|
297
598
|
if (main !== "") bits.push(main);
|
|
298
|
-
if (platform !== "") bits.push("平台 " + platform);
|
|
599
|
+
if (platform !== "") bits.push(t("平台 ") + platform);
|
|
299
600
|
if (bits.length > 0) parts.push(bits.join(" · "));
|
|
300
601
|
}
|
|
301
602
|
if (parts.length === 0) return "";
|
|
302
|
-
if (parts.length > 4) return parts.slice(0, 4).join(";") + ";等 " + String(parts.length) + " 条适用范围";
|
|
603
|
+
if (parts.length > 4) return parts.slice(0, 4).join(";") + t(";等 ") + String(parts.length) + t(" 条适用范围");
|
|
303
604
|
return parts.join(";");
|
|
304
605
|
}
|
|
305
606
|
|
|
@@ -343,22 +644,22 @@ function normalizeKnowledge(value, now, limit) {
|
|
|
343
644
|
state: state,
|
|
344
645
|
stateLabel: lessonStateLabel(state),
|
|
345
646
|
stateClass: lessonStateClass(state),
|
|
346
|
-
scopeLabel: trimmedText(raw.scopeLabel) || "范围未标注",
|
|
347
|
-
evidenceSummary: trimmedText(raw.evidenceSummary) || "暂无可展示的证据摘要",
|
|
647
|
+
scopeLabel: scopeText(trimmedText(raw.scopeLabel)) || t("范围未标注"),
|
|
648
|
+
evidenceSummary: evidenceText(trimmedText(raw.evidenceSummary)) || t("暂无可展示的证据摘要"),
|
|
348
649
|
evidence: Array.isArray(source.evidence) && Array.isArray(raw.evidenceIds)
|
|
349
650
|
? source.evidence.filter(function (item) { return item && raw.evidenceIds.indexOf(item.id) !== -1; }).slice(0, 20) : [],
|
|
350
651
|
feedback: raw.feedback && typeof raw.feedback === "object" ? raw.feedback : { useful: 0, notApplicable: 0, recent: [] },
|
|
351
652
|
lastValidatedAt: lastValidatedAt,
|
|
352
|
-
lastText: lastValidatedAt === "" ? "尚未核验" : relativeTime(lastValidatedAt, now),
|
|
353
|
-
lastTitle: lastValidatedAt === "" ? "没有 lastValidatedAt(从未核验)" : formatDate(lastValidatedAt) + (formatTime(lastValidatedAt) === "" ? "" : " " + formatTime(lastValidatedAt)),
|
|
653
|
+
lastText: lastValidatedAt === "" ? t("尚未核验") : relativeTime(lastValidatedAt, now),
|
|
654
|
+
lastTitle: lastValidatedAt === "" ? t("没有 lastValidatedAt(从未核验)") : formatDate(lastValidatedAt) + (formatTime(lastValidatedAt) === "" ? "" : " " + formatTime(lastValidatedAt)),
|
|
354
655
|
revision: typeof raw.revision === "number" && Number.isFinite(raw.revision) && raw.revision >= 0 ? Math.floor(raw.revision) : 0,
|
|
355
656
|
independentSupportCount: typeof raw.independentSupportCount === "number" && raw.independentSupportCount > 0 ? Math.floor(raw.independentSupportCount) : 0,
|
|
356
657
|
projectId: typeof raw.scope === "object" && raw.scope !== null ? trimmedText(raw.scope.projectId) : "",
|
|
357
658
|
workspaceRoot: typeof raw.scope === "object" && raw.scope !== null ? trimmedText(raw.scope.workspaceRoot) : "",
|
|
358
659
|
conflictIds: Array.isArray(raw.conflictIds) ? raw.conflictIds.filter(function (id) { return typeof id === "string" && id.trim() !== ""; }) : [],
|
|
359
|
-
conflicts: Array.isArray(raw.conflictIds) ? raw.conflictIds.filter(function (id) { return typeof id === "string" && id.trim() !== ""; }).map(function (id) { return { id: id, title: lessonTitles.get(id) || "未加载的经验(" + id + ")" }; }) : [],
|
|
660
|
+
conflicts: Array.isArray(raw.conflictIds) ? raw.conflictIds.filter(function (id) { return typeof id === "string" && id.trim() !== ""; }).map(function (id) { return { id: id, title: lessonTitles.get(id) || t("未加载的经验(") + id + ")" }; }) : [],
|
|
360
661
|
holdReason: trimmedText(raw.holdReason) !== "" ? trimmedText(raw.holdReason) : holdReasonOf(raw),
|
|
361
|
-
holdReasonLabel: trimmedText(raw.holdReasonLabel) !== "" ? trimmedText(raw.holdReasonLabel) : skippedReasonLabel(holdReasonOf(raw)),
|
|
662
|
+
holdReasonLabel: trimmedText(raw.holdReasonLabel) !== "" ? t(trimmedText(raw.holdReasonLabel)) : skippedReasonLabel(holdReasonOf(raw)),
|
|
362
663
|
bridgeable: bridgeableOf(raw.state) && trimmedText(raw.id) !== "",
|
|
363
664
|
candidate: state === "candidate",
|
|
364
665
|
disputed: state === "disputed",
|
|
@@ -387,7 +688,7 @@ function knowledgeCountText(stats) {
|
|
|
387
688
|
}
|
|
388
689
|
if (parts.length === 0) {
|
|
389
690
|
const total = typeof source.lessons === "number" && source.lessons > 0 ? source.lessons : 0;
|
|
390
|
-
return total > 0 ? "共 " + String(total) + " 条" : "还没有经验";
|
|
691
|
+
return total > 0 ? t("共 ") + String(total) + t(" 条") : t("还没有经验");
|
|
391
692
|
}
|
|
392
693
|
return parts.join(" · ");
|
|
393
694
|
}
|
|
@@ -399,32 +700,32 @@ function knowledgeNoticeText(stats) {
|
|
|
399
700
|
const badLines = typeof source.badLines === "number" && Number.isFinite(source.badLines) && source.badLines > 0 ? Math.floor(source.badLines) : 0;
|
|
400
701
|
if (truncated !== true && badLines === 0) return "";
|
|
401
702
|
const parts = [];
|
|
402
|
-
if (truncated === true) parts.push("日志过大,仅回放最近一段事件");
|
|
403
|
-
if (badLines > 0) parts.push("有 " + String(badLines) + " 行损坏已跳过");
|
|
404
|
-
return parts.join(";") + ";events.jsonl 未改动。";
|
|
703
|
+
if (truncated === true) parts.push(t("日志过大,仅回放最近一段事件"));
|
|
704
|
+
if (badLines > 0) parts.push(t("有 ") + String(badLines) + t(" 行损坏已跳过"));
|
|
705
|
+
return parts.join(";") + t(";events.jsonl 未改动。");
|
|
405
706
|
}
|
|
406
707
|
|
|
407
708
|
/** 经验区块的独立空态:与「还没有梦境日记」分开,说明经验从哪来。 */
|
|
408
709
|
function knowledgeEmptyState() {
|
|
409
710
|
return {
|
|
410
|
-
title: "还没有经验记录",
|
|
411
|
-
body: "经验是带证据、有适用条件的技术结论:收尾时用 dream_learn 提交候选,证据足够、范围明确的会出现在这里。",
|
|
412
|
-
hint: "旧日记里的教训不会自动升级为经验;采纳、驳回或标记冲突请用 dream_review。",
|
|
711
|
+
title: t("还没有经验记录"),
|
|
712
|
+
body: t("经验是带证据、有适用条件的技术结论:收尾时用 dream_learn 提交候选,证据足够、范围明确的会出现在这里。"),
|
|
713
|
+
hint: t("旧日记里的教训不会自动升级为经验;采纳、驳回或标记冲突请用 dream_review。"),
|
|
413
714
|
};
|
|
414
715
|
}
|
|
415
716
|
|
|
416
717
|
/** 检索层 skipped reason 的中文映射(面板扣留原因文案;与 retrieval.ts 的 reason 常量同名)。 */
|
|
417
718
|
function skippedReasonLabel(reason) {
|
|
418
719
|
switch (trimmedText(reason)) {
|
|
419
|
-
case "state:rejected": return "已驳回:不参与默认检索与注入";
|
|
420
|
-
case "state:stale": return "待复核:条件或依赖可能已过期,默认不注入";
|
|
421
|
-
case "state:disputed": return "冲突未解决:默认不注入,先 resolve-conflict";
|
|
422
|
-
case "candidate-hold": return "候选:可被检索到,但默认不作为任务建议注入";
|
|
423
|
-
case "no-evidence": return "证据不足:没有独立证据,默认不注入";
|
|
424
|
-
case "version-mismatch": return "版本不匹配:当前依赖版本不满足适用范围";
|
|
425
|
-
case "limit": return "超出条数预算,本次未取回";
|
|
426
|
-
case "budget-stop": return "超出字符预算:按排名整条装入,遇到第一条放不下即停";
|
|
427
|
-
case "budget": return "超出字符预算,本次未取回";
|
|
720
|
+
case "state:rejected": return t("已驳回:不参与默认检索与注入");
|
|
721
|
+
case "state:stale": return t("待复核:条件或依赖可能已过期,默认不注入");
|
|
722
|
+
case "state:disputed": return t("冲突未解决:默认不注入,先 resolve-conflict");
|
|
723
|
+
case "candidate-hold": return t("候选:可被检索到,但默认不作为任务建议注入");
|
|
724
|
+
case "no-evidence": return t("证据不足:没有独立证据,默认不注入");
|
|
725
|
+
case "version-mismatch": return t("版本不匹配:当前依赖版本不满足适用范围");
|
|
726
|
+
case "limit": return t("超出条数预算,本次未取回");
|
|
727
|
+
case "budget-stop": return t("超出字符预算:按排名整条装入,遇到第一条放不下即停");
|
|
728
|
+
case "budget": return t("超出字符预算,本次未取回");
|
|
428
729
|
default: return "";
|
|
429
730
|
}
|
|
430
731
|
}
|
|
@@ -506,12 +807,12 @@ function shortHash(value) {
|
|
|
506
807
|
|
|
507
808
|
function bridgeActionLabel(action) {
|
|
508
809
|
switch (trimmedText(action)) {
|
|
509
|
-
case "create": return "新建";
|
|
510
|
-
case "replace": return "替换管理块";
|
|
511
|
-
case "append": return "追加管理块";
|
|
512
|
-
case "unchanged": return "未改动";
|
|
513
|
-
case "rollback": return "回滚";
|
|
514
|
-
default: return trimmedText(action) || "未知动作";
|
|
810
|
+
case "create": return t("新建");
|
|
811
|
+
case "replace": return t("替换管理块");
|
|
812
|
+
case "append": return t("追加管理块");
|
|
813
|
+
case "unchanged": return t("未改动");
|
|
814
|
+
case "rollback": return t("回滚");
|
|
815
|
+
default: return trimmedText(action) || t("未知动作");
|
|
515
816
|
}
|
|
516
817
|
}
|
|
517
818
|
|
|
@@ -549,17 +850,17 @@ function normalizeBridgeRecords(value, now, limit) {
|
|
|
549
850
|
rows.push({
|
|
550
851
|
id: id,
|
|
551
852
|
at: at,
|
|
552
|
-
atText: at === "" ? "时间未知" : relativeTime(at, now),
|
|
553
|
-
atTitle: at === "" ? "没有记录时间" : formatDate(at) + (formatTime(at) === "" ? "" : " " + formatTime(at)),
|
|
554
|
-
target: target || "目标未记录",
|
|
853
|
+
atText: at === "" ? t("时间未知") : relativeTime(at, now),
|
|
854
|
+
atTitle: at === "" ? t("没有记录时间") : formatDate(at) + (formatTime(at) === "" ? "" : " " + formatTime(at)),
|
|
855
|
+
target: target || t("目标未记录"),
|
|
555
856
|
action: trimmedText(raw.action),
|
|
556
857
|
actionLabel: bridgeActionLabel(raw.action),
|
|
557
858
|
lessons: lessonRefs,
|
|
558
|
-
lessonsText: lessonRefs.length === 0 ? "没有经验引用" : lessonRefs.join("、"),
|
|
859
|
+
lessonsText: lessonRefs.length === 0 ? t("没有经验引用") : lessonRefs.join("、"),
|
|
559
860
|
beforeShort: shortHash(raw.beforeSha256),
|
|
560
861
|
afterShort: shortHash(raw.afterSha256),
|
|
561
862
|
rollbackable: rollbackable,
|
|
562
|
-
rollbackText: rollbackable ? "可回滚" : ("不可回滚" + (rollbackReason !== "" ?
|
|
863
|
+
rollbackText: rollbackable ? t("可回滚") : (t("不可回滚") + (rollbackReason !== "" ? (languageOf() === 'en' ? ': ' : ':') + rollbackReasonText(rollbackReason) : "")),
|
|
563
864
|
rollbackReason: rollbackReason,
|
|
564
865
|
});
|
|
565
866
|
}
|
|
@@ -576,15 +877,15 @@ function normalizeBridgeRecords(value, now, limit) {
|
|
|
576
877
|
/** 「写入记录」的独立空态。 */
|
|
577
878
|
function bridgeEmptyState() {
|
|
578
879
|
return {
|
|
579
|
-
title: "还没有写入记录",
|
|
580
|
-
body: "写入记录来自 dream_bridge 的 preview → apply;面板只读展示目标、时间、经验 revision 与前后哈希,不会写文件。",
|
|
581
|
-
hint: "在可桥接经验行点「复制预览命令」,回到对话里执行;写操作只在工具中完成。",
|
|
880
|
+
title: t("还没有写入记录"),
|
|
881
|
+
body: t("写入记录来自 dream_bridge 的 preview → apply;面板只读展示目标、时间、经验 revision 与前后哈希,不会写文件。"),
|
|
882
|
+
hint: t("在可桥接经验行点「复制预览命令」,回到对话里执行;写操作只在工具中完成。"),
|
|
582
883
|
};
|
|
583
884
|
}
|
|
584
885
|
|
|
585
886
|
/** 「待核实」的独立空态。 */
|
|
586
887
|
function reviewQueueEmptyState() {
|
|
587
|
-
return "没有待核实的经验 —— 候选、有冲突、待复核的经验会出现在这里,并写明被扣下的原因。";
|
|
888
|
+
return t("没有待核实的经验 —— 候选、有冲突、待复核的经验会出现在这里,并写明被扣下的原因。");
|
|
588
889
|
}
|
|
589
890
|
|
|
590
891
|
function spanOf(dreams) {
|
|
@@ -602,7 +903,7 @@ function spanOf(dreams) {
|
|
|
602
903
|
|
|
603
904
|
function formatDate(iso) {
|
|
604
905
|
const date = new Date(iso);
|
|
605
|
-
if (!Number.isFinite(date.getTime())) return iso === "" ? "时间未知" : iso;
|
|
906
|
+
if (!Number.isFinite(date.getTime())) return iso === "" ? t("时间未知") : iso;
|
|
606
907
|
return date.getFullYear() + "-" + pad2(date.getMonth() + 1) + "-" + pad2(date.getDate());
|
|
607
908
|
}
|
|
608
909
|
|
|
@@ -633,10 +934,10 @@ function relativeTime(iso, now) {
|
|
|
633
934
|
const minute = 60000;
|
|
634
935
|
const hour = 3600000;
|
|
635
936
|
const day = 86400000;
|
|
636
|
-
if (diff < minute) return "刚刚";
|
|
637
|
-
if (diff < hour) return Math.floor(diff / minute) + " 分钟前";
|
|
638
|
-
if (diff < day) return Math.floor(diff / hour) + " 小时前";
|
|
639
|
-
if (diff < day * 30) return Math.floor(diff / day) + " 天前";
|
|
937
|
+
if (diff < minute) return t("刚刚");
|
|
938
|
+
if (diff < hour) return Math.floor(diff / minute) + t(" 分钟前");
|
|
939
|
+
if (diff < day) return Math.floor(diff / hour) + t(" 小时前");
|
|
940
|
+
if (diff < day * 30) return Math.floor(diff / day) + t(" 天前");
|
|
640
941
|
return formatDate(iso);
|
|
641
942
|
}
|
|
642
943
|
|
|
@@ -648,9 +949,9 @@ function recentText(iso, now) {
|
|
|
648
949
|
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
649
950
|
const that = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
650
951
|
const days = Math.round((today.getTime() - that.getTime()) / 86400000);
|
|
651
|
-
if (days <= 0) return "今天";
|
|
652
|
-
if (days === 1) return "昨天";
|
|
653
|
-
if (days < 30) return String(days) + " 天前";
|
|
952
|
+
if (days <= 0) return t("今天");
|
|
953
|
+
if (days === 1) return t("昨天");
|
|
954
|
+
if (days < 30) return String(days) + t(" 天前");
|
|
654
955
|
return formatDate(iso);
|
|
655
956
|
}
|
|
656
957
|
|
|
@@ -744,23 +1045,23 @@ function needsOlderDreams(loaded, total) {
|
|
|
744
1045
|
}
|
|
745
1046
|
|
|
746
1047
|
function countTextOf(query, total, count, filterCount, visibleCount) {
|
|
747
|
-
if (filterCount > 0) return "筛选后 " + String(visibleCount) + " / " + String(count) + " 场";
|
|
748
|
-
if (query !== "") return "「" + query + "」命中 " + String(count) + " 场";
|
|
749
|
-
return "共 " + String(total) + " 场梦";
|
|
1048
|
+
if (filterCount > 0) return t("筛选后 ") + String(visibleCount) + " / " + String(count) + t(" 场");
|
|
1049
|
+
if (query !== "") return (languageOf() === "en" ? "“" : "「") + query + t("」命中 ") + String(count) + t(" 场");
|
|
1050
|
+
return t("共 ") + String(total) + t(" 场梦");
|
|
750
1051
|
}
|
|
751
1052
|
|
|
752
1053
|
function listHintOf(query, count, filterCount, visibleCount) {
|
|
753
|
-
if (filterCount > 0) return "筛选当前已加载的 " + String(count) + " 场 · 命中 " + String(visibleCount) + " 场";
|
|
754
|
-
if (query !== "") return "「" + query + "」命中 " + String(count) + " 场";
|
|
755
|
-
return "新梦在前";
|
|
1054
|
+
if (filterCount > 0) return t("筛选当前已加载的 ") + String(count) + t(" 场 · 命中 ") + String(visibleCount) + t(" 场");
|
|
1055
|
+
if (query !== "") return (languageOf() === "en" ? "“" : "「") + query + t("」命中 ") + String(count) + t(" 场");
|
|
1056
|
+
return t("新梦在前");
|
|
756
1057
|
}
|
|
757
1058
|
|
|
758
1059
|
function footerTextOf(query, limit, total, count) {
|
|
759
1060
|
if (limit > DEFAULT_LIMIT) {
|
|
760
|
-
const capped = limit >= MORE_LIMIT && count < total ? "(已达路由上限)" : "";
|
|
761
|
-
return "最近 " + String(limit) + " 场 / 共 " + String(total) + " 场" + capped;
|
|
1061
|
+
const capped = limit >= MORE_LIMIT && count < total ? t("(已达路由上限)") : "";
|
|
1062
|
+
return t("最近 ") + String(limit) + t(" 场 / 共 ") + String(total) + t(" 场") + capped;
|
|
762
1063
|
}
|
|
763
|
-
return query === "" ? "新梦在前 · 最多显示 " + String(DEFAULT_LIMIT) + " 场" : "搜索结果 · 最多显示 " + String(DEFAULT_LIMIT) + " 场";
|
|
1064
|
+
return query === "" ? t("新梦在前 · 最多显示 ") + String(DEFAULT_LIMIT) + t(" 场") : t("搜索结果 · 最多显示 ") + String(DEFAULT_LIMIT) + t(" 场");
|
|
764
1065
|
}
|
|
765
1066
|
|
|
766
1067
|
/** 面板的视图模型:所有文案/数字集中在这里算,组件只负责画。 */
|
|
@@ -784,15 +1085,15 @@ function viewModelOf(dreams, stats, now) {
|
|
|
784
1085
|
share: Math.max(6, Math.round(row.count / Math.max(lessonTotal, 1) * 100)),
|
|
785
1086
|
showBar: row.count >= 2,
|
|
786
1087
|
candidate: row.count >= 3,
|
|
787
|
-
lastText: knownAt === "" ? "" : (fromStats ? "最近 " : "本页内最近 ") + recentText(knownAt, now),
|
|
1088
|
+
lastText: knownAt === "" ? "" : (fromStats ? t("最近 ") : t("本页内最近 ")) + recentText(knownAt, now),
|
|
788
1089
|
lastTitle: knownAt === "" ? "" : formatDate(knownAt) + (formatTime(knownAt) === "" ? "" : " " + formatTime(knownAt))
|
|
789
|
-
+ (fromStats ? "" : "(统计未给 lastAt,按本页已加载的 " + String(list.length) + " 场现算)"),
|
|
1090
|
+
+ (fromStats ? "" : t("(统计未给 lastAt,按本页已加载的 ") + String(list.length) + t(" 场现算)")),
|
|
790
1091
|
};
|
|
791
1092
|
});
|
|
792
1093
|
const span = spanOf(list);
|
|
793
1094
|
const spanText = span === null ? "" : spanTextOf(span);
|
|
794
1095
|
const spanFull = span === null ? "" : formatDate(new Date(span.from).toISOString()) + " → " + formatDate(new Date(span.to).toISOString());
|
|
795
|
-
const spanHint = span === null ? "" : (span.days === 0 ? "同一天" : "约 " + String(span.days) + " 天");
|
|
1096
|
+
const spanHint = span === null ? "" : (span.days === 0 ? t("同一天") : t("约 ") + String(span.days) + t(" 天"));
|
|
796
1097
|
const blocks = list.slice().reverse().map((dream, index) => ({
|
|
797
1098
|
key: "block-" + (dream.id !== "" ? dream.id : "no-id") + "-" + String(index),
|
|
798
1099
|
tone: toneClass(moodSlot(dream.mood)),
|
|
@@ -823,12 +1124,12 @@ function headlineOf(dreams, total, span, now) {
|
|
|
823
1124
|
if (dreams.length === 0) return "";
|
|
824
1125
|
if (dreams.length === 1 && total <= 1) {
|
|
825
1126
|
const dream = dreams[0];
|
|
826
|
-
return "第 1 场梦 · " + formatDate(dream.at) + " · 心境「" + dream.mood + "」";
|
|
1127
|
+
return t("第 1 场梦 · ") + formatDate(dream.at) + t(" · 心境「") + dream.mood + (languageOf() === "en" ? "”" : "」");
|
|
827
1128
|
}
|
|
828
1129
|
const parts = [];
|
|
829
|
-
parts.push(total > dreams.length ? "共 " + String(total) + " 场梦 · 本页最近 " + String(dreams.length) + " 场" : String(dreams.length) + " 场梦");
|
|
1130
|
+
parts.push(total > dreams.length ? t("共 ") + String(total) + t(" 场梦 · 本页最近 ") + String(dreams.length) + t(" 场") : String(dreams.length) + t(" 场梦"));
|
|
830
1131
|
if (span !== null) parts.push(span.from === span.to ? formatDate(new Date(span.from).toISOString()) : formatDate(new Date(span.from).toISOString()) + " → " + formatDate(new Date(span.to).toISOString()));
|
|
831
|
-
parts.push("最近:" + relativeTime(dreams[0].at, now));
|
|
1132
|
+
parts.push(t("最近:") + relativeTime(dreams[0].at, now));
|
|
832
1133
|
return parts.join(" · ");
|
|
833
1134
|
}
|
|
834
1135
|
|
|
@@ -873,20 +1174,20 @@ function StatTile(props) {
|
|
|
873
1174
|
function MoodCard(props) {
|
|
874
1175
|
const vm = props.vm;
|
|
875
1176
|
return h("section", { className: "dshd-card" }, [
|
|
876
|
-
h("h4", { key: "t" }, "心境"),
|
|
1177
|
+
h("h4", { key: "t" }, t("心境")),
|
|
877
1178
|
h("div", { className: "dshd-mblocks", key: "blocks" }, vm.blocks.map((block) => h("span", {
|
|
878
1179
|
key: block.key,
|
|
879
1180
|
className: "dshd-mblock " + block.tone,
|
|
880
1181
|
title: block.title,
|
|
881
1182
|
}))),
|
|
882
1183
|
vm.count === 1
|
|
883
|
-
? h("div", { className: "dshd-hint", key: "one" }, "1 场梦,先不谈分布 —— 再做梦会累积。")
|
|
1184
|
+
? h("div", { className: "dshd-hint", key: "one" }, t("1 场梦,先不谈分布 —— 再做梦会累积。"))
|
|
884
1185
|
: h("div", { key: "legend" }, [
|
|
885
|
-
h("div", { className: "dshd-note", key: "cap" }, "左 → 右:从早到晚"),
|
|
1186
|
+
h("div", { className: "dshd-note", key: "cap" }, t("左 → 右:从早到晚")),
|
|
886
1187
|
h("div", { className: "dshd-legend", key: "rows" }, vm.moodRows.map((row, index) => h("div", { className: "dshd-legend-row", key: "mood-" + String(index) + "-" + row.mood }, [
|
|
887
1188
|
h("span", { className: "dshd-mblock " + toneClass(moodSlot(row.mood)), key: "d" }),
|
|
888
1189
|
h("span", { className: "dshd-legend-name", key: "n", title: row.mood }, row.mood),
|
|
889
|
-
h("span", { className: "dshd-legend-count", key: "c" }, String(row.count) + " 场"),
|
|
1190
|
+
h("span", { className: "dshd-legend-count", key: "c" }, String(row.count) + t(" 场")),
|
|
890
1191
|
]))),
|
|
891
1192
|
]),
|
|
892
1193
|
]);
|
|
@@ -895,9 +1196,9 @@ function MoodCard(props) {
|
|
|
895
1196
|
function LessonCard(props) {
|
|
896
1197
|
const vm = props.vm;
|
|
897
1198
|
return h("section", { className: "dshd-card" }, [
|
|
898
|
-
h("h4", { key: "t" }, "教训榜"),
|
|
1199
|
+
h("h4", { key: "t" }, t("教训榜")),
|
|
899
1200
|
vm.lessonRows.length === 0
|
|
900
|
-
? h("div", { className: "dshd-hint", key: "e" }, "还没有教训 —— 记梦时带上 lessons,出现次数会在这里累积。")
|
|
1201
|
+
? h("div", { className: "dshd-hint", key: "e" }, t("还没有教训 —— 记梦时带上 lessons,出现次数会在这里累积。"))
|
|
901
1202
|
: h("div", { key: "rows" }, vm.lessonRows.map((row, index) => h("div", { className: "dshd-lesson", key: "lesson-" + String(index) + "-" + row.lesson }, [
|
|
902
1203
|
h("span", { className: "dshd-rank", key: "r" }, String(row.rank)),
|
|
903
1204
|
h("span", { className: "dshd-lesson-body", key: "b" }, [
|
|
@@ -907,10 +1208,10 @@ function LessonCard(props) {
|
|
|
907
1208
|
h("span", { className: "dshd-lesson-meta", key: "m" }, [
|
|
908
1209
|
h("span", { className: "dshd-times", key: "c" }, "×", h("b", { key: "n" }, String(row.count))),
|
|
909
1210
|
row.lastText === "" ? null : h("span", { className: "dshd-last", key: "l", title: row.lastTitle }, row.lastText),
|
|
910
|
-
row.candidate ? h("span", { className: "dshd-badge", key: "badge" }, "常见教训") : null,
|
|
1211
|
+
row.candidate ? h("span", { className: "dshd-badge", key: "badge" }, t("常见教训")) : null,
|
|
911
1212
|
]),
|
|
912
1213
|
]))),
|
|
913
|
-
h("div", { className: "dshd-note", key: "note" }, "次数仅表示日记里的出现频率;出现 ≥3 次标为「常见教训」。写入项目规则前仍需核验来源并审阅经验。最近时间取自 stats.lastAt;统计缺失时按本页已加载的梦现算。"),
|
|
1214
|
+
h("div", { className: "dshd-note", key: "note" }, t("次数仅表示日记里的出现频率;出现 ≥3 次标为「常见教训」。写入项目规则前仍需核验来源并审阅经验。最近时间取自 stats.lastAt;统计缺失时按本页已加载的梦现算。")),
|
|
914
1215
|
]);
|
|
915
1216
|
}
|
|
916
1217
|
|
|
@@ -926,11 +1227,11 @@ function CopyCommand(props) {
|
|
|
926
1227
|
setFailed(ok !== true);
|
|
927
1228
|
});
|
|
928
1229
|
};
|
|
929
|
-
const label = props.label === undefined ? "复制命令" : props.label;
|
|
1230
|
+
const label = props.label === undefined ? t("复制命令") : props.label;
|
|
930
1231
|
return h("span", { className: "dshd-copywrap" }, [
|
|
931
|
-
h(Btn, { key: "b", onClick: onCopy, disabled: props.disabled === true, title: props.title === undefined ? command : props.title }, copied === true ? "已复制" : label),
|
|
1232
|
+
h(Btn, { key: "b", onClick: onCopy, disabled: props.disabled === true, title: props.title === undefined ? command : props.title }, copied === true ? t("已复制") : label),
|
|
932
1233
|
failed === true ? h("code", { key: "cmd", className: "dshd-cmd", tabIndex: 0 }, command) : null,
|
|
933
|
-
failed === true ? h("span", { key: "hint", className: "dshd-note" }, "剪贴板不可用:请手动选中上面的命令复制。") : null,
|
|
1234
|
+
failed === true ? h("span", { key: "hint", className: "dshd-note" }, t("剪贴板不可用:请手动选中上面的命令复制。")) : null,
|
|
934
1235
|
]);
|
|
935
1236
|
}
|
|
936
1237
|
|
|
@@ -947,10 +1248,10 @@ function ReviewControls(props) {
|
|
|
947
1248
|
const [pending, setPending] = useState(false);
|
|
948
1249
|
const [error, setError] = useState("");
|
|
949
1250
|
const [preview, setPreview] = useState(null);
|
|
950
|
-
const commands = row.state === "rejected" ? [["reopen", "重新送审"]]
|
|
951
|
-
: row.state === "disputed" ? [["resolve-conflict", "处理冲突"], ["reject", "驳回这条"]]
|
|
952
|
-
: row.state === "usable" ? [["mark-stale", "标记待复核"], ["reject", "驳回"]]
|
|
953
|
-
: [["accept", "采纳"], ["reject", "驳回"], ["mark-stale", "标记待复核"]];
|
|
1251
|
+
const commands = row.state === "rejected" ? [["reopen", t("重新送审")]]
|
|
1252
|
+
: row.state === "disputed" ? [["resolve-conflict", t("处理冲突")], ["reject", t("驳回这条")]]
|
|
1253
|
+
: row.state === "usable" ? [["mark-stale", t("标记待复核")], ["reject", t("驳回")]]
|
|
1254
|
+
: [["accept", t("采纳")], ["reject", t("驳回")], ["mark-stale", t("标记待复核")]];
|
|
954
1255
|
const effectiveAction = commands.some(function (item) { return item[0] === action; }) ? action : commands[0][0];
|
|
955
1256
|
const command = reviewCommandOf(row, { action: effectiveAction, resolution: resolution, note: note, affectedIds: affected === "" ? [] : [affected] });
|
|
956
1257
|
const submit = async function (body, message, isPreview) {
|
|
@@ -959,65 +1260,65 @@ function ReviewControls(props) {
|
|
|
959
1260
|
try {
|
|
960
1261
|
const result = await postAction(body);
|
|
961
1262
|
if (isPreview) setPreview(result);
|
|
962
|
-
else { setPreview(null); if (props.onChanged) props.onChanged(result.unchanged === true ? "管理块内容已一致,无需写入。" : message); }
|
|
1263
|
+
else { setPreview(null); if (props.onChanged) props.onChanged(result.unchanged === true ? t("管理块内容已一致,无需写入。") : message); }
|
|
963
1264
|
} catch (failure) {
|
|
964
|
-
setError(failure.name === "TimeoutError" ? "请求超时,请先刷新确认结果后再重试。" :
|
|
1265
|
+
setError(failure.name === "TimeoutError" ? t("请求超时,请先刷新确认结果后再重试。") : displayError(failure));
|
|
965
1266
|
if (body.operation === "apply") setPreview(null);
|
|
966
1267
|
} finally { setPending(false); }
|
|
967
1268
|
};
|
|
968
1269
|
const review = function (chosen) {
|
|
969
|
-
return submit({ operation: "review", action: chosen, lessonId: row.id, expectedRevision: row.revision, resolution: resolution, note: note, affectedIds: affected === "" ? [] : [affected] }, "已更新「" + row.title + "」的审阅状态。");
|
|
1270
|
+
return submit({ operation: "review", action: chosen, lessonId: row.id, expectedRevision: row.revision, resolution: resolution, note: note, affectedIds: affected === "" ? [] : [affected] }, t("已更新「") + row.title + t("」的审阅状态。"));
|
|
970
1271
|
};
|
|
971
1272
|
const feedback = function (vote) {
|
|
972
1273
|
const signature = row.id + "@" + row.revision + ":" + vote + ":" + feedbackNote;
|
|
973
1274
|
const requestId = feedbackRetry && feedbackRetry.signature === signature ? feedbackRetry.id
|
|
974
1275
|
: typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : "feedback_" + Date.now() + "_" + Math.random().toString(36).slice(2);
|
|
975
1276
|
setFeedbackRetry({ signature: signature, id: requestId });
|
|
976
|
-
return submit({ operation: "feedback", lessonId: row.id, expectedRevision: row.revision, vote: vote, requestId: requestId, ...(feedbackNote.trim() === "" ? {} : { note: feedbackNote.trim() }) }, "已记录使用反馈;「" + row.title + "」的审阅状态保持原样。");
|
|
1277
|
+
return submit({ operation: "feedback", lessonId: row.id, expectedRevision: row.revision, vote: vote, requestId: requestId, ...(feedbackNote.trim() === "" ? {} : { note: feedbackNote.trim() }) }, t("已记录使用反馈;「") + row.title + t("」的审阅状态保持原样。"));
|
|
977
1278
|
};
|
|
978
|
-
return h("div", { className: "dshd-review", role: "group", "aria-label": row.title + "的操作", onClick: function (event) { event.stopPropagation(); } }, [
|
|
1279
|
+
return h("div", { className: "dshd-review", role: "group", "aria-label": row.title + t("的操作"), onClick: function (event) { event.stopPropagation(); } }, [
|
|
979
1280
|
h("div", { className: "dshd-kcmd", key: "buttons" }, [
|
|
980
1281
|
...commands.filter(function (item) { return item[0] !== "resolve-conflict"; }).map(function (item) {
|
|
981
1282
|
return h(Btn, { key: item[0], disabled: pending || row.revision < 1, onClick: function () { review(item[0]); } }, item[1]);
|
|
982
1283
|
}),
|
|
983
|
-
h(Btn, { key: "review", disabled: pending, onClick: function () { setOpen(!open); }, "aria-expanded": open }, open ? "收起审阅" : row.state === "disputed" ? "处理冲突" : "审阅详情"),
|
|
984
|
-
row.bridgeable === true && row.workspaceRoot !== "" ? h(Btn, { key: "preview", disabled: pending, onClick: function () { submit({ operation: "preview", lessonId: row.id, expectedRevision: row.revision }, "", true); } }, "预览规则变更") : null,
|
|
985
|
-
row.bridgeable === true && row.workspaceRoot === "" ? h(CopyCommand, { key: "preview-command", command: bridgePreviewCommandOf(row.id, row.projectId, row.workspaceRoot), label: "复制项目预览命令" }) : null,
|
|
1284
|
+
h(Btn, { key: "review", disabled: pending, onClick: function () { setOpen(!open); }, "aria-expanded": open }, open ? t("收起审阅") : row.state === "disputed" ? t("处理冲突") : t("审阅详情")),
|
|
1285
|
+
row.bridgeable === true && row.workspaceRoot !== "" ? h(Btn, { key: "preview", disabled: pending, onClick: function () { submit({ operation: "preview", lessonId: row.id, expectedRevision: row.revision }, "", true); } }, t("预览规则变更")) : null,
|
|
1286
|
+
row.bridgeable === true && row.workspaceRoot === "" ? h(CopyCommand, { key: "preview-command", command: bridgePreviewCommandOf(row.id, row.projectId, row.workspaceRoot), label: t("复制项目预览命令") }) : null,
|
|
986
1287
|
]),
|
|
987
|
-
pending ? h("div", { key: "pending", role: "status", className: "dshd-note" }, "正在处理…") : null,
|
|
988
|
-
error !== "" ? h("div", { key: "error", role: "alert", className: "dshd-hint" }, [error, h(Btn, { key: "refresh", disabled: pending, onClick: function () { if (props.onChanged) props.onChanged("已刷新经验列表。"); } }, "刷新列表")]) : null,
|
|
1288
|
+
pending ? h("div", { key: "pending", role: "status", className: "dshd-note" }, t("正在处理…")) : null,
|
|
1289
|
+
error !== "" ? h("div", { key: "error", role: "alert", className: "dshd-hint" }, [error, h(Btn, { key: "refresh", disabled: pending, onClick: function () { if (props.onChanged) props.onChanged(t("已刷新经验列表。")); } }, t("刷新列表"))]) : null,
|
|
989
1290
|
open ? h("div", { className: "dshd-reviewform", key: "form", onKeyDown: function (event) {
|
|
990
1291
|
if (event.key === "Escape" && !pending) { event.preventDefault(); event.stopPropagation(); setOpen(false); }
|
|
991
1292
|
} }, [
|
|
992
|
-
h("label", { key: "action" }, ["审阅动作", h("select", { "aria-label": "审阅动作", disabled: pending, value: effectiveAction, onChange: function (event) { setAction(event.target.value); } }, commands.map(function (item) { return h("option", { value: item[0], key: item[0] }, item[1]); }))]),
|
|
1293
|
+
h("label", { key: "action" }, [t("审阅动作"), h("select", { "aria-label": t("审阅动作"), disabled: pending, value: effectiveAction, onChange: function (event) { setAction(event.target.value); } }, commands.map(function (item) { return h("option", { value: item[0], key: item[0] }, item[1]); }))]),
|
|
993
1294
|
effectiveAction === "resolve-conflict" ? h("div", { key: "conflict" }, [
|
|
994
|
-
h("label", { key: "against" }, ["冲突经验", h("select", { "aria-label": "冲突经验", disabled: pending, value: affected, onChange: function (event) { setAffected(event.target.value); } }, row.conflicts.map(function (item) { return h("option", { key: item.id, value: item.id, title: item.title }, item.title); }))]),
|
|
995
|
-
h("label", { key: "resolution" }, ["处理方式", h("select", { "aria-label": "处理方式", disabled: pending, value: resolution, onChange: function (event) { setResolution(event.target.value); } }, [
|
|
996
|
-
h("option", { key: "prefer", value: "prefer" }, "采用这条,驳回对方"),
|
|
997
|
-
h("option", { key: "drop", value: "drop" }, "放弃这条,保留对方"),
|
|
998
|
-
h("option", { key: "merge", value: "merge" }, "合并到这条,对方留作历史"),
|
|
1295
|
+
h("label", { key: "against" }, [t("冲突经验"), h("select", { "aria-label": t("冲突经验"), disabled: pending, value: affected, onChange: function (event) { setAffected(event.target.value); } }, row.conflicts.map(function (item) { return h("option", { key: item.id, value: item.id, title: item.title }, item.title); }))]),
|
|
1296
|
+
h("label", { key: "resolution" }, [t("处理方式"), h("select", { "aria-label": t("处理方式"), disabled: pending, value: resolution, onChange: function (event) { setResolution(event.target.value); } }, [
|
|
1297
|
+
h("option", { key: "prefer", value: "prefer" }, t("采用这条,驳回对方")),
|
|
1298
|
+
h("option", { key: "drop", value: "drop" }, t("放弃这条,保留对方")),
|
|
1299
|
+
h("option", { key: "merge", value: "merge" }, t("合并到这条,对方留作历史")),
|
|
999
1300
|
])]),
|
|
1000
|
-
h("label", { key: "note" }, ["处理依据", h("textarea", { "aria-label": "处理依据", disabled: pending, rows: 2, maxLength: 500, value: note, placeholder: "说明哪个来源支持这次决定…", onChange: function (event) { setNote(event.target.value); } })]),
|
|
1301
|
+
h("label", { key: "note" }, [t("处理依据"), h("textarea", { "aria-label": t("处理依据"), disabled: pending, rows: 2, maxLength: 500, value: note, placeholder: t("说明哪个来源支持这次决定…"), onChange: function (event) { setNote(event.target.value); } })]),
|
|
1001
1302
|
]) : null,
|
|
1002
|
-
h(Btn, { key: "confirm", disabled: pending || command === "", onClick: function () { review(effectiveAction); }, variant: "primary" }, "确认审阅"),
|
|
1003
|
-
h("details", { key: "fallback" }, [h("summary", { key: "summary" }, "在对话里处理"), command === "" ? h("div", { key: "hint", className: "dshd-note" }, "选择冲突经验并填写处理依据后即可处理。") : h(CopyCommand, { key: "copy", command: command, label: "复制审阅命令" })]),
|
|
1303
|
+
h(Btn, { key: "confirm", disabled: pending || command === "", onClick: function () { review(effectiveAction); }, variant: "primary" }, t("确认审阅")),
|
|
1304
|
+
h("details", { key: "fallback" }, [h("summary", { key: "summary" }, t("在对话里处理")), command === "" ? h("div", { key: "hint", className: "dshd-note" }, t("选择冲突经验并填写处理依据后即可处理。")) : h(CopyCommand, { key: "copy", command: command, label: t("复制审阅命令") })]),
|
|
1004
1305
|
]) : null,
|
|
1005
|
-
preview ? h("section", { key: "preview", className: "dshd-reviewform", "aria-label": "规则变更预览" }, [
|
|
1006
|
-
h("strong", { key: "heading" }, "规则变更预览"),
|
|
1007
|
-
h("div", { key: "target", className: "dshd-note" }, "目标:" + preview.resolvedPath),
|
|
1008
|
-
h("pre", { key: "diff", className: "dshd-diff", tabIndex: 0 }, preview.diff || "管理块内容已经一致,无需更改。"),
|
|
1009
|
-
h("div", { key: "help", className: "dshd-note" }, "将按此预览重建 Dream 管理块,请核对新增和删除内容。块外内容保留,写入前创建回滚备份。"),
|
|
1010
|
-
h(Btn, { key: "apply", disabled: pending, variant: "primary", onClick: function () { submit({ operation: "apply", token: preview.token }, "规则已应用,可在「写入记录」中回滚。"); } }, "应用此变更"),
|
|
1011
|
-
h(Btn, { key: "cancel", disabled: pending, onClick: function () { setPreview(null); } }, "取消预览"),
|
|
1306
|
+
preview ? h("section", { key: "preview", className: "dshd-reviewform", "aria-label": t("规则变更预览") }, [
|
|
1307
|
+
h("strong", { key: "heading" }, t("规则变更预览")),
|
|
1308
|
+
h("div", { key: "target", className: "dshd-note" }, t("目标:") + preview.resolvedPath),
|
|
1309
|
+
h("pre", { key: "diff", className: "dshd-diff", tabIndex: 0 }, preview.diff || t("管理块内容已经一致,无需更改。")),
|
|
1310
|
+
h("div", { key: "help", className: "dshd-note" }, t("将按此预览重建 Dream 管理块,请核对新增和删除内容。块外内容保留,写入前创建回滚备份。")),
|
|
1311
|
+
h(Btn, { key: "apply", disabled: pending, variant: "primary", onClick: function () { submit({ operation: "apply", token: preview.token }, t("规则已应用,可在「写入记录」中回滚。")); } }, t("应用此变更")),
|
|
1312
|
+
h(Btn, { key: "cancel", disabled: pending, onClick: function () { setPreview(null); } }, t("取消预览")),
|
|
1012
1313
|
]) : null,
|
|
1013
1314
|
h("details", { key: "feedback", className: "dshd-feedback" }, [
|
|
1014
|
-
h("summary", { key: "summary" }, "使用反馈 · 有用 " + (row.feedback.useful || 0) + " · 不适用 " + (row.feedback.notApplicable || 0)),
|
|
1015
|
-
h("label", { key: "note" }, ["场景备注(可选)", h("textarea", { "aria-label": "反馈场景备注", rows: 2, disabled: pending, maxLength: 500, value: feedbackNote, placeholder: "这次在哪个场景使用…", onChange: function (event) { setFeedbackNote(event.target.value); } })]),
|
|
1016
|
-
h(Btn, { key: "useful", disabled: pending, onClick: function () { feedback("useful"); } }, "这次有用"),
|
|
1017
|
-
h(Btn, { key: "not", disabled: pending, onClick: function () { feedback("not-applicable"); } }, "这次不适用"),
|
|
1018
|
-
h("div", { key: "help", className: "dshd-note" }, "记录使用情况,供之后复核;采纳与驳回由审阅决定。"),
|
|
1315
|
+
h("summary", { key: "summary" }, t("使用反馈 · 有用 ") + (row.feedback.useful || 0) + t(" · 不适用 ") + (row.feedback.notApplicable || 0)),
|
|
1316
|
+
h("label", { key: "note" }, [t("场景备注(可选)"), h("textarea", { "aria-label": t("反馈场景备注"), rows: 2, disabled: pending, maxLength: 500, value: feedbackNote, placeholder: t("这次在哪个场景使用…"), onChange: function (event) { setFeedbackNote(event.target.value); } })]),
|
|
1317
|
+
h(Btn, { key: "useful", disabled: pending, onClick: function () { feedback("useful"); } }, t("这次有用")),
|
|
1318
|
+
h(Btn, { key: "not", disabled: pending, onClick: function () { feedback("not-applicable"); } }, t("这次不适用")),
|
|
1319
|
+
h("div", { key: "help", className: "dshd-note" }, t("记录使用情况,供之后复核;采纳与驳回由审阅决定。")),
|
|
1019
1320
|
...(Array.isArray(row.feedback.recent) ? row.feedback.recent.slice(-3).reverse().map(function (item, index) {
|
|
1020
|
-
return h("div", { key: "recent-" + index, className: "dshd-note" }, formatDate(item.at) + " · " + (item.vote === "useful" ? "有用" : "不适用") + (item.note ? " · " + item.note : ""));
|
|
1321
|
+
return h("div", { key: "recent-" + index, className: "dshd-note" }, formatDate(item.at) + " · " + (item.vote === "useful" ? t("有用") : t("不适用")) + (item.note ? " · " + item.note : ""));
|
|
1021
1322
|
}) : []),
|
|
1022
1323
|
]),
|
|
1023
1324
|
]);
|
|
@@ -1033,15 +1334,15 @@ function RollbackControl(props) {
|
|
|
1033
1334
|
try {
|
|
1034
1335
|
await postAction({ operation: "rollback", backupId: props.row.id });
|
|
1035
1336
|
setConfirm(false);
|
|
1036
|
-
if (props.onChanged) props.onChanged("已恢复上一次的 Dream 管理块,块外内容保留。");
|
|
1037
|
-
} catch (failure) { setError(
|
|
1337
|
+
if (props.onChanged) props.onChanged(t("已恢复上一次的 Dream 管理块,块外内容保留。"));
|
|
1338
|
+
} catch (failure) { setError(displayError(failure)); }
|
|
1038
1339
|
finally { setPending(false); }
|
|
1039
1340
|
};
|
|
1040
1341
|
return h("div", { className: "dshd-reviewform" }, [
|
|
1041
|
-
h(Btn, { key: "start", disabled: pending, onClick: function () { setConfirm(!confirm); } }, confirm ? "取消回滚" : "回滚这次变更"),
|
|
1342
|
+
h(Btn, { key: "start", disabled: pending, onClick: function () { setConfirm(!confirm); } }, confirm ? t("取消回滚") : t("回滚这次变更")),
|
|
1042
1343
|
confirm ? h("div", { key: "confirm" }, [
|
|
1043
|
-
h("p", { key: "target", className: "dshd-note" }, "将恢复「" + props.row.target + "」的前一个 Dream 管理块。块外内容保留;管理块已被修改时会拒绝回滚。"),
|
|
1044
|
-
h(Btn, { key: "apply", variant: "primary", disabled: pending, onClick: rollback }, pending ? "正在回滚…" : "确认回滚管理块"),
|
|
1344
|
+
h("p", { key: "target", className: "dshd-note" }, t("将恢复「") + props.row.target + t("」的前一个 Dream 管理块。块外内容保留;管理块已被修改时会拒绝回滚。")),
|
|
1345
|
+
h(Btn, { key: "apply", variant: "primary", disabled: pending, onClick: rollback }, pending ? t("正在回滚…") : t("确认回滚管理块")),
|
|
1045
1346
|
]) : null,
|
|
1046
1347
|
error ? h("div", { key: "error", role: "alert", className: "dshd-hint" }, error) : null,
|
|
1047
1348
|
]);
|
|
@@ -1055,36 +1356,36 @@ function KnowledgeRow(props) {
|
|
|
1055
1356
|
return h("div", {
|
|
1056
1357
|
className: "dshd-knowledge " + row.stateClass + (privacy === true && peek === true ? " dshd-peek" : ""),
|
|
1057
1358
|
onClick: reveal,
|
|
1058
|
-
title: privacy === true ? "隐私模式:点击此条临时查看" : undefined,
|
|
1359
|
+
title: privacy === true ? t("隐私模式:点击此条临时查看") : undefined,
|
|
1059
1360
|
}, [
|
|
1060
1361
|
h("div", { className: "dshd-khead", key: "h" }, [
|
|
1061
1362
|
h("span", { className: "dshd-kbadge", key: "b" }, row.stateLabel),
|
|
1062
1363
|
h("span", { className: "dshd-ktitle", key: "t", title: row.title }, row.title),
|
|
1063
1364
|
]),
|
|
1064
1365
|
h("div", { className: "dshd-kmeta", key: "m" }, [
|
|
1065
|
-
h("span", { className: "dshd-klast", key: "l", title: row.lastTitle }, "最近核验:" + row.lastText),
|
|
1366
|
+
h("span", { className: "dshd-klast", key: "l", title: row.lastTitle }, t("最近核验:") + row.lastText),
|
|
1066
1367
|
]),
|
|
1067
1368
|
h("div", { className: "dshd-kbody", key: "body" }, [
|
|
1068
|
-
h("div", { key: "when" }, "何时用:" + (row.when !== "" ? row.when : "未标注适用条件")),
|
|
1069
|
-
h("div", { key: "action", className: "dshd-action" }, "怎么做:" + (row.action || "未标注具体动作")),
|
|
1369
|
+
h("div", { key: "when" }, t("何时用:") + (row.when !== "" ? row.when : t("未标注适用条件"))),
|
|
1370
|
+
h("div", { key: "action", className: "dshd-action" }, t("怎么做:") + (row.action || t("未标注具体动作"))),
|
|
1070
1371
|
h("details", { key: "details", onClick: function (event) { event.stopPropagation(); } }, [
|
|
1071
|
-
h("summary", { key: "summary" }, "适用范围与证据"),
|
|
1072
|
-
h("div", { key: "scope" }, "范围:" + row.scopeLabel),
|
|
1073
|
-
row.applicability !== "" ? h("div", { key: "applicability" }, "适用:" + row.applicability) : null,
|
|
1074
|
-
row.exceptions.length > 0 ? h("div", { key: "exceptions" }, "例外:" + row.exceptions.join(";")) : null,
|
|
1075
|
-
h("div", { key: "evidence" }, "证据:" + row.evidenceSummary),
|
|
1076
|
-
row.evidence.length === 0 ? h("div", { key: "sources-empty", className: "dshd-note" }, "没有关联来源记录,采纳前请核对依据。") : h("div", { key: "sources", className: "dshd-sources" }, row.evidence.map(function (item) {
|
|
1372
|
+
h("summary", { key: "summary" }, t("适用范围与证据")),
|
|
1373
|
+
h("div", { key: "scope" }, t("范围:") + row.scopeLabel),
|
|
1374
|
+
row.applicability !== "" ? h("div", { key: "applicability" }, t("适用:") + row.applicability) : null,
|
|
1375
|
+
row.exceptions.length > 0 ? h("div", { key: "exceptions" }, t("例外:") + row.exceptions.join(";")) : null,
|
|
1376
|
+
h("div", { key: "evidence" }, t("证据:") + row.evidenceSummary),
|
|
1377
|
+
row.evidence.length === 0 ? h("div", { key: "sources-empty", className: "dshd-note" }, t("没有关联来源记录,采纳前请核对依据。")) : h("div", { key: "sources", className: "dshd-sources" }, row.evidence.map(function (item) {
|
|
1077
1378
|
return h("details", { key: item.id }, [
|
|
1078
|
-
h("summary", { key: "summary" }, "来源记录 · " + (item.verification === "read" ? "已读到来源" : "待核验") + " · " + (item.kind === "session" ? "会话" : item.kind === "user-correction" ? "用户纠正" : "本地资料")),
|
|
1079
|
-
h("div", { key: "summary-text" }, item.summary || "未提供摘要"),
|
|
1080
|
-
item.sessionId ? h("div", { key: "session", className: "dshd-note" }, "会话:" + item.sessionId + (Number.isInteger(item.recordSeq) ? " · 记录 " + item.recordSeq : "")) : null,
|
|
1081
|
-
h("div", { key: "date", className: "dshd-note" }, "记录时间:" + formatDate(item.observedAt)),
|
|
1082
|
-
item.verificationReason ? h("div", { key: "reason", className: "dshd-note" }, "核验说明:" + item.verificationReason) : null,
|
|
1083
|
-
item.sourceHash ? h("code", { key: "hash", className: "dshd-cmd", title: item.sourceHash }, "来源校验值:" + item.sourceHash) : null,
|
|
1379
|
+
h("summary", { key: "summary" }, t("来源记录 · ") + (item.verification === "read" ? t("已读到来源") : t("待核验")) + " · " + (item.kind === "session" ? t("会话") : item.kind === "user-correction" ? t("用户纠正") : t("本地资料"))),
|
|
1380
|
+
h("div", { key: "summary-text" }, item.summary || t("未提供摘要")),
|
|
1381
|
+
item.sessionId ? h("div", { key: "session", className: "dshd-note" }, t("会话:") + item.sessionId + (Number.isInteger(item.recordSeq) ? t(" · 记录 ") + item.recordSeq : "")) : null,
|
|
1382
|
+
h("div", { key: "date", className: "dshd-note" }, t("记录时间:") + formatDate(item.observedAt)),
|
|
1383
|
+
item.verificationReason ? h("div", { key: "reason", className: "dshd-note" }, t("核验说明:") + item.verificationReason) : null,
|
|
1384
|
+
item.sourceHash ? h("code", { key: "hash", className: "dshd-cmd", title: item.sourceHash }, t("来源校验值:") + item.sourceHash) : null,
|
|
1084
1385
|
]);
|
|
1085
1386
|
})),
|
|
1086
1387
|
]),
|
|
1087
|
-
props.showHoldReason === true && row.holdReasonLabel !== "" ? h("div", { key: "hold", className: "dshd-holdreason" }, "为什么被扣下:" + row.holdReasonLabel) : null,
|
|
1388
|
+
props.showHoldReason === true && row.holdReasonLabel !== "" ? h("div", { key: "hold", className: "dshd-holdreason" }, t("为什么被扣下:") + row.holdReasonLabel) : null,
|
|
1088
1389
|
]),
|
|
1089
1390
|
h(ReviewControls, { row: row, key: row.id + "@" + row.revision, onChanged: props.onChanged }),
|
|
1090
1391
|
]);
|
|
@@ -1099,22 +1400,22 @@ function KnowledgeCard(props) {
|
|
|
1099
1400
|
const noticeNode = notice === "" ? null : h("div", { className: "dshd-knotice", key: "notice", role: "status" }, notice);
|
|
1100
1401
|
const head = function (extra) {
|
|
1101
1402
|
return h("div", { className: "dshd-khead", key: "head" }, [
|
|
1102
|
-
h("h4", { key: "t" }, "已审阅经验"),
|
|
1403
|
+
h("h4", { key: "t" }, t("已审阅经验")),
|
|
1103
1404
|
h("span", { className: "dshd-spacer", key: "s" }),
|
|
1104
1405
|
extra,
|
|
1105
1406
|
]);
|
|
1106
1407
|
};
|
|
1107
1408
|
if (props.error !== null && props.error !== undefined) {
|
|
1108
1409
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1109
|
-
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, "重试")),
|
|
1110
|
-
h("div", { className: "dshd-hint", key: "e", role: "alert" }, "读取经验失败:" + (props.error
|
|
1111
|
-
h("div", { className: "dshd-note", key: "n" }, "旧日记不受影响;修好后点「重试」。"),
|
|
1410
|
+
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, t("重试"))),
|
|
1411
|
+
h("div", { className: "dshd-hint", key: "e", role: "alert" }, t("读取经验失败:") + displayError(props.error)),
|
|
1412
|
+
h("div", { className: "dshd-note", key: "n" }, t("旧日记不受影响;修好后点「重试」。")),
|
|
1112
1413
|
]);
|
|
1113
1414
|
}
|
|
1114
1415
|
if (props.empty === true) {
|
|
1115
1416
|
const empty = knowledgeEmptyState();
|
|
1116
1417
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1117
|
-
h("h4", { key: "t" }, "经验"),
|
|
1418
|
+
h("h4", { key: "t" }, t("经验")),
|
|
1118
1419
|
noticeNode,
|
|
1119
1420
|
h("div", { className: "dshd-kempty", key: "e" }, [
|
|
1120
1421
|
h("div", { className: "dshd-kempty-title", key: "t2" }, empty.title),
|
|
@@ -1125,21 +1426,21 @@ function KnowledgeCard(props) {
|
|
|
1125
1426
|
}
|
|
1126
1427
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1127
1428
|
head(props.loading === true
|
|
1128
|
-
? h("span", { className: "dshd-hint", key: "loading" }, "加载中…")
|
|
1429
|
+
? h("span", { className: "dshd-hint", key: "loading" }, t("加载中…"))
|
|
1129
1430
|
: h("span", { className: "dshd-hint", key: "count" }, knowledgeCountText(vm.stats))),
|
|
1130
1431
|
noticeNode,
|
|
1131
|
-
h("label", { className: "dshd-note", key: "filter" }, ["查看 ", h("select", { "aria-label": "经验状态", value: stateFilter, onChange: function (event) { setStateFilter(event.target.value); setPageSize(5); } }, [
|
|
1132
|
-
h("option", { key: "usable", value: "usable" }, "可用经验"), h("option", { key: "rejected", value: "rejected" }, "已驳回历史"),
|
|
1432
|
+
h("label", { className: "dshd-note", key: "filter" }, [t("查看 "), h("select", { "aria-label": t("经验状态"), value: stateFilter, onChange: function (event) { setStateFilter(event.target.value); setPageSize(5); } }, [
|
|
1433
|
+
h("option", { key: "usable", value: "usable" }, t("可用经验")), h("option", { key: "rejected", value: "rejected" }, t("已驳回历史")),
|
|
1133
1434
|
])]),
|
|
1134
|
-
reviewed.length === 0 ? h("div", { className: "dshd-hint", key: "none" }, stateFilter === "usable" ? "暂无可用经验;待核实的记录在下方。" : "没有已驳回记录。") : null,
|
|
1435
|
+
reviewed.length === 0 ? h("div", { className: "dshd-hint", key: "none" }, stateFilter === "usable" ? t("暂无可用经验;待核实的记录在下方。") : t("没有已驳回记录。")) : null,
|
|
1135
1436
|
h("div", { className: "dshd-krows", key: "rows" }, reviewed.slice(0, pageSize).map(function (row, index) {
|
|
1136
1437
|
return h(KnowledgeRow, { key: row.id !== "" ? row.id : "k" + String(index), row: row, onChanged: props.onChanged, privacy: props.privacy === true });
|
|
1137
1438
|
})),
|
|
1138
|
-
reviewed.length > pageSize ? h(Btn, { key: "next", onClick: function () { setPageSize(pageSize + 5); } }, "显示更多经验") : null,
|
|
1439
|
+
reviewed.length > pageSize ? h(Btn, { key: "next", onClick: function () { setPageSize(pageSize + 5); } }, t("显示更多经验")) : null,
|
|
1139
1440
|
vm.shown < vm.total
|
|
1140
|
-
? h("div", { className: "dshd-note", key: "more" }, "只显示最近 " + String(vm.shown) + " / " + String(vm.total) + " 条经验。")
|
|
1441
|
+
? h("div", { className: "dshd-note", key: "more" }, t("只显示最近 ") + String(vm.shown) + " / " + String(vm.total) + t(" 条经验。"))
|
|
1141
1442
|
: null,
|
|
1142
|
-
h("div", { className: "dshd-note", key: "note" }, "采纳记录仍需满足当前任务的平台、版本与项目条件;来源已读不代表结论永远适用。"),
|
|
1443
|
+
h("div", { className: "dshd-note", key: "note" }, t("采纳记录仍需满足当前任务的平台、版本与项目条件;来源已读不代表结论永远适用。")),
|
|
1143
1444
|
]);
|
|
1144
1445
|
}
|
|
1145
1446
|
|
|
@@ -1152,34 +1453,34 @@ function ReviewQueueCard(props) {
|
|
|
1152
1453
|
: [];
|
|
1153
1454
|
const head = function (extra) {
|
|
1154
1455
|
return h("div", { className: "dshd-khead", key: "head" }, [
|
|
1155
|
-
h("h4", { key: "t" }, "待核实"),
|
|
1456
|
+
h("h4", { key: "t" }, t("待核实")),
|
|
1156
1457
|
h("span", { className: "dshd-spacer", key: "s" }),
|
|
1157
1458
|
extra,
|
|
1158
1459
|
]);
|
|
1159
1460
|
};
|
|
1160
1461
|
if (props.error !== null && props.error !== undefined) {
|
|
1161
1462
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1162
|
-
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, "重试")),
|
|
1163
|
-
h("div", { className: "dshd-hint", key: "e", role: "alert" }, "读取经验失败:" + (props.error
|
|
1463
|
+
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, t("重试"))),
|
|
1464
|
+
h("div", { className: "dshd-hint", key: "e", role: "alert" }, t("读取经验失败:") + displayError(props.error)),
|
|
1164
1465
|
]);
|
|
1165
1466
|
}
|
|
1166
1467
|
if (props.loading === true && rows.length === 0) {
|
|
1167
1468
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1168
|
-
head(h("span", { className: "dshd-hint", key: "l" }, "加载中…")),
|
|
1469
|
+
head(h("span", { className: "dshd-hint", key: "l" }, t("加载中…"))),
|
|
1169
1470
|
]);
|
|
1170
1471
|
}
|
|
1171
1472
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1172
|
-
head(rows.length === 0 ? null : h("span", { className: "dshd-hint", key: "c" }, String(rows.length) + " 条待核实")),
|
|
1473
|
+
head(rows.length === 0 ? null : h("span", { className: "dshd-hint", key: "c" }, String(rows.length) + t(" 条待核实"))),
|
|
1173
1474
|
rows.length === 0
|
|
1174
1475
|
? h("div", { className: "dshd-kempty", key: "empty" }, [
|
|
1175
|
-
h("div", { className: "dshd-kempty-title", key: "t" }, "没有待核实的经验"),
|
|
1476
|
+
h("div", { className: "dshd-kempty-title", key: "t" }, t("没有待核实的经验")),
|
|
1176
1477
|
h("p", { className: "dshd-kempty-body", key: "b" }, reviewQueueEmptyState()),
|
|
1177
1478
|
])
|
|
1178
1479
|
: h("div", { className: "dshd-krows", key: "rows" }, rows.slice(0, pageSize).map(function (row, index) {
|
|
1179
1480
|
return h(KnowledgeRow, { key: row.id !== "" ? row.id : "held-" + String(index), row: row, onChanged: props.onChanged, privacy: props.privacy === true, showHoldReason: true });
|
|
1180
1481
|
})),
|
|
1181
|
-
rows.length > pageSize ? h(Btn, { key: "more", onClick: function () { setPageSize(pageSize + 5); } }, "显示更多待核实经验") : null,
|
|
1182
|
-
h("div", { className: "dshd-note", key: "note" }, "这些经验默认不会作为任务建议注入。先核对来源与适用范围,再决定采纳、驳回或继续复核。"),
|
|
1482
|
+
rows.length > pageSize ? h(Btn, { key: "more", onClick: function () { setPageSize(pageSize + 5); } }, t("显示更多待核实经验")) : null,
|
|
1483
|
+
h("div", { className: "dshd-note", key: "note" }, t("这些经验默认不会作为任务建议注入。先核对来源与适用范围,再决定采纳、驳回或继续复核。")),
|
|
1183
1484
|
]);
|
|
1184
1485
|
}
|
|
1185
1486
|
|
|
@@ -1188,26 +1489,26 @@ function BridgeLogCard(props) {
|
|
|
1188
1489
|
const vm = props.vm;
|
|
1189
1490
|
const head = function (extra) {
|
|
1190
1491
|
return h("div", { className: "dshd-khead", key: "head" }, [
|
|
1191
|
-
h("h4", { key: "t" }, "写入记录"),
|
|
1492
|
+
h("h4", { key: "t" }, t("写入记录")),
|
|
1192
1493
|
h("span", { className: "dshd-spacer", key: "s" }),
|
|
1193
1494
|
extra,
|
|
1194
1495
|
]);
|
|
1195
1496
|
};
|
|
1196
1497
|
if (props.error !== null && props.error !== undefined) {
|
|
1197
1498
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1198
|
-
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, "重试")),
|
|
1199
|
-
h("div", { className: "dshd-hint", key: "e", role: "alert" }, "读取写入记录失败:" + (props.error
|
|
1499
|
+
head(h(Btn, { key: "retry", onClick: props.onRetry, disabled: props.loading === true }, t("重试"))),
|
|
1500
|
+
h("div", { className: "dshd-hint", key: "e", role: "alert" }, t("读取写入记录失败:") + displayError(props.error)),
|
|
1200
1501
|
]);
|
|
1201
1502
|
}
|
|
1202
1503
|
if (props.loading === true) {
|
|
1203
1504
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1204
|
-
head(h("span", { className: "dshd-hint", key: "l" }, "加载中…")),
|
|
1505
|
+
head(h("span", { className: "dshd-hint", key: "l" }, t("加载中…"))),
|
|
1205
1506
|
]);
|
|
1206
1507
|
}
|
|
1207
1508
|
if (vm.rows.length === 0) {
|
|
1208
1509
|
const empty = bridgeEmptyState();
|
|
1209
1510
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1210
|
-
h("h4", { key: "t" }, "写入记录"),
|
|
1511
|
+
h("h4", { key: "t" }, t("写入记录")),
|
|
1211
1512
|
h("div", { className: "dshd-kempty", key: "empty" }, [
|
|
1212
1513
|
h("div", { className: "dshd-kempty-title", key: "t2" }, empty.title),
|
|
1213
1514
|
h("p", { className: "dshd-kempty-body", key: "b" }, empty.body),
|
|
@@ -1216,7 +1517,7 @@ function BridgeLogCard(props) {
|
|
|
1216
1517
|
]);
|
|
1217
1518
|
}
|
|
1218
1519
|
return h("section", { className: "dshd-card dshd-kcard" }, [
|
|
1219
|
-
head(h("span", { className: "dshd-hint", key: "c" }, String(vm.total) + " 次写入 · " + String(vm.rollbackable) + " 次可回滚")),
|
|
1520
|
+
head(h("span", { className: "dshd-hint", key: "c" }, String(vm.total) + t(" 次写入 · ") + String(vm.rollbackable) + t(" 次可回滚"))),
|
|
1220
1521
|
h("div", { className: "dshd-krows", key: "rows" }, vm.rows.map(function (row, index) {
|
|
1221
1522
|
return h("div", { className: "dshd-bridge", key: row.id !== "" ? row.id : "bridge-" + String(index) }, [
|
|
1222
1523
|
h("div", { className: "dshd-bridge-head", key: "h" }, [
|
|
@@ -1226,21 +1527,21 @@ function BridgeLogCard(props) {
|
|
|
1226
1527
|
h("span", { className: "dshd-klast", key: "at", title: row.atTitle }, row.atText),
|
|
1227
1528
|
]),
|
|
1228
1529
|
h("div", { className: "dshd-bridge-meta", key: "m" }, [
|
|
1229
|
-
h("span", { key: "lessons", title: row.lessonsText }, "经验:" + row.lessonsText),
|
|
1230
|
-
row.beforeShort === "" ? null : h("span", { key: "before" }, "前哈希:" + row.beforeShort),
|
|
1231
|
-
row.afterShort === "" ? null : h("span", { key: "after" }, "后哈希:" + row.afterShort),
|
|
1530
|
+
h("span", { key: "lessons", title: row.lessonsText }, t("经验:") + row.lessonsText),
|
|
1531
|
+
row.beforeShort === "" ? null : h("span", { key: "before" }, t("前哈希:") + row.beforeShort),
|
|
1532
|
+
row.afterShort === "" ? null : h("span", { key: "after" }, t("后哈希:") + row.afterShort),
|
|
1232
1533
|
h("span", { key: "rb", className: row.rollbackable === true ? "dshd-rollback-ok" : "dshd-rollback-no" }, row.rollbackText),
|
|
1233
1534
|
]),
|
|
1234
1535
|
h("details", { key: "backup" }, [
|
|
1235
|
-
h("summary", { key: "summary" }, "备份标识与回滚命令"),
|
|
1536
|
+
h("summary", { key: "summary" }, t("备份标识与回滚命令")),
|
|
1236
1537
|
h("code", { key: "id", className: "dshd-cmd" }, row.id),
|
|
1237
1538
|
row.rollbackable === true ? h("code", { key: "command", className: "dshd-cmd", tabIndex: 0 }, bridgeRollbackCommandOf(row)) : null,
|
|
1238
1539
|
]),
|
|
1239
1540
|
row.rollbackable === true ? h(RollbackControl, { key: "rollback", row: row, onChanged: props.onChanged }) : null,
|
|
1240
1541
|
]);
|
|
1241
1542
|
})),
|
|
1242
|
-
vm.shown < vm.total ? h("div", { className: "dshd-note", key: "more" }, "只显示最近 " + String(vm.shown) + " / " + String(vm.total) + " 条记录。") : null,
|
|
1243
|
-
h("div", { className: "dshd-note", key: "note" }, "回滚恢复对应备份的 Dream 管理块;块外内容保留,外部修改的管理块会停止回滚。"),
|
|
1543
|
+
vm.shown < vm.total ? h("div", { className: "dshd-note", key: "more" }, t("只显示最近 ") + String(vm.shown) + " / " + String(vm.total) + t(" 条记录。")) : null,
|
|
1544
|
+
h("div", { className: "dshd-note", key: "note" }, t("回滚恢复对应备份的 Dream 管理块;块外内容保留,外部修改的管理块会停止回滚。")),
|
|
1244
1545
|
]);
|
|
1245
1546
|
}
|
|
1246
1547
|
|
|
@@ -1255,12 +1556,12 @@ function DreamCard(props) {
|
|
|
1255
1556
|
return h("article", {
|
|
1256
1557
|
className: "dshd-dream dshd-mood-" + String(moodSlot(dream.mood)) + (privacy && peek === true ? " dshd-peek" : ""),
|
|
1257
1558
|
onClick: clickCard,
|
|
1258
|
-
title: privacy ? "隐私模式:点击卡片临时查看" : undefined,
|
|
1559
|
+
title: privacy ? t("隐私模式:点击卡片临时查看") : undefined,
|
|
1259
1560
|
}, [
|
|
1260
1561
|
h("header", { className: "dshd-dream-head", key: "h" }, [
|
|
1261
1562
|
h("time", { className: "dshd-time", key: "t", dateTime: dream.at === "" ? undefined : dream.at, title: dream.at === "" ? timeText : timeText + " · " + dream.at }, timeText),
|
|
1262
1563
|
dreamAgo(dream, props.now) === "" ? null : h("span", { className: "dshd-ago", key: "a" }, dreamAgo(dream, props.now)),
|
|
1263
|
-
h("span", { className: "dshd-mood", key: "m", title: "心境:" + dream.mood }, [
|
|
1564
|
+
h("span", { className: "dshd-mood", key: "m", title: t("心境:") + dream.mood }, [
|
|
1264
1565
|
h("i", { className: "dshd-mooddot " + toneClass(moodSlot(dream.mood)), key: "d" }),
|
|
1265
1566
|
dream.mood,
|
|
1266
1567
|
]),
|
|
@@ -1270,7 +1571,7 @@ function DreamCard(props) {
|
|
|
1270
1571
|
className: "dshd-linkbtn",
|
|
1271
1572
|
key: "btn",
|
|
1272
1573
|
onClick: function (event) { if (event !== undefined && event.stopPropagation !== undefined) event.stopPropagation(); setExpanded(expanded !== true); },
|
|
1273
|
-
}, expanded === true ? "收起" : "展开全文") : null,
|
|
1574
|
+
}, expanded === true ? t("收起") : t("展开全文")) : null,
|
|
1274
1575
|
]),
|
|
1275
1576
|
h("div", { className: "dshd-reflection" + (collapsible && expanded !== true ? " dshd-clamp" : ""), key: "r" }, inlineNodes(dream.reflection, "reflection-" + String(props.index))),
|
|
1276
1577
|
dream.lessons.length === 0 ? null : h("ol", { className: "dshd-lessons", key: "l" }, dream.lessons.map((lesson, index) => h("li", { key: "li-" + String(index) + "-" + lesson }, [
|
|
@@ -1287,17 +1588,17 @@ function EmptyState(props) {
|
|
|
1287
1588
|
if (props.query !== undefined && props.query !== "") {
|
|
1288
1589
|
return h("div", { className: "dshd-empty" }, [
|
|
1289
1590
|
moon,
|
|
1290
|
-
h("h3", { key: "t" }, "没有找到「" + props.query + "」相关的梦"),
|
|
1291
|
-
h("p", { key: "p" }, "换个关键词试试,或者清空搜索看全部梦境。"),
|
|
1292
|
-
props.onClear ? h("div", { key: "a" }, h(Btn, { onClick: props.onClear }, "清空搜索")) : null,
|
|
1591
|
+
h("h3", { key: "t" }, t("没有找到「") + props.query + t("」相关的梦")),
|
|
1592
|
+
h("p", { key: "p" }, t("换个关键词试试,或者清空搜索看全部梦境。")),
|
|
1593
|
+
props.onClear ? h("div", { key: "a" }, h(Btn, { onClick: props.onClear }, t("清空搜索"))) : null,
|
|
1293
1594
|
]);
|
|
1294
1595
|
}
|
|
1295
1596
|
return h("div", { className: "dshd-empty" }, [
|
|
1296
1597
|
moon,
|
|
1297
|
-
h("h3", { key: "t" }, "还没有梦境日记"),
|
|
1298
|
-
h("p", { key: "p" }, "对 agent 说「做个梦」,它会回放最近的会话、反思之后把第一场梦写进这里。"),
|
|
1598
|
+
h("h3", { key: "t" }, t("还没有梦境日记")),
|
|
1599
|
+
h("p", { key: "p" }, t("对 agent 说「做个梦」,它会回放最近的会话、反思之后把第一场梦写进这里。")),
|
|
1299
1600
|
h("div", { className: "dshd-chips", key: "c" }, [
|
|
1300
|
-
h("span", { className: "dshd-chip", key: "s1" }, "对 agent 说「做个梦」"),
|
|
1601
|
+
h("span", { className: "dshd-chip", key: "s1" }, t("对 agent 说「做个梦」")),
|
|
1301
1602
|
h("span", { className: "dshd-chip", key: "s2" }, "dream_digest → dream_save"),
|
|
1302
1603
|
]),
|
|
1303
1604
|
]);
|
|
@@ -1363,7 +1664,7 @@ function DreamPanel() {
|
|
|
1363
1664
|
setKnowledgeError(null);
|
|
1364
1665
|
fetchKnowledge().then(function (payload) {
|
|
1365
1666
|
if (alive !== true) return;
|
|
1366
|
-
setKnowledge(
|
|
1667
|
+
setKnowledge(payload);
|
|
1367
1668
|
setKnowledgeLoading(false);
|
|
1368
1669
|
}).catch(function (failure) {
|
|
1369
1670
|
if (alive !== true) return;
|
|
@@ -1381,7 +1682,7 @@ function DreamPanel() {
|
|
|
1381
1682
|
setBridgeError(null);
|
|
1382
1683
|
fetchBridgeRecords().then(function (payload) {
|
|
1383
1684
|
if (alive !== true) return;
|
|
1384
|
-
setBridge(
|
|
1685
|
+
setBridge(payload);
|
|
1385
1686
|
setBridgeLoading(false);
|
|
1386
1687
|
}).catch(function (failure) {
|
|
1387
1688
|
if (alive !== true) return;
|
|
@@ -1393,8 +1694,8 @@ function DreamPanel() {
|
|
|
1393
1694
|
}, [reloadToken]);
|
|
1394
1695
|
|
|
1395
1696
|
const vm = viewModelOf(dreams, stats, now);
|
|
1396
|
-
const knowledgeVm = knowledge === null ? { rows: [], total: 0, shown: 0, stats: null } : knowledge;
|
|
1397
|
-
const bridgeVm = bridge === null ? { rows: [], total: 0, shown: 0, rollbackable: 0 } : bridge;
|
|
1697
|
+
const knowledgeVm = knowledge === null ? { rows: [], total: 0, shown: 0, stats: null } : normalizeKnowledge(knowledge, now);
|
|
1698
|
+
const bridgeVm = bridge === null ? { rows: [], total: 0, shown: 0, rollbackable: 0 } : normalizeBridgeRecords(bridge, now);
|
|
1398
1699
|
const pristine = vm.count === 0 && query === "";
|
|
1399
1700
|
const filterCount = filterCountOf(filters);
|
|
1400
1701
|
const visibleDreams = applyFilters(dreams, filters);
|
|
@@ -1486,8 +1787,8 @@ function DreamPanel() {
|
|
|
1486
1787
|
className: "dshd-input",
|
|
1487
1788
|
type: "text",
|
|
1488
1789
|
value: queryInput,
|
|
1489
|
-
placeholder: "搜索反思与教训…",
|
|
1490
|
-
"aria-label": "搜索梦境",
|
|
1790
|
+
placeholder: t("搜索反思与教训…"),
|
|
1791
|
+
"aria-label": t("搜索梦境"),
|
|
1491
1792
|
onChange: function (event) { setQueryInput(event.target.value); },
|
|
1492
1793
|
onKeyDown: function (event) { if (event.key === "Enter") setQuery(event.target.value.trim()); },
|
|
1493
1794
|
}),
|
|
@@ -1495,13 +1796,13 @@ function DreamPanel() {
|
|
|
1495
1796
|
key: "clear-search",
|
|
1496
1797
|
type: "button",
|
|
1497
1798
|
className: "dshd-clear",
|
|
1498
|
-
"aria-label": "清空搜索",
|
|
1799
|
+
"aria-label": t("清空搜索"),
|
|
1499
1800
|
onClick: clearSearch,
|
|
1500
1801
|
}, "×"),
|
|
1501
1802
|
]),
|
|
1502
1803
|
h("span", { className: "dshd-spacer", key: "spacer" }),
|
|
1503
1804
|
pristine || activeTab !== "journal" ? null : h("span", { className: "dshd-hint", key: "count" }, loading === true
|
|
1504
|
-
? "加载中…"
|
|
1805
|
+
? t("加载中…")
|
|
1505
1806
|
: countTextOf(query, vm.total, vm.count, filterCount, visibleDreams.length)),
|
|
1506
1807
|
showFilterButton === true && activeTab === "journal" ? h("div", { className: "dshd-filter", key: "filter" }, [
|
|
1507
1808
|
h(Btn, {
|
|
@@ -1509,10 +1810,10 @@ function DreamPanel() {
|
|
|
1509
1810
|
on: filterCount > 0,
|
|
1510
1811
|
pressed: filterCount > 0,
|
|
1511
1812
|
onClick: function () { setFilterOpen(filterOpen !== true); },
|
|
1512
|
-
title: "筛选作用于已加载窗口,不影响全量统计",
|
|
1513
|
-
}, "筛选" + (filterCount > 0 ? " · " + String(filterCount) : "") + " ▾"),
|
|
1514
|
-
filterOpen === true ? h("div", { className: "dshd-filterpop", key: "pop", role: "group", "aria-label": "筛选梦境" }, [
|
|
1515
|
-
h("div", { className: "dshd-filtertitle", key: "title" }, "筛选当前已加载的 " + String(vm.count) + " 场,不影响全量统计"),
|
|
1813
|
+
title: t("筛选作用于已加载窗口,不影响全量统计"),
|
|
1814
|
+
}, t("筛选") + (filterCount > 0 ? " · " + String(filterCount) : "") + " ▾"),
|
|
1815
|
+
filterOpen === true ? h("div", { className: "dshd-filterpop", key: "pop", role: "group", "aria-label": t("筛选梦境") }, [
|
|
1816
|
+
h("div", { className: "dshd-filtertitle", key: "title" }, t("筛选当前已加载的 ") + String(vm.count) + t(" 场,不影响全量统计")),
|
|
1516
1817
|
h("div", { key: "moods" }, vm.moodRows.map(function (row) {
|
|
1517
1818
|
const on = filters.moods.indexOf(row.mood) !== -1;
|
|
1518
1819
|
return h("button", {
|
|
@@ -1528,10 +1829,10 @@ function DreamPanel() {
|
|
|
1528
1829
|
className: "dshd-chipbtn" + (filters.withLessons === true ? " on" : ""),
|
|
1529
1830
|
"aria-pressed": filters.withLessons === true ? "true" : "false",
|
|
1530
1831
|
onClick: toggleOnlyLessons,
|
|
1531
|
-
}, "只看有教训的梦")),
|
|
1832
|
+
}, t("只看有教训的梦"))),
|
|
1532
1833
|
h("div", { className: "dshd-filterfoot", key: "foot" }, [
|
|
1533
|
-
h("button", { key: "clearbtn", type: "button", className: "dshd-linkbtn", onClick: clearFilters, disabled: filterCount === 0 }, "清空筛选"),
|
|
1534
|
-
h(Btn, { key: "done", onClick: function () { setFilterOpen(false); } }, "完成"),
|
|
1834
|
+
h("button", { key: "clearbtn", type: "button", className: "dshd-linkbtn", onClick: clearFilters, disabled: filterCount === 0 }, t("清空筛选")),
|
|
1835
|
+
h(Btn, { key: "done", onClick: function () { setFilterOpen(false); } }, t("完成")),
|
|
1535
1836
|
]),
|
|
1536
1837
|
]) : null,
|
|
1537
1838
|
]) : null,
|
|
@@ -1540,15 +1841,15 @@ function DreamPanel() {
|
|
|
1540
1841
|
onClick: togglePrivacy,
|
|
1541
1842
|
on: privacy === true,
|
|
1542
1843
|
pressed: privacy === true,
|
|
1543
|
-
title: "显示层模糊;DOM 仍为明文,不改文件、不加密",
|
|
1544
|
-
}, privacy === true ? "隐私模式:开" : "隐私模式"),
|
|
1545
|
-
h(Btn, { key: "refresh", onClick: reload, disabled: loading === true, title: "重新读取梦境日记" }, "刷新"),
|
|
1844
|
+
title: t("显示层模糊;DOM 仍为明文,不改文件、不加密"),
|
|
1845
|
+
}, privacy === true ? t("隐私模式:开") : t("隐私模式")),
|
|
1846
|
+
h(Btn, { key: "refresh", onClick: reload, disabled: loading === true, title: t("重新读取梦境日记") }, t("刷新")),
|
|
1546
1847
|
]);
|
|
1547
1848
|
|
|
1548
1849
|
const errorBox = error === null ? null : h("div", { className: "dshd-alert error", role: "alert", key: "error" }, [
|
|
1549
|
-
h("span", { key: "m" }, "读取梦境日记失败:" + (error
|
|
1850
|
+
h("span", { key: "m" }, t("读取梦境日记失败:") + displayError(error)),
|
|
1550
1851
|
h("span", { className: "dshd-spacer", key: "s" }),
|
|
1551
|
-
h(Btn, { key: "retry", onClick: reload, variant: "primary" }, "重试"),
|
|
1852
|
+
h(Btn, { key: "retry", onClick: reload, variant: "primary" }, t("重试")),
|
|
1552
1853
|
]);
|
|
1553
1854
|
|
|
1554
1855
|
const body = function () {
|
|
@@ -1558,8 +1859,8 @@ function DreamPanel() {
|
|
|
1558
1859
|
if (vm.count === 0) {
|
|
1559
1860
|
if (error !== null) return h("div", { key: "failed-wrap" }, [
|
|
1560
1861
|
h("div", { className: "dshd-empty", key: "failed" }, [
|
|
1561
|
-
h("h3", { key: "t" }, "暂时读不到梦境日记"),
|
|
1562
|
-
h("p", { key: "p" }, "等宿主路由就绪后点上面的「重试」;如果一直失败,看一眼 DSH 日志里有没有 [dsh-dream] 告警。"),
|
|
1862
|
+
h("h3", { key: "t" }, t("暂时读不到梦境日记")),
|
|
1863
|
+
h("p", { key: "p" }, t("等宿主路由就绪后点上面的「重试」;如果一直失败,看一眼 DSH 日志里有没有 [dsh-dream] 告警。")),
|
|
1563
1864
|
]),
|
|
1564
1865
|
]);
|
|
1565
1866
|
return h(EmptyState, { key: "empty", query: query, onClear: query === "" ? null : clearSearch });
|
|
@@ -1567,10 +1868,10 @@ function DreamPanel() {
|
|
|
1567
1868
|
return h("div", { key: "content" }, [
|
|
1568
1869
|
vm.showTiles
|
|
1569
1870
|
? h("div", { className: "dshd-tiles", key: "tiles" }, [
|
|
1570
|
-
h(StatTile, { key: "total", label: "梦境总数", value: String(vm.total) + " 场", hint: vm.total > vm.count ? "本页显示最近 " + String(vm.count) + " 场" : "全部梦境日记" }),
|
|
1571
|
-
h(StatTile, { key: "span", label: "时间跨度", value: vm.spanText, hint: vm.spanHint, title: vm.spanFull }),
|
|
1572
|
-
h(StatTile, { key: "latest", label: "最近一场", value: vm.latestText, hint: vm.latestDate }),
|
|
1573
|
-
h(StatTile, { key: "mood", label: "心境", value: String(vm.moodCount) + " 种", hint: vm.moodCount === 0 ? "" : "最常见:" + vm.moodRows[0].mood }),
|
|
1871
|
+
h(StatTile, { key: "total", label: t("梦境总数"), value: String(vm.total) + t(" 场"), hint: vm.total > vm.count ? t("本页显示最近 ") + String(vm.count) + t(" 场") : t("全部梦境日记") }),
|
|
1872
|
+
h(StatTile, { key: "span", label: t("时间跨度"), value: vm.spanText, hint: vm.spanHint, title: vm.spanFull }),
|
|
1873
|
+
h(StatTile, { key: "latest", label: t("最近一场"), value: vm.latestText, hint: vm.latestDate }),
|
|
1874
|
+
h(StatTile, { key: "mood", label: t("心境"), value: String(vm.moodCount) + t(" 种"), hint: vm.moodCount === 0 ? "" : t("最常见:") + vm.moodRows[0].mood }),
|
|
1574
1875
|
])
|
|
1575
1876
|
: h("div", { className: "dshd-headline", key: "headline" }, vm.headline),
|
|
1576
1877
|
h("div", { className: "dshd-two", key: "two" }, [
|
|
@@ -1578,14 +1879,14 @@ function DreamPanel() {
|
|
|
1578
1879
|
h(LessonCard, { key: "lessons", vm: vm }),
|
|
1579
1880
|
]),
|
|
1580
1881
|
h("div", { className: "dshd-listhead", key: "listhead" }, [
|
|
1581
|
-
h("h4", { key: "t" }, query === "" && filterCount === 0 ? "梦境时间线" : (query === "" ? "筛选结果" : "搜索结果")),
|
|
1882
|
+
h("h4", { key: "t" }, query === "" && filterCount === 0 ? t("梦境时间线") : (query === "" ? t("筛选结果") : t("搜索结果"))),
|
|
1582
1883
|
h("span", { className: "dshd-hint", key: "n" }, listHintOf(query, vm.count, filterCount, visibleDreams.length)),
|
|
1583
1884
|
]),
|
|
1584
1885
|
visibleDreams.length === 0 && filterCount > 0
|
|
1585
1886
|
? h("div", { className: "dshd-filterempty", key: "filtered" }, [
|
|
1586
|
-
h("span", { key: "t" }, "没有符合筛选的梦(筛选作用于已加载的 " + String(vm.count) + " 场)。"),
|
|
1887
|
+
h("span", { key: "t" }, t("没有符合筛选的梦(筛选作用于已加载的 ") + String(vm.count) + t(" 场)。")),
|
|
1587
1888
|
h("span", { className: "dshd-spacer", key: "s" }),
|
|
1588
|
-
h(Btn, { key: "clearbtn", onClick: clearFilters }, "清空筛选"),
|
|
1889
|
+
h(Btn, { key: "clearbtn", onClick: clearFilters }, t("清空筛选")),
|
|
1589
1890
|
])
|
|
1590
1891
|
: h("div", { className: "dshd-timeline", key: "timeline" }, visibleDreams.map(function (dream, index) {
|
|
1591
1892
|
return h(DreamCard, { key: (dream.id !== "" ? dream.id : "dream") + "#" + String(index), dream: dream, now: now, privacy: privacy, index: index });
|
|
@@ -1596,26 +1897,27 @@ function DreamPanel() {
|
|
|
1596
1897
|
return h("div", { className: "dshd-root" + (privacy === true ? " dshd-privacy" : ""), key: "root" }, [
|
|
1597
1898
|
h("div", { className: "dshd-shell", key: "shell" }, [
|
|
1598
1899
|
errorBox,
|
|
1599
|
-
h("nav", { className: "dshd-tabs", key: "tabs", "aria-label": "梦境面板视图" }, [["journal", "日记"], ["knowledge", "经验"], ["bridge", "写入记录"]].map(function (item) {
|
|
1900
|
+
h("nav", { className: "dshd-tabs", key: "tabs", "aria-label": t("梦境面板视图") }, [["journal", t("日记")], ["knowledge", t("经验")], ["bridge", t("写入记录")]].map(function (item) {
|
|
1600
1901
|
return h("button", { key: item[0], type: "button", className: "dshd-chipbtn" + (activeTab === item[0] ? " on" : ""), "aria-pressed": activeTab === item[0], onClick: function () { setFilterOpen(false); setActiveTab(item[0]); } }, item[1]);
|
|
1601
1902
|
})),
|
|
1602
1903
|
toolbar,
|
|
1603
|
-
privacy === true ? h("div", { className: "dshd-note", key: "privacynote", role: "status" }, "隐私模式:显示层模糊;DOM 仍为明文,不改文件、不加密。点击卡片可临时查看。") : null,
|
|
1904
|
+
privacy === true ? h("div", { className: "dshd-note", key: "privacynote", role: "status" }, t("隐私模式:显示层模糊;DOM 仍为明文,不改文件、不加密。点击卡片可临时查看。")) : null,
|
|
1604
1905
|
body(),
|
|
1605
1906
|
activeTab !== "journal" ? null : h("div", { className: "dshd-footer", key: "footer" }, [
|
|
1606
1907
|
h("span", { key: "l" }, footerTextOf(query, limit, vm.total, vm.count)),
|
|
1607
1908
|
h("span", { className: "dshd-spacer", key: "sp" }),
|
|
1608
|
-
canLoadMore === true ? h(Btn, { key: "more", onClick: showMore, disabled: loading === true }, loading === true ? "加载中…" : "显示更早的梦") : null,
|
|
1609
|
-
h("span", { key: "r" }, "数据来自梦境日记(dreams.jsonl)"),
|
|
1909
|
+
canLoadMore === true ? h(Btn, { key: "more", onClick: showMore, disabled: loading === true }, loading === true ? t("加载中…") : t("显示更早的梦")) : null,
|
|
1910
|
+
h("span", { key: "r" }, t("数据来自梦境日记(dreams.jsonl)")),
|
|
1610
1911
|
]),
|
|
1611
1912
|
]),
|
|
1612
1913
|
]);
|
|
1613
1914
|
}
|
|
1614
1915
|
|
|
1615
|
-
function DreamSection() {
|
|
1916
|
+
function DreamSection({ locale } = {}) {
|
|
1917
|
+
useLanguage(locale);
|
|
1616
1918
|
return h("div", null, [
|
|
1617
|
-
h("h2", { key: "t" }, "梦境日记"),
|
|
1618
|
-
h("p", { className: "dshd-sub", key: "p" }, "会话回放 → 反思 → 记忆巩固。这里按时间倒序陈列 agent 做过的梦,附心境方块与教训榜。"),
|
|
1919
|
+
h("h2", { key: "t" }, t("梦境日记")),
|
|
1920
|
+
h("p", { className: "dshd-sub", key: "p" }, t("会话回放 → 反思 → 记忆巩固。这里按时间倒序陈列 agent 做过的梦,附心境方块与教训榜。")),
|
|
1619
1921
|
h(DreamPanel, { key: "panel" }),
|
|
1620
1922
|
]);
|
|
1621
1923
|
}
|
|
@@ -1761,9 +2063,10 @@ const CSS = [
|
|
|
1761
2063
|
|
|
1762
2064
|
// ───────────────────────── 挂载 ─────────────────────────
|
|
1763
2065
|
|
|
1764
|
-
const inject = ["slots"];
|
|
2066
|
+
const inject = ["slots", "locale"];
|
|
1765
2067
|
|
|
1766
2068
|
function apply(ctx) {
|
|
2069
|
+
try { localeService = ctx.get?.("locale") || ctx.locale; } catch { localeService = ctx.locale; }
|
|
1767
2070
|
try {
|
|
1768
2071
|
ctx.effect(function () {
|
|
1769
2072
|
const id = "dsh-dream/client";
|
|
@@ -1783,8 +2086,8 @@ function apply(ctx) {
|
|
|
1783
2086
|
name: "settings.section",
|
|
1784
2087
|
id: "dsh-dream",
|
|
1785
2088
|
order: 55,
|
|
1786
|
-
label: function () { return "梦境日记"; },
|
|
1787
|
-
inject: function () { return {}; },
|
|
2089
|
+
label: function () { return t("梦境日记"); },
|
|
2090
|
+
inject: function () { return { locale: localeService }; },
|
|
1788
2091
|
}, DreamSection);
|
|
1789
2092
|
});
|
|
1790
2093
|
} catch (error) { /* 没有 slots 服务:仍是合法插件,只是没有设置页 */ }
|
|
@@ -1794,6 +2097,7 @@ exports.apply = apply;
|
|
|
1794
2097
|
exports.inject = inject;
|
|
1795
2098
|
// 纯函数内部件:验证脚本可直接取来测,不必另抄一份算法。
|
|
1796
2099
|
exports.__internals = {
|
|
2100
|
+
displayError: displayError,
|
|
1797
2101
|
routeUrl: routeUrl,
|
|
1798
2102
|
routeUrlFor: routeUrlFor,
|
|
1799
2103
|
KNOWLEDGE_ROUTE: KNOWLEDGE_ROUTE,
|