@linxin666/dsh-pet 0.1.14 → 0.1.16

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 (90) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +73 -38
  3. package/README.zh.md +91 -55
  4. package/assets/whale/pet.json +12 -1
  5. package/cordis.patch.yml +4 -4
  6. package/lib/client.js +296 -347
  7. package/lib/client.js.map +1 -1
  8. package/lib/index.js +975 -317
  9. package/lib/invariant.js +1 -1
  10. package/lib/{state-P-wsVJ6O.js → state-CFyJv0sQ.js} +15 -1
  11. package/lib/types/affinity.d.ts +15 -0
  12. package/lib/types/affinity.d.ts.map +1 -1
  13. package/lib/types/affinity.js +14 -0
  14. package/lib/types/client/PetDockEntry.d.ts +14 -12
  15. package/lib/types/client/PetDockEntry.d.ts.map +1 -1
  16. package/lib/types/client/PetDockEntry.js +13 -8
  17. package/lib/types/client/PetSettingsCard.d.ts +22 -9
  18. package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
  19. package/lib/types/client/PetSettingsCard.js +42 -7
  20. package/lib/types/client/{WhalePet.d.ts → PetSprite.d.ts} +21 -20
  21. package/lib/types/client/PetSprite.d.ts.map +1 -0
  22. package/lib/types/client/{WhalePet.js → PetSprite.js} +44 -67
  23. package/lib/types/client/PluginSettingsCard.d.ts +26 -11
  24. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  25. package/lib/types/client/PluginSettingsCard.js +30 -8
  26. package/lib/types/client/index.d.ts +11 -8
  27. package/lib/types/client/index.d.ts.map +1 -1
  28. package/lib/types/client/index.js +32 -14
  29. package/lib/types/client/locales.d.ts +10 -10
  30. package/lib/types/client/locales.js +10 -10
  31. package/lib/types/client/pet-store.d.ts +5 -0
  32. package/lib/types/client/pet-store.d.ts.map +1 -1
  33. package/lib/types/client/pet-store.js +4 -0
  34. package/lib/types/client/settings-form.d.ts +76 -5
  35. package/lib/types/client/settings-form.d.ts.map +1 -1
  36. package/lib/types/client/settings-form.js +97 -20
  37. package/lib/types/client/spritesheet.d.ts +14 -48
  38. package/lib/types/client/spritesheet.d.ts.map +1 -1
  39. package/lib/types/client/spritesheet.js +13 -81
  40. package/lib/types/dsh-home.d.ts +17 -0
  41. package/lib/types/dsh-home.d.ts.map +1 -0
  42. package/lib/types/dsh-home.js +34 -0
  43. package/lib/types/event-projection.d.ts +36 -0
  44. package/lib/types/event-projection.d.ts.map +1 -0
  45. package/lib/types/event-projection.js +98 -0
  46. package/lib/types/index.d.ts +24 -11
  47. package/lib/types/index.d.ts.map +1 -1
  48. package/lib/types/index.js +53 -33
  49. package/lib/types/ledger.d.ts +79 -0
  50. package/lib/types/ledger.d.ts.map +1 -0
  51. package/lib/types/ledger.js +146 -0
  52. package/lib/types/persist.d.ts +15 -4
  53. package/lib/types/persist.d.ts.map +1 -1
  54. package/lib/types/persist.js +39 -8
  55. package/lib/types/registry.d.ts +158 -0
  56. package/lib/types/registry.d.ts.map +1 -0
  57. package/lib/types/registry.js +276 -0
  58. package/lib/types/routes.d.ts +10 -10
  59. package/lib/types/routes.d.ts.map +1 -1
  60. package/lib/types/routes.js +169 -51
  61. package/lib/types/service.d.ts +59 -57
  62. package/lib/types/service.d.ts.map +1 -1
  63. package/lib/types/service.js +111 -211
  64. package/package.json +2 -2
  65. package/src/affinity.ts +33 -0
  66. package/src/client/PetDockEntry.tsx +34 -26
  67. package/src/client/PetSettingsCard.tsx +67 -22
  68. package/src/client/{WhalePet.test.tsx → PetSprite.test.tsx} +56 -8
  69. package/src/client/{WhalePet.tsx → PetSprite.tsx} +55 -73
  70. package/src/client/PluginSettingsCard.tsx +90 -18
  71. package/src/client/index.ts +38 -16
  72. package/src/client/locales.ts +10 -10
  73. package/src/client/pet-store.ts +9 -0
  74. package/src/client/settings-card.module.css +9 -0
  75. package/src/client/settings-form.ts +153 -19
  76. package/src/client/spritesheet.ts +16 -92
  77. package/src/dsh-home.test.ts +35 -0
  78. package/src/dsh-home.ts +36 -0
  79. package/src/event-projection.ts +128 -0
  80. package/src/index.test.ts +23 -0
  81. package/src/index.ts +89 -45
  82. package/src/ledger.test.ts +69 -0
  83. package/src/ledger.ts +190 -0
  84. package/src/persist.test.ts +52 -3
  85. package/src/persist.ts +49 -11
  86. package/src/registry.test.ts +132 -0
  87. package/src/registry.ts +400 -0
  88. package/src/routes.ts +166 -53
  89. package/src/service.ts +153 -285
  90. package/lib/types/client/WhalePet.d.ts.map +0 -1
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Pet affinity economy (ledger) — composes the pure affinity and treats
3
+ * modules with the cooldown/dedup bookkeeping and emits updated persistence
4
+ * snapshots, marking dirty so the owning facade decides when to flush. Read
5
+ * paths (view) no longer settle the economy; settlements happen on explicit
6
+ * economic events: completed-turn rewards (official or legacy) and feeds.
7
+ * @module @linxin666/dsh-pet/ledger
8
+ */
9
+ import { applyInteraction, affinityViewOf, applyTurnReward, defaultAffinityConfig, } from "./affinity.js";
10
+ import { consumeTreat, defaultTreatConfig, settleTreatGrants, } from "./treats.js";
11
+ /**
12
+ * Holds the current persistence snapshot and all economy bookkeeping. Every
13
+ * mutating call flags takeDirty so the facade persists exactly once per
14
+ * batch of changes; read methods (snapshot, affinityView) never write.
15
+ */
16
+ export class PetLedger {
17
+ affinityConfig;
18
+ treatConfig;
19
+ current;
20
+ /** Completed turns already rewarded, per session (turn numbers are per-session). */
21
+ rewardedTurns = new Map();
22
+ lastLegacyTurnRewardAt = 0;
23
+ dirty = false;
24
+ constructor(persist, config = {}) {
25
+ this.affinityConfig = { ...defaultAffinityConfig, ...(config.affinity ?? {}) };
26
+ this.treatConfig = { ...defaultTreatConfig, ...(config.treats ?? {}) };
27
+ this.current = persist;
28
+ }
29
+ /** Affinity cooldown/rank tuning (read-only). */
30
+ get affinity() {
31
+ return this.affinityConfig;
32
+ }
33
+ /** The current persistence snapshot (trade a copy when mutating). */
34
+ get snapshot() {
35
+ return this.current;
36
+ }
37
+ /** Stock cap reported to clients. */
38
+ get treatMax() {
39
+ return this.treatConfig.maxTreats;
40
+ }
41
+ /** Consume the pending-write flag if any mutation occurred. */
42
+ takeDirty() {
43
+ const was = this.dirty;
44
+ this.dirty = false;
45
+ return was;
46
+ }
47
+ /** Replace the display block (clamping stays a caller concern). */
48
+ setDisplay(display) {
49
+ this.current = { ...this.current, display };
50
+ this.dirty = true;
51
+ }
52
+ /** Replace the selected pet id (validation stays a caller concern). */
53
+ setPetId(petId) {
54
+ if (this.current.petId === petId)
55
+ return;
56
+ this.current = { ...this.current, petId };
57
+ this.dirty = true;
58
+ }
59
+ /** Replace one pet's display name (validation stays a caller concern). */
60
+ setPetName(petId, name) {
61
+ this.current = { ...this.current, names: { ...this.current.names, [petId]: name } };
62
+ this.dirty = true;
63
+ }
64
+ /**
65
+ * Settle the treat economy (work + time output since the last settlement).
66
+ * A zero-gain first settlement still starts the time clock (anchor write),
67
+ * which is how the 30-minute time output can ever accrue. Returns true when
68
+ * the in-memory ledger changed and should be persisted.
69
+ */
70
+ settleTreats(nowMs) {
71
+ const settlement = settleTreatGrants(this.current.treats, this.current.affinity.turns, nowMs, this.treatConfig);
72
+ if (settlement.ledger === this.current.treats)
73
+ return false;
74
+ this.current = { ...this.current, treats: settlement.ledger };
75
+ this.dirty = true;
76
+ return true;
77
+ }
78
+ /**
79
+ * Award the completed-turn reward once per session+turn (idempotent) and
80
+ * run the treat settlement that work output feeds. Returns true when the
81
+ * snapshot changed.
82
+ */
83
+ rewardTurn(sessionId, turn, nowMs) {
84
+ const last = this.rewardedTurns.get(sessionId) ?? 0;
85
+ if (turn <= last)
86
+ return false;
87
+ this.rewardedTurns.set(sessionId, turn);
88
+ let changed = this.applyTurnReward();
89
+ if (this.settleTreats(nowMs))
90
+ changed = true;
91
+ return changed;
92
+ }
93
+ /** Preserve turn rewards for installations that only emit legacy activity. */
94
+ rewardLegacyTurn(nowMs) {
95
+ // A legacy done snapshot may repeat during the celebration window.
96
+ if (nowMs - this.lastLegacyTurnRewardAt < 5_000)
97
+ return false;
98
+ this.lastLegacyTurnRewardAt = nowMs;
99
+ let changed = this.applyTurnReward();
100
+ if (this.settleTreats(nowMs))
101
+ changed = true;
102
+ return changed;
103
+ }
104
+ applyTurnReward() {
105
+ this.current = {
106
+ ...this.current,
107
+ affinity: applyTurnReward(this.current.affinity, this.affinityConfig),
108
+ };
109
+ this.dirty = true;
110
+ return true;
111
+ }
112
+ /**
113
+ * Pet or feed the pet. Feeding settles first, then gates on the feed
114
+ * cooldown before spending stock — a feed inside the cooldown must not burn
115
+ * a treat for nothing.
116
+ */
117
+ interact(kind, nowMs) {
118
+ if (kind === 'feed')
119
+ this.settleTreats(nowMs);
120
+ const outcome = applyInteraction(this.current.affinity, kind, nowMs, this.affinityConfig);
121
+ if (kind === 'feed' && !outcome.accepted) {
122
+ return { reaction: outcome.reaction, delta: 0, affinity: this.affinityView(nowMs) };
123
+ }
124
+ if (kind === 'feed') {
125
+ const consume = consumeTreat(this.current.treats);
126
+ if (!consume.ok) {
127
+ return {
128
+ reaction: '没有小鱼干了,多陪我工作一会儿吧~',
129
+ delta: 0,
130
+ affinity: this.affinityView(nowMs),
131
+ };
132
+ }
133
+ this.current = { ...this.current, treats: consume.ledger };
134
+ this.dirty = true;
135
+ }
136
+ if (outcome.accepted) {
137
+ this.current = { ...this.current, affinity: outcome.affinity };
138
+ this.dirty = true;
139
+ }
140
+ return { reaction: outcome.reaction, delta: outcome.delta, affinity: this.affinityView(nowMs) };
141
+ }
142
+ /** Current affinity view for the RPC snapshot. */
143
+ affinityView(nowMs) {
144
+ return affinityViewOf(this.current.affinity, nowMs, this.affinityConfig);
145
+ }
146
+ }
@@ -24,19 +24,30 @@ export declare const DISPLAY_SIZE_MAX = 512;
24
24
  export declare const DISPLAY_INSET_MAX = 10000;
