@lmzhen/dsh-evolution-skill-history 0.13.2 → 0.13.3
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 +5 -3
- package/lib/client.js +71 -28
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -33,13 +33,15 @@ The row is **inert without a web server** (`ctx.get('webServer')`, not a declare
|
|
|
33
33
|
|
|
34
34
|
## What the panel shows
|
|
35
35
|
|
|
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.
|
|
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, clipped with an ellipsis (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
|
|
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 3 hours ago (+1363 chars)`, where the absolute clock is the row's hover text — 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
|
+
|
|
40
|
+
While nothing is selected the pane says so — a line pointing at the left column, or the empty listing when no skill has a history yet.
|
|
39
41
|
|
|
40
42
|
**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
43
|
|
|
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
|
|
44
|
+
**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 type follows the shell rather than the conversation: the panel declares no `font-size` of its own (the way the platform's own surfaces declare none) and steps with `calc(1em + 2px)` for the title and `calc(1em - 1px)` for a group label. Only the diff block takes the platform's code-block token — it renders content, not chrome. The setting behind `--dsh-content-font-size*` is documented as affecting conversation content only, so the panel deliberately does not follow it. 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
45
|
|
|
44
46
|
## Build
|
|
45
47
|
|
package/lib/client.js
CHANGED
|
@@ -76,6 +76,7 @@ window.__ModuleLoader__.load({
|
|
|
76
76
|
"group.support.note": "附带文件的字节与正文共用一份索引;回退不恢复它们。",
|
|
77
77
|
"empty.skills": "还没有技能留下过内容版本。",
|
|
78
78
|
"empty.versions": "这个技能还没有记录版本。",
|
|
79
|
+
"empty.pick": "从左栏选一个技能,看它记录过的版本。",
|
|
79
80
|
loading: "读取中…",
|
|
80
81
|
refresh: "刷新",
|
|
81
82
|
undo: "回退到此版",
|
|
@@ -104,8 +105,10 @@ window.__ModuleLoader__.load({
|
|
|
104
105
|
"action.patch": "定点修改",
|
|
105
106
|
"action.update": "整篇更新",
|
|
106
107
|
"action.restore": "回退",
|
|
107
|
-
"action.delete": "
|
|
108
|
-
"action.
|
|
108
|
+
"action.delete": "删除",
|
|
109
|
+
"action.delete.note": "这一版存的是被删掉的正文",
|
|
110
|
+
"action.archive": "归档",
|
|
111
|
+
"action.archive.note": "这一版存的是被归档的正文",
|
|
109
112
|
"action.consolidate": "合并",
|
|
110
113
|
"action.restructure": "结构调整",
|
|
111
114
|
"action.support-write": "写入附带文件",
|
|
@@ -137,6 +140,7 @@ window.__ModuleLoader__.load({
|
|
|
137
140
|
"group.support.note": "Support-file bytes share this index with the body; restoring the body does not bring them back.",
|
|
138
141
|
"empty.skills": "No skill has recorded a content version yet.",
|
|
139
142
|
"empty.versions": "This skill has no recorded versions.",
|
|
143
|
+
"empty.pick": "Pick a skill on the left to see the versions it recorded.",
|
|
140
144
|
loading: "Loading…",
|
|
141
145
|
refresh: "Refresh",
|
|
142
146
|
undo: "Restore this version",
|
|
@@ -165,8 +169,10 @@ window.__ModuleLoader__.load({
|
|
|
165
169
|
"action.patch": "targeted edit",
|
|
166
170
|
"action.update": "whole-body update",
|
|
167
171
|
"action.restore": "restored",
|
|
168
|
-
"action.delete": "deleted
|
|
169
|
-
"action.
|
|
172
|
+
"action.delete": "deleted",
|
|
173
|
+
"action.delete.note": "this version holds the removed body",
|
|
174
|
+
"action.archive": "archived",
|
|
175
|
+
"action.archive.note": "this version holds the archived body",
|
|
170
176
|
"action.consolidate": "merged",
|
|
171
177
|
"action.restructure": "restructured",
|
|
172
178
|
"action.support-write": "support file written",
|
|
@@ -226,12 +232,12 @@ window.__ModuleLoader__.load({
|
|
|
226
232
|
const CSS_TAG_ID = "@lmzhen/dsh-evolution-skill-history/panel.css";
|
|
227
233
|
/** The stylesheet the panel injects once. */
|
|
228
234
|
const CSS = [
|
|
229
|
-
".evo-hist-root{display:flex;height:100%;min-height:0;min-width:0;
|
|
235
|
+
".evo-hist-root{display:flex;height:100%;min-height:0;min-width:0;color:var(--dsw-alias-label-primary)}",
|
|
230
236
|
".evo-hist-aside{display:flex;flex-direction:column;flex:0 0 264px;width:264px;min-height:0;border-right:.5px solid var(--dsw-alias-border-l2)}",
|
|
231
|
-
".evo-hist-search{padding:
|
|
237
|
+
".evo-hist-search{padding:12px 12px 4px}",
|
|
232
238
|
".evo-hist-search input{width:100%;box-sizing:border-box;padding:6px 10px;border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit}",
|
|
233
239
|
".evo-hist-search input:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}",
|
|
234
|
-
".evo-hist-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px
|
|
240
|
+
".evo-hist-list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 12px 10px}",
|
|
235
241
|
".evo-hist-empty{padding:8px 12px;color:var(--dsw-alias-label-tertiary)}",
|
|
236
242
|
".evo-hist-skill{display:block;width:100%;box-sizing:border-box;padding:7px 8px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;text-align:left;cursor:pointer}",
|
|
237
243
|
".evo-hist-skill:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
@@ -241,20 +247,22 @@ window.__ModuleLoader__.load({
|
|
|
241
247
|
".evo-hist-skill-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
242
248
|
".evo-hist-skill-count{flex:none;margin-left:auto;color:var(--dsw-alias-label-tertiary)}",
|
|
243
249
|
".evo-hist-skill-desc{display:block;margin-top:2px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
244
|
-
".evo-hist-main{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;padding:12px
|
|
250
|
+
".evo-hist-main{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;padding:12px 12px}",
|
|
245
251
|
".evo-hist-head{display:flex;align-items:center;gap:10px;margin:0 0 4px}",
|
|
246
|
-
".evo-hist-title{flex:1 1 auto;margin:0;font-size:
|
|
252
|
+
".evo-hist-title{flex:1 1 auto;margin:0;font-size:calc(1em + 2px);font-weight:600}",
|
|
247
253
|
".evo-hist-hint{margin:0 0 10px;color:var(--dsw-alias-label-secondary);line-height:1.5}",
|
|
254
|
+
".evo-hist-pane-empty{margin:2px 0 0;color:var(--dsw-alias-label-tertiary)}",
|
|
248
255
|
".evo-hist-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary);line-height:1.5;white-space:pre-wrap}",
|
|
249
256
|
".evo-hist-note[data-error=\"true\"]{color:var(--dsw-alias-state-error-primary)}",
|
|
250
|
-
".evo-hist-group{margin:10px 0 2px;font-size:
|
|
251
|
-
".evo-hist-row{display:flex;align-items:flex-start;gap:8px;padding:8px 0;border-top:.5px solid var(--dsw-alias-border-
|
|
257
|
+
".evo-hist-group{margin:10px 0 2px;font-size:calc(1em - 1px);font-weight:600;color:var(--dsw-alias-label-secondary)}",
|
|
258
|
+
".evo-hist-row{display:flex;align-items:flex-start;gap:8px;padding:8px 0;border-top:.5px solid var(--dsw-alias-border-l2)}",
|
|
259
|
+
".evo-hist-row:first-of-type{border-top:0}",
|
|
252
260
|
".evo-hist-row-body{flex:1 1 auto;min-width:0}",
|
|
253
261
|
".evo-hist-row-title{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}",
|
|
254
262
|
".evo-hist-row-meta{color:var(--dsw-alias-label-tertiary)}",
|
|
255
263
|
".evo-hist-row-summary{margin-top:2px;color:var(--dsw-alias-label-secondary);line-height:1.5}",
|
|
256
264
|
".evo-hist-actions{display:flex;flex:none;align-items:center;gap:6px}",
|
|
257
|
-
".evo-hist-capsule{flex:none;padding:1px
|
|
265
|
+
".evo-hist-capsule{flex:none;padding:1px 8px;border:0;border-radius:999px;background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-secondary)}",
|
|
258
266
|
".evo-hist-button{flex:none;font:inherit;padding:3px 10px;border:.5px solid var(--dsw-alias-border-l2);border-radius:8px;background:transparent;color:inherit;cursor:pointer}",
|
|
259
267
|
".evo-hist-button:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
260
268
|
".evo-hist-button:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}",
|
|
@@ -299,6 +307,17 @@ window.__ModuleLoader__.load({
|
|
|
299
307
|
if (skill.protectedBy !== null) return "state.protected";
|
|
300
308
|
return skill.managed ? "state.managed" : "state.foreign";
|
|
301
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* The one clause an action word needs: what the recorded artifact IS. It rides the action word's
|
|
312
|
+
* `title` instead of widening the row (a parenthetical in the sentence pushed time and size aside).
|
|
313
|
+
* @param face - copy plus the data callbacks.
|
|
314
|
+
* @param row - the version row whose action is being named.
|
|
315
|
+
* @returns the clause, or undefined for the actions that need none.
|
|
316
|
+
*/
|
|
317
|
+
function actionNote(face, row) {
|
|
318
|
+
if (row.actionKind === "delete") return face.t("action.delete.note");
|
|
319
|
+
if (row.actionKind === "archive") return face.t("action.archive.note");
|
|
320
|
+
}
|
|
302
321
|
/** The relative-time sentence for one row's bucket. */
|
|
303
322
|
function ageText(face, age) {
|
|
304
323
|
if (age.unit === "now") return face.t("time.now");
|
|
@@ -337,11 +356,15 @@ window.__ModuleLoader__.load({
|
|
|
337
356
|
key: "v",
|
|
338
357
|
className: "evo-hist-row-meta"
|
|
339
358
|
}, "v" + String(row.v)),
|
|
340
|
-
(0, react.createElement)("span", {
|
|
359
|
+
(0, react.createElement)("span", {
|
|
360
|
+
key: "action",
|
|
361
|
+
title: actionNote(face, row)
|
|
362
|
+
}, face.t("action." + row.actionKind)),
|
|
341
363
|
(0, react.createElement)("span", {
|
|
342
364
|
key: "time",
|
|
343
|
-
className: "evo-hist-row-meta"
|
|
344
|
-
|
|
365
|
+
className: "evo-hist-row-meta",
|
|
366
|
+
title: localTime(row.at)
|
|
367
|
+
}, ageText(face, row.age))
|
|
345
368
|
];
|
|
346
369
|
const delta = deltaText(face, row);
|
|
347
370
|
if (delta !== null) parts.push((0, react.createElement)("span", {
|
|
@@ -539,7 +562,7 @@ window.__ModuleLoader__.load({
|
|
|
539
562
|
type: "button",
|
|
540
563
|
className: "evo-hist-button",
|
|
541
564
|
onClick: refresh
|
|
542
|
-
}, face.t("refresh"))), (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("hint")), noteText === void 0 ? null : (0, react.createElement)("p", {
|
|
565
|
+
}, face.t("refresh"))), (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("hint")), selected === void 0 ? (0, react.createElement)("p", { className: "evo-hist-pane-empty" }, face.t(skills.length === 0 ? "empty.skills" : "empty.pick")) : null, noteText === void 0 ? null : (0, react.createElement)("p", {
|
|
543
566
|
className: "evo-hist-note",
|
|
544
567
|
"data-error": noteError ? "true" : void 0
|
|
545
568
|
}, 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));
|
|
@@ -564,24 +587,44 @@ window.__ModuleLoader__.load({
|
|
|
564
587
|
/** Row order inside the global panel list (beside the platform's own rows). */
|
|
565
588
|
const PANEL_ORDER = 35;
|
|
566
589
|
/**
|
|
567
|
-
* The row's icon: a platform-style line glyph,
|
|
590
|
+
* The row's icon: a platform-style line glyph, whose SIZE comes from the sidebar's contract and whose
|
|
591
|
+
* glyph BOX is the one the neighbouring rows draw at.
|
|
592
|
+
*
|
|
593
|
+
* Size: the platform hands every `sidebar.panellist` occupant `{ size, active }` (`SidebarRoot.tsx`
|
|
594
|
+
* `renderSlot('sidebar.panellist', { size: wide ? 16 : 18, active }, …)`), so the glyph takes its size
|
|
595
|
+
* from that prop; a glyph that sizes itself from CSS (`width: 100%`) resolves against an unsized flex
|
|
596
|
+
* box and fills the whole column.
|
|
568
597
|
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
*
|
|
598
|
+
* Box: the three rows above (task board / SSH / skill centre) are NOT slot occupants — they inject
|
|
599
|
+
* plain DOM into the sidebar with their own CSS (`.entry{padding:0 10px;gap:8px}` plus a 24px icon box
|
|
600
|
+
* holding an 18px glyph, `shared/client/sidebar-entry-core.ts`), so their glyph ink starts 13px into
|
|
601
|
+
* the column and their label 42px. Inside the platform panel row (8px padding, 8px gap) those two
|
|
602
|
+
* lines are reached by a 26px box: `8 + (26 - 16) / 2 = 13` and `8 + 26 + 8 = 42`.
|
|
603
|
+
* Matching the neighbours is a CHOICE (the platform's own rows below — the workspace folders — sit at
|
|
604
|
+
* 8px and 32px); it keeps the four plugin rows reading as one column. The label carries the accessible
|
|
605
|
+
* name, so the glyph stays decoration.
|
|
577
606
|
*/
|
|
607
|
+
/** The glyph box in px: the neighbours' 24px icon box, placed to land on their two content lines. */
|
|
608
|
+
const GLYPH_BOX = 26;
|
|
609
|
+
/** The glyph's own coordinate system: every path below is drawn inside 16 units. */
|
|
610
|
+
const GLYPH_UNITS = 16;
|
|
611
|
+
/**
|
|
612
|
+
* Trim a computed viewBox number: three decimals stay readable and are far below one pixel.
|
|
613
|
+
* @param value - the raw number.
|
|
614
|
+
* @returns the value rounded to three decimals.
|
|
615
|
+
*/
|
|
616
|
+
function round3(value) {
|
|
617
|
+
return Math.round(value * 1e3) / 1e3;
|
|
618
|
+
}
|
|
578
619
|
function PanelIcon(props = {}) {
|
|
579
620
|
const size = typeof props.size === "number" && props.size > 0 ? props.size : 18;
|
|
621
|
+
const units = round3(GLYPH_UNITS * GLYPH_BOX / size);
|
|
622
|
+
const inset = round3((units - GLYPH_UNITS) / 2);
|
|
580
623
|
return (0, react.createElement)("svg", {
|
|
581
624
|
"aria-hidden": "true",
|
|
582
|
-
width: String(
|
|
583
|
-
height: String(
|
|
584
|
-
viewBox: "
|
|
625
|
+
width: String(GLYPH_BOX),
|
|
626
|
+
height: String(GLYPH_BOX),
|
|
627
|
+
viewBox: String(-inset) + " " + String(-inset) + " " + String(units) + " " + String(units),
|
|
585
628
|
fill: "none",
|
|
586
629
|
stroke: "currentColor",
|
|
587
630
|
strokeWidth: "1.5",
|
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.
|
|
4
|
+
"version": "0.13.3",
|
|
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.
|
|
33
|
+
"@lmzhen/dsh-evolution-core": "^0.13.3"
|
|
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.
|
|
39
|
+
"@lmzhen/dsh-evolution-curator": "^0.13.3"
|
|
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.
|
|
45
|
-
"@lmzhen/dsh-evolution-curator": "^0.13.
|
|
46
|
-
"@lmzhen/dsh-evolution-io": "^0.13.
|
|
47
|
-
"@lmzhen/dsh-evolution-io-node": "^0.13.
|
|
44
|
+
"@lmzhen/dsh-evolution-core": "^0.13.3",
|
|
45
|
+
"@lmzhen/dsh-evolution-curator": "^0.13.3",
|
|
46
|
+
"@lmzhen/dsh-evolution-io": "^0.13.3",
|
|
47
|
+
"@lmzhen/dsh-evolution-io-node": "^0.13.3"
|
|
48
48
|
},
|
|
49
49
|
"dsh": {
|
|
50
50
|
"client": {
|