@laplace.live/persona-sdk 1.20.0 → 1.22.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.
@@ -8,6 +8,7 @@ import { type BaseControllerInputName, type ControllerInputName, type Controller
8
8
  import { type HandInputName } from '../values/hands.ts';
9
9
  import { type LipSyncMode, type VoiceInputName } from '../values/lipsync.ts';
10
10
  import { type MouseInputName } from '../values/mouse.ts';
11
+ import { type TimeInputName } from '../values/time.ts';
11
12
  export type ModelFormat = 'live2d' | 'vrm';
12
13
  /** Where an item came from: shipped with the app, or added by the user. */
13
14
  export type ContentOrigin = 'bundled' | 'user';
@@ -16,7 +17,7 @@ export type ContentOrigin = 'bundled' | 'user';
16
17
  * ever an environment map, and a `.vmd` splits by content — `cameraMotion` for one that
17
18
  * frames a shot, `animation` for one that drives a rig.
18
19
  */
19
- export declare const ASSET_KINDS: readonly ["image", "video", "audio", "prop", "ibl", "lut", "animation", "cameraMotion"];
20
+ export declare const ASSET_KINDS: readonly ["image", "video", "audio", "prop", "ibl", "lut", "animation", "cameraMotion", "lyrics"];
20
21
  export type AssetKind = (typeof ASSET_KINDS)[number];
21
22
  /** Narrow a kind string to the set the asset registry owns — models and effects have their own. */
22
23
  export declare function isAssetKind(v: unknown): v is AssetKind;
@@ -47,6 +48,13 @@ export interface ContentRef {
47
48
  /** A model as the registry lists it. */
48
49
  export interface ModelRef extends ContentRef {
49
50
  kind: ModelFormat;
51
+ /** Entry file basename (`.model3.json`, `.vrm`), with no directory. Absent on older hosts. */
52
+ entryFile?: string;
53
+ /**
54
+ * `.vtube.json` basename, the name a VTS model-load hotkey stores (see `modelMatchesFile`); null
55
+ * without one. Only `model.list` fills it: absent on scene item refs, `model.register`'s answer, and older hosts.
56
+ */
57
+ vtubeFile?: string | null;
50
58
  }
51
59
  /** A registered object-source file. `exists` is false once the file is gone from disk. */
52
60
  export interface AssetRef extends ContentRef {
@@ -80,7 +88,7 @@ export interface CatalogItem extends Omit<ContentRef, 'origin'> {
80
88
  /** Creator or content homepage (https). */
81
89
  url?: string;
82
90
  }
83
- /** Screen-space placement: pixels from the stage centre, rotation in radians. */
91
+ /** Screen-space placement: calibrated stage units from the viewport centre, rotation in radians. */
84
92
  export interface ScreenPlacement {
85
93
  x: number;
86
94
  y: number;
@@ -207,9 +215,30 @@ export interface SceneModelItem {
207
215
  }
208
216
  export declare const OBJECT_SPACES: readonly ["2d", "3d"];
209
217
  export type ObjectSpace = (typeof OBJECT_SPACES)[number];
210
- /** Where a webpage overlay renders relative to the stage. */
211
- export type WebLayer = 'behind' | 'front';
212
218
  export type CaptureKind = 'display' | 'window';
219
+ export declare const TEXT_ALIGNS: readonly ["left", "center", "right", "justify"];
220
+ export type TextAlign = (typeof TEXT_ALIGNS)[number];
221
+ /** `baseline` is the first line's baseline — Blender's Top Base-Line. */
222
+ export declare const TEXT_VERTICAL_ALIGNS: readonly ["top", "middle", "baseline", "bottom"];
223
+ export type TextVerticalAlign = (typeof TEXT_VERTICAL_ALIGNS)[number];
224
+ /** Blender's text-box overflow: spill past the box, shrink to fit it, or drop the lines that don't fit. */
225
+ export declare const TEXT_OVERFLOWS: readonly ["overflow", "scale", "truncate"];
226
+ export type TextOverflow = (typeof TEXT_OVERFLOWS)[number];
227
+ export declare const TEXT_STROKE_JOINS: readonly ["round", "miter", "bevel"];
228
+ export type TextStrokeJoin = (typeof TEXT_STROKE_JOINS)[number];
229
+ /**
230
+ * A system font face, named exactly: its PostScript name is the identity, since a family's
231
+ * nearest weight is not the face the user picked. The rest lets a machine without that face
232
+ * fall back to the closest one in the same family.
233
+ */
234
+ export interface TextFont {
235
+ postscriptName: string;
236
+ family: string;
237
+ weight: number;
238
+ italic: boolean;
239
+ /** CSS `font-stretch` percent; 100 is normal. */
240
+ stretch: number;
241
+ }
213
242
  /** What an object renders. Mirrors VTube Studio's items and Warudo's screen/prop assets. */
214
243
  export type ObjectContent = {
215
244
  kind: 'image';
@@ -220,6 +249,8 @@ export type ObjectContent = {
220
249
  loop: boolean;
221
250
  muted: boolean;
222
251
  volume: number;
252
+ /** Held rather than played while shown, through a restart too; the playback position is not saved. */
253
+ paused: boolean;
223
254
  } | {
224
255
  kind: 'prop';
225
256
  assetId: string;
@@ -236,8 +267,6 @@ export type ObjectContent = {
236
267
  transparent: boolean;
237
268
  /** Stylesheet injected into every page the object loads; empty for none. */
238
269
  css: string;
239
- /** 2D only: composited behind or in front of the whole stage, not interleaved with other layers. */
240
- layer: WebLayer;
241
270
  /** Close the page while the object is hidden and reload it on show, as an OBS browser source can. */
242
271
  shutdownWhenHidden: boolean;
243
272
  } | {
@@ -245,11 +274,95 @@ export type ObjectContent = {
245
274
  source: CaptureKind;
246
275
  sourceId: string;
247
276
  label: string;
277
+ } | {
278
+ kind: 'text';
279
+ text: string;
280
+ /** null draws in the platform's default sans-serif. */
281
+ font: TextFont | null;
282
+ /** Em size, px; 3D reads px at the image quads' 1000 px per metre. */
283
+ size: number;
284
+ color: string;
285
+ /** Blender's shear: the slant as a fraction of height, -1..1. */
286
+ shear: number;
287
+ /** Em fractions added between characters and to each space. */
288
+ letterSpacing: number;
289
+ wordSpacing: number;
290
+ /** Line box as a multiple of `size`. */
291
+ lineHeight: number;
292
+ align: TextAlign;
293
+ verticalAlign: TextVerticalAlign;
294
+ /** Text box, px; 0 sizes that side to the text, and a set width wraps. */
295
+ boxWidth: number;
296
+ boxHeight: number;
297
+ overflow: TextOverflow;
298
+ /** Outside the glyphs, px; width 0 draws none. */
299
+ stroke: {
300
+ width: number;
301
+ color: string;
302
+ join: TextStrokeJoin;
303
+ };
304
+ /** px; opacity 0 draws none. */
305
+ shadow: {
306
+ color: string;
307
+ opacity: number;
308
+ offsetX: number;
309
+ offsetY: number;
310
+ blur: number;
311
+ };
312
+ /** A backing box around the text, px; opacity 0 draws none. */
313
+ plate: {
314
+ color: string;
315
+ opacity: number;
316
+ padding: number;
317
+ radius: number;
318
+ };
319
+ /** Karaoke: a lyrics clip paints what is sung in `color`, outlined in `strokeColor` (2D only). */
320
+ sung: {
321
+ color: string;
322
+ strokeColor: string;
323
+ };
324
+ /** 3D: extrusion depth and bevel as fractions of `size`; bevel segments. */
325
+ extrude: number;
326
+ bevel: number;
327
+ bevelResolution: number;
328
+ /** 3D surface: PBR roughness and metalness 0..1, and how strongly the text glows in its own colour. */
329
+ roughness: number;
330
+ metalness: number;
331
+ emission: number;
332
+ /** 3D: turn the text to face the camera every frame, ignoring the placement's rotation. */
333
+ faceCamera: boolean;
248
334
  };
335
+ /** The video variant of {@link ObjectContent}. */
336
+ export type VideoContent = Extract<ObjectContent, {
337
+ kind: 'video';
338
+ }>;
249
339
  /** The webpage variant of {@link ObjectContent}. */
250
340
  export type WebContent = Extract<ObjectContent, {
251
341
  kind: 'web';
252
342
  }>;
343
+ /** The text variant of {@link ObjectContent}. */
344
+ export type TextContent = Extract<ObjectContent, {
345
+ kind: 'text';
346
+ }>;
347
+ /** One face of a {@link FontFamilyInfo}. */
348
+ export interface FontFaceInfo {
349
+ postscriptName: string;
350
+ /** The subfamily as the font names it: `Semibold`, `W6`, `Bold Italic`. */
351
+ style: string;
352
+ weight: number;
353
+ italic: boolean;
354
+ /** CSS `font-stretch` percent. */
355
+ stretch: number;
356
+ /** A variable font's named instance. */
357
+ variable: boolean;
358
+ }
359
+ /** An installed font family and its faces. File paths never cross the wire. */
360
+ export interface FontFamilyInfo {
361
+ family: string;
362
+ /** Every name the family goes by, localized ones included, canonical first. */
363
+ names: string[];
364
+ faces: FontFaceInfo[];
365
+ }
253
366
  /** Where on a parent model an object rides. `root` is the model's own transform. */
254
367
  export type AttachAnchor = {
255
368
  kind: 'root';
@@ -368,6 +481,108 @@ export interface OrbitTransform {
368
481
  targetY: number;
369
482
  targetZ: number;
370
483
  }
484
+ /**
485
+ * How a following camera turns with its anchor — Warudo's Transposer binding modes. `world` follows
486
+ * position only; `yaw`, `yaw-pitch` and `full` also turn with it (Lock To Target With World Up, No Roll,
487
+ * and Lock To Target); `lazy` trails it, turning only as it passes (Simple Follow With World Up).
488
+ */
489
+ export declare const CAMERA_FOLLOW_BINDINGS: readonly ["world", "yaw", "yaw-pitch", "full", "lazy"];
490
+ export type CameraFollowBinding = (typeof CAMERA_FOLLOW_BINDINGS)[number];
491
+ /** Seconds a following camera takes to settle, 0 locking it: position along the view's own axes, then each turn. */
492
+ export interface CameraFollowDamping {
493
+ x: number;
494
+ y: number;
495
+ z: number;
496
+ yaw: number;
497
+ pitch: number;
498
+ roll: number;
499
+ }
500
+ /** The point a camera follows or aims at on a model: a humanoid bone, or its root. An object is tracked by its own origin. */
501
+ export type CameraAnchor = Extract<AttachAnchor, {
502
+ kind: 'root' | 'bone';
503
+ }>;
504
+ /**
505
+ * The layer a scene camera follows, aims at or focuses on: a VRM model or 3D object by id, or whichever model
506
+ * is primary as the scene changes. An id naming anything else heals to no tracking; a primary model that
507
+ * cannot be tracked holds the view.
508
+ */
509
+ export type CameraTrackTarget = {
510
+ target: 'primary';
511
+ instanceId?: never;
512
+ } | {
513
+ target?: never;
514
+ instanceId: string;
515
+ };
516
+ /** How a following camera tracks its layer, whichever layer that is. */
517
+ export interface SceneCameraFollowSettings {
518
+ /** Always the root on an object. */
519
+ anchor: CameraAnchor;
520
+ binding: CameraFollowBinding;
521
+ damping: CameraFollowDamping;
522
+ }
523
+ /**
524
+ * A scene camera following a layer. While set, `orbit` frames the layer as if its anchor rested at the stage
525
+ * origin, and the view moves with the anchor — placement, motion and tracking alike — without writing
526
+ * `orbit` back.
527
+ */
528
+ export type SceneCameraFollow = CameraTrackTarget & SceneCameraFollowSettings;
529
+ /**
530
+ * A scene camera turning to keep a layer at a spot on screen — Warudo's Composer. Screen values are
531
+ * fractions of the view: x from the left, y from the top.
532
+ */
533
+ export interface SceneCameraAimSettings {
534
+ /** Always the root on an object. */
535
+ anchor: CameraAnchor;
536
+ /** Where the anchor sits on screen, 0–1; 0.5 centres it. */
537
+ screenX: number;
538
+ screenY: number;
539
+ /** The camera holds still while the anchor stays inside this zone around its spot. */
540
+ deadZone: {
541
+ width: number;
542
+ height: number;
543
+ };
544
+ /** Past the dead zone the camera turns gradually; past this zone, at once. Never narrower than the dead zone. */
545
+ softZone: {
546
+ width: number;
547
+ height: number;
548
+ };
549
+ /** Shifts the soft zone off the spot, −0.5–0.5 of the slack between the zones. */
550
+ bias: {
551
+ x: number;
552
+ y: number;
553
+ };
554
+ /** Seconds to turn the anchor back into the dead zone, 0 turning at once. */
555
+ damping: {
556
+ horizontal: number;
557
+ vertical: number;
558
+ };
559
+ /**
560
+ * Aims `time` seconds (0–1, 0 off) ahead of a moving anchor, its velocity smoothed over `smoothing` (0–30);
561
+ * `ignoreY` leaves vertical motion out.
562
+ */
563
+ lookahead: {
564
+ time: number;
565
+ smoothing: number;
566
+ ignoreY: boolean;
567
+ };
568
+ }
569
+ export type SceneCameraAim = CameraTrackTarget & SceneCameraAimSettings;
570
+ /** A scene camera keeping a layer sharp under Depth of Field — Warudo's Focus Character. */
571
+ export interface SceneCameraFocusSettings {
572
+ /** Always the root on an object. */
573
+ anchor: CameraAnchor;
574
+ /** Seconds for the focus plane to catch up with the anchor, 0 at once. */
575
+ damping: number;
576
+ }
577
+ export type SceneCameraFocus = CameraTrackTarget & SceneCameraFocusSettings;
578
+ /** Noise-driven sway over the scene camera's own framing — Warudo's Handheld Movement. */
579
+ export interface SceneCameraHandheld {
580
+ enabled: boolean;
581
+ /** 0–1; the default 0.5 is a mild handheld. */
582
+ intensity: number;
583
+ /** 0–2; 1 sways at the authored pace. */
584
+ speed: number;
585
+ }
371
586
  /** Scene-level VRM camera. `orbit: null` = never framed — the first VRM load frames it from model height. */
372
587
  export interface SceneCamera {
373
588
  orbit: OrbitTransform | null;
@@ -382,6 +597,14 @@ export interface SceneCamera {
382
597
  * is set it overrides the framing every frame without writing it back.
383
598
  */
384
599
  clipAssetId: string | null;
600
+ /** The layer the view follows, or null. A camera motion overrides it. Absent on older hosts; gate on `camera-follow`. */
601
+ follow?: SceneCameraFollow | null;
602
+ /** The layer the view turns to keep on screen, or null. A camera motion overrides it. Absent on older hosts; gate on `camera-aim`. */
603
+ aim?: SceneCameraAim | null;
604
+ /** The layer Depth of Field keeps sharp, or null for the look-at point. Absent on older hosts; gate on `camera-focus`. */
605
+ focus?: SceneCameraFocus | null;
606
+ /** Handheld sway; a camera motion overrides it. Absent on older hosts; gate on `camera-handheld`. */
607
+ handheld?: SceneCameraHandheld;
385
608
  }
386
609
  /**
387
610
  * The slice of {@link SceneCamera} a saved pose snapshots and applies — a framing, never
@@ -661,6 +884,28 @@ export interface EffectBlur {
661
884
  mode: 'gaussian' | 'bokeh';
662
885
  highQuality: boolean;
663
886
  }
887
+ /**
888
+ * Pencil sketch on paper (Jaume Sanchez's NPR sketch shader): graphite grain shades the darker
889
+ * tones and pencil lines trace color and coverage edges, over a procedural paper grain.
890
+ */
891
+ export interface EffectSketch {
892
+ enabled: boolean;
893
+ /** Paper tint (hex) under the drawing. */
894
+ paperColor: string;
895
+ /** Scene only: paper also fills transparent pixels. Layers always keep their coverage. */
896
+ paperBackground: boolean;
897
+ /** Pencil-line darkness, 0–2. */
898
+ lines: number;
899
+ /** Pencil-line width in CSS pixels, 0.5–4, so it holds across display densities. */
900
+ lineWidth: number;
901
+ /** Graphite grain in the darker tones, 0–2: 0 leaves only the lines, 1 is the source's density. */
902
+ shading: number;
903
+ /** How strongly a wide blur of the surrounding tones lightens the shading, 0–1. */
904
+ softness: number;
905
+ /** 0 draws in graphite, 1 keeps the image's colors at the same lightness. */
906
+ saturation: number;
907
+ opacity: number;
908
+ }
664
909
  /** Lens fringing that grows toward frame edges (Unity PPv2's curve, the one VTube Studio wraps). */
665
910
  export interface SceneChromaticAberration {
666
911
  enabled: boolean;
@@ -877,6 +1122,7 @@ export interface EffectValues {
877
1122
  gradient: EffectGradient;
878
1123
  flare: SceneFlare;
879
1124
  blur: EffectBlur;
1125
+ sketch: EffectSketch;
880
1126
  chromaticAberration: SceneChromaticAberration;
881
1127
  grain: SceneFilmGrain;
882
1128
  dof: SceneDepthOfField;
@@ -1041,7 +1287,7 @@ export interface ScenePatch {
1041
1287
  * App-level features a client gates on (never version-sniff): `hello` and
1042
1288
  * `app.info` report them — the per-app mirror of {@link InstanceRuntime.capabilities}.
1043
1289
  */
1044
- export declare const APP_CAPABILITIES: readonly ["storage", "speech", "automations", "controllers", "model-editing", "asset-inspection", "layer-effects", "scene-transitions", "area-lights", "spot-lights", "camera-follow-lights", "shadow-filters", "environment-map-model", "spawn", "tracking-lost", "motion-stop", "stage-capture", "model-movement"];
1290
+ export declare const APP_CAPABILITIES: readonly ["storage", "speech", "automations", "controllers", "model-editing", "asset-inspection", "layer-effects", "scene-transitions", "area-lights", "spot-lights", "camera-follow-lights", "shadow-filters", "environment-map-model", "spawn", "tracking-lost", "motion-stop", "stage-capture", "model-movement", "object-pin-depth", "text-objects", "camera-follow", "camera-aim", "camera-focus", "camera-handheld", "video-sound"];
1045
1291
  export type AppCapability = (typeof APP_CAPABILITIES)[number];
1046
1292
  export declare function isAppCapability(v: unknown): v is AppCapability;
1047
1293
  /** What a loaded model instance can do; absent capabilities answer `unsupported-for-format`. */
@@ -1110,7 +1356,7 @@ export interface Hotkey {
1110
1356
  name: string;
1111
1357
  /** The VTS `Action` string verbatim; `action` is null when Persona cannot run it. */
1112
1358
  vtsAction: string;
1113
- action: 'toggle-expression' | 'play-motion' | 'remove-all-expressions' | 'load-model' | null;
1359
+ action: 'expression-toggle' | 'motion-play' | 'expression-clear' | 'model-load' | null;
1114
1360
  file: string;
1115
1361
  accelerator: string | null;
1116
1362
  global: boolean;
@@ -1133,11 +1379,22 @@ export interface HotkeyState extends HotkeyConfig {
1133
1379
  registered: string[];
1134
1380
  }
1135
1381
  /** Action kinds an app automation can carry today; servers may send kinds newer than this list. */
1136
- export declare const AUTOMATION_ACTION_KINDS: readonly ["effect-toggle", "effect-params", "effect-clip", "camera-pose", "reset-camera", "play-camera-motion", "stop-camera-motion", "layer-visibility", "stream-mode", "switch-scene", "toggle-expression", "play-motion", "play-audio", "audio-control", "remove-all-expressions", "load-model", "model-position"];
1382
+ export declare const AUTOMATION_ACTION_KINDS: readonly ["effect-toggle", "effect-params", "effect-clip", "camera-pose", "camera-reset", "camera-motion-play", "camera-motion-stop", "camera-follow", "camera-aim", "camera-focus", "camera-handheld", "layer-visibility", "text-set", "lyrics-play", "stream-mode", "scene-switch", "expression-toggle", "motion-play", "audio-play", "audio-control", "expression-clear", "model-load", "model-position"];
1137
1383
  export type AutomationActionKind = (typeof AUTOMATION_ACTION_KINDS)[number];
1384
+ /**
1385
+ * Deprecated action kind names, each mapped to its category-first replacement. Older hosts send them in
1386
+ * `actionKinds` and `Hotkey.action`, and older files store them; the client renames them on receipt.
1387
+ */
1388
+ export declare const LEGACY_AUTOMATION_ACTION_KINDS: ReadonlyMap<string, AutomationActionKind>;
1389
+ /** The current name of an action kind; kinds that were never renamed, known or not, pass through. */
1390
+ export declare function canonicalActionKind(kind: string): string;
1138
1391
  /** Setup kinds that finish loading before the sequence clock starts. */
1139
- export declare const AUTOMATION_BOUNDARY_KINDS: readonly ["switch-scene", "load-model"];
1140
- export type AutomationBoundaryKind = (typeof AUTOMATION_BOUNDARY_KINDS)[number];
1392
+ export declare const AUTOMATION_SETUP_KINDS: readonly ["scene-switch", "model-load"];
1393
+ export type AutomationSetupKind = (typeof AUTOMATION_SETUP_KINDS)[number];
1394
+ /** @deprecated Use `AUTOMATION_SETUP_KINDS`. */
1395
+ export declare const AUTOMATION_BOUNDARY_KINDS: readonly ["scene-switch", "model-load"];
1396
+ /** @deprecated Use `AutomationSetupKind`. */
1397
+ export type AutomationBoundaryKind = AutomationSetupKind;
1141
1398
  /**
1142
1399
  * One app automation as clients see it. Action kinds and scene targets drive derived
1143
1400
  * labels; other action payloads stay app-side.
@@ -1302,12 +1559,12 @@ declare const VTS_INPUT_RANGES: {
1302
1559
  };
1303
1560
  type VtsInputName = keyof typeof VTS_INPUT_RANGES;
1304
1561
  /**
1305
- * Default input vocabulary: face, hand and cursor inputs, raw ARKit channels and controller
1562
+ * Default input vocabulary: face, hand, cursor and time inputs, raw ARKit channels and controller
1306
1563
  * profile 1. Additional controller profile ids are accepted by isInputName without appearing
1307
1564
  * in this list.
1308
1565
  */
1309
- export declare const INPUT_NAMES: readonly (VtsInputName | HandInputName | VoiceInputName | MouseInputName | ArkitInputName | BaseControllerInputName)[];
1310
- export type InputName = VtsInputName | HandInputName | VoiceInputName | MouseInputName | ArkitInputName | ControllerInputName;
1566
+ export declare const INPUT_NAMES: readonly (VtsInputName | HandInputName | VoiceInputName | MouseInputName | TimeInputName | ArkitInputName | BaseControllerInputName)[];
1567
+ export type InputName = VtsInputName | HandInputName | VoiceInputName | MouseInputName | TimeInputName | ArkitInputName | ControllerInputName;
1311
1568
  /** Whether an untrusted string names a tracking input — the guard every wire boundary needs. */
1312
1569
  export declare function isInputName(v: string): v is InputName;
1313
1570
  /**
@@ -1335,7 +1592,7 @@ export declare function arkitTwinOf(input: InputName): BindingInput;
1335
1592
  * Default inputs' natural spans. Use getInputRange for a dynamically numbered controller input.
1336
1593
  * Head angles are degrees; the rest are unitless.
1337
1594
  */
1338
- export declare const INPUT_RANGES: Record<VtsInputName | HandInputName | VoiceInputName | MouseInputName | ArkitInputName | BaseControllerInputName, readonly [number, number]>;
1595
+ export declare const INPUT_RANGES: Record<VtsInputName | HandInputName | VoiceInputName | MouseInputName | TimeInputName | ArkitInputName | BaseControllerInputName, readonly [number, number]>;
1339
1596
  /** Natural span of any valid input, including dynamically numbered controller profiles. */
1340
1597
  export declare function getInputRange(name: InputName): readonly [number, number];
1341
1598
  /**
@@ -7,18 +7,41 @@ import { isOneOf, keysOf } from "../values/guards.js";
7
7
  import { HAND_INPUT_NAMES, HAND_INPUT_RANGES } from "../values/hands.js";
8
8
  import { VOICE_INPUT_NAMES, VOICE_INPUT_RANGES } from "../values/lipsync.js";
9
9
  import { MOUSE_INPUT_NAMES, MOUSE_INPUT_RANGES } from "../values/mouse.js";
10
+ import { TIME_INPUT_NAMES, TIME_INPUT_RANGES } from "../values/time.js";
10
11
  /**
11
12
  * How a registered file is labelled. Wider than an object's content kinds: `.hdr` is only
12
13
  * ever an environment map, and a `.vmd` splits by content — `cameraMotion` for one that
13
14
  * frames a shot, `animation` for one that drives a rig.
14
15
  */
15
- export const ASSET_KINDS = ['image', 'video', 'audio', 'prop', 'ibl', 'lut', 'animation', 'cameraMotion'];
16
+ export const ASSET_KINDS = [
17
+ 'image',
18
+ 'video',
19
+ 'audio',
20
+ 'prop',
21
+ 'ibl',
22
+ 'lut',
23
+ 'animation',
24
+ 'cameraMotion',
25
+ 'lyrics',
26
+ ];
16
27
  /** Narrow a kind string to the set the asset registry owns — models and effects have their own. */
17
28
  export function isAssetKind(v) {
18
29
  return isOneOf(v, ASSET_KINDS);
19
30
  }
20
31
  // ---- Objects -------------------------------------------------------------------
21
32
  export const OBJECT_SPACES = ['2d', '3d'];
33
+ export const TEXT_ALIGNS = ['left', 'center', 'right', 'justify'];
34
+ /** `baseline` is the first line's baseline — Blender's Top Base-Line. */
35
+ export const TEXT_VERTICAL_ALIGNS = ['top', 'middle', 'baseline', 'bottom'];
36
+ /** Blender's text-box overflow: spill past the box, shrink to fit it, or drop the lines that don't fit. */
37
+ export const TEXT_OVERFLOWS = ['overflow', 'scale', 'truncate'];
38
+ export const TEXT_STROKE_JOINS = ['round', 'miter', 'bevel'];
39
+ /**
40
+ * How a following camera turns with its anchor — Warudo's Transposer binding modes. `world` follows
41
+ * position only; `yaw`, `yaw-pitch` and `full` also turn with it (Lock To Target With World Up, No Roll,
42
+ * and Lock To Target); `lazy` trails it, turning only as it passes (Simple Follow With World Up).
43
+ */
44
+ export const CAMERA_FOLLOW_BINDINGS = ['world', 'yaw', 'yaw-pitch', 'full', 'lazy'];
22
45
  export const SCENE_LIGHT_TYPES = ['directional', 'point', 'ambient', 'area', 'spot'];
23
46
  /**
24
47
  * Per-light shadow tier. `off` is the old `castShadow: false`; the rest raise
@@ -55,6 +78,23 @@ export const APP_CAPABILITIES = [
55
78
  'stage-capture',
56
79
  /** `model.getMovement` / `model.setMovement` and the `model.movement` event. */
57
80
  'model-movement',
81
+ /**
82
+ * 2D images, videos and webpages all paint in the stage's layer stack (a webpage has no `layer`), and
83
+ * one pinned to a Live2D model takes `attach.depth` in that model's stack, as a Live2D item does.
84
+ */
85
+ 'object-pin-depth',
86
+ /** `text` object content and `fonts.list`. */
87
+ 'text-objects',
88
+ /** `SceneCamera.follow`. */
89
+ 'camera-follow',
90
+ /** `SceneCamera.aim`. */
91
+ 'camera-aim',
92
+ /** `SceneCamera.focus`. */
93
+ 'camera-focus',
94
+ /** `SceneCamera.handheld`. */
95
+ 'camera-handheld',
96
+ /** `object.setContent` edits a video's `loop`, `muted` and `volume` in place; its sound follows the output device. */
97
+ 'video-sound',
58
98
  ];
59
99
  export function isAppCapability(v) {
60
100
  return isOneOf(v, APP_CAPABILITIES);
@@ -66,25 +106,51 @@ export const AUTOMATION_ACTION_KINDS = [
66
106
  'effect-params',
67
107
  'effect-clip',
68
108
  'camera-pose',
69
- 'reset-camera',
70
- 'play-camera-motion',
71
- 'stop-camera-motion',
109
+ 'camera-reset',
110
+ 'camera-motion-play',
111
+ 'camera-motion-stop',
112
+ 'camera-follow',
113
+ 'camera-aim',
114
+ 'camera-focus',
115
+ 'camera-handheld',
72
116
  'layer-visibility',
117
+ 'text-set',
118
+ 'lyrics-play',
73
119
  'stream-mode',
74
- 'switch-scene',
75
- 'toggle-expression',
76
- 'play-motion',
77
- 'play-audio',
120
+ 'scene-switch',
121
+ 'expression-toggle',
122
+ 'motion-play',
123
+ 'audio-play',
78
124
  'audio-control',
79
- 'remove-all-expressions',
80
- 'load-model',
125
+ 'expression-clear',
126
+ 'model-load',
81
127
  'model-position',
82
128
  ];
129
+ /**
130
+ * Deprecated action kind names, each mapped to its category-first replacement. Older hosts send them in
131
+ * `actionKinds` and `Hotkey.action`, and older files store them; the client renames them on receipt.
132
+ */
133
+ export const LEGACY_AUTOMATION_ACTION_KINDS = new Map([
134
+ ['reset-camera', 'camera-reset'],
135
+ ['play-camera-motion', 'camera-motion-play'],
136
+ ['stop-camera-motion', 'camera-motion-stop'],
137
+ ['set-text', 'text-set'],
138
+ ['lyrics', 'lyrics-play'],
139
+ ['switch-scene', 'scene-switch'],
140
+ ['toggle-expression', 'expression-toggle'],
141
+ ['play-motion', 'motion-play'],
142
+ ['play-audio', 'audio-play'],
143
+ ['remove-all-expressions', 'expression-clear'],
144
+ ['load-model', 'model-load'],
145
+ ]);
146
+ /** The current name of an action kind; kinds that were never renamed, known or not, pass through. */
147
+ export function canonicalActionKind(kind) {
148
+ return LEGACY_AUTOMATION_ACTION_KINDS.get(kind) ?? kind;
149
+ }
83
150
  /** Setup kinds that finish loading before the sequence clock starts. */
84
- export const AUTOMATION_BOUNDARY_KINDS = [
85
- 'switch-scene',
86
- 'load-model',
87
- ];
151
+ export const AUTOMATION_SETUP_KINDS = ['scene-switch', 'model-load'];
152
+ /** @deprecated Use `AUTOMATION_SETUP_KINDS`. */
153
+ export const AUTOMATION_BOUNDARY_KINDS = AUTOMATION_SETUP_KINDS;
88
154
  // ---- Settings ------------------------------------------------------------------
89
155
  export const TRACKING_SOURCE_IDS = ['persona-ios', 'ifacialmocap', 'vts-ios'];
90
156
  /** Body-pose protocols. Every one so far is UDP with a configurable port. */
@@ -195,7 +261,7 @@ const VTS_INPUT_RANGES = {
195
261
  /** VTS's input vocabulary, plus `JawOpen` — the derived half of {@link INPUT_NAMES}. */
196
262
  const VTS_INPUT_NAMES = keysOf(VTS_INPUT_RANGES);
197
263
  /**
198
- * Default input vocabulary: face, hand and cursor inputs, raw ARKit channels and controller
264
+ * Default input vocabulary: face, hand, cursor and time inputs, raw ARKit channels and controller
199
265
  * profile 1. Additional controller profile ids are accepted by isInputName without appearing
200
266
  * in this list.
201
267
  */
@@ -204,6 +270,7 @@ export const INPUT_NAMES = [
204
270
  ...VOICE_INPUT_NAMES,
205
271
  ...HAND_INPUT_NAMES,
206
272
  ...MOUSE_INPUT_NAMES,
273
+ ...TIME_INPUT_NAMES,
207
274
  ...ARKIT_INPUT_NAMES,
208
275
  ...BASE_CONTROLLER_INPUT_NAMES,
209
276
  ];
@@ -244,6 +311,7 @@ export const INPUT_RANGES = {
244
311
  ...VOICE_INPUT_RANGES,
245
312
  ...HAND_INPUT_RANGES,
246
313
  ...MOUSE_INPUT_RANGES,
314
+ ...TIME_INPUT_RANGES,
247
315
  ...BASE_CONTROLLER_INPUT_RANGES,
248
316
  // `fromEntries` widens the key type back to `string`; the annotation above is what keeps
249
317
  // this exhaustive, and it fails to typecheck if a name ever lacks a range.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laplace.live/persona-sdk",
3
- "version": "1.20.0",
3
+ "version": "1.22.0",
4
4
  "description": "TypeScript SDK and wire schema for the LAPLACE Persona plugin API",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -28,7 +28,7 @@
28
28
  "devDependencies": {
29
29
  "rimraf": "^6.1.3",
30
30
  "typescript": "~6.0.3",
31
- "vitest": "^5.0.1"
31
+ "vitest": "^5.0.2"
32
32
  },
33
33
  "engines": {
34
34
  "node": ">=24"