AkurAI Build
Menu

Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f

AkurAI-Framework / site/frontend/ai/components/display/terminal.js

Latest change 88d669a6b3c5464f8a947a66be0437a90424470f - feat(ai): add Display components — citation, link_preview, stats_display, terminal, weather_widget by Ólafur Búi Ólafsson

/**
 * Terminal — command + output lines + exit code → styled terminal block.
 * Payload: { command: string, lines: string[], exit_code?: number }
 */

function el(tag, cls) {
  const node = document.createElement(tag);
  if (cls) node.className = cls;
  return node;
}

export const def = {
  name: "terminal",
  category: "Display",
  title: "Terminal",

  match(payload) {
    return (
      payload !== null &&
      typeof payload === "object" &&
      typeof payload.command === "string" &&
      Array.isArray(payload.lines)
    );
  },

  render(payload) {
    const { command, lines, exit_code } = payload;

    const wrap = el("div", "tc-display-terminal");

    // Title bar
    const bar = el("div", "tc-display-terminal__bar");
    const dots = el("span", "tc-display-terminal__dots");
    dots.innerHTML =
      '<span style="background:#ff6b6b"></span>' +
      '<span style="background:#f5b945"></span>' +
      '<span style="background:#3ddc84"></span>';
    bar.appendChild(dots);
    const barLabel = el("span", "tc-display-terminal__bar-label muted small");
    barLabel.textContent = "terminal";
    bar.appendChild(barLabel);
    wrap.appendChild(bar);

    // Body (pre-formatted)
    const pre = document.createElement("pre");
    pre.className = "tc-display-terminal__body";

    // Prompt + command line
    const promptLine = document.createElement("div");
    const prompt = el("span", "tc-display-terminal__prompt");
    prompt.textContent = "$ ";
    promptLine.appendChild(prompt);
    const cmd = el("span", "tc-display-terminal__cmd");
    cmd.textContent = command; // textContent is XSS-safe
    promptLine.appendChild(cmd);
    pre.appendChild(promptLine);

    // Output lines
    for (const line of lines) {
      const lineEl = document.createElement("div");
      lineEl.className = "tc-display-terminal__line";
      lineEl.textContent = line; // XSS-safe
      pre.appendChild(lineEl);
    }

    // Exit code row (only when present and non-zero, or explicitly 0)
    if (exit_code !== undefined && exit_code !== null) {
      const exitEl = el("div", "tc-display-terminal__exit muted small");
      exitEl.textContent =
        "[exit " + String(Number(exit_code)) + "]";
      if (Number(exit_code) !== 0) {
        exitEl.style.color = "var(--danger)";
      }
      pre.appendChild(exitEl);
    }

    wrap.appendChild(pre);
    return wrap;
  },

  example: {
    command: "bun run build",
    lines: [
      "$ bun run build",
      "  Building routes...",
      "  Compiled 42 pages in 180ms",
      "  Static assets copied: 7 files",
      "  Done.",
    ],
    exit_code: 0,
  },
};