Skip to content

Commit 6c84cf7

Browse files
committed
chore: Bump version to 0.3.1 and improve error handling
- Update version from 0.2.8 to 0.3.1 across app.go, package.json, splash screen, and wails.json - Update application icons and branding assets (appicon.png, windows icon.ico) - Initialize keyboard shortcuts hook in App component on mount - Disable default React Flow delete key handling to prevent conflicts with custom keyboard shortcut logic - Add existence checks before node operations in useKeyboardShortcuts to prevent errors when selected node is deleted - Add validation checks in customNodeStore operations (update, delete, duplicate) to warn and gracefully handle missing nodes - Add input validation and existence checks in executionStore deleteExecution method - Improve error handling and user feedback with console warnings and toast notifications for edge cases
1 parent f436a10 commit 6c84cf7

13 files changed

Lines changed: 188 additions & 48 deletions

File tree

app.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (a *App) shutdown(ctx context.Context) {
2626
func (a *App) GetAppInfo() map[string]string {
2727
return map[string]string{
2828
"name": "ForgeFlow",
29-
"version": "0.2.8",
29+
"version": "0.3.1",
3030
"tagline": "Local automation. Zero cloud. Full control.",
3131
}
3232
}

build/appicon.png

-107 KB
Loading

build/splash.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ <h1>ForgeFlow</h1>
153153
<div class="loader">
154154
<div class="loader-bar"></div>
155155
</div>
156-
<p class="version">v0.2.8</p>
156+
<p class="version">v0.3.1</p>
157157
</div>
158158
</body>
159159
</html>

build/windows/icon.ico

-7.83 KB
Binary file not shown.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "forgeflow",
33
"private": true,
4-
"version": "0.2.8",
4+
"version": "0.3.1",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

frontend/src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useSettingsStore } from "@/stores/settingsStore";
1818
import { toast } from "@/stores/dialogStore";
1919
import { useEffect, useState } from "react";
2020
import { APP_VERSION } from "./version";
21+
import { useKeyboardShortcuts } from "@/hooks/useKeyboardShortcuts";
2122

