@linxin666/dsh-pet 0.2.6 → 0.2.8

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 (66) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +1 -1
  3. package/README.zh.md +1 -1
  4. package/assets/whale/pet.json +54 -54
  5. package/assets/whale-refined/pet.json +96 -1
  6. package/lib/client.js +84 -14
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +62 -58
  9. package/lib/types/access.js +1 -1
  10. package/lib/types/affinity.d.ts +18 -18
  11. package/lib/types/affinity.d.ts.map +1 -1
  12. package/lib/types/affinity.js +1 -1
  13. package/lib/types/chatter.d.ts.map +1 -1
  14. package/lib/types/client/PetDockEntry.js +2 -2
  15. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  16. package/lib/types/client/PetSettingsCard.js +2 -2
  17. package/lib/types/client/PetSprite.d.ts.map +1 -1
  18. package/lib/types/client/PetSprite.js +27 -21
  19. package/lib/types/client/PluginSettingsCard.d.ts +1 -1
  20. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  21. package/lib/types/client/index.d.ts.map +1 -1
  22. package/lib/types/client/index.js +56 -9
  23. package/lib/types/client/locales.d.ts +100 -100
  24. package/lib/types/client/locales.d.ts.map +1 -1
  25. package/lib/types/client/renderers/PetRendererSwitch.js +2 -2
  26. package/lib/types/client/renderers/live2d/Live2dVisualMount.js +2 -2
  27. package/lib/types/client/renderers/live2d.js +2 -2
  28. package/lib/types/client/renderers/registry.d.ts.map +1 -1
  29. package/lib/types/client/settings-form.d.ts.map +1 -1
  30. package/lib/types/client/spritesheet.js +1 -1
  31. package/lib/types/client/ui-teardown.d.ts +29 -0
  32. package/lib/types/client/ui-teardown.d.ts.map +1 -0
  33. package/lib/types/client/ui-teardown.js +41 -0
  34. package/lib/types/contracts/status-decoration.d.ts +1 -1
  35. package/lib/types/decoration.d.ts +1 -1
  36. package/lib/types/decoration.d.ts.map +1 -1
  37. package/lib/types/decoration.js +1 -1
  38. package/lib/types/event-projection.js +1 -1
  39. package/lib/types/index.js +13 -13
  40. package/lib/types/invariant.js +2 -2
  41. package/lib/types/ledger.d.ts.map +1 -1
  42. package/lib/types/ledger.js +3 -3
  43. package/lib/types/manifest-v2.d.ts +1 -1
  44. package/lib/types/manifest-v2.d.ts.map +1 -1
  45. package/lib/types/model3.js +1 -1
  46. package/lib/types/persist.js +3 -3
  47. package/lib/types/registry.d.ts +5 -1
  48. package/lib/types/registry.d.ts.map +1 -1
  49. package/lib/types/registry.js +22 -18
  50. package/lib/types/remarks.d.ts.map +1 -1
  51. package/lib/types/routes.d.ts.map +1 -1
  52. package/lib/types/routes.js +4 -4
  53. package/lib/types/service.d.ts.map +1 -1
  54. package/lib/types/service.js +7 -7
  55. package/lib/types/state.d.ts.map +1 -1
  56. package/lib/types/voice-pack.d.ts +3 -3
  57. package/lib/types/voice-pack.d.ts.map +1 -1
  58. package/lib/types/voice-pack.js +1 -1
  59. package/package.json +12 -9
  60. package/src/client/PetSprite.test.tsx +168 -26
  61. package/src/client/PetSprite.tsx +21 -15
  62. package/src/client/index.test.tsx +118 -11
  63. package/src/client/index.ts +51 -3
  64. package/src/client/ui-teardown.ts +47 -0
  65. package/src/registry.test.ts +14 -0
  66. package/src/registry.ts +14 -10
@@ -30,14 +30,14 @@ import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, s
30
30
  import { homedir } from 'node:os';
31
31
  import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
32
32
  import { fileURLToPath } from 'node:url';
33
- import { normalizePetRemarks } from './remarks.js';
34
- import { mergeVoicePacks, normalizeVoicePack } from './voice-pack.js';
35
- import { parseDecorationManifest } from './decoration.js';
36
- import { imageDimensions } from './image-dimensions.js';
37
- import { PET_DECORATION_API_VERSION } from './contracts/status-decoration.js';
38
- import { dshHome } from './dsh-home.js';
39
- import { parsePetManifest } from './manifest-v2.js';
40
- import { collectModel3References } from './model3.js';
33
+ import { normalizePetRemarks } from "./remarks.js";
34
+ import { mergeVoicePacks, normalizeVoicePack } from "./voice-pack.js";
35
+ import { parseDecorationManifest } from "./decoration.js";
36
+ import { imageDimensions } from "./image-dimensions.js";
37
+ import { PET_DECORATION_API_VERSION } from "./contracts/status-decoration.js";
38
+ import { dshHome } from "./dsh-home.js";
39
+ import { parsePetManifest } from "./manifest-v2.js";
40
+ import { collectModel3References } from "./model3.js";
41
41
  /** Fixed row order of the 9-state animation contract. */
