lunchboxjs 0.2.1016 → 0.2.1018

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.
@@ -4,8 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Lunchbox = {}, global.vue, global.three));
5
5
  })(this, (function (exports, vue, THREE) { 'use strict';
6
6
 
7
- function _interopNamespace(e) {
8
- if (e && e.__esModule) return e;
7
+ function _interopNamespaceDefault(e) {
9
8
  var n = Object.create(null);
10
9
  if (e) {
11
10
  Object.keys(e).forEach(function (k) {
@@ -18,41 +17,38 @@
18
17
  }
19
18
  });
20
19
  }
21
- n["default"] = e;
20
+ n.default = e;
22
21
  return Object.freeze(n);
23
22
  }
24
23
 
25
- var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
24
+ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
26
25
 
27
26
  function find(target) {
28
- target = vue.isRef(target) ? target.value : target; // handle standard lunchbox node
29
-
27
+ target = vue.isRef(target) ? target.value : target;
28
+ // handle standard lunchbox node
30
29
  if (isLunchboxStandardNode(target)) {
31
30
  return target?.instance;
32
- } // handle component
33
-
34
-
31
+ }
32
+ // handle component
35
33
  if (isLunchboxComponent(target)) {
36
34
  return target?.$el?.instance;
37
- } // handle vnode
38
-
39
-
35
+ }
36
+ // handle vnode
40
37
  if (vue.isVNode(target)) {
41
38
  return target.el?.instance;
42
39
  }
43
-
44
40
  return null;
45
41
  }
46
42
 
47
43
  const get = (obj, path, defValue) => {
48
44
  // If path is not defined or it has false value
49
- if (!path) return undefined; // Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
45
+ if (!path) return undefined;
46
+ // Check if path is string or array. Regex : ensure that we do not have '.' and brackets.
50
47
  // Regex explained: https://regexr.com/58j0k
51
-
52
- const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g); // Find value
53
-
54
- const result = pathArray?.reduce((prevObj, key) => prevObj && prevObj[key], obj); // If found value is undefined return default value; otherwise return the value
55
-
48
+ const pathArray = Array.isArray(path) ? path : path.match(/([^[.\]])+/g);
49
+ // Find value
50
+ const result = pathArray?.reduce((prevObj, key) => prevObj && prevObj[key], obj);
51
+ // If found value is undefined return default value; otherwise return the value
56
52
  return result === undefined ? defValue : result;
57
53
  };
58
54
 
@@ -65,18 +61,15 @@
65
61
  * Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
66
62
  * Available under MIT license <https://lodash.com/license>
67
63
  */
68
-
69
64
  /** `Object#toString` result references. */
70
65
  const numberTag = '[object Number]';
71
66
  /** Used for built-in method references. */
72
-
73
67
  const objectProto = Object.prototype;
74
68
  /**
75
69
  * Used to resolve the
76
70
  * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
77
71
  * of values.
78
72
  */
79
-
80
73
  const objectToString = objectProto.toString;
81
74
  /**
82
75
  * Checks if `value` is object-like. A value is object-like if it's not `null`
@@ -102,7 +95,6 @@
102
95
  * _.isObjectLike(null);
103
96
  * // => false
104
97
  */
105
-
106
98
  function isObjectLike(value) {
107
99
  return !!value && typeof value == 'object';
108
100
  }
@@ -132,15 +124,11 @@
132
124
  * _.isNumber('3');
133
125
  * // => false
134
126
  */
135
-
136
-
137
127
  const output = function isNumber(value) {
138
128
  return typeof value == 'number' || isObjectLike(value) && objectToString.call(value) == numberTag;
139
129
  };
140
-
141
130
  return output;
142
131
  };
143
-
144
132
  const isNumber = buildIsNumber();
145
133
 
146
134
  const set = (obj, path, value) => {
@@ -153,10 +141,11 @@
153
141
  }, obj);
154
142
  };
155
143
 
144
+ // MAKE SURE THESE MATCH VALUES IN types.EventKey
156
145
  /** Type check on whether target is a Lunchbox.EventKey */
157
-
158
146
  const isEventKey = target => {
159
- return ['onClick', 'onContextMenu', 'onDoubleClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove', // 'onPointerMissed',
147
+ return ['onClick', 'onContextMenu', 'onDoubleClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove',
148
+ // 'onPointerMissed',
160
149
  // 'onUpdate',
161
150
  'onWheel'].includes(target);
162
151
  };
@@ -175,61 +164,61 @@
175
164
  };
176
165
 
177
166
  /** Create a new Lunchbox comment node. */
178
-
179
167
  function createCommentNode(options = {}) {
180
168
  const defaults = {
181
169
  text: options.text ?? ''
182
170
  };
183
- return new exports.MiniDom.RendererCommentNode({ ...defaults,
171
+ return new exports.MiniDom.RendererCommentNode({
172
+ ...defaults,
184
173
  ...options,
185
174
  metaType: 'commentMeta'
186
175
  });
187
176
  }
188
177
  /** Create a new DOM node. */
189
-
190
178
  function createDomNode(options = {}) {
191
179
  const domElement = document.createElement(options.type ?? '');
192
180
  const defaults = {
193
181
  domElement
194
182
  };
195
- const node = new exports.MiniDom.RendererDomNode({ ...defaults,
183
+ const node = new exports.MiniDom.RendererDomNode({
184
+ ...defaults,
196
185
  ...options,
197
186
  metaType: 'domMeta'
198
187
  });
199
188
  return node;
200
189
  }
201
190
  /** Create a new Lunchbox text node. */
202
-
203
191
  function createTextNode(options = {}) {
204
192
  const defaults = {
205
193
  text: options.text ?? ''
206
194
  };
207
- return new exports.MiniDom.RendererTextNode({ ...options,
195
+ return new exports.MiniDom.RendererTextNode({
196
+ ...options,
208
197
  ...defaults,
209
198
  metaType: 'textMeta'
210
199
  });
211
200
  }
212
201
  /** Create a new Lunchbox standard node. */
213
-
214
202
  function createNode(options = {}, props = {}) {
215
203
  const defaults = {
216
204
  attached: options.attached ?? [],
217
205
  attachedArray: options.attachedArray ?? {},
218
206
  instance: options.instance ?? null
219
207
  };
220
- const node = new exports.MiniDom.RendererStandardNode({ ...options,
208
+ const node = new exports.MiniDom.RendererStandardNode({
209
+ ...options,
221
210
  ...defaults,
222
211
  metaType: 'standardMeta'
223
212
  });
224
-
225
213
  if (node.type && !isLunchboxRootNode(node) && !node.instance) {
226
- node.instance = instantiateThreeObject({ ...node,
227
- props: { ...node.props,
214
+ node.instance = instantiateThreeObject({
215
+ ...node,
216
+ props: {
217
+ ...node.props,
228
218
  ...props
229
219
  }
230
220
  });
231
221
  }
232
-
233
222
  return node;
234
223
  }
235
224
 
@@ -244,14 +233,12 @@
244
233
  if (!node.eventListeners[key]) {
245
234
  node.eventListeners[key] = [];
246
235
  }
247
-
248
236
  if (!node.eventListenerRemoveFunctions[key]) {
249
237
  node.eventListenerRemoveFunctions[key] = [];
250
- } // add event listener
251
-
252
-
253
- node.eventListeners[key].push(value); // if we need it, let's get/create the main raycaster
254
-
238
+ }
239
+ // add event listener
240
+ node.eventListeners[key].push(value);
241
+ // if we need it, let's get/create the main raycaster
255
242
  if (interactionsRequiringRaycaster.includes(key)) {
256
243
  if (node.instance && !interactables.value.includes(node)) {
257
244
  // add to interactables
@@ -259,27 +246,25 @@
259
246
  node.eventListenerRemoveFunctions[key].push(() => {
260
247
  // remove from interactables
261
248
  const idx = interactables.value.indexOf(node);
262
-
263
249
  if (idx !== -1) {
264
250
  interactables.value.splice(idx, 1);
265
251
  }
266
252
  });
267
253
  }
268
254
  }
269
-
270
255
  return node;
271
256
  }
272
- const interactionsRequiringRaycaster = ['onClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove' // 'onPointerMissed',
257
+ const interactionsRequiringRaycaster = ['onClick', 'onPointerUp', 'onPointerDown', 'onPointerOver', 'onPointerOut', 'onPointerEnter', 'onPointerLeave', 'onPointerMove'
258
+ // 'onPointerMissed',
273
259
  ];
274
260
 
275
261
  const resizeCanvas = (camera, renderer, scene, width, height) => {
276
262
  // ignore if no element
277
263
  if (!renderer?.domElement || !scene || !camera) return;
278
264
  width = width ?? window.innerWidth;
279
- height = height ?? window.innerHeight; // update camera
280
-
265
+ height = height ?? window.innerHeight;
266
+ // update camera
281
267
  const aspect = width / height;
282
-
283
268
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
284
269
  const perspectiveCamera = camera;
285
270
  perspectiveCamera.aspect = aspect;
@@ -293,11 +278,10 @@
293
278
  orthoCamera.right = 10;
294
279
  orthoCamera.left = -10;
295
280
  orthoCamera.updateProjectionMatrix();
296
- } else ; // update canvas
297
-
298
-
299
- renderer.setSize(width, height); // render immediately so there's no flicker
300
-
281
+ } else ;
282
+ // update canvas
283
+ renderer.setSize(width, height);
284
+ // render immediately so there's no flicker
301
285
  if (scene && camera) {
302
286
  renderer.render(vue.toRaw(scene), vue.toRaw(camera));
303
287
  }
@@ -312,42 +296,37 @@
312
296
  height
313
297
  };
314
298
  };
315
-
316
299
  const prepCanvas = (container, camera, renderer, scene, sizePolicy) => {
317
300
  const containerElement = container.value?.domElement;
318
- if (!containerElement) throw new Error('missing container'); // save and size element
319
-
301
+ if (!containerElement) throw new Error('missing container');
302
+ // save and size element
320
303
  const resizeCanvasByPolicy = () => {
321
304
  if (sizePolicy === 'container') {
322
305
  const dims = getInnerDimensions(containerElement);
323
306
  resizeCanvas(camera, renderer, scene, dims.width, dims.height);
324
307
  } else resizeCanvas(camera, renderer, scene);
325
308
  };
326
-
327
- resizeCanvasByPolicy(); // attach listeners
328
-
309
+ resizeCanvasByPolicy();
310
+ // attach listeners
329
311
  let observer = new ResizeObserver(() => {
330
312
  resizeCanvasByPolicy();
331
- }); // window.addEventListener('resize', resizeCanvas)
332
-
313
+ });
314
+ // window.addEventListener('resize', resizeCanvas)
333
315
  if (containerElement) {
334
316
  observer.observe(containerElement);
335
- } // cleanup
336
-
337
-
317
+ }
318
+ // cleanup
338
319
  return {
339
320
  dispose() {
340
321
  if (containerElement) {
341
322
  observer.unobserve(containerElement);
342
323
  }
343
324
  }
344
-
345
325
  };
346
326
  };
347
327
 
348
328
  const LunchboxScene = vue.defineComponent({
349
329
  name: 'LunchboxScene',
350
-
351
330
  setup(props, {
352
331
  slots
353
332
  }) {
@@ -355,12 +334,10 @@
355
334
  default: () => [slots.default?.()]
356
335
  });
357
336
  }
358
-
359
337
  });
360
338
 
361
339
  const LunchboxEventHandlers = vue.defineComponent({
362
340
  name: 'LunchboxEventHandlers',
363
-
364
341
  setup() {
365
342
  const interactables = useLunchboxInteractables();
366
343
  const globals = useGlobals();
@@ -371,7 +348,6 @@
371
348
  const inputActive = vue.ref(false);
372
349
  let currentIntersections = [];
373
350
  const raycaster = new THREE__namespace.Raycaster(new THREE__namespace.Vector3(), new THREE__namespace.Vector3(0, 0, -1));
374
-
375
351
  const fireEventsFromIntersections = ({
376
352
  element,
377
353
  eventKeys,
@@ -387,52 +363,44 @@
387
363
  });
388
364
  }
389
365
  });
390
- }; // add mouse listener to renderer DOM element when the element is ready
391
-
392
-
366
+ };
367
+ // add mouse listener to renderer DOM element when the element is ready
393
368
  onRendererReady(v => {
394
- if (!v?.domElement) return; // we have a DOM element, so let's add mouse listeners
395
-
369
+ if (!v?.domElement) return;
370
+ // we have a DOM element, so let's add mouse listeners
396
371
  const {
397
372
  domElement
398
373
  } = v;
399
-
400
374
  const mouseMoveListener = evt => {
401
375
  const screenWidth = (domElement.width ?? 1) / globals.dpr;
402
376
  const screenHeight = (domElement.height ?? 1) / globals.dpr;
403
377
  mousePos.value.x = evt.offsetX / screenWidth * 2 - 1;
404
378
  mousePos.value.y = -(evt.offsetY / screenHeight) * 2 + 1;
405
379
  };
406
-
407
380
  const mouseDownListener = () => inputActive.value = true;
408
-
409
- const mouseUpListener = () => inputActive.value = false; // add mouse events
410
-
411
-
381
+ const mouseUpListener = () => inputActive.value = false;
382
+ // add mouse events
412
383
  domElement.addEventListener('pointermove', mouseMoveListener);
413
384
  domElement.addEventListener('pointerdown', mouseDownListener);
414
385
  domElement.addEventListener('pointerup', mouseUpListener);
415
386
  });
