@linxin666/dsh-pet 0.4.2 → 0.4.4
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 +2 -2
- package/README.md +16 -28
- package/README.zh.md +16 -28
- package/lib/client.js +74 -65
- package/lib/client.js.map +1 -1
- package/lib/index.js +169 -21
- package/lib/types/client/PetSettingsCard.d.ts +16 -12
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +77 -49
- package/lib/types/client/locales.d.ts +0 -2
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +0 -2
- package/lib/types/index.d.ts +6 -2
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +82 -10
- package/lib/types/mount-once.d.ts +38 -7
- package/lib/types/mount-once.d.ts.map +1 -1
- package/lib/types/mount-once.js +96 -12
- package/lib/types/service.d.ts +9 -0
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +11 -1
- package/package.json +1 -1
- package/src/client/PetSettingsCard.test.tsx +115 -4
- package/src/client/PetSettingsCard.tsx +76 -66
- package/src/client/locales.ts +0 -2
- package/src/client/pet-css.test.ts +8 -0
- package/src/client/settings-section.module.css +0 -23
- package/src/index.test.ts +39 -0
- package/src/index.ts +105 -11
- package/src/mount-once.ts +107 -13
- package/src/service.ts +11 -1
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:
|
|
6
|
-
README.zh.md:
|
|
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
|
|
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.
|
|
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 |
|
|
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
|
-
##
|
|
220
|
+
## Most popular pets
|
|
221
221
|
|
|
222
|
-
|
|
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
|
-
|
|
|
224
|
+
| Pet | Renderer | Author | Source |
|
|
225
225
|
|---|---|---|---|
|
|
226
|
-
|
|
|
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
|
-
|
|
|
229
|
-
|
|
230
|
-
|  |  |  |  |
|
|
237
|
-
|
|
238
|
-
| waving | review | failed | move left/right |
|
|
239
|
-
|---|---|---|---|
|
|
240
|
-
|  |  |  |   |
|
|
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
|
+
|  |  |  |
|
|
243
233
|
|
|
244
|
-
|
|
245
|
-
|---|---|---|---|
|
|
246
|
-
|  |  |  |  |
|
|
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
|
-
|
|
|
236
|
+
| shy | work | work success | sleep |
|
|
249
237
|
|---|---|---|---|
|
|
250
|
-
|  |  |  |  |
|
|
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
|
|
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
|
-
|
|
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 或根不是对象 →
|
|
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 贡献):带打工/睡觉/触摸玩法与三款可选皮肤(暗夜鎏金 / 蓝海霓裳 / 冰晶公主)的女仆鲸鱼同桌宠,每款皮肤各带一个概率掷骰点击动作;暗夜鎏金另将专属休息循环换入睡觉玩法 |
|
|
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
|
-
|
|
222
|
+
[dsh-market.com](https://dsh-market.com) 创意工坊宠物分类里人气最高的三只宠物,按网站默认的「按人气」排序。每只都是「一个 `pet.json` 加素材」,由上文契约渲染——无需任何按宠物编写的代码:
|
|
223
223
|
|
|
224
|
-
|
|
|
224
|
+
| 宠物 | 渲染器 | 作者 | 来源 |
|
|
225
225
|
|---|---|---|---|
|
|
226
|
-
|
|
|
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
|
-
|
|
|
229
|
-
|
|
230
|
-
|  |  |  |  |
|
|
237
|
-
|
|
238
|
-
| waving | review | failed | 左右移动 |
|
|
239
|
-
|---|---|---|---|
|
|
240
|
-
|  |  |  |   |
|
|
241
|
-
|
|
242
|
-
社区贡献的**星夜人偶**宠物(仅经创意工坊分发,Theater-ahyeon 以 CC-BY-NC-SA-4.0 贡献)随附同一套状态预览:
|
|
230
|
+
| 鲸鱼娘(精致版) | Miku | 女仆鲸鱼娘 |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
|  |  |  |
|
|
243
233
|
|
|
244
|
-
|
|
245
|
-
|---|---|---|---|
|
|
246
|
-
|  |  |  |  |
|
|
234
|
+
精灵宠物使用由 [hatch-pet](https://github.com/dsh2026) 流水线生成的 8 列图集,单元格为 192×208。经典图集包含 9 行动画;v2 图集额外增加 2 行,共提供 16 个观察方向。女仆鲸鱼娘改用目录式帧序列,因此它的其它玩法状态是各自独立的预览文件:
|
|
247
235
|
|
|
248
|
-
|
|
|
236
|
+
| 害羞 | 工作 | 工作成功 | 睡眠 |
|
|
249
237
|
|---|---|---|---|
|
|
250
|
-
|  |  |  |  |
|
|
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}
|
|
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
|
-
/**
|
|
3539
|
-
async function
|
|
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
|
|
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.
|
|
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
|
|
3602
|
+
async loadPetState() {
|
|
3620
3603
|
try {
|
|
3621
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
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
|
|
3703
|
-
this.
|
|
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
|
|
3710
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
4135
|
+
return "0.4.4";
|
|
4127
4136
|
} catch {
|
|
4128
4137
|
return;
|
|
4129
4138
|
}
|