@laplace.live/persona-sdk 1.17.0 → 1.19.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.
@@ -9,6 +9,17 @@ export declare function hasCommandModifier(accelerator: string | null): boolean;
9
9
  export declare function canRunInBackground(accelerator: string | null): boolean;
10
10
  /** True when a model hotkey is meant to fire while Persona sits in the background — what main registers on, and what the globe shows. */
11
11
  export declare function hotkeyRunsInBackground(h: Hotkey): boolean;
12
+ /**
13
+ * Why a `hold` hotkey toggles instead of holding, or null when it can hold. A combo registered with
14
+ * the OS reaches Persona as a press alone — Electron reports no key-up for it — and one registration
15
+ * serves every hotkey on the combo: `'own'` when this hotkey asks for the background itself,
16
+ * `'shared'` when only another one on its combo does, which `registered` (main's list) alone can tell.
17
+ */
18
+ export declare function holdBlockedBy(h: Pick<Hotkey, 'hold' | 'global' | 'accelerator'>, registered: boolean): 'own' | 'shared' | null;
19
+ /** VTS's editor limits for a hotkey's fade and auto-off times, in seconds; healing clamps API input to them. */
20
+ export declare const HOTKEY_FADE_SECONDS_MAX = 2;
21
+ export declare const HOTKEY_DEACTIVATE_SECONDS_MIN = 0.1;
22
+ export declare const HOTKEY_DEACTIVATE_SECONDS_MAX = 3600;
12
23
  /** Automations are enabled unless explicitly paused. */
