import { type NodeProps, Position, type Connection, type Edge, type Node, } from '@xyflow/react'; import { Toolbar } from '../components/NodeComponents'; import styles from '../../VisProg.module.css'; import {MultiConnectionHandle, SingleConnectionHandle} from "../components/RuleBasedHandle.tsx"; import {allowOnlyConnectionsFromHandle, allowOnlyConnectionsFromType} from "../HandleRules.ts"; import useFlowStore from '../VisProgStores'; import { PlanReduce, type Plan } from '../components/Plan'; import PlanEditorDialog from '../components/PlanEditor'; import {BeliefReduce} from "./BeliefReduce.ts"; /** * The default data structure for a Trigger node * * Represents configuration for a node that activates when a specific condition is met, * such as keywords being spoken or emotions detected. * * @property label: the display label of this Trigger node. * @property droppable: Whether this node can be dropped from the toolbar (default: true). * @property hasReduce - Whether this node supports reduction logic. */ export type TriggerNodeData = { label: string; droppable: boolean; condition?: string; // id of the belief plan?: Plan; hasReduce: boolean; }; export type TriggerNode = Node /** * Determines whether a Trigger node can connect to another node or edge. * * @param connection - The connection or edge being attempted to connect towards. * @returns `true` if the connection is defined; otherwise, `false`. * */ export function TriggerNodeCanConnect(connection: Connection | Edge): boolean { return (connection != undefined); } /** * Defines how a Trigger node should be rendered * @param props - Node properties provided by React Flow, including `id` and `data`. * @returns The rendered TriggerNode React element (React.JSX.Element). */ export default function TriggerNode(props: NodeProps) { const data = props.data; const {updateNodeData} = useFlowStore(); return <>
Triggers when the condition is met.
Condition/ Belief is currently {data.condition ? "" : "not"} set. {data.condition ? "🟢" : "🔴"}
Plan{data.plan ? (": " + data.plan.name) : ""} is currently {data.plan ? "" : "not"} set. {data.plan ? "🟢" : "🔴"}
{ updateNodeData(props.id, { ...data, plan, }); }} />
; } /** * Reduces each Trigger, including its children down into its core data. * @param node - The Trigger node to reduce. * @param _nodes - The list of all nodes in the current flow graph. * @returns A simplified object containing the node label and its list of triggers. */ export function TriggerReduce(node: Node, nodes: Node[]) { const data = node.data as TriggerNodeData; const conditionNode = data.condition ? nodes.find((n)=>n.id===data.condition) : undefined const conditionData = conditionNode ? BeliefReduce(conditionNode, nodes) : "" return { id: node.id, condition: conditionData, // Make sure we have a condition before reducing, or default to "" plan: !data.plan ? "" : PlanReduce(data.plan), // Make sure we have a plan when reducing, or default to "" } } /** * This function is called whenever a connection is made with this node type as the target * @param _thisNode the node of this node type which function is called * @param _sourceNodeId the source of the received connection */ export function TriggerConnectionTarget(_thisNode: Node, _sourceNodeId: string) { // no additional connection logic exists yet const data = _thisNode.data as TriggerNodeData; // If we got a belief connected, this is the condition for the norm. if ((useFlowStore.getState().nodes.find((node) => node.id === _sourceNodeId && ['basic_belief', 'inferred_belief'].includes(node.type!)))) { data.condition = _sourceNodeId; } } /** * This function is called whenever a connection is made with this node type as the source * @param _thisNode the node of this node type which function is called * @param _targetNodeId the target of the created connection */ export function TriggerConnectionSource(_thisNode: Node, _targetNodeId: string) { // no additional connection logic exists yet } /** * This function is called whenever a connection is disconnected with this node type as the target * @param _thisNode the node of this node type which function is called * @param _sourceNodeId the source of the disconnected connection */ export function TriggerDisconnectionTarget(_thisNode: Node, _sourceNodeId: string) { // no additional connection logic exists yet const data = _thisNode.data as TriggerNodeData; // remove if the target of disconnection was our condition if (_sourceNodeId == data.condition) data.condition = undefined } /** * This function is called whenever a connection is disconnected with this node type as the source * @param _thisNode the node of this node type which function is called * @param _targetNodeId the target of the diconnected connection */ export function TriggerDisconnectionSource(_thisNode: Node, _targetNodeId: string) { // no additional connection logic exists yet } // Definitions for the possible triggers, being keywords and emotions /** Represents a single keyword trigger entry. */ type Keyword = { id: string, keyword: string }; /** Properties for an emotion-type trigger node. */ export type EmotionTriggerNodeProps = { type: "emotion"; value: string; } /** Props for a keyword-type trigger node. */ export type KeywordTriggerNodeProps = { type: "keywords"; value: Keyword[]; } /** Union type for all possible Trigger node configurations. */ export type TriggerNodeProps = EmotionTriggerNodeProps | KeywordTriggerNodeProps;