Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a39f305
feat: first FSA draft with Reactflow, the wizard definitely need some…
HongleiGu Jan 16, 2026
b9e9a30
fix: dev notice in FSA.md
HongleiGu Jan 16, 2026
675d612
fix: updated UI display, removed prompting, add information panel, ne…
HongleiGu Jan 16, 2026
da333ba
refactor: Reactflow is not so suitable for maths, changing to cytosca…
HongleiGu Jan 25, 2026
21e9a11
fix: align with the backend, can pass basic tests
HongleiGu Jan 27, 2026
3e813d7
feat: added feedback
HongleiGu Jan 28, 2026
a467516
Merge pull request #3 from HongleiGu/cytoscape
HongleiGu Jan 28, 2026
5456800
fix: remove debugging logs
HongleiGu Jan 28, 2026
897f324
Merge pull request #4 from HongleiGu/cytoscape
HongleiGu Jan 28, 2026
822f87e
feat: implement GraphQL data fetching and add live preview functional…
JohnnyWan1123 Jan 30, 2026
8b60ada
fix: style modification
HongleiGu Jan 30, 2026
107050e
Merge branch 'preview' of github.com:HongleiGu/FSA-response-area into…
HongleiGu Jan 30, 2026
ce94457
Merge pull request #5 from HongleiGu/preview
HongleiGu Jan 30, 2026
c88db1b
revert: revert back to before preview
HongleiGu Jan 30, 2026
b7c8401
Merge pull request #6 from HongleiGu/cytoscape
HongleiGu Jan 30, 2026
1370a0d
fix: styles forgetten by the revert
HongleiGu Jan 30, 2026
04a1e4d
feat: should be everything
HongleiGu Feb 1, 2026
88b141a
Merge pull request #7 from HongleiGu/cytoscape
HongleiGu Feb 2, 2026
690aff5
feat: first draft, still need fix
HongleiGu Feb 5, 2026
3117859
implemented feedback panel
HarrySu123 Feb 17, 2026
605e6e6
fixed panels
HarrySu123 Feb 18, 2026
06f1588
functioning front end
HarrySu123 Feb 18, 2026
544c107
Merge branch 'main' into sync
HongleiGu Feb 26, 2026
1c4f985
Refactor code structure for improved readability and maintainability,…
HarrySu123 Feb 26, 2026
c60111a
feat: added configuration panel to control evlauation type
HarrySu123 Feb 28, 2026
f7cb4e1
Merge pull request #1 from lambda-feedback/sync
HongleiGu Mar 1, 2026
01853f5
feat: removed redundant graph eidotr in answer response area
HarrySu123 Mar 1, 2026
5695892
Merge branch 'main' of github.com:lambda-feedback/Graph-response-area…
HongleiGu Mar 3, 2026
210e4c4
feat: made markdown file for Graph and deleted FSA.md
HarrySu123 Mar 3, 2026
684c86d
fix: removed unnecessary gitignore items
HarrySu123 Mar 3, 2026
0406b54
fix: removed mention and FSA in README.md and replaced with Graph ref…
HarrySu123 Mar 3, 2026
af2e31d
fix: removed commented out code
HarrySu123 Mar 3, 2026
3dc8d16
feat: removed validation on the front end, and removed preview panel …
HarrySu123 Mar 4, 2026
8a520b4
feat: removed utils.ts, duplicated algorithm
HarrySu123 Mar 4, 2026
594188d
fix: student response area not influenced by reference answer graph e…
HarrySu123 Mar 4, 2026
85dd5a2
fix: moved Graph.component styles into its own file
HarrySu123 Mar 5, 2026
c891e36
fix: moved styles from ConfigPanel out into its own files
HarrySu123 Mar 5, 2026
bc1152e
feat: updated Graph.md
HarrySu123 Mar 5, 2026
4a2f42f
feat: removed all consol logs
HarrySu123 Mar 9, 2026
555aa31
fix: removed magic numbers in graph.components.tsx
HarrySu123 Mar 9, 2026
b5d2a77
fix: removed use of compressed Graph
HarrySu123 Mar 10, 2026
eb504fc
feat: used Vite version 7.0.0
HarrySu123 Mar 10, 2026
a7ec6fa
fix: added back missing imports
HarrySu123 Mar 10, 2026
faaf3e4
fix: added back removed response types
HarrySu123 Mar 10, 2026
46c160a
feat: updated Graph.md
HarrySu123 Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be ignored or just not added to the commit? (Do we want to ignore these in the future?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is here by accident, FSA and PropositionalLogic never existed in this repo, will definitely fix this

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this was done by me, I had an issue with my laptop where I couldn't open more than 1 vscode open at the same time, but I wanted to see the code for PL config panel and FSA graph to take inspiration for Graph. Fixed now.

Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ yarn-error.log*
tsconfig.tsbuildinfo

# env files (copy .env.example)
.env
.env
220 changes: 220 additions & 0 deletions Graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# Graph Module Documentation

This module provides a visual editor for Graphs built with **Cytoscape.js** and **Paper.js**. It bridges a **Python-style backend** (deeply nested objects) and a **TypeScript/Zod frontend** (restricted to 2-level JSON nesting via pipe-delimited strings).

---

## 1. The Core Data Structures

### Frontend Schema (`Graph`)

The full graph type used internally by the editor. Defined as a Zod schema in `type.ts`.

```typescript
type Graph = {
nodes: Array<{
id: string;
label?: string;
x?: number;
y?: number;
metadata: Record<string, any>;
}>;
edges: Array<{
source: string;
target: string;
weight?: number; // currently unused
label?: string;
id?: string;
metadata: Record<string, any>;
}>;
directed: boolean;
weighted: boolean; // currently unused
multigraph: boolean; // currently unused
metadata: Record<string, any>;
}
```

### Flattened Schema (`SimpleGraph`)

To satisfy the `jsonNestedSchema` (2-level nesting limit, Shimmy communication issues), nodes and edges are serialised as pipe-delimited strings.

```typescript
type SimpleGraph = {
nodes: string[]; // Format: "id|label|x|y"
edges: string[]; // Format: "source|target|weight|label"
directed: boolean;
weighted: boolean;
multigraph: boolean;
evaluation_type: string[];
}
```

### Config Schema (`GraphConfig`)

Teacher-configured parameters stored separately in `config`, **not** in the answer. `evaluation_type` is a plain string (the backend Pydantic `EvaluationParams` expects a string, not an array).

```typescript
type GraphConfig = {
directed: boolean;
weighted: boolean;
multigraph: boolean;
evaluation_type: string; // e.g. 'connectivity', 'isomorphism', ...
}
```

### Answer Schema (`GraphAnswer`)

Topology-only answer — config flags live in `GraphConfig`, not here.

```typescript
type GraphAnswer = {
nodes: string[]; // pipe-delimited node strings
edges: string[]; // pipe-delimited edge strings
}
```

### Validation & Feedback Types

```typescript
enum CheckPhase { Idle = 'idle', Evaluated = 'evaluated' }

interface ValidationError {
type: 'error' | 'warning'
message: string
field?: string
}

interface GraphFeedback {
valid: boolean
errors: ValidationError[]
phase: CheckPhase
}
```

---

## 2. File Structure

```
src/types/Graph/
Graph.component.tsx # GraphEditor — Cytoscape + Paper.js visual editor (panel inlined)
Graph.component.styles.ts # makeStyles (emotion/tss-react) styles for GraphEditor
index.tsx # GraphResponseAreaTub, WizardPanel, InputComponent
type.ts # All Zod schemas, types, and conversion utilities
components/
ConfigPanel.tsx # Teacher config UI (graph type, evaluation type)
ConfigPanel.styles.ts # makeStyles styles for ConfigPanel
```

> **Note:** Styles use `makeStyles` from `@styles` (tss-react/emotion) rather than CSS modules. This is required because the project builds as an IIFE library — CSS modules are extracted into a separate file that may not be loaded by the consuming app, while emotion injects styles at runtime inside the JS bundle.

---

## 3. Key Components

### `Graph.component.tsx` — `GraphEditor`

The primary visual editor.

- **Rendering**: Uses **Cytoscape.js** for graph rendering and interaction.
- **Draw Mode**: Uses **Paper.js** (overlaid canvas) for freehand drawing:
- **Draw a circle** → creates a new node at the circle's centre.
- **Draw a line between nodes** → creates a new edge between the two closest nodes.
- **Click two nodes** (while in draw mode) → creates an edge between them.
- **Selection**: Clicking a node or edge selects it; its properties appear in the inlined **Item Properties** side panel.
- **Sync**: Every mutation (add/delete/edit) calls `syncToGraph()`, which reads Cytoscape state and fires `onChange(graph)`.

### `components/ConfigPanel.tsx`

Teacher-facing configuration panel (rendered in `WizardComponent` only).

- Toggle **Directed / Undirected**.
- Select an **Evaluation Type** (e.g. `isomorphism`, `connectivity`, `tree`, ...).
- Accepts an optional `AnswerPanel?: React.ReactNode` prop, rendered below the evaluation-type selector when `isomorphism` is selected.
- Styles are extracted to `ConfigPanel.styles.ts`.

### Item Properties Panel (inlined in `Graph.component.tsx`)

Left side panel rendered directly inside `GraphEditor` for editing selected elements:

- **Add Node** button.
- **Fit to Screen** button.
- **Draw Edge** toggle (activates draw mode).
- Edit **Display Name** of a selected node.
- Edit **Edge Label** of a selected edge.
- **Delete** button for nodes and edges.

---

## 4. Transformation Logic

Conversion utilities in `type.ts` handle the boundary between the rich editor format and the flattened wire format.

| Function | Source | Target |
|---|---|---|
| `toSimpleGraph(graph, evaluationType?)` | `Graph` | `SimpleGraph` |
| `fromSimpleGraph(simple)` | `SimpleGraph` | `Graph` |
| `graphAnswerToSimple(answer, config)` | `GraphAnswer` + `GraphConfig` | `SimpleGraph` |
| `simpleToAnswer(simple)` | `SimpleGraph` | `GraphAnswer` |

---

## 5. Usage in the Pipeline

1. **Load**: Data arrives from the backend as a flattened answer object (nodes + edges + config flags merged together).
2. **Convert**: `fromSimpleGraph(graphAnswerToSimple(answer, config))` reconstructs the rich `Graph` for the editor.
3. **Edit**: The user interacts with `GraphEditor`. Internal state stays in the rich `Graph` format.
4. **Save**: On change, `simpleToAnswer(toSimpleGraph(graph))` flattens the topology back. Config flags are merged into the answer object before sending to the backend:
```typescript
const flatAnswer = {
...answer,
directed: config.directed,
weighted: config.weighted,
multigraph: config.multigraph,
evaluation_type: config.evaluation_type,
}
```

### Legacy migration

`extractConfig` and `extractAnswer` in `GraphResponseAreaTub` handle two legacy cases:
- `evaluation_type` stored as `string[]` (old format) — first element is taken.
- Config flags flattened directly into the answer object (old format) — flags are read from there and a proper `GraphConfig` is reconstructed.

---

## 6. Important Implementation Notes

- **Node IDs**: Auto-generated as `n0`, `n1`, `n2`, ... The counter tracks the highest existing ID to avoid duplicates on reload.
- **Edge IDs**: Generated as `` `e-${source}-${target}-${Date.now()}` `` to guarantee uniqueness, including in multigraphs.
- **Config is flattened into answer**: The backend reads `directed`, `weighted`, `multigraph`, and `evaluation_type` from the answer object, not a separate config field. This is handled in `WizardPanel.onChange`.
- **Stable sub-components**: `WizardPanel` is defined outside the `GraphResponseAreaTub` class to prevent React from treating it as a new component type on re-render, which would unmount/remount `GraphEditor` and destroy Cytoscape state.
- **Cytoscape vs Paper.js layering**: Paper.js canvas sits on top of Cytoscape (`zIndex: 10`) with `pointerEvents: none` when not in draw mode, and `pointerEvents: auto` + `cursor: crosshair` when draw mode is active.
- **Arrow direction**: The Cytoscape edge style `target-arrow-shape` is reactively updated whenever `graph.directed` changes.
- **Isomorphism mode**: When `evaluation_type === 'isomorphism'`, `WizardPanel` renders a second `GraphEditor` for the teacher to define the reference graph.
- **Initial emit**: `WizardPanel` emits `onChange` on mount so config is always persisted to the DB even if the teacher never interacts with it.

---

## 7. Supported Evaluation Types

```
isomorphism, connectivity, bipartite, cycle_detection,
graph_coloring, planarity, tree, forest, dag, eulerian,
semi_eulerian, regular, complete, degree_sequence,
subgraph, hamiltonian_path, hamiltonian_cycle, clique_number
```

---

## 8. Dev Notice

Run `yarn dev` to start (runs `build:watch` + `preview` in parallel).

Note: for dev mode only, there is an extra config in `vite.config.ts`:

```ts
root: 'dev', // for dev only
```

Remember to remove it before going to production.
4 changes: 4 additions & 0 deletions README.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

References FSA.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as FSA.md, simple fix

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Develop local TypeScript code to create custom response area types, and see them previewed live in the main application. When they're ready, provide your code to the Lambda Feedback team, who will consider including it in the main application after (human) review.

## Graph Response Area

Please refer to [Graph.md](./Graph.md)

## Overview

To create a new response area type, you'll need to:
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"build": "tsc -b && vite build",
"build:watch": "vite build --watch",
"build:watch": "node --max-old-space-size=4096 --no-opt ./node_modules/.bin/vite build --watch",
"format": "prettier --check \"**/*.{js,ts,tsx}\"",
"format:fix": "prettier --write \"**/*.{js,ts,tsx}\"",
"lint": "eslint .",
Expand All @@ -14,7 +14,8 @@
"ci": "run-p format lint typecheck",
"fix": "run-s format:fix lint:fix",
"preview": "vite preview",
"dev": "run-p build:watch preview"
"dev": "run-p build:watch preview",
"dev:fsa": "vite"
},
"dependencies": {
"@date-io/date-fns": "^2.13.2",
Expand All @@ -35,6 +36,7 @@
"@nivo/core": "^0.88.0",
"@nivo/line": "^0.88.0",
"@nivo/pie": "^0.88.0",
"cytoscape": "^3.33.1",
"axios": "^1.7.2",
"date-fns": "^2.28.0",
"firebase": "^11.0.1",
Expand All @@ -46,6 +48,8 @@
"markdown-to-jsx": "^7.1.7",
"monaco-editor": "^0.50.0",
"next": "^14.2.4",
"paper": "^0.12.18",
"reactflow": "^11.11.4",
"notistack": "^2.0.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
2 changes: 1 addition & 1 deletion src/sandbox-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function ResponseAreaInputWrapper({ children }: { children: React.ReactNode }) {
}

// wrap the components with the necessary providers; only in the sandbox
class WrappedSandboxResponseAreaTub extends SandboxResponseAreaTub {
class WrappedSandboxResponseAreaTub extends GraphResponseAreaTub {
constructor() {
super()

Expand Down
1 change: 0 additions & 1 deletion src/types/Code/Code.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export const CodeInput: React.FC<CodeInputProps> = ({
loading={<Loading />}
language={config.language}
className={classes.codearea}
beforeMount={console.log}
/>
)
}
Expand Down
Loading