@lmzhen/dsh-evolution-skill-history 0.13.0 → 0.13.2

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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/lib/client.js +44 -26
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -35,11 +35,11 @@ The row is **inert without a web server** (`ctx.get('webServer')`, not a declare
35
35
 
36
36
  **The left column** lists the skills that recorded versions, one row per skill: its name, how many versions it has, its own one-line description (the listing already carried it) and the management state the marker probe reported. A **search box** filters that list by name or description — client state only, no route behind it.
37
37
 
38
- **The right pane** shows the selected skill's versions as two groups: **Body versions** (restorable) and **File versions** (history only, because a restore rewrites `SKILL.md`). A body row reads like a sentence rather than a dump — `v3 targeted edit 2026-09-27 18:20 · 3 hours ago (+1363 chars)` — with a *current* capsule on the live content, **Restore this version** (an inline two-click confirmation) on the others, and **Diff** expanding that version against its predecessor (lines added and removed, plus the changed region, marked truncated when it is long). After a restore the rows are read again — the restore itself is a new version — while the curator's result sentence stays on screen. **Refresh** re-reads the skill list and the open skill.
38
+ **The right pane** shows the selected skill's versions as two groups: **Body versions** (restorable) and **File versions** (history only, because a restore rewrites `SKILL.md`). A body row reads like a sentence rather than a dump — `v3 targeted edit 2026-09-27 18:20 · 3 hours ago (+1363 chars)` — with a *current* capsule on the live content, **Restore this version** (an inline two-click confirmation, with a **Cancel** beside it once it is armed) on the others, and **Diff** expanding that version against its predecessor (lines added and removed, plus the changed region, marked truncated when it is long). After a restore the rows are read again — the restore itself is a new version — while the curator's result sentence stays on screen. **Refresh** re-reads the skill list and the open skill.
39
39
 
40
40
  **The panel owns no rule and no arithmetic.** The host reports every row with its whole verdict — `undoable` (evolution-core's artifact classification against the live bytes), `actionKind` (a closed vocabulary key), `age` (a bucket plus a count), `charsDelta`, `summary` — and this half substitutes words from its locale dictionary and renders. The time, the action words and the sentences are therefore localized; the facts are computed once, host-side, so the panel and the slash commands cannot drift.
41
41
 
42
- **Appearance follows the platform's tokens, not its modules.** The panel carries its own stylesheet (`src/client/styles.ts`, injected once behind `data-plugin-css`) and copies its metrics from the platform's own components — the command card's `<pre>` for the diff block, the sidebar row's hover and focus ring for the controls — while every size is a `--dsh-content-font-size*` token or a `calc()` over one, so the panel follows the deployment's content size the way the platform's panels do. Importing the platform's control package is NOT possible from here without splitting this package's compiler faces: the specifier is path-mapped to source, and `tsc -b` then refuses the foreign project (host/client face split, `react` devDependencies and a CI graph change would all be needed). The roll call of what the panel deliberately does not do is in `## Known limitations and deferred work`.
42
+ **Appearance follows the platform's tokens, not its modules.** The panel carries its own stylesheet (`src/client/styles.ts`, injected once behind `data-plugin-css`) and copies its metrics from the platform's own components — the command card's `<pre>` for the diff block, the sidebar row's hover and focus ring for the controls, and the theme's semantic state pair (`--dsw-alias-state-success-primary` / `--dsw-alias-state-error-primary`) for the diff's added and removed lines — while every size is a `--dsh-content-font-size*` token or a `calc()` over one, so the panel follows the deployment's content size the way the platform's panels do. Importing the platform's control package is NOT possible from here without splitting this package's compiler faces: the specifier is path-mapped to source, and `tsc -b` then refuses the foreign project (host/client face split, `react` devDependencies and a CI graph change would all be needed). The roll call of what the panel deliberately does not do is in `## Known limitations and deferred work`.
43
43
 
44
44
  ## Build
45
45
 
package/lib/client.js CHANGED
@@ -68,8 +68,7 @@ window.__ModuleLoader__.load({
68
68
  const zh = {
69
69
  "entry.label": "技能历史",
70
70
  title: "技能历史",
71
- hint: "每次技能写入留下的内容版本。回退只改 SKILL.md 正文,标记、使用计数与策展状态不变。",
72
- "hint.current": "内容相同的版本会同时标「当前」。",
71
+ hint: "每次技能写入留下的内容版本。回退只改 SKILL.md 正文,标记、使用计数与策展状态不变。内容相同的版本会同时标「当前」。",
73
72
  search: "按名字或描述筛选",
74
73
  "search.none": "没有匹配的技能。",
75
74
  "group.content": "正文版本",
@@ -130,8 +129,7 @@ window.__ModuleLoader__.load({
130
129
  const en = {
131
130
  "entry.label": "Skill history",
132
131
  title: "Skill history",
133
- hint: "The content versions every skill write leaves behind. Restoring changes the SKILL.md body only: markers, usage counts and curation state stay as they are.",
134
- "hint.current": "Versions holding the same content are all marked “current”.",
132
+ hint: "The content versions every skill write leaves behind. Restoring changes the SKILL.md body only: markers, usage counts and curation state stay as they are. Versions holding the same content are all marked “current”.",
135
133
  search: "Filter by name or description",
136
134
  "search.none": "No skill matches.",
137
135
  "group.content": "Body versions",
@@ -242,7 +240,7 @@ window.__ModuleLoader__.load({
242
240
  ".evo-hist-skill-line{display:flex;align-items:baseline;gap:6px}",
243
241
  ".evo-hist-skill-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
244
242
  ".evo-hist-skill-count{flex:none;margin-left:auto;color:var(--dsw-alias-label-tertiary)}",
245
- ".evo-hist-skill-desc{margin-top:2px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
243
+ ".evo-hist-skill-desc{display:block;margin-top:2px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
246
244
  ".evo-hist-main{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;padding:12px 14px}",
247
245
  ".evo-hist-head{display:flex;align-items:center;gap:10px;margin:0 0 4px}",
248
246
  ".evo-hist-title{flex:1 1 auto;margin:0;font-size:inherit;font-weight:600}",
@@ -262,7 +260,7 @@ window.__ModuleLoader__.load({
262
260
  ".evo-hist-button:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}",
263
261
  ".evo-hist-button[data-tone=\"primary\"]{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}",
264
262
  ".evo-hist-pre{margin:6px 0 2px;padding:12px 16px;max-height:260px;overflow:auto;border:.5px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-primary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap}",
265
- ".evo-hist-pre-add{color:var(--dsw-alias-brand-primary)}",
263
+ ".evo-hist-pre-add{color:var(--dsw-alias-state-success-primary)}",
266
264
  ".evo-hist-pre-del{color:var(--dsw-alias-state-error-primary)}"
267
265
  ].join("\n");
268
266
  //#endregion
@@ -333,7 +331,7 @@ window.__ModuleLoader__.load({
333
331
  return (0, react.createElement)("pre", { className: "evo-hist-pre" }, rows.length === 0 ? "±" : rows);
334
332
  }
335
333
  /** One version row: what happened, when, how big, and what it is against. */
336
- function versionRow(face, row, confirming, openDiff, diff, restore, toggleDiff) {
334
+ function versionRow(face, row, confirming, openDiff, diff, restore, cancelRestore, toggleDiff) {
337
335
  const parts = [
338
336
  (0, react.createElement)("span", {
339
337
  key: "v",
@@ -355,15 +353,25 @@ window.__ModuleLoader__.load({
355
353
  key: "current",
356
354
  className: "evo-hist-capsule"
357
355
  }, face.t("current")));
358
- if (row.undoable === true) actions.push((0, react.createElement)("button", {
359
- key: "undo",
360
- type: "button",
361
- className: "evo-hist-button",
362
- "data-tone": confirming === row.v ? "primary" : void 0,
363
- onClick: () => {
364
- restore(row.v);
365
- }
366
- }, confirming === row.v ? face.t("undo.confirm") : face.t("undo")));
356
+ if (row.undoable === true) {
357
+ if (confirming === row.v) actions.push((0, react.createElement)("button", {
358
+ key: "cancel",
359
+ type: "button",
360
+ className: "evo-hist-button",
361
+ onClick: () => {
362
+ cancelRestore();
363
+ }
364
+ }, face.t("undo.no")));
365
+ actions.push((0, react.createElement)("button", {
366
+ key: "undo",
367
+ type: "button",
368
+ className: "evo-hist-button",
369
+ "data-tone": confirming === row.v ? "primary" : void 0,
370
+ onClick: () => {
371
+ restore(row.v);
372
+ }
373
+ }, confirming === row.v ? face.t("undo.confirm") : face.t("undo")));
374
+ }
367
375
  if (row.undoable === true || row.charsDelta !== void 0) actions.push((0, react.createElement)("button", {
368
376
  key: "diff",
369
377
  type: "button",
@@ -389,7 +397,7 @@ window.__ModuleLoader__.load({
389
397
  /** One group: a heading, an optional note, and its rows. */
390
398
  function group(face, title, entries, noteText, state) {
391
399
  if (entries.length === 0) return null;
392
- return (0, react.createElement)("section", { key: title }, (0, react.createElement)("h3", { className: "evo-hist-group" }, title), noteText === void 0 ? null : (0, react.createElement)("p", { className: "evo-hist-hint" }, noteText), ...entries.map((entry) => versionRow(face, entry, state.confirming, state.openDiff, state.diffs.get(entry.v), state.restore, state.toggleDiff)));
400
+ return (0, react.createElement)("section", { key: title }, (0, react.createElement)("h3", { className: "evo-hist-group" }, title), noteText === void 0 ? null : (0, react.createElement)("p", { className: "evo-hist-hint" }, noteText), ...entries.map((entry) => versionRow(face, entry, state.confirming, state.openDiff, state.diffs.get(entry.v), state.restore, state.cancelRestore, state.toggleDiff)));
393
401
  }
394
402
  /**
395
403
  * The panel body.
@@ -499,11 +507,16 @@ window.__ModuleLoader__.load({
499
507
  const needle = query.trim().toLowerCase();
500
508
  const visible = (0, react.useMemo)(() => needle === "" ? skills : skills.filter((skill) => skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle)), [skills, needle]);
501
509
  const loaded = payload !== void 0;
510
+ /** Drop the pending confirmation: the button that opened it must not be the only way out. */
511
+ const cancelRestore = () => {
512
+ setPending(void 0);
513
+ };
502
514
  const rowState = {
503
515
  confirming: pending,
504
516
  openDiff,
505
517
  diffs,
506
518
  restore,
519
+ cancelRestore,
507
520
  toggleDiff
508
521
  };
509
522
  return (0, react.createElement)("div", { className: "evo-hist-root" }, (0, react.createElement)("aside", { className: "evo-hist-aside" }, (0, react.createElement)("div", { className: "evo-hist-search" }, (0, react.createElement)("input", {
@@ -526,7 +539,7 @@ window.__ModuleLoader__.load({
526
539
  type: "button",
527
540
  className: "evo-hist-button",
528
541
  onClick: refresh
529
- }, face.t("refresh"))), (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("hint") + " " + face.t("hint.current")), noteText === void 0 ? null : (0, react.createElement)("p", {
542
+ }, face.t("refresh"))), (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("hint")), noteText === void 0 ? null : (0, react.createElement)("p", {
530
543
  className: "evo-hist-note",
531
544
  "data-error": noteError ? "true" : void 0
532
545
  }, noteText), selected === void 0 ? null : loaded ? null : (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("loading")), loaded ? (0, react.createElement)("div", null, group(face, face.t("group.content"), payload.content, void 0, rowState), group(face, face.t("group.support"), payload.support, face.t("group.support.note"), rowState), payload.content.length === 0 && payload.support.length === 0 ? (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("empty.versions")) : null) : null));
@@ -551,18 +564,23 @@ window.__ModuleLoader__.load({
551
564
  /** Row order inside the global panel list (beside the platform's own rows). */
552
565
  const PANEL_ORDER = 35;
553
566
  /**
554
- * The row's icon: a platform-style line glyph rather than an emoji.
567
+ * The row's icon: a platform-style line glyph, sized by the SIDEBAR's own contract.
555
568
  *
556
- * An emoji cannot follow `currentColor`, cannot match the 16/18px the sidebar hands its glyph slot,
557
- * and renders differently on every platform font — the sidebar's other rows are 1.5px-stroke line
558
- * icons, and this one now is too. The label carries the accessible name, so the glyph stays
559
- * decoration.
569
+ * The platform hands every `sidebar.panellist` occupant `{ size, active }` (`SidebarRoot.tsx`
570
+ * `renderSlot('sidebar.panellist', { size: wide ? 16 : 18, active }, …)`) and its own icons render at
571
+ * that size (`IconGlobeOutline14 size={size}` in the sidebar's own spec). The neighbouring rows in a
572
+ * shipped GUI take the OTHER route — they inject their entry into the sidebar's DOM with their own
573
+ * `svg { width: 18px; height: 18px }` CSS — so 18px is the size a row glyph is expected to occupy.
574
+ * A glyph that sizes itself from CSS (`width: 100%`) resolves against an unsized flex box and fills
575
+ * the column: the size must come from the prop, with the neighbours' 18 as the fallback. The label
576
+ * carries the accessible name, so the glyph stays decoration.
560
577
  */
561
- function PanelIcon() {
578
+ function PanelIcon(props = {}) {
579
+ const size = typeof props.size === "number" && props.size > 0 ? props.size : 18;
562
580
  return (0, react.createElement)("svg", {
563
581
  "aria-hidden": "true",
564
- width: "100%",
565
- height: "100%",
582
+ width: String(size),
583
+ height: String(size),
566
584
  viewBox: "0 0 16 16",
567
585
  fill: "none",
568
586
  stroke: "currentColor",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-skill-history",
3
3
  "description": "Web surface for skill content history: loopback host routes plus the sidebar panel (community build)",
4
- "version": "0.13.0",
4
+ "version": "0.13.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,21 +30,21 @@
30
30
  ],
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@lmzhen/dsh-evolution-core": "^0.13.0"
33
+ "@lmzhen/dsh-evolution-core": "^0.13.2"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@deepseek-ai/cordis": "^4.0.1",
37
37
  "@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.2",
38
38
  "react": "^18.2.0",
39
- "@lmzhen/dsh-evolution-curator": "^0.13.0"
39
+ "@lmzhen/dsh-evolution-curator": "^0.13.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@deepseek-ai/cordis": "^4.0.1",
43
43
  "@deepseek-ai/dsh-host-webserver": "^0.1.5-rc.2",
44
- "@lmzhen/dsh-evolution-core": "^0.13.0",
45
- "@lmzhen/dsh-evolution-curator": "^0.13.0",
46
- "@lmzhen/dsh-evolution-io": "^0.13.0",
47
- "@lmzhen/dsh-evolution-io-node": "^0.13.0"
44
+ "@lmzhen/dsh-evolution-core": "^0.13.2",
45
+ "@lmzhen/dsh-evolution-curator": "^0.13.2",
46
+ "@lmzhen/dsh-evolution-io": "^0.13.2",
47
+ "@lmzhen/dsh-evolution-io-node": "^0.13.2"
48
48
  },
49
49
  "dsh": {
50
50
  "client": {