Agent Template

Creador de proyectos HTML, PHP y SQLite

Genera una base de proyecto web con interfaz HTML, API PHP y esquema SQLite a partir de una descripción en español. Permite revisar y descargar los archivos generados.

HTML PHP SQLite generador de código aplicaciones web
luan920530 Oct 25, 2026 1 use

Preview Mode

This is a preview with sample data. The template uses placeholders like which will be replaced with actual agent data.

About This Template

Creador de proyectos HTML, PHP y SQLite is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.

Topics HTML PHP SQLite generador de código aplicaciones web
Template Preview

Template Metadata

Slug
creador-de-proyectos-html-php-y-sqlite
Created By
luan920530
Created
Oct 25, 2026
Usage Count
1

Tags

HTML PHP SQLite generador de código aplicaciones web

Code Statistics

HTML Lines
52
CSS Lines
1
JS Lines
124
Python Lines
44

Source Code

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ agent_name }}</title>
  <style>{{ css_code }}</style>
</head>
<body>
  <div class="app-shell">
    <header class="masthead">
      <span class="eyebrow">TALLER DE APLICACIONES WEB</span>
      <h1>🧩 {{ agent_name }}</h1>
      <p>De una idea a los archivos iniciales de una aplicación HTML, PHP y SQLite.</p>
    </header>

    <main class="workspace">
      <section class="panel brief-panel" aria-labelledby="brief-title">
        <div class="panel-heading"><span class="step">01</span><div><h2 id="brief-title">Define tu proyecto</h2><p>Explica qué debe hacer y qué datos necesita guardar.</p></div></div>
        <form id="project-form" novalidate>
          <label for="project-name">Nombre de la aplicación</label>
          <input id="project-name" type="text" maxlength="80" placeholder="Por ejemplo: Biblioteca del barrio" required>
          <label for="project-brief">¿Qué hará la aplicación?</label>
          <textarea id="project-brief" rows="7" maxlength="4500" placeholder="Por ejemplo: registrar libros, buscar por título y eliminar entradas..." required></textarea>
          <label for="data-fields">Datos que quieres guardar <span class="optional">(opcional)</span></label>
          <textarea id="data-fields" rows="3" maxlength="1200" placeholder="Por ejemplo: título, autor, año de publicación"></textarea>
          <div class="form-actions"><button id="generate-button" type="submit">✨ Crear archivos del proyecto</button><button id="reset-button" type="button" class="secondary">Limpiar proyecto</button></div>
          <p id="status" class="status" role="status" aria-live="polite">Inicializando el taller…</p>
          <p id="error" class="error hidden" role="alert"></p>
        </form>
      </section>

      <section class="panel output-panel" aria-labelledby="output-title">
        <div class="panel-heading"><span class="step">02</span><div><h2 id="output-title">Archivos del proyecto</h2><p>Inspecciona el código y descarga el resultado.</p></div></div>
        <div id="empty-state" class="empty-state">
          <div class="empty-symbol">📂</div><h3>Tu proyecto empieza aquí</h3>
          <p>Completa la descripción para generar una base que incluya:</p>
          <ul><li>Una interfaz HTML para tus usuarios.</li><li>Un endpoint PHP para las operaciones.</li><li>Un esquema SQL compatible con SQLite.</li><li>Indicaciones para ejecutarlo localmente.</li></ul>
        </div>
        <div id="output-content" class="hidden">
          <div class="file-toolbar"><label for="file-select">Archivo para revisar</label><select id="file-select" aria-label="Archivo del proyecto para revisar"><option value="full">Resultado completo</option></select></div>
          <pre id="code-view" class="code-view" tabindex="0"></pre>
          <div class="download-actions"><button id="download-file" type="button" class="secondary">⬇ Descargar archivo seleccionado</button><button id="download-all" type="button">⬇ Descargar proyecto completo</button></div>
          <p class="hint">El proyecto completo se descarga como texto. Revisa el código antes de usarlo y sirve PHP desde un servidor; abrir el HTML como archivo local no ejecuta PHP.</p>
        </div>
      </section>
    </main>
  </div>
  <div id="loading-overlay" class="loading-overlay hidden" role="status" aria-live="polite"><div class="loading-card"><span class="spinner" aria-hidden="true"></span><strong>Construyendo tu proyecto…</strong><span>Preparando HTML, PHP y SQLite.</span></div></div>
  <script>{{ js_code }}</script>
