useUIChatCommand
The useUIChatCommand hook allows you to register UI chat commands that execute directly on the client. These commands bypass the LLM and execute immediately when typed, perfect for UI actions, settings changes, and local operations.
Syntax
interface UIChatCommand {
name: string;
description: string;
parameters: ZodSchema;
execute: (params: unknown) => void | Promise<void>;
}
function useUIChatCommand(command: Omit<UIChatCommand, 'type'>): voidParameters
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique command identifier (used with / prefix) |
description | string | Yes | Clear description for the command |
parameters | ZodSchema | Yes | Zod schema defining the command’s parameters |
execute | (params: unknown) => void | Promise<void> | Yes | Function that executes when the command is called |
Command Registration
Basic Example
import { useUIChatCommand } from "ai-chat-bootstrap";
import { z } from "zod";
function MyCommands() {
useUIChatCommand({
name: "clear",
description: "Clear the chat history",
parameters: z.object({}),
execute: async () => {
await clearChatHistory();
console.log("Chat history cleared");
}
});
return null;
}Multiple UI Commands
function UICommands() {
const [theme, setTheme] = useState("light");
const [fontSize, setFontSize] = useState(14);
// Theme command
useUIChatCommand({
name: "theme",
description: "Change the application theme",
parameters: z.object({
mode: z.enum(["light", "dark", "auto"])
.describe("Theme mode to apply"),
}),
execute: async ({ mode }) => {
setTheme(mode);
document.documentElement.setAttribute("data-theme", mode);
localStorage.setItem("theme", mode);
}
});
// Font size command
useUIChatCommand({
name: "fontsize",
description: "Change the font size",
parameters: z.object({
size: z.number().min(10).max(24)
.describe("Font size in pixels"),
}),
execute: async ({ size }) => {
setFontSize(size);
document.documentElement.style.setProperty("--font-size", `${size}px`);
localStorage.setItem("fontSize", String(size));
}
});
// Export command
useUIChatCommand({
name: "export",
description: "Export chat history",
parameters: z.object({
format: z.enum(["json", "markdown", "txt"])
.default("json")
.describe("Export format"),
}),
execute: async ({ format }) => {
const data = await exportChatHistory(format);
downloadFile(data, `chat-export.${format}`);
}
});
return null;
}How UI Commands Work
- User Input: User types
/commandwith parameters - Command Parsing: System parses and validates parameters
- Direct Execution: Execute function runs immediately
- No AI Involved: Command bypasses the LLM completely
- Instant Feedback: UI updates happen synchronously
Parameter Schemas
No Parameters
parameters: z.object({}) // Empty object for commands without parametersSimple Parameters
parameters: z.object({
message: z.string().describe("Message to display"),
duration: z.number().default(3000).describe("Duration in milliseconds"),
})Complex Parameters
parameters: z.object({
action: z.enum(["save", "load", "delete"]).describe("Action to perform"),
target: z.object({
type: z.enum(["file", "database", "localStorage"]),
name: z.string(),
path: z.string().optional(),
}).describe("Target for the action"),
options: z.object({
overwrite: z.boolean().default(false),
backup: z.boolean().default(true),
compress: z.boolean().default(false),
}).optional().describe("Action options"),
})Execute Functions
Synchronous Execution
execute: (params) => {
// Direct state update
setState(params.value);
// DOM manipulation
document.getElementById("target").classList.add("active");
// Local storage
localStorage.setItem("key", JSON.stringify(params));
}Asynchronous Execution
execute: async (params) => {
try {
// Show loading state
setLoading(true);
// Perform async operation
const result = await fetchData(params.id);
// Update state with result
setData(result);
// Show success notification
toast.success("Operation completed");
} catch (error) {
// Handle errors
console.error("Command failed:", error);
toast.error("Operation failed: " + error.message);
} finally {
setLoading(false);
}
}With Side Effects
execute: async (params) => {
// Update multiple states
setConfig(params.config);
setStatus("updated");
// Trigger side effects
await saveToDatabase(params);
eventEmitter.emit("configChanged", params);
// Analytics
trackEvent("command_executed", {
command: "configure",
params: params,
});
}Complete Integration Example
"use client";
import React, { useState } from "react";
import {
MockChatContainer,
useAIChat,
useUIChatCommand,
} from "ai-chat-bootstrap";
import { toast } from "sonner";
import { z } from "zod";
export function ChatWithUICommands() {
const [theme, setTheme] = useState("light");
const [debugMode, setDebugMode] = useState(false);
const chat = useAIChat({
transport: { api: "/api/chat" },
messages: {
systemPrompt:
"You are a helpful assistant that supports slash commands for app control.",
},
});
const messages = chat.messages;
// Clear command
useUIChatCommand({
name: "clear",
description: "Clear all chat messages",
parameters: z.object({}),
execute: async () => {
chat.setMessages([]);
toast.success("Chat cleared");
}
});
// Theme command
useUIChatCommand({
name: "theme",
description: "Change the UI theme",
parameters: z.object({
mode: z.enum(["light", "dark", "auto"]).describe("Theme mode"),
}),
execute: async ({ mode }) => {
setTheme(mode);
document.documentElement.setAttribute("data-theme", mode);
localStorage.setItem("theme", mode);
toast.success(`Theme changed to ${mode}`);
}
});
// Debug command
useUIChatCommand({
name: "debug",
description: "Toggle debug mode",
parameters: z.object({
enabled: z.boolean().optional().describe("Enable or disable debug mode"),
}),
execute: async ({ enabled }) => {
const newState = enabled !== undefined ? enabled : !debugMode;
setDebugMode(newState);
console.log(`Debug mode: ${newState ? "ON" : "OFF"}`);
if (newState) {
// Enable debug features
window.__DEBUG__ = true;
console.log("Debug information:", {
messages: messages.length,
theme,
timestamp: new Date().toISOString(),
});
} else {
delete window.__DEBUG__;
}
}
});
// Settings command
useUIChatCommand({
name: "settings",
description: "Update application settings",
parameters: z.object({
autoSave: z.boolean().optional().describe("Auto-save messages"),
notifications: z.boolean().optional().describe("Enable notifications"),
sound: z.boolean().optional().describe("Enable sound effects"),
}),
execute: async (params) => {
const settings = {
autoSave: params.autoSave ?? true,
notifications: params.notifications ?? true,
sound: params.sound ?? false,
};
localStorage.setItem("settings", JSON.stringify(settings));
// Apply settings
if (settings.notifications) {
await Notification.requestPermission();
}
toast.success("Settings updated");
}
});
const chat = useAIChat({
api: "/api/chat",
messages,
onMessagesChange: setMessages,
});
return (
<div className="h-[600px] w-full">
<MockChatContainer
chat={chat}
header={{ title: "Chat with UI Commands", subtitle: "Try: /clear, /theme dark, /debug true" }}
ui={{ placeholder: "Type / to see available commands" }}
/>
{debugMode && (
<div className="mt-4 p-4 bg-muted rounded-lg">
<h3 className="font-bold">Debug Info</h3>
<pre className="text-xs">
{JSON.stringify({
messageCount: messages.length,
theme,
lastUpdate: new Date().toISOString(),
}, null, 2)}
</pre>
</div>
)}
</div>
);
}Advanced Examples
Navigation Command
useUIChatCommand({
name: "goto",
description: "Navigate to a page",
parameters: z.object({
page: z.enum(["home", "settings", "profile", "help"])
.describe("Page to navigate to"),
newTab: z.boolean().default(false).describe("Open in new tab"),
}),
execute: async ({ page, newTab }) => {
const routes = {
home: "/",
settings: "/settings",
profile: "/profile",
help: "/help",
};
if (newTab) {
window.open(routes[page], "_blank");
} else {
router.push(routes[page]);
}
}
});Data Management Command
useUIChatCommand({
name: "data",
description: "Manage local data",
parameters: z.object({
action: z.enum(["save", "load", "delete", "clear"])
.describe("Action to perform"),
key: z.string().optional().describe("Data key"),
}),
execute: async ({ action, key }) => {
switch (action) {
case "save":
if (key) {
localStorage.setItem(key, JSON.stringify(currentData));
toast.success(`Data saved to ${key}`);
}
break;
case "load":
if (key) {
const data = localStorage.getItem(key);
if (data) {
setCurrentData(JSON.parse(data));
toast.success(`Data loaded from ${key}`);
} else {
toast.error(`No data found for ${key}`);
}
}
break;
case "delete":
if (key) {
localStorage.removeItem(key);
toast.success(`Data deleted for ${key}`);
}
break;
case "clear":
localStorage.clear();
toast.success("All local data cleared");
break;
}
}
});Batch Operations Command
useUIChatCommand({
name: "batch",
description: "Perform batch operations",
parameters: z.object({
operation: z.enum(["select-all", "deselect-all", "invert", "delete-selected"]),
filter: z.string().optional().describe("Filter items before operation"),
}),
execute: async ({ operation, filter }) => {
let items = getAllItems();
if (filter) {
items = items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}
switch (operation) {
case "select-all":
items.forEach(item => item.selected = true);
break;
case "deselect-all":
items.forEach(item => item.selected = false);
break;
case "invert":
items.forEach(item => item.selected = !item.selected);
break;
case "delete-selected":
const toDelete = items.filter(item => item.selected);
await deleteItems(toDelete);
break;
}
updateItems(items);
}
});TypeScript Interface
interface UIChatCommand {
type: 'ui';
name: string;
description: string;
parameters: z.ZodSchema;
execute: (params: unknown) => void | Promise<void>;
}
type CommandRegistration = Omit<UIChatCommand, 'type'>;Common Command Patterns
- Settings Management: Change themes, preferences, configurations
- Navigation: Route to different pages or sections
- Data Operations: Clear, export, import, backup data
- UI Control: Toggle panels, modals, debug modes
- Shortcuts: Quick actions for common operations
- Admin Functions: Development and debugging tools
Difference from AI Commands
| Feature | UI Commands | AI Commands |
|---|---|---|
| Execution | Direct client-side | Through AI/LLM |
| Speed | Instant | Depends on AI |
| System Prompts | Not applicable | Supported |
| Flexibility | Exact behavior | AI interpretation |
| Tool Usage | Not required | Required |
| Offline Support | Yes | No |
Error Handling
execute: async (params) => {
try {
// Validate state before execution
if (!isValidState()) {
throw new Error("Invalid state for this operation");
}
// Perform operation
const result = await performOperation(params);
// Verify success
if (!result.success) {
throw new Error(result.error || "Operation failed");
}
// Update UI
updateUI(result.data);
toast.success("Command executed successfully");
} catch (error) {
console.error("Command execution failed:", error);
// User-friendly error message
const message = error.message || "An unexpected error occurred";
toast.error(message);
// Optional: Report to error tracking
reportError(error, { command: "commandName", params });
}
}See Also
- useAIChatCommand - Register AI commands that use the LLM
- Commands Guide - Complete guide to using commands
- useAIChat - Main chat hook
- useAIFrontendTool - Register tools for AI commands
Last updated on