@unseenco/theatre-core 0.1.9 → 0.1.10

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/README.md CHANGED
@@ -40,6 +40,34 @@ Join us on [Discord](https://discord.gg/bm9f8F9Y9N), follow the updates on [twit
40
40
 
41
41
  Theatre.js comes in two packages: `@unseenco/theatre-core` (the library) and `@unseenco/theatre-studio` (the editor). This package is the core library.
42
42
 
43
+ ### Listing and unloading sheets / objects
44
+
45
+ Runtime helpers for tearing down loaded sheets and objects (for example when switching scenes). These drop in-memory instances so Studio stops showing them, but **do not** clear persisted project state. Recreating the same `sheetId` / object `key` restores prior prop overrides and sequence data.
46
+
47
+ ```ts
48
+ import {getProject} from '@unseenco/theatre-core'
49
+
50
+ const project = getProject('My project')
51
+ const sheet = project.sheet('Scene')
52
+ sheet.object('Box', {x: 0, y: 0})
53
+
54
+ // List what is currently loaded
55
+ project.getSheets() // ISheet[]
56
+ sheet.getObjects() // ISheetObject[]
57
+
58
+ // Detach one object (sheet stays loaded)
59
+ sheet.detachObject('Box')
60
+
61
+ // Unload this sheet instance (all of its objects, then the sheet)
62
+ sheet.unload()
63
+
64
+ // Or from the project:
65
+ project.unloadSheet('Scene') // optional second arg: instanceId
66
+ project.unloadSheets() // unload every loaded sheet
67
+ ```
68
+
69
+ Try the interactive demo in the playground: `/shared/unload-sheets/`.
70
+
43
71
  ## Bundle size
44
72
 
45
73
  `@unseenco/theatre-core` is currently around 20KiB compressed with all its dependencies.
package/dist/index.js CHANGED
@@ -6520,6 +6520,12 @@ var TheatreSheet = class {
6520
6520
  }
6521
6521
  internal2.deleteObject(sanitizedPath);
6522
6522
  }
