mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 08:02:40 +00:00
* Remove $$props.field usage * Rename slots to ensure no hyphen * _props * _trigger * OnSelectedIteration type correct capitalization * rename _content * Remove afterUpdate * Migrate everything to svelte 5 * array bind * Fix popover * type never * nit fixes * Fixed many trivial errors * onClick * Fix errors * use let: * nit typing * fix: wrap state_referenced_locally vars with untrack() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add untrack import * Fix all syntax errors due to untrack migration * Fix undefined errors * Fix more undefined errors * untrack(() => initialOpen) * svelte-ignore * Fix state_descriptors_fixed error in Chart.svelte Use $state.snapshot() to pass plain copies of data/options to Chart.js instead of $state proxies. Chart.js's listenArrayEvents tries to define property descriptors on data arrays, which Svelte 5 proxies reject. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * nit typing * Merge issue * Fix "path is not set" error in resource picker / editor * Fix InputTransformForm error when rerunning some flows * fix npm run check --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
150 lines
4.1 KiB
Svelte
150 lines
4.1 KiB
Svelte
<script lang="ts">
|
|
import { createBubbler, stopPropagation } from 'svelte/legacy'
|
|
|
|
const bubble = createBubbler()
|
|
import Dropdown from '$lib/components/DropdownV2.svelte'
|
|
import { classNames } from '$lib/utils'
|
|
import { createEventDispatcher, getContext, untrack } from 'svelte'
|
|
import type { AppViewerContext } from '../types'
|
|
import type { DecisionTreeNode } from './component'
|
|
import { isDebugging } from './settingsPanel/decisionTree/utils'
|
|
import { X, Bug } from 'lucide-svelte'
|
|
|
|
interface Props {
|
|
nodes?: DecisionTreeNode[]
|
|
id: string
|
|
isSmall?: boolean
|
|
componentIsDebugging?: boolean
|
|
}
|
|
|
|
let { nodes = [], id, isSmall = false, componentIsDebugging = $bindable(false) }: Props = $props()
|
|
|
|
$effect(() => {
|
|
componentIsDebugging = isDebugging($debuggingComponents, id)
|
|
})
|
|
|
|
const { componentControl, debuggingComponents, worldStore } =
|
|
getContext<AppViewerContext>('AppViewerContext')
|
|
const dispatch = createEventDispatcher()
|
|
|
|
let currentNodeId: string = $state($worldStore.outputsById[untrack(() => id)]?.currentNodeId?.peak() ?? 'a')
|
|
|
|
function subscribeToCurrentNode(id: string) {
|
|
return $worldStore.outputsById[id]?.currentNodeId?.subscribe(
|
|
{
|
|
id: `id-${id}-${currentNodeId}`,
|
|
next: (value) => {
|
|
currentNodeId = value
|
|
}
|
|
},
|
|
currentNodeId
|
|
)
|
|
}
|
|
|
|
let subscription = subscribeToCurrentNode(untrack(() => id))
|
|
|
|
function onDebugNode(debuggedNodeIndex: number | undefined) {
|
|
if (debuggedNodeIndex === undefined) {
|
|
return
|
|
}
|
|
|
|
if (debuggedNodeIndex !== undefined && nodes[debuggedNodeIndex]?.id === undefined) {
|
|
currentNodeId = nodes[0]?.id ?? ''
|
|
$componentControl?.[id]?.setTab?.(0)
|
|
|
|
$debuggingComponents = Object.fromEntries(
|
|
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
|
)
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
onDebugNode($debuggingComponents[id])
|
|
})
|
|
|
|
let renderCount: number = $state(0)
|
|
let lastNodes: DecisionTreeNode[] = untrack(() => nodes)
|
|
|
|
function onNodesChange(newNodes: DecisionTreeNode[]) {
|
|
if (JSON.stringify(newNodes) !== JSON.stringify(lastNodes)) {
|
|
lastNodes = newNodes
|
|
|
|
if (subscription) {
|
|
subscription?.()
|
|
}
|
|
subscription = subscribeToCurrentNode(id)
|
|
|
|
renderCount++
|
|
}
|
|
}
|
|
|
|
$effect(() => {
|
|
onNodesChange(nodes)
|
|
})
|
|
|
|
async function getDropdownItems() {
|
|
return [
|
|
// Debug node items
|
|
...(nodes ?? []).map((node, index) => ({
|
|
displayName: `Debug node ${node.label}`,
|
|
action: () => {
|
|
$componentControl?.[id]?.setTab?.(index)
|
|
$debuggingComponents[id] = index
|
|
},
|
|
type: 'action' as const
|
|
})),
|
|
// Reset debug mode item
|
|
{
|
|
displayName: 'Reset debug mode',
|
|
action: () => {
|
|
$componentControl?.[id]?.setTab?.(0)
|
|
$debuggingComponents = Object.fromEntries(
|
|
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
|
)
|
|
},
|
|
type: 'delete' as const
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
|
|
{#key renderCount}
|
|
<Dropdown items={getDropdownItems} class="w-fit h-auto" usePointerDownOutside={true}>
|
|
{#snippet buttonReplacement()}
|
|
<button
|
|
title={'Debug tabs'}
|
|
class={classNames(
|
|
'px-1 text-2xs font-bold rounded cursor-pointer w-fit h-full',
|
|
componentIsDebugging
|
|
? ' hover:bg-red-300 hover:text-red-800'
|
|
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
|
|
)}
|
|
onclick={() => dispatch('triggerInlineEditor')}
|
|
onpointerdown={stopPropagation(bubble('pointerdown'))}
|
|
>
|
|
{#if componentIsDebugging}
|
|
<div class="flex flex-row items-center gap-2">
|
|
{`${isSmall ? '' : 'Debugging node'} ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
|
|
<!-- svelte-ignore node_invalid_placement_ssr -->
|
|
<button
|
|
onclick={() => {
|
|
$componentControl?.[id]?.setTab?.(0)
|
|
|
|
$debuggingComponents = Object.fromEntries(
|
|
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
|
)
|
|
}}
|
|
>
|
|
<X size={11} />
|
|
</button>
|
|
</div>
|
|
{:else if isSmall}
|
|
<div class="flex h-full w-fit items-center"><Bug size={11} /></div>
|
|
{:else}<div class="whitespace-nowrap h-full"
|
|
>{`Debug nodes (current node: ${currentNodeId})`}</div
|
|
>{/if}
|
|
</button>
|
|
{/snippet}
|
|
</Dropdown>
|
|
{/key}
|