界面包
界面包可以改变故事的外观,并加入你自己的小块界面:状态条、按钮、开场卡片、背景、带地图或笔记本的标签页。世界可以自带界面包,你也可以保存自己的包,用在任何故事里。
界面包是一个放纯文件的文件夹:一份清单、一个可选的样式表,以及带少量 JavaScript 的 HTML 页面。不需要安装,也不需要构建。
界面包不会在 DSH 页面中运行代码。它的脚本运行在不透明来源的沙盒框架里,无法访问页面、你的登录状态或浏览器存储;fetch、图片等普通资源加载只能读取包自带的文件。框架只能通过一组固定的小型接口与页面交流,并且只会被告知玩家角色本来就能知道的内容。界面包仍然是受信任的代码:它可以让自己的框架跳转到外部地址,也可以把收到的数据放进该地址。完整说明见界面包格式参考。
界面包能做什么
| 部分 | 是什么 | 是否需要 JavaScript |
|---|---|---|
| 主题 | 替换 DSH 的部分颜色,浅色和深色各一套。 | 否 |
| 皮肤 | 一个样式表,改变故事自身文字的样式:叙述、台词、建议行动。 | 否 |
状态条(hud) | 输入框上方的一条,显示地点、时间、状态。 | 是 |
操作条(dock) | 紧靠输入框上方的一条,适合放按钮。 | 是 |
开场卡片(opening) | 在第一个回合播放之前显示的卡片。 | 是 |
背景(backdrop) | 文字后面的一层。 | 是 |
标签页(tab) | 聊天旁的一个页面:地图、笔记本、物品栏。 | 是 |
侧栏页面(sidebar) | 右侧栏中任意类型的页面,从侧栏的引导页进入:实时地图、状态面板、某样东西的渲染。 | 是 |
一个包可以包含其中任意几项,多数只用一两项。
右侧栏中的页面
DSH 的右侧栏以一个引导页开始,里面为它能显示的每样东西列一个入口:工作区文件、终端。如果故事的界面包带有 sidebar 页面,这里会多出一个入口,名称取自页面的 title。选择它,就会在侧栏中打开界面包的页面,上方是我们的一条细栏,带 返回 按钮,可回到引导页。细栏以下全部归界面包所有:页面显示什么、怎么显示,由作者决定。
"surfaces": { "sidebar": { "file": "map.html", "title": "地图" } }
页面与其他页面一样是沙盒页面,得到同样的 rp 对象,所以可以用 rp.watch 实时跟随故事。在侧栏的其他标签页显示时,它仍保持运行,所以地图或动画不会每次都从头开始。其他页面可以用 rp.open('sidebar') 把它调出来(需要在清单的 actions 中列出 open):示例的状态条里就有一个这样做的“地图”按钮。
在框架 内部,页面可以使用 canvas、WebGL、音频、worker、WebAssembly,以及对包自己文件的 fetch,例如它自带的数据文件或库。它无法读取 DSH 页面、你的登录状态或浏览器存储,普通的外部 fetch、图片和 WebSocket 也会被阻止。自行跳转是例外,因此只应使用你信任的包;见框架。
试试示例
初始世界《黑水观测站》自带一个名为“野外笔记”的界面包。添加这个世界并开始一个故事,你会看到一个显示地点、时间和在场者的状态条,还有一个 界面 标签页,里面有你已知的地点、在场的人、随身物品、建议行动、故事回顾,以及一个随故事保存的笔记框。它的文件在世界的 ui/notebook/ 中,可以复制这个文件夹作为起点。