416
387
  const camera = useCamera();
417
-
418
388
  const update = () => {
419
389
  const c = camera.value;
420
- if (!c) return; // console.log(camera.value)
421
-
390
+ if (!c) return;
391
+ // console.log(camera.value)
422
392
  raycaster.setFromCamera(mousePos.value, c);
423
393
  const intersections = raycaster.intersectObjects(interactables?.value.map(v => v.instance) ?? []);
424
394
  let leaveValues = [],
425
- entering = [],
426
- staying = []; // intersection arrays
427
-
428
- leaveValues = currentIntersections.map(v => v.intersection); // element arrays
429
-
395
+ entering = [],
396
+ staying = [];
397
+ // intersection arrays
398
+ leaveValues = currentIntersections.map(v => v.intersection);
399
+ // element arrays
430
400
  intersections?.forEach(intersection => {
431
401
  const currentIdx = currentIntersections.findIndex(v => v.intersection.object === intersection.object);
432
-
433
402
  if (currentIdx === -1) {
434
403
  const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
435
-
436
404
  if (found) {
437
405
  entering.push({
438
406
  element: found,
@@ -441,18 +409,15 @@
441
409
  }
442
410
  } else {
443
411
  const found = interactables?.value.find(v => v.instance?.uuid === intersection.object.uuid);
444
-
445
412
  if (found) {
446
413
  staying.push({
447
414
  element: found,
448
415
  intersection
449
416
  });
450
417
  }
451
- } // this is a current intersection, so it won't be in our `leave` array
452
-
453
-
418
+ }
419
+ // this is a current intersection, so it won't be in our `leave` array
454
420
  const leaveIdx = leaveValues.findIndex(v => v.object.uuid === intersection.object.uuid);
455
-
456
421
  if (leaveIdx !== -1) {
457
422
  leaveValues.splice(leaveIdx, 1);
458
423
  }
@@ -462,8 +427,8 @@
462
427
  element: interactables?.value.find(interactable => interactable.instance?.uuid === intersection.object.uuid),
463
428
  intersection
464
429
  };
465
- }); // new interactions
466
-
430
+ });
431
+ // new interactions
467
432
  entering.forEach(({
468
433
  element,
469
434
  intersection
@@ -473,8 +438,8 @@
473
438
  eventKeys: ['onPointerEnter'],
474
439
  intersection
475
440
  });
476
- }); // unchanged interactions
477
-
441
+ });
442
+ // unchanged interactions
478
443
  staying.forEach(({
479
444
  element,
480
445
  intersection
@@ -485,8 +450,8 @@
485
450
  eventKeys,
486
451
  intersection
487
452
  });
488
- }); // exited interactions
489
-
453
+ });
454
+ // exited interactions
490
455
  leaving.forEach(({
491
456
  element,
492
457
  intersection
@@ -499,28 +464,24 @@
499
464
  });
500
465
  });
501
466
  currentIntersections = [].concat(entering, staying);
502
- }; // update function
503
-
504
-
467
+ };
468
+ // update function
505
469
  onBeforeRender(update);
506
-
507
470
  const teardown = () => offBeforeRender(update);
508
-
509
471
  vue.onBeforeUnmount(teardown);
510
472
  const clickEventKeys = ['onClick', 'onPointerDown', 'onPointerUp'];
511
473
  vue.watch(inputActive, isDown => {
512
474
  // run raycaster on click (necessary when `update` is not automatically called,
513
475
  // for example in `updateSource` functions)
514
- update(); // meshes with multiple intersections receive multiple callbacks by default -
476
+ update();
477
+ // meshes with multiple intersections receive multiple callbacks by default -
515
478
  // let's make it so they only receive one callback of each type per frame.
516
479
  // (ie usually when you click on a mesh, you expect only one click event to fire, even
517
480
  // if there are technically multiple intersections with that mesh)
518
-
519
481
  const uuidsInteractedWithThisFrame = [];
520
482
  currentIntersections.forEach(v => {
521
483
  clickEventKeys.forEach(key => {
522
484
  const id = v.element.uuid + key;
523
-
524
485
  if (isDown && (key === 'onClick' || key === 'onPointerDown')) {
525
486
  if (!uuidsInteractedWithThisFrame.includes(id)) {
526
487
  v.element.eventListeners[key]?.forEach(cb => cb({
@@ -538,16 +499,14 @@
538
499
  }
539
500
  });
540
501
  });
541
- }); // return arbitrary object to ensure instantiation
502
+ });
503
+ // return arbitrary object to ensure instantiation
542
504
  // TODO: why can't we return a <raycaster/> here?
543
-
544
505
  return () => vue.createVNode(vue.resolveComponent("object3D"), null, null);
545
506
  }
546
-
547
507
  });
548
508
 
549
509
  /** fixed & fill styling for container */
550
-
551
510
  const fillStyle = position => {
552
511
  return {
553
512
  position,
@@ -560,7 +519,6 @@
560
519
  display: 'block'
561
520
  };
562
521
  };
