Skip to content

Commit

Permalink
Merge branch 'main' into model-library-sidebar-tab
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Sep 17, 2024
2 parents d2cd56c + 582acd7 commit 2d2ad1c
Show file tree
Hide file tree
Showing 25 changed files with 666 additions and 527 deletions.
45 changes: 30 additions & 15 deletions browser_tests/ComfyPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,25 +366,40 @@ export class ComfyPage {
await this.nextFrame()
}

// Default graph positions
get clipTextEncodeNode1InputSlot(): Position {
return { x: 427, y: 198 }
}

get clipTextEncodeNode2InputSlot(): Position {
return { x: 422, y: 402 }
}

// A point on input edge.
get clipTextEncodeNode2InputLinkPath(): Position {
return {
x: 395,
y: 422
}
}

get loadCheckpointNodeClipOutputSlot(): Position {
return { x: 332, y: 509 }
}

get emptySpace(): Position {
return { x: 427, y: 98 }
}

async disconnectEdge() {
// CLIP input anchor
await this.page.mouse.move(427, 198)
await this.page.mouse.down()
await this.page.mouse.move(427, 98)
await this.page.mouse.up()
// Move out the way to avoid highlight of menu item.
await this.page.mouse.move(10, 10)
await this.nextFrame()
await this.dragAndDrop(this.clipTextEncodeNode1InputSlot, this.emptySpace)
}

async connectEdge() {
// CLIP output anchor on Load Checkpoint Node.
await this.page.mouse.move(332, 509)
await this.page.mouse.down()
// CLIP input anchor on CLIP Text Encode Node.
await this.page.mouse.move(427, 198)
await this.page.mouse.up()
await this.nextFrame()
await this.dragAndDrop(
this.loadCheckpointNodeClipOutputSlot,
this.clipTextEncodeNode1InputSlot
)
}

async adjustWidgetValue() {
Expand Down
60 changes: 50 additions & 10 deletions browser_tests/interaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,56 @@ test.describe('Node Interaction', () => {
await expect(comfyPage.canvas).toHaveScreenshot('dragged-node1.png')
})

test('Can disconnect/connect edge', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
await comfyPage.disconnectEdge()
await expect(comfyPage.canvas).toHaveScreenshot(
'disconnected-edge-with-menu.png'
)
await comfyPage.connectEdge()
// Litegraph renders edge with a slight offset.
await expect(comfyPage.canvas).toHaveScreenshot('default.png', {
maxDiffPixels: 50
test.describe('Edge Interaction', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'no action')
})

test('Can disconnect/connect edge', async ({ comfyPage }) => {
await comfyPage.disconnectEdge()
await expect(comfyPage.canvas).toHaveScreenshot('disconnected-edge.png')
await comfyPage.connectEdge()
// Litegraph renders edge with a slight offset.
await expect(comfyPage.canvas).toHaveScreenshot('default.png', {
maxDiffPixels: 50
})
})

// Chromium 2x cannot move link.
// See https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/10876381315/job/30176211513
test.skip('Can move link', async ({ comfyPage }) => {
await comfyPage.dragAndDrop(
comfyPage.clipTextEncodeNode1InputSlot,
comfyPage.emptySpace
)
await expect(comfyPage.canvas).toHaveScreenshot('disconnected-edge.png')
await comfyPage.dragAndDrop(
comfyPage.clipTextEncodeNode2InputSlot,
comfyPage.clipTextEncodeNode1InputSlot
)
await expect(comfyPage.canvas).toHaveScreenshot('moved-link.png')
})

// Copy link is not working on CI at all
// Chromium 2x recognize it as dragging canvas.
// Chromium triggers search box after link release. The link is indeed copied.
// See https://github.com/Comfy-Org/ComfyUI_frontend/actions/runs/10876381315/job/30176211513
test.skip('Can copy link by shift-drag existing link', async ({
comfyPage
}) => {
await comfyPage.dragAndDrop(
comfyPage.clipTextEncodeNode1InputSlot,
comfyPage.emptySpace
)
await expect(comfyPage.canvas).toHaveScreenshot('disconnected-edge.png')
await comfyPage.page.keyboard.down('Shift')
await comfyPage.dragAndDrop(
comfyPage.clipTextEncodeNode2InputLinkPath,
comfyPage.clipTextEncodeNode1InputSlot
)
await comfyPage.page.keyboard.up('Shift')
await expect(comfyPage.canvas).toHaveScreenshot('copied-link.png')
})
})

Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "comfyui-frontend",
"private": true,
"version": "1.2.55",
"version": "1.2.56",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down Expand Up @@ -62,7 +62,7 @@
},
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.74",
"@comfyorg/litegraph": "^0.7.75",
"@primevue/themes": "^4.0.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<GlobalDialog />
<GlobalToast />
<UnloadWindowConfirmDialog />
<BrowserTabTitle />
</template>

<script setup lang="ts">
Expand All @@ -28,6 +29,7 @@ import ModelLibrarySidebarTab from './components/sidebar/tabs/ModelLibrarySideba
import GlobalDialog from './components/dialog/GlobalDialog.vue'
import GlobalToast from './components/toast/GlobalToast.vue'
import UnloadWindowConfirmDialog from './components/dialog/UnloadWindowConfirmDialog.vue'
import BrowserTabTitle from './components/BrowserTabTitle.vue'
import { api } from './scripts/api'
import { StatusWsMessageStatus } from './types/apiTypes'
import { useQueuePendingTaskCountStore } from './stores/queueStore'
Expand Down
33 changes: 33 additions & 0 deletions src/components/BrowserTabTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<div>
<!-- This component does not render anything visible. -->
</div>
</template>

