[object Object]

← back to Re Flyer Aggregator

search+sources: nav-agent v1.3 (sidebar collapse on load) + add nav-agent to / sources dashboard

c4de3266ec44472a49cf1e3af10685194602e79f · 2026-08-20 08:07:59 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit c4de3266ec44472a49cf1e3af10685194602e79f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 20 08:07:59 2026 -0700

    search+sources: nav-agent v1.3 (sidebar collapse on load) + add nav-agent to / sources dashboard
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 public/nav-agent/nav-agent.css | 11 +++++++
 public/nav-agent/nav-agent.js  | 72 ++++++++++++++++++++++++++++++++++++++++++
 public/sources/index.html      |  4 ++-
 3 files changed, 86 insertions(+), 1 deletion(-)

diff --git a/public/nav-agent/nav-agent.css b/public/nav-agent/nav-agent.css
index c80b88a..d43a0d1 100644
--- a/public/nav-agent/nav-agent.css
+++ b/public/nav-agent/nav-agent.css
@@ -119,4 +119,15 @@ table .na-group-h td{padding:8px 6px;color:var(--na-acc);font-weight:600;border-
 .na-lmk{color:var(--na-mut);font-size:10px;text-transform:uppercase;letter-spacing:.5px;margin-right:5px}
 .na-lempty{padding:40px;text-align:center;color:var(--na-mut)}
 
+/* host left-sidebar collapse (v1.3) — always collapsed on load, ☰ reveals */
+.na-side{transition:transform .18s ease}
+.na-side.na-side-collapsed{transform:translateX(-100%)!important;pointer-events:none}
+.na-side-main{transition:margin-left .18s ease}
+.na-side-main.na-side-shift{margin-left:0!important}
+.na-side-toggle{position:fixed;left:10px;top:10px;z-index:2147483002;width:38px;height:34px;
+  display:flex;align-items:center;justify-content:center;border-radius:8px;
+  border:1px solid var(--na-line);background:var(--na-bg2);color:var(--na-fg);
+  font-size:16px;line-height:1;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.28)}
+.na-side-toggle:hover,.na-side-toggle.on{border-color:var(--na-acc);color:var(--na-acc)}
+
 @media (max-width:640px){.na-panel{width:86vw}.na-lmain{min-width:100%}}
diff --git a/public/nav-agent/nav-agent.js b/public/nav-agent/nav-agent.js
index 9aeb59e..f955268 100644
--- a/public/nav-agent/nav-agent.js
+++ b/public/nav-agent/nav-agent.js
@@ -511,13 +511,85 @@
   };
   window.NavAgent = API;
 
