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