</body>
</html>
:root{--bg:#f3f6f5;--paper:#fff;--ink:#18312c;--muted:#637771;--line:#dce5e0;--accent:#146e5a;--accent-hover:#0e5545;--soft:#e7f4ed;--error:#a02d35;--code:#122921;--radius:18px;--shadow:0 14px 40px rgba(20,55,43,.08);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;color:var(--ink)}*{box-sizing:border-box}body{margin:0;background:var(--bg);min-height:100vh}.hidden{display:none!important}.app-shell{max-width:1500px;margin:0 auto;padding:38px 34px 60px}.masthead{padding:12px 2px 28px}.eyebrow{font-size:.76rem;letter-spacing:.17em;font-weight:800;color:var(--accent)}h1{font-size:clamp(2rem,3vw,3.1rem);letter-spacing:-.04em;margin:10px 0}h2,h3,p{margin-top:0}.masthead p,.panel-heading p,.hint{color:var(--muted)}.workspace{display:grid;grid-template-columns:minmax(330px,.9fr) minmax(440px,1.1fr);gap:22px;align-items:start}.panel{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:27px;min-width:0}.panel-heading{display:flex;gap:14px;margin-bottom:22px}.panel-heading h2{margin:0 0 5px;font-size:1.24rem}.panel-heading p{margin:0;font-size:.9rem}.step{background:var(--soft);color:var(--accent);border-radius:10px;padding:8px;font-size:.78rem;font-weight:800;height:max-content}label{display:block;font-weight:700;font-size:.88rem;margin:17px 0 8px}.optional{font-weight:400;color:var(--muted)}input,textarea,select{width:100%;border:1px solid #cbd9d2;border-radius:11px;padding:12px 14px;background:white;color:var(--ink);font:inherit}textarea{resize:vertical;line-height:1.5}input::placeholder,textarea::placeholder{color:#84948d}button{font:inherit;font-weight:700;border-radius:11px;border:1px solid var(--accent);padding:12px 16px;background:var(--accent);color:white;cursor:pointer;transition:background .15s,transform .15s}button:hover:not(:disabled){background:var(--accent-hover)}button:active:not(:disabled){transform:translateY(1px)}button.secondary{background:#fff;color:var(--accent)}button.secondary:hover:not(:disabled){background:var(--soft)}button:disabled{opacity:.5;cursor:not-allowed}button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,.code-view:focus-visible{outline:3px solid #70c7a8;outline-offset:3px}.form-actions,.download-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}.status{font-size:.85rem;color:var(--muted);margin:16px 0 0}.error{color:var(--error);background:#fff0f0;border:1px solid #efc6c6;border-radius:10px;padding:10px 12px;margin:14px 0 0;font-size:.9rem}.empty-state{border:1px dashed #b4cfc0;border-radius:14px;background:#f7fbf8;padding:24px}.empty-symbol{font-size:2rem;margin-bottom:10px}.empty-state h3{margin-bottom:6px}.empty-state p{color:var(--muted)}.empty-state ul{padding-left:21px;margin:12px 0 0;line-height:1.9}.file-toolbar{display:flex;align-items:center;gap:13px;flex-wrap:wrap;margin-bottom:12px}.file-toolbar label{margin:0;white-space:nowrap}.file-toolbar select{max-width:290px}.code-view{margin:0;background:var(--code);color:#dff5e7;border-radius:12px;padding:20px;line-height:1.55;min-height:360px;max-height:650px;overflow:auto;white-space:pre;font-size:.82rem;tab-size:2}.hint{font-size:.82rem;line-height:1.5;margin:14px 0 0}.loading-overlay{position:fixed;inset:0;background:rgba(14,35,29,.54);display:grid;place-items:center;z-index:20;padding:20px}.loading-card{background:white;border-radius:18px;padding:32px;display:flex;flex-direction:column;align-items:center;gap:11px;text-align:center;box-shadow:var(--shadow)}.loading-card span:last-child{color:var(--muted)}.spinner{width:36px;height:36px;border:4px solid #d8eae0;border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1200px){.workspace{grid-template-columns:1fr 1fr}.app-shell{padding:28px 22px}}@media(max-width:768px){.workspace{grid-template-columns:1fr}.app-shell{padding:18px 14px 40px}.panel{padding:20px}.code-view{min-height:280px;max-height:450px}.download-actions button,.form-actions button{flex:1 1 100%}}
(() => {
  'use strict';
  const el = id => document.getElementById(id);
  const form = el('project-form');
  const generate = el('generate-button');
  const reset = el('reset-button');
  const status = el('status');
  const error = el('error');
  const overlay = el('loading-overlay');
  const empty = el('empty-state');
  const output = el('output-content');
  const select = el('file-select');
  const code = el('code-view');
  const downloadFile = el('download-file');
  const downloadAll = el('download-all');
  let ready = false;
  let busy = false;
  let fullResult = '';
  let files = {};
  const escapeHtml = value => String(value).replace(/[&<>"']/g, ch => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[ch]));
  function showError(message) { error.innerHTML = escapeHtml(message); error.classList.remove('hidden'); }
  function clearError() { error.textContent = ''; error.classList.add('hidden'); }
  function localProvider() { return /local|wllama|browser/.test(String(window.PROVIDER || '').toLowerCase()); }
  function modelLoaded() { return !localProvider() || !!(window.agentManager && window.agentManager.isLoaded); }
  function refreshStatus() {
    generate.disabled = busy || !ready || !modelLoaded();
    reset.disabled = busy;
    select.disabled = busy || !fullResult;
    downloadFile.disabled = busy || !fullResult;
    downloadAll.disabled = busy || !fullResult;
    status.textContent = busy ? 'Generando los archivos del proyecto…' : !ready ? 'Inicializando Python en el navegador…' : !modelLoaded() ? 'Carga un modelo desde la barra superior para crear el proyecto.' : 'Listo para crear tu proyecto HTML, PHP y SQLite.';
  }
  function stripFence(text) {
    return text.trim().replace(/^```[^\n]*\n/, '').replace(/\n?```\s*$/, '').trim();
  }
  function parseFiles(text) {
    const parsed = {};
    const marker = /^[ \t]*(?:#{1,3}[ \t]*)?===[ \t]*(index\.html|app\.php|schema\.sql)[ \t]*===[ \t]*$/gim;
    const found = [...text.matchAll(marker)];
    found.forEach((match, index) => {
      const end = index + 1 < found.length ? found[index + 1].index : text.length;
      const body = stripFence(text.slice(match.index + match[0].length, end));
      if (body) parsed[match[1].toLowerCase()] = body;
    });
    return parsed;
  }
  function render() {
    empty.classList.toggle('hidden', !!fullResult);
    output.classList.toggle('hidden', !fullResult);
    const chosen = select.value;
    code.textContent = chosen === 'full' ? fullResult : (files[chosen] || fullResult);
    downloadFile.textContent = chosen === 'full' ? '⬇ Descargar resultado completo' : '⬇ Descargar ' + chosen;
    refreshStatus();
  }
  function save(filename, text, mime) {
    const url = URL.createObjectURL(new Blob([text], {type:mime}));
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    link.remove();
    setTimeout(() => URL.revokeObjectURL(url), 30000);
  }
  function setupGuide() {
    return '\n\nGUÍA DE EJECUCIÓN\n1. Guarda index.html, app.php y schema.sql en una carpeta pública servida por PHP.\n2. Activa PDO_SQLITE y SQLITE3 en PHP.\n3. Ejecuta php -S localhost:8000 desde esa carpeta y abre http://localhost:8000/index.html.\n4. El PHP debe crear la base SQLite fuera del directorio público y aplicar schema.sql (o un esquema equivalente) antes del primer uso. Revisa la ruta y los permisos según tu alojamiento.\n5. Revisa y prueba el código generado antes de publicarlo. No abras index.html con file:// para probar las peticiones a PHP.\n';
  }
  form.addEventListener('submit', async event => {
    event.preventDefault();
    clearError();
    const name = el('project-name').value.trim();
    const brief = el('project-brief').value.trim();
    const fields = el('data-fields').value.trim();
    if (!name || !brief) { showError('Escribe el nombre y la descripción de la aplicación.'); return; }
    if (!ready || !window.pyodide) { showError('El entorno Python aún se está iniciando. Inténtalo de nuevo en un momento.'); return; }
    if (!modelLoaded()) { showError('Carga un modelo desde la barra superior antes de crear el proyecto.'); return; }
    busy = true;
    overlay.classList.remove('hidden');
    refreshStatus();
    try {
      const request = JSON.stringify({name, brief, fields});
      window.pyodide.globals.set('user_query', request);
      const response = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
      fullResult = String(response ?? '').trim();
      if (!fullResult) throw new Error('El generador no devolvió archivos. Prueba con una descripción más concreta.');
      files = parseFiles(fullResult);
      select.replaceChildren();
      select.add(new Option('Resultado completo', 'full'));
      for (const filename of ['index.html', 'app.php', 'schema.sql']) {
        if (files[filename]) select.add(new Option(filename, filename));
      }
      if (Object.keys(files).length < 3) showError('No se pudieron identificar los tres archivos por separado. Puedes revisar y descargar el resultado completo.');
      render();
    } catch (cause) {
      showError('No se pudo crear el proyecto: ' + (cause && cause.message ? cause.message : String(cause)));
    } finally {
      busy = false;
      overlay.classList.add('hidden');
      refreshStatus();
    }
  });
  reset.addEventListener('click', () => {
    form.reset();
    fullResult = '';
    files = {};
    select.replaceChildren(new Option('Resultado completo', 'full'));
    clearError();
    render();
    el('project-name').focus();
  });
  select.addEventListener('change', render);
  downloadFile.addEventListener('click', () => {
    if (!fullResult) return;
    const selected = select.value;
    if (selected === 'full') save('proyecto-completo.txt', fullResult + setupGuide(), 'text/plain;charset=utf-8');
    else if (files[selected]) save(selected, files[selected] + '\n', selected.endsWith('.html') ? 'text/html;charset=utf-8' : 'text/plain;charset=utf-8');
  });
  downloadAll.addEventListener('click', () => {
    if (fullResult) save('proyecto-completo.txt', fullResult + setupGuide(), 'text/plain;charset=utf-8');
  });
  document.addEventListener('pyodide-ready', () => { ready = true; refreshStatus(); });
  if (window.pyodideReady) ready = true;
  refreshStatus();
})();
import json


async def process_user_query(query: str) -> str:
    """Generate HTML, PHP, and SQLite project files from a project brief.

    Args:
        query: JSON containing the project name, brief, and optional data fields.

    Returns:
        The generated files, separated by filename markers.
    """
    try:
        data = json.loads(query)
    except (ValueError, TypeError):
        return "No se pudo interpretar el proyecto. Completa el formulario e inténtalo de nuevo."
    if not isinstance(data, dict):
        return "Los datos del proyecto no tienen el formato esperado."
    name = str(data.get("name", "")).strip()[:80]
    brief = str(data.get("brief", "")).strip()[:4500]
    fields = str(data.get("fields", "")).strip()[:1200]
    if not name or not brief:
        return "Escribe un nombre y una descripción para crear el proyecto."
    prompt = (
        "Crea una aplicación web inicial PEQUEÑA y funcional, en español.\n"
        f"Nombre: {name}\nObjetivo: {brief}\nCampos deseados: {fields or 'elige los mínimos necesarios'}\n\n"
        "Devuelve EXACTAMENTE tres secciones con estos encabezados en líneas independientes:\n"
        "=== index.html ===\n```html\n[código completo]\n```\n"
        "=== app.php ===\n```php\n[código completo]\n```\n"
        "=== schema.sql ===\n```sql\n[código completo]\n```\n"
        "Sin explicaciones fuera de las tres secciones. No omitas código ni uses puntos suspensivos. "
        "index.html debe llamar con fetch('app.php') a una API JSON, mostrar listas y errores, "
        "y escapar el texto de usuarios al insertarlo en HTML (o usar textContent). "
        "app.php debe usar PDO_SQLITE y sentencias preparadas para datos del usuario, implementar "
        "como mínimo crear y listar registros, validar entradas y devolver JSON y códigos HTTP. "
        "Debe crear automáticamente una base SQLite en una carpeta privada fuera del directorio público "
        "(por ejemplo dirname(__DIR__) . '/private'), crear la carpeta si falta, y ejecutar al inicio "
        "un CREATE TABLE IF NOT EXISTS equivalente al de schema.sql; admitir SQLITE_PATH como ruta "
        "alternativa mediante getenv. No concatenes valores de usuario en SQL. "
        "El HTML y PHP deben concordar en nombres de campos, rutas y formato de respuesta. "
        "No dependas de frameworks, CDNs ni archivos adicionales. Mantén el proyecto breve y ejecutable "
        "con php -S localhost:8000 desde la carpeta pública, y con extensiones PDO_SQLITE habilitadas."
    )
    return str(await agentop_llm.generate(prompt[:7400], globals().get("TEMPLATE_SYSTEM_PROMPT", "")))