@tresjs/core 5.7.0 → 5.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,80 @@
1
+ <p align="center">
2
+ <a href="https://npmx.dev/package/@tresjs/core"><img src="https://npmx.dev/api/registry/badge/version/@tresjs/core" alt="version"></a>
3
+ <a href="https://www.npmjs.com/package/@tresjs/core"><img src="https://img.shields.io/npm/dm/@tresjs/core?color=82DBA1" alt="npm downloads"></a>
4
+ <a href="https://tresjs.org/discord"><img src="https://img.shields.io/badge/chat-discord-purple?style=flat&logo=discord" alt="discord chat"></a>
5
+ </p>
6
+ <br/>
7
+
8
+ # TresJS Core
9
+
10
+ > Declarative ThreeJS using Vue Components
11
+
12
+ - 💡 Build 3D scenes with Vue components
13
+ - ⚡️ Powered by Vite
14
+ - 🎨 Vue custom renderer for Three.js
15
+ - 🥰 Always up-to-date with the latest Three.js features
16
+ - 🦾 Fully Typed
17
+
18
+ TresJS is a Vue custom renderer that lets you build Three.js scenes declaratively using Vue components. Any Three.js class can be used as a component by prefixing it with `Tres` (e.g., `<TresMesh>`, `<TresBoxGeometry>`).
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pnpm i @tresjs/core three
24
+ ```
25
+
26
+ ## TypeScript
27
+
28
+ Install Three.js type definitions:
29
+
30
+ ```bash
31
+ pnpm i @types/three -D
32
+ ```
33
+
34
+ ### Vite Configuration
35
+
36
+ Add the template compiler options to your `vite.config.ts` to make the custom renderer work and avoid console warnings:
37
+
38
+ ```ts
39
+ import { templateCompilerOptions } from '@tresjs/core'
40
+ import vue from '@vitejs/plugin-vue'
41
+ import { defineConfig } from 'vite'
42
+
43
+ export default defineConfig({
44
+ plugins: [
45
+ vue({
46
+ ...templateCompilerOptions,
47
+ }),
48
+ ],
49
+ })
50
+ ```
51
+
52
+ ## Docs
53
+
54
+ Checkout the [docs](https://docs.tresjs.org/)
55
+
56
+ ### Build
57
+
58
+ To build the package run:
59
+
60
+ ```bash
61
+ pnpm run build
62
+ ```
63
+
64
+ ### Test
65
+
66
+ ```bash
67
+ pnpm run test
68
+ ```
69
+
70
+ ### Playground
71
+
72
+ To run the playground:
73
+
74
+ ```bash
75
+ pnpm run playground
76
+ ```
77
+
78
+ ## License
79
+
80
+ [MIT](/LICENSE)
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.7.0
3
+ * version: v5.8.1
4
4
  * (c) 2026
5
5
  * description: Declarative ThreeJS using Vue Components
6
6
  * author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.7.0
3
+ * version: v5.8.1
4
4
  * (c) 2026
5
5
  * description: Declarative ThreeJS using Vue Components
6
6
  * author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
package/dist/tres.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.7.0
3
+ * version: v5.8.1
4
4
  * (c) 2026
5
5
  * description: Declarative ThreeJS using Vue Components
6
6
  * author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
7
7
  */
8
- import * as vue4 from "vue";
8
+ import * as vue15 from "vue";
9
9
  import { App, ComputedRef, DefineComponent, MaybeRef, MaybeRefOrGetter, Reactive, Ref, ShallowRef, VNode, VNodeRef } from "vue";
10
10
  import * as THREE from "three";
11
11
  import { BufferGeometry, Camera, Color, ColorRepresentation, ColorSpace, Event, Fog, Group, Layers, Light, Loader, LoadingManager, Material, Mesh, Object3D, Object3DEventMap, OrthographicCamera, PerspectiveCamera, Scene, ShadowMapType, ToneMapping, Vector3, WebGLRenderer } from "three";
@@ -297,12 +297,14 @@ interface UseRendererOptions {
297
297
  scene: ShallowRef<TresScene>;
298
298
  canvas: MaybeRef<HTMLCanvasElement>;
299
299
  options: Reactive<RendererOptions>;
300
+ fpsLimit?: MaybeRefOrGetter<number>;
300
301
  contextParts: Pick<TresContext, 'sizes' | 'camera'>;
301
302
  }
302
303
  declare function useRendererManager({
303
304
  scene,
304
305
  canvas,
305
306
  options,
307
+ fpsLimit,
306
308
  contextParts: {
307
309
  sizes,
308
310
  camera
@@ -321,11 +323,11 @@ declare function useRendererManager({
321
323
  onRender: _vueuse_core0.EventHookOn<TresRenderer>;
322
324
  onError: _vueuse_core0.EventHookOn<TresRendererError>;
323
325
  invalidate: (amountOfFramesToInvalidate?: number) => void;
324
- canBeInvalidated: vue4.ComputedRef<boolean>;
326
+ canBeInvalidated: vue15.ComputedRef<boolean>;
325
327
  mode: "always" | "on-demand" | "manual" | undefined;
326
328
  replaceRenderFunction: (fn: RenderFunction) => void;
327
- isInitialized: vue4.Ref<boolean, boolean>;
328
- error: vue4.Ref<TresRendererError | null, TresRendererError | null>;
329
+ isInitialized: vue15.Ref<boolean, boolean>;
330
+ error: vue15.Ref<TresRendererError | null, TresRendererError | null>;
329
331
  };
330
332
  type UseRendererManagerReturn = ReturnType<typeof useRendererManager>;
331
333
  //#endregion
@@ -357,6 +359,7 @@ interface TresContext {
357
359
  declare const useTresContextProvider: (args_0: {
358
360
  scene: TresScene;
359
361
  canvas: MaybeRef<HTMLCanvasElement>;
362
+ fpsLimit?: MaybeRefOrGetter<number>;
360
363
  windowSize: MaybeRefOrGetter<boolean>;
361
364
  rendererOptions: RendererOptions;
362
365
  }) => TresContext;
@@ -512,7 +515,7 @@ declare function buildGraph(object: Object3D | TresObject): TresObjectMap;
512
515
  //#endregion
513
516
  //#region src/composables/useLoader/component.vue.d.ts
514
517
  declare const __VLS_export$2: <T extends TresObjectMap>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
515
- props: vue4.PublicProps & __VLS_PrettifyLocal<{
518
+ props: vue15.PublicProps & __VLS_PrettifyLocal<{
516
519
  /**
517
520
  * The THREE.js loader to use
518
521
  */
@@ -525,9 +528,10 @@ declare const __VLS_export$2: <T extends TresObjectMap>(__VLS_props: NonNullable
525
528
  * Optional THREE.js LoadingManager
526
529
  */
527
530
  manager?: LoadingManager;
531
+ extensions?: (res: T) => void;
528
532
  } & {
529
- onLoaded?: ((result: T) => any) | undefined;
530
533
  onError?: ((error: unknown) => any) | undefined;
534
+ onLoaded?: ((result: T) => any) | undefined;
531
535
  }> & (typeof globalThis extends {
532
536
  __VLS_PROPS_FALLBACK: infer P;
533
537
  } ? P : {});
@@ -535,20 +539,20 @@ declare const __VLS_export$2: <T extends TresObjectMap>(__VLS_props: NonNullable
535
539
  attrs: any;
536
540
  slots: {
537
541
  default?: (props: {
538
- state: vue4.UnwrapRef<T>;
542
+ state: vue15.UnwrapRef<T>;
539
543
  isLoading: boolean;
540
544
  error: unknown;
541
545
  }) => any;
542
546
  };
543
- emit: ((evt: "loaded", result: T) => void) & ((evt: "error", error: unknown) => void);
544
- }>) => vue4.VNode & {
547
+ emit: ((evt: "error", error: unknown) => void) & ((evt: "loaded", result: T) => void);
548
+ }>) => vue15.VNode & {
545
549
  __ctx?: Awaited<typeof __VLS_setup>;
546
550
  };
547
551
  declare const _default$2: typeof __VLS_export$2;
548
552
  type __VLS_PrettifyLocal<T> = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {};
549
553
  //#endregion
550
554
  //#region src/composables/useGraph/index.d.ts
551
- declare const useGraph: (object: MaybeRef$1<TresObject | undefined>) => vue4.ComputedRef<TresObjectMap | undefined>;
555
+ declare const useGraph: (object: MaybeRef$1<TresObject | undefined>) => vue15.ComputedRef<TresObjectMap | undefined>;
552
556
  //#endregion
553
557
  //#region src/utils/createPriorityEventHook.d.ts
554
558
  type Callback<T> = Parameters<EventHookOff<T>>[0];
@@ -599,7 +603,7 @@ type LoopContext = RafLoopContext & TresPartialContext;
599
603
  declare const useLoop: () => {
600
604
  stop: () => void;
601
605
  start: () => void;
602
- isActive: Readonly<vue4.ShallowRef<boolean>>;
606
+ isActive: Readonly<vue15.ShallowRef<boolean>>;
603
607
  onBeforeRender: PriorityEventHookOn<LoopContext>;
604
608
  onRender: PriorityEventHookOn<LoopContext>;
605
609
  render: (fn: RenderFunction) => void;
@@ -675,6 +679,11 @@ interface ContextProps extends RendererOptions {
675
679
  * @default false
676
680
  */
677
681
  windowSize?: boolean;
682
+ /**
683
+ * The maximum number of frames per second to render
684
+ * @default undefined
685
+ */
686
+ fpsLimit?: number;
678
687
  /**
679
688
  * Whether to enable the provide/inject bridge between Vue and TresJS
680
689
  * When true, Vue's provide/inject will work across the TresJS boundary
@@ -701,10 +710,10 @@ type __VLS_Props = ContextProps & {
701
710
  type __VLS_Slots$1 = {
702
711
  default: () => any;
703
712
  };
704
- declare const __VLS_base$1: vue4.DefineComponent<__VLS_Props, {
705
- context: vue4.ShallowRef<TresContext, TresContext>;
713
+ declare const __VLS_base$1: vue15.DefineComponent<__VLS_Props, {
714
+ context: vue15.ShallowRef<TresContext, TresContext>;
706
715
  dispose: () => void;
707
- }, {}, {}, {}, vue4.ComponentOptionsMixin, vue4.ComponentOptionsMixin, {
716
+ }, {}, {}, {}, vue15.ComponentOptionsMixin, vue15.ComponentOptionsMixin, {
708
717
  click: (event: TresPointerEvent) => any;
709
718
  contextmenu: (event: TresPointerEvent) => any;
710
719
  pointermove: (event: TresPointerEvent) => any;
@@ -718,13 +727,13 @@ declare const __VLS_base$1: vue4.DefineComponent<__VLS_Props, {
718
727
  pointercancel: (event: TresPointerEvent) => any;
719
728
  lostpointercapture: (event: TresPointerEvent) => any;
720
729
  wheel: (event: TresPointerEvent) => any;
721
- error: (error: Error) => any;
722
730
  ready: (context: TresContext) => any;
731
+ error: (error: Error) => any;
723
732
  pointermissed: (event: TresPointerEvent) => any;
724
733
  render: (context: TresContext) => any;
725
734
  beforeLoop: (context: TresContextWithClock) => any;
726
735
  loop: (context: TresContextWithClock) => any;
727
- }, string, vue4.PublicProps, Readonly<__VLS_Props> & Readonly<{
736
+ }, string, vue15.PublicProps, Readonly<__VLS_Props> & Readonly<{
728
737
  onClick?: ((event: TresPointerEvent) => any) | undefined;
729
738
  onContextmenu?: ((event: TresPointerEvent) => any) | undefined;
730
739
  onPointermove?: ((event: TresPointerEvent) => any) | undefined;
@@ -738,13 +747,13 @@ declare const __VLS_base$1: vue4.DefineComponent<__VLS_Props, {
738
747
  onPointercancel?: ((event: TresPointerEvent) => any) | undefined;
739
748
  onLostpointercapture?: ((event: TresPointerEvent) => any) | undefined;
740
749
  onWheel?: ((event: TresPointerEvent) => any) | undefined;
741
- onError?: ((error: Error) => any) | undefined;
742
750
  onReady?: ((context: TresContext) => any) | undefined;
751
+ onError?: ((error: Error) => any) | undefined;
743
752
  onPointermissed?: ((event: TresPointerEvent) => any) | undefined;
744
753
  onRender?: ((context: TresContext) => any) | undefined;
745
754
  onBeforeLoop?: ((context: TresContextWithClock) => any) | undefined;
746
755
  onLoop?: ((context: TresContextWithClock) => any) | undefined;
747
- }>, {}, {}, {}, {}, string, vue4.ComponentProvideOptions, false, {}, any>;
756
+ }>, {}, {}, {}, {}, string, vue15.ComponentProvideOptions, false, {}, any>;
748
757
  declare const __VLS_export$1: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
749
758
  declare const _default$1: typeof __VLS_export$1;
750
759
  type __VLS_WithSlots$1<T, S> = T & {
@@ -763,7 +772,7 @@ interface TresCanvasInstance {
763
772
  type __VLS_Slots = {
764
773
  default: () => any;
765
774
  };
766
- declare const __VLS_base: vue4.DefineComponent<ContextProps, TresCanvasInstance, {}, {}, {}, vue4.ComponentOptionsMixin, vue4.ComponentOptionsMixin, {
775
+ declare const __VLS_base: vue15.DefineComponent<ContextProps, TresCanvasInstance, {}, {}, {}, vue15.ComponentOptionsMixin, vue15.ComponentOptionsMixin, {
767
776
  click: (event: TresPointerEvent) => any;
768
777
  contextmenu: (event: TresPointerEvent) => any;
769
778
  pointermove: (event: TresPointerEvent) => any;
@@ -777,13 +786,13 @@ declare const __VLS_base: vue4.DefineComponent<ContextProps, TresCanvasInstance,
777
786
  pointercancel: (event: TresPointerEvent) => any;
778
787
  lostpointercapture: (event: TresPointerEvent) => any;
779
788
  wheel: (event: TresPointerEvent) => any;
780
- error: (error: Error) => any;
781
789
  ready: (context: TresContext) => any;
790
+ error: (error: Error) => any;
782
791
  pointermissed: (event: TresPointerEvent) => any;
783
792
  render: (context: TresContext) => any;
784
793
  beforeLoop: (context: TresContextWithClock) => any;
785
794
  loop: (context: TresContextWithClock) => any;
786
- }, string, vue4.PublicProps, Readonly<ContextProps> & Readonly<{
795
+ }, string, vue15.PublicProps, Readonly<ContextProps> & Readonly<{
787
796
  onClick?: ((event: TresPointerEvent) => any) | undefined;
788
797
  onContextmenu?: ((event: TresPointerEvent) => any) | undefined;
789
798
  onPointermove?: ((event: TresPointerEvent) => any) | undefined;
@@ -797,14 +806,15 @@ declare const __VLS_base: vue4.DefineComponent<ContextProps, TresCanvasInstance,
797
806
  onPointercancel?: ((event: TresPointerEvent) => any) | undefined;
798
807
  onLostpointercapture?: ((event: TresPointerEvent) => any) | undefined;
799
808
  onWheel?: ((event: TresPointerEvent) => any) | undefined;
800
- onError?: ((error: Error) => any) | undefined;
801
809
  onReady?: ((context: TresContext) => any) | undefined;
810
+ onError?: ((error: Error) => any) | undefined;
802
811
  onPointermissed?: ((event: TresPointerEvent) => any) | undefined;
803
812
  onRender?: ((context: TresContext) => any) | undefined;
804
813
  onBeforeLoop?: ((context: TresContextWithClock) => any) | undefined;
805
814
  onLoop?: ((context: TresContextWithClock) => any) | undefined;
806
815
  }>, {
807
816
  windowSize: boolean;
817
+ enableProvideBridge: boolean;
808
818
  antialias: boolean;
809
819
  stencil: boolean;
810
820
  depth: boolean;
@@ -819,8 +829,7 @@ declare const __VLS_base: vue4.DefineComponent<ContextProps, TresCanvasInstance,
819
829
  shadowMapType: THREE.ShadowMapType;
820
830
  useLegacyLights: boolean;
821
831
  renderMode: "always" | "on-demand" | "manual";
822
- enableProvideBridge: boolean;
823
- }, {}, {}, {}, string, vue4.ComponentProvideOptions, false, {}, any>;
832
+ }, {}, {}, {}, string, vue15.ComponentProvideOptions, false, {}, any>;
824
833
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
825
834
  declare const _default: typeof __VLS_export;
826
835
  type __VLS_WithSlots<T, S> = T & {
package/dist/tres.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.7.0
3
+ * version: v5.8.1
4
4
  * (c) 2026
5
5
  * description: Declarative ThreeJS using Vue Components
6
6
  * author: Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)
7
7
  */
8
- import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createRenderer, defineComponent, getCurrentInstance, h, isRef, mergeProps, nextTick, normalizeClass, normalizeStyle, onMounted, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, shallowRef, toValue, unref, useSlots, watch, watchEffect, withCtx } from "vue";
8
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createRenderer, defineComponent, getCurrentInstance, h, isRef, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onUnmounted, openBlock, provide, reactive, readonly, ref, renderSlot, shallowRef, toValue, unref, useSlots, watch, watchEffect, withCtx } from "vue";
9
9
  import * as THREE from "three";
10
10
  import { ACESFilmicToneMapping, ArrowHelper, BackSide, BufferAttribute, BufferGeometry, Color, DirectionalLightHelper, DoubleSide, Float32BufferAttribute, HemisphereLightHelper, Layers, Line, LineBasicMaterial, Material, MathUtils, Mesh, MeshBasicMaterial, PCFSoftShadowMap, PerspectiveCamera, PointLightHelper, REVISION, Scene, SpotLightHelper, Vector3, WebGLRenderer } from "three";
11
11
  import { createEventHook, createInjectionState, promiseTimeout, refDebounced, tryOnScopeDispose, unrefElement, useAsyncState, useDevicePixelRatio, useElementSize, useFps, useMemory, useRafFn, useTimeout, useWindowSize, whenever } from "@vueuse/core";
@@ -14,7 +14,7 @@ import { forwardHtmlEvents, getVoidObject } from "@pmndrs/pointer-events";
14
14
  import { setupDevtoolsPlugin } from "@vue/devtools-api";
15
15
 
16
16
  //#region package.json
17
- var version = "5.7.0";
17
+ var version = "5.8.1";
18
18
 
19
19
  //#endregion
20
20
  //#region src/utils/makeMap.ts
@@ -533,13 +533,20 @@ var component_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
533
533
  manager: {
534
534
  type: Object,
535
535
  required: false
536
+ },
537
+ extensions: {
538
+ type: Function,
539
+ required: false
536
540
  }
537
541
  },
538
542
  emits: ["loaded", "error"],
539
543
  setup(__props, { emit: __emit }) {
540
544
  const props = __props;
541
545
  const emit = __emit;
542
- const { state, isLoading, error } = useLoader(props.loader, props.path, { manager: props.manager });
546
+ const { state, isLoading, error } = useLoader(props.loader, props.path, {
547
+ manager: props.manager,
548
+ extensions: props.extensions
549
+ });
543
550
  whenever(error, (err) => {
544
551
  if (err) emit("error", err);
545
552
  });
@@ -763,7 +770,7 @@ function createTimer() {
763
770
  /**
764
771
  * @param cycleFn the function that is called before the after event hook is triggered and after the before event hook is triggered.
765
772
  */
766
- const useCreateRafLoop = (cycleFn) => {
773
+ const useCreateRafLoop = (cycleFn, { fpsLimit } = {}) => {
767
774
  const timer = createTimer();
768
775
  const eventHooks = {
769
776
  before: createEventHook(),
@@ -778,7 +785,10 @@ const useCreateRafLoop = (cycleFn) => {
778
785
  eventHooks.before.trigger(context);
779
786
  cycleFn();
780
787
  eventHooks.after.trigger(context);
781
- }, { immediate: false });
788
+ }, {
789
+ immediate: false,
790
+ fpsLimit
791
+ });
782
792
  const start = () => {
783
793
  timer.start();
784
794
  resume();
@@ -808,7 +818,7 @@ var TresRendererError = class extends Error {
808
818
 
809
819
  //#endregion
810
820
  //#region src/composables/useRenderer/useRendererManager.ts
811
- function useRendererManager({ scene, canvas, options, contextParts: { sizes, camera } }) {
821
+ function useRendererManager({ scene, canvas, options, fpsLimit, contextParts: { sizes, camera } }) {
812
822
  const getRenderer = () => {
813
823
  if (isFunction(options.renderer)) return options.renderer({
814
824
  sizes,
@@ -883,7 +893,7 @@ Error details: ${rendererError.message}`, rendererError);
883
893
  };
