@vctrl/hooks 0.9.5 → 0.16.2

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +380 -0
  2. package/README.md +202 -383
  3. package/package.json +66 -55
  4. package/project.json +55 -0
  5. package/src/index.ts +38 -0
  6. package/src/use-export-model/use-export-model.ts +113 -0
  7. package/src/use-load-model/event-system.ts +46 -0
  8. package/src/use-load-model/index.ts +15 -0
  9. package/src/use-load-model/model-context.tsx +174 -0
  10. package/src/use-load-model/state.ts +75 -0
  11. package/src/use-load-model/types.ts +284 -0
  12. package/src/use-load-model/use-load-model.ts +682 -0
  13. package/src/use-load-model/utils/calculate-referenced-bytes.ts +123 -0
  14. package/src/use-load-model/utils/index.ts +7 -0
  15. package/src/use-load-model/utils/read-directory.ts +35 -0
  16. package/src/use-load-model/utils/reconstruct-files.ts +85 -0
  17. package/src/use-load-model/utils/resolve-scene-payload.ts +151 -0
  18. package/{use-optimize-model/index.d.ts → src/use-optimize-model/index.ts} +2 -1
  19. package/src/use-optimize-model/state.ts +40 -0
  20. package/src/use-optimize-model/types.ts +37 -0
  21. package/src/use-optimize-model/use-calc-optimization-info.ts +101 -0
  22. package/src/use-optimize-model/use-optimize-model.ts +467 -0
  23. package/src/use-optimize-model/utils/index.ts +29 -0
  24. package/src/use-optimize-model/utils/texture-optimization.ts +292 -0
  25. package/src/use-optimize-model/utils/validation.ts +62 -0
  26. package/src/utils/server-communication.ts +351 -0
  27. package/tsconfig.json +21 -0
  28. package/tsconfig.lib.json +16 -0
  29. package/vite.config.ts +77 -0
  30. package/GLTFExporter-BmLF430n.cjs +0 -18
  31. package/GLTFExporter-C4hCeb4H.js +0 -1263
  32. package/index.cjs.js +0 -1
  33. package/index.d.ts +0 -3
  34. package/index.es.js +0 -12
  35. package/model-context-CQJta_vi.cjs +0 -9
  36. package/model-context-D1Q8WdpT.js +0 -3431
  37. package/use-export-model/types.d.ts +0 -19
  38. package/use-export-model/use-export-model.d.ts +0 -14
  39. package/use-export-model/utils/data-uri-to-blob.d.ts +0 -9
  40. package/use-export-model/utils/export-handlers.d.ts +0 -23
  41. package/use-export-model/utils/file-helpers.d.ts +0 -17
  42. package/use-export-model/utils/index.d.ts +0 -3
  43. package/use-export-model-AFharGg5.js +0 -77
  44. package/use-export-model-CWcKp4A3.cjs +0 -1
  45. package/use-export-model.cjs.js +0 -1
  46. package/use-export-model.es.js +0 -4
  47. package/use-load-model/event-system.d.ts +0 -7
  48. package/use-load-model/file-type-hooks/index.d.ts +0 -2
  49. package/use-load-model/file-type-hooks/use-load-binary.d.ts +0 -11
  50. package/use-load-model/file-type-hooks/use-load-gltf.d.ts +0 -5
  51. package/use-load-model/index.d.ts +0 -3
  52. package/use-load-model/loaders/create-gltf-loader.d.ts +0 -3
  53. package/use-load-model/loaders/create-usdz-loader.d.ts +0 -3
  54. package/use-load-model/loaders/index.d.ts +0 -2
  55. package/use-load-model/model-context.d.ts +0 -78
  56. package/use-load-model/state.d.ts +0 -19
  57. package/use-load-model/types.d.ts +0 -51
  58. package/use-load-model/use-load-model.d.ts +0 -74
  59. package/use-load-model/utils/array-buffer-to-base64.d.ts +0 -2
  60. package/use-load-model/utils/index.d.ts +0 -2
  61. package/use-load-model/utils/read-directory.d.ts +0 -8
  62. package/use-load-model.cjs.js +0 -1
  63. package/use-load-model.es.js +0 -8
  64. package/use-optimize-model/state.d.ts +0 -13
  65. package/use-optimize-model/types.d.ts +0 -37
  66. package/use-optimize-model/use-optimize-model.d.ts +0 -29
  67. package/use-optimize-model-Cqkp__Vi.cjs +0 -1
  68. package/use-optimize-model-DZWIb_40.js +0 -127
  69. package/use-optimize-model.cjs.js +0 -1
  70. package/use-optimize-model.es.js +0 -4
  71. /package/{use-export-model/index.d.ts → src/use-export-model/index.ts} +0 -0