42
42
  export const PET_ROW_ORDER = [
43
43
  'idle',
@@ -86,17 +86,21 @@ function assetUrl(prefix, id, file) {
86
86
  const path = file.split('/').filter(segment => segment !== '').join('/');
87
87
  return prefix + '/' + encodeURIComponent(id) + '/' + path;
88
88
  }
89
- /** Default per-track rhythm (hatch-pet contract table). */
89
+ /**
90
+ * Default per-track rhythm — the shared slow baseline every sprite2d pet
91
+ * plays unless its manifest overrides a track (user request: all pets were
92
+ * too fast at the legacy hatch-pet contract pace).
93
+ */
90
94
  export const DEFAULT_TRACK_PATTERNS = {
91
- idle: { durations: [280, 110, 110, 140, 140, 320], loop: true },
92
- 'running-right': { durations: [120, 120, 120, 120, 120, 120, 120, 220], loop: true },
93
- 'running-left': { durations: [120, 120, 120, 120, 120, 120, 120, 220], loop: true },
94
- waving: { durations: [140, 140, 140, 280], loop: true },
95
- jumping: { durations: [140, 140, 140, 140, 280], loop: false, fallback: 'idle' },
96
- failed: { durations: [140, 140, 140, 140, 140, 140, 140, 240], loop: false, fallback: 'idle' },
97
- waiting: { durations: [150, 150, 150, 150, 150, 260], loop: true },
98
- running: { durations: [120, 120, 120, 120, 120, 220], loop: true },
99
- review: { durations: [150, 150, 150, 150, 150, 280], loop: true },
95
+ idle: { durations: [500, 500, 600, 500, 500, 600], loop: true },
96
+ 'running-right': { durations: [300, 300, 300, 300, 300, 300, 300, 400], loop: true },
97
+ 'running-left': { durations: [300, 300, 300, 300, 300, 300, 300, 400], loop: true },
98
+ waving: { durations: [450, 450, 450, 450], loop: true },
99
+ jumping: { durations: [400, 400, 400, 450, 450], loop: false, fallback: 'idle' },
100
+ failed: { durations: [550, 550, 550, 600, 650, 700, 550, 550], loop: false, fallback: 'idle' },
101
+ waiting: { durations: [550, 550, 600, 550, 550, 600], loop: true },
102
+ running: { durations: [330, 330, 330, 330, 330, 400], loop: true },
103
+ review: { durations: [650, 650, 650, 650, 650, 650], loop: true },
100
104
  };
101
105
  /** Stable id charset: keeps asset URLs plain and filesystem-safe. */
102
106
  const PET_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
@@ -1 +1 @@
1
- {"version":3,"file":"remarks.d.ts","sourceRoot":"","sources":["../../src/remarks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAAA;AAErF,4CAA4C;AAC5C,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAM7C,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAE9D,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;AAE/E,2EAA2E;AAC3E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC,CA2F3E,CAAA;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,UAAU,GAAG,SAAS,CAyBxB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;IAE7D,YAAY,SAAS,CAAC,EAAE,UAAU,EAMjC;IAED,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE,CAExC;IAED,gEAAgE;IAChE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAK7B;IAED,wFAAwF;IACxF,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9C;CACF"}
1
+ {"version":3,"file":"remarks.d.ts","sourceRoot":"","sources":["../../src/remarks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,CAAA;AAErF,4CAA4C;AAC5C,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAM7C,CAAA;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC,CAAA;AAE9D,yEAAyE;AACzE,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAAA;AAE/E,2EAA2E;AAC3E,eAAO,MAAM,eAAe,MAAM,CAAA;AAClC,sCAAsC;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC,CA2F3E,CAAA;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,UAAU,GAAG,SAAS,CAyBxB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuC;gBAEjD,SAAS,CAAC,EAAE,UAAU;IAQlC,qEAAqE;IACrE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE;IAIzC,gEAAgE;IAChE,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM;IAO9B,wFAAwF;IACxF,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;CAIhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAM9C,+CAA+C;AAC/C,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAMtC;;;;GAIG;AACH,eAAO,MAAM,cAAc;IACzB,yBAAyB;aACzB,QAAQ;IACR,iDAAiD;aACjD,KAAK;IACL,8EAA8E;aAC9E,KAAK;CACG,CAAA;AAEV,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAWD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUrF;AAsPD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAA8B,CAAA;AAe7D,4EAA4E;AAC5E,eAAO,MAAM,eAAe,QAAmB,CAAA;AAE/C,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwMD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,YAAY,CAAA;CAAE,GAAG,eAAe,GAAG,QAAQ,EAAE,CAwDjI;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAM9C,+CAA+C;AAC/C,eAAO,MAAM,cAAc,aAAa,CAAA;AAExC,0EAA0E;AAC1E,eAAO,MAAM,gBAAgB,SAAS,CAAA;AAMtC;;;;GAIG;AACH,eAAO,MAAM,cAAc;IACzB,yBAAyB;;IAEzB,iDAAiD;;IAEjD,8EAA8E;;CAEtE,CAAA;AAEV,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAWD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUrF;AAsPD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,QAA8B,CAAA;AAe7D,4EAA4E;AAC5E,eAAO,MAAM,eAAe,QAAmB,CAAA;AAE/C,6EAA6E;AAC7E,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwMD,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,YAAY,CAAA;CAAE,GAAG,eAAe,GAAG,QAAQ,EAAE,CAwDjI;AAGD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
@@ -14,9 +14,9 @@
14
14
  import { existsSync, realpathSync, statSync } from 'node:fs';
15
15
  import { readFile } from 'node:fs/promises';
16
16
  import { join, sep } from 'node:path';
17
- import { DECORATION_ASSET_PREFIX, petEntryView, petPackageRoot } from './registry.js';
18
- import { isPetAllowed } from './access.js';
19
- import { dshHome } from './dsh-home.js';
17
+ import { DECORATION_ASSET_PREFIX, petEntryView, petPackageRoot } from "./registry.js";
18
+ import { isPetAllowed } from "./access.js";
19
+ import { dshHome } from "./dsh-home.js";
20
20
  /** Browser-facing base path of the pet API. */
21
21
  export const PET_API_PREFIX = '/api/pet';
22
22
  /** Browser-facing base path of the pet asset routes ('/pet/<id>/...'). */
@@ -589,4 +589,4 @@ export function makePetRoutes(deps) {
589
589
  return [...apiRoutes, assetRoute, runtimeRoute, decorationRoute];
590
590
  }
591
591
  // Re-exported for the package surface (the registry owns the definition now).
592
- export { petPackageRoot } from './registry.js';
592
+ export { petPackageRoot } from "./registry.js";
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/service.ts"],"names":[],"mappings":"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"}
@@ -12,13 +12,13 @@
12
12
  * @module @linxin666/dsh-pet/service
13
13
  */
14
14
  import { Service } from '@deepseek-ai/cordis';
15
- import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from './event-projection.js';
16
- import { PetLedger } from './ledger.js';
17
- import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from './persist.js';
18
- import { DEFAULT_DECORATION_ID, decorationView, loadPetRegistry, petEntryView, petPackageRoot, } from './registry.js';
19
- import { WHISPER_TTL_MS } from './chatter.js';
20
- import { mergeVoicePacks } from './voice-pack.js';
21
- import { defaultPetStateConfig, PetStateMachine, } from './state.js';
15
+ import { emptyProjectionRuntime, isActivityPhase, projectOfficialEvent, } from "./event-projection.js";
16
+ import { PetLedger } from "./ledger.js";
17
+ import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
18
+ import { DEFAULT_DECORATION_ID, decorationView, loadPetRegistry, petEntryView, petPackageRoot, } from "./registry.js";
19
+ import { WHISPER_TTL_MS } from "./chatter.js";
20
+ import { mergeVoicePacks } from "./voice-pack.js";
21
+ import { defaultPetStateConfig, PetStateMachine, } from "./state.js";
22
22
  /** Settings namespace of the pet capability. Spelled here rather than imported: the browser half spells the same value. */
23
23
  export const PET_SETTINGS_NAMESPACE = 'pet';
24
24
  /** Hard cap on simultaneously displayed session bubbles (most recent first). */
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnG,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,eAAe,GACf,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,KAAK,EAAE,aAAa,CAAA;IACpB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAA;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAA;IACpB,0DAA0D;IAC1D,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAuD,CAAA;AAE3F;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAUpE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAarD;AAED;;;GAGG;AACH,qBAAa,eAAe;IAWxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,YACE,MAAM,GAAE,OAAO,CAAC,cAAc,CAAyB,EACtC,GAAG,GAAE,MAAM,MAAiB,EAG9C;IAED,6CAA6C;IAC7C,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAM3C;IAED,oEAAoE;IACpE,eAAe,IAAI,IAAI,CAEtB;IAED,sDAAsD;IACtD,iBAAiB,IAAI,IAAI,CAOxB;IAED,6CAA6C;IAC7C,MAAM,IAAI,gBAAgB,CAqBzB;CACF"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,kDAAkD;AAClD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEnG,0EAA0E;AAC1E,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,eAAe,GACf,cAAc,GACd,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,SAAS,GACT,SAAS,GACT,QAAQ,CAAA;AAEZ,kDAAkD;AAClD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,KAAK,EAAE,aAAa,CAAA;IACpB,+CAA+C;IAC/C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,qCAAqC;IACrC,SAAS,EAAE,YAAY,CAAA;IACvB,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oEAAoE;IACpE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,oEAAoE;IACpE,KAAK,EAAE,aAAa,CAAA;IACpB,0DAA0D;IAC1D,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,6BAA6B;AAC7B,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAuD,CAAA;AAE3F;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAUpE;AAED,yDAAyD;AACzD,wBAAgB,KAAK,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,CAarD;AAED;;;GAGG;AACH,qBAAa,eAAe;IAWxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,IAAI,CAAoB;IAChC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAGrC,MAAM,GAAE,OAAO,CAAC,cAAc,CAAyB,EACtC,GAAG,GAAE,MAAM,MAAiB;IAK/C,6CAA6C;IAC7C,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAQ5C,oEAAoE;IACpE,eAAe,IAAI,IAAI;IAIvB,sDAAsD;IACtD,iBAAiB,IAAI,IAAI;IASzB,6CAA6C;IAC7C,MAAM,IAAI,gBAAgB;CAsB3B"}
@@ -31,13 +31,13 @@ import { type VoicePackOverrides, type WhisperRule } from './chatter.ts';
31
31
  /** Schema version this module normalizes (optional field; missing = 1). */
32
32
  export declare const VOICE_PACK_V1: 1;
33
33
  /** Hover-panel action buttons a pack can show or hide (canonical order). */
34
- export declare const PANEL_ACTIONS: readonly ['feed', 'rename', 'hide'];
34
+ export declare const PANEL_ACTIONS: readonly ["feed", "rename", "hide"];
35
35
  export type PanelAction = (typeof PANEL_ACTIONS)[number];
36
36
  /** Panel label slots (unset slots keep the client's i18n dictionary copy). */
37
- export declare const PANEL_LABEL_KEYS: readonly ['feed', 'rename', 'hide', 'confirm'];
37
+ export declare const PANEL_LABEL_KEYS: readonly ["feed", "rename", "hide", "confirm"];
38
38
  export type PanelLabelKey = (typeof PANEL_LABEL_KEYS)[number];
39
39
  /** Panel stat slots ({rank}/{n}/{points} interpolate the live values). */
40
- export declare const PANEL_STAT_KEYS: readonly ['rank', 'treats', 'points'];
40
+ export declare const PANEL_STAT_KEYS: readonly ["rank", "treats", "points"];
41
41
  export type PanelStatKey = (typeof PANEL_STAT_KEYS)[number];
42
42
  /** Hover-panel chrome overrides served to the browser half. */
43
43
  export interface PetPanelView {
@@ -1 +1 @@
1
- {"version":3,"file":"voice-pack.d.ts","sourceRoot":"","sources":["../../src/voice-pack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"voice-pack.d.ts","sourceRoot":"","sources":["../../src/voice-pack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAA;AAErB,2EAA2E;AAC3E,eAAO,MAAM,aAAa,EAAG,CAAU,CAAA;AAEvC,4EAA4E;AAC5E,eAAO,MAAM,aAAa,qCAAsC,CAAA;AAChE,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,8EAA8E;AAC9E,eAAO,MAAM,gBAAgB,gDAAiD,CAAA;AAC9E,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,0EAA0E;AAC1E,eAAO,MAAM,eAAe,uCAAwC,CAAA;AACpE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;IAC/C,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAA;IAC7C,2EAA2E;IAC3E,OAAO,CAAC,EAAE,WAAW,EAAE,CAAA;CACxB;AAED,6EAA6E;AAC7E,MAAM,WAAW,SAAS;IACxB,yEAAyE;IACzE,SAAS,EAAE,kBAAkB,CAAA;IAC7B,sDAAsD;IACtD,KAAK,CAAC,EAAE,YAAY,CAAA;CACrB;AAED,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,eAAO,MAAM,cAAc,MAAM,CAAA;AACjC,eAAO,MAAM,2BAA2B,KAAK,CAAA;AAC7C,eAAO,MAAM,iBAAiB,KAAK,CAAA;AACnC,eAAO,MAAM,eAAe,KAAK,CAAA;AACjC,eAAO,MAAM,eAAe,KAAK,CAAA;AACjC,eAAO,MAAM,cAAc,KAAK,CAAA;AAYhC,KAAK,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,eAAe,GAAG,gBAAgB,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,CAAA;AA8B1G;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,QAAQ,EACd,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,MAAM,EAAE,GAAG,SAAS,CAoBtB;AAED,iFAAiF;AACjF,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,WAAW,EAAE,GAAG,SAAS,CAyC3B;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,YAAY,GAAG,SAAS,CA+D1B;AAED,8FAA8F;AAC9F,eAAO,MAAM,eAAe,aAAoG,CAAA;AAEhI,8DAA8D;AAC9D,eAAO,MAAM,YAAY,aAAgC,CAAA;AAEzD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,OAAO,EACZ,SAAS,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAe,GAC9C,SAAS,GAAG,SAAS,CAmFvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,MAAM,EAAE,CAAC,SAAS,GAAG,SAAS,CAAC,EAAE,GAAG,SAAS,GAAG,SAAS,CAsC3F"}
@@ -27,7 +27,7 @@
27
27
  * TypeScript mode: keep it erasable-syntax-only.
28
28
  * @module @linxin666/dsh-pet/voice-pack
29
29
  */
30
- import { STATUS_SCENES, TOOL_CATEGORIES, } from './chatter.js';
30
+ import { STATUS_SCENES, TOOL_CATEGORIES, } from "./chatter.js";
31
31
  /** Schema version this module normalizes (optional field; missing = 1). */
32
32
  export const VOICE_PACK_V1 = 1;
33
33
  /** Hover-panel action buttons a pack can show or hide (canonical order). */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-pet",
3
3
  "description": "Multi-pet companion plugin for the dsh web GUI: a registry-driven floating pet that reacts to model activity, with per-pet naming, petting/feeding interactions and an affinity score",
4
- "version": "0.2.6",
4
+ "version": "0.2.8",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -36,6 +36,9 @@
36
36
  ],
37
37
  "license": "Apache-2.0",
38
38
  "dsh": {
39
+ "engines": {
40
+ "dsh": ">=0.1.1-rc.1"
41
+ },
39
42
  "bundle": {
40
43
  "patch": "./cordis.patch.yml"
41
44
  },
@@ -59,14 +62,14 @@
59
62
  },
60
63
  "devDependencies": {
61
64
  "@deepseek-ai/cordis": "^4.0.1",
62
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
63
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
64
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.8",
65
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
66
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
67
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
68
- "@deepseek-ai/dsh-session": "^0.1.0-rc.8",
69
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
65
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
66
+ "@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
67
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.1-rc.2",
68
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
69
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
70
+ "@deepseek-ai/dsh-host-webserver": "^0.1.1-rc.2",
71
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
72
+ "@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
70
73
  "@testing-library/dom": "^10.4.1",
71
74
  "@testing-library/react": "^16.3.2",
72
75
  "@types/node": "^22.20.0",
@@ -687,7 +687,7 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
687
687
  expect(ornament()).toBeNull()
688
688
  })
689
689
 
690
- it('advances the ornament frames on the rAF loop and wraps when looping', () => {
690
+ it('advances the ornament frames on the duration timer and wraps when looping', () => {
691
691
  vi.spyOn(window, 'matchMedia').mockReturnValue({
692
692
  matches: false,
693
693
  media: '(prefers-reduced-motion: reduce)',
@@ -698,7 +698,17 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
698
698
  removeListener: () => {},
699
699
  dispatchEvent: () => false,
700
700
  })
701
- vi.spyOn(performance, 'now').mockReturnValue(0)
701
+ let now = 0
702
+ vi.spyOn(performance, 'now').mockImplementation(() => now)
703
+ // The ornament schedules by frame duration (setTimeout), the sprite
704
+ // still uses rAF; capture both so stepping advances both loops.
705
+ const timers: { at: number; callback: () => void }[] = []
706
+ let timerId = 0
707
+ vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
708
+ timers.push({ at: now + delay, callback })
709
+ return ++timerId
710
+ }) as typeof window.setTimeout)
711
+ vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
702
712
  const frames: FrameRequestCallback[] = []
703
713
  vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
704
714
  frames.push(callback)
@@ -707,18 +717,31 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
707
717
  vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
708
718
  renderPet({ snapshot: { ...snapshot, bubble: '正在思考', phase: 'thinking', decoration } })
709
719
  const el = ornament()!
710
- // Both the sprite loop and the ornament loop schedule frames; step every
711
- // pending callback together (the sprite's idle track never moves).
712
- const step = (ts: number): void => { for (const callback of frames.splice(0)) callback(ts) }
720
+ // Run the sprite rAF callbacks immediately (its idle track never moves);
721
+ // run ornament timers as the clock reaches them, repeatedly, because a
722
+ // due timer reschedules the next one at now + duration.
723
+ const step = (ms: number): void => {
724
+ for (const callback of frames.splice(0)) callback(now)
725
+ now += ms
726
+ for (;;) {
727
+ const due = timers.filter(t => t.at <= now)
728
+ if (due.length === 0) break
729
+ for (const t of due) {
730
+ const idx = timers.indexOf(t)
731
+ if (idx >= 0) timers.splice(idx, 1)
732
+ t.callback()
733
+ }
734
+ }
735
+ }
713
736
  // frameWidth = round(64 * 18 / 48) = 24 px; thinking binds frames 0..3.
714
737
  expect(el.style.backgroundPosition).toBe('0px 0px')
715
738
  act(() => { step(161) })
716
739
  expect(el.style.backgroundPosition).toBe('-24px 0px')
717
- act(() => { step(322) })
740
+ act(() => { step(161) })
718
741
  expect(el.style.backgroundPosition).toBe('-48px 0px')
719
- act(() => { step(483) })
742
+ act(() => { step(161) })
720
743
  expect(el.style.backgroundPosition).toBe('-72px 0px')
721
- act(() => { step(644) })
744
+ act(() => { step(161) })
722
745
  // The looping segment wraps back to its first frame.
723
746
  expect(el.style.backgroundPosition).toBe('0px 0px')
724
747
  })
@@ -734,7 +757,15 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
734
757
  removeListener: () => {},
735
758
  dispatchEvent: () => false,
736
759
  })
737
- vi.spyOn(performance, 'now').mockReturnValue(0)
760
+ let now = 0
761
+ vi.spyOn(performance, 'now').mockImplementation(() => now)
762
+ const timers: { at: number; callback: () => void }[] = []
763
+ let timerId = 0
764
+ vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
765
+ timers.push({ at: now + delay, callback })
766
+ return ++timerId
767
+ }) as typeof window.setTimeout)
768
+ vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
738
769
  const frames: FrameRequestCallback[] = []
