mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 08:01:35 +00:00
UX improvements
This commit is contained in:
Generated
+6
-6
@@ -6560,9 +6560,9 @@
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"name": "@codingame/monaco-editor-treemended",
|
||||
"version": "1.83.5",
|
||||
"resolved": "https://registry.npmjs.org/@codingame/monaco-editor-treemended/-/monaco-editor-treemended-1.83.5.tgz",
|
||||
"integrity": "sha512-YiiEUReD4YytuoEZh4KjWRlliRfeOFsFwJvMs2cMMUcIsAJKj804nramTmpWyJDSoxdSgLgaeiKu1gSEvxCvJw=="
|
||||
"version": "1.83.7",
|
||||
"resolved": "https://registry.npmjs.org/@codingame/monaco-editor-treemended/-/monaco-editor-treemended-1.83.7.tgz",
|
||||
"integrity": "sha512-ol1evPNZ2DyR3vrXo8SVyVXurwfuqIz28TXVq5zNsSmfBbfA1iHPqKLW+krrObREXdzsE4JUwELLoqdjvKIrsw=="
|
||||
},
|
||||
"node_modules/monaco-editor-workers": {
|
||||
"version": "0.44.0",
|
||||
@@ -14843,9 +14843,9 @@
|
||||
}
|
||||
},
|
||||
"monaco-editor": {
|
||||
"version": "npm:@codingame/monaco-editor-treemended@1.83.5",
|
||||
"resolved": "https://registry.npmjs.org/@codingame/monaco-editor-treemended/-/monaco-editor-treemended-1.83.5.tgz",
|
||||
"integrity": "sha512-YiiEUReD4YytuoEZh4KjWRlliRfeOFsFwJvMs2cMMUcIsAJKj804nramTmpWyJDSoxdSgLgaeiKu1gSEvxCvJw=="
|
||||
"version": "npm:@codingame/monaco-editor-treemended@1.83.7",
|
||||
"resolved": "https://registry.npmjs.org/@codingame/monaco-editor-treemended/-/monaco-editor-treemended-1.83.7.tgz",
|
||||
"integrity": "sha512-ol1evPNZ2DyR3vrXo8SVyVXurwfuqIz28TXVq5zNsSmfBbfA1iHPqKLW+krrObREXdzsE4JUwELLoqdjvKIrsw=="
|
||||
},
|
||||
"monaco-editor-workers": {
|
||||
"version": "0.44.0",
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { FlowGraph } from './graph'
|
||||
import HighlightCode from './HighlightCode.svelte'
|
||||
import InputTransformsViewer from './InputTransformsViewer.svelte'
|
||||
import IconedPath from './IconedPath.svelte'
|
||||
|
||||
import type { FlowModule, FlowValue } from '$lib/gen'
|
||||
import { Badge, Button, Drawer, DrawerContent } from './common'
|
||||
import { Highlight } from 'svelte-highlight'
|
||||
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
|
||||
import typescript from 'svelte-highlight/languages/typescript'
|
||||
import FlowPathViewer from './flows/content/FlowPathViewer.svelte'
|
||||
import SchemaViewer from './SchemaViewer.svelte'
|
||||
import { scriptPathToHref } from '$lib/scripts'
|
||||
import { cleanExpr } from '$lib/utils'
|
||||
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import FlowModuleScript from './flows/content/FlowModuleScript.svelte'
|
||||
|
||||
import FlowGraphViewerStep from './FlowGraphViewerStep.svelte'
|
||||
|
||||
export let flow: {
|
||||
summary: string
|
||||
@@ -28,54 +20,10 @@
|
||||
export let noGraph = false
|
||||
|
||||
export let stepDetail: FlowModule | string | undefined = undefined
|
||||
let codeViewer: Drawer
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={codeViewer} size="900px">
|
||||
<DrawerContent title={'Expanded Code'} on:close={codeViewer.closeDrawer}>
|
||||
{#if stepDetail && typeof stepDetail != 'string'}
|
||||
{#if stepDetail.value.type == 'script'}
|
||||
<div class="mb-4">
|
||||
<a
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '')}
|
||||
class=""
|
||||
>
|
||||
<IconedPath path={stepDetail?.value?.path ?? ''} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-2xs mb-4">
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
{#if stepDetail.value.path.startsWith('hub/')}
|
||||
<div class="mt-6">
|
||||
<h3 class="mb-2">Code</h3>
|
||||
<iframe
|
||||
class="w-full h-full text-sm"
|
||||
title="embedded script from hub"
|
||||
frameborder="0"
|
||||
src="https://hub.windmill.dev/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if stepDetail.value.type == 'rawscript'}
|
||||
<div class="text-2xs mb-4">
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
|
||||
<h3 class="mb-2">Code</h3>
|
||||
<span class="!text-xs">
|
||||
<HighlightCode language={stepDetail.value.language} code={stepDetail.value.content} />
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<div class="grid grid-cols-3 w-full h-full">
|
||||
{#if !noGraph}
|
||||
<div
|
||||
@@ -101,134 +49,7 @@
|
||||
noGraph ? 'border-0 w-max' : ''
|
||||
)}
|
||||
>
|
||||
{#if stepDetail == undefined}
|
||||
<div>
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
Click on a step to see its details
|
||||
</p>
|
||||
<h3 class="mb-2 font-semibold">Flow Inputs</h3>
|
||||
<SchemaViewer schema={flow?.schema} />
|
||||
</div>
|
||||
{:else if stepDetail == 'Input'}
|
||||
<SchemaViewer schema={flow?.schema} />
|
||||
{:else if stepDetail == 'Result'}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8"> End of the flow </p>
|
||||
{:else if typeof stepDetail != 'string' && stepDetail.value}
|
||||
<div class="">
|
||||
<div class="sticky top-0 bg-surface w-full flex items-center py-2">
|
||||
{#if stepDetail.id}
|
||||
<Badge color="indigo">
|
||||
{stepDetail.id}
|
||||
</Badge>
|
||||
{/if}
|
||||
<span class="ml-2 font-medium text-lg">
|
||||
{#if stepDetail.summary}
|
||||
{stepDetail.summary}
|
||||
{:else if stepDetail.value.type == 'identity'}
|
||||
Identity
|
||||
{:else if stepDetail.value.type == 'forloopflow'}
|
||||
For loop {#if stepDetail.value.parallel}(parallel){/if}
|
||||
{#if stepDetail.value.skip_failures}(skip failures){/if}
|
||||
{:else if stepDetail.value.type == 'branchall'}
|
||||
Run all branches {#if stepDetail.value.parallel}(parallel){/if}
|
||||
{:else if stepDetail.value.type == 'branchone'}
|
||||
Run one branch
|
||||
{:else if stepDetail.value.type == 'flow'}
|
||||
Inner flow
|
||||
{:else}
|
||||
Anonymous step
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
{#if stepDetail.value.type == 'script'}
|
||||
<div class="pb-2">
|
||||
<a
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
href={scriptPathToHref(stepDetail?.value?.path ?? '')}
|
||||
class=""
|
||||
>
|
||||
<IconedPath path={stepDetail?.value?.path ?? ''} />
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if stepDetail.value.type == 'identity'}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
An identity step returns its inputs as outputs
|
||||
</p>
|
||||
{:else if stepDetail.value.type == 'rawscript'}
|
||||
<div class="text-xs">
|
||||
<h3 class="mb-2 font-semibold">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-2 flex justify-between items-center">
|
||||
<h3 class="font-semibold">Code</h3>
|
||||
<Button size="xs2" color="light" variant="contained" on:click={codeViewer.openDrawer}>
|
||||
Expand
|
||||
</Button>
|
||||
</div>
|
||||
<div class="w-full overflow-auto">
|
||||
<HighlightCode language={stepDetail.value.language} code={stepDetail.value.content} />
|
||||
</div>
|
||||
</div>
|
||||
{:else if stepDetail.value.type == 'script'}
|
||||
<div class="text-2xs">
|
||||
<h3 class="mb-2 font-semibold">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
{#if stepDetail.value.path.startsWith('hub/')}
|
||||
<div class="flex flex-col grow">
|
||||
<div class="mb-2 flex justify-between items-center">
|
||||
<h3 class="font-semibold">Code</h3>
|
||||
<Button
|
||||
size="xs2"
|
||||
color="light"
|
||||
variant="contained"
|
||||
on:click={codeViewer.openDrawer}
|
||||
>
|
||||
Expand
|
||||
</Button>
|
||||
</div>
|
||||
<iframe
|
||||
class="w-full grow text-sm"
|
||||
title="embedded script from hub"
|
||||
frameborder="0"
|
||||
src="https://hub.windmill.dev/embed/script/{stepDetail.value?.path?.substring(4)}"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<FlowModuleScript path={stepDetail.value.path} />
|
||||
{/if}
|
||||
{:else if stepDetail.value.type == 'forloopflow'}
|
||||
<div>
|
||||
<p class="font-medium text-secondary pb-2"> Iterator expression: </p>
|
||||
{#if stepDetail.value.iterator.type == 'static'}
|
||||
<ObjectViewer json={stepDetail.value.iterator.value} />
|
||||
{:else}
|
||||
<span class="text-xs">
|
||||
<Highlight language={typescript} code={cleanExpr(stepDetail.value.iterator.expr)} />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if stepDetail.value.type == 'branchall'}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
All branches will run, regardless of the inputs
|
||||
</p>
|
||||
{:else if stepDetail.value.type == 'branchone'}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
Only one branch will run based on a predicate
|
||||
</p>
|
||||
{:else if stepDetail.value.type == 'flow'}
|
||||
<FlowPathViewer noSide path={stepDetail.value.path} />
|
||||
{/if}
|
||||
{:else}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
Step {stepDetail} selected
|
||||
</p>
|
||||
{/if}
|
||||
<FlowGraphViewerStep {flow} {stepDetail} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -191,6 +191,25 @@
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
Only one branch will run based on a predicate
|
||||
</p>
|
||||
<div class="flex-col flex gap-2">
|
||||
<div class="flex flex-row gap-4 text-sm p-2">
|
||||
<Badge large={true} color="blue">Default branch</Badge>
|
||||
<p class="italic text-tertiary"
|
||||
>If none of the predicates' expressions evaluated in-order match, this branch is chosen</p
|
||||
>
|
||||
</div>
|
||||
{#each stepDetail.value.branches as v, i}
|
||||
<div class="flex flex-col gap-4-2 items-center">
|
||||
<div class="w-full flex gap-2 px-2 pt-4 pb-2">
|
||||
<Badge large={true} color="blue">Branch {i + 1}</Badge>
|
||||
<span>{v.summary}</span>
|
||||
</div>
|
||||
<div class="w-full border p-2">
|
||||
<HighlightCode language="frontend" code={v.expr} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if stepDetail.value.type == 'flow'}
|
||||
<FlowPathViewer noSide path={stepDetail.value.path} />
|
||||
{/if}
|
||||
|
||||
+8
-7
@@ -204,23 +204,24 @@
|
||||
<Button
|
||||
title="Delete"
|
||||
size="xs"
|
||||
color="light"
|
||||
btnClasses="!px-2 !bg-red-100 hover:!bg-red-200"
|
||||
color="red"
|
||||
variant="border"
|
||||
btnClasses="!px-2"
|
||||
aria-label="Delete"
|
||||
on:click={() => dispatch('delete')}
|
||||
>
|
||||
<Trash2 size={14} class="text-red-800" />
|
||||
</Button>
|
||||
endIcon={{ icon: Trash2 }}
|
||||
/>
|
||||
{#if inlineScript.language != 'frontend'}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
btnClasses="!px-2 !bg-surface-secondary hover:!bg-surface-hover"
|
||||
btnClasses="!px-2 !py-1 !bg-surface-secondary hover:!bg-surface-hover"
|
||||
on:click={() => {
|
||||
inlineScriptEditorDrawer?.openDrawer()
|
||||
}}
|
||||
endIcon={{ icon: Maximize2 }}
|
||||
>
|
||||
Full Editor <Maximize2 size={14} />
|
||||
Full Editor
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
|
||||
+6
-4
@@ -108,6 +108,7 @@
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: RefreshCw }}
|
||||
on:click={async () => {
|
||||
sendUserToast('Refreshing inputs')
|
||||
@@ -115,9 +116,7 @@
|
||||
$stateId = $stateId + 1
|
||||
await tick()
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="border"
|
||||
@@ -133,6 +132,7 @@
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: Eye }}
|
||||
on:click={() => {
|
||||
flowPath = runnable.path
|
||||
@@ -144,6 +144,7 @@
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: Pen }}
|
||||
endIcon={{ icon: ExternalLink }}
|
||||
target="_blank"
|
||||
@@ -152,12 +153,13 @@
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
color="light"
|
||||
startIcon={{ icon: Eye }}
|
||||
endIcon={{ icon: ExternalLink }}
|
||||
target="_blank"
|
||||
href="/flows/get/{runnable.path}?workspace={$workspaceStore}"
|
||||
>
|
||||
Details page
|
||||
Details
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
loading={runLoading}
|
||||
size="xs"
|
||||
color="dark"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1 !bg-gray-700 !text-white hover:!bg-gray-900"
|
||||
btnClasses="!px-2 !py-1"
|
||||
on:click={async () => {
|
||||
runLoading = true
|
||||
try {
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
{#if filteredItems.length == 0}
|
||||
<NoItemFound />
|
||||
{:else}
|
||||
<ul class="divide-y divide-gray-200 border rounded-md">
|
||||
<ul class="divide-y border rounded-md">
|
||||
{#each filteredItems as item (item)}
|
||||
<li class="flex flex-row w-full">
|
||||
<button
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
},
|
||||
red: {
|
||||
border:
|
||||
'border-red-600 hover:border-red-700 bg-surface hover:bg-red-100 text-red-600 hover:text-red-700 focus:ring-red-300',
|
||||
contained: 'bg-red-600 hover:bg-red-700 text-white focus:ring-red-300',
|
||||
'border-red-600/60 hover:border-red-600 bg-surface hover:bg-red-100 text-red-600 hover:text-red-700 focus:ring-red-300',
|
||||
contained: 'bg-red-600 hover:bg-red-600 text-white focus:ring-red-300',
|
||||
divider: 'divide-x divide-red-700'
|
||||
},
|
||||
green: {
|
||||
|
||||
@@ -261,6 +261,8 @@
|
||||
path
|
||||
})
|
||||
}
|
||||
|
||||
let collapseAll = false
|
||||
</script>
|
||||
|
||||
<SearchItems
|
||||
@@ -375,6 +377,21 @@
|
||||
<div class="flex w-full flex-row-reverse gap-2">
|
||||
<Toggle size="xs" bind:checked={archived} options={{ right: 'Show archived' }} />
|
||||
<Toggle size="xs" bind:checked={treeView} options={{ right: 'Tree view' }} />
|
||||
{#if treeView}
|
||||
<Button
|
||||
wrapperClasses="mb-0.5"
|
||||
size="xs"
|
||||
variant="contained"
|
||||
color="light"
|
||||
on:click={() => (collapseAll = !collapseAll)}
|
||||
>
|
||||
{#if collapseAll}
|
||||
Expand
|
||||
{:else}
|
||||
Collapse
|
||||
{/if}</Button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -393,6 +410,7 @@
|
||||
{#each groupedItems.slice(0, nbDisplayed) as item (item['folderName'] ?? 'user__' + item['username'])}
|
||||
{#if item}
|
||||
<TreeView
|
||||
{collapseAll}
|
||||
{item}
|
||||
on:scriptChanged={loadScripts}
|
||||
on:flowChanged={loadFlows}
|
||||
|
||||
@@ -10,12 +10,20 @@
|
||||
|
||||
export let item: ItemType | FolderItem | UserItem
|
||||
|
||||
export let collapseAll: boolean
|
||||
export let depth: number = 0
|
||||
|
||||
const isFolder = (i: any): i is FolderItem => i && 'folderName' in i
|
||||
const isUser = (i: any): i is UserItem => i && 'username' in i
|
||||
|
||||
let opened: boolean = true
|
||||
|
||||
$: toggleOpened(collapseAll)
|
||||
|
||||
function toggleOpened(collapseAll: boolean) {
|
||||
opened = !collapseAll
|
||||
}
|
||||
|
||||
export let showCode: (path: string, summary: string) => void
|
||||
|
||||
let showMax = 30
|
||||
@@ -61,6 +69,7 @@
|
||||
<div transition:slide>
|
||||
{#each item.items.slice(0, showMax) as subItem ((subItem['path'] ? subItem['type'] + '__' + subItem['path'] : undefined) ?? 'folder__' + subItem['folderName'])}
|
||||
<svelte:self
|
||||
{collapseAll}
|
||||
item={subItem}
|
||||
on:scriptChanged
|
||||
on:flowChanged
|
||||
@@ -121,6 +130,7 @@
|
||||
<div transition:slide>
|
||||
{#each item.items.slice(0, showMax) as subItem ((subItem['path'] ? subItem['type'] + '__' + subItem['path'] : undefined) ?? 'folder__' + subItem['folderName'])}
|
||||
<svelte:self
|
||||
{collapseAll}
|
||||
item={subItem}
|
||||
on:scriptChanged
|
||||
on:flowChanged
|
||||
|
||||
@@ -89,7 +89,9 @@
|
||||
// Unhandled errors from Monaco Editor don't logout the user
|
||||
if (
|
||||
monacoEditorUnhandledErrors.includes(message) ||
|
||||
message.startsWith('Failed to fetch dynamically imported')
|
||||
message.startsWith('Failed to fetch dynamically imported') ||
|
||||
message.startsWith('Unable to figure out browser width and height') ||
|
||||
message.startsWith('Unable to read file')
|
||||
) {
|
||||
console.warn(message)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user