// ModuleView — renders module content with sections, code blocks, tables
function ModuleView({ module, currentSection, onSectionChange, previewMode = false }) {
  const [copied, setCopied] = React.useState(null);
  const [progress, setProgress] = React.useState(getProgress());
  const sectionRefs = React.useRef({});
  const exerciseRef = React.useRef(null);

  // Scroll to section
  React.useEffect(() => {
    if (!currentSection) return;
    if (currentSection === 'exercises') {
      if (exerciseRef.current) {
        const offset = exerciseRef.current.getBoundingClientRect().top + window.scrollY - 80;
        window.scrollTo({ top: offset, behavior: 'smooth' });
      }
      return;
    }
    if (sectionRefs.current[currentSection]) {
      const el = sectionRefs.current[currentSection];
      const offset = el.getBoundingClientRect().top + window.scrollY - 80;
      window.scrollTo({ top: offset, behavior: 'smooth' });
    }
  }, [currentSection]);

  // Intersection observer
  React.useEffect(() => {
    if (!module) return;
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => { if (entry.isIntersecting) onSectionChange(entry.target.dataset.sectionId); });
    }, { rootMargin: '-20% 0px -70% 0px' });
    Object.values(sectionRefs.current).forEach(el => { if (el) observer.observe(el); });
    return () => observer.disconnect();
  }, [module]);

  function handleMarkComplete(sectionId) {
    markSectionComplete(module.id, sectionId);
    setProgress(getProgress());
  }

  function handleCopy(code, id) {
    navigator.clipboard.writeText(code).then(() => { setCopied(id); setTimeout(() => setCopied(null), 1800); });
  }

  if (!module) return null;

  const accentColor = module.accent === 'tech' ? '#3B82F6' : '#C9A84C';
  const pct = getModuleProgress(module.id);
  const allDone = pct === 100;
  const completedSections = Object.keys(progress[module.id] || {}).length;

  // Estimate reading time (~200 words/min, code counts as 50 wpm equivalent)
  function readingTime(sec) {
    const words = (sec.content || '').split(/\s+/).length + (sec.code?.content || '').split(/\s+/).length * 0.25;
    return Math.max(1, Math.round(words / 200));
  }

  return (
    <div style={{ maxWidth: 820, margin: '0 auto', padding: '40px 32px 120px' }}>

      {/* Module header */}
      <div style={{ borderBottom:'1px solid #1a1a1a', paddingBottom:36, marginBottom:40 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:16 }}>
          <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.22em', color:accentColor, textTransform:'uppercase' }}>
            Module {module.num}
          </span>
          <div style={{ display:'flex', gap:14, alignItems:'center' }}>
            <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, color:'#444', letterSpacing:'0.14em', textTransform:'uppercase' }}>{module.duration}</span>
            <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, color:'#444', letterSpacing:'0.14em', textTransform:'uppercase' }}>{module.difficulty}</span>
          </div>
        </div>
        <h1 style={{ fontFamily:'Syne,sans-serif', fontWeight:700, fontSize:'clamp(28px,4vw,48px)', letterSpacing:'-0.03em', lineHeight:0.95, color:'#fff', margin:'0 0 14px' }}>
          {module.title}
        </h1>
        <p style={{ fontFamily:'Inter,sans-serif', fontSize:14, color:'#555', margin:'0 0 24px', lineHeight:1.6 }}>{module.subtitle}</p>
        {module.prereqs && (
          <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.14em', color:'#333', marginBottom:20 }}>
            <span style={{ color:'#444' }}>PREREQS:</span> {module.prereqs}
          </div>
        )}

        {/* Progress bar + stats */}
        <div style={{ display:'flex', alignItems:'center', gap:16, marginBottom:12 }}>
          <div style={{ flex:1, height:3, background:'#111', borderRadius:2, overflow:'hidden' }}>
            <div style={{ height:'100%', width:`${pct}%`, background:accentColor, transition:'width 0.4s ease', borderRadius:2 }} />
          </div>
          <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, color: allDone ? accentColor : '#444', letterSpacing:'0.14em', flexShrink:0 }}>
            {allDone ? '✓ COMPLETE' : `${completedSections} / ${module.sections.length} sections`}
          </span>
        </div>
      </div>

      {/* Learning outcomes */}
      <div style={{ background:'#080808', border:`1px solid #1a1a1a`, borderLeft:`3px solid ${accentColor}`, padding:'20px 24px', marginBottom:40 }}>
        <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.2em', color:accentColor, textTransform:'uppercase', marginBottom:14 }}>
          Learning Outcomes — {module.outcomes.length} skills
        </div>
        <ol style={{ margin:0, paddingLeft:18 }}>
          {module.outcomes.map((o, i) => (
            <li key={i} style={{ fontFamily:'Inter,sans-serif', fontSize:13, color:'#888', lineHeight:1.75, marginBottom:2 }}>{o}</li>
          ))}
        </ol>
      </div>

      {/* Sections */}
      {module.sections.map((sec, idx) => {
        const isDone = progress[module.id]?.[sec.id];
        const mins = readingTime(sec);
        return (
          <div key={sec.id} data-section-id={sec.id}
            ref={el => sectionRefs.current[sec.id] = el}
            style={{ marginBottom:52, paddingTop:4 }}>

            {/* Section header */}
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:18, paddingBottom:12, borderBottom:'1px solid #0d0d0d' }}>
              <div style={{ display:'flex', alignItems:'baseline', gap:10 }}>
                <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, color:'#2a2a2a', letterSpacing:'0.14em' }}>
                  {String(idx+1).padStart(2,'0')}
                </span>
                <h2 style={{ fontFamily:'Syne,sans-serif', fontWeight:600, fontSize:20, letterSpacing:'-0.02em', color:'#fff', margin:0 }}>
                  {sec.title}
                </h2>
                <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:8, color:'#2a2a2a', letterSpacing:'0.1em' }}>{mins} min</span>
              </div>
              <button onClick={() => handleMarkComplete(sec.id)} style={{
                fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.14em', textTransform:'uppercase',
                background: isDone ? `${accentColor}18` : 'none',
                border:`1px solid ${isDone ? accentColor : '#222'}`,
                color: isDone ? accentColor : '#333',
                padding:'5px 12px', cursor:'pointer', transition:'all 0.15s', flexShrink:0
              }}>
                {isDone ? '✓ Done' : 'Mark done'}
              </button>
            </div>

            {sec.content && <RichText text={sec.content} accentColor={accentColor} />}
            {sec.table && <DataTable table={sec.table} accentColor={accentColor} />}
            {sec.code && <CodeBlock code={sec.code.content} language={sec.code.language} id={`${sec.id}-code`} copied={copied} onCopy={handleCopy} accentColor={accentColor} />}
          </div>
        );
      })}

      {/* ── EXERCISES ── */}
      {module.exercises && module.exercises.length > 0 && (
        <div ref={exerciseRef} style={{ marginTop:16, paddingTop:40, borderTop:`1px solid #111` }}>
          <div style={{ display:'flex', alignItems:'baseline', gap:12, marginBottom:8 }}>
            <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.22em', color:accentColor, textTransform:'uppercase' }}>
              Exercises
            </div>
            <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, color:'#333', letterSpacing:'0.12em' }}>
              {module.exercises.filter((_,i) => { try { return JSON.parse(localStorage.getItem('bwc_exercises')||'{}')[`${module.id}_${i}`]; } catch { return false; } }).length}/{module.exercises.length} completed
            </div>
          </div>
          <div style={{ fontFamily:'Inter,sans-serif', fontSize:13, color:'#555', marginBottom:20, lineHeight:1.6 }}>
            Click any exercise to expand hints, starter code, and AI-powered code review.
          </div>
          <div style={{ display:'flex', flexDirection:'column', gap:2 }}>
            {module.exercises.map((ex, i) => (
              <ExerciseCard key={i} ex={ex} exIdx={i} moduleId={module.id} accentColor={accentColor} previewMode={previewMode} />
            ))}
          </div>
        </div>
      )}

      {/* Module completion card */}
      {pct === 100 && (
        <div style={{ marginTop:48, background:'#080808', border:`1px solid ${accentColor}33`, padding:'28px 28px', textAlign:'center' }}>
          <div style={{ fontFamily:'Syne,sans-serif', fontWeight:700, fontSize:28, color:accentColor, letterSpacing:'-0.02em', marginBottom:8 }}>
            Module Complete ✓
          </div>
          <div style={{ fontFamily:'Inter,sans-serif', fontSize:14, color:'#666', marginBottom:20 }}>
            You've completed all sections of {module.title}.
          </div>
          {!previewMode && MODULES_DATA.findIndex(m => m.id === module.id) < MODULES_DATA.length - 1 && (
            <button onClick={() => {
              const next = MODULES_DATA[MODULES_DATA.findIndex(m => m.id === module.id) + 1];
              window.dispatchEvent(new CustomEvent('selectModule', { detail: next }));
            }} style={{
              fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.18em', textTransform:'uppercase',
              background:accentColor, color:'#000', border:'none', padding:'12px 24px', cursor:'pointer', fontWeight:600
            }}>
              Next Module →
            </button>
          )}
          {previewMode && (
            <a href="/#pricing" style={{
              display:'inline-block', fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.18em', textTransform:'uppercase',
              background:accentColor, color:'#000', padding:'12px 24px', fontWeight:600, textDecoration:'none'
            }}>
              Unlock modules 2–12 →
            </a>
          )}
        </div>
      )}

      {/* Next/prev module nav */}
      <NextModuleNav module={module} accentColor={accentColor} previewMode={previewMode} />
    </div>
  );
}