739
770
  vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
740
771
  frames.push(callback)
@@ -743,13 +774,25 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
743
774
  vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
744
775
  renderPet({ snapshot: { ...snapshot, bubble: '完成', phase: 'done', decoration: { ...decoration, loop: false } } })
745
776
  const el = ornament()!
746
- const step = (ts: number): void => { for (const callback of frames.splice(0)) callback(ts) }
777
+ const step = (ms: number): void => {
778
+ for (const callback of frames.splice(0)) callback(now)
779
+ now += ms
780
+ for (;;) {
781
+ const due = timers.filter(t => t.at <= now)
782
+ if (due.length === 0) break
783
+ for (const t of due) {
784
+ const idx = timers.indexOf(t)
785
+ if (idx >= 0) timers.splice(idx, 1)
786
+ t.callback()
787
+ }
788
+ }
789
+ }
747
790
  // done binds frames 2..3; the segment starts on frame 2.
748
791
  expect(el.style.backgroundPosition).toBe('-48px 0px')
749
792
  act(() => { step(161) })
750
793
  expect(el.style.backgroundPosition).toBe('-72px 0px')
751
- // The ornament stopped scheduling (only the sprite loop remains pending).
752
- expect(frames).toHaveLength(1)
794
+ // The ornament stopped scheduling timers (only the sprite rAF remains).
795
+ expect(timers).toHaveLength(0)
753
796
  act(() => { step(161) })