563
-
564
522
  const LunchboxWrapper = vue.defineComponent({
565
523
  name: 'Lunchbox',
566
524
  props: {
@@ -582,7 +540,6 @@
582
540
  zoom: Number,
583
541
  updateSource: Object
584
542
  },
585
-
586
543
  setup(props, context) {
587
544
  const canvas = vue.ref();
588
545
  let dpr = props.dpr ?? -1;
@@ -594,101 +551,87 @@
594
551
  const updateGlobals = useUpdateGlobals();
595
552
  const app = useApp();
596
553
  const consolidatedCameraProperties = vue.reactive({});
597
- const startCallbacks = useStartCallbacks(); // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
598
-
554
+ const startCallbacks = useStartCallbacks();
555
+ // https://threejs.org/docs/index.html#manual/en/introduction/Color-management
599
556
  if (props.r3f && THREE__namespace?.ColorManagement) {
600
557
  THREE__namespace.ColorManagement.legacyMode = false;
601
558
  }
602
-
603
- const interactables = useLunchboxInteractables(); // MOUNT
559
+ const interactables = useLunchboxInteractables();
560
+ // MOUNT
604
561
  // ====================
605
-
606
562
  vue.onMounted(async () => {
607
563
  // canvas needs to exist (or user needs to handle it on their own)
608
- if (!canvas.value && !context.slots?.renderer?.()?.length) throw new Error('missing canvas'); // no camera provided, so let's create one
609
-
564
+ if (!canvas.value && !context.slots?.renderer?.()?.length) throw new Error('missing canvas');
565
+ // no camera provided, so let's create one
610
566
  if (!context.slots?.camera?.()?.length) {
611
567
  if (props.cameraPosition) {
612
568
  consolidatedCameraProperties.position = props.cameraPosition;
613
569
  }
614
-
615
570
  if (props.cameraLook || props.cameraLookAt) {
616
571
  consolidatedCameraProperties.lookAt = props.cameraLook || props.cameraLookAt;
617
572
  }
618
-
619
573
  if (props.zoom !== undefined) {
620
574
  consolidatedCameraProperties.zoom = props.zoom;
621
575
  }
622
- } // SCENE
576
+ }
577
+ // SCENE
623
578
  // ====================
624
579
  // set background color
625
-
626
-
627
580
  if (scene.value?.$el?.instance && props.background) {
628
581
  scene.value.$el.instance.background = new THREE__namespace.Color(props.background);
629
- } // MISC PROPERTIES
582
+ }
583
+ // MISC PROPERTIES
630
584
  // ====================
631
-
632
-
633
585
  if (dpr === -1) {
634
586
  dpr = window.devicePixelRatio;
635
587
  }
636
-
637
588
  updateGlobals?.({
638
589
  dpr
639
590
  });
640
-
641
- while (!renderer.value?.$el?.instance && // TODO: remove `as any`
591
+ while (!renderer.value?.$el?.instance &&
592
+ // TODO: remove `as any`
642
593
  !renderer.value?.component?.ctx.$el?.instance) {
643
594
  await new Promise(r => requestAnimationFrame(r));
644
595
  }
645
-
646
- while (!scene.value?.$el?.instance && // TODO: remove `as any`
596
+ while (!scene.value?.$el?.instance &&
597
+ // TODO: remove `as any`
647
598
  !scene.value?.component?.ctx.$el?.instance) {
648
599
  await new Promise(r => requestAnimationFrame(r));
649
600
  }
650
-
651
601
  const normalizedRenderer = renderer.value?.$el?.instance ?? renderer.value?.component?.ctx.$el?.instance;
652
602
  normalizedRenderer.setPixelRatio(globals.dpr);
653
603
  const normalizedScene = scene.value?.$el?.instance ?? scene.value?.component?.ctx.$el?.instance;
654
- const normalizedCamera = camera.value?.$el?.instance ?? camera.value?.component?.ctx.$el?.instance; // TODO: update DPR on monitor switch
604
+ const normalizedCamera = camera.value?.$el?.instance ?? camera.value?.component?.ctx.$el?.instance;
605
+ // TODO: update DPR on monitor switch
655
606
  // prep canvas (sizing, observe, unmount, etc)
656
607
  // (only run if no custom renderer)
657
-
658
608
  if (!context.slots?.renderer?.()?.length) {
659
609
  // TODO: use dispose
660
610
  prepCanvas(container, normalizedCamera, normalizedRenderer, normalizedScene, props.sizePolicy);
661
-
662
611
  if (props.r3f) {
663
612
  normalizedRenderer.outputEncoding = THREE__namespace.sRGBEncoding;
664
613
  normalizedRenderer.toneMapping = THREE__namespace.ACESFilmicToneMapping;
665
- } // update render sugar
666
-
667
-
614
+ }
615
+ // update render sugar
668
616
  const sugar = {
669
617
  shadow: props.shadow
670
618
  };
671
-
672
619
  if (sugar?.shadow) {
673
620
  normalizedRenderer.shadowMap.enabled = true;
674
-
675
621
  if (typeof sugar.shadow === 'object') {
676
622
  normalizedRenderer.shadowMap.type = sugar.shadow.type;
677
623
  }
678
624
  }
679
- } // START
625
+ }
626
+ // START
680
627
  // ====================
681
-
682
-
683
628
  if (!app) {
684
629
  throw new Error('error creating app');
685
- } // save renderer, scene, camera
686
-
687
-
630
+ }
631
+ // save renderer, scene, camera
688
632
  app.config.globalProperties.lunchbox.camera = normalizedCamera;
689
633
  app.config.globalProperties.lunchbox.renderer = normalizedRenderer;
690
634
  app.config.globalProperties.lunchbox.scene = normalizedScene;
691
-
692
635
  for (let startCallback of startCallbacks ?? []) {
693
636
  startCallback({
694
637
  app,
@@ -696,10 +639,9 @@
696
639
  renderer: normalizedRenderer,
697
640
  scene: normalizedScene
698
641
  });
699
- } // KICK UPDATE
642
+ }
643
+ // KICK UPDATE
700
644
  // ====================
701
-
702
-
703
645
  update({
704
646
  app,
705
647
  camera: normalizedCamera,
@@ -707,36 +649,35 @@
707
649
  scene: normalizedScene,
708
650
  updateSource: props.updateSource
709
651
  });
710
- }); // UNMOUNT
652
+ });
653
+ // UNMOUNT
711
654
  // ====================
712
-
713
655
  vue.onBeforeUnmount(() => {
714
656
  cancelUpdate();
715
657
  cancelUpdateSource();
716
- }); // RENDER FUNCTION
658
+ });
659
+ // RENDER FUNCTION
717
660
  // ====================
718
-
719
661
  const containerFillStyle = props.sizePolicy === 'container' ? 'static' : 'absolute';
720
- const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed'; // REACTIVE CUSTOM CAMERAS
662
+ const canvasFillStyle = props.sizePolicy === 'container' ? 'static' : 'fixed';
663
+ // REACTIVE CUSTOM CAMERAS
721
664
  // ====================
722
665
  // find first camera with `type.name` property
723
666
  // (which indicates a Lunch.Node)
724
-
725
667
  const activeCamera = vue.computed(() => {
726
668
  const output = context.slots?.camera?.().find(c => c.type?.name);
727
-
728
669
  if (output) {
729
670
  return output;
730
671
  }
731
-
732
672
  return output;
733
- }); // TODO: make custom cameras reactive
734
-
673
+ });
674
+ // TODO: make custom cameras reactive
735
675
  vue.watch(activeCamera, async (newVal, oldVal) => {
736
676
  // console.log('got camera', newVal)
737
677
  if (newVal && newVal?.props?.key !== oldVal?.props?.key) {
738
678
  // TODO: remove cast
739
- camera.value = newVal; // TODO: why isn't this updating app camera?
679
+ camera.value = newVal;
680
+ // TODO: why isn't this updating app camera?
740
681
  // const el = await waitFor(() => newVal.el)
741
682
  // console.log(el)
742
683
  // camera.value = el
@@ -745,10 +686,11 @@
745
686
  }
746
687
  }, {
747
688
  immediate: true
748
- }); // RENDER FUNCTION
689
+ });
690
+ // RENDER FUNCTION
749
691
  // ====================
750
-
751
- return () => vue.createVNode(vue.Fragment, null, [context.slots?.renderer?.()?.length ? // TODO: remove `as any` cast
692
+ return () => vue.createVNode(vue.Fragment, null, [context.slots?.renderer?.()?.length ?
693
+ // TODO: remove `as any` cast
752
694
  renderer.value = context.slots?.renderer?.()[0] : // ...otherwise, add canvas...
753
695
  vue.createVNode(vue.Fragment, null, [vue.createVNode("div", {
754
696
  "class": "lunchbox-container",
@@ -769,14 +711,16 @@
769
711
  powerPreference: !!props.r3f ? 'high-performance' : 'default',
770
712
  ...(props.rendererArguments ?? {})
771
713
  }]
772
- }), null)]), context.slots?.scene?.()?.length ? // TODO: remove `as any` cast
714
+ }), null)]), context.slots?.scene?.()?.length ?
715
+ // TODO: remove `as any` cast
773
716
  scene.value = context.slots?.scene?.()[0] : // ...otherwise, add default scene
774
717
  // TODO: why does this need to be a separate component? <scene> throws an error
775
718
  vue.createVNode(LunchboxScene, {
776
719
  "ref": scene
777
720
  }, {
778
721
  default: () => [context.slots?.default?.()]
779
- }), context.slots?.camera?.()?.length ? // TODO: remove `any` cast
722
+ }), context.slots?.camera?.()?.length ?
723
+ // TODO: remove `any` cast
780
724
  camera.value : props.ortho || props.orthographic ? vue.createVNode(vue.resolveComponent("orthographicCamera"), vue.mergeProps({
781
725
  "ref": camera,
782
726
  "args": props.cameraArgs ?? []
@@ -785,22 +729,32 @@
785
729
  "args": props.cameraArgs ?? [props.r3f ? 75 : 45, 0.5625, 1, 1000]
786
730
  }, consolidatedCameraProperties), null), interactables?.value.length && vue.createVNode(LunchboxEventHandlers, null, null)]);
787
731
  }
788
-
789
732
  });
790
733
 
791
734
  // list of all components to register out of the box