插件自带的 Scene 界面包
插件自带一个小界面包 Scene,适用于任何世界:一个显示地点、时间和在场人物的状态条,以及右侧栏里你所知地点的实时地图。DSH 启动时会把它放进你的角色扮演文件夹的 ui 文件夹(你改过的副本不会被覆盖;删掉的会回来)。要在某个故事里使用,可以在故事的对话设置里选择 Scene,或在开始故事时把界面包写成 scene。它会跟随界面语言(英文或中文),你自己的界面包也可以通过 rp.theme() 读到这个语言。
写的时候就能看到效果
在世界编辑器的界面部分,可以看到这个世界携带的界面包:每个包要求什么、包含什么、每个问题出在哪个文件,以及预览。预览一次显示一个界面位置(状态条、标签页、右侧栏页面等),放在一个框架里,可选窄、中、宽三种宽度,内容就是由这个世界开始的故事的样子:第一个可扮演的角色、他所在的位置、身边的人和他知道的地点。在界面包里点一个按钮,这一部分会告诉你它请求了什么(“以玩家身份说……”“请求打开侧栏”);不会发送到任何地方。界面包存下的东西留在内存里,重新载入后消失。皮肤和主题在故事里生效,预览里不应用;由于没有进行过任何回合,“到目前为止的故事”是空的。修改文件后点重新载入再看。同一部分也可以编辑这些文件:新建界面包会创建带清单的文件夹,新建文件添加文本文件,文件上的编辑会在文本框中打开它;保存时会像开始故事时那样检查界面包,并告诉你还有什么问题。
界面包 放在哪里,故事用哪一个
| 位置 | 用途 |
|---|---|
<世界>/ui/<包>/ | 随世界发布的包。故事开始时会检查并复制到该故事中,所以世界之后改动也不会影响已开始的故事。 |
<故事>/ui/<包>/ | 故事实际使用的副本。修改它只影响该故事。分支会保留其来源故事的界面包。 |
<角色扮演目录>/ui/<包>/ | 你自己的包。只有在故事设置中指定名称时才会使用。 |
默认情况下,故事使用它自带的第一个包。在故事的 对话设置 标签页中,界面包 部分会显示正在使用哪个包,可以从列表中选择另一个(包括 无,即显示普通聊天),并指出任何问题所在的文件和行号。
故事在开始时复制世界的界面包,之后对世界的修改不会传到故事中。当世界的包与故事的副本不同,这一部分会提供 从世界更新:添加新的包,替换已改动的包,被替换的副本保存在故事的 .trash 文件夹中。只要世界中有任何一个包存在问题,就不会做任何改动。
文件改动后会重新读取,所以你可以一边编辑故事中的副本,一边在几秒内看到页面跟随变化。有问题的包会被撤下,直到修好为止。
做一个自己的包
最小的可用包只有一份清单和一个页面。
pack.json
{
"name": "地点与时间",
"api": 1,
"surfaces": { "hud": { "file": "hud.html", "height": 40 } }
}
hud.html
<!doctype html>
<p id="line"></p>
<script>
rp.watch((view) => {
const place = view.place ? view.place.name : '无处'
const time = view.time ? view.time.text : ''
document.getElementById('line').textContent = `${place} — ${time}`
})
</script>
把两个文件放进 <世界>/ui/place-and-time/,用这个世界开始一个故事,状态条就会出现在输入框上方。rp.watch 会立即以玩家能知道的内容调用你的函数,此后每次变化再调用。
几个让界面包外观正确 的习惯:
- 使用 DSH 的颜色。 框架里的颜色以 CSS 变量提供,所以
color: var(--dsw-alias-label-primary)和background: var(--dsw-alias-bg-layer-1)会跟随浅色和深色主题。不要写死白色或黑色。 - 状态条保持简短。 状态条用
rp.resize(height)申请高度;状态条可为 24 到 320 像素,开场卡片为 120 到 640 像素。 - 只列出用到的操作。 让按钮发言或填写输入框需要在清单的
actions中列出say、fill或choose,未列出的会被拒绝。 - 用相对地址 引用自己的样式表、脚本、数据和图片。无法从互联网加载任何内容,所以需要的东西(包括库)都要随包一起提供。
- 用文本,不用故事里的标记。 显示名字和描述时用
textContent,不要用innerHTML。
改变故事文字的样式
皮肤不需要 JavaScript,就能改变故事自身文字的样式。在清单中指定它("skin": "skin.css")。故事的每个元素都带有 data-rp 属性,选择器的每一部分都必须指明其中之一:
[data-rp="utterance"] { border-left: 3px solid var(--dsw-alias-brand-primary); padding-left: 10px; }
[data-rp="utterance"] [data-rp="words"] { font-family: Georgia, serif; }
[data-rp="speaker"] { color: var(--dsw-alias-brand-primary); }
可以设置样式的元素以及皮肤必须遵守的规则见参考。有错误的皮肤会整体被拒绝,并指出行号。
让助手来写
世界创作助手可以为你编写界面包。请描述你想看到什么、放在哪里,例如“一个显示时间和在场者情绪的状态条”。它会先读一份简短的指南,再逐个文件写入,并反复检查直到没有问题。
它使用 DSH 自带的文件工具,而在这个对话中,这些工具只能触及世界界面包的普通文本文件(<世界>/ui/<包>/:html、css、js、json、txt 和 svg,每次调用最多 200 KB)。路径中的符号链接会被拒绝,因此看似位于包内的已批准路径不能把工具引到世界之外。它无法访问世界的其他任何文件,请求更宽的权限也不会改变这一点。每次改动都会先征求你的同意,DSH 会把改动显示为差异。图片和字体需要手动添加。