754
797
  // The last frame holds.
755
798
  expect(el.style.backgroundPosition).toBe('-72px 0px')
@@ -767,6 +810,13 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
767
810
  dispatchEvent: () => false,
768
811
  })
769
812
  vi.spyOn(performance, 'now').mockReturnValue(0)
813
+ const timers: { at: number; callback: () => void }[] = []
814
+ let timerId = 0
815
+ vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
816
+ timers.push({ at: delay, callback })
817
+ return ++timerId
818
+ }) as typeof window.setTimeout)
819
+ vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
770
820
  const frames: FrameRequestCallback[] = []
771
821
  vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
772
822
  frames.push(callback)
@@ -784,15 +834,15 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
784
834
  })
785
835
  const el = ornament()!
786
836
  // The ornament settles on its only frame, exactly like the reduced-motion
787
- // hold — no rAF loop may start, so only the sprite's idle loop is pending.
837
+ // hold — no timer may start (only the sprite's idle rAF is pending).
788
838
  expect(el.style.backgroundPosition).toBe('-72px 0px')
789
- expect(frames).toHaveLength(1)
839
+ expect(timers).toHaveLength(0)
790
840
  const step = (ts: number): void => { for (const callback of frames.splice(0)) callback(ts) }
791
841
  act(() => { step(161) })
