diff --git a/backend/windmill-store/src/resources.rs b/backend/windmill-store/src/resources.rs index a03d97c320..269edf2612 100644 --- a/backend/windmill-store/src/resources.rs +++ b/backend/windmill-store/src/resources.rs @@ -1345,6 +1345,12 @@ async fn create_resource_type( let is_fileset = resource_type.is_fileset.unwrap_or(false); + if is_fileset && resource_type.format_extension.is_some() { + return Err(Error::BadRequest( + "A fileset resource type cannot have a format_extension".to_string(), + )); + } + sqlx::query!( "INSERT INTO resource_type (workspace_id, name, schema, description, created_by, format_extension, is_fileset, edited_at) diff --git a/frontend/src/lib/components/FilesetEditor.svelte b/frontend/src/lib/components/FilesetEditor.svelte index a811e1e363..96956e038a 100644 --- a/frontend/src/lib/components/FilesetEditor.svelte +++ b/frontend/src/lib/components/FilesetEditor.svelte @@ -32,28 +32,30 @@ selectedFileKey?.replace(/^\//, '') ) + function flushEditContent() { + if (selectedFileKey != null && selectedFileKey in files && files[selectedFileKey] !== editContent) { + files = { ...files, [selectedFileKey]: editContent } + } + } + function handleSelectPath(path: string) { + flushEditContent() selectedPath = path if (!path.endsWith('/') && path !== '') { editContent = files[path] ?? '' } } - // Sync editContent → files → args reactively - $effect(() => { - const key = selectedFileKey - const content = editContent - if (key != null && key in files && files[key] !== content) { - files = { ...files, [key]: content } - } - }) - - // Sync files → args (strip / prefix, skip folder entries) + // Sync files → args, overlaying current editContent for the active file. + // This avoids spreading a new files object on every keystroke. $effect(() => { + const currentKey = selectedFileKey + const currentContent = editContent const newArgs: Record = {} for (const [key, value] of Object.entries(files)) { if (!key.endsWith('/')) { - newArgs[key.replace(/^\//, '')] = value + const argKey = key.replace(/^\//, '') + newArgs[argKey] = key === currentKey ? currentContent : value } } args = newArgs diff --git a/frontend/src/lib/components/raw_apps/RawAppSidebar.svelte b/frontend/src/lib/components/raw_apps/RawAppSidebar.svelte index 997d722eb2..859e9e5386 100644 --- a/frontend/src/lib/components/raw_apps/RawAppSidebar.svelte +++ b/frontend/src/lib/components/raw_apps/RawAppSidebar.svelte @@ -16,7 +16,7 @@ interface Props { runnables: Record selectedRunnable: string | undefined - files: Record | undefined + files: Record modules?: Modules onSelectFile?: (path: string) => void selectedDocument: string | undefined @@ -37,7 +37,7 @@ let { runnables, selectedRunnable = $bindable(), - files = $bindable(), + files = $bindable({}), modules, onSelectFile, selectedDocument = $bindable(), @@ -74,21 +74,6 @@ } } - // Ensure files is always an object for FileExplorer binding - let explorerFiles: Record = $state(files ?? {}) - - // Sync explorerFiles → files (parent binding) - $effect(() => { - files = explorerFiles - }) - - // Sync files → explorerFiles when files changes externally - $effect(() => { - if (files && files !== explorerFiles) { - explorerFiles = files - } - }) - let fileExplorer: FileExplorer | undefined = $state() function handleSelectPath(path: string) { @@ -126,7 +111,7 @@ {/snippet}
{#if editResourceType.isFileset} - + This resource type represents a collection of files. The schema cannot be edited. {:else if editResourceType.formatExtension}