mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 06:47:33 +00:00
## Summary Centralized all download functionalities across app. Then changed downloadFile on the cloud distribution to stream assets via blob fetches while desktop/local retains direct anchor downloads. This fixes issue where trying to download cross-origin resources opens them in the window, potentially losing the user's unsaved changes. ## Changes - **What**: Moved `downloadBlob` into `downloadUtil`, routed all callers (3D exporter, recording manager, node template export, workflow/palette export, Litegraph save, ~~`useDownload` consumers~~) through shared helpers, and changed `downloadFile` to `fetch` first when `isCloud` so cross-origin URLs download reliably - `useDownload` is the exception since we simply cannot do model downloads through blob (forcing user to transfer the entire model data twice is bad). Fortunately on cloud, the user doesn't need to download models locally anyway. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6188-refactor-centralize-all-download-utils-across-app-and-apply-special-cloud-specific-behav-2946d73d365081de9f27f0994950511d) by [Unito](https://www.unito.io)
114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
import { applyTextReplacements as _applyTextReplacements } from '@/utils/searchAndReplace'
|
|
|
|
import { api } from './api'
|
|
import type { ComfyApp } from './app'
|
|
import { $el } from './ui'
|
|
|
|
export function clone<T>(obj: T): T {
|
|
try {
|
|
if (typeof structuredClone !== 'undefined') {
|
|
return structuredClone(obj)
|
|
}
|
|
} catch (error) {
|
|
// structuredClone is stricter than using JSON.parse/stringify so fallback to that
|
|
}
|
|
|
|
return JSON.parse(JSON.stringify(obj))
|
|
}
|
|
|
|
/**
|
|
* @knipIgnoreUnusedButUsedByCustomNodes
|
|
* @deprecated Use `applyTextReplacements` from `@/utils/searchAndReplace` instead
|
|
* There are external callers to this function, so we need to keep it for now
|
|
*/
|
|
export function applyTextReplacements(app: ComfyApp, value: string): string {
|
|
return _applyTextReplacements(app.graph, value)
|
|
}
|
|
|
|
/** @knipIgnoreUnusedButUsedByCustomNodes */
|
|
export async function addStylesheet(
|
|
urlOrFile: string,
|
|
relativeTo?: string
|
|
): Promise<void> {
|
|
return new Promise((res, rej) => {
|
|
let url
|
|
if (urlOrFile.endsWith('.js')) {
|
|
url = urlOrFile.substr(0, urlOrFile.length - 2) + 'css'
|
|
} else {
|
|
url = new URL(
|
|
urlOrFile,
|
|
relativeTo ?? `${window.location.protocol}//${window.location.host}`
|
|
).toString()
|
|
}
|
|
$el('link', {
|
|
parent: document.head,
|
|
rel: 'stylesheet',
|
|
type: 'text/css',
|
|
href: url,
|
|
onload: res,
|
|
onerror: rej
|
|
})
|
|
})
|
|
}
|
|
|
|
/** @knipIgnoreUnusedButUsedByCustomNodes */
|
|
export { downloadBlob } from '@/base/common/downloadUtil'
|
|
|
|
export function uploadFile(accept: string) {
|
|
return new Promise<File>((resolve, reject) => {
|
|
const input = document.createElement('input')
|
|
input.type = 'file'
|
|
input.accept = accept
|
|
input.onchange = (e) => {
|
|
const file = (e.target as HTMLInputElement).files?.[0]
|
|
if (!file) return reject(new Error('No file selected'))
|
|
resolve(file)
|
|
}
|
|
input.click()
|
|
})
|
|
}
|
|
|
|
export function prop<T>(
|
|
target: object,
|
|
name: string,
|
|
defaultValue: T,
|
|
onChanged?: (
|
|
currentValue: T,
|
|
previousValue: T,
|
|
target: object,
|
|
name: string
|
|
) => void
|
|
): T {
|
|
// @ts-expect-error fixme ts strict error
|
|
let currentValue
|
|
Object.defineProperty(target, name, {
|
|
get() {
|
|
// @ts-expect-error fixme ts strict error
|
|
return currentValue
|
|
},
|
|
set(newValue) {
|
|
// @ts-expect-error fixme ts strict error
|
|
const prevValue = currentValue
|
|
currentValue = newValue
|
|
onChanged?.(currentValue, prevValue, target, name)
|
|
}
|
|
})
|
|
return defaultValue
|
|
}
|
|
|
|
export function getStorageValue(id: string) {
|
|
const clientId = api.clientId ?? api.initialClientId
|
|
return (
|
|
(clientId && sessionStorage.getItem(`${id}:${clientId}`)) ??
|
|
localStorage.getItem(id)
|
|
)
|
|
}
|
|
|
|
export function setStorageValue(id: string, value: string) {
|
|
const clientId = api.clientId ?? api.initialClientId
|
|
if (clientId) {
|
|
sessionStorage.setItem(`${id}:${clientId}`, value)
|
|
}
|
|
localStorage.setItem(id, value)
|
|
}
|