dsh-wisp 0.6.0 → 1.25.1

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.
Files changed (68) hide show
  1. package/README.md +869 -10
  2. package/assets/canon/attn.webp +0 -0
  3. package/assets/canon/happy.webp +0 -0
  4. package/assets/canon/idle.webp +0 -0
  5. package/assets/canon/poked.webp +0 -0
  6. package/assets/canon/proud.webp +0 -0
  7. package/assets/canon/sleepy.webp +0 -0
  8. package/assets/canon/work.webp +0 -0
  9. package/assets/classic/attn.webp +0 -0
  10. package/assets/classic/happy.webp +0 -0
  11. package/assets/classic/idle.webp +0 -0
  12. package/assets/classic/poked.webp +0 -0
  13. package/assets/classic/proud.webp +0 -0
  14. package/assets/classic/sleepy.webp +0 -0
  15. package/assets/classic/work.webp +0 -0
  16. package/assets/deepsea/attn.webp +0 -0
  17. package/assets/deepsea/happy.webp +0 -0
  18. package/assets/deepsea/idle.webp +0 -0
  19. package/assets/deepsea/poked.webp +0 -0
  20. package/assets/deepsea/proud.webp +0 -0
  21. package/assets/deepsea/sleepy.webp +0 -0
  22. package/assets/deepsea/work.webp +0 -0
  23. package/assets/night/attn.webp +0 -0
  24. package/assets/night/happy.webp +0 -0
  25. package/assets/night/idle.webp +0 -0
  26. package/assets/night/poked.webp +0 -0
  27. package/assets/night/proud.webp +0 -0
  28. package/assets/night/sleepy.webp +0 -0
  29. package/assets/night/work.webp +0 -0
  30. package/assets/pajama/attn.webp +0 -0
  31. package/assets/pajama/happy.webp +0 -0
  32. package/assets/pajama/idle.webp +0 -0
  33. package/assets/pajama/poked.webp +0 -0
  34. package/assets/pajama/proud.webp +0 -0
  35. package/assets/pajama/sleepy.webp +0 -0
  36. package/assets/pajama/work.webp +0 -0
  37. package/audit/generation-audit.jsonl +35 -0
  38. package/build.mjs +93 -30
  39. package/cordis.patch.yml +2 -0
  40. package/lib/client.js +1293 -41
  41. package/lib/client.template.js +1248 -37
  42. package/lib/index.js +115 -12
  43. package/package.json +8 -1
  44. package/tools/CHARACTER-PROMPT.md +221 -3
  45. package/tools/assets.mjs +39 -16
  46. package/tools/audit-log.mjs +245 -0
  47. package/tools/bundle-cost.mjs +105 -0
  48. package/tools/pack-check.mjs +78 -0
  49. package/tools/perf.mjs +186 -0
  50. package/tools/smoke.mjs +577 -0
  51. package/tools/tiers.mjs +104 -0
  52. package/verify-wisp.mjs +2681 -872
  53. package/assets/happy.webp +0 -0
  54. package/assets/happy_hi.webp +0 -0
  55. package/assets/happy_md.webp +0 -0
  56. package/assets/happy_sm.webp +0 -0
  57. package/assets/idle.webp +0 -0
  58. package/assets/idle_hi.webp +0 -0
  59. package/assets/idle_md.webp +0 -0
  60. package/assets/idle_sm.webp +0 -0
  61. package/assets/sleepy.webp +0 -0
  62. package/assets/sleepy_hi.webp +0 -0
  63. package/assets/sleepy_md.webp +0 -0
  64. package/assets/sleepy_sm.webp +0 -0
  65. package/assets/work.webp +0 -0
  66. package/assets/work_hi.webp +0 -0
  67. package/assets/work_md.webp +0 -0
  68. package/assets/work_sm.webp +0 -0
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,35 @@
1
+ {"index":0,"prev":"genesis","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.298Z","name":"canon_idle","skin":"canon","mood":"idle","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_idle_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_idle_20260929185539.png","sha256":"8a783a679ae8d9f4f28449741d8d941acae641377f864126c28e623531d04c2c"},"output":{"path":"assets/canon/idle.webp","sha256":"9174b21d6852d86debfed174889d96b2bf00d0b7952e34ad62bca547cd397718","bytes":127654},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"15d6e4ef4bf8130b2bd6427dabe787a0380613fc484794bb0bf2bdf9ece65c6f"}
2
+ {"index":1,"prev":"15d6e4ef4bf8130b2bd6427dabe787a0380613fc484794bb0bf2bdf9ece65c6f","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.319Z","name":"canon_happy","skin":"canon","mood":"happy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_happy_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_happy_20260929185539.png","sha256":"32056f976afbc67a946b8a48be7af0fda4adac47addb34412c6177cd4f30d6db"},"output":{"path":"assets/canon/happy.webp","sha256":"9b0fa81076cf4b02a98285e88342098cadac05833d6523a532442ad62758e1b3","bytes":142168},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"9324e910d706ed0ec7533cad7e910a43a9c5f802560e78bc4dc066fb802a4a3f"}
3
+ {"index":2,"prev":"9324e910d706ed0ec7533cad7e910a43a9c5f802560e78bc4dc066fb802a4a3f","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.332Z","name":"canon_sleepy","skin":"canon","mood":"sleepy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_sleepy_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_sleepy_20260929185539.png","sha256":"c92d56846c975b834292a937c4473ebb0d38a4d108ec1fb8cb0e6b5d668869d9"},"output":{"path":"assets/canon/sleepy.webp","sha256":"46b7c22d182aa29831756b6ced708c1398584e4289650378a8d51e070eb1f42a","bytes":123758},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"4e788be0a802428d1d21a66e2bf460becf51db7006d2f91018bcf382456f4c98"}
4
+ {"index":3,"prev":"4e788be0a802428d1d21a66e2bf460becf51db7006d2f91018bcf382456f4c98","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.345Z","name":"canon_work","skin":"canon","mood":"work","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_work_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_work_20260929185539.png","sha256":"57bd85febfbda157e9fe1d6775c93414763443b1781fda922069ec800ae480a0"},"output":{"path":"assets/canon/work.webp","sha256":"7c35eb87f563a05a99d26c4ebba1fc0bfee051a3bf52a5815bc596a9b6286eca","bytes":149982},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"8bb71551d74d7d95fa4d98a13e71ef808b4edcdfdc050f68cc60e0d5268b26e1"}
5
+ {"index":4,"prev":"8bb71551d74d7d95fa4d98a13e71ef808b4edcdfdc050f68cc60e0d5268b26e1","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.357Z","name":"canon_attn","skin":"canon","mood":"attn","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_attn_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_attn_20260929185539.png","sha256":"e28c2e204e6bb0666ff8c862dd59d91470830ac7362d7a5027051f995c256a3d"},"output":{"path":"assets/canon/attn.webp","sha256":"1f7dfd6a69253e28824b200ffca767c9f651a76f86517a82d670f5fdfd018bc3","bytes":140564},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"c7f6190dc071fb03b2630c5271e6a4ddeb440780f4414e6be092cfce0d0c0684"}
6
+ {"index":5,"prev":"c7f6190dc071fb03b2630c5271e6a4ddeb440780f4414e6be092cfce0d0c0684","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.370Z","name":"canon_poked","skin":"canon","mood":"poked","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_poked_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_poked_20260929185539.png","sha256":"98817f78a9c01d9e3648abf71413d54bd8890a01bf3c67a3aa18053ac4dba4b2"},"output":{"path":"assets/canon/poked.webp","sha256":"4985bb577c2a49585654efd08246897ad7c9c7d434f27c5b497b7b2d3842434b","bytes":138386},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"89580b20e033bb0b26c5b94f70d003ab67e77e700e1c414caff937e22d2e163e"}
7
+ {"index":6,"prev":"89580b20e033bb0b26c5b94f70d003ab67e77e700e1c414caff937e22d2e163e","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.378Z","name":"canon_proud","skin":"canon","mood":"proud","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T18:55:39.243Z","localPath":"assets/image/canon_proud_20260929185539.png"},"master":{"path":"F:\\孤屿\\assets\\image\\canon_proud_20260929185539.png","sha256":"b15bc63be53650bec6abd9a7ee5b081766bc63a03b95af83873b4fe103422f4a"},"output":{"path":"assets/canon/proud.webp","sha256":"66f2a008cc98d4348877e7aaa9ecdb125c11d80d79bbf90a7ffa516effd4b3f2","bytes":124790},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"1ec1dd07061f458ed00ccfc1dd1d230d42b69f7699316340baeab0f7ecd4056f"}
8
+ {"index":7,"prev":"1ec1dd07061f458ed00ccfc1dd1d230d42b69f7699316340baeab0f7ecd4056f","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.391Z","name":"ds2_idle","skin":"deepsea","mood":"idle","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_idle_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_idle_20260929191136.png","sha256":"a77a3f62f253dff5f64f069091cfad4004bc31eb82f30af3e3a976baa2b487b2"},"output":{"path":"assets/deepsea/idle.webp","sha256":"bc396c646d8a45b6796d23f790db85af148e7c79323175aa328ee9fd208bf6b8","bytes":107130},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"1bdbc9d854996d0fae1db579be27665958a854ebdc44008f171037c1fc072416"}
9
+ {"index":8,"prev":"1bdbc9d854996d0fae1db579be27665958a854ebdc44008f171037c1fc072416","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.398Z","name":"ds2_happy","skin":"deepsea","mood":"happy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_happy_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_happy_20260929191136.png","sha256":"cf589754e8b6eb69abc84e28634c4bebd990b60151d8e78c15de81cc46c3dfa3"},"output":{"path":"assets/deepsea/happy.webp","sha256":"df00f27cf0bcd880a1e4d59e86971dcf2321c09ca8495c7eca507f2caf979195","bytes":114128},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"e9d9010d1c09954f63841904a884280ff8d5cba808f079aa867ce33113b536a8"}
10
+ {"index":9,"prev":"e9d9010d1c09954f63841904a884280ff8d5cba808f079aa867ce33113b536a8","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.406Z","name":"ds2_sleepy","skin":"deepsea","mood":"sleepy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_sleepy_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_sleepy_20260929191136.png","sha256":"fa074e2609e73710d188a22a23f9e516032ca0bf6caf0a432459047e838e3d29"},"output":{"path":"assets/deepsea/sleepy.webp","sha256":"49a6f6e7397669703bf6a3f5d75f36e410cef5d732b1bc0dcdb362bbcea55d32","bytes":119086},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"0d06b5a71af7780de15e5842e6218488b947d8776bb070c685227ae175e70cf5"}
11
+ {"index":10,"prev":"0d06b5a71af7780de15e5842e6218488b947d8776bb070c685227ae175e70cf5","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.413Z","name":"ds2_work","skin":"deepsea","mood":"work","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_work_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_work_20260929191136.png","sha256":"65be6ab099bd98a3d9880ee97dccb25be76b8a0d0efa0802a196babfc7aa6fca"},"output":{"path":"assets/deepsea/work.webp","sha256":"c866fc507ca6173222c1a80ae49f6657aa5068fece93ae8e014ddca35c8b60e8","bytes":110934},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"8f0cb34856c53f3fcea92ec0c8ae637bd3bb71ee70fe7a4d82e52ce6e7a2a059"}
12
+ {"index":11,"prev":"8f0cb34856c53f3fcea92ec0c8ae637bd3bb71ee70fe7a4d82e52ce6e7a2a059","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.420Z","name":"ds2_attn","skin":"deepsea","mood":"attn","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_attn_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_attn_20260929191136.png","sha256":"3bcfe7ac73469daf1ad3c303db1fd1dea9719ec5d0dd7c7a0b64f01261e6cc67"},"output":{"path":"assets/deepsea/attn.webp","sha256":"7debb4a21ecc4e25ed45c913eb248978d11725325b0dc82426b058d7451528b3","bytes":99666},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"3a4594e088a2b5c241601dfea68cf319b88a4047fe6ab16fc1c4e3dd3e7cb441"}
13
+ {"index":12,"prev":"3a4594e088a2b5c241601dfea68cf319b88a4047fe6ab16fc1c4e3dd3e7cb441","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.428Z","name":"ds2_poked","skin":"deepsea","mood":"poked","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_poked_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_poked_20260929191136.png","sha256":"8383d5294c26873c1ed9e134bba0c7be5a53df5a046caeba5a15ed721cbc12b2"},"output":{"path":"assets/deepsea/poked.webp","sha256":"780dbe8ea3680ce6d59b6e28c37319f49d7c56c6e25010187f5a9c3896751351","bytes":108474},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"e676a785f7104154390c23ba06778bafffcf736d6277e857f3c6216fcf7af056"}
14
+ {"index":13,"prev":"e676a785f7104154390c23ba06778bafffcf736d6277e857f3c6216fcf7af056","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.435Z","name":"ds2_proud","skin":"deepsea","mood":"proud","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:11:36.911Z","localPath":"assets/image/ds2_proud_20260929191136.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ds2_proud_20260929191136.png","sha256":"0809dbc6e92280a811d613b9562e2e295a19a9fa2fce725d91d76826d825fec9"},"output":{"path":"assets/deepsea/proud.webp","sha256":"f345e814c54b926c6fa96765fa0de1e9ff2b9bb3909c0a0d534d5c7bc5d7574a","bytes":99244},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"1e3b15612c6e94699298850086ab3ecf44d98f2ca1cd59925b8b1a43a4cdd69d"}
15
+ {"index":14,"prev":"1e3b15612c6e94699298850086ab3ecf44d98f2ca1cd59925b8b1a43a4cdd69d","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.443Z","name":"cl2_idle","skin":"classic","mood":"idle","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_idle_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_idle_20260929191247.png","sha256":"a69ad868558b9efa366ee8925d866758c8b95c47b7342b64a22ae3af7c75e7b6"},"output":{"path":"assets/classic/idle.webp","sha256":"c6393fa555c6986493f0b8d95bbb538d6bdd99415b019e2fc0078d346a7a476a","bytes":88366},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"cb25df478bc1111916e415dc72e3c1ce1cb0db50e1de9abdb7390ea1945c11f1"}
16
+ {"index":15,"prev":"cb25df478bc1111916e415dc72e3c1ce1cb0db50e1de9abdb7390ea1945c11f1","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.449Z","name":"cl2_happy","skin":"classic","mood":"happy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_happy_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_happy_20260929191247.png","sha256":"5c7e0e245dfab050ebd31d57dd2685eb356a5c000c8d8b830791854e63acc50e"},"output":{"path":"assets/classic/happy.webp","sha256":"f7e7007f07a7e97398769ef70fec54b48c2f27a3d2301e0bcbc4f28ecd59366c","bytes":108522},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"9435e761fb1171539c3e25712f64b47d5d8314f93cc774f1e22955e21e5602b7"}
17
+ {"index":16,"prev":"9435e761fb1171539c3e25712f64b47d5d8314f93cc774f1e22955e21e5602b7","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.456Z","name":"cl2_sleepy","skin":"classic","mood":"sleepy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_sleepy_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_sleepy_20260929191247.png","sha256":"9dc80c6c74740808f9f9fc96c5b0606fd2da6f3c52ec6bdb6fe070952593a551"},"output":{"path":"assets/classic/sleepy.webp","sha256":"706b718e4ea9fcaa882c41e21721d1ea1bdcb70de4015621946db6b8b9c7a5a8","bytes":99548},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"fea15f42792ad86d4cfdc7e2864e06709cb8660f5b8bbebb5d5017e8f5b11246"}
18
+ {"index":17,"prev":"fea15f42792ad86d4cfdc7e2864e06709cb8660f5b8bbebb5d5017e8f5b11246","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.462Z","name":"cl2_work","skin":"classic","mood":"work","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_work_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_work_20260929191247.png","sha256":"28e68495eb2fa2d94839899c3468af3470eaa18b62b1ff9bc82ed5f944a2fd98"},"output":{"path":"assets/classic/work.webp","sha256":"771acaf447a12582766f12ea36a8c9fe55406e177660337e5a1ba9ff92745f0f","bytes":93002},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"e574de66e7b953d8409688423777e876388f7a524793427de569d05d3f789382"}
19
+ {"index":18,"prev":"e574de66e7b953d8409688423777e876388f7a524793427de569d05d3f789382","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.470Z","name":"cl2_attn","skin":"classic","mood":"attn","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_attn_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_attn_20260929191247.png","sha256":"b1ff0d8f1253bed3537cdfae9af5e36aeb90c61661406f1ad1d66d724ba9ef70"},"output":{"path":"assets/classic/attn.webp","sha256":"9b4b1e589a3bdab1c838e0aabed9194931b4179a6a6d3f0635d68224f686370b","bytes":98708},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"2ac82169391a9796708877205340dee915e03cf518a111e97b6608fb5b8c9ffa"}
20
+ {"index":19,"prev":"2ac82169391a9796708877205340dee915e03cf518a111e97b6608fb5b8c9ffa","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.477Z","name":"cl2_poked","skin":"classic","mood":"poked","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_poked_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_poked_20260929191247.png","sha256":"c570d4d8e085745f70c472bbc6a7c9c0f4902cd068d6c926b3b8c1fbffc2d4a4"},"output":{"path":"assets/classic/poked.webp","sha256":"bd1ffc78b8534a68df36b28a9482a1ca1c0d02d618c21add5e01d92e8eed924e","bytes":93084},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"213effbecc7233a3cb74706b5c4f6dd731399ddc8b91d4f4d963c1cb9c407e10"}
21
+ {"index":20,"prev":"213effbecc7233a3cb74706b5c4f6dd731399ddc8b91d4f4d963c1cb9c407e10","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.485Z","name":"cl2_proud","skin":"classic","mood":"proud","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:12:47.023Z","localPath":"assets/image/cl2_proud_20260929191247.png"},"master":{"path":"F:\\孤屿\\assets\\image\\cl2_proud_20260929191247.png","sha256":"37577b9fb448bca9ec4edcdd14d261681fa647d8f8487611e271bc518eb17683"},"output":{"path":"assets/classic/proud.webp","sha256":"32247eb59fbb7c0e951397811184c73b96705469fa945cc214744787f696e69e","bytes":91558},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"ece594c365aa5119980b713e09a1cc377c67801b7667d42b0078ffbf1f5c30a2"}
22
+ {"index":21,"prev":"ece594c365aa5119980b713e09a1cc377c67801b7667d42b0078ffbf1f5c30a2","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.492Z","name":"ng2_idle","skin":"night","mood":"idle","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_idle_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_idle_20260929191512.png","sha256":"434087975a49405aa0fd5d4a796c2dfa0312eb017ce6e949821c77c7ef0db1a2"},"output":{"path":"assets/night/idle.webp","sha256":"722ade879efb6504cb5214cbe22ecd58bffa8e2d65859f0a3a0ca40d5579d861","bytes":106974},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"2094c90fba21b38255d7016454dd99914a2c3f2085aad198d8afd5f6c9c40d5c"}
23
+ {"index":22,"prev":"2094c90fba21b38255d7016454dd99914a2c3f2085aad198d8afd5f6c9c40d5c","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.500Z","name":"ng2_happy","skin":"night","mood":"happy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_happy_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_happy_20260929191512.png","sha256":"e96494f22c2dda357952e3795dd1ca5aa83184439185be20587864b0b2ac56ed"},"output":{"path":"assets/night/happy.webp","sha256":"8d4dca267d79274445872b50c61927dd7d25c8eacd6e6081cfee3b1903b31616","bytes":115850},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"518de521e5e4c282798771fa708004effb1c573427c1d9ca5f28f19893ac8f42"}
24
+ {"index":23,"prev":"518de521e5e4c282798771fa708004effb1c573427c1d9ca5f28f19893ac8f42","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.511Z","name":"ng2_sleepy","skin":"night","mood":"sleepy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_sleepy_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_sleepy_20260929191512.png","sha256":"f35cf70f37849e7f3873e4b849cdc202402b3b04379cdc83bc1370d5af7223b0"},"output":{"path":"assets/night/sleepy.webp","sha256":"4b6d773fddfc8c8d9c46a0791eb278e42a3c8477ece65606e3507b8d685be21b","bytes":101618},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"92950e3c7f9ae77742ce4a8759aec32b07cb67e0843751680cdfcded9a459436"}
25
+ {"index":24,"prev":"92950e3c7f9ae77742ce4a8759aec32b07cb67e0843751680cdfcded9a459436","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.520Z","name":"ng2_work","skin":"night","mood":"work","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_work_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_work_20260929191512.png","sha256":"867295660089b85e17c39ae230bebf8cb5fc29fc26ee928ac606dbab356ea621"},"output":{"path":"assets/night/work.webp","sha256":"e335ca07b47f38e4adbabe0ac5bd10129d68f7d73842523ca513cf2178c4a254","bytes":110360},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"c5b3546a709d32ab89089cea464ee6c42faf7a8d5be8ab682094489a868b8e09"}
26
+ {"index":25,"prev":"c5b3546a709d32ab89089cea464ee6c42faf7a8d5be8ab682094489a868b8e09","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.530Z","name":"ng2_attn","skin":"night","mood":"attn","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_attn_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_attn_20260929191512.png","sha256":"4efabc805024f00b456dc18533ddb1c409064b0ef78759463ee36b00f182efd1"},"output":{"path":"assets/night/attn.webp","sha256":"1d3c5208bbf4ad6135a27c5184fdddc4d76b6fc815c2ec38dfdde67b7501684d","bytes":98214},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"bf66d2d13302e29575042e3a7dde276459c54590e156ec11de890b93bb76379f"}
27
+ {"index":26,"prev":"bf66d2d13302e29575042e3a7dde276459c54590e156ec11de890b93bb76379f","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.537Z","name":"ng2_poked","skin":"night","mood":"poked","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_poked_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_poked_20260929191512.png","sha256":"c886b7b6f4661f107a4b66a4fec7811988b74ca75ae20e33ce264b32b2c38f82"},"output":{"path":"assets/night/poked.webp","sha256":"ddead75b17aa57f81d61ac9cd54c5c1b86710f53ce2c0d945f7f2f3d737bc14d","bytes":102968},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"192315ad89a95c24be97d4a8618535500a63a00057adb90c2a0c97de5a508643"}
28
+ {"index":27,"prev":"192315ad89a95c24be97d4a8618535500a63a00057adb90c2a0c97de5a508643","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.544Z","name":"ng2_proud","skin":"night","mood":"proud","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:15:12.600Z","localPath":"assets/image/ng2_proud_20260929191512.png"},"master":{"path":"F:\\孤屿\\assets\\image\\ng2_proud_20260929191512.png","sha256":"cd357b4416515c02fc6356a603c9ad1a86c7d08fd9d88065f83141bce2afcdc6"},"output":{"path":"assets/night/proud.webp","sha256":"28ca2084b5ca77fbc15ae463a75f085e1d88e054bf1c11e0d20934fa22065107","bytes":106622},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"645c768ecf6d4702053abfcc869b51951272dba73d309d0200870268014b14ad"}
29
+ {"index":28,"prev":"645c768ecf6d4702053abfcc869b51951272dba73d309d0200870268014b14ad","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.551Z","name":"pj2_idle","skin":"pajama","mood":"idle","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_idle_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_idle_20260929191609.png","sha256":"c30757d40d1a2c7bc1ccedced44250249c96dd31714a9a9140bb57e2c663ee88"},"output":{"path":"assets/pajama/idle.webp","sha256":"959ac8345222a3e9b30afad6d971cf2217038b1f10d44fd177cf0d9146c87692","bytes":115700},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"45e3a2cf17b4a02bdca56af234eb219d270ded7666401046ec6c6bc6e9baa389"}
30
+ {"index":29,"prev":"45e3a2cf17b4a02bdca56af234eb219d270ded7666401046ec6c6bc6e9baa389","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.558Z","name":"pj2_happy","skin":"pajama","mood":"happy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_happy_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_happy_20260929191609.png","sha256":"5f0bf3604e2b5f873927fea497b9a9f7892c4ef61cdce497573d65a205042501"},"output":{"path":"assets/pajama/happy.webp","sha256":"59611660dd486e4d8965c1f43f0d3b03b843f25d51c8dacdc6ecaafad1a9f608","bytes":124802},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"432106ebdec273ead79d5cb4bd12af50f96da5a169cb7b804838549483bde2e9"}
31
+ {"index":30,"prev":"432106ebdec273ead79d5cb4bd12af50f96da5a169cb7b804838549483bde2e9","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.567Z","name":"pj2_sleepy","skin":"pajama","mood":"sleepy","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_sleepy_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_sleepy_20260929191609.png","sha256":"381cfe7555f75238d0927a42df998cb7f44c64c2e4439f12767c491905ee83d3"},"output":{"path":"assets/pajama/sleepy.webp","sha256":"d45fdd86f4dd5a585245ae18af051b285853da657746eb41a2e6ad24d28c0fe1","bytes":113010},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"1d445b3691f0fe443fd20fecdc7da78021493e0f31e5a5d9c34013a3d3ee19e7"}
32
+ {"index":31,"prev":"1d445b3691f0fe443fd20fecdc7da78021493e0f31e5a5d9c34013a3d3ee19e7","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.577Z","name":"pj2_work","skin":"pajama","mood":"work","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_work_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_work_20260929191609.png","sha256":"4aabcd79db49036dd9c2514456a8adf136de3b20ae9488a4ea3dccd0f46acaf5"},"output":{"path":"assets/pajama/work.webp","sha256":"e68c2d74f11866d4c2222329690d5fcfeb3108e50af89adcc8537103f83f8f2d","bytes":110346},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"0cf1a63cf80010ebbd60732da414c498e737982541bd99cd6ff17630dc400be8"}
33
+ {"index":32,"prev":"0cf1a63cf80010ebbd60732da414c498e737982541bd99cd6ff17630dc400be8","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.585Z","name":"pj2_attn","skin":"pajama","mood":"attn","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_attn_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_attn_20260929191609.png","sha256":"88c210219d8ec51d06a0cbf0688501b63edfbfe4e8483560cb27cf26770b1ed5"},"output":{"path":"assets/pajama/attn.webp","sha256":"4b83d0824414b2010512bf69adca90a67a52c2a900dee768480c892d08f18b7d","bytes":120264},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"87af13c5d179617c97ee9c0af6d4bc25a7f90d0ce670bb033fe2accfa977e01e"}
34
+ {"index":33,"prev":"87af13c5d179617c97ee9c0af6d4bc25a7f90d0ce670bb033fe2accfa977e01e","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.593Z","name":"pj2_poked","skin":"pajama","mood":"poked","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_poked_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_poked_20260929191609.png","sha256":"ddc769a7d748a076dd555af189410851ef73af2cb141f75a876de602b296ecda"},"output":{"path":"assets/pajama/poked.webp","sha256":"f61e303037c9a67bc2ec769ae240f88a4ba8722311e8975258ea8a0a2350ef03","bytes":110922},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"15e5ec5b886db9837bb139d644c7999e3afa1dec60355a8314585ea4c042feeb"}
35
+ {"index":34,"prev":"15e5ec5b886db9837bb139d644c7999e3afa1dec60355a8314585ea4c042feeb","kind":"confirm","source":"registry-backfill","at":"2026-09-29T19:38:40.602Z","name":"pj2_proud","skin":"pajama","mood":"proud","registry":{"tool":"batch_generate_images","createdAt":"2026-09-29T19:16:09.300Z","localPath":"assets/image/pj2_proud_20260929191609.png"},"master":{"path":"F:\\孤屿\\assets\\image\\pj2_proud_20260929191609.png","sha256":"565c20a42a41c5ee5e3b25eb460e2baa8e67b1c58cf2ceef3d8b94e1add8ae44"},"output":{"path":"assets/pajama/proud.webp","sha256":"bb70bd99c8cb259edf4291414fca4d93fa7a3245d95433f611b8e4ddcd34d7a9","bytes":102080},"declared":null,"note":"回溯补录:只有代理登记表的事实,没有当次自我声明的参数","hash":"79943856f73dff0063ac4c91efb2e4c447b0b73e3aa95739f002f0cba56b0d3d"}
package/build.mjs CHANGED
@@ -11,23 +11,22 @@
11
11
  extra route, and no missing-asset failure mode.
