From 84f51248506ad72aabae53f5c76f9ab8d7883c08 Mon Sep 17 00:00:00 2001 From: jason Date: Sat, 7 Mar 2026 18:33:54 -0600 Subject: [PATCH] =?UTF-8?q?docs:=20rewrite=20ReadmeModal=20as=20admin=20us?= =?UTF-8?q?age=20guide=20=E2=80=94=20feature=20map,=20workflow,=20tier=20s?= =?UTF-8?q?ystem,=20roadmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ReadmeModal.jsx | 567 ++++++++++++-------------- 1 file changed, 250 insertions(+), 317 deletions(-) diff --git a/client/src/components/ReadmeModal.jsx b/client/src/components/ReadmeModal.jsx index e2c4588..edadd0a 100644 --- a/client/src/components/ReadmeModal.jsx +++ b/client/src/components/ReadmeModal.jsx @@ -1,286 +1,145 @@ import React, { useEffect, useRef } from 'react'; // ─── Minimal Markdown → HTML renderer ──────────────────────────────────────── -// Handles: headings, bold, inline-code, fenced code blocks, tables, hr, -// unordered lists, ordered lists, and paragraphs. function mdToHtml(md) { - const lines = md.split('\n'); - const out = []; - let i = 0; - let inUl = false; - let inOl = false; - let inTable = false; - let tableHead = false; + const lines = md.split('\n'); + const out = []; + let i = 0, inUl = false, inOl = false, inTable = false; - const closeOpenLists = () => { - if (inUl) { out.push(''); inUl = false; } - if (inOl) { out.push(''); inOl = false; } - if (inTable) { out.push(''); inTable = false; tableHead = false; } + const close = () => { + if (inUl) { out.push(''); inUl = false; } + if (inOl) { out.push(''); inOl = false; } + if (inTable) { out.push(''); inTable = false; } }; - const inline = (s) => - s - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/`([^`]+)`/g, '$1'); + const inline = s => + s.replace(/&/g,'&').replace(//g,'>') + .replace(/\*\*(.+?)\*\*/g,'$1') + .replace(/`([^`]+)`/g,'$1'); while (i < lines.length) { const line = lines[i]; - // Fenced code block if (line.startsWith('```')) { - closeOpenLists(); - const lang = line.slice(3).trim(); - const codeLines = []; + close(); i++; - while (i < lines.length && !lines[i].startsWith('```')) { - codeLines.push(lines[i].replace(/&/g,'&').replace(//g,'>')); - i++; - } - out.push(`
${codeLines.join('\n')}
`); - i++; - continue; + while (i < lines.length && !lines[i].startsWith('```')) i++; + i++; continue; + } + if (/^---+$/.test(line.trim())) { close(); out.push('
'); i++; continue; } + + const hm = line.match(/^(#{1,4})\s+(.+)/); + if (hm) { + close(); + const lvl = hm[1].length; + const id = hm[2].toLowerCase().replace(/[^a-z0-9]+/g,'-'); + out.push(`${inline(hm[2])}`); + i++; continue; } - // HR - if (/^---+$/.test(line.trim())) { - closeOpenLists(); - out.push('
'); - i++; - continue; - } - - // Headings - const hMatch = line.match(/^(#{1,4})\s+(.+)/); - if (hMatch) { - closeOpenLists(); - const level = hMatch[1].length; - const id = hMatch[2].toLowerCase().replace(/[^a-z0-9]+/g, '-'); - out.push(`${inline(hMatch[2])}`); - i++; - continue; - } - - // Table row if (line.trim().startsWith('|')) { - const cells = line.trim().replace(/^\||\|$/g, '').split('|').map(c => c.trim()); + const cells = line.trim().replace(/^\||\|$/g,'').split('|').map(c=>c.trim()); if (!inTable) { - closeOpenLists(); - inTable = true; - tableHead = true; + close(); inTable = true; out.push(''); cells.forEach(c => out.push(``)); out.push(''); i++; - // skip separator row - if (i < lines.length && lines[i].trim().startsWith('|') && /^[\|\s\-:]+$/.test(lines[i])) i++; + if (i < lines.length && /^[\|\s\-:]+$/.test(lines[i])) i++; continue; } else { out.push(''); cells.forEach(c => out.push(``)); out.push(''); - i++; - continue; + i++; continue; } } - // Unordered list - const ulMatch = line.match(/^[-*]\s+(.*)/); - if (ulMatch) { - if (inTable) closeOpenLists(); - if (!inUl) { if (inOl) { out.push(''); inOl = false; } out.push(''); inUl=false; } out.push('
    '); inOl=true; } + out.push(`
  1. ${inline(ol[1])}
  2. `); + i++; continue; } - // Blank line - if (line.trim() === '') { - closeOpenLists(); - i++; - continue; - } + if (line.trim() === '') { close(); i++; continue; } - // Paragraph - closeOpenLists(); + close(); out.push(`

    ${inline(line)}

    `); i++; } - - closeOpenLists(); + close(); return out.join('\n'); } // ─── Styles ─────────────────────────────────────────────────────────────────── -const overlay = { - position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.75)', - zIndex: 2000, display: 'flex', alignItems: 'flex-start', justifyContent: 'flex-end', +const S = { + overlay: { + position:'fixed', inset:0, background:'rgba(0,0,0,0.75)', + zIndex:2000, display:'flex', alignItems:'flex-start', justifyContent:'flex-end', + }, + panel: { + background:'#111217', color:'#f8f9fa', width:'780px', maxWidth:'95vw', + height:'100vh', overflowY:'auto', boxShadow:'-4px 0 32px rgba(0,0,0,0.85)', + display:'flex', flexDirection:'column', + }, + header: { + background:'linear-gradient(135deg,#000000,#151622)', color:'white', + padding:'22px 28px', position:'sticky', top:0, zIndex:10, + borderBottom:'1px solid #222', display:'flex', alignItems:'center', + justifyContent:'space-between', + }, + closeBtn: { background:'none', border:'none', color:'white', fontSize:'22px', cursor:'pointer', lineHeight:1 }, + toc: { + background:'#0d1117', borderBottom:'1px solid #1e1f2e', + padding:'10px 32px', display:'flex', flexWrap:'wrap', gap:'4px 18px', fontSize:'11px', + }, + body: { padding:'28px 32px', flex:1, fontSize:'13px', lineHeight:'1.75' }, + footer: { padding:'14px 32px', borderTop:'1px solid #1e1f2e', fontSize:'11px', color:'#555770', textAlign:'center' }, }; -const panel = { - background: '#111217', color: '#f8f9fa', width: '760px', maxWidth: '95vw', - height: '100vh', overflowY: 'auto', boxShadow: '-4px 0 32px rgba(0,0,0,0.8)', - display: 'flex', flexDirection: 'column', -}; - -const header = { - background: 'linear-gradient(135deg, #000000, #151622)', color: 'white', - padding: '22px 28px', position: 'sticky', top: 0, zIndex: 10, - borderBottom: '1px solid #222', display: 'flex', alignItems: 'center', - justifyContent: 'space-between', -}; - -const closeBtn = { - background: 'none', border: 'none', color: 'white', - fontSize: '22px', cursor: 'pointer', lineHeight: 1, -}; - -const body = { - padding: '28px 32px', flex: 1, fontSize: '13px', lineHeight: '1.7', -}; - -// Injected -
    e.stopPropagation()}> +
    e.stopPropagation()}> {/* Header */} -
    +
    -
    - 📋 CPAS Tracker — Documentation +
    + 📋 CPAS Tracker — Admin Guide
    -
    - Admin reference · use Esc or click outside to close +
    + Feature map · workflow reference · roadmap · Esc or click outside to close
    - +
    {/* TOC strip */} -
    - {toc.filter(h => h.level <= 2).map((h) => ( +
    + {toc.map(h => (
${inline(c)}
${inline(c)}