portals-mcp 1.3.2 → 1.3.4

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.
Files changed (41) hide show
  1. package/dist/marketplace-client.d.ts +9 -0
  2. package/dist/marketplace-client.d.ts.map +1 -1
  3. package/dist/marketplace-client.js +120 -20
  4. package/dist/resources/index/effects.json +88 -1
  5. package/dist/resources/index/intents.json +164 -164
  6. package/dist/resources/index/items.json +59 -8
  7. package/dist/resources/index/knowledge-map.json +13 -0
  8. package/dist/resources/index/triggers.json +28 -0
  9. package/dist/resources/logic/js-function-reference.md +16 -6
  10. package/dist/resources/logic/js-use-effector.md +60 -7
  11. package/dist/resources/python/README.md +2 -0
  12. package/dist/resources/python/lib/portals_core.py +159 -8
  13. package/dist/resources/python/lib/portals_effects.py +180 -7
  14. package/dist/resources/python/lib/portals_ops.py +148 -16
  15. package/dist/resources/python/lib/portals_utils.py +1171 -1170
  16. package/dist/resources/python/tools/build_reactgui_feature_test_room.py +386 -0
  17. package/dist/resources/python/tools/validate_room.py +430 -40
  18. package/dist/resources/python/tools/verify_reactgui_feature_room.py +199 -0
  19. package/dist/resources/ref/items/camera.json +52 -47
  20. package/dist/resources/ref/items/cube.json +51 -5
  21. package/dist/resources/ref/items/effects-vfx.json +171 -115
  22. package/dist/resources/ref/items/glb.json +97 -64
  23. package/dist/resources/ref/items/text.json +71 -69
  24. package/dist/resources/ref/items/trigger-zone.json +4 -0
  25. package/dist/resources/ref/settings.json +198 -191
  26. package/dist/resources/ref/systems/function-effector-js.json +9 -6
  27. package/dist/resources/ref/systems/use-effector.json +74 -73
  28. package/dist/resources/ref/template-layouts.json +2594 -0
  29. package/dist/resources/reference/api-cheatsheet.md +16 -3
  30. package/dist/resources/reference/cameras.md +569 -565
  31. package/dist/resources/reference/gotchas.md +17 -4
  32. package/dist/resources/reference/interactions.md +6 -1
  33. package/dist/resources/reference/items/building.md +52 -0
  34. package/dist/resources/reference/items/effects.md +110 -170
  35. package/dist/resources/reference/items/gameplay.md +7 -5
  36. package/dist/resources/reference/items/models.md +184 -168
  37. package/dist/resources/reference/reactgui-feature-test-room.md +97 -0
  38. package/dist/resources/reference/template-layouts.md +167 -0
  39. package/dist/tools.d.ts.map +1 -1
  40. package/dist/tools.js +163 -20
  41. package/package.json +1 -1
@@ -24,6 +24,12 @@ export interface MarketplaceItem {
24
24
  claimCount: number;
25
25
  acquireCount: number;
26
26
  }