792
- const autoGeneratedComponents = [// ThreeJS basics
793
- 'mesh', 'instancedMesh', 'scene', 'sprite', 'object3D', // geometry
794
- 'instancedBufferGeometry', 'bufferGeometry', 'boxBufferGeometry', 'circleBufferGeometry', 'coneBufferGeometry', 'cylinderBufferGeometry', 'dodecahedronBufferGeometry', 'extrudeBufferGeometry', 'icosahedronBufferGeometry', 'latheBufferGeometry', 'octahedronBufferGeometry', 'parametricBufferGeometry', 'planeBufferGeometry', 'polyhedronBufferGeometry', 'ringBufferGeometry', 'shapeBufferGeometry', 'sphereBufferGeometry', 'tetrahedronBufferGeometry', 'textBufferGeometry', 'torusBufferGeometry', 'torusKnotBufferGeometry', 'tubeBufferGeometry', 'wireframeGeometry', 'parametricGeometry', 'tetrahedronGeometry', 'octahedronGeometry', 'icosahedronGeometry', 'dodecahedronGeometry', 'polyhedronGeometry', 'tubeGeometry', 'torusKnotGeometry', 'torusGeometry', // textgeometry has been moved to /examples/jsm/geometries/TextGeometry
735
+ const autoGeneratedComponents = [
736
+ // ThreeJS basics
737
+ 'mesh', 'instancedMesh', 'scene', 'sprite', 'object3D',
738
+ // geometry
739
+ 'instancedBufferGeometry', 'bufferGeometry', 'boxBufferGeometry', 'circleBufferGeometry', 'coneBufferGeometry', 'cylinderBufferGeometry', 'dodecahedronBufferGeometry', 'extrudeBufferGeometry', 'icosahedronBufferGeometry', 'latheBufferGeometry', 'octahedronBufferGeometry', 'parametricBufferGeometry', 'planeBufferGeometry', 'polyhedronBufferGeometry', 'ringBufferGeometry', 'shapeBufferGeometry', 'sphereBufferGeometry', 'tetrahedronBufferGeometry', 'textBufferGeometry', 'torusBufferGeometry', 'torusKnotBufferGeometry', 'tubeBufferGeometry', 'wireframeGeometry', 'parametricGeometry', 'tetrahedronGeometry', 'octahedronGeometry', 'icosahedronGeometry', 'dodecahedronGeometry', 'polyhedronGeometry', 'tubeGeometry', 'torusKnotGeometry', 'torusGeometry',
740
+ // textgeometry has been moved to /examples/jsm/geometries/TextGeometry
795
741
  // 'textGeometry',
796
- 'sphereGeometry', 'ringGeometry', 'planeGeometry', 'latheGeometry', 'shapeGeometry', 'extrudeGeometry', 'edgesGeometry', 'coneGeometry', 'cylinderGeometry', 'circleGeometry', 'boxGeometry', // materials
797
- 'material', 'shadowMaterial', 'spriteMaterial', 'rawShaderMaterial', 'shaderMaterial', 'pointsMaterial', 'meshPhysicalMaterial', 'meshStandardMaterial', 'meshPhongMaterial', 'meshToonMaterial', 'meshNormalMaterial', 'meshLambertMaterial', 'meshDepthMaterial', 'meshDistanceMaterial', 'meshBasicMaterial', 'meshMatcapMaterial', 'lineDashedMaterial', 'lineBasicMaterial', // lights
798
- 'light', 'spotLightShadow', 'spotLight', 'pointLight', 'rectAreaLight', 'hemisphereLight', 'directionalLightShadow', 'directionalLight', 'ambientLight', 'lightShadow', 'ambientLightProbe', 'hemisphereLightProbe', 'lightProbe', // textures
799
- 'texture', 'videoTexture', 'dataTexture', 'dataTexture3D', 'compressedTexture', 'cubeTexture', 'canvasTexture', 'depthTexture', // Texture loaders
800
- 'textureLoader', // misc
801
- 'group', 'catmullRomCurve3', 'points', 'raycaster', // helpers
802
- 'cameraHelper', // cameras
803
- 'camera', 'perspectiveCamera', 'orthographicCamera', 'cubeCamera', 'arrayCamera', // renderers
742
+ 'sphereGeometry', 'ringGeometry', 'planeGeometry', 'latheGeometry', 'shapeGeometry', 'extrudeGeometry', 'edgesGeometry', 'coneGeometry', 'cylinderGeometry', 'circleGeometry', 'boxGeometry',
743
+ // materials
744
+ 'material', 'shadowMaterial', 'spriteMaterial', 'rawShaderMaterial', 'shaderMaterial', 'pointsMaterial', 'meshPhysicalMaterial', 'meshStandardMaterial', 'meshPhongMaterial', 'meshToonMaterial', 'meshNormalMaterial', 'meshLambertMaterial', 'meshDepthMaterial', 'meshDistanceMaterial', 'meshBasicMaterial', 'meshMatcapMaterial', 'lineDashedMaterial', 'lineBasicMaterial',
745
+ // lights
746
+ 'light', 'spotLightShadow', 'spotLight', 'pointLight', 'rectAreaLight', 'hemisphereLight', 'directionalLightShadow', 'directionalLight', 'ambientLight', 'lightShadow', 'ambientLightProbe', 'hemisphereLightProbe', 'lightProbe',
747
+ // textures
748
+ 'texture', 'videoTexture', 'dataTexture', 'dataTexture3D', 'compressedTexture', 'cubeTexture', 'canvasTexture', 'depthTexture',
749
+ // Texture loaders
750
+ 'textureLoader',
751
+ // misc
752
+ 'group', 'catmullRomCurve3', 'points', 'raycaster',
753
+ // helpers
754
+ 'cameraHelper',
755
+ // cameras
756
+ 'camera', 'perspectiveCamera', 'orthographicCamera', 'cubeCamera', 'arrayCamera',
757
+ // renderers
804
758
  'webGLRenderer'
805
759
  /*
806
760
  // List copied from r3f:
@@ -846,42 +800,36 @@
846
800
  fog: FogProps
847
801
  fogExp2: FogExp2Props
848
802
  shape: ShapeProps
849
- */
850
- ];
851
-
852
- const catalogue = {}; // component creation utility
803
+ */];
853
804
 
805
+ const catalogue = {};
806
+ // component creation utility
854
807
  const createComponent$1 = tag => vue.defineComponent({
855
808
  inheritAttrs: false,
856
809
  name: tag,
857
-
858
810
  setup(props, context) {
859
811
  return () => {
860
812
  return vue.h(tag, context.attrs, context.slots?.default?.() || []);
861
813
  };
862
814
  }
863
-
864
- }); // turn components into registered map
865
-
866
-
815
+ });
816
+ // turn components into registered map
867
817
  const processed = autoGeneratedComponents.map(createComponent$1).reduce((acc, curr) => {
868
818
  acc[curr.name] = curr;
869
819
  return acc;
870
820
  }, {});
871
- const components = { ...processed,
821
+ const components = {
822
+ ...processed,
872
823
  Lunchbox: LunchboxWrapper
873
824
  };
874
825
 
875
826
  const createComponent = tag => vue.defineComponent({
876
827
  inheritAttrs: false,
877
828
  name: tag,
878
-
879
829
  render() {
880
830
  return vue.h(tag, this.$attrs, this.$slots?.default?.() || []);
881
831
  }
882
-
883
832
  });
884
-
885
833
  const extend = ({
886
834
  app,
887
835
  ...targets
@@ -900,14 +848,12 @@
900
848
  // return $attachedArray value if needed
901
849
  if (typeof prop === 'string' && prop.startsWith('$attachedArray')) {
902
850
  return node.attachedArray[prop.replace('$attachedArray.', '')];
903
- } // return $attached value if needed
904
-
905
-
851
+ }
852
+ // return $attached value if needed
906
853
  if (typeof prop === 'string' && prop.startsWith('$attached')) {
907
854
  return node.attached[prop.replace('$attached.', '')];
908
- } // otherwise, return plain value
909
-
910
-
855
+ }
856
+ // otherwise, return plain value
911
857
  return prop;
912
858
  }
