@linxin666/dsh-pet 0.4.2 → 0.4.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.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: 7b66d973fd5a0a6c072fb2f52160287befec81f5
6
- README.zh.md: 092feaa5923944f3f24942fa54ce91907e81c13f
5
+ README.md: 53dfffd48c986d38f1fc92e03da5eb8bcc9c4ef4
6
+ README.zh.md: 5123ac69f7d50cbaf4e198e18285a58b1c4791b7
package/README.md CHANGED
@@ -86,7 +86,7 @@ node scripts/dsh-pet install <dir> # validate, then copy into $DSH_HO
86
86
  node scripts/dsh-pet install <dir> --force # overwrite an existing same-id install
87
87
  ```
88
88
 
89
- Invalid entries never override a working pet: they are skipped with a diagnostic listed in the settings (Pet section). The registry is built once at host startup; add or change a pet, then restart `dsh web`.
89
+ Invalid entries never override a working pet: they are skipped (the settings page no longer lists them; run `node scripts/dsh-pet validate <dir>` to see why a directory was refused). The registry is built once at host startup; add or change a pet, then restart `dsh web`.
90
90
 
91
91
  ## Voice packs and panel chrome (voice.json, pet-center M4, #677)
92
92
 
@@ -122,7 +122,7 @@ Every word in the thought bubble (status / tool / whisper copy) and the hover pa
122
122
  - Merge precedence (per slot): the pet voice.json > the global .voice.json > built-in copy. status/tools/whispers merge per key, panel merges per slot; any slot a layer misses falls through.
123
123
  - Placeholder whitelist: tools accept {tool} / {hint}; toolRemaining accepts {n}; panel.stats accept {rank} / {n} / {points}; status, whisper and panel-label lines accept no placeholders (lines carrying one are dropped with a warning).
124
124
  - Caps (warn-and-drop): at most 64 lines per pool and 160 characters per line; panel labels 40 and stats 80 characters.
125
- - A broken pack never breaks the pet: voice.json that is not valid JSON or whose root is not an object is ignored with a warning; every other issue drops its slot only. Diagnostics appear under Settings > Pet directory diagnostics. node scripts/dsh-pet validate <dir> fails installs on structure errors and lists content issues as warnings.
125
+ - A broken pack never breaks the pet: voice.json that is not valid JSON or whose root is not an object is ignored with a warning; every other issue drops its slot only. node scripts/dsh-pet validate <dir> fails installs on structure errors and lists content issues as warnings.
126
126
  - Semantics: an empty status/tools pool falls back to the built-in copy (a scene line always renders); an explicit empty whisper pool mutes that channel; an empty panel actions array hides all three buttons; uncovered buttons and stats keep the plugin bilingual dictionary. Legacy whispers.generic / whispers.rules fields are no longer supported and are ignored with a warning.
127
127
 
128
128
  ## Live2D pets (renderer: live2d)
@@ -208,7 +208,7 @@ Host-side sibling plugins can push one structured announcement through the `pet`
208
208
  |---|---|---|
209
209
  | `blue-throated-bee-eater` | 蓝喉蜂虎 | Companion art contributed by the repository contributor under Apache-2.0 (12 AI-illustrated pose references in the palette of the blue-throated-bee-eater skin; composed by docs/archive/blue-throated-bee-eater-pet/gen-pet.py with per-track standalone poses: perched, flight, front hover, waving, landing, droop, tilt, review; ship 小蜜蜂 as the treat name) |
210
210
  | `doro` | doro | Frames2d gameplay pet (contributed by stushansusu under MIT): 11 tracks / 802 frames of 42 ms webp sequences — breathing idle, a work trio, sleep, a wash mode (mood +3/s), a four-direction crawl used by random roaming, three idle-director acts (cola / orange / tongue) and a drag-only struggle loop. Doro is an unofficial, fan-made derivative of Dorothy from *Goddess of Victory: Nikke*; the character and all related rights belong to SHIFT UP — personal non-commercial use only, unofficial and not affiliated with SHIFT UP (see THIRD_PARTY_NOTICES.md) |
211
- | `jyn` | 女仆鲸鱼娘 | Frames2d gameplay pet (contributed by stushansusu under MIT): maid-whale desktop pet with work/sleep/touch gameplay and three selectable skins (暗夜鎏金 / 蓝海霓裳 / 冰晶公主), each with a probability-rolled click action; 暗夜鎏金 also swaps its own rest loop into the sleep gameplay | (docs(dsh-pet): list three jyn skins in the registry rows and re-record pairing)
211
+ | `jyn` | 女仆鲸鱼娘 | Frames2d gameplay pet (contributed by stushansusu under MIT): maid-whale desktop pet with work/sleep/touch gameplay and three selectable skins (暗夜鎏金 / 蓝海霓裳 / 冰晶公主), each with a probability-rolled click action; 暗夜鎏金 also swaps its own rest loop into the sleep gameplay |
212
212
  | `ouo-neko` | OUO Neko | Pink-sakura cat-eared companion contributed by `Pessimist0906` under MIT |
213
213
  | `whale-girl` | 鲸鱼娘(原版) | The repository's original whale-girl atlas |
214
214
  | `whale-girl-refined` | 鲸鱼娘(精致版) | An AI-assisted derivative with repaired and refined details, based on the whale-girl design direction |
@@ -217,37 +217,25 @@ The Miku pet is deliberately not bundled: it is a frames2d gameplay pet installe
217
217
 
218
218
  The refined variant references DreamSkin's “DeepSeek-Whale” theme. The historical source record identifies `powerdog996` as the original theme author and marks the theme as MIT: [DreamSkin](https://dreamskin.cc), [repository source record](https://github.com/zhu1090093659/dsh-web/commit/87edd7ff4800dffd40bc93fb76e4ae450390facd). This attribution records the source and derivative relationship; it does not present the refined variant as an official work of the original author or redefine the original artwork's licensing scope.
219
219
 
220
- ## Animation preview
220
+ ## Most popular pets
221
221
 
222
- Sprite pets use 8-column atlases with 192×208 cells generated by the [hatch-pet](https://github.com/dsh2026) pipeline. Classic atlases carry 9 animation rows; v2 atlases add 2 rows for 16 look directions. Below are previews of the standard animation states:
222
+ The three most-liked pets in the Workshop's pet category on [dsh-market.com](https://dsh-market.com), in the site's default popularity order. Each is one `pet.json` plus its assets, rendered by the contracts above — no per-pet code:
223
223
 
224
- | idle | waiting | running | jumping |
224
+ | Pet | Renderer | Author | Source |
225
225
  |---|---|---|---|
226
- | ![idle](assets/whale/previews/idle.gif) | ![waiting](assets/whale/previews/waiting.gif) | ![running](assets/whale/previews/running.gif) | ![jumping](assets/whale/previews/jumping.gif) |
226
+ | [鲸鱼娘(精致版)](https://dsh-market.com/#pet:whale-girl-refined) (`whale-girl-refined`) | sprite2d | dsh-web | Built-in atlas `assets/whale-refined/` |
227
+ | [Miku](https://dsh-market.com/#pet:miku) (`miku`) | frames2d | stushansusu (MIT) | Workshop-only, installs into `$DSH_HOME/pets/miku/`; Hatsune Miku character rights belong to Crypton Future Media under the Piapro Character License |
228
+ | [女仆鲸鱼娘](https://dsh-market.com/#pet:jyn) (`jyn`) | frames2d | stushansusu (MIT) | Built-in `assets/jyn/` |
227
229
 
228
- | waving | review | failed | move left/right |
229
- |---|---|---|---|
230
- | ![waving](assets/whale/previews/waving.gif) | ![review](assets/whale/previews/review.gif) | ![failed](assets/whale/previews/failed.gif) | ![running-left](assets/whale/previews/running-left.gif) ![running-right](assets/whale/previews/running-right.gif) |
231
-
232
- The built-in **Blue-throated Bee-eater** pet carries the same preview set:
233
-
234
- | idle | waiting | running | jumping |
235
- |---|---|---|---|
236
- | ![idle](assets/blue-throated-bee-eater/previews/idle.gif) | ![waiting](assets/blue-throated-bee-eater/previews/waiting.gif) | ![running](assets/blue-throated-bee-eater/previews/running.gif) | ![jumping](assets/blue-throated-bee-eater/previews/jumping.gif) |
237
-
238
- | waving | review | failed | move left/right |
239
- |---|---|---|---|
240
- | ![waving](assets/blue-throated-bee-eater/previews/waving.gif) | ![review](assets/blue-throated-bee-eater/previews/review.gif) | ![failed](assets/blue-throated-bee-eater/previews/failed.gif) | ![running-left](assets/blue-throated-bee-eater/previews/running-left.gif) ![running-right](assets/blue-throated-bee-eater/previews/running-right.gif) |
241
-
242
- The community **Starry Doll** pet (Workshop-only, contributed by Theater-ahyeon under CC-BY-NC-SA-4.0) ships the same preview set:
230
+ | 鲸鱼娘(精致版) | Miku | 女仆鲸鱼娘 |
231
+ |---|---|---|
232
+ | ![whale-girl-refined](assets/whale-refined/previews/idle.gif) | ![Miku](assets/miku/previews/idle.webp) | ![jyn](assets/jyn/previews/idle.webp) |
243
233
 
244
- | idle | waiting | running | jumping |
245
- |---|---|---|---|
246
- | ![idle](assets/starry-doll/previews/idle.gif) | ![waiting](assets/starry-doll/previews/waiting.gif) | ![running](assets/starry-doll/previews/running.gif) | ![jumping](assets/starry-doll/previews/jumping.gif) |
234
+ Sprite pets use 8-column atlases with 192×208 cells generated by the [hatch-pet](https://github.com/dsh2026) pipeline. Classic atlases carry 9 animation rows; v2 atlases add 2 rows for 16 look directions. 女仆鲸鱼娘 ships directory-style frame sequences instead, so its other gameplay states are separate preview files:
247
235
 
248
- | waving | review | failed | move left/right |
236
+ | shy | work | work success | sleep |
249
237
  |---|---|---|---|
250
- | ![waving](assets/starry-doll/previews/waving.gif) | ![review](assets/starry-doll/previews/review.gif) | ![failed](assets/starry-doll/previews/failed.gif) | ![running-left](assets/starry-doll/previews/running-left.gif) ![running-right](assets/starry-doll/previews/running-right.gif) |
238
+ | ![jyn shy](assets/jyn/previews/shy.webp) | ![jyn work](assets/jyn/previews/work.webp) | ![jyn work-success](assets/jyn/previews/work-success.webp) | ![jyn sleep](assets/jyn/previews/sleep.webp) |
251
239
 
252
240
  ## Architecture
253
241
 
@@ -340,7 +328,7 @@ OUO Neko uses the extended v2 contract: a 1536×2288 atlas with the same 9 anima
340
328
  - Asset serving resolves both the pet directory and the candidate file through `realpath`; symlink escapes are refused (403). Files are size-capped before being read into memory (manifest 64 KB, imagery 20 MB; over-cap answers 413).
341
329
  - Live2D models are served by closure: only the manifest, the declared primary assets, and the files the `.model3.json` references (each screened against traversal, absolute and URL forms).
342
330
  - The plugin never downloads executables and never bundles the Live2D Cubism Core.
343
- - Manifests are fail-closed on structure: unknown fields or renderers reject the entry with a diagnostic shown in settings.
331
+ - Manifests are fail-closed on structure: unknown fields or renderers reject the entry instead of loading a half-broken pet (run `node scripts/dsh-pet validate <dir>` to read the rejection reason).
344
332
 
345
333
  ## Telemetry
346
334
 
package/README.zh.md CHANGED
@@ -86,7 +86,7 @@ node scripts/dsh-pet install <dir> # 校验通过后拷入 $DSH_HOME/
86
86
  node scripts/dsh-pet install <dir> --force # 覆盖同名已安装宠物
87
87
  ```
