react-three-game 0.0.30 → 0.0.31

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.
@@ -6,6 +6,12 @@ import { Suspense, useEffect, useState, useRef } from "react";
6
6
  import { TextureLoader } from "three";
7
7
  import { loadModel } from "../dragdrop/modelLoader";
8
8
  // view models and textures in manifest, onselect callback
9
+ const styles = {
10
+ errorIcon: { color: '#fca5a5', fontSize: 12 }, // text-red-400 text-xs
11
+ flexFillRelative: { flex: 1, position: 'relative' },
12
+ bottomLabel: { backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' },
13
+ iconLarge: { fontSize: 20 }
14
+ };
9
15
  function getItemsInPath(files, currentPath) {
10
16
  // Remove the leading category folder (e.g., /textures/, /models/, /sounds/)
11
17
  const filesWithoutCategory = files.map(file => {
@@ -30,7 +36,15 @@ function getItemsInPath(files, currentPath) {
30
36
  return { folders: Array.from(folders), filesInCurrentPath };
31
37
  }
32
38
  function FolderTile({ name, onClick }) {
33
- return (_jsxs("div", { onClick: onClick, className: "aspect-square bg-gray-800 cursor-pointer hover:bg-gray-700 flex flex-col items-center justify-center", children: [_jsx("div", { className: "text-3xl", children: "\uD83D\uDCC1" }), _jsx("div", { className: "text-xs text-center truncate w-full px-1 mt-1", children: name })] }));
39
+ return (_jsxs("div", { onClick: onClick, style: {
40
+ aspectRatio: '1 / 1',
41
+ backgroundColor: '#1f2937', /* gray-800 */
42
+ cursor: 'pointer',
43
+ display: 'flex',
44
+ flexDirection: 'column',
45
+ alignItems: 'center',
46
+ justifyContent: 'center'
47
+ }, children: [_jsx("div", { style: { fontSize: 24 }, children: "\uD83D\uDCC1" }), _jsx("div", { style: { fontSize: 10, textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%', padding: '0 4px', marginTop: 4 }, children: name })] }));
34
48
  }
35
49
  function useInView() {
36
50
  const [isInView, setIsInView] = useState(false);
@@ -56,13 +70,13 @@ function AssetListViewer({ files, selected, onSelect, renderCard }) {
56
70
  const { folders, filesInCurrentPath } = getItemsInPath(files, currentPath);
57
71
  const showCompactView = selected && !showPicker;
58
72
  if (showCompactView) {
59
- return (_jsxs("div", { className: "flex gap-1 items-center", children: [renderCard(selected, onSelect), _jsx("button", { onClick: () => setShowPicker(true), className: "px-2 py-1 bg-gray-800 hover:bg-gray-700 text-xs", children: "Change" })] }));
73
+ return (_jsxs("div", { style: { display: 'flex', gap: 4, alignItems: 'center' }, children: [renderCard(selected, onSelect), _jsx("button", { onClick: () => setShowPicker(true), style: { padding: '4px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 12, cursor: 'pointer', border: 'none' }, children: "Change" })] }));
60
74
  }
61
75
  return (_jsxs("div", { children: [currentPath && (_jsx("button", { onClick: () => {
62
76
  const pathParts = currentPath.split('/').filter(Boolean);
63
77
  pathParts.pop();
64
78
  setCurrentPath(pathParts.join('/'));
65
- }, className: "mb-1 px-2 py-1 bg-gray-800 hover:bg-gray-700 text-xs", children: "\u2190 Back" })), _jsxs("div", { className: "grid grid-cols-3 gap-1", children: [folders.map((folder) => (_jsx(FolderTile, { name: folder, onClick: () => setCurrentPath(currentPath ? `${currentPath}/${folder}` : folder) }, folder))), filesInCurrentPath.map((file) => (_jsx("div", { children: renderCard(file, (f) => {
79
+ }, style: { marginBottom: 4, padding: '4px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 12, cursor: 'pointer', border: 'none' }, children: "\u2190 Back" })), _jsxs("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }, children: [folders.map((folder) => (_jsx(FolderTile, { name: folder, onClick: () => setCurrentPath(currentPath ? `${currentPath}/${folder}` : folder) }, folder))), filesInCurrentPath.map((file) => (_jsx("div", { children: renderCard(file, (f) => {
66
80
  onSelect(f);
67
81
  if (selected)
68
82
  setShowPicker(false);
@@ -77,9 +91,9 @@ function TextureCard({ file, onSelect, basePath = "" }) {
77
91
  const { ref, isInView } = useInView();
78
92
  const fullPath = basePath ? `/${basePath}${file}` : file;
79
93
  if (error) {
80
- return (_jsx("div", { ref: ref, className: "aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex items-center justify-center", onClick: () => onSelect(file), children: _jsx("div", { className: "text-red-400 text-xs", children: "\u2717" }) }));
94
+ return (_jsx("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }, onClick: () => onSelect(file), children: _jsx("div", { style: styles.errorIcon, children: "\u2717" }) }));
81
95
  }
82
- return (_jsxs("div", { ref: ref, className: "aspect-square bg-gray-800 cursor-pointer hover:bg-gray-700 flex flex-col", onClick: () => onSelect(file), onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [_jsx("div", { className: "flex-1 relative", children: isInView ? (_jsxs(View, { className: "w-full h-full", children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 0, 2.5], fov: 50 }), _jsxs(Suspense, { fallback: null, children: [_jsx("ambientLight", { intensity: 0.8 }), _jsx("pointLight", { position: [5, 5, 5], intensity: 0.5 }), _jsx(TextureSphere, { url: fullPath, onError: () => setError(true) }), _jsx(OrbitControls, { enableZoom: false, enablePan: false, autoRotate: isHovered, autoRotateSpeed: 2 })] })] })) : null }), _jsx("div", { className: "bg-black/60 text-[10px] px-1 truncate text-center", children: file.split('/').pop() })] }));
96
+ return (_jsxs("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor: '#1f2937', cursor: 'pointer', display: 'flex', flexDirection: 'column' }, onClick: () => onSelect(file), onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [_jsx("div", { style: { flex: 1, position: 'relative' }, children: isInView ? (_jsxs(View, { style: { width: '100%', height: '100%' }, children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 0, 2.5], fov: 50 }), _jsxs(Suspense, { fallback: null, children: [_jsx("ambientLight", { intensity: 0.8 }), _jsx("pointLight", { position: [5, 5, 5], intensity: 0.5 }), _jsx(TextureSphere, { url: fullPath, onError: () => setError(true) }), _jsx(OrbitControls, { enableZoom: false, enablePan: false, autoRotate: isHovered, autoRotateSpeed: 2 })] })] })) : null }), _jsx("div", { style: { backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' }, children: file.split('/').pop() })] }));
83
97
  }
84
98
  function TextureSphere({ url, onError }) {
85
99
  const texture = useLoader(TextureLoader, url, undefined, (error) => {
@@ -96,9 +110,9 @@ function ModelCard({ file, onSelect, basePath = "" }) {
96
110
  const { ref, isInView } = useInView();
97
111
  const fullPath = basePath ? `/${basePath}${file}` : file;
98
112
  if (error) {
99
- return (_jsx("div", { ref: ref, className: "aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex items-center justify-center", onClick: () => onSelect(file), children: _jsx("div", { className: "text-red-400 text-xs", children: "\u2717" }) }));
113
+ return (_jsx("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }, onClick: () => onSelect(file), children: _jsx("div", { style: styles.errorIcon, children: "\u2717" }) }));
100
114
  }
101
- return (_jsxs("div", { ref: ref, className: "aspect-square bg-gray-900 cursor-pointer hover:bg-gray-800 flex flex-col", onClick: () => onSelect(file), children: [_jsx("div", { className: "flex-1 relative", children: isInView ? (_jsxs(View, { className: "w-full h-full", children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 1, 3], fov: 50 }), _jsxs(Suspense, { fallback: null, children: [_jsx(Stage, { intensity: 0.5, environment: "city", children: _jsx(ModelPreview, { url: fullPath, onError: () => setError(true) }) }), _jsx(OrbitControls, { enableZoom: false })] })] })) : null }), _jsx("div", { className: "bg-black/60 text-[10px] px-1 truncate text-center", children: file.split('/').pop() })] }));
115
+ return (_jsxs("div", { ref: ref, style: { aspectRatio: '1 / 1', backgroundColor: '#111827', cursor: 'pointer', display: 'flex', flexDirection: 'column' }, onClick: () => onSelect(file), children: [_jsx("div", { style: styles.flexFillRelative, children: isInView ? (_jsxs(View, { style: { width: '100%', height: '100%' }, children: [_jsx(PerspectiveCamera, { makeDefault: true, position: [0, 1, 3], fov: 50 }), _jsxs(Suspense, { fallback: null, children: [_jsx(Stage, { intensity: 0.5, environment: "city", children: _jsx(ModelPreview, { url: fullPath, onError: () => setError(true) }) }), _jsx(OrbitControls, { enableZoom: false })] })] })) : null }), _jsx("div", { style: { backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' }, children: file.split('/').pop() })] }));
102
116
  }
103
117
  function ModelPreview({ url, onError }) {
104
118
  const [model, setModel] = useState(null);
@@ -130,7 +144,7 @@ export function SoundListViewer({ files, selected, onSelect, basePath = "" }) {
130
144
  function SoundCard({ file, onSelect, basePath = "" }) {
131
145
  const fileName = file.split('/').pop() || '';
132
146
  const fullPath = basePath ? `/${basePath}${file}` : file;
133
- return (_jsxs("div", { onClick: () => onSelect(file), className: "aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex flex-col items-center justify-center", children: [_jsx("div", { className: "text-2xl", children: "\uD83D\uDD0A" }), _jsx("div", { className: "text-[10px] px-1 mt-1 truncate text-center w-full", children: fileName })] }));
147
+ return (_jsxs("div", { onClick: () => onSelect(file), style: { aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }, children: [_jsx("div", { style: styles.iconLarge, children: "\uD83D\uDD0A" }), _jsx("div", { style: { fontSize: 12, padding: '0 4px', marginTop: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center', width: '100%' }, children: fileName })] }));
134
148
  }
135
149
  // Shared Canvas Component - can be used independently in any viewer
136
150
  export function SharedCanvas() {
@@ -29,7 +29,11 @@ function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, transf
29
29
  setSelectedId(null);
30
30
  };
31
31
  const selectedNode = selectedId && prefabData ? findNode(prefabData.root, selectedId) : null;
32
- return _jsxs(_Fragment, { children: [_jsxs("div", { style: inspector.panel, children: [_jsxs("div", { style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Inspector" }), _jsx("span", { children: collapsed ? '◀' : '▼' })] }), !collapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNodeHandler, deleteNode: deleteNodeHandler, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath }))] }), _jsx("div", { style: { position: 'absolute', top: 8, left: 8, zIndex: 20 }, children: _jsx(EditorTree, { prefabData: prefabData, setPrefabData: setPrefabData, selectedId: selectedId, setSelectedId: setSelectedId }) })] });
32
+ return _jsxs(_Fragment, { children: [_jsx("style", { children: `.prefab-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
33
+ .prefab-scroll::-webkit-scrollbar-track { background: transparent; }
34
+ .prefab-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.06); border-radius: 8px; }
35
+ .prefab-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.06) transparent; }
36
+ ` }), _jsxs("div", { style: inspector.panel, children: [_jsxs("div", { style: base.header, onClick: () => setCollapsed(!collapsed), children: [_jsx("span", { children: "Inspector" }), _jsx("span", { children: collapsed ? '◀' : '▼' })] }), !collapsed && selectedNode && (_jsx(NodeInspector, { node: selectedNode, updateNode: updateNodeHandler, deleteNode: deleteNodeHandler, transformMode: transformMode, setTransformMode: setTransformMode, basePath: basePath }))] }), _jsx("div", { style: { position: 'absolute', top: 8, left: 8, zIndex: 20 }, children: _jsx(EditorTree, { prefabData: prefabData, setPrefabData: setPrefabData, selectedId: selectedId, setSelectedId: setSelectedId }) })] });
33
37
  }
34
38
  function NodeInspector({ node, updateNode, deleteNode, transformMode, setTransformMode, basePath }) {
35
39
  var _a;
@@ -42,7 +46,7 @@ function NodeInspector({ node, updateNode, deleteNode, transformMode, setTransfo
42
46
  if (!newAvailable.includes(addType))
43
47
  setAddType(newAvailable[0] || "");
44
48
  }, [Object.keys(node.components || {}).join(',')]);
45
- return _jsxs("div", { style: inspector.content, children: [_jsxs("div", { style: base.section, children: [_jsx("div", { style: base.label, children: "Node ID" }), _jsx("input", { style: base.input, value: (_a = node.name) !== null && _a !== void 0 ? _a : "", onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { name: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, children: [_jsx("div", { style: base.label, children: "Components" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), onClick: deleteNode, children: "Delete Node" })] }), node.components && Object.entries(node.components).map(([key, comp]) => {
49
+ return _jsxs("div", { style: inspector.content, className: "prefab-scroll", children: [_jsxs("div", { style: base.section, children: [_jsx("div", { style: base.label, children: "Node ID" }), _jsx("input", { style: base.input, value: (_a = node.name) !== null && _a !== void 0 ? _a : "", onChange: e => updateNode(n => (Object.assign(Object.assign({}, n), { name: e.target.value }))) })] }), _jsxs("div", { style: base.section, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, children: [_jsx("div", { style: base.label, children: "Components" }), _jsx("button", { style: Object.assign(Object.assign({}, base.btn), base.btnDanger), onClick: deleteNode, children: "Delete Node" })] }), node.components && Object.entries(node.components).map(([key, comp]) => {
46
50
  if (!comp)
47
51
  return null;
48
52
  const def = ALL_COMPONENTS[comp.type];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-three-game",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "Batteries included React Three Fiber game engine",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -8,6 +8,13 @@ import { loadModel } from "../dragdrop/modelLoader";
8
8
 
9
9
  // view models and textures in manifest, onselect callback
10
10
 
11
+ const styles: Record<string, any> = {
12
+ errorIcon: { color: '#fca5a5', fontSize: 12 }, // text-red-400 text-xs
13
+ flexFillRelative: { flex: 1, position: 'relative' },
14
+ bottomLabel: { backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' },
15
+ iconLarge: { fontSize: 20 }
16
+ };
17
+
11
18
  function getItemsInPath(files: string[], currentPath: string) {
12
19
  // Remove the leading category folder (e.g., /textures/, /models/, /sounds/)
13
20
  const filesWithoutCategory = files.map(file => {
@@ -40,10 +47,18 @@ function FolderTile({ name, onClick }: { name: string; onClick: () => void }) {
40
47
  return (
41
48
  <div
42
49
  onClick={onClick}
43
- className="aspect-square bg-gray-800 cursor-pointer hover:bg-gray-700 flex flex-col items-center justify-center"
50
+ style={{
51
+ aspectRatio: '1 / 1',
52
+ backgroundColor: '#1f2937', /* gray-800 */
53
+ cursor: 'pointer',
54
+ display: 'flex',
55
+ flexDirection: 'column',
56
+ alignItems: 'center',
57
+ justifyContent: 'center'
58
+ }}
44
59
  >
45
- <div className="text-3xl">📁</div>
46
- <div className="text-xs text-center truncate w-full px-1 mt-1">{name}</div>
60
+ <div style={{ fontSize: 24 }}>📁</div>
61
+ <div style={{ fontSize: 10, textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%', padding: '0 4px', marginTop: 4 }}>{name}</div>
47
62
  </div>
48
63
  );
49
64
  }
@@ -90,11 +105,11 @@ function AssetListViewer({ files, selected, onSelect, renderCard }: AssetListVie
90
105
 
91
106
  if (showCompactView) {
92
107
  return (
93
- <div className="flex gap-1 items-center">
108
+ <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
94
109
  {renderCard(selected, onSelect)}
95
110
  <button
96
111
  onClick={() => setShowPicker(true)}
97
- className="px-2 py-1 bg-gray-800 hover:bg-gray-700 text-xs"
112
+ style={{ padding: '4px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 12, cursor: 'pointer', border: 'none' }}
98
113
  >
99
114
  Change
100
115
  </button>
@@ -111,12 +126,12 @@ function AssetListViewer({ files, selected, onSelect, renderCard }: AssetListVie
111
126
  pathParts.pop();
112
127
  setCurrentPath(pathParts.join('/'));
113
128
  }}
114
- className="mb-1 px-2 py-1 bg-gray-800 hover:bg-gray-700 text-xs"
129
+ style={{ marginBottom: 4, padding: '4px 8px', backgroundColor: '#1f2937', color: 'inherit', fontSize: 12, cursor: 'pointer', border: 'none' }}
115
130
  >
116
131
  ← Back
117
132
  </button>
118
133
  )}
119
- <div className="grid grid-cols-3 gap-1">
134
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 4 }}>
120
135
  {folders.map((folder) => (
121
136
  <FolderTile
122
137
  key={folder}
@@ -170,10 +185,10 @@ function TextureCard({ file, onSelect, basePath = "" }: { file: string; onSelect
170
185
  return (
171
186
  <div
172
187
  ref={ref}
173
- className="aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex items-center justify-center"
188
+ style={{ aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
174
189
  onClick={() => onSelect(file)}
175
190
  >
176
- <div className="text-red-400 text-xs">✗</div>
191
+ <div style={styles.errorIcon}>✗</div>
177
192
  </div>
178
193
  );
179
194
  }
@@ -181,14 +196,14 @@ function TextureCard({ file, onSelect, basePath = "" }: { file: string; onSelect
181
196
  return (
182
197
  <div
183
198
  ref={ref}
184
- className="aspect-square bg-gray-800 cursor-pointer hover:bg-gray-700 flex flex-col"
199
+ style={{ aspectRatio: '1 / 1', backgroundColor: '#1f2937', cursor: 'pointer', display: 'flex', flexDirection: 'column' }}
185
200
  onClick={() => onSelect(file)}
186
201
  onMouseEnter={() => setIsHovered(true)}
187
202
  onMouseLeave={() => setIsHovered(false)}
188
203
  >
189
- <div className="flex-1 relative">
204
+ <div style={{ flex: 1, position: 'relative' }}>
190
205
  {isInView ? (
191
- <View className="w-full h-full">
206
+ <View style={{ width: '100%', height: '100%' }}>
192
207
  <PerspectiveCamera makeDefault position={[0, 0, 2.5]} fov={50} />
193
208
  <Suspense fallback={null}>
194
209
  <ambientLight intensity={0.8} />
@@ -204,7 +219,7 @@ function TextureCard({ file, onSelect, basePath = "" }: { file: string; onSelect
204
219
  </View>
205
220
  ) : null}
206
221
  </div>
207
- <div className="bg-black/60 text-[10px] px-1 truncate text-center">
222
+ <div style={{ backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' }}>
208
223
  {file.split('/').pop()}
209
224
  </div>
210
225
  </div>
@@ -256,10 +271,10 @@ function ModelCard({ file, onSelect, basePath = "" }: { file: string; onSelect:
256
271
  return (
257
272
  <div
258
273
  ref={ref}
259
- className="aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex items-center justify-center"
274
+ style={{ aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
260
275
  onClick={() => onSelect(file)}
261
276
  >
262
- <div className="text-red-400 text-xs">✗</div>
277
+ <div style={styles.errorIcon}>✗</div>
263
278
  </div>
264
279
  );
265
280
  }
@@ -267,12 +282,12 @@ function ModelCard({ file, onSelect, basePath = "" }: { file: string; onSelect:
267
282
  return (
268
283
  <div
269
284
  ref={ref}
270
- className="aspect-square bg-gray-900 cursor-pointer hover:bg-gray-800 flex flex-col"
285
+ style={{ aspectRatio: '1 / 1', backgroundColor: '#111827', cursor: 'pointer', display: 'flex', flexDirection: 'column' }}
271
286
  onClick={() => onSelect(file)}
272
287
  >
273
- <div className="flex-1 relative">
288
+ <div style={styles.flexFillRelative}>
274
289
  {isInView ? (
275
- <View className="w-full h-full">
290
+ <View style={{ width: '100%', height: '100%' }}>
276
291
  <PerspectiveCamera makeDefault position={[0, 1, 3]} fov={50} />
277
292
  <Suspense fallback={null}>
278
293
  <Stage intensity={0.5} environment="city">
@@ -283,7 +298,7 @@ function ModelCard({ file, onSelect, basePath = "" }: { file: string; onSelect:
283
298
  </View>
284
299
  ) : null}
285
300
  </div>
286
- <div className="bg-black/60 text-[10px] px-1 truncate text-center">
301
+ <div style={{ backgroundColor: 'rgba(0,0,0,0.6)', fontSize: 10, padding: '0 4px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center' }}>
287
302
  {file.split('/').pop()}
288
303
  </div>
289
304
  </div>
@@ -341,10 +356,10 @@ function SoundCard({ file, onSelect, basePath = "" }: { file: string; onSelect:
341
356
  return (
342
357
  <div
343
358
  onClick={() => onSelect(file)}
344
- className="aspect-square bg-gray-700 cursor-pointer hover:bg-gray-600 flex flex-col items-center justify-center"
359
+ style={{ aspectRatio: '1 / 1', backgroundColor: '#374151', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}
345
360
  >
346
- <div className="text-2xl">🔊</div>
347
- <div className="text-[10px] px-1 mt-1 truncate text-center w-full">{fileName}</div>
361
+ <div style={styles.iconLarge}>🔊</div>
362
+ <div style={{ fontSize: 12, padding: '0 4px', marginTop: 4, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'center', width: '100%' }}>{fileName}</div>
348
363
  </div>
349
364
  );
350
365
  }
@@ -33,6 +33,11 @@ function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, transf
33
33
  const selectedNode = selectedId && prefabData ? findNode(prefabData.root, selectedId) : null;
34
34
 
35
35
  return <>
36
+ <style>{`.prefab-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
37
+ .prefab-scroll::-webkit-scrollbar-track { background: transparent; }
38
+ .prefab-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.06); border-radius: 8px; }
39
+ .prefab-scroll { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,0.06) transparent; }
40
+ `}</style>
36
41
  <div style={inspector.panel}>
37
42
  <div style={base.header} onClick={() => setCollapsed(!collapsed)}>
38
43
  <span>Inspector</span>
@@ -46,6 +51,7 @@ function EditorUI({ prefabData, setPrefabData, selectedId, setSelectedId, transf
46
51
  transformMode={transformMode}
47
52
  setTransformMode={setTransformMode}
48
53
  basePath={basePath}
54
+ // add class to make scrollbar gutter transparent via CSS above
49
55
  />
50
56
  )}
51
57
  </div>
@@ -79,7 +85,7 @@ function NodeInspector({ node, updateNode, deleteNode, transformMode, setTransfo
79
85
  if (!newAvailable.includes(addType)) setAddType(newAvailable[0] || "");
80
86
  }, [Object.keys(node.components || {}).join(',')]);
81
87
 
82
- return <div style={inspector.content}>
88
+ return <div style={inspector.content} className="prefab-scroll">
83
89
  {/* Node ID */}
84
90
  <div style={base.section}>
85
91
  <div style={base.label}>Node ID</div>