Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back IDE ARCHITECT: THE EPIC SINGLE-FILE APPLICATION BUILDER Programming
Download Open
Show description 1,542 chars · Programming

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

74,430 bytes · HTML source
<!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>