88
88
 
89
- 非法条目永不覆盖可用宠物:它们被跳过并在设置(宠物栏目)给出诊断。注册表在宿主启动时构建一次;新增或修改宠物后重启 `dsh web` 生效。
89
+ 非法条目永不覆盖可用宠物:它们被跳过(设置页不再列出它们;用 `node scripts/dsh-pet validate <dir>` 查看目录被拒的原因)。注册表在宿主启动时构建一次;新增或修改宠物后重启 `dsh web` 生效。
90
90
 
91
91
  ## 语音包与面板定制(voice.json,宠物中心 M4,#677)
92
92
 
@@ -122,7 +122,7 @@ node scripts/dsh-pet install <dir> --force # 覆盖同名已安装宠物
122
122
  - 合并优先级(逐槽):宠物自带 voice.json > 全局 .voice.json > 内置文案。status/tools/whispers 逐键合并、panel 逐槽合并,任何层缺失的槽位回落下一层。
123
123
  - 占位符白名单:tools 允许 {tool} / {hint};toolRemaining 允许 {n};panel.stats 允许 {rank} / {n} / {points};status、碎碎念与面板标签不允许任何占位符(含非法占位符的行被警告丢弃)。
124
124
  - 上限(warn-and-drop):每池 64 行以内、每行 160 字符以内;面板标签 40 以内、统计 80 字符以内。