913
859
  function processPropAsArray({
@@ -923,17 +869,17 @@
923
869
  }
924
870
 
925
871
  function instantiateThreeObject(node) {
926
- if (!node.type) return null; // what class will we be instantiating?
927
-
872
+ if (!node.type) return null;
873
+ // what class will we be instantiating?
928
874
  const uppercaseType = node.type[0].toUpperCase() + node.type.slice(1);
929
875
  const translatedType = uppercaseType.replace(/Lunchbox$/, '');
930
876
  const targetClass = catalogue[node.type] || THREE__namespace[uppercaseType] || catalogue[translatedType] || THREE__namespace[translatedType];
931
- if (!targetClass) throw `${uppercaseType} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`; // what args have we been provided?
932
-
933
- const args = node.props.args ?? []; // replace $attached values with their instances
877
+ if (!targetClass) throw `${uppercaseType} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;
878
+ // what args have we been provided?
879
+ const args = node.props.args ?? [];
880
+ // replace $attached values with their instances
934
881
  // we need to guarantee everything comes back as an array so we can spread $attachedArrays,
935
882
  // so we'll use processPropAsArray
936
-
937
883
  const argsWrappedInArrays = args.map(arg => {
938
884
  return processPropAsArray({
939
885
  node,
@@ -959,12 +905,10 @@
959
905
  // getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. Also,
960
906
  // find the complete implementation of crypto (msCrypto) on IE11.
961
907
  getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);
962
-
963
908
  if (!getRandomValues) {
964
909
  throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
965
910
  }
966
911
  }
967
-
968
912
  return getRandomValues(rnds8);
969
913
  }
970
914
 
@@ -980,15 +924,13 @@
980
924
  */
981
925
 
982
926
  var byteToHex = [];
983
-
984
927
  for (var i = 0; i < 256; ++i) {
985
928
  byteToHex.push((i + 0x100).toString(16).substr(1));
986
929
  }
987
-
988
930
  function stringify(arr) {
989
- var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; // Note: Be careful editing this code! It's been tuned for performance
931
+ var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
932
+ // Note: Be careful editing this code! It's been tuned for performance
990
933
  // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
991
-
992
934
  var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one
993
935
  // of the following:
994
936
  // - One or more input array values don't map to a hex octet (leading to
@@ -998,7 +940,6 @@
998
940
  if (!validate(uuid)) {
999
941
  throw TypeError('Stringified UUID is invalid');
1000
942
  }
1001
-
1002
943
  return uuid;
1003
944
  }
1004
945
 
@@ -1011,119 +952,97 @@
1011
952
 
1012
953
  if (buf) {
1013
954
  offset = offset || 0;
1014
-
1015
955
  for (var i = 0; i < 16; ++i) {
1016
956
  buf[offset + i] = rnds[i];
1017
957
  }
1018
-
1019
958
  return buf;
1020
959
  }
1021
-
1022
960
  return stringify(rnds);
1023
961
  }
1024
962
 
963
+ // MiniDom recreates DOM node properties and methods.
1025
964
  // Since Vue 3 is a DOM-first framework, many of its nodeOps depend on
1026
965
  // properties and methods the DOM naturally contains. MiniDom recreates
1027
966
  // those properties (as well as a few from the tree-model npm package)
1028
967
  // to make a DOM-like but otherwise agnostic hierarchy structure.
1029
-
1030
968
  exports.MiniDom = void 0;
1031
-
1032
969
  (function (MiniDom) {
1033
970
  class BaseNode {
1034
971
  constructor(options = {}, parent) {
1035
972
  this.parentNode = options?.parentNode ?? parent ?? null;
1036
973
  this.minidomType = 'MinidomBaseNode';
1037
- this.uuid = options?.uuid ?? v4(); // allNodes.push(this)
974
+ this.uuid = options?.uuid ?? v4();
975
+ // allNodes.push(this)
1038
976
  }
1039
977
 
1040
- uuid; // DOM FEATURES
978
+ uuid;
979
+ // DOM FEATURES
1041
980
  // ====================
1042
-
1043
981
  parentNode;
1044
-
1045
982
  get nextSibling() {
1046
983
  if (!this.parentNode) return null;
1047
- const idx = this.parentNode.children.findIndex(n => n.uuid === this.uuid); // return next sibling if we're present and not the last child of the parent
1048
-
984
+ const idx = this.parentNode.children.findIndex(n => n.uuid === this.uuid);
985
+ // return next sibling if we're present and not the last child of the parent
1049
986
  if (idx !== -1 && idx < this.parentNode.children.length - 1) {
1050
987
  return this.parentNode.children[idx + 1];
1051
988
  }
1052
-
1053
989
  return null;
1054
990
  }
1055
-
1056
991
  insertBefore(child, anchor) {
1057
992
  child.removeAsChildFromAnyParents();
1058
993
  child.parentNode = this;
1059
994
  const anchorIdx = this.children.findIndex(n => n.uuid === anchor?.uuid);
1060
-
1061
995
  if (anchorIdx !== -1) {
1062
996
  this.children.splice(anchorIdx, 0, child);
1063
997
  } else {
1064
998
  this.children.push(child);
1065
999
  }
1066
1000
  }
1067
-
1068
1001
  removeChild(child) {
1069
1002
  const idx = this.children.findIndex(n => n?.uuid === child?.uuid);
1070
-
1071
1003
  if (idx !== -1) {
1072
1004
  this.children.splice(idx, 1);
1073
1005
  }
1074
- } // TREE FEATURES
1006
+ }
1007
+ // TREE FEATURES
1075
1008
  // ====================
1076
-
1077
-
1078
1009
  children = [];
1079
-
1080
1010
  addChild(child) {
1081
1011
  if (child) {
1082
1012
  // remove child from any other parents
1083
- child.removeAsChildFromAnyParents(); // add to this node
1084
-
1013
+ child.removeAsChildFromAnyParents();
1014
+ // add to this node
1085
1015
  child.parentNode = this;
1086
1016
  this.insertBefore(child, null);
1087
1017
  }
1088
-
1089
1018
  return this;
1090
1019
  }
1091
1020
  /** Get the array of Nodes representing the path from the root to this Node (inclusive). */
1092
-
1093
-
1094
1021
  getPath() {
1095
1022
  const output = [];
1096
1023
  let current = this;
1097
-
1098
1024
  while (current) {
1099
1025
  output.unshift(current);
1100
1026
  current = current.parentNode;
1101
1027
  }
1102
-
1103
1028
  return output;
1104
1029
  }
1105
1030
  /** Drop this node. Removes parent's knowledge of this node
1106
1031
  * and resets this node's internal parent. */
1107
-
1108
-
1109
1032
  drop() {
1110
1033
  // remove as child
1111
- this.removeAsChildFromAnyParents(); // remove parent
1112
-
1034
+ this.removeAsChildFromAnyParents();
1035
+ // remove parent
1113
1036
  this.parentNode = null;
1114
1037
  }
1115
1038
  /** Walk over the entire subtree. Return falsey value in callback to end early. */
1116
1039
  // TODO: depth-first vs breadth-first
1117
-
1118
-
1119
1040
  walk(callback) {
1120
1041
  const queue = [this, ...this.children];
1121
1042
  const traversed = [];
1122
1043
  let canContinue = true;
1123
-
1124
1044
  while (queue.length && canContinue) {
1125
1045
  const current = queue.shift();
1126
-
1127
1046
  if (current) {
1128
1047
  if (traversed.includes(current)) continue;
1129
1048
  traversed.push(current);
@@ -1133,20 +1052,15 @@
1133
1052
  canContinue = false;
1134
1053
  }
1135
1054
  }
1136
- } // INTERNAL FEATURES
1055
+ }
1056
+ // INTERNAL FEATURES
1137
1057
  // ====================
1138
-
1139
-
1140
1058
  minidomType;
1141
-
1142
1059
  removeAsChildFromAnyParents() {
1143
1060
  this.parentNode?.removeChild(this);
1144
1061
  }
1145
-
1146
1062
  }
1147
-
1148
1063
  MiniDom.BaseNode = BaseNode;
1149
-
1150
1064
  class RendererBaseNode extends MiniDom.BaseNode {
1151
1065
  constructor(options = {}, parent) {
1152
1066
  super(options, parent);
@@ -1158,73 +1072,57 @@
1158
1072
  this.props = options.props ?? [];
1159
1073
  this.type = options.type ?? '';
1160
1074
  }
1161
-
1162
1075
  eventListeners;
1163
1076
  eventListenerRemoveFunctions;
1164
1077
  name;
1165
1078
  metaType;
1166
1079
  props;
1167
1080
  type;
1168
-
1169
1081
  drop() {
1170
- super.drop(); // handle remove functions
1171
-
1082
+ super.drop();
1083
+ // handle remove functions
1172
1084
  Object.keys(this.eventListenerRemoveFunctions).forEach(key => {
1173
1085
  this.eventListenerRemoveFunctions[key].forEach(func => func());
1174
1086
  });
1175
1087
  }
1176
-
1177
1088
  }
1178
-
1179
- MiniDom.RendererBaseNode = RendererBaseNode; // ====================
1089
+ MiniDom.RendererBaseNode = RendererBaseNode;
1090
+ // ====================
1180
1091
  // SPECIFIC RENDERER NODES BELOW
1181
1092
  // ====================
1182
-
1183
1093
  class RendererRootNode extends MiniDom.RendererBaseNode {
1184
1094
  constructor(options = {}, parent) {
1185
1095
  super(options, parent);
1186
1096
  this.domElement = options.domElement ?? document.createElement('div');
1187
1097
  }
1188
-
1189
1098
  domElement;
1190
1099
  isLunchboxRootNode = true;
1191
1100
  }
1192
-
1193
1101
  MiniDom.RendererRootNode = RendererRootNode;
1194
-
1195
1102
  class RendererCommentNode extends MiniDom.RendererBaseNode {
1196
1103
  constructor(options = {}, parent) {
1197
1104
  super(options, parent);
1198
1105
  this.text = options.text ?? '';
1199
1106
  }
1200
-
1201
1107
  text;
1202
1108
  }
1203
-
1204
1109
  MiniDom.RendererCommentNode = RendererCommentNode;
1205
-
1206
1110
  class RendererDomNode extends MiniDom.RendererBaseNode {
1207
1111
  constructor(options = {}, parent) {
1208
1112
  super(options, parent);
1209
1113
  this.domElement = options.domElement ?? document.createElement('div');
1210
1114
  }
1211
-
1212
1115
  domElement;
1213
1116
  }
1214
-
1215
1117
  MiniDom.RendererDomNode = RendererDomNode;
1216
-
1217
1118
  class RendererTextNode extends MiniDom.RendererBaseNode {
1218
1119
  constructor(options = {}, parent) {
1219
1120
  super(options, parent);
1220
1121
  this.text = options.text ?? '';
1221
1122
  }
1222
-
1223
1123
  text;
1224
1124
  }
1225
-
1226
1125
  MiniDom.RendererTextNode = RendererTextNode;
1227
-
1228
1126
  class RendererStandardNode extends MiniDom.RendererBaseNode {
1229
1127
  constructor(options = {}, parent) {
1230
1128
  super(options, parent);
@@ -1232,15 +1130,12 @@
1232
1130
  this.attachedArray = options.attachedArray ?? {};
1233
1131
  this.instance = options.instance ?? null;
1234
1132
  }
1235
-
1236
1133
  attached;
1237
1134
  attachedArray;
1238
1135
  instance;
1239
1136
  }
1240
-
1241
1137
  MiniDom.RendererStandardNode = RendererStandardNode;
1242
1138
  })(exports.MiniDom || (exports.MiniDom = {}));
1243
-
1244
1139
  function isMinidomNode(item) {
1245
1140
  return item?.minidomType === 'RendererNode';
1246
1141
  }
@@ -1269,7 +1164,6 @@
1269
1164
  if (typeof opts.app.config.globalProperties.lunchbox.frameId === 'number') {
1270
1165
  cancelAnimationFrame(opts.app.config.globalProperties.lunchbox.frameId);
1271
1166
  }
1272
-
1273
1167
  opts.app.config.globalProperties.lunchbox.frameId = requestAnimationFrame(() => update({
1274
1168
  app: opts.app,
1275
1169
  renderer: opts.renderer,
@@ -1278,7 +1172,6 @@
1278
1172
  updateSource: opts.updateSource
1279
1173
  }));
1280
1174
  };
1281
-
1282
1175
  const update = opts => {
1283
1176
  if (opts.updateSource) {
1284
1177
  if (!opts.app.config.globalProperties.lunchbox.watchStopHandle) {
@@ -1292,37 +1185,35 @@
1292
1185
  } else {
1293
1186
  // request next frame on a continuous loop
1294
1187
  requestUpdate(opts);
1295
- } // prep options
1296
-
1297
-
1188
+ }
1189
+ // prep options
1298
1190
  const {
1299
1191
  app,
1300
1192
  renderer,
1301
1193
  scene
1302
- } = opts; // BEFORE RENDER
1303
-
1194
+ } = opts;
1195
+ // BEFORE RENDER
1304
1196
  app.config.globalProperties.lunchbox.beforeRender.forEach(cb => {
1305
1197
  cb?.(opts);
1306
- }); // RENDER
1307
-
1198
+ });
1199
+ // RENDER
1308
1200
  if (renderer && scene && opts.app.config.globalProperties.lunchbox.camera) {
1309
1201
  if (app.customRender) {
1310
1202
  app.customRender(opts);
1311
1203
  } else {
1312
- renderer.render(vue.toRaw(scene), opts.app.config.globalProperties.lunchbox.camera // toRaw(camera)
1204
+ renderer.render(vue.toRaw(scene), opts.app.config.globalProperties.lunchbox.camera
1205
+ // toRaw(camera)
1313
1206
  );
1314
1207
  }
1315
- } // AFTER RENDER
1316
-
1317
-
1208
+ }
1209
+ // AFTER RENDER
1318
1210
  app.config.globalProperties.lunchbox.afterRender.forEach(cb => {
1319
1211
  cb?.(opts);
1320
1212
  });
1321
- }; // before render
1213
+ };
1214
+ // before render
1322
1215
  // ====================
1323
-
1324
1216
  /** Obtain callback methods for `onBeforeRender` and `offBeforeRender`. Usually used internally by Lunchbox. */
1325
-
1326
1217
  const useBeforeRender = () => {
1327
1218
  return {
1328
1219
  onBeforeRender: vue.inject(onBeforeRenderKey),
@@ -1334,21 +1225,18 @@
1334
1225
  * Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
1335
1226
  * before a render triggered by that `updateSource`. Normally, the function should run every frame.
1336
1227
  */
1337
-
1338
1228
  const onBeforeRender = (cb, index = Infinity) => {
1339
1229
  useBeforeRender().onBeforeRender?.(cb, index);
1340
1230
  };
1341
1231
  /** Remove a function from the `beforeRender` callback list. Useful for tearing down functions added
1342
1232
  * by `onBeforeRender`.
1343
1233
  */
1344
-
1345
1234
  const offBeforeRender = cb => {
1346
1235
  useBeforeRender().offBeforeRender?.(cb);
1347
- }; // after render
1236
+ };
1237
+ // after render
1348
1238
  // ====================
1349
-
1350
1239
  /** Obtain callback methods for `onAfterRender` and `offAfterRender`. Usually used internally by Lunchbox. */
1351
-
1352
1240
  const useAfterRender = () => {
1353
1241
  return {
1354
1242
  onAfterRender: vue.inject(onBeforeRenderKey),
@@ -1360,21 +1248,18 @@
1360
1248
  * Note that if `updateSource` is set in the Lunchbox wrapper component, this will **only** run
1361
1249
  * after a render triggered by that `updateSource`. Normally, the function should run every frame.
1362
1250
  */
1363
-
1364
1251
  const onAfterRender = (cb, index = Infinity) => {
1365
1252
  useBeforeRender().onBeforeRender?.(cb, index);
1366
1253
  };
1367
1254
  /** Remove a function from the `afterRender` callback list. Useful for tearing down functions added
1368
1255
  * by `onAfterRender`.
1369
1256
  */
1370
-
1371
1257
  const offAfterRender = cb => {
1372
1258
  useBeforeRender().offBeforeRender?.(cb);
1373
1259
  };
1374
1260
  /** Obtain a function used to cancel the current update frame. Use `cancelUpdate` if you wish
1375
1261
  * to immediately invoke the cancellation function. Usually used internally by Lunchbox.
1376
1262
  */
1377
-
1378
1263
  const useCancelUpdate = () => {
1379
1264
  const frameId = vue.inject(frameIdKey);
1380
1265
  return () => {
@@ -1382,26 +1267,22 @@
1382
1267
  };
1383
1268
  };
1384
1269
  /** Cancel the current update frame. Usually used internally by Lunchbox. */
1385
-
1386
1270
  const cancelUpdate = () => {
1387
1271
  useCancelUpdate()?.();
1388
1272
  };
1389
1273
  /** Obtain a function used to cancel an update source. Use `cancelUpdateSource` if you wish to
1390
1274
  * immediately invoke the cancellation function. Usually used internally by Lunchbox.
1391
1275
  */
1392
-
1393
1276
  const useCancelUpdateSource = () => {
1394
1277
  const cancel = vue.inject(watchStopHandleKey);
1395
1278
  return () => cancel?.();
1396
1279
  };
1397
1280
  /** Cancel an update source. Usually used internally by Lunchbox. */
1398
-
1399
1281
  const cancelUpdateSource = () => {
1400
1282
  useCancelUpdateSource()?.();
1401
1283
  };
1402
1284
 
1403
1285
  /** Update a single prop on a given node. */
1404
-
1405
1286
  function updateObjectProp({
1406
1287
  node,
1407
1288
  key,
@@ -1417,37 +1298,33 @@
1417
1298
  interactables,
1418
1299
  value
1419
1300
  });
1420
- } // update THREE property
1301
+ }
1302
+ // update THREE property
1421
1303
  // get final key
1422
-
1423
-
1424
1304
  const camelKey = key.replace(/-/g, '.');
1425
- const finalKey = propertyShortcuts[camelKey] || camelKey; // handle and return early if prop is specific to Vue/Lunchbox
1426
-
1427
- if (internalLunchboxVueKeys.includes(key) || internalLunchboxVueKeys.includes(finalKey)) return node; // everything else should be Three-specific, so let's cancel if this isn't a standard node
1428
-
1429
- if (!isLunchboxStandardNode(node)) return node; // parse $attached values
1430
-
1305
+ const finalKey = propertyShortcuts[camelKey] || camelKey;
1306
+ // handle and return early if prop is specific to Vue/Lunchbox
1307
+ if (internalLunchboxVueKeys.includes(key) || internalLunchboxVueKeys.includes(finalKey)) return node;
1308
+ // everything else should be Three-specific, so let's cancel if this isn't a standard node
1309
+ if (!isLunchboxStandardNode(node)) return node;
1310
+ // parse $attached values
1431
1311
  if (typeof value === 'string' && value.startsWith('$attached')) {
1432
1312
  const attachedName = value.replace('$attached.', '');
1433
1313
  value = get(node.attached, attachedName, null);
1434
- } // save instance
1435
-
1436
-
1437
- const target = node.instance; // cancel if no target
1438
-
1439
- if (!target) return node; // burrow down until we get property to change
1440
-
1314
+ }
1315
+ // save instance
1316
+ const target = node.instance;
1317
+ // cancel if no target
1318
+ if (!target) return node;
1319
+ // burrow down until we get property to change
1441
1320
  let liveProperty;
1442
-
1443
1321
  for (let i = 0; i < nestedPropertiesToCheck.length && !liveProperty; i++) {
1444
1322
  const nestedProperty = nestedPropertiesToCheck[i];
1445
1323
  const fullPath = [nestedProperty, finalKey].filter(Boolean).join('.');
1446
1324
  liveProperty = liveProperty = get(target, fullPath);
1447
- } // change property
1325
+ }
1326
+ // change property
1448
1327
  // first, save as array in case we need to spread it
1449
-
1450
-
1451
1328
  if (liveProperty && isNumber(value) && liveProperty?.setScalar) {
1452
1329
  // if value is a number and the property has a `setScalar` method, use that
1453
1330
  liveProperty.setScalar(value);
@@ -1462,18 +1339,17 @@
1462
1339
  } else {
1463
1340
  if (!Array.isArray(value)) {
1464
1341
  throw new Error('Arguments on a declarative method must be wrapped in an array.\nWorks:\n<example :methodCall="[256]" />\nDoesn\'t work:\n<example :methodCall="256" />');
1465
- } // if property is a function, let's try calling it
1466
-
1467
-
1342
+ }
1343
+ // if property is a function, let's try calling it
1468
1344
  liveProperty.bind(node.instance)(...value);
1469
- } // pass the result to the parent
1345
+ }
1346
+ // pass the result to the parent
1470
1347
  // const parent = node.parentNode
1471
1348
  // if (parent) {
1472
1349
  // const parentAsLunchboxNode = parent as Lunchbox.Node
1473
1350
  // parentAsLunchboxNode.attached[finalKey] = result
1474
1351
  // ; (parentAsLunchboxNode.instance as any)[finalKey] = result
1475
1352
  // }
1476
-
1477
1353
  } else if (get(target, finalKey, undefined) !== undefined) {
1478
1354
  // blank strings evaluate to `true`
1479
1355
  // <mesh castShadow receiveShadow /> will work the same as
@@ -1483,25 +1359,20 @@
1483
1359
  // if you see this error in production, you might need to add `finalKey`
1484
1360
  // to `internalLunchboxVueKeys` below
1485
1361
  console.log(`No property ${finalKey} found on ${target}`);
1486
- } // mark that we need to update if needed
1487
-
1488
-
1362
+ }
1363
+ // mark that we need to update if needed
1489
1364
  const targetTypeRaw = target?.texture?.type || target?.type;
1490
-
1491
1365
  if (typeof targetTypeRaw === 'string') {
1492
1366
  const targetType = targetTypeRaw.toLowerCase();
1493
-
1494
1367
  switch (true) {
1495
1368
  case targetType.includes('material'):
1496
1369
  target.needsUpdate = true;
1497
1370
  break;
1498
-
1499
1371
  case targetType.includes('camera') && target.updateProjectionMatrix:
1500
1372
  target.updateProjectionMatrix();
1501
1373
  break;
1502
1374
  }
1503
1375
  }
1504
-
1505
1376
  return node;
1506
1377
  }
1507
1378
  const propertyShortcuts = {
@@ -1511,8 +1382,8 @@
1511
1382
  };
1512
1383
  const nestedPropertiesToCheck = ['', 'parameters'];
1513
1384
  /** props that Lunchbox intercepts and prevents passing to created instances */
1514
-
1515
- const internalLunchboxVueKeys = ['args', 'attach', 'attachArray', 'is.default', 'isDefault', 'key', 'onAdded', // 'onReady',
1385
+ const internalLunchboxVueKeys = ['args', 'attach', 'attachArray', 'is.default', 'isDefault', 'key', 'onAdded',
1386
+ // 'onReady',
1516
1387
  'ref', 'src'];
1517
1388
 
1518
1389
  const autoAttach = ['geometry', 'material'];
@@ -1520,55 +1391,48 @@
1520
1391
  const options = {
1521
1392
  type,
1522
1393
  props: vnodeProps
1523
- }; // handle dom node
1524
-
1394
+ };
1395
+ // handle dom node
1525
1396
  const isDomNode = isLunchboxDomComponent(options);
1526
-
1527
1397
  if (isDomNode) {
1528
1398
  const node = createDomNode(options);
1529
1399
  return node;
1530
- } // handle standard node
1531
-
1532
-
1533
- const node = createNode(options); // autoattach
1534
-
1400
+ }
1401
+ // handle standard node
1402
+ const node = createNode(options);
1403
+ // autoattach
1535
1404
  autoAttach.forEach(key => {
1536
1405
  if (type.toLowerCase().endsWith(key)) {
1537
1406
  node.props.attach = key;
1538
1407
  }
1539
- }); // TODO: array autoattach
1540
-
1408
+ });
1409
+ // TODO: array autoattach
1541
1410
  return node;
1542
1411
  };
1543
1412
 
1544
1413
  const insert = (child, parent, anchor) => {
1545
1414
  if (!parent) {
1546
1415
  throw new Error('missing parent');
1547
- } // add to parent tree node if we have one
1416
+ }
1417
+ // add to parent tree node if we have one
1548
1418
  // let effectiveParent = parent ?? ensureRootNode()
1549
-
1550
-
1551
- parent.insertBefore(child, anchor); // handle comment & text nodes
1552
-
1419
+ parent.insertBefore(child, anchor);
1420
+ // handle comment & text nodes
1553
1421
  if (child.metaType === 'commentMeta' || child.metaType === 'textMeta') {
1554
1422
  return;
1555
- } // handle dom element
1556
-
1557
-
1423
+ }
1424
+ // handle dom element
1558
1425
  if (isLunchboxDomComponent(child)) {
1559
1426
  if (isLunchboxDomComponent(parent) || isLunchboxRootNode(parent)) {
1560
1427
  parent.domElement.appendChild(child.domElement);
1561
1428
  }
1562
- } // handle standard nodes
1563
-
1564
-
1429
+ }
1430
+ // handle standard nodes
1565
1431
  if (isLunchboxStandardNode(child)) {
1566
1432
  // let effectiveParent = parent
1567
1433
  let effectiveParentNodeType = parent.metaType;
1568
-
1569
1434
  if (effectiveParentNodeType === 'textMeta' || effectiveParentNodeType === 'commentMeta') {
1570
1435
  const path = parent.getPath();
1571
-
1572
1436
  for (let i = path.length - 1; i >= 0; i--) {
1573
1437
  if (path[i].metaType !== 'textMeta' && path[i].metaType !== 'commentMeta') {
1574
1438
  parent = path[i];
@@ -1576,26 +1440,23 @@
1576
1440
  }
1577
1441
  }
1578
1442
  }
1579
-
1580
1443
  if (isLunchboxStandardNode(child) && child.instance?.isObject3D && isLunchboxStandardNode(parent) && parent.instance?.isObject3D) {
1581
1444
  parent.instance?.add?.(child.instance);
1582
- } // add attached props
1583
-
1584
-
1445
+ }
1446
+ // add attached props
1585
1447
  if (child?.props?.attach && isLunchboxStandardNode(parent) && parent?.instance) {
1586
1448
  // if this element is a loader and the `src` attribute is being used,
1587
1449
  // let's assume we want to create the loader and run `load`
1588
- const isUsingLoaderSugar = child.type?.toLowerCase().endsWith('loader') && child.props.src && (child.props.attach || child.props.attachArray); // run special loader behavior
1589
-
1450
+ const isUsingLoaderSugar = child.type?.toLowerCase().endsWith('loader') && child.props.src && (child.props.attach || child.props.attachArray);
1451
+ // run special loader behavior
1590
1452
  if (isUsingLoaderSugar) {
1591
1453
  runLoader(child, parent);
1592
1454
  } else {
1593
1455
  // update attached normally
1594
1456
  attachToParentInstance(child, parent, child.props.attach);
1595
1457
  }
1596
- } // fire onAdded event
1597
-
1598
-
1458
+ }
1459
+ // fire onAdded event
1599
1460
  if (child.props?.onAdded) {
1600
1461
  child.props.onAdded({
1601
1462
  instance: child.instance
@@ -1603,15 +1464,13 @@
1603
1464
  }
1604
1465
  }
1605
1466
  };
1606
-
1607
1467
  function runLoader(child, parent) {
1608
- const loader = child.instance; // ensure parent has attached spaces ready
1609
-
1468
+ const loader = child.instance;
1469
+ // ensure parent has attached spaces ready
1610
1470
  parent.attached = parent.attached || {};
1611
- parent.attachedArray = parent.attachedArray || {}; // this should never be true, but just in case
1612
-
1471
+ parent.attachedArray = parent.attachedArray || {};
1472
+ // this should never be true, but just in case
1613
1473
  if (!child.props.attach) return;
1614
-
1615
1474
  if (child.type?.toLowerCase() === 'textureloader') {
1616
1475
  // if this is a texture loader, immediately pass
1617
1476
  // load function to parent attachment
@@ -1627,11 +1486,9 @@
1627
1486
  });
1628
1487
  }
1629
1488
  }
1630
-
1631
1489
  function attachToParentInstance(child, parent, key, value) {
1632
1490
  const finalValueToAttach = value ?? child.instance;
1633
1491
  const parentInstanceAsAny = parent.instance;
1634
-
1635
1492
  if (child.props.attach === key) {
1636
1493
  parent.attached = {
1637
1494
  [key]: finalValueToAttach,
@@ -1639,40 +1496,38 @@
1639
1496
  };
1640
1497
  parentInstanceAsAny[key] = value ?? child.instance;
1641
1498
  }
1642
-
1643
1499
  if (child.props.attachArray === key) {
1644
1500
  if (!parent.attachedArray[child.props.attachArray]) {
1645
1501
  parent.attachedArray[child.props.attachArray] = [];
1646
1502
  }
1647
-
1648
- parent.attachedArray[child.props.attachArray].push(finalValueToAttach); // TODO: implement auto-attaching array
1649
-
1503
+ parent.attachedArray[child.props.attachArray].push(finalValueToAttach);
1504
+ // TODO: implement auto-attaching array
1650
1505
  parentInstanceAsAny[key] = [parentInstanceAsAny[key]];
1651
1506
  }
1652
1507
  }
1653
1508
 
1654
1509
  const remove = node => {
1655
- if (!node) return; // prep subtree
1656
-
1510
+ if (!node) return;
1511
+ // prep subtree
1657
1512
  const subtree = [];
1658
1513
  node.walk(descendant => {
1659
1514
  subtree.push(descendant);
1660
1515
  return true;
1661
- }); // clean up subtree
1662
-
1516
+ });
1517
+ // clean up subtree
1663
1518
  subtree.forEach(n => {
1664
1519
  if (isLunchboxStandardNode(n)) {
1665
1520
  // try to remove three object
1666
- n.instance?.removeFromParent?.(); // try to dispose three object
1667
-
1668
- const dispose = // calling `dispose` on a scene triggers an error,
1521
+ n.instance?.removeFromParent?.();
1522
+ // try to dispose three object
1523
+ const dispose =
1524
+ // calling `dispose` on a scene triggers an error,
1669
1525
  // so let's ignore if this node is a scene
1670
1526
  n.type !== 'scene' && n.instance?.dispose;
1671
1527
  if (dispose) dispose.bind(n.instance)();
1672
1528
  n.instance = null;
1673
- } // drop tree node
1674
-
1675
-
1529
+ }
1530
+ // drop tree node
1676
1531
  n.drop();
1677
1532
  });
1678
1533
  };
@@ -1680,45 +1535,38 @@
1680
1535
  /*
1681
1536
  Elements are `create`d from the outside in, then `insert`ed from the inside out.
1682
1537
  */
1683
-
1684
1538
  const createNodeOps = () => {
1685
1539
  // APP-LEVEL GLOBALS
1686
1540
  // ====================
1687
1541
  // These need to exist at the app level in a place where the node ops can access them.
1688
1542
  // It'd be better to set these via `app.provide` at app creation, but the node ops need access
1689
1543
  // to these values before the app is instantiated, so this is the next-best place for them to exist.
1690
- const interactables = vue.ref([]); // NODE OPS
1544
+ const interactables = vue.ref([]);
1545
+ // NODE OPS
1691
1546
  // ====================
1692
-
1693
1547
  const nodeOps = {
1694
1548
  createElement,
1695
-
1696
1549
  createText(text) {
1697
1550
  return createTextNode({
1698
1551
  text
1699
1552
  });
1700
1553
  },
1701
-
1702
1554
  createComment(text) {
1703
1555
  return createCommentNode({
1704
1556
  text
1705
1557
  });
1706
1558
  },
1707
-
1708
1559
  insert,
1709
-
1710
1560
  nextSibling(node) {
1711
1561
  const result = node.nextSibling;
1712
1562
  if (!result) return null;
1713
1563
  return result;
1714
1564
  },
1715
-
1716
1565
  parentNode(node) {
1717
1566
  const result = node.parentNode;
1718
1567
  if (!result) return null;
1719
1568
  return result;
1720
1569
  },
1721
-
1722
1570
  patchProp(node, key, prevValue, nextValue) {
1723
1571
  if (isLunchboxDomComponent(node)) {
1724
1572
  // handle DOM node
@@ -1730,16 +1578,13 @@
1730
1578
  } else {
1731
1579
  node.domElement.setAttribute(key, nextValue);
1732
1580
  }
1733
-
1734
1581
  return;
1735
- } // ignore if root node, or Lunchbox internal prop
1736
-
1737
-
1582
+ }
1583
+ // ignore if root node, or Lunchbox internal prop
1738
1584
  if (isLunchboxRootNode(node) || key.startsWith('$')) {
1739
1585
  return;
1740
- } // otherwise, update prop
1741
-
1742
-
1586
+ }
1587
+ // otherwise, update prop
1743
1588
  updateObjectProp({
1744
1589
  node: node,
1745
1590
  key,
@@ -1747,15 +1592,13 @@
1747
1592
  value: nextValue
1748
1593
  });
1749
1594
  },
1750
-
1751
1595
  remove,
1752
-
1753
- setElementText() {// noop
1596
+ setElementText() {
1597
+ // noop
1754
1598
  },
1755
-
1756
- setText() {// noop
1599
+ setText() {
1600
+ // noop
1757
1601
  }
1758
-
1759
1602
  };
1760
1603
  return {
1761
1604
  nodeOps,
@@ -1764,18 +1607,14 @@
1764
1607
  };
1765
1608
 
1766
1609
  /** The current camera as a computed value. */
1767
-
1768
1610
  const useCamera = () => vue.inject(appCameraKey);
1769
1611
  /** Run a function using the current camera when it's present. */
1770
-
1771
1612
  const onCameraReady = cb => {
1772
1613
  const existing = useCamera();
1773
-
1774
1614
  if (existing.value) {
1775
1615
  cb(existing.value);
1776
1616
  return;
1777
1617
  }
1778
-
1779
1618
  let stopWatch = null;
1780
1619
  stopWatch = vue.watch(useCamera(), newVal => {
1781
1620
  if (newVal) {
@@ -1785,18 +1624,14 @@
1785
1624
  });
1786
1625
  };
1787
1626
  /** The current renderer as a computed value. */
1788
-
1789
1627
  const useRenderer = () => vue.inject(appRenderersKey);
1790
1628
  /** Run a function using the current renderer when it's present. */
1791
-
1792
1629
  const onRendererReady = cb => {
1793
1630
  const existing = useRenderer();
1794
-
1795
1631
  if (existing.value) {
1796
1632
  cb(existing.value);
1797
1633
  return;
1798
1634
  }
1799
-
1800
1635
  let stopWatch = null;
1801
1636
  stopWatch = vue.watch(useRenderer(), newVal => {
1802
1637
  if (newVal) {
@@ -1808,18 +1643,14 @@
1808
1643
  });
1809
1644
  };
1810
1645
  /** The current scene as a computed value. */
1811
-
1812
1646
  const useScene = () => vue.inject(appSceneKey);
1813
1647
  /** Run a function using the current scene when it's present. */
1814
-
1815
1648
  const onSceneReady = cb => {
1816
1649
  const existing = useScene();
1817
-
1818
1650
  if (existing.value) {
1819
1651
  cb(existing.value);
1820
1652
  return;
1821
1653
  }
1822
-
1823
1654
  let stopWatch = null;
1824
1655
  stopWatch = vue.watch(useScene(), newVal => {
1825
1656
  if (newVal) {
@@ -1829,16 +1660,15 @@
1829
1660
  }, {
1830
1661
  immediate: true
1831
1662
  });
1832
- }; // CUSTOM RENDER SUPPORT
1663
+ };
1664
+ // CUSTOM RENDER SUPPORT
1833
1665
  // ====================
1834
-
1835
1666
  /** Set a custom render function, overriding the Lunchbox app's default render function.
1836
1667
  * Changing this requires the user to manually render their scene.
1837
1668
  *
1838
1669
  * Invokes immediately - use `useCustomRender().setCustomRender`
1839
1670
  * if you need to call somewhere outside of `setup`.
1840
1671
  */
1841
-
1842
1672
  const setCustomRender = render => {
1843
1673
  useCustomRender()?.setCustomRender?.(render);
1844
1674
  };
@@ -1847,24 +1677,20 @@
1847
1677
  * Invokes immediately - use `useCustomRender().clearCustomRender`
1848
1678
  * if you need to call somewhere outside of `setup`.
1849
1679
  */
1850
-
1851
1680
  const clearCustomRender = () => {
1852
1681
  useCustomRender()?.clearCustomRender?.();
1853
1682
  };
1854
1683
  /** Provides `setCustomRender` and `clearCustomRender` functions to be called in a non-`setup` context. */
1855
-
1856
1684
  const useCustomRender = () => {
1857
1685
  return {
1858
1686
  /** Set a custom render function, overriding the Lunchbox app's default render function.
1859
1687
  * Changing this requires the user to manually render their scene. */
1860
1688
  setCustomRender: vue.inject(setCustomRenderKey),
1861
-
1862
1689
  /** Clear the active app's custom render function. */
1863
1690
  clearCustomRender: vue.inject(clearCustomRenderKey)
1864
1691
  };
1865
1692
  };
1866
1693
  /** Use app-level globals. */
1867
-
1868
1694
  const useGlobals = () => vue.inject(globalsInjectionKey);
1869
1695
  /** Construct a function to update your app-level globals.
1870
1696
  *
@@ -1876,29 +1702,23 @@
1876
1702
  * updateGlobals({ dpr: 2 })
1877
1703
  * ```
1878
1704
  */
1879
-
1880
1705
  const useUpdateGlobals = () => vue.inject(updateGlobalsInjectionKey);
1881
1706
  /** Update app-level globals.
1882
1707
  *
1883
1708
  * Invokes immediately - use `useUpdateGlobals`
1884
1709
  * if you need to call somewhere outside of `setup`.
1885
1710
  */
1886
-
1887
1711
  const updateGlobals = newValue => {
1888
1712
  useUpdateGlobals()?.(newValue);
1889
1713
  };
1890
1714
  /** Use the current Lunchbox app. Usually used internally by Lunchbox. */
1891
-
1892
1715
  const useApp = () => vue.inject(appKey);
1893
1716
  /** Obtain a list of the start callback functions. Usually used internally by Lunchbox. */
1894
-
1895
1717
  const useStartCallbacks = () => vue.inject(startCallbackKey);
1896
1718
  /** Run a given callback once when the Lunchbox app starts. Include an index to
1897
1719
  * splice the callback at that index in the callback queue. */
1898
-
1899
1720
  const onStart = (cb, index = Infinity) => {
1900
1721
  const callbacks = useStartCallbacks();
1901
-
1902
1722
  if (index === Infinity) {
1903
1723
  callbacks?.push(cb);
1904
1724
  } else {
@@ -1906,41 +1726,39 @@
1906
1726
  }
1907
1727
  };
1908
1728
  /** Obtain a list of interactable objects (registered via onClick, onHover, etc events). Usually used internally by Lunchbox. */
1909
-
1910
1729
  const useLunchboxInteractables = () => vue.inject(lunchboxInteractables);
1911
1730
  /** Build a computed instance-getter from a specified ref. Defaults to a `toRaw`'d result. */
1912
-
1913
1731
  const getInstance = (target, raw = true) => vue.computed(() => {
1914
1732
  const output = target.value?.$el?.instance ?? target.value?.instance ?? null;
1915
1733
  if (output && raw) return vue.toRaw(output);
1916
1734
  return output;
1917
- }); // CREATE APP
1735
+ });
1736
+ // CREATE APP
1918
1737
  // ====================