12
12
 
13
13
  RUN
14
- node build.mjs # default tier: 1024x1536
15
- node build.mjs --tier=hi # 2048x3072 masters (~3x the bundle)
14
+ node build.mjs # default tier: 1024x1536 —— 这一档进包,也是唯一被显示的
15
+ node build.mjs --tier=hi # 2048x3072(需先 tools/assets.mjs --all-tiers 生成 _hi)
16
16
  node build.mjs --tier=md # 512x768
17
17
  node build.mjs --tier=sm # 256x384 (--small is an alias)
18
18
  node build.mjs --from assets/ # optional, defaults to ./assets
19
19
 
20
- WHICH SOURCE IS INLINED MATTERS FOR SHARPNESS. assets/ carries four tiers per
21
- mood, all downsampled from one 2048x3072 master:
20
+ ONLY ONE TIER SHIPS. assets/ now carries just <mood>.webp (1024x1536) per skin.
21
+ The other three tiers are no longer published: runtime never reads them, and
22
+ _hi measured no sharper than the default (DPR 2: 0.3% high-frequency
23
+ difference; DPR 1/3: 1024 was 4~5% BETTER — 2048 gets resampled harder, and
24
+ resampling is itself a low-pass filter). They cost 9.9 MB of the package for
25
+ nothing. Regenerate on demand with `tools/assets.mjs --all-tiers`; the TIERS
26
+ map below falls back down its chain when a preferred file is absent.
22
27
 
