dsh-showme-html
Manifest valid★ 4DSH Plugin: Displays HTML pages written in the workspace within the conversation, and lets users' feedback on the page quickly get back to the agent. Includes four preset styles.
dsh-showme-html
让 agent 把成果摊开给你看,让你点着回话。
DSH(DeepSeek Harness)插件。给 agent 一个快捷入口:把工作区里写好的 HTML 页直接展示在对话里; 并让用户在页面上"点名 + 表态"产生的文本,一行一条地回到 agent 手里。
仓库:https://github.com/liceses/dsh-showme-html

上图是一次真实交付,一屏里把三件事都拍到了:
- 卡片头部 ——
show_html徽章、标题、体积,以及 刷新 / 全屏 / 浏览器打开; - 页面本体 —— 瑞士国际主义皮肤,顶部那排还能现场换皮肤(换的只是那一个
<link>); - 底部的反馈条 ——
pain-markdown 补充就是用户在页面上点选之后,由页面替他组织好的一行回执。 他接着按「填入输入框」,这句话就追加进了对话输入框。
它解决什么
一、读完一屏 Markdown,你还是"没看见"
agent 干完一件大事,甩给你 200 行 Markdown。你读完了,但你没有看见那个东西。
十几个变体的像素级差异、时间线、两种方案并排、改前改后对比——这些本质上是空间与视觉的信息。 压成文字就是降维,你只能在自己脑子里把它重新拼回一张图。
判断标准很简单:这件事你是"读"更省事,还是"看"更省事。 前者继续用 Markdown;后者交给这一页。
二、AI 产出了十几个变体,而你叫不出它们的名字
这是最初做出这个插件的动机,原话是:
"我有很多 AI 生成的图片或者网页组件,我没办法一个一个准确地叫出它们的名字, 他们的名字是 AI 可以给出的。我希望我选择哪一个,就可以得到它的名称用来和 AI 交流, 还能表达我的意见,比如说同意,或者要某个组件的风格。"
于是对话常常变成——"把第三个、就是那个带渐变的那张再改一下"。而你嘴里的"第三个", 和 agent 理解的"第三个"是不是同一个东西,全凭运气。
这个插件给每样东西一个稳定、唯一、页面上看得见的名字(hero-banner-v3、f2、shot-07)。
指认从此没有歧义。
三、反馈回去的路上,意思总会走样
读页面 → 组织语言 → 打字 → agent 猜你指的是哪一项。这条路上每一步都在丢信息。
这里把它压成 点两下 → 页面替你写好一句话 → 粘回去:
同意 hero-banner-v3(第 3 版),按它的风格继续;不要 stats-panel-v1(颜色太灰)。
这句话放进一个能鼠标选中、能 Ctrl+C 的 textarea 里——这是唯一永远可用的回传方式。
页面还会顺手把它 postMessage 给卡片,于是你能一键追加进输入框(不覆盖你已经写了一半的草稿)。
四、AI 写出来的页面有"廉价感"
标题和正文一样大、行高挤成一团、颜色随机、还得连 CDN 才敢用。
插件附了四套预设样式,每套都把字号刻度与间距节奏做成设计令牌—— 那才是"好看"的真正来源,不是配色。四套吃同一套语义标记,所以换皮肤一个字都不用改标记。
一次完整的使用长这样
1. agent 写完东西,按共用语义标记写一页 HTML,挑一套皮肤:
<link rel="stylesheet" href="https://github.com/liceses/dsh-showme-html/blob/HEAD/presets/swiss.css">
2. 调一次工具:
show_html({ path: ".dsh/showme/review.html",
title: "设计评审",
note: "挑一个变体" })
3. 卡片直接出现在对话里 —— 不用去文件树里翻、不用切浏览器。
卡片头部:刷新 / 全屏 / 在浏览器打开原文件。
4. 你在页面上点选、写备注;页面实时把它组织成一行一条的文本。
5. 点「填入输入框」—— 追加到你已有的内容后面。或者直接选中复制。
不新增任何常驻服务:宿主就是 DSH 已经在跑的那个 web server;反馈在浏览器内完成。
页面默认落在 .dsh/showme/(隐藏目录,不污染源码树、不进 git)。
装
# 从 GitHub 直接装
dsh plugin --profile web add "github:liceses/dsh-showme-html"
# 或者从本地源码目录装(开发时用,改完源码好追踪)
dsh plugin --profile web add "link:<本目录绝对路径>"
# 两种方式都需要重启 dsh web 才生效(宿主半区在启动时装载)
配套的 showme-report skill 用一个目录联接挂进去,热发现、不用重启:
New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\skills\showme-report" `
-Target "<本目录绝对路径>\skill\showme-report"
这个联接是可选的。 插件在启动时会检查它:在,就什么都不做(让文件系统 provider 继续提供"改完 SKILL.md 立刻生效"的热发现);不在,就用
ctx.skills.register()运行时注册兜底,保证装了这个插件就有这个 skill。之所以做这层兜底:联接在仓库之外,任何一次清理都可能把它悄悄弄丢, 而症状只是"模型不再加载 skill",从外面完全看不出来。想知道它还在不在:
npm run doctor # 一体检:资产 / skill 投递 / profile 装配 / 运行中的路由
没有构建步骤:
lib/就是成品,仓库里没有"编译产物"这一层。
核心能力
工具 show_html | agent 调一下,就等于"把这一页给用户看"。参数只有 path / title / note;不收 HTML 正文——正文进参数会白烧一遍 token,而且页面会被反复改,留成文件才可迭代、可留档。 |
| 镜像读路由 | GET /api/showme/raw/<sessionId>/<工作区相对路径>。按工作区目录结构分发,于是页面里的相对引用天然可用(<img src="https://github.com/liceses/dsh-showme-html/blob/HEAD/stills/a.png">、预设 CSS、下级页),同一个地址就是「在浏览器打开原文件」。 |
| 对话内卡片 | 标题 / 源文件路径 / 刷新 / 全屏 / 浏览器打开;非可见的卡片不持有活 iframe(历史里几十张卡片不会把会话拖卡)。 |
| 画面内全屏 | 走框架级浮层,Esc 退出回到原位、滚动位置不丢。 |
| 反馈两条路 | ① 页面 postMessage 给卡片 → 一键填入输入框;② 页面 POST /api/showme/feedback 追加到 <工作区>/.dsh/showme/inbox.jsonl。都不实现也不影响——页面里的可选中文本本来就是通用交换格式。 |
| 四套预设样式 | 见下。 |
| 回执体检 + 引用体检 | show_html 展示前扫一遍页面:缺可点名的 id、缺 postMessage 回传(只给"可复制文本"不算),或引用了不存在的相对资源时,把提醒随工具结果交回给模型。实测过 skill 被加载、被读,却没被照做——工具结果是这条链路上唯一不依赖模型自觉的通道。 |
skill showme-report | 教 agent 怎么写这一页:挑皮肤、回执契约、交换格式、沙箱坑。不含版式要求——版式仍然是 agent 的活。 |
四套预设样式
放在插件包的 styles/,宿主在你第一次展示页面时自动落到工作区
<工作区>/.dsh/showme/presets/(create-only:已存在的不覆盖,所以你可以自己改)。
页面引用就是一行:<link rel="stylesheet" href="https://github.com/liceses/dsh-showme-html/blob/HEAD/presets/soft.css">
| slug | 名字 | 什么时候用 |
|---|---|---|
soft | 柔和现代 | 默认。通用汇报、进展同步、长时间阅读 |
swiss | 瑞士国际主义 | 编辑部长文、结论多的分析、要权威感 |
brutal | 新野兽派 | 设计评审、多变体挑选、强指认感 |
blueprint | 蓝图 / 工程图 | 架构、数据流、时序、依赖关系 |
四套吃同一套语义标记(.page .masthead .kicker .lead .section .card .cols
.stat/.num .chips/.chip .btn .table .callout .evidence .timeline .kv
.code .muted .mark .rule)——换皮肤不用改标记。这也是「可切换预览器」能成立的前提。
styles/index.json是清单,里面那行default就是默认皮肤,改一行即可。soft/swiss跟随系统明暗;brutal固定亮色;blueprint固定暗色。- 自包含:无
@import、无外链字体、无url()、无 CDN。断网也完整。 - 想用 StyleKit 目录里的其他风格(148 种)?
它的
tokens/components是 Tailwind 类名与 React JSX,单文件静态页直接用不了; 把它当约束清单(colors/doList/dontList/philosophy)自己翻译成原生 CSS。 具体路径写在skill/showme-report/SKILL.md§2.4。 - 预设不是强制的:要评审/复刻某个具体系统的视觉时(比如评审 DSH 插件自己的设置页),
用那个系统的官方调色板才对——这时不要写
<link>,否则两套视觉会叠在一起。 理由写在 skill §2.3。
模板:三份骨架
skill 用文字规定"回执怎么做",模型每次都重新实现一遍,于是控件位置、组句、通道时机反复出错。 模板把"实现"变成"填数据"。
宿主在你第一次展示页面时把骨架落到 .dsh/showme/templates/(create-only):
| 骨架 | 形状 | 什么时候用 |
|---|---|---|
review.html | 逐项表态 | 一排条目,每条一个判定 + 备注。带图不带图都行。最常用 |
pick.html | 候选挑选 | 单选 / 多选。封面选哪张、这几张素材留哪几个 |
report.html | 图文汇报 | 有图 / 表 / 时间线、不需要逐项表态;每小节一个轻回执 |
一页 = 外壳 + 内核 + 数据,你只写最后那个:
templates/<shape>.html 外壳(**直接 write,别 cp** —— 见下)
templates/core.js 内核:渲染与交互全在这里,三个形状共用一份,别读、别改
<名字>.html 你写的页面
<名字>.data.js ← 你唯一要写的,内容就是 window.SHOWME = { … }
⚠️
templates/在新工作区里还不存在。 宿主是在第一次show_html时才创建它的, 而 agent 需要外壳的动作排在流程开头——所以第一步cp必然失败。 落点已改成:先落地、后校验,直接write这个外壳就行:
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"><title>—</title>
<link id="skin" rel="stylesheet" href="https://github.com/liceses/dsh-showme-html/blob/HEAD/presets/soft.css"></head><body>
<div class="page"><div id="sm-masthead"></div><div id="sm-content"></div><div id="sm-receipt"></div></div>
<script>window.SHOWME_SHAPE = 'review'</script>
<script src="https://github.com/liceses/dsh-showme-html/blob/HEAD/templates/core.js"></script></body></html>
'review' 换成 'pick' / 'report' 就是另外两个形状。你引用的 core.js 与 presets/*.css
会在展示那一刻已经就位,浏览器取它们时一定存在。
想先把资产催出来(例如要读 templates/README.md):随便调一次 show_html,
哪怕路径不存在——落地排在校验之前,失败也会跑。
文件名必须同名:mypage.html ↔ mypage.data.js(内核按页面文件名推导)。
📖 数据字段表在
templates/README.md——三个形状、全部字段、 10 种内容块、常见错误,4KB 一份。它也会落到.dsh/showme/templates/README.md。不要读
core.js:那是实现(20KB),字段表那一份就够了。 实测过:一个会话为了搞清"它认哪些块类型",把内核通读了一遍(≈5–6 千 token), 而答案就是那张 10 行的块类型表。test/templates.test.mjs里有一条专门盯着 这份文档与内核一致性的测试——内核加了块类型而 README 没跟上,测试会红。
- 为什么最省:只写数据(几百 token)。老写法每次从头写一个 10KB 页面 ≈ 3–4 千 token, 而且每次都可能把回执写错。
- 为什么数据走
<script src>而不是fetch:展示页在 sandbox 的不透明源里,fetch同源资源会被当成跨域请求拦掉(我们不发 CORS 头)。经典 script 不受这条限制。 - 内核只写一份:三个形状共用
core.js。这一路我已经被"抄来抄去抄出 bug"咬过一次 (照抄dsh-text-drop的props.input,而那个属性根本不存在),不想再来第二次。 - 模板是可选骨架,不是形式强制:架构图、时序图、长文分析不是"条目式"的形状, 那就自己写——§3 的回执契约、§4 的通道、§5 的沙箱约束照样适用。
examples/ 里的 template-demo.html 是一个真实用例:外壳 548 字节,内容全在数据文件里。
安全模型(两层,刻意不同)
同一个地址,两种档位:
| 卡片内(iframe) | 真浏览器新标签页 | |
|---|---|---|
| 生效的 sandbox | iframe 属性 ∩ 响应头 CSP | 只有响应头 CSP |
| 脚本 | ✅ | ✅ |
表单 / 下载 / alert / 弹窗 | ❌ | ✅ |
| 能碰到 DSH 应用本身 | ❌(不透明源) | ❌(CSP sandbox 强制不透明源) |
所以卡片是安全模式,工具栏的「浏览器打开」是逃生门:功能完整,但依然读不到应用数据。
其余围栏:
- 回环围栏:只服务
127.0.0.1/::1且 Host 是回环的请求。 ⚠️ 不要用Sec-Fetch-Site做判断——在这里它完全冗余,而且会咬自己两次: 展示页跑在sandbox的不透明源里,它加载自己的图片、嵌套页、页内<a href>跳转、 以及自己的fetch,统统被浏览器标成cross-site,Origin是字面量"null"。 真正管用的是只收回环地址 + Host 必须是回环 + 一个 CORS 头都不发 (跨站方发起了也只会拿到不透明响应,拿不走工作区文件)。 - 工作区根 + realpath 双重校验:
..、编码穿越(%2f/%5c)、指向工作区外的软链接,一律 403。 - 扩展名白名单:不在表里的一律 415。
X-Content-Type-Options: nosniff、Referrer-Policy: no-referrer、Cache-Control: no-store。
已知限制
- 不缓存:每次刷新都重读文件(故意的——页面会被反复改)。
- 不做范围请求:大视频文件会整个读进内存再发。
- 不做文件监听:改完文件要按卡片上的「刷新」(自动刷新属于后续里程碑)。
- 页面在卡片里能力受限:见上表;需要
localStorage/ 表单 / 下载时请用「浏览器打开」。 - 不支持
file://链接:浏览器禁止从 http 页面跳到file://,所以「在浏览器打开」走 HTTP 路由。 - 单页上限 8 MiB;反馈正文上限 256 KiB。
ctx.get('sessions')/ctx.get('sessionQuery')在路由所在的插件上下文里取不到 (Inspect 把它们标注为optional)。所以权威来源改成工具执行时记下的「会话 → 工作区根」, 服务查询只作兜底;认不出来时错误信息会回报具体哪个服务缺了。- 重写页面要记得换
<link>:预设是外链的,页面本身不携带样式。
仓库结构
lib/index.js 宿主半区:show_html 工具 + 镜像读路由 + 反馈落盘 + 预设落地
lib/client.js 浏览器半区:对话卡片 + 画面内全屏 + 输入框镜像(closure-factory)
styles/*.css 四套预设样式(插件资产,宿主动态落地到工作区)
styles/index.json 预设清单:一行 default 就是默认皮肤
templates/ 三份骨架 + core.js(插件资产,同样动态落地)
skill/showme-report/ 产品核心:功能契约 + 交换格式 + 沙箱坑
examples/ 示例页源码(页面冒烟测试的夹具)
test/ 离线验收:宿主 / 客户端 / 预设 / 模板 / 页面 五组
docs/ 需求演进与定稿方案
.dsh/ 不进仓库——那是 DSH 在本工作区里的运行痕迹(展示副本、预设与模板的落地副本、反馈信箱)。
开发
npm test # 221 项离线断言:宿主 84 / 客户端 33 / 预设 38 / 模板 29 / 页面 37
npm run doctor # 装机体检:资产 / skill 投递 / profile 装配 / 运行中的路由
# 装配是否真的生效 —— 对着运行中的 3080 打真实请求
node test/live-probe.mjs <sessionId>
# sessionId 在 ~/.dsh/sessions/--<工作区路径转义>--/session-<uuid>/ 的目录名里
零构建、零依赖:lib/index.js 与 lib/client.js 就是成品,package.json 里没有任何
dependencies / devDependencies / peerDependencies。
- 宿主半区不 import 任何
@deepseek-ai/*。以link:安装时插件位于工作区外, Node 从真实路径向上找不到 profile 的node_modules,外部 import 解析不了 (同 profile 里dsh-text-drop/dsh-workspace-tree的宿主半区同样只用 node 内置模块)。 因此工具定义按ToolDefinition契约手写:{name, description, parameters}+output: {schema, render, presentationMeta?}+execute,其中parameters/output.schema都是已编译好的 JSON Schema。代价是参数校验要自己写,execute里做了。 - 浏览器半区是 closure-factory 形态(
window.__ModuleLoader__.load({id, factory})), 平台模块(react等)由加载器的模块表提供。
改完什么要做什么(实测结论,没有捷径)
| 改了 | 生效方式 |
|---|---|
skill/showme-report/** | 热发现,什么都不用做 |
lib/index.js(宿主半区) | 重启 dsh web(改配置触发不了重载,实测三次都不行) |
lib/client.js(浏览器半区) | 重启后由 client-modules 扫描;之后改它需要浏览器 F5 |
写测试时的坑
桩必须照真实契约写。本项目就吃过一次:宿主测试的假 sessions 返回了 {meta:{cwd}},
而真实 Session 只有 header.cwd,于是离线 46 项全绿、线上全红。
离线测试证明逻辑,live-probe 证明装配——两个都要。
MIT © 2026 liceses
Comments
Loading…
Similar plugins
by masknull
DSH 插件:在每个会话的系统提示词最顶部注入自定义提示词,并可在 Web 设置页中即时编辑。
★ 0
MIT
TypeScript
Aug 31, 2026
dsh plugin --profile web add dsh-session-promptby buhuikongpan
DSH 分层插件管理器:原生插件按 系统层/WebUI 层/工具层 只读展示,用户扩展支持停用/启用、补登记、卸载与可编辑描述。
★ 9
JavaScript
Aug 20, 2026
dsh plugin --profile web add dsh-pluginmanagerby BiKing567
DSH 插件:把每个子代理渲染成主对话里可点击的卡片,点击直接进入子代理会话(与标题栏同一路径)。| DSH plugin: render every subagent as a clickable inline card that opens the child session — same navigation the header's subagent catalog uses.
★ 0
MIT
JavaScript
Aug 31, 2026
dsh plugin --profile web add dsh-subagent-panelby EChan684
把 agent 的网页验证请求路由进 DSH 窗口右侧栏的内嵌浏览器(dsh-better-sidebar),不再打开系统浏览器遮挡会话界面;自动加宽面板、只截页面区域、并在会话首轮开放工具
★ 0
MIT
JavaScript
Sep 3, 2026
dsh plugin --profile web add dsh-web-verify-panelby Kazusa1085
DSH插件:提供“模型能力”设置页面,用于声明模型输入模态、上下文窗口、输出上限及图像配额,无需手动编辑 `settings.yaml` 文件。
★ 0
MIT
JavaScript
Sep 9, 2026
dsh plugin --profile web add dsh-awesome-model-settingby BPTumbleweed
DSH「置顶对话」插件:会话头部与会话行内一键 📌,把重要对话钉在侧栏分组顶部;宿主端只存事实(原子写 JSON + 信任栅栏内的 HTTP 接口),排序由客户端在 DOM 层完成。零运行时依赖、能力探测与熔断、面向跨版本升级设计。
★ 0
MIT
JavaScript
Sep 16, 2026
dsh plugin --profile web add dsh-pin-session