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, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
/**
* 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 (& < > ").
*/
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')
}
};