Back to home@LiweiDonVee

dsh-tavern-renderer

Independent DSH message renderer with sanitized HTML/CSS, macros and eight immersive document templates.

Stars
1
Language
JavaScript
Created
Sep 8, 2026
Updated
Sep 8, 2026
GitHub repo

Introduction

DeepSeek Harness Tavern Renderer

An independent DSH message renderer with ordered regex/macros, a constrained expression evaluator, sanitized Markdown/HTML/CSS, and eight original document templates. Tested with DSH 0.1.2-rc.1. Requires Node.js 24 for development; no arbitrary JavaScript execution. MIT licensed. The installation commands below also work in a POSIX shell. Keep the checkout in place when installed as a local link.

面向 DeepSeek Harness 的自包含“酒馆式”消息渲染插件。它以 Harness 原生 Cordis keyed slot 接入,不改 Harness 源码,并提供有序正则、宏/变量、安全表达式、Markdown、HTML/CSS 清洗、角色模板、主题系统,以及八种可复用的沉浸式文档模板。

规格依据见 SPEC.md,架构与安全决策见 DESIGN.md,外部参考与许可证见 SOURCES.md

安装和启用

当前适配目标为 DeepSeek Harness 0.1.2-rc.1,Node.js 24+。首次从源码安装需要构建;不包含用户配置或会话数据。它是独立社区插件,与 DeepSeek AI 无隶属关系。

git clone https://github.com/LiweiDonVee/dsh-tavern-renderer.git
cd dsh-tavern-renderer
npm ci
npm install --prefix .host --no-audit --no-fund @deepseek-ai/dsh@0.1.2-rc.1
npm run check
npx --yes @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add .
npx --yes @deepseek-ai/dsh@0.1.2-rc.1 web

启动后使用终端打印的完整 Web URL(含一次性 token)进入;不要把 token 固化进配置、分享链接或测试产物。认证由宿主处理,插件无需自行管理 token。

bundle 会向 Web profile 插入 tavern-renderer 行。客户端随后以 priority: -100 覆盖 assistant-stepusersteering 三个消息 renderer。消息 slot 由 @deepseek-ai/dsh-client-ui-chat 声明,语言域为 chat;图片使用宿主传入的 renderMessageImages,保留宿主授权加载。删除插件后原 renderer 自动恢复:

npx --yes @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web remove dsh-tavern-renderer

固定渲染顺序

regex -> macros -> expressions -> documents -> markdown -> sanitize -> template

每一环都可关闭,也可在指定环节前后注册扩展。插件只改变 DOM 展示,不修改持久会话事件,也不会改变送给模型的 prompt。

正则替换

紧凑语法是 /pattern/replacement/flags

DeepSeekTavernRenderer.configure({
  regexRules: [
    '/([A-Z]+)-(\\d+)/编号 $2,角色 {{char}},类型 $1/g',
  ],
})

也可使用接近 SillyTavern 存储结构的对象:

DeepSeekTavernRenderer.configure({
  regexRules: [{
    name: 'dialogue',
    findRegex: '/“([^”]+)”/g',
    replaceString: '<span class="dialogue">“$1”</span>',
    roles: ['assistant'],
    trimStrings: [],
  }],
})

支持 $1..$99$<name>$&$${{match}}。捕获组插值后立即求值替换串中的宏;规则按数组顺序串行执行。

宏和变量

{{char}}  {{user}}  {{role}}
{{persona}}  {{description}}  {{scenario}}
{{time}}  {{date}}  {{weekday}}  {{isotime}}  {{isodate}}
{{newline}}
{{setvar::mood::calm}}
{{getvar::mood}}
{{addvar::score::2}}
{{incvar::turn}}

变量按 Harness Session 隔离。setvar/addvar 不输出文本,incvar 输出更新后的数值。宏支持内向外嵌套,未知宏保持原样。

注册扩展宏:

const unregister = DeepSeekTavernRenderer.registerMacro('upper', ({ args }) =>
  args.join('::').toUpperCase(),
)

安全内联表达式

SillyTavern 当前消息渲染源码没有任意内联 JavaScript 宏。本插件明确增加以下 Harness 扩展语法:

{{js::1 + 2 * 3}}
{{js::user + " -> " + char}}
{{js::score >= 10 ? "win" : "continue"}}

这不是任意 JavaScript。只允许字面量、扁平变量、括号、一元运算、算术、比较、相等、逻辑/空值合并和三元运算。属性读取、索引、赋值、函数调用、new、模板字符串、浏览器全局、原型和构造器全部拒绝;实现中不调用 evalFunction

Markdown 和 HTML

支持:

  • 斜体、粗体、删除线、下划线、行内代码;
  • 围栏代码块、标题、分隔线;
  • 有序/无序列表、引用;
  • 链接、远程图片、表格和简单换行;
  • 原始 HTML(进入 DOM 前统一清洗)。

HTML 清洗采用 DOM 树和显式标签/属性白名单。script、iframe、表单、SVG/MathML、事件属性、内联 style、危险协议等均被移除。链接只允许绝对 HTTP(S)/mailto,并自动加 noopener noreferrer;图片只允许绝对 HTTP(S)。

消息可包含 <style>,但 CSS 会移除 at-rule、远程资源、动态表达式和高风险属性,并把选择器限定在 .dsh-tr-message-content 下。消息类名统一改写为 custom-*

