Files
JetCore/jetc/barant-site/src/email.page.retired-into-admin-subpage-20260905
T
Brad ArantandClaude Sonnet 5 905fbafa5a Fix <sql> silent-failure bug; split /vst into overview + subpage; misc site fixes
- __mysql.cpp: <sql> never checked mysql_real_query's own return code,
  so a genuine query failure was indistinguishable from "ran fine, zero
  rows" - <try>/<catch> around a failing <sql> never actually caught
  anything anywhere. Now throws on a real failure.
- spa.js: dynamically re-injected scripts now run in true sequential
  order (wait for each external script's load event before running the
  next), fixing checkout's Square card field never building on the
  first client-side navigation to /checkout (only after a refresh) -
  async=false alone only orders multiple external scripts relative to
  each other, it doesn't delay a plain inline sibling.
- /vst split into a real overview + /vst/modular1 subpage with a left
  menu, reusing opensource.page's existing subpage-tier pattern
  (opensource.menu/main.css's submenuitem-active highlighting, and
  several other subpage-tier retirements, were already finished earlier
  this session but never committed - included here too).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-13 15:14:25 +00:00

129 lines
7.7 KiB
Plaintext

<page secure="yes" loggedin="yes">
<title>EMAIL</title>
<contents>
<style>
#email-connect-form { max-width: 360px; margin: 40px auto; }
#email-connect-form input { display: block; width: 100%; box-sizing: border-box; margin-bottom: 10px; padding: 6px; }
#email-connect-error { color: #a00; min-height: 1.2em; margin-bottom: 10px; }
#email-app { height: 100%; }
#email-client { display: flex; flex-direction: column; max-height: 100%; }
#email-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1pt solid var(--bond-line-soft); margin-bottom: 6px; }
#email-toolbar-actions { display: flex; align-items: center; gap: 6px; }
#email-search-box { display: flex; align-items: center; gap: 4px; margin-right: 6px; }
#email-search-input {
background: #101014; color: var(--bond-text);
border: 1px solid var(--bond-line-soft); border-radius: 4px;
padding: 4px 8px; font-size: 9pt; width: 150px;
}
#email-search-input:focus {
outline: none; border-color: var(--bond-gold-bright);
box-shadow: 0 0 0.6em rgba(197, 164, 90, 0.5), 0 0 0.2em rgba(227, 196, 126, 0.55);
}
#email-list-count { color: var(--bond-text-dim); font-size: 9pt; margin-left: 6px; }
#email-layout { display: flex; flex: 1; min-height: 0; }
#email-list { width: 340px; flex-shrink: 0; overflow-y: auto; border-right: 1pt solid var(--bond-line-soft); transition: opacity 0.18s ease; }
#email-reading-pane { flex: 1; min-width: 0; overflow-y: auto; padding: 0 12px; transition: opacity 0.18s ease; }
.email-fading { opacity: 0; }
.email-row {
display: flex; align-items: center; gap: 6px; padding: 8px 10px 8px 13px;
border-bottom: 1pt solid var(--bond-line-soft); border-left: 3px solid transparent; cursor: pointer;
overflow: hidden;
transition: opacity 0.2s ease,
height 0.22s ease 0.2s, padding-top 0.22s ease 0.2s, padding-bottom 0.22s ease 0.2s,
margin-top 0.22s ease 0.2s, margin-bottom 0.22s ease 0.2s, border-width 0.22s ease 0.2s;
}
.email-row:hover { background: rgba(255, 255, 255, 0.05); }
.email-row.unread {
font-weight: bold;
background: rgba(197, 164, 90, 0.12);
box-shadow: inset 0 0 0 1px var(--bond-gold);
}
.email-row.selected { background: rgba(197, 164, 90, 0.16); border-left-color: var(--bond-gold); }
.email-row.selected:hover { background: rgba(197, 164, 90, 0.22); }
/* Per-row removal animation for the trash button (see trashMessage()
in email.js) - dissolves in place first (the opacity transition,
no delay), then collapses its own box to nothing (the delayed
height/padding/margin/border transitions kick in once the fade is
mostly done) so every row below it slides up smoothly to close the
gap, purely as a side effect of ordinary document-flow reflow - no
other row is touched, faded, or re-rendered to make that happen. */
.email-row.email-row-removing {
opacity: 0;
height: 0 !important;
padding-top: 0; padding-bottom: 0;
margin-top: 0; margin-bottom: 0;
border-width: 0;
}
.email-row-info { flex: 1; min-width: 0; }
.email-row .email-from { font-size: 10pt; }
.email-row .email-subject { font-size: 9pt; color: var(--bond-text-dim); }
.email-row .email-date { font-size: 8pt; color: var(--bond-text-dim); }
.email-row-trash-btn { flex-shrink: 0; width: 24px; height: 24px; opacity: 0.55; }
.email-row-trash-btn svg { width: 13px; height: 13px; }
.email-row-trash-btn:hover { opacity: 1; }
#email-reading-pane .email-header { border-bottom: 1pt solid var(--bond-line-soft); margin-bottom: 10px; padding-bottom: 10px; }
#email-reading-pane .email-body { white-space: pre-wrap; overflow-wrap: break-word; word-break: break-word; font-family: monospace; font-size: 9pt; }
#email-reading-pane .email-body-frame { width: 100%; height: 520px; border: none; border-radius: 4px; background: #fff; }
#email-reading-pane .email-actions { margin-top: 10px; }
#email-empty-state { color: var(--bond-text-dim); padding: 20px; }
#email-folder-list { display: flex; flex-direction: column; }
.email-folder-link { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px; margin-bottom: 2px; border-radius: 4px; text-decoration: none; color: inherit; }
.email-folder-link:hover { background: rgba(255,255,255,0.08); }
.email-folder-link.email-folder-selected { background: rgba(255,255,255,0.14); font-weight: bold; }
.email-folder-count { font-size: 8pt; padding: 1px 6px; border-radius: 8px; background: rgba(197, 164, 90, 0.25); color: var(--bond-gold-bright); }
.email-folder-count:empty { display: none; }
.icon-btn {
display: inline-flex; align-items: center; justify-content: center;
width: 30px; height: 30px; padding: 0; margin: 0; cursor: pointer;
border-radius: 6px; border: 1px solid var(--bond-gold);
background: linear-gradient(180deg, #1c1c24, #131318);
color: var(--bond-gold-bright);
box-shadow: 0 0 0.4em rgba(197, 164, 90, 0.25);
transition: box-shadow 0.2s ease, background 0.2s ease, transform 0.1s ease;
}
.icon-btn svg { width: 16px; height: 16px; display: block; }
.icon-btn:hover {
box-shadow: 0 0 0.8em rgba(197, 164, 90, 0.5), 0 0 0.25em rgba(227, 196, 126, 0.55);
background: linear-gradient(180deg, #24242e, #1a1a20);
}
.icon-btn:active { transform: scale(0.94); }
.icon-btn.icon-btn-active {
color: #131318;
background: linear-gradient(180deg, var(--bond-gold-bright), var(--bond-gold));
box-shadow: 0 0 0.7em rgba(197, 164, 90, 0.6);
}
</style>
<div id="email-app">
<if value1="$[@_imap_user]" value2="" type="eq">
<div id="email-connect-form">
<h3>Connect to Mailbox</h3>
<input type="text" id="email-user-input" placeholder="you@barant.com" autocomplete="username">
<input type="password" id="email-password-input" placeholder="Password" autocomplete="current-password">
<button type="button" id="email-connect-btn">Connect</button>
<div id="email-connect-error"></div>
</div>
<else>
<div id="email-client">
<div id="email-toolbar">
<span id="email-connected-as">$[@_imap_user]</span><span id="email-list-count"></span>
<span id="email-toolbar-actions">
<span id="email-search-box">
<input type="text" id="email-search-input" placeholder="Search mail&hellip;">
<button type="button" id="email-search-btn" class="icon-btn" title="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/></svg></button>
</span>
<button type="button" id="email-compose-btn" class="icon-btn" title="New Mail"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h13a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2z"/><path d="M2.5 7.5 12 14l9.5-6.5"/><path d="M18.5 3.5v5M16 6h5"/></svg></button>
<button type="button" id="email-refresh-btn" class="icon-btn" title="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 11a8 8 0 0 0-14.6-4.4M4 4v5h5"/><path d="M4 13a8 8 0 0 0 14.6 4.4M20 20v-5h-5"/></svg></button>
<button type="button" id="email-disconnect-btn" class="icon-btn" title="Disconnect"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><path d="M16 17l5-5-5-5"/><path d="M21 12H9"/></svg></button>
</span>
</div>
<div id="email-layout">
<div id="email-list"></div>
<div id="email-reading-pane"><div id="email-empty-state">Select a message to read it.</div></div>
</div>
</div>
</if>
</div>
<script src="/assets/email.js?v=20260905m"></script>
</contents>
</page>