@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
@@ -1,40 +1,12 @@
1
1
  /**
2
- * Whale-girl spritesheet geometry and animation tracks.
3
- *
4
- * The atlas follows the Codex/hatch-pet contract: 8 columns × 9 rows of
5
- * 192×208 cells (1536×1872 total), rows in this order:
6
- * 0 idle, 1 running-right, 2 running-left, 3 waving, 4 jumping,
7
- * 5 failed, 6 waiting, 7 running, 8 review
8
- *
9
- * Frame counts and per-frame durations are per-track definitions below; the
10
- * whale-girl atlas is produced by the hatch-pet pipeline, so calibrate
11
- * `TRACKS` against the actual run (`pet_request.json` frame counts) when the
12
- * asset lands. Tracks that do not loop hand off to `fallback`.
2
+ * Spritesheet geometry helpers — parameterized by the pet definition the
3
+ * host serves over '/api/pet/pets', so the browser half renders any registry
4
+ * entry without per-pet code. The per-track tables (frames, durations, loop,
5
+ * fallback) also come from the registry; these helpers only place frames,
6
+ * guard track lengths, and map the fixed 9-row animation contract.
13
7
  * @module @linxin666/dsh-pet/client/spritesheet
14
8
  */
15
- /** Atlas cell size in px (Codex contract). */
16
- export const FRAME_WIDTH = 192;
17
- export const FRAME_HEIGHT = 208;
18
- /** Columns per row (max frames per track). */
19
- export const FRAME_COLUMNS = 8;
20
- /**
21
- * Track definitions for the whale-girl. Durations are tuned for a soft,
22
- * slow-healing feel (roughly 2.5× the earlier fast draft — the pet should
23
- * breathe, not race); calibrate frame counts against the hatch-pet run when
24
- * the asset lands (rows may carry 4–8 frames).
25
- */
26
- export const TRACKS = {
27
- idle: { frames: [0, 1, 2, 3, 4, 5], durations: [400, 400, 500, 400, 400, 500], loop: true },
28
- 'running-right': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },
29
- 'running-left': { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [225, 225, 225, 225, 225, 225, 225, 225], loop: true },
30
- waving: { frames: [0, 1, 2, 3], durations: [350, 350, 350, 350], loop: true },
31
- jumping: { frames: [0, 1, 2, 3, 4], durations: [300, 300, 300, 350, 350], loop: false, fallback: 'idle' },
32
- failed: { frames: [0, 1, 2, 3, 4, 5, 6, 7], durations: [450, 450, 450, 500, 550, 600, 450, 450], loop: false, fallback: 'idle' },
33
- waiting: { frames: [0, 1, 2, 3, 4, 5], durations: [450, 450, 500, 450, 450, 500], loop: true },
34
- running: { frames: [0, 1, 2, 3, 4, 5], durations: [250, 250, 250, 250, 250, 250], loop: true },
35
- review: { frames: [0, 1, 2, 3, 4, 5], durations: [550, 550, 550, 550, 550, 550], loop: true },
36
- };
37
- /** Row index of one animation track (mirrors state.ts rowOf). */
9
+ /** Row index of one animation track (the fixed 9-row contract). */
38
10
  export function rowOfTrack(animation) {
39
11
  const rows = {
40
12
  idle: 0,
@@ -56,61 +28,21 @@ export function rowOfTrack(animation) {
56
28
  * atlas coordinates here would drift each frame by the scale factor and
57
29
  * render torn/overlapping frames.
58
30
  */
59
- export function framePosition(row, col, scale = 1) {
60
- return { x: -col * FRAME_WIDTH * scale, y: -row * FRAME_HEIGHT * scale };
31
+ export function framePosition(cell, columns, row, col, scale = 1) {
32
+ return { x: -col * cell.width * scale, y: -row * cell.height * scale };
61
33
  }
62
34
  /** Total duration of one track, ms. */
63
35
  export function trackDuration(track) {
64
36
  return track.durations.reduce((sum, d) => sum + d, 0);
65
37
  }
66
38
  /**
67
- * Detect how many frames each row actually carries by scanning the decoded
68
- * atlas for non-transparent cells (hatch-pet rows may hold 4–8 frames; the
69
- * unused trailing cells are fully transparent). Rows whose every sample is
70
- * transparent report 0.
71
- * @param image - the fully decoded spritesheet (natural size 1536×1872).
72
- * @returns per-row frame counts, length 9.
73
- */
74
- export function detectFrameCounts(image) {
75
- const canvas = document.createElement('canvas');
76
- canvas.width = image.naturalWidth;
77
- canvas.height = image.naturalHeight;
78
- const ctx = canvas.getContext('2d');
79
- if (ctx === null)
80
- return Array.from({ length: 9 }, () => FRAME_COLUMNS);
81
- ctx.drawImage(image, 0, 0);
82
- const data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
83
- const counts = [];
84
- const stride = FRAME_COLUMNS * FRAME_WIDTH;
85
- const probeStep = 8;
86
- const margin = 12;
87
- for (let row = 0; row < 9; row++) {
88
- let count = 0;
89
- for (let col = 0; col < FRAME_COLUMNS; col++) {
90
- let hasContent = false;
91
- const x0 = col * FRAME_WIDTH;
92
- const y0 = row * FRAME_HEIGHT;
93
- for (let y = y0 + margin; y < y0 + FRAME_HEIGHT - margin && !hasContent; y += probeStep) {
94
- for (let x = x0 + margin; x < x0 + FRAME_WIDTH - margin && !hasContent; x += probeStep) {
95
- const idx = (y * stride + x) * 4;
96
- if ((data[idx + 3] ?? 0) > 8)
97
- hasContent = true;
98
- }
99
- }
100
- if (hasContent)
101
- count += 1;
102
- }
103
- counts.push(count);
104
- }
105
- return counts;
106
- }
107
- /**
108
- * Trim a track to the actual frame count of its row. A row with 0 detected
109
- * frames degrades to the first frame (the atlas is still loading or corrupt)
110
- * so the pet never renders blank.
39
+ * Trim a track to the actual frame count of its row (the manifest's per-row
40
+ * counts are authoritative; this is a last-line guard against a definition
41
+ * whose row count disagrees with its track table). A row with 0 detected
42
+ * frames degrades to the first frame so the pet never renders blank.
111
43
  */
112
44
  export function trimTrack(track, frameCount) {
113
- const n = Math.max(1, Math.min(frameCount, track.frames.length));
45
+ const n = Math.max(1, Math.min(frameCount, track.frames.length, track.durations.length));
114
46
  return {
115
47
  frames: track.frames.slice(0, n),
116
48
  durations: track.durations.slice(0, n),
@@ -0,0 +1,17 @@
1
+ /**
2
+ * DSH_HOME resolution shared by the plugin family's Host halves: the
3
+ * environment override wins, the platform home fallback follows. Mirrors
4
+ * what dsh-pet and dsh-liangshen each used to implement locally.
5
+ */
6
+ /** Expand a leading ~ (or ~user) in a path, platform-style. */
7
+ export declare function expandHome(path: string, home?: string): string;
8
+ /**
9
+ * Resolve the DSH home directory.
10
+ * @param env - process environment to read DSH_HOME from.
11
+ * @param home - platform home directory fallback (test seam).
12
+ * @returns the absolute DSH home path.
13
+ */
14
+ export declare function resolveDshHome(env?: NodeJS.ProcessEnv, home?: string): string;
15
+ /** Resolve the DSH home directory from the live environment. */
16
+ export declare function dshHome(): string;
17
+ //# sourceMappingURL=dsh-home.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsh-home.d.ts","sourceRoot":"","sources":["../../src/dsh-home.ts"],"names":[],"mappings":"AACA;;;;GAIG;AAKH,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAIzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAOrG;AAED,gEAAgE;AAChE,wBAAgB,OAAO,IAAI,MAAM,CAEhC"}
@@ -0,0 +1,34 @@
1
+ // Generated by scripts/sync-shared.mjs from shared/host/dsh-home.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
2
+ /**
3
+ * DSH_HOME resolution shared by the plugin family's Host halves: the
4
+ * environment override wins, the platform home fallback follows. Mirrors
5
+ * what dsh-pet and dsh-liangshen each used to implement locally.
6
+ */
7
+ import { homedir } from 'node:os';
8
+ import { isAbsolute, join } from 'node:path';
9
+ /** Expand a leading ~ (or ~user) in a path, platform-style. */
10
+ export function expandHome(path, home = homedir()) {
11
+ if (path === '~')
12
+ return home;
13
+ if (path.startsWith('~/') || path.startsWith('~\\'))
14
+ return join(home, path.slice(2));
15
+ return path;
16
+ }
17
+ /**
18
+ * Resolve the DSH home directory.
19
+ * @param env - process environment to read DSH_HOME from.
20
+ * @param home - platform home directory fallback (test seam).
21
+ * @returns the absolute DSH home path.
22
+ */
23
+ export function resolveDshHome(env = process.env, home = homedir()) {
24
+ const raw = env.DSH_HOME;
25
+ if (raw !== undefined && raw.trim() !== '') {
26
+ const expanded = expandHome(raw.trim(), home);
27
+ return isAbsolute(expanded) ? expanded : join(process.cwd(), expanded);
28
+ }
29
+ return join(home, '.dsh');
30
+ }
31
+ /** Resolve the DSH home directory from the live environment. */
32
+ export function dshHome() {
33
+ return resolveDshHome();
34
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Official session event projection — pure. Maps the durable DSH session
3
+ * vocabulary onto the pet's visual phases and carries an optional completed-
4
+ * turn reward for the ledger. Holds no state of its own; callers keep a
5
+ * {@link ProjectionRuntime} per session and feed events in arrival order.
6
+ * @module @linxin666/dsh-pet/event-projection
7
+ */
8
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
9
+ import type { PetStateInput } from './state.ts';
10
+ /** Runtime shape of the optional legacy activity event. */
11
+ export interface ActivityStatusEventLike {
12
+ phase?: string;
13
+ line?: string;
14
+ phrase?: string;
15
+ }
16
+ /** Per-session facts needed to project the official event stream. */
17
+ export interface ProjectionRuntime {
18
+ activeTools: Set<string>;
19
+ officialEventsSeen: boolean;
20
+ stepHadFailure: boolean;
21
+ }
22
+ /** One official event projection, optionally carrying a completed turn reward. */
23
+ export interface PetActivityTransition {
24
+ input: PetStateInput;
25
+ completedTurn?: number;
26
+ }
27
+ /** Fresh projection runtime for a newly seen session. */
28
+ export declare function emptyProjectionRuntime(): ProjectionRuntime;
29
+ /** Whether a legacy phase is part of the pet's supported vocabulary. */
30
+ export declare function isActivityPhase(phase: string): phase is PetStateInput['phase'];
31
+ /**
32
+ * Project the durable DSH session vocabulary into the pet's visual phases.
33
+ * Unknown and log-only events do not disturb the last meaningful activity.
34
+ */
35
+ export declare function projectOfficialEvent(event: SessionEvent, runtime: ProjectionRuntime): PetActivityTransition | undefined;
36
+ //# sourceMappingURL=event-projection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-projection.d.ts","sourceRoot":"","sources":["../../src/event-projection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,cAAc,EAAE,OAAO,CAAA;CACxB;AAED,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,aAAa,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,IAAI,iBAAiB,CAE1D;AAQD,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,aAAa,CAAC,OAAO,CAAC,CAE9E;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,iBAAiB,GACzB,qBAAqB,GAAG,SAAS,CAyEnC"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Official session event projection — pure. Maps the durable DSH session
3
+ * vocabulary onto the pet's visual phases and carries an optional completed-
4
+ * turn reward for the ledger. Holds no state of its own; callers keep a
5
+ * {@link ProjectionRuntime} per session and feed events in arrival order.
6
+ * @module @linxin666/dsh-pet/event-projection
7
+ */
8
+ /** Fresh projection runtime for a newly seen session. */
9
+ export function emptyProjectionRuntime() {
10
+ return { activeTools: new Set(), officialEventsSeen: false, stepHadFailure: false };
11
+ }
12
+ /** Keep tool names readable inside the compact status bubble. */
13
+ function displayToolName(name) {
14
+ const compact = name.replace(/\s+/g, ' ').trim() || '工具';
15
+ return compact.length <= 24 ? compact : `${compact.slice(0, 21)}...`;
16
+ }
17
+ /** Whether a legacy phase is part of the pet's supported vocabulary. */
18
+ export function isActivityPhase(phase) {
19
+ return ['idle', 'waiting', 'thinking', 'tool', 'review', 'done', 'failed'].includes(phase);
20
+ }
21
+ /**
22
+ * Project the durable DSH session vocabulary into the pet's visual phases.
23
+ * Unknown and log-only events do not disturb the last meaningful activity.
24
+ */
25
+ export function projectOfficialEvent(event, runtime) {
26
+ switch (event.type) {
27
+ case 'turn/start':
28
+ runtime.activeTools.clear();
29
+ runtime.stepHadFailure = false;
30
+ return { input: { phase: 'waiting', line: '准备开始' } };
31
+ case 'step/start':
32
+ runtime.activeTools.clear();
33
+ runtime.stepHadFailure = false;
34
+ return { input: { phase: 'waiting', line: '等待模型响应' } };
35
+ case 'assistant/chunk': {
36
+ const { chunk } = event.data;
37
+ if (chunk.type === 'reasoning-delta' && chunk.text.length > 0) {
38
+ return { input: { phase: 'thinking', line: '正在思考' } };
39
+ }
40
+ if (chunk.type === 'text-delta' && chunk.text.length > 0) {
41
+ return { input: { phase: 'review', line: '整理回复中' } };
42
+ }
43
+ return undefined;
44
+ }
45
+ case 'assistant/message':
46
+ return { input: { phase: 'review', line: '整理回复中' } };
47
+ case 'tool/call':
48
+ runtime.activeTools.add(String(event.data.callId));
49
+ return {
50
+ input: {
51
+ phase: 'tool',
52
+ line: `正在使用 ${displayToolName(event.data.name)}`,
53
+ },
54
+ };
55
+ case 'tool/result': {
56
+ const block = event.data.message.content[0];
57
+ runtime.activeTools.delete(String(event.data.message.source.callId));
58
+ runtime.stepHadFailure ||= event.data.error !== undefined || block.isError === true;
59
+ if (runtime.activeTools.size > 0) {
60
+ return {
61
+ input: {
62
+ phase: 'tool',
63
+ line: `还有 ${runtime.activeTools.size} 个工具运行中`,
64
+ },
65
+ };
66
+ }
67
+ return runtime.stepHadFailure
68
+ ? { input: { phase: 'failed', line: '工具执行失败' } }
69
+ : { input: { phase: 'thinking', line: '处理工具结果' } };
70
+ }
71
+ case 'turn/end': {
72
+ runtime.activeTools.clear();
73
+ switch (event.data.reason.kind) {
74
+ case 'completed':
75
+ return {
76
+ input: { phase: 'done', line: '完成啦' },
77
+ completedTurn: event.data.turn,
78
+ };
79
+ case 'error':
80
+ return { input: { phase: 'failed', line: '执行失败' } };
81
+ case 'max-tokens':
82
+ return { input: { phase: 'failed', line: '达到输出上限' } };
83
+ case 'interrupted':
84
+ return { input: { phase: 'failed', line: '执行意外中断' } };
85
+ case 'blocked':
86
+ return { input: { phase: 'waiting', line: '等待继续' } };
87
+ case 'aborted':
88
+ return { input: { phase: 'idle', line: '已停止' } };
89
+ default:
90
+ // TurnEndReasonMap is merge-extensible; a newer ending must not
91
+ // leave the pet showing stale in-progress work.
92
+ return { input: { phase: 'idle' } };
93
+ }
94
+ }
95
+ default:
96
+ return undefined;
97
+ }
98
+ }
@@ -1,43 +1,56 @@
1
1
  /**
2
2
  * dsh-pet host half — mounts the pet service and its HTTP routes. The
3
- * browser half (the `./client` entry) renders the whale-girl companion and
4
- * drives it through the same-origin `/api/pet/*` JSON endpoints plus the
5
- * `/pet/whale/*` media route. Install via `dsh plugin --profile web add
6
- * link:<dsh-web-ui>/packages/dsh-pet`; the cordis.patch.yml inserts this plugin row.
3
+ * browser half (the './client' entry) renders the selected pet and drives it
4
+ * through the same-origin '/api/pet/*' JSON endpoints plus the '/pet/<id>/*'
5
+ * media route. The host builds the multi-pet registry once at startup from
6
+ * the package assets, the hatch-pet custom pets directory, and composed
7
+ * config entries; adding a pet means dropping a manifest + atlas into one of
8
+ * those sources, never touching host or client code. Install via
9
+ * 'dsh plugin --profile web add link:<dsh-web-ui>/packages/dsh-pet'; the
10
+ * cordis.patch.yml inserts this plugin row.
7
11
  * @module @linxin666/dsh-pet
8
12
  */
9
13
  import { Context } from '@deepseek-ai/cordis';
10
14
  import z from 'schemastery';
11
15
  import { type PetConfig } from './service.ts';
12
16
  export { PetService } from './service.ts';
13
- export type { PetConfig, PetInteractResult, PetStateView, } from './service.ts';
17
+ export type { PetConfig, PetInteractResult, PetSettingsSection, PetStateView, } from './service.ts';
14
18
  export { AFFINITY_MAX, AFFINITY_RANKS, applyInteraction, applyTurnReward, emptyAffinity, rankOf, } from './affinity.ts';
15
19
  export type { AffinityConfig, AffinityState, InteractionOutcome, PetInteraction, } from './affinity.ts';
16
20
  export { animationForPhase, PetStateMachine, rowOf, } from './state.ts';
17
21
  export type { ActivityPhase, PetAnimation, PetStateConfig, PetStateInput, PetStateSnapshot, } from './state.ts';
18
22
  export { consumeTreat, defaultTreatConfig, emptyTreatLedger, settleTreatGrants, } from './treats.ts';
19
23
  export type { TreatConfig, TreatLedger, TreatSettlement } from './treats.ts';
20
- export { defaultDisplayConfig, emptyPersist, loadPetPersist, petHomeDir, savePetPersist, } from './persist.ts';
24
+ export { DEFAULT_PET_ID, DEFAULT_PET_NAME, PET_NAME_MAX_LENGTH, defaultDisplayConfig, emptyPersist, loadPetPersist, petHomeDir, savePetPersist, } from './persist.ts';
21
25
  export type { PetDisplayConfig, PetPersist } from './persist.ts';
22
- export { makePetRoutes, petPackageRoot, PET_API_PREFIX, PET_ASSET_PREFIX, } from './routes.ts';
26
+ export { DEFAULT_FRAME_COUNTS, DEFAULT_PET_CELL, DEFAULT_PET_COLUMNS, DEFAULT_PET_ROW_COUNT, DEFAULT_TRACK_PATTERNS, PET_ROW_ORDER, codexPetsDir, loadPetRegistry, petEntryView, petPackageRoot, resolvePetManifest, } from './registry.ts';
27
+ export type { PetDefinition, PetEntry, PetManifest, PetRegistry, PetRegistryOptions, PetTrackDef, PetTrackOverride, } from './registry.ts';
28
+ export { makePetRoutes, PET_API_PREFIX, PET_ASSET_PREFIX, } from './routes.ts';
23
29
  /** Stable cordis plugin name (matches cordis.patch.yml insert id). */
24
30
  export declare const name = "pet";
25
31
  /** Services required before the pet can mount its surfaces. */
26
32
  export declare const inject: string[];
27
- /** Settings section schema: the display fields and name the web settings surface edits. */
28
- export declare const PET_SETTINGS_SCHEMA: z<Schemastery.ObjectS<{
33
+ /**
34
+ * Settings section schema: pet selection and display fields the web settings
35
+ * surface edits. petId is a plain string on purpose: the service clamps the
36
+ * resolved value against the registry, so a stored selection that points at
37
+ * a removed pet cannot invalidate the section (a strict union would refuse
38
+ * the whole registration). The settings card renders the actual registry
39
+ * choices itself from '/api/pet/pets'.
40
+ */
41
+ export declare function makePetSettingsSchema(fallbackPetId: string): z<Schemastery.ObjectS<{
29
42
  visible: z<boolean, boolean>;
30
43
  size: z<number, number>;
31
44
  right: z<number, number>;
32
45
  bottom: z<number, number>;
33
- name: z<string, string>;
46
+ petId: z<string, string>;
34
47
  enabled: z<boolean, boolean>;
35
48
  }>, Schemastery.ObjectT<{
36
49
  visible: z<boolean, boolean>;
37
50
  size: z<number, number>;
38
51
  right: z<number, number>;
39
52
  bottom: z<number, number>;
40
- name: z<string, string>;
53
+ petId: z<string, string>;
41
54
  enabled: z<boolean, boolean>;
42
55
  }>>;
43
56
  /** Register the pet service and its API + asset routes on the context. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C,OAAO,CAAC,MAAM,aAAa,CAAA;AAC3B,OAAO,EAAsC,KAAK,SAAS,EAA2B,MAAM,cAAc,CAAA;AAU1G,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,MAAM,GACP,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,GACN,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEhE,OAAO,EACL,aAAa,EACb,cAAc,EACd,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAA;AAEpB,sEAAsE;AACtE,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAAgB,CAAA;AAEnC,2FAA2F;AAC3F,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;GAO9B,CAAA;AAEF,0EAA0E;AAC1E,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc,GAAG,IAAI,CAoDhE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C,OAAO,CAAC,MAAM,aAAa,CAAA;AAC3B,OAAO,EAAsC,KAAK,SAAS,EAA2B,MAAM,cAAc,CAAA;AAK1G,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,MAAM,GACP,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,GACN,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,GACjB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,UAAU,EACV,cAAc,GACf,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAChE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,cAAc,EACd,kBAAkB,GACnB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,aAAa,EACb,QAAQ,EACR,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,gBAAgB,GACjB,MAAM,eAAe,CAAA;AAEtB,OAAO,EACL,aAAa,EACb,cAAc,EACd,gBAAgB,GACjB,MAAM,aAAa,CAAA;AAEpB,sEAAsE;AACtE,eAAO,MAAM,IAAI,QAAQ,CAAA;AAEzB,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAAgB,CAAA;AAEnC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM;;;;;;;;;;;;;;IAS1D;AAED,0EAA0E;AAC1E,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,SAAc,GAAG,IAAI,CA+DhE"}
@@ -1,61 +1,81 @@
1
1
  /**
2
2
  * dsh-pet host half — mounts the pet service and its HTTP routes. The
3
- * browser half (the `./client` entry) renders the whale-girl companion and
4
- * drives it through the same-origin `/api/pet/*` JSON endpoints plus the
5
- * `/pet/whale/*` media route. Install via `dsh plugin --profile web add
6
- * link:<dsh-web-ui>/packages/dsh-pet`; the cordis.patch.yml inserts this plugin row.
3
+ * browser half (the './client' entry) renders the selected pet and drives it
4
+ * through the same-origin '/api/pet/*' JSON endpoints plus the '/pet/<id>/*'
5
+ * media route. The host builds the multi-pet registry once at startup from
6
+ * the package assets, the hatch-pet custom pets directory, and composed
7
+ * config entries; adding a pet means dropping a manifest + atlas into one of
8
+ * those sources, never touching host or client code. Install via
9
+ * 'dsh plugin --profile web add link:<dsh-web-ui>/packages/dsh-pet'; the
10
+ * cordis.patch.yml inserts this plugin row.
7
11
  * @module @linxin666/dsh-pet
8
12
  */
9
13
  import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
10
14
  import z from 'schemastery';
11
15
  import { PetService, PET_SETTINGS_NAMESPACE } from "./service.js";
12
- import { makePetRoutes, petPackageRoot } from "./routes.js";
13
- import { DEFAULT_PET_NAME, DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN, PET_NAME_MAX_LENGTH, } from "./persist.js";
16
+ import { makePetRoutes } from "./routes.js";
17
+ import { loadPetRegistry, petPackageRoot } from "./registry.js";
18
+ import { DISPLAY_INSET_MAX, DISPLAY_SIZE_MAX, DISPLAY_SIZE_MIN } from "./persist.js";
14
19
  export { PetService } from "./service.js";
15
20
  export { AFFINITY_MAX, AFFINITY_RANKS, applyInteraction, applyTurnReward, emptyAffinity, rankOf, } from "./affinity.js";
16
21
  export { animationForPhase, PetStateMachine, rowOf, } from "./state.js";
17
22
  export { consumeTreat, defaultTreatConfig, emptyTreatLedger, settleTreatGrants, } from "./treats.js";
18
- export { defaultDisplayConfig, emptyPersist, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
19
- export { makePetRoutes, petPackageRoot, PET_API_PREFIX, PET_ASSET_PREFIX, } from "./routes.js";
23
+ export { DEFAULT_PET_ID, DEFAULT_PET_NAME, PET_NAME_MAX_LENGTH, defaultDisplayConfig, emptyPersist, loadPetPersist, petHomeDir, savePetPersist, } from "./persist.js";
24
+ export { DEFAULT_FRAME_COUNTS, DEFAULT_PET_CELL, DEFAULT_PET_COLUMNS, DEFAULT_PET_ROW_COUNT, DEFAULT_TRACK_PATTERNS, PET_ROW_ORDER, codexPetsDir, loadPetRegistry, petEntryView, petPackageRoot, resolvePetManifest, } from "./registry.js";
25
+ export { makePetRoutes, PET_API_PREFIX, PET_ASSET_PREFIX, } from "./routes.js";
20
26
  /** Stable cordis plugin name (matches cordis.patch.yml insert id). */
21
27
  export const name = 'pet';
22
28
  /** Services required before the pet can mount its surfaces. */
23
29
  export const inject = ['webServer'];
24
- /** Settings section schema: the display fields and name the web settings surface edits. */
25
- export const PET_SETTINGS_SCHEMA = z.object({
26
- visible: z.boolean().default(true),
27
- size: z.number().step(1).min(DISPLAY_SIZE_MIN).max(DISPLAY_SIZE_MAX).default(160),
28
- right: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(24),
29
- bottom: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(20),
30
- name: z.string().min(1).max(PET_NAME_MAX_LENGTH).pattern(/\S/).default(DEFAULT_PET_NAME),
31
- enabled: z.boolean().default(true),
32
- });
30
+ /**
31
+ * Settings section schema: pet selection and display fields the web settings
32
+ * surface edits. petId is a plain string on purpose: the service clamps the
33
+ * resolved value against the registry, so a stored selection that points at
34
+ * a removed pet cannot invalidate the section (a strict union would refuse
35
+ * the whole registration). The settings card renders the actual registry
36
+ * choices itself from '/api/pet/pets'.
37
+ */
38
+ export function makePetSettingsSchema(fallbackPetId) {
39
+ return z.object({
40
+ visible: z.boolean().default(true),
41
+ size: z.number().step(1).min(DISPLAY_SIZE_MIN).max(DISPLAY_SIZE_MAX).default(160),
42
+ right: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(24),
43
+ bottom: z.number().step(1).min(0).max(DISPLAY_INSET_MAX).default(20),
44
+ petId: z.string().default(fallbackPetId),
45
+ enabled: z.boolean().default(true),
46
+ });
47
+ }
33
48
  /** Register the pet service and its API + asset routes on the context. */
34
49
  export function apply(ctx, config = {}) {
35
- const service = new PetService(ctx, config);
36
- // The settings surface edits the display config through the `pet`
37
- // namespace. The composition `base` starts as the persisted pet.json
38
- // values (clamped to schema bounds), so an empty user layer resolves to
39
- // exactly what the pet already shows — a fresh deployment never
40
- // overwrites a customized layout, and reset re-inherits it. Runtime drag
41
- // interactions mirror back into the settings document through the service
42
- // (see syncSettingsFromPet), keeping both views consistent.
50
+ const registry = config.registry
51
+ ?? loadPetRegistry({
52
+ packageRoot: petPackageRoot(import.meta.url),
53
+ ...(config.pets === undefined ? {} : { extra: config.pets }),
54
+ });
55
+ const service = new PetService(ctx, { ...config, registry });
56
+ // The settings surface edits the pet selection + display config through
57
+ // the 'pet' namespace. The composition 'base' starts as the persisted
58
+ // pet.json values (clamped to schema bounds), so an empty user layer
59
+ // resolves to exactly what the pet already shows — a fresh deployment
60
+ // never overwrites a customized layout, and reset re-inherits it. Runtime
61
+ // drag interactions mirror back into the settings document through the
62
+ // service (see syncSettingsFromPet), keeping both views consistent.
43
63
  let current = () => base;
44
64
  const base = {
45
65
  visible: service.display().visible,
46
66
  size: service.display().size,
47
67
  right: service.display().right,
48
68
  bottom: service.display().bottom,
49
- name: service.petName(),
69
+ petId: service.selectedPetId(),
50
70
  enabled: config.enabled ?? true,
51
71
  };
52
72
  // The browser half talks to the pet through same-origin JSON endpoints and
53
- // loads the atlas from the pet's own media route (RPC domains are
54
- // platform-registered, so the pet serves its own API — the same pattern as
55
- // dsh-remote-web-ui's /api/pair family). The routes are registered while
56
- // the plugin is enabled; toggling the setting off makes the pet API
57
- // disappear until it is re-enabled.
58
- const routes = makePetRoutes({ service, packageRoot: petPackageRoot(import.meta.url) });
73
+ // loads each pet's atlas from the registry's own media route (RPC domains
74
+ // are platform-registered, so the pet serves its own API — the same
75
+ // pattern as dsh-remote-web-ui's /api/pair family). The routes are
76
+ // registered while the plugin is enabled; toggling the setting off makes
77
+ // the pet API disappear until it is re-enabled.
78
+ const routes = makePetRoutes({ service });
59
79
  let disposeRoutes;
60
80
  const syncRoutes = () => {
61
81
  const enabled = current().enabled ?? true;
@@ -71,7 +91,7 @@ export function apply(ctx, config = {}) {
71
91
  disposeRoutes = undefined;
72
92
  }
73
93
  };
74
- installSettingsSection(ctx, settingsNamespace(PET_SETTINGS_NAMESPACE), PET_SETTINGS_SCHEMA, base, {
94
+ installSettingsSection(ctx, settingsNamespace(PET_SETTINGS_NAMESPACE), makePetSettingsSchema(service.selectedPetId()), base, {
75
95
  setSource: (source) => { current = source; },
76
96
  onChange: () => {
77
97
  const section = current();
@@ -0,0 +1,79 @@
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 { type AffinityConfig, type PetAffinityView, type PetInteraction } from './affinity.ts';
10
+ import { type TreatConfig } from './treats.ts';
11
+ import type { PetDisplayConfig, PetPersist } from './persist.ts';
12
+ /** Tuning overrides for the affinity economy. */
13
+ export interface LedgerConfig {
14
+ affinity?: Partial<AffinityConfig>;
15
+ treats?: Partial<TreatConfig>;
16
+ }
17
+ /** Result of one ledger interaction (the shape the pet RPC returns). */
18
+ export interface LedgerInteractionResult {
19
+ /** Reaction copy bubble. */
20
+ reaction: string;
21
+ /** Points gained (0 when inside the cooldown). */
22
+ delta: number;
23
+ /** Full affinity snapshot (same shape as the state view). */
24
+ affinity: PetAffinityView;
25
+ }
26
+ /**
27
+ * Holds the current persistence snapshot and all economy bookkeeping. Every
28
+ * mutating call flags takeDirty so the facade persists exactly once per
29
+ * batch of changes; read methods (snapshot, affinityView) never write.
30
+ */
31
+ export declare class PetLedger {
32
+ private readonly affinityConfig;
33
+ private readonly treatConfig;
34
+ private current;
35
+ /** Completed turns already rewarded, per session (turn numbers are per-session). */
36
+ private rewardedTurns;
37
+ private lastLegacyTurnRewardAt;
38
+ private dirty;
39
+ constructor(persist: PetPersist, config?: LedgerConfig);
40
+ /** Affinity cooldown/rank tuning (read-only). */
41
+ get affinity(): AffinityConfig;
42
+ /** The current persistence snapshot (trade a copy when mutating). */
43
+ get snapshot(): PetPersist;
44
+ /** Stock cap reported to clients. */
45
+ get treatMax(): number;
46
+ /** Consume the pending-write flag if any mutation occurred. */
47
+ takeDirty(): boolean;
48
+ /** Replace the display block (clamping stays a caller concern). */
49
+ setDisplay(display: PetDisplayConfig): void;
50
+ /** Replace the selected pet id (validation stays a caller concern). */
51
+ setPetId(petId: string): void;
52
+ /** Replace one pet's display name (validation stays a caller concern). */
53
+ setPetName(petId: string, name: string): void;
54
+ /**
55
+ * Settle the treat economy (work + time output since the last settlement).
56
+ * A zero-gain first settlement still starts the time clock (anchor write),
57
+ * which is how the 30-minute time output can ever accrue. Returns true when
58
+ * the in-memory ledger changed and should be persisted.
59
+ */
60
+ settleTreats(nowMs: number): boolean;
61
+ /**
62
+ * Award the completed-turn reward once per session+turn (idempotent) and
63
+ * run the treat settlement that work output feeds. Returns true when the
64
+ * snapshot changed.
65
+ */
66
+ rewardTurn(sessionId: string, turn: number, nowMs: number): boolean;
67
+ /** Preserve turn rewards for installations that only emit legacy activity. */
68
+ rewardLegacyTurn(nowMs: number): boolean;
69
+ private applyTurnReward;
70
+ /**
71
+ * Pet or feed the pet. Feeding settles first, then gates on the feed
72
+ * cooldown before spending stock — a feed inside the cooldown must not burn
73
+ * a treat for nothing.
74
+ */
75
+ interact(kind: PetInteraction, nowMs: number): LedgerInteractionResult;
76
+ /** Current affinity view for the RPC snapshot. */
77
+ affinityView(nowMs: number): PetAffinityView;
78
+ }
79
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACpB,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEhE,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;IAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;CAC9B;AAED,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,6DAA6D;IAC7D,QAAQ,EAAE,eAAe,CAAA;CAC1B;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,OAAO,CAAY;IAC3B,oFAAoF;IACpF,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,sBAAsB,CAAI;IAClC,OAAO,CAAC,KAAK,CAAQ;gBAET,OAAO,EAAE,UAAU,EAAE,MAAM,GAAE,YAAiB;IAM1D,iDAAiD;IACjD,IAAI,QAAQ,IAAI,cAAc,CAE7B;IAED,qEAAqE;IACrE,IAAI,QAAQ,IAAI,UAAU,CAEzB;IAED,qCAAqC;IACrC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,+DAA+D;IAC/D,SAAS,IAAI,OAAO;IAMpB,mEAAmE;IACnE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAK3C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7B,0EAA0E;IAC1E,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAK7C;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAapC;;;;OAIG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IASnE,8EAA8E;IAC9E,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IASxC,OAAO,CAAC,eAAe;IASvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,uBAAuB;IAyBtE,kDAAkD;IAClD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;CAG7C"}