125
- - 坏包不影响宠物:voice.json 不是合法 JSON 或根不是对象 → 警告并整体忽略;其余问题逐槽警告丢弃。诊断显示在设置 → 宠物目录诊断。node scripts/dsh-pet validate <dir> 会把结构错误判为安装失败、内容问题列为警告。
125
+ - 坏包不影响宠物:voice.json 不是合法 JSON 或根不是对象 → 警告并整体忽略;其余问题逐槽警告丢弃。node scripts/dsh-pet validate <dir> 会把结构错误判为安装失败、内容问题列为警告。
126
126
  - 语义细节:status/tools 的空池回落内置文案(场景行始终有话说);whispers 的显式空数组是静音(关掉该通道);面板 actions 为空数组 = 三个按钮全部隐藏;未覆盖的按钮/统计继续使用插件双语字典。旧的 whispers.generic / whispers.rules 字段已不再支持,会被忽略并给出警告。
127
127
 
128
128
  ## Live2D 宠物(renderer: live2d)
@@ -208,7 +208,7 @@ frames2d 宠物可声明 `gameplay` 块——从 miku 桌宠泛化而来的可
208
208
  |---|---|---|
209
209
  | `blue-throated-bee-eater` | 蓝喉蜂虎 | 贡献者以 Apache-2.0 贡献的伙伴插画(12 张同角色 AI 立绘素材,配色取蓝喉蜂虎皮肤同源;由 docs/archive/blue-throated-bee-eater-pet/gen-pet.py 组装,各轨道独立姿态:栖枝/飞行/正面悬停/挥翅/降落/垂头/歪头/昂首;饲料以「小蜜蜂」命名) |