13
24
  export declare function automationEnabled(s: {
14
25
  enabled?: boolean;
@@ -27,3 +38,25 @@ export declare function automationBindable<T extends {
27
38
  export declare function duplicateAccelerators(bindings: readonly {
28
39
  accelerator: string | null;
29
40
  }[]): Set<string>;
41
+ /** The slice of a scene model item the ordering reads; a full `SceneModelItem` comes back as itself. */
42
+ interface HotkeyModelLike {
43
+ kind: 'model';
44
+ instanceId: string;
45
+ ref: {
46
+ id: string;
47
+ };
48
+ }
49
+ /**
50
+ * The models whose hotkeys are live — every model in the scene, the primary first, one per model id
51
+ * (a model on two layers lists once). The desktop fires all of them, VTS-style, and every shortcut
52
+ * list shows them in this order; `hotkey.list`/`hotkey.trigger` take each one's `ref.id` as `modelId`.
53
+ * Generic over the item union so a scene document and the desktop's loaded runtimes both infer.
54
+ */
55
+ export declare function hotkeyModels<T extends HotkeyModelLike | {
56
+ kind: 'object';
57
+ instanceId: string;
58
+ }>(scene: {
59
+ items: readonly T[];
60
+ primaryInstanceId: string | null;
61
+ }): Extract<T, HotkeyModelLike>[];
62
+ export {};
@@ -25,6 +25,23 @@ export function canRunInBackground(accelerator) {
25
25
  export function hotkeyRunsInBackground(h) {
26
26
  return h.global && h.active && h.action !== null && canRunInBackground(h.accelerator);
27
27
  }
28
+ /**
29
+ * Why a `hold` hotkey toggles instead of holding, or null when it can hold. A combo registered with
30
+ * the OS reaches Persona as a press alone — Electron reports no key-up for it — and one registration
31
+ * serves every hotkey on the combo: `'own'` when this hotkey asks for the background itself,
32
+ * `'shared'` when only another one on its combo does, which `registered` (main's list) alone can tell.
33
+ */
34
+ export function holdBlockedBy(h, registered) {
35
+ if (h.hold !== true)
36
+ return null;
37
+ if (h.global && hasCommandModifier(h.accelerator))
38
+ return 'own';
39
+ return registered ? 'shared' : null;
40
+ }
41
+ /** VTS's editor limits for a hotkey's fade and auto-off times, in seconds; healing clamps API input to them. */
42
+ export const HOTKEY_FADE_SECONDS_MAX = 2;
43
+ export const HOTKEY_DEACTIVATE_SECONDS_MIN = 0.1;
44
+ export const HOTKEY_DEACTIVATE_SECONDS_MAX = 3600;
28
45
  /** Automations are enabled unless explicitly paused. */
29
46
  export function automationEnabled(s) {
30
47
  return s.enabled !== false;
@@ -49,3 +66,22 @@ export function duplicateAccelerators(bindings) {
49
66
  }
50
67
  return dupes;
51
68
  }
69
+ /**
70
+ * The models whose hotkeys are live — every model in the scene, the primary first, one per model id
71
+ * (a model on two layers lists once). The desktop fires all of them, VTS-style, and every shortcut
72
+ * list shows them in this order; `hotkey.list`/`hotkey.trigger` take each one's `ref.id` as `modelId`.
73
+ * Generic over the item union so a scene document and the desktop's loaded runtimes both infer.
74
+ */
75
+ export function hotkeyModels(scene) {
76
+ const models = scene.items.filter((i) => i.kind === 'model');
77
+ const primary = models.find(i => i.instanceId === scene.primaryInstanceId);
78
+ const out = [];
79
+ const seen = new Set();
80
+ for (const item of primary ? [primary, ...models] : models) {
81
+ if (seen.has(item.ref.id))
82
+ continue;
83
+ seen.add(item.ref.id);
84
+ out.push(item);
85
+ }
86
+ return out;
87
+ }
@@ -162,6 +162,10 @@ export declare const SPAWN_ALIVE_MAX = 500;
162
162
  export declare const SPAWN_SPREAD_MAX = 20;
163
163
  export declare const SPAWN_SPEED_MAX = 50;
164
164
  export declare const SPAWN_TTL_MAX_MS = 600000;
165
+ /** `stage.capture` longest-edge bounds and default, px. */
166
+ export declare const STAGE_CAPTURE_EDGE_MIN = 64;
167
+ export declare const STAGE_CAPTURE_EDGE_MAX = 2048;
168
+ export declare const STAGE_CAPTURE_EDGE_DEFAULT = 1024;
165
169
  /** Web object viewport bounds, CSS px per side. */
166
170
  export declare const WEB_SIZE_MIN = 16;
167
171
  export declare const WEB_SIZE_MAX = 7680;
@@ -282,6 +282,10 @@ export const SPAWN_ALIVE_MAX = 500;
282
282
  export const SPAWN_SPREAD_MAX = 20;
283
283
  export const SPAWN_SPEED_MAX = 50;
284
284
  export const SPAWN_TTL_MAX_MS = 600_000;
285
+ /** `stage.capture` longest-edge bounds and default, px. */
286
+ export const STAGE_CAPTURE_EDGE_MIN = 64;
287
+ export const STAGE_CAPTURE_EDGE_MAX = 2048;
288
+ export const STAGE_CAPTURE_EDGE_DEFAULT = 1024;
285
289
  /** Web object viewport bounds, CSS px per side. */
286
290
  export const WEB_SIZE_MIN = 16;
287
291
  export const WEB_SIZE_MAX = 7680;
@@ -6,6 +6,10 @@ export interface EventMap {
6
6
  'settings.changed': {
7
7
  settings: Settings;
8
8
  };
9
+ /**
10
+ * One model's live hotkey set changed — every model on stage has one, and a model that just left the
11
+ * stage arrives empty. Older hosts sent `modelId: null` for an empty stage instead.
12
+ */
9
13
  'hotkey.state': {
10
14
  modelId: string | null;
11
15
  config: HotkeyState;
@@ -23,6 +23,7 @@ export interface SceneActivateRequest {
23
23
  }
24
24
  export type SceneActivateResponse = SceneState;
25
25
  export type SceneCreateRequest = EmptyRequest;
26
+ /** The create activates the scene it adds: `activeSceneId` is the new scene's id. */
26
27
  export type SceneCreateResponse = SceneState;
27
28
  export interface SceneDuplicateRequest {
28
29
  sceneId: string;
@@ -339,6 +340,8 @@ export interface HotkeySetRequest {
339
340
  export type HotkeySetResponse = HotkeyState;
340
341
  export interface HotkeyTriggerRequest {
341
342
  hotkeyId: string;
343
+ /** The model that owns the hotkey — every model on stage has a live set; defaults to the primary. */
344
+ modelId?: string;
342
345
  }
343
346
  export type HotkeyTriggerResponse = EmptyResponse;
344
347
  export type AutomationListRequest = EmptyRequest;
@@ -493,6 +496,20 @@ export type StageClearSpawnedRequest = EmptyRequest;
493
496
  export interface StageClearSpawnedResponse {
494
497
  cleared: number;
495
498
  }
499
+ /**
500
+ * A PNG of the stage window as it is displayed — what a client that cannot see the screen
501
+ * checks its edits against. Each capture costs the stage a frame, so read on demand, never poll.
502
+ */
503
+ export interface StageCaptureRequest {
504
+ /** Longest edge in px, `STAGE_CAPTURE_EDGE_MIN`–`STAGE_CAPTURE_EDGE_MAX`; default `STAGE_CAPTURE_EDGE_DEFAULT`. */
505
+ maxEdge?: number;
506
+ }
507
+ export interface StageCaptureResponse {
508
+ /** `data:image/png;base64,…`, alpha preserved. */
509
+ dataUrl: string;
510
+ width: number;
511
+ height: number;
512
+ }
496
513
  export type AppInfoRequest = EmptyRequest;
497
514
  export interface AppInfoResponse {
498
515
  name: string;
@@ -1024,6 +1041,11 @@ export interface MethodMap {
1024
1041
  request: StageClearSpawnedRequest;
1025
1042
  response: StageClearSpawnedResponse;
1026
1043
  };
1044
+ /** Needs the `stage-capture` app capability. */
1045
+ 'stage.capture': {
1046
+ request: StageCaptureRequest;
1047
+ response: StageCaptureResponse;
1048
+ };
1027
1049
  'app.info': {
1028
1050
  request: AppInfoRequest;
1029
1051
  response: AppInfoResponse;
@@ -1089,3 +1111,4 @@ export interface MethodMap {
1089
1111
  export type MethodName = keyof MethodMap;
1090
1112
  export type MethodRequest<M extends MethodName> = MethodMap[M]['request'];
1091
1113
  export type MethodResponse<M extends MethodName> = MethodMap[M]['response'];
1114
+ export declare const METHOD_NAMES: readonly MethodName[];
@@ -1 +1,109 @@
1
- export {};
1
+ // Built from a `Record<MethodName, …>` so a new MethodMap key that is missing
2
+ // here fails the build instead of silently dropping out of the runtime list.
3
+ export const METHOD_NAMES = Object.keys({
4
+ 'scene.list': true,
5
+ 'scene.get': true,
6
+ 'scene.activate': true,
7
+ 'scene.create': true,
8
+ 'scene.duplicate': true,
9
+ 'scene.rename': true,
10
+ 'scene.delete': true,
11
+ 'scene.patch': true,
12
+ 'scene.setLightFollowCamera': true,
13
+ 'scene.inspect': true,
14
+ 'instance.list': true,
15
+ 'instance.get': true,
16
+ 'instance.add': true,
17
+ 'instance.setModel': true,
18
+ 'instance.remove': true,
19
+ 'instance.setVisible': true,
20
+ 'instance.setEffects': true,
21
+ 'instance.setTrackingSources': true,
22
+ 'instance.setControllerMovement': true,
23
+ 'instance.reorder': true,
24
+ 'instance.setPrimary': true,
25
+ 'instance.setPlacement': true,
26
+ 'instance.attach': true,
27
+ 'instance.setMToon': true,
28
+ 'instance.setIdle': true,
29
+ 'instance.setBreath': true,
30
+ 'instance.setLipSync': true,
31
+ 'instance.info': true,
32
+ 'selection.set': true,
33
+ 'selection.get': true,
34
+ 'object.add': true,
35
+ 'object.addMany': true,
36
+ 'object.rename': true,
37
+ 'object.setContent': true,
38
+ 'object.setSpace': true,
39
+ 'object.setPlacement': true,
40
+ 'object.attach': true,
41
+ 'object.setLightOverrides': true,
42
+ 'object.anchors': true,
43
+ 'expression.list': true,
44
+ 'expression.active': true,
45
+ 'expression.toggle': true,
46
+ 'expression.setWeight': true,
47
+ 'expression.getPersistence': true,
48
+ 'expression.setPersistence': true,
49
+ 'motion.list': true,
50
+ 'motion.play': true,
51
+ 'motion.playAsset': true,
52
+ 'motion.playing': true,
53
+ 'motion.stop': true,
54
+ 'speech.play': true,
55
+ 'speech.stop': true,
56
+ 'hotkey.list': true,
57
+ 'hotkey.set': true,
58
+ 'hotkey.trigger': true,
59
+ 'automation.list': true,
60
+ 'automation.run': true,
61
+ 'model.list': true,
62
+ 'model.register': true,
63
+ 'asset.list': true,
64
+ 'asset.register': true,
65
+ 'registry.thumbnail': true,
66
+ 'settings.get': true,
67
+ 'settings.patch': true,
68
+ 'tracking.status': true,
69
+ 'tracking.setEnabled': true,
70
+ 'tracking.setSource': true,
71
+ 'tracking.addSource': true,
72
+ 'tracking.updateSource': true,
73
+ 'tracking.removeSource': true,
74
+ 'pose.setEnabled': true,
75
+ 'pose.setSource': true,
76
+ 'pose.setPort': true,
77
+ 'lipSync.state': true,
78
+ 'lipSync.configure': true,
79
+ 'lipSync.restart': true,
80
+ 'lipSync.calibrate': true,
81
+ 'binding.get': true,
82
+ 'binding.preview': true,
83
+ 'binding.set': true,
84
+ 'binding.release': true,
85
+ 'binding.inputs': true,
86
+ 'binding.override': true,
87
+ 'controller.state': true,
88
+ 'controller.rename': true,
89
+ 'controller.setDeadZone': true,
90
+ 'controller.remove': true,
91
+ 'controller.assign': true,
92
+ 'stage.resetTransform': true,
93
+ 'stage.resetCamera': true,
94
+ 'stage.spawn': true,
95
+ 'stage.clearSpawned': true,
96
+ 'stage.capture': true,
97
+ 'app.info': true,
98
+ 'app.localAddresses': true,
99
+ 'app.stats': true,
100
+ 'session.identify': true,
101
+ 'storage.get': true,
102
+ 'storage.set': true,
103
+ 'storage.delete': true,
104
+ 'storage.list': true,
105
+ 'events.subscribe': true,
106
+ 'events.unsubscribe': true,
107
+ 'param.inject': true,
108
+ 'param.release': true,
109
+ });
@@ -163,6 +163,7 @@ export declare const requestSchemas: {
163
163
  }, z.core.$strip>;
164
164
  'hotkey.trigger': z.ZodObject<{
165
165
  hotkeyId: z.ZodString;
166
+ modelId: z.ZodOptional<z.ZodString>;
166
167
  }, z.core.$strip>;
167
168
  'automation.run': z.ZodObject<{
168
169
  automationId: z.ZodString;
@@ -330,6 +331,9 @@ export declare const requestSchemas: {
330
331
  outMs: z.ZodOptional<z.ZodNumber>;
331
332
  }, z.core.$strip>>;
332
333
  }, z.core.$strip>;
334
+ 'stage.capture': z.ZodObject<{
335
+ maxEdge: z.ZodOptional<z.ZodNumber>;
336
+ }, z.core.$strip>;
333
337
  'events.subscribe': z.ZodObject<{
334
338
  events: z.ZodArray<z.ZodCustom<keyof import("../events.ts").EventMap, keyof import("../events.ts").EventMap>>;
335
339
  }, z.core.$strip>;
@@ -2,7 +2,7 @@ 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
4
  import { ITEM_TRANSITION_STYLES } from "../../values/item-transition.js";
5
- import { OBJECT_ADD_MANY_MAX, SCENE_COLOR_RE, SPEECH_URL_MAX_LENGTH, STORAGE_KEY_MAX_LENGTH, STORAGE_VALUE_MAX_LENGTH, } from "../../values/limits.js";
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";
8
8
  import { ASSET_KINDS, INJECT_TARGET_TYPES, POSE_SOURCE_IDS, TRACKING_SOURCE_IDS, TRACKING_SOURCE_KINDS, } from "../types.js";
@@ -107,7 +107,7 @@ export const requestSchemas = {
107
107
  modelId: nonEmpty.optional(),
108
108
  config: z.custom(isRecord, 'config must be a hotkey config'),
109
109
  }),
110
- 'hotkey.trigger': z.object({ hotkeyId: nonEmpty }),
110
+ 'hotkey.trigger': z.object({ hotkeyId: nonEmpty, modelId: nonEmpty.optional() }),
111
111
  'automation.run': z.object({ automationId: nonEmpty }),
112
112
  'tracking.addSource': z.object({
113
113
  // Straight off the kind registry, for the same reason as ASSET_KINDS below: a schema
@@ -168,6 +168,9 @@ export const requestSchemas = {
168
168
  })
169
169
  .optional(),
170
170
  }),
171
+ 'stage.capture': z.object({
172
+ maxEdge: z.number().int().min(STAGE_CAPTURE_EDGE_MIN).max(STAGE_CAPTURE_EDGE_MAX).optional(),
173
+ }),
171
174
  'events.subscribe': z.object({ events: z.array(lenientEventNameSchema).min(1) }),
172
175
  'events.unsubscribe': z.object({ events: z.array(lenientEventNameSchema).optional() }),
173
176
  'param.inject': z.object({ entries: z.array(InjectEntrySchema).min(1) }),
@@ -1011,7 +1011,7 @@ export interface Scene {
1011
1011
  name: string;
1012
1012
  /** Models and objects in one list; array order = z-order (bottom to top) within each space. */
1013
1013
  items: SceneItem[];
1014
- /** Hotkey target and the default instance for model-scoped methods. null only when `items` holds no model. */
1014
+ /** The default hotkey target and the default instance for model-scoped methods. null only when `items` holds no model. */
1015
1015
  primaryInstanceId: string | null;
1016
1016
  background: SceneBackground;
1017
1017
  vrmCamera: SceneCamera;
@@ -1041,7 +1041,7 @@ export interface ScenePatch {
1041
1041
  * App-level features a client gates on (never version-sniff): `hello` and
1042
1042
  * `app.info` report them — the per-app mirror of {@link InstanceRuntime.capabilities}.
1043
1043
  */
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"];
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"];
1045
1045
  export type AppCapability = (typeof APP_CAPABILITIES)[number];
1046
1046
  export declare function isAppCapability(v: unknown): v is AppCapability;
1047
1047
  /** What a loaded model instance can do; absent capabilities answer `unsupported-for-format`. */
@@ -1115,13 +1115,21 @@ export interface Hotkey {
1115
1115
  accelerator: string | null;
1116
1116
  global: boolean;
1117
1117
  active: boolean;
1118
+ /** Expression: on only while the combo is held down (VTS `DeactivateAfterKeyUp`). Default false. */
1119
+ hold?: boolean;
1120
+ /** Fade time in seconds, in and out, of the expression or motion; null uses the file's own. 0–2. */
1121
+ fadeSeconds?: number | null;
1122
+ /** Expression: switches itself off after this many seconds. Motion: plays at most this long. null is off. 0.1–3600. */
1123
+ deactivateAfterSeconds?: number | null;
1124
+ /** Motion: holds its last frame until the hotkey fires again (VTS `StopsOnLastFrame`). Default false. */
1125
+ stopsOnLastFrame?: boolean;
1118
1126
  }
1119
1127
  export interface HotkeyConfig {
1120
1128
  keyboardEnabled: boolean;
1121
1129
  hotkeys: Hotkey[];
1122
1130
  }
1123
1131
  export interface HotkeyState extends HotkeyConfig {
1124
- /** Hotkey ids currently registered with the OS (active model only). */
1132
+ /** Hotkey ids currently registered with the OS (models on stage only). */
1125
1133
  registered: string[];
1126
1134
  }
1127
1135
  /** Action kinds an app automation can carry today; servers may send kinds newer than this list. */
@@ -51,6 +51,8 @@ export const APP_CAPABILITIES = [
51
51
  'tracking-lost',
52
52
  /** `motion.stop`; `motion.playing` and `motion.started` carry `oneShot`. */
53
53
  'motion-stop',
54
+ /** `stage.capture`. */
55
+ 'stage-capture',
54
56
  ];
55
57
  export function isAppCapability(v) {
56
58
  return isOneOf(v, APP_CAPABILITIES);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laplace.live/persona-sdk",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "description": "TypeScript SDK and wire schema for the LAPLACE Persona plugin API",
5
5
  "license": "MIT",
6
6
  "type": "module",