6523
+ getObjects() {
6524
+ return privateAPI(this).getObjects().map((obj) => obj.publicApi);
6525
+ }
6526
+ unload() {
6527
+ privateAPI(this).unload();
6528
+ }
6523
6529
  declareOutlineNamespace(namespacePath, opts) {
6524
6530
  const internal2 = privateAPI(this);
6525
6531
  const parsedPath = parseOutlineNamespacePath(
@@ -6607,12 +6613,36 @@ var Sheet = class {
6607
6613
  getObject(key) {
6608
6614
  return this._objects.get()[key];
6609
6615
  }
6616
+ getObjects() {
6617
+ return Object.values(this._objects.get()).filter(
6618
+ (obj) => !!obj
6619
+ );
6620
+ }
6610
6621
  deleteObject(objectKey) {
6622
+ const obj = this._objects.get()[objectKey];
6611
6623
  this._objects.reduce((state) => {
6612
6624
  const newState = { ...state };
6613
6625
  delete newState[objectKey];
6614
6626
  return newState;
6615
6627
  });
6628
+ if (obj) {
6629
+ this.project._remoteSync.unregisterObject(obj);
6630
+ }
6631
+ }
6632
+ /**
6633
+ * Runtime-only teardown: pause sequences, detach all objects, and remove
6634
+ * this sheet instance from the project. Persisted state is kept.
6635
+ */
6636
+ unload() {
6637
+ for (const sequence of Object.values(this._sequences)) {
6638
+ sequence.pause();
6639
+ }
6640
+ for (const objectKey of Object.keys(
6641
+ this._objects.get()
6642
+ )) {
6643
+ this.deleteObject(objectKey);
6644
+ }
6645
+ this.project._unloadSheetInstance(this);
6616
6646
  }
6617
6647
  getSequence(variant) {
6618
6648
  const variantId = variant ?? (0, import_theatre_dataverse15.val)(this._activeSequenceVariant.pointer);
@@ -6707,6 +6737,21 @@ var SheetTemplate = class {
6707
6737
  }
6708
6738
  return inst;
6709
6739
  }
6740
+ getInstances() {
6741
+ return Object.values(this._instances.get()).filter(
6742
+ (inst) => !!inst
6743
+ );
6744
+ }
6745
+ hasInstance(instanceId) {
6746
+ return !!this._instances.get()[instanceId];
6747
+ }
6748
+ removeInstance(instanceId) {
6749
+ this._instances.reduce((state) => {
6750
+ const next = { ...state };
6751
+ delete next[instanceId];
6752
+ return next;
6753
+ });
6754
+ }
6710
6755
  getObjectTemplate(objectKey, nativeObject, config, actions, transient, staticPropPaths) {
6711
6756
  let template = this._objectTemplates.get()[objectKey];
6712
6757
  if (!template) {
@@ -7240,6 +7285,7 @@ var RemoteSync = class {
7240
7285
  __publicField(this, "channel");
7241
7286
  __publicField(this, "sheets", /* @__PURE__ */ new Map());
7242
7287
  __publicField(this, "objects", /* @__PURE__ */ new Map());
7288
+ __publicField(this, "objectUnsubs", /* @__PURE__ */ new Map());
7243
7289
  __publicField(this, "studio");
7244
7290
  __publicField(this, "activeSheet");
7245
7291
  if (typeof BroadcastChannel === "undefined")
@@ -7277,12 +7323,26 @@ var RemoteSync = class {
7277
7323
  registerSheet(sheet) {
7278
7324
  this.sheets.set(sheet.address.sheetId, sheet);
7279
7325
  }
7326
+ /**
7327
+ * Removes `sheet` from the sync map if it is the currently registered
7328
+ * instance for its `sheetId`. Returns whether it was removed.
7329
+ */
7330
+ unregisterSheet(sheet) {
7331
+ if (this.sheets.get(sheet.address.sheetId) !== sheet) {
7332
+ return false;
7333
+ }
7334
+ this.sheets.delete(sheet.address.sheetId);
7335
+ if (this.activeSheet === sheet) {
7336
+ this.activeSheet = void 0;
7337
+ }
7338
+ return true;
7339
+ }
7280
7340
  registerObject(obj) {
7281
7341
  const id = "".concat(obj.address.sheetId, "_").concat(obj.address.objectKey);
7282
7342
  this.objects.set(id, obj);
7283
7343
  if (this.isEditor && this.channel) {
7284
7344
  const channel = this.channel;
7285
- obj.onFinalValueChange((values) => {
7345
+ const unsubscribe = obj.onFinalValueChange((values) => {
7286
7346
  const message = {
7287
7347
  event: "updateSheetObject",
7288
7348
  // Some prop values (e.g. rgba colors) are class instances with
@@ -7293,6 +7353,16 @@ var RemoteSync = class {
7293
7353
  };
7294
7354
  channel.postMessage(message);
7295
7355
  });
7356
+ this.objectUnsubs.set(id, unsubscribe);
7357
+ }
7358
+ }
7359
+ unregisterObject(obj) {
7360
+ const id = "".concat(obj.address.sheetId, "_").concat(obj.address.objectKey);
7361
+ this.objects.delete(id);
7362
+ const unsubscribe = this.objectUnsubs.get(id);
7363
+ if (unsubscribe) {
7364
+ unsubscribe();
7365
+ this.objectUnsubs.delete(id);
7296
7366
  }
7297
7367
  }
7298
7368
  attachStudio(studio) {
@@ -7630,6 +7700,61 @@ var Project = class {
7630
7700
  this._remoteSync.registerSheet(sheet);
7631
7701
  return sheet;
7632
7702
  }
7703
+ getSheets() {
7704
+ const sheets = [];
7705
+ for (const template of Object.values(this._sheetTemplates.get())) {
7706
+ if (!template)
7707
+ continue;
7708
+ sheets.push(...template.getInstances());
7709
+ }
7710
+ return sheets;
7711
+ }
7712
+ /**
7713
+ * Runtime-only: unload one or all instances of a sheet. Persisted state is kept.
7714
+ * When `instanceId` is omitted, all loaded instances of `sheetId` are unloaded.
7715
+ */
7716
+ unloadSheet(sheetId, instanceId) {
7717
+ const template = this._sheetTemplates.get()[sheetId];
7718
+ if (!template) {
7719
+ return false;
7720
+ }
7721
+ const instances = instanceId ? template.hasInstance(instanceId) ? [template.getInstance(instanceId)] : [] : template.getInstances();
7722
+ if (instances.length === 0) {
7723
+ return false;
7724
+ }
7725
+ for (const sheet of instances) {
7726
+ sheet.unload();
7727
+ }
7728
+ return true;
7729
+ }
7730
+ unloadSheets() {
7731
+ for (const sheet of this.getSheets()) {
7732
+ sheet.unload();
7733
+ }
7734
+ }
7735
+ /**
7736
+ * Called by {@link Sheet.unload} after objects are detached. Removes the
7737
+ * instance from its template and drops the template when no instances remain.
7738
+ */
7739
+ _unloadSheetInstance(sheet) {
7740
+ const sheetId = sheet.address.sheetId;
7741
+ const template = this._sheetTemplates.get()[sheetId];
7742
+ if (!template || !template.hasInstance(sheet.instanceId)) {
7743
+ return;
7744
+ }
7745
+ template.removeInstance(sheet.instanceId);
7746
+ const wasRegistered = this._remoteSync.unregisterSheet(sheet);
7747
+ const remaining = template.getInstances();
7748
+ if (remaining.length === 0) {
7749
+ this._sheetTemplates.reduce((state) => {
7750
+ const next = { ...state };
7751
+ delete next[sheetId];
7752
+ return next;
7753
+ });
7754
+ } else if (wasRegistered) {
7755
+ this._remoteSync.registerSheet(remaining[0]);
7756
+ }
7757
+ }
7633
7758
  _commitOutlineNamespaceConfig(sheetId, namespacePathKey, config) {
7634
7759
  this._mutateCoreAhistoric((ahistoric) => {
7635
7760
  var _a;
@@ -7890,6 +8015,43 @@ var TheatreProject = class {
7890
8015
  sheetOpts
7891
8016
  ).publicApi;
7892
8017
  }
8018
+ getSheets() {
8019
+ return privateAPI(this).getSheets().map((sheet) => sheet.publicApi);
8020
+ }
8021
+ unloadSheet(sheetId, instanceId) {
8022
+ const sanitizedPath = validateAndSanitiseSlashedPathOrThrow(
8023
+ sheetId,
8024
+ "project.unloadSheet"
8025
+ );
8026
+ let sanitizedInstanceId;
8027
+ if (instanceId !== void 0) {
8028
+ if (process.env.NODE_ENV !== "production") {
8029
+ validateInstanceId(
8030
+ instanceId,
8031
+ "instanceId in project.unloadSheet(sheetId, instanceId)",
8032
+ true
8033
+ );
8034
+ }
8035
+ sanitizedInstanceId = instanceId;
8036
+ }
8037
+ const unloaded = privateAPI(this).unloadSheet(
8038
+ sanitizedPath,
8039
+ sanitizedInstanceId
8040
+ );
8041
+ if (!unloaded) {
8042
+ const instanceHint = instanceId !== void 0 ? ' (instance "'.concat(instanceId, '")') : "";
8043
+ notify.warning(
8044
+ "Couldn't unload sheet \"".concat(sanitizedPath, '"').concat(instanceHint),
8045
+ 'There is no loaded sheet with id "'.concat(sanitizedPath, '"').concat(instanceHint, ".\n\nTo fix this, make sure you are calling `project.unloadSheet` with a sheet that was previously created via `project.sheet(...)`.")
8046
+ );
8047
+ console.warn(
8048
+ 'Sheet "'.concat(sanitizedPath, '"').concat(instanceHint, " is not currently loaded.")
8049
+ );
8050
+ }
8051
+ }
8052
+ unloadSheets() {
8053
+ privateAPI(this).unloadSheets();
8054
+ }
7893
8055
  };
7894
8056
 
7895
8057
  // core/src/coreExports.ts
@@ -7990,7 +8152,7 @@ var CoreBundle = class {
7990
8152
  return "Theatre_CoreBundle";
7991
8153
  }
7992
8154
  get version() {
7993
- return "0.1.9";
8155
+ return "0.1.10";
7994
8156
  }
7995
8157
  getBitsForStudio(studio, callback) {
7996
8158
  if (this._studio) {