Interactive Widget

v0.1.2

Create shareable interactive web pages — dashboards, charts, forms, simulations — via the duoduo-widget CLI. Each widget gets a permanent URL that works in a...

1· 188·0 current·0 all-time

Install

OpenClaw Prompt Flow

Install with OpenClaw

Best for remote or guided setup. Copy the exact prompt, then paste it into OpenClaw for kuaner/interactive-widget.

Previewing Install & Setup.
Prompt PreviewInstall & Setup
Install the skill "Interactive Widget" (kuaner/interactive-widget) from ClawHub.
Skill page: https://clawhub.ai/kuaner/interactive-widget
Keep the work scoped to this skill only.
After install, inspect the skill metadata and help me finish setup.
Use only the metadata you can verify from ClawHub; do not invent missing requirements.
Ask before making any broader environment changes.

Command Line

CLI Commands

Use the direct CLI path if you want to install manually and keep every step visible.

OpenClaw CLI

Canonical install target

openclaw skills install kuaner/interactive-widget

ClawHub CLI

Package manager switcher

npx clawhub@latest install interactive-widget
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Benign
medium confidence
Purpose & Capability
Name/description match the instructions: the SKILL.md documents how to open, stream, update, patch, and finalize shareable widgets via the duoduo-widget CLI. No unrelated env vars, binaries, or config paths are requested.
Instruction Scope
Runtime instructions are narrowly scoped to building and pushing HTML fragments to the duoduo-widget service, using patches or full HTML. The doc explicitly forbids network APIs from the widget (fetch/XHR/WebSocket) and warns not to expose control_url/control_token. The instructions do not instruct reading unrelated system files or environment variables.
Install Mechanism
There is no install spec (instruction-only). The Quick Start suggests npm install -g @openduo/duoduo-widgets, but the skill does not itself install anything. That is proportionate for a CLI guide, however the referenced npm package and service are external and the skill provides no homepage or source link to verify.
Credentials
The skill requires no environment variables, credentials, or config paths. All operations are expected to use the duoduo-widget CLI and its output; nothing requests unrelated secrets.
Persistence & Privilege
always:false and no install or system-wide changes are requested. The skill does not request persistent privileges or modify other skills' configs.
Assessment
This is a usage guide for a third-party CLI/service rather than code bundled into the agent. Before using: (1) verify you trust the @openduo/duoduo-widgets package and the service it talks to (there is no homepage/source in the skill metadata), (2) avoid putting secrets or private data into widget HTML or patches (these are uploaded to the provider), and (3) follow the skill's guidance to never reveal control_url/control_token. If you need to display sensitive information, consider hosting widgets yourself or using a trusted alternative.

Like a lobster shell, security has layers — review code before you run it.

latestvk976r6qx6svkv0g24e4neprzah83tdsj
188downloads
1stars
3versions
Updated 4w ago
v0.1.2
MIT-0

Widget — Durable Interactive Artifacts

The user watches the widget live — stream content fast, one section per tool call.

Quick start

npm install -g @openduo/duoduo-widgets  # if not installed

1. Open

duoduo-widget open --title "Dashboard" --ttl-seconds 300

Send links.feishu_sidebar or links.browser to user. NEVER send control_url/control_token.

2. Skeleton + push

cat > /tmp/w-{wid}.html << 'SKELETON'
<div style="background:#1a1a1a;color:#e0e0e0;padding:20px;font-family:system-ui;min-height:100vh;">
  <h1 style="color:#fff;font-size:28px;font-weight:500;margin:0 0 4px;">Title</h1>
  <p style="color:#999;font-size:14px;margin:0 0 20px;">Subtitle</p>
<!-- NEXT -->
</div>
SKELETON
cat /tmp/w-{wid}.html | duoduo-widget update --wid "wid_..."

3a. Append section via full HTML (classic)

python3 - /tmp/w-{wid}.html << 'PYEOF'
import sys
f = sys.argv[1]
html = open(f).read()
section = """<div style="background:#2a2a2a;padding:16px;border-radius:8px;margin-bottom:12px;">
  <h3 style="margin:0 0 8px;color:#fff;font-size:16px;font-weight:500;">Section title</h3>
  <p style="margin:0;color:#999;font-size:14px;">Content — $100 safe, no escaping needed</p>
</div>
<!-- NEXT -->"""
html = html.replace('<!-- NEXT -->', section)
open(f, 'w').write(html)
PYEOF
cat /tmp/w-{wid}.html | duoduo-widget update --wid "wid_..."

