@tresjs/core 5.8.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.8.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.8.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,6 +1,6 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.8.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/)
@@ -528,6 +528,7 @@ declare const __VLS_export$2: <T extends TresObjectMap>(__VLS_props: NonNullable
528
528
  * Optional THREE.js LoadingManager
529
529
  */
530
530
  manager?: LoadingManager;
531
+ extensions?: (res: T) => void;
531
532
  } & {
532
533
  onError?: ((error: unknown) => any) | undefined;
533
534
  onLoaded?: ((result: T) => any) | undefined;
package/dist/tres.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * name: @tresjs/core
3
- * version: v5.8.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.8.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
  });
@@ -1397,6 +1404,15 @@ const nodeOps = ({ context, options = { primitivePrefix: "" } }) => {
1397
1404
  };
1398
1405
  };
1399
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
+
1400
1416
  //#endregion
1401
1417
  //#region src/devtools/DevtoolsMessenger.ts
1402
1418
  const QUEUEABLE_MESSAGE_TYPES = ["asset-load"];
@@ -2071,7 +2087,7 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2071
2087
  const scene = shallowRef(new Scene());
2072
2088
  const instance = getCurrentInstance();
2073
2089
  extend(THREE);
2074
- const createInternalComponent = (context$1, empty = false) => defineComponent({ setup() {
2090
+ const createInternalComponent = (context$1, hmrTick) => defineComponent({ setup() {
2075
2091
  const ctx = getCurrentInstance()?.appContext;
2076
2092
  if (ctx) ctx.app = instance?.appContext.app;
2077
2093
  const provides = {};
@@ -2089,15 +2105,27 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2089
2105
  provide(INJECTION_KEY, context$1);
2090
2106
  provide("extend", extend);
2091
2107
  if (typeof window !== "undefined" && ctx?.app) registerTresDevtools(ctx?.app, context$1);
2092
- return () => h(Fragment, null, !empty ? slots.default() : []);
2108
+ return () => {
2109
+ hmrTick.value;
2110
+ return h(Fragment, null, slots.default?.() ?? []);
2111
+ };
2093
2112
  } });
2094
- const mountCustomRenderer = (context$1, empty = false) => {
2095
- const InternalComponent = createInternalComponent(context$1, empty);
2096
- 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({
2097
2119
  context: context$1,
2098
2120
  options: props.customRendererOptions
2099
2121
  }));
2100
- 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
+ });
2101
2129
  };
2102
2130
  const dispose = (context$1, force = false) => {
2103
2131
  disposeObject3D(context$1.scene.value);
@@ -2125,14 +2153,18 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2125
2153
  context,
2126
2154
  dispose: () => dispose(context.value, true)
2127
2155
  });
2128
- const handleHMR = (context$1) => {
2129
- mountCustomRenderer(context$1);
2156
+ const handleHMR = () => {
2157
+ const root = getRoot(props.canvas);
2158
+ if (root) root.hmrTick.value++;
2130
2159
  };
2131
2160
  const unmountCanvas = () => {
2161
+ const root = getRoot(props.canvas);
2162
+ if (!root) return;
2132
2163
  const isTresScene = (value) => isScene(value) && "__tres" in value;
2133
2164
  if (isTresScene(scene.value)) scene.value.__tres.isUnmounting = true;
2134
- mountCustomRenderer(context.value, true);
2165
+ root.renderer.render(null, scene.value);
2135
2166
  dispose(context.value);
2167
+ deleteRoot(props.canvas);
2136
2168
  };
2137
2169
  const { camera, renderer } = context.value;
2138
2170
  const { registerCamera, cameras, activeCamera, deregisterCamera } = camera;
@@ -2175,14 +2207,20 @@ var Context_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
2175
2207
  });
2176
2208
  });
2177
2209
  renderer.onReady(() => {
2178
- mountCustomRenderer(context.value, false);
2210
+ mountCustomRenderer(context.value);
2179
2211
  emit("ready", context.value);
2180
2212
  if (!activeCamera.value) addDefaultCamera();
2181
2213
  });
2182
2214
  renderer.onError((error) => {
2183
2215
  emit("error", error);
2184
2216
  });
2185
- 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
+ }
2186
2224
  onMounted(async () => {
2187
2225
  await promiseTimeout(3e3);
2188
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.`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tresjs/core",
3
3
  "type": "module",
4
- "version": "5.8.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": [