Merging dev into main #49

Merged
8464960 merged 260 commits from dev into main 2026-01-28 10:48:52 +00:00
5 changed files with 214 additions and 2 deletions
Showing only changes of commit 10d5a15c88 - Show all commits

View File

@@ -248,3 +248,11 @@ button.no-button {
text-decoration: underline;
}
}
.flex-center-x {
display: flex;
justify-content: center; /* horizontal centering */
text-align: center; /* center multi-line text */
width: 100%; /* allow it to stretch */
flex-wrap: wrap; /* optional: let text wrap naturally */
}

View File

@@ -71,6 +71,11 @@
filter: drop-shadow(0 0 0.25rem red);
}
.node-basic_belief {
outline: plum solid 2pt;
filter: drop-shadow(0 0 0.25rem plum);
}
.draggable-node {
padding: 3px 10px;
background-color: canvas;
@@ -126,3 +131,11 @@
outline: red solid 2pt;
filter: drop-shadow(0 0 0.25rem red);
}
.draggable-node-basic_belief {
padding: 3px 10px;
background-color: canvas;
border-radius: 5pt;
outline: plum solid 2pt;
filter: drop-shadow(0 0 0.25rem plum);
}

View File

@@ -10,6 +10,8 @@ import GoalNode, { GoalConnects, GoalReduce } from "./nodes/GoalNode";
import { GoalNodeDefaults } from "./nodes/GoalNode.default";
import TriggerNode, { TriggerConnects, TriggerReduce } from "./nodes/TriggerNode";
import { TriggerNodeDefaults } from "./nodes/TriggerNode.default";
import BasicBeliefNode, { BasicBeliefConnects, BasicBeliefReduce } from "./nodes/BasicBeliefNode";
import { BasicBeliefNodeDefaults } from "./nodes/BasicBeliefNode.default";
/**
* Registered node types in the visual programming system.
@@ -24,6 +26,7 @@ export const NodeTypes = {
norm: NormNode,
goal: GoalNode,
trigger: TriggerNode,
basic_belief: BasicBeliefNode,
};
/**
@@ -38,6 +41,7 @@ export const NodeDefaults = {
norm: NormNodeDefaults,
goal: GoalNodeDefaults,
trigger: TriggerNodeDefaults,
basic_belief: BasicBeliefNodeDefaults,
};
@@ -54,6 +58,7 @@ export const NodeReduces = {
norm: NormReduce,
goal: GoalReduce,
trigger: TriggerReduce,
basic_belief: BasicBeliefReduce,
}
@@ -69,6 +74,7 @@ export const NodeConnects = {
norm: NormConnects,
goal: GoalConnects,
trigger: TriggerConnects,
basic_belief: BasicBeliefConnects,
}
/**
@@ -79,7 +85,6 @@ export const NodeConnects = {
export const NodeDeletes = {
start: () => false,
end: () => false,
test: () => false, // Used for coverage of universal/ undefined nodes
}
/**
@@ -92,5 +97,5 @@ export const NodesInPhase = {
start: () => false,
end: () => false,
phase: () => false,
test: () => false, // Used for coverage of universal/ undefined nodes
// basic_belief: () => false,
}

View File

@@ -0,0 +1,12 @@
import type { BasicBeliefNodeData } from "./BasicBeliefNode";
/**
* Default data for this node
*/
export const BasicBeliefNodeDefaults: BasicBeliefNodeData = {
label: "Belief",
droppable: true,
belief: {type: "keyword", id: "help", value: "help", label: "Keyword said:"},
hasReduce: true,
};

View File

