← back to Fromental Internal
nav-agent: sync v1.3 (host left-sidebar collapse on load)
ee8ee40e0da8f8a2c0c287f8a9ec5df492abcaf0 · 2026-08-20 08:14:32 -0700 · Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M public/nav-agent/nav-agent.cssM public/nav-agent/nav-agent.js
Diff
commit ee8ee40e0da8f8a2c0c287f8a9ec5df492abcaf0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 20 08:14:32 2026 -0700
nav-agent: sync v1.3 (host left-sidebar collapse on load)
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 | 79 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 88 insertions(+), 2 deletions(-)
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 4c16ce8..f955268 100644
--- a/public/nav-agent/nav-agent.js
+++ b/public/nav-agent/nav-agent.js
@@ -78,10 +78,13 @@
var cards = [].slice.call(target.children).filter(function (c) { return c.nodeType === 1 && !c.classList.contains('na-group-h') && !c.classList.contains('na-group-body'); });
var sample = cards.slice(0, 24), seen = {}, order = [];
sample.forEach(function (card) {
- [].slice.call(card.querySelectorAll('[data-field]')).forEach(function (n) {
+ var dfCells = [].slice.call(card.querySelectorAll('[data-field]'));
+ dfCells.forEach(function (n) {
var k = n.getAttribute('data-field'); if (!seen[k]) { seen[k] = { key: slug(k), label: k, cls: '[data-field="' + k + '"]', df: k, hits: 0 }; order.push(seen[k]); } seen[k].hits++;
});
- // also collect direct-child element classes as coarse fields when no data-field present
+ // coarse child-class fields ONLY when this card tags no [data-field] cells —
+ // otherwise a cell with both a class and a data-field yields a duplicate field.
+ if (dfCells.length) return;
[].slice.call(card.children).forEach(function (n) {
var cls = (n.className && typeof n.className === 'string') ? n.className.split(/\s+/)[0] : '';
if (!cls || cls.indexOf('na-') === 0) return;
@@ -508,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) {
← ed7e812 nav-agent: List data full-width tight columns (grid) — layou
·
back to Fromental Internal
·
TK-11438: migrate postgres pool to unix socket (darwin fallb 9d86785 →