dsh-element-source
> 在你的开发页面里点击任意 UI 元素,跳转到它的 Vue / React / Svelte / Angular 源代码。DeepSeek Harness(DSH)的点击定位源码检查器,兼容 dsh-better-sidebar:预览本地 dev server,点击元素,源码位置进入 DSH 聊天,交给助手微调。
<p align="center"> <img src="https://img.shields.io/badge/dsh--plugin--better--sidebar-blue" alt="dsh 插件,兼容 dsh-better-sidebar" /> <img src="https://img.shields.io/npm/v/dsh-element-source" alt="npm 版本" /> <img src="https://img.shields.io/github/license/GULI-lab/DSH-element-source" alt="开源协议" /> </p>
English | 中文
这是什么
开发者经常要在「页面上的某个按钮 / 某段文案」和「源代码里对应的那一行」之间来回找。这个插件把这一过程变成一次点击:
1. 在「本地预览」入口里打开你的前端页面(dev server)——装有 dsh-better-sidebar 时它是侧边栏里的一个 Tab,没装时是本插件自带的右侧侧边栏; 2. 打开「拾取模式」,鼠标悬停高亮目标,点击一下; 3. 插件自动解析出对应的源文件 + 行号,并把一条 [元素定位] 消息填入聊天输入框(不会自动发送,由你决定); 4. 助手用 read 工具读取该文件,在对话框里展示定位到的代码,等你提出修改需求。
不需要安装浏览器扩展,支持 Vue / React / Svelte / Angular 及任意其他框架(通用兜底)。
框架支持
| 框架 | 文件 | 行 | 说明 |
|---|---|---|---|
| React(dev 构建) | ✅ 精确 | ✅ 精确 | 读取 fiber 上的 JSX __source(@vitejs/plugin-react / CRA 自带) |
| Vue 2 / Vue 3(dev 构建) | ✅ 精确 | ~ 精确 | 读取 __file 定位文件;行号由模板文本匹配确定 |
| Svelte(dev 构建) | ✅ 精确 | ✅ 精确 | 读取 dev 模式注入的 __svelte_meta |
| Angular | ~ 尽力 | ~ 尽力 | 识别 ng-reflect-* 标记,走组件/文本搜索 |
| 任意框架 + 已装 code-inspector-plugin | ✅ 精确 | ✅ 精确 | 直接读取它注入的 data-insp-* DOM 属性 |
| 其他 / 未知框架 | ✅ | ~ | 按点击元素的文本 / 类名 / id 在会话工作区内全文搜索 |
工作原理
代理模式:GET /dsh-element-source/preview?url=…
插件 host 取回 dev 页面 → 注入 <base href=dev地址> + 探针脚本 + 真实地址标记
→ 从 GUI 源返回(页面获得真实 origin,localStorage/登录态正常,探针自动就绪)
页面内探针(inject.js,IIFE、零依赖)
└─ 悬停高亮 → 点击 → 探针链(data-insp → React __source → Vue __file → Svelte → Angular → 通用)
→ postMessage(跨域可用)→ DSH 页面
「本地预览」入口(本插件注册,可打开 localhost)
├─ 已装 dsh-better-sidebar → 侧边栏「本地预览」Tab(推荐入口)
└─ 未装 → 右侧全高侧边栏(可收起为边缘条,跟随当前会话)
└─ 收 postMessage → POST /dsh-element-source/api/resolve
DSH Host
├─ GET /dsh-element-source/inject.js (对外服务探针脚本)
├─ GET /dsh-element-source/preview (取回页面 + 注入探针,仅限本机地址)
├─ POST /dsh-element-source/api/resolve (信任围栏保护)
│ ├─ 路径规范化(webpack /src、Vite 绝对路径、Windows 盘符、可配置 mappings)
│ ├─ 越界检查:定位结果必须落在会话 cwd 内
│ └─ 文本 / 组件搜索兜底定位行号
└─ 定位结果填入聊天输入框草稿(不自动发送),由你决定何时发送与 dsh-better-sidebar 的兼容
插件不依赖 dsh-better-sidebar,但完全兼容它:装了 better-sidebar 时入口是它的侧边栏 Tab(本插件的独立右侧侧边栏不显示,避免重叠);没装时是右侧全高侧边栏(可收起为边缘条),背景用 DSH 主题 token,随 GUI 明暗主题自动变化。两者路由(/sidebar/* vs /dsh-element-source/*)、postMessage 命名空间、UI 插槽完全独立,互不冲突。
为什么自带一个「本地预览」Tab
前端 dev server 通常跑在 localhost,因此本插件自带一个极简的「本地预览」Tab——一个能直接打开本机地址(localhost / 127.x)的 iframe + 地址栏(它不是浏览器,不做多标签 / 历史等功能)。预览页面经由 DSH 源代理加载,探针自动注入、自动就绪。如果页面不在本机地址(例如绑定了局域网 IP 的 dev server),用 better-sidebar 内置浏览器或普通浏览器标签打开同样可以——inject.js 通过 postMessage 回传,不受沙箱影响。
安装
前置:已装好 DSH(dsh web 可运行)。可选:dsh-better-sidebar——装了它,入口是侧边栏「本地预览」Tab;不装,则是右侧全高侧边栏。
macOS / Linux(Windows 装了 Git Bash 或 WSL 也可):
curl -fsSL https://raw.githubusercontent.com/GULI-lab/DSH-element-source/main/scripts/install.sh | bashWindows(PowerShell 5.1+ / pwsh):
irm https://raw.githubusercontent.com/GULI-lab/DSH-element-source/main/scripts/install.ps1 | iex手动安装:
cd ~/.dsh/profiles/web
npx -y --package @deepseek-ai/dsh dsh plugin --profile web add dsh-element-source装完硬刷新浏览器(Ctrl/Cmd+Shift+R)。
让页面自己加载探针(可选)
「本地预览」Tab 的代理模式会自动注入探针。只有在你不用本插件预览、而是用其他浏览器打开页面(例如 better-sidebar 内置浏览器打开非本机地址)的场景,才需要让页面自己加载探针,二选一:
方式 A:手动加一行 script。在你的 index.html 的 <body> 里加:
<script src="http://127.0.0.1:3080/dsh-element-source/inject.js"></script>(3080 换成你 DSH Web UI 的实际端口;也可以用局域网 IP 以便其他设备访问。)
方式 B:Vite 插件自动注入(Vue / React / Svelte / Angular 的 Vite 项目通用):
// vite.config.ts
import { defineConfig } from 'vite'
import { dshElementSourcePlugin } from 'dsh-element-source/vite-plugin'
export default defineConfig({
plugins: [dshElementSourcePlugin(), /* 你的其他插件 */],
})插件只在 dev server(apply: 'serve')注入,生产构建不受影响。如果你的 DSH 不在默认地址,传 dshOrigin:
dshElementSourcePlugin({ dshOrigin: 'http://192.168.1.5:3080' })使用
1. 打开入口:装了 better-sidebar → 侧边栏「本地预览」Tab;没装 → 右侧全高侧边栏(默认展开,可收起为边缘条)(注意:这里打开的是你自己的开发页面,不是 DSH 界面本身); 2. 地址栏输入 http://localhost:3000(你的 dev server),回车; 3. 看探针状态图标变绿(wifi 图标,代理自动就绪); 4. 点选择图标(十字准星),在页面里移动鼠标——目标元素高亮;点击即定位源码; 5. 面板底部显示定位信息:文件:行号 与源码片段; 6. 点击后结果自动填入聊天输入框(如 [元素定位] src/components/App.vue:12)——不会自动发送,你可以继续补充自己的需求再回车发送。
> Esc 取消选择。点击只把定位结果填入聊天输入框(草稿),发送与否完全由你决定。
工具栏另有:刷新(重载当前页面)、外部打开(真实浏览器标签页)。
配置
在 DSH 的 cordis.patch.yml(或 profile 的 patch)中按行覆盖:
- id: element-source
config:
autoSteer: false # 拾取后自动唤醒助手(默认 false:只填聊天输入框草稿)
mappings: # 源码路径前缀映射(monorepo / node_modules 重定向)
- find: '@app/ui/src'
replacement: 'D:/workspace/my-app/packages/ui/src'
proxyHosts: # 代理模式额外放行的 host(默认仅本机回环地址)
- '192.168.1.10'
sessionId: 'fixed-session' # 固定会话(一般不需要)安全
/dsh-element-source/api/resolve与/dsh-element-source/preview都走与/api网关相同的浏览器信任围栏(Host 头 +trustedHosts);- 解析出的文件路径必须落在会话工作区(cwd)内,否则拒绝;
- 预览代理只允许本机回环地址(默认),且返回的页面与 DSH 同源——它只能承载你信任的本地 dev server;页面相对路径的 API 请求会打到 DSH 源(已知代价;需要同源数据接口的应用请让页面自己加载探针);
- inject.js 是只读代码,只收集点击元素的元数据,不访问 DSH 数据;
inject.js路由对外公开(让页面跨域加载它); trust-fence.ts源自 DSH 的 BSD-3-Clause 实现(行为一致,独立拷贝,见文件头注释)。
与 code-inspector-plugin 的关系
code-inspector 是编译期方案:它作为 bundler 插件改写 JSX / SFC 编译,给元素注入 data-insp-* 属性,点击后用 launch-ide 打开本地 IDE。本插件是运行期方案:读取 dev 运行时已有的元数据,把定位结果交给 DSH 聊天而不是外部 IDE。两者互补——如果你已经装了 code-inspector-plugin,本插件会直接读取它注入的 data-insp-* 属性,获得全框架的精确行号,零额外成本。
参与贡献
欢迎贡献!开发环境搭建与提交流程见 [CONTRIBUTING.md](CONTRIBUTING.md)。发现 bug 或有新想法,欢迎开 issue。
开发
pnpm install
pnpm typecheck # tsc --noEmit
pnpm test # vitest(resolve / probes / steer / protocol / preview-proxy / draft)
pnpm build # tsc 类型 + tsdown 双端产物(lib/index.js、lib/client.js、lib/inject.js、lib/vite-plugin.js)产物:lib/index.js(host)、lib/client.js(浏览器端,window.__ModuleLoader__.load 模块表格式)、lib/inject.js(页面探针,经典 <script>,零依赖)、lib/vite-plugin.js(Vite 注入插件)。
想在 npm 发布前先本地试用源码版本:
git clone https://github.com/GULI-lab/DSH-element-source
cd ~/.dsh/profiles/web
npx -y --package @deepseek-ai/dsh dsh plugin --profile web add link:/path/to/DSH-element-source已知限制
- 本地预览 Tab 面向你自己的开发页面:把 DSH 界面本身(如
http://127.0.0.1:3080)放进预览 iframe 不会渲染——GUI 无法以被代理的方式启动,这不是本插件的 bug; - 预览页面从 DSH 源加载:相对路径的 API 请求会打到 DSH 源而非 dev server(需要同源数据接口的应用请让页面自己加载探针);页面自带严格 CSP(如
script-src 'self')的极少数应用可能拦掉注入的探针; - Vue 的行号依赖模板文本匹配:纯图标 / 无文本元素会退化为「只定位文件」或组件定义行;
- Angular / Svelte 生产构建不带运行期源码信息,需 dev 构建;
- 若要在本地预览 Tab 之外的浏览器里(非本机地址)使用定位,页面需自行加载探针(手动一行或 Vite 插件);这是跨域 iframe 无法从父页面读取 DOM 的同源策略决定的,任何 iframe 方案都绕不开。
License
[MIT](LICENSE) © dsh-element-source contributors