Wavetype commited on
Commit
0457181
·
verified ·
1 Parent(s): be05c4b

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +179 -134
index.html CHANGED
@@ -3,7 +3,8 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Live Modular Builder</title>
 
7
  <style>
8
  :root {
9
  --bg: #0a0a0a;
@@ -17,7 +18,7 @@
17
  body {
18
  background: var(--bg);
19
  color: var(--text);
20
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
21
  margin: 0;
22
  padding: 20px;
23
  display: flex;
@@ -27,10 +28,10 @@
27
 
28
  #canvas {
29
  width: 100%;
30
- max-width: 800px;
31
  display: flex;
32
  flex-direction: column;
33
- gap: 20px;
34
  }
35
 
36
  .module-box {
@@ -38,7 +39,6 @@
38
  border: 2px solid var(--accent);
39
  border-radius: 12px;
40
  padding: 20px;
41
- position: relative;
42
  box-shadow: 0 8px 32px rgba(0,0,0,0.8);
43
  }
44
 
@@ -51,95 +51,74 @@
51
  color: var(--accent);
52
  }
53
 
54
- .exit-btn {
55
- color: #ff4444;
56
- cursor: pointer;
57
- text-decoration: none;
 
 
58
  }
59
 
60
  textarea {
61
  width: 100%;
62
- height: 150px;
63
  background: #000;
64
  color: #00ff41;
65
  border: 1px solid var(--border);
66
  border-radius: 6px;
67
- font-family: 'Courier New', monospace;
68
  padding: 12px;
69
  box-sizing: border-box;
70
- resize: vertical;
71
- font-size: 0.9rem;
72
  }
73
 
74
- .preview-container {
75
- margin-top: 15px;
76
- background: white;
77
- color: black;
78
  border-radius: 6px;
79
- min-height: 50px;
80
- overflow: hidden;
81
- display: block;
 
82
  }
83
 
84
- .preview-label {
85
- background: #222;
86
- color: #aaa;
87
- font-size: 0.7rem;
88
- padding: 4px 10px;
89
- display: block;
90
  }
91
 
92
- iframe {
93
  width: 100%;
94
- border: none;
95
- background: white;
96
- min-height: 50px;
97
- }
98
-
99
- .controls {
100
- margin-top: 15px;
101
- display: flex;
102
- gap: 12px;
103
- flex-wrap: wrap;
104
  }
105
 
106
- button {
107
- background: transparent;
108
- color: white;
109
- border: 1px solid var(--accent);
110
- padding: 10px 20px;
111
- border-radius: 6px;
112
  cursor: pointer;
113
- transition: all 0.2s ease;
114
- font-size: 0.85rem;
115
- font-weight: 600;
116
- text-transform: uppercase;
117
- }
118
-
119
- button:hover {
120
- background: var(--accent);
121
  }
122
 
123
- .save-btn {
124
- border-color: var(--success);
125
- color: var(--success);
 
 
 
 
 
 
126
  }
127
 
128
- #library {
129
  width: 100%;
130
- max-width: 800px;
131
- margin: 40px 0;
132
- padding: 20px;
133
- background: #111;
134
- border: 1px dashed #444;
135
- border-radius: 12px;
136
- }
137
-
138
- .library-grid {
139
- display: flex;
140
- gap: 10px;
141
- flex-wrap: wrap;
142
- margin-top: 10px;
143
  }
144
 
145
  .lib-item {
@@ -149,13 +128,19 @@
149
  font-size: 0.8rem;
150
  cursor: pointer;
151
  border-radius: 4px;
 
 
152
  }
153
 
154
  .add-module-trigger {
155
- margin-top: 30px;
156
- padding: 18px 50px;
157
- font-size: 1rem;
158
- border-width: 2px;
 
 
 
 
159
  }
160
  </style>
161
  </head>
@@ -163,118 +148,178 @@
163
 
164
  <div id="canvas"></div>
165
 
166
- <button class="add-module-trigger" onclick="createModule()">+ ADD NEW MODULE</button>
167
 
168
- <div id="library">
169
- <div style="font-size: 0.9rem; color: #888;">SAVED HTML SNIPPETS</div>
170
- <div id="library-grid" class="library-grid"></div>
171
  </div>
172
 
173
  <script>
174
- let library = JSON.parse(localStorage.getItem('html_library')) || [];
175
- let activeSessions = JSON.parse(localStorage.getItem('active_sessions')) || [];
176
 
177
  function init() {
178
  renderLibrary();
179
- // Reload previous session if it exists, otherwise create empty module
180
  if (activeSessions.length > 0) {
181
- activeSessions.forEach(content => createModule(content));
182
  } else {
183
  createModule();
184
  }
185
  }
