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

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +188 -204
index.html CHANGED
@@ -3,134 +3,114 @@
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 {
10
- --bg: #0a0a0a;
11
- --card: #121212;
12
  --accent: #7b2ff7;
13
- --text: #e0e0e0;
14
- --border: #333;
15
- --success: #4caf50;
16
  }
17
 
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;
25
- flex-direction: column;
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 {
61
- background: var(--card);
62
- border: 2px solid var(--accent);
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 {
71
- display: flex;
72
- justify-content: space-between;
73
- margin-bottom: 15px;
74
- font-size: 0.8rem;
75
- font-weight: bold;
76
- color: var(--accent);
77
  }
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);
87
- border-radius: 6px;
88
- font-family: monospace;
89
- padding: 12px;
90
- box-sizing: border-box;
91
- font-size: 0.8rem;
92
  }
93
 
94
- .dimension-controls {
95
- background: #1a1a1a;
96
- padding: 10px;
97
- border-radius: 6px;
98
- margin: 10px 0;
99
- display: grid;
100
- grid-template-columns: 1fr 1fr;
101
- gap: 15px;
102
  }
103
 
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 {
@@ -140,175 +120,176 @@
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;
168
- padding: 8px 16px;
169
- font-size: 0.8rem;
170
- cursor: pointer;
171
- border-radius: 4px;
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 {
205
- createModule();
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) {
275
  const box = document.getElementById(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) {
@@ -317,7 +298,7 @@
317
  const name = prompt("Name this asset:");
318
  if (name) {
319
  library.push({ name, content, type });
320
- localStorage.setItem('asset_library', JSON.stringify(library));
321
  renderLibrary();
322
  }
323
  }
@@ -327,14 +308,17 @@
327
  grid.innerHTML = '';
328
  library.forEach(item => {
329
  const btn = document.createElement('button');
330
- btn.className = 'lib-item';
331
- btn.innerText = `[${item.type.toUpperCase()}] ${item.name}`;
332
- btn.onclick = () => createModule(item.content, 100, 300, item.type);
333
  grid.appendChild(btn);
334
  });
335
  }
336
 
337
- function removeModule(id) { document.getElementById(id).remove(); updateSessionStore(); }
 
 
 
338
 
339
  init();
340
  </script>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>3x3 Asset Matrix</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: #050505;
 
11
  --accent: #7b2ff7;
12
+ --card: #111;
13
+ --text: #fff;
 
14
  }
15
 
16
+ body, html {
17
+ margin: 0;
18
+ padding: 0;
19
  background: var(--bg);
20
  color: var(--text);
21
+ font-family: sans-serif;
22
+ overflow-x: hidden;
 
 
 
 
23
  }
24
 
 
25
  #top-bar {
26
+ padding: 15px;
 
27
  display: flex;
28
+ gap: 10px;
29
+ background: rgba(0,0,0,0.8);
 
30
  position: sticky;
31
  top: 0;
32
+ z-index: 1000;
33
+ border-bottom: 1px solid #222;
 
34
  }
35
 
36
+ /* 3x3 GRID SYSTEM */
37
  #canvas {
38
+ display: grid;
39
+ grid-template-columns: repeat(3, 1fr);
40
+ gap: 10px;
41
+ padding: 10px;
42
+ width: 100vw;
43
+ box-sizing: border-box;
44
+ min-height: calc(100vh - 70px);
45
  }
46
 
47
+ .module-wrapper {
48
+ aspect-ratio: 1 / 1;
49
+ perspective: 1000px;
50
+ position: relative;
 
51
  }
52
 
53
  .module-box {
54
+ width: 100%;
55
+ height: 100%;
56
+ position: relative;
57
+ transform-style: preserve-3d;
58
+ transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),
59
+ width 0.5s, height 0.5s, top 0.5s, left 0.5s;
60
+ cursor: pointer;
61
  }
62
 
63
+ /* STATES */
64
+ .module-wrapper.expanded {
65
+ position: fixed;
66
+ top: 0; left: 0;
67
+ width: 100vw; height: 100vh;
68
+ z-index: 500;
69
+ background: var(--bg);
70
  }
71
 
72
+ .module-box.flipped {
73
+ transform: rotateY(180deg);
74
+ }
75
 
76
+ /* FACES */
77
+ .face {
78
+ position: absolute;
79
  width: 100%;
80
+ height: 100%;
81
+ backface-visibility: hidden;
82
+ border-radius: 8px;
83
+ overflow: hidden;
84
+ border: 1px solid #333;
85
  background: #000;
 
 
 
 
 
 
 
86
  }
