@laplace.live/persona-sdk 1.5.0 → 1.7.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.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './client/address.ts';
2
2
  export * from './client/client.ts';
3
3
  export * from './values/arkit.ts';
4
4
  export * from './values/bindings.ts';
5
+ export * from './values/camera.ts';
5
6
  export * from './values/controller.ts';
6
7
  export * from './values/curve.ts';
7
8
  export * from './values/edit-history.ts';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from "./client/address.js";
4
4
  export * from "./client/client.js";
5
5
  export * from "./values/arkit.js";
6
6
  export * from "./values/bindings.js";
7
+ export * from "./values/camera.js";
7
8
  export * from "./values/controller.js";
8
9
  export * from "./values/curve.js";
9
10
  export * from "./values/edit-history.js";
@@ -0,0 +1,13 @@
1
+ import type { OrbitTransform } from '../wire/types.ts';
2
+ /** Camera position for an orbit transform, in world units. Y is up, +Z toward the viewer. */
3
+ export declare function orbitPosition(t: OrbitTransform): {
4
+ x: number;
5
+ y: number;
6
+ z: number;
7
+ };
8
+ /** Translate a camera and its look-at point to a world position, preserving its view direction and distance. */
9
+ export declare function orbitAtPosition(orbit: OrbitTransform, position: {
10
+ x: number;
11
+ y: number;
12
+ z: number;
13
+ }): OrbitTransform;
@@ -0,0 +1,19 @@
1
+ /** Camera position for an orbit transform, in world units. Y is up, +Z toward the viewer. */
2
+ export function orbitPosition(t) {
3
+ const r = Math.cos(t.elevation) * t.distance;
4
+ return {
5
+ x: t.targetX + Math.sin(t.azimuth) * r,
6
+ y: t.targetY + Math.sin(t.elevation) * t.distance,
7
+ z: t.targetZ + Math.cos(t.azimuth) * r,
8
+ };
9
+ }
10
+ /** Translate a camera and its look-at point to a world position, preserving its view direction and distance. */
11
+ export function orbitAtPosition(orbit, position) {
12
+ const current = orbitPosition(orbit);
13
+ return {
14
+ ...orbit,
15
+ targetX: orbit.targetX + position.x - current.x,
16
+ targetY: orbit.targetY + position.y - current.y,
17
+ targetZ: orbit.targetZ + position.z - current.z,
18
+ };
19
+ }
@@ -1,4 +1,4 @@
1
- import type { AutomationInfo } from '../wire/types.ts';
1
+ import type { AutomationBoundaryKind, AutomationInfo } from '../wire/types.ts';
2
2
  /** Last path segment, tolerating both `/` and `\` separators. */
3
3
  export declare function fileBasename(path: string): string;
4
4
  /**
@@ -9,9 +9,11 @@ export declare function fileBasename(path: string): string;
9
9
  export declare function motionLabel(file: string): string;
10
10
  /** English label for one automation action kind; kinds newer than this SDK read `Action`. */
11
11
  export declare function automationActionLabel(kind: string): string;
12
+ /** True for an action that ends a batch and waits before continuing. */
13
+ export declare function isAutomationBoundary(kind: string | undefined): kind is AutomationBoundaryKind;
12
14
  /**
13
- * Join one automation's action labels: a wait ends a batch, so it reads as an arrow, while
14
- * actions inside a batch read as ` + `. `kinds` must match `labels` in length and position.
15
+ * Join one automation's action labels: a batch boundary reads as an arrow, while actions
16
+ * inside a batch read as ` + `. `kinds` must match `labels` in length and position.
15
17
  */
16
18
  export declare function joinAutomationActionLabels(labels: readonly string[], kinds: readonly string[]): string;
17
19
  /** Display label for an app automation: its title, else its joined action-kind labels. */
@@ -1,3 +1,5 @@
1
+ import { AUTOMATION_BOUNDARY_KINDS } from "../wire/types.js";
2
+ import { isOneOf } from "./guards.js";
1
3
  /** Last path segment, tolerating both `/` and `\` separators. */
