@lmzhen/dsh-evolution-merge-ui 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,11 @@
1
1
  # @lmzhen/dsh-evolution-merge-ui
2
2
 
3
- 技能合并的只读浏览器面板:三队列计数、行列表与刷新;动作只展示为命令文本,不在浏览器写技能。
4
-
5
- ## 数据面
6
-
7
- 读取 `/api/dsh-evolution/merge/view` 与 `/api/dsh-evolution/merge/rows`,走生成的路由表和 route-client。失败按四态显示,不伪造空成功。
8
-
9
- ## 已知限制
10
-
11
- - 只读;approve/reject 等动作需复制命令执行。
12
- - 面板仅在 `evolution-merge` host 行与其 bundle 同时挂载时可用。
3
+ Browser-only sidebar panel for the merge queues. It reads the four host routes
4
+ and never writes skills: row actions are copyable `/evolution merge ...`
5
+ commands.
6
+
7
+ - Queues: pending / retry / skipped.
8
+ - Row facts: target, confidence, failure, attempts, origin, staging expiry.
9
+ - Run strip: latest run id/state/stage/progress.
10
+ - Four states: loading / ready / empty / failed; view schemaVersion guard.
11
+ - Mounted by the `all` bundle only.
package/lib/client.js CHANGED
@@ -100,12 +100,16 @@ window.__ModuleLoader__.load({
100
100
  ok: true,
101
101
  data: record.data
102
102
  };
103
- if (record.ok === false && typeof record.message === "string" && record.message !== "") return {
103
+ const nested = record.error;
104
+ const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
105
+ const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
106
+ const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
107
+ if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
104
108
  ok: false,
105
109
  failure: {
106
110
  kind: "refused",
107
- code: typeof record.code === "string" && record.code !== "" ? record.code : "refused",
108
- message: record.message
111
+ code,
112
+ message
109
113
  }
110
114
  };
111
115
  }
