Enables the DSH model to directly generate interactive charts, forms, statistical cards, and other UI elements in responses. Users can
- Language
- TypeScript
- License
- MIT
- Branch
- main
Install
$ dsh plugin --profile web add @changfenhuang/dsh-genuiRun the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial
Install via your agent
Install the DeepSeek Harness plugin omdsh-dev/dsh-genui for me: review the repository at https://github.com/omdsh-dev/dsh-genui first, then run the install command and verify the plugin loads successfully.
Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.
One-line Positioning
Let DSH model generate interactive UI components (stat cards, charts, forms, panels) directly in chat responses. Users can browse data, interact with components, and pass interactions back to the model without leaving the conversation.
Core Capabilities
- Stream-render JSON-described components in model responses: model renders while writing, first completed component appears immediately without waiting for entire response
- Provides 30+ whitelisted components: text, cards, tables, charts (bar/line/pie), forms (input/select/checkbox/switch/slider/radio/submit), progress bar, step bar, timeline, file tree, Mermaid charts, 3D scenes, math function plotting, conversational Q&A, etc.
- Register
render_uitool: model can also render the same component spec as a tool-row card via tool call (suitable for "deliverable-type" UI) - Register
validate_dsh_uitool: model self-checks before emitting complex fences, bad nodes are auto-fixed with the fixed JSON attached - Support component action event flowback: buttons/inputs/switches with
actiontrigger a[genui-action]message back to the model when clicked, model updates UI accordingly (300ms trailing debounce) - Provide session top panel: model can continuously stack/replace components in the panel (
/panelcommand to invoke), drag top border to change height
Technical Implementation
- Language: TypeScript
- Key Dependencies:
@deepseek-ai/cordis(Cordis injects into host),@deepseek-ai/dsh-client-runtimeand@deepseek-ai/dsh-client-ui-primitives(browser rendering primitives),@deepseek-ai/dsh-tools(register render_ui/validate_dsh_ui tools),react(UI framework) - Architecture Pattern: Plugin consists of "server half + browser half" two parts. Server half (
src/plugin/index.ts) injects into host via Cordis, insertsdsh-uifence language description into system prompt, registers two tools, and mounts an on-demand resource route for mermaid/three on host WebServer; browser half (src/client/index.tsx) detects at startup whether host provides fence-registry extension point, chooses "registry channel" or "DOM channel" to render fence - Entry Files:
src/index.ts(package entry) →src/plugin/index.ts(server logic); client logic insrc/client/index.tsx
Use Cases
Upgrade model responses from plain text to interactive panels: business monitoring, order/revenue trend display, teaching question cards with self-grading, flowcharts and architecture diagrams, real-time function curve parameter tuning, lightweight form collection, etc. Best suited for "user asks one question, model replies with clickable components" scenario, eliminating back-and-forth jumps to external BI/tool pages.
Prerequisites and Compatibility
| Dependency | Min Version | Description |
|---|---|---|
| DSH (@deepseek-ai/dsh-client-runtime / dsh-client-ui-primitives / dsh-client-ui-slots / dsh-client-ui-tool / dsh-invariants / dsh-llm / dsh-system-prompt / dsh-tools / cordis) | ^0.1.0-rc.6 | Host needs fence-registry extension point or DOM channel rendering capability (DOM channel covers any 0.1.0-rc.6+ build) |
| Node.js | ^22.19.0 or >=24.0.0 | Required by install script and build script |
| pnpm | >=11.7.0 and <12 | Required by dsh plugin command; can be enabled via corepack |
| React | ^18.0.0 or ^19.0.0 | Injected via peerDependencies, provided by host |
| Platform | Cross-platform | No os/cpu restrictions; mermaid and three loaded on-demand as IIFE resources |
Installation
dsh plugin --profile web add github:omdsh-dev/dsh-genui
Configuration
No additional configuration required. After installation, host automatically injects dsh-ui fence language segment, registers render_ui and validate_dsh_ui tools, and mounts /plugins/@omdsh-dev/dsh-genui/assets/* static resource route on host WebServer for on-demand loading of mermaid and three engines. Model output fence specs have built-in hard resource limits (200 nodes / 8 nesting levels / field length limits), excess parts are silently dropped without crashing the UI.
FAQ
Q: Fence renders as regular code block, what to do?
A: Check three things in order: whether host dsh has fence-registry extension point (falls back to DOM channel if not), whether dsh plugin --profile web list shows this plugin, finally restart dsh web and hard refresh (Cmd/Ctrl+Shift+R).
Q: Installation fails with pnpm not found on PATH, how to fix?
A: dsh's plugin subcommand depends on pnpm. Run corepack enable or npm i -g pnpm to install, must open a new terminal for PATH to take effect, confirm pnpm -v outputs before retrying.
Q: Installed but mermaid or 3D scene won't render?
A: These two engines load on-demand, first use pulls from /plugins/@omdsh-dev/dsh-genui/assets/*.js. Hard refresh browser once; if still fails, remove and reinstall (dsh plugin --profile web remove @omdsh-dev/dsh-genui then add).
Q: Model doesn't output dsh-ui fence, only returns text?
A: New sessions need dsh web restart to take effect; or directly say "use dsh-ui to draw a stat panel" in your question to remind the model.
Q: Just cloned, no lib/ directory, can I use it directly?
A: Cannot use directly. lib/ is build output, need to run pnpm install then pnpm run check (builds automatically).
Q: Will too many fence nodes get truncated?
A: Yes. Plugin has hard resource limits: max 200 nodes per fence, 8 nesting levels; excess parts are silently dropped without crashing the UI.
Q: How to uninstall this plugin?
A: Run dsh plugin --profile web remove @omdsh-dev/dsh-genui, then restart dsh web; fence will naturally degrade to regular code block without polluting existing sessions.
Learning Curve
Beginner — ordinary users need no configuration; model's syntax is automatically taught via SKILL.md and system prompt, users only need to describe "use dsh-ui to draw an XX panel" in prompt. Developers doing secondary development need familiarity with React, Cordis, and DSH client runtime.
Known Issues and Limitations
- Hardcoded resource limits: single fence max 200 nodes / 8 nesting levels; panel max 200 nodes / 200 appends, after hitting limit model needs to send
replaceto rebuild (src/client/guard.ts:25-65,src/client/panel-store.ts:29-34) - mermaid / three engines load on-demand from
/plugins/@omdsh-dev/dsh-genui/assets/*.js; very old host builds without this resource route degrade to source/failure message, need to update dsh (README.md:136) - Tool registration depends on optional
toolsservice: hosts without tools channel still retain fence channel but loserender_ui/validate_dsh_uitools (src/plugin/index.ts:157-197) - DOM channel may wipe plugin-mounted root when host React re-renders: plugin uses MutationObserver + 1 second scan as double insurance fix, but extreme cases still have visual jitter (
src/client/dom-fence.tsx:25-28) - Passwords, API Keys, access tokens and other "secrets" in fence content are disabled by protocol layer: model is prompted to refuse fetching, but this plugin has no runtime forced masking, relies on model following prompt
English · 简体中文
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.
The model no longer just answers in text. Install this plugin, ask "how are this month's orders doing", and it renders a clickable data panel right inside the answer as it analyzes: watch trends, drag sliders, hit refresh — and the model actually responds.
Real output: an interactive monitoring panel rendered by the model (click "refresh" and it regenerates the data)
Player won't load? Download the mp4. Four-act demo script: demo-prompts.md.
⚠️ Read this first: dual-channel rendering (works with any dsh build)
The plugin ships two rendering channels and picks one automatically at startup — no dependency on a specific host version:
- 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 stock DSH and older 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).
Whichever channel is active, components, interactions, panels, and persistence behave identically.
✨ 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 installed (any open-source build works — the plugin picks its rendering channel at startup, see "dual-channel rendering" above)
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 (one command, all dependencies included):
# Public GitHub install (works without an npm account)
dsh plugin --profile web add git+https://github.com/omdsh-dev/dsh-genui.git
⚠️ 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 git URL form above; reservelink:for local development iteration (see below).
Restart dsh web + hard refresh, then in a new session say "use dsh-ui to draw a stats dashboard" to verify.
One-click script (recommended)
After cloning, just run it — the script checks the prerequisites above, performs the install, and prompts you to restart:
git clone https://github.com/omdsh-dev/dsh-genui.git
cd dsh-genui
./scripts/install.sh
Developer iteration (link mode)
cd dsh-genui
pnpm install
dsh plugin --profile web add link:$PWD
🧩 What it can do
- 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…
- Function plots:
plotdraws curves; parameter sliders redraw in real time, with optional auto-animation
- Quiz:
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'sfields - Secrets 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/switches/inputs/dropdowns/checkboxes/radios/textareas/quizzes carry
action; click or blur sends back to the model, which updates the UI; same-name actions are debounced with a 300 ms trailing edge — rapid clicks merge into one (last value wins) - Tool 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 rebuild - Self-healing & limits: every fence passes a spec guard — bad nodes are silently dropped, numbers clamped, strings truncated; the whole tree is capped at 200 nodes / 8 nesting levels; pathological specs never crash the UI
- 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 screen - Accessibility: 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: SKILL.md (also copyable to ~/.dsh/skills/genui/ to boost the model).
📄 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.
🔧 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 and three.js 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.
❓ FAQ
- Rendering as a code block? Check three things: your dsh build has fence-registry (see "dual-channel rendering" at the top — builds without the extension point fall back to the DOM channel),
dsh plugin --profile web listshows this plugin, restart + hard refresh. - Chat UI goes blank when rendering a dsh-ui fence? Your dsh is too old — update dsh first, then reinstall the plugin.
dsh: pnpm not found on PATH? Install pnpm, then open a new terminal and retry (corepack enableornpm i -g pnpm).- Stuck on git credentials / 404 during install? The repo is public (
omdsh-dev/dsh-genui) — the git URL above needs no login; a 404 for@omdsh-dev/dsh-genuimeans the npm package has not been published yet. - Installed but scene3d/mermaid don't render? The engines (mermaid / three) are no longer inlined in client.js — they load on demand the first time they're used (
/plugins/@omdsh-dev/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 @omdsh-dev/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
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):
DEEPSEEK_API_KEY=sk-... node scripts/e2e.mjs # link-installs the current workspace
DEEPSEEK_API_KEY=sk-... node scripts/e2e.mjs --install git # friend path (git URL)
Prereqs: dsh/pnpm on PATH, DEEPSEEK_API_KEY, and the main repo's web build output (playwright resolves it from the main repo). On PASS it saves an e2e-final.png screenshot.
🗺️ 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 debounce/dedup | ✅ Done (300 ms trailing edge, per action name) | Rapid-click spam is real friction; one choke point |
| 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 |
Tests parse the dsh source (vitest.config.ts's DSH_ROOT, default ~/.dsh/source/current).
🔗 Friendly links
📄 License: MIT
Read the usage guide →
Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.
Listing badge
[](https://deepseek-plugin.org/plugins/omdsh-dev/dsh-genui)Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.