news 2026/9/26 5:04:56

如何注册文件预览器:DSH-better-sidebar的FileViewer API完整参考(匹配算法/取数策略/真实案例)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何注册文件预览器:DSH-better-sidebar的FileViewer API完整参考(匹配算法/取数策略/真实案例)

如何注册文件预览器:DSH-better-sidebar的FileViewer API完整参考(匹配算法/取数策略/真实案例)

【免费下载链接】DSH-better-sidebar开放的侧边栏底座,支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 | Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar

DSH-better-sidebar 是一个开放侧边栏底座,内置文件编辑/预览、终端、Git、子代理与侧边对话页面,并开放ctx.betterSidebar服务给第三方插件。本文聚焦其中最实用的FileViewer API(registerFileViewer):如何用不到 20 行代码让你的插件在侧边栏中预览.csv、.parquet等任意文件类型,并彻底讲清匹配算法(matchFileViewer)、5 种取数策略(fetchStrategy)与仓库内的真实案例。

📖 完整接入文档见 外部插件接入指南,内置预览器参考实现在 src/client/builtins/viewers.tsx。

一、FileViewer 能做什么:内置预览器全是这么注册的

在 DSH-better-sidebar 里,侧边栏能预览什么文件,完全由"注册表"决定。打开一个文件时,宿主按你注册的扩展名/内容嗅探规则找到对应的预览器组件,再按你声明的取数策略把文件内容喂给它。

关键在于:内置的 6 个预览器(image / pdf / markdown / html / code / binary-download)自己也是通过同一套 FileViewer API 注册的("吃自己的狗粮")。这意味着:

  • 你的插件预览器和内置功能完全对等:同样出现在设置页「侧边卡片」中,可被用户独立启停;
  • 想覆盖内置行为?注册同扩展名 + 更高priority即可,无需改宿主代码。

二、三步注册一个文件预览器(最小骨架)

注册只需三步:声明依赖 → 包在ctx.effect里 → 调用registerFileViewer。

// 你的插件 client 入口(完整示例见指南 §3 / §13) export const inject = ['betterSidebar'] // 保证 better-sidebar 先就绪 export function apply(ctx: Context): void { ctx.effect(() => ctx.betterSidebar.registerFileViewer({ id: 'my-plugin:csv', // 建议带包前缀,避免 id 冲突 exts: ['csv'], // 小写、不带点的扩展名 fetchStrategy: 'custom', // 取数策略,见下节 load: async (path, scope) => parseCsv(await fetchText(scope, path)), component: ({ customData, path }) => <CsvGrid data={customData} />, }) ) }

⚠️两个必踩的坑:

  1. 注册必须包在ctx.effect(...)里。注册函数返回的 disposer 会在插件卸载/HMR 时自动执行;不包 effect 的话,再次激活会直接抛"already registered";
  2. 不要 value-importdsh-better-sidebar。跨插件交互只走ctx.betterSidebar方法调用(构建纯度门会拦截),类型用import type {}导入即可。

三、FileViewerDescriptor 完整字段速查

字段作用缺省行为
id唯一标识,也是设置页开关的 key必填,重复注册会抛错
exts认领的扩展名数组(小写无点);[]= catch-all 兜底必填
priority优先级,高者优先裁决0
fetchStrategy字节获取策略(5 选 1,见下节)必填
detect内容嗅探:(path, head) => boolean,按文件头字节认领,可忽略扩展名无
loadfetchStrategy: 'custom'时的加载函数,支持AbortSignal中止无
title/icon设置清单展示名与图标(支持 i18n 函数形式)回退到id
settings声明式设置行(开关/数值/下拉/自定义面板),自动渲染到设置页无
component渲染函数,接收FileViewerProps必填

组件收到的FileViewerProps里,你真正关心的是path、scope(会话标识,调 API 必带)、以及按策略填充的content/mediaUrl/customData三选一。

四、5 种 fetchStrategy 取数策略:字节从哪来

