AkurAI Build
Menu

Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f

AkurAI-Framework / site/frontend/ai/components/artifacts/code_block.js

Latest change 8847e6f4a315fb27eba8cb69b87397dd08a068eb - feat(ai/artifacts): add Chart, DataTable, CodeBlock, CodeDiff ES modules by Ólafur Búi Ólafsson

/**
 * Artifact: Code Block
 * Payload: { language: string, code: string, filename?: string }
 * Renders a code card with a copy button and lightweight regex token highlighting.
 * XSS-safe: HTML-escape first, then layer colour spans onto the escaped output.
 */

function esc(s) {
  return String(s)
    .replace(/&/g, '&')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;');
}

/**
 * Token definitions — order matters.
 * Strings and comments must match first so keyword patterns don't fire inside them.
 * All regexes run against the already-escaped source, so the only HTML-special
 * characters present are the four entity sequences (&amp; &lt; &gt; &quot;).
 */
const TOKENS = [
  {
    color: '#3ddc84',
    re: /("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)/g
  },
  {
    color: '#5c647a',
    re: /(\/\/[^\n]*|\/\*[\s\S]*?\*\/|#[^\n]*)/g
  },
  {
    color: '#8a6bff',
    re: /\b(function|const|let|var|return|if|else|for|while|do|class|import|export|from|default|new|this|typeof|instanceof|async|await|try|catch|finally|throw|in|of|null|undefined|true|false|void|delete|switch|case|break|continue|yield|static|extends|super|type|interface|struct|fn|pub|mod|use|impl|trait|enum|match|where|mut|ref|self|Self|None|Some|Ok|Err|int|str|bool|float)\b/g
  },
  {
    color: '#f5b945',
    re: /\b(\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\b/g
  }
];

function highlight(code) {
  const escaped = esc(code);

  // Collect non-overlapping token ranges
  const ranges = [];
  for (const { color, re } of TOKENS) {
    re.lastIndex = 0;
    let m;
    while ((m = re.exec(escaped)) !== null) {
      const start = m.index, end = m.index + m[0].length;
      if (!ranges.some(r => r.start < end && r.end > start)) {
        ranges.push({ start, end, color, text: m[0] });
      }
    }
  }
  ranges.sort((a, b) => a.start - b.start);

  let out = '';
  let pos = 0;
  for (const r of ranges) {
    if (r.start > pos) out += escaped.slice(pos, r.start);
    out += `<span style="color:${r.color}">${r.text}</span>`;
    pos = r.end;
  }
  out += escaped.slice(pos);
  return out;
}

export const def = {
  name: 'code_block',
  category: 'Artifacts',
  title: 'Code Block',

  match(p) {
    return p != null && typeof p.code === 'string' && typeof p.language === 'string';
  },

  render(payload) {
    const { code, language, filename } = payload;

    const wrap = document.createElement('div');
    wrap.style.cssText = 'border:1px solid var(--border);border-radius:12px;overflow:hidden;background:#0c0f16';

    /* ── Header bar ── */
    const header = document.createElement('div');
    header.style.cssText = 'display:flex;align-items:center;justify-content:space-between;padding:8px 14px;border-bottom:1px solid var(--border);background:var(--panel-2)';

    const meta = document.createElement('div');
    meta.style.cssText = 'display:flex;align-items:center;gap:8px';

    const badge = document.createElement('span');
    badge.className = 'badge badge-muted';
    badge.style.fontSize = '11px';
    badge.textContent = String(language);
    meta.appendChild(badge);

    if (filename) {
      const fn = document.createElement('span');
      fn.style.cssText = 'font-family:var(--mono);font-size:12px;color:var(--muted)';
      fn.textContent = String(filename);
      meta.appendChild(fn);
    }

    header.appendChild(meta);

    const copyBtn = document.createElement('button');
    copyBtn.className = 'btn btn-sm btn-ghost';
    copyBtn.textContent = 'Copy';
    copyBtn.addEventListener('click', () => {
      navigator.clipboard.writeText(code).then(() => {
        copyBtn.textContent = 'Copied!';
        setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
      }).catch(() => {});
    });
    header.appendChild(copyBtn);
    wrap.appendChild(header);

    /* ── Code body ── */
    const pre = document.createElement('pre');
    pre.style.cssText = 'margin:0;padding:16px 18px;overflow-x:auto;background:transparent';

    const codeEl = document.createElement('code');
    codeEl.style.cssText = 'font-family:var(--mono);font-size:13px;line-height:1.7;color:#cdd6ea';
    codeEl.innerHTML = highlight(code);

    pre.appendChild(codeEl);
    wrap.appendChild(pre);

    return wrap;
  },

  example: {
    language: 'javascript',
    filename: 'greet.js',
    code: [
      'const greet = async (name) => {',
      '  // say hello',
      '  return `Hello, ${name}!`;',
      '};',
      '',
      'console.log(await greet("world"));'
    ].join('\n')
  }
};