feat: account settings page + dedicated help page

- Add /settings page with password change form and personas list
- Add /help dedicated page (replaces help modal); renders HELP.md with
  collapsible sections, dark theme, back link to active persona
- Add 👤 account button and convert ? button to link in header
- Remove help modal HTML and ~55 lines of modal JS from main app
- Register settings and help routers in main.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-03-23 21:41:18 -04:00
parent c01ef663f5
commit 1b425a539f
9 changed files with 588 additions and 74 deletions

View File

@@ -912,8 +912,6 @@
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (fileModal.classList.contains('open')) fileModal.classList.remove('open');
if (document.getElementById('help-modal')?.classList.contains('open'))
document.getElementById('help-modal').classList.remove('open');
}
// Ctrl+S to save when file modal is open
if ((e.ctrlKey || e.metaKey) && e.key === 's' && fileModal.classList.contains('open')) {
@@ -1121,62 +1119,6 @@
syncHeight();
addMessage('system', 'Session started');
// ── Help modal ────────────────────────────────────────────────
const helpBtn = document.getElementById('help-btn');
const helpModal = document.getElementById('help-modal');
const helpBody = document.getElementById('help-modal-body');
const helpClose = document.getElementById('help-close-btn');
// Sections open by default — everything after "Notes" starts collapsed
const HELP_OPEN_SECTIONS = new Set(['Header Controls', 'Chat', 'Sessions', 'Notes']);
function makeCollapsible(container) {
const h2s = Array.from(container.querySelectorAll('h2'));
for (const h2 of h2s) {
const title = h2.textContent.trim();
const details = document.createElement('details');
if (HELP_OPEN_SECTIONS.has(title)) details.open = true;
const summary = document.createElement('summary');
summary.textContent = title;
details.appendChild(summary);
// Collect all following siblings until the next h2
const siblings = [];
let node = h2.nextSibling;
while (node && node.nodeName !== 'H2') {
siblings.push(node);
node = node.nextSibling;
}
for (const sib of siblings) details.appendChild(sib);
h2.parentNode.replaceChild(details, h2);
}
}
async function openHelp() {
helpBody.textContent = 'Loading…';
helpModal.classList.add('open');
try {
const res = await fetch(`/files/HELP.md?${_fileParams}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
helpBody.innerHTML = marked.parse(data.content);
helpBody.querySelectorAll('a').forEach(a => {
a.target = '_blank'; a.rel = 'noopener noreferrer';
});
makeCollapsible(helpBody);
} catch (err) {
helpBody.textContent = `Failed to load help: ${err.message}`;
}
}
helpBtn.addEventListener('click', openHelp);
helpClose.addEventListener('click', () => helpModal.classList.remove('open'));
helpModal.addEventListener('click', (e) => {
if (e.target === helpModal) helpModal.classList.remove('open');
});
// ── Auth token warning banner ─────────────────────────────
const authBanner = document.getElementById('auth-banner');
const authBannerMsg = document.getElementById('auth-banner-msg');