@linxin666/dsh-client-ui-skin-center 0.3.4 → 0.3.5

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # node scripts/verify-docs.mjs --write <dir>
5
- README.md: 04b1c524d8222926a64008329b4fb9454ab2f0c0
6
- README.zh.md: 56cd3679ad66705e5543409086911be3bf723fbd
5
+ README.md: 6c66e64a68379e434045dd573fae29d2fbdf75f3
6
+ README.zh.md: 278218950a5310342387b1a3c19c4b91013d43d3
package/README.md CHANGED
@@ -22,7 +22,7 @@ dsh plugin --profile web add @linxin666/dsh-client-ui-skin-center
22
22
  # From the repo (dev): dsh plugin --profile web add link:$(pwd)/packages/skins/skin-center
23
23
  ```
24
24
 
25
- `$(pwd)` is your clone of the dsh-web monorepo. Only the default skin (blue-fantasy) ships inside this package; every other skin installs on demand from dsh-market.com into `$DSH_HOME/skins/<id>/`, and community skins are plain directories in the same place (no install command, no restart — reopen the card or reload to pick them up). A fresh install activates blue-fantasy by default (host seed); an update whose active skin is no longer available falls back to the stock look. The package ships `skins/blue-fantasy` only; the remaining skins stay in the repository under `skins/` as the market-build and gallery source and are never published in the package.
25
+ `$(pwd)` is your clone of the dsh-web monorepo. Only the default skin (blue-fantasy) ships inside this package; every other skin installs on demand from dsh-market.com into `$DSH_HOME/skins/<id>/`, and community skins are plain directories in the same place (no install command, no restart — reopen the card or reload to pick them up). A fresh install activates blue-fantasy by default (host seed); an update whose active skin is no longer available falls back to the stock look. The package ships `skins/blue-fantasy` only; the remaining skins stay in the repository under `skins/` as the market-build source and are never published in the package.
26
26
 
27
27
  skin-center is a self-contained bundle meeting the official DSH plugin standard (`dsh.bundle.patch` points to `cordis.patch.yml`); it can also be installed via git: `dsh plugin --profile web add github:<org>/dsh-web#<sha>` (the `prepare` script builds `lib/` in place). pnpm ≥10 requires authorizing `allowBuilds` before installing a git dependency; a local `link:` install has no such requirement.
28
28
 
@@ -40,7 +40,7 @@ skin-center is a self-contained bundle meeting the official DSH plugin standard
40
40
  - All `/api/skin-center/*` routes are same-origin only: writes reject cross-site requests (Sec-Fetch-Site / Origin fence), and asset reads are contained inside each skin directory (path escapes fail closed).
41
41
  - Skin CSS is sanitized (whitelist) before serving; `patches.css` (L3) is arbitrary CSS by design and disclosed as such — it runs with full page styling power and is not a security boundary.
42
42
  - The custom-theme editor emits only fixed declarations from `CUSTOM_THEME_ALLOWED_TOKENS`, each verified against the official token registry. User input is normalized color/contrast data and never becomes a selector, URL or free-form CSS payload.
43
- - `hooks.mjs` is trusted code that shares this repository's review and release; it is served same-origin only and its import/apply errors can never take the static skin down. Hooks run for built-in skins, and for user-directory skins installed from the official DSH Market whose `dsh-market.provenance.json` sha256-pins the on-disk `skin.json` and hooks entry to the market-served bytes (verified by `src/provenance.ts`, issue #1073); a missing or mismatched provenance hand-dropped or tampered directories keeps the hooks facet refused while the declarative parts still load.
43
+ - `hooks.mjs` is trusted code that shares this repository's review and release; it is served same-origin only and its import/apply errors can never take the static skin down. Hooks run for built-in skins and for user-directory skins whose executable identity is byte-verified as reviewed official-market content: current Workshop installs use `dsh-market.provenance.json`, while legacy pre-provenance installs must match the generated `src/reviewed-hooks.generated.ts` identity for id, declared entry, complete `skin.json`, and hooks bytes (verified by `src/provenance.ts`, issue #1073). The fallback is read-only and offline; any modified, renamed, hand-dropped, or tampered directory keeps the hooks facet refused while the declarative parts still load.
44
44
 
45
45
  ## Known limitations
46
46
 