27
+ export interface MarketplaceGlbResolution {
28
+ glb: string | null;
29
+ source: "catalog_direct" | "user_inventory" | "room_build_items" | "unresolved";
30
+ attachedToRoomInventory: boolean;
31
+ reason?: string | undefined;
32
+ }
27
33
  export interface MarketplaceFilter {
28
34
  query?: string | undefined;
29
35
  type?: string | undefined;
@@ -44,6 +50,8 @@ export interface ClaimResult {
44
50
  itemId: string;
45
51
  message?: string | undefined;
46
52
  }
53
+ export declare function isMarketplaceAssetRoute(value: string | null | undefined): boolean;
54
+ export declare function isRuntimeGlbUrl(value: string | null | undefined): boolean;
47
55
  export declare function clearMarketplaceCache(): void;
48
56
  export declare function searchMarketplace(filter?: MarketplaceFilter): Promise<MarketplaceItem[]>;
49
57
  export declare function getPackItems(packId: string): Promise<MarketplaceItem[]>;
@@ -116,4 +124,5 @@ export interface OwnershipCheck {
116
124
  export declare function verifyOwnership(cids: string[]): Promise<OwnershipCheck[]>;
117
125
  export declare function attachItemsToRoom(roomId: string, itemIds: string[]): Promise<AttachToRoomResult>;
118
126
  export declare function claimMarketplaceItem(itemId: string): Promise<ClaimResult>;
127
+ export declare function resolveMarketplaceGlbUrl(item: MarketplaceItem, roomId?: string): Promise<MarketplaceGlbResolution>;
119
128
  //# sourceMappingURL=marketplace-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"marketplace-client.d.ts","sourceRoot":"","sources":["../src/marketplace-client.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,eAAe,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAC9C,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AAkBD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AA0DD,wBAAsB,iBAAiB,CAAC,MAAM,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAkClG;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAK7E;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAGxF;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC,CAcD;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CASrE;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAehF;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrC,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAmD/E;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA4CtG;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8B/E"}
1
+ {"version":3,"file":"marketplace-client.d.ts","sourceRoot":"","sources":["../src/marketplace-client.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,54 @@
1
1
  import fetch from "node-fetch";
2
+ import { getAccessKey, isAuthenticated } from "./api.js";
2
3
  const API_BASE = process.env.API_BASE || "https://portals.to";
3
4
  const CACHE_TTL_MS = 60_000;
4
5
  let catalogueCache = null;
6
+ function getRuntimeAccessKey() {
7
+ if (process.env.PORTALS_ACCESS_KEY)
8
+ return process.env.PORTALS_ACCESS_KEY;
9
+ if (!isAuthenticated())
10
+ return null;
11
+ try {
12
+ return getAccessKey();
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function authHeaders() {
19
+ const headers = {};
20
+ const token = process.env.PORTALS_USER_TOKEN;
21
+ const accessKey = getRuntimeAccessKey();
22
+ if (token)
23
+ headers["Authorization"] = `Bearer ${token}`;
24
+ if (accessKey)
25
+ headers["x-access-key"] = accessKey;
26
+ return headers;
27
+ }
28
+ function jsonAuthHeaders() {
29
+ return { "Content-Type": "application/json", ...authHeaders() };
30
+ }
31
+ export function isMarketplaceAssetRoute(value) {
32
+ if (!value)
33
+ return false;
34
+ try {
35
+ return new URL(value, API_BASE).pathname.startsWith("/api/v2/marketplace/asset/");
36
+ }
37
+ catch {
38
+ return value.startsWith("/api/v2/marketplace/asset/");
39
+ }
40
+ }
41
+ export function isRuntimeGlbUrl(value) {
42
+ if (!value)
43
+ return false;
44
+ if (isMarketplaceAssetRoute(value))
45
+ return false;
46
+ const trimmed = value.trim();
47
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("~") || trimmed.startsWith("#")) {
48
+ return /\.glb(?:[?#]|$)/i.test(trimmed);
49
+ }
50
+ return false;
51
+ }
5
52
  async function fetchCatalogue() {
6
53
  if (catalogueCache && Date.now() - catalogueCache.ts < CACHE_TTL_MS) {
7
54
  return catalogueCache.items;
@@ -156,7 +203,7 @@ export async function getUserInventory() {
156
203
  if (!token)
157
204
  return [];
158
205
  const res = await fetch(`${API_BASE}/api/v2/marketplace/my-items`, {
159
- headers: { Authorization: `Bearer ${token}` },
206
+ headers: authHeaders(),
160
207
  });
161
208
  if (!res.ok)
162
209
  return [];
@@ -171,17 +218,10 @@ export async function getUserInventory() {
171
218
  * scene with AI so we know what assets the user actually has.
172
219
  */
173
220
  export async function getRoomBuildItems(roomId) {
174
- const token = process.env.PORTALS_USER_TOKEN;
175
- const accessKey = process.env.PORTALS_ACCESS_KEY;
176
221
  if (!roomId)
177
222
  return [];
178
- const headers = {};
179
- if (token)
180
- headers["Authorization"] = `Bearer ${token}`;
181
- if (accessKey)
182
- headers["x-access-key"] = accessKey;
183
223
  try {
184
- const res = await fetch(`${API_BASE}/api/v2/room/get-build-items?roomId=${encodeURIComponent(roomId)}`, { headers });
224
+ const res = await fetch(`${API_BASE}/api/v2/room/get-build-items?roomId=${encodeURIComponent(roomId)}`, { headers: authHeaders() });
185
225
  if (!res.ok)
186
226
  return [];
187
227
  const json = (await res.json());
@@ -199,7 +239,7 @@ export async function getRoomBuildItems(roomId) {
199
239
  */
200
240
  export async function verifyOwnership(cids) {
201
241
  const token = process.env.PORTALS_USER_TOKEN;
202
- const accessKey = process.env.PORTALS_ACCESS_KEY;
242
+ const accessKey = getRuntimeAccessKey();
203
243
  if (!token && !accessKey) {
204
244
  // No auth — return unknowns (the caller decides how to proceed).
205
245
  return cids.map((cid) => ({
@@ -210,9 +250,7 @@ export async function verifyOwnership(cids) {
210
250
  }));
211
251
  }
212
252
  // Pull catalogue + user inventory in parallel.
213
- const headers = {};
214
- if (token)
215
- headers["Authorization"] = `Bearer ${token}`;
253
+ const headers = authHeaders();
216
254
  const [inventoryRes, catalogue] = await Promise.all([
217
255
  token ? fetch(`${API_BASE}/api/v2/marketplace/my-items`, { headers }) : Promise.resolve(null),
218
256
  fetchCatalogue(),
@@ -248,18 +286,14 @@ export async function verifyOwnership(cids) {
248
286
  }
249
287
  export async function attachItemsToRoom(roomId, itemIds) {
250
288
  const token = process.env.PORTALS_USER_TOKEN;
251
- const accessKey = process.env.PORTALS_ACCESS_KEY;
289
+ const accessKey = getRuntimeAccessKey();
252
290
  if (!token && !accessKey) {
253
- return { ok: false, code: "AUTH_REQUIRED", message: "No Firebase token or access key in env.", attachedItemIds: [] };
291
+ return { ok: false, code: "AUTH_REQUIRED", message: "No Firebase token, access key env, or authenticated MCP access key available.", attachedItemIds: [] };
254
292
  }
255
293
  if (!roomId) {
256
294
  return { ok: false, code: "ROOM_ID_REQUIRED", message: "roomId is required.", attachedItemIds: [] };
257
295
  }
258
- const headers = { "Content-Type": "application/json" };
259
- if (token)
260
- headers["Authorization"] = `Bearer ${token}`;
261
- if (accessKey)
262
- headers["x-access-key"] = accessKey;
296
+ const headers = jsonAuthHeaders();
263
297
  let existing = [];
264
298
  try {
265
299
  const settingsRes = await fetch(`${API_BASE}/api/v2/room/get-settings?roomID=${encodeURIComponent(roomId)}`, { headers });
@@ -324,3 +358,69 @@ export async function claimMarketplaceItem(itemId) {
324
358
  message: body.message,
325
359
  };
326
360
  }
361
+ function matchBuildItem(items, itemId) {
362
+ return items.find((item) => item.id === itemId) ?? null;
363
+ }
364
+ function matchInventoryItem(items, itemId) {
365
+ return items.find((item) => item.itemId === itemId || item.id === itemId) ?? null;
366
+ }
367
+ export async function resolveMarketplaceGlbUrl(item, roomId) {
368
+ if (isRuntimeGlbUrl(item.glb)) {
369
+ return {
370
+ glb: item.glb,
371
+ source: "catalog_direct",
372
+ attachedToRoomInventory: false,
373
+ };
374
+ }
375
+ const inventoryItem = matchInventoryItem(await getUserInventory(), item.id);
376
+ if (inventoryItem?.glb && isRuntimeGlbUrl(inventoryItem.glb)) {
377
+ return {
378
+ glb: inventoryItem.glb,
379
+ source: "user_inventory",
380
+ attachedToRoomInventory: false,
381
+ };
382
+ }
383
+ if (!roomId) {
384
+ return {
385
+ glb: null,
386
+ source: "unresolved",
387
+ attachedToRoomInventory: false,
388
+ reason: isMarketplaceAssetRoute(item.glb)
389
+ ? "Catalog glb is a protected marketplace asset route; pass roomId so MCP can resolve the room build-palette CDN URL."
390
+ : "Catalog item did not include a runtime GLB URL.",
391
+ };
392
+ }
393
+ let buildItems = await getRoomBuildItems(roomId);
394
+ let buildItem = matchBuildItem(buildItems, item.id);
395
+ if (buildItem?.glb && isRuntimeGlbUrl(buildItem.glb)) {
396
+ return {
397
+ glb: buildItem.glb,
398
+ source: "room_build_items",
399
+ attachedToRoomInventory: false,
400
+ };
401
+ }
402
+ const attachResult = await attachItemsToRoom(roomId, [item.id]);
403
+ if (!attachResult.ok) {
404
+ return {
405
+ glb: null,
406
+ source: "unresolved",
407
+ attachedToRoomInventory: false,
408
+ reason: `Could not attach marketplace item to room inventory for URL resolution (${attachResult.code}${attachResult.message ? `: ${attachResult.message}` : ""}).`,
409
+ };
410
+ }
411
+ buildItems = await getRoomBuildItems(roomId);
412
+ buildItem = matchBuildItem(buildItems, item.id);
413
+ if (buildItem?.glb && isRuntimeGlbUrl(buildItem.glb)) {
414
+ return {
415
+ glb: buildItem.glb,
416
+ source: "room_build_items",
417
+ attachedToRoomInventory: true,
418
+ };
419
+ }
420
+ return {
421
+ glb: null,
422
+ source: "unresolved",
423
+ attachedToRoomInventory: true,
424
+ reason: "Item was attached to the room inventory, but get_room_build_items did not return a runtime .glb URL for it.",
425
+ };
426
+ }
@@ -39,6 +39,16 @@
39
39
  "Scale in relative mode is ADDITIVE (scale [1,1,1] adds 1 to each axis per trigger)"
40
40
  ]
41
41
  },
42
+ {
43
+ "$type": "SetTransform",
44
+ "category": "movement",
45
+ "parameters": {
46
+ "position": "array - optional [x,y,z] world position",
47
+ "rotation": "array - optional [x,y,z] Euler degrees",
48
+ "scale": "array - optional [x,y,z] scale"
49
+ },
50
+ "gotchas": ["Immediate local-player transform write. Include at least one of position, rotation, or scale."]
51
+ },
42
52
  {
43
53
  "$type": "PortalsAnimation",
44
54
  "category": "movement",
@@ -76,6 +86,82 @@
76
86
  "parameters": {},
77
87
  "gotchas": []
78
88
  },
89
+ {
90
+ "$type": "AddForceToCubeEffector",
91
+ "category": "physics",
92
+ "parameters": {
93
+ "fx": "float - impulse X",
94
+ "fy": "float - impulse Y",
95
+ "fz": "float - impulse Z",
96
+ "rel": "bool - true=force vector is local to item rotation"
97
+ },
98
+ "gotchas": ["Direct task effector only; not in ReactGUI UseEffector/PrepareEffector map. Works on ResizableCube and GLB-family physics items."]
99
+ },
100
+ {
101
+ "$type": "AddTorqueToCubeEffector",
102
+ "category": "physics",
103
+ "parameters": {
104
+ "tx": "float - rotational impulse X",
105
+ "ty": "float - rotational impulse Y",
106
+ "tz": "float - rotational impulse Z",
107
+ "rel": "bool - true=torque vector is local to item rotation"
108
+ },
109
+ "gotchas": ["Direct task effector only; not in ReactGUI UseEffector/PrepareEffector map. Works on ResizableCube and GLB-family physics items."]
110
+ },
111
+ {
112
+ "$type": "ExplosionForceCubeEffector",
113
+ "category": "physics",
114
+ "parameters": {
115
+ "f": "float - explosion impulse force",
116
+ "r": "float - explosion radius",
117
+ "u": "float - upward modifier",
118
+ "ox/oy/oz": "floats - origin offset from item position",
119
+ "aff": "bool - true=also affect nearby physics cubes"
120
+ },
121
+ "gotchas": ["Direct task effector only; not in ReactGUI UseEffector/PrepareEffector map."]
122
+ },
123
+ {
124
+ "$type": "SetCubeVelocityEffector",
125
+ "category": "physics",
126
+ "parameters": {
127
+ "vx/vy/vz": "floats - linear velocity",
128
+ "ax/ay/az": "floats - angular velocity",
129
+ "sl": "bool - set linear velocity",
130
+ "sa": "bool - set angular velocity",
131
+ "rel": "bool - true=vectors are local to item rotation"
132
+ },
133
+ "gotchas": ["Direct task effector only. Velocity assignment is most reliable when this client already owns the physics item."]
134
+ },
135
+ {
136
+ "$type": "ResetCubeVelocityEffector",
137
+ "category": "physics",
138
+ "parameters": {},
139
+ "gotchas": ["Direct task effector only. Stops linear and angular velocity on the owned physics item."]
140
+ },
141
+ {
142
+ "$type": "ToggleCubeGravityEffector",
143
+ "category": "physics",
144
+ "parameters": {
145
+ "g": "bool - true=use gravity, false=float"
146
+ },
147
+ "gotchas": ["Direct task effector only. Ensures physics is attached before changing gravity."]
148
+ },
149
+ {
150
+ "$type": "AttachToMouseCubeEffector",
151
+ "category": "physics",
152
+ "parameters": {
153
+ "snap": "bool - true=snap item bottom to placeable surfaces"
154
+ },
155
+ "gotchas": ["JS UseEffector name is AttachToMouse, while direct room-data $type is AttachToMouseCubeEffector."]
156
+ },
157
+ {
158
+ "$type": "DetachFromMouseCubeEffector",
159
+ "category": "physics",
160
+ "parameters": {
161
+ "save": "bool - true=persist final drop position when server permissions and item opt-in allow it"
162
+ },
163
+ "gotchas": ["JS UseEffector name is DetachFromMouse, while direct room-data $type is DetachFromMouseCubeEffector."]
164
+ },
79
165
  {
80
166
  "$type": "AddVelocityToPlayer",
81
167
  "category": "player",
@@ -235,7 +321,8 @@
235
321
  "category": "camera",
236
322
  "parameters": {
237
323
  "camState": "string — camera state name (matches CameraObject sn)",
238
- "transitionSpeed": "floattransition speed"
324
+ "smoothTransition": "boolblend to the new state, default true",
325
+ "transitionSpeed": "float — transition speed, default 6.0"
239
326
  },
240
327
  "gotchas": []
241
328
  },