Complete toolkit for configuring and extending OpenCode: agent creation, custom slash commands, configuration management, plugin development, and SDK usage.
98
98%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Advisory
Suggest reviewing before use
Reference for showing toast notifications in OpenCode TUI
Plugins can display toast notifications - temporary popup messages that appear in the TUI corner. These are ideal for brief status updates, confirmations, warnings, or alerts that don't need to persist in the chat.
Use toasts for:
SHOULD NOT use for:
ui-feedback.md)await client.tui.showToast({
body: {
title: "Optional Title", // Optional heading
message: "Toast message", // Required message text
variant: "success", // "info" | "success" | "warning" | "error"
duration: 5000, // Optional: milliseconds
},
})| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | No | Optional heading for the toast |
message | string | Yes | The main message content |
variant | "info" | "success" | "warning" | "error" | Yes | Visual style and icon |
duration | number | No | Auto-dismiss time in milliseconds |
| Variant | Use Case | Visual |
|---|---|---|
info | Neutral information, fallback notices | Blue/neutral styling |
success | Successful operations | Green styling with checkmark |
warning | Configuration issues, caution notices | Yellow/orange styling |
error | Failures or critical problems | Red styling |
import type { Plugin } from "@opencode-ai/plugin"
export const ToastPlugin: Plugin = async ({ client }) => {
return {
event: async ({ event }) => {
if (event.type === "session.idle") {
try {
await client.tui.showToast({
body: {
title: "Session Complete",
message: "All tasks finished successfully",
variant: "success",
duration: 4000,
},
})
} catch {
// Ignore toast errors - TUI may not be available
}
}
},
}
}MUST wrap toast calls in try/catch - the TUI MAY not be available (e.g., in headless mode):
async function showToast(
client: any,
message: string,
variant: "info" | "success" | "warning" | "error" = "info",
title?: string,
duration?: number,
): Promise<void> {
try {
await client.tui.showToast({
body: {
title,
message,
variant,
duration,
},
})
} catch {
// Ignore - TUI may not be available
}
}When showing toasts during plugin initialization, SHOULD use setTimeout to avoid blocking:
export const ConfigPlugin: Plugin = async ({ client }) => {
const config = loadConfig()
if (config.hasErrors) {
// Delay toast to avoid blocking plugin init
setTimeout(async () => {
try {
await client.tui.showToast({
body: {
title: "Plugin: Invalid config",
message: `${config.path}\n${config.errorMessage}\nUsing default values`,
variant: "warning",
duration: 7000,
},
})
} catch {}
}, 7000) // Delay allows TUI to fully initialize
}
return {
// ... hooks
}
}Use \n for line breaks in toast messages:
await client.tui.showToast({
body: {
title: "Model Fallback",
message: `anthropic/claude-3-opus failed\nUsing openai/gpt-4 instead`,
variant: "info",
duration: 5000,
},
})return {
event: async ({ event }) => {
switch (event.type) {
case "session.idle":
try {
await client.tui.showToast({
body: {
message: "Session completed",
variant: "success",
},
})
} catch {}
break
case "session.error":
try {
await client.tui.showToast({
body: {
title: "Error",
message: "Session encountered an error",
variant: "error",
},
})
} catch {}
break
}
},
}function showConfigWarning(client: any, configPath: string, errors: string[]): void {
const message = [configPath, ...errors.slice(0, 2), errors.length > 2 ? `(+${errors.length - 2} more errors)` : ""]
.filter(Boolean)
.join("\n")
setTimeout(async () => {
try {
await client.tui.showToast({
body: {
title: "MyPlugin: Invalid config",
message,
variant: "warning",
duration: 7000,
},
})
} catch {}
}, 7000)
}return {
"chat.params": async (input, output) => {
const preferredModel = getPreferredModel()
const actualModel = input.model
if (preferredModel && actualModel.id !== preferredModel.id) {
try {
await client.tui.showToast({
body: {
title: "Model Fallback",
message: `${preferredModel.provider}/${preferredModel.id} unavailable\nUsing ${actualModel.providerID}/${actualModel.id}`,
variant: "info",
duration: 5000,
},
})
} catch {}
}
},
}| Aspect | Toast | Inline Message |
|---|---|---|
| Visibility | Temporary popup corner | Persistent in chat |
| Duration | Auto-dismisses | Stays until scrolled away |
| Detail level | Brief (1-3 lines) | Can be multi-line |
| History | Not saved | Visible in session |
| Use case | Quick alerts, warnings | Detailed status with data |
Use toasts for ephemeral alerts and warnings. Use inline messages (see ui-feedback.md) for detailed status that users might want to reference.
| Limitation | Details |
|---|---|
| No interactivity | MUST NOT include buttons or inputs |
| Brief content only | SHOULD keep to 1-3 lines |
| No custom styling | Limited to predefined variants |
| TUI only | Won't appear in web or headless mode |
| May fail silently | MUST wrap in try/catch |
| Rate limiting | SHOULD avoid rapid-fire toasts |