V2 Node Search (+ hidden Node Library changes) (#8987)

## Summary

Redesigned node search with categories

## Changes

- **What**: Adds a v2 search component, leaving the existing
implementation untouched
- It also brings onboard the incomplete node library & preview changes,
disabled and behind a hidden setting
- **Breaking**: Changes the 'default' value of the node search setting
to v2, adding v1 (legacy) as an option

## Screenshots (if applicable)




https://github.com/user-attachments/assets/2ab797df-58f0-48e8-8b20-2a1809e3735f

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8987-V2-Node-Search-hidden-Node-Library-changes-30c6d73d36508160902bcb92553f147c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Yourz <crazilou@vip.qq.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Christian Byrne <cbyrne@comfy.org>
This commit is contained in:
pythongosssss
2026-02-20 09:10:03 +00:00
committed by GitHub
parent 8f5cdead73
commit 6902e38e6a
183 changed files with 7972 additions and 127 deletions

View File

@@ -0,0 +1,149 @@
import type { CSSProperties, Ref } from 'vue'
import { computed, ref } from 'vue'
import { useNodeDragToCanvas } from '@/composables/node/useNodeDragToCanvas'
import { useSettingStore } from '@/platform/settings/settingStore'
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
const PREVIEW_WIDTH = 200
const PREVIEW_MARGIN = 16
export function useNodePreviewAndDrag(
nodeDef: Ref<ComfyNodeDefImpl | undefined>,
options?: { panelRef?: Ref<HTMLElement | null> }
) {
const { startDrag, handleNativeDrop } = useNodeDragToCanvas()
const settingStore = useSettingStore()
const sidebarLocation = computed<'left' | 'right'>(() =>
settingStore.get('Comfy.Sidebar.Location')
)
const previewRef = ref<HTMLElement | null>(null)
const isHovered = ref(false)
const isDragging = ref(false)
const showPreview = computed(() => isHovered.value && !isDragging.value)
const nodePreviewStyle = ref<CSSProperties>({
position: 'fixed',
top: '0px',
left: '0px',
pointerEvents: 'none',
zIndex: 1000
})
function calculatePreviewPosition(rect: DOMRect) {
const viewportHeight = window.innerHeight
const viewportWidth = window.innerWidth
let left: number
if (sidebarLocation.value === 'left') {
left = rect.right + PREVIEW_MARGIN
if (left + PREVIEW_WIDTH > viewportWidth) {
left = rect.left - PREVIEW_MARGIN - PREVIEW_WIDTH
}
} else {
left = rect.left - PREVIEW_MARGIN - PREVIEW_WIDTH
if (left < 0) {
left = rect.right + PREVIEW_MARGIN
}
}
return { left, viewportHeight }
}
function handleMouseEnter(e: MouseEvent) {
if (!nodeDef.value) return
const target = e.currentTarget as HTMLElement
const rect = target.getBoundingClientRect()
const horizontalRect =
options?.panelRef?.value?.getBoundingClientRect() ?? rect
const { left, viewportHeight } = calculatePreviewPosition(horizontalRect)
let top = rect.top
nodePreviewStyle.value = {
position: 'fixed',
top: `${top}px`,
left: `${left}px`,
pointerEvents: 'none',
zIndex: 1000,
opacity: 0
}
isHovered.value = true
requestAnimationFrame(() => {
if (previewRef.value) {
const previewRect = previewRef.value.getBoundingClientRect()
const previewHeight = previewRect.height
const mouseY = rect.top + rect.height / 2
top = mouseY - previewHeight * 0.3
const minTop = PREVIEW_MARGIN
const maxTop = viewportHeight - previewHeight - PREVIEW_MARGIN
top = Math.max(minTop, Math.min(top, maxTop))
nodePreviewStyle.value = {
...nodePreviewStyle.value,
top: `${top}px`,
opacity: 1
}
}
})
}
function handleMouseLeave() {
isHovered.value = false
}
function createEmptyDragImage(): HTMLElement {
const el = document.createElement('div')
el.style.position = 'absolute'
el.style.left = '-9999px'
el.style.top = '-9999px'
el.style.width = '1px'
el.style.height = '1px'
return el
}
function handleDragStart(e: DragEvent) {
if (!nodeDef.value) return
isDragging.value = true
isHovered.value = false
startDrag(nodeDef.value, 'native')
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'copy'
e.dataTransfer.setData('application/x-comfy-node', nodeDef.value.name)
const dragImage = createEmptyDragImage()
document.body.appendChild(dragImage)
e.dataTransfer.setDragImage(dragImage, 0, 0)
requestAnimationFrame(() => {
document.body.removeChild(dragImage)
})
}
}
function handleDragEnd(e: DragEvent) {
isDragging.value = false
handleNativeDrop(e.clientX, e.clientY)
}
return {
previewRef,
isHovered,
isDragging,
showPreview,
nodePreviewStyle,
sidebarLocation,
handleMouseEnter,
handleMouseLeave,
handleDragStart,
handleDragEnd
}
}