@vctrl/hooks 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,54 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.25.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.24.1...hooks-v0.25.0) (2026-08-04)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **packages:** match repository.url org casing for npm provenance ([455caa9](https://github.com/Vectreal/vectreal-platform/commit/455caa927ff7a668ea2b6af6a633d9057d69f203))
9
+ * **packages:** match repository.url org casing for npm provenance ([7289fdc](https://github.com/Vectreal/vectreal-platform/commit/7289fdcbc5678588acccaf5b650d62a4d70a7e10))
10
+
11
+
12
+ ### Dependencies
13
+
14
+ * The following workspace dependencies were updated
15
+ * dependencies
16
+ * @vctrl/core bumped to 0.25.0
17
+
18
+ ## [0.24.1](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.24.0...hooks-v0.24.1) (2026-08-02)
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * **ci:** resolve workspace protocol at publish time and publish via OIDC ([07e4b62](https://github.com/Vectreal/vectreal-platform/commit/07e4b628be16b61f3ddea9c91cc36c052d60bc8f))
24
+ * **ci:** resolve workspace protocol at publish time and publish via OIDC ([03b9305](https://github.com/Vectreal/vectreal-platform/commit/03b9305719b1cf67548f62730493ca6e3a84c823))
25
+
26
+
27
+ ### Dependencies
28
+
29
+ * The following workspace dependencies were updated
30
+ * dependencies
31
+ * @vctrl/core bumped to 0.24.1
32
+
33
+ ## [0.24.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.23.0...hooks-v0.24.0) (2026-08-01)
34
+
35
+
36
+ ### Features
37
+
38
+ * **publisher:** draco-first optimization pipeline and panel rework ([#652](https://github.com/Vectreal/vectreal-platform/issues/652)) ([1161236](https://github.com/Vectreal/vectreal-platform/commit/1161236d0f4b2461b6b4af1ea4069fdf9da6d9c2))
39
+
40
+
41
+ ### Bug Fixes
42
+
43
+ * **publisher:** scene size inflation and stale model reloads ([#653](https://github.com/Vectreal/vectreal-platform/issues/653)) ([9c5da02](https://github.com/Vectreal/vectreal-platform/commit/9c5da02f3f524337b5d715ff5454ac4687fc73e3))
44
+
45
+
46
+ ### Dependencies
47
+
48
+ * The following workspace dependencies were updated
49
+ * dependencies
50
+ * @vctrl/core bumped to 0.24.0
51
+
3
52
  ## [0.23.0](https://github.com/Vectreal/vectreal-platform/compare/hooks-v0.22.0...hooks-v0.23.0) (2026-07-03)
4
53
 
5
54
 
package/README.md CHANGED
@@ -120,7 +120,7 @@ export default function App() {
120
120
 
121
121
  ## `useOptimizeModel`
122
122
 
123
- Runs mesh simplification, deduplication, quantization, and normals optimization using [glTF-Transform](https://gltf-transform.dev) — geometry passes run in a Web Worker. Texture compression runs in the main thread via browser-native OffscreenCanvas encoding.
123
+ Runs mesh simplification, deduplication, quantization, and normals optimization using [glTF-Transform](https://gltf-transform.dev), plus texture compression via browser-native OffscreenCanvas encoding. Every pass runs on the calling thread: this package spawns no Web Worker. If you need the work off the main thread, run the hook inside a worker you own, as the Vectreal Platform does.
124
124
 
125
125
  ```tsx
126
126
  import { useOptimizeModel } from '@vctrl/hooks/use-optimize-model'
@@ -152,7 +152,6 @@ function Optimizer() {
152
152
  | ------------------------------------ | ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
153
153
  | `load(model)` | `(model: Object3D) => Promise<void>` | Load a Three.js scene into the optimizer |
154
154
  | `loadFromServerSceneData(sceneData)` | `Promise<void>` | Initialize optimizer from a server scene payload |
155
- | `applyOptimization(fn, opts?)` | `<T>(fn?: (opts?: T) => Promise<void>, opts?: T) => Promise<void>` | Apply optimization and sync the optimized model back into loader state |
156
155
  | `simplifyOptimization(options?)` | `Promise<void>` | Simplify mesh geometry |
157
156
  | `dedupOptimization(options?)` | `Promise<void>` | Deduplicate model data |
158
157
  | `quantizeOptimization(options?)` | `Promise<void>` | Quantize vertex attributes |
@@ -162,6 +161,8 @@ function Optimizer() {
162
161
  | `report` / `info` | Objects | Optimization metrics and derived stats |
163
162
  | `loading` / `error` | State | Optimization status |
164
163
 
164
+ > `applyOptimization(fn, opts?)` is **not** returned by `useOptimizeModel` directly. It becomes available on the `optimizer` object returned by `useLoadModel(optimizer)` when you pass an optimizer instance in; it runs an optimization step and syncs the result back into loader state (see the example above).
165
+
165
166
  ### Optimization option types
166
167
 
167
168
  `useOptimizeModel` methods map directly to `@vctrl/core/model-optimizer` option types:
@@ -180,7 +181,7 @@ Texture compression runs browser-native via `OffscreenCanvas`. No server call is
180
181
 
181
182
  ## `useExportModel`
182
183
 
183
- Exports the current scene from `ModelProvider` context to a downloadable file.
184
+ Exports a `ModelFile` to a downloadable file. The hook takes optional `onSaved` and `onError` callbacks; the model itself is passed to the export method as an argument, not read from `ModelProvider` context.
184
185
 
185
186
  ```tsx
186
187
  import { useExportModel } from '@vctrl/hooks/use-export-model'
@@ -218,11 +219,10 @@ function ExportButton({ file }: { file: ModelFile | null }) {
218
219
 
219
220
  ## Peer dependencies
220
221
 
221
- | Package | Version |
222
- | -------------------- | -------------- |
223
- | `react` | `^18 \|\| ^19` |
224
- | `three` | `^0.170` |
225
- | `@react-three/fiber` | `^9` |
222
+ | Package | Version |
223
+ | ------- | -------------- |
224
+ | `react` | `^18 \|\| ^19` |
225
+ | `three` | `^0.177` |
226
226
 
227
227
  ---
228
228
 
package/index.cjs.js CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-load-model-B8oqou4I.cjs"),t=require("./use-export-model-8JbUalyf.cjs"),n=require("./use-optimize-model-Jie2FUHc.cjs");exports.reconstructGltfFiles=e.r,exports.useExportModel=t.t,exports.useLoadModel=e.t,exports.useOptimizeModel=n.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-load-model-B8oqou4I.cjs"),t=require("./use-export-model-yQWV3O3H.cjs"),n=require("./use-optimize-model-CD5zqTLK.cjs");exports.reconstructGltfFiles=e.r,exports.useExportModel=t.t,exports.useLoadModel=e.t,exports.useOptimizeModel=n.t;
package/index.es.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { r as e, t } from "./use-load-model-C88r8-HE.js";
2
- import { t as n } from "./use-export-model-BXg6L_W5.js";
3
- import { t as r } from "./use-optimize-model-CweunXiv.js";
2
+ import { t as n } from "./use-export-model-efFsSWe-.js";
3
+ import { t as r } from "./use-optimize-model-D16-4Zwm.js";
4
4
  export { e as reconstructGltfFiles, n as useExportModel, t as useLoadModel, r as useOptimizeModel };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
- "version": "0.23.0",
2
+ "version": "0.25.0",
3
3
  "name": "@vctrl/hooks",
4
4
  "description": "vctrl/hooks is a React hooks package designed to simplify 3D model loading and management within React applications. It's part of the vectreal-core ecosystem and is primarily used in the vctrl/viewer React component and the official website application.",
5
5
  "bugs": {
6
- "url": "https://github.com/vectreal/vectreal-platform/issues"
6
+ "url": "https://github.com/Vectreal/vectreal-platform/issues"
7
7
  },
8
8
  "homepage": "https://vectreal.com",
9
9
  "keywords": [
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "repository": {
27
27
  "type": "git",
28
- "url": "git+https://github.com/vectreal/vectreal-platform.git"
28
+ "url": "git+https://github.com/Vectreal/vectreal-platform.git"
29
29
  },
30
30
  "license": "AGPL-3.0-only",
31
31
  "types": "./index.d.ts",
@@ -62,13 +62,7 @@
62
62
  "three": "^0.177.0"
63
63
  },
64
64
  "dependencies": {
65
- "@vctrl/core": "^0.23.0",
65
+ "@vctrl/core": "^0.25.0",
66
66
  "file-saver": "^2.0.5"
67
- },
68
- "devDependencies": {
69
- "@nx/vite": "22.5.0",
70
- "@vitejs/plugin-react": "^6.0.0",
71
- "vite": "^7.3.1",
72
- "vite-plugin-dts": "4.5.3"
73
67
  }
74
68
  }
@@ -10,5 +10,7 @@ import { ModelFile } from '../use-load-model';
10
10
  declare const useExportModel: (onSaved?: () => void, onError?: (error: Error) => void) => {
11
11
  handleThreeGltfExport: (file: ModelFile | null, binary: boolean) => Promise<void>;
12
12
  handleDocumentGltfExport: (jsonDocument: Document, file: ModelFile | null, binary?: boolean, download?: boolean) => Promise<import('../../../core/src/model-exporter/index.ts').GLTFExportResult | undefined>;
13
+ handleThreeUsdzExport: (file: ModelFile | null) => Promise<void>;
14
+ handleDocumentGlbDracoExport: (jsonDocument: Document, file: ModelFile | null) => Promise<void>;
13
15
  };
14
16
  export default useExportModel;
@@ -0,0 +1,70 @@
1
+ import { useCallback as e, useRef as t } from "react";
2
+ import { ModelExporter as n } from "@vctrl/core/model-exporter";
3
+ import r from "file-saver";
4
+ //#region packages/hooks/src/use-export-model/use-export-model.ts
5
+ var i = (i, a) => {
6
+ let o = t(new n()), s = e(async (e, t) => {
7
+ let n = e?.model;
8
+ if (!n) {
9
+ console.error("Scene not initialized");
10
+ return;
11
+ }
12
+ try {
13
+ let a = o.current, s = e.name.replace(/\.[^/.]+$/, "");
14
+ if (t) {
15
+ let e = await a.exportThreeJSGLB(n, {});
16
+ if (!(e.data instanceof Uint8Array)) throw Error("Expected GLB export to return Uint8Array");
17
+ r.saveAs(new Blob([new Uint8Array(e.data)]), `${s}.glb`);
18
+ } else {
19
+ let e = await a.exportThreeJSGLTF(n), t = await a.createZIPArchive(e, s);
20
+ r.saveAs(new Blob([new Uint8Array(t)]), `${s}.zip`);
21
+ }
22
+ i && i();
23
+ } catch (e) {
24
+ console.error("Export failed:", e), a && a(e);
25
+ }
26
+ }, [i, a]), c = e(async (e) => {
27
+ let t = e?.model;
28
+ if (!t) {
29
+ console.error("Scene not initialized");
30
+ return;
31
+ }
32
+ try {
33
+ let n = o.current, a = e.name.replace(/\.[^/.]+$/, ""), s = await n.exportThreeJSUSDZ(t);
34
+ r.saveAs(new Blob([new Uint8Array(s.data)]), `${a}.usdz`), i && i();
35
+ } catch (e) {
36
+ console.error("USDZ export failed:", e), a && a(e);
37
+ }
38
+ }, [i, a]), l = e(async (e, t) => {
39
+ try {
40
+ let n = o.current, a = t?.name.replace(/\.[^/.]+$/, "") || "model", s = await n.exportDocumentGLBDraco(e);
41
+ r.saveAs(new Blob([new Uint8Array(s.data)]), `${a}.glb`), i && i();
42
+ } catch (e) {
43
+ console.error("Draco GLB export failed:", e), a && a(e);
44
+ }
45
+ }, [i, a]);
46
+ return {
47
+ handleThreeGltfExport: s,
48
+ handleDocumentGltfExport: e(async function(e, t, n = !1, s = !0) {
49
+ try {
50
+ let a = o.current, c = t?.name.replace(/\.[^/.]+$/, "") || "model";
51
+ if (n) {
52
+ let t = await a.exportDocumentGLB(e);
53
+ r.saveAs(new Blob([new Uint8Array(t.data)]), `${c}.glb`);
54
+ } else {
55
+ let t = await a.exportDocumentGLTF(e);
56
+ if (!s) return t;
57
+ let n = await a.createZIPArchive(t, c);
58
+ r.saveAs(new Blob([new Uint8Array(n)]), `${c}.zip`);
59
+ }
60
+ i && i();
61
+ } catch (e) {
62
+ console.error("Export failed:", e), a && a(e);
63
+ }
64
+ }, [a, i]),
65
+ handleThreeUsdzExport: c,
66
+ handleDocumentGlbDracoExport: l
67
+ };
68
+ };
69
+ //#endregion
70
+ export { i as t };
@@ -0,0 +1 @@
1
+ const e=require("./chunk-CMqjfN_6.cjs");let t=require("react"),n=require("@vctrl/core/model-exporter"),r=require("file-saver");r=e.t(r,1);var i=(e,i)=>{let a=(0,t.useRef)(new n.ModelExporter),o=(0,t.useCallback)(async(t,n)=>{let o=t?.model;if(!o){console.error(`Scene not initialized`);return}try{let i=a.current,s=t.name.replace(/\.[^/.]+$/,``);if(n){let e=await i.exportThreeJSGLB(o,{});if(!(e.data instanceof Uint8Array))throw Error(`Expected GLB export to return Uint8Array`);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${s}.glb`)}else{let e=await i.exportThreeJSGLTF(o),t=await i.createZIPArchive(e,s);r.default.saveAs(new Blob([new Uint8Array(t)]),`${s}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[e,i]),s=(0,t.useCallback)(async t=>{let n=t?.model;if(!n){console.error(`Scene not initialized`);return}try{let i=a.current,o=t.name.replace(/\.[^/.]+$/,``),s=await i.exportThreeJSUSDZ(n);r.default.saveAs(new Blob([new Uint8Array(s.data)]),`${o}.usdz`),e&&e()}catch(e){console.error(`USDZ export failed:`,e),i&&i(e)}},[e,i]),c=(0,t.useCallback)(async(t,n)=>{try{let i=a.current,o=n?.name.replace(/\.[^/.]+$/,``)||`model`,s=await i.exportDocumentGLBDraco(t);r.default.saveAs(new Blob([new Uint8Array(s.data)]),`${o}.glb`),e&&e()}catch(e){console.error(`Draco GLB export failed:`,e),i&&i(e)}},[e,i]);return{handleThreeGltfExport:o,handleDocumentGltfExport:(0,t.useCallback)(async function(t,n,o=!1,s=!0){try{let i=a.current,c=n?.name.replace(/\.[^/.]+$/,``)||`model`;if(o){let e=await i.exportDocumentGLB(t);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${c}.glb`)}else{let e=await i.exportDocumentGLTF(t);if(!s)return e;let n=await i.createZIPArchive(e,c);r.default.saveAs(new Blob([new Uint8Array(n)]),`${c}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[i,e]),handleThreeUsdzExport:s,handleDocumentGlbDracoExport:c}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return i}});
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-export-model-8JbUalyf.cjs");exports.useExportModel=e.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-export-model-yQWV3O3H.cjs");exports.useExportModel=e.t;
@@ -1,2 +1,2 @@
1
- import { t as e } from "./use-export-model-BXg6L_W5.js";
1
+ import { t as e } from "./use-export-model-efFsSWe-.js";
2
2
  export { e as useExportModel };
@@ -1,6 +1,11 @@
1
1
  import { DedupOptions, NormalsOptions, QuantizeOptions, SimplifyOptions, TextureCompressOptions } from '../../../core/src/model-optimizer/index.ts';
2
2
  import { Object3D } from 'three';
3
- import { ServerSceneData } from '../../../core/src/index.ts';
3
+ import { DracoCompressionReport, ServerSceneData } from '../../../core/src/index.ts';
4
+ /** Optimizer state produced inside the geometry Web Worker. */
5
+ export interface WorkerResultMeta {
6
+ appliedOptimizations: string[];
7
+ dracoReport?: DracoCompressionReport;
8
+ }
4
9
  /**
5
10
  * Custom React hook for optimizing 3D models using the ModelOptimizer from @vctrl/core.
6
11
  *
@@ -42,7 +47,7 @@ declare const useOptimizeModel: () => {
42
47
  */
43
48
  load: (model: Object3D) => Promise<void>;
44
49
  loadFromServerSceneData: (sceneData: ServerSceneData) => Promise<void>;
45
- loadFromGlbBuffer: (buffer: Uint8Array) => Promise<void>;
50
+ loadFromGlbBuffer: (buffer: Uint8Array, meta?: WorkerResultMeta) => Promise<void>;
46
51
  loadFromGLTFWithAssets: (gltfBytes: Uint8Array, assets: Map<string, Uint8Array>) => Promise<void>;
47
52
  /**
48
53
  * Retrieves the current model as a binary Uint8Array in glTF (.glb) format.
@@ -1 +1 @@
1
- let e=require("@vctrl/core"),t=require("react"),n=require("@vctrl/core/model-optimizer");var r={info:null,error:null,loading:!1,report:null},i=(e,t)=>{switch(t.type){case`LOAD_START`:return{...e,loading:!0,error:null};case`LOAD_SUCCESS`:return{...e,loading:!1,report:t.payload.report};case`LOAD_ERROR`:return{...e,loading:!1,error:t.payload};case`RESET`:return{...r};default:return e}};function a(e){return e?{verticesCount:e.stats.vertices.after??0,primitivesCount:e.stats.triangles.after??0,meshesCount:e.stats.meshes.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materials.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var o=e=>{let[n,r]=(0,t.useState)(null),i=e.report;return(0,t.useEffect)(()=>{!n&&i&&r(i)},[n,i]),{info:(0,t.useMemo)(()=>{let e=a(n),t=a(i);return{initial:{verticesCount:e.verticesCount,primitivesCount:e.primitivesCount,meshesCount:e.meshesCount,texturesCount:e.textureAssetCount,sceneBytes:e.sceneBytes},optimized:{verticesCount:t.verticesCount,primitivesCount:t.primitivesCount,meshesCount:t.meshesCount,texturesCount:t.textureAssetCount,sceneBytes:t.sceneBytes},improvement:{verticesCount:e.verticesCount-t.verticesCount,primitivesCount:e.primitivesCount-t.primitivesCount,meshesCount:e.meshesCount-t.meshesCount,texturesCount:e.textureAssetCount-t.textureAssetCount,sceneBytes:e.sceneBytes-t.sceneBytes}}},[i,n]),reset:(0,t.useCallback)(()=>{r(null)},[])}};async function s(e,t){let n=new Blob([e]),r=await createImageBitmap(n),{width:i,height:a}=r;if(t.maxWidth||t.maxHeight){let e=t.maxWidth?t.maxWidth/i:1/0,n=t.maxHeight?t.maxHeight/a:1/0,r=Math.min(e,n,1);r<1&&(i=Math.round(i*r),a=Math.round(a*r))}let o=new OffscreenCanvas(i,a),s=o.getContext(`2d`);if(!s)throw r.close(),Error(`OffscreenCanvas 2D context unavailable`);s.drawImage(r,0,0,i,a),r.close();let c=t.format===`webp`?`image/webp`:t.format===`jpeg`?`image/jpeg`:`image/png`,l=await o.convertToBlob({type:c,quality:t.quality/100});return new Uint8Array(await l.arrayBuffer())}function c(){return function(e){let t=e instanceof Uint8Array?e:new Uint8Array(e),n=`webp`,r=80,i,a,o={resize(e,t,n){return i=e,a=t,o},webp(e){return n=`webp`,typeof e?.quality==`number`&&(r=e.quality),o},jpeg(e){return n=`jpeg`,typeof e?.quality==`number`&&(r=e.quality),o},png(e){return n=`png`,o},toFormat(e,t){return(e===`webp`||e===`jpeg`||e===`png`)&&(n=e),typeof t?.quality==`number`&&(r=t.quality),o},async toBuffer(){return s(t,{format:n,quality:r,maxWidth:i,maxHeight:a})},async metadata(){let e=new Blob([t]),n=await createImageBitmap(e),r={width:n.width,height:n.height,format:`unknown`};return n.close(),r}};return o}}var l=async(e,t)=>{e.hasModel()&&await e.compressTextures({...t,encoder:c()})},u=()=>{let[a,s]=(0,t.useReducer)(i,r),{report:c,error:u,loading:d}=a,{info:f,reset:p}=o(a),m=(0,t.useRef)(new n.ModelOptimizer);(0,t.useEffect)(()=>{let e=m.current;return()=>{e.reset()}},[]);let h=(0,t.useCallback)(async e=>{s({type:`LOAD_START`});try{let t=m.current;await t.loadFromThreeJS(e),s({type:`LOAD_SUCCESS`,payload:{report:await t.getReport()}})}catch(e){s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),g=(0,t.useCallback)(async t=>{s({type:`LOAD_START`});try{let n=m.current,r=new Map;for(let n of Object.values(t.assetData??{}))for(let t of(0,e.buildAssetLookupKeys)(n.fileName))r.set(t,n);let i={},a=t.gltfJson,o=[...(a.images??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`)),...(a.buffers??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`))];for(let t of o){let n=(0,e.normalizeAssetUri)(t),a=n.split(`/`).pop()||n,o=r.get(t)??r.get(n)??r.get(a);o&&(i[t]=(0,e.toSerializedAssetBytes)(o))}let c={json:t.gltfJson,resources:i};await n.loadFromJSON(c),s({type:`LOAD_SUCCESS`,payload:{report:await n.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),_=(0,t.useCallback)(async e=>{s({type:`LOAD_START`});try{await m.current.loadFromBuffer(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLB buffer into optimizer:`,e),e}},[]),v=(0,t.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromGLTFWithAssets(e,t),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLTF with assets into optimizer:`,e),e}},[]),y=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.simplify(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Simplification failed:`,e)}},[]),b=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.deduplicate(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Deduplication failed:`,e)}},[]),x=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.quantize(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Quantization failed:`,e)}},[]),S=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.optimizeNormals(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Normals optimization failed:`,e)}},[]),C=(0,t.useCallback)(async e=>{try{await l(m.current,e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw console.error(`Texture optimization failed:`,e),e}},[]),w=(0,t.useCallback)(async()=>{if(!m.current.hasModel())return null;try{return await m.current.export()}catch(e){return console.error(`Error getting model binary:`,e),null}},[]),T=(0,t.useCallback)(()=>m.current.hasModel()?m.current.document:null,[]);return{load:h,loadFromServerSceneData:g,loadFromGlbBuffer:_,loadFromGLTFWithAssets:v,getModel:w,isReady:!!T()&&!d,_getDocument:T,reset:(0,t.useCallback)(()=>{m.current.reset(),s({type:`RESET`}),p()},[p]),error:u,loading:d,report:c,info:f,simplifyOptimization:y,dedupOptimization:b,quantizeOptimization:x,normalsOptimization:S,texturesOptimization:C}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
1
+ let e=require("@vctrl/core"),t=require("react"),n=require("@vctrl/core/model-optimizer");var r={info:null,error:null,loading:!1,report:null},i=(e,t)=>{switch(t.type){case`LOAD_START`:return{...e,loading:!0,error:null};case`LOAD_SUCCESS`:return{...e,loading:!1,report:t.payload.report};case`LOAD_ERROR`:return{...e,loading:!1,error:t.payload};case`RESET`:return{...r};default:return e}};function a(e){return e?{verticesCount:e.stats.vertices.after??0,primitivesCount:e.stats.triangles.after??0,meshesCount:e.stats.meshes.after??0,textureAssetCount:e.stats.texturesCount.after??0,materialsCount:e.stats.materials.after??0,sceneBytes:e.optimizedSize??0}:{verticesCount:0,primitivesCount:0,meshesCount:0,textureAssetCount:0,materialsCount:0,sceneBytes:0}}var o=e=>{let[n,r]=(0,t.useState)(null),i=e.report;return(0,t.useEffect)(()=>{!n&&i&&r(i)},[n,i]),{info:(0,t.useMemo)(()=>{let e=a(n),t=a(i);return{initial:{verticesCount:e.verticesCount,primitivesCount:e.primitivesCount,meshesCount:e.meshesCount,texturesCount:e.textureAssetCount,sceneBytes:e.sceneBytes},optimized:{verticesCount:t.verticesCount,primitivesCount:t.primitivesCount,meshesCount:t.meshesCount,texturesCount:t.textureAssetCount,sceneBytes:t.sceneBytes},improvement:{verticesCount:e.verticesCount-t.verticesCount,primitivesCount:e.primitivesCount-t.primitivesCount,meshesCount:e.meshesCount-t.meshesCount,texturesCount:e.textureAssetCount-t.textureAssetCount,sceneBytes:e.sceneBytes-t.sceneBytes}}},[i,n]),reset:(0,t.useCallback)(()=>{r(null)},[])}};async function s(e,t){let n=new Blob([e]),r=await createImageBitmap(n),{width:i,height:a}=r;if(t.maxWidth||t.maxHeight){let e=t.maxWidth?t.maxWidth/i:1/0,n=t.maxHeight?t.maxHeight/a:1/0,r=Math.min(e,n,1);r<1&&(i=Math.round(i*r),a=Math.round(a*r))}let o=new OffscreenCanvas(i,a),s=o.getContext(`2d`);if(!s)throw r.close(),Error(`OffscreenCanvas 2D context unavailable`);s.drawImage(r,0,0,i,a),r.close();let c=t.format===`webp`?`image/webp`:t.format===`jpeg`?`image/jpeg`:`image/png`,l=await o.convertToBlob({type:c,quality:t.quality/100});return new Uint8Array(await l.arrayBuffer())}function c(){return function(e){let t=e instanceof Uint8Array?e:new Uint8Array(e),n=`webp`,r=80,i,a,o={resize(e,t,n){return i=e,a=t,o},webp(e){return n=`webp`,typeof e?.quality==`number`&&(r=e.quality),o},jpeg(e){return n=`jpeg`,typeof e?.quality==`number`&&(r=e.quality),o},png(e){return n=`png`,o},toFormat(e,t){return(e===`webp`||e===`jpeg`||e===`png`)&&(n=e),typeof t?.quality==`number`&&(r=t.quality),o},async toBuffer(){return s(t,{format:n,quality:r,maxWidth:i,maxHeight:a})},async metadata(){let e=new Blob([t]),n=await createImageBitmap(e),r={width:n.width,height:n.height,format:`unknown`};return n.close(),r}};return o}}var l=async(e,t)=>{e.hasModel()&&await e.compressTextures({...t,encoder:c()})},u=()=>{let[a,s]=(0,t.useReducer)(i,r),{report:c,error:u,loading:d}=a,{info:f,reset:p}=o(a),m=(0,t.useRef)(new n.ModelOptimizer);(0,t.useEffect)(()=>{let e=m.current;return()=>{e.reset()}},[]);let h=(0,t.useCallback)(async e=>{s({type:`LOAD_START`});try{let t=m.current;await t.loadFromThreeJS(e),s({type:`LOAD_SUCCESS`,payload:{report:await t.getReport()}})}catch(e){s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading model:`,e)}},[]),g=(0,t.useCallback)(async t=>{s({type:`LOAD_START`});try{let n=m.current,r=new Map;for(let n of Object.values(t.assetData??{}))for(let t of(0,e.buildAssetLookupKeys)(n.fileName))r.set(t,n);let i={},a=t.gltfJson,o=[...(a.images??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`)),...(a.buffers??[]).map(e=>e.uri).filter(e=>!!e&&!e.startsWith(`data:`))];for(let t of o){let n=(0,e.normalizeAssetUri)(t),a=n.split(`/`).pop()||n,o=r.get(t)??r.get(n)??r.get(a);o&&(i[t]=(0,e.toSerializedAssetBytes)(o))}let c={json:t.gltfJson,resources:i};await n.loadFromJSON(c),s({type:`LOAD_SUCCESS`,payload:{report:await n.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading server scene into optimizer:`,e),e}},[]),_=(0,t.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromBuffer(e),m.current.setAppliedOptimizations(t?.appliedOptimizations??[]),m.current.setDracoReport(t?.dracoReport??null),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLB buffer into optimizer:`,e),e}},[]),v=(0,t.useCallback)(async(e,t)=>{s({type:`LOAD_START`});try{await m.current.loadFromGLTFWithAssets(e,t),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw s({type:`LOAD_ERROR`,payload:e}),console.error(`Error loading GLTF with assets into optimizer:`,e),e}},[]),y=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.simplify(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Simplification failed:`,e)}},[]),b=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.deduplicate(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Deduplication failed:`,e)}},[]),x=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.quantize(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Quantization failed:`,e)}},[]),S=(0,t.useCallback)(async e=>{if(m.current.hasModel())try{await m.current.optimizeNormals(e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){console.error(`Normals optimization failed:`,e)}},[]),C=(0,t.useCallback)(async e=>{try{await l(m.current,e),s({type:`LOAD_SUCCESS`,payload:{report:await m.current.getReport()}})}catch(e){throw console.error(`Texture optimization failed:`,e),e}},[]),w=(0,t.useCallback)(async()=>{if(!m.current.hasModel())return null;try{return await m.current.export()}catch(e){return console.error(`Error getting model binary:`,e),null}},[]),T=(0,t.useCallback)(()=>m.current.hasModel()?m.current.document:null,[]);return{load:h,loadFromServerSceneData:g,loadFromGlbBuffer:_,loadFromGLTFWithAssets:v,getModel:w,isReady:!!T()&&!d,_getDocument:T,reset:(0,t.useCallback)(()=>{m.current.reset(),s({type:`RESET`}),p()},[p]),error:u,loading:d,report:c,info:f,simplifyOptimization:y,dedupOptimization:b,quantizeOptimization:x,normalsOptimization:S,texturesOptimization:C}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
@@ -191,10 +191,10 @@ var g = async (e, t) => {
191
191
  payload: e
192
192
  }), console.error("Error loading server scene into optimizer:", e), e;
193
193
  }
194
- }, []), S = r(async (e) => {
194
+ }, []), S = r(async (e, t) => {
195
195
  c({ type: "LOAD_START" });
196
196
  try {
197
- await y.current.loadFromBuffer(e), c({
197
+ await y.current.loadFromBuffer(e), y.current.setAppliedOptimizations(t?.appliedOptimizations ?? []), y.current.setDracoReport(t?.dracoReport ?? null), c({
198
198
  type: "LOAD_SUCCESS",
199
199
  payload: { report: await y.current.getReport() }
200
200
  });
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-optimize-model-Jie2FUHc.cjs");exports.useOptimizeModel=e.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./use-optimize-model-CD5zqTLK.cjs");exports.useOptimizeModel=e.t;
@@ -1,2 +1,2 @@
1
- import { t as e } from "./use-optimize-model-CweunXiv.js";
1
+ import { t as e } from "./use-optimize-model-D16-4Zwm.js";
2
2
  export { e as useOptimizeModel };
@@ -1 +0,0 @@
1
- const e=require("./chunk-CMqjfN_6.cjs");let t=require("react"),n=require("@vctrl/core/model-exporter"),r=require("file-saver");r=e.t(r,1);var i=(e,i)=>{let a=(0,t.useRef)(new n.ModelExporter);return{handleThreeGltfExport:(0,t.useCallback)(async(t,n)=>{let o=t?.model;if(!o){console.error(`Scene not initialized`);return}try{let i=a.current,s=t.name.replace(/\.[^/.]+$/,``);if(n){let e=await i.exportThreeJSGLB(o,{});if(!(e.data instanceof Uint8Array))throw Error(`Expected GLB export to return Uint8Array`);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${s}.glb`)}else{let e=await i.exportThreeJSGLTF(o),t=await i.createZIPArchive(e,s);r.default.saveAs(new Blob([new Uint8Array(t)]),`${s}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[e,i]),handleDocumentGltfExport:(0,t.useCallback)(async function(t,n,o=!1,s=!0){try{let i=a.current,c=n?.name.replace(/\.[^/.]+$/,``)||`model`;if(o){let e=await i.exportDocumentGLB(t);r.default.saveAs(new Blob([new Uint8Array(e.data)]),`${c}.glb`)}else{let e=await i.exportDocumentGLTF(t);if(!s)return e;let n=await i.createZIPArchive(e,c);r.default.saveAs(new Blob([new Uint8Array(n)]),`${c}.zip`)}e&&e()}catch(e){console.error(`Export failed:`,e),i&&i(e)}},[i,e])}};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return i}});
@@ -1,49 +0,0 @@
1
- import { useCallback as e, useRef as t } from "react";
2
- import { ModelExporter as n } from "@vctrl/core/model-exporter";
3
- import r from "file-saver";
4
- //#region packages/hooks/src/use-export-model/use-export-model.ts
5
- var i = (i, a) => {
6
- let o = t(new n());
7
- return {
8
- handleThreeGltfExport: e(async (e, t) => {
9
- let n = e?.model;
10
- if (!n) {
11
- console.error("Scene not initialized");
12
- return;
13
- }
14
- try {
15
- let a = o.current, s = e.name.replace(/\.[^/.]+$/, "");
16
- if (t) {
17
- let e = await a.exportThreeJSGLB(n, {});
18
- if (!(e.data instanceof Uint8Array)) throw Error("Expected GLB export to return Uint8Array");
19
- r.saveAs(new Blob([new Uint8Array(e.data)]), `${s}.glb`);
20
- } else {
21
- let e = await a.exportThreeJSGLTF(n), t = await a.createZIPArchive(e, s);
22
- r.saveAs(new Blob([new Uint8Array(t)]), `${s}.zip`);
23
- }
24
- i && i();
25
- } catch (e) {
26
- console.error("Export failed:", e), a && a(e);
27
- }
28
- }, [i, a]),
29
- handleDocumentGltfExport: e(async function(e, t, n = !1, s = !0) {
30
- try {
31
- let a = o.current, c = t?.name.replace(/\.[^/.]+$/, "") || "model";
32
- if (n) {
33
- let t = await a.exportDocumentGLB(e);
34
- r.saveAs(new Blob([new Uint8Array(t.data)]), `${c}.glb`);
35
- } else {
36
- let t = await a.exportDocumentGLTF(e);
37
- if (!s) return t;
38
- let n = await a.createZIPArchive(t, c);
39
- r.saveAs(new Blob([new Uint8Array(n)]), `${c}.zip`);
40
- }
41
- i && i();
42
- } catch (e) {
43
- console.error("Export failed:", e), a && a(e);
44
- }
45
- }, [a, i])
46
- };
47
- };
48
- //#endregion
49
- export { i as t };