diff --git a/frontend/src/lib/components/apps/components/layout/AppDecisionTree.svelte b/frontend/src/lib/components/apps/components/layout/AppDecisionTree.svelte index 0460b93ebf..b6c6db5557 100644 --- a/frontend/src/lib/components/apps/components/layout/AppDecisionTree.svelte +++ b/frontend/src/lib/components/apps/components/layout/AppDecisionTree.svelte @@ -10,6 +10,9 @@ import type { DecisionTreeNode } from '../../editor/component' import Button from '$lib/components/common/button/Button.svelte' import { ArrowLeft, ArrowRight } from 'lucide-svelte' + import { initOutput } from '../../editor/appUtils' + import Badge from '$lib/components/common/badge/Badge.svelte' + import { getFirstNode, isDebugging } from '../../editor/settingsPanel/decisionTree/utils' export let id: string export let componentContainerHeight: number @@ -17,12 +20,24 @@ export let render: boolean export let nodes: DecisionTreeNode[] - const { app, focusedGrid, selectedComponent, connectingInput, componentControl } = - getContext('AppViewerContext') + const { + app, + focusedGrid, + selectedComponent, + connectingInput, + componentControl, + worldStore, + debuggingComponents + } = getContext('AppViewerContext') let css = initCss($app.css?.conditionalwrapper, customCss) let selectedConditionIndex = 0 - let currentNodeId = nodes[0].id + let currentNodeId = getFirstNode(nodes)?.id ?? '' + + let outputs = initOutput($worldStore, id, { + currentNodeId, + currentNodeIndex: selectedConditionIndex + }) $: resolvedConditions = nodes.reduce((acc, node) => { acc[node.id] = acc[node.id] || [] @@ -35,7 +50,11 @@ }, resolvedNext || {}) $: if (!nodes.map((n) => n.id).includes(currentNodeId)) { - currentNodeId = nodes[0].id + const firstNode = getFirstNode(nodes)?.id + + if (firstNode) { + currentNodeId = firstNode + } } $: lastNodeId = nodes?.find((node) => node.next.length === 0)?.id @@ -81,15 +100,35 @@ parentComponentId: id, subGridIndex: selectedConditionIndex } + } else { + // if no history, go to first node + + const node = getFirstNode(nodes) + + if (node) { + currentNodeId = node.id + + selectedConditionIndex = nodes.findIndex((next) => next.id == currentNodeId) + + $focusedGrid = { + parentComponentId: id, + subGridIndex: selectedConditionIndex + } + } } } function onFocus(newIndex: number) { selectedConditionIndex = newIndex - currentNodeId = nodes[selectedConditionIndex].id - $focusedGrid = { - parentComponentId: id, - subGridIndex: selectedConditionIndex + + const nodeId = nodes[newIndex]?.id + + if (nodeId) { + currentNodeId = nodeId + $focusedGrid = { + parentComponentId: id, + subGridIndex: selectedConditionIndex + } } } @@ -101,6 +140,11 @@ } } + $: if (currentNodeId) { + outputs.currentNodeId.set(currentNodeId) + outputs.currentNodeIndex.set(nodes.findIndex((next) => next.id == currentNodeId)) + } + $: if ($selectedComponent?.[0] === id && !$focusedGrid) { $focusedGrid = { parentComponentId: id, @@ -169,15 +213,30 @@
- {#if nodes[0].id !== currentNodeId} - + {#if isDebugging($debuggingComponents, id)} + + {`Debugging. Actions are disabled.`} + + {/if} + {#if getFirstNode(nodes)?.id !== currentNodeId} + {/if} diff --git a/frontend/src/lib/components/apps/editor/AppEditor.svelte b/frontend/src/lib/components/apps/editor/AppEditor.svelte index a303f8f89a..30c6308339 100644 --- a/frontend/src/lib/components/apps/editor/AppEditor.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditor.svelte @@ -153,7 +153,8 @@ allIdsInPath: writable([]), darkMode, cssEditorOpen, - previewTheme + previewTheme, + debuggingComponents: writable({}) }) let scale = writable(100) diff --git a/frontend/src/lib/components/apps/editor/AppPreview.svelte b/frontend/src/lib/components/apps/editor/AppPreview.svelte index 542f60e973..e85fd68172 100644 --- a/frontend/src/lib/components/apps/editor/AppPreview.svelte +++ b/frontend/src/lib/components/apps/editor/AppPreview.svelte @@ -102,7 +102,8 @@ allIdsInPath, darkMode, cssEditorOpen: writable(false), - previewTheme: writable(undefined) + previewTheme: writable(undefined), + debuggingComponents: writable({}) }) let previousSelectedIds: string[] | undefined = undefined diff --git a/frontend/src/lib/components/apps/editor/DecisionTreeDebug.svelte b/frontend/src/lib/components/apps/editor/DecisionTreeDebug.svelte index 11ad40fe9a..42a45b53ac 100644 --- a/frontend/src/lib/components/apps/editor/DecisionTreeDebug.svelte +++ b/frontend/src/lib/components/apps/editor/DecisionTreeDebug.svelte @@ -5,22 +5,43 @@ import { createEventDispatcher, getContext } from 'svelte' import type { AppViewerContext } from '../types' import type { DecisionTreeNode } from './component' + import { isDebugging } from './settingsPanel/decisionTree/utils' + import { X } from 'lucide-svelte' export let nodes: DecisionTreeNode[] = [] export let id: string - const { componentControl } = getContext('AppViewerContext') + const { componentControl, debuggingComponents, worldStore } = + getContext('AppViewerContext') const dispatch = createEventDispatcher() - let isManuallySelected: boolean = false - let selected: number | null = null + let currentNodeId: string = '' + + $worldStore.outputsById[id]?.currentNodeId?.subscribe( + { + id: id, + next: (value) => { + currentNodeId = value + } + }, + currentNodeId + ) + + $: if (nodes[$debuggingComponents[id] ?? 0]?.id === undefined) { + currentNodeId = '' + $componentControl?.[id]?.setTab?.(0) + + $debuggingComponents = Object.fromEntries( + Object.entries($debuggingComponents).filter(([key]) => key !== id) + ) + }
{:else} - {`Debug nodes`} + {`Debug nodes (current node: ${currentNodeId})`} {/if} @@ -44,8 +76,8 @@ { $componentControl?.[id]?.setTab?.(index) - selected = index - isManuallySelected = true + + $debuggingComponents[id] = index }} >
{ - $componentControl?.[id]?.setTab?.(-1) - isManuallySelected = false + $componentControl?.[id]?.setTab?.(0) + + $debuggingComponents = Object.fromEntries( + Object.entries($debuggingComponents).filter(([key]) => key !== id) + ) }} >
('AppViewerContext') const name = getComponentNameById(gridItem.id) - const nameOverrides = + $: nameOverrides = gridItem.data.type === 'decisiontreecomponent' - ? gridItem.data.nodes.map((n) => n.label) + ? gridItem.data.nodes.map((n, i) => `${n.label} (Tab index ${i})`) : undefined function getComponentNameById(componentId: string) { diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte index 97fff97c02..b827571f2c 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/DecisionTreeGraphNode.svelte @@ -49,7 +49,7 @@ {node.label === '' ? `Tab: ${node.id}` : node.label}
- {index} + Tab index: {index} You can manually select a node using the setTab function with this index in a frontend runnable. diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte index 8a193cf2c7..edba908d39 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/DecisionTreePreview.svelte @@ -15,6 +15,7 @@ addNewBranch, addNode, findCollapseNode, + getFirstNode, getParents, insertNode, removeBranch, @@ -52,6 +53,12 @@ } function buildStartNode() { + const firstNode = getFirstNode(nodes) + + if (!firstNode) { + return + } + const startNodeConfig = { id: 'start', data: { @@ -62,7 +69,7 @@ id: 'start', label: 'Start', next: { - id: nodes[0].id, + id: firstNode.id, condition: { type: 'evalv2', expr: 'true', @@ -80,15 +87,16 @@ const startNode = createNode(startNodeConfig) displayedNodes.push(startNode) + edges.push( createEdge({ - id: `start-${nodes[0].id}`, + id: `start-${firstNode?.id}`, source: 'start', - target: nodes[0].id + target: firstNode?.id }) ) } - const { app, runnableComponents, componentControl } = + const { app, runnableComponents, componentControl, debuggingComponents } = getContext('AppViewerContext') function buildEndNode() { @@ -183,16 +191,18 @@ $selectedNodeId = detail const index = nodes.findIndex((node) => node.id === detail) $componentControl?.[component.id]?.setTab?.(index) + $debuggingComponents[component.id] = index break case 'nodeInsert': { addSubGrid() + if (branchInsert) { if (parentIds.length === 1 && graphNode) { - console.log('A', parentIds) + // console.log('A', parentIds) nodes = insertNode(nodes, parentIds[0], graphNode) } else { - console.log('B', parentIds) + // console.log('B', parentIds) // find parent with multiple next const parentWithMultipleNext = nodes.find((node) => { return node.next.length > 1 && parentIds.includes(node.id) @@ -214,9 +224,11 @@ case 'delete': { const graphhNodeIndex = nodes.findIndex((node) => node.id == graphNode?.id) + if (graphhNodeIndex > -1) { deleteSubgrid(graphhNodeIndex) } + nodes = removeNode(nodes, graphNode) break } @@ -228,6 +240,7 @@ case 'removeBranch': { nodes = removeBranch(nodes, graphNode, parentIds[0], (nodeId) => { const index = nodes.findIndex((node) => node.id === nodeId) + deleteSubgrid(index) }) break @@ -449,7 +462,7 @@ }) $: if (nodes.length > 0 && !$selectedNodeId) { - $selectedNodeId = nodes[0].id + $selectedNodeId = getFirstNode(nodes)?.id } diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/utils.ts b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/utils.ts index 2312df5198..d05a86d41e 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/utils.ts +++ b/frontend/src/lib/components/apps/editor/settingsPanel/decisionTree/utils.ts @@ -14,19 +14,18 @@ function createBooleanRC(): RichConfiguration { export function addNode(nodes: DecisionTreeNode[], sourceNode: DecisionTreeNode | undefined) { const nextId = getNextId(nodes.map((node) => node.id)) - const newNode: DecisionTreeNode = { - id: nextId, - label: nextId, - next: sourceNode ? sourceNode.next : [{ id: nodes[0].id }], - allowed: createBooleanRC() - } - if (sourceNode) { - let index = nodes.findIndex((n) => n.id === sourceNode.id) - nodes = [...nodes.slice(0, index), newNode, ...nodes.slice(index)] + const newNode: DecisionTreeNode = { + id: nextId, + label: nextId, + next: sourceNode.next, + allowed: createBooleanRC() + } + + nodes.push(newNode) nodes = nodes.map((node) => { - if (node.id === sourceNode.id) { + if (node.id === sourceNode?.id) { node.next = [ { id: newNode.id, @@ -36,10 +35,23 @@ export function addNode(nodes: DecisionTreeNode[], sourceNode: DecisionTreeNode } return node }) + + return nodes } else { - nodes = [newNode, ...nodes] + const firstNode = getFirstNode(nodes) + + if (firstNode) { + const newNode: DecisionTreeNode = { + id: nextId, + label: nextId, + next: [{ id: firstNode.id, condition: createBooleanRC() }], + allowed: createBooleanRC() + } + + nodes.push(newNode) + } + return nodes } - return nodes } export function insertNode( @@ -49,7 +61,6 @@ export function insertNode( ) { const nextId = getNextId(nodes.map((node) => node.id)) - console.log('insertNode', parentId, sourceNode) const newNode: DecisionTreeNode = { id: nextId, label: nextId, @@ -273,7 +284,11 @@ export function getParents(nodes: DecisionTreeNode[], nodeId: string): string[] return parentIds } -function getFirstNode(nodes: DecisionTreeNode[]): DecisionTreeNode | undefined { +export function getFirstNode(nodes: DecisionTreeNode[]): DecisionTreeNode | undefined { // No other nodes has this node as next return nodes.find((node) => !nodes.some((n) => n.next.some((next) => next.id === node.id))) } + +export function isDebugging(debuggingComponents: Record, id: string): boolean { + return Object.keys(debuggingComponents).includes(id) +} diff --git a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/TabSelectInput.svelte b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/TabSelectInput.svelte index f01ea27ed1..e96f8f1d8b 100644 --- a/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/TabSelectInput.svelte +++ b/frontend/src/lib/components/apps/editor/settingsPanel/inputEditor/TabSelectInput.svelte @@ -10,7 +10,9 @@ const tabComponents = allItems($app.grid, $app.subgrids).filter( (component) => - component.data.type === 'tabscomponent' || component.data.type === 'conditionalwrapper' + component.data.type === 'tabscomponent' || + component.data.type === 'conditionalwrapper' || + component.data.type === 'decisiontreecomponent' ) diff --git a/frontend/src/lib/components/apps/types.ts b/frontend/src/lib/components/apps/types.ts index c9f802aba8..9601fd2adc 100644 --- a/frontend/src/lib/components/apps/types.ts +++ b/frontend/src/lib/components/apps/types.ts @@ -260,6 +260,7 @@ export type AppViewerContext = { darkMode: Writable cssEditorOpen: Writable previewTheme: Writable + debuggingComponents: Writable> } export type AppEditorContext = {