<style>.status { color: #167565; font-weight: 600 }</style>
<span class="status">ready</span>

沉浸式文档模板

模型只需输出一个短围栏,不需要手写复杂 HTML。首行选择模板,--- 之前是 key: value 元数据,之后照常写 Markdown;结尾使用单独一行 :::

:::letter
from: {{char}}
to: {{user}}
date: {{date}}
place: North Observatory
subject: The signal after midnight
seal: crimson
---
Dear **{{user}}**,

The receiver woke at 23:40.
:::

内置模板及推荐字段:

类型用途推荐字段
letter信件、情书、正式函件from, to, date, place, subject, seal
exam试卷、测验、审讯问卷title, course, student, date, duration, total, instructions
postcard明信片、旅行短笺from, to, location, date, stamp
cipher密信、密码电文、机密备忘录classification, code, from, to, date, key, status
telegram电报、紧急短讯priority, from, to, station, date, time, operator, charge
newspaper报纸剪报、新闻报道name, date, edition, section, headline, deck, byline
dossier人物档案、案件卷宗agency, file, subject, classification, status, date, handler
diary日记、航海日志、私人记录owner, date, time, location, mood, weather, title

每种模板都有独立的语义结构和视觉语言,而不是只替换颜色:例如信件带折痕与火漆,试卷带考生栏和评分框,明信片带邮票/邮戳/地址线,密信带分级条和删节线。所有元数据都先按文本转义,正文按 Markdown 渲染后进入统一 HTML 白名单;字段值不能生成类名、样式或属性。

完整样例见 demo/documents-message.txt。文档围栏不支持嵌套;未知类型或最终仍未闭合的围栏按普通文本显示。生成期间,已知但未闭合的围栏会以 Draft 状态增量渲染,闭合后自动转为最终文档。

程序化调用可从 core 导入:

import { DOCUMENT_TEMPLATE_IDS, renderDocument } from 'dsh-tavern-renderer/core'

const html = renderDocument('postcard', {
  from: 'Luna', to: 'Alex', location: 'Selene Harbor', stamp: 'airmail',
}, 'Wish you were **here**.')

renderDocument() 返回尚未经过最终 DOM 清洗的模板 HTML,只有受信任的插件代码应直接调用;普通消息应始终使用完整 renderer。

主题和自定义 CSS

DeepSeekTavernRenderer.setTheme('harness')
DeepSeekTavernRenderer.setTheme('tavern-dark')
DeepSeekTavernRenderer.setTheme('paper')

DeepSeekTavernRenderer.setCustomCss(`
  .dsh-tr-message { line-height: 1.85 }
  .custom-dialogue { color: #a13f4b }
`)

主题和自定义 CSS 保存在浏览器 localStorage。自定义 CSS 同样会清洗,并限定到 [data-dsh-tavern-renderer];不能隐藏或改写整个 Harness 页面。

角色模板与上下文

默认角色模板分别使用 asidesectionarticle 包装 system/user/assistant。可用替换点:{{content}}{{role}}{{char}}{{user}}

DeepSeekTavernRenderer.configure({
  char: 'Luna',
  user: 'Alex',
  templates: {
    assistant: '<article class="dsh-tr-message">{{content}}</article>',
  },
})

content 已清洗,身份变量按文本转义,模板包装完成后还会再次清洗。Harness 当前 chat-node contract 没有角色卡字段,因此默认值是 Assistant/User,可用上述配置注入实际名称。

管线扩展和开关

DeepSeekTavernRenderer.setStageEnabled('expressions', false)

const removeStage = DeepSeekTavernRenderer.registerStage({
  name: 'append-marker',
  after: 'macros',
  run: value => `${value}\n\n---`,
})

内置阶段名:regexmacrosexpressionsdocumentsmarkdownsanitizetemplate。扩展阶段在浏览器本地以受信任代码运行;不要注册来源不明的回调。

最小自测

npm test
npm run build
npm pack --dry-run

测试文件为九项原始验收能力和八种文档模板各提供至少一条“输入 -> 期望输出”样例,并额外覆盖 slot 优先级、编码后的危险协议、SVG/iframe/form、CSS 远程加载、恶意角色模板、文档字段注入与流式草稿。

浏览器 API

插件加载后可在页面控制台读取 window.DeepSeekTavernRenderer。常用方法:

方法用途
render(text, context)离线运行完整管线并返回 { html, trace, diagnostics }
configure(partial)更新身份、正则、模板和阶段配置
getConfig()返回可序列化配置副本
registerMacro() / unregisterMacro()扩展宏注册表
registerStage() / setStageEnabled()扩展或切换管线阶段
setTheme() / setCustomCss()切换主题和注入安全作用域 CSS

公开版本的浏览器 API version 为 0.2.1;每条渲染内容带 data-render-trace 诊断属性。npm run check 包含 38 项单测和 2 项真实宿主 loader/slot 契约测试。宿主位于 .host/node_modules,也可通过 DSH_HOST_NODE_MODULES 指定。

DSH 兼容边界(2026-09-07)

  • Host 空 apply()dsh.bundle.patchdsh.client.injectwindow.__ModuleLoader__.load 继续使用;客户端依赖显式包含 ui-chat
  • 此插件没有 ApiProxy、后端 Remote 服务或自定义 settings card;主题/宏配置仍由浏览器 API 和 localStorage 管理,无 settings card 迁移项。
  • 只消费 assistant-step / user / steering 的展示节点,不读写 Session 日志,也不参与 RP settlement。流式 running 与最终 settled / interrupted 语义保持。
  • Session v2、持久化 assistant settlement 和异步 agentLoop.create() 属于 0.1.3-alpha.1,未纳入本次验证;peer 范围限制在 0.1.2 系列,升级需重新检查展示契约。
  • npm run check 覆盖核心渲染、slot 优先级/语言域和宿主图片入口;不等同于已做真实浏览器端登录和 UI 验收。