UX improvements

This commit is contained in:
Ruben Fiszel
2023-11-15 08:59:43 +01:00
parent af25b22143
commit 3efbef03b8
11 changed files with 79 additions and 207 deletions
+6 -6
View File
@@ -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}
@@ -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&nbsp;<Maximize2 size={14} />
Full Editor
</Button>
{/if}
@@ -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 {
@@ -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
+3 -1
View File
@@ -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