@laplace.live/persona-sdk 1.24.0 → 1.25.1
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.md +10 -10
- package/dist/client/client.js +5 -6
- package/dist/client/legacy-names.d.ts +2 -0
- package/dist/client/{legacy-kinds.js → legacy-names.js} +12 -3
- package/dist/client/node.d.ts +13 -0
- package/dist/client/node.js +29 -0
- package/dist/client/scene-hotkeys.d.ts +16 -0
- package/dist/client/scene-hotkeys.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/values/bindings.d.ts +40 -15
- package/dist/values/controller.d.ts +9 -6
- package/dist/values/edit-history.d.ts +4 -0
- package/dist/values/edit-history.js +14 -7
- package/dist/values/effect-schema.d.ts +26 -0
- package/dist/values/effect-schema.js +76 -91
- package/dist/values/hotkey-targets.d.ts +10 -3
- package/dist/values/hotkey-targets.js +20 -0
- package/dist/values/hotkeys.d.ts +9 -1
- package/dist/values/hotkeys.js +11 -0
- package/dist/values/labels.js +1 -0
- package/dist/values/limits.d.ts +96 -8
- package/dist/values/limits.js +109 -1
- package/dist/values/lipsync.d.ts +7 -8
- package/dist/values/locale.d.ts +4 -1
- package/dist/values/locale.js +4 -0
- package/dist/values/model-movement.d.ts +19 -4
- package/dist/values/model-movement.js +17 -4
- package/dist/values/volumetric-lighting.d.ts +1 -1
- package/dist/values/volumetric-lighting.js +1 -1
- package/dist/wire/envelope.js +6 -3
- package/dist/wire/methods.d.ts +16 -17
- package/dist/wire/protocol.d.ts +2 -0
- package/dist/wire/protocol.js +4 -0
- package/dist/wire/schemas/fields.d.ts +8 -0
- package/dist/wire/schemas/fields.js +1 -0
- package/dist/wire/schemas/requests.d.ts +2 -2
- package/dist/wire/schemas/requests.js +70 -20
- package/dist/wire/schemas/settings.d.ts +3 -3
- package/dist/wire/schemas/settings.js +3 -3
- package/dist/wire/types.d.ts +68 -115
- package/dist/wire/types.js +68 -9
- package/package.json +2 -2
- package/dist/client/legacy-kinds.d.ts +0 -2
package/dist/values/limits.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { effectLayerKeys } from "./effect-schema.js";
|
|
2
|
+
import { isFiniteNumber, isOneOf, isRecord } from "./guards.js";
|
|
2
3
|
export function clamp(v, min, max) {
|
|
3
4
|
return Math.min(max, Math.max(min, v));
|
|
4
5
|
}
|
|
@@ -16,6 +17,10 @@ export function wrapAngle(a) {
|
|
|
16
17
|
r += TAU;
|
|
17
18
|
return r;
|
|
18
19
|
}
|
|
20
|
+
/** Wrap degrees into (-180°, 180°], {@link wrapAngle}'s range; whole turns keep in-range values exact. */
|
|
21
|
+
export function wrapDegrees(v) {
|
|
22
|
+
return v - 360 * Math.ceil((v - 180) / 360);
|
|
23
|
+
}
|
|
19
24
|
export const DEFAULT_LIVE2D_PLACEMENT = { x: 0, y: 0, scale: 1, rotation: 0 };
|
|
20
25
|
export const DEFAULT_VRM_PLACEMENT = { x: 0, y: 0, z: 0, rotX: 0, rotY: 0, rotZ: 0, scale: 1 };
|
|
21
26
|
/** Scene colors heal to 6-digit hex; both cases are spelled out because JSON Schema patterns carry no flags. */
|
|
@@ -43,6 +48,11 @@ export const SCENE_LIGHT_SPOT_ANGLE_MIN = 1;
|
|
|
43
48
|
export const SCENE_LIGHT_SPOT_ANGLE_MAX = 89;
|
|
44
49
|
export const DEFAULT_SPOT_ANGLE_DEG = 30;
|
|
45
50
|
export const DEFAULT_SPOT_PENUMBRA = 0.3;
|
|
51
|
+
/** Light azimuth and roll bounds in degrees, symmetric: a half turn either way. */
|
|
52
|
+
export const SCENE_LIGHT_AZIMUTH_MAX = 180;
|
|
53
|
+
export const SCENE_LIGHT_ROLL_MAX = 180;
|
|
54
|
+
/** Light elevation bound in degrees, symmetric: straight up or straight down. */
|
|
55
|
+
export const SCENE_LIGHT_ELEVATION_MAX = 90;
|
|
46
56
|
/** Positional lights need falloff headroom; the slider and scene healing share this ceiling. */
|
|
47
57
|
export function sceneLightIntensityMax(type) {
|
|
48
58
|
return type === 'directional' || type === 'ambient' ? SCENE_LIGHT_INTENSITY_MAX : SCENE_LIGHT_POINT_INTENSITY_MAX;
|
|
@@ -68,6 +78,7 @@ export const LIVE2D_SCALE_MIN = 0.1;
|
|
|
68
78
|
export const LIVE2D_SCALE_MAX = 24;
|
|
69
79
|
export const VRM_SCALE_MIN = 0.05;
|
|
70
80
|
export const VRM_SCALE_MAX = 10;
|
|
81
|
+
export const SCENE_LIGHT_RANGE_MIN = 0.1;
|
|
71
82
|
// Range only *cuts off* a point light — three's 1/d² decay has extinguished it
|
|
72
83
|
// by ~18 m even at max intensity, so a slider past 20 changes nothing visible.
|
|
73
84
|
export const SCENE_LIGHT_RANGE_MAX = 20;
|
|
@@ -77,6 +88,8 @@ export const SCENE_LIGHT_SHADOW_RADIUS_MAX = 16;
|
|
|
77
88
|
export const SCENE_LIGHT_POINT_SHADOW_RADIUS_MAX = 32;
|
|
78
89
|
export const SCENE_LIGHT_SHADOW_SOURCE_SIZE_MAX = 10;
|
|
79
90
|
export const DEFAULT_LIGHT_SHADOW_SOURCE_SIZE = 0.1;
|
|
91
|
+
/** A light override's key: the baked light's traversal index, in decimal digits. */
|
|
92
|
+
export const LIGHT_OVERRIDE_KEY_RE = /^\d+$/;
|
|
80
93
|
/** Shadow softness (PCF disk radius, in shadow-map texels) ceiling for a light of `type`. */
|
|
81
94
|
export function sceneLightShadowRadiusMax(type) {
|
|
82
95
|
return type === 'point' ? SCENE_LIGHT_POINT_SHADOW_RADIUS_MAX : SCENE_LIGHT_SHADOW_RADIUS_MAX;
|
|
@@ -99,6 +112,8 @@ export const CAMERA_DAMPING_MAX = 10;
|
|
|
99
112
|
/** Ceiling for {@link SceneCameraAim}'s lookahead time, in seconds, and its smoothing. */
|
|
100
113
|
export const CAMERA_AIM_LOOKAHEAD_MAX = 1;
|
|
101
114
|
export const CAMERA_AIM_SMOOTHING_MAX = 30;
|
|
115
|
+
/** {@link SceneCameraAim}'s bias bound on each axis, symmetric: a share of the slack between its zones. */
|
|
116
|
+
export const CAMERA_AIM_BIAS_MAX = 0.5;
|
|
102
117
|
/** Ceiling for {@link SceneCameraHandheld}'s speed. */
|
|
103
118
|
export const CAMERA_HANDHELD_SPEED_MAX = 2;
|
|
104
119
|
/** Warudo's Handheld Movement defaults, switched off. */
|
|
@@ -158,6 +173,18 @@ export function cameraTrackKind(item) {
|
|
|
158
173
|
return item.ref.kind === 'vrm' ? 'model' : null;
|
|
159
174
|
return item.space === '3d' ? 'object' : null;
|
|
160
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* The layer `target` tracks in `scene` — the primary model for a primary pick, else the first model — when it
|
|
178
|
+
* is one a camera can track.
|
|
179
|
+
*/
|
|
180
|
+
export function cameraTrackItem(scene, target) {
|
|
181
|
+
// Runs per frame for each camera track, so it searches the list rather than filtering a copy.
|
|
182
|
+
const item = target.target === 'primary'
|
|
183
|
+
? (scene.items.find(i => i.kind === 'model' && i.instanceId === scene.primaryInstanceId) ??
|
|
184
|
+
scene.items.find(i => i.kind === 'model'))
|
|
185
|
+
: scene.items.find(i => i.instanceId === target.instanceId);
|
|
186
|
+
return item && cameraTrackKind(item) ? item : undefined;
|
|
187
|
+
}
|
|
161
188
|
/** Rows for the camera's follow, aim and focus pickers, in layer order. One rule, or the console offers a layer the desktop drops. */
|
|
162
189
|
export function cameraTrackCandidates(items) {
|
|
163
190
|
return items.flatMap(item => {
|
|
@@ -196,6 +223,23 @@ export const MTOON_OUTLINE_WIDTH_MAX = 2;
|
|
|
196
223
|
/** HDR headroom: past ×1 the point is pushing emissive parts over the bloom threshold. */
|
|
197
224
|
export const MTOON_EMISSIVE_MAX = 4;
|
|
198
225
|
export const MTOON_UV_ANIMATION_MAX = 4;
|
|
226
|
+
/** Each {@link MToonTuning} field's `[min, max]`: offsets −1..1, `shade` 0..1, multipliers 0 to their ceiling. */
|
|
227
|
+
export const MTOON_RANGES = {
|
|
228
|
+
shade: [0, 1],
|
|
229
|
+
shadingShift: [-1, 1],
|
|
230
|
+
shadingToony: [-1, 1],
|
|
231
|
+
giEqualization: [-1, 1],
|
|
232
|
+
normalScale: [0, MTOON_NORMAL_SCALE_MAX],
|
|
233
|
+
rim: [0, MTOON_RIM_MAX],
|
|
234
|
+
rimLift: [-1, 1],
|
|
235
|
+
rimFresnelPower: [0, MTOON_RIM_FRESNEL_POWER_MAX],
|
|
236
|
+
rimLightingMix: [-1, 1],
|
|
237
|
+
matcap: [0, MTOON_MATCAP_MAX],
|
|
238
|
+
outlineWidth: [0, MTOON_OUTLINE_WIDTH_MAX],
|
|
239
|
+
outlineLightingMix: [-1, 1],
|
|
240
|
+
emissive: [0, MTOON_EMISSIVE_MAX],
|
|
241
|
+
uvAnimation: [0, MTOON_UV_ANIMATION_MAX],
|
|
242
|
+
};
|
|
199
243
|
export function defaultMToonTuning() {
|
|
200
244
|
return {
|
|
201
245
|
shade: 1,
|
|
@@ -402,13 +446,35 @@ export const TEXT_BEVEL_RESOLUTION_MAX = 8;
|
|
|
402
446
|
export const TEXT_EMISSION_MAX = 4;
|
|
403
447
|
/** A PostScript name: printable ASCII, at most 63 chars, minus the quote and backslash that would escape `local("…")`. */
|
|
404
448
|
export const TEXT_POSTSCRIPT_NAME_RE = /^[\x21\x23-\x5b\x5d-\x7e]{1,63}$/;
|
|
449
|
+
/** A text font's CSS `font-weight` bounds. */
|
|
450
|
+
export const TEXT_FONT_WEIGHT_MIN = 1;
|
|
451
|
+
export const TEXT_FONT_WEIGHT_MAX = 1000;
|
|
452
|
+
/** A text font's CSS `font-stretch` bounds, in percent. */
|
|
453
|
+
export const TEXT_FONT_STRETCH_MIN = 50;
|
|
454
|
+
export const TEXT_FONT_STRETCH_MAX = 200;
|
|
455
|
+
/** CSS's normal face: what a text font, or a font file, that leaves its weight, slope or width unsaid reads as. */
|
|
456
|
+
export const DEFAULT_TEXT_FONT_FACE = {
|
|
457
|
+
weight: 400,
|
|
458
|
+
italic: false,
|
|
459
|
+
stretch: 100,
|
|
460
|
+
};
|
|
405
461
|
/** A prop is a mesh, so it only exists in the three.js scene; every other kind renders in both. */
|
|
406
462
|
export function objectSupportsSpace(kind, space) {
|
|
407
463
|
return kind === 'prop' ? space === '3d' : true;
|
|
408
464
|
}
|
|
465
|
+
/** The {@link ObjectContent} kinds that stream from a registered asset; the rest carry their source inline. */
|
|
466
|
+
const ASSET_CONTENT_KINDS = ['image', 'video', 'prop'];
|
|
467
|
+
/** Whether `kind` — a content kind, or an Inventory row's — is one whose asset streams into a layer. */
|
|
468
|
+
export function isAssetContentKind(kind) {
|
|
469
|
+
return isOneOf(kind, ASSET_CONTENT_KINDS);
|
|
470
|
+
}
|
|
471
|
+
/** Whether the content streams from a registered asset: image, video and prop; the rest carry their source inline. */
|
|
472
|
+
export function isAssetContent(content) {
|
|
473
|
+
return isAssetContentKind(content.kind);
|
|
474
|
+
}
|
|
409
475
|
/** The asset an object streams from, or null for kinds that carry their source inline (web, capture, text). */
|
|
410
476
|
export function contentAssetId(content) {
|
|
411
|
-
return content
|
|
477
|
+
return isAssetContent(content) ? content.assetId : null;
|
|
412
478
|
}
|
|
413
479
|
/**
|
|
414
480
|
* The one model format a space's objects can ride: the screen layer always composites
|
|
@@ -453,6 +519,14 @@ export function pinnableParents(models, items, instanceId) {
|
|
|
453
519
|
}
|
|
454
520
|
return out;
|
|
455
521
|
}
|
|
522
|
+
/**
|
|
523
|
+
* {@link pinnableParents} for an object: every model of the one format its space can ride. Nothing
|
|
524
|
+
* rides an object, so no row is ever flagged.
|
|
525
|
+
*/
|
|
526
|
+
export function objectPinParents(models, space) {
|
|
527
|
+
const format = attachableParentFormat(space);
|
|
528
|
+
return models.filter(m => m.ref.kind === format).map(m => ({ instanceId: m.instanceId, name: m.ref.name }));
|
|
529
|
+
}
|
|
456
530
|
/**
|
|
457
531
|
* Whether pinning puts an item inside its Live2D host's ArtMesh stack, where `attach.depth` places
|
|
458
532
|
* it: a Live2D item, or a 2D object as one quad. A 3D object only ever paints in front.
|
|
@@ -477,6 +551,38 @@ export function depthStopIndex(meshes, depth) {
|
|
|
477
551
|
const at = meshes.indexOf(depth.id);
|
|
478
552
|
return at < 0 ? meshes.length + 1 : at + 1;
|
|
479
553
|
}
|
|
554
|
+
/** Three non-negative finite numbers — whole ones when `int` — or null. */
|
|
555
|
+
function healTriple(raw, int) {
|
|
556
|
+
if (!Array.isArray(raw) || raw.length !== 3)
|
|
557
|
+
return null;
|
|
558
|
+
const usable = (v) => isFiniteNumber(v) && v >= 0 && (!int || Number.isInteger(v));
|
|
559
|
+
const [a, b, c] = raw;
|
|
560
|
+
return usable(a) && usable(b) && usable(c) ? [a, b, c] : null;
|
|
561
|
+
}
|
|
562
|
+
/** A stored anchor — a model's root, a VRM bone or a point on a Live2D ArtMesh — as a usable one, or null. */
|
|
563
|
+
export function healAttachAnchor(raw) {
|
|
564
|
+
if (!isRecord(raw))
|
|
565
|
+
return null;
|
|
566
|
+
if (raw.kind === 'root')
|
|
567
|
+
return { kind: 'root' };
|
|
568
|
+
if (raw.kind === 'bone')
|
|
569
|
+
return typeof raw.bone === 'string' && raw.bone !== '' ? { kind: 'bone', bone: raw.bone } : null;
|
|
570
|
+
if (raw.kind !== 'artMesh' || typeof raw.id !== 'string' || raw.id === '')
|
|
571
|
+
return null;
|
|
572
|
+
const verts = healTriple(raw.verts, true);
|
|
573
|
+
const weights = healTriple(raw.weights, false);
|
|
574
|
+
if (!verts || !weights)
|
|
575
|
+
return null;
|
|
576
|
+
const sum = weights[0] + weights[1] + weights[2];
|
|
577
|
+
// Finite weights can still sum to Infinity; dividing by it would zero the point.
|
|
578
|
+
if (!Number.isFinite(sum) || sum <= 0)
|
|
579
|
+
return null;
|
|
580
|
+
// Normalized so the point stays inside its triangle whatever was stored. A normalized triple is kept
|
|
581
|
+
// bit for bit: dividing it again can flip last bits, and hosts key anchor caches on them.
|
|
582
|
+
if (Math.abs(sum - 1) < 1e-9)
|
|
583
|
+
return { kind: 'artMesh', id: raw.id, verts, weights };
|
|
584
|
+
return { kind: 'artMesh', id: raw.id, verts, weights: [weights[0] / sum, weights[1] / sum, weights[2] / sum] };
|
|
585
|
+
}
|
|
480
586
|
export const DEFAULT_HEAD_ANGLE = {
|
|
481
587
|
multiplier: 1,
|
|
482
588
|
parallaxX: 0,
|
|
@@ -514,6 +620,8 @@ export const STORAGE_KEY_MAX_LENGTH = 128;
|
|
|
514
620
|
export const STORAGE_VALUE_MAX_LENGTH = 64 * 1024;
|
|
515
621
|
/** Keys one API key may hold; a `storage.set` that would exceed it answers `invalid-state`. */
|
|
516
622
|
export const STORAGE_KEYS_MAX = 256;
|
|
623
|
+
/** `session.identify` refuses a longer name, so a client composing one trims it to this. */
|
|
624
|
+
export const SESSION_NAME_MAX_LENGTH = 64;
|
|
517
625
|
/** `speech.play` URL ceiling — sized for a ~40 s WAV as a base64 `data:audio/*` payload. */
|
|
518
626
|
export const SPEECH_URL_MAX_LENGTH = 8_000_000;
|
|
519
627
|
/**
|
package/dist/values/lipsync.d.ts
CHANGED
|
@@ -54,19 +54,18 @@ export interface LipSyncSample {
|
|
|
54
54
|
/** Smoothed vowel weights sum to at most one; the remainder represents non-vowel audio. */
|
|
55
55
|
vowels: Record<LipSyncVowel, number>;
|
|
56
56
|
}
|
|
57
|
+
/** A capture or output device as a device picker lists it. */
|
|
58
|
+
export interface MediaDeviceOption {
|
|
59
|
+
deviceId: string;
|
|
60
|
+
label: string;
|
|
61
|
+
}
|
|
57
62
|
export interface LipSyncState {
|
|
58
63
|
config: LipSyncConfig;
|
|
59
64
|
status: 'off' | 'starting' | 'listening' | 'error';
|
|
60
65
|
error: 'permission' | 'device' | 'analysis' | null;
|
|
61
|
-
devices:
|
|
62
|
-
deviceId: string;
|
|
63
|
-
label: string;
|
|
64
|
-
}[];
|
|
66
|
+
devices: MediaDeviceOption[];
|
|
65
67
|
sample: LipSyncSample | null;
|
|
66
|
-
microphone:
|
|
67
|
-
deviceId: string;
|
|
68
|
-
label: string;
|
|
69
|
-
} | null;
|
|
68
|
+
microphone: MediaDeviceOption | null;
|
|
70
69
|
profile: Omit<LipSyncProfile, 'samples'> | null;
|
|
71
70
|
calibration: LipSyncCalibrationState | null;
|
|
72
71
|
}
|
package/dist/values/locale.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export type Locale = keyof typeof LOCALE_LABELS;
|
|
|
2
2
|
/** `ui.language` setting: an explicit locale, or follow the OS language. */
|
|
3
3
|
export type LanguageSetting = 'system' | Locale;
|
|
4
4
|
/** `ui.theme` setting: an explicit appearance, or follow the OS. */
|
|
5
|
-
export
|
|
5
|
+
export declare const THEME_SETTINGS: readonly ["system", "light", "dark"];
|
|
6
|
+
export type ThemeSetting = (typeof THEME_SETTINGS)[number];
|
|
6
7
|
/** Native-language display names for the language picker (deliberately untranslated). */
|
|
7
8
|
export declare const LOCALE_LABELS: {
|
|
8
9
|
en: string;
|
|
@@ -12,5 +13,7 @@ export declare const LOCALE_LABELS: {
|
|
|
12
13
|
};
|
|
13
14
|
/** Locales shipped in the compiled catalogs (`pnpm i18n`). */
|
|
14
15
|
export declare const LOCALES: readonly Locale[];
|
|
16
|
+
/** Every `ui.language` value, `system` first. */
|
|
17
|
+
export declare const LANGUAGE_SETTINGS: readonly LanguageSetting[];
|
|
15
18
|
/** Best supported locale for a BCP 47-ish tag: exact match first, then fuzzy per language. */
|
|
16
19
|
export declare function matchLocaleTag(tag: string): Locale;
|
package/dist/values/locale.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isOneOf, keysOf } from "./guards.js";
|
|
2
|
+
/** `ui.theme` setting: an explicit appearance, or follow the OS. */
|
|
3
|
+
export const THEME_SETTINGS = ['system', 'light', 'dark'];
|
|
2
4
|
/** Native-language display names for the language picker (deliberately untranslated). */
|
|
3
5
|
export const LOCALE_LABELS = {
|
|
4
6
|
en: 'English',
|
|
@@ -8,6 +10,8 @@ export const LOCALE_LABELS = {
|
|
|
8
10
|
};
|
|
9
11
|
/** Locales shipped in the compiled catalogs (`pnpm i18n`). */
|
|
10
12
|
export const LOCALES = keysOf(LOCALE_LABELS);
|
|
13
|
+
/** Every `ui.language` value, `system` first. */
|
|
14
|
+
export const LANGUAGE_SETTINGS = ['system', ...LOCALES];
|
|
11
15
|
/** Best supported locale for a BCP 47-ish tag: exact match first, then fuzzy per language. */
|
|
12
16
|
export function matchLocaleTag(tag) {
|
|
13
17
|
if (isOneOf(tag, LOCALES))
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { AttachAnchor } from '../wire/types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The point `z` grows and shrinks the model around: its centre (`root`), or a point on one of its
|
|
4
|
+
* ArtMeshes, taken where that mesh sits in the model's rest pose so head motion never moves it.
|
|
5
|
+
*/
|
|
6
|
+
export type ModelMovementPivot = Extract<AttachAnchor, {
|
|
7
|
+
kind: 'root' | 'artMesh';
|
|
8
|
+
}>;
|
|
1
9
|
/**
|
|
2
10
|
* VTube Studio's Movement Config: how far tracked face position slides (`x`, `y`) and scales (`z`)
|
|
3
11
|
* a whole Live2D model. It moves the model itself and never writes a parameter, so it stacks with
|
|
@@ -9,21 +17,28 @@ export interface ModelMovementConfig {
|
|
|
9
17
|
x: number;
|
|
10
18
|
y: number;
|
|
11
19
|
z: number;
|
|
20
|
+
/** Persona-only: VTS has no such setting, so it lives in the sidecar even beside a `.vtube.json`. */
|
|
21
|
+
pivot: ModelMovementPivot;
|
|
12
22
|
/** Smoothing levels 0–100, read once when the model loads; file-only, as in VTS. */
|
|
13
23
|
smoothingX: number;
|
|
14
24
|
smoothingY: number;
|
|
15
25
|
smoothingZ: number;
|
|
16
26
|
}
|
|
17
|
-
/**
|
|
18
|
-
export
|
|
27
|
+
/** The per-axis smoothing levels, read once when the model loads and never edited. */
|
|
28
|
+
export type ModelMovementSmoothing = Pick<ModelMovementConfig, 'smoothingX' | 'smoothingY' | 'smoothingZ'>;
|
|
29
|
+
/**
|
|
30
|
+
* The editor's limit: an edited amount is a whole number within ±50. VTS's own sliders stop at ±20
|
|
31
|
+
* (`MAX_MOVEMENT`), and releasing one there writes all three axes back clamped to that.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MODEL_MOVEMENT_MAX = 50;
|
|
19
34
|
/** VTS's `ModelPositionMovement` field initializers — a model nobody configured moves by default. */
|
|
20
35
|
export declare const DEFAULT_MODEL_MOVEMENT: Readonly<ModelMovementConfig>;
|
|
21
36
|
/**
|
|
22
37
|
* A stored or relayed configuration, read per field over the defaults. Amounts are taken as
|
|
23
|
-
* written:
|
|
38
|
+
* written: the editors clamp what they write, never what a file holds.
|
|
24
39
|
*/
|
|
25
40
|
export declare function healModelMovement(raw: unknown): ModelMovementConfig;
|
|
26
41
|
/** What the Movement Config editor changes; a file's smoothing levels are left as they are. */
|
|
27
|
-
export type ModelMovementEdit = Partial<Pick<ModelMovementConfig, 'enabled' | 'x' | 'y' | 'z'>>;
|
|
42
|
+
export type ModelMovementEdit = Partial<Pick<ModelMovementConfig, 'enabled' | 'x' | 'y' | 'z' | 'pivot'>>;
|
|
28
43
|
/** An edit healed to the editor's limits; an absent or unusable field is dropped, not defaulted. */
|
|
29
44
|
export declare function healModelMovementEdit(raw: unknown): ModelMovementEdit;
|
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import { finiteOr, isFiniteNumber, isRecord } from "./guards.js";
|
|
2
|
-
import { clamp } from "./limits.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
2
|
+
import { clamp, healAttachAnchor } from "./limits.js";
|
|
3
|
+
/**
|
|
4
|
+
* The editor's limit: an edited amount is a whole number within ±50. VTS's own sliders stop at ±20
|
|
5
|
+
* (`MAX_MOVEMENT`), and releasing one there writes all three axes back clamped to that.
|
|
6
|
+
*/
|
|
7
|
+
export const MODEL_MOVEMENT_MAX = 50;
|
|
5
8
|
/** VTS's `ModelPositionMovement` field initializers — a model nobody configured moves by default. */
|
|
6
9
|
export const DEFAULT_MODEL_MOVEMENT = {
|
|
7
10
|
enabled: true,
|
|
8
11
|
x: 6,
|
|
9
12
|
y: 8,
|
|
10
13
|
z: 11,
|
|
14
|
+
pivot: { kind: 'root' },
|
|
11
15
|
smoothingX: 10,
|
|
12
16
|
smoothingY: 10,
|
|
13
17
|
smoothingZ: 10,
|
|
14
18
|
};
|
|
19
|
+
/** A stored or relayed pivot as a usable one: any anchor but a bone, which no Live2D model has. */
|
|
20
|
+
function healPivot(raw) {
|
|
21
|
+
const anchor = healAttachAnchor(raw);
|
|
22
|
+
return anchor?.kind === 'bone' ? null : anchor;
|
|
23
|
+
}
|
|
15
24
|
/**
|
|
16
25
|
* A stored or relayed configuration, read per field over the defaults. Amounts are taken as
|
|
17
|
-
* written:
|
|
26
|
+
* written: the editors clamp what they write, never what a file holds.
|
|
18
27
|
*/
|
|
19
28
|
export function healModelMovement(raw) {
|
|
20
29
|
const r = isRecord(raw) ? raw : {};
|
|
@@ -24,6 +33,7 @@ export function healModelMovement(raw) {
|
|
|
24
33
|
x: finiteOr(r.x, d.x),
|
|
25
34
|
y: finiteOr(r.y, d.y),
|
|
26
35
|
z: finiteOr(r.z, d.z),
|
|
36
|
+
pivot: healPivot(r.pivot) ?? d.pivot,
|
|
27
37
|
smoothingX: finiteOr(r.smoothingX, d.smoothingX),
|
|
28
38
|
smoothingY: finiteOr(r.smoothingY, d.smoothingY),
|
|
29
39
|
smoothingZ: finiteOr(r.smoothingZ, d.smoothingZ),
|
|
@@ -40,5 +50,8 @@ export function healModelMovementEdit(raw) {
|
|
|
40
50
|
if (isFiniteNumber(v))
|
|
41
51
|
edit[axis] = clamp(Math.round(v), -MODEL_MOVEMENT_MAX, MODEL_MOVEMENT_MAX);
|
|
42
52
|
}
|
|
53
|
+
const pivot = healPivot(r.pivot);
|
|
54
|
+
if (pivot)
|
|
55
|
+
edit.pivot = pivot;
|
|
43
56
|
return edit;
|
|
44
57
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { SceneEnvironment, SceneVolumetricLighting } from '../wire/types.ts';
|
|
2
2
|
/** Slider and healing metadata for the haze's numeric settings. */
|
|
3
3
|
export declare const SCENE_VOLUMETRIC_LIGHTING_SPECS: {
|
|
4
4
|
readonly density: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EFFECTS_QUALITY_LEVELS } from "
|
|
1
|
+
import { EFFECTS_QUALITY_LEVELS } from "./effect-schema.js";
|
|
2
2
|
import { finiteOr, isOneOf, isRecord } from "./guards.js";
|
|
3
3
|
import { clamp } from "./limits.js";
|
|
4
4
|
/** Slider and healing metadata for the haze's numeric settings. */
|
package/dist/wire/envelope.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isRecord } from "../values/guards.js";
|
|
2
2
|
import { isApiErrorCode } from "./errors.js";
|
|
3
3
|
import { isEventName } from "./events.js";
|
|
4
|
-
import { isAppCapability } from "./types.js";
|
|
4
|
+
import { canonicalAppCapability, isAppCapability } from "./types.js";
|
|
5
5
|
/**
|
|
6
6
|
* Parse one inbound client frame. Returns the request, or an error code telling
|
|
7
7
|
* the server what to answer: `parse-error` for junk bytes, `invalid-request`
|
|
@@ -47,8 +47,11 @@ export function parseServerMessage(raw) {
|
|
|
47
47
|
const { name, version, platform } = v.app;
|
|
48
48
|
if (typeof name !== 'string' || typeof version !== 'string' || typeof platform !== 'string')
|
|
49
49
|
return null;
|
|
50
|
-
// Tolerant both ways: absent on older servers, unknown names from newer ones dropped.
|
|
51
|
-
|
|
50
|
+
// Tolerant both ways: absent on older servers, unknown names from newer ones dropped. Old names fold
|
|
51
|
+
// into their replacements, which a newer server also lists.
|
|
52
|
+
const names = Array.isArray(v.app.capabilities) ? v.app.capabilities : [];
|
|
53
|
+
const folded = names.map(n => (typeof n === 'string' ? canonicalAppCapability(n) : n));
|
|
54
|
+
const capabilities = [...new Set(folded.filter(isAppCapability))];
|
|
52
55
|
return { kind: 'hello', protocol: v.protocol, app: { name, version, platform, capabilities } };
|
|
53
56
|
}
|
|
54
57
|
case 'response':
|
package/dist/wire/methods.d.ts
CHANGED
|
@@ -4,8 +4,9 @@ import type { SpawnTransition } from '../values/item-transition.ts';
|
|
|
4
4
|
import type { LipSyncCalibrationCommand, LipSyncConfig, LipSyncMode, LipSyncState } from '../values/lipsync.ts';
|
|
5
5
|
import type { ModelMovementConfig, ModelMovementEdit } from '../values/model-movement.ts';
|
|
6
6
|
import type { SceneInspection } from '../values/stage-info.ts';
|
|
7
|
+
import type { AppInfo } from './envelope.ts';
|
|
7
8
|
import type { EventName } from './events.ts';
|
|
8
|
-
import type { AnchorOption,
|
|
9
|
+
import type { AnchorOption, AssetKind, AssetRef, Attach, AttachHeadAngle, AutomationInfo, BindingInput, ChannelBindings, Expression, ExpressionPersistence, FontFamilyInfo, HotkeyConfig, HotkeyState, InjectEntry, InjectTarget, InstanceRuntime, JsonValue, LayerEffectKey, LayerEffects, LayerEffectsPatch, MediaPipeConfig, ModelInfo, ModelRef, MotionGroup, MToonTuning, ObjectContent, ObjectLightOverride, ObjectSpace, Place2D, Place3D, PlayingMotion, PoseSourceId, PoseStatus, Scene, SceneItem, SceneLight, SceneLightCameraFollowOptions, SceneModelItem, ScenePatch, SceneState, ScreenPlacement, Settings, SettingsPatch, TrackingLostBehavior, TrackingSourceConfig, TrackingSourceId, TrackingSourceKind, TrackingStatus, VrmPlacement } from './types.ts';
|
|
9
10
|
/** Marker for methods that take no parameters; the client lets you omit the argument. */
|
|
10
11
|
export type EmptyRequest = Record<never, never>;
|
|
11
12
|
/** Marker for methods whose success response carries no data. */
|
|
@@ -85,7 +86,10 @@ export interface InstanceSetVisibleRequest {
|
|
|
85
86
|
visible: boolean;
|
|
86
87
|
}
|
|
87
88
|
export type InstanceSetVisibleResponse = EmptyResponse;
|
|
88
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Edits one model or object's source effects; gate on the `layer-effects` app capability, and a `2d`
|
|
91
|
+
* object on `layer-effects-2d`.
|
|
92
|
+
*/
|
|
89
93
|
export interface InstanceSetEffectsRequest {
|
|
90
94
|
instanceId: string;
|
|
91
95
|
effects: LayerEffectsPatch;
|
|
@@ -96,13 +100,12 @@ export interface InstanceSetEffectsResponse {
|
|
|
96
100
|
effects: LayerEffects;
|
|
97
101
|
effectLayers: LayerEffectKey[];
|
|
98
102
|
}
|
|
99
|
-
/**
|
|
100
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The channels' source bindings (`ChannelBindings`). Omitted fields keep their binding;
|
|
105
|
+
* null stops tracking that channel for this instance.
|
|
106
|
+
*/
|
|
107
|
+
export interface InstanceSetTrackingSourcesRequest extends Partial<ChannelBindings>, Partial<Pick<SceneModelItem, 'handTrackingMode'>> {
|
|
101
108
|
instanceId: string;
|
|
102
|
-
faceSourceId?: string | null;
|
|
103
|
-
poseSourceId?: string | null;
|
|
104
|
-
handSourceId?: string | null;
|
|
105
|
-
handTrackingMode?: HandTrackingMode;
|
|
106
109
|
}
|
|
107
110
|
export type InstanceSetTrackingSourcesResponse = EmptyResponse;
|
|
108
111
|
export interface InstanceSetControllerMovementRequest {
|
|
@@ -173,7 +176,7 @@ export interface InstanceSetIdleRequest {
|
|
|
173
176
|
idleClip?: string;
|
|
174
177
|
/** Live2D only: a motion file from `motion.list`, or null for the model's `Idle` group. */
|
|
175
178
|
idleMotion?: string | null;
|
|
176
|
-
/** The `tracking-lost` app capability carries these three. */
|
|
179
|
+
/** The `idle-tracking-lost` app capability carries these three. */
|
|
177
180
|
trackingLostBehavior?: TrackingLostBehavior;
|
|
178
181
|
/** Live2D only: a motion file from `motion.list`, or null for none. */
|
|
179
182
|
trackingLostMotion?: string | null;
|
|
@@ -215,7 +218,7 @@ export interface ObjectSetContentRequest {
|
|
|
215
218
|
content: ObjectContent;
|
|
216
219
|
}
|
|
217
220
|
export type ObjectSetContentResponse = EmptyResponse;
|
|
218
|
-
/** A `text` object's text alone, under `text
|
|
221
|
+
/** A `text` object's text alone, under `object-text`. */
|
|
219
222
|
export interface ObjectSetTextRequest {
|
|
220
223
|
instanceId: string;
|
|
221
224
|
/** Null drops a transient text, showing the saved one again. */
|
|
@@ -526,13 +529,9 @@ export interface StageCaptureResponse {
|
|
|
526
529
|
height: number;
|
|
527
530
|
}
|
|
528
531
|
export type AppInfoRequest = EmptyRequest;
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
version: string;
|
|
532
|
-
platform: string;
|
|
532
|
+
/** What `hello` carries, the app identity plus its protocol version. */
|
|
533
|
+
export interface AppInfoResponse extends AppInfo {
|
|
533
534
|
protocol: number;
|
|
534
|
-
/** Same list `hello` carries; gate features on it, never on `version`. */
|
|
535
|
-
capabilities: AppCapability[];
|
|
536
535
|
}
|
|
537
536
|
export type AppLocalAddressesRequest = EmptyRequest;
|
|
538
537
|
export interface AppLocalAddressesResponse {
|
|
@@ -662,7 +661,7 @@ export interface ModelSetMovementRequest {
|
|
|
662
661
|
export interface ModelSetMovementResponse {
|
|
663
662
|
/** The configuration now moving the model, whether or not it reached the file. */
|
|
664
663
|
movement: ModelMovementConfig;
|
|
665
|
-
/** False when
|
|
664
|
+
/** False when any part missed its file — a refused write (read-only, unreadable) — or no field was usable. */
|
|
666
665
|
saved: boolean;
|
|
667
666
|
}
|
|
668
667
|
export interface InstanceSetBreathRequest {
|
package/dist/wire/protocol.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare const DEFAULT_API_PORT = 25034;
|
|
|
7
7
|
export declare const DEFAULT_API_HOST = "127.0.0.1";
|
|
8
8
|
/** Scene activation waits for asset preparation before committing the new scene. */
|
|
9
9
|
export declare const SCENE_ACTIVATION_TIMEOUT_MS = 120000;
|
|
10
|
+
/** How long a caller waits for `method`'s answer: scene activation prepares assets first, the rest get `fallbackMs`. */
|
|
11
|
+
export declare function requestTimeoutMs(method: string, fallbackMs: number): number;
|
|
10
12
|
/** Server close code: the session's API key was revoked. Terminal — the client must not redial. */
|
|
11
13
|
export declare const CLOSE_KEY_REVOKED = 4001;
|
|
12
14
|
/** Server close code: the user disconnected this session from Persona's settings. Terminal — the client must not redial. */
|
package/dist/wire/protocol.js
CHANGED
|
@@ -6,6 +6,10 @@ export const DEFAULT_API_PORT = 25034;
|
|
|
6
6
|
export const DEFAULT_API_HOST = '127.0.0.1';
|
|
7
7
|
/** Scene activation waits for asset preparation before committing the new scene. */
|
|
8
8
|
export const SCENE_ACTIVATION_TIMEOUT_MS = 120_000;
|
|
9
|
+
/** How long a caller waits for `method`'s answer: scene activation prepares assets first, the rest get `fallbackMs`. */
|
|
10
|
+
export function requestTimeoutMs(method, fallbackMs) {
|
|
11
|
+
return method === 'scene.activate' ? SCENE_ACTIVATION_TIMEOUT_MS : fallbackMs;
|
|
12
|
+
}
|
|
9
13
|
/** Server close code: the session's API key was revoked. Terminal — the client must not redial. */
|
|
10
14
|
export const CLOSE_KEY_REVOKED = 4001;
|
|
11
15
|
/** Server close code: the user disconnected this session from Persona's settings. Terminal — the client must not redial. */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type * as z from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* A `z.object` shape naming every key of `T`, optional ones included. `satisfies` it so a field added
|
|
4
|
+
* to `T` fails to compile until the schema takes it — `z.object` strips the keys it does not name.
|
|
5
|
+
*/
|
|
6
|
+
export type Fields<T> = {
|
|
7
|
+
[K in keyof T]-?: z.ZodType;
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -170,12 +170,12 @@ export declare const requestSchemas: {
|
|
|
170
170
|
}, z.core.$strip>;
|
|
171
171
|
'tracking.addSource': z.ZodObject<{
|
|
172
172
|
kind: z.ZodEnum<{
|
|
173
|
+
mediapipe: "mediapipe";
|
|
173
174
|
"persona-ios": "persona-ios";
|
|
174
175
|
ifacialmocap: "ifacialmocap";
|
|
175
176
|
"vts-ios": "vts-ios";
|
|
176
177
|
vmc: "vmc";
|
|
177
178
|
mocopi: "mocopi";
|
|
178
|
-
mediapipe: "mediapipe";
|
|
179
179
|
}>;
|
|
180
180
|
name: z.ZodOptional<z.ZodString>;
|
|
181
181
|
phoneIp: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -221,7 +221,7 @@ export declare const requestSchemas: {
|
|
|
221
221
|
}, z.core.$strip>;
|
|
222
222
|
'model.setMovement': z.ZodObject<{
|
|
223
223
|
modelId: z.ZodOptional<z.ZodString>;
|
|
224
|
-
movement: z.ZodCustom<Partial<Pick<import("../../index.ts").ModelMovementConfig, "enabled" | "x" | "y" | "z">>, Partial<Pick<import("../../index.ts").ModelMovementConfig, "enabled" | "x" | "y" | "z">>>;
|
|
224
|
+
movement: z.ZodCustom<Partial<Pick<import("../../index.ts").ModelMovementConfig, "enabled" | "x" | "y" | "z" | "pivot">>, Partial<Pick<import("../../index.ts").ModelMovementConfig, "enabled" | "x" | "y" | "z" | "pivot">>>;
|
|
225
225
|
}, z.core.$strip>;
|
|
226
226
|
'asset.register': z.ZodObject<{
|
|
227
227
|
path: z.ZodString;
|