Skip to content

Widgets and visualizations

Dang Nguyen edited this page Jun 7, 2026 · 1 revision

Steward can render inline HTML and SVG widgets in your conversation-animations, interactive demos, diagrams, and simple games. Ask in natural language; the agent uses the built-in show_widget tool to create and display them in a sandboxed iframe inside your chat note.

HTML project widgets live under Steward/Widgets/{widgetId}/ as multi-file projects (index.html, style.css, main.js, Widget.md, etc.). They persist across conversations and hot-reload when project files change. SVG widgets are self-contained vector graphics rendered inline without a project folder.

To find a widget again later, save it as an artifact (.art note under Steward/Artifacts/). Artifacts are lightweight bookmarks that open in Steward's reading view and render the live widget from its project folder.

For YAML block structure in Widget.md, see YAML blocks in markdown.

How widgets are rendered

Create

When the agent calls show_widget in project mode, Steward:

  1. Writes project files under {stewardFolder}/Widgets/{widgetId}/ (widgetId = slugified name + short unique suffix).
  2. Creates Widget.md with a host-maintained manifest YAML block at the top of the note body.
  3. Appends a stw-widget-project fence to the conversation note:
```stw-widget-project
My-Game-abc12
```

For quick one-off graphics, the agent can use SVG mode or a single HTML code blob instead of a full project folder. Only HTML project widgets get persistent state and vault asset loading.

Mount

When Obsidian displays the conversation note, WidgetPostProcessor:

  1. Reads the stw-widget-project fence and resolves {stewardFolder}/Widgets/{widgetId}.
  2. Bundles the entry HTML (inlines linked CSS/JS from the project folder).
  3. Injects window.stw into the bundle for state and assets.
  4. Renders the result in a sandboxed iframe (srcdoc, allow-scripts, strict CSP, no network access).

Manifest assets are not inlined into HTML. The iframe requests allowed vault files from the host at runtime; the host reads bytes from your vault and the iframe creates local blob URLs.

Hot-reload

When Steward (or you) edits widget project files via the edit tool, Steward re-bundles the project and refreshes mounted iframes in open notes-without losing persisted state in state.json.

Saving widgets as artifacts

When a project widget is embedded in a conversation note, Steward shows two links under the iframe:

  • Open in new tab — opens a generated reading note inside the widget project folder (Steward/Widgets/{widgetId}/{widgetName}.md).
  • Save as artifact — creates or updates a bookmark note in Steward/Artifacts/ ({widgetName}.art).

Both actions write the same note content: a widgetName frontmatter property and a stw-widget-project fence with the widget ID. Only the location and extension differ. Example:

---
widgetName: 'Tic Tac Toe'
---

```stw-widget-project
Tic-Tac-Toe-abc12
```

The filename is based on the widget name (for example Tic Tac Toe.art). If another widget already uses that name, Steward appends the widget ID to avoid overwriting it.

What artifacts are for

Project folder (Steward/Widgets/) Artifact (Steward/Artifacts/)
Contains Source files, Widget.md, state.json Same reading-note body as Open in new tab (frontmatter + fence only)
Purpose Build and edit the widget Find and reopen the widget quickly from one folder
Opens in Editor (for source files) or reading view (generated .md note) Steward reading view (.art extension)

Click any .art file in the vault file explorer to open it in reading view. The widget still loads from Steward/Widgets/{widgetId}/, so edits and state.json stay in sync. Action links under the iframe are hidden when you are already viewing an artifact or the widget's dedicated reading note.

How state is managed (iframe and host)

Project widgets run inside an iframe. The host (Steward plugin) and guest (widget JavaScript) communicate through window.stw, injected by the host before your scripts run. You do not author this API in vault files.

API Role
window.stw.getState() Returns the last saved data object, or null on first load.
window.stw.setState(data) Saves a JSON-serializable snapshot; debounced (~400 ms), then written to vault.
window.stw.getAsset(id) Async; requests an allowlisted vault file from the host and returns a blob URL inside the iframe.
state.json Created lazily in the project folder on first successful setState. Host-owned-do not edit manually.

On disk, state.json looks like:

{
  "version": 1,
  "updatedAt": "2026-05-29T00:00:00.000Z",
  "data": {}
}

Your widget supplies only the inner data object via setState. The widget must call setState after every meaningful user action; otherwise state is not persisted and state.json never appears.

Assets: only vault paths listed under assets: in the manifest YAML block in Widget.md can be loaded. Reference them in HTML/CSS as asset:Images/photo.png, or resolve dynamically with await window.stw.getAsset(id).

For implementation patterns (main.js, manifest fields, asset rules), see the bundled stateful-widget skill (also copied to Steward/Skills/stateful-widget/SKILL.md in your vault).

Turn-based widgets (play vs AI, chess, poker, etc.) add registerAction and extra YAML blocks in Widget.md; see interactive-widget.

Updating a widget

After a project widget is created, ask Steward to change it in natural language-for example:

/ Fix the tic-tac-toe widget so X goes first.
/ Add a reset button to my counter widget.

Steward reads the project under Steward/Widgets/{widgetId}/ (via content_reading) and applies changes with edit. Hot-reload updates the iframe in your open note.

You can also open and edit files yourself (index.html, main.js, style.css). To add vault images or raise size limits, edit the manifest block in Widget.md (assets, maxAssetSize)-see the stateful-widget skill.

Do not remove or replace the host-maintained manifest YAML fence at the top of Widget.md. Additional blocks (actions, actors, agent) belong below it.

Standard skills for widgets

Steward ships two standard skills for widget work:

Skill When it applies
stateful-widget Interactive HTML projects where clicks, forms, or game state must survive reopening the note
interactive-widget Turn-based play where humans and models take turns (after stateful-widget patterns are in place)

Steward creates and updates these automatically under Steward/Skills/ when you install or upgrade the plugin (same as other standard skills). The agent reads them via content_reading when building or fixing widgets.

Keep both skills enabled for the best widget creation. Disabling them (enabled: false in frontmatter) removes guidance the model relies on for persistence, assets, and turn-based play.

Source copies in the repo:

Troubleshooting

Assets aren't showing in the widget

Check the assets: list in the manifest YAML block at the top of Widget.md. Every vault path you reference (e.g. asset:Images/photo.png in HTML or CSS) must appear there. After adding paths, save the note and reopen the conversation if the iframe does not hot-reload.

If files are large, raise maxAssetSize in the same manifest block (default is 5MB).

Widget behavior isn't as expected

Describe the problem and ask Steward to edit the widget project. Point to the widget by name or open Steward/Widgets/{widgetId}/Widget.md and link to the project in your message.

For turn-based or AI-play bugs, mention whether the widget should use model turns so Steward loads interactive-widget as well.

Output limit reached

Widgets often produce large tool output (many files, long HTML/JS). If generation stops early or the widget is incomplete, increase Max generation tokens in Steward → Settings and ask Steward to continue or recreate the widget.

Example prompts

/ Draw an animated bar chart of my weekly habits.
/ Make a tic-tac-toe game I can play against you.
/ Visualize this note as an SVG diagram.

Clone this wiki locally