/**
* faq-sidebar.js — VS Code-style embedded FAQ sidebar
*
* Usage:
* import { initFaqSidebar } from '/static/shared/faq-sidebar.js';
* initFaqSidebar('turnbased'); // loads /static/faq/{lang}/turnbased.md
*/
const STORAGE_KEY_LANG = 'minicpmo_lang';
/* ── lightweight markdown → HTML ── */
function md2html(src) {
const lines = src.split('\n');
const out = [];
let inUl = false, inOl = false, inCode = false, codeBuf = [];
const flush = (tag) => {
if (tag === 'ul' && inUl) { out.push(''); inUl = false; }
if (tag === 'ol' && inOl) { out.push(''); inOl = false; }
};
const flushAll = () => { flush('ul'); flush('ol'); };
const inline = (t) => t
.replace(/`([^`]+)`/g, '$1')
.replace(/\*\*(.+?)\*\*/g, '$1')
.replace(/\*(.+?)\*/g, '$1')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
for (let i = 0; i < lines.length; i++) {
const raw = lines[i];
if (inCode) {
if (raw.trimEnd() === '```') {
out.push('
' + codeBuf.join('\n').replace(//g, '>') + '');
codeBuf = []; inCode = false;
} else { codeBuf.push(raw); }
continue;
}
if (raw.trimStart().startsWith('```')) { flushAll(); inCode = true; continue; }
const trimmed = raw.trim();
if (!trimmed) { flushAll(); continue; }
if (trimmed.startsWith('---') && trimmed.replace(/-/g, '').trim() === '') { flushAll(); out.push(''); continue; } flushAll(); out.push('' + inline(bq.join(' ')) + '
' + inline(trimmed) + '
'); } flushAll(); return out.join('\n'); } /* ── make h2 sections into collapsible accordion ── */ function enableAccordion(container) { const headings = container.querySelectorAll('h2'); headings.forEach((h2) => { const answer = document.createElement('div'); answer.className = 'faq-answer'; let sib = h2.nextSibling; while (sib && !(sib.nodeType === 1 && /^(H[12]|HR)$/.test(sib.tagName))) { const next = sib.nextSibling; answer.appendChild(sib); sib = next; } h2.after(answer); h2.addEventListener('click', () => { const open = h2.classList.toggle('faq-open'); answer.classList.toggle('faq-show', open); }); }); if (headings.length > 0) { headings[0].classList.add('faq-open'); headings[0].nextElementSibling.classList.add('faq-show'); } } /* ── build sidebar DOM ── */ function buildSidebar(lang) { const sidebar = document.createElement('div'); sidebar.className = 'faq-sidebar'; sidebar.id = 'faqSidebar'; sidebar.innerHTML = `FAQ load failed: ${e.message}
`; } } /* ── public API ── */ export async function initFaqSidebar(mode) { const cssHref = '/static/shared/faq-sidebar.css'; if (!document.querySelector(`link[href="${cssHref}"]`)) { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = cssHref; document.head.appendChild(link); } let lang = window.I18n?.getLang?.() || localStorage.getItem(STORAGE_KEY_LANG) || 'zh'; const sidebar = buildSidebar(lang); wrapPageContent(sidebar); const collapseBtn = document.getElementById('faqCollapseBtn'); const langToggle = document.getElementById('faqLangToggle'); const content = document.getElementById('faqContent'); collapseBtn.addEventListener('click', () => { sidebar.classList.toggle('collapsed'); }); langToggle.addEventListener('click', async (e) => { const btn = e.target.closest('.faq-lang-btn'); if (!btn || btn.classList.contains('active')) return; langToggle.querySelectorAll('.faq-lang-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); lang = btn.dataset.lang; localStorage.setItem(STORAGE_KEY_LANG, lang); await loadFaq(mode, lang, content); if (window.I18n?.setLang) window.I18n.setLang(lang, true); }); await loadFaq(mode, lang, content); }