feat: show loading spinner and uploading filename during image upload (#9189)

## Summary
- Show a canvas-based loading spinner on image upload nodes (LoadImage)
during file upload via drag-drop, paste, or file picker
- Display the uploading file's name immediately in the filename dropdown
instead of showing the previous file's name
- Show the uploading audio file's name immediately in the audio widget
during upload

## Changes
- **`useNodeImageUpload.ts`**: Add `isUploading` flag and
`onUploadStart` callback to the upload lifecycle; clear `node.imgs`
during upload to prevent stale previews
- **`useImagePreviewWidget.ts`**: Add `renderUploadSpinner` that draws
an animated arc spinner on the canvas when `node.isUploading` is true;
guard against empty `imgs` array
- **`useImageUploadWidget.ts`**: Set `fileComboWidget.value` to the new
filename on upload start; clear `node.imgs` on combo widget change
- **`uploadAudio.ts`**: Set `audioWidget.value` to the new filename on
upload start
- **`litegraph-augmentation.d.ts`**: Add `isUploading` property to
`LGraphNode`



https://github.com/user-attachments/assets/818ce529-cb83-428a-8c98-dd900a128343



## Test plan
- [x] Upload an image via file picker on LoadImage node — spinner shows
during upload, filename updates immediately
- [x] Drag-and-drop an image onto LoadImage node — same behavior
- [x] Paste an image onto LoadImage node — same behavior
- [x] Change the dropdown selection on LoadImage — old preview clears,
new image loads
- [x] Upload an audio file — filename updates immediately in the widget

🤖 Generated with [Claude Code](https://claude.com/claude-code)

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9189-feat-show-loading-spinner-and-uploading-filename-during-image-upload-3126d73d365081e4af27cd7252f34298)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dante
2026-02-26 13:22:42 +09:00
committed by GitHub
parent 8e215b3174
commit c24c4ab607
7 changed files with 272 additions and 6 deletions

View File

@@ -43,6 +43,36 @@ function scheduleDeferredImageRender() {
})
}
const TWO_PI = Math.PI * 2
const SPINNER_ARC_LENGTH = Math.PI * 1.5
function renderUploadSpinner(
ctx: CanvasRenderingContext2D,
node: LGraphNode,
shiftY: number,
computedHeight: number | undefined
) {
const dw = node.size[0]
const dh = computedHeight ?? 220
const centerX = dw / 2
const centerY = shiftY + dh / 2
const radius = 16
const angle = ((Date.now() % 1000) / 1000) * TWO_PI
ctx.save()
ctx.strokeStyle = LiteGraph.NODE_TEXT_COLOR
ctx.lineWidth = 3
ctx.lineCap = 'round'
ctx.beginPath()
ctx.arc(centerX, centerY, radius, angle, angle + SPINNER_ARC_LENGTH)
ctx.stroke()
ctx.restore()
// Schedule next frame to keep spinner animating continuously.
// Only runs while node.isUploading is true (checked by caller).
node.graph?.setDirtyCanvas(true)
}
const renderPreview = (
ctx: CanvasRenderingContext2D,
node: LGraphNode,
@@ -51,6 +81,11 @@ const renderPreview = (
) => {
if (!node.size) return
if (node.isUploading) {
renderUploadSpinner(ctx, node, shiftY, computedHeight)
return
}
const canvas = useCanvasStore().getCanvas()
const mouse = canvas.graph_mouse
@@ -65,6 +100,8 @@ const renderPreview = (
}
const imgs = node.imgs ?? []
if (imgs.length === 0) return
let { imageIndex } = node
const numImages = imgs.length
if (numImages === 1 && !imageIndex) {

View File

@@ -54,12 +54,27 @@ export const useImageUploadWidget = () => {
createAnnotatedPath(value, { rootFolder: image_folder })
// Setup file upload handling
let rollback: (() => void) | undefined
const { openFileSelection } = useNodeImageUpload(node, {
allow_batch,
fileFilter,
accept,
folder,
onUploadStart: (files) => {
if (files.length > 0) {
const prev = fileComboWidget.value
fileComboWidget.value = files[0].name
rollback = () => {
fileComboWidget.value = prev
}
}
},
onUploadError: () => {
rollback?.()
rollback = undefined
},
onUploadComplete: (output) => {
rollback = undefined
const annotated = output.map(formatPath)
annotated.forEach((path) => {
addToComboValues(fileComboWidget, path)
@@ -88,6 +103,7 @@ export const useImageUploadWidget = () => {
// Add our own callback to the combo widget to render an image when it changes
fileComboWidget.callback = function () {
node.imgs = undefined
nodeOutputStore.setNodeOutputs(node, String(fileComboWidget.value), {
isAnimated
})