DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

yuanyiHY /

yuanyiHY/deepseek-media-gallery

Verified

DSH (cordis) plugin: browse, preview, download and delete generated images and videos from a semi-transparent sidebar popup.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@c84a6f80

DeepSeek Media Gallery

DSH(cordis)插件:把一个媒体目录里的图片/视频列成画廊 —— 预览、Range 播放、下载到桌面、删除。入口是侧边栏底部的按钮,打开一个半透明弹出窗口(不占中央聊天区)。

媒体画廊弹窗

插件分两半:

半边 文件 作用
宿主(Node) lib/index.js cordis 插件:name / inject / Config / apply,注册 <prefix>/* 路由
浏览器 lib/client.js DSH 客户端插件:window.__ModuleLoader__.load({id, factory}),apply(ctx) 里经 ctx.slots 注册入口按钮 + 弹窗

浏览器半边是 React 原生渲染,不是 iframe。

client/ 目录下另有一套独立页面(index.html + client.js),由宿主路由 <prefix>/ 与 <prefix>/client.js 提供,用于直接在浏览器打开画廊调试。它与 lib/client.js 无关,别混淆。

侧边栏与弹窗

插槽 kind 注册项 说明
sidebar.footer.action list { id: 'media-gallery-panel', order: 60 } 侧边栏底部(设置旁边)的入口按钮,owner props 含 wide
shell.overlay list { id: 'media-gallery-panel', order: 60 } 帧级浮层,弹窗本体渲染在这里

刻意不注册 main / sidebar.panellist —— 那两个会把画廊变成占据中央区的常驻面板("把聊天框都占了")。弹窗关闭时组件返回 null,不常驻 DOM。

关闭方式有三种:右上角 ✕、点背景遮罩、按 Esc(先关预览大图,再关窗口)。

设置(位置可二次设置)

弹窗右上角齿轮进入设置页,三项路径都能改,保存后立即生效,不需要重启:

键 含义 默认
generatedDir 图片存放位置(画廊扫描这里) $DSH_HOME/plugin-data/image-gen/generated
dataRoot 记录存放位置(tasks.json / mcp-records.json) $DSH_HOME/plugin-data/media-gallery
downloadDir 下载位置 自动探测的桌面
  • 「选择…」按钮先试宿主原生目录对话框(ctx.uiWorkspace.pickDirectory()),失败则自动退到内置目录浏览器,并在顶部说明退化的原因。两条路径都拿不到时提示直接填路径。
    • 内置浏览器:盘符切换行(点一下换到别的盘)+ 面包屑跳转 + 逐层进入子文件夹 + 「选用此目录」+ 新建文件夹 + 「转到」直接粘路径(支持 E:\ 和 \\server\share 这类 UNC)。
    • 为什么需要盘符行:browse 能力只会列"某个盘里面",crumbs 的祖先链到 C: 就断了 —— 不列盘符就永远出不了当前盘。盘符只有宿主能枚举,所以走 GET /api/drives(Windows 上探测 A–Z,比调 wmic/PowerShell 便宜也安全)。
    • 为什么必须内置一个:pick() 需要 native 能力,而 @deepseek-ai/dsh-host-directory-picker-auto 的判定是 if (facts.platform !== "linux" || !facts.linuxChooser) return "browse" —— 即 native 只在 Linux(zenity/kdialog + DISPLAY)成立,Windows/macOS 上 pick() 必然报 directoryPicker.pick needs the native capability; the composed picker serves "browse"。而 browse 能力(listDirectory / createDirectory)处处可用,内置浏览器就走它。
    • 另外两个踩过的坑:① 服务由 @deepseek-ai/dsh-client-ui-workspace 提供,可能晚于本插件注册,所以不能在 apply() 时快照,必须点击现取;② cordis 的 reflect 层规定「属性访问未 inject 的服务会抛 cannot get property "..." without inject」,而 ctx.get(name) 是 read a service without the inject requirement —— 必须先走 ctx.get(),否则异常被 catch 吞掉,永远取不到服务。
    • listDirectory 只接受全限定路径;输入框里是相对值时浏览器从宿主主目录起步。
  • 输入框留空(灰色占位符显示默认值)表示回退到默认值。
  • 每行下方显示该目录当前状态:是否存在、以及图片目录里有多少个媒体文件。
  • 值的优先级:用户保存的设置 > profile cordis.patch.yml 里的 config > 环境变量 > 自动默认。前三者通过 dsh-settings 的 base 组合层实现(generatedDir/dataRoot 仍可用 DEEPSEEK_MEDIA_GENERATED_DIR / DEEPSEEK_MEDIA_DATA_DIR 覆盖种子)。

因为设置是每次请求实时读取的,改完目录后下一次 /api/tasks 就是新目录,无需重启 host。

目录扫描兜底

画廊不只列"登记过"的文件:tasks.json / mcp-records.json 里没有、但确实躺在图片目录里的媒体文件也会列出来(上限 500 个,按修改时间倒序,adapterId: "scan")。所以把插件指向一个装满图片的普通文件夹也能直接用。

背景板(与「律动」同款半透明)

dsh-rail-equalizer 的面板底色是 var(--dsw-specific-input-major, var(--dsw-alias-bg-base))。这个 token 由毛玻璃主题 wallpaper-engine 定义成 rgba(255,255,255,var(--we-glass-alpha)),所以只要用同一个 token,半透明观感就和律动一致,不需要自己拍透明度:

用途 取值
弹窗底色 var(--dsw-specific-input-major, var(--dsw-alias-bg-base))
卡片底色 var(--dsw-specific-bubble, var(--dsw-alias-bg-layer-1))
模糊 blur(var(--we-blur,16px)) saturate(var(--we-saturate,1.8))

后两个 --we-* 变量同样来自 wallpaper-engine,缺省值即其默认值;没有主题时全部回落到官方 --dsw-alias-*。

下载(原素材作为备份,不动)

方式 行为
卡片上的 ⤓ / 预览里的「下载」 POST /api/download → 服务端把文件复制到下载位置,原文件保持原样
复制失败(如没有写权限) 自动退回浏览器下载 <prefix>/api/file/<name>(Content-Disposition: attachment)

同名文件不会覆盖:第二份会存成 名字 (2).ext。删除才是真正删掉存档文件,别拿删除当"清空桌面"。

目录结构

lib/index.js          宿主半边(cordis 插件)
lib/client.js         浏览器半边(侧边栏按钮 + 弹窗 + 设置页)
client/index.html     独立页面
client/client.js      独立页面脚本(≠ lib/client.js)
cordis.patch.yml      bundle patch:把本插件插进 profile 层栈
test/plugin.test.mjs  宿主半边测试(60 条)
test/client.test.mjs  浏览器半边测试(73 条)
test/preview.mjs      本地预览:用真实 handler 起临时服务
legacy/               Hana 形状的旧实现(routes/、旧 index.js、manifest.json),DSH 不加载,仅作参考

数据文件格式(dataRoot 下):

  • tasks.json:[{ taskId, status: "done", files: [...], prompt, modelId, createdAt }],只有 status === "done" 且文件存在才渲染。
  • mcp-records.json:[{ taskId?, filename, prompt?, modelId?, createdAt? }],MCP 生图(ofapp-image-mcp)的产物登记在这里。

两处都会读,并按 filename 去重(同名时保留 tasks.json 的条目,元数据更全),剩下的再交给目录扫描兜底。

宿主路由(前缀默认 /deepseek-media-gallery)

方法 路径 说明
GET / 独立页面
GET /client.js 独立页面脚本
GET /api/tasks 媒体列表(tasks + mcp-records + 目录扫描,去重)
GET /api/media/:filename 媒体流,支持 Range(bytes=0-9、bytes=-10、bytes=50-,越界/畸形 416)
GET /api/file/:filename 附件下载(浏览器兜底)
GET /api/drives 目录浏览器可跳转的根(Windows 盘符列表 / POSIX /)
GET /api/settings 当前值 / 默认值 / 各目录状态
POST /api/settings 更新路径(空字符串 = 回退默认)
POST /api/download 复制到下载位置({ filename, destDir?, overwrite? })
DELETE /api/tasks/:id 删除记录 + 存档文件 + 对应 mcp 记录;体 { "filename": "..." }

删除不可撤销。filename 只接受媒体目录内的裸文件名,拒绝路径分隔符、..、: 与 NUL。

配置项

Config 是 schemastery schema(不能是普通对象:cordis 走 Config["~standard"].validate(),dsh-settings 会把 schema 当函数调用):

键 默认 说明
routePrefix /deepseek-media-gallery 路由前缀
dshHome "" 留空用 DSH_HOME
generatedDir / dataRoot / downloadDir "" 设置项的种子值,用户在面板里保存的值优先

浏览器半边用 PREFIX 常量拼请求地址(lib/client.js 顶部),改了 routePrefix 要同步改它。

开发

npm test          # 宿主 60 条 + 浏览器 73 条
node test/preview.mjs --port 43199 \
  --generated "<图片目录>" --data "<记录目录>"

test/ 下的脚本会先把 lib/、client/ 和自己复制到 ~/.dsh/profiles/ 下一个临时目录再跑 —— 插件自身没有 node_modules,@deepseek-ai/* 只有从 profile 的 node_modules 树里才解析得到。

说明与局限

  • 纯本地:所有读写都在本机文件系统上完成,不联网、不上传任何东西。
  • 宿主路由挂在 DSH 的本地 webserver 上(默认只监听 127.0.0.1),没有额外的鉴权层;routePrefix 可以改,但别把它暴露到公网。
  • 平台:Windows / macOS / Linux 都能用。差异只有目录选择器 —— 原生对话框仅 Linux 有(zenity/kdialog),其余平台自动走内置浏览器。
  • 删除不可撤销:会同时删掉记录和磁盘上的媒体文件。下载是复制,不动原文件。
  • 画廊按 dataRoot 里的 tasks.json / mcp-records.json 索引;没有任何登记时用目录扫描兜底。这两个文件由你使用的生图插件(例如 ofapp-image-mcp)写入。

安装

npm 包形状:package.json 的 dsh.bundle.patch 指向 cordis.patch.yml(插进 profile 层栈),dsh.client 声明浏览器半边(exports["./client"] → lib/client.js)。peer 依赖从 profile 的 node_modules 解析。改完需要重启 host 才会重新加载。

—/ 5

No ratings yet

Verified DSH bundle

Commit c84a6f80aa13

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout