@playcanvas/web-components 0.16.0 → 0.18.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.
Files changed (126) hide show
  1. package/README.md +5 -3
  2. package/dist/app.d.cts +18 -13
  3. package/dist/app.d.ts +18 -13
  4. package/dist/asset.d.cts +13 -3
  5. package/dist/asset.d.ts +13 -3
  6. package/dist/async-element.d.cts +13 -13
  7. package/dist/async-element.d.ts +13 -13
  8. package/dist/components/anim-clip.d.cts +4 -0
  9. package/dist/components/anim-clip.d.ts +4 -0
  10. package/dist/components/anim-component.d.cts +38 -5
  11. package/dist/components/anim-component.d.ts +38 -5
  12. package/dist/components/audio-listener-component.d.cts +26 -0
  13. package/dist/components/audio-listener-component.d.ts +26 -0
  14. package/dist/components/button-component.d.cts +10 -1
  15. package/dist/components/button-component.d.ts +10 -1
  16. package/dist/components/camera-component.d.cts +37 -1
  17. package/dist/components/camera-component.d.ts +37 -1
  18. package/dist/components/collision-component.d.cts +9 -1
  19. package/dist/components/collision-component.d.ts +9 -1
  20. package/dist/components/component.d.cts +3 -2
  21. package/dist/components/component.d.ts +3 -2
  22. package/dist/components/element-component.d.cts +11 -0
  23. package/dist/components/element-component.d.ts +11 -0
  24. package/dist/components/gsplat-component.d.cts +6 -0
  25. package/dist/components/gsplat-component.d.ts +6 -0
  26. package/dist/components/joint-component.d.cts +14 -2
  27. package/dist/components/joint-component.d.ts +14 -2
  28. package/dist/components/{layoutchild-component.d.cts → layout-child-component.d.cts} +7 -1
  29. package/dist/components/{layoutchild-component.d.ts → layout-child-component.d.ts} +7 -1
  30. package/dist/components/{layoutgroup-component.d.cts → layout-group-component.d.cts} +7 -1
  31. package/dist/components/{layoutgroup-component.d.ts → layout-group-component.d.ts} +7 -1
  32. package/dist/components/light-component.d.cts +52 -1
  33. package/dist/components/light-component.d.ts +52 -1
  34. package/dist/components/{particlesystem-component.d.cts → particle-system-component.d.cts} +7 -1
  35. package/dist/components/{particlesystem-component.d.ts → particle-system-component.d.ts} +7 -1
  36. package/dist/components/render-component.d.cts +7 -0
  37. package/dist/components/render-component.d.ts +7 -0
  38. package/dist/components/{rigidbody-component.d.cts → rigid-body-component.d.cts} +8 -1
  39. package/dist/components/{rigidbody-component.d.ts → rigid-body-component.d.ts} +8 -1
  40. package/dist/components/screen-component.d.cts +6 -0
  41. package/dist/components/screen-component.d.ts +6 -0
  42. package/dist/components/script-component.d.cts +26 -12
  43. package/dist/components/script-component.d.ts +26 -12
  44. package/dist/components/{script.d.cts → script-instance.d.cts} +11 -7
  45. package/dist/components/{script.d.ts → script-instance.d.ts} +11 -7
  46. package/dist/components/{scrollview-component.d.cts → scroll-view-component.d.cts} +7 -1
  47. package/dist/components/{scrollview-component.d.ts → scroll-view-component.d.ts} +7 -1
  48. package/dist/components/scrollbar-component.d.cts +6 -0
  49. package/dist/components/scrollbar-component.d.ts +6 -0
  50. package/dist/components/sound-component.d.cts +7 -1
  51. package/dist/components/sound-component.d.ts +7 -1
  52. package/dist/components/sound-slot.d.cts +5 -2
  53. package/dist/components/sound-slot.d.ts +5 -2
  54. package/dist/custom-elements.json +1852 -974
  55. package/dist/entity-base.d.cts +7 -6
  56. package/dist/entity-base.d.ts +7 -6
  57. package/dist/entity-owner.d.cts +118 -0
  58. package/dist/entity-owner.d.ts +118 -0
  59. package/dist/entity.d.cts +14 -102
  60. package/dist/entity.d.ts +14 -102
  61. package/dist/index.d.cts +26 -22
  62. package/dist/index.d.ts +26 -22
  63. package/dist/material.d.cts +18 -7
  64. package/dist/material.d.ts +18 -7
  65. package/dist/model.d.cts +72 -24
  66. package/dist/model.d.ts +72 -24
  67. package/dist/node.d.cts +6 -1
  68. package/dist/node.d.ts +6 -1
  69. package/dist/pwc.cjs +807 -293
  70. package/dist/pwc.cjs.map +1 -1
  71. package/dist/pwc.js +807 -293
  72. package/dist/pwc.js.map +1 -1
  73. package/dist/pwc.min.js +1 -1
  74. package/dist/pwc.min.js.map +1 -1
  75. package/dist/pwc.min.mjs +1 -1
  76. package/dist/pwc.min.mjs.map +1 -1
  77. package/dist/pwc.mjs +805 -292
  78. package/dist/pwc.mjs.map +1 -1
  79. package/dist/scene.d.cts +3 -0
  80. package/dist/scene.d.ts +3 -0
  81. package/dist/sky.d.cts +4 -0
  82. package/dist/sky.d.ts +4 -0
  83. package/dist/vscode.html-custom-data.json +351 -121
  84. package/dist/{module.d.cts → wasm.d.cts} +10 -6
  85. package/dist/{module.d.ts → wasm.d.ts} +10 -6
  86. package/dist/web-types.json +465 -291
  87. package/package.json +7 -7
  88. package/src/app.ts +49 -35
  89. package/src/asset.ts +13 -4
  90. package/src/async-element.ts +14 -14
  91. package/src/components/anim-clip.ts +4 -0
  92. package/src/components/anim-component.ts +89 -14
  93. package/src/components/audio-listener-component.ts +36 -0
  94. package/src/components/button-component.ts +10 -1
  95. package/src/components/camera-component.ts +65 -6
  96. package/src/components/collision-component.ts +9 -1
  97. package/src/components/component.ts +4 -3
  98. package/src/components/element-component.ts +11 -0
  99. package/src/components/gsplat-component.ts +6 -0
  100. package/src/components/joint-component.ts +14 -2
  101. package/src/components/{layoutchild-component.ts → layout-child-component.ts} +8 -2
  102. package/src/components/{layoutgroup-component.ts → layout-group-component.ts} +8 -2
  103. package/src/components/light-component.ts +110 -17
  104. package/src/components/{particlesystem-component.ts → particle-system-component.ts} +8 -2
  105. package/src/components/render-component.ts +7 -0
  106. package/src/components/{rigidbody-component.ts → rigid-body-component.ts} +9 -2
  107. package/src/components/screen-component.ts +6 -0
  108. package/src/components/script-component.ts +66 -42
  109. package/src/components/{script.ts → script-instance.ts} +18 -14
  110. package/src/components/{scrollview-component.ts → scroll-view-component.ts} +8 -2
  111. package/src/components/scrollbar-component.ts +6 -0
  112. package/src/components/sound-component.ts +8 -2
  113. package/src/components/sound-slot.ts +7 -4
  114. package/src/entity-base.ts +8 -7
  115. package/src/entity-owner.ts +298 -0
  116. package/src/entity.ts +21 -271
  117. package/src/index.ts +38 -25
  118. package/src/material.ts +19 -8
  119. package/src/model.ts +164 -72
  120. package/src/node.ts +15 -6
  121. package/src/scene.ts +3 -0
  122. package/src/sky.ts +4 -0
  123. package/src/{module.ts → wasm.ts} +12 -8
  124. package/dist/components/listener-component.d.cts +0 -20
  125. package/dist/components/listener-component.d.ts +0 -20
  126. package/src/components/listener-component.ts +0 -30
package/dist/pwc.js CHANGED
@@ -31,13 +31,13 @@
31
31
  return this.parentElement?.closest('pc-app') ?? null;
32
32
  }
33
33
  /**
34
- * The nearest ancestor element that fronts an entity — `<pc-entity>` or `<pc-node>` or
35
- * `null` if this element has no such ancestor. The search starts at the parent, so an element
36
- * never resolves to itself.
34
+ * The nearest ancestor element that fronts an entity — `<pc-entity>`, `<pc-model>` or
35
+ * `<pc-node>` — or `null` if this element has no such ancestor. The search starts at the
36
+ * parent, so an element never resolves to itself.
37
37
  * @returns The closest entity-fronting element, or `null`.
38
38
  */
39
39
  get closestEntity() {
40
- return this.parentElement?.closest('pc-entity, pc-node') ?? null;
40
+ return this.parentElement?.closest('pc-entity, pc-model, pc-node') ?? null;
41
41
  }
42
42
  /**
43
43
  * Called when the element is fully initialized and ready. Subclasses should call this when
@@ -111,9 +111,9 @@
111
111
  }
112
112
 
113
113
  /**
114
- * The ModuleElement interface provides properties and methods for manipulating
115
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-module/ | `<pc-module>`}
116
- * elements. The ModuleElement interface also inherits the properties and methods of the
114
+ * The WasmElement interface provides properties and methods for manipulating
115
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-wasm/ | `<pc-wasm>`}
116
+ * elements. The WasmElement interface also inherits the properties and methods of the
117
117
  * {@link AsyncElement} interface.
118
118
  *
119
119
  * The attributes are read once, when the module starts loading - on the element's first
@@ -122,9 +122,13 @@
122
122
  * modules configure engine-global state that never unloads, so readiness is not re-armed by
123
123
  * removing the element, and a re-inserted element does not load again.
124
124
  *
125
- * A `<pc-module>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
125
+ * A `<pc-wasm>` without a `name` warns and never becomes ready; a containing `<pc-app>` still
126
126
  * boots.
127
127
  *
128
+ * @elementSummary The `<pc-wasm>` element loads a WebAssembly module the engine needs before the
129
+ * application starts — `Ammo` for physics, `Basis` or `DracoDecoderModule` for compressed assets.
130
+ * Must be a direct child of `<pc-app>`.
131
+ *
128
132
  * @attribute {string} name - The name of the WebAssembly module to configure, e.g. `Basis` or
129
133
  * `Ammo`.
130
134
  * @attribute {string} glue - The URL of the module's glue script.
@@ -132,7 +136,7 @@
132
136
  * @attribute {string} fallback - The URL of the module's asm.js fallback script, used when
133
137
  * WebAssembly is unavailable.
134
138
  */