210
210
  | `doro` | doro | 由 stushansusu 以 MIT 许可证贡献的 frames2d 玩法宠物:11 条轨道 / 802 帧 42ms webp 序列——呼吸待机、打工三件套、睡觉、洗澡模式(心情 +3/秒)、随机漫游用的四向爬行、三个待机小动作(cola / orange / tongue)与仅拖拽期间播放的挣扎循环。Doro 是《胜利女神:妮姬》桃乐丝的非官方同人二创/梗衍生形象,角色及相关权利归 SHIFT UP 所有——仅限个人非商业使用,与官方无隶属或背书关系(见 THIRD_PARTY_NOTICES.md)。 |
211
- | `jyn` | 女仆鲸鱼娘 | frames2d 玩法宠物(stushansusu 以 MIT 贡献):带打工/睡觉/触摸玩法与三款可选皮肤(暗夜鎏金 / 蓝海霓裳 / 冰晶公主)的女仆鲸鱼同桌宠,每款皮肤各带一个概率掷骰点击动作;暗夜鎏金另将专属休息循环换入睡觉玩法 | (docs(dsh-pet): list three jyn skins in the registry rows and re-record pairing)
211
+ | `jyn` | 女仆鲸鱼娘 | frames2d 玩法宠物(stushansusu 以 MIT 贡献):带打工/睡觉/触摸玩法与三款可选皮肤(暗夜鎏金 / 蓝海霓裳 / 冰晶公主)的女仆鲸鱼同桌宠,每款皮肤各带一个概率掷骰点击动作;暗夜鎏金另将专属休息循环换入睡觉玩法 |
212
212
  | `ouo-neko` | OUO Neko | `Pessimist0906` 以 MIT 许可证贡献的粉色樱花猫耳伙伴 |
213
213
  | `whale-girl` | 鲸鱼娘(原版) | 仓库原有的鲸鱼娘图集 |
214
214
  | `whale-girl-refined` | 鲸鱼娘(精致版) | 以鲸鱼娘设计方向为基础,经 AI 辅助二次创作、修复和细节精修的衍生版本 |
@@ -217,37 +217,25 @@ Miku 宠物有意不随包内置:它是 frames2d 玩法宠物,经创意工
217
217
 
