mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: rename demo widget to lora-manager-widgets and remove demo node
- Update documentation to reflect new widget filename `lora-manager-widgets.js` - Remove `LoraManagerDemoNode` import and registration from `__init__.py` - Translate development guide from Chinese to English for broader accessibility - Clean up obsolete demo references to align with actual widget implementation
This commit is contained in:
22
AGENTS.md
22
AGENTS.md
@@ -1,22 +0,0 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
ComfyUI LoRA Manager pairs a Python backend with browser-side widgets. Backend modules live in <code>py/</code> with HTTP entry points in <code>py/routes/</code>, feature logic in <code>py/services/</code>, shared helpers in <code>py/utils/</code>, and custom nodes in <code>py/nodes/</code>. UI scripts extend ComfyUI from <code>web/comfyui/</code>, while deploy-ready assets remain in <code>static/</code> and <code>templates/</code>. Localization files live in <code>locales/</code>, example workflows in <code>example_workflows/</code>, and interim tests such as <code>test_i18n.py</code> sit beside their source until a dedicated <code>tests/</code> tree lands.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- <code>pip install -r requirements.txt</code> installs backend dependencies.
|
||||
- <code>python standalone.py --port 8188</code> launches the standalone server for iterative development.
|
||||
- <code>python -m pytest test_i18n.py</code> runs the current regression suite; target new files explicitly, e.g. <code>python -m pytest tests/test_recipes.py</code>.
|
||||
- <code>python scripts/sync_translation_keys.py</code> synchronizes locale keys after UI string updates.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
Follow PEP 8 with four-space indentation and descriptive snake_case file and function names such as <code>settings_manager.py</code>. Classes stay PascalCase, constants in UPPER_SNAKE_CASE, and loggers retrieved via <code>logging.getLogger(__name__)</code>. Prefer explicit type hints and docstrings on public APIs. JavaScript under <code>web/comfyui/</code> uses ES modules with camelCase helpers and the <code>_widget.js</code> suffix for UI components.
|
||||
|
||||
## Testing Guidelines
|
||||
Pytest powers backend tests. Name modules <code>test_<feature>.py</code> and keep them near the code or in a future <code>tests/</code> package. Mock ComfyUI dependencies through helpers in <code>standalone.py</code>, keep filesystem fixtures deterministic, and ensure translations are covered. Run <code>python -m pytest</code> before submitting changes.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
Commits follow the conventional format, e.g. <code>feat(settings): add default model path</code>, and should stay focused on a single concern. Pull requests must outline the problem, summarize the solution, list manual verification steps (server run, targeted pytest), and link related issues. Include screenshots or GIFs for UI or locale updates and call out migration steps such as <code>settings.json</code> adjustments.
|
||||
|
||||
## Configuration & Localization Tips
|
||||
Copy <code>settings.json.example</code> to <code>settings.json</code> and adapt model directories before running the standalone server. Store reference assets in <code>civitai/</code> or <code>docs/</code> to keep runtime directories deploy-ready. Whenever UI text changes, update every <code>locales/<lang>.json</code> file and rerun the translation sync script so ComfyUI surfaces localized strings.
|
||||
@@ -1,397 +0,0 @@
|
||||
# Vue Widget 构建流程实施方案
|
||||
|
||||
## 已实施方案:方案1 + 方案4 组合
|
||||
|
||||
我们采用了**提交构建产物 + 智能检测**的混合方案,同时满足用户便利性和开发灵活性。
|
||||
|
||||
---
|
||||
|
||||
## 🎯 方案特点
|
||||
|
||||
### 对于用户
|
||||
✅ **安装即用** - Clone仓库后无需任何构建步骤
|
||||
✅ **无需Node.js** - 构建产物已包含在仓库中
|
||||
✅ **快速启动** - ComfyUI启动时无延迟
|
||||
|
||||
### 对于开发者
|
||||
✅ **自动检测** - 源代码变更后自动检测是否需要重新构建
|
||||
✅ **自动构建** - 如果检测到需要,可自动执行构建(需要Node.js)
|
||||
✅ **灵活配置** - 可选择手动或自动构建模式
|
||||
|
||||
---
|
||||
|
||||
## 📦 实施的组件
|
||||
|
||||
### 1. Git 配置调整
|
||||
|
||||
**文件**: `.gitignore`
|
||||
|
||||
```diff
|
||||
- # Vue widgets build output
|
||||
- web/comfyui/vue-widgets/
|
||||
|
||||
+ # Vue widgets development cache (but keep build output)
|
||||
+ vue-widgets/node_modules/
|
||||
+ vue-widgets/.vite/
|
||||
+ vue-widgets/dist/
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- ✅ 构建产物 `web/comfyui/vue-widgets/` **提交到Git**
|
||||
- ✅ 开发缓存(node_modules等)被忽略
|
||||
- ✅ 仓库大小增加约 1.4MB(可接受)
|
||||
|
||||
---
|
||||
|
||||
### 2. 智能构建检测模块
|
||||
|
||||
**文件**: `py/vue_widget_builder.py`
|
||||
|
||||
**核心功能**:
|
||||
- ✅ 检查构建产物是否存在
|
||||
- ✅ 检查源代码是否比构建新(开发模式)
|
||||
- ✅ 检查Node.js/npm是否可用
|
||||
- ✅ 自动执行构建(如果需要且可行)
|
||||
- ✅ 友好的错误提示和日志
|
||||
|
||||
**主要类和方法**:
|
||||
|
||||
```python
|
||||
class VueWidgetBuilder:
|
||||
def check_build_exists() -> bool
|
||||
"""检查构建产物是否存在"""
|
||||
|
||||
def check_build_outdated() -> bool
|
||||
"""检查源代码是否比构建新"""
|
||||
|
||||
def check_node_available() -> bool
|
||||
"""检查Node.js是否可用"""
|
||||
|
||||
def build_widgets(force=False) -> bool
|
||||
"""执行构建"""
|
||||
|
||||
def ensure_built(auto_build=True, warn_only=True) -> bool
|
||||
"""确保构建存在,智能处理"""
|
||||
```
|
||||
|
||||
**便捷函数**:
|
||||
```python
|
||||
check_and_build_vue_widgets(auto_build=True, warn_only=True, force=False)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 启动时自动检测
|
||||
|
||||
**文件**: `__init__.py`
|
||||
|
||||
在ComfyUI加载插件时自动检测并构建:
|
||||
|
||||
```python
|
||||
# Check and build Vue widgets if needed (development mode)
|
||||
try:
|
||||
from .py.vue_widget_builder import check_and_build_vue_widgets
|
||||
# Auto-build in development, warn only if fails
|
||||
check_and_build_vue_widgets(auto_build=True, warn_only=True)
|
||||
except Exception as e:
|
||||
logging.warning(f"[LoRA Manager] Vue widget build check skipped: {e}")
|
||||
```
|
||||
|
||||
**行为**:
|
||||
- ✅ 如果构建产物存在且最新 → 静默通过
|
||||
- ✅ 如果构建产物缺失/过期 → 尝试自动构建(需Node.js)
|
||||
- ✅ 如果构建失败 → 警告但不阻止ComfyUI启动
|
||||
- ✅ 开发模式下源代码变更后自动重建
|
||||
|
||||
---
|
||||
|
||||
### 4. 增强的构建脚本
|
||||
|
||||
**文件**: `vue-widgets/package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "vite build",
|
||||
"build:production": "vite build --mode production",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"clean": "rm -rf ../web/comfyui/vue-widgets",
|
||||
"rebuild": "npm run clean && npm run build",
|
||||
"prepare": "npm run build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新增脚本**:
|
||||
- `clean` - 清理构建产物
|
||||
- `rebuild` - 完全重建
|
||||
- `build:production` - 生产模式构建
|
||||
- `prepare` - npm install后自动构建(可选)
|
||||
|
||||
---
|
||||
|
||||
### 5. Pre-commit Hook 示例
|
||||
|
||||
**文件**: `vue-widgets/pre-commit.example`
|
||||
|
||||
提供了Git pre-commit hook示例,确保提交前构建:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
cd vue-widgets && npm run build && git add web/comfyui/vue-widgets/
|
||||
```
|
||||
|
||||
**使用方法**:
|
||||
```bash
|
||||
# 手动安装(简单方法)
|
||||
cp vue-widgets/pre-commit.example .git/hooks/pre-commit
|
||||
chmod +x .git/hooks/pre-commit
|
||||
|
||||
# 或使用Husky(推荐用于团队)
|
||||
npm install --save-dev husky
|
||||
npx husky install
|
||||
npx husky add .git/hooks/pre-commit "cd vue-widgets && npm run build"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 场景A: 用户安装
|
||||
|
||||
```bash
|
||||
# 1. 用户clone仓库
|
||||
git clone <repo-url>
|
||||
cd ComfyUI-Lora-Manager
|
||||
|
||||
# 2. 启动ComfyUI(无需任何构建步骤)
|
||||
# 构建产物已在仓库中,直接可用
|
||||
```
|
||||
|
||||
**结果**: ✅ 即装即用,无需Node.js
|
||||
|
||||
---
|
||||
|
||||
### 场景B: 开发者修改Vue代码
|
||||
|
||||
**方式1: 手动构建**
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm run build
|
||||
# 修改会被检测到,ComfyUI重启时会看到最新版本
|
||||
```
|
||||
|
||||
**方式2: 监听模式**
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm run dev # Watch mode,自动重建
|
||||
# 浏览器刷新即可看到变化
|
||||
```
|
||||
|
||||
**方式3: 自动检测**
|
||||
```bash
|
||||
# 修改Vue源代码
|
||||
vim vue-widgets/src/components/DemoWidget.vue
|
||||
|
||||
# 重启ComfyUI
|
||||
# __init__.py会检测到源代码比构建新,自动重建(如果有Node.js)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 场景C: 提交代码
|
||||
|
||||
**如果安装了pre-commit hook**:
|
||||
```bash
|
||||
git commit -m "Update widget"
|
||||
# Hook自动执行构建
|
||||
# 构建产物自动添加到commit
|
||||
```
|
||||
|
||||
**如果没有hook(手动)**:
|
||||
```bash
|
||||
cd vue-widgets && npm run build && cd ..
|
||||
git add .
|
||||
git commit -m "Update widget and build output"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 场景D: CI/CD 发布
|
||||
|
||||
```bash
|
||||
# 在GitHub Actions或其他CI中
|
||||
cd vue-widgets
|
||||
npm install
|
||||
npm run build
|
||||
# 构建产物自动包含在release中
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 测试结果
|
||||
|
||||
已测试以下场景,全部通过:
|
||||
|
||||
### ✅ Test 1: 构建产物存在时
|
||||
```
|
||||
Result: True (静默通过,无日志)
|
||||
```
|
||||
|
||||
### ✅ Test 2: 构建产物缺失时
|
||||
```
|
||||
自动检测 → 自动npm install → 自动build → 成功
|
||||
Result: True
|
||||
Build created: web/comfyui/vue-widgets/demo-widget.js (418K)
|
||||
```
|
||||
|
||||
### ✅ Test 3: 源代码变更检测
|
||||
```
|
||||
修改.vue文件 → 时间戳检测 → 自动重建
|
||||
Result: True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Git 仓库状态
|
||||
|
||||
### 应该提交的文件:
|
||||
|
||||
```
|
||||
✅ vue-widgets/src/**/*.{ts,vue} # 源代码
|
||||
✅ vue-widgets/package.json # 依赖配置
|
||||
✅ vue-widgets/package-lock.json # 锁定版本
|
||||
✅ vue-widgets/vite.config.mts # 构建配置
|
||||
✅ vue-widgets/tsconfig.json # TS配置
|
||||
✅ vue-widgets/*.md # 文档
|
||||
✅ web/comfyui/vue-widgets/**/*.js # 构建产物 ⭐
|
||||
✅ web/comfyui/vue-widgets/**/*.css # 构建CSS ⭐
|
||||
✅ web/comfyui/vue-widgets/**/*.map # Source maps ⭐
|
||||
✅ py/vue_widget_builder.py # 构建检测模块 ⭐
|
||||
```
|
||||
|
||||
### 应该忽略的文件:
|
||||
|
||||
```
|
||||
❌ vue-widgets/node_modules/ # npm依赖
|
||||
❌ vue-widgets/.vite/ # Vite缓存
|
||||
❌ vue-widgets/dist/ # Vite临时目录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎓 开发者指南
|
||||
|
||||
### 首次设置
|
||||
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 日常开发
|
||||
|
||||
```bash
|
||||
# 开发模式(推荐)
|
||||
cd vue-widgets
|
||||
npm run dev
|
||||
# 在另一个终端启动ComfyUI,修改后刷新浏览器
|
||||
|
||||
# 或者依赖自动检测
|
||||
# 修改代码 → 重启ComfyUI → 自动重建
|
||||
```
|
||||
|
||||
### 提交前
|
||||
|
||||
```bash
|
||||
# 确保构建最新
|
||||
cd vue-widgets && npm run build && cd ..
|
||||
|
||||
# 或者安装pre-commit hook自动化
|
||||
cp vue-widgets/pre-commit.example .git/hooks/pre-commit
|
||||
chmod +x .git/hooks/pre-commit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 故障排除
|
||||
|
||||
### 问题1: 构建产物不是最新的
|
||||
|
||||
**症状**: 修改了Vue代码,但ComfyUI中看不到变化
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm run rebuild # 强制重建
|
||||
# 然后刷新浏览器
|
||||
```
|
||||
|
||||
### 问题2: 自动构建失败
|
||||
|
||||
**症状**: ComfyUI启动时显示构建失败警告
|
||||
|
||||
**检查**:
|
||||
```bash
|
||||
# 检查Node.js是否安装
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
# 手动测试构建
|
||||
cd vue-widgets
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 问题3: Git显示大量构建产物变更
|
||||
|
||||
**这是正常的** - 构建产物应该提交
|
||||
|
||||
**最小化变更**:
|
||||
- 使用 `npm run build` 而非 `npm run dev`(watch模式)
|
||||
- 确保vite配置中 `minify: false`(已配置)
|
||||
- 只在需要时重新构建
|
||||
|
||||
---
|
||||
|
||||
## 📈 优势总结
|
||||
|
||||
| 方面 | 优势 |
|
||||
|------|------|
|
||||
| 用户体验 | ⭐⭐⭐⭐⭐ 安装即用 |
|
||||
| 开发体验 | ⭐⭐⭐⭐⭐ 自动检测+构建 |
|
||||
| 可靠性 | ⭐⭐⭐⭐⭐ 构建产物已验证 |
|
||||
| 灵活性 | ⭐⭐⭐⭐ 支持多种工作流 |
|
||||
| 维护性 | ⭐⭐⭐⭐ 清晰的构建流程 |
|
||||
| Git仓库 | ⭐⭐⭐ 略大但可接受 |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 未来优化(可选)
|
||||
|
||||
1. **添加Husky** - 自动化pre-commit hooks
|
||||
2. **GitHub Actions** - CI自动构建和测试
|
||||
3. **构建缓存** - 加速CI构建
|
||||
4. **Minification** - 生产模式压缩代码(减小体积)
|
||||
5. **代码分割** - 按需加载不同widget
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
当前实施的方案完美平衡了用户便利性和开发灵活性:
|
||||
|
||||
- ✅ **用户**: Clone后即用,无需Node.js
|
||||
- ✅ **开发者**: 自动检测和构建,开发流畅
|
||||
- ✅ **可靠性**: 构建产物已验证提交
|
||||
- ✅ **可维护性**: 清晰的构建流程和文档
|
||||
|
||||
用户安装时,`web/comfyui/vue-widgets/`中的JS代码**始终是由vue-widgets中的最新代码编译得到的**,因为:
|
||||
|
||||
1. 开发者提交前会构建(手动或通过hook)
|
||||
2. ComfyUI启动时会检测并自动重建(开发模式)
|
||||
3. 构建产物已包含在Git仓库中(用户直接获得)
|
||||
|
||||
这个方案已经过测试验证,可以投入生产使用。
|
||||
@@ -1,267 +0,0 @@
|
||||
# Vue Widget 构建流程解决方案
|
||||
|
||||
## 问题分析
|
||||
|
||||
当前配置问题:
|
||||
- ✅ Vue源代码在 `vue-widgets/src/` 中
|
||||
- ✅ 构建产物输出到 `web/comfyui/vue-widgets/`
|
||||
- ❌ **构建产物被 `.gitignore` 忽略,不会提交到仓库**
|
||||
- ❌ **用户安装后没有构建产物,widget无法工作**
|
||||
|
||||
## 解决方案对比
|
||||
|
||||
### 方案1:提交构建产物到Git(推荐 ⭐)
|
||||
|
||||
**优点:**
|
||||
- ✅ 用户安装即可用,无需额外步骤
|
||||
- ✅ 最简单可靠
|
||||
- ✅ 适合大多数ComfyUI用户(不一定有Node.js环境)
|
||||
- ✅ 与现有ComfyUI插件生态一致
|
||||
|
||||
**缺点:**
|
||||
- ⚠️ Git仓库略大(每次构建产物变化都会commit)
|
||||
- ⚠️ 需要确保开发者提交前构建
|
||||
|
||||
**实现方式:**
|
||||
1. 从 `.gitignore` 移除 `web/comfyui/vue-widgets/`
|
||||
2. 添加 pre-commit hook 自动构建
|
||||
3. 提交构建产物到仓库
|
||||
|
||||
**适用场景:**
|
||||
- 生产环境发布
|
||||
- 用户通过ComfyUI Manager或git clone安装
|
||||
|
||||
---
|
||||
|
||||
### 方案2:用户安装时自动构建
|
||||
|
||||
**优点:**
|
||||
- ✅ Git仓库小,只包含源代码
|
||||
- ✅ 始终使用最新代码构建
|
||||
- ✅ 开发者友好
|
||||
|
||||
**缺点:**
|
||||
- ❌ 要求用户有Node.js环境
|
||||
- ❌ 安装时间长(需要npm install + build)
|
||||
- ❌ 可能构建失败影响安装体验
|
||||
- ❌ ComfyUI Manager可能不支持
|
||||
|
||||
**实现方式:**
|
||||
1. 保持 `.gitignore` 设置
|
||||
2. 添加安装脚本自动检测并构建
|
||||
3. 在Python `__init__.py` 启动时检查构建产物
|
||||
|
||||
**适用场景:**
|
||||
- 开发环境
|
||||
- 技术用户
|
||||
|
||||
---
|
||||
|
||||
### 方案3:混合方案(开发 + 生产分离)
|
||||
|
||||
**优点:**
|
||||
- ✅ 开发时只提交源代码
|
||||
- ✅ Release时提供完整构建
|
||||
- ✅ Git仓库保持干净
|
||||
- ✅ 用户安装release版本即可用
|
||||
|
||||
**缺点:**
|
||||
- ⚠️ 需要CI/CD配置
|
||||
- ⚠️ 工作流稍复杂
|
||||
|
||||
**实现方式:**
|
||||
1. 开发分支:gitignore构建产物
|
||||
2. GitHub Actions:自动构建
|
||||
3. Release分支/Tag:包含构建产物
|
||||
4. 用户安装release版本
|
||||
|
||||
**适用场景:**
|
||||
- 成熟项目
|
||||
- 多人协作开发
|
||||
|
||||
---
|
||||
|
||||
### 方案4:Python启动时自动构建(智能方案)
|
||||
|
||||
**优点:**
|
||||
- ✅ 自动检测是否需要构建
|
||||
- ✅ 开发模式自动构建
|
||||
- ✅ 生产模式使用已有构建
|
||||
- ✅ 最灵活
|
||||
|
||||
**缺点:**
|
||||
- ⚠️ 需要编写构建检测逻辑
|
||||
- ⚠️ 首次启动可能较慢
|
||||
|
||||
**实现方式:**
|
||||
1. 在 `__init__.py` 中检查构建产物
|
||||
2. 如果不存在或过期,尝试自动构建
|
||||
3. 如果无Node.js环境,给出明确提示
|
||||
|
||||
**适用场景:**
|
||||
- 开发+生产通用
|
||||
- 技术用户为主
|
||||
|
||||
---
|
||||
|
||||
## 推荐实现:方案1 + 方案4 组合
|
||||
|
||||
### 为什么?
|
||||
|
||||
1. **对普通用户**:提交构建产物,安装即用
|
||||
2. **对开发者**:pre-commit hook确保提交前构建
|
||||
3. **智能检测**:Python启动时检查,开发模式可自动重建
|
||||
|
||||
### 实现步骤
|
||||
|
||||
#### Step 1: 修改 .gitignore(提交构建产物)
|
||||
|
||||
```bash
|
||||
# 移除这行:
|
||||
# web/comfyui/vue-widgets/
|
||||
|
||||
# 但保留源码构建缓存:
|
||||
vue-widgets/node_modules/
|
||||
vue-widgets/dist/
|
||||
```
|
||||
|
||||
#### Step 2: 添加 pre-commit hook
|
||||
|
||||
创建 `.husky/pre-commit` 或使用简单的git hook:
|
||||
```bash
|
||||
#!/bin/sh
|
||||
# 在commit前自动构建Vue widgets
|
||||
cd vue-widgets && npm run build && cd .. && git add web/comfyui/vue-widgets/
|
||||
```
|
||||
|
||||
#### Step 3: 在Python中添加智能检测
|
||||
|
||||
在 `__init__.py` 或专门的构建检查模块中:
|
||||
```python
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
def check_vue_widgets_build():
|
||||
"""检查Vue widgets是否已构建,如果需要则自动构建"""
|
||||
project_root = Path(__file__).parent
|
||||
build_file = project_root / "web/comfyui/vue-widgets/demo-widget.js"
|
||||
src_dir = project_root / "vue-widgets/src"
|
||||
|
||||
# 如果构建产物不存在
|
||||
if not build_file.exists():
|
||||
print("[LoRA Manager] Vue widget build not found, attempting to build...")
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["npm", "run", "build"],
|
||||
cwd=project_root / "vue-widgets",
|
||||
capture_output=True,
|
||||
timeout=120
|
||||
)
|
||||
if result.returncode == 0:
|
||||
print("[LoRA Manager] ✓ Vue widgets built successfully")
|
||||
else:
|
||||
print(f"[LoRA Manager] ⚠️ Build failed: {result.stderr.decode()}")
|
||||
print("[LoRA Manager] Please run: cd vue-widgets && npm install && npm run build")
|
||||
except FileNotFoundError:
|
||||
print("[LoRA Manager] ⚠️ Node.js not found. Please install Node.js and run:")
|
||||
print("[LoRA Manager] cd vue-widgets && npm install && npm run build")
|
||||
except Exception as e:
|
||||
print(f"[LoRA Manager] ⚠️ Build error: {e}")
|
||||
|
||||
# 检查源代码是否比构建产物新(开发模式)
|
||||
elif src_dir.exists():
|
||||
src_mtime = max(f.stat().st_mtime for f in src_dir.rglob("*") if f.is_file())
|
||||
build_mtime = build_file.stat().st_mtime
|
||||
|
||||
if src_mtime > build_mtime:
|
||||
print("[LoRA Manager] Source code newer than build, rebuilding...")
|
||||
# 同样的构建逻辑
|
||||
```
|
||||
|
||||
#### Step 4: package.json 添加便捷脚本
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "vite build",
|
||||
"build:check": "node scripts/check-build.js",
|
||||
"typecheck": "vue-tsc --noEmit",
|
||||
"prepare": "npm run build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 对于不同场景的建议
|
||||
|
||||
### 场景A:当前开发阶段(快速验证)
|
||||
**使用:方案1(提交构建产物)**
|
||||
```bash
|
||||
# 1. 移除gitignore
|
||||
# 2. 构建并提交
|
||||
cd vue-widgets && npm run build && cd ..
|
||||
git add -f web/comfyui/vue-widgets/
|
||||
git commit -m "Add Vue widget build output"
|
||||
```
|
||||
|
||||
### 场景B:多人协作开发
|
||||
**使用:方案1 + pre-commit hook**
|
||||
- 提交构建产物保证可用性
|
||||
- Hook确保开发者不会忘记构建
|
||||
|
||||
### 场景C:成熟生产项目
|
||||
**使用:方案3(GitHub Actions)**
|
||||
- main分支不含构建产物
|
||||
- CI自动构建并发布到release
|
||||
- 用户安装release tag
|
||||
|
||||
---
|
||||
|
||||
## 立即可用的解决方案
|
||||
|
||||
### 最简单方案(推荐现在使用):
|
||||
|
||||
```bash
|
||||
# 1. 修改 .gitignore,移除构建产物忽略
|
||||
sed -i '/web\/comfyui\/vue-widgets/d' .gitignore
|
||||
|
||||
# 2. 添加源码缓存到gitignore
|
||||
echo "vue-widgets/node_modules/" >> .gitignore
|
||||
echo "vue-widgets/.vite/" >> .gitignore
|
||||
|
||||
# 3. 确保已构建
|
||||
cd vue-widgets && npm run build && cd ..
|
||||
|
||||
# 4. 提交所有文件
|
||||
git add .
|
||||
git commit -m "feat: Add Vue + PrimeVue widget scaffold with demo"
|
||||
```
|
||||
|
||||
这样用户clone后即可直接使用,同时开发者在修改Vue代码后需要手动运行 `npm run build`。
|
||||
|
||||
---
|
||||
|
||||
## 未来改进
|
||||
|
||||
可以考虑:
|
||||
1. 添加 Husky + lint-staged 自动化pre-commit
|
||||
2. 添加 GitHub Actions 自动构建和发布
|
||||
3. 编写安装后检查脚本
|
||||
4. 在ComfyUI Manager元数据中说明Node.js依赖(如果选择方案2)
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 方案 | 用户体验 | 开发体验 | Git仓库大小 | 实现难度 |
|
||||
|------|---------|---------|------------|---------|
|
||||
| 方案1 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
|
||||
| 方案2 | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
|
||||
| 方案3 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
|
||||
| 方案4 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
|
||||
|
||||
**当前阶段推荐:方案1(提交构建产物)**
|
||||
**长期推荐:方案1 + 方案4 组合(提交产物 + 智能检测)**
|
||||
@@ -1,290 +0,0 @@
|
||||
# Vue Widget 开发快速参考
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 用户安装
|
||||
```bash
|
||||
# Clone仓库后直接启动ComfyUI,无需其他步骤
|
||||
git clone <repo>
|
||||
# 构建产物已包含,直接可用
|
||||
```
|
||||
|
||||
### 开发者设置
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm install # 首次安装依赖
|
||||
npm run build # 构建widget
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 常用命令
|
||||
|
||||
### 构建命令
|
||||
```bash
|
||||
cd vue-widgets
|
||||
|
||||
npm run build # 单次构建
|
||||
npm run dev # 监听模式(自动重建)
|
||||
npm run rebuild # 清理并重建
|
||||
npm run typecheck # TypeScript类型检查
|
||||
npm run clean # 清理构建产物
|
||||
```
|
||||
|
||||
### 开发工作流
|
||||
```bash
|
||||
# 方式1: 监听模式(推荐)
|
||||
cd vue-widgets && npm run dev
|
||||
# 修改代码后,刷新ComfyUI浏览器页面
|
||||
|
||||
# 方式2: 手动构建
|
||||
# 修改代码 → npm run build → 刷新浏览器
|
||||
|
||||
# 方式3: 自动检测
|
||||
# 修改代码 → 重启ComfyUI(会自动重建)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📂 项目结构
|
||||
|
||||
```
|
||||
vue-widgets/ # Vue源代码目录
|
||||
├── src/
|
||||
│ ├── main.ts # 扩展注册入口
|
||||
│ └── components/
|
||||
│ └── DemoWidget.vue # Widget组件
|
||||
├── package.json # 依赖和脚本
|
||||
└── vite.config.mts # 构建配置
|
||||
|
||||
web/comfyui/vue-widgets/ # 构建产物(提交到Git)
|
||||
├── demo-widget.js # 编译后的JS
|
||||
├── demo-widget.js.map # Source map
|
||||
└── assets/ # CSS等资源
|
||||
|
||||
py/nodes/
|
||||
└── demo_vue_widget_node.py # Python节点定义
|
||||
|
||||
py/
|
||||
└── vue_widget_builder.py # 构建检测模块
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 创建新Widget
|
||||
|
||||
### 1. 创建Python节点
|
||||
`py/nodes/my_widget_node.py`:
|
||||
```python
|
||||
class MyWidgetNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"my_widget": ("MY_WIDGET", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "loramanager"
|
||||
|
||||
def process(self, my_widget):
|
||||
return (str(my_widget),)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"MyWidgetNode": MyWidgetNode}
|
||||
```
|
||||
|
||||
### 2. 创建Vue组件
|
||||
`vue-widgets/src/components/MyWidget.vue`:
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<Button label="Click" @click="handleClick" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
const props = defineProps<{
|
||||
widget: { serializeValue?: Function }
|
||||
node: { id: number }
|
||||
}>()
|
||||
|
||||
onMounted(() => {
|
||||
props.widget.serializeValue = async () => {
|
||||
return { /* 数据 */ }
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
### 3. 注册Widget
|
||||
`vue-widgets/src/main.ts`:
|
||||
```typescript
|
||||
import MyWidget from '@/components/MyWidget.vue'
|
||||
|
||||
// 在 getCustomWidgets() 中:
|
||||
MY_WIDGET(node) {
|
||||
return createVueWidget(node, MyWidget, 'my-widget')
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 注册节点
|
||||
`__init__.py`:
|
||||
```python
|
||||
from .py.nodes.my_widget_node import MyWidgetNode
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
# ...
|
||||
"MyWidgetNode": MyWidgetNode
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 构建并测试
|
||||
```bash
|
||||
cd vue-widgets && npm run build
|
||||
# 重启ComfyUI并测试
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 构建流程保证
|
||||
|
||||
### 如何确保用户安装后有最新的构建产物?
|
||||
|
||||
**实施的方案**: 提交构建产物 + 智能检测
|
||||
|
||||
#### ✅ 对用户
|
||||
1. 构建产物已包含在Git仓库中
|
||||
2. Clone后即可使用,无需Node.js
|
||||
3. ComfyUI启动时自动检查(如果有Node.js会自动重建)
|
||||
|
||||
#### ✅ 对开发者
|
||||
1. 修改Vue代码后,ComfyUI重启时自动检测并重建
|
||||
2. 可使用 `npm run dev` 监听模式自动重建
|
||||
3. 提交前运行 `npm run build`(或使用pre-commit hook)
|
||||
|
||||
#### ✅ Git配置
|
||||
```bash
|
||||
# .gitignore
|
||||
✅ 提交: web/comfyui/vue-widgets/ # 构建产物
|
||||
❌ 忽略: vue-widgets/node_modules/ # 开发依赖
|
||||
❌ 忽略: vue-widgets/.vite/ # 构建缓存
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 故障排除
|
||||
|
||||
### 问题: Widget不显示最新修改
|
||||
```bash
|
||||
# 解决方案1: 强制重建
|
||||
cd vue-widgets && npm run rebuild
|
||||
|
||||
# 解决方案2: 清理缓存
|
||||
rm -rf web/comfyui/vue-widgets
|
||||
npm run build
|
||||
|
||||
# 解决方案3: 硬刷新浏览器
|
||||
Ctrl+Shift+R (Windows/Linux)
|
||||
Cmd+Shift+R (Mac)
|
||||
```
|
||||
|
||||
### 问题: 自动构建失败
|
||||
```bash
|
||||
# 检查Node.js
|
||||
node --version # 需要 >= 18
|
||||
npm --version
|
||||
|
||||
# 重新安装依赖
|
||||
cd vue-widgets
|
||||
rm -rf node_modules package-lock.json
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 问题: TypeScript错误
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm run typecheck # 检查类型错误
|
||||
npm run build # 构建(忽略类型错误)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 常用PrimeVue组件
|
||||
|
||||
```typescript
|
||||
import Button from 'primevue/button'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import InputNumber from 'primevue/inputnumber'
|
||||
import Dropdown from 'primevue/dropdown'
|
||||
import Card from 'primevue/card'
|
||||
import DataTable from 'primevue/datatable'
|
||||
import Column from 'primevue/column'
|
||||
import Dialog from 'primevue/dialog'
|
||||
import Tree from 'primevue/tree'
|
||||
import Checkbox from 'primevue/checkbox'
|
||||
import Slider from 'primevue/slider'
|
||||
```
|
||||
|
||||
文档: https://primevue.org/
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Git工作流
|
||||
|
||||
### 提交代码
|
||||
```bash
|
||||
# 方式1: 手动构建
|
||||
cd vue-widgets && npm run build && cd ..
|
||||
git add .
|
||||
git commit -m "feat: update widget"
|
||||
|
||||
# 方式2: 使用pre-commit hook
|
||||
cp vue-widgets/pre-commit.example .git/hooks/pre-commit
|
||||
chmod +x .git/hooks/pre-commit
|
||||
git commit -m "feat: update widget"
|
||||
# Hook自动构建并添加产物
|
||||
```
|
||||
|
||||
### 提交前检查清单
|
||||
- [ ] Vue源代码已修改
|
||||
- [ ] 运行 `npm run build`
|
||||
- [ ] 测试widget功能正常
|
||||
- [ ] 构建产物已生成在 `web/comfyui/vue-widgets/`
|
||||
- [ ] `git add` 包含构建产物
|
||||
- [ ] Commit
|
||||
|
||||
---
|
||||
|
||||
## 📖 更多文档
|
||||
|
||||
- **VUE_WIDGETS_SETUP.md** - 完整架构和设置指南
|
||||
- **vue-widgets/README.md** - Widget开发详细指南
|
||||
- **vue-widgets/DEMO_INSTRUCTIONS.md** - Demo widget测试说明
|
||||
- **BUILD_WORKFLOW_SOLUTIONS.md** - 构建流程方案对比
|
||||
- **BUILD_WORKFLOW_IMPLEMENTATION.md** - 已实施方案详解
|
||||
|
||||
---
|
||||
|
||||
## 💡 提示
|
||||
|
||||
- 开发时优先使用 `npm run dev` 监听模式
|
||||
- 提交前确保运行 `npm run build`
|
||||
- 构建产物约1.4MB,会提交到Git(正常)
|
||||
- ComfyUI会在启动时自动检测并重建(如果需要)
|
||||
- Source maps已启用,便于调试
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习资源
|
||||
|
||||
- [Vue 3 文档](https://vuejs.org/)
|
||||
- [PrimeVue 文档](https://primevue.org/)
|
||||
- [TypeScript 文档](https://www.typescriptlang.org/)
|
||||
- [Vite 文档](https://vitejs.dev/)
|
||||
- [ComfyUI 自定义节点开发](https://docs.comfy.org/)
|
||||
@@ -20,7 +20,7 @@ ComfyUI-Lora-Manager/
|
||||
│
|
||||
├── web/comfyui/ # ComfyUI web directory
|
||||
│ ├── vue-widgets/ # Compiled Vue widgets (gitignored)
|
||||
│ │ ├── demo-widget.js # Built JavaScript
|
||||
│ │ ├── lora-manager-widgets.js # Built JavaScript
|
||||
│ │ └── assets/ # CSS and other assets
|
||||
│ └── *.js # Existing vanilla JS widgets
|
||||
│
|
||||
@@ -148,7 +148,7 @@ For the full component library, see [PrimeVue Documentation](https://primevue.or
|
||||
- Check that Node.js version is 18+ (`node --version`)
|
||||
|
||||
### Widget doesn't appear in ComfyUI
|
||||
- Verify the build completed successfully (`web/comfyui/vue-widgets/demo-widget.js` exists)
|
||||
- Verify the build completed successfully (`web/comfyui/vue-widgets/lora-manager-widgets.js` exists)
|
||||
- Check that the Python node is registered in `__init__.py`
|
||||
- Restart ComfyUI completely (not just refresh browser)
|
||||
- Check browser console for JavaScript errors
|
||||
|
||||
@@ -8,7 +8,6 @@ try: # pragma: no cover - import fallback for pytest collection
|
||||
from .py.nodes.debug_metadata import DebugMetadata
|
||||
from .py.nodes.wanvideo_lora_select import WanVideoLoraSelectLM
|
||||
from .py.nodes.wanvideo_lora_select_from_text import WanVideoLoraSelectFromText
|
||||
from .py.nodes.demo_vue_widget_node import LoraManagerDemoNode
|
||||
from .py.nodes.lora_pool import LoraPoolNode
|
||||
from .py.metadata_collector import init as init_metadata_collector
|
||||
except ImportError: # pragma: no cover - allows running under pytest without package install
|
||||
@@ -30,7 +29,6 @@ except ImportError: # pragma: no cover - allows running under pytest without pa
|
||||
DebugMetadata = importlib.import_module("py.nodes.debug_metadata").DebugMetadata
|
||||
WanVideoLoraSelectLM = importlib.import_module("py.nodes.wanvideo_lora_select").WanVideoLoraSelectLM
|
||||
WanVideoLoraSelectFromText = importlib.import_module("py.nodes.wanvideo_lora_select_from_text").WanVideoLoraSelectFromText
|
||||
LoraManagerDemoNode = importlib.import_module("py.nodes.demo_vue_widget_node").LoraManagerDemoNode
|
||||
LoraPoolNode = importlib.import_module("py.nodes.lora_pool").LoraPoolNode
|
||||
init_metadata_collector = importlib.import_module("py.metadata_collector").init
|
||||
|
||||
@@ -44,7 +42,6 @@ NODE_CLASS_MAPPINGS = {
|
||||
DebugMetadata.NAME: DebugMetadata,
|
||||
WanVideoLoraSelectLM.NAME: WanVideoLoraSelectLM,
|
||||
WanVideoLoraSelectFromText.NAME: WanVideoLoraSelectFromText,
|
||||
"LoraManagerDemoNode": LoraManagerDemoNode,
|
||||
LoraPoolNode.NAME: LoraPoolNode
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
# ComfyUI Custom DOMWidget 开发说明文档 (Vanilla JavaScript)
|
||||
# DOMWidget Development Guide
|
||||
|
||||
本文档旨在说明如何使用 Vanilla JavaScript (纯 JS) 在 ComfyUI 前端中实现自定义 `DOMWidget`。`DOMWidget` 允许你将标准的 HTML 元素(如 `div`, `video`, `canvas`, `input` 等)嵌入到 ComfyUI 的节点中,并享受前端自动布局和缩放管理。
|
||||
This document provides a comprehensive guide for developing custom DOMWidgets in ComfyUI using Vanilla JavaScript. DOMWidgets allow you to embed standard HTML elements (div, video, canvas, input, etc.) into ComfyUI nodes while benefitting from the frontend's automatic layout and zoom management.
|
||||
|
||||
## 1. Core Concepts
|
||||
|
||||
In ComfyUI, a `DOMWidget` extends the default LiteGraph Canvas rendering logic. It maintains an HTML layer on top of the Canvas, making complex interactions and media displays significantly easier to implement than pure Canvas drawing.
|
||||
|
||||
### Key APIs
|
||||
* **`app.registerExtension`**: The entry point for registering extensions.
|
||||
* **`getCustomWidgets`**: A hook for defining new widget types associated with specific input types.
|
||||
* **`node.addDOMWidget`**: The core method to add HTML elements to a node.
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心概念
|
||||
## 2. Basic Structure
|
||||
|
||||
在 ComfyUI 中,`DOMWidget` 是对 LiteGraph 默认 Canvas 渲染逻辑的扩展。它在 Canvas 之上维护一个 HTML 层,使得复杂的交互和媒体显示变得非常容易。
|
||||
|
||||
### 核心 API
|
||||
* `app.registerExtension`: 注册扩展的入口。
|
||||
* `getCustomWidgets`: 定义新部件类型的钩子。
|
||||
* `node.addDOMWidget(name, type, element, options)`: 将 HTML 元素添加到节点的关键方法。
|
||||
|
||||
---
|
||||
|
||||
## 2. 基础结构
|
||||
|
||||
一个标准的自定义 `DOMWidget` 扩展通常遵循以下结构:
|
||||
A standard custom DOMWidget extension typically follows this structure:
|
||||
|
||||
```javascript
|
||||
import { app } from "../../scripts/app.js";
|
||||
@@ -26,19 +24,20 @@ app.registerExtension({
|
||||
name: "My.Custom.Extension",
|
||||
async getCustomWidgets() {
|
||||
return {
|
||||
// 定义一个名为 "MY_WIDGET_TYPE" 的新部件类型
|
||||
// Define a new widget type named "MY_WIDGET_TYPE"
|
||||
MY_WIDGET_TYPE(node, inputName, inputData, app) {
|
||||
// 1. 创建 HTML 元素
|
||||
// 1. Create the HTML element
|
||||
const container = document.createElement("div");
|
||||
container.innerHTML = "Hello <b>DOMWidget</b>!";
|
||||
|
||||
// 2. 样式设置 (可选)
|
||||
// 2. Setup styles (Optional but recommended)
|
||||
container.style.color = "white";
|
||||
container.style.backgroundColor = "#222";
|
||||
container.style.padding = "5px";
|
||||
|
||||
// 3. 调用 addDOMWidget 并返回结果
|
||||
// 3. Add the DOMWidget and return the result
|
||||
const widget = node.addDOMWidget(inputName, "MY_WIDGET_TYPE", container, {
|
||||
// 配置选项
|
||||
// Configuration options
|
||||
getValue() {
|
||||
return container.innerText;
|
||||
},
|
||||
@@ -47,7 +46,7 @@ app.registerExtension({
|
||||
}
|
||||
});
|
||||
|
||||
// 4. 固定返回格式
|
||||
// 4. Return in the standard format
|
||||
return { widget };
|
||||
}
|
||||
};
|
||||
@@ -57,74 +56,209 @@ app.registerExtension({
|
||||
|
||||
---
|
||||
|
||||
## 3. `addDOMWidget` 详细参数
|
||||
## 3. The `addDOMWidget` API
|
||||
|
||||
```javascript
|
||||
node.addDOMWidget(name, type, element, options)
|
||||
```
|
||||
|
||||
### 参数说明:
|
||||
1. **`name`**: 部件的内部名称(通常匹配输入名称)。
|
||||
2. **`type`**: 部件的类型标识符。
|
||||
3. **`element`**: 实际的 HTML 元素。
|
||||
4. **`options`**: (Object) 包含生命周期和行为的配置项。
|
||||
### Parameters
|
||||
1. **`name`**: The internal name of the widget (usually matches the input name).
|
||||
2. **`type`**: The type identifier for the widget.
|
||||
3. **`element`**: The actual HTMLElement to embed.
|
||||
4. **`options`**: (Object) Configuration for lifecycle, sizing, and persistence.
|
||||
|
||||
### `options` 常用字段:
|
||||
| 字段 | 类型 | 说明 |
|
||||
### Common `options` Fields
|
||||
| Field | Type | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| `getValue` | `Function` | 定义部件序列化时的值来源。 |
|
||||
| `setValue` | `Function` | 定义如何从工作流数据中恢复部件状态。 |
|
||||
| `getMinHeight` | `Function` | 返回部件的最小像素高度。 |
|
||||
| `getHeight` | `Function` | 返回部件的期望高度(可返回百分比字符串,如 `"50%"`)。 |
|
||||
| `hideOnZoom`| `Boolean` | 当 Canvas 缩小到一定程度时是否隐藏 DOM 元素(提高性能,默认 `true`)。 |
|
||||
| `onDraw` | `Function` | 每一帧绘制时触发,可用于在 Canvas 上做额外标注。 |
|
||||
| `afterResize` | `Function` | 节点缩放后的回调。 |
|
||||
| `getValue` | `Function` | Defines how to retrieve the widget's value for serialization. |
|
||||
| `setValue` | `Function` | Defines how to restore the widget's state from workflow data. |
|
||||
| `getMinHeight` | `Function` | Returns the minimum height in pixels. |
|
||||
| `getHeight` | `Function` | Returns the preferred height (supports numbers or percentage strings like `"50%"`). |
|
||||
| `onResize` | `Function` | Callback triggered when the widget is resized. |
|
||||
| `hideOnZoom`| `Boolean` | Whether to hide the DOM element when zoomed out to improve performance (default: `true`). |
|
||||
| `selectOn` | `string[]` | Events on the element that should trigger node selection (default: `['focus', 'click']`). |
|
||||
|
||||
---
|
||||
|
||||
## 4. 样式与布局管理
|
||||
## 4. Size Control
|
||||
|
||||
ComfyUI 前端使用 CSS 变量来协调 Canvas 节点与 DOM 部件的尺寸。你可以在元素的 `style` 或 CSS 中设置这些变量:
|
||||
Custom DOMWidgets must actively inform the parent Node of their size requirements to ensure the Node layout is calculated correctly and connection wires remain aligned.
|
||||
|
||||
### 4.1 Core Mechanism
|
||||
|
||||
Whether in Canvas Mode or Vue Mode, the underlying logic model (`LGraphNode`) calls the widget's `computeLayoutSize` method to determine dimensions. This logic is used to calculate the Node's total size and the position of input/output slots.
|
||||
|
||||
### 4.2 Controlling Height
|
||||
|
||||
It is recommended to use the `options` parameter to define height behavior.
|
||||
|
||||
**Method 1: Using `options` (Recommended)**
|
||||
|
||||
```javascript
|
||||
container.style.setProperty('--comfy-widget-min-height', '100px');
|
||||
container.style.setProperty('--comfy-widget-max-height', '500px');
|
||||
const widget = node.addDOMWidget("MyWidget", "custom", element, {
|
||||
// Specify minimum height in pixels
|
||||
getMinHeight: () => 150,
|
||||
|
||||
// Or specify preferred height (pixels or percentage string)
|
||||
// getHeight: () => "50%",
|
||||
});
|
||||
```
|
||||
|
||||
由于 `DOMWidget` 被放置在绝对定位的容器中,建议容器元素的样式设为:
|
||||
**Method 2: Using CSS Variables**
|
||||
|
||||
You can also set specific CSS variables on the root element:
|
||||
|
||||
```javascript
|
||||
element.style.setProperty("--comfy-widget-min-height", "150px");
|
||||
// or --comfy-widget-height
|
||||
```
|
||||
|
||||
### 4.3 Controlling Width
|
||||
|
||||
By default, a DOMWidget's width automatically stretches to fit the Node's width (which is determined by the Title or other Input Slots).
|
||||
|
||||
If you must **force the Node to be wider** to accommodate your widget, you need to override the widget instance's `computeLayoutSize` method:
|
||||
|
||||
```javascript
|
||||
const widget = node.addDOMWidget("WideWidget", "custom", element);
|
||||
|
||||
// Override the default layout calculation
|
||||
widget.computeLayoutSize = (targetNode) => {
|
||||
return {
|
||||
minHeight: 150, // Must return height
|
||||
minWidth: 300 // Force the Node to be at least 300px wide
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 4.4 Dynamic Resizing
|
||||
|
||||
If your widget's content changes dynamically (e.g., expanding sections, loading images), you must manually trigger a node resize:
|
||||
|
||||
```javascript
|
||||
// Execute after internal size changes:
|
||||
node.setSize(node.computeSize());
|
||||
node.setDirtyCanvas(true, true); // Force redraw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. State Persistence (Serialization)
|
||||
|
||||
### 5.1 Default Behavior
|
||||
|
||||
DOMWidgets have **serialization enabled** by default (`serialize` property is `true`).
|
||||
* **Saving**: ComfyUI attempts to read the widget's value to save into the Workflow file.
|
||||
* **Loading**: ComfyUI reads the value from the Workflow file and assigns it to the widget.
|
||||
|
||||
### 5.2 Custom Serialization
|
||||
|
||||
To make persistence work effectively (saving internal DOM state and restoring it), you must implement `getValue` and `setValue` in the `options`:
|
||||
|
||||
* **`getValue`**: Returns the state to be saved (Number, String, or Object).
|
||||
* **`setValue`**: Receives the restored value and updates the DOM element.
|
||||
|
||||
**Example:**
|
||||
|
||||
```javascript
|
||||
const inputEl = document.createElement("input");
|
||||
const widget = node.addDOMWidget("MyInput", "custom", inputEl, {
|
||||
// 1. Called during Save
|
||||
getValue: () => {
|
||||
return inputEl.value;
|
||||
},
|
||||
// 2. Called during Load or Copy/Paste
|
||||
setValue: (value) => {
|
||||
inputEl.value = value || "";
|
||||
}
|
||||
});
|
||||
|
||||
// Optional: Listen for changes to update widget.value immediately
|
||||
inputEl.addEventListener("change", () => {
|
||||
widget.value = inputEl.value; // Triggers callbacks
|
||||
});
|
||||
```
|
||||
|
||||
### 5.3 The Restoration Mechanism (`configure`)
|
||||
|
||||
* **`configure(data)`**: When a Workflow is loaded, `LGraphNode` calls its `configure(data)` method.
|
||||
* **`setValue` Chain**: During `configure`, the Node iterates over the saved `widgets_values` array and assigns each value (`widget.value = savedValue`). For DOMWidgets, this assignment triggers the `setValue` callback defined in your options.
|
||||
|
||||
Therefore, `options.setValue` is the critical hook for restoring widget state.
|
||||
|
||||
### 5.4 Disabling Serialization
|
||||
|
||||
If your widget is purely for display (e.g., a real-time monitor or generated chart) and doesn't need to save state, disable serialization to reduce workflow file size.
|
||||
|
||||
**Note**: You cannot set this via `options`. You must modify the widget instance directly.
|
||||
|
||||
```javascript
|
||||
const widget = node.addDOMWidget("DisplayOnly", "custom", element);
|
||||
widget.serialize = false; // Explicitly disable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Lifecycle & Events
|
||||
|
||||
### 6.1 `onResize`
|
||||
|
||||
When the Node size changes (e.g., user drags the corner), the widget can receive a notification via `options`:
|
||||
|
||||
```javascript
|
||||
const widget = node.addDOMWidget("ResizingWidget", "custom", element, {
|
||||
onResize: (w) => {
|
||||
// 'w' is the widget instance
|
||||
// Adjust internal DOM layout here if necessary
|
||||
console.log("Widget resized");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 6.2 Construction & Mounting
|
||||
|
||||
* **Construction**: Occurs immediately when `addDOMWidget` is called.
|
||||
* **Mounting**:
|
||||
* **Canvas Mode**: Appended to `.dom-widget-container` via `DomWidget.vue`.
|
||||
* **Vue Mode**: Appended inside the Node component via `WidgetDOM.vue`.
|
||||
* **Caution**: When `addDOMWidget` returns, the element may not be in the `document.body` yet. If you need to access layout properties like `getBoundingClientRect`, use `setTimeout` or wait for the first `onResize`.
|
||||
|
||||
### 6.3 Cleanup
|
||||
|
||||
If you create external references (like `setInterval` or global event listeners), ensure you clean them up using `node.onRemoved`:
|
||||
|
||||
```javascript
|
||||
node.onRemoved = function() {
|
||||
clearInterval(myInterval);
|
||||
// Call original onRemoved if it existed
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Styling & Best Practices
|
||||
|
||||
### 7.1 Styling
|
||||
Since DOMWidgets are placed in absolute positioned containers or managed by Vue, ensure your container handles sizing gracefully:
|
||||
|
||||
```javascript
|
||||
container.style.width = "100%";
|
||||
container.style.boxSizing = "border-box";
|
||||
```
|
||||
|
||||
---
|
||||
### 7.2 Path References
|
||||
When importing `app`, adjust the path based on your extension's folder depth. Typically:
|
||||
`import { app } from "../../scripts/app.js";`
|
||||
|
||||
## 5. 生命周期与交互
|
||||
|
||||
如果你需要访问其他部件或在特定时刻触发逻辑,可以结合 `nodeCreated` 钩子:
|
||||
|
||||
```javascript
|
||||
app.registerExtension({
|
||||
name: "My.Lifecycle.Extension",
|
||||
nodeCreated(node) {
|
||||
if (node.comfyClass === "MyCustomNode") {
|
||||
// 查找刚才创建好的 DOMWidget
|
||||
const myWidget = node.widgets.find(w => w.name === "my_input");
|
||||
|
||||
// 可以在这里绑定事件
|
||||
myWidget.element.addEventListener("click", () => {
|
||||
console.log("Widget clicked!", myWidget.value);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
### 7.3 Security
|
||||
If setting `innerHTML` dynamically, ensure the content is sanitized or trusted to prevent XSS attacks.
|
||||
|
||||
---
|
||||
|
||||
## 6. 完整实战示例: 简易文本预览器
|
||||
## 8. Complete Example: Text Counter
|
||||
|
||||
这个示例实现了一个动态显示文本字数统计的预览器部件。
|
||||
This example implements a simple widget that displays the character count of another text widget in the same node.
|
||||
|
||||
```javascript
|
||||
import { app } from "../../scripts/app.js";
|
||||
@@ -135,23 +269,29 @@ app.registerExtension({
|
||||
return {
|
||||
TEXT_COUNTER(node, inputName) {
|
||||
const el = document.createElement("div");
|
||||
el.style.background = "#222";
|
||||
el.style.border = "1px solid #444";
|
||||
el.style.padding = "8px";
|
||||
el.style.borderRadius = "4px";
|
||||
el.style.fontSize = "12px";
|
||||
Object.assign(el.style, {
|
||||
background: "#222",
|
||||
border: "1px solid #444",
|
||||
padding: "8px",
|
||||
borderRadius: "4px",
|
||||
fontSize: "12px",
|
||||
color: "#eee"
|
||||
});
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.innerText = "Characters: 0";
|
||||
el.appendChild(label);
|
||||
|
||||
const widget = node.addDOMWidget(inputName, "TEXT_COUNTER", el, {
|
||||
getValue() { return node.widgets[0]?.value || ""; },
|
||||
setValue(v) { /* 逻辑通常由上游触发 */ },
|
||||
getValue() { return ""; }, // Nothing to save
|
||||
setValue(v) { }, // Nothing to restore
|
||||
getMinHeight() { return 40; }
|
||||
});
|
||||
|
||||
// Disable serialization for this display-only widget
|
||||
widget.serialize = false;
|
||||
|
||||
// 设置一个自定义更新逻辑
|
||||
// Custom method to update UI
|
||||
widget.updateCount = (text) => {
|
||||
label.innerText = `Characters: ${text.length}`;
|
||||
};
|
||||
@@ -161,12 +301,13 @@ app.registerExtension({
|
||||
};
|
||||
},
|
||||
nodeCreated(node) {
|
||||
// Logic to link widgets after the node is initialized
|
||||
if (node.comfyClass === "MyTextNode") {
|
||||
const counterWidget = node.widgets.find(w => w.type === "TEXT_COUNTER");
|
||||
const textWidget = node.widgets.find(w => w.name === "text");
|
||||
|
||||
if (counterWidget && textWidget) {
|
||||
// 监听文本部件的回调
|
||||
// Hook into the text widget's callback
|
||||
const oldCallback = textWidget.callback;
|
||||
textWidget.callback = function(v) {
|
||||
if (oldCallback) oldCallback.apply(this, arguments);
|
||||
@@ -177,12 +318,3 @@ app.registerExtension({
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 注意事项
|
||||
|
||||
1. **路径引用**: 引用 `app` 时请根据你的插件目录深度调整路径,通常是 `../../scripts/app.js`。
|
||||
2. **清理**: 如果你创建了外部引用(如 `setInterval` 或全局监听),请确在 `node.onRemoved` 中进行清理。
|
||||
3. **安全性**: 如果动态设置 `innerHTML`,请确保内容来源可靠,防止 XSS 攻击。
|
||||
4. **性能**: 对于高频更新的 DOM 元素,注意不要触发过多的重排(Reflow)。
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
"""
|
||||
Demo node to showcase Vue + PrimeVue widget integration in ComfyUI LoRA Manager.
|
||||
|
||||
This node demonstrates:
|
||||
- Vue 3 + PrimeVue custom widget
|
||||
- Widget state serialization
|
||||
- Integration with ComfyUI workflow
|
||||
"""
|
||||
|
||||
|
||||
class LoraManagerDemoNode:
|
||||
"""
|
||||
A demo node that uses a Vue + PrimeVue widget to configure LoRA parameters.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"lora_demo_widget": ("LORA_DEMO_WIDGET", {}),
|
||||
},
|
||||
"optional": {
|
||||
"text": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": True,
|
||||
"placeholder": "Additional prompt text..."
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "FLOAT", "STRING")
|
||||
RETURN_NAMES = ("model_name", "strength", "info")
|
||||
|
||||
FUNCTION = "process"
|
||||
|
||||
CATEGORY = "loramanager/demo"
|
||||
|
||||
def process(self, lora_demo_widget, text=""):
|
||||
"""
|
||||
Process the widget data and return the configuration.
|
||||
|
||||
Args:
|
||||
lora_demo_widget: Widget data containing model_name and strength
|
||||
text: Optional text input
|
||||
|
||||
Returns:
|
||||
Tuple of (model_name, strength, info_string)
|
||||
"""
|
||||
model_name = lora_demo_widget.get("modelName", "")
|
||||
strength = lora_demo_widget.get("strength", 1.0)
|
||||
|
||||
info = f"Vue Widget Demo - Model: {model_name}, Strength: {strength}"
|
||||
if text:
|
||||
info += f"\nAdditional text: {text}"
|
||||
|
||||
print(f"[LoraManagerDemoNode] {info}")
|
||||
|
||||
return (model_name, strength, info)
|
||||
|
||||
|
||||
# Node class mappings for ComfyUI
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"LoraManagerDemoNode": LoraManagerDemoNode
|
||||
}
|
||||
|
||||
# Display name mappings
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"LoraManagerDemoNode": "LoRA Manager Demo (Vue)"
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
# Demo Widget Testing Instructions
|
||||
|
||||
## What Was Created
|
||||
|
||||
A complete Vue + PrimeVue development scaffold for creating custom ComfyUI widgets, including a working demo to validate the entire workflow.
|
||||
|
||||
## Components
|
||||
|
||||
### 1. Python Node (`py/nodes/demo_vue_widget_node.py`)
|
||||
- **Node Name**: LoRA Manager Demo (Vue)
|
||||
- **Category**: loramanager/demo
|
||||
- **Inputs**:
|
||||
- `lora_demo_widget` (custom widget)
|
||||
- `text` (optional string)
|
||||
- **Outputs**:
|
||||
- `model_name` (STRING)
|
||||
- `strength` (FLOAT)
|
||||
- `info` (STRING)
|
||||
|
||||
### 2. Vue Widget (`vue-widgets/src/components/DemoWidget.vue`)
|
||||
Uses PrimeVue components:
|
||||
- **InputText** - For model name input
|
||||
- **InputNumber** - For strength value (0-2, step 0.1) with +/- buttons
|
||||
- **Button** - Apply and Reset actions
|
||||
- **Card** - Display current configuration
|
||||
|
||||
### 3. Build System
|
||||
- **Vite** - Fast build tool
|
||||
- **TypeScript** - Type-safe development
|
||||
- **Output**: `web/comfyui/vue-widgets/demo-widget.js`
|
||||
|
||||
## How to Test
|
||||
|
||||
### 1. Build the Widget (if not already built)
|
||||
|
||||
```bash
|
||||
cd vue-widgets
|
||||
npm install # Only needed once
|
||||
npm run build
|
||||
```
|
||||
|
||||
### 2. Start/Restart ComfyUI
|
||||
|
||||
```bash
|
||||
# In your ComfyUI root directory
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 3. Add the Demo Node
|
||||
|
||||
1. Open ComfyUI in your browser (usually http://localhost:8188)
|
||||
2. Right-click on the canvas → **Add Node**
|
||||
3. Navigate to: **loramanager** → **demo** → **LoRA Manager Demo (Vue)**
|
||||
4. The node should appear with a Vue-powered widget inside
|
||||
|
||||
### 4. Test the Widget
|
||||
|
||||
The widget provides an interactive demo:
|
||||
|
||||
1. **Enter a model name** in the text field (e.g., "test-lora-model")
|
||||
2. **Adjust the strength** using the number input or +/- buttons (0.0 - 2.0)
|
||||
3. **Click "Apply"** to set the configuration
|
||||
4. A card will appear showing the current configuration
|
||||
5. **Click "Reset"** to clear everything
|
||||
|
||||
### 5. Test Workflow Integration
|
||||
|
||||
1. Add some input/output nodes to create a minimal workflow
|
||||
2. Connect the demo node outputs to other nodes:
|
||||
- `model_name` → Can connect to any STRING input
|
||||
- `strength` → Can connect to any FLOAT input
|
||||
- `info` → Informational STRING output
|
||||
3. Click **Queue Prompt** to execute the workflow
|
||||
4. Check the console/terminal - you should see:
|
||||
```
|
||||
[LoraManagerDemoNode] Vue Widget Demo - Model: test-lora-model, Strength: 1.5
|
||||
```
|
||||
|
||||
### 6. Test State Persistence
|
||||
|
||||
1. Configure the widget (set model name and strength, click Apply)
|
||||
2. Save the workflow (Ctrl+S / Cmd+S)
|
||||
3. Reload the page
|
||||
4. Load the saved workflow
|
||||
5. The widget should restore its state
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
✅ **Success Indicators:**
|
||||
- Widget appears inside the node with proper styling
|
||||
- PrimeVue components are rendered correctly
|
||||
- Buttons respond to clicks
|
||||
- Input values update reactively
|
||||
- Configuration card appears after clicking Apply
|
||||
- Node outputs the correct data when workflow executes
|
||||
- State persists when saving/loading workflows
|
||||
|
||||
❌ **Common Issues:**
|
||||
|
||||
**Widget doesn't appear:**
|
||||
- Check browser console for JavaScript errors
|
||||
- Verify `web/comfyui/vue-widgets/demo-widget.js` exists
|
||||
- Restart ComfyUI completely
|
||||
|
||||
**Build errors:**
|
||||
- Make sure you're in the `vue-widgets` directory when running npm commands
|
||||
- Check Node.js version: `node --version` (should be 18+)
|
||||
- Try deleting `node_modules` and running `npm install` again
|
||||
|
||||
**Widget shows but crashes:**
|
||||
- Check browser console for errors
|
||||
- Verify PrimeVue components are imported correctly
|
||||
- Check that the widget type matches between Python and JavaScript
|
||||
|
||||
## Development Workflow
|
||||
|
||||
For active development:
|
||||
|
||||
```bash
|
||||
# Terminal 1: Watch mode for auto-rebuild
|
||||
cd vue-widgets
|
||||
npm run dev
|
||||
|
||||
# Terminal 2: ComfyUI server
|
||||
cd ../../.. # Back to ComfyUI root
|
||||
python main.py
|
||||
```
|
||||
|
||||
When you make changes to Vue files:
|
||||
1. Vite automatically rebuilds
|
||||
2. Hard refresh the browser (Ctrl+Shift+R / Cmd+Shift+R)
|
||||
3. Changes should appear
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that the demo works, you can:
|
||||
|
||||
1. **Modify the demo widget** to add more features
|
||||
2. **Create new widgets** for actual LoRA Manager functionality
|
||||
3. **Add more PrimeVue components** (see [PrimeVue Docs](https://primevue.org/))
|
||||
4. **Integrate with the LoRA Manager API** to fetch real data
|
||||
5. **Add styling** to match ComfyUI's theme better
|
||||
|
||||
## File Structure Reference
|
||||
|
||||
```
|
||||
ComfyUI-Lora-Manager/
|
||||
├── vue-widgets/ # Vue source code
|
||||
│ ├── src/
|
||||
│ │ ├── main.ts # Extension registration
|
||||
│ │ └── components/
|
||||
│ │ └── DemoWidget.vue # Demo widget component
|
||||
│ ├── package.json # Dependencies
|
||||
│ ├── vite.config.mts # Build config
|
||||
│ ├── tsconfig.json # TypeScript config
|
||||
│ ├── README.md # Development guide
|
||||
│ └── DEMO_INSTRUCTIONS.md # This file
|
||||
│
|
||||
├── web/comfyui/
|
||||
│ └── vue-widgets/ # Build output (gitignored)
|
||||
│ ├── demo-widget.js # Compiled JavaScript
|
||||
│ └── assets/
|
||||
│ └── demo-widget-*.css # Compiled CSS
|
||||
│
|
||||
├── py/nodes/
|
||||
│ └── demo_vue_widget_node.py # Python node definition
|
||||
│
|
||||
├── __init__.py # Updated with demo node
|
||||
├── VUE_WIDGETS_SETUP.md # Complete setup guide
|
||||
└── .gitignore # Updated to ignore build output
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
1. Check the browser console for errors
|
||||
2. Check the ComfyUI terminal for Python errors
|
||||
3. Review `VUE_WIDGETS_SETUP.md` for detailed documentation
|
||||
4. Review `vue-widgets/README.md` for development guide
|
||||
@@ -1,6 +1,5 @@
|
||||
import { createApp, type App as VueApp } from 'vue'
|
||||
import PrimeVue from 'primevue/config'
|
||||
import DemoWidget from '@/components/DemoWidget.vue'
|
||||
import LoraPoolWidget from '@/components/LoraPoolWidget.vue'
|
||||
|
||||
// @ts-ignore - ComfyUI external module
|
||||
@@ -8,51 +7,6 @@ import { app } from '../../../scripts/app.js'
|
||||
|
||||
const vueApps = new Map<number, VueApp>()
|
||||
|
||||
// @ts-ignore
|
||||
function createVueWidget(node) {
|
||||
const container = document.createElement('div')
|
||||
container.id = `lora-manager-demo-widget-${node.id}`
|
||||
container.style.width = '100%'
|
||||
container.style.height = '100%'
|
||||
container.style.display = 'flex'
|
||||
container.style.flexDirection = 'column'
|
||||
container.style.overflow = 'hidden'
|
||||
|
||||
const widget = node.addDOMWidget(
|
||||
'lora_demo_widget',
|
||||
'lora-manager-demo',
|
||||
container,
|
||||
{
|
||||
getMinHeight: () => 320,
|
||||
hideOnZoom: false,
|
||||
serialize: true
|
||||
}
|
||||
)
|
||||
|
||||
const vueApp = createApp(DemoWidget, {
|
||||
widget,
|
||||
node
|
||||
})
|
||||
|
||||
vueApp.use(PrimeVue, {
|
||||
unstyled: true,
|
||||
ripple: false
|
||||
})
|
||||
|
||||
vueApp.mount(container)
|
||||
vueApps.set(node.id, vueApp)
|
||||
|
||||
widget.onRemove = () => {
|
||||
const vueApp = vueApps.get(node.id)
|
||||
if (vueApp) {
|
||||
vueApp.unmount()
|
||||
vueApps.delete(node.id)
|
||||
}
|
||||
}
|
||||
|
||||
return { widget }
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
function createLoraPoolWidget(node) {
|
||||
const container = document.createElement('div')
|
||||
@@ -68,8 +22,7 @@ function createLoraPoolWidget(node) {
|
||||
'LORA_POOL_CONFIG',
|
||||
container,
|
||||
{
|
||||
getMinHeight: () => 680,
|
||||
hideOnZoom: false,
|
||||
// getMinHeight: () => 680,
|
||||
serialize: true
|
||||
}
|
||||
)
|
||||
@@ -95,6 +48,11 @@ function createLoraPoolWidget(node) {
|
||||
}
|
||||
}
|
||||
|
||||
widget.computeLayoutSize = () => ({
|
||||
minHeight: 600,
|
||||
minWidth: 500
|
||||
})
|
||||
|
||||
return { widget }
|
||||
}
|
||||
|
||||
@@ -103,10 +61,6 @@ app.registerExtension({
|
||||
|
||||
getCustomWidgets() {
|
||||
return {
|
||||
// @ts-ignore
|
||||
LORA_DEMO_WIDGET(node) {
|
||||
return createVueWidget(node)
|
||||
},
|
||||
// @ts-ignore
|
||||
LORA_POOL_CONFIG(node) {
|
||||
return createLoraPoolWidget(node)
|
||||
|
||||
@@ -17,7 +17,7 @@ export default defineConfig({
|
||||
lib: {
|
||||
entry: resolve(__dirname, './src/main.ts'),
|
||||
formats: ['es'],
|
||||
fileName: 'demo-widget'
|
||||
fileName: 'lora-manager-widgets'
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [
|
||||
@@ -25,7 +25,7 @@ export default defineConfig({
|
||||
],
|
||||
output: {
|
||||
dir: '../web/comfyui/vue-widgets',
|
||||
entryFileNames: 'demo-widget.js',
|
||||
entryFileNames: 'lora-manager-widgets.js',
|
||||
chunkFileNames: 'assets/[name]-[hash].js',
|
||||
assetFileNames: 'assets/[name]-[hash][extname]'
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
web/comfyui/vue-widgets/lora-manager-widgets.js.map
Normal file
1
web/comfyui/vue-widgets/lora-manager-widgets.js.map
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user