这是新手最常问的问题——"我的组件怎么拿到文件内容?"答案就在策略选择里:

策略字节来源传给组件的字段适用场景
none不需要字节无纯 UI 自渲染
fsRead宿主/sidebar/api的fs.readcontent、truncated文本类:CSV / JSON / XML
mediaUrl/sidebar/file媒体路由 URLmediaUrl图片 / PDF(viewer 自己 fetch)
custom你写的load()函数customData远程拉取、需二次解析的格式
binary-download不预览,直接显示下载按钮无无客户端渲染器的二进制

选型建议:

  • 能当文本读的格式(CSV、JSON、日志)→fsRead,宿主帮你读,零网络代码;
  • 媒体文件(图、PDF)→mediaUrl,拿到一个同源 URL 直接塞给<img>/ PDF 组件;
  • 需要解析成结构(Parquet、YAML、自定义协议)或数据不在本地→custom+load(),插件内用fetch('/sidebar/api/fs.read')拉字节即可(响应体是{ value: ... }包装);
  • 真·二进制(exe、压缩包)→binary-download,给用户一个体面的下载按钮而不是乱码。

五、匹配算法 matchFileViewer 深度解析:谁抢到了这个文件?

当你打开一个文件时,宿主用matchFileViewer(path, head?)决定交给谁渲染。源码在 src/client/service.ts,核心规则一句话:按 priority 降序单趟遍历,每个描述符在自己的回合里先试detect、再试exts,先命中者赢。

拆开看:

  1. 单趟 + 优先级降序(稳定排序,同优先级按注册先后)。高priority的描述符先获得裁决权——它的detect或exts任一命中即赢,低优先级的规则没机会参与;
  2. detect 优先于 exts:若文件头字节(head)可用且你声明了detect,先跑嗅探;嗅探失败时,exts: []的纯嗅探型本轮直接放弃(防止"magic number 预览器吞掉所有文件");
  3. catch-all 语义:exts: []且无detect是"盲兜底",命中任何路径——内置的code预览器正是用它以-100的最低优先级接住所有漏网文件;
  4. head 字节从哪来:第一次匹配(纯扩展名)没有 head。若文件读出来是二进制,宿主fs.read响应会带head字段(base64 前 4KB),编辑器拿它对detect型预览器重匹配一次。所以嗅探型预览器的实际触发场景是"扩展名落空 / 二进制文件";
  5. 全部落空返回undefined,编辑器显示下载按钮;在设置页被用户禁用的预览器整轮跳过。

内置 6 个预览器的优先级全景

预览器priorityexts策略备注
image0png/jpg/gif/webp/svg/…mediaUrl常见图片格式
pdf0pdfmediaUrl浏览器原生查看器内嵌
markdown0md/markdownfsRead含 Mermaid、内嵌 HTML 消毒、浮动目录
html0html/htmfsRead沙箱 iframe 预览
binary-download-50doc/xls/ppt +NUL 嗅探binary-downloaddetect检查 head 含 NUL 字节
code-100[]catch-allfsReadCodeMirror 文本编辑兜底

💡覆盖套路:想自定义.svg渲染,注册exts: ['svg'], priority: 10即可压过内置image;想按 magic bytes 认领无扩展名的 Parquet,注册exts: [], priority: 100, detect: (p, head) => head[0] === 0x50 …('PAR1' 魔数)。

六、真实案例:仓库里的预览器是怎么长的

案例 1:Office 三件套预览(.docx / .xlsx / .pptx)——官方不再内置,而是拆成推荐插件,通过设置页「添加插件」→ 文件预览弹窗安装,插件以相同 id 走同一套 API 注册。目录数据源在 src/client/plugins-viewers.ts:加一条数据即"上架",tests/plugin-list.spec.ts 守护。

案例 2:内置binary-download——src/client/binary-download.tsx 配detect: (_path, head) => head.includes(0):文件头含 NUL 即判定二进制,在 head 重匹配阶段抢在code兜底之前认领,给用户一个下载按钮而非乱码页。

