diff --git a/src/components/common/TreeExplorer.vue b/src/components/common/TreeExplorer.vue index a3e0e1181..2adaa18dd 100644 --- a/src/components/common/TreeExplorer.vue +++ b/src/components/common/TreeExplorer.vue @@ -40,8 +40,10 @@ import type { RenderedTreeExplorerNode, TreeExplorerNode } from '@/types/treeExplorerTypes' -import type { MenuItem } from 'primevue/menuitem' +import type { MenuItem, MenuItemCommandEvent } from 'primevue/menuitem' import { useI18n } from 'vue-i18n' +import { useToast } from 'primevue/usetoast' +import { useErrorHandling } from '@/hooks/errorHooks' const expandedKeys = defineModel>('expandedKeys') provide('expandedKeys', expandedKeys) @@ -105,25 +107,31 @@ const deleteCommand = (node: RenderedTreeExplorerNode) => { node.handleDelete?.(node) emit('nodeDelete', node) } -const menuItems = computed(() => [ - { - label: t('rename'), - icon: 'pi pi-file-edit', - command: () => renameCommand(menuTargetNode.value), - visible: menuTargetNode.value?.handleRename !== undefined - }, - { - label: t('delete'), - icon: 'pi pi-trash', - command: () => deleteCommand(menuTargetNode.value), - visible: menuTargetNode.value?.handleDelete !== undefined - }, - ...(props.extraMenuItems - ? typeof props.extraMenuItems === 'function' - ? props.extraMenuItems(menuTargetNode.value) - : props.extraMenuItems - : []) -]) +const menuItems = computed(() => + [ + { + label: t('rename'), + icon: 'pi pi-file-edit', + command: () => renameCommand(menuTargetNode.value), + visible: menuTargetNode.value?.handleRename !== undefined + }, + { + label: t('delete'), + icon: 'pi pi-trash', + command: () => deleteCommand(menuTargetNode.value), + visible: menuTargetNode.value?.handleDelete !== undefined + }, + ...(props.extraMenuItems + ? typeof props.extraMenuItems === 'function' + ? props.extraMenuItems(menuTargetNode.value) + : props.extraMenuItems + : []) + ].map((menuItem) => ({ + ...menuItem, + command: wrapCommandWithErrorHandler(menuItem.command) + })) +) + const handleContextMenu = (node: RenderedTreeExplorerNode, e: MouseEvent) => { menuTargetNode.value = node emit('contextMenu', node, e) @@ -131,6 +139,17 @@ const handleContextMenu = (node: RenderedTreeExplorerNode, e: MouseEvent) => { menu.value?.show(e) } } + +const errorHandling = useErrorHandling() +const wrapCommandWithErrorHandler = ( + command: (event: MenuItemCommandEvent) => void +) => { + return errorHandling.wrapWithErrorHandling( + command, + menuTargetNode.value?.handleError + ) +} + defineExpose({ renameCommand, deleteCommand @@ -145,6 +164,7 @@ defineExpose({ margin-left: var(--p-tree-node-gap); flex-grow: 1; } + /* * The following styles are necessary to avoid layout shift when dragging nodes over folders. * By setting the position to relative on the parent and using an absolutely positioned pseudo-element, @@ -153,6 +173,7 @@ defineExpose({ :deep(.p-tree-node-content:has(.tree-folder)) { position: relative; } + :deep(.p-tree-node-content:has(.tree-folder.can-drop))::after { content: ''; position: absolute; diff --git a/src/components/sidebar/tabs/nodeLibrary/NodeBookmarkTreeExplorer.vue b/src/components/sidebar/tabs/nodeLibrary/NodeBookmarkTreeExplorer.vue index b2e4ebaee..bbbf32fb8 100644 --- a/src/components/sidebar/tabs/nodeLibrary/NodeBookmarkTreeExplorer.vue +++ b/src/components/sidebar/tabs/nodeLibrary/NodeBookmarkTreeExplorer.vue @@ -36,12 +36,12 @@ import type { TreeExplorerNode } from '@/types/treeExplorerTypes' import type { TreeNode } from 'primevue/treenode' -import { useToast } from 'primevue/usetoast' import { computed, nextTick, ref, watch } from 'vue' -import { useI18n } from 'vue-i18n' import { useTreeExpansion } from '@/hooks/treeHooks' import { app } from '@/scripts/app' import { findNodeByKey } from '@/utils/treeUtil' +import { useErrorHandling } from '@/hooks/errorHooks' +import { useI18n } from 'vue-i18n' const props = defineProps<{ filteredNodeDefs: ComfyNodeDefImpl[] @@ -182,22 +182,13 @@ defineExpose({ addNewBookmarkFolder }) -const toast = useToast() -const { t } = useI18n() -const handleRename = (node: TreeNode, newName: string) => { - if (node.data && node.data.isDummyFolder) { - try { +const handleRename = useErrorHandling().wrapWithErrorHandling( + (node: TreeNode, newName: string) => { + if (node.data && node.data.isDummyFolder) { nodeBookmarkStore.renameBookmarkFolder(node.data, newName) - } catch (e) { - toast.add({ - severity: 'error', - summary: t('error'), - detail: e.message, - life: 3000 - }) } } -} +) const showCustomizationDialog = ref(false) const initialIcon = ref(nodeBookmarkStore.defaultBookmarkIcon) @@ -212,6 +203,7 @@ const updateCustomization = (icon: string, color: string) => { } } +const { t } = useI18n() const extraMenuItems = computed( () => (menuTargetNode: RenderedTreeExplorerNode) => [ { diff --git a/src/hooks/errorHooks.ts b/src/hooks/errorHooks.ts new file mode 100644 index 000000000..f14491fa6 --- /dev/null +++ b/src/hooks/errorHooks.ts @@ -0,0 +1,28 @@ +import { useToast } from 'primevue/usetoast' +import { useI18n } from 'vue-i18n' + +export function useErrorHandling() { + const toast = useToast() + const { t } = useI18n() + + const wrapWithErrorHandling = + (action: (...args: any[]) => any, errorHandler?: (error: any) => void) => + (...args: any[]) => { + try { + return action(...args) + } catch (e) { + if (errorHandler) { + errorHandler(e) + } else { + toast.add({ + severity: 'error', + summary: t('error'), + detail: e.message, + life: 3000 + }) + } + } + } + + return { wrapWithErrorHandling } +} diff --git a/src/types/treeExplorerTypes.ts b/src/types/treeExplorerTypes.ts index cb6b64dd9..cec478f75 100644 --- a/src/types/treeExplorerTypes.ts +++ b/src/types/treeExplorerTypes.ts @@ -24,6 +24,8 @@ export interface TreeExplorerNode { node: TreeExplorerNode, data: TreeExplorerDragAndDropData ) => void + // Function to handle errors + handleError?: (error: Error) => void } export interface RenderedTreeExplorerNode extends TreeExplorerNode {