package/package.json CHANGED
@@ -1,57 +1,68 @@
1
1
  {
2
- "version": "0.9.5",
3
- "name": "@vctrl/hooks",
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
- "bugs": {
6
- "url": "https://github.com/vectreal/vectreal-core/issues"
7
- },
8
- "homepage": "https://core.vectreal.com",
9
- "keywords": [
10
- "vectreal",
11
- "react",
12
- "3d",
13
- "model",
14
- "viewer",
15
- "react-component",
16
- "react-three-fiber",
17
- "react-three-drei",
18
- "threejs",
19
- "react-three-gltf",
20
- "gltf",
21
- "glb",
22
- "usdz",
23
- "three-model-loading",
24
- "model-viewer"
25
- ],
26
- "repository": {
27
- "url": "https://github.com/vectreal/vectreal-core",
28
- "type": "https"
29
- },
30
- "license": "AGPL-3.0-only",
31
- "main": "./index.cjs.js",
32
- "module": "./index.es.js",
33
- "types": "./index.d.ts",
34
- "type": "module",
35
- "publishConfig": {
36
- "access": "public"
37
- },
38
- "exports": {
39
- "./use-load-model": {
40
- "import": "./use-load-model.es.js",
41
- "require": "./use-load-model.cjs.js",
42
- "types": "./use-load-model/index.d.ts"
43
- }
44
- },
45
- "peerDependencies": {
46
- "react": "^18.0.0 || ^19.0.0"
47
- },
48
- "dependencies": {
49
- "@gltf-transform/core": "^4.0.8",
50
- "@gltf-transform/extensions": "^4.0.10",
51
- "@gltf-transform/functions": "^4.0.8",
52
- "file-saver": "^2.0.5",
53
- "jszip": "^3.10.1",
54
- "meshoptimizer": "^0.21.0",
55
- "three": "^0.168.0"
56
- }
2
+ "version": "0.16.2",
3
+ "name": "@vctrl/hooks",
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
+ "bugs": {
6
+ "url": "https://github.com/vectreal/vectreal-platform/issues"
7
+ },
8
+ "homepage": "https://vectreal.com",
9
+ "keywords": [
10
+ "vectreal",
11
+ "react",
12
+ "3d",
13
+ "model",
14
+ "viewer",
15
+ "react-component",
16
+ "react-three-fiber",
17
+ "react-three-drei",
18
+ "threejs",
19
+ "react-three-gltf",
20
+ "gltf",
21
+ "glb",
22
+ "usdz",
23
+ "three-model-loading",
24
+ "model-viewer"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/vectreal/vectreal-platform.git"
29
+ },
30
+ "license": "AGPL-3.0-only",
31
+ "types": "./index.d.ts",
32
+ "main": "./index.cjs.js",
33
+ "module": "./index.es.js",
34
+ "type": "module",
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "exports": {
39
+ "./use-load-model": {
40
+ "types": "./use-load-model/index.d.ts",
41
+ "import": "./use-load-model.es.js",
42
+ "require": "./use-load-model.cjs.js"
43
+ },
44
+ "./use-export-model": {
45
+ "types": "./use-export-model/index.d.ts",
46
+ "import": "./use-export-model.es.js",
47
+ "require": "./use-export-model.cjs.js"
48
+ },
49
+ "./use-optimize-model": {
50
+ "types": "./use-optimize-model/index.d.ts",
51
+ "import": "./use-optimize-model.es.js",
52
+ "require": "./use-optimize-model.cjs.js"
53
+ }
54
+ },
55
+ "peerDependencies": {
56
+ "react": "^18.0.0 || ^19.0.0",
57
+ "@gltf-transform/core": "^4.0.8",
58
+ "file-saver": "^2.0.5",
59
+ "@nx/vite": "22.5.0",
60
+ "@vitejs/plugin-react": "^4.3.4",
61
+ "vite": "^7.3.1",
62
+ "vite-plugin-dts": "4.5.3"
63
+ },
64
+ "dependencies": {
65
+ "@vctrl/core": "workspace:*",
66
+ "three": "^0.177.0"
67
+ }
57
68
  }
