@linxin666/dsh-pet 0.2.2 → 0.2.3
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.
- package/README.i18n.yaml +2 -2
- package/README.md +66 -3
- package/README.zh.md +66 -3
- package/assets/whale/pet.json +169 -20
- package/assets/whale-refined/pet.json +17 -2
- package/lib/client.js +438 -21
- package/lib/client.js.map +1 -1
- package/lib/index.js +752 -36
- package/lib/live2d-vendor.js +995 -0
- package/lib/live2d-vendor.js.map +1 -0
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +2 -1
- package/lib/types/client/PetSettingsCard.d.ts +10 -0
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +22 -1
- package/lib/types/client/PetSprite.d.ts +7 -1
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +17 -8
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +5 -0
- package/lib/types/client/locales.d.ts +10 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +10 -0
- package/lib/types/client/phase-stream.d.ts +22 -0
- package/lib/types/client/phase-stream.d.ts.map +1 -0
- package/lib/types/client/phase-stream.js +28 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts +25 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts.map +1 -0
- package/lib/types/client/renderers/PetRendererSwitch.js +24 -0
- package/lib/types/client/renderers/live2d/Live2dVisualMount.d.ts +22 -0
- package/lib/types/client/renderers/live2d/Live2dVisualMount.d.ts.map +1 -0
- package/lib/types/client/renderers/live2d/Live2dVisualMount.js +75 -0
- package/lib/types/client/renderers/live2d/runtime.d.ts +89 -0
- package/lib/types/client/renderers/live2d/runtime.d.ts.map +1 -0
- package/lib/types/client/renderers/live2d/runtime.js +63 -0
- package/lib/types/client/renderers/live2d/vendor-entry.d.ts +15 -0
- package/lib/types/client/renderers/live2d/vendor-entry.d.ts.map +1 -0
- package/lib/types/client/renderers/live2d/vendor-entry.js +14 -0
- package/lib/types/client/renderers/live2d.d.ts +52 -0
- package/lib/types/client/renderers/live2d.d.ts.map +1 -0
- package/lib/types/client/renderers/live2d.js +180 -0
- package/lib/types/client/renderers/registry.d.ts +32 -0
- package/lib/types/client/renderers/registry.d.ts.map +1 -0
- package/lib/types/client/renderers/registry.js +49 -0
- package/lib/types/contracts/renderer.d.ts +50 -0
- package/lib/types/contracts/renderer.d.ts.map +1 -0
- package/lib/types/contracts/renderer.js +15 -0
- package/lib/types/manifest-v2.d.ts +129 -0
- package/lib/types/manifest-v2.d.ts.map +1 -0
- package/lib/types/manifest-v2.js +387 -0
- package/lib/types/model3.d.ts +26 -0
- package/lib/types/model3.d.ts.map +1 -0
- package/lib/types/model3.js +96 -0
- package/lib/types/registry.d.ts +57 -3
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +208 -33
- package/lib/types/routes.d.ts +39 -2
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +183 -12
- package/lib/types/service.d.ts +5 -1
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +4 -0
- package/package.json +3 -1
- package/src/client/PetDockEntry.tsx +19 -11
- package/src/client/PetSettingsCard.tsx +39 -0
- package/src/client/PetSprite.test.tsx +11 -0
- package/src/client/PetSprite.tsx +24 -9
- package/src/client/index.ts +6 -0
- package/src/client/locales.ts +10 -0
- package/src/client/phase-stream.test.ts +76 -0
- package/src/client/phase-stream.ts +39 -0
- package/src/client/renderers/PetRendererSwitch.test.tsx +66 -0
- package/src/client/renderers/PetRendererSwitch.tsx +48 -0
- package/src/client/renderers/live2d/Live2dVisualMount.tsx +98 -0
- package/src/client/renderers/live2d/runtime.test.ts +61 -0
- package/src/client/renderers/live2d/runtime.ts +119 -0
- package/src/client/renderers/live2d/vendor-entry.ts +14 -0
- package/src/client/renderers/live2d.test.ts +242 -0
- package/src/client/renderers/live2d.ts +218 -0
- package/src/client/renderers/registry.ts +58 -0
- package/src/client/settings-section.module.css +23 -0
- package/src/contracts/renderer.ts +54 -0
- package/src/manifest-v2.test.ts +251 -0
- package/src/manifest-v2.ts +414 -0
- package/src/model3.test.ts +79 -0
- package/src/model3.ts +91 -0
- package/src/registry.test.ts +215 -2
- package/src/registry.ts +246 -34
- package/src/routes.ts +202 -13
- package/src/service.ts +6 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"PetDockEntry.d.ts","sourceRoot":"","sources":["../../../src/client/PetDockEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAmC,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAGtD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,KAAK,EAAE,gBAAgB,CAAA;IACvB,kFAAkF;IAClF,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,uBAAuB;IACvB,IAAI,EAAE,MAAM,IAAI,CAAA;IAChB,qCAAqC;IACrC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,uDAAuD;IACvD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,2DAA2D;IAC3D,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,iCAAiC;IACjC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB;AAED,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAC3B,WAAW,GACT,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;AAI1B;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CA6DnE"}
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
*/
|
|
14
14
|
import { useEffect, useSyncExternalStore } from 'react';
|
|
15
15
|
import { PetSprite } from './PetSprite.js';
|
|
16
|
+
import { PetRendererSwitch } from './renderers/PetRendererSwitch.js';
|
|
16
17
|
import styles from './pet.module.css';
|
|
17
18
|
const DEFAULT_DISPLAY = { visible: true, size: 160, right: 24, bottom: 20 };
|
|
18
19
|
/**
|
|
@@ -35,7 +36,7 @@ export function PetDockEntry(props) {
|
|
|
35
36
|
if (visible) {
|
|
36
37
|
return (_jsx("span", { "data-pet-dock": true, "data-testid": "pet-dock", children: snapshot === null || definition === null
|
|
37
38
|
? null
|
|
38
|
-
: (_jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, t: props.t })) }));
|
|
39
|
+
: (_jsx(PetRendererSwitch, { definition: definition, phase: snapshot?.phase ?? 'idle', onPet: props.pet, t: props.t, children: _jsx(PetSprite, { snapshot: snapshot, definition: definition, display: snapshot.display, feedback: feedback, onPet: props.pet, onFeed: props.feed, onHide: props.hide, onDragEnd: props.dragEnd, onRename: props.rename, onOpenSession: props.openSession, onFeedbackDone: props.feedbackDone, t: props.t }) })) }));
|
|
39
40
|
}
|
|
40
41
|
const display = snapshot?.display ?? DEFAULT_DISPLAY;
|
|
41
42
|
return (_jsx("button", { type: "button", className: styles.summon, style: {
|
|
@@ -44,6 +44,8 @@ export interface PetSettingsCardState extends CardShell {
|
|
|
44
44
|
value: string;
|
|
45
45
|
label: string;
|
|
46
46
|
}[];
|
|
47
|
+
/** Registry diagnostics (v1 migration hints, invalid entries), host-served. */
|
|
48
|
+
petDiagnostics: readonly PetDiagnosticView[];
|
|
47
49
|
}
|
|
48
50
|
/** The registration-side face the card's slot entry injects. */
|
|
49
51
|
export interface PetSettingsCardFace extends CardActions {
|
|
@@ -52,16 +54,24 @@ export interface PetSettingsCardFace extends CardActions {
|
|
|
52
54
|
petSettingsCard: SnapshotStore<PetSettingsCardState>;
|
|
53
55
|
};
|
|
54
56
|
}
|
|
57
|
+
/** One registry diagnostic as served by '/api/pet/diagnostics' (#623). */
|
|
58
|
+
export interface PetDiagnosticView {
|
|
59
|
+
level: 'error' | 'warning';
|
|
60
|
+
message: string;
|
|
61
|
+
}
|
|
55
62
|
/** Bridges the 'pet' scope onto the card's staged form. */
|
|
56
63
|
export declare class PetSettingsCardController {
|
|
57
64
|
private readonly form;
|
|
58
65
|
private readonly store;
|
|
59
66
|
private readonly petChoices;
|
|
60
67
|
private readonly petLabels;
|
|
68
|
+
private diagnostics;
|
|
61
69
|
private loaded;
|
|
62
70
|
private attempts;
|
|
63
71
|
/** @param scope - the bound settings scope for the 'pet' namespace. */
|
|
64
72
|
constructor(scope: SettingsScope<PetSettings>);
|
|
73
|
+
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
74
|
+
private loadDiagnostics;
|
|
65
75
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
66
76
|
private loadPets;
|
|
67
77
|
private projection;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,14 @@ async function fetchPetChoices() {
|
|
|
9
9
|
throw new Error('pet pets failed: ' + response.status);
|
|
10
10
|
return (await response.json());
|
|
11
11
|
}
|
|
12
|
+
/** Fetch the registry diagnostics (v1 migration hints, invalid entries). */
|
|
13
|
+
async function fetchPetDiagnostics() {
|
|
14
|
+
const response = await fetch('/api/pet/diagnostics');
|
|
15
|
+
if (!response.ok)
|
|
16
|
+
throw new Error('pet diagnostics failed: ' + response.status);
|
|
17
|
+
const body = (await response.json());
|
|
18
|
+
return body.diagnostics ?? [];
|
|
19
|
+
}
|
|
12
20
|
/** Bridges the 'pet' scope onto the card's staged form. */
|
|
13
21
|
export class PetSettingsCardController {
|
|
14
22
|
form;
|
|
@@ -18,6 +26,7 @@ export class PetSettingsCardController {
|
|
|
18
26
|
// without rebuilding the form.
|
|
19
27
|
petChoices = [];
|
|
20
28
|
petLabels = new Map();
|
|
29
|
+
diagnostics = [];
|
|
21
30
|
loaded = false;
|
|
22
31
|
attempts = 0;
|
|
23
32
|
/** @param scope - the bound settings scope for the 'pet' namespace. */
|
|
@@ -32,6 +41,17 @@ export class PetSettingsCardController {
|
|
|
32
41
|
]);
|
|
33
42
|
this.store = this.form.bind(() => this.projection());
|
|
34
43
|
void this.loadPets();
|
|
44
|
+
void this.loadDiagnostics();
|
|
45
|
+
}
|
|
46
|
+
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
47
|
+
async loadDiagnostics() {
|
|
48
|
+
try {
|
|
49
|
+
this.diagnostics = await fetchPetDiagnostics();
|
|
50
|
+
this.store.set(this.projection());
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
this.diagnostics = [];
|
|
54
|
+
}
|
|
35
55
|
}
|
|
36
56
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
37
57
|
async loadPets() {
|
|
@@ -62,6 +82,7 @@ export class PetSettingsCardController {
|
|
|
62
82
|
bottom: this.form.field('bottom'),
|
|
63
83
|
petId: this.form.field('petId'),
|
|
64
84
|
petChoices: this.petChoices.map(id => ({ value: id, label: this.petLabels.get(id) ?? id })),
|
|
85
|
+
petDiagnostics: this.diagnostics,
|
|
65
86
|
};
|
|
66
87
|
}
|
|
67
88
|
/**
|
|
@@ -94,7 +115,7 @@ export function PetSettingsCard(props) {
|
|
|
94
115
|
invalidLabel: t('settings.invalidNumber'),
|
|
95
116
|
disabled,
|
|
96
117
|
};
|
|
97
|
-
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, alwaysOpen: true, children: [_jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } })] }));
|
|
118
|
+
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, alwaysOpen: true, children: [_jsx(BooleanField, { id: "settings-pet-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ChoiceField, { id: "settings-pet-pet", label: t('settings.pet'), hint: t('settings.petHint'), inheritLabel: t('settings.inherit'), ...fieldProps, ...state.petId, choices: state.petChoices, onEdit: (text) => { props.edit('petId', text); }, onReset: () => { props.resetField('petId'); } }), state.petDiagnostics.length === 0 ? null : (_jsxs("li", { className: sectionCss.diagnostics, "data-dsh-part": "diagnostics", children: [_jsx("span", { className: sectionCss.diagnosticsTitle, children: t('settings.diagnosticsTitle') }), _jsx("ul", { children: state.petDiagnostics.map((diagnostic, index) => (_jsx("li", { "data-level": diagnostic.level, children: diagnostic.message }, index))) })] })), _jsx(BooleanField, { id: "settings-pet-visible", label: t('settings.visible'), hint: t('settings.visibleHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.visible, onEdit: (text) => { props.edit('visible', text); }, onReset: () => { props.resetField('visible'); } }), _jsx(ValueField, { id: "settings-pet-size", label: t('settings.size'), hint: t('settings.sizeHint'), numeric: true, ...fieldProps, ...state.size, onEdit: (text) => { props.edit('size', text); }, onReset: () => { props.resetField('size'); } }), _jsx(ValueField, { id: "settings-pet-right", label: t('settings.right'), hint: t('settings.rightHint'), numeric: true, ...fieldProps, ...state.right, onEdit: (text) => { props.edit('right', text); }, onReset: () => { props.resetField('right'); } }), _jsx(ValueField, { id: "settings-pet-bottom", label: t('settings.bottom'), hint: t('settings.bottomHint'), numeric: true, ...fieldProps, ...state.bottom, onEdit: (text) => { props.edit('bottom', text); }, onReset: () => { props.resetField('bottom'); } })] }));
|
|
98
119
|
}
|
|
99
120
|
/** Render the pet settings card as a first-level settings page. */
|
|
100
121
|
export function PetSettingsSection(props) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* pet id), so one component renders every registry entry.
|
|
9
9
|
* @module @linxin666/dsh-pet/client/PetSprite
|
|
10
10
|
*/
|
|
11
|
-
import type { ReactPortal } from 'react';
|
|
11
|
+
import type { ReactNode, ReactPortal } from 'react';
|
|
12
12
|
import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
13
|
import type { PetDisplayConfig } from '../persist.ts';
|
|
14
14
|
import type { PetStateView } from '../service.ts';
|
|
@@ -39,6 +39,12 @@ export interface PetSpriteProps {
|
|
|
39
39
|
onOpenSession: (sessionId: string) => void;
|
|
40
40
|
/** Clear the reaction bubble (after its CSS animation). */
|
|
41
41
|
onFeedbackDone: () => void;
|
|
42
|
+
/**
|
|
43
|
+
* Custom visual replacing the sprite2d atlas animation (pet-center M3).
|
|
44
|
+
* The chrome (drag, bubbles, panel, tap economy) is untouched: the visual
|
|
45
|
+
* renders inside the sprite box, and the atlas load + frame loop skip.
|
|
46
|
+
*/
|
|
47
|
+
visual?: ReactNode;
|
|
42
48
|
/** Locale translate seat (namespace-bound). */
|
|
43
49
|
t: TranslateNS<typeof NS>;
|
|
44
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAoD,WAAW,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAoD,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAOD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAge5D"}
|
|
@@ -67,8 +67,11 @@ export function PetSprite(props) {
|
|
|
67
67
|
const tracks = definition.tracks;
|
|
68
68
|
const sequences = definition.sequences;
|
|
69
69
|
// Load the atlas once; the definition carries the authoritative per-row
|
|
70
|
-
// frame counts and per-track durations, so nothing else is fetched.
|
|
70
|
+
// frame counts and per-track durations, so nothing else is fetched. A
|
|
71
|
+
// custom visual (pet-center M3) replaces the atlas entirely.
|
|
71
72
|
useEffect(() => {
|
|
73
|
+
if (props.visual !== undefined)
|
|
74
|
+
return;
|
|
72
75
|
let cancelled = false;
|
|
73
76
|
const img = new Image();
|
|
74
77
|
img.onload = () => {
|
|
@@ -80,7 +83,7 @@ export function PetSprite(props) {
|
|
|
80
83
|
cancelled = true;
|
|
81
84
|
img.onload = null;
|
|
82
85
|
};
|
|
83
|
-
}, [definition.atlasUrl]);
|
|
86
|
+
}, [definition.atlasUrl, props.visual]);
|
|
84
87
|
// Frame loop: advance the current track and write background-position.
|
|
85
88
|
// Offsets must be in SCALED coordinates (background-position applies to the
|
|
86
89
|
// scaled background image), so the current sprite scale rides a ref that
|
|
@@ -93,6 +96,8 @@ export function PetSprite(props) {
|
|
|
93
96
|
const scaleRef = useRef(spriteScale);
|
|
94
97
|
scaleRef.current = spriteScale;
|
|
95
98
|
useEffect(() => {
|
|
99
|
+
if (props.visual !== undefined)
|
|
100
|
+
return;
|
|
96
101
|
const reduceMotion = typeof window !== 'undefined'
|
|
97
102
|
&& window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true;
|
|
98
103
|
const sequence = animation === animationForPhase(phase) ? sequences?.[phase] : undefined;
|
|
@@ -160,7 +165,7 @@ export function PetSprite(props) {
|
|
|
160
165
|
};
|
|
161
166
|
raf = requestAnimationFrame(tick);
|
|
162
167
|
return () => cancelAnimationFrame(raf);
|
|
163
|
-
}, [animation, phase, cell, columns, rows, tracks, sequences]);
|
|
168
|
+
}, [animation, phase, cell, columns, rows, tracks, sequences, props.visual]);
|
|
164
169
|
// Auto-clear the feedback bubble after its CSS animation. The callback
|
|
165
170
|
// rides a ref so re-renders never reset the timer: the 2s poll rebuilds
|
|
166
171
|
// `props` every tick, and depending on it would starve the timeout.
|
|
@@ -292,10 +297,14 @@ export function PetSprite(props) {
|
|
|
292
297
|
}, children: [_jsx("div", { ref: spriteRef, className: styles.sprite, style: {
|
|
293
298
|
width: spriteWidth,
|
|
294
299
|
height: spriteHeight,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
300
|
+
...(props.visual === undefined
|
|
301
|
+
? {
|
|
302
|
+
backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,
|
|
303
|
+
backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',
|
|
304
|
+
backgroundRepeat: 'no-repeat',
|
|
305
|
+
backgroundPosition: '0 0',
|
|
306
|
+
}
|
|
307
|
+
: {}),
|
|
299
308
|
cursor: dragRef.current === null ? 'grab' : 'grabbing',
|
|
300
309
|
}, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onClick: () => {
|
|
301
310
|
// A pointer sequence that moved (dragged) still fires a trailing
|
|
@@ -303,7 +312,7 @@ export function PetSprite(props) {
|
|
|
303
312
|
if (draggedRef.current)
|
|
304
313
|
return;
|
|
305
314
|
props.onPet();
|
|
306
|
-
}, role: "button", "aria-label": definition.displayName }), feedback !== null && (_jsx("div", { ref: bubbleRef, className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined) && (_jsxs("div", { ref: bubbleRef, className: styles.bubbleStack, onPointerEnter: () => setStackPeek(true), onPointerLeave: () => setStackPeek(false), children: [visibleSessions.map((session, index) => {
|
|
315
|
+
}, role: "button", "aria-label": definition.displayName, children: props.visual }), feedback !== null && (_jsx("div", { ref: bubbleRef, className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined) && (_jsxs("div", { ref: bubbleRef, className: styles.bubbleStack, onPointerEnter: () => setStackPeek(true), onPointerLeave: () => setStackPeek(false), children: [visibleSessions.map((session, index) => {
|
|
307
316
|
// The whisper rides the display session's bubble — the stack's
|
|
308
317
|
// primary entry (DOM-first, rendered bottom-most by the reversed
|
|
309
318
|
// column so it stays glued to the sprite when extras open above).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AA+DnI,wEAAwE;AACxE,eAAO,MAAM,MAAM,UAA2E,CAAA;AAE9F,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAsN9C"}
|
|
@@ -14,6 +14,8 @@ import { createElement } from 'react';
|
|
|
14
14
|
import { createRoot } from 'react-dom/client';
|
|
15
15
|
import { createPetStore } from './pet-store.js';
|
|
16
16
|
import { PetDockEntry } from './PetDockEntry.js';
|
|
17
|
+
import { defaultPetRendererRegistry } from './renderers/registry.js';
|
|
18
|
+
import { live2dRenderer } from './renderers/live2d.js';
|
|
17
19
|
import { PetSettingsSection, PetSettingsCardController } from './PetSettingsCard.js';
|
|
18
20
|
import { NS, en, zh, t } from './locales.js';
|
|
19
21
|
/** Same-origin JSON fetch helper (GET without body, POST with JSON body). */
|
|
@@ -54,6 +56,9 @@ export const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote
|
|
|
54
56
|
*/
|
|
55
57
|
export function apply(ctx) {
|
|
56
58
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'pet: dictionaries');
|
|
59
|
+
// Built-in renderers dispatch through the plugin-wide registry (pet-center
|
|
60
|
+
// M3). Registration is idempotent (id wins), so re-applies stay clean.
|
|
61
|
+
defaultPetRendererRegistry.register(live2dRenderer);
|
|
57
62
|
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope;
|
|
58
63
|
const settingsScope = binder.bind({ namespace: PET_SETTINGS_NS });
|
|
59
64
|
const enabled = () => {
|
|
@@ -17,10 +17,15 @@ export declare const zh: {
|
|
|
17
17
|
readonly 'pet.treats': '小鱼干 ×{n}';
|
|
18
18
|
readonly 'pet.state.loading': '宠物正在赶来…';
|
|
19
19
|
readonly 'pet.state.error': '宠物迷路了(连接失败)';
|
|
20
|
+
readonly 'pet.renderer.unavailable': '这只宠物需要的渲染器({renderer})在当前版本不可用。';
|
|
21
|
+
readonly 'pet.live2d.core-missing': 'Live2D 核心未安装:请把官方 live2dcubismcore.min.js 放入 $DSH_HOME/pets/.runtime/ 后刷新(步骤见宠物插件 README)。';
|
|
22
|
+
readonly 'pet.live2d.vendor-missing': 'Live2D 组件缺失,请升级宠物插件。';
|
|
23
|
+
readonly 'pet.live2d.load-failed': 'Live2D 模型加载失败,请检查该宠物目录的完整性。';
|
|
20
24
|
readonly 'pet.openSessionHint': '点击跳转到对应会话';
|
|
21
25
|
readonly 'pet.moreSessions': '展开其余 {n} 个会话的气泡';
|
|
22
26
|
readonly 'pet.collapseSessions': '收起会话气泡';
|
|
23
27
|
readonly 'settings.title': '宠物';
|
|
28
|
+
readonly 'settings.diagnosticsTitle': '宠物目录诊断';
|
|
24
29
|
readonly 'settings.description': '选择宠物并调整它的显示布局。';
|
|
25
30
|
readonly 'settings.pet': '宠物';
|
|
26
31
|
readonly 'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。';
|
|
@@ -63,10 +68,15 @@ export declare const en: {
|
|
|
63
68
|
readonly 'pet.treats': 'Treats ×{n}';
|
|
64
69
|
readonly 'pet.state.loading': 'The pet is on its way…';
|
|
65
70
|
readonly 'pet.state.error': 'The pet is lost (connection failed)';
|
|
71
|
+
readonly 'pet.renderer.unavailable': 'This pet needs a renderer ({renderer}) that is not available in this build.';
|
|
72
|
+
readonly 'pet.live2d.core-missing': 'Live2D Cubism Core is not installed: place the official live2dcubismcore.min.js under $DSH_HOME/pets/.runtime/ and refresh (see the pet plugin README).';
|
|
73
|
+
readonly 'pet.live2d.vendor-missing': 'The Live2D component is missing; please update the pet plugin.';
|
|
74
|
+
readonly 'pet.live2d.load-failed': 'The Live2D model failed to load; check the pet directory is complete.';
|
|
66
75
|
readonly 'pet.openSessionHint': 'Click to jump to this session';
|
|
67
76
|
readonly 'pet.moreSessions': 'Expand {n} more session bubbles';
|
|
68
77
|
readonly 'pet.collapseSessions': 'Collapse session bubbles';
|
|
69
78
|
readonly 'settings.title': 'Pet';
|
|
79
|
+
readonly 'settings.diagnosticsTitle': 'Pet directory diagnostics';
|
|
70
80
|
readonly 'settings.description': 'Pick a pet and tune its display layout.';
|
|
71
81
|
readonly 'settings.pet': 'Pet';
|
|
72
82
|
readonly 'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,11 +17,16 @@ export const zh = {
|
|
|
17
17
|
'pet.treats': '小鱼干 ×{n}',
|
|
18
18
|
'pet.state.loading': '宠物正在赶来…',
|
|
19
19
|
'pet.state.error': '宠物迷路了(连接失败)',
|
|
20
|
+
'pet.renderer.unavailable': '这只宠物需要的渲染器({renderer})在当前版本不可用。',
|
|
21
|
+
'pet.live2d.core-missing': 'Live2D 核心未安装:请把官方 live2dcubismcore.min.js 放入 $DSH_HOME/pets/.runtime/ 后刷新(步骤见宠物插件 README)。',
|
|
22
|
+
'pet.live2d.vendor-missing': 'Live2D 组件缺失,请升级宠物插件。',
|
|
23
|
+
'pet.live2d.load-failed': 'Live2D 模型加载失败,请检查该宠物目录的完整性。',
|
|
20
24
|
'pet.openSessionHint': '点击跳转到对应会话',
|
|
21
25
|
'pet.moreSessions': '展开其余 {n} 个会话的气泡',
|
|
22
26
|
'pet.collapseSessions': '收起会话气泡',
|
|
23
27
|
// 一级设置页(settings.section 席位)。
|
|
24
28
|
'settings.title': '宠物',
|
|
29
|
+
'settings.diagnosticsTitle': '宠物目录诊断',
|
|
25
30
|
'settings.description': '选择宠物并调整它的显示布局。',
|
|
26
31
|
'settings.pet': '宠物',
|
|
27
32
|
'settings.petHint': '选择显示哪只宠物;每只宠物独立命名,可在宠物悬浮面板改名。',
|
|
@@ -64,11 +69,16 @@ export const en = {
|
|
|
64
69
|
'pet.treats': 'Treats ×{n}',
|
|
65
70
|
'pet.state.loading': 'The pet is on its way…',
|
|
66
71
|
'pet.state.error': 'The pet is lost (connection failed)',
|
|
72
|
+
'pet.renderer.unavailable': 'This pet needs a renderer ({renderer}) that is not available in this build.',
|
|
73
|
+
'pet.live2d.core-missing': 'Live2D Cubism Core is not installed: place the official live2dcubismcore.min.js under $DSH_HOME/pets/.runtime/ and refresh (see the pet plugin README).',
|
|
74
|
+
'pet.live2d.vendor-missing': 'The Live2D component is missing; please update the pet plugin.',
|
|
75
|
+
'pet.live2d.load-failed': 'The Live2D model failed to load; check the pet directory is complete.',
|
|
67
76
|
'pet.openSessionHint': 'Click to jump to this session',
|
|
68
77
|
'pet.moreSessions': 'Expand {n} more session bubbles',
|
|
69
78
|
'pet.collapseSessions': 'Collapse session bubbles',
|
|
70
79
|
// First-level settings section (the `settings.section` seat).
|
|
71
80
|
'settings.title': 'Pet',
|
|
81
|
+
'settings.diagnosticsTitle': 'Pet directory diagnostics',
|
|
72
82
|
'settings.description': 'Pick a pet and tune its display layout.',
|
|
73
83
|
'settings.pet': 'Pet',
|
|
74
84
|
'settings.petHint': 'Choose which pet shows. Names are stored per pet; rename from the pet hover panel.',
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase stream — bridges the polled host snapshots onto the renderer
|
|
3
|
+
* contract's { get, subscribe } shape (pet-center M2 P4, issue #623). The
|
|
4
|
+
* existing poll loop pushes each snapshot's ActivityPhase; subscribers are
|
|
5
|
+
* dispatched on CHANGE only (phase transitions are sparse — done/failed hold
|
|
6
|
+
* a timed window before falling back to idle — and renderers like Live2D pay
|
|
7
|
+
* per transition, not per tick).
|
|
8
|
+
* @module @linxin666/dsh-pet/client/phase-stream
|
|
9
|
+
*/
|
|
10
|
+
import type { ActivityPhase } from '../state.ts';
|
|
11
|
+
/** The renderer-facing phase stream. */
|
|
12
|
+
export interface PhaseStream {
|
|
13
|
+
/** The latest pushed phase. */
|
|
14
|
+
get(): ActivityPhase;
|
|
15
|
+
/** Subscribe to phase changes; returns the unsubscribe. */
|
|
16
|
+
subscribe(listener: (phase: ActivityPhase) => void): () => void;
|
|
17
|
+
/** Feed a fresh snapshot phase; no-op when unchanged. */
|
|
18
|
+
push(phase: ActivityPhase): void;
|
|
19
|
+
}
|
|
20
|
+
/** Create the stream (one per pet entry lifetime, owned by the plugin body). */
|
|
21
|
+
export declare function createPhaseStream(initial?: ActivityPhase): PhaseStream;
|
|
22
|
+
//# sourceMappingURL=phase-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-stream.d.ts","sourceRoot":"","sources":["../../../src/client/phase-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,+BAA+B;IAC/B,GAAG,IAAI,aAAa,CAAA;IACpB,2DAA2D;IAC3D,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAC/D,yDAAyD;IACzD,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;CACjC;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,aAAsB,GAAG,WAAW,CAe9E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase stream — bridges the polled host snapshots onto the renderer
|
|
3
|
+
* contract's { get, subscribe } shape (pet-center M2 P4, issue #623). The
|
|
4
|
+
* existing poll loop pushes each snapshot's ActivityPhase; subscribers are
|
|
5
|
+
* dispatched on CHANGE only (phase transitions are sparse — done/failed hold
|
|
6
|
+
* a timed window before falling back to idle — and renderers like Live2D pay
|
|
7
|
+
* per transition, not per tick).
|
|
8
|
+
* @module @linxin666/dsh-pet/client/phase-stream
|
|
9
|
+
*/
|
|
10
|
+
/** Create the stream (one per pet entry lifetime, owned by the plugin body). */
|
|
11
|
+
export function createPhaseStream(initial = 'idle') {
|
|
12
|
+
let current = initial;
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
return {
|
|
15
|
+
get: () => current,
|
|
16
|
+
subscribe(listener) {
|
|
17
|
+
listeners.add(listener);
|
|
18
|
+
return () => { listeners.delete(listener); };
|
|
19
|
+
},
|
|
20
|
+
push(phase) {
|
|
21
|
+
if (phase === current)
|
|
22
|
+
return;
|
|
23
|
+
current = phase;
|
|
24
|
+
for (const listener of [...listeners])
|
|
25
|
+
listener(phase);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renderer switch — the client dispatch seam of the pet center (issue #623,
|
|
3
|
+
* milestone M2 P5 / M3). The pet's manifest picks the renderer: sprite2d
|
|
4
|
+
* hands straight through to the sprite; live2d injects its visual INTO the
|
|
5
|
+
* sprite chrome (the dock, bubbles and panel belong to the pet center, not
|
|
6
|
+
* the renderer); a renderer this build cannot serve renders a clear
|
|
7
|
+
* diagnostic card instead of blanking.
|
|
8
|
+
* @module @linxin666/dsh-pet/client/renderers/PetRendererSwitch
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactElement, type ReactNode } from 'react';
|
|
11
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
|
+
import type { PetDefinition } from '../../registry.ts';
|
|
13
|
+
import type { ActivityPhase } from '../../state.ts';
|
|
14
|
+
import type { NS } from '../locales.ts';
|
|
15
|
+
/** Dispatch one pet definition to its renderer; unknown kinds get a card. */
|
|
16
|
+
export declare function PetRendererSwitch(props: {
|
|
17
|
+
definition: PetDefinition;
|
|
18
|
+
/** Current activity phase (fed to renderer visuals). */
|
|
19
|
+
phase: ActivityPhase;
|
|
20
|
+
/** The chrome's pet interaction (affinity write-back owner). */
|
|
21
|
+
onPet: () => void;
|
|
22
|
+
t: PropsLocale<typeof NS>['t'];
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
}): ReactElement;
|
|
25
|
+
//# sourceMappingURL=PetRendererSwitch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PetRendererSwitch.d.ts","sourceRoot":"","sources":["../../../../src/client/renderers/PetRendererSwitch.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAgC,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AACvF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAInD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAA;AAEvC,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,UAAU,EAAE,aAAa,CAAA;IACzB,wDAAwD;IACxD,KAAK,EAAE,aAAa,CAAA;IACpB,gEAAgE;IAChE,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;IAC9B,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,GAAG,YAAY,CAmBf"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Renderer switch — the client dispatch seam of the pet center (issue #623,
|
|
4
|
+
* milestone M2 P5 / M3). The pet's manifest picks the renderer: sprite2d
|
|
5
|
+
* hands straight through to the sprite; live2d injects its visual INTO the
|
|
6
|
+
* sprite chrome (the dock, bubbles and panel belong to the pet center, not
|
|
7
|
+
* the renderer); a renderer this build cannot serve renders a clear
|
|
8
|
+
* diagnostic card instead of blanking.
|
|
9
|
+
* @module @linxin666/dsh-pet/client/renderers/PetRendererSwitch
|
|
10
|
+
*/
|
|
11
|
+
import { cloneElement, isValidElement } from 'react';
|
|
12
|
+
import { defaultPetRendererRegistry } from './registry.js';
|
|
13
|
+
import { Live2dVisualMount } from './live2d/Live2dVisualMount.js';
|
|
14
|
+
/** Dispatch one pet definition to its renderer; unknown kinds get a card. */
|
|
15
|
+
export function PetRendererSwitch(props) {
|
|
16
|
+
const renderer = props.definition.renderer ?? 'sprite2d';
|
|
17
|
+
if (renderer === 'sprite2d')
|
|
18
|
+
return _jsx(_Fragment, { children: props.children });
|
|
19
|
+
if (renderer === 'live2d' && defaultPetRendererRegistry.has('live2d') && isValidElement(props.children)) {
|
|
20
|
+
const visual = (_jsx(Live2dVisualMount, { definition: props.definition, phase: props.phase, onPet: props.onPet, t: props.t }));
|
|
21
|
+
return cloneElement(props.children, { visual });
|
|
22
|
+
}
|
|
23
|
+
return (_jsx("span", { "data-dsh-pet-renderer-fallback": renderer, children: props.t('pet.renderer.unavailable', { renderer }) }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live2D visual mount (pet-center M3) — the React bridge between the pet
|
|
3
|
+
* center chrome and the imperative live2d renderer. The bridge owns the
|
|
4
|
+
* contract context (asset base, phase stream, interaction write-back,
|
|
5
|
+
* activation cleanups), feeds the polled phase into the stream, forwards
|
|
6
|
+
* sub-4px taps as hit-test coordinates, and renders the localized error
|
|
7
|
+
* card when the renderer reports a fatal boot failure.
|
|
8
|
+
* @module @linxin666/dsh-pet/client/renderers/live2d/Live2dVisualMount
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactElement } from 'react';
|
|
11
|
+
import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
|
+
import type { PetDefinition } from '../../../registry.ts';
|
|
13
|
+
import type { ActivityPhase } from '../../../state.ts';
|
|
14
|
+
import type { NS } from '../../locales.ts';
|
|
15
|
+
/** Mount the live2d renderer as the sprite's visual (inside the chrome). */
|
|
16
|
+
export declare function Live2dVisualMount(props: {
|
|
17
|
+
definition: PetDefinition;
|
|
18
|
+
phase: ActivityPhase;
|
|
19
|
+
onPet: () => void;
|
|
20
|
+
t: PropsLocale<typeof NS>['t'];
|
|
21
|
+
}): ReactElement;
|
|
22
|
+
//# sourceMappingURL=Live2dVisualMount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Live2dVisualMount.d.ts","sourceRoot":"","sources":["../../../../../src/client/renderers/live2d/Live2dVisualMount.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKtD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAE1C,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,UAAU,EAAE,aAAa,CAAA;IACzB,KAAK,EAAE,aAAa,CAAA;IACpB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA;CAC/B,GAAG,YAAY,CAuEf"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Live2D visual mount (pet-center M3) — the React bridge between the pet
|
|
4
|
+
* center chrome and the imperative live2d renderer. The bridge owns the
|
|
5
|
+
* contract context (asset base, phase stream, interaction write-back,
|
|
6
|
+
* activation cleanups), feeds the polled phase into the stream, forwards
|
|
7
|
+
* sub-4px taps as hit-test coordinates, and renders the localized error
|
|
8
|
+
* card when the renderer reports a fatal boot failure.
|
|
9
|
+
* @module @linxin666/dsh-pet/client/renderers/live2d/Live2dVisualMount
|
|
10
|
+
*/
|
|
11
|
+
import { useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { createPhaseStream } from '../../phase-stream.js';
|
|
13
|
+
import { defaultPetRendererRegistry } from '../registry.js';
|
|
14
|
+
/** Mount the live2d renderer as the sprite's visual (inside the chrome). */
|
|
15
|
+
export function Live2dVisualMount(props) {
|
|
16
|
+
const containerRef = useRef(null);
|
|
17
|
+
const streamRef = useRef(null);
|
|
18
|
+
const handleRef = useRef(null);
|
|
19
|
+
const downRef = useRef(null);
|
|
20
|
+
const [error, setError] = useState(null);
|
|
21
|
+
// One activation per pet definition: build the contract context and mount.
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const container = containerRef.current;
|
|
24
|
+
const live2d = props.definition.live2d;
|
|
25
|
+
if (container === null || live2d === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
streamRef.current ??= createPhaseStream(props.phase);
|
|
28
|
+
const cleanups = [];
|
|
29
|
+
const ctx = {
|
|
30
|
+
petId: props.definition.id,
|
|
31
|
+
assetBase: '/pet/' + encodeURIComponent(props.definition.id),
|
|
32
|
+
container,
|
|
33
|
+
phase: streamRef.current,
|
|
34
|
+
interact: props.onPet,
|
|
35
|
+
onCleanup: (fn) => { cleanups.push(fn); },
|
|
36
|
+
};
|
|
37
|
+
let handle;
|
|
38
|
+
try {
|
|
39
|
+
handle = defaultPetRendererRegistry.mount('live2d', ctx, live2d);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
setError('load-failed');
|
|
43
|
+
return () => { for (const fn of cleanups.splice(0))
|
|
44
|
+
fn(); };
|
|
45
|
+
}
|
|
46
|
+
handleRef.current = handle;
|
|
47
|
+
handle.onError?.(setError);
|
|
48
|
+
return () => {
|
|
49
|
+
handleRef.current = null;
|
|
50
|
+
for (const fn of cleanups.splice(0))
|
|
51
|
+
fn();
|
|
52
|
+
handle.dispose();
|
|
53
|
+
};
|
|
54
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- one activation per pet identity
|
|
55
|
+
}, [props.definition]);
|
|
56
|
+
// Feed the polled phase into the activation's stream (change-only).
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
streamRef.current?.push(props.phase);
|
|
59
|
+
}, [props.phase]);
|
|
60
|
+
return (_jsx("div", { ref: containerRef, "data-dsh-pet-live2d": props.definition.id, style: { width: '100%', height: '100%' }, onPointerDown: (e) => { downRef.current = { x: e.clientX, y: e.clientY }; }, onPointerUp: (e) => {
|
|
61
|
+
const down = downRef.current;
|
|
62
|
+
downRef.current = null;
|
|
63
|
+
if (down === null)
|
|
64
|
+
return;
|
|
65
|
+
// A moved pointer is a drag (the chrome owns it), not a tap.
|
|
66
|
+
if (Math.abs(e.clientX - down.x) > 4 || Math.abs(e.clientY - down.y) > 4)
|
|
67
|
+
return;
|
|
68
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
69
|
+
handleRef.current?.tap(e.clientX - rect.left, e.clientY - rect.top);
|
|
70
|
+
}, children: error !== null && (_jsx("span", { "data-dsh-pet-live2d-error": error, children: error === 'core-missing'
|
|
71
|
+
? props.t('pet.live2d.core-missing')
|
|
72
|
+
: error === 'vendor-missing'
|
|
73
|
+
? props.t('pet.live2d.vendor-missing')
|
|
74
|
+
: props.t('pet.live2d.load-failed') })) }));
|
|
75
|
+
}
|