wip: create a test widget to test

This commit is contained in:
Rizumu Ayaka
2025-08-29 02:14:26 +08:00
parent d9efa78723
commit 5f8e5365ed
8 changed files with 364 additions and 9 deletions

View File

@@ -26,6 +26,8 @@ const TYPE_TO_ENUM_MAP: Record<string, string> = {
// Selection
combo: WidgetType.COMBO,
COMBO: WidgetType.COMBO,
selectbutton: WidgetType.SELECTBUTTON,
SELECTBUTTON: WidgetType.SELECTBUTTON,
// Boolean
toggle: WidgetType.TOGGLESWITCH,

View File

@@ -902,14 +902,14 @@ export const CORE_SETTINGS: SettingParams[] = [
type: 'hidden',
tooltip:
'Render nodes as Vue components instead of canvas. Hidden; toggle via Experimental keybinding.',
defaultValue: false,
defaultValue: true,
experimental: true
},
{
id: 'Comfy.VueNodes.DebugPanel.Visible',
name: 'Vue Nodes Debug Panel Visible (hidden)',
type: 'hidden',
defaultValue: false,
defaultValue: true,
experimental: true
}
]

View File

@@ -0,0 +1,216 @@
# Test Widgets 节点使用说明
## 概述
这是一个专门用于测试 ComfyUI 各种 Widget 组件的测试节点。它包含了前端支持的所有 widget 类型,方便开发者测试和学习不同组件的使用方法。
## 如何使用
### 1. 启动应用
重新启动 ComfyUI 前端应用以加载新的扩展。
### 2. 添加测试节点
在节点搜索中输入 "Test Widgets" 或者在 "testing" 分类中找到 "Test Widgets" 节点。
### 3. 测试各种 Widget
#### 基础 Widget 类型 (Required)
- **string_widget**: 文本输入框
- **int_widget**: 整数滑块 (0-100)
- **float_widget**: 浮点数滑块 (0.0-10.0)
- **boolean_widget**: 布尔切换开关
- **combo_widget**: 下拉选择框
- **color_widget**: 颜色选择器
- **textarea_widget**: 多行文本输入
- **file_widget**: 文件上传
- **image_widget**: 图片显示
- **markdown_widget**: Markdown 编辑器
#### 高级 Widget 类型 (Optional)
- **selectbutton_widget**: 选择按钮组 (Small/Medium/Large/Extra Large)
- **multiselect_widget**: 多选组件 (颜色选择)
- **treeselect_widget**: 树形选择器 (水果/蔬菜分类)
- **chart_widget**: 图表组件
- **slider_widget**: 数值滑块
- **toggleswitch_widget**: 切换开关
- **button_widget**: 点击按钮
## Widget 配置示例
### SelectButton Widget
```typescript
selectbutton_widget: ['SELECTBUTTON', {
values: ['Small', 'Medium', 'Large', 'Extra Large'],
default: 'Medium'
}]
```
### MultiSelect Widget
```typescript
multiselect_widget: ['MULTISELECT', {
values: ['Red', 'Green', 'Blue', 'Yellow', 'Purple'],
default: ['Red', 'Blue']
}]
```
### TreeSelect Widget
```typescript
treeselect_widget: ['TREESELECT', {
values: [
{
label: 'Fruits',
value: 'fruits',
children: [
{ label: 'Apple', value: 'apple' },
{ label: 'Orange', value: 'orange' }
]
}
]
}]
```
## 输出
节点的输出会显示所有 widget 的当前值,以 JSON 格式呈现:
```json
{
"string_widget": "Hello World",
"int_widget": 42,
"selectbutton_widget": "Medium",
"multiselect_widget": ["Red", "Blue"],
...
}
```
## 开发用途
这个测试节点主要用于:
1. **Widget 开发测试**: 验证新开发的 widget 组件功能
2. **界面设计**: 查看不同 widget 的外观和交互效果
3. **配置学习**: 了解各种 widget 的配置参数
4. **功能演示**: 向其他开发者展示 ComfyUI 的 widget 能力
## 注意事项
- 这是一个开发和测试用的节点,不建议在生产工作流中使用
- 某些 widget (如 IMAGE) 可能需要实际的数据输入才能正常显示
- 按钮点击会触发 alert 提示,这是为了演示按钮功能
## 文件修改清单
在创建此测试组件过程中,对以下文件进行了修改:
### 📁 新增文件
- `src/extensions/core/testWidgets.ts` - 主测试节点文件
- `src/extensions/core/TEST_WIDGETS_README.md` - 本说明文档
### 🔧 修改文件
#### 1. `src/extensions/core/index.ts`
```diff
+ import './testWidgets'
```
**用途**: 注册测试扩展到核心扩展系统
#### 2. `src/schemas/nodeDef/migration.ts`
```diff
// 在 transformInputSpecV1ToV2 函数中添加
+ // Special handling for SELECTBUTTON to ensure options.values is properly set
+ if (inputSpecV1[0] === 'SELECTBUTTON' && options.values) {
+ result.options = {
+ ...result.options,
+ values: options.values
+ }
+ }
```
**用途**: 修复 SELECTBUTTON widget 的数据传递问题
#### 3. `src/renderer/extensions/vueNodes/widgets/components/WidgetSelectButton.vue`
```diff
// 模板部分
- v-bind="filteredProps"
+ :options="filteredProps.values || filteredProps.options || []"
// Script 部分添加了调试日志和数据处理
+ const filteredProps = computed(() => {
+ const filtered = filterWidgetProps(props.widget.options, STANDARD_EXCLUDED_PROPS)
+ console.log('WidgetSelectButton filteredProps:', filtered)
+ console.log('Widget options:', props.widget.options)
+
+ // Ensure options array is available for SelectButton
+ if (filtered.values && Array.isArray(filtered.values)) {
+ filtered.options = filtered.values
+ }
+
+ return filtered
+ })
```
**用途**: 修复 PrimeVue SelectButton 组件的属性绑定问题
### 🔄 其他修改的文件
以下文件也被 git 记录为修改,但主要是开发过程中的临时修改:
- `src/components/graph/debug/VueNodeDebugPanel.vue`
- `src/composables/graph/useWidgetRenderer.ts`
- `src/constants/coreSettings.ts`
- `src/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.ts`
## 完整还原指南
### 🗑️ 快速清理(推荐)
如果要完全移除测试组件和相关修改:
```bash
# 1. 删除新增的文件
rm src/extensions/core/testWidgets.ts
rm src/extensions/core/TEST_WIDGETS_README.md
# 2. 还原所有修改的文件
git checkout src/extensions/core/index.ts
git checkout src/schemas/nodeDef/migration.ts
git checkout src/renderer/extensions/vueNodes/widgets/components/WidgetSelectButton.vue
# 3. 清理其他临时修改(可选)
git checkout src/components/graph/debug/VueNodeDebugPanel.vue
git checkout src/composables/graph/useWidgetRenderer.ts
git checkout src/constants/coreSettings.ts
git checkout src/renderer/extensions/vueNodes/widgets/composables/useWidgetRenderer.ts
# 4. 重新启动应用
```
### 🛠️ 选择性保留
如果想保留 SelectButton 修复但移除测试组件:
```bash
# 1. 只删除测试相关文件
rm src/extensions/core/testWidgets.ts
rm src/extensions/core/TEST_WIDGETS_README.md
# 2. 只还原扩展注册
git checkout src/extensions/core/index.ts
# 3. 保留以下修复这些是有用的bug修复
# - src/schemas/nodeDef/migration.ts
# - src/renderer/extensions/vueNodes/widgets/components/WidgetSelectButton.vue
```
### 📝 手动还原
如果不使用 git可以手动还原
#### `src/extensions/core/index.ts`
移除这一行:
```typescript
import './testWidgets'
```
#### `src/schemas/nodeDef/migration.ts`
删除 SELECTBUTTON 的特殊处理代码第130-135行左右
#### `src/renderer/extensions/vueNodes/widgets/components/WidgetSelectButton.vue`
-`:options="filteredProps.values || filteredProps.options || []"` 改回 `v-bind="filteredProps"`
- 移除 console.log 调试语句
- 恢复原始的 `filteredProps` computed 函数
## 注意事项
- ⚠️ **SelectButton 修复很重要**: `migration.ts``WidgetSelectButton.vue` 的修改修复了实际的 bug建议保留
- 🧹 **清理调试日志**: 如果保留修复,记得移除 console.log 调试语句
- 🔄 **重启应用**: 任何文件修改后都需要重新启动应用才能生效
- 💾 **备份重要修改**: 在还原前考虑是否需要保留某些有用的修复

