<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{% block title %}CoderAI{% endblock %}</title>
  <link rel="icon" type="image/x-icon" href="{{ root_path }}/favicon.ico">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="{{ root_path }}/static/admin/style.css">
  <script>const ROOT_PATH = "{{ root_path }}";</script>
  <style>
    /* Pulsing dot shown on a model's engine tag while it is serving a request. */
    @keyframes craiPulse { 0%,100% { opacity: 1 } 50% { opacity: .2 } }
    .crai-run-dot { color: #3fb950; animation: craiPulse 1s ease-in-out infinite;
                    margin-right: .25rem; font-size: 1.1em; line-height: 0 }
  </style>
  <script>
  // ── Batched page bootstrap ────────────────────────────────────────────────
  // A page's first paint often needs ~10 /admin/api reads. Fired as separate
  // fetches they hit the browser's ~6-connections-per-host cap and, during a
  // generation (engine busy), pile up and freeze the page. bootstrapPage() asks
  // the front to fetch them all CONCURRENTLY server-side and return one response;
  // pageFetch() then transparently serves each from that bundle (once), falling
  // back to a normal fetch on miss/expiry. Render code keeps using pageFetch like
  // fetch — no other change needed.
  const _bootCache = new Map();
  async function bootstrapPage(paths){
    try{
      const r = await fetch(ROOT_PATH + '/admin/api/batch', {
        method:'POST', headers:{'Content-Type':'application/json'},
        body: JSON.stringify({paths})});
      if(!r.ok) return;
      const d = await r.json();
      const now = Date.now();
      for(const [p, res] of Object.entries(d.results||{})){
        _bootCache.set(p, {res, at: now});
      }
    }catch{}
  }
  // Drop-in replacement for fetch(ROOT_PATH + path) on GET reads: returns the
  // batched result if present (consumed once, valid ~8s), else a real fetch.
  function pageFetch(path){
    const hit = _bootCache.get(path);
    if(hit && (Date.now() - hit.at) < 8000){
      _bootCache.delete(path);
      const res = hit.res || {};
      const status = res.status || 200;
      const body = ('json' in res) ? JSON.stringify(res.json) : (res.text || '');
      return Promise.resolve(new Response(body, {status,
        headers:{'Content-Type': ('json' in res) ? 'application/json' : 'text/plain'}}));
    }
    return fetch(ROOT_PATH + path);
  }
  </script>
  {% block head %}{% endblock %}
</head>
<body>

{% if username %}
<nav class="topnav">
  <div class="topnav-inner">
    <div class="topnav-left">
      <a href="{{ root_path }}/admin" class="nav-logo">
        <div class="nav-logo-mark">AI</div>
        <span class="nav-logo-name">CoderAI</span>
        {% if coderai_version %}<span class="nav-logo-version" title="CoderAI version">v{{ coderai_version }}</span>{% endif %}
      </a>
      <div class="nav-links">
        <a href="{{ root_path }}/admin" class="nav-link {% if request.url.path == '/admin' %}active{% endif %}">Overview</a>
        <a href="{{ root_path }}/chat" class="nav-link {% if request.url.path == '/chat' %}active{% endif %}">Studio</a>
        <a href="{{ root_path }}/docs" class="nav-link" target="_blank">API Docs</a>
        {% if is_admin|default(false) %}
        <a href="{{ root_path }}/admin/models" class="nav-link {% if '/models' in request.url.path %}active{% endif %}">Models</a>
        <a href="{{ root_path }}/admin/tasks" class="nav-link {% if '/tasks' in request.url.path %}active{% endif %}">Tasks</a>
        <a href="{{ root_path }}/admin/tokens" class="nav-link {% if '/tokens' in request.url.path %}active{% endif %}">Tokens</a>
        <a href="{{ root_path }}/admin/users" class="nav-link {% if '/users' in request.url.path %}active{% endif %}">Users</a>
        <a href="{{ root_path }}/admin/archive" class="nav-link {% if '/archive' in request.url.path %}active{% endif %}">Archive</a>
        <a href="{{ root_path }}/admin/settings" class="nav-link {% if '/settings' in request.url.path %}active{% endif %}">Settings</a>
        {% endif %}
        <button class="nav-link nav-donate-btn" onclick="document.getElementById('donateModal').classList.add('show')">&#9829; Donate</button>
      </div>
    </div>
    <div class="topnav-right">
      <span class="nav-username">{{ username }}</span>
      <div class="nav-sep"></div>
      <a href="{{ root_path }}/logout" class="nav-logout">Sign out</a>
    </div>
  </div>
</nav>

