@laplace.live/persona-sdk 1.21.0 → 1.23.0

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.
@@ -94,6 +94,78 @@ export const SCENE_FOV_MAX = 90;
94
94
  /** Nonsingular authored field of view in degrees, including every valid whole-degree VMD key. */
95
95
  export const CAMERA_FOV_MIN = 1;
96
96
  export const CAMERA_FOV_MAX = 179;
97
+ /** Ceiling for each follow and aim damping, in seconds. */
98
+ export const CAMERA_DAMPING_MAX = 10;
99
+ /** Ceiling for {@link SceneCameraAim}'s lookahead time, in seconds, and its smoothing. */
100
+ export const CAMERA_AIM_LOOKAHEAD_MAX = 1;
101
+ export const CAMERA_AIM_SMOOTHING_MAX = 30;
102
+ /** Ceiling for {@link SceneCameraHandheld}'s speed. */
103
+ export const CAMERA_HANDHELD_SPEED_MAX = 2;
104
+ /** Warudo's Handheld Movement defaults, switched off. */
105
+ export const DEFAULT_CAMERA_HANDHELD = { enabled: false, intensity: 0.5, speed: 1 };
106
+ /**
107
+ * A fresh follow's damping: Warudo's second of position lag, and half a second on each turn so a
108
+ * tracked head's jitter never shakes the view.
109
+ */
110
+ export const DEFAULT_CAMERA_FOLLOW_DAMPING = { x: 1, y: 1, z: 1, yaw: 0.5, pitch: 0.5, roll: 0.5 };
111
+ /** Where a fresh follow, aim or focus tracks a model; an object is always tracked by its root. */
112
+ export const DEFAULT_CAMERA_ANCHOR = { kind: 'bone', bone: 'head' };
113
+ /** A fresh follow's settings: position only. */
114
+ export function defaultCameraFollowSettings() {
115
+ return { anchor: DEFAULT_CAMERA_ANCHOR, binding: 'world', damping: { ...DEFAULT_CAMERA_FOLLOW_DAMPING } };
116
+ }
117
+ /** A fresh follow of `target`, position only. */
118
+ export function defaultCameraFollow(target) {
119
+ return { ...target, ...defaultCameraFollowSettings() };
120
+ }
121
+ /** A fresh aim's settings, Warudo's Composer defaults: centred, no dead zone, 0.8 soft zone. */
122
+ export function defaultCameraAimSettings() {
123
+ return {
124
+ anchor: DEFAULT_CAMERA_ANCHOR,
125
+ screenX: 0.5,
126
+ screenY: 0.5,
127
+ deadZone: { width: 0, height: 0 },
128
+ softZone: { width: 0.8, height: 0.8 },
129
+ bias: { x: 0, y: 0 },
130
+ damping: { horizontal: 0.5, vertical: 0.5 },
131
+ lookahead: { time: 0, smoothing: 0.5, ignoreY: false },
132
+ };
133
+ }
134
+ /** A fresh aim at `target`. */
135
+ export function defaultCameraAim(target) {
136
+ return { ...target, ...defaultCameraAimSettings() };
137
+ }
138
+ /** A fresh focus's settings, refocusing about as fast as Warudo's default focusing speed. */
139
+ export function defaultCameraFocusSettings() {
140
+ return { anchor: DEFAULT_CAMERA_ANCHOR, damping: 0.2 };
141
+ }
142
+ /** A fresh focus on `target`. */
143
+ export function defaultCameraFocus(target) {
144
+ return { ...target, ...defaultCameraFocusSettings() };
145
+ }
146
+ /** A follow's, aim's or focus's settings alone, without the layer it names. */
147
+ export function cameraTrackSettingsOf(track) {
148
+ const { instanceId: _instanceId, target: _target, ...settings } = track;
149
+ return settings;
150
+ }
151
+ /** The instance `target` tracks in a scene whose primary model is `primaryInstanceId`. */
152
+ export function cameraTrackInstanceId(target, primaryInstanceId) {
153
+ return target.target === 'primary' ? primaryInstanceId : target.instanceId;
154
+ }
155
+ /** How a camera can track `item`: by any anchor on a VRM model, by the root of a 3D object, or not at all. */
156
+ export function cameraTrackKind(item) {
157
+ if (item.kind === 'model')
158
+ return item.ref.kind === 'vrm' ? 'model' : null;
159
+ return item.space === '3d' ? 'object' : null;
160
+ }
161
+ /** 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
+ export function cameraTrackCandidates(items) {
163
+ return items.flatMap(item => {
164
+ const kind = cameraTrackKind(item);
165
+ const name = item.kind === 'model' ? item.ref.name : item.name;
166
+ return kind ? [{ instanceId: item.instanceId, name, model: kind === 'model' }] : [];
167
+ });
168
+ }
97
169
  /** Manual navigation stops short of ±90°; authored camera elevations remain unrestricted. */