792
842
  act(() => { step(322) })
793
843
  // The frame never moves and the ornament never reschedules itself.
794
844
  expect(el.style.backgroundPosition).toBe('-72px 0px')
795
- expect(frames).toHaveLength(1)
845
+ expect(timers).toHaveLength(0)
796
846
  })
797
847
 
798
848
  it('does not advance the background while a frame is still in play', () => {
@@ -806,7 +856,15 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
806
856
  removeListener: () => {},
807
857
  dispatchEvent: () => false,
808
858
  })
809
- vi.spyOn(performance, 'now').mockReturnValue(0)
859
+ let now = 0
860
+ vi.spyOn(performance, 'now').mockImplementation(() => now)
861
+ const timers: { at: number; callback: () => void }[] = []
862
+ let timerId = 0
863
+ vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
864
+ timers.push({ at: now + delay, callback })
865
+ return ++timerId
866
+ }) as typeof window.setTimeout)
867
+ vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
810
868
  const frames: FrameRequestCallback[] = []
811
869
  vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
812
870
  frames.push(callback)
@@ -815,7 +873,19 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
815
873
  vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
816
874
  renderPet({ snapshot: { ...snapshot, bubble: '正在思考', phase: 'thinking', decoration } })
817
875
  const el = ornament()!
818
- const step = (ts: number): void => { for (const callback of frames.splice(0)) callback(ts) }
876
+ const step = (ms: number): void => {
877
+ for (const callback of frames.splice(0)) callback(now)
878
+ now += ms
879
+ for (;;) {
880
+ const due = timers.filter(t => t.at <= now)
881
+ if (due.length === 0) break
882
+ for (const t of due) {
883
+ const idx = timers.indexOf(t)
884
+ if (idx >= 0) timers.splice(idx, 1)
885
+ t.callback()
886
+ }
887
+ }
888
+ }
819
889
  // thinking binds frames 0..3 at 160 ms/frame. The effect holds frame 0;
