Menu
Public repository / 91bd2a37654909c5a3aef6ddd614991bd378ccb8
AkurAI-Framework / site/frontend/ai/runtime/suggestions.js
Latest change 22a320a8e40a1822b386e2d5fd9bcf3537282876 - feat(ai): generative-UI / tool-call runtime + primitives by Ólafur Búi Ólafsson
// suggestions.js — tappable suggestion chips that fill / submit the composer.
//
// Renders a row of chips. Tapping one emits a `suggestion:pick` CustomEvent
// (detail = { text, submit }), fills a target composer input if given, and
// optionally submits. Lets the assistant offer canned next prompts.
//
// createSuggestions(items, opts) -> {
// element, // <div class="toolui-suggestions">
// setItems(items),
// }
// items = ["Tell me more", { text: "Summarize", submit: true }, ...]
// opts = {
// composer, // <input>/<textarea> to fill (optional)
// form, // <form> to submit when submit:true (optional)
// submit = false, // default submit behavior for plain-string items
// onPick(text, item),
// }
function normalizeItem(item) {
if (typeof item === "string") return { text: item };
return { text: String(item.text ?? ""), submit: item.submit };
}
function createSuggestions(items, opts) {
const o = opts || {};
const element = document.createElement("div");
element.className = "toolui-suggestions";
element.style.display = "flex";
element.style.flexWrap = "wrap";
element.style.gap = "8px";
element.setAttribute("role", "listbox");
element.setAttribute("aria-label", "Suggestions");
function pick(item) {
const submit = item.submit ?? o.submit ?? false;
const detail = { text: item.text, submit };
// Fill the composer if one was provided.
if (o.composer) {
o.composer.value = item.text;
o.composer.dispatchEvent(new Event("input", { bubbles: true }));
}
element.dispatchEvent(new CustomEvent("suggestion:pick", { detail, bubbles: true }));
document.dispatchEvent(new CustomEvent("suggestion:pick", { detail }));
if (typeof o.onPick === "function") o.onPick(item.text, item);
console.log("[runtime] suggestion:pick", detail);
// Submit the form if asked (and a form is wired up).
if (submit && o.form && typeof o.form.requestSubmit === "function") {
o.form.requestSubmit();
}
}
function render(list) {
element.textContent = "";
for (const raw of list) {
const item = normalizeItem(raw);
if (!item.text) continue;
const chip = document.createElement("button");
chip.type = "button";
chip.className = "btn btn-secondary btn-sm toolui-suggestion-chip";
chip.setAttribute("role", "option");
chip.textContent = item.text;
chip.addEventListener("click", () => pick(item));
element.appendChild(chip);
}
}
render(Array.isArray(items) ? items : []);
return {
element,
setItems: (next) => render(Array.isArray(next) ? next : []),
};
}
const ToolUiSuggestions = { createSuggestions };
if (typeof window !== "undefined") window.ToolUiSuggestions = ToolUiSuggestions;
export { createSuggestions };
export default ToolUiSuggestions;