Skip to content

Commit 2599ade

Browse files
committed
fix: apply some fixes
1 parent 7100c99 commit 2599ade

16 files changed

Lines changed: 129 additions & 68 deletions

File tree

electron-app/main.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ app.on("window-all-closed", () => {
116116
});
117117

118118
// Cleanup before app quits
119-
app.on("before-quit", () => {
120-
// Stop backend process gracefully
121-
stopBackend();
122-
// Stop packet sender process gracefully
123-
stopPacketSender();
119+
app.on("before-quit", (e) => {
120+
e.preventDefault();
121+
Promise.all([stopBackend(), stopPacketSender()])
122+
.catch((error) => logger.electron.error("Error during shutdown:", error))
123+
.finally(() => app.exit());
124124
});
125125

126126
// Handle uncaught exceptions globally

electron-app/preload.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ contextBridge.exposeInMainWorld("electronAPI", {
3737
// Open folder selection dialog
3838
selectFolder: () => ipcRenderer.invoke("select-folder"),
3939
// Receive log message from backend
40-
onLog: (callback) =>
41-
ipcRenderer.on("log", (_event, value) => callback(value)),
40+
onLog: (callback) => {
41+
const listener = (_event, value) => callback(value);
42+
ipcRenderer.removeAllListeners("log");
43+
ipcRenderer.on("log", listener);
44+
return () => ipcRenderer.removeListener("log", listener);
45+
},
4246
});

electron-app/src/config/__tests__/updateTomlValue.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,45 @@ key2 = "value2"`;
164164
0
165165
);
166166
});
167+
168+
it("should not corrupt a [section]-like line inside a multiline string", () => {
169+
const toml = `[app]
170+
note = """
171+
[not-a-section]
172+
just text
173+
"""
174+
name = "old"`;
175+
176+
const result = updateTomlValue(toml, "app", "name", "new");
177+
178+
expect(result).toContain('name = "new"');
179+
expect(result).toContain("[not-a-section]");
180+
});
181+
182+
it("should not update a key inside a multiline string", () => {
183+
const toml = `[app]
184+
note = """
185+
name = "inside multiline"
186+
"""
187+
name = "real"`;
188+
189+
const result = updateTomlValue(toml, "app", "name", "updated");
190+
191+
expect(result).toContain('name = "updated"');
192+
expect(result).toContain('name = "inside multiline"');
193+
expect(result.indexOf('name = "inside multiline"')).toBeLessThan(
194+
result.indexOf('name = "updated"')
195+
);
196+
});
197+
198+
it("should handle a multiline string that opens and closes on the same line", () => {
199+
const toml = `[app]
200+
note = """single line multiline"""
201+
name = "old"`;
202+
203+
const result = updateTomlValue(toml, "app", "name", "new");
204+
205+
expect(result).toContain('name = "new"');
206+
expect(result).toContain('note = """single line multiline"""');
207+
});
167208
});

electron-app/src/config/configManager.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,30 @@ import { logger } from "../utils/logger.js";
2121
* const updated = updateTomlValue(content, "database", "host", "192.168.1.1");
2222
*/
2323
function updateTomlValue(tomlContent, section, key, newValue) {
24+
const lineEnding = tomlContent.includes("\r\n") ? "\r\n" : "\n";
2425
// Split content into lines for processing
2526
const lines = tomlContent.split(/\r?\n/);
2627
// Track current section while iterating
2728
let currentSection = null;
2829
// Flag to track if update was successful
2930
let updated = false;
31+
// Track if we're inside a multiline string
32+
let inMultilineString = false;
3033

3134
// Process each line
3235
const result = lines.map((line) => {
3336
// Get trimmed version for parsing
3437
const trimmed = line.trim();
3538

39+
// Track multiline string boundaries (""" or ''')
40+
const tripleDoubleQuotes = (line.match(/"""/g) || []).length;
41+
const tripleSingleQuotes = (line.match(/'''/g) || []).length;
42+
if (tripleDoubleQuotes % 2 !== 0 || tripleSingleQuotes % 2 !== 0) {
43+
inMultilineString = !inMultilineString;
44+
return line;
45+
}
46+
if (inMultilineString) return line;
47+
3648
// Track current section
3749
const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/);
3850
if (sectionMatch) {
@@ -104,7 +116,7 @@ function updateTomlValue(tomlContent, section, key, newValue) {
104116
}
105117

106118
// Join lines back into string
107-
return result.join("\n");
119+
return result.join(lineEnding);
108120
}
109121

110122
/**

electron-app/src/processes/backend.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ async function startBackend(logWindow = null) {
8888
backendProcess.stderr.on("data", (data) => {
8989
const errorMsg = data.toString().trim();
9090
logger.backend.error(errorMsg);
91-
// Store the last error message
92-
lastBackendError = errorMsg;
91+
if (errorMsg) lastBackendError = errorMsg;
9392

9493
// Send error message to log window
9594
if (currentLogWindow && !currentLogWindow.isDestroyed()) {
@@ -111,9 +110,9 @@ async function startBackend(logWindow = null) {
111110
// Handle process exit
112111
backendProcess.on("close", (code) => {
113112
logger.backend.info(`Backend process exited with code ${code}`);
114-
// Show error dialog if process crashed (non-zero exit code)
113+
clearTimeout(startupTimer);
114+
115115
if (code !== 0 && code !== null) {
116-
// Build error message with actual error details
117116
let errorMessage = `Backend exited with code ${code}`;
118117

119118
if (lastBackendError) {
@@ -123,15 +122,16 @@ async function startBackend(logWindow = null) {
123122
}
124123

125124
dialog.showErrorBox("Backend Crashed", errorMessage);
126-
// Clear error message after showing
127125
lastBackendError = null;
126+
backendProcess = null;
127+
return reject(new Error(errorMessage));
128128
}
129129

130130
backendProcess = null;
131131
});
132132

133133
// If the backend didn't fail in this period of time, resolve the promise
134-
setTimeout(() => {
134+
const startupTimer = setTimeout(() => {
135135
resolve(backendProcess);
136136
}, 2000);
137137
});

electron-app/src/windows/mainWindow.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ function loadView(view) {
8181
// Construct path to view HTML file
8282
const viewPath = path.join(appPath, "renderer", view, "index.html");
8383

84+
if (!mainWindow || mainWindow.isDestroyed()) return;
85+
8486
// Check if view file exists
8587
if (fs.existsSync(viewPath)) {
8688
// Load the view HTML file

frontend/testing-view/src/features/charts/components/ChartLegend.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import { ChartSettings } from "./ChartSettings";
66
interface ChartLegendProps {
77
chartId: string;
88
series: WorkspaceChartSeries[];
9-
disabledIndices: Set<number>;
10-
onToggle: (index: number) => void;
11-
onRemove: (variable: string, index: number) => void;
9+
disabledVariables: Set<string>;
10+
onToggle: (seriesKey: string) => void;
11+
onRemove: (variable: string) => void;
1212
}
1313

1414
export const ChartLegend = ({
1515
chartId,
1616
series,
17-
disabledIndices,
17+
disabledVariables,
1818
onToggle,
1919
onRemove,
2020
}: ChartLegendProps) => (
@@ -25,9 +25,9 @@ export const ChartLegend = ({
2525
className="border-border flex items-center overflow-hidden rounded-md border shadow-sm transition-transform active:scale-95"
2626
>
2727
<button
28-
onClick={() => onToggle(i)}
28+
onClick={() => onToggle(p.variable)}
2929
className={`flex items-center gap-2 px-2.5 py-1 text-[10px] font-bold uppercase transition-colors ${
30-
disabledIndices.has(i)
30+
disabledVariables.has(p.variable)
3131
? "bg-muted text-muted-foreground grayscale"
3232
: "bg-background text-foreground hover:bg-accent"
3333
}`}
@@ -39,7 +39,8 @@ export const ChartLegend = ({
3939
{p.variable}
4040
</button>
4141
<button
42-
onClick={() => onRemove(p.variable, i)}
42+
title={`Remove variable ${p.variable}`}
43+
onClick={() => onRemove(p.variable)}
4344
className="border-border text-muted-foreground hover:bg-destructive/10 hover:text-destructive h-full border-l px-1.5 py-1 transition-colors"
4445
>
4546
<X className="h-3 w-3" />

frontend/testing-view/src/features/charts/components/ChartSurface.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import { createTooltipPlugin } from "./tooltipPlugin";
1414
interface ChartSurfaceProps {
1515
chartId: string;
1616
series: WorkspaceChartSeries[];
17-
disabledIndices: Set<number>;
17+
disabledVariables: Set<string>;
1818
}
1919

2020
// IMPORTANT: This component was almost completely vibe-coded
2121
// It could provoke bugs, thus it could be improved
2222

2323
export const ChartSurface = memo(
24-
({ chartId, series, disabledIndices }: ChartSurfaceProps) => {
24+
({ chartId, series, disabledVariables }: ChartSurfaceProps) => {
2525
const containerRef = useRef<HTMLDivElement>(null);
2626
const uplotRef = useRef<uPlot | null>(null);
2727
const historyRef = useRef<any[]>([]);
@@ -72,13 +72,13 @@ export const ChartSurface = memo(
7272
useEffect(() => {
7373
if (uplotRef.current) {
7474
// Index in series starts at 1 because 0 is the X-axis
75-
series.forEach((_, i) => {
75+
series.forEach((s, i) => {
7676
const seriesIdx = i + 1;
77-
const isVisible = !disabledIndices.has(i);
77+
const isVisible = !disabledVariables.has(s.variable);
7878
uplotRef.current?.setSeries(seriesIdx, { show: isVisible });
7979
});
8080
}
81-
}, [disabledIndices, series]);
81+
}, [disabledVariables, series]);
8282

8383
const handleDoubleClick = () => {
8484
setIsZooming(false);

frontend/testing-view/src/features/charts/components/TelemetryChart.tsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,25 @@ export const TelemetryChart = ({
4141
const removeChart = useStore((s) => s.removeChart);
4242
const removeSeries = useStore((s) => s.removeSeriesFromChart);
4343

44-
const [disabledIndices, setDisabledIndices] = useState<Set<number>>(
44+
const [disabledVariables, setDisabledVariables] = useState<Set<string>>(
4545
new Set(),
4646
);
4747

48-
const toggleSeries = (index: number) => {
49-
setDisabledIndices((prev) => {
48+
const toggleSeries = (variable: string) => {
49+
setDisabledVariables((prev) => {
5050
const next = new Set(prev);
51-
if (next.has(index)) next.delete(index);
52-
else next.add(index);
51+
if (next.has(variable)) next.delete(variable);
52+
else next.add(variable);
5353
return next;
5454
});
5555
};
5656

57-
const handleRemoveSeries = (variable: string, index: number) => {
57+
const handleRemoveSeries = (variable: string) => {
5858
if (!activeWorkspaceId) return;
5959
removeSeries(activeWorkspaceId, id, variable);
60-
setDisabledIndices((prev) => {
60+
setDisabledVariables((prev) => {
6161
const next = new Set(prev);
62-
next.delete(index);
62+
next.delete(variable);
6363
return next;
6464
});
6565
};
@@ -91,6 +91,7 @@ export const TelemetryChart = ({
9191

9292
{/* Delete Button */}
9393
<button
94+
title="Remove chart"
9495
onClick={() =>
9596
activeWorkspaceId && removeChart(activeWorkspaceId, id)
9697
}
@@ -103,15 +104,15 @@ export const TelemetryChart = ({
103104
<ChartLegend
104105
chartId={id}
105106
series={series}
106-
disabledIndices={disabledIndices}
107+
disabledVariables={disabledVariables}
107108
onToggle={toggleSeries}
108-
onRemove={(v, i) => handleRemoveSeries(v, i)}
109+
onRemove={handleRemoveSeries}
109110
/>
110111

111112
<ChartSurface
112113
chartId={id}
113114
series={series}
114-
disabledIndices={disabledIndices}
115+
disabledVariables={disabledVariables}
115116
/>
116117
</div>
117118
);

frontend/testing-view/src/features/charts/store/chartsSlice.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export interface ChartsSlice {
3333
variable: string,
3434
) => void;
3535

36+
clearCharts: () => void;
37+
3638
/** Sets new buffer size or history limit for a chart. */
3739
setChartHistoryLimit: (
3840
workspaceId: string,

0 commit comments

Comments
 (0)