Merge branch 'main' into feat/version-default-searchbox

This commit is contained in:
Yiqun Xu
2025-06-12 17:32:21 -07:00
19 changed files with 268 additions and 32 deletions

View File

@@ -762,7 +762,7 @@ export class ComfyPage {
y: 625 y: 625
} }
}) })
this.page.mouse.move(10, 10) await this.page.mouse.move(10, 10)
await this.nextFrame() await this.nextFrame()
} }
@@ -774,7 +774,7 @@ export class ComfyPage {
}, },
button: 'right' button: 'right'
}) })
this.page.mouse.move(10, 10) await this.page.mouse.move(10, 10)
await this.nextFrame() await this.nextFrame()
} }
@@ -1046,6 +1046,8 @@ export class ComfyPage {
} }
} }
export const testComfySnapToGridGridSize = 50
export const comfyPageFixture = base.extend<{ export const comfyPageFixture = base.extend<{
comfyPage: ComfyPage comfyPage: ComfyPage
comfyMouse: ComfyMouse comfyMouse: ComfyMouse
@@ -1072,7 +1074,8 @@ export const comfyPageFixture = base.extend<{
'Comfy.EnableTooltips': false, 'Comfy.EnableTooltips': false,
'Comfy.userId': userId, 'Comfy.userId': userId,
// Set tutorial completed to true to avoid loading the tutorial workflow. // Set tutorial completed to true to avoid loading the tutorial workflow.
'Comfy.TutorialCompleted': true 'Comfy.TutorialCompleted': true,
'Comfy.SnapToGrid.GridSize': testComfySnapToGridGridSize
}) })
} catch (e) { } catch (e) {
console.error(e) console.error(e)

View File

@@ -1,6 +1,12 @@
import { expect } from '@playwright/test' import { expect } from '@playwright/test'
import { Position } from '@vueuse/core'
import { type ComfyPage, comfyPageFixture as test } from '../fixtures/ComfyPage' import {
type ComfyPage,
comfyPageFixture as test,
testComfySnapToGridGridSize
} from '../fixtures/ComfyPage'
import { type NodeReference } from '../fixtures/utils/litegraphUtils'
test.describe('Item Interaction', () => { test.describe('Item Interaction', () => {
test('Can select/delete all items', async ({ comfyPage }) => { test('Can select/delete all items', async ({ comfyPage }) => {
@@ -57,8 +63,10 @@ test.describe('Node Interaction', () => {
await expect(comfyPage.canvas).toHaveScreenshot('selected-node2.png') await expect(comfyPage.canvas).toHaveScreenshot('selected-node2.png')
}) })
test('Can drag-select nodes with Meta (mac)', async ({ comfyPage }) => { const dragSelectNodes = async (
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode') comfyPage: ComfyPage,
clipNodes: NodeReference[]
) => {
const clipNode1Pos = await clipNodes[0].getPosition() const clipNode1Pos = await clipNodes[0].getPosition()
const clipNode2Pos = await clipNodes[1].getPosition() const clipNode2Pos = await clipNodes[1].getPosition()
const offset = 64 const offset = 64
@@ -74,10 +82,67 @@ test.describe('Node Interaction', () => {
} }
) )
await comfyPage.page.keyboard.up('Meta') await comfyPage.page.keyboard.up('Meta')
}
test('Can drag-select nodes with Meta (mac)', async ({ comfyPage }) => {
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
await dragSelectNodes(comfyPage, clipNodes)
expect(await comfyPage.getSelectedGraphNodesCount()).toBe( expect(await comfyPage.getSelectedGraphNodesCount()).toBe(
clipNodes.length clipNodes.length
) )
}) })
test('Can move selected nodes using the Comfy.Canvas.MoveSelectedNodes.{Up|Down|Left|Right} commands', async ({
comfyPage
}) => {
const clipNodes = await comfyPage.getNodeRefsByType('CLIPTextEncode')
const getPositions = () =>
Promise.all(clipNodes.map((node) => node.getPosition()))
const testDirection = async ({
direction,
expectedPosition
}: {
direction: string
expectedPosition: (originalPosition: Position) => Position
}) => {
const originalPositions = await getPositions()
await dragSelectNodes(comfyPage, clipNodes)
await comfyPage.executeCommand(
`Comfy.Canvas.MoveSelectedNodes.${direction}`
)
await comfyPage.canvas.press(`Control+Arrow${direction}`)
const newPositions = await getPositions()
expect(newPositions).toEqual(originalPositions.map(expectedPosition))
}
await testDirection({
direction: 'Down',
expectedPosition: (originalPosition) => ({
...originalPosition,
y: originalPosition.y + testComfySnapToGridGridSize
})
})
await testDirection({
direction: 'Right',
expectedPosition: (originalPosition) => ({
...originalPosition,
x: originalPosition.x + testComfySnapToGridGridSize
})
})
await testDirection({
direction: 'Up',
expectedPosition: (originalPosition) => ({
...originalPosition,
y: originalPosition.y - testComfySnapToGridGridSize
})
})
await testDirection({
direction: 'Left',
expectedPosition: (originalPosition) => ({
...originalPosition,
x: originalPosition.x - testComfySnapToGridGridSize
})
})
})
}) })
test('Can drag node', async ({ comfyPage }) => { test('Can drag node', async ({ comfyPage }) => {

View File

@@ -1,11 +1,37 @@
<template> <template>
<img <div :style="{ width: cssWidth, height: cssHeight }" class="overflow-hidden">
:src="isImageError ? DEFAULT_BANNER : imgSrc" <!-- default banner show -->
:alt="nodePack.name + ' banner'" <div v-if="showDefaultBanner" class="w-full h-full">
class="object-cover" <img
:style="{ width: cssWidth, height: cssHeight }" :src="DEFAULT_BANNER"
@error="isImageError = true" alt="default banner"
/> class="w-full h-full object-cover"
/>
</div>
<!-- banner_url or icon show -->
<div v-else class="relative w-full h-full">
<!-- blur background -->
<div
v-if="imgSrc"
class="absolute inset-0 bg-cover bg-center bg-no-repeat opacity-30"
:style="{
backgroundImage: `url(${imgSrc})`,
filter: 'blur(10px)'
}"
></div>
<!-- image -->
<img
:src="isImageError ? DEFAULT_BANNER : imgSrc"
:alt="nodePack.name + ' banner'"
:class="
isImageError
? 'relative w-full h-full object-cover z-10'
: 'relative w-full h-full object-contain z-10'
"
@error="isImageError = true"
/>
</div>
</div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -26,12 +52,9 @@ const {
}>() }>()
const isImageError = ref(false) const isImageError = ref(false)
const shouldShowFallback = computed(
() => !nodePack.banner || nodePack.banner.trim() === '' || isImageError.value const showDefaultBanner = computed(() => !nodePack.banner_url && !nodePack.icon)
) const imgSrc = computed(() => nodePack.banner_url || nodePack.icon)
const imgSrc = computed(() =>
shouldShowFallback.value ? DEFAULT_BANNER : nodePack.banner
)
const convertToCssValue = (value: string | number) => const convertToCssValue = (value: string | number) =>
typeof value === 'number' ? `${value}rem` : value typeof value === 'number' ? `${value}rem` : value

View File

@@ -4,6 +4,7 @@ import {
LGraphNode, LGraphNode,
LiteGraph LiteGraph
} from '@comfyorg/litegraph' } from '@comfyorg/litegraph'
import { Point } from '@comfyorg/litegraph'
import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'
import { import {
@@ -27,6 +28,8 @@ import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore'
import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore' import { useSearchBoxStore } from '@/stores/workspace/searchBoxStore'
import { useWorkspaceStore } from '@/stores/workspaceStore' import { useWorkspaceStore } from '@/stores/workspaceStore'
const moveSelectedNodesVersionAdded = '1.22.2'
export function useCoreCommands(): ComfyCommand[] { export function useCoreCommands(): ComfyCommand[] {
const workflowService = useWorkflowService() const workflowService = useWorkflowService()
const workflowStore = useWorkflowStore() const workflowStore = useWorkflowStore()
@@ -58,6 +61,20 @@ export function useCoreCommands(): ComfyCommand[] {
}) })
} }
const moveSelectedNodes = (
positionUpdater: (pos: Point, gridSize: number) => Point
) => {
const selectedNodes = getSelectedNodes()
if (selectedNodes.length === 0) return
const gridSize = useSettingStore().get('Comfy.SnapToGrid.GridSize')
selectedNodes.forEach((node) => {
node.pos = positionUpdater(node.pos, gridSize)
})
app.canvas.state.selectionChanged = true
app.canvas.setDirty(true, true)
}
const commands = [ const commands = [
{ {
id: 'Comfy.NewBlankWorkflow', id: 'Comfy.NewBlankWorkflow',
@@ -673,6 +690,34 @@ export function useCoreCommands(): ComfyCommand[] {
function: async () => { function: async () => {
await firebaseAuthActions.logout() await firebaseAuthActions.logout()
} }
},
{
id: 'Comfy.Canvas.MoveSelectedNodes.Up',
icon: 'pi pi-arrow-up',
label: 'Move Selected Nodes Up',
versionAdded: moveSelectedNodesVersionAdded,
function: () => moveSelectedNodes(([x, y], gridSize) => [x, y - gridSize])
},
{
id: 'Comfy.Canvas.MoveSelectedNodes.Down',
icon: 'pi pi-arrow-down',
label: 'Move Selected Nodes Down',
versionAdded: moveSelectedNodesVersionAdded,
function: () => moveSelectedNodes(([x, y], gridSize) => [x, y + gridSize])
},
{
id: 'Comfy.Canvas.MoveSelectedNodes.Left',
icon: 'pi pi-arrow-left',
label: 'Move Selected Nodes Left',
versionAdded: moveSelectedNodesVersionAdded,
function: () => moveSelectedNodes(([x, y], gridSize) => [x - gridSize, y])
},
{
id: 'Comfy.Canvas.MoveSelectedNodes.Right',
icon: 'pi pi-arrow-right',
label: 'Move Selected Nodes Right',
versionAdded: moveSelectedNodesVersionAdded,
function: () => moveSelectedNodes(([x, y], gridSize) => [x + gridSize, y])
} }
] ]

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "Fit view to selected nodes" "label": "Fit view to selected nodes"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "Move Selected Nodes Down"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "Move Selected Nodes Left"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "Move Selected Nodes Right"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "Move Selected Nodes Up"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "Reset View" "label": "Reset View"
}, },

