Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40e05092d7 | ||
|
|
3f2fc7e033 |
@@ -0,0 +1,6 @@
|
||||
.github/
|
||||
preview/
|
||||
tests/
|
||||
registry/
|
||||
VERIFICATION.md
|
||||
ROLLBACK.md
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Validate plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cpu-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- uses: actions/setup-python@v7.0.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
- name: Install CPU test dependencies
|
||||
run: |
|
||||
python -m pip install torch --index-url https://download.pytorch.org/whl/cpu
|
||||
python -m pip install numpy Pillow
|
||||
- name: Run backend regression tests
|
||||
run: python -m unittest discover -s tests -v
|
||||
- name: Check frontend syntax and metadata
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import pathlib, subprocess, tomllib
|
||||
config = tomllib.loads(pathlib.Path('pyproject.toml').read_text(encoding='utf-8'))
|
||||
assert config['project']['name'] == 'adachi-inpaint-workbench'
|
||||
assert config['tool']['comfy']['DisplayName'] == 'Adachi-Inpaint-Workbench'
|
||||
for source in pathlib.Path('web').glob('*.js'):
|
||||
subprocess.run(['node', '--input-type=module', '--check'], input=source.read_bytes(), check=True)
|
||||
PY
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Publish to Comfy Registry
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.1
|
||||
- name: Verify publisher configuration
|
||||
env:
|
||||
REGISTRY_ACCESS_TOKEN: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import os, pathlib, tomllib
|
||||
config = tomllib.loads(pathlib.Path('pyproject.toml').read_text(encoding='utf-8'))
|
||||
if not config['tool']['comfy']['PublisherId'].strip():
|
||||
raise SystemExit('Set tool.comfy.PublisherId to your registered publisher ID before publishing.')
|
||||
if not os.environ.get('REGISTRY_ACCESS_TOKEN'):
|
||||
raise SystemExit('Add your Registry API key as the REGISTRY_ACCESS_TOKEN repository secret.')
|
||||
PY
|
||||
- name: Publish custom node
|
||||
uses: Comfy-Org/publish-node-action@main
|
||||
with:
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.1
|
||||
|
||||
- Rename the public node pack to **Adachi-Inpaint-Workbench**; keep existing node class IDs compatible with saved workflows.
|
||||
- Show an eraser cursor and a distinct dashed brush outline while Alt is held or the eraser tool is selected; restore the brush cursor on release.
|
||||
- Add Comfy Registry metadata, dependency declarations, a Manager registration entry, CPU CI and a manual Registry publishing workflow.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
- Move the original live editor controls and sampling controls into the WebUI Inpaint node.
|
||||
- Keep the inline node focused on the image and a small upload button.
|
||||
- Move comparison controls below the picture and remove the top toolbar band.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
- Add WebUI-style sliders, separate width/height rows and a source-size button.
|
||||
- Use result-left/original-right comparison and show the full result when the pointer leaves.
|
||||
- Route toolbar wheel events to the workflow and keep image wheel gestures separate.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Add temporary Alt erase, image comparison and result adoption.
|
||||
@@ -1,12 +1,28 @@
|
||||
# 安达工作台 / Adachi Workbench
|
||||
# Adachi-Inpaint-Workbench
|
||||
|
||||
直接在 ComfyUI 工作流节点里载入原图、涂抹蒙版、局部重绘,无需另开遮罩编辑器喵。
|
||||
|
||||