1919
-
1920
- const createApp = root => {
1738
+ const createApp = (root, rootProps = {}) => {
1921
1739
  const {
1922
1740
  nodeOps,
1923
1741
  interactables
1924
1742
  } = createNodeOps();
1925
- const app = vue.createRenderer(nodeOps).createApp(root); // provide Lunchbox interaction handlers flag (modified when user references events via
1743
+ const app = vue.createRenderer(nodeOps).createApp(root, rootProps);
1744
+ // provide Lunchbox interaction handlers flag (modified when user references events via
1926
1745
  // @click, etc)
1927
-
1928
- app.provide(lunchboxInteractables, interactables); // register all components
1746
+ app.provide(lunchboxInteractables, interactables);
1747
+ // register all components
1929
1748
  // ====================
1930
-
1931
1749
  Object.keys(components).forEach(key => {
1932
1750
  app?.component(key, components[key]);
1933
- }); // provide custom renderer functions
1751
+ });
1752
+ // provide custom renderer functions
1934
1753
  // ====================
1935
-
1936
1754
  app.provide(setCustomRenderKey, render => {
1937
1755
  app.setCustomRender(render);
1938
1756
  });
1939
1757
  app.provide(clearCustomRenderKey, () => {
1940
1758
  app.clearCustomRender();
1941
- }); // before render
1759
+ });
1760
+ // before render
1942
1761
  // ====================