View File

@@ -794,6 +794,10 @@
"Browse Templates": "Browse Templates", "Browse Templates": "Browse Templates",
"Delete Selected Items": "Delete Selected Items", "Delete Selected Items": "Delete Selected Items",
"Fit view to selected nodes": "Fit view to selected nodes", "Fit view to selected nodes": "Fit view to selected nodes",
"Move Selected Nodes Down": "Move Selected Nodes Down",
"Move Selected Nodes Left": "Move Selected Nodes Left",
"Move Selected Nodes Right": "Move Selected Nodes Right",
"Move Selected Nodes Up": "Move Selected Nodes Up",
"Reset View": "Reset View", "Reset View": "Reset View",
"Resize Selected Nodes": "Resize Selected Nodes", "Resize Selected Nodes": "Resize Selected Nodes",
"Canvas Toggle Link Visibility": "Canvas Toggle Link Visibility", "Canvas Toggle Link Visibility": "Canvas Toggle Link Visibility",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "Ajustar vista a los nodos seleccionados" "label": "Ajustar vista a los nodos seleccionados"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "Mover nodos seleccionados hacia abajo"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "Mover nodos seleccionados a la izquierda"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "Mover nodos seleccionados a la derecha"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "Mover nodos seleccionados hacia arriba"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "Restablecer vista" "label": "Restablecer vista"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "Interrumpir", "Interrupt": "Interrumpir",
"Load Default Workflow": "Cargar flujo de trabajo predeterminado", "Load Default Workflow": "Cargar flujo de trabajo predeterminado",
"Manage group nodes": "Gestionar nodos de grupo", "Manage group nodes": "Gestionar nodos de grupo",
"Move Selected Nodes Down": "Mover nodos seleccionados hacia abajo",
"Move Selected Nodes Left": "Mover nodos seleccionados hacia la izquierda",
"Move Selected Nodes Right": "Mover nodos seleccionados hacia la derecha",
"Move Selected Nodes Up": "Mover nodos seleccionados hacia arriba",
"Mute/Unmute Selected Nodes": "Silenciar/Activar sonido de nodos seleccionados", "Mute/Unmute Selected Nodes": "Silenciar/Activar sonido de nodos seleccionados",
"New": "Nuevo", "New": "Nuevo",
"Next Opened Workflow": "Siguiente flujo de trabajo abierto", "Next Opened Workflow": "Siguiente flujo de trabajo abierto",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "Ajuster la vue aux nœuds sélectionnés" "label": "Ajuster la vue aux nœuds sélectionnés"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "Déplacer les nœuds sélectionnés vers le bas"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "Déplacer les nœuds sélectionnés vers la gauche"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "Déplacer les nœuds sélectionnés vers la droite"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "Déplacer les nœuds sélectionnés vers le haut"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "Réinitialiser la vue" "label": "Réinitialiser la vue"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "Interrompre", "Interrupt": "Interrompre",
"Load Default Workflow": "Charger le flux de travail par défaut", "Load Default Workflow": "Charger le flux de travail par défaut",
"Manage group nodes": "Gérer les nœuds de groupe", "Manage group nodes": "Gérer les nœuds de groupe",
"Move Selected Nodes Down": "Déplacer les nœuds sélectionnés vers le bas",
"Move Selected Nodes Left": "Déplacer les nœuds sélectionnés vers la gauche",
"Move Selected Nodes Right": "Déplacer les nœuds sélectionnés vers la droite",
"Move Selected Nodes Up": "Déplacer les nœuds sélectionnés vers le haut",
"Mute/Unmute Selected Nodes": "Mettre en sourdine/Activer le son des nœuds sélectionnés", "Mute/Unmute Selected Nodes": "Mettre en sourdine/Activer le son des nœuds sélectionnés",
"New": "Nouveau", "New": "Nouveau",
"Next Opened Workflow": "Prochain flux de travail ouvert", "Next Opened Workflow": "Prochain flux de travail ouvert",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "選択したノードにビューを合わせる" "label": "選択したノードにビューを合わせる"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "選択したノードを下に移動"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "選択したノードを左に移動"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "選択したノードを右に移動"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "選択したノードを上に移動"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "ビューをリセット" "label": "ビューをリセット"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "中断", "Interrupt": "中断",
"Load Default Workflow": "デフォルトワークフローを読み込む", "Load Default Workflow": "デフォルトワークフローを読み込む",
"Manage group nodes": "グループノードを管理", "Manage group nodes": "グループノードを管理",
"Move Selected Nodes Down": "選択したノードを下へ移動",
"Move Selected Nodes Left": "選択したノードを左へ移動",
"Move Selected Nodes Right": "選択したノードを右へ移動",
"Move Selected Nodes Up": "選択したノードを上へ移動",
"Mute/Unmute Selected Nodes": "選択したノードのミュート/ミュート解除", "Mute/Unmute Selected Nodes": "選択したノードのミュート/ミュート解除",
"New": "新規", "New": "新規",
"Next Opened Workflow": "次に開いたワークフロー", "Next Opened Workflow": "次に開いたワークフロー",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "선택한 노드에 뷰 맞추기" "label": "선택한 노드에 뷰 맞추기"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "선택한 노드 아래로 이동"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "선택한 노드 왼쪽으로 이동"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "선택한 노드 오른쪽으로 이동"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "선택한 노드 위로 이동"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "뷰 재설정" "label": "뷰 재설정"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "중단", "Interrupt": "중단",
"Load Default Workflow": "기본 워크플로 불러오기", "Load Default Workflow": "기본 워크플로 불러오기",
"Manage group nodes": "그룹 노드 관리", "Manage group nodes": "그룹 노드 관리",
"Move Selected Nodes Down": "선택한 노드 아래로 이동",
"Move Selected Nodes Left": "선택한 노드 왼쪽으로 이동",
"Move Selected Nodes Right": "선택한 노드 오른쪽으로 이동",
"Move Selected Nodes Up": "선택한 노드 위로 이동",
"Mute/Unmute Selected Nodes": "선택한 노드 활성화/비활성화", "Mute/Unmute Selected Nodes": "선택한 노드 활성화/비활성화",
"New": "새로 만들기", "New": "새로 만들기",
"Next Opened Workflow": "다음 열린 워크플로", "Next Opened Workflow": "다음 열린 워크플로",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "Подогнать вид к выбранным нодам" "label": "Подогнать вид к выбранным нодам"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "Переместить выбранные узлы вниз"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "Переместить выбранные узлы влево"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "Переместить выбранные узлы вправо"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "Переместить выбранные узлы вверх"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "Сбросить вид" "label": "Сбросить вид"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "Прервать", "Interrupt": "Прервать",
"Load Default Workflow": "Загрузить стандартный рабочий процесс", "Load Default Workflow": "Загрузить стандартный рабочий процесс",
"Manage group nodes": "Управление групповыми нодами", "Manage group nodes": "Управление групповыми нодами",
"Move Selected Nodes Down": "Переместить выбранные узлы вниз",
"Move Selected Nodes Left": "Переместить выбранные узлы влево",
"Move Selected Nodes Right": "Переместить выбранные узлы вправо",
"Move Selected Nodes Up": "Переместить выбранные узлы вверх",
"Mute/Unmute Selected Nodes": "Отключить/включить звук для выбранных нод", "Mute/Unmute Selected Nodes": "Отключить/включить звук для выбранных нод",
"New": "Новый", "New": "Новый",
"Next Opened Workflow": "Следующий открытый рабочий процесс", "Next Opened Workflow": "Следующий открытый рабочий процесс",