|
||||
|
||||
由 Adachi(安达)开发,MIT 开源喵。
|
||||
支持整图范围与蒙版范围重绘、Alt 临时橡皮擦、Ctrl+滚轮笔径、图像对比及一键采用重绘结果喵。
|
||||
|
||||
## ComfyUI Manager 与安装
|
||||
|
||||
Manager 的 Git URL 安装可使用 `https://github.com/adachi0130/Adachi-Inpaint-Workbench`;或在 `ComfyUI/custom_nodes` 中克隆喵。
|
||||
|
||||
```bash
|
||||
git clone https://github.com/adachi0130/Adachi-Inpaint-Workbench.git
|
||||
```
|
||||
|
||||
Manager 搜索收录申请已提交:[ComfyUI-Manager PR #3378](https://github.com/Comfy-Org/ComfyUI-Manager/pull/3378),仍需维护者合并及索引更新,尚不能保证搜索可见喵。
|
||||
当前尚未配置 Comfy Registry 发布者,Registry 发布流程见 [发布说明](registry/README.md) 喵。
|
||||
|
||||
## 安装
|
||||
|
||||
将整个 `ComfyUI-Adachi-Workbench` 文件夹复制到 `ComfyUI/custom_nodes/`,保留其中的 `web/`、`backend.py`、`__init__.py` 喵。
|
||||
将整个 `Adachi-Inpaint-Workbench` 文件夹复制到 `ComfyUI/custom_nodes/`,保留其中的 `web/`、`backend.py`、`__init__.py` 喵。
|
||||
重启 ComfyUI,然后在浏览器按 `Ctrl+F5` 刷新前端缓存喵。
|
||||
在节点搜索中输入 `Inline Mask` 或 `WebUI Inpaint`,分类是 **安达工作台 / Adachi Workbench** 喵。
|
||||
在节点搜索中输入 `Inline Mask` 或 `WebUI Inpaint`,分类是 **安达工作台 / Adachi-Inpaint-Workbench** 喵。
|
||||
|
||||
Windows 也可以从解压后的插件文件夹运行安装脚本,将参数改为自己的、包含 `main.py` 的 ComfyUI 目录喵。
|
||||
|
||||
@@ -16,7 +32,7 @@ powershell -ExecutionPolicy Bypass -File .\install.ps1 -ComfyUIPath "D:\ComfyUI_
|
||||
|
||||
脚本只复制本插件;更新时会把旧插件压缩备份到所选 ComfyUI 的 `user/adachi-workbench-backups/`,不会自动重启服务喵。
|
||||
插件使用 ComfyUI 已有的 PyTorch、NumPy、Pillow,不需要额外 pip/npm 安装喵。
|
||||
卸载时关闭 ComfyUI,再删除 `custom_nodes/ComfyUI-Adachi-Workbench` 即可喵。
|
||||
卸载时关闭 ComfyUI,再删除 `custom_nodes/Adachi-Inpaint-Workbench` 即可喵。
|
||||
|
||||
## 最快上手
|
||||
|
||||
@@ -46,7 +62,7 @@ powershell -ExecutionPolicy Bypass -File .\install.ps1 -ComfyUIPath "D:\ComfyUI_
|
||||
|
||||
| 操作 | 使用方式 |
|
||||
|---|---|
|
||||
| 画笔 / 橡皮 | 工具栏切换,`B` / `E`;按住 `Alt` 或鼠标右键临时擦除,松开恢复原工具 |
|
||||
| 画笔 / 橡皮 | 工具栏切换,`B` / `E`;按住 `Alt` 或鼠标右键临时擦除,松开恢复原工具;擦除时显示橡皮擦图标与淡粉色虚线范围 |
|
||||
| 矩形 / 自由套索 | `R` / `L`,拖动画出要重绘的区域 |
|
||||
| 笔径 / 硬度 / 笔触 | 分别控制原图像素笔径、边缘硬度、绘制不透明度 |
|
||||
| 橙色叠加 | 仅调节显示透明度,不改变真实蒙版 |
|
||||
@@ -137,8 +153,10 @@ Inline Mask ── image/mask ──> Inpaint Prepare
|
||||
|
||||
## English quick start
|
||||
|
||||
Copy the entire `ComfyUI-Adachi-Workbench` folder into `ComfyUI/custom_nodes`, restart ComfyUI, then hard-refresh your browser喵.
|
||||
Search for `Inline Mask` or `WebUI Inpaint` under **安达工作台 / Adachi Workbench**喵.
|
||||
**Adachi-Inpaint-Workbench** is an MIT-licensed ComfyUI custom node pack for direct mask painting, WebUI-style inpainting, result/original comparison, and adopting results for another edit pass喵.
|
||||
|
||||
Copy the entire `Adachi-Inpaint-Workbench` folder into `ComfyUI/custom_nodes`, restart ComfyUI, then hard-refresh your browser喵.
|
||||
Search for `Inline Mask` or `WebUI Inpaint` under **安达工作台 / Adachi-Inpaint-Workbench**喵.
|
||||
No extra pip or npm packages are required beyond ComfyUI's PyTorch, NumPy and Pillow喵.
|
||||
|
||||
Drag `examples/01-inline-inpaint.json` into ComfyUI, select a checkpoint, upload an image inside **Inline Mask**, paint, and run the workflow喵.
|
||||
|
||||
+3
-3
@@ -1,19 +1,19 @@
|
||||
# Git 版本与回滚
|
||||
|
||||
`v0.2.0` 是最早的界面回滚点,`v0.3.0` 包含内联参数面板、左右对比调整和滚轮路由修复;`v0.4.0` 将画笔与参数集中到 WebUI 重绘节点,并精简原图与对比节点喵。
|
||||
`v0.2.0` 是最早的界面回滚点,`v0.3.0` 包含内联参数面板、左右对比调整和滚轮路由修复;`v0.4.0` 将画笔与参数集中到 WebUI 重绘节点;`v0.4.1` 更新橡皮擦光标及公开名称喵。
|
||||
本机安装目录与源码目录均保留 Git 历史;普通发布 ZIP 不含 `.git`,可另用 Git bundle 恢复历史喵。
|
||||
|
||||
在插件目录运行以下命令,先确认没有未保存改动,再切换版本喵。
|
||||
|
||||
```powershell
|
||||
git status --short
|
||||
git switch --detach v0.3.0
|
||||
git switch --detach v0.4.0
|
||||
```
|
||||
|
||||
恢复本版使用以下命令喵。
|
||||
|
||||
```powershell
|
||||
git switch --detach v0.4.0
|
||||
git switch --detach v0.4.1
|
||||
```
|
||||
|
||||
切换后重启 ComfyUI 并在浏览器按 `Ctrl+F5` 刷新喵。
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
- 0.4.0 在经典节点与 Nodes 2.0 验证内联画布只保留载入按钮,原画笔 DOM 控件迁移到 WebUI 节点;真实鼠标涂抹后可从重绘节点撤销,工具和滑条控制原画布,采样参数正确进入执行请求喵。
|
||||
- 0.4.0 验证按原图比例缩减画布空边、细高度条拉伸、保存重开保持尺寸、原图尺寸填写、断开重连、删除重建重绘节点,以及工具区域滚轮继续操作外部画布喵。
|
||||
- 0.4.0 图像对比顶部不再有工具栏,结果选择、尺寸和适应移到底部;真实 API 队列中的左右对比、移出显示完整结果、批量选择及结果回填通过喵。
|
||||
- 0.4.1 真实浏览器按住 Alt 后确认橡皮擦 SVG 光标及擦除状态生效,松开后恢复十字画笔光标;临时擦除、常规绘画和 18 项后端 CPU 回归通过喵。
|
||||
- 0.4.1 统一公开名称为 Adachi-Inpaint-Workbench,保留节点类 ID 以兼容已有工作流喵。
|
||||
|
||||
采样路径还使用本机 ComfyUI 的真实 common_ksampler/KSampler 函数进行了噪声调度衔接检查;尚未加载实际 checkpoint 测试 GPU 生成质量或所有模型家族,不能将 CPU/接口测试视为模型效果验证喵。
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
"""Adachi Inline Inpaint custom-node package for ComfyUI."""
|
||||
"""Adachi-Inpaint-Workbench custom-node package for ComfyUI."""
|
||||
|
||||
from .backend import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import torch
|
||||
import torch.nn.functional as F
|
||||
|
||||
|
||||
CATEGORY = "安达工作台 / Adachi Workbench"
|
||||
CATEGORY = "安达工作台 / Adachi-Inpaint-Workbench"
|
||||
MAX_IMAGE_PIXELS = 32_000_000
|
||||
MAX_IMAGE_BYTES = 64 * 1024 * 1024
|
||||
MAX_DATA_URL_CHARS = 4 * ((MAX_IMAGE_BYTES + 2) // 3) + 128
|
||||
|
||||
@@ -635,7 +635,7 @@
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"安达工作台 / Adachi Workbench\n1. 选择自己的 checkpoint 喵\n2. 在左侧节点载入原图并直接涂抹喵\n3. 在 WebUI 重绘节点中选择画笔并调整重绘参数喵\n4. width/height 是采样分辨率,最终 image 回贴为原图尺寸喵\n分享时在重绘节点勾选「原图嵌入工作流」喵"
|
||||
"安达工作台 / Adachi-Inpaint-Workbench\n1. 选择自己的 checkpoint 喵\n2. 在左侧节点载入原图并直接涂抹喵\n3. 在 WebUI 重绘节点中选择画笔并调整重绘参数喵\n4. width/height 是采样分辨率,最终 image 回贴为原图尺寸喵\n分享时在重绘节点勾选「原图嵌入工作流」喵"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
|
||||
@@ -792,7 +792,7 @@
|
||||
"outputs": [],
|
||||
"properties": {},
|
||||
"widgets_values": [
|
||||
"安达工作台 / Adachi Workbench — 标准节点组合\n选择模型,载入原图并涂抹,然后运行喵\nPrepare → VAE Encode → Set Latent Noise Mask → KSampler → VAE Decode → Composite 喵\n独立 Prepare 的 fill 请选择 original 或 fill 喵\nlatent_noise / latent_nothing 由一体采样节点实现,独立预处理不修改 latent 喵\n使用专用 inpaint checkpoint 时请改用原生 InpaintModelConditioning 或一体节点 inpaint_model 喵"
|
||||
"安达工作台 / Adachi-Inpaint-Workbench — 标准节点组合\n选择模型,载入原图并涂抹,然后运行喵\nPrepare → VAE Encode → Set Latent Noise Mask → KSampler → VAE Decode → Composite 喵\n独立 Prepare 的 fill 请选择 original 或 fill 喵\nlatent_noise / latent_nothing 由一体采样节点实现,独立预处理不修改 latent 喵\n使用专用 inpaint checkpoint 时请改用原生 InpaintModelConditioning 或一体节点 inpaint_model 喵"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
|
||||
+18
-6
@@ -16,7 +16,8 @@ if (-not (Test-Path -LiteralPath $customRoot)) {
|
||||
New-Item -ItemType Directory -Path $customRoot | Out-Null
|
||||
}
|
||||
$customRoot = (Resolve-Path -LiteralPath $customRoot).ProviderPath
|
||||
$destination = [IO.Path]::GetFullPath((Join-Path $customRoot 'ComfyUI-Adachi-Workbench'))
|
||||
$destination = [IO.Path]::GetFullPath((Join-Path $customRoot 'Adachi-Inpaint-Workbench'))
|
||||
$legacyPath = [IO.Path]::GetFullPath((Join-Path $customRoot 'ComfyUI-Adachi-Workbench'))
|
||||
if (-not $destination.StartsWith($customRoot.TrimEnd('\') + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw 'Install destination must stay within the selected custom_nodes directory.'
|
||||
}
|
||||
@@ -24,16 +25,27 @@ if ($destination.TrimEnd('\') -eq $packageRoot.TrimEnd('\')) {
|
||||
Write-Host 'This package is already in the selected ComfyUI custom_nodes directory. 喵'
|
||||
exit 0
|
||||
}
|
||||
if (Test-Path -LiteralPath $destination) {
|
||||
$existing = Get-Item -LiteralPath $destination
|
||||
if ((Test-Path -LiteralPath $destination) -and (Test-Path -LiteralPath $legacyPath)) {
|
||||
throw 'Both current and legacy plugin folders exist. Keep one installation to avoid duplicate node registrations.'
|
||||
}
|
||||
$existingPath = if (Test-Path -LiteralPath $destination) { $destination } elseif (Test-Path -LiteralPath $legacyPath) { $legacyPath } else { $null }
|
||||
if ($existingPath) {
|
||||
$existing = Get-Item -LiteralPath $existingPath
|
||||
if ($existing.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
||||
throw 'Refusing to overwrite a junction or symbolic-link installation. Use a normal folder.'
|
||||
}
|
||||
$backupRoot = Join-Path $comfyRoot 'user\adachi-workbench-backups'
|
||||
New-Item -ItemType Directory -Path $backupRoot -Force | Out-Null
|
||||
$backupFile = Join-Path $backupRoot ('ComfyUI-Adachi-Workbench-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.zip')
|
||||
Compress-Archive -LiteralPath $destination -DestinationPath $backupFile
|
||||
$backupFile = Join-Path $backupRoot ('Adachi-Inpaint-Workbench-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.zip')
|
||||
Compress-Archive -LiteralPath $existingPath -DestinationPath $backupFile
|
||||
Write-Host "Previous installation backed up: $backupFile 喵"
|
||||
if ($existingPath -ne $destination) {
|
||||
# Verify both resolved absolute paths before moving the legacy folder.
|
||||
if ([IO.Path]::GetDirectoryName($existingPath) -ne $customRoot -or [IO.Path]::GetDirectoryName($destination) -ne $customRoot) {
|
||||
throw 'Migration paths must remain directly inside the selected custom_nodes directory.'
|
||||
}
|
||||
Move-Item -LiteralPath $existingPath -Destination $destination
|
||||
}
|
||||
} else {
|
||||
New-Item -ItemType Directory -Path $destination | Out-Null
|
||||
}
|
||||
@@ -42,5 +54,5 @@ Get-ChildItem -LiteralPath $packageRoot -Force | Where-Object {
|
||||
} | ForEach-Object {
|
||||
Copy-Item -LiteralPath $_.FullName -Destination $destination -Recurse -Force
|
||||
}
|
||||
Write-Host "Installed Adachi Workbench: $destination 喵"
|
||||
Write-Host "Installed Adachi-Inpaint-Workbench: $destination 喵"
|
||||
Write-Host 'Restart ComfyUI, then refresh the browser with Ctrl+F5. 喵'
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 128 KiB |
+18
-2
@@ -1,11 +1,27 @@
|
||||
[project]
|
||||
name = "comfyui-adachi-workbench"
|
||||
version = "0.4.0"
|
||||
name = "adachi-inpaint-workbench"
|
||||
version = "0.4.1"
|
||||
description = "Inline mask painting and WebUI-style inpainting nodes for ComfyUI"
|
||||
requires-python = ">=3.10"
|
||||
license = { file = "LICENSE" }
|
||||
readme = "README.md"
|
||||
authors = [{ name = "Adachi" }]
|
||||
dependencies = ["numpy", "Pillow", "torch"]
|
||||
classifiers = [
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/adachi0130/Adachi-Inpaint-Workbench"
|
||||
Documentation = "https://github.com/adachi0130/Adachi-Inpaint-Workbench#readme"
|
||||
"Bug Tracker" = "https://github.com/adachi0130/Adachi-Inpaint-Workbench/issues"
|
||||
|
||||
[tool.comfy]
|
||||
# Set this to your actual Comfy Registry publisher ID before Registry publishing.
|
||||
# GitHub publication and the Manager legacy-list submission do not require it.
|
||||
PublisherId = ""
|
||||
DisplayName = "Adachi-Inpaint-Workbench"
|
||||
|
||||
[tool.adachi-workbench]
|
||||
frontend = "web"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Manager / Registry 发布
|
||||
|
||||
GitHub 仓库公开并不代表 Manager 搜索清单或 Comfy Registry 已收录喵。
|
||||
本插件的 Manager 收录申请见 [PR #3378](https://github.com/Comfy-Org/ComfyUI-Manager/pull/3378),当前等待上游处理喵。
|
||||
|
||||
## Manager 清单
|
||||
|
||||
[manager-entry.json](manager-entry.json) 是提交给 [ComfyUI-Manager](https://github.com/Comfy-Org/ComfyUI-Manager) 的条目,加入其根目录的 `custom-node-list.json` 并提交 PR,等待维护者合并及索引更新喵。
|
||||
提交方式以 [Manager 官方说明](https://github.com/Comfy-Org/ComfyUI-Manager#how-to-register-your-custom-node-into-comfyui-manager) 为准喵。
|
||||
收录前仍可使用 Manager 的 Git URL 安装,仓库地址为 `https://github.com/adachi0130/Adachi-Inpaint-Workbench` 喵。
|
||||
|
||||
## Comfy Registry
|
||||
|
||||
根据 [官方发布指南](https://docs.comfy.org/registry/publishing),首次发布需要开发者建立自己的 Publisher 及 API Key 喵。
|
||||
|
||||
1. 登录 [Comfy Registry](https://registry.comfy.org/),创建自己的 Publisher,记录实际 Publisher ID 喵。
|
||||
2. 将 `pyproject.toml` 中 `[tool.comfy].PublisherId` 的空值改成该 ID,提交到 GitHub 喵。
|
||||
3. 在仓库 Settings → Secrets and variables → Actions 中添加 `REGISTRY_ACCESS_TOKEN`,值为 Registry 的发布 API Key 喵。
|
||||
4. 在 Actions 中手动运行 **Publish to Comfy Registry**,查看发布和平台校验结果喵。
|
||||
|
||||
当前工作流仅手动触发,不会在未配置 Publisher 或 API Key 时自动发布喵。
|
||||
Registry 的版本发布后不可覆盖,后续发布需要先递增 `pyproject.toml` 版本号喵。
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"author": "adachi0130",
|
||||
"title": "Adachi-Inpaint-Workbench",
|
||||
"reference": "https://github.com/adachi0130/Adachi-Inpaint-Workbench",
|
||||
"files": ["https://github.com/adachi0130/Adachi-Inpaint-Workbench"],
|
||||
"install_type": "git-clone",
|
||||
"description": "Inline mask painting with a compact image node, WebUI-style inpainting controls, whole-image and masked-area sampling, hover image comparison, and one-click result adoption. 内联蒙版、WebUI 风格局部重绘、整图/蒙版区域重绘、图像对比与结果回填。"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
numpy
|
||||
Pillow
|
||||
torch
|
||||
@@ -27,6 +27,7 @@
|
||||
.dd-inpaint .dd-stage { position: relative; flex: 1 1 290px; min-height: 170px; overflow: hidden; border-top: 1px solid var(--dd-line); border-bottom: 1px solid var(--dd-line); background-color: #161a20; background-image: linear-gradient(45deg, #222831 25%, transparent 25%), linear-gradient(-45deg, #222831 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #222831 75%), linear-gradient(-45deg, transparent 75%, #222831 75%); background-size: 20px 20px; background-position: 0 0, 0 10px, 10px -10px, -10px 0; }
|
||||
.dd-inpaint .dd-stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: none; cursor: crosshair; }
|
||||
.dd-inpaint .dd-stage.dd-pan canvas { cursor: grab; }
|
||||
.dd-inpaint .dd-stage.dd-erasing canvas { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M6 19 18 7a3 3 0 0 1 4 0l5 5a3 3 0 0 1 0 4L16 27h-5l-5-5a2 2 0 0 1 0-3Z' fill='%23ffb3c0' stroke='%23151a22' stroke-width='3' stroke-linejoin='round'/%3E%3Cpath d='m7 19 7-7 10 10-5 5h-8l-4-4a3 3 0 0 1 0-4Z' fill='%23fff4f6'/%3E%3Cpath d='m13 13 10 10M11 28h17' fill='none' stroke='%23151a22' stroke-width='2'/%3E%3C/svg%3E") 16 16, crosshair; }
|
||||
.dd-inpaint .dd-stage.dd-drop { outline: 3px solid var(--dd-accent); outline-offset: -3px; }
|
||||
.dd-inpaint .dd-empty { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; gap: 6px; color: var(--dd-muted); padding: 15px; text-align: center; }
|
||||
.dd-inpaint .dd-empty[hidden] { display: none; }
|
||||
|
||||
+22
-1
@@ -42,6 +42,17 @@ export class InlineMaskEditor {
|
||||
// Nodes 2.0 captures wheel events before widget handlers. Route only
|
||||
// this canvas at window capture so image gestures stay inside it.
|
||||
this.stopWheelCapture = registerImageWheel(this.canvas, () => this.ready, event => this.wheel(event), options.onGraphWheel);
|
||||
this.modifierDown = event => {
|
||||
if (event.key !== "Alt" || !this.ready || !this.canvas.matches(":hover")) return;
|
||||
event.preventDefault(); this.altErase = true; this.updateButtons(); this.schedule();
|
||||
};
|
||||
this.modifierUp = event => {
|
||||
if (event.type !== "blur" && event.key !== "Alt") return;
|
||||
this.altErase = false; this.updateButtons(); this.schedule();
|
||||
};
|
||||
window.addEventListener("keydown", this.modifierDown, true);
|
||||
window.addEventListener("keyup", this.modifierUp, true);
|
||||
window.addEventListener("blur", this.modifierUp);
|
||||
this.root.addEventListener("keydown", (event) => this.keydown(event));
|
||||
this.root.addEventListener("keyup", (event) => {
|
||||
if (event.code === "Space") { this.space = false; this.stage.classList.remove("dd-pan"); }
|
||||
@@ -281,6 +292,7 @@ export class InlineMaskEditor {
|
||||
}
|
||||
|
||||
updateButtons() {
|
||||
this.stage?.classList.toggle("dd-erasing", !!(this.altErase || this.tool === "erase" || this.stroke?.type === "erase"));
|
||||
for (const [name, button] of Object.entries(this.toolButtons || {})) {
|
||||
const active = name === (this.altErase ? "erase" : this.tool);
|
||||
button.classList.toggle("dd-active", active);
|
||||
@@ -324,6 +336,7 @@ export class InlineMaskEditor {
|
||||
}
|
||||
if (this.view === "image") { this.view = "overlay"; this.updateButtons(); }
|
||||
this.stroke = { type: event.button === 2 || event.altKey ? "erase" : this.tool, size: this.brushSize, hardness: this.hardness, opacity: this.brushOpacity, points: [this.cursor] };
|
||||
this.updateButtons();
|
||||
if (["brush", "erase"].includes(this.stroke.type)) this.paintSegment(this.mask.getContext("2d"), this.stroke, this.cursor, this.cursor);
|
||||
this.schedule();
|
||||
}
|
||||
@@ -374,6 +387,7 @@ export class InlineMaskEditor {
|
||||
this.drag = null;
|
||||
const id = this.pointerId; this.pointerId = null;
|
||||
if (this.canvas.hasPointerCapture(id)) this.canvas.releasePointerCapture(id);
|
||||
this.updateButtons();
|
||||
this.schedule();
|
||||
}
|
||||
|
||||
@@ -559,8 +573,12 @@ export class InlineMaskEditor {
|
||||
ctx.restore();
|
||||
}
|
||||
if (this.cursor && (this.altErase || ["brush", "erase"].includes(this.tool)) && !this.drag) {
|
||||
const erasing = this.altErase || this.tool === "erase" || this.stroke?.type === "erase";
|
||||
ctx.save();
|
||||
if (erasing) ctx.setLineDash([4 / d.scale, 3 / d.scale]);
|
||||
ctx.beginPath(); ctx.arc(this.cursor.x, this.cursor.y, this.brushSize * this.cursor.p / 2, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = "#15191f"; ctx.lineWidth = 3 / d.scale; ctx.stroke(); ctx.strokeStyle = "white"; ctx.lineWidth = 1 / d.scale; ctx.stroke();
|
||||
ctx.strokeStyle = "#15191f"; ctx.lineWidth = 3 / d.scale; ctx.stroke(); ctx.strokeStyle = erasing ? "#ffd0d7" : "white"; ctx.lineWidth = 1 / d.scale; ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
this.zoomLabel.textContent = `${Math.round(this.zoom * 100)}%`;
|
||||
}
|
||||
@@ -569,6 +587,9 @@ export class InlineMaskEditor {
|
||||
this.destroyed = true; ++this.loadVersion;
|
||||
cancelAnimationFrame(this.frame); this.observer.disconnect();
|
||||
this.stopWheelCapture();
|
||||
window.removeEventListener("keydown", this.modifierDown, true);
|
||||
window.removeEventListener("keyup", this.modifierUp, true);
|
||||
window.removeEventListener("blur", this.modifierUp);
|
||||
for (const canvas of [this.mask, this.baseMask, this.overlay, this.canvas]) canvas.width = canvas.height = 0;
|
||||
this.history = []; this.future = []; this.image = null; this.controls?.remove(); this.root.remove();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user