Node library custom bookmark folder (#631)

* Add new folder button

* Add tree util test

* nit

* Support empty folder in node library

* Drag to bookmark folder

* Use bookmark icon for bookmark folder

* Highlight on dragover

* nit

* Auto-expand on item added

* Extract bookmark system as store

* Add context menu on bookmark folder

* Add editable text

* Fix reactivity

* Plumb editable text

* refactor

* Rename node

* Fix focus

* Prevent name collision

* nit

* Add new folder

* nested folder support

* Change drag behavior

* Add basic playwright tests

* nit

* Target tree-node-content instead of tree-node
This commit is contained in:
Chenlei Hu
2024-08-25 21:53:58 -04:00
committed by GitHub
parent f36c934d37
commit 090fda2f22
10 changed files with 692 additions and 82 deletions

View File

@@ -0,0 +1,137 @@
import { defineStore } from 'pinia'
import { computed } from 'vue'
import { useSettingStore } from './settingStore'
import { useNodeDefStore } from './nodeDefStore'
import { ComfyNodeDefImpl, createDummyFolderNodeDef } from './nodeDefStore'
import { buildNodeDefTree } from './nodeDefStore'
import type { TreeNode } from 'primevue/treenode'
import _ from 'lodash'
export const useNodeBookmarkStore = defineStore('nodeBookmark', () => {
const settingStore = useSettingStore()
const nodeDefStore = useNodeDefStore()
const bookmarks = computed<string[]>(() =>
settingStore.get('Comfy.NodeLibrary.Bookmarks')
)
const bookmarksSet = computed<Set<string>>(() => new Set(bookmarks.value))
const bookmarkedRoot = computed<TreeNode>(() =>
buildBookmarkTree(bookmarks.value)
)
// For a node in custom bookmark folders, check if its nodePath is in bookmarksSet
// For a node in the nodeDefStore, check if its name is bookmarked at top level
const isBookmarked = (node: ComfyNodeDefImpl) =>
bookmarksSet.value.has(node.nodePath) ||
bookmarksSet.value.has(node.display_name)
const toggleBookmark = (node: ComfyNodeDefImpl) => {
if (isBookmarked(node)) {
deleteBookmark(node.nodePath)
} else {
addBookmark(node.display_name)
}
}
const buildBookmarkTree = (bookmarks: string[]) => {
const bookmarkNodes = bookmarks
.map((bookmark: string) => {
if (bookmark.endsWith('/')) return createDummyFolderNodeDef(bookmark)
const parts = bookmark.split('/')
const displayName = parts.pop()
const category = parts.join('/')
const srcNodeDef = nodeDefStore.nodeDefsByDisplayName[displayName]
if (!srcNodeDef) {
return null
}
const nodeDef = _.clone(srcNodeDef)
nodeDef.category = category
return nodeDef
})
.filter((nodeDef) => nodeDef !== null)
return buildNodeDefTree(bookmarkNodes)
}
const addBookmark = (nodePath: string) => {
settingStore.set('Comfy.NodeLibrary.Bookmarks', [
...bookmarks.value,
nodePath
])
}
const deleteBookmark = (nodePath: string) => {
settingStore.set(
'Comfy.NodeLibrary.Bookmarks',
bookmarks.value.filter((b: string) => b !== nodePath)
)
}
const addNewBookmarkFolder = (parent?: ComfyNodeDefImpl) => {
const parentPath = parent ? parent.nodePath : ''
let newFolderPath = parentPath + 'New Folder/'
let suffix = 1
while (bookmarks.value.some((b: string) => b.startsWith(newFolderPath))) {
newFolderPath = parentPath + `New Folder ${suffix}/`
suffix++
}
addBookmark(newFolderPath)
return newFolderPath
}
const renameBookmarkFolder = (
folderNode: ComfyNodeDefImpl,
newName: string
) => {
if (!folderNode.isDummyFolder) {
throw new Error('Cannot rename non-folder node')
}
const newNodePath =
folderNode.category.split('/').slice(0, -1).concat(newName).join('/') +
'/'
if (newNodePath === folderNode.nodePath) {
return
}
if (bookmarks.value.some((b: string) => b.startsWith(newNodePath))) {
throw new Error(`Folder name "${newNodePath}" already exists`)
}
settingStore.set(
'Comfy.NodeLibrary.Bookmarks',
bookmarks.value.map((b: string) =>
b.startsWith(folderNode.nodePath)
? b.replace(folderNode.nodePath, newNodePath)
: b
)
)
}
const deleteBookmarkFolder = (folderNode: ComfyNodeDefImpl) => {
if (!folderNode.isDummyFolder) {
throw new Error('Cannot delete non-folder node')
}
settingStore.set(
'Comfy.NodeLibrary.Bookmarks',
bookmarks.value.filter(
(b: string) =>
b !== folderNode.nodePath && !b.startsWith(folderNode.nodePath)
)
)
}
return {
bookmarks,
bookmarkedRoot,
isBookmarked,
toggleBookmark,
addBookmark,
addNewBookmarkFolder,
renameBookmarkFolder,
deleteBookmarkFolder
}
})