1943
-
1944
1762
  const beforeRender = [];
1945
1763
  app.provide(beforeRenderKey, beforeRender);
1946
1764
  app.provide(onBeforeRenderKey, (cb, index = Infinity) => {
@@ -1955,14 +1773,13 @@
1955
1773
  beforeRender.splice(cb, 1);
1956
1774
  } else {
1957
1775
  const idx = beforeRender.findIndex(v => v == cb);
1958
-
1959
1776
  if (idx !== -1) {
1960
1777
  beforeRender.splice(idx, 1);
1961
1778
  }
1962
1779
  }
1963
- }); // after render
1780
+ });
1781
+ // after render
1964
1782
  // ====================
1965
-
1966
1783
  const afterRender = [];
1967
1784
  app.provide(afterRenderKey, afterRender);
1968
1785
  app.provide(onAfterRenderKey, (cb, index = Infinity) => {
@@ -1977,14 +1794,13 @@
1977
1794
  afterRender.splice(cb, 1);
1978
1795
  } else {
1979
1796
  const idx = afterRender.findIndex(v => v == cb);
1980
-
1981
1797
  if (idx !== -1) {
1982
1798
  afterRender.splice(idx, 1);
1983
1799
  }
1984
1800
  }
1985
- }); // save app-level components
1801
+ });
1802
+ // save app-level components
1986
1803
  // ====================
