fix: improve flow editor step switch performance

* replace on click wt on pointer down on flow node click

* pointerdown on virtualitems

* Load monaco async with a placeholder to avoid size flash

* monaco placeholder for editor

* less flashing

* simulate first line bg

* better match to monaco

* more fine tune

* fix for increased browser font sizes

* flow nodes feel much better to click on

* move setTimeout upwards

* only load async in flow editor

* load async monaco in app

* lots of components dont respect the type and pass undefined

* weird outline when opening and closing OutputPicker

* fixed hover flow nodes

* moved setTimeout upwards

* hover color for virtual items

* wrong Cargo.lock

* disable interaction if not selectable

* pixel perfect editor placeholder

* fake monaco editor perfect in flow editor

* fake monaco for app json editor

* (temp) never load editor monaco

* os dependant constants in monaco

* Revert " (temp) never load editor monaco"

This reverts commit c20fca134d.

---------

Co-authored-by: Diego Imbert <diego@windmill.dev>
This commit is contained in:
Diego Imbert
2025-04-17 15:41:41 +02:00
committed by GitHub
co-authored by Diego Imbert
parent 34b307b2be
commit 58fa4c8006
32 changed files with 318 additions and 64 deletions
+14 -2
View File
@@ -175,6 +175,7 @@
import GlobalReviewButtons from './copilot/chat/GlobalReviewButtons.svelte'
import { writable } from 'svelte/store'
import { formatResourceTypes } from './copilot/chat/core'
import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte'
// import EditorTheme from './EditorTheme.svelte'
let divEl: HTMLDivElement | null = null
@@ -221,6 +222,7 @@
export let disabled: boolean = false
export let lineNumbersMinChars = 3
export let isAiPanelOpen: boolean = false
export let loadAsync = false
const rHash = randomHash()
$: filePath = computePath(path)
@@ -1374,9 +1376,14 @@
})
}
onMount(() => {
onMount(async () => {
if (BROWSER) {
loadMonaco().then((x) => (disposeMethod = x))
if (loadAsync) {
setTimeout(() => loadMonaco().then((x) => (disposeMethod = x)), 0)
} else {
let m = await loadMonaco()
disposeMethod = m
}
}
})
@@ -1408,6 +1415,11 @@
</script>
<EditorTheme />
{#if !editor}
<div class="inset-0 absolute overflow-clip">
<FakeMonacoPlaceHolder {code} />
</div>
{/if}
<div bind:this={divEl} class="{$$props.class} editor {disabled ? 'disabled' : ''}"></div>
{#if $vimMode}
<div class="fixed bottom-0 z-30" bind:this={statusDiv}></div>
@@ -0,0 +1,112 @@
<!-- Used to avoid height jitter when loading monaco asynchronously -->
<script lang="ts">
import { getOS } from '$lib/utils'
type Props = {
code?: string
autoheight?: boolean
lineNumbersWidth?: number
lineNumbersOffset?: number
class?: string
fontSize?: number
}
let {
code,
autoheight = false,
lineNumbersWidth = 51,
lineNumbersOffset = 0,
class: className = '',
fontSize = 14
}: Props = $props()
// https://github.com/microsoft/vscode/blob/baa2dad3cdacd97ac02eff0604984faf1167ff1e/src/vs/editor/common/config/editorOptions.ts#L5421
const DEFAULT_WINDOWS_FONT_FAMILY = "Consolas, 'Courier New', monospace"
const DEFAULT_MAC_FONT_FAMILY = "Menlo, Monaco, 'Courier New', monospace"
const DEFAULT_LINUX_FONT_FAMILY = "'Droid Sans Mono', 'monospace', monospace"
const fontFamily =
getOS() === 'Windows'
? DEFAULT_WINDOWS_FONT_FAMILY
: getOS() === 'macOS'
? DEFAULT_MAC_FONT_FAMILY
: DEFAULT_LINUX_FONT_FAMILY
// https://github.com/microsoft/vscode/blob/baa2dad3cdacd97ac02eff0604984faf1167ff1e/src/vs/editor/common/config/fontInfo.ts#L14
const GOLDEN_LINE_HEIGHT_RATIO = getOS() == 'macOS' ? 1.5 : 1.35
let lines = $derived(code?.split('\n') ?? [])
const charWidth = 9 // try to match as closely as possible to monaco editor
const lineHeight = fontSize * GOLDEN_LINE_HEIGHT_RATIO
let [clientWidth, clientHeight] = $state([0, 0])
let showHorizontalScrollbar = $derived(
lines.some((line) => line.length * charWidth > clientWidth - 40)
)
let [editorWidth, editorHeight] = $derived([
clientWidth,
autoheight ? lines.length * lineHeight + (showHorizontalScrollbar ? 12 : 0) : clientHeight
])
</script>
<!-- Copy pasted from actual monaco editor in the web inspector -->
<div
bind:clientWidth
bind:clientHeight
class="h-full w-full relative editor dark:bg-[#272D38] {className}"
style="--vscode-editorCodeLens-lineHeight: 18px; --vscode-editorCodeLens-fontSize: 12px; --vscode-editorCodeLens-fontFeatureSettings: 'liga' off, 'calt' off; --code-editorInlayHintsFontFamily: {fontFamily};"
>
<div
class="monaco-editor no-user-select mac standalone showUnused showDeprecated vs-dark"
role="code"
>
<div class="overflow-guard" style="width: {editorWidth}px; height: {editorHeight}px;">
<div
class="margin"
role="presentation"
aria-hidden="true"
style="position: absolute; transform: translate3d(0px, 0px, 0px); contain: strict; top: 0px; width: {editorWidth}px; height: {editorHeight}px;"
>
<div
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;"
>
{#each lines as _, i}
<div style="top:{lineHeight * i}px;height:{lineHeight}px;">
<div class="line-numbers" style="left:{lineNumbersOffset}px;width:25px;">{i + 1}</div>
</div>
{/each}
</div>
</div>
<div
class="monaco-scrollable-element editor-scrollable vs-dark mac"
style="position: absolute; overflow: hidden; left: {lineNumbersWidth}px; width: {editorWidth}px; height: {editorHeight}px"
>
<div
class="lines-content monaco-editor-background"
style="position: absolute; overflow: hidden; width: 1.67772e+07px; height: 1.67772e+07px; transform: translate3d(0px, 0px, 0px); contain: strict; top: 0px; left: 0px;"
>
<div
class="view-lines monaco-mouse-cursor-text text-tertiary/60"
style="line-height: {lineHeight}px; 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: 1143px; height: 789px;"
>
{#each lines as line, i}
<div
style="height: {lineHeight}px; top: {i * lineHeight}px;"
class="text-nowrap whitespace-pre"
>
{line}
</div>
{/each}
</div>
</div>
</div>
</div>
</div>
</div>
@@ -515,6 +515,7 @@
on:change={() => {
dispatch('change', { argName, arg })
}}
loadAsync
/>
{/if}
</div>
@@ -583,6 +584,7 @@
focused = false
}}
autoHeight
loadAsync
/>
</div>
{#if !hideHelpButton}
@@ -9,6 +9,7 @@
export let error = ''
export let editor: SimpleEditor | undefined = undefined
export let small = false
export let loadAsync = false
$: tooBig = code && code?.length > 1000000
@@ -38,6 +39,7 @@
<div class="flex flex-col w-full">
<div class="border w-full">
<SimpleEditor
{loadAsync}
{small}
on:focus
on:blur
@@ -135,7 +135,7 @@
}}
/>
<div class="flex flex-col w-full items-start">
<div class="flex flex-col w-full items-start min-h-9">
<div class="flex flex-row w-full items-center">
{#if collection?.length > 0}
<Select
@@ -65,6 +65,7 @@
import { vimMode } from '$lib/stores'
import { initVim } from './monaco_keybindings'
import { buildWorkerDefinition } from '$lib/monaco_workers/build_workers'
import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte'
// import { createConfiguredEditor } from 'vscode/monaco'
// import type { IStandaloneCodeEditor } from 'vscode/vscode/vs/editor/standalone/browser/standaloneCodeEditor'
@@ -99,7 +100,8 @@
autofocus = false,
allowVim = false,
tailwindClasses = [],
class: className = ''
class: className = '',
loadAsync = false
} = $props<{
lang: string
code?: string
@@ -121,6 +123,7 @@
allowVim?: boolean
tailwindClasses?: string[]
class?: string
loadAsync?: boolean
}>()
const dispatch = createEventDispatcher()
@@ -279,6 +282,8 @@
}
})
let fontSize = $derived(small ? 12 : 14)
async function loadMonaco() {
await initializeVscode()
initialized = true
@@ -327,7 +332,7 @@
model,
lineDecorationsWidth: 6,
lineNumbersMinChars: 2,
fontSize: small ? 12 : 14,
fontSize: fontSize,
quickSuggestions: disableSuggestions
? { other: false, comments: false, strings: false }
: { other: true, comments: true, strings: true },
@@ -511,12 +516,16 @@
onMount(async () => {
if (BROWSER) {
mounted = true
await loadMonaco()
if (autofocus) {
setTimeout(() => {
focus()
if (loadAsync) {
setTimeout(async () => {
await loadMonaco()
mounted = true
if (autofocus) setTimeout(() => focus(), 0)
}, 0)
} else {
await loadMonaco()
mounted = true
if (autofocus) setTimeout(() => focus(), 0)
}
}
})
@@ -556,9 +565,22 @@
{suggestion}
</div>
{/if}
{#if !editor}
<FakeMonacoPlaceHolder
{code}
autoheight
lineNumbersWidth={(23 * fontSize) / 14}
lineNumbersOffset={fontSize == 14 ? -8 : -11}
{fontSize}
/>
{/if}
<div
bind:this={divEl}
class="relative {className} editor simple-editor {!allowVim ? 'nonmain-editor' : ''}"
class="relative {className} {!editor ? 'hidden' : ''} editor simple-editor {!allowVim
? 'nonmain-editor'
: ''}"
bind:clientWidth={width}
>
{#if placeholder}
@@ -19,6 +19,7 @@
import { initializeVscode } from './vscode'
import EditorTheme from './EditorTheme.svelte'
import { buildWorkerDefinition } from '$lib/monaco_workers/build_workers'
import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte'
export const conf = {
wordPattern:
@@ -374,6 +375,7 @@
export let autoHeight = true
export let fixedOverflowWidgets = true
export let fontSize = 16
export let loadAsync = false
if (typeof code != 'string') {
code = ''
@@ -594,8 +596,15 @@
let mounted = false
onMount(async () => {
if (BROWSER) {
await loadMonaco()
mounted = true
if (loadAsync) {
setTimeout(async () => {
await loadMonaco()
mounted = true
}, 0)
} else {
await loadMonaco()
mounted = true
}
}
})
@@ -626,10 +635,22 @@
<EditorTheme />
{#if !editor}
<FakeMonacoPlaceHolder
autoheight
{code}
lineNumbersWidth={23}
lineNumbersOffset={-8}
class="border template nonmain-editor rounded min-h-4 mx-0.5 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"
class="{$$props.class ??
''} border template nonmain-editor rounded min-h-4 mx-0.5 overflow-clip {!editor
? 'hidden'
: ''}"
bind:clientWidth={width}
></div>
@@ -73,10 +73,11 @@
<Splitpanes horizontal class="h-full">
<Pane size={50}>
<SimpleEditor
loadAsync
class="h-full w-full"
bind:this={editor}
lang="javascript"
bind:code={componentInput.expr}
bind:code={() => componentInput.expr ?? '', (e) => (componentInput.expr = e)}
shouldBindKey={false}
fixedOverflowWidgets={false}
{extraLib}
@@ -105,10 +106,11 @@
<div class="border relative">
{#if !fullscreen}
<SimpleEditor
loadAsync
small
bind:this={editor}
lang="javascript"
bind:code={componentInput.expr}
bind:code={() => componentInput.expr ?? '', (e) => (componentInput.expr = e)}
shouldBindKey={false}
{extraLib}
autoHeight
@@ -235,6 +235,7 @@
{:else}
<div class="flex w-full flex-col">
<JsonEditor
loadAsync
small
bind:value={componentInput.value}
code={JSON.stringify(componentInput.value, null, 2)}
@@ -358,11 +358,12 @@
<div class="min-h-0 flex-grow" id="flow-editor-editor">
<Splitpanes horizontal>
<Pane bind:size={editorPanelSize} minSize={10}>
<Pane bind:size={editorPanelSize} minSize={10} class="relative">
{#if flowModule.value.type === 'rawscript'}
{#if !noEditor}
{#key flowModule.id}
<Editor
loadAsync
folding
path={$pathStore + '/' + flowModule.id}
bind:websocketAlive
@@ -8,7 +8,10 @@
$: settingsClass = classNames(
'border w-full rounded-sm p-2 bg-surface text-sm cursor-pointer flex items-center',
$selectedId == 'constants' ? 'border border-1 border-slate-800 dark:bg-white/5 dark:border-slate-400/60 dark:border-gray-400' : ''
$selectedId == 'constants'
? 'border border-1 border-slate-800 dark:bg-white/5 dark:border-slate-400/60 dark:border-gray-400'
: '',
'hover:!bg-surface-secondary active:!bg-surface'
)
</script>
@@ -53,6 +53,7 @@
export let path: string = ''
export let modType: string | undefined = undefined
export let bgColor: string = ''
export let bgHoverColor: string = ''
export let concurrency: boolean = false
export let retries: number | undefined = undefined
export let warningMessage: string | undefined = undefined
@@ -195,15 +196,17 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class={classNames(
'w-full module flex rounded-sm cursor-pointer max-w-full',
selected ? 'outline outline-offset-0 outline-2 outline-slate-500 dark:outline-gray-400' : '',
'w-full module flex rounded-sm cursor-pointer max-w-full outline-offset-0 outline-slate-500 dark:outline-gray-400',
selected ? 'outline outline-2' : 'active:outline active:outline-2',
'flex relative',
$copilotCurrentStepStore === id ? 'z-[901]' : ''
)}
style="width: 275px; height: 38px; background-color: {bgColor};"
style="width: 275px; height: 38px; background-color: {hover && bgHoverColor
? bgHoverColor
: bgColor};"
on:mouseenter={() => (hover = true)}
on:mouseleave={() => (hover = false)}
on:pointerdown|preventDefault|stopPropagation
on:pointerdown|preventDefault|stopPropagation={() => dispatch('pointerdown')}
>
<div class="absolute text-sm right-12 -bottom-3 flex flex-row gap-1 z-10">
{#if retry}
@@ -11,7 +11,8 @@
'border w-full rounded-sm p-2 bg-surface text-sm cursor-pointer flex items-center h-[32px]',
$selectedId?.startsWith('settings')
? 'border border-1 border-slate-800 dark:bg-white/5 dark:border-slate-400/60 dark:border-gray-400'
: ''
: '',
'hover:!bg-surface-secondary active:!bg-surface'
)
</script>
@@ -19,6 +19,7 @@
export let insertable: boolean
export let annotation: string | undefined = undefined
export let bgColor: string = ''
export let bgHoverColor: string = ''
export let moving: string | undefined = undefined
export let duration_ms: number | undefined = undefined
@@ -129,6 +130,7 @@
}}
{...itemProps}
{bgColor}
{bgHoverColor}
warningMessage={mod?.value?.type === 'forloopflow' &&
mod?.value?.iterator?.type === 'javascript' &&
mod?.value?.iterator?.expr === ''
@@ -153,6 +155,7 @@
id={mod.id}
label={mod.summary || 'Run one branch'}
{bgColor}
{bgHoverColor}
>
<div slot="icon">
<GitBranch size={16} />
@@ -170,6 +173,7 @@
{...itemProps}
label={mod.summary || `Run all branches${mod.value.parallel ? ' (parallel)' : ''}`}
{bgColor}
{bgHoverColor}
>
<div slot="icon">
<GitBranch size={16} />
@@ -192,6 +196,7 @@
{...itemProps}
modType={mod.value.type}
{bgColor}
{bgHoverColor}
label={mod.summary ||
(mod.id === 'preprocessor'
? 'Preprocessor'
@@ -13,6 +13,7 @@
export let label: string | undefined = undefined
export let bgColor: string = ''
export let bgHoverColor: string = ''
export let selected: boolean
export let selectable: boolean
export let id: string | undefined = undefined
@@ -36,6 +37,7 @@
<VirtualItemWrapper
{label}
{bgColor}
{bgHoverColor}
{selected}
{selectable}
{id}
@@ -9,6 +9,7 @@
export let id: string | undefined
export let onTop: boolean = false
export let bgColor: string
export let bgHoverColor: string = ''
const dispatch = createEventDispatcher<{
insert: {
@@ -28,18 +29,17 @@
<div
class={classNames(
'w-full flex relative rounded-sm',
selectable ? 'cursor-pointer' : '',
selected ? 'outline outline-offset-1 outline-2 outline-gray-600 dark:outline-gray-400' : '',
onTop ? 'z-[901]' : ''
selectable ? 'cursor-pointer active:outline active:outline-2' : '',
selected ? 'outline outline-2' : '',
onTop ? 'z-[901]' : '',
'outline-offset-1 outline-gray-600 dark:outline-gray-400'
)}
style="width: 275px; max-height: 38px; background-color: {bgColor} !important;"
style="width: 275px; max-height: 38px; background-color: {hover && bgHoverColor && selectable
? bgHoverColor
: bgColor};"
on:pointerdown={() => {
if (selectable) {
if (id) {
dispatch('select', id)
} else {
dispatch('select', label || label || '')
}
dispatch('select', id || label || '')
}
}}
on:mouseenter={() => {
@@ -80,6 +80,7 @@
fullScreenWidthOffset={2 * width}
extraProps={{ 'data-prop-picker': true }}
closeOnOtherPopoverOpen
class="outline-none"
>
<svelte:fragment slot="trigger" let:isOpen>
<div
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import { X } from 'lucide-svelte'
import type { GraphModuleState } from '../../model'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
import { computeBorderStatus } from '../utils'
@@ -33,14 +33,15 @@
selectable
selected={data.selected}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={borderStatus
? getStateColor(borderStatus, darkMode) + (!darkMode ? '; border-width: 3px' : '')
: undefined}
on:select={() => {
data.eventHandlers.select(data.id)
setTimeout(() => data.eventHandlers.select(data.id))
}}
on:insert={(e) => {
data.eventHandlers.insert(e.detail)
setTimeout(() => data.eventHandlers.insert(e.detail))
}}
/>
{#if data.insertable}
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import { X } from 'lucide-svelte'
import type { GraphModuleState } from '../../model'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
import { computeBorderStatus } from '../utils'
@@ -26,7 +26,6 @@
'branchone',
data.flowModuleStates?.[data.id]
)
</script>
<NodeWrapper let:darkMode offset={data.offset}>
@@ -36,9 +35,10 @@
selectable
selected={data.selected}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={borderStatus ? getStateColor(borderStatus, darkMode) : undefined}
on:select={() => {
data.eventHandlers.select(data.id)
setTimeout(() => data?.eventHandlers?.select(data.id))
}}
/>
{#if data.insertable}
@@ -26,7 +26,7 @@
bgColor={getStateColor(undefined, darkMode)}
borderColor={getStateColor(data.flowModuleStates?.[data.id]?.type, darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
/>
{:else}
@@ -38,7 +38,7 @@
bgColor={getStateColor(undefined, darkMode)}
borderColor={getStateColor(data.flowModuleStates?.[data.id]?.type, darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
/>
{/if}
@@ -2,7 +2,7 @@
import VirtualItem from '$lib/components/flows/map/VirtualItem.svelte'
import NodeWrapper from './NodeWrapper.svelte'
import type { FlowModule, FlowStatusModule } from '$lib/gen'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { GraphModuleState } from '../../model'
import type { GraphEventHandlers } from '../../graphBuilder'
import { getContext } from 'svelte'
@@ -38,9 +38,11 @@
return {}
}
function computeStatus(state: GraphModuleState | undefined): FlowStatusModule["type"] | undefined {
function computeStatus(
state: GraphModuleState | undefined
): FlowStatusModule['type'] | undefined {
if (state?.type == 'InProgress' || state?.type == 'Success' || state?.type == 'Failure') {
let r = state?.flow_jobs_success?.[state?.selectedForloopIndex ?? 0]
let r = state?.flow_jobs_success?.[state?.selectedForloopIndex ?? 0]
if (r == undefined) return 'InProgress'
return r ? 'Success' : 'InProgress'
}
@@ -77,9 +79,10 @@
id={data.id}
hideId
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={getStateColor(computeStatus(data.flowModuleStates?.[data.id]), darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
inputJson={filteredInput}
prefix="flow_input"
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import type { GraphEventHandlers } from '../../graphBuilder'
import type { FlowModule } from '$lib/gen'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import { getContext } from 'svelte'
import type { Writable } from 'svelte/store'
import InsertModuleButton from '$lib/components/flows/map/InsertModuleButton.svelte'
@@ -73,11 +73,12 @@
selectable
selected={$selectedId === 'Input'}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
on:insert={(e) => {
data.eventHandlers?.insert(e.detail)
setTimeout(() => data?.eventHandlers?.insert(e.detail))
}}
on:select={(e) => {
data.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
inputJson={topFlowInput}
prefix="flow_input"
@@ -5,7 +5,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import type { GraphEventHandlers } from '../../graphBuilder'
import type { GraphModuleState } from '../../model'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
export let data: {
offset: number
@@ -41,7 +41,7 @@
selected: state?.selectedForloopIndex ?? 0,
selectedManually: state?.selectedForLoopSetManually,
flowJobsSuccess: state?.flow_jobs_success
}
}
: (undefined as any)
</script>
@@ -66,13 +66,14 @@
annotation={flowJobs &&
(data.module.value.type === 'forloopflow' || data.module.value.type === 'whileloopflow')
? 'Iteration: ' +
((state?.selectedForloopIndex ?? 0) >= 0
((state?.selectedForloopIndex ?? 0) >= 0
? (state?.selectedForloopIndex ?? 0) + 1
: state?.flow_jobs?.length) +
'/' +
(state?.iteration_total ?? '?')
'/' +
(state?.iteration_total ?? '?')
: ''}
bgColor={getStateColor(type, darkMode, true, state?.skipped)}
bgHoverColor={getStateHoverColor(type, darkMode, true, state?.skipped)}
moving={data.moving}
duration_ms={state?.duration_ms}
retries={data.retries}
@@ -93,7 +94,7 @@
data.eventHandlers.newBranch(data.module)
}}
on:select={(e) => {
data.eventHandlers.select(e.detail)
setTimeout(() => data.eventHandlers.select(e.detail))
}}
on:selectedIteration={(e) => {
data.eventHandlers.selectedIteration(e.detail, data.module.id)
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { GraphModuleState } from '../../model'
import { computeBorderStatus } from '../utils'
@@ -31,9 +31,10 @@
selectable={true}
selected={false}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={getStateColor(borderStatus, darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
/>
</NodeWrapper>
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { Writable } from 'svelte/store'
import { getContext } from 'svelte'
@@ -29,8 +29,12 @@
data.success == undefined ? undefined : data.success ? 'Success' : 'Failure',
darkMode
)}
bgHoverColor={getStateHoverColor(
data.success == undefined ? undefined : data.success ? 'Success' : 'Failure',
darkMode
)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
/>
</NodeWrapper>
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import { Minimize2 } from 'lucide-svelte'
import type { GraphModuleState } from '../../model'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
@@ -28,9 +28,10 @@
selectable
selected={data.selected}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={undefined}
on:select={() => {
data.eventHandlers.select(data.id)
setTimeout(() => data.eventHandlers?.select(data.id))
}}
/>
<button
@@ -6,7 +6,7 @@
import { getContext } from 'svelte'
import type { Writable } from 'svelte/store'
import { Maximize2, Minimize2, Calendar } from 'lucide-svelte'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import { setScheduledPollSchedule, type TriggerContext } from '$lib/components/triggers'
import VirtualItemWrapper from '$lib/components/flows/map/VirtualItemWrapper.svelte'
@@ -37,6 +37,7 @@
isEditor={data.isEditor}
path={data.path}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
on:new={(e) => {
data?.eventHandlers.insert({
modules: data.modules,
@@ -3,7 +3,7 @@
import NodeWrapper from './NodeWrapper.svelte'
import type { FlowModule } from '$lib/gen'
import type { GraphEventHandlers } from '../../graphBuilder'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { GraphModuleState } from '../../model'
export let data: {
@@ -23,6 +23,7 @@
selectable={true}
selected={false}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={getStateColor(data?.flowModuleStates?.[data?.id]?.type, darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
@@ -2,7 +2,7 @@
import VirtualItem from '$lib/components/flows/map/VirtualItem.svelte'
import NodeWrapper from './NodeWrapper.svelte'
import type { GraphEventHandlers } from '../../graphBuilder'
import { getStateColor } from '../../util'
import { getStateColor, getStateHoverColor } from '../../util'
import type { GraphModuleState } from '../../model'
export let data: {
@@ -21,9 +21,10 @@
selectable={true}
selected={false}
bgColor={getStateColor(undefined, darkMode)}
bgHoverColor={getStateHoverColor(undefined, darkMode)}
borderColor={getStateColor(data?.flowModuleStates?.[data?.id]?.type, darkMode)}
on:select={(e) => {
data?.eventHandlers?.select(e.detail)
setTimeout(() => data?.eventHandlers?.select(e.detail))
}}
/>
</NodeWrapper>
@@ -15,19 +15,24 @@
export let disableAi: boolean = false
export let modules: FlowModule[] = []
export let bgColor: string
export let bgHoverColor: string = ''
const dispatch = createEventDispatcher()
let hover = false
</script>
<div style={`width: ${NODE.width}px;`}>
<button
style="background-color: {bgColor} !important;"
style="background-color: {hover && bgHoverColor ? bgHoverColor : bgColor};"
class="flex w-full flex-row gap-1 px-2 p-1 items-center {selected
? 'outline outline-2 outline-gray-600 rounded-sm dark:bg-white/5 dark:outline-gray-400'
: ''}"
on:pointerdown={() => {
dispatch('select')
}}
on:mouseenter={() => (hover = true)}
on:mouseleave={() => (hover = false)}
>
<div class="flex flex-col mr-1 ml-1">
<div class="flex flex-row items-center text-2xs font-normal"> Triggers </div>
+23
View File
@@ -38,3 +38,26 @@ export function getStateColor(
}
}
}
export function getStateHoverColor(
state: FlowStatusModule['type'] | undefined,
isDark: boolean,
nonVirtualItem?: boolean,
isSkipped?: boolean
): string {
switch (state) {
case 'Success':
case 'Failure':
case 'InProgress':
case 'WaitingForEvents':
case 'WaitingForExecutor':
return getStateColor(state, isDark, nonVirtualItem, isSkipped)
default:
if (nonVirtualItem) {
return isDark ? '#343B46' : '#f6f6f6'
} else {
return isDark ? '#343B46' : '#d5dee8'
}
}
}
+21
View File
@@ -1226,3 +1226,24 @@ export function formatDateShort(dateString: string | undefined): string {
day: 'numeric'
}).format(date)
}
export function getOS() {
const userAgent = window.navigator.userAgent
const platform = window.navigator.platform
const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K']
const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE']
const iosPlatforms = ['iPhone', 'iPad', 'iPod']
if (macosPlatforms.includes(platform)) {
return 'macOS' as const
} else if (iosPlatforms.includes(platform)) {
return 'iOS' as const
} else if (windowsPlatforms.includes(platform)) {
return 'Windows' as const
} else if (/Android/.test(userAgent)) {
return 'Android' as const
} else if (/Linux/.test(platform)) {
return 'Linux' as const
}
return 'Unknown OS' as const
}