원클릭으로
canvas
Render rich interactive UI surfaces (tables, forms, cards, buttons) in the sidebar canvas or inline in chat using the render_ui tool.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Render rich interactive UI surfaces (tables, forms, cards, buttons) in the sidebar canvas or inline in chat using the render_ui tool.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Interact with companion devices (phones, laptops, headless servers) connected to Suzent.
Become a helpful co-worker in the workspace. Use it whenever you need to access, manage, or reference files.
Access and maintain the notebook knowledge base with Obsidian markdown conventions.
Create a new Suzent AgentSkill, or improve an existing one. Use when the user wants to author a skill from scratch, scaffold a SKILL.md with supporting scripts/references/assets, or refine a skill's description so it triggers reliably.
Install a Suzent AgentSkill from a Git repo, ZIP URL, or owner/repo GitHub shorthand. Use when the user wants to add a third-party or community skill to Suzent. Fetches and copies only; never executes fetched code at install time.
Schedule recurring tasks (cron) and periodic agent check-ins (heartbeat).
| name | canvas |
| description | Render rich interactive UI surfaces (tables, forms, cards, buttons) in the sidebar canvas or inline in chat using the render_ui tool. |
Use render_ui to display structured, interactive content alongside the chat. Surfaces appear in the sidebar canvas panel and persist across the session.
render_ui(surface_id, component, title="", target="canvas")
"results" or "booking_form"; calling again with the same id replaces (upserts) the surface"type" field describing the root component"canvas" (sidebar, default) or "inline" (inside the chat message)"children" list| Type | Description | Extra fields |
|---|---|---|
card | Titled bordered panel | title |
stack | Vertical (default) or horizontal group | direction: "horizontal" |
columns | Side-by-side columns | widths: [1, 2] (relative ratios) |
| Type | Key fields | Notes |
|---|---|---|
text | content, variant | variants: body (default), heading, subheading, caption, code |
badge | label, color | colors: success, warning, error, info, default |
button | label, action, variant, context | variants: primary, secondary, danger |
table | columns [{key, label}], rows [{}] | |
form | action, submit_label, fields [{name,label,type}] | field types: text, number, textarea, select |
list | items [str], ordered | items support markdown |
progress | value (0-100), label | |
divider | — | |
html | html, height | free-form HTML in a sandboxed iframe — for charts, SVG, custom dashboards the other types can't express |
Critical: use label for buttons/badges, content for text. Never use "text" as a field name.
When the user clicks a button or submits a form, you receive a message:
[canvas: <action>] "<button_label>" ← button click
[canvas: <action>] {"field": "value"} ← form submit
Always set "action" on buttons and forms so you can identify what was triggered.
Use "context" on buttons to pass extra data: {"context": {"id": 42}}.
render_ui(
surface_id="status",
title="Analysis",
component={
"type": "card",
"title": "Results",
"children": [
{"type": "text", "content": "Evaluation complete."},
{"type": "badge", "label": "92% Accuracy", "color": "success"},
{"type": "button", "label": "Export CSV", "action": "export_csv"},
{"type": "button", "label": "Re-run", "action": "rerun", "variant": "secondary"},
],
}
)
render_ui(
surface_id="results",
title="Search Results",
component={
"type": "table",
"columns": [{"key": "name", "label": "Name"}, {"key": "score", "label": "Score"}],
"rows": [{"name": "Claude", "score": "92%"}, {"name": "GPT-4o", "score": "88%"}],
}
)
render_ui(
surface_id="booking",
title="Book a Table",
component={
"type": "form",
"action": "confirm_booking",
"submit_label": "Confirm",
"fields": [
{"name": "date", "label": "Date", "type": "text", "required": True},
{"name": "guests", "label": "Guests", "type": "number"},
],
}
)
render_ui(
surface_id="quick_actions",
target="inline",
component={
"type": "stack",
"children": [
{"type": "text", "content": "What would you like to do?", "variant": "subheading"},
{"type": "button", "label": "Deep Analysis", "action": "deep_analysis"},
{"type": "button", "label": "Skip", "action": "skip", "variant": "secondary"},
],
}
)
render_ui(
surface_id="overview",
component={
"type": "columns",
"widths": [1, 2],
"children": [
{
"type": "stack",
"children": [
{"type": "text", "content": "Status", "variant": "subheading"},
{"type": "badge", "label": "Active", "color": "success"},
]
},
{
"type": "table",
"columns": [{"key": "k", "label": "Key"}, {"key": "v", "label": "Value"}],
"rows": [{"k": "CPU", "v": "12%"}, {"k": "RAM", "v": "4.2 GB"}],
}
],
}
)
html component)When the typed components can't express what you need — charts, SVG diagrams, custom dashboards, interactive prototypes — use the html component. It renders self-contained HTML in a sandboxed iframe (scripts run, but isolated from the app: no cookies, storage, or parent-DOM access). Omit height to auto-size, or set it (px) for a fixed height.
render_ui(
surface_id="chart",
title="Weekly Traffic",
component={
"type": "html",
"html": """
<div style="font-family: monospace; padding: 16px;">
<h2>Weekly Traffic</h2>
<svg width="300" height="100">
<rect x="0" y="40" width="40" height="60" fill="black"/>
<rect x="60" y="20" width="40" height="80" fill="black"/>
<rect x="120" y="55" width="40" height="45" fill="black"/>
</svg>
<button onclick="
window.parent.postMessage(
{type:'a2ui:action', action:'refresh_chart', context:{range:'30d'}}, '*')
">Load 30 days</button>
</div>
""",
}
)
Feedback from HTML → you. Because the iframe is isolated, interactive HTML sends actions back via postMessage. Have your HTML post to window.parent:
window.parent.postMessage(
{type: 'a2ui:action', action: 'my_action', context: {/* any JSON */}}, '*');
You receive it exactly like a button click: [canvas: my_action] {...}. So buttons, chart clicks, or forms inside your HTML can drive the conversation — use distinct action names just like with button/form.
Prefer typed components (table, form, button, …) for simple structured UI that talks back — they match the app's style and are schema-validated. Reach for html only when you need visuals or layouts the vocabulary can't express.
Prefer render_ui over plain text when asking the user to choose. Render an inline surface with one button per option; the user clicks instead of typing, and you get a structured callback.
render_ui(
surface_id="clarify_tone",
target="inline",
component={
"type": "stack",
"children": [
{"type": "text", "content": "What tone should the report use?", "variant": "subheading"},
{"type": "button", "label": "Formal", "action": "choose_tone", "context": {"tone": "formal"}},
{"type": "button", "label": "Casual", "action": "choose_tone", "context": {"tone": "casual"}, "variant": "secondary"},
{"type": "button", "label": "Technical", "action": "choose_tone", "context": {"tone": "technical"}, "variant": "secondary"},
],
}
)
You will receive: [canvas: choose_tone] "Formal" — use button_label or context to determine the choice.
For binary yes/no confirmations:
{"type": "button", "label": "Yes, proceed", "action": "confirm", "variant": "primary"},
{"type": "button", "label": "Cancel", "action": "cancel", "variant": "secondary"},
For longer option lists, use direction: "horizontal" on the stack to render buttons side by side.
html component