820
890
  // a step at 80 ms — inside the first frame — must not move the ornament,
821
891
  // and only crossing the 160 ms boundary advances to the next frame.
@@ -826,6 +896,58 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
826
896
  expect(el.style.backgroundPosition).toBe('-24px 0px')
827
897
  })
828
898
 
899
+ it('catches up every due frame after a long idle gap', () => {
900
+ vi.spyOn(window, 'matchMedia').mockReturnValue({
901
+ matches: false,
902
+ media: '(prefers-reduced-motion: reduce)',
903
+ onchange: null,
904
+ addEventListener: () => {},
905
+ removeEventListener: () => {},
906
+ addListener: () => {},
907
+ removeListener: () => {},
908
+ dispatchEvent: () => false,
909
+ })
910
+ let now = 0
911
+ vi.spyOn(performance, 'now').mockImplementation(() => now)
912
+ const timers: { at: number; callback: () => void }[] = []
913
+ let timerId = 0
914
+ vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
915
+ timers.push({ at: now + delay, callback })
916
+ return ++timerId
917
+ }) as typeof window.setTimeout)
918
+ vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
919
+ const frames: FrameRequestCallback[] = []
920
+ vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
921
+ frames.push(callback)
922
+ return frames.length
923
+ })
924
+ vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
925
+ renderPet({ snapshot: { ...snapshot, bubble: '正在思考', phase: 'thinking', decoration } })
926
+ const el = ornament()!
927
+ const step = (ms: number): void => {
928
+ for (const callback of frames.splice(0)) callback(now)
929
+ now += ms
930
+ for (;;) {
931
+ const due = timers.filter(t => t.at <= now)
932
+ if (due.length === 0) break
933
+ for (const t of due) {
934
+ const idx = timers.indexOf(t)
935
+ if (idx >= 0) timers.splice(idx, 1)
936
+ t.callback()
937
+ }
938
+ }
939
+ }
940
+ // A 500 ms gap (jank / background tab) spans three 160 ms frames; the
941
+ // ornament must advance through all of them (0 -> 1 -> 2 -> 3), not
942
+ // drop the surplus time.
943
+ expect(el.style.backgroundPosition).toBe('0px 0px')
944
+ act(() => { step(500) })
945
+ expect(el.style.backgroundPosition).toBe('-72px 0px')
946
+ act(() => { step(161) })
947
+ // The next frame tick wraps the looping segment back to its first frame.
948
+ expect(el.style.backgroundPosition).toBe('0px 0px')
949
+ })
950
+
829
951
  it('does not restart the frame loop when an equal-content decoration re-renders', () => {
830
952
  vi.spyOn(window, 'matchMedia').mockReturnValue({
831
953
  matches: false,
@@ -837,21 +959,41 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
837
959
  removeListener: () => {},
838
960
  dispatchEvent: () => false,
839
961
  })
840
- vi.spyOn(performance, 'now').mockReturnValue(0)
962
+ let now = 0
963
+ vi.spyOn(performance, 'now').mockImplementation(() => now)
964
+ const timers: { at: number; callback: () => void }[] = []
965
+ let timerId = 0
966
+ const timerSpy = vi.spyOn(window, 'setTimeout').mockImplementation(((callback: () => void, delay = 0) => {
967
+ timers.push({ at: now + delay, callback })
968
+ return ++timerId
969
+ }) as typeof window.setTimeout)
970
+ const clearSpy = vi.spyOn(window, 'clearTimeout').mockImplementation(() => {})
841
971
  const frames: FrameRequestCallback[] = []
842
- const rafSpy = vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
972
+ vi.spyOn(window, 'requestAnimationFrame').mockImplementation(callback => {
843
973
  frames.push(callback)
844
974
  return frames.length
845
975
  })
846
- const cancelSpy = vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
976
+ vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
847
977
  // The definition comes from '/api/pet/pets', fetched once — a state
848
978
  // poll never replaces it, so both renders share one definition object.
849
979
  const definition = petDefinition()
850
980
  const { result } = renderPet({ definition, snapshot: { ...snapshot, bubble: '正在思考', phase: 'thinking', decoration } })
851
- const step = (ts: number): void => { for (const callback of frames.splice(0)) callback(ts) }
981
+ const step = (ms: number): void => {
982
+ for (const callback of frames.splice(0)) callback(now)
983
+ now += ms
984
+ for (;;) {
985
+ const due = timers.filter(t => t.at <= now)
986
+ if (due.length === 0) break
987
+ for (const t of due) {
988
+ const idx = timers.indexOf(t)
989
+ if (idx >= 0) timers.splice(idx, 1)
990
+ t.callback()
991
+ }
992
+ }
993
+ }
852
994
  act(() => { step(161) })
853
995
  expect(ornament()!.style.backgroundPosition).toBe('-24px 0px')
854
- const schedulesBefore = rafSpy.mock.calls.length
996
+ const schedulesBefore = timerSpy.mock.calls.length
855
997
  // The 2 s poll delivers a fresh JSON round-trip: identical content, new
856
998
  // object identities everywhere. The loop must not cancel/restart.
857
999
  const repolled: DecorationView = {
@@ -861,10 +1003,10 @@ describe('PetSprite status decoration (pet-center M5, #567)', () => {
861
1003
  phases: { ...decoration.phases },
862
1004
  }
863
1005
  result.rerender(<PetSprite {...petProps({ definition, snapshot: { ...snapshot, bubble: '正在思考', phase: 'thinking', decoration: repolled } })} />)
864
- act(() => { step(322) })
1006
+ act(() => { step(161) })
865
1007
  expect(ornament()!.style.backgroundPosition).toBe('-48px 0px')
866
- expect(cancelSpy).not.toHaveBeenCalled()
867
- // One reschedule per loop tick; no effect restart added new schedules.
868
- expect(rafSpy.mock.calls.length).toBe(schedulesBefore + 2)
1008
+ expect(clearSpy).not.toHaveBeenCalled()
1009
+ // One reschedule per frame tick; no effect restart added new timers.
1010
+ expect(timerSpy.mock.calls.length).toBe(schedulesBefore + 1)
869
1011
  })
870
1012
  })