87
 
88
+ .face-front {
89
+ display: flex;
90
+ flex-direction: column;
 
 
 
 
 
91
  }
92
 
93
+ .face-back {
94
+ transform: rotateY(180deg);
95
+ background: #111;
96
+ padding: 20px;
97
+ box-sizing: border-box;
98
  display: flex;
99
  flex-direction: column;
100
+ gap: 10px;
 
101
  }
102
 
103
+ /* PREVIEW CONTENT */
104
+ .preview-area {
105
+ flex-grow: 1;
106
+ width: 100%;
107
+ height: 100%;
108
+ background: #fff;
109
+ pointer-events: none; /* Allows click to pass to box when in grid */
 
 
 
 
110
  }
111
 
112
+ .expanded .preview-area {
113
+ pointer-events: auto; /* Allow interaction when full screen */
 
 
 
 
 
 
 
 
114
  }
115
 
116
  iframe, model-viewer, img {
 
120
  display: block;
121
  }
122
 
123
+ /* UI ELEMENTS */
124
+ textarea {
125
+ flex-grow: 1;
126
+ background: #000;
127
+ color: #00ff41;
128
+ border: 1px solid var(--accent);
129
+ font-family: monospace;
130
+ padding: 10px;
131
+ resize: none;
132
+ }
133
+
134
+ .controls {
135
+ display: flex;
136
+ gap: 5px;
137
+ padding: 10px;
138
  background: #1a1a1a;
139
+ }
140
+
141
+ button {
142
+ background: var(--accent);
143
+ border: none;
144
  color: white;
145
+ padding: 8px;
146
+ border-radius: 4px;
147
+ font-size: 0.7rem;
148
  cursor: pointer;
 
 
149
  }
150
 
151
+ .lib-panel {
152
+ padding: 20px;
 
 
 
 
 
153
  border-top: 1px solid #333;
 
154
  }
155
 
156
+ .lib-grid { display: flex; flex-wrap: wrap; gap: 10px; }
157
+
158
+ @media (max-width: 768px) {
159
+ #canvas { grid-template-columns: repeat(2, 1fr); }
 
 
 
 
 
160
  }
161
  </style>
162
  </head>
163
  <body>
164
 
165
  <div id="top-bar">
166
+ <button onclick="createModule()">+ NEW NODE</button>
167
+ <span style="font-size: 0.7rem; color: #666; align-self: center;">
168
+ TAP: Expand | TAP AGAIN: Flip Code | DBL-TAP: Close
169
+ </span>
 
170
  </div>
171
 
172
  <div id="canvas"></div>
173
 
174
+ <div class="lib-panel">
175
+ <div style="color:#888; margin-bottom:10px">LIBRARY</div>
176
+ <div id="library-grid" class="lib-grid"></div>
177
  </div>
178
 
179
  <script>
180
+ let library = JSON.parse(localStorage.getItem('asset_library_v3')) || [];
181
+ let activeSessions = JSON.parse(localStorage.getItem('asset_sessions_v3')) || [];
 
182
 
183
  function init() {
184
  renderLibrary();
 
185
  if (activeSessions.length > 0) {
186
+ activeSessions.forEach(s => createModule(s.content, s.type));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  } else {
188
+ for(let i=0; i<3; i++) createModule();
189
  }
190
  }
191
 