View File

@@ -16,6 +16,7 @@ import './saveImageExtraOutput'
import './saveMesh'
import './simpleTouchSupport'
import './slotDefaults'
import './testWidgets'
import './uploadAudio'
import './uploadImage'
import './webcamCapture'

View File

@@ -0,0 +1,113 @@
/**
* Test Widgets Extension - 用于测试各种 Widget 组件的测试节点
*/
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { app } from '@/scripts/app'
import { ComfyWidgets } from '@/scripts/widgets'
app.registerExtension({
name: 'comfy.TestWidgets',
registerCustomNodes() {
class TestWidgetsNode extends LGraphNode {
static override title = 'Test Widgets'
static override category = 'testing'
constructor(title: string) {
super(title)
this.title = 'Test Widgets'
this.size = [450, 800]
// 添加输出端口
this.addOutput('output', 'STRING')
// 添加基础 widgets
this.addTestWidgets()
this.serialize_widgets = true
}
addTestWidgets() {
try {
// 主题选择器
ComfyWidgets.SELECTBUTTON(
this,
'theme_selector',
[
'SELECTBUTTON',
{
values: ['Light', 'Dark', 'Auto'],
default: 'Auto'
}
],
app
)
// 质量设置
ComfyWidgets.SELECTBUTTON(
this,
'quality_setting',
[
'SELECTBUTTON',
{
values: ['Low', 'Medium', 'High', 'Ultra'],
default: 'High'
}
],
app
)
// 方向选择
ComfyWidgets.SELECTBUTTON(
this,
'orientation',
[
'SELECTBUTTON',
{
values: ['Portrait', 'Landscape', 'Square'],
default: 'Portrait'
}
],
app
)
// 6. 可选的高级 widgets
if (ComfyWidgets.COLOR) {
ComfyWidgets.COLOR(
this,
'color_test',
['COLOR', { default: '#ff0000' }],
app
)
}
if (ComfyWidgets.MARKDOWN) {
ComfyWidgets.MARKDOWN(
this,
'markdown_test',
['MARKDOWN', { default: '# Test Markdown' }],
app
)
}
} catch (error) {
console.error('Error adding widgets:', error)
}
}
override onExecute() {
// 收集所有 widget 的值
const values: Record<string, any> = {}
if (this.widgets) {
for (const widget of this.widgets) {
values[widget.name] = widget.value
}
}
// 输出 JSON 格式的值
this.setOutputData(0, JSON.stringify(values, null, 2))
}
}
LiteGraph.registerNodeType('TestWidgets', TestWidgetsNode)
}
})