@@ -162,11 +166,28 @@ window.__ModuleLoader__.load({
162
166
  "empty.skipped": "没有标记为无需合并的对。",
163
167
  "loading": "正在读取…",
164
168
  "refresh": "刷新",
169
+ "loadMore": "加载更多",
170
+ "approval.on": "审批开:合并前需人工批准。",
171
+ "approval.off": "审批关:合并自动执行。",
165
172
  "failed": "读取失败,保留上次结果。",
166
173
  "stage.decide": "判定",
167
174
  "stage.author": "合并",
168
175
  "stage.validate": "检查",
169
- "stage.commit": "提交"
176
+ "stage.commit": "提交",
177
+ "row.target": "目标",
178
+ "row.confidence": "置信度",
179
+ "row.attempts": "尝试",
180
+ "row.origin": "来源",
181
+ "row.expires": "暂存到期",
182
+ "row.failure": "失败",
183
+ "row.noRetry": "不可重试",
184
+ "row.copy": "复制命令",
185
+ "origin.agent": "自动",
186
+ "origin.human": "人工",
187
+ "state.verify": "待判定",
188
+ "state.pending": "待批准",
189
+ "state.fuse": "待融合",
190
+ "state.skipped": "已跳过"
170
191
  };
171
192
  const en = {
172
193
  "entry.label": "Skill merge",
@@ -179,17 +200,35 @@ window.__ModuleLoader__.load({
179
200
  "empty.skipped": "No pairs marked as not worth merging.",
180
201
  "loading": "Loading…",
181
202
  "refresh": "Refresh",
203
+ "loadMore": "Load more",
204
+ "approval.on": "Approval on: merges wait for a human.",
205
+ "approval.off": "Approval off: merges run automatically.",
182
206
  "failed": "Read failed; keeping the last result.",
183
207
  "stage.decide": "Decide",
184
208
  "stage.author": "Author",
185
209
  "stage.validate": "Validate",
186
- "stage.commit": "Commit"
210
+ "stage.commit": "Commit",
211
+ "row.target": "Target",
212
+ "row.confidence": "Confidence",
213
+ "row.attempts": "Attempts",
214
+ "row.origin": "Origin",
215
+ "row.expires": "Staging expires",
216
+ "row.failure": "Failure",
217
+ "row.noRetry": "not retryable",
218
+ "row.copy": "Copy command",
219
+ "origin.agent": "auto",
220
+ "origin.human": "human",
221
+ "state.verify": "Unverified",
222
+ "state.pending": "Pending approval",
223
+ "state.fuse": "Ready to fuse",
224
+ "state.skipped": "Skipped"
187
225
  };
188
226
  //#endregion
189
227
  //#region src/client/styles.ts
190
228
  const CSS_TAG_ID = "evolution-merge-panel";
191
229
  const CSS = [
192
- ".evolution-merge-panel { display:flex; flex-direction:column; gap:var(--evo-space-12); padding:var(--evo-space-16); }",
230
+ ":where(.evolution-merge-panel) { --evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px }",
231
+ ".evolution-merge-panel { display:flex; flex-direction:column; gap:var(--evo-space-12); padding:var(--evo-space-16); color:var(--dsw-alias-label-primary); font-size:var(--evo-type-sm); line-height:var(--evo-leading-base); }",
193
232
  ".evolution-merge-panel [data-role=\"hint\"] { margin:0; color:var(--dsw-alias-label-tertiary); font-size:var(--evo-type-xs); }",
194
233
  ".evolution-merge-panel [data-role=\"stats\"] { display:flex; gap:var(--evo-space-8); flex-wrap:wrap; }",
195
234
  ".evolution-merge-panel [data-role=\"stat\"] { border:var(--evo-hairline-width) solid var(--dsw-alias-border-l2); border-radius:var(--evo-radius-control); padding:var(--evo-space-6) var(--evo-space-10); min-width:calc(var(--evo-space-16) * 4); }",
@@ -274,15 +313,21 @@ window.__ModuleLoader__.load({
274
313
  const [view, setView] = (0, react.useState)(face.view);
275
314
  const [tab, setTab] = (0, react.useState)("pending");
276
315
  const [rows, setRows] = (0, react.useState)(face.rows);
277
- const [loading, setLoading] = (0, react.useState)(false);
316
+ const [loading, setLoading] = (0, react.useState)(face.loadRows !== void 0 || face.loadView !== void 0);
278
317
  const [failed, setFailed] = (0, react.useState)(void 0);
279
- const loadRows = (queue) => {
318
+ const [nextCursor, setNextCursor] = (0, react.useState)(void 0);
319
+ const request = (0, react.useRef)(0);
320
+ const loadRows = (queue, cursor = 0) => {
280
321
  const load = face.loadRows;
281
322
  if (load === void 0) return;
323
+ const token = request.current + 1;
324
+ request.current = token;
282
325
  setLoading(true);
283
- load(queue).then((outcome) => {
326
+ load(queue, cursor).then((outcome) => {
327
+ if (token !== request.current) return;
284
328
  if (outcome.ok) {
285
- setRows(outcome.payload.items);
329
+ setRows((current) => [...cursor === 0 ? [] : current, ...outcome.payload.items].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)));
330
+ setNextCursor(outcome.payload.nextCursor);
286
331
  setFailed(void 0);
287
332
  } else setFailed(outcome.failure);
288
333
  }).catch(() => {
@@ -298,15 +343,32 @@ window.__ModuleLoader__.load({
298
343
  const load = face.loadView;
299
344
  if (load === void 0) return;
300
345
  load().then((outcome) => {
301
- if (outcome.ok) setView(outcome.payload);
302
- }).catch(() => {});
346
+ if (!outcome.ok) {
347
+ setFailed(outcome.failure);
348
+ return;
349
+ }
350
+ if (outcome.payload.schemaVersion !== void 0 && outcome.payload.schemaVersion !== 1) {
351
+ setFailed({
352
+ kind: "server",
353
+ status: 0
354
+ });
355
+ return;
356
+ }
357
+ setView(outcome.payload);
358
+ }).catch(() => {
359
+ setFailed({
360
+ kind: "server",
361
+ status: 0
362
+ });
363
+ });
303
364
  };
304
365
  (0, react.useEffect)(() => {
305
366
  loadView();
306
367
  loadRows("pending");
307
368
  }, [face]);
308
369
  (0, react.useEffect)(() => {
309
- loadRows(tab);
370
+ setNextCursor(void 0);
371
+ loadRows(tab, 0);
310
372
  }, [tab]);
311
373
  const counts = view.counts;
312
374
  const stats = (0, react.createElement)("div", { "data-role": "stats" }, QUEUES.map((queue) => (0, react.createElement)("div", {
@@ -314,25 +376,59 @@ window.__ModuleLoader__.load({
314
376
  "data-role": "stat",
315
377
  "data-queue": queue
316
378
  }, (0, react.createElement)("div", { "data-role": "stat-value" }, String(counts[queue])), (0, react.createElement)("div", { "data-role": "stat-label" }, t("tab." + queue)))));
317
- const tabs = (0, react.createElement)("div", { "data-role": "tabs" }, QUEUES.map((queue) => (0, react.createElement)("button", {
379
+ const latest = view.latestRun;
380
+ const runStrip = latest === void 0 ? null : (0, react.createElement)("div", { "data-role": "run-strip" }, (0, react.createElement)("span", { "data-role": "run-id" }, latest.id), (0, react.createElement)("span", { "data-role": "run-state" }, latest.state), latest.stage === void 0 ? null : (0, react.createElement)("span", { "data-role": "run-stage" }, t("stage." + latest.stage)), latest.progress === void 0 ? null : (0, react.createElement)("span", { "data-role": "run-progress" }, String(latest.progress.done) + "/" + String(latest.progress.total)));
381
+ const tabs = (0, react.createElement)("div", {
382
+ "data-role": "tabs",
383
+ role: "tablist"
384
+ }, QUEUES.map((queue) => (0, react.createElement)("button", {
318
385
  key: queue,
319
386
  type: "button",
387
+ role: "tab",
388
+ "aria-selected": tab === queue ? "true" : "false",
320
389
  "data-role": "tab",
321
390
  "data-active": tab === queue ? "true" : "false",
322
391
  onClick: () => {
323
392
  setTab(queue);
324
393
  }
325
394
  }, t("tab." + queue) + " " + String(counts[queue]))));
326
- const list = rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty." + tab)) : (0, react.createElement)("div", { "data-role": "rows" }, rows.map((row) => (0, react.createElement)("div", {
327
- key: row.id,
328
- "data-role": "row"
329
- }, (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-label" }, row.label), (0, react.createElement)("div", { "data-role": "row-meta" }, row.state + (row.stage === null ? "" : " · " + t("stage." + row.stage)))), (0, react.createElement)("div", { "data-role": "row-meta" }, row.updatedAt))));
330
- return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), stats, tabs, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failure(t, failed)), list, (0, react.createElement)("button", {
395
+ const list = rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty." + tab)) : (0, react.createElement)("div", { "data-role": "rows" }, rows.map((row) => {
396
+ const facts = [
397
+ row.target === void 0 ? void 0 : t("row.target") + " " + row.target,
398
+ row.confidence === void 0 ? void 0 : t("row.confidence") + " " + row.confidence.toFixed(2),
399
+ row.attempts === void 0 ? void 0 : t("row.attempts") + " " + String(row.attempts),
400
+ row.origin === void 0 ? void 0 : t("row.origin") + " " + t("origin." + row.origin),
401
+ row.stagingExpiresAt === void 0 ? void 0 : t("row.expires") + " " + new Date(row.stagingExpiresAt).toLocaleString(),
402
+ row.failure === void 0 ? void 0 : t("row.failure") + " " + row.failure.message + (row.failure.retryable ? "" : " · " + t("row.noRetry"))
403
+ ].filter((value) => value !== void 0).join(" · ");
404
+ return (0, react.createElement)("div", {
405
+ key: row.id,
406
+ "data-role": "row"
407
+ }, (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-label" }, row.label), (0, react.createElement)("div", { "data-role": "row-meta" }, t("state." + row.state) + (row.stage === null ? "" : " · " + t("stage." + row.stage))), facts === "" ? null : (0, react.createElement)("div", { "data-role": "row-facts" }, facts)), (0, react.createElement)("div", null, (0, react.createElement)("div", { "data-role": "row-meta" }, row.updatedAt), (0, react.createElement)("div", { "data-role": "row-actions" }, (row.actions ?? []).filter((action) => view.approval !== false || !action.includes("/evolution merge approve")).map((action) => (0, react.createElement)("button", {
408
+ key: action,
409
+ type: "button",
410
+ "data-role": "row-action",
411
+ title: action,
412
+ onClick: () => {
413
+ navigator.clipboard.writeText(action);
414
+ }
415
+ }, t("row.copy"))))));
416
+ }));
417
+ return (0, react.createElement)("section", {
418
+ className: CSS_TAG_ID,
419
+ "data-state": loading ? "loading" : failed !== void 0 ? "failed" : rows.length === 0 ? "empty" : "ready"
420
+ }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), view.approval === void 0 ? null : (0, react.createElement)("p", { "data-role": "approval" }, t(view.approval ? "approval.on" : "approval.off")), runStrip, stats, tabs, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failure(t, failed)), list, nextCursor === void 0 ? null : (0, react.createElement)("button", {
421
+ type: "button",
422
+ "data-role": "load-more",
423
+ onClick: () => {
424
+ loadRows(tab, Number(nextCursor));
425
+ }
426
+ }, t("loadMore")), (0, react.createElement)("button", {
331
427
  type: "button",
332
428
  "data-role": "refresh",
333
429
  onClick: () => {
334
430
  loadView();
335
- loadRows(tab);
431
+ loadRows(tab, 0);
336
432
  }
337
433
  }, t("refresh")));
338
434
  }
@@ -11,13 +11,27 @@ export interface MergeCounts {
11
11
  retry: number;
12
12
  skipped: number;
13
13
  }
14
+ export interface MergeRunView {
15
+ id: string;
16
+ state: string;
17
+ command?: string;
18
+ stage?: string;
19
+ progress?: {
20
+ done: number;
21
+ total: number;
22
+ };
23
+ startedAt: number;
24
+ }
14
25
  export interface MergeViewPayload {
26
+ schemaVersion?: number;
15
27
  counts: MergeCounts;
16
28
  tabs: {
17
29
  queue: MergeQueue;
18
30
  count: number;
19
31
  }[];
20
32
  commands: string[];
33
+ latestRun?: MergeRunView;
34
+ approval?: boolean;
21
35
  }
22
36
  export interface MergeRowView {
23
37
  id: string;
@@ -29,6 +43,19 @@ export interface MergeRowView {
29
43
  stage: string | null;
30
44
  label: string;
31
45
  updatedAt: string;
46
+ createdAt?: string;
47
+ target?: string;
48
+ confidence?: number;
49
+ failure?: {
50
+ stage: string;
51
+ code: string;
52
+ message: string;
53
+ retryable: boolean;
54
+ };
55
+ attempts?: number;
56
+ origin?: 'agent' | 'human';
57
+ stagingExpiresAt?: number;
58
+ actions?: string[];
32
59
  }
33
60
  export interface QueuePage {
34
61
  queue: MergeQueue;
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  ]
12
12
  }
13
13
  },
14
- "version": "0.28.0",
14
+ "version": "0.29.0",
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },