fix: improve template URL loading UX and prevent re-triggering (#6593)

Fixes the janky UX when loading templates via URL query parameters by
moving the loading logic earlier in the app lifecycle (from
GraphView.onGraphReady to
useWorkflowPersistence.restorePreviousWorkflow). The saved workflow now
loads first as a background tab, then the template loads as the active
tab, eliminating the visual flash where the saved workflow briefly
appears before being replaced. After loading, the template and source
query parameters are removed from the URL using router.replace to
prevent the template from re-loading on page refresh. This preserves
user work by keeping both workflows open in separate tabs and matches
the existing behavior when clicking templates from the dialog. All 15
tests pass including 3 new tests for URL cleanup.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6593-fix-improve-template-URL-loading-UX-and-prevent-re-triggering-2a26d73d36508137a0cae6cf92c842fc)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Christian Byrne <c.byrne@comfy.org>
This commit is contained in:
Christian Byrne
2025-11-11 12:51:56 -08:00
committed by GitHub
parent 6541f5cda5
commit 3c550e953a
5 changed files with 79 additions and 15 deletions

View File

@@ -1,9 +1,11 @@
import { tryOnScopeDispose } from '@vueuse/core'
import { computed, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useWorkflowService } from '@/platform/workflow/core/services/workflowService'
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
import { useTemplateUrlLoader } from '@/platform/workflow/templates/composables/useTemplateUrlLoader'
import { api } from '@/scripts/api'
import { app as comfyApp } from '@/scripts/app'
import { getStorageValue, setStorageValue } from '@/scripts/utils'
@@ -12,6 +14,8 @@ import { useCommandStore } from '@/stores/commandStore'
export function useWorkflowPersistence() {
const workflowStore = useWorkflowStore()
const settingStore = useSettingStore()
const route = useRoute()
const templateUrlLoader = useTemplateUrlLoader()
const workflowPersistenceEnabled = computed(() =>
settingStore.get('Comfy.Workflow.Persist')
@@ -82,8 +86,9 @@ export function useWorkflowPersistence() {
}
}
const restorePreviousWorkflow = async () => {
const initializeWorkflow = async () => {
if (!workflowPersistenceEnabled.value) return
try {
const restored = await loadPreviousWorkflowFromStorage()
if (!restored) {
@@ -95,6 +100,15 @@ export function useWorkflowPersistence() {
}
}
const loadTemplateFromUrlIfPresent = async () => {
const hasTemplateUrl =
route.query.template && typeof route.query.template === 'string'
if (hasTemplateUrl) {
await templateUrlLoader.loadTemplateFromUrl()
}
}
// Setup watchers
watch(
() => workflowStore.activeWorkflow?.key,
@@ -160,7 +174,8 @@ export function useWorkflowPersistence() {
}
return {
restorePreviousWorkflow,
initializeWorkflow,
loadTemplateFromUrlIfPresent,
restoreWorkflowTabsState
}
}

View File

@@ -1,6 +1,6 @@
import { useToast } from 'primevue/usetoast'
import { useI18n } from 'vue-i18n'
import { useRoute } from 'vue-router'
import { useRoute, useRouter } from 'vue-router'
import { useTemplateWorkflows } from './useTemplateWorkflows'
@@ -17,6 +17,7 @@ import { useTemplateWorkflows } from './useTemplateWorkflows'
*/
export function useTemplateUrlLoader() {
const route = useRoute()
const router = useRouter()
const { t } = useI18n()
const toast = useToast()
const templateWorkflows = useTemplateWorkflows()
@@ -28,6 +29,16 @@ export function useTemplateUrlLoader() {
return /^[a-zA-Z0-9_-]+$/.test(param)
}
/**
* Removes template and source parameters from URL
*/
const cleanupUrlParams = () => {
const newQuery = { ...route.query }
delete newQuery.template
delete newQuery.source
void router.replace({ query: newQuery })
}
/**
* Loads template from URL query parameters if present
* Handles errors internally and shows appropriate user feedback
@@ -39,7 +50,6 @@ export function useTemplateUrlLoader() {
return
}
// Validate template name format
if (!isValidParameter(templateParam)) {
console.warn(
`[useTemplateUrlLoader] Invalid template parameter format: ${templateParam}`
@@ -49,7 +59,6 @@ export function useTemplateUrlLoader() {
const sourceParam = (route.query.source as string | undefined) || 'default'
// Validate source parameter format
if (!isValidParameter(sourceParam)) {
console.warn(
`[useTemplateUrlLoader] Invalid source parameter format: ${sourceParam}`
@@ -57,7 +66,6 @@ export function useTemplateUrlLoader() {
return
}
// Load template with error handling
try {
await templateWorkflows.loadTemplates()
@@ -87,6 +95,8 @@ export function useTemplateUrlLoader() {
detail: t('g.errorLoadingTemplate'),
life: 3000
})
} finally {
cleanupUrlParams()
}
}