23
- <mood>.webp 1024x1536 default sharp to devicePixelRatio ~4.9 at the
24
- shipped render size (210x315 CSS px)
25
- <mood>_md.webp 512x768 the pre-0.4 size
26
- <mood>_sm.webp 256x384 smallest
27
- <mood>_hi.webp 2048x3072 headroom for a much larger wisp
28
-
29
- The default is the smallest tier that is sharp on every realistic display; the
30
- tiers above it add bytes, not visible pixels, until she is drawn much bigger.
28
+ The default is the smallest tier that is sharp on every realistic display:
29
+ sharp to devicePixelRatio ~4.9 at the shipped render size (210x315 CSS px).
31
30
  ========================================================================== */
32
31
 
33
32
  import { readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs'
@@ -46,7 +45,20 @@ const assetsDir = resolve(here, flagValue('--from', 'assets'))
46
45
  const templatePath = join(here, 'lib', 'client.template.js')
47
46
  const outPath = join(here, 'lib', 'client.js')
48
47
 
49
- const MOODS = ['idle', 'happy', 'sleepy', 'work']
48
+ const MOODS = ['idle', 'happy', 'sleepy', 'work', 'attn', 'poked', 'proud']
49
+
50
+ /* 皮肤 = assets/ 下的一个子目录,里面是这套皮肤的 <mood>[_tier].webp。
51
+ 如果没有子目录含精灵图,就把 assets/ 本身当作一个名为 default 的皮肤 ——
52
+ 这样 --from <任意目录> 的单套素材用法仍然成立。 */
53
+ const SKIN_LABELS = { deepsea: '深海女仆', canon: '原版女仆', classic: '素绘女仆', default: '默认' }
54
+ const skinLabel = (id) => SKIN_LABELS[id] ?? id
55
+ /* 顺序即优先级:列表里第一个就是默认皮肤。不这么写的话默认值会由目录名的字母序
56
+ 决定 —— 那是构建实现的偶然,不该变成产品行为。未列出的排在后面。 */
57
+ const SKIN_PRIORITY = ['deepsea', 'canon', 'classic', 'night', 'pajama']
58
+ const skinRank = (id) => {
59
+ const at = SKIN_PRIORITY.indexOf(id)
60
+ return at < 0 ? SKIN_PRIORITY.length : at
61
+ }
50
62
 
51
63
  /** Preference order per tier: the first hit wins, later entries are fallbacks. */
52
64
  const TIERS = {
@@ -67,30 +79,48 @@ if (!existsSync(assetsDir)) {
67
79
  process.exit(1)
68
80
  }
69
81
 
70
- const pickFile = (mood) => {
82
+ const pickFile = (dir, mood) => {
71
83
  for (const suffix of TIERS[tier]) {
72
84
  for (const ext of ['.webp', '.png']) {
73
- const p = join(assetsDir, `${mood}${suffix}${ext}`)
85
+ const p = join(dir, `${mood}${suffix}${ext}`)
74
86
  if (existsSync(p)) return p
75
87
  }
76
88
  }
77
89
  return null
78
90
  }
91
+ const hasSprite = (dir) => MOODS.some((mood) => pickFile(dir, mood) !== null)
92
+
93
+ const skinIds = readdirSync(assetsDir, { withFileTypes: true })
94
+ .filter((e) => e.isDirectory())
95
+ .map((e) => e.name)
96
+ .filter((name) => hasSprite(join(assetsDir, name)))
97
+ .sort((a, b) => skinRank(a) - skinRank(b) || a.localeCompare(b))
98
+ const legacySingleSkin = skinIds.length === 0 && hasSprite(assetsDir)
99
+ const skins = legacySingleSkin ? ['default'] : skinIds
100
+ if (skins.length === 0) {
101
+ console.error(`build: no sprites anywhere under ${assetsDir}`)
102
+ process.exit(1)
103
+ }
79
104
 
80
105
  const sprites = {}
81
106
  let total = 0
82
- for (const mood of MOODS) {
83
- const file = pickFile(mood)
84
- if (!file) {
85
- console.error(`build: no sprite for mood "${mood}" in ${assetsDir}`)
86
- process.exit(1)
107
+ for (const skin of skins) {
108
+ const dir = skin === 'default' ? assetsDir : join(assetsDir, skin)
109
+ sprites[skin] = {}
110
+ console.log(` ${skin}(${skinLabel(skin)})`)
111
+ for (const mood of MOODS) {
112
+ const file = pickFile(dir, mood)
113
+ if (!file) {
114
+ console.error(`build: skin "${skin}" has no sprite for mood "${mood}"`)
115
+ process.exit(1)
116
+ }
117
+ const bytes = readFileSync(file)
118
+ total += bytes.length
119
+ const ext = file.slice(file.lastIndexOf('.') + 1).toLowerCase()
120
+ const mime = ext === 'png' ? 'image/png' : 'image/webp'
121
+ sprites[skin][mood] = `data:${mime};base64,${bytes.toString('base64')}`
122
+ console.log(` ${mood.padEnd(7)} ${String(Math.round(bytes.length / 1024)).padStart(4)} KB <- ${file.slice(here.length + 1)}`)
87
123
  }
88
- const bytes = readFileSync(file)
89
- total += bytes.length
90
- const ext = file.slice(file.lastIndexOf('.') + 1).toLowerCase()
91
- const mime = ext === 'png' ? 'image/png' : 'image/webp'
92
- sprites[mood] = `data:${mime};base64,${bytes.toString('base64')}`
93
- console.log(` ${mood.padEnd(7)} ${String(Math.round(bytes.length / 1024)).padStart(4)} KB <- ${file.slice(here.length + 1)}`)
94
124
  }
95
125
 
96
126
  const PLACEHOLDER = '__SPRITES_LITERAL__'
@@ -105,7 +135,11 @@ if (placeholderCount !== 1) {
105
135
  process.exit(1)
106
136
  }
107
137
 
108
- const literal = '{\n' + MOODS.map((m) => ` ${m}: '${sprites[m]}',`).join('\n') + '\n }'
138
+ const literal = '{\n' + skins.map((skin) => (
139
+ ` ${JSON.stringify(skin)}: {\n`
140
+ + MOODS.map((m) => ` ${m}: '${sprites[skin][m]}',`).join('\n')
141
+ + '\n },'
142
+ )).join('\n') + '\n }'
109
143
  const out = template.replace(PLACEHOLDER, literal)
110
144
 
111
145
  if (out.includes(PLACEHOLDER)) {
@@ -146,10 +180,23 @@ if (!table || MOODS.some((m) => !new RegExp(`\\b${m}:\\s*'data:image/`).test(tab
146
180
  process.exit(1)
147
181
  }
148
182
  try {
149
- // Evaluate exactly that statement to be certain it is not commented out.
183
+ // Evaluate exactly that statement to be certain it is not commented out, and
184
+ // walk every skin/mood pair — a missing pair is a blank companion, not an error
185
+ // anyone would see at build time.
150
186
  const value = new Function(`${table[0]}\nreturn SPRITES`)()
151
- if (MOODS.some((m) => typeof value[m] !== 'string' || value[m].indexOf('data:image/') !== 0)) {
152
- console.error(`build: SPRITES evaluated to ${JSON.stringify(Object.keys(value))}; aborting`)
187
+ const broken = []
188
+ for (const skin of skins) {
189
+ for (const mood of MOODS) {
190
+ const uri = value?.[skin]?.[mood]
191
+ if (typeof uri !== 'string' || uri.indexOf('data:image/') !== 0) broken.push(`${skin}/${mood}`)
192
+ }
193
+ }
194
+ if (broken.length > 0) {
195
+ console.error(`build: SPRITES is missing ${broken.join(', ')}; aborting`)
196
+ process.exit(1)
197
+ }
198
+ if (Object.keys(value).length !== skins.length) {
199
+ console.error(`build: SPRITES holds ${Object.keys(value).length} skins, expected ${skins.length}; aborting`)
153
200
  process.exit(1)
154
201
  }
155
202
  } catch (error) {
@@ -164,6 +211,22 @@ console.log(`\n sprites total ${kb(total)} KB`)
164
211
  console.log(` base64 inlined ${kb(out.length)} KB`)
165
212
  console.log(` wrote lib/client.js`)
166
213
 
214
+ /* 体积与锐度是同一个决定的两面,所以把账算在构建输出里,而不是只写在文档里:
215
+ 她在屏上是 140*size x 210*size CSS px,需要多少设备像素只取决于当前屏幕的 DPR。 */
216
+ const SOURCE_WIDTH = { hi: 2048, full: 1024, md: 512, sm: 256 }[tier]
217
+ const drawnW = 140 * 4
218
+ const drawnH = 210 * 4
219
+ console.log(`\n tier ${tier} (${SOURCE_WIDTH}px wide per sprite)`)
220
+ console.log(` she is drawn ${drawnW}x${drawnH} CSS px (size 4)`)
221
+ for (const dpr of [1, 1.5, 2, 3]) {
222
+ const need = drawnW * dpr
223
+ const ratio = SOURCE_WIDTH / need
224
+ const verdict = ratio >= 1
225
+ ? `downscale ${ratio.toFixed(2)}x — crisp`
226
+ : `UPSCALE ${(1 / ratio).toFixed(2)}x — slightly soft`
227
+ console.log(` DPR ${String(dpr).padEnd(3)} needs ${String(Math.round(need)).padStart(4)}px → ${verdict}`)
228
+ }
229
+
167
230
  if (assetsDir !== join(here, 'assets') && readdirSync(assetsDir).length === 0) {
168
231
  console.warn('\n warning: assets directory is empty')
169
232
  }
package/cordis.patch.yml CHANGED
@@ -35,6 +35,8 @@
35
35
  # wander stroll around on her own while idle (default true)
36
36
  # wanderMs how often she considers a stroll (default 45000)
37
37
  # wanderRange how far one stroll may travel, in px (default 260)
38
+ # skin name a skin from assets/<skin>/ (default '' = last used)
39
+ # hidden start hidden behind a mini tab (default false)
38
40
  #
39
41
  # Example — add a `config:` block to the row below:
40
42
  #