<script setup lang="ts">
import { useExecutionStore } from '@/stores/executionStore'
import { useSettingStore } from '@/stores/settingStore'
import { useWorkflowStore } from '@/stores/workflowStore'
import { useTitle } from '@vueuse/core'
import { computed } from 'vue'
const executionStore = useExecutionStore()
const executionText = computed(() =>
executionStore.isIdle ? '' : `[${executionStore.executionProgress}%]`
)
const settingStore = useSettingStore()
const betaMenuEnabled = computed(
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
)
const workflowStore = useWorkflowStore()
const workflowNameText = computed(
() =>
(betaMenuEnabled.value ? workflowStore.activeWorkflow?.name : undefined) ??
'ComfyUI'
)
const title = computed(() => executionText.value + workflowNameText.value)
useTitle(title)
</script>
4 changes: 2 additions & 2 deletions src/components/common/InputSlider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ const updateValue = (newValue: number | null) => {
newValue = Number(props.min) || 0
}
const min = Number(props.min) || Number.NEGATIVE_INFINITY
const max = Number(props.max) || Number.POSITIVE_INFINITY
const min = Number(props.min ?? Number.NEGATIVE_INFINITY)
const max = Number(props.max ?? Number.POSITIVE_INFINITY)
const step = Number(props.step) || 1
// Ensure the value is within the allowed range
Expand Down
8 changes: 8 additions & 0 deletions src/components/dialog/UnloadWindowConfirmDialog.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
<template>
<div>
<!-- This component does not render anything visible. It is used to confirm
the user wants to close the window, and if they do, it will call the
beforeunload event. -->
</div>
</template>

<script setup lang="ts">
import { useSettingStore } from '@/stores/settingStore'
import { onMounted, onUnmounted } from 'vue'
Expand Down
71 changes: 2 additions & 69 deletions src/components/searchbox/NodeSearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,38 +43,7 @@
:optionLabel="'display_name'"
>
<template v-slot:option="{ option }">
<div class="option-container">
<div class="option-display-name">
<div>
<span
v-html="highlightQuery(option.display_name, currentQuery)"
></span>
<span>&nbsp;</span>
<Tag v-if="showIdName" severity="secondary">
<span v-html="highlightQuery(option.name, currentQuery)"></span>
</Tag>
</div>
<div v-if="showCategory" class="option-category">
{{ option.category.replaceAll('/', ' > ') }}
</div>
</div>
<div class="option-badges">
<Tag
v-if="option.experimental"
:value="$t('experimental')"
severity="primary"
/>
<Tag
v-if="option.deprecated"
:value="$t('deprecated')"
severity="danger"
/>
<NodeSourceChip
v-if="option.python_module !== undefined"
:python_module="option.python_module"
/>
</div>
</div>
<NodeSearchItem :nodeDef="option" :currentQuery="currentQuery" />
</template>
<!-- FilterAndValue -->
<template v-slot:chip="{ value }">
Expand All @@ -92,11 +61,10 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import AutoCompletePlus from '@/components/primevueOverride/AutoCompletePlus.vue'
import Tag from 'primevue/tag'
import Dialog from 'primevue/dialog'
import Button from 'primevue/button'
import NodeSearchFilter from '@/components/searchbox/NodeSearchFilter.vue'
import NodeSourceChip from '@/components/node/NodeSourceChip.vue'
import NodeSearchItem from '@/components/searchbox/NodeSearchItem.vue'
import { type FilterAndValue } from '@/services/nodeSearchService'
import NodePreview from '@/components/node/NodePreview.vue'
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
Expand All @@ -110,12 +78,6 @@ const { t } = useI18n()
const enableNodePreview = computed(() =>
settingStore.get('Comfy.NodeSearchBoxImpl.NodePreview')
)
const showCategory = computed(() =>
settingStore.get('Comfy.NodeSearchBoxImpl.ShowCategory')
)
const showIdName = computed(() =>
settingStore.get('Comfy.NodeSearchBoxImpl.ShowIdName')
)
const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -145,12 +107,6 @@ const search = (query: string) => {
]
}
const highlightQuery = (text: string, query: string) => {
if (!query) return text
const regex = new RegExp(`(${query})`, 'gi')
return text.replace(regex, '<span class="highlight">$1</span>')
}
const emit = defineEmits(['addFilter', 'removeFilter', 'addNode'])
const reFocusInput = () => {
Expand Down Expand Up @@ -202,29 +158,6 @@ const setHoverSuggestion = (index: number) => {
@apply z-10 flex-grow;
}
.option-container {
@apply flex justify-between items-center px-2 py-0 cursor-pointer overflow-hidden w-full;
}
.option-display-name {
@apply font-semibold flex flex-col;
}
.option-category {
@apply font-light text-sm text-gray-400 overflow-hidden text-ellipsis;
/* Keeps the text on a single line by default */
white-space: nowrap;
}
:deep(.highlight) {
background-color: var(--p-primary-color);
color: var(--p-primary-contrast-color);
font-weight: bold;
border-radius: 0.25rem;
padding: 0rem 0.125rem;
margin: -0.125rem 0.125rem;
}
._filter-button {
z-index: 10;
}
Expand Down
Loading

0 comments on commit 2d2ad1c

Please sign in to comment.