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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +124 -108
index.html CHANGED
@@ -3,7 +3,7 @@
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 {
@@ -26,12 +26,35 @@
26
  align-items: center;
27
  }
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 {
@@ -40,6 +63,8 @@
40
  border-radius: 12px;
41
  padding: 20px;
42
  box-shadow: 0 8px 32px rgba(0,0,0,0.8);
 
 
43
  }
44
 
45
  .module-header {
@@ -53,15 +78,9 @@
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);
@@ -69,6 +88,7 @@
69
  font-family: monospace;
70
  padding: 12px;
71
  box-sizing: border-box;
 
72
  }
73
 
74
  .dimension-controls {
@@ -84,43 +104,64 @@
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 {
125
  background: #1a1a1a;
126
  border: 1px solid #333;
@@ -131,36 +172,33 @@
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>
147
  <body>
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 {
@@ -168,76 +206,69 @@
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) {
@@ -245,47 +276,45 @@
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));
@@ -305,20 +334,7 @@
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>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Asset Grid 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 {
 
26
  align-items: center;
27
  }
28
 
29
+ /* Layout Controls */
30
+ #top-bar {
31
  width: 100%;
32
  max-width: 900px;
33
  display: flex;
34
+ justify-content: space-between;
35
+ align-items: center;
36
+ margin-bottom: 20px;
37
+ position: sticky;
38
+ top: 0;
39
+ z-index: 100;
40
+ background: var(--bg);
41
+ padding: 10px 0;
42
+ }
43
+
44
+ #canvas {
45
+ width: 100%;
46
+ max-width: 1200px;
47
+ display: flex;
48
+ flex-direction: column; /* Default Portrait */
49
  gap: 25px;
50
+ transition: all 0.3s ease;
51
+ }
52
+
53
+ /* Grid Mode Class */
54
+ #canvas.grid-mode {
55
+ display: grid;
56
+ grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
57
+ flex-direction: row;
58
  }
59
 
60
  .module-box {
 
63
  border-radius: 12px;
64
  padding: 20px;
65
  box-shadow: 0 8px 32px rgba(0,0,0,0.8);
66
+ display: flex;
67
+ flex-direction: column;
68
  }
69
 