案例 3:三方插件生态——首批通过ctx.betterSidebar接入的三方插件(dsh-sentinel 监控台、dsh-sidebar-qa 划选追问)都以"可选软依赖"方式注册,better-sidebar 未安装时注册静默跳过,宿主零侵入。

七、实战提示与避坑清单

陷阱说明
注册没包ctx.effectHMR / 插件禁用后注册残留,再次激活抛"already registered"
id 与内置冲突6 个内置 viewer id 不可重复注册;务必用my-plugin:xxx前缀
catch-all 慎用exts: []会接走所有未被更高优先级认领的文件;只想补几个扩展名就用exts精确列举
文本文件别指望 detect嗅探只在二进制 head 重匹配时触发;文本格式直接用exts或custom策略
别 import 内部模块src/client/api.ts是内部封装,外部插件按指南 §6 的 fetch 模式自己请求/sidebar/api/*

配套阅读:

  • 匹配算法与生命周期测试:tests/service.spec.ts / tests/builtins.spec.ts(内置清单断言 7 tab + 6 viewer)
  • 声明式设置页(你的 viewer 自动出现在这里):src/client/SideCardSection.tsx
  • 完整最小示例(Database tab + CSV viewer):指南 §13

【免费下载链接】DSH-better-sidebar开放的侧边栏底座,支持三方拓展注册新侧边栏页面。内置文件渲染编辑/终端/侧边对话/Git/子代理页面 | Open sidebar foundation, supports third-party extensions to register new sidebar pages. Built-in file rendering/editing, terminal, side chat, Git, and sub-agent pages.项目地址: https://gitcode.com/gh_mirrors/ds/DSH-better-sidebar

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/26 5:04:19

CSS毛玻璃效果实战:backdrop-filter属性从入门到性能调优

1. 毛玻璃效果为什么突然火了——backdrop-filter的价值定位我最早注意到毛玻璃效果&#xff0c;是在做一套后台管理系统的时候。设计师给的设计稿里&#xff0c;侧边栏和顶部导航都带有一层半透明的磨砂质感&#xff0c;底下表格滚动时&#xff0c;内容透过导航栏能隐隐约约看…

作者头像 李华
网站建设 2026/9/26 5:01:03

桂电编译原理期末实战:语法树、DFA、LR(0)与FIRST/FOLLOW避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 5:00:54

Flutter鸿蒙跨平台开发实战:构建旅行规划助手全流程解析

1. 方案选择与整体设计1.1 为什么旅行规划助手选Flutter而不是ArkTS原生或uniApp拿到“Flutter 框架跨平台鸿蒙开发 - 旅行规划助手应用开发教程”这个标题&#xff0c;可能有人第一反应是&#xff1a;既然要上鸿蒙&#xff0c;直接用ArkTS写原生不就行了&#xff0c;何必绕一圈…

作者头像 李华
网站建设 2026/9/26 5:00:12

YOLOv8+PaddleOCR车牌识别实战:从环境搭建到端到端调优

简介&#xff1a;这份资源面向计算机视觉方向的毕业设计、课程设计学生及入门开发者&#xff0c;提供一套基于YOLOv8与PaddleOCR融合的智能车牌识别系统完整工程。系统覆盖图像预处理、车牌定位、字符分割与OCR识别全流程&#xff0c;可应用于车辆监控、停车场管理与交通流量控…

作者头像 李华
网站建设 2026/9/26 4:59:09

Omarchy:面向专业工作流的GNOME+Wayland原生桌面重构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/26 4:58:46

本田雅阁直降10万背后:B级车价格战与合资品牌生存逻辑

最近车友群和短视频平台都在刷同一句话&#xff1a;本田雅阁直降10万。第一次看到这个标题&#xff0c;我的反应是又有人在搞流量&#xff1b;可等我去4S店转了一圈&#xff0c;发现事情没有这么简单。展厅里确实挂出了“限时冲量”的牌子&#xff0c;销售报出来的价格&#xf…

作者头像 李华