884
894
  const loop = useCreateRafLoop(() => {
885
895
  if (frames.value) renderFunction(notifyFrameRendered);
886
- });
896
+ }, { fpsLimit });
887
897
  readyEventHook.on(() => {
888
898
  if (isInitialized.value) loop.start();
889
899
  });
@@ -1009,7 +1019,7 @@ function useEventManager({ canvas, contextParts: { scene, camera, renderer } })
1009
1019
  //#endregion
1010
1020
  //#region src/composables/useTresContextProvider/index.ts
1011
1021
  const INJECTION_KEY = "useTres";
1012
- const [useTresContextProvider, _useTresContext] = createInjectionState(({ scene, canvas, windowSize, rendererOptions }) => {
1022
+ const [useTresContextProvider, _useTresContext] = createInjectionState(({ scene, canvas, fpsLimit, windowSize, rendererOptions }) => {
1013
1023
  const localScene = shallowRef(scene);
1014
1024
  const sizes = useSizes(windowSize, canvas);
1015
1025
  const camera = useCameraManager({ sizes });
@@ -1017,6 +1027,7 @@ const [useTresContextProvider, _useTresContext] = createInjectionState(({ scene,
1017
1027
  scene: localScene,
1018
1028
  canvas,
1019
1029
  options: rendererOptions,
1030
+ fpsLimit,
1020
1031
  contextParts: {
1021
1032
  sizes,
1022
1033
  camera
@@ -1393,6 +1404,15 @@ const nodeOps = ({ context, options = { primitivePrefix: "" } }) => {
1393
1404
  };
1394
1405
  };
1395
1406
 
1407
+ //#endregion
1408
+ //#region src/core/roots.ts
1409
+ const _roots = /* @__PURE__ */ new Map();
1410
+ const getRoot = (canvas) => _roots.get(canvas);
1411
+ const setRoot = (canvas, root) => {
1412
+ _roots.set(canvas, root);
1413
+ };
1414
+ const deleteRoot = (canvas) => _roots.delete(canvas);
1415
+
1396
1416
  //#endregion
1397
1417
  //#region src/devtools/DevtoolsMessenger.ts
1398
1418
  const QUEUEABLE_MESSAGE_TYPES = ["asset-load"];
@@ -1934,6 +1954,10 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
1934
1954
  type: Boolean,
1935
1955
  required: false
1936
1956
  },
1957
+ fpsLimit: {
1958
+ type: Number,
1959
+ required: false
1960
+ },
1937
1961
  enableProvideBridge: {
1938
1962
  type: Boolean,
1939
1963
  required: false
@@ -2063,7 +2087,7 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2063
2087
  const scene = shallowRef(new Scene());
2064
2088
  const instance = getCurrentInstance();
2065
2089
  extend(THREE);
2066
- const createInternalComponent = (context$1, empty = false) => defineComponent({ setup() {
2090
+ const createInternalComponent = (context$1, hmrTick) => defineComponent({ setup() {
2067
2091
  const ctx = getCurrentInstance()?.appContext;
2068
2092
  if (ctx) ctx.app = instance?.appContext.app;
2069
2093
  const provides = {};
@@ -2081,15 +2105,27 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2081
2105
  provide(INJECTION_KEY, context$1);
2082
2106
  provide("extend", extend);
2083
2107
  if (typeof window !== "undefined" && ctx?.app) registerTresDevtools(ctx?.app, context$1);
2084
- return () => h(Fragment, null, !empty ? slots.default() : []);
2108
+ return () => {
2109
+ hmrTick.value;
2110
+ return h(Fragment, null, slots.default?.() ?? []);
2111
+ };
2085
2112
  } });
2086
- const mountCustomRenderer = (context$1, empty = false) => {
2087
- const InternalComponent = createInternalComponent(context$1, empty);
2088
- const { render } = createRenderer(nodeOps({
2113
+ const mountCustomRenderer = (context$1) => {
2114
+ const canvas = props.canvas;
2115
+ if (getRoot(canvas)) return;
2116
+ const hmrTick = ref(0);
2117
+ const internalComponent = createInternalComponent(context$1, hmrTick);
2118
+ const renderer$1 = createRenderer(nodeOps({
2089
2119
  context: context$1,
2090
2120
  options: props.customRendererOptions
2091
2121
  }));
2092
- render(h(InternalComponent), scene.value);
2122
+ renderer$1.render(h(internalComponent), scene.value);
2123
+ setRoot(canvas, {
2124
+ renderer: renderer$1,
2125
+ internalComponent,
2126
+ hmrTick,
2127
+ context: context$1
2128
+ });
2093
2129
  };
2094
2130
  const dispose = (context$1, force = false) => {
2095
2131
  disposeObject3D(context$1.scene.value);
@@ -2109,6 +2145,7 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2109
2145
  const context = shallowRef(useTresContextProvider({
2110
2146
  scene: scene.value,
2111
2147
  canvas: props.canvas,
2148
+ fpsLimit: () => props.fpsLimit ?? Infinity,
2112
2149
  windowSize: props.windowSize ?? false,
2113
2150
  rendererOptions: props
2114
2151
  }));
@@ -2116,14 +2153,18 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2116
2153
  context,
2117
2154
  dispose: () => dispose(context.value, true)
2118
2155
  });
2119
- const handleHMR = (context$1) => {
2120
- mountCustomRenderer(context$1);
2156
+ const handleHMR = () => {
2157
+ const root = getRoot(props.canvas);
2158
+ if (root) root.hmrTick.value++;
2121
2159
  };
2122
2160
  const unmountCanvas = () => {
2161
+ const root = getRoot(props.canvas);
2162
+ if (!root) return;
2123
2163
  const isTresScene = (value) => isScene(value) && "__tres" in value;
2124
2164
  if (isTresScene(scene.value)) scene.value.__tres.isUnmounting = true;
2125
- mountCustomRenderer(context.value, true);
2165
+ root.renderer.render(null, scene.value);
2126
2166
  dispose(context.value);
2167
+ deleteRoot(props.canvas);
2127
2168
  };
2128
2169
  const { camera, renderer } = context.value;
2129
2170
  const { registerCamera, cameras, activeCamera, deregisterCamera } = camera;
@@ -2166,14 +2207,20 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2166
2207
  });
2167
2208
  });
2168
2209
  renderer.onReady(() => {
2169
- mountCustomRenderer(context.value, false);
2210
+ mountCustomRenderer(context.value);
2170
2211
  emit("ready", context.value);
2171
2212
  if (!activeCamera.value) addDefaultCamera();
2172
2213
  });
2173
2214
  renderer.onError((error) => {
2174
2215
  emit("error", error);
2175
2216
  });
2176
- if (import.meta.hot) import.meta.hot.on("vite:afterUpdate", () => handleHMR(context.value));
2217
+ if (import.meta.hot) {
2218
+ const hmrHandler = () => handleHMR();
2219
+ import.meta.hot.on("vite:afterUpdate", hmrHandler);
2220
+ onBeforeUnmount(() => {
2221
+ import.meta.hot?.off?.("vite:afterUpdate", hmrHandler);
2222
+ });
2223
+ }
2177
2224
  onMounted(async () => {
2178
2225
  await promiseTimeout(3e3);
2179
2226
  if (!context.value.sizes.width || !context.value.sizes.height.value) console.warn(`TresCanvas: The canvas has no area, so nothing can be rendered. Set it manually on the parent element or use the prop windowSize.`);
@@ -2202,6 +2249,10 @@ var TresCanvas_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
2202
2249
  required: false,
2203
2250
  default: void 0
2204
2251
  },
2252
+ fpsLimit: {
2253
+ type: Number,
2254
+ required: false
2255
+ },
2205
2256
  enableProvideBridge: {
2206
2257
  type: Boolean,
2207
2258
  required: false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tresjs/core",
3
3
  "type": "module",
4
- "version": "5.7.0",
4
+ "version": "5.8.1",
5
5
  "description": "Declarative ThreeJS using Vue Components",
6
6
  "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
7
7
  "license": "MIT",
@@ -77,7 +77,7 @@
77
77
  "vue": "^3.5.22",
78
78
  "vue-demi": "^0.14.10",
79
79
  "vue-tsc": "^3.2.1",
80
- "@tresjs/eslint-config": "1.5.1"
80
+ "@tresjs/eslint-config": "1.6.0"
81
81
  },
82
82
  "nx": {
83
83
  "implicitDependencies": [