feat(frontend): Update split panes (#741)

This commit is contained in:
Ádám Kovács
2022-10-17 15:43:31 +02:00
committed by GitHub
parent 9e0c2d759b
commit 8a774e0d04
21 changed files with 2141 additions and 483 deletions
+1625 -31
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -48,7 +48,7 @@
"svelte-overlay": "^1.4.1",
"svelte-popperjs": "^1.3.2",
"svelte-preprocess": "^4.9.8",
"svelte-split-pane": "^0.1.2",
"svelte-splitpanes": "^0.7.3",
"svelte2tsx": "^0.5.20",
"tailwindcss": "^3.1.8",
"tslib": "^2.3.1",
+3 -4
View File
@@ -3,9 +3,9 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare namespace App {
interface Stuff {
title: string?
}
interface Stuff {
title: string?
}
}
declare var __pkg__: { version: string }
@@ -14,4 +14,3 @@ declare module 'svelte-highlight/languages/json'
declare module 'svelte-highlight/languages/python'
declare module 'svelte-highlight/languages/typescript'
declare module 'svelte-highlight/styles/github'
declare module 'svelte-split-pane'
+1 -1
View File
@@ -219,7 +219,7 @@
<div>
<Button
color="light"
btnClasses="mr-1 !font-medium"
btnClasses="mx-1 !font-medium"
on:click={variablePicker.openModal}
size="xs"
spacingSize="md"
@@ -2,9 +2,7 @@
import type { Schema } from '$lib/common'
import type { FlowModule, Job } from '$lib/gen'
import { getScriptByPath, sendUserToast, truncateRev } from '$lib/utils'
import { HSplitPane, VSplitPane } from 'svelte-split-pane'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import RunForm from './RunForm.svelte'
import TestJobLoader from './TestJobLoader.svelte'
import LogViewer from './LogViewer.svelte'
@@ -67,50 +65,47 @@
bind:isLoading={testIsLoading}
bind:job={testJob}
/>
<HSplitPane leftPaneSize="50%" rightPaneSize="50%" minLeftPaneSize="20%" minRightPaneSize="20%">
<left slot="left" class="relative">
<div class="overflow-auto h-full p-4">
<RunForm
runnable={{ summary: mod.summary ?? '', schema, description: '' }}
runAction={(_, args) => runTest(args)}
schedulable={false}
buttonText="Test just this step (Ctrl+Enter)"
detailed={false}
bind:args={stepArgs}
/>
{#if testIsLoading}
<Button
on:click={testJobLoader?.cancelJob}
btnClasses="w-full mt-4"
color="red"
size="sm"
startIcon={{
icon: faRotateRight,
classes: 'animate-spin'
}}
>
Cancel
</Button>
{/if}
</div>
</left>
<right slot="right">
<div class="overflow-auto h-full">
<VSplitPane topPanelSize="50%" downPanelSize="50%">
<top slot="top">
<LogViewer content={testJob?.logs} isLoading={testIsLoading} />
</top>
<down slot="down">
<pre class="overflow-x-auto break-all relative h-full p-2 text-sm"
>{#if testJob != undefined && 'result' in testJob && testJob.result != undefined}<DisplayResult
result={testJob.result}
/>
{:else if testIsLoading}Waiting for result...
{:else}Test to see the result here
{/if}
</pre>
</down>
</VSplitPane>
</div>
</right>
</HSplitPane>
<Splitpanes>
<Pane size={50} minSize={20} class="p-4">
<RunForm
runnable={{ summary: mod.summary ?? '', schema, description: '' }}
runAction={(_, args) => runTest(args)}
schedulable={false}
buttonText="Test just this step (Ctrl+Enter)"
detailed={false}
bind:args={stepArgs}
/>
{#if testIsLoading}
<Button
on:click={testJobLoader?.cancelJob}
btnClasses="w-full mt-4"
color="red"
size="sm"
startIcon={{
icon: faRotateRight,
classes: 'animate-spin'
}}
>
Cancel
</Button>
{/if}
</Pane>
<Pane size={50} minSize={20}>
<Splitpanes horizontal>
<Pane size={50} minSize={10}>
<LogViewer content={testJob?.logs} isLoading={testIsLoading} />
</Pane>
<Pane size={50} minSize={10} class="text-sm text-gray-600">
{#if testJob != undefined && 'result' in testJob && testJob.result != undefined}
<pre class="overflow-x-auto break-all relative h-full px-2">
<DisplayResult result={testJob.result} />
</pre>
{:else}
<div class="p-2">
{testIsLoading ? 'Waiting for result...' : 'Test to see the result here'}
</div>
{/if}
</Pane>
</Splitpanes>
</Pane>
</Splitpanes>
+83 -95
View File
@@ -5,19 +5,17 @@
import { emptySchema, scriptLangToEditorLang } from '$lib/utils'
import { faPlay, faRotateRight } from '@fortawesome/free-solid-svg-icons'
import Editor from './Editor.svelte'
import { inferArgs } from '$lib/infer'
import type { Preview } from '$lib/gen/models/Preview'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import SchemaForm from './SchemaForm.svelte'
import LogPanel from './script_editor/LogPanel.svelte'
import { HSplitPane, VSplitPane } from 'svelte-split-pane'
import LogPanel from './scriptEditor/LogPanel.svelte'
import { faGithub } from '@fortawesome/free-brands-svg-icons'
import EditorBar from './EditorBar.svelte'
import TestJobLoader from './TestJobLoader.svelte'
import { onMount } from 'svelte'
import { Button, Kbd } from './common'
import SplitPanesWrapper from './splitPanes/SplitPanesWrapper.svelte'
// Exported
export let schema: Schema = emptySchema()
@@ -122,94 +120,84 @@
</div>
</div>
</div>
<div class="flex-1 overflow-auto">
<HSplitPane leftPaneSize="60%" rightPaneSize="40%" minLeftPaneSize="50px" minRightPaneSize="50px">
<left slot="left">
<div class="h-full">
<div
class="p-2 h-full"
on:mouseleave={() => {
inferSchema()
}}
>
<Editor
bind:code
bind:websocketAlive
bind:this={editor}
cmdEnterAction={async () => {
await inferSchema()
runTest()
}}
formatAction={async () => {
await inferSchema()
localStorage.setItem(path ?? 'last_save', code)
lastSave = code
}}
class="flex flex-1 h-full"
lang={scriptLangToEditorLang(lang)}
automaticLayout={true}
/>
<SplitPanesWrapper>
<Pane size={60} minSize={10}>
<div
class="p-2 h-full"
on:mouseleave={() => {
inferSchema()
}}
>
<Editor
bind:code
bind:websocketAlive
bind:this={editor}
cmdEnterAction={async () => {
await inferSchema()
runTest()
}}
formatAction={async () => {
await inferSchema()
localStorage.setItem(path ?? 'last_save', code)
lastSave = code
}}
class="flex flex-1 h-full"
lang={scriptLangToEditorLang(lang)}
automaticLayout={true}
/>
</div>
</Pane>
<Pane size={40} minSize={10}>
<Splitpanes horizontal>
<Pane size={30}>
<div class="p-4">
<div class="break-all relative font-sans">
<p class="items-baseline break-normal text-sm text-gray-600 hidden md:block mb-3">
To recompute the input schema press <Kbd>Ctrl/Cmd</Kbd> + <Kbd>S</Kbd> or move the focus
outside of the text editor
</p>
<SchemaForm {schema} bind:args bind:isValid />
</div>
</div>
</div>
</left>
<right slot="right">
<div class="h-full">
<VSplitPane topPanelSize="30%" downPanelSize="70%">
<top slot="top">
<div class="h-full overflow-auto">
<div class="p-4">
<div class="break-all relative font-sans">
<p class="items-baseline break-normal text-sm text-gray-600 hidden md:block mb-3">
To recompute the input schema press <Kbd>Ctrl/Cmd</Kbd> + <Kbd>S</Kbd> or move the
focus outside of the text editor
</p>
<SchemaForm {schema} bind:args bind:isValid />
</div>
</div>
</div>
</top>
<down slot="down">
<div class="h-full overflow-hidden">
<div class="px-2 py-1">
{#if testIsLoading}
<Button
on:click={testJobLoader?.cancelJob}
btnClasses="w-full"
color="red"
size="xs"
startIcon={{
icon: faRotateRight,
classes: 'animate-spin'
}}
>
'Cancel'
</Button>
{:else}
<Button
on:click={runTest}
btnClasses="w-full"
size="xs"
startIcon={{
icon: faPlay,
classes: 'animate-none'
}}
>
{testIsLoading ? 'Running' : 'Test (Ctrl+Enter)'}
</Button>
{/if}
</div>
<LogPanel
{path}
{lang}
previewJob={testJob}
{pastPreviews}
previewIsLoading={testIsLoading}
bind:lastSave
/>
</div>
</down>
</VSplitPane>
</div>
</right>
</HSplitPane>
</div>
</Pane>
<Pane size={70}>
<div class="px-2 py-1">
{#if testIsLoading}
<Button
on:click={testJobLoader?.cancelJob}
btnClasses="w-full"
color="red"
size="xs"
startIcon={{
icon: faRotateRight,
classes: 'animate-spin'
}}
>
'Cancel'
</Button>
{:else}
<Button
on:click={runTest}
btnClasses="w-full"
size="xs"
startIcon={{
icon: faPlay,
classes: 'animate-none'
}}
>
{testIsLoading ? 'Running' : 'Test (Ctrl+Enter)'}
</Button>
{/if}
</div>
<LogPanel
{path}
{lang}
previewJob={testJob}
{pastPreviews}
previewIsLoading={testIsLoading}
bind:lastSave
/>
</Pane>
</Splitpanes>
</Pane>
</SplitPanesWrapper>
@@ -4,6 +4,7 @@
export let value: string
export let alwaysMounted: boolean = false
export let style = ''
let clazz: string = ''
export { clazz as class }
@@ -11,7 +12,7 @@
</script>
{#if value === $selected || alwaysMounted}
<div class={`${clazz} ${value === $selected ? 'visible' : 'hidden'}`}>
<div class={`${clazz} ${value === $selected ? 'visible' : 'hidden'}`} {style}>
<slot />
</div>
{/if}
@@ -33,7 +33,9 @@
}
</script>
<div class="border-b border-gray-200 flex flex-row">
<div
class="border-b border-gray-200 flex flex-row whitespace-nowrap overflow-y-auto {$$props.class}"
>
<slot />
</div>
<slot name="content" />
@@ -1,6 +1,5 @@
<!-- Flow Editor: Top level component -->
<script lang="ts">
import { HSplitPane } from 'svelte-split-pane'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import FlowEditorPanel from './content/FlowEditorPanel.svelte'
import FlowModuleSchemaMap from './map/FlowModuleSchemaMap.svelte'
import { flowStore } from './flowStore'
@@ -11,8 +10,8 @@
</script>
<div class="h-full overflow-hidden border-t">
<HSplitPane leftPaneSize="25%" rightPaneSize="75%" minLeftPaneSize="20%" minRightPaneSize="40%">
<left slot="left" class="h-full flex flex-col ">
<Splitpanes>
<Pane size={25} minSize={20} class="h-full flex flex-col">
<FlowPreviewButtons />
<div class="grow overflow-auto p-4 bg-gray-50">
{#if $flowStore.value.modules && $flowStateStore.modules}
@@ -22,11 +21,9 @@
/>
{/if}
</div>
</left>
<right slot="right" class="h-full">
<div class="h-full overflow-auto bg-white ">
<FlowEditorPanel {initialPath} />
</div>
</right>
</HSplitPane>
</Pane>
<Pane size={75} minSize={40}>
<FlowEditorPanel {initialPath} />
</Pane>
</Splitpanes>
</div>
@@ -9,13 +9,13 @@
import { getStepPropPicker } from '../flowStateUtils'
import { flowStateStore } from '../flowState'
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import { VSplitPane } from 'svelte-split-pane'
import FlowModuleEarlyStop from './FlowModuleEarlyStop.svelte'
import FlowModuleSuspend from './FlowModuleSuspend.svelte'
import FlowRetries from './FlowRetries.svelte'
import { Button, Tab, TabContent, Tabs } from '$lib/components/common'
import type { FlowModule } from '$lib/gen/models/FlowModule'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { faTrash, faPlus } from '@fortawesome/free-solid-svg-icons'
const { previewArgs } = getContext<FlowEditorContext>('FlowEditorContext')
@@ -40,85 +40,78 @@
<div slot="header" class="grow">
<input bind:value={mod.summary} placeholder={'Summary'} />
</div>
<div class="overflow-hidden flex-grow">
<VSplitPane topPanelSize="60%" downPanelSize="40%" minTopPaneSize="20%" minDownPaneSize="20%">
<top slot="top" class="h-full">
<div class="p-6 flex flex-col h-full overflow-clip">
{#if mod.value.type === 'forloopflow'}
<span class="mb-2 text-sm font-bold"
>Iterator expression
<Tooltip>
List to iterate over. For more information see the
<a href="https://docs.windmill.dev/docs/getting_started/flows#for-loops">docs.</a>
</Tooltip>
</span>
{#if mod.value.iterator.type == 'javascript'}
<div class="border w-full">
<PropPickerWrapper
{pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
}}
>
<SimpleEditor
bind:this={editor}
lang="javascript"
bind:code={mod.value.iterator.expr}
class="small-editor"
shouldBindKey={false}
/>
</PropPickerWrapper>
</div>
{:else}
<Button
on:click={() => {
if (mod.value.type === 'forloopflow') mod.value.iterator.type = 'javascript'
}}
/>
{/if}
<span class="my-2 text-sm font-bold">Skip failures</span>
<Toggle
bind:checked={mod.value.skip_failures}
options={{
right: 'Skip failures'
<Splitpanes horizontal class="!max-h-[calc(100%-48px)]">
<Pane size={60} minSize={20} class="p-4">
{#if mod.value.type === 'forloopflow'}
<div class="mb-2 text-sm font-bold">
Iterator expression
<Tooltip>
List to iterate over. For more information see the
<a href="https://docs.windmill.dev/docs/getting_started/flows#for-loops">docs.</a>
</Tooltip>
</div>
{#if mod.value.iterator.type == 'javascript'}
<div class="border w-full">
<PropPickerWrapper
{pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
}}
/>
{/if}
</div></top
>
<down slot="down" class="flex flex-col flex-1 h-full">
<Tabs bind:selected>
<Tab value="retries">Retries</Tab>
<Tab value="early-stop">Early Stop</Tab>
<Tab value="suspend">Sleep/Suspend</Tab>
>
<SimpleEditor
bind:this={editor}
lang="javascript"
bind:code={mod.value.iterator.expr}
class="small-editor"
shouldBindKey={false}
/>
</PropPickerWrapper>
</div>
{:else}
<Button
on:click={() => {
if (mod.value.type === 'forloopflow') mod.value.iterator.type = 'javascript'
}}
/>
{/if}
<div class="mt-6 mb-2 text-sm font-bold">Skip failures</div>
<Toggle
bind:checked={mod.value.skip_failures}
options={{
right: 'Skip failures'
}}
/>
{/if}
</Pane>
<Pane size={40} minSize={20} class="flex flex-col flex-1">
<Tabs bind:selected>
<Tab value="retries">Retries</Tab>
<Tab value="early-stop">Early Stop</Tab>
<Tab value="suspend">Sleep/Suspend</Tab>
<svelte:fragment slot="content">
<div class="overflow-hidden bg-white" style="height:calc(100% - 32px);">
<TabContent value="retries" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowRetries bind:flowModule={mod} />
</div>
</TabContent>
<svelte:fragment slot="content">
<div class="overflow-hidden bg-white" style="height:calc(100% - 32px);">
<TabContent value="retries" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowRetries bind:flowModule={mod} />
</div>
</TabContent>
<TabContent value="early-stop" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleEarlyStop bind:flowModule={mod} />
</div>
</TabContent>
<TabContent value="early-stop" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleEarlyStop bind:flowModule={mod} />
</div>
</TabContent>
<TabContent value="suspend" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleSuspend bind:flowModule={mod} />
</div>
</TabContent>
</div>
</svelte:fragment>
</Tabs>
</down>
</VSplitPane>
</div></FlowCard
>
<TabContent value="suspend" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleSuspend bind:flowModule={mod} />
</div>
</TabContent>
</div>
</svelte:fragment>
</Tabs>
</Pane>
</Splitpanes>
</FlowCard>
</div>
@@ -6,10 +6,8 @@
</script>
<script lang="ts">
import { VSplitPane } from 'svelte-split-pane'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import Tab from '$lib/components/common/tabs/Tab.svelte'
import TabContent from '$lib/components/common/tabs/TabContent.svelte'
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
import Editor from '$lib/components/Editor.svelte'
import EditorBar from '$lib/components/EditorBar.svelte'
@@ -26,14 +24,13 @@
} from '$lib/components/flows/flowStateUtils'
import { flowStore } from '$lib/components/flows/flowStore'
import SchemaForm from '$lib/components/SchemaForm.svelte'
import { RawScript, type FlowModule } from '$lib/gen'
import FlowCard from '../common/FlowCard.svelte'
import FlowModuleHeader from './FlowModuleHeader.svelte'
import { flowStateStore, type FlowModuleState } from '../flowState'
import { scriptLangToEditorLang } from '$lib/utils'
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import { getContext, setContext } from 'svelte'
import { afterUpdate, getContext, setContext } from 'svelte'
import type { FlowEditorContext } from '../types'
import { loadSchemaFromModule, selectedIdToIndexes } from '../utils'
import { writable, type Writable } from 'svelte/store'
@@ -54,6 +51,9 @@
let modulePreview: ModulePreview
let websocketAlive = { pyright: false, black: false, deno: false, go: false }
let selected = 'inputs'
let wrapper: HTMLDivElement
let panes: HTMLElement
let totalTopGap = 0
$: shouldPick = isEmptyFlowModule(flowModule)
$: stepPropPicker = failureModule
@@ -99,11 +99,20 @@
width,
threshold: FLOW_MODULE_WIDTH_THRESHOLD
})
afterUpdate(() => {
totalTopGap = 0
if (!(wrapper && panes)) return
const wrapperTop = wrapper.getBoundingClientRect().top
const panesTop = panes.getBoundingClientRect().top
totalTopGap = panesTop - wrapperTop
})
</script>
<svelte:window on:keydown={onKeyDown} />
<div class="flex flex-col h-full" bind:clientWidth={$width}>
<div class="h-full" bind:this={wrapper} bind:clientWidth={$width}>
<FlowCard bind:flowModule>
<svelte:fragment slot="header">
<FlowModuleHeader
@@ -143,7 +152,7 @@
/>
{:else}
{#if flowModule.value.type === 'rawscript'}
<div class="flex-shrink-0 border-b-2 shadow-sm p-1 mb-1">
<div class="border-b-2 shadow-sm p-1 mb-1">
<EditorBar
{editor}
lang={flowModule.value['language'] ?? 'deno'}
@@ -153,16 +162,15 @@
</div>
{/if}
<div class="overflow-hidden flex-grow">
<VSplitPane
topPanelSize={flowModule.value.type === 'rawscript' ? '50%' : '0%'}
downPanelSize={flowModule.value.type === 'rawscript' ? '50%' : '100%'}
minTopPaneSize="20%"
minDownPaneSize="20%"
>
<top slot="top">
<div
bind:this={panes}
class="h-full"
style="max-height: calc(100% - {totalTopGap}px) !important;"
>
<Splitpanes horizontal>
<Pane size={50} minSize={20}>
{#if flowModule.value.type === 'rawscript'}
<div on:mouseleave={() => reload(flowModule)} class="h-full overflow-auto">
<div on:mouseleave={() => reload(flowModule)} class="h-full">
<Editor
bind:websocketAlive
bind:this={editor}
@@ -182,9 +190,8 @@
{:else if flowModule.value.type === 'script'}
<FlowModuleScript {flowModule} />
{/if}
</top>
<down slot="down" class="flex flex-col flex-1 h-full">
</Pane>
<Pane size={50} minSize={20}>
<Tabs bind:selected>
<Tab value="inputs">Inputs</Tab>
<Tab value="test">Test</Tab>
@@ -193,56 +200,43 @@
<Tab value="early-stop">Early Stop</Tab>
<Tab value="suspend">Sleep/Suspend</Tab>
{/if}
<svelte:fragment slot="content">
<div class="overflow-hidden bg-white" style="height:calc(100% - 32px);">
<TabContent value="inputs" class="flex flex-col flex-1 h-full">
<PropPickerWrapper pickableProperties={stepPropPicker.pickableProperties}>
<!-- <pre class="text-xs">{JSON.stringify($flowStateStore, null, 4)}</pre> -->
<p class="items-baseline text-xs text-gray-700 italic hidden md:block mb-2">
Move the focus outside of the text editor to recompute the inputs or press
Ctrl/Cmd+S
</p>
<SchemaForm
schema={flowModuleState.schema}
inputTransform={true}
importPath={$selectedId}
bind:args={flowModule.input_transforms}
bind:extraLib={stepPropPicker.extraLib}
/>
</PropPickerWrapper>
</TabContent>
<TabContent value="test" class="flex flex-col flex-1 h-full" alwaysMounted={true}>
<ModulePreview
bind:this={modulePreview}
mod={flowModule}
schema={flowModuleState.schema}
indices={[parentIndex, childIndex]}
/>
</TabContent>
<TabContent value="retries" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowRetries bind:flowModule />
</div>
</TabContent>
<TabContent value="early-stop" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleEarlyStop bind:flowModule />
</div>
</TabContent>
<TabContent value="suspend" class="flex flex-col flex-1 h-full">
<div class="p-4 overflow-y-auto">
<FlowModuleSuspend bind:flowModule />
</div>
</TabContent>
</div>
</svelte:fragment>
</Tabs>
</down>
</VSplitPane>
<div class="h-[calc(100%-32px)]">
{#if selected === 'inputs'}
<div class="h-full overflow-auto">
<PropPickerWrapper pickableProperties={stepPropPicker.pickableProperties}>
<p class="items-baseline text-xs text-gray-700 italic hidden md:block mb-2">
Move the focus outside of the text editor to recompute the inputs or press
Ctrl/Cmd+S
</p>
<SchemaForm
schema={flowModuleState.schema}
inputTransform={true}
importPath={$selectedId}
bind:args={flowModule.input_transforms}
bind:extraLib={stepPropPicker.extraLib}
/>
</PropPickerWrapper>
</div>
{:else if selected === 'test'}
<ModulePreview
bind:this={modulePreview}
mod={flowModule}
schema={flowModuleState.schema}
indices={[parentIndex, childIndex]}
/>
{:else if selected === 'retries'}
<FlowRetries bind:flowModule class="px-4 pb-4 h-full overflow-auto" />
{:else if selected === 'early-stop'}
<FlowModuleEarlyStop bind:flowModule class="px-4 pb-4 h-full overflow-auto" />
{:else if selected === 'suspend'}
<div class="px-4 pb-4 h-full overflow-auto">
<FlowModuleSuspend bind:flowModule />
</div>
{/if}
</div>
</Pane>
</Splitpanes>
</div>
{/if}
</FlowCard>
@@ -42,7 +42,7 @@
}
</script>
<div class="flex flex-col items-start space-y-2">
<div class="flex flex-col items-start space-y-2 {$$props.class}">
<Toggle
checked={isStopAfterIfEnabled}
on:change={() => {
@@ -86,6 +86,6 @@
<span class="text-xs font-bold">Should skip if stopped</span>
<input type="checkbox" disabled />
<span class="text-xs font-bold">Stop condition expression</span>
<textarea disabled rows="3" />
<textarea disabled rows="3" class="min-h-[80px]" />
{/if}
</div>
@@ -21,6 +21,6 @@
$: flowModule && loadCode(flowModule)
</script>
<div class="flex flex-col flex-1 h-full overflow-auto">
<div class="flex flex-col flex-1 h-full overflow-auto p-2">
<HighlightCode {language} {code} />
</div>
@@ -85,15 +85,17 @@
<span class="text-xs font-bold">Sleep for duration (seconds)</span>
{#if flowModule.sleep && schema.properties['sleep']}
<PropPickerWrapper
displayContext={false}
{pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
}}
>
<InputTransformForm bind:arg={flowModule.sleep} argName="sleep" {schema} />
</PropPickerWrapper>
<div class="border">
<PropPickerWrapper
displayContext={false}
{pickableProperties}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
}}
>
<InputTransformForm bind:arg={flowModule.sleep} argName="sleep" {schema} />
</PropPickerWrapper>
</div>
{:else}
<input type="number" disabled />
{/if}
@@ -29,7 +29,7 @@
$: isExponentialRetryEnabled = Boolean(flowModule.retry?.exponential)
</script>
<div class="flex flex-col items-start space-y-1">
<div class="flex flex-col items-start space-y-1 {$$props.class}">
<Toggle
checked={isConstantRetryEnabled}
on:change={() => {
@@ -23,7 +23,7 @@
</script>
<span
class="flex flex-row-reverse justify-between flex-wrap gap-2 w-full py-2 px-4 bg-white border-b border-t"
class="flex flex-row-reverse justify-between items-center flex-wrap gap-2 w-full min-h-[48px] py-1 px-4 bg-white border-b"
>
<Button
btnClasses="grow"
@@ -19,7 +19,7 @@
<script lang="ts">
import PropPicker from '$lib/components/propertyPicker/PropPicker.svelte'
import { createEventDispatcher, setContext } from 'svelte'
import { HSplitPane } from 'svelte-split-pane'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { writable, type Writable } from 'svelte/store'
export let pickableProperties: Object = {}
@@ -43,23 +43,19 @@
})
</script>
<HSplitPane leftPaneSize="50%" rightPaneSize="50%" minLeftPaneSize="20%" minRightPaneSize="20%">
<left slot="left" class="relative">
<div class="overflow-auto h-full p-4">
<slot />
</div>
</left>
<right slot="right">
<div class="overflow-auto h-full">
<PropPicker
{displayContext}
{pickableProperties}
on:select={({ detail }) => {
dispatch('select', detail)
$propPickerConfig?.onSelect(detail)
propPickerConfig.set(undefined)
}}
/>
</div>
</right>
</HSplitPane>
<Splitpanes>
<Pane minSize={20} class="relative p-4 !duration-[0ms]">
<slot />
</Pane>
<Pane minSize={20} class="px-2 py-2 h-full !duration-[0ms]">
<PropPicker
{displayContext}
{pickableProperties}
on:select={({ detail }) => {
dispatch('select', detail)
$propPickerConfig?.onSelect(detail)
propPickerConfig.set(undefined)
}}
/>
</Pane>
</Splitpanes>
@@ -49,92 +49,90 @@
}
</script>
<div class="px-2 pt-2">
<input
type="text"
bind:value={search}
class="bg-gray-50 mt-1 border border-gray-300 text-gray-900 text-sm rounded-lg block px-2 mb-2 w-full"
placeholder="Search prop..."
/>
<div class="flex justify-between items-center space-x-1">
<span class="font-bold text-sm">Step Context</span>
<div class="flex space-x-2 items-center">
{#if $propPickerConfig}
<span
class="flex items-center bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded dark:bg-green-200 dark:text-green-900"
>
{`Selected: ${$propPickerConfig?.propName}`}
</span>
<span
class="flex items-center bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded dark:bg-green-200 dark:text-green-900"
>
{`Mode: ${$propPickerConfig?.insertionMode}`}
</span>
<button
class="border px-2 py-1 text-xs rounded-md flex items-center hover:bg-gray-50 hover:text-gray-900"
on:click={() => clearFocus()}
>
<Icon data={faClose} class="mr-2" scale={0.8} />
Deselect
</button>
{/if}
</div>
<input
type="text"
bind:value={search}
class="bg-gray-50 mt-1 border border-gray-300 text-gray-900 text-sm rounded-lg block px-2 mb-2 w-full"
placeholder="Search prop..."
/>
<div class="flex justify-between items-center space-x-1">
<span class="font-bold text-sm">Step Context</span>
<div class="flex space-x-2 items-center">
{#if $propPickerConfig}
<span
class="flex items-center bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded dark:bg-green-200 dark:text-green-900"
>
{`Selected: ${$propPickerConfig?.propName}`}
</span>
<span
class="flex items-center bg-blue-100 text-blue-800 text-xs font-semibold px-2 py-1 rounded dark:bg-green-200 dark:text-green-900"
>
{`Mode: ${$propPickerConfig?.insertionMode}`}
</span>
<button
class="border px-2 py-1 text-xs rounded-md flex items-center hover:bg-gray-50 hover:text-gray-900"
on:click={() => clearFocus()}
>
<Icon data={faClose} class="mr-2" scale={0.8} />
Deselect
</button>
{/if}
</div>
<div class="overflow-y-auto mb-2">
<ObjectViewer json={propsFiltered} on:select />
</div>
{#if displayContext}
<span class="font-bold text-sm">Variables </span>
<div class="overflow-y-auto mb-2">
{#if displayVariable}
<Button
color="light"
size="xs"
on:click={() => {
displayVariable = false
}}>(-)</Button
>
<ObjectViewer
rawKey={true}
json={variables}
on:select={(e) => dispatch('select', `variable('${e.detail}')`)}
/>
{:else}
<Button
color="light"
size="xs"
on:click={async () => {
await loadVariables()
displayVariable = true
}}>{'{...}'}</Button
>
{/if}
</div>
<span class="font-bold text-sm">Resources</span>
<div class="overflow-y-auto mb-2">
{#if displayResources}
<Button
color="light"
size="xs"
on:click={() => {
displayResources = false
}}>(-)</Button
>
<ObjectViewer
rawKey={true}
json={resources}
on:select={(e) => dispatch('select', `resource('${e.detail}')`)}
/>
{:else}
<Button
color="light"
size="xs"
on:click={async () => {
await loadResources()
displayResources = true
}}>{'{...}'}</Button
>
{/if}
</div>
{/if}
</div>
<div class="overflow-y-auto mb-2">
<ObjectViewer json={propsFiltered} on:select />
</div>
{#if displayContext}
<span class="font-bold text-sm">Variables </span>
<div class="overflow-y-auto mb-2">
{#if displayVariable}
<Button
color="light"
size="xs"
on:click={() => {
displayVariable = false
}}>(-)</Button
>
<ObjectViewer
rawKey={true}
json={variables}
on:select={(e) => dispatch('select', `variable('${e.detail}')`)}
/>
{:else}
<Button
color="light"
size="xs"
on:click={async () => {
await loadVariables()
displayVariable = true
}}>{'{...}'}</Button
>
{/if}
</div>
<span class="font-bold text-sm">Resources</span>
<div class="overflow-y-auto mb-2">
{#if displayResources}
<Button
color="light"
size="xs"
on:click={() => {
displayResources = false
}}>(-)</Button
>
<ObjectViewer
rawKey={true}
json={resources}
on:select={(e) => dispatch('select', `resource('${e.detail}')`)}
/>
{:else}
<Button
color="light"
size="xs"
on:click={async () => {
await loadResources()
displayResources = true
}}>{'{...}'}</Button
>
{/if}
</div>
{/if}
@@ -5,7 +5,6 @@
import { faTimes } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import { check } from 'svelte-awesome/icons'
import Tabs from '../common/tabs/Tabs.svelte'
import Tab from '../common/tabs/Tab.svelte'
import TabContent from '../common/tabs/TabContent.svelte'
@@ -16,8 +15,9 @@
import { json } from 'svelte-highlight/languages'
import DrawerContent from '../common/drawer/DrawerContent.svelte'
import HighlightCode from '../HighlightCode.svelte'
import { VSplitPane } from 'svelte-split-pane'
import LogViewer from '../LogViewer.svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import SplitPanesWrapper from '../splitPanes/SplitPanesWrapper.svelte'
export let path: string | undefined
export let lang: Preview.language
@@ -70,23 +70,28 @@
<Tab value="last_save"><span class="text-xs">Last save</span></Tab>
<svelte:fragment slot="content">
<TabContent value="logs" class="h-full w-full relative">
<VSplitPane topPanelSize="50%" downPanelSize="50%">
<top slot="top">
<!--
TabContent would wrap it in an extra div which is undesirable in this situation
because SplitPanesWrapper uses the parent element as a reference point.
-->
{#if selectedTab === 'logs'}
<SplitPanesWrapper horizontal>
<Pane class="!duration-[0ms]">
<LogViewer content={previewJob?.logs} isLoading={previewIsLoading} />
</top>
<down slot="down">
<pre class="overflow-x-auto break-all relative h-full p-2 text-sm"
>{#if previewJob != undefined && 'result' in previewJob && previewJob.result != undefined}<DisplayResult
result={previewJob.result}
/>
{:else if previewIsLoading}Waiting for result...
{:else}Test to see the result here
{/if}
</pre>
</down>
</VSplitPane>
</TabContent>
</Pane>
<Pane class="!duration-[0ms]">
{#if previewJob != undefined && 'result' in previewJob && previewJob.result != undefined}
<pre class="overflow-x-auto break-all relative h-full px-2">
<DisplayResult result={previewJob.result} />
</pre>
{:else}
<div class="text-sm text-gray-600 p-2">
{previewIsLoading ? 'Waiting for result...' : 'Test to see the result here'}
</div>
{/if}
</Pane>
</SplitPanesWrapper>
{/if}
<TabContent value="history" class="p-2">
<TableCustom>
<tr slot="header-row">
@@ -0,0 +1,39 @@
<script lang="ts">
import { afterUpdate } from 'svelte'
import { Splitpanes } from 'svelte-splitpanes'
/**
* This component should be used instead of `Splitpanes` if the wrapper `Splitpanes`
* has elements that are **NOT** `Pane` above the place of this component.
*/
/** This element will act as the reference point to the `Splitpanes`
* and the top difference will be calculated from it. */
export let refElement: HTMLElement | undefined = undefined
export let panesClass = ''
let wrapper: HTMLDivElement
let gap = 0
function getTopDifference(): number {
const parent = refElement || wrapper.parentElement
if (!(wrapper && parent)) return 0
const wrapperTop = wrapper.getBoundingClientRect().top
const parentTop = parent.getBoundingClientRect().top
return wrapperTop - parentTop
}
afterUpdate(() => {
gap = getTopDifference()
})
</script>
<div
bind:this={wrapper}
class="h-full {$$props.class || ''}"
style="max-height: calc(100% - {gap}px) !important;"
>
<Splitpanes class={panesClass} {...$$restProps}>
<slot />
</Splitpanes>
</div>
+56 -1
View File
@@ -7,7 +7,9 @@ const config = {
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
],
safelist: [
'hljs'
'hljs',
'splitpanes__pane',
'splitpanes__splitter'
],
theme: {
colors: {
@@ -293,6 +295,59 @@ const config = {
color: 'transparent',
backgroundClip: 'text',
backgroundImage: `linear-gradient(to right, ${theme('colors.blue.600')}, ${theme('colors.blue.500')})`
},
'.splitpanes__pane': {
backgroundColor: theme('colors.white') + ' !important',
overflow: 'auto !important',
},
'.splitpanes__splitter': {
backgroundColor: theme('colors.gray.300') + ' !important',
margin: '0 !important',
border: 'none !important',
'&::before': {
backgroundColor: '#00000060 !important',
},
'&::after': {
backgroundColor: '#3f83f850 !important',
margin: '0 !important',
transform: 'none !important',
zIndex: '1001 !important',
transition: 'opacity 200ms !important',
opacity: '0',
'--splitter-hover-size': '7px',
'--splitter-hover-adjustment': '-2px'
},
'&:hover::after': {
opacity: '1',
}
},
'.splitpanes--vertical>.splitpanes__splitter': {
width: '3px !important',
'&::before': {
left: '1px !important',
width: '1px !important',
marginLeft: '0 !important',
},
'&::after': {
top: '0 !important',
height: '100% !important',
left: 'var(--splitter-hover-adjustment) !important',
width: 'var(--splitter-hover-size) !important',
}
},
'.splitpanes--horizontal>.splitpanes__splitter': {
height: '3px !important',
'&::before': {
top: '1px !important',
height: '1px !important',
marginTop: '0 !important',
},
'&::after': {
top: 'var(--splitter-hover-adjustment) !important',
height: 'var(--splitter-hover-size) !important',
left: '0 !important',
width: '100% !important',
}
}
});
addUtilities({