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