AkurAI Build
Menu

Public repository / 439f0dcabf5cdf2b120c7724be895cf1df30507f

AkurAI-Framework / site/frontend/ai/components/content/message_draft.js

Latest change 67a966d4abf972b300e8c6c214a30a0279103337 - feat(ai): Artifacts content components + geo map + item carousel (AB3-C) by Ólafur Búi Ólafsson

/**
 * Message Draft — Artifacts / content component.
 * Payload: { to?: string, subject?: string, body: string }
 * Shows an editable draft card with Edit + Send/Approve actions via ctx.
 */

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

export const def = {
  name: "message_draft",
  category: "Artifacts",
  title: "Message Draft",

  match(payload) {
    return (
      payload !== null &&
      typeof payload === "object" &&
      typeof payload.body === "string" &&
      payload.body.length > 0
    );
  },

  render(payload, ctx) {
    const { to = "", subject = "", body } = payload;
    const card = document.createElement("div");
    card.className = "tc-content-draft";
    card.setAttribute("role", "region");
    card.setAttribute("aria-label", "Message Draft");

    const hasCtx = ctx && (typeof ctx.onAction === "function" || typeof ctx.sendReceipt === "function");

    card.innerHTML = `
<div class="tc-content-draft__header">
  <span class="tc-content-draft__label">Draft</span>
  ${to ? `<span class="tc-content-draft__to">To: <strong>${esc(to)}</strong></span>` : ""}
</div>
${subject ? `<div class="tc-content-draft__subject">${esc(subject)}</div>` : ""}
<div class="tc-content-draft__body" contenteditable="false" data-field="body">${esc(body)}</div>
${hasCtx ? `
<div class="tc-content-draft__actions">
  <button class="tc-content-draft__btn tc-content-draft__btn--edit" type="button">Edit</button>
  <button class="tc-content-draft__btn tc-content-draft__btn--send" type="button">Send</button>
</div>` : ""}
`;

    if (!hasCtx) return card;

    const bodyEl = card.querySelector("[data-field=body]");
    const editBtn = card.querySelector(".tc-content-draft__btn--edit");
    const sendBtn = card.querySelector(".tc-content-draft__btn--send");

    let editing = false;

    editBtn.addEventListener("click", () => {
      editing = !editing;
      bodyEl.contentEditable = editing ? "true" : "false";
      editBtn.textContent = editing ? "Done" : "Edit";
      if (editing) {
        bodyEl.focus();
        const range = document.createRange();
        range.selectNodeContents(bodyEl);
        range.collapse(false);
        const sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
      }
      if (typeof ctx.onAction === "function") {
        ctx.onAction("draft:edit", { editing });
      }
    });

    sendBtn.addEventListener("click", () => {
      const currentBody = bodyEl.textContent;
      if (typeof ctx.onAction === "function") {
        ctx.onAction("draft:send", { to, subject, body: currentBody });
      }
      if (typeof ctx.sendReceipt === "function") {
        ctx.sendReceipt({ type: "message_draft", action: "send", to, subject, body: currentBody });
      }
    });

    return card;
  },

  example: {
    to: "team@example.com",
    subject: "Sprint recap",
    body: "Hi team,\n\nHere's a quick recap of this week's sprint. We shipped the new dashboard and closed 12 issues.\n\nBest,\nOli",
  },
};