@linxin666/dsh-pet 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +65 -2
  3. package/README.zh.md +65 -2
  4. package/assets/decorations/whale/decoration.json +20 -0
  5. package/assets/decorations/whale/whale-frames.png +0 -0
  6. package/contracts/pet-manifest-v2.schema.json +281 -0
  7. package/contracts/status-decoration-v1.schema.json +144 -0
  8. package/contracts/voice-pack-v1.schema.json +234 -0
  9. package/lib/client.js +251 -43
  10. package/lib/client.js.map +1 -1
  11. package/lib/index.js +1139 -100
  12. package/lib/types/access.d.ts +16 -0
  13. package/lib/types/access.d.ts.map +1 -0
  14. package/lib/types/access.js +20 -0
  15. package/lib/types/chatter.d.ts +61 -3
  16. package/lib/types/chatter.d.ts.map +1 -1
  17. package/lib/types/chatter.js +71 -12
  18. package/lib/types/client/PetSettingsCard.d.ts +4 -0
  19. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  20. package/lib/types/client/PetSettingsCard.js +11 -3
  21. package/lib/types/client/PetSprite.d.ts.map +1 -1
  22. package/lib/types/client/PetSprite.js +111 -4
  23. package/lib/types/client/locales.d.ts +4 -0
  24. package/lib/types/client/locales.d.ts.map +1 -1
  25. package/lib/types/client/locales.js +4 -0
  26. package/lib/types/client/renderers/live2d/Live2dVisualMount.d.ts.map +1 -1
  27. package/lib/types/client/renderers/live2d/Live2dVisualMount.js +1 -0
  28. package/lib/types/client/renderers/live2d/runtime.d.ts +13 -1
  29. package/lib/types/client/renderers/live2d/runtime.d.ts.map +1 -1
  30. package/lib/types/client/renderers/live2d.d.ts.map +1 -1
  31. package/lib/types/client/renderers/live2d.js +133 -25
  32. package/lib/types/client/settings-form.d.ts.map +1 -1
  33. package/lib/types/client/settings-form.js +9 -6
  34. package/lib/types/contracts/status-decoration.d.ts +85 -0
  35. package/lib/types/contracts/status-decoration.d.ts.map +1 -0
  36. package/lib/types/contracts/status-decoration.js +21 -0
  37. package/lib/types/decoration.d.ts +39 -0
  38. package/lib/types/decoration.d.ts.map +1 -0
  39. package/lib/types/decoration.js +210 -0
  40. package/lib/types/event-projection.d.ts +8 -3
  41. package/lib/types/event-projection.d.ts.map +1 -1
  42. package/lib/types/event-projection.js +9 -4
  43. package/lib/types/image-dimensions.d.ts +26 -0
  44. package/lib/types/image-dimensions.d.ts.map +1 -0
  45. package/lib/types/image-dimensions.js +77 -0
  46. package/lib/types/index.d.ts +2 -0
  47. package/lib/types/index.d.ts.map +1 -1
  48. package/lib/types/index.js +3 -1
  49. package/lib/types/registry.d.ts +55 -2
  50. package/lib/types/registry.d.ts.map +1 -1
  51. package/lib/types/registry.js +240 -16
  52. package/lib/types/routes.d.ts +6 -1
  53. package/lib/types/routes.d.ts.map +1 -1
  54. package/lib/types/routes.js +163 -32
  55. package/lib/types/service.d.ts +33 -0
  56. package/lib/types/service.d.ts.map +1 -1
  57. package/lib/types/service.js +42 -3
  58. package/lib/types/voice-pack.d.ts +98 -0
  59. package/lib/types/voice-pack.d.ts.map +1 -0
  60. package/lib/types/voice-pack.js +384 -0
  61. package/package.json +11 -10
  62. package/src/access.ts +40 -0
  63. package/src/chatter.test.ts +89 -2
  64. package/src/chatter.ts +120 -14
  65. package/src/client/PetSettingsCard.tsx +26 -2
  66. package/src/client/PetSprite.test.tsx +325 -12
  67. package/src/client/PetSprite.tsx +150 -25
  68. package/src/client/locales.ts +4 -0
  69. package/src/client/renderers/live2d/Live2dVisualMount.test.tsx +72 -0
  70. package/src/client/renderers/live2d/Live2dVisualMount.tsx +1 -0
  71. package/src/client/renderers/live2d/runtime.ts +8 -2
  72. package/src/client/renderers/live2d.test.ts +233 -9
  73. package/src/client/renderers/live2d.ts +143 -30
  74. package/src/client/settings-form.ts +8 -6
  75. package/src/contracts/status-decoration.ts +78 -0
  76. package/src/decoration.test.ts +178 -0
  77. package/src/decoration.ts +220 -0
  78. package/src/event-projection.ts +10 -5
  79. package/src/image-dimensions.test.ts +85 -0
  80. package/src/image-dimensions.ts +76 -0
  81. package/src/index.ts +3 -1
  82. package/src/registry.test.ts +276 -0
  83. package/src/registry.ts +274 -15
  84. package/src/routes.ts +162 -33
  85. package/src/service.ts +59 -3
  86. package/src/voice-pack.test.ts +216 -0
  87. package/src/voice-pack.ts +413 -0
