Skip to content

Commit

Permalink
Restore context menu for new searchbox (#724)
Browse files Browse the repository at this point in the history
* Searchbox revamp

* nit

* nit

* Add playwright test

* Update litegraph

* Rename setting

* Update test expectations [skip ci]

---------

Co-authored-by: github-actions <github-actions@github.com>
  • Loading branch information
huchenlei and github-actions authored Sep 3, 2024
1 parent 974a7ef commit 36cdebc
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 94 deletions.
4 changes: 1 addition & 3 deletions browser_tests/interaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ test.describe('Node Interaction', () => {
})

test('Can disconnect/connect edge', async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'no action')
await comfyPage.disconnectEdge()
// Close search menu popped up.
await comfyPage.page.keyboard.press('Escape')
await comfyPage.nextFrame()
await expect(comfyPage.canvas).toHaveScreenshot(
'disconnected-edge-with-menu.png'
)
Expand Down
37 changes: 22 additions & 15 deletions browser_tests/nodeSearchBox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,14 @@ import { comfyPageFixture as test } from './ComfyPage'

test.describe('Node search box', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting(
'Comfy.NodeSearchBoxImpl.LinkReleaseTrigger',
'always'
)
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'search box')
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
})
;['always', 'hold shift', 'NOT hold shift'].forEach((triggerMode) => {
test(`Can trigger on empty canvas double click (${triggerMode})`, async ({
comfyPage
}) => {
await comfyPage.setSetting(
'Comfy.NodeSearchBoxImpl.LinkReleaseTrigger',
triggerMode
)
await comfyPage.doubleClickCanvas()
await expect(comfyPage.searchBox.input).toHaveCount(1)
})

test(`Can trigger on empty canvas double click`, async ({ comfyPage }) => {
await comfyPage.doubleClickCanvas()
await expect(comfyPage.searchBox.input).toHaveCount(1)
})

test('Can trigger on link release', async ({ comfyPage }) => {
Expand Down Expand Up @@ -78,3 +70,18 @@ test.describe('Node search box', () => {
)
})
})

test.describe('Release context menu', () => {
test.beforeEach(async ({ comfyPage }) => {
await comfyPage.setSetting('Comfy.LinkRelease.Action', 'context menu')
await comfyPage.setSetting('Comfy.LinkRelease.ActionShift', 'search box')
await comfyPage.setSetting('Comfy.NodeSearchBoxImpl', 'default')
})

test('Can trigger on link release', async ({ comfyPage }) => {
await comfyPage.disconnectEdge()
await expect(comfyPage.canvas).toHaveScreenshot(
'link-release-context-menu.png'
)
})
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
},
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "^1.2.1",
"@comfyorg/litegraph": "^0.7.59",
"@comfyorg/litegraph": "^0.7.60",
"@primevue/themes": "^4.0.0-rc.2",
"@vitejs/plugin-vue": "^5.0.5",
"@vueuse/core": "^11.0.0",
Expand Down
4 changes: 4 additions & 0 deletions src/components/dialog/content/setting/SettingGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
<div class="setting-label">
<span>
<Tag v-if="setting.experimental" :value="$t('experimental')" />
<Tag
v-if="setting.deprecated"
:value="$t('deprecated')"
severity="danger" />
{{ setting.name }}
<i
v-if="setting.tooltip"
Expand Down
14 changes: 2 additions & 12 deletions src/components/graph/GraphCanvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<TitleEditor />
<canvas ref="canvasRef" id="graph-canvas" tabindex="1" />
</teleport>
<NodeSearchboxPopover v-if="nodeSearchEnabled" />
<NodeSearchboxPopover />
<NodeTooltip />
</template>

Expand All @@ -18,7 +18,7 @@ import SideToolbar from '@/components/sidebar/SideToolbar.vue'
import LiteGraphCanvasSplitterOverlay from '@/components/LiteGraphCanvasSplitterOverlay.vue'
import NodeSearchboxPopover from '@/components/searchbox/NodeSearchBoxPopover.vue'
import NodeTooltip from '@/components/graph/NodeTooltip.vue'
import { ref, computed, onUnmounted, watch, onMounted, watchEffect } from 'vue'
import { ref, computed, onUnmounted, onMounted, watchEffect } from 'vue'
import { app as comfyApp } from '@/scripts/app'
import { useSettingStore } from '@/stores/settingStore'
import { dropTargetForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter'
Expand Down Expand Up @@ -48,16 +48,6 @@ const canvasStore = useCanvasStore()
const betaMenuEnabled = computed(
() => settingStore.get('Comfy.UseNewMenu') !== 'Disabled'
)
const nodeSearchEnabled = computed<boolean>(
() => settingStore.get('Comfy.NodeSearchBoxImpl') === 'default'
)
watchEffect(() => {
LiteGraph.release_link_on_empty_shows_menu = !nodeSearchEnabled.value
if (canvasStore.canvas) {
canvasStore.canvas.allow_searchbox = !nodeSearchEnabled.value
}
})
watchEffect(() => {
const canvasInfoEnabled = settingStore.get('Comfy.Graph.CanvasInfo')
Expand Down
16 changes: 8 additions & 8 deletions src/components/searchbox/NodeSearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ const showIdName = computed(() =>
settingStore.get('Comfy.NodeSearchBoxImpl.ShowIdName')
)
const props = defineProps({
filters: {
type: Array<FilterAndValue>
},
searchLimit: {
type: Number,
default: 64
const props = withDefaults(
defineProps<{
filters: FilterAndValue[]
searchLimit: number
}>(),
{
searchLimit: 64
}
})
)
const nodeSearchFilterVisible = ref(false)
const inputId = `comfy-vue-node-search-box-input-${Math.random()}`
Expand Down
142 changes: 96 additions & 46 deletions src/components/searchbox/NodeSearchBoxPopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,21 @@

<script setup lang="ts">
import { app } from '@/scripts/app'
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
import { computed, onMounted, onUnmounted, ref, watchEffect } from 'vue'
import NodeSearchBox from './NodeSearchBox.vue'
import Dialog from 'primevue/dialog'
import { LiteGraphCanvasEvent, ConnectingLink } from '@comfyorg/litegraph'
import { ConnectingLink } from '@comfyorg/litegraph'
import { FilterAndValue } from '@/services/nodeSearchService'
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
import { ConnectingLinkImpl } from '@/types/litegraphTypes'
import { useSettingStore } from '@/stores/settingStore'
import { LinkReleaseTriggerMode } from '@/types/searchBoxTypes'
import { LinkReleaseTriggerAction } from '@/types/searchBoxTypes'
import { useCanvasStore } from '@/stores/graphStore'
import { LiteGraphCanvasEvent } from '@comfyorg/litegraph'
import { LiteGraph } from '@comfyorg/litegraph'
const settingStore = useSettingStore()
interface LiteGraphPointerEvent extends Event {
canvasX: number
canvasY: number
}
const visible = ref(false)
const dismissable = ref(true)
const triggerEvent = ref<LiteGraphCanvasEvent | null>(null)
Expand All @@ -56,22 +54,19 @@ const getNewNodeLocation = (): [number, number] => {
return [100, 100]
}
const originalEvent = triggerEvent.value.detail
.originalEvent as LiteGraphPointerEvent
const originalEvent = triggerEvent.value.detail.originalEvent
// @ts-expect-error LiteGraph types are not typed
return [originalEvent.canvasX, originalEvent.canvasY]
}
const nodeFilters = reactive([])
const nodeFilters = ref<FilterAndValue[]>([])
const addFilter = (filter: FilterAndValue) => {
nodeFilters.push(filter)
nodeFilters.value.push(filter)
}
const removeFilter = (filter: FilterAndValue) => {
const index = nodeFilters.findIndex((f) => f === filter)
if (index !== -1) {
nodeFilters.splice(index, 1)
}
nodeFilters.value = nodeFilters.value.filter((f) => f !== filter)
}
const clearFilters = () => {
nodeFilters.splice(0, nodeFilters.length)
nodeFilters.value = []
}
const closeDialog = () => {
visible.value = false
Expand All @@ -95,62 +90,117 @@ const addNode = (nodeDef: ComfyNodeDefImpl) => {
}, 100)
}
const linkReleaseTriggerMode = computed(() => {
return settingStore.get('Comfy.NodeSearchBoxImpl.LinkReleaseTrigger')
})
const canvasEventHandler = (e: LiteGraphCanvasEvent) => {
if (!['empty-release', 'empty-double-click'].includes(e.detail.subType)) {
return
const newSearchBoxEnabled = computed(
() => settingStore.get('Comfy.NodeSearchBoxImpl') === 'default'
)
const showSearchBox = (e: LiteGraphCanvasEvent) => {
if (newSearchBoxEnabled.value) {
showNewSearchBox(e)
} else {
canvasStore.canvas.showSearchBox(e.detail.originalEvent as MouseEvent)
}
}
const shiftPressed = (e.detail.originalEvent as KeyboardEvent).shiftKey
if (e.detail.subType === 'empty-release') {
if (
(linkReleaseTriggerMode.value === LinkReleaseTriggerMode.HOLD_SHIFT &&
!shiftPressed) ||
(linkReleaseTriggerMode.value === LinkReleaseTriggerMode.NOT_HOLD_SHIFT &&
shiftPressed)
) {
return
}
const context = e.detail.linkReleaseContext
if (context.links.length === 0) {
const nodeDefStore = useNodeDefStore()
const showNewSearchBox = (e: LiteGraphCanvasEvent) => {
if (e.detail.linkReleaseContext) {
const links = e.detail.linkReleaseContext.links
if (links.length === 0) {
console.warn('Empty release with no links! This should never happen')
return
}
const firstLink = ConnectingLinkImpl.createFromPlainObject(context.links[0])
const filter = useNodeDefStore().nodeSearchService.getFilterById(
const firstLink = ConnectingLinkImpl.createFromPlainObject(links[0])
const filter = nodeDefStore.nodeSearchService.getFilterById(
firstLink.releaseSlotType
)
const dataType = firstLink.type
addFilter([filter, dataType])
}
triggerEvent.value = e
visible.value = true
triggerEvent.value = e
// Prevent the dialog from being dismissed immediately
dismissable.value = false
setTimeout(() => {
dismissable.value = true
}, 300)
}
const handleEscapeKeyPress = (event) => {
if (event.key === 'Escape') {
closeDialog()
const showContextMenu = (e: LiteGraphCanvasEvent) => {
const links = e.detail.linkReleaseContext.links
if (links.length === 0) {
console.warn('Empty release with no links! This should never happen')
return
}
const firstLink = ConnectingLinkImpl.createFromPlainObject(links[0])
const mouseEvent = e.detail.originalEvent as MouseEvent
const commonOptions = {
e: mouseEvent,
allow_searchbox: true,
showSearchBox: () => showSearchBox(e)
}
const connectionOptions = firstLink.output
? { nodeFrom: firstLink.node, slotFrom: firstLink.output }
: { nodeTo: firstLink.node, slotTo: firstLink.input }
canvasStore.canvas.showConnectionMenu({
...connectionOptions,
...commonOptions
})
}
// Disable litegraph's default behavior of release link and search box.
const canvasStore = useCanvasStore()
watchEffect(() => {
if (canvasStore.canvas) {
LiteGraph.release_link_on_empty_shows_menu = false
canvasStore.canvas.allow_searchbox = false
}
})
const canvasEventHandler = (e: LiteGraphCanvasEvent) => {
if (e.detail.subType === 'empty-double-click') {
showSearchBox(e)
} else if (e.detail.subType === 'empty-release') {
handleCanvasEmptyRelease(e)
}
}
const linkReleaseAction = computed(() => {
return settingStore.get('Comfy.LinkRelease.Action')
})
const linkReleaseActionShift = computed(() => {
return settingStore.get('Comfy.LinkRelease.ActionShift')
})
const handleCanvasEmptyRelease = (e: LiteGraphCanvasEvent) => {
const originalEvent = e.detail.originalEvent as MouseEvent
const shiftPressed = originalEvent.shiftKey
const action = shiftPressed
? linkReleaseActionShift.value
: linkReleaseAction.value
switch (action) {
case LinkReleaseTriggerAction.SEARCH_BOX:
showSearchBox(e)
break
case LinkReleaseTriggerAction.CONTEXT_MENU:
showContextMenu(e)
break
case LinkReleaseTriggerAction.NO_ACTION:
default:
break
}
}
onMounted(() => {
document.addEventListener('litegraph:canvas', canvasEventHandler)
document.addEventListener('keydown', handleEscapeKeyPress)
})
onUnmounted(() => {
document.removeEventListener('litegraph:canvas', canvasEventHandler)
document.removeEventListener('keydown', handleEscapeKeyPress)
})
</script>

Expand Down
Loading

0 comments on commit 36cdebc

Please sign in to comment.