react-three-game 0.0.68 → 0.0.70

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 (52) hide show
  1. package/dist/helpers/SoundManager.d.ts +2 -0
  2. package/dist/helpers/SoundManager.js +6 -0
  3. package/dist/index.d.ts +10 -7
  4. package/dist/index.js +8 -4
  5. package/dist/shared/GameCanvas.js +0 -2
  6. package/dist/tools/assetviewer/page.d.ts +5 -0
  7. package/dist/tools/assetviewer/page.js +3 -0
  8. package/dist/tools/dragdrop/DragDropLoader.d.ts +3 -2
  9. package/dist/tools/dragdrop/DragDropLoader.js +18 -3
  10. package/dist/tools/dragdrop/index.d.ts +2 -2
  11. package/dist/tools/dragdrop/index.js +1 -1
  12. package/dist/tools/dragdrop/modelLoader.d.ts +10 -0
  13. package/dist/tools/dragdrop/modelLoader.js +60 -0
  14. package/dist/tools/prefabeditor/EditorTree.js +6 -30
  15. package/dist/tools/prefabeditor/EditorTreeMenus.js +3 -3
  16. package/dist/tools/prefabeditor/EditorUI.js +6 -4
  17. package/dist/tools/prefabeditor/InstanceProvider.d.ts +2 -0
  18. package/dist/tools/prefabeditor/InstanceProvider.js +54 -52
  19. package/dist/tools/prefabeditor/PrefabEditor.d.ts +22 -0
  20. package/dist/tools/prefabeditor/PrefabEditor.js +68 -27
  21. package/dist/tools/prefabeditor/PrefabRoot.d.ts +5 -1
  22. package/dist/tools/prefabeditor/PrefabRoot.js +148 -145
  23. package/dist/tools/prefabeditor/components/ClickComponent.js +10 -7
  24. package/dist/tools/prefabeditor/components/ComponentRegistry.d.ts +10 -4
  25. package/dist/tools/prefabeditor/components/ComponentRegistry.js +6 -6
  26. package/dist/tools/prefabeditor/components/GeometryComponent.js +1 -1
  27. package/dist/tools/prefabeditor/components/Input.d.ts +16 -0
  28. package/dist/tools/prefabeditor/components/Input.js +33 -0
  29. package/dist/tools/prefabeditor/components/MaterialComponent.js +10 -2
  30. package/dist/tools/prefabeditor/components/ModelComponent.js +35 -43
  31. package/dist/tools/prefabeditor/components/PhysicsComponent.d.ts +10 -1
  32. package/dist/tools/prefabeditor/components/PhysicsComponent.js +122 -28
  33. package/dist/tools/prefabeditor/components/SoundComponent.d.ts +3 -0
  34. package/dist/tools/prefabeditor/components/SoundComponent.js +240 -0
  35. package/dist/tools/prefabeditor/components/SpotLightComponent.js +6 -1
  36. package/dist/tools/prefabeditor/components/TransformComponent.js +2 -2
  37. package/dist/tools/prefabeditor/components/index.js +2 -0
  38. package/dist/tools/prefabeditor/prefabStore.d.ts +1 -0
  39. package/dist/tools/prefabeditor/prefabStore.js +11 -13
  40. package/dist/tools/prefabeditor/sceneApi.d.ts +15 -1
  41. package/dist/tools/prefabeditor/sceneApi.js +77 -32
  42. package/dist/tools/prefabeditor/styles.d.ts +1 -0
  43. package/dist/tools/prefabeditor/styles.js +9 -0
  44. package/dist/tools/prefabeditor/types.d.ts +13 -0
  45. package/dist/tools/prefabeditor/types.js +28 -1
  46. package/dist/tools/prefabeditor/useClickValid.d.ts +13 -0
  47. package/dist/tools/prefabeditor/useClickValid.js +21 -0
  48. package/dist/tools/prefabeditor/utils.d.ts +2 -0
  49. package/dist/tools/prefabeditor/utils.js +34 -35
  50. package/package.json +1 -1
  51. package/dist/tools/prefabeditor/EditorContext.d.ts +0 -16
  52. package/dist/tools/prefabeditor/EditorContext.js +0 -9
@@ -19,6 +19,8 @@ declare class SoundManager {
19
19
  detune?: number;
20
20
  pitch?: number;
21
21
  }): void;
22
+ hasBuffer(path: string): boolean;
23
+ setBuffer(path: string, buffer: AudioBuffer): void;
22
24
  /** Load and play SFX - accepts file path directly */
