Spaces:
Running
Running
file saving
Browse files
client/src/components/BlockEditor/BlockEditor.tsx
CHANGED
|
@@ -738,6 +738,14 @@ export default function BlockEditor() {
|
|
| 738 |
ws.addChangeListener(saveBlocks);
|
| 739 |
|
| 740 |
return () => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
ws.removeChangeListener(saveBlocks);
|
| 742 |
ws.dispose();
|
| 743 |
workspaceRef.current = null;
|
|
|
|
| 738 |
ws.addChangeListener(saveBlocks);
|
| 739 |
|
| 740 |
return () => {
|
| 741 |
+
// Save current blocks for the file before workspace is disposed
|
| 742 |
+
if (activeFileId) {
|
| 743 |
+
try {
|
| 744 |
+
const dom = Blockly.Xml.workspaceToDom(ws);
|
| 745 |
+
const xml = Blockly.Xml.domToText(dom);
|
| 746 |
+
setBlocksXml(activeFileId, xml);
|
| 747 |
+
} catch (e) {}
|
| 748 |
+
}
|
| 749 |
ws.removeChangeListener(saveBlocks);
|
| 750 |
ws.dispose();
|
| 751 |
workspaceRef.current = null;
|
client/src/components/ProjectEditor/ProjectEditor.tsx
CHANGED
|
@@ -60,8 +60,9 @@ export default function ProjectEditor() {
|
|
| 60 |
editorMode, setEditorMode, viewMode, setViewMode,
|
| 61 |
activeFileId, setActiveFile, fileTree, setFileTree,
|
| 62 |
visualElements, setVisualElements, setElementRegistry,
|
| 63 |
-
elementRegistry,
|
| 64 |
-
|
|
|
|
| 65 |
} = useEditorStore();
|
| 66 |
const [showSidebar, setShowSidebar] = useState(true);
|
| 67 |
const [saving, setSaving] = useState(false);
|
|
@@ -111,6 +112,12 @@ export default function ProjectEditor() {
|
|
| 111 |
setBlockCode(fid, code);
|
| 112 |
});
|
| 113 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
if (!activeFileId && currentProject.data.files?.length > 0) {
|
| 115 |
const firstFile = currentProject.data.files[0];
|
| 116 |
if (firstFile) {
|
|
@@ -122,7 +129,7 @@ export default function ProjectEditor() {
|
|
| 122 |
}
|
| 123 |
}
|
| 124 |
}
|
| 125 |
-
}, [currentProject, setFileTree, setVisualElements, setActiveFile, activeFileId, setEditorMode, setBlocksXml, setElementRegistry]);
|
| 126 |
|
| 127 |
// When activeFileId changes, load visual elements from the registry
|
| 128 |
useEffect(() => {
|
|
@@ -227,7 +234,7 @@ export default function ProjectEditor() {
|
|
| 227 |
if (!currentProject || !id) return;
|
| 228 |
setSaving(true);
|
| 229 |
try {
|
| 230 |
-
const { blocksXml: currentBlocksXml, blockCode: currentBlockCode, elementRegistry: currentRegistry } = useEditorStore.getState();
|
| 231 |
|
| 232 |
// Sync current visual elements into registry before saving
|
| 233 |
const fullRegistry = { ...currentRegistry };
|
|
@@ -241,6 +248,7 @@ export default function ProjectEditor() {
|
|
| 241 |
visual: fullRegistry,
|
| 242 |
blocksXml: currentBlocksXml,
|
| 243 |
blockCode: currentBlockCode,
|
|
|
|
| 244 |
};
|
| 245 |
|
| 246 |
// Try WebSocket save first, fall back to REST
|
|
@@ -259,7 +267,7 @@ export default function ProjectEditor() {
|
|
| 259 |
if (!currentProject || !id || !connected) return;
|
| 260 |
const timer = setTimeout(() => handleSave(), 2000);
|
| 261 |
return () => clearTimeout(timer);
|
| 262 |
-
}, [currentProject, id, fileTree, visualElements, elementRegistry, activeFileId, connected, handleSave]);
|
| 263 |
|
| 264 |
useEffect(() => {
|
| 265 |
const handleKeyDown = (e: KeyboardEvent) => {
|
|
|
|
| 60 |
editorMode, setEditorMode, viewMode, setViewMode,
|
| 61 |
activeFileId, setActiveFile, fileTree, setFileTree,
|
| 62 |
visualElements, setVisualElements, setElementRegistry,
|
| 63 |
+
elementRegistry, blocksXml, blockCode, cssRules,
|
| 64 |
+
snapToGrid, toggleSnapToGrid,
|
| 65 |
+
showMiniMap, toggleMiniMap, setBlocksXml, setCssRules
|
| 66 |
} = useEditorStore();
|
| 67 |
const [showSidebar, setShowSidebar] = useState(true);
|
| 68 |
const [saving, setSaving] = useState(false);
|
|
|
|
| 112 |
setBlockCode(fid, code);
|
| 113 |
});
|
| 114 |
}
|
| 115 |
+
// Restore per-file CSS rules
|
| 116 |
+
if (currentProject.data.cssRules) {
|
| 117 |
+
Object.entries(currentProject.data.cssRules).forEach(([fid, rules]) => {
|
| 118 |
+
setCssRules(fid, rules as any[]);
|
| 119 |
+
});
|
| 120 |
+
}
|
| 121 |
if (!activeFileId && currentProject.data.files?.length > 0) {
|
| 122 |
const firstFile = currentProject.data.files[0];
|
| 123 |
if (firstFile) {
|
|
|
|
| 129 |
}
|
| 130 |
}
|
| 131 |
}
|
| 132 |
+
}, [currentProject, setFileTree, setVisualElements, setActiveFile, activeFileId, setEditorMode, setBlocksXml, setElementRegistry, setCssRules]);
|
| 133 |
|
| 134 |
// When activeFileId changes, load visual elements from the registry
|
| 135 |
useEffect(() => {
|
|
|
|
| 234 |
if (!currentProject || !id) return;
|
| 235 |
setSaving(true);
|
| 236 |
try {
|
| 237 |
+
const { blocksXml: currentBlocksXml, blockCode: currentBlockCode, elementRegistry: currentRegistry, cssRules: currentCssRules } = useEditorStore.getState();
|
| 238 |
|
| 239 |
// Sync current visual elements into registry before saving
|
| 240 |
const fullRegistry = { ...currentRegistry };
|
|
|
|
| 248 |
visual: fullRegistry,
|
| 249 |
blocksXml: currentBlocksXml,
|
| 250 |
blockCode: currentBlockCode,
|
| 251 |
+
cssRules: currentCssRules,
|
| 252 |
};
|
| 253 |
|
| 254 |
// Try WebSocket save first, fall back to REST
|
|
|
|
| 267 |
if (!currentProject || !id || !connected) return;
|
| 268 |
const timer = setTimeout(() => handleSave(), 2000);
|
| 269 |
return () => clearTimeout(timer);
|
| 270 |
+
}, [currentProject, id, fileTree, visualElements, elementRegistry, activeFileId, blocksXml, blockCode, cssRules, connected, handleSave]);
|
| 271 |
|
| 272 |
useEffect(() => {
|
| 273 |
const handleKeyDown = (e: KeyboardEvent) => {
|