<div class="modal" id="donateModal" onclick="if(event.target===this)this.classList.remove('show')">
  <div class="modal-box donate-modal-box">
    <div class="modal-head">
      <span class="modal-title">Support CoderAI</span>
      <button class="modal-close" onclick="document.getElementById('donateModal').classList.remove('show')">&times;</button>
    </div>
    <div class="modal-body">
      <p class="donate-tagline">CoderAI is free and open source. If you find it useful, consider supporting its development — every contribution helps.</p>
      <div class="donate-coins">
        <div class="donate-coin">
          <span class="donate-coin-label">Bitcoin &mdash; BTC</span>
          <img class="donate-qr" src="https://api.qrserver.com/v1/create-qr-code/?size=160x160&color=DDE1F0&bgcolor=161820&qzone=2&data=bitcoin:bc1qcpt2uutqkz4456j5r78rjm3gwq03h5fpwmcc5u" alt="BTC QR code" width="160" height="160">
          <div class="donate-addr-row">
            <code class="donate-addr" id="btcAddr">bc1qcpt2uutqkz4456j5r78rjm3gwq03h5fpwmcc5u</code>
            <button class="donate-copy" onclick="donateCopy('btcAddr',this)">Copy</button>
          </div>
        </div>
        <div class="donate-coin">
          <span class="donate-coin-label">Ethereum &mdash; ETH</span>
          <img class="donate-qr" src="https://api.qrserver.com/v1/create-qr-code/?size=160x160&color=DDE1F0&bgcolor=161820&qzone=2&data=ethereum:0xdA6dAb526515b5cb556d20269207D43fcc760E51" alt="ETH QR code" width="160" height="160">
          <div class="donate-addr-row">
            <code class="donate-addr" id="ethAddr">0xdA6dAb526515b5cb556d20269207D43fcc760E51</code>
            <button class="donate-copy" onclick="donateCopy('ethAddr',this)">Copy</button>
          </div>
        </div>
        <div class="donate-coin">
          <span class="donate-coin-label">PayPal</span>
          <img class="donate-qr" src="https://api.qrserver.com/v1/create-qr-code/?size=160x160&color=DDE1F0&bgcolor=161820&qzone=2&data=https://paypal.me/nexlab" alt="PayPal QR code" width="160" height="160">
          <div class="donate-addr-row">
            <a class="donate-addr donate-paypal-link" id="ppAddr" href="https://paypal.me/nexlab" target="_blank" rel="noopener">paypal.me/nexlab</a>
            <a class="donate-copy" href="https://paypal.me/nexlab" target="_blank" rel="noopener">Open</a>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
<script>
function donateCopy(id, btn) {
  var el = document.getElementById(id);
  var text = el.href || el.textContent;
  navigator.clipboard.writeText(text.trim()).then(function() {
    var orig = btn.innerHTML;
    btn.innerHTML = '&#10003;';
    setTimeout(function(){ btn.innerHTML = orig; }, 1500);
  });
}
</script>
<main class="main">
{% endif %}

<div class="{% block wrapper_class %}container{% endblock %}">
  {% block content %}{% endblock %}
</div>

{% if username %}
</main>
{% endif %}

<!-- Shared confirm / notice modal (replaces window.confirm / window.alert) -->
<div id="confirm-modal" class="modal" onclick="if(event.target===this)document.getElementById('confirm-modal-cancel').click()">
  <div class="modal-box" style="max-width:420px">
    <div class="modal-head">
      <span class="modal-title" id="confirm-modal-title">Confirm</span>
      <button class="modal-close" id="confirm-modal-x">&times;</button>
    </div>
    <div class="modal-body">
      <p id="confirm-modal-msg" style="margin:0 0 1.25rem;white-space:pre-wrap"></p>
      <div style="display:flex;gap:.5rem;justify-content:flex-end">
        <button class="btn btn-ghost" id="confirm-modal-cancel">Cancel</button>
        <button class="btn btn-danger" id="confirm-modal-ok">Confirm</button>
      </div>
    </div>
  </div>
</div>
<script>
// Global modal helpers, shared by every admin page. Defined here so templates
// can call showAlert()/showConfirm() instead of window.alert()/window.confirm().
if(typeof window.openModal!=='function') window.openModal=function(id){document.getElementById(id).classList.add('show')};
if(typeof window.closeModal!=='function') window.closeModal=function(id){document.getElementById(id).classList.remove('show')};

window.showConfirm=function(title, msg, okLabel){
  return new Promise(resolve => {
    document.getElementById('confirm-modal-title').textContent = title;
    document.getElementById('confirm-modal-msg').textContent = msg;
    const okBtn    = document.getElementById('confirm-modal-ok');
    const cancelBtn= document.getElementById('confirm-modal-cancel');
    const xBtn     = document.getElementById('confirm-modal-x');
    okBtn.className = 'btn btn-danger';
    okBtn.textContent = okLabel || 'Confirm';
    cancelBtn.style.display = '';
    openModal('confirm-modal');
    function cleanup(result){
      closeModal('confirm-modal');
      okBtn.removeEventListener('click', onOk);
      cancelBtn.removeEventListener('click', onCancel);
      xBtn.removeEventListener('click', onCancel);
      resolve(result);
    }
    function onOk(){ cleanup(true); }
    function onCancel(){ cleanup(false); }
    okBtn.addEventListener('click', onOk);
    cancelBtn.addEventListener('click', onCancel);
    xBtn.addEventListener('click', onCancel);
  });
};

// Styled replacement for window.alert(): a single-button notice modal.
window.showAlert=function(msg, title, kind){
  return new Promise(resolve => {
    if(!title && !kind && /^\s*(error|failed|cannot|could not)\b/i.test(String(msg||''))) kind = 'error';
    document.getElementById('confirm-modal-title').textContent =
      title || (kind === 'error' ? 'Error' : 'Notice');
    document.getElementById('confirm-modal-msg').textContent = msg;
    const okBtn     = document.getElementById('confirm-modal-ok');
    const cancelBtn = document.getElementById('confirm-modal-cancel');
    const xBtn      = document.getElementById('confirm-modal-x');
    okBtn.className = 'btn btn-primary';
    okBtn.textContent = 'OK';
    cancelBtn.style.display = 'none';
    openModal('confirm-modal');
    function cleanup(){
      closeModal('confirm-modal');
      cancelBtn.style.display = '';
      okBtn.removeEventListener('click', onOk);
      xBtn.removeEventListener('click', onOk);
      resolve();
    }
    function onOk(){ cleanup(); }
    okBtn.addEventListener('click', onOk);
    xBtn.addEventListener('click', onOk);
  });
};
</script>
{% block scripts %}{% endblock %}
</body>
</html>