70
  .module-header {
 
78
 
79
  .exit-btn { color: #ff4444; cursor: pointer; }
80
 
 
 
 
 
 
 
81
  textarea {
82
  width: 100%;
83
+ height: 100px;
84
  background: #000;
85
  color: #00ff41;
86
  border: 1px solid var(--border);
 
88
  font-family: monospace;
89
  padding: 12px;
90
  box-sizing: border-box;
91
+ font-size: 0.8rem;
92
  }
93
 
94
  .dimension-controls {
 
104
  .slider-group {
105
  display: flex;
106
  flex-direction: column;
107
+ font-size: 0.7rem;
108
  color: #888;
109
  }
110
 
111
+ input[type="range"] { width: 100%; accent-color: var(--accent); }
 
 
 
112
 
113
  .file-upload-btn {
114
  background: #222;
115
  border: 1px dashed var(--accent);
116
+ padding: 8px;
117
  text-align: center;
118
  cursor: pointer;
119
+ font-size: 0.75rem;
120
  margin-bottom: 10px;
121
+ border-radius: 4px;
122
  }
123
 
124
  .preview-container {
125
  margin-top: 15px;
126
+ background: #fff;
127
  border-radius: 6px;
128
  overflow: hidden;
129
  position: relative;
130
  display: flex;
131
  justify-content: center;
132
  align-items: center;
133
+ flex-grow: 1;
134
  }
135
 
136
  iframe, model-viewer, img {
137
  width: 100%;
138
+ height: 100%;
139
  border: none;
140
  display: block;
141
  }
142
 
143
+ .btn-ui {
144
+ background: #1a1a1a;
145
+ color: white;
146
+ border: 1px solid var(--accent);
147
+ padding: 10px 15px;
148
+ border-radius: 6px;
149
+ cursor: pointer;
150
+ font-size: 0.8rem;
151
+ font-weight: bold;
152
+ }
153
+
154
+ .btn-ui:hover { background: var(--accent); }
155
+ .btn-ui.active { background: var(--accent); box-shadow: 0 0 10px var(--accent); }
156
+
157
+ #library {
158
+ width: 100%;
159
+ max-width: 1200px;
160
+ margin: 40px 0;
161
+ border-top: 1px solid #333;
162
+ padding-top: 20px;
163
+ }
164
+
165
  .lib-item {
166
  background: #1a1a1a;
167
  border: 1px solid #333;
 
172
  color: white;
173
  margin: 5px;
174
  }
 
 
 
 
 
 
 
 
 
 
 
175
  </style>
176
  </head>
177
  <body>
178
 
179
+ <div id="top-bar">
180
+ <div style="display:flex; gap:10px;">
181
+ <button class="btn-ui active" id="btn-portrait" onclick="setLayout('portrait')">PORTRAIT</button>
182
+ <button class="btn-ui" id="btn-grid" onclick="setLayout('grid')">GRID VIEW</button>
183
+ </div>
184
+ <button class="btn-ui" style="border-color:var(--success)" onclick="createModule()">+ ADD MODULE</button>
185
+ </div>
186
 
187
+ <div id="canvas"></div>
188
 
189
+ <div id="library">
190
  <div style="color: #888; font-size: 0.8rem; margin-bottom: 10px;">SAVED ASSETS</div>
191
+ <div id="library-grid" style="display:flex; flex-wrap:wrap;"></div>
192
  </div>
193
 
194
  <script>
195
  let library = JSON.parse(localStorage.getItem('asset_library')) || [];
196
  let activeSessions = JSON.parse(localStorage.getItem('asset_sessions')) || [];
197
+ let currentLayout = localStorage.getItem('layout_pref') || 'portrait';
198
 
199
  function init() {
200
  renderLibrary();
201
+ setLayout(currentLayout);
202
  if (activeSessions.length > 0) {
203
  activeSessions.forEach(data => createModule(data.content, data.width, data.height, data.type));
204
  } else {
 
206
  }
207
  }
208
 
209
+ function setLayout(mode) {
210
+ currentLayout = mode;
211
+ localStorage.setItem('layout_pref', mode);
212
+ const canvas = document.getElementById('canvas');
213
+
214
+ document.getElementById('btn-portrait').classList.toggle('active', mode === 'portrait');
215
+ document.getElementById('btn-grid').classList.toggle('active', mode === 'grid');
216
+
217
+ if (mode === 'grid') {
218
+ canvas.classList.add('grid-mode');
219
+ } else {
220
+ canvas.classList.remove('grid-mode');
221
+ }
222
+ }
223
+
224
  function createModule(content = '', width = 100, height = 300, type = 'html') {
225
  const canvas = document.getElementById('canvas');
226
  const id = 'mod_' + Math.random().toString(36).substr(2, 9);
 
227
  const box = document.createElement('div');
228
  box.className = 'module-box';
229
  box.id = id;
230
+ box.dataset.type = type;
231
+
232
  box.innerHTML = `
233
  <div class="module-header">
234
+ <span>${type.toUpperCase()} NODE</span>
235
  <span class="exit-btn" onclick="removeModule('${id}')">[EXIT]</span>
236
  </div>
237
+ <label class="file-upload-btn">📁 UPLOAD ASSET<input type="file" hidden onchange="handleFile(this, '${id}')"></label>
238
+ <textarea id="text_${id}" oninput="updateLive('${id}')">${content}</textarea>
239
+ <div class="dimension-controls">
240
+ <div class="slider-group">
241
+ <label>W: <span id="w_val_${id}">${width}</span>%</label>
242
+ <input type="range" min="10" max="100" value="${width}" oninput="updateDims('${id}', 'width', this.value)">
243
+ </div>
244
+ <div class="slider-group">
245
+ <label>H: <span id="h_val_${id}">${height}</span>px</label>
246
+ <input type="range" min="50" max="800" value="${height}" oninput="updateDims('${id}', 'height', this.value)">
 
 
 
 
 
 
 
 
247
  </div>
 
 
248
  </div>
249
+ <button class="lib-item" style="margin:0; width:100%; border-color:var(--success)" onclick="saveToLibrary('${id}')">SAVE ASSET</button>
250
+ <div class="preview-container">
251
  <div id="display_${id}" style="width:${width}%; height:${height}px"></div>
252
  </div>
253
  `;
254
 
255
  canvas.appendChild(box);
 
256
  updateLive(id);
257
  }
258
 
259
  function handleFile(input, id) {
260
  const file = input.files[0];
261
  if (!file) return;
 
262
  const reader = new FileReader();
263
  const ext = file.name.split('.').pop().toLowerCase();
 
264
  reader.onload = (e) => {
265
  const box = document.getElementById(id);
266
+ box.dataset.type = ['glb','gltf'].includes(ext) ? '3d' : (['png','jpg','jpeg','webp'].includes(ext) ? 'img' : 'html');
267
+ document.getElementById(`text_${id}`).value = e.target.result;
 
 
 
 
 
268
  updateLive(id);
269
  };
270
+ if (['glb','gltf','png','jpg','jpeg','webp'].includes(ext)) reader.readAsDataURL(file);
271
+ else reader.readAsText(file);
 
 
 
 
272
  }
273
 
274
  function updateLive(id) {
 
276
  const content = document.getElementById(`text_${id}`).value;
277
  const display = document.getElementById(`display_${id}`);
278
  const type = box.dataset.type;
279
+ display.innerHTML = '';
 
 
280
  if (type === '3d') {
281
  const mv = document.createElement('model-viewer');
282
+ mv.src = content; mv.setAttribute('auto-rotate', ''); mv.setAttribute('camera-controls', '');
283
+ mv.style.height = '100%'; display.appendChild(mv);
 
 
 
284
  } else if (type === 'img') {
285
  const img = document.createElement('img');
286
+ img.src = content; img.style.objectFit = 'contain'; img.style.height = '100%'; display.appendChild(img);
 
 
 
287
  } else {
288
  const iframe = document.createElement('iframe');
289
  const blob = new Blob([content], { type: 'text/html' });
290
  iframe.src = URL.createObjectURL(blob);
291
+ iframe.style.height = '100%'; display.appendChild(iframe);
 
292
  }
293
  updateSessionStore();
294
  }
295
 
296
  function updateDims(id, dim, val) {
297
  const display = document.getElementById(`display_${id}`);
298
+ document.getElementById(`${dim === 'width' ? 'w' : 'h'}_val_${id}`).innerText = val;
 
 
299
  if (dim === 'width') display.style.width = val + '%';
300
  else display.style.height = val + 'px';
 
301
  updateSessionStore();
302
  }
303
 
304
+ function updateSessionStore() {
305
+ const sessions = Array.from(document.querySelectorAll('.module-box')).map(box => ({
306
+ content: document.getElementById(`text_${box.id}`).value,
307
+ width: document.querySelector(`#w_val_${box.id}`).innerText,
308
+ height: document.querySelector(`#h_val_${box.id}`).innerText,
309
+ type: box.dataset.type
310
+ }));
311
+ localStorage.setItem('asset_sessions', JSON.stringify(sessions));
312
+ }
313
+
314
  function saveToLibrary(id) {
315
  const content = document.getElementById(`text_${id}`).value;
316
  const type = document.getElementById(id).dataset.type;
317
+ const name = prompt("Name this asset:");
318
  if (name) {
319
  library.push({ name, content, type });
320
  localStorage.setItem('asset_library', JSON.stringify(library));
 
334
  });
335
  }
336
 
337
+ function removeModule(id) { document.getElementById(id).remove(); updateSessionStore(); }
 
 
 
 
 
 
 
 
 
 
 
 
 
338
 
339
  init();
340
  </script>