diff --git a/frontend/testing-view/src/constants/boards.ts b/frontend/testing-view/src/constants/boards.ts
deleted file mode 100644
index 68a8a4127..000000000
--- a/frontend/testing-view/src/constants/boards.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-/** List of names of available boards. */
-export const BOARD_NAMES: readonly string[] = [
- "BCU", // Battery Control Unit
- "PCU", // Propulsion Control Unit
- "LCU", // Levitation Control Unit
- "HVSCU", // High Voltage System Control Unit
- "BMSL", // Battery Management System Level
- "VCU", // Vehicle Control Unit
- "HVSCU-Cabinet", // High Voltage System Control Unit Cabinet
-];
diff --git a/frontend/testing-view/src/constants/settingsSchema.ts b/frontend/testing-view/src/constants/settingsSchema.ts
index eecc75f85..28561ea8c 100644
--- a/frontend/testing-view/src/constants/settingsSchema.ts
+++ b/frontend/testing-view/src/constants/settingsSchema.ts
@@ -1,8 +1,8 @@
import type { SettingsSection } from "../types/common/settings";
-import { BOARD_NAMES } from "./boards";
+import type { BoardName } from "../types/data/board";
/** Settings form is generated from this schema. */
-export const SETTINGS_SCHEMA: SettingsSection[] = [
+export const getSettingsSchema = (boards: BoardName[]): SettingsSection[] => [
{
title: "Vehicle Configuration",
fields: [
@@ -10,7 +10,7 @@ export const SETTINGS_SCHEMA: SettingsSection[] = [
label: "Boards",
path: "vehicle.boards",
type: "multi-checkbox",
- options: BOARD_NAMES as string[],
+ options: boards,
},
],
},
diff --git a/frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx b/frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx
index 747f80aff..05f2a2467 100644
--- a/frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx
+++ b/frontend/testing-view/src/features/filtering/components/FilterCategoryItem.tsx
@@ -22,7 +22,7 @@ export const FilterCategoryItem = ({ category }: FilterCategoryItemProps) => {
const toggleCategoryFilter = useStore((s) => s.toggleCategoryFilter);
const toggleItemFilter = useStore((s) => s.toggleItemFilter);
- const items = useStore((s) => s.getCatalog(scope)[category]);
+ const items = useStore((s) => s.getCatalog(scope)[category]) || [];
const totalItems = items.length;
const selectedIds = useStore(
@@ -61,7 +61,7 @@ export const FilterCategoryItem = ({ category }: FilterCategoryItemProps) => {
- {items.map((item) => (
+ {items?.map((item) => (
{
const { isOpen, scope } = useStore((s) => s.filterDialog);
const close = useStore((s) => s.closeFilterDialog);
+ const boards = useStore((s) => s.boards);
+
const clearFilters = useStore((s) => s.clearFilters);
const selectAllFilters = useStore((s) => s.selectAllFilters);
@@ -20,7 +21,7 @@ export const FilterController = () => {
onClose={close}
onClearAll={() => clearFilters(scope)}
onSelectAll={() => selectAllFilters(scope)}
- categories={BOARD_NAMES}
+ categories={boards}
FilterCategoryComponent={FilterCategoryItem}
/>
);
diff --git a/frontend/testing-view/src/features/filtering/store/filteringSlice.ts b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
index 1bcc4cc6d..ecb047379 100644
--- a/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
+++ b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
@@ -135,7 +135,7 @@ export const createFilteringSlice: StateCreator<
const currentWorkspaceFilters = get().workspaceFilters[workspaceId] || {};
const currentTabFilter =
- currentWorkspaceFilters[scope] || createEmptyFilter();
+ currentWorkspaceFilters[scope] || createEmptyFilter(get().boards);
const currentCategoryIds = currentTabFilter[category] || [];
@@ -157,13 +157,13 @@ export const createFilteringSlice: StateCreator<
const items = get().getCatalog(scope);
- const fullFilter = createFullFilter(items);
+ const fullFilter = createFullFilter(items, get().boards);
get().updateFilters(scope, fullFilter);
},
clearFilters: (scope) => {
const workspaceId = get().getActiveWorkspaceId();
if (!workspaceId) return;
- const emptyFilter = createEmptyFilter();
+ const emptyFilter = createEmptyFilter(get().boards);
get().updateFilters(scope, emptyFilter);
},
toggleCategoryFilter: (scope, category, checked) => {
@@ -173,7 +173,8 @@ export const createFilteringSlice: StateCreator<
const catalog = get().getCatalog(scope);
const currentFilters =
- get().workspaceFilters[workspaceId]?.[scope] || createEmptyFilter();
+ get().workspaceFilters[workspaceId]?.[scope] ||
+ createEmptyFilter(get().boards);
const newItems = checked
? catalog?.[category]?.map((item) => item.id) || []
@@ -196,9 +197,9 @@ export const createFilteringSlice: StateCreator<
if (Object.keys(currentFilters).length === 0) {
set({
workspaceFilters: generateInitialFilters({
- commands: createFullFilter(commands),
- telemetry: createFullFilter(telemetry),
- logs: createFullFilter(telemetry),
+ commands: createFullFilter(commands, get().boards),
+ telemetry: createFullFilter(telemetry, get().boards),
+ logs: createFullFilter(telemetry, get().boards),
}),
});
}
diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/CommandsSection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/CommandsSection.tsx
index 60b1a0876..59b3dbaff 100644
--- a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/CommandsSection.tsx
+++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/CommandsSection.tsx
@@ -1,15 +1,19 @@
-import { BOARD_NAMES } from "../../../../../constants/boards";
+import { useStore } from "../../../../../store/store";
import type { CommandCatalogItem } from "../../../../../types/data/commandCatalogItem";
import { CommandItem } from "../tabs/commands/CommandItem";
import { Tab } from "../tabs/Tab";
-export const CommandsSection = () => (
- (
-
- )}
- />
-);
+export const CommandsSection = () => {
+ const boards = useStore((s) => s.boards);
+
+ return (
+ (
+
+ )}
+ />
+ );
+};
diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TelemetrySection.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TelemetrySection.tsx
index 4798da54d..3cb19cfce 100644
--- a/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TelemetrySection.tsx
+++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/sections/TelemetrySection.tsx
@@ -1,16 +1,20 @@
-import { BOARD_NAMES } from "../../../../../constants/boards";
+import { useStore } from "../../../../../store/store";
import type { TelemetryCatalogItem } from "../../../../../types/data/telemetryCatalogItem";
import { Tab } from "../tabs/Tab";
import { TelemetryItem } from "../tabs/telemetry/TelemetryItem";
-export const TelemetrySection = () => (
- (
-
- )}
- virtualized
- />
-);
+export const TelemetrySection = () => {
+ const boards = useStore((s) => s.boards);
+
+ return (
+ (
+
+ )}
+ virtualized
+ />
+ );
+};
diff --git a/frontend/testing-view/src/hooks/useBoardData.ts b/frontend/testing-view/src/hooks/useBoardData.ts
index 22ff3d8f9..3f40f4cfc 100644
--- a/frontend/testing-view/src/hooks/useBoardData.ts
+++ b/frontend/testing-view/src/hooks/useBoardData.ts
@@ -90,6 +90,8 @@ export function useBoardData(
logger.testingView.log("[useBoardData] Commands data processed");
+ console.log("availableBoards", availableBoards);
+
return {
telemetryCatalog: telemetryCatalogResult,
commandsCatalog: commandsCatalogResult,
diff --git a/frontend/testing-view/src/hooks/useTransformedBoards.ts b/frontend/testing-view/src/hooks/useTransformedBoards.ts
index c8fc04c40..aa7cda62b 100644
--- a/frontend/testing-view/src/hooks/useTransformedBoards.ts
+++ b/frontend/testing-view/src/hooks/useTransformedBoards.ts
@@ -12,6 +12,7 @@ export function useTransformedBoards(
const setTelemetryCatalog = useStore((s) => s.setTelemetryCatalog);
const setCommandsCatalog = useStore((s) => s.setCommandsCatalog);
+ const setBoards = useStore((s) => s.setBoards);
const initializeWorkspaceFilters = useStore(
(s) => s.initializeWorkspaceFilters,
);
@@ -25,7 +26,14 @@ export function useTransformedBoards(
setTelemetryCatalog(transformedBoards.telemetryCatalog);
setCommandsCatalog(transformedBoards.commandsCatalog);
- initializeWorkspaceFilters();
+ setBoards(Array.from(transformedBoards.boards));
+
+ const hasTelemetryData =
+ Object.keys(transformedBoards.telemetryCatalog).length > 0;
+ const hasCommandsData =
+ Object.keys(transformedBoards.commandsCatalog).length > 0;
+
+ if (hasTelemetryData && hasCommandsData) initializeWorkspaceFilters();
}, [
transformedBoards,
setTelemetryCatalog,
diff --git a/frontend/testing-view/src/lib/utils.ts b/frontend/testing-view/src/lib/utils.ts
index fe0306338..adbcdfc2f 100644
--- a/frontend/testing-view/src/lib/utils.ts
+++ b/frontend/testing-view/src/lib/utils.ts
@@ -1,5 +1,4 @@
import { ACRONYMS } from "../constants/acronyms";
-import { BOARD_NAMES } from "../constants/boards";
import { variablesBadgeClasses } from "../constants/variablesBadgeClasses";
import type {
FilterScope,
@@ -29,8 +28,8 @@ export const generateInitialFilters = (
);
};
-export const createEmptyFilter = (): TabFilter => {
- return BOARD_NAMES.reduce((acc, category) => {
+export const createEmptyFilter = (boards: BoardName[]): TabFilter => {
+ return boards.reduce((acc, category) => {
acc[category] = [];
return acc;
}, {} as TabFilter);
@@ -38,8 +37,9 @@ export const createEmptyFilter = (): TabFilter => {
export const createFullFilter = (
dataSource: Record,
+ boards: BoardName[],
): TabFilter => {
- return BOARD_NAMES.reduce((acc, category) => {
+ return boards.reduce((acc, category) => {
acc[category] = dataSource[category]?.map((item) => item.id) || [];
return acc;
}, {} as TabFilter);
diff --git a/frontend/testing-view/src/store/slices/catalogSlice.ts b/frontend/testing-view/src/store/slices/catalogSlice.ts
index e2e60017f..dcd365534 100644
--- a/frontend/testing-view/src/store/slices/catalogSlice.ts
+++ b/frontend/testing-view/src/store/slices/catalogSlice.ts
@@ -15,6 +15,10 @@ export interface CatalogSlice {
setTelemetryCatalog: (
telemetryCatalog: Record,
) => void;
+
+ // Boards
+ boards: BoardName[];
+ setBoards: (boards: BoardName[]) => void;
}
export const createCatalogSlice: StateCreator = (
@@ -24,4 +28,6 @@ export const createCatalogSlice: StateCreator = (
telemetryCatalog: {} as Record,
setCommandsCatalog: (commandsCatalog) => set({ commandsCatalog }),
setTelemetryCatalog: (telemetryCatalog) => set({ telemetryCatalog }),
+ boards: [] as BoardName[],
+ setBoards: (boards) => set({ boards }),
});
diff --git a/packet-sender/package.json b/packet-sender/package.json
index 4842b09d2..d96551e08 100644
--- a/packet-sender/package.json
+++ b/packet-sender/package.json
@@ -7,7 +7,6 @@
"scripts": {
"build": "go build -o packet-sender main.go",
"build:ci": "go build",
- "dev": "go run main.go",
"test": "go test ./..."
}
}
From 8168f40a2d2420879ac3a0e7b9c650b858863108 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 13:28:37 +0100
Subject: [PATCH 05/24] feat: increase backend resolving time
---
electron-app/src/processes/backend.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/electron-app/src/processes/backend.js b/electron-app/src/processes/backend.js
index 1e215619d..c994a94e3 100644
--- a/electron-app/src/processes/backend.js
+++ b/electron-app/src/processes/backend.js
@@ -86,7 +86,7 @@ function startBackend() {
// If the backend didn't fail in this period of time, resolve the promise
setTimeout(() => {
resolve(backendProcess);
- }, 1000);
+ }, 2000);
// Handle process exit
backendProcess.on("close", (code) => {
From b6e9f00a79ce141cc3e843a34e8a7fc33c302cb7 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 15:37:34 +0100
Subject: [PATCH 06/24] fix: workflows
---
.github/workflows/build.yaml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 2564f79db..37d11b4c4 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -57,7 +57,6 @@ jobs:
- uses: dorny/paths-filter@v3
id: filter
with:
- ref: "production"
filters: |
backend:
- 'backend/**/*'
From eb85a77697d21bc1e1ec7b7b6802f7a6a38d09bb Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 15:41:31 +0100
Subject: [PATCH 07/24] fix
---
.github/workflows/build.yaml | 2 +-
.../workspace/store/workspacesSlice.ts | 6 ++---
frontend/testing-view/src/lib/utils.test.ts | 24 +++++++++++++++++--
3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 37d11b4c4..dbbfce409 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -112,7 +112,7 @@ jobs:
with:
workflow: build.yaml
branch: production
- workflow_conclusion: success
+ workflow_conclusion: completed
name: backend-${{ matrix.platform }}
path: backend/cmd
diff --git a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
index ea09a6844..28a1be861 100644
--- a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
+++ b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
@@ -63,9 +63,9 @@ export const createWorkspacesSlice: StateCreator<
const newWorkspaceFilters = {
...state.workspaceFilters,
[newWorkspaceId]: {
- commands: createFullFilter(commands),
- telemetry: createFullFilter(telemetry),
- logs: createFullFilter(telemetry),
+ commands: createFullFilter(commands, get().boards),
+ telemetry: createFullFilter(telemetry, get().boards),
+ logs: createFullFilter(telemetry, get().boards),
},
};
diff --git a/frontend/testing-view/src/lib/utils.test.ts b/frontend/testing-view/src/lib/utils.test.ts
index 947b6c5c7..fa667af7a 100644
--- a/frontend/testing-view/src/lib/utils.test.ts
+++ b/frontend/testing-view/src/lib/utils.test.ts
@@ -105,7 +105,17 @@ describe("getTypeBadgeClass", () => {
describe("emptyFilter", () => {
it("should return the correct empty filter", () => {
- expect(createEmptyFilter()).toStrictEqual({
+ const boards = [
+ "BCU",
+ "PCU",
+ "LCU",
+ "HVSCU",
+ "HVSCU-Cabinet",
+ "BMSL",
+ "VCU",
+ ];
+
+ expect(createEmptyFilter(boards)).toStrictEqual({
BCU: [],
PCU: [],
LCU: [],
@@ -133,7 +143,17 @@ describe("fullFilter", () => {
VCU: [],
};
- expect(createFullFilter(testDataSource)).toStrictEqual({
+ const boards = [
+ "BCU",
+ "PCU",
+ "LCU",
+ "HVSCU",
+ "HVSCU-Cabinet",
+ "BMSL",
+ "VCU",
+ ];
+
+ expect(createFullFilter(testDataSource, boards)).toStrictEqual({
BCU: [1],
PCU: [2],
LCU: [3],
From 47156f5f16bc67f45f3a6128db3cf37aa77b6882 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 15:54:52 +0100
Subject: [PATCH 08/24] Update build.yaml
---
.github/workflows/build.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index dbbfce409..c6a335ac0 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -53,6 +53,8 @@ jobs:
competition-view: ${{ steps.filter.outputs.competition-view == 'true' || github.event.inputs.rebuild-competition-view == 'true' || inputs.build-competition-view == true }}
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
- uses: dorny/paths-filter@v3
id: filter
From 7ccfe4ba9907591c5ab0f1f09a1abe6327754ca2 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 15:56:16 +0100
Subject: [PATCH 09/24] check
---
.../testing-view/src/features/workspace/store/workspacesSlice.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
index 28a1be861..191ab3c75 100644
--- a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
+++ b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
@@ -68,6 +68,7 @@ export const createWorkspacesSlice: StateCreator<
logs: createFullFilter(telemetry, get().boards),
},
};
+ // test
// Initialize expanded items for the new workspace
const newExpandedItems = {
From 913fe95a44b5a683694812720229852d20d03d47 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 16:01:40 +0100
Subject: [PATCH 10/24] Update build.yaml
---
.github/workflows/build.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index c6a335ac0..516ddbdc7 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -59,6 +59,7 @@ jobs:
- uses: dorny/paths-filter@v3
id: filter
with:
+ base: ${{ github.event.before }}
filters: |
backend:
- 'backend/**/*'
From 322c199d92259d52570f0e9b2a9634f9e027d850 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Fri, 20 Feb 2026 16:02:37 +0100
Subject: [PATCH 11/24] fix
---
.../testing-view/src/features/workspace/store/workspacesSlice.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
index 191ab3c75..28a1be861 100644
--- a/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
+++ b/frontend/testing-view/src/features/workspace/store/workspacesSlice.ts
@@ -68,7 +68,6 @@ export const createWorkspacesSlice: StateCreator<
logs: createFullFilter(telemetry, get().boards),
},
};
- // test
// Initialize expanded items for the new workspace
const newExpandedItems = {
From c55f0ac82508e3d521a45e01554d24d0a075ac5b Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 20:43:31 +0100
Subject: [PATCH 12/24] feat: add building to frontend testing
---
.github/workflows/frontend-tests.yaml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/workflows/frontend-tests.yaml b/.github/workflows/frontend-tests.yaml
index 149d955df..0599a5097 100644
--- a/.github/workflows/frontend-tests.yaml
+++ b/.github/workflows/frontend-tests.yaml
@@ -41,5 +41,8 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile --filter=testing-view --filter=ui --filter=core
+ - name: Build frontend
+ run: pnpm build --filter="./frontend/**"
+
- name: Run tests
run: pnpm test --filter="./frontend/**"
From 225cbe5a2c23df9abadea0b8b44aa34a146ad8b9 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 20:54:13 +0100
Subject: [PATCH 13/24] feat: increase backend resolving time
---
electron-app/src/processes/backend.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/electron-app/src/processes/backend.js b/electron-app/src/processes/backend.js
index c994a94e3..747336e3d 100644
--- a/electron-app/src/processes/backend.js
+++ b/electron-app/src/processes/backend.js
@@ -86,7 +86,7 @@ function startBackend() {
// If the backend didn't fail in this period of time, resolve the promise
setTimeout(() => {
resolve(backendProcess);
- }, 2000);
+ }, 4000);
// Handle process exit
backendProcess.on("close", (code) => {
From 6840db855b67b7bf8560122fe930c48b1d800689 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 21:11:57 +0100
Subject: [PATCH 14/24] feat: include rpm and pacman distributives
---
electron-app/package.json | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/electron-app/package.json b/electron-app/package.json
index a37d20f36..30344aa16 100644
--- a/electron-app/package.json
+++ b/electron-app/package.json
@@ -106,7 +106,9 @@
"linux": {
"target": [
"AppImage",
- "deb"
+ "deb",
+ "rpm",
+ "pacman"
],
"icon": "icons/512x512.png",
"category": "Utility",
From 4740414d812f30fca609c7989d171cdd12e88ac4 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 21:20:39 +0100
Subject: [PATCH 15/24] fix: include dependencies and new files
---
.github/workflows/release.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index abcc3c225..01f375565 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -86,6 +86,12 @@ jobs:
echo "Updated version to:"
cat package.json | grep version
+ - name: Install Linux build dependencies
+ if: runner.os == 'Linux'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y rpm libarchive-tools
+
# Download ONLY the appropriate backend for this platform
- name: Download Linux backend
if: runner.os == 'Linux'
@@ -182,6 +188,8 @@ jobs:
electron-app/dist/*.exe
electron-app/dist/*.AppImage
electron-app/dist/*.deb
+ electron-app/dist/*.rpm
+ electron-app/dist/*.pacman
electron-app/dist/*.dmg
electron-app/dist/*.zip
electron-app/dist/*.yml
From 33630b216013516a9d2cef649e40851ccd5cb713 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 21:30:59 +0100
Subject: [PATCH 16/24] Update README.md
---
electron-app/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/electron-app/README.md b/electron-app/README.md
index 854930745..a881da8e5 100644
--- a/electron-app/README.md
+++ b/electron-app/README.md
@@ -79,7 +79,7 @@ pnpm run dist:linux # Linux
On macOS, the backend requires the loopback address `127.0.0.9` to be configured. If you encounter a "can't assign requested address" error when starting the backend, run:
```
-sudo ipconfig set en0 INFORM 127.0.0.9
+sudo ifconfig lo0 alias 127.0.0.9 up
```
## Available Scripts
From ee6441cdc57928ab8886e616febfdcbe9a49488f Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sat, 21 Feb 2026 21:33:43 +0100
Subject: [PATCH 17/24] Update README.md
---
electron-app/README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/electron-app/README.md b/electron-app/README.md
index a881da8e5..7034c163e 100644
--- a/electron-app/README.md
+++ b/electron-app/README.md
@@ -89,6 +89,7 @@ sudo ifconfig lo0 alias 127.0.0.9 up
- `pnpm start` - Run application in development mode
- `pnpm run dist` - Build production executable
- `pnpm test` - Run tests
+- `pnpm build-icons` - build icon from the icon.png file in the `/electron-app` folder
...and many custom variations (see package.json)
# Only works and makes sense after running `pnpm run dist`
From ef5bd72490e123896ec9e82c74c66fedacbdd2e7 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Sun, 22 Feb 2026 13:34:46 +0100
Subject: [PATCH 18/24] feat: add stale boards indication
---
.../filtering/components/FilterController.tsx | 6 +++
.../filtering/components/FilterDialog.tsx | 30 ++++++++++-
.../filtering/store/filteringSlice.ts | 3 +-
.../rightSidebar/tabs/TabHeader.tsx | 53 +++++++++++++------
frontend/testing-view/src/lib/utils.ts | 8 +++
5 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/frontend/testing-view/src/features/filtering/components/FilterController.tsx b/frontend/testing-view/src/features/filtering/components/FilterController.tsx
index 538cef4a9..fdea53a8d 100644
--- a/frontend/testing-view/src/features/filtering/components/FilterController.tsx
+++ b/frontend/testing-view/src/features/filtering/components/FilterController.tsx
@@ -1,3 +1,5 @@
+import { useShallow } from "zustand/shallow";
+import { detectExtraBoards } from "../../../lib/utils";
import { useStore } from "../../../store/store";
import { FilterCategoryItem } from "./FilterCategoryItem";
import { FilterDialog } from "./FilterDialog";
@@ -7,12 +9,15 @@ export const FilterController = () => {
const close = useStore((s) => s.closeFilterDialog);
const boards = useStore((s) => s.boards);
+ const activeFilters = useStore(useShallow((s) => s.getActiveFilters(scope)));
const clearFilters = useStore((s) => s.clearFilters);
const selectAllFilters = useStore((s) => s.selectAllFilters);
if (!scope) return null;
+ const extraBoards = detectExtraBoards(activeFilters, boards);
+
return (
{
onClearAll={() => clearFilters(scope)}
onSelectAll={() => selectAllFilters(scope)}
categories={boards}
+ extraCategories={extraBoards}
FilterCategoryComponent={FilterCategoryItem}
/>
);
diff --git a/frontend/testing-view/src/features/filtering/components/FilterDialog.tsx b/frontend/testing-view/src/features/filtering/components/FilterDialog.tsx
index 6c72ba88c..f211b2b33 100644
--- a/frontend/testing-view/src/features/filtering/components/FilterDialog.tsx
+++ b/frontend/testing-view/src/features/filtering/components/FilterDialog.tsx
@@ -6,6 +6,7 @@ import {
DialogHeader,
DialogTitle,
} from "@workspace/ui";
+import { AlertTriangle } from "@workspace/ui/icons";
import { type ComponentType } from "react";
import type { BoardName } from "../../../types/data/board";
@@ -17,6 +18,7 @@ interface FilterDialogProps {
onClearAll: () => void;
onSelectAll: () => void;
categories: readonly BoardName[];
+ extraCategories: readonly BoardName[];
FilterCategoryComponent: ComponentType<{ category: BoardName }>;
}
@@ -28,11 +30,13 @@ export const FilterDialog = ({
onClearAll,
onSelectAll,
categories,
+ extraCategories,
FilterCategoryComponent,
}: FilterDialogProps) => {
+ console.log(extraCategories);
return (
+ {extraCategories.length > 0 && (
+
+
+
+ Stale filters detected
+
+
+ The following boards are in your saved filters but not in the
+ current configuration:{" "}
+
+ {extraCategories.join(", ")}
+
+
+
+
+ )}
+
{categories.map((category) => (
diff --git a/frontend/testing-view/src/features/filtering/store/filteringSlice.ts b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
index ecb047379..58cefaccc 100644
--- a/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
+++ b/frontend/testing-view/src/features/filtering/store/filteringSlice.ts
@@ -38,7 +38,7 @@ export interface FilteringSlice {
workspaceFilters: Record
;
initializeWorkspaceFilters: () => void;
updateFilters: (scope: FilterScope, filters: TabFilter) => void;
- getActiveFilters: (scope: FilterScope) => TabFilter | undefined;
+ getActiveFilters: (scope: FilterScope | null) => TabFilter | undefined;
/** Filter Actions */
selectAllFilters: (scope: FilterScope) => void;
@@ -229,6 +229,7 @@ export const createFilteringSlice: StateCreator<
// Helper getters
getActiveFilters: (scope) => {
const id = get().getActiveWorkspaceId();
+ if (!scope) return {};
return id ? get().workspaceFilters[id]?.[scope] : undefined;
},
diff --git a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx
index 08730d14c..e93c10110 100644
--- a/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx
+++ b/frontend/testing-view/src/features/workspace/components/rightSidebar/tabs/TabHeader.tsx
@@ -1,5 +1,7 @@
import { Button } from "@workspace/ui";
-import { ListFilterPlus } from "@workspace/ui/icons";
+import { AlertTriangle, ListFilterPlus } from "@workspace/ui/icons";
+import { useShallow } from "zustand/shallow";
+import { detectExtraBoards } from "../../../../../lib/utils";
import { useStore } from "../../../../../store/store";
import type { SidebarTab } from "../../../types/sidebar";
@@ -13,23 +15,40 @@ export const TabHeader = ({ title, scope }: TabHeaderProps) => {
const totalCount = useStore((state) => state.getTotalCount(scope));
const filteredCount = useStore((state) => state.getFilteredCount(scope));
+ const boards = useStore((s) => s.boards);
+ const activeFilters = useStore(useShallow((s) => s.getActiveFilters(scope)));
+ const extraBoards = detectExtraBoards(activeFilters, boards);
+
return (
-
-
- {title}
-
- {filteredCount} / {totalCount}
-
-
-
+
+
+
+ {title}
+
+ {filteredCount} / {totalCount}
+
+
+
+
+
+ {/* Warning for stale boards */}
+ {extraBoards.length > 0 && (
+
+
+
{extraBoards.length} stale board(s) affecting counts
+
+ )}
);
};
diff --git a/frontend/testing-view/src/lib/utils.ts b/frontend/testing-view/src/lib/utils.ts
index adbcdfc2f..0df30e9c3 100644
--- a/frontend/testing-view/src/lib/utils.ts
+++ b/frontend/testing-view/src/lib/utils.ts
@@ -119,3 +119,11 @@ export const formatTimestamp = (ts: MessageTimestamp) => {
if (!ts) return "00:00:00";
return `${ts.hour.toString().padStart(2, "0")}:${ts.minute.toString().padStart(2, "0")}:${ts.second.toString().padStart(2, "0")}`;
};
+
+export const detectExtraBoards = (
+ activeFilters: TabFilter | undefined,
+ boards: BoardName[],
+) =>
+ Object.keys(activeFilters || {}).filter(
+ (key) => !boards.includes(key),
+ ) as BoardName[];
From e2fc74e5c45e76216556df70f34461db2ab1e779 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 16:57:43 +0100
Subject: [PATCH 19/24] feat: add backend logs window to electron
---
electron-app/main.js | 16 +++++++++---
electron-app/package.json | 1 +
electron-app/preload.js | 3 +++
electron-app/src/processes/backend.js | 34 +++++++++++++++++++++++---
electron-app/src/windows/logWindow.js | 26 ++++++++++++++++++++
electron-app/src/windows/mainWindow.js | 14 +++++++----
pnpm-lock.yaml | 17 +++++++++++++
7 files changed, 100 insertions(+), 11 deletions(-)
create mode 100644 electron-app/src/windows/logWindow.js
diff --git a/electron-app/main.js b/electron-app/main.js
index fd7a0a40d..ea52752c4 100644
--- a/electron-app/main.js
+++ b/electron-app/main.js
@@ -4,13 +4,14 @@
* Handles application lifecycle, initialization, and cleanup of processes and windows.
*/
-import { app, BrowserWindow, dialog } from "electron";
+import { app, BrowserWindow, dialog, screen } from "electron";
import pkg from "electron-updater";
import { getConfigManager } from "./src/config/configInstance.js";
import { setupIpcHandlers } from "./src/ipc/handlers.js";
import { startBackend, stopBackend } from "./src/processes/backend.js";
import { stopPacketSender } from "./src/processes/packetSender.js";
import { logger } from "./src/utils/logger.js";
+import { createLogWindow } from "./src/windows/logWindow.js";
import { createWindow } from "./src/windows/mainWindow.js";
const { autoUpdater } = pkg;
@@ -38,15 +39,22 @@ app.setName("hyperloop-control-station");
// App lifecycle: wait for Electron to be ready
app.whenReady().then(async () => {
+ // Get the screen width and height
+ // Only can be used inside app.whenReady()
+ const { width: screenWidth, height: screenHeight } =
+ screen.getPrimaryDisplay().workAreaSize;
+
// Initialize ConfigManager and ensure config exists BEFORE starting backend
logger.electron.header("Initializing configuration...");
// Get ConfigManager instance (creates config from template if needed)
await getConfigManager();
logger.electron.header("Configuration ready");
+ const logWindow = createLogWindow(screenWidth, screenHeight);
+
// Start backend process
try {
- await startBackend();
+ await startBackend(logWindow);
logger.electron.header("Backend process spawned");
} catch (error) {
// Start backend already shows these errors
@@ -54,7 +62,9 @@ app.whenReady().then(async () => {
}
// Create main application window
- createWindow();
+ const mainWindow = createWindow(screenWidth, screenHeight);
+ mainWindow.maximize();
+
logger.electron.header("Main application window created");
// Updater setup
diff --git a/electron-app/package.json b/electron-app/package.json
index 30344aa16..c5957648d 100644
--- a/electron-app/package.json
+++ b/electron-app/package.json
@@ -38,6 +38,7 @@
},
"dependencies": {
"@iarna/toml": "^2.2.5",
+ "ansi-to-html": "^0.7.2",
"electron-store": "^11.0.2",
"electron-updater": "^6.7.3",
"picocolors": "^1.1.1"
diff --git a/electron-app/preload.js b/electron-app/preload.js
index 761fa8dcf..2bc27909c 100644
--- a/electron-app/preload.js
+++ b/electron-app/preload.js
@@ -36,4 +36,7 @@ contextBridge.exposeInMainWorld("electronAPI", {
importConfig: () => ipcRenderer.invoke("import-config"),
// Open folder selection dialog
selectFolder: () => ipcRenderer.invoke("select-folder"),
+ // Receive log message from backend
+ onLog: (callback) =>
+ ipcRenderer.on("log", (_event, value) => callback(value)),
});
diff --git a/electron-app/src/processes/backend.js b/electron-app/src/processes/backend.js
index 747336e3d..9075f3b45 100644
--- a/electron-app/src/processes/backend.js
+++ b/electron-app/src/processes/backend.js
@@ -4,6 +4,7 @@
* Handles starting, stopping, and restarting the backend process with proper error handling and logging.
*/
+import AnsiToHtml from "ansi-to-html";
import { spawn } from "child_process";
import { app, dialog } from "electron";
import fs from "fs";
@@ -15,6 +16,9 @@ import {
getUserConfigPath,
} from "../utils/paths.js";
+// Create ANSI to HTML converter
+const convert = new AnsiToHtml();
+
// Get the application root path
const appPath = getAppPath();
@@ -30,7 +34,7 @@ let lastBackendError = null;
* @example
* startBackend();
*/
-function startBackend() {
+function startBackend(logWindow = null) {
return new Promise((resolve, reject) => {
// Get paths for binary and config
const backendBin = getBinaryPath("backend");
@@ -63,6 +67,12 @@ function startBackend() {
// Log stdout output from backend
backendProcess.stdout.on("data", (data) => {
logger.backend.info(`${data.toString().trim()}`);
+
+ // Send log message to log window
+ if (logWindow) {
+ const htmlData = convert.toHtml(data.toString().trim());
+ logWindow.webContents.send("log", htmlData);
+ }
});
// Capture stderr output (where Go errors/panics are written)
@@ -71,6 +81,12 @@ function startBackend() {
logger.backend.error(errorMsg);
// Store the last error message
lastBackendError = errorMsg;
+
+ // Send error message to log window
+ if (logWindow) {
+ const htmlError = convert.toHtml(errorMsg);
+ logWindow.webContents.send("log", htmlError);
+ }
});
// Handle spawn errors
@@ -120,8 +136,20 @@ function stopBackend() {
// Only stop if process exists and is still running
if (backendProcess && !backendProcess.killed) {
logger.backend.info("Stopping backend...");
- // Send termination signal
- backendProcess.kill("SIGTERM");
+
+ backendProcess.stdin.end();
+
+ const fallbackTimer = setTimeout(() => {
+ if (backendProcess && !backendProcess.killed) {
+ logger.backend.warning(
+ "Backend did not exit gracefully, force killing..."
+ );
+ backendProcess.kill("SIGKILL");
+ }
+ }, 2000);
+
+ fallbackTimer.unref();
+
// Clear the process reference
backendProcess = null;
}
diff --git a/electron-app/src/windows/logWindow.js b/electron-app/src/windows/logWindow.js
new file mode 100644
index 000000000..7aac0e928
--- /dev/null
+++ b/electron-app/src/windows/logWindow.js
@@ -0,0 +1,26 @@
+import { BrowserWindow } from "electron";
+import path from "path";
+
+import { getAppPath } from "../utils/paths.js";
+
+// Get the application root path
+const appPath = getAppPath();
+
+export const createLogWindow = (screenWidth, screenHeight) => {
+ const logWindow = new BrowserWindow({
+ x: Math.floor(screenWidth * 0.65),
+ y: 0,
+ width: Math.floor(screenWidth * 0.35),
+ height: screenHeight,
+ title: "Backend Logs",
+ webPreferences: {
+ preload: path.join(appPath, "preload.js"),
+ contextIsolation: true,
+ nodeIntegration: false,
+ },
+ });
+
+ logWindow.loadFile(path.join(appPath, "src/logs/logs.html"));
+
+ return logWindow;
+};
diff --git a/electron-app/src/windows/mainWindow.js b/electron-app/src/windows/mainWindow.js
index 259f8dc79..213dc03e9 100644
--- a/electron-app/src/windows/mainWindow.js
+++ b/electron-app/src/windows/mainWindow.js
@@ -24,13 +24,15 @@ let currentView = "testing-view";
* @example
* createWindow();
*/
-function createWindow() {
+function createWindow(screenWidth, screenHeight) {
// Create new browser window with configuration
mainWindow = new BrowserWindow({
- width: 1920,
- height: 1080,
- minWidth: 1280,
- minHeight: 720,
+ x: 0,
+ y: 0,
+ width: screenWidth,
+ height: screenHeight,
+ minWidth: 800,
+ minHeight: 600,
webPreferences: {
// Path to preload script for secure IPC
preload: path.join(appPath, "preload.js"),
@@ -60,6 +62,8 @@ function createWindow() {
mainWindow.on("closed", () => {
mainWindow = null;
});
+
+ return mainWindow;
}
/**
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9698bd788..29d83d227 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -34,6 +34,9 @@ importers:
'@iarna/toml':
specifier: ^2.2.5
version: 2.2.5
+ ansi-to-html:
+ specifier: ^0.7.2
+ version: 0.7.2
electron-store:
specifier: ^11.0.2
version: 11.0.2
@@ -1805,6 +1808,11 @@ packages:
resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
engines: {node: '>=12'}
+ ansi-to-html@0.7.2:
+ resolution: {integrity: sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==}
+ engines: {node: '>=8.0.0'}
+ hasBin: true
+
app-builder-bin@4.0.0:
resolution: {integrity: sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==}
@@ -2392,6 +2400,9 @@ packages:
resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==}
engines: {node: '>=10.13.0'}
+ entities@2.2.0:
+ resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
+
env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -6218,6 +6229,10 @@ snapshots:
ansi-styles@6.2.3: {}
+ ansi-to-html@0.7.2:
+ dependencies:
+ entities: 2.2.0
+
app-builder-bin@4.0.0: {}
app-builder-bin@5.0.0-alpha.12: {}
@@ -7045,6 +7060,8 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.0
+ entities@2.2.0: {}
+
env-paths@2.2.1: {}
env-paths@3.0.0: {}
From df01f6b6ebfb90f461e7ba752257a1a0a329f1b9 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 17:40:35 +0100
Subject: [PATCH 20/24] feat: fix menus
---
electron-app/src/menu/menu.js | 14 +++++++++++---
electron-app/src/windows/mainWindow.js | 3 ++-
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/electron-app/src/menu/menu.js b/electron-app/src/menu/menu.js
index 64f776da9..c2000533d 100644
--- a/electron-app/src/menu/menu.js
+++ b/electron-app/src/menu/menu.js
@@ -30,7 +30,11 @@ function createMenu(mainWindow) {
{
label: "Reload",
accelerator: "CmdOrCtrl+R",
- click: () => mainWindow.reload(),
+ click: (_, browserWindow) => {
+ if (browserWindow) {
+ browserWindow.reload();
+ }
+ },
},
{ type: "separator" },
{
@@ -61,7 +65,11 @@ function createMenu(mainWindow) {
{
label: "Toggle DevTools",
accelerator: "F12",
- click: () => mainWindow.webContents.toggleDevTools(),
+ click: (_, browserWindow) => {
+ if (browserWindow) {
+ browserWindow.webContents.toggleDevTools();
+ }
+ },
},
],
},
@@ -118,7 +126,7 @@ function createMenu(mainWindow) {
];
const menu = Menu.buildFromTemplate(template);
- Menu.setApplicationMenu(menu);
+ return menu;
}
export { createMenu };
diff --git a/electron-app/src/windows/mainWindow.js b/electron-app/src/windows/mainWindow.js
index 213dc03e9..7b9d8a5f0 100644
--- a/electron-app/src/windows/mainWindow.js
+++ b/electron-app/src/windows/mainWindow.js
@@ -51,7 +51,8 @@ function createWindow(screenWidth, screenHeight) {
loadView(currentView);
// Create application menu
- createMenu(mainWindow);
+ const menu = createMenu(mainWindow);
+ mainWindow.setApplicationMenu(menu);
// Open DevTools in development mode
if (!app.isPackaged) {
From 050b5112f269d08615e3fe1378e74473695fe223 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 17:43:33 +0100
Subject: [PATCH 21/24] feat: update README.md
---
electron-app/src/windows/README.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/electron-app/src/windows/README.md b/electron-app/src/windows/README.md
index bb4e4ec5a..c13435b7a 100644
--- a/electron-app/src/windows/README.md
+++ b/electron-app/src/windows/README.md
@@ -4,24 +4,25 @@ Window management module for the Electron application. Handles creation, configu
## Overview
-Manages the primary Electron `BrowserWindow` instance and provides functionality for switching between different application views (Competition View and Testing View).
+Manages the primary and logs Electron `BrowserWindow` instances and provides functionality for switching between different application views (Competition View and Testing View).
## Files
+- `logWindow.js` - Backend logs and messages
- `mainWindow.js` - Main window creation and management
## Window Configuration
- **Default Size**: 1920x1080 pixels
-- **Minimum Size**: 1280x720 pixels
+- **Minimum Size**: 800x600 pixels
- **Title**: "Hyperloop Control Station"
- **Background Color**: `#1a1a1a` (dark theme)
- **Security**: Context isolation enabled, node integration disabled
## Available Views
-- **Ethernet View** (default) - Testing interface, loads from `renderer/ethernet-view/index.html`
-- **Control Station** - Competition interface, loads from `renderer/control-station/index.html`
+- **Testing View** (default) - Testing interface, loads from `renderer/testing-view/index.html`
+- **Competition View** - Competition interface, loads from `renderer/competition-view/index.html`
## Functions
From e4ce2d00393124c62adf495127696585685ab274 Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 18:03:40 +0100
Subject: [PATCH 22/24] fix: logs path
---
electron-app/src/windows/logWindow.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/electron-app/src/windows/logWindow.js b/electron-app/src/windows/logWindow.js
index 7aac0e928..fe0d09106 100644
--- a/electron-app/src/windows/logWindow.js
+++ b/electron-app/src/windows/logWindow.js
@@ -20,7 +20,8 @@ export const createLogWindow = (screenWidth, screenHeight) => {
},
});
- logWindow.loadFile(path.join(appPath, "src/logs/logs.html"));
+ const logFilePath = path.join(appPath, "src", "logs", "logs.html");
+ logWindow.loadFile(logFilePath);
return logWindow;
};
From e804e4b9d472d09ca94f602f1c5f657f5b3382ab Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 18:33:52 +0100
Subject: [PATCH 23/24] feat: rename folders
---
electron-app/src/log-viewer/index.html | 34 ++++++++++++++++++++++++++
electron-app/src/windows/logWindow.js | 2 +-
electron-app/src/windows/mainWindow.js | 2 +-
3 files changed, 36 insertions(+), 2 deletions(-)
create mode 100644 electron-app/src/log-viewer/index.html
diff --git a/electron-app/src/log-viewer/index.html b/electron-app/src/log-viewer/index.html
new file mode 100644
index 000000000..2513be5f8
--- /dev/null
+++ b/electron-app/src/log-viewer/index.html
@@ -0,0 +1,34 @@
+
+
+
+
Backend Logs
+
+
+
+
+
+
+
+
diff --git a/electron-app/src/windows/logWindow.js b/electron-app/src/windows/logWindow.js
index fe0d09106..4c1c0e27e 100644
--- a/electron-app/src/windows/logWindow.js
+++ b/electron-app/src/windows/logWindow.js
@@ -20,7 +20,7 @@ export const createLogWindow = (screenWidth, screenHeight) => {
},
});
- const logFilePath = path.join(appPath, "src", "logs", "logs.html");
+ const logFilePath = path.join(appPath, "src", "log-viewer", "index.html");
logWindow.loadFile(logFilePath);
return logWindow;
diff --git a/electron-app/src/windows/mainWindow.js b/electron-app/src/windows/mainWindow.js
index 7b9d8a5f0..a6b41380c 100644
--- a/electron-app/src/windows/mainWindow.js
+++ b/electron-app/src/windows/mainWindow.js
@@ -52,7 +52,7 @@ function createWindow(screenWidth, screenHeight) {
// Create application menu
const menu = createMenu(mainWindow);
- mainWindow.setApplicationMenu(menu);
+ mainWindow.setMenu(menu);
// Open DevTools in development mode
if (!app.isPackaged) {
From 8b64a887353f1a59378c04c36779b175973b378a Mon Sep 17 00:00:00 2001
From: Maxim <74974283+maximka76667@users.noreply.github.com>
Date: Tue, 24 Feb 2026 18:44:50 +0100
Subject: [PATCH 24/24] fix: scrolling
---
electron-app/src/log-viewer/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/electron-app/src/log-viewer/index.html b/electron-app/src/log-viewer/index.html
index 2513be5f8..c9496d24a 100644
--- a/electron-app/src/log-viewer/index.html
+++ b/electron-app/src/log-viewer/index.html
@@ -27,7 +27,7 @@
// Use innerHTML to render the colors, but be careful of XSS if logs are untrusted
entry.innerHTML = data;
container.appendChild(entry);
- container.scrollTop = container.scrollHeight;
+ window.scrollTo(0, document.body.scrollHeight);
});