@lmzhen/dsh-evolution-skill-history 0.13.1 → 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 +101 -45
- 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
|
|
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
|
@@ -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": "正文版本",
|
|
@@ -77,6 +76,7 @@ window.__ModuleLoader__.load({
|
|
|
77
76
|
"group.support.note": "附带文件的字节与正文共用一份索引;回退不恢复它们。",
|
|
78
77
|
"empty.skills": "还没有技能留下过内容版本。",
|
|
79
78
|
"empty.versions": "这个技能还没有记录版本。",
|
|
79
|
+
"empty.pick": "从左栏选一个技能,看它记录过的版本。",
|
|
80
80
|
loading: "读取中…",
|
|
81
81
|
refresh: "刷新",
|
|
82
82
|
undo: "回退到此版",
|
|
@@ -105,8 +105,10 @@ window.__ModuleLoader__.load({
|
|
|
105
105
|
"action.patch": "定点修改",
|
|
106
106
|
"action.update": "整篇更新",
|
|
107
107
|
"action.restore": "回退",
|
|
108
|
-
"action.delete": "
|
|
109
|
-
"action.
|
|
108
|
+
"action.delete": "删除",
|
|
109
|
+
"action.delete.note": "这一版存的是被删掉的正文",
|
|
110
|
+
"action.archive": "归档",
|
|
111
|
+
"action.archive.note": "这一版存的是被归档的正文",
|
|
110
112
|
"action.consolidate": "合并",
|
|
111
113
|
"action.restructure": "结构调整",
|
|
112
114
|
"action.support-write": "写入附带文件",
|
|
@@ -130,8 +132,7 @@ window.__ModuleLoader__.load({
|
|
|
130
132
|
const en = {
|
|
131
133
|
"entry.label": "Skill history",
|
|
132
134
|
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”.",
|
|
135
|
+
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
136
|
search: "Filter by name or description",
|
|
136
137
|
"search.none": "No skill matches.",
|
|
137
138
|
"group.content": "Body versions",
|
|
@@ -139,6 +140,7 @@ window.__ModuleLoader__.load({
|
|
|
139
140
|
"group.support.note": "Support-file bytes share this index with the body; restoring the body does not bring them back.",
|
|
140
141
|
"empty.skills": "No skill has recorded a content version yet.",
|
|
141
142
|
"empty.versions": "This skill has no recorded versions.",
|
|
143
|
+
"empty.pick": "Pick a skill on the left to see the versions it recorded.",
|
|
142
144
|
loading: "Loading…",
|
|
143
145
|
refresh: "Refresh",
|
|
144
146
|
undo: "Restore this version",
|
|
@@ -167,8 +169,10 @@ window.__ModuleLoader__.load({
|
|
|
167
169
|
"action.patch": "targeted edit",
|
|
168
170
|
"action.update": "whole-body update",
|
|
169
171
|
"action.restore": "restored",
|
|
170
|
-
"action.delete": "deleted
|
|
171
|
-
"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",
|
|
172
176
|
"action.consolidate": "merged",
|
|
173
177
|
"action.restructure": "restructured",
|
|
174
178
|
"action.support-write": "support file written",
|
|
@@ -228,12 +232,12 @@ window.__ModuleLoader__.load({
|
|
|
228
232
|
const CSS_TAG_ID = "@lmzhen/dsh-evolution-skill-history/panel.css";
|
|
229
233
|
/** The stylesheet the panel injects once. */
|
|
230
234
|
const CSS = [
|
|
231
|
-
".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)}",
|
|
232
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)}",
|
|
233
|
-
".evo-hist-search{padding:
|
|
237
|
+
".evo-hist-search{padding:12px 12px 4px}",
|
|
234
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}",
|
|
235
239
|
".evo-hist-search input:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}",
|
|
236
|
-
".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}",
|
|
237
241
|
".evo-hist-empty{padding:8px 12px;color:var(--dsw-alias-label-tertiary)}",
|
|
238
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}",
|
|
239
243
|
".evo-hist-skill:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
@@ -242,27 +246,29 @@ window.__ModuleLoader__.load({
|
|
|
242
246
|
".evo-hist-skill-line{display:flex;align-items:baseline;gap:6px}",
|
|
243
247
|
".evo-hist-skill-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
244
248
|
".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}",
|
|
246
|
-
".evo-hist-main{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;padding:12px
|
|
249
|
+
".evo-hist-skill-desc{display:block;margin-top:2px;color:var(--dsw-alias-label-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
250
|
+
".evo-hist-main{flex:1 1 auto;min-width:0;min-height:0;overflow-y:auto;padding:12px 12px}",
|
|
247
251
|
".evo-hist-head{display:flex;align-items:center;gap:10px;margin:0 0 4px}",
|
|
248
|
-
".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}",
|
|
249
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)}",
|
|
250
255
|
".evo-hist-note{margin:0 0 10px;color:var(--dsw-alias-label-secondary);line-height:1.5;white-space:pre-wrap}",
|
|
251
256
|
".evo-hist-note[data-error=\"true\"]{color:var(--dsw-alias-state-error-primary)}",
|
|
252
|
-
".evo-hist-group{margin:10px 0 2px;font-size:
|
|
253
|
-
".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}",
|
|
254
260
|
".evo-hist-row-body{flex:1 1 auto;min-width:0}",
|
|
255
261
|
".evo-hist-row-title{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}",
|
|
256
262
|
".evo-hist-row-meta{color:var(--dsw-alias-label-tertiary)}",
|
|
257
263
|
".evo-hist-row-summary{margin-top:2px;color:var(--dsw-alias-label-secondary);line-height:1.5}",
|
|
258
264
|
".evo-hist-actions{display:flex;flex:none;align-items:center;gap:6px}",
|
|
259
|
-
".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)}",
|
|
260
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}",
|
|
261
267
|
".evo-hist-button:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
262
268
|
".evo-hist-button:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}",
|
|
263
269
|
".evo-hist-button[data-tone=\"primary\"]{border-color:var(--dsw-alias-brand-primary);color:var(--dsw-alias-brand-primary)}",
|
|
264
270
|
".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-
|
|
271
|
+
".evo-hist-pre-add{color:var(--dsw-alias-state-success-primary)}",
|
|
266
272
|
".evo-hist-pre-del{color:var(--dsw-alias-state-error-primary)}"
|
|
267
273
|
].join("\n");
|
|
268
274
|
//#endregion
|
|
@@ -301,6 +307,17 @@ window.__ModuleLoader__.load({
|
|
|
301
307
|
if (skill.protectedBy !== null) return "state.protected";
|
|
302
308
|
return skill.managed ? "state.managed" : "state.foreign";
|
|
303
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
|
+
}
|
|
304
321
|
/** The relative-time sentence for one row's bucket. */
|
|
305
322
|
function ageText(face, age) {
|
|
306
323
|
if (age.unit === "now") return face.t("time.now");
|
|
@@ -333,17 +350,21 @@ window.__ModuleLoader__.load({
|
|
|
333
350
|
return (0, react.createElement)("pre", { className: "evo-hist-pre" }, rows.length === 0 ? "±" : rows);
|
|
334
351
|
}
|
|
335
352
|
/** One version row: what happened, when, how big, and what it is against. */
|
|
336
|
-
function versionRow(face, row, confirming, openDiff, diff, restore, toggleDiff) {
|
|
353
|
+
function versionRow(face, row, confirming, openDiff, diff, restore, cancelRestore, toggleDiff) {
|
|
337
354
|
const parts = [
|
|
338
355
|
(0, react.createElement)("span", {
|
|
339
356
|
key: "v",
|
|
340
357
|
className: "evo-hist-row-meta"
|
|
341
358
|
}, "v" + String(row.v)),
|
|
342
|
-
(0, react.createElement)("span", {
|
|
359
|
+
(0, react.createElement)("span", {
|
|
360
|
+
key: "action",
|
|
361
|
+
title: actionNote(face, row)
|
|
362
|
+
}, face.t("action." + row.actionKind)),
|
|
343
363
|
(0, react.createElement)("span", {
|
|
344
364
|
key: "time",
|
|
345
|
-
className: "evo-hist-row-meta"
|
|
346
|
-
|
|
365
|
+
className: "evo-hist-row-meta",
|
|
366
|
+
title: localTime(row.at)
|
|
367
|
+
}, ageText(face, row.age))
|
|
347
368
|
];
|
|
348
369
|
const delta = deltaText(face, row);
|
|
349
370
|
if (delta !== null) parts.push((0, react.createElement)("span", {
|
|
@@ -355,15 +376,25 @@ window.__ModuleLoader__.load({
|
|
|
355
376
|
key: "current",
|
|
356
377
|
className: "evo-hist-capsule"
|
|
357
378
|
}, face.t("current")));
|
|
358
|
-
if (row.undoable === true)
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
379
|
+
if (row.undoable === true) {
|
|
380
|
+
if (confirming === row.v) actions.push((0, react.createElement)("button", {
|
|
381
|
+
key: "cancel",
|
|
382
|
+
type: "button",
|
|
383
|
+
className: "evo-hist-button",
|
|
384
|
+
onClick: () => {
|
|
385
|
+
cancelRestore();
|
|
386
|
+
}
|
|
387
|
+
}, face.t("undo.no")));
|
|
388
|
+
actions.push((0, react.createElement)("button", {
|
|
389
|
+
key: "undo",
|
|
390
|
+
type: "button",
|
|
391
|
+
className: "evo-hist-button",
|
|
392
|
+
"data-tone": confirming === row.v ? "primary" : void 0,
|
|
393
|
+
onClick: () => {
|
|
394
|
+
restore(row.v);
|
|
395
|
+
}
|
|
396
|
+
}, confirming === row.v ? face.t("undo.confirm") : face.t("undo")));
|
|
397
|
+
}
|
|
367
398
|
if (row.undoable === true || row.charsDelta !== void 0) actions.push((0, react.createElement)("button", {
|
|
368
399
|
key: "diff",
|
|
369
400
|
type: "button",
|
|
@@ -389,7 +420,7 @@ window.__ModuleLoader__.load({
|
|
|
389
420
|
/** One group: a heading, an optional note, and its rows. */
|
|
390
421
|
function group(face, title, entries, noteText, state) {
|
|
391
422
|
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)));
|
|
423
|
+
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
424
|
}
|
|
394
425
|
/**
|
|
395
426
|
* The panel body.
|
|
@@ -499,11 +530,16 @@ window.__ModuleLoader__.load({
|
|
|
499
530
|
const needle = query.trim().toLowerCase();
|
|
500
531
|
const visible = (0, react.useMemo)(() => needle === "" ? skills : skills.filter((skill) => skill.name.toLowerCase().includes(needle) || skill.description.toLowerCase().includes(needle)), [skills, needle]);
|
|
501
532
|
const loaded = payload !== void 0;
|
|
533
|
+
/** Drop the pending confirmation: the button that opened it must not be the only way out. */
|
|
534
|
+
const cancelRestore = () => {
|
|
535
|
+
setPending(void 0);
|
|
536
|
+
};
|
|
502
537
|
const rowState = {
|
|
503
538
|
confirming: pending,
|
|
504
539
|
openDiff,
|
|
505
540
|
diffs,
|
|
506
541
|
restore,
|
|
542
|
+
cancelRestore,
|
|
507
543
|
toggleDiff
|
|
508
544
|
};
|
|
509
545
|
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 +562,7 @@ window.__ModuleLoader__.load({
|
|
|
526
562
|
type: "button",
|
|
527
563
|
className: "evo-hist-button",
|
|
528
564
|
onClick: refresh
|
|
529
|
-
}, face.t("refresh"))), (0, react.createElement)("p", { className: "evo-hist-hint" }, face.t("hint")
|
|
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", {
|
|
530
566
|
className: "evo-hist-note",
|
|
531
567
|
"data-error": noteError ? "true" : void 0
|
|
532
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));
|
|
@@ -551,24 +587,44 @@ window.__ModuleLoader__.load({
|
|
|
551
587
|
/** Row order inside the global panel list (beside the platform's own rows). */
|
|
552
588
|
const PANEL_ORDER = 35;
|
|
553
589
|
/**
|
|
554
|
-
* 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.
|
|
555
592
|
*
|
|
556
|
-
*
|
|
557
|
-
* `renderSlot('sidebar.panellist', { size: wide ? 16 : 18, active }, …)`)
|
|
558
|
-
* that
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
*
|
|
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.
|
|
597
|
+
*
|
|
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.
|
|
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.
|
|
564
615
|
*/
|
|
616
|
+
function round3(value) {
|
|
617
|
+
return Math.round(value * 1e3) / 1e3;
|
|
618
|
+
}
|
|
565
619
|
function PanelIcon(props = {}) {
|
|
566
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);
|
|
567
623
|
return (0, react.createElement)("svg", {
|
|
568
624
|
"aria-hidden": "true",
|
|
569
|
-
width: String(
|
|
570
|
-
height: String(
|
|
571
|
-
viewBox: "
|
|
625
|
+
width: String(GLYPH_BOX),
|
|
626
|
+
height: String(GLYPH_BOX),
|
|
627
|
+
viewBox: String(-inset) + " " + String(-inset) + " " + String(units) + " " + String(units),
|
|
572
628
|
fill: "none",
|
|
573
629
|
stroke: "currentColor",
|
|
574
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": {
|