Skip to content

Commit d1fcb96

Browse files
Copilotmikebarkmin
andcommitted
Add graphical user interface for defining klasses and setting options
Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
1 parent 0e0816b commit d1fcb96

4 files changed

Lines changed: 336 additions & 1 deletion

File tree

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import useStore, { RFState } from './store';
44
import { shallow } from 'zustand/shallow';
55
import { MemoryView } from "./MemoryView";
66
import { EditView } from "./EditView";
7+
import { ConfigView } from "./ConfigView";
78

89
const selector = (state: RFState) => ({
910
route: state.route
@@ -20,6 +21,7 @@ function App() {
2021
{route === "view" &&
2122
<MemoryView />}
2223
{route === "edit" && <EditView />}
24+
{route === "config" && <ConfigView />}
2325
</div>
2426
);
2527
}

src/ConfigView.tsx

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
import { shallow } from "zustand/shallow";
2+
import useStore, { RFState } from "./store";
3+
import { useCallback, useState } from "react";
4+
import { DataType } from "./memory";
5+
6+
const selector = (state: RFState) => ({
7+
updateMemory: state.updateMemory,
8+
memory: state.memory,
9+
setRoute: state.setRoute,
10+
});
11+
12+
export const ConfigView = () => {
13+
const { memory, updateMemory, setRoute } = useStore(selector, shallow);
14+
15+
const [klasses, setKlasses] = useState(memory.klasses);
16+
const [options, setOptions] = useState(memory.options);
17+
18+
const onSave = useCallback(() => {
19+
updateMemory({
20+
...memory,
21+
klasses,
22+
options,
23+
});
24+
}, [memory, klasses, options, updateMemory]);
25+
26+
const onView = useCallback(() => {
27+
setRoute("view");
28+
}, [setRoute]);
29+
30+
const onEdit = useCallback(() => {
31+
setRoute("edit");
32+
}, [setRoute]);
33+
34+
const handleOptionChange = useCallback(
35+
(key: string, value: boolean) => {
36+
setOptions((prev) => ({
37+
...prev,
38+
[key]: value,
39+
}));
40+
},
41+
[]
42+
);
43+
44+
const handleAddKlass = useCallback(() => {
45+
const name = window.prompt("Enter class name:");
46+
if (name && name.trim()) {
47+
setKlasses((prev) => ({
48+
...prev,
49+
[name.trim()]: {
50+
attributes: {},
51+
},
52+
}));
53+
}
54+
}, []);
55+
56+
const handleRemoveKlass = useCallback((name: string) => {
57+
if (window.confirm(`Are you sure you want to delete class "${name}"?`)) {
58+
setKlasses((prev) => {
59+
const newKlasses = { ...prev };
60+
delete newKlasses[name];
61+
return newKlasses;
62+
});
63+
}
64+
}, []);
65+
66+
const handleAddAttribute = useCallback((klassName: string) => {
67+
const attrName = window.prompt("Enter attribute name:");
68+
if (attrName && attrName.trim()) {
69+
const dataType = window.prompt(
70+
`Enter data type for "${attrName}" (e.g., String, int, boolean, or custom class name):`
71+
);
72+
if (dataType && dataType.trim()) {
73+
setKlasses((prev) => ({
74+
...prev,
75+
[klassName]: {
76+
...prev[klassName],
77+
attributes: {
78+
...prev[klassName].attributes,
79+
[attrName.trim()]: dataType.trim() as DataType,
80+
},
81+
},
82+
}));
83+
}
84+
}
85+
}, []);
86+
87+
const handleRemoveAttribute = useCallback(
88+
(klassName: string, attrName: string) => {
89+
if (
90+
window.confirm(
91+
`Are you sure you want to delete attribute "${attrName}" from class "${klassName}"?`
92+
)
93+
) {
94+
setKlasses((prev) => {
95+
const newKlasses = { ...prev };
96+
const newAttributes = { ...newKlasses[klassName].attributes };
97+
delete newAttributes[attrName];
98+
newKlasses[klassName] = {
99+
...newKlasses[klassName],
100+
attributes: newAttributes,
101+
};
102+
return newKlasses;
103+
});
104+
}
105+
},
106+
[]
107+
);
108+
109+
const handleEditAttribute = useCallback(
110+
(klassName: string, attrName: string, currentDataType: DataType) => {
111+
const newDataType = window.prompt(
112+
`Edit data type for "${attrName}" (current: ${currentDataType}):`,
113+
currentDataType
114+
);
115+
if (newDataType && newDataType.trim()) {
116+
setKlasses((prev) => ({
117+
...prev,
118+
[klassName]: {
119+
...prev[klassName],
120+
attributes: {
121+
...prev[klassName].attributes,
122+
[attrName]: newDataType.trim() as DataType,
123+
},
124+
},
125+
}));
126+
}
127+
},
128+
[]
129+
);
130+
131+
return (
132+
<div style={{ padding: "20px", maxWidth: "1200px", margin: "0 auto" }}>
133+
<h1>Configuration</h1>
134+
135+
<div className="button-group">
136+
<button onClick={onSave}>Save</button>
137+
<button onClick={onView}>View</button>
138+
<button onClick={onEdit}>Edit JSON</button>
139+
</div>
140+
141+
<div style={{ marginTop: "20px" }}>
142+
<h2>Options</h2>
143+
<div style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
144+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
145+
<input
146+
type="checkbox"
147+
checked={options.disableGarbageCollector || false}
148+
onChange={(e) =>
149+
handleOptionChange("disableGarbageCollector", e.target.checked)
150+
}
151+
/>
152+
Disable Garbage Collector
153+
</label>
154+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
155+
<input
156+
type="checkbox"
157+
checked={options.hideSidebar || false}
158+
onChange={(e) => handleOptionChange("hideSidebar", e.target.checked)}
159+
/>
160+
Hide Sidebar
161+
</label>
162+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
163+
<input
164+
type="checkbox"
165+
checked={options.hideCallMethod || false}
166+
onChange={(e) =>
167+
handleOptionChange("hideCallMethod", e.target.checked)
168+
}
169+
/>
170+
Hide Call Method
171+
</label>
172+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
173+
<input
174+
type="checkbox"
175+
checked={options.hideDeclareGlobalVariable || false}
176+
onChange={(e) =>
177+
handleOptionChange("hideDeclareGlobalVariable", e.target.checked)
178+
}
179+
/>
180+
Hide Declare Global Variable
181+
</label>
182+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
183+
<input
184+
type="checkbox"
185+
checked={options.hideNewArray || false}
186+
onChange={(e) =>
187+
handleOptionChange("hideNewArray", e.target.checked)
188+
}
189+
/>
190+
Hide New Array
191+
</label>
192+
<label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
193+
<input
194+
type="checkbox"
195+
checked={options.createNewOnEdgeDrop || false}
196+
onChange={(e) =>
197+
handleOptionChange("createNewOnEdgeDrop", e.target.checked)
198+
}
199+
/>
200+
Create New On Edge Drop
201+
</label>
202+
</div>
203+
</div>
204+
205+
<div style={{ marginTop: "30px" }}>
206+
<div
207+
style={{
208+
display: "flex",
209+
justifyContent: "space-between",
210+
alignItems: "center",
211+
marginBottom: "10px",
212+
}}
213+
>
214+
<h2>Classes</h2>
215+
<button onClick={handleAddKlass}>Add Class</button>
216+
</div>
217+
218+
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
219+
{Object.entries(klasses).map(([klassName, klass]) => (
220+
<div
221+
key={klassName}
222+
style={{
223+
border: "2px solid #ccc",
224+
borderRadius: "8px",
225+
padding: "15px",
226+
backgroundColor: "#f9f9f9",
227+
}}
228+
>
229+
<div
230+
style={{
231+
display: "flex",
232+
justifyContent: "space-between",
233+
alignItems: "center",
234+
marginBottom: "10px",
235+
}}
236+
>
237+
<h3 style={{ margin: 0 }}>{klassName}</h3>
238+
<div style={{ display: "flex", gap: "8px" }}>
239+
<button onClick={() => handleAddAttribute(klassName)}>
240+
Add Attribute
241+
</button>
242+
<button onClick={() => handleRemoveKlass(klassName)}>
243+
Delete Class
244+
</button>
245+
</div>
246+
</div>
247+
248+
<div>
249+
{Object.keys(klass.attributes).length === 0 ? (
250+
<p style={{ color: "#666", fontStyle: "italic" }}>
251+
No attributes defined
252+
</p>
253+
) : (
254+
<table
255+
style={{
256+
width: "100%",
257+
borderCollapse: "collapse",
258+
backgroundColor: "white",
259+
}}
260+
>
261+
<thead>
262+
<tr style={{ borderBottom: "2px solid #ddd" }}>
263+
<th style={{ padding: "8px", textAlign: "left" }}>
264+
Attribute Name
265+
</th>
266+
<th style={{ padding: "8px", textAlign: "left" }}>
267+
Data Type
268+
</th>
269+
<th style={{ padding: "8px", textAlign: "right" }}>
270+
Actions
271+
</th>
272+
</tr>
273+
</thead>
274+
<tbody>
275+
{Object.entries(klass.attributes).map(
276+
([attrName, dataType]) => (
277+
<tr
278+
key={attrName}
279+
style={{ borderBottom: "1px solid #eee" }}
280+
>
281+
<td style={{ padding: "8px" }}>{attrName}</td>
282+
<td style={{ padding: "8px" }}>{dataType}</td>
283+
<td
284+
style={{
285+
padding: "8px",
286+
textAlign: "right",
287+
}}
288+
>
289+
<button
290+
onClick={() =>
291+
handleEditAttribute(
292+
klassName,
293+
attrName,
294+
dataType
295+
)
296+
}
297+
style={{ marginRight: "8px" }}
298+
>
299+
Edit
300+
</button>
301+
<button
302+
onClick={() =>
303+
handleRemoveAttribute(klassName, attrName)
304+
}
305+
>
306+
Delete
307+
</button>
308+
</td>
309+
</tr>
310+
)
311+
)}
312+
</tbody>
313+
</table>
314+
)}
315+
</div>
316+
</div>
317+
))}
318+
</div>
319+
</div>
320+
</div>
321+
);
322+
};

src/EditView.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export const EditView = () => {
3030
setRoute("view");
3131
}, []);
3232

