🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

网页批注预览 CanglongCl/dsh-web-review

DeepSeek Harnessv1.1.2
CanglongCl59 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness Web GUI 的网页预览与元素批注插件,让 AI 根据可视化反馈直接修改前端源码。

43/100
综合分
20
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/17 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查未通过:缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装。以下命令仍可尝试,但可能失败。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add CanglongCl/dsh-web-review
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:需留意
是什么
dsh 原生插件 · ui
装得上吗
实装验证未通过(unknown),装前请到仓库确认最近更新与 issue
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 59 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
⚠︎ 实装验证未通过(unknown · 2026/10/21) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装

缺少 main/exports/bin 入口声明;仓库 package.json 标记 private,未发布到 npm,需从源码安装

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-web-review @ 1.1.2
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✗入口文件缺少入口声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/30 15:48:16

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 154 处,展示前 120 条)
  • 执行外部命令来自配置/变量
    eval/capture/capture.ts:70
    const child = spawn(process.execPath, ['--import', 'tsx', join(REPO_ROOT, 'eval', 'fixtures', 'serve.ts'), baselineDir(task.fixture), String(port)], {
  • 发起网络请求
    eval/capture/capture.ts:78
    const response = await fetch(url, { signal: AbortSignal.timeout(3000) })
  • 执行外部命令来自配置/变量
    eval/capture/capture.ts:89
    const child = spawn(process.execPath, [viteBin, '--port', String(port), '--strictPort'], {
  • 发起网络请求
    eval/capture/capture.ts:96
    await waitFor(async () => (await fetch(url)).ok, 30_000, `${task.id} vite server`)
  • 删除本地文件递归
    eval/capture/capture.ts:97
    return { url, stop: () => { child.kill('SIGTERM'); rmSync(copy, { recursive: true, force: true }) } }
  • 写入本地文件
    eval/capture/capture.ts:110
    writeFileSync(join(dshHome, 'settings.yaml'), [
  • 读取本地文件
    eval/capture/capture.ts:117
    readFileSync(join(REPO_ROOT, 'packages', 'dsh-web-review', 'entry-name.json'), 'utf8'),
  • 写入本地文件
    eval/capture/capture.ts:120
    writeFileSync(overlayPath, [
  • 读取密钥环境变量
    eval/capture/capture.ts:149
    if (process.env.DEEPSEEK_API_KEY !== undefined) break
  • 执行外部命令来自配置/变量
    eval/capture/capture.ts:159
    const web = spawn(launch.command, launch.args, { cwd: REPO_ROOT, stdio: ['ignore', 'pipe', 'pipe'], env: launch.env })
  • 删除本地文件递归
    eval/capture/capture.ts:167
    rmSync(dshHome, { recursive: true, force: true })
  • 发起网络请求
    eval/capture/capture.ts:170
    await waitFor(async () => (await fetch(webUrl)).ok, 90_000, 'web GUI ready')
  • 写入本地文件
    eval/capture/capture.ts:179
    mkdirSync(join(root, name), { recursive: true })
  • 发起网络请求
    eval/capture/capture.ts:272
    const body = route.request().postData()
  • 写入本地文件
    eval/capture/capture.ts:393
    mkdirSync(dir, { recursive: true })
  • 写入本地文件
    eval/capture/capture.ts:394
    writeFileSync(frozenPath(task.id, round, 'snapshot.json'), JSON.stringify(snapshot, null, 2))
  • 写入本地文件
    eval/capture/capture.ts:395
    writeFileSync(frozenPath(task.id, round, 'meta.json'), JSON.stringify({
  • 读取本地文件
    eval/capture/capture.ts:470
    if (!existsSync(frozenPath(task.id, roundNumber, 'snapshot.json'))) {
  • 读取本地文件
    eval/capture/capture.ts:475
    const frozen = JSON.parse(readFileSync(frozenPath(task.id, roundNumber, 'snapshot.json'), 'utf8')) as FrozenSnapshot
  • 读取本地文件
    eval/capture/capture.ts:480
    const meta = JSON.parse(readFileSync(metaPath, 'utf8')) as CaptureMeta
  • 读取本地文件
    eval/fixtures/serve.ts:38
    const body = await readFile(file)
  • 发起网络请求
    eval/grader.ts:40
    const response = await fetch(url)
  • 执行外部命令来自配置/变量
    eval/grader.ts:59
    const child = spawn(process.execPath, ['--import', 'tsx', SERVE_SCRIPT, dir, String(port)], {
  • 执行外部命令来自配置/变量
    eval/grader.ts:70
    const child = spawn(process.execPath, [viteBin, '--port', String(port), '--strictPort'], {
  • 读取本地文件
    eval/grader.ts:96
    if (task.fixtureKind !== 'static' && !existsSync(join(temp, 'node_modules'))) {
  • 读取本地文件
    eval/grader.ts:98
    if (existsSync(modules)) {
  • 读取本地文件
    eval/grader.ts:104
    const patch = readFileSync(join(base, task.golden.patchFile), 'utf8')
  • 执行外部命令固定命令
    eval/grader.ts:105
    execFileSync('git', ['apply', '-p1'], { cwd: temp, input: patch, encoding: 'utf8' })
  • 读取本地文件
    eval/grader.ts:539
    if (!existsSync(path)) return { ok: false, expected: `${assertion.file} contains ${assertion.contains.join(' | ')}`, measured: 'file missing' }
  • 读取本地文件
    eval/grader.ts:540
    const content = readFileSync(path, 'utf8')
  • 读取本地文件
    eval/grader.ts:552
    for (const entry of readdirSync(dir)) {
  • 写入本地文件
    eval/grader.ts:611
    mkdirSync(evidenceDir, { recursive: true })
  • 删除本地文件递归
    eval/grader.ts:640
    rmSync(variantDir, { recursive: true, force: true })
  • 读取本地文件
    eval/identity.ts:25
    return digest(`${readFileSync(GRADER_SOURCE, 'utf8')}\n${JSON.stringify(task.grader)}`)
  • 读取本地文件
    eval/identity.ts:29
    return digest(EXECUTION_SOURCES.map(url => readFileSync(fileURLToPath(url), 'utf8')).join('\n'))
  • 读取本地文件
    eval/process-stats.ts:64
    const lines = readFileSync(sessionPath, 'utf8').split('\n').filter(line => line.trim() !== '')
  • 写入本地文件
    eval/process-stats.ts:237
    writeFileSync(tracePath, trace.join('\n'))
  • 写入本地文件
    eval/process-stats.ts:259
    writeFileSync(join(outDir, 'process.json'), JSON.stringify(stats, null, 2))
  • 读取本地文件
    eval/regrade.ts:14
    if (!existsSync(RESULTS_FILE)) throw new Error(`no results at ${RESULTS_FILE}`)
  • 读取本地文件
    eval/regrade.ts:21
    for (const line of readFileSync(RESULTS_FILE, 'utf8').split('\n')) {
  • 读取本地文件
    eval/regrade.ts:35
    if (!existsSync(workspace)) continue
  • 写入本地文件
    eval/regrade.ts:37
    mkdirSync(join(record.runDir, 'evidence'), { recursive: true })
  • 读取本地文件
    eval/regrade.ts:38
    const process = existsSync(session)
  • 写入本地文件
    eval/regrade.ts:41
    if (process !== undefined) writeFileSync(join(record.runDir, 'process.json'), JSON.stringify(process, null, 2))
  • 写入本地文件
    eval/regrade.ts:49
    writeFileSync(join(record.runDir, 'grader.json'), JSON.stringify(grader, null, 2))
  • 写入本地文件
    eval/regrade.ts:64
    appendFileSync(RESULTS_FILE, `${JSON.stringify(updated)}\n`)
  • 读取本地文件
    eval/report.ts:64
    if (record.runDir !== '' && existsSync(join(record.runDir, 'session.jsonl'))) {
  • 读取本地文件
    eval/report.ts:68
    if (record.runDir !== '' && existsSync(join(record.runDir, 'trace.md'))) detail.traceHref = linkTo('trace.md')
  • 读取本地文件
    eval/report.ts:76
    if (existsSync(path)) {
  • 读取本地文件
    eval/report.ts:77
    const text = readFileSync(path, 'utf8')
  • 读取本地文件
    eval/report.ts:86
    if (!existsSync(resultsFile)) {
  • 读取本地文件
    eval/report.ts:92
    const parsed = readFileSync(resultsFile, 'utf8').split('\n')
  • 写入本地文件
    eval/report.ts:579
    mkdirSync(outDir, { recursive: true })
  • 写入本地文件
    eval/report.ts:581
    writeFileSync(out, html)
  • 写入本地文件
    eval/report.ts:610
    mkdirSync(archiveDir, { recursive: true })
  • 写入本地文件
    eval/report.ts:611
    writeFileSync(join(archiveDir, 'report.html'), html)
  • 写入本地文件
    eval/report.ts:631
    writeFileSync(join(archiveDir, 'summary.json'), JSON.stringify({
  • 执行外部命令固定命令
    eval/runner/batch.ts:112
    execFileSync('pnpm', ['--filter', '@dsh-web-review-dev/eval-runner', 'build'], { cwd: REPO_ROOT, stdio: 'inherit' })
  • 执行外部命令固定命令
    eval/runner/batch.ts:114
    const currentRepoCommit = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: REPO_ROOT, encoding: 'utf8' }).trim()
  • 执行外部命令固定命令
    eval/runner/batch.ts:116
    ? execFileSync('git', ['rev-parse', 'HEAD'], { cwd: harnessRoot, encoding: 'utf8' }).trim()
  • 读取本地文件
    eval/runner/batch.ts:118
    const manifest = JSON.parse(readFileSync(join(dirname(dirname(dshCli)), 'package.json'), 'utf8')) as { name?: string; version?: string }
  • 读取本地文件
    eval/runner/batch.ts:125
    if (!flags.force && existsSync(RESULTS_FILE)) {
  • 读取本地文件
    eval/runner/batch.ts:126
    for (const line of readFileSync(RESULTS_FILE, 'utf8').split('\n')) {
  • 写入本地文件
    eval/runner/batch.ts:141
    mkdirSync(ARTIFACTS_ROOT, { recursive: true })
  • 写入本地文件
    eval/runner/batch.ts:142
    mkdirSync(RESULTS_PATH, { recursive: true })
  • 写入本地文件
    eval/runner/batch.ts:169
    appendFileSync(RESULTS_FILE, `${JSON.stringify(record)}\n`)
  • 写入本地文件
    eval/runner/batch.ts:208
    appendFileSync(RESULTS_FILE, `${JSON.stringify(record)}\n`)
  • 写入本地文件
    eval/runner/batch.ts:217
    writeFileSync(join(RESULTS_PATH, 'run-summary.json'), JSON.stringify({
  • 写入本地文件
    eval/runner/run-one.ts:50
    mkdirSync(join(liveRoot, 'runtime', 'dependencies'), { recursive: true })
  • 删除本地文件
    eval/runner/run-one.ts:52
    unlinkSync(join(workspaceDir, 'node_modules'))
  • 写入本地文件
    eval/runner/run-one.ts:74
    mkdirSync(runDir, { recursive: true })
  • 写入本地文件
    eval/runner/run-one.ts:75
    mkdirSync(dshHome, { recursive: true })
  • 读取本地文件
    eval/runner/run-one.ts:78
    if (liveRoot === undefined && !existsSync(workspaceDir)) stageWorkspace(task, workspaceDir)
  • 写入本地文件
    eval/runner/run-one.ts:88
    mkdirSync(runtimeRunner, { recursive: true })
  • 写入本地文件
    eval/runner/run-one.ts:89
    mkdirSync(join(runtimeRepo, 'packages', 'dsh-web-review'), { recursive: true })
  • 写入本地文件
    eval/runner/run-one.ts:121
    writeFileSync(join(runDir, 'stdout.txt'), launch.stdout)
  • 删除本地文件递归
    eval/runner/run-one.ts:168
    rmSync(liveRoot, { recursive: true, force: true })
  • 读取本地文件
    eval/runner/runner.ts:66
    for (const entry of readdirSync(current).sort()) {
  • 读取本地文件
    eval/runner/runner.ts:68
    const stat = statSync(path)
  • 读取本地文件
    eval/runner/runner.ts:72
    hash.update(readFileSync(path))
  • 写入本地文件
    eval/runner/runner.ts:83
    mkdirSync(workspaceDir, { recursive: true })
  • 读取本地文件
    eval/runner/runner.ts:90
    if (!existsSync(sharedModules)) {
  • 读取密钥环境变量
    eval/runner/runner.ts:123
    if (process.env.DEEPSEEK_API_KEY !== undefined) return { stagedCredentials: false }
  • 读取密钥环境变量
    eval/runner/runner.ts:127
    if (process.env.DEEPSEEK_API_KEY !== undefined) return { stagedCredentials: false }
  • 读取本地文件
    eval/runner/runner.ts:133
    if (existsSync(defaultCredentials)) {
  • 写入本地文件
    eval/runner/runner.ts:135
    copyFileSync(defaultCredentials, staged)
  • 写入本地文件
    eval/runner/runner.ts:136
    chmodSync(staged, 0o600)
  • 写入本地文件
    eval/runner/runner.ts:188
    writeFileSync(path, overlay)
  • 执行外部命令来自配置/变量
    eval/runner/runner.ts:225
    const child = spawn(process.execPath, [
  • 读取本地文件
    eval/runner/runner.ts:255
    if (!existsSync(current)) return
  • 执行外部命令固定命令
    eval/runner/runner.ts:336
    return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: REPO_ROOT, encoding: 'utf8' }).trim()
  • 执行外部命令固定命令
    eval/runner/runner.ts:340
    return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: harnessRoot, encoding: 'utf8' }).trim()
  • 删除本地文件递归
    eval/runner/runner.ts:352
    rmSync(runDir, { recursive: true, force: true })
  • 读取本地文件
    eval/runner-plugin/src/index.ts:120
    content: skillBody(readFileSync(join(directory, 'SKILL.md'), 'utf8')),
  • 读取本地文件
    eval/runner-plugin/src/index.ts:173
    if (!existsSync(join(round.snapshotDir, file))) {
  • 执行外部命令来自配置/变量
    eval/smoke.ts:61
    const result = spawnSync(
  • 读取本地文件
    eval/snapshot-stage.ts:61
    if (!existsSync(frozenPagePath(task.id, roundNumber, suffix))) {
  • 写入本地文件
    eval/snapshot-stage.ts:73
    mkdirSync(dir, { recursive: true, mode: 0o700 })
  • 写入本地文件
    eval/snapshot-stage.ts:76
    copyFileSync(htmlPath, join(dir, 'page.html'))
  • 写入本地文件
    eval/snapshot-stage.ts:77
    copyFileSync(pngPath, join(dir, 'page.png'))
  • 读取本地文件
    eval/snapshot-stage.ts:78
    const html = readFileSync(htmlPath, 'utf8')
  • 读取本地文件
    eval/snapshot-stage.ts:79
    const png = readFileSync(pngPath)
  • 写入本地文件
    eval/snapshot-stage.ts:80
    writeFileSync(
  • 删除本地文件递归
    eval/snapshot-stage.ts:90
    for (const dir of dirs) rmSync(dir, { recursive: true, force: true })
  • 读取本地文件
    eval/tasks/frozen.ts:24
    if (!existsSync(snapshotPath) || !existsSync(metaPath)) {
  • 读取本地文件
    eval/tasks/frozen.ts:30
    snapshot: JSON.parse(readFileSync(snapshotPath, 'utf8')) as FrozenSnapshot,
  • 读取本地文件
    eval/tasks/frozen.ts:31
    captureMeta: JSON.parse(readFileSync(metaPath, 'utf8')) as CaptureMeta,
  • 读取本地文件
    eval/tasks/register.ts:37
    const files = readdirSync(TASKS_DIR)
  • 读取本地文件
    eval/view-session.ts:73
    if (!existsSync(path)) throw new Error(`eval session path does not exist: ${path}`)
  • 读取本地文件
    eval/view-session.ts:74
    const log = statSync(path).isDirectory() ? join(path, 'session.jsonl') : path
  • 读取本地文件
    eval/view-session.ts:75
    if (!existsSync(log)) throw new Error(`eval run has no session.jsonl: ${path}`)
  • 读取本地文件
    eval/view-session.ts:80
    const firstLine = readFileSync(log, 'utf8').split('\n', 1)[0]
  • 写入本地文件
    eval/view-session.ts:135
    mkdirSync(dirname(stagedLog), { recursive: true })
  • 写入本地文件
    eval/view-session.ts:136
    copyFileSync(log, stagedLog)
  • 写入本地文件
    eval/view-session.ts:138
    writeFileSync(overlay, [
  • 执行外部命令来自配置/变量
    eval/view-session.ts:152
    const child = spawn(isJavaScriptEntry ? process.execPath : cli, [
  • 读取本地文件
    eval/view-session.ts:156
    cwd: header.cwd !== undefined && existsSync(header.cwd) ? header.cwd : dirname(log),
  • 删除本地文件递归
    eval/view-session.ts:164
    rmSync(temporary, { recursive: true, force: true })
  • 读取本地文件
    packages/dsh-web-review/src/index.ts:99
    return await readFile(candidate, 'utf8')
  • 读取本地文件
    packages/dsh-web-review/src/skill-provider.ts:97
    content: skillBody(await readFile(skillFile(name), 'utf8')),
扫描于 2026/11/3(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

3 张 · 点击查看大图

功能亮点

  • 网页预览
  • 元素批注
  • AI修改前端源码

README

由 DeepSeek 最新模型翻译生成
dsh-web-review ⭐ 如果这个项目对你有帮助,欢迎点个 Star 支持一下!你的支持是我持续维护和改进的动力。 在内置浏览器中,像使用设计工具一样选择页面元素、填写修改意见,并临时调整文本、颜色、字体、尺寸、间距、边框与效果。确认发送后,智能体会结合页面批注修改当前工作区中的源码。 如果你用过 v0、Codex 等 Coding Agent 应用的内置浏览器,你应该对此会很熟悉。 安装 安装并启动: dsh plugin --profile web add @canglongcl/dsh-web-review dsh web 使用方法 1. 告诉启动要评审的前端页面,点击AI返回的地址页面。 也可以切换到 DSH 的「网页预览」Tab,输入页面的绝对 HTTP(S) URL。 2. 点击批注按钮,再点击页面中的目标元素。 3. 填写修改意见;如需视觉调整,展开「调整」并修改属性。 4. 点击批注工具栏中的发送按钮,发送后会自动切回「对话」Tab;或在 DSH 的输入框中填写更多提示词,然后点击 DSH 发送按钮,注释会随着你的提示词一同发送。 5. 智能体修改源码后,刷新预览进行验收;不满意可以继续下一轮批注。 主要功能 网页预览 - 在 DSH 内打开智能体提供的链接页面 元素批注 - 悬停高亮并点选页面元素。 - 为多个目标添加批注。
展开完整文档(README 剩余 1 节)
dsh-web-review

⭐ 如果这个项目对你有帮助,欢迎点个 Star 支持一下!你的支持是我持续维护和改进的动力。

在内置浏览器中,像使用设计工具一样选择页面元素、填写修改意见,并临时调整文本、颜色、字体、尺寸、间距、边框与效果。确认发送后,智能体会结合页面批注修改当前工作区中的源码。

如果你用过 v0、Codex 等 Coding Agent 应用的内置浏览器,你应该对此会很熟悉。

安装

安装并启动:

dsh plugin --profile web add @canglongcl/dsh-web-review
dsh web

使用方法

1. 告诉启动要评审的前端页面,点击AI返回的地址页面。
也可以切换到 DSH 的「网页预览」Tab,输入页面的绝对 HTTP(S) URL。
2. 点击批注按钮,再点击页面中的目标元素。
3. 填写修改意见;如需视觉调整,展开「调整」并修改属性。
4. 点击批注工具栏中的发送按钮,发送后会自动切回「对话」Tab;或在 DSH 的输入框中填写更多提示词,然后点击 DSH 发送按钮,注释会随着你的提示词一同发送。
5. 智能体修改源码后,刷新预览进行验收;不满意可以继续下一轮批注。

主要功能

网页预览

- 在 DSH 内打开智能体提供的链接页面

元素批注

- 悬停高亮并点选页面元素。
- 为多个目标添加批注。
- 自动附带选择器、文本、可访问名称和源码线索,帮助智能体找到对应实现。

实时视觉调整

- 修改文本、颜色、字体、字号、行高、尺寸和透明度。
- 调整间距、布局、边框、圆角和效果。
- 所有修改即时预览。

AI 协作

- 批注会作为独立上下文随你的提示词注入。
- 对话中的页面批注沿用 DSH 原生折叠行:收起时显示页面与批注数,展开后只展示目标、修改意图、前后值和可用源码线索。
- 智能体根据批注修改当前工作区源码,页面中的临时调整不会直接写入工程。

dsh-better-sidebar集成

安装 dsh-better-sidebar 后,网页预览会自动在侧边栏中打开。你也可以在侧边栏新建 tab 菜单中打开网页预览以启用。

UI 优化 Skills

插件内置了 Jakub Krehel 的设计 Skills:

- better-ui
- better-typography
- better-layout
- better-writing
- better-accessibility
- better-colors
- better-interface
- interface-review

你可以通过斜杠命令调用 skill,也可以在批注编辑器中选择,让智能体在本轮修改中参考相应规则。

插件能力评测

项目包含一套面向真实使用流程的评测,用于验证智能体在收到插件生成的页面批注后,能否正确定位源码并完成前端修改。

评测覆盖:

- 文案、样式、布局和响应式修改。
- 多元素与多项关联修改。
- React、Vue 和静态页面。
- 源码锚点可用及缺失时的定位。
- 语义化与无障碍要求。
- 多轮批注和修改范围判断。
- Token 使用量、执行步骤和运行耗时。

评测设计、运行方式和结果解释见 Eval suite。

参与开发

开发环境、架构说明与验证流程见 CONTRIBUTING.md。

版本变更记录见 CHANGELOG.md。
预览分享卡片长什么样 ↓CanglongCl/dsh-web-review
分享 · DeepSeek Harness Hub
CanglongCl/dsh-web-review

DeepSeek Harness Web GUI 的网页预览与元素批注插件,让 AI 根据可视化反馈直接修改前端源码。

回到顶部