Compare commits

...

6 Commits

Author SHA1 Message Date
Björn Otgaar
26894cc2d2 Merge branch 'dev' into chore/clean-visprog-code 2026-01-28 12:27:27 +01:00
Björn Otgaar
dcf4f01e68 chore: replace plan editor functionality into own folder with own files. 2026-01-28 12:26:57 +01:00
Gerla, J. (Justin)
c84088dd9d Merge branch 'chore/editing-css-strings' into 'dev'
chore: updated css comments

See merge request ics/sp/2025/n25b/pepperplus-ui!51
2026-01-28 11:19:16 +00:00
JGerla
dfe793e04a chore: updated css comments 2026-01-28 12:07:33 +01:00
Björn Otgaar
1876138fe2 Merge branch 'dev' into chore/clean-visprog-code 2026-01-28 11:36:21 +01:00
Björn Otgaar
5930e24bf4 chore: intitial commit, starting seperating concerns for plan editor. 2026-01-28 11:35:34 +01:00
35 changed files with 416 additions and 295 deletions

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
export default function Next({ fill }: { fill?: string }) {
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
<path d="M664.07-224.93v-510.14h91v510.14h-91Zm-459.14 0v-510.14L587.65-480 204.93-224.93Z"/>

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
export default function Pause({ fill }: { fill?: string }) {
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
<path d="M556.17-185.41v-589.18h182v589.18h-182Zm-334.34 0v-589.18h182v589.18h-182Z"/>

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
export default function Play({ fill }: { fill?: string }) {
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
<path d="M311.87-185.41v-589.18L775.07-480l-463.2 294.59Z"/>

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
export default function Redo({ fill }: { fill?: string }) {
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
<path d="M390.98-191.87q-98.44 0-168.77-65.27-70.34-65.27-70.34-161.43 0-96.15 70.34-161.54 70.33-65.39 168.77-65.39h244.11l-98.98-98.98 63.65-63.65L808.13-600 599.76-391.87l-63.65-63.65 98.98-98.98H390.98q-60.13 0-104.12 38.92-43.99 38.93-43.99 96.78 0 57.84 43.99 96.89 43.99 39.04 104.12 39.04h286.15v91H390.98Z"/>

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
export default function Replay({ fill }: { fill?: string }) {
return <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill={fill ?? "canvas"}>
<path d="M480.05-70.43q-76.72 0-143.78-29.1-67.05-29.1-116.75-78.8-49.69-49.69-78.79-116.75-29.1-67.05-29.1-143.49h91q0 115.81 80.73 196.47Q364.1-161.43 480-161.43q115.8 0 196.47-80.74 80.66-80.73 80.66-196.63 0-115.81-80.73-196.47-80.74-80.66-196.64-80.66h-6.24l60.09 60.08-58.63 60.63-166.22-166.21 166.22-166.22 58.63 60.87-59.85 59.85h6q76.74 0 143.76 29.09 67.02 29.1 116.84 78.8 49.81 49.69 78.91 116.64 29.1 66.95 29.1 143.61 0 76.66-29.1 143.71-29.1 67.06-78.79 116.75-49.7 49.7-116.7 78.8-67.01 29.1-143.73 29.1Z"/>

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import type {Cell} from "../../utils/cellStore.ts";
import type {LogRecord} from "./useLogs.ts";

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.filter-root {
position: relative;
display: flex;

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.logging-container {
box-sizing: border-box;

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.text-field {
border: 1px solid transparent;
border-radius: 5pt;

View File

@@ -4,6 +4,11 @@ University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.read_the_docs {
color: #888;
}

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.dashboardContainer {
display: grid;
grid-template-columns: 2fr 1fr; /* Left = content, Right = logs */

View File

@@ -1,3 +1,8 @@
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/
.logs {
/* grid-area used in MonitoringPage.module.css */
grid-area: logs;

View File

@@ -1,3 +1,6 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import styles from "./ExperimentLogs.module.css";
import {LogMessages} from "../../../components/Logging/Logging.tsx";
import {useEffect, useMemo, useState} from "react";

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
/* editor UI */
.inner-editor-container {

View File

@@ -1,253 +0,0 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import {useRef, useState} from "react";
import useFlowStore from "../VisProgStores.tsx";
import styles from './PlanEditor.module.css';
import { GetActionValue, type Action, type ActionTypes, type Plan } from "../components/Plan";
import { defaultPlan } from "../components/Plan.default";
import { TextField } from "../../../../components/TextField";
import GestureValueEditor from "./GestureValueEditor";
type PlanEditorDialogProps = {
plan?: Plan;
onSave: (plan: Plan | undefined) => void;
description? : string;
};
export default function PlanEditorDialog({
plan,
onSave,
description,
}: PlanEditorDialogProps) {
// UseStates and references
const dialogRef = useRef<HTMLDialogElement | null>(null);
const [draftPlan, setDraftPlan] = useState<Plan | null>(null);
const [newActionType, setNewActionType] = useState<ActionTypes>("speech");
const [newActionGestureType, setNewActionGestureType] = useState<boolean>(true);
const [newActionValue, setNewActionValue] = useState("");
const [hasInteractedWithPlan, setHasInteractedWithPlan] = useState<boolean>(false)
const { setScrollable } = useFlowStore();
const nodes = useFlowStore().nodes;
//Button Actions
const openCreate = () => {
setScrollable(false);
setDraftPlan({...structuredClone(defaultPlan), id: crypto.randomUUID()});
dialogRef.current?.showModal();
};
const openCreateWithDescription = () => {
setScrollable(false);
setDraftPlan({...structuredClone(defaultPlan), id: crypto.randomUUID(), name: description!});
setNewActionType("llm")
setNewActionValue(description!)
dialogRef.current?.showModal();
}
const openEdit = () => {
setScrollable(false);
if (!plan) return;
setDraftPlan(structuredClone(plan));
dialogRef.current?.showModal();
};
const close = () => {
setScrollable(true);
dialogRef.current?.close();
setDraftPlan(null);
};
const buildAction = (): Action => {
const id = crypto.randomUUID();
setHasInteractedWithPlan(true)
switch (newActionType) {
case "speech":
return { id, text: newActionValue, type: "speech" };
case "gesture":
return { id, gesture: newActionValue, isTag: newActionGestureType, type: "gesture" };
case "llm":
return { id, goal: newActionValue, type: "llm" };
}
};
return (<>
{/* Create and edit buttons */}
{!plan && (
<button className={styles.nodeButton} onClick={description ? openCreateWithDescription : openCreate}>
Create Plan
</button>
)}
{plan && (
<button className={styles.nodeButton} onClick={openEdit}>
Edit Plan
</button>
)}
{/* Start of dialog (plan editor) */}
<dialog
ref={dialogRef}
className={`${styles.planDialog}`}
//onWheel={(e) => e.stopPropagation()}
data-testid={"PlanEditorDialogTestID"}
>
<form method="dialog" className="flex-col gap-md">
<h3> {draftPlan?.id === plan?.id ? "Edit Plan" : "Create Plan"} </h3>
{/* Plan name text field */}
{draftPlan && (
<TextField
value={draftPlan.name}
setValue={(name) =>
setDraftPlan({ ...draftPlan, name })}
placeholder="Plan name"
data-testid="name_text_field"/>
)}
{/* Entire "bottom" part (adder and steps) without cancel, confirm and reset */}
{draftPlan && (<div className={styles.planEditor}>
<div className={styles.planEditorLeft}>
{/* Left Side (Action Adder) */}
<h4>Add Action</h4>
{(!plan && description && draftPlan.steps.length === 0 && !hasInteractedWithPlan) && (<div className={styles.stepSuggestion}>
<label> Filled in as a suggestion! </label>
<label> Feel free to change! </label>
</div>)}
<label>
Action Type <wbr />
{/* Type selection */}
<select
value={newActionType}
onChange={(e) => {
setNewActionType(e.target.value as ActionTypes);
// Reset value when action type changes
setNewActionValue("");
}}>
<option value="speech">Speech Action</option>
<option value="gesture">Gesture Action</option>
<option value="llm">LLM Action</option>
</select>
</label>
{/* Action value editor*/}
{newActionType === "gesture" ? (
// Gesture get their own editor component
<GestureValueEditor
value={newActionValue}
setValue={setNewActionValue}
setType={setNewActionGestureType}
placeholder="Gesture name"
/>
) : (
<TextField
value={newActionValue}
setValue={setNewActionValue}
placeholder={
newActionType === "speech" ? "Speech text"
: "LLM goal"
}
/>
)}
{/* Adding steps */}
<button
type="button"
disabled={!newActionValue}
onClick={() => {
if (!draftPlan) return;
// Add action to steps
const action = buildAction();
setDraftPlan({
...draftPlan,
steps: [...draftPlan.steps, action],});
// Reset current action building
setNewActionValue("");
setNewActionType("speech");
}}>
Add Step
</button>
</div>
{/* Right Side (Steps shown) */}
<div className={styles.planEditorRight}>
<h4>Steps</h4>
{/* Show if there are no steps yet */}
{draftPlan.steps.length === 0 && (
<div className={styles.emptySteps}>
No steps yet
</div>
)}
{/* Map over all steps */}
{draftPlan.steps.map((step, index) => (
<div
role="button"
tabIndex={0}
key={step.id}
className={styles.planStep}
// Extra logic for screen readers to access using keyboard
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
setDraftPlan({
...draftPlan,
steps: draftPlan.steps.filter((s) => s.id !== step.id),});
}}}
onClick={() => {
setDraftPlan({
...draftPlan,
steps: draftPlan.steps.filter((s) => s.id !== step.id),});
}}>
<span className={styles.stepIndex}>{index + 1}.</span>
<span className={styles.stepType}>{step.type}:</span>
<span className={styles.stepName}>
{
// This just tries to find the goals name, i know it looks ugly:(
step.type === "goal"
? ((nodes.find(x => x.id === step.id)?.data.name as string) == "" ?
"unnamed goal": (nodes.find(x => x.id === step.id)?.data.name as string))
: (GetActionValue(step) ?? "")}
</span>
</div>
))}
</div>
</div>
)}
{/* Buttons */}
<div className="flex-row gap-md">
{/* Close button */}
<button type="button" onClick={close}>
Cancel
</button>
{/* Confirm/ Create button */}
<button
type="button"
disabled={!draftPlan}
onClick={() => {
if (!draftPlan) return;
onSave(draftPlan);
close();
}}>
{draftPlan?.id === plan?.id ? "Confirm" : "Create"}
</button>
{/* Reset button */}
<button
type="button"
disabled={!draftPlan}
onClick={() => {
onSave(undefined);
close();
}}>
Reset
</button>
</div>
</form>
</dialog>
</>
);
}

View File

@@ -0,0 +1,72 @@
import { TextField } from "../../../../../components/TextField";
import GestureValueEditor from "./GestureValueEditor";
import type { ActionTypes } from "./Plan";
import styles from './PlanEditor.module.css';
type ActionAdderProps = {
newActionType: ActionTypes;
setNewActionType: (t: ActionTypes) => void;
newActionValue: string;
setNewActionValue: (v: string) => void;
setNewActionGestureType: (b: boolean) => void;
onAdd: () => void;
showSuggestion: boolean;
};
export function ActionAdder({
newActionType,
setNewActionType,
newActionValue,
setNewActionValue,
setNewActionGestureType,
onAdd,
showSuggestion,
}: ActionAdderProps) {
return (
<div className={styles.planEditorLeft}>
<h4>Add Action</h4>
{showSuggestion && (
<div className={styles.stepSuggestion}>
<label>Filled in as a suggestion!</label>
<label>Feel free to change!</label>
</div>
)}
<label>
Action Type <wbr />
<select
value={newActionType}
onChange={(e) => {
setNewActionType(e.target.value as ActionTypes);
setNewActionValue("");
}}
>
<option value="speech">Speech Action</option>
<option value="gesture">Gesture Action</option>
<option value="llm">LLM Action</option>
</select>
</label>
{newActionType === "gesture" ? (
<GestureValueEditor
value={newActionValue}
setValue={setNewActionValue}
setType={setNewActionGestureType}
placeholder="Gesture name"
/>
) : (
<TextField
value={newActionValue}
setValue={setNewActionValue}
placeholder={newActionType === "speech" ? "Speech text" : "LLM goal"}
/>
)}
<button type="button" disabled={!newActionValue} onClick={onAdd}>
Add Step
</button>
</div>
);
}

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.gestureEditor {
display: flex;
flex-direction: column;

View File

@@ -2,7 +2,7 @@
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import { type Node } from "@xyflow/react"
import { GoalReduce } from "../nodes/GoalNode"
import { GoalReduce } from "../../nodes/GoalNode"
export type Plan = {
@@ -124,4 +124,6 @@ export function GetActionValue(action: Action) {
return returnAction.goal;
default:
}
}
}

View File

@@ -2,7 +2,7 @@
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
// This file is to avoid sharing both functions and components which eslint dislikes. :)
import type { GoalNode } from "../nodes/GoalNode"
import type { GoalNode } from "../../nodes/GoalNode"
import type { Goal, Plan } from "./Plan"
/**
@@ -35,4 +35,5 @@ export function deleteGoalInPlanByID(plan: Plan, goalID: string) {
steps: plan.steps.filter((x) => x.id !== goalID)
}
return updatedPlan.steps.length == 0 ? undefined : updatedPlan
}
}

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.planDialog {
overflow:visible;
width: 80vw;

View File

@@ -0,0 +1,214 @@
// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import {useRef, useState} from "react";
import useFlowStore from "../../VisProgStores.tsx";
import styles from './PlanEditor.module.css';
import { type Action, type ActionTypes, type Plan } from "./Plan.tsx";
import { defaultPlan } from "./Plan.default.ts";
import { TextField } from "../../../../../components/TextField.tsx";
import { StepsList } from "./StepList.tsx";
import { ActionAdder } from "./ActionAdder.tsx";
type PlanEditorDialogProps = {
plan?: Plan;
onSave: (plan: Plan | undefined) => void;
description? : string;
};
/**
* Creates an action, as a step for a plan.
* @param type the type of action to build
* @param value the value of this action to build
* @param isGestureTag whether or not this action, restricted to gestures, is a tag.
* @returns An action
*/
function buildAction(
type: ActionTypes,
value: string,
isGestureTag: boolean
): Action {
const id = crypto.randomUUID();
switch (type) {
case "speech":
return { id, text: value, type: "speech" };
case "gesture":
return { id, gesture: value, isTag: isGestureTag, type: "gesture" };
case "llm":
return { id, goal: value, type: "llm" };
}
}
export default function PlanEditorDialog({
plan,
onSave,
description,
}: PlanEditorDialogProps) {
// UseStates and references
const dialogRef = useRef<HTMLDialogElement | null>(null);
const [draftPlan, setDraftPlan] = useState<Plan | null>(null);
const [newActionType, setNewActionType] = useState<ActionTypes>("speech");
const [newActionGestureType, setNewActionGestureType] = useState<boolean>(true);
const [newActionValue, setNewActionValue] = useState("");
const [hasInteractedWithPlan, setHasInteractedWithPlan] = useState<boolean>(false)
const { setScrollable } = useFlowStore();
const nodes = useFlowStore().nodes;
//Button Actions
const openCreate = () => {
setScrollable(false);
setDraftPlan({...structuredClone(defaultPlan), id: crypto.randomUUID()});
dialogRef.current?.showModal();
};
const openCreateWithDescription = () => {
setScrollable(false);
setDraftPlan({...structuredClone(defaultPlan), id: crypto.randomUUID(), name: description!});
setNewActionType("llm")
setNewActionValue(description!)
dialogRef.current?.showModal();
}
const openEdit = () => {
setScrollable(false);
if (!plan) return;
setDraftPlan(structuredClone(plan));
dialogRef.current?.showModal();
};
const close = () => {
setScrollable(true);
dialogRef.current?.close();
setDraftPlan(null);
};
const addAction = () => {
if (!draftPlan) return;
// Add action to steps
const action = buildAction(newActionType, newActionValue, newActionGestureType);
setDraftPlan({
...draftPlan,
steps: [...draftPlan.steps, action],});
// Reset current action building
setNewActionValue("");
setNewActionType("speech");
setHasInteractedWithPlan(true);
}
const showSuggestion : boolean = (
!plan &&
!!description &&
draftPlan !== null &&
draftPlan.steps.length === 0 &&
!hasInteractedWithPlan)
return (<>
{/* Create and edit buttons */}
{!plan && (
<button className={styles.nodeButton} onClick={description ? openCreateWithDescription : openCreate}>
Create Plan
</button>
)}
{plan && (
<button className={styles.nodeButton} onClick={openEdit}>
Edit Plan
</button>
)}
{/* Start of dialog (plan editor) */}
<dialog
ref={dialogRef}
className={`${styles.planDialog}`}
//onWheel={(e) => e.stopPropagation()}
data-testid={"PlanEditorDialogTestID"}
>
<form method="dialog" className="flex-col gap-md">
<h3> {draftPlan?.id === plan?.id ? "Edit Plan" : "Create Plan"} </h3>
{/* Plan name text field */}
{draftPlan && (
<TextField
value={draftPlan.name}
setValue={(name) =>
setDraftPlan({ ...draftPlan, name })}
placeholder="Plan name"
data-testid="name_text_field"/>
)}
{/* Entire "bottom" part (adder and steps) without cancel, confirm and reset */}
{draftPlan && (<div className={styles.planEditor}>
<div className={styles.planEditorLeft}>
{/* Left Side (Action Adder) */}
<ActionAdder
newActionType={newActionType}
setNewActionType={setNewActionType}
newActionValue={newActionValue}
setNewActionValue={setNewActionValue}
setNewActionGestureType={setNewActionGestureType}
onAdd={addAction}
showSuggestion={showSuggestion}
/>
</div>
{/* Right Side (Steps shown) */}
<div className={styles.planEditorRight}>
<h4>Steps</h4>
{/* Map over all steps */}
<StepsList
steps={draftPlan.steps}
nodes={nodes}
onRemove={(id) =>
setDraftPlan({
...draftPlan,
steps: draftPlan.steps.filter(s => s.id !== id),
})
}
/>
</div>
</div>
)}
{/* Buttons */}
<div className="flex-row gap-md">
{/* Close button */}
<button type="button" onClick={close}>
Cancel
</button>
{/* Confirm/ Create button */}
<button
type="button"
disabled={!draftPlan}
onClick={() => {
if (!draftPlan) return;
onSave(draftPlan);
close();
}}>
{draftPlan?.id === plan?.id ? "Confirm" : "Create"}
</button>
{/* Reset button */}
<button
type="button"
disabled={!draftPlan}
onClick={() => {
onSave(undefined);
close();
}}>
Reset
</button>
</div>
</form>
</dialog>
</>
);
}

View File

@@ -0,0 +1,54 @@
import { GetActionValue, type PlanElement } from "./Plan";
import styles from './PlanEditor.module.css';
import { type Node} from "@xyflow/react"
type StepsListProps = {
steps: PlanElement[];
onRemove: (id: string) => void;
nodes: Node[];
};
function getStepLabel(
step: PlanElement,
nodes: Node[],
): string {
if (step.type === "goal") {
// For goals, we lookup the value through the nodes in the diagram
const node = nodes.find(n => n.id === step.id);
return (node?.data?.name as string)?.trim() || "unnamed goal";
}
// Not a goal, we lookup the correct action value of the action
return GetActionValue(step) ?? "";
}
export function StepsList({ steps, onRemove, nodes }: StepsListProps) {
if (steps.length === 0) {
return <div className={styles.emptySteps}>No steps yet</div>;
}
return (
<>
{steps.map((step, index) => (
<div
key={step.id}
role="button"
tabIndex={0}
className={styles.planStep}
onClick={() => onRemove(step.id)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
onRemove(step.id);
}
}}
>
<span className={styles.stepIndex}>{index + 1}.</span>
<span className={styles.stepType}>{step.type}:</span>
<span className={styles.stepName}>
{getStepLabel(step, nodes)}
</span>
</div>
))}
</>
);
}

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
:global(.react-flow__handle.source){
border-radius: 100%;
}

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.save-load-panel {
border-radius: 0 0 5pt 5pt;
background-color: canvas;

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.warnings-sidebar {
min-width: auto;
max-width: 340px;

View File

@@ -14,11 +14,11 @@ import { TextField } from '../../../../components/TextField';
import {MultiConnectionHandle} from "../components/RuleBasedHandle.tsx";
import {allowOnlyConnectionsFromHandle, allowOnlyConnectionsFromType} from "../HandleRules.ts";
import useFlowStore from '../VisProgStores';
import {DoesPlanIterate, HasCheckingSubGoal, PlanReduce, type Plan } from '../components/Plan';
import PlanEditorDialog from '../components/PlanEditor';
import {DoesPlanIterate, HasCheckingSubGoal, PlanReduce, type Plan } from '../components/PlanEditor/Plan.tsx';
import PlanEditorDialog from '../components/PlanEditor/PlanEditor.tsx';
import { MultilineTextField } from '../../../../components/MultilineTextField';
import { defaultPlan } from '../components/Plan.default.ts';
import { deleteGoalInPlanByID, insertGoalInPlan } from '../components/PlanEditingFunctions.tsx';
import { defaultPlan } from '../components/PlanEditor/Plan.default.ts';
import { deleteGoalInPlanByID, insertGoalInPlan } from '../components/PlanEditor/PlanEditingFunctions.tsx';
/**
* The default data dot a phase node

View File

@@ -1,8 +1,8 @@
{/*
/*
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
*/}
*/
.operator-switch {
display: inline-flex;
align-items: center;

View File

@@ -13,12 +13,12 @@ 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 {PlanReduce, type Plan } from '../components/PlanEditor/Plan.tsx';
import PlanEditorDialog from '../components/PlanEditor/PlanEditor.tsx';
import {BeliefGlobalReduce} from "./BeliefGlobals.ts";
import type { GoalNode } from './GoalNode.tsx';
import { defaultPlan } from '../components/Plan.default.ts';
import { deleteGoalInPlanByID, insertGoalInPlan } from '../components/PlanEditingFunctions.tsx';
import { defaultPlan } from '../components/PlanEditor/Plan.default.ts';
import { deleteGoalInPlanByID, insertGoalInPlan } from '../components/PlanEditor/PlanEditingFunctions.tsx';
import { TextField } from '../../../../components/TextField.tsx';
/**

View File

@@ -4,7 +4,7 @@
import { useState } from 'react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders, screen } from '../../../../test-utils/test-utils.tsx';
import GestureValueEditor from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/GestureValueEditor';
import GestureValueEditor from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/GestureValueEditor.tsx';
function TestHarness({ initialValue = '', initialType=true, placeholder = 'Gesture name' } : { initialValue?: string, initialType?: boolean, placeholder?: string }) {
const [value, setValue] = useState(initialValue);

View File

@@ -6,12 +6,12 @@ import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import type { Node } from '@xyflow/react';
import { renderWithProviders } from '../../../../test-utils/test-utils.tsx';
import PlanEditorDialog from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor';
import { PlanReduce, type Plan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/Plan';
import PlanEditorDialog from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/PlanEditor.tsx';
import { PlanReduce, type Plan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/Plan.tsx';
import '@testing-library/jest-dom';
import { GoalReduce, type GoalNodeData } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/GoalNode.tsx';
import { GoalNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/GoalNode.default.ts';
import { insertGoalInPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditingFunctions.tsx';
import { insertGoalInPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/PlanEditingFunctions.tsx';
// Mock structuredClone

View File

@@ -10,7 +10,7 @@ import useFlowStore from '../../../../../src/pages/VisProgPage/visualProgramming
import type { Node } from '@xyflow/react';
import '@testing-library/jest-dom';
import { GoalNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/GoalNode.default.ts';
import { defaultPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/Plan.default.ts';
import { defaultPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/Plan.default.ts';
describe('GoalNode', () => {
let user: ReturnType<typeof userEvent.setup>;

View File

@@ -14,7 +14,7 @@ import type { Node } from '@xyflow/react';
import '@testing-library/jest-dom';
import { TriggerNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/TriggerNode.default.ts';
import { BasicBeliefNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/BasicBeliefNode.default.ts';
import { defaultPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/Plan.default.ts';
import { defaultPlan } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/components/PlanEditor/Plan.default.ts';
import { NormNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/NormNode.default.ts';
import { GoalNodeDefaults } from '../../../../../src/pages/VisProgPage/visualProgrammingUI/nodes/GoalNode.default.ts';
import { act } from '@testing-library/react';