2223
function SplashScreen() {
2324
return (
@@ -74,6 +75,9 @@ export default function App() {
7475
const { loadSettings, applyTheme } = useSettingsStore();
7576
const [isLoading, setIsLoading] = useState(true);
7677

78+
// Initialize keyboard shortcuts
79+
useKeyboardShortcuts();
80+
7781
const activeFlow = flows.find((f) => f.id === activeFlowId);
7882

7983
const handleSaveFlow = async (name: string, description: string) => {

frontend/src/components/flow/FlowCanvas.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ export default function FlowCanvas() {
207207
nodesDraggable={!isRunning}
208208
nodesConnectable={!isRunning}
209209
elementsSelectable={!isRunning}
210-
deleteKeyCode={["Backspace", "Delete"]}
210+
deleteKeyCode={null}
211211
snapToGrid
212212
snapGrid={[16, 16]}
213213
minZoom={0.2}

frontend/src/hooks/useKeyboardShortcuts.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ export function useKeyboardShortcuts() {
4747
// Delete/Backspace - delete selected node
4848
if ((e.key === 'Delete' || e.key === 'Backspace') && selectedNodeId) {
4949
e.preventDefault();
50-
removeNode(selectedNodeId);
51-
setSelectedNodeId(null);
50+
const nodeExists = nodes.some((n) => n.id === selectedNodeId);
51+
if (nodeExists) {
52+
removeNode(selectedNodeId);
53+
setSelectedNodeId(null);
54+
} else {
55+
console.warn('Selected node no longer exists');
56+
setSelectedNodeId(null);
57+
}
5258
return;
5359
}
5460

@@ -87,6 +93,9 @@ export function useKeyboardShortcuts() {
8793
};
8894
addNode(newNode);
8995
setSelectedNodeId(newNode.id);
96+
} else {
97+
console.warn('Selected node no longer exists');
98+
setSelectedNodeId(null);
9099
}
91100
return;
92101
}
@@ -119,7 +128,13 @@ export function useKeyboardShortcuts() {
119128
// Ctrl+C - copy selected node
120129
if (isMod && e.key === 'c' && selectedNodeId) {
121130
e.preventDefault();
122-
copyNode(selectedNodeId);
131+
const nodeExists = nodes.some((n) => n.id === selectedNodeId);
132+
if (nodeExists) {
133+
copyNode(selectedNodeId);
134+
} else {
135+
console.warn('Selected node no longer exists');
136+
setSelectedNodeId(null);
137+
}
123138
return;
124139
}
125140

frontend/src/stores/customNodeStore.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,16 @@ export const useCustomNodeStore = create<CustomNodeState>()(
6060
},
6161

6262
updateCustomNode: (type, updates) => {
63+
const { customNodes } = get();
64+
const nodeExists = customNodes.some((n) => n.type === type);
65+
66+
if (!nodeExists) {
67+
console.warn(`Custom node ${type} not found, cannot update`);
68+
return;
69+
}
70+
6371
set({
64-
customNodes: get().customNodes.map((n) =>
72+
customNodes: customNodes.map((n) =>
6573
n.type === type
6674
? { ...n, ...updates, updatedAt: new Date().toISOString() }
6775
: n
@@ -70,22 +78,34 @@ export const useCustomNodeStore = create<CustomNodeState>()(
7078
},
7179

7280
deleteCustomNode: (type) => {
73-
set({ customNodes: get().customNodes.filter((n) => n.type !== type) });
81+
const { customNodes } = get();
82+
const nodeExists = customNodes.some((n) => n.type === type);
83+
84+
if (!nodeExists) {
85+
console.warn(`Custom node ${type} not found, cannot delete`);
86+
return;
87+
}
88+
89+
set({ customNodes: customNodes.filter((n) => n.type !== type) });
7490
},
7591

7692
duplicateCustomNode: (type) => {
7793
const node = get().customNodes.find((n) => n.type === type);
78-
if (node) {
79-
const now = new Date().toISOString();
80-
const newNode: CustomNodeDefinition = {
81-
...node,
82-
type: `${node.type}_copy_${Date.now()}`,
83-
name: `${node.name} (Copy)`,
84-
createdAt: now,
85-
updatedAt: now,
86-
};
87-
set({ customNodes: [...get().customNodes, newNode] });
94+
95+
if (!node) {
96+
console.warn(`Custom node ${type} not found, cannot duplicate`);
97+
return;
8898
}
99+
100+
const now = new Date().toISOString();
101+
const newNode: CustomNodeDefinition = {
102+
...node,
103+
type: `${node.type}_copy_${Date.now()}`,
104+
name: `${node.name} (Copy)`,
105+
createdAt: now,
106+
updatedAt: now,
107+
};
108+
set({ customNodes: [...get().customNodes, newNode] });
89109
},
90110
}),
91111
{

frontend/src/stores/executionStore.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,25 +57,57 @@ export const useExecutionStore = create<ExecutionState>()((set, get) => ({
5757
},
5858

5959
deleteExecution: async (execId: string) => {
60+
if (!execId || execId.trim() === '') {
61+
console.warn('Invalid execution ID provided for deletion');
62+
return;
63+
}
64+
65+
const { executions, selectedExecution } = get();
66+
const execution = executions.find(e => e.id === execId);
67+
68+
if (!execution) {
69+
console.warn(`Execution ${execId} not found, may have already been deleted`);
70+
toast.warning('Execution not found', 'It may have already been deleted');
71+
return;
72+
}
73+
6074
try {
6175
await DeleteExecution(execId);
62-
set({ executions: get().executions.filter(e => e.id !== execId) });
76+
set({
77+
executions: executions.filter(e => e.id !== execId),
78+
selectedExecution: selectedExecution?.id === execId ? null : selectedExecution,
79+
});
6380
toast.success("Execution deleted");
6481
} catch (error) {
6582
console.error("Failed to delete execution:", error);
66-
toast.error("Failed to delete execution");
83+
toast.error("Failed to delete execution", error instanceof Error ? error.message : 'Unknown error');
84+
throw error;
6785
}
6886
},
6987

7088
clearExecutions: async () => {
7189
const { executions } = get();
90+
91+
if (executions.length === 0) {
92+
console.log('No executions to clear');
93+
return;
94+
}
95+
7296
try {
73-
await Promise.all(executions.map(e => DeleteExecution(e.id)));
74-
set({ executions: [] });
97+
const deletePromises = executions.map(e =>
98+
DeleteExecution(e.id).catch(err => {
99+
console.error(`Failed to delete execution ${e.id}:`, err);
100+
return null;
101+
})
102+
);
103+
104+
await Promise.all(deletePromises);
105+
set({ executions: [], selectedExecution: null });
75106
toast.success("All executions cleared");
76107
} catch (error) {
77108
console.error("Failed to clear executions:", error);
78-
toast.error("Failed to clear executions");
109+
toast.error("Failed to clear executions", error instanceof Error ? error.message : 'Unknown error');
110+
throw error;
79111
}
80112
},
81113

0 commit comments

Comments
 (0)