33+
const onConfig = useCallback(() => {
34+
setRoute("config");
35+
}, []);
36+
3337
return (
3438
<div>
3539
<CodeMirror
@@ -41,6 +45,7 @@ export const EditView = () => {
4145
<div className="button-group">
4246
<button onClick={onSave}>Save</button>
4347
<button onClick={onView}>View</button>
48+
<button onClick={onConfig}>Config</button>
4449
</div>
4550
</div>
4651
);

src/MemoryView.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ export const MemoryView = () => {
231231
setRoute("edit");
232232
};
233233

234+
const onConfig = () => {
235+
onSaveURL();
236+
setRoute("config");
237+
};
238+
234239
const onSaveURL = () => {
235240
if (reactFlowInstance) {
236241
updateMemory({
@@ -495,7 +500,8 @@ export const MemoryView = () => {
495500
<div className="button-group">
496501
<button onClick={onSaveURL}>Save (URL)</button>
497502
<button onClick={onDownloadPng}>Download (PNG)</button>
498-
<button onClick={onEdit}>Edit</button>
503+
<button onClick={onConfig}>Config</button>
504+
<button onClick={onEdit}>Edit JSON</button>
499505
</div>
500506
</Panel>
501507
{!memory.options.disableGarbageCollector && <Panel position="bottom-right">

0 commit comments

Comments
 (0)