App mode output feed to only show current session results for outputs defined in the app (#9307)

## Summary

Updates app mode to only show images from:
- the workflow that generated the image
- in the current session
- for the outputs selected in the builder

## Changes

- **What**: 
- adds new mapping of jobid -> workflow path [cant use id here as it is
not guaranteed unique], capped at 4k entries
- fix bug where executing a workflow then quickly switching tabs
associated incorrect workflow
- add missing output history tests

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9307-App-mode-output-feed-to-only-show-current-session-results-for-outputs-defined-in-the-app-3156d73d36508142b4bbca3f938fc5c2)
by [Unito](https://www.unito.io)
This commit is contained in:
pythongosssss
2026-03-02 19:10:20 +00:00
committed by GitHub
parent 1dd789fa54
commit 0d7dc15916
7 changed files with 692 additions and 85 deletions

View File

@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import { computed, ref, shallowRef } from 'vue'
import { useNodeProgressText } from '@/composables/node/useNodeProgressText'
import { isCloud } from '@/platform/distribution/types'
@@ -65,6 +65,12 @@ export const useExecutionStore = defineStore('execution', () => {
*/
const jobIdToWorkflowId = ref<Map<string, string>>(new Map())
/**
* Map of job ID to workflow file path in the current session.
* Only populated for jobs that are queued in this browser tab.
*/
const jobIdToSessionWorkflowPath = shallowRef<Map<string, string>>(new Map())
const initializingJobIds = ref<Set<string>>(new Set())
const mergeExecutionProgressStates = (
@@ -218,6 +224,13 @@ export const useExecutionStore = defineStore('execution', () => {
activeJobId.value = e.detail.prompt_id
queuedJobs.value[activeJobId.value] ??= { nodes: {} }
clearInitializationByJobId(activeJobId.value)
// Ensure path mapping exists — execution_start can arrive via WebSocket
// before the HTTP response from queuePrompt triggers storeJob.
if (!jobIdToSessionWorkflowPath.value.has(activeJobId.value)) {
const path = queuedJobs.value[activeJobId.value]?.workflow?.path
if (path) ensureSessionWorkflowPath(activeJobId.value, path)
}
}
function handleExecutionCached(e: CustomEvent<ExecutionCachedWsMessage>) {
@@ -482,6 +495,24 @@ export const useExecutionStore = defineStore('execution', () => {
if (wid) {
jobIdToWorkflowId.value.set(String(id), String(wid))
}
if (workflow?.path) {
ensureSessionWorkflowPath(String(id), workflow.path)
}
}
// ~0.65 MB at capacity (32 char GUID key + 50 char path value)
const MAX_SESSION_PATH_ENTRIES = 4000
function ensureSessionWorkflowPath(jobId: string, path: string) {
if (jobIdToSessionWorkflowPath.value.get(jobId) === path) return
const next = new Map(jobIdToSessionWorkflowPath.value)
next.set(jobId, path)
while (next.size > MAX_SESSION_PATH_ENTRIES) {
const oldest = next.keys().next().value
if (oldest !== undefined) next.delete(oldest)
else break
}
jobIdToSessionWorkflowPath.value = next
}
/**
@@ -550,6 +581,8 @@ export const useExecutionStore = defineStore('execution', () => {
_executingNodeProgress,
// NodeLocatorId conversion helpers
nodeLocatorIdToExecutionId,
jobIdToWorkflowId
jobIdToWorkflowId,
jobIdToSessionWorkflowPath,
ensureSessionWorkflowPath
}
})