+  /* -- host left-sidebar collapse (standard feature, v1.3, 2026-08-20) ----
+   * The Nav Agent collapses the host page's primary LEFT sidebar on load and
+   * adds a ☰ toggle to reveal/hide it. ALWAYS collapsed on load — the reveal is
+   * an in-session choice (deliberately NOT persisted), so every page load starts
+   * clean and collapsed. Runs even on pages with no data grid.
+   *   Opt out:  <body data-nav-sidebar="off">
+   *   Force:    <body data-nav-sidebar="#mySelector">  or  <aside data-nav-sidebar>
+   *   Content:  the shifted main column auto-detects; force with [data-nav-main]. */
+  function naIsSidebar(e) {
+    if (!e || e === document.body) return false;
+    if (e.classList && (e.classList.contains('na-panel') || e.classList.contains('na-list') ||
+        e.classList.contains('na-launch') || e.classList.contains('na-side-toggle'))) return false;
+    var cs; try { cs = getComputedStyle(e); } catch (x) { return false; }
+    if (['fixed', 'sticky', 'absolute'].indexOf(cs.position) < 0) return false;
+    var r = e.getBoundingClientRect();
+    var atLeft = r.left <= 6;
+    var tall = r.height >= Math.min(window.innerHeight * 0.55, 380);
+    var narrow = r.width > 20 && r.width <= 360;
+    var hasNav = e.querySelectorAll('a,button,nav,.nav').length >= 1;
+    return atLeft && tall && narrow && hasNav;
+  }
+  function naFindSidebar() {
+    var body = document.body, a = body.getAttribute('data-nav-sidebar');
+    if (a === 'off') return null;
+    if (a && a !== 'on' && a !== '') { var s = document.querySelector(a); if (s) return s; }
+    var marked = document.querySelector('[data-nav-sidebar]');
+    if (marked && marked !== body) return marked;
+    var sel = ['#side', '#sidebar', '.sidebar', '#side-nav', '.side-nav', '#leftnav', '.leftnav', 'aside'];
+    for (var i = 0; i < sel.length; i++) { var e = document.querySelector(sel[i]); if (naIsSidebar(e)) return e; }
+    var kids = [].slice.call(body.children);
+    for (var j = 0; j < kids.length; j++) { if (naIsSidebar(kids[j])) return kids[j]; }
+    return null;
+  }
+  function naFindMain(side) {
+    var m = document.querySelector('[data-nav-main]'); if (m) return m;
+    var w = side.getBoundingClientRect().width;
+    var pool = [].slice.call(document.body.children).filter(function (c) {
+      return c.nodeType === 1 && c !== side &&
+        !(c.classList && (c.classList.contains('na-panel') || c.classList.contains('na-launch') ||
+          c.classList.contains('na-side-toggle') || c.classList.contains('na-list')));
+    });
+    for (var i = 0; i < pool.length; i++) {
+      var ml = parseFloat(getComputedStyle(pool[i]).marginLeft) || 0;
+      if (ml > 40 && Math.abs(ml - w) <= 28) return pool[i];
+    }
+    return side.nextElementSibling || null;
+  }
+  function naMountSidebar() {
+    try {
+      if (document.body.getAttribute('data-nav-agent') === 'off') return;
+      if (document.querySelector('.na-side-toggle')) return;   // once per page
+      var side = naFindSidebar(); if (!side) return;
+      side.classList.add('na-side');
+      var main = naFindMain(side); if (main) main.classList.add('na-side-main');
+      var open = false;                                        // ALWAYS collapsed on load
+      var tog = el('button', 'na-side-toggle', '☰'); tog.type = 'button';
+      function apply() {
+        side.classList.toggle('na-side-collapsed', !open);
+        if (main) main.classList.toggle('na-side-shift', !open);
+        tog.classList.toggle('on', open);
+        tog.title = open ? 'Collapse sidebar' : 'Expand sidebar';
+        tog.setAttribute('aria-expanded', open ? 'true' : 'false');
+      }
+      tog.onclick = function () { open = !open; apply(); };
+      document.body.appendChild(tog);
+      apply();
+    } catch (e) { if (window.console) console.warn('[nav-agent] sidebar collapse skipped:', e); }
+  }
+  API.collapseSidebar = naMountSidebar;
+
   function boot() {
     try {
       var body = document.body;
       if (!body || body.getAttribute('data-nav-agent') === 'off') return;
+      naMountSidebar(); // collapse the host left sidebar on load (grid-independent)
       // wait a tick for API-driven grids to render their first batch
       var tries = 0;
       (function attempt() {
+        naMountSidebar(); // idempotent — catches a late-rendered sidebar too
         var t = findTarget();
         var populated = false;
         if (t) {
diff --git a/public/sources/index.html b/public/sources/index.html
index 3849787..a009b9d 100644
--- a/public/sources/index.html
+++ b/public/sources/index.html
@@ -87,6 +87,7 @@
  @media(max-width:820px){.grid{grid-template-columns:1fr}#srccards.grid,#srccards.kv{grid-template-columns:1fr}}
 </style>
 <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
+  <link rel="stylesheet" href="/nav-agent/nav-agent.css"><!-- nav-agent -->
 </head><body>
 <div id="nav"><div class="nh"><span>NAV AGENT</span><span><button id="navdock" title="dock left/right">⇄</button><button id="navcol" title="collapse">‹</button></span></div>
  <div class="hint">FIELDS · CLICK A VALUE TO FILTER</div><div id="navfields"></div></div>
@@ -308,4 +309,5 @@ function loadCrawl(){fetch('/advertising/broker-ad-signals.json?t='+Date.now()).
   buildCrawlCharts(d);
 }).catch(()=>document.getElementById('crawl').textContent='crawl data not available yet (running…)');}
 applyDock(); loadSrc(); loadCrawl(); setInterval(()=>{loadSrc();loadCrawl();},20000);
-</script></body></html>
+</script>  <script src="/nav-agent/nav-agent.js" defer></script><!-- nav-agent -->
+</body></html>

← 9193ea1 archive: extract buyer/seller from headlines (same patterns  ·  back to Re Flyer Aggregator  ·  repo hygiene: gitignore regenerable build artifacts (flyers. e531cc6 →