// Builds docs/index.html from docs/pithos-docs.md (the Markdown stays the // source of truth and is published beside it for AI assistants). // node docs/build-docs.mjs // Handles the Markdown this page uses: headings, paragraphs, lists (one // nested level), fenced code, tables, inline code, bold, italics and links. import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const dir = path.dirname(fileURLToPath(import.meta.url)); const md = fs.readFileSync(path.join(dir, 'pithos-docs.md'), 'utf8').replace(/\r\n/g, '\n'); const esc = (s) => s.replace(/&/g, '&').replace(//g, '>'); const slug = (s) => s.toLowerCase().replace(/<[^>]+>/g, '').replace(/[`*]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); function inline(s) { const codes = []; s = s.replace(/`([^`]+)`/g, (_, c) => { codes.push(`${esc(c)}`); return `\u0000${codes.length - 1}\u0000`; }); s = esc(s) .replace(/\*\*([^*]+)\*\*/g, '$1') .replace(/(^|[\s(])\*([^*\s][^*]*)\*/g, '$1$2') .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '$1'); return s.replace(/\u0000(\d+)\u0000/g, (_, i) => codes[+i]); } const lines = md.split('\n'); const out = []; const toc = []; let i = 0; let title = 'Pithos documentation'; while (i < lines.length) { const line = lines[i]; if (!line.trim()) { i++; continue; } const fence = /^```(\w*)/.exec(line); if (fence) { const buf = []; i++; while (i < lines.length && !lines[i].startsWith('```')) buf.push(lines[i++]); i++; out.push(`
${esc(buf.join('\n'))}
`); continue; } const hd = /^(#{1,3}) (.+)$/.exec(line); if (hd) { const level = hd[1].length; const text = hd[2]; if (level === 1) { title = text; i++; continue; } const id = slug(text); toc.push({ level, id, text: inline(text) }); out.push(`${inline(text)}`); i++; continue; } if (line.startsWith('|')) { const rows = []; while (i < lines.length && lines[i].startsWith('|')) rows.push(lines[i++]); const cells = (r) => r.replace(/^\||\|$/g, '').split('|').map((c) => c.trim()); const head = cells(rows[0]); const body = rows.slice(2).map(cells); out.push(`
${head.map((c) => ``).join('')}${body.map((r) => `${r.map((c) => ``).join('')}`).join('')}
${inline(c)}
${inline(c)}
`); continue; } const li = /^(\s*)([-*]|\d+\.) (.+)$/.exec(line); if (li) { // Collect the list, with one nested level (3+ spaces of indent). const ordered = /\d/.test(li[2]); const items = []; while (i < lines.length) { const m = /^(\s*)([-*]|\d+\.) (.+)$/.exec(lines[i]); if (!m) { if (lines[i].startsWith(' ') && items.length) { items[items.length - 1].text += ' ' + lines[i].trim(); i++; continue; } break; } if (m[1].length >= 3 && items.length) (items[items.length - 1].sub ||= { ordered: /\d/.test(m[2]), items: [] }).items.push(m[3]); else items.push({ text: m[3] }); i++; } const tag = ordered ? 'ol' : 'ul'; out.push(`<${tag}>${items.map((it) => `
  • ${inline(it.text)}${it.sub ? `<${it.sub.ordered ? 'ol' : 'ul'}>${it.sub.items.map((s) => `
  • ${inline(s)}
  • `).join('')}` : ''}`).join('')}`); continue; } const para = []; while (i < lines.length && lines[i].trim() && !/^(#{1,3} |```|\||\s*([-*]|\d+\.) )/.test(lines[i])) para.push(lines[i++].trim()); out.push(`

    ${inline(para.join(' '))}

    `); } const tocHtml = toc.filter((t) => t.level === 2).map((t) => `${t.text}`).join(''); const html = ` ${esc(title)} — pithos.sia

    ${esc(title)}

    Using an AI assistant? Give it this page or the Markdown version, then describe what you want to do. Type recovery phrases and secret keys only into s3d or Pithos on your own computer, never into a chat.
    ${out.join('\n ')}
    `; fs.writeFileSync(path.join(dir, 'index.html'), html); console.log(`docs/index.html: ${toc.length} headings, ${out.length} blocks`);