Plugin directory / UI & Skins / dsh-genui
dsh-genui
Verified · install-tested on dsh omdsh-dev
What it does
GenUI for DeepSeek Harness renders interactive UI components (layout, charts, plots, forms, quizzes, mermaid, 3D scenes) inline in assistant replies via the dsh-ui fence.
Recommended — verified working and popular
GenUI for DeepSeek Harness renders interactive UI components (layout, charts, plots, forms, quizzes, mermaid, 3D scenes) inline in assistant replies via the dsh-ui fence. It installs cleanly and boots without issues in our testing. With 483+ stars it's a community-endorsed, low-risk pick.
“Verified” means our automated CI actually ran dsh plugin add in a clean profile and it booted — nothing more. Feature descriptions and version compatibility are the author’s claims. This is not a security audit and not an endorsement of third-party code.
README
🎨 dsh-genui
Give the model's answers a face — the text is still there, and an interactive UI is already live.
🔌 Ecosystem: the repo carries the
#dsh·#dsh-plugintopics — welcome to be listed by @dsh-plugin.
dsh-genui turns a model reply into a safe, interactive DSH surface. Ask “how are this month’s orders doing?” and the answer can include a sortable data panel, a native video, a draggable plot, a local quiz, or a persistent session panel — without replacing the surrounding text.
Start with the evidence
| If you want to… | Go straight to… | What you can verify |
|---|---|---|
| See the complete DSH flow first | 40-second real walkthrough | Components are rendered inside a real DSH conversation. |
| Inspect concrete UI outputs | Three real outputs | Monitoring, function plots, and composable layout primitives. |
| Try it in your own DSH | Quick start | A public npm install, a prompt to run, and an activation check. |
| Learn the JSON language | Component syntax | The supported, guarded dsh-ui component specification. |
Watch the real interface
No concept mockups. The recording and images in this section are captured from
dsh-genuirendering in the DSH interface. Use them to see the actual visual language before installing.
40-second walkthrough
Click the preview to download the original MP4 if the GitHub player is unavailable.
The walkthrough moves from an answer-embedded panel through forms, plotting, Mermaid, and 3D-oriented components. The player does not auto-play. If it does not load, use the original MP4; the four-step prompt sequence is documented in demo-prompts.md.
Three real outputs inside a DSH reply
1. A monitoring panel is an answer, not a separate dashboard
Real output: refresh/reset controls, time-range selection, statistics, charts, and a service table live inside the assistant reply.
2. A function plot redraws locally as its parameters change
Real output: `plot` renders curves while sliders, reset, and animation controls update the graph locally.
3. Layout primitives compose into structured work surfaces
Real output: typography, grid, card, and row/column primitives combine into a hierarchy the model can describe declaratively.
⚠️ Read this first: dual-channel rendering (no host source patch required)
The plugin ships two rendering channels and picks one automatically after the host activates its browser module:
- Registry channel: when the host exposes the
fence-registryextension point (newer dsh builds), fences register through the host's streaming render pipeline and behave seamlessly with the host; - DOM channel: when the host lacks that extension point (including supported stock DSH builds), the plugin observes the session DOM and mounts its own render tree. Since 0.7.2 it supports streaming rendering: components appear as the model writes them — the first finished component shows up immediately, no need to wait for the whole reply. Since 0.8.3 fence discovery is multi-surface: it matches the stock
md-code-blocksurface, the deepsuite-style.code-block/.code-block-smallsurfaces some host builds render instead, and — as a structural backstop — any element whose banner labels itdsh-uiand contains a<pre>body. If your dsh build renders fences with a different class name, they still render (and a one-time console warning tells you the host DOM drifted). - DSH 0.1.7 generic code banner: when the host's final DOM omits fenced-code language metadata, dsh-genui reads the current assistant's original Markdown from the public ChatSnapshot and only takes over
dsh-uifences. The DOM identifies the mount location. If source data is temporarily unavailable, a settled assistant CodeBlock can use the strict canonical GenUI validation as a final fallback.
Whichever channel is active, components, interactions, panels, and persistence behave identically.
CI's packed host smoke installs the generated npm tarball in a real DSH host and verifies host startup and rendering. Source-backed fence routing is covered by integration tests; the smoke does not claim to validate a real model response. Real-model E2E requires configured model credentials.
The repository ships both renderer channels, the host plugin, and the built browser bundle. The host still owns client activation and must provide the slots and sessions services. A downloaded client.js or a ModuleLoader cache entry proves only that bytes arrived — successful activation always prints [genui] client active; fence-channel=registry|dom. If that line is absent, fix package/profile identity or host activation first; DOM attributes such as data-streaming and data-chat-anchor-key are optional fallbacks, not installation prerequisites.
✨ Before vs. after
| Plain answer | With dsh-genui |
|---|---|
| "Revenue this month: ¥128,430, +12.4% MoM — watch the conversion rate." | One line of analysis + three stat cards (revenue / orders / conversion), a trend chart, and a progress bar rendered right beside it |
| Want to see more? Type another question. | The panel already has "Refresh" / "Switch view" buttons — click, and the model updates the data |
🚀 Quick start
Prerequisites — all required:
- dsh
^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1(verified host tags:dsh-v0.1.2-rc.1,dsh-v0.1.7-alpha.1, anddsh-v0.1.7-alpha.2; users on DSH<=0.1.1-rc.xshould use dsh-genui0.9.8) pnpmon your PATH: thedsh plugincommand depends on it. If missing:corepack enable(ornpm i -g pnpm), then open a new terminal and confirmpnpm -vprints a version
Install and activate in DSH (one command, all dependencies included):
# Public npm package (works without an npm account)
dsh plugin --profile web add @changfenhuang/dsh-genui
To add it only as a Node dependency in an existing project:
npm install @changfenhuang/dsh-genui
npm installonly adds the dependency; it does not register the plugin with DSH. Usedsh plugin addabove when installing it into DSH.
⚠️ Don't use
link:on a freshly cloned directory —link:does not install the plugin's dependencies (mermaid / three / react), so the renderer will break. Use the npm command above for normal installation; reservelink:for local development iteration (see below).
Migrating from the old @omdsh-dev package name
If you installed from github:omdsh-dev/dsh-genui before v0.9.2, pnpm may keep the dependency under the old @omdsh-dev/dsh-genui key even though the repository now declares @changfenhuang/dsh-genui. The loader resolves plugins from the profile's dependency keys, so a later reinstall can then fail with Cannot find package '@changfenhuang/dsh-genui'. Re-add the plugin under its current package name:
dsh plugin --profile web remove @omdsh-dev/dsh-genui
dsh plugin --profile web add @changfenhuang/dsh-genui
This migration is required once for old GitHub-spec installs. New installs should use the npm command above and will use the current dependency key.
Verify the install in 60 seconds
After the command completes, restart dsh web and hard-refresh the browser. In a new session, say:
Use dsh-ui to draw a stats dashboard with a sortable service table.
You should see the reply turn into an in-place dashboard rather than a code block. For an unambiguous technical check, open the browser console: successful activation prints [genui] client active; fence-channel=registry|dom.
Developer iteration (link mode)
cd dsh-genui
pnpm install
dsh plugin --profile web add link:$PWD
🧩 Capability map
| Surface | First thing to try | Observable behavior |
|---|---|---|
| Data | Ask for an order or service dashboard | stat, table, chart, and progress appear inside the reply; supported numeric table values sort numerically. |
| Media | Ask for an audio or video reference | Browser-reachable media plays inline, with poster/aspect-ratio and failure states. |
| Exploration | Ask for plot with a parameter |
Dragging sliders redraws the curve locally and immediately. |
| Feedback | Ask for a short quiz | The UI grades and explains locally; only the next model step needs an action. |
| Workspace | Ask for /panel or panel: true |
A persistent, resizable session dock is updated in place. |
The following is the detailed capability reference. Every behavior is constrained by the whitelisted dsh-ui specification; see SKILL.md for the JSON syntax.
Answer-as-UI: components are embedded in the reply and appear as they stream — no waiting for the whole message
30+ components: cards, tables, charts, forms, tabs, accordions, file trees, timelines, diffs…
Native media: audio and video play inline from browser-reachable http(s) or same-origin relative URLs, with user-controlled playback, video posters/aspect ratios, and visible failure states
ECharts integration: the
echartnode renders full ECharts charts with theme-aware colors, tooltips, and legends. Two modes: preset shorthand (preset: 'bar' | 'line' | 'area' | 'pie' | 'scatter'+data/series) for quick upgrade from thechartnode, or full option (optionfield) for custom chart types, dataZoom, visualMap, and other advanced ECharts features. The echarts engine (~1 MB) is lazy-loaded on demand — the main bundle never carries it, and conversations withoutechartnodes never download it- Function plots:plotdraws curves; parameter sliders redraw in real time, with optional auto-animationQuiz:
quizgrades on click with explanation and retry; withaction, the answer is also sent back to the model (grading stays local and instant)Local grading (submit): a multiple-choice set = one
radioper question withgroup+answer(correct answer) +explanation, plus onesubmitbutton — after the user answers everything and clicks once, the score, per-question right/wrong, and explanations appear right in the UI with zero model round-trips; the quiz then locks, and "retake" resets locally (optionalresetActionnotifies the model). Questions without an answer fall back to an aggregated action (fieldscollects every input with anid)State persistence: answers, submission locks, and input values are saved per "session + content fingerprint" — refresh or reopen restores everything; re-rendering identical content keeps user state; new content starts fresh; LRU cap of 200 blocks
Form semantics:
inputEnter /textareaCtrl+Enter submits immediately (submit:true), no blur needed; fields with anidare collected into the submit'sfieldsSecrets ban: GenUI must never ask for passwords, API keys, access tokens, recovery codes, or other secrets; even if a password input appears, it stays masked, is never persisted, and never enters form collection
Local-first principle: state changes the UI can do itself (grading, quiz checking, resets, expand/collapse, selection) always happen locally and instantly; actions are reserved for things that genuinely need the model (generating new content, running tools, next-step suggestions)
Honest interactions: interactive components must carry
action; buttons without one render disabled (kills the "looks clickable, does nothing" fake button); buttons withactionshow instant "triggered" local feedback (proof the local event fired, not that the model received it)Event loop: buttons, checkboxes, radio buttons, switches, selects, inputs, textareas, submits, and quizzes send one event immediately per gesture; slider drags keep trailing-edge debounce, sending only the final value per slider, with different
ids handled independentlyTool channel: the
render_uitool renders the same spec as a card in the tool row (deliverable-style UI goes through the tool, answer-style UI through the fence)Session panel: a persistent dock above the composer;
render_ui/panel: truefences update the same surface in place;/panelopens it from the client (/panel <instruction>customizes via the model,/panel clearclears); the top border is draggable to resize;append: truemerges incrementally — same-named tabs append content, new tabs get added; the whole panel caps at 200 nodes / 200 appends, after which the model should sendreplaceto rebuildFence auto-repair: enabled by default; set
fenceFeedback: falsein this plugin's config (under the plugin entry'sconfig:in your profile's cordis.patch.yml) to disable it. When a reply's final dsh-ui fence fails to render, the plugin steers the SAME turn with the per-node diagnosis so the model can resend a fixed fence; at most one correction per turn and per fence, never in subagents, so it cannot loop.Self-healing & limits: every fence passes a spec guard — bad nodes are silently dropped (the surviving siblings keep rendering: one bad component no longer degrades the whole fence), numbers clamped, strings truncated; the whole tree is capped at 200 nodes / 8 nesting levels; pathological specs never crash the UI
Canonical component protocol: native field aliases such as
card.label→title,table.data/table.items→rows,callout.kind/callout.desc→tone/content(tone valuedanger→error),steps.items→steps,keyvalue.items→pairs(recordlabel→key), andfile-tree.nodes→items(recordlabel→name,typedefaults todirwhen children exist) are normalized deterministically before validation and rendering. A root-level component array is adopted asitems, and a double-encoded JSON string is decoded once.validate_dsh_uireports these normalizations and warns about unknown native fields without blocking custom renderer nodes.Chart error self-healing: mermaid failures auto-retry with repairs (strip backticks, quote Chinese/space labels, remove
<br/>) before degrading to source; a broken chart never hits the screenAccessibility: tabs/accordions/switches/progress bars carry full ARIA and keyboard navigation (arrow keys switch tabs, Home/End jump)
Zero intrusion: without the plugin, fences are just code blocks — no errors, no session pollution
Component JSON syntax lives in SKILL.md. On hosts with the public skill registry, the plugin registers this bundled genui skill automatically, so new Sessions receive the complete component and field catalog without copying files into ~/.dsh.
chart stays the compact three-kind renderer: use kind: 'bars' | 'line' | 'donut' with finite numeric data[].value fields. validate_dsh_ui reports variant, unsupported kinds, and invalid datum fields explicitly; render_ui rejects the same errors instead of silently rendering the default bars view. Unknown extension fields remain allowed.
📄 Example
The model outputs this fence (written for the browser — you don't need to read it):
{"title":"Order overview","items":[
{"type":"stat","label":"Total revenue","value":"¥128,430","delta":"+12.4%"},
{"type":"stat","label":"Orders","value":"1,024","delta":"-3.1%"}
]}
What you see: two stat cards.
ECharts example
{"title":"Q1 Revenue","items":[
{"type":"echart","title":"Monthly Revenue","preset":"bar","data":[
{"label":"Jan","value":98},
{"label":"Feb","value":112},
{"label":"Mar","value":128}
]}
]}
What you see: a themed bar chart with tooltips and axis labels — rendered by ECharts, lazy-loaded on demand.
🔧 How it works
The model writes the interface description as JSON inside a dsh-ui fence; the browser-side renderer (src/client) claims this language through the main repo's fence-registry interface and renders it. Components are whitelisted — the model can't smuggle in HTML/scripts; function expressions go through a standalone parser, never eval.
The core render package stays light (≈110 KB min / 28 KB gzip); the mermaid, three.js, and echarts engines are bundled separately as on-demand assets (loaded through the plugin's self-registered HTTP routes the first time they're used), so startup only downloads the rendering core.
Export a GenUI artifact
Settled GenUI blocks expose Export → HTML and GenUI JSON. The .html file includes its renderer, styles, KaTeX WOFF2 fonts, and only the chart engines used by the spec. Local controls continue to work; model actions are disabled. Relative media URLs become absolute URLs based on the export page; media still requires network access when the file is opened offline. .genui.json preserves the original media URLs, normalized spec, durable interaction state, locale, and theme. Custom components disable HTML export, and export errors appear beside the menu. Hosts can use createGenuiArtifact, parseGenuiArtifact, serializeGenuiArtifact, and buildStandaloneHtml from @changfenhuang/dsh-genui/embed; @changfenhuang/dsh-genui/assets/standalone resolves to the built runtime script for copying or serving.
❓ FAQ
- Rendering as a code block? First check the browser console for
[genui] client active; fence-channel=registry|dom. If absent, the client bundle was not activated even if its URL returns 200 — align the profile dependency,package.json.name,cordis.patch.yml, ModuleLoader id, and configured bundle name. If present, inspect the fence label/body; registry-less hosts automatically use the DOM channel. - Chat UI goes blank when rendering a dsh-ui fence? This dsh-genui release requires DSH
^0.1.2-rc.1 || ^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1; users on DSH<=0.1.1-rc.xshould use dsh-genui0.9.8. dsh: pnpm not found on PATH? Install pnpm, then open a new terminal and retry (corepack enableornpm i -g pnpm).- npm install returns 404? The npm package is public and requires no login. Run
npm view @changfenhuang/dsh-genui versionto verify the package name and public registry; if a newly published version still returns 404, retry shortly. - Installed but scene3d/mermaid/echarts don't render? The engines (mermaid / three / echarts) are no longer inlined in client.js — they load on demand the first time they're used (
/plugins/@changfenhuang/dsh-genui/assets/*.js, hosted by the plugin's own HTTP routes). First restart dsh web + hard refresh (Cmd+Shift+R); still broken, remove and reinstall (dsh plugin --profile web remove @changfenhuang/dsh-genui, then add again). Hosts without the asset routes degrade to source/load-error hints — update dsh. - Model not outputting fences? New sessions pick it up after a restart; or just say "output it with dsh-ui".
- No lib/ after cloning? Build it yourself:
pnpm install && pnpm run check.
🧑💻 Development
pnpm install
pnpm run check # type check + full tests + build
With the locked dependencies installed, the check script (pnpm run check or npm run check) uses the pinned DSH 0.1.2-rc.1 release packages.
pnpm run check:host-api dsh-v0.1.7-alpha.1 and pnpm run check:host-api dsh-v0.1.7-alpha.2 install the corresponding published DSH packages in an isolated workspace and run TypeScript typecheck plus tsdown build. CI runs both checks before packed host smoke tests.
Run node scripts/verify-pack.mjs --keep to retain the verified tarball for inspection or e2e use. The default node scripts/verify-pack.mjs removes its temporary directory after verification.
Real-device e2e
The real chain end to end: start a temporary dsh web → install the plugin → send a message in a browser so the model outputs a dsh-ui fence → assert the rendering → click an action button → assert the model responds (event-loop closure):
export DSH_ROOT=/path/to/deepseek-harness-0.1.2-rc.1
export DSH_BIN="$DSH_ROOT/apps/cli/lib/bin.js"
DEEPSEEK_API_KEY=sk-... node scripts/e2e.mjs # link-installs the current workspace
Build the DSH 0.1.2-rc.1 checkout first. Set DSH_ROOT to that checkout and DSH_BIN to its apps/cli/lib/bin.js, as shown above; also provide pnpm, DEEPSEEK_API_KEY, and the main repo's web build output. On PASS it saves an e2e-final.png screenshot.
Visual e2e (no model key)
For style/component iterations, a visual smoke that needs no API key: boots a real DSH 0.1.2-rc.1 checkout with the plugin link-installed, injects the component gallery fence through the DOM channel, renders it in headless Chrome, screenshots the full page, and exercises local interactions (table sort, quiz judging, tree collapse, numeric alignment) with hard assertions. Set DSH_ROOT and DSH_BIN to that checkout as shown above before running it.
npx tsx scripts/e2e-visual.mts # → .e2e-artifacts/gallery.png + interactions.png
npx tsx scripts/e2e-visual.mts --keep # keep the scratch DSH_HOME for debugging
Overridable: --port 3098, --out <dir>, DSH_BIN (set it to the apps/cli/lib/bin.js inside the DSH 0.1.2-rc.1 checkout), PLAYWRIGHT_PATH (defaults to the global playwright-core).
🗺️ Roadmap (evaluated)
| Direction | Verdict | Rationale |
|---|---|---|
| Incremental patching (model sends diffs, not full specs) | Not doing | A fence costs 200–800 tokens; resending is nearly free; a patch protocol's teaching cost and error rate aren't worth it. Revisit if sub-second auto-refreshing panels ever appear |
| Action delivery | ✅ Discrete interactions send immediately; slider drags debounce by action and id |
Discrete gestures remain individually observable while slider drags send their final value |
| Cross-session state persistence (replay restores tabs/switches) | Not doing | Replay-reset is the more correct default (the model has already updated the UI with a new fence); state survives naturally during streaming |
| MCP adapter / standalone gallery page / i18n | Not doing | No cross-tool demand signal; gallery material is covered by gallery.ts + demo-prompts + README screenshots; only 6 built-in strings |
🔗 Friendly links
📄 License: MIT
Install
Install the catalog once, then DeepSeek Harness can find and install any plugin from this site automatically:
dsh plugin add dshbase-catalog Then say "install dsh-genui for me" — your agent finds it in the directory and installs it. Docs: dshbase-catalog · verified packs.
This plugin is GitHub source (not published to npm) — install it straight from the repo:
Web profile:
dsh plugin --profile web add github:omdsh-dev/dsh-genui Headless (CLI) profile:
dsh plugin --profile headless add github:omdsh-dev/dsh-genui Test report
Verified: L1 install + L2 load + L3 runtime from GitHub source on dsh 0.1.0-rc.6.
When to use it
Render interactive UI components — layout, charts, plots, forms, quizzes, mermaid — directly in chat output.
Who it's for
Users who want the model to produce working widgets for data visualization or structured input capture.
For developers — extending it
Extend the component library — new chart types, form controls, or diagram renderers the model can target.
- Dangerous command (high)