lunchboxjs 0.2.1015 → 0.2.1017-beta

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