Compare commits

...

4 Commits

Author SHA1 Message Date
Chenlei Hu
5e6e34cfd3 Fix regex handling of folderName 2025-03-17 15:05:24 -04:00
Chenlei Hu
2a445f3f94 workaround placeholder filename 2025-03-17 14:49:21 -04:00
Chenlei Hu
f8dcb915aa nit 2025-03-17 14:12:49 -04:00
Chenlei Hu
11925ce345 Create folder support 2025-03-17 14:09:07 -04:00
2 changed files with 76 additions and 7 deletions

View File

@@ -4,6 +4,13 @@
class="workflows-sidebar-tab bg-[var(--p-tree-background)]"
>
<template #tool-buttons>
<Button
icon="pi pi-folder-plus"
@click="addNewFolder"
severity="secondary"
text
v-tooltip.bottom="$t('g.newFolder')"
/>
<Button
icon="pi pi-refresh"
@click="workflowStore.syncWorkflows()"
@@ -91,6 +98,7 @@
:root="renderTreeNode(workflowsTree, WorkflowTreeType.Browse)"
v-model:expandedKeys="expandedKeys"
:selectionKeys="selectionKeys"
ref="persistedWorkflowsTreeExplorerRef"
v-if="workflowStore.persistedWorkflows.length > 0"
>
<template #node="{ node }">
@@ -136,7 +144,6 @@ import SidebarTabTemplate from '@/components/sidebar/tabs/SidebarTabTemplate.vue
import WorkflowTreeLeaf from '@/components/sidebar/tabs/workflows/WorkflowTreeLeaf.vue'
import { useTreeExpansion } from '@/composables/useTreeExpansion'
import { useWorkflowService } from '@/services/workflowService'
import { useCommandStore } from '@/stores/commandStore'
import { useSettingStore } from '@/stores/settingStore'
import {
useWorkflowBookmarkStore,
@@ -174,7 +181,6 @@ const handleSearch = (query: string) => {
})
}
const commandStore = useCommandStore()
const workflowStore = useWorkflowStore()
const workflowService = useWorkflowService()
const workspaceStore = useWorkspaceStore()
@@ -221,7 +227,6 @@ const renderTreeNode = (
type: WorkflowTreeType
): TreeExplorerNode<ComfyWorkflow> => {
const children = node.children?.map((child) => renderTreeNode(child, type))
const workflow: ComfyWorkflow = node.data
function handleClick(this: TreeExplorerNode<ComfyWorkflow>, e: MouseEvent) {
@@ -232,7 +237,7 @@ const renderTreeNode = (
}
}
const actions = node.leaf
const actions: Partial<TreeExplorerNode<ComfyWorkflow>> = node.leaf
? {
handleClick,
async handleRename(newName: string) {
@@ -262,7 +267,16 @@ const renderTreeNode = (
},
draggable: true
}
: { handleClick }
: {
handleClick,
async handleAddFolder(folderName: string) {
if (folderName === '') return
const parentPath = this.key.replace(/^root\/?/, '')
const folderPath = parentPath + '/' + folderName
await workflowStore.createFolder(folderPath)
}
}
return {
key: node.key,
@@ -282,4 +296,11 @@ const workflowBookmarkStore = useWorkflowBookmarkStore()
onMounted(async () => {
await workflowBookmarkStore.loadBookmarks()
})
const persistedWorkflowsTreeExplorerRef = ref<InstanceType<
typeof TreeExplorer
> | null>(null)
const addNewFolder = () => {
persistedWorkflowsTreeExplorerRef.value?.addFolderCommand?.('root')
}
</script>

View File

@@ -13,6 +13,7 @@ import { UserFile } from './userFileStore'
export class ComfyWorkflow extends UserFile {
static readonly basePath = 'workflows/'
static readonly folderPlaceholderFilename = 'folder.index'
/**
* The change tracker for the workflow. Non-reactive raw object.
@@ -32,7 +33,9 @@ export class ComfyWorkflow extends UserFile {
}
get key() {
return this.path.substring(ComfyWorkflow.basePath.length)
const key = this.isFolderPlaceholder ? this.directory + '/' : this.path
return key.substring(ComfyWorkflow.basePath.length)
}
get activeState(): ComfyWorkflowJSON | null {
@@ -55,6 +58,13 @@ export class ComfyWorkflow extends UserFile {
this._isModified = value
}
/**
* Whether the workflow is a folder placeholder.
*/
get isFolderPlaceholder(): boolean {
return this.fullFilename === ComfyWorkflow.folderPlaceholderFilename
}
/**
* Load the workflow content from remote storage. Directly returns the loaded
* workflow if the content is already loaded.
@@ -153,6 +163,8 @@ export interface WorkflowStore {
getWorkflowByPath: (path: string) => ComfyWorkflow | null
syncWorkflows: (dir?: string) => Promise<void>
reorderWorkflows: (from: number, to: number) => void
createFolder: (folderPath: string) => Promise<void>
}
export const useWorkflowStore = defineStore('workflow', () => {
@@ -418,6 +430,40 @@ export const useWorkflowStore = defineStore('workflow', () => {
}
}
/**
* Creates a new folder in the workflows directory
* @param folderPath The path of the folder to create (relative to workflows/)
* @returns Promise that resolves when the folder is created
*/
const createFolder = async (folderPath: string): Promise<void> => {
isBusy.value = true
try {
// Ensure the path is properly formatted
const normalizedPath = folderPath.endsWith('/')
? folderPath.slice(0, -1)
: folderPath
// Create the full path including the reserved index file
const indexFilePath = `${ComfyWorkflow.basePath}${normalizedPath}/${ComfyWorkflow.folderPlaceholderFilename}`
// Create an empty file to represent the folder
const resp = await api.storeUserData(indexFilePath, '', {
overwrite: false,
throwOnError: true,
full_info: true
})
if (resp.status !== 200) {
throw new Error('Failed to create folder')
}
// Sync workflows to update the file tree
await syncWorkflows()
} finally {
isBusy.value = false
}
}
return {
activeWorkflow,
isActive,
@@ -439,7 +485,9 @@ export const useWorkflowStore = defineStore('workflow', () => {
persistedWorkflows,
modifiedWorkflows,
getWorkflowByPath,
syncWorkflows
syncWorkflows,
createFolder
}
}) as unknown as () => WorkflowStore