186
 
187
- function createModule(initialContent = '') {
188
  const canvas = document.getElementById('canvas');
189
- const moduleId = 'mod_' + Math.random().toString(36).substr(2, 9);
190
 
191
  const box = document.createElement('div');
192
  box.className = 'module-box';
193
- box.id = moduleId;
194
 
195
  box.innerHTML = `
196
  <div class="module-header">
197
- <span>LIVE HTML NODE</span>
198
- <span class="exit-btn" onclick="removeModule('${moduleId}')">[EXIT]</span>
199
  </div>
200
- <textarea
201
- placeholder="Type code here (Auto-renders)..."
202
- id="text_${moduleId}"
203
- spellcheck="false"
204
- oninput="handleInput('${moduleId}')"
205
- >${initialContent}</textarea>
206
-
207
- <div class="controls">
208
- <button class="save-btn" onclick="saveToLibrary('${moduleId}')">SAVE TO LIBRARY</button>
 
 
 
 
 
 
 
 
 
 
 
 
209
  </div>
210
 
211
- <div class="preview-container">
212
- <span class="preview-label">LIVE OUTPUT</span>
213
- <iframe id="iframe_${moduleId}" sandbox="allow-scripts"></iframe>
214
  </div>
215
  `;
216
 
217
  canvas.appendChild(box);
218
- if(initialContent) renderPreview(moduleId);
219
- updateSessionStore();
220
  }
221
 
222
- function handleInput(moduleId) {
223
- renderPreview(moduleId);
224
- updateSessionStore();
225
- }
226
 
227
- function renderPreview(moduleId) {
228
- const code = document.getElementById(`text_${moduleId}`).value;
229
- const iframe = document.getElementById(`iframe_${moduleId}`);
230
- if (!iframe) return;
231
-
232
- const blob = new Blob([code], { type: 'text/html' });
233
- const url = URL.createObjectURL(blob);
234
- iframe.src = url;
235
-
236
- iframe.onload = () => {
237
- setTimeout(() => {
238
- if(iframe.contentWindow.document.body) {
239
- iframe.style.height = (iframe.contentWindow.document.body.scrollHeight + 20) + 'px';
240
- }
241
- }, 50);
242
  };
243
- }
244
 
245
- function saveToLibrary(moduleId) {
246
- const content = document.getElementById(`text_${moduleId}`).value;
247
- if (!content.trim()) return;
248
- const name = prompt("Name this snippet:", "Snippet " + (library.length + 1));
249
- if (name) {
250
- library.push({ name, content });
251
- localStorage.setItem('html_library', JSON.stringify(library));
252
- renderLibrary();
253
  }
254
  }
255
 
256
- function updateSessionStore() {
257
- const texts = Array.from(document.querySelectorAll('textarea')).map(t => t.value);
258
- localStorage.setItem('active_sessions', JSON.stringify(texts));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  }
260
 
261
- function removeModule(moduleId) {
262
- document.getElementById(moduleId).remove();
 
 
 
 
 
 
263
  updateSessionStore();
264
  }
265
 
 
 
 
 
 
 
 
 
 
 
 
266
  function renderLibrary() {
267
  const grid = document.getElementById('library-grid');
268
  grid.innerHTML = '';
269
  library.forEach(item => {
270
  const btn = document.createElement('button');
271
  btn.className = 'lib-item';
272
- btn.innerText = item.name;
273
- btn.onclick = () => createModule(item.content);
274
  grid.appendChild(btn);
275
  });
276
  }
277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  init();
279
  </script>
280
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Asset Live Builder</title>
7
+ <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
8
  <style>
9
  :root {
10
  --bg: #0a0a0a;
 
18
  body {
19
  background: var(--bg);
20
  color: var(--text);
21
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
22
  margin: 0;
23
  padding: 20px;
24
  display: flex;
 
28
 
29
  #canvas {
30
  width: 100%;
31
+ max-width: 900px;
32
  display: flex;
33
  flex-direction: column;
34
+ gap: 25px;
35
  }
36
 
37
  .module-box {
 
39
  border: 2px solid var(--accent);
40
  border-radius: 12px;
41
  padding: 20px;
 
42
  box-shadow: 0 8px 32px rgba(0,0,0,0.8);
43
  }
44
 
 
51
  color: var(--accent);
52
  }
53
 
