mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-16 08:02:28 +00:00
Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9998ca3810 | ||
|
|
e64b069d77 | ||
|
|
13f58cb5f0 | ||
|
|
0f30574f55 | ||
|
|
ba551a641d | ||
|
|
ec5142d984 | ||
|
|
082960ce21 | ||
|
|
4d014abfa9 | ||
|
|
93e59a9327 | ||
|
|
890e6d0bec | ||
|
|
d69c2ed212 | ||
|
|
617948aa49 | ||
|
|
664b2e2335 | ||
|
|
8b01825dc9 | ||
|
|
4f5c41064f | ||
|
|
77d157b732 | ||
|
|
114399c848 | ||
|
|
866a7fe391 | ||
|
|
7b07295bd2 | ||
|
|
c48a3da3b3 | ||
|
|
ac918a84b6 | ||
|
|
c4f516fa47 | ||
|
|
4e892b2ed6 | ||
|
|
8b8025e60a | ||
|
|
6d9b0e1f2c | ||
|
|
87ab80fcc4 | ||
|
|
a498b09dd1 | ||
|
|
0941ecc077 | ||
|
|
cd3b1e43c3 | ||
|
|
a4d25cb529 | ||
|
|
a1fa8e0068 | ||
|
|
5aa89d213c | ||
|
|
1c142cc1a7 | ||
|
|
797e49fbfe | ||
|
|
bb6399fff6 | ||
|
|
187b1065e1 | ||
|
|
b9c9914024 | ||
|
|
31b5ace88a | ||
|
|
4708118f96 | ||
|
|
c20d094408 | ||
|
|
b6d6795418 | ||
|
|
5d3f3c1999 | ||
|
|
41604e6513 | ||
|
|
20306f8e87 | ||
|
|
28feda1791 | ||
|
|
a95c566ee3 | ||
|
|
22999e53fd | ||
|
|
7fa3eb434e | ||
|
|
631be4735a | ||
|
|
060dbc8e73 | ||
|
|
ba025e08f5 | ||
|
|
e73a72af8c | ||
|
|
357b1d0f82 | ||
|
|
4e15bf1029 | ||
|
|
ae8788b8d1 | ||
|
|
059b2a5ca7 | ||
|
|
fc818e9ed8 |
@@ -8,6 +8,7 @@
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ResourcePicker from './ResourcePicker.svelte'
|
||||
import ToggleButtonMore from './common/toggleButton-v2/ToggleButtonMore.svelte'
|
||||
|
||||
interface ProviderValue {
|
||||
kind?: AIProvider
|
||||
@@ -154,22 +155,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="w-full flex flex-col gap-3">
|
||||
<div class="w-full flex flex-col gap-1 bg-surface-secondary rounded-md">
|
||||
<!-- Provider Selection -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<ToggleButtonGroup selected={value?.kind} onSelected={onProviderChange} {disabled} wrap>
|
||||
<ToggleButtonGroup
|
||||
selected={value?.kind}
|
||||
onSelected={onProviderChange}
|
||||
{disabled}
|
||||
wrap
|
||||
tabListClass="w-full"
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
{#each providerOptions as option}
|
||||
{#each providerOptions.slice(0, 3) as option}
|
||||
<ToggleButton value={option.value} label={option.label} {item} />
|
||||
{/each}
|
||||
<ToggleButtonMore
|
||||
class="ml-auto"
|
||||
togglableItems={providerOptions.slice(3)}
|
||||
{item}
|
||||
bind:selected={() => value?.kind, (v) => v && onProviderChange(v)}
|
||||
/>
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
<!-- Resource Selection -->
|
||||
<div class="flex flex-col border border-gray-200 rounded-md p-2 gap-2">
|
||||
<div class="flex flex-col rounded-md p-2 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
<p class="text-sm font-medium text-primary">resource</p>
|
||||
<p class="text-sm font-normal text-tertiary">resource</p>
|
||||
<ResourcePicker
|
||||
bind:value={
|
||||
() => resourceValueToPath(value?.resource),
|
||||
@@ -183,12 +196,13 @@
|
||||
disabled={disabled || !value?.kind}
|
||||
placeholder="Select resource"
|
||||
selectFirst={true}
|
||||
selectInputClass="!bg-surface"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Model Selection -->
|
||||
<div class="flex flex-col gap-1">
|
||||
<p class="text-sm font-medium text-primary">model</p>
|
||||
<p class="text-sm font-normal text-tertiary">model</p>
|
||||
<Select
|
||||
{items}
|
||||
bind:value={value.model}
|
||||
@@ -203,6 +217,7 @@
|
||||
clearable={false}
|
||||
noItemsMsg={'No models available'}
|
||||
bind:filterText
|
||||
inputClass="min-h-10 !bg-surface disabled:!bg-surface-disabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
valid: boolean
|
||||
create: boolean
|
||||
enumLabels?: Record<string, string> | undefined
|
||||
selectClass?: string
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -22,7 +23,8 @@
|
||||
defaultValue,
|
||||
valid,
|
||||
create,
|
||||
enumLabels = undefined
|
||||
enumLabels = undefined,
|
||||
selectClass = ''
|
||||
}: Props = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -46,7 +48,7 @@
|
||||
<div class="w-full flex-col">
|
||||
<div class="w-full">
|
||||
<Select
|
||||
inputClass={valid ? '' : '!border-red-500/60'}
|
||||
error={!valid}
|
||||
clearable
|
||||
{disabled}
|
||||
autofocus={autofocus ?? undefined}
|
||||
@@ -62,6 +64,7 @@
|
||||
}
|
||||
onFocus={() => dispatch('focus')}
|
||||
onBlur={() => dispatch('blur')}
|
||||
inputClass={selectClass}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { getJsonSchemaFromResource } from './schema/jsonSchemaResource.svelte'
|
||||
import AIProviderPicker from './AIProviderPicker.svelte'
|
||||
import TextInput, { inputBaseClass, inputBorderClass } from './text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
label?: string
|
||||
@@ -71,6 +72,7 @@
|
||||
properties?: { [name: string]: SchemaProperty }
|
||||
}
|
||||
| undefined
|
||||
lightHeaderFont?: boolean
|
||||
displayHeader?: boolean
|
||||
properties?: { [name: string]: SchemaProperty } | undefined
|
||||
nestedRequired?: string[] | undefined
|
||||
@@ -120,6 +122,8 @@
|
||||
| undefined
|
||||
workspace?: string | undefined
|
||||
actions?: import('svelte').Snippet
|
||||
innerBottomSnippet?: import('svelte').Snippet
|
||||
fieldHeaderActions?: import('svelte').Snippet
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -133,7 +137,7 @@
|
||||
oneOf = $bindable(undefined),
|
||||
required = false,
|
||||
pattern = $bindable(undefined),
|
||||
valid = $bindable(undefined),
|
||||
valid = $bindable(undefined), // Note : this should not exist, valid and error should be one coherent state
|
||||
enum_ = $bindable(undefined),
|
||||
disabled = false,
|
||||
itemsType = $bindable(undefined),
|
||||
@@ -176,7 +180,10 @@
|
||||
appPath = undefined,
|
||||
computeS3ForceViewerPolicies = undefined,
|
||||
workspace = undefined,
|
||||
actions
|
||||
actions,
|
||||
innerBottomSnippet,
|
||||
fieldHeaderActions,
|
||||
lightHeaderFont = false
|
||||
}: Props = $props()
|
||||
|
||||
$effect(() => {
|
||||
@@ -463,6 +470,11 @@
|
||||
|
||||
let { debounced, clearDebounce } = debounce(() => compareValues(value), 50)
|
||||
let inputCat = $derived(computeInputCat(type, format, itemsType?.type, enum_, contentEncoding))
|
||||
let displayJsonToggleHeader = $derived(
|
||||
displayHeader &&
|
||||
inputCat === 'list' &&
|
||||
!(itemsType?.resourceType === 's3_object' || itemsType?.resourceType === 's3object')
|
||||
)
|
||||
$effect(() => {
|
||||
oneOf && untrack(() => updateOneOfSelected(oneOf))
|
||||
})
|
||||
@@ -582,24 +594,43 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if displayHeader}
|
||||
<FieldHeader
|
||||
prettify={prettifyHeader}
|
||||
label={title && !emptyString(title) ? title : label}
|
||||
{disabled}
|
||||
{required}
|
||||
{type}
|
||||
{contentEncoding}
|
||||
{format}
|
||||
{simpleTooltip}
|
||||
{lightHeader}
|
||||
{displayType}
|
||||
labelClass={css?.label?.class}
|
||||
/>
|
||||
{/if}
|
||||
<div class="flex">
|
||||
<FieldHeader
|
||||
prettify={prettifyHeader}
|
||||
label={title && !emptyString(title) ? title : label}
|
||||
{disabled}
|
||||
{required}
|
||||
{type}
|
||||
{contentEncoding}
|
||||
{format}
|
||||
{simpleTooltip}
|
||||
{lightHeader}
|
||||
{displayType}
|
||||
labelClass={twMerge(
|
||||
lightHeaderFont ? '!font-normal !text-sm text-tertiary' : '',
|
||||
css?.label?.class
|
||||
)}
|
||||
/>
|
||||
<div class="ml-auto flex gap-2">
|
||||
{#if displayJsonToggleHeader}
|
||||
<Toggle
|
||||
on:change={(e) => {
|
||||
// Once the user has changed the input type, we should not change it back automatically
|
||||
if (!hasIsListJsonChanged) {
|
||||
hasIsListJsonChanged = true
|
||||
}
|
||||
|
||||
{#if description}
|
||||
<div class={twMerge('text-xs italic pb-1 text-secondary', css?.description?.class)}>
|
||||
<pre class="font-main whitespace-normal">{description}</pre>
|
||||
evalValueToRaw()
|
||||
isListJson = !isListJson
|
||||
}}
|
||||
checked={isListJson}
|
||||
textClass="text-secondary"
|
||||
size="xs"
|
||||
options={{ left: 'json' }}
|
||||
/>
|
||||
{/if}
|
||||
{@render fieldHeaderActions?.()}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -624,7 +655,22 @@
|
||||
/>
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
<TextInput
|
||||
inputProps={{
|
||||
autofocus,
|
||||
onfocus: bubble('focus'),
|
||||
onblur: bubble('blur'),
|
||||
disabled,
|
||||
type: 'number',
|
||||
onkeydown: () => (ignoreValueUndefined = true),
|
||||
placeholder: placeholder ?? defaultValue ?? '',
|
||||
min: extra['min'],
|
||||
max: extra['max']
|
||||
}}
|
||||
{error}
|
||||
bind:value
|
||||
/>
|
||||
<!-- <input
|
||||
{autofocus}
|
||||
onfocus={bubble('focus')}
|
||||
onblur={bubble('blur')}
|
||||
@@ -640,7 +686,7 @@
|
||||
bind:value
|
||||
min={extra['min']}
|
||||
max={extra['max']}
|
||||
/>
|
||||
/> -->
|
||||
</div>
|
||||
{/if}
|
||||
{:else if inputCat == 'boolean'}
|
||||
@@ -674,6 +720,7 @@
|
||||
bind:editor
|
||||
{appPath}
|
||||
{computeS3ForceViewerPolicies}
|
||||
bottom={innerBottomSnippet}
|
||||
/>
|
||||
{:else if inputCat == 'object' && format == 'json-schema'}
|
||||
{#await import('$lib/components/EditableSchemaForm.svelte')}
|
||||
@@ -717,8 +764,9 @@
|
||||
<Module.default code={JSON.stringify(value, null, 2)} bind:value />
|
||||
{/await}
|
||||
{:else}
|
||||
<div class="py-4 pr-2 pl-6 border rounded-md w-full">
|
||||
<div class="px-2 pt-2 border rounded-md w-full">
|
||||
<SchemaForm
|
||||
lightHeaderFont
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
@@ -763,14 +811,34 @@
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full">
|
||||
<div class="w-full flex flex-col gap-y-1">
|
||||
{#key redraw}
|
||||
{#if Array.isArray(value)}
|
||||
{#each value ?? [] as v, i}
|
||||
{#if i < itemsLimit}
|
||||
<div class="flex max-w-md mt-1 w-full items-center">
|
||||
<div class="flex w-full items-center relative">
|
||||
{#snippet deleteItemBtn()}
|
||||
<button
|
||||
transition:fade|local={{ duration: 100 }}
|
||||
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2"
|
||||
aria-label="Clear"
|
||||
onclick={() => {
|
||||
value = value.filter((_, index) => index !== i)
|
||||
redraw += 1
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
{/snippet}
|
||||
{#if itemsType?.type == 'number'}
|
||||
<input type="number" bind:value={value[i]} id="arg-input-number-array" />
|
||||
<TextInput
|
||||
inputProps={{ type: 'number', id: 'arg-input-number-array' }}
|
||||
class="pr-8"
|
||||
bind:value={value[i]}
|
||||
/>
|
||||
<div class="absolute z-10 right-1.5">
|
||||
{@render deleteItemBtn()}
|
||||
</div>
|
||||
{:else if itemsType?.type == 'string' && itemsType?.contentEncoding == 'base64'}
|
||||
<input
|
||||
type="file"
|
||||
@@ -778,6 +846,7 @@
|
||||
onchange={(x) => fileChanged(x, (val) => (value[i] = val))}
|
||||
multiple={false}
|
||||
/>
|
||||
{@render deleteItemBtn()}
|
||||
{:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined && itemsType?.properties === undefined && !(format?.startsWith('resource-') && resourceTypes?.includes(format.split('-')[1]))}
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
@@ -787,6 +856,7 @@
|
||||
bind:value={value[i]}
|
||||
/>
|
||||
{/await}
|
||||
{@render deleteItemBtn()}
|
||||
{:else if Array.isArray(itemsType?.enum)}
|
||||
<ArgEnum
|
||||
create={extra['disableCreate'] != true}
|
||||
@@ -804,6 +874,7 @@
|
||||
enum_={itemsType?.enum ?? []}
|
||||
enumLabels={extra['enumLabels']}
|
||||
/>
|
||||
{@render deleteItemBtn()}
|
||||
{:else if (itemsType?.type == 'resource' && itemsType?.resourceType && resourceTypes?.includes(itemsType.resourceType)) || (format?.startsWith('resource-') && resourceTypes?.includes(format.split('-')[1]))}
|
||||
{@const resourceFormat =
|
||||
itemsType?.type == 'resource' &&
|
||||
@@ -816,6 +887,7 @@
|
||||
format={resourceFormat}
|
||||
defaultValue={undefined}
|
||||
/>
|
||||
{@render deleteItemBtn()}
|
||||
{:else if itemsType?.type == 'resource'}
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
@@ -832,9 +904,11 @@
|
||||
bind:value={value[i]}
|
||||
/>
|
||||
{/await}
|
||||
{@render deleteItemBtn()}
|
||||
{:else if itemsType?.type === 'object' && itemsType?.properties}
|
||||
<div class="p-8 border rounded-md w-full">
|
||||
<SchemaForm
|
||||
lightHeaderFont
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
@@ -843,20 +917,17 @@
|
||||
bind:args={value[i]}
|
||||
/>
|
||||
</div>
|
||||
{@render deleteItemBtn()}
|
||||
{:else}
|
||||
<input type="text" bind:value={value[i]} id="arg-input-array" />
|
||||
<TextInput
|
||||
inputProps={{ type: 'text', id: 'arg-input-array' }}
|
||||
class="pr-8"
|
||||
bind:value={value[i]}
|
||||
/>
|
||||
<div class="absolute z-10 right-1.5">
|
||||
{@render deleteItemBtn()}
|
||||
</div>
|
||||
{/if}
|
||||
<button
|
||||
transition:fade|local={{ duration: 100 }}
|
||||
class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2"
|
||||
aria-label="Clear"
|
||||
onclick={() => {
|
||||
value = value.filter((_, index) => index !== i)
|
||||
redraw += 1
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -869,7 +940,7 @@
|
||||
{#if value.startsWith('$res:')}
|
||||
{@render resourceInput()}
|
||||
{:else}
|
||||
<div class="text-red-500">
|
||||
<div class="text-red-500 text-xs">
|
||||
Invalid string value: "{value}", expected array. Click add item to turn it
|
||||
into an array.
|
||||
</div>
|
||||
@@ -877,59 +948,58 @@
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
<div class="flex mt-2 gap-20 items-baseline">
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="mt-1"
|
||||
on:click={() => {
|
||||
if (value == undefined || !Array.isArray(value)) {
|
||||
value = []
|
||||
}
|
||||
if (itemsType?.type == 'number') {
|
||||
value = value.concat(0)
|
||||
} else if (
|
||||
(itemsType?.type == 'object' &&
|
||||
!(
|
||||
format?.startsWith('resource-') &&
|
||||
resourceTypes?.includes(format.split('-')[1])
|
||||
)) ||
|
||||
(itemsType?.type == 'resource' &&
|
||||
!(
|
||||
itemsType?.resourceType && resourceTypes?.includes(itemsType?.resourceType)
|
||||
))
|
||||
) {
|
||||
value = value.concat({})
|
||||
} else {
|
||||
value = value.concat('')
|
||||
}
|
||||
}}
|
||||
id="arg-input-add-item"
|
||||
startIcon={{ icon: Plus }}
|
||||
>
|
||||
Add item
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="text-tertiary"
|
||||
wrapperClasses="w-full mt-2"
|
||||
on:click={() => {
|
||||
if (value == undefined || !Array.isArray(value)) {
|
||||
value = []
|
||||
}
|
||||
if (itemsType?.type == 'number') {
|
||||
value = value.concat(0)
|
||||
} else if (
|
||||
(itemsType?.type == 'object' &&
|
||||
!(
|
||||
format?.startsWith('resource-') &&
|
||||
resourceTypes?.includes(format.split('-')[1])
|
||||
)) ||
|
||||
(itemsType?.type == 'resource' &&
|
||||
!(itemsType?.resourceType && resourceTypes?.includes(itemsType?.resourceType)))
|
||||
) {
|
||||
value = value.concat({})
|
||||
} else {
|
||||
value = value.concat('')
|
||||
}
|
||||
}}
|
||||
id="arg-input-add-item"
|
||||
startIcon={{ icon: Plus }}
|
||||
>
|
||||
Add item
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="mt-2 mr-4">
|
||||
<Toggle
|
||||
on:change={(e) => {
|
||||
// Once the user has changed the input type, we should not change it back automatically
|
||||
if (!hasIsListJsonChanged) {
|
||||
hasIsListJsonChanged = true
|
||||
}
|
||||
{#if !displayHeader}
|
||||
<div class="block mt-2.5 pl-2">
|
||||
<Toggle
|
||||
on:change={(e) => {
|
||||
// Once the user has changed the input type, we should not change it back automatically
|
||||
if (!hasIsListJsonChanged) {
|
||||
hasIsListJsonChanged = true
|
||||
}
|
||||
|
||||
evalValueToRaw()
|
||||
isListJson = !isListJson
|
||||
}}
|
||||
checked={isListJson}
|
||||
textClass="text-secondary"
|
||||
size="xs"
|
||||
options={{ right: 'json' }}
|
||||
/>
|
||||
</div>
|
||||
evalValueToRaw()
|
||||
isListJson = !isListJson
|
||||
}}
|
||||
checked={isListJson}
|
||||
textClass="text-secondary"
|
||||
size="xs"
|
||||
options={{ left: 'json' }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if inputCat == 'dynamic'}
|
||||
<DynamicInput name={label} {otherArgs} {helperScript} bind:value format={format ?? ''} />
|
||||
@@ -952,7 +1022,7 @@
|
||||
/>
|
||||
{:else if inputCat == 'object' || inputCat == 'resource-object' || isListJson}
|
||||
{#if oneOf && oneOf.length >= 2}
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
<div class="flex flex-col gap-2 w-full border rounded-md p-2">
|
||||
{#if oneOf && oneOf.length >= 2}
|
||||
<ToggleButtonGroup
|
||||
selected={oneOfSelected}
|
||||
@@ -992,70 +1062,70 @@
|
||||
{@const obj = oneOf[objIdx]}
|
||||
{#if obj && obj.properties && Object.keys(obj.properties).length > 0}
|
||||
{#key redraw}
|
||||
<div class="py-4 pr-2 pl-6 border rounded w-full">
|
||||
{#if orderEditable}
|
||||
<SchemaFormDnd
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{prettifyHeader}
|
||||
bind:schema={
|
||||
() => ({
|
||||
properties: obj.properties ?? {},
|
||||
order: obj.order,
|
||||
$schema: '',
|
||||
required: obj.required ?? [],
|
||||
type: 'object'
|
||||
}),
|
||||
() => {
|
||||
dispatch('nestedChange')
|
||||
}
|
||||
}
|
||||
bind:args={value}
|
||||
hiddenArgs={[
|
||||
oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind'))
|
||||
? 'kind'
|
||||
: 'label'
|
||||
]}
|
||||
on:reorder={(e) => {
|
||||
if (oneOf && oneOf[objIdx]) {
|
||||
const keys = e.detail
|
||||
oneOf[objIdx].order = keys
|
||||
}
|
||||
}}
|
||||
on:nestedChange
|
||||
{shouldDispatchChanges}
|
||||
/>
|
||||
{:else}
|
||||
<SchemaForm
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{prettifyHeader}
|
||||
hiddenArgs={['label', 'kind']}
|
||||
schema={{
|
||||
properties: obj.properties,
|
||||
{#if orderEditable}
|
||||
<SchemaFormDnd
|
||||
lightHeaderFont
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{prettifyHeader}
|
||||
bind:schema={
|
||||
() => ({
|
||||
properties: obj.properties ?? {},
|
||||
order: obj.order,
|
||||
$schema: '',
|
||||
required: obj.required ?? [],
|
||||
type: 'object'
|
||||
}}
|
||||
bind:args={
|
||||
() => value,
|
||||
(v) => {
|
||||
value = { ...v, [tagKey]: oneOfSelected }
|
||||
}
|
||||
}
|
||||
{shouldDispatchChanges}
|
||||
on:change={() => {
|
||||
}),
|
||||
() => {
|
||||
dispatch('nestedChange')
|
||||
}}
|
||||
on:nestedChange
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
bind:args={value}
|
||||
hiddenArgs={[
|
||||
oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind'))
|
||||
? 'kind'
|
||||
: 'label'
|
||||
]}
|
||||
on:reorder={(e) => {
|
||||
if (oneOf && oneOf[objIdx]) {
|
||||
const keys = e.detail
|
||||
oneOf[objIdx].order = keys
|
||||
}
|
||||
}}
|
||||
on:nestedChange
|
||||
{shouldDispatchChanges}
|
||||
/>
|
||||
{:else}
|
||||
<SchemaForm
|
||||
lightHeaderFont
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
{disabled}
|
||||
{prettifyHeader}
|
||||
hiddenArgs={['label', 'kind']}
|
||||
schema={{
|
||||
properties: obj.properties,
|
||||
order: obj.order,
|
||||
$schema: '',
|
||||
required: obj.required ?? [],
|
||||
type: 'object'
|
||||
}}
|
||||
bind:args={
|
||||
() => value,
|
||||
(v) => {
|
||||
value = { ...v, [tagKey]: oneOfSelected }
|
||||
}
|
||||
}
|
||||
{shouldDispatchChanges}
|
||||
on:change={() => {
|
||||
dispatch('nestedChange')
|
||||
}}
|
||||
on:nestedChange
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
{:else if disabled}
|
||||
<textarea disabled></textarea>
|
||||
@@ -1102,9 +1172,10 @@
|
||||
{/if}
|
||||
</div>
|
||||
{:else if properties && Object.keys(properties).length > 0 && inputCat !== 'list'}
|
||||
<div class={hideNested ? 'hidden' : 'py-4 pr-2 pl-6 border rounded-md w-full'}>
|
||||
<div class={hideNested ? 'hidden' : 'px-2 pt-2 border rounded-md w-full'}>
|
||||
{#if orderEditable}
|
||||
<SchemaFormDnd
|
||||
lightHeaderFont
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
@@ -1135,6 +1206,7 @@
|
||||
/>
|
||||
{:else}
|
||||
<SchemaForm
|
||||
lightHeaderFont
|
||||
{nestedClasses}
|
||||
{onlyMaskPassword}
|
||||
{disablePortal}
|
||||
@@ -1185,8 +1257,8 @@
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
{#if inputCat == 'list'}
|
||||
<div class="block">
|
||||
{#if inputCat == 'list' && !displayHeader}
|
||||
<div class="block mt-2.5 pl-2">
|
||||
<Toggle
|
||||
on:change={(e) => {
|
||||
isListJson = !isListJson
|
||||
@@ -1194,7 +1266,7 @@
|
||||
checked={isListJson}
|
||||
textClass="text-secondary"
|
||||
size="xs"
|
||||
options={{ right: 'json' }}
|
||||
options={{ left: 'json' }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -1215,6 +1287,7 @@
|
||||
dispatch('blur')
|
||||
}}
|
||||
enumLabels={extra['enumLabels']}
|
||||
selectClass="min-h-10"
|
||||
/>
|
||||
</div>
|
||||
{:else if inputCat == 'date'}
|
||||
@@ -1323,12 +1396,7 @@
|
||||
use:autosize
|
||||
onkeydown={onKeyDown}
|
||||
{disabled}
|
||||
class={twMerge(
|
||||
'w-full',
|
||||
valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'
|
||||
)}
|
||||
class={twMerge('w-full', inputBaseClass, inputBorderClass({ error: !!error }))}
|
||||
placeholder={placeholder ?? defaultValue ?? ''}
|
||||
bind:value
|
||||
></textarea>
|
||||
@@ -1339,7 +1407,7 @@
|
||||
<button
|
||||
class="absolute {password || extra?.['password'] == true
|
||||
? 'right-16 top-1.5'
|
||||
: 'right-1 top-1'} opacity-0 group-hover:opacity-100 duration-200 py-1 min-w-min !px-2 items-center text-gray-800 bg-surface-secondary border rounded center-center hover:bg-gray-300 transition-all cursor-pointer"
|
||||
: 'right-1 top-[7px]'} opacity-0 group-hover:opacity-100 duration-200 py-1 min-w-min !px-2 items-center text-gray-800 bg-surface-secondary border rounded center-center hover:bg-gray-300 transition-all cursor-pointer"
|
||||
onclick={() => {
|
||||
pickForField = label
|
||||
itemPicker?.openDrawer?.()
|
||||
@@ -1357,14 +1425,20 @@
|
||||
{@render actions?.()}
|
||||
</div>
|
||||
|
||||
{#if !compact || (error && error != '')}
|
||||
<div class="text-right text-xs text-red-600 dark:text-red-400">
|
||||
{#if disabled || error === ''}
|
||||
|
||||
{:else}
|
||||
{error}
|
||||
{/if}
|
||||
{#if description}
|
||||
<div class={twMerge('text-2xs italic py-1 text-hint', css?.description?.class)}>
|
||||
<pre class="font-main whitespace-normal">{description}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !compact || (error && error != '')}
|
||||
{#if disabled || error === ''}
|
||||
|
||||
{:else}
|
||||
<div class="text-right text-xs text-red-600 dark:text-red-400">
|
||||
{error}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if !noMargin}
|
||||
<div class="mb-2"></div>
|
||||
{/if}
|
||||
@@ -1376,10 +1450,4 @@
|
||||
-webkit-appearance: none !important;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
input[type='number'] {
|
||||
-moz-appearance: textfield !important;
|
||||
appearance: textfield !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import type { SchemaProperty } from '$lib/common'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { tick } from 'svelte'
|
||||
import Select from './select/Select.svelte'
|
||||
|
||||
interface Props {
|
||||
canEditResourceType?: boolean
|
||||
@@ -54,39 +55,44 @@
|
||||
|
||||
{#if canEditResourceType || originalType == 'string[]' || originalType == 'object[]'}
|
||||
<Label label="Items type">
|
||||
<select
|
||||
bind:value={selected}
|
||||
onchange={() => {
|
||||
if (selected == 'enum') {
|
||||
itemsType = { type: 'string', enum: [] }
|
||||
} else if (selected == 'string') {
|
||||
itemsType = { type: 'string' }
|
||||
} else if (selected == 'number') {
|
||||
itemsType = { type: 'number' }
|
||||
} else if (selected == 'object') {
|
||||
itemsType = { ...itemsType, type: 'object' }
|
||||
} else if (selected == 'bytes') {
|
||||
itemsType = { type: 'string', contentEncoding: 'base64' }
|
||||
} else if (selected == 'resource') {
|
||||
itemsType = { type: 'resource', resourceType: itemsType?.resourceType }
|
||||
} else if (selected == 's3object') {
|
||||
itemsType = { type: 'object', resourceType: 's3object' }
|
||||
} else {
|
||||
itemsType = undefined
|
||||
<Select
|
||||
bind:value={
|
||||
() => selected,
|
||||
(v) => {
|
||||
selected = v
|
||||
if (selected == 'enum') {
|
||||
itemsType = { type: 'string', enum: [] }
|
||||
} else if (selected == 'string') {
|
||||
itemsType = { type: 'string' }
|
||||
} else if (selected == 'number') {
|
||||
itemsType = { type: 'number' }
|
||||
} else if (selected == 'object') {
|
||||
itemsType = { ...itemsType, type: 'object' }
|
||||
} else if (selected == 'bytes') {
|
||||
itemsType = { type: 'string', contentEncoding: 'base64' }
|
||||
} else if (selected == 'resource') {
|
||||
itemsType = { type: 'resource', resourceType: itemsType?.resourceType }
|
||||
} else if (selected == 's3object') {
|
||||
itemsType = { type: 'object', resourceType: 's3object' }
|
||||
} else {
|
||||
itemsType = undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
id="array-type-narrowing"
|
||||
>
|
||||
<option value="string"> Items are strings</option>
|
||||
<option value="enum">Items are strings from an enum</option>
|
||||
{#if originalType != 'string[]'}
|
||||
<option value="s3object">Items are S3 objects</option>
|
||||
<option value="object"> Items are objects (JSON)</option>
|
||||
<option value="resource"> Items are resources</option>
|
||||
<option value="number">Items are numbers</option>
|
||||
<option value="bytes">Items are bytes</option>
|
||||
{/if}
|
||||
</select>
|
||||
}
|
||||
items={[
|
||||
{ value: 'string', label: 'Items are strings' },
|
||||
{ value: 'enum', label: 'Items are strings from an enum' },
|
||||
...(originalType != 'string[]'
|
||||
? [
|
||||
{ value: 's3object', label: 'Items are S3 objects' },
|
||||
{ value: 'object', label: 'Items are objects (JSON)' },
|
||||
{ value: 'resource', label: 'Items are resources' },
|
||||
{ value: 'number', label: 'Items are numbers' },
|
||||
{ value: 'bytes', label: 'Items are bytes' }
|
||||
]
|
||||
: [])
|
||||
] as { value: string; label: string }[]}
|
||||
/>
|
||||
</Label>
|
||||
{:else if itemsType?.resourceType}
|
||||
<Label label="Resource type">
|
||||
|
||||
@@ -530,7 +530,7 @@
|
||||
{:else}
|
||||
<!-- WIP -->
|
||||
{#if jsonEnabled && customUi?.jsonOnly != true}
|
||||
<div class="w-full p-3 flex gap-4 justify-end items-center">
|
||||
<div class="w-full px-3 flex gap-4 justify-end items-center">
|
||||
{#if addPropertyInEditorTab}
|
||||
<AddPropertyV2
|
||||
bind:schema
|
||||
@@ -549,8 +549,8 @@
|
||||
label="JSON View"
|
||||
size="xs"
|
||||
options={{
|
||||
right: 'JSON editor',
|
||||
rightTooltip:
|
||||
left: 'JSON editor',
|
||||
leftTooltip:
|
||||
'Arguments can be edited either using the wizard, or by editing their JSON Schema.'
|
||||
}}
|
||||
lightMode
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
autoheight?: boolean
|
||||
lineNumbersWidth?: number
|
||||
lineNumbersOffset?: number
|
||||
showNumbers?: boolean
|
||||
class?: string
|
||||
fontSize?: number
|
||||
}
|
||||
@@ -18,6 +19,7 @@
|
||||
lineNumbersWidth = 51,
|
||||
lineNumbersOffset = 0,
|
||||
class: className = '',
|
||||
showNumbers = true,
|
||||
fontSize = 14
|
||||
}: Props = $props()
|
||||
|
||||
@@ -76,7 +78,9 @@
|
||||
class="margin-view-overlays"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
style="position: absolute; font-family: {fontFamily}; font-weight: normal; font-size: {fontSize}px; font-feature-settings: 'liga' 0, 'calt' 0; font-variation-settings: normal; line-height: {lineHeight}px; letter-spacing: 0px; width: {lineNumbersWidth}px; height: 4893px;"
|
||||
style="position: absolute; font-family: {fontFamily}; font-weight: normal; font-size: {fontSize}px; font-feature-settings: 'liga' 0, 'calt' 0; font-variation-settings: normal; line-height: {lineHeight}px; letter-spacing: 0px; width: {lineNumbersWidth}px; height: 4893px; display: {!showNumbers
|
||||
? 'none'
|
||||
: 'block'};"
|
||||
>
|
||||
{#each lines as _, i}
|
||||
<div style="top:{lineHeight * i}px;height:{lineHeight}px;">
|
||||
|
||||
@@ -40,17 +40,15 @@
|
||||
{/if}
|
||||
|
||||
{#if displayType}
|
||||
{#if format && !format.startsWith('resource') && !format.startsWith('jsonschema-')}
|
||||
<span class="text-xs italic ml-2 text-tertiary dark:text-indigo-400">
|
||||
<span class="text-xs italic ml-2 text-hint dark:text-indigo-400">
|
||||
{#if format && !format.startsWith('resource') && !format.startsWith('jsonschema-')}
|
||||
{format}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="text-xs italic ml-2 text-tertiary dark:text-indigo-400">
|
||||
{:else}
|
||||
{type ?? 'any'}{contentEncoding && contentEncoding != ''
|
||||
? `, encoding: ${contentEncoding}`
|
||||
: ''}
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
{#if !emptyString(simpleTooltip)}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import { Plug } from 'lucide-svelte'
|
||||
import { Button } from './common'
|
||||
import AnimatedButton from './common/button/AnimatedButton.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let connecting: boolean
|
||||
export let id: undefined | string = undefined
|
||||
export let wrapperClasses = ''
|
||||
</script>
|
||||
|
||||
<AnimatedButton animate={connecting} baseRadius="6px" animationDuration="2s" marginWidth="2px">
|
||||
@@ -12,10 +14,14 @@
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs2"
|
||||
btnClasses={connecting ? 'text-blue-500' : 'text-primary'}
|
||||
btnClasses={twMerge(
|
||||
connecting ? 'text-blue-500' : 'text-tertiary',
|
||||
'group/plug-btn overflow-clip flex p-0'
|
||||
)}
|
||||
on:click
|
||||
{...id ? { id } : {}}
|
||||
{wrapperClasses}
|
||||
>
|
||||
<Plug size={16} /> →
|
||||
<Plug size={14} />
|
||||
</Button>
|
||||
</AnimatedButton>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { tick } from 'svelte'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { buildPrefixRegex } from './flows/previousResults'
|
||||
import type VariableEditor from './VariableEditor.svelte'
|
||||
import type ItemPicker from './ItemPicker.svelte'
|
||||
@@ -39,6 +38,7 @@
|
||||
import FlowPlugConnect from './FlowPlugConnect.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import S3ArrayHelperButton from './S3ArrayHelperButton.svelte'
|
||||
import { inputBorderClass } from './text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
schema: Schema | { properties?: Record<string, any>; required?: string[] }
|
||||
@@ -470,17 +470,9 @@
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if arg != undefined && !hidden}
|
||||
<div
|
||||
class={twMerge(
|
||||
'pl-2 pt-2 pb-2 ml-2 relative hover:bg-surface hover:shadow-md transition-all duration-200',
|
||||
$propPickerConfig?.propName == argName
|
||||
? 'bg-surface border-l-4 border-blue-500 shadow-md rounded-l-md z-50 '
|
||||
: 'hover:rounded-md',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-row justify-between gap-1 pb-1 px-2">
|
||||
{#if arg != undefined}
|
||||
<div class={twMerge('pt-2 pb-2 relative group', className)}>
|
||||
<div class="flex flex-row justify-between gap-1 pb-1">
|
||||
<div class="flex flex-wrap grow">
|
||||
<FieldHeader
|
||||
label={argName}
|
||||
@@ -496,7 +488,7 @@
|
||||
{#if isStaticTemplate(inputCat)}
|
||||
<div>
|
||||
<span
|
||||
class="bg-blue-100 text-blue-800 text-sm font-medium mr-2 px-2.5 !py-0.5 rounded ml-2 {propertyType ==
|
||||
class="border border-blue-100 text-blue-500 text-2xs font-medium mr-2 px-1 !py-[1px] rounded ml-2 {propertyType ==
|
||||
'static' && arg.type === 'javascript'
|
||||
? 'visible'
|
||||
: 'invisible'}"
|
||||
@@ -507,7 +499,9 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#if !noDynamicToggle}
|
||||
<div class="flex flex-row gap-x-2 gap-y-1 flex-wrap z-10 items-center">
|
||||
<div
|
||||
class="flex flex-row gap-x-2 z-10 absolute right-0 group-hover:bg-surface transition-colors"
|
||||
>
|
||||
{#if enableAi}
|
||||
<StepInputGen
|
||||
bind:this={stepInputGen}
|
||||
@@ -536,8 +530,40 @@
|
||||
}}
|
||||
{pickableProperties}
|
||||
{argName}
|
||||
btnClass={twMerge(
|
||||
'h-6 min-w-8 px-2',
|
||||
'group-hover:opacity-100 transition-opacity',
|
||||
!connecting ? 'opacity-0' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if propPickerWrapperContext}
|
||||
<FlowPlugConnect
|
||||
wrapperClasses={twMerge(
|
||||
connecting ? 'h-5 w-7' : 'h-6 w-8',
|
||||
'group-hover:opacity-100 transition-opacity p-0',
|
||||
!connecting ? 'opacity-0' : ''
|
||||
)}
|
||||
id="flow-editor-plug"
|
||||
{connecting}
|
||||
on:click={() => {
|
||||
if (
|
||||
$propPickerConfig?.propName == argName &&
|
||||
$propPickerConfig?.insertionMode == 'connect'
|
||||
) {
|
||||
clearFocus()
|
||||
} else {
|
||||
focusProp?.(argName, 'connect', (path) => {
|
||||
connectProperty(path)
|
||||
dispatch('change', { argName })
|
||||
return true
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div>
|
||||
<ToggleButtonGroup
|
||||
selected={propertyType}
|
||||
@@ -593,19 +619,18 @@
|
||||
propertyType = 'static'
|
||||
}
|
||||
}}
|
||||
class="h-6"
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
{#if isStaticTemplate(inputCat)}
|
||||
<ToggleButton
|
||||
tooltip={`Write text or surround javascript with \`\$\{\` and \`\}\`. Use \`results\` to connect to another node\'s output.`}
|
||||
light
|
||||
value="static"
|
||||
size="xs2"
|
||||
label={'${}'}
|
||||
{item}
|
||||
/>
|
||||
{:else}
|
||||
<ToggleButton small label="Static" value="static" {item} />
|
||||
<ToggleButton small label="static" value="static" {item} />
|
||||
{/if}
|
||||
|
||||
{#if codeInjectionDetected && propertyType == 'static'}
|
||||
@@ -634,27 +659,6 @@
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
{#if propPickerWrapperContext && inputCat !== 'dynamic'}
|
||||
<FlowPlugConnect
|
||||
id="flow-editor-plug"
|
||||
{connecting}
|
||||
on:click={() => {
|
||||
if (
|
||||
$propPickerConfig?.propName == argName &&
|
||||
$propPickerConfig?.insertionMode == 'connect'
|
||||
) {
|
||||
clearFocus()
|
||||
} else {
|
||||
focusProp?.(argName, 'connect', (path) => {
|
||||
connectProperty(path)
|
||||
dispatch('change', { argName })
|
||||
return true
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -674,12 +678,7 @@
|
||||
<div class="relative flex flex-row items-top gap-2 justify-between">
|
||||
<div class="min-w-0 grow">
|
||||
{#if isStaticTemplate(inputCat) && propertyType == 'static' && !noDynamicToggle}
|
||||
{#if argName && schema?.properties?.[argName]?.description}
|
||||
<div class="text-xs italic pb-1 text-secondary">
|
||||
<pre class="font-main">{schema.properties[argName].description}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="mt-2 min-h-[28px]">
|
||||
<div class="min-h-[28px]">
|
||||
{#if arg}
|
||||
<TemplateEditor
|
||||
bind:this={monacoTemplate}
|
||||
@@ -694,8 +693,14 @@
|
||||
dispatch('change', { argName, arg })
|
||||
}}
|
||||
loadAsync
|
||||
class="bg-surface-secondary"
|
||||
/>
|
||||
{/if}
|
||||
{#if argName && schema?.properties?.[argName]?.description}
|
||||
<div class="text-xs italic py-1 text-hint">
|
||||
<pre class="font-main">{schema.properties[argName].description}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if (propertyType === undefined || propertyType == 'static') && schema?.properties?.[argName]}
|
||||
<ArgInput
|
||||
@@ -743,23 +748,30 @@
|
||||
transform?.type === 'static' ? transform.value : transform?.expr
|
||||
])
|
||||
)}
|
||||
/>
|
||||
|
||||
{#if shouldShowS3ArrayHelper}
|
||||
<S3ArrayHelperButton
|
||||
{connecting}
|
||||
onClick={() =>
|
||||
switchToJsAndConnect((path) => appendPathToArrayExpr(arg.expr, path))}
|
||||
/>
|
||||
{/if}
|
||||
>
|
||||
{#snippet innerBottomSnippet()}
|
||||
{#if shouldShowS3ArrayHelper}
|
||||
<S3ArrayHelperButton
|
||||
{connecting}
|
||||
onClick={() =>
|
||||
switchToJsAndConnect((path) => appendPathToArrayExpr(arg.expr, path))}
|
||||
/>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</ArgInput>
|
||||
{:else if arg.expr != undefined}
|
||||
<div class="border mt-2">
|
||||
<div
|
||||
class={`bg-surface-secondary rounded-md min-h-[2.5rem] flex flex-col pl-4 ${inputBorderClass({ forceFocus: focused })}`}
|
||||
>
|
||||
<SimpleEditor
|
||||
bind:this={monaco}
|
||||
bind:code={arg.expr}
|
||||
{extraLib}
|
||||
lang="javascript"
|
||||
shouldBindKey={false}
|
||||
renderLineHighlight="none"
|
||||
hideLineNumbers
|
||||
fakeMonacoPlaceholderClass="mt-2"
|
||||
on:focus={() => {
|
||||
focused = true
|
||||
focusProp?.(argName, 'insert', (path) => {
|
||||
@@ -778,12 +790,10 @@
|
||||
/>
|
||||
<!-- <input type="text" bind:value={arg.expr} /> -->
|
||||
</div>
|
||||
{#if !hideHelpButton}
|
||||
<DynamicInputHelpBox />
|
||||
{/if}
|
||||
|
||||
{#if shouldShowS3ArrayHelper}
|
||||
<S3ArrayHelperButton
|
||||
class="mt-2"
|
||||
{connecting}
|
||||
onClick={() =>
|
||||
focusProp?.(argName, 'connect', (path) => {
|
||||
@@ -793,6 +803,10 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !hideHelpButton}
|
||||
<DynamicInputHelpBox />
|
||||
{/if}
|
||||
|
||||
<div class="mb-2"></div>
|
||||
{:else}
|
||||
Not recognized input type {argName} ({arg.expr}, {propertyType})
|
||||
@@ -807,29 +821,6 @@
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if $propPickerConfig?.propName == argName}
|
||||
<div
|
||||
class="text-blue-500 absolute top-2 lg:-right-2.5 -right-1"
|
||||
in:fade={{ duration: 200 }}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="24 24 12 12 24 0" />
|
||||
</svg>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-0"></div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import ItemPicker from './ItemPicker.svelte'
|
||||
import VariableEditor from './VariableEditor.svelte'
|
||||
import { Plus } from 'lucide-svelte'
|
||||
import ResizeTransitionWrapper from './common/ResizeTransitionWrapper.svelte'
|
||||
|
||||
interface Props {
|
||||
schema: Schema | { properties?: Record<string, any> }
|
||||
@@ -80,9 +81,9 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="w-full {clazz}">
|
||||
<div class="w-full mb-6 {clazz}">
|
||||
{#if enableAi}
|
||||
<div class="px-0.5 pt-0.5">
|
||||
<div class="mt-2">
|
||||
<StepInputsGen
|
||||
{pickableProperties}
|
||||
argNames={keys
|
||||
@@ -101,7 +102,7 @@
|
||||
{#if keys.length > 0}
|
||||
{#each keys as argName, index (argName)}
|
||||
{#if (!filter || filter.includes(argName)) && Object.keys(schema.properties ?? {}).includes(argName)}
|
||||
<div class="pt-2 relative">
|
||||
<ResizeTransitionWrapper class="mt-2 relative" innerClass="w-full" vertical>
|
||||
<InputTransformForm
|
||||
{previousModuleId}
|
||||
bind:arg={args[argName]}
|
||||
@@ -122,7 +123,7 @@
|
||||
Object.entries(args ?? {}).filter(([key]) => key !== argName)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</ResizeTransitionWrapper>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
|
||||
import Button from './common/button/Button.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { inputBorderClass } from './text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
code: string | undefined
|
||||
@@ -34,6 +36,7 @@
|
||||
|
||||
let tooBig = $derived(code && code?.length > 1000000)
|
||||
let loadTooBigAnyway = $state(false)
|
||||
let focused = $state(false)
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
|
||||
@@ -66,12 +69,17 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="border w-full">
|
||||
<div
|
||||
class={twMerge(
|
||||
'w-full rounded-md bg-surface-secondary',
|
||||
inputBorderClass({ error: !!error, forceFocus: focused })
|
||||
)}
|
||||
>
|
||||
<SimpleEditor
|
||||
{loadAsync}
|
||||
{small}
|
||||
on:focus
|
||||
on:blur
|
||||
on:focus={() => (dispatch('focus'), (focused = true))}
|
||||
on:blur={() => (dispatch('blur'), (focused = false))}
|
||||
bind:this={editor}
|
||||
on:change
|
||||
autoHeight
|
||||
@@ -80,10 +88,11 @@
|
||||
class={clazz}
|
||||
{disabled}
|
||||
{fixedOverflowWidgets}
|
||||
renderLineHighlight="none"
|
||||
/>
|
||||
</div>
|
||||
{#if error != ''}
|
||||
<span class="text-red-600 text-xs">{error}</span>
|
||||
<span class="text-red-600 text-xs mt-1">{error}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -150,20 +150,21 @@
|
||||
nullable={schema.properties[argName].nullable}
|
||||
title={schema.properties[argName].title}
|
||||
placeholder={schema.properties[argName].placeholder}
|
||||
/>
|
||||
{/if}
|
||||
{#if stepsInputArgs?.isArgManuallySet(mod.id, argName)}
|
||||
<div class="pt-6 mt-0.5">
|
||||
<Button
|
||||
on:click={() => {
|
||||
plugIt(argName)
|
||||
}}
|
||||
size="sm"
|
||||
variant="border"
|
||||
color="light"
|
||||
title="Re-evaluate input step"><RefreshCw size={14} /></Button
|
||||
>
|
||||
</div>
|
||||
>
|
||||
{#snippet fieldHeaderActions()}
|
||||
{#if stepsInputArgs?.isArgManuallySet(mod.id, argName)}
|
||||
<Button
|
||||
on:click={() => {
|
||||
plugIt(argName)
|
||||
}}
|
||||
size="xs2"
|
||||
variant="contained"
|
||||
color="light"
|
||||
title="Re-evaluate input step"><RefreshCw size={12} /></Button
|
||||
>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</ArgInput>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import Toggle from './Toggle.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { selectOptions } from './apps/editor/component'
|
||||
import Select from './select/Select.svelte'
|
||||
import TextInput from './text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
min: number | undefined
|
||||
@@ -44,7 +46,11 @@
|
||||
options={{ right: 'Enabled' }}
|
||||
size="xs"
|
||||
/>
|
||||
{/snippet} <input type="number" bind:value={min} disabled={!minChecked} />
|
||||
{/snippet}
|
||||
<TextInput
|
||||
inputProps={{ type: 'number', disabled: !minChecked }}
|
||||
bind:value={() => min?.toString(), (v) => (min = v ? parseInt(v) : undefined)}
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Label label="Max" class="w-full col-span-1 ">
|
||||
@@ -68,31 +74,41 @@
|
||||
size="xs"
|
||||
/>
|
||||
{/snippet}
|
||||
<input type="number" bind:value={max} disabled={!maxChecked} />
|
||||
<TextInput
|
||||
inputProps={{ type: 'number', disabled: !maxChecked }}
|
||||
bind:value={() => max?.toString(), (v) => (max = v ? parseInt(v) : undefined)}
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<Label label="Currency" class="w-full col-span-2">
|
||||
<div class="flex gap-2">
|
||||
<Label label="Currency" class=" col-span-2">
|
||||
{#snippet header()}
|
||||
<Tooltip light small>
|
||||
Select a currency to display the number in. If a currency is selected, you can also select
|
||||
a locale to format the number according to that locale.
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
<select bind:value={currency}>
|
||||
<option value={undefined}> No currency </option>
|
||||
{#each selectOptions.currencyOptions as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Select
|
||||
bind:value={
|
||||
() => currency,
|
||||
(v) => {
|
||||
currency = v
|
||||
if (!v) currencyLocale = undefined
|
||||
}
|
||||
}
|
||||
items={selectOptions.currencyOptions.map((c) => ({ label: c, value: c }))}
|
||||
placeholder="No currency"
|
||||
clearable
|
||||
/>
|
||||
</Label>
|
||||
<Label label="Currency locale" class="w-full">
|
||||
<select bind:value={currencyLocale} disabled={!currency}>
|
||||
<option value={undefined}> No locale </option>
|
||||
{#each selectOptions.localeOptions as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<Select
|
||||
bind:value={currencyLocale}
|
||||
items={selectOptions.localeOptions.map((c) => ({ label: c, value: c }))}
|
||||
placeholder="No locale"
|
||||
disabled={!currency}
|
||||
clearable
|
||||
/>
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,11 +6,13 @@
|
||||
import ResourceEditorDrawer from './ResourceEditorDrawer.svelte'
|
||||
|
||||
import { Button } from './common'
|
||||
import { Pen, Plus, RotateCw } from 'lucide-svelte'
|
||||
import { Loader2, Pen, Plus, RotateCw } from 'lucide-svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Select from './select/Select.svelte'
|
||||
import DbManagerDrawer from './DBManagerDrawer.svelte'
|
||||
import ExploreAssetButton, { assetCanBeExplored } from './ExploreAssetButton.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import DropdownV2 from './DropdownV2.svelte'
|
||||
|
||||
interface Props {
|
||||
initialValue?: string | undefined
|
||||
@@ -24,6 +26,7 @@
|
||||
expressOAuthSetup?: boolean
|
||||
defaultValues?: Record<string, any> | undefined
|
||||
placeholder?: string | undefined
|
||||
selectInputClass?: string
|
||||
onClear?: () => void
|
||||
excludedValues?: string[]
|
||||
}
|
||||
@@ -40,6 +43,7 @@
|
||||
expressOAuthSetup = false,
|
||||
defaultValues = undefined,
|
||||
placeholder = undefined,
|
||||
selectInputClass = '',
|
||||
onClear = undefined,
|
||||
excludedValues = undefined
|
||||
}: Props = $props()
|
||||
@@ -176,91 +180,102 @@
|
||||
}}
|
||||
/>
|
||||
<!-- {JSON.stringify({ value, collection })} -->
|
||||
<div class="flex flex-col w-full items-start min-h-9">
|
||||
<div class="flex flex-col w-full items-start min-h-10">
|
||||
<div class="flex flex-row w-full items-center">
|
||||
{#if collection?.length > 0}
|
||||
<Select
|
||||
{disabled}
|
||||
{disablePortal}
|
||||
bind:value={
|
||||
() => value,
|
||||
(v) => {
|
||||
value = v
|
||||
valueType = collection.find((x) => x?.value == v)?.type
|
||||
}
|
||||
<Select
|
||||
{disabled}
|
||||
{disablePortal}
|
||||
bind:value={
|
||||
() => value,
|
||||
(v) => {
|
||||
value = v
|
||||
valueType = collection.find((x) => x?.value == v)?.type
|
||||
}
|
||||
onClear={() => {
|
||||
initialValue = undefined
|
||||
value = undefined
|
||||
valueType = undefined
|
||||
onClear?.()
|
||||
}}
|
||||
items={collection}
|
||||
clearable
|
||||
class="text-clip grow min-w-0"
|
||||
placeholder={placeholder ?? `${resourceType ?? 'any'} resource`}
|
||||
/>
|
||||
{:else if !loading}
|
||||
<div class="text-2xs text-tertiary mr-2">0 found</div>
|
||||
{/if}
|
||||
{#if !loading}
|
||||
<div class="mx-0.5"></div>
|
||||
{#if value && value != ''}
|
||||
}
|
||||
onClear={() => {
|
||||
initialValue = undefined
|
||||
value = undefined
|
||||
valueType = undefined
|
||||
onClear?.()
|
||||
}}
|
||||
items={collection}
|
||||
clearable
|
||||
class="text-clip grow min-w-0"
|
||||
inputClass={twMerge('min-h-10', selectInputClass)}
|
||||
placeholder={placeholder ?? `${resourceType ?? 'any'} resource`}
|
||||
itemLabelWrapperClasses="flex-1"
|
||||
itemButtonWrapperClasses="flex"
|
||||
>
|
||||
{#snippet endSnippet({ item, close })}
|
||||
<Button
|
||||
{disabled}
|
||||
color="light"
|
||||
variant="contained"
|
||||
size="sm"
|
||||
btnClasses="w-8 px-0.5 py-1.5"
|
||||
on:click={() => resourceEditor?.initEdit?.(value ?? '')}
|
||||
size="xs3"
|
||||
btnClasses="w-8 px-0.5 py-1.5 bg-transparent hover:bg-surface-secondary"
|
||||
wrapperClasses="-mr-2 pl-1"
|
||||
on:click={() => (resourceEditor?.initEdit?.(item.value ?? ''), close())}
|
||||
startIcon={{ icon: Pen }}
|
||||
iconOnly
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if resourceType?.includes(',')}
|
||||
{#each resourceType.split(',') as rt}
|
||||
{/snippet}
|
||||
{#snippet bottomSnippet({ close })}
|
||||
<div class="flex bg-surface border-t divide-x">
|
||||
{#if resourceType?.includes(',')}
|
||||
<DropdownV2
|
||||
enableFlyTransition
|
||||
items={resourceType?.split(',').map((rt) => ({
|
||||
displayName: `${rt} resource`,
|
||||
icon: Plus,
|
||||
action: () => (appConnect?.open?.(rt), close())
|
||||
})) ?? []}
|
||||
>
|
||||
{#snippet buttonReplacement()}
|
||||
<Button
|
||||
{disabled}
|
||||
color="light"
|
||||
variant="contained"
|
||||
wrapperClasses="flex-1"
|
||||
btnClasses="rounded-none mt-0.5"
|
||||
size="sm"
|
||||
startIcon={{ icon: Plus }}
|
||||
>
|
||||
Add a resource
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownV2>
|
||||
{:else}
|
||||
<Button
|
||||
{disabled}
|
||||
color="light"
|
||||
variant="contained"
|
||||
wrapperClasses="flex-1"
|
||||
btnClasses="rounded-none"
|
||||
size="sm"
|
||||
on:click={() => (appConnect?.open?.(resourceType), close())}
|
||||
startIcon={{ icon: Plus }}
|
||||
>
|
||||
Add a {resourceType} resource
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
{disabled}
|
||||
color="light"
|
||||
variant="contained"
|
||||
color="light"
|
||||
btnClasses="rounded-none"
|
||||
size="sm"
|
||||
btnClasses="w-8 px-0.5 py-1.5"
|
||||
on:click={() => appConnect?.open?.(rt)}
|
||||
startIcon={{ icon: Plus }}>{rt}</Button
|
||||
>
|
||||
{/each}
|
||||
{:else}
|
||||
<Button
|
||||
{disabled}
|
||||
color="light"
|
||||
variant="border"
|
||||
size="sm"
|
||||
on:click={() => appConnect?.open?.(resourceType)}
|
||||
startIcon={{ icon: Plus }}
|
||||
iconOnly={collection?.length > 0}
|
||||
>{#if collection?.length == 0}
|
||||
Add a {resourceType} resource
|
||||
{/if}</Button
|
||||
>
|
||||
<div class="mx-0.5"></div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="light"
|
||||
btnClasses="w-8 px-0.5 py-1.5"
|
||||
size="sm"
|
||||
on:click={() => {
|
||||
loadResources(resourceType)
|
||||
}}
|
||||
startIcon={{ icon: RotateCw }}
|
||||
iconOnly
|
||||
/>
|
||||
on:click={() => {
|
||||
loadResources(resourceType)
|
||||
}}
|
||||
startIcon={loading ? { icon: Loader2, classes: 'animate-spin' } : { icon: RotateCw }}
|
||||
iconOnly
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</Select>
|
||||
</div>
|
||||
{#if showSchemaExplorer && value && assetCanBeExplored({ kind: 'resource', path: value }, { resource_type: resourceType })}
|
||||
<ExploreAssetButton
|
||||
class="mt-1"
|
||||
_resourceMetadata={{ resource_type: resourceType }}
|
||||
asset={{ kind: 'resource', path: value }}
|
||||
{dbManagerDrawer}
|
||||
|
||||
@@ -6,15 +6,27 @@
|
||||
label?: string
|
||||
onClick: () => void
|
||||
connecting?: boolean
|
||||
class?: string
|
||||
}
|
||||
|
||||
let { label = 'Add object from an expression', onClick, connecting = false }: Props = $props()
|
||||
let {
|
||||
label = 'Add object from an expression',
|
||||
onClick,
|
||||
connecting = false,
|
||||
class: className = ''
|
||||
}: Props = $props()
|
||||
</script>
|
||||
|
||||
{#if !connecting}
|
||||
<div class="mt-2 mb-2">
|
||||
<Button variant="border" color="light" size="xs" startIcon={{ icon: Plug }} onclick={onClick}>
|
||||
{label}
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="font-normal text-tertiary"
|
||||
startIcon={{ icon: Plug }}
|
||||
onclick={onClick}
|
||||
wrapperClasses={className}
|
||||
>
|
||||
{label}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
import { dragHandleZone, type Options as DndOptions } from '@windmill-labs/svelte-dnd-action'
|
||||
import type { SchemaDiff } from '$lib/components/schema/schemaUtils.svelte'
|
||||
import type { ComponentCustomCSS } from './apps/types'
|
||||
import ResizeTransitionWrapper from './common/ResizeTransitionWrapper.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
interface Props {
|
||||
schema: Schema | any
|
||||
@@ -56,6 +58,7 @@
|
||||
displayType?: boolean
|
||||
appPath?: string | undefined
|
||||
className?: string
|
||||
lightHeaderFont?: boolean
|
||||
computeS3ForceViewerPolicies?:
|
||||
| (() =>
|
||||
| {
|
||||
@@ -106,6 +109,7 @@
|
||||
displayType = true,
|
||||
appPath = undefined,
|
||||
className = '',
|
||||
lightHeaderFont = false,
|
||||
computeS3ForceViewerPolicies = undefined,
|
||||
workspace = undefined,
|
||||
actions: actions_render = undefined
|
||||
@@ -269,10 +273,15 @@
|
||||
{#if keys.length > 0 && args}
|
||||
{#each fields as item, i (item.id)}
|
||||
{@const argName = item.value}
|
||||
<div
|
||||
class={typeof diff[argName] === 'object' && diff[argName].diff !== 'same'
|
||||
? 'bg-red-300 dark:bg-red-800 rounded-md'
|
||||
: ''}
|
||||
<ResizeTransitionWrapper
|
||||
vertical
|
||||
class={twMerge(
|
||||
typeof diff[argName] === 'object' &&
|
||||
diff[argName].diff !== 'same' &&
|
||||
'bg-red-300 dark:bg-red-800 rounded-md',
|
||||
'relative'
|
||||
)}
|
||||
innerClass="w-full"
|
||||
>
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
{#if !hiddenArgs.includes(argName) && keys.includes(argName)}
|
||||
@@ -280,6 +289,7 @@
|
||||
{@const formerProperty = diff[argName].oldSchema}
|
||||
<div class="px-2">
|
||||
<ArgInput
|
||||
{lightHeaderFont}
|
||||
{disablePortal}
|
||||
{resourceTypes}
|
||||
{prettifyHeader}
|
||||
@@ -342,6 +352,7 @@
|
||||
{JSON.stringify(args?.[argName])} -->
|
||||
{#if !hidden[argName]}
|
||||
<ArgInput
|
||||
{lightHeaderFont}
|
||||
on:change={() => {
|
||||
dispatch('change')
|
||||
}}
|
||||
@@ -443,7 +454,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</ResizeTransitionWrapper>
|
||||
{/each}
|
||||
{:else if !shouldHideNoInputs}
|
||||
<div class="text-secondary text-sm">No inputs</div>
|
||||
|
||||
@@ -537,7 +537,7 @@
|
||||
<SplitPanesWrapper>
|
||||
<Splitpanes class="!overflow-visible">
|
||||
<Pane bind:size={codePanelSize} minSize={10} class="!overflow-visible">
|
||||
<div class="h-full !overflow-visible bg-gray-50 dark:bg-[#272D38] relative">
|
||||
<div class="h-full !overflow-visible bg-surface dark:bg-[#272D38] relative">
|
||||
<div class="absolute top-2 right-4 z-10 flex flex-row gap-2">
|
||||
{#if assets?.length}
|
||||
<AssetsDropdownButton {assets} />
|
||||
|
||||
@@ -85,10 +85,12 @@
|
||||
allowVim = false,
|
||||
tailwindClasses = [],
|
||||
class: className = '',
|
||||
fakeMonacoPlaceholderClass = '',
|
||||
loadAsync = false,
|
||||
key,
|
||||
disabled = false,
|
||||
minHeight = 1000
|
||||
minHeight = 1000,
|
||||
renderLineHighlight = 'line'
|
||||
}: {
|
||||
lang: string
|
||||
code?: string
|
||||
@@ -110,11 +112,13 @@
|
||||
allowVim?: boolean
|
||||
tailwindClasses?: string[]
|
||||
class?: string
|
||||
fakeMonacoPlaceholderClass?: string
|
||||
loadAsync?: boolean
|
||||
initialCursorPos?: IPosition
|
||||
key?: string
|
||||
disabled?: boolean
|
||||
minHeight?: number
|
||||
renderLineHighlight?: 'all' | 'line' | 'gutter' | 'none'
|
||||
} = $props()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -332,6 +336,11 @@
|
||||
$relativeLineNumbers
|
||||
),
|
||||
model,
|
||||
padding: {
|
||||
bottom: 8,
|
||||
top: 8
|
||||
},
|
||||
renderLineHighlight,
|
||||
lineDecorationsWidth: 6,
|
||||
lineNumbersMinChars: 2,
|
||||
fontSize: fontSize,
|
||||
@@ -602,9 +611,11 @@
|
||||
<FakeMonacoPlaceHolder
|
||||
{code}
|
||||
autoheight
|
||||
lineNumbersWidth={(23 * fontSize) / 14}
|
||||
lineNumbersWidth={hideLineNumbers ? 0 : (23 * fontSize) / 14}
|
||||
lineNumbersOffset={fontSize == 14 ? -8 : -11}
|
||||
{fontSize}
|
||||
showNumbers={!hideLineNumbers}
|
||||
class={fakeMonacoPlaceholderClass}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import RegexGen from './copilot/RegexGen.svelte'
|
||||
import TextInput from './text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
pattern: string | undefined
|
||||
@@ -326,7 +327,10 @@
|
||||
{:else if kind == 'none'}
|
||||
{#if !noExtra}
|
||||
<Label label="Min textarea rows">
|
||||
<input type="number" bind:value={minRows} />
|
||||
<TextInput
|
||||
inputProps={{ type: 'number' }}
|
||||
bind:value={() => minRows?.toString(), (v) => (minRows = v ? parseInt(v) : undefined)}
|
||||
/>
|
||||
</Label>
|
||||
{/if}
|
||||
{:else if kind === 'base64'}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
import EditorTheme from './EditorTheme.svelte'
|
||||
import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte'
|
||||
import { setMonacoJsonOptions } from './monacoLanguagesOptions'
|
||||
import { inputBorderClass } from './text_input/TextInput.svelte'
|
||||
|
||||
export const conf = {
|
||||
wordPattern:
|
||||
@@ -449,7 +450,14 @@
|
||||
lineDecorationsWidth: 6,
|
||||
lineNumbersMinChars: 2,
|
||||
fontSize,
|
||||
suggestOnTriggerCharacters: true
|
||||
suggestOnTriggerCharacters: true,
|
||||
renderLineHighlight: 'none',
|
||||
lineNumbers: 'off',
|
||||
|
||||
padding: {
|
||||
bottom: 8,
|
||||
top: 8
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('Error loading monaco:', e)
|
||||
@@ -498,10 +506,12 @@
|
||||
|
||||
editor.onDidFocusEditorText(() => {
|
||||
dispatch('focus')
|
||||
isFocus = true
|
||||
})
|
||||
|
||||
editor.onDidBlurEditorText(() => {
|
||||
dispatch('blur')
|
||||
isFocus = false
|
||||
updateCode()
|
||||
})
|
||||
|
||||
@@ -597,6 +607,7 @@
|
||||
editor?.focus()
|
||||
}
|
||||
|
||||
let isFocus = false
|
||||
let mounted = false
|
||||
let loadTimeout: number | undefined = undefined
|
||||
onMount(async () => {
|
||||
@@ -650,19 +661,18 @@
|
||||
{#if !editor}
|
||||
<FakeMonacoPlaceHolder
|
||||
autoheight
|
||||
showNumbers={false}
|
||||
{code}
|
||||
lineNumbersWidth={23}
|
||||
lineNumbersOffset={-8}
|
||||
class="border template nonmain-editor rounded min-h-4 mx-0.5 overflow-clip"
|
||||
lineNumbersWidth={14}
|
||||
lineNumbersOffset={-20}
|
||||
class="template nonmain-editor rounded-md min-h-4 bg-surface-secondary !py-[9px] overflow-clip"
|
||||
/>
|
||||
{/if}
|
||||
<div
|
||||
bind:this={divEl}
|
||||
style="height: 18px;"
|
||||
class="{$$props.class ??
|
||||
''} border template nonmain-editor rounded min-h-4 mx-0.5 overflow-clip {!editor
|
||||
? 'hidden'
|
||||
: ''}"
|
||||
style="height: 18px; padding-left: 8px;"
|
||||
class="{inputBorderClass({ forceFocus: isFocus })} {$$props.class ??
|
||||
''} template nonmain-editor rounded-md min-h-4 overflow-clip {!editor ? 'hidden' : ''}"
|
||||
bind:clientWidth={width}
|
||||
></div>
|
||||
|
||||
|
||||
@@ -43,8 +43,12 @@
|
||||
{#if Boolean(options?.left)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'mr-2 font-medium duration-50 select-none',
|
||||
bothOptions || textDisabled ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
|
||||
'mr-2 font-normal duration-50 select-none',
|
||||
bothOptions || textDisabled
|
||||
? checked
|
||||
? 'text-disabled'
|
||||
: 'text-tertiary'
|
||||
: 'text-tertiary',
|
||||
size === 'xs' || size === '2sm' ? 'text-xs' : size === '2xs' ? 'text-[0.5rem]' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
@@ -103,8 +107,12 @@
|
||||
{#if Boolean(options?.right)}
|
||||
<span
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-50 select-none',
|
||||
bothOptions || textDisabled ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
|
||||
'ml-2 font-normal duration-50 select-none',
|
||||
bothOptions || textDisabled
|
||||
? checked
|
||||
? 'text-tertiary'
|
||||
: 'text-disabled'
|
||||
: 'text-tertiary',
|
||||
size === 'xs' || size === '2sm' ? 'text-xs' : size === '2xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
|
||||
import { inputBaseClass, inputBorderClass } from '$lib/components/text_input/TextInput.svelte'
|
||||
import { onMount, untrack } from 'svelte'
|
||||
|
||||
/* Forked from MIT LICENSE
|
||||
@@ -208,7 +209,7 @@
|
||||
bind:value
|
||||
/>
|
||||
<input
|
||||
class="
|
||||
class="{inputBaseClass} {inputBorderClass()}
|
||||
{inputClasses?.formatted ?? DEFAULT_CLASS_FORMATTED}
|
||||
{isNegativeAllowed && !isZero && !isNegative
|
||||
? (inputClasses?.formattedPositive ?? DEFAULT_CLASS_FORMATTED_POSITIVE)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
type Props = {
|
||||
children: import('svelte').Snippet
|
||||
innerClass?: string
|
||||
class?: string
|
||||
horizontal?: boolean
|
||||
vertical?: boolean
|
||||
}
|
||||
let { children, innerClass, class: className = '', horizontal, vertical }: Props = $props()
|
||||
|
||||
let innerContainer: HTMLElement | null = $state(null)
|
||||
let outerContainer: HTMLElement | null = $state(null)
|
||||
|
||||
let width: number | null = $state(null)
|
||||
let height: number | null = $state(null)
|
||||
|
||||
let style = $derived.by(() => {
|
||||
let s = 'transition: width 0.2s, height 0.2s; overflow: clip;'
|
||||
if (width !== null) s += `width: ${width}px;`
|
||||
if (height !== null) s += `height: ${height}px;`
|
||||
return s
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (!innerContainer || !outerContainer) return
|
||||
let observer = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (horizontal) width = entry.contentRect.width
|
||||
if (vertical) height = entry.contentRect.height
|
||||
}
|
||||
})
|
||||
observer.observe(innerContainer)
|
||||
return () => {
|
||||
if (innerContainer) observer.unobserve(innerContainer)
|
||||
observer.disconnect()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={outerContainer} class={className} {style}>
|
||||
<div class={twMerge('absolute', innerClass)} bind:this={innerContainer}>
|
||||
{@render children()}
|
||||
</div>
|
||||
</div>
|
||||
@@ -154,7 +154,7 @@
|
||||
variant === 'border' ? 'border' : '',
|
||||
ButtonType.FontSizeClasses[size],
|
||||
ButtonType.SpacingClasses[spacingSize][variant],
|
||||
'focus-visible:ring-2 font-semibold',
|
||||
'focus-visible:ring-2 font-normal',
|
||||
dropdownItems && dropdownItems.length > 0 ? 'rounded-l-md h-full' : 'rounded-md',
|
||||
'justify-center items-center text-center whitespace-nowrap inline-flex gap-2',
|
||||
btnClasses,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
export let multiple = false
|
||||
export let convertTo: ReadFileAs | undefined = undefined
|
||||
export let hideIcon = false
|
||||
export let iconSize = 36
|
||||
export let iconSize = 24
|
||||
export let returnFileNames = false
|
||||
export let submittedText: string | undefined = undefined
|
||||
export let defaultFile: string | string[] | undefined = undefined
|
||||
@@ -187,10 +187,11 @@
|
||||
|
||||
<button
|
||||
class={twMerge(
|
||||
`relative center-center flex-col text-center font-medium text-tertiary
|
||||
border border-dashed border-gray-400 hover:border-blue-500
|
||||
`relative center-center flex flex-col gap-x-2 gap-y-1 justify-center items-center flex-wrap text-center font-normal text-hint text-xs rounded-md
|
||||
bg-surface-secondary
|
||||
border border-dashed border-transparent hover:border-blue-500
|
||||
focus-within:border-blue-300 hover:bg-blue-50 dark:hover:bg-frost-900
|
||||
duration-200 rounded-component p-1`,
|
||||
duration-200 px-1 py-8`,
|
||||
c
|
||||
)}
|
||||
on:dragover={handleDragOver}
|
||||
@@ -209,7 +210,7 @@
|
||||
{disabled}
|
||||
>
|
||||
{#if !hideIcon && !files}
|
||||
<FileUp size={iconSize} class="mb-2" />
|
||||
<FileUp size={iconSize} />
|
||||
{/if}
|
||||
{#if files}
|
||||
<div class="w-full max-h-full overflow-auto px-6">
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
computeS3ForceViewerPolicies,
|
||||
workspace,
|
||||
editor = $bindable(),
|
||||
appPath
|
||||
appPath,
|
||||
bottom
|
||||
}: {
|
||||
multiple: boolean
|
||||
value: any
|
||||
@@ -38,6 +39,7 @@
|
||||
workspace: string | undefined
|
||||
editor: SimpleEditor | undefined
|
||||
appPath: string | undefined
|
||||
bottom?: import('svelte').Snippet
|
||||
} = $props()
|
||||
|
||||
let s3FileUploadRawMode: boolean = $state(false)
|
||||
@@ -67,13 +69,7 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col w-full gap-1">
|
||||
<Toggle
|
||||
class="flex justify-end"
|
||||
bind:checked={s3FileUploadRawMode}
|
||||
size="xs"
|
||||
options={{ left: `Raw S3 object${multiple ? 's' : ''} input` }}
|
||||
/>
|
||||
<div class="flex flex-col w-full gap-1 relative">
|
||||
{#if s3FileUploadRawMode}
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
@@ -135,7 +131,7 @@
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="mt-1"
|
||||
btnClasses="mt-1 font-normal text-tertiary"
|
||||
on:click={() => {
|
||||
s3FilePicker?.open?.(value)
|
||||
}}
|
||||
@@ -144,4 +140,12 @@
|
||||
{multiple ? 'Add' : 'Choose'} an object from the catalog
|
||||
</Button>
|
||||
{/if}
|
||||
{@render bottom?.()}
|
||||
<Toggle
|
||||
class="mt-1"
|
||||
textClass="font-normal text-tertiary"
|
||||
bind:checked={s3FileUploadRawMode}
|
||||
size="xs"
|
||||
options={{ right: `Raw S3 object${multiple ? 's' : ''} input` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<div
|
||||
use:clickOutside={{
|
||||
eventToListenName: 'pointerdown',
|
||||
stopPropagation: true,
|
||||
onClickOutside: () => (open = false)
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -30,10 +30,11 @@
|
||||
{id}
|
||||
{disabled}
|
||||
class={twMerge(
|
||||
'group rounded-md transition-all text-xs flex gap-1 flex-row items-center',
|
||||
'group rounded-md transition-all text-xs font-normal flex gap-1 flex-row items-center border',
|
||||
small ? 'px-1.5 py-0.5 text-2xs' : 'px-2 py-1',
|
||||
light ? 'font-medium' : '',
|
||||
'data-[state=on]:bg-surface data-[state=on]:shadow-md',
|
||||
'data-[state=on]:bg-surface data-[state=off]:border-transparent data-[state=on]:border-gray-300 dark:data-[state=on]:border-gray-500',
|
||||
'text-hint data-[state=on]:text-tertiary',
|
||||
'bg-surface-secondary hover:bg-surface-hover',
|
||||
disabled ? '!shadow-none' : '',
|
||||
$$props.class
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex bg-surface-secondary rounded-md p-0.5 gap-1 h-full',
|
||||
'flex bg-surface-secondary rounded-md h-full',
|
||||
wrap ? 'flex-wrap' : '',
|
||||
tabListClass
|
||||
)}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
item?: any | undefined
|
||||
selected?: string | undefined
|
||||
togglableItems: TogglableItem[]
|
||||
class?: string
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -27,7 +28,8 @@
|
||||
id = undefined,
|
||||
item = undefined,
|
||||
selected = $bindable(undefined),
|
||||
togglableItems
|
||||
togglableItems,
|
||||
class: className = ''
|
||||
}: Props = $props()
|
||||
|
||||
let items = togglableItems.map((i) => ({
|
||||
@@ -44,7 +46,7 @@
|
||||
<Popover
|
||||
disablePopup={true}
|
||||
notClickable
|
||||
class={twMerge('flex', disabled ? 'cursor-not-allowed' : 'cursor-pointer')}
|
||||
class={twMerge('flex', disabled ? 'cursor-not-allowed' : 'cursor-pointer', className)}
|
||||
disappearTimeout={0}
|
||||
>
|
||||
<div {id} class="flex">
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
export let pickableProperties: PickableProperties | undefined = undefined
|
||||
export let argName: string
|
||||
export let showPopup: boolean
|
||||
export let btnClass = ''
|
||||
|
||||
let empty = false
|
||||
$: empty =
|
||||
@@ -211,10 +212,11 @@ Only return the expression without any wrapper.`
|
||||
size="xs"
|
||||
color="light"
|
||||
btnClasses={twMerge(
|
||||
'text-violet-800 dark:text-violet-400 bg-violet-100 dark:bg-gray-700 dark:hover:bg-surface-hover',
|
||||
'text-violet-500 dark:text-violet-400 border border-violet-100 dark:border-gray-700 dark:hover:bg-surface-hover',
|
||||
!loading && generatedContent.length > 0
|
||||
? 'bg-green-100 text-green-800 hover:bg-green-100 dark:text-green-400 dark:bg-green-700 dark:hover:bg-green-700'
|
||||
: ''
|
||||
: '',
|
||||
btnClass
|
||||
)}
|
||||
on:click={() => {
|
||||
if (!loading && generatedContent.length > 0) {
|
||||
|
||||
@@ -178,6 +178,7 @@ input_name2: expression2
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
wrapperClasses="flex-1 border rounded-md"
|
||||
btnClasses={twMerge(
|
||||
'text-violet-800 dark:text-violet-400',
|
||||
!loading && Object.keys($generatedExprs || {}).length > 0
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { ChevronDown } from 'lucide-svelte'
|
||||
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
$: opened = false
|
||||
</script>
|
||||
|
||||
<div class="text-xs flex flex-row-reverse mt-1">
|
||||
<div class="text-xs flex flex-row-reverse">
|
||||
<Button
|
||||
on:click={() => {
|
||||
opened = !opened
|
||||
}}
|
||||
color="light"
|
||||
variant="divider"
|
||||
size="xs2"
|
||||
endIcon={{ icon: ChevronDown, classes: `rotate-0 duration-300 ${opened ? '!rotate-180' : ''}` }}
|
||||
btnClasses="text-hint font-normal pt-1"
|
||||
>
|
||||
Help
|
||||
</Button>
|
||||
@@ -22,8 +22,7 @@
|
||||
|
||||
{#if opened}
|
||||
<div
|
||||
transition:slide|local
|
||||
class="bg-surface-secondary border-l-4 text-secondary p-4 m-4"
|
||||
class="bg-surface-secondary border-l-4 text-sm text-secondary p-4 mt-2"
|
||||
role="alert"
|
||||
id="dynamic-input-help-box"
|
||||
>
|
||||
|
||||
@@ -548,7 +548,7 @@
|
||||
></div>
|
||||
{/if}
|
||||
<InputTransformSchemaForm
|
||||
class="px-1 xl:px-2"
|
||||
class="px-2 xl:px-4"
|
||||
bind:this={inputTransformSchemaForm}
|
||||
pickableProperties={stepPropPicker.pickableProperties}
|
||||
schema={flowStateStore.val[$selectedId]?.schema ?? {}}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import ToggleButton from '../common/toggleButton-v2/ToggleButton.svelte'
|
||||
import { createEventDispatcher, onMount, untrack } from 'svelte'
|
||||
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
|
||||
import TextInput, { inputBaseClass, inputBorderClass } from '../text_input/TextInput.svelte'
|
||||
|
||||
interface Props {
|
||||
description?: string
|
||||
@@ -179,6 +180,7 @@
|
||||
onkeydown={onKeyDown}
|
||||
onchange={() => dispatch('change')}
|
||||
placeholder="Field description"
|
||||
class={twMerge(inputBorderClass(), inputBaseClass, 'w-full')}
|
||||
></textarea>
|
||||
</Label>
|
||||
|
||||
@@ -186,30 +188,31 @@
|
||||
{#snippet header()}
|
||||
<Tooltip light>Will be displayed in the UI instead of the field name.</Tooltip>
|
||||
{/snippet}
|
||||
<input
|
||||
<TextInput
|
||||
bind:value={title}
|
||||
onchange={() => dispatch('change')}
|
||||
onkeydown={onKeyDown}
|
||||
placeholder="Field title"
|
||||
inputProps={{ placeholder: 'Field title', onkeydown, onchange: () => dispatch('change') }}
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Label label="Placeholder">
|
||||
{#snippet header()}
|
||||
<Tooltip light>
|
||||
Will be displayed in the input field when the field is empty. If not set, the default
|
||||
value will be used. The placeholder is disabled depending on the field type, format, etc.
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
{#if shouldDisplayPlaceholder(type, format, enum_, contentEncoding, pattern, extra)}
|
||||
<Label label="Placeholder">
|
||||
{#snippet header()}
|
||||
<Tooltip light>
|
||||
Will be displayed in the input field when the field is empty. If not set, the default
|
||||
value will be used. The placeholder is disabled depending on the field type, format,
|
||||
etc.
|
||||
</Tooltip>
|
||||
{/snippet}
|
||||
|
||||
<textarea
|
||||
placeholder="Enter a placeholder"
|
||||
rows="1"
|
||||
bind:value={placeholder}
|
||||
onchange={() => dispatch('change')}
|
||||
disabled={!shouldDisplayPlaceholder(type, format, enum_, contentEncoding, pattern, extra)}
|
||||
></textarea>
|
||||
</Label>
|
||||
<textarea
|
||||
placeholder="Enter a placeholder"
|
||||
rows="1"
|
||||
bind:value={placeholder}
|
||||
onchange={() => dispatch('change')}
|
||||
class={twMerge(inputBorderClass(), inputBaseClass, 'w-full')}
|
||||
></textarea>
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
{#if type == 'array'}
|
||||
<ArrayTypeNarrowing
|
||||
@@ -285,11 +288,3 @@
|
||||
{@render children?.()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
input::-webkit-outer-spin-button,
|
||||
input::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none !important;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
helperScript?: DynamicInput.HelperScript
|
||||
className?: string
|
||||
dndType?: string
|
||||
lightHeaderFont?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -44,7 +45,8 @@
|
||||
isValid = $bindable(true),
|
||||
noVariablePicker = false,
|
||||
className = '',
|
||||
dndType = generateRandomString()
|
||||
dndType = generateRandomString(),
|
||||
lightHeaderFont
|
||||
}: Props = $props()
|
||||
|
||||
$effect.pre(() => {
|
||||
@@ -135,10 +137,11 @@
|
||||
{shouldDispatchChanges}
|
||||
bind:isValid
|
||||
{noVariablePicker}
|
||||
{lightHeaderFont}
|
||||
>
|
||||
{#snippet actions()}
|
||||
{#if !disableDnd}
|
||||
<div class="w-4 h-8 cursor-move ml-2 handle" aria-label="drag-handle" use:dragHandle>
|
||||
<div class="w-4 h-8 cursor-move ml-2 handle mt-[9px]" aria-label="drag-handle" use:dragHandle>
|
||||
<GripVertical size={16} />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
role="listitem"
|
||||
class={twMerge(
|
||||
allowClear ? 'pr-1' : 'pr-3',
|
||||
'pl-3 min-h-6 bg-surface-secondary rounded-full flex items-center gap-0.5',
|
||||
'pl-3 min-h-6 border bg-surface rounded-full flex items-center gap-0.5',
|
||||
currentlyDraggingIndex !== undefined ? 'hover:opacity-20' : ''
|
||||
)}
|
||||
style={currentlyDraggingIndex === index
|
||||
@@ -54,7 +54,7 @@
|
||||
<span class="text-sm select-none">{item.label || item.value}</span>
|
||||
{#if allowClear}
|
||||
<CloseButton
|
||||
class="text-tertiary"
|
||||
class="text-hint bg-transparent"
|
||||
small
|
||||
on:close={(e) => (onRemove(item), e.stopPropagation())}
|
||||
/>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import DraggableTags from './DraggableTags.svelte'
|
||||
import { Search } from 'lucide-svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import TextInput, { inputBorderClass } from '../text_input/TextInput.svelte'
|
||||
|
||||
type Value = Item['value']
|
||||
|
||||
@@ -59,7 +60,7 @@
|
||||
let filterText = $state<string>('')
|
||||
let open = $state<boolean>(false)
|
||||
let wrapperEl: HTMLDivElement | undefined = $state()
|
||||
let searchInputEl: HTMLInputElement | undefined = $state()
|
||||
let searchInputEl: TextInput | undefined = $state()
|
||||
|
||||
$effect(() => searchInputEl?.focus())
|
||||
|
||||
@@ -99,7 +100,8 @@
|
||||
<div
|
||||
bind:this={wrapperEl}
|
||||
class={twMerge(
|
||||
'relative min-h-8 flex items-center w-full bg-surface border border-gray-300 rounded-md text-tertiary',
|
||||
'relative min-h-10 flex items-center w-full bg-surface-secondary text-tertiary rounded-md',
|
||||
inputBorderClass({ forceFocus: open && !disabled }),
|
||||
disabled ? 'pointer-events-none' : '',
|
||||
open && !disabled ? 'open' : '',
|
||||
disabled ? 'disabled' : '',
|
||||
@@ -113,7 +115,9 @@
|
||||
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
||||
|
||||
{#if value.length === 0}
|
||||
<span class={twMerge('text-sm ml-2 h-full flex items-center flex-1', placeholderClass)}>
|
||||
<span
|
||||
class={twMerge('text-sm ml-4 h-full flex items-center flex-1 text-hint', placeholderClass)}
|
||||
>
|
||||
{placeholder}
|
||||
</span>
|
||||
{:else}
|
||||
@@ -134,10 +138,10 @@
|
||||
/>
|
||||
</ul>
|
||||
{/if}
|
||||
{#if allowClear && !hideMainClearBtn}
|
||||
{#if allowClear && !hideMainClearBtn && !!value?.length}
|
||||
<CloseButton
|
||||
noBg
|
||||
class="mr-1 remove-all"
|
||||
class="mr-1 remove-all bg-transparent text-hint"
|
||||
small
|
||||
on:close={(e) => (clearValue(), e.stopPropagation())}
|
||||
/>
|
||||
@@ -163,12 +167,14 @@
|
||||
{#snippet header()}
|
||||
{#if processedItems.length - value.length > 0 || onCreateItem}
|
||||
<div class="mx-2 mb-1 mt-2 flex items-center relative">
|
||||
<input
|
||||
<TextInput
|
||||
bind:this={searchInputEl}
|
||||
bind:value={filterText}
|
||||
onblur={(e) => (e.preventDefault(), searchInputEl?.focus())}
|
||||
placeholder="Search"
|
||||
class="!pr-7"
|
||||
inputProps={{
|
||||
onblur: (e) => (e.preventDefault(), searchInputEl?.focus()),
|
||||
placeholder: 'Search...'
|
||||
}}
|
||||
class="!pr-7 !bg-surface"
|
||||
/>
|
||||
<Search size={16} class="absolute right-2 text-tertiary" />
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { getLabel, processItems, type ProcessedItem } from './utils.svelte'
|
||||
import SelectDropdown from './SelectDropdown.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { inputBaseClass, inputBorderClass } from '../text_input/TextInput.svelte'
|
||||
|
||||
type Value = Item['value']
|
||||
|
||||
@@ -23,19 +24,24 @@
|
||||
inputClass = '',
|
||||
disablePortal = false,
|
||||
loading = false,
|
||||
error = false,
|
||||
autofocus,
|
||||
RightIcon,
|
||||
createText,
|
||||
noItemsMsg,
|
||||
open = $bindable(false),
|
||||
id,
|
||||
itemLabelWrapperClasses,
|
||||
itemButtonWrapperClasses,
|
||||
groupBy,
|
||||
sortBy,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onClear,
|
||||
onCreateItem,
|
||||
startSnippet
|
||||
startSnippet,
|
||||
endSnippet,
|
||||
bottomSnippet
|
||||
}: {
|
||||
items?: Item[]
|
||||
value: Value | undefined
|
||||
@@ -49,19 +55,24 @@
|
||||
inputClass?: string
|
||||
disablePortal?: boolean
|
||||
loading?: boolean
|
||||
error?: boolean
|
||||
autofocus?: boolean
|
||||
RightIcon?: any
|
||||
createText?: string
|
||||
noItemsMsg?: string
|
||||
open?: boolean
|
||||
id?: string
|
||||
itemLabelWrapperClasses?: string
|
||||
itemButtonWrapperClasses?: string
|
||||
groupBy?: (item: Item) => string
|
||||
sortBy?: (a: Item, b: Item) => number
|
||||
onFocus?: () => void
|
||||
onBlur?: () => void
|
||||
onClear?: () => void
|
||||
onCreateItem?: (value: string) => void
|
||||
startSnippet?: Snippet<[{ item: ProcessedItem<Value> }]>
|
||||
startSnippet?: Snippet<[{ item: ProcessedItem<Value>; close: () => void }]>
|
||||
endSnippet?: Snippet<[{ item: ProcessedItem<Value>; close: () => void }]>
|
||||
bottomSnippet?: Snippet<[{ close: () => void }]>
|
||||
} = $props()
|
||||
|
||||
let disabled = $derived(_disabled || (loading && !value))
|
||||
@@ -112,7 +123,7 @@
|
||||
</div>
|
||||
{:else if clearable && !disabled && value}
|
||||
<div class="absolute z-10 right-2 h-full flex items-center">
|
||||
<CloseButton class="text-secondary" noBg small on:close={clearValue} />
|
||||
<CloseButton class="bg-transparent text-hint" noBg small on:close={clearValue} />
|
||||
</div>
|
||||
{:else if RightIcon}
|
||||
<div class="absolute z-10 right-2 h-full flex items-center">
|
||||
@@ -130,10 +141,12 @@
|
||||
: (valueEntry?.label ?? getLabel({ value }) ?? placeholder)}
|
||||
style={containerStyle}
|
||||
class={twMerge(
|
||||
'!bg-surface text-ellipsis',
|
||||
inputBaseClass,
|
||||
inputBorderClass({ error, forceFocus: open }),
|
||||
'w-full',
|
||||
open ? '' : 'cursor-pointer',
|
||||
!loading && value ? '!placeholder-primary' : '',
|
||||
(clearable || RightIcon) && !disabled && value ? '!pr-8' : '',
|
||||
!loading && value ? 'placeholder-primary' : 'placeholder-hint',
|
||||
(clearable || RightIcon) && !disabled && value ? 'pr-8' : '',
|
||||
inputClass ?? ''
|
||||
)}
|
||||
autocomplete="off"
|
||||
@@ -152,6 +165,10 @@
|
||||
getInputRect={inputEl && (() => inputEl!.getBoundingClientRect())}
|
||||
{listAutoWidth}
|
||||
{noItemsMsg}
|
||||
{itemLabelWrapperClasses}
|
||||
{itemButtonWrapperClasses}
|
||||
{startSnippet}
|
||||
{endSnippet}
|
||||
{bottomSnippet}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { untrack, type Snippet } from 'svelte'
|
||||
import type { ProcessedItem } from './utils.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
let {
|
||||
processedItems: _processedItems,
|
||||
@@ -16,10 +17,14 @@
|
||||
noItemsMsg = 'No items found',
|
||||
class: className = '',
|
||||
ulClass = '',
|
||||
itemLabelWrapperClasses = '',
|
||||
itemButtonWrapperClasses = '',
|
||||
header,
|
||||
getInputRect,
|
||||
onSelectValue,
|
||||
startSnippet
|
||||
startSnippet,
|
||||
endSnippet,
|
||||
bottomSnippet
|
||||
}: {
|
||||
processedItems?: ProcessedItem<T>[]
|
||||
value: T | undefined
|
||||
@@ -31,10 +36,14 @@
|
||||
noItemsMsg?: string
|
||||
class?: string
|
||||
ulClass?: string
|
||||
itemLabelWrapperClasses?: string
|
||||
itemButtonWrapperClasses?: string
|
||||
header?: Snippet
|
||||
getInputRect?: () => DOMRect
|
||||
onSelectValue: (item: ProcessedItem<T>) => void
|
||||
startSnippet?: Snippet<[{ item: ProcessedItem<T> }]>
|
||||
startSnippet?: Snippet<[{ item: ProcessedItem<T>; close: () => void }]>
|
||||
endSnippet?: Snippet<[{ item: ProcessedItem<T>; close: () => void }]>
|
||||
bottomSnippet?: Snippet<[{ close: () => void }]>
|
||||
} = $props()
|
||||
|
||||
let processedItems = $derived(
|
||||
@@ -101,53 +110,63 @@
|
||||
<ConditionalPortal condition={!disablePortal} name="select-dropdown-portal">
|
||||
{#if open && !disabled}
|
||||
<div
|
||||
transition:slide={{ duration: 150 }}
|
||||
class={twMerge(
|
||||
disablePortal ? 'absolute' : 'fixed',
|
||||
'flex flex-col z-[5001] max-h-64 overflow-y-auto bg-surface-secondary text-tertiary text-sm select-none border rounded-lg shadow-lg',
|
||||
'z-[5001] rounded-md bg-surface-secondary text-tertiary text-sm select-none',
|
||||
className
|
||||
)}
|
||||
style="{`top: ${dropdownPos.y}px; left: ${dropdownPos.x}px;`} {listAutoWidth
|
||||
? `min-width: ${dropdownPos.width}px;`
|
||||
: ''}"
|
||||
bind:this={listEl}
|
||||
>
|
||||
{@render header?.()}
|
||||
{#if processedItems?.length === 0}
|
||||
<div class="py-8 px-4 text-center text-primary">{noItemsMsg}</div>
|
||||
{/if}
|
||||
<ul class={twMerge('flex-1 overflow-y-auto flex flex-col', ulClass)}>
|
||||
{#each processedItems ?? [] as item, itemIndex}
|
||||
{#if (item.__select_group && itemIndex === 0) || processedItems?.[itemIndex - 1]?.__select_group !== item.__select_group}
|
||||
<li
|
||||
class={twMerge(
|
||||
'mx-4 pb-1 mb-2 text-xs font-semibold text-primary border-b',
|
||||
itemIndex === 0 ? 'mt-3' : 'mt-6'
|
||||
)}
|
||||
>
|
||||
{item.__select_group}
|
||||
<div
|
||||
bind:this={listEl}
|
||||
class="flex flex-col max-h-64 border rounded-md shadow-lg overflow-clip"
|
||||
>
|
||||
{@render header?.()}
|
||||
{#if processedItems?.length === 0}
|
||||
<div class="py-8 px-4 text-center text-primary">{noItemsMsg}</div>
|
||||
{/if}
|
||||
<ul class={twMerge('flex-1 overflow-y-auto flex flex-col', ulClass)}>
|
||||
{#each processedItems ?? [] as item, itemIndex}
|
||||
{#if (item.__select_group && itemIndex === 0) || processedItems?.[itemIndex - 1]?.__select_group !== item.__select_group}
|
||||
<li
|
||||
class={twMerge(
|
||||
'mx-4 pb-1 mb-2 text-xs font-semibold text-primary border-b',
|
||||
itemIndex === 0 ? 'mt-3' : 'mt-6'
|
||||
)}
|
||||
>
|
||||
{item.__select_group}
|
||||
</li>
|
||||
{/if}
|
||||
<li>
|
||||
<button
|
||||
class={twMerge(
|
||||
'py-2 px-4 w-full font-normal text-left text-primary',
|
||||
itemIndex === keyArrowPos ? 'bg-surface-hover' : '',
|
||||
item.value === value ? 'bg-surface-selected' : 'hover:bg-surface-hover',
|
||||
itemButtonWrapperClasses
|
||||
)}
|
||||
onclick={(e) => {
|
||||
e.stopImmediatePropagation()
|
||||
onSelectValue(item)
|
||||
}}
|
||||
>
|
||||
{@render startSnippet?.({ item, close: () => (open = false) })}
|
||||
<span class={itemLabelWrapperClasses}>
|
||||
{item.label || '\xa0'}
|
||||
</span>
|
||||
{@render endSnippet?.({ item, close: () => (open = false) })}
|
||||
{#if item.subtitle}
|
||||
<div class="text-xs text-tertiary">{item.subtitle}</div>
|
||||
{/if}
|
||||
</button>
|
||||
</li>
|
||||
{/if}
|
||||
<li>
|
||||
<button
|
||||
class={twMerge(
|
||||
'py-2 px-4 w-full font-normal text-left text-primary',
|
||||
itemIndex === keyArrowPos ? 'bg-surface-hover' : '',
|
||||
item.value === value ? 'bg-surface-selected' : 'hover:bg-surface-hover'
|
||||
)}
|
||||
onclick={(e) => {
|
||||
e.stopImmediatePropagation()
|
||||
onSelectValue(item)
|
||||
}}
|
||||
>
|
||||
{@render startSnippet?.({ item })}
|
||||
{item.label || '\xa0'}
|
||||
{#if item.subtitle}
|
||||
<div class="text-xs text-tertiary">{item.subtitle}</div>
|
||||
{/if}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/each}
|
||||
</ul>
|
||||
{@render bottomSnippet?.({ close: () => (open = false) })}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</ConditionalPortal>
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
class={twMerge(
|
||||
'group flex items-center px-2 py-2 font-light rounded-md h-8 gap-3 w-full',
|
||||
lightMode
|
||||
? 'text-primary data-[highlighted]:bg-surface-hover hover:bg-surface-hover'
|
||||
: 'data-[highlighted]:bg-[#2A3648] hover:bg-[#2A3648] text-primary-inverse dark:text-primary',
|
||||
? 'text-tertiary data-[highlighted]:bg-surface-hover hover:bg-surface-hover'
|
||||
: 'data-[highlighted]:bg-[#2A3648] hover:bg-[#2A3648] text-tertiary dark:text-primary',
|
||||
color ? 'border-4' : '',
|
||||
'transition-all relative',
|
||||
classNames
|
||||
@@ -101,8 +101,8 @@
|
||||
class={twMerge(
|
||||
'flex-shrink-0',
|
||||
lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-primary-inverse group-hover:text-secondary-inverse dark:group-hover:text-secondary dark:text-primary',
|
||||
? 'text-tertiary group-hover:text-tertiary'
|
||||
: 'text-tertiary group-hover:text-tertiary dark:group-hover:text-secondary dark:text-primary',
|
||||
'transition-all',
|
||||
iconClasses
|
||||
)}
|
||||
@@ -114,13 +114,13 @@
|
||||
<span
|
||||
class={twMerge(
|
||||
'whitespace-pre truncate',
|
||||
lightMode ? 'text-primary' : 'text-primary-inverse dark:text-primary',
|
||||
lightMode ? 'text-primary' : 'text-tertiary dark:text-primary',
|
||||
'transition-all',
|
||||
classNames
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
<span class="pl-2 text-xs dark:text-secondary light:text-secondary-inverse font-semibold">
|
||||
<span class="pl-2 text-xs dark:text-secondary light:text-tertiary font-semibold">
|
||||
{shortcut}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -83,10 +83,10 @@
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: 'text-tertiary group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
: 'text-tertiary group-hover:text-white',
|
||||
'transition-all'
|
||||
)}
|
||||
/>
|
||||
@@ -99,10 +99,10 @@
|
||||
isSelected
|
||||
? lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-frost-200 group-hover:text-white'
|
||||
: 'text-tertiary group-hover:text-white'
|
||||
: lightMode
|
||||
? 'text-primary group-hover:text-secondary'
|
||||
: 'text-gray-100 group-hover:text-white',
|
||||
: 'text-tertiary group-hover:text-white',
|
||||
'transition-all duration-75'
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -359,7 +359,7 @@
|
||||
}
|
||||
]
|
||||
: []),
|
||||
...($workspaceStore?.startsWith("wm-fork")
|
||||
...($workspaceStore?.startsWith('wm-fork')
|
||||
? [
|
||||
{
|
||||
label: 'Delete Forked Workspace',
|
||||
@@ -370,7 +370,7 @@
|
||||
faIcon: undefined
|
||||
}
|
||||
]
|
||||
: []),
|
||||
: [])
|
||||
],
|
||||
disabled: $userStore?.operator
|
||||
},
|
||||
@@ -665,20 +665,20 @@
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
|
||||
{#if $workspaceStore?.startsWith("wm-fork-")}
|
||||
<ConfirmationModal
|
||||
open={deleteWorkspaceForkModal}
|
||||
title="Delete forked workspace"
|
||||
confirmationText="Remove"
|
||||
on:canceled={() => {
|
||||
deleteWorkspaceForkModal = false
|
||||
}}
|
||||
on:confirmed={() => {
|
||||
deleteFork()
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col w-full space-y-4">
|
||||
<span>Are you sure you want to delete this workspace fork? (deleting {$workspaceStore})</span>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
{#if $workspaceStore?.startsWith('wm-fork-')}
|
||||
<ConfirmationModal
|
||||
open={deleteWorkspaceForkModal}
|
||||
title="Delete forked workspace"
|
||||
confirmationText="Remove"
|
||||
on:canceled={() => {
|
||||
deleteWorkspaceForkModal = false
|
||||
}}
|
||||
on:confirmed={() => {
|
||||
deleteFork()
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col w-full space-y-4">
|
||||
<span>Are you sure you want to delete this workspace fork? (deleting {$workspaceStore})</span>
|
||||
</div>
|
||||
</ConfirmationModal>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<script module lang="ts">
|
||||
export function inputBorderClass({
|
||||
error,
|
||||
forceFocus
|
||||
}: {
|
||||
error?: boolean
|
||||
forceFocus?: boolean
|
||||
} = {}) {
|
||||
return twMerge(
|
||||
'transition-colors border',
|
||||
forceFocus
|
||||
? '!border-nord-900 dark:!border-nord-900'
|
||||
: '!border-transparent focus:!border-nord-900 dark:focus:!border-nord-900 hover:!border-nord-400 dark:hover:!border-nord-300',
|
||||
error
|
||||
? '!border-red-300 focus:!border-red-400 hover:!border-red-500 dark:!border-red-400/40 dark:hover:!border-red-600/40'
|
||||
: ''
|
||||
)
|
||||
}
|
||||
|
||||
export const inputBaseClass =
|
||||
'rounded-md focus:ring-0 no-default-style text-sm text-tertiary !bg-surface-secondary disabled:!bg-surface-disabled disabled:!text-hint disabled:cursor-not-allowed shadow-none py-2 px-4 placeholder-hint'
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { HTMLInputAttributes } from 'svelte/elements'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
type Props = {
|
||||
inputProps?: HTMLInputAttributes
|
||||
value?: string
|
||||
class?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export function focus() {
|
||||
inputEl?.focus()
|
||||
}
|
||||
|
||||
let inputEl: HTMLInputElement | undefined = $state()
|
||||
|
||||
let { inputProps, value = $bindable(), class: className = '', error }: Props = $props()
|
||||
</script>
|
||||
|
||||
<input
|
||||
{...inputProps}
|
||||
class={twMerge(
|
||||
inputBaseClass,
|
||||
'w-full',
|
||||
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
|
||||
inputBorderClass({ error: !!error }),
|
||||
className
|
||||
)}
|
||||
bind:this={inputEl}
|
||||
bind:value
|
||||
/>
|
||||
@@ -377,7 +377,7 @@
|
||||
{/key}
|
||||
{:else}
|
||||
<span class="text-sm text-tertiary text-center mx-auto mt-2"
|
||||
>{`Select a trigger from the ${useVerticalTriggerBar ? 'left toolbar' : 'table'} or a create a new one`}</span
|
||||
>{`Select a trigger from the ${useVerticalTriggerBar ? 'left toolbar' : 'table'} or create a new one`}</span
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -231,7 +231,7 @@ export async function initializeVscode(caller?: string, htmlContainer?: HTMLElem
|
||||
],
|
||||
colors: {
|
||||
'editor.foreground': '#D8DEE9',
|
||||
'editor.background': '#272D38',
|
||||
'editor.background': '#00000000',
|
||||
'editor.selectionBackground': '#434C5ECC',
|
||||
'editor.lineHighlightBackground': '#3B4252',
|
||||
'editorCursor.foreground': '#D8DEE9',
|
||||
@@ -244,8 +244,10 @@ export async function initializeVscode(caller?: string, htmlContainer?: HTMLElem
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editorLineNumber.foreground': '#999',
|
||||
'editorGutter.background': '#F9FAFB'
|
||||
'editor.background': '#FFFFFF00',
|
||||
'editorLineNumber.foreground': '#C2C9D1',
|
||||
'editorLineNumber.activeForeground': '#989DA5',
|
||||
'editorGutter.background': '#FFFFFF00'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
import { Menubar } from '$lib/components/meltComponents'
|
||||
import { aiChatManager } from '$lib/components/copilot/chat/AIChatManager.svelte'
|
||||
import AiChatLayout from '$lib/components/copilot/chat/AiChatLayout.svelte'
|
||||
import { useIsDarkMode } from '$lib/components/DarkModeObserver.svelte'
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet
|
||||
}
|
||||
@@ -387,6 +388,8 @@
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let isDarkMode = useIsDarkMode()
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth />
|
||||
@@ -423,7 +426,7 @@
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-[#202125] dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 z-40 !dark',
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-surface dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 z-40',
|
||||
|
||||
menuOpen ? 'opacity-100' : 'opacity-0'
|
||||
)}
|
||||
@@ -467,7 +470,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dark:bg-[#1e232e] bg-[#202125] h-full !dark flex flex-col">
|
||||
<div class="dark:bg-[#1e232e] bg-surface h-full flex flex-col">
|
||||
<div class="flex gap-x-2 flex-shrink-0 p-4 font-semibold text-gray-200 w-40">
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
{#if $whitelabelNameStore}
|
||||
@@ -521,13 +524,13 @@
|
||||
<div
|
||||
id="sidebar"
|
||||
class={classNames(
|
||||
'flex flex-col fixed inset-y-0 transition-all ease-in-out duration-200 shadow-md z-40 ',
|
||||
'flex flex-col fixed inset-y-0 transition-all ease-in-out duration-200 z-40 ',
|
||||
isCollapsed ? 'md:w-12' : 'md:w-40',
|
||||
devOnly ? '!hidden' : ''
|
||||
)}
|
||||
>
|
||||
<div
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-lg dark:bg-[#1e232e] bg-[#202125] !dark"
|
||||
class="flex-1 flex flex-col min-h-0 h-screen border-r dark:bg-[#1e232e] bg-surface"
|
||||
>
|
||||
<button
|
||||
onclick={() => {
|
||||
@@ -539,10 +542,10 @@
|
||||
class:w-40={!isCollapsed}
|
||||
>
|
||||
<div class:mr-1={!isCollapsed}>
|
||||
<WindmillIcon white={true} height="20px" width="20px" />
|
||||
<WindmillIcon white={!isDarkMode} height="20px" width="20px" />
|
||||
</div>
|
||||
{#if !isCollapsed}
|
||||
<div class="text-sm mt-0.5 text-white">
|
||||
<div class="text-sm mt-0.5 text-tertiary">
|
||||
{#if $whitelabelNameStore}{capitalize(
|
||||
$whitelabelNameStore
|
||||
)}{:else}Windmill{/if}
|
||||
@@ -550,7 +553,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
<div class="px-2 py-4 border-y border-gray-700 flex flex-col gap-1">
|
||||
<div class="px-2 py-4 border-y border-hint/40 flex flex-col gap-1">
|
||||
<Menubar class="flex flex-col gap-1">
|
||||
{#snippet children({ createMenu })}
|
||||
<WorkspaceMenu {createMenu} {isCollapsed} />
|
||||
@@ -597,7 +600,7 @@
|
||||
<ArrowLeft
|
||||
size={16}
|
||||
class={classNames(
|
||||
'flex-shrink-0 h-4 w-4 transition-all ease-in-out duration-200 text-white',
|
||||
'flex-shrink-0 h-4 w-4 transition-all ease-in-out duration-200 text-tertiary',
|
||||
isCollapsed ? 'rotate-180' : 'rotate-0'
|
||||
)}
|
||||
/>
|
||||
@@ -615,7 +618,7 @@
|
||||
<!-- Legacy menu -->
|
||||
<div
|
||||
class={classNames(
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-[#202125] dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 !dark',
|
||||
'fixed inset-0 dark:bg-[#1e232e] bg-surface dark:bg-opacity-75 bg-opacity-75 transition-opacity ease-linear duration-300 ',
|
||||
'opacity-0 pointer-events-none'
|
||||
)}
|
||||
>
|
||||
@@ -653,7 +656,7 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="dark:bg-[#1e232e] bg-[#202125] h-full !dark">
|
||||
<div class="dark:bg-[#1e232e] bg-surface h-full">
|
||||
<div
|
||||
class="flex gap-x-2 flex-shrink-0 p-4 font-semibold text-gray-200 w-10"
|
||||
class:w-40={!isCollapsed}
|
||||
|
||||
@@ -14,6 +14,7 @@ const lightTheme = {
|
||||
textPrimary: '#2d3748',
|
||||
textSecondary: '#4a5568',
|
||||
textTertiary: '#505c70',
|
||||
textHint: '#A0A6B2',
|
||||
textDisabled: '#a0aec0',
|
||||
|
||||
border: '#dddddd',
|
||||
@@ -32,6 +33,7 @@ const darkTheme = {
|
||||
textPrimary: '#EEEEEE',
|
||||
textSecondary: '#C2C9D1',
|
||||
textTertiary: '#A8AEB7',
|
||||
textHint: '#989DA5',
|
||||
textDisabled: '#989DA5',
|
||||
|
||||
border: '#3e4c60',
|
||||
@@ -413,6 +415,7 @@ const config = {
|
||||
primary: 'rgb(var(--color-text-primary) / <alpha-value>)',
|
||||
secondary: 'rgb(var(--color-text-secondary) / <alpha-value>)',
|
||||
tertiary: 'rgb(var(--color-text-tertiary) / <alpha-value>)',
|
||||
hint: 'rgb(var(--color-text-hint) / <alpha-value>)',
|
||||
disabled: 'rgb(var(--color-text-disabled) / <alpha-value>)',
|
||||
|
||||
'surface-inverse': 'rgb(var(--color-surface-inverse) / <alpha-value>)',
|
||||
@@ -424,6 +427,7 @@ const config = {
|
||||
'primary-inverse': 'rgb(var(--color-text-primary-inverse) / <alpha-value>)',
|
||||
'secondary-inverse': 'rgb(var(--color-text-secondary-inverse) / <alpha-value>)',
|
||||
'tertiary-inverse': 'rgb(var(--color-text-tertiary-inverse) / <alpha-value>)',
|
||||
'hint-inverse': 'rgb(var(--color-text-hint-inverse) / <alpha-value>)',
|
||||
'disabled-inverse': 'rgb(var(--color-text-disabled-inverse) / <alpha-value>)'
|
||||
},
|
||||
fontFamily: {
|
||||
@@ -519,6 +523,7 @@ const config = {
|
||||
'--color-text-primary': lightThemeRgb.textPrimary,
|
||||
'--color-text-secondary': lightThemeRgb.textSecondary,
|
||||
'--color-text-tertiary': lightThemeRgb.textTertiary,
|
||||
'--color-text-hint': lightThemeRgb.textHint,
|
||||
'--color-text-disabled': lightThemeRgb.textDisabled,
|
||||
|
||||
'--color-surface-inverse': darkThemeRgb.surface,
|
||||
@@ -530,6 +535,7 @@ const config = {
|
||||
'--color-text-primary-inverse': darkThemeRgb.textPrimary,
|
||||
'--color-text-secondary-inverse': darkThemeRgb.textSecondary,
|
||||
'--color-text-tertiary-inverse': darkThemeRgb.textTertiary,
|
||||
'--color-text-hint-inverse': darkThemeRgb.texthint,
|
||||
'--color-text-disabled-inverse': darkThemeRgb.textDisabled,
|
||||
|
||||
'--color-border': lightThemeRgb.border,
|
||||
@@ -560,6 +566,7 @@ const config = {
|
||||
'--color-text-primary': darkThemeRgb.textPrimary,
|
||||
'--color-text-secondary': darkThemeRgb.textSecondary,
|
||||
'--color-text-tertiary': darkThemeRgb.textTertiary,
|
||||
'--color-text-hint': darkThemeRgb.textHint,
|
||||
'--color-text-disabled': darkThemeRgb.textDisabled,
|
||||
|
||||
'--color-surface-inverse': lightThemeRgb.surface,
|
||||
@@ -571,6 +578,7 @@ const config = {
|
||||
'--color-text-primary-inverse': lightThemeRgb.textPrimary,
|
||||
'--color-text-secondary-inverse': lightThemeRgb.textSecondary,
|
||||
'--color-text-tertiary-inverse': lightThemeRgb.textTertiary,
|
||||
'--color-text-hint-inverse': lightThemeRgb.textHint,
|
||||
'--color-text-disabled-inverse': lightThemeRgb.textDisabled,
|
||||
|
||||
'--color-border': darkThemeRgb.border,
|
||||
@@ -661,7 +669,7 @@ const config = {
|
||||
".dark [type='checkbox']:checked": {
|
||||
backgroundImage: `url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e")`
|
||||
},
|
||||
'input:not(.windmillapp),input[type="text"]:not(.windmillapp),input[type="email"]:not(.windmillapp),input[type="url"]:not(.windmillapp),input[type="password"]:not(.windmillapp),input[type="number"]:not(.windmillapp),input[type="date"]:not(.windmillapp),input[type="datetime-local"]:not(.windmillapp),input[type="month"]:not(.windmillapp),input[type="search"]:not(.windmillapp),input[type="tel"]:not(.windmillapp),input[type="time"]:not(.windmillapp),input[type="week"]:not(.windmillapp),textarea:not(.windmillapp):not(.monaco-mouse-cursor-text),select:not(.windmillapp)':
|
||||
'input:not(.windmillapp):not(.no-default-style),input[type="text"]:not(.windmillapp):not(.no-default-style),input[type="email"]:not(.windmillapp):not(.no-default-style),input[type="url"]:not(.windmillapp):not(.no-default-style),input[type="password"]:not(.windmillapp):not(.no-default-style),input[type="number"]:not(.windmillapp):not(.no-default-style),input[type="date"]:not(.windmillapp):not(.no-default-style),input[type="datetime-local"]:not(.windmillapp):not(.no-default-style),input[type="month"]:not(.windmillapp):not(.no-default-style),input[type="search"]:not(.windmillapp):not(.no-default-style),input[type="tel"]:not(.windmillapp):not(.no-default-style),input[type="time"]:not(.windmillapp):not(.no-default-style),input[type="week"]:not(.windmillapp):not(.no-default-style),textarea:not(.windmillapp):not(.no-default-style):not(.monaco-mouse-cursor-text),select:not(.windmillapp):not(.no-default-style)':
|
||||
{
|
||||
display: 'block',
|
||||
fontSize: theme('fontSize.sm'),
|
||||
@@ -686,7 +694,7 @@ const config = {
|
||||
}
|
||||
}
|
||||
},
|
||||
'.dark input:not(.windmillapp),.dark input[type="text"]:not(.windmillapp),.dark input[type="email"]:not(.windmillapp),.dark input[type="url"]:not(.windmillapp),.dark input[type="password"]:not(.windmillapp),.dark input[type="number"]:not(.windmillapp),.dark input[type="date"]:not(.windmillapp),.dark input[type="datetime-local"]:not(.windmillapp),.dark input[type="month"]:not(.windmillapp),.dark input[type="search"]:not(.windmillapp),.dark input[type="tel"]:not(.windmillapp),.dark input[type="time"]:not(.windmillapp),.dark input[type="week"]:not(.windmillapp),.dark textarea:not(.windmillapp):not(.monaco-mouse-cursor-text),.dark select:not(.windmillapp)':
|
||||
'.dark input:not(.windmillapp):not(.no-default-style),.dark input[type="text"]:not(.windmillapp):not(.no-default-style),.dark input[type="email"]:not(.windmillapp):not(.no-default-style),.dark input[type="url"]:not(.windmillapp):not(.no-default-style),.dark input[type="password"]:not(.windmillapp):not(.no-default-style),.dark input[type="number"]:not(.windmillapp):not(.no-default-style),.dark input[type="date"]:not(.windmillapp):not(.no-default-style),.dark input[type="datetime-local"]:not(.windmillapp):not(.no-default-style),.dark input[type="month"]:not(.windmillapp):not(.no-default-style),.dark input[type="search"]:not(.windmillapp):not(.no-default-style),.dark input[type="tel"]:not(.windmillapp):not(.no-default-style),.dark input[type="time"]:not(.windmillapp):not(.no-default-style),.dark input[type="week"]:not(.windmillapp):not(.no-default-style),.dark textarea:not(.windmillapp):not(.no-default-style):not(.monaco-mouse-cursor-text),.dark select:not(.windmillapp):not(.no-default-style)':
|
||||
{
|
||||
backgroundColor: theme('colors.gray.700'),
|
||||
color: theme('colors.gray.200'),
|
||||
|
||||
Reference in New Issue
Block a user