Fix dangling node connection (#8902)

Fixes #8871

The issue was caused by invalid port registration. Because of the existing context switch expression (which is not visible in GUI),  the port incorrectly considered itself to belong to another node. This happened because the port was only aware of the visible part of the node’s AST and considered it the whole node.

There are two fixes in this PR. Either of them fixes the issue, and they are both implemented for robustness:
1. We provide `nodeId` information to the widget tree, so it no longer assumes the node ID from AST.
2. The order of checks in `getPortNodeId` is swapped. Now we first search by AST, only then try to look up the port. It makes sense to me because the AST is a single root of truth, and we should only rely on registered ports if AST does not exist (which happens for unconnected ports).
This commit is contained in:
Ilya Bogdanov 2024-02-05 13:49:55 +04:00 committed by GitHub
parent 2028d6b2c1
commit 801cc7b37d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 7 additions and 8 deletions

View File

@ -388,7 +388,7 @@ function portGroupStyle(port: PortData) {
<div class="node" @pointerdown="handleNodeClick" v-on="dragPointer.events">
<SvgIcon class="icon grab-handle" :name="icon"></SvgIcon>
<div ref="contentNode" class="widget-tree">
<NodeWidgetTree :ast="displayedExpression" />
<NodeWidgetTree :ast="displayedExpression" :nodeId="nodeId" />
</div>
</div>
<GraphNodeError v-if="error" class="error" :error="error" />

View File

@ -3,11 +3,11 @@ import NodeWidget from '@/components/GraphEditor/NodeWidget.vue'
import { useTransitioning } from '@/composables/animation'
import { WidgetInput, type WidgetUpdate } from '@/providers/widgetRegistry'
import { provideWidgetTree } from '@/providers/widgetTree'
import { useGraphStore } from '@/stores/graph'
import { useGraphStore, type NodeId } from '@/stores/graph'
import { Ast } from '@/util/ast'
import { computed, toRef } from 'vue'
const props = defineProps<{ ast: Ast.Ast }>()
const props = defineProps<{ ast: Ast.Ast; nodeId: NodeId }>()
const graph = useGraphStore()
const rootPort = computed(() => {
const input = WidgetInput.FromAst(props.ast)
@ -54,7 +54,7 @@ function handleWidgetUpdates(update: WidgetUpdate) {
}
const layoutTransitions = useTransitioning(observedLayoutTransitions)
provideWidgetTree(toRef(props, 'ast'), layoutTransitions.active)
provideWidgetTree(toRef(props, 'ast'), toRef(props, 'nodeId'), layoutTransitions.active)
</script>
<template>

View File

@ -1,13 +1,12 @@
import { createContextStore } from '@/providers'
import { asNodeId } from '@/stores/graph/graphDatabase'
import { type NodeId } from '@/stores/graph/graphDatabase'
import { Ast } from '@/util/ast'
import { computed, proxyRefs, type Ref } from 'vue'
export { injectFn as injectWidgetTree, provideFn as provideWidgetTree }
const { provideFn, injectFn } = createContextStore(
'Widget tree',
(astRoot: Ref<Ast.Ast>, hasActiveAnimations: Ref<boolean>) => {
const nodeId = computed(() => asNodeId(astRoot.value.id))
(astRoot: Ref<Ast.Ast>, nodeId: Ref<NodeId>, hasActiveAnimations: Ref<boolean>) => {
const nodeSpanStart = computed(() => astRoot.value.span![0])
return proxyRefs({ astRoot, nodeId, nodeSpanStart, hasActiveAnimations })
},

View File

@ -388,7 +388,7 @@ export const useGraphStore = defineStore('graph', () => {
}
function getPortNodeId(id: PortId): NodeId | undefined {
return getPortPrimaryInstance(id)?.nodeId ?? db.getExpressionNodeId(id as string as Ast.AstId)
return db.getExpressionNodeId(id as string as Ast.AstId) ?? getPortPrimaryInstance(id)?.nodeId
}
/**