DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

LINinLIN-0079 /

LINinLIN-0079/godot-imagegen

Verified

DSH 插件「美术工坊」:把本机秋叶 SD WebUI / ComfyUI 接进 DeepSeek Harness,对话里直接出图、改写并运行 ComfyUI 工作流,产物落成 Godot 可用美术资产

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@51db5aef

godot-imagegen

本地 AI 出图 → Godot 可用美术资产 的 DSH 插件。

它把本机已装好的两套秋叶出图后端接进 DeepSeek Harness:模型通过 10 个 image_* 工具在对话里直接出图、改写并运行 ComfyUI 工作流、直接驱动右侧面板, 你通过右侧边栏的 「美术工坊」 面板点选提示词、看图、抠图、放大;产物自动落进 Godot 项目的 assets/generated/<品类>/,并给出 res:// 路径,一键注册进 godot-asset-planner 的资产库。

作者:LINinLIN-0079 · 许可证:MIT(见 LICENSE) · 更新记录:CHANGELOG.md

面板里的 ComfyUI 画布不是截图,而是 真正的 ComfyUI 前端(内嵌 iframe)。装上一个随插件分发的 轻量 web 扩展后,对话里说「把 item-icon 放到画布上」就真的会切过去,画布上正在编辑什么也会回传给代理。


1. 能力范围