218
218
  精致版参考了 DreamSkin 的「DeepSeek-鲸鱼娘」主题。历史来源记录标注原主题作者为 `powerdog996`,并标注主题为 MIT:[DreamSkin](https://dreamskin.cc)、[仓库来源记录](https://github.com/zhu1090093659/dsh-web/commit/87edd7ff4800dffd40bc93fb76e4ae450390facd)。此处用于记录素材来源与衍生关系;精致版不表述为原作者的官方作品,也不重新定义原始美术作品的授权范围。
219
219
 
220
- ## 动画预览
220
+ ## 人气宠物
221
221
 
222
- 精灵宠物使用由 [hatch-pet](https://github.com/dsh2026) 流水线生成的 8 列图集,单元格为 192×208。经典图集包含 9 行动画;v2 图集额外增加 2 行,共提供 16 个观察方向。以下为标准动画状态预览:
222
+ [dsh-market.com](https://dsh-market.com) 创意工坊宠物分类里人气最高的三只宠物,按网站默认的「按人气」排序。每只都是「一个 `pet.json` 加素材」,由上文契约渲染——无需任何按宠物编写的代码:
223
223
 
224
- | idle | waiting | running | jumping |
224
+ | 宠物 | 渲染器 | 作者 | 来源 |
225
225
  |---|---|---|---|
226
- | ![idle](assets/whale/previews/idle.gif) | ![waiting](assets/whale/previews/waiting.gif) | ![running](assets/whale/previews/running.gif) | ![jumping](assets/whale/previews/jumping.gif) |
226
+ | [鲸鱼娘(精致版)](https://dsh-market.com/#pet:whale-girl-refined)(`whale-girl-refined`) | sprite2d | dsh-web | 内置图集 `assets/whale-refined/` |
227
+ | [Miku](https://dsh-market.com/#pet:miku)(`miku`) | frames2d | stushansusu(MIT) | 仅经创意工坊分发,安装到 `$DSH_HOME/pets/miku/`;初音未来角色权利归 Crypton Future Media,适用 Piapro Character License |
228
+ | [女仆鲸鱼娘](https://dsh-market.com/#pet:jyn)(`jyn`) | frames2d | stushansusu(MIT) | 内置 `assets/jyn/` |
227
229
 
228
- | waving | review | failed | 左右移动 |
229
- |---|---|---|---|
230
- | ![waving](assets/whale/previews/waving.gif) | ![review](assets/whale/previews/review.gif) | ![failed](assets/whale/previews/failed.gif) | ![running-left](assets/whale/previews/running-left.gif) ![running-right](assets/whale/previews/running-right.gif) |
231
-
232
- 内置的**蓝喉蜂虎**宠物随附同一套状态预览:
233
-
234
- | idle | waiting | running | jumping |
235
- |---|---|---|---|
236
- | ![idle](assets/blue-throated-bee-eater/previews/idle.gif) | ![waiting](assets/blue-throated-bee-eater/previews/waiting.gif) | ![running](assets/blue-throated-bee-eater/previews/running.gif) | ![jumping](assets/blue-throated-bee-eater/previews/jumping.gif) |
237
-
238
- | waving | review | failed | 左右移动 |
239
- |---|---|---|---|
240
- | ![waving](assets/blue-throated-bee-eater/previews/waving.gif) | ![review](assets/blue-throated-bee-eater/previews/review.gif) | ![failed](assets/blue-throated-bee-eater/previews/failed.gif) | ![running-left](assets/blue-throated-bee-eater/previews/running-left.gif) ![running-right](assets/blue-throated-bee-eater/previews/running-right.gif) |
241
-
242
- 社区贡献的**星夜人偶**宠物(仅经创意工坊分发,Theater-ahyeon 以 CC-BY-NC-SA-4.0 贡献)随附同一套状态预览:
230
+ | 鲸鱼娘(精致版) | Miku | 女仆鲸鱼娘 |
231
+ |---|---|---|
232
+ | ![鲸鱼娘(精致版)](assets/whale-refined/previews/idle.gif) | ![Miku](assets/miku/previews/idle.webp) | ![女仆鲸鱼娘](assets/jyn/previews/idle.webp) |
243
233
 
244
- | idle | waiting | running | jumping |
245
- |---|---|---|---|
246
- | ![idle](assets/starry-doll/previews/idle.gif) | ![waiting](assets/starry-doll/previews/waiting.gif) | ![running](assets/starry-doll/previews/running.gif) | ![jumping](assets/starry-doll/previews/jumping.gif) |
234
+ 精灵宠物使用由 [hatch-pet](https://github.com/dsh2026) 流水线生成的 8 列图集,单元格为 192×208。经典图集包含 9 行动画;v2 图集额外增加 2 行,共提供 16 个观察方向。女仆鲸鱼娘改用目录式帧序列,因此它的其它玩法状态是各自独立的预览文件:
247
235
 
248
- | waving | review | failed | 左右移动 |
236
+ | 害羞 | 工作 | 工作成功 | 睡眠 |
249
237
  |---|---|---|---|
250
- | ![waving](assets/starry-doll/previews/waving.gif) | ![review](assets/starry-doll/previews/review.gif) | ![failed](assets/starry-doll/previews/failed.gif) | ![running-left](assets/starry-doll/previews/running-left.gif) ![running-right](assets/starry-doll/previews/running-right.gif) |
238
+ | ![害羞](assets/jyn/previews/shy.webp) | ![工作](assets/jyn/previews/work.webp) | ![工作成功](assets/jyn/previews/work-success.webp) | ![睡眠](assets/jyn/previews/sleep.webp) |
251
239
 
252
240
  ## 架构
253
241
 
@@ -340,7 +328,7 @@ OUO Neko 使用扩展 v2 契约:图集为 1536×2288,在相同 9 行动画
340
328
  - 资产服务对宠物目录与目标文件双双做 `realpath` 解析;symlink 越界一律拒绝(403)。文件读入内存前按类限大小(清单 64 KB、图像 20 MB;超限 413)。
341
329
  - Live2D 模型按闭包放行:仅清单、声明的主资产与 `.model3.json` 引用到的文件(引用先经穿越/绝对路径/URL 形态筛查)。
342
330
  - 插件从不下载可执行文件,也从不内置 Live2D Cubism Core。
343
- - 清单结构 fail-closed:未知字段或未知渲染器直接拒载,并在设置中给出诊断。
331
+ - 清单结构 fail-closed:未知字段或未知渲染器直接拒载,而不是加载一只半坏的宠物(拒载原因可用 `node scripts/dsh-pet validate <dir>` 查看)。
344
332
 
345
333
  ## 数据遥测
346
334
 
package/lib/client.js CHANGED
@@ -3513,7 +3513,7 @@ window.__ModuleLoader__.load({
3513
3513
  };
3514
3514
  //#endregion
3515
3515
  //#region \0dsh-css:src/client/settings-section.module.css.mjs
3516
- const css = ".c5M8OW_sectionList{margin:0;padding:0;list-style:none}.c5M8OW_diagnostics{border:1px dashed var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);border-radius:8px;margin:8px 0;padding:8px 12px;font-size:12px}.c5M8OW_diagnosticsTitle{color:var(--dsw-alias-label-primary);margin-bottom:4px;font-weight:600;display:block}.c5M8OW_diagnostics ul{margin:0;padding-left:16px}.c5M8OW_diagnostics li[data-level=error]{color:var(--dsw-alias-label-error,#c04848)}";
3516
+ const css = ".c5M8OW_sectionList{margin:0;padding:0;list-style:none}";
3517
3517
  const tagId = "@linxin666/dsh-pet/src/client/settings-section.module.css";
3518
3518
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
3519
3519
  const tag = document.createElement("style");
@@ -3522,11 +3522,7 @@ window.__ModuleLoader__.load({
3522
3522
  tag.textContent = css;
3523
3523
  document.head.appendChild(tag);
3524
3524
  }
3525
- var settings_section_module_css_default = {
3526
- "diagnostics": "c5M8OW_diagnostics",
3527
- "diagnosticsTitle": "c5M8OW_diagnosticsTitle",
3528
- "sectionList": "c5M8OW_sectionList"
3529
- };
3525
+ var settings_section_module_css_default = { "sectionList": "c5M8OW_sectionList" };
3530
3526
  //#endregion
3531
3527
  //#region src/client/PetSettingsCard.tsx
3532
3528
  /** Fetch the registry list (the same data the sprite renders from). */
@@ -3535,19 +3531,16 @@ window.__ModuleLoader__.load({
3535
3531
  if (!response.ok) throw new Error("pet pets failed: " + response.status);
3536
3532
  return await response.json();
3537
3533
  }
3538
- /** Fetch the registry diagnostics (v1 migration hints, invalid entries). */
3539
- async function fetchPetDiagnostics() {
3540
- const response = await fetch("/api/pet/diagnostics");
3541
- if (!response.ok) throw new Error("pet diagnostics failed: " + response.status);
3542
- return (await response.json()).diagnostics ?? [];
3543
- }
3544
- /** Read the selection from the same persisted state the pet renders. */
3545
- async function fetchSelectedPetId() {
3534
+ /** Read the selection and visibility from the same persisted state the pet renders. */
3535
+ async function fetchPetState() {
3546
3536
  const response = await fetch("/api/pet/state");
3547
3537
  if (!response.ok) throw new Error("pet state failed: " + response.status);
3548
3538
  const body = await response.json();
3549
3539
  if (typeof body.pet?.id !== "string") throw new Error("pet state has no selected pet");
3550
- return body.pet.id;
3540
+ return {
3541
+ petId: body.pet.id,
3542
+ visible: body.display?.visible !== false
3543
+ };
3551
3544
  }
3552
3545
  /** Bridges the 'pet' scope onto the card's staged form. */
3553
3546
  var PetSettingsCardController = class {
@@ -3555,9 +3548,10 @@ window.__ModuleLoader__.load({
3555
3548
  store;
3556
3549
  petChoices = [];
3557
3550
  petLabels = /* @__PURE__ */ new Map();
3558
- diagnostics = [];
3559
3551
  selectedPetId;
3552
+ selectedVisible;
3560
3553
  stagedPetId;
3554
+ stagedVisible;
3561
3555
  savingPet = false;
3562
3556
  petSaveFailed = false;
3563
3557
  loaded = false;
@@ -3582,20 +3576,9 @@ window.__ModuleLoader__.load({
3582
3576
  this.pendingTimer = void 0;
3583
3577
  if (this.disposed) return;
3584
3578
  this.loadPets();
3585
- this.loadDiagnostics();
3586
- this.loadSelectedPet();
3579
+ this.loadPetState();
3587
3580
  }, 0);
3588
3581
  }
3589
- /** Fetch registry diagnostics once (soft-fail: an empty list on error). */
3590
- async loadDiagnostics() {
3591
- try {
3592
- this.diagnostics = await fetchPetDiagnostics();
3593
- if (this.disposed) return;
3594
- this.store.set(this.projection());
3595
- } catch {
3596
- this.diagnostics = [];
3597
- }
3598
- }
3599
3582
  /** Resolve the registry choices once (retried a few times on failure). */
3600
3583
  async loadPets() {
3601
3584
  if (this.loaded || this.disposed) return;
@@ -3616,11 +3599,12 @@ window.__ModuleLoader__.load({
3616
3599
  }, 3e3);
3617
3600
  }
3618
3601
  }
3619
- async loadSelectedPet() {
3602
+ async loadPetState() {
3620
3603
  try {
3621
- const petId = await fetchSelectedPetId();
3604
+ const state = await fetchPetState();
3622
3605
  if (this.disposed) return;
3623
- this.selectedPetId = petId;
3606
+ this.selectedPetId = state.petId;
3607
+ this.selectedVisible = state.visible;
3624
3608
  this.store.set(this.projection());
3625
3609
  } catch {}
3626
3610
  }
@@ -3628,22 +3612,58 @@ window.__ModuleLoader__.load({
3628
3612
  const shell = this.form.shell();
3629
3613
  return shell.available && !shell.exposed && this.selectedPetId !== void 0;
3630
3614
  }
3631
- async savePetSelection() {
3615
+ /**
3616
+ * The visibility the fallback switch renders: the staged draft when the user
3617
+ * moved it, the persisted value otherwise. There is no user layer to override
3618
+ * here, so the field is never marked overridden.
3619
+ */
3620
+ fallbackVisible() {
3621
+ const value = this.stagedVisible ?? this.selectedVisible;
3622
+ return {
3623
+ text: value === void 0 ? "" : String(value),
3624
+ overridden: false,
3625
+ invalid: false
3626
+ };
3627
+ }
3628
+ /**
3629
+ * Persist the staged selection and visibility through the pet API — the only
3630
+ * writer available when the aggregate shell serves this card, since there is
3631
+ * no Host settings form to mutate — then confirm the read-back before the
3632
+ * drafts are cleared.
3633
+ */
3634
+ async saveFallback() {
3632
3635
  const petId = this.stagedPetId;
3633
- if (petId === void 0 || this.savingPet || !this.petChoices.includes(petId)) return;
3636
+ const visible = this.stagedVisible;
3637
+ if (this.savingPet) return;
3638
+ if (petId === void 0 && visible === void 0) return;
3639
+ if (petId !== void 0 && !this.petChoices.includes(petId)) return;
3634
3640
  this.savingPet = true;
3635
3641
  this.petSaveFailed = false;
3636
3642
  this.store.set(this.projection());
3637
3643
  try {
3638
- const response = await fetch("/api/pet/set-pet", {
3639
- method: "POST",
3640
- headers: { "content-type": "application/json" },
3641
- body: JSON.stringify({ petId })
3642
- });
3643
- const result = await response.json();
3644
- if (!response.ok || result.ok !== true || result.petId !== petId || await fetchSelectedPetId() !== petId) throw new Error("pet selection was not persisted");
3645
- this.selectedPetId = petId;
3644
+ if (petId !== void 0) {
3645
+ const response = await fetch("/api/pet/set-pet", {
3646
+ method: "POST",
3647
+ headers: { "content-type": "application/json" },
3648
+ body: JSON.stringify({ petId })
3649
+ });
3650
+ const result = await response.json();
3651
+ if (!response.ok || result.ok !== true || result.petId !== petId) throw new Error("pet selection was not persisted");
3652
+ }
3653
+ if (visible !== void 0) {
3654
+ if (!(await fetch("/api/pet/set-visible", {
3655
+ method: "POST",
3656
+ headers: { "content-type": "application/json" },
3657
+ body: JSON.stringify({ visible })
3658
+ })).ok) throw new Error("pet visibility was not persisted");
3659
+ }
3660
+ const persisted = await fetchPetState();
3661
+ if (petId !== void 0 && persisted.petId !== petId) throw new Error("pet selection was not persisted");
3662
+ if (visible !== void 0 && persisted.visible !== visible) throw new Error("pet visibility was not persisted");
3663
+ this.selectedPetId = persisted.petId;
3664
+ this.selectedVisible = persisted.visible;
3646
3665
  if (this.stagedPetId === petId) this.stagedPetId = void 0;
3666
+ if (this.stagedVisible === visible) this.stagedVisible = void 0;
3647
3667
  } catch {
3648
3668
  this.petSaveFailed = true;
3649
3669
  } finally {
@@ -3660,7 +3680,7 @@ window.__ModuleLoader__.load({
3660
3680
  ...fallback ? {
3661
3681
  exposed: true,
3662
3682
  writable: true,
3663
- dirty: this.stagedPetId !== void 0 && this.stagedPetId !== this.selectedPetId,
3683
+ dirty: this.stagedPetId !== void 0 && this.stagedPetId !== this.selectedPetId || this.stagedVisible !== void 0 && this.stagedVisible !== this.selectedVisible,
3664
3684
  invalid: this.stagedPetId !== void 0 && !this.petChoices.includes(this.stagedPetId),
3665
3685
  saving: this.savingPet,
3666
3686
  failed: this.petSaveFailed,
@@ -3669,7 +3689,7 @@ window.__ModuleLoader__.load({
3669
3689
  petSelectionFallback: fallback,
3670
3690
  enabled: this.form.field("enabled"),
3671
3691
  decorationEnabled: this.form.field("decorationEnabled"),
3672
- visible: this.form.field("visible"),
3692
+ visible: fallback ? this.fallbackVisible() : this.form.field("visible"),
3673
3693
  size: this.form.field("size"),
3674
3694
  right: this.form.field("right"),
3675
3695
  bottom: this.form.field("bottom"),
@@ -3685,8 +3705,7 @@ window.__ModuleLoader__.load({
3685
3705
  petChoices: this.petChoices.map((id) => ({
3686
3706
  value: id,
3687
3707
  label: this.petLabels.get(id) ?? id
3688
- })),
3689
- petDiagnostics: this.diagnostics
3708
+ }))
3690
3709
  };
3691
3710
  }
3692
3711
  /**
@@ -3699,25 +3718,28 @@ window.__ModuleLoader__.load({
3699
3718
  hooks: { petSettingsCard: this.store },
3700
3719
  edit: (field, value) => {
3701
3720
  if (!this.fallback()) return actions.edit(field, value);
3702
- if (field !== "petId") return;
3703
- this.stagedPetId = value === "" ? void 0 : value;
3721
+ if (field === "petId") this.stagedPetId = value === "" ? void 0 : value;
3722
+ else if (field === "visible") this.stagedVisible = value === "" ? void 0 : value === "true";
3723
+ else return;
3704
3724
  this.petSaveFailed = false;
3705
3725
  this.store.set(this.projection());
3706
3726
  },
3707
3727
  resetField: (field) => {
3708
3728
  if (!this.fallback()) return actions.resetField(field);
3709
- if (field !== "petId") return;
3710
- this.stagedPetId = void 0;
3729
+ if (field === "petId") this.stagedPetId = void 0;
3730
+ else if (field === "visible") this.stagedVisible = void 0;
3731
+ else return;
3711
3732
  this.petSaveFailed = false;
3712
3733
  this.store.set(this.projection());
3713
3734
  },
3714
3735
  save: () => {
3715
- if (this.fallback()) this.savePetSelection();
3736
+ if (this.fallback()) this.saveFallback();
3716
3737
  else actions.save();
3717
3738
  },
3718
3739
  discard: () => {
3719
3740
  if (!this.fallback()) return actions.discard();
3720
3741
  this.stagedPetId = void 0;
3742
+ this.stagedVisible = void 0;
3721
3743
  this.petSaveFailed = false;
3722
3744
  this.store.set(this.projection());
3723
3745
  }
@@ -3809,18 +3831,7 @@ window.__ModuleLoader__.load({
3809
3831
  props.resetField("petId");
3810
3832
  }
3811
3833
  }),
3812
- state.petDiagnostics.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
3813
- className: settings_section_module_css_default.diagnostics,
3814
- "data-dsh-part": "diagnostics",
3815
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3816
- className: settings_section_module_css_default.diagnosticsTitle,
3817
- children: t("settings.diagnosticsTitle")
3818
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: state.petDiagnostics.map((diagnostic, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
3819
- "data-level": diagnostic.level,
3820
- children: diagnostic.message
3821
- }, index)) })]
3822
- }),
3823
- state.petSelectionFallback ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BooleanField, {
3834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BooleanField, {
3824
3835
  id: "settings-pet-visible",
3825
3836
  label: t("settings.visible"),
3826
3837
  hint: t("settings.visibleHint"),
@@ -3961,7 +3972,6 @@ window.__ModuleLoader__.load({
3961
3972
  "pet.moreSessions": "展开其余 {n} 个会话的气泡",
3962
3973
  "pet.collapseSessions": "收起会话气泡",
3963
3974
  "settings.title": "宠物",
3964
- "settings.diagnosticsTitle": "宠物目录诊断",
3965
3975
  "settings.description": "选择宠物并调整它的显示布局。",
3966
3976
  "settings.pet": "宠物",
3967
3977
  "settings.petHint": "选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。",
@@ -4044,7 +4054,6 @@ window.__ModuleLoader__.load({
4044
4054
  "pet.moreSessions": "Expand {n} more session bubbles",
4045
4055
  "pet.collapseSessions": "Collapse session bubbles",
4046
4056
  "settings.title": "Pet",
4047
- "settings.diagnosticsTitle": "Pet directory diagnostics",
4048
4057
  "settings.description": "Pick a pet and tune its display layout.",
4049
4058
  "settings.pet": "Pet",
4050
4059
  "settings.petHint": "Choose which pet shows. Names are stored per pet; rename from the pet hover panel.",
@@ -4123,7 +4132,7 @@ window.__ModuleLoader__.load({
4123
4132
  /** The building package's version, when the bundle carries it. */
4124
4133
  function bakedVersion() {
4125
4134
  try {
4126
- return "0.4.2";
4135
+ return "0.4.3";
4127
4136
  } catch {
4128
4137
  return;
4129
4138
  }