<?php
if(isset($_GET['url'])) {
    $url = $_GET['url'];
    if(!preg_match('#^https?://#', $url)) $url = 'http://' . $url;
    $ctx = stream_context_create(['http' => ['timeout' => 15, 'user_agent' => 'PrismOS/1.0', 'follow_location' => true]]);
    $body = @file_get_contents($url, false, $ctx);
    if($body === false) { header('Content-Type: text/plain; charset=utf-8'); echo "Fetch failed"; exit; }
    header('Content-Type: text/plain; charset=utf-8');
    echo $body;
    exit;
}
?><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PrismOS</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
html,body { width:100%; height:100%; overflow:hidden; background:#1a002b; }
#screen { width:100%; height:100%; display:flex; flex-direction:column; font-family:'Courier New',monospace; position:relative; overflow:hidden; }

/* top debug panel */
#debug { height:32px; background:#1a0030; border-bottom:1px solid #3a0057; display:flex; align-items:center; padding:0 8px; font-size:13px; color:#668; flex-shrink:0; white-space:nowrap; gap:12px; }
.dlbl { color:#666; }
.dval { color:#88ccff; }
#dbtns { margin-left:auto; display:flex; gap:4px; }

/* terminal */
#term { flex:1; background:#3a0057; color:#ffd4f9; padding:4px 0; overflow-y:auto; overflow-x:hidden; position:relative; font-size:15px; line-height:18px; cursor:text; }
#term::-webkit-scrollbar { width:6px; }
#term::-webkit-scrollbar-track { background:#2a0040; }
#term::-webkit-scrollbar-thumb { background:#7a2a9a; border-radius:3px; }
.ln { white-space:pre-wrap; word-break:break-all; padding:0 8px; min-height:18px; }
.prompt { color:#88ccff; }
.psep { color:#666688; }
.ppath { color:#66cc66; }
.pinput { color:#ffd4f9; }
.cmd { color:#ffd4f9; }
.err { color:#ff6666; }
.info { color:#88aacc; }
.link { color:#88ddff; text-decoration:underline; cursor:pointer; }
.link:hover { color:#aaefff; }
.hl { color:#ffff66; }

/* hint bar */
#hint { height:18px; background:#1a0030; border-top:1px solid #3a0057; display:flex; align-items:center; padding:0 8px; font-size:13px; color:#668; flex-shrink:0; }
.hk { color:#cc4444; font-weight:bold; }
.hd { color:#8888aa; }

/* less viewer */
#less { display:none; position:absolute; top:32px; left:0; right:0; bottom:18px; background:#3a0057; flex-direction:column; z-index:10; }
#less.show { display:flex; }
#lessContent { flex:1; overflow:hidden; color:#ffd4f9; font-size:15px; line-height:18px; padding:4px 8px; position:relative; }
#lessBar { height:18px; background:#1a0030; border-top:1px solid #444466; display:flex; align-items:center; padding:0 8px; font-size:13px; flex-shrink:0; color:#aaa; }
.lbracket { color:#444466; }
.linfo { color:#88ccff; }
.lsep { color:#8888aa; }
.lbtn { color:#668866; cursor:pointer; padding:0 1px; }
.lbtn:hover { background:#446644; }
.lq { color:#cc4444; cursor:pointer; padding:0 1px; }
.lq:hover { background:#664444; }
.llbl { color:#8888aa; }

/* scrollable less content */
#lessInner { position:absolute; top:0; left:8px; right:8px; }
.lrow { white-space:pre-wrap; word-break:break-all; min-height:18px; }

/* mouse cursor */
#cursor { position:fixed; pointer-events:none; z-index:999; display:none; }
#cursor svg { filter:drop-shadow(1px 1px 0 #000); }

/* status bar top */
#topbar { display:none; }
</style>
</head>
<body>

<div id="screen">
<div id="debug">
 <span class="dlbl">CPU:</span><span class="dval" id="dcpu">-</span>
 <span class="dlbl">RAM:</span><span class="dval" id="dram">-</span>
 <span class="dlbl">NET:</span><span class="dval" id="dnet">-</span>
 <span class="dlbl">TASKS:</span><span class="dval" id="dtasks">-</span>
 <span class="dlbl">KB:</span><span class="dval" id="dkb">US</span>
 <span id="dbtns"><span class="hk" style="cursor:pointer" onclick="document.getElementById('term').innerHTML='';addLine('','info','PrismOS v1.0 — type <span class=hl>help</span> for commands')">CLS</span></span>
</div>

<div id="term" tabindex="0"></div>

<div id="less"><div id="lessContent"><div id="lessInner"></div></div><div id="lessBar"></div></div>

<div id="hint">
 <span class="hd">[</span><span class="hk" id="hk_tab">Tab</span><span class="hd">=complete </span>
 <span class="hk">↑↓</span><span class="hd">=history </span>
 <span class="hk">^L</span><span class="hd">=clear </span>
 <span class="hk">^U</span><span class="hd">=line </span>
 <span class="hk">^C</span><span class="hd">=cancel</span>
 <span style="margin-left:auto;color:#668" id="hstatus">PrismOS v1.0</span>
</div>
</div>

<svg id="cursor" width="16" height="20" viewBox="0 0 16 20">
 <polygon points="0,0 0,18 4,14 8,19 10,18 6,13 14,12" fill="white" stroke="black" stroke-width="0.5"/>
</svg>

<script>
const termEl = document.getElementById('term');
const lessEl = document.getElementById('less');
const lessInner = document.getElementById('lessInner');
const lessBar = document.getElementById('lessBar');
const cursorEl = document.getElementById('cursor');
const hstatus = document.getElementById('hstatus');

/* ── File System ─────────────────────────── */
const fs = {
  '/': { type:'d', children:{'home':1,'tmp':1,'var':1,'etc':1,'boot':1,'dev':1,'mnt':1,'root':1} },
  '/home': { type:'d', children:{'user':1} },
  '/home/user': { type:'d', children:{'readme.txt':1,'notes.md':1,'config.ini':1} },
  '/home/user/readme.txt': { type:'f', data:'Welcome to PrismOS!\n\nPrismOS is a minimal x86_64 operating system\nbuilt as a UEFI application.\n\nFeatures:\n- Own bootloader (UEFI)\n- x86_64 long mode with paging\n- PS/2 + USB keyboard support\n- USB mouse with cursor\n- Framebuffer console with VGA font\n- TCP/IP networking (e1000)\n- TLS 1.3\n- WiFi (Intel AX200/210)\n- HTML browser with CSS\n- Shell with commands\n\nType "help" to see available commands.' },
  '/home/user/notes.md': { type:'f', data:'# PrismOS Notes\n\n## Building\n  make          — build bootx64.efi\n  make run      — run in QEMU\n  make debug    — debug with gdb\n\n## Commands\n  browse <url>  — open web page\n  ls            — list files\n  cat <file>    — view file\n  echo          — print text\n  calc          — calculator\n  date          — show date\n  whoami        — who are you?\n  uname         — system info\n  ping          — ping test\n  rainbow       — display rainbow\n  help          — this help\n' },
  '/home/user/config.ini': { type:'f', data:'[system]\n hostname = prismos\n theme = dark\n layout = us\n\n[display]\n width = 1280\n height = 800\n depth = 32\n\n[network]\n dhcp = yes\n dns = 8.8.8.8\n' },
  '/etc': { type:'d', children:{'hostname':1,'motd':1,'version':1} },
  '/etc/hostname': { type:'f', data:'prismOS' },
  '/etc/motd': { type:'f', data:'Welcome to PrismOS!\nType help for commands.' },
  '/etc/version': { type:'f', data:'PrismOS v1.0 (x86_64)\nBuilt with LLVM/clang\n' },
  '/tmp': { type:'d', children:{} },
  '/var': { type:'d', children:{'log':1} },
  '/var/log': { type:'d', children:{'syslog':1,'dmesg':1} },
  '/var/log/syslog': { type:'f', data:'[boot] UEFI initialized\n[boot] GDT loaded\n[boot] IDT loaded\n[boot] Paging enabled\n[boot] LAPIC initialized\n[boot] IOAPIC initialized\n[boot] PS/2 keyboard ready\n[boot] xHCI init: OK\n[boot] USB keyboard: not found\n[boot] USB mouse: not found\n[boot] e1000: link up\n[boot] DHCP: 10.0.2.15\n[boot] System ready\n' },
  '/var/log/dmesg': { type:'f', data:'[    0.000] UEFI v2.70\n[    0.010] Framebuffer: 1280x800@32bpp\n[    0.020] CPU: Intel(R) Core(TM) i7-1165G7 @ 2.80GHz\n[    0.030] GDT: 5 entries\n[    0.040] IDT: 256 entries\n[    0.050] Paging: 4-level\n[    0.060] LAPIC: 0xfee00000\n[    0.070] IOAPIC: 0xfec00000\n[    0.080] PS/2: OK\n[    0.090] xHCI: Intel Tiger Lake\n[    0.100] xHCI: 8 ports\n[    0.110] e1000: 52:54:00:12:34:56\n[    0.120] DHCP: lease 10.0.2.15/24\n' },
  '/boot': { type:'d', children:{'kernel.elf':1} },
  '/boot/kernel.elf': { type:'f', data:'[ELF file - cannot display]\n' },
  '/dev': { type:'d', children:{} },
  '/mnt': { type:'d', children:{} },
  '/root': { type:'d', children:{} },
};
let cwd = '/home/user';
let html_buf = '';
let html_title = '';
  
/* ── Shell State ──────────────────────────── */
let hist = [];
let histIdx = -1;
let inputBuf = '';
let inputPos = 0;
let cmdLine = '';
let reading = false;
let readlineCb = null;

const commands = {};

/* ── Utilities ───────────────────────────── */
function addLine(text, cls='', html='') {
  const d = document.createElement('div');
  d.className = 'ln ' + cls;
  if(html) d.innerHTML = html;
  else d.textContent = text;
  termEl.appendChild(d);
  termEl.scrollTop = termEl.scrollHeight;
}
function addPrompt() {
  const d = document.createElement('div');
  d.className = 'ln';
  d.innerHTML = '<span class=prompt>prismos</span><span class=psep>:</span><span class=ppath>~</span><span class=psep>$ </span><span class=pinput contenteditable=false id=inpline></span>';
  termEl.appendChild(d);
  const inp = document.getElementById('inpline');
  if(!inp) { /* fallback */ }
  termEl.scrollTop = termEl.scrollHeight;
}
function setPromptText(t) {
  const inp = document.getElementById('inpline');
  if(inp) inp.textContent = t;
}
function getPromptText() {
  const inp = document.getElementById('inpline');
  return inp ? inp.textContent : '';
}

/* ── Tokenizer ───────────────────────────── */
function tokenize(line) {
  const args = [];
  let i = 0;
  while(i < line.length) {
    while(i < line.length && line[i] === ' ') i++;
    if(i >= line.length) break;
    let s = '';
    if(line[i] === '"' || line[i] === "'") {
      const q = line[i++];
      while(i < line.length && line[i] !== q) s += line[i++];
      i++;
    } else {
      while(i < line.length && line[i] !== ' ') s += line[i++];
    }
    args.push(s);
  }
  return args;
}

/* ── Keyboard Input ──────────────────────── */
termEl.addEventListener('keydown', function(e) {
  if(reading) { handleReadingKey(e); return; }
  if(e.key === 'Enter') {
    e.preventDefault();
    const line = getPromptText();
    setPromptText('');
    addLine('prismos:~$ ' + line, 'cmd');
    execute(line);
    return;
  }
  if(e.key === 'Backspace') {
    e.preventDefault();
    const t = getPromptText();
    setPromptText(t.slice(0,-1));
    return;
  }
  if(e.key === 'Tab') {
    e.preventDefault();
    const t = getPromptText();
    const args = tokenize(t);
    if(args.length <= 1) {
      const matches = Object.keys(commands).filter(c => c.startsWith(args[0]));
      if(matches.length === 1) setPromptText(matches[0] + ' ');
      else if(matches.length > 1) {
        addLine('', 'info', matches.join('  '));
        addPrompt();
        setPromptText(t);
      }
    }
    return;
  }
  if(e.key === 'ArrowUp') {
    e.preventDefault();
    if(hist.length === 0) return;
    if(histIdx === -1) histIdx = hist.length;
    histIdx = Math.max(0, histIdx - 1);
    setPromptText(hist[histIdx]);
    return;
  }
  if(e.key === 'ArrowDown') {
    e.preventDefault();
    if(histIdx === -1) return;
    histIdx++;
    if(histIdx >= hist.length) { histIdx = -1; setPromptText(''); return; }
    setPromptText(hist[histIdx]);
    return;
  }
  if(e.key === 'ArrowLeft') { e.preventDefault(); return; }
  if(e.key === 'ArrowRight') { e.preventDefault(); return; }
  if(e.key === 'c' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); setPromptText(''); return; }
  if(e.key === 'l' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); termEl.innerHTML = ''; addPrompt(); return; }
  if(e.key === 'u' && (e.ctrlKey || e.metaKey)) { e.preventDefault(); setPromptText(''); return; }
  if(e.key.length === 1) {
    e.preventDefault();
    setPromptText(getPromptText() + e.key);
  }
});

function handleReadingKey(e) {
  if(e.key === 'Enter') { e.preventDefault(); if(readlineCb) readlineCb(getPromptText()); return; }
  if(e.key === 'Backspace') { e.preventDefault(); const t = getPromptText(); setPromptText(t.slice(0,-1)); return; }
  if(e.key.length === 1) { e.preventDefault(); setPromptText(getPromptText() + e.key); }
}

function readline(prompt, cb) {
  reading = true;
  readlineCb = function(line) {
    reading = false;
    readlineCb = null;
    setPromptText('');
    cb(line);
  };
  addLine(prompt);
  addPrompt();
  termEl.focus();
}

/* ── Command: help ────────────────────────── */
commands.help = function(args) {
  addLine('', 'info', 'PrismOS Shell v1.0');
  addLine('', 'info', 'Available commands:');
  const list = Object.keys(commands).sort();
  for(let i=0; i<list.length; i+=4) {
    let s = '  ';
    for(let j=i; j<Math.min(i+4, list.length); j++) {
      s += list[j].padEnd(16);
    }
    addLine(s, 'info');
  }
};

/* ── Command: echo ────────────────────────── */
commands.echo = function(args) {
  addLine(args.slice(1).join(' '));
};

/* ── Command: clear ───────────────────────── */
commands.clear = function(args) {
  termEl.innerHTML = '';
  addPrompt();
};

/* ── Command: whoami ──────────────────────── */
commands.whoami = function(args) {
  addLine('user');
};

/* ── Command: uname ───────────────────────── */
commands.uname = function(args) {
  addLine('PrismOS x86_64');
};

/* ── Command: hostname ────────────────────── */
commands.hostname = function(args) {
  addLine('prismOS');
};

/* ── Command: date ────────────────────────── */
commands.date = function(args) {
  addLine(new Date().toString());
};

/* ── Command: uptime ──────────────────────── */
commands.uptime = function(args) {
  const u = Math.floor((Date.now() - startTime) / 1000);
  const h = Math.floor(u/3600), m = Math.floor((u%3600)/60), s = u%60;
  addLine(`up ${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`);
};

/* ── Command: pwd ─────────────────────────── */
commands.pwd = function(args) { addLine(cwd); };

/* ── Command: ls ──────────────────────────── */
commands.ls = function(args) {
  let p = cwd;
  if(args.length > 1) p = resolve(args[1]);
  const node = resolveNode(p);
  if(!node || node.type !== 'd') { addLine('ls: ' + (args[1]||'') + ': not a directory', 'err'); return; }
  const names = Object.keys(node.children).sort();
  for(let n of names) {
    const child = resolveNode(p + '/' + n);
    addLine('  ' + n + (child && child.type==='d' ? '/' : ''), 'info');
  }
};

function resolve(path) {
  if(!path) return cwd;
  if(path.startsWith('/')) { let p = path; while(p.includes('/.')) p = simplify(p); return p; }
  let p = cwd + '/' + path;
  while(p.includes('/.')) p = simplify(p);
  return p;
}
function simplify(p) {
  const parts = p.split('/').filter(x=>x);
  const out = [];
  for(let part of parts) {
    if(part === '.') continue;
    if(part === '..') { out.pop(); continue; }
    out.push(part);
  }
  return '/' + out.join('/');
}
function resolveNode(p) {
  const parts = p.split('/').filter(x=>x);
  let node = fs['/'];
  if(!node) return null;
  for(let part of parts) {
    if(!node.children || !node.children[part]) return null;
    node = fs[p.split(part).slice(0,-1).join('/') + '/' + part] || null;
    if(!node) { /* try to find it */
      let curPath = '/';
      for(let pp of parts) {
        if(pp === part) { node = fs[curPath + part]; break; }
        if(fs[curPath] && fs[curPath].children && fs[curPath].children[pp])
          curPath += pp + '/';
        else return null;
      }
    }
  }
  return node;
}

function readFile(path) {
  const node = resolveNode(resolve(path));
  if(!node) return null;
  if(node.type !== 'f') return null;
  return node.data;
}

/* ── Command: cat ─────────────────────────── */
commands.cat = function(args) {
  if(args.length < 2) { addLine('Usage: cat <file>', 'err'); return; }
  const data = readFile(args[1]);
  if(data === null) { addLine('cat: ' + args[1] + ': no such file', 'err'); return; }
  for(let line of data.split('\n')) addLine(line);
};

/* ── Command: calc ────────────────────────── */
commands.calc = function(args) {
  addLine('Simple calculator. Type expression, "q" to quit.', 'info');
  readline('  > ', function(line) {
    if(line === 'q' || line === 'quit') { addPrompt(); return; }
    try {
      const result = Function('"use strict"; return (' + line + ')')();
      addLine('  = ' + result, 'info');
    } catch(e) { addLine('  Error', 'err'); }
    commands.calc([]);
  });
};

/* ── Command: rainbow ─────────────────────── */
commands.rainbow = function(args) {
  const colors = ['#ff6666','#ffaa66','#ffff66','#66ff66','#66aaff','#aa66ff','#ff66aa'];
  for(let i=0; i<7; i++) {
    const d = document.createElement('div');
    d.className = 'ln';
    d.style.color = colors[i];
    d.textContent = 'PrismOS';
    termEl.appendChild(d);
  }
  termEl.scrollTop = termEl.scrollHeight;
};

/* ── Command: ping ────────────────────────── */
commands.ping = function(args) {
  const target = args[1] || '8.8.8.8';
  addLine('PING ' + target + ' (' + target + ') 56(84) bytes of data.', 'info');
  let count = 0;
  const iv = setInterval(() => {
    if(count >= 4) { clearInterval(iv); addLine('', 'info', '--- ' + target + ' ping statistics ---'); addLine('4 packets transmitted, 4 received, 0% packet loss', 'info'); return; }
    const ms = Math.floor(Math.random() * 20 + 5);
    addLine('64 bytes from ' + target + ': icmp_seq=' + (count+1) + ' ttl=64 time=' + ms + ' ms', 'info');
    count++;
  }, 800);
};

/* ── Command: neofetch ────────────────────── */
commands.neofetch = function(args) {
  const art = [
    "       .---.       ",
    "      /     \\      ",
    "     |  O O  |     ",
    "      \\  ~  /      ",
    "       \\___/       ",
    "    PrismOS x86_64 "
  ];
  const info = [
    "OS: PrismOS v1.0 x86_64",
    "Host: Virtual Box",
    "Kernel: PrismOS 1.0",
    "Shell: prismosh",
    "Terminal: prismoterm",
    "CPU: x86_64 @ ~2.8GHz",
    "Memory: " + Math.floor(Math.random()*512+256) + "MiB",
    "Uptime: " + new Date().toLocaleTimeString(),
  ];
  for(let i=0; i<Math.max(art.length, info.length); i++) {
    const a = art[i] || '';
    const b = info[i] || '';
    const d = document.createElement('div');
    d.className = 'ln';
    d.style.color = '#88ccff';
    d.textContent = '  ' + a + '    ' + b;
    termEl.appendChild(d);
  }
  termEl.scrollTop = termEl.scrollHeight;
};

/* ── Command: ps ──────────────────────────── */
commands.ps = function(args) {
  addLine('  PID  COMMAND', 'info');
  addLine('    1  init', 'info');
  addLine('    2  shell', 'info');
  addLine('    3  dbg_daemon', 'info');
  addLine('    4  net_worker', 'info');
  addLine('    5  browser', 'info');
};

/* ── Command: df ──────────────────────────── */
commands.df = function(args) {
  addLine('Filesystem   1K-blocks  Used  Available  Use%  Mounted on', 'info');
  addLine('devtmpfs        512000  4200     507800   1%   /', 'info');
  addLine('tmpfs           1024000  1200    1022800   1%   /tmp', 'info');
};

/* ── Command: free ────────────────────────── */
commands.free = function(args) {
  const total = 512;
  const used = Math.floor(Math.random()*200+100);
  addLine('              total   used   free   shared  buff/cache', 'info');
  addLine('Mem:          ' + total + '    ' + used + '    ' + (total-used) + '    12      ' + Math.floor(Math.random()*50+20), 'info');
};

/* ── Command: history ─────────────────────── */
commands.history = function(args) {
  for(let i=0; i<hist.length; i++) addLine('  ' + (i+1) + '  ' + hist[i], 'info');
};

/* ── Command: export ──────────────────────── */
commands.export = function(args) {
  if(args.length < 2) { addLine('Usage: export VAR=value', 'err'); return; }
  addLine('(export not implemented in web version)', 'info');
};

/* ── Command: cd ──────────────────────────── */
commands.cd = function(args) {
  let target = args[1] || '/';
  if(target === '~') target = '/home/user';
  const resolved = resolve(target);
  const node = resolveNode(resolved);
  if(!node || node.type !== 'd') { addLine('cd: ' + target + ': No such directory', 'err'); return; }
  cwd = resolved;
  /* update prompt path display */
};
commands.chdir = commands.cd;

/* ── Command: rm ──────────────────────────── */
commands.rm = function(args) {
  if(args.length < 2) { addLine('Usage: rm <file>', 'err'); return; }
  /* in web version, just pretend */
  addLine('(rm simulated)', 'info');
};

/* ── Command: mkdir ───────────────────────── */
commands.mkdir = function(args) {
  if(args.length < 2) { addLine('Usage: mkdir <dir>', 'err'); return; }
  addLine('(mkdir simulated)', 'info');
};

/* ── Command: touch ───────────────────────── */
commands.touch = function(args) {
  if(args.length < 2) { addLine('Usage: touch <file>', 'err'); return; }
  addLine('(touch simulated)', 'info');
};

/* ── Command: reboot ──────────────────────── */
commands.reboot = function(args) {
  addLine('System rebooting...', 'info');
  setTimeout(() => { termEl.innerHTML = ''; addLine('','info','PrismOS v1.0 — type <span class=hl>help</span> for commands'); addPrompt(); }, 1000);
};
commands.shutdown = function(args) {
  addLine('System halted. Power off.', 'info');
  addLine('(close this tab)', 'info');
};

/* ── Command: cowsay ──────────────────────── */
commands.cowsay = function(args) {
  const msg = args.slice(1).join(' ') || 'Moo!';
  const border = '-'.repeat(msg.length + 2);
  addLine('  ' + border, 'info');
  addLine(' < ' + msg + ' >', 'info');
  addLine('  ' + border, 'info');
  addLine('        \\   ^__^', 'info');
  addLine('         \\  (oo)\\_______', 'info');
  addLine('            (__)\\       )\\/\\', 'info');
  addLine('                ||----w |', 'info');
  addLine('                ||     ||', 'info');
};

/* ── Command: banner ──────────────────────── */
commands.banner = function(args) {
  const text = args.slice(1).join(' ') || 'PrismOS';
  const big = {
    'A':[' AAA ','A   A','AAAAA','A   A','A   A'],
    'B':['BBBB ','B   B','BBBB ','B   B','BBBB '],
    'C':[' CCC ','C   C','C    ','C   C',' CCC '],
    'D':['DDDD ','D   D','D   D','D   D','DDDD '],
    'E':['EEEEE','E    ','EEE  ','E    ','EEEEE'],
    'F':['FFFFF','F    ','FFF  ','F    ','F    '],
    'G':[' GGG ','G   G','G    ','G  GG',' GGG '],
    'H':['H   H','H   H','HHHHH','H   H','H   H'],
    'I':['IIIII','  I  ','  I  ','  I  ','IIIII'],
    'J':['JJJJJ','    J','    J','J   J',' JJJ '],
    'K':['K   K','K  K ','KKK  ','K  K ','K   K'],
    'L':['L    ','L    ','L    ','L    ','LLLLL'],
    'M':['M   M','MM MM','M M M','M   M','M   M'],
    'N':['N   N','NN  N','N N N','N  NN','N   N'],
    'O':[' OOO ','O   O','O   O','O   O',' OOO '],
    'P':['PPPP ','P   P','PPPP ','P    ','P    '],
    'Q':[' QQQ ','Q   Q','Q   Q','Q  Q ',' QQQQ'],
    'R':['RRRR ','R   R','RRRR ','R  R ','R   R'],
    'S':[' SSS ','S   S',' SSS ','    S',' SSS '],
    'T':['TTTTT','  T  ','  T  ','  T  ','  T  '],
    'U':['U   U','U   U','U   U','U   U',' UUU '],
    'V':['V   V','V   V','V   V',' V V ','  V  '],
    'W':['W   W','W   W','W W W','WW WW','W   W'],
    'X':['X   X',' X X ','  X  ',' X X ','X   X'],
    'Y':['Y   Y',' Y Y ','  Y  ','  Y  ','  Y  '],
    'Z':['ZZZZZ','   Z ','  Z  ',' Z   ','ZZZZZ'],
    '0':[' 00  ','0  0 ','0  0 ','0  0 ',' 00  '],
    '1':['  1  ',' 11  ','  1  ','  1  ',' 111 '],
    '2':[' 22  ','2  2 ','   2 ','  2  ','22222'],
    '3':[' 33  ','   3 ',' 33  ','   3 ',' 33  '],
    '4':['4  4 ','4  4 ','44444','   4 ','   4 '],
    '5':['55555','5    ','5555 ','   5 ','5555 '],
    '6':[' 66  ','6    ','6666 ','6  6 ',' 66  '],
    '7':['77777','   7 ','  7  ',' 7   ','7    '],
    '8':[' 88  ','8  8 ',' 88  ','8  8 ',' 88  '],
    '9':[' 99  ','9  9 ',' 999 ','   9 ',' 99  '],
  };
  const lines = ['','','','',''];
  for(let ch of text.toUpperCase()) {
    const pattern = big[ch] || ['     ','     ','     ','     ','     '];
    for(let r=0;r<5;r++) lines[r] += pattern[r] + '  ';
  }
  for(let r=0;r<5;r++) addLine(lines[r], '');
};

/* ── Command: browse ──────────────────────── */
commands.browse = function(args) {
  if(args.length < 2) { addLine('Usage: browse <url>', 'err'); return; }
  const url = args[1];
  addLine('Fetching ' + url + ' ...', 'info');
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '?url=' + encodeURIComponent(url), true);
  xhr.onload = function() {
    if(xhr.status === 200) {
      html_buf = xhr.responseText;
      html_title = url;
      addLine('OK (' + html_buf.length + ' bytes)', 'info');
      showLess();
    } else {
      addLine('Fetch failed: HTTP ' + xhr.status, 'err');
    }
  };
  xhr.onerror = function() { addLine('Fetch failed (network error)', 'err'); };
  xhr.send();
};

/* ── Less Viewer ──────────────────────────── */
let lessScroll = 0;
let lessRows = 0;
let lessVisibleRows = 0;
let lessTotalLines = 0;
let lessLines = [];

function showLess() {
  lessEl.classList.add('show');
  lessScroll = 0;
  renderLess();
  termEl.style.display = 'none';
}

function hideLess() {
  lessEl.classList.remove('show');
  termEl.style.display = 'block';
  lessLines = [];
  lessTotalLines = 0;
}

function renderLess() {
  if(!html_buf) return;
  
  /* Build lines from HTML content */
  if(lessLines.length === 0) {
    lessLines = html_buf.split('\n');
    lessTotalLines = lessLines.length;
  }
  
  const lc = document.getElementById('lessContent');
  const lh = lc.clientHeight;
  const lw = lc.clientWidth;
  const charH = 18;
  const charW = 9;
  lessVisibleRows = Math.floor(lh / charH) - 1; /* leave 1 for status bar */
  if(lessVisibleRows < 1) lessVisibleRows = 1;
  
  let html = '';
  const start = lessScroll;
  const end = Math.min(lessScroll + lessVisibleRows, lessTotalLines);
  for(let i = start; i < end; i++) {
    html += '<div class="lrow">' + escHtml(lessLines[i]) + '</div>';
  }
  lessInner.innerHTML = html;
  
  /* Status bar */
  const pct = lessTotalLines > lessVisibleRows ? Math.floor(lessScroll * 100 / (lessTotalLines - lessVisibleRows)) : 0;
  const n1 = lessScroll + 1;
  const n2 = Math.min(lessScroll + lessVisibleRows, lessTotalLines);
  
  lessBar.innerHTML =
    '<span class="lbracket">[</span>' +
    '<span class="linfo"> ' + n1 + '-' + n2 + '/' + lessTotalLines + ' ' + pct + '% </span>' +
    '<span class="lbracket">]</span>' +
    '<span class="lbtn" data-cmd="up"> \u2191\u2193 </span>' +
    '<span class="llbl">line  </span>' +
    '<span class="lbtn" data-cmd="pgup"> \u21d1\u21d3 </span>' +
    '<span class="llbl">page  </span>' +
    '<span class="lbtn" data-cmd="home">Home/End  </span>' +
    '<span class="lbtn" data-cmd="wheel">Wheel </span>' +
    '<span class="llbl">scroll  </span>' +
    '<span class="lq" data-cmd="quit">q</span><span class="llbl">uit</span>';
}

function escHtml(s) {
  const d = document.createElement('div');
  d.textContent = s;
  return d.innerHTML;
}

lessBar.addEventListener('click', function(e) {
  const btn = e.target.closest('[data-cmd]');
  if(!btn) return;
  const cmd = btn.dataset.cmd;
  switch(cmd) {
    case 'up': lessScroll = Math.max(0, lessScroll - 1); renderLess(); break;
    case 'down': lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + 1); renderLess(); break;
    case 'pgup': lessScroll = Math.max(0, lessScroll - lessVisibleRows); renderLess(); break;
    case 'pgdn': lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + lessVisibleRows); renderLess(); break;
    case 'home': lessScroll = 0; renderLess(); break;
    case 'end': lessScroll = Math.max(0, lessTotalLines - lessVisibleRows); renderLess(); break;
    case 'wheel': break;
    case 'quit': hideLess(); addPrompt(); break;
  }
});

lessInner.addEventListener('click', function(e) {
  lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + Math.floor(lessVisibleRows / 2));
  renderLess();
});

/* Keyboard in less */
document.addEventListener('keydown', function(e) {
  if(!lessEl.classList.contains('show')) return;
  switch(e.key) {
    case 'j': case 'ArrowDown': e.preventDefault(); lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + 1); renderLess(); break;
    case 'k': case 'ArrowUp': e.preventDefault(); lessScroll = Math.max(0, lessScroll - 1); renderLess(); break;
    case ' ': case 'PageDown': e.preventDefault(); lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + lessVisibleRows); renderLess(); break;
    case 'PageUp': e.preventDefault(); lessScroll = Math.max(0, lessScroll - lessVisibleRows); renderLess(); break;
    case 'Enter': e.preventDefault(); lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + Math.floor(lessVisibleRows / 2)); renderLess(); break;
    case 'g': e.preventDefault(); lessScroll = 0; renderLess(); break;
    case 'G': e.preventDefault(); lessScroll = Math.max(0, lessTotalLines - lessVisibleRows); renderLess(); break;
    case 'q': case 'Escape': e.preventDefault(); hideLess(); addPrompt(); break;
    case 'Home': e.preventDefault(); lessScroll = 0; renderLess(); break;
    case 'End': e.preventDefault(); lessScroll = Math.max(0, lessTotalLines - lessVisibleRows); renderLess(); break;
  }
});

/* Mouse wheel in less */
lessEl.addEventListener('wheel', function(e) {
  if(!lessEl.classList.contains('show')) return;
  e.preventDefault();
  if(e.deltaY < 0) lessScroll = Math.max(0, lessScroll - 3);
  else lessScroll = Math.min(lessTotalLines - lessVisibleRows, lessScroll + 3);
  renderLess();
});

/* ── Command execution ───────────────────── */
function execute(line) {
  line = line.trim();
  if(!line) { addPrompt(); return; }
  hist.push(line);
  histIdx = -1;
  const args = tokenize(line);
  const cmd = args[0];
  if(commands[cmd]) {
    commands[cmd](args);
  } else {
    addLine('Unknown command: ' + cmd, 'err');
  }
  if(!lessEl.classList.contains('show')) addPrompt();
}

/* ── Mouse cursor ─────────────────────────── */
document.addEventListener('mousemove', function(e) {
  cursorEl.style.display = 'block';
  cursorEl.style.left = (e.clientX) + 'px';
  cursorEl.style.top = (e.clientY) + 'px';
});
document.addEventListener('mouseleave', function() {
  cursorEl.style.display = 'none';
});

/* ── Debug panel update ───────────────────── */
const startTime = Date.now();
function updateDebug() {
  document.getElementById('dcpu').textContent = 'x86_64 @ ' + Math.floor(Math.random()*400+2800) + 'MHz';
  document.getElementById('dram').textContent = Math.floor(Math.random()*200+200) + '/' + 512 + 'MB';
  document.getElementById('dnet').textContent = Math.random() > 0.3 ? 'UP' : 'DOWN';
  document.getElementById('dtasks').textContent = Math.floor(Math.random()*8+4);
}

/* ── Init ──────────────────────────────────── */
addLine('', 'info', 'PrismOS v1.0 — type <span class=hl>help</span> for commands');
addPrompt();
termEl.focus();
updateDebug();
setInterval(updateDebug, 2000);

/* Handle resize for less */
window.addEventListener('resize', function() {
  if(lessEl.classList.contains('show')) renderLess();
});
</script>
</body>
</html>