1987
-
1988
1804
  app.config.globalProperties.lunchbox = vue.reactive({
1989
1805
  afterRender,
1990
1806
  beforeRender,
@@ -1993,35 +1809,34 @@
1993
1809
  frameId: -1,
1994
1810
  renderer: null,
1995
1811
  scene: null,
1996
- watchStopHandle: null // TODO: inputActive, mousePos
1997
-
1998
- }); // provide app-level globals & globals update method
1812
+ watchStopHandle: null
1813
+ // TODO: inputActive, mousePos
1814
+ });
1815
+ // provide app-level globals & globals update method
1999
1816
  // ====================
2000
-
2001
1817
  app.provide(globalsInjectionKey, app.config.globalProperties.lunchbox);
2002
1818
  app.provide(updateGlobalsInjectionKey, newGlobals => {
2003
1819
  Object.keys(newGlobals).forEach(key => {
2004
- const typedKey = key; // TODO: fix
2005
-
1820
+ const typedKey = key;
1821
+ // TODO: fix
2006
1822
  app.config.globalProperties.lunchbox[typedKey] = newGlobals[typedKey];
2007
1823
  });
2008
- }); // frame ID (used for update functions)
1824
+ });
1825
+ // frame ID (used for update functions)
2009
1826
  // ====================
2010
-
2011
- app.provide(frameIdKey, app.config.globalProperties.lunchbox.frameId); // watch stop handler (used for conditional update loop)
1827
+ app.provide(frameIdKey, app.config.globalProperties.lunchbox.frameId);
1828
+ // watch stop handler (used for conditional update loop)
2012
1829
  // ====================
2013
-
2014
- app.provide(watchStopHandleKey, app.config.globalProperties.lunchbox.watchStopHandle); // update mount function to match Lunchbox.Node
1830
+ app.provide(watchStopHandleKey, app.config.globalProperties.lunchbox.watchStopHandle);
1831
+ // update mount function to match Lunchbox.Node
2015
1832
  // ====================
2016
-
2017
1833
  const {
2018
1834
  mount
2019
1835
  } = app;
2020
-
2021
1836
  app.mount = (root, ...args) => {
2022
1837
  // find DOM element to use as app root
2023
- const domElement = typeof root === 'string' ? document.querySelector(root) : root; // create or find root node
2024
-
1838
+ const domElement = typeof root === 'string' ? document.querySelector(root) : root;
1839
+ // create or find root node
2025
1840
  const rootNode = new exports.MiniDom.RendererRootNode({
2026
1841
  domElement,
2027
1842
  isLunchboxRootNode: true,
@@ -2034,44 +1849,41 @@
2034
1849
  app.provide(appRootNodeKey, rootNode);
2035
1850
  const mounted = mount(rootNode, ...args);
2036
1851
  return mounted;
2037
- }; // embed .extend function
1852
+ };
1853
+ // embed .extend function
2038
1854
  // ====================
2039
-
2040
-
2041
1855
  app.extend = targets => {
2042
1856
  extend({
2043
1857
  app: app,
2044
1858
  ...targets
2045
1859
  });
2046
1860
  return app;
2047
- }; // start callback functions
1861
+ };
1862
+ // start callback functions
2048
1863
  // ====================
2049
-
2050
-
2051
1864
  const startCallbacks = [];
2052
- app.provide(startCallbackKey, startCallbacks); // prep for custom render support
1865
+ app.provide(startCallbackKey, startCallbacks);
1866
+ // prep for custom render support
2053
1867
  // ====================
2054
-
2055
1868
  app.setCustomRender = newRender => {
2056
1869
  if (app) {
2057
1870
  app.customRender = newRender;
2058
1871
  }
2059
- }; // add custom render removal
2060
-
2061
-
1872
+ };
1873
+ // add custom render removal
2062
1874
  app.clearCustomRender = () => {
2063
1875
  if (app) {
2064
1876
  app.customRender = null;
2065
1877
  }
2066
- }; // provide app
1878
+ };
1879
+ // provide app
2067
1880
  // ====================
2068
-
2069
-
2070
1881
  app.provide(appKey, app);
2071
1882
  app.provide(appRenderersKey, vue.computed(() => app.config.globalProperties.lunchbox.renderer));
2072
1883
  app.provide(appSceneKey, vue.computed(() => app.config.globalProperties.lunchbox.scene));
2073
- app.provide(appCameraKey, vue.computed(() => app.config.globalProperties.lunchbox.camera)); // done
2074
-
1884
+ app.provide(appCameraKey, vue.computed(() => app.config.globalProperties.lunchbox.camera));
1885
+ app._props;
1886
+ // done
2075
1887
  return app;
2076
1888
  };
2077
1889
 
@@ -2135,6 +1947,4 @@
2135
1947
  exports.useUpdateGlobals = useUpdateGlobals;
2136
1948
  exports.watchStopHandleKey = watchStopHandleKey;
2137
1949
 
2138
- Object.defineProperty(exports, '__esModule', { value: true });
2139
-
2140
1950
  }));