Quoted heredoc 'PYEOF' — write raw HTML, no shell escaping. Only change content inside """...""".

3b. Incremental update via --patch (preferred for data-heavy widgets)

After the skeleton is pushed, use --patch to update specific parts of the page without re-sending the entire HTML. This is faster, uses less bandwidth, and avoids morphdom re-rendering.

duoduo-widget update --wid "wid_..." --patch '[
  {"op":"append","selector":"#rows","html":"<tr><td>New item</td><td>$100</td></tr>"},
  {"op":"text","selector":"#count","text":"42"},
  {"op":"innerHTML","selector":"#status","html":"<strong style=\"color:#4ade80\">Done</strong>"}
]'

Patch operations:

OpWhat it doesRequires
appendInsert html as last child of selectorhtml
prependInsert html as first child of selectorhtml
replaceReplace element matching selectorhtml
innerHTMLSet innerHTML of selectorhtml
textSet textContent of selectortext
removeRemove element matching selector

When to use patch vs full HTML:

  • Patch: tables gaining rows, dashboards updating numbers, status text changes, progressive list building
  • Full HTML: first skeleton push, layout changes, adding new scripts/CDN libraries

Important: Patches update the live viewer instantly but do NOT update the stored HTML on the server. To ensure the finalized artifact includes all changes, use this pattern:

  1. Push skeleton via full update --html (keep the temp file)
  2. Stream data via --patch for live viewer speed
  3. In parallel, keep appending to /tmp/w-{wid}.html locally
  4. Before finalize, do one last cat /tmp/w-{wid}.html | duoduo-widget update --wid ...
  5. Then finalize

Or simply: pass the final complete HTML via duoduo-widget finalize --wid ... --html "..." if available.

Skeleton design for patch: Give target elements id attributes so patches can address them:

<tbody id="rows"></tbody>
<!-- append rows here -->
<span id="count">0</span>
<!-- update text here -->
<div id="status">Loading...</div>
<!-- update status here -->

4. Finalize

duoduo-widget finalize --wid "wid_..."

Rules

  1. Copy from references/html_patterns.md — read it first, pick a section template, change only the data values. Never design HTML from scratch
  2. One section per Bash call — heredoc + cat pipe in a single command
  3. Push after every section — never batch
  4. Never build full HTML in context — the temp file accumulates; context only sees the section
  5. Never read the temp file back — it only flows through the pipe
  6. Act on _hints in update output: no_viewers → send link; ttl_low/ttl_expiring → finalize now; many_updates → wrap up

Interactive widgets

duoduo-widget open --title "Confirm" --ttl-seconds 300 \
  --interaction-mode submit --interaction-prompt "Review and confirm"

Button: <button onclick="window.duoduo.submit('action', {key:'val'})" style="background:#4a9;color:#fff;border:none;padding:10px 24px;border-radius:6px;font-size:14px;cursor:pointer;">Label</button>

Read result: duoduo-widget wait --wid "wid_..." --timeout-seconds 120

HTML rules

  • Inline styles only. CDN: cdnjs.cloudflare.com, esm.sh, cdn.jsdelivr.net, unpkg.com
  • Forbidden: fetch(), XMLHttpRequest, WebSocket, eval(), new Function()

Templates — read references/html_patterns.md first. Copy a template, change data values only.

CLI reference

CommandPurposeKey flags
openCreate draft--title, --ttl-seconds, --interaction-mode, --interaction-prompt
updatePush HTML or patch--wid, stdin or --html, --patch <json>, --text-fallback
finalizeFreeze--wid
waitBlock for submit--wid, --timeout-seconds
getPoll status--wid

State machine

draftfinalizedawaiting_inputsubmitted (terminal)

Finalized artifacts are permanent. Fork: open --fork <widget_id>.

Environment

WIDGET_SERVICE_URL env var (default: https://aidgets.dev).

Comments

Loading comments...