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