98
170
  export const ELEVATION_LIMIT = (80 * Math.PI) / 180;
99
171
  /** Manual navigation distance bounds; authored cameras retain signed, unrestricted distances. */
@@ -292,11 +364,49 @@ export const WEB_SIZE_MAX = 7680;
292
364
  /** Web object paint-rate bounds, frames per second. */
293
365
  export const WEB_FPS_MIN = 1;
294
366
  export const WEB_FPS_MAX = 60;
367
+ /** Chroma key tolerance bounds, OBS's 1–1000 ÷ 1000; the floor keeps the edge ramps from dividing by zero. */
368
+ export const CHROMA_KEY_TOLERANCE_MIN = 0.001;
369
+ export const CHROMA_KEY_TOLERANCE_MAX = 1;
370
+ /** OBS's Chroma Key defaults: green, similarity 400, smoothness 80, spill reduction 100. */
371
+ export const DEFAULT_CHROMA_KEY = {
372
+ enabled: false,
373
+ color: '#00ff00',
374
+ similarity: 0.4,
375
+ smoothness: 0.08,
376
+ spill: 0.1,
377
+ };
378
+ /** Whether a chroma key can reach the content's pixels: image, video and webpage quads. */
379
+ export function isKeyableContent(content) {
380
+ return content.kind === 'image' || content.kind === 'video' || content.kind === 'web';
381
+ }
382
+ /** Text object bounds: characters, em size in px, and the box and paint extents in px. */
383
+ export const TEXT_LENGTH_MAX = 10_000;
384
+ export const TEXT_SIZE_MIN = 4;
385
+ export const TEXT_SIZE_MAX = 1000;
386
+ export const TEXT_SHEAR_MAX = 1;
387
+ /** Letter and word spacing, em. */
388
+ export const TEXT_SPACING_MIN = -0.5;
389
+ export const TEXT_SPACING_MAX = 2;
390
+ export const TEXT_LINE_HEIGHT_MIN = 0.5;
391
+ export const TEXT_LINE_HEIGHT_MAX = 4;
392
+ export const TEXT_BOX_MAX = 7680;
393
+ export const TEXT_STROKE_MAX = 100;
394
+ export const TEXT_SHADOW_OFFSET_MAX = 200;
395
+ export const TEXT_SHADOW_BLUR_MAX = 100;
396
+ export const TEXT_PLATE_PADDING_MAX = 500;
397
+ export const TEXT_PLATE_RADIUS_MAX = 500;
398
+ /** 3D text: extrusion and bevel in em, bevel segments, and emission strength. */
399
+ export const TEXT_EXTRUDE_MAX = 2;
400
+ export const TEXT_BEVEL_MAX = 0.2;
401
+ export const TEXT_BEVEL_RESOLUTION_MAX = 8;
402
+ export const TEXT_EMISSION_MAX = 4;
403
+ /** A PostScript name: printable ASCII, at most 63 chars, minus the quote and backslash that would escape `local("…")`. */
404
+ export const TEXT_POSTSCRIPT_NAME_RE = /^[\x21\x23-\x5b\x5d-\x7e]{1,63}$/;
295
405
  /** A prop is a mesh, so it only exists in the three.js scene; every other kind renders in both. */
296
406
  export function objectSupportsSpace(kind, space) {
297
407
  return kind === 'prop' ? space === '3d' : true;
298
408
  }