192
+ function createModule(content = '', type = 'html') {
193
  const canvas = document.getElementById('canvas');
194
  const id = 'mod_' + Math.random().toString(36).substr(2, 9);
195
+
196
+ const wrapper = document.createElement('div');
197
+ wrapper.className = 'module-wrapper';
198
+ wrapper.id = 'wrap_' + id;
199
+
200
+ wrapper.innerHTML = `
201
+ <div class="module-box" id="${id}" data-type="${type}">
202
+ <div class="face face-front">
203
+ <div class="preview-area" id="prev_${id}"></div>
 
 
 
 
 
 
 
204
  </div>
205
+ <div class="face face-back">
206
+ <div style="font-size:0.7rem; color:var(--accent)">SOURCE CODE</div>
207
+ <input type="file" id="file_${id}" style="font-size:0.7rem" onchange="handleFile(this, '${id}')">
208
+ <textarea id="text_${id}" oninput="updateLive('${id}')" placeholder="Paste HTML...">${content}</textarea>
209
+ <div class="controls">
210
+ <button onclick="saveToLibrary('${id}')">SAVE TO LIB</button>
211
+ <button style="background:#ff4444" onclick="removeModule('${id}')">DELETE</button>
212
+ </div>
213
  </div>
214
  </div>
 
 
 
 
215
  `;
 
 
 
 
216
 
217
+ canvas.appendChild(wrapper);
218
+
219
+ const box = document.getElementById(id);
220
+ let lastTap = 0;
221
+
222
+ // TRIP-WIRE LOGIC (TAP / FLIP / CLOSE)
223
+ box.addEventListener('click', (e) => {
224
+ const now = Date.now();
225
+ const DOUBLE_TAP_DELAY = 300;
226
+
227
+ if (now - lastTap < DOUBLE_TAP_DELAY) {
228
+ // DOUBLE TAP: Close/Minimize
229
+ wrapper.classList.remove('expanded');
230
+ box.classList.remove('flipped');
231
+ return;
232
+ }
233
+ lastTap = now;
234
+
235
+ if (!wrapper.classList.contains('expanded')) {
236
+ // FIRST TAP: Expand to full screen
237
+ wrapper.classList.add('expanded');
238
+ } else {
239
+ // SECOND TAP (while expanded): Flip to code
240
+ box.classList.toggle('flipped');
241
+ }
242
+ });
243
+
244
+ updateLive(id);
245
  }
246
 
247
  function updateLive(id) {
248
  const box = document.getElementById(id);
249
  const content = document.getElementById(`text_${id}`).value;
250
+ const prev = document.getElementById(`prev_${id}`);
251
  const type = box.dataset.type;
252
+
253
+ prev.innerHTML = '';
254
+
255
+ if (type === '3d' && content) {
256
  const mv = document.createElement('model-viewer');
257
  mv.src = content; mv.setAttribute('auto-rotate', ''); mv.setAttribute('camera-controls', '');
258
+ prev.appendChild(mv);
259
+ } else if (type === 'img' && content) {
260
  const img = document.createElement('img');
261
+ img.src = content; img.style.objectFit = 'contain';
262
+ prev.appendChild(img);
263
  } else {
264
  const iframe = document.createElement('iframe');
265
+ const blob = new Blob([content || ' '], { type: 'text/html' });
266
  iframe.src = URL.createObjectURL(blob);
267
+ prev.appendChild(iframe);
268
  }
269
  updateSessionStore();
270
  }
271
 
272
+ function handleFile(input, id) {
273
+ const file = input.files[0];
274
+ if (!file) return;
275
+ const reader = new FileReader();
276
+ const ext = file.name.split('.').pop().toLowerCase();
277
+ reader.onload = (e) => {
278
+ const box = document.getElementById(id);
279
+ box.dataset.type = ['glb','gltf'].includes(ext) ? '3d' : (['png','jpg','jpeg','webp'].includes(ext) ? 'img' : 'html');
280
+ document.getElementById(`text_${id}`).value = e.target.result;
281
+ updateLive(id);
282
+ };
283
+ if (['glb','gltf','png','jpg','jpeg','webp'].includes(ext)) reader.readAsDataURL(file);
284
+ else reader.readAsText(file);
285
  }
286
 
287
  function updateSessionStore() {
288
  const sessions = Array.from(document.querySelectorAll('.module-box')).map(box => ({
289
  content: document.getElementById(`text_${box.id}`).value,
 
 
290
  type: box.dataset.type
291
  }));
292
+ localStorage.setItem('asset_sessions_v3', JSON.stringify(sessions));
293
  }
294
 
295
  function saveToLibrary(id) {
 
298
  const name = prompt("Name this asset:");
299
  if (name) {
300
  library.push({ name, content, type });
301
+ localStorage.setItem('asset_library_v3', JSON.stringify(library));
302
  renderLibrary();
303
  }
304
  }
 
308
  grid.innerHTML = '';
309
  library.forEach(item => {
310
  const btn = document.createElement('button');
311
+ btn.style.background = '#222';
312
+ btn.innerText = item.name;
313
+ btn.onclick = () => createModule(item.content, item.type);
314
  grid.appendChild(btn);
315
  });
316
  }
317
 
318
+ function removeModule(id) {
319
+ document.getElementById('wrap_' + id).remove();
320
+ updateSessionStore();
321
+ }
322
 
323
  init();
324
  </script>