Skip to main content

UI Tools

Ov/erview

The embed widget provides tools that can run with AI context or arguments, enabling you to display information or manipulate the DOM. The widget includes built-in tools accessible via window.cmndBuiltInTools and supports custom tool creation.

Built-in Tools

The following tools come pre-configured with the embed widget by default:

Tool NameDescription
show_formDisplays a form based on a provided JSON schema
capture_signatureCaptures a user's signature
show_ratingDisplays a rating input widget
show_video_playerDisplays a video player
show_image_viewerDisplays an image viewer

These tools can be triggered by providing a prompt related to their functionality. For example, asking "Show me a login form" will render a login form.

Custom UI Tools

Custom UI tools allow you to extend the widget's functionality with your own tools tailored to specific use cases. These can override the default built-in tools or work alongside them.

warning

Providing custom UI tools overwrites the built-in tools, but you can extend your custom UI tools with the built-in tools using window.cmndBuiltInTools.

Defining a Custom UI Tool

Each custom tool must have a functionType set to ui and follow a consistent structure.

Key Properties of a Custom UI Tool

PropertyDescriptionExample
nameUnique identifier for the tool"change_background_color"
descriptionShort description of what the tool does"Changes the background color of the page"
dangerousIndicates if the tool performs potentially harmful actionsfalse
associatedCommandsAdditional commands required by the tool[]
prerequisitesPrerequisites required by the tool[]
argumentSchemaDefines required arguments in JSON schema formatSee example below
rerunWhether the tool can be rerun with the same parameterstrue
rerunWithDifferentParametersWhether the tool can be rerun with different parameterstrue
capturesUserInputWhether the tool captures input from the usertrue
runCmdFunction implementing the tool's functionalitySee example below

Example: Custom UI Tool Definition

{
name: "change_background_color", // Unique tool name
description: "Changes the background color of the page", // Tool description
category: "UI", // Main category
subcategory: "BG", // Subcategory
functionType: "ui", // Function type (always "ui")
dangerous: false, // Safe tool indicator
associatedCommands: [], // Additional commands (if any)
prerequisites: [], // Prerequisites (if any)
argumentSchema: { // Arguments required by the tool
type: "object",
properties: {
bgColor: {
type: "string", // The background color argument
minLength: 1, // Minimum length validation
errorMessage: {
required: "Background Color is required", // Error message if missing
},
},
},
required: ["bgColor"], // Required arguments
},
rerun: true, // Allows rerunning the tool
rerunWithDifferentParameters: true, // Allows rerunning with different inputs
capturesUserInput: true, // Captures user input
runCmd: ({ functionArgs }, ref) => {
const { bgColor } = functionArgs;
const messageContainer = ref;

if (messageContainer) {
messageContainer.innerHTML = `Background color changed to ${bgColor}`;
}

document.body.style.backgroundColor = bgColor; // Change background color
},
}

runCmd Function Parameters

The runCmd function receives the following parameters:

ParameterDescription
functionArgsContains the tool's arguments defined in the schema
captureResultsCallback function that must be called with input captured from the user
previousRunResultsIf the tool was run previously, contains the string captureResults was called with
refReference to the DOM element where you can render UI elements

Usage

To register custom UI tools, use the registerTools command:

window.cmndChat("registerTools", {
ui_tools: [
{
name: "change_background_color",
// ... other values
},
{
name: "navigate_to_page",
//... other values
},
],
});

To extend custom UI tools with built-in tools:

window.cmndChat("registerTools", {
ui_tools: [
window.cmndBuiltInTools.capture_signature,
window.cmndBuiltInTools.show_form,
{
name: "change_background_color",
// ... other values
},
{
name: "navigate_to_page",
//... other values
},
],
});

Types of Custom UI Tools

There are four variations of UI tools you can implement:

TypeDescriptionRenderingUser InputParameters Used
Type 1Rendered in chat box without capturing inputYesNoref
Type 2Rendered in chat box with input captureYesYesref, captureResults
Type 3Not rendered in chat box without capturing inputNoNofunctionArgs
Type 4Not rendered in chat box with input captureNoYesfunctionArgs, captureResults

Example: Type 1 - Rendered in chat box without capturing input

