把 Danbooru-Tag-Selector(纯前端单文件网页工具)以 iframe 方案嵌入 ComfyUI 的插件,并让「ComfyUI 内的浮窗」与「双击打开的本地网页」共享同一份数据账本。
插件完全自包含:页面、数据集、账本全部在本目录内,运行时不依赖任何外部路径。
插件在 ComfyUI 本地服务器上注册三个网址:
| 网址 | 内容 |
|---|---|
/tag_selector/page |
返回本目录的 Danbooru-Tag-Selector.html(上游页面快照 + 桥接脚本) |
/tag_selector/dataset |
按需返回内置数据集 tags_with_groups.csv(首次读取后常驻内存) |
/tag_selector/data |
共享账本 data/dts_data.json 的读 / 写接口 |
ComfyUI 浮窗 ── HTTP 路由 ──┐
├──→ data/dts_data.json(唯一真相源)
双击离线页面 ── File System Access ──┘
- 浮窗入口:节点上点按钮或双击节点,弹出可拖拽 / 可缩放 / 失焦不关闭的真浮窗
- 本地入口:双击本目录的
Danbooru-Tag-Selector.html,通过 File System Access API 直接读写账本文件 - 任一处改动都会在半秒内写入账本,另一边打开时自动灌入;并发写入采用最后写入获胜
- 首次连接账本时若本地有旧收藏而账本为空,自动整柜搬入
因为账本是硬盘上的普通 JSON 文件:备份 = 复制文件,迁移 = 带着它走,换浏览器 / 清缓存不再丢数据。
ComfyUI-Tag-Selector/
├── __init__.py # 节点定义 + 路由挂载(薄壳)
├── core.py # 共享逻辑:读页面 / 数据集 / 账本原子读写
├── sync_upstream.py # 上游同步命令(见下)
├── Danbooru-Tag-Selector.html # 页面资产:上游快照 + 桥接脚本(双击入口;生成物,不入库)
├── backup/
│ └── Danbooru-Tag-Selector.prev.html # 被替换掉的上一个版本(自动备份,不入库)
├── data/
│ └── dts_data.json # 收藏 · 历史 · 预设的唯一账本(个人数据,不入库)
└── web/
├── bridge_inject.js # 桥接脚本真相源(sync 时注入进页面资产)
└── tag_selector_bridge.js # 宿主侧扩展:按钮 · 浮窗 · postMessage 双向同步
插件不再等到文件缺失才下载:每次启动都会比对版本,本地页面不是最新就自动更新,更新前把上一个版本备份到 backup/Danbooru-Tag-Selector.prev.html;本地版本比上游新时不会被降级覆盖。首次完全没有页面时会同步下载,之后这项检查在后台完成,不阻塞 ComfyUI 启动。版本号取自页面里的 <meta name="dts-version">。
python sync_upstream.py # 检查并按需更新(版本比较 + 自动备份)
python sync_upstream.py --force # 无条件重新拉取全部
python sync_upstream.py --local <上游工程目录> # 从本地上游工程重新生成
设置环境变量 DTS_SKIP_ASSET_UPDATE=1 可完全关掉启动时的更新检查(离线 / 锁定版本时用)。
数据集 CSV 不在上游仓库中,需按上游「数据集」章节自行生成后放入本目录(支持 tags_with_groups.csv 或 tags_enhanced.csv 文件名);缺失时浮窗与启动日志都会给出指引。
宿主 → 页面: dts_set_text {text} · dts_load_dataset {url} · dts_get_text
dts_visibility {visible} · dts_pause {paused}
页面 → 宿主: dts_ready · dts_text_changed {text} · dts_text {text} · dts_ledger_status {connected, mode, error} · dts_theme {theme}
节点文本与候选区文本框双向实时同步;节点文本在数据集装载完成后才应用(避免被初始化清空)。
节点 → 浮窗:节点里的文本一有改动就推到浮窗(每个停顿推送一次,不逐字符),自己写回节点时不回声。重新打开浮窗、或换到另一个节点,会立刻显示该节点当前的文本。
浮窗 → 节点:节点拿着光标时节点优先。 页面会把自己的标签解析结果回写,而 1girl, 会被解析成单个标签 1girl —— 若在用户还在打字时写回,就等于删掉刚输入的字符(输入法合成也会被打断),所以这时只挂起,等该节点交出光标后再按「谁最后改动」结算。只有浮窗当前显示的那个节点会触发挂起,在另一个节点上打字不影响已打开的浮窗。
dts_visibility / dts_pause 是休眠开关:浮窗收起、队列开始执行时由宿主告知页面,页面随即停止后台活动,恢复时补一次同步。
工具页跑在 ComfyUI 的 iframe 里,和画布共用同一个渲染主线程,所以它在后台做的事会直接变成画布卡顿。为此:
- 没有常驻轮询:账本只在真的发生改动时才同步一次,另有 30 秒一次的兜底对账
- 浮窗收起与队列执行期间完全休眠(不起定时器、不读数据)
- 服务端的账本与数据集读写不占用 ComfyUI 的事件循环(数据集约 9.8 MB,缓存后直接下发)
整个文件夹放进 ComfyUI 的 custom_nodes 目录,重启 ComfyUI。
仓库出于体积与隐私考虑不含两样东西,需要自行生成 / 放置:
| 文件 | 性质 | 如何获得 |
|---|---|---|
Danbooru-Tag-Selector.html |
生成物 | 首次启动 ComfyUI 时自动从上游仓库下载;也可手动运行 python sync_upstream.py --force 重新拉取 |
tags_with_groups.csv |
数据集 | 获取方式见上游 Danbooru-Tag-Selector 的「数据集」章节:由第三方标签数据经 data_tool/ 脚本合并生成,放入本目录即可;文件名 tags_enhanced.csv 也被识别 |
data/ |
个人账本 | 首次写入时自动创建,无需手动建 |
未生成页面前,浮窗与双击入口会提示运行 sync 命令。
- PapaParse 仍走 CDN:完全离线时上传 CSV 与内置数据集解析不可用(
web/papaparse.min.js为占位,待填入官方构建后切换为本地引用) file://打开的离线页面,其文件访问权限不跨浏览器会话持久(Chromium 对本地页面的安全设计):每次重新打开需点一次横幅按钮并在浏览器弹窗中允许。需要彻底免授权时,可通过本地 HTTP 服务访问(权限即持久)