From 87fddf6b7bd2ab9aeb18e685c348f5ce93a50c4d Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Tue, 31 Mar 2026 20:14:05 +0200 Subject: [PATCH] Add functionality to import background images --- src/app/App.tsx | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/app/App.tsx b/src/app/App.tsx index fb9b1751..e2f62b39 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -2137,6 +2137,10 @@ export function App({ store, initialStatusMessage }: AppProps) { importModelInputRef.current?.click(); }; + const handleImportBackgroundImageButtonClick = () => { + importBackgroundImageInputRef.current?.click(); + }; + const handleImportModelChange = async (event: ChangeEvent) => { const input = event.currentTarget; const files = Array.from(input.files ?? []); @@ -2191,6 +2195,61 @@ export function App({ store, initialStatusMessage }: AppProps) { } }; + const handleImportBackgroundImageChange = async (event: ChangeEvent) => { + const input = event.currentTarget; + const file = input.files?.[0]; + + if (file === undefined) { + return; + } + + if (projectAssetStorage === null) { + setAssetStatusMessage("Imported background images require project asset storage. IndexedDB is unavailable in this browser."); + input.value = ""; + return; + } + + let importedImageForCleanup: ImportedImageAssetResult | null = null; + + try { + const importedImage = await importBackgroundImageAssetFromFile(file, projectAssetStorage); + importedImageForCleanup = importedImage; + + store.executeCommand( + createImportBackgroundImageAssetCommand({ + asset: importedImage.asset, + world: { + ...editorState.document.world, + background: changeWorldBackgroundMode(editorState.document.world.background, "image", importedImage.asset.id) + }, + label: `Import ${importedImage.asset.sourceName} as background` + }) + ); + + loadedImageAssetsRef.current = { + ...loadedImageAssetsRef.current, + [importedImage.asset.id]: importedImage.loadedAsset + }; + setLoadedImageAssets((currentLoadedAssets) => ({ + ...currentLoadedAssets, + [importedImage.asset.id]: importedImage.loadedAsset + })); + setAssetStatusMessage(null); + setStatusMessage(`Imported ${importedImage.asset.sourceName} and set it as the world background.`); + } catch (error) { + if (importedImageForCleanup !== null) { + await projectAssetStorage.deleteAsset(importedImageForCleanup.asset.storageKey).catch(() => undefined); + disposeLoadedImageAsset(importedImageForCleanup.loadedAsset); + } + + const message = getErrorMessage(error); + setStatusMessage(message); + setAssetStatusMessage(message); + } finally { + input.value = ""; + } + }; + const applyFaceMaterial = (materialId: string) => { if (selectedBrush === null || selectedFaceId === null || selectedFace === null) { setStatusMessage("Select a single box face before applying a material.");