diff --git a/backend/parsers/windmill-parser-py/src/lib.rs b/backend/parsers/windmill-parser-py/src/lib.rs index 89ebcc659e..002ea51cbc 100644 --- a/backend/parsers/windmill-parser-py/src/lib.rs +++ b/backend/parsers/windmill-parser-py/src/lib.rs @@ -235,6 +235,9 @@ fn parse_typ(id: &str) -> Typ { x @ _ if x.starts_with("DynSelect_") => { Typ::DynSelect(x.strip_prefix("DynSelect_").unwrap().to_string()) } + x @ _ if x.starts_with("DynMultiselect_") => { + Typ::DynMultiselect(x.strip_prefix("DynMultiselect_").unwrap().to_string()) + } _ => Typ::Resource(map_resource_name(id)), } } diff --git a/backend/parsers/windmill-parser-ts/src/lib.rs b/backend/parsers/windmill-parser-ts/src/lib.rs index 7d998dbac7..399e13aa0e 100644 --- a/backend/parsers/windmill-parser-ts/src/lib.rs +++ b/backend/parsers/windmill-parser-ts/src/lib.rs @@ -810,6 +810,10 @@ fn tstype_to_typ( Typ::DynSelect(symbol.strip_prefix("DynSelect_").unwrap().to_string()), false, ), + symbol @ _ if symbol.starts_with("DynMultiselect_") => ( + Typ::DynMultiselect(symbol.strip_prefix("DynMultiselect_").unwrap().to_string()), + false, + ), symbol @ _ => { let symbol = to_snake_case(symbol); resolve_ts_interface_and_type_alias( @@ -846,7 +850,8 @@ fn parse_one_of_type( if ["Resource", "Date", "Base64", "Email", "Sql"] .iter() .any(|s| *s == symbol) - || symbol.starts_with("DynSelect_") => + || symbol.starts_with("DynSelect_") + || symbol.starts_with("DynMultiselect_") => { return None } diff --git a/backend/parsers/windmill-parser-ts/tests/tests.rs b/backend/parsers/windmill-parser-ts/tests/tests.rs index 57368ed09e..f5a3404df8 100644 --- a/backend/parsers/windmill-parser-ts/tests/tests.rs +++ b/backend/parsers/windmill-parser-ts/tests/tests.rs @@ -617,6 +617,35 @@ mod tests { ); } + #[test] + fn test_parse_dyn_multiselect() { + let code = r#" + export async function main( + selector: DynMultiselect_my_options + ) { + return selector; + } + "#; + let sig = parse_deno_signature(code, false, false, None).unwrap(); + assert_eq!( + sig, + MainArgSignature { + star_args: false, + star_kwargs: false, + args: vec![Arg { + name: "selector".to_string(), + otyp: None, + typ: Typ::DynMultiselect("my_options".to_string()), + default: None, + has_default: false, + oidx: None, + }], + no_main_func: Some(false), + has_preprocessor: Some(false), + } + ); + } + #[test] fn test_parse_invalid_typescript() { let code = r#" diff --git a/backend/parsers/windmill-parser/src/lib.rs b/backend/parsers/windmill-parser/src/lib.rs index 565be52cd4..c8f19f33e1 100644 --- a/backend/parsers/windmill-parser/src/lib.rs +++ b/backend/parsers/windmill-parser/src/lib.rs @@ -69,6 +69,7 @@ pub enum Typ { Email, Sql, DynSelect(String), + DynMultiselect(String), Object(ObjectType), OneOf(Vec), #[default] diff --git a/backend/windmill-worker/src/ai_executor.rs b/backend/windmill-worker/src/ai_executor.rs index d196b5f3cd..35dc6b4ee8 100644 --- a/backend/windmill-worker/src/ai_executor.rs +++ b/backend/windmill-worker/src/ai_executor.rs @@ -274,6 +274,7 @@ impl OpenAPISchema { Typ::Email => Self::from_str("string"), Typ::Sql => Self::from_str("string"), Typ::DynSelect(_) => Self::from_str("string"), + Typ::DynMultiselect(_) => Self::from_str("string"), Typ::List(typ) => OpenAPISchema { r#type: Some(SchemaType::Single("array".to_string())), items: Some(Box::new(Self::from_typ(typ))), diff --git a/backend/windmill-worker/src/schema.rs b/backend/windmill-worker/src/schema.rs index f3ae7d9688..eca9afa72e 100644 --- a/backend/windmill-worker/src/schema.rs +++ b/backend/windmill-worker/src/schema.rs @@ -73,6 +73,7 @@ fn make_rules_for_arg_typ(typ: &Typ) -> Vec { } Typ::Resource(_) => (), Typ::DynSelect(_) => (), + Typ::DynMultiselect(_) => (), Typ::Unknown => (), } diff --git a/cli/wasm/py/windmill_parser_wasm_bg.wasm b/cli/wasm/py/windmill_parser_wasm_bg.wasm index afeb717c24..a73be2ecfc 100644 Binary files a/cli/wasm/py/windmill_parser_wasm_bg.wasm and b/cli/wasm/py/windmill_parser_wasm_bg.wasm differ diff --git a/cli/wasm/ts/windmill_parser_wasm.js b/cli/wasm/ts/windmill_parser_wasm.js index 7cad1331c3..d5fb34d262 100644 --- a/cli/wasm/ts/windmill_parser_wasm.js +++ b/cli/wasm/ts/windmill_parser_wasm.js @@ -254,7 +254,7 @@ const imports = { const ret = Object.entries(arg0); return ret; }, - __wbg_eval_5e26645562cd9430: function(arg0, arg1) { + __wbg_eval_d0dfcbbfaeff4b3c: function(arg0, arg1) { const ret = eval(getStringFromWasm0(arg0, arg1)); return ret; }, diff --git a/cli/wasm/ts/windmill_parser_wasm_bg.wasm b/cli/wasm/ts/windmill_parser_wasm_bg.wasm index 78d98a7863..3e92fd399c 100644 Binary files a/cli/wasm/ts/windmill_parser_wasm_bg.wasm and b/cli/wasm/ts/windmill_parser_wasm_bg.wasm differ diff --git a/cli/windmill-utils-internal/package-lock.json b/cli/windmill-utils-internal/package-lock.json index 72bb67e2e7..990a8d4fc2 100644 --- a/cli/windmill-utils-internal/package-lock.json +++ b/cli/windmill-utils-internal/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-utils-internal", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-utils-internal", - "version": "1.0.0", + "version": "1.1.0", "license": "Apache 2.0", "devDependencies": { "@types/node": "^24.2.0", diff --git a/cli/windmill-utils-internal/package.json b/cli/windmill-utils-internal/package.json index 68a7f40104..9c6dd0ee7e 100644 --- a/cli/windmill-utils-internal/package.json +++ b/cli/windmill-utils-internal/package.json @@ -1,6 +1,6 @@ { "name": "windmill-utils-internal", - "version": "1.0.0", + "version": "1.1.0", "description": "Internal utility functions for Windmill", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/cli/windmill-utils-internal/src/parse/parse-schema.ts b/cli/windmill-utils-internal/src/parse/parse-schema.ts index c9eb5005da..5afaf9d46e 100644 --- a/cli/windmill-utils-internal/src/parse/parse-schema.ts +++ b/cli/windmill-utils-internal/src/parse/parse-schema.ts @@ -60,6 +60,7 @@ export function argSigToJsonSchemaType( | null } | { dynselect: string } + | { dynmultiselect: string } | { str: string[] | null } | { object: { name?: string; props?: { key: string; typ: any }[] } } | { @@ -150,7 +151,11 @@ export function argSigToJsonSchemaType( } else if (typeof t !== 'string' && `dynselect` in t) { newS.type = 'object' newS.format = `dynselect-${t.dynselect}` - } else if (typeof t !== 'string' && `list` in t) { + } else if (typeof t !== 'string' && `dynmultiselect` in t) { + newS.type = 'object' + newS.format = `dynmultiselect-${t.dynmultiselect}` + } + else if (typeof t !== 'string' && `list` in t) { newS.type = 'array' if (t.list === 'int' || t.list === 'float') { newS.items = { type: 'number' } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 91bdc296d8..f33f56b9b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -80,14 +80,14 @@ "windmill-parser-wasm-java": "1.510.1", "windmill-parser-wasm-nu": "1.510.1", "windmill-parser-wasm-php": "1.510.1", - "windmill-parser-wasm-py": "1.510.1", + "windmill-parser-wasm-py": "1.538.0", "windmill-parser-wasm-regex": "1.512.0", "windmill-parser-wasm-ruby": "1.526.1", "windmill-parser-wasm-rust": "1.510.1", - "windmill-parser-wasm-ts": "1.514.1", + "windmill-parser-wasm-ts": "1.538.0", "windmill-parser-wasm-yaml": "1.510.1", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.0.0", + "windmill-utils-internal": "^1.1.0", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4", @@ -12900,9 +12900,9 @@ "integrity": "sha512-qM+yeaqPdMuAaPpqND31ZabpeHlPxxtmRWLs11cGHOCHU32FIaZ92/icNUoAxgBhdJjRhR4GXaeZG32nWit2cg==" }, "node_modules/windmill-parser-wasm-py": { - "version": "1.510.1", - "resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.510.1.tgz", - "integrity": "sha512-0ES6W1l/j3NjvHgGX0l6wHvioMX3SsTWe4kQIkYP12ISpAIKfSvDpE2sFm6FQ+xoxekzGqYS4luPSmKX4aDYpQ==" + "version": "1.538.0", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-py/-/windmill-parser-wasm-py-1.538.0.tgz", + "integrity": "sha512-s+bdIgT/fA5em3zYUwF8D14uA/dZh7iu0krZYZQqZUO7txN37hwSCVfovbMkIwm4zPbsJ50mU8DRLt7UpAPZIw==" }, "node_modules/windmill-parser-wasm-regex": { "version": "1.512.0", @@ -12920,9 +12920,9 @@ "integrity": "sha512-tqT+w5gvwiX9NZCzT7iafh7dWrWSs46t+LI+N8/1+QOpv95G9/NJ/m0DIfH9Wyfkvtx0ge6igLMFha2wlPKG+w==" }, "node_modules/windmill-parser-wasm-ts": { - "version": "1.514.1", - "resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.514.1.tgz", - "integrity": "sha512-8GHDOlPqcEd/4qBVUMUP4SdU8CgvvQJw1JZg5YDMJd3dj6mhqyt7Znye9ZudmNqdhGGqsdUXxQDBc8dvcmEFXg==" + "version": "1.538.0", + "resolved": "https://registry.npmjs.org/windmill-parser-wasm-ts/-/windmill-parser-wasm-ts-1.538.0.tgz", + "integrity": "sha512-hHhMIVIPhmsHx0lsNCGMoIa7cDBFlVWhhd9j/5yOIq2sxwqg5sl5juQIIJGvuwg5umdsD0ChlSm2/uES78DLYg==" }, "node_modules/windmill-parser-wasm-yaml": { "version": "1.510.1", @@ -12935,9 +12935,9 @@ "integrity": "sha512-uHNL8F72/Tf96xF3hOHnPDjkEyqXw7fNjcPJiUhth9sTQkcwUIoJMOdwm8/cs+j9kKVRJ4tgNYMHEBLylazp6g==" }, "node_modules/windmill-utils-internal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.0.0.tgz", - "integrity": "sha512-S93XgzdM8WNmDt+MUqiBzyA/3GacZV3Wo9DxxYpAuDHvIP4s8c9zKBhNIZqB008i1opI4tTxB8SwhsYBhTaRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/windmill-utils-internal/-/windmill-utils-internal-1.1.0.tgz", + "integrity": "sha512-Vzm+lNTYR+75hSefDKgjBafNN5OGhhjkdsSElOeS8L1QcgB21DEdLtBmF7mXUGe147deFu36raq/LQzOZ7jqoQ==", "license": "Apache 2.0" }, "node_modules/word-wrap": { diff --git a/frontend/package.json b/frontend/package.json index dec955bee1..190b6b7927 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -147,14 +147,14 @@ "windmill-parser-wasm-java": "1.510.1", "windmill-parser-wasm-nu": "1.510.1", "windmill-parser-wasm-php": "1.510.1", - "windmill-parser-wasm-py": "1.510.1", + "windmill-parser-wasm-py": "1.538.0", "windmill-parser-wasm-regex": "1.512.0", "windmill-parser-wasm-ruby": "1.526.1", "windmill-parser-wasm-rust": "1.510.1", - "windmill-parser-wasm-ts": "1.514.1", + "windmill-parser-wasm-ts": "1.538.0", "windmill-parser-wasm-yaml": "1.510.1", "windmill-sql-datatype-parser-wasm": "1.512.0", - "windmill-utils-internal": "^1.0.0", + "windmill-utils-internal": "^1.1.0", "xterm": "^5.3.0", "xterm-readline": "^1.1.2", "y-monaco": "^0.1.4", diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 9801dbe33c..79b6c3f794 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -9,7 +9,7 @@ emptySchema, emptyString, getSchemaFromProperties, - type DynamicSelect + type DynamicInput as DynamicInputTypes } from '$lib/utils' import { DollarSign, Plus, X, Check, Loader2, ExternalLink } from 'lucide-svelte' import { createEventDispatcher, onDestroy, onMount, tick, untrack } from 'svelte' @@ -36,7 +36,7 @@ import SchemaFormDnd from './schema/SchemaFormDND.svelte' import SchemaForm from './SchemaForm.svelte' import { deepEqual } from 'fast-equals' - import DynSelect from './DynSelect.svelte' + import DynamicInput from './DynamicInput.svelte' import type { SchemaDiff } from '$lib/components/schema/schemaUtils.svelte' import type { ComponentCustomCSS } from './apps/types' import MultiSelect from './select/MultiSelect.svelte' @@ -97,7 +97,7 @@ orderEditable?: boolean shouldDispatchChanges?: boolean noDefaultOnSelectFirst?: boolean - helperScript?: DynamicSelect.HelperScript + helperScript?: DynamicInputTypes.HelperScript otherArgs?: Record lightHeader?: boolean diffStatus?: SchemaDiff | undefined @@ -917,13 +917,13 @@ /> - {:else if inputCat == 'dynselect'} - {:else if inputCat == 'resource-object' && resourceTypes == undefined} Loading resource types... diff --git a/frontend/src/lib/components/DynSelect.svelte b/frontend/src/lib/components/DynamicInput.svelte similarity index 65% rename from frontend/src/lib/components/DynSelect.svelte rename to frontend/src/lib/components/DynamicInput.svelte index 23f45cd22c..0bd8697922 100644 --- a/frontend/src/lib/components/DynSelect.svelte +++ b/frontend/src/lib/components/DynamicInput.svelte @@ -18,21 +18,32 @@ import { usePromise } from '$lib/svelte5Utils.svelte' import JobLoader, { type Callbacks } from './JobLoader.svelte' import Select from './select/Select.svelte' + import MultiSelect from './select/MultiSelect.svelte' + import { safeSelectItems } from './select/utils.svelte' import Tooltip from './Tooltip.svelte' import { Loader2 } from 'lucide-svelte' - import { type DynamicSelect } from '$lib/utils' + import { type DynamicInput } from '$lib/utils' import { deepEqual } from 'fast-equals' import { untrack } from 'svelte' interface Props { value?: any - helperScript?: DynamicSelect.HelperScript - entrypoint: string + helperScript?: DynamicInput.HelperScript + format: string otherArgs?: Record name: string } - let { value = $bindable(), helperScript, entrypoint, otherArgs: otherArgs }: Props = $props() + let { value = $bindable(), helperScript, format, otherArgs: otherArgs }: Props = $props() + + const [inputType, entrypoint] = format.includes('-') ? format.split('-', 2) : [format, ''] + + const isMultiple = inputType === 'dynmultiselect' + const isSelect = inputType === 'dynselect' || inputType === 'dynmultiselect' + + if (isMultiple && value === undefined) { + value = [] + } let resultJobLoader: JobLoader | undefined = $state() let _items = usePromise(getItemsFromOptions, { clearValueOnRefresh: false }) @@ -48,7 +59,7 @@ if (!result || !Array.isArray(result)) { if (result?.error?.message && result?.error?.name) { reject( - 'Error in DynSelect function execution: ' + + `Error in ${inputType} function execution: ` + result?.error?.name + ' - ' + result?.error?.message @@ -95,9 +106,17 @@ let neverLoaded = $state(true) $effect(() => { - if (_items.value && value) { - if (!_items.value.find((x) => x.value == value)) { - value = undefined + if (_items.value && value !== undefined && isSelect) { + if (isMultiple && Array.isArray(value)) { + const availableValues = new Set(_items.value.map((x) => x.value)) + const filteredValue = value.filter((v) => availableValues.has(v)) + if (filteredValue.length !== value.length) { + value = filteredValue + } + } else if (!isMultiple && value !== undefined) { + if (!_items.value.find((x) => x.value == value)) { + value = undefined + } } } }) @@ -128,15 +147,30 @@
- + {:else} + +
+ Unsupported dynamic input type: {inputType} +
+ {/if} {#if _items.error}
error: {_items.error} @@ -146,7 +180,7 @@ {:else}
Dynamic Select is not available in this mode, write value directly
Dynamic input ({inputType}) is not available in this mode, write value directly
{#await import('$lib/components/JsonEditor.svelte')} diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index 57a11397e4..34109cb083 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -21,7 +21,7 @@ import { sendUserToast } from '$lib/toast' import Toggle from './Toggle.svelte' import { - DynamicSelect, + DynamicInput, emptyString, generateRandomString, readFieldsRecursively @@ -71,9 +71,9 @@ customUi?: EditableSchemaFormUi | undefined pannelExtraButtonWidth?: number class?: string - dynSelectCode?: string | undefined - dynSelectLang?: ScriptLang | undefined - showDynSelectOpt?: boolean + dynCode?: string | undefined + dynLang?: ScriptLang | undefined + showDynOpt?: boolean addPropertyInEditorTab?: boolean openEditTab?: import('svelte').Snippet addProperty?: import('svelte').Snippet @@ -108,9 +108,9 @@ customUi = undefined, pannelExtraButtonWidth = 0, class: clazz = '', - dynSelectCode = $bindable(), - dynSelectLang = $bindable(), - showDynSelectOpt = false, + dynCode = $bindable(), + dynLang = $bindable(), + showDynOpt = false, addPropertyInEditorTab = false, openEditTab, addProperty, @@ -124,11 +124,11 @@ if (args == undefined) { args = {} } - if (dynSelectLang === undefined) { - dynSelectLang = schema?.['x-windmill-dyn-select-lang'] || 'bun' + if (dynLang === undefined) { + dynLang = schema?.['x-windmill-dyn-select-lang'] || 'bun' } - if (dynSelectCode === undefined) { - dynSelectCode = schema?.['x-windmill-dyn-select-code'] || '' + if (dynCode === undefined) { + dynCode = schema?.['x-windmill-dyn-select-code'] || '' } }) @@ -140,10 +140,10 @@ }) $effect(() => { - if (schema && dynSelectCode !== undefined && dynSelectLang !== undefined) { - if (dynSelectCode && dynSelectCode.trim()) { - schema['x-windmill-dyn-select-code'] = dynSelectCode.trim() - schema['x-windmill-dyn-select-lang'] = dynSelectLang + if (schema && dynCode !== undefined && dynLang !== undefined) { + if (dynCode && dynCode.trim()) { + schema['x-windmill-dyn-select-code'] = dynCode.trim() + schema['x-windmill-dyn-select-lang'] = dynLang } else { delete schema['x-windmill-dyn-select-code'] delete schema['x-windmill-dyn-select-lang'] @@ -223,6 +223,7 @@ if (property.type !== 'object') return property.type if (property.format === 'resource-s3_object') return 'S3' if (property.format?.startsWith('dynselect-')) return 'dynselect' + if (property.format?.startsWith('dynmultiselect-')) return 'dynmultiselect' if (property.oneOf && property.oneOf.length >= 2) return 'oneOf' if (property.format?.startsWith('resource-')) return 'resource' return 'object' @@ -339,19 +340,21 @@ !!editTab ? openEditTabFn() : closeEditTab() }) - let dynSelectFunctions = $derived( + let dynamicFunctions = $derived( Object.entries(schema?.properties ?? {}) .filter(([_, property]) => { const props = property as any - return ( - props.type === 'object' && - (props.format?.startsWith('dynselect-') || props.format?.startsWith('dynselect_')) - ) + return props.type === 'object' && DynamicInput.isDynInputFormat(props.format) }) .map(([fieldName, _]) => fieldName.replace(/\s+/g, '_')) ) - let typeOptions = [ + const DYNAMIC_OPTIONS = [ + ['DynSelect', 'dynselect'], + ['DynMultiselect', 'dynmultiselect'] + ] + + let typeOptions = $state([ ['String', 'string'], ['Number', 'number'], ['Integer', 'integer'], @@ -361,22 +364,17 @@ ['Array', 'array'], ['Boolean', 'boolean'], ['S3', 'S3'] - ] - if (showDynSelectOpt) { - typeOptions.push(['DynSelect', 'dynselect']) + ]) + + function initDynFn(lang: ScriptLang) { + const generateFn = DynamicInput.getGenerateTemplateFn(lang) + return dynamicFunctions.map((functionName) => generateFn(functionName)).join('') } - function initDynSelectFn(lang: ScriptLang) { - const generateFn = DynamicSelect.getGenerateTemplateFn(lang) - return Object.entries(schema?.properties ?? {}) - .map(([functionName]) => generateFn(functionName)) - .join('') - } - - function updateDynSelectCode(functionName: string, lang: ScriptLang = 'bun') { - const generateFn = DynamicSelect.getGenerateTemplateFn(lang) + function updateDynCode(functionName: string, lang: ScriptLang = 'bun') { + const generateFn = DynamicInput.getGenerateTemplateFn(lang) const code = generateFn(functionName) - dynSelectCode = dynSelectCode ? dynSelectCode.concat(code) : code + dynCode = dynCode ? dynCode.concat(code) : code } let dndType = $state(generateRandomString()) @@ -445,8 +443,8 @@ }} helperScript={{ type: 'inline', - code: dynSelectCode!, - lang: dynSelectLang! + code: dynCode!, + lang: dynLang! }} prettifyHeader={isAppInput} disabled={!!previewSchema} @@ -460,55 +458,53 @@ {@render runButton?.()} - {#if dynSelectFunctions.length > 0} -
-
-
- {#if dynSelectFunctions.length > 0} -
-
- Expected Functions for Dynamic Select Fields: -
-
    - {#each dynSelectFunctions as functionName} -
  • - {functionName}() -
  • - {/each} -
+ {#if dynamicFunctions.length > 0} +
+
+ {#if dynamicFunctions.length > 0} +
+
+ Expected Functions for Dynamic Input Fields:
- {/if} - { - dynSelectCode = initDynSelectFn(detail) - }} - > - {#snippet children({ item })} - - - {/snippet} - - {#key dynSelectLang} -
- -
- {/key} -
-
-
+
    + {#each dynamicFunctions as functionName} +
  • + {functionName}() +
  • + {/each} +
+
+ {/if} + { + dynCode = initDynFn(detail) + }} + > + {#snippet children({ item })} + + + {/snippet} + + {#key dynLang} +
+ +
+ {/key} +
+ {/if}
@@ -687,6 +683,7 @@ const isS3 = v == 'S3' const isOneOf = v == 'oneOf' const isDynSelect = v == 'dynselect' + const isDynMultiselect = v == 'dynmultiselect' const emptyProperty = { contentEncoding: undefined, enum_: undefined, @@ -716,15 +713,17 @@ type: 'object', format: 'resource-' } - } else if (isDynSelect) { + } else if (isDynSelect || isDynMultiselect) { const functionName = argName.replace(/\s+/g, '_') schema.properties[argName] = { ...emptyProperty, type: 'object', - format: 'dynselect-' + functionName + format: + (isDynSelect ? 'dynselect-' : 'dynmultiselect-') + + functionName } - updateDynSelectCode(argName, dynSelectLang) - dynamicSelectEditor?.setCode(dynSelectCode || '') + updateDynCode(argName, dynLang) + dynamicSelectEditor?.setCode(dynCode || '') } else if (isOneOf) { schema.properties[argName] = { ...emptyProperty, @@ -775,6 +774,11 @@ {#each typeOptions as x} {/each} + {#if showDynOpt} + {#each DYNAMIC_OPTIONS as x} + + {/each} + {/if} {/snippet} diff --git a/frontend/src/lib/components/SchemaForm.svelte b/frontend/src/lib/components/SchemaForm.svelte index 1123585f64..7313831558 100644 --- a/frontend/src/lib/components/SchemaForm.svelte +++ b/frontend/src/lib/components/SchemaForm.svelte @@ -5,7 +5,7 @@ import type { Schema } from '$lib/common' import { VariableService } from '$lib/gen' import { workspaceStore } from '$lib/stores' - import { allTrue, computeShow, type DynamicSelect } from '$lib/utils' + import { allTrue, computeShow, type DynamicInput } from '$lib/utils' import { Button } from './common' import ItemPicker from './ItemPicker.svelte' import VariableEditor from './VariableEditor.svelte' @@ -44,7 +44,7 @@ onlyMaskPassword?: boolean dndConfig?: DndOptions | undefined items?: { id: string; value: string }[] | undefined - helperScript?: DynamicSelect.HelperScript + helperScript?: DynamicInput.HelperScript lightHeader?: boolean diff?: Record nestedParent?: { label: string; nestedParent: any | undefined } | undefined diff --git a/frontend/src/lib/components/flows/content/FlowInput.svelte b/frontend/src/lib/components/flows/content/FlowInput.svelte index 2d52e13904..24a2b3e559 100644 --- a/frontend/src/lib/components/flows/content/FlowInput.svelte +++ b/frontend/src/lib/components/flows/content/FlowInput.svelte @@ -77,8 +77,8 @@ let editableSchemaForm: EditableSchemaForm | undefined = $state(undefined) let savedPreviewArgs: Record | undefined = $state(undefined) let isValid = $state(true) - let dynSelectCode: string | undefined = $state(undefined) - let dynSelectLang: ScriptLang | undefined = $state(undefined) + let dynCode: string | undefined = $state(undefined) + let dynLang: ScriptLang | undefined = $state(undefined) function updateEditPanelSize(size: number | undefined) { if (!$flowInputEditorState) return @@ -375,7 +375,7 @@ on:delete={(e) => { addPropertyV2?.handleDeleteArgument([e.detail]) }} - showDynSelectOpt + showDynOpt displayWebhookWarning editTab={$flowInputEditorState?.selectedTab} {previewSchema} @@ -414,8 +414,8 @@ } }} bind:isValid - bind:dynSelectCode - bind:dynSelectLang + bind:dynCode + bind:dynLang > {#snippet openEditTab()}
diff --git a/frontend/src/lib/components/schema/FlowPropertyEditor.svelte b/frontend/src/lib/components/schema/FlowPropertyEditor.svelte index aca9ce1f0f..7ba10f7c5b 100644 --- a/frontend/src/lib/components/schema/FlowPropertyEditor.svelte +++ b/frontend/src/lib/components/schema/FlowPropertyEditor.svelte @@ -16,6 +16,7 @@ import { Pen, Plus, Trash2 } from 'lucide-svelte' import Popover from '$lib/components/meltComponents/Popover.svelte' import ResourcePicker from '../ResourcePicker.svelte' + import { DynamicInput } from '$lib/utils' interface Props { format?: string | undefined @@ -131,7 +132,7 @@ ? 'resource' : 'custom-object' ) - let isDynSelect = $derived(format?.startsWith('dynselect-') ?? false) + let isDyn = $derived(DynamicInput.isDynInputFormat(format)) let customObjectSelected: 'editor' | 'json-schema-resource' = $state( format?.startsWith('jsonschema-') ? 'json-schema-resource' : 'editor' @@ -274,9 +275,9 @@ {/if}
- {:else if type === 'object' && format?.startsWith('resource-') && format !== 'resource-s3_object' && !isDynSelect} + {:else if type === 'object' && format?.startsWith('resource-') && format !== 'resource-s3_object' && !isDyn} - {:else if type === 'object' && !format?.startsWith('resource-') && !isDynSelect} + {:else if type === 'object' && !format?.startsWith('resource-') && !isDyn}