View File

@@ -44,6 +44,18 @@
"Comfy_Canvas_FitView": { "Comfy_Canvas_FitView": {
"label": "适应视图到选中节点" "label": "适应视图到选中节点"
}, },
"Comfy_Canvas_MoveSelectedNodes_Down": {
"label": "下移选中的节点"
},
"Comfy_Canvas_MoveSelectedNodes_Left": {
"label": "向左移动选中节点"
},
"Comfy_Canvas_MoveSelectedNodes_Right": {
"label": "向右移动选中节点"
},
"Comfy_Canvas_MoveSelectedNodes_Up": {
"label": "上移选中的节点"
},
"Comfy_Canvas_ResetView": { "Comfy_Canvas_ResetView": {
"label": "重置视图" "label": "重置视图"
}, },

View File

@@ -709,6 +709,10 @@
"Interrupt": "中断", "Interrupt": "中断",
"Load Default Workflow": "加载默认工作流", "Load Default Workflow": "加载默认工作流",
"Manage group nodes": "管理组节点", "Manage group nodes": "管理组节点",
"Move Selected Nodes Down": "下移所选节点",
"Move Selected Nodes Left": "左移所选节点",
"Move Selected Nodes Right": "右移所选节点",
"Move Selected Nodes Up": "上移所选节点",
"Mute/Unmute Selected Nodes": "静音/取消静音选定节点", "Mute/Unmute Selected Nodes": "静音/取消静音选定节点",
"New": "新建", "New": "新建",
"Next Opened Workflow": "下一个打开的工作流", "Next Opened Workflow": "下一个打开的工作流",

View File

@@ -124,19 +124,7 @@ export const useAlgoliaSearchService = (
maxCacheSize = DEFAULT_MAX_CACHE_SIZE, maxCacheSize = DEFAULT_MAX_CACHE_SIZE,
minCharsForSuggestions = DEFAULT_MIN_CHARS_FOR_SUGGESTIONS minCharsForSuggestions = DEFAULT_MIN_CHARS_FOR_SUGGESTIONS
} = options } = options
const searchClient = algoliasearch(__ALGOLIA_APP_ID__, __ALGOLIA_API_KEY__, { const searchClient = algoliasearch(__ALGOLIA_APP_ID__, __ALGOLIA_API_KEY__)
hosts: [
{
url: 'search.comfy.org/api/search',
accept: 'read',
protocol: 'https'
}
],
baseHeaders: {
'X-Algolia-Application-Id': __ALGOLIA_APP_ID__,
'X-Algolia-API-Key': __ALGOLIA_API_KEY__
}
})
const searchPacksCache = new QuickLRU<string, SearchPacksResult>({ const searchPacksCache = new QuickLRU<string, SearchPacksResult>({
maxSize: maxCacheSize maxSize: maxCacheSize
}) })