diff --git a/src/features/editor/views/GraphView/stores/useGraph.ts b/src/features/editor/views/GraphView/stores/useGraph.ts index 6e067c3c2a7..9f2231d6c2d 100644 --- a/src/features/editor/views/GraphView/stores/useGraph.ts +++ b/src/features/editor/views/GraphView/stores/useGraph.ts @@ -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"; @@ -16,6 +17,8 @@ export interface Graph { selectedNode: NodeData | null; path: string; aboveSupportedLimit: boolean; + //Ai generated below line + refreshKey: number; } const initialStates: Graph = { @@ -28,6 +31,8 @@ const initialStates: Graph = { selectedNode: null, path: "", aboveSupportedLimit: false, + //Ai generated below line + refreshKey: 0, }; interface GraphActions { @@ -43,6 +48,7 @@ interface GraphActions { centerView: () => void; clearGraph: () => void; setZoomFactor: (zoomFactor: number) => void; + updateNodeData: (newData: string) => void; } const useGraph = create((set, get) => ({ @@ -101,6 +107,67 @@ const useGraph = create((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; diff --git a/src/features/modals/NodeModal/index.tsx b/src/features/modals/NodeModal/index.tsx index caba85febac..cef39d28782 100644 --- a/src/features/modals/NodeModal/index.tsx +++ b/src/features/modals/NodeModal/index.tsx @@ -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"; @@ -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>({}); + + const handleEditClick = () => { + // Create object with key-value pairs for editing + const values: Record = {}; + 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 = {}; + + 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 ( @@ -39,15 +82,49 @@ export const NodeModal = ({ opened, onClose }: ModalProps) => { - - - + + {/*Co-pilot Generated Code Below*/} + + {isEditing ? ( + + {Object.entries(editValue).map(([key, value]) => ( +
+ + {key} + + handleValueChange(key, e.currentTarget.value)} + placeholder={`Edit ${key}`} + style={{ fontFamily: "monospace" }} + /> +
+ ))} + + + + +
+ ) : ( + <> + + + + + + )} JSON Path diff --git a/src/store/useJson.ts b/src/store/useJson.ts index 62512c79743..39f4f7893c1 100644 --- a/src/store/useJson.ts +++ b/src/store/useJson.ts @@ -10,6 +10,8 @@ interface JsonActions { const initialStates = { json: "{}", loading: true, + //Ai generated below line + version: 0, }; export type JsonStates = typeof initialStates; @@ -18,11 +20,13 @@ const useJson = create()((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(); }, }));