diff --git a/src/base/common/downloadUtil.ts b/src/base/common/downloadUtil.ts
new file mode 100644
index 000000000..307a3e35b
--- /dev/null
+++ b/src/base/common/downloadUtil.ts
@@ -0,0 +1,41 @@
+/**
+ * Utility functions for downloading files
+ */
+
+// Constants
+const DEFAULT_DOWNLOAD_FILENAME = 'download.png'
+
+/**
+ * Download a file from a URL by creating a temporary anchor element
+ * @param url - The URL of the file to download (must be a valid URL string)
+ * @param filename - Optional filename override (will use URL filename or default if not provided)
+ * @throws {Error} If the URL is invalid or empty
+ */
+export const downloadFile = (url: string, filename?: string): void => {
+ if (!url || typeof url !== 'string' || url.trim().length === 0) {
+ throw new Error('Invalid URL provided for download')
+ }
+ const link = document.createElement('a')
+ link.href = url
+ link.download =
+ filename || extractFilenameFromUrl(url) || DEFAULT_DOWNLOAD_FILENAME
+
+ // Trigger download
+ document.body.appendChild(link)
+ link.click()
+ document.body.removeChild(link)
+}
+
+/**
+ * Extract filename from a URL's query parameters
+ * @param url - The URL to extract filename from
+ * @returns The extracted filename or null if not found
+ */
+const extractFilenameFromUrl = (url: string): string | null => {
+ try {
+ const urlObj = new URL(url, window.location.origin)
+ return urlObj.searchParams.get('filename')
+ } catch {
+ return null
+ }
+}
diff --git a/src/composables/graph/useGraphNodeManager.ts b/src/composables/graph/useGraphNodeManager.ts
index e91d1f332..e4031fd51 100644
--- a/src/composables/graph/useGraphNodeManager.ts
+++ b/src/composables/graph/useGraphNodeManager.ts
@@ -53,6 +53,7 @@ export interface VueNodeData {
mode: number
selected: boolean
executing: boolean
+ subgraphId?: string | null
widgets?: SafeWidgetData[]
inputs?: unknown[]
outputs?: unknown[]
@@ -167,6 +168,11 @@ export const useGraphNodeManager = (graph: LGraph): GraphNodeManager => {
// Extract safe data from LiteGraph node for Vue consumption
const extractVueNodeData = (node: LGraphNode): VueNodeData => {
+ // Determine subgraph ID - null for root graph, string for subgraphs
+ const subgraphId =
+ node.graph && 'id' in node.graph && node.graph !== node.graph.rootGraph
+ ? String(node.graph.id)
+ : null
// Extract safe widget data
const safeWidgets = node.widgets?.map((widget) => {
try {
@@ -216,6 +222,8 @@ export const useGraphNodeManager = (graph: LGraph): GraphNodeManager => {
mode: node.mode || 0,
selected: node.selected || false,
executing: false, // Will be updated separately based on execution state
+ subgraphId,
+
hasErrors: !!node.has_errors,
widgets: safeWidgets,
inputs: node.inputs ? [...node.inputs] : undefined,
diff --git a/src/locales/en/main.json b/src/locales/en/main.json
index 0f98ee103..f736ca149 100644
--- a/src/locales/en/main.json
+++ b/src/locales/en/main.json
@@ -6,6 +6,14 @@
"noWorkflowsFound": "No workflows found.",
"comingSoon": "Coming Soon",
"download": "Download",
+ "downloadImage": "Download image",
+ "editOrMaskImage": "Edit or mask image",
+ "removeImage": "Remove image",
+ "viewImageOfTotal": "View image {index} of {total}",
+ "imagePreview": "Image preview - Use arrow keys to navigate between images",
+ "errorLoadingImage": "Error loading image",
+ "failedToDownloadImage": "Failed to download image",
+ "calculatingDimensions": "Calculating dimensions",
"import": "Import",
"loadAllFolders": "Load All Folders",
"refresh": "Refresh",
diff --git a/src/renderer/extensions/vueNodes/components/ImagePreview.vue b/src/renderer/extensions/vueNodes/components/ImagePreview.vue
new file mode 100644
index 000000000..120b24657
--- /dev/null
+++ b/src/renderer/extensions/vueNodes/components/ImagePreview.vue
@@ -0,0 +1,258 @@
+
+ {{ $t('g.imageFailedToLoad') }} {{ currentImageUrl }}
+
+
+