[object Object]

← back to Dead Agentabrams

room: interactive touring-history year map for archive browse

0c6d91e5d6d3af48f71ac2190b5af3a859671ee5 · 2026-09-02 07:50:37 -0700 · Steve Abrams

Files touched

Diff

commit 0c6d91e5d6d3af48f71ac2190b5af3a859671ee5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 2 07:50:37 2026 -0700

    room: interactive touring-history year map for archive browse
---
 room/index.html | 55 +++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 49 insertions(+), 6 deletions(-)

diff --git a/room/index.html b/room/index.html
index 05c9f58..d08161d 100644
--- a/room/index.html
+++ b/room/index.html
@@ -154,6 +154,18 @@
   .mr-search{width:100%;background:rgba(40,12,16,.8);color:var(--ink);border:1px solid var(--panel-edge);border-radius:9px;padding:8px 10px;font-size:12px;margin-bottom:8px}
   .mr-search::placeholder{color:var(--ink-dim)}
   .mr-year{width:100%;background:rgba(40,12,16,.8);color:var(--ink);border:1px solid var(--panel-edge);border-radius:9px;padding:7px 9px;font-size:12px;margin-bottom:8px;cursor:pointer}
+  /* interactive touring-history year map */
+  .mr-map-head{display:flex;justify-content:space-between;align-items:center;font-size:10px;color:var(--ink-dim);margin:2px 0 3px;gap:8px}
+  #mrMapLabel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+  .mr-allbtn{flex:none;background:rgba(70,20,26,.6);color:var(--ink);border:1px solid var(--panel-edge);border-radius:6px;font-size:10px;padding:2px 8px;cursor:pointer}
+  .mr-allbtn.on{background:var(--gold);color:#2a0d10;border-color:var(--gold)}
+  .mr-map{display:flex;align-items:flex-end;gap:2px;height:60px;overflow-x:auto;overflow-y:hidden;padding:3px 2px 0;margin-bottom:8px;border-bottom:1px solid var(--panel-edge);scrollbar-width:thin}
+  .mr-yr{flex:0 0 auto;width:11px;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;height:100%;cursor:pointer;background:none;border:0;padding:0}
+  .mr-yr .bar{width:8px;min-height:3px;border-radius:2px 2px 0 0;background:linear-gradient(180deg,var(--gold),#7a2a1e);opacity:.5;transition:opacity .15s,box-shadow .15s}
+  .mr-yr:hover .bar{opacity:.9}
+  .mr-yr.on .bar{opacity:1;background:linear-gradient(180deg,#ffd97a,var(--gold));box-shadow:0 0 8px rgba(240,190,90,.85)}
+  .mr-yr .yl{font-size:7px;line-height:1;color:var(--ink-dim);margin-top:2px;writing-mode:vertical-rl;transform:rotate(180deg);font-variant-numeric:tabular-nums;letter-spacing:-.5px;height:14px}
+  .mr-yr.on .yl{color:var(--gold)}
   .mr-results{max-height:150px;overflow:auto;margin-bottom:8px;display:flex;flex-direction:column;gap:2px}
   .mr-results:empty{display:none}
   .mr-result{display:flex;gap:9px;align-items:baseline;width:100%;text-align:left;background:none;border:0;color:var(--ink-dim);font-family:var(--sans);font-size:11.5px;padding:5px 6px;cursor:pointer;border-radius:6px}
@@ -243,7 +255,9 @@
     </header>
     <div class="mrbody" id="mrbody">
       <input class="mr-search" id="mrSearch" type="search" placeholder="🔍 Search the whole Dead archive — date, city, song…" aria-label="Search the Grateful Dead archive">
-      <select class="mr-year" id="mrYear" aria-label="Browse by year"></select>
+      <div class="mr-map-head"><span id="mrMapLabel">Touring history — tap a year</span><button type="button" class="mr-allbtn on" id="mrAll">All years</button></div>
+      <div class="mr-map" id="mrMap" role="listbox" aria-label="Browse shows by year"></div>
+      <input type="hidden" id="mrYear" value="">
       <div class="mr-results" id="mrResults" role="list"></div>
       <div class="mr-picks">Curated nights:</div>
       <select class="mr-show" id="mrShow" aria-label="Choose a recording from the Live Music Archive"></select>
@@ -1646,11 +1660,40 @@ sel.addEventListener('change',()=>loadShow(+sel.value,{play:true}));
 
 // archive search wiring — type anything, debounced; Enter searches immediately
 const searchEl=$('mrSearch'), yearEl=$('mrYear');
-if(yearEl){
-  let opts='<option value="">All years · most played</option>';
-  for(let y=1965;y<=1995;y++) opts+=`<option value="${y}">${y}</option>`;
-  yearEl.innerHTML=opts;
-  yearEl.addEventListener('change',()=>searchArchive(searchEl?searchEl.value:''));
+// Interactive touring-history map: one bar per year, height ∝ shows surviving that year.
+const mapEl=$('mrMap'), mapLabel=$('mrMapLabel'), allBtn=$('mrAll');
+if(mapEl && yearEl){
+  const YEARS=[]; for(let y=1965;y<=1995;y++) YEARS.push(y);
+  const counts={};
+  function setYear(y){
+    yearEl.value=y?String(y):'';
+    [...mapEl.children].forEach(c=>c.classList.toggle('on', c.dataset.y===String(y)));
+    allBtn.classList.toggle('on', !y);
+    mapLabel.textContent = y ? `${y} · ${(counts[y]||0).toLocaleString()} shows` : 'Touring history — tap a year';
+    searchArchive(searchEl?searchEl.value:'');
+  }
+  YEARS.forEach(y=>{
+    const b=document.createElement('button'); b.type='button'; b.className='mr-yr'; b.dataset.y=String(y); b.setAttribute('role','option');
+    b.title=y+' — counting…';
+    b.innerHTML=`<span class="bar" style="height:3px"></span><span class="yl">${y%5===0?y:''}</span>`;
+    b.addEventListener('click',()=>setYear(y));
+    b.addEventListener('mouseenter',()=>{ if(counts[y]!=null) mapLabel.textContent=`${y} · ${counts[y].toLocaleString()} shows`; });
+    mapEl.appendChild(b);
+  });
+  mapEl.addEventListener('mouseleave',()=>{ const on=yearEl.value; mapLabel.textContent = on?`${on} · ${(counts[on]||0).toLocaleString()} shows`:'Touring history — tap a year'; });
+  allBtn.addEventListener('click',()=>setYear(0));
+  // Fetch per-year show counts in parallel, then scale bars (sqrt to tame the '77–'90 spikes).
+  Promise.all(YEARS.map(y=>
+    fetch('https://archive.org/advancedsearch.php?q='+encodeURIComponent('collection:GratefulDead AND year:'+y)+'&rows=0&output=json',{mode:'cors'})
+      .then(r=>r.json()).then(j=>({y,n:(j.response&&j.response.numFound)||0})).catch(()=>({y,n:0}))
+  )).then(rows=>{
+    const max=Math.max(1,...rows.map(r=>Math.sqrt(r.n)));
+    rows.forEach(({y,n})=>{ counts[y]=n; const el=mapEl.querySelector('[data-y="'+y+'"]'); if(!el) return;
+      el.querySelector('.bar').style.height=Math.round(3+(Math.sqrt(n)/max)*40)+'px';
+      el.title=y+' — '+n.toLocaleString()+' shows';
+    });
+    const cur=yearEl.value; if(cur) mapLabel.textContent=`${cur} · ${(counts[cur]||0).toLocaleString()} shows`;
+  }).catch(()=>{});
 }
 if(searchEl){
   searchEl.addEventListener('input',()=>{ clearTimeout(searchTimer); searchTimer=setTimeout(()=>searchArchive(searchEl.value),450); });

← cb10224 Sync ROOM 01: full Grateful Dead archive browse (popular + b  ·  back to Dead Agentabrams  ·  snapshot before orb UFO/celestial + roaming-animal build 0feb28b →