层 内容
模型工具(10) image_backend_status image_generate image_edit image_variants image_upscale image_cutout image_interrogate image_gallery image_workflow image_workshop
REST(/gig/*) 面板与宿主的同源通道:状态、图库、原图字节、生成/编辑/放大/抠图/反推任务、任务轮询与取消、后端启停、设置与预设、工作流库读写、面板指令与画布中继轮询、画布桥接安装/卸载
工作流库 插件本地($DSH_HOME/godot-imagegen/workflows/,从不写入你的 ComfyUI):6 套游戏美术模板 + 任意新建/改写;元数据写在文档自身的 extra 里
画布桥接 随插件分发的 ComfyUI web 扩展(源码在仓库 comfyui-extension/),安装时把 DSH origin + 一次性令牌 烤进副本;装/卸都是一键,写入前必先备份
对话驱动 代理可通过 image_workflow / image_workshop 改写工作流、把工作流直接推到 ComfyUI 画布、把提示词与参数填进面板、在面板里定位图片——你只需在对话里说
浏览器面板 better-sidebar tab「美术工坊」(order 63)。按可用宽度布局:宽 ≥700px 三栏(图库 / ComfyUI 画布 / 参数),窄了上下堆叠。画布分区默认折叠,载入失败时自动展开并标注。含提示词预设、任务进度(含后端实时预览图)、图库灯箱(放大/抠图/反推/发送到对话/注册资产/删除)。浮窗与全屏只在支持浮窗的侧边栏上出现——dsh-better-sidebar 从 v0.19.0 起已移除该功能(见第 2 节)
DSH 技能(可选配对) 本机技能目录里的 game-art —— 游戏美术提示词手册、分品类尺寸与透明规范、端到端工作流、21 条样例提示词、色键抠图脚本。不属于本仓库,插件不依赖它,只是提示词写法与它对齐
持久化 独立 KV 单元 godot_imagegen(图库记录 + 用户预设 + 设置)

每个产物都会带上:绝对路径、res:// 路径(落在 Godot 项目内时)、尺寸、seed、模型、后端、抠图/放大方法。

2. 面板怎么用

侧边栏放不下节点画布,所以面板按可用宽度决定布局:宽 ≥700px 时三栏(图库 / ComfyUI 画布 / 参数),窄了上下堆叠。

画布在侧边栏里就能用,不需要浮窗。 dsh-better-sidebar 从 v0.19.0 起删掉了浮窗功能 (floatWindows 被移除,右列交给 DSH 原生侧边栏),本机装的是 0.24.1,于是:

  • 面板直接显示完整工作台(含内嵌画布)。插件的 readPlacement 在侧边栏报不出 floats 数组时 返回 known: false,而它刻意不在未知时收窄——"showing less by accident is worse than showing more",宁可多显示;
  • 因为不再有浮窗,面板头部不会再出现「如何浮窗?」;那个按钮只在支持浮窗的侧边栏上出现。 判断依据是能力而不是版本号:侧边栏一旦重新报出 floats,浮窗与「⛶ 全屏」按钮会自己回来;
  • 画布分区默认折叠,点标题展开。载入被画布拒绝时它会自动展开,标题上同时标注 ⚠ 画布拒绝了最近 N 次载入——ComfyUI 自己的报错是弹在这个分区里面的,折叠着就完全看不见。

历史:0.18.0 上浮窗要靠右键 tab →「移动到自由窗口」,而「把 tab 拖到主会话区」那个手势因为中心列 锚点是旧版槽位名(#root [data-slot="conversation"],当前 DSH 渲染的是 main.conversation)而整个 不触发——与本插件无关,也不是拖拽动作本身坏了。这两条如今只对旧版侧边栏有意义。

面板里还能:选工作流 → 在画布上编辑或直接「运行工作流」→ 产物照常落进 Godot 项目并进图库。 「发送到对话」把当前提示词与参数(或选中的图)送回输入框。

表单只管 SD WebUI:ComfyUI 的一切都交给画布,面板不替它重画一套参数表单(那只会与 ComfyUI 自己的界面不一致)。所以参数区永远是 SD WebUI 的字段,ComfyUI 走「工作流」这一栏。

3. 画布桥接:对话直接改 ComfyUI 画布

为什么需要它,以及为什么它走 postMessage 而不是 HTTP

画布是 iframe,里面是另一个源(127.0.0.1:8188)的页面。浏览器不允许面板把手伸进 iframe 去点节点, 所以唯一的通路是:往 ComfyUI 里放一个属于它自己的 web 扩展。

最直觉的做法——让扩展在 iframe 里 fetch 回 DSH 的 HTTP 路由——在这台机器上不成立,原因值得记下来, 免得以后有人又"优化"回去:

DSH Desktop 会给 Electron 渲染进程的流量附加一枚每代独有的能力头 (x-dsh-desktop-renderer),其 Web 服务器会 403 掉任何没带这枚头的请求; 而这枚头只在 details.frame.origin === 载体 origin 时附加 (见 requestComesFromRendererOrigin)。画布 iframe 的 frame origin 是 ComfyUI 的源, 所以头不会被附加,它发给 DSH 的每一个请求都会在路由之前就被拒(403)。 这是刻意的:能力不得离开本地载体。CORS 头救不了它——请求根本没走到路由; WebSocket 升级受同一条规则约束。 (dsh-desktop.mode: advanced + openBrowser: false 下,普通浏览器访问本身就是关闭的, 任何没有渲染器头的请求都会被拒。)

postMessage 才是能用的那条路:父窗口就是 DSH 页面本身,它天生持有那枚能力头。 于是面板充当中继——把指令递下去,把画布的状态捎上来:

 对话 / 代理 ──image_workflow_open(to_canvas)──► 宿主画布队列
 美术工坊面板 ──POST /gig/canvas/load──────────►│
                                                ▼
              POST /gig/ui/poll  ◄── 面板(DSH 源,同源,带渲染器头)
              { canvasMessages: [ {__gig, type:'canvas.load', seq, graph} ] }
                     │
                     └──iframe.contentWindow.postMessage(msg, 'http://127.0.0.1:8188')──► 扩展
                                                                                           │
                                    POST /gig/canvas/inbound  ◄── 面板转交 ◄── postMessage ◄┘

两端的门都是硬的:

方向 校验
面板 → 扩展 event.origin === __DSH_ORIGIN__ 且 data.__gig === __TOKEN__
扩展 → 面板 event.source === iframe.contentWindow(并预筛信封形状)
面板 → 宿主 面板把信封原样转交;宿主自己验 __gig 令牌

令牌是每次安装烤进副本的,重新同步时复用;所以另一个 ComfyUI 安装里的旧副本既不能被本宿主驱动, 它的上报也不会被本宿主采信。面板从不解读令牌,它只是个搬运工。

代价(已知且明确):通道经过面板,所以面板必须开着。面板关着时画布指令会留在队列里(10 分钟), 下次打开再投递;用系统浏览器「外部打开」的 ComfyUI 也不在这个通道里(那边没有父窗口)。

安装 / 卸载(一键,且可逆)

面板「画布桥接(对话 ↔ ComfyUI)」卡片:

  • 安装到 ComfyUI —— 把 comfyui-extension/ 的内容复制到 <comfyRoot>/ComfyUI/custom_nodes/godot-imagegen-bridge/,并把当前页面 origin 与一枚新令牌烤进副本。
  • 卸载扩展 —— 只删除清单里登记过的文件,并且只回收它自己创建的目录;不是它写的东西一律不碰。
  • 安装后 必须重启 ComfyUI(WEB_DIRECTORY 是启动时读的),卡片会一直提醒这一点。

写入前一定先备份到 <dataRoot>/backups/<时间戳>/comfyui/,并附一份 BEFORE.json: 里面写清这次是不是"本来就没有东西"(existed: false)还是"备份了 N 个既有文件"。备份不会被卸载删掉—— 它是审计线索,不是临时文件。

清单在 <dataRoot>/installed-files.json:令牌、origin、目标目录、每一个文件、每一个"我创建的目录"。

同步时机是有讲究的:ComfyUI 只在启动时读一次 WEB_DIRECTORY,所以 「启动后端」会先把已安装的桥接刷新到当前源码,再拉起进程——反过来做,这次启动加载的 仍然是旧副本,只能等下一次重启。而且只有内容真的不一致时才写:否则每次启动都会去动你的 ComfyUI 安装目录、还多留一份备份,毫无意义。

心跳:扩展只在两种时刻说话——启动时打个招呼,以及被问到的时候。所以宿主在每次面板轮询 里都塞一条 canvas.ping(带令牌、不带 seq,因此不进指令队列、不需要确认),面板把它转进 iframe, 扩展回一份画布摘要,面板再捎回宿主。没有这条心跳,"画布已连接"会在连上后十几秒变成"没连接"—— 报告过期了,而画布其实好好的。

两个时钟是故意分开的:面板在线判定用 15 秒,画布用 60 秒。因为轮询是面板和画布共用的同一次 往返,而窗口最小化/被遮挡时 Chromium 会把定时器节流到大约每分钟一次——用 15 秒判画布,就会出现 "人切了个窗口,画布就显示掉线"。卡片上还会写明"已经 N 秒没应声",并提示多半是「画布」小节没打开 (面板窄时它默认折叠),而不是让你去重启一个本来正常的后端。

还有一个实测发现的坑:ComfyUI 启动时 import 这个包,Python 会在包里写下 __pycache__/__init__.cpython-3xx.pyc。它也算残留,所以卸载会一并删掉——但只删文件名词干 与"本次安装过的文件"对得上的 .pyc,别人放进去的 .pyc 会保留并如实报告。

对话里怎么用

image_workshop  action="bridge-status"     # 装没装?画布应声没?画布上有几个节点?
image_workshop  action="install-bridge"    # 装(会先备份;需要重启 ComfyUI)
image_workshop  action="uninstall-bridge"  # 卸(只删自己登记过的,零残留)
image_workshop  action="read-canvas"       # 画布正在编辑什么、缺哪些节点类型
image_workflow  action="open"  id="item-icon"                 # 载入面板 + 推到画布
image_workflow  action="open"  id="item-icon" to_canvas=false  # 只给面板,不动画布
image_workflow  action="patch" id="item-icon" edits=[…] to_canvas=true   # 改完立刻反映到画布

两个队列是分开的:/gig/ui/poll 给面板,画布指令搭同一次往返;面板没开不会让画布收到, 画布没连上也不会让面板看不到。画布载入失败(loadGraphData 抛错)会被记下来, bridge-status 和 GET /gig/extension 都能看到,而不是永远默默重试。

桥接没装时,open 不会偷偷把图排进画布队列——它会把 canvas:false 和原因一起返回。 否则那张图会在几分钟后你打开画布时凭空冒出来,而你早已忘了自己说过什么。

4. 后端:本机实测的能力矩阵

插件自动探测、可手动指定,auto 会优先用 defaultBackend 并自动回落。

表单里「模型」留空 ≠ 不指定模型:A1111 会用它当前已加载的那个 checkpoint。 本机实测同一个 512²/12 步请求,落在 SDXL 上 231 秒、落在 SD1.5 上约 5 秒—— 差 40 倍。面板的「模型」下拉因此会把当前加载的那个名字直接写出来 (— 后端当前:waiIllustriousSDXL_v150.safetensors —),免得这个选择是隐形的。 草稿阶段建议显式选 anything-v5。

能力 SD WebUI(秋叶 v4.10,:7860) ComfyUI(秋叶 v2,:8188)
出图 / 图生图 ✅ waiIllustriousSDXL_v150(SDXL/Illustrious)、anything-v5(SD1.5) ✅ 同 3 个 checkpoint(经 extra_model_paths 共享 SD WebUI 目录)
ControlNet ✅ 15 个模型(openpose / lineart_anime / depth / canny / normalbae / seg / scribble / softedge / tile …) ✅ 权重可见(同 15 个)——但本插件的 ComfyUI 通道尚未实现 ControlNet
放大 ✅ 16 个可选(R-ESRGAN 4x+ Anime6B、4x-AnimeSharp、BSRGAN、ESRGAN_4x、SwinIR_4x …) ✅ 权重可见(6 个 .pth)——同上,未实现
反推提示词 ✅ WD14 tagger 扩展(16 个模型,默认 wd14-convnextv2-v2;内置 /sdapi/v1/interrogators 在本机是 404) ⚠️ 节点在、ONNX 权重缺失
人脸修复 ✅ GFPGAN + CodeFormer —
生成式透明 ❌ ⚠️ layerdiffuse 节点已装但 models/layerdiffuse 权重未下载
面板一键启动 ✅ python launch.py --api … ✅ python -S main.py …

关键事实:秋叶 ComfyUI 的 extra_model_paths.yaml 把 a111.base_path 指向了 SD WebUI 安装目录, 所以 ComfyUI 能看到同一批 checkpoint / ControlNet / 放大权重(image_backend_status 会实测报告数量)。 但本插件的 ComfyUI 通道目前只实现文生图与图生图——ControlNet、放大、反推请走 SD WebUI, 工具返回值里也会这样提示。

本机实测(test-imagegen-live.mjs / test-imagegen-live-comfy.mjs,RTX 5060 Laptop / 8GB 显存):

任务 耗时
SD1.5 512²/14 步 · SD WebUI 冷启动 ≈24s(含 7GB 换模型),模型已就位 ≈7s
SD1.5 512²/12 步 · ComfyUI ≈8.5s;图生图 ≈2.4s
rembg(u2net) 抠图 ≈6s
R-ESRGAN 4x+ Anime6B 放大 ×2 ≈1.2s
ControlNet(lineart_anime) 512²/12 步 ≈4s
SDXL 768² ≈60–77 秒/步 —— 18 步实测超过 15 分钟(GPU 99%、显存 7.7/8.1GB 打满)

所以默认 requestTimeoutMs 定为 30 分钟。草稿阶段优先 ComfyUI + SD1.5(最快)或 SD WebUI + SD1.5, 尺寸 ≤640×640,定稿后再 image_upscale。image_generate 在检测到 SDXL + ≥768² 时会把耗时提示放进返回值。

所以默认后端是 SD WebUI;ComfyUI 作为 SD1.5 / 自定义工作流的第二后端。

后处理(抠图 / 放大 / 无缝平铺)走秋叶自带的 Python,它已经装了 Pillow、numpy、OpenCV、scipy、rembg、onnxruntime,所以插件不额外安装任何依赖。

5. 依赖与鸣谢

5.1 依赖

宿主(必需)

依赖 形态 说明
DeepSeek Harness 宿主 插件运行的环境(dsh.bundle + dsh.client 一个包两半)
tools 服务 宿主服务 export const inject = ['tools', 'storage'] 之一:image_* 工具的注册面
storage 服务(@deepseek-ai/storage-json) 宿主服务 图库记录 / 用户预设 / 设置的 KV 单元,默认名 godot_imagegen(unitName 可改)
@deepseek-ai/cordis ^4.0.1 peerDependency 只是包关系:宿主半边不 import 它,而是在本地声明所需的结构类型(见 godot-imagegen.ts 的 Context),因此不产生运行时耦合

宿主能力(可选,有则增强)

能力 缺了会怎样
subprocess 服务 优先用它启动后端与跑后处理;取不到时回落到 node:child_process,功能不变
webServer(dsh-host-webserver) /gig 路由经 ctx.inject(['webServer']) 条件注册;没有它时面板与宿主之间的同源 HTTP 通道不存在
dsh-better-sidebar 「美术工坊」面板的挂载点。没有它时 image_* 工具与 /gig 路由照常工作,只是看不到侧边栏面板。客户端注册走内层 ctx.inject(['betterSidebar']),所以缺宿主时不会把客户端 fiber 卡成 pending、也不会被启动健康门判成渲染器启动失败

外部运行时(不是 npm 依赖;插件不安装、不升级、不卸载它们)

依赖 默认地址 用途
SD WebUI(秋叶整合包) 127.0.0.1:7860 主后端:文生图、图生图、局部重绘、batch 变体、放大、反推
ComfyUI(秋叶整合包) 127.0.0.1:8188 第二后端:工作流库与内嵌画布
上面两套自带的 Python — 后处理(抠图 / 放大 / 无缝平铺)走它已装的 Pillow、numpy、OpenCV、scipy、rembg、onnxruntime。所以本插件自身不安装任何 Python 包

首次使用某项功能时才下载

  • rembg 的 u2net 权重(约 176 MB)——image_cutout
  • WD14 tagger 的 ONNX 模型——image_interrogate(可能数百 MB、首次耗时数分钟;未装 WD14 时先退内置 deepdanbooru)
  • ControlNet 模型——只有用到 control_type 时才需要
  • 出图 checkpoint(如 anything-v5、waiIllustriousSDXL_v150)与放大权重(RealESRGAN / SwinIR / AnimeSharp)由你自己的后端提供,插件不附带也不下载

随插件分发

  • ComfyUI 画布桥接 web 扩展(源码在 comfyui-extension/)。安装时把它拷进 <comfyRoot>/ComfyUI/custom_nodes/godot-imagegen-bridge/——这是插件唯一的写入点,装/卸都带清单与时间戳备份,见第 10 节。

可选配对(不属于本仓库)

  • game-art 技能:游戏美术提示词手册与分品类尺寸/透明规范。插件不依赖它,只是提示词写法与它对齐。

5.2 鸣谢

这个插件能成立,靠的是一整条上游链路。感谢:

  • ComfyUI(comfyanonymous)——工作流引擎,以及被内嵌进面板的那个真实画布;
  • Stable Diffusion WebUI(AUTOMATIC1111)——主后端与 /sdapi/v1/* 接口面;
  • 秋叶整合包(绘世启动器)——把上述两套后端在 Windows 上装成开箱可用,并自带后处理所需的 Python 环境;本插件的默认路径、端口与启动方式都是按它设计的;
  • rembg(danielgatis)与 U²-Net(Xuebin Qin)——抠图;
  • Real-ESRGAN(Xintao Wang)、SwinIR(Jingyun Liang)与 4x-AnimeSharp——放大权重;
  • WD14 tagger 扩展(picobyte)、DeepDanbooru(Kichang Kim)与 SmilingWolf 的 WD v1.4 系列模型——反推提示词;
  • ControlNet(Lvmin Zhang)——保姿势 / 保构图;
  • dsh-better-sidebar——「美术工坊」面板赖以挂载的侧边栏宿主。

也感谢各家 checkpoint 与放大权重的作者:插件提供的是接口,画出来什么样是他们的功劳。

本插件自身以 MIT 分发(见 LICENSE)。上面每一项都有自己的许可证,它们的条款不因被本插件调用而改变——尤其是各家 checkpoint / 权重,商用与再分发前请先读它们各自的授权。

6. 安装与挂载

插件本体就是一个独立仓库(也就是你正在看的这个),通过与 godot-asset-planner 完全相同的 bundle 通道挂载。

推荐:走官方 CLI。 包内的 cordis.patch.yml 就是 dsh.bundle 层,CLI 装包时会据此把 godot-imagegen 追加进 dsh.profile.bundles,不需要手工改 profile:

dsh plugin --profile <profile> add godot-imagegen

手工挂载(本地开发 / 直接指向本仓库时用):

  1. Junction(把包放进 profile 的 node_modules,客户端半端才能被 client-modules 扫描到)

    New-Item -ItemType Junction `
      -Path   "$env:USERPROFILE\.dsh\profiles\desktop\node_modules\godot-imagegen" `
      -Target "<本仓库的绝对路径>"
    
  2. profile 的 package.json:dependencies 加 "godot-imagegen": "link:<本仓库的绝对路径>", dsh.profile.bundles 数组追加 "godot-imagegen"。包自身的 cordis.patch.yml 会插入宿主插件行。

    profiles/desktop(桌面应用)与 profiles/web(dsh web)都需要按上述方式挂载。

  3. 重启 DSH Desktop:新增 bundle 行在启动时合并,重启前 /gig 路由与 image_* 工具都不存在。 之后纯客户端改动(lib/client.js)刷新页面即可;宿主改动(godot-imagegen.ts)需再次重启。

安装后:

  • 工具出现在会话的工具目录里(所有 profile 下的会话共享);
  • 「美术工坊」出现在 better-sidebar(需要 dsh-better-sidebar,desktop / web profile 都有);
  • 在面板「设置」里填 Godot 项目根目录,之后产物才会带 res:// 路径。

7. 配置项

都写在 profile 的 cordis.patch.yml 里,作为 godot-imagegen 行的 config(全部可选,都有可用默认值):

- id: godot-imagegen
  config:
    godotProject: D:/my-game          # 产物落 <root>/assets/generated 并获得 res:// 路径
    # outDir: D:/art-out              # 显式覆盖输出根目录(优先于 godotProject)
    # unitName: godot_imagegen        # 图库 KV 单元名
    # sdwebuiRoot: <秋叶根目录>/sd-webui-aki-v4.10
    # sdwebuiPython: <秋叶根目录>/sd-webui-aki-v4.10/python/python.exe
    # sdwebuiPort: 7860
    # comfyRoot: <秋叶根目录>/ComfyUI-aki-v2
    # comfyPython: <秋叶根目录>/ComfyUI-aki-v2/python/python.exe
    # comfyPort: 8188
    # postPython: 后处理解释器(默认用 ComfyUI 的那个)
    # defaultBackend: sdwebui | comfyui
    # sdwebuiModel / comfyModel: 默认 checkpoint 文件名
    # dataRoot: <DSH_HOME>/godot-imagegen   # 工作流库 / 布局缓存 / 备份 / 安装清单
    # extensionSource: <本仓库>/comfyui-extension   # 画布扩展源码目录
    # requestTimeoutMs: 1800000     # 单次后端操作超时(默认 30 分钟;SDXL 长任务需要)
    # startTimeoutMs: 360000        # 「启动后端」等待就绪的上限

godotProject 也可以直接在面板「设置」里填,会存进插件自己的 storage(跨会话保留),不必改配置。 extensionSource 默认取本模块旁边的 comfyui-extension/(按 import.meta.url 解析),所以仓库搬家、 或者经 profile junction 加载,都不需要改这一项。

8. 工具速查

image_generate — 文生图

必填 prompt。关键参数:category(character / item / ui / tile / background / texture / concept)、 width height steps cfg_scale sampler seed batch model backend、 transparent + transparent_method(auto / rembg / chroma / generative)、seamless、 control_image + control_type + control_weight、project_id out_dir。

category 决定落盘子目录、默认尺寸与默认是否透明:

category 默认尺寸 默认透明 说明
character 832×1216 ✅ 角色立绘 / 精灵图
item 1024×1024 ✅ 道具 / 技能 / 状态图标
ui 1024×1024 ✅ 按钮 / 边框 / 九宫格
tile 768×768 ❌ 瓦片(默认做无缝平铺后处理)
background 1344×768 ❌ 背景 / 视差层
texture 1024×1024 ❌ 3D / PBR 贴图(默认做无缝平铺)
concept 1024×1024 ❌ 概念图

其余七个

  • image_edit — 图生图 / 局部重绘(image + mask + denoise),支持 ControlNet 保姿势
  • image_variants — 一次出多个变体(count),SD WebUI 走单次 batch
  • image_upscale — method: auto|esrgan|lanczos,优先 SD WebUI 的动漫向 RealESRGAN
  • image_cutout — method: auto|rembg|chroma,输出带 alpha 的 PNG(首次 rembg 会下载 ~176 MB 的 u2net 模型)
  • image_interrogate — 反推提示词:先试内置 deepdanbooru,404 时自动改用 WD14 tagger 扩展 (首次调用会下载所选的 ONNX 模型,可能数分钟),用来对齐已有美术风格
  • image_gallery — list / delete / clear,按项目与品类过滤
  • image_backend_status — status / models / start / stop

image 参数接受三种形式:绝对路径、res:// 路径、图库 id(如 img_1a2b3c4d5e6f)。

image_workflow — 把 ComfyUI 工作流当成可编辑对象

action:list / read / create / patch / save / to-api / to-ui / run / open / delete

关键是 patch 按参数名改,而不是按下标:

{ "action": "patch", "id": "sd15-basic",
  "edits": [
    { "classType": "CLIPTextEncode", "title": "正向提示词", "set": { "text": "a red potion, game icon" } },
    { "classType": "KSampler", "set": { "steps": 9, "seed": 1234 } }
  ] }

为什么这点很重要:UI 格式的 widgets_values 是位置数组,而 ComfyUI 给任何带 control_after_generate 的 widget(也就是每个 seed)多留一个槽位。按下标改会让 seed 之后的每个字段整体错位——不报错,只是安静地出一张参数全乱的图。本插件的布局是从 /object_info 推导的,并且会缓存,所以后端关着也能继续编辑。

run 会把工作流跑出来的图按普通生成物一样归档:落进 <项目>/assets/generated/<品类>/、 拿到 res:// 路径、进图库。工作流没有 SaveImage/PreviewImage 节点时会明确报错,而不是空转到超时。

open 会同时把整份图推给内嵌画布(走桥接扩展);to_canvas: false 可以只给面板。

image_workshop — 让代理直接操作面板与画布

action:fill-form / focus / select-image / toast / read-state / status

  • 画布:bridge-status / install-bridge / uninstall-bridge / read-canvas

面板在它已有的 1.5s 轮询里一次往返完成两件事:上报自己在看什么(含未保存的草稿),取走代理排队的指令。 面板没打开时指令会排队(保留 10 分钟),工具会明确告诉你"已排队、未送达",而不是假装成功。

画布四个动作用的是另一条通道(桥接扩展的轮询),状态与面板互不影响。

9. 与 godot-asset-planner 的协作

不共享 storage。 godot_asset_planner 这个 KV 单元每个后端只允许一个活动句柄,归 godot-asset-planner 所有, 所以本插件用自己的 godot_imagegen 单元,二者互不干扰。

两条联动路径:

  1. 对话里:image_generate 的返回值里就带着 res:// 路径,直接 godot_register_asset(asset_name, asset_type="image", file_path="res://assets/generated/...")。
  2. 面板里:灯箱的「一键注册」走同源 HTTP 调 godot-asset-planner 的 POST /gap/assets ({ assetName, assetType:"image", filePath, projectId, linkedGoal? }),不碰它的存储实现。 面板顶部会显示从 /gap/state 读到的当前 planner 项目作为归属。

10. 安全边界

  • 只接管、不重复启动:后端已在运行时 action=start 直接复用,绝不新起一个抢端口。
  • 绝不终止非本插件启动的进程:action=stop 对你自己用绘世启动器拉起的实例会明确拒绝并说明原因。
  • 后端启动是面板上点按钮才发生,不会在你不知情时占显存。
  • 插件 dispose 时会 abort 在飞任务并关闭 KV 单元,避免热重载后 unit is already open。
  • 唯一的写入点在画布桥接扩展,而且它守四条规矩:
    1. 写入对象只有 <comfyRoot>/ComfyUI/custom_nodes/godot-imagegen-bridge/,绝不碰别处;
    2. 写之前先做带 BEFORE.json 的时间戳备份,备份永不自动删除;
    3. 每个写过的文件都进清单,卸载只删清单里的路径,越出目标目录的记录会被拒绝执行并报错;
    4. 只回收它自己创建的目录(安装前就存在的目录不记录、不删)。 目录里出现清单之外的文件时,卸载会报告残留而不是假装干净——那些不是它写的,它不删。
  • 画布通道不开任何网络入口:扩展不发 HTTP、不开 socket,只用 postMessage 与父窗口(也就是本面板)通信, 并且双向都要过 origin + 每次安装的令牌。宿主这一侧没有任何跨域路由可以被打(/gig 全部同源)。
  • 工作流库、节点布局缓存、备份目录都在 $DSH_HOME/godot-imagegen/ 下,不在你的 ComfyUI 安装里。
  • 不自动安装:桥接扩展的首次安装必须是显式动作(面板按钮或 install-bridge); 只有"已经装过"时,「启动后端」才会在拉起进程之前重新同步一次,且只在内容不一致时才写。

11. 验证

cd <本仓库根目录>
npm test                              # = 下面五条"离线"套件;每套自己打印 N passed / M failed
node tests/test-workflow.mjs          # 工作流层,含 10 个真实 ComfyUI 导出文件
node tests/test-workflow-library.mjs  # 工作流库、6 套模板、布局缓存
node tests/test-imagegen.mjs          # 10 个工具 + REST + 真实 Python 后处理 + 面板那条路与进度行
node tests/test-extension.mjs         # 桥接:备份/清单/卸载/令牌/中继/心跳/零残留/真跑扩展/启动同步
node tests/test-canvas-e2e.mjs        # 端到端——用**本机真实安装的那个 .js** 走真实宿主路由,
                                      #   跑通心跳/推送/拒绝/回执,并复现"16 秒后是否掉线";只读
node tests/test-render-imagegen.mjs   # 客户端面板渲染,含入口 / 工作台 / 全屏三种布局与浮窗几何补丁,
                                      #   真的跑一遍画布中继与心跳,
                                      #   并断言 启动/停止 真的挂在面板上、点下去真的打到宿主
node tests/test-imagegen-live.mjs          # 真实 SD WebUI 端到端(后端没起时自动跳过)——
                                           #   含面板「生成」按钮真正用的 POST /gig/generate 那条路
node tests/test-imagegen-live-comfy.mjs    # 真实 ComfyUI 端到端,含真跑内置模板(没起时跳过)
node tests/test-extension-live.mjs         # 已装好时:对真实安装的**副本**验证卸载零残留,真实目录不动;
                                           #   没装时走写路径:真装真卸、全树扫描无残片
node tests/test-extension-live.mjs --serve # 先安装、再自己启动 ComfyUI,验证它真的把扩展 serve 出来
                                           #   (要 ComfyUI 先关着)
node --check lib/client.js
# 技能 frontmatter / 行尾校验(可选:需要本机另有 godot-asset-planner 仓库与 game-art 技能目录)
# node <godot-asset-planner>/check-skill-frontmatter.mjs "<技能目录>"

这套件不引用固定的断言条数:每个套件自己打印 N passed, M failed,以实际输出为准。 条数会随两件事变化,都是设计而不是漏测:

  • 后端是否在跑:test-workflow.mjs 与 test-workflow-library.mjs 在后端应答时会顺手拿 真实的 /object_info 再验一遍「真实工作流的 UI→API 转换」与「模板能否在本机跑」;后端没起时 它们打印一行 NOTE ... skipped,而不是假装通过。
  • 本机装了什么模型:那几条实时检查只能描述跑它的那台机器。缺某个 checkpoint 时它打印一行 NOTE,点名缺哪个模型、影响哪些模板,而不是让整套变红——所以一台只装了 SDXL 的机器,跑出来的 条数会比全装齐的机器少。

这里此前引用的是一组实测总数(按套件逐条列出)。它们随开发不断漂移,早已不再准确, 也每加一条断言就要重修一次——所以改成了不引用条数,只说明变化的原因。

画布扩展的测试分层(每层都跑在能跑到的最真实的一层):

层 谁在测 覆盖
管理器 test-extension.mjs 备份内容、清单、令牌复用、越界拒删、既有目录不回收、零残留(含 __pycache__)、启动前同步、在线窗口
宿主路由 test-extension.mjs 心跳信封、指令队列与 ack、失败上报、卸载后不再 ping、409 拒绝无去处的推送
扩展本体 test-extension.mjs 把装进 ComfyUI 的那个 .js 丢进 node:vm:错 origin/错令牌/无令牌/垃圾载荷都不载入,正确信封真的调到 app.loadGraphData,拒绝时回 ok:false
面板中继 test-render-imagegen.mjs 真的跑 effect:信封以 postMessage 投进 iframe(targetOrigin 是 ComfyUI 源)、回包搭下一次轮询上行、心跳在空转 tick 上照样发出、冒充窗口的消息被丢弃
端到端 test-canvas-e2e.mjs 真实安装的 .js + 真实宿主路由 + 真实工作流库:心跳→在线、推送→loadGraphData→回执→清队列、拒绝→错误可见、16 秒不掉线;只读(断言清单与扩展的 mtime 未变)
真机写路径 test-extension-live.mjs 真的写进你真实的 <comfyRoot> 再真的卸干净、全树扫描无残片。已装好时自动改为对副本验证:复制真实安装(含 __pycache__)到临时目录、改写清单路径后卸载,并断言真实目录与清单分毫未动
  • 离线套件用两个真实 HTTP 服务冒充 A1111 与 ComfyUI(覆盖 /sdapi/v1/*、/controlnet/model_list、 /tagger/v1/*、/prompt、/history、/view),并用真实的秋叶 Python 跑抠图与放大, 因此断言的是真实行为而不是 mock 行为;同时覆盖重启持久化与热重载时的 KV 单元释放。
  • 扩展套件在一个临时假 ComfyUI 目录上把桥接的每条承诺都跑一遍:备份内容、清单内容、 令牌复用、越界路径拒删、既有目录不回收、非本插件文件保留、卸载后零残留(含 ComfyUI 导入时 生成的 __pycache__),以及中继的令牌校验、指令投递与 ack、失败上报,并且断言 不存在任何跨域 HTTP 入口(扩展只会用 postMessage)。 它还把真正装进 ComfyUI 的那个 .js 放进一个 node:vm 里,配一个假的 window 与假的 ComfyUI app 跑起来:错 origin / 错令牌 / 无令牌 / 垃圾载荷都不得触发载入,正确的信封必须真的调到 app.loadGraphData 并回一个带 seq 的结果,画布拒绝载入时必须回 ok:false 与原因。
  • 客户端套件用最小 React 替身递归执行每个组件函数(本机磁盘上没有 react/react-dom, DSH 的客户端运行时在 app.asar 内),因此未定义标识符、错误的解构、坏的 createElement 调用都会暴露。 它还会真的跑一遍 effect:装一个假 fetch 与假定时器,让面板的空转轮询与窗口消息监听真正执行, 于是中继的两个方向都被验证——宿主排队的信封确实以 postMessage 投进 iframe(且 targetOrigin 是 ComfyUI 的源、绝不是 *),而画布的回包会搭下一次轮询送回宿主,冒充窗口发来的消息被丢弃。
  • 实时套件驱动真正安装的 SD WebUI / ComfyUI:真实出图、真实 rembg 抠图、真实 R-ESRGAN 放大、 真实 ControlNet 引导,并断言产物真的落在 Godot 项目的 assets/generated/<品类>/ 且带 res:// 路径; 桥接的实时套件则真的写进你真实的 <comfyRoot>,再真的卸干净并全树扫描确认无残片。 --serve 只有在"先安装、再由它启动 ComfyUI"的顺序下才能验证「ComfyUI 把扩展 serve 出来」—— 它会自己启动、自己关掉;发现 ComfyUI 已经开着时跳过这两项并说明原因,而不是假装失败或假装通过。

已实测的关键前提(用浏览器实际会发的请求头打真实 ComfyUI):

请求 结果 含义
iframe 导航到 :8188/(sec-fetch-site: cross-site + DSH referer) 200,无 X-Frame-Options、无 CSP 画布可以被内嵌
从 DSH 页取 :8188/extensions/...(无 Origin) 200(装好后)/ 404(没装) 扩展脚本能在页面里加载
从 DSH 页 fetch(':8188/...')(带 Origin: DSH) 403(origin_only_middleware) 面板不能直接跟 ComfyUI 说话——所以工作流层必须走宿主
画布内部同源 API 调用 200 画布自身功能正常

「拖到主会话区」那个手势(已无意义,留作记录)

早期文档在这里给过一个补丁:把查找中心列的 JS 字符串从"只认旧槽位名"改成"两个都认", 以换回「把 tab 拖到主会话区」的浮窗手势。这个补丁现在没有意义了,不要打——

  • 两个 profile 早已不是那时钉的 0.18.0 / 0.16.1,而是 0.24.1;
  • 更根本的是:dsh-better-sidebar 从 v0.19.0 起删掉了浮窗功能本身, 所以那个手势即使修好也无处可去。

画布现在就在侧边栏里用(见第 2 节)。这一节只用来解释为什么旧文档里会出现那段操作。

12. 已知限制

  • 没有浮窗:dsh-better-sidebar 从 v0.19.0 起删掉了 floatWindows,所以内嵌画布只能在侧边栏里用 (见第 2 节)。插件按能力判断而不是版本号:侧边栏若重新报出 floats,浮窗与全屏按钮会自己回来。 详见第 2 节。
  • 后端调用不使用全局 fetch:undici 有 300s 的 headersTimeout/bodyTimeout 且无法在无 undici 包时调高, 真实 SDXL 出图(7GB 换模型 + 采样)会超过它并抛 UND_ERR_HEADERS_TIMEOUT。插件改用 node:http, 超时只受自身的 requestTimeoutMs 控制。
  • ComfyUI 通道只实现文生图 / 图生图。它能看到 SD WebUI 共享来的 ControlNet / 放大权重, 但通过工作流 JSON 驱动 ControlNet、UltimateSDUpscale、WD14 尚未接线;这些能力请走 SD WebUI。
  • transparent_method: generative(ComfyUI layerdiffuse 直出)已实现工作流接线, 但需要先把 layerdiffuse 权重放进 ComfyUI/models/layerdiffuse(extra_model_paths 没有映射它)。
  • image_cutout 的 rembg 首次运行会下载约 176 MB 的 u2net 模型(本机已缓存则瞬间完成)。
  • 桥接扩展有 .js 改动就必须重启 ComfyUI(WEB_DIRECTORY 只在启动时读),不是热重载的。
  • 画布通道经过面板(postMessage),所以面板必须开着;用系统浏览器「外部打开」的 ComfyUI 不在通道里。这不是偷懒:DSH 会给"frame origin 不是载体 origin"的请求一律 403, iframe 自己发不出任何请求,父窗口是唯一通路。
  • 面板不做九宫格切片、图集打包、调色板量化等"后期"加工——那些更适合放在 Godot 导入侧或技能里。
  • RTX 5060 Laptop 上 SDXL 出图偏慢,建议先用 SD1.5 试参数再放大。
—/ 5

No ratings yet

Verified DSH bundle

Commit 51db5aef52c6

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