@arcfold/dsh-method-ui 0.0.0-stage → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/lib/client.js +4237 -0
- package/package.json +35 -4
- package/src/Brand.mjs +17 -0
- package/src/CollaborationView.mjs +56 -0
- package/src/DesktopModels.mjs +40 -0
- package/src/DesktopSetup.mjs +48 -0
- package/src/GoalDock.mjs +130 -0
- package/src/GoalWorkList.mjs +52 -0
- package/src/ImportDock.mjs +172 -0
- package/src/MethodDock.mjs +145 -0
- package/src/ModelSettings.mjs +144 -0
- package/src/OpportunityDock.mjs +66 -0
- package/src/UnderstandingDock.mjs +117 -0
- package/src/browse-scope.mjs +6 -0
- package/src/browse-source.mjs +31 -0
- package/src/client.mjs +138 -0
- package/src/desktop-setup-host.mjs +30 -0
- package/src/desktop-workspace.mjs +8 -0
- package/src/goal-host.mjs +133 -0
- package/src/goal-locales.mjs +78 -0
- package/src/goal-source.mjs +72 -0
- package/src/import-host.mjs +98 -0
- package/src/import-locales.mjs +126 -0
- package/src/import-source.mjs +83 -0
- package/src/index.mjs +127 -0
- package/src/locales.mjs +153 -0
- package/src/main-session.mjs +7 -0
- package/src/method-source.mjs +82 -0
- package/src/model-host.mjs +56 -0
- package/src/model-settings-locales.mjs +91 -0
- package/src/model-settings-source.mjs +119 -0
- package/src/opportunity-host.mjs +118 -0
- package/src/opportunity-locales.mjs +34 -0
- package/src/opportunity-source.mjs +56 -0
- package/src/style.css +99 -0
- package/src/tree-client.mjs +15 -0
- package/src/tree-host.mjs +39 -0
- package/src/tree-source.mjs +65 -0
- package/src/tree-style.css +19 -0
- package/src/tree-view.mjs +278 -0
- package/src/understanding-host.mjs +123 -0
- package/src/understanding-locales.mjs +38 -0
- package/src/understanding-source.mjs +64 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,4237 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({id:"@arcfold/dsh-method-ui",factory:(require)=>{var module={exports:{}};var exports=module.exports;
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/client.mjs
|
|
31
|
+
var client_exports = {};
|
|
32
|
+
__export(client_exports, {
|
|
33
|
+
apply: () => apply,
|
|
34
|
+
inject: () => inject
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(client_exports);
|
|
37
|
+
|
|
38
|
+
// src/CollaborationView.mjs
|
|
39
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
40
|
+
|
|
41
|
+
// src/OpportunityDock.mjs
|
|
42
|
+
var import_react = __toESM(require("react"), 1);
|
|
43
|
+
var h = import_react.default.createElement;
|
|
44
|
+
function status(item, t) {
|
|
45
|
+
const key = item.control?.dismissed ? "opportunityDismissed" : item.stopped ? "opportunityStopped" : !item.run ? item.rejection ? "opportunityRejected" : "opportunityPending" : {
|
|
46
|
+
prepared: "opportunityPrepared",
|
|
47
|
+
submitted: "opportunitySubmitted",
|
|
48
|
+
unknown: "opportunityUncertain",
|
|
49
|
+
stopped: "opportunityStopped",
|
|
50
|
+
settled: "opportunitySettled"
|
|
51
|
+
}[item.run.state] ?? "presentationUnknown";
|
|
52
|
+
return t(key);
|
|
53
|
+
}
|
|
54
|
+
function OpportunityCard({ item, readDetails, sessionId, t }) {
|
|
55
|
+
const [detail, setDetail] = (0, import_react.useState)(null), [error, setError] = (0, import_react.useState)(null);
|
|
56
|
+
(0, import_react.useEffect)(() => {
|
|
57
|
+
let active = true;
|
|
58
|
+
Promise.resolve().then(() => readDetails(item.opportunity.id)).then((value) => {
|
|
59
|
+
if (active) setDetail(value);
|
|
60
|
+
}, (failure) => {
|
|
61
|
+
if (active) setError(failure.message);
|
|
62
|
+
});
|
|
63
|
+
return () => {
|
|
64
|
+
active = false;
|
|
65
|
+
};
|
|
66
|
+
}, [item.opportunity.id, readDetails]);
|
|
67
|
+
const current = detail ?? item, row = current.opportunity;
|
|
68
|
+
return h(
|
|
69
|
+
"article",
|
|
70
|
+
{ className: "opportunity-card", "aria-label": row.description },
|
|
71
|
+
h(
|
|
72
|
+
"div",
|
|
73
|
+
{ className: "opportunity-status" },
|
|
74
|
+
h("span", { className: "status-badge" }, status(current, t)),
|
|
75
|
+
current.notice && h("span", { className: "meta" }, t(current.notice.status === "unread" ? "opportunityUnread" : "opportunityAlreadyRead"))
|
|
76
|
+
),
|
|
77
|
+
h("h3", { className: "text" }, row.description),
|
|
78
|
+
sessionId && row.conversationId !== sessionId && h("p", { className: "meta" }, t("browseOtherSession")),
|
|
79
|
+
row.rationale && h("div", { className: "opportunity-field" }, h("h4", null, t("opportunityRationale")), h("p", { className: "text" }, row.rationale)),
|
|
80
|
+
row.suggestedPreparation && h("div", { className: "opportunity-field" }, h("h4", null, t("opportunitySuggestion")), h("p", { className: "text" }, row.suggestedPreparation)),
|
|
81
|
+
error && h("p", { role: "alert" }, error),
|
|
82
|
+
current.reason && (current.run || current.control || current.rejection) && h("p", { className: "meta" }, current.reason),
|
|
83
|
+
current.rejection && !current.run && h("p", { role: "alert" }, current.rejection.error.message),
|
|
84
|
+
current.run?.error && h("p", { role: "alert" }, current.run.error.message),
|
|
85
|
+
current.artifact && h(
|
|
86
|
+
"div",
|
|
87
|
+
{ className: "opportunity-draft" },
|
|
88
|
+
h("h4", null, t("opportunityDraft")),
|
|
89
|
+
!current.available && h("p", { role: "alert" }, t("opportunityStopped")),
|
|
90
|
+
h("p", { className: "meta" }, current.artifact.evidence.path),
|
|
91
|
+
h("pre", { className: "text" }, current.artifact.evidence.text),
|
|
92
|
+
h("p", { className: "meta" }, t("opportunityNotUsefulYet"))
|
|
93
|
+
),
|
|
94
|
+
detail && h(
|
|
95
|
+
"details",
|
|
96
|
+
{ className: "opportunity-evidence" },
|
|
97
|
+
h("summary", null, t("opportunityEvidence")),
|
|
98
|
+
h("pre", { className: "text" }, JSON.stringify(detail.run?.input ?? detail.originals, null, 2))
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function OpportunityDock({ useOpportunities, onOpportunityDetails, onOpportunityRefresh, onOpportunityPage, opportunitySessionId, viewScope, t }) {
|
|
103
|
+
const state = useOpportunities((value) => value);
|
|
104
|
+
const [open, setOpen] = (0, import_react.useState)(false), [refreshVersion, setRefreshVersion] = (0, import_react.useState)(0);
|
|
105
|
+
const button2 = (label, action, disabled = false, extra = {}) => h("button", { type: "button", disabled, onClick: action, ...extra }, label);
|
|
106
|
+
return h(
|
|
107
|
+
"section",
|
|
108
|
+
{ className: "arcfold-methods arcfold-opportunities", "aria-label": t("opportunityTitle") },
|
|
109
|
+
h(
|
|
110
|
+
"header",
|
|
111
|
+
null,
|
|
112
|
+
h("strong", null, `${t("opportunityTitle")} · ${state.total}`),
|
|
113
|
+
button2(t(open ? "opportunityHide" : "opportunityOpen"), () => setOpen(!open), false, { "aria-expanded": open }),
|
|
114
|
+
button2(t("refresh"), () => {
|
|
115
|
+
setRefreshVersion((value) => value + 1);
|
|
116
|
+
void onOpportunityRefresh();
|
|
117
|
+
})
|
|
118
|
+
),
|
|
119
|
+
state.error && h("p", { role: "alert" }, state.error),
|
|
120
|
+
open && h(
|
|
121
|
+
"div",
|
|
122
|
+
{ className: "detail opportunity-content" },
|
|
123
|
+
h(
|
|
124
|
+
"div",
|
|
125
|
+
{ className: "opportunity-overview" },
|
|
126
|
+
h("p", null, t("opportunityReadOnly")),
|
|
127
|
+
viewScope !== "all" && h("div", { className: "opportunity-status" }, h("span", { className: "status-badge" }, t(state.permission?.enabled ? "opportunityOn" : "opportunityOff"))),
|
|
128
|
+
state.workspace && h("p", { className: "meta" }, `${t("opportunityWorkspace")} · ${state.workspace}`)
|
|
129
|
+
),
|
|
130
|
+
h("ul", { className: "opportunity-list" }, state.items.map((item) => h("li", {
|
|
131
|
+
// Versioned snapshots discard late reads across reconnects and refresh.
|
|
132
|
+
key: JSON.stringify([item, state.permission?.revision, state.resetVersion, refreshVersion])
|
|
133
|
+
}, h(OpportunityCard, { item, readDetails: onOpportunityDetails, sessionId: opportunitySessionId, t })))),
|
|
134
|
+
!state.loading && !state.items.length && h("p", { className: "meta" }, t("opportunityEmpty"))
|
|
135
|
+
),
|
|
136
|
+
open && (state.offset > 0 || state.nextOffset != null) && h(
|
|
137
|
+
"nav",
|
|
138
|
+
{ className: "opportunity-pagination", "aria-label": t("opportunityTitle") },
|
|
139
|
+
button2(t("previous"), () => onOpportunityPage(Math.max(0, state.offset - 20)), !state.offset),
|
|
140
|
+
button2(t("next"), () => onOpportunityPage(state.nextOffset), state.nextOffset == null)
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/locales.mjs
|
|
146
|
+
var zh = {
|
|
147
|
+
collaborationTitle: "Arcfold",
|
|
148
|
+
collaborationClose: "关闭",
|
|
149
|
+
collaborationViewScope: "查看范围",
|
|
150
|
+
collaborationAll: "全部积累",
|
|
151
|
+
collaborationCurrent: "与当前聊天相关",
|
|
152
|
+
browseOtherSession: "来自其他聊天",
|
|
153
|
+
browseReturnToOrigin: "请回到原聊天修改这条记录或继续相关工作。",
|
|
154
|
+
presentationUnknown: "暂未识别",
|
|
155
|
+
stateTrial: "试用中",
|
|
156
|
+
stateActive: "采用中",
|
|
157
|
+
statePaused: "已暂停",
|
|
158
|
+
stateUnavailable: "记录暂不可用",
|
|
159
|
+
stateSuperseded: "已有后继版本",
|
|
160
|
+
stateWithdrawn: "已停止采用",
|
|
161
|
+
stateReserved: "已领取,待核对",
|
|
162
|
+
stateSettled: "已核对",
|
|
163
|
+
roleUser: "用户",
|
|
164
|
+
roleAssistant: "助手",
|
|
165
|
+
roleTool: "工具",
|
|
166
|
+
roleProgram: "程序",
|
|
167
|
+
outcomePending: "尚待结束",
|
|
168
|
+
outcomeCompleted: "本轮已完成",
|
|
169
|
+
outcomeCancelled: "本轮已取消",
|
|
170
|
+
outcomeFailed: "本轮失败",
|
|
171
|
+
outcomeInterrupted: "本轮中断",
|
|
172
|
+
outcomeUnknown: "本轮结果未知",
|
|
173
|
+
admissionAccepted: "原生入口已受理",
|
|
174
|
+
admissionFailed: "提交失败",
|
|
175
|
+
admissionUnknown: "提交结果未知",
|
|
176
|
+
actionStopUnderstanding: "停止采用理解",
|
|
177
|
+
actionWithdrawSource: "撤回来源",
|
|
178
|
+
actionCorrect: "提交逐字纠正",
|
|
179
|
+
title: "协作方法",
|
|
180
|
+
pending: "待决定",
|
|
181
|
+
unread: "未读提醒",
|
|
182
|
+
loading: "正在读取方法…",
|
|
183
|
+
empty: "此范围内暂无协作方法。",
|
|
184
|
+
decisionPrompt: "需要你决定:是否继续采用此版本。",
|
|
185
|
+
reviewDecision: "查看并决定",
|
|
186
|
+
viewDetails: "查看详情",
|
|
187
|
+
history: "历史提醒",
|
|
188
|
+
backToMethods: "返回方法列表",
|
|
189
|
+
keepPaused: "保持暂停",
|
|
190
|
+
open: "查看全文与理由",
|
|
191
|
+
close: "收起详情",
|
|
192
|
+
refresh: "刷新",
|
|
193
|
+
previous: "上一页",
|
|
194
|
+
next: "下一页",
|
|
195
|
+
guidance: "完整指导",
|
|
196
|
+
applies: "适用条件",
|
|
197
|
+
excludes: "不适用条件",
|
|
198
|
+
sources: "依据原件",
|
|
199
|
+
reason: "当前状态与理由",
|
|
200
|
+
version: "内容版本",
|
|
201
|
+
revision: "记录版本",
|
|
202
|
+
status: "状态",
|
|
203
|
+
usage: "已选入试用",
|
|
204
|
+
remaining: "剩余次数",
|
|
205
|
+
pendingAdoptions: "尚待核对的采用",
|
|
206
|
+
conversation: "交流",
|
|
207
|
+
turn: "轮次",
|
|
208
|
+
successors: "后继版本",
|
|
209
|
+
continue: "继续采用此版本",
|
|
210
|
+
pause: "暂停采用此版本",
|
|
211
|
+
busy: "正在保存…",
|
|
212
|
+
saved: "决定已保存。继续采用不代表效果已经验证,也不增加工作或工具权限。",
|
|
213
|
+
failed: "操作未完成。",
|
|
214
|
+
unknown: "结果尚未确认;可再次点击同一操作,沿用原请求编号。",
|
|
215
|
+
conflict: "方法已变化。请重新查看以下全文和理由,再决定是否提交。",
|
|
216
|
+
notifications: "停止与到期提醒",
|
|
217
|
+
read: "已读",
|
|
218
|
+
acknowledge: "标记已读",
|
|
219
|
+
noNotifications: "目前没有提醒。",
|
|
220
|
+
noSources: "没有可显示的原件。",
|
|
221
|
+
sourceIdentity: "原件身份",
|
|
222
|
+
sourceTime: "原始时间",
|
|
223
|
+
author: "作者角色",
|
|
224
|
+
provenance: "来源归属",
|
|
225
|
+
retry: "重新读取详情",
|
|
226
|
+
boundary: "查看与已读不会恢复方法;只有下方按钮会保存你的决定。"
|
|
227
|
+
};
|
|
228
|
+
var en = {
|
|
229
|
+
collaborationTitle: "Arcfold",
|
|
230
|
+
collaborationClose: "Close",
|
|
231
|
+
collaborationViewScope: "View scope",
|
|
232
|
+
collaborationAll: "All records",
|
|
233
|
+
collaborationCurrent: "Related to this conversation",
|
|
234
|
+
browseOtherSession: "From another conversation",
|
|
235
|
+
browseReturnToOrigin: "Return to the original conversation to change this record or continue its work.",
|
|
236
|
+
presentationUnknown: "Not yet recognized",
|
|
237
|
+
stateTrial: "In trial",
|
|
238
|
+
stateActive: "In use",
|
|
239
|
+
statePaused: "Paused",
|
|
240
|
+
stateUnavailable: "Record unavailable",
|
|
241
|
+
stateSuperseded: "Superseded",
|
|
242
|
+
stateWithdrawn: "Withdrawn",
|
|
243
|
+
stateReserved: "Reserved; awaiting reconciliation",
|
|
244
|
+
stateSettled: "Reconciled",
|
|
245
|
+
roleUser: "User",
|
|
246
|
+
roleAssistant: "Assistant",
|
|
247
|
+
roleTool: "Tool",
|
|
248
|
+
roleProgram: "Program",
|
|
249
|
+
outcomePending: "Awaiting turn end",
|
|
250
|
+
outcomeCompleted: "Turn completed",
|
|
251
|
+
outcomeCancelled: "Turn cancelled",
|
|
252
|
+
outcomeFailed: "Turn failed",
|
|
253
|
+
outcomeInterrupted: "Turn interrupted",
|
|
254
|
+
outcomeUnknown: "Turn outcome unknown",
|
|
255
|
+
admissionAccepted: "Accepted by native input",
|
|
256
|
+
admissionFailed: "Submission failed",
|
|
257
|
+
admissionUnknown: "Submission outcome unknown",
|
|
258
|
+
actionStopUnderstanding: "Stop using understanding",
|
|
259
|
+
actionWithdrawSource: "Withdraw source",
|
|
260
|
+
actionCorrect: "Submit exact correction",
|
|
261
|
+
title: "Collaboration methods",
|
|
262
|
+
pending: "Awaiting decision",
|
|
263
|
+
unread: "Unread notices",
|
|
264
|
+
loading: "Loading methods…",
|
|
265
|
+
empty: "No collaboration methods in this view.",
|
|
266
|
+
decisionPrompt: "Your decision: whether to keep using this version.",
|
|
267
|
+
reviewDecision: "Review and decide",
|
|
268
|
+
viewDetails: "View details",
|
|
269
|
+
history: "Notice history",
|
|
270
|
+
backToMethods: "Back to methods",
|
|
271
|
+
keepPaused: "Keep paused",
|
|
272
|
+
open: "Read full method and reason",
|
|
273
|
+
close: "Close details",
|
|
274
|
+
refresh: "Refresh",
|
|
275
|
+
previous: "Previous",
|
|
276
|
+
next: "Next",
|
|
277
|
+
guidance: "Full guidance",
|
|
278
|
+
applies: "Applies when",
|
|
279
|
+
excludes: "Does not apply when",
|
|
280
|
+
sources: "Original evidence",
|
|
281
|
+
reason: "Current state and reason",
|
|
282
|
+
version: "Content version",
|
|
283
|
+
revision: "Record revision",
|
|
284
|
+
status: "State",
|
|
285
|
+
usage: "Trial selections",
|
|
286
|
+
remaining: "Remaining",
|
|
287
|
+
pendingAdoptions: "Selections awaiting reconciliation",
|
|
288
|
+
conversation: "Conversation",
|
|
289
|
+
turn: "Turn",
|
|
290
|
+
successors: "Later versions",
|
|
291
|
+
continue: "Continue this version",
|
|
292
|
+
pause: "Pause this version",
|
|
293
|
+
busy: "Saving…",
|
|
294
|
+
saved: "Decision saved. Continuing does not validate effectiveness or grant additional work or tool permissions.",
|
|
295
|
+
failed: "The action did not complete.",
|
|
296
|
+
unknown: "Outcome not confirmed; repeat the same action to reuse its request ID.",
|
|
297
|
+
conflict: "The method changed. Read the refreshed guidance and reason before deciding again.",
|
|
298
|
+
notifications: "Pause and trial notices",
|
|
299
|
+
read: "Read",
|
|
300
|
+
acknowledge: "Mark read",
|
|
301
|
+
noNotifications: "No notices.",
|
|
302
|
+
noSources: "No original evidence is available to display.",
|
|
303
|
+
sourceIdentity: "Source identity",
|
|
304
|
+
sourceTime: "Original time",
|
|
305
|
+
author: "Author role",
|
|
306
|
+
provenance: "Provenance",
|
|
307
|
+
retry: "Reload details",
|
|
308
|
+
boundary: "Viewing or marking read does not resume a method. Only these controls save your decision."
|
|
309
|
+
};
|
|
310
|
+
var presentationKeys = {
|
|
311
|
+
"method": {
|
|
312
|
+
"trial": "stateTrial",
|
|
313
|
+
"active": "stateActive",
|
|
314
|
+
"paused": "statePaused",
|
|
315
|
+
"unavailable": "stateUnavailable",
|
|
316
|
+
"superseded": "stateSuperseded"
|
|
317
|
+
},
|
|
318
|
+
"understanding": {
|
|
319
|
+
"active": "stateActive",
|
|
320
|
+
"withdrawn": "stateWithdrawn",
|
|
321
|
+
"superseded": "stateSuperseded"
|
|
322
|
+
},
|
|
323
|
+
"adoption": {
|
|
324
|
+
"reserved": "stateReserved",
|
|
325
|
+
"settled": "stateSettled"
|
|
326
|
+
},
|
|
327
|
+
"role": {
|
|
328
|
+
"user": "roleUser",
|
|
329
|
+
"assistant": "roleAssistant",
|
|
330
|
+
"tool": "roleTool",
|
|
331
|
+
"program": "roleProgram"
|
|
332
|
+
},
|
|
333
|
+
"outcome": {
|
|
334
|
+
"pending": "outcomePending",
|
|
335
|
+
"completed": "outcomeCompleted",
|
|
336
|
+
"cancelled": "outcomeCancelled",
|
|
337
|
+
"failed": "outcomeFailed",
|
|
338
|
+
"interrupted": "outcomeInterrupted",
|
|
339
|
+
"unknown": "outcomeUnknown"
|
|
340
|
+
},
|
|
341
|
+
"admission": {
|
|
342
|
+
"accepted": "admissionAccepted",
|
|
343
|
+
"failed": "admissionFailed",
|
|
344
|
+
"unknown": "admissionUnknown"
|
|
345
|
+
},
|
|
346
|
+
"understandingAction": {
|
|
347
|
+
"stop-understanding": "actionStopUnderstanding",
|
|
348
|
+
"withdraw-source": "actionWithdrawSource",
|
|
349
|
+
"correct": "actionCorrect"
|
|
350
|
+
},
|
|
351
|
+
"goal": {
|
|
352
|
+
"tracking": "goalTracking",
|
|
353
|
+
"paused": "goalPaused",
|
|
354
|
+
"closed": "goalClosed"
|
|
355
|
+
},
|
|
356
|
+
"relation": {
|
|
357
|
+
"part_of": "goalPart",
|
|
358
|
+
"supports": "goalSupports",
|
|
359
|
+
"derived_from": "goalDerived"
|
|
360
|
+
},
|
|
361
|
+
"claim": {
|
|
362
|
+
"user_statement": "understandingLiteral",
|
|
363
|
+
"inference": "understandingInference",
|
|
364
|
+
"open_question": "understandingQuestion"
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
function presentationLabel(group, value, t) {
|
|
368
|
+
const entries = Object.hasOwn(presentationKeys, group) ? presentationKeys[group] : null;
|
|
369
|
+
return t(entries && Object.hasOwn(entries, value) ? entries[value] : "presentationUnknown");
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// src/MethodDock.mjs
|
|
373
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
374
|
+
var h2 = import_react2.default.createElement;
|
|
375
|
+
var text = (key, value) => h2("div", { className: "text", key }, value ?? "");
|
|
376
|
+
function MethodDock({ useMethods, onDetails, onDecide, onAcknowledge, onRefresh, onPage, t }) {
|
|
377
|
+
const state = useMethods((value) => value);
|
|
378
|
+
const [expanded, setExpanded] = (0, import_react2.useState)(false);
|
|
379
|
+
const [detail, setDetail] = (0, import_react2.useState)(null);
|
|
380
|
+
const [detailLoading, setDetailLoading] = (0, import_react2.useState)(false);
|
|
381
|
+
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
382
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
383
|
+
const [notice, setNotice] = (0, import_react2.useState)(null);
|
|
384
|
+
const actionPending = (0, import_react2.useRef)(false), detailGeneration = (0, import_react2.useRef)(0), retryIds = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
385
|
+
async function show(methodId, keepError = false) {
|
|
386
|
+
const generation = ++detailGeneration.current;
|
|
387
|
+
setDetailLoading(true);
|
|
388
|
+
setExpanded(true);
|
|
389
|
+
if (!keepError) {
|
|
390
|
+
setError(null);
|
|
391
|
+
setNotice(null);
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
const value = await onDetails(methodId);
|
|
395
|
+
if (generation === detailGeneration.current) setDetail(value);
|
|
396
|
+
} catch (failure) {
|
|
397
|
+
if (generation === detailGeneration.current) {
|
|
398
|
+
setDetail(null);
|
|
399
|
+
setError(failure.message || t("failed"));
|
|
400
|
+
}
|
|
401
|
+
} finally {
|
|
402
|
+
if (generation === detailGeneration.current) setDetailLoading(false);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function closeDetail() {
|
|
406
|
+
detailGeneration.current++;
|
|
407
|
+
setDetail(null);
|
|
408
|
+
setDetailLoading(false);
|
|
409
|
+
}
|
|
410
|
+
async function historyPage(offset) {
|
|
411
|
+
if (!detail) return;
|
|
412
|
+
const methodId = detail.method.id, generation = ++detailGeneration.current;
|
|
413
|
+
setDetailLoading(true);
|
|
414
|
+
setError(null);
|
|
415
|
+
try {
|
|
416
|
+
const value = await onDetails(methodId, offset);
|
|
417
|
+
if (generation === detailGeneration.current) setDetail((current) => current?.method.id === methodId ? { ...current, notifications: value.notifications } : current);
|
|
418
|
+
} catch (failure) {
|
|
419
|
+
if (generation === detailGeneration.current) setError(failure.message || t("failed"));
|
|
420
|
+
} finally {
|
|
421
|
+
if (generation === detailGeneration.current) setDetailLoading(false);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function decide(action) {
|
|
425
|
+
if (actionPending.current || detailLoading || !detail) return;
|
|
426
|
+
actionPending.current = true;
|
|
427
|
+
setBusy(true);
|
|
428
|
+
setError(null);
|
|
429
|
+
setNotice(null);
|
|
430
|
+
const request = {
|
|
431
|
+
methodId: detail.method.id,
|
|
432
|
+
expectedRevision: detail.method.revision,
|
|
433
|
+
contentHash: detail.contentHash,
|
|
434
|
+
action
|
|
435
|
+
};
|
|
436
|
+
const key = JSON.stringify(request);
|
|
437
|
+
if (!retryIds.current.has(key)) retryIds.current.set(key, globalThis.crypto.randomUUID());
|
|
438
|
+
request.requestId = retryIds.current.get(key);
|
|
439
|
+
try {
|
|
440
|
+
await onDecide(request);
|
|
441
|
+
retryIds.current.delete(key);
|
|
442
|
+
setNotice(t("saved"));
|
|
443
|
+
await show(request.methodId, true);
|
|
444
|
+
} catch (failure) {
|
|
445
|
+
if (failure.remote) retryIds.current.delete(key);
|
|
446
|
+
setError(failure.remote ? failure.message : `${failure.message || t("failed")} ${t("unknown")}`);
|
|
447
|
+
if (failure.code === "revision_conflict" || failure.code === "method_superseded" || failure.code === "method_source_invalid") {
|
|
448
|
+
setError(`${failure.message || ""} ${t("conflict")}`);
|
|
449
|
+
await show(request.methodId, true);
|
|
450
|
+
}
|
|
451
|
+
} finally {
|
|
452
|
+
actionPending.current = false;
|
|
453
|
+
setBusy(false);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async function acknowledge(id) {
|
|
457
|
+
if (actionPending.current) return;
|
|
458
|
+
actionPending.current = true;
|
|
459
|
+
setBusy(true);
|
|
460
|
+
setError(null);
|
|
461
|
+
try {
|
|
462
|
+
await onAcknowledge(id);
|
|
463
|
+
await historyPage(detail?.notifications?.offset ?? 0);
|
|
464
|
+
} catch (failure) {
|
|
465
|
+
setError(failure.message || t("failed"));
|
|
466
|
+
} finally {
|
|
467
|
+
actionPending.current = false;
|
|
468
|
+
setBusy(false);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const button2 = (label, action, disabled = false, extra = {}) => h2("button", {
|
|
472
|
+
type: "button",
|
|
473
|
+
disabled,
|
|
474
|
+
onClick: () => {
|
|
475
|
+
void action();
|
|
476
|
+
},
|
|
477
|
+
...extra
|
|
478
|
+
}, label);
|
|
479
|
+
const method = detail?.method;
|
|
480
|
+
const history = detail?.notifications;
|
|
481
|
+
return h2(
|
|
482
|
+
"section",
|
|
483
|
+
{ className: "arcfold-methods", "aria-label": t("title") },
|
|
484
|
+
h2(
|
|
485
|
+
"header",
|
|
486
|
+
null,
|
|
487
|
+
h2("strong", null, `${t("title")} · ${t("pending")} ${state.decisionCount} · ${t("unread")} ${state.unreadCount}`),
|
|
488
|
+
button2(expanded ? t("close") : t("open"), () => setExpanded(!expanded), false, { "aria-expanded": expanded }),
|
|
489
|
+
button2(t("refresh"), onRefresh)
|
|
490
|
+
),
|
|
491
|
+
state.loading && h2("p", { role: "status" }, t("loading")),
|
|
492
|
+
state.error && h2("p", { role: "alert" }, state.error),
|
|
493
|
+
expanded && h2(
|
|
494
|
+
"div",
|
|
495
|
+
{ className: "detail" },
|
|
496
|
+
error && h2("p", { role: "alert" }, error),
|
|
497
|
+
notice && h2("p", { role: "status" }, notice),
|
|
498
|
+
!method && !detailLoading && h2("ul", null, state.items.map((item) => h2(
|
|
499
|
+
"li",
|
|
500
|
+
{ key: item.method.id },
|
|
501
|
+
h2("strong", null, item.method.title),
|
|
502
|
+
h2("p", { className: "meta" }, `${presentationLabel("method", item.method.status, t)} · ${t("version")} ${item.method.version ?? 1}`),
|
|
503
|
+
text("current-reason", item.reason),
|
|
504
|
+
item.needsDecision && h2("p", null, t("decisionPrompt")),
|
|
505
|
+
item.unreadCount > 0 && h2("p", { className: "meta" }, `${t("unread")} ${item.unreadCount}`),
|
|
506
|
+
button2(t(item.needsDecision ? "reviewDecision" : "viewDetails"), () => show(item.method.id), busy)
|
|
507
|
+
))),
|
|
508
|
+
!method && !detailLoading && !state.loading && !state.error && !state.items.length && h2("p", null, t("empty")),
|
|
509
|
+
!method && !detailLoading && (state.offset > 0 || state.nextOffset != null) && h2(
|
|
510
|
+
"nav",
|
|
511
|
+
{ "aria-label": t("title") },
|
|
512
|
+
button2(t("previous"), () => onPage(Math.max(0, state.offset - 20)), state.offset === 0),
|
|
513
|
+
button2(t("next"), () => onPage(state.nextOffset), state.nextOffset === null || state.nextOffset === void 0)
|
|
514
|
+
),
|
|
515
|
+
detailLoading && h2("p", { role: "status" }, t("loading")),
|
|
516
|
+
method && h2("ul", null, h2("li", null, h2(
|
|
517
|
+
"article",
|
|
518
|
+
{ "aria-label": method.title },
|
|
519
|
+
button2(t("backToMethods"), closeDetail, busy),
|
|
520
|
+
h2("h3", null, method.title),
|
|
521
|
+
h2("p", { className: "meta" }, [
|
|
522
|
+
`${t("version")} ${method.version ?? 1}`,
|
|
523
|
+
method.revision != null ? `${t("revision")} ${method.revision}` : null,
|
|
524
|
+
`${t("status")} ${presentationLabel("method", method.status, t)}`
|
|
525
|
+
].filter(Boolean).join(" · ")),
|
|
526
|
+
h2("h4", null, t("reason")),
|
|
527
|
+
text("reason", detail.reason),
|
|
528
|
+
detail.needsDecision && h2("p", null, t("decisionPrompt")),
|
|
529
|
+
detail.usage && h2("p", null, `${t("usage")} ${detail.usage.trialUses}/${detail.usage.maxTrialUses} · ${t("remaining")} ${detail.usage.remaining}`),
|
|
530
|
+
detail.pendingAdoptionCount > 0 && h2(
|
|
531
|
+
"div",
|
|
532
|
+
null,
|
|
533
|
+
h2("h4", null, `${t("pendingAdoptions")} ${detail.pendingAdoptionCount}`),
|
|
534
|
+
(detail.pendingAdoptions || []).map((adoption) => h2(
|
|
535
|
+
"p",
|
|
536
|
+
{ className: "meta", key: adoption.id },
|
|
537
|
+
`${adoption.id} · ${t("conversation")} ${adoption.sessionId} · ${t("turn")} ${adoption.turn} · ${presentationLabel("adoption", adoption.state, t)}`
|
|
538
|
+
))
|
|
539
|
+
),
|
|
540
|
+
method.guidance !== void 0 && h2(
|
|
541
|
+
"div",
|
|
542
|
+
null,
|
|
543
|
+
h2("h4", null, t("guidance")),
|
|
544
|
+
text("guidance", method.guidance),
|
|
545
|
+
h2("h4", null, t("applies")),
|
|
546
|
+
text("applies", method.appliesTo),
|
|
547
|
+
h2("h4", null, t("excludes")),
|
|
548
|
+
text("excludes", method.doesNotApplyTo)
|
|
549
|
+
),
|
|
550
|
+
h2("h4", null, t("sources")),
|
|
551
|
+
detail.sources.length ? detail.sources.map((source) => h2(
|
|
552
|
+
"div",
|
|
553
|
+
{ className: "source", key: source.id },
|
|
554
|
+
h2("p", { className: "meta" }, `${t("sourceIdentity")}: ${source.id} · ${t("revision")} ${source.revision} · ${t("author")}: ${presentationLabel("role", source.role, t)} · ${t("sourceTime")}: ${source.occurredAt ?? ""}`),
|
|
555
|
+
text("content", source.content),
|
|
556
|
+
h2("p", { className: "meta" }, `${t("provenance")}: ${JSON.stringify(source.provenance ?? null)}`)
|
|
557
|
+
)) : h2("p", null, t("noSources")),
|
|
558
|
+
detail.successors.length > 0 && h2(
|
|
559
|
+
"div",
|
|
560
|
+
null,
|
|
561
|
+
h2("h4", null, t("successors")),
|
|
562
|
+
detail.successors.map((next) => h2(
|
|
563
|
+
"p",
|
|
564
|
+
{ key: next.id },
|
|
565
|
+
`${next.title} · ${t("version")} ${next.version} · ${presentationLabel("method", next.status, t)} `,
|
|
566
|
+
button2(t("open"), () => show(next.id), busy)
|
|
567
|
+
))
|
|
568
|
+
),
|
|
569
|
+
!detail.activation.allowed && method.status !== "active" && h2("p", { role: "alert" }, detail.activation.message),
|
|
570
|
+
h2("p", { className: "meta" }, t("boundary")),
|
|
571
|
+
h2(
|
|
572
|
+
"div",
|
|
573
|
+
{ className: "actions" },
|
|
574
|
+
button2(busy ? t("busy") : t("continue"), () => decide("continue"), busy || detailLoading || !detail.activation.allowed),
|
|
575
|
+
button2(
|
|
576
|
+
t(method.status === "paused" ? "keepPaused" : "pause"),
|
|
577
|
+
() => decide("pause"),
|
|
578
|
+
busy || detailLoading || !(["trial", "active"].includes(method.status) || method.status === "paused" && detail.needsDecision)
|
|
579
|
+
),
|
|
580
|
+
button2(t("retry"), () => show(method.id), busy)
|
|
581
|
+
),
|
|
582
|
+
h2(
|
|
583
|
+
"details",
|
|
584
|
+
{ key: method.id, "aria-label": t("history") },
|
|
585
|
+
h2("summary", null, `${t("history")} ${history?.total ?? 0} · ${t("unread")} ${history?.unreadCount ?? 0}`),
|
|
586
|
+
h2("ul", null, (history?.items || []).map((item) => h2(
|
|
587
|
+
"li",
|
|
588
|
+
{ key: item.id },
|
|
589
|
+
h2("strong", null, item.title),
|
|
590
|
+
text("message", item.displayMessage ?? item.message),
|
|
591
|
+
h2("p", { className: "meta" }, `${t("version")} ${item.methodVersion} · ${item.createdAt ?? ""}`),
|
|
592
|
+
item.status === "unread" ? button2(t("acknowledge"), () => acknowledge(item.id), busy || detailLoading) : h2("span", null, t("read"))
|
|
593
|
+
))),
|
|
594
|
+
!history?.total && h2("p", null, t("noNotifications")),
|
|
595
|
+
(history?.offset > 0 || history?.nextOffset != null) && h2(
|
|
596
|
+
"nav",
|
|
597
|
+
{ "aria-label": t("history") },
|
|
598
|
+
button2(t("previous"), () => historyPage(Math.max(0, history.offset - 20)), busy || detailLoading || history.offset === 0),
|
|
599
|
+
button2(t("next"), () => historyPage(history.nextOffset), busy || detailLoading || history.nextOffset == null)
|
|
600
|
+
)
|
|
601
|
+
)
|
|
602
|
+
)))
|
|
603
|
+
)
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/GoalWorkList.mjs
|
|
608
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
609
|
+
var h3 = import_react3.default.createElement;
|
|
610
|
+
function workStatus(work, t) {
|
|
611
|
+
if (work.stage) {
|
|
612
|
+
const keys2 = { waiting: "goalStageWaiting", completed: "goalStageComplete", paused: "goalWorkPaused", needs_review: "goalStageReview" };
|
|
613
|
+
if (Object.hasOwn(keys2, work.stage.status)) return t(keys2[work.stage.status]);
|
|
614
|
+
} else if (work.phase === "active") return t("goalStageReview");
|
|
615
|
+
if (work.phase === "active") return t(work.activation === "armed" ? "goalWorkEnabled" : work.activation === "disarmed" ? "goalWorkDisarmed" : "presentationUnknown");
|
|
616
|
+
const keys = { paused: "goalWorkPaused", blocked: "goalWorkBlocked", complete: "goalWorkComplete", cleared: "goalWorkCleared" };
|
|
617
|
+
return t(Object.hasOwn(keys, work.phase) ? keys[work.phase] : "presentationUnknown");
|
|
618
|
+
}
|
|
619
|
+
function GoalWorkList({ works = [], title, onAction, busy, t }) {
|
|
620
|
+
if (!works.length) return null;
|
|
621
|
+
return h3(
|
|
622
|
+
"section",
|
|
623
|
+
{ className: "goal-work-list", "aria-label": t("goalWorkTitle") },
|
|
624
|
+
title && h3("h4", null, title),
|
|
625
|
+
works.map((work) => {
|
|
626
|
+
const relation = work.relation;
|
|
627
|
+
const linked = relation?.state === "current";
|
|
628
|
+
const blocker = work.blockedReason;
|
|
629
|
+
const reasonKey = {
|
|
630
|
+
"round-limit": "goalBlockRoundLimit",
|
|
631
|
+
"queue-failed": "goalBlockQueueFailed",
|
|
632
|
+
"prompt-rejected": "goalBlockPromptRejected"
|
|
633
|
+
}[blocker?.code];
|
|
634
|
+
return h3(
|
|
635
|
+
"div",
|
|
636
|
+
{ className: "goal-work", key: work.id },
|
|
637
|
+
h3("span", { className: "goal-status-badge" }, workStatus(work, t)),
|
|
638
|
+
work.otherSession && h3("p", { className: "meta" }, t("browseReturnToOrigin")),
|
|
639
|
+
h3("p", { className: "text" }, work.stage?.available === false ? t("goalStageReview") : work.stage?.objective || (linked ? relation.reason : work.objective)),
|
|
640
|
+
work.stage?.reason && h3("p", { className: "meta" }, work.stage.reason),
|
|
641
|
+
!relation && h3("p", { className: "meta" }, t("goalWorkUnlinked")),
|
|
642
|
+
relation && !linked && h3(
|
|
643
|
+
"p",
|
|
644
|
+
{ className: "meta" },
|
|
645
|
+
t(relation.state === "needs_review" ? "goalWorkRelationReview" : "goalRelationUnavailable")
|
|
646
|
+
),
|
|
647
|
+
work.phase === "blocked" && h3(
|
|
648
|
+
"div",
|
|
649
|
+
{ className: "goal-work-blocker" },
|
|
650
|
+
h3("h5", null, t("goalBlockReason")),
|
|
651
|
+
h3("p", { className: "text" }, reasonKey ? t(reasonKey) : blocker?.message || t("goalBlockUnknown"))
|
|
652
|
+
),
|
|
653
|
+
work.canStop && onAction && h3("button", {
|
|
654
|
+
type: "button",
|
|
655
|
+
disabled: busy,
|
|
656
|
+
onClick: () => {
|
|
657
|
+
void onAction("stop", work);
|
|
658
|
+
}
|
|
659
|
+
}, t("goalWorkStop")),
|
|
660
|
+
work.canResume && onAction && h3("button", {
|
|
661
|
+
type: "button",
|
|
662
|
+
disabled: busy,
|
|
663
|
+
onClick: () => {
|
|
664
|
+
void onAction("resume", work);
|
|
665
|
+
}
|
|
666
|
+
}, t("goalWorkResume")),
|
|
667
|
+
h3(
|
|
668
|
+
"details",
|
|
669
|
+
{ className: "goal-work-details" },
|
|
670
|
+
h3("summary", null, t("goalWorkOriginal")),
|
|
671
|
+
h3("p", { className: "text" }, work.stage?.available === false ? t("goalStageReview") : work.stage?.objective || work.objective),
|
|
672
|
+
work.stage?.doneWhen && h3("p", { className: "text" }, `${t("goalStageDoneWhen")} ${work.stage.doneWhen}`),
|
|
673
|
+
work.stage?.boundaries && h3("p", { className: "text" }, `${t("goalStageBoundaries")} ${work.stage.boundaries}`),
|
|
674
|
+
work.phase === "blocked" && blocker?.message && h3("p", { className: "text" }, blocker.message),
|
|
675
|
+
h3("p", { className: "meta" }, `${t("goalWorkRounds")} ${work.roundsStarted} / ${work.maxGoalRounds}`),
|
|
676
|
+
h3("p", { className: "meta" }, t(work.provenance === "verified-native-delegation" ? "goalWorkVerified" : work.provenance === "source-unavailable" ? "goalWorkSourceUnavailable" : "goalWorkLegacy")),
|
|
677
|
+
h3("p", { className: "meta" }, `${work.sessionId} · ${work.nativeGoalId} · ${t("revision")} ${work.nativeRevision}`),
|
|
678
|
+
relation && h3("p", { className: "meta" }, `${relation.goalId} · ${t("goalDefinition")} ${relation.definitionVersion}`)
|
|
679
|
+
)
|
|
680
|
+
);
|
|
681
|
+
})
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/GoalDock.mjs
|
|
686
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
687
|
+
var h4 = import_react4.default.createElement;
|
|
688
|
+
var content = (value) => typeof value === "string" ? value : value?.content ?? "";
|
|
689
|
+
var version = (goal) => JSON.stringify([goal.id, goal.revision, goal.definitionVersion]);
|
|
690
|
+
function GoalCard({ detail, works, canDiscuss, onGoalDiscuss, onGoalRefresh, onAction, workBusy, t }) {
|
|
691
|
+
const goal = detail.goal, allowed = detail.availability?.allowed === true;
|
|
692
|
+
const [draft, setDraft] = (0, import_react4.useState)(""), [draftVersion, setDraftVersion] = (0, import_react4.useState)(null);
|
|
693
|
+
const [busy, setBusy] = (0, import_react4.useState)(false), [error, setError] = (0, import_react4.useState)(null), [notice, setNotice] = (0, import_react4.useState)(null);
|
|
694
|
+
const pending = (0, import_react4.useRef)(false), retryIds = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
695
|
+
const staleDraft = Boolean(draft && draftVersion !== version(goal));
|
|
696
|
+
async function discuss() {
|
|
697
|
+
if (pending.current || !draft.trim() || staleDraft || !canDiscuss || !(allowed || detail.repairAllowed)) return;
|
|
698
|
+
const request = {
|
|
699
|
+
goalId: goal.id,
|
|
700
|
+
expectedRevision: goal.revision,
|
|
701
|
+
definitionVersion: goal.definitionVersion,
|
|
702
|
+
mode: allowed ? "discuss" : "repair",
|
|
703
|
+
text: draft
|
|
704
|
+
};
|
|
705
|
+
const key = JSON.stringify(request);
|
|
706
|
+
if (!retryIds.current.has(key)) retryIds.current.set(key, globalThis.crypto.randomUUID());
|
|
707
|
+
request.requestId = retryIds.current.get(key);
|
|
708
|
+
pending.current = true;
|
|
709
|
+
setBusy(true);
|
|
710
|
+
setError(null);
|
|
711
|
+
setNotice(null);
|
|
712
|
+
try {
|
|
713
|
+
const result = await onGoalDiscuss(request), admission = result.binding?.admission?.state;
|
|
714
|
+
setNotice(t(admission === "accepted" ? "goalAccepted" : admission === "failed" ? "goalFailed" : "goalUnknown"));
|
|
715
|
+
await onGoalRefresh();
|
|
716
|
+
} catch (failure) {
|
|
717
|
+
setError(failure.remote ? failure.message : `${failure.message || t("failed")} ${t("goalUnknown")}`);
|
|
718
|
+
if (["revision_conflict", "goal_source_invalid", "goal_unavailable"].includes(failure.code)) await onGoalRefresh();
|
|
719
|
+
} finally {
|
|
720
|
+
pending.current = false;
|
|
721
|
+
setBusy(false);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const text2 = (key, value) => h4("div", { key, className: "text" }, content(value));
|
|
725
|
+
const label = (kind, value) => presentationLabel(kind, value, t);
|
|
726
|
+
return h4(
|
|
727
|
+
"article",
|
|
728
|
+
{ className: "goal-card", id: `arcfold-goal-${goal.id}`, "aria-label": goal.title || t("goalUnavailable") },
|
|
729
|
+
h4(
|
|
730
|
+
"header",
|
|
731
|
+
{ className: "goal-card-heading" },
|
|
732
|
+
h4("h3", null, goal.title || t("goalUnavailable")),
|
|
733
|
+
h4("span", { className: "goal-status-badge" }, label("goal", goal.status))
|
|
734
|
+
),
|
|
735
|
+
detail.browse?.related === false && h4("p", { className: "meta" }, t("browseReturnToOrigin")),
|
|
736
|
+
!allowed && h4("p", { role: "alert" }, detail.availability?.message || t("goalUnavailable")),
|
|
737
|
+
allowed && h4("div", { className: "goal-outcome text" }, content(goal.outcome)),
|
|
738
|
+
h4(
|
|
739
|
+
"div",
|
|
740
|
+
{ className: "goal-card-section" },
|
|
741
|
+
works.length ? h4(GoalWorkList, { works, title: t("goalCurrentWork"), onAction, busy: workBusy, t }) : h4(import_react4.default.Fragment, null, h4("h4", null, t("goalCurrentWork")), h4("p", { className: "meta" }, t("goalNoCurrentWork")))
|
|
742
|
+
),
|
|
743
|
+
allowed && h4(
|
|
744
|
+
"div",
|
|
745
|
+
{ className: "goal-card-section goal-progress" },
|
|
746
|
+
h4("h4", null, t("goalProgress")),
|
|
747
|
+
text2("progress", detail.coverage?.progressUnavailable > 0 ? t("goalProgressUnavailable") : content(goal.progress) || t("goalNoProgress"))
|
|
748
|
+
),
|
|
749
|
+
h4(
|
|
750
|
+
"details",
|
|
751
|
+
{ className: "goal-card-evidence" },
|
|
752
|
+
h4("summary", null, t("goalEvidence")),
|
|
753
|
+
h4("p", { className: "meta" }, `${t("goalDefinition")} ${goal.definitionVersion} · ${t("revision")} ${goal.revision}`),
|
|
754
|
+
allowed && h4(
|
|
755
|
+
import_react4.default.Fragment,
|
|
756
|
+
null,
|
|
757
|
+
h4("h4", null, t("goalRelations")),
|
|
758
|
+
!(detail.relations || []).length && h4("p", { className: "meta" }, t("goalNoRelations")),
|
|
759
|
+
(detail.relations || []).map((item, index) => h4(
|
|
760
|
+
"p",
|
|
761
|
+
{ className: "meta", key: index },
|
|
762
|
+
`${label("relation", item.type)} · ${item.targetId} · ${item.state === "needs_review" ? t("goalRelationReview") : item.state === "unavailable" ? t("goalRelationUnavailable") : ""}`
|
|
763
|
+
)),
|
|
764
|
+
h4("h4", null, t("sources")),
|
|
765
|
+
(detail.sources || []).map((source) => h4(
|
|
766
|
+
"div",
|
|
767
|
+
{ className: "source", key: source.id },
|
|
768
|
+
h4("p", { className: "meta" }, `${source.id} · ${label("role", source.role)} · ${t("revision")} ${source.revision} · ${source.occurredAt || ""}`),
|
|
769
|
+
text2("content", source.content),
|
|
770
|
+
h4("p", { className: "meta" }, JSON.stringify(source.provenance || null))
|
|
771
|
+
))
|
|
772
|
+
),
|
|
773
|
+
(detail.links || []).map((link) => h4(
|
|
774
|
+
"div",
|
|
775
|
+
{ className: "source", key: link.id },
|
|
776
|
+
h4("p", { className: "meta" }, `${link.sessionId} · ${t("turn")} ${link.turn} · ${t("goalDefinition")} ${link.definitionVersion} · ${label("outcome", link.outcome?.outcome ?? "pending")} · ${t("goalUnassessed")}`),
|
|
777
|
+
h4("p", { className: "meta" }, t("goalContribution")),
|
|
778
|
+
text2("contribution", link.contribution?.available === true ? link.contribution.reason : t("goalContributionUnavailable"))
|
|
779
|
+
)),
|
|
780
|
+
!(detail.links || []).length && !works.length && h4("p", { className: "meta" }, t("goalNoLinks")),
|
|
781
|
+
(detail.discussions || []).length > 0 && h4(
|
|
782
|
+
"div",
|
|
783
|
+
null,
|
|
784
|
+
h4("h4", null, `${t("goalHistory")} · ${detail.discussions.length}/${detail.discussionsTotal ?? detail.discussions.length}`),
|
|
785
|
+
detail.discussions.map((item) => h4(
|
|
786
|
+
"div",
|
|
787
|
+
{ className: "source", key: item.id },
|
|
788
|
+
h4("p", { className: "meta" }, `${item.requestId} · ${t("goalAdmission")}: ${label("admission", item.admission?.state ?? "unknown")}`),
|
|
789
|
+
text2("submitted", item.content?.map((part) => part.text || "").join("") || ""),
|
|
790
|
+
item.admission?.state !== "accepted" && h4("p", null, t("goalUnknown"))
|
|
791
|
+
))
|
|
792
|
+
)
|
|
793
|
+
),
|
|
794
|
+
(allowed || detail.repairAllowed) && h4(
|
|
795
|
+
"details",
|
|
796
|
+
{ className: "goal-discussion" },
|
|
797
|
+
h4("summary", null, t("goalCorrection")),
|
|
798
|
+
h4("p", { className: "meta" }, t(allowed ? "goalDiscussHelp" : "goalRepairHelp")),
|
|
799
|
+
!canDiscuss && h4("p", { className: "meta" }, t("goalMainRequired")),
|
|
800
|
+
staleDraft && h4("p", { role: "alert" }, t("goalDraftChanged")),
|
|
801
|
+
error && h4("p", { role: "alert" }, error),
|
|
802
|
+
notice && h4("p", { role: "status" }, notice),
|
|
803
|
+
h4("label", null, t("goalDiscussLabel"), h4("textarea", {
|
|
804
|
+
value: draft,
|
|
805
|
+
maxLength: 24e3,
|
|
806
|
+
disabled: busy,
|
|
807
|
+
onChange: (event) => {
|
|
808
|
+
setDraft(event.target.value);
|
|
809
|
+
setDraftVersion(version(goal));
|
|
810
|
+
}
|
|
811
|
+
})),
|
|
812
|
+
h4(
|
|
813
|
+
"button",
|
|
814
|
+
{ type: "button", onClick: () => {
|
|
815
|
+
void discuss();
|
|
816
|
+
}, disabled: busy || staleDraft || !draft.trim() || !canDiscuss },
|
|
817
|
+
t(allowed ? "goalDiscuss" : "goalRepair")
|
|
818
|
+
)
|
|
819
|
+
)
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
function GoalDock({ useGoals, onGoalDiscuss, onGoalStopWork, onGoalResumeWork, onGoalRefresh, onGoalPage, t }) {
|
|
823
|
+
const state = useGoals((value) => value);
|
|
824
|
+
const [expanded, setExpanded] = (0, import_react4.useState)(false), [busy, setBusy] = (0, import_react4.useState)(false);
|
|
825
|
+
const [error, setError] = (0, import_react4.useState)(null), [notice, setNotice] = (0, import_react4.useState)(null);
|
|
826
|
+
const pending = (0, import_react4.useRef)(false), retryIds = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
827
|
+
async function controlWork(action, work) {
|
|
828
|
+
const control = action === "resume" ? onGoalResumeWork : onGoalStopWork;
|
|
829
|
+
if (pending.current || !control) return;
|
|
830
|
+
const request = { workId: work.id, nativeGoalId: work.nativeGoalId, nativeRevision: work.nativeRevision };
|
|
831
|
+
const key = JSON.stringify({ action, ...request });
|
|
832
|
+
if (!retryIds.current.has(key)) retryIds.current.set(key, globalThis.crypto.randomUUID());
|
|
833
|
+
request.requestId = retryIds.current.get(key);
|
|
834
|
+
pending.current = true;
|
|
835
|
+
setBusy(true);
|
|
836
|
+
setError(null);
|
|
837
|
+
setNotice(null);
|
|
838
|
+
try {
|
|
839
|
+
const result = await control(request);
|
|
840
|
+
if (result?.state === "applied") {
|
|
841
|
+
retryIds.current.delete(key);
|
|
842
|
+
setNotice(t(action === "resume" ? "goalWorkResumed" : "goalWorkStopped"));
|
|
843
|
+
} else setNotice(t("goalWorkUnknown"));
|
|
844
|
+
} catch (failure) {
|
|
845
|
+
if (failure.remote) retryIds.current.delete(key);
|
|
846
|
+
setError(`${failure.message || t("failed")}${failure.remote ? "" : ` ${t("goalWorkUnknown")}`}`);
|
|
847
|
+
await onGoalRefresh();
|
|
848
|
+
} finally {
|
|
849
|
+
pending.current = false;
|
|
850
|
+
setBusy(false);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
const works = [...new Map([...state.items.flatMap((item) => item.works || []), ...state.works || []].map((work) => [work.id, work])).values()];
|
|
854
|
+
const belongs = (work, id) => work.relation?.state === "current" && work.relation.goalId === id;
|
|
855
|
+
const others = works.filter((work) => !state.items.some((item) => belongs(work, item.goal.id)));
|
|
856
|
+
const button2 = (label, action, disabled = false, props = {}) => h4("button", {
|
|
857
|
+
type: "button",
|
|
858
|
+
disabled,
|
|
859
|
+
onClick: () => {
|
|
860
|
+
void action();
|
|
861
|
+
},
|
|
862
|
+
...props
|
|
863
|
+
}, label);
|
|
864
|
+
return h4(
|
|
865
|
+
"section",
|
|
866
|
+
{ className: "arcfold-methods arcfold-goals", "aria-label": t("goalTitle") },
|
|
867
|
+
h4(
|
|
868
|
+
"header",
|
|
869
|
+
null,
|
|
870
|
+
h4("strong", null, `${t("goalTitle")} · ${state.total}`),
|
|
871
|
+
button2(expanded ? t("goalHide") : t("goalOpen"), () => setExpanded(!expanded), false, { "aria-expanded": expanded }),
|
|
872
|
+
button2(t("refresh"), onGoalRefresh)
|
|
873
|
+
),
|
|
874
|
+
state.loading && h4("p", { role: "status" }, t("goalLoading")),
|
|
875
|
+
state.error && h4("p", { role: "alert" }, state.error),
|
|
876
|
+
// Collapsing the panel preserves unsent drafts and each card's request identity.
|
|
877
|
+
h4(
|
|
878
|
+
"div",
|
|
879
|
+
{ className: "detail", hidden: !expanded },
|
|
880
|
+
h4("p", { className: "meta" }, t("goalBoundary")),
|
|
881
|
+
error && h4("p", { role: "alert" }, error),
|
|
882
|
+
notice && h4("p", { role: "status" }, notice),
|
|
883
|
+
h4("div", { className: "goal-cards" }, state.items.map((item) => h4(GoalCard, {
|
|
884
|
+
key: item.goal.id,
|
|
885
|
+
detail: item,
|
|
886
|
+
works: works.filter((work) => belongs(work, item.goal.id)),
|
|
887
|
+
canDiscuss: state.canDiscuss === true && item.browse?.related !== false,
|
|
888
|
+
onGoalDiscuss,
|
|
889
|
+
onGoalRefresh,
|
|
890
|
+
onAction: controlWork,
|
|
891
|
+
workBusy: busy,
|
|
892
|
+
t
|
|
893
|
+
}))),
|
|
894
|
+
!state.loading && !state.error && !state.items.length && h4("p", null, t("goalEmpty")),
|
|
895
|
+
others.length > 0 && h4(
|
|
896
|
+
"details",
|
|
897
|
+
{ className: "goal-other-work" },
|
|
898
|
+
h4("summary", null, `${t("goalOtherWork")} · ${others.length}`),
|
|
899
|
+
h4(GoalWorkList, { works: others, onAction: controlWork, busy, t })
|
|
900
|
+
),
|
|
901
|
+
(state.offset > 0 || state.nextOffset != null) && h4(
|
|
902
|
+
"nav",
|
|
903
|
+
null,
|
|
904
|
+
button2(t("previous"), () => onGoalPage(Math.max(0, state.offset - 20)), state.offset === 0),
|
|
905
|
+
button2(t("next"), () => onGoalPage(state.nextOffset), state.nextOffset == null)
|
|
906
|
+
)
|
|
907
|
+
)
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/UnderstandingDock.mjs
|
|
912
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
913
|
+
var h5 = import_react5.default.createElement;
|
|
914
|
+
var literal = (value) => typeof value === "string" ? value : JSON.stringify(value ?? "");
|
|
915
|
+
function UnderstandingDock({
|
|
916
|
+
useUnderstandings,
|
|
917
|
+
onUnderstandingDetails,
|
|
918
|
+
onUnderstandingDecide,
|
|
919
|
+
onUnderstandingCorrect,
|
|
920
|
+
onUnderstandingRefresh,
|
|
921
|
+
onUnderstandingPage,
|
|
922
|
+
t
|
|
923
|
+
}) {
|
|
924
|
+
const state = useUnderstandings((value) => value);
|
|
925
|
+
const [expanded, expand] = (0, import_react5.useState)(false), [detail, setDetail] = (0, import_react5.useState)(null), [edit, setEdit] = (0, import_react5.useState)(null);
|
|
926
|
+
const [busy, setBusy] = (0, import_react5.useState)(false), [loading, setLoading] = (0, import_react5.useState)(false), [notice, setNotice] = (0, import_react5.useState)(""), [error, setError] = (0, import_react5.useState)("");
|
|
927
|
+
const generation = (0, import_react5.useRef)(0), pending = (0, import_react5.useRef)(false), identities = (0, import_react5.useRef)(/* @__PURE__ */ new Map()), mounted = (0, import_react5.useRef)(true);
|
|
928
|
+
(0, import_react5.useEffect)(() => {
|
|
929
|
+
mounted.current = true;
|
|
930
|
+
return () => {
|
|
931
|
+
mounted.current = false;
|
|
932
|
+
generation.current++;
|
|
933
|
+
};
|
|
934
|
+
}, []);
|
|
935
|
+
(0, import_react5.useEffect)(() => {
|
|
936
|
+
generation.current++;
|
|
937
|
+
setDetail(null);
|
|
938
|
+
setEdit(null);
|
|
939
|
+
setLoading(false);
|
|
940
|
+
setError("");
|
|
941
|
+
setNotice("");
|
|
942
|
+
}, [state.readEpoch]);
|
|
943
|
+
async function show(id, retainNotice = false, retainedEdit = null) {
|
|
944
|
+
const current = ++generation.current;
|
|
945
|
+
setLoading(true);
|
|
946
|
+
setEdit(null);
|
|
947
|
+
setDetail(null);
|
|
948
|
+
if (!retainNotice) {
|
|
949
|
+
setError("");
|
|
950
|
+
setNotice("");
|
|
951
|
+
}
|
|
952
|
+
try {
|
|
953
|
+
const value = await onUnderstandingDetails(id);
|
|
954
|
+
if (mounted.current && current === generation.current) {
|
|
955
|
+
setDetail(value);
|
|
956
|
+
if (retainedEdit?.id === value.understanding.id && retainedEdit.revision === value.understanding.revision) setEdit(retainedEdit);
|
|
957
|
+
}
|
|
958
|
+
} catch (failure) {
|
|
959
|
+
if (mounted.current && current === generation.current) setError(failure.message || t("failed"));
|
|
960
|
+
} finally {
|
|
961
|
+
if (mounted.current && current === generation.current) setLoading(false);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
async function refresh() {
|
|
965
|
+
const current = generation.current, id = detail?.understanding.id;
|
|
966
|
+
try {
|
|
967
|
+
await onUnderstandingRefresh();
|
|
968
|
+
if (mounted.current && current === generation.current && id) await show(id);
|
|
969
|
+
} catch (failure) {
|
|
970
|
+
if (mounted.current && current === generation.current) setError(failure.message || t("failed"));
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
async function act(action, source) {
|
|
974
|
+
if (pending.current || loading || !detail || detail.browse?.related === false) return;
|
|
975
|
+
const current = detail.understanding, selectedGeneration = generation.current;
|
|
976
|
+
if (action === "correct" && (!edit || edit.id !== current.id || edit.revision !== current.revision || !edit.text.trim())) return;
|
|
977
|
+
const request = {
|
|
978
|
+
understandingId: current.id,
|
|
979
|
+
expectedRevision: current.revision,
|
|
980
|
+
...action === "correct" ? { text: edit.text } : { action },
|
|
981
|
+
...source ? { sourceRef: { id: source.id, revision: source.revision, expectedControlRevision: source.expectedControlRevision } } : {}
|
|
982
|
+
};
|
|
983
|
+
const key = JSON.stringify(request);
|
|
984
|
+
if (!identities.current.has(key)) identities.current.set(key, globalThis.crypto.randomUUID());
|
|
985
|
+
request.requestId = identities.current.get(key);
|
|
986
|
+
pending.current = true;
|
|
987
|
+
setBusy(true);
|
|
988
|
+
setNotice("");
|
|
989
|
+
setError("");
|
|
990
|
+
try {
|
|
991
|
+
const value = await (action === "correct" ? onUnderstandingCorrect(request) : onUnderstandingDecide(request));
|
|
992
|
+
if (!mounted.current || selectedGeneration !== generation.current) return;
|
|
993
|
+
const command = value.command;
|
|
994
|
+
setNotice(t(command?.result?.state === "applied" ? "understandingApplied" : command?.result?.state === "failed" ? "understandingFailed" : action !== "correct" ? "understandingSaved" : command?.admission?.state === "accepted" ? "understandingPending" : "understandingUnknown"));
|
|
995
|
+
await onUnderstandingRefresh();
|
|
996
|
+
if (mounted.current && selectedGeneration === generation.current)
|
|
997
|
+
await show(current.id, true, action === "correct" && !command?.result && command?.admission?.state !== "accepted" ? edit : null);
|
|
998
|
+
} catch (failure) {
|
|
999
|
+
if (mounted.current && selectedGeneration === generation.current) {
|
|
1000
|
+
if (failure.code === "revision_conflict") {
|
|
1001
|
+
setError(failure.message || t("failed"));
|
|
1002
|
+
await show(current.id, true);
|
|
1003
|
+
} else setError((failure.message || t("failed")) + " " + t("understandingUnknown"));
|
|
1004
|
+
}
|
|
1005
|
+
} finally {
|
|
1006
|
+
pending.current = false;
|
|
1007
|
+
if (mounted.current) setBusy(false);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const button2 = (label, action, disabled = false, props = {}) => h5("button", {
|
|
1011
|
+
type: "button",
|
|
1012
|
+
disabled,
|
|
1013
|
+
onClick: () => {
|
|
1014
|
+
void action();
|
|
1015
|
+
},
|
|
1016
|
+
...props
|
|
1017
|
+
}, t(label));
|
|
1018
|
+
const body = (value) => h5("div", { className: "text" }, literal(value));
|
|
1019
|
+
const item = detail?.understanding;
|
|
1020
|
+
const claim = (value) => presentationLabel("claim", value, t);
|
|
1021
|
+
return h5(
|
|
1022
|
+
"section",
|
|
1023
|
+
{ className: "arcfold-methods arcfold-understandings", "aria-label": t("understandingTitle") },
|
|
1024
|
+
h5(
|
|
1025
|
+
"header",
|
|
1026
|
+
null,
|
|
1027
|
+
h5("strong", null, `${t("understandingTitle")} · ${state.total}`),
|
|
1028
|
+
button2(expanded ? "understandingHide" : "understandingOpen", () => expand(!expanded), false, { "aria-expanded": expanded }),
|
|
1029
|
+
button2("refresh", refresh, busy || loading)
|
|
1030
|
+
),
|
|
1031
|
+
expanded && h5(
|
|
1032
|
+
"div",
|
|
1033
|
+
{ className: "detail" },
|
|
1034
|
+
h5("p", { className: "meta" }, t("understandingBoundary")),
|
|
1035
|
+
(state.loading || loading) && h5("p", { role: "status" }, t("understandingLoading")),
|
|
1036
|
+
(error || state.error) && h5("p", { role: "alert" }, error || state.error),
|
|
1037
|
+
notice && h5("p", { role: "status" }, notice),
|
|
1038
|
+
h5("ul", null, state.items.map((row) => h5(
|
|
1039
|
+
"li",
|
|
1040
|
+
{ key: row.understanding.id },
|
|
1041
|
+
body(row.understanding.content),
|
|
1042
|
+
h5("p", { className: "meta" }, claim(row.understanding.claimType)),
|
|
1043
|
+
button2("understandingOpen", () => show(row.understanding.id), busy || loading)
|
|
1044
|
+
))),
|
|
1045
|
+
!state.loading && !state.error && !state.items.length && h5("p", null, t("understandingEmpty")),
|
|
1046
|
+
h5(
|
|
1047
|
+
"nav",
|
|
1048
|
+
null,
|
|
1049
|
+
button2("previous", () => onUnderstandingPage(Math.max(0, state.offset - 20)), busy || loading || !state.offset),
|
|
1050
|
+
button2("next", () => onUnderstandingPage(state.nextOffset), busy || loading || state.nextOffset == null)
|
|
1051
|
+
),
|
|
1052
|
+
item && h5(
|
|
1053
|
+
"article",
|
|
1054
|
+
{ "aria-label": t("understandingTitle") + " " + item.id },
|
|
1055
|
+
h5("h3", null, claim(item.claimType)),
|
|
1056
|
+
body(item.content),
|
|
1057
|
+
detail.browse?.related === false && h5("p", { className: "meta" }, t("browseReturnToOrigin")),
|
|
1058
|
+
h5("p", { className: "meta" }, `${t("revision")} ${item.revision} · ${t("understandingVersion")} ${item.version ?? "—"} · ${presentationLabel("understanding", item.status, t)} · ${t(detail.availability.allowed ? "understandingAvailable" : "understandingUnavailable")}`),
|
|
1059
|
+
!detail.availability.allowed && h5("p", null, detail.availability.message),
|
|
1060
|
+
h5("p", { className: "meta" }, t("understandingWithdrawalHelp")),
|
|
1061
|
+
h5(
|
|
1062
|
+
"div",
|
|
1063
|
+
{ className: "actions" },
|
|
1064
|
+
button2("understandingStop", () => act("stop-understanding"), busy || loading || !detail.controls?.stopAllowed),
|
|
1065
|
+
button2("understandingCorrect", () => setEdit({ id: item.id, revision: item.revision, text: item.content }), busy || loading || !detail.controls?.correctionAllowed)
|
|
1066
|
+
),
|
|
1067
|
+
edit && h5(
|
|
1068
|
+
"div",
|
|
1069
|
+
{ className: "understanding-correction" },
|
|
1070
|
+
h5("p", null, t("understandingCorrectionHelp")),
|
|
1071
|
+
h5("label", null, t("understandingReplacement"), h5("textarea", {
|
|
1072
|
+
value: edit.text,
|
|
1073
|
+
maxLength: 16e3,
|
|
1074
|
+
disabled: busy || loading,
|
|
1075
|
+
onChange: (event) => setEdit({ ...edit, text: event.target.value })
|
|
1076
|
+
})),
|
|
1077
|
+
button2("understandingSubmit", () => act("correct"), busy || loading || !edit.text.trim()),
|
|
1078
|
+
button2("understandingCancel", () => setEdit(null), busy)
|
|
1079
|
+
),
|
|
1080
|
+
h5("h4", null, t("understandingSource")),
|
|
1081
|
+
(detail.sources || []).map((source) => h5(
|
|
1082
|
+
"div",
|
|
1083
|
+
{ className: "source", key: source.id },
|
|
1084
|
+
h5("p", { className: "meta" }, `${source.id} · ${source.author && source.author !== source.role ? source.author : presentationLabel("role", source.role, t)} · ${source.occurredAt || ""} · ${t("revision")} ${source.revision ?? "—"} · ${t(source.available ? "understandingAvailable" : "understandingUnavailable")}`),
|
|
1085
|
+
body(source.missing ? t("understandingMissing") : source.content),
|
|
1086
|
+
h5("details", null, h5("summary", null, t("understandingProvenance")), body(source.provenance), source.control && body(source.control)),
|
|
1087
|
+
button2("understandingWithdraw", () => act("withdraw-source", source), busy || loading || detail.controls?.sourceControlAllowed === false || !source.available || !Number.isSafeInteger(source.revision))
|
|
1088
|
+
)),
|
|
1089
|
+
h5("h4", null, t("understandingSuccessors")),
|
|
1090
|
+
(detail.successors || []).map((next) => h5(
|
|
1091
|
+
"div",
|
|
1092
|
+
{ key: next.id },
|
|
1093
|
+
h5("span", null, `${next.id} · ${t("revision")} ${next.revision} `),
|
|
1094
|
+
button2("understandingOpen", () => show(next.id), busy || loading)
|
|
1095
|
+
)),
|
|
1096
|
+
h5(
|
|
1097
|
+
"details",
|
|
1098
|
+
null,
|
|
1099
|
+
h5("summary", null, `${t("understandingHistory")} · ${detail.history?.length || 0}/${detail.historyTotal || 0}`),
|
|
1100
|
+
(detail.history || []).map((row) => h5(
|
|
1101
|
+
"div",
|
|
1102
|
+
{ className: "source", key: row.understanding.id },
|
|
1103
|
+
h5("p", { className: "meta" }, `${row.understanding.id} · ${t("revision")} ${row.understanding.revision} · ${t("understandingVersion")} ${row.understanding.version ?? "—"}`),
|
|
1104
|
+
body(row.understanding.content)
|
|
1105
|
+
))
|
|
1106
|
+
),
|
|
1107
|
+
h5("h4", null, `${t("understandingCommands")} · ${detail.commands?.length || 0}/${detail.commandsTotal || 0}`),
|
|
1108
|
+
(detail.commands || []).map((command) => h5(
|
|
1109
|
+
"div",
|
|
1110
|
+
{ className: "source", key: command.id },
|
|
1111
|
+
h5("p", null, `${presentationLabel("understandingAction", command.action, t)} · ${command.requestId} · ${t(command.result?.state === "applied" ? "understandingApplied" : command.result?.state === "failed" ? "understandingFailed" : "understandingAwaiting")}`),
|
|
1112
|
+
command.admission && h5("p", null, t("understandingAdmission") + ": " + presentationLabel("admission", command.admission.state, t)),
|
|
1113
|
+
command.request?.text !== void 0 && body(command.request.text),
|
|
1114
|
+
command.result?.error && body(command.result.error.message),
|
|
1115
|
+
command.result?.changes?.length > 0 && h5("details", null, h5("summary", null, t("understandingEffects")), body(command.result.changes)),
|
|
1116
|
+
command.result?.goalImpacts?.length > 0 && h5("details", null, h5("summary", null, t("understandingGoalEffects")), body(command.result.goalImpacts))
|
|
1117
|
+
))
|
|
1118
|
+
)
|
|
1119
|
+
)
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// src/import-locales.mjs
|
|
1124
|
+
var importsZh = {
|
|
1125
|
+
importSnapshot: "以下为当时保存的导入预览,资格、来源状态和试用次数不代表当前状态。",
|
|
1126
|
+
importWithdrawnNotice: "这批导入已撤回,不再作为当前依据。以下保留当时预览和原文。",
|
|
1127
|
+
importCountsAtPreview: "当时预览统计",
|
|
1128
|
+
importEligibilityAtPreview: "当时预览资格",
|
|
1129
|
+
importSourceAtPreview: "当时来源状态",
|
|
1130
|
+
importUsageAtPreview: "导入时试用次数",
|
|
1131
|
+
importContent: "读取完整内容",
|
|
1132
|
+
importOriginalRecord: "查看保留的旧记录",
|
|
1133
|
+
importAppliesTo: "适用情况",
|
|
1134
|
+
importDoesNotApplyTo: "不适用情况",
|
|
1135
|
+
importTopics: "原主题范围",
|
|
1136
|
+
importAttributeUnavailable: "这项内容不完整或超出可采用范围,请查看保留的旧记录。",
|
|
1137
|
+
importClaimInference: "AI 推断",
|
|
1138
|
+
importClaimLiteral: "用户明确陈述",
|
|
1139
|
+
importClaimQuestion: "待确认的问题",
|
|
1140
|
+
importReason_user_authored_range_unproven: "无法确认这段引文是用户自己的陈述,而非他引用的资料。",
|
|
1141
|
+
"importReason_old_state_not_current": "旧记录已停止使用,或无法确认它仍然有效。",
|
|
1142
|
+
"importReason_source_closure_unproven": "无法完整核对这条记录所依据的来源。",
|
|
1143
|
+
"importReason_source_reference_unproven": "引用的来源或版本无法核对。",
|
|
1144
|
+
"importReason_source_range_mismatch": "引文与原文中的位置不一致。",
|
|
1145
|
+
"importReason_old_tasks_history_only": "旧事项保留为历史,不会恢复执行。",
|
|
1146
|
+
"importReason_source_cycle": "依据之间存在循环引用,需要核对。",
|
|
1147
|
+
"importReason_old_record_not_current": "旧记录的状态、版本或保存时间范围无法确认。",
|
|
1148
|
+
"importReason_source_unavailable": "所依赖的来源已撤回、缺失或不可用。",
|
|
1149
|
+
"importReason_source_revision_unproven": "缺少原来源版本依据,不能按最新版本补齐。",
|
|
1150
|
+
"importReason_dependency_version_unproven": "依赖的理解或方法已经变化,或其版本无法核对。",
|
|
1151
|
+
"importReason_method_contract_unproven": "方法内容、适用条件或状态不完整。",
|
|
1152
|
+
"importReason_method_trial_basis_unknown": "缺少原试用次数依据,不会重新按零次开始。",
|
|
1153
|
+
"importReason_method_trial_history_mismatch": "原试用次数与采用记录不一致。",
|
|
1154
|
+
"importReason_method_active_authority_unproven": "无法确认此前继续采用这条方法的明确决定。",
|
|
1155
|
+
"importReason_historical_control_not_migrated": "保留目标或机会的历史,暂不迁入控制或执行状态。",
|
|
1156
|
+
"importReason_scope_not_mapped": "这条记录只适用于旧交流,尚未关联当前会话。",
|
|
1157
|
+
"importReason_dependency_not_eligible": "所依赖的其他记录在本批预览中不可采用。",
|
|
1158
|
+
"importReason_method_already_present": "方法已存在,沿用当前状态和已用次数,不会重新试用。",
|
|
1159
|
+
"importReason_method_existing_conflict": "与已有方法的内容或范围不一致,不会覆盖现有决定。",
|
|
1160
|
+
"importReason_method_batch_conflict": "这批旧记录中有重复方法,需先核对;不会重复增加试用次数。",
|
|
1161
|
+
"importReason_dependency_scope_mismatch": "依赖材料的适用范围更窄,不能扩大到其他交流。",
|
|
1162
|
+
"importReason_scope_unsupported": "旧适用范围无法准确迁入,需要核对后再使用。",
|
|
1163
|
+
"importRole_user": "用户原话",
|
|
1164
|
+
"importRole_assistant": "助手回答",
|
|
1165
|
+
"importRole_tool": "工具结果",
|
|
1166
|
+
"importRole_system": "系统信息",
|
|
1167
|
+
"importRole_material": "资料",
|
|
1168
|
+
"importRole_program": "程序产生的信息",
|
|
1169
|
+
"importRoleUnknown": "来源角色待核对",
|
|
1170
|
+
"importReasonUnrecognized": "这条记录还需要进一步核对。",
|
|
1171
|
+
"importExistingRecord": "对应已有方法记录",
|
|
1172
|
+
importWorking: "正在处理…",
|
|
1173
|
+
importTitle: "旧历史导入",
|
|
1174
|
+
importOpen: "查看导入",
|
|
1175
|
+
importHide: "收起导入",
|
|
1176
|
+
importEmpty: "还没有导入批次。",
|
|
1177
|
+
importBoundary: "先选择完整导出文件并查看预览。只有确认后,列出的理解与方法才会按其适用范围进入后续交流;不会恢复旧任务或授予执行权限。",
|
|
1178
|
+
importFile: "完整导出文件(最多 768 MiB)",
|
|
1179
|
+
importPrepare: "上传并预览",
|
|
1180
|
+
importPreparing: "正在上传或核对完整文件…",
|
|
1181
|
+
importStorage: "上传的文件会保存在本地附件中。未确认的预览不会影响回答;撤回后保留原文与操作记录。",
|
|
1182
|
+
importLoading: "正在读取导入记录…",
|
|
1183
|
+
importPreview: "查看这一批",
|
|
1184
|
+
importRecords: "理解、方法与历史",
|
|
1185
|
+
importSources: "原始来源",
|
|
1186
|
+
importText: "读取完整原文",
|
|
1187
|
+
importTextPage: "完整内容(分段)",
|
|
1188
|
+
importExcerpt: "以下是节选,可分段查看完整原文。",
|
|
1189
|
+
importReady: "待确认",
|
|
1190
|
+
importPreparingStatus: "准备中",
|
|
1191
|
+
importFailed: "准备失败",
|
|
1192
|
+
importConfirmed: "已导入",
|
|
1193
|
+
importWithdrawn: "已撤回",
|
|
1194
|
+
importScopeTitle: "旧交流的适用范围",
|
|
1195
|
+
importKeepScope: "保留原范围;旧会话内容仅作为历史",
|
|
1196
|
+
importMapScope: "将选中的一段旧交流关联到当前会话",
|
|
1197
|
+
importScopeHelp: "全局内容仍按原全局范围处理。最多关联一段旧交流;其他旧会话保留为历史,不影响当前交流。",
|
|
1198
|
+
importScopeChanged: "范围已修改。请先重新生成预览,再确认导入。",
|
|
1199
|
+
importReprepare: "按新范围生成预览",
|
|
1200
|
+
importNoReceipt: "若要修改范围,请重新选择原导出文件并预览。",
|
|
1201
|
+
importMapped: "关联到当前会话",
|
|
1202
|
+
importHistoricalScope: "保留在旧会话",
|
|
1203
|
+
importGlobal: "沿用原全局范围",
|
|
1204
|
+
importConfirm: "确认导入这一批",
|
|
1205
|
+
importConfirmHelp: "这一次确认包含本批列出的指导内容和方法试用次数。原文与推断仍有区别,导入不表示内容已经证实。",
|
|
1206
|
+
importWithdraw: "撤回这一批的采用",
|
|
1207
|
+
importWithdrawHelp: "撤回后将停止采用依赖本批来源的理解、方法与目标依据;独立的新经历仍保留,目标的暂停或关闭状态不变,已执行的工作不会撤销。",
|
|
1208
|
+
importOtherSession: "这是另一段会话准备的预览,请回到原会话确认。",
|
|
1209
|
+
importUnknown: "结果尚未确认,没有自动重发。请刷新批次查看实际状态。",
|
|
1210
|
+
importSaved: "导入状态已更新。",
|
|
1211
|
+
importEnabled: "可采用",
|
|
1212
|
+
importTrial: "有限试用",
|
|
1213
|
+
importPaused: "保持暂停",
|
|
1214
|
+
importHistory: "仅保留历史",
|
|
1215
|
+
importNeedsReview: "需要核对",
|
|
1216
|
+
importCountSources: "来源",
|
|
1217
|
+
importCountRecords: "记录",
|
|
1218
|
+
importRemaining: "剩余试用次数",
|
|
1219
|
+
importUsed: "已用",
|
|
1220
|
+
importMaximum: "上限",
|
|
1221
|
+
importUnavailable: "来源不可用",
|
|
1222
|
+
importAvailable: "来源可用",
|
|
1223
|
+
importRevision: "预览版本",
|
|
1224
|
+
importFileHash: "文件指纹",
|
|
1225
|
+
importReadMore: "下一段",
|
|
1226
|
+
importReadPrevious: "上一段",
|
|
1227
|
+
importReason: "资格核对结果",
|
|
1228
|
+
importFileTooLarge: "请选择不超过 768 MiB 的非空导出文件。"
|
|
1229
|
+
};
|
|
1230
|
+
var importsEn = {
|
|
1231
|
+
importSnapshot: "This is the saved import preview. Eligibility, source availability and trial counts describe that preview, not the current state.",
|
|
1232
|
+
importWithdrawnNotice: "This batch has been withdrawn and is no longer used as evidence. Its original preview and source text remain below.",
|
|
1233
|
+
importCountsAtPreview: "Preview counts",
|
|
1234
|
+
importEligibilityAtPreview: "Eligibility at preview",
|
|
1235
|
+
importSourceAtPreview: "Source state at preview",
|
|
1236
|
+
importUsageAtPreview: "Trial counts at import",
|
|
1237
|
+
importContent: "Read full content",
|
|
1238
|
+
importOriginalRecord: "View retained earlier record",
|
|
1239
|
+
importAppliesTo: "Applies to",
|
|
1240
|
+
importDoesNotApplyTo: "Does not apply to",
|
|
1241
|
+
importTopics: "Original topics",
|
|
1242
|
+
importAttributeUnavailable: "This field is incomplete or outside the eligible bounds. View the retained earlier record.",
|
|
1243
|
+
importClaimInference: "AI inference",
|
|
1244
|
+
importClaimLiteral: "Explicit user statement",
|
|
1245
|
+
importClaimQuestion: "Open question",
|
|
1246
|
+
importReason_user_authored_range_unproven: "The quotation is not proven to be the user’s own statement rather than cited material.",
|
|
1247
|
+
"importReason_old_state_not_current": "The earlier state is stopped or unproven.",
|
|
1248
|
+
"importReason_source_closure_unproven": "The complete supporting evidence is unproven.",
|
|
1249
|
+
"importReason_source_reference_unproven": "The cited source or version is unproven.",
|
|
1250
|
+
"importReason_source_range_mismatch": "The quotation does not match its recorded position.",
|
|
1251
|
+
"importReason_old_tasks_history_only": "Earlier tasks remain historical and do not resume execution.",
|
|
1252
|
+
"importReason_source_cycle": "The evidence contains a circular reference.",
|
|
1253
|
+
"importReason_old_record_not_current": "The earlier state, version or snapshot boundary is unproven.",
|
|
1254
|
+
"importReason_source_unavailable": "A supporting source is withdrawn, missing or unavailable.",
|
|
1255
|
+
"importReason_source_revision_unproven": "The original source version is unproven and cannot be replaced with the latest version.",
|
|
1256
|
+
"importReason_dependency_version_unproven": "A supporting record changed or its version is unproven.",
|
|
1257
|
+
"importReason_method_contract_unproven": "The method content, applicability or state is incomplete.",
|
|
1258
|
+
"importReason_method_trial_basis_unknown": "Trial history is missing; usage will not restart from zero.",
|
|
1259
|
+
"importReason_method_trial_history_mismatch": "Trial counts and adoption records disagree.",
|
|
1260
|
+
"importReason_method_active_authority_unproven": "The earlier explicit continuation decision is unproven.",
|
|
1261
|
+
"importReason_historical_control_not_migrated": "Goal or opportunity history is retained without restoring controls or execution.",
|
|
1262
|
+
"importReason_scope_not_mapped": "The earlier conversation is not connected here.",
|
|
1263
|
+
"importReason_dependency_not_eligible": "A supporting record is not eligible in this preview.",
|
|
1264
|
+
"importReason_method_already_present": "The method already exists; its current state and usage are retained.",
|
|
1265
|
+
"importReason_method_existing_conflict": "It conflicts with an existing method or scope and will not overwrite the current decision.",
|
|
1266
|
+
"importReason_method_batch_conflict": "These old records contain duplicate methods; review them first. Trial uses will not be duplicated.",
|
|
1267
|
+
"importReason_dependency_scope_mismatch": "A supporting record has a narrower scope and cannot be used in other conversations.",
|
|
1268
|
+
"importReason_scope_unsupported": "The earlier scope cannot be transferred accurately and needs review.",
|
|
1269
|
+
"importRole_user": "User",
|
|
1270
|
+
"importRole_assistant": "Assistant",
|
|
1271
|
+
"importRole_tool": "Tool result",
|
|
1272
|
+
"importRole_system": "System",
|
|
1273
|
+
"importRole_material": "Material",
|
|
1274
|
+
"importRole_program": "Program-generated information",
|
|
1275
|
+
"importRoleUnknown": "Source role unconfirmed",
|
|
1276
|
+
"importReasonUnrecognized": "This record needs further review.",
|
|
1277
|
+
"importExistingRecord": "View existing record reference",
|
|
1278
|
+
importWorking: "Processing…",
|
|
1279
|
+
importTitle: "Import history",
|
|
1280
|
+
importOpen: "View imports",
|
|
1281
|
+
importHide: "Hide imports",
|
|
1282
|
+
importEmpty: "No import batches yet.",
|
|
1283
|
+
importBoundary: "Choose a complete export and review its preview. Understanding and methods become eligible only after confirmation, within the displayed scope. Old tasks and execution permissions are not restored.",
|
|
1284
|
+
importFile: "Complete export (up to 768 MiB)",
|
|
1285
|
+
importPrepare: "Upload and preview",
|
|
1286
|
+
importPreparing: "Uploading or checking the complete file…",
|
|
1287
|
+
importStorage: "Uploaded files remain in local attachments. Unconfirmed previews do not affect answers. Withdrawal retains originals and audit records.",
|
|
1288
|
+
importLoading: "Loading import records…",
|
|
1289
|
+
importPreview: "View this batch",
|
|
1290
|
+
importRecords: "Understanding, methods and history",
|
|
1291
|
+
importSources: "Original sources",
|
|
1292
|
+
importText: "Read full original",
|
|
1293
|
+
importTextPage: "Complete content segments",
|
|
1294
|
+
importExcerpt: "This is an excerpt. The complete original is available in segments.",
|
|
1295
|
+
importReady: "Awaiting confirmation",
|
|
1296
|
+
importPreparingStatus: "Preparing",
|
|
1297
|
+
importFailed: "Preparation failed",
|
|
1298
|
+
importConfirmed: "Imported",
|
|
1299
|
+
importWithdrawn: "Withdrawn",
|
|
1300
|
+
importScopeTitle: "Scope of earlier conversations",
|
|
1301
|
+
importKeepScope: "Keep original scope; earlier conversations remain historical",
|
|
1302
|
+
importMapScope: "Connect the selected earlier conversation to this conversation",
|
|
1303
|
+
importScopeHelp: "Global material retains its global scope. At most one earlier conversation can be connected; all others remain historical.",
|
|
1304
|
+
importScopeChanged: "Scope changed. Generate a new preview before confirming.",
|
|
1305
|
+
importReprepare: "Preview the new scope",
|
|
1306
|
+
importNoReceipt: "To change scope, select the original export file again.",
|
|
1307
|
+
importMapped: "Connected to this conversation",
|
|
1308
|
+
importHistoricalScope: "Earlier conversation only",
|
|
1309
|
+
importGlobal: "Original global scope",
|
|
1310
|
+
importConfirm: "Confirm this batch",
|
|
1311
|
+
importConfirmHelp: "One confirmation covers the listed guidance and method trial counts. Originals and inferences remain distinct; import does not prove their correctness.",
|
|
1312
|
+
importWithdraw: "Withdraw this batch",
|
|
1313
|
+
importWithdrawHelp: "Dependent understanding, methods and goal evidence stop being eligible. Independent new experience remains. Goal pause/close decisions and already executed work are not undone.",
|
|
1314
|
+
importOtherSession: "This preview belongs to another conversation. Confirm it in the original conversation.",
|
|
1315
|
+
importUnknown: "The outcome is unconfirmed. Nothing was resent automatically. Refresh to inspect the batch.",
|
|
1316
|
+
importSaved: "Import status updated.",
|
|
1317
|
+
importEnabled: "Eligible",
|
|
1318
|
+
importTrial: "Limited trial",
|
|
1319
|
+
importPaused: "Remains paused",
|
|
1320
|
+
importHistory: "Historical only",
|
|
1321
|
+
importNeedsReview: "Needs review",
|
|
1322
|
+
importCountSources: "Sources",
|
|
1323
|
+
importCountRecords: "Records",
|
|
1324
|
+
importRemaining: "Trials remaining",
|
|
1325
|
+
importUsed: "Used",
|
|
1326
|
+
importMaximum: "Limit",
|
|
1327
|
+
importUnavailable: "Source unavailable",
|
|
1328
|
+
importAvailable: "Source available",
|
|
1329
|
+
importRevision: "Preview revision",
|
|
1330
|
+
importFileHash: "File fingerprint",
|
|
1331
|
+
importReadMore: "Next segment",
|
|
1332
|
+
importReadPrevious: "Previous segment",
|
|
1333
|
+
importReason: "Eligibility checks",
|
|
1334
|
+
importFileTooLarge: "Choose a nonempty export file up to 768 MiB."
|
|
1335
|
+
};
|
|
1336
|
+
var importReasonKeys = Object.freeze(["user_authored_range_unproven", "old_state_not_current", "source_closure_unproven", "source_reference_unproven", "source_range_mismatch", "old_tasks_history_only", "source_cycle", "old_record_not_current", "source_unavailable", "source_revision_unproven", "dependency_version_unproven", "method_contract_unproven", "method_trial_basis_unknown", "method_trial_history_mismatch", "method_active_authority_unproven", "historical_control_not_migrated", "scope_not_mapped", "dependency_not_eligible", "method_already_present", "method_existing_conflict", "method_batch_conflict", "dependency_scope_mismatch", "scope_unsupported"]);
|
|
1337
|
+
|
|
1338
|
+
// src/ImportDock.mjs
|
|
1339
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
1340
|
+
|
|
1341
|
+
// src/browse-source.mjs
|
|
1342
|
+
function viewScopeFields(viewScope) {
|
|
1343
|
+
if (viewScope !== void 0 && !["all", "current"].includes(viewScope))
|
|
1344
|
+
throw new TypeError("Unknown Arcfold view scope");
|
|
1345
|
+
return viewScope === void 0 ? {} : { viewScope };
|
|
1346
|
+
}
|
|
1347
|
+
function requireViewScope(viewScope) {
|
|
1348
|
+
if (viewScope === void 0) throw new TypeError("A view scope is required");
|
|
1349
|
+
viewScopeFields(viewScope);
|
|
1350
|
+
}
|
|
1351
|
+
function createBrowseScopeSource(sources) {
|
|
1352
|
+
let snapshot = { viewScope: "all", epoch: 0 }, disposed = false;
|
|
1353
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1354
|
+
return {
|
|
1355
|
+
getSnapshot: () => snapshot,
|
|
1356
|
+
subscribe(listener) {
|
|
1357
|
+
if (disposed) return () => {
|
|
1358
|
+
};
|
|
1359
|
+
listeners.add(listener);
|
|
1360
|
+
return () => listeners.delete(listener);
|
|
1361
|
+
},
|
|
1362
|
+
setViewScope(viewScope) {
|
|
1363
|
+
requireViewScope(viewScope);
|
|
1364
|
+
if (disposed || snapshot.viewScope === viewScope) return;
|
|
1365
|
+
for (const source of sources) source.setViewScope?.(viewScope);
|
|
1366
|
+
snapshot = { viewScope, epoch: snapshot.epoch + 1 };
|
|
1367
|
+
for (const listener of listeners) listener();
|
|
1368
|
+
},
|
|
1369
|
+
dispose() {
|
|
1370
|
+
disposed = true;
|
|
1371
|
+
listeners.clear();
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// src/import-source.mjs
|
|
1377
|
+
var IMPORT_MAX_FILE_BYTES = 805306368;
|
|
1378
|
+
function createImportSource({ rpc, fileUpload, sessionId, viewScope, subscribeReset, intervalMs = 5e3 }) {
|
|
1379
|
+
viewScopeFields(viewScope);
|
|
1380
|
+
let snapshot = {
|
|
1381
|
+
loading: true,
|
|
1382
|
+
error: null,
|
|
1383
|
+
items: [],
|
|
1384
|
+
total: 0,
|
|
1385
|
+
offset: 0,
|
|
1386
|
+
nextOffset: null,
|
|
1387
|
+
canImport: viewScope === void 0 ? void 0 : false,
|
|
1388
|
+
readEpoch: 0
|
|
1389
|
+
};
|
|
1390
|
+
let generation = 0, listRead, timer, reset, disposed = false;
|
|
1391
|
+
const listeners = /* @__PURE__ */ new Set(), pending = /* @__PURE__ */ new Set();
|
|
1392
|
+
const stopped = () => Object.assign(new Error("连接已经变化,请重新读取导入记录。"), { code: "import_request_cancelled" });
|
|
1393
|
+
const publish = (value) => {
|
|
1394
|
+
snapshot = { ...snapshot, ...value };
|
|
1395
|
+
for (const listener of listeners) listener();
|
|
1396
|
+
};
|
|
1397
|
+
async function operation(fn) {
|
|
1398
|
+
if (disposed) throw stopped();
|
|
1399
|
+
const controller = new AbortController(), epoch = snapshot.readEpoch;
|
|
1400
|
+
pending.add(controller);
|
|
1401
|
+
try {
|
|
1402
|
+
const result = await fn(controller.signal);
|
|
1403
|
+
if (disposed || controller.signal.aborted || epoch !== snapshot.readEpoch) throw stopped();
|
|
1404
|
+
return result;
|
|
1405
|
+
} finally {
|
|
1406
|
+
pending.delete(controller);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
async function call(endpoint, request, signal) {
|
|
1410
|
+
const scope = ["list", "details"].includes(endpoint) ? viewScopeFields(viewScope) : {};
|
|
1411
|
+
const result = await rpc.call("/api", `arcfoldImports/${endpoint}`, { args: { request: { ...request, sessionId, ...scope } } }, signal);
|
|
1412
|
+
if (result?.ok !== true) throw Object.assign(
|
|
1413
|
+
new Error(result?.error?.message || "导入操作未完成。"),
|
|
1414
|
+
{ code: result?.error?.code || "import_request_failed", remote: true }
|
|
1415
|
+
);
|
|
1416
|
+
return result.value;
|
|
1417
|
+
}
|
|
1418
|
+
function invalidate() {
|
|
1419
|
+
listRead?.abort();
|
|
1420
|
+
generation++;
|
|
1421
|
+
for (const controller of pending) controller.abort();
|
|
1422
|
+
publish({
|
|
1423
|
+
loading: true,
|
|
1424
|
+
error: null,
|
|
1425
|
+
items: [],
|
|
1426
|
+
total: 0,
|
|
1427
|
+
offset: 0,
|
|
1428
|
+
nextOffset: null,
|
|
1429
|
+
canImport: viewScope === void 0 ? void 0 : false,
|
|
1430
|
+
readEpoch: snapshot.readEpoch + 1
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
async function refresh(offset = snapshot.offset) {
|
|
1434
|
+
if (disposed) return;
|
|
1435
|
+
listRead?.abort();
|
|
1436
|
+
listRead = new AbortController();
|
|
1437
|
+
const current = ++generation, signal = listRead.signal;
|
|
1438
|
+
try {
|
|
1439
|
+
const result = await call("list", { offset, limit: 20 }, signal);
|
|
1440
|
+
if (!disposed && !signal.aborted && current === generation) publish({ ...result, offset, loading: false, error: null });
|
|
1441
|
+
} catch (error) {
|
|
1442
|
+
if (!disposed && !signal.aborted && current === generation) publish({ loading: false, error: error.message });
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function stop() {
|
|
1446
|
+
clearInterval(timer);
|
|
1447
|
+
timer = void 0;
|
|
1448
|
+
reset?.();
|
|
1449
|
+
reset = void 0;
|
|
1450
|
+
invalidate();
|
|
1451
|
+
}
|
|
1452
|
+
return {
|
|
1453
|
+
getSnapshot: () => snapshot,
|
|
1454
|
+
subscribe(listener) {
|
|
1455
|
+
if (disposed) return () => {
|
|
1456
|
+
};
|
|
1457
|
+
listeners.add(listener);
|
|
1458
|
+
if (listeners.size === 1) {
|
|
1459
|
+
void refresh();
|
|
1460
|
+
timer = setInterval(() => {
|
|
1461
|
+
void refresh();
|
|
1462
|
+
}, intervalMs);
|
|
1463
|
+
reset = subscribeReset(() => {
|
|
1464
|
+
invalidate();
|
|
1465
|
+
void refresh(0);
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
return () => {
|
|
1469
|
+
listeners.delete(listener);
|
|
1470
|
+
if (!listeners.size) stop();
|
|
1471
|
+
};
|
|
1472
|
+
},
|
|
1473
|
+
refresh,
|
|
1474
|
+
setViewScope(value) {
|
|
1475
|
+
requireViewScope(value);
|
|
1476
|
+
if (disposed || viewScope === value) return;
|
|
1477
|
+
viewScope = value;
|
|
1478
|
+
invalidate();
|
|
1479
|
+
if (listeners.size) void refresh(0);
|
|
1480
|
+
},
|
|
1481
|
+
details: (request) => operation((signal) => call("details", request, signal)),
|
|
1482
|
+
prepare: (request) => operation((signal) => call("prepare", request, signal)),
|
|
1483
|
+
confirm: (request) => operation((signal) => call("confirm", request, signal)),
|
|
1484
|
+
withdraw: (request) => operation((signal) => call("withdraw", request, signal)),
|
|
1485
|
+
upload(file, onProgress) {
|
|
1486
|
+
return operation(async (signal) => {
|
|
1487
|
+
if (!(file instanceof Blob) || !Number.isSafeInteger(file.size) || file.size < 1 || file.size > IMPORT_MAX_FILE_BYTES)
|
|
1488
|
+
throw Object.assign(new Error("请选择不超过 768 MiB 的完整导出文件。"), { code: "invalid_import_file", remote: true });
|
|
1489
|
+
const result = await fileUpload.upload(sessionId, file, file.name || "arcfold-export.json", signal, onProgress);
|
|
1490
|
+
if (result?.ok !== true) throw Object.assign(
|
|
1491
|
+
new Error(result?.error?.message || "文件上传未完成。"),
|
|
1492
|
+
{ code: result?.error?.code || "import_upload_failed", remote: true }
|
|
1493
|
+
);
|
|
1494
|
+
if (typeof result.value?.receiptId !== "string" || result.value.file?.bytes !== file.size)
|
|
1495
|
+
throw new Error("上传回执与所选文件不一致,请查看记录后重新选择。");
|
|
1496
|
+
return result.value;
|
|
1497
|
+
});
|
|
1498
|
+
},
|
|
1499
|
+
dispose() {
|
|
1500
|
+
disposed = true;
|
|
1501
|
+
stop();
|
|
1502
|
+
listeners.clear();
|
|
1503
|
+
}
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
// src/ImportDock.mjs
|
|
1508
|
+
var h6 = import_react6.default.createElement;
|
|
1509
|
+
var statusKeys = { preparing: "importPreparingStatus", ready: "importReady", failed: "importFailed", confirmed: "importConfirmed", withdrawn: "importWithdrawn" };
|
|
1510
|
+
var eligibilityKeys = { enabled: "importEnabled", trial: "importTrial", paused: "importPaused", history: "importHistory", needs_review: "importNeedsReview" };
|
|
1511
|
+
var scopeKeys = { global: "importGlobal", mapped: "importMapped", historical: "importHistoricalScope" };
|
|
1512
|
+
function ImportDock({
|
|
1513
|
+
useImports,
|
|
1514
|
+
importSessionId,
|
|
1515
|
+
onImportUpload,
|
|
1516
|
+
onImportPrepare,
|
|
1517
|
+
onImportDetails,
|
|
1518
|
+
onImportConfirm,
|
|
1519
|
+
onImportWithdraw,
|
|
1520
|
+
onImportRefresh,
|
|
1521
|
+
onImportPage,
|
|
1522
|
+
t
|
|
1523
|
+
}) {
|
|
1524
|
+
const state = useImports((value) => value);
|
|
1525
|
+
const [expanded, expand] = (0, import_react6.useState)(false), [file, setFile] = (0, import_react6.useState)(null), [receipt, setReceipt] = (0, import_react6.useState)(null);
|
|
1526
|
+
const [detail, setDetail] = (0, import_react6.useState)(null), [scope, setScope] = (0, import_react6.useState)(""), [text2, setText] = (0, import_react6.useState)(null);
|
|
1527
|
+
const [busy, setBusy] = (0, import_react6.useState)(false), [loading, setLoading] = (0, import_react6.useState)(false), [progress, setProgress] = (0, import_react6.useState)(null);
|
|
1528
|
+
const [error, setError] = (0, import_react6.useState)(""), [notice, setNotice] = (0, import_react6.useState)("");
|
|
1529
|
+
const mounted = (0, import_react6.useRef)(true), generation = (0, import_react6.useRef)(0), pending = (0, import_react6.useRef)(false), identities = (0, import_react6.useRef)(/* @__PURE__ */ new Map());
|
|
1530
|
+
(0, import_react6.useEffect)(() => {
|
|
1531
|
+
mounted.current = true;
|
|
1532
|
+
return () => {
|
|
1533
|
+
mounted.current = false;
|
|
1534
|
+
generation.current++;
|
|
1535
|
+
};
|
|
1536
|
+
}, []);
|
|
1537
|
+
(0, import_react6.useEffect)(() => {
|
|
1538
|
+
generation.current++;
|
|
1539
|
+
setDetail(null);
|
|
1540
|
+
setText(null);
|
|
1541
|
+
setReceipt(null);
|
|
1542
|
+
setScope("");
|
|
1543
|
+
setLoading(false);
|
|
1544
|
+
setProgress(null);
|
|
1545
|
+
setError("");
|
|
1546
|
+
if (pending.current) setNotice(t("importUnknown"));
|
|
1547
|
+
}, [state.readEpoch]);
|
|
1548
|
+
const current = (value) => mounted.current && value === generation.current;
|
|
1549
|
+
async function show(batchId, view = "records", offset = 0, retainNotice = false) {
|
|
1550
|
+
const version2 = ++generation.current;
|
|
1551
|
+
setLoading(true);
|
|
1552
|
+
setText(null);
|
|
1553
|
+
setDetail(null);
|
|
1554
|
+
if (!retainNotice) {
|
|
1555
|
+
setError("");
|
|
1556
|
+
setNotice("");
|
|
1557
|
+
}
|
|
1558
|
+
try {
|
|
1559
|
+
const result = await onImportDetails({ batchId, view, offset, limit: 20 });
|
|
1560
|
+
if (current(version2)) {
|
|
1561
|
+
setDetail({ ...result, offset });
|
|
1562
|
+
setScope(result.batch.scopeMap[0]?.sourceConversationId || "");
|
|
1563
|
+
}
|
|
1564
|
+
} catch (failure) {
|
|
1565
|
+
if (current(version2)) setError(failure.message || t("failed"));
|
|
1566
|
+
} finally {
|
|
1567
|
+
if (current(version2)) setLoading(false);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
async function readText(textKey, offset = 0) {
|
|
1571
|
+
if (!detail || pending.current) return;
|
|
1572
|
+
const version2 = generation.current, batchId = detail.batch.id;
|
|
1573
|
+
setLoading(true);
|
|
1574
|
+
setText(null);
|
|
1575
|
+
try {
|
|
1576
|
+
const result = await onImportDetails({ batchId, view: "text", textKey, offset, limit: 16e3 });
|
|
1577
|
+
if (current(version2)) setText(result);
|
|
1578
|
+
} catch (failure) {
|
|
1579
|
+
if (current(version2)) setError(failure.message || t("failed"));
|
|
1580
|
+
} finally {
|
|
1581
|
+
if (current(version2)) setLoading(false);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
async function refresh() {
|
|
1585
|
+
const version2 = generation.current, selected = detail;
|
|
1586
|
+
await onImportRefresh();
|
|
1587
|
+
if (current(version2) && selected) await show(selected.batch.id, selected.view, selected.offset, true);
|
|
1588
|
+
}
|
|
1589
|
+
async function prepare(reuse = false) {
|
|
1590
|
+
if (state.canImport === false || pending.current || loading || !reuse && !file || reuse && !receipt) return;
|
|
1591
|
+
const version2 = ++generation.current;
|
|
1592
|
+
pending.current = true;
|
|
1593
|
+
setBusy(true);
|
|
1594
|
+
setError("");
|
|
1595
|
+
setNotice("");
|
|
1596
|
+
setText(null);
|
|
1597
|
+
setProgress(null);
|
|
1598
|
+
try {
|
|
1599
|
+
let staged = receipt;
|
|
1600
|
+
if (!reuse) {
|
|
1601
|
+
staged = await onImportUpload(file, (value2) => {
|
|
1602
|
+
if (current(version2)) setProgress(value2);
|
|
1603
|
+
});
|
|
1604
|
+
if (!current(version2)) return;
|
|
1605
|
+
setReceipt(staged);
|
|
1606
|
+
}
|
|
1607
|
+
const value = await onImportPrepare({
|
|
1608
|
+
receiptId: staged.receiptId,
|
|
1609
|
+
requestId: globalThis.crypto.randomUUID(),
|
|
1610
|
+
scopeMap: reuse && scope ? [{ sourceConversationId: scope, targetSessionId: importSessionId }] : []
|
|
1611
|
+
});
|
|
1612
|
+
if (!current(version2)) return;
|
|
1613
|
+
setReceipt({ ...staged, batchId: value.id });
|
|
1614
|
+
await onImportRefresh();
|
|
1615
|
+
if (current(version2)) await show(value.id, "records", 0, true);
|
|
1616
|
+
} catch (failure) {
|
|
1617
|
+
if (current(version2)) setError((failure.message || t("failed")) + (failure.remote ? "" : " " + t("importUnknown")));
|
|
1618
|
+
} finally {
|
|
1619
|
+
pending.current = false;
|
|
1620
|
+
if (mounted.current) {
|
|
1621
|
+
setBusy(false);
|
|
1622
|
+
setProgress(null);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
async function decide(action) {
|
|
1627
|
+
const batch2 = detail?.batch;
|
|
1628
|
+
if (state.canImport === false || detail?.canImport === false || !batch2 || pending.current || loading) return;
|
|
1629
|
+
const changed = scope !== (batch2.scopeMap[0]?.sourceConversationId || "");
|
|
1630
|
+
if (action === "confirm" && (batch2.status !== "ready" || batch2.sessionId !== importSessionId || changed)) return;
|
|
1631
|
+
if (action === "withdraw" && batch2.status !== "confirmed") return;
|
|
1632
|
+
const version2 = generation.current;
|
|
1633
|
+
const request = { batchId: batch2.id, expectedRevision: batch2.revision, previewHash: batch2.previewHash };
|
|
1634
|
+
const key = JSON.stringify([action, request]);
|
|
1635
|
+
if (!identities.current.has(key)) identities.current.set(key, globalThis.crypto.randomUUID());
|
|
1636
|
+
request.requestId = identities.current.get(key);
|
|
1637
|
+
pending.current = true;
|
|
1638
|
+
setBusy(true);
|
|
1639
|
+
setError("");
|
|
1640
|
+
setNotice("");
|
|
1641
|
+
try {
|
|
1642
|
+
const value = await (action === "confirm" ? onImportConfirm(request) : onImportWithdraw(request));
|
|
1643
|
+
if (!current(version2)) return;
|
|
1644
|
+
setNotice(t("importSaved"));
|
|
1645
|
+
await onImportRefresh();
|
|
1646
|
+
if (current(version2)) await show(value.id, "records", 0, true);
|
|
1647
|
+
} catch (failure) {
|
|
1648
|
+
if (current(version2)) {
|
|
1649
|
+
setError((failure.message || t("failed")) + (failure.remote ? "" : " " + t("importUnknown")));
|
|
1650
|
+
await onImportRefresh();
|
|
1651
|
+
if (current(version2)) await show(batch2.id, "records", 0, true);
|
|
1652
|
+
}
|
|
1653
|
+
} finally {
|
|
1654
|
+
pending.current = false;
|
|
1655
|
+
if (mounted.current) setBusy(false);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
const button2 = (label, action, disabled = false, props = {}) => h6("button", {
|
|
1659
|
+
type: "button",
|
|
1660
|
+
disabled,
|
|
1661
|
+
onClick: () => {
|
|
1662
|
+
void action();
|
|
1663
|
+
},
|
|
1664
|
+
...props
|
|
1665
|
+
}, t(label));
|
|
1666
|
+
const body = (value) => h6("div", { className: "text" }, String(value ?? ""));
|
|
1667
|
+
const reason = (code) => t(importReasonKeys.includes(code) ? "importReason_" + code : "importReasonUnrecognized");
|
|
1668
|
+
const role = (value) => t(["user", "assistant", "tool", "system", "material", "program"].includes(value) ? "importRole_" + value : "importRoleUnknown");
|
|
1669
|
+
const batch = detail?.batch, locked = busy || loading;
|
|
1670
|
+
const actionLocked = locked || state.canImport === false || detail?.canImport === false;
|
|
1671
|
+
const scopeChanged = batch && scope !== (batch.scopeMap[0]?.sourceConversationId || "");
|
|
1672
|
+
const receiptMatches = receipt && batch && receipt.batchId === batch.id;
|
|
1673
|
+
return h6(
|
|
1674
|
+
"section",
|
|
1675
|
+
{ className: "arcfold-methods arcfold-imports", "aria-label": t("importTitle") },
|
|
1676
|
+
h6(
|
|
1677
|
+
"header",
|
|
1678
|
+
null,
|
|
1679
|
+
h6("strong", null, `${t("importTitle")} · ${state.total}`),
|
|
1680
|
+
button2(expanded ? "importHide" : "importOpen", () => expand(!expanded), busy, { "aria-expanded": expanded }),
|
|
1681
|
+
button2("refresh", refresh, locked)
|
|
1682
|
+
),
|
|
1683
|
+
expanded && h6(
|
|
1684
|
+
"div",
|
|
1685
|
+
{ className: "detail" },
|
|
1686
|
+
h6("p", null, t("importBoundary")),
|
|
1687
|
+
h6("label", null, t("importFile"), h6("input", {
|
|
1688
|
+
type: "file",
|
|
1689
|
+
accept: ".json,.jsonl,.ndjson,application/json",
|
|
1690
|
+
disabled: actionLocked,
|
|
1691
|
+
onChange: (event) => {
|
|
1692
|
+
const chosen = event.target.files?.[0] || null;
|
|
1693
|
+
generation.current++;
|
|
1694
|
+
setDetail(null);
|
|
1695
|
+
setText(null);
|
|
1696
|
+
setReceipt(null);
|
|
1697
|
+
setScope("");
|
|
1698
|
+
setNotice("");
|
|
1699
|
+
const valid = chosen && chosen.size > 0 && chosen.size <= IMPORT_MAX_FILE_BYTES;
|
|
1700
|
+
setFile(valid ? chosen : null);
|
|
1701
|
+
setError(chosen && !valid ? t("importFileTooLarge") : "");
|
|
1702
|
+
}
|
|
1703
|
+
})),
|
|
1704
|
+
button2("importPrepare", () => prepare(), actionLocked || !file),
|
|
1705
|
+
busy && h6("p", { role: "status" }, t("importWorking"), progress && ` ${progress.loaded.toLocaleString()} / ${file?.size.toLocaleString()} bytes`),
|
|
1706
|
+
h6("p", { className: "meta" }, t("importStorage")),
|
|
1707
|
+
(state.loading || loading) && h6("p", { role: "status" }, t("importLoading")),
|
|
1708
|
+
(error || state.error) && h6("p", { role: "alert" }, error || state.error),
|
|
1709
|
+
notice && h6("p", { role: "status" }, notice),
|
|
1710
|
+
h6("ul", null, state.items.map((row) => h6(
|
|
1711
|
+
"li",
|
|
1712
|
+
{ key: row.id },
|
|
1713
|
+
body(row.file.name),
|
|
1714
|
+
h6("p", { className: "meta" }, `${t(statusKeys[row.status])} · ${row.createdAt}`),
|
|
1715
|
+
button2("importPreview", () => show(row.id), locked)
|
|
1716
|
+
))),
|
|
1717
|
+
!state.loading && !state.error && !state.items.length && h6("p", null, t("importEmpty")),
|
|
1718
|
+
h6(
|
|
1719
|
+
"nav",
|
|
1720
|
+
null,
|
|
1721
|
+
button2("previous", () => onImportPage(Math.max(0, state.offset - 20)), locked || !state.offset),
|
|
1722
|
+
button2("next", () => onImportPage(state.nextOffset), locked || state.nextOffset == null)
|
|
1723
|
+
),
|
|
1724
|
+
batch && h6(
|
|
1725
|
+
"article",
|
|
1726
|
+
{ "aria-label": t("importTitle") + " " + batch.id },
|
|
1727
|
+
h6("h3", null, batch.file.name),
|
|
1728
|
+
h6("p", null, t(statusKeys[batch.status])),
|
|
1729
|
+
h6("p", { className: "meta" }, t("importSnapshot")),
|
|
1730
|
+
batch.status === "withdrawn" && h6("p", { role: "status" }, t("importWithdrawnNotice")),
|
|
1731
|
+
h6("p", { className: "meta" }, `${t("importRevision")} ${batch.revision} · ${batch.file.bytes.toLocaleString()} bytes`),
|
|
1732
|
+
h6("details", null, h6("summary", null, t("importFileHash")), body(batch.file.sha256)),
|
|
1733
|
+
batch.error && h6("p", { role: "alert" }, batch.error.message),
|
|
1734
|
+
h6("h4", null, t("importCountsAtPreview")),
|
|
1735
|
+
h6("dl", { className: "import-counts" }, Object.entries({ sources: "importCountSources", records: "importCountRecords", enabled: "importEnabled", trial: "importTrial", paused: "importPaused", history: "importHistory", needsReview: "importNeedsReview" }).map(([name, key]) => h6("div", { key: name }, h6("dt", null, t(key)), h6("dd", null, String(batch.counts[name] ?? 0))))),
|
|
1736
|
+
h6("h4", null, t("importScopeTitle")),
|
|
1737
|
+
h6("p", { className: "meta" }, t("importScopeHelp")),
|
|
1738
|
+
h6("label", null, t("importMapScope"), h6(
|
|
1739
|
+
"select",
|
|
1740
|
+
{
|
|
1741
|
+
value: scope,
|
|
1742
|
+
disabled: actionLocked || batch.status !== "ready" || !receiptMatches || batch.sessionId !== importSessionId,
|
|
1743
|
+
onChange: (event) => setScope(event.target.value)
|
|
1744
|
+
},
|
|
1745
|
+
h6("option", { value: "" }, t("importKeepScope")),
|
|
1746
|
+
batch.sourceScopes.map((source) => h6("option", { key: source.sourceConversationId, value: source.sourceConversationId }, `${source.title || source.sourceConversationId} · ${source.messageCount}`))
|
|
1747
|
+
)),
|
|
1748
|
+
scope && h6("p", { className: "text" }, batch.sourceScopes.find((source) => source.sourceConversationId === scope)?.preview),
|
|
1749
|
+
scopeChanged && h6("p", { role: "status" }, t("importScopeChanged")),
|
|
1750
|
+
batch.status === "ready" && (receiptMatches ? button2("importReprepare", () => prepare(true), actionLocked || !scopeChanged) : h6("p", { className: "meta" }, t("importNoReceipt"))),
|
|
1751
|
+
h6("nav", null, button2("importRecords", () => show(batch.id, "records"), locked), button2("importSources", () => show(batch.id, "sources"), locked)),
|
|
1752
|
+
h6("p", { className: "meta" }, t("importExcerpt")),
|
|
1753
|
+
h6("ul", null, detail.items.map((row) => h6(
|
|
1754
|
+
"li",
|
|
1755
|
+
{ key: row.id, className: "source" },
|
|
1756
|
+
row.title && h6("h4", null, row.title),
|
|
1757
|
+
body(row.textPreview),
|
|
1758
|
+
row.eligibility && h6("p", null, `${t("importEligibilityAtPreview")}:${t(eligibilityKeys[row.eligibility])}`),
|
|
1759
|
+
row.claimType && h6("p", null, t({ user_statement: "importClaimLiteral", inference: "importClaimInference", open_question: "importClaimQuestion" }[row.claimType] || "importClaimInference")),
|
|
1760
|
+
row.kind === "methods" && h6(
|
|
1761
|
+
import_react6.default.Fragment,
|
|
1762
|
+
null,
|
|
1763
|
+
h6("h4", null, t("importAppliesTo")),
|
|
1764
|
+
body(row.appliesTo ?? t("importAttributeUnavailable")),
|
|
1765
|
+
h6("h4", null, t("importDoesNotApplyTo")),
|
|
1766
|
+
body(row.doesNotApplyTo ?? t("importAttributeUnavailable"))
|
|
1767
|
+
),
|
|
1768
|
+
row.scope && h6("p", { className: "meta" }, t(scopeKeys[row.scope.kind])),
|
|
1769
|
+
row.scope?.topics?.length > 0 && h6("p", { className: "meta" }, t("importTopics") + ": " + row.scope.topics.join(" · ")),
|
|
1770
|
+
row.role && h6("p", { className: "meta" }, `${role(row.role)} · ${row.occurredAt || ""} · ${t("importSourceAtPreview")}:${t(row.available ? "importAvailable" : "importUnavailable")}`),
|
|
1771
|
+
row.methodUsage && h6("p", null, `${t("importUsageAtPreview")}:${t("importUsed")} ${row.methodUsage.trialUses ?? "—"} / ${t("importMaximum")} ${row.methodUsage.maxTrialUses ?? "—"} · ${t("importRemaining")} ${row.methodUsage.remaining ?? "—"}`),
|
|
1772
|
+
row.reasonCodes?.length > 0 && h6("details", null, h6("summary", null, t("importReason")), body(row.reasonCodes.map(reason).join("\n"))),
|
|
1773
|
+
row.existingRecordId && h6("details", null, h6("summary", null, t("importExistingRecord")), body(row.existingRecordId)),
|
|
1774
|
+
row.originalTextKey && button2("importOriginalRecord", () => readText(row.originalTextKey), locked),
|
|
1775
|
+
row.textKey && button2(detail.view === "sources" ? "importText" : "importContent", () => readText(row.textKey), locked)
|
|
1776
|
+
))),
|
|
1777
|
+
h6(
|
|
1778
|
+
"nav",
|
|
1779
|
+
null,
|
|
1780
|
+
button2("previous", () => show(batch.id, detail.view, Math.max(0, detail.offset - 20)), locked || !detail.offset),
|
|
1781
|
+
button2("next", () => show(batch.id, detail.view, detail.nextOffset), locked || detail.nextOffset == null)
|
|
1782
|
+
),
|
|
1783
|
+
text2 && h6(
|
|
1784
|
+
"section",
|
|
1785
|
+
{ "aria-label": t("importTextPage") },
|
|
1786
|
+
h6("h4", null, t("importTextPage")),
|
|
1787
|
+
h6("p", { className: "meta" }, `${text2.offset}–${text2.offset + text2.text.length} / ${text2.total}`),
|
|
1788
|
+
body(text2.text),
|
|
1789
|
+
button2("importReadPrevious", () => readText(text2.textKey, Math.max(0, text2.offset - 16e3)), locked || !text2.offset),
|
|
1790
|
+
button2("importReadMore", () => readText(text2.textKey, text2.nextOffset), locked || text2.nextOffset == null)
|
|
1791
|
+
),
|
|
1792
|
+
batch.status === "ready" && h6(
|
|
1793
|
+
"div",
|
|
1794
|
+
{ className: "import-decision" },
|
|
1795
|
+
h6("p", null, t("importConfirmHelp")),
|
|
1796
|
+
batch.sessionId !== importSessionId && h6("p", null, t("importOtherSession")),
|
|
1797
|
+
button2("importConfirm", () => decide("confirm"), actionLocked || scopeChanged || batch.sessionId !== importSessionId)
|
|
1798
|
+
),
|
|
1799
|
+
batch.status === "confirmed" && h6(
|
|
1800
|
+
"div",
|
|
1801
|
+
{ className: "import-decision" },
|
|
1802
|
+
h6("p", null, t("importWithdrawHelp")),
|
|
1803
|
+
button2("importWithdraw", () => decide("withdraw"), actionLocked)
|
|
1804
|
+
)
|
|
1805
|
+
)
|
|
1806
|
+
)
|
|
1807
|
+
);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
// src/ModelSettings.mjs
|
|
1811
|
+
var import_react7 = __toESM(require("react"), 1);
|
|
1812
|
+
|
|
1813
|
+
// src/model-settings-source.mjs
|
|
1814
|
+
var PROVIDERS = /* @__PURE__ */ new Set(["deepseek-official", "openai"]);
|
|
1815
|
+
var own = (value, keys) => value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
1816
|
+
var refused = (code) => ({ status: "refused", code });
|
|
1817
|
+
var remoteCode = (code) => ["revision_conflict", "learning_defaults_conflict"].includes(code) ? "conflict" : "refused";
|
|
1818
|
+
function deployedRoutes(data) {
|
|
1819
|
+
return (data?.routes || []).filter((route) => PROVIDERS.has(route.provider));
|
|
1820
|
+
}
|
|
1821
|
+
function validLearningChoice(data, request) {
|
|
1822
|
+
const route = deployedRoutes(data).find((row) => row.provider === request.provider);
|
|
1823
|
+
const model = route?.models?.find((row) => row.id === request.model);
|
|
1824
|
+
return Boolean(model?.reasoningEfforts?.includes(request.reasoningEffort));
|
|
1825
|
+
}
|
|
1826
|
+
function createModelSettingsSource({ rpc, subscribeReset }) {
|
|
1827
|
+
let snapshot = { loading: false, data: null, error: null, credentials: {}, credentialError: false };
|
|
1828
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1829
|
+
let generation = 0, readController, mutationController, reset, disposed = false, writing = false;
|
|
1830
|
+
const publish = (patch) => {
|
|
1831
|
+
snapshot = { ...snapshot, ...patch };
|
|
1832
|
+
for (const listener of listeners) listener();
|
|
1833
|
+
};
|
|
1834
|
+
const active = () => !disposed && listeners.size > 0;
|
|
1835
|
+
async function refresh() {
|
|
1836
|
+
if (!active()) return false;
|
|
1837
|
+
readController?.abort();
|
|
1838
|
+
const controller = new AbortController(), current = ++generation;
|
|
1839
|
+
readController = controller;
|
|
1840
|
+
const stillCurrent = () => active() && current === generation && !controller.signal.aborted;
|
|
1841
|
+
publish({ loading: true, error: null });
|
|
1842
|
+
try {
|
|
1843
|
+
const reply = await rpc.call("/api", "arcfoldModels/read", { args: { request: {} } }, controller.signal);
|
|
1844
|
+
if (reply?.ok !== true) throw new Error("read refused");
|
|
1845
|
+
if (!stillCurrent()) return false;
|
|
1846
|
+
const data = reply.value;
|
|
1847
|
+
const refs = [...new Set(deployedRoutes(data).map((route) => route.apiKeyRef))];
|
|
1848
|
+
let credentials = {}, credentialError = false;
|
|
1849
|
+
if (refs.length) {
|
|
1850
|
+
try {
|
|
1851
|
+
const result = await rpc.call("/api", "credentials/describe", { args: { refs } }, controller.signal);
|
|
1852
|
+
if (result?.ok !== true) throw new Error("credential metadata refused");
|
|
1853
|
+
credentials = Object.fromEntries(refs.map((ref) => {
|
|
1854
|
+
const value = result.value?.[ref];
|
|
1855
|
+
return [ref, {
|
|
1856
|
+
configured: value?.configured === true,
|
|
1857
|
+
writable: value?.writable === true,
|
|
1858
|
+
...typeof value?.source === "string" ? { source: value.source } : {}
|
|
1859
|
+
}];
|
|
1860
|
+
}));
|
|
1861
|
+
} catch {
|
|
1862
|
+
credentialError = true;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
if (!stillCurrent()) return false;
|
|
1866
|
+
publish({ loading: false, data, error: null, credentials, credentialError });
|
|
1867
|
+
return true;
|
|
1868
|
+
} catch {
|
|
1869
|
+
if (stillCurrent()) publish({ loading: false, error: "read_failed" });
|
|
1870
|
+
return false;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
function stop() {
|
|
1874
|
+
reset?.();
|
|
1875
|
+
reset = void 0;
|
|
1876
|
+
readController?.abort();
|
|
1877
|
+
mutationController?.abort();
|
|
1878
|
+
generation++;
|
|
1879
|
+
}
|
|
1880
|
+
async function mutate(endpoint, args, credential = false) {
|
|
1881
|
+
if (!active() || writing) return refused("unavailable");
|
|
1882
|
+
writing = true;
|
|
1883
|
+
const controller = new AbortController();
|
|
1884
|
+
mutationController = controller;
|
|
1885
|
+
try {
|
|
1886
|
+
const result = await rpc.call("/api", endpoint, { args }, controller.signal);
|
|
1887
|
+
if (result?.ok !== true) {
|
|
1888
|
+
const code = credential ? "credential_refused" : remoteCode(result?.error?.code);
|
|
1889
|
+
await refresh();
|
|
1890
|
+
return refused(code);
|
|
1891
|
+
}
|
|
1892
|
+
const refreshed = await refresh();
|
|
1893
|
+
return { status: "saved", refreshed };
|
|
1894
|
+
} catch {
|
|
1895
|
+
return { status: "unknown", code: credential ? "credential_unknown" : "unknown" };
|
|
1896
|
+
} finally {
|
|
1897
|
+
writing = false;
|
|
1898
|
+
if (mutationController === controller) mutationController = void 0;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
return {
|
|
1902
|
+
getSnapshot: () => snapshot,
|
|
1903
|
+
subscribe(listener) {
|
|
1904
|
+
if (disposed) return () => {
|
|
1905
|
+
};
|
|
1906
|
+
listeners.add(listener);
|
|
1907
|
+
if (listeners.size === 1) {
|
|
1908
|
+
reset = subscribeReset(() => {
|
|
1909
|
+
void refresh();
|
|
1910
|
+
});
|
|
1911
|
+
void refresh();
|
|
1912
|
+
}
|
|
1913
|
+
return () => {
|
|
1914
|
+
listeners.delete(listener);
|
|
1915
|
+
if (!listeners.size) stop();
|
|
1916
|
+
};
|
|
1917
|
+
},
|
|
1918
|
+
refresh,
|
|
1919
|
+
saveLearning(request) {
|
|
1920
|
+
const data = snapshot.data;
|
|
1921
|
+
if (!own(request, ["expectedRevision", "provider", "model", "reasoningEffort"]) || !Number.isSafeInteger(request.expectedRevision) || request.expectedRevision < 1 || data?.gate?.phase !== "ready" || snapshot.error || snapshot.loading || !data?.learning?.enabled || !data.learning.defaults || !validLearningChoice(data, request)) return Promise.resolve(refused("unavailable"));
|
|
1922
|
+
return mutate("arcfoldModels/saveLearning", { request: { ...request } });
|
|
1923
|
+
},
|
|
1924
|
+
storeCredential(provider, value) {
|
|
1925
|
+
const route = deployedRoutes(snapshot.data).find((row) => row.provider === provider);
|
|
1926
|
+
if (!route || !snapshot.credentials[route.apiKeyRef]?.writable || snapshot.error || snapshot.loading || typeof value !== "string" || !value.trim()) return Promise.resolve(refused("credential_unavailable"));
|
|
1927
|
+
return mutate("credentials/set", { ref: route.apiKeyRef, value }, true);
|
|
1928
|
+
},
|
|
1929
|
+
removeCredential(provider) {
|
|
1930
|
+
const route = deployedRoutes(snapshot.data).find((row) => row.provider === provider);
|
|
1931
|
+
if (!route || !snapshot.credentials[route.apiKeyRef]?.writable || snapshot.error || snapshot.loading)
|
|
1932
|
+
return Promise.resolve(refused("credential_unavailable"));
|
|
1933
|
+
return mutate("credentials/unset", { ref: route.apiKeyRef }, true);
|
|
1934
|
+
},
|
|
1935
|
+
dispose() {
|
|
1936
|
+
disposed = true;
|
|
1937
|
+
stop();
|
|
1938
|
+
listeners.clear();
|
|
1939
|
+
}
|
|
1940
|
+
};
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/ModelSettings.mjs
|
|
1944
|
+
var h7 = import_react7.default.createElement;
|
|
1945
|
+
var PHASE_LABELS = {
|
|
1946
|
+
ready: "modelsPhaseReady",
|
|
1947
|
+
starting: "modelsPhaseStarting",
|
|
1948
|
+
"requires-restart": "modelsPhaseRestart",
|
|
1949
|
+
failed: "modelsPhaseFailed",
|
|
1950
|
+
closed: "modelsPhaseClosed"
|
|
1951
|
+
};
|
|
1952
|
+
var REASON_LABELS = {
|
|
1953
|
+
"main-not-configured": "modelsReasonNoModel",
|
|
1954
|
+
"learn-not-configured": "modelsReasonNoModel",
|
|
1955
|
+
"native-credential-not-configured": "modelsReasonCredential",
|
|
1956
|
+
"transport-not-configured": "modelsReasonUnsupported",
|
|
1957
|
+
"selected-route-not-supported": "modelsReasonUnsupported",
|
|
1958
|
+
"selected-model-controls-unavailable": "modelsReasonUnsupported",
|
|
1959
|
+
"main-output-limit-unavailable": "modelsReasonUnsupported",
|
|
1960
|
+
"selection-outside-model-policy": "modelsReasonUnsupported",
|
|
1961
|
+
"model-catalog-controls-invalid": "modelsReasonUnsupported",
|
|
1962
|
+
"model-catalog-has-no-common-controls": "modelsReasonUnsupported",
|
|
1963
|
+
"native-selection-changed-during-status": "modelsReasonChanged",
|
|
1964
|
+
"learning-selection-changed-during-status": "modelsReasonChanged",
|
|
1965
|
+
"runtime-not-ready": "modelsReasonNotReady",
|
|
1966
|
+
"runtime-readiness-unavailable": "modelsReasonNotReady",
|
|
1967
|
+
"guard-unavailable": "modelsReasonNotReady",
|
|
1968
|
+
"startup-failed": "modelsReasonNotReady",
|
|
1969
|
+
"runtime-closed": "modelsReasonNotReady",
|
|
1970
|
+
"runtime-restart-required": "modelsReasonRestart",
|
|
1971
|
+
"critical-component-changed": "modelsReasonRestart",
|
|
1972
|
+
"critical-configuration-changed": "modelsReasonRestart",
|
|
1973
|
+
"model-topology-changed": "modelsReasonRestart"
|
|
1974
|
+
};
|
|
1975
|
+
var EFFORT_LABELS = {
|
|
1976
|
+
off: "modelsEffortOff",
|
|
1977
|
+
low: "modelsEffortLow",
|
|
1978
|
+
medium: "modelsEffortMedium",
|
|
1979
|
+
high: "modelsEffortHigh",
|
|
1980
|
+
xhigh: "modelsEffortXHigh",
|
|
1981
|
+
max: "modelsEffortMax"
|
|
1982
|
+
};
|
|
1983
|
+
var reasonText = (reason, t) => t(REASON_LABELS[reason] || "modelsReasonUnknown");
|
|
1984
|
+
var effortText = (effort, t) => t(EFFORT_LABELS[effort] || "modelsEffortOther");
|
|
1985
|
+
var selectionText = (value, t) => value ? [
|
|
1986
|
+
value.provider,
|
|
1987
|
+
value.model,
|
|
1988
|
+
value.reasoningEffort ? effortText(value.reasoningEffort, t) : null
|
|
1989
|
+
].filter(Boolean).join(" · ") : "";
|
|
1990
|
+
function outcomeText(result, t, credential = false) {
|
|
1991
|
+
if (result?.status === "saved") return t(result.refreshed ? "modelsSaved" : "modelsSavedUnread");
|
|
1992
|
+
if (credential) return t(result?.status === "unknown" ? "modelsCredentialUnknown" : "modelsCredentialFailed");
|
|
1993
|
+
return t(result?.status === "unknown" ? "modelsUnknown" : result?.code === "conflict" ? "modelsConflict" : "modelsRefused");
|
|
1994
|
+
}
|
|
1995
|
+
function CredentialCard({ route, info, disabled, readFailed, onStore, onRemove, t }) {
|
|
1996
|
+
const [draft, setDraft] = (0, import_react7.useState)(""), [busy, setBusy] = (0, import_react7.useState)(false), [notice, setNotice] = (0, import_react7.useState)("");
|
|
1997
|
+
const pending = (0, import_react7.useRef)(false), mounted = (0, import_react7.useRef)(true);
|
|
1998
|
+
(0, import_react7.useEffect)(() => {
|
|
1999
|
+
mounted.current = true;
|
|
2000
|
+
return () => {
|
|
2001
|
+
mounted.current = false;
|
|
2002
|
+
};
|
|
2003
|
+
}, []);
|
|
2004
|
+
async function write(remove) {
|
|
2005
|
+
if (pending.current || disabled || !info?.writable) return;
|
|
2006
|
+
pending.current = true;
|
|
2007
|
+
setBusy(true);
|
|
2008
|
+
setNotice("");
|
|
2009
|
+
const value = draft;
|
|
2010
|
+
setDraft("");
|
|
2011
|
+
try {
|
|
2012
|
+
const result = remove ? await onRemove(route.provider) : await onStore(route.provider, value);
|
|
2013
|
+
if (mounted.current) setNotice(outcomeText(result, t, true));
|
|
2014
|
+
} catch {
|
|
2015
|
+
if (mounted.current) setNotice(t("modelsCredentialUnknown"));
|
|
2016
|
+
} finally {
|
|
2017
|
+
pending.current = false;
|
|
2018
|
+
if (mounted.current) {
|
|
2019
|
+
setDraft("");
|
|
2020
|
+
setBusy(false);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
return h7(
|
|
2025
|
+
"article",
|
|
2026
|
+
{ className: "model-card" },
|
|
2027
|
+
h7("h3", null, route.label),
|
|
2028
|
+
h7("p", null, readFailed ? t("modelsCredentialReadFailed") : t(info?.configured ? "modelsConfigured" : "modelsNotConfigured")),
|
|
2029
|
+
info?.source && h7("p", { className: "meta" }, t("modelsSource") + ": " + info.source),
|
|
2030
|
+
!readFailed && !info?.writable && h7("p", null, t("modelsReadOnly")),
|
|
2031
|
+
h7(
|
|
2032
|
+
"label",
|
|
2033
|
+
null,
|
|
2034
|
+
route.label + " · " + t("modelsCredential"),
|
|
2035
|
+
h7("input", {
|
|
2036
|
+
type: "password",
|
|
2037
|
+
value: draft,
|
|
2038
|
+
autoComplete: "new-password",
|
|
2039
|
+
spellCheck: false,
|
|
2040
|
+
disabled: disabled || busy || !info?.writable,
|
|
2041
|
+
onChange: (event) => setDraft(event.target.value)
|
|
2042
|
+
})
|
|
2043
|
+
),
|
|
2044
|
+
h7(
|
|
2045
|
+
"div",
|
|
2046
|
+
{ className: "actions" },
|
|
2047
|
+
h7("button", { type: "button", disabled: disabled || busy || !info?.writable || !draft.trim(), onClick: () => {
|
|
2048
|
+
void write(false);
|
|
2049
|
+
} }, t("modelsCredentialSave")),
|
|
2050
|
+
h7("button", { type: "button", disabled: disabled || busy || !info?.writable || !info?.configured, onClick: () => {
|
|
2051
|
+
void write(true);
|
|
2052
|
+
} }, t("modelsCredentialRemove")),
|
|
2053
|
+
h7("button", { type: "button", onClick: () => {
|
|
2054
|
+
setDraft("");
|
|
2055
|
+
setNotice("");
|
|
2056
|
+
} }, t("modelsCredentialCancel"))
|
|
2057
|
+
),
|
|
2058
|
+
notice && h7("p", { role: "status" }, notice)
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
function Usage({ route, t }) {
|
|
2062
|
+
const usage = route.usage;
|
|
2063
|
+
const scope = { limited: "modelsScopeLimited", "web-unlimited": "modelsScopeWeb", "web-openai": "modelsScopeOpenai" };
|
|
2064
|
+
const row = (label, value) => h7("div", { key: label }, h7("dt", null, t(label)), h7("dd", null, value));
|
|
2065
|
+
return h7(
|
|
2066
|
+
"article",
|
|
2067
|
+
{ className: "model-card" },
|
|
2068
|
+
h7("h3", null, route.label),
|
|
2069
|
+
!usage ? h7("p", null, t("modelsUsageMissing")) : h7(
|
|
2070
|
+
import_react7.default.Fragment,
|
|
2071
|
+
null,
|
|
2072
|
+
h7("p", { className: "meta" }, scope[usage.scope] ? t(scope[usage.scope]) : usage.scope),
|
|
2073
|
+
h7(
|
|
2074
|
+
"dl",
|
|
2075
|
+
null,
|
|
2076
|
+
row("modelsRequests", usage.requests),
|
|
2077
|
+
row("modelsUnknownCount", usage.unknownCount),
|
|
2078
|
+
usage.accounting === "cny" ? [
|
|
2079
|
+
row("modelsSpent", "¥ " + usage.spentCny),
|
|
2080
|
+
row("modelsReserved", "¥ " + usage.reservedCny),
|
|
2081
|
+
row("modelsCeiling", usage.ceilingCny === null ? t("modelsNoCeiling") : "¥ " + usage.ceilingCny)
|
|
2082
|
+
] : [
|
|
2083
|
+
row("modelsPrompt", usage.promptTokens),
|
|
2084
|
+
row("modelsCompletion", usage.completionTokens),
|
|
2085
|
+
row("modelsCached", usage.cachedTokens),
|
|
2086
|
+
row("modelsPending", usage.pendingCount)
|
|
2087
|
+
]
|
|
2088
|
+
),
|
|
2089
|
+
usage.accounting === "tokens_only" && h7("p", null, t("modelsTokensOnly"))
|
|
2090
|
+
)
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
function LearningForm({ data, disabled, onSave, t }) {
|
|
2094
|
+
const defaults = data.learning?.defaults;
|
|
2095
|
+
const [draft, setDraft] = (0, import_react7.useState)(() => ({ ...defaults }));
|
|
2096
|
+
const [busy, setBusy] = (0, import_react7.useState)(false), [notice, setNotice] = (0, import_react7.useState)("");
|
|
2097
|
+
const pending = (0, import_react7.useRef)(false), mounted = (0, import_react7.useRef)(true);
|
|
2098
|
+
(0, import_react7.useEffect)(() => {
|
|
2099
|
+
mounted.current = true;
|
|
2100
|
+
return () => {
|
|
2101
|
+
mounted.current = false;
|
|
2102
|
+
};
|
|
2103
|
+
}, []);
|
|
2104
|
+
(0, import_react7.useEffect)(() => {
|
|
2105
|
+
setDraft({ ...defaults });
|
|
2106
|
+
}, [defaults?.revision, defaults?.provider, defaults?.model, defaults?.reasoningEffort]);
|
|
2107
|
+
const routes = deployedRoutes(data), route = routes.find((row) => row.provider === draft.provider);
|
|
2108
|
+
const model = route?.models?.find((row) => row.id === draft.model);
|
|
2109
|
+
const blocked = disabled || busy || data.gate?.phase !== "ready" || !data.learning?.enabled || !defaults;
|
|
2110
|
+
const select2 = (label, value, options, update) => h7(
|
|
2111
|
+
"label",
|
|
2112
|
+
{ key: label },
|
|
2113
|
+
t(label),
|
|
2114
|
+
h7(
|
|
2115
|
+
"select",
|
|
2116
|
+
{
|
|
2117
|
+
value: options.some((item) => item.id === value) ? value : "",
|
|
2118
|
+
disabled: blocked,
|
|
2119
|
+
onChange: (event) => update(event.target.value)
|
|
2120
|
+
},
|
|
2121
|
+
h7("option", { value: "", disabled: true }, t("modelsChoose")),
|
|
2122
|
+
options.map((item) => h7("option", { key: item.id, value: item.id }, item.label || item.id))
|
|
2123
|
+
)
|
|
2124
|
+
);
|
|
2125
|
+
async function save() {
|
|
2126
|
+
if (pending.current || blocked || !validLearningChoice(data, draft)) return;
|
|
2127
|
+
pending.current = true;
|
|
2128
|
+
setBusy(true);
|
|
2129
|
+
setNotice("");
|
|
2130
|
+
try {
|
|
2131
|
+
const result = await onSave({
|
|
2132
|
+
expectedRevision: defaults.revision,
|
|
2133
|
+
provider: draft.provider,
|
|
2134
|
+
model: draft.model,
|
|
2135
|
+
reasoningEffort: draft.reasoningEffort
|
|
2136
|
+
});
|
|
2137
|
+
if (mounted.current) setNotice(outcomeText(result, t));
|
|
2138
|
+
} catch {
|
|
2139
|
+
if (mounted.current) setNotice(t("modelsUnknown"));
|
|
2140
|
+
} finally {
|
|
2141
|
+
pending.current = false;
|
|
2142
|
+
if (mounted.current) setBusy(false);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
return h7(
|
|
2146
|
+
"div",
|
|
2147
|
+
null,
|
|
2148
|
+
!data.learning?.enabled && h7("p", null, t("modelsLearnDisabled")),
|
|
2149
|
+
!defaults && h7("p", null, t("modelsNoDefault")),
|
|
2150
|
+
!data.learning?.availability?.available && h7("p", null, reasonText(data.learning?.availability?.reason, t)),
|
|
2151
|
+
defaults && h7("p", { className: "meta" }, t("modelsRevision") + ": " + defaults.revision + " · " + selectionText(defaults, t)),
|
|
2152
|
+
h7(
|
|
2153
|
+
"div",
|
|
2154
|
+
{ className: "model-fields" },
|
|
2155
|
+
select2(
|
|
2156
|
+
"modelsProvider",
|
|
2157
|
+
draft.provider,
|
|
2158
|
+
routes.map((row) => ({ id: row.provider, label: row.label })),
|
|
2159
|
+
(provider) => {
|
|
2160
|
+
setNotice("");
|
|
2161
|
+
setDraft({ provider, model: "", reasoningEffort: "" });
|
|
2162
|
+
}
|
|
2163
|
+
),
|
|
2164
|
+
select2(
|
|
2165
|
+
"modelsModel",
|
|
2166
|
+
draft.model,
|
|
2167
|
+
route?.models || [],
|
|
2168
|
+
(model2) => {
|
|
2169
|
+
setNotice("");
|
|
2170
|
+
setDraft({ ...draft, model: model2, reasoningEffort: "" });
|
|
2171
|
+
}
|
|
2172
|
+
),
|
|
2173
|
+
select2(
|
|
2174
|
+
"modelsReasoning",
|
|
2175
|
+
draft.reasoningEffort,
|
|
2176
|
+
(model?.reasoningEfforts || []).map((id) => ({ id, label: effortText(id, t) })),
|
|
2177
|
+
(reasoningEffort) => {
|
|
2178
|
+
setNotice("");
|
|
2179
|
+
setDraft({ ...draft, reasoningEffort });
|
|
2180
|
+
}
|
|
2181
|
+
)
|
|
2182
|
+
),
|
|
2183
|
+
h7(
|
|
2184
|
+
"button",
|
|
2185
|
+
{ type: "button", disabled: blocked || !validLearningChoice(data, draft), onClick: () => {
|
|
2186
|
+
void save();
|
|
2187
|
+
} },
|
|
2188
|
+
t(busy ? "modelsBusy" : "modelsSave")
|
|
2189
|
+
),
|
|
2190
|
+
notice && h7("p", { role: "status" }, notice)
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
2193
|
+
function ModelSettings({ useModels, onRefresh, onSaveLearning, onStoreCredential, onRemoveCredential, t }) {
|
|
2194
|
+
const state = useModels((value) => value), data = state.data;
|
|
2195
|
+
return h7(
|
|
2196
|
+
"section",
|
|
2197
|
+
{ className: "arcfold-methods arcfold-model-settings", "aria-label": t("modelsTitle") },
|
|
2198
|
+
h7(
|
|
2199
|
+
"header",
|
|
2200
|
+
null,
|
|
2201
|
+
h7("h2", null, t("modelsTitle")),
|
|
2202
|
+
h7("button", { type: "button", disabled: state.loading, onClick: () => {
|
|
2203
|
+
void onRefresh();
|
|
2204
|
+
} }, t("modelsRefresh"))
|
|
2205
|
+
),
|
|
2206
|
+
state.loading && h7("p", { role: "status" }, t("modelsLoading")),
|
|
2207
|
+
state.error && h7("p", { role: "alert" }, t("modelsReadFailed")),
|
|
2208
|
+
data && h7(
|
|
2209
|
+
import_react7.default.Fragment,
|
|
2210
|
+
null,
|
|
2211
|
+
h7("p", { className: "meta" }, t("modelsGate") + ": " + t(PHASE_LABELS[data.gate.phase] || "modelsReasonUnknown") + (data.gate.reason ? " · " + reasonText(data.gate.reason, t) : "")),
|
|
2212
|
+
h7("h3", null, t("modelsMain")),
|
|
2213
|
+
h7("p", null, t("modelsMainScope")),
|
|
2214
|
+
h7("p", null, t("modelsDefault") + ": " + (selectionText(data.main?.defaultSelection, t) || t("modelsUnavailable"))),
|
|
2215
|
+
!data.main?.availability?.available && h7("p", null, reasonText(data.main?.availability?.reason, t)),
|
|
2216
|
+
h7("h3", null, t("modelsLearn")),
|
|
2217
|
+
h7("p", null, t("modelsLearnScope")),
|
|
2218
|
+
h7(LearningForm, {
|
|
2219
|
+
data,
|
|
2220
|
+
disabled: Boolean(state.error || state.loading),
|
|
2221
|
+
onSave: onSaveLearning,
|
|
2222
|
+
t
|
|
2223
|
+
}),
|
|
2224
|
+
h7("h3", null, t("modelsCredentials")),
|
|
2225
|
+
h7("p", null, t("modelsCredentialScope")),
|
|
2226
|
+
!deployedRoutes(data).length && h7("p", null, t("modelsNoRoutes")),
|
|
2227
|
+
deployedRoutes(data).map((route) => h7(CredentialCard, {
|
|
2228
|
+
key: route.provider + ":" + route.apiKeyRef,
|
|
2229
|
+
route,
|
|
2230
|
+
info: state.credentials[route.apiKeyRef],
|
|
2231
|
+
readFailed: state.credentialError,
|
|
2232
|
+
disabled: Boolean(state.error || state.loading),
|
|
2233
|
+
onStore: onStoreCredential,
|
|
2234
|
+
onRemove: onRemoveCredential,
|
|
2235
|
+
t
|
|
2236
|
+
})),
|
|
2237
|
+
h7("h3", null, t("modelsUsage")),
|
|
2238
|
+
h7("p", null, t("modelsUsageBoundary")),
|
|
2239
|
+
deployedRoutes(data).map((route) => h7(Usage, { key: route.provider, route, t }))
|
|
2240
|
+
)
|
|
2241
|
+
);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/CollaborationView.mjs
|
|
2245
|
+
var h8 = import_react8.default.createElement;
|
|
2246
|
+
function Panels(props) {
|
|
2247
|
+
return h8(
|
|
2248
|
+
import_react8.default.Fragment,
|
|
2249
|
+
null,
|
|
2250
|
+
h8(OpportunityDock, props),
|
|
2251
|
+
h8(MethodDock, props),
|
|
2252
|
+
h8(GoalDock, props),
|
|
2253
|
+
h8(UnderstandingDock, props),
|
|
2254
|
+
h8(ImportDock, props)
|
|
2255
|
+
);
|
|
2256
|
+
}
|
|
2257
|
+
function Models(props) {
|
|
2258
|
+
return h8(ModelSettings, { ...props, onRefresh: props.onModelsRefresh });
|
|
2259
|
+
}
|
|
2260
|
+
function Dialog({ title, onClose, children, t }) {
|
|
2261
|
+
const ref = (0, import_react8.useRef)(null), titleId = (0, import_react8.useId)();
|
|
2262
|
+
(0, import_react8.useEffect)(() => {
|
|
2263
|
+
const dialog = ref.current;
|
|
2264
|
+
dialog.showModal();
|
|
2265
|
+
return () => {
|
|
2266
|
+
dialog.close();
|
|
2267
|
+
};
|
|
2268
|
+
}, []);
|
|
2269
|
+
return h8(
|
|
2270
|
+
"dialog",
|
|
2271
|
+
{
|
|
2272
|
+
ref,
|
|
2273
|
+
className: "arcfold-dialog",
|
|
2274
|
+
"aria-labelledby": titleId,
|
|
2275
|
+
onCancel: (event) => {
|
|
2276
|
+
event.preventDefault();
|
|
2277
|
+
onClose();
|
|
2278
|
+
}
|
|
2279
|
+
},
|
|
2280
|
+
h8(
|
|
2281
|
+
"header",
|
|
2282
|
+
{ className: "arcfold-toolbar" },
|
|
2283
|
+
h8("h2", { id: titleId }, title),
|
|
2284
|
+
h8("button", { type: "button", onClick: onClose, autoFocus: true }, t("collaborationClose"))
|
|
2285
|
+
),
|
|
2286
|
+
children
|
|
2287
|
+
);
|
|
2288
|
+
}
|
|
2289
|
+
function CollaborationView(props) {
|
|
2290
|
+
const scope = props.useBrowseScope ? props.useBrowseScope((value) => value) : { viewScope: "all", epoch: 0 };
|
|
2291
|
+
const [modelsOpen, setModelsOpen] = (0, import_react8.useState)(false);
|
|
2292
|
+
(0, import_react8.useEffect)(() => {
|
|
2293
|
+
setModelsOpen(false);
|
|
2294
|
+
}, [props.opportunitySessionId]);
|
|
2295
|
+
return h8(
|
|
2296
|
+
"section",
|
|
2297
|
+
{ className: "arcfold-view", "aria-label": props.t("collaborationTitle") },
|
|
2298
|
+
h8(
|
|
2299
|
+
"header",
|
|
2300
|
+
{ className: "arcfold-toolbar" },
|
|
2301
|
+
h8("h2", null, props.t("collaborationTitle")),
|
|
2302
|
+
h8("button", { type: "button", onClick: () => props.onOpenModels ? props.onOpenModels() : setModelsOpen(true) }, props.t("modelsTitle"))
|
|
2303
|
+
),
|
|
2304
|
+
h8(
|
|
2305
|
+
"div",
|
|
2306
|
+
{ className: "arcfold-view-scope", role: "group", "aria-label": props.t("collaborationViewScope") },
|
|
2307
|
+
["all", "current"].map((viewScope) => h8("button", {
|
|
2308
|
+
key: viewScope,
|
|
2309
|
+
type: "button",
|
|
2310
|
+
"aria-pressed": scope.viewScope === viewScope,
|
|
2311
|
+
onClick: () => props.onViewScopeChange?.(viewScope)
|
|
2312
|
+
}, props.t(viewScope === "all" ? "collaborationAll" : "collaborationCurrent")))
|
|
2313
|
+
),
|
|
2314
|
+
h8(Panels, { ...props, viewScope: scope.viewScope, key: `${props.opportunitySessionId}:${scope.epoch}` }),
|
|
2315
|
+
modelsOpen && h8(Dialog, { title: props.t("modelsTitle"), t: props.t, onClose: () => setModelsOpen(false) }, h8(Models, props))
|
|
2316
|
+
);
|
|
2317
|
+
}
|
|
2318
|
+
function CollaborationHero(props) {
|
|
2319
|
+
const [panel, setPanel] = (0, import_react8.useState)(null);
|
|
2320
|
+
(0, import_react8.useEffect)(() => {
|
|
2321
|
+
setPanel(null);
|
|
2322
|
+
}, [props.opportunitySessionId]);
|
|
2323
|
+
return h8(
|
|
2324
|
+
"div",
|
|
2325
|
+
{ className: "arcfold-hero-entry" },
|
|
2326
|
+
h8("button", { type: "button", onClick: () => setPanel("collaboration") }, props.t("collaborationTitle")),
|
|
2327
|
+
h8("button", { type: "button", onClick: () => setPanel("models") }, props.t("modelsTitle")),
|
|
2328
|
+
panel && h8(
|
|
2329
|
+
Dialog,
|
|
2330
|
+
{
|
|
2331
|
+
title: props.t(panel === "models" ? "modelsTitle" : "collaborationTitle"),
|
|
2332
|
+
t: props.t,
|
|
2333
|
+
onClose: () => setPanel(null)
|
|
2334
|
+
},
|
|
2335
|
+
panel === "models" ? h8(Models, props) : h8(CollaborationView, { ...props, onOpenModels: () => setPanel("models") })
|
|
2336
|
+
)
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// src/opportunity-source.mjs
|
|
2341
|
+
async function callOpportunity(rpc, endpoint, payload, signal) {
|
|
2342
|
+
const result = await rpc.call("/api", `arcfoldOpportunities/${endpoint}`, { args: { request: payload } }, signal);
|
|
2343
|
+
if (result?.ok !== true) throw Object.assign(new Error(result?.error?.message ?? "机会请求未完成。"), { code: result?.error?.code ?? "opportunity_request_failed", remote: true });
|
|
2344
|
+
return result.value;
|
|
2345
|
+
}
|
|
2346
|
+
function createOpportunitySource({ rpc, sessionId, viewScope, subscribeReset, intervalMs = 5e3 }) {
|
|
2347
|
+
viewScopeFields(viewScope);
|
|
2348
|
+
let state = { items: [], total: 0, offset: 0, nextOffset: null, loading: true, permission: null, error: null, resetVersion: 0 };
|
|
2349
|
+
let generation = 0, disposed = false, controller, timer, reset;
|
|
2350
|
+
const listeners = /* @__PURE__ */ new Set(), reads = /* @__PURE__ */ new Set();
|
|
2351
|
+
const publish = (value) => {
|
|
2352
|
+
state = { ...state, ...value };
|
|
2353
|
+
for (const listener of listeners) listener();
|
|
2354
|
+
};
|
|
2355
|
+
function invalidateReads() {
|
|
2356
|
+
controller?.abort();
|
|
2357
|
+
generation++;
|
|
2358
|
+
for (const read of reads) read.abort();
|
|
2359
|
+
publish({
|
|
2360
|
+
items: [],
|
|
2361
|
+
total: 0,
|
|
2362
|
+
offset: 0,
|
|
2363
|
+
nextOffset: null,
|
|
2364
|
+
loading: true,
|
|
2365
|
+
permission: null,
|
|
2366
|
+
canPrepare: false,
|
|
2367
|
+
canContinue: false,
|
|
2368
|
+
error: null,
|
|
2369
|
+
resetVersion: state.resetVersion + 1
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
async function refresh(offset = state.offset) {
|
|
2373
|
+
if (disposed) return;
|
|
2374
|
+
controller?.abort();
|
|
2375
|
+
controller = new AbortController();
|
|
2376
|
+
const signal = controller.signal, current = ++generation;
|
|
2377
|
+
try {
|
|
2378
|
+
const value = await callOpportunity(rpc, "list", { sessionId, offset, limit: 20, ...viewScopeFields(viewScope) }, signal);
|
|
2379
|
+
if (!disposed && current === generation && !signal.aborted) publish({ ...value, offset, loading: false, error: null });
|
|
2380
|
+
} catch (error) {
|
|
2381
|
+
if (!disposed && current === generation && !signal.aborted) publish({ loading: false, error: error.message });
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
const assertLive = () => {
|
|
2385
|
+
if (disposed) throw Object.assign(new Error("机会视图已关闭。"), { code: "opportunity_view_disposed" });
|
|
2386
|
+
};
|
|
2387
|
+
const stop = () => {
|
|
2388
|
+
clearInterval(timer);
|
|
2389
|
+
timer = void 0;
|
|
2390
|
+
reset?.();
|
|
2391
|
+
reset = null;
|
|
2392
|
+
invalidateReads();
|
|
2393
|
+
};
|
|
2394
|
+
return {
|
|
2395
|
+
getSnapshot: () => state,
|
|
2396
|
+
subscribe(listener) {
|
|
2397
|
+
if (disposed) return () => {
|
|
2398
|
+
};
|
|
2399
|
+
listeners.add(listener);
|
|
2400
|
+
if (listeners.size === 1) {
|
|
2401
|
+
void refresh();
|
|
2402
|
+
timer = setInterval(() => {
|
|
2403
|
+
void refresh();
|
|
2404
|
+
}, intervalMs);
|
|
2405
|
+
reset = subscribeReset(() => {
|
|
2406
|
+
invalidateReads();
|
|
2407
|
+
void refresh(0);
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
return () => {
|
|
2411
|
+
listeners.delete(listener);
|
|
2412
|
+
if (!listeners.size) stop();
|
|
2413
|
+
};
|
|
2414
|
+
},
|
|
2415
|
+
refresh,
|
|
2416
|
+
setViewScope(value) {
|
|
2417
|
+
requireViewScope(value);
|
|
2418
|
+
if (disposed || viewScope === value) return;
|
|
2419
|
+
viewScope = value;
|
|
2420
|
+
invalidateReads();
|
|
2421
|
+
if (listeners.size) void refresh(0);
|
|
2422
|
+
},
|
|
2423
|
+
async details(opportunityId) {
|
|
2424
|
+
assertLive();
|
|
2425
|
+
const version2 = state.resetVersion, read = new AbortController();
|
|
2426
|
+
reads.add(read);
|
|
2427
|
+
try {
|
|
2428
|
+
const value = await callOpportunity(rpc, "details", { sessionId, opportunityId, ...viewScopeFields(viewScope) }, read.signal);
|
|
2429
|
+
assertLive();
|
|
2430
|
+
if (read.signal.aborted || version2 !== state.resetVersion)
|
|
2431
|
+
throw Object.assign(new Error("查看范围或连接已变化,请重新查看机会。"), { code: "opportunity_view_reset" });
|
|
2432
|
+
return value;
|
|
2433
|
+
} finally {
|
|
2434
|
+
reads.delete(read);
|
|
2435
|
+
}
|
|
2436
|
+
},
|
|
2437
|
+
source: (sourceId) => {
|
|
2438
|
+
assertLive();
|
|
2439
|
+
return callOpportunity(rpc, "source", { sourceId });
|
|
2440
|
+
},
|
|
2441
|
+
async act(endpoint, request) {
|
|
2442
|
+
assertLive();
|
|
2443
|
+
const payload = ["savePermission", "continue"].includes(endpoint) ? { ...request, sessionId } : request;
|
|
2444
|
+
const value = await callOpportunity(rpc, endpoint, payload);
|
|
2445
|
+
await refresh();
|
|
2446
|
+
return value;
|
|
2447
|
+
},
|
|
2448
|
+
dispose() {
|
|
2449
|
+
disposed = true;
|
|
2450
|
+
stop();
|
|
2451
|
+
listeners.clear();
|
|
2452
|
+
}
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
// src/opportunity-locales.mjs
|
|
2457
|
+
var opportunitiesZh = {
|
|
2458
|
+
opportunityReadOnly: "这里汇集值得留意的建议与已有准备稿,供你阅读和判断。",
|
|
2459
|
+
opportunityRationale: "为什么值得关注",
|
|
2460
|
+
opportunitySuggestion: "可以准备什么",
|
|
2461
|
+
opportunityWorkspace: "工作目录",
|
|
2462
|
+
opportunityDismissed: "已忽略",
|
|
2463
|
+
opportunityTitle: "机会与准备",
|
|
2464
|
+
opportunityOpen: "查看机会",
|
|
2465
|
+
opportunityHide: "收起",
|
|
2466
|
+
opportunityEmpty: "暂无已提交的机会。",
|
|
2467
|
+
opportunityBoundary: "机会是待判断的建议。许可只允许在选定材料范围内准备一份新稿,不代表采纳或继续执行。",
|
|
2468
|
+
opportunityPermission: "自动准备范围",
|
|
2469
|
+
opportunityEnable: "允许本交流的新机会自动准备(含未来消息)",
|
|
2470
|
+
opportunityFiles: "本工作目录内的材料文件,每行一个路径,最多四个",
|
|
2471
|
+
opportunitySources: "附加原件编号,每行一个;只授权这些具体原件",
|
|
2472
|
+
opportunityCapacity: "每个文件最多 256 KiB,合计 512 KiB;完整材料最多 1 MiB。每份新稿最多 64 KiB、4 次模型请求、6 次获准工具动作、5 分钟。这是单次容量限制,不是费用上限。",
|
|
2473
|
+
opportunitySave: "保存范围许可",
|
|
2474
|
+
opportunityOff: "自动准备未开启",
|
|
2475
|
+
opportunityOn: "自动准备已开启",
|
|
2476
|
+
opportunityVersion: "许可版本",
|
|
2477
|
+
opportunityHistorical: "开启前已有的机会不会自动启动;需要逐项选择准备。",
|
|
2478
|
+
opportunityPrepare: "准备这一项",
|
|
2479
|
+
opportunityStop: "停止本次准备",
|
|
2480
|
+
opportunityDismiss: "忽略机会",
|
|
2481
|
+
opportunityRead: "标为已读",
|
|
2482
|
+
opportunityContinue: "发送新的委托",
|
|
2483
|
+
opportunityText: "你现在希望怎样继续?请写完整原话。",
|
|
2484
|
+
opportunityMainRequired: "请先打开原来的主交流,再发送继续工作的要求。",
|
|
2485
|
+
opportunityDraft: "已写入并全文回读的准备稿",
|
|
2486
|
+
opportunityEvidence: "冻结时的原件、角色、版本及全部材料",
|
|
2487
|
+
opportunityNotUsefulYet: "完成文件检查不证明建议有用;请审阅内容后决定。",
|
|
2488
|
+
opportunitySaved: "决定已保存。",
|
|
2489
|
+
opportunityFailed: "提交未开始执行,原记录已保留,不会自动重发。",
|
|
2490
|
+
opportunityUnknown: "提交结果未确认,保留原记录,不自动重发。",
|
|
2491
|
+
opportunityAccepted: "原生入口已受理;实际执行仍以工作记录为准。",
|
|
2492
|
+
opportunityRejected: "材料检查未通过,尚未执行",
|
|
2493
|
+
opportunityPending: "尚未启动",
|
|
2494
|
+
opportunityPrepared: "准备提交中",
|
|
2495
|
+
opportunitySubmitted: "已提交原生执行",
|
|
2496
|
+
opportunityUncertain: "结果未确定,需要查看",
|
|
2497
|
+
opportunityStopped: "已停止或依据失效",
|
|
2498
|
+
opportunitySettled: "完成记录已保留",
|
|
2499
|
+
opportunityUnread: "未读",
|
|
2500
|
+
opportunityAlreadyRead: "已读"
|
|
2501
|
+
};
|
|
2502
|
+
var opportunitiesEn = {
|
|
2503
|
+
opportunityReadOnly: "Suggestions and existing drafts, collected here for you to read and consider.",
|
|
2504
|
+
opportunityRationale: "Why it matters",
|
|
2505
|
+
opportunitySuggestion: "Suggested preparation",
|
|
2506
|
+
opportunityWorkspace: "Workspace",
|
|
2507
|
+
opportunityDismissed: "Dismissed",
|
|
2508
|
+
opportunityTitle: "Opportunities and drafts",
|
|
2509
|
+
opportunityOpen: "View opportunities",
|
|
2510
|
+
opportunityHide: "Hide",
|
|
2511
|
+
opportunityEmpty: "No committed opportunities yet.",
|
|
2512
|
+
opportunityBoundary: "An opportunity is a suggestion to review. Permission allows one new draft using selected material; it does not authorize adoption or further work.",
|
|
2513
|
+
opportunityPermission: "Automatic preparation scope",
|
|
2514
|
+
opportunityEnable: "Allow new opportunities in this conversation, including future messages, to prepare a draft",
|
|
2515
|
+
opportunityFiles: "Material files inside this workspace, one path per line, at most four",
|
|
2516
|
+
opportunitySources: "Additional original source IDs, one per line; only these exact originals are authorized",
|
|
2517
|
+
opportunityCapacity: "Each file: 256 KiB; combined files: 512 KiB; complete input: 1 MiB. Each draft: 64 KiB, 4 model requests, 6 authorized tool actions and 5 minutes. These are per-attempt capacity limits, not spending limits.",
|
|
2518
|
+
opportunitySave: "Save scope permission",
|
|
2519
|
+
opportunityOff: "Automatic preparation is off",
|
|
2520
|
+
opportunityOn: "Automatic preparation is on",
|
|
2521
|
+
opportunityVersion: "Permission version",
|
|
2522
|
+
opportunityHistorical: "Existing opportunities do not start when permission is enabled. Select each one explicitly.",
|
|
2523
|
+
opportunityPrepare: "Prepare this",
|
|
2524
|
+
opportunityStop: "Stop preparation",
|
|
2525
|
+
opportunityDismiss: "Dismiss opportunity",
|
|
2526
|
+
opportunityRead: "Mark read",
|
|
2527
|
+
opportunityContinue: "Send new instruction",
|
|
2528
|
+
opportunityText: "What should happen next? Enter your complete new instruction.",
|
|
2529
|
+
opportunityMainRequired: "Open the original main conversation before sending a new instruction.",
|
|
2530
|
+
opportunityDraft: "Draft written and fully read back",
|
|
2531
|
+
opportunityEvidence: "Originals, roles, versions and complete frozen material",
|
|
2532
|
+
opportunityNotUsefulYet: "File verification does not prove usefulness. Review the content before deciding.",
|
|
2533
|
+
opportunitySaved: "Decision saved.",
|
|
2534
|
+
opportunityFailed: "Execution did not start. The original submission is retained and will not be resent automatically.",
|
|
2535
|
+
opportunityUnknown: "Submission is unconfirmed. Its original record is retained and will not be resent automatically.",
|
|
2536
|
+
opportunityAccepted: "The native entry accepted the submission. Actual work is shown by its execution record.",
|
|
2537
|
+
opportunityRejected: "Admission refused; no execution occurred",
|
|
2538
|
+
opportunityPending: "Not started",
|
|
2539
|
+
opportunityPrepared: "Preparing submission",
|
|
2540
|
+
opportunitySubmitted: "Submitted to native execution",
|
|
2541
|
+
opportunityUncertain: "Outcome unknown; review needed",
|
|
2542
|
+
opportunityStopped: "Stopped or supporting material unavailable",
|
|
2543
|
+
opportunitySettled: "Completion record retained",
|
|
2544
|
+
opportunityUnread: "Unread",
|
|
2545
|
+
opportunityAlreadyRead: "Read"
|
|
2546
|
+
};
|
|
2547
|
+
|
|
2548
|
+
// src/understanding-source.mjs
|
|
2549
|
+
function createUnderstandingSource({ rpc, sessionId, viewScope, subscribeReset, intervalMs = 5e3 }) {
|
|
2550
|
+
viewScopeFields(viewScope);
|
|
2551
|
+
let snapshot = { loading: true, error: null, items: [], total: 0, offset: 0, nextOffset: null, readEpoch: 0 };
|
|
2552
|
+
let generation = 0, controller, timer, reset, disposed = false;
|
|
2553
|
+
const listeners = /* @__PURE__ */ new Set(), reads = /* @__PURE__ */ new Set();
|
|
2554
|
+
const stopped = () => Object.assign(new Error("这次读取已经结束,请重新打开。"), { code: "understanding_read_cancelled" });
|
|
2555
|
+
const call = async (endpoint, payload, signal) => {
|
|
2556
|
+
if (disposed) throw stopped();
|
|
2557
|
+
const scope = ["list", "details"].includes(endpoint) ? viewScopeFields(viewScope) : {};
|
|
2558
|
+
const result = await rpc.call("/api", `arcfoldUnderstandings/${endpoint}`, { args: { request: { ...payload, sessionId, ...scope } } }, signal);
|
|
2559
|
+
if (result?.ok !== true) throw Object.assign(
|
|
2560
|
+
new Error(result?.error?.message || "理解请求未完成。"),
|
|
2561
|
+
{ code: result?.error?.code || "understanding_request_failed", remote: true }
|
|
2562
|
+
);
|
|
2563
|
+
return result.value;
|
|
2564
|
+
};
|
|
2565
|
+
const publish = (value) => {
|
|
2566
|
+
snapshot = { ...snapshot, ...value };
|
|
2567
|
+
for (const listener of listeners) listener();
|
|
2568
|
+
};
|
|
2569
|
+
function invalidateReads() {
|
|
2570
|
+
controller?.abort();
|
|
2571
|
+
generation++;
|
|
2572
|
+
for (const read of reads) read.abort();
|
|
2573
|
+
publish({ loading: true, error: null, items: [], total: 0, offset: 0, nextOffset: null, readEpoch: snapshot.readEpoch + 1 });
|
|
2574
|
+
}
|
|
2575
|
+
async function refresh(offset = snapshot.offset) {
|
|
2576
|
+
if (disposed) return;
|
|
2577
|
+
controller?.abort();
|
|
2578
|
+
controller = new AbortController();
|
|
2579
|
+
const current = ++generation, signal = controller.signal;
|
|
2580
|
+
try {
|
|
2581
|
+
const result = await call("list", { offset, limit: 20 }, signal);
|
|
2582
|
+
if (current === generation && !disposed && !signal.aborted) publish({ ...result, offset, loading: false, error: null });
|
|
2583
|
+
} catch (error) {
|
|
2584
|
+
if (current === generation && !disposed && !signal.aborted) publish({ loading: false, error: error.message });
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
async function details(understandingId) {
|
|
2588
|
+
if (disposed) throw stopped();
|
|
2589
|
+
const read = new AbortController(), epoch = snapshot.readEpoch;
|
|
2590
|
+
reads.add(read);
|
|
2591
|
+
try {
|
|
2592
|
+
const result = await call("details", { understandingId }, read.signal);
|
|
2593
|
+
if (disposed || read.signal.aborted || epoch !== snapshot.readEpoch) throw stopped();
|
|
2594
|
+
return result;
|
|
2595
|
+
} finally {
|
|
2596
|
+
reads.delete(read);
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
function stop() {
|
|
2600
|
+
clearInterval(timer);
|
|
2601
|
+
timer = void 0;
|
|
2602
|
+
reset?.();
|
|
2603
|
+
reset = void 0;
|
|
2604
|
+
invalidateReads();
|
|
2605
|
+
}
|
|
2606
|
+
return {
|
|
2607
|
+
getSnapshot: () => snapshot,
|
|
2608
|
+
subscribe(listener) {
|
|
2609
|
+
if (disposed) return () => {
|
|
2610
|
+
};
|
|
2611
|
+
listeners.add(listener);
|
|
2612
|
+
if (listeners.size === 1) {
|
|
2613
|
+
void refresh();
|
|
2614
|
+
timer = setInterval(() => {
|
|
2615
|
+
void refresh();
|
|
2616
|
+
}, intervalMs);
|
|
2617
|
+
reset = subscribeReset(() => {
|
|
2618
|
+
invalidateReads();
|
|
2619
|
+
void refresh(0);
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
return () => {
|
|
2623
|
+
listeners.delete(listener);
|
|
2624
|
+
if (!listeners.size) stop();
|
|
2625
|
+
};
|
|
2626
|
+
},
|
|
2627
|
+
refresh,
|
|
2628
|
+
details,
|
|
2629
|
+
setViewScope(value) {
|
|
2630
|
+
requireViewScope(value);
|
|
2631
|
+
if (disposed || viewScope === value) return;
|
|
2632
|
+
viewScope = value;
|
|
2633
|
+
invalidateReads();
|
|
2634
|
+
if (listeners.size) void refresh(0);
|
|
2635
|
+
},
|
|
2636
|
+
decide: (request) => call("decide", request),
|
|
2637
|
+
correct: (request) => call("correct", request),
|
|
2638
|
+
dispose() {
|
|
2639
|
+
disposed = true;
|
|
2640
|
+
stop();
|
|
2641
|
+
listeners.clear();
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
// src/understanding-locales.mjs
|
|
2647
|
+
var understandingZh = {
|
|
2648
|
+
understandingTitle: "理解与依据",
|
|
2649
|
+
understandingOpen: "查看理解",
|
|
2650
|
+
understandingHide: "收起理解",
|
|
2651
|
+
understandingEmpty: "还没有可查看的理解。",
|
|
2652
|
+
understandingLoading: "正在读取理解…",
|
|
2653
|
+
understandingBoundary: "这里保留原话、AI 的推断和后续纠正。可采用表示依据仍有效,不代表已经证实,也不授予执行权限。",
|
|
2654
|
+
understandingAvailable: "可采用",
|
|
2655
|
+
understandingUnavailable: "已停止采用或依据不可用",
|
|
2656
|
+
understandingInference: "AI 推断",
|
|
2657
|
+
understandingLiteral: "你的明确陈述",
|
|
2658
|
+
understandingQuestion: "待确认的问题",
|
|
2659
|
+
understandingStop: "停止采用这条理解",
|
|
2660
|
+
understandingWithdraw: "停止采用这条来源",
|
|
2661
|
+
understandingWithdrawalHelp: "停止来源会影响依赖它的理解、方法和目标依据;原聊天记录仍保留,已发出的工作不会撤销,目标的追踪状态不会改变。",
|
|
2662
|
+
understandingCorrect: "纠正这条理解",
|
|
2663
|
+
understandingReplacement: "完整替换为",
|
|
2664
|
+
understandingCorrectionHelp: "请写出这条理解应当完整替换成的内容。原话会进入当前对话,在下一次尚未发出的步骤中核对并采用;旧版本仍可查看。",
|
|
2665
|
+
understandingSubmit: "发送纠正原话",
|
|
2666
|
+
understandingCancel: "取消编辑",
|
|
2667
|
+
understandingSaved: "操作已记录。",
|
|
2668
|
+
understandingPending: "已送入对话,尚未确认采用。请刷新查看实际结果。",
|
|
2669
|
+
understandingUnknown: "发送结果未确认,未自动重发。请刷新查看记录。",
|
|
2670
|
+
understandingApplied: "已采用",
|
|
2671
|
+
understandingFailed: "未采用",
|
|
2672
|
+
understandingAwaiting: "等待实际采用",
|
|
2673
|
+
understandingAdmission: "对话接收",
|
|
2674
|
+
understandingAccepted: "已接收",
|
|
2675
|
+
understandingAdmissionFailed: "未送入",
|
|
2676
|
+
understandingAdmissionUnknown: "未确认",
|
|
2677
|
+
understandingVersion: "内容版本",
|
|
2678
|
+
understandingHistory: "保留的版本",
|
|
2679
|
+
understandingCommands: "操作记录",
|
|
2680
|
+
understandingSuccessors: "后续版本",
|
|
2681
|
+
understandingEffects: "受影响的记录",
|
|
2682
|
+
understandingGoalEffects: "目标依据变化",
|
|
2683
|
+
understandingSource: "原始来源",
|
|
2684
|
+
understandingProvenance: "查看来源信息",
|
|
2685
|
+
understandingMissing: "来源缺失"
|
|
2686
|
+
};
|
|
2687
|
+
var understandingEn = {
|
|
2688
|
+
understandingTitle: "Understanding and evidence",
|
|
2689
|
+
understandingOpen: "View understanding",
|
|
2690
|
+
understandingHide: "Hide understanding",
|
|
2691
|
+
understandingEmpty: "No understanding is available yet.",
|
|
2692
|
+
understandingLoading: "Loading understanding…",
|
|
2693
|
+
understandingBoundary: "Original statements, AI inferences and corrections are retained. Eligible evidence is neither verified truth nor permission to act.",
|
|
2694
|
+
understandingAvailable: "Eligible",
|
|
2695
|
+
understandingUnavailable: "Stopped or evidence unavailable",
|
|
2696
|
+
understandingInference: "AI inference",
|
|
2697
|
+
understandingLiteral: "Your explicit statement",
|
|
2698
|
+
understandingQuestion: "Open question",
|
|
2699
|
+
understandingStop: "Stop using this understanding",
|
|
2700
|
+
understandingWithdraw: "Stop using this source",
|
|
2701
|
+
understandingWithdrawalHelp: "This affects dependent understanding, methods and goal evidence. Native history remains; sent work is not undone and goal tracking is unchanged.",
|
|
2702
|
+
understandingCorrect: "Correct this understanding",
|
|
2703
|
+
understandingReplacement: "Full replacement text",
|
|
2704
|
+
understandingCorrectionHelp: "Write the complete replacement. Your exact words enter this conversation and are checked before the next unsent step. Earlier versions remain available.",
|
|
2705
|
+
understandingSubmit: "Send correction",
|
|
2706
|
+
understandingCancel: "Cancel editing",
|
|
2707
|
+
understandingSaved: "Operation recorded.",
|
|
2708
|
+
understandingPending: "Admitted to the conversation; application is not confirmed. Refresh to see the actual result.",
|
|
2709
|
+
understandingUnknown: "Admission is unconfirmed. Nothing was resent automatically. Refresh the record.",
|
|
2710
|
+
understandingApplied: "Applied",
|
|
2711
|
+
understandingFailed: "Not applied",
|
|
2712
|
+
understandingAwaiting: "Awaiting application",
|
|
2713
|
+
understandingAdmission: "Conversation admission",
|
|
2714
|
+
understandingAccepted: "Accepted",
|
|
2715
|
+
understandingAdmissionFailed: "Not sent",
|
|
2716
|
+
understandingAdmissionUnknown: "Unknown",
|
|
2717
|
+
understandingVersion: "Content version",
|
|
2718
|
+
understandingHistory: "Retained versions",
|
|
2719
|
+
understandingCommands: "Operations",
|
|
2720
|
+
understandingSuccessors: "Later versions",
|
|
2721
|
+
understandingEffects: "Affected records",
|
|
2722
|
+
understandingGoalEffects: "Goal evidence changes",
|
|
2723
|
+
understandingSource: "Original source",
|
|
2724
|
+
understandingProvenance: "View provenance",
|
|
2725
|
+
understandingMissing: "Source missing"
|
|
2726
|
+
};
|
|
2727
|
+
|
|
2728
|
+
// src/model-settings-locales.mjs
|
|
2729
|
+
var modelsZh = {
|
|
2730
|
+
modelsPhaseReady: "已就绪",
|
|
2731
|
+
modelsPhaseStarting: "启动中",
|
|
2732
|
+
modelsPhaseRestart: "需重启",
|
|
2733
|
+
modelsPhaseFailed: "启动失败",
|
|
2734
|
+
modelsPhaseClosed: "已停止",
|
|
2735
|
+
modelsReasonNoModel: "尚未选择模型。",
|
|
2736
|
+
modelsReasonCredential: "尚未填写所选提供方的凭据。",
|
|
2737
|
+
modelsReasonUnsupported: "所选模型或思考强度不受当前部署支持。",
|
|
2738
|
+
modelsReasonChanged: "设置刚刚变化,请刷新查看。",
|
|
2739
|
+
modelsReasonNotReady: "当前尚未就绪,请稍后刷新。",
|
|
2740
|
+
modelsReasonRestart: "运行配置已变化,请重启后再使用。",
|
|
2741
|
+
modelsReasonUnknown: "暂时无法使用,请刷新查看。",
|
|
2742
|
+
modelsEffortOff: "关闭",
|
|
2743
|
+
modelsEffortLow: "低",
|
|
2744
|
+
modelsEffortMedium: "中",
|
|
2745
|
+
modelsEffortHigh: "高",
|
|
2746
|
+
modelsEffortXHigh: "很高",
|
|
2747
|
+
modelsEffortMax: "最高",
|
|
2748
|
+
modelsEffortOther: "其他",
|
|
2749
|
+
modelsTitle: "模型与学习",
|
|
2750
|
+
modelsRefresh: "刷新状态",
|
|
2751
|
+
modelsLoading: "正在读取设置…",
|
|
2752
|
+
modelsReadFailed: "未能读取当前设置;暂不可保存,请刷新后再试。",
|
|
2753
|
+
modelsUnavailable: "当前不可用",
|
|
2754
|
+
modelsMain: "主会话模型",
|
|
2755
|
+
modelsMainScope: "在会话输入区使用原生模型按钮。更改影响该会话下一次回答,并保存为新会话默认;其他已有会话保留自己的选择。",
|
|
2756
|
+
modelsDefault: "新会话默认",
|
|
2757
|
+
modelsLearn: "回答后的学习",
|
|
2758
|
+
modelsLearnScope: "这里独立选择学习模型。已开始的学习保留原配置;修改只影响之后开始的学习,不重发未知请求。",
|
|
2759
|
+
modelsLearnDisabled: "此部署未启用学习。",
|
|
2760
|
+
modelsNoDefault: "尚无可用的学习默认配置。",
|
|
2761
|
+
modelsProvider: "提供方",
|
|
2762
|
+
modelsModel: "模型",
|
|
2763
|
+
modelsReasoning: "思考强度",
|
|
2764
|
+
modelsChoose: "请选择",
|
|
2765
|
+
modelsSave: "保存学习设置",
|
|
2766
|
+
modelsSaved: "已保存并读回。",
|
|
2767
|
+
modelsSavedUnread: "保存已返回成功,但最新状态未能读回;请刷新确认。",
|
|
2768
|
+
modelsConflict: "设置已变化,未覆盖。已尝试刷新,请查看当前版本后重新选择。",
|
|
2769
|
+
modelsRefused: "保存被拒绝,设置未获确认。请刷新状态后检查可用选项。",
|
|
2770
|
+
modelsUnknown: "未收到明确结果;没有自动重发。请刷新核对后再决定是否重新操作。",
|
|
2771
|
+
modelsBusy: "正在处理…",
|
|
2772
|
+
modelsCredentials: "连接凭据",
|
|
2773
|
+
modelsCredentialScope: "仅写入官方凭据存储,不显示旧密钥。“已配置”不代表已通过服务商验证。这里不会发送模型测试请求。",
|
|
2774
|
+
modelsConfigured: "已配置",
|
|
2775
|
+
modelsNotConfigured: "未配置",
|
|
2776
|
+
modelsCredentialReadFailed: "凭据状态暂不可读。",
|
|
2777
|
+
modelsReadOnly: "当前凭据来源不可写。",
|
|
2778
|
+
modelsCredential: "新密钥",
|
|
2779
|
+
modelsCredentialSave: "保存密钥",
|
|
2780
|
+
modelsCredentialRemove: "移除已存密钥",
|
|
2781
|
+
modelsCredentialCancel: "清空输入",
|
|
2782
|
+
modelsCredentialFailed: "凭据操作未获确认;输入已清空。请刷新状态后重试。",
|
|
2783
|
+
modelsCredentialUnknown: "凭据操作结果未知,输入已清空且未自动重发。请先刷新核对状态。",
|
|
2784
|
+
modelsSource: "来源",
|
|
2785
|
+
modelsNoRoutes: "没有已部署的可用路线。",
|
|
2786
|
+
modelsUsage: "用量与费用",
|
|
2787
|
+
modelsUsageMissing: "当前用量不可读;不按零费用显示。",
|
|
2788
|
+
modelsRequests: "请求数",
|
|
2789
|
+
modelsUnknownCount: "结果未知",
|
|
2790
|
+
modelsSpent: "已结算",
|
|
2791
|
+
modelsReserved: "预留",
|
|
2792
|
+
modelsCeiling: "上限",
|
|
2793
|
+
modelsNoCeiling: "此范围无金额上限",
|
|
2794
|
+
modelsPrompt: "输入 token",
|
|
2795
|
+
modelsCompletion: "输出 token",
|
|
2796
|
+
modelsCached: "缓存输入 token",
|
|
2797
|
+
modelsPending: "待结算",
|
|
2798
|
+
modelsTokensOnly: "这里只记录 token 用量,未提供人民币费用估算。",
|
|
2799
|
+
modelsUsageBoundary: "各范围独立显示;网页用量不与开发预算相加。改变模型设置不修改费用授权。",
|
|
2800
|
+
modelsScopeLimited: "开发研究预算(独立范围)",
|
|
2801
|
+
modelsScopeWeb: "DeepSeek 网页",
|
|
2802
|
+
modelsScopeOpenai: "OpenAI 网页",
|
|
2803
|
+
modelsGate: "运行状态",
|
|
2804
|
+
modelsRevision: "设置版本"
|
|
2805
|
+
};
|
|
2806
|
+
var modelsEn = {
|
|
2807
|
+
modelsPhaseReady: "Ready",
|
|
2808
|
+
modelsPhaseStarting: "Starting",
|
|
2809
|
+
modelsPhaseRestart: "Restart required",
|
|
2810
|
+
modelsPhaseFailed: "Startup failed",
|
|
2811
|
+
modelsPhaseClosed: "Stopped",
|
|
2812
|
+
modelsReasonNoModel: "No model is selected.",
|
|
2813
|
+
modelsReasonCredential: "Credentials for the selected provider have not been entered.",
|
|
2814
|
+
modelsReasonUnsupported: "The selected model or reasoning effort is not supported by this deployment.",
|
|
2815
|
+
modelsReasonChanged: "Settings just changed. Refresh to see the current selection.",
|
|
2816
|
+
modelsReasonNotReady: "The runtime is not ready. Refresh shortly.",
|
|
2817
|
+
modelsReasonRestart: "Runtime configuration changed. Restart before continuing.",
|
|
2818
|
+
modelsReasonUnknown: "Temporarily unavailable. Refresh to check again.",
|
|
2819
|
+
modelsEffortOff: "Off",
|
|
2820
|
+
modelsEffortLow: "Low",
|
|
2821
|
+
modelsEffortMedium: "Medium",
|
|
2822
|
+
modelsEffortHigh: "High",
|
|
2823
|
+
modelsEffortXHigh: "Very high",
|
|
2824
|
+
modelsEffortMax: "Maximum",
|
|
2825
|
+
modelsEffortOther: "Other",
|
|
2826
|
+
modelsTitle: "Models and learning",
|
|
2827
|
+
modelsRefresh: "Refresh status",
|
|
2828
|
+
modelsLoading: "Loading settings…",
|
|
2829
|
+
modelsReadFailed: "Settings could not be read. Refresh before saving.",
|
|
2830
|
+
modelsUnavailable: "Unavailable",
|
|
2831
|
+
modelsMain: "Conversation model",
|
|
2832
|
+
modelsMainScope: "Use the native model picker in the conversation input. A change applies to that conversation’s next answer and the default for new conversations; other existing conversations keep their selection.",
|
|
2833
|
+
modelsDefault: "New conversation default",
|
|
2834
|
+
modelsLearn: "Learning after an answer",
|
|
2835
|
+
modelsLearnScope: "Learning has its own model selection. Started learning keeps its original configuration; changes apply only to learning started later and never resend unknown requests.",
|
|
2836
|
+
modelsLearnDisabled: "Learning is not enabled for this deployment.",
|
|
2837
|
+
modelsNoDefault: "No learning default is available.",
|
|
2838
|
+
modelsProvider: "Provider",
|
|
2839
|
+
modelsModel: "Model",
|
|
2840
|
+
modelsReasoning: "Reasoning effort",
|
|
2841
|
+
modelsChoose: "Select",
|
|
2842
|
+
modelsSave: "Save learning settings",
|
|
2843
|
+
modelsSaved: "Saved and read back.",
|
|
2844
|
+
modelsSavedUnread: "Save returned success, but the latest state could not be read. Refresh to confirm.",
|
|
2845
|
+
modelsConflict: "Settings changed and were not overwritten. A refresh was attempted; review the current version before selecting again.",
|
|
2846
|
+
modelsRefused: "Save was refused. Refresh and check the available choices.",
|
|
2847
|
+
modelsUnknown: "No definite result was received. Nothing was retried automatically. Refresh before deciding whether to submit again.",
|
|
2848
|
+
modelsBusy: "Working…",
|
|
2849
|
+
modelsCredentials: "Credentials",
|
|
2850
|
+
modelsCredentialScope: "Write only to the native credential store. Existing keys are never displayed. Configured does not mean verified with the provider; this page makes no model test calls.",
|
|
2851
|
+
modelsConfigured: "Configured",
|
|
2852
|
+
modelsNotConfigured: "Not configured",
|
|
2853
|
+
modelsCredentialReadFailed: "Credential status is unavailable.",
|
|
2854
|
+
modelsReadOnly: "This credential source is not writable.",
|
|
2855
|
+
modelsCredential: "New key",
|
|
2856
|
+
modelsCredentialSave: "Save key",
|
|
2857
|
+
modelsCredentialRemove: "Remove stored key",
|
|
2858
|
+
modelsCredentialCancel: "Clear input",
|
|
2859
|
+
modelsCredentialFailed: "Credential operation was not confirmed. The input was cleared. Refresh before trying again.",
|
|
2860
|
+
modelsCredentialUnknown: "Credential outcome is unknown. The input was cleared and nothing was retried. Refresh to check status.",
|
|
2861
|
+
modelsSource: "Source",
|
|
2862
|
+
modelsNoRoutes: "No supported route is deployed.",
|
|
2863
|
+
modelsUsage: "Usage and cost",
|
|
2864
|
+
modelsUsageMissing: "Usage is unavailable; it is not shown as zero.",
|
|
2865
|
+
modelsRequests: "Requests",
|
|
2866
|
+
modelsUnknownCount: "Unknown outcomes",
|
|
2867
|
+
modelsSpent: "Settled",
|
|
2868
|
+
modelsReserved: "Reserved",
|
|
2869
|
+
modelsCeiling: "Limit",
|
|
2870
|
+
modelsNoCeiling: "No monetary limit for this scope",
|
|
2871
|
+
modelsPrompt: "Input tokens",
|
|
2872
|
+
modelsCompletion: "Output tokens",
|
|
2873
|
+
modelsCached: "Cached input tokens",
|
|
2874
|
+
modelsPending: "Pending",
|
|
2875
|
+
modelsTokensOnly: "Token usage only. No CNY cost estimate is supplied.",
|
|
2876
|
+
modelsUsageBoundary: "Scopes are shown separately; web usage is not added to the development budget. Model settings do not change spending authorization.",
|
|
2877
|
+
modelsScopeLimited: "Development research budget (separate)",
|
|
2878
|
+
modelsScopeWeb: "DeepSeek web",
|
|
2879
|
+
modelsScopeOpenai: "OpenAI web",
|
|
2880
|
+
modelsGate: "Runtime status",
|
|
2881
|
+
modelsRevision: "Settings revision"
|
|
2882
|
+
};
|
|
2883
|
+
|
|
2884
|
+
// src/goal-source.mjs
|
|
2885
|
+
async function callGoal(rpc, endpoint, payload, signal) {
|
|
2886
|
+
const result = await rpc.call("/api", `arcfoldGoals/${endpoint}`, { args: { request: payload } }, signal);
|
|
2887
|
+
if (result?.ok !== true) throw Object.assign(
|
|
2888
|
+
new Error(result?.error?.message || "目标请求未完成。"),
|
|
2889
|
+
{ code: result?.error?.code || "goal_request_failed", remote: true }
|
|
2890
|
+
);
|
|
2891
|
+
return result.value;
|
|
2892
|
+
}
|
|
2893
|
+
function createGoalSource({ rpc, sessionId, viewScope, subscribeReset, intervalMs = 5e3 }) {
|
|
2894
|
+
viewScopeFields(viewScope);
|
|
2895
|
+
let snapshot = { loading: true, error: null, items: [], works: [], total: 0, offset: 0, nextOffset: null, readEpoch: 0 };
|
|
2896
|
+
let generation = 0, controller, timer, reset, disposed = false;
|
|
2897
|
+
const listeners = /* @__PURE__ */ new Set(), reads = /* @__PURE__ */ new Set();
|
|
2898
|
+
const publish = (value) => {
|
|
2899
|
+
snapshot = { ...snapshot, ...value };
|
|
2900
|
+
for (const listener of listeners) listener();
|
|
2901
|
+
};
|
|
2902
|
+
const cancelled = () => Object.assign(new Error("查看范围已变化,请重新打开目标。"), { code: "goal_read_cancelled" });
|
|
2903
|
+
function invalidateReads({ clear = true } = {}) {
|
|
2904
|
+
controller?.abort();
|
|
2905
|
+
generation++;
|
|
2906
|
+
for (const read of reads) read.abort();
|
|
2907
|
+
publish({
|
|
2908
|
+
loading: true,
|
|
2909
|
+
error: null,
|
|
2910
|
+
readEpoch: snapshot.readEpoch + 1,
|
|
2911
|
+
...clear ? { items: [], works: [], total: 0, offset: 0, nextOffset: null, canDiscuss: false } : {}
|
|
2912
|
+
});
|
|
2913
|
+
}
|
|
2914
|
+
async function refresh(offset = snapshot.offset) {
|
|
2915
|
+
if (disposed) return;
|
|
2916
|
+
controller?.abort();
|
|
2917
|
+
controller = new AbortController();
|
|
2918
|
+
const current = ++generation, signal = controller.signal;
|
|
2919
|
+
try {
|
|
2920
|
+
const result = await callGoal(rpc, "list", { sessionId, offset, limit: 20, ...viewScopeFields(viewScope) }, signal);
|
|
2921
|
+
if (current !== generation || signal.aborted || disposed) return;
|
|
2922
|
+
publish({ ...result, offset, loading: false, error: null });
|
|
2923
|
+
} catch (error) {
|
|
2924
|
+
if (current === generation && !signal.aborted && !disposed)
|
|
2925
|
+
publish({ loading: false, error: error instanceof Error ? error.message : "读取目标失败。" });
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
function stop() {
|
|
2929
|
+
clearInterval(timer);
|
|
2930
|
+
timer = void 0;
|
|
2931
|
+
reset?.();
|
|
2932
|
+
reset = void 0;
|
|
2933
|
+
invalidateReads();
|
|
2934
|
+
}
|
|
2935
|
+
return {
|
|
2936
|
+
getSnapshot: () => snapshot,
|
|
2937
|
+
subscribe(listener) {
|
|
2938
|
+
if (disposed) return () => {
|
|
2939
|
+
};
|
|
2940
|
+
listeners.add(listener);
|
|
2941
|
+
if (listeners.size === 1) {
|
|
2942
|
+
void refresh();
|
|
2943
|
+
timer = setInterval(() => {
|
|
2944
|
+
void refresh();
|
|
2945
|
+
}, intervalMs);
|
|
2946
|
+
reset = subscribeReset(() => {
|
|
2947
|
+
invalidateReads({ clear: false });
|
|
2948
|
+
void refresh();
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
return () => {
|
|
2952
|
+
listeners.delete(listener);
|
|
2953
|
+
if (!listeners.size) stop();
|
|
2954
|
+
};
|
|
2955
|
+
},
|
|
2956
|
+
refresh,
|
|
2957
|
+
setViewScope(value) {
|
|
2958
|
+
requireViewScope(value);
|
|
2959
|
+
if (disposed || viewScope === value) return;
|
|
2960
|
+
viewScope = value;
|
|
2961
|
+
invalidateReads();
|
|
2962
|
+
if (listeners.size) void refresh(0);
|
|
2963
|
+
},
|
|
2964
|
+
async details(goalId) {
|
|
2965
|
+
if (disposed) throw cancelled();
|
|
2966
|
+
const read = new AbortController(), epoch = snapshot.readEpoch;
|
|
2967
|
+
reads.add(read);
|
|
2968
|
+
try {
|
|
2969
|
+
const value = await callGoal(rpc, "details", { goalId, sessionId, ...viewScopeFields(viewScope) }, read.signal);
|
|
2970
|
+
if (disposed || read.signal.aborted || epoch !== snapshot.readEpoch) throw cancelled();
|
|
2971
|
+
return value;
|
|
2972
|
+
} finally {
|
|
2973
|
+
reads.delete(read);
|
|
2974
|
+
}
|
|
2975
|
+
},
|
|
2976
|
+
async decide(request) {
|
|
2977
|
+
const result = await callGoal(rpc, "decide", request);
|
|
2978
|
+
await refresh();
|
|
2979
|
+
return result;
|
|
2980
|
+
},
|
|
2981
|
+
async stopWork(request) {
|
|
2982
|
+
const result = await callGoal(rpc, "stopWork", { ...request, sessionId });
|
|
2983
|
+
await refresh();
|
|
2984
|
+
return result;
|
|
2985
|
+
},
|
|
2986
|
+
async resumeWork(request) {
|
|
2987
|
+
const result = await callGoal(rpc, "resumeWork", { ...request, sessionId });
|
|
2988
|
+
await refresh();
|
|
2989
|
+
return result;
|
|
2990
|
+
},
|
|
2991
|
+
discuss: (request) => callGoal(rpc, "discuss", { ...request, sessionId }),
|
|
2992
|
+
dispose() {
|
|
2993
|
+
disposed = true;
|
|
2994
|
+
stop();
|
|
2995
|
+
listeners.clear();
|
|
2996
|
+
}
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
// src/goal-locales.mjs
|
|
3001
|
+
var goalZh = {
|
|
3002
|
+
goalCurrentWork: "当前工作",
|
|
3003
|
+
goalNoCurrentWork: "此目标尚未关联具体工作。",
|
|
3004
|
+
goalOtherWork: "其他工作",
|
|
3005
|
+
goalEvidence: "目标依据与历史记录",
|
|
3006
|
+
goalUnavailable: "目标内容暂不可用",
|
|
3007
|
+
goalWorkOriginal: "完整委托与来源",
|
|
3008
|
+
goalBlockReason: "阻塞原因",
|
|
3009
|
+
goalBlockUnknown: "执行已受阻,但尚未记录具体原因。",
|
|
3010
|
+
goalBlockRoundLimit: "已达到这次委托允许的连续执行次数。",
|
|
3011
|
+
goalBlockQueueFailed: "下一轮工作未能加入执行队列。",
|
|
3012
|
+
goalBlockPromptRejected: "下一轮工作在开始前被拒绝。",
|
|
3013
|
+
goalDraftChanged: "目标内容已更新。草稿已保留,请核对当前内容并编辑草稿后再发送。",
|
|
3014
|
+
goalTitle: "长期目标",
|
|
3015
|
+
goalOpen: "查看目标",
|
|
3016
|
+
goalHide: "收起目标",
|
|
3017
|
+
goalEmpty: "目前没有可显示的长期目标。",
|
|
3018
|
+
goalLoading: "正在读取目标…",
|
|
3019
|
+
goalOutcome: "希望实现的结果",
|
|
3020
|
+
goalProgress: "已记录的进展",
|
|
3021
|
+
goalRelations: "与其他目标的关系",
|
|
3022
|
+
goalDefinition: "定义版本",
|
|
3023
|
+
goalTracking: "追踪中",
|
|
3024
|
+
goalPaused: "已暂停追踪",
|
|
3025
|
+
goalClosed: "已结束追踪",
|
|
3026
|
+
goalPart: "组成",
|
|
3027
|
+
goalSupports: "支持",
|
|
3028
|
+
goalDerived: "由此形成",
|
|
3029
|
+
goalRelationReview: "定义已变化,关系待复核",
|
|
3030
|
+
goalRelationUnavailable: "关系依据已失效",
|
|
3031
|
+
goalResume: "恢复追踪",
|
|
3032
|
+
goalPause: "暂停追踪",
|
|
3033
|
+
goalClose: "结束追踪",
|
|
3034
|
+
goalBoundary: "Arcfold 从交流和共同经历中整理目标,把相关工作、背景和进展联系起来。你可以在对话中补充或纠正;一次工作完成不代表长期目标已经实现。",
|
|
3035
|
+
goalSaved: "追踪决定已保存,不会因此启动工作。",
|
|
3036
|
+
goalDiscuss: "围绕此目标发送",
|
|
3037
|
+
goalRepair: "用新原话重新澄清",
|
|
3038
|
+
goalDiscussLabel: "本次要发送的原话",
|
|
3039
|
+
goalDiscussHelp: "点击后把这段原话发送到当前交流,并只为这一次消息关联所选目标;不会恢复暂停或结束的追踪。",
|
|
3040
|
+
goalRepairHelp: "旧依据已经失效。这里只发送你这次的新原话供重新澄清,不引用失效旧内容,也不恢复追踪。",
|
|
3041
|
+
goalAccepted: "宿主已受理这次发送;目标关联须等这条真实消息进入当前工作后才生效。",
|
|
3042
|
+
goalUnknown: "这次发送是否受理尚未确认。再次点击只查询同一请求,不会自动重发。请先核对当前交流。",
|
|
3043
|
+
goalFailed: "发送前已停止;没有重新发送。请核对当前交流后另行决定。",
|
|
3044
|
+
goalConflict: "目标已变化,请读完更新后的目标,再明确决定是否发送或改变追踪状态。",
|
|
3045
|
+
goalLinkedWork: "已关联的工作",
|
|
3046
|
+
goalContribution: "助手当时的关联判断(依据当时定义版本)",
|
|
3047
|
+
goalUnassessed: "贡献效果尚未评估",
|
|
3048
|
+
goalContributionUnavailable: "关联判断的依据已失效或无法核对,暂不展示旧正文。",
|
|
3049
|
+
goalProgressUnavailable: "进展依据已失效,暂不作为当前依据。",
|
|
3050
|
+
goalNoProgress: "尚未记录进展。",
|
|
3051
|
+
goalNoRelations: "尚未记录目标关系。",
|
|
3052
|
+
goalNoLinks: "尚未关联工作。",
|
|
3053
|
+
goalHistory: "本交流最近的目标讨论发送记录",
|
|
3054
|
+
goalOriginal: "发送原话",
|
|
3055
|
+
goalAdmission: "受理状态",
|
|
3056
|
+
goalMainRequired: "请打开当前的 Arcfold 主交流,再从这里发送目标讨论。",
|
|
3057
|
+
goalWorkTitle: "相关工作与实际进展",
|
|
3058
|
+
goalWorkHelp: "要修改或继续工作,直接在对话中告诉 Arcfold。停止推进只暂停这件工作的自动继续。",
|
|
3059
|
+
goalWorkEnabled: "持续推进已开启",
|
|
3060
|
+
goalWorkDisarmed: "待继续",
|
|
3061
|
+
goalWorkPaused: "已暂停",
|
|
3062
|
+
goalWorkBlocked: "推进受阻",
|
|
3063
|
+
goalWorkComplete: "这件工作已完成",
|
|
3064
|
+
goalWorkRounds: "已开始的工作轮次",
|
|
3065
|
+
goalWorkVerified: "已核对原始委托",
|
|
3066
|
+
goalWorkLegacy: "历史委托尚未核对",
|
|
3067
|
+
goalWorkFor: "对应目标:",
|
|
3068
|
+
goalWorkUnlinked: "目标联系待整理",
|
|
3069
|
+
goalWorkRelationReview: "这项联系需要重新核对。",
|
|
3070
|
+
goalWorkReadGoal: "查看对应目标",
|
|
3071
|
+
goalWorkStop: "停止这件工作的推进",
|
|
3072
|
+
goalWorkStopped: "这件工作的后续推进已暂停。",
|
|
3073
|
+
goalWorkUnknown: "操作结果尚未确认,请先刷新查看实际状态。再次点击会核对同一请求。",
|
|
3074
|
+
goalWorkResume: "继续这件工作",
|
|
3075
|
+
goalWorkResumed: "这件工作已启用后续推进。",
|
|
3076
|
+
goalWorkDetails: "查看工作详情",
|
|
3077
|
+
goalWorkLinked: "已关联目标",
|
|
3078
|
+
goalCorrection: "补充或纠正",
|
|
3079
|
+
goalWorkCleared: "不再作为当前工作",
|
|
3080
|
+
goalWorkSourceUnavailable: "委托来源已失效",
|
|
3081
|
+
goalStageWaiting: "等待明确下一步",
|
|
3082
|
+
goalStageComplete: "当前阶段已完成",
|
|
3083
|
+
goalStageReview: "待明确当前阶段",
|
|
3084
|
+
goalStageDoneWhen: "完成条件:",
|
|
3085
|
+
goalStageBoundaries: "推进范围:"
|
|
3086
|
+
};
|
|
3087
|
+
var goalEn = {
|
|
3088
|
+
goalCurrentWork: "Current work",
|
|
3089
|
+
goalNoCurrentWork: "No work is linked to this goal yet.",
|
|
3090
|
+
goalOtherWork: "Other work",
|
|
3091
|
+
goalEvidence: "Goal sources and history",
|
|
3092
|
+
goalUnavailable: "Goal content unavailable",
|
|
3093
|
+
goalWorkOriginal: "Full delegation and sources",
|
|
3094
|
+
goalBlockReason: "Blocking reason",
|
|
3095
|
+
goalBlockUnknown: "Execution is blocked; no specific reason is recorded.",
|
|
3096
|
+
goalBlockRoundLimit: "This delegation reached its allowed number of continuation rounds.",
|
|
3097
|
+
goalBlockQueueFailed: "The next round could not be queued.",
|
|
3098
|
+
goalBlockPromptRejected: "The next round was rejected before starting.",
|
|
3099
|
+
goalDraftChanged: "The goal changed. Your draft is preserved; review the current content and edit your draft before sending.",
|
|
3100
|
+
goalTitle: "Long-term goals",
|
|
3101
|
+
goalOpen: "Read goal",
|
|
3102
|
+
goalHide: "Hide goals",
|
|
3103
|
+
goalEmpty: "No long-term goals are available.",
|
|
3104
|
+
goalLoading: "Loading goals…",
|
|
3105
|
+
goalOutcome: "Intended outcome",
|
|
3106
|
+
goalProgress: "Recorded progress",
|
|
3107
|
+
goalRelations: "Related goals",
|
|
3108
|
+
goalDefinition: "Definition version",
|
|
3109
|
+
goalTracking: "Tracking",
|
|
3110
|
+
goalPaused: "Tracking paused",
|
|
3111
|
+
goalClosed: "Tracking ended",
|
|
3112
|
+
goalPart: "Part of",
|
|
3113
|
+
goalSupports: "Supports",
|
|
3114
|
+
goalDerived: "Derived from",
|
|
3115
|
+
goalRelationReview: "Definition changed; relation needs review",
|
|
3116
|
+
goalRelationUnavailable: "Relation evidence unavailable",
|
|
3117
|
+
goalResume: "Resume tracking",
|
|
3118
|
+
goalPause: "Pause tracking",
|
|
3119
|
+
goalClose: "End tracking",
|
|
3120
|
+
goalBoundary: "Arcfold connects goals, related work, context and progress from conversations and shared experience. Clarify or correct them in conversation. Completing a piece of work does not establish that a long-term goal has been achieved.",
|
|
3121
|
+
goalSaved: "Tracking decision saved. No work is started by this decision.",
|
|
3122
|
+
goalDiscuss: "Send about this goal",
|
|
3123
|
+
goalRepair: "Clarify with new words",
|
|
3124
|
+
goalDiscussLabel: "Your words to send",
|
|
3125
|
+
goalDiscussHelp: "Send these exact words to this conversation and relate only that submission to this goal. Paused or ended tracking remains unchanged.",
|
|
3126
|
+
goalRepairHelp: "Prior evidence is unavailable. Only your new words will be sent for clarification; invalid old content is not used and tracking is not resumed.",
|
|
3127
|
+
goalAccepted: "The host accepted the submission. The relation takes effect only when that real message enters the current work.",
|
|
3128
|
+
goalUnknown: "Admission is unconfirmed. Clicking again only reads the same request and never resends it. Check the conversation first.",
|
|
3129
|
+
goalFailed: "Stopped before submission; not resent. Check the conversation before deciding what to do next.",
|
|
3130
|
+
goalConflict: "The goal changed. Read the updated goal before deciding again.",
|
|
3131
|
+
goalLinkedWork: "Related work",
|
|
3132
|
+
goalContribution: "Assistant interpretation at the recorded definition version",
|
|
3133
|
+
goalUnassessed: "Contribution effect not assessed",
|
|
3134
|
+
goalContributionUnavailable: "The interpretation evidence is unavailable or cannot be verified; prior wording is not shown.",
|
|
3135
|
+
goalProgressUnavailable: "Progress evidence is unavailable and is not used as current evidence.",
|
|
3136
|
+
goalNoProgress: "No progress is recorded.",
|
|
3137
|
+
goalNoRelations: "No relations are recorded.",
|
|
3138
|
+
goalNoLinks: "No work is linked.",
|
|
3139
|
+
goalHistory: "Recent goal discussion submissions in this conversation",
|
|
3140
|
+
goalOriginal: "Submitted words",
|
|
3141
|
+
goalAdmission: "Admission",
|
|
3142
|
+
goalMainRequired: "Open the current Arcfold main conversation to send a goal discussion here.",
|
|
3143
|
+
goalWorkTitle: "Related work and actual progress",
|
|
3144
|
+
goalWorkHelp: "Tell Arcfold in conversation when you want to change or continue work. Stopping pauses only this work’s automatic continuation.",
|
|
3145
|
+
goalWorkEnabled: "Automatic continuation enabled",
|
|
3146
|
+
goalWorkDisarmed: "Awaiting continuation",
|
|
3147
|
+
goalWorkPaused: "Paused",
|
|
3148
|
+
goalWorkBlocked: "Work blocked",
|
|
3149
|
+
goalWorkComplete: "This work is complete",
|
|
3150
|
+
goalWorkRounds: "Rounds started",
|
|
3151
|
+
goalWorkVerified: "Original delegation verified",
|
|
3152
|
+
goalWorkLegacy: "Historical delegation not yet verified",
|
|
3153
|
+
goalWorkFor: "Related goal:",
|
|
3154
|
+
goalWorkUnlinked: "Goal relation not yet recorded",
|
|
3155
|
+
goalWorkRelationReview: "This relation needs to be checked again.",
|
|
3156
|
+
goalWorkReadGoal: "Read related goal",
|
|
3157
|
+
goalWorkStop: "Stop this work’s continuation",
|
|
3158
|
+
goalWorkStopped: "This work’s continuation is paused.",
|
|
3159
|
+
goalWorkUnknown: "The action result is unconfirmed. Refresh to inspect actual execution. Clicking again checks the same request.",
|
|
3160
|
+
goalWorkResume: "Continue this work",
|
|
3161
|
+
goalWorkResumed: "Continuation is enabled for this work.",
|
|
3162
|
+
goalWorkDetails: "Read work details",
|
|
3163
|
+
goalWorkLinked: "A related goal",
|
|
3164
|
+
goalCorrection: "Clarify or correct",
|
|
3165
|
+
goalWorkCleared: "No longer the current work",
|
|
3166
|
+
goalWorkSourceUnavailable: "Delegation source is unavailable",
|
|
3167
|
+
goalStageWaiting: "Awaiting the next decision",
|
|
3168
|
+
goalStageComplete: "Current stage completed",
|
|
3169
|
+
goalStageReview: "Current stage needs clarification",
|
|
3170
|
+
goalStageDoneWhen: "Completion criteria:",
|
|
3171
|
+
goalStageBoundaries: "Scope:"
|
|
3172
|
+
};
|
|
3173
|
+
|
|
3174
|
+
// src/method-source.mjs
|
|
3175
|
+
var CHANNEL = "/api";
|
|
3176
|
+
async function callMethod(rpc, endpoint, payload, signal) {
|
|
3177
|
+
const result = await rpc.call(CHANNEL, `arcfoldMethods/${endpoint}`, { args: { request: payload } }, signal);
|
|
3178
|
+
if (result?.ok !== true) throw Object.assign(
|
|
3179
|
+
new Error(result?.error?.message || "方法请求未完成。"),
|
|
3180
|
+
{ code: result?.error?.code || "method_request_failed", remote: true }
|
|
3181
|
+
);
|
|
3182
|
+
return result.value;
|
|
3183
|
+
}
|
|
3184
|
+
function createMethodSource({ rpc, sessionId, viewScope, subscribeReset, intervalMs = 5e3 }) {
|
|
3185
|
+
viewScopeFields(viewScope);
|
|
3186
|
+
let snapshot = {
|
|
3187
|
+
loading: true,
|
|
3188
|
+
error: null,
|
|
3189
|
+
items: [],
|
|
3190
|
+
total: 0,
|
|
3191
|
+
decisionCount: 0,
|
|
3192
|
+
nextOffset: null,
|
|
3193
|
+
offset: 0,
|
|
3194
|
+
unreadCount: 0,
|
|
3195
|
+
readEpoch: 0
|
|
3196
|
+
};
|
|
3197
|
+
const listeners = /* @__PURE__ */ new Set(), reads = /* @__PURE__ */ new Set();
|
|
3198
|
+
let generation = 0, controller, timer, reset, disposed = false;
|
|
3199
|
+
const publish = (value) => {
|
|
3200
|
+
snapshot = { ...snapshot, ...value };
|
|
3201
|
+
for (const listener of listeners) listener();
|
|
3202
|
+
};
|
|
3203
|
+
const scope = () => viewScope === void 0 ? {} : { sessionId, ...viewScopeFields(viewScope) };
|
|
3204
|
+
const cancelled = () => Object.assign(new Error("查看范围已变化,请重新打开方法。"), { code: "method_read_cancelled" });
|
|
3205
|
+
function invalidateReads() {
|
|
3206
|
+
controller?.abort();
|
|
3207
|
+
generation++;
|
|
3208
|
+
for (const read of reads) read.abort();
|
|
3209
|
+
publish({
|
|
3210
|
+
loading: true,
|
|
3211
|
+
error: null,
|
|
3212
|
+
items: [],
|
|
3213
|
+
total: 0,
|
|
3214
|
+
decisionCount: 0,
|
|
3215
|
+
nextOffset: null,
|
|
3216
|
+
offset: 0,
|
|
3217
|
+
unreadCount: 0,
|
|
3218
|
+
readEpoch: snapshot.readEpoch + 1
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
async function refresh(offset = snapshot.offset) {
|
|
3222
|
+
if (disposed) return;
|
|
3223
|
+
controller?.abort();
|
|
3224
|
+
controller = new AbortController();
|
|
3225
|
+
const current = ++generation, signal = controller.signal;
|
|
3226
|
+
try {
|
|
3227
|
+
const cards = await callMethod(rpc, "cards", { offset, limit: 20, ...scope() }, signal);
|
|
3228
|
+
if (current !== generation || signal.aborted || disposed) return;
|
|
3229
|
+
publish({
|
|
3230
|
+
loading: false,
|
|
3231
|
+
error: null,
|
|
3232
|
+
items: cards.items,
|
|
3233
|
+
total: cards.total,
|
|
3234
|
+
decisionCount: cards.decisionCount,
|
|
3235
|
+
nextOffset: cards.nextOffset,
|
|
3236
|
+
offset,
|
|
3237
|
+
unreadCount: cards.unreadCount
|
|
3238
|
+
});
|
|
3239
|
+
} catch (error) {
|
|
3240
|
+
if (current === generation && !signal.aborted && !disposed)
|
|
3241
|
+
publish({ loading: false, error: error instanceof Error ? error.message : "读取方法失败。" });
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
function stop() {
|
|
3245
|
+
clearInterval(timer);
|
|
3246
|
+
timer = void 0;
|
|
3247
|
+
reset?.();
|
|
3248
|
+
reset = void 0;
|
|
3249
|
+
invalidateReads();
|
|
3250
|
+
}
|
|
3251
|
+
return {
|
|
3252
|
+
getSnapshot: () => snapshot,
|
|
3253
|
+
subscribe(listener) {
|
|
3254
|
+
if (disposed) return () => {
|
|
3255
|
+
};
|
|
3256
|
+
listeners.add(listener);
|
|
3257
|
+
if (listeners.size === 1) {
|
|
3258
|
+
void refresh();
|
|
3259
|
+
timer = setInterval(() => {
|
|
3260
|
+
void refresh();
|
|
3261
|
+
}, intervalMs);
|
|
3262
|
+
reset = subscribeReset(() => {
|
|
3263
|
+
invalidateReads();
|
|
3264
|
+
void refresh(0);
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
3267
|
+
return () => {
|
|
3268
|
+
listeners.delete(listener);
|
|
3269
|
+
if (!listeners.size) stop();
|
|
3270
|
+
};
|
|
3271
|
+
},
|
|
3272
|
+
refresh,
|
|
3273
|
+
setViewScope(value) {
|
|
3274
|
+
requireViewScope(value);
|
|
3275
|
+
if (disposed || viewScope === value) return;
|
|
3276
|
+
viewScope = value;
|
|
3277
|
+
invalidateReads();
|
|
3278
|
+
if (listeners.size) void refresh(0);
|
|
3279
|
+
},
|
|
3280
|
+
async details(methodId, offset = 0) {
|
|
3281
|
+
if (disposed) throw cancelled();
|
|
3282
|
+
const read = new AbortController(), epoch = snapshot.readEpoch;
|
|
3283
|
+
reads.add(read);
|
|
3284
|
+
try {
|
|
3285
|
+
const value = await callMethod(rpc, "cardDetails", { methodId, offset, limit: 20, ...scope() }, read.signal);
|
|
3286
|
+
if (disposed || read.signal.aborted || epoch !== snapshot.readEpoch) throw cancelled();
|
|
3287
|
+
return value;
|
|
3288
|
+
} finally {
|
|
3289
|
+
reads.delete(read);
|
|
3290
|
+
}
|
|
3291
|
+
},
|
|
3292
|
+
async acknowledge(notificationId) {
|
|
3293
|
+
const result = await callMethod(rpc, "acknowledge", { notificationId });
|
|
3294
|
+
await refresh();
|
|
3295
|
+
return result;
|
|
3296
|
+
},
|
|
3297
|
+
async decide(request) {
|
|
3298
|
+
const result = await callMethod(rpc, "decide", request);
|
|
3299
|
+
await refresh();
|
|
3300
|
+
return result;
|
|
3301
|
+
},
|
|
3302
|
+
dispose() {
|
|
3303
|
+
disposed = true;
|
|
3304
|
+
stop();
|
|
3305
|
+
listeners.clear();
|
|
3306
|
+
}
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
// src/tree-client.mjs
|
|
3311
|
+
var import_react10 = __toESM(require("react"), 1);
|
|
3312
|
+
|
|
3313
|
+
// src/tree-source.mjs
|
|
3314
|
+
function createTreeSource({ rpc, sessionId, subscribeReset = () => () => {
|
|
3315
|
+
}, intervalMs = 5e3 }) {
|
|
3316
|
+
let snapshot = { loading: true, error: null, nodes: [], treeRevision: 0, current: { paths: [], turnIds: [] }, operations: {}, selection: null, epoch: 0, refreshRevision: 0 };
|
|
3317
|
+
let disposed = false, generation = 0, timer, reset;
|
|
3318
|
+
const listeners = /* @__PURE__ */ new Set(), reads = /* @__PURE__ */ new Set(), writes = /* @__PURE__ */ new Map(), pending = /* @__PURE__ */ new Map();
|
|
3319
|
+
const stopped = () => Object.assign(new Error("连接已结束,请重新打开。"), { code: "TREE_READ_CANCELLED" });
|
|
3320
|
+
const publish = (patch) => {
|
|
3321
|
+
snapshot = { ...snapshot, ...patch };
|
|
3322
|
+
listeners.forEach((listener) => listener());
|
|
3323
|
+
};
|
|
3324
|
+
const operation = (id, value) => publish({ operations: { ...snapshot.operations, [id]: { ...snapshot.operations[id], ...value } } });
|
|
3325
|
+
async function call(endpoint, payload, signal) {
|
|
3326
|
+
if (disposed) throw stopped();
|
|
3327
|
+
const result = await rpc.call("/api", "arcfoldTreeV3/" + endpoint, { args: { request: { ...payload, sessionId } } }, signal);
|
|
3328
|
+
if (result?.ok !== true) throw Object.assign(new Error(result?.error?.message ?? "请求未确认,请读取操作记录。"), { code: result?.error?.code ?? "TREE_UNKNOWN", remote: result?.ok === false });
|
|
3329
|
+
return result.value;
|
|
3330
|
+
}
|
|
3331
|
+
async function read(endpoint, payload = {}) {
|
|
3332
|
+
const controller = new AbortController(), epoch = snapshot.epoch;
|
|
3333
|
+
reads.add(controller);
|
|
3334
|
+
try {
|
|
3335
|
+
const result = await call(endpoint, payload, controller.signal);
|
|
3336
|
+
if (disposed || controller.signal.aborted || snapshot.epoch !== epoch) throw stopped();
|
|
3337
|
+
return result;
|
|
3338
|
+
} finally {
|
|
3339
|
+
reads.delete(controller);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
function invalidate() {
|
|
3343
|
+
generation++;
|
|
3344
|
+
reads.forEach((c) => c.abort());
|
|
3345
|
+
publish({ epoch: snapshot.epoch + 1, nodes: [], selection: null, current: { paths: [], turnIds: [] }, loading: true });
|
|
3346
|
+
}
|
|
3347
|
+
async function refresh() {
|
|
3348
|
+
if (disposed) return;
|
|
3349
|
+
const serial = ++generation;
|
|
3350
|
+
try {
|
|
3351
|
+
let nodes = [], cursor, treeRevision;
|
|
3352
|
+
do {
|
|
3353
|
+
const page = await read("tree.list", { ...cursor ? { cursor } : {}, limit: 100 });
|
|
3354
|
+
nodes.push(...page.nodes);
|
|
3355
|
+
cursor = page.nextCursor;
|
|
3356
|
+
treeRevision = page.treeRevision;
|
|
3357
|
+
} while (cursor);
|
|
3358
|
+
const current = await read("session.current");
|
|
3359
|
+
if (!disposed && serial === generation) publish({ nodes, current, treeRevision, loading: false, error: null, refreshRevision: snapshot.refreshRevision + 1 });
|
|
3360
|
+
} catch (error) {
|
|
3361
|
+
if (!disposed && serial === generation) publish({ loading: false, error: error.message });
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
async function readback(requestId) {
|
|
3365
|
+
const receipt = await read("receipts.read", { requestId });
|
|
3366
|
+
operation(requestId, { state: receipt.state, receipt });
|
|
3367
|
+
if (["success", "completed"].includes(receipt.state)) {
|
|
3368
|
+
pending.delete(requestId);
|
|
3369
|
+
void refresh();
|
|
3370
|
+
}
|
|
3371
|
+
return receipt;
|
|
3372
|
+
}
|
|
3373
|
+
function act(endpoint, payload, requestId = globalThis.crypto.randomUUID()) {
|
|
3374
|
+
const hash = JSON.stringify([endpoint, payload]);
|
|
3375
|
+
const old = writes.get(requestId);
|
|
3376
|
+
if (old) {
|
|
3377
|
+
if (old.hash !== hash) return Promise.reject(Object.assign(new Error("这次提交编号已用于另一项操作。"), { code: "IDEMPOTENCY_CONFLICT" }));
|
|
3378
|
+
return old.promise;
|
|
3379
|
+
}
|
|
3380
|
+
const controller = new AbortController();
|
|
3381
|
+
pending.set(requestId, { endpoint, payload, hash });
|
|
3382
|
+
operation(requestId, { endpoint, state: "pending", message: "正在提交,尚未确认" });
|
|
3383
|
+
const promise = (async () => {
|
|
3384
|
+
try {
|
|
3385
|
+
const receipt = await call(endpoint, { ...payload, requestId }, controller.signal);
|
|
3386
|
+
operation(requestId, { state: receipt.state, receipt, message: receipt.state === "success" ? "已保存" : "结果尚待确认" });
|
|
3387
|
+
if (receipt.state === "success") pending.delete(requestId);
|
|
3388
|
+
await refresh();
|
|
3389
|
+
return receipt;
|
|
3390
|
+
} catch (error) {
|
|
3391
|
+
operation(requestId, { state: error.remote ? "conflict" : "unknown", message: error.message, code: error.code });
|
|
3392
|
+
if (!error.remote && !disposed) {
|
|
3393
|
+
try {
|
|
3394
|
+
const found = await readback(requestId);
|
|
3395
|
+
if (found.state === "success") return found;
|
|
3396
|
+
} catch {
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
throw error;
|
|
3400
|
+
}
|
|
3401
|
+
})();
|
|
3402
|
+
writes.set(requestId, { hash, promise, controller });
|
|
3403
|
+
return promise;
|
|
3404
|
+
}
|
|
3405
|
+
function stop() {
|
|
3406
|
+
clearInterval(timer);
|
|
3407
|
+
timer = void 0;
|
|
3408
|
+
reset?.();
|
|
3409
|
+
reset = void 0;
|
|
3410
|
+
}
|
|
3411
|
+
return {
|
|
3412
|
+
getSnapshot: () => snapshot,
|
|
3413
|
+
subscribe(listener) {
|
|
3414
|
+
if (disposed) return () => {
|
|
3415
|
+
};
|
|
3416
|
+
listeners.add(listener);
|
|
3417
|
+
if (listeners.size === 1) {
|
|
3418
|
+
void refresh();
|
|
3419
|
+
timer = setInterval(() => void refresh(), intervalMs);
|
|
3420
|
+
reset = subscribeReset(() => {
|
|
3421
|
+
invalidate();
|
|
3422
|
+
void refresh();
|
|
3423
|
+
for (const id of pending.keys()) void readback(id).catch(() => {
|
|
3424
|
+
});
|
|
3425
|
+
});
|
|
3426
|
+
}
|
|
3427
|
+
return () => {
|
|
3428
|
+
listeners.delete(listener);
|
|
3429
|
+
if (!listeners.size) stop();
|
|
3430
|
+
};
|
|
3431
|
+
},
|
|
3432
|
+
read,
|
|
3433
|
+
act,
|
|
3434
|
+
readback,
|
|
3435
|
+
refresh,
|
|
3436
|
+
select: (nodeId) => publish({ selection: nodeId }),
|
|
3437
|
+
openDetails: (nodeId) => publish({ selection: nodeId, detailsNodeId: nodeId }),
|
|
3438
|
+
closeDetails: () => publish({ detailsNodeId: null }),
|
|
3439
|
+
dispose() {
|
|
3440
|
+
disposed = true;
|
|
3441
|
+
stop();
|
|
3442
|
+
reads.forEach((c) => c.abort());
|
|
3443
|
+
writes.forEach((w) => w.controller.abort());
|
|
3444
|
+
listeners.clear();
|
|
3445
|
+
}
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
|
|
3449
|
+
// src/tree-view.mjs
|
|
3450
|
+
var import_react9 = __toESM(require("react"), 1);
|
|
3451
|
+
var h9 = import_react9.default.createElement;
|
|
3452
|
+
var statuses = { active: "进行中", paused: "暂停", completed: "已完成", abandoned: "已放弃" };
|
|
3453
|
+
var nodeStatus = (node) => (statuses[node.status] ?? node.status) + (node.statusOrigin === "cascade" ? "(随上级)" : node.status === "completed" && node.statusOrigin === "model" ? "(助手判断)" : "");
|
|
3454
|
+
var useSource = (source) => (0, import_react9.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
3455
|
+
function useRead(source, endpoint, payload, refresh = 0) {
|
|
3456
|
+
const [value, setValue] = (0, import_react9.useState)({ loading: true });
|
|
3457
|
+
const key = JSON.stringify([endpoint, payload, source.getSnapshot?.().epoch ?? 0]);
|
|
3458
|
+
const refreshRevision = source.getSnapshot?.().refreshRevision ?? 0;
|
|
3459
|
+
(0, import_react9.useEffect)(() => {
|
|
3460
|
+
let live = true;
|
|
3461
|
+
if (!payload) return;
|
|
3462
|
+
source.read(endpoint, payload).then((data) => {
|
|
3463
|
+
if (live) setValue({ source, key, data });
|
|
3464
|
+
}, (error) => {
|
|
3465
|
+
if (live) setValue({ source, key, error: error.message });
|
|
3466
|
+
});
|
|
3467
|
+
return () => {
|
|
3468
|
+
live = false;
|
|
3469
|
+
};
|
|
3470
|
+
}, [source, endpoint, key, refresh, refreshRevision]);
|
|
3471
|
+
return value.source === source && value.key === key && payload ? value : { loading: true };
|
|
3472
|
+
}
|
|
3473
|
+
var button = (label, onClick, props = {}) => h9("button", { type: "button", onClick, ...props }, label);
|
|
3474
|
+
var field = (label, value, change, props = {}) => h9("label", { className: "tree-field" }, label, h9("input", { value, onChange: (e) => change(e.target.value), ...props }));
|
|
3475
|
+
var select = (label, value, change, options, props = {}) => h9("label", { className: "tree-field" }, label, h9("select", { "aria-label": label, value, onChange: (e) => change(e.target.value), ...props }, options.map(([id, label2]) => h9("option", { key: id, value: id }, label2))));
|
|
3476
|
+
function ReadState({ value, children }) {
|
|
3477
|
+
return value.error ? h9("p", { role: "alert" }, value.error) : value.loading ? h9("p", { role: "status" }, "正在读取…") : children(value.data);
|
|
3478
|
+
}
|
|
3479
|
+
function Command({ source, endpoint, payload, children, onDone, disabled = false, className, title }) {
|
|
3480
|
+
const state = useSource(source), [id, setId] = (0, import_react9.useState)(null), op = state.operations[id];
|
|
3481
|
+
const waiting = op && ["pending", "unknown", "not_found", "partial", "prepared", "spawn_intent"].includes(op.state);
|
|
3482
|
+
const run = async () => {
|
|
3483
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
3484
|
+
setId(requestId);
|
|
3485
|
+
try {
|
|
3486
|
+
const receipt = await source.act(endpoint, typeof payload === "function" ? payload() : payload, requestId);
|
|
3487
|
+
if (receipt.state === "success") onDone?.(receipt.value);
|
|
3488
|
+
} catch {
|
|
3489
|
+
}
|
|
3490
|
+
};
|
|
3491
|
+
return h9("span", { className: "tree-command" }, button(children, run, { disabled: disabled || waiting, className, title }), op && h9("small", { role: op.state === "conflict" ? "alert" : "status" }, op.state === "success" ? "已保存" : op.message ?? "结果尚待确认"), waiting && op.state !== "pending" && button("核对操作结果", () => source.readback(id).catch(() => {
|
|
3492
|
+
})));
|
|
3493
|
+
}
|
|
3494
|
+
function StatusDot({ status: status2 }) {
|
|
3495
|
+
return h9("i", { className: "tree-dot " + status2, "aria-hidden": true });
|
|
3496
|
+
}
|
|
3497
|
+
function NodeChip({ node, path, source }) {
|
|
3498
|
+
const label = (path ?? [node]).map((n) => n.name).join(" › ");
|
|
3499
|
+
return button(h9(import_react9.default.Fragment, null, h9(StatusDot, { status: node.status }), h9("span", null, label), h9("small", null, nodeStatus(node))), () => source.openDetails(node.id), { className: "tree-chip", title: label + " · " + nodeStatus(node) });
|
|
3500
|
+
}
|
|
3501
|
+
function AttributionRange({ source, state }) {
|
|
3502
|
+
const range = state.current.attribution, [editing, setEditing] = (0, import_react9.useState)(false), [branch, setBranch] = (0, import_react9.useState)("");
|
|
3503
|
+
(0, import_react9.useEffect)(() => setBranch(range?.selection.nodeId ?? ""), [range?.selection.revision]);
|
|
3504
|
+
if (!range || !range.blocked && !range.selection.nodeId && !range.pending.length && !editing) return null;
|
|
3505
|
+
const payload = (pendingId) => ({ nodeId: branch || null, expectedTreeRevision: state.treeRevision, expectedSelectionRevision: range.selection.revision, pendingId });
|
|
3506
|
+
const options = [["", "整棵目标树"], ...orderedNodes(state.nodes).map((n) => {
|
|
3507
|
+
const path = [n.name];
|
|
3508
|
+
let parent = state.nodes.find((p) => p.id === n.parentId);
|
|
3509
|
+
const seen = /* @__PURE__ */ new Set([n.id]);
|
|
3510
|
+
while (parent && !seen.has(parent.id)) {
|
|
3511
|
+
seen.add(parent.id);
|
|
3512
|
+
path.unshift(parent.name);
|
|
3513
|
+
parent = state.nodes.find((p) => p.id === parent.parentId);
|
|
3514
|
+
}
|
|
3515
|
+
return [n.id, path.join(" › ")];
|
|
3516
|
+
})];
|
|
3517
|
+
return h9(
|
|
3518
|
+
"section",
|
|
3519
|
+
{ className: "tree-attribution-range", "aria-label": "本聊天目标范围" },
|
|
3520
|
+
range.blocked ? h9("p", { role: "alert" }, range.selectionMissing ? "此前选定的目标已不存在,请重新选择。" : `当前范围有 ${range.count} 个候选,超过一次可检查的 ${range.limit} 个。请选择一支目标后继续。`) : h9("p", null, "本聊天只在这支目标下判断归属:" + (range.path.map((n) => n.name).join(" › ") || "整棵目标树")),
|
|
3521
|
+
h9("small", null, "选定范围后,其他分支暂不参与本聊天的归属判断。原话会保留,选择前不调用模型。"),
|
|
3522
|
+
range.blocked || range.pending.length > 0 || editing ? h9(
|
|
3523
|
+
import_react9.default.Fragment,
|
|
3524
|
+
null,
|
|
3525
|
+
select("本聊天使用哪支目标", branch, setBranch, options),
|
|
3526
|
+
h9(Command, { source, endpoint: "session.selectBranch", payload: () => payload(null), onDone: () => setEditing(false) }, "保存范围"),
|
|
3527
|
+
range.pending.map((p) => h9(
|
|
3528
|
+
"article",
|
|
3529
|
+
{ key: p.id },
|
|
3530
|
+
h9("p", null, "等待继续的原话"),
|
|
3531
|
+
h9("blockquote", null, p.text),
|
|
3532
|
+
p.state === "waiting" ? h9(Command, { source, endpoint: "session.selectBranch", payload: () => payload(p.id), onDone: () => setEditing(false) }, "选定并继续这条消息") : h9("p", { role: "status" }, p.state === "queued" ? "已经排入对话,请勿重复发送。" : "继续请求已记录,发送状态待核对;不会自动重发。")
|
|
3533
|
+
))
|
|
3534
|
+
) : button("更改范围", () => {
|
|
3535
|
+
setBranch(range.selection.nodeId ?? "");
|
|
3536
|
+
setEditing(true);
|
|
3537
|
+
})
|
|
3538
|
+
);
|
|
3539
|
+
}
|
|
3540
|
+
function TreeDock({ source }) {
|
|
3541
|
+
const state = useSource(source), [revision, setRevision] = (0, import_react9.useState)(0), detail = useRead(source, "node.details", state.detailsNodeId ? { nodeId: state.detailsNodeId } : null, revision + state.epoch);
|
|
3542
|
+
return h9(
|
|
3543
|
+
"div",
|
|
3544
|
+
{ className: "arcfold-tree tree-dock", "aria-label": "当前事项" },
|
|
3545
|
+
h9(AttributionRange, { source, state }),
|
|
3546
|
+
state.current.paths.map((path) => path.length ? h9(NodeChip, { key: path.at(-1).id, node: path.at(-1), path, source }) : null),
|
|
3547
|
+
state.error && h9("small", { role: "alert" }, state.error),
|
|
3548
|
+
state.detailsNodeId && h9("div", { role: "dialog", "aria-modal": true, "aria-label": "事项详情", className: "tree-detail-dialog" }, button("关闭详情", () => source.closeDetails()), h9(ReadState, { value: detail }, (data) => h9(NodeDetail, { key: data.node.id, source, detail: data, navigate: (id) => source.openDetails(id), onDone: () => {
|
|
3549
|
+
setRevision((v) => v + 1);
|
|
3550
|
+
void source.refresh();
|
|
3551
|
+
} })))
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
3554
|
+
function ItemEditor({ source, item, onDone, onCancel }) {
|
|
3555
|
+
const [expectedVersion] = (0, import_react9.useState)(item.current.version);
|
|
3556
|
+
const [text2, setText] = (0, import_react9.useState)(typeof item.current.content === "string" ? item.current.content : ""), [short, setShort] = (0, import_react9.useState)(item.method?.short ?? item.current.content), [long, setLong] = (0, import_react9.useState)(item.method?.long), [reason, setReason] = (0, import_react9.useState)("");
|
|
3557
|
+
const replacement = () => item.kind === "understanding" ? text2 : item.kind === "supplement" ? short : { short, long };
|
|
3558
|
+
return h9(
|
|
3559
|
+
"section",
|
|
3560
|
+
{ className: "tree-editor" },
|
|
3561
|
+
item.kind === "understanding" ? h9("label", null, "理解内容", h9("textarea", { value: text2, onChange: (e) => setText(e.target.value), maxLength: 2e3 })) : h9(
|
|
3562
|
+
import_react9.default.Fragment,
|
|
3563
|
+
null,
|
|
3564
|
+
Object.entries({ situation: "何时适用", action: "怎样做", basis: "依据", notApplicable: "何时不适用" }).map(([key, label]) => field(label, short?.[key] ?? "", (value) => setShort({ ...short, [key]: value }), { key, maxLength: 200 })),
|
|
3565
|
+
long && h9("details", null, h9("summary", null, "详细方法文字"), Object.entries({ name: "名称", description: "说明", whenToUse: "适用情境", steps: "步骤", pitfalls: "注意点", verify: "如何检查" }).map(([key, label]) => h9("label", { key }, label, h9("textarea", { value: long[key] ?? "", onChange: (e) => setLong({ ...long, [key]: e.target.value }) }))))
|
|
3566
|
+
),
|
|
3567
|
+
field("修改理由", reason, setReason),
|
|
3568
|
+
h9("div", { className: "tree-item-actions" }, h9(Command, { source, endpoint: "item.edit", className: "tree-primary", disabled: !reason.trim() || item.kind === "understanding" && !text2.trim(), payload: () => ({ itemId: item.id, expectedVersion, replacement: replacement(), reason }), onDone }, "保存修改"), button("取消", onCancel))
|
|
3569
|
+
);
|
|
3570
|
+
}
|
|
3571
|
+
function ItemCard({ source, item, relatedItems = [], onDone }) {
|
|
3572
|
+
const [editing, setEditing] = (0, import_react9.useState)(false), [deleting, setDeleting] = (0, import_react9.useState)(false), current = item.current, method = item.method;
|
|
3573
|
+
const evaluation = item.evaluations?.filter((e) => e.version === current.version).at(-1), short = method?.short ?? current.content?.short ?? current.content;
|
|
3574
|
+
const label = typeof current.content === "string" ? current.content : method?.long?.name ?? short?.action ?? "查看内容";
|
|
3575
|
+
const active = item.lifecycle === "active";
|
|
3576
|
+
const replacement = relatedItems.find((candidate) => candidate.id === item.replacedBy);
|
|
3577
|
+
const facts = method || typeof current.content !== "string" ? [
|
|
3578
|
+
["何时用", short?.situation],
|
|
3579
|
+
["怎样做", short?.action],
|
|
3580
|
+
["预期", method?.expectation?.text ?? "尚未设定"],
|
|
3581
|
+
["检验", evaluation ? ({ met: "达到预期", not_met: "未达到预期", unclear: "尚不清楚" }[evaluation.result] ?? "尚不清楚") + (evaluation.observed ? " · " + evaluation.observed : "") : "尚待检验"]
|
|
3582
|
+
] : [];
|
|
3583
|
+
return h9(
|
|
3584
|
+
"details",
|
|
3585
|
+
{ className: "tree-item", "data-item-id": item.id },
|
|
3586
|
+
h9("summary", null, h9("span", null, label), !active && h9("small", null, { invalid: "已更正", changed: "已变化", draft: "待采用" }[item.lifecycle] ?? "已停止采用")),
|
|
3587
|
+
h9(
|
|
3588
|
+
"div",
|
|
3589
|
+
{ className: "tree-item-detail" },
|
|
3590
|
+
h9(
|
|
3591
|
+
"div",
|
|
3592
|
+
{ className: "tree-item-actions" },
|
|
3593
|
+
item.kind === "understanding" && current.sourceKind === "assistant_inferred" && active && !item.stale && h9(Command, { source, endpoint: "item.confirm", className: "tree-primary", payload: { itemId: item.id, expectedVersion: current.version }, onDone }, "确认"),
|
|
3594
|
+
method && active && (item.hooks ?? []).map((hook) => h9(Command, { key: hook.id, source, endpoint: "hook.disable", disabled: !hook.enabled || item.stale, title: item.hooks.length > 1 ? hook.message : void 0, payload: { methodId: item.id, hookId: hook.hookId, expectedVersion: current.version }, onDone }, hook.enabled ? "停用" : "已停用")),
|
|
3595
|
+
active && button("修改", () => {
|
|
3596
|
+
setEditing(true);
|
|
3597
|
+
setDeleting(false);
|
|
3598
|
+
}, { disabled: editing }),
|
|
3599
|
+
["active", "draft"].includes(item.lifecycle) && button("删除", () => {
|
|
3600
|
+
setDeleting(true);
|
|
3601
|
+
setEditing(false);
|
|
3602
|
+
}, { className: "tree-danger", disabled: deleting })
|
|
3603
|
+
),
|
|
3604
|
+
deleting && h9("div", { className: "tree-delete-confirm" }, h9("p", null, "删除这条内容?"), h9(Command, { source, endpoint: "item.withdraw", className: "tree-danger", payload: { itemId: item.id, expectedVersion: current.version, reason: "用户在目标树中删除此条内容" }, onDone }, "确认删除"), button("取消", () => setDeleting(false))),
|
|
3605
|
+
editing && h9(ItemEditor, { source, item, onDone: () => {
|
|
3606
|
+
setEditing(false);
|
|
3607
|
+
onDone();
|
|
3608
|
+
}, onCancel: () => setEditing(false) }),
|
|
3609
|
+
facts.length > 0 && h9(
|
|
3610
|
+
"div",
|
|
3611
|
+
{ className: "tree-method-description" },
|
|
3612
|
+
h9("dl", null, facts.map(([name, value]) => h9("div", { key: name }, h9("dt", null, name), h9("dd", { className: name === "检验" && evaluation ? "tree-evaluation-" + evaluation.result : void 0 }, value || "尚未填写")))),
|
|
3613
|
+
(short?.basis || short?.notApplicable || method?.long || item.scripts?.length) && h9(
|
|
3614
|
+
"details",
|
|
3615
|
+
{ className: "tree-method-more" },
|
|
3616
|
+
h9("summary", null, "完整说明"),
|
|
3617
|
+
short?.basis && h9("p", null, short.basis),
|
|
3618
|
+
short?.notApplicable && h9("p", null, "不适用:" + short.notApplicable),
|
|
3619
|
+
method?.long && Object.entries({ description: "说明", whenToUse: "适用情境", steps: "步骤", pitfalls: "注意点", verify: "如何检查" }).filter(([key]) => method.long[key]).map(([key, name]) => h9("div", { key }, h9("strong", null, name), h9("p", null, method.long[key]))),
|
|
3620
|
+
(item.scripts ?? []).filter((script) => script.usage).map((script) => h9("p", { key: script.id }, script.usage))
|
|
3621
|
+
)
|
|
3622
|
+
),
|
|
3623
|
+
item.stale && h9("p", { role: "status", className: "tree-muted" }, item.sourceReview?.status === "needs_evidence" ? "归属已变化,现有证据不足,暂不使用:" + item.sourceReview.reason : "归属已变化,此条暂不使用;请核对内容。"),
|
|
3624
|
+
replacement && ["invalid", "changed"].includes(item.lifecycle) && h9(
|
|
3625
|
+
"details",
|
|
3626
|
+
null,
|
|
3627
|
+
h9("summary", null, "撤销自动更正"),
|
|
3628
|
+
h9("p", null, "恢复原理解,并撤回系统生成的替代理解。"),
|
|
3629
|
+
h9(Command, { source, endpoint: "item.undoCorrection", payload: { itemId: item.id, expectedRevision: item.revision, replacementId: replacement.id, expectedReplacementRevision: replacement.revision }, onDone }, "确认撤销更正")
|
|
3630
|
+
)
|
|
3631
|
+
)
|
|
3632
|
+
);
|
|
3633
|
+
}
|
|
3634
|
+
function NodeDetail({ source, detail, onDone, navigate = (id) => source.select(id) }) {
|
|
3635
|
+
const { node, summary } = detail, [name, setName] = (0, import_react9.useState)(node.name), [nameRevision, setNameRevision] = (0, import_react9.useState)(node.revision);
|
|
3636
|
+
const items = detail.items.filter((item) => item.lifecycle !== "withdrawn");
|
|
3637
|
+
return h9(
|
|
3638
|
+
"section",
|
|
3639
|
+
{ className: "tree-detail" },
|
|
3640
|
+
h9("nav", { className: "tree-breadcrumbs", "aria-label": "目标路径" }, detail.path.slice(0, -1).map((n) => h9(import_react9.default.Fragment, { key: n.id }, button(n.name, () => navigate(n.id), { title: n.name }), h9("span", { "aria-hidden": true }, "›")))),
|
|
3641
|
+
h9(
|
|
3642
|
+
"details",
|
|
3643
|
+
{ className: "tree-node-title" },
|
|
3644
|
+
h9("summary", { onClick: (e) => {
|
|
3645
|
+
if (!e.currentTarget.parentElement.open) {
|
|
3646
|
+
setName(node.name);
|
|
3647
|
+
setNameRevision(node.revision);
|
|
3648
|
+
}
|
|
3649
|
+
} }, h9("h2", null, node.name)),
|
|
3650
|
+
h9("div", { className: "tree-editor" }, field("事项名称", name, setName, { maxLength: 80 }), h9(Command, { source, endpoint: "node.rename", className: "tree-primary", disabled: !name.trim() || name === node.name, payload: () => ({ nodeId: node.id, name, expectedRevision: nameRevision }), onDone }, "保存名称"))
|
|
3651
|
+
),
|
|
3652
|
+
h9("p", { className: "tree-node-status" }, h9(StatusDot, { status: node.status }), nodeStatus(node)),
|
|
3653
|
+
h9("section", { className: "tree-panel tree-summary" }, h9("h3", null, "当前摘要"), summary ? h9(
|
|
3654
|
+
"div",
|
|
3655
|
+
{ className: "tree-summary-copy" },
|
|
3656
|
+
h9("p", null, summary.oneLine),
|
|
3657
|
+
summary.body.status && summary.body.status !== summary.oneLine && h9("p", null, summary.body.status),
|
|
3658
|
+
h9(
|
|
3659
|
+
"details",
|
|
3660
|
+
{ className: "tree-summary-more" },
|
|
3661
|
+
h9("summary", null, "展开完整摘要"),
|
|
3662
|
+
summary.body.outputs.length > 0 && h9(import_react9.default.Fragment, null, h9("h4", null, "已有产出"), h9("ul", null, summary.body.outputs.map((text2, i) => h9("li", { key: i }, text2)))),
|
|
3663
|
+
summary.body.decisions.length > 0 && h9(import_react9.default.Fragment, null, h9("h4", null, "已作出的决定"), h9("ul", null, summary.body.decisions.map((decision, i) => h9("li", { key: i }, decision.what)))),
|
|
3664
|
+
summary.body.next && h9("p", null, "下一步:" + summary.body.next),
|
|
3665
|
+
summary.body.pending.length > 0 && h9(import_react9.default.Fragment, null, h9("h4", null, "待确认"), h9("ul", null, summary.body.pending.map((text2, i) => h9("li", { key: i }, text2))))
|
|
3666
|
+
)
|
|
3667
|
+
) : h9("p", { className: "tree-empty" }, "还没有整理出的摘要。")),
|
|
3668
|
+
h9(
|
|
3669
|
+
"div",
|
|
3670
|
+
{ className: "tree-detail-grid" },
|
|
3671
|
+
...[["理解", items.filter((item) => item.kind === "understanding")], ["方法", items.filter((item) => item.kind !== "understanding")]].map(([label, entries]) => h9("section", { className: "tree-panel", key: label }, h9("h3", null, label, h9("small", null, entries.length + " 条")), entries.length ? entries.map((item) => h9(ItemCard, { key: item.id, source, item, relatedItems: detail.items, onDone })) : h9("p", { className: "tree-empty" }, label === "理解" ? "还没有理解。" : "还没有方法。"))),
|
|
3672
|
+
h9("section", { className: "tree-panel" }, h9("h3", null, "子节点", h9("small", null, detail.children.length ? detail.children.length + " 个" : "无")), detail.children.length ? detail.children.map((child) => button(h9(import_react9.default.Fragment, null, h9(StatusDot, { status: child.status }), h9("span", null, child.name), h9("span", { "aria-hidden": true }, "›")), () => navigate(child.id), { key: child.id, className: "tree-child", title: child.name })) : h9("p", { className: "tree-empty" }, "还没有子节点。")),
|
|
3673
|
+
h9("section", { className: "tree-panel" }, h9("h3", null, "扩大范围记录", h9("small", null, detail.promotionHistory.length + " 条")), detail.promotionHistory.length ? detail.promotionHistory.map((row) => h9("p", { key: row.itemId }, row.reason ?? "此条内容已扩大适用范围。")) : h9("p", { className: "tree-empty" }, "暂无扩大范围记录。"))
|
|
3674
|
+
)
|
|
3675
|
+
);
|
|
3676
|
+
}
|
|
3677
|
+
var metricCards = [
|
|
3678
|
+
{ title: "默契", label: "次需要重新对齐", count: (o) => o.rates.understandingProblems.numerator },
|
|
3679
|
+
{ title: "可靠", label: "次要求重做", count: (o) => o.counts.redo },
|
|
3680
|
+
{ title: "精进", label: "次重复纠正", count: (o) => o.rates.recurrence.numerator }
|
|
3681
|
+
];
|
|
3682
|
+
function Metrics({ source, refresh }) {
|
|
3683
|
+
const value = useRead(source, "metrics.read", {}, refresh);
|
|
3684
|
+
return h9(ReadState, { value }, (data) => {
|
|
3685
|
+
const current = data.observations, comparable = data.comparison?.comparable && data.windows?.length === 2 && data.windows[1].fromOrdinal === data.fromOrdinal && data.windows[1].toOrdinal === data.toOrdinal, previous = comparable ? data.windows[0].observations : null;
|
|
3686
|
+
const growth = current.growth, hasData = current.logicalTurns > 0;
|
|
3687
|
+
return h9(
|
|
3688
|
+
"section",
|
|
3689
|
+
{ className: "tree-page tree-metrics", "aria-label": "指标" },
|
|
3690
|
+
h9("header", { className: "tree-page-heading" }, h9("div", null, h9("h2", null, "三个特征"), h9("p", null, "一起做事的现状与变化")), h9("small", null, hasData ? "最近 " + current.logicalTurns + " 轮对话" : "暂无对话记录")),
|
|
3691
|
+
h9("div", { className: "tree-metric-grid" }, metricCards.map((metric) => {
|
|
3692
|
+
const now = hasData ? metric.count(current) : null, points = comparable ? data.windows.map((w) => metric.count(w.observations)) : [];
|
|
3693
|
+
const max = Math.max(1, ...points), coordinates = points.map((n, i) => 10 + i * 240 + "," + (68 - n / max * 50)).join(" ");
|
|
3694
|
+
return h9(
|
|
3695
|
+
"article",
|
|
3696
|
+
{ className: "tree-panel tree-metric-card", key: metric.title },
|
|
3697
|
+
h9("h3", null, metric.title),
|
|
3698
|
+
h9("div", { className: "tree-metric-value" }, now ?? "—", h9("small", null, metric.label)),
|
|
3699
|
+
h9("p", { className: "tree-muted" }, previous ? "前一观察阶段 " + metric.count(previous) + " 次(" + previous.logicalTurns + " 轮)" : "暂无可比较的历史记录"),
|
|
3700
|
+
points.length > 1 ? h9("svg", { className: "tree-chart", viewBox: "0 0 260 80", role: "img", "aria-label": "前后两个观察阶段:" + points.join("、") + "次" }, h9("path", { d: "M10 72H250", className: "tree-chart-base" }), h9("polyline", { points: coordinates }), points.map((n, i) => h9("circle", { key: i, cx: 10 + i * 240, cy: 68 - n / max * 50, r: 3 }))) : h9("div", { className: "tree-chart-empty" }, hasData ? "积累更多记录后显示趋势" : "暂无数据")
|
|
3701
|
+
);
|
|
3702
|
+
})),
|
|
3703
|
+
h9("section", { className: "tree-panel tree-outcomes" }, h9("h3", null, "自主的拓展"), h9("div", { className: "tree-outcome-grid" }, [
|
|
3704
|
+
["用户确认的变化", growth.confirmedUserChanges],
|
|
3705
|
+
["新建目标", growth.byType.created],
|
|
3706
|
+
["作出取舍", growth.userStatusChoices.paused + growth.userStatusChoices.abandoned],
|
|
3707
|
+
["确认完成", growth.userStatusChoices.completed]
|
|
3708
|
+
].map(([label, count]) => h9("div", { key: label }, h9("span", null, label), h9("strong", null, hasData ? count : "—")))))
|
|
3709
|
+
);
|
|
3710
|
+
});
|
|
3711
|
+
}
|
|
3712
|
+
function Reports({ source, refresh }) {
|
|
3713
|
+
const [cursor, setCursor] = (0, import_react9.useState)(null), [selected, setSelected] = (0, import_react9.useState)(null), [includeEvidence, setEvidence] = (0, import_react9.useState)(false), [preview, setPreview] = (0, import_react9.useState)(null), [download, setDownload] = (0, import_react9.useState)(null);
|
|
3714
|
+
const value = useRead(source, "reports.list", { ...cursor ? { cursor } : {}, limit: 50 }, refresh);
|
|
3715
|
+
(0, import_react9.useEffect)(() => {
|
|
3716
|
+
let live = true;
|
|
3717
|
+
setPreview(null);
|
|
3718
|
+
setDownload(null);
|
|
3719
|
+
if (selected) source.read("reports.details", { reportId: selected.reportId, includeEvidence }).then((value2) => {
|
|
3720
|
+
if (live) setPreview(value2);
|
|
3721
|
+
}).catch((error) => {
|
|
3722
|
+
if (live) setPreview({ error: error.message });
|
|
3723
|
+
});
|
|
3724
|
+
return () => {
|
|
3725
|
+
live = false;
|
|
3726
|
+
};
|
|
3727
|
+
}, [source, selected, includeEvidence]);
|
|
3728
|
+
(0, import_react9.useEffect)(() => () => {
|
|
3729
|
+
if (download) URL.revokeObjectURL(download.url);
|
|
3730
|
+
}, [download]);
|
|
3731
|
+
const open = (report) => {
|
|
3732
|
+
setSelected(selected?.reportId === report.reportId ? null : report);
|
|
3733
|
+
setEvidence(false);
|
|
3734
|
+
};
|
|
3735
|
+
return h9(
|
|
3736
|
+
"section",
|
|
3737
|
+
{ className: "tree-page tree-reports" },
|
|
3738
|
+
h9("header", { className: "tree-page-heading" }, h9("div", null, h9("h2", null, "给开发者的报告"), h9("p", null, "需要从系统本身改进的问题"))),
|
|
3739
|
+
h9(ReadState, { value }, (data) => h9(
|
|
3740
|
+
import_react9.default.Fragment,
|
|
3741
|
+
null,
|
|
3742
|
+
data.reports.length ? h9("div", { className: "tree-panel tree-report-list" }, data.reports.map((report) => h9(
|
|
3743
|
+
"article",
|
|
3744
|
+
{ className: "tree-report", key: report.reportId },
|
|
3745
|
+
button(h9(import_react9.default.Fragment, null, h9("span", null, report.category ?? "问题报告"), h9("span", { "aria-hidden": true }, selected?.reportId === report.reportId ? "⌄" : "›")), () => open(report), { className: "tree-report-toggle", "aria-expanded": selected?.reportId === report.reportId, "aria-label": report.category ?? "问题报告" }),
|
|
3746
|
+
selected?.reportId === report.reportId && h9(
|
|
3747
|
+
"div",
|
|
3748
|
+
{ className: "tree-report-detail" },
|
|
3749
|
+
preview?.error ? h9("p", { role: "alert" }, preview.error) : !preview ? h9("p", { role: "status" }, "正在读取…") : h9("div", { className: "tree-method-description" }, h9("dl", null, [
|
|
3750
|
+
["当前状态", { unhandled: "待开发者处理", accepted: "已处理", rejected: "未采用", exported: "已导出" }[preview.status] ?? "等待核对"],
|
|
3751
|
+
["发生了什么", preview.problem],
|
|
3752
|
+
["影响环节", preview.step],
|
|
3753
|
+
["影响范围", preview.whyUniversal],
|
|
3754
|
+
["改进建议", preview.suggestedFix],
|
|
3755
|
+
["相关材料", preview.includesEvidence ? preview.evidenceRefs.length + " 条记录将随报告导出" : "未附原始证据"]
|
|
3756
|
+
].map(([label, text2]) => h9("div", { key: label }, h9("dt", null, label), h9("dd", null, text2))))),
|
|
3757
|
+
h9("label", { className: "tree-report-evidence" }, h9("input", { type: "checkbox", checked: includeEvidence, onChange: (e) => setEvidence(e.target.checked) }), "附上原始证据"),
|
|
3758
|
+
h9(
|
|
3759
|
+
"div",
|
|
3760
|
+
{ className: "tree-item-actions" },
|
|
3761
|
+
h9(Command, { source, endpoint: "reports.export", disabled: !preview || Boolean(preview.error), payload: () => ({ reportIds: [selected.reportId], includeEvidence, expectedRevisions: { [selected.reportId]: preview.revision } }), onDone: (receipt) => {
|
|
3762
|
+
const artifact = receipt.result;
|
|
3763
|
+
setDownload({ url: URL.createObjectURL(new Blob([artifact.content], { type: artifact.mimeType })), filename: artifact.filename });
|
|
3764
|
+
} }, "生成本地下载"),
|
|
3765
|
+
download && h9("a", { href: download.url, download: download.filename }, "下载报告")
|
|
3766
|
+
)
|
|
3767
|
+
)
|
|
3768
|
+
))) : h9("p", { className: "tree-empty" }, "暂无报告。"),
|
|
3769
|
+
data.nextCursor && button("下一页", () => {
|
|
3770
|
+
setSelected(null);
|
|
3771
|
+
setCursor(data.nextCursor);
|
|
3772
|
+
}),
|
|
3773
|
+
cursor && button("回到第一页", () => {
|
|
3774
|
+
setSelected(null);
|
|
3775
|
+
setCursor(null);
|
|
3776
|
+
})
|
|
3777
|
+
))
|
|
3778
|
+
);
|
|
3779
|
+
}
|
|
3780
|
+
function TreeView({ source }) {
|
|
3781
|
+
const state = useSource(source), [tab, setTab] = (0, import_react9.useState)("tree"), [revision, setRevision] = (0, import_react9.useState)(0), [drawer, setDrawer] = (0, import_react9.useState)(false);
|
|
3782
|
+
const selected = state.nodes.some((n) => n.id === state.selection) ? state.selection : state.current.paths?.[0]?.at(-1)?.id ?? state.nodes.find((n) => !n.parentId)?.id;
|
|
3783
|
+
const detail = useRead(source, "node.details", selected ? { nodeId: selected } : null, revision + state.epoch);
|
|
3784
|
+
const reload = () => {
|
|
3785
|
+
setRevision((v) => v + 1);
|
|
3786
|
+
void source.refresh();
|
|
3787
|
+
}, navigate = (id) => {
|
|
3788
|
+
source.select(id);
|
|
3789
|
+
setDrawer(false);
|
|
3790
|
+
};
|
|
3791
|
+
return h9(
|
|
3792
|
+
"div",
|
|
3793
|
+
{ className: "arcfold-tree tree-view" },
|
|
3794
|
+
h9(
|
|
3795
|
+
"header",
|
|
3796
|
+
{ className: "tree-toolbar" },
|
|
3797
|
+
h9(
|
|
3798
|
+
"nav",
|
|
3799
|
+
{ "aria-label": "Arcfold 内容" },
|
|
3800
|
+
button("选目标", () => setDrawer(!drawer), { className: "tree-mobile-toggle", "aria-expanded": drawer }),
|
|
3801
|
+
...[["tree", "目标树"], ["metrics", "指标"], ["reports", "给开发者的报告"]].map(([id, label]) => button(label, () => {
|
|
3802
|
+
setTab(id);
|
|
3803
|
+
setDrawer(false);
|
|
3804
|
+
}, { key: id, "aria-pressed": tab === id }))
|
|
3805
|
+
),
|
|
3806
|
+
button("刷新", reload, { className: "tree-refresh" })
|
|
3807
|
+
),
|
|
3808
|
+
state.error && h9("p", { role: "alert" }, state.error),
|
|
3809
|
+
h9(AttributionRange, { source, state }),
|
|
3810
|
+
tab === "reports" ? h9(Reports, { source, refresh: revision }) : tab === "metrics" ? h9(Metrics, { source, refresh: revision }) : h9(
|
|
3811
|
+
"div",
|
|
3812
|
+
{ className: "tree-columns" + (drawer ? " tree-drawer-open" : ""), onKeyDown: (e) => {
|
|
3813
|
+
if (e.key === "Escape") setDrawer(false);
|
|
3814
|
+
} },
|
|
3815
|
+
drawer && button("关闭目标列表", () => setDrawer(false), { className: "tree-scrim", tabIndex: -1 }),
|
|
3816
|
+
h9(TreeNavigation, { nodes: state.nodes, selected, navigate }),
|
|
3817
|
+
h9("div", { className: "tree-detail-scroll" }, selected ? h9(ReadState, { value: detail }, (data) => h9(NodeDetail, { key: data.node.id, source, detail: data, onDone: reload, navigate })) : h9("p", { className: "tree-empty" }, state.loading ? "正在读取目标树…" : "还没有目标。"))
|
|
3818
|
+
)
|
|
3819
|
+
);
|
|
3820
|
+
}
|
|
3821
|
+
function TreeNavigation({ nodes, selected, navigate }) {
|
|
3822
|
+
const [query, setQuery] = (0, import_react9.useState)(""), [collapsed, setCollapsed] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
|
|
3823
|
+
const byId = new Map(nodes.map((n) => [n.id, n])), queryText = query.trim().toLocaleLowerCase();
|
|
3824
|
+
const ancestors = (id) => {
|
|
3825
|
+
const result = [], seen = /* @__PURE__ */ new Set([id]);
|
|
3826
|
+
let node = byId.get(id);
|
|
3827
|
+
while (node?.parentId && !seen.has(node.parentId)) {
|
|
3828
|
+
seen.add(node.parentId);
|
|
3829
|
+
result.push(node.parentId);
|
|
3830
|
+
node = byId.get(node.parentId);
|
|
3831
|
+
}
|
|
3832
|
+
return result;
|
|
3833
|
+
};
|
|
3834
|
+
(0, import_react9.useEffect)(() => {
|
|
3835
|
+
setCollapsed((old) => new Set([...old].filter((id) => !ancestors(selected).includes(id))));
|
|
3836
|
+
}, [selected]);
|
|
3837
|
+
const matches = new Set(nodes.filter((n) => n.name.toLocaleLowerCase().includes(queryText)).flatMap((n) => [n.id, ...ancestors(n.id)]));
|
|
3838
|
+
const visible = orderedNodes(nodes).filter((n) => queryText ? matches.has(n.id) : !ancestors(n.id).some((id) => collapsed.has(id)));
|
|
3839
|
+
const toggle = (id) => setCollapsed((old) => {
|
|
3840
|
+
const next = new Set(old);
|
|
3841
|
+
next.has(id) ? next.delete(id) : next.add(id);
|
|
3842
|
+
return next;
|
|
3843
|
+
});
|
|
3844
|
+
const keyboard = (event, node, index) => {
|
|
3845
|
+
let target;
|
|
3846
|
+
if (event.key === "ArrowDown") target = visible[Math.min(index + 1, visible.length - 1)];
|
|
3847
|
+
if (event.key === "ArrowUp") target = visible[Math.max(index - 1, 0)];
|
|
3848
|
+
if (event.key === "Home") target = visible[0];
|
|
3849
|
+
if (event.key === "End") target = visible.at(-1);
|
|
3850
|
+
if (event.key === "ArrowLeft") {
|
|
3851
|
+
if (!collapsed.has(node.id) && nodes.some((n) => n.parentId === node.id) && !queryText) toggle(node.id);
|
|
3852
|
+
else target = byId.get(node.parentId);
|
|
3853
|
+
}
|
|
3854
|
+
if (event.key === "ArrowRight") {
|
|
3855
|
+
if (collapsed.has(node.id) && !queryText) toggle(node.id);
|
|
3856
|
+
else target = nodes.find((n) => n.parentId === node.id);
|
|
3857
|
+
}
|
|
3858
|
+
if (/^(Arrow|Home|End)/.test(event.key)) event.preventDefault();
|
|
3859
|
+
if (target) {
|
|
3860
|
+
const buttons = event.currentTarget.closest("[role=tree]").querySelectorAll("[role=treeitem]");
|
|
3861
|
+
[...buttons].find((b) => b.dataset.nodeId === target.id)?.focus();
|
|
3862
|
+
}
|
|
3863
|
+
};
|
|
3864
|
+
return h9(
|
|
3865
|
+
"aside",
|
|
3866
|
+
{ className: "tree-pane", "aria-label": "目标树" },
|
|
3867
|
+
h9("label", { className: "tree-search" }, h9("span", null, "搜索目标"), h9("input", { type: "search", value: query, placeholder: "搜索目标…", onChange: (e) => setQuery(e.target.value) })),
|
|
3868
|
+
h9("div", { className: "tree-list", role: "tree", "aria-label": "所有目标" }, visible.map((node, index) => {
|
|
3869
|
+
const branch = nodes.some((n) => n.parentId === node.id), expanded = queryText ? true : !collapsed.has(node.id), depth = ancestors(node.id).length;
|
|
3870
|
+
return h9(
|
|
3871
|
+
"div",
|
|
3872
|
+
{ key: node.id, className: "tree-row" + (selected === node.id ? " selected" : ""), style: { paddingInlineStart: Math.min(depth, 5) * 14 } },
|
|
3873
|
+
branch ? button(expanded ? "⌄" : "›", () => toggle(node.id), { className: "tree-disclosure", title: (expanded ? "收起" : "展开") + node.name, "aria-label": (expanded ? "收起" : "展开") + node.name, "aria-expanded": expanded, disabled: Boolean(queryText) }) : h9("span", { className: "tree-disclosure-placeholder" }),
|
|
3874
|
+
button(h9(import_react9.default.Fragment, null, h9(StatusDot, { status: node.status }), h9("span", { className: "tree-node-name" }, node.name), node.statusOrigin === "cascade" && h9("small", null, "随上级")), () => navigate(node.id), { role: "treeitem", "aria-level": depth + 1, "aria-selected": selected === node.id, ...branch ? { "aria-expanded": expanded } : {}, className: "tree-node", "data-node-id": node.id, title: node.name + " · " + nodeStatus(node), onKeyDown: (event) => keyboard(event, node, index) })
|
|
3875
|
+
);
|
|
3876
|
+
}), !visible.length && h9("p", { className: "tree-empty" }, "没有找到匹配的目标。")),
|
|
3877
|
+
h9("div", { className: "tree-legend" }, Object.entries(statuses).map(([status2, label]) => h9("span", { key: status2 }, h9(StatusDot, { status: status2 }), label)))
|
|
3878
|
+
);
|
|
3879
|
+
}
|
|
3880
|
+
function orderedNodes(nodes) {
|
|
3881
|
+
const result = [], seen = /* @__PURE__ */ new Set(), visit = (node) => {
|
|
3882
|
+
if (seen.has(node.id)) return;
|
|
3883
|
+
seen.add(node.id);
|
|
3884
|
+
result.push(node);
|
|
3885
|
+
nodes.filter((n) => n.parentId === node.id).forEach(visit);
|
|
3886
|
+
};
|
|
3887
|
+
nodes.filter((n) => !n.parentId || !nodes.some((parent) => parent.id === n.parentId)).forEach(visit);
|
|
3888
|
+
nodes.forEach(visit);
|
|
3889
|
+
return result;
|
|
3890
|
+
}
|
|
3891
|
+
function TreeTurnTail() {
|
|
3892
|
+
return null;
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
// src/tree-client.mjs
|
|
3896
|
+
function installTreeClient(ctx) {
|
|
3897
|
+
const sources = /* @__PURE__ */ new Map();
|
|
3898
|
+
const props = (sessionId) => {
|
|
3899
|
+
if (!sources.has(sessionId)) sources.set(sessionId, createTreeSource({ rpc: ctx.connection.rpc, sessionId, subscribeReset: (listener) => ctx.on("connection/reset", listener) }));
|
|
3900
|
+
return { source: sources.get(sessionId) };
|
|
3901
|
+
};
|
|
3902
|
+
ctx.effect(() => () => {
|
|
3903
|
+
sources.forEach((source) => source.dispose());
|
|
3904
|
+
sources.clear();
|
|
3905
|
+
}, "Arcfold tree reads");
|
|
3906
|
+
ctx.effect(() => {
|
|
3907
|
+
const style = document.createElement("style");
|
|
3908
|
+
style.dataset.plugin = "arcfold-tree-v3";
|
|
3909
|
+
style.textContent = true ? "/* The native shell owns brand, theme, chat and draft state. Only the Arcfold view is laid out here. */\n.arcfold-tree{font:inherit;font-size:13px;line-height:1.65;color:var(--dsw-alias-text-primary,inherit);--tree-bg:var(--dsw-alias-bg-base,Canvas);--tree-line:var(--dsw-alias-border-l2,color-mix(in srgb,currentColor 12%,transparent));--tree-faint:var(--dsw-alias-border-l1,color-mix(in srgb,currentColor 5%,transparent));--tree-soft:var(--dsw-alias-interactive-bg-hover,color-mix(in srgb,currentColor 4%,transparent));--tree-muted:var(--dsw-alias-text-tertiary,color-mix(in srgb,currentColor 55%,transparent));--tree-secondary:var(--dsw-alias-text-secondary,color-mix(in srgb,currentColor 70%,transparent));--tree-accent:#658f77;--tree-danger:#bd6353}\n.arcfold-tree *{box-sizing:border-box}.arcfold-tree button,.arcfold-tree input,.arcfold-tree textarea,.arcfold-tree select{font:inherit;color:inherit;background:var(--tree-bg);border:1px solid var(--tree-line);border-radius:8px;padding:6px 12px;max-width:100%}\n.arcfold-tree button{cursor:pointer;min-height:34px;line-height:1.5;font-weight:500}.arcfold-tree button:hover{background:var(--tree-soft)}.arcfold-tree button:disabled{opacity:.5;cursor:default}.arcfold-tree :focus-visible{outline:2px solid var(--tree-accent);outline-offset:3px}.arcfold-tree h2,.arcfold-tree h3,.arcfold-tree p{margin:0}.arcfold-tree h2{font-size:21px;font-weight:600;line-height:1.5}.arcfold-tree h3{font-size:12px;font-weight:600;color:var(--tree-secondary)}.arcfold-tree h4{font-size:12px;margin:12px 0 4px}.arcfold-tree small{font-size:11px;font-weight:400;color:var(--tree-muted)}.arcfold-tree pre{white-space:pre-wrap;overflow-wrap:anywhere;font:inherit}.arcfold-tree summary{cursor:pointer}.tree-muted,.tree-empty{color:var(--tree-muted)}.tree-empty{padding:7px 0 4px;font-size:12px}.arcfold-tree [role=alert]{color:var(--tree-danger);padding:8px 16px}.arcfold-tree [role=status]{font-size:12px}.arcfold-tree .tree-primary{background:var(--dsw-alias-text-primary,#0f1115);color:var(--tree-bg);border-color:transparent}.arcfold-tree .tree-primary:hover{opacity:.85}.arcfold-tree .tree-danger{color:var(--tree-danger);border-color:color-mix(in srgb,var(--tree-danger) 35%,transparent);background:color-mix(in srgb,var(--tree-danger) 3%,var(--tree-bg))}.arcfold-tree .tree-danger:hover{background:color-mix(in srgb,var(--tree-danger) 10%,var(--tree-bg))}\n/* Native data attributes are stable shell boundaries; hide, never unmount, the saved chat draft. */\n[data-conversation-scroll]:has(.tree-view){overflow:hidden}\n[data-conversation-scroll]:has(.tree-view) [data-composer-seat]{display:none}\n.tree-view{container-type:inline-size;display:flex;flex-direction:column;flex:1;min-height:0;height:var(--dsh-conversation-viewport-height,calc(100dvh - 76px));max-height:var(--dsh-conversation-viewport-height,calc(100dvh - 76px));width:100%;background:var(--tree-bg)}\n.tree-toolbar{height:52px;flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 24px;border-bottom:1px solid var(--tree-faint)}.tree-toolbar nav{display:flex;gap:7px;min-width:0}.tree-toolbar button{border:0;min-height:30px;padding:5px 12px;font-size:12px;color:var(--tree-secondary);background:transparent;white-space:nowrap}.tree-toolbar button[aria-pressed=true]{background:var(--tree-soft);color:inherit}.tree-toolbar .tree-refresh{font-size:11px;color:var(--tree-muted);padding:5px 8px}.tree-toolbar .tree-mobile-toggle{display:none}\n.tree-columns{display:grid;grid-template-columns:258px minmax(0,1fr);flex:1;min-height:0;position:relative}.tree-pane{display:flex;flex-direction:column;min-height:0;padding:18px 12px 16px;border-right:1px solid var(--tree-line);background:var(--tree-bg)}.tree-search{display:block;position:relative;margin-bottom:14px}.tree-search>span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}.tree-search input{width:100%;font-size:12px;border:0;background:var(--tree-soft);padding:8px 10px}.tree-list{flex:1;min-height:0;overflow:auto}.tree-row{display:flex;align-items:center;min-height:35px;margin:2px 0;border-radius:8px;padding-right:6px;gap:1px}.tree-row:hover{background:var(--tree-soft)}.tree-row.selected{background:color-mix(in srgb,var(--tree-accent) 12%,var(--tree-bg))}.tree-row.selected .tree-node-name{font-weight:600}.arcfold-tree .tree-disclosure{font-size:15px;width:20px;padding:0;flex:none;min-height:28px;border:0;background:transparent;color:var(--tree-muted)}.tree-disclosure-placeholder{width:20px;flex:none}.arcfold-tree .tree-node{display:flex;align-items:center;gap:7px;flex:1;min-width:0;text-align:left;border:0;background:transparent;padding:5px 2px;min-height:35px;font-size:12px;font-weight:400}.tree-node-name{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.tree-node small{white-space:nowrap;font-size:10px}.tree-dot{width:7px;height:7px;flex:none;display:inline-block;border-radius:50%;background:#54816b}.tree-dot.paused{background:#b88238}.tree-dot.completed{background:#868e96}.tree-dot.abandoned{background:var(--tree-danger)}.tree-legend{display:flex;flex-wrap:wrap;gap:8px 11px;padding:14px 4px 0;margin-top:10px;border-top:1px solid var(--tree-faint);font-size:10px;color:var(--tree-muted)}.tree-legend span{display:flex;align-items:center;gap:5px}.tree-legend .tree-dot{width:6px;height:6px}.tree-scrim{display:none}\n.tree-detail-scroll{min-width:0;overflow:auto;padding:26px 30px 44px}.tree-detail{max-width:1040px;margin:auto;min-width:0}.tree-breadcrumbs{display:flex;align-items:center;flex-wrap:wrap;gap:5px 7px;margin-bottom:8px;color:var(--tree-muted);font-size:11px}.tree-breadcrumbs button{padding:0;border:0;min-height:0;font-size:11px;max-width:240px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:400;background:none}.tree-node-title>summary{list-style:none;display:flex;align-items:flex-start;gap:8px}.tree-node-title>summary::-webkit-details-marker{display:none}.tree-node-title>summary:after{content:'⌄';opacity:0;color:var(--tree-muted);padding-top:5px}.tree-node-title>summary:hover:after,.tree-node-title>summary:focus-visible:after,.tree-node-title[open]>summary:after{opacity:1}.tree-node-title h2{overflow-wrap:anywhere;letter-spacing:-.3px}.tree-node-status{display:flex;align-items:center;gap:6px;color:var(--tree-secondary);font-size:11px;margin-top:7px!important;margin-bottom:22px!important}.tree-panel{min-width:0;border:1px solid var(--tree-line);border-radius:12px;padding:17px 19px;background:var(--tree-bg)}.tree-panel>h3{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:10px}.tree-summary{margin-bottom:16px}.tree-summary-copy{font-size:13px;line-height:1.85;overflow-wrap:anywhere}.tree-summary-copy p+p{margin-top:8px}.tree-summary-more{margin-top:10px;font-size:12px}.tree-summary-more>summary{color:var(--tree-muted);font-size:11px}.tree-summary-more ul{margin:4px 0;padding-left:18px}.tree-detail-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;align-items:start}.tree-item{border-top:1px solid var(--tree-faint)}.tree-panel>.tree-item:nth-child(2){border-top:0}.tree-item>summary{display:flex;align-items:flex-start;gap:12px;list-style:none;padding:13px 0;font-size:13px;line-height:1.75;overflow-wrap:anywhere}.tree-item>summary::-webkit-details-marker{display:none}.tree-item>summary>span{flex:1;min-width:0}.tree-item>summary:after{content:'›';font-size:18px;line-height:22px;color:var(--tree-muted);transform:rotate(0);transition:transform .15s}.tree-item[open]>summary:after{transform:rotate(90deg)}.tree-item[open]>summary{padding-bottom:9px}.tree-item>summary:hover{color:var(--tree-accent)}.tree-item-detail{font-size:12px;padding:0 0 16px}.tree-item-actions{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap;margin:2px 0 1px}.tree-item-actions button{font-size:12px;padding:6px 14px}.tree-command{display:inline-flex;flex-wrap:wrap;align-items:center;gap:6px;max-width:100%}.tree-command small{font-size:11px}.tree-method-description{background:var(--tree-soft);border-radius:8px;padding:12px;margin-top:12px;color:var(--tree-secondary)}.tree-method-description dl{display:grid;gap:7px;margin:0}.tree-method-description dl>div{display:grid;grid-template-columns:50px minmax(0,1fr);gap:9px}.tree-method-description dt{color:var(--tree-muted)}.tree-method-description dd{margin:0;overflow-wrap:anywhere;white-space:pre-wrap}.tree-evaluation-met{color:#54816b}.tree-evaluation-not_met{color:var(--tree-danger)}.tree-method-more{margin-top:10px}.tree-method-more>summary{color:var(--tree-muted)}.tree-method-more p{white-space:pre-wrap;margin-top:8px}.tree-method-more strong{display:block;margin-top:10px}.tree-editor,.tree-delete-confirm{margin:12px 0;padding:12px;border:1px solid var(--tree-line);border-radius:8px}.tree-delete-confirm p{margin-bottom:8px}.tree-delete-confirm>button{margin-left:8px}.tree-field{display:block;margin:8px 0;color:var(--tree-secondary)}.tree-field input,.tree-field select{display:block;width:100%;margin-top:4px}.arcfold-tree textarea{display:block;width:100%;min-height:90px;resize:vertical;margin:6px 0 12px}.arcfold-tree .tree-child{display:flex;align-items:center;gap:8px;width:100%;text-align:left;font-size:12px;padding:9px 0;border:0;border-top:1px solid var(--tree-faint);border-radius:0;background:none;font-weight:400}.tree-child>span:nth-child(2){flex:1;overflow-wrap:anywhere}.tree-child>span:last-child{color:var(--tree-muted)}.tree-child:nth-child(2){border-top:0}\n.tree-page{overflow:auto;flex:1;min-height:0;padding:32px 40px 48px}.tree-page-heading{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:28px}.tree-page-heading p{font-size:12px;color:var(--tree-muted);margin-top:6px}.tree-metric-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.tree-metric-card{padding:19px 20px}.tree-metric-card h3{color:inherit;font-size:13px}.tree-metric-value{font-size:30px;font-weight:500;line-height:1.5;margin-top:20px}.tree-metric-value small{font-size:12px;margin-left:7px;color:var(--tree-secondary)}.tree-metric-card p{font-size:11px;margin-top:7px}.tree-chart{width:100%;height:100px;margin-top:24px;overflow:visible}.tree-chart polyline{fill:none;stroke:var(--tree-accent);stroke-width:2}.tree-chart circle{fill:var(--tree-accent)}.tree-chart-base{stroke:var(--tree-line);fill:none}.tree-chart-empty{height:100px;display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--tree-muted);margin-top:24px}.tree-outcomes{margin-top:16px}.tree-outcome-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:20px}.tree-outcome-grid span{display:block;color:var(--tree-secondary);font-size:12px}.tree-outcome-grid strong{display:block;font-size:26px;font-weight:500;margin-top:13px}.tree-reports>p{color:var(--tree-muted);font-size:12px;margin:8px 0 24px}.tree-reports article{border:1px solid var(--tree-line);border-radius:12px;padding:16px;margin-top:12px}.tree-reports article>button{display:block;text-align:left;border:0;padding:0;background:transparent;font-size:13px}.tree-reports pre{padding:12px;background:var(--tree-soft);border-radius:8px}\n.tree-dock{display:flex;gap:6px;flex-wrap:wrap;font-size:11px;min-width:0}.arcfold-tree .tree-chip{display:inline-flex;align-items:center;gap:7px;max-width:min(100%,360px);min-width:0;min-height:28px;border-radius:7px;font-size:11px;padding:4px 8px;text-align:left;font-weight:400}.tree-chip>span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-chip>small{white-space:nowrap;flex:none}.tree-detail-dialog{position:fixed;inset:8vh max(16px,10vw);z-index:1000;background:var(--tree-bg);overflow:auto;padding:24px;border:1px solid var(--tree-line);border-radius:12px;box-shadow:0 0 0 100vmax #0005}.tree-detail-dialog>button{float:right;margin-left:12px}.tree-attribution-range{width:100%;min-width:0;padding:12px;border:1px solid var(--tree-line);border-radius:8px;font-size:12px}.tree-attribution-range blockquote{white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0;padding-left:12px;border-left:2px solid var(--tree-line)}.tree-attribution-range select{max-width:100%}\n@container(max-width:1000px){.tree-columns{grid-template-columns:230px minmax(0,1fr)}.tree-detail-scroll{padding:24px}.tree-detail-grid{grid-template-columns:1fr}.tree-metric-value small{display:block;margin-left:0}.tree-page{padding:28px 24px}}\n@container(max-width:700px){.tree-toolbar{padding:0 12px;gap:4px}.tree-toolbar nav{gap:2px}.tree-toolbar button{padding:5px 8px;font-size:11px}.tree-toolbar .tree-mobile-toggle{display:block}.tree-toolbar .tree-refresh{padding:5px}.tree-columns{display:block;overflow:hidden}.tree-pane{position:absolute;inset:0 auto 0 0;width:min(290px,85%);z-index:3;visibility:hidden;transform:translateX(-100%);transition:transform .15s;background:var(--tree-bg)}.tree-drawer-open .tree-pane{transform:none;visibility:visible;box-shadow:12px 0 24px #0001}.tree-drawer-open .tree-scrim{display:block;position:absolute;inset:0;border:0;border-radius:0;max-width:none;width:100%;height:100%;padding:0;background:#0003;color:transparent;font-size:0;z-index:2}.tree-detail-scroll{height:100%;padding:24px 16px 36px}.tree-detail h2{font-size:19px}.tree-panel{padding:15px 16px}.tree-breadcrumbs button{max-width:180px}.tree-page{padding:24px 16px}.tree-page-heading{align-items:flex-start}.tree-page-heading h2{font-size:19px}.tree-metric-grid{grid-template-columns:1fr}.tree-metric-value small{display:inline;margin-left:7px}.tree-chart-empty{height:70px;margin-top:12px}.tree-outcome-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.tree-detail-dialog{inset:16px;padding:18px}.tree-detail-dialog .tree-detail-grid{grid-template-columns:1fr}}\n@media(pointer:coarse){.arcfold-tree .tree-item-actions button,.arcfold-tree .tree-command button{min-height:44px}.tree-item>summary{min-height:44px}}\n@media(prefers-reduced-motion:reduce){.arcfold-tree *{transition:none!important}}\n\n.tree-report-list .tree-report{border:0;border-top:1px solid var(--tree-faint);border-radius:0;padding:0;margin:0}.tree-report-list .tree-report:first-child{border:0}.tree-report-list .tree-report-toggle{display:flex;align-items:center;justify-content:space-between;width:100%;gap:12px;padding:14px 0;font-weight:400}.tree-report-toggle>span:last-child{color:var(--tree-muted)}.tree-report-detail{padding-bottom:16px}.tree-report-evidence{display:flex;align-items:center;gap:8px;margin:14px 0;font-size:12px;color:var(--tree-secondary)}.tree-report-detail .tree-method-description{margin-top:0}\n" : "";
|
|
3910
|
+
document.head.append(style);
|
|
3911
|
+
return () => style.remove();
|
|
3912
|
+
}, "Arcfold tree appearance");
|
|
3913
|
+
for (const [name, component] of [["conversation.input.dock", TreeDock], ["conversation.view", TreeView], ["conversation.chat.turnTail", ({ source, turn }) => import_react10.default.createElement(TreeTurnTail, { source, nativeTurn: turn.turn })]])
|
|
3914
|
+
ctx.slots.inject(name, () => ctx.slots.register({ name, id: "arcfold-tree-v3", order: 20, label: "Arcfold", inject: props }, component));
|
|
3915
|
+
}
|
|
3916
|
+
|
|
3917
|
+
// src/DesktopSetup.mjs
|
|
3918
|
+
var import_react11 = __toESM(require("react"), 1);
|
|
3919
|
+
var h10 = import_react11.default.createElement;
|
|
3920
|
+
function DesktopSetup({ initial, rpc, openWorkspace }) {
|
|
3921
|
+
const [info, setInfo] = (0, import_react11.useState)(initial), [checked, setChecked] = (0, import_react11.useState)(false);
|
|
3922
|
+
const [busy, setBusy] = (0, import_react11.useState)(false), [message, setMessage] = (0, import_react11.useState)("");
|
|
3923
|
+
async function refresh() {
|
|
3924
|
+
const reply = await rpc.call("/api", "arcfoldDesktop/status", { args: { request: {} } });
|
|
3925
|
+
if (reply?.ok !== true) throw new Error("无法读取设置状态。");
|
|
3926
|
+
setInfo(reply.value);
|
|
3927
|
+
return reply.value;
|
|
3928
|
+
}
|
|
3929
|
+
async function open(path) {
|
|
3930
|
+
if (typeof openWorkspace !== "function") throw new Error("DSH 工作区服务尚未就绪。");
|
|
3931
|
+
await openWorkspace(path);
|
|
3932
|
+
globalThis.location.reload();
|
|
3933
|
+
}
|
|
3934
|
+
async function start() {
|
|
3935
|
+
if (!checked || busy) return;
|
|
3936
|
+
setBusy(true);
|
|
3937
|
+
setMessage("正在创建独立数据并启动 Arcfold…");
|
|
3938
|
+
let initialized = false;
|
|
3939
|
+
try {
|
|
3940
|
+
const result = await rpc.call("/api", "arcfoldDesktop/start", { args: { request: { confirmed: true } } });
|
|
3941
|
+
if (result?.ok !== true) throw new Error("设置结果尚未确认。");
|
|
3942
|
+
setInfo(result.value);
|
|
3943
|
+
if (result.value?.state === "ready") {
|
|
3944
|
+
initialized = true;
|
|
3945
|
+
setMessage("设置完成,正在打开独立工作区。");
|
|
3946
|
+
await open(result.value.workspace);
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
setMessage("设置尚未完成,请查看当前状态。");
|
|
3950
|
+
} catch {
|
|
3951
|
+
setMessage(initialized ? "设置已完成,但工作区未能打开。请重试打开工作区。" : "设置结果尚未确认,请刷新状态,不要重复提交。");
|
|
3952
|
+
try {
|
|
3953
|
+
await refresh();
|
|
3954
|
+
} catch {
|
|
3955
|
+
}
|
|
3956
|
+
} finally {
|
|
3957
|
+
setBusy(false);
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
const refreshButton = h10("button", { type: "button", onClick: () => void refresh().catch(() => setMessage("无法读取设置状态。")) }, "刷新状态");
|
|
3961
|
+
if (info.state !== "needs_setup") return h10(
|
|
3962
|
+
"section",
|
|
3963
|
+
{ className: "arcfold-desktop-setup" },
|
|
3964
|
+
h10("h2", null, "Arcfold 设置"),
|
|
3965
|
+
h10("p", null, info.state === "configured" ? "正在读取已保存的设置。" : info.state === "ready" ? "Arcfold 已就绪。" : "Arcfold 尚未完成启动,请保留已有数据并检查设置。"),
|
|
3966
|
+
info.activationError && h10("p", { role: "alert" }, info.activationError),
|
|
3967
|
+
message && h10("p", { role: "status" }, message),
|
|
3968
|
+
info.state === "ready" && h10("button", { type: "button", disabled: busy, onClick: () => {
|
|
3969
|
+
setBusy(true);
|
|
3970
|
+
void open(info.workspace).catch(() => setMessage("无法打开工作区,请稍后重试。")).finally(() => setBusy(false));
|
|
3971
|
+
} }, "打开 Arcfold 工作区"),
|
|
3972
|
+
refreshButton
|
|
3973
|
+
);
|
|
3974
|
+
return h10(
|
|
3975
|
+
"section",
|
|
3976
|
+
{ className: "arcfold-desktop-setup" },
|
|
3977
|
+
h10("h2", null, "开始使用 Arcfold"),
|
|
3978
|
+
h10("p", null, "Arcfold 会在 Desktop 中保存独立的目标、经历和费用记录。其他版本已有的数据不会自动导入。"),
|
|
3979
|
+
h10("p", null, "工作区:", h10("code", null, info.workspace)),
|
|
3980
|
+
h10("p", null, "数据位置:", h10("code", null, info.dataRoot)),
|
|
3981
|
+
h10("p", null, "主对话和与其相关的后台处理使用当前对话选择的模型;不同后台任务保留各自的思考设置。完成后可在“设置 → 模型”中配置 DeepSeek 密钥。"),
|
|
3982
|
+
h10("p", null, "跨多段对话自动整理时,使用最新相关对话的模型;没有关联对话时使用默认模型。"),
|
|
3983
|
+
h10("p", null, "模型服务会产生费用。这里记录请求和估算费用,但不设本地消费上限;实际扣费以服务商为准。"),
|
|
3984
|
+
h10("p", null, "当前估算价格资料核对日期:", info.priceReviewedAt, "。"),
|
|
3985
|
+
h10(
|
|
3986
|
+
"button",
|
|
3987
|
+
{ type: "button", "aria-pressed": checked, onClick: () => setChecked((value) => !value) },
|
|
3988
|
+
checked ? "已确认:模型调用可能产生费用,本机不设消费上限" : "确认:模型调用可能产生费用,本机不设消费上限"
|
|
3989
|
+
),
|
|
3990
|
+
h10("div", null, h10("button", { type: "button", disabled: !checked || busy, onClick: () => void start() }, busy ? "正在设置…" : "创建并开始"), " ", refreshButton),
|
|
3991
|
+
message && h10("p", { role: "status" }, message)
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
// src/DesktopModels.mjs
|
|
3996
|
+
var import_react12 = __toESM(require("react"), 1);
|
|
3997
|
+
var h11 = import_react12.default.createElement;
|
|
3998
|
+
var REF = "DEEPSEEK_API_KEY";
|
|
3999
|
+
function DesktopModels({ rpc }) {
|
|
4000
|
+
const [info, setInfo] = (0, import_react12.useState)(null), [secret, setSecret] = (0, import_react12.useState)("");
|
|
4001
|
+
const [busy, setBusy] = (0, import_react12.useState)(false), [message, setMessage] = (0, import_react12.useState)("");
|
|
4002
|
+
async function refresh() {
|
|
4003
|
+
const result = await rpc.call("/api", "credentials/describe", { args: { refs: [REF] } });
|
|
4004
|
+
if (result?.ok !== true) throw new Error("无法读取密钥状态。");
|
|
4005
|
+
const value = result.value?.[REF];
|
|
4006
|
+
setInfo({ configured: value?.configured === true, writable: value?.writable === true });
|
|
4007
|
+
}
|
|
4008
|
+
(0, import_react12.useEffect)(() => {
|
|
4009
|
+
let active = true;
|
|
4010
|
+
void rpc.call("/api", "credentials/describe", { args: { refs: [REF] } }).then((result) => {
|
|
4011
|
+
if (!active) return;
|
|
4012
|
+
if (result?.ok !== true) throw new Error("unavailable");
|
|
4013
|
+
const value = result.value?.[REF];
|
|
4014
|
+
setInfo({ configured: value?.configured === true, writable: value?.writable === true });
|
|
4015
|
+
}).catch(() => {
|
|
4016
|
+
if (active) setMessage("无法读取密钥状态,请稍后重试。");
|
|
4017
|
+
});
|
|
4018
|
+
return () => {
|
|
4019
|
+
active = false;
|
|
4020
|
+
};
|
|
4021
|
+
}, [rpc]);
|
|
4022
|
+
async function save() {
|
|
4023
|
+
if (!info?.writable || !secret.trim() || busy) return;
|
|
4024
|
+
setBusy(true);
|
|
4025
|
+
setMessage("正在保存…");
|
|
4026
|
+
try {
|
|
4027
|
+
const result = await rpc.call("/api", "credentials/set", { args: { ref: REF, value: secret } });
|
|
4028
|
+
if (result?.ok !== true) throw new Error("unconfirmed");
|
|
4029
|
+
await refresh();
|
|
4030
|
+
setMessage("已保存到 Desktop 的凭据服务。");
|
|
4031
|
+
} catch {
|
|
4032
|
+
setMessage("保存结果尚未确认,请刷新状态后再决定。");
|
|
4033
|
+
} finally {
|
|
4034
|
+
setSecret("");
|
|
4035
|
+
setBusy(false);
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
return h11(
|
|
4039
|
+
"section",
|
|
4040
|
+
{ className: "arcfold-desktop-setup" },
|
|
4041
|
+
h11("h2", null, "DeepSeek"),
|
|
4042
|
+
h11("p", null, "密钥由 DSH 统一保存和使用;Arcfold 不把密钥写进自身数据或费用记录。"),
|
|
4043
|
+
h11("p", null, !info ? "正在读取密钥状态…" : info.configured ? "DeepSeek 密钥已配置。" : "尚未配置 DeepSeek 密钥。"),
|
|
4044
|
+
h11("label", null, "DeepSeek API 密钥", h11("input", {
|
|
4045
|
+
type: "password",
|
|
4046
|
+
autoComplete: "off",
|
|
4047
|
+
value: secret,
|
|
4048
|
+
disabled: !info?.writable || busy,
|
|
4049
|
+
onChange: (event) => setSecret(event.target.value)
|
|
4050
|
+
})),
|
|
4051
|
+
h11("button", { type: "button", disabled: !info?.writable || !secret.trim() || busy, onClick: () => void save() }, "保存密钥"),
|
|
4052
|
+
h11("button", { type: "button", disabled: busy, onClick: () => void refresh().catch(() => setMessage("无法读取密钥状态。")) }, "刷新状态"),
|
|
4053
|
+
message && h11("p", { role: "status" }, message)
|
|
4054
|
+
);
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
// src/desktop-workspace.mjs
|
|
4058
|
+
async function openDesktopWorkspace(workspaces, navigation, path) {
|
|
4059
|
+
if (!workspaces || !navigation || typeof path !== "string" || !path.startsWith("/"))
|
|
4060
|
+
throw new Error("DSH 工作区服务尚未就绪。");
|
|
4061
|
+
const workspace = await workspaces.create({ path });
|
|
4062
|
+
if (!workspace?.workspaceId) throw new Error("无法确认新工作区。");
|
|
4063
|
+
await navigation.openWorkspace(workspace.workspaceId, void 0, { agentPreset: "arcfold-main" });
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
// src/client.mjs
|
|
4067
|
+
var inject = ["slots", "connection", "locale", "fileUpload"];
|
|
4068
|
+
var NS = "arcfold-methods";
|
|
4069
|
+
async function apply(ctx) {
|
|
4070
|
+
let desktop;
|
|
4071
|
+
try {
|
|
4072
|
+
desktop = await ctx.connection.rpc.call("/api", "arcfoldDesktop/status", { args: { request: {} } });
|
|
4073
|
+
} catch {
|
|
4074
|
+
}
|
|
4075
|
+
if (desktop?.ok === true && ["needs_setup", "configured", "activation_failed", "interrupted", "unavailable", "ready"].includes(desktop.value?.state)) {
|
|
4076
|
+
ctx.effect(() => {
|
|
4077
|
+
const style = document.createElement("style");
|
|
4078
|
+
style.dataset.plugin = "arcfold-desktop-setup";
|
|
4079
|
+
style.textContent = ".arcfold-desktop-setup{max-width:42rem;margin:1rem auto;padding:1.5rem;line-height:1.6}.arcfold-desktop-setup code{overflow-wrap:anywhere}.arcfold-desktop-setup button{margin-top:1rem;padding:.5rem .9rem}.arcfold-desktop-setup label{display:block}";
|
|
4080
|
+
document.head.append(style);
|
|
4081
|
+
return () => style.remove();
|
|
4082
|
+
}, "Arcfold Desktop setup style");
|
|
4083
|
+
ctx.slots.inject("settings.models.footer", () => ctx.slots.register({
|
|
4084
|
+
name: "settings.models.footer",
|
|
4085
|
+
id: "arcfold-deepseek-credential",
|
|
4086
|
+
order: 11,
|
|
4087
|
+
inject: () => ({ rpc: ctx.connection.rpc })
|
|
4088
|
+
}, DesktopModels));
|
|
4089
|
+
}
|
|
4090
|
+
if (desktop?.ok === true && ["needs_setup", "configured", "activation_failed", "interrupted", "unavailable"].includes(desktop.value?.state)) {
|
|
4091
|
+
const props = {
|
|
4092
|
+
initial: desktop.value,
|
|
4093
|
+
rpc: ctx.connection.rpc,
|
|
4094
|
+
openWorkspace: (path) => openDesktopWorkspace(ctx.get("workspaces"), ctx.get("uiWorkspace"), path)
|
|
4095
|
+
};
|
|
4096
|
+
for (const name of ["settings.section", "conversation.hero.options"])
|
|
4097
|
+
ctx.slots.inject(name, () => ctx.slots.register({ name, id: "arcfold-desktop-setup", order: 11, label: "Arcfold 设置", inject: () => props }, DesktopSetup));
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
const mode = await ctx.connection.rpc.call("/api", "arcfoldMode/current", { args: {} });
|
|
4101
|
+
if (mode?.ok !== true || typeof mode.value?.goalTreeV3 !== "boolean") throw new Error("Arcfold 页面模式尚未确认,请重新连接。");
|
|
4102
|
+
if (mode.value.goalTreeV3) {
|
|
4103
|
+
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({ name: "conversation.input.dock", id: "goal", priority: -1 }, () => null));
|
|
4104
|
+
return installTreeClient(ctx);
|
|
4105
|
+
}
|
|
4106
|
+
ctx.slots.inject("conversation.input.dock", () => ctx.slots.register({
|
|
4107
|
+
name: "conversation.input.dock",
|
|
4108
|
+
id: "goal",
|
|
4109
|
+
priority: -1
|
|
4110
|
+
}, () => null));
|
|
4111
|
+
const sources = /* @__PURE__ */ new Set(), sessions = /* @__PURE__ */ new Map();
|
|
4112
|
+
const browseScope = createBrowseScopeSource(sources);
|
|
4113
|
+
ctx.effect(() => ctx.locale.register(NS, { zh: { ...zh, ...goalZh, ...modelsZh, ...understandingZh, ...importsZh, ...opportunitiesZh }, en: { ...en, ...goalEn, ...modelsEn, ...understandingEn, ...importsEn, ...opportunitiesEn } }), "Arcfold method UI copy");
|
|
4114
|
+
ctx.effect(() => {
|
|
4115
|
+
const style = document.createElement("style");
|
|
4116
|
+
style.dataset.plugin = "@arcfold/dsh-method-ui";
|
|
4117
|
+
style.textContent = true ? '.arcfold-methods {box-sizing:border-box;width:calc(100% - 4 * var(--dsh-composer-dock-inset, 8px));max-width:var(--dsh-composer-card-max-width, 800px);margin:0 auto 8px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:var(--dsw-radius-md, 12px);background:var(--dsw-specific-menu);color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;}\n.arcfold-methods header,.arcfold-methods nav,.arcfold-methods .actions {display:flex;align-items:center;flex-wrap:wrap;gap:8px;}\n.arcfold-methods header strong {flex:1;}\n.arcfold-methods button {font:inherit;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:4px 8px;cursor:pointer;}\n.arcfold-methods button:disabled {opacity:.5;cursor:not-allowed;}\n.arcfold-methods button:focus-visible {outline:2px solid currentColor;outline-offset:2px;}\n.arcfold-methods ul {list-style:none;padding:0;margin:8px 0;}\n.arcfold-methods li {margin:8px 0;padding-block:4px;}\n.arcfold-methods p {margin:6px 0;}\n.arcfold-methods .detail {max-height:min(60vh,36rem);overflow:auto;overscroll-behavior:contain;margin-top:8px;border-top:1px solid var(--dsw-alias-border-l1);padding-top:8px;}\n.arcfold-methods .text {white-space:pre-wrap;overflow-wrap:anywhere;}\n.arcfold-methods .meta {font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;}\n.arcfold-methods [role=alert] {color:var(--dsw-alias-state-error-primary);white-space:pre-wrap;}\n.arcfold-methods h3,.arcfold-methods h4 {font-size:13px;margin:12px 0 4px;}\n.arcfold-methods .source {border-left:2px solid var(--dsw-alias-border-l1);padding-left:8px;}\n.arcfold-goals textarea {display:block;box-sizing:border-box;width:100%;min-height:5rem;margin:6px 0;padding:8px;font:inherit;resize:vertical;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;}\n.arcfold-goals .goal-discussion {margin-top:12px;}\n.arcfold-goals .goal-work {margin:10px 0;padding-block:4px;}\n.arcfold-goals .goal-work button {margin:4px 8px 4px 0;}\n.arcfold-goals summary {cursor:pointer;}\n.arcfold-goals .goal-cards {display:grid;gap:14px;}\n.arcfold-goals .goal-card {min-width:0;padding:18px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);overflow-wrap:anywhere;}\n.arcfold-goals .goal-card-heading {align-items:flex-start;justify-content:space-between;gap:12px;}\n.arcfold-goals .goal-card-heading h3 {flex:1;min-width:0;margin:0;font-size:15px;line-height:1.6;}\n.arcfold-goals .goal-status-badge {display:inline-block;flex-shrink:0;border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:2px 8px;font-size:11px;color:var(--dsw-alias-label-secondary);}\n.arcfold-goals .goal-outcome {margin-top:12px;color:var(--dsw-alias-label-secondary);}\n.arcfold-goals .goal-card-section {margin-top:18px;}\n.arcfold-goals .goal-card-section h4 {margin:0 0 8px;font-size:12px;color:var(--dsw-alias-label-secondary);}\n.arcfold-goals .goal-work {margin:0;padding:0;}\n.arcfold-goals .goal-work + .goal-work {border-top:1px solid var(--dsw-alias-border-l1);margin-top:14px;padding-top:14px;}\n.arcfold-goals .goal-work-blocker {margin-top:10px;padding:10px 12px;border-left:3px solid var(--dsw-alias-border-l1);background:var(--dsw-specific-menu);border-radius:0 6px 6px 0;}\n.arcfold-goals .goal-work-blocker h5 {margin:0;font-size:12px;}\n.arcfold-goals .goal-work-details {margin-top:8px;font-size:12px;}\n.arcfold-goals .goal-card-evidence {border-top:1px solid var(--dsw-alias-border-l1);margin-top:18px;padding-top:12px;}\n.arcfold-goals .goal-card-evidence > summary,.arcfold-goals .goal-work-details > summary {color:var(--dsw-alias-label-secondary);}\n.arcfold-goals .goal-other-work {margin-top:16px;}\n.arcfold-goals summary:focus-visible {outline:2px solid currentColor;outline-offset:3px;}\n@media (max-width:480px) {.arcfold-goals .goal-card {padding:14px;}.arcfold-goals .goal-card-heading h3 {flex-basis:100%;}}\n\n.arcfold-model-settings {width:100%;max-width:52rem;margin:0;padding:16px;}\n.arcfold-model-settings header h2 {flex:1;font-size:16px;margin:0;}\n.arcfold-model-settings h3 {font-size:14px;margin-top:18px;}\n.arcfold-model-settings .model-card {border-top:1px solid var(--dsw-alias-border-l1);padding:8px 0;}\n.arcfold-model-settings label {display:flex;flex-direction:column;gap:4px;min-width:0;}\n.arcfold-model-settings input,.arcfold-model-settings select {box-sizing:border-box;min-width:0;max-width:100%;width:100%;padding:7px;font:inherit;color:inherit;background:var(--dsw-specific-menu);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;}\n.arcfold-model-settings .model-fields {display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:8px;margin:10px 0;}\n.arcfold-model-settings .actions {margin:8px 0;}\n.arcfold-model-settings dl {margin:8px 0;display:flex;flex-wrap:wrap;gap:8px 20px;}\n.arcfold-model-settings dt {font-size:12px;color:var(--dsw-alias-label-secondary);}\n.arcfold-model-settings dd {margin:0;overflow-wrap:anywhere;}\n\n.arcfold-understandings .text { white-space: pre-wrap; overflow-wrap: anywhere; }\n.arcfold-understandings textarea { display: block; box-sizing: border-box; width: 100%; min-height: 8rem; resize: vertical; }\n.arcfold-understandings summary { cursor: pointer; overflow-wrap: anywhere; }\n\n.arcfold-imports label { display:flex; flex-direction:column; gap:4px; margin:8px 0; }\n.arcfold-imports input,.arcfold-imports select { box-sizing:border-box; width:100%; max-width:100%; min-width:0; padding:7px; font:inherit; color:inherit; background:var(--dsw-specific-menu); border:1px solid var(--dsw-alias-border-l1); border-radius:8px; }\n.arcfold-imports .import-counts { display:flex; flex-wrap:wrap; gap:8px 20px; }\n.arcfold-imports dt { font-size:12px; color:var(--dsw-alias-label-secondary); }\n.arcfold-imports dd { margin:0; }\n.arcfold-imports .import-decision { border-top:1px solid var(--dsw-alias-border-l1); margin-top:12px; padding-top:8px; }\n.arcfold-imports summary { cursor:pointer; overflow-wrap:anywhere; }\n\n.arcfold-view {box-sizing:border-box;width:100%;max-width:60rem;margin:0 auto;padding:16px;min-width:0;color:var(--dsw-alias-label-primary);}\n.arcfold-toolbar {display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;margin-bottom:16px;}\n.arcfold-toolbar h2 {font-size:16px;margin:0;}\n.arcfold-view-scope {display:flex;flex-wrap:wrap;gap:6px;margin:0 0 16px;}\n.arcfold-view-scope button {font:inherit;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:7px 12px;cursor:pointer;}\n.arcfold-view-scope button[aria-pressed="true"] {background:var(--dsw-alias-bg-layer-1);border-color:currentColor;}\n.arcfold-view-scope button:focus-visible {outline:2px solid currentColor;outline-offset:2px;}\n.arcfold-toolbar button,.arcfold-hero-entry button {font:inherit;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:7px 12px;cursor:pointer;}\n.arcfold-toolbar button:focus-visible,.arcfold-hero-entry button:focus-visible {outline:2px solid currentColor;outline-offset:2px;}\n.arcfold-view > .arcfold-methods,.arcfold-dialog > .arcfold-model-settings {width:100%;max-width:none;}\n.arcfold-view > .arcfold-methods {margin-bottom:16px;padding:18px;border-radius:14px;}\n.arcfold-view > .arcfold-methods > header {gap:10px;min-height:30px;}\n.arcfold-view > .arcfold-methods > header > strong {font-size:14px;}\n.arcfold-view > .arcfold-methods > .detail {max-height:none;overflow:visible;margin-top:16px;padding-top:16px;}\n.arcfold-view > .arcfold-methods > .detail > ul > li {padding:14px;margin:10px 0;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;overflow-wrap:anywhere;}\n.arcfold-opportunities .opportunity-overview {padding:0 0 16px;}\n.arcfold-opportunities .opportunity-overview > p:first-child {margin:0 0 10px;color:var(--dsw-alias-label-secondary);}\n.arcfold-opportunities .opportunity-status {display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-bottom:10px;}\n.arcfold-opportunities .status-badge {display:inline-block;border:1px solid var(--dsw-alias-border-l1);border-radius:6px;padding:2px 8px;font-size:11px;line-height:1.6;color:var(--dsw-alias-label-secondary);}\n.arcfold-opportunities ul.opportunity-list {display:grid;gap:12px;margin:0;}\n.arcfold-view > .arcfold-opportunities > .detail > ul.opportunity-list > li {min-width:0;margin:0;padding:18px;background:var(--dsw-alias-bg-layer-1);border-radius:12px;}\n.arcfold-opportunities .opportunity-card > h3 {margin:0;font-size:14px;font-weight:600;line-height:1.7;}\n.arcfold-opportunities .opportunity-field {margin-top:14px;}\n.arcfold-opportunities .opportunity-card h4 {margin:0 0 4px;font-size:11px;font-weight:500;color:var(--dsw-alias-label-secondary);}\n.arcfold-opportunities .opportunity-field p {margin:0;line-height:1.7;}\n.arcfold-opportunities .opportunity-draft,.arcfold-opportunities .opportunity-evidence {border-top:1px solid var(--dsw-alias-border-l1);margin-top:16px;padding-top:12px;}\n.arcfold-opportunities .opportunity-evidence summary {cursor:pointer;font-size:12px;color:var(--dsw-alias-label-secondary);}\n.arcfold-opportunities .opportunity-evidence summary:focus-visible {outline:2px solid currentColor;outline-offset:4px;}\n.arcfold-opportunities .opportunity-card pre {font:inherit;font-size:12px;line-height:1.7;max-width:100%;}\n.arcfold-opportunities .opportunity-pagination {margin-top:16px;justify-content:flex-end;}\n.arcfold-hero-entry {display:flex;flex-wrap:wrap;justify-content:center;gap:8px;color:var(--dsw-alias-label-primary);}\n.arcfold-dialog {box-sizing:border-box;width:min(56rem,calc(100vw - 24px));max-width:calc(100vw - 24px);max-height:calc(100dvh - 32px);overflow:auto;overscroll-behavior:contain;padding:16px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);}\n.arcfold-dialog::backdrop {background:rgb(0 0 0 / .35);}\n.arcfold-dialog > .arcfold-view {padding:0;}\n@media (max-width:480px) {.arcfold-hero-entry {flex-direction:column;align-items:stretch;}.arcfold-view,.arcfold-dialog {padding:12px;}.arcfold-methods header {align-items:flex-start;}.arcfold-methods header strong {flex-basis:100%;}}\n@media (max-width:480px) {.arcfold-view > .arcfold-methods {padding:14px;margin-bottom:12px;}.arcfold-view > .arcfold-opportunities > .detail > ul.opportunity-list > li {padding:14px;}}\n' : "";
|
|
4118
|
+
document.head.append(style);
|
|
4119
|
+
return () => style.remove();
|
|
4120
|
+
}, "Arcfold method UI style");
|
|
4121
|
+
ctx.effect(() => () => {
|
|
4122
|
+
browseScope.dispose();
|
|
4123
|
+
for (const source of sources) source.dispose();
|
|
4124
|
+
sources.clear();
|
|
4125
|
+
sessions.clear();
|
|
4126
|
+
}, "Arcfold method reads");
|
|
4127
|
+
const modelSettings = createModelSettingsSource({
|
|
4128
|
+
rpc: ctx.connection.rpc,
|
|
4129
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
4130
|
+
});
|
|
4131
|
+
sources.add(modelSettings);
|
|
4132
|
+
const modelProps = {
|
|
4133
|
+
hooks: { models: modelSettings },
|
|
4134
|
+
onRefresh: () => modelSettings.refresh(),
|
|
4135
|
+
onSaveLearning: (request) => modelSettings.saveLearning(request),
|
|
4136
|
+
onStoreCredential: (provider, value) => modelSettings.storeCredential(provider, value),
|
|
4137
|
+
onRemoveCredential: (provider) => modelSettings.removeCredential(provider)
|
|
4138
|
+
};
|
|
4139
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
4140
|
+
name: "settings.section",
|
|
4141
|
+
id: "arcfold-models",
|
|
4142
|
+
order: 11,
|
|
4143
|
+
locale: NS,
|
|
4144
|
+
label: () => ctx.locale.bind(NS)("modelsTitle"),
|
|
4145
|
+
inject: () => modelProps
|
|
4146
|
+
}, ModelSettings));
|
|
4147
|
+
const sessionProps = (sessionId) => {
|
|
4148
|
+
if (sessions.has(sessionId)) return sessions.get(sessionId);
|
|
4149
|
+
const viewScope = browseScope.getSnapshot().viewScope;
|
|
4150
|
+
const source = createMethodSource({
|
|
4151
|
+
rpc: ctx.connection.rpc,
|
|
4152
|
+
sessionId,
|
|
4153
|
+
viewScope,
|
|
4154
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
4155
|
+
});
|
|
4156
|
+
sources.add(source);
|
|
4157
|
+
const goals = createGoalSource({
|
|
4158
|
+
rpc: ctx.connection.rpc,
|
|
4159
|
+
sessionId,
|
|
4160
|
+
viewScope,
|
|
4161
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
4162
|
+
});
|
|
4163
|
+
sources.add(goals);
|
|
4164
|
+
const understandings = createUnderstandingSource({
|
|
4165
|
+
rpc: ctx.connection.rpc,
|
|
4166
|
+
sessionId,
|
|
4167
|
+
viewScope,
|
|
4168
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
4169
|
+
});
|
|
4170
|
+
sources.add(understandings);
|
|
4171
|
+
const imports = createImportSource({
|
|
4172
|
+
rpc: ctx.connection.rpc,
|
|
4173
|
+
fileUpload: ctx.fileUpload,
|
|
4174
|
+
sessionId,
|
|
4175
|
+
viewScope,
|
|
4176
|
+
subscribeReset: (listener) => ctx.on("connection/reset", listener)
|
|
4177
|
+
});
|
|
4178
|
+
sources.add(imports);
|
|
4179
|
+
const opportunities = createOpportunitySource({ rpc: ctx.connection.rpc, sessionId, viewScope, subscribeReset: (listener) => ctx.on("connection/reset", listener) });
|
|
4180
|
+
sources.add(opportunities);
|
|
4181
|
+
const props = {
|
|
4182
|
+
...modelProps,
|
|
4183
|
+
onModelsRefresh: modelProps.onRefresh,
|
|
4184
|
+
opportunitySessionId: sessionId,
|
|
4185
|
+
understandingSessionId: sessionId,
|
|
4186
|
+
importSessionId: sessionId,
|
|
4187
|
+
hooks: { models: modelSettings, browseScope, methods: source, goals, understandings, imports, opportunities },
|
|
4188
|
+
onViewScopeChange: (viewScope2) => browseScope.setViewScope(viewScope2),
|
|
4189
|
+
onOpportunityDetails: (id) => opportunities.details(id),
|
|
4190
|
+
onOpportunityAct: (endpoint, request) => opportunities.act(endpoint, request),
|
|
4191
|
+
onOpportunityRefresh: () => opportunities.refresh(),
|
|
4192
|
+
onOpportunityPage: (offset) => opportunities.refresh(offset),
|
|
4193
|
+
onImportUpload: (file, onProgress) => imports.upload(file, onProgress),
|
|
4194
|
+
onImportPrepare: (request) => imports.prepare(request),
|
|
4195
|
+
onImportDetails: (request) => imports.details(request),
|
|
4196
|
+
onImportConfirm: (request) => imports.confirm(request),
|
|
4197
|
+
onImportWithdraw: (request) => imports.withdraw(request),
|
|
4198
|
+
onImportRefresh: () => imports.refresh(),
|
|
4199
|
+
onImportPage: (offset) => imports.refresh(offset),
|
|
4200
|
+
onDetails: (methodId, offset) => source.details(methodId, offset),
|
|
4201
|
+
onDecide: (request) => source.decide(request),
|
|
4202
|
+
onAcknowledge: (id) => source.acknowledge(id),
|
|
4203
|
+
onRefresh: () => source.refresh(),
|
|
4204
|
+
onPage: (offset) => source.refresh(offset),
|
|
4205
|
+
onGoalDetails: (id) => goals.details(id),
|
|
4206
|
+
onGoalDecide: (request) => goals.decide(request),
|
|
4207
|
+
onGoalDiscuss: (request) => goals.discuss(request),
|
|
4208
|
+
onGoalRefresh: () => goals.refresh(),
|
|
4209
|
+
onGoalStopWork: (request) => goals.stopWork(request),
|
|
4210
|
+
onGoalResumeWork: (request) => goals.resumeWork(request),
|
|
4211
|
+
onGoalPage: (offset) => goals.refresh(offset),
|
|
4212
|
+
onUnderstandingDetails: (id) => understandings.details(id),
|
|
4213
|
+
onUnderstandingDecide: (request) => understandings.decide(request),
|
|
4214
|
+
onUnderstandingCorrect: (request) => understandings.correct(request),
|
|
4215
|
+
onUnderstandingRefresh: () => understandings.refresh(),
|
|
4216
|
+
onUnderstandingPage: (offset) => understandings.refresh(offset)
|
|
4217
|
+
};
|
|
4218
|
+
sessions.set(sessionId, props);
|
|
4219
|
+
return props;
|
|
4220
|
+
};
|
|
4221
|
+
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
4222
|
+
name: "conversation.view",
|
|
4223
|
+
id: "arcfold",
|
|
4224
|
+
order: 20,
|
|
4225
|
+
locale: NS,
|
|
4226
|
+
label: () => ctx.locale.bind(NS)("collaborationTitle"),
|
|
4227
|
+
inject: sessionProps
|
|
4228
|
+
}, CollaborationView));
|
|
4229
|
+
ctx.slots.inject("conversation.hero.options", () => ctx.slots.register({
|
|
4230
|
+
name: "conversation.hero.options",
|
|
4231
|
+
id: "arcfold",
|
|
4232
|
+
order: 20,
|
|
4233
|
+
locale: NS,
|
|
4234
|
+
inject: sessionProps
|
|
4235
|
+
}, CollaborationHero));
|
|
4236
|
+
}
|
|
4237
|
+
return module.exports;}});
|