2
4
  export function fileBasename(path) {
3
5
  return path.split(/[/\\]/).at(-1) ?? path;
@@ -19,20 +21,32 @@ const AUTOMATION_ACTION_KIND_LABELS = {
19
21
  'reset-camera': 'Reset Camera',
20
22
  'layer-visibility': 'Layer Visibility',
21
23
  'stream-mode': 'Stream Mode',
24
+ 'switch-scene': 'Switch Scene',
25
+ 'toggle-expression': 'Toggle Expression',
26
+ 'play-motion': 'Play Motion',
27
+ 'remove-all-expressions': 'Clear All Expressions',
28
+ 'load-model': 'Swap Model',
29
+ 'model-position': 'Model Position',
22
30
  delay: 'Wait',
23
31
  };
24
32
  /** English label for one automation action kind; kinds newer than this SDK read `Action`. */
25
33
  export function automationActionLabel(kind) {
26
34
  return AUTOMATION_ACTION_KIND_LABELS[kind] ?? 'Action';
27
35
  }
36
+ /** True for an action that ends a batch and waits before continuing. */
37
+ export function isAutomationBoundary(kind) {
38
+ return isOneOf(kind, AUTOMATION_BOUNDARY_KINDS);
39
+ }
28
40
  /**
29
- * Join one automation's action labels: a wait ends a batch, so it reads as an arrow, while
30
- * actions inside a batch read as ` + `. `kinds` must match `labels` in length and position.
41
+ * Join one automation's action labels: a batch boundary reads as an arrow, while actions
42
+ * inside a batch read as ` + `. `kinds` must match `labels` in length and position.
31
43
  */
32
44
  export function joinAutomationActionLabels(labels, kinds) {
33
45
  if (labels.length !== kinds.length)
34
46
  throw new RangeError('automation action labels and kinds must have the same length');
35
- return labels.reduce((out, label, i) => out + (i === 0 ? '' : kinds[i] === 'delay' || kinds[i - 1] === 'delay' ? ' → ' : ' + ') + label, '');
47
+ return labels.reduce((out, label, i) => out +
48
+ (i === 0 ? '' : isAutomationBoundary(kinds[i]) || isAutomationBoundary(kinds[i - 1]) ? ' → ' : ' + ') +
49
+ label, '');
36
50
  }
37
51
  /** Display label for an app automation: its title, else its joined action-kind labels. */
38
52
  export function automationLabel(automation) {
@@ -104,6 +104,7 @@ export function defaultSceneLightOf(type) {
104
104
  return {
105
105
  id: crypto.randomUUID(),
106
106
  type,
107
+ enabled: true,
107
108
  color: '#ffffff',
108
109
  // Ambient is a flat albedo floor with no shading — 1.0 alone washes the model out.
109
110
  intensity: type === 'ambient' ? 0.4 : 1,
@@ -2,6 +2,7 @@ import type { AssetRef, SceneTransition } from '../wire/types.ts';
2
2
  export declare const SCENE_TRANSITION_TYPES: readonly ["cut", "fade", "wipe", "circle", "image", "video"];
3
3
  export declare const SCENE_TRANSITION_DURATION_MIN_MS = 100;
4
4
  export declare const SCENE_TRANSITION_DURATION_MAX_MS = 10000;
5
+ export declare const SCENE_TRANSITION_FADE_DEFAULT_MS = 300;
5
6
  export declare const SCENE_TRANSITION_SWITCH_POINT_MIN = 0.05;
6
7
  export declare const SCENE_TRANSITION_SWITCH_POINT_MAX = 0.95;
7
8
  /** The destination scene's entrance effect; existing scenes keep an immediate cut. */
@@ -3,11 +3,21 @@ import { clamp, hexColorOr } from "./limits.js";
3
3
  export const SCENE_TRANSITION_TYPES = ['cut', 'fade', 'wipe', 'circle', 'image', 'video'];
4
4
  export const SCENE_TRANSITION_DURATION_MIN_MS = 100;
5
5
  export const SCENE_TRANSITION_DURATION_MAX_MS = 10_000;
6
+ export const SCENE_TRANSITION_FADE_DEFAULT_MS = 300;
6
7
  export const SCENE_TRANSITION_SWITCH_POINT_MIN = 0.05;
7
8
  export const SCENE_TRANSITION_SWITCH_POINT_MAX = 0.95;
8
9
  /** The destination scene's entrance effect; existing scenes keep an immediate cut. */
9
10
  export function defaultSceneTransition() {
10
- return { type: 'cut', durationMs: 1000, color: '#000000', assetId: null, switchPoint: 0.5, autoFade: false };
11
+ return {
12
+ type: 'cut',
13
+ durationMs: 1000,
14
+ color: '#000000',
15
+ assetId: null,
16
+ switchPoint: 0.5,
17
+ autoFade: false,
18
+ fadeInMs: SCENE_TRANSITION_FADE_DEFAULT_MS,
19
+ fadeOutMs: SCENE_TRANSITION_FADE_DEFAULT_MS,
20
+ };
11
21
  }
12
22
  /** The media kind a transition type plays, or null for the built-in effects. */
13
23
  export function sceneTransitionMedia(type) {
@@ -32,5 +42,7 @@ export function healSceneTransition(raw) {
32
42
  assetId: nonEmptyString(raw.assetId),
33
43
  switchPoint: clamp(finiteOr(raw.switchPoint, d.switchPoint), SCENE_TRANSITION_SWITCH_POINT_MIN, SCENE_TRANSITION_SWITCH_POINT_MAX),
34
44
  autoFade: typeof raw.autoFade === 'boolean' ? raw.autoFade : d.autoFade,
45
+ fadeInMs: clamp(finiteOr(raw.fadeInMs, d.fadeInMs), 0, SCENE_TRANSITION_DURATION_MAX_MS),
46
+ fadeOutMs: clamp(finiteOr(raw.fadeOutMs, d.fadeOutMs), 0, SCENE_TRANSITION_DURATION_MAX_MS),
35
47
  };
36
48
  }
@@ -1,7 +1,7 @@
1
1
  import type { AutomationInfo, ExpressionPersistence, HotkeyState, ModelFormat, PoseStatus, SceneState, Settings, TrackingStatus } from './types.ts';
2
2
  /** Every push event a session can subscribe to, with its payload. */
3
3
  export interface EventMap {
4
- /** Any scene mutation: create/delete/rename/activate/shortcut and persisted scene edits. */
4
+ /** Any scene mutation: create/delete/rename/activate and persisted scene edits. */
5
5
  'scene.changed': SceneState;
6
6
  'settings.changed': {
7
7
  settings: Settings;
@@ -36,12 +36,6 @@ export interface SceneDeleteRequest {
36
36
  sceneId: string;
37
37
  }
38
38
  export type SceneDeleteResponse = SceneState;
39
- export interface SceneSetShortcutRequest {
40
- sceneId: string;
41
- /** Electron accelerator string, or null to clear. */
42
- accelerator: string | null;
43
- }
44
- export type SceneSetShortcutResponse = SceneState;
45
39
  export type ScenePatchRequest = ScenePatch;
46
40
  export interface ScenePatchResponse {
47
41
  scene: Scene;
@@ -668,10 +662,6 @@ export interface MethodMap {
668
662
  request: SceneDeleteRequest;
669
663
  response: SceneDeleteResponse;
670
664
  };
671
- 'scene.setShortcut': {
672
- request: SceneSetShortcutRequest;
673
- response: SceneSetShortcutResponse;
674
- };
675
665
  /** Live edit of the active scene's sections; applies next frame, persists debounced. */
676
666
  'scene.patch': {
677
667
  request: ScenePatchRequest;
@@ -724,7 +714,7 @@ export interface MethodMap {
724
714
  request: InstanceSetPrimaryRequest;
725
715
  response: InstanceSetPrimaryResponse;
726
716
  };
727
- /** Drives the desktop's editing selection (outline + Model tab); ephemeral, never persisted. */
717
+ /** Drives the desktop's editing selection (handles and inspector); ephemeral, never persisted. */
728
718
  'selection.set': {
729
719
  request: SelectionSetRequest;
730
720
  response: SelectionSetResponse;
@@ -16,6 +16,8 @@ export declare const SceneTransitionSchema: z.ZodObject<{
16
16
  assetId: z.ZodNullable<z.ZodString>;
17
17
  switchPoint: z.ZodNumber;
18
18
  autoFade: z.ZodDefault<z.ZodBoolean>;
19
+ fadeInMs: z.ZodDefault<z.ZodNumber>;
20
+ fadeOutMs: z.ZodDefault<z.ZodNumber>;
19
21
  }, z.core.$strip>;
20
22
  export declare const InjectTargetSchema: z.ZodObject<{
21
23
  type: z.ZodEnum<{
@@ -57,7 +59,6 @@ export declare const SettingsPatchSchema: z.ZodObject<{
57
59
  performance: z.ZodOptional<z.ZodObject<{
58
60
  showFps: z.ZodOptional<z.ZodBoolean>;
59
61
  fpsLimit: z.ZodOptional<z.ZodNumber>;
60
- selectionOutline: z.ZodOptional<z.ZodBoolean>;
61
62
  effectsQuality: z.ZodOptional<z.ZodEnum<{
62
63
  low: "low";
63
64
  medium: "medium";
@@ -144,10 +145,6 @@ export declare const requestSchemas: {
144
145
  'scene.delete': z.ZodObject<{
145
146
  sceneId: z.ZodString;
146
147
  }, z.core.$strip>;
147
- 'scene.setShortcut': z.ZodObject<{
148
- sceneId: z.ZodString;
149
- accelerator: z.ZodNullable<z.ZodString>;
150
- }, z.core.$strip>;
151
148
  'instance.add': z.ZodObject<{
152
149
  modelId: z.ZodString;
153
150
  }, z.core.$strip>;
@@ -264,7 +261,6 @@ export declare const requestSchemas: {
264
261
  performance: z.ZodOptional<z.ZodObject<{
265
262
  showFps: z.ZodOptional<z.ZodBoolean>;
266
263
  fpsLimit: z.ZodOptional<z.ZodNumber>;
267
- selectionOutline: z.ZodOptional<z.ZodBoolean>;
268
264
  effectsQuality: z.ZodOptional<z.ZodEnum<{
269
265
  low: "low";
270
266
  medium: "medium";
@@ -3,7 +3,7 @@ import { CONTROLLER_DEAD_ZONE_MAX } from "../values/controller.js";
3
3
  import { isRecord } from "../values/guards.js";
4
4
  import { SCENE_COLOR_RE, SPEECH_URL_MAX_LENGTH, STORAGE_KEY_MAX_LENGTH, STORAGE_VALUE_MAX_LENGTH, } from "../values/limits.js";
5
5
  import { LIP_SYNC_CALIBRATION_ACTIONS, LIP_SYNC_GAIN_MAX, LIP_SYNC_MODES, LIP_SYNC_NOISE_GATE_MIN, LIP_SYNC_PHONEMES, LIP_SYNC_SMOOTHING_MAX, } from "../values/lipsync.js";
6
- import { SCENE_TRANSITION_DURATION_MAX_MS, SCENE_TRANSITION_DURATION_MIN_MS, SCENE_TRANSITION_SWITCH_POINT_MAX, SCENE_TRANSITION_SWITCH_POINT_MIN, SCENE_TRANSITION_TYPES, } from "../values/scene-transition.js";
6
+ 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";
7
7
  import { ASSET_KINDS, EFFECTS_QUALITY_LEVELS, INJECT_TARGET_TYPES, LIVE2D_ENGINES, MEDIAPIPE_DELEGATES, POSE_SOURCE_IDS, TRACKING_SOURCE_IDS, TRACKING_SOURCE_KINDS, } from "./types.js";
8
8
  // Runtime validation for the request side of the wire. Schemas exist for the
9
9
  // methods whose params the app's main process consumes directly; methods without
@@ -19,6 +19,8 @@ export const SceneTransitionSchema = z.object({
19
19
  assetId: nonEmpty.refine(v => v.trim() !== '', 'assetId must not be blank').nullable(),
20
20
  switchPoint: z.number().min(SCENE_TRANSITION_SWITCH_POINT_MIN).max(SCENE_TRANSITION_SWITCH_POINT_MAX),
21
21
  autoFade: z.boolean().default(false),
22
+ fadeInMs: z.number().min(0).max(SCENE_TRANSITION_DURATION_MAX_MS).default(SCENE_TRANSITION_FADE_DEFAULT_MS),
23
+ fadeOutMs: z.number().min(0).max(SCENE_TRANSITION_DURATION_MAX_MS).default(SCENE_TRANSITION_FADE_DEFAULT_MS),
22
24
  });
23
25
  const port = z.number().int().min(1).max(65535);
24
26
  const lipSyncCalibration = z.discriminatedUnion('action', [
@@ -58,7 +60,6 @@ export const SettingsPatchSchema = z.object({
58
60
  .object({
59
61
  showFps: z.boolean().optional(),
60
62
  fpsLimit: z.number().optional(),
61
- selectionOutline: z.boolean().optional(),
62
63
  effectsQuality: z.enum(EFFECTS_QUALITY_LEVELS).optional(),
63
64
  renderScale: z.number().optional(),
64
65
  live2dEngine: z.enum(LIVE2D_ENGINES).optional(),
@@ -105,7 +106,6 @@ export const requestSchemas = {
105
106
  'scene.duplicate': z.object({ sceneId: nonEmpty }),
106
107
  'scene.rename': z.object({ sceneId: nonEmpty, name: nonEmpty }),
107
108
  'scene.delete': z.object({ sceneId: nonEmpty }),
108
- 'scene.setShortcut': z.object({ sceneId: nonEmpty, accelerator: nonEmpty.nullable() }),
109
109
  'instance.add': z.object({ modelId: nonEmpty }),
110
110
  'instance.setModel': z.object({ instanceId: nonEmpty, modelId: nonEmpty }),
111
111
  'object.add': z.object({
@@ -335,6 +335,8 @@ export type ShadowQuality = (typeof SHADOW_QUALITY_LEVELS)[number];
335
335
  export interface SceneLight {
336
336
  id: string;
337
337
  type: SceneLightType;
338
+ /** Mutes illumination and shadows. Defaults to true; omitted by hosts without light toggles. */
339
+ enabled?: boolean;
338
340
  color: string;
339
341
  intensity: number;
340
342
  azimuth: number;
@@ -854,6 +856,10 @@ export interface SceneTransition {
854
856
  switchPoint: number;
855
857
  /** Fade a video's opacity at its start and end; false preserves the clip's authored alpha. */
856
858
  autoFade: boolean;
859
+ /** Video fade-in playback time in milliseconds, 0–10,000, capped at the switch point; requires autoFade. */
860
+ fadeInMs: number;
861
+ /** Video fade-out playback time in milliseconds, 0–10,000, capped after the switch point; requires autoFade. */
862
+ fadeOutMs: number;
857
863
  }
858
864
  export interface Scene {
859
865
  id: string;
@@ -869,14 +875,10 @@ export interface Scene {
869
875
  environment: SceneEnvironment;
870
876
  /** Absent on older hosts; gate editing on the `scene-transitions` capability. */
871
877
  transition?: SceneTransition;
872
- /** Electron accelerator that applies this scene, or null. */
873
- shortcut: string | null;
874
878
  }
875
879
  export interface SceneState {
876
880
  scenes: Scene[];
877
881
  activeSceneId: string;
878
- /** Scene ids whose shortcut is currently registered with the OS. */
879
- registeredSceneIds: string[];
880
882
  }
881
883
  /**
882
884
  * Editable sections of the active scene. Items are deliberately absent — they
@@ -967,11 +969,14 @@ export interface HotkeyState extends HotkeyConfig {
967
969
  registered: string[];
968
970
  }
969
971
  /** Action kinds an app automation can carry today; servers may send kinds newer than this list. */
970
- export declare const AUTOMATION_ACTION_KINDS: readonly ["effect-toggle", "effect-params", "camera-pose", "reset-camera", "layer-visibility", "stream-mode", "delay"];
972
+ export declare const AUTOMATION_ACTION_KINDS: readonly ["effect-toggle", "effect-params", "camera-pose", "reset-camera", "layer-visibility", "stream-mode", "switch-scene", "toggle-expression", "play-motion", "remove-all-expressions", "load-model", "model-position", "delay"];
971
973
  export type AutomationActionKind = (typeof AUTOMATION_ACTION_KINDS)[number];
974
+ /** Kinds that end an action batch: the actions after one wait until it finishes. */
975
+ export declare const AUTOMATION_BOUNDARY_KINDS: readonly ["switch-scene", "load-model", "delay"];
976
+ export type AutomationBoundaryKind = (typeof AUTOMATION_BOUNDARY_KINDS)[number];
972
977
  /**
973
- * One app automation as clients see it. Action payloads stay app-side `actionKinds`
974
- * drives derived labels.
978
+ * One app automation as clients see it. Action kinds and scene targets drive derived
979
+ * labels; other action payloads stay app-side.
975
980
  */
976
981
  export interface AutomationInfo {
977
982
  id: string;
@@ -981,6 +986,8 @@ export interface AutomationInfo {
981
986
  accelerator: string | null;
982
987
  /** May include kinds newer than this SDK; label those generically. */
983
988
  actionKinds: string[];
989
+ /** Scene targets aligned with `actionKinds`; null for other actions. Older hosts omit this field. */
990
+ actionSceneIds?: (string | null)[];
984
991
  /** Whether the combo is currently registered with the OS. */
985
992
  registered: boolean;
986
993
  /** Whether this automation can run. */
@@ -1109,7 +1116,6 @@ export interface Settings {
1109
1116
  performance: {
1110
1117
  showFps: boolean;
1111
1118
  fpsLimit: number;
1112
- selectionOutline: boolean;
1113
1119
  effectsQuality: EffectsQuality;
1114
1120
  /** Absent on older hosts. */
1115
1121
  renderScale?: number;
@@ -1140,7 +1146,6 @@ export interface SettingsPatch {
1140
1146
  performance?: {
1141
1147
  showFps?: boolean;
1142
1148
  fpsLimit?: number;
1143
- selectionOutline?: boolean;
1144
1149
  effectsQuality?: EffectsQuality;
1145
1150
  renderScale?: number;
1146
1151
  live2dEngine?: Live2DEngine;
@@ -51,6 +51,18 @@ export const AUTOMATION_ACTION_KINDS = [
51
51
  'reset-camera',
52
52
  'layer-visibility',
53
53
  'stream-mode',
54
+ 'switch-scene',
55
+ 'toggle-expression',
56
+ 'play-motion',
57
+ 'remove-all-expressions',
58
+ 'load-model',
59
+ 'model-position',
60
+ 'delay',
61
+ ];
62
+ /** Kinds that end an action batch: the actions after one wait until it finishes. */
63
+ export const AUTOMATION_BOUNDARY_KINDS = [
64
+ 'switch-scene',
65
+ 'load-model',
54
66
  'delay',
55
67
  ];
56
68
  // ---- Settings ------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laplace.live/persona-sdk",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "TypeScript SDK and wire schema for the LAPLACE Persona plugin API",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -34,7 +34,7 @@
34
34
  "node": ">=24"
35
35
  },
36
36
  "dependencies": {
37
- "zod": "^4.5.4"
37
+ "zod": "^4.6.1"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "rimraf dist && tsc -p tsconfig.build.json",