← back to Marketing Command Center
quickpost picker: inline nav-agent controls — density slider + Grid/List/Details(kv) views
64b7fcf8f64a80def62944c08f381b73b49bfafc · 2026-09-03 11:07:34 -0700 · Steve Abrams
The 'Make a post' picture picker was a fixed cramped grid with no way to
space out or re-view the images. Add an inline control row (matching the
wizard's kid-simple styling): a Grid/List/Details view toggle and a density
slider that drives --cols on the grid so cards spread out. Details is the
nav-agent kv-list third view (thumb + name + KIND/TAGS chips). Selection
works in every view and persists across view/density re-renders; choices
persist to localStorage. Verified headless across all 3 views, density,
selection, persistence — zero console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GDFjK9hKdrY7ExEupvWGr
Files touched
M public/panels/quickpost.htmlM public/panels/quickpost.js
Diff
commit 64b7fcf8f64a80def62944c08f381b73b49bfafc
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 3 11:07:34 2026 -0700
quickpost picker: inline nav-agent controls — density slider + Grid/List/Details(kv) views
The 'Make a post' picture picker was a fixed cramped grid with no way to
space out or re-view the images. Add an inline control row (matching the
wizard's kid-simple styling): a Grid/List/Details view toggle and a density
slider that drives --cols on the grid so cards spread out. Details is the
nav-agent kv-list third view (thumb + name + KIND/TAGS chips). Selection
works in every view and persists across view/density re-renders; choices
persist to localStorage. Verified headless across all 3 views, density,
selection, persistence — zero console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GDFjK9hKdrY7ExEupvWGr
---
public/panels/quickpost.html | 26 +++++++++++++++++++++++---
public/panels/quickpost.js | 33 ++++++++++++++++++++++++++++++---
2 files changed, 53 insertions(+), 6 deletions(-)
diff --git a/public/panels/quickpost.html b/public/panels/quickpost.html
index dcb2d5d..f1bb9c0 100644
--- a/public/panels/quickpost.html
+++ b/public/panels/quickpost.html
@@ -10,7 +10,11 @@
<section class="kp-pane on" data-step="1">
<div class="kp-number">1</div><div class="kp-copy"><h3>Pick a picture or video</h3><p>Tap one you like. Every DW post needs a picture or video.</p></div>
<div class="kp-asset-tools"><div class="kp-filter" role="group" aria-label="Picture choices"><button type="button" class="on" data-filter="all">✨ All ideas</button><button type="button" data-filter="catalog">🏷️ DW picks</button><button type="button" data-filter="upload">📤 My uploads</button></div><input id="kp-asset-search" type="search" placeholder="🔎 Find a color or room" aria-label="Find a picture"></div>
- <div id="kp-assets" class="kp-assets" aria-live="polite"><div class="kp-wait">Loading your pictures…</div></div>
+ <div class="kp-view-tools" role="group" aria-label="How the pictures are shown">
+ <div class="kp-views" role="group" aria-label="View"><button type="button" class="on" data-view="grid" aria-pressed="true">▦ Grid</button><button type="button" data-view="list" aria-pressed="false">☰ List</button><button type="button" data-view="kv" aria-pressed="false">⊞ Details</button></div>
+ <label class="kp-density"><span>Bigger</span><input id="kp-density" type="range" min="2" max="8" step="1" value="4" aria-label="Space between pictures — left is bigger, right fits more"><span>Smaller</span></label>
+ </div>
+ <div id="kp-assets" class="kp-assets view-grid" aria-live="polite"><div class="kp-wait">Loading your pictures…</div></div>
<details class="kp-more"><summary>Paste a picture link instead</summary><input id="kp-media" type="url" placeholder="https://…/picture.jpg" autocomplete="off"></details>
</section>
<section class="kp-pane" data-step="2" hidden>
@@ -36,7 +40,7 @@
<style>
.kidpost{--navy:#172235;--paper:#fffdf7;--sun:#f6c84c;--green:#287a55;--coral:#df684f;max-width:1120px;margin:0 auto;color:var(--navy);font-family:"Avenir Next",Avenir,"Trebuchet MS",sans-serif}.kp-hero{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;padding:24px 28px;background:var(--navy);color:#fff;border-radius:24px 24px 0 0;box-shadow:0 18px 45px rgba(23,34,53,.14)}.kp-kicker{font-size:12px;font-weight:800;letter-spacing:.16em;color:var(--sun)}.kp-hero h2{font:700 clamp(28px,4vw,48px)/1.02 "Cormorant Garamond",Georgia,serif;margin:7px 0}.kp-hero p{margin:0;color:#dce4ef;font-size:16px}.kp-score{background:var(--sun);color:var(--navy);border-radius:18px;padding:12px 17px;text-align:center;min-width:94px}.kp-score b{display:block;font-size:20px}.kp-score span{font-size:11px;font-weight:700}
.kp-steps{display:grid;grid-template-columns:repeat(5,1fr);background:#edf0f3;padding:12px;gap:8px}.kp-step{border:0;background:transparent;padding:9px 4px;border-radius:14px;color:#6c7480;cursor:pointer}.kp-step span{display:grid;place-items:center;width:34px;height:34px;margin:0 auto 5px;border:2px solid #aab1ba;border-radius:50%;font-weight:800}.kp-step b{font-size:12px}.kp-step.on{background:#fff;color:var(--navy);box-shadow:0 4px 14px rgba(23,34,53,.09)}.kp-step.on span,.kp-step.done span{border-color:var(--green);background:var(--green);color:#fff}.kp-card{background:var(--paper);border:1px solid #e0ddd2;border-top:0;border-radius:0 0 24px 24px;box-shadow:0 18px 45px rgba(23,34,53,.14);overflow:hidden}.kp-pane{padding:30px;min-height:390px}.kp-number{float:left;display:grid;place-items:center;width:48px;height:48px;border-radius:16px;background:var(--sun);font-size:24px;font-weight:900;margin-right:14px}.kp-copy{min-height:58px}.kp-copy h3{font:700 28px/1.1 "Cormorant Garamond",Georgia,serif;margin:0 0 5px}.kp-copy p{margin:0;color:#667080;font-size:15px}
-.kp-assets{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:12px;margin-top:22px;max-height:360px;overflow:auto;padding:4px}.kp-asset{position:relative;aspect-ratio:1;border:3px solid transparent;border-radius:16px;overflow:hidden;background:#ece8de;cursor:pointer;padding:0}.kp-asset img{width:100%;height:100%;object-fit:cover}.kp-asset-name{position:absolute;inset:auto 0 0;padding:18px 7px 6px;background:linear-gradient(transparent,rgba(23,34,53,.88));color:#fff;font-size:11px;font-weight:700;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}.kp-asset.on{border-color:var(--green);box-shadow:0 0 0 3px #c8ead9}.kp-asset.on:after{content:'✓';position:absolute;right:7px;top:7px;display:grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--green);color:#fff;font-weight:900;pointer-events:none}.kp-more{margin-top:18px;color:#5c6570}.kp-more summary{cursor:pointer;font-weight:700}.kp-more input,.kp-prompt input,#kp-caption,#kp-account{width:100%;box-sizing:border-box;margin-top:9px;border:2px solid #d7d9dc;border-radius:13px;padding:12px 14px;font:inherit;background:#fff}.kp-more input:focus,.kp-prompt input:focus,#kp-caption:focus,#kp-account:focus{outline:3px solid #f8dfa0;border-color:#c99927}
+.kp-assets{display:grid;grid-template-columns:repeat(var(--cols,4),minmax(0,1fr));gap:12px;margin-top:18px;max-height:440px;overflow:auto;padding:4px}.kp-asset{position:relative;aspect-ratio:1;border:3px solid transparent;border-radius:16px;overflow:hidden;background:#ece8de;cursor:pointer;padding:0}.kp-asset img{width:100%;height:100%;object-fit:cover}.kp-asset-name{position:absolute;inset:auto 0 0;padding:18px 7px 6px;background:linear-gradient(transparent,rgba(23,34,53,.88));color:#fff;font-size:11px;font-weight:700;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}.kp-asset.on{border-color:var(--green);box-shadow:0 0 0 3px #c8ead9}.kp-asset.on:after{content:'✓';position:absolute;right:7px;top:7px;display:grid;place-items:center;width:28px;height:28px;border-radius:50%;background:var(--green);color:#fff;font-weight:900;pointer-events:none}.kp-more{margin-top:18px;color:#5c6570}.kp-more summary{cursor:pointer;font-weight:700}.kp-more input,.kp-prompt input,#kp-caption,#kp-account{width:100%;box-sizing:border-box;margin-top:9px;border:2px solid #d7d9dc;border-radius:13px;padding:12px 14px;font:inherit;background:#fff}.kp-more input:focus,.kp-prompt input:focus,#kp-caption:focus,#kp-account:focus{outline:3px solid #f8dfa0;border-color:#c99927}
.kp-asset-tools{display:flex;gap:10px;align-items:center;margin-top:20px;flex-wrap:wrap}.kp-filter{display:flex;gap:6px;flex-wrap:wrap}.kp-filter button{border:1px solid #dadddf;background:#fff;border-radius:999px;padding:8px 11px;font:700 12px inherit;cursor:pointer}.kp-filter button.on{background:var(--navy);border-color:var(--navy);color:#fff}.kp-asset-tools input{flex:1;min-width:180px;border:2px solid #d7d9dc;border-radius:999px;padding:9px 13px;font:inherit}
.kp-choices,.kp-channels{display:grid;grid-template-columns:repeat(auto-fill,minmax(205px,1fr));gap:12px;margin-top:22px}.kp-choice,.kp-channel{min-height:86px;text-align:left;border:2px solid #dadddf;background:#fff;border-radius:16px;padding:14px;cursor:pointer;color:var(--navy);font:inherit}.kp-choice b,.kp-channel b{display:block;font-size:15px;margin-bottom:4px}.kp-choice small,.kp-channel small{display:block;color:#6b7480;line-height:1.35}.kp-choice.on,.kp-channel.on{border-color:var(--green);background:#ecf8f1;box-shadow:0 0 0 2px #c8ead9}.kp-channel.off{opacity:.48;cursor:not-allowed}.kp-label{display:block;margin-top:20px;font-weight:800;font-size:14px}.kp-prompt{display:flex;gap:10px}.kp-prompt input{margin:9px 0 0}.kp-prompt button,#kp-all,#kp-none{border:0;border-radius:12px;padding:12px 16px;font-weight:800;cursor:pointer;white-space:nowrap}.kp-prompt button{margin-top:9px;background:var(--sun);color:var(--navy)}
.kp-music-card{border:2px solid #dadddf;background:#fff;border-radius:16px;padding:10px}.kp-music-card.on{border-color:var(--green);background:#ecf8f1;box-shadow:0 0 0 2px #c8ead9}.kp-music-card .kp-choice{width:100%;border:0;padding:5px;background:transparent;box-shadow:none;min-height:66px}.kp-music-card audio{width:100%;height:34px;margin-top:6px}.kp-license{font-size:11px;color:#52606d;margin:5px 4px 0}.kp-disclosure{font-size:12px;color:#5d6874;background:#f1f4f6;border-radius:10px;padding:9px;margin-top:8px}
@@ -44,4 +48,20 @@
.kp-review{display:grid;grid-template-columns:190px 1fr;gap:20px;margin-top:22px;border:2px solid #dadddf;border-radius:18px;padding:16px;background:#fff}.kp-preview{aspect-ratio:1;border-radius:13px;overflow:hidden;background:#ece8de}.kp-preview img,.kp-preview video{width:100%;height:100%;object-fit:cover}.kp-summary h4{margin:2px 0 5px;font-size:13px;text-transform:uppercase;letter-spacing:.08em;color:#75808c}.kp-summary p{white-space:pre-wrap;line-height:1.5;margin:0 0 14px}.kp-tags{display:flex;gap:7px;flex-wrap:wrap}.kp-tag{background:#edf1f4;border-radius:999px;padding:6px 10px;font-size:12px;font-weight:700}.kp-confirm{display:flex;gap:12px;align-items:flex-start;background:#fff5d5;border:2px solid #efd06f;border-radius:15px;padding:14px;margin-top:17px;font-weight:700;line-height:1.4}.kp-confirm input{width:22px;height:22px;flex:none}.kp-publish,.kp-draft{width:100%;border:0;border-radius:14px;padding:15px;margin-top:12px;font:800 17px/1 inherit;cursor:pointer}.kp-publish{background:var(--green);color:#fff}.kp-publish:disabled{background:#aeb6b1;cursor:not-allowed}.kp-draft{background:#e9ecef;color:var(--navy)}.kp-result{margin-top:12px;font-weight:700;line-height:1.45}.kp-result.ok{color:var(--green)}.kp-result.bad{color:#a33c2f}.kp-nav{display:flex;justify-content:space-between;border-top:1px solid #e3e1d9;padding:16px 30px;background:#fff}.kp-nav button{border:0;border-radius:12px;padding:12px 18px;font-weight:800;cursor:pointer}.kp-nav button:disabled{opacity:.35}.kp-nav #kp-next{background:var(--coral);color:#fff}.kp-wait{grid-column:1/-1;padding:28px;text-align:center;color:#68717c}.kp-error{background:#fff0eb;color:#923f32;border-radius:12px;padding:12px;margin-top:12px}
@media(max-width:700px){.kp-hero{align-items:flex-start;padding:20px}.kp-score{display:none}.kp-steps{gap:3px}.kp-step b{display:none}.kp-pane{padding:22px 16px}.kp-assets{grid-template-columns:repeat(3,1fr)}.kp-prompt{display:block}.kp-prompt button{width:100%}.kp-review{grid-template-columns:1fr}.kp-preview{max-height:280px}.kp-nav{padding:14px 16px}}
</style>
-<style>.kp-asset img{pointer-events:none}.kp-asset-name,.kp-asset.on:after{pointer-events:none}.kp-step:disabled{cursor:not-allowed;opacity:.5}</style>
+<style>.kp-asset img{pointer-events:none}.kp-asset-name,.kp-asset.on:after{pointer-events:none}.kp-step:disabled{cursor:not-allowed;opacity:.5}
+/* Inline view + density controls (nav-agent style) */
+.kp-view-tools{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:12px}
+.kp-views{display:flex;gap:6px}.kp-views button{border:1px solid #dadddf;background:#fff;border-radius:999px;padding:8px 13px;font:700 12px inherit;color:var(--navy);cursor:pointer}.kp-views button.on{background:var(--green);border-color:var(--green);color:#fff}
+.kp-density{display:flex;align-items:center;gap:9px;font-size:11px;font-weight:800;color:#8894a2;text-transform:uppercase;letter-spacing:.05em}.kp-density input[type=range]{width:150px;accent-color:var(--green);cursor:pointer}.kp-density[hidden]{display:none}
+/* thumbnail + row shared bits */
+.kp-thumb{display:block;overflow:hidden;background:#ece8de;flex:none}.kp-thumb img{width:100%;height:100%;object-fit:cover;display:block}
+.kp-row-main{min-width:0;overflow:hidden}.kp-row-name{display:block;font-size:14px;font-weight:800;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--navy)}
+.kp-kv{display:flex;gap:6px;flex-wrap:wrap;margin-top:6px}.kp-kv-chip{background:#edf1f4;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:700;color:#3d4653}.kp-kv-chip i{font-style:normal;text-transform:uppercase;letter-spacing:.06em;color:#8894a2;margin-right:5px;font-size:9px}
+/* List + Details (kv) view: one column, thumb on the left, text on the right */
+.kp-assets.view-list,.kp-assets.view-kv{grid-template-columns:1fr;gap:8px}
+.view-list .kp-asset,.view-kv .kp-asset{aspect-ratio:auto;display:flex;align-items:center;gap:12px;text-align:left;padding:8px;background:#fff;border:2px solid #e4e1d8;border-radius:14px}
+.view-list .kp-asset .kp-thumb,.view-kv .kp-asset .kp-thumb{width:56px;height:56px;border-radius:10px}
+.view-kv .kp-asset .kp-thumb{width:70px;height:70px}
+.view-list .kp-asset-name,.view-kv .kp-asset-name{display:none}
+.view-list .kp-asset.on,.view-kv .kp-asset.on{border-color:var(--green);background:#ecf8f1}
+.view-list .kp-asset.on:after,.view-kv .kp-asset.on:after{top:50%;right:12px;transform:translateY(-50%)}</style>
diff --git a/public/panels/quickpost.js b/public/panels/quickpost.js
index 9cf3b68..fedede0 100644
--- a/public/panels/quickpost.js
+++ b/public/panels/quickpost.js
@@ -8,8 +8,11 @@ window.MCC_PANELS.quickpost = {
const $$ = s => [...root.querySelectorAll(s)];
const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const icons = {facebook:'📘',instagram:'📸',bluesky:'🦋',linkedin:'💼',tiktok:'🎵',youtube:'▶️',threads:'🧵'};
- const state = { step:1, mediaUrl:'', song:null, channels:[], targets:[], status:{} };
+ const state = { step:1, mediaUrl:'', song:null, channels:[], targets:[], status:{}, view:'grid', cols:4 };
let allAssets = [];
+ const PICKER_KEY = 'mcc.quickpost.picker';
+ try { const s = JSON.parse(localStorage.getItem(PICKER_KEY) || '{}'); if (['grid','list','kv'].includes(s.view)) state.view = s.view; if (+s.cols >= 2 && +s.cols <= 8) state.cols = +s.cols; } catch (_) {}
+ const savePicker = () => { try { localStorage.setItem(PICKER_KEY, JSON.stringify({ view: state.view, cols: state.cols })); } catch (_) {} };
const names = ['','Picture','Music','Words','Where','Check'];
function note(step, msg) {
@@ -58,13 +61,37 @@ window.MCC_PANELS.quickpost = {
renderAssets();
} catch (_) { $('#kp-assets').innerHTML = '<div class="kp-error">Pictures could not load. Paste a picture link below.</div>'; }
}
+ function cardInner(a, i) {
+ const name = esc(a.name || 'DW picture');
+ const img = `<img loading="lazy" src="${esc(a.src)}" alt="${esc(a.name || 'Wallpaper picture')}">`;
+ if (state.view === 'grid') return `${img}<span class="kp-asset-name">${name}</span>`;
+ const kind = { catalog:'DW pick', upload:'My upload' }[String(a.kind || '').toLowerCase()] || esc(a.kind || 'Picture');
+ const chips = state.view === 'kv'
+ ? `<span class="kp-kv"><span class="kp-kv-chip"><i>Kind</i>${kind}</span>${(a.tags && a.tags.length) ? `<span class="kp-kv-chip"><i>Tags</i>${esc(a.tags.slice(0,3).join(' · '))}</span>` : ''}</span>`
+ : '';
+ return `<span class="kp-thumb">${img}</span><span class="kp-row-main"><b class="kp-row-name">${name}</b>${chips}</span>`;
+ }
function renderAssets() {
const filter = root.querySelector('.kp-filter button.on')?.dataset.filter || 'all';
const q = ($('#kp-asset-search')?.value || '').trim().toLowerCase();
const assets = allAssets.filter(a => (filter === 'all' || String(a.kind || '').toLowerCase() === filter) && (!q || `${a.name || ''} ${(a.tags || []).join(' ')} ${JSON.stringify(a.aiTags || {})}`.toLowerCase().includes(q))).slice(0, 120);
- $('#kp-assets').innerHTML = assets.length ? assets.map((a,i) => `<button class="kp-asset" type="button" data-url="${esc(new URL(a.src, O).href)}" aria-label="Choose ${esc(a.name || 'picture '+(i+1))}" aria-pressed="false"><img loading="lazy" src="${esc(a.src)}" alt="${esc(a.name || 'Wallpaper picture')}"><span class="kp-asset-name">${esc(a.name || 'DW picture')}</span></button>`).join('') : '<div class="kp-wait">No matching pictures yet. Try another word or paste a link below.</div>';
- $$('.kp-asset').forEach(b => b.onclick = () => { $$('.kp-asset').forEach(x => { x.classList.remove('on'); x.setAttribute('aria-pressed','false'); }); b.classList.add('on'); b.setAttribute('aria-pressed','true'); state.mediaUrl = b.dataset.url; $('#kp-media').value = state.mediaUrl; });
+ const box = $('#kp-assets');
+ box.classList.remove('view-grid','view-list','view-kv'); box.classList.add('view-' + state.view);
+ box.style.setProperty('--cols', state.cols);
+ box.innerHTML = assets.length ? assets.map((a,i) => `<button class="kp-asset" type="button" data-url="${esc(new URL(a.src, O).href)}" aria-label="Choose ${esc(a.name || 'picture '+(i+1))}" aria-pressed="false">${cardInner(a,i)}</button>`).join('') : '<div class="kp-wait">No matching pictures yet. Try another word or paste a link below.</div>';
+ $$('.kp-asset').forEach(b => {
+ if (b.dataset.url === state.mediaUrl) { b.classList.add('on'); b.setAttribute('aria-pressed','true'); }
+ b.onclick = () => { $$('.kp-asset').forEach(x => { x.classList.remove('on'); x.setAttribute('aria-pressed','false'); }); b.classList.add('on'); b.setAttribute('aria-pressed','true'); state.mediaUrl = b.dataset.url; $('#kp-media').value = state.mediaUrl; };
+ });
+ }
+ function syncViewUI() {
+ $$('.kp-views button').forEach(b => { const on = b.dataset.view === state.view; b.classList.toggle('on', on); b.setAttribute('aria-pressed', String(on)); });
+ const dens = root.querySelector('.kp-density'); if (dens) dens.hidden = state.view !== 'grid';
+ const r = $('#kp-density'); if (r) r.value = state.cols;
}
+ $$('.kp-views button').forEach(b => b.onclick = () => { state.view = b.dataset.view; savePicker(); syncViewUI(); renderAssets(); });
+ $('#kp-density').addEventListener('input', e => { state.cols = +e.target.value; savePicker(); renderAssets(); });
+ syncViewUI();
$$('.kp-filter button').forEach(b => b.onclick = () => { $$('.kp-filter button').forEach(x => x.classList.remove('on')); b.classList.add('on'); renderAssets(); });
$('#kp-asset-search').addEventListener('input', renderAssets);
$('#kp-media').addEventListener('input', e => { state.mediaUrl = e.target.value.trim(); $$('.kp-asset').forEach(x => x.classList.toggle('on', x.dataset.url === state.mediaUrl)); });
← 23750b0 auto-data-snapshot: 2026-09-03T10:32:28 (1 data files) — pub
·
back to Marketing Command Center
·
auto-data-snapshot: 2026-09-03T11:10:51 (1 data files) — pub d3e2d6d →