Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
67 changes: 67 additions & 0 deletions src/features/editor/views/GraphView/stores/useGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { CanvasDirection } from "reaflow/dist/layout/elkLayout";
import { create } from "zustand";
import { SUPPORTED_LIMIT } from "../../../../../constants/graph";
import useJson from "../../../../../store/useJson";
import useFile from "../../../../../store/useFile";
import type { EdgeData, NodeData } from "../../../../../types/graph";
import { parser } from "../lib/jsonParser";

Expand All @@ -16,6 +17,8 @@ export interface Graph {
selectedNode: NodeData | null;
path: string;
aboveSupportedLimit: boolean;
//Ai generated below line
refreshKey: number;
}

const initialStates: Graph = {
Expand All @@ -28,6 +31,8 @@ const initialStates: Graph = {
selectedNode: null,
path: "",
aboveSupportedLimit: false,
//Ai generated below line
refreshKey: 0,
};

interface GraphActions {
Expand All @@ -43,6 +48,7 @@ interface GraphActions {
centerView: () => void;
clearGraph: () => void;
setZoomFactor: (zoomFactor: number) => void;
updateNodeData: (newData: string) => void;
}

const useGraph = create<Graph & GraphActions>((set, get) => ({
Expand Down Expand Up @@ -101,6 +107,67 @@ const useGraph = create<Graph & GraphActions>((set, get) => ({
},
toggleFullscreen: fullscreen => set({ fullscreen }),
setViewPort: viewPort => set({ viewPort }),

//AI-Generated Code Below
updateNodeData: (newData: string) => {
const selectedNode = get().selectedNode;
if (!selectedNode) return;

try {
// Parse the new data
const parsedData = JSON.parse(newData);
const jsonState = useJson.getState();
const currentJson = JSON.parse(jsonState.json);

// Navigate to the node's path in the JSON and update it
let target: any = currentJson;
for (const key of selectedNode.path || []) {
target = target[key];
}

// Update the target with new data
Object.assign(target, parsedData);

// Update the JSON in the store
const updatedJsonString = JSON.stringify(currentJson, null, 2);
jsonState.setJson(updatedJsonString);

// Also update the file contents so the left panel reflects changes
const useFile = require("../../../../../store/useFile").default;
useFile.getState().setContents({
contents: updatedJsonString,
hasChanges: true,
skipUpdate: true // prevent double graph refresh
});

// Refresh the graph with updated data
get().setGraph(JSON.stringify(currentJson));

// bump refreshKey for other subscribers
set(state => ({ refreshKey: (state.refreshKey ?? 0) + 1 }));

// Update the selected node to reflect changes in real-time
const updatedNode = { ...selectedNode };
const allowedTypes = new Set(["string", "number", "boolean", "null", "array", "object"]);

updatedNode.text = Object.entries(parsedData).map(([key, value]) => {
const candidateType =
value === null ? "null" : Array.isArray(value) ? "array" : typeof value;

const nodeType = allowedTypes.has(candidateType) ? candidateType : "string";

return {
key,
value: String(value),
type: nodeType as any,
};
});

set({ selectedNode: updatedNode });
} catch (error) {
console.error("Failed to update node data:", error);
}
},
}));

export default useGraph;
99 changes: 88 additions & 11 deletions src/features/modals/NodeModal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";
import React, {useState} from "react";
import type { ModalProps } from "@mantine/core";
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton } from "@mantine/core";
import { Modal, Stack, Text, ScrollArea, Flex, CloseButton, Button, Textarea, TextInput } from "@mantine/core";
import { CodeHighlight } from "@mantine/code-highlight";
import type { NodeData } from "../../../types/graph";
import useGraph from "../../editor/views/GraphView/stores/useGraph";
Expand Down Expand Up @@ -28,6 +28,49 @@ const jsonPathToString = (path?: NodeData["path"]) => {

export const NodeModal = ({ opened, onClose }: ModalProps) => {
const nodeData = useGraph(state => state.selectedNode);
//Start of Copilot AI Generated Code
const [isEditing, setIsEditing] = useState(false);
const [editValue, setEditValue] = useState<Record<string, string>>({});

const handleEditClick = () => {
// Create object with key-value pairs for editing
const values: Record<string, string> = {};
nodeData?.text?.forEach(row => {
if (row.type !== "array" && row.type !== "object" && row.key) {
//Used Copilot Quick Fix feature to debug line below
values[row.key] = String(row.value ?? "");
}
});
setEditValue(values);
setIsEditing(true);
};

const handleSave = () => {
// Parse values back to their original types
const parsedValues: Record<string, any> = {};

Object.entries(editValue).forEach(([key, value]) => {
// Try to parse as JSON first (handles numbers, booleans, null, etc.)
try {
parsedValues[key] = JSON.parse(value);
} catch {
// If JSON parsing fails, keep as string
parsedValues[key] = value;
}
});

const updatedJson = JSON.stringify(parsedValues, null, 2);
console.log("Saving:", updatedJson);
useGraph.getState().updateNodeData(updatedJson);
setIsEditing(false);
};

const handleValueChange = (key: string, newValue: string) => {
setEditValue(prev => ({
...prev,
[key]: newValue
}));
};

return (
<Modal size="auto" opened={opened} onClose={onClose} centered withCloseButton={false}>
Expand All @@ -39,15 +82,49 @@ export const NodeModal = ({ opened, onClose }: ModalProps) => {
</Text>
<CloseButton onClick={onClose} />
</Flex>
<ScrollArea.Autosize mah={250} maw={600}>
<CodeHighlight
code={normalizeNodeData(nodeData?.text ?? [])}
miw={350}
maw={600}
language="json"
withCopyButton
/>
</ScrollArea.Autosize>

{/*Co-pilot Generated Code Below*/}

{isEditing ? (
<Stack gap="xs">
{Object.entries(editValue).map(([key, value]) => (
<div key={key}>
<Text fz="xs" fw={500} mb={4}>
{key}
</Text>
<TextInput
value={value}
onChange={(e) => handleValueChange(key, e.currentTarget.value)}
placeholder={`Edit ${key}`}
style={{ fontFamily: "monospace" }}
/>
</div>
))}
<Flex gap="xs">
<Button color="green" onClick={handleSave}>
Save
</Button>
<Button color="red" onClick={() => setIsEditing(false)}>
Cancel
</Button>
</Flex>
</Stack>
) : (
<>
<ScrollArea.Autosize mah={250} maw={600}>
<CodeHighlight
code={normalizeNodeData(nodeData?.text ?? [])}
miw={350}
maw={600}
language="json"
withCopyButton
/>
</ScrollArea.Autosize>
<Button color="green" onClick={handleEditClick}>
Edit
</Button>
</>
)}
</Stack>
<Text fz="xs" fw={500}>
JSON Path
Expand Down
8 changes: 6 additions & 2 deletions src/store/useJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ interface JsonActions {
const initialStates = {
json: "{}",
loading: true,
//Ai generated below line
version: 0,
};

export type JsonStates = typeof initialStates;
Expand All @@ -18,11 +20,13 @@ const useJson = create<JsonStates & JsonActions>()((set, get) => ({
...initialStates,
getJson: () => get().json,
setJson: json => {
set({ json, loading: false });
//AI generated below line
set(state => ({ json, loading: false, version: (state.version ?? 0) + 1 }));
useGraph.getState().setGraph(json);
},
clear: () => {
set({ json: "", loading: false });
//ai generated below line
set(state => ({ json: "", loading: false, version: (state.version ?? 0) + 1 }));
useGraph.getState().clearGraph();
},
}));
Expand Down