299
- /** The asset an object streams from, or null for kinds that carry their source inline (web, capture). */
409
+ /** The asset an object streams from, or null for kinds that carry their source inline (web, capture, text). */
300
410
  export function contentAssetId(content) {
301
411
  return content.kind === 'image' || content.kind === 'video' || content.kind === 'prop' ? content.assetId : null;
302
412
  }
@@ -367,24 +477,27 @@ export function depthStopIndex(meshes, depth) {
367
477
  const at = meshes.indexOf(depth.id);
368
478
  return at < 0 ? meshes.length + 1 : at + 1;
369
479
  }
370
- /** A fresh pin onto `parentInstanceId`: root anchor, in front of the model, no response tuning. */
371
- export function defaultAttach(parentInstanceId) {
480
+ export const DEFAULT_HEAD_ANGLE = {
481
+ multiplier: 1,
482
+ parallaxX: 0,
483
+ parallaxY: 0,
484
+ smoothing: 15,
485
+ };
486
+ /**
487
+ * A fresh pin onto `parentInstanceId`: root anchor, in front of the model. A screen-space item (a
488
+ * Live2D model, a 2D object) follows the host's head at the default response; others take none.
489
+ */
490
+ export function defaultAttach(parentInstanceId, screenSpace = false) {
372
491
  return {
373
492
  parentInstanceId,
374
493
  anchor: { kind: 'root' },
375
494
  followRotation: true,
376
495
  depth: { kind: 'front' },
377
496
  split: null,
378
- headAngle: null,
497
+ headAngle: screenSpace ? { ...DEFAULT_HEAD_ANGLE } : null,
379
498
  elasticity: null,
380
499
  };
381
500
  }
382
- export const DEFAULT_HEAD_ANGLE = {
383
- multiplier: 1,
384
- parallaxX: 0,
385
- parallaxY: 0,
386
- smoothing: 15,
387
- };
388
501
  export const ATTACH_MULTIPLIER_MIN = -2;
389
502
  export const ATTACH_MULTIPLIER_MAX = 2;
390
503
  /** Parallax slider ceiling, symmetric: stage px at a full head turn. */
@@ -19,6 +19,11 @@ export interface SceneAssetInfo {
19
19
  extensions: string[] | null;
20
20
  mediaSize: ObjectMediaSize | null;
21
21
  }
22
+ /** One scene object's loaded metadata. */
23
+ export interface SceneObjectInfo extends SceneAssetInfo {
24
+ /** A text object's transient text, null while it shows the saved one; absent on other kinds and older hosts. */
25
+ liveText?: string | null;
26
+ }
22
27
  /** Metadata for the active scene, tagged so a response from a previous scene can be discarded. */
23
28
  export interface SceneInspection {
24
29
  sceneId: string;
@@ -26,5 +31,5 @@ export interface SceneInspection {
26
31
  /** The set's own bounding radius before placement scale, for `cameraDistanceLimits`; omitted by older hosts. */
27
32
  radius?: number | null;
28
33
  };
29
- objects: Record<string, SceneAssetInfo>;
34
+ objects: Record<string, SceneObjectInfo>;
30
35
  }
@@ -30,6 +30,14 @@ export interface EventMap {
30
30
  instanceId: string;
31
31
  format: ModelFormat;
32
32
  };
33
+ /**
34
+ * An instance's motion or expression list changed without a reload — a recorded motion, an edited
35
+ * expression file, or a registered clip a VRM lists: re-run `motion.list` or `expression.list`.
36
+ */
37
+ 'instance.listing': {
38
+ instanceId: string;
39
+ listing: 'motions' | 'expressions';
40
+ };
33
41
  /** `weights` rides along on instances with `expression-weights`; a drag is not echoed (see `expression.setWeight`). */
34
42
  'expression.changed': {
35
43
  instanceId: string;
@@ -8,6 +8,7 @@ export const EVENT_NAMES = Object.keys({
8
8
  'automation.state': true,
9
9
  'tracking.status': true,
10
10
  'instance.loaded': true,
11
+ 'instance.listing': true,
11
12
  'expression.changed': true,
12
13
  'motion.started': true,
13
14
  'motion.ended': true,
@@ -5,7 +5,7 @@ import type { LipSyncCalibrationCommand, LipSyncConfig, LipSyncMode, LipSyncStat
5
5
  import type { ModelMovementConfig, ModelMovementEdit } from '../values/model-movement.ts';
6
6
  import type { SceneInspection } from '../values/stage-info.ts';
7
7
  import type { EventName } from './events.ts';
8
- import type { AnchorOption, AppCapability, AssetKind, AssetRef, Attach, AttachHeadAngle, AutomationInfo, BindingInput, Expression, ExpressionPersistence, HandTrackingMode, 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, ScenePatch, SceneState, ScreenPlacement, Settings, SettingsPatch, TrackingLostBehavior, TrackingSourceConfig, TrackingSourceId, TrackingSourceKind, TrackingStatus, VrmPlacement } from './types.ts';
8
+ import type { AnchorOption, AppCapability, AssetKind, AssetRef, Attach, AttachHeadAngle, AutomationInfo, BindingInput, Expression, ExpressionPersistence, FontFamilyInfo, HandTrackingMode, 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, ScenePatch, SceneState, ScreenPlacement, Settings, SettingsPatch, TrackingLostBehavior, TrackingSourceConfig, TrackingSourceId, TrackingSourceKind, TrackingStatus, VrmPlacement } from './types.ts';
9
9
  /** Marker for methods that take no parameters; the client lets you omit the argument. */
10
10
  export type EmptyRequest = Record<never, never>;
11
11
  /** Marker for methods whose success response carries no data. */
@@ -215,6 +215,15 @@ export interface ObjectSetContentRequest {
215
215
  content: ObjectContent;
216
216
  }
217
217
  export type ObjectSetContentResponse = EmptyResponse;
218
+ /** A `text` object's text alone, under `text-objects`. */
219
+ export interface ObjectSetTextRequest {
220
+ instanceId: string;
221
+ /** Null drops a transient text, showing the saved one again. */
222
+ text: string | null;
223
+ /** Show it without saving: no `scenes.json` write and no undo step. */
224
+ transient?: boolean;
225
+ }
226
+ export type ObjectSetTextResponse = EmptyResponse;
218
227
  export interface ObjectSetSpaceRequest {
219
228
  instanceId: string;
220
229
  space: ObjectSpace;
@@ -361,6 +370,11 @@ export type AssetListRequest = EmptyRequest;
361
370
  export interface AssetListResponse {
362
371
  assets: AssetRef[];
363
372
  }
373
+ export type FontsListRequest = EmptyRequest;
374
+ /** The host machine's installed families — what a text object's `font` can name. */
375
+ export interface FontsListResponse {
376
+ families: FontFamilyInfo[];
377
+ }
364
378
  /** Register a model entry file under a user-configured plugin folder. */
365
379
  export interface ModelRegisterRequest {
366
380
  path: string;
@@ -887,6 +901,10 @@ export interface MethodMap {
887
901
  request: ObjectSetContentRequest;
888
902
  response: ObjectSetContentResponse;
889
903
  };
904
+ 'object.setText': {
905
+ request: ObjectSetTextRequest;
906
+ response: ObjectSetTextResponse;
907
+ };
890
908
  'object.setSpace': {
891
909
  request: ObjectSetSpaceRequest;
892
910
  response: ObjectSetSpaceResponse;
@@ -988,6 +1006,10 @@ export interface MethodMap {
988
1006
  request: AssetListRequest;
989
1007
  response: AssetListResponse;
990
1008
  };
1009
+ 'fonts.list': {
1010
+ request: FontsListRequest;
1011
+ response: FontsListResponse;
1012
+ };
991
1013
  'model.register': {
992
1014
  request: ModelRegisterRequest;
993
1015
  response: ModelRegisterResponse;
@@ -35,6 +35,7 @@ export const METHOD_NAMES = Object.keys({
35
35
  'object.addMany': true,
36
36
  'object.rename': true,
37
37
  'object.setContent': true,
38
+ 'object.setText': true,
38
39
  'object.setSpace': true,
39
40
  'object.setPlacement': true,
40
41
  'object.attach': true,
@@ -63,6 +64,7 @@ export const METHOD_NAMES = Object.keys({
63
64
  'model.getMovement': true,
64
65
  'model.setMovement': true,
65
66
  'asset.list': true,
67
+ 'fonts.list': true,
66
68
  'asset.register': true,
67
69
  'registry.thumbnail': true,
68
70
  'settings.get': true,
@@ -7,8 +7,8 @@ export declare const SceneTransitionSchema: z.ZodObject<{
7
7
  image: "image";
8
8
  video: "video";
9
9
  circle: "circle";
10
- cut: "cut";
11
10
  fade: "fade";
11
+ cut: "cut";
12
12
  wipe: "wipe";
13
13
  }>;
14
14
  durationMs: z.ZodNumber;
@@ -234,6 +234,7 @@ export declare const requestSchemas: {
234
234
  lut: "lut";
235
235
  animation: "animation";
236
236
  cameraMotion: "cameraMotion";
237
+ lyrics: "lyrics";
237
238
  }>>;
238
239
  }, z.core.$strip>;
239
240
  'settings.patch': z.ZodObject<{
@@ -328,12 +329,13 @@ export declare const requestSchemas: {
328
329
  scale: z.ZodOptional<z.ZodNumber>;
329
330
  ttlMs: z.ZodOptional<z.ZodNumber>;
330
331
  transition: z.ZodOptional<z.ZodObject<{
331
- style: z.ZodOptional<z.ZodEnum<{
332
- glitch: "glitch";
333
- dither: "dither";
332
+ style: z.ZodOptional<z.ZodPipe<z.ZodEnum<{
334
333
  pop: "pop";
334
+ glitch: "glitch";
335
+ fade: "fade";
335
336
  cut: "cut";
336
- }>>;
337
+ dither: "dither";
338
+ }>, z.ZodTransform<"pop" | "glitch" | "fade" | "cut", "pop" | "glitch" | "fade" | "cut" | "dither">>>;
337
339
  inMs: z.ZodOptional<z.ZodNumber>;
338
340
  outMs: z.ZodOptional<z.ZodNumber>;
339
341
  }, z.core.$strip>>;
@@ -1,7 +1,7 @@
1
1
  import * as z from 'zod';
2
2
  import { CONTROLLER_DEAD_ZONE_MAX } from "../../values/controller.js";
3
3
  import { isRecord } from "../../values/guards.js";
4
- import { ITEM_TRANSITION_STYLES } from "../../values/item-transition.js";
4
+ import { ITEM_TRANSITION_STYLES, LEGACY_DITHER_STYLE } from "../../values/item-transition.js";
5
5
  import { OBJECT_ADD_MANY_MAX, SCENE_COLOR_RE, SPEECH_URL_MAX_LENGTH, STAGE_CAPTURE_EDGE_MAX, STAGE_CAPTURE_EDGE_MIN, STORAGE_KEY_MAX_LENGTH, STORAGE_VALUE_MAX_LENGTH, } from "../../values/limits.js";
6
6
  import { LIP_SYNC_CALIBRATION_ACTIONS, LIP_SYNC_MODES, LIP_SYNC_PHONEMES, } from "../../values/lipsync.js";
7
7
  import { SCENE_TRANSITION_DURATION_MAX_MS, SCENE_TRANSITION_DURATION_MIN_MS, SCENE_TRANSITION_FADE_DEFAULT_MS, SCENE_TRANSITION_SWITCH_POINT_MAX, SCENE_TRANSITION_SWITCH_POINT_MIN, SCENE_TRANSITION_TYPES, } from "../../values/scene-transition.js";
@@ -168,7 +168,11 @@ export const requestSchemas = {
168
168
  ttlMs: z.number().optional(),
169
169
  transition: z
170
170
  .object({
171
- style: z.enum(ITEM_TRANSITION_STYLES).optional(),
171
+ // Older clients still send `fade` by its old name.
172
+ style: z
173
+ .enum([...ITEM_TRANSITION_STYLES, LEGACY_DITHER_STYLE])
174
+ .transform(style => (style === LEGACY_DITHER_STYLE ? 'fade' : style))
175
+ .optional(),
172
176
  inMs: z.number().optional(),
173
177
  outMs: z.number().optional(),
174
178
  })