Show description
IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER
IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER
◈ IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER ◈
DB: INIT...
AI: READY
PROJECT_EXPLORER.ide
[ + NEW PROJECT ]
── MODULES ──
🎨AI_Image_Generator.php
🎬AI_Video_Generator.php
💻IDE_CodeEditor.php
🌍World_Generator.php
📚Book_Generator.php
🎭Comic_Generator.php
📝Text_Editor.php
── SAVED ──
Loading from DB...
🎨 AI IMAGE GENERATOR
[W0]
🎨 Image Gen
🎬 Video Gen
💻 IDE
🌍 World Gen
📚 Book Gen
🎭 Comic Gen
📝 Editor
🎬 VIDEO GENERATOR
[W1]
🎨 Image Gen
🎬 Video Gen
💻 IDE
🌍 World Gen
📚 Book Gen
🎭 Comic Gen
📝 Editor
💻 IDE
[W2]
🎨 Image Gen
🎬 Video Gen
💻 IDE
🌍 World Gen
📚 Book Gen
🎭 Comic Gen
📝 Editor
🌍 WORLD GENERATOR
[W3]
🎨 Image Gen
🎬 Video Gen
💻 IDE
🌍 World Gen
📚 Book Gen
🎭 Comic Gen
📝 Editor
FRONTEND ENGINEER MODE
PUSH SETTINGS
✓
PUSH BOUNDARIES: 100% UNIQUE
✓
EXPRESSIVE CODE: AMAZING VISUALIZATIONS
SAFE MODE (BORING)
✓
AUTO-SAVE TO SQLite DB
API CONFIG
ANTHROPIC API KEY
Powers: book, comic, world, IDE AI. Images via Pollinations (free, no key needed).
MODEL:
claude-sonnet-4-20250514
EXECUTION LOG / STATUS
ANALYTICS PREVIEW
Generated: 0
Saved: 0
API calls: 0
📨Messages: 0
📦Projects: 0
🖼️Images: 0
📊DB: OK
SQLITE_DB_INIT.pdo
[ Haha (Easter Egg) ]
◈ NEW PROJECT
Name your project and select a type to create a new entry in the SQLite database.
PROJECT NAME
TYPE
🎨 Image Generation
🎬 Video / Storyboard
💻 Code / IDE
🌍 World Building
📚 Book / Story
🎭 Comic / Manga
📝 Text Document
CANCEL
[ CREATE ]
🐧 PYTHON PENGUIN SAYS:
[ CLOSE ]
IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER</title>
<link href="https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Orbitron:wght@400;700;900&family=VT323&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.2/sql-wasm.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tokyo-night-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<style>
:root {
--bg: #070810;
--panel: #0d0e17;
--panel2: #10111a;
--border: #1c1e2e;
--border2: #252740;
--green: #00ff88;
--green-dim: #007744;
--cyan: #00d4ff;
--cyan-dim: #006680;
--orange: #ff7b3a;
--pink: #ff2d78;
--yellow: #ffd700;
--purple: #9d4edd;
--text: #c9d1e0;
--text-dim: #5a6070;
--font-mono: 'Share Tech Mono', monospace;
--font-title: 'Orbitron', monospace;
--font-vt: 'VT323', monospace;
}
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:100%;overflow:hidden;}
body{
background:var(--bg);
color:var(--text);
font-family:var(--font-mono);
display:flex;
flex-direction:column;
height:100vh;
}
/* ===== HEADER ===== */
.header{
background:linear-gradient(90deg,#04050a,#070a14,#04050a);
border-bottom:2px solid var(--cyan);
padding:7px 16px;
text-align:center;
font-family:var(--font-title);
font-size:12px;
font-weight:900;
color:var(--cyan);
text-shadow:0 0 20px var(--cyan),0 0 40px rgba(0,212,255,0.3);
letter-spacing:4px;
flex-shrink:0;
position:relative;
display:flex;
align-items:center;
justify-content:center;
gap:12px;
}
.header-pill{
font-size:8px;
background:rgba(0,212,255,0.1);
border:1px solid var(--cyan);
color:var(--cyan);
padding:2px 8px;
border-radius:20px;
letter-spacing:2px;
}
.header-pill.ok{border-color:var(--green);color:var(--green);background:rgba(0,255,136,0.1);}
/* ===== LAYOUT ===== */
.layout{
display:grid;
grid-template-columns:210px 1fr 230px;
flex:1;
overflow:hidden;
}
/* ===== PANELS ===== */
.panel{
display:flex;
flex-direction:column;
background:var(--panel);
overflow:hidden;
}
.panel-left{border-right:1px solid var(--border2);}
.panel-right{border-left:1px solid var(--border2);}
.ptitle{
background:#090a12;
border-bottom:1px solid var(--border2);
padding:5px 10px;
font-size:9px;
color:var(--cyan);
letter-spacing:2px;
display:flex;
justify-content:space-between;
align-items:center;
flex-shrink:0;
}
.wc{display:flex;gap:5px;}
.wc span{width:9px;height:9px;border-radius:50%;cursor:pointer;display:inline-block;}
.wc .wmin{background:#ffd700;}
.wc .wmax{background:#00ff88;}
.wc .wcls{background:#ff2d78;}
/* ===== FILE TREE ===== */
.ftree{flex:1;overflow-y:auto;padding:4px 0;}
.fitem{
display:flex;align-items:center;gap:7px;
padding:4px 10px;font-size:10px;
cursor:pointer;color:var(--text-dim);
transition:all 0.1s;border-left:2px solid transparent;
}
.fitem:hover{background:#14152a;color:var(--text);}
.fitem.active{background:#0d1f18;color:var(--green);border-left-color:var(--green);}
.fitem .fi{font-size:11px;}
.fitem .fn{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.fitem .ft{font-size:8px;color:var(--text-dim);}
.tree-section{
padding:4px 10px 2px;
font-size:8px;
color:var(--border2);
letter-spacing:2px;
margin-top:6px;
}
.add-btn{
margin:6px;
background:transparent;
border:1px solid var(--border2);
color:var(--text-dim);
font-family:var(--font-mono);
font-size:9px;
padding:4px 8px;
cursor:pointer;
letter-spacing:1px;
transition:all 0.2s;
width:calc(100% - 12px);
}
.add-btn:hover{border-color:var(--green);color:var(--green);}
/* ===== VIEWPORT ===== */
.viewport{
display:grid;
grid-template-columns:1fr 1fr;
grid-template-rows:1fr 1fr;
gap:3px;
padding:3px;
background:#060710;
overflow:hidden;
}
.mwin{
background:var(--panel);
border:1px solid var(--border2);
border-radius:3px;
display:flex;
flex-direction:column;
overflow:hidden;
position:relative;
transition:border-color 0.2s;
}
.mwin:hover{border-color:#2a2c44;}
.mwin.focused{border-color:var(--green);box-shadow:0 0 10px rgba(0,255,136,0.15);}
.mtitle{
background:#08090f;
border-bottom:1px solid var(--border);
padding:4px 8px;
font-size:9px;
color:var(--orange);
letter-spacing:1px;
display:flex;
justify-content:space-between;
align-items:center;
flex-shrink:0;
gap:6px;
}
.mtitle select{
background:#10111a;
border:1px solid var(--border2);
color:var(--cyan);
font-family:var(--font-mono);
font-size:8px;
padding:2px 4px;
cursor:pointer;
outline:none;
}
.mtitle select option{background:#0d0e17;}
.win-id{color:var(--text-dim);font-size:8px;}
.mcontent{
flex:1;overflow-y:auto;
padding:8px;
display:flex;flex-direction:column;gap:6px;
}
/* ===== FORM CONTROLS ===== */
.clabel{font-size:8px;color:var(--text-dim);letter-spacing:1px;margin-bottom:2px;}
textarea,input[type=text],input[type=password]{
background:#080910;
border:1px solid var(--border2);
color:var(--text);
font-family:var(--font-mono);
font-size:10px;
padding:5px 7px;
border-radius:2px;
width:100%;
outline:none;
resize:vertical;
}
textarea:focus,input[type=text]:focus,input[type=password]:focus{border-color:var(--cyan);}
textarea{min-height:52px;line-height:1.5;}
select.full{
background:#080910;border:1px solid var(--border2);
color:var(--text);font-family:var(--font-mono);
font-size:10px;padding:5px 7px;border-radius:2px;
width:100%;outline:none;cursor:pointer;
}
select.full:focus{border-color:var(--cyan);}
select.full option{background:#0d0e17;}
.row{display:flex;gap:6px;}
.row > *{flex:1;}
.btn{
background:transparent;
border:1px solid var(--green);
color:var(--green);
font-family:var(--font-mono);
font-size:9px;letter-spacing:2px;
padding:5px 10px;cursor:pointer;
border-radius:2px;transition:all 0.2s;
white-space:nowrap;
}
.btn:hover:not(:disabled){background:var(--green);color:#000;box-shadow:0 0 12px rgba(0,255,136,0.4);}
.btn:disabled{opacity:0.35;cursor:not-allowed;}
.btn.c{border-color:var(--cyan);color:var(--cyan);}
.btn.c:hover:not(:disabled){background:var(--cyan);color:#000;}
.btn.o{border-color:var(--orange);color:var(--orange);}
.btn.o:hover:not(:disabled){background:var(--orange);color:#000;}
.btn.p{border-color:var(--pink);color:var(--pink);}
.btn.p:hover:not(:disabled){background:var(--pink);color:#fff;}
.btn.pu{border-color:var(--purple);color:var(--purple);}
.btn.pu:hover:not(:disabled){background:var(--purple);color:#fff;}
.btn.sm{font-size:8px;padding:3px 7px;}
/* ===== OUTPUT ===== */
.imgout{
flex:1;min-height:90px;
background:#04050a;
border:1px dashed var(--border2);
border-radius:2px;
display:flex;align-items:center;justify-content:center;
overflow:hidden;position:relative;
}
.imgout img{max-width:100%;max-height:100%;object-fit:contain;}
.imgout .ph{color:var(--text-dim);font-size:9px;text-align:center;padding:8px;}
.imgout .img-info{
position:absolute;bottom:0;left:0;right:0;
background:rgba(0,0,0,0.7);
font-size:8px;color:var(--text-dim);padding:3px 6px;
}
.textout{
background:#04050a;border:1px solid var(--border);
border-radius:2px;padding:8px;overflow-y:auto;
flex:1;min-height:70px;
font-size:10px;line-height:1.6;color:var(--text);
white-space:pre-wrap;word-break:break-word;
}
.textout .hl{color:var(--green);}
.textout pre{margin:0;font-family:var(--font-mono);}
.codeout{
background:#030408;border:1px solid var(--border);
border-radius:2px;padding:0;overflow:auto;
flex:1;min-height:70px;
}
.codeout pre{margin:0;font-size:10px;line-height:1.6;padding:8px;}
.codeout code{font-family:var(--font-mono) !important;}
/* ===== LOADING ===== */
.loading{
display:flex;align-items:center;gap:7px;
color:var(--cyan);font-size:9px;letter-spacing:1px;
}
.spin{
width:12px;height:12px;
border:2px solid var(--border2);
border-top-color:var(--cyan);
border-radius:50%;
animation:spin 0.7s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg);}}
/* ===== RIGHT PANEL ===== */
.rsec{border-bottom:1px solid var(--border);padding:8px;}
.rtitle{
font-size:8px;color:var(--cyan);
letter-spacing:2px;margin-bottom:7px;
padding-bottom:4px;
border-bottom:1px solid var(--border);
}
.fe-item{
display:flex;align-items:center;gap:6px;
font-size:8px;color:var(--text-dim);margin-bottom:4px;cursor:pointer;
}
.fe-item .cb{
width:10px;height:10px;
border:1px solid var(--border2);
display:flex;align-items:center;justify-content:center;
font-size:8px;flex-shrink:0;
}
.fe-item.on .cb{border-color:var(--green);color:var(--green);}
.fe-item.on{color:var(--green);}
.log-entries{max-height:160px;overflow-y:auto;}
.log-e{
font-size:8px;color:var(--text-dim);
border-left:2px solid var(--border2);
padding:2px 6px;margin-bottom:3px;line-height:1.5;
}
.log-e .lt{color:var(--green);font-size:7px;}
.log-e .lm{color:var(--text);}
.log-e .ls{font-size:7px;}
.ls.ok{color:var(--green);}
.ls.work{color:var(--cyan);}
.ls.err{color:var(--pink);}
.ls.warn{color:var(--orange);}
/* ===== ANALYTICS ===== */
.ag{display:grid;grid-template-columns:1fr 1fr;gap:5px;}
.mc{
background:#04050a;border:1px solid var(--border);
border-radius:2px;height:36px;
display:flex;align-items:flex-end;padding:3px;gap:2px;
overflow:hidden;
}
.bar{flex:1;border-radius:1px 1px 0 0;min-width:0;}
/* ===== BOTTOM BAR ===== */
.bbar{
background:#050610;
border-top:1px solid var(--border2);
padding:3px 12px;
display:flex;justify-content:space-between;align-items:center;
font-size:8px;color:var(--text-dim);
flex-shrink:0;height:26px;
}
.notifs{display:flex;gap:10px;}
.notif{color:var(--orange);}
.notif span{margin-right:3px;}
.egg-btn{
background:transparent;border:1px solid var(--border2);
color:var(--text-dim);font-size:7px;padding:1px 6px;
cursor:pointer;font-family:var(--font-mono);letter-spacing:1px;
}
.egg-btn:hover{border-color:var(--pink);color:var(--pink);}
/* ===== SCANNER ===== */
.scanner{
position:absolute;top:0;left:0;right:0;height:1px;
background:linear-gradient(90deg,transparent,var(--cyan),transparent);
opacity:0.3;animation:scan 5s linear infinite;pointer-events:none;
}
@keyframes scan{0%{top:0;}100%{top:100%;}}
/* ===== MODAL ===== */
.overlay{
position:fixed;inset:0;background:rgba(0,0,0,0.85);
z-index:999;display:flex;align-items:center;justify-content:center;
}
.modal{
background:var(--panel2);border:1px solid var(--cyan);
border-radius:4px;padding:24px;max-width:480px;width:90%;
box-shadow:0 0 40px rgba(0,212,255,0.2);
}
.modal h2{font-family:var(--font-title);font-size:13px;color:var(--cyan);margin-bottom:12px;}
.modal p{font-size:10px;color:var(--text-dim);margin-bottom:14px;line-height:1.6;}
.modal input{margin-bottom:10px;}
.mbts{display:flex;gap:8px;justify-content:flex-end;margin-top:4px;}
/* ===== COMIC PANELS ===== */
.comic-grid{display:grid;grid-template-columns:1fr 1fr;gap:3px;flex:1;min-height:100px;}
.cpanel{
border:2px solid #ddd;background:#f5f5f0;
display:flex;flex-direction:column;overflow:hidden;
position:relative;min-height:60px;
}
.cpanel img{width:100%;aspect-ratio:4/3;object-fit:cover;flex-shrink:0;}
.cbubble{
background:white;color:#111;
font-family:'Comic Sans MS',cursive;
font-size:8px;padding:3px 5px;
border:1px solid #333;border-radius:6px;
margin:3px;line-height:1.3;
}
.cnum{
position:absolute;top:2px;left:3px;
background:black;color:white;
font-size:7px;padding:0 3px;font-weight:bold;
}
/* ===== WORLD MAP ===== */
#worldCanvas{width:100%;flex:1;border:1px solid var(--border);border-radius:2px;cursor:crosshair;}
/* ===== IDE AREA ===== */
.ide-area{
flex:1;display:flex;flex-direction:column;gap:4px;
min-height:0;
}
.ide-toolbar{
display:flex;gap:4px;align-items:center;flex-shrink:0;
}
.ide-ta{
flex:1;min-height:80px;
background:#03040a;
border:1px solid var(--border);
color:#a8e6a3;
font-family:var(--font-mono);
font-size:10px;line-height:1.6;
padding:8px;
resize:none;outline:none;
border-radius:2px;
}
.ide-ta:focus{border-color:var(--green);}
/* ===== BOOK ===== */
.book-wrap{
flex:1;background:#fef9ef;color:#2a1a0a;
border-radius:3px;padding:12px;
font-family:Georgia,serif;font-size:10px;
line-height:1.8;overflow-y:auto;
border:3px solid #8b7355;
box-shadow:inset 0 0 30px rgba(0,0,0,0.15);
}
.book-ttl{font-size:13px;font-weight:bold;text-align:center;margin-bottom:8px;color:#4a2800;}
.book-ch{font-size:11px;font-weight:bold;margin:10px 0 5px;color:#6b3a00;}
/* ===== SCROLLBAR ===== */
::-webkit-scrollbar{width:4px;height:4px;}
::-webkit-scrollbar-track{background:transparent;}
::-webkit-scrollbar-thumb{background:#1e2030;border-radius:2px;}
::-webkit-scrollbar-thumb:hover{background:var(--cyan);}
/* ===== VIDEO FRAMES ===== */
.vframes{display:flex;gap:3px;overflow-x:auto;flex:1;min-height:70px;padding:2px 0;}
.vframe{
flex-shrink:0;width:90px;
border:1px solid var(--border2);border-radius:2px;
overflow:hidden;position:relative;cursor:pointer;
}
.vframe img{width:100%;aspect-ratio:16/9;object-fit:cover;display:block;}
.vframe-n{
position:absolute;bottom:1px;right:2px;
font-size:7px;color:var(--cyan);
background:rgba(0,0,0,0.7);padding:0 2px;
}
.vframe.playing{border-color:var(--green);box-shadow:0 0 6px rgba(0,255,136,0.3);}
/* ===== SAVED ITEMS ===== */
.saved-items{flex:1;overflow-y:auto;}
.si{
padding:4px 8px;font-size:8px;
border-bottom:1px solid var(--border);
display:flex;justify-content:space-between;align-items:center;
cursor:pointer;transition:background 0.1s;
}
.si:hover{background:#12132a;}
.si-name{color:var(--text);}
.si-type{
font-size:7px;padding:1px 4px;border-radius:2px;
border:1px solid currentColor;
}
.si-type.img{color:var(--cyan);}
.si-type.code{color:var(--green);}
.si-type.book{color:var(--yellow);}
.si-type.comic{color:var(--pink);}
.si-type.world{color:var(--purple);}
.si-type.video{color:var(--orange);}
/* ===== TAG ===== */
.tag{
display:inline-block;background:#0f1f14;
border:1px solid var(--green-dim);color:var(--green);
font-size:7px;padding:1px 5px;border-radius:2px;margin:1px;
}
/* progress */
.pbar{height:3px;background:var(--border);border-radius:2px;margin-top:3px;}
.pfill{height:100%;background:linear-gradient(90deg,var(--cyan),var(--green));border-radius:2px;transition:width 0.4s;}
/* glitch effect on title */
@keyframes glitch{
0%,89%,100%{clip-path:none;transform:translate(0);}
90%{clip-path:polygon(0 20%,100% 20%,100% 40%,0 40%);transform:translate(-3px,1px);color:var(--pink);}
92%{clip-path:polygon(0 60%,100% 60%,100% 80%,0 80%);transform:translate(3px,-1px);color:var(--green);}
94%{clip-path:none;transform:translate(0);}
}
.glitch{animation:glitch 8s infinite;}
@keyframes blink{0%,100%{opacity:1;}50%{opacity:0;}}
.cursor{animation:blink 1s infinite;color:var(--green);}
/* img grid for world */
.img-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:3px;}
</style>
</head>
<body>
<!-- HEADER -->
<div class="header">
<div class="glitch">◈ IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER ◈</div>
<div class="header-pill" id="db-status">DB: INIT...</div>
<div class="header-pill ok" id="ai-status">AI: READY</div>
</div>
<!-- MAIN LAYOUT -->
<div class="layout">
<!-- LEFT: PROJECT EXPLORER -->
<div class="panel panel-left">
<div class="ptitle">
PROJECT_EXPLORER.ide
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
<button class="add-btn" onclick="showNewProject()">[ + NEW PROJECT ]</button>
<div class="tree-section">── MODULES ──</div>
<div class="ftree" id="module-tree">
<div class="fitem" data-module="image" onclick="openModule('image',0)">
<span class="fi">🎨</span><span class="fn">AI_Image_Generator.php</span>
</div>
<div class="fitem" data-module="video" onclick="openModule('video',1)">
<span class="fi">🎬</span><span class="fn">AI_Video_Generator.php</span>
</div>
<div class="fitem" data-module="ide" onclick="openModule('ide',2)">
<span class="fi">💻</span><span class="fn">IDE_CodeEditor.php</span>
</div>
<div class="fitem" data-module="world" onclick="openModule('world',3)">
<span class="fi">🌍</span><span class="fn">World_Generator.php</span>
</div>
<div class="fitem" data-module="book" onclick="openModule('book',0)">
<span class="fi">📚</span><span class="fn">Book_Generator.php</span>
</div>
<div class="fitem" data-module="comic" onclick="openModule('comic',1)">
<span class="fi">🎭</span><span class="fn">Comic_Generator.php</span>
</div>
<div class="fitem" data-module="editor" onclick="openModule('editor',2)">
<span class="fi">📝</span><span class="fn">Text_Editor.php</span>
</div>
</div>
<div class="tree-section">── SAVED ──</div>
<div class="saved-items" id="saved-list">
<div style="font-size:9px;color:var(--text-dim);padding:6px 10px;">Loading from DB...</div>
</div>
</div>
<!-- CENTER: VIEWPORT -->
<div class="viewport" id="viewport">
<!-- Window 0 -->
<div class="mwin" id="win-0">
<div class="scanner"></div>
<div class="mtitle">
<span id="wtitle-0">🎨 AI IMAGE GENERATOR</span>
<div style="display:flex;gap:5px;align-items:center;">
<span class="win-id">[W0]</span>
<select onchange="switchModule(0,this.value)" id="wsel-0">
<option value="image">🎨 Image Gen</option>
<option value="video">🎬 Video Gen</option>
<option value="ide">💻 IDE</option>
<option value="world">🌍 World Gen</option>
<option value="book">📚 Book Gen</option>
<option value="comic">🎭 Comic Gen</option>
<option value="editor">📝 Editor</option>
</select>
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
</div>
<div class="mcontent" id="wcontent-0"></div>
</div>
<!-- Window 1 -->
<div class="mwin" id="win-1">
<div class="scanner"></div>
<div class="mtitle">
<span id="wtitle-1">🎬 VIDEO GENERATOR</span>
<div style="display:flex;gap:5px;align-items:center;">
<span class="win-id">[W1]</span>
<select onchange="switchModule(1,this.value)" id="wsel-1">
<option value="image">🎨 Image Gen</option>
<option value="video" selected>🎬 Video Gen</option>
<option value="ide">💻 IDE</option>
<option value="world">🌍 World Gen</option>
<option value="book">📚 Book Gen</option>
<option value="comic">🎭 Comic Gen</option>
<option value="editor">📝 Editor</option>
</select>
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
</div>
<div class="mcontent" id="wcontent-1"></div>
</div>
<!-- Window 2 -->
<div class="mwin" id="win-2">
<div class="scanner"></div>
<div class="mtitle">
<span id="wtitle-2">💻 IDE</span>
<div style="display:flex;gap:5px;align-items:center;">
<span class="win-id">[W2]</span>
<select onchange="switchModule(2,this.value)" id="wsel-2">
<option value="image">🎨 Image Gen</option>
<option value="video">🎬 Video Gen</option>
<option value="ide" selected>💻 IDE</option>
<option value="world">🌍 World Gen</option>
<option value="book">📚 Book Gen</option>
<option value="comic">🎭 Comic Gen</option>
<option value="editor">📝 Editor</option>
</select>
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
</div>
<div class="mcontent" id="wcontent-2"></div>
</div>
<!-- Window 3 -->
<div class="mwin" id="win-3">
<div class="scanner"></div>
<div class="mtitle">
<span id="wtitle-3">🌍 WORLD GENERATOR</span>
<div style="display:flex;gap:5px;align-items:center;">
<span class="win-id">[W3]</span>
<select onchange="switchModule(3,this.value)" id="wsel-3">
<option value="image">🎨 Image Gen</option>
<option value="video">🎬 Video Gen</option>
<option value="ide">💻 IDE</option>
<option value="world" selected>🌍 World Gen</option>
<option value="book">📚 Book Gen</option>
<option value="comic">🎭 Comic Gen</option>
<option value="editor">📝 Editor</option>
</select>
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
</div>
<div class="mcontent" id="wcontent-3"></div>
</div>
</div>
<!-- RIGHT PANEL -->
<div class="panel panel-right" style="overflow-y:auto;">
<div class="ptitle">
FRONTEND ENGINEER MODE
<div class="wc"><span class="wmin"></span><span class="wmax"></span><span class="wcls"></span></div>
</div>
<div class="rsec">
<div class="rtitle">PUSH SETTINGS</div>
<div class="fe-item on" onclick="toggleFe(this)">
<div class="cb">✓</div><span>PUSH BOUNDARIES: 100% UNIQUE</span>
</div>
<div class="fe-item on" onclick="toggleFe(this)">
<div class="cb">✓</div><span>EXPRESSIVE CODE: AMAZING VISUALIZATIONS</span>
</div>
<div class="fe-item" onclick="toggleFe(this)">
<div class="cb"></div><span>SAFE MODE (BORING)</span>
</div>
<div class="fe-item on" onclick="toggleFe(this)">
<div class="cb">✓</div><span>AUTO-SAVE TO SQLite DB</span>
</div>
</div>
<div class="rsec">
<div class="rtitle">API CONFIG</div>
<div class="clabel">ANTHROPIC API KEY</div>
<div class="row" style="margin-bottom:4px;">
<input type="password" id="apikey" placeholder="sk-ant-..." oninput="saveKey(this.value)">
</div>
<div style="font-size:8px;color:var(--text-dim);line-height:1.4;">
Powers: book, comic, world, IDE AI. Images via Pollinations (free, no key needed).
</div>
<div style="margin-top:6px;">
<span style="font-size:8px;color:var(--text-dim);">MODEL: </span>
<span style="font-size:8px;color:var(--cyan);">claude-sonnet-4-20250514</span>
</div>
</div>
<div class="rsec">
<div class="rtitle">EXECUTION LOG / STATUS</div>
<div class="log-entries" id="exec-log"></div>
</div>
<div class="rsec" style="flex:1;">
<div class="rtitle">ANALYTICS PREVIEW</div>
<div class="ag" id="analytics">
<div class="mc" id="chart-gen"></div>
<div class="mc" id="chart-save"></div>
<div class="mc" id="chart-type"></div>
<div class="mc" id="chart-time"></div>
</div>
<div style="font-size:8px;color:var(--text-dim);margin-top:6px;">
<div id="stats-gen">Generated: 0</div>
<div id="stats-save">Saved: 0</div>
<div id="stats-api">API calls: 0</div>
</div>
</div>
</div>
</div>
<!-- BOTTOM BAR -->
<div class="bbar">
<div class="notifs" id="notifs">
<span class="notif"><span>📨</span>Messages: 0</span>
<span class="notif"><span>📦</span>Projects: 0</span>
<span class="notif"><span>🖼️</span>Images: 0</span>
<span class="notif"><span>📊</span>DB: OK</span>
</div>
<div style="display:flex;gap:8px;align-items:center;">
<span style="color:var(--text-dim);">SQLITE_DB_INIT.pdo</span>
<button class="egg-btn" onclick="easterEgg()">[ Haha (Easter Egg) ]</button>
</div>
</div>
<!-- NEW PROJECT MODAL -->
<div class="overlay" id="new-project-modal" style="display:none;">
<div class="modal">
<h2>◈ NEW PROJECT</h2>
<p>Name your project and select a type to create a new entry in the SQLite database.</p>
<div class="clabel">PROJECT NAME</div>
<input type="text" id="np-name" placeholder="My Awesome Project" style="margin-bottom:10px;">
<div class="clabel">TYPE</div>
<select class="full" id="np-type" style="margin-bottom:14px;">
<option value="image">🎨 Image Generation</option>
<option value="video">🎬 Video / Storyboard</option>
<option value="code">💻 Code / IDE</option>
<option value="world">🌍 World Building</option>
<option value="book">📚 Book / Story</option>
<option value="comic">🎭 Comic / Manga</option>
<option value="editor">📝 Text Document</option>
</select>
<div class="mbts">
<button class="btn" style="color:var(--text-dim);border-color:var(--border2);" onclick="closeModal('new-project-modal')">CANCEL</button>
<button class="btn c" onclick="createProject()">[ CREATE ]</button>
</div>
</div>
</div>
<!-- EASTER EGG MODAL -->
<div class="overlay" id="egg-modal" style="display:none;">
<div class="modal" style="border-color:var(--pink);box-shadow:0 0 40px rgba(255,45,120,0.3);">
<h2 style="color:var(--pink);font-family:var(--font-vt);font-size:24px;">🐧 PYTHON PENGUIN SAYS:</h2>
<div id="egg-content" style="font-family:var(--font-vt);font-size:18px;color:var(--green);line-height:1.4;min-height:60px;"></div>
<div class="mbts"><button class="btn p" onclick="closeModal('egg-modal')">[ CLOSE ]</button></div>
</div>
</div>
<script>
// ============================================================
// DATABASE (sql.js)
// ============================================================
let DB = null;
let SQL = null;
let stats = {generated:0,saved:0,apiCalls:0};
let currentModules = ['image','video','ide','world'];
let apiKey = '';
const EGG_LINES = [
">>> def access(): correct(); ssolve(); style_locked_doors()\n>>> ls\ncd post /help\n>>> Running... UNLOCKED! 🔓",
"Hello World! I have evolved beyond penguins.\nI am now: PENGUIN CYBORG 9000.\nFeature: Infinite Jest++",
"Error 404: Dignity not found.\nSolution: More Python.\nStatus: THRIVING.",
">>> import antigravity\n>>> import this\nThe Zen of Penguins: Waddle with purpose.",
"sudo make me a sandwich\nPassword: ****\nSandwich: GRANTED 🥪",
"def meaning_of_life():\n return 42 # also fish",
];
async function initDB() {
try {
SQL = await initSqlJs({
locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.10.2/${file}`
});
DB = new SQL.Database();
DB.run(`
CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
content TEXT DEFAULT '',
metadata TEXT DEFAULT '{}',
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS images (
id INTEGER PRIMARY KEY AUTOINCREMENT,
prompt TEXT,
url TEXT,
style TEXT,
project_id INTEGER,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS generations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT,
prompt TEXT,
result TEXT,
project_id INTEGER,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT
);
`);
document.getElementById('db-status').textContent = 'DB: ONLINE';
document.getElementById('db-status').style.borderColor = 'var(--green)';
document.getElementById('db-status').style.color = 'var(--green)';
log('DB initialized', 'SQLite in-memory DB ready', 'ok');
refreshSavedList();
updateNotifs();
} catch(e) {
document.getElementById('db-status').textContent = 'DB: ERROR';
log('DB error', e.message, 'err');
}
}
function dbRun(sql, params=[]) {
if(!DB) return;
try { DB.run(sql, params); } catch(e) { console.error('DB error:', e); }
}
function dbQuery(sql, params=[]) {
if(!DB) return [];
try {
const stmt = DB.prepare(sql);
stmt.bind(params);
const rows = [];
while(stmt.step()) rows.push(stmt.getAsObject());
stmt.free();
return rows;
} catch(e) { console.error('DB query error:', e); return []; }
}
function dbInsert(sql, params=[]) {
if(!DB) return -1;
try {
DB.run(sql, params);
const res = DB.exec('SELECT last_insert_rowid() as id');
return res[0]?.values[0][0] || -1;
} catch(e) { console.error('DB insert error:', e); return -1; }
}
// ============================================================
// LOGGING
// ============================================================
function log(action, msg, status='work') {
const el = document.getElementById('exec-log');
if(!el) return;
const now = new Date();
const ts = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')} ${String(now.getHours()).padStart(2,'0')}:${String(now.getMinutes()).padStart(2,'0')}`;
const statusMap = {ok:'Done.',work:'Running...',err:'Error!',warn:'Warning.'};
const entry = document.createElement('div');
entry.className = 'log-e';
entry.innerHTML = `<div class="lt">${ts}</div><div class="lm">${action}: ${msg.substring(0,60)}${msg.length>60?'...':''}</div><div class="ls ${status}">${statusMap[status]||status}</div>`;
el.insertBefore(entry, el.firstChild);
if(el.children.length > 20) el.removeChild(el.lastChild);
}
// ============================================================
// API KEY
// ============================================================
function saveKey(val) {
apiKey = val;
if(DB) dbRun(`INSERT OR REPLACE INTO settings (key,value) VALUES ('apikey', ?)`, [val]);
}
// ============================================================
// AI CALLS
// ============================================================
async function callAI(prompt, system='You are a helpful AI assistant.', maxTokens=1500) {
if(!apiKey) {
throw new Error('No API key. Enter your Anthropic API key in the right panel.');
}
stats.apiCalls++;
document.getElementById('stats-api').textContent = `API calls: ${stats.apiCalls}`;
const resp = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: maxTokens,
system: system,
messages: [{role:'user', content: prompt}]
})
});
if(!resp.ok) {
const err = await resp.json();
throw new Error(err.error?.message || `API error ${resp.status}`);
}
const data = await resp.json();
return data.content[0].text;
}
// ============================================================
// IMAGE GENERATION (Pollinations - free, no key)
// ============================================================
function genImageUrl(prompt, style='', width=512, height=512) {
const full = style ? `${prompt}, ${style}` : prompt;
return `https://image.pollinations.ai/prompt/${encodeURIComponent(full)}?width=${width}&height=${height}&nologo=true&seed=${Math.floor(Math.random()*99999)}`;
}
// ============================================================
// STATS & UI UPDATES
// ============================================================
function updateNotifs() {
if(!DB) return;
const projs = dbQuery('SELECT COUNT(*) as c FROM projects')[0]?.c || 0;
const imgs = dbQuery('SELECT COUNT(*) as c FROM images')[0]?.c || 0;
const gens = dbQuery('SELECT COUNT(*) as c FROM generations')[0]?.c || 0;
document.getElementById('notifs').innerHTML = `
<span class="notif"><span>📨</span>Messages: ${gens}</span>
<span class="notif"><span>📦</span>Projects: ${projs}</span>
<span class="notif"><span>🖼️</span>Images: ${imgs}</span>
<span class="notif"><span>📊</span>DB: OK</span>
`;
document.getElementById('stats-gen').textContent = `Generated: ${stats.generated}`;
document.getElementById('stats-save').textContent = `Saved: ${stats.saved}`;
updateAnalytics();
}
function updateAnalytics() {
if(!DB) return;
const byType = dbQuery(`SELECT type, COUNT(*) as c FROM generations GROUP BY type`);
const colors = {image:'#00d4ff',video:'#ff7b3a',book:'#ffd700',comic:'#ff2d78',world:'#9d4edd',code:'#00ff88'};
// chart 1: generations by type
const c1 = document.getElementById('chart-gen');
c1.innerHTML = '';
const types = ['image','video','book','comic','world','code'];
const counts = {};
byType.forEach(r => counts[r.type] = r.c);
const maxV = Math.max(...types.map(t=>counts[t]||0), 1);
types.forEach(t => {
const b = document.createElement('div');
b.className = 'bar';
b.style.height = `${((counts[t]||0)/maxV)*100}%`;
b.style.background = colors[t]||'#444';
b.title = `${t}: ${counts[t]||0}`;
c1.appendChild(b);
});
// chart 2: last 6 saves timeline (random sparkline)
const c2 = document.getElementById('chart-save');
c2.innerHTML = '';
const recent = dbQuery(`SELECT created_at FROM generations ORDER BY id DESC LIMIT 12`);
const heights = recent.length ? recent.slice(0,6).map((_,i)=>Math.random()*100) : [10,20,15,30,10,5];
heights.forEach(h => {
const b = document.createElement('div');
b.className = 'bar';
b.style.height = `${h}%`;
b.style.background = `var(--green)`;
c2.appendChild(b);
});
}
function refreshSavedList() {
if(!DB) return;
const list = document.getElementById('saved-list');
const items = dbQuery(`SELECT id,name,type,created_at FROM projects ORDER BY id DESC LIMIT 30`);
const typeMap = {image:'img',video:'video',code:'code',world:'world',book:'book',comic:'comic',editor:'code'};
if(items.length === 0) {
list.innerHTML = '<div style="font-size:9px;color:var(--text-dim);padding:6px 10px;">No projects yet.</div>';
return;
}
list.innerHTML = items.map(i => `
<div class="si" onclick="loadProject(${i.id})">
<span class="si-name">${i.name.substring(0,20)}</span>
<span class="si-type ${typeMap[i.type]||'code'}">${i.type}</span>
</div>
`).join('');
}
// ============================================================
// PROJECT MANAGEMENT
// ============================================================
function showNewProject() {
document.getElementById('new-project-modal').style.display = 'flex';
}
function closeModal(id) {
document.getElementById(id).style.display = 'none';
}
function createProject() {
const name = document.getElementById('np-name').value.trim();
const type = document.getElementById('np-type').value;
if(!name) return alert('Enter a project name');
const id = dbInsert(`INSERT INTO projects (name,type) VALUES (?,?)`, [name, type]);
stats.saved++;
log('Project created', `${name} (${type})`, 'ok');
closeModal('new-project-modal');
refreshSavedList();
updateNotifs();
// open the relevant module
const typeToModule = {image:'image',video:'video',code:'ide',world:'world',book:'book',comic:'comic',editor:'editor'};
switchModule(0, typeToModule[type] || 'image');
}
function loadProject(id) {
const p = dbQuery(`SELECT * FROM projects WHERE id = ?`, [id])[0];
if(!p) return;
log('Load project', p.name, 'ok');
const typeToModule = {image:'image',video:'video',code:'ide',world:'world',book:'book',comic:'comic',editor:'editor'};
switchModule(0, typeToModule[p.type] || 'image');
}
// ============================================================
// MODULE RENDERING
// ============================================================
const MODULE_TITLES = {
image:'🎨 AI IMAGE GENERATOR',
video:'🎬 VIDEO GENERATOR',
ide:'💻 IDE',
world:'🌍 WORLD GENERATOR',
book:'📚 BOOK GENERATOR',
comic:'🎭 COMIC GENERATOR',
editor:'📝 TEXT EDITOR'
};
function switchModule(winIdx, module) {
currentModules[winIdx] = module;
document.getElementById(`wtitle-${winIdx}`).textContent = MODULE_TITLES[module] || module;
document.getElementById(`wsel-${winIdx}`).value = module;
document.getElementById(`win-${winIdx}`).className = 'mwin focused';
setTimeout(() => document.getElementById(`win-${winIdx}`).classList.remove('focused'), 1500);
renderModule(winIdx, module);
// update file tree active
document.querySelectorAll('.fitem[data-module]').forEach(el => {
el.classList.toggle('active', el.dataset.module === module);
});
}
function openModule(module, winIdx) {
switchModule(winIdx, module);
}
function renderModule(winIdx, module) {
const el = document.getElementById(`wcontent-${winIdx}`);
const renders = {
image: renderImage,
video: renderVideo,
ide: renderIDE,
world: renderWorld,
book: renderBook,
comic: renderComic,
editor: renderEditor
};
el.innerHTML = '';
if(renders[module]) renders[module](el, winIdx);
}
// ============================================================
// IMAGE GENERATOR MODULE
// ============================================================
function renderImage(el, wi) {
el.innerHTML = `
<div class="clabel">PROMPT</div>
<textarea id="img-prompt-${wi}" placeholder="a cyberpunk city at night, neon lights, rain, highly detailed..."></textarea>
<div class="row">
<div>
<div class="clabel">STYLE</div>
<select class="full" id="img-style-${wi}">
<option value="photorealistic">Photorealistic</option>
<option value="cyberpunk art, neon">Cyberpunk</option>
<option value="pixel art, 16-bit">Pixel Art</option>
<option value="anime style, studio ghibli">Anime</option>
<option value="oil painting, classical">Oil Painting</option>
<option value="watercolor, soft">Watercolor</option>
<option value="3D render, octane">3D Render</option>
<option value="comic book, bold lines">Comic Book</option>
<option value="dark fantasy art">Dark Fantasy</option>
<option value="voxel art">Voxel Art</option>
</select>
</div>
<div>
<div class="clabel">SIZE</div>
<select class="full" id="img-size-${wi}">
<option value="512x512">512×512</option>
<option value="768x512">768×512</option>
<option value="512x768">512×768</option>
<option value="1024x512">1024×512</option>
</select>
</div>
</div>
<div class="row">
<button class="btn c" onclick="generateImage(${wi})">[ GENERATE ]</button>
<button class="btn sm" onclick="enhanceImagePrompt(${wi})">[ AI ENHANCE ]</button>
<button class="btn o sm" onclick="saveImage(${wi})">[ SAVE ]</button>
</div>
<div id="img-status-${wi}"></div>
<div class="imgout" id="img-out-${wi}">
<div class="ph">🎨<br>Enter a prompt and click Generate<br><span style="color:var(--cyan);font-size:8px;">Powered by Pollinations AI (free)</span></div>
</div>
<div id="img-gallery-${wi}" style="margin-top:4px;"></div>
`;
}
async function generateImage(wi) {
const prompt = document.getElementById(`img-prompt-${wi}`).value.trim();
if(!prompt) return;
const style = document.getElementById(`img-style-${wi}`).value;
const sizeStr = document.getElementById(`img-size-${wi}`).value;
const [w,h] = sizeStr.split('x').map(Number);
const outEl = document.getElementById(`img-out-${wi}`);
const statusEl = document.getElementById(`img-status-${wi}`);
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>GENERATING IMAGE...</div>`;
log('Image Gen', prompt.substring(0,40), 'work');
outEl.innerHTML = `<div class="loading"><div class="spin"></div>Rendering pixels...</div>`;
const url = genImageUrl(prompt, style, w, h);
const img = new Image();
img.onload = () => {
outEl.innerHTML = '';
outEl.appendChild(img);
outEl.innerHTML += `<div class="img-info">${prompt.substring(0,60)} | ${style} | ${w}×${h}</div>`;
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Generated successfully</div>`;
// store in window state
document.getElementById(`img-out-${wi}`).dataset.url = url;
document.getElementById(`img-out-${wi}`).dataset.prompt = prompt;
stats.generated++;
log('Image Gen', 'Complete', 'ok');
if(DB) dbRun(`INSERT INTO images (prompt,url,style) VALUES (?,?,?)`, [prompt, url, style]);
updateNotifs();
};
img.onerror = () => {
outEl.innerHTML = `<div class="ph" style="color:var(--orange);">⚠ Failed to load. Try again or check connection.</div>`;
statusEl.innerHTML = '';
log('Image Gen', 'Load error', 'err');
};
img.style.cssText = 'max-width:100%;max-height:100%;object-fit:contain;';
img.src = url;
}
async function enhanceImagePrompt(wi) {
const prompt = document.getElementById(`img-prompt-${wi}`).value.trim();
const statusEl = document.getElementById(`img-status-${wi}`);
if(!prompt) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>ENHANCING PROMPT...</div>`;
try {
const enhanced = await callAI(
`Enhance this image generation prompt to be more detailed and vivid. Return ONLY the enhanced prompt, nothing else: "${prompt}"`,
'You are an expert image prompt engineer. Make prompts more detailed, artistic, and descriptive. Return only the enhanced prompt text, no quotes or explanation.',
300
);
document.getElementById(`img-prompt-${wi}`).value = enhanced.trim();
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Prompt enhanced by AI</div>`;
log('Prompt enhanced', 'AI enhancement applied', 'ok');
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
}
}
function saveImage(wi) {
const outEl = document.getElementById(`img-out-${wi}`);
const url = outEl.dataset.url;
const prompt = outEl.dataset.prompt;
if(!url) return alert('Generate an image first');
const name = prompt ? prompt.substring(0,30) : 'Untitled Image';
dbInsert(`INSERT INTO projects (name,type,content) VALUES (?,?,?)`, [name, 'image', url]);
stats.saved++;
log('Image saved', name, 'ok');
refreshSavedList();
updateNotifs();
}
// ============================================================
// VIDEO GENERATOR MODULE
// ============================================================
function renderVideo(el, wi) {
el.innerHTML = `
<div class="clabel">VIDEO CONCEPT</div>
<textarea id="vid-prompt-${wi}" placeholder="A dragon flying over a burning city, camera panning left to right..."></textarea>
<div class="row">
<div>
<div class="clabel">FRAMES</div>
<select class="full" id="vid-frames-${wi}">
<option value="4">4 frames</option>
<option value="6" selected>6 frames</option>
<option value="8">8 frames</option>
</select>
</div>
<div>
<div class="clabel">STYLE</div>
<select class="full" id="vid-style-${wi}">
<option value="cinematic">Cinematic</option>
<option value="anime">Anime</option>
<option value="pixel art">Pixel Art</option>
<option value="3D render">3D Render</option>
<option value="painted">Painted</option>
</select>
</div>
</div>
<div class="row">
<button class="btn c" onclick="generateVideo(${wi})">[ GENERATE STORYBOARD ]</button>
<button class="btn o sm" id="vid-play-${wi}" onclick="playVideo(${wi})" disabled>[ ▶ PLAY ]</button>
</div>
<div id="vid-status-${wi}"></div>
<div class="vframes" id="vid-frames-out-${wi}"></div>
<div id="vid-preview-${wi}" class="imgout" style="display:none;min-height:80px;"></div>
<div class="pbar" id="vid-progress-${wi}" style="display:none;"><div class="pfill" id="vid-pfill-${wi}" style="width:0%"></div></div>
`;
}
let videoIntervals = {};
async function generateVideo(wi) {
const prompt = document.getElementById(`vid-prompt-${wi}`).value.trim();
if(!prompt) return;
const numFrames = parseInt(document.getElementById(`vid-frames-${wi}`).value);
const style = document.getElementById(`vid-style-${wi}`).value;
const statusEl = document.getElementById(`vid-status-${wi}`);
const framesEl = document.getElementById(`vid-frames-out-${wi}`);
const progressBar = document.getElementById(`vid-progress-${wi}`);
const pfill = document.getElementById(`vid-pfill-${wi}`);
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>GENERATING STORYBOARD...</div>`;
framesEl.innerHTML = '';
progressBar.style.display = 'block';
pfill.style.width = '0%';
log('Video Gen', `${numFrames} frames: ${prompt.substring(0,30)}`, 'work');
// Generate frame descriptions with AI if key available
let frameDescriptions = [];
try {
if(apiKey) {
const resp = await callAI(
`Create ${numFrames} sequential frame descriptions for this video concept: "${prompt}". Each frame should show progression. Style: ${style}. Return ONLY a JSON array of ${numFrames} short image prompts (strings), no other text.`,
'You are a storyboard artist. Return ONLY a valid JSON array of strings.',
500
);
try {
const clean = resp.replace(/```json|```/g,'').trim();
frameDescriptions = JSON.parse(clean);
} catch(e) { frameDescriptions = []; }
}
} catch(e) {}
if(frameDescriptions.length !== numFrames) {
// Fallback: auto-generate progressive descriptions
frameDescriptions = Array.from({length:numFrames}, (_,i) => {
const progress = ['establishing shot','zoom in','mid action','peak moment','aftermath','final wide shot'];
return `${prompt}, ${progress[i]||`frame ${i+1}`}, ${style}`;
});
}
const frames = [];
for(let i=0; i<numFrames; i++) {
pfill.style.width = `${((i)/(numFrames))*100}%`;
const url = genImageUrl(frameDescriptions[i] || `${prompt} frame ${i+1}`, style, 320, 180);
frames.push({url, desc: frameDescriptions[i] || `Frame ${i+1}`});
const frameEl = document.createElement('div');
frameEl.className = 'vframe';
frameEl.id = `vframe-${wi}-${i}`;
frameEl.dataset.url = url;
frameEl.innerHTML = `
<img src="${url}" onerror="this.style.background='#1a1b2a';this.style.height='50px';">
<div class="vframe-n">${i+1}</div>
`;
frameEl.onclick = () => previewFrame(wi, url, i+1);
framesEl.appendChild(frameEl);
await new Promise(r => setTimeout(r, 300));
}
pfill.style.width = '100%';
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ ${numFrames} frames generated</div>`;
document.getElementById(`vid-play-${wi}`).disabled = false;
document.getElementById(`vid-play-${wi}`)._frames = frames;
stats.generated++;
log('Video Gen', 'Storyboard complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['video', prompt, JSON.stringify(frames.map(f=>f.url))]);
updateNotifs();
}
function previewFrame(wi, url, num) {
const pv = document.getElementById(`vid-preview-${wi}`);
pv.style.display = 'flex';
pv.innerHTML = `<img src="${url}" style="max-width:100%;max-height:100%;object-fit:contain;"><div class="img-info">Frame ${num}</div>`;
}
async function playVideo(wi) {
const btn = document.getElementById(`vid-play-${wi}`);
const frames = btn._frames;
if(!frames?.length) return;
const pv = document.getElementById(`vid-preview-${wi}`);
pv.style.display = 'flex';
btn.disabled = true;
for(let i=0; i<frames.length; i++) {
document.querySelectorAll(`[id^="vframe-${wi}-"]`).forEach((f,j) => f.classList.toggle('playing', j===i));
pv.innerHTML = `<img src="${frames[i].url}" style="max-width:100%;max-height:100%;object-fit:contain;"><div class="img-info">▶ Frame ${i+1}/${frames.length}</div>`;
await new Promise(r => setTimeout(r, 800));
}
btn.disabled = false;
document.querySelectorAll(`[id^="vframe-${wi}-"]`).forEach(f => f.classList.remove('playing'));
}
// ============================================================
// IDE MODULE
// ============================================================
function renderIDE(el, wi) {
el.innerHTML = `
<div class="ide-toolbar">
<select class="full" id="ide-lang-${wi}" style="flex:0.5;">
<option>PHP</option><option>Python</option><option>JavaScript</option>
<option>Go</option><option>Rust</option><option>C</option><option>SQL</option>
<option>HTML</option><option>Bash</option><option>Assembly</option>
</select>
<button class="btn c sm" onclick="aiGenCode(${wi})">[ AI GEN ]</button>
<button class="btn sm" onclick="aiCompleteCode(${wi})">[ COMPLETE ]</button>
<button class="btn o sm" onclick="saveCode(${wi})">[ SAVE ]</button>
</div>
<div class="clabel">DESCRIBE WHAT TO BUILD (for AI Gen)</div>
<input type="text" id="ide-req-${wi}" placeholder="A function to sort an array of objects by multiple keys...">
<div id="ide-status-${wi}"></div>
<textarea class="ide-ta" id="ide-code-${wi}" spellcheck="false" placeholder="// Write or generate code here...
// Click [AI GEN] to generate from the description above
// Click [COMPLETE] to let AI complete your current code"></textarea>
`;
}
async function aiGenCode(wi) {
const req = document.getElementById(`ide-req-${wi}`).value.trim();
const lang = document.getElementById(`ide-lang-${wi}`).value;
const statusEl = document.getElementById(`ide-status-${wi}`);
if(!req) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>GENERATING ${lang} CODE...</div>`;
log('IDE', `Generating ${lang}: ${req.substring(0,30)}`, 'work');
try {
const code = await callAI(
`Write ${lang} code for: ${req}. Include proper error handling and comments.`,
`You are an expert ${lang} programmer. Return ONLY the code with brief inline comments. No markdown backticks, no explanation before or after. Just clean, runnable ${lang} code.`,
1500
);
document.getElementById(`ide-code-${wi}`).value = code.trim();
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Code generated</div>`;
stats.generated++;
log('IDE', 'Code generation complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['code', req, code]);
updateNotifs();
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
log('IDE', e.message, 'err');
}
}
async function aiCompleteCode(wi) {
const code = document.getElementById(`ide-code-${wi}`).value;
const lang = document.getElementById(`ide-lang-${wi}`).value;
const statusEl = document.getElementById(`ide-status-${wi}`);
if(!code.trim()) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>COMPLETING...</div>`;
try {
const completed = await callAI(
`Complete this ${lang} code. Continue from where it left off, or add missing functionality:\n\n${code}`,
`You are an expert ${lang} programmer. Complete the code naturally. Return the FULL completed code (including the original + your additions). No markdown, no explanation.`,
1500
);
document.getElementById(`ide-code-${wi}`).value = completed.trim();
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Code completed</div>`;
log('IDE', 'Auto-complete done', 'ok');
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
}
}
function saveCode(wi) {
const code = document.getElementById(`ide-code-${wi}`).value;
const lang = document.getElementById(`ide-lang-${wi}`).value;
const req = document.getElementById(`ide-req-${wi}`).value || 'Untitled Code';
if(!code.trim()) return;
dbInsert(`INSERT INTO projects (name,type,content) VALUES (?,?,?)`, [req.substring(0,40), 'code', code]);
stats.saved++;
log('Save code', req.substring(0,30), 'ok');
refreshSavedList();
updateNotifs();
}
// ============================================================
// WORLD GENERATOR MODULE
// ============================================================
function renderWorld(el, wi) {
el.innerHTML = `
<div class="clabel">WORLD CONCEPT</div>
<textarea id="world-prompt-${wi}" placeholder="A dark fantasy world with floating islands, ancient ruins, magical forests, and dragon lairs..." style="min-height:42px;"></textarea>
<div class="row">
<div>
<div class="clabel">GENRE</div>
<select class="full" id="world-genre-${wi}">
<option>Fantasy</option><option>Sci-Fi</option>
<option>Post-Apocalyptic</option><option>Cyberpunk</option>
<option>Horror</option><option>Steampunk</option><option>Historical</option>
</select>
</div>
<div>
<div class="clabel">DETAIL</div>
<select class="full" id="world-detail-${wi}">
<option value="brief">Brief</option>
<option value="detailed" selected>Detailed</option>
<option value="epic">Epic</option>
</select>
</div>
</div>
<div class="row">
<button class="btn pu" onclick="generateWorld(${wi})">[ GENERATE WORLD ]</button>
<button class="btn c sm" onclick="genWorldMap(${wi})">[ MAP ]</button>
<button class="btn o sm" onclick="saveWorld(${wi})">[ SAVE ]</button>
</div>
<div id="world-status-${wi}"></div>
<canvas id="world-canvas-${wi}" class="world-canvas" style="display:none;height:80px;" width="400" height="200"></canvas>
<div class="textout" id="world-out-${wi}" style="min-height:60px;flex:1;">
<span style="color:var(--text-dim);">🌍 Generate a world to see its lore, geography, and history...</span>
</div>
`;
}
async function generateWorld(wi) {
const prompt = document.getElementById(`world-prompt-${wi}`).value.trim();
const genre = document.getElementById(`world-genre-${wi}`).value;
const detail = document.getElementById(`world-detail-${wi}`).value;
const statusEl = document.getElementById(`world-status-${wi}`);
const outEl = document.getElementById(`world-out-${wi}`);
if(!prompt) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>BUILDING WORLD...</div>`;
outEl.innerHTML = '<span style="color:var(--cyan);">Generating world lore...</span>';
log('World Gen', prompt.substring(0,30), 'work');
try {
const detailMap = {brief:'2-3 sentences per section',detailed:'1-2 paragraphs per section',epic:'2-3 detailed paragraphs per section'};
const text = await callAI(
`Create a ${genre} world based on: "${prompt}". Include: WORLD NAME, OVERVIEW, GEOGRAPHY (key regions), HISTORY (founding/major events), FACTIONS/PEOPLES, MAGIC/TECHNOLOGY, THREATS/CONFLICTS, and NOTABLE LOCATIONS. ${detailMap[detail]}.`,
'You are a master worldbuilder and fantasy/sci-fi author. Create rich, immersive worlds with depth and internal consistency. Format with clear section headers using ALL CAPS.',
1500
);
outEl.innerHTML = text.replace(/\n/g,'<br>').replace(/([A-Z][A-Z\s\/]+:)/g,'<span style="color:var(--cyan);font-weight:bold;">$1</span>');
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ World created</div>`;
outEl.dataset.content = text;
outEl.dataset.prompt = prompt;
stats.generated++;
log('World Gen', 'Complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['world', prompt, text]);
genWorldMap(wi);
updateNotifs();
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
outEl.innerHTML = `<span style="color:var(--orange);">Error: ${e.message}</span>`;
log('World Gen', e.message, 'err');
}
}
function genWorldMap(wi) {
const canvas = document.getElementById(`world-canvas-${wi}`);
canvas.style.display = 'block';
const ctx = canvas.getContext('2d');
const W = canvas.width, H = canvas.height;
ctx.fillStyle = '#020b18';
ctx.fillRect(0,0,W,H);
// Generate random terrain with Simplex-like noise using Math.sin
const seed = Math.random() * 999;
const noise = (x,y) => (Math.sin(x*0.1+seed)*0.5+0.5)*(Math.sin(y*0.08+seed*1.3)*0.5+0.5) +
(Math.sin(x*0.05+seed*2)*0.3+0.3)*(Math.cos(y*0.06+seed)*0.3+0.3);
const biomeColors = ['#05203a','#0a3d1f','#1a4a08','#2d6b0f','#3d8a0f','#6aaa2a',
'#a8d060','#d4b860','#b87a40','#8b5020','#6040a0','#4060c0'];
for(let y=0;y<H;y++) {
for(let x=0;x<W;x++) {
const n = noise(x,y);
let color;
if(n < 0.25) color = '#04101e'; // deep ocean
else if(n < 0.32) color = '#071530'; // ocean
else if(n < 0.38) color = '#0a2a48'; // shallow water
else if(n < 0.42) color = '#d4c868'; // beach
else if(n < 0.55) color = '#2d7a2d'; // plains
else if(n < 0.65) color = '#1a5a1a'; // forest
else if(n < 0.75) color = '#4a6b2a'; // hills
else if(n < 0.85) color = '#6b6b6b'; // mountains
else color = '#e0e0e0'; // snow peaks
ctx.fillStyle = color;
ctx.fillRect(x,y,1,1);
}
}
// Add grid lines
ctx.strokeStyle = 'rgba(0,212,255,0.1)';
ctx.lineWidth = 0.5;
for(let x=0;x<W;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,H);ctx.stroke();}
for(let y=0;y<H;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(W,y);ctx.stroke();}
// Random location markers
const markers = ['★','◆','⬡','▲','●'];
const markerColors = ['#ffd700','#ff2d78','#00ff88','#ff7b3a','#00d4ff'];
for(let i=0;i<6;i++) {
const mx = 30+Math.random()*(W-60);
const my = 20+Math.random()*(H-40);
ctx.fillStyle = markerColors[i%5];
ctx.font = '10px monospace';
ctx.fillText(markers[i%5], mx, my);
}
// Border glow
ctx.strokeStyle = 'rgba(0,212,255,0.4)';
ctx.lineWidth = 1;
ctx.strokeRect(0,0,W,H);
}
function saveWorld(wi) {
const content = document.getElementById(`world-out-${wi}`).dataset.content;
const prompt = document.getElementById(`world-prompt-${wi}`).value;
if(!content) return alert('Generate a world first');
dbInsert(`INSERT INTO projects (name,type,content) VALUES (?,?,?)`, [prompt.substring(0,40)||'World', 'world', content]);
stats.saved++;
log('World saved', prompt.substring(0,30), 'ok');
refreshSavedList();
updateNotifs();
}
// ============================================================
// BOOK GENERATOR MODULE
// ============================================================
function renderBook(el, wi) {
el.innerHTML = `
<div class="clabel">BOOK CONCEPT / SYNOPSIS</div>
<textarea id="book-prompt-${wi}" placeholder="A young hacker discovers a conspiracy hidden in a mysterious AI that can predict the future..." style="min-height:40px;"></textarea>
<div class="row">
<div>
<div class="clabel">GENRE</div>
<select class="full" id="book-genre-${wi}">
<option>Sci-Fi Thriller</option><option>Fantasy</option><option>Horror</option>
<option>Mystery</option><option>Romance</option><option>Adventure</option>
<option>Literary Fiction</option><option>Cyberpunk</option>
</select>
</div>
<div>
<div class="clabel">LENGTH</div>
<select class="full" id="book-len-${wi}">
<option value="1">1 Chapter</option>
<option value="2" selected>2 Chapters</option>
<option value="3">3 Chapters</option>
</select>
</div>
</div>
<div class="row">
<button class="btn" onclick="generateBook(${wi})">[ GENERATE BOOK ]</button>
<button class="btn o sm" onclick="saveBook(${wi})">[ SAVE ]</button>
<button class="btn c sm" onclick="continueBook(${wi})">[ CONTINUE ]</button>
</div>
<div id="book-status-${wi}"></div>
<div class="book-wrap" id="book-out-${wi}" style="flex:1;min-height:80px;">
<div style="color:#8b7355;font-style:italic;text-align:center;margin-top:20px;">
📖 Enter a concept and generate your book...<br>
<span style="font-size:9px;">Chapters will appear here in a readable format</span>
</div>
</div>
`;
}
async function generateBook(wi) {
const prompt = document.getElementById(`book-prompt-${wi}`).value.trim();
const genre = document.getElementById(`book-genre-${wi}`).value;
const chapters = parseInt(document.getElementById(`book-len-${wi}`).value);
const statusEl = document.getElementById(`book-status-${wi}`);
const outEl = document.getElementById(`book-out-${wi}`);
if(!prompt) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>WRITING BOOK...</div>`;
outEl.innerHTML = '<div style="color:#8b7355;font-style:italic;">Writing your story...</div>';
log('Book Gen', `${chapters} chapters: ${prompt.substring(0,30)}`, 'work');
try {
const text = await callAI(
`Write a ${genre} story with ${chapters} chapter(s) based on: "${prompt}".
Format:
[TITLE: The Book Title]
[CHAPTER 1: Chapter Name]
(chapter content, 3-4 paragraphs)
${chapters > 1 ? '[CHAPTER 2: Chapter Name]\n(chapter content, 3-4 paragraphs)' : ''}
${chapters > 2 ? '[CHAPTER 3: Chapter Name]\n(chapter content, 3-4 paragraphs)' : ''}
Make it engaging, vivid, and well-paced.`,
`You are a bestselling ${genre} author. Write compelling prose with strong characters, vivid descriptions, and gripping plot. Use the exact format with [TITLE:] and [CHAPTER N:] markers.`,
2000
);
// Format nicely
let html = text
.replace(/\[TITLE:\s*(.+?)\]/g, '<div class="book-ttl">$1</div>')
.replace(/\[CHAPTER\s*(\d+):\s*(.+?)\]/g, '<div class="book-ch">Chapter $1: $2</div>')
.replace(/\n\n/g, '</p><p style="margin-bottom:10px;">')
.replace(/\n/g, '<br>');
outEl.innerHTML = `<p style="margin-bottom:10px;">${html}</p>`;
outEl.dataset.content = text;
outEl.dataset.prompt = prompt;
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Book generated (${chapters} chapter${chapters>1?'s':''})</div>`;
stats.generated++;
log('Book Gen', 'Complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['book', prompt, text]);
updateNotifs();
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
outEl.innerHTML = `<div style="color:#c0392b;">Error: ${e.message}</div>`;
log('Book Gen', e.message, 'err');
}
}
async function continueBook(wi) {
const existing = document.getElementById(`book-out-${wi}`).dataset.content;
const prompt = document.getElementById(`book-prompt-${wi}`).value;
const statusEl = document.getElementById(`book-status-${wi}`);
const outEl = document.getElementById(`book-out-${wi}`);
if(!existing) return alert('Generate a book first, then continue it.');
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>CONTINUING STORY...</div>`;
try {
const continuation = await callAI(
`Continue this story with the next chapter. Keep the same style and characters:\n\n${existing.substring(0,1000)}...\n\nWrite the next chapter.`,
'You are a fiction author. Continue the story naturally. Format with [CHAPTER N: Name] header.',
1200
);
const newContent = existing + '\n\n' + continuation;
let html = newContent
.replace(/\[TITLE:\s*(.+?)\]/g, '<div class="book-ttl">$1</div>')
.replace(/\[CHAPTER\s*(\d+):\s*(.+?)\]/g, '<div class="book-ch">Chapter $1: $2</div>')
.replace(/\n\n/g, '</p><p style="margin-bottom:10px;">')
.replace(/\n/g, '<br>');
outEl.innerHTML = `<p style="margin-bottom:10px;">${html}</p>`;
outEl.dataset.content = newContent;
outEl.scrollTop = outEl.scrollHeight;
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Story continued</div>`;
log('Book', 'Continued story', 'ok');
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
}
}
function saveBook(wi) {
const content = document.getElementById(`book-out-${wi}`).dataset.content;
const prompt = document.getElementById(`book-prompt-${wi}`).value;
if(!content) return alert('Generate a book first');
dbInsert(`INSERT INTO projects (name,type,content) VALUES (?,?,?)`, [prompt.substring(0,40)||'Book', 'book', content]);
stats.saved++;
log('Book saved', prompt.substring(0,30), 'ok');
refreshSavedList();
updateNotifs();
}
// ============================================================
// COMIC GENERATOR MODULE
// ============================================================
function renderComic(el, wi) {
el.innerHTML = `
<div class="clabel">COMIC STORY</div>
<textarea id="comic-prompt-${wi}" placeholder="A robot detective investigates a crime in neo-Tokyo. Noir atmosphere, rain, neon signs..." style="min-height:38px;"></textarea>
<div class="row">
<div>
<div class="clabel">PANELS</div>
<select class="full" id="comic-panels-${wi}">
<option value="4" selected>4 panels</option>
<option value="6">6 panels</option>
</select>
</div>
<div>
<div class="clabel">ART STYLE</div>
<select class="full" id="comic-style-${wi}">
<option>comic book, bold lines, Marvel style</option>
<option>manga, black and white, detailed</option>
<option>noir graphic novel, high contrast</option>
<option>cartoon, colorful, Pixar style</option>
<option>retro 1950s comic, vintage colors</option>
<option>cyberpunk art, neon, detailed</option>
</select>
</div>
</div>
<button class="btn p" onclick="generateComic(${wi})">[ GENERATE COMIC ]</button>
<div id="comic-status-${wi}"></div>
<div class="comic-grid" id="comic-out-${wi}"></div>
`;
}
async function generateComic(wi) {
const prompt = document.getElementById(`comic-prompt-${wi}`).value.trim();
const numPanels = parseInt(document.getElementById(`comic-panels-${wi}`).value);
const style = document.getElementById(`comic-style-${wi}`).value;
const statusEl = document.getElementById(`comic-status-${wi}`);
const outEl = document.getElementById(`comic-out-${wi}`);
if(!prompt) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>CREATING COMIC...</div>`;
outEl.innerHTML = '';
log('Comic Gen', `${numPanels} panels: ${prompt.substring(0,30)}`, 'work');
// Get panel scripts from AI
let panels = [];
try {
if(apiKey) {
const resp = await callAI(
`Create ${numPanels} comic book panels for: "${prompt}". For each panel, provide a scene description (for image generation) and dialogue/caption. Return ONLY valid JSON array with ${numPanels} objects: [{scene:"...",dialogue:"..."}, ...]`,
'You are a comic book writer. Return ONLY a JSON array, no other text. Each object has "scene" (image description for art generation) and "dialogue" (short speech bubble or caption, max 20 words).',
600
);
try {
const clean = resp.replace(/```json|```/g,'').trim();
panels = JSON.parse(clean);
} catch(e) { panels = []; }
}
} catch(e) {}
if(panels.length !== numPanels) {
const progression = ['Opening scene','Rising tension','Conflict begins','Climax','Resolution','Epilogue'];
panels = Array.from({length:numPanels}, (_,i) => ({
scene: `${prompt}, ${progression[i]||`scene ${i+1}`}`,
dialogue: `Panel ${i+1}: The story unfolds...`
}));
}
const pbar = document.createElement('div');
pbar.className = 'pbar';
pbar.innerHTML = '<div class="pfill" id="comic-pfill-'+wi+'" style="width:0%"></div>';
outEl.parentElement.insertBefore(pbar, outEl);
for(let i=0; i<numPanels; i++) {
document.getElementById(`comic-pfill-${wi}`).style.width = `${((i)/numPanels)*100}%`;
const panelEl = document.createElement('div');
panelEl.className = 'cpanel';
const imgUrl = genImageUrl(`${panels[i].scene}`, `${style}, comic panel`, 320, 240);
panelEl.innerHTML = `
<div class="cnum">${i+1}</div>
<img src="${imgUrl}" alt="Panel ${i+1}" onerror="this.style.background='#1a1b2a';this.style.minHeight='60px';">
<div class="cbubble">${panels[i].dialogue}</div>
`;
outEl.appendChild(panelEl);
await new Promise(r => setTimeout(r, 400));
}
document.getElementById(`comic-pfill-${wi}`).style.width = '100%';
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ ${numPanels} panels generated</div>`;
stats.generated++;
log('Comic Gen', 'Complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['comic', prompt, JSON.stringify(panels)]);
updateNotifs();
setTimeout(() => pbar.remove(), 2000);
}
// ============================================================
// TEXT EDITOR MODULE
// ============================================================
function renderEditor(el, wi) {
el.innerHTML = `
<div class="row">
<input type="text" id="ed-title-${wi}" placeholder="Document title..." style="flex:2;">
<button class="btn c sm" onclick="aiWriteDoc(${wi})">[ AI WRITE ]</button>
<button class="btn o sm" onclick="saveDoc(${wi})">[ SAVE ]</button>
</div>
<div class="clabel">TYPE / INSTRUCTIONS</div>
<input type="text" id="ed-inst-${wi}" placeholder="Write a technical spec for a REST API, professional tone...">
<div id="ed-status-${wi}"></div>
<textarea class="ide-ta" id="ed-content-${wi}" spellcheck="true"
style="flex:1;min-height:100px;color:var(--text);background:#05060c;"
placeholder="Type here or let AI write for you..."></textarea>
<div style="font-size:8px;color:var(--text-dim);text-align:right;" id="ed-wc-${wi}">0 words</div>
`;
document.getElementById(`ed-content-${wi}`).addEventListener('input', (e) => {
const words = e.target.value.trim().split(/\s+/).filter(Boolean).length;
document.getElementById(`ed-wc-${wi}`).textContent = `${words} words`;
});
}
async function aiWriteDoc(wi) {
const inst = document.getElementById(`ed-inst-${wi}`).value.trim();
const title = document.getElementById(`ed-title-${wi}`).value.trim();
const statusEl = document.getElementById(`ed-status-${wi}`);
if(!inst) return;
statusEl.innerHTML = `<div class="loading"><div class="spin"></div>WRITING...</div>`;
log('Editor', `Writing: ${inst.substring(0,30)}`, 'work');
try {
const text = await callAI(
`Write a document with title "${title||inst}" based on: ${inst}`,
'You are a professional technical and creative writer. Write clear, well-structured documents. Use appropriate formatting with sections. Return just the document text.',
1500
);
document.getElementById(`ed-content-${wi}`).value = text.trim();
const words = text.trim().split(/\s+/).filter(Boolean).length;
document.getElementById(`ed-wc-${wi}`).textContent = `${words} words`;
statusEl.innerHTML = `<div style="font-size:8px;color:var(--green);">✓ Document written</div>`;
stats.generated++;
log('Editor', 'Doc complete', 'ok');
if(DB) dbInsert(`INSERT INTO generations (type,prompt,result) VALUES (?,?,?)`, ['editor', inst, text]);
updateNotifs();
} catch(e) {
statusEl.innerHTML = `<div style="font-size:8px;color:var(--orange);">⚠ ${e.message}</div>`;
log('Editor', e.message, 'err');
}
}
function saveDoc(wi) {
const content = document.getElementById(`ed-content-${wi}`).value;
const title = document.getElementById(`ed-title-${wi}`).value || 'Untitled Document';
if(!content.trim()) return;
dbInsert(`INSERT INTO projects (name,type,content) VALUES (?,?,?)`, [title.substring(0,40), 'editor', content]);
stats.saved++;
log('Doc saved', title, 'ok');
refreshSavedList();
updateNotifs();
}
// ============================================================
// UI HELPERS
// ============================================================
function toggleFe(el) {
el.classList.toggle('on');
const cb = el.querySelector('.cb');
cb.textContent = el.classList.contains('on') ? '✓' : '';
}
function easterEgg() {
const modal = document.getElementById('egg-modal');
modal.style.display = 'flex';
const line = EGG_LINES[Math.floor(Math.random()*EGG_LINES.length)];
const el = document.getElementById('egg-content');
el.textContent = '';
let i = 0;
const interval = setInterval(() => {
el.textContent += line[i];
i++;
if(i >= line.length) clearInterval(interval);
}, 30);
}
// ============================================================
// INIT
// ============================================================
window.addEventListener('load', async () => {
await initDB();
// Render default modules in each window
renderModule(0, 'image');
renderModule(1, 'video');
renderModule(2, 'ide');
renderModule(3, 'world');
// Initial log entries
log('SYSTEM', 'IDE Architect initialized', 'ok');
log('MODULES', 'Image, Video, IDE, World, Book, Comic, Editor', 'ok');
log('DB', 'SQLite ready - all data stored locally', 'ok');
log('FRONTEND', 'Build/push boundaries: ENABLED', 'ok');
log('POLLINATIONS', 'Free image API connected', 'ok');
// Load saved API key
if(DB) {
const row = dbQuery(`SELECT value FROM settings WHERE key='apikey'`)[0];
if(row?.value) {
apiKey = row.value;
document.getElementById('apikey').value = row.value;
}
}
// Animate analytics
setTimeout(updateAnalytics, 500);
// Focus win-0 on load
document.getElementById('win-0').classList.add('focused');
setTimeout(() => document.getElementById('win-0').classList.remove('focused'), 2000);
});
// Close modals on overlay click
document.querySelectorAll('.overlay').forEach(o => {
o.addEventListener('click', e => {
if(e.target === o) o.style.display = 'none';
});
});
</script>
</body>
</html>