Imagulessss / index.html
Wavetype's picture
Update index.html
0a4e615 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3x3 Asset Matrix</title>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
<style>
:root {
--bg: #050505;
--accent: #7b2ff7;
--card: #111;
--text: #fff;
}
body, html {
margin: 0;
padding: 0;
background: var(--bg);
color: var(--text);
font-family: sans-serif;
overflow-x: hidden;
}
#top-bar {
padding: 15px;
display: flex;
gap: 10px;
background: rgba(0,0,0,0.8);
position: sticky;
top: 0;
z-index: 1000;
border-bottom: 1px solid #222;
}
/* 3x3 GRID SYSTEM */
#canvas {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
padding: 10px;
width: 100vw;
box-sizing: border-box;
min-height: calc(100vh - 70px);
}
.module-wrapper {
aspect-ratio: 1 / 1;
perspective: 1000px;
position: relative;
}
.module-box {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.4, 0, 0.2, 1),
width 0.5s, height 0.5s, top 0.5s, left 0.5s;
cursor: pointer;
}
/* STATES */
.module-wrapper.expanded {
position: fixed;
top: 0; left: 0;
width: 100vw; height: 100vh;
z-index: 500;
background: var(--bg);
}
.module-box.flipped {
transform: rotateY(180deg);
}
/* FACES */
.face {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 8px;
overflow: hidden;
border: 1px solid #333;
background: #000;
}
.face-front {
display: flex;
flex-direction: column;
}
.face-back {
transform: rotateY(180deg);
background: #111;
padding: 20px;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 10px;
}
/* PREVIEW CONTENT */
.preview-area {
flex-grow: 1;
width: 100%;
height: 100%;
background: #fff;
pointer-events: none; /* Allows click to pass to box when in grid */
}
.expanded .preview-area {
pointer-events: auto; /* Allow interaction when full screen */
}
iframe, model-viewer, img {
width: 100%;
height: 100%;
border: none;
display: block;
}
/* UI ELEMENTS */
textarea {
flex-grow: 1;
background: #000;
color: #00ff41;
border: 1px solid var(--accent);
font-family: monospace;
padding: 10px;
resize: none;
}
.controls {
display: flex;
gap: 5px;
padding: 10px;
background: #1a1a1a;
}
button {
background: var(--accent);
border: none;
color: white;
padding: 8px;
border-radius: 4px;
font-size: 0.7rem;
cursor: pointer;
}
.lib-panel {
padding: 20px;
border-top: 1px solid #333;
}
.lib-grid { display: flex; flex-wrap: wrap; gap: 10px; }
@media (max-width: 768px) {
#canvas { grid-template-columns: repeat(2, 1fr); }
}
</style>
</head>
<body>
<div id="top-bar">
<button onclick="createModule()">+ NEW NODE</button>
<span style="font-size: 0.7rem; color: #666; align-self: center;">
TAP: Expand | TAP AGAIN: Flip Code | DBL-TAP: Close
</span>
</div>
<div id="canvas"></div>
<div class="lib-panel">
<div style="color:#888; margin-bottom:10px">LIBRARY</div>
<div id="library-grid" class="lib-grid"></div>
</div>
<script>
let library = JSON.parse(localStorage.getItem('asset_library_v3')) || [];
let activeSessions = JSON.parse(localStorage.getItem('asset_sessions_v3')) || [];
function init() {
renderLibrary();
if (activeSessions.length > 0) {
activeSessions.forEach(s => createModule(s.content, s.type));
} else {
for(let i=0; i<3; i++) createModule();
}
}
function createModule(content = '', type = 'html') {
const canvas = document.getElementById('canvas');
const id = 'mod_' + Math.random().toString(36).substr(2, 9);
const wrapper = document.createElement('div');
wrapper.className = 'module-wrapper';
wrapper.id = 'wrap_' + id;
wrapper.innerHTML = `
<div class="module-box" id="${id}" data-type="${type}">
<div class="face face-front">
<div class="preview-area" id="prev_${id}"></div>
</div>
<div class="face face-back">
<div style="font-size:0.7rem; color:var(--accent)">SOURCE CODE</div>
<input type="file" id="file_${id}" style="font-size:0.7rem" onchange="handleFile(this, '${id}')">
<textarea id="text_${id}" oninput="updateLive('${id}')" placeholder="Paste HTML...">${content}</textarea>
<div class="controls">
<button onclick="saveToLibrary('${id}')">SAVE TO LIB</button>
<button style="background:#ff4444" onclick="removeModule('${id}')">DELETE</button>
</div>
</div>
</div>
`;
canvas.appendChild(wrapper);
const box = document.getElementById(id);
let lastTap = 0;
// TRIP-WIRE LOGIC (TAP / FLIP / CLOSE)
box.addEventListener('click', (e) => {
const now = Date.now();
const DOUBLE_TAP_DELAY = 300;
if (now - lastTap < DOUBLE_TAP_DELAY) {
// DOUBLE TAP: Close/Minimize
wrapper.classList.remove('expanded');
box.classList.remove('flipped');
return;
}
lastTap = now;
if (!wrapper.classList.contains('expanded')) {
// FIRST TAP: Expand to full screen
wrapper.classList.add('expanded');
} else {
// SECOND TAP (while expanded): Flip to code
box.classList.toggle('flipped');
}
});
updateLive(id);
}
function updateLive(id) {
const box = document.getElementById(id);
const content = document.getElementById(`text_${id}`).value;
const prev = document.getElementById(`prev_${id}`);
const type = box.dataset.type;
prev.innerHTML = '';
if (type === '3d' && content) {
const mv = document.createElement('model-viewer');
mv.src = content; mv.setAttribute('auto-rotate', ''); mv.setAttribute('camera-controls', '');
prev.appendChild(mv);
} else if (type === 'img' && content) {
const img = document.createElement('img');
img.src = content; img.style.objectFit = 'contain';
prev.appendChild(img);
} else {
const iframe = document.createElement('iframe');
const blob = new Blob([content || ' '], { type: 'text/html' });
iframe.src = URL.createObjectURL(blob);
prev.appendChild(iframe);
}
updateSessionStore();
}
function handleFile(input, id) {
const file = input.files[0];
if (!file) return;
const reader = new FileReader();
const ext = file.name.split('.').pop().toLowerCase();
reader.onload = (e) => {
const box = document.getElementById(id);
box.dataset.type = ['glb','gltf'].includes(ext) ? '3d' : (['png','jpg','jpeg','webp'].includes(ext) ? 'img' : 'html');
document.getElementById(`text_${id}`).value = e.target.result;
updateLive(id);
};
if (['glb','gltf','png','jpg','jpeg','webp'].includes(ext)) reader.readAsDataURL(file);
else reader.readAsText(file);
}
function updateSessionStore() {
const sessions = Array.from(document.querySelectorAll('.module-box')).map(box => ({
content: document.getElementById(`text_${box.id}`).value,
type: box.dataset.type
}));
localStorage.setItem('asset_sessions_v3', JSON.stringify(sessions));
}
function saveToLibrary(id) {
const content = document.getElementById(`text_${id}`).value;
const type = document.getElementById(id).dataset.type;
const name = prompt("Name this asset:");
if (name) {
library.push({ name, content, type });
localStorage.setItem('asset_library_v3', JSON.stringify(library));
renderLibrary();
}
}
function renderLibrary() {
const grid = document.getElementById('library-grid');
grid.innerHTML = '';
library.forEach(item => {
const btn = document.createElement('button');
btn.style.background = '#222';
btn.innerText = item.name;
btn.onclick = () => createModule(item.content, item.type);
grid.appendChild(btn);
});
}
function removeModule(id) {
document.getElementById('wrap_' + id).remove();
updateSessionStore();
}
init();
</script>
</body>
</html>