135
- class ModuleElement extends AsyncElement {
139
+ class WasmElement extends AsyncElement {
136
140
  _loadPromise = null;
137
141
  connectedCallback() {
138
142
  this._getLoadPromise();
@@ -140,7 +144,7 @@
140
144
  async _loadModule() {
141
145
  const name = this.getAttribute('name');
142
146
  if (!name) {
143
- console.warn("pc-module requires a 'name' attribute - no module was configured");
147
+ console.warn("pc-wasm requires a 'name' attribute - no module was configured");
144
148
  return;
145
149
  }
146
150
  const config = {
@@ -175,7 +179,7 @@
175
179
  return this._loadPromise;
176
180
  }
177
181
  }
178
- customElements.define('pc-module', ModuleElement);
182
+ customElements.define('pc-wasm', WasmElement);
179
183
 
180
184
  /** Covers the 0.2s opacity transition; jsdom never fires transitionend, so removal is timed. */
181
185
  const REMOVAL_DELAY_MS = 250;
@@ -732,6 +736,12 @@
732
736
 
733
737
  /** The pointer event types the application synthesizes on `<pc-entity>` elements via picking. */
734
738
  const pointerEventTypes = ['pointermove', 'pointerdown', 'pointerup', 'pointerenter', 'pointerleave'];
739
+ /**
740
+ * The event types whose listeners make an element a hover target. Hover resolution walks past
741
+ * elements listening for none of them, so a silent element never swallows an ancestor's
742
+ * enter/leave pair.
743
+ */
744
+ const hoverEventTypes = ['pointerenter', 'pointerleave', 'pointermove'];
735
745
  /**
736
746
  * Gives `pc-app` the sizing contract of a replaced element (`<video>`, `<img>`): a block-level
737
747
  * box that the page's CSS sizes, defaulting to the canvas's own 300x150 intrinsic size, with the
@@ -760,6 +770,10 @@
760
770
  * tracked live via a ResizeObserver — so the element can be embedded at any size, resized by
761
771
  * its container, or made fullscreen with ordinary CSS such as `width: 100vw; height: 100dvh`.
762
772
  *
773
+ * @elementSummary The `<pc-app>` element creates a PlayCanvas application and the canvas it renders
774
+ * into, and is the root of every scene. It holds the `<pc-asset>`, `<pc-material>`, `<pc-wasm>` and
775
+ * `<pc-scene>` elements, and the page's CSS sizes it, as it would a `<video>`.
776
+ *
763
777
  * @fires {ProgressEvent} progress - Fired while the application preloads its assets. `loaded` and
764
778
  * `total` are asset counts, not bytes, and an asset that fails to load still counts as loaded.
765
779
  * Fired at least once per boot, and the final event always has `loaded` equal to `total`. Does
@@ -806,9 +820,9 @@
806
820
  _bootGeneration = 0;
807
821
  /**
808
822
  * The elements backing this application's entities, keyed by the entity itself. Registered
809
- * by EntityElement at creation (and NodeElement at binding) and removed when an entity is
810
- * destroyed or unbound, this joins engine scene nodes back to their owning elements by
811
- * identity - never by name.
823
+ * by entity-owning elements at creation (pc-entity, and pc-model for its host) and by
824
+ * NodeElement at binding, and removed when an entity is destroyed or unbound, this joins
825
+ * engine scene nodes back to their owning elements by identity - never by name.
812
826
  */
813
827
  _entityElements = new Map();
814
828
  _picker = null;
@@ -887,7 +901,7 @@
887
901
  // there, leaving the element permanently unready: no canvas, no entities, no application.
888
902
  //
889
903
  // Upgrading is the fix here rather than skipping whatever has not upgraded, because a
890
- // <pc-module> is the one child that nothing else ever builds on its own behalf - skipping
904
+ // <pc-wasm> is the one child that nothing else ever builds on its own behalf - skipping
891
905
  // it would drop the wasm module the app asked for, silently and only for cloned apps.
892
906
  // Upgrading runs each descendant's connectedCallback synchronously, a few lines earlier
893
907
  // than the parser's path runs them but into the same state they see there: no application
@@ -896,10 +910,10 @@
896
910
  // as any other disconnect is. An already-upgraded subtree - every other insertion path -
897
911
  // is left completely untouched.
898
912
  customElements.upgrade(this);
899
- // Get all pc-module elements that are direct children of the pc-app element
900
- const moduleElements = this.querySelectorAll(':scope > pc-module');
913
+ // Get all pc-wasm elements that are direct children of the pc-app element
914
+ const wasmElements = this.querySelectorAll(':scope > pc-wasm');
901
915
  // Wait for all modules to load
902
- await Promise.all(Array.from(moduleElements).map((module) => module._getLoadPromise()));
916
+ await Promise.all(Array.from(wasmElements).map((element) => element._getLoadPromise()));
903
917
  // The element may have been removed while the modules loaded. Nothing beyond the loading
904
918
  // bar exists yet, and disconnectedCallback has already destroyed that.
905
919
  if (generation !== this._bootGeneration) {
@@ -1069,14 +1083,15 @@
1069
1083
  Array.from(materialElements).forEach((materialElement) => {
1070
1084
  materialElement._createMaterial();
1071
1085
  });
1072
- // Create all entities
1073
- const entityElements = this.querySelectorAll('pc-entity');
1074
- Array.from(entityElements).forEach((entityElement) => {
1075
- entityElement._createEntity(app);
1086
+ // Create all entities. pc-model joins the sweep because it owns a host entity of its
1087
+ // own; its instantiated content arrives later, beneath that host.
1088
+ const ownerElements = this.querySelectorAll('pc-entity, pc-model');
1089
+ Array.from(ownerElements).forEach((ownerElement) => {
1090
+ ownerElement._createEntity(app);
1076
1091
  });
1077
1092
  // Build hierarchy
1078
- entityElements.forEach((entityElement) => {
1079
- entityElement._buildHierarchy(app);
1093
+ ownerElements.forEach((ownerElement) => {
1094
+ ownerElement._buildHierarchy(app);
1080
1095
  });
1081
1096
  // Building the hierarchy dispatched each entity's ready event synchronously, and a
1082
1097
  // listener may have removed the element. The sweep itself degrades safely - destroying
@@ -1187,7 +1202,7 @@
1187
1202
  // created from onpointer* attributes when their elements were first upgraded, or
1188
1203
  // listeners carried over from before a re-boot)
1189
1204
  pointerEventTypes.forEach((type) => {
1190
- const anyListeners = Array.from(this.querySelectorAll('pc-entity, pc-node')).some((entity) => entity._hasListeners(type));
1205
+ const anyListeners = Array.from(this.querySelectorAll('pc-entity, pc-model, pc-node')).some((entity) => entity._hasListeners(type));
1191
1206
  if (anyListeners) {
1192
1207
  this._onPointerListenerAdded(type);
1193
1208
  }
@@ -1237,10 +1252,10 @@
1237
1252
  this._entityElements.delete(entity);
1238
1253
  }
1239
1254
  /**
1240
- * Returns the `<pc-entity>` or `<pc-node>` element whose backing entity is `entity`, or
1241
- * `null` if the entity is not fronted by an element of this application - for example, an
1242
- * unbound node inside a model's instantiated hierarchy, or an entity created through the
1243
- * engine API.
1255
+ * Returns the `<pc-entity>`, `<pc-model>` or `<pc-node>` element whose backing entity is
1256
+ * `entity`, or `null` if the entity is not fronted by an element of this application - for
1257
+ * example, an unbound node inside a model's instantiated hierarchy, or an entity created
1258
+ * through the engine API.
1244
1259
  *
1245
1260
  * @param entity - The entity to look up.
1246
1261
  * @returns The element fronting the entity, or `null`.
@@ -1249,18 +1264,19 @@
1249
1264
  return this._entityElements.get(entity) ?? null;
1250
1265
  }
1251
1266
  /**
1252
- * Resolves the element that owns a picked node: the nearest node up the parent chain -
1253
- * starting with the node itself - that is fronted by a `<pc-entity>` or `<pc-node>` of this
1254
- * application. A hit inside a model's instantiated hierarchy therefore resolves to the
1255
- * nearest bound `<pc-node>`, or failing that the element hosting the model.
1267
+ * Resolves the element that owns hover for a picked node: the nearest node up the parent
1268
+ * chain - starting with the node itself - whose element listens for any of the hover event
1269
+ * types. Skipping silent elements matches {@link _elementWithListener}, so a registered
1270
+ * element with no hover listeners (a `<pc-model>` host, a plain child entity) is transparent
1271
+ * to hover rather than swallowing a listening ancestor's enter/leave pair.
1256
1272
  *
1257
1273
  * @param node - The picked node, or `null`.
1258
- * @returns The owning element, or `null`.
1274
+ * @returns The hover-owning element, or `null`.
1259
1275
  */
1260
- _elementFromNode(node) {
1276
+ _hoverTarget(node) {
1261
1277
  while (node !== null) {
1262
1278
  const element = this._entityElements.get(node);
1263
- if (element) {
1279
+ if (element && hoverEventTypes.some((type) => element._hasListeners(type))) {
1264
1280
  return element;
1265
1281
  }
1266
1282
  node = node.parent;
@@ -1396,9 +1412,10 @@
1396
1412
  const node = await this._pickNode(event);
1397
1413
  if (token !== this._pickToken || !this._picker)
1398
1414
  return;
1399
- // The hovered element is the nearest one up the node's parent chain, listening or not -
1400
- // dispatch is gated per event type below
1401
- const newHoverEntity = this._elementFromNode(node);
1415
+ // The hovered element is the nearest one up the node's parent chain with a hover
1416
+ // listener - the nearest-listener rule down/up use. Dispatch is still gated per event
1417
+ // type below: having any hover listener selects the target, each event needs its own.
1418
+ const newHoverEntity = this._hoverTarget(node);
1402
1419
  // Handle enter/leave events
1403
1420
  if (this._hoveredEntity !== newHoverEntity) {
1404
1421
  if (this._hoveredEntity && this._hoveredEntity._hasListeners('pointerleave')) {
@@ -1450,7 +1467,7 @@
1450
1467
  }
1451
1468
  }
1452
1469
  _onPointerListenerRemoved(type) {
1453
- const hasListeners = Array.from(this.querySelectorAll('pc-entity, pc-node')).some((entity) => entity._hasListeners(type));
1470
+ const hasListeners = Array.from(this.querySelectorAll('pc-entity, pc-model, pc-node')).some((entity) => entity._hasListeners(type));
1454
1471
  if (!hasListeners && this._canvas) {
1455
1472
  this._hasPointerListeners[type] = false;
1456
1473
  const handler = type === 'pointerenter' || type === 'pointerleave'
@@ -1641,11 +1658,12 @@
1641
1658
  'onpointermove'
1642
1659
  ];
1643
1660
  /**
1644
- * The base class for elements that front an engine {@link Entity}: `<pc-entity>`, which creates
1645
- * one, and `<pc-node>`, which binds to one inside a model's instantiated hierarchy. It carries
1646
- * what both need — the `entity` contract, registration with the owning application (which joins
1647
- * picked scene nodes back to elements by identity, never by name), and the pointer listener
1648
- * bookkeeping that lets the application lazily attach its canvas handlers.
1661
+ * The base class for elements that front an engine {@link Entity}: `<pc-entity>` and
1662
+ * `<pc-model>`, which create one, and `<pc-node>`, which binds to one inside a model's
1663
+ * instantiated hierarchy. It carries what all of them need — the `entity` contract, registration
1664
+ * with the owning application (which joins picked scene nodes back to elements by identity,
1665
+ * never by name), and the pointer listener bookkeeping that lets the application lazily attach
1666
+ * its canvas handlers.
1649
1667
  */
1650
1668
  class EntityBaseElement extends AsyncElement {
1651
1669
  _entity = null;
@@ -1694,7 +1712,7 @@
1694
1712
  * Tracks whether an inline `onpointer*` attribute is present. The browser itself compiles and
1695
1713
  * runs these attributes — they are standard `GlobalEventHandlers`, so setting one replaces
1696
1714
  * the previous handler and removing it removes the handler, exactly like `onclick` on any
1697
- * HTML element. But because they bypass {@link addEventListener}, the connect/disconnect
1715
+ * HTML element. But because they bypass {@link EventTarget.addEventListener}, the connect/disconnect
1698
1716
  * bookkeeping that lets the application lazily attach its canvas pointer handlers must be
1699
1717
  * kept in sync here.
1700
1718
  *
@@ -1735,7 +1753,7 @@
1735
1753
  }
1736
1754
  /**
1737
1755
  * Whether the element has a listener for an event type, registered either with
1738
- * {@link addEventListener} or with the matching inline `onpointer*` attribute. Read by the
1756
+ * {@link EventTarget.addEventListener} or with the matching inline `onpointer*` attribute. Read by the
1739
1757
  * containing `<pc-app>` element to gate pointer event synthesis.
1740
1758
  *
1741
1759
  * @param type - The event type.
@@ -1748,17 +1766,17 @@
1748
1766
  }
1749
1767
 
1750
1768
  /**
1751
- * Creates and parents the entities of every descendant `<pc-entity>` of `root`, in two passes so
1752
- * that no parent's existence depends on document order. Called wherever a subtree could not build
1753
- * itself: an element inserted into an application that is already running, and a `<pc-node>` whose
1754
- * children waited for it to bind.
1769
+ * Creates and parents the entities of every descendant entity-owning element of `root`, in two
1770
+ * passes so that no parent's existence depends on document order. Called wherever a subtree could
1771
+ * not build itself: an element inserted into an application that is already running, and a
1772
+ * `<pc-node>` whose children waited for it to bind.
1755
1773
  *
1756
1774
  * Descendants that are not yet custom elements are skipped, because there is nothing useful to do
1757
1775
  * for them and reaching for `_createEntity` would throw. A subtree cloned from a `<template>`
1758
1776
  * arrives entirely unupgraded — template content lives in an inert document, where custom element
1759
1777
  * definitions are never looked up — and appending the clone upgrades its elements in tree order,
1760
1778
  * an element before its descendants. So a sweep from an element's own `connectedCallback` sees
1761
- * plain `HTMLElement`s below it. Each becomes an `EntityElement` moments later and its own
1779
+ * plain `HTMLElement`s below it. Each becomes an entity-owning element moments later and its own
1762
1780
  * `connectedCallback` creates and parents it, by which time the ancestor it parents under has its
1763
1781
  * entity — the same guarantee tree order gives this sweep.
1764
1782
  *
@@ -1767,35 +1785,18 @@
1767
1785
  * @internal
1768
1786
  */
1769
1787
  const buildDescendantEntities = (root, app) => {
1770
- const children = Array.from(root.querySelectorAll('pc-entity')).filter((child) => child instanceof EntityElement);
1788
+ const children = Array.from(root.querySelectorAll('pc-entity, pc-model')).filter((child) => child instanceof EntityOwnerElement);
1771
1789
  children.forEach((child) => child._createEntity(app));
1772
1790
  children.forEach((child) => child._buildHierarchy(app));
1773
1791
  };
1774
1792
  /**
1775
- * The EntityElement interface provides properties and methods for manipulating
1776
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-entity/ | `<pc-entity>`} elements.
1777
- * The EntityElement interface also inherits the properties and methods of the
1778
- * {@link HTMLElement} interface.
1779
- *
1780
- * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
1781
- * intersects this entity's geometry. They are only generated while the entity has a listener for
1782
- * them, registered either with {@link addEventListener} or with the matching inline `onpointer*`
1783
- * attribute.
1784
- *
1785
- * @attribute {string} onpointerenter - Script to run when the pointer moves onto the entity.
1786
- * @attribute {string} onpointerleave - Script to run when the pointer moves off the entity.
1787
- * @attribute {string} onpointermove - Script to run when the pointer moves over the entity.
1788
- * @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
1789
- * entity.
1790
- * @attribute {string} onpointerup - Script to run when a pointer button is released over the
1791
- * entity.
1792
- * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the entity.
1793
- * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the entity.
1794
- * @fires {PointerEvent} pointermove - Fired when the pointer moves over the entity.
1795
- * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
1796
- * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
1793
+ * The base class for elements that create and own their backing entity: `<pc-entity>` and
1794
+ * `<pc-model>`, whose host entity carries the same authored properties. It carries the cached
1795
+ * property state, entity creation and parenting, and the reset that follows the entity's
1796
+ * destruction. `<pc-node>` sits outside this class: it borrows an entity a model instantiated,
1797
+ * and its properties are nullable overrides rather than owned values.
1797
1798
  */
1798
- class EntityElement extends EntityBaseElement {
1799
+ class EntityOwnerElement extends EntityBaseElement {
1799
1800
  /**
1800
1801
  * Whether the entity is enabled.
1801
1802
  */
@@ -1821,7 +1822,8 @@
1821
1822
  */
1822
1823
  _tags = [];
1823
1824
  /**
1824
- * Whether the hierarchy has been built for this entity.
1825
+ * Whether the hierarchy has been built for this entity — set once `_buildHierarchy` has
1826
+ * parented it. Read by subclasses that gate work on the entity being in the scene graph.
1825
1827
  */
1826
1828
  _built = false;
1827
1829
  /**
@@ -1874,10 +1876,10 @@
1874
1876
  this._resetReady();
1875
1877
  }
1876
1878
  /**
1877
- * Parents the backing entity: under the entity of the nearest ancestor `<pc-entity>` or
1878
- * `<pc-node>` when there is one, and under the application root otherwise. Called by the
1879
- * containing `<pc-app>` element once a sweep has created every entity, so a parent's
1880
- * existence never depends on document order.
1879
+ * Parents the backing entity: under the entity of the nearest ancestor `<pc-entity>`,
1880
+ * `<pc-model>` or `<pc-node>` when there is one, and under the application root otherwise.
1881
+ * Called by the containing `<pc-app>` element once a sweep has created every entity, so a
1882
+ * parent's existence never depends on document order.
1881
1883
  *
1882
1884
  * @param app - The application whose root adopts parentless entities.
1883
1885
  * @internal
@@ -1892,6 +1894,13 @@
1892
1894
  if (closestEntity && !closestEntity.entity) {
1893
1895
  return;
1894
1896
  }
1897
+ // An owner that exists but is not yet parented is itself deferred behind an unresolved
1898
+ // node further up. Building beneath it would announce readiness for an entity that is
1899
+ // not in the scene graph; stay unbuilt with it - the node's bind sweeps the whole
1900
+ // subtree, ancestors before descendants.
1901
+ if (closestEntity instanceof EntityOwnerElement && !closestEntity._built) {
1902
+ return;
1903
+ }
1895
1904
  this._built = true;
1896
1905
  if (closestEntity?.entity) {
1897
1906
  closestEntity.entity.addChild(this.entity);
@@ -1899,35 +1908,16 @@
1899
1908
  else {
1900
1909
  app.root.addChild(this.entity);
1901
1910
  }
1902
- this._onReady();
1911
+ this._onBuilt();
1903
1912
  }
1904
- connectedCallback() {
1905
- // Wait for app to be ready
1906
- const closestApp = this.closestApp;
1907
- if (!closestApp) {
1908
- // An entity outside an application is inert and never becomes ready, so awaiting it
1909
- // hangs. Warn rather than fail silently, naming the parent it requires, as every other
1910
- // misplaced element does.
1911
- const name = this.getAttribute('name');
1912
- const label = name ? ` '${name}'` : '';
1913
- console.warn(`pc-entity${label} must be a descendant of pc-app - entity not created`);
1914
- return;
1915
- }
1916
- // If app is already running, create entity immediately
1917
- if (closestApp._hierarchyReady) {
1918
- const app = closestApp.app;
1919
- this._createEntity(app);
1920
- this._buildHierarchy(app);
1921
- // Handle any child entities that might exist
1922
- buildDescendantEntities(this, app);
1923
- }
1924
- }
1925
- disconnectedCallback() {
1926
- // Destroying the entity destroys its whole subtree, and the engine fires 'destroy' for
1927
- // every entity in it - so _onEntityDestroy resets this element AND every descendant
1928
- // element before the descendants' own disconnectedCallbacks run. Their entities are null
1929
- // by then, making this call a no-op for them.
1930
- this._entity?.destroy();
1913
+ /**
1914
+ * Called by `_buildHierarchy` once the backing entity has been parented — exactly once
1915
+ * per build cycle. The default announces readiness, which is what a parented `<pc-entity>`
1916
+ * means; `<pc-model>` overrides it to start loading content instead, because its readiness
1917
+ * tracks the content settling rather than the host entering the scene graph.
1918
+ */
1919
+ _onBuilt() {
1920
+ this._onReady();
1931
1921
  }
1932
1922
  /**
1933
1923
  * Sets the enabled state of the entity.
@@ -2032,6 +2022,75 @@
2032
2022
  get tags() {
2033
2023
  return this._tags;
2034
2024
  }
2025
+ }
2026
+
2027
+ /**
2028
+ * The EntityElement interface provides properties and methods for manipulating
2029
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-entity/ | `<pc-entity>`} elements.
2030
+ * The EntityElement interface also inherits the properties and methods of the
2031
+ * {@link HTMLElement} interface.
2032
+ *
2033
+ * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
2034
+ * intersects this entity's geometry. They are only generated while the entity has a listener for
2035
+ * them, registered either with {@link EventTarget.addEventListener} or with the matching inline `onpointer*`
2036
+ * attribute.
2037
+ *
2038
+ * @elementSummary The `<pc-entity>` element creates an entity: a named, transformable node of the
2039
+ * scene hierarchy, and the host for component elements such as `<pc-camera>`, `<pc-light>` and
2040
+ * `<pc-render>`. Place it in the `<pc-scene>`, or nest it under another `<pc-entity>`, a
2041
+ * `<pc-model>` or a `<pc-node>` to parent it there.
2042
+ *
2043
+ * @attribute {boolean} enabled - The enabled state of the entity.
2044
+ * @attribute {string} name - The name of the entity.
2045
+ * @attribute {string} position - The position of the entity.
2046
+ * @attribute {string} rotation - The rotation of the entity.
2047
+ * @attribute {string} scale - The scale of the entity.
2048
+ * @attribute {string} tags - The tags of the entity.
2049
+ * @attribute {string} onpointerenter - Script to run when the pointer moves onto the entity.
2050
+ * @attribute {string} onpointerleave - Script to run when the pointer moves off the entity.
2051
+ * @attribute {string} onpointermove - Script to run when the pointer moves over the entity.
2052
+ * @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
2053
+ * entity.
2054
+ * @attribute {string} onpointerup - Script to run when a pointer button is released over the
2055
+ * entity.
2056
+ * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the entity.
2057
+ * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the entity.
2058
+ * @fires {PointerEvent} pointermove - Fired when the pointer moves over the entity.
2059
+ * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the entity.
2060
+ * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the entity.
2061
+ */
2062
+ class EntityElement extends EntityOwnerElement {
2063
+ connectedCallback() {
2064
+ // Wait for app to be ready
2065
+ const closestApp = this.closestApp;
2066
+ if (!closestApp) {
2067
+ // An entity outside an application is inert and never becomes ready, so awaiting it
2068
+ // hangs. Warn rather than fail silently, naming the parent it requires, as every other
2069
+ // misplaced element does.
2070
+ const name = this.getAttribute('name');
2071
+ const label = name ? ` '${name}'` : '';
2072
+ console.warn(`pc-entity${label} must be a descendant of pc-app - entity not created`);
2073
+ return;
2074
+ }
2075
+ // If app is already running, create entity immediately
2076
+ if (closestApp._hierarchyReady) {
2077
+ const app = closestApp.app;
2078
+ this._createEntity(app);
2079
+ this._buildHierarchy(app);
2080
+ // Handle any child entities that might exist. A build that deferred (an unresolved
2081
+ // pc-node above) defers the whole subtree with it - the node's bind sweeps it.
2082
+ if (this._built) {
2083
+ buildDescendantEntities(this, app);
2084
+ }
2085
+ }
2086
+ }
2087
+ disconnectedCallback() {
2088
+ // Destroying the entity destroys its whole subtree, and the engine fires 'destroy' for
2089
+ // every entity in it - so _onEntityDestroy resets this element AND every descendant
2090
+ // element before the descendants' own disconnectedCallbacks run. Their entities are null
2091
+ // by then, making this call a no-op for them.
2092
+ this._entity?.destroy();
2093
+ }
2035
2094
  static get observedAttributes() {
2036
2095
  return ['enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
2037
2096
  }
@@ -2361,6 +2420,10 @@
2361
2420
  * Apart from `lazy` and the texture options, these attributes are read once when the asset is
2362
2421
  * created, so changing them later has no effect.
2363
2422
  *
2423
+ * @elementSummary The `<pc-asset>` element declares an asset for the application to load — a model,
2424
+ * texture, font, sound, script or JSON file — under an `id` that other elements reference. Must be
2425
+ * a direct child of `<pc-app>`.
2426
+ *
2364
2427
  * @attribute {string} id - The identifier used to reference the asset from other elements.
2365
2428
  * @attribute {string} src - The URL of the asset to load.
2366
2429
  * @attribute {string} type - The asset type. Inferred from the `src` file extension when omitted.
@@ -2969,14 +3032,47 @@
2969
3032
  * The ModelElement interface also inherits the properties and methods of the
2970
3033
  * {@link HTMLElement} interface.
2971
3034
  *
2972
- * The element becomes ready once its container asset has loaded and the instantiated hierarchy has
2973
- * been added to the scene `entity` is non-null by then. A failed load also settles readiness,
2974
- * with `entity` remaining `null`: readiness means the load settled, not that it succeeded — listen
2975
- * for `error`, or check `entity`, to tell the outcomes apart. Changing `asset` re-arms readiness
2976
- * and instantiates anew, so a `ready()` obtained after the change resolves against the new
2977
- * hierarchy. A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never
2978
- * becomes ready.
3035
+ * The element creates and fronts a stable host entity: `entity` is that host, created when the
3036
+ * application builds its hierarchy and kept across `asset` changes, so the element's transform
3037
+ * and tags are instance placement that composes with whatever transform the asset authored on
3038
+ * its root. The instantiated content is parented beneath the host and exposed as
3039
+ * {@link contentEntity}.
3040
+ *
3041
+ * The element becomes ready once its current asset selection has settled: the container asset
3042
+ * has loaded and its content root has been parented beneath the host, the load has failed
3043
+ * (`contentEntity` stays `null` — listen for `error`, or check `contentEntity`, to tell the
3044
+ * outcomes apart), or no asset is assigned at all. Changing `asset` re-arms readiness and
3045
+ * instantiates anew, so a `ready()` obtained after the change resolves against the new content.
3046
+ * A `pc-model` outside a `pc-app`, or referencing an unknown asset id, warns and never becomes
3047
+ * ready.
3048
+ *
3049
+ * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
3050
+ * intersects the model's geometry, exactly as for `<pc-entity>` — a hit on a content node that no
3051
+ * `pc-node` fronts resolves to this element.
3052
+ *
3053
+ * @elementSummary The `<pc-model>` element instantiates a 3D model from a container asset
3054
+ * (typically a GLB) beneath an entity of its own, so the element's transform and tags place the
3055
+ * instance in the scene. Its `<pc-node>` children override what the asset authored. Place it in the
3056
+ * `<pc-scene>`, or nest it under a `<pc-entity>`, another `<pc-model>` or a `<pc-node>`.
2979
3057
  *
3058
+ * @attribute {boolean} enabled - The enabled state of the model.
3059
+ * @attribute {string} name - The name of the model.
3060
+ * @attribute {string} position - The position of the model.
3061
+ * @attribute {string} rotation - The rotation of the model.
3062
+ * @attribute {string} scale - The scale of the model.
3063
+ * @attribute {string} tags - The tags of the model.
3064
+ * @attribute {string} onpointerenter - Script to run when the pointer moves onto the model.
3065
+ * @attribute {string} onpointerleave - Script to run when the pointer moves off the model.
3066
+ * @attribute {string} onpointermove - Script to run when the pointer moves over the model.
3067
+ * @attribute {string} onpointerdown - Script to run when a pointer button is pressed over the
3068
+ * model.
3069
+ * @attribute {string} onpointerup - Script to run when a pointer button is released over the
3070
+ * model.
3071
+ * @fires {PointerEvent} pointerenter - Fired when the pointer moves onto the model.
3072
+ * @fires {PointerEvent} pointerleave - Fired when the pointer moves off the model.
3073
+ * @fires {PointerEvent} pointermove - Fired when the pointer moves over the model.
3074
+ * @fires {PointerEvent} pointerdown - Fired when a pointer button is pressed over the model.
3075
+ * @fires {PointerEvent} pointerup - Fired when a pointer button is released over the model.
2980
3076
  * @fires {Event} load - Fired each time a container asset finishes instantiating, including
2981
3077
  * re-instantiation after `asset` changes. Does not bubble — listen on this element, or use a
2982
3078
  * capture-phase listener on an ancestor.
@@ -2984,14 +3080,14 @@
2984
3080
  * error in `message`. Does not bubble. The element still becomes ready — readiness means the load
2985
3081
  * settled, not that it succeeded.
2986
3082
  */
2987
- class ModelElement extends AsyncElement {
3083
+ class ModelElement extends EntityOwnerElement {
2988
3084
  _asset = '';
2989
- _entity = null;
3085
+ _contentEntity = null;
2990
3086
  /**
2991
- * Incremented on every new load and on disconnect, and captured by a load when it starts. A
2992
- * load that resumes from an await or a load callback abandons itself if the value has moved
2993
- * on, so a superseded load can neither instantiate a second entity nor parent one that has
2994
- * since been destroyed.
3087
+ * Incremented on every new load, on disconnect, and when the host entity dies, and captured
3088
+ * by a load when it starts. A load that resumes from an await or a load callback abandons
3089
+ * itself if the value has moved on, so a superseded load can neither instantiate a second
3090
+ * content root nor parent one under a host a newer cycle has already replaced.
2995
3091
  */
2996
3092
  _loadGeneration = 0;
2997
3093
  /**
@@ -3002,12 +3098,13 @@
3002
3098
  _loadHandle = null;
3003
3099
  _errorHandle = null;
3004
3100
  /**
3005
- * The root entity of the instantiated model. `null` until the container asset has loaded
3006
- * and been instantiated, and again once the element has been removed from the document.
3007
- * @returns The model's root entity, or `null`.
3101
+ * The root entity of the instantiated model content, parented beneath the host entity.
3102
+ * `null` until the container asset has loaded and been instantiated, after a failed load,
3103
+ * and again once the element has been removed from the document.
3104
+ * @returns The content root entity, or `null`.
3008
3105
  */
3009
- get entity() {
3010
- return this._entity;
3106
+ get contentEntity() {
3107
+ return this._contentEntity;
3011
3108
  }
3012
3109
  /**
3013
3110
  * Returns a snapshot of the instantiated node tree, or `null` while there is none (the
@@ -3019,12 +3116,13 @@
3019
3116
  * is the printable form.
3020
3117
  *
3021
3118
  * The snapshot is plain data, computed afresh each call: it does not follow later changes
3022
- * to the hierarchy, and mutating it changes nothing.
3119
+ * to the hierarchy, and mutating it changes nothing. It covers the instantiated content
3120
+ * only — the host entity the element fronts is not part of the asset's node tree.
3023
3121
  *
3024
3122
  * @returns The root of the instantiated node tree, or `null`.
3025
3123
  */
3026
3124
  hierarchy() {
3027
- const root = this._entity;
3125
+ const root = this._contentEntity;
3028
3126
  if (!root) {
3029
3127
  return null;
3030
3128
  }
@@ -3064,18 +3162,53 @@
3064
3162
  // A model outside an application is inert and never becomes ready, so awaiting it hangs.
3065
3163
  // Warn rather than fail silently, naming the parent it requires, as every other misplaced
3066
3164
  // element does.
3067
- if (!this.closestApp) {
3165
+ const closestApp = this.closestApp;
3166
+ if (!closestApp) {
3068
3167
  const label = this._asset ? ` '${this._asset}'` : '';
3069
3168
  console.warn(`pc-model${label} must be a descendant of pc-app - model not created`);
3070
3169
  return;
3071
3170
  }
3072
- this._loadModel();
3171
+ // If the app is already running, create the host immediately; during a boot, the app's
3172
+ // own sweep does it. Either way, _onBuilt starts the content load once the host is
3173
+ // parented.
3174
+ if (closestApp._hierarchyReady) {
3175
+ const app = closestApp.app;
3176
+ this._createEntity(app);
3177
+ this._buildHierarchy(app);
3178
+ // A build that deferred (an unresolved pc-node above) defers the whole subtree with
3179
+ // it - the node's bind sweeps it.
3180
+ if (this._built) {
3181
+ buildDescendantEntities(this, app);
3182
+ }
3183
+ }
3073
3184
  }
3074
3185
  disconnectedCallback() {
3186
+ // Destroying the host destroys the instantiated content with it, and the destroy hook
3187
+ // resets the element. The generation guard comes first so a load suspended on an await
3188
+ // cannot resume against the torn-down element.
3075
3189
  this._loadGeneration++;
3076
3190
  this._detachLoadHandlers();
3077
- this._unloadModel();
3078
- this._resetReady();
3191
+ this._entity?.destroy();
3192
+ }
3193
+ /**
3194
+ * Starts (or restarts) the content load once the host has been parented. Readiness is not
3195
+ * announced here — it tracks the content settling, not the host entering the scene graph.
3196
+ */
3197
+ _onBuilt() {
3198
+ this._loadContent();
3199
+ }
3200
+ /**
3201
+ * Extends the owner reset for the content: the engine's destroy cascade has already taken
3202
+ * the content root down with the host subtree, so only the reference and the in-flight load
3203
+ * are dropped here. The next build re-creates the host and re-instantiates the content.
3204
+ *
3205
+ * @param entity - The host entity that was destroyed.
3206
+ */
3207
+ _onEntityDestroy(entity) {
3208
+ this._loadGeneration++;
3209
+ this._detachLoadHandlers();
3210
+ this._contentEntity = null;
3211
+ super._onEntityDestroy(entity);
3079
3212
  }
3080
3213
  _detachLoadHandlers() {
3081
3214
  this._loadHandle?.off();
@@ -3084,51 +3217,34 @@
3084
3217
  this._errorHandle = null;
3085
3218
  }
3086
3219
  /**
3087
- * Resolves readiness and dispatches the `load` event. Called once the instantiated hierarchy
3088
- * has been parented — readiness means "in the scene graph", matching `pc-entity`, so a ready
3089
- * model's entity always has world transforms.
3220
+ * Resolves readiness and dispatches the `load` event. Called once the instantiated content
3221
+ * has been parented beneath the host the host itself is already in the scene graph by
3222
+ * then, so a ready model's content always has world transforms.
3090
3223
  */
3091
3224
  _announceLoad() {
3092
3225
  this._onReady();
3093
3226
  this.dispatchEvent(new Event('load'));
3094
3227
  }
3095
3228
  _instantiate(container) {
3096
- const generation = this._loadGeneration;
3097
- const entity = container.instantiateRenderEntity();
3098
- this._entity = entity;
3099
- // The parent's readiness re-arms when it is torn down, so these can resume in a later
3100
- // connection cycle. The entity is captured above and the generation re-checked, so a
3101
- // stale resume cannot parent an entity a newer cycle has already destroyed.
3102
- const parentEntityElement = this.closestEntity;
3103
- if (parentEntityElement) {
3104
- parentEntityElement.ready().then(() => {
3105
- if (generation !== this._loadGeneration) {
3106
- return;
3107
- }
3108
- parentEntityElement.entity.addChild(entity);
3109
- this._announceLoad();
3110
- });
3111
- }
3112
- else {
3113
- const appElement = this.closestApp;
3114
- if (appElement) {
3115
- appElement.ready().then(() => {
3116
- if (generation !== this._loadGeneration) {
3117
- return;
3118
- }
3119
- appElement.app.root.addChild(entity);
3120
- this._announceLoad();
3121
- });
3122
- }
3123
- }
3124
- }
3125
- async _loadModel() {
3126
- this._unloadModel();
3229
+ const content = container.instantiateRenderEntity();
3230
+ this._contentEntity = content;
3231
+ this._entity.addChild(content);
3232
+ this._announceLoad();
3233
+ }
3234
+ _destroyContent() {
3235
+ this._contentEntity?.destroy();
3236
+ this._contentEntity = null;
3237
+ }
3238
+ async _loadContent() {
3239
+ // The old content goes down synchronously, so a reader that checks after an asset change
3240
+ // never sees the outgoing hierarchy. The host survives - components and child entities
3241
+ // attached to it carry over to the new content.
3242
+ this._destroyContent();
3127
3243
  // Supersede any load already in flight - only the newest load may instantiate
3128
3244
  const generation = ++this._loadGeneration;
3129
3245
  this._detachLoadHandlers();
3130
3246
  // Re-arm readiness so a waiter obtained after an asset change resolves against the new
3131
- // hierarchy. A no-op on first connection, where readiness is still pending.
3247
+ // content. A no-op on first connection, where readiness is still pending.
3132
3248
  this._resetReady();
3133
3249
  const appElement = this.closestApp;
3134
3250
  if (!appElement) {
@@ -3140,14 +3256,23 @@
3140
3256
  if (generation !== this._loadGeneration) {
3141
3257
  return;
3142
3258
  }
3259
+ // The host may not be parented yet - a model under a pc-node that has not bound, reached
3260
+ // through the asset setter. Nothing settles here: _onBuilt re-runs this load once the
3261
+ // host builds.
3262
+ if (!this._entity || !this._built) {
3263
+ return;
3264
+ }
3265
+ if (this._asset === '') {
3266
+ // No asset assigned is a settled selection: the element is a usable host (components
3267
+ // attach, waiters resolve) with no content. Assigning an asset later re-arms.
3268
+ this._onReady();
3269
+ return;
3270
+ }
3143
3271
  const asset = useAsset(this._asset);
3144
3272
  if (!asset) {
3145
- // An empty id is a legitimate transient (the asset may be assigned later); a
3146
- // non-empty one that resolves to nothing is a dead end - say so rather than staying
3273
+ // A non-empty id that resolves to nothing is a dead end - say so rather than staying
3147
3274
  // silently pending.
3148
- if (this._asset) {
3149
- console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
3150
- }
3275
+ console.warn(`pc-model could not find asset '${this._asset}' - model not created`);
3151
3276
  return;
3152
3277
  }
3153
3278
  if (asset.loaded) {
@@ -3169,7 +3294,7 @@
3169
3294
  if (generation !== this._loadGeneration) {
3170
3295
  return;
3171
3296
  }
3172
- // A failed load settles readiness with a null entity, mirroring pc-asset:
3297
+ // A failed load settles readiness with a null contentEntity, mirroring pc-asset:
3173
3298
  // readiness means the load settled, not that it succeeded.
3174
3299
  this.dispatchEvent(new ErrorEvent('error', {
3175
3300
  message: err instanceof Error ? err.message : String(err)
@@ -3178,10 +3303,6 @@
3178
3303
  });
3179
3304
  }
3180
3305
  }
3181
- _unloadModel() {
3182
- this._entity?.destroy();
3183
- this._entity = null;
3184
- }
3185
3306
  /**
3186
3307
  * Sets the id of the `pc-asset` to use for the model.
3187
3308
  * @param value - The asset ID.
@@ -3189,7 +3310,7 @@
3189
3310
  set asset(value) {
3190
3311
  this._asset = value;
3191
3312
  if (this.isConnected) {
3192
- this._loadModel();
3313
+ this._loadContent();
3193
3314
  }
3194
3315
  }
3195
3316
  /**
@@ -3200,13 +3321,38 @@
3200
3321
  return this._asset;
3201
3322
  }
3202
3323
  static get observedAttributes() {
3203
- return ['asset'];
3324
+ return ['asset', 'enabled', 'name', 'position', 'rotation', 'scale', 'tags', ...POINTER_ATTRIBUTES];
3204
3325
  }
3205
3326
  attributeChangedCallback(name, _oldValue, newValue) {
3206
3327
  switch (name) {
3207
3328
  case 'asset':
3208
3329
  this.asset = newValue ?? '';
3209
3330
  break;
3331
+ case 'enabled':
3332
+ this.enabled = parseBool(newValue, true);
3333
+ break;
3334
+ case 'name':
3335
+ this.name = newValue ?? 'Untitled';
3336
+ break;
3337
+ case 'position':
3338
+ this.position = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
3339
+ break;
3340
+ case 'rotation':
3341
+ this.rotation = parseVec3(newValue, playcanvas.Vec3.ZERO, name);
3342
+ break;
3343
+ case 'scale':
3344
+ this.scale = parseVec3(newValue, playcanvas.Vec3.ONE, name);
3345
+ break;
3346
+ case 'tags':
3347
+ this.tags = parseTags(newValue);
3348
+ break;
3349
+ case 'onpointerenter':
3350
+ case 'onpointerleave':
3351
+ case 'onpointerdown':
3352
+ case 'onpointerup':
3353
+ case 'onpointermove':
3354
+ this._updateInlineHandler(name, newValue);
3355
+ break;
3210
3356
  }
3211
3357
  }
3212
3358
  }
@@ -3297,7 +3443,7 @@
3297
3443
  // A component can only exist on an entity, so an element placed outside one is inert.
3298
3444
  // It still becomes ready (with a null `component`), so warn rather than fail silently
3299
3445
  const label = this.id ? ` '${this.id}'` : '';
3300
- console.warn(`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity - component not added`);
3446
+ console.warn(`${this.tagName.toLowerCase()}${label} must be a descendant of pc-entity, pc-model or pc-node - component not added`);
3301
3447
  return;
3302
3448
  }
3303
3449
  await entityElement.ready();
@@ -3385,8 +3531,9 @@
3385
3531
  }
3386
3532
  /**
3387
3533
  * The PlayCanvas component instance. `null` until the element is ready, and also for an
3388
- * element that is not a descendant of a `<pc-entity>` — await {@link whenReady} or the
3389
- * element's `ready()` promise before accessing it.
3534
+ * element that is not a descendant of an entity-fronting element (`<pc-entity>`,
3535
+ * `<pc-model>` or `<pc-node>`) — await {@link whenReady} or the element's `ready()` promise
3536
+ * before accessing it.
3390
3537
  * @returns The component instance, or `null`.
3391
3538
  */
3392
3539
  get component() {
@@ -3439,6 +3586,13 @@
3439
3586
  * the underlying {@link AnimComponent} (via {@link component}) for playback state beyond what
3440
3587
  * this element exposes.
3441
3588
  *
3589
+ * Engine component: {@link AnimComponent} (`anim`).
3590
+ *
3591
+ * @elementSummary The `<pc-anim>` element plays animation clips over its entity's hierarchy, taken
3592
+ * from `<pc-anim-clip>` children or from the enclosing `<pc-model>`'s own animations. The first
3593
+ * clip plays automatically, and the `clip` attribute switches between them. Must be a child of a
3594
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
3595
+ *
3442
3596
  * @category Components
3443
3597
  */
3444
3598
  class AnimComponentElement extends ComponentElement {
@@ -3460,6 +3614,11 @@
3460
3614
  * The name of the active clip.
3461
3615
  */
3462
3616
  _clip = '';
3617
+ /**
3618
+ * The binding root this element last assigned through {@link _applyRootBone}, distinguishing
3619
+ * its own writes from a `rootBone` assigned through the engine API — which is left alone.
3620
+ */
3621
+ _managedRootBone = null;
3463
3622
  /**
3464
3623
  * The element the model-readiness listener is attached to, held so disconnection can detach
3465
3624
  * it after `closestEntity` no longer resolves.
@@ -3485,27 +3644,71 @@
3485
3644
  */
3486
3645
  _warnedClip = null;
3487
3646
  /**
3488
- * Rebinds when a model under the host announces readiness. The engine resolves each curve
3489
- * once, at the first tick after assignment, and never retries and its mesh-instance
3647
+ * Rebinds when a model that is not this component's host announces readiness a sibling or
3648
+ * deeper model whose content changed under the assigned clips. The engine resolves each
3649
+ * curve once, at the first tick after assignment, and never retries — and its mesh-instance
3490
3650
  * broadcast fires before an instantiated hierarchy is parented, so a model that loads after
3491
- * the clips were assigned would otherwise stay silently unbound. A re-instantiation of the
3492
- * implicit clip source (the parent `<pc-model>`) means a new container, so the clip set
3493
- * refreshes instead unless every clip declares its own asset, where a rebind suffices.
3651
+ * the clips were assigned would otherwise stay silently unbound. The host model's own cycles
3652
+ * are excluded: those re-enter through {@link initComponent}, which refreshes the whole clip
3653
+ * set against the new container.
3494
3654
  */
3495
3655
  _onModelReady = (event) => {
3496
3656
  if (!(event.target instanceof ModelElement) || !this.component) {
3497
3657
  return;
3498
3658
  }
3499
- if (event.target === this.parentElement) {
3500
- const implicit = this._autoAssigned ||
3501
- [...this._assignedClips.values()].some(clip => !clip.asset);
3502
- if (implicit) {
3503
- this._refreshClips();
3504
- return;
3505
- }
3659
+ // The host model's own readiness cycle already re-initialized this component through
3660
+ // ComponentElement's host-ready listener, which is attached first and so has run by now.
3661
+ // Acting here too would resolve every track twice and capture the playhead-restore
3662
+ // snapshot mid-rebuild.
3663
+ if (event.target === this._modelListenerTarget) {
3664
+ return;
3506
3665
  }
3666
+ // A model cycle can replace the skeleton source's host entity (a rebuild under a
3667
+ // retargeting pc-node), so the binding root is re-asserted before the rebind.
3668
+ this._applyRootBone();
3507
3669
  this.component.rebind();
3508
3670
  };
3671
+ /**
3672
+ * The model whose host entity scopes this component's curve binding: the parent `pc-model`,
3673
+ * or the sole `pc-model` among the parent's direct children (the arrangement where clips
3674
+ * live in a library asset beside the skeleton). `null` when there is no such model, or more
3675
+ * than one — an ambiguous skeleton is left to the engine's name-based resolution.
3676
+ */
3677
+ _skeletonSource() {
3678
+ const parent = this.parentElement;
3679
+ if (parent instanceof ModelElement) {
3680
+ return parent;
3681
+ }
3682
+ const models = parent ? parent.querySelectorAll(':scope > pc-model') : null;
3683
+ return models?.length === 1 && models[0] instanceof ModelElement ? models[0] : null;
3684
+ }
3685
+ /**
3686
+ * Keeps the component's binding root pointing at the skeleton source's host entity. The host
3687
+ * wraps the instantiated content, so left at its default — the component's own entity — the
3688
+ * engine binder mis-resolves curves that target the asset's root node: its fallback treats
3689
+ * the graph as the asset root once the root is no longer a direct child.
3690
+ *
3691
+ * Authoritative in both directions for values this element assigned: a source appearing pins
3692
+ * its host, and a source dissolving (the model gone, or a second model making the skeleton
3693
+ * ambiguous) clears the pin rather than leaving it on a stale host. A root assigned through
3694
+ * the engine API is never overwritten — the user's choice outranks the managed default.
3695
+ * Writes are skipped while unchanged, because the engine setter itself triggers a rebind.
3696
+ */
3697
+ _applyRootBone() {
3698
+ const component = this.component;
3699
+ // A non-null root this element did not assign came through the engine API. A fresh
3700
+ // component starts at null, which is always reclaimable.
3701
+ if (component.rootBone !== null && component.rootBone !== this._managedRootBone) {
3702
+ return;
3703
+ }
3704
+ const host = this._skeletonSource()?.entity ?? null;
3705
+ if (component.rootBone !== host) {
3706
+ // The engine setter accepts null - restoring the component's own entity as the
3707
+ // binding graph - but its declared type does not
3708
+ component.rootBone = host;
3709
+ }
3710
+ this._managedRootBone = host;
3711
+ }
3509
3712
  /** @ignore */
3510
3713
  constructor() {
3511
3714
  super('anim');
@@ -3530,7 +3733,16 @@
3530
3733
  host.addEventListener('ready', this._onModelReady);
3531
3734
  this._modelListenerTarget = host;
3532
3735
  }
3533
- this._applyClips();
3736
+ this._applyRootBone();
3737
+ if (this.component.baseLayer) {
3738
+ // The component survived the host's readiness cycle (a pc-model reloading content on
3739
+ // its stable host entity). A loaded graph cannot be reassigned in place, so drop it
3740
+ // and reassign from the current source, restoring the active clip and playhead.
3741
+ this._refreshClips();
3742
+ }
3743
+ else {
3744
+ this._applyClips();
3745
+ }
3534
3746
  }
3535
3747
  disconnectedCallback() {
3536
3748
  this._modelListenerTarget?.removeEventListener('ready', this._onModelReady);
@@ -3540,6 +3752,7 @@
3540
3752
  this._sourceGeneration++;
3541
3753
  this._assignedClips.clear();
3542
3754
  this._autoAssigned = false;
3755
+ this._managedRootBone = null;
3543
3756
  super.disconnectedCallback();
3544
3757
  }
3545
3758
  /**
@@ -3708,6 +3921,10 @@
3708
3921
  if (!component) {
3709
3922
  return;
3710
3923
  }
3924
+ // A clip-set change is also a chance for the skeleton source to have changed shape (a
3925
+ // clip child appearing or leaving can accompany a model coming or going) - re-derive the
3926
+ // binding root before the reassignment binds against it.
3927
+ this._applyRootBone();
3711
3928
  const layer = component.baseLayer;
3712
3929
  const restore = layer ? {
3713
3930
  state: layer.activeState,
@@ -3994,6 +4211,10 @@
3994
4211
  * named; in a multi-track source the track named `name` is chosen, falling back to the first
3995
4212
  * with a warning. The element becomes ready once its resolved track is assigned.
3996
4213
  *
4214
+ * @elementSummary The `<pc-anim-clip>` element declares one named animation clip on its parent
4215
+ * `<pc-anim>`, taken from the `asset` it names or, without one, from the enclosing `<pc-model>`'s
4216
+ * own animations. Must be a direct child of `<pc-anim>`.
4217
+ *
3997
4218
  * @category Components
3998
4219
  */
3999
4220
  class AnimClipElement extends AsyncElement {
@@ -4315,14 +4536,20 @@
4315
4536
  customElements.define('pc-anim-clip', AnimClipElement);
4316
4537
 
4317
4538
  /**
4318
- * The ListenerComponentElement interface provides properties and methods for manipulating
4319
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-listener/ | `<pc-listener>`} elements.
4320
- * The ListenerComponentElement interface also inherits the properties and methods of the
4539
+ * The AudioListenerComponentElement interface provides properties and methods for manipulating
4540
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-audio-listener/ | `<pc-audio-listener>`} elements.
4541
+ * The AudioListenerComponentElement interface also inherits the properties and methods of the
4321
4542
  * {@link HTMLElement} interface.
4322
4543
  *
4544
+ * Engine component: {@link AudioListenerComponent} (`audiolistener`).
4545
+ *
4546
+ * @elementSummary The `<pc-audio-listener>` element makes its entity the point from which
4547
+ * positional sounds are heard, typically the entity holding the `<pc-camera>`. Must be a child of a
4548
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
4549
+ *
4323
4550
  * @category Components
4324
4551
  */
4325
- class ListenerComponentElement extends ComponentElement {
4552
+ class AudioListenerComponentElement extends ComponentElement {
4326
4553
  /** @ignore */
4327
4554
  constructor() {
4328
4555
  super('audiolistener');
@@ -4335,7 +4562,7 @@
4335
4562
  return super.component;
4336
4563
  }
4337
4564
  }
4338
- customElements.define('pc-listener', ListenerComponentElement);
4565
+ customElements.define('pc-audio-listener', AudioListenerComponentElement);
4339
4566
 
4340
4567
  const transitionModes = new Map([
4341
4568
  ['tint', playcanvas.BUTTON_TRANSITION_MODE_TINT],
@@ -4347,6 +4574,13 @@
4347
4574
  * The ButtonComponentElement interface also inherits the properties and methods of the
4348
4575
  * {@link HTMLElement} interface.
4349
4576
  *
4577
+ * Engine component: {@link ButtonComponent} (`button`).
4578
+ *
4579
+ * @elementSummary The `<pc-button>` element makes its entity respond to pointer input, tinting or
4580
+ * swapping its image as the pointer hovers, presses and releases it. The entity also needs a
4581
+ * `<pc-element>` with `use-input` set. Must be a child of a `<pc-entity>`, `<pc-model>` or
4582
+ * `<pc-node>`.
4583
+ *
4350
4584
  * @category Components
4351
4585
  */
4352
4586
  class ButtonComponentElement extends ComponentElement {
@@ -4427,7 +4661,9 @@
4427
4661
  }
4428
4662
  /**
4429
4663
  * Sets the reference (CSS selector, element id or entity name) to the `<pc-entity>` whose image
4430
- * element is used for visual transitions. Defaults to the button's own entity.
4664
+ * element is used for visual transitions. Defaults to the button's own entity — inside a
4665
+ * `<pc-model>`, that is the model's host entity, so supply an explicit reference to target a
4666
+ * UI entity instead.
4431
4667
  * @param value - The image entity reference.
4432
4668
  */
4433
4669
  set image(value) {
@@ -4746,13 +4982,20 @@
4746
4982
  * The CameraComponentElement interface also inherits the properties and methods of the
4747
4983
  * {@link HTMLElement} interface.
4748
4984
  *
4985
+ * Engine component: {@link CameraComponent} (`camera`).
4986
+ *
4987
+ * @elementSummary The `<pc-camera>` element renders the scene from its entity's transform, with
4988
+ * attributes for the projection, field of view, clip planes, clear color and tonemapping. Must be a
4989
+ * child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
4990
+ *
4749
4991
  * @category Components
4750
4992
  */
4751
4993
  class CameraComponentElement extends ComponentElement {
4752
4994
  _clearColor = new playcanvas.Color(0.75, 0.75, 0.75, 1);
4753
4995
  _clearColorBuffer = true;
4996
+ _clearDepth = 1;
4754
4997
  _clearDepthBuffer = true;
4755
- _clearStencilBuffer = false;
4998
+ _clearStencilBuffer = true;
4756
4999
  _cullFaces = true;
4757
5000
  _farClip = 1000;
4758
5001
  _flipFaces = false;
@@ -4775,6 +5018,7 @@
4775
5018
  return {
4776
5019
  clearColor: this._clearColor,
4777
5020
  clearColorBuffer: this._clearColorBuffer,
5021
+ clearDepth: this._clearDepth,
4778
5022
  clearDepthBuffer: this._clearDepthBuffer,
4779
5023
  clearStencilBuffer: this._clearStencilBuffer,
4780
5024
  cullFaces: this._cullFaces,
@@ -4793,9 +5037,31 @@
4793
5037
  toneMapping: tonemaps.get(this._tonemap) ?? playcanvas.TONEMAP_NONE
4794
5038
  };
4795
5039
  }
4796
- get xrAvailable() {
5040
+ /**
5041
+ * Whether immersive AR is available. Independent of {@link vrAvailable}: a device can offer
5042
+ * either mode without the other.
5043
+ * @returns Whether immersive AR is available.
5044
+ */
5045
+ get arAvailable() {
5046
+ return this._available(playcanvas.XRTYPE_AR);
5047
+ }
5048
+ /**
5049
+ * Whether immersive VR is available. Independent of {@link arAvailable}: a device can offer
5050
+ * either mode without the other.
5051
+ * @returns Whether immersive VR is available.
5052
+ */
5053
+ get vrAvailable() {
5054
+ return this._available(playcanvas.XRTYPE_VR);
5055
+ }
5056
+ /**
5057
+ * Whether one XR session type is available on this device.
5058
+ *
5059
+ * @param type - The XR session type to test.
5060
+ * @returns Whether that type is available.
5061
+ */
5062
+ _available(type) {
4797
5063
  const xrManager = this.component?.system.app.xr;
4798
- return xrManager && xrManager.supported && xrManager.isAvailable(playcanvas.XRTYPE_VR);
5064
+ return Boolean(xrManager?.supported && xrManager.isAvailable(type));
4799
5065
  }
4800
5066
  /**
4801
5067
  * Starts the camera in XR mode.
@@ -4803,11 +5069,13 @@
4803
5069
  * @param space - The space to start the camera in.
4804
5070
  */
4805
5071
  startXr(type, space) {
4806
- if (this.component && this.xrAvailable) {
5072
+ // Gated on the mode being started, not on XR in general: a device that offers only
5073
+ // one of the two would otherwise accept a session it cannot serve
5074
+ if (this.component && this._available(type)) {
4807
5075
  this.component.startXr(type, space, {
4808
5076
  callback: (err) => {
4809
5077
  if (err)
4810
- console.error(`WebXR Immersive VR failed to start: ${err.message}`);
5078
+ console.error(`WebXR ${type} failed to start: ${err.message}`);
4811
5079
  }
4812
5080
  });
4813
5081
  }
@@ -4861,6 +5129,23 @@
4861
5129
  get clearColorBuffer() {
4862
5130
  return this._clearColorBuffer;
4863
5131
  }
5132
+ /**
5133
+ * Sets the depth value the depth buffer is cleared to. Defaults to 1.
5134
+ * @param value - The clear depth value.
5135
+ */
5136
+ set clearDepth(value) {
5137
+ this._clearDepth = value;
5138
+ if (this.component) {
5139
+ this.component.clearDepth = value;
5140
+ }
5141
+ }
5142
+ /**
5143
+ * Gets the depth value the depth buffer is cleared to.
5144
+ * @returns The clear depth value.
5145
+ */
5146
+ get clearDepth() {
5147
+ return this._clearDepth;
5148
+ }
4864
5149
  /**
4865
5150
  * Sets the clear depth buffer of the camera.
4866
5151
  * @param value - The clear depth buffer.
@@ -5139,6 +5424,7 @@
5139
5424
  ...super.observedAttributes,
5140
5425
  'clear-color',
5141
5426
  'clear-color-buffer',
5427
+ 'clear-depth',
5142
5428
  'clear-depth-buffer',
5143
5429
  'clear-stencil-buffer',
5144
5430
  'cull-faces',
@@ -5166,11 +5452,14 @@
5166
5452
  case 'clear-color-buffer':
5167
5453
  this.clearColorBuffer = parseBool(newValue, true);
5168
5454
  break;
5455
+ case 'clear-depth':
5456
+ this.clearDepth = parseNumber(newValue, 1, name);
5457
+ break;
5169
5458
  case 'clear-depth-buffer':
5170
5459
  this.clearDepthBuffer = parseBool(newValue, true);
5171
5460
  break;
5172
5461
  case 'clear-stencil-buffer':
5173
- this.clearStencilBuffer = parseBool(newValue, false);
5462
+ this.clearStencilBuffer = parseBool(newValue, true);
5174
5463
  break;
5175
5464
  case 'cull-faces':
5176
5465
  this.cullFaces = parseBool(newValue, true);
@@ -5231,6 +5520,12 @@
5231
5520
  * retargets or rebinds picks up the new node's geometry. An entity with no asset-backed render
5232
5521
  * component warns, and the collider has no shape.
5233
5522
  *
5523
+ * Engine component: {@link CollisionComponent} (`collision`).
5524
+ *
5525
+ * @elementSummary The `<pc-collision>` element gives its entity a collision shape — a box, sphere,
5526
+ * capsule, cone, cylinder or mesh — for the physics simulation to collide against. Pair it with a
5527
+ * `<pc-rigid-body>`. Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
5528
+ *
5234
5529
  * @category Components
5235
5530
  */
5236
5531
  class CollisionComponentElement extends ComponentElement {
@@ -5267,7 +5562,9 @@
5267
5562
  * no attribute to supply it - so the host's visible geometry, the meaning a mesh collider
5268
5563
  * on a glTF node carries, fills the gap. Runs on every application (so a rebound `pc-node`
5269
5564
  * recomputes it) and on a runtime switch to `type="mesh"`; an explicitly assigned
5270
- * `renderAsset` is never overwritten.
5565
+ * `renderAsset` is never overwritten. A `pc-model` host entity never carries a render
5566
+ * component (the instantiated content beneath it does), so a mesh collider that should take
5567
+ * an asset's geometry belongs on a bound `pc-node`.
5271
5568
  */
5272
5569
  _applyMeshGeometryDefault() {
5273
5570
  const component = this.component;
@@ -5412,6 +5709,17 @@
5412
5709
  * The ElementComponentElement interface also inherits the properties and methods of the
5413
5710
  * {@link HTMLElement} interface.
5414
5711
  *
5712
+ * Despite the name, this is not a base class or a generic wrapper: it is the engine's 2D UI
5713
+ * component, which gives its host entity a rectangle in a `<pc-screen>` hierarchy that draws
5714
+ * either an image, a line of text or nothing (`type="image"`, `"text"` or `"group"`). The tag
5715
+ * spells the engine component it adds, as every component element does.
5716
+ *
5717
+ * Engine component: {@link ElementComponent} (`element`).
5718
+ *
5719
+ * @elementSummary The `<pc-element>` element gives its entity a 2D UI rectangle inside a
5720
+ * `<pc-screen>` hierarchy, drawing an image, a line of text or nothing (`type="image"`, `"text"` or
5721
+ * `"group"`). Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
5722
+ *
5415
5723
  * @category Components
5416
5724
  */
5417
5725
  class ElementComponentElement extends ComponentElement {
@@ -6096,10 +6404,17 @@
6096
6404
  * a rigid body component; leaving `entity-b` empty constrains `entity-a` to a fixed point in world
6097
6405
  * space. The underlying engine component is in alpha, so its API may change.
6098
6406
  *
6407
+ * @elementSummary The `<pc-joint>` element constrains two rigid bodies to each other — a hinged
6408
+ * door, a swinging chain, a sliding drawer. Its entity's transform is the joint frame, and
6409
+ * `entity-a` and `entity-b` name the bodies. Must be a child of a `<pc-entity>`, `<pc-model>` or
6410
+ * `<pc-node>`.
6411
+ *
6099
6412
  * @fires {CustomEvent} break - Fired when the impulse on the joint exceeds `break-impulse` and the
6100
6413
  * constraint breaks. A broken joint no longer constrains its bodies; calling `refreshFrames()` on
6101
6414
  * the underlying component re-attaches it. Bubbles and is composed.
6102
6415
  *
6416
+ * Engine component: {@link JointComponent} (`joint`).
6417
+ *
6103
6418
  * @category Components
6104
6419
  */
6105
6420
  class JointComponentElement extends ComponentElement {
@@ -6958,10 +7273,16 @@
6958
7273
 
6959
7274
  /**
6960
7275
  * The LayoutChildComponentElement interface provides properties and methods for manipulating
6961
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layoutchild/ | `<pc-layoutchild>`} elements.
7276
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layout-child/ | `<pc-layout-child>`} elements.
6962
7277
  * The LayoutChildComponentElement interface also inherits the properties and methods of the
6963
7278
  * {@link HTMLElement} interface.
6964
7279
  *
7280
+ * Engine component: {@link LayoutChildComponent} (`layoutchild`).
7281
+ *
7282
+ * @elementSummary The `<pc-layout-child>` element controls how its entity is sized by the
7283
+ * `<pc-layout-group>` above it, through minimum and maximum sizes and fit proportions. Must be a
7284
+ * child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
7285
+ *
6965
7286
  * @category Components
6966
7287
  */
6967
7288
  class LayoutChildComponentElement extends ComponentElement {
@@ -7154,7 +7475,7 @@
7154
7475
  }
7155
7476
  }
7156
7477
  }
7157
- customElements.define('pc-layoutchild', LayoutChildComponentElement);
7478
+ customElements.define('pc-layout-child', LayoutChildComponentElement);
7158
7479
 
7159
7480
  const orientations$1 = new Map([
7160
7481
  ['horizontal', playcanvas.ORIENTATION_HORIZONTAL],
@@ -7168,10 +7489,16 @@
7168
7489
  ]);
7169
7490
  /**
7170
7491
  * The LayoutGroupComponentElement interface provides properties and methods for manipulating
7171
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layoutgroup/ | `<pc-layoutgroup>`} elements.
7492
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-layout-group/ | `<pc-layout-group>`} elements.
7172
7493
  * The LayoutGroupComponentElement interface also inherits the properties and methods of the
7173
7494
  * {@link HTMLElement} interface.
7174
7495
  *
7496
+ * Engine component: {@link LayoutGroupComponent} (`layoutgroup`).
7497
+ *
7498
+ * @elementSummary The `<pc-layout-group>` element arranges its entity's children in a row or
7499
+ * column, with spacing, padding, alignment and fitting. Must be a child of a `<pc-entity>`,
7500
+ * `<pc-model>` or `<pc-node>`.
7501
+ *
7175
7502
  * @category Components
7176
7503
  */
7177
7504
  class LayoutGroupComponentElement extends ComponentElement {
@@ -7411,7 +7738,7 @@
7411
7738
  }
7412
7739
  }
7413
7740
  }
7414
- customElements.define('pc-layoutgroup', LayoutGroupComponentElement);
7741
+ customElements.define('pc-layout-group', LayoutGroupComponentElement);
7415
7742
 
7416
7743
  const shadowTypes = new Map([
7417
7744
  ['pcf1-16f', playcanvas.SHADOW_PCF1_16F],
@@ -7430,23 +7757,32 @@
7430
7757
  * The LightComponentElement interface also inherits the properties and methods of the
7431
7758
  * {@link HTMLElement} interface.
7432
7759
  *
7760
+ * Engine component: {@link LightComponent} (`light`).
7761
+ *
7762
+ * @elementSummary The `<pc-light>` element lights the scene from its entity — as a directional,
7763
+ * omni or spot light — with attributes for color, intensity, range and shadows. Must be a child of
7764
+ * a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
7765
+ *
7433
7766
  * @category Components
7434
7767
  */
7435
7768
  class LightComponentElement extends ComponentElement {
7769
+ _cascadeBlend = 0;
7770
+ _cascadeDistribution = 0.5;
7436
7771
  _castShadows = false;
7437
7772
  _color = new playcanvas.Color(1, 1, 1);
7438
7773
  _innerConeAngle = 40;
7439
7774
  _intensity = 1;
7440
- _normalOffsetBias = 0.05;
7775
+ _normalOffsetBias = 0;
7776
+ _numCascades = 1;
7441
7777
  _outerConeAngle = 45;
7442
7778
  _range = 10;
7443
- _shadowBias = 0.2;
7444
- _shadowDistance = 16;
7779
+ _shadowBias = 0.05;
7780
+ _shadowDistance = 40;
7445
7781
  _shadowIntensity = 1;
7446
7782
  _shadowResolution = 1024;
7447
7783
  _shadowType = 'pcf3-32f';
7448
7784
  _type = 'directional';
7449
- _vsmBias = 0.01;
7785
+ _vsmBias = 0.0025;
7450
7786
  _vsmBlurSize = 11;
7451
7787
  _penumbraSize = 1;
7452
7788
  _penumbraFalloff = 1;
@@ -7458,11 +7794,14 @@
7458
7794
  }
7459
7795
  getInitialComponentData() {
7460
7796
  return {
7797
+ cascadeBlend: this._cascadeBlend,
7798
+ cascadeDistribution: this._cascadeDistribution,
7461
7799
  castShadows: this._castShadows,
7462
7800
  color: this._color,
7463
7801
  innerConeAngle: this._innerConeAngle,
7464
7802
  intensity: this._intensity,
7465
7803
  normalOffsetBias: this._normalOffsetBias,
7804
+ numCascades: this._numCascades,
7466
7805
  outerConeAngle: this._outerConeAngle,
7467
7806
  penumbraFalloff: this._penumbraFalloff,
7468
7807
  penumbraSize: this._penumbraSize,
@@ -7473,7 +7812,7 @@
7473
7812
  shadowIntensity: this._shadowIntensity,
7474
7813
  shadowResolution: this._shadowResolution,
7475
7814
  shadowSamples: this._shadowSamples,
7476
- shadowType: shadowTypes.get(this._shadowType),
7815
+ shadowType: shadowTypes.get(this._shadowType) ?? playcanvas.SHADOW_PCF3_32F,
7477
7816
  type: this._type,
7478
7817
  vsmBias: this._vsmBias,
7479
7818
  vsmBlurSize: this._vsmBlurSize
@@ -7486,6 +7825,47 @@
7486
7825
  get component() {
7487
7826
  return super.component;
7488
7827
  }
7828
+ /**
7829
+ * Sets the fraction of each shadow cascade that is blended into the next one, from 0 (no
7830
+ * blending) to 1, which applies only to `directional` lights with `num-cascades` greater than
7831
+ * 1. Defaults to 0.
7832
+ * @param value - The cascade blend factor.
7833
+ */
7834
+ set cascadeBlend(value) {
7835
+ this._cascadeBlend = value;
7836
+ if (this.component) {
7837
+ this.component.cascadeBlend = value;
7838
+ }
7839
+ }
7840
+ /**
7841
+ * Gets the cascade blend factor of the light, from 0 (no blending) to 1, which applies only to
7842
+ * `directional` lights with `num-cascades` greater than 1.
7843
+ * @returns The cascade blend factor.
7844
+ */
7845
+ get cascadeBlend() {
7846
+ return this._cascadeBlend;
7847
+ }
7848
+ /**
7849
+ * Sets the distribution of the camera frustum split between shadow cascades, from 0 (linear
7850
+ * split) to 1 (logarithmic split, concentrating shadow resolution near the camera), which
7851
+ * applies only to `directional` lights with `num-cascades` greater than 1. Defaults to 0.5.
7852
+ * @param value - The cascade distribution.
7853
+ */
7854
+ set cascadeDistribution(value) {
7855
+ this._cascadeDistribution = value;
7856
+ if (this.component) {
7857
+ this.component.cascadeDistribution = value;
7858
+ }
7859
+ }
7860
+ /**
7861
+ * Gets the cascade distribution of the light, from 0 (linear split) to 1 (logarithmic split,
7862
+ * concentrating shadow resolution near the camera), which applies only to `directional` lights
7863
+ * with `num-cascades` greater than 1.
7864
+ * @returns The cascade distribution.
7865
+ */
7866
+ get cascadeDistribution() {
7867
+ return this._cascadeDistribution;
7868
+ }
7489
7869
  /**
7490
7870
  * Sets the cast shadows flag of the light.
7491
7871
  * @param value - The cast shadows flag.
@@ -7571,6 +7951,25 @@
7571
7951
  get normalOffsetBias() {
7572
7952
  return this._normalOffsetBias;
7573
7953
  }
7954
+ /**
7955
+ * Sets the number of shadow cascades of the light, an integer from 1 (no cascades) to 4, which
7956
+ * applies only to `directional` lights. Defaults to 1.
7957
+ * @param value - The number of shadow cascades.
7958
+ */
7959
+ set numCascades(value) {
7960
+ this._numCascades = value;
7961
+ if (this.component) {
7962
+ this.component.numCascades = value;
7963
+ }
7964
+ }
7965
+ /**
7966
+ * Gets the number of shadow cascades of the light, an integer from 1 (no cascades) to 4, which
7967
+ * applies only to `directional` lights.
7968
+ * @returns The number of shadow cascades.
7969
+ */
7970
+ get numCascades() {
7971
+ return this._numCascades;
7972
+ }
7574
7973
  /**
7575
7974
  * Sets the outer cone angle of the light.
7576
7975
  * @param value - The outer cone angle.
@@ -7823,11 +8222,14 @@
7823
8222
  static get observedAttributes() {
7824
8223
  return [
7825
8224
  ...super.observedAttributes,
7826
- 'color',
8225
+ 'cascade-blend',
8226
+ 'cascade-distribution',
7827
8227
  'cast-shadows',
7828
- 'intensity',
8228
+ 'color',
7829
8229
  'inner-cone-angle',
8230
+ 'intensity',
7830
8231
  'normal-offset-bias',
8232
+ 'num-cascades',
7831
8233
  'outer-cone-angle',
7832
8234
  'penumbra-falloff',
7833
8235
  'penumbra-size',
@@ -7847,12 +8249,18 @@
7847
8249
  attributeChangedCallback(name, _oldValue, newValue) {
7848
8250
  super.attributeChangedCallback(name, _oldValue, newValue);
7849
8251
  switch (name) {
7850
- case 'color':
7851
- this.color = parseColor(newValue, playcanvas.Color.WHITE, name);
8252
+ case 'cascade-blend':
8253
+ this.cascadeBlend = parseNumber(newValue, 0, name);
8254
+ break;
8255
+ case 'cascade-distribution':
8256
+ this.cascadeDistribution = parseNumber(newValue, 0.5, name);
7852
8257
  break;
7853
8258
  case 'cast-shadows':
7854
8259
  this.castShadows = parseBool(newValue, false);
7855
8260
  break;
8261
+ case 'color':
8262
+ this.color = parseColor(newValue, playcanvas.Color.WHITE, name);
8263
+ break;
7856
8264
  case 'inner-cone-angle':
7857
8265
  this.innerConeAngle = parseNumber(newValue, 40, name);
7858
8266
  break;
@@ -7860,7 +8268,10 @@
7860
8268
  this.intensity = parseNumber(newValue, 1, name);
7861
8269
  break;
7862
8270
  case 'normal-offset-bias':
7863
- this.normalOffsetBias = parseNumber(newValue, 0.05, name);
8271
+ this.normalOffsetBias = parseNumber(newValue, 0, name);
8272
+ break;
8273
+ case 'num-cascades':
8274
+ this.numCascades = parseNumber(newValue, 1, name);
7864
8275
  break;
7865
8276
  case 'outer-cone-angle':
7866
8277
  this.outerConeAngle = parseNumber(newValue, 45, name);
@@ -7875,20 +8286,20 @@
7875
8286
  this.range = parseNumber(newValue, 10, name);
7876
8287
  break;
7877
8288
  case 'shadow-bias':
7878
- this.shadowBias = parseNumber(newValue, 0.2, name);
7879
- break;
7880
- case 'shadow-distance':
7881
- this.shadowDistance = parseNumber(newValue, 16, name);
8289
+ this.shadowBias = parseNumber(newValue, 0.05, name);
7882
8290
  break;
7883
8291
  case 'shadow-blocker-samples':
7884
8292
  this.shadowBlockerSamples = parseNumber(newValue, 16, name);
7885
8293
  break;
7886
- case 'shadow-resolution':
7887
- this.shadowResolution = parseNumber(newValue, 1024, name);
8294
+ case 'shadow-distance':
8295
+ this.shadowDistance = parseNumber(newValue, 40, name);
7888
8296
  break;
7889
8297
  case 'shadow-intensity':
7890
8298
  this.shadowIntensity = parseNumber(newValue, 1, name);
7891
8299
  break;
8300
+ case 'shadow-resolution':
8301
+ this.shadowResolution = parseNumber(newValue, 1024, name);
8302
+ break;
7892
8303
  case 'shadow-samples':
7893
8304
  this.shadowSamples = parseNumber(newValue, 16, name);
7894
8305
  break;
@@ -7899,7 +8310,7 @@
7899
8310
  this.type = parseEnum(newValue, ['directional', 'omni', 'spot'], 'directional', name);
7900
8311
  break;
7901
8312
  case 'vsm-bias':
7902
- this.vsmBias = parseNumber(newValue, 0.01, name);
8313
+ this.vsmBias = parseNumber(newValue, 0.0025, name);
7903
8314
  break;
7904
8315
  case 'vsm-blur-size':
7905
8316
  this.vsmBlurSize = parseNumber(newValue, 11, name);
@@ -7911,10 +8322,16 @@
7911
8322
 
7912
8323
  /**
7913
8324
  * The ParticleSystemComponentElement interface provides properties and methods for manipulating
7914
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-particles/ | `<pc-particles>`} elements.
8325
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-particle-system/ | `<pc-particle-system>`} elements.
7915
8326
  * The ParticleSystemComponentElement interface also inherits the properties and methods of the
7916
8327
  * {@link HTMLElement} interface.
7917
8328
  *
8329
+ * Engine component: {@link ParticleSystemComponent} (`particlesystem`).
8330
+ *
8331
+ * @elementSummary The `<pc-particle-system>` element emits particles from its entity, with
8332
+ * attributes for the emitter's shape, rate, lifetime, textures and blending. Must be a child of a
8333
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
8334
+ *
7918
8335
  * @category Components
7919
8336
  */
7920
8337
  class ParticleSystemComponentElement extends ComponentElement {
@@ -8033,7 +8450,7 @@
8033
8450
  }
8034
8451
  }
8035
8452
  }
8036
- customElements.define('pc-particles', ParticleSystemComponentElement);
8453
+ customElements.define('pc-particle-system', ParticleSystemComponentElement);
8037
8454
 
8038
8455
  const blendTypes = new Map([
8039
8456
  ['none', playcanvas.BLEND_NONE],
@@ -8063,6 +8480,8 @@
8063
8480
  ['ao', playcanvas.SPECOCC_AO],
8064
8481
  ['gloss-dependent', playcanvas.SPECOCC_GLOSSDEPENDENT]
8065
8482
  ]);
8483
+ // The DITHER_* constants are strings whose values are exactly these names, so a parsed value is
8484
+ // assigned to the material unchanged rather than mapped through a table.
8066
8485
  const opacityDithers = ['none', 'bayer8', 'bluenoise', 'ignnoise'];
8067
8486
  const colorChannels = ['r', 'g', 'b', 'a', 'rgb'];
8068
8487
  const scalarChannels = ['r', 'g', 'b', 'a'];
@@ -8099,6 +8518,10 @@
8099
8518
  * The two aliases are documented here rather than on an accessor, because they resolve to the
8100
8519
  * `gloss` properties and would otherwise inherit gloss's description - which reads inverted.
8101
8520
  *
8521
+ * @elementSummary The `<pc-material>` element defines a physically based material, which
8522
+ * `<pc-render>` elements apply by naming its `id`. It is metal/rough by default, with `metalness`
8523
+ * starting at 0. Must be a direct child of `<pc-app>`.
8524
+ *
8102
8525
  * @attribute {number} roughness - The roughness of the material, from 0 (shiny) to 1 (rough). An
8103
8526
  * alias for `gloss` that also inverts it, so do not combine it with the `gloss` attributes.
8104
8527
  * @attribute {string} roughness-map - The id of the `pc-asset` to use as the roughness map. An
@@ -10265,6 +10688,13 @@
10265
10688
  * to `box`). It does not cover the engine's `asset` render type, since there is no way to supply
10266
10689
  * a render asset here — use `pc-model` for glTF content instead.
10267
10690
  *
10691
+ * Engine component: {@link RenderComponent} (`render`).
10692
+ *
10693
+ * @elementSummary The `<pc-render>` element renders one of the engine's built-in primitives at its
10694
+ * entity — box, sphere, capsule, cone, cylinder or plane — shaded by the `<pc-material>` its
10695
+ * `material` attribute names. For glTF content, use `<pc-model>` instead. Must be a child of a
10696
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
10697
+ *
10268
10698
  * @category Components
10269
10699
  */
10270
10700
  class RenderComponentElement extends ComponentElement {
@@ -10389,10 +10819,17 @@
10389
10819
 
10390
10820
  /**
10391
10821
  * The RigidBodyComponentElement interface provides properties and methods for manipulating
10392
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-rigidbody/ | `<pc-rigidbody>`} elements.
10822
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-rigid-body/ | `<pc-rigid-body>`} elements.
10393
10823
  * The RigidBodyComponentElement interface also inherits the properties and methods of the
10394
10824
  * {@link HTMLElement} interface.
10395
10825
  *
10826
+ * Engine component: {@link RigidBodyComponent} (`rigidbody`).
10827
+ *
10828
+ * @elementSummary The `<pc-rigid-body>` element hands its entity to the physics simulation, with
10829
+ * attributes for its type, mass, friction and restitution. It needs a sibling `<pc-collision>` for
10830
+ * its shape, and `Ammo` loaded through `<pc-wasm>`. Must be a child of a `<pc-entity>`,
10831
+ * `<pc-model>` or `<pc-node>`.
10832
+ *
10396
10833
  * @category Components
10397
10834
  */
10398
10835
  class RigidBodyComponentElement extends ComponentElement {
@@ -10584,7 +11021,7 @@
10584
11021
  }
10585
11022
  }
10586
11023
  }
10587
- customElements.define('pc-rigidbody', RigidBodyComponentElement);
11024
+ customElements.define('pc-rigid-body', RigidBodyComponentElement);
10588
11025
 
10589
11026
  // The engine's SCALEMODE_* constants are the strings 'none' and 'blend', so this map happens to be
10590
11027
  // an identity. It is still the right shape: it supplies parseEnum's valid-name list, it is what the
@@ -10600,6 +11037,12 @@
10600
11037
  * The ScreenComponentElement interface also inherits the properties and methods of the
10601
11038
  * {@link HTMLElement} interface.
10602
11039
  *
11040
+ * Engine component: {@link ScreenComponent} (`screen`).
11041
+ *
11042
+ * @elementSummary The `<pc-screen>` element gives its entity a 2D space — in screen space or in the
11043
+ * world — that a hierarchy of `<pc-element>` descendants lays out inside. Must be a child of a
11044
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
11045
+ *
10603
11046
  * @category Components
10604
11047
  */
10605
11048
  class ScreenComponentElement extends ComponentElement {
@@ -10752,6 +11195,12 @@
10752
11195
  * The ScrollbarComponentElement interface also inherits the properties and methods of the
10753
11196
  * {@link HTMLElement} interface.
10754
11197
  *
11198
+ * Engine component: {@link ScrollbarComponent} (`scrollbar`).
11199
+ *
11200
+ * @elementSummary The `<pc-scrollbar>` element gives its entity a draggable handle reporting a
11201
+ * position from 0 to 1, which a `<pc-scroll-view>` references to scroll its content. Must be a
11202
+ * child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
11203
+ *
10755
11204
  * @category Components
10756
11205
  */
10757
11206
  class ScrollbarComponentElement extends ComponentElement {
@@ -10887,10 +11336,16 @@
10887
11336
  ]);
10888
11337
  /**
10889
11338
  * The ScrollViewComponentElement interface provides properties and methods for manipulating
10890
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scrollview/ | `<pc-scrollview>`} elements.
11339
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scroll-view/ | `<pc-scroll-view>`} elements.
10891
11340
  * The ScrollViewComponentElement interface also inherits the properties and methods of the
10892
11341
  * {@link HTMLElement} interface.
10893
11342
  *
11343
+ * Engine component: {@link ScrollViewComponent} (`scrollview`).
11344
+ *
11345
+ * @elementSummary The `<pc-scroll-view>` element scrolls a larger content entity within a clipped
11346
+ * viewport at its entity, optionally driven by the `<pc-scrollbar>` elements it references. Must be
11347
+ * a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
11348
+ *
10894
11349
  * @category Components
10895
11350
  */
10896
11351
  class ScrollViewComponentElement extends ComponentElement {
@@ -11249,11 +11704,11 @@
11249
11704
  }
11250
11705
  }
11251
11706
  }
11252
- customElements.define('pc-scrollview', ScrollViewComponentElement);
11707
+ customElements.define('pc-scroll-view', ScrollViewComponentElement);
11253
11708
 
11254
11709
  /**
11255
- * The ScriptElement interface provides properties and methods for manipulating
11256
- * `<pc-script>` elements. The ScriptElement interface also inherits the properties and
11710
+ * The ScriptInstanceElement interface provides properties and methods for manipulating
11711
+ * `<pc-script-instance>` elements. The ScriptInstanceElement interface also inherits the properties and
11257
11712
  * methods of the {@link AsyncElement} interface.
11258
11713
  *
11259
11714
  * Script attributes can be supplied through two channels:
@@ -11275,7 +11730,11 @@
11275
11730
  * new-name one, re-applying both attribute channels to it.
11276
11731
  *
11277
11732
  * The element becomes ready once its script instance has been created by the parent
11278
- * `<pc-scripts>` element.
11733
+ * `<pc-script>` element.
11734
+ *
11735
+ * @elementSummary The `<pc-script-instance>` element attaches one script class, named by `name`, to
11736
+ * the entity of its parent `<pc-script>`. Its other attributes set script attributes of the same
11737
+ * name, and `attributes` takes a JSON object instead. Must be a direct child of `<pc-script>`.
11279
11738
  *
11280
11739
  * @fires {CustomEvent} scriptattributeschange - Fired when the script's attributes change. The
11281
11740
  * `detail` carries the new `attributes` object. Bubbles.
@@ -11284,11 +11743,11 @@
11284
11743
  * @fires {CustomEvent} scriptnamechange - Fired when the script is renamed on a live element. The
11285
11744
  * `detail` carries `oldName` and `newName`. Bubbles.
11286
11745
  */
11287
- class ScriptElement extends AsyncElement {
11746
+ class ScriptInstanceElement extends AsyncElement {
11288
11747
  _attributes = {};
11289
11748
  _enabled = true;
11290
11749
  /**
11291
- * The Script instance created for this element by its parent `<pc-scripts>` element.
11750
+ * The Script instance created for this element by its parent `<pc-script>` element.
11292
11751
  * @internal
11293
11752
  */
11294
11753
  _script = null;
@@ -11334,11 +11793,11 @@
11334
11793
  }
11335
11794
  /**
11336
11795
  * Sets the name of the script to create. The `name` attribute is the single source of truth
11337
- * (it is what the parent `<pc-scripts>` element reads when creating the instance), so the
11796
+ * (it is what the parent `<pc-script>` element reads when creating the instance), so the
11338
11797
  * property writes through to it — assigning before insertion works as expected:
11339
11798
  *
11340
11799
  * ```js
11341
- * const script = document.createElement('pc-script');
11800
+ * const script = document.createElement('pc-script-instance');
11342
11801
  * script.name = 'rotate';
11343
11802
  * scriptsElement.appendChild(script);
11344
11803
  * await script.ready();
@@ -11365,10 +11824,10 @@
11365
11824
  return this._script;
11366
11825
  }
11367
11826
  connectedCallback() {
11368
- // Script instances are created by the parent pc-scripts element, so an element placed
11827
+ // Script instances are created by the parent pc-script element, so an element placed
11369
11828
  // anywhere else is inert and never becomes ready - warn rather than hang silently
11370
- if (this.parentElement?.tagName !== 'PC-SCRIPTS') {
11371
- console.warn(`pc-script '${this.getAttribute('name')}' must be a direct child of pc-scripts - script not created`);
11829
+ if (this.parentElement?.tagName !== 'PC-SCRIPT') {
11830
+ console.warn(`pc-script-instance '${this.getAttribute('name')}' must be a direct child of pc-script - script not created`);
11372
11831
  }
11373
11832
  }
11374
11833
  disconnectedCallback() {
@@ -11379,7 +11838,7 @@
11379
11838
  this._resetReady();
11380
11839
  }
11381
11840
  /**
11382
- * Called by the parent `<pc-scripts>` element when the script instance has been created.
11841
+ * Called by the parent `<pc-script>` element when the script instance has been created.
11383
11842
  * Creation can happen more than once per connection (a runtime `name` change recreates the
11384
11843
  * instance), but `_onReady` signals readiness at most once per cycle.
11385
11844
  * @internal
@@ -11401,7 +11860,7 @@
11401
11860
  this.scriptAttributes = JSON.parse(newValue);
11402
11861
  }
11403
11862
  catch (error) {
11404
- console.warn(`Invalid 'attributes' JSON on pc-script '${this.getAttribute('name')}': ${error.message}`);
11863
+ console.warn(`Invalid 'attributes' JSON on pc-script-instance '${this.getAttribute('name')}': ${error.message}`);
11405
11864
  }
11406
11865
  break;
11407
11866
  case 'enabled':
@@ -11421,14 +11880,14 @@
11421
11880
  }
11422
11881
  }
11423
11882
  }
11424
- customElements.define('pc-script', ScriptElement);
11883
+ customElements.define('pc-script-instance', ScriptInstanceElement);
11425
11884
 
11426
11885
  /**
11427
- * Attributes on `pc-script` that never map to script attributes: the element's own API (derived
11886
+ * Attributes on `pc-script-instance` that never map to script attributes: the element's own API (derived
11428
11887
  * from its observed attributes) plus reserved and global HTML attribute names.
11429
11888
  */
11430
11889
  const RESERVED_ATTRIBUTES = new Set([
11431
- ...ScriptElement.observedAttributes,
11890
+ ...ScriptInstanceElement.observedAttributes,
11432
11891
  'accesskey',
11433
11892
  'autocapitalize',
11434
11893
  'autofocus',
@@ -11459,7 +11918,7 @@
11459
11918
  'translate'
11460
11919
  ]);
11461
11920
  /**
11462
- * Checks whether a `pc-script` attribute name is reserved (and so never maps to a script
11921
+ * Checks whether a `pc-script-instance` attribute name is reserved (and so never maps to a script
11463
11922
  * attribute). Reserved names are the element's own API, global HTML attribute names, `data-*`
11464
11923
  * and `aria-*` attributes, names starting with `_` (framework-stamped attributes), and real
11465
11924
  * inline event handler names (`onclick` etc. — detected via the platform, so script attributes
@@ -11615,10 +12074,15 @@
11615
12074
  };
11616
12075
  /**
11617
12076
  * The ScriptComponentElement interface provides properties and methods for manipulating
11618
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scripts/ | `<pc-scripts>`} elements.
12077
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-script/ | `<pc-script>`} elements.
11619
12078
  * The ScriptComponentElement interface also inherits the properties and methods of the
11620
12079
  * {@link HTMLElement} interface.
11621
12080
  *
12081
+ * Engine component: {@link ScriptComponent} (`script`).
12082
+ *
12083
+ * @elementSummary The `<pc-script>` element holds the `<pc-script-instance>` children that attach
12084
+ * scripts to its entity. Must be a child of a `<pc-entity>`, `<pc-model>` or `<pc-node>`.
12085
+ *
11622
12086
  * @category Components
11623
12087
  */
11624
12088
  class ScriptComponentElement extends ComponentElement {
@@ -11635,14 +12099,23 @@
11635
12099
  }
11636
12100
  connectedCallback() {
11637
12101
  // (Re-)observe on every connection - disconnectedCallback disconnects the observer.
11638
- // Attribute changes on child pc-script elements are watched here too: per-property
12102
+ // Attribute changes on child pc-script-instance elements are watched here too: per-property
11639
12103
  // script attributes are not statically known, so they cannot use observedAttributes.
11640
12104
  this.observer.observe(this, { childList: true, subtree: true, attributes: true });
11641
12105
  return super.connectedCallback();
11642
12106
  }
11643
12107
  initComponent() {
11644
- // Handle initial script elements
11645
- this.querySelectorAll(':scope > pc-script').forEach((scriptElement) => {
12108
+ this.querySelectorAll(':scope > pc-script-instance').forEach((scriptElement) => {
12109
+ // A host readiness cycle re-runs this against a component that can have survived it
12110
+ // (a pc-model reloading content on its stable host entity). The engine rejects a
12111
+ // duplicate create - returning null, silently in production builds - which would
12112
+ // skip attribute application entirely. A surviving instance is re-asserted instead,
12113
+ // so both cycle outcomes leave the component reflecting the element's declared state.
12114
+ const script = this.scriptFor(scriptElement);
12115
+ if (script) {
12116
+ this.applyDeclaredState(script, scriptElement);
12117
+ return;
12118
+ }
11646
12119
  this.createScript(scriptElement);
11647
12120
  });
11648
12121
  }
@@ -11776,9 +12249,9 @@
11776
12249
  this.mergeDeep(script, converted);
11777
12250
  }
11778
12251
  /**
11779
- * Returns the camelCase keys of the per-property attributes present on a `pc-script`
12252
+ * Returns the camelCase keys of the per-property attributes present on a `pc-script-instance`
11780
12253
  * element.
11781
- * @param scriptElement - The `pc-script` element.
12254
+ * @param scriptElement - The `pc-script-instance` element.
11782
12255
  * @returns The camelCase keys.
11783
12256
  */
11784
12257
  inlineKeys(scriptElement) {
@@ -11791,10 +12264,10 @@
11791
12264
  return keys;
11792
12265
  }
11793
12266
  /**
11794
- * Resolves the script instance owned by a `pc-script` element. Returns `null` when the
12267
+ * Resolves the script instance owned by a `pc-script-instance` element. Returns `null` when the
11795
12268
  * element has no created script, or when its name resolves to a script created by a
11796
12269
  * different element (e.g. a duplicate-named sibling).
11797
- * @param scriptElement - The `pc-script` element.
12270
+ * @param scriptElement - The `pc-script-instance` element.
11798
12271
  * @returns The owned script, or `null`.
11799
12272
  */
11800
12273
  scriptFor(scriptElement) {
@@ -11823,7 +12296,7 @@
11823
12296
  }
11824
12297
  }
11825
12298
  /**
11826
- * Handles a runtime `name` change on a child `pc-script`, swapping the engine script instance
12299
+ * Handles a runtime `name` change on a child `pc-script-instance`, swapping the engine script instance
11827
12300
  * to match. Without this the element would keep pointing at the old-name instance: the old
11828
12301
  * script would go on running while every subsequent update (attribute changes, enable
11829
12302
  * changes, destruction on removal) resolved the new name and silently no-opped.
@@ -11834,8 +12307,8 @@
11834
12307
  */
11835
12308
  handleScriptNameChange(event) {
11836
12309
  const scriptElement = event.target;
11837
- // Only direct children are managed, matching initComponent's ':scope > pc-script'
11838
- // contract - the event bubbles, so a deeper pc-script must not be created here
12310
+ // Only direct children are managed, matching initComponent's ':scope > pc-script-instance'
12311
+ // contract - the event bubbles, so a deeper pc-script-instance must not be created here
11839
12312
  if (scriptElement.parentElement !== this)
11840
12313
  return;
11841
12314
  // Before the component exists there is nothing to swap: initComponent creates from
@@ -11852,11 +12325,11 @@
11852
12325
  this.createScript(scriptElement);
11853
12326
  }
11854
12327
  /**
11855
- * Creates the script instance for a `pc-script` element. The instance is created disabled,
12328
+ * Creates the script instance for a `pc-script-instance` element. The instance is created disabled,
11856
12329
  * the element's converted attributes are merged over the instance's defaults (which is what
11857
12330
  * allows plain numeric arrays to be typed against those defaults), and only then is the
11858
12331
  * declared enabled state applied — so `initialize()` runs with every attribute in place.
11859
- * @param scriptElement - The `pc-script` element to create the script instance for.
12332
+ * @param scriptElement - The `pc-script-instance` element to create the script instance for.
11860
12333
  * @returns The created script, or `null`.
11861
12334
  */
11862
12335
  createScript(scriptElement) {
@@ -11867,21 +12340,30 @@
11867
12340
  if (!script)
11868
12341
  return null;
11869
12342
  scriptElement._script = script;
11870
- // The JSON blob first with per-property-shadowed keys stripped, then the per-property
11871
- // attributes: each property is written exactly once and individual attributes win
12343
+ this.applyDeclaredState(script, scriptElement);
12344
+ scriptElement._onScriptCreated();
12345
+ return script;
12346
+ }
12347
+ /**
12348
+ * Applies a `pc-script-instance` element's declared state to a script instance: the `attributes` JSON
12349
+ * first with per-property-shadowed keys stripped, then the per-property attributes — each
12350
+ * property is written exactly once and individual attributes win — and finally the declared
12351
+ * enabled state, so `initialize()` runs with every attribute in place.
12352
+ * @param script - The script instance.
12353
+ * @param scriptElement - The `pc-script-instance` element holding the declared state.
12354
+ */
12355
+ applyDeclaredState(script, scriptElement) {
11872
12356
  this.applyAttributes(script, scriptElement.scriptAttributes, this.inlineKeys(scriptElement));
11873
12357
  this.applyInlineAttributes(script, scriptElement);
11874
12358
  script.enabled = scriptElement.enabled;
11875
- scriptElement._onScriptCreated();
11876
- return script;
11877
12359
  }
11878
12360
  /**
11879
- * Applies the per-property attributes present on a `pc-script` element — any attribute that
12361
+ * Applies the per-property attributes present on a `pc-script-instance` element — any attribute that
11880
12362
  * is not part of the element's own API or a reserved HTML attribute name. These are applied
11881
12363
  * after the `attributes` JSON, so an individual attribute always takes precedence over the
11882
12364
  * blob.
11883
12365
  * @param script - The script to apply the attributes to.
11884
- * @param scriptElement - The `pc-script` element holding the attributes.
12366
+ * @param scriptElement - The `pc-script-instance` element holding the attributes.
11885
12367
  */
11886
12368
  applyInlineAttributes(script, scriptElement) {
11887
12369
  const scriptName = scriptElement.getAttribute('name') ?? '';
@@ -11892,10 +12374,10 @@
11892
12374
  }
11893
12375
  }
11894
12376
  /**
11895
- * Applies a single per-property attribute change to the script of a `pc-script` element.
12377
+ * Applies a single per-property attribute change to the script of a `pc-script-instance` element.
11896
12378
  * When the attribute has been removed, the value from the `attributes` JSON (if any) takes
11897
12379
  * effect again.
11898
- * @param scriptElement - The `pc-script` element whose attribute changed.
12380
+ * @param scriptElement - The `pc-script-instance` element whose attribute changed.
11899
12381
  * @param attributeName - The name of the changed attribute.
11900
12382
  */
11901
12383
  applyScriptProperty(scriptElement, attributeName) {
@@ -11931,7 +12413,7 @@
11931
12413
  try {
11932
12414
  const current = script[key];
11933
12415
  if (typeof current === 'function' || SCRIPT_API_MEMBERS.has(key)) {
11934
- console.warn(`Ignoring attribute '${attributeName}' on pc-script '${scriptName}' - '${key}' is part of the Script API.`);
12416
+ console.warn(`Ignoring attribute '${attributeName}' on pc-script-instance '${scriptName}' - '${key}' is part of the Script API.`);
11935
12417
  return;
11936
12418
  }
11937
12419
  if (typeof current === 'string') {
@@ -11987,10 +12469,10 @@
11987
12469
  }
11988
12470
  handleMutations(mutations) {
11989
12471
  for (const mutation of mutations) {
11990
- // Handle per-property attribute changes on child pc-script elements
12472
+ // Handle per-property attribute changes on child pc-script-instance elements
11991
12473
  if (mutation.type === 'attributes') {
11992
12474
  const target = mutation.target;
11993
- if (target instanceof ScriptElement &&
12475
+ if (target instanceof ScriptInstanceElement &&
11994
12476
  target.parentElement === this &&
11995
12477
  mutation.attributeName &&
11996
12478
  !isReservedAttribute(mutation.attributeName)) {
@@ -12000,16 +12482,16 @@
12000
12482
  }
12001
12483
  // Only direct children are managed - the observer watches the subtree for attribute
12002
12484
  // changes, but deeper childList records must not create or destroy scripts
12003
- // (matching initComponent's ':scope > pc-script' contract)
12485
+ // (matching initComponent's ':scope > pc-script-instance' contract)
12004
12486
  if (mutation.target !== this) {
12005
12487
  continue;
12006
12488
  }
12007
- // Handle removed nodes first, so that replacing a pc-script with a same-named one
12489
+ // Handle removed nodes first, so that replacing a pc-script-instance with a same-named one
12008
12490
  // destroys the old script before the replacement is created. Only destroy a script
12009
12491
  // this element actually owns - a duplicate-named element whose own create() failed
12010
12492
  // must not take down the live script on removal.
12011
12493
  mutation.removedNodes.forEach((node) => {
12012
- if (node instanceof ScriptElement) {
12494
+ if (node instanceof ScriptInstanceElement) {
12013
12495
  const scriptName = node.getAttribute('name');
12014
12496
  if (scriptName &&
12015
12497
  node._script &&
@@ -12022,7 +12504,7 @@
12022
12504
  });
12023
12505
  // Handle added nodes
12024
12506
  mutation.addedNodes.forEach((node) => {
12025
- if (node instanceof ScriptElement) {
12507
+ if (node instanceof ScriptInstanceElement) {
12026
12508
  this.createScript(node);
12027
12509
  }
12028
12510
  });
@@ -12040,14 +12522,20 @@
12040
12522
  return super.component;
12041
12523
  }
12042
12524
  }
12043
- customElements.define('pc-scripts', ScriptComponentElement);
12525
+ customElements.define('pc-script', ScriptComponentElement);
12044
12526
 
12045
12527
  /**
12046
12528
  * The SoundComponentElement interface provides properties and methods for manipulating
12047
- * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-sounds/ | `<pc-sounds>`} elements.
12529
+ * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-sound/ | `<pc-sound>`} elements.
12048
12530
  * The SoundComponentElement interface also inherits the properties and methods of the
12049
12531
  * {@link HTMLElement} interface.
12050
12532
  *
12533
+ * Engine component: {@link SoundComponent} (`sound`).
12534
+ *
12535
+ * @elementSummary The `<pc-sound>` element holds the `<pc-sound-slot>` children that play sounds at
12536
+ * its entity, along with the positional audio settings they share. Must be a child of a
12537
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
12538
+ *
12051
12539
  * @category Components
12052
12540
  */
12053
12541
  class SoundComponentElement extends ComponentElement {
@@ -12238,12 +12726,15 @@
12238
12726
  }
12239
12727
  }
12240
12728
  }
12241
- customElements.define('pc-sounds', SoundComponentElement);
12729
+ customElements.define('pc-sound', SoundComponentElement);
12242
12730
 
12243
12731
  /**
12244
12732
  * The SoundSlotElement interface provides properties and methods for manipulating
12245
- * `<pc-sound>` elements. The SoundSlotElement interface also inherits the properties and
12733
+ * `<pc-sound-slot>` elements. The SoundSlotElement interface also inherits the properties and
12246
12734
  * methods of the {@link AsyncElement} interface.
12735
+ *
12736
+ * @elementSummary The `<pc-sound-slot>` element declares one named sound on its parent `<pc-sound>`
12737
+ * — its asset, volume, pitch, looping and autoplay. Must be a direct child of `<pc-sound>`.
12247
12738
  */
12248
12739
  class SoundSlotElement extends AsyncElement {
12249
12740
  _asset = '';
@@ -12256,7 +12747,7 @@
12256
12747
  _startTime = 0;
12257
12748
  _volume = 1;
12258
12749
  /**
12259
- * The `<pc-sounds>` this slot was added to, captured at connect time.
12750
+ * The `<pc-sound>` this slot was added to, captured at connect time.
12260
12751
  *
12261
12752
  * `disconnectedCallback` cannot rediscover it: by the time the element is disconnected its
12262
12753
  * `parentElement` is already `null`, so a lookup would both fail to find the component and
@@ -12319,7 +12810,7 @@
12319
12810
  get soundElement() {
12320
12811
  const soundElement = this.parentElement;
12321
12812
  if (!(soundElement instanceof SoundComponentElement)) {
12322
- console.warn('pc-sound must be a direct child of a pc-sounds element');
12813
+ console.warn('pc-sound-slot must be a direct child of a pc-sound element');
12323
12814
  return null;
12324
12815
  }
12325
12816
  return soundElement;
@@ -12515,7 +13006,7 @@
12515
13006
  }
12516
13007
  }
12517
13008
  }
12518
- customElements.define('pc-sound', SoundSlotElement);
13009
+ customElements.define('pc-sound-slot', SoundSlotElement);
12519
13010
 
12520
13011
  /**
12521
13012
  * The GSplatComponentElement interface provides properties and methods for manipulating
@@ -12523,6 +13014,12 @@
12523
13014
  * The GSplatComponentElement interface also inherits the properties and methods of the
12524
13015
  * {@link HTMLElement} interface.
12525
13016
  *
13017
+ * Engine component: {@link GSplatComponent} (`gsplat`).
13018
+ *
13019
+ * @elementSummary The `<pc-gsplat>` element renders the 3D Gaussian splats of a `gsplat` asset at
13020
+ * its entity, with attributes for shadow casting and level of detail. Must be a child of a
13021
+ * `<pc-entity>`, `<pc-model>` or `<pc-node>`.
13022
+ *
12526
13023
  * @category Components
12527
13024
  */
12528
13025
  class GSplatComponentElement extends ComponentElement {
@@ -12818,6 +13315,11 @@
12818
13315
  * The pointer events below are dispatched by the containing `<pc-app>` element when the pointer
12819
13316
  * intersects the bound node's geometry, exactly as for `<pc-entity>`.
12820
13317
  *
13318
+ * @elementSummary The `<pc-node>` element binds to a node inside the hierarchy a `<pc-model>`
13319
+ * instantiated and declares overrides against it: a transform, an enabled state, tags, components
13320
+ * to add, or content to attach. Its `name` is a lookup, never a rename. Must be a descendant of
13321
+ * `<pc-model>`.
13322
+ *
12821
13323
  * @attribute {string} name - The name of the node to bind, resolved within the nearest ancestor
12822
13324
  * `pc-model` (or `pc-node`) once it has instantiated.
12823
13325
  * @attribute {number} index - Which match to bind when `name` matches more than one node,
@@ -12944,7 +13446,10 @@
12944
13446
  * is retained untouched — a redundant edit must not flicker overrides through a revert.
12945
13447
  */
12946
13448
  _rebind() {
12947
- const hostEntity = this._host?.entity ?? null;
13449
+ // A model fronts a host entity of its own; the names this element resolves are the
13450
+ // asset's, so the search starts at the instantiated content root, not the wrapper.
13451
+ const host = this._host;
13452
+ const hostEntity = (host instanceof ModelElement ? host.contentEntity : host?.entity) ?? null;
12948
13453
  if (!hostEntity || !this._name) {
12949
13454
  // Host not instantiated (or nothing to look up yet): return to pending. An assigned
12950
13455
  // name arriving later, or the host's next cycle, resolves it.
@@ -13041,8 +13546,9 @@
13041
13546
  }
13042
13547
  this._revertOverrides();
13043
13548
  // Attachment points anchor to the bound node, so they cannot outlive the binding. Each
13044
- // destroyed entity resets its element, which the next _buildChildren re-creates.
13045
- this.querySelectorAll('pc-entity').forEach((child) => {
13549
+ // destroyed entity resets its element, which the next _buildChildren re-creates - a
13550
+ // model host among them re-instantiates its content when it rebuilds.
13551
+ this.querySelectorAll('pc-entity, pc-model').forEach((child) => {
13046
13552
  if (child.closestEntity === this) {
13047
13553
  child.entity?.destroy();
13048
13554
  }
@@ -13554,6 +14060,9 @@
13554
14060
  * {@link https://developer.playcanvas.com/user-manual/web-components/tags/pc-scene/ | `<pc-scene>`} elements.
13555
14061
  * The SceneElement interface also inherits the properties and methods of the
13556
14062
  * {@link HTMLElement} interface.
14063
+ *
14064
+ * @elementSummary The `<pc-scene>` element holds the entity hierarchy the application renders,
14065
+ * along with the scene-wide fog and gravity settings. Must be a direct child of `<pc-app>`.
13557
14066
  */
13558
14067
  class SceneElement extends AsyncElement {
13559
14068
  /**
@@ -13777,6 +14286,10 @@
13777
14286
  * The SkyElement interface provides properties and methods for manipulating
13778
14287
  * `<pc-sky>` elements. The SkyElement interface also inherits the properties and
13779
14288
  * methods of the {@link HTMLElement} interface.
14289
+ *
14290
+ * @elementSummary The `<pc-sky>` element renders a skybox from a texture asset, projected as an
14291
+ * infinite background, a box or a dome, and optionally lights the scene from it. Must be a direct
14292
+ * child of `<pc-scene>`.
13780
14293
  */
13781
14294
  class SkyElement extends AsyncElement {
13782
14295
  _asset = '';
@@ -14073,6 +14586,7 @@
14073
14586
  exports.AppElement = AppElement;
14074
14587
  exports.AssetElement = AssetElement;
14075
14588
  exports.AsyncElement = AsyncElement;
14589
+ exports.AudioListenerComponentElement = AudioListenerComponentElement;
14076
14590
  exports.ButtonComponentElement = ButtonComponentElement;
14077
14591
  exports.CameraComponentElement = CameraComponentElement;
14078
14592
  exports.CollisionComponentElement = CollisionComponentElement;
@@ -14080,15 +14594,14 @@
14080
14594
  exports.ElementComponentElement = ElementComponentElement;
14081
14595
  exports.EntityBaseElement = EntityBaseElement;
14082
14596
  exports.EntityElement = EntityElement;
14597
+ exports.EntityOwnerElement = EntityOwnerElement;
14083
14598
  exports.GSplatComponentElement = GSplatComponentElement;
14084
14599
  exports.JointComponentElement = JointComponentElement;
14085
14600
  exports.LayoutChildComponentElement = LayoutChildComponentElement;
14086
14601
  exports.LayoutGroupComponentElement = LayoutGroupComponentElement;
14087
14602
  exports.LightComponentElement = LightComponentElement;
14088
- exports.ListenerComponentElement = ListenerComponentElement;
14089
14603
  exports.MaterialElement = MaterialElement;
14090
14604
  exports.ModelElement = ModelElement;
14091
- exports.ModuleElement = ModuleElement;
14092
14605
  exports.NodeElement = NodeElement;
14093
14606
  exports.ParticleSystemComponentElement = ParticleSystemComponentElement;
14094
14607
  exports.RenderComponentElement = RenderComponentElement;
@@ -14096,12 +14609,13 @@
14096
14609
  exports.SceneElement = SceneElement;
14097
14610
  exports.ScreenComponentElement = ScreenComponentElement;
14098
14611
  exports.ScriptComponentElement = ScriptComponentElement;
14099
- exports.ScriptElement = ScriptElement;
14612
+ exports.ScriptInstanceElement = ScriptInstanceElement;
14100
14613
  exports.ScrollViewComponentElement = ScrollViewComponentElement;
14101
14614
  exports.ScrollbarComponentElement = ScrollbarComponentElement;
14102
14615
  exports.SkyElement = SkyElement;
14103
14616
  exports.SoundComponentElement = SoundComponentElement;
14104
14617
  exports.SoundSlotElement = SoundSlotElement;
14618
+ exports.WasmElement = WasmElement;
14105
14619
  exports.whenReady = whenReady;
14106
14620
 
14107
14621
  }));