mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-10 16:05:58 +00:00
feat(frontend): detect expr in flow input transform + filter right panel based on expr (#4651)
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
<script context="module">
|
||||
const dynamicTemplateRegexPairs = buildPrefixRegex([
|
||||
'flow_input',
|
||||
'results',
|
||||
'resource',
|
||||
'variable'
|
||||
])
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Schema } from '$lib/common'
|
||||
import type { InputCat } from '$lib/utils'
|
||||
@@ -13,8 +22,9 @@
|
||||
import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte'
|
||||
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'
|
||||
import type { InputTransform } from '$lib/gen'
|
||||
@@ -43,6 +53,7 @@
|
||||
let monaco: SimpleEditor | undefined = undefined
|
||||
let monacoTemplate: TemplateEditor | undefined = undefined
|
||||
let argInput: ArgInput | undefined = undefined
|
||||
let focusedPrev = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -59,6 +70,9 @@
|
||||
const { shouldUpdatePropertyType, exprsToSet } =
|
||||
getContext<FlowCopilotContext | undefined>('FlowCopilotContext') || {}
|
||||
|
||||
const { inputMatches, focusProp, propPickerConfig } =
|
||||
getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
|
||||
function setExpr() {
|
||||
const newArg = $exprsToSet?.[argName]
|
||||
if (newArg) {
|
||||
@@ -133,6 +147,50 @@
|
||||
}
|
||||
}
|
||||
|
||||
let codeInjectionDetected = false
|
||||
|
||||
function checkCodeInjection(rawValue: string) {
|
||||
if (!arg || !rawValue || rawValue.length < 3 || !dynamicTemplateRegexPairs) {
|
||||
return undefined
|
||||
}
|
||||
if (rawValue.trim() !== rawValue) {
|
||||
return undefined
|
||||
}
|
||||
const matches = dynamicTemplateRegexPairs.filter(({ regex }) => regex.test(rawValue))
|
||||
if (matches.length > 0) {
|
||||
return matches.map((m) => ({ word: m.word, value: rawValue }))
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
async function setJavaScriptExpr(rawValue: string) {
|
||||
arg = {
|
||||
type: 'javascript',
|
||||
expr: rawValue
|
||||
}
|
||||
propertyType = 'javascript'
|
||||
monaco?.setCode('')
|
||||
monaco?.insertAtCursor(rawValue)
|
||||
await tick()
|
||||
monaco?.focus()
|
||||
await tick()
|
||||
monaco?.setCursorToEnd()
|
||||
}
|
||||
|
||||
function handleKeyUp(e: KeyboardEvent) {
|
||||
if (
|
||||
e.key === 'Tab' &&
|
||||
isStaticTemplate(inputCat) &&
|
||||
propertyType == 'static' &&
|
||||
!noDynamicToggle &&
|
||||
codeInjectionDetected
|
||||
) {
|
||||
setJavaScriptExpr(arg.value)
|
||||
} else {
|
||||
stepInputGen?.onKeyUp?.(e)
|
||||
}
|
||||
}
|
||||
|
||||
function isStaticTemplate(inputCat: InputCat) {
|
||||
return inputCat === 'string' || inputCat === 'sql' || inputCat == 'yaml'
|
||||
}
|
||||
@@ -166,9 +224,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
const { focusProp, propPickerConfig } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
$: updateStaticInput(inputCat, propertyType, arg)
|
||||
|
||||
$: isStaticTemplate(inputCat) && propertyType == 'static' && setPropertyType(arg?.value)
|
||||
function updateStaticInput(
|
||||
inputCat: InputCat,
|
||||
propertyType: 'static' | 'javascript',
|
||||
arg: InputTransform | any
|
||||
) {
|
||||
if (!isStaticTemplate(inputCat)) {
|
||||
return
|
||||
}
|
||||
if (propertyType == 'static') {
|
||||
setPropertyType(arg?.value)
|
||||
codeInjectionDetected = checkCodeInjection(arg?.value) != undefined
|
||||
} else if (propertyType == 'javascript' && focused) {
|
||||
setPropertyType(arg?.expr)
|
||||
$inputMatches = checkCodeInjection(arg?.expr)
|
||||
}
|
||||
}
|
||||
|
||||
function setDefaultCode() {
|
||||
if (!arg?.value) {
|
||||
@@ -176,6 +249,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateFocused(newFocused: boolean) {
|
||||
if (focusedPrev && !newFocused) {
|
||||
$inputMatches = undefined
|
||||
}
|
||||
focusedPrev = focused
|
||||
}
|
||||
$: updateFocused(focused)
|
||||
|
||||
$: schema?.properties?.[argName].default && setDefaultCode()
|
||||
|
||||
let resourceTypes: string[] | undefined = undefined
|
||||
@@ -325,13 +406,27 @@
|
||||
<ToggleButton small label="Static" value="static" />
|
||||
{/if}
|
||||
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
tooltip="JavaScript expression ('flow_input' or 'results')."
|
||||
value="javascript"
|
||||
icon={FunctionSquare}
|
||||
/>
|
||||
{#if codeInjectionDetected && propertyType == 'static'}
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
btnClasses="font-normal text-xs w-fit bg-green-100 text-green-800 hover:bg-green-100 dark:text-green-300 dark:bg-green-700 dark:hover:bg-green-600"
|
||||
on:click={() => setJavaScriptExpr(arg.value)}
|
||||
>
|
||||
<span class="font-normal whitespace-nowrap flex gap-2 items-center"
|
||||
><FunctionSquare size={14} /> detected -
|
||||
<span class="font-bold">TAB</span>
|
||||
</span>
|
||||
</Button>
|
||||
{:else}
|
||||
<ToggleButton
|
||||
small
|
||||
light
|
||||
tooltip="JavaScript expression ('flow_input' or 'results')."
|
||||
value="javascript"
|
||||
icon={FunctionSquare}
|
||||
/>
|
||||
{/if}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
|
||||
@@ -363,7 +458,7 @@
|
||||
|
||||
<div class="max-w-xs" />
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div class="relative" on:keyup={stepInputGen?.onKeyUp}>
|
||||
<div class="relative" on:keyup={handleKeyUp}>
|
||||
<!-- {#if $propPickerConfig?.propName == argName && $propPickerConfig?.insertionMode == 'connect'}
|
||||
<span
|
||||
class={'text-white z-50 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute top-0 right-0 bg-blue-500'}
|
||||
|
||||
@@ -405,6 +405,15 @@
|
||||
editor && editor.dispose()
|
||||
} catch (err) {}
|
||||
})
|
||||
|
||||
export function setCursorToEnd(): void {
|
||||
if (editor) {
|
||||
const lastLine = editor.getModel()?.getLineCount() ?? 1
|
||||
const lastColumn = editor.getModel()?.getLineMaxColumn(lastLine) ?? 1
|
||||
editor.setPosition({ lineNumber: lastLine, column: lastColumn })
|
||||
editor.focus()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<EditorTheme />
|
||||
|
||||
@@ -77,6 +77,8 @@
|
||||
.filter(([i, f, m]) => f.length > 0)
|
||||
|
||||
setContext<PropPickerWrapperContext>('PropPickerWrapper', {
|
||||
inputMatches: writable(undefined),
|
||||
filteredPickableProperties: writable(undefined),
|
||||
focusProp: () => {},
|
||||
propPickerConfig: writable(undefined),
|
||||
clearFocus: () => {}
|
||||
|
||||
@@ -260,3 +260,40 @@ declare const approvers: string
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export function buildPrefixRegex(words: string[]): Array<{ regex: RegExp; word: string }> {
|
||||
return words.map((word) => {
|
||||
const prefixes: string[] = []
|
||||
for (let i = 1; i <= word.length; i++) {
|
||||
prefixes.push(word.slice(0, i) + '$')
|
||||
}
|
||||
prefixes.push(word + '\\.')
|
||||
prefixes.push(word + '\\[')
|
||||
|
||||
return {
|
||||
regex: new RegExp(`^(${prefixes.join('|')}).*`),
|
||||
word
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function filterNestedObject(obj: any, nestedKeys: string[]) {
|
||||
if (nestedKeys.length === 0) return {}
|
||||
if (nestedKeys.length === 1) {
|
||||
if (nestedKeys[0] === '') {
|
||||
return obj
|
||||
}
|
||||
const regexes = buildPrefixRegex(Object.keys(obj))
|
||||
const matches = regexes.filter(({ regex }) => regex.test(nestedKeys[0]))
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter(([key]) => matches.some(({ word }) => word === key))
|
||||
)
|
||||
}
|
||||
const [key, ...rest] = nestedKeys
|
||||
if (obj && typeof obj === 'object' && key in obj) {
|
||||
const result = {}
|
||||
result[key] = filterNestedObject(obj[key], rest)
|
||||
return result
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
export type PropPickerWrapperContext = {
|
||||
propPickerConfig: Writable<PropPickerConfig | undefined>
|
||||
inputMatches: Writable<{ word: string; value: string }[] | undefined>
|
||||
filteredPickableProperties: Writable<PickableProperties | undefined>
|
||||
focusProp: (propName: string, insertionMode: InsertionMode, onSelect: SelectCallback) => void
|
||||
clearFocus: () => void
|
||||
}
|
||||
@@ -37,10 +39,14 @@
|
||||
export let noPadding: boolean = false
|
||||
|
||||
const propPickerConfig = writable<PropPickerConfig | undefined>(undefined)
|
||||
const filteredPickableProperties = writable<PickableProperties | undefined>(undefined)
|
||||
const inputMatches = writable<{ word: string; value: string }[] | undefined>(undefined)
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
setContext<PropPickerWrapperContext>('PropPickerWrapper', {
|
||||
propPickerConfig,
|
||||
inputMatches,
|
||||
filteredPickableProperties,
|
||||
focusProp: (propName, insertionMode, onSelect) => {
|
||||
propPickerConfig.set({
|
||||
propName,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { keepByKey } from './utils'
|
||||
import type { PickableProperties } from '../flows/previousResults'
|
||||
import ClearableInput from '../common/clearableInput/ClearableInput.svelte'
|
||||
import { filterNestedObject } from '../flows/previousResults'
|
||||
|
||||
export let pickableProperties: PickableProperties
|
||||
export let displayContext = true
|
||||
@@ -20,18 +21,31 @@
|
||||
let resources: Record<string, any> = {}
|
||||
let displayVariable = false
|
||||
let displayResources = false
|
||||
|
||||
let allResultsCollapsed = true
|
||||
let collapsableInitialState:
|
||||
| {
|
||||
allResultsCollapsed: boolean
|
||||
displayVariable: boolean
|
||||
displayResources: boolean
|
||||
}
|
||||
| undefined
|
||||
|
||||
let filterActive = false
|
||||
|
||||
const EMPTY_STRING = ''
|
||||
let search = ''
|
||||
|
||||
const { propPickerConfig } = getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
const { propPickerConfig, filteredPickableProperties, inputMatches } =
|
||||
getContext<PropPickerWrapperContext>('PropPickerWrapper')
|
||||
|
||||
$filteredPickableProperties = { ...pickableProperties }
|
||||
let flowInputsFiltered = pickableProperties.flow_input
|
||||
let resultByIdFiltered = pickableProperties.priorIds
|
||||
|
||||
let timeout: NodeJS.Timeout
|
||||
function onSearch(search: string) {
|
||||
filterActive = false
|
||||
clearTimeout(timeout)
|
||||
setTimeout(() => {
|
||||
flowInputsFiltered =
|
||||
@@ -72,29 +86,124 @@
|
||||
).map((resource) => [resource.path, resource.description ?? ''])
|
||||
)
|
||||
}
|
||||
|
||||
async function filterPickableProperties() {
|
||||
if (!filterActive) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!$inputMatches?.some((match) => match.word === 'flow_input')) {
|
||||
flowInputsFiltered = []
|
||||
}
|
||||
if (!$inputMatches?.some((match) => match.word === 'results')) {
|
||||
resultByIdFiltered = []
|
||||
}
|
||||
if ($inputMatches?.length == 1) {
|
||||
if ($inputMatches[0].word === 'flow_input') {
|
||||
flowInputsFiltered = pickableProperties.flow_input
|
||||
let [, ...nestedKeys] = $inputMatches[0].value.split('.')
|
||||
let filtered = filterNestedObject(flowInputsFiltered, nestedKeys)
|
||||
if (Object.keys(filtered).length > 0) {
|
||||
flowInputsFiltered = filtered
|
||||
}
|
||||
} else if ($inputMatches[0].word === 'results') {
|
||||
resultByIdFiltered = pickableProperties.priorIds
|
||||
let [, ...nestedKeys] = $inputMatches[0].value.split('.')
|
||||
let filtered = filterNestedObject(resultByIdFiltered, nestedKeys)
|
||||
if (Object.keys(filtered).length > 0) {
|
||||
resultByIdFiltered = filtered
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($filteredPickableProperties) {
|
||||
resultByIdFiltered && ($filteredPickableProperties.priorIds = resultByIdFiltered)
|
||||
flowInputsFiltered && ($filteredPickableProperties.flow_input = flowInputsFiltered)
|
||||
}
|
||||
}
|
||||
|
||||
async function updateCollapsable() {
|
||||
if (!$inputMatches || $inputMatches.length !== 1) {
|
||||
resetCollapsable()
|
||||
return
|
||||
}
|
||||
|
||||
if (!collapsableInitialState) {
|
||||
collapsableInitialState = { allResultsCollapsed, displayVariable, displayResources }
|
||||
}
|
||||
|
||||
if ($inputMatches[0].word === 'variable') {
|
||||
await loadVariables()
|
||||
displayVariable = true
|
||||
return
|
||||
}
|
||||
if ($inputMatches[0].word === 'resource') {
|
||||
await loadResources()
|
||||
displayResources = true
|
||||
return
|
||||
}
|
||||
if ($inputMatches[0].word === 'results') {
|
||||
allResultsCollapsed = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function resetCollapsable() {
|
||||
if (!collapsableInitialState) {
|
||||
return
|
||||
}
|
||||
;({ allResultsCollapsed, displayVariable, displayResources } = collapsableInitialState)
|
||||
collapsableInitialState = undefined
|
||||
}
|
||||
|
||||
async function updateFilterActive() {
|
||||
const prev = filterActive
|
||||
|
||||
filterActive = Boolean(
|
||||
$inputMatches &&
|
||||
$inputMatches?.length > 0 &&
|
||||
$propPickerConfig?.insertionMode === 'insert' &&
|
||||
search === EMPTY_STRING
|
||||
)
|
||||
|
||||
if (prev && !filterActive) {
|
||||
flowInputsFiltered = pickableProperties.flow_input
|
||||
resultByIdFiltered = pickableProperties.priorIds
|
||||
}
|
||||
}
|
||||
|
||||
async function updateState() {
|
||||
await updateFilterActive()
|
||||
await filterPickableProperties()
|
||||
await updateCollapsable()
|
||||
}
|
||||
|
||||
$: search, $inputMatches, $propPickerConfig, updateState()
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full rounded overflow-hidden">
|
||||
<div class="px-2 py-2">
|
||||
<ClearableInput bind:value={search} placeholder="Search prop..." />
|
||||
</div>
|
||||
<div class="overflow-y-auto px-2 pt-2 grow">
|
||||
<div class="flex justify-between items-center space-x-1">
|
||||
<span class="font-normal text-sm text-secondary">Flow Input</span>
|
||||
<div class="flex space-x-2 items-center" />
|
||||
</div>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={flowInputsFiltered}
|
||||
prefix="flow_input"
|
||||
on:select
|
||||
/>
|
||||
</div>
|
||||
<div class="overflow-y-auto px-2 pt-2 grow" class:bg-surface-secondary={!$propPickerConfig}>
|
||||
{#if flowInputsFiltered && (Object.keys(flowInputsFiltered).length > 0 || !filterActive)}
|
||||
<div class="flex justify-between items-center space-x-1">
|
||||
<span class="font-normal text-sm text-secondary">Flow Input</span>
|
||||
<div class="flex space-x-2 items-center" />
|
||||
</div>
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
json={flowInputsFiltered}
|
||||
prefix="flow_input"
|
||||
on:select
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if error}
|
||||
<span class="font-normal text-sm text-secondary">Error</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
@@ -112,7 +221,7 @@
|
||||
{#if Object.keys(pickableProperties.priorIds).length > 0}
|
||||
{#if suggestedPropsFiltered && Object.keys(suggestedPropsFiltered).length > 0}
|
||||
<span class="font-normal text-sm text-secondary">Suggested Results</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
@@ -124,11 +233,11 @@
|
||||
</div>
|
||||
{/if}
|
||||
<span class="font-normal text-sm text-secondary">All Results</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
collapsed={true}
|
||||
collapseLevel={allResultsCollapsed ? 1 : undefined}
|
||||
json={resultByIdFiltered}
|
||||
prefix="results"
|
||||
on:select
|
||||
@@ -136,9 +245,12 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
{#if previousId}
|
||||
{@const json = Object.fromEntries(
|
||||
Object.entries(resultByIdFiltered).filter(([k, v]) => k == previousId)
|
||||
)}
|
||||
{#if previousId && Object.keys(json).length > 0}
|
||||
<span class="font-normal text-sm text-secondary">Previous Result</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
@@ -152,7 +264,7 @@
|
||||
{/if}
|
||||
{#if pickableProperties.hasResume}
|
||||
<span class="font-normal text-sm text-secondary">Resume payloads</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
@@ -166,9 +278,9 @@
|
||||
</div>
|
||||
{/if}
|
||||
{#if Object.keys(pickableProperties.priorIds).length > 0}
|
||||
{#if suggestedPropsFiltered && Object.keys(suggestedPropsFiltered).length > 0}
|
||||
{#if !filterActive && suggestedPropsFiltered && Object.keys(suggestedPropsFiltered).length > 0}
|
||||
<span class="font-normal text-sm text-secondary">Suggested Results</span>
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
@@ -179,112 +291,105 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<span class="font-normal text-sm text-secondary">All Results</span>
|
||||
{#if !allResultsCollapsed}
|
||||
{#if Object.keys(resultByIdFiltered).length > 0}
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<span class="font-normal text-sm text-secondary">All Results</span>
|
||||
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
collapseLevel={allResultsCollapsed ? 1 : undefined}
|
||||
json={resultByIdFiltered}
|
||||
prefix="results"
|
||||
on:select
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if displayContext}
|
||||
{#if !filterActive || $inputMatches?.some((match) => match.word === 'variable')}
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<span class="font-normal text-sm text-secondary">Variables:</span>
|
||||
|
||||
{#if displayVariable}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
allResultsCollapsed = true
|
||||
displayVariable = false
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-mono h-4 text-2xs font-thin px-1 rounded-[0.275rem]">-</Button
|
||||
>
|
||||
{/if}
|
||||
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
bind:collapsed={allResultsCollapsed}
|
||||
json={resultByIdFiltered}
|
||||
prefix="results"
|
||||
on:select
|
||||
/>
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={variables}
|
||||
prefix="variable"
|
||||
on:select
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={async () => {
|
||||
await loadVariables()
|
||||
displayVariable = true
|
||||
}}
|
||||
wrapperClasses="inline-flex w-fit"
|
||||
btnClasses="font-normal text-2xs rounded-[0.275rem] h-4 px-1"
|
||||
>
|
||||
{'{...}'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if !filterActive || $inputMatches?.some((match) => match.word === 'resource')}
|
||||
<div class="overflow-y-auto pb-2">
|
||||
<span class="font-normal text-sm text-secondary">Resources:</span>
|
||||
|
||||
{#if displayResources}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
displayResources = false
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-mono h-4 text-2xs font-thin px-1 rounded-[0.275rem]">-</Button
|
||||
>
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={resources}
|
||||
prefix="resource"
|
||||
on:select
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={async () => {
|
||||
await loadResources()
|
||||
displayResources = true
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-normal text-2xs rounded-[0.275rem] h-4 px-1"
|
||||
>
|
||||
{'{...}'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if displayContext}
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<span class="font-normal text-sm text-secondary">Variables:</span>
|
||||
|
||||
{#if displayVariable}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
displayVariable = false
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-mono h-4 text-2xs font-thin px-1 rounded-[0.275rem]">-</Button
|
||||
>
|
||||
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={variables}
|
||||
prefix="variable"
|
||||
on:select
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={async () => {
|
||||
await loadVariables()
|
||||
displayVariable = true
|
||||
}}
|
||||
wrapperClasses="inline-flex w-fit"
|
||||
btnClasses="font-normal text-2xs rounded-[0.275rem] h-4 px-1"
|
||||
>
|
||||
{'{...}'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="overflow-y-auto mb-2">
|
||||
<span class="font-normal text-sm text-secondary">Resources:</span>
|
||||
|
||||
{#if displayResources}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
displayResources = false
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-mono h-4 text-2xs font-thin px-1 rounded-[0.275rem]">-</Button
|
||||
>
|
||||
<ObjectViewer
|
||||
{allowCopy}
|
||||
pureViewer={!$propPickerConfig}
|
||||
rawKey={true}
|
||||
json={resources}
|
||||
prefix="resource"
|
||||
on:select
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="border"
|
||||
on:click={async () => {
|
||||
await loadResources()
|
||||
displayResources = true
|
||||
}}
|
||||
wrapperClasses="inline-flex whitespace-nowrap w-fit"
|
||||
btnClasses="font-normal text-2xs rounded-[0.275rem] h-4 px-1"
|
||||
>
|
||||
{'{...}'}
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user