// ── Rich text renderer
function RichText({ text, accentColor }) {
  const lines = text.split('\n');
  const elements = [];
  let i = 0;

  while (i < lines.length) {
    const line = lines[i];

    // Code fence
    if (line.trim().startsWith('```')) {
      const lang = line.trim().slice(3);
      const codeLines = [];
      i++;
      while (i < lines.length && !lines[i].trim().startsWith('```')) { codeLines.push(lines[i]); i++; }
      elements.push(
        <pre key={i} style={{ background:'#080808', border:'1px solid #1a1a1a', borderLeft:`2px solid ${accentColor}44`, padding:'16px 18px', margin:'16px 0', overflowX:'auto' }}>
          <code style={{ fontFamily:'JetBrains Mono,monospace', fontSize:12, color:'#ccc', lineHeight:1.65 }}>{codeLines.join('\n')}</code>
        </pre>
      );
      i++; continue;
    }

    // Blockquote — callout box
    if (line.startsWith('> ')) {
      const calloutLines = [];
      while (i < lines.length && lines[i].startsWith('> ')) { calloutLines.push(lines[i].slice(2)); i++; }
      const calloutText = calloutLines.join(' ');
      const isNote = calloutText.match(/^(NOTE|TIP|INFO):/i);
      const isWarn = calloutText.match(/^(WARNING|CAUTION|IMPORTANT):/i);
      const bg = isWarn ? '#1a0a00' : '#0a0d14';
      const border = isWarn ? '#EF4444' : accentColor;
      const label = isWarn ? 'Warning' : 'Note';
      elements.push(
        <div key={i} style={{ background:bg, borderLeft:`3px solid ${border}`, padding:'14px 18px', margin:'16px 0' }}>
          <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.18em', color:border, textTransform:'uppercase', marginBottom:6 }}>{label}</div>
          <div style={{ fontFamily:'Inter,sans-serif', fontSize:13, color:'#999', lineHeight:1.65 }}>
            <InlineFormat text={calloutText.replace(/^(NOTE|TIP|INFO|WARNING|CAUTION|IMPORTANT):\s*/i,'')} accentColor={accentColor} />
          </div>
        </div>
      );
      continue;
    }

    // Markdown table
    if (line.trim().startsWith('|')) {
      const tableLines = [];
      while (i < lines.length && lines[i].trim().startsWith('|')) { tableLines.push(lines[i]); i++; }
      const parseRow = (row) => row.split('|').filter((_,idx,arr) => idx>0 && idx<arr.length-1).map(c=>c.trim());
      const headers = parseRow(tableLines[0]);
      const dataRows = tableLines.slice(2).map(parseRow);
      elements.push(
        <div key={i} style={{ margin:'16px 0', overflowX:'auto' }}>
          <table style={{ width:'100%', borderCollapse:'collapse', border:'1px solid #1a1a1a' }}>
            <thead>
              <tr style={{ background:'#0d0d0d' }}>
                {headers.map((h,hi) => <th key={hi} style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.14em', textTransform:'uppercase', color:accentColor, padding:'10px 14px', textAlign:'left', borderBottom:'1px solid #1a1a1a', whiteSpace:'nowrap' }}>{h}</th>)}
              </tr>
            </thead>
            <tbody>
              {dataRows.map((row,ri) => (
                <tr key={ri} style={{ background: ri%2===0 ? '#000' : '#050505' }}>
                  {row.map((cell,ci) => <td key={ci} style={{ fontFamily: ci===0 ? 'JetBrains Mono,monospace' : 'Inter,sans-serif', fontSize: ci===0 ? 11 : 13, color: ci===0 ? '#aaa' : '#777', padding:'10px 14px', borderBottom:'1px solid #0d0d0d' }}>{cell}</td>)}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      );
      continue;
    }

    // H3
    if (line.startsWith('### ')) { elements.push(<h3 key={i} style={{ fontFamily:'Syne,sans-serif', fontWeight:600, fontSize:16, color:'#fff', margin:'28px 0 12px', letterSpacing:'-0.01em' }}>{line.slice(4)}</h3>); i++; continue; }

    // H2
    if (line.startsWith('## ')) { elements.push(<h2 key={i} style={{ fontFamily:'Syne,sans-serif', fontWeight:600, fontSize:20, color:'#fff', margin:'32px 0 14px', letterSpacing:'-0.02em' }}>{line.slice(3)}</h2>); i++; continue; }

    // Bullets
    if (line.startsWith('- ') || line.startsWith('* ')) {
      const bulletLines = [];
      while (i < lines.length && (lines[i].startsWith('- ') || lines[i].startsWith('* '))) { bulletLines.push(lines[i].slice(2)); i++; }
      elements.push(
        <ul key={i} style={{ margin:'8px 0 16px', paddingLeft:20 }}>
          {bulletLines.map((bl,bi) => <li key={bi} style={{ fontFamily:'Inter,sans-serif', fontSize:13, color:'#888', lineHeight:1.75, marginBottom:4 }}><InlineFormat text={bl} accentColor={accentColor} /></li>)}
        </ul>
      );
      continue;
    }

    // Numbered list
    if (/^\d+\.\s/.test(line)) {
      const numLines = [];
      while (i < lines.length && /^\d+\.\s/.test(lines[i])) { numLines.push(lines[i].replace(/^\d+\.\s/,'')); i++; }
      elements.push(
        <ol key={i} style={{ margin:'8px 0 16px', paddingLeft:20 }}>
          {numLines.map((nl,ni) => <li key={ni} style={{ fontFamily:'Inter,sans-serif', fontSize:13, color:'#888', lineHeight:1.75, marginBottom:4 }}><InlineFormat text={nl} accentColor={accentColor} /></li>)}
        </ol>
      );
      continue;
    }

    // Horizontal rule
    if (line.trim() === '---') { elements.push(<hr key={i} style={{ border:'none', borderTop:'1px solid #111', margin:'24px 0' }} />); i++; continue; }

    // Empty line
    if (line.trim() === '') { i++; continue; }

    // Paragraph
    elements.push(
      <p key={i} style={{ fontFamily:'Inter,sans-serif', fontSize:14, color:'#888', lineHeight:1.8, margin:'0 0 14px' }}>
        <InlineFormat text={line} accentColor={accentColor} />
      </p>
    );
    i++;
  }
  return <div>{elements}</div>;
}

function InlineFormat({ text, accentColor }) {
  const parts = [];
  let remaining = text, key = 0;
  while (remaining.length > 0) {
    const boldIdx = remaining.indexOf('**');
    const codeIdx = remaining.indexOf('`');
    if (boldIdx === -1 && codeIdx === -1) { parts.push(<React.Fragment key={key++}>{remaining}</React.Fragment>); break; }
    if ((boldIdx !== -1 && boldIdx < (codeIdx === -1 ? Infinity : codeIdx))) {
      const end = remaining.indexOf('**', boldIdx+2);
      if (end === -1) { parts.push(<React.Fragment key={key++}>{remaining}</React.Fragment>); break; }
      if (boldIdx > 0) parts.push(<React.Fragment key={key++}>{remaining.slice(0,boldIdx)}</React.Fragment>);
      parts.push(<strong key={key++} style={{ color:'#ddd', fontWeight:600 }}>{remaining.slice(boldIdx+2,end)}</strong>);
      remaining = remaining.slice(end+2);
    } else if (codeIdx !== -1) {
      const end = remaining.indexOf('`', codeIdx+1);
      if (end === -1) { parts.push(<React.Fragment key={key++}>{remaining}</React.Fragment>); break; }
      if (codeIdx > 0) parts.push(<React.Fragment key={key++}>{remaining.slice(0,codeIdx)}</React.Fragment>);
      parts.push(<code key={key++} style={{ fontFamily:'JetBrains Mono,monospace', fontSize:12, color:accentColor, background:'#111', padding:'1px 5px' }}>{remaining.slice(codeIdx+1,end)}</code>);
      remaining = remaining.slice(end+1);
    } else { parts.push(<React.Fragment key={key++}>{remaining}</React.Fragment>); break; }
  }
  return <>{parts}</>;
}

function CodeBlock({ code, language, id, copied, onCopy, accentColor }) {
  const lines = code.split('\n');
  return (
    <div style={{ margin:'20px 0', border:'1px solid #1a1a1a', background:'#060606' }}>
      <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'8px 16px', borderBottom:'1px solid #0d0d0d', background:'#0a0a0a' }}>
        <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.2em', color:'#333', textTransform:'uppercase' }}>{language}</span>
        <div style={{ display:'flex', alignItems:'center', gap:16 }}>
          <span style={{ fontFamily:'JetBrains Mono,monospace', fontSize:8, color:'#2a2a2a' }}>{lines.length} lines</span>
          <button onClick={() => onCopy(code, id)} style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.16em', textTransform:'uppercase', background:'none', border:'none', color: copied===id ? accentColor : '#444', cursor:'pointer', transition:'color 0.2s' }}>
            {copied===id ? '✓ copied' : 'copy'}
          </button>
        </div>
      </div>
      <div style={{ display:'flex', overflow:'auto' }}>
        {/* Line numbers */}
        <div style={{ padding:'16px 12px', background:'#050505', borderRight:'1px solid #0d0d0d', userSelect:'none', flexShrink:0 }}>
          {lines.map((_,i) => <div key={i} style={{ fontFamily:'JetBrains Mono,monospace', fontSize:11, color:'#2a2a2a', lineHeight:'1.65', textAlign:'right', minWidth:24 }}>{i+1}</div>)}
        </div>
        <pre style={{ margin:0, padding:'16px 20px', flex:1, overflowX:'auto' }}>
          <code style={{ fontFamily:'JetBrains Mono,monospace', fontSize:13, color:'#c9d1d9', lineHeight:1.65, display:'block' }}>{code}</code>
        </pre>
      </div>
    </div>
  );
}

function DataTable({ table, accentColor }) {
  return (
    <div style={{ margin:'20px 0', overflowX:'auto' }}>
      <table style={{ width:'100%', borderCollapse:'collapse', border:'1px solid #1a1a1a' }}>
        <thead>
          <tr style={{ background:'#0d0d0d' }}>
            {table.headers.map((h,i) => <th key={i} style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.14em', textTransform:'uppercase', color:accentColor, padding:'10px 14px', textAlign:'left', borderBottom:'1px solid #1a1a1a', whiteSpace:'nowrap' }}>{h}</th>)}
          </tr>
        </thead>
        <tbody>
          {table.rows.map((row,ri) => (
            <tr key={ri} style={{ background: ri%2===0 ? '#000' : '#050505' }}>
              {row.map((cell,ci) => <td key={ci} style={{ fontFamily: ci===0 ? 'JetBrains Mono,monospace' : 'Inter,sans-serif', fontSize: ci===0 ? 11 : 13, color: ci===0 ? '#aaa' : '#777', padding:'10px 14px', borderBottom:'1px solid #0d0d0d' }}>{cell}</td>)}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function NextModuleNav({ module, accentColor, previewMode = false }) {
  const idx = MODULES_DATA.findIndex(m => m.id === module.id);
  const prev = idx > 0 ? MODULES_DATA[idx-1] : null;
  const next = idx < MODULES_DATA.length-1 ? MODULES_DATA[idx+1] : null;
  if (previewMode) {
    return (
      <div style={{ marginTop:64, background:'#111', border:'1px solid #1a1a1a', padding:'22px 24px', textAlign:'center' }}>
        <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.18em', color:'#666', textTransform:'uppercase', marginBottom:10 }}>Full course</div>
        <div style={{ fontFamily:'Inter,sans-serif', fontSize:14, color:'#888', marginBottom:16, lineHeight:1.6 }}>
          Modules 2–12 include agents, RAG, deployment, industry tracks, and more.
        </div>
        <a href="/#pricing" style={{ display:'inline-block', fontFamily:'JetBrains Mono,monospace', fontSize:10, letterSpacing:'0.16em', textTransform:'uppercase', background:accentColor, color:'#000', padding:'12px 22px', fontWeight:600, textDecoration:'none' }}>
          Get lifetime access →
        </a>
      </div>
    );
  }
  return (
    <div style={{ display:'grid', gridTemplateColumns: prev && next ? '1fr 1fr' : '1fr', gap:2, marginTop:64, background:'#111', border:'1px solid #111' }}>
      {prev && <button onClick={() => window.dispatchEvent(new CustomEvent('selectModule',{detail:prev}))} style={{ background:'#000', border:'none', cursor:'pointer', padding:'20px 22px', textAlign:'left' }}>
        <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.14em', color:'#444', textTransform:'uppercase', marginBottom:6 }}>← Previous · {prev.num}</div>
        <div style={{ fontFamily:'Syne,sans-serif', fontWeight:600, fontSize:14, color:'#777' }}>{prev.title}</div>
      </button>}
      {next && <button onClick={() => window.dispatchEvent(new CustomEvent('selectModule',{detail:next}))} style={{ background:'#000', border:'none', cursor:'pointer', padding:'20px 22px', textAlign: prev ? 'right' : 'left' }}>
        <div style={{ fontFamily:'JetBrains Mono,monospace', fontSize:9, letterSpacing:'0.14em', color:accentColor, textTransform:'uppercase', marginBottom:6 }}>Next · {next.num} →</div>
        <div style={{ fontFamily:'Syne,sans-serif', fontWeight:600, fontSize:14, color:'#fff' }}>{next.title}</div>
      </button>}
    </div>
  );
}

Object.assign(window, { ModuleView, RichText, InlineFormat, CodeBlock, DataTable, NextModuleNav });