@@ -0,0 +1,174 @@
import {
Handle,
type NodeProps,
Position,
type Connection,
type Edge,
type Node,
} from '@xyflow/react';
import { Toolbar } from '../components/NodeComponents';
import styles from '../../VisProg.module.css';
import useFlowStore from '../VisProgStores';
import { TextField } from '../../../../components/TextField';
/**
* The default data structure for a BasicBelief 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 BasicBelief node.
* @property droppable: Whether this node can be dropped from the toolbar (default: true).
* @property BasicBeliefType - The type of BasicBelief ("keywords" or a custom string).
* @property BasicBeliefs - The list of keyword BasicBeliefs (if applicable).
* @property hasReduce - Whether this node supports reduction logic.
*/
export type BasicBeliefNodeData = {
label: string;
droppable: boolean;
belief: BasicBeliefType;
hasReduce: boolean;
};
// These are all the types a basic belief could be.
type BasicBeliefType = Keyword | Semantic | Object | Emotion
type Keyword = { type: "keyword", id: string, value: string, label: "Keyword said:"};
type Semantic = { type: "semantic", id: string, value: string, label: "Detected with LLM:"};
type Object = { type: "object", id: string, value: string, label: "Object found:"};
type Emotion = { type: "emotion", id: string, value: string, label: "Emotion recognised:"};
export type BasicBeliefNode = Node<BasicBeliefNodeData>
/**
* Determines whether a BasicBelief 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 BasicBeliefNodeCanConnect(connection: Connection | Edge): boolean {
return (connection != undefined);
}
/**
* Defines how a BasicBelief node should be rendered
* @param props - Node properties provided by React Flow, including `id` and `data`.
* @returns The rendered BasicBeliefNode React element (React.JSX.Element).
*/
export default function BasicBeliefNode(props: NodeProps<BasicBeliefNode>) {
const data = props.data;
const {updateNodeData} = useFlowStore();
const updateValue = (value: string) => updateNodeData(props.id, {...data, belief: {...data.belief, value: value}});
const label_input_id = `basic_belief_${props.id}_label_input`;
type BeliefString = BasicBeliefType["type"];
function updateBeliefType(newType: BeliefString) {
updateNodeData(props.id, {
...data,
belief: {
...data.belief,
type: newType,
},
});
}
// Use this
const emotionOptions = ["Happy", "Angry", "Sad", "Cheerful"]
let placeholder = ""
let wrapping = ""
switch (props.data.belief.type) {
case ("keyword"):
placeholder = "keyword..."
wrapping = '"'
break;
case ("semantic"):
placeholder = "word..."
wrapping = '"'
break;
case ("object"):
placeholder = "object..."
break;
case ("emotion"):
// TODO: emotion should probably be a drop-down menu rather than a string
// So this placeholder won't hold for always
placeholder = "emotion..."
break;
default:
break;
}
return (
<>
<Toolbar nodeId={props.id} allowDelete={true}/>
<div className={`${styles.defaultNode} ${styles.nodeBasicBelief /*TODO: Change this*/}`}>
<div className={"flex-center-x gap-sm"}>
<label htmlFor={label_input_id}>Belief:</label>
</div>
<div className={"flex-row gap-sm"}>
<select
value={data.belief.type}
onChange={(e) => updateBeliefType(e.target.value as BeliefString)}
>
<option value="keyword">Keyword said:</option>
<option value="semantic">Detected with LLM:</option>
<option value="object">Object found:</option>
<option value="emotion">Emotion recognised:</option>
</select>
{wrapping}
{data.belief.type === "emotion" && (
<select
value={data.belief.value}
onChange={(e) => updateValue(e.target.value)}
>
{emotionOptions.map((emotion) => (
<option key={emotion} value={emotion.toLowerCase()}>
{emotion}
</option>
))}
</select>
)}
{data.belief.type !== "emotion" &&
(<TextField
id={label_input_id}
value={data.belief.value}
setValue={updateValue}
placeholder={placeholder}
/>)}
{wrapping}
</div>
<Handle type="source" position={Position.Right} id="source"/>
</div>
</>
);
};
/**
* Reduces each BasicBelief, including its children down into its core data.
* @param node - The BasicBelief 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 BasicBeliefs.
*/
export function BasicBeliefReduce(node: BasicBeliefNode, _nodes: Node[]) {
const data = node.data;
return {
id: node.id,
type: data.belief.type,
value: data.belief.value
}
}
/**
* This function is called whenever a connection is made with this node type (BasicBelief)
* @param _thisNode the node of this node type which function is called
* @param _otherNode the other node which was part of the connection
* @param _isThisSource whether this instance of the node was the source in the connection, true = yes.
*/
export function BasicBeliefConnects(_thisNode: Node, _otherNode: Node, _isThisSource: boolean) {
}