package/README.zh.md CHANGED
@@ -40,7 +40,7 @@ dsh plugin --profile web add @linxin666/dsh-client-ui-skin-center
40
40
  - 所有 `/api/skin-center/*` 路由仅接受同源请求:写操作拒绝跨站请求(Sec-Fetch-Site / Origin 围栏),资产读取限定在各皮肤目录之内(路径逃逸 fail-closed)。
41
41
  - 皮肤 CSS 在服务前经白名单净化;`patches.css`(L3)按设计就是任意 CSS 并如实公示——它拥有完整页面样式能力,不构成安全边界。
42
42
  - 自定义主题编辑器只会从 `CUSTOM_THEME_ALLOWED_TOKENS` 生成固定声明,且每个 token 都对照官方 token 注册表校验。用户输入只作为规范化后的颜色/对比度数据,不会成为选择器、URL 或自由 CSS 载荷。
43
- - `hooks.mjs` 是与本仓库同审同发的受信代码,仅同源 serve,其 import/apply 错误永远不会拖垮静态皮肤。hooks 对内置皮肤放行;对用户目录皮肤,仅当其从官方 DSH 市场安装且 `dsh-market.provenance.json` sha256 钉住磁盘上的 `skin.json` 与 hooks 入口字节时放行(由 `src/provenance.ts` 校验,issue #1073);provenance 缺失或不匹配——手工投放或被篡改的目录——hooks facet 保持拒绝,声明式部分仍正常加载。
43
+ - `hooks.mjs` 是与本仓库同审同发的受信代码,仅同源 serve,其 import/apply 错误永远不会拖垮静态皮肤。hooks 对内置皮肤及可按字节验证为官方市场已审查内容的用户目录皮肤放行:当前 Workshop 安装使用 `dsh-market.provenance.json`,而早于 provenance 的历史安装必须同时匹配生成的 `src/reviewed-hooks.generated.ts` 身份中的 id、声明入口、完整 `skin.json` 与 hooks 字节(由 `src/provenance.ts` 校验,issue #1073)。回退只读且离线;任何被修改、改名、手工投放或篡改的目录都会继续拒绝 hooks facet,声明式部分仍正常加载。
44
44
 
45
45
  ## 已知限制
46
46
 
@@ -44,16 +44,9 @@ and release. It is served verbatim over `GET /skins/<id>/hooks.mjs` only for:
44
44
 
45
45
  - **built-in skins** — shipped inside the skin-center npm package, same
46
46
  review and release by definition;
47
- - **official-market installs** — user-directory skins carrying a
48
- `dsh-market.provenance.json` whose sha256 pins the on-disk `skin.json`
49
- and hooks entry to the bytes the official DSH Market served (the market
50
- content is built from this repository, so a hash match means the bytes
51
- are the reviewed ones). Verification lives in `src/provenance.ts` and
52
- fails closed: any missing, foreign-source or mismatched provenance keeps
53
- the hooks facet refused with a catalog warning while the declarative
54
- parts (skin.css / patches.css / assets) still load (issue #1073).
55
-
56
- Locally dropped or third-party skin directories never run hooks.
47
+ - **official-market installs** — user-directory skins whose executable identity is byte-verified as reviewed content. Current Workshop installs carry a `dsh-market.provenance.json` whose sha256 pins the on-disk `skin.json` and hooks entry to the bytes the official DSH Market served. Historical installs created before provenance existed may recover only when id, declared entry, complete `skin.json`, and hooks bytes match one identity in generated `src/reviewed-hooks.generated.ts`. The registry is generated from this repository's market skin sources by `scripts/skin-hooks-registry.mjs`; `--check` is part of `skin-center:check`, so any reviewed manifest or hook change must regenerate it. Verification lives in `src/provenance.ts`, is read-only and offline, and fails closed: a foreign source, unknown identity, renamed skin, or an executable-identity mismatch keeps the hooks facet refused with a catalog warning while the declarative parts (skin.css / patches.css / assets) still load (issue #1073).
48
+
49
+ Locally dropped or third-party skin directories never run hooks unless their executable identity is byte-identical to a reviewed official-market skin. Declarative files outside that identity may be customized without granting additional executable capability.
57
50
 
58
51
  ## Loader-side rules (pinned here, enforced in M2)
59
52
 
@@ -0,0 +1,107 @@
1
+ # Skin Runtime Performance Guidelines (v1)
2
+
3
+ Contract owner: skin-center (single owner, conflict resolution does not rely
4
+ on load order). Version: `performance-guidelines/v1` (2026-02, authored after
5
+ the orca-link session-switch latency investigation).
6
+
7
+ These are the runtime-performance rules for skin **hooks** (`hooks.mjs`,
8
+ `facets.client`) and their stylesheets. They exist because skins share the
9
+ host page with the DSH shell and every plugin: a skin that watches the whole
10
+ document or keeps GPU layers alive degrades everyone. The rules are advice
11
+ with teeth — a code review hook check and a human review of the case study
12
+ ([orca-link hooks](../../../packages/skins/skin-center/skins/orca-link/hooks.mjs),
13
+ [patches](../../../packages/skins/skin-center/skins/orca-link/patches.css))
14
+ should reject their violation.
15
+
16
+ ## R1 — DOM observation must be scoped and short-circuited
17
+
18
+ Never broadcast a full-tree scan from a `MutationObserver` callback:
19
+
20
+ - Watch the narrowest possible scope. Observing `document.body` with
21
+ `subtree: true` is only acceptable when every callback answers "did my
22
+ anchor actually change?" in O(1) before doing any work.
23
+ - Cache the anchor. A conversation/session root is rebuilt only on session
24
+ switches; between switches the phase lives on the same node, so a
25
+ `let memo = null; memo = memo?.isConnected ? memo : locate()` cache is
26
+ exact (a rebuilt root is a different node, `isConnected` flips on the old
27
+ one, and the next callback re-locates).
28
+ - Skip fully mounted state. For per-seat work, record the mount phase or the
29
+ exact element references on the binding and return early when nothing
30
+ changed; only re-run the "may need rebuild" path when an element or phase
31
+ actually changed.
32
+ - High-churn regions (terminals, streaming input backdrops) should be
33
+ excluded from work with a fast `closest()` check before any scanning.
34
+
35
+ ## R2 — Host-glyph fingerprint matching must be one regex pre-scan
36
+
37
+ When a skin redraws host SVG glyphs by matching path fingerprints:
38
+
39
+ - Compile the fingerprint table once into a single alternation regex and use
40
+ `regex.test(html)` as the boolean gate. Per-svg `includes()` loops over
41
+ N fingerprints turn a session-loading DOM insert into O(svgs × N × len)
42
+ string work; one regex test makes it O(len).
43
+ - Keep the ordered key table for the *decision*: "first key in the table that
44
+ appears" is the precedence contract, and the pre-scan regex only answers
45
+ the boolean. Do not reorder or deduplicate keys to let the regex decide.
46
+ - Never clone a fresh svg. A svg that has no skin art yet carries only host
47
+ markup — read `svg.innerHTML` directly. The clone/remove-art dance is only
48
+ needed on an already-reconciled svg.
49
+ - Preserve idempotency: re-reconciling an already-art'd svg must not stack a
50
+ second art group (guard on the art attribute / recorded name).
51
+
52
+ ## R3 — Frame loops and infinite animations must pause when hidden
53
+
54
+ - A `setTimeout` frame chain (character sprite atlases, per-frame CSS-variable
55
+ writes) must subscribe to `visibilitychange`: hidden = clear the pending
56
+ timeout; visible = reschedule from the current sequence index (never reset
57
+ the animation state).
58
+ - Mirror the hidden state onto an attribute the stylesheet can read
59
+ (e.g. `body[data-*tab-hidden]`) and set `animation-play-state: paused` for
60
+ the skin's own infinite `@keyframes` loops. The stylesheet pause is what
61
+ actually stops the compositor work.
62
+ - Do not write CSS custom properties that nothing consumes. Each
63
+ `style.setProperty` is a style-recalc surface; a consumed-by-nobody
64
+ variable written 60+ times per second is pure waste.
65
+ - Animate with composited properties (`transform`, `opacity`). Animating
66
+ `background-position` repaints per frame: it rasterizes instead of
67
+ compositing and shows up in long-task traces.
68
+
69
+ ## R4 — `will-change` must not be parked on full-screen or idle layers
70
+
71
+ - `will-change: opacity, transform, filter` keeps a GPU compositing layer
72
+ alive **forever**, even when nothing is animating. A static 0px filter
73
+ (`blur()`) is a no-op; the layer cost is not.
74
+ - Only keep `will-change` on elements in a *sustained* animation (e.g. an
75
+ infinite loop already covered by R3); never on full-viewport fixed
76
+ backgrounds that merely crossfade — a .64s transition promotes the layer
77
+ automatically for its duration and releases it afterwards.
78
+ - Prefer visibility/display switches (with the opposite theme hidden) over
79
+ layering both scene sets, and keep the visible scene count at one.
80
+ - `backdrop-filter` is realtime and expensive: keep its covered area to the
81
+ smallest surface that needs the effect, avoid animating it, and test with
82
+ scrolling under the blurred surface.
83
+
84
+ ## R5 — Never interleave layout reads and writes in mutation callbacks
85
+
86
+ - `getBoundingClientRect()` / `offset*` reads force layout. In a mutation
87
+ storm (large DOM inserts), a read-write-read-write pattern in one callback
88
+ is layout thrashing: batch all reads first, then all writes.
89
+ - Cache measurements on the binding; recompute only when the referenced
90
+ element changes. A restore-button re-anchor on an unchanged seat is the
91
+ classic offender — skip it via the R1 short-circuit.
92
+
93
+ ## R6 — Delegation of cleanup (existing contract, restated)
94
+
95
+ Every observer, listener (`visibilitychange`, `pointermove`, `click`,
96
+ `focusin`...), timer and interval created in `apply()` must be registered
97
+ through `ctx.onCleanup` and torn down in reverse order, including new
98
+ global-body attributes. A skin that leaks observers doubles the cost of every
99
+ future session switch.
100
+
101
+ ## Verification
102
+
103
+ Measure before and after with the DevTools Performance panel during session
104
+ load and session switch (the heaviest DOM operations a skin hooks into).
105
+ Long tasks with an aggregate > 100ms, or frames on the main busied line
106
+ during those operations, indicate a violation of the rules above. Per-skin
107
+ tracking numbers serve as regression baselines; include them in the PR body.
@@ -78,6 +78,7 @@ family / 插件区域:
78
78
  | `dialog` | git-graph | 图对话框;`[data-gitgraph-dialog]` |
79
79
  | `graph-row` | git-graph | 提交行;dialog 内行容器 |
80
80
  | `ref` | git-graph | 分支徽标;`[data-gitgraph-ref]` |
81
+ | `worktree-create` / `worktree-manage` | git-graph | worktree 创建/管理入口按钮;分支弹层 footer 内裸值按钮 |
81
82
  | `sprite` | pet | 精灵;`[data-dsh-pet-root]` 子树 float 容器 |
82
83
  | `bubble` | pet | 气泡容器 |
83
84
  | `panel` | pet | 交互面板;`[data-placement]` |
@@ -120,7 +121,7 @@ family / 插件区域:
120
121
  | `remote-web-ui` | dsh-remote-web-ui | slot entry id `remote-web-ui` |
121
122
  | `web-ui-settings` | dsh-web-settings | settings.section id `web-ui-plugins` |
122
123
  | `skill-explorer` | dsh-skill-explorer | `[data-dsh-skill-explorer-view]` / `[data-dsh-skill-explorer-entry]` |
123
- | `doctor` | dsh-doctor | web-ui.plugin.item 槽 entry id `doctor`(设置 → Web UI 插件 → Doctor 卡片);卡片内 `[data-dsh-plugin="doctor"]` |
124
+ | `doctor` | dsh-doctor | web-ui.plugin.item 槽 entry id `doctor`(设置 → Web 插件 → Doctor 卡片);卡片内 `[data-dsh-plugin="doctor"]` |
124
125
  | `aionui-panel` | dsh-aionui-panel(停更) | dock entry id `aionui-*` |
125
126
  | `dsh-web-ui-market` | dsh-market | 创意工坊商店一级页(settings.section id `dsh-web-ui-market`),商店卡与目录条目容器 |
126
127
  | `skin-center` | skins/skin-center | 一级设置分区 settings.section id `skin-center`(列已安装皮肤,属内置源时显式标记) |
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "attribution": {
82
82
  "type": "string",
83
- "description": "Optional attribution line shown in the gallery."
83
+ "description": "Optional attribution line shown in the shop."
84
84
  },
85
85
  "preview": {
86
86
  "type": "object",
@@ -144,7 +144,7 @@
144
144
  },
145
145
  "patches": {
146
146
  "$ref": "#/$defs/relPath",
147
- "description": "Optional L3 free-selector patches. High sensitivity; disclosed in UI and gallery."
147
+ "description": "Optional L3 free-selector patches. High sensitivity; disclosed in the Skin Center UI and the shop."
148
148
  },
149
149
  "backgroundMedia": {
150
150
  "type": "object",