package/project.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "vctrl/hooks",
3
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "packages/hooks/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "lint": {
9
+ "executor": "@nx/eslint:lint",
10
+ "outputs": ["{options.outputFile}"],
11
+ "options": {
12
+ "fix": true,
13
+ "lintFilePatterns": [
14
+ "packages/hooks/**/*.{ts,tsx,js,jsx}",
15
+ "packages/hooks/package.json"
16
+ ]
17
+ }
18
+ },
19
+ "publish": {
20
+ "dependsOn": ["copy-md"],
21
+ "executor": "nx:run-commands",
22
+ "outputs": [],
23
+ "options": {
24
+ "command": "npm publish --access public",
25
+ "cwd": "build/packages/vctrl/hooks"
26
+ }
27
+ },
28
+ "copy-md": {
29
+ "dependsOn": ["build"],
30
+ "executor": "nx:run-commands",
31
+ "outputs": [],
32
+ "options": {
33
+ "commands": [
34
+ "rsync -rat packages/hooks/*.md build/packages/vctrl/hooks/"
35
+ ]
36
+ }
37
+ },
38
+ "build": {
39
+ "executor": "@nx/vite:build",
40
+ "outputs": ["{options.outputPath}"],
41
+ "options": {
42
+ "outputPath": "build/packages/vctrl/hooks",
43
+ "configFile": "packages/hooks/vite.config.ts"
44
+ }
45
+ },
46
+ "build-ci": {
47
+ "executor": "@nx/vite:build",
48
+ "outputs": ["{options.outputPath}"],
49
+ "options": {
50
+ "outputPath": "build/packages/vctrl/hooks",
51
+ "configFile": "packages/hooks/vite.config.ts"
52
+ }
53
+ }
54
+ }
55
+ }
package/src/index.ts ADDED
@@ -0,0 +1,38 @@
1
+ /* vectreal-core | vctrl/hooks
2
+ Copyright (C) 2024 Moritz Becker
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+
17
+ // Export hooks
18
+ export { default as useLoadModel } from './use-load-model/use-load-model'
19
+ export { default as useExportModel } from './use-export-model/use-export-model'
20
+ export { default as useOptimizeModel } from './use-optimize-model/use-optimize-model'
21
+
22
+ // Export utilities
23
+ export { reconstructGltfFiles } from './use-load-model/utils/reconstruct-files'
24
+ export { ServerCommunicationService } from './utils/server-communication'
25
+ export type { ServerRequestConfig } from './utils/server-communication'
26
+
27
+ // Re-export types for convenience
28
+ export type {
29
+ EventTypes,
30
+ EventHandler,
31
+ ModelFile,
32
+ LoadData,
33
+ UseLoadModelReturn,
34
+ SceneDataLoadOptions,
35
+ SceneLoadOptions,
36
+ ServerSceneData,
37
+ SceneLoadResult
38
+ } from './use-load-model/types'
@@ -0,0 +1,113 @@
1
+ import { Document } from '@gltf-transform/core'
2
+ import { ModelExporter } from '@vctrl/core/model-exporter'
3
+ import fileSaver from 'file-saver'
4
+ import { useCallback, useRef } from 'react'
5
+
6
+ import { ModelFile } from '../use-load-model'
7
+
8
+ /**
9
+ * A React hook for exporting a 3D model using the core ModelExporter.
10
+ *
11
+ * @param {Function} [onSaved] Called when the export is complete.
12
+ * @param {Function} [onError] Called when an error occurs during exporting.
13
+ * @returns An object with export functions.
14
+ */
15
+ const useExportModel = (
16
+ onSaved?: () => void,
17
+ onError?: (error: Error) => void
18
+ ) => {
19
+ const exporterRef = useRef<ModelExporter>(new ModelExporter())
20
+
21
+ const handleThreeGltfExport = useCallback(
22
+ async (file: ModelFile | null, binary: boolean): Promise<void> => {
23
+ const scene = file?.model
24
+ if (!scene) {
25
+ console.error('Scene not initialized')
26
+ return
27
+ }
28
+
29
+ try {
30
+ const exporter = exporterRef.current
31
+ const baseFileName = file.name.replace(/\.[^/.]+$/, '') // Remove extension
32
+
33
+ if (binary) {
34
+ // Export as GLB
35
+ const result = await exporter.exportThreeJSGLB(scene, {})
36
+ if (!(result.data instanceof Uint8Array)) {
37
+ throw new Error('Expected GLB export to return Uint8Array')
38
+ }
39
+ fileSaver.saveAs(
40
+ new Blob([new Uint8Array(result.data)]),
41
+ `${baseFileName}.glb`
42
+ )
43
+ } else {
44
+ // Export as GLTF with ZIP
45
+ const result = await exporter.exportThreeJSGLTF(scene)
46
+ const zipBuffer = await exporter.createZIPArchive(
47
+ result,
48
+ baseFileName
49
+ )
50
+ fileSaver.saveAs(
51
+ new Blob([new Uint8Array(zipBuffer)]),
52
+ `${baseFileName}.zip`
53
+ )
54
+ }
55
+
56
+ if (onSaved) onSaved()
57
+ } catch (error) {
58
+ console.error('Export failed:', error)
59
+ if (onError) onError(error as Error)
60
+ }
61
+ },
62
+ [onSaved, onError]
63
+ )
64
+
65
+ const handleDocumentGltfExport = useCallback(
66
+ async function (
67
+ jsonDocument: Document,
68
+ file: ModelFile | null,
69
+ binary = false,
70
+ download = true
71
+ ) {
72
+ try {
73
+ const exporter = exporterRef.current
74
+ const baseFileName = file?.name.replace(/\.[^/.]+$/, '') || 'model'
75
+
76
+ if (binary) {
77
+ // Export as GLB
78
+ const result = await exporter.exportDocumentGLB(jsonDocument)
79
+ fileSaver.saveAs(
80
+ new Blob([new Uint8Array(result.data)]),
81
+ `${baseFileName}.glb`
82
+ )
83
+ } else {
84
+ const result = await exporter.exportDocumentGLTF(jsonDocument)
85
+
86
+ if (!download) return result
87
+
88
+ // Export as GLTF with ZIP
89
+ const zipBuffer = await exporter.createZIPArchive(
90
+ result,
91
+ baseFileName
92
+ )
93
+ fileSaver.saveAs(
94
+ new Blob([new Uint8Array(zipBuffer)]),
95
+ `${baseFileName}.zip`
96
+ )
97
+ }
98
+
99
+ if (onSaved) onSaved()
100
+ } catch (error) {
101
+ console.error('Export failed:', error)
102
+ if (onError) onError(error as Error)
103
+ }
104
+ },
105
+ [onError, onSaved]
106
+ )
107
+ return {
108
+ handleThreeGltfExport,
109
+ handleDocumentGltfExport
110
+ }
111
+ }
112
+
113
+ export default useExportModel
@@ -0,0 +1,46 @@
1
+ /* vectreal-core | vctrl/hooks
2
+ Copyright (C) 2024 Moritz Becker
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+
17
+ import { EventData, EventHandler, EventTypes } from './types'
18
+
19
+ const listeners = {} as Record<EventTypes, EventHandler<EventTypes>[]>
20
+ const eventSystem = {
21
+ emit<T extends EventTypes>(event: T, data?: EventData[T]): void {
22
+ const handlers = listeners[event]
23
+ console.debug(`Event emitted: ${event}`, data)
24
+ if (handlers) {
25
+ handlers.forEach((handler) => handler(data))
26
+ }
27
+ },
28
+
29
+ on<T extends EventTypes>(event: T, handler?: EventHandler<T>): void {
30
+ if (!listeners[event]) {
31
+ listeners[event] = []
32
+ }
33
+ listeners[event].push(handler as EventHandler<EventTypes>)
34
+ },
35
+
36
+ off<T extends EventTypes>(event: T, handler?: EventHandler<T>): void {
37
+ const handlers = listeners[event]
38
+ if (handlers) {
39
+ listeners[event] = handlers.filter(
40
+ (h) => h !== handler
41
+ ) as EventHandler<EventTypes>[]
42
+ }
43
+ }
44
+ }
45
+
46
+ export default eventSystem
@@ -0,0 +1,15 @@
1
+ export { default as useLoadModel } from './use-load-model'
2
+ export {
3
+ type EventHandler,
4
+ type EventTypes,
5
+ type InputFileOrDirectory,
6
+ type LoadData,
7
+ type ModelFile,
8
+ type SceneDataLoadOptions,
9
+ type SceneLoadOptions,
10
+ type SceneLoadResult,
11
+ type UseLoadModelReturn,
12
+ type ServerSceneData
13
+ } from './types'
14
+ export { reconstructGltfFiles } from './utils/reconstruct-files'
15
+ export * from './model-context'
@@ -0,0 +1,174 @@
1
+ /* vectreal-core | vctrl/hooks
2
+ Copyright (C) 2024 Moritz Becker
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+
17
+ import React, { createContext, useContext } from 'react'
18
+
19
+ import { useOptimizeModel } from '../use-optimize-model'
20
+ import { UseLoadModelReturn } from './types'
21
+ import useLoadModel from './use-load-model'
22
+
23
+ /**
24
+ * Props for ModelProvider when an optimizer is provided.
25
+ * This ensures the optimizer property is required and properly typed.
26
+ */
27
+ interface ModelProviderPropsWithOptimizer extends React.PropsWithChildren {
28
+ /** The optimizer instance from useOptimizeModel hook */
29
+ optimizer: ReturnType<typeof useOptimizeModel>
30
+ }
31
+
32
+ /**
33
+ * Props for ModelProvider when no optimizer is used.
34
+ * The optimizer property must not be provided.
35
+ */
36
+ interface ModelProviderPropsWithoutOptimizer extends React.PropsWithChildren {
37
+ /** Explicitly prevents optimizer from being passed */
38
+ optimizer?: never
39
+ }
40
+
41
+ /**
42
+ * Discriminated union of ModelProvider props.
43
+ * Allows the provider to be used with or without an optimizer,
44
+ * ensuring type safety for both scenarios.
45
+ */
46
+ type ModelProviderProps =
47
+ | ModelProviderPropsWithOptimizer
48
+ | ModelProviderPropsWithoutOptimizer
49
+
50
+ /**
51
+ * Context for model loading with optimizer integration.
52
+ * When this context has a value, the optimizer property will be fully typed
53
+ * with all optimization methods and state.
54
+ */
55
+ const ModelContextWithOptimizer = createContext<
56
+ UseLoadModelReturn<true> | undefined
57
+ >(undefined)
58
+
59
+ /**
60
+ * Context for model loading without optimizer integration.
61
+ * When this context has a value, the optimizer property will be typed as null.
62
+ */
63
+ const ModelContextWithoutOptimizer = createContext<
64
+ UseLoadModelReturn<false> | undefined
65
+ >(undefined)
66
+
67
+ /**
68
+ * Provider component for model loading context.
69
+ *
70
+ * This component uses a dual-context pattern to provide type-safe access to model loading
71
+ * functionality with or without optimizer integration.
72
+ *
73
+ * @example
74
+ * // With optimizer
75
+ * const optimizer = useOptimizeModel()
76
+ * <ModelProvider optimizer={optimizer}>
77
+ * <App />
78
+ * </ModelProvider>
79
+ *
80
+ * @example
81
+ * // Without optimizer
82
+ * <ModelProvider>
83
+ * <App />
84
+ * </ModelProvider>
85
+ *
86
+ * @param props - Component props
87
+ * @param props.children - Child components that will have access to the model context
88
+ * @param props.optimizer - Optional optimizer instance from useOptimizeModel hook
89
+ */
90
+ const ModelProvider = ({ children, optimizer }: ModelProviderProps) => {
91
+ // Call the hook once to get the model loading functionality
92
+ // The generic type will be inferred based on whether optimizer is defined
93
+ const value = useLoadModel(optimizer)
94
+
95
+ // Provide the value to the appropriate context based on whether optimizer is present
96
+ // This enables type-safe consumption via useModelContext
97
+ if (optimizer) {
98
+ // Optimizer is present: use ModelContextWithOptimizer
99
+ // The type assertion is safe because we know optimizer is defined
100
+ return (
101
+ <ModelContextWithOptimizer.Provider
102
+ value={value as unknown as UseLoadModelReturn<true>}
103
+ >
104
+ {children}
105
+ </ModelContextWithOptimizer.Provider>
106
+ )
107
+ }
108
+
109
+ // No optimizer: use ModelContextWithoutOptimizer
110
+ // The type assertion is safe because we know optimizer is undefined
111
+ return (
112
+ <ModelContextWithoutOptimizer.Provider
113
+ value={value as unknown as UseLoadModelReturn<false>}
114
+ >
115
+ {children}
116
+ </ModelContextWithoutOptimizer.Provider>
117
+ )
118
+ }
119
+
120
+ /**
121
+ * Hook to access the model loading context.
122
+ *
123
+ * This hook provides type-safe access to model loading functionality.
124
+ * The return type is automatically inferred based on how the ModelProvider was configured.
125
+ *
126
+ * By default (no argument), it assumes an optimizer is present and returns the full
127
+ * optimizer integration. You can explicitly specify the expected configuration using
128
+ * the requireOptimizer parameter.
129
+ *
130
+ * @example
131
+ * // Default: assumes optimizer is present
132
+ * const model = useModelContext()
133
+ * model.optimizer.applyOptimization() // ✅ Fully typed
134
+ *
135
+ * @example
136
+ * // Explicitly require optimizer
137
+ * const model = useModelContext(true)
138
+ * model.optimizer.simplifyOptimization() // ✅ Fully typed
139
+ *
140
+ * @example
141
+ * // Explicitly indicate no optimizer
142
+ * const model = useModelContext(false)
143
+ * model.optimizer // ✅ Typed as null
144
+ *
145
+ * @throws {Error} If used outside of a ModelProvider
146
+ * @returns The model loading context with type-safe optimizer property
147
+ */
148
+ function useModelContext(): UseLoadModelReturn<true>
149
+ /**
150
+ * @param requireOptimizer - Set to true to explicitly require optimizer integration
151
+ */
152
+ function useModelContext(requireOptimizer: true): UseLoadModelReturn<true>
153
+ /**
154
+ * @param requireOptimizer - Set to false to explicitly indicate no optimizer
155
+ */
156
+ function useModelContext(requireOptimizer: false): UseLoadModelReturn<false>
157
+
158
+ function useModelContext(_?: boolean): UseLoadModelReturn<boolean> {
159
+ // Check both contexts - only one will have a value based on how ModelProvider was configured
160
+ const withOptimizer = useContext(ModelContextWithOptimizer)
161
+ const withoutOptimizer = useContext(ModelContextWithoutOptimizer)
162
+
163
+ // Use whichever context has a value (they're mutually exclusive)
164
+ const context = withOptimizer ?? withoutOptimizer
165
+
166
+ // Ensure the hook is used within a ModelProvider
167
+ if (!context) {
168
+ throw new Error('useModelContext must be used within a ModelProvider')
169
+ }
170
+
171
+ return context
172
+ }
173
+
174
+ export { ModelProvider, useModelContext }
@@ -0,0 +1,75 @@
1
+ /* vectreal-core | vctrl/hooks
2
+ Copyright (C) 2024 Moritz Becker
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+
17
+ import { ModelFileTypes } from '@vctrl/core/model-loader'
18
+
19
+ import { Action, LoadData } from './types'
20
+
21
+ /**
22
+ * Initial state for the useReadModelFiles hook
23
+ *
24
+ * @property file - Currently loaded file
25
+ * @property isFileLoading - Flag indicating if a file is currently being loaded
26
+ * @property progress - Current loading progress (0-100)
27
+ * @property supportedFileTypes - List of supported file types
28
+ */
29
+ export const initialState: LoadData = {
30
+ /**
31
+ * Currently loaded file
32
+ */
33
+ file: null,
34
+ /**
35
+ * Flag indicating if a file is currently being loaded
36
+ */
37
+ isFileLoading: false,
38
+ /**
39
+ * Current loading progress (0-100)
40
+ */
41
+ progress: 0,
42
+ /**
43
+ * List of supported file types
44
+ * @type {ModelFileTypes[]}
45
+ */
46
+ supportedFileTypes: [
47
+ ModelFileTypes.gltf,
48
+ ModelFileTypes.glb,
49
+ ModelFileTypes.usdz
50
+ ]
51
+ }
52
+
53
+ /**
54
+ * Reducer function for the useReadModelFiles hook
55
+ *
56
+ * @param state - The current state of the reducer
57
+ * @param action - The action to be performed
58
+ * @returns The updated state
59
+ */
60
+ function reducer(state: LoadData, action: Action): LoadData {
61
+ switch (action.type) {
62
+ case 'set-file':
63
+ return { ...state, file: action.payload }
64
+ case 'set-file-loading':
65
+ return { ...state, isFileLoading: action.payload }
66
+ case 'set-progress':
67
+ return { ...state, progress: action.payload }
68
+ case 'reset-state':
69
+ return { ...initialState }
70
+ default:
71
+ return state
72
+ }
73
+ }
74
+
75
+ export default reducer