Execute and Render Action
How to trigger Chatterblocks
After your execute(params) resolves, you also implement render(data) to return a UI component (Chatterblock) that the agent will embed directly in the chat stream. Users can interact with that component, and you still return data back to the agent.
When using an input or update display component like Chatterblock awaitUserInput should be true
Render Options
Parameter | Type | Description |
---|---|---|
data | any | The value returned by your execute handler (e.g. an object or array of results). |
host | HTMLElement | The container element where you should append your custom UI nodes (forms, cards, lists, etc.). |
header | HTMLElement | A header element you can style or populate with a title, instructions, or status messages. |
callback | (value: string, disableOnSubmit?: boolean) => void | Call this when the user submits. Pass the return value to the agent. By default, disableOnSubmit is true (the SDK auto-disable your UI after submit). Set it to false to not disable the UI component after submit. |
cancel | () => void | Call this if the user aborts, so the agent resumes without new data. A default “Cancel” control is also rendered in the chat U |
eucera("when", "ready", () => {
eucera.agent({ /* …common setup… */ })
.addActionHandlers({
show_task: {
// 1. Fetch the task by ID
execute: async (params) => {
const { taskId } = params;
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${taskId}`
);
const data = await response.json();
// Return the task object to both agent and renderer
return data;
},
// 2. Tell the agent to wait for user input before continuing
awaitUserInput: true,
// 3. Render an editable card UI
render: (task, host, header, callback, cancel) => {
const style = document.createElement('style');
style.textContent = `
.task-card {
border: 1px solid #e3e3e3;
border-radius: 8px;
margin-top: 8px;
font-family: sans-serif;
overflow: hidden;
}
.task-card-header {
background-color: #f5f5f5;
padding: 12px 16px;
font-weight: bold;
}
.task-card-body {
padding: 16px;
}
.task-card-body label {
display: block;
margin-bottom: 4px;
font-size: 14px;
}
.task-card-body input,
.task-card-body textarea {
width: 100%;
margin-bottom: 12px;
padding: 8px;
box-sizing: border-box;
font-size: 14px;
}
.task-card-footer {
padding: 12px 16px;
text-align: right;
background-color: #fafafa;
}
.task-card-footer button {
margin-left: 8px;
padding: 8px 16px;
cursor: pointer;
}
`;
header.appendChild(style);
// 2. Build the card container
const card = document.createElement('div');
card.className = 'task-card';
// Header section (title)
const cardHeader = document.createElement('div');
cardHeader.className = 'task-card-header';
cardHeader.textContent = `Edit Task #${task.id}`;
// Body section (form fields)
const body = document.createElement('div');
body.className = 'task-card-body';
const titleLabel = document.createElement('label');
titleLabel.textContent = 'Title:';
const titleInput = document.createElement('input');
titleInput.type = 'text';
titleInput.value = task.title;
const bodyLabel = document.createElement('label');
bodyLabel.textContent = 'Body:';
const bodyInput = document.createElement('textarea');
bodyInput.rows = 4;
bodyInput.value = task.body;
body.append(titleLabel, titleInput, bodyLabel, bodyInput);
// Footer section (buttons)
const footer = document.createElement('div');
footer.className = 'task-card-footer';
const saveBtn = document.createElement('button');
saveBtn.textContent = 'Save';
saveBtn.onclick = () => {
callback({
id: task.id,
title: titleInput.value,
body: bodyInput.value,
});
};
const cancelBtn = document.createElement('button');
cancelBtn.textContent = 'Cancel';
cancelBtn.onclick = cancel;
footer.append(cancelBtn, saveBtn);
// 3. Assemble and mount
card.append(cardHeader, body, footer);
host.appendChild(card);
}
}
});
});
Updated 14 days ago