View File

@@ -2,7 +2,7 @@
<WidgetLayoutField :widget="widget">
<SelectButton
v-model="localValue"
v-bind="filteredProps"
:options="filteredProps.values || filteredProps.options || []"
:disabled="readonly"
class="w-full text-xs"
:pt="{
@@ -46,9 +46,21 @@ const { localValue, onChange } = useWidgetValue({
emit
})
const filteredProps = computed(() =>
filterWidgetProps(props.widget.options, STANDARD_EXCLUDED_PROPS)
)
const filteredProps = computed(() => {
const filtered = filterWidgetProps(
props.widget.options,
STANDARD_EXCLUDED_PROPS
)
console.log('WidgetSelectButton filteredProps:', filtered)
console.log('Widget options:', props.widget.options)
// Ensure options array is available for SelectButton
if (filtered.values && Array.isArray(filtered.values)) {
filtered.options = filtered.values
}
return filtered
})
</script>
<style scoped>

View File

@@ -23,6 +23,8 @@ const TYPE_TO_ENUM_MAP: Record<string, string> = {
// Selection
combo: WidgetType.COMBO,
COMBO: WidgetType.COMBO,
selectbutton: WidgetType.SELECTBUTTON,
SELECTBUTTON: WidgetType.SELECTBUTTON,
// Boolean
toggle: WidgetType.TOGGLESWITCH,

View File

@@ -101,13 +101,12 @@ export function transformInputSpecV1ToV2(
const { name, isOptional = false } = kwargs
// Extract options from the input spec
const options = inputSpecV1[1] || {}
// Base properties for all input types
const baseProps = {
name,
isOptional,
...options
...(inputSpecV1[1] || {})
}
// Handle different input types
@@ -121,10 +120,20 @@ export function transformInputSpecV1ToV2(
}
} else if (typeof inputSpecV1[0] === 'string') {
// Handle standard types (INT, FLOAT, BOOLEAN, STRING) and custom types
return {
const result = {
type: inputSpecV1[0],
...baseProps
}
// Special handling for SELECTBUTTON to ensure options.values is properly set
if (inputSpecV1[0] === 'SELECTBUTTON' && (inputSpecV1[1] || {}).values) {
;(result as any).options = {
...(result as any).options,
values: (inputSpecV1[1] || {}).values
}
}
return result
}
// Fallback for any unhandled cases