54
+ .exit-btn { color: #ff4444; cursor: pointer; }
55
+
56
+ .input-area {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: 10px;
60
  }
61
 
62
  textarea {
63
  width: 100%;
64
+ height: 120px;
65
  background: #000;
66
  color: #00ff41;
67
  border: 1px solid var(--border);
68
  border-radius: 6px;
69
+ font-family: monospace;
70
  padding: 12px;
71
  box-sizing: border-box;
 
 
72
  }
73
 
74
+ .dimension-controls {
75
+ background: #1a1a1a;
76
+ padding: 10px;
 
77
  border-radius: 6px;
78
+ margin: 10px 0;
79
+ display: grid;
80
+ grid-template-columns: 1fr 1fr;
81
+ gap: 15px;
82
  }
83
 
84
+ .slider-group {
85
+ display: flex;
86
+ flex-direction: column;
87
+ font-size: 0.75rem;
88
+ color: #888;
 
89
  }
90
 
91
+ input[type="range"] {
92
  width: 100%;
93
+ accent-color: var(--accent);
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
+ .file-upload-btn {
97
+ background: #222;
98
+ border: 1px dashed var(--accent);
99
+ padding: 10px;
100
+ text-align: center;
 
101
  cursor: pointer;
102
+ font-size: 0.8rem;
103
+ margin-bottom: 10px;
 
 
 
 
 
 
104
  }
105
 
106
+ .preview-container {
107
+ margin-top: 15px;
108
+ background: #fff; /* White background for visibility */
109
+ border-radius: 6px;
110
+ overflow: hidden;
111
+ position: relative;
112
+ display: flex;
113
+ justify-content: center;
114
+ align-items: center;
115
  }
116
 
117
+ iframe, model-viewer, img {
118
  width: 100%;
119
+ height: 300px; /* Default */
120
+ border: none;
121
+ display: block;
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
  .lib-item {
 
128
  font-size: 0.8rem;
129
  cursor: pointer;
130
  border-radius: 4px;
131
+ color: white;
132
+ margin: 5px;
133
  }
134
 
135
  .add-module-trigger {
136
+ margin: 30px;
137
+ padding: 15px 40px;
138
+ background: var(--accent);
139
+ border: none;
140
+ color: white;
141
+ font-weight: bold;
142
+ border-radius: 8px;
143
+ cursor: pointer;
144
  }
145
  </style>
146
  </head>
 
148
 
149
  <div id="canvas"></div>
150
 
151
+ <button class="add-module-trigger" onclick="createModule()">+ NEW MULTI-ASSET MODULE</button>
152
 
153
+ <div id="library" style="width: 100%; max-width: 900px; border-top: 1px solid #333; padding-top: 20px;">
154
+ <div style="color: #888; font-size: 0.8rem; margin-bottom: 10px;">SAVED ASSETS</div>
155
+ <div id="library-grid"></div>
156
  </div>
157
 
158
  <script>
159
+ let library = JSON.parse(localStorage.getItem('asset_library')) || [];
160
+ let activeSessions = JSON.parse(localStorage.getItem('asset_sessions')) || [];
161
 
162
  function init() {
163
  renderLibrary();
 
164
  if (activeSessions.length > 0) {
165
+ activeSessions.forEach(data => createModule(data.content, data.width, data.height, data.type));
166
  } else {
167
  createModule();
168
  }
169
  }
170
 
171
+ function createModule(content = '', width = 100, height = 300, type = 'html') {
172
  const canvas = document.getElementById('canvas');
173
+ const id = 'mod_' + Math.random().toString(36).substr(2, 9);
174
 
175
  const box = document.createElement('div');
176
  box.className = 'module-box';
177
+ box.id = id;
178
 
179
  box.innerHTML = `
180
  <div class="module-header">
181
+ <span>ASSET NODE (${type.toUpperCase()})</span>
182
+ <span class="exit-btn" onclick="removeModule('${id}')">[EXIT]</span>
183
  </div>
184
+
185
+ <div class="input-area">
186
+ <label class="file-upload-btn">
187
+ 📁 UPLOAD GLB / IMAGE / HTML
188
+ <input type="file" hidden onchange="handleFile(this, '${id}')">
189
+ </label>
190
+
191
+ <textarea id="text_${id}" placeholder="Or paste HTML/CSS/JS here..." oninput="updateLive('${id}')">${content}</textarea>
192
+
193
+ <div class="dimension-controls">
194
+ <div class="slider-group">
195
+ <label>WIDTH: <span id="w_val_${id}">${width}</span>%</label>
196
+ <input type="range" min="10" max="100" value="${width}" oninput="updateDims('${id}', 'width', this.value)">
197
+ </div>
198
+ <div class="slider-group">
199
+ <label>HEIGHT: <span id="h_val_${id}">${height}</span>px</label>
200
+ <input type="range" min="50" max="800" value="${height}" oninput="updateDims('${id}', 'height', this.value)">
201
+ </div>
202
+ </div>
203
+
204
+ <button class="lib-item" style="border-color: var(--success)" onclick="saveToLibrary('${id}')">SAVE TO LIBRARY</button>
205
  </div>
206
 
207
+ <div class="preview-container" id="cont_${id}">
208
+ <div id="display_${id}" style="width:${width}%; height:${height}px"></div>
 
209
  </div>
210
  `;
211
 
212
  canvas.appendChild(box);
213
+ box.dataset.type = type;
214
+ updateLive(id);
215
  }
216
 
217
+ function handleFile(input, id) {
218
+ const file = input.files[0];
219
+ if (!file) return;
 
220
 
221
+ const reader = new FileReader();
222
+ const ext = file.name.split('.').pop().toLowerCase();
223
+
224
+ reader.onload = (e) => {
225
+ const box = document.getElementById(id);
226
+ const content = e.target.result;
227
+
228
+ if (['glb', 'gltf'].includes(ext)) box.dataset.type = '3d';
229
+ else if (['png', 'jpg', 'jpeg', 'webp', 'gif'].includes(ext)) box.dataset.type = 'img';
230
+ else box.dataset.type = 'html';
231
+
232
+ document.getElementById(`text_${id}`).value = content;
233
+ updateLive(id);
 
 
234
  };
 
235
 
236
+ if (['glb', 'gltf', 'png', 'jpg', 'jpeg', 'webp', 'gif'].includes(ext)) {
237
+ reader.readAsDataURL(file);
238
+ } else {
239
+ reader.readAsText(file);
 
 
 
 
240
  }
241
  }
242
 
243
+ function updateLive(id) {
244
+ const box = document.getElementById(id);
245
+ const content = document.getElementById(`text_${id}`).value;
246
+ const display = document.getElementById(`display_${id}`);
247
+ const type = box.dataset.type;
248
+
249
+ display.innerHTML = ''; // Clear previous
250
+
251
+ if (type === '3d') {
252
+ const mv = document.createElement('model-viewer');
253
+ mv.src = content;
254
+ mv.setAttribute('auto-rotate', '');
255
+ mv.setAttribute('camera-controls', '');
256
+ mv.style.height = '100%';
257
+ display.appendChild(mv);
258
+ } else if (type === 'img') {
259
+ const img = document.createElement('img');
260
+ img.src = content;
261
+ img.style.objectFit = 'contain';
262
+ img.style.height = '100%';
263
+ display.appendChild(img);
264
+ } else {
265
+ const iframe = document.createElement('iframe');
266
+ const blob = new Blob([content], { type: 'text/html' });
267
+ iframe.src = URL.createObjectURL(blob);
268
+ iframe.style.height = '100%';
269
+ display.appendChild(iframe);
270
+ }
271
+ updateSessionStore();
272
  }
273
 
274
+ function updateDims(id, dim, val) {
275
+ const display = document.getElementById(`display_${id}`);
276
+ const label = document.getElementById(`${dim === 'width' ? 'w' : 'h'}_val_${id}`);
277
+ label.innerText = val;
278
+
279
+ if (dim === 'width') display.style.width = val + '%';
280
+ else display.style.height = val + 'px';
281
+
282
  updateSessionStore();
283
  }
284
 
285
+ function saveToLibrary(id) {
286
+ const content = document.getElementById(`text_${id}`).value;
287
+ const type = document.getElementById(id).dataset.type;
288
+ const name = prompt("Name this asset:", "Asset " + (library.length + 1));
289
+ if (name) {
290
+ library.push({ name, content, type });
291
+ localStorage.setItem('asset_library', JSON.stringify(library));
292
+ renderLibrary();
293
+ }
294
+ }
295
+
296
  function renderLibrary() {
297
  const grid = document.getElementById('library-grid');
298
  grid.innerHTML = '';
299
  library.forEach(item => {
300
  const btn = document.createElement('button');
301
  btn.className = 'lib-item';
302
+ btn.innerText = `[${item.type.toUpperCase()}] ${item.name}`;
303
+ btn.onclick = () => createModule(item.content, 100, 300, item.type);
304
  grid.appendChild(btn);
305
  });
306
  }
307
 
308
+ function updateSessionStore() {
309
+ const sessions = Array.from(document.querySelectorAll('.module-box')).map(box => ({
310
+ content: document.getElementById(`text_${box.id}`).value,
311
+ width: document.querySelector(`#w_val_${box.id}`).innerText,
312
+ height: document.querySelector(`#h_val_${box.id}`).innerText,
313
+ type: box.dataset.type
314
+ }));
315
+ localStorage.setItem('asset_sessions', JSON.stringify(sessions));
316
+ }
317
+
318
+ function removeModule(id) {
319
+ document.getElementById(id).remove();
320
+ updateSessionStore();
321
+ }
322
+
323
  init();
324
  </script>
325
  </body>