[object Object]

← back to Contact Mailer Ideas

viewer: deep-link by batch slug not date (multiple batches per date now disambiguated); dropdown values switched to batch slugs

a514e3f615c292466e4154081b390b43b8deae78 · 2026-05-11 16:04:51 -0700 · Steve Abrams

Files touched

Diff

commit a514e3f615c292466e4154081b390b43b8deae78
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon May 11 16:04:51 2026 -0700

    viewer: deep-link by batch slug not date (multiple batches per date now disambiguated); dropdown values switched to batch slugs
---
 viewer/server.js | 39 ++++++++++++++++++++++++++++++---------
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/viewer/server.js b/viewer/server.js
index 37986ec..8c0df66 100644
--- a/viewer/server.js
+++ b/viewer/server.js
@@ -66,9 +66,12 @@ function listBatches() {
   return batches;
 }
 
-function loadBatch(date) {
+function loadBatch(dateOrBatch) {
   const batches = listBatches();
-  let target = batches.find(b => b.date === date) || batches[0];
+  // Match by batch slug first (more specific when multiple batches share a date), then by date
+  let target = batches.find(b => b.batch === dateOrBatch)
+            || batches.find(b => b.date === dateOrBatch)
+            || batches[0];
   if (!target) return { meta: {}, byIdx: {}, allTicks: [], batches: [] };
   const doc = JSON.parse(fs.readFileSync(path.join(BATCHES_DIR, target.file), 'utf8'));
   const ideas = doc.ideas || [];
@@ -107,8 +110,8 @@ function loadIdeas() {
 }
 
 app.get('/api/state', (req, res) => {
-  const date = req.query.date;
-  const data = date ? loadBatch(date) : loadIdeas();
+  const selector = req.query.batch || req.query.date;
+  const data = selector ? loadBatch(selector) : loadIdeas();
   data.notes = readNotes()[data.selected && data.selected.batch] || {};
   res.json(data);
 });
@@ -326,7 +329,9 @@ footer{padding:20px 48px 40px;color:#5a5a62;font-size:12px;text-align:center}
 <footer>port 9773 · localhost only · source ~/.claude/skills/idea-loop/data/ideas.jsonl</footer>
 <script>
 const COLORS = ${JSON.stringify(COLORS)};
-let currentDate = null;
+// Parse URL params for deep-link to a specific batch or date
+const _urlParams = new URLSearchParams(window.location.search);
+let currentDate = _urlParams.get('batch') || _urlParams.get('date') || null;
 let state = null;
 const LS = window.localStorage;
 const q = document.getElementById('q');
@@ -371,10 +376,26 @@ density.addEventListener('input', () => {
 async function loadBatches(){
   const r = await fetch('/api/batches'); const list = await r.json();
   const sel = document.getElementById('batchSel');
-  sel.innerHTML = list.map(b => '<option value="'+b.date+'">'+b.date+' · '+b.count+' ideas'+(b.generator!=='manual'?' (auto)':'')+'</option>').join('');
-  if (!currentDate && list.length) currentDate = list[0].date;
+  // Use batch slug as the value (unique) instead of date (which can collide)
+  sel.innerHTML = list.map(b => '<option value="'+b.batch+'">'+b.date+' · '+b.batch+' · '+b.count+' ideas'+(b.generator && b.generator!=='manual'?' (auto)':'')+'</option>').join('');
+  // Map currentDate to a matching batch if user passed a date
+  if (currentDate) {
+    const direct = list.find(b => b.batch === currentDate);
+    if (!direct) {
+      const byDate = list.find(b => b.date === currentDate);
+      if (byDate) currentDate = byDate.batch;
+    }
+  }
+  if (!currentDate && list.length) currentDate = list[0].batch;
   sel.value = currentDate || '';
-  sel.onchange = () => { currentDate = sel.value; load(); };
+  sel.onchange = () => {
+    currentDate = sel.value;
+    // Update URL so it's deep-linkable
+    const u = new URL(window.location);
+    u.searchParams.set('batch', currentDate);
+    window.history.replaceState({}, '', u);
+    load();
+  };
   renderBanner(list);
 }
 function renderBanner(list){
@@ -395,7 +416,7 @@ function renderBanner(list){
 }
 let stateSig = '';
 async function load(){
-  const url = currentDate ? '/api/state?date='+encodeURIComponent(currentDate) : '/api/state';
+  const url = currentDate ? '/api/state?batch='+encodeURIComponent(currentDate) : '/api/state';
   const r = await fetch(url); const j = await r.json();
   // Compare a cheap signature so background refreshes don't disturb DOM/scroll when nothing changed
   const sig = JSON.stringify({

← a8c0f7d combos #7 Spectrum (36/40) — productize age-adaptive-theme s  ·  back to Contact Mailer Ideas  ·  viewer: dedicated renderers for SWOT (5 colored tiles + comp 008d6db →