feat/fix: App mode QA feedback 2 (#9511)

## Summary

Additional fixes and updates based on testing

## Changes

- **What**: 
- add warning to welcome screen & when sharing an app that has had all
outputs removed
- fix target workflow when changing mode via tab right click menu
- change build app text to be conditional "edit" vs "build" depending on
if an app is already defined
- update empty apps sidebar tab button text to make it clearer
- remove templates button from app mode (we will reintroduce this once
we have app templates)
- add "exit to graph" after applying default mode of node graph
- update cancel button to remove item from queue if it hasn't started
yet
- improve scoping of jobs/outputs to the current workflow [not perfect
but should be much improved]
- close sidebar tabs on entering app mode
- change tooltip to be under the workflow menu rather than covering the
button

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9511-feat-fix-App-mode-QA-feedback-2-31b6d73d365081d59bbbc13111100d46)
by [Unito](https://www.unito.io)
This commit is contained in:
pythongosssss
2026-03-07 02:57:03 +00:00
committed by GitHub
parent 8bfd93963f
commit 1058b7d12d
27 changed files with 471 additions and 107 deletions

View File

@@ -1,4 +1,5 @@
import { useAsyncState } from '@vueuse/core'
import type { ComputedRef } from 'vue'
import { computed, ref, watchEffect } from 'vue'
import type { IAssetsProvider } from '@/platform/assets/composables/media/IAssetsProvider'
@@ -9,14 +10,20 @@ import { useWorkflowStore } from '@/platform/workflow/management/stores/workflow
import { flattenNodeOutput } from '@/renderer/extensions/linearMode/flattenNodeOutput'
import { useLinearOutputStore } from '@/renderer/extensions/linearMode/linearOutputStore'
import { getJobDetail } from '@/services/jobOutputCache'
import { api } from '@/scripts/api'
import { useAppModeStore } from '@/stores/appModeStore'
import { useCommandStore } from '@/stores/commandStore'
import { useExecutionStore } from '@/stores/executionStore'
import { useQueueStore } from '@/stores/queueStore'
import type { ResultItemImpl } from '@/stores/queueStore'
export function useOutputHistory(): {
outputs: IAssetsProvider
allOutputs: (item?: AssetItem) => ResultItemImpl[]
selectFirstHistory: () => void
mayBeActiveWorkflowPending: ComputedRef<boolean>
isWorkflowActive: ComputedRef<boolean>
cancelActiveWorkflowJobs: () => Promise<void>
} {
const backingOutputs = useMediaAssets('output')
void backingOutputs.fetchMediaList()
@@ -24,6 +31,37 @@ export function useOutputHistory(): {
const workflowStore = useWorkflowStore()
const executionStore = useExecutionStore()
const appModeStore = useAppModeStore()
const queueStore = useQueueStore()
function matchesActiveWorkflow(task: { jobId: string | number }): boolean {
const path = workflowStore.activeWorkflow?.path
if (!path) return false
return (
executionStore.jobIdToSessionWorkflowPath.get(String(task.jobId)) === path
)
}
function hasActiveWorkflowJobs(): boolean {
if (!workflowStore.activeWorkflow?.path) return false
return (
queueStore.runningTasks.some(matchesActiveWorkflow) ||
queueStore.pendingTasks.some(matchesActiveWorkflow)
)
}
// True when there are queued/running jobs for the active workflow but no
// in-progress output items yet.
const mayBeActiveWorkflowPending = computed(() => {
if (linearStore.activeWorkflowInProgressItems.length > 0) return false
return hasActiveWorkflowJobs()
})
// True when the active workflow has running/pending jobs or in-progress items.
const isWorkflowActive = computed(
() =>
linearStore.activeWorkflowInProgressItems.length > 0 ||
hasActiveWorkflowJobs()
)
function filterByOutputNodes(items: ResultItemImpl[]): ResultItemImpl[] {
const nodeIds = appModeStore.selectedOutputs
@@ -140,5 +178,29 @@ export function useOutputHistory(): {
}
})
return { outputs, allOutputs, selectFirstHistory }
async function cancelActiveWorkflowJobs() {
if (!workflowStore.activeWorkflow?.path) return
// Interrupt the running job if it belongs to this workflow
if (queueStore.runningTasks.some(matchesActiveWorkflow)) {
void useCommandStore().execute('Comfy.Interrupt')
} else {
// Delete first pending job for this workflow from the queue
for (const task of queueStore.pendingTasks) {
if (matchesActiveWorkflow(task)) {
await api.deleteItem('queue', String(task.jobId))
break
}
}
}
}
return {
outputs,
allOutputs,
selectFirstHistory,
mayBeActiveWorkflowPending,
isWorkflowActive,
cancelActiveWorkflowJobs
}
}