@@ -98,32 +98,38 @@ function StatusOrnament(props: { decoration: DecorationView; phase: ActivityPhas
98
98
  // tick would keep rescheduling a no-op rAF forever. Settle on the one
99
99
  // frame instead — same as the reduced-motion static hold.
100
100
  if (reduceMotion || segment.from === segment.to) return
101
- let raf = 0
101
+ let timer = 0
102
102
  let index = segment.from
103
103
  let elapsed = 0
104
104
  let last = performance.now()
105
- const tick = (ts: number): void => {
106
- const delta = ts - last
107
- last = ts
105
+ const tick = (): void => {
106
+ const now = performance.now()
107
+ const delta = now - last
108
+ last = now
108
109
  elapsed += delta
109
- const duration = decoration.durations[index] ?? 160
110
+ const duration = decoration.durations[index] ?? 120
111
+ // The segment's frame rate (duration ms, typically 90-160) is far
112
+ // below the rAF cadence, so a 60fps loop would spend ~90% of its
113
+ // ticks doing nothing. Schedule by the remaining time to the next
114
+ // frame instead — the ornament wakes once per frame, not once per
115
+ // screen refresh. A late wake (background tab, jank) carries extra
116
+ // elapsed time, so catch up every due frame like the sprite loop.
110
117
  if (elapsed >= duration) {
111
- elapsed = 0
112
- if (index < segment.to) index += 1
113
- else if (decoration.loop) index = segment.from
114
- // Only advance the background when the frame actually changes:
115
- // the segment's frame rate (duration ms, typically 90-160) is far
116
- // below the rAF cadence, so writing the same position every frame
117
- // would churn style recalculations for no visual change.
118
+ do {
119
+ elapsed -= duration
120
+ if (index < segment.to) index += 1
121
+ else if (decoration.loop) index = segment.from
122
+ } while (elapsed >= duration)
123
+ // Only advance the background when the frame actually changes.
118
124
  el.style.backgroundPosition = position(index)
119
125
  }
120
126
  // A non-looping segment settles on its last frame; stop scheduling
121
127
  // instead of repainting the same position every frame.
122
128
  if (!decoration.loop && index === segment.to) return
123
- raf = requestAnimationFrame(tick)
129
+ timer = window.setTimeout(tick, Math.max(1, duration - elapsed))
124
130
  }
125
- raf = requestAnimationFrame(tick)
126
- return () => cancelAnimationFrame(raf)
131
+ timer = window.setTimeout(tick, 0)
132
+ return () => window.clearTimeout(timer)
127
133
  }, [shown, segmentKey, frameWidth, decoration.loop, durationsKey])
128
134
  if (!shown) return null
129
135
  return (