- Startseite
- Plugins
- UI-Erweiterungen
- dsh-boot-animation
dsh-boot-animation
mu915275-cmd/dsh-boot-animation
Plays an intro full-frame on a new conversation, or every time you open the one you pinned. Four clips ship inside the plugin itself, embedded in code with no separate video files, and an in-app library switches between them, previews the choice, and accepts your own clip. The picker has two independent sections, so the intro animation and the workspace wallpaper are picked separately; the wallpaper can be a still or a looping tail of the last N seconds, with independent transparency for the picture and the sidebar plus three blur levels. The sidebar and the workspace also get a text colour each, for when the picture is at full strength and the theme own text no longer reads.
Installation
dsh plugin --profile web add github:mu915275-cmd/dsh-boot-animationREADME
dsh-boot-animation
给 DSH 加一段开机动画:打开一个新对话、或打开你指定的那个会话时,视频铺满整个窗口播放; 选中那段片子的画面还能当工作区皮肤。
English: README.en.md
本仓库是 NativeDog1/dsh-boot-animation 的分支(上游 BSD-3-Clause,
LICENSE原样保留)。上游的能力全部保留,本分支新增: 工作区皮肤 —— 把选中那段片子的画面铺在工作区画布后面,静帧或循环片尾皆可, 皮肤与侧边栏各有独立透明度,模糊三档。改动清单见仓库根的workspace-skin.patch(可git apply -p1到上游 v0.2.2)或 git 历史。
- 一个会话只播一次(新对话默认行为)
- 指定会话每次打开都播 —— 在侧边栏页脚点一下图钉即可
- 铺满窗口、可跳过、放完自动关闭
- 可以换自己的片子(三种方式,见下)
- 尾帧/片尾当工作区皮肤,可选静帧或动态(默认只循环最后 3 秒)
安装
dsh plugin --profile web add github:mu915275-cmd/dsh-boot-animation
仓库里已经提交了构建产物
lib/,也没有prepare生命周期脚本, 所以这条命令不编译任何东西,不会触发 pnpm 的allowBuilds构建授权。 (等包发布到 npm 之后,也可以写成dsh plugin --profile web add dsh-boot-animation。)
装完必须重启一次 DSH 服务才生效(bundle 层是在启动时装配的):
# 停掉当前的 dsh web,然后
dsh web
装完看不到效果?先做这件事
DSH 的客户端 bundle 响应带 cache-control: max-age=31536000, immutable,
而 URL 上的 rev 是进程 nonce、不会随内容变化。所以浏览器会一直用第一次抓到的副本。
装好或升级后请在新窗口里按 Ctrl+Shift+R(硬刷新)。普通 F5 不够。
用法
新对话自动播
打开一个还没说过话的新对话时会自动播一次。
让某个会话每次打开都播(推荐)
- 打开那个会话
- 点侧边栏最下面的 🎞 图标(就在「设置」旁边)
- 图标变绿 🎬 = 已钉住
之后每次进入这个会话都会播一遍 —— 切走再切回来、刷新页面,都会重播。
再点一下图标取消。
注意:如果刚启动时你的活动主面板不是「对话」(比如停在某个插件的面板上), 当前会话还不存在,图钉是禁用状态。先打开一个对话即可。
入场动画和工作区壁纸,各选各的
面板上半部分是两个并列的分区,互不影响:
| 分区 | 它决定什么 |
|---|---|
| 入场动画 | 打开新对话、以及打开你钉住的会话时,铺满窗口播放哪一段 |
| 工作区壁纸 | 铺在工作区画布后面当皮肤的是哪一段 |
每个分区各有一个 ✓,点哪一区的行就换哪一区。默认两者是同一段(壁纸跟着入场动画走); 只在你单独点过壁纸之后,它才固定下来 —— 这时换入场动画不会连带换掉壁纸。
把选中的那一段当工作区壁纸
片库面板里有 工作区皮肤 一行,它用的是工作区壁纸那个分区选中的片子:
- 点 用尾帧 开启
- 在 工作区壁纸 分区里点另一段 —— 皮肤跟着换成那一段的尾帧
- 清晰 / 柔和 / 沉浸 切换模糊程度(清晰=原图,沉浸=重模糊)
- 透明度 滑块调节皮肤浓淡;整个窗口 / 仅对话区 切换覆盖范围
皮肤取的是最后一帧,而且是在浏览器里现取的(离屏 video 跳到末尾,画进 canvas),
所以你自己丢进 videos/ 的片子同样有效,插件不需要 ffmpeg。取到的帧是一张 JPEG,
存在浏览器里,因此之后每次打开都是瞬间出现 —— 也不依赖那个视频文件还在不在。
片尾淡出成黑很常见,所以插件会从末尾往前退着找第一帧有画面(亮度 + 方差判定), 最多退 2.2 秒;面板底部那行会写明这一帧取自末尾前多少秒。
透明度与侧边栏
皮肤是两层:最底下是当前主题的底色,尾帧叠在它上面,透明度控制的就是上面这层:
| 透明度 | 效果 |
|---|---|
| 0% | 尾帧原样呈现 —— 皮肤最清楚(文字也最直接压在图上) |
| 30%(默认) | 掺入三成主题底色,皮肤收敛一些,文字更稳 |
| 100% | 尾帧完全透明 —— 等于没有皮肤,就是原本的实底界面 |
因为底下垫着的是主题自己的底色,所以透明度拉到底也不会露出浏览器的白画布。
早先的版本把「压暗」焊死在观感预设里(暗色下
brightness(0.5)再加 30% 黑纱, 等于只让 34% 的亮度到达屏幕),结果 0% 也只是一团暗影,被反馈为「还是太深、看不清」。 现在那层纱很薄(暗色下brightness(0.92)+ 10% 黑纱,约 80% 到达屏幕),可读性交给 透明度滑块去调:嫌文字压图就往上拉,想看清楚的皮肤就往下拉。这层纱不随观感档变化:三档只差模糊。曾经给「清晰」配了更厚的纱(小字压在锐利 细节上确实更难认),结果
清晰反而成了三档里最暗的 —— 标签写的是模糊程度,亮度 却不一致,会被当成 bug。
侧边栏跟着一起变:它用的 --dsw-specific-sidebar-fill 和皮肤层读的是同一个
--dba-skin-strength,所以拖动滑块时侧边栏的透出程度同步变化 —— 透明度 0% 时侧边栏
最透(露出 38% 的皮肤),透明度越高侧边栏越接近它原来的实底。范围选 仅对话区 时
侧边栏不动。
| 做什么 | 结果 |
|---|---|
| 换片库里的另一段 | 皮肤跟着换(面板底部显示新片名) |
| 关掉皮肤 | 立刻恢复原来的实底背景 |
| 切换亮/暗主题 | 皮肤自动改明暗方向:暗色下压暗,亮色下提亮 |
| 皮肤太抢眼 | 拉高透明度,或选 沉浸(重模糊),或把范围切成 仅对话区 |
皮肤只改两个主题 token(--dsw-alias-bg-base 透明、--dsw-specific-sidebar-fill 半透明),
卡片、菜单、对话框仍然是实底,所以文字不会直接坐在图上。
字体颜色:皮肤压住文字时(工作区 / 侧边栏各一个)
透明度过低、或片子本身偏暗时,主题自带的字会看不清 —— 亮色主题下尤其明显:
近黑的字(#0f1115)直接压在一张深色画面上,几乎读不出来。所以面板里多了一行:
字体颜色: 工作区 [■] 侧边栏 [■] 默认
- 两个色块各自独立:改工作区不动侧边栏,反之亦然(各存在
skin-text-workspace/skin-text-sidebar) - 点一下色块就是系统的取色器;未设置时色块显示的是那个区域当前的字色,所以是从现状开始调
- 默认 把两个区域都还原成主题自带的字色
- 工作区那一份作用于对话区 + 右侧栏(右侧栏只在「整个窗口」范围下才压在图上,所以「仅对话区」时它会自动还原)
- 侧边栏那一份连它的图标和
deepseek HARNESS文字一起改(--dsw-alias-menu-icon/--dsw-alias-brand-text) - 只在皮肤开启时生效,和皮肤一起开关
被改的是主题的正文色族:--dsw-alias-label-primary / -primary-dimmed / -primary-bluish /
-secondary / -tertiary / -caption(主题里并没有 --dsw-alias-text-*,那是常见误记)。
故意不动 -primary-inverted 与 -primary-foreground —— 它们表示「浅色块上的字」「彩色按钮上的字」,
一旦改掉,按钮标签就会消失。
底板会跟着字色翻转,否则就是白底白字。当你的字色与当前主题相反时,该区域里的 中性底色一起翻过来:选浅色字 → 卡片 / 气泡(对话里的文件名就在这)/ 输入框 / 侧边栏按钮变深; 选深色字 → 它们变浅。字色与主题同向时(暗色主题 + 浅色字)什么都不动,所以默认观感与旧版完全一致。
被翻转的是这些中性 token(括号里是实测它画在哪):
| 一族 | 画在哪 |
|---|---|
--dsw-specific-input-major / -minor | 输入框卡片 |
--dsw-specific-bubble | 消息气泡(对话里的文件名就在这) |
--dsw-specific-selector | chip、加号按钮 |
--dsw-alias-button-elevated-fill | 侧边栏「新会话」 |
--dsw-alias-markdown-inline-code | 行内代码小框(消息里那一个个 代码) |
--dsw-alias-markdown-code-block / -banner | 代码块与它的标题条 |
--dsw-alias-markdown-tag / -placeholder / -citation / -code-segment-* | 其它 markdown 底 |
--dsw-alias-settings-card-fill、-button-floating-fill、-bg-layer-1/2/3、-interactive-bg-hover-solid | 各种卡片 / 浮层 / 悬停态 |
--dsw-specific-menu / --dsw-menu-surface-fill | 菜单 |
--deliverable-fill + [class$="_file"]、[class$="_deliverable"] | 文件卡片(见下) |
文件卡片是个特例:它的底色是按主题写死的(在它和它四层祖先上把 433 个自定义属性逐个设成标记色,没有一个能让它动, 可它确实是亮主题
#fafafa、暗主题#212123),没有 token 可挂,所以只能按类名结尾匹配 ([class$="_file"],hash 前缀会随构建变,结尾不会)。同理,对话里的分隔线是拿--dsw-alias-label-caption(一个文字色)当线色画的,我改文字时会把它一起改成白色,所以单独把它压回光晕色。
三种东西故意不碰 —— 它们是成对的语义色,改了反而坏掉:
| 不碰 | 为什么 |
|---|---|
label-primary-inverted / label-primary-foreground | "浅色块上的字""彩色按钮上的字":改了就变成按钮标签消失 |
label-primary-bluish | "蓝底胶囊上的字",比如标题旁那个「预览版」:它的淡蓝底属于信息色,不跟着翻 |
| 彩色填充本身(品牌色 / 信息色按钮,例如发送键) | 它们的标签用的是上面那两个 token |
直接压在画面上的文字:加一层反色光晕
会话里大部分文字其实没有卡片(消息正文、路径、时间、标签都直接躺在画面上)。这类文字靠换颜色
永远解决不了:亮画面吃浅字、暗画面吃深字。所以设了字色之后,该区域的文字会带一层
与字色相反的光晕(浅字配深光晕、深字配浅光晕,text-shadow),既不挡画面又保证读得出。
唯一还需要你自己权衡的是直接压在画面上的文字(最明显的是会话标题「探索未至之境」)—— 它底下没有卡片,只有你的片子。这类文字读不读得出取决于你的片子:亮底配深字、暗底配浅字, 或者把透明度往上拉一点让片子淡一些。
换自己的视频(片库)
插件现在是一个片库,不是单个槽位:它会把所有能找到的视频都列出来,你选一个,选择会被记住。
插件自带四段片头(内嵌在代码里)
装完不用加任何东西,片库里就已经有四段可选:
| 片库里的名字 | 来源 | 大小 |
|---|---|---|
DeepSeek 品牌片头 | 内嵌 lib/clips.data.js | 1.2 MB |
DeepSeek 赛博朋克片头 | 内嵌 lib/clips.data.js | 1.8 MB |
DeepSeek 数字角色苏醒 | 内嵌 lib/clips.data.js | 2.5 MB |
DeepSeek 启动问题 | 内嵌 lib/clips.data.js | 3.2 MB |
这些片段没有落盘的 mp4 文件 —— 它们以 base64 存在 lib/clips.data.js 里,host 在
第一次被请求时才 import(约 11.5 MB 的模块,如果在启动时解析,每次开 DSH 都要白付这个代价)。
这样做的意义是:不会再有 files 字段漏写、安装副本过期、或者随包发出一个没做 faststart
的容器这些事。media/*.mp4 只是 npm run embed-clips 的输入,不随包发布。
四段都是 faststart 过的(moov 在文件头),可以边下边播;生成脚本会拒绝任何
moov 不在前面的输入。这很重要:索引表在文件末尾的 mp4 要整段下载完才出画面,
叠加客户端 25 秒看门狗,表现就是「片头全黑」。
体积:npm 包 9.1 MB(tarball)/ 解包 12.2 MB。四段都做过 CRF 20 重编码 + faststart
重排,相比各自的原片省下 36%–71%(合计 20.4 MB → 8.7 MB),画质指标 SSIM 0.988–0.996、
PSNR 44–48 dB —— 这是「肉眼看不出差别」的区间。原片另存于仓库外 ~/dsh-dev/_clip-masters/。
想换成自己的片子:把 mp4 放进 media/,改 scripts/embed-clips.mjs 里的清单,
跑 npm run embed-clips。(临时试片不用这么麻烦 —— 见下面的「最省事的方式」。)
最省事的方式(推荐)
- 把 mp4 丢进
~/.dsh/boot-animation/videos/ - 在侧边栏页脚点 🎛(在 🎞 图钉旁边)打开「片头片库」
- 点一下你想播的那一条
选中的那段会在下一次播放片头时登场:新对话、以及你钉住的会话。
Windows 上就是
C:\Users\<你>\.dsh\boot-animation\videos\具体路径以片库面板底部显示的那一行为准。你不需要为了"干净"删任何东西:同一个视频存在多份副本时,片库只列一条 (按 大小+mtime 判定同一份内容),并标注「合并 N 份重复」。你的文件一直留在 原处,只是不重复显示。这解决的是历史上一个真实的困惑:用户自己也放了一份
intro.mp4,于是同一段片子在面板上出现两次、挂两个不同徽章,看起来像 "插件里没有这一段"。
片库面板
| 元素 | 作用 |
|---|---|
| 入场动画 / 工作区壁纸 两个分区 | 各自一个 ✓,点哪一区的行就换哪一区,互不影响 |
| 来源徽章 | 你自己加的 / 插件自带 / 内置原始 / 环境变量 |
| 文件大小 | 帮你确认换对了没有 |
| ▶ 预览入场动画 | 立刻播放当前选中的入场动画,不用等下一次触发(见下) |
| 刷新 | 刚往文件夹里丢完文件,点它重新扫描 |
原片源 徽章 | 历史上那个 intro.mp4 落点,仍然优先 |
| ⚠ 未优化 徽章 | 该文件的索引表 moov 在末尾,建议重排(见下) |
铺满屏幕 / 完整显示 | 播放时怎么贴合窗口,见下 |
换了片子却"没反应"?先点「预览入场动画」
片头动画的触发故意很窄:
- 新对话:只播一次(记住播过的会话,不会重复打扰)
- 钉住的会话:每次打开都播
所以在同一个新对话里刷新页面,片子本来就不会重播 —— 这很容易被误认为 "我换了片子但另一个视频不出现"。点片库里的 ▶ 预览入场动画 可以立刻播放选中的那段, 确认换对了没有。
播放时怎么贴合窗口(黑边问题)
覆盖层铺满整个窗口,但窗口的长宽比几乎不会是视频的长宽比——浏览器有标题栏和 工具栏,可视区通常比 16:9 更宽。这时:
| 模式 | CSS | 效果 |
|---|---|---|
| 铺满屏幕(默认) | object-fit: cover | 填满窗口,没有黑边,超出部分被裁掉 |
| 完整显示 | object-fit: contain | 整帧都在,长宽比不匹配时留黑边 |
在片库面板里切换,下次播放生效。选「完整显示」的情况:片子里有贴着边缘的字幕、 logo 或水印,不想被裁掉。
如果黑边来自视频本身烧进去的边框(导出时带上的),改 CSS 没用,要用 ffmpeg 裁掉:
ffmpeg -i in.mp4 -vf "crop=W:H:X:Y" -c:a copy out.mp4。 判断方法:ffmpeg -v info -i in.mp4 -vf cropdetect=24:16:0 -f null -, 若crop=值全程稳定,就是烧进去的;若随画面变化,那只是深色背景,别裁。
支持的格式
.mp4 .m4v .webm .mov .mkv —— 但能不能播取决于浏览器解码。
H.264 + AAC 的 mp4 最稳;HEVC(H.265)、ProRes、部分 mkv 大概率只有声或黑屏。
手动方式(老办法,仍然有效)
host 半侧按这个顺序解析,每次请求都重新解析(换片子不用重启):
| 顺序 | 位置 |
|---|---|
| 1 | ~/.dsh/boot-animation/selection.json 里选中的那个 id(片库面板写的) |
| 2 | 环境变量 DSH_BOOT_ANIMATION 指向的文件 |
| 3 | ~/.dsh/boot-animation/intro.mp4(历史落点,仍优先于片库里的其他文件) |
| 4 | ~/.dsh/boot-animation/videos/ 里最新修改的那个 |
| 5 | 内嵌的四段(按 scripts/embed-clips.mjs 里的顺序,品牌片头优先)—— 永远兜得住,因为它在代码里 |
所以最保险的手动换法依然是:
mkdir -p ~/.dsh/boot-animation
cp 我的片子.mp4 ~/.dsh/boot-animation/intro.mp4
想确认当前用的是哪一个,直接访问状态端点:
curl http://127.0.0.1:3080/dsh-boot-animation/status.json
curl http://127.0.0.1:3080/dsh-boot-animation/videos.json
排错:视频是黑的 / 放着放着没了
多半是容器没做 faststart。 如果 mp4 的索引表 moov 在文件末尾,浏览器必须
整段下完才能解码,中间一直黑屏;而客户端有 25 秒看门狗(STALL_TIMEOUT_MS),
超时就自己把覆盖层关掉 —— 症状就是「点开什么都没有」。
用 ffmpeg 重排一下容器(无损,不重新编码):
ffmpeg -i 原片.mp4 -c copy -movflags +faststart 修好的.mp4
验证 moov 是否前置:
ffprobe -v trace 修好的.mp4 2>&1 | grep -m1 moov # 偏移应该很小
浏览器的两条硬性策略
自动播放带声音、以及 Fullscreen API,都要求用户手势,任何网页都绕不过。所以:
- 动画以静音在铺满窗口的覆盖层里自动开始(视觉上已经是全屏)
- 点一下画面:同时开启声音并进入真全屏
- 万一连静音自动播放也被拒,会显示「点击播放」而不是黑屏
排错
| 现象 | 原因 / 处理 |
|---|---|
| 完全没出现 | 十有八九是缓存:Ctrl+Shift+R。或重启一次 DSH 服务 |
| 新对话不播 | 这个会话已经播过了(每个会话只播一次)。钉住它可变成每次都播 |
| 钉住了也不播 | 确认图钉是绿色;确认打开的就是被钉的那个会话 |
| 黑屏无画面 | 先看 moov 是否前置(见上「排错:视频是黑的」);再访问 /dsh-boot-animation/status.json 看片源;最后看浏览器控制台有没有解码错误 |
| 换了片没生效 | 片库里点完要有 ✓ 才生效;确认文件在 videos/ 里并点了「刷新」 |
| 播到一半自己没了 | 25 秒看门狗(STALL_TIMEOUT_MS)超时 —— 通常还是 faststart 或解码太慢 |
| 想看到插件在干什么 | 把 src/client/index.ts 顶部的 DEBUG 改成 true 重新构建,控制台会打印每次决策 |
实现速记(给维护者)
- 挂载点:
shell.overlay(帧级浮动层,kind: list,新增一格不顶替官方 UI)+sidebar.footer.action(页脚那个图钉和 🎛 片库入口) - 当前会话来自
ctx.uiSession.adapter.current这个 React 友好的 store。 它的快照不是会话记录,而是解析后的描述符产物{ key, hooks, keyedHooks, props }—— 会话 id 在props.sessionId,会话快照在hooks.session - 「这是个全新对话」的字段是
blankBit(hooks.session.blankBit)。session.blank属于别的包的投影对象,不在这个快照上 - 「每次点开都播」实现为监听进入会话这个动作,而不是记"播过没有", 所以被钉的会话不受"已看过"记录限制
- 视频路由支持 Range(浏览器对媒体会发 Range;该给 206 却给 200 时有些播放器会拒绝播放)
- 媒体响应是
no-cache+ ETag,不是no-store:no-store让浏览器一个字节都不能留, 于是每次开片头都要重下整段,加载期间就是黑屏。no-cache表示"留着但要先问", 配合 ETag:没换片子 → 304 直接用本地副本(秒开);换了片子 → ETag 不同 → 重新下发。 这条的正确性由verify-routes.mjs断言(含"带旧 ETag 请求新片子必须 200"的反例) - hook 只能在组件里调:
apply()是插件加载器调的,不是 React 调的,所以状态 全部住在AppRoot组件内。片库入口在图钉那个 slot、对话框在 overlay 那个 slot, 是两个独立的 React 根,用模块级libraryOpeners订阅集合桥接 - 片库的路由:
videos.json(列)、media/<id>(按 id 流)、select(POST 写选择)、boot.mp4(老路由,服务当前生效的那条,向后兼容) - 内嵌片段的 id 是
builtin:<name>,与路径派生的 id 不会撞;它们的 ETag 用自身的 内容哈希("embedded-<sha256前16位>"),所以重校验是精确的、不依赖 stat - 同一个视频在多个位置时按内容去重(sha256;文件侧按 size+mtime 缓存哈希结果), 内嵌那份优先胜出 —— 它不可能被删掉,所以指向它的选择永远解析得到
- prefix 路由不能带尾部斜杠:webserver 用
pathname !== prefix && !pathname.startsWith(prefix + '/')匹配,注册.../media/会被当成.../media//,永远匹配不上(曾导致 /media/ 全 404) - 工作区皮肤只靠一个 token:
--dsw-alias-bg-base同时是 AppFrame(ui-layout)和 对话列(ui-conversation)的底色,把它改成transparent,下面的皮肤层就透出来了; 卡片和菜单走--dsw-alias-bg-layer-1/2,文字仍然坐在实底上。侧边栏列走--dsw-specific-sidebar-fill,只在「整个窗口」范围下被改成 color-mix 半透明 - 皮肤层是
position:fixed; z-index:-1:负 z 只能被盖住、不可能盖住 UI;正 z 要靠猜 paint order,猜错就是把界面遮住。配套把html/body背景也压成透明,否则负 z 层会被它们挡住 - 皮肤是「底色 + 帧」两层,不是一层图:
.dba-skin铺主题底色,.dba-skin-shot叠尾帧,opacity由--dba-skin-strength给。这样透明度 0/100 的语义才是「最浓 / 等于没有皮肤」, 否则淡出会透到浏览器自己的画布色上。侧边栏的--dsw-specific-sidebar-fill用 同一个变量 做 color-mix(满强度只吃到 38% 透明),所以它天然跟着滑块走 - 暗色/亮色方向相反:亮色主题下必须把帧提亮(否则深色文字坐在深色图上),所以
brightness与 scrim 由 JS 按body[data-ds-dark-theme]现算,并用 MutationObserver 跟着主题切 - 尾帧是浏览器里现取的(离屏 video 跳到末尾 → canvas → JPEG → localStorage), 所以没有 ffmpeg 依赖,用户自己的片子也一样能用;采样亮度/方差是为了识别「片尾淡出成黑」并往前退帧
- 面板里的
--dsw-alias-text-*/--dsw-alias-bg-elevated在主题里并不存在(主题用的是--dsw-alias-label-*/--dsw-alias-bg-layer-*),原来一直落到硬编码#fff,暗色下就是白底。 现在写成嵌套 fallback:var(--dsw-alias-text-primary, var(--dsw-alias-label-primary, #191919))
验证脚本(改完跑一遍)
| 命令 | 作用 |
|---|---|
npm run verify:routes | 用服务器自己的匹配规则驱动真实 handler,断言每条路由 |
npm run verify:letterbox | 用 CDP 驱动本机 Edge,量出所选贴合方式实际留多少黑边 |
npm run check | 上面两个 + CSS 模板反引号检查 |
npm run build:client | 先跑 CSS 检查再构建(防带病构建) |
两个脚本都是被真实 bug 逼出来的,各自都有过一次"用自己的规则测自己"的教训: 它们的断言刻意复刻被测方的规则,并且在提交前会做反向验证(故意改坏 → 必须报错)。
客户端构建有一个坑:整个 CSS 是一段模板字符串,注释里写一个反引号就会提前把它 结束掉,而报错是 TypeScript 的 parse error 指向某行 CSS,同时
lib/client.js保持不变 —— 看起来像改成功了其实没生效。scripts/check-css-template.mjs专门 拦这个,已接进build:client。
许可
BSD-3-Clause,见 LICENSE。包内的 assets/boot.mp4 与 videos/ 下的
默认片源以相同条款分发。
Ähnliche Plugins
dsh-web (dsh-task-board)
zhu1090093659/dsh-web
dsh-web (dsh-web-all)
zhu1090093659/dsh-web
dsh-web-ui (dsh-task-board)
zhu1090093659/dsh-web-ui
dsh-web-ui (dsh-web-ui-all)
zhu1090093659/dsh-web-ui