AI-Debate-Arena / index.html
fivetech's picture
Update index.html
fa96ab0 verified
Raw History Blame Contribute Delete
22.8 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>⚔️</text></svg>">
<title>AI Debate Arena — Multi-Agent Intelligence Competition</title>
<style>
:root{
--bg:#0a0e1a; --bg2:#0f1526; --panel:#141b31; --panel2:#1a2340;
--border:#263156; --text:#e8ecf8; --muted:#8b94b8;
--accent:#f43f5e; --accent2:#8b5cf6; --gold:#fbbf24; --ok:#34d399; --warn:#f59e0b;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{background:radial-gradient(1200px 600px at 50% -10%,#1b2242 0%,var(--bg) 60%);color:var(--text);font-family:'Segoe UI',system-ui,'Apple Color Emoji','Segoe UI Emoji','Noto Color Emoji',sans-serif;min-height:100vh;display:flex;flex-direction:column}
header{display:flex;align-items:center;gap:14px;padding:12px 20px;border-bottom:1px solid var(--border);background:rgba(10,14,26,.85);backdrop-filter:blur(8px);position:sticky;top:0;z-index:50;flex-wrap:wrap}
.logo{font-size:1.25rem;font-weight:900;letter-spacing:.06em;background:linear-gradient(90deg,var(--accent),var(--accent2));-webkit-background-clip:text;background-clip:text;color:transparent;white-space:cursor:default}
.logo small{display:block;font-size:.6rem;letter-spacing:.3em;color:var(--muted);-webkit-text-fill-color:var(--muted);font-weight:600}
nav{display:flex;gap:6px;flex-wrap:wrap}
nav button{background:transparent;border:1px solid transparent;color:var(--muted);padding:7px 13px;border-radius:999px;cursor:pointer;font-size:.85rem;font-weight:600}
nav button:hover{color:var(--text)}
nav button.active{background:var(--panel2);border-color:var(--border);color:var(--text)}
#model-pill{margin-left:auto;font-size:.75rem;color:var(--muted);border:1px solid var(--border);border-radius:999px;padding:5px 12px;display:flex;align-items:center;gap:7px}
#model-pill .dot{width:8px;height:8px;border-radius:50%;background:var(--muted)}
#model-pill.ready .dot{background:var(--ok)}
#model-pill.loading .dot{background:var(--warn);animation:pulse 1s infinite}
@keyframes pulse{50%{opacity:.3}}
main{flex:1;width:100%;max-width:1200px;margin:0 auto;padding:22px 16px 60px}
.view{display:none;animation:fadein .3s}
.view.active{display:block}
@keyframes fadein{from{opacity:0;transform:translateY(6px)}to{opacity:1}}
h2.section{font-size:1.05rem;letter-spacing:.12em;color:var(--muted);text-transform:uppercase;margin:26px 0 12px;font-weight:700}
.btn{background:linear-gradient(90deg,var(--accent),var(--accent2));border:none;color:#fff;padding:13px 30px;border-radius:12px;font-size:1rem;font-weight:800;letter-spacing:.08em;cursor:pointer;transition:transform .15s,box-shadow .15s}
.btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 24px rgba(244,63,94,.35)}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn.ghost{background:var(--panel2);border:1px solid var(--border);font-weight:600;letter-spacing:0;padding:10px 18px}
.btn.ghost:hover:not(:disabled){box-shadow:none;border-color:var(--accent2)}
.btn.small{padding:7px 13px;font-size:.8rem;border-radius:9px}
input[type=text],textarea,select{background:var(--bg2);border:1px solid var(--border);color:var(--text);border-radius:10px;padding:11px 14px;font-size:.95rem;width:100%;outline:none;font-family:inherit}
input[type=text]:focus,textarea:focus,select:focus{border-color:var(--accent2)}
label{display:block;font-size:.78rem;color:var(--muted);margin:10px 0 5px;font-weight:600;letter-spacing:.04em}
/* LOBBY */
.hero{text-align:center;padding:26px 0 8px}
.hero h1{font-size:clamp(1.8rem,5vw,3rem);font-weight:900;letter-spacing:.04em}
.hero h1 span{background:linear-gradient(90deg,var(--accent),var(--gold),var(--accent2));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero p{color:var(--muted);letter-spacing:.28em;font-size:.72rem;margin-top:8px;text-transform:uppercase}
.modes{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-top:10px}
.mode-card{background:var(--panel);border:2px solid var(--border);border-radius:14px;padding:16px;cursor:pointer;transition:border-color .15s,transform .15s;position:relative}
.mode-card:hover{transform:translateY(-2px)}
.mode-card.sel{border-color:var(--accent)}
.mode-card.soon{opacity:.55;cursor:not-allowed}
.mode-card h3{font-size:1rem;margin-bottom:4px;padding-right:96px}
.mode-card p{font-size:.78rem;color:var(--muted);line-height:1.4}
.badge{position:absolute;top:10px;right:10px;font-size:.62rem;font-weight:800;letter-spacing:.08em;padding:3px 8px;border-radius:999px;background:var(--panel2);border:1px solid var(--border);color:var(--muted)}
.badge.live{background:rgba(52,211,153,.15);border-color:var(--ok);color:var(--ok)}
.badge.dev{background:rgba(245,158,11,.12);border-color:var(--warn);color:var(--warn)}
.lobby-grid{display:grid;grid-template-columns:1.4fr 1fr;gap:18px;margin-top:18px}
@media(max-width:820px){.lobby-grid{grid-template-columns:1fr}}
.panel{background:var(--panel);border:1px solid var(--border);border-radius:16px;padding:18px}
.topic-row{display:flex;gap:8px}
.topic-row input{flex:1}
.topic-row select{width:190px;flex:none}
.pick-line{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:12px}
.chip{display:inline-flex;align-items:center;gap:6px;background:var(--panel2);border:1px solid var(--border);border-radius:999px;padding:5px 11px;font-size:.8rem}
.chip b{font-weight:700}
.progress-wrap{height:8px;background:var(--bg2);border-radius:99px;overflow:hidden;margin-top:12px;border:1px solid var(--border)}
.progress-wrap>div{height:100%;width:0;background:linear-gradient(90deg,var(--accent),var(--accent2));transition:width .3s}
#load-status{font-size:.78rem;color:var(--muted);margin-top:8px;min-height:1.1em}
/* ROSTER */
.roster{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px}
.agent-card{background:var(--panel);border:2px solid var(--border);border-radius:16px;padding:16px;cursor:pointer;transition:border-color .15s,transform .15s;position:relative;overflow:hidden}
.agent-card::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:var(--ac,#888)}
.agent-card:hover{transform:translateY(-3px)}
.agent-card.sel{border-color:var(--ac,#888);box-shadow:0 0 0 1px var(--ac,#888),0 8px 24px rgba(0,0,0,.35)}
.agent-card .avatar{font-size:2rem}
.agent-card h3{margin:6px 0 2px;font-size:1.02rem}
.agent-card .spec{font-size:.75rem;color:var(--ac,#888);font-weight:700;letter-spacing:.06em;text-transform:uppercase}
.agent-card .style{font-size:.78rem;color:var(--muted);margin:4px 0 10px}
.stat{display:grid;grid-template-columns:88px 1fr 26px;align-items:center;gap:8px;font-size:.7rem;color:var(--muted);margin-top:5px}
.stat .bar{height:6px;background:var(--bg2);border-radius:99px;overflow:hidden}
.stat .bar>div{height:100%;background:var(--ac,#888)}
.stat b{color:var(--text);text-align:right;font-size:.72rem}
.agent-card .tools{position:absolute;top:10px;right:10px;display:flex;gap:5px}
.icon-btn{background:var(--panel2);border:1px solid var(--border);color:var(--muted);border-radius:8px;width:28px;height:28px;cursor:pointer;font-size:.8rem;line-height:1}
.icon-btn:hover{color:var(--text);border-color:var(--accent2)}
.stats-note{font-size:.72rem;color:var(--muted);margin-top:10px;font-style:italic}
.record{font-size:.7rem;color:var(--muted);margin-top:8px;border-top:1px dashed var(--border);padding-top:7px}
/* BATTLE */
.battle-top{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.phase-track{display:flex;gap:5px;flex:1;min-width:200px;flex-wrap:wrap}
.phase-step{font-size:.66rem;font-weight:800;letter-spacing:.06em;padding:5px 10px;border-radius:999px;background:var(--panel);border:1px solid var(--border);color:var(--muted);text-transform:uppercase}
.phase-step.now{background:rgba(244,63,94,.15);border-color:var(--accent);color:var(--accent);animation:pulse 1.4s infinite}
.phase-step.done{color:var(--ok);border-color:rgba(52,211,153,.4)}
.fighters{display:grid;grid-template-columns:1fr auto 1fr;gap:12px;align-items:stretch;margin-bottom:12px}
@media(max-width:700px){.fighters{grid-template-columns:1fr}}
.fighter{background:var(--panel);border:2px solid var(--border);border-radius:16px;padding:14px;text-align:center;transition:border-color .2s,box-shadow .2s}
.fighter.turn{border-color:var(--ac,#888);box-shadow:0 0 22px -4px var(--ac,#888)}
.fighter .avatar{font-size:2.2rem}
.fighter h3{font-size:.95rem;margin:4px 0 2px}
.fighter .strat{font-size:.68rem;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}
.fighter.winner-card{border-color:var(--gold);box-shadow:0 0 26px -4px var(--gold)}
.vs{align-self:center;font-weight:900;font-size:1.4rem;color:var(--accent);text-align:center}
#battle-log{background:var(--bg2);border:1px solid var(--border);border-radius:16px;padding:16px;min-height:280px;max-height:52vh;overflow-y:auto}
.msg{margin-bottom:14px;display:flex;gap:10px}
.msg .mavatar{width:38px;height:38px;flex:none;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.15rem;background:var(--panel2);border:1px solid var(--border)}
.msg .mbody{flex:1;min-width:0}
.msg .mhead{font-size:.74rem;font-weight:800;margin-bottom:3px;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.msg .mhead .ph{color:var(--muted);font-weight:600;font-size:.66rem;text-transform:uppercase;letter-spacing:.06em}
.msg .mtext{font-size:.9rem;line-height:1.55;background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:10px 13px;white-space:pre-wrap;word-wrap:break-word}
.msg.streaming .mtext{border-color:var(--ac,#888)}
.msg.user .mtext{border-color:var(--accent2)}
.msg.system .mtext{background:transparent;border-style:dashed;color:var(--muted);text-align:center;font-size:.8rem}
.battle-controls{display:flex;gap:10px;margin-top:12px;flex-wrap:wrap;align-items:center}
.battle-controls input{flex:1;min-width:180px}
.flash{animation:flash .8s}
@keyframes flash{0%{background:rgba(244,63,94,.35)}100%{background:transparent}}
/* RESULTS */
.verdict-hero{text-align:center;padding:18px 0 6px}
.verdict-hero .crown{font-size:3rem}
.score-table{width:100%;border-collapse:collapse;margin-top:12px;font-size:.85rem}
.score-table th,.score-table td{padding:8px 10px;border-bottom:1px solid var(--border);text-align:right}
.score-table th:first-child,.score-table td:first-child{text-align:left}
.score-table th{color:var(--muted);font-size:.72rem;text-transform:uppercase;letter-spacing:.05em}
.judge-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:12px}
.judge-card{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:14px;min-width:0}
.judge-card h4{font-size:.85rem;margin-bottom:8px}
.judge-card p{font-size:.78rem;color:var(--muted);line-height:1.5;overflow-wrap:anywhere;word-break:break-word}
.judge-card .jline{display:block;margin:2px 0}
.mock-pill{display:inline-block;background:rgba(245,158,11,.12);border:1px solid #f59e0b;color:#f59e0b;border-radius:10px;padding:6px 12px;font-size:.78rem;font-weight:700}
.awards{display:flex;gap:10px;flex-wrap:wrap;margin-top:12px}
.award{background:var(--panel2);border:1px solid var(--gold);color:var(--gold);border-radius:12px;padding:9px 14px;font-size:.8rem;font-weight:700}
.note{font-size:.75rem;color:var(--muted);margin-top:14px;font-style:italic;line-height:1.5}
/* HALL */
table.hall{width:100%;border-collapse:collapse;font-size:.85rem}
table.hall th,table.hall td{padding:10px;border-bottom:1px solid var(--border);text-align:left}
table.hall th{color:var(--muted);font-size:.7rem;text-transform:uppercase;letter-spacing:.06em}
.conf{font-size:.66rem;padding:2px 8px;border-radius:99px;border:1px solid var(--border)}
.conf.low{color:var(--warn);border-color:var(--warn)}
.conf.mid{color:var(--accent2);border-color:var(--accent2)}
.conf.high{color:var(--ok);border-color:var(--ok)}
.match-item{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:12px 14px;margin-bottom:10px;display:flex;gap:12px;align-items:center;flex-wrap:wrap;cursor:pointer}
.match-item:hover{border-color:var(--accent2)}
.match-item .mtopic{flex:1;min-width:160px;font-weight:600;font-size:.9rem}
.match-item .mmeta{font-size:.72rem;color:var(--muted)}
/* ROADMAP */
.phase{background:var(--panel);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:12px;border-left:4px solid var(--muted)}
.phase.done{border-left-color:var(--ok)}
.phase.partial{border-left-color:var(--warn)}
.phase.todo{border-left-color:var(--muted)}
.phase h3{font-size:.95rem;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.phase ul{margin:10px 0 0 18px;font-size:.82rem;color:var(--muted);line-height:1.7}
.tag{font-size:.62rem;font-weight:800;padding:3px 9px;border-radius:99px;letter-spacing:.06em}
.tag.done{background:rgba(52,211,153,.15);color:var(--ok)}
.tag.partial{background:rgba(245,158,11,.15);color:var(--warn)}
.tag.todo{background:var(--panel2);color:var(--muted)}
/* MODAL */
.modal-back{position:fixed;inset:0;background:rgba(0,0,0,.65);display:none;align-items:center;justify-content:center;z-index:100;padding:16px}
.modal-back.open{display:flex}
.modal{background:var(--panel);border:1px solid var(--border);border-radius:18px;padding:22px;width:100%;max-width:480px;max-height:88vh;overflow-y:auto}
.modal h3{margin-bottom:6px}
.emoji-pick{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
.emoji-pick button{font-size:1.2rem;background:var(--bg2);border:1px solid var(--border);border-radius:9px;width:38px;height:38px;cursor:pointer}
.emoji-pick button.sel{border-color:var(--accent2)}
.color-pick{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}
.color-pick button{width:30px;height:30px;border-radius:50%;border:2px solid transparent;cursor:pointer}
.color-pick button.sel{border-color:#fff}
footer{text-align:center;color:var(--muted);font-size:.72rem;padding:18px;border-top:1px solid var(--border);line-height:1.8}
footer a{color:var(--accent2)}
</style>
</head>
<body>
<header>
<div class="logo">⚔️ AI DEBATE ARENA<small>THINK · CHALLENGE · DEFEND · WIN</small></div>
<nav>
<button data-view="lobby" class="active">Lobby</button>
<button data-view="roster">Agentes</button>
<button data-view="hall">Hall of Fame</button>
<button data-view="roadmap">Roadmap V2</button>
</nav>
<div id="model-pill"><span class="dot"></span><span id="model-pill-text">modelo sin cargar</span></div>
</header>
<main>
<!-- ============ LOBBY ============ -->
<section id="view-lobby" class="view active">
<div class="hero">
<h1>⚔️ <span>AI DEBATE ARENA</span></h1>
<p>Multi-Agent Intelligence Competition Platform</p>
</div>
<h2 class="section">Modalidad de batalla</h2>
<div class="modes" id="modes"></div>
<div class="lobby-grid">
<div class="panel">
<h2 class="section" style="margin-top:0">Configuración del combate</h2>
<label>Tema del debate</label>
<div class="topic-row">
<input type="text" id="topic" placeholder="Escribe el tema y pulsa Enter para luchar…">
<select id="topic-pick"><option value="">🎲 10 temas al azar…</option></select>
</div>
<label>Rondas de crossfire + refutación</label>
<select id="rounds"><option value="1">1 ronda</option><option value="2" selected>2 rondas</option><option value="3">3 rondas</option><option value="4">4 rondas</option></select>
<label>Modelo local (se descarga en tu navegador)</label>
<select id="model-sel">
<option value="qwen" selected>Qwen2.5 0.5B · prueba rápida (~350 MB)</option>
<option value="mimo">MiMo 9B · calidad (~5,8 GB) · ⚠️ fallo de generación observado en pruebas con wllama 3.5.1/3.8.1, en validación</option>
</select>
<div class="pick-line" id="picked-line"></div>
<div style="display:flex;gap:10px;margin-top:16px;flex-wrap:wrap">
<button class="btn" id="start-btn">▶ START BATTLE</button>
<button class="btn ghost" id="random-match">🎲 Random Match</button>
</div>
<div class="progress-wrap"><div id="load-bar"></div></div>
<div id="load-status"></div>
</div>
<div class="panel">
<h2 class="section" style="margin-top:0">Arena Control Center</h2>
<p style="font-size:.82rem;color:var(--muted);line-height:1.7">
Los agentes comparten <b style="color:var(--text)">un único modelo local</b> (wllama, CPU/WASM):
son identidades y contextos independientes, no modelos distintos.
Los jueces evalúan con contextos separados y sin ver las puntuaciones de los otros jueces.
</p>
<p style="font-size:.82rem;color:var(--muted);line-height:1.7;margin-top:10px">
Modo activo: <b style="color:var(--ok)">Sequential Arena</b> (turnos sobre el mismo runtime).
Multi-Model Arena: fase futura. Historial y estadísticas se guardan solo en este navegador.
</p>
</div>
</div>
</section>
<!-- ============ ROSTER ============ -->
<section id="view-roster" class="view">
<h2 class="section">Agent Roster — elige combatientes <span id="pick-count" style="color:var(--accent)"></span></h2>
<p style="font-size:.8rem;color:var(--muted);margin-bottom:12px">Haz clic en las fichas para seleccionar: 2 para Duel, de 3 a 8 para Free For All.</p>
<div class="roster" id="roster"></div>
<p class="stats-note">Las estadísticas de las fichas (lógica, creatividad, agresividad) son valores ilustrativos de personalidad, no capacidades medidas del modelo.</p>
<button class="btn ghost" id="add-agent" style="margin-top:14px">+ Crear agente personalizado</button>
</section>
<!-- ============ BATTLE ============ -->
<section id="view-battle" class="view">
<div class="battle-top">
<div class="phase-track" id="phase-track"></div>
<button class="btn ghost small" id="stop-btn" style="display:none">⏸ Pausar</button>
</div>
<div class="fighters" id="fighters"></div>
<div id="battle-log"></div>
<div class="battle-controls">
<input type="text" id="challenge-input" placeholder="Challenge Agent: intervén tú en la batalla… (no puntúa)">
<button class="btn ghost small" id="challenge-btn">⚡ Intervenir</button>
<button class="btn ghost small" id="exit-battle">← Salir</button>
</div>
</section>
<!-- ============ RESULTS ============ -->
<section id="view-results" class="view">
<div class="verdict-hero" id="verdict-hero"></div>
<h2 class="section">Puntuaciones (ponderación Arena)</h2>
<div style="overflow-x:auto"><table class="score-table" id="score-table"></table></div>
<h2 class="section">Los tres jueces + Head Judge</h2>
<div class="judge-cards" id="judge-cards"></div>
<div id="referee-card"></div>
<h2 class="section">Reconocimientos</h2>
<div class="awards" id="awards"></div>
<p class="note">Los jueces son evaluaciones del mismo modelo local con contextos independientes: no son modelos distintos ni verificación factual externa.</p>
<div style="display:flex;gap:10px;margin-top:18px;flex-wrap:wrap">
<button class="btn" id="rematch-btn">🔁 Revancha estratégica</button>
<button class="btn ghost" id="export-md">⬇ Exportar Markdown</button>
<button class="btn ghost" id="export-json">⬇ Exportar JSON</button>
<button class="btn ghost" id="results-lobby">← Volver al Lobby</button>
</div>
</section>
<!-- ============ HALL ============ -->
<section id="view-hall" class="view">
<h2 class="section">Hall of Fame — clasificación histórica (este navegador)</h2>
<div style="overflow-x:auto"><table class="hall" id="hall-table"></table></div>
<h2 class="section">Arena Memory — batallas guardadas</h2>
<div id="match-list"></div>
</section>
<!-- ============ ROADMAP ============ -->
<section id="view-roadmap" class="view">
<h2 class="section">Roadmap V2 — estado real del proyecto</h2>
<div id="roadmap-list"></div>
</section>
</main>
<footer>
AI Debate Arena · motor <a href="https://github.com/ngxson/wllama" target="_blank" rel="noopener">wllama</a> (MIT) · modelo por defecto Qwen2.5-0.5B (Apache 2.0) ·
<a href="https://huggingface.co/XiaomiMiMo/MiMo-V2.6-Distill-Qwen-9B" target="_blank" rel="noopener">MiMo-V2.6-Distill-Qwen-9B</a> (MIT, Xiaomi) ofrecido con fallo de generación observado en pruebas con wllama 3.5.1/3.8.1, en validación ·
inferencia 100% local en tu navegador (CPU/WASM)<br>
Basado en <a href="https://huggingface.co/spaces/fivetech/Multi-Agent-Debate-MiMo-WebGPU" target="_blank" rel="noopener">Multi-Agent-Debate-MiMo-WebGPU</a> · V2 en desarrollo: las fases no marcadas como disponibles aún están en construcción.<br>
<span id="model-in-use" style="color:var(--accent2)">Modelo en esta sesión: sin cargar</span>
</footer>
<!-- AGENT MODAL -->
<div class="modal-back" id="agent-modal">
<div class="modal">
<h3 id="am-title">Editar agente</h3>
<label>Nombre</label><input type="text" id="am-name" maxlength="30">
<label>Avatar</label><div class="emoji-pick" id="am-emojis"></div>
<label>Color</label><div class="color-pick" id="am-colors"></div>
<label>Especialidad</label><input type="text" id="am-spec" maxlength="40">
<label>Estilo</label><input type="text" id="am-style" maxlength="60">
<label>Estrategia de debate</label>
<select id="am-strategy">
<option value="attack">Attack — buscar debilidades rivales</option>
<option value="defense">Defense — proteger la posición</option>
<option value="evidence">Evidence — priorizar evidencias</option>
<option value="socratic">Socratic — cuestionar con preguntas</option>
<option value="counterattack">Counterattack — responder objeciones</option>
<option value="adaptive">Adaptive — cambiar entre rondas</option>
</select>
<label>Personalidad (prompt del agente)</label>
<textarea id="am-system" rows="4"></textarea>
<div style="display:flex;gap:10px;margin-top:16px;justify-content:flex-end">
<button class="btn ghost small" id="am-delete">Eliminar</button>
<button class="btn ghost small" id="am-cancel">Cancelar</button>
<button class="btn small" id="am-save">Guardar</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script type="module" src="app.js"></script>
</body>
</html>