23
25
  play(path: string, options?: {
24
26
  volume?: number;
@@ -68,6 +68,12 @@ class SoundManager {
68
68
  gain.connect(this.sfxGain);
69
69
  src.start();
70
70
  }
71
+ hasBuffer(path) {
72
+ return this.buffers.has(path);
73
+ }
74
+ setBuffer(path, buffer) {
75
+ this.buffers.set(path, buffer);
76
+ }
71
77
  /** Load and play SFX - accepts file path directly */
72
78
  play(path, options) {
73
79
  return __awaiter(this, void 0, void 0, function* () {
package/dist/index.d.ts CHANGED
@@ -4,14 +4,15 @@ export { ground } from './helpers';
4
4
  export type { GroundOptions, Vec3 } from './helpers';
5
5
  export { sound as soundManager } from './helpers/SoundManager';
6
6
  export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
7
+ export { PrefabEditorMode } from './tools/prefabeditor/PrefabEditor';
7
8
  export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
8
- export { useEditorContext } from './tools/prefabeditor/EditorContext';
9
- export type { EditorContextType } from './tools/prefabeditor/EditorContext';
10
- export { createPrefabStore, prefabStoreToPrefab } from './tools/prefabeditor/prefabStore';
9
+ export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
10
+ export type { EditorContextType } from './tools/prefabeditor/PrefabEditor';
11
+ export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
11
12
  export type { PrefabStoreApi, PrefabStoreState } from './tools/prefabeditor/prefabStore';
12
13
  export { createScene } from './tools/prefabeditor/sceneApi';
13
14
  export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
14
- export { FieldRenderer, FieldGroup, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
15
+ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
15
16
  export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, createModelNode, createImageNode, } from './tools/prefabeditor/utils';
16
17
  export type { ExportGLBOptions } from './tools/prefabeditor/utils';
17
18
  export type { PrefabEditorProps, PrefabEditorRef, } from './tools/prefabeditor/PrefabEditor';
@@ -19,10 +20,12 @@ export type { SpawnOptions, Scene, Entity, EntityComponent, EntityData, EntityUp
19
20
  export type { PrefabRootProps, PrefabRootRef } from './tools/prefabeditor/PrefabRoot';
20
21
  export type { Component } from './tools/prefabeditor/components/ComponentRegistry';
21
22
  export type { FieldDefinition, FieldType } from './tools/prefabeditor/components/Input';
22
- export type { Prefab, GameObject, ComponentData as ComponentDefinition } from './tools/prefabeditor/types';
23
+ export type { Prefab, GameObject, ComponentData } from './tools/prefabeditor/types';
24
+ export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
23
25
  export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
24
26
  export type { GameEventType, GameEventMap, GameEventPayload, PhysicsEventType, InteractionEventType, PhysicsEventPayload, ClickEventPayload } from './tools/prefabeditor/GameEvents';
25
27
  export { loadFiles } from './tools/dragdrop/DragDropLoader';
26
28
  export type { AssetLoadOptions } from './tools/dragdrop/DragDropLoader';
27
- export { loadModel, loadTexture } from './tools/dragdrop/modelLoader';
28
- export type { LoadedModel, LoadedTexture, LoadedModels, LoadedTextures, ModelLoadResult, ProgressCallback, TextureLoadResult } from './tools/dragdrop/modelLoader';
29
+ export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
30
+ export type { LoadedModel, LoadedModels, ModelLoadResult, LoadedSound, LoadedSounds, SoundLoadResult, LoadedTexture, LoadedTextures, TextureLoadResult, ProgressCallback, } from './tools/dragdrop/modelLoader';
31
+ export { ModelListViewer, SoundListViewer, ModelPicker, SoundPicker, TextureListViewer, TexturePicker, SingleModelViewer, SingleSoundViewer, SingleTextureViewer, SharedCanvas, } from './tools/assetviewer/page';
package/dist/index.js CHANGED
@@ -5,19 +5,23 @@ export { ground } from './helpers';
5
5
  export { sound as soundManager } from './helpers/SoundManager';
6
6
  // Prefab Editor
7
7
  export { default as PrefabEditor } from './tools/prefabeditor/PrefabEditor';
8
+ export { PrefabEditorMode } from './tools/prefabeditor/PrefabEditor';
8
9
  export { default as PrefabRoot } from './tools/prefabeditor/PrefabRoot';
9
- export { useEditorContext } from './tools/prefabeditor/EditorContext';
10
+ export { useEditorContext } from './tools/prefabeditor/PrefabEditor';
10
11
  // Prefab Editor - Store & Scene API
11
- export { createPrefabStore, prefabStoreToPrefab } from './tools/prefabeditor/prefabStore';
12
+ export { createPrefabStore, prefabStoreToPrefab, usePrefabStoreApi } from './tools/prefabeditor/prefabStore';
12
13
  export { createScene } from './tools/prefabeditor/sceneApi';
13
14
  // Prefab Editor - Component Registry
14
15
  export { registerComponent } from './tools/prefabeditor/components/ComponentRegistry';
15
16
  // Prefab Editor - Input Components
16
- export { FieldRenderer, FieldGroup, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
17
+ export { FieldRenderer, FieldGroup, ListEditor, Label, Vector3Input, Vector3Field, NumberField, ColorInput, ColorField, StringInput, StringField, BooleanInput, BooleanField, SelectInput, SelectField, } from './tools/prefabeditor/components/Input';
17
18
  // Prefab Editor - Utils
18
19
  export { loadJson, saveJson, exportGLB, exportGLBData, regenerateIds, computeParentWorldMatrix, createModelNode, createImageNode, } from './tools/prefabeditor/utils';
20
+ export { findComponent, findComponentEntry, hasComponent } from './tools/prefabeditor/types';
19
21
  // Game Events (physics + custom events)
20
22
  export { gameEvents, useGameEvent, getEntityIdFromRigidBody } from './tools/prefabeditor/GameEvents';
21
23
  // Asset Loading
22
24
  export { loadFiles } from './tools/dragdrop/DragDropLoader';
23
- export { loadModel, loadTexture } from './tools/dragdrop/modelLoader';
25
+ export { loadModel, loadSound, loadTexture } from './tools/dragdrop/modelLoader';
26
+ // Asset Viewer
27
+ export { ModelListViewer, SoundListViewer, ModelPicker, SoundPicker, TextureListViewer, TexturePicker, SingleModelViewer, SingleSoundViewer, SingleTextureViewer, SharedCanvas, } from './tools/assetviewer/page';
@@ -23,8 +23,6 @@ import { Canvas, extend } from "@react-three/fiber";
23
23
  import { WebGPURenderer, MeshBasicNodeMaterial, MeshStandardNodeMaterial, SpriteNodeMaterial, PCFShadowMap } from "three/webgpu";
24
24
  import { Suspense, useState } from "react";
25
25
  import { Loader } from "@react-three/drei";
26
- // generic version
27
- // extend(THREE as any)
28
26
  extend({
29
27
  MeshBasicNodeMaterial: MeshBasicNodeMaterial,
30
28
  MeshStandardNodeMaterial: MeshStandardNodeMaterial,
@@ -30,6 +30,11 @@ export declare function ModelPicker({ value, onChange, basePath, pickerKey }: {
30
30
  basePath?: string;
31
31
  pickerKey?: string;
32
32
  }): import("react/jsx-runtime").JSX.Element;
33
+ export declare function SoundPicker({ value, onChange, basePath }: {
34
+ value: string | undefined;
35
+ onChange: (value: string | undefined) => void;
36
+ basePath?: string;
37
+ }): import("react/jsx-runtime").JSX.Element;
33
38
  export declare function SingleTextureViewer({ file, basePath }: {
34
39
  file?: string;
35
40
  basePath?: string;
@@ -209,6 +209,9 @@ export function TexturePicker({ value, onChange, basePath = "" }) {
209
209
  export function ModelPicker({ value, onChange, basePath = "", pickerKey }) {
210
210
  return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "models", rootStyle: { maxHeight: 160, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: { width: '100%', padding: '6px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 10, cursor: 'pointer', border: '1px solid rgba(34, 211, 238, 0.3)' }, clearButtonStyle: { width: '100%', padding: '6px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 10, cursor: 'pointer', border: '1px solid rgba(34, 211, 238, 0.3)' }, popupStyle: { background: 'rgba(0,0,0,0.9)', border: '1px solid rgba(34, 211, 238, 0.3)' }, preview: _jsx("div", { style: { flex: '0 0 auto' }, children: _jsx(SingleModelViewer, { file: value ? `/${value}` : undefined, basePath: basePath }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(ModelListViewer, { files: files, selected: selectedValue ? `/${selectedValue}` : undefined, onSelect: (file) => onSelect(file.startsWith('/') ? file.slice(1) : file), basePath: currentBasePath }, pickerKey)) }));
211
211
  }
212
+ export function SoundPicker({ value, onChange, basePath = "" }) {
213
+ return (_jsx(AssetPicker, { value: value, onChange: onChange, basePath: basePath, manifestFolder: "sound", rootStyle: { maxHeight: 76, overflow: 'visible', position: 'relative', display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }, controlsStyle: { display: 'flex', flexDirection: 'column', gap: 6, flex: '0 0 84px', minWidth: 84, justifyContent: 'flex-end' }, changeButtonStyle: { width: '100%', padding: '6px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 10, cursor: 'pointer', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 3 }, clearButtonStyle: { width: '100%', padding: '6px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 10, cursor: 'pointer', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 3 }, preview: _jsx("div", { style: { flex: '0 0 auto', minWidth: 84 }, children: value ? _jsx(SingleSoundViewer, { file: value, basePath: basePath }) : _jsx("div", { style: { width: 84, height: 60, backgroundColor: '#1f2937', border: '1px dashed rgba(255,255,255,0.12)', borderRadius: 4 } }) }), renderList: ({ files, value: selectedValue, onSelect, basePath: currentBasePath }) => (_jsx(SoundListViewer, { files: files, selected: selectedValue || undefined, onSelect: onSelect, basePath: currentBasePath })) }));
214
+ }
212
215
  // Single Asset Viewer Components - display only one selected asset
213
216
  export function SingleTextureViewer({ file, basePath = "" }) {
214
217
  if (!file)
@@ -1,8 +1,9 @@
1
1
  import type { HTMLAttributes, ReactNode } from "react";
2
- import type { LoadedModel, LoadedTexture } from "./modelLoader";
2
+ import type { LoadedModel, LoadedSound, LoadedTexture } from "./modelLoader";
3
3
  export interface AssetLoadOptions {
4
4
  onModelLoaded?: (model: LoadedModel, filename: string, file: File) => void | Promise<void>;
5
5
  onTextureLoaded?: (texture: LoadedTexture, filename: string, file: File) => void | Promise<void>;
6
+ onSoundLoaded?: (sound: LoadedSound, filename: string, file: File) => void | Promise<void>;
6
7
  onUnhandledFile?: (file: File) => void | Promise<void>;
7
8
  onFilesLoaded?: (files: File[]) => void | Promise<void>;
8
9
  onLoadError?: (error: unknown, filename: string, file: File) => void | Promise<void>;
@@ -16,7 +17,7 @@ export interface FilePickerProps extends AssetLoadOptions, DivProps {
16
17
  children?: ReactNode;
17
18
  multiple?: boolean;
18
19
  }
19
- export declare function loadFiles(files: File[], { onModelLoaded, onTextureLoaded, onUnhandledFile, onFilesLoaded, onLoadError }: AssetLoadOptions): Promise<void>;
20
+ export declare function loadFiles(files: File[], { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onFilesLoaded, onLoadError }: AssetLoadOptions): Promise<void>;
20
21
  export declare function DragDropLoader({ children, ...divProps }: DragDropLoaderProps): import("react/jsx-runtime").JSX.Element;
21
22
  export declare function FilePicker({ accept, children, multiple, ...divProps }: FilePickerProps): import("react/jsx-runtime").JSX.Element;
22
23
  export {};
@@ -20,16 +20,17 @@ var __rest = (this && this.__rest) || function (s, e) {
20
20
  };
21
21
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
22
  import { useRef } from "react";
23
- import { canParseModelFile, canParseTextureFile, parseModelFromFile, parseTextureFromFile } from "./modelLoader";
24
- const DEFAULT_ACCEPT = ".glb,.gltf,.fbx,.png,.jpg,.jpeg,.webp,.gif,.bmp,.svg";
23
+ import { canParseModelFile, canParseSoundFile, canParseTextureFile, parseModelFromFile, parseSoundFromFile, parseTextureFromFile } from "./modelLoader";
24
+ const DEFAULT_ACCEPT = ".glb,.gltf,.fbx,.png,.jpg,.jpeg,.webp,.gif,.bmp,.svg,.mp3,.wav,.ogg,.m4a";
25
25
  function getFiles(fileList) {
26
26
  return fileList ? Array.from(fileList) : [];
27
27
  }
28
28
  export function loadFiles(files_1, _a) {
29
- return __awaiter(this, arguments, void 0, function* (files, { onModelLoaded, onTextureLoaded, onUnhandledFile, onFilesLoaded, onLoadError }) {
29
+ return __awaiter(this, arguments, void 0, function* (files, { onModelLoaded, onTextureLoaded, onSoundLoaded, onUnhandledFile, onFilesLoaded, onLoadError }) {
30
30
  yield Promise.all(files.map((file) => __awaiter(this, void 0, void 0, function* () {
31
31
  const shouldParseModel = canParseModelFile(file);
32
32
  const shouldParseTexture = canParseTextureFile(file);
33
+ const shouldParseSound = canParseSoundFile(file);
33
34
  if (shouldParseModel) {
34
35
  const result = yield parseModelFromFile(file);
35
36
  if (result.success && result.model) {
@@ -56,6 +57,19 @@ export function loadFiles(files_1, _a) {
56
57
  console.error("Texture parse error:", result.error);
57
58
  return;
58
59
  }
60
+ if (shouldParseSound) {
61
+ const result = yield parseSoundFromFile(file);
62
+ if (result.success && result.sound) {
63
+ yield (onSoundLoaded === null || onSoundLoaded === void 0 ? void 0 : onSoundLoaded(result.sound, file.name, file));
64
+ return;
65
+ }
66
+ if (onLoadError) {
67
+ yield onLoadError(result.error, file.name, file);
68
+ return;
69
+ }
70
+ console.error("Sound parse error:", result.error);
71
+ return;
72
+ }
59
73
  if (onUnhandledFile) {
60
74
  yield onUnhandledFile(file);
61
75
  }
@@ -70,6 +84,7 @@ function createLoadHandlers(options) {
70
84
  return {
71
85
  onFilesLoaded: options.onFilesLoaded,
72
86
  onModelLoaded: options.onModelLoaded,
87
+ onSoundLoaded: options.onSoundLoaded,
73
88
  onTextureLoaded: options.onTextureLoaded,
74
89
  onUnhandledFile: options.onUnhandledFile,
75
90
  onLoadError: options.onLoadError,
@@ -1,4 +1,4 @@
1
1
  export { DragDropLoader, FilePicker, loadFiles } from "./DragDropLoader";
2
2
  export type { AssetLoadOptions, DragDropLoaderProps, FilePickerProps } from "./DragDropLoader";
3
- export { loadModel, loadTexture, parseModelFromFile, parseTextureFromFile } from "./modelLoader";
4
- export type { LoadedModel, LoadedTexture, LoadedModels, LoadedTextures, ModelLoadResult, ProgressCallback, TextureLoadResult } from "./modelLoader";
3
+ export { loadModel, loadSound, loadTexture, parseModelFromFile, parseSoundFromFile, parseTextureFromFile } from "./modelLoader";
4
+ export type { LoadedModel, LoadedSound, LoadedTexture, LoadedModels, LoadedSounds, LoadedTextures, ModelLoadResult, ProgressCallback, SoundLoadResult, TextureLoadResult } from "./modelLoader";
@@ -1,2 +1,2 @@
1
1
  export { DragDropLoader, FilePicker, loadFiles } from "./DragDropLoader";
2
- export { loadModel, loadTexture, parseModelFromFile, parseTextureFromFile } from "./modelLoader";
2
+ export { loadModel, loadSound, loadTexture, parseModelFromFile, parseSoundFromFile, parseTextureFromFile } from "./modelLoader";
@@ -1,8 +1,10 @@
1
1
  import type { Object3D, Texture } from "three";
2
2
  export type LoadedModel = Object3D;
3
3
  export type LoadedTexture = Texture;
4
+ export type LoadedSound = AudioBuffer;
4
5
  export type LoadedModels = Record<string, LoadedModel>;
5
6
  export type LoadedTextures = Record<string, LoadedTexture>;
7
+ export type LoadedSounds = Record<string, LoadedSound>;
6
8
  export type ModelLoadResult = {
7
9
  success: boolean;
8
10
  model?: LoadedModel;
@@ -13,10 +15,18 @@ export type TextureLoadResult = {
13
15
  texture?: LoadedTexture;
14
16
  error?: unknown;
15
17
  };
18
+ export type SoundLoadResult = {
19
+ success: boolean;
20
+ sound?: LoadedSound;
21
+ error?: unknown;
22
+ };
16
23
  export type ProgressCallback = (filename: string, loaded: number, total: number) => void;
17
24
  export declare function canParseModelFile(file: File | string): boolean;
18
25
  export declare function canParseTextureFile(file: File | string): boolean;
26
+ export declare function canParseSoundFile(file: File | string): boolean;
19
27
  export declare function parseModelFromFile(file: File): Promise<ModelLoadResult>;
20
28
  export declare function parseTextureFromFile(file: File): Promise<TextureLoadResult>;
29
+ export declare function parseSoundFromFile(file: File): Promise<SoundLoadResult>;
21
30
  export declare function loadModel(filename: string, onProgress?: ProgressCallback): Promise<ModelLoadResult>;
22
31
  export declare function loadTexture(filename: string): Promise<TextureLoadResult>;
32
+ export declare function loadSound(filename: string): Promise<SoundLoadResult>;
@@ -16,6 +16,21 @@ gltfLoader.setDRACOLoader(dracoLoader);
16
16
  const fbxLoader = new FBXLoader();
17
17
  const textureLoader = new TextureLoader();
18
18
  const TEXTURE_FILE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".svg"];
19
+ const SOUND_FILE_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a"];
20
+ let decodeAudioContext = null;
21
+ function getAudioContext() {
22
+ if (typeof window === 'undefined') {
23
+ throw new Error('Audio loading is only supported in the browser');
24
+ }
25
+ if (!decodeAudioContext) {
26
+ const AudioCtx = window.AudioContext || window.webkitAudioContext;
27
+ if (!AudioCtx) {
28
+ throw new Error('Web Audio API is not available in this browser');
29
+ }
30
+ decodeAudioContext = new AudioCtx();
31
+ }
32
+ return decodeAudioContext;
33
+ }
19
34
  function normalizeModelPath(name) {
20
35
  return name.split(/[?#]/, 1)[0].toLowerCase();
21
36
  }
@@ -38,6 +53,11 @@ export function canParseTextureFile(file) {
38
53
  const normalizedName = normalizeModelPath(filename);
39
54
  return TEXTURE_FILE_EXTENSIONS.some(extension => normalizedName.endsWith(extension));
40
55
  }
56
+ export function canParseSoundFile(file) {
57
+ const filename = typeof file === "string" ? file : file.name;
58
+ const normalizedName = normalizeModelPath(filename);
59
+ return SOUND_FILE_EXTENSIONS.some(extension => normalizedName.endsWith(extension));
60
+ }
41
61
  function parseModelBuffer(arrayBuffer, sourceName) {
42
62
  const modelFileKind = getModelFileKind(sourceName);
43
63
  if (modelFileKind === "gltf") {
@@ -89,6 +109,29 @@ export function parseTextureFromFile(file) {
89
109
  });
90
110
  });
91
111
  }
112
+ export function parseSoundFromFile(file) {
113
+ return new Promise(resolve => {
114
+ const reader = new FileReader();
115
+ reader.onload = (event) => __awaiter(this, void 0, void 0, function* () {
116
+ var _a;
117
+ const arrayBuffer = (_a = event.target) === null || _a === void 0 ? void 0 : _a.result;
118
+ if (!arrayBuffer) {
119
+ resolve({ success: false, error: new Error('Failed to read file') });
120
+ return;
121
+ }
122
+ try {
123
+ const context = getAudioContext();
124
+ const sound = yield context.decodeAudioData(arrayBuffer.slice(0));
125
+ resolve({ success: true, sound });
126
+ }
127
+ catch (error) {
128
+ resolve({ success: false, error });
129
+ }
130
+ });
131
+ reader.onerror = () => resolve({ success: false, error: reader.error });
132
+ reader.readAsArrayBuffer(file);
133
+ });
134
+ }
92
135
  export function loadModel(filename, onProgress) {
93
136
  return __awaiter(this, void 0, void 0, function* () {
94
137
  try {
@@ -141,3 +184,20 @@ export function loadTexture(filename) {
141
184
  }
142
185
  });
143
186
  }
187
+ export function loadSound(filename) {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ try {
190
+ if (!canParseSoundFile(filename)) {
191
+ return { success: false, error: new Error(`Unsupported file format: ${filename}`) };
192
+ }
193
+ const response = yield fetch(filename);
194
+ const arrayBuffer = yield response.arrayBuffer();
195
+ const context = getAudioContext();
196
+ const sound = yield context.decodeAudioData(arrayBuffer.slice(0));
197
+ return { success: true, sound };
198
+ }
199
+ catch (error) {
200
+ return { success: false, error };
201
+ }
202
+ });
203
+ }
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { memo, useCallback, useState } from 'react';
3
- import { getComponent } from './components/ComponentRegistry';
3
+ import { getComponentDef } from './components/ComponentRegistry';
4
4
  import { base, colors, tree } from './styles';
5
- import { useEditorContext } from './EditorContext';
5
+ import { useEditorContext } from './PrefabEditor';
6
6
  import { Dropdown } from './Dropdown';
7
7
  import { FileMenu, TreeContextMenu, TreeNodeMenu } from './EditorTreeMenus';
8
8
  import { usePrefabChildIds, usePrefabNode, usePrefabRootId, usePrefabStore, usePrefabStoreApi } from './prefabStore';
@@ -37,7 +37,7 @@ export default function EditorTree({ selectedId, setSelectedId, getPrefab, onRep
37
37
  components: {
38
38
  transform: {
39
39
  type: "Transform",
40
- properties: Object.assign({}, (_a = getComponent('Transform')) === null || _a === void 0 ? void 0 : _a.defaultProperties)
40
+ properties: Object.assign({}, (_a = getComponentDef('Transform')) === null || _a === void 0 ? void 0 : _a.defaultProperties)
41
41
  }
42
42
  }
43
43
  };
@@ -142,37 +142,13 @@ const TreeNode = memo(function TreeNode({ nodeId, depth, rootId, visibleIds, col
142
142
  marginRight: 4,
143
143
  cursor: 'pointer',
144
144
  visibility: hasChildren ? 'visible' : 'hidden'
145
- }, onClick: (e) => hasChildren && onToggleCollapse(e, nodeId), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id }), node.locked && _jsx("span", { style: { marginLeft: 6, opacity: 0.6 }, children: "\uD83D\uDD12" })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Node Actions", style: {
146
- background: 'none',
147
- border: 'none',
148
- cursor: 'pointer',
149
- padding: '0 4px',
150
- fontSize: 14,
151
- opacity: 0.7,
152
- color: 'inherit',
153
- }, onClick: (e) => {
145
+ }, onClick: (e) => hasChildren && onToggleCollapse(e, nodeId), children: isCollapsed ? '▶' : '▼' }), !isRoot && _jsx("span", { style: { marginRight: 4, opacity: 0.4 }, children: "\u22EE\u22EE" }), _jsx("span", { style: { overflow: 'hidden', textOverflow: 'ellipsis' }, children: (_a = node.name) !== null && _a !== void 0 ? _a : node.id }), node.locked && _jsx("span", { style: { marginLeft: 6, opacity: 0.6 }, children: "\uD83D\uDD12" })] }), !isRoot && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 2 }, children: [_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Node Actions", style: tree.iconButton, onClick: (e) => {
154
146
  e.stopPropagation();
155
147
  toggle();
156
- }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, false, close) }), _jsx("button", { style: {
157
- background: 'none',
158
- border: 'none',
159
- cursor: 'pointer',
160
- padding: '0 4px',
161
- fontSize: 14,
162
- opacity: node.disabled ? 0.5 : 0.7,
163
- color: 'inherit',
164
- }, onClick: (e) => {
148
+ }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, false, close) }), _jsx("button", { style: Object.assign(Object.assign({}, tree.iconButton), { opacity: node.disabled ? 0.5 : 0.7 }), onClick: (e) => {
165
149
  e.stopPropagation();
166
150
  onToggleDisabled(nodeId);
167
- }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Scene Actions", style: {
168
- background: 'none',
169
- border: 'none',
170
- cursor: 'pointer',
171
- padding: '0 4px',
172
- fontSize: 14,
173
- opacity: 0.7,
174
- color: 'inherit',
175
- }, onClick: (e) => {
151
+ }, title: node.disabled ? 'Enable' : 'Disable', children: node.disabled ? '◎' : '◉' })] })), isRoot && (_jsx(Dropdown, { placement: "bottom-end", trigger: ({ ref, toggle }) => (_jsx("button", { ref: ref, title: "Scene Actions", style: tree.iconButton, onClick: (e) => {
176
152
  e.stopPropagation();
177
153
  toggle();
178
154
  }, children: "\u22EF" })), children: (close) => renderTreeNodeMenu(nodeId, true, close) }))] }), !isCollapsed && childIds.map(childId => (_jsx(TreeNode, { nodeId: childId, depth: depth + 1, rootId: rootId, visibleIds: visibleIds, collapsedIds: collapsedIds, dropTarget: dropTarget, selectedNodeId: selectedNodeId, onToggleCollapse: onToggleCollapse, onOpenContextMenu: onOpenContextMenu, onDragStart: onDragStart, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, onDragEnd: onDragEnd, renderTreeNodeMenu: renderTreeNodeMenu, onToggleDisabled: onToggleDisabled, setSelectedId: setSelectedId }, childId)))] }));
@@ -11,8 +11,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { useEffect, useRef, useState } from 'react';
12
12
  import { createPortal } from 'react-dom';
13
13
  import { menu } from './styles';
14
- import { useEditorContext } from './EditorContext';
15
- import { getComponent } from './components/ComponentRegistry';
14
+ import { useEditorContext } from './PrefabEditor';
15
+ import { getComponentDef } from './components/ComponentRegistry';
16
16
  import { loadJson, saveJson } from './utils';
17
17
  function createEmptyPrefab() {
18
18
  var _a;
@@ -25,7 +25,7 @@ function createEmptyPrefab() {
25
25
  components: {
26
26
  transform: {
27
27
  type: 'Transform',
28
- properties: Object.assign({}, (_a = getComponent('Transform')) === null || _a === void 0 ? void 0 : _a.defaultProperties)
28
+ properties: Object.assign({}, (_a = getComponentDef('Transform')) === null || _a === void 0 ? void 0 : _a.defaultProperties)
29
29
  }
30
30
  },
31
31
  children: []
@@ -11,8 +11,9 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
13
  import { useState } from 'react';
14
+ import { hasComponent } from "./types";
14
15
  import EditorTree from './EditorTree';
15
- import { getAllComponents } from './components/ComponentRegistry';
16
+ import { getAllComponentDefs } from './components/ComponentRegistry';
16
17
  import { base, colors, inspector, componentCard } from './styles';
17
18
  import { usePrefabStore } from './prefabStore';
18
19
  function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImportPrefab, basePath, onUndo, onRedo, canUndo, canRedo }) {
@@ -36,9 +37,9 @@ function EditorUI({ selectedId, setSelectedId, getPrefab, onReplacePrefab, onImp
36
37
  }
37
38
  function NodeInspector({ node, updateNode, deleteNode, basePath }) {
38
39
  var _a;
39
- const ALL_COMPONENTS = getAllComponents();
40
+ const ALL_COMPONENTS = getAllComponentDefs();
40
41
  const allKeys = Object.keys(ALL_COMPONENTS);
41
- const available = allKeys.filter(k => { var _a; return !((_a = node.components) === null || _a === void 0 ? void 0 : _a[k.toLowerCase()]); });
42
+ const available = allKeys.filter(k => !hasComponent(node, k));
42
43
  const [preferredAddType, setAddType] = useState(available[0] || "");
43
44
  const addType = available.includes(preferredAddType) ? preferredAddType : (available[0] || "");
44
45
  return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: "flex", marginBottom: 8, alignItems: 'center', gap: 8 }, children: [_jsx("div", { style: { fontSize: 10, color: colors.textDim, wordBreak: 'break-all', border: `1px solid ${colors.border}`, padding: '2px 6px', borderRadius: 3, flex: 1, fontFamily: 'monospace' }, children: node.id }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), title: "Delete Node", onClick: deleteNode, children: "\u274C" })] }), _jsx("input", { style: base.input, value: (_a = node.name) !== null && _a !== void 0 ? _a : "", placeholder: 'Node name', onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { name: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsx("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, children: _jsx("div", { style: base.label, children: "Components" }) }), node.components && Object.entries(node.components).map(([key, comp]) => {
@@ -48,7 +49,8 @@ function NodeInspector({ node, updateNode, deleteNode, basePath }) {
48
49
  if (!def)
49
50
  return _jsxs("div", { style: { color: colors.danger, fontSize: 11 }, children: ["Unknown: ", comp.type] }, key);
50
51
  return (_jsxs("div", { style: componentCard.container, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 500 }, children: key }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), { padding: '2px 6px' }), title: "Remove Component", onClick: () => updateNode(n => {
51
- const _a = n.components || {}, _b = key, _ = _a[_b], rest = __rest(_a, [typeof _b === "symbol" ? _b : _b + ""]);
52
+ var _a;
53
+ const _b = (_a = n.components) !== null && _a !== void 0 ? _a : {}, _c = key, _ = _b[_c], rest = __rest(_b, [typeof _c === "symbol" ? _c : _c + ""]);
52
54
  return Object.assign(Object.assign({}, n), { components: rest });
53
55
  }), children: "\u2715" })] }), def.Editor && (_jsx(def.Editor, { component: comp, node: node, onUpdate: (newProps) => updateNode(n => (Object.assign(Object.assign({}, n), { components: Object.assign(Object.assign({}, n.components), { [key]: Object.assign(Object.assign({}, comp), { properties: Object.assign(Object.assign({}, comp.properties), newProps) }) }) }))), basePath: basePath }))] }, key));
54
56
  })] }), available.length > 0 && (_jsx("div", { children: _jsxs("div", { style: base.row, children: [_jsx("select", { style: Object.assign(Object.assign({}, base.input), { flex: 1 }), value: addType, onChange: e => setAddType(e.target.value), children: available.map(k => _jsx("option", { value: k, children: k }, k)) }), _jsx("button", { style: base.btn, disabled: !addType, onClick: () => {
@@ -13,6 +13,7 @@ export type InstanceData = {
13
13
  id: string;
14
14
  sourceId: string;
15
15
  clickable?: boolean;
16
+ clickEventName?: string;
16
17
  locked?: boolean;
17
18
  position: [number, number, number];
18
19
  rotation: [number, number, number];
@@ -35,6 +36,7 @@ export declare const GameInstance: React.ForwardRefExoticComponent<{
35
36
  id: string;
36
37
  sourceId?: string;
37
38
  clickable?: boolean;
39
+ clickEventName?: string;
38
40
  modelUrl: string;
39
41
  locked?: boolean;
40
42
  position: [number, number, number];