{
name: "plot_graph",
description: "Plots a graph on the page using Chart.js",
dangerous: false,
associatedCommands: [],
prerequisites: [],
argumentSchema: {
type: "object",
properties: {
type: {
type: "string",
enum: ["line", "bar", "pie", "doughnut"],
errorMessage: {
required: "Graph type is required",
enum:
"Graph type must be one of: line, bar, pie, or doughnut",
},
},
data: {
type: "object",
properties: {
labels: {
type: "array",
items: { type: "string" },
errorMessage: {
required: "Labels for the graph are required",
},
},
datasets: {
type: "array",
items: {
type: "object",
properties: {
label: { type: "string" },
data: {
type: "array",
items: { type: "number" },
errorMessage: {
required: "Dataset values are required",
},
},
backgroundColor: {
type: "array",
items: { type: "string" },
},
borderColor: {
type: "array",
items: { type: "string" },
},
borderWidth: { type: "number" },
},
},
},
},
required: ["labels", "datasets"],
},
},
required: ["type", "data"],
},
rerun: true,
rerunWithDifferentParameters: true,
capturesUserInput: true,
runCmd: (
{ functionArgs, previousRunResults, captureResults },
ref
) => {
const { type, data } = functionArgs;

// Check if Chart.js is already loaded
if (!window.Chart) {
console.log("Chart.js not found. Loading dynamically...");

// Create a script element to load Chart.js
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/chart.js";
script.onload = () => {
console.log("Chart.js loaded successfully.");
renderChart();
};
script.onerror = () => {
console.error("Failed to load Chart.js.");
};

document.head.appendChild(script);
} else {
renderChart();
}

function renderChart() {
// Ensure the ref element exists
if (!ref) {
console.error(
"No reference element provided for the chart."
);
return;
}

// Check if the ref element already contains a canvas, if so, reuse it
let canvas = ref.querySelector("canvas");
if (!canvas) {
// Create a new canvas element
canvas = document.createElement("canvas");
ref.appendChild(canvas);
}

// Destroy any existing chart on the canvas
if (ref.chartInstance) {
ref.chartInstance.destroy();
}

// Render the chart using Chart.js
const ctx = canvas.getContext("2d");
ref.chartInstance = new Chart(ctx, {
type,
data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top",
},
},
},
});

if (!previousRunResults) {
captureResults(
"Graph successfully plotted inside the provided ref."
);
}
}
},
}

Example: Type 2 - Rendered in chat box with input capture

{
name: "capture_user_name",
description: "Renders a text field to capture the user's name",
dangerous: false,
associatedCommands: [],
prerequisites: [],
argumentSchema: {
type: "object",
properties: {},
},
rerun: true,
rerunWithDifferentParameters: true,
capturesUserInput: true,
runCmd: ({ previousRunResults, captureResults }, ref) => {
// Ensure the ref element exists
if (!ref) {
console.error("No reference element provided.");
return;
}

// Clear the content of the ref
ref.innerHTML = "";

if (previousRunResults) {
// Display the user's name if previous results exist
const greeting = document.createElement("span");
greeting.textContent = `Hello, ${previousRunResults}`;
greeting.style.fontSize = "1.2rem";
greeting.style.fontWeight = "bold";
ref.appendChild(greeting);
} else {
// Create a container for the input field and button
const container = document.createElement("div");
container.style.margin = "1rem";

// Create a label and input field
const input = document.createElement("input");
input.type = "text";
input.placeholder = "Enter your name";
input.style.marginRight = "0.5rem";

// Create a submit button
const button = document.createElement("button");
button.textContent = "Submit";
button.style.padding = "0.5rem 1rem";

// Append elements to the container
container.appendChild(input);
container.appendChild(button);
ref.appendChild(container);

// Add event listener for the submit button
button.addEventListener("click", () => {
const name = input.value.trim();
if (name) {
captureResults(name);

// Replace the input and button with the greeting
ref.innerHTML = "";
const greeting = document.createElement("span");
greeting.textContent = `Hello, ${name}`;
greeting.style.fontSize = "1.2rem";
greeting.style.fontWeight = "bold";
ref.appendChild(greeting);
} else {
alert("Please enter a name.");
}
});
}
}
}

Example: Type 3 - Not rendered in chat box without capturing input

{
name: "change_background_color",
description: "Changes the background color of the page",
category: "UI",
subcategory: "BG",
functionType: "ui",
dangerous: false,
associatedCommands: [],
prerequisites: [],
argumentSchema: {
type: "object",
properties: {
bgColor: {
type: "string",
minLength: 1,
errorMessage: {
required: "Background Color is required",
},
},
},
required: ["bgColor"],
},
rerun: true,
rerunWithDifferentParameters: true,
capturesUserInput: false,
runCmd: (
{ functionArgs, previousRunResults, captureResults },
ref
) => {
const { bgColor } = functionArgs;
if (!previousRunResults) {
captureResults(`Background color changed to ${bgColor}`);
}
const messageContainer = ref;
if (messageContainer) {
console.log("we got message container", messageContainer);
messageContainer.innerHTML = `Background color changed to ${bgColor}`;
}
document.body.style.backgroundColor = bgColor;
}
}

Example: Type 4 - Not rendered in chat box with input capture

{
name: "show_prompt_window",
description:
"Prompts the user with a message and captures their input",
category: "UI",
subcategory: "Prompt",
functionType: "ui",
dangerous: false,
associatedCommands: [],
prerequisites: [],
argumentSchema: {
type: "object",
properties: {
message: {
type: "string",
minLength: 1,
errorMessage: {
required: "Prompt message is required",
},
},
},
required: ["message"],
},
rerun: true,
rerunWithDifferentParameters: true,
capturesUserInput: true,
runCmd: (
{ functionArgs, previousRunResults, captureResults },
ref
) => {
if (previousRunResults) {
return;
}
const { message } = functionArgs;

// Prompt the user with the provided message
const userInput = prompt(message);

// If user input is captured, store the result
if (userInput !== null) {
captureResults(`User entered: ${userInput}`);
} else {
captureResults("User cancelled the prompt.");
}

// If a reference element is provided, display the user input there
const messageContainer = ref;
if (messageContainer) {
messageContainer.innerHTML =
userInput !== null
? `User input: ${userInput}`
: "Prompt was cancelled by the user.";
}

// Return the user input for further processing
return userInput;
}
}