@@ -32,6 +32,8 @@ export const zh = {
32
32
  'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',
33
33
  'settings.enabled': '启用宠物',
34
34
  'settings.enabledHint': '关闭后隐藏宠物并停止轮询,可在设置里重新启用。',
35
+ 'settings.decoration': '状态装饰',
36
+ 'settings.decorationHint': '在宠物状态气泡里显示喷水鲸鱼等状态装饰;关闭后气泡只剩文字。',
35
37
  'settings.visible': '显示宠物',
36
38
  'settings.visibleHint': '关闭后宠物隐藏,可从聊天输入区重新召唤。',
37
39
  'settings.size': '大小(px)',
@@ -84,6 +86,8 @@ export const en = {
84
86
  'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',
85
87
  'settings.enabled': 'Enable the pet',
86
88
  'settings.enabledHint': 'When off, the pet hides and polling stops; re-enable it here.',
89
+ 'settings.decoration': 'Status decoration',
90
+ 'settings.decorationHint': 'Show ornaments like the spouting whale inside the pet status bubbles; when off, bubbles stay text-only.',
87
91
  'settings.visible': 'Show the pet',
88
92
  'settings.visibleHint': 'When off, the pet hides; summon it again from the input row.',
89
93
  'settings.size': 'Size (px)',
@@ -1 +1 @@
1
- {"version":3,"file":"Live2dVisualMount.d.ts","sourceRoot":"","sources":["../../../../../src/client/renderers/live2d/Live2dVisualMount.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAE1C,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,UAAU,EAAE,aAAa,CAAA;IACzB,KAAK,EAAE,aAAa,CAAA;IACpB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;CAC/B,GAAG,YAAY,CAuEf"}
1
+ {"version":3,"file":"Live2dVisualMount.d.ts","sourceRoot":"","sources":["../../../../../src/client/renderers/live2d/Live2dVisualMount.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAE1C,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,UAAU,EAAE,aAAa,CAAA;IACzB,KAAK,EAAE,aAAa,CAAA;IACpB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;CAC/B,GAAG,YAAY,CAwEf"}
@@ -20,6 +20,7 @@ export function Live2dVisualMount(props) {
20
20
  const [error, setError] = useState(null);
21
21
  // One activation per pet definition: build the contract context and mount.
22
22
  useEffect(() => {
23
+ setError(null);
23
24
  const container = containerRef.current;
24
25
  const live2d = props.definition.live2d;
25
26
  if (container === null || live2d === undefined)
@@ -21,12 +21,19 @@ export interface Live2dVendorApp {
21
21
  renderer: {
22
22
  readonly width: number;
23
23
  readonly height: number;
24
+ resize(width: number, height: number): void;
24
25
  };
25
26
  init(options: Record<string, unknown>): Promise<void>;
26
- destroy(removeView?: boolean, options?: Record<string, unknown>): void;
27
+ destroy(rendererOptions?: boolean | {
28
+ removeView?: boolean;
29
+ releaseGlobalResources?: boolean;
30
+ }, options?: Record<string, unknown>): void;
27
31
  }
28
32
  /** The Live2DModel slice the renderer uses. */
29
33
  export interface Live2dVendorModel {
34
+ automator: {
35
+ autoUpdate: boolean;
36
+ };
30
37
  anchor: {
31
38
  set(x: number, y?: number): void;
32
39
  };
@@ -50,6 +57,11 @@ export interface Live2dVendorModel {
50
57
  expression(name?: string): unknown;
51
58
  hitTest(x: number, y: number): string[];
52
59
  on(event: string, fn: () => void): unknown;
60
+ destroy(options?: {
61
+ children?: boolean;
62
+ texture?: boolean;
63
+ baseTexture?: boolean;
64
+ }): void;
53
65
  }
54
66
  /** The vendor bundle global (window.__dshPetLive2d). */
55
67
  export interface Live2dVendor {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/client/renderers/live2d/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,oDAAoD;AACpD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,iBAAiB,CAAA;IACzB,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAA;KAAE,CAAA;IAC5C,QAAQ,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CACvE;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QAAE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC5C,QAAQ,EAAE;QAAE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC7C,KAAK,EAAE;QAAE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE;YACR,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAA;YACnC,QAAQ,CAAC,EAAE,SAAS;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;SACxC,CAAA;KACF,CAAA;IACD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvD,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAClC,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IACvC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAA;CAC3C;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,UAAU,eAAe,CAAA;IACtC,UAAU,EAAE;QAAE,GAAG,CAAC,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;KAAE,CAAA;IAC9C,YAAY,EAAE,OAAO,CAAA;IACrB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC1D,WAAW,EAAE;QAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;KAAE,CAAA;CACrG;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,gBAAgB,CAAC,EAAE,OAAO,CAAA;QAC1B,cAAc,CAAC,EAAE,YAAY,CAAA;KAC9B;CACF;AAED,0EAA0E;AAC1E,KAAK,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAUpD,uDAAuD;AACvD,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB;AAKD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,OAAO,CAAC,CAWjF;AAED,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAAC,KAAK,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAWpG;AAED,gDAAgD;AAChD,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/client/renderers/live2d/runtime.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"live2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/live2d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAA;AASpC,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;IACpD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,CAAA;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,2EAA2E;IAC3E,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI,CAAA;CACzD;AAeD,kCAAkC;AAClC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAcD,0CAA0C;AAC1C,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,eAAe,CA+HvD,CAAA"}
1
+ {"version":3,"file":"live2d.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/live2d.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,iBAAiB,EACvB,MAAM,6BAA6B,CAAA;AASpC,gFAAgF;AAChF,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;IACpD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,sDAAsD;AACtD,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,CAAA;AAE/E,4EAA4E;AAC5E,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,2EAA2E;IAC3E,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,0EAA0E;IAC1E,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI,CAAA;CACzD;AA6DD,kCAAkC;AAClC,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAcD,0CAA0C;AAC1C,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,eAAe,CAkMvD,CAAA"}
@@ -24,6 +24,34 @@ import { PET_RENDERER_API_VERSION, } from '../../contracts/renderer.js';
24
24
  import { ensureCubismCore, ensureLive2dVendor, } from './live2d/runtime.js';
25
25
  /** The de-facto tap-motion group of Cubism sample models. */
26
26
  const TAP_GROUP = 'TapBody';
27
+ /**
28
+ * Keep one screen-appropriate atlas LOD instead of asking Pixi for the
29
+ * engine's default full mip chain. A user model can legitimately carry an
30
+ * 8192px texture while the pet itself is only a few hundred pixels tall;
31
+ * `single-auto` preserves the source for larger renders and generates one
32
+ * downsampled atlas only when the effective on-screen scale warrants it.
33
+ */
34
+ const TEXTURE_OPTIONS = { lod: 'single-auto' };
35
+ /** Recursively release the activation without invalidating shared texture caches. */
36
+ const DESTROY_OPTIONS = { children: true };
37
+ /** Remove only this activation's canvas; `true` would release Pixi globals. */
38
+ const RENDERER_DESTROY_OPTIONS = { removeView: true };
39
+ /** Ignore hidden/zero boxes and keep Pixi dimensions stable and integral. */
40
+ function normalizeRendererSize(width, height) {
41
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0)
42
+ return undefined;
43
+ return {
44
+ width: Math.max(1, Math.round(width)),
45
+ height: Math.max(1, Math.round(height)),
46
+ };
47
+ }
48
+ /** Fit the model from its unscaled dimensions into the current Pixi screen. */
49
+ function layoutModel(app, model, sourceSize, config) {
50
+ const fit = Math.min(app.renderer.width / sourceSize.width, app.renderer.height / sourceSize.height) * 0.92;
51
+ model.scale.set(fit * (config.scale ?? 1));
52
+ model.anchor.set(0.5);
53
+ model.position.set(app.renderer.width / 2 + (config.translate?.x ?? 0), app.renderer.height / 2 + (config.translate?.y ?? 0));
54
+ }
27
55
  let vendorConfigured = false;
28
56
  /** Configure pixi extensions + the Cubism SDK once per page. */
29
57
  function configureOnce(vendor) {
@@ -59,11 +87,65 @@ export const live2dRenderer = {
59
87
  let disposed = false;
60
88
  let app;
61
89
  let model;
90
+ let modelAttached = false;
91
+ let modelSourceSize;
92
+ let resizeObserver;
93
+ let resizeTracking = false;
62
94
  let errorListener;
63
95
  let unsubscribe;
64
96
  /** The motion group the current phase maps to (resume target after taps). */
65
97
  let phaseGroup = config.motions.idle;
66
98
  let tapPlaying = false;
99
+ const stopResizeTracking = () => {
100
+ resizeTracking = false;
101
+ resizeObserver?.disconnect();
102
+ resizeObserver = undefined;
103
+ };
104
+ const resizeRenderer = (pixiApp, width, height) => {
105
+ const next = normalizeRendererSize(width, height);
106
+ if (disposed || !resizeTracking || next === undefined)
107
+ return;
108
+ if (pixiApp.renderer.width === next.width && pixiApp.renderer.height === next.height)
109
+ return;
110
+ pixiApp.renderer.resize(next.width, next.height);
111
+ if (model !== undefined && modelSourceSize !== undefined) {
112
+ layoutModel(pixiApp, model, modelSourceSize, config);
113
+ }
114
+ };
115
+ const trackContainerSize = (pixiApp) => {
116
+ if (typeof ResizeObserver === 'undefined')
117
+ return;
118
+ resizeTracking = true;
119
+ resizeObserver = new ResizeObserver((entries) => {
120
+ const entry = entries.find(candidate => candidate.target === ctx.container);
121
+ if (entry === undefined)
122
+ return;
123
+ resizeRenderer(pixiApp, entry.contentRect.width, entry.contentRect.height);
124
+ });
125
+ resizeObserver.observe(ctx.container);
126
+ // Catch a synchronous layout change between init() and observe().
127
+ resizeRenderer(pixiApp, ctx.container.clientWidth, ctx.container.clientHeight);
128
+ };
129
+ /** Release every resource currently owned by this activation exactly once. */
130
+ const destroyResources = () => {
131
+ stopResizeTracking();
132
+ unsubscribe?.();
133
+ unsubscribe = undefined;
134
+ const currentApp = app;
135
+ const currentModel = model;
136
+ const modelOwnedByApp = currentApp !== undefined && modelAttached;
137
+ app = undefined;
138
+ model = undefined;
139
+ modelSourceSize = undefined;
140
+ modelAttached = false;
141
+ try {
142
+ if (currentModel !== undefined && !modelOwnedByApp)
143
+ currentModel.destroy(DESTROY_OPTIONS);
144
+ }
145
+ finally {
146
+ currentApp?.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
147
+ }
148
+ };
67
149
  const playGroup = (group) => {
68
150
  if (model === undefined)
69
151
  return;
@@ -99,36 +181,62 @@ export const live2dRenderer = {
99
181
  }
100
182
  configureOnce(vendor);
101
183
  const pixiApp = new vendor.Application();
102
- await pixiApp.init({
103
- width: Math.max(1, ctx.container.clientWidth || 160),
104
- height: Math.max(1, ctx.container.clientHeight || 174),
105
- backgroundAlpha: 0,
106
- antialias: true,
107
- autoDensity: true,
108
- preference: 'webgl',
109
- });
184
+ const initialSize = normalizeRendererSize(ctx.container.clientWidth, ctx.container.clientHeight) ?? {
185
+ width: 160,
186
+ height: 174,
187
+ };
188
+ try {
189
+ await pixiApp.init({
190
+ width: initialSize.width,
191
+ height: initialSize.height,
192
+ backgroundAlpha: 0,
193
+ antialias: true,
194
+ autoDensity: true,
195
+ preference: 'webgl',
196
+ });
197
+ }
198
+ catch (error) {
199
+ // init() can fail after allocating a partial renderer; cleanup is
200
+ // best-effort because Pixi may not consider that partial app ready.
201
+ try {
202
+ pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
203
+ }
204
+ catch { }
205
+ throw error;
206
+ }
110
207
  if (disposed) {
111
- pixiApp.destroy(true, { children: true });
208
+ pixiApp.destroy(RENDERER_DESTROY_OPTIONS, DESTROY_OPTIONS);
112
209
  return;
113
210
  }
211
+ app = pixiApp;
114
212
  pixiApp.canvas.style.display = 'block';
115
213
  pixiApp.canvas.style.width = '100%';
116
214
  pixiApp.canvas.style.height = '100%';
117
215
  ctx.container.appendChild(pixiApp.canvas);
118
- const loaded = await vendor.Live2DModel.from(config.modelUrl, { autoHitTest: false, autoFocus: false });
216
+ // Keep a model that rejects during setup off Ticker.shared; from()
217
+ // does not expose that partial instance to callers for disposal.
218
+ trackContainerSize(pixiApp);
219
+ const loaded = await vendor.Live2DModel.from(config.modelUrl, {
220
+ autoUpdate: false,
221
+ autoHitTest: false,
222
+ autoFocus: false,
223
+ textureOptions: TEXTURE_OPTIONS,
224
+ });
225
+ model = loaded;
119
226
  if (disposed) {
120
- pixiApp.destroy(true, { children: true });
227
+ destroyResources();
121
228
  return;
122
229
  }
123
- app = pixiApp;
124
- model = loaded;
230
+ modelSourceSize = {
231
+ width: Math.max(1, loaded.width),
232
+ height: Math.max(1, loaded.height),
233
+ };
125
234
  // Auto-fit the model into the container; the manifest scale multiplies
126
235
  // the fit and translate offsets from the center anchor.
127
- const fit = Math.min(pixiApp.renderer.width / Math.max(1, loaded.width), pixiApp.renderer.height / Math.max(1, loaded.height)) * 0.92;
128
- loaded.scale.set(fit * (config.scale ?? 1));
129
- loaded.anchor.set(0.5);
130
- loaded.position.set(pixiApp.renderer.width / 2 + (config.translate?.x ?? 0), pixiApp.renderer.height / 2 + (config.translate?.y ?? 0));
236
+ layoutModel(pixiApp, loaded, modelSourceSize, config);
131
237
  pixiApp.stage.addChild(loaded);
238
+ modelAttached = true;
239
+ loaded.automator.autoUpdate = true;
132
240
  // Resume the phase group once a tap motion finishes playing.
133
241
  loaded.on('motionFinish', () => {
134
242
  if (tapPlaying) {
@@ -140,20 +248,20 @@ export const live2dRenderer = {
140
248
  unsubscribe = ctx.phase.subscribe(applyPhase);
141
249
  };
142
250
  void boot().catch(() => {
143
- if (!disposed)
144
- errorListener?.('load-failed');
251
+ try {
252
+ destroyResources();
253
+ }
254
+ finally {
255
+ if (!disposed)
256
+ errorListener?.('load-failed');
257
+ }
145
258
  });
146
259
  return {
147
260
  dispose() {
148
261
  if (disposed)
149
262
  return;
150
263
  disposed = true;
151
- unsubscribe?.();
152
- model = undefined;
153
- if (app !== undefined) {
154
- app.destroy(true, { children: true });
155
- app = undefined;
156
- }
264
+ destroyResources();
157
265
  },
158
266
  tap(x, y) {
159
267
  const current = model;
@@ -1 +1 @@
1
- {"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}
@@ -184,9 +184,12 @@ export class CardForm {
184
184
  if (plan.length === 0 || this.saving || valid.length !== plan.length)
185
185
  return;
186
186
  const plannedWrites = valid.map(item => item.op);
187
- // Snapshot the fields this save writes, so edits staged while it is in
188
- // flight survive: only the staged keys this save actually wrote are cleared.
189
- const fields = new Set(plan.map(item => item.field));
187
+ // Snapshot the staged entries this save writes, so an edit staged while it
188
+ // is in flight (which replaces the same key) survives: only delete the key
189
+ // when the entry is still the one this save started from.
190
+ const pending = new Map();
191
+ for (const item of plan)
192
+ pending.set(item.field, this.staged.get(item.field));
190
193
  this.saving = true;
191
194
  this.failed = false;
192
195
  this.failedReason = undefined;
@@ -211,12 +214,12 @@ export class CardForm {
211
214
  landed.add(item.field);
212
215
  }
213
216
  }
214
- for (const field of fields) {
215
- if (landed.has(field))
217
+ for (const [field, before] of pending) {
218
+ if (landed.has(field) && this.staged.get(field) === before)
216
219
  this.staged.delete(field);
217
220
  }
218
221
  this.saving = false;
219
- this.failed = landed.size !== fields.size;
222
+ this.failed = landed.size !== pending.size;
220
223
  this.publish();
221
224
  }
222
225
  /** The scope's batch surface when it supports one; undefined conservatively otherwise. */
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Status-decoration contract — the L3 extension slot for small status
3
+ * ornaments (issue #623 milestone M5, protocol #567, first reference
4
+ * implementation #463). A decoration is an INDEPENDENT content entry (own
5
+ * id, own directory, own descriptor) whose PNG/WebP sprite strip ornaments
6
+ * the pet center's status bubble chrome; it never touches the pet
7
+ * manifests and never changes the bubble's semantics.
8
+ *
9
+ * Adopted disciplines (#623): entry assets are PNG/WebP sprite strips only
10
+ * (no SVG, no CSS animation); the bubble's own role=status/aria-live (or
11
+ * session-bubble button semantics) always stays intact and the ornament is
12
+ * aria-hidden; load failure or prefers-reduced-motion degrades to no
13
+ * ornament or the static first frame.
14
+ *
15
+ * The ActivityPhase stream the pet center owns drives the ornament: each
16
+ * phase binds to a frame segment (inclusive from/to indices into the
17
+ * strip) or to 'hide' (no ornament for that phase; the default).
18
+ * @module @linxin666/dsh-pet/contracts/status-decoration
19
+ */
20
+ import type { ActivityPhase } from '../state.ts';
21
+ /** Contract version decorations declare against (independent of manifests). */
22
+ export declare const PET_DECORATION_API_VERSION: 'x-org.linxin666.pet-center/status-decoration-v1';
23
+ /** One phase binding: a frame segment, or hidden. */
24
+ export type PhaseSegment = {
25
+ from: number;
26
+ to: number;
27
+ } | 'hide';
28
+ /** The ActivityPhase -> frame-segment binding table (unmapped phases hide). */
29
+ export type PhaseBindings = Partial<Record<ActivityPhase, PhaseSegment>>;
30
+ /** Normalized decoration descriptor as the registry consumes it. */
31
+ export interface DecorationManifest {
32
+ decorationManifestVersion: 1;
33
+ id: string;
34
+ displayName: string;
35
+ license: string;
36
+ /** Strip path relative to the descriptor directory (PNG/WebP). */
37
+ entry: string;
38
+ cell: {
39
+ width: number;
40
+ height: number;
41
+ };
42
+ columns: number;
43
+ /** Per-frame duration ms (same length as columns, or a single constant). */
44
+ durations: number[];
45
+ loop: boolean;
46
+ phases: PhaseBindings;
47
+ }
48
+ /** One structured diagnostic emitted while parsing a descriptor. */
49
+ export interface DecorationDiagnostic {
50
+ level: 'error' | 'warning';
51
+ message: string;
52
+ }
53
+ /** Parse outcome: a usable descriptor plus diagnostics, or rejection. */
54
+ export type DecorationManifestParse = {
55
+ ok: true;
56
+ manifest: DecorationManifest;
57
+ diagnostics: DecorationDiagnostic[];
58
+ } | {
59
+ ok: false;
60
+ diagnostics: DecorationDiagnostic[];
61
+ };
62
+ /**
63
+ * The decoration block the host serves inside the pet state view — exactly
64
+ * what the browser half needs to render the ornament, nothing more. The
65
+ * apiVersion rides the wire so a future protocol revision can be detected
66
+ * and negotiated by clients (review-spd follow-up, pet-center M5).
67
+ */
68
+ export interface DecorationView {
69
+ /** The protocol version this view speaks (PET_DECORATION_API_VERSION). */
70
+ apiVersion: typeof PET_DECORATION_API_VERSION;
71
+ id: string;
72
+ /** Same-origin URL prefix of the decoration's assets. */
73
+ assetBase: string;
74
+ /** Browser URL of the strip. */
75
+ entryUrl: string;
76
+ cell: {
77
+ width: number;
78
+ height: number;
79
+ };
80
+ columns: number;
81
+ durations: number[];
82
+ loop: boolean;
83
+ phases: PhaseBindings;
84
+ }
85
+ //# sourceMappingURL=status-decoration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-decoration.d.ts","sourceRoot":"","sources":["../../../src/contracts/status-decoration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,+EAA+E;AAC/E,eAAO,MAAM,0BAA0B,EAAG,iDAA0D,CAAA;AAEpG,qDAAqD;AACrD,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAA;AAEhE,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAA;AAExE,oEAAoE;AACpE,MAAM,WAAW,kBAAkB;IACjC,yBAAyB,EAAE,CAAC,CAAA;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,aAAa,CAAA;CACtB;AAED,oEAAoE;AACpE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,yEAAyE;AACzE,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAC;IAAC,WAAW,EAAE,oBAAoB,EAAE,CAAA;CAAE,GAC/E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,WAAW,EAAE,oBAAoB,EAAE,CAAA;CAAE,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,0EAA0E;IAC1E,UAAU,EAAE,OAAO,0BAA0B,CAAA;IAC7C,EAAE,EAAE,MAAM,CAAA;IACV,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,aAAa,CAAA;CACtB"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Status-decoration contract — the L3 extension slot for small status
3
+ * ornaments (issue #623 milestone M5, protocol #567, first reference
4
+ * implementation #463). A decoration is an INDEPENDENT content entry (own
5
+ * id, own directory, own descriptor) whose PNG/WebP sprite strip ornaments
6
+ * the pet center's status bubble chrome; it never touches the pet
7
+ * manifests and never changes the bubble's semantics.
8
+ *
9
+ * Adopted disciplines (#623): entry assets are PNG/WebP sprite strips only
10
+ * (no SVG, no CSS animation); the bubble's own role=status/aria-live (or
11
+ * session-bubble button semantics) always stays intact and the ornament is
12
+ * aria-hidden; load failure or prefers-reduced-motion degrades to no
13
+ * ornament or the static first frame.
14
+ *
15
+ * The ActivityPhase stream the pet center owns drives the ornament: each
16
+ * phase binds to a frame segment (inclusive from/to indices into the
17
+ * strip) or to 'hide' (no ornament for that phase; the default).
18
+ * @module @linxin666/dsh-pet/contracts/status-decoration
19
+ */
20
+ /** Contract version decorations declare against (independent of manifests). */
21
+ export const PET_DECORATION_API_VERSION = 'x-org.linxin666.pet-center/status-decoration-v1';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Status-decoration manifest v1 — fail-closed structure + warn-and-drop
3
+ * content, mirroring the pet manifest-v2 discipline (issue #623 M5,
4
+ * protocol #567). Unknown top-level fields, unsafe entry paths, out-of-
5
+ * range geometry and unknown renderer content reject the descriptor with
6
+ * human-readable diagnostics; per-phase binding issues drop that binding
7
+ * only. The JSON Schema twin lives at
8
+ * contracts/status-decoration-v1.schema.json; this hand-rolled parser is
9
+ * authoritative. Keep this file erasable-syntax-only (scripts/ import it
10
+ * under node's strip-only mode).
11
+ * @module @linxin666/dsh-pet/decoration
12
+ */
13
+ import type { DecorationManifestParse } from './contracts/status-decoration.ts';
14
+ /** Geometry and content caps (the adopted PNG/WebP sprite-strip bounds). */
15
+ export declare const DECORATION_CELL_MAX = 256;
16
+ export declare const DECORATION_COLUMNS_MAX = 16;
17
+ export declare const DECORATION_DURATION_MAX_MS = 2000;
18
+ export declare const DECORATION_ENTRY_EXTENSIONS: readonly ['.webp', '.png'];
19
+ export declare const DECORATION_DISPLAY_NAME_MAX = 64;
20
+ /** Field allow-list (drift-locked to the schema twin in tests). */
21
+ export declare const KNOWN_DECORATION_TOP_LEVEL: Set<string>;
22
+ /**
23
+ * Validate a descriptor-relative entry path: no absolute paths, no
24
+ * backslashes, no traversal, plain safe segments only, and an exact
25
+ * lowercase PNG/WebP extension (the adopted entry discipline — SVG/CSS are
26
+ * not accepted). The extension match is case-sensitive on purpose: the
27
+ * asset route serves the declared path verbatim, so a case-mismatched
28
+ * suffix (frames.PNG vs frames.png) would pass a lenient check but 403 on
29
+ * case-sensitive filesystems.
30
+ * Returns the normalized path or undefined.
31
+ */
32
+ export declare function safeDecorationEntry(raw: unknown): string | undefined;
33
+ /**
34
+ * Parse and validate one decoration.json document. Fail-closed over the
35
+ * structure (types, key sets, paths, ranges); phase-binding content issues
36
+ * drop that binding only (warn-and-drop, the registry never-throw rule).
37
+ */
38
+ export declare function parseDecorationManifest(raw: unknown, source?: string): DecorationManifestParse;
39
+ //# sourceMappingURL=decoration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decoration.d.ts","sourceRoot":"","sources":["../../src/decoration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAGV,uBAAuB,EAGxB,MAAM,kCAAkC,CAAA;AAEzC,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,MAAM,CAAA;AACtC,eAAO,MAAM,sBAAsB,KAAK,CAAA;AACxC,eAAO,MAAM,0BAA0B,OAAO,CAAA;AAC9C,eAAO,MAAM,2BAA2B,YAAI,OAAO,EAAE,MAAM,CAAU,CAAA;AACrE,eAAO,MAAM,2BAA2B,KAAK,CAAA;AAE7C,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,aAGrC,CAAA;AA6BF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAYpE;AAsBD;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,MAAM,GAAE,MAA0B,GACjC,uBAAuB,CAuGzB"}