25
25
  /** Everything persisted for the pet. */
26
26
  export interface PetPersist {
27
- /** User-customizable pet display name. */
28
- name: string;
27
+ /** Selected pet id (a registry entry; clamped at service startup). */
28
+ petId: string;
29
+ /**
30
+ * Per-pet display names keyed by pet id. A pet without an entry falls back
31
+ * to its manifest displayName, so only user renames are stored here.
32
+ */
33
+ names: Record<string, string>;
29
34
  affinity: AffinityState;
30
35
  /** Treat (小鱼干) stock ledger. */
31
36
  treats: TreatLedger;
32
37
  display: PetDisplayConfig;
33
38
  }
34
- /** Default pet name (used until the user renames the pet). */
39
+ /** Pet id the legacy single-pet installs resolve to on migration. */
40
+ export declare const DEFAULT_PET_ID = "whale-girl";
41
+ /** Default pet name (used only when a manifest carries no displayName). */
35
42
  export declare const DEFAULT_PET_NAME = "\u9CB8\u9C7C\u5A18";
36
43
  /** Name constraints. */
37
44
  export declare const PET_NAME_MAX_LENGTH = 20;
38
45
  export declare function emptyPersist(): PetPersist;
39
- /** Resolve the persistence directory ($DSH_HOME or ~/.dsh). */
46
+ /**
47
+ * Resolve the persistence directory ($DSH_HOME or ~/.dsh). Delegates to the
48
+ * shared {@link dshHome} resolution so the plugin family keeps one DSH_HOME
49
+ * definition (env override, ~ expansion, cwd-joined relative values).
50
+ */
40
51
  export declare function petHomeDir(): string;
41
52
  /** Load persisted state; missing or corrupt files fall back to defaults. */
42
53
  export declare function loadPetPersist(dir?: string): PetPersist;
@@ -1 +1 @@
1
- {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAKlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAED,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,uBAAQ,CAAA;AAErC,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAOzC;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAYD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAwCrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAEpF,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAC/B,qBAAqB;IACrB,OAAO,EAAE,OAAO,CAAA;IAChB,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAA;IACZ,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,eAAO,MAAM,oBAAoB,EAAE,gBAKlC,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AACnC,eAAO,MAAM,iBAAiB,QAAS,CAAA;AAEvC,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,aAAa,CAAA;IACvB,gCAAgC;IAChC,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,gBAAgB,CAAA;CAC1B;AAED,qEAAqE;AACrE,eAAO,MAAM,cAAc,eAAe,CAAA;AAE1C,2EAA2E;AAC3E,eAAO,MAAM,gBAAgB,uBAAQ,CAAA;AAErC,wBAAwB;AACxB,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,wBAAgB,YAAY,IAAI,UAAU,CAQzC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AA4BD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAqB,GAAG,UAAU,CAiDrE;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,GAAE,MAAqB,GAAG,IAAI,CAMjF"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
8
8
  import { join } from 'node:path';
9
- import { homedir } from 'node:os';
9
+ import { dshHome } from "./dsh-home.js";
10
10
  import { AFFINITY_MAX, emptyAffinity } from "./affinity.js";
11
11
  import { defaultTreatConfig, emptyTreatLedger } from "./treats.js";
12
12
  export const defaultDisplayConfig = {
@@ -19,26 +19,48 @@ export const defaultDisplayConfig = {
19
19
  export const DISPLAY_SIZE_MIN = 32;
20
20
  export const DISPLAY_SIZE_MAX = 512;
21
21
  export const DISPLAY_INSET_MAX = 10_000;
22
- /** Default pet name (used until the user renames the pet). */
22
+ /** Pet id the legacy single-pet installs resolve to on migration. */
23
+ export const DEFAULT_PET_ID = 'whale-girl';
24
+ /** Default pet name (used only when a manifest carries no displayName). */
23
25
  export const DEFAULT_PET_NAME = '鲸鱼娘';
24
26
  /** Name constraints. */
25
27
  export const PET_NAME_MAX_LENGTH = 20;
26
28
  export function emptyPersist() {
27
29
  return {
28
- name: DEFAULT_PET_NAME,
30
+ petId: DEFAULT_PET_ID,
31
+ names: {},
29
32
  affinity: emptyAffinity(),
30
33
  treats: emptyTreatLedger(),
31
34
  display: { ...defaultDisplayConfig },
32
35
  };
33
36
  }
34
- /** Resolve the persistence directory ($DSH_HOME or ~/.dsh). */
37
+ /**
38
+ * Resolve the persistence directory ($DSH_HOME or ~/.dsh). Delegates to the
39
+ * shared {@link dshHome} resolution so the plugin family keeps one DSH_HOME
40
+ * definition (env override, ~ expansion, cwd-joined relative values).
41
+ */
35
42
  export function petHomeDir() {
36
- return process.env.DSH_HOME ?? join(homedir(), '.dsh');
43
+ return dshHome();
37
44
  }
38
45
  /** Numeric field guard: finite numbers only, else the fallback. */
39
46
  function finiteNum(value, fallback) {
40
47
  return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
41
48
  }
49
+ /** Sanitize the per-pet names map (string keys, non-empty trimmed values). */
50
+ function loadPetNames(parsed) {
51
+ const names = {};
52
+ if (typeof parsed.names !== 'object' || parsed.names === null)
53
+ return names;
54
+ for (const [id, value] of Object.entries(parsed.names)) {
55
+ if (id === '' || typeof value !== 'string')
56
+ continue;
57
+ const name = value.trim();
58
+ if (name === '')
59
+ continue;
60
+ names[id] = name.slice(0, PET_NAME_MAX_LENGTH);
61
+ }
62
+ return names;
63
+ }
42
64
  /** Clamp one count/score into [0, max]. */
43
65
  function clamp(value, max) {
44
66
  return Math.min(max, Math.max(0, value));
@@ -73,10 +95,19 @@ export function loadPetPersist(dir = petHomeDir()) {
73
95
  right: Math.round(clamp(finiteNum(rawDisplay.right, base.display.right), DISPLAY_INSET_MAX)),
74
96
  bottom: Math.round(clamp(finiteNum(rawDisplay.bottom, base.display.bottom), DISPLAY_INSET_MAX)),
75
97
  };
98
+ const petId = typeof parsed.petId === 'string' && parsed.petId.trim() !== ''
99
+ ? parsed.petId.trim()
100
+ : base.petId;
101
+ const names = loadPetNames(parsed);
102
+ // Legacy migration: pre-registry installs persisted one flat `name`
103
+ // field. Move it onto the selected pet (the legacy whale-girl unless the
104
+ // file already names another pet) so renames survive the upgrade.
105
+ if (typeof parsed.name === 'string' && parsed.name.trim() !== '' && names[petId] === undefined) {
106
+ names[petId] = parsed.name.trim().slice(0, PET_NAME_MAX_LENGTH);
107
+ }
76
108
  return {
77
- name: typeof parsed.name === 'string' && parsed.name.trim() !== ''
78
- ? parsed.name
79
- : base.name,
109
+ petId,
110
+ names,
80
111
  affinity,
81
112
  treats,
82
113
  display,
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Pet registry — the multi-pet contract. One pet is a directory holding a
3
+ * 'pet.json' manifest plus an atlas image; nothing else is required, and no
4
+ * host or client code changes when a pet is added. The registry scans three
5
+ * sources, later sources overriding earlier ones on an id collision:
6
+ *
7
+ * 1. the package's own 'assets' subdirectories (built-in pets);
8
+ * 2. '${CODEX_HOME:-~/.codex}/pets' subdirectories (hatch-pet custom pets);
9
+ * 3. 'PetConfig.pets' manifests composed by the embedding application
10
+ * (highest precedence).
11
+ *
12
+ * The manifest follows the Codex/hatch-pet contract (8 columns x 9 rows of
13
+ * 192x208 cells, the 9-state row order below). Legacy whale-girl manifests
14
+ * that only carry 'frames' keep working: geometry, per-row frame counts and
15
+ * per-track rhythm all fall back to the hatch-pet contract defaults, and the
16
+ * whale-girl manifest overrides its own durations.
17
+ * @module @linxin666/dsh-pet/registry
18
+ */
19
+ import type { PetAnimation } from './state.ts';
20
+ /** Fixed row order of the 9-state animation contract. */
21
+ export declare const PET_ROW_ORDER: readonly PetAnimation[];
22
+ /** Atlas cell size in px. */
23
+ export interface PetCell {
24
+ width: number;
25
+ height: number;
26
+ }
27
+ /** Atlas cell size in px (Codex/hatch-pet contract). */
28
+ export declare const DEFAULT_PET_CELL: PetCell;
29
+ /** Columns per row (max frames per track). */
30
+ export declare const DEFAULT_PET_COLUMNS = 8;
31
+ /** Rows in the atlas (fixed by the animation contract). */
32
+ export declare const DEFAULT_PET_ROW_COUNT = 9;
33
+ /**
34
+ * Per-row used-column counts from the hatch-pet contract table. Manifests
35
+ * that carry no 'frames' field (the Codex custom-pet shape) resolve here.
36
+ */
37
+ export declare const DEFAULT_FRAME_COUNTS: readonly number[];
38
+ /** Absolute package root, resolved from a module URL (lib/ or src/). */
39
+ export declare function petPackageRoot(importMetaUrl: string): string;
40
+ /** Resolve the hatch-pet custom pets directory (CODEX_HOME or ~/.codex). */
41
+ export declare function codexPetsDir(env?: NodeJS.ProcessEnv, home?: string): string;
42
+ /** One animation track as served to the browser half. */
43
+ export interface PetTrackDef {
44
+ /** Frame indices (columns) played in order. */
45
+ frames: number[];
46
+ /** Per-frame duration in ms; same length as frames. */
47
+ durations: number[];
48
+ /** Whether the track loops; a non-looping track holds its last frame. */
49
+ loop: boolean;
50
+ /** Track to switch to after a non-looping track finishes. */
51
+ fallback?: PetAnimation;
52
+ }
53
+ /** Default per-track rhythm (hatch-pet contract table). */
54
+ export declare const DEFAULT_TRACK_PATTERNS: Record<PetAnimation, {
55
+ durations: number[];
56
+ loop: boolean;
57
+ fallback?: PetAnimation;
58
+ }>;
59
+ /** Manifest shape a pet directory (or 'PetConfig.pets' entry) declares. */
60
+ export interface PetManifest {
61
+ /** Unique pet id, lowercase kebab-case. */
62
+ id: string;
63
+ /** Human-readable display name (settings selector, panel header). */
64
+ displayName: string;
65
+ /** One-line description. */
66
+ description?: string;
67
+ /** Atlas path relative to the manifest's directory. */
68
+ spritesheetPath: string;
69
+ /** Atlas cell size; defaults to the Codex contract 192x208. */
70
+ cell?: {
71
+ width?: number;
72
+ height?: number;
73
+ };
74
+ /** Columns per row; defaults to 8. */
75
+ columns?: number;
76
+ /**
77
+ * Per-row frame counts (9 entries, row order above). Manifests that omit
78
+ * it resolve the hatch-pet contract table.
79
+ */
80
+ frames?: number[];
81
+ /** Optional per-track rhythm overrides; omitted tracks use the defaults. */
82
+ tracks?: Partial<Record<PetAnimation, PetTrackOverride>>;
83
+ }
84
+ /** Per-track rhythm overrides a manifest may carry. */
85
+ export interface PetTrackOverride {
86
+ /** Per-frame durations in ms (cycled to the row's frame count). */
87
+ durations?: number[];
88
+ /** Whether the track loops. */
89
+ loop?: boolean;
90
+ /** Track to switch to after a non-looping track finishes. */
91
+ fallback?: PetAnimation;
92
+ }
93
+ /** A normalized pet as served to the browser half. */
94
+ export interface PetDefinition {
95
+ id: string;
96
+ displayName: string;
97
+ description: string;
98
+ /** Atlas cell size in px. */
99
+ cell: PetCell;
100
+ /** Columns per row. */
101
+ columns: number;
102
+ /** Per-row frame counts (length 9, row order above). */
103
+ rows: number[];
104
+ /** Fully resolved animation tracks (frames + durations + loop/fallback). */
105
+ tracks: Record<PetAnimation, PetTrackDef>;
106
+ /** Browser URL of the atlas (served by the host asset route). */
107
+ atlasUrl: string;
108
+ /** Browser URL of the manifest (served by the host asset route). */
109
+ manifestUrl: string;
110
+ }
111
+ /** A resolved pet plus its host-side file location. */
112
+ export interface PetEntry extends PetDefinition {
113
+ /** Absolute directory holding the manifest and atlas. */
114
+ dir: string;
115
+ /** Atlas path relative to 'dir' (declared by the manifest). */
116
+ spritesheetPath: string;
117
+ }
118
+ /** Registry load result: resolved entries plus load warnings. */
119
+ export interface PetRegistry {
120
+ entries: PetEntry[];
121
+ warnings: string[];
122
+ byId(id: string): PetEntry | undefined;
123
+ /** The pet an installation falls back to when the selection is unknown. */
124
+ defaultEntry(): PetEntry;
125
+ }
126
+ /** Registry sources. */
127
+ export interface PetRegistryOptions {
128
+ /** Absolute package root whose 'assets/*' hold built-in pets. */
129
+ packageRoot: string;
130
+ /** Asset route prefix the browser URLs are built under. */
131
+ assetPrefix?: string;
132
+ /** Custom pet directory (defaults to '${CODEX_HOME:-~/.codex}/pets'). */
133
+ petsDir?: string;
134
+ /** Extra manifest entries composed by the embedding application. */
135
+ extra?: readonly PetManifest[];
136
+ }
137
+ /**
138
+ * Normalize one parsed manifest into a renderable pet entry, or undefined
139
+ * (with a warning recorded) when the manifest violates the contract.
140
+ */
141
+ export declare function resolvePetManifest(raw: unknown, dir: string, options?: {
142
+ assetPrefix?: string;
143
+ warnings?: string[];
144
+ }): PetEntry | undefined;
145
+ /**
146
+ * Load the pet registry: built-in 'assets/*' first, then the hatch-pet
147
+ * custom pets directory, then composed 'extra' manifests (each later source
148
+ * overrides an earlier one on id collision). The registry never throws on a
149
+ * bad manifest: it skips it and records a warning.
150
+ */
151
+ export declare function loadPetRegistry(options: PetRegistryOptions): PetRegistry;
152
+ /** Strip host-only fields, leaving the client-visible definition. */
153
+ export declare function petEntryView(entry: PetEntry): PetDefinition;
154
+ /** The absolute file a pet's atlas resolves to (host asset route). */
155
+ export declare function petAtlasFile(entry: PetEntry): string;
156
+ /** The directory basename of one entry (legacy asset URL alias, e.g. whale). */
157
+ export declare function petDirAlias(entry: PetEntry): string;
158
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}