From 8e02c788b994a266a71f5295be99bf943a2698c3 Mon Sep 17 00:00:00 2001 From: grml Date: Sun, 28 Jun 2026 15:19:17 +0800 Subject: [PATCH 1/4] fix(presets): wire up fileInput field so file selection works #1653 and #1630 --- src/components/Windows/Presets/Presets.vue | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/components/Windows/Presets/Presets.vue b/src/components/Windows/Presets/Presets.vue index a7b380826..773fde00d 100644 --- a/src/components/Windows/Presets/Presets.vue +++ b/src/components/Windows/Presets/Presets.vue @@ -76,6 +76,32 @@ const validationError: ComputedRef = computed(() => { return null }) +function openFileInput(fieldId: string) { + const input = document.getElementById(`preset-file-input-${fieldId}`) + + if (input instanceof HTMLInputElement) input.click() +} + +async function onFileInputChange(event: Event, fieldId: string) { + const input = event.target + + const file = input.files?.[0] + + if (!file) return + + const content = new Uint8Array(await file.arrayBuffer()) + + // Store a fake file handle matching the shape that preset scripts (and loadPresetFile) expect: + // `name` + `content` (binary, read by createFile) + `text()` (read by e.g. the Block Model script). + createPresetOptions.value[fieldId] = { + name: file.name, + content, + async text() { + return new TextDecoder().decode(content) + }, + } +} + async function create() { if (validationError.value !== null) return @@ -214,15 +240,22 @@ watch(filteredCategories, () => { class="mb-6 flex bg-background" v-slot="{ focus, blur }" > - + From d32342b1892bc2c6a0b4206988c36e9f60c29292 Mon Sep 17 00:00:00 2001 From: grml Date: Sun, 28 Jun 2026 15:20:01 +0800 Subject: [PATCH 2/4] fix(fs): delete directory contents recursively in LocalFileSystem --- src/libs/fileSystem/LocalFileSystem.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libs/fileSystem/LocalFileSystem.ts b/src/libs/fileSystem/LocalFileSystem.ts index 7972c7ce5..1cd8f5f0a 100644 --- a/src/libs/fileSystem/LocalFileSystem.ts +++ b/src/libs/fileSystem/LocalFileSystem.ts @@ -173,7 +173,14 @@ export class LocalFileSystem extends BaseFileSystem { path = resolve('/', path) - await del(`localFileSystem/${this.rootName}${path}`) + // Remove the directory entry itself as well as every file and subdirectory nested inside of it. + // idb-keyval has no concept of folders, so deleting only the directory key would orphan its children. + const directoryKey = `localFileSystem/${this.rootName}${path}` + const childPrefix = `${directoryKey}/` + + const childKeys = (await keys()).filter((key) => key.toString().startsWith(childPrefix)) + + await Promise.all([del(directoryKey), ...childKeys.map((key) => del(key))]) if ( this.pathsToWatch.find((watchPath) => path.startsWith(watchPath)) !== undefined && From ba22375c48791bf93cf09ca936f8a2135e4f5eca Mon Sep 17 00:00:00 2001 From: grml Date: Sun, 28 Jun 2026 15:20:53 +0800 Subject: [PATCH 3/4] fix(compiler): post structured-clone-safe config to dash worker (safari seems to be a lot stricter chromium) --- src/libs/compiler/DashService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/compiler/DashService.ts b/src/libs/compiler/DashService.ts index 7113c5dc8..3916ea81c 100644 --- a/src/libs/compiler/DashService.ts +++ b/src/libs/compiler/DashService.ts @@ -71,7 +71,10 @@ export class DashService implements AsyncDisposable { await sendAndWait( { action: 'setup', - config: this.project.config, + // Pass a plain, structured-clone-safe copy of the config. Safari's structured clone + // is stricter than Chromium's and throws DataCloneError on non-plain objects, which + // broke .mcaddon exports (the only export path that hands data to the Dash worker). + config: this.project.config ? JSON.parse(JSON.stringify(this.project.config)) : this.project.config, mode, configPath: join(this.project.path, 'config.json'), compilerConfigPath, From d8a14a2af0c6ee0a334d99352a25fcff325f315f Mon Sep 17 00:00:00 2001 From: grml Date: Sun, 28 Jun 2026 15:21:05 +0800 Subject: [PATCH 4/4] fix(filesystem): request persistent storage on the LocalFileSystem path --- src/App.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/App.ts b/src/App.ts index 391642dd5..5a5e35242 100644 --- a/src/App.ts +++ b/src/App.ts @@ -82,7 +82,16 @@ export async function setup() { if (fileSystem instanceof TauriFileSystem) await setupTauriFileSystem() - if (fileSystem instanceof LocalFileSystem) fileSystem.setRootName('fileSystemPolyfill') + if (fileSystem instanceof LocalFileSystem) { + fileSystem.setRootName('fileSystemPolyfill') + + // On browsers without the File System Access API (e.g. Safari, Firefox, Android Chrome) the + // entire project lives in IndexedDB. Request persistent storage so the browser doesn't evict + // it, which otherwise causes saved files to silently disappear. + if (navigator.storage?.persist) { + navigator.storage.persist().catch(() => {}) + } + } setupTypescript() setupLang()