← back to Dw Pitch Followup
Pitch drawer: render all Gmail correspondence as a table
e26fbb59affd9ace9dc304586a29748ce29b7c41 · 2026-08-06 08:36:50 -0700 · Steve Abrams
The 'All emails with this client' drawer section now renders every message
(both directions, deduped across info@ + steve-office, up to the API max of
50) as a fixed-layout table — Dir / Date / Subject / Preview — with sticky
header. Each email is a <tbody class=em> (summary <tr> + hidden body <tr>),
preserving the .em/.emrow/.embody/.emcaret structure so the existing
click-a-row-to-read-full-body handler works with zero JS-handler changes.
Relabeled the section header to 'All Gmail correspondence'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit e26fbb59affd9ace9dc304586a29748ce29b7c41
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Aug 6 08:36:50 2026 -0700
Pitch drawer: render all Gmail correspondence as a table
The 'All emails with this client' drawer section now renders every message
(both directions, deduped across info@ + steve-office, up to the API max of
50) as a fixed-layout table — Dir / Date / Subject / Preview — with sticky
header. Each email is a <tbody class=em> (summary <tr> + hidden body <tr>),
preserving the .em/.emrow/.embody/.emcaret structure so the existing
click-a-row-to-read-full-body handler works with zero JS-handler changes.
Relabeled the section header to 'All Gmail correspondence'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
public/index.html | 42 ++++++++++++++++++++++++++++++++----------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/public/index.html b/public/index.html
index 46cd17c..e9a0901 100644
--- a/public/index.html
+++ b/public/index.html
@@ -81,6 +81,19 @@
.emails .em .emsnip{color:var(--mut);font-size:11.5px;margin-top:2px}
.emails .em .embody{white-space:pre-wrap;font-size:12px;line-height:1.5;color:var(--ink);background:var(--panel2);border:1px solid var(--line);border-radius:6px;padding:8px 10px;margin-top:5px;max-height:320px;overflow-y:auto}
.emails .em .embody[hidden]{display:none}
+ /* Gmail-correspondence TABLE (drawer). tbody-per-email keeps .em/.emrow/.embody intact. */
+ .emtable{width:100%;border-collapse:collapse;font-size:12px;margin-top:5px;table-layout:fixed}
+ .emtable col.c-dir{width:52px}.emtable col.c-date{width:104px}.emtable col.c-subj{width:38%}
+ .emtable thead th{position:sticky;top:0;background:var(--panel);z-index:1;text-align:left;font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--mut);font-weight:600;border-bottom:1px solid var(--line);padding:3px 8px 4px}
+ .emtable tbody.em{border-left:0;padding:0;border-bottom:1px solid var(--line)}
+ .emtable tr.emrow td{padding:5px 8px;vertical-align:top;line-height:1.35}
+ .emtable tr.emrow:hover td{background:var(--panel2)}
+ .emtable tr.emrow:hover .emc-subj{color:var(--gold)}
+ .emtable td.emc-date{color:var(--mut);white-space:nowrap;font-variant-numeric:tabular-nums}
+ .emtable td.emc-subj{color:var(--ink);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ .emtable td.emc-snip{color:var(--mut);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+ .emtable td.embodycell{padding:0 8px 8px}
+ .emtable td.embodycell .embody{margin-top:0}
.reorder-note{color:var(--red);font-weight:600;margin:6px 0}
.spacer{flex:1}
.selcount{color:var(--gold);font-size:13px}
@@ -442,7 +455,7 @@ function panelContextHTML(list,r){
if(list!=='list3') html+=merchBlock(r);
html+=samplesBlock(r);
html+=`<div class="drow lastcorr" data-lastcorr>📨 Last corresponded: <span data-lc>${(list==='list3')?'—':'click to load…'}</span></div>`;
- if(list!=='list3') html+=`<div class="drow emblock"><div class="emhdr" data-emtoggle>📧 All emails with this client <span class="emcaret">▸</span></div><div class="emails" data-emails hidden></div></div>`;
+ if(list!=='list3') html+=`<div class="drow emblock"><div class="emhdr" data-emtoggle>📧 All Gmail correspondence <span class="emcaret">▸</span></div><div class="emails" data-emails hidden></div></div>`;
return html;
}
function openPanel(){document.getElementById('panel').classList.add('open');document.body.classList.add('panel-open')}
@@ -567,17 +580,26 @@ async function loadEmails(r){
const email=(r.email||'').trim(); if(!email){el.textContent='no email on file for this client';return}
el.textContent='loading emails…';
try{
- const j=await (await fetch(API('/api/emails?email='+encodeURIComponent(email)+'&limit=40'))).json();
+ const j=await (await fetch(API('/api/emails?email='+encodeURIComponent(email)+'&limit=50'))).json();
const ems=j.emails||[];
if(!ems.length){el.textContent='no emails found with this client';return}
- el.innerHTML=`<div class="emcount">${ems.length} email${ems.length===1?'':'s'} (newest first) · click one to read it</div>`+ems.map(m=>{
- const inb=m.dir==='in';
- return `<div class="em" data-emid="${esc(m.id)}" data-mbx="${esc(m.mailbox||'info')}">`
- +`<div class="emrow"><span class="chip ${inb?'g':'a'}" title="${inb?'from client':'we sent'}">${inb?'← in':'→ out'}</span> <span class="emdate">${esc(fmtWhen(m.date))}</span>${m.subject?` · <b>${esc(m.subject.slice(0,80))}</b>`:''} <span class="emcaret">▸</span></div>`
- +`${m.snippet?`<div class="emsnip">${esc(m.snippet)}</div>`:''}`
- +`<div class="embody" hidden></div>`
- +`</div>`;
- }).join('');
+ // Full Gmail correspondence as a table — one <tbody> per email (summary row + hidden
+ // full-body row) so the existing .em/.emrow/.embody click-to-read handler still works.
+ el.innerHTML=`<div class="emcount">${ems.length} message${ems.length===1?'':'s'} (newest first) · click a row to read the full email</div>`
+ +`<table class="emtable"><colgroup><col class="c-dir"><col class="c-date"><col class="c-subj"><col></colgroup>`
+ +`<thead><tr><th>Dir</th><th>Date</th><th>Subject</th><th>Preview</th></tr></thead>`
+ +ems.map(m=>{
+ const inb=m.dir==='in';
+ return `<tbody class="em" data-emid="${esc(m.id)}" data-mbx="${esc(m.mailbox||'info')}">`
+ +`<tr class="emrow">`
+ +`<td class="emc-dir"><span class="chip ${inb?'g':'a'}" title="${inb?'from client':'we sent'}">${inb?'← in':'→ out'}</span></td>`
+ +`<td class="emc-date">${esc(fmtWhen(m.date))}</td>`
+ +`<td class="emc-subj" title="${esc(m.subject||'(no subject)')}">${m.subject?esc(m.subject):'<span style="color:var(--mut)">(no subject)</span>'} <span class="emcaret">▸</span></td>`
+ +`<td class="emc-snip" title="${esc(m.snippet||'')}">${esc(m.snippet||'')}</td>`
+ +`</tr>`
+ +`<tr><td class="embodycell" colspan="4"><div class="embody" hidden></div></td></tr>`
+ +`</tbody>`;
+ }).join('')+`</table>`;
}catch(e){el.textContent='(email lookup failed)'}
}
← 705c715 Add repo-local pre-commit: lint staged launchd/*.plist
·
back to Dw Pitch Followup
·
chore: v0.6.0 (session close) — catch-all route, Gmail corre 32ac85c →