artel3D Claude Sonnet 4.6 commited on
Commit
2f63f0b
·
1 Parent(s): 7f41aa2

feat: wire split panel — ImportPanel + Viewer3D assembled

Browse files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (2) hide show
  1. index.html +195 -0
  2. index.tsx +16 -4
index.html ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>IMAGE → 3D</title>
7
+ <style>
8
+ :root {
9
+ --bg-color: #121212;
10
+ --panel-bg: rgba(20, 20, 20, 0.85);
11
+ --accent: #3b82f6;
12
+ --text: #ffffff;
13
+ --danger: #ef4444;
14
+ }
15
+
16
+ body, html {
17
+ margin: 0;
18
+ padding: 0;
19
+ width: 100%;
20
+ height: 100%;
21
+ overflow: hidden;
22
+ background-color: var(--bg-color);
23
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
24
+ color: var(--text);
25
+ }
26
+
27
+ #root {
28
+ width: 100%;
29
+ height: 100%;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ }
34
+
35
+ /* Layout pour le canvas responsive selon le ratio choisi */
36
+ .canvas-container {
37
+ position: relative;
38
+ box-shadow: 0 0 50px rgba(0,0,0,0.5);
39
+ transition: width 0.3s ease, height 0.3s ease;
40
+ background: #000;
41
+ overflow: hidden;
42
+ }
43
+
44
+ /* Overlay UI */
45
+ .ui-panel {
46
+ position: absolute;
47
+ bottom: 20px;
48
+ left: 50%;
49
+ transform: translateX(-50%);
50
+ background: var(--panel-bg);
51
+ backdrop-filter: blur(10px);
52
+ padding: 16px;
53
+ border-radius: 16px;
54
+ border: 1px solid rgba(255,255,255,0.1);
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 12px;
58
+ width: 90%;
59
+ max-width: 500px;
60
+ z-index: 10;
61
+ box-shadow: 0 10px 30px rgba(0,0,0,0.5);
62
+ }
63
+
64
+ .ui-row {
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: space-between;
68
+ gap: 10px;
69
+ }
70
+
71
+ .ui-group {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: 8px;
75
+ flex: 1;
76
+ }
77
+
78
+ button {
79
+ background: rgba(255,255,255,0.1);
80
+ border: none;
81
+ color: white;
82
+ padding: 8px 12px;
83
+ border-radius: 8px;
84
+ cursor: pointer;
85
+ transition: all 0.2s;
86
+ font-size: 0.9rem;
87
+ display: flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ gap: 6px;
91
+ }
92
+
93
+ button:hover {
94
+ background: rgba(255,255,255,0.2);
95
+ }
96
+
97
+ button.active {
98
+ background: var(--accent);
99
+ color: white;
100
+ }
101
+
102
+ button.record-btn {
103
+ background: var(--danger);
104
+ font-weight: bold;
105
+ width: 100%;
106
+ }
107
+
108
+ button.record-btn.recording {
109
+ animation: pulse 2s infinite;
110
+ }
111
+
112
+ input[type="range"] {
113
+ width: 100%;
114
+ accent-color: var(--accent);
115
+ background: rgba(255,255,255,0.1);
116
+ height: 4px;
117
+ border-radius: 2px;
118
+ }
119
+
120
+ label {
121
+ font-size: 0.75rem;
122
+ color: #aaa;
123
+ text-transform: uppercase;
124
+ letter-spacing: 0.5px;
125
+ min-width: 80px;
126
+ }
127
+
128
+ .file-input {
129
+ display: none;
130
+ }
131
+
132
+ .drop-zone {
133
+ position: absolute;
134
+ top: 0;
135
+ left: 0;
136
+ width: 100%;
137
+ height: 100%;
138
+ display: flex;
139
+ flex-direction: column;
140
+ align-items: center;
141
+ justify-content: center;
142
+ background: rgba(0,0,0,0.8);
143
+ z-index: 5;
144
+ pointer-events: none;
145
+ opacity: 0;
146
+ transition: opacity 0.3s;
147
+ }
148
+
149
+ .drop-zone.visible {
150
+ opacity: 1;
151
+ pointer-events: all;
152
+ }
153
+
154
+ @keyframes pulse {
155
+ 0% { opacity: 1; }
156
+ 50% { opacity: 0.6; }
157
+ 100% { opacity: 1; }
158
+ }
159
+
160
+ /* Loader Spinner */
161
+ .loader {
162
+ border: 3px solid rgba(255,255,255,0.1);
163
+ border-top: 3px solid var(--accent);
164
+ border-radius: 50%;
165
+ width: 24px;
166
+ height: 24px;
167
+ animation: spin 1s linear infinite;
168
+ }
169
+
170
+ @keyframes spin {
171
+ 0% { transform: rotate(0deg); }
172
+ 100% { transform: rotate(360deg); }
173
+ }
174
+ </style>
175
+ <script type="importmap">
176
+ {
177
+ "imports": {
178
+ "react": "https://esm.sh/react@^19.2.3",
179
+ "react/": "https://esm.sh/react@^19.2.3/",
180
+ "three": "https://esm.sh/three@^0.182.0",
181
+ "three/": "https://esm.sh/three@^0.182.0/",
182
+ "lucide-react": "https://esm.sh/lucide-react@^0.561.0",
183
+ "react-dom/": "https://esm.sh/react-dom@^19.2.3/",
184
+ "@react-three/fiber": "https://esm.sh/@react-three/fiber@^9.4.2",
185
+ "@react-three/drei": "https://esm.sh/@react-three/drei@^10.7.7"
186
+ }
187
+ }
188
+ </script>
189
+ <link rel="stylesheet" href="/index.css">
190
+ </head>
191
+ <body>
192
+ <div id="root"></div>
193
+ <script type="module" src="/index.tsx"></script>
194
+ </body>
195
+ </html>
index.tsx CHANGED
@@ -1,7 +1,19 @@
1
- import React from 'react';
2
  import { createRoot } from 'react-dom/client';
3
  import Viewer3D from './Viewer3D';
 
4
 
5
- createRoot(document.getElementById('root')!).render(
6
- <Viewer3D modelUrl={null} />
7
- );
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
  import { createRoot } from 'react-dom/client';
3
  import Viewer3D from './Viewer3D';
4
+ import ImportPanel from './ImportPanel';
5
 
6
+ function App() {
7
+ const [glbUrl, setGlbUrl] = useState<string | null>(null);
8
+
9
+ return (
10
+ <div style={{ display: 'flex', width: '100vw', height: '100vh', overflow: 'hidden' }}>
11
+ <ImportPanel onGlbReady={setGlbUrl} />
12
+ <div style={{ flex: 1, position: 'relative' }}>
13
+ <Viewer3D modelUrl={glbUrl} />
14
+ </div>
15
+ </div>
16
+ );
17
+ }
18
+
19
+ createRoot(document.getElementById('root')!).render(<App />);