AntDX316 commited on
Commit
4d0d400
·
1 Parent(s): 6fca17b
.bolt/config.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "template": "bolt-vite-react-ts"
3
+ }
.bolt/prompt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ For all designs I ask you to make, have them be beautiful, not cookie cutter. Make webpages that are fully featured and worthy for production.
2
+
3
+ By default, this template supports JSX syntax with Tailwind CSS classes, React hooks, and Lucide React for icons. Do not install other packages for UI themes, icons, etc unless absolutely necessary or I request them.
4
+
5
+ Use icons from lucide-react for logos.
6
+
7
+ Use stock photos from unsplash where appropriate, only valid URLs you know exist. Do not download the images, only link to them in image tags.
8
+
.gitignore ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ # Editor directories and files
16
+ .vscode/*
17
+ !.vscode/extensions.json
18
+ .idea
19
+ .DS_Store
20
+ *.suo
21
+ *.ntvs*
22
+ *.njsproj
23
+ *.sln
24
+ *.sw?
eslint.config.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import js from '@eslint/js';
2
+ import globals from 'globals';
3
+ import reactHooks from 'eslint-plugin-react-hooks';
4
+ import reactRefresh from 'eslint-plugin-react-refresh';
5
+ import tseslint from 'typescript-eslint';
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ },
27
+ }
28
+ );
index.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Vite + React + TS</title>
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="/src/main.tsx"></script>
12
+ </body>
13
+ </html>
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "vite-react-typescript-starter",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@react-three/drei": "^9.99.0",
14
+ "@react-three/fiber": "^8.15.16",
15
+ "@react-three/postprocessing": "^2.15.12",
16
+ "lucide-react": "^0.344.0",
17
+ "react": "^18.3.1",
18
+ "react-dom": "^18.3.1",
19
+ "three": "^0.161.0"
20
+ },
21
+ "devDependencies": {
22
+ "@eslint/js": "^9.9.1",
23
+ "@types/react": "^18.3.5",
24
+ "@types/react-dom": "^18.3.0",
25
+ "@types/three": "^0.161.2",
26
+ "@vitejs/plugin-react": "^4.3.1",
27
+ "autoprefixer": "^10.4.18",
28
+ "eslint": "^9.9.1",
29
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
30
+ "eslint-plugin-react-refresh": "^0.4.11",
31
+ "globals": "^15.9.0",
32
+ "postcss": "^8.4.35",
33
+ "tailwindcss": "^3.4.1",
34
+ "typescript": "^5.5.3",
35
+ "typescript-eslint": "^8.3.0",
36
+ "vite": "^5.4.2"
37
+ }
38
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
src/App.tsx ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { Scene } from './components/Scene';
3
+
4
+ function App() {
5
+ return (
6
+ <div className="w-full h-screen bg-black">
7
+ <Scene />
8
+ </div>
9
+ );
10
+ }
11
+
12
+ export default App;
src/components/Scene.tsx ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useRef, useState, Suspense, useEffect } from 'react';
2
+ import { Canvas, useFrame } from '@react-three/fiber';
3
+ import { OrbitControls, Environment, Stars, Effects } from '@react-three/drei';
4
+ import { Box, Disc, Hexagon, Octagon, Pentagon, Star, Palette, Zap, Maximize } from 'lucide-react';
5
+ import * as THREE from 'three';
6
+ import { EffectComposer, Bloom, ChromaticAberration } from '@react-three/postprocessing';
7
+
8
+ interface ShapeProps {
9
+ geometry: 'box' | 'sphere' | 'torus' | 'icosahedron' | 'octahedron' | 'dodecahedron';
10
+ color: string;
11
+ textured: boolean;
12
+ speed: number;
13
+ scale: number;
14
+ effects: boolean;
15
+ }
16
+
17
+ function createVoronoiTexture() {
18
+ const size = 512;
19
+ const data = new Uint8Array(size * size * 4);
20
+ const points = Array(16).fill(0).map(() => ({
21
+ x: Math.random() * size,
22
+ y: Math.random() * size,
23
+ color: Math.random() * 255
24
+ }));
25
+
26
+ for (let y = 0; y < size; y++) {
27
+ for (let x = 0; x < size; x++) {
28
+ let minDist = Infinity;
29
+ let cellColor = 0;
30
+
31
+ // Find nearest point for voronoi cell
32
+ points.forEach(point => {
33
+ const dx = x - point.x;
34
+ const dy = y - point.y;
35
+ const dist = dx * dx + dy * dy;
36
+ if (dist < minDist) {
37
+ minDist = dist;
38
+ cellColor = point.color;
39
+ }
40
+ });
41
+
42
+ // Add some noise to break up the pattern
43
+ const noise = Math.random() * 30 - 15;
44
+ const finalColor = Math.max(0, Math.min(255, cellColor + noise));
45
+
46
+ const i = (y * size + x) * 4;
47
+ data[i] = finalColor; // r
48
+ data[i + 1] = finalColor; // g
49
+ data[i + 2] = finalColor; // b
50
+ data[i + 3] = 255; // a
51
+ }
52
+ }
53
+
54
+ const texture = new THREE.DataTexture(data, size, size, THREE.RGBAFormat);
55
+ texture.wrapS = THREE.RepeatWrapping;
56
+ texture.wrapT = THREE.RepeatWrapping;
57
+ texture.needsUpdate = true;
58
+ return texture;
59
+ }
60
+
61
+ function Shape({ geometry, color, textured, speed, scale, effects }: ShapeProps) {
62
+ const meshRef = useRef<THREE.Mesh>(null);
63
+ const matRef = useRef<THREE.MeshPhysicalMaterial>(null);
64
+ const [normalMap] = useState(() => createVoronoiTexture());
65
+
66
+ useFrame((state, delta) => {
67
+ if (meshRef.current) {
68
+ meshRef.current.rotation.x += delta * speed;
69
+ meshRef.current.rotation.y += delta * speed * 0.5;
70
+ }
71
+ if (matRef.current && effects) {
72
+ matRef.current.roughness = Math.sin(state.clock.elapsedTime) * 0.3 + 0.5;
73
+ }
74
+ });
75
+
76
+ const getGeometry = () => {
77
+ switch (geometry) {
78
+ case 'box':
79
+ return <boxGeometry args={[1, 1, 1]} />;
80
+ case 'sphere':
81
+ return <sphereGeometry args={[0.7, 64, 64]} />;
82
+ case 'torus':
83
+ return <torusGeometry args={[0.7, 0.3, 64, 128]} />;
84
+ case 'icosahedron':
85
+ return <icosahedronGeometry args={[0.8, 1]} />;
86
+ case 'octahedron':
87
+ return <octahedronGeometry args={[0.8, 2]} />;
88
+ case 'dodecahedron':
89
+ return <dodecahedronGeometry args={[0.8, 1]} />;
90
+ default:
91
+ return <boxGeometry args={[1, 1, 1]} />;
92
+ }
93
+ };
94
+
95
+ return (
96
+ <mesh ref={meshRef} scale={scale} castShadow receiveShadow>
97
+ {getGeometry()}
98
+ <meshPhysicalMaterial
99
+ ref={matRef}
100
+ color={color}
101
+ wireframe={!textured}
102
+ roughness={textured ? 0.5 : 0}
103
+ metalness={textured ? 0.9 : 0}
104
+ clearcoat={textured ? 0.5 : 0}
105
+ clearcoatRoughness={textured ? 0.2 : 0}
106
+ normalMap={textured ? normalMap : null}
107
+ normalScale={new THREE.Vector2(1.0, 1.0)}
108
+ envMapIntensity={2.5}
109
+ transmission={textured ? 0 : 0}
110
+ thickness={0}
111
+ />
112
+ </mesh>
113
+ );
114
+ }
115
+
116
+ function SceneContent(props: ShapeProps) {
117
+ return (
118
+ <>
119
+ <color attach="background" args={['#000000']} />
120
+ <fog attach="fog" args={['#000000', 5, 15]} />
121
+
122
+ <ambientLight intensity={0.4} />
123
+ <pointLight position={[10, 10, 10]} intensity={1} castShadow />
124
+ <spotLight
125
+ position={[-10, -10, -10]}
126
+ angle={0.3}
127
+ penumbra={1}
128
+ intensity={2}
129
+ castShadow
130
+ />
131
+
132
+ <Suspense fallback={null}>
133
+ <Shape {...props} />
134
+ <Environment preset="sunset" />
135
+ <Stars radius={100} depth={50} count={5000} factor={4} saturation={0} fade speed={1} />
136
+ </Suspense>
137
+
138
+ {props.effects && (
139
+ <EffectComposer>
140
+ <Bloom
141
+ intensity={1.5}
142
+ luminanceThreshold={0.8}
143
+ luminanceSmoothing={0.5}
144
+ height={200}
145
+ />
146
+ <ChromaticAberration offset={[0.001, 0.001]} />
147
+ </EffectComposer>
148
+ )}
149
+
150
+ <OrbitControls makeDefault />
151
+ </>
152
+ );
153
+ }
154
+
155
+ export function Scene() {
156
+ const [settings, setSettings] = useState({
157
+ geometry: 'box' as const,
158
+ color: '#ff6b6b',
159
+ textured: false,
160
+ speed: 1,
161
+ scale: 1,
162
+ effects: true,
163
+ });
164
+
165
+ const geometryOptions = [
166
+ { value: 'box', label: 'Cube', icon: Box },
167
+ { value: 'sphere', label: 'Sphere', icon: Disc },
168
+ { value: 'torus', label: 'Torus', icon: Hexagon },
169
+ { value: 'icosahedron', label: 'Icosahedron', icon: Pentagon },
170
+ { value: 'octahedron', label: 'Octahedron', icon: Octagon },
171
+ { value: 'dodecahedron', label: 'Dodecahedron', icon: Star },
172
+ ];
173
+
174
+ return (
175
+ <div className="w-full h-screen flex bg-black">
176
+ <div className="w-3/4 h-full">
177
+ <Canvas
178
+ camera={{ position: [3, 3, 3], fov: 50 }}
179
+ shadows
180
+ gl={{ antialias: true }}
181
+ dpr={[1, 2]}
182
+ className="w-full h-full"
183
+ >
184
+ <SceneContent {...settings} />
185
+ </Canvas>
186
+ </div>
187
+
188
+ <div className="w-1/4 bg-gray-900 p-8 overflow-y-auto border-l border-gray-800">
189
+ <h2 className="text-2xl font-bold mb-8 text-white flex items-center">
190
+ <Palette className="w-6 h-6 mr-2" /> Scene Settings
191
+ </h2>
192
+
193
+ <div className="space-y-8">
194
+ <div>
195
+ <label className="block text-sm font-medium text-gray-300 mb-4 flex items-center">
196
+ <Box className="w-4 h-4 mr-2" /> Geometry
197
+ </label>
198
+ <div className="grid grid-cols-2 gap-4">
199
+ {geometryOptions.map((option) => {
200
+ const Icon = option.icon;
201
+ return (
202
+ <button
203
+ key={option.value}
204
+ onClick={() => setSettings({ ...settings, geometry: option.value as ShapeProps['geometry'] })}
205
+ className={`flex flex-col items-center justify-center p-4 rounded-lg transition-all ${
206
+ settings.geometry === option.value
207
+ ? 'bg-blue-600 text-white shadow-lg shadow-blue-500/30'
208
+ : 'bg-gray-800 text-gray-400 hover:bg-gray-700 hover:text-gray-200'
209
+ }`}
210
+ >
211
+ <Icon className="w-8 h-8 mb-2" strokeWidth={1.5} />
212
+ <span className="text-sm font-medium">{option.label}</span>
213
+ </button>
214
+ );
215
+ })}
216
+ </div>
217
+ </div>
218
+
219
+ <div>
220
+ <label className="block text-sm font-medium text-gray-300 mb-3 flex items-center">
221
+ <Palette className="w-4 h-4 mr-2" /> Color
222
+ </label>
223
+ <input
224
+ type="color"
225
+ value={settings.color}
226
+ onChange={(e) => setSettings({ ...settings, color: e.target.value })}
227
+ className="w-full h-12 rounded-lg cursor-pointer bg-gray-800 border-2 border-gray-700"
228
+ />
229
+ </div>
230
+
231
+ <div className="flex items-center justify-between">
232
+ <div className="flex items-center space-x-3">
233
+ <label className="text-sm font-medium text-gray-300 flex items-center">
234
+ <Zap className="w-4 h-4 mr-2" /> Effects
235
+ </label>
236
+ <button
237
+ onClick={() => setSettings({ ...settings, effects: !settings.effects })}
238
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
239
+ settings.effects ? 'bg-blue-600' : 'bg-gray-700'
240
+ }`}
241
+ >
242
+ <span
243
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
244
+ settings.effects ? 'translate-x-6' : 'translate-x-1'
245
+ }`}
246
+ />
247
+ </button>
248
+ </div>
249
+
250
+ <div className="flex items-center space-x-3">
251
+ <label className="text-sm font-medium text-gray-300 flex items-center">
252
+ Metallic
253
+ </label>
254
+ <button
255
+ onClick={() => setSettings({ ...settings, textured: !settings.textured })}
256
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
257
+ settings.textured ? 'bg-blue-600' : 'bg-gray-700'
258
+ }`}
259
+ >
260
+ <span
261
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
262
+ settings.textured ? 'translate-x-6' : 'translate-x-1'
263
+ }`}
264
+ />
265
+ </button>
266
+ </div>
267
+ </div>
268
+
269
+ <div>
270
+ <label className="block text-sm font-medium text-gray-300 mb-3 flex items-center">
271
+ <Zap className="w-4 h-4 mr-2" /> Rotation Speed: {settings.speed.toFixed(1)}
272
+ </label>
273
+ <input
274
+ type="range"
275
+ min="0"
276
+ max="5"
277
+ step="0.1"
278
+ value={settings.speed}
279
+ onChange={(e) => setSettings({ ...settings, speed: parseFloat(e.target.value) })}
280
+ className="w-full accent-blue-600 bg-gray-700 h-2 rounded-lg appearance-none cursor-pointer"
281
+ />
282
+ </div>
283
+
284
+ <div>
285
+ <label className="block text-sm font-medium text-gray-300 mb-3 flex items-center">
286
+ <Maximize className="w-4 h-4 mr-2" /> Scale: {settings.scale.toFixed(1)}
287
+ </label>
288
+ <input
289
+ type="range"
290
+ min="0.1"
291
+ max="3"
292
+ step="0.1"
293
+ value={settings.scale}
294
+ onChange={(e) => setSettings({ ...settings, scale: parseFloat(e.target.value) })}
295
+ className="w-full accent-blue-600 bg-gray-700 h-2 rounded-lg appearance-none cursor-pointer"
296
+ />
297
+ </div>
298
+ </div>
299
+ </div>
300
+ </div>
301
+ );
302
+ }
src/index.css ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
src/main.tsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App.tsx';
4
+ import './index.css';
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
src/vite-env.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="vite/client" />
tailwind.config.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
4
+ theme: {
5
+ extend: {},
6
+ },
7
+ plugins: [],
8
+ };
tsconfig.app.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "isolatedModules": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src"]
24
+ }
tsconfig.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ]
7
+ }
tsconfig.node.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2023"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+
8
+ /* Bundler mode */
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "isolatedModules": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+
15
+ /* Linting */
16
+ "strict": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noFallthroughCasesInSwitch": true
20
+ },
21
+ "include": ["vite.config.ts"]
22
+ }
vite.config.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ optimizeDeps: {
8
+ exclude: ['lucide-react'],
9
+ },
10
+ });