[object Object]

← back to Model Arena

auto-save: 2026-07-22T23:47:26 (3 files) — data/challenges.json public/index.html server.js

956ad95e7d0b0b8e6c71a1fa89314571a4e0ea70 · 2026-07-22 23:47:32 -0700 · Steve Abrams

Files touched

Diff

commit 956ad95e7d0b0b8e6c71a1fa89314571a4e0ea70
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Jul 22 23:47:32 2026 -0700

    auto-save: 2026-07-22T23:47:26 (3 files) — data/challenges.json public/index.html server.js
---
 data/challenges.json | 29 +++++++++++++++++++++++++++++
 public/index.html    | 13 ++++++++++++-
 server.js            | 27 +++++++++++++++++++++++++++
 3 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/data/challenges.json b/data/challenges.json
index ea213c2..dc53815 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -884,5 +884,34 @@
     "judging": false,
     "aiPick": "gemma3-12b",
     "judged_at": "2026-07-23T06:44:54.324Z"
+  },
+  {
+    "id": "661e816553cf",
+    "title": "Remix: Luxury Product Page",
+    "prompt": "Build a single-file HTML product page for a luxury grasscloth wallpaper \"Kyoto Silk\" in the colorway Celadon. Include: a hero area (CSS gradient placeholder), elegant serif display typography, price ($148/roll), an Add to Cart button, and a spec table (Material: Grasscloth, Width 36in, Repeat Random, Origin Japan). High-end interior-design aesthetic, no external resources.\n\nHere is a strong previous attempt at this exact challenge. IMPROVE on it — keep what works, fix its weaknesses, and elevate the visual quality and polish. Return a complete, improved single-file HTML document.\n\n=== PREVIOUS ATTEMPT (Gemma3 12B) ===\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Kyoto Silk - Celadon Wallpaper</title>\n    <style>\n        body {\n            font-family: 'Playfair Display', serif; /* Placeholder - replace with actual font */\n            margin: 0;\n            padding: 0;\n            background-color: #f9f7f6;\n            color: #333;\n            line-height: 1.6;\n        }\n\n        /* Hero Area (CSS Gradient Placeholder) */\n        .hero {\n            background: linear-gradient(to bottom, rgba(255, 255, 255, 0.8), rgba(245, 245, 245, 1)); /* Placeholder gradient - replace with desired effect */\n            padding: 60px;\n            text-align: center;\n        }\n\n        h1 {\n            font-size: 3em;\n            margin-bottom: 10px;\n            color: #4a4a4a;\n        }\n\n        .subtitle {\n            font-style: italic;\n            font-size: 1.2em;\n            color: #777;\n        }\n\n        /* Product Details */\n        .product-details {\n            padding: 40px;\n            max-width: 960px;\n            margin: 0 auto;\n        }\n\n        .price {\n            font-size: 1.5em;\n            font-weight: bold;\n            color: #a87b3d; /* Gold/Bronze Accent */\n        }\n\n        button {\n            background-color: #a87b3d;\n            color: white;\n            border: none;\n            padding: 12px 24px;\n            font-size: 1em;\n            cursor: pointer;\n            transition: background-color 0.3s ease;\n        }\n\n        button:hover {\n            background-color: #8a692d;\n        }\n\n\n        table {\n            width: 100%;\n            border-collapse: collapse;\n            margin-top: 30px;\n        }\n\n        th, td {\n            padding: 12px;\n            text-align: left;\n            border-bottom: 1px solid #ddd;\n        }\n\n        th {\n            background-color: #f5f5f5;\n            font-weight: bold;\n        }\n\n        /* Responsive Adjustments */\n        @media (max-width: 768px) {\n            .product-details {\n                padding: 20px;\n            }\n            h1 {\n                font-size: 2.5em;\n            }\n            button {\n                display: block;\n                margin: 20px auto;\n            }\n        }\n    </style>\n</head>\n<body>\n\n    <div class=\"hero\">\n        <h1>Kyoto Silk Wallpaper</h1>\n        <p class=\"subtitle\">Celadon Colorway</p>\n    </div>\n\n    <div class=\"product-details\">\n        <h2>Exquisite Grasscloth Texture</h2>\n        <p>Immerse your space in the timeless elegance of Kyoto Silk, a luxurious grasscloth wallpaper handcrafted in Japan. The Celadon colorway evokes a sense of tranquility and natural beauty.</p>\n\n        <div class=\"price\">$148/roll</div>\n\n        <button onclick=\"addToCart()\">Add to Cart</button>\n\n        <h3>Specifications</h3>\n        <table>\n            <tr>\n                <th>Material</th>\n                <td>Grasscloth</td>\n            </tr>\n            <tr>\n                <th>Width</th>\n                <td>36in</td>\n            </tr>\n            <tr>\n                <th>Repeat</th>\n                <td>Random</td>\n            </tr>\n            <tr>\n                <th>Origin</th>\n                <td>Japan</td>\n            </tr>\n        </table>\n\n         <script>\n            function addToCart() {\n              alert(\"Added to cart! (Simulated)\"); // Simple simulation of adding to cart.  Replace with actual logic.\n            }\n         </script>\n    </div>\n\n</body>\n</html>",
+    "remixOf": "248ea25bf69b",
+    "remixSource": "gemma3-12b",
+    "created_at": "2026-07-23T06:46:37.185Z",
+    "winner": null,
+    "runs": [
+      {
+        "model": "gemma3-12b",
+        "status": "running",
+        "error": null,
+        "seconds": null,
+        "cost": null,
+        "started_at": "2026-07-23T06:46:37.192Z",
+        "finished_at": null
+      },
+      {
+        "model": "hermes3-8b",
+        "status": "queued",
+        "error": null,
+        "seconds": null,
+        "cost": null,
+        "started_at": null,
+        "finished_at": null
+      }
+    ]
   }
 ]
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 53d8103..fd4ad49 100644
--- a/public/index.html
+++ b/public/index.html
@@ -305,7 +305,8 @@ async function renderDetail(id, statusOnly){
   if (!r.ok) return null;
   const c = await r.json(); current = c;
   $('#d-title').textContent = c.title + (c.winner ? '  —  👑 ' + mLabel(c.winner) : '');
-  $('#d-meta').textContent = '🕓 ' + fmtWhen(c.created_at) + '\n' + c.prompt;
+  const remixNote = c.remixOf ? '🧬 remixed from ' + mLabel(c.remixSource) + "'s artifact\n" : '';
+  $('#d-meta').textContent = '🕓 ' + fmtWhen(c.created_at) + '\n' + remixNote + c.prompt;
   // AI referee status bar
   const anyArt = c.runs.some(r=>r.status==='done'&&r.thumb);
   const ai = $('#d-ai');
@@ -317,6 +318,16 @@ async function renderDetail(id, statusOnly){
   else ai.innerHTML = '';
   const jb = $('#judge')||$('#rejudge');
   if (jb) jb.onclick = async ()=>{ jb.disabled=true; jb.textContent='judging…'; await fetch(API+'/api/challenges/'+c.id+'/judge',{method:'POST'}); openDetail(c.id); };
+  // Remix control — feed the best artifact back to all local models to improve on ($0)
+  if (anyArt && !c.judging){
+    const src = c.winner || c.aiPick;
+    const rx = document.createElement('span');
+    rx.innerHTML = ` &nbsp;·&nbsp; 🧬 <button class="btn pink" id="remix">Remix${src?' '+mLabel(src)+"'s":' best'} artifact → improve it</button>`;
+    ai.appendChild(rx);
+    $('#remix').onclick = async e=>{ e.target.disabled=true; e.target.textContent='remixing…';
+      const nc = await (await fetch(API+'/api/challenges/'+c.id+'/remix',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'})).json();
+      if (nc.id) openDetail(nc.id); };
+  }
   const arena = $('#d-arena');
   const sig = c.runs.map(r=>r.model+':'+r.status+':'+(r.aiScore??'')+':'+(r.thumb?'t':'')).join('|') + '|' + c.winner + '|' + c.aiPick + '|' + c.judging;
   if (statusOnly && arena.dataset.sig === sig) return c;
diff --git a/server.js b/server.js
index fef703f..68b8816 100644
--- a/server.js
+++ b/server.js
@@ -52,6 +52,8 @@ const MODELS = [
   { id: 'grok',       label: 'Grok 4.5',       kind: 'metered', provider: 'xai',       model: process.env.GROK_MODEL || 'grok-4.5', envKey: 'XAI_API_KEY', estCost: 0.10 },
 ];
 
+const mLabelServer = (id) => { const m = MODELS.find(x => x.id === id); return m ? m.label : id; };
+
 // ---------- storage ----------
 function loadChallenges() { try { return JSON.parse(fs.readFileSync(CH_FILE, 'utf8')); } catch { return []; } }
 function saveChallenges(list) {
@@ -394,6 +396,31 @@ const server = http.createServer(async (req, res) => {
     return c ? send(res, 200, c) : send(res, 404, { error: 'not found' });
   }
 
+  if ((m = p.match(/^\/api\/challenges\/([a-f0-9]+)\/remix$/)) && req.method === 'POST') {
+    const c = challenges.find(x => x.id === m[1]);
+    if (!c) return send(res, 404, { error: 'not found' });
+    const body = await readBody(req);
+    // source artifact: explicit, else the crown, else the AI pick, else first done
+    const srcId = body.source || c.winner || c.aiPick || (c.runs.find(r => r.status === 'done') || {}).model;
+    const srcRun = c.runs.find(r => r.model === srcId && r.status === 'done');
+    if (!srcRun) return send(res, 400, { error: 'no rendered source artifact to remix' });
+    let srcHtml;
+    try { srcHtml = fs.readFileSync(path.join(ART, c.id, srcId + '.html'), 'utf8'); } catch { return send(res, 400, { error: 'source artifact file missing' }); }
+    const ids = Array.isArray(body.models) && body.models.length ? body.models.filter(id => MODELS.some(x => x.id === id))
+      : MODELS.filter(x => x.kind === 'local').map(x => x.id); // default: all local ($0)
+    const remixPrompt = `${c.prompt}\n\nHere is a strong previous attempt at this exact challenge. IMPROVE on it — keep what works, fix its weaknesses, and elevate the visual quality and polish. Return a complete, improved single-file HTML document.\n\n=== PREVIOUS ATTEMPT (${mLabelServer(srcId)}) ===\n${srcHtml.slice(0, 14000)}`;
+    const nc = {
+      id: crypto.randomBytes(6).toString('hex'),
+      title: 'Remix: ' + c.title.replace(/^Remix: /, ''),
+      prompt: remixPrompt, remixOf: c.id, remixSource: srcId,
+      created_at: new Date().toISOString(), winner: null,
+      runs: ids.map(id => ({ model: id, status: 'queued', error: null, seconds: null, cost: null })),
+    };
+    challenges.push(nc); saveChallenges(challenges);
+    for (const id of ids) runModel(nc, id);
+    return send(res, 201, nc);
+  }
+
   if ((m = p.match(/^\/api\/challenges\/([a-f0-9]+)\/vote$/)) && req.method === 'POST') {
     const c = challenges.find(x => x.id === m[1]);
     if (!c) return send(res, 404, { error: 'not found' });

← b15a98b yolo idea 6: daily auto-challenge generator (accrues real ju  ·  back to Model Arena  ·  yolo idea 7: remix mode — feed the winning/AI-pick artifact' 9944dae →