From 1ca87be4a1e3bf0c2d88303aed044aaf12f3c9f0 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 13:52:38 -0400 Subject: [PATCH 01/32] Step 1,2 --- ui/src/Map.svelte | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index c9e303c..d4ca933 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -27,6 +27,8 @@ import { getCoordinates, getNodeById, getNodeName, getNodeNameById, setPosition } from './lib/util' import { showConfigModal, showPage } from './SettingsModal.svelte' import { newsVisible } from './News.svelte' + import { fromLonLat } from 'ol/proj' + import { plotTrail } from './lib/OpenLayersMap.svelte' export let ol: OpenLayersMap = undefined @@ -68,6 +70,36 @@ } let modalPage = 'Settings' + + let trailArray: { coords: [number, number]; ts: number }[] = [] + let pendingTrail = false + let timeWindowMs = 6 * 3600 * 1000 // default 6 hours + + function pruneOldPoints() { + const cutoff = Date.now() - timeWindowMs + trailArray = trailArray.filter((p) => p.ts >= cutoff) + } + + function scheduleTrailUpdate() { + if (pendingTrail) return + pendingTrail = true + requestAnimationFrame(() => { + const coordsTransformed = trailArray.map((p) => fromLonLat(p.coords)) + plotTrail(coordsTransformed) + pendingTrail = false + }) + } + + // Example: inside your node update logic + const { lon, lat } = /* your code fetching the new coordinate */ + trailArray.push({ coords: [lon, lat], ts: Date.now() }) + pruneOldPoints() + scheduleTrailUpdate() + + $: if (ol) { + plotTrail([]) + // ...existing plotData() or other init calls... + } From 4db50dd3bd4ad95f7126e56cfa364ffc200e849a Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 14:14:08 -0400 Subject: [PATCH 02/32] Basic implementation maybe? --- ui/src/Map.svelte | 23 +++++++++++++++++------ ui/src/lib/OpenLayersMap.svelte | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index d4ca933..5b697e6 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -90,16 +90,27 @@ }) } - // Example: inside your node update logic - const { lon, lat } = /* your code fetching the new coordinate */ - trailArray.push({ coords: [lon, lat], ts: Date.now() }) - pruneOldPoints() - scheduleTrailUpdate() - $: if (ol) { plotTrail([]) // ...existing plotData() or other init calls... } + + $: { + // Whenever the selected node or its coords change… + const selectedNode = nodesWithCoords.find(n => n.num === $myNodeNum) + if (selectedNode?.position?.latitudeI && selectedNode?.position?.longitudeI) { + const lon = selectedNode.position.longitudeI / 1e7 + const lat = selectedNode.position.latitudeI / 1e7 + + // Only push if it’s truly new + const last = trailArray[trailArray.length - 1] + if (!last || last.coords[0] !== lon || last.coords[1] !== lat) { + trailArray.push({ coords: [lon, lat], ts: Date.now() }) + pruneOldPoints() + scheduleTrailUpdate() + } + } + } diff --git a/ui/src/lib/OpenLayersMap.svelte b/ui/src/lib/OpenLayersMap.svelte index 0dcad8e..f5e3856 100644 --- a/ui/src/lib/OpenLayersMap.svelte +++ b/ui/src/lib/OpenLayersMap.svelte @@ -21,6 +21,7 @@ import Text from 'ol/style/Text' import type { LoadingStrategy } from 'ol/source/Vector' import type { Coordinate } from 'ol/coordinate' + import VectorSource from 'ol/source/Vector' useGeographic() let dispatch = createEventDispatcher() @@ -277,6 +278,37 @@ } }) }) + + // Exposed to parent components + export function plotTrail(coordinates: [number, number][]) { + // If a previous trail exists, remove it + if (layers['trail']) { + map.removeLayer(layers['trail']) + delete layers['trail'] + } + + // Create new trail layer + const trailLayer = new VectorLayer({ + source: new VectorSource({ + features: [ + new Feature({ + geometry: new LineString(coordinates) + }) + ] + }), + style: new Style({ + stroke: new Stroke({ + color: '#FF0000', + width: 4 + }) + }), + updateWhileAnimating: true, + updateWhileInteracting: true + }) + + layers['trail'] = trailLayer + map.addLayer(trailLayer) + }
From 30a29057b4934d2720b3b5def9f58a276db79ea3 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 15:08:15 -0400 Subject: [PATCH 03/32] Fix broken Util in Update.mjs (styleText replaced with chalk) --- update.mjs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/update.mjs b/update.mjs index a996380..5507814 100755 --- a/update.mjs +++ b/update.mjs @@ -1,7 +1,16 @@ #!/usr/bin/env node +// styleText used to live in util, but Node v18+ no longer exports it. +// We’ll use chalk to get the same effect, or fall back to plaintext. +import chalk from 'chalk'; +/** + * styleText(text, colorName) → colored text + * supported colors: green, red (add more if you like) + */ +const styleText = (txt, col) => + ({ green: chalk.green, red: chalk.red }[col] ?? ((s) => s))(txt); + import { spawn } from 'child_process' import { argv } from 'process'; -import { styleText } from 'util'; let runCmd = (commandString) => new Promise((resolve, reject) => { let cmd = spawn(commandString, { shell: true, env: process.env }) From f7fa87adc1632a70bffbc6147d12c20b98e3584f Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 15:44:04 -0400 Subject: [PATCH 04/32] Fix compile issues with maptrails --- ui/src/Map.svelte | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 5b697e6..315b655 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -28,9 +28,8 @@ import { showConfigModal, showPage } from './SettingsModal.svelte' import { newsVisible } from './News.svelte' import { fromLonLat } from 'ol/proj' - import { plotTrail } from './lib/OpenLayersMap.svelte' - export let ol: OpenLayersMap = undefined + export let ol: any; // or use the correct type if you have one $: nodesWithCoords = $filteredNodes.filter((n) => !(n.position?.latitudeI == undefined || n.position?.latitudeI == 0) || n.approximatePosition) @@ -85,13 +84,13 @@ pendingTrail = true requestAnimationFrame(() => { const coordsTransformed = trailArray.map((p) => fromLonLat(p.coords)) - plotTrail(coordsTransformed) + ol?.plotTrail(coordsTransformed) pendingTrail = false }) } $: if (ol) { - plotTrail([]) + ol.plotTrail([]) // ...existing plotData() or other init calls... } @@ -144,7 +143,7 @@ } }} onDarkModeToggle={plotData} - > + /> {#if $setPositionMode}
Click on a new position for {getNodeNameById($myNodeNum)} From 4776530373d162ea822729c2a75f0966e3b7decb Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 16:25:50 -0400 Subject: [PATCH 05/32] Add crypto import to API index.ts --- api/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/index.ts b/api/src/index.ts index 2d79146..d5fe55e 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -10,6 +10,7 @@ import { createWriteStream } from 'fs' import { dataDirectory } from './lib/paths' import { join } from 'path' import axios from 'axios' +import crypto from 'crypto' // ← ADD THIS setInterval(() => currentTime.set(Date.now()), 15000) process.on('uncaughtException', (err, origin) => { From c1765c744146a28de616189799463ee01e3ebd5a Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 16:30:12 -0400 Subject: [PATCH 06/32] Undid previous change --- api/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/index.ts b/api/src/index.ts index d5fe55e..2d79146 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -10,7 +10,6 @@ import { createWriteStream } from 'fs' import { dataDirectory } from './lib/paths' import { join } from 'path' import axios from 'axios' -import crypto from 'crypto' // ← ADD THIS setInterval(() => currentTime.set(Date.now()), 15000) process.on('uncaughtException', (err, origin) => { From ccc63bc85e328fa3fae128e214429ebc0b982f3d Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 16:37:30 -0400 Subject: [PATCH 07/32] Connect button not working. Testing changes --- api/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/index.ts b/api/src/index.ts index 2d79146..557ffe6 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,3 +1,4 @@ +console.log('globalThis.crypto →', globalThis.crypto) import 'dotenv/config' import './lib/persistence' import { app, createRoutes, finalize, server } from './lib/server' From a7871426de5e6662b363d0ab90f7a425b398d0b4 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Fri, 30 May 2025 16:55:32 -0400 Subject: [PATCH 08/32] Something something Node bullshit with the crypto module --- api/src/index.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/api/src/index.ts b/api/src/index.ts index 557ffe6..a732c77 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -1,4 +1,13 @@ -console.log('globalThis.crypto →', globalThis.crypto) +import { webcrypto } from 'crypto'; + +declare global { + // Let TypeScript know about globalThis.crypto + // eslint-disable-next-line no-var + var crypto: typeof webcrypto; +} + +globalThis.crypto = webcrypto; + import 'dotenv/config' import './lib/persistence' import { app, createRoutes, finalize, server } from './lib/server' From a3ffac1195d2184a14ea91e014581bb275ab12ba Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 19:00:07 -0400 Subject: [PATCH 09/32] It ran before this, lets find out if it still does (Maybe fix trail lines?) --- ui/src/Map.svelte | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 315b655..d5e64f8 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -28,6 +28,7 @@ import { showConfigModal, showPage } from './SettingsModal.svelte' import { newsVisible } from './News.svelte' import { fromLonLat } from 'ol/proj' + import { getNodeHistory } from '../stores/nodes'; // Adjust as needed export let ol: any; // or use the correct type if you have one @@ -70,9 +71,9 @@ let modalPage = 'Settings' - let trailArray: { coords: [number, number]; ts: number }[] = [] - let pendingTrail = false - let timeWindowMs = 6 * 3600 * 1000 // default 6 hours + let trailArray: { coords: [number, number]; ts: number }[] = []; + let pendingTrail = false; + let timeWindowMs = 6 * 3600 * 1000; // default 6 hours function pruneOldPoints() { const cutoff = Date.now() - timeWindowMs @@ -110,6 +111,24 @@ } } } + + function onTimestampClick(clickedEntry) { + const historyRecords = getNodeHistory($myNodeNum); + const points = historyRecords + .map(r => ({ + coords: [r.longitudeI / 1e7, r.latitudeI / 1e7], + ts: r.timestampMs + })) + .sort((a, b) => a.ts - b.ts); + const uniquePoints = points.filter((p, i, arr) => { + if (i === 0) return true; + const prev = arr[i - 1].coords; + return p.coords[0] !== prev[0] || p.coords[1] !== prev[1]; + }); + trailArray = uniquePoints; + pruneOldPoints(); + scheduleTrailUpdate(); + } @@ -150,4 +169,10 @@
{/if} + + {#each historyList as entry} +
onTimestampClick(entry)}> + {new Date(entry.timestampMs).toLocaleString()} +
+ {/each} From 723fb38c3411c243799101d6c8f6a0faefb6b15b Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 19:04:44 -0400 Subject: [PATCH 10/32] Oops. Added an exta "." --- ui/src/Map.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index d5e64f8..b53f46f 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -28,7 +28,7 @@ import { showConfigModal, showPage } from './SettingsModal.svelte' import { newsVisible } from './News.svelte' import { fromLonLat } from 'ol/proj' - import { getNodeHistory } from '../stores/nodes'; // Adjust as needed + import { getNodeHistory } from './stores/nodes'; export let ol: any; // or use the correct type if you have one From ee2ab3d34c9d1cc7048a346d8ae58b17db111188 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 19:32:52 -0400 Subject: [PATCH 11/32] Added historical trail resources --- api/src/index.ts | 11 +++++++++++ ui/src/Map.svelte | 19 ++++++++++++------- ui/src/stores/nodes.ts | 20 ++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 ui/src/stores/nodes.ts diff --git a/api/src/index.ts b/api/src/index.ts index a732c77..ddf82fb 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -20,6 +20,8 @@ import { createWriteStream } from 'fs' import { dataDirectory } from './lib/paths' import { join } from 'path' import axios from 'axios' +import express from 'express'; +import { nodeHistoryMap } from './nodeHistoryStore'; // <-- implement or import this setInterval(() => currentTime.set(Date.now()), 15000) process.on('uncaughtException', (err, origin) => { @@ -115,6 +117,15 @@ createRoutes((app) => { return res.sendStatus(200) }) + app.get('/api/nodes/:nodeNum/history', (req, res) => { + const nodeNum = parseInt(req.params.nodeNum, 10); + if (isNaN(nodeNum)) { + return res.status(400).json({ error: 'Invalid nodeNum' }); + } + const history = nodeHistoryMap.get(nodeNum) || []; + return res.json(history); + }); + //** Set accessKey via environment variable */ if (process.env.ACCESS_KEY) { accessKey.set(process.env.ACCESS_KEY) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index b53f46f..1c39d65 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -28,7 +28,7 @@ import { showConfigModal, showPage } from './SettingsModal.svelte' import { newsVisible } from './News.svelte' import { fromLonLat } from 'ol/proj' - import { getNodeHistory } from './stores/nodes'; + import { getNodeHistory, type HistoryRecord } from './stores/nodes'; export let ol: any; // or use the correct type if you have one @@ -112,19 +112,22 @@ } } - function onTimestampClick(clickedEntry) { - const historyRecords = getNodeHistory($myNodeNum); + async function onTimestampClick(entry: any) { + const historyRecords: HistoryRecord[] = await getNodeHistory($myNodeNum); + const points = historyRecords .map(r => ({ - coords: [r.longitudeI / 1e7, r.latitudeI / 1e7], + coords: [r.longitudeI / 1e7, r.latitudeI / 1e7] as [number, number], ts: r.timestampMs })) .sort((a, b) => a.ts - b.ts); + const uniquePoints = points.filter((p, i, arr) => { if (i === 0) return true; - const prev = arr[i - 1].coords; - return p.coords[0] !== prev[0] || p.coords[1] !== prev[1]; + const [prevLon, prevLat] = arr[i - 1].coords; + return p.coords[0] !== prevLon || p.coords[1] !== prevLat; }); + trailArray = uniquePoints; pruneOldPoints(); scheduleTrailUpdate(); @@ -171,7 +174,9 @@ {/if} {#each historyList as entry} -
onTimestampClick(entry)}> +
onTimestampClick(entry)}> {new Date(entry.timestampMs).toLocaleString()}
{/each} diff --git a/ui/src/stores/nodes.ts b/ui/src/stores/nodes.ts new file mode 100644 index 0000000..5669c12 --- /dev/null +++ b/ui/src/stores/nodes.ts @@ -0,0 +1,20 @@ +export interface HistoryRecord { + latitudeI: number; + longitudeI: number; + timestampMs: number; +} + +export async function getNodeHistory(nodeNum: number): Promise { + try { + const resp = await fetch(`/api/nodes/${nodeNum}/history`); + if (!resp.ok) { + console.error(`Failed to fetch history for node ${nodeNum}:`, resp.statusText); + return []; + } + const data = await resp.json(); + return Array.isArray(data) ? data : []; + } catch (err) { + console.error(`Error fetching history for node ${nodeNum}:`, err); + return []; + } +} \ No newline at end of file From 3826141b1413168b0a3066efb7e0d827a7ff9af3 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 19:45:31 -0400 Subject: [PATCH 12/32] OOps. Forgot a file --- api/src/nodeHistoryStore.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 api/src/nodeHistoryStore.ts diff --git a/api/src/nodeHistoryStore.ts b/api/src/nodeHistoryStore.ts new file mode 100644 index 0000000..96c6a06 --- /dev/null +++ b/api/src/nodeHistoryStore.ts @@ -0,0 +1,7 @@ +export const nodeHistoryMap = new Map>(); + +// Example: Add a fake history for node 123 +nodeHistoryMap.set(123, [ + { latitudeI: 404123456, longitudeI: -747654321, timestampMs: 1620000000000 }, + { latitudeI: 404223456, longitudeI: -747654123, timestampMs: 1620000050000 }, +]); \ No newline at end of file From 3fa1c6176a685d8173208fbb33a2520604feb225 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 20:15:46 -0400 Subject: [PATCH 13/32] Fixes or something --- ui/src/Map.svelte | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 1c39d65..400b2d6 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -132,6 +132,12 @@ pruneOldPoints(); scheduleTrailUpdate(); } + + let historyList: any[] = []; + + $: if ($myNodeNum) { + getNodeHistory($myNodeNum).then(list => historyList = list); + } From 73bf954f87af174eac92f0d274b7b6452069e27d Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 21:53:36 -0400 Subject: [PATCH 14/32] Windows compile fixes with Electron --- electron/package.json | 9 +++++++++ electron/src/main/index.ts | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/electron/package.json b/electron/package.json index aec67bb..c21fbb0 100644 --- a/electron/package.json +++ b/electron/package.json @@ -39,5 +39,14 @@ "prettier": "^3.3.3", "typescript": "^5.6.3", "vite": "^5.4.9" + }, + "build": { + "extraResources": [ + { + "from": "../api/dist", + "to": "resources/api", + "filter": ["**/*"] + } + ] } } diff --git a/electron/src/main/index.ts b/electron/src/main/index.ts index b0cfe51..f1eeac0 100644 --- a/electron/src/main/index.ts +++ b/electron/src/main/index.ts @@ -58,6 +58,14 @@ app.whenReady().then(async () => { console.log(`DIRNAME`, __dirname) let apiPath = join(__dirname, '../../resources/api/index.cjs').replace('app.asar', 'app.asar.unpacked') console.log(`API_PATH`, apiPath) + createWindow() + + // Only run auto-update check if we actually have an update manifest + try { + await autoUpdater.checkForUpdatesAndNotify() + } catch (e) { + console.warn('AutoUpdater: no update manifest found, skipping. ', e) + } apiProcess = utilityProcess.fork(apiPath, process.argv, { stdio: 'pipe' }) apiProcess.stdout?.on('data', (e) => process.stdout.write(e)) From 9a196cf37da9b1eb871144b28c8aea354ec94f96 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 22:03:39 -0400 Subject: [PATCH 15/32] Still having issues on windows compile --- electron/src/main/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/electron/src/main/index.ts b/electron/src/main/index.ts index f1eeac0..587475f 100644 --- a/electron/src/main/index.ts +++ b/electron/src/main/index.ts @@ -56,7 +56,14 @@ function createWindow(): void { // Some APIs can only be used after this event occurs. app.whenReady().then(async () => { console.log(`DIRNAME`, __dirname) - let apiPath = join(__dirname, '../../resources/api/index.cjs').replace('app.asar', 'app.asar.unpacked') + let apiPath: string + if (app.isPackaged) { + // In packaged builds, use process.resourcesPath + apiPath = join(process.resourcesPath, 'api', 'index.cjs') + } else { + // In dev, use relative to source + apiPath = join(__dirname, '..', 'resources', 'api', 'index.cjs') + } console.log(`API_PATH`, apiPath) createWindow() From 309237c9fd0a93b3b996ff20e7289e8c30047e67 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 22:18:04 -0400 Subject: [PATCH 16/32] Node 18 remove styletext --- build.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.mjs b/build.mjs index b53d0fd..b32a925 100755 --- a/build.mjs +++ b/build.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import { spawn } from 'child_process' -import { styleText } from 'util'; +//import { styleText } from 'util'; import './api/node_modules/dotenv/config.js' let runCmd = (commandString) => new Promise((resolve, reject) => { From 4671b8c406fee5f5b5b89ef22e560f731ed22995 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 22:25:09 -0400 Subject: [PATCH 17/32] more styletext --- build.mjs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/build.mjs b/build.mjs index b32a925..48ebb7b 100755 --- a/build.mjs +++ b/build.mjs @@ -1,6 +1,5 @@ #!/usr/bin/env node import { spawn } from 'child_process' -//import { styleText } from 'util'; import './api/node_modules/dotenv/config.js' let runCmd = (commandString) => new Promise((resolve, reject) => { @@ -13,14 +12,16 @@ let runCmd = (commandString) => new Promise((resolve, reject) => { let platform = process.platform.replace(/32$/, '').replace('darwin', 'mac') -console.log(styleText(['magenta', 'bold'], 'Building UI')) +console.log('🏗 Building UI…') process.chdir('ui') await runCmd('npm run build') -console.log(styleText(['magenta', 'bold'], 'Building API')) +console.log('🏗 Building API…') process.chdir('../api') await runCmd('npm run build') -console.log(styleText(['magenta', 'bold'], 'Building Electron')) +console.log('🏗 Building Electron…') process.chdir('../electron') -await runCmd(`npm run build:${platform} --c.extraMetadata.version=2.0.0`) \ No newline at end of file +await runCmd(`npm run build:${platform} --c.extraMetadata.version=2.0.0`) + +console.log('✅ Build complete!') From cea322e4b207c41ffd586b12f269aec8913f8878 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 22:34:13 -0400 Subject: [PATCH 18/32] hate --- electron/src/main/index.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/electron/src/main/index.ts b/electron/src/main/index.ts index 587475f..8a03423 100644 --- a/electron/src/main/index.ts +++ b/electron/src/main/index.ts @@ -56,14 +56,8 @@ function createWindow(): void { // Some APIs can only be used after this event occurs. app.whenReady().then(async () => { console.log(`DIRNAME`, __dirname) - let apiPath: string - if (app.isPackaged) { - // In packaged builds, use process.resourcesPath - apiPath = join(process.resourcesPath, 'api', 'index.cjs') - } else { - // In dev, use relative to source - apiPath = join(__dirname, '..', 'resources', 'api', 'index.cjs') - } + // Always resolve API_PATH from process.resourcesPath for both dev and packaged + const apiPath = join(process.resourcesPath, 'api', 'index.cjs') console.log(`API_PATH`, apiPath) createWindow() From 95a006057b47b021e4772c88d6631fcb6dd9c98d Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Sun, 1 Jun 2025 23:05:06 -0400 Subject: [PATCH 19/32] Pain --- api/rollup.config.mjs | 3 ++- api/tsconfig.json | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api/rollup.config.mjs b/api/rollup.config.mjs index 1be0c69..2895810 100644 --- a/api/rollup.config.mjs +++ b/api/rollup.config.mjs @@ -74,8 +74,9 @@ export default defineConfig({ ignoreGlobal: true }), typescript({ - target: 'esnext' + target: 'esnext', // sourceMap: true + exclude: ['webbluetooth/build/**'] }), json() ] diff --git a/api/tsconfig.json b/api/tsconfig.json index f20531b..0c78a60 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -97,5 +97,9 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + "exclude": [ + "node_modules", + "webbluetooth/build" + ] } \ No newline at end of file From 8ae0a4cb196338f52e8f50100bde1856a1da8885 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 14:10:38 -0400 Subject: [PATCH 20/32] Maybe lines show up now, added some debugging --- ui/src/Map.svelte | 24 ++++++++++++++++-------- ui/src/lib/OpenLayersMap.svelte | 13 ++++++------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 400b2d6..ca36279 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -76,18 +76,24 @@ let timeWindowMs = 6 * 3600 * 1000; // default 6 hours function pruneOldPoints() { - const cutoff = Date.now() - timeWindowMs - trailArray = trailArray.filter((p) => p.ts >= cutoff) + const cutoff = Date.now() - timeWindowMs; + console.log('→ Pruning old points; cutoff =', new Date(cutoff).toLocaleString()); + console.log(' before prune:', trailArray.length); + trailArray = trailArray.filter(p => p.ts >= cutoff); + console.log(' after prune:', trailArray.length); } function scheduleTrailUpdate() { - if (pendingTrail) return - pendingTrail = true + if (pendingTrail) return; + pendingTrail = true; + requestAnimationFrame(() => { - const coordsTransformed = trailArray.map((p) => fromLonLat(p.coords)) - ol?.plotTrail(coordsTransformed) - pendingTrail = false - }) + const coordsTransformed = trailArray.map(p => fromLonLat(p.coords)); + console.log('→ scheduleTrailUpdate called, trailArray length =', trailArray.length); + console.log(' transformed coords:', coordsTransformed); + ol?.plotTrail(coordsTransformed); + pendingTrail = false; + }); } $: if (ol) { @@ -114,6 +120,7 @@ async function onTimestampClick(entry: any) { const historyRecords: HistoryRecord[] = await getNodeHistory($myNodeNum); + console.log('→ getNodeHistory returned', historyRecords.length, 'records'); const points = historyRecords .map(r => ({ @@ -127,6 +134,7 @@ const [prevLon, prevLat] = arr[i - 1].coords; return p.coords[0] !== prevLon || p.coords[1] !== prevLat; }); + console.log('→ uniquePoints (after dedupe) =', uniquePoints.length); trailArray = uniquePoints; pruneOldPoints(); diff --git a/ui/src/lib/OpenLayersMap.svelte b/ui/src/lib/OpenLayersMap.svelte index f5e3856..7b8efd4 100644 --- a/ui/src/lib/OpenLayersMap.svelte +++ b/ui/src/lib/OpenLayersMap.svelte @@ -281,13 +281,12 @@ // Exposed to parent components export function plotTrail(coordinates: [number, number][]) { - // If a previous trail exists, remove it + console.log('→ plotTrail called, adding layer with', coordinates.length, 'pts'); if (layers['trail']) { - map.removeLayer(layers['trail']) - delete layers['trail'] + map.removeLayer(layers['trail']); + delete layers['trail']; } - // Create new trail layer const trailLayer = new VectorLayer({ source: new VectorSource({ features: [ @@ -304,10 +303,10 @@ }), updateWhileAnimating: true, updateWhileInteracting: true - }) + }); - layers['trail'] = trailLayer - map.addLayer(trailLayer) + layers['trail'] = trailLayer; + map.addLayer(trailLayer); } From 37ae7eac791c7c1ceedbba8046a2bdf6bebd1934 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 15:07:15 -0400 Subject: [PATCH 21/32] Add some ui elements to show trail history --- ui/src/Map.svelte | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index ca36279..31f400d 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -119,6 +119,7 @@ } async function onTimestampClick(entry: any) { + console.log('Clicked:', entry); const historyRecords: HistoryRecord[] = await getNodeHistory($myNodeNum); console.log('→ getNodeHistory returned', historyRecords.length, 'records'); @@ -128,13 +129,14 @@ ts: r.timestampMs })) .sort((a, b) => a.ts - b.ts); + console.log('→ Converted and sorted points:', points); const uniquePoints = points.filter((p, i, arr) => { if (i === 0) return true; const [prevLon, prevLat] = arr[i - 1].coords; return p.coords[0] !== prevLon || p.coords[1] !== prevLat; }); - console.log('→ uniquePoints (after dedupe) =', uniquePoints.length); + console.log('→ uniquePoints (deduped) length =', uniquePoints.length); trailArray = uniquePoints; pruneOldPoints(); @@ -186,12 +188,20 @@
{/if} - - {#each historyList as entry} -
onTimestampClick(entry)}> - {new Date(entry.timestampMs).toLocaleString()} -
- {/each} + +
+

Node History for #{$myNodeNum}

+ {#if historyList.length === 0} +

No history available for this node.

+ {:else} + {#each historyList as entry (entry.timestampMs)} + + {/each} + {/if} +
From ef328f057295e106abfe7a8f8163550fb3d19e56 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 15:41:25 -0400 Subject: [PATCH 22/32] Added some buttons hopefully --- ui/src/Map.svelte | 42 +++++++++++++++++++++++------------------- ui/src/Nodes.svelte | 1 + ui/src/stores/ui.ts | 4 ++++ 3 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 ui/src/stores/ui.ts diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 31f400d..d1113dd 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -29,6 +29,7 @@ import { newsVisible } from './News.svelte' import { fromLonLat } from 'ol/proj' import { getNodeHistory, type HistoryRecord } from './stores/nodes'; + import { selectedHistoryNode, showHistoryPanel } from './stores/ui'; export let ol: any; // or use the correct type if you have one @@ -119,8 +120,8 @@ } async function onTimestampClick(entry: any) { - console.log('Clicked:', entry); - const historyRecords: HistoryRecord[] = await getNodeHistory($myNodeNum); + if (!$selectedHistoryNode) return; + const historyRecords: HistoryRecord[] = await getNodeHistory($selectedHistoryNode); console.log('→ getNodeHistory returned', historyRecords.length, 'records'); const points = historyRecords @@ -145,8 +146,8 @@ let historyList: any[] = []; - $: if ($myNodeNum) { - getNodeHistory($myNodeNum).then(list => historyList = list); + $: if ($selectedHistoryNode && $showHistoryPanel) { + getNodeHistory($selectedHistoryNode).then(list => historyList = list); } @@ -189,19 +190,22 @@
{/if} -
-

Node History for #{$myNodeNum}

- {#if historyList.length === 0} -

No history available for this node.

- {:else} - {#each historyList as entry (entry.timestampMs)} - - {/each} - {/if} -
+ {#if $showHistoryPanel} +
+

Node History for #{$selectedHistoryNode}

+ {#if historyList.length === 0} +

No history available for this node.

+ {:else} + {#each historyList as entry (entry.timestampMs)} + + {/each} + {/if} + +
+ {/if}
diff --git a/ui/src/Nodes.svelte b/ui/src/Nodes.svelte index d6583c9..17dd5d3 100644 --- a/ui/src/Nodes.svelte +++ b/ui/src/Nodes.svelte @@ -22,6 +22,7 @@ import { getSvgUri, setPositionMode } from './Map.svelte' import ChannelUtilization from './lib/ChannelUtilization.svelte' import ObservedRF from './lib/ObservedRF.svelte' + import { selectedHistoryNode, showHistoryPanel } from './stores/ui'; export let includeMqtt = (localStorage.getItem('includeMqtt') ?? 'true') == 'true' let selectedNode: NodeInfo diff --git a/ui/src/stores/ui.ts b/ui/src/stores/ui.ts new file mode 100644 index 0000000..e4d2b05 --- /dev/null +++ b/ui/src/stores/ui.ts @@ -0,0 +1,4 @@ +import { writable } from 'svelte/store'; + +export const selectedHistoryNode = writable(null); +export const showHistoryPanel = writable(false); \ No newline at end of file From c2ce4fc3aa08cc9f2b970534b0a0cd92dc8caa15 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 16:00:23 -0400 Subject: [PATCH 23/32] Forgot the buttons --- ui/src/Nodes.svelte | 110 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/ui/src/Nodes.svelte b/ui/src/Nodes.svelte index 17dd5d3..4137d1b 100644 --- a/ui/src/Nodes.svelte +++ b/ui/src/Nodes.svelte @@ -358,6 +358,116 @@ {/if} {/if} +
+ + + + + {#key $currentTime} +
{unixSecondsTimeAgo(node.lastHeard)}
+ {/key} + + +
+ {(node.deviceMetrics?.voltage || 0).toFixed(1)}V +
+ +
+ {#if node.deviceMetrics?.batteryLevel === 101} + + ⚡︎ + {:else} + {node.deviceMetrics?.batteryLevel || 0}% + {/if} +
+
+ + +
{node.num == $myNodeNum ? '-' : (node.hopsAway ?? '?')}
+ + + + + {#if node.num != $myNodeNum} + + {:else if $hasAccess} + + {/if} + + + + {#if node.position?.latitudeI} + + {:else} + + {/if} +
+ + {#if node.environmentMetrics} +
+ {#if node.environmentMetrics.temperature} +
+ {formatTemp(node.environmentMetrics.temperature, $displayFahrenheit)} +
+ {/if} + {#if node.environmentMetrics.barometricPressure} +
+ {Math.round(node.environmentMetrics.barometricPressure)} hPA +
+ {/if} + {#if node.environmentMetrics.relativeHumidity} +
+ {Math.round(node.environmentMetrics.relativeHumidity)}% +
+ {/if} + {#if node.environmentMetrics.gasResistance} +
+ {Math.round(node.environmentMetrics.gasResistance)} MOhm +
+ {/if} + {#if node.environmentMetrics.iaq} +
+ {Math.round(node.environmentMetrics.iaq)} IAQ +
+ {/if} +
+ {/if} {/each} {#if $hasAccess && $nodeVisibilityMode !== 'active' && $inactiveNodes.length >= 10} From dc21a29485875533f32c4231f622e61a4bdef0c9 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 16:08:44 -0400 Subject: [PATCH 24/32] Toooo many buttons now --- ui/src/Nodes.svelte | 152 ++++---------------------------------------- 1 file changed, 11 insertions(+), 141 deletions(-) diff --git a/ui/src/Nodes.svelte b/ui/src/Nodes.svelte index 4137d1b..aabdf85 100644 --- a/ui/src/Nodes.svelte +++ b/ui/src/Nodes.svelte @@ -270,7 +270,7 @@ {/if}
@@ -289,6 +289,16 @@ title="Traceroute {node.hopsAway == 0 ? 'Direct' : ''}{node?.trace ? [$myNodeNum, ...node?.trace?.route, node?.num].map((id) => getNodeNameById(id)).join(' -> ') : ''}" on:click={() => axios.post('/traceRoute', { destination: node.num })}>↯ + + {:else if $hasAccess} - - - {#key $currentTime} -
{unixSecondsTimeAgo(node.lastHeard)}
- {/key} - - -
- {(node.deviceMetrics?.voltage || 0).toFixed(1)}V -
- -
- {#if node.deviceMetrics?.batteryLevel === 101} - - ⚡︎ - {:else} - {node.deviceMetrics?.batteryLevel || 0}% - {/if} -
-
- - -
{node.num == $myNodeNum ? '-' : (node.hopsAway ?? '?')}
- - - - - {#if node.num != $myNodeNum} - - {:else if $hasAccess} - - {/if} - - - - {#if node.position?.latitudeI} - - {:else} - - {/if} - - - {#if node.environmentMetrics} -
- {#if node.environmentMetrics.temperature} -
- {formatTemp(node.environmentMetrics.temperature, $displayFahrenheit)} -
- {/if} - {#if node.environmentMetrics.barometricPressure} -
- {Math.round(node.environmentMetrics.barometricPressure)} hPA -
- {/if} - {#if node.environmentMetrics.relativeHumidity} -
- {Math.round(node.environmentMetrics.relativeHumidity)}% -
- {/if} - {#if node.environmentMetrics.gasResistance} -
- {Math.round(node.environmentMetrics.gasResistance)} MOhm -
- {/if} - {#if node.environmentMetrics.iaq} -
- {Math.round(node.environmentMetrics.iaq)} IAQ -
- {/if} -
{/if} {/each} From f3f675c02ab45e79a8616f534b83c4a5268f09be Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Mon, 2 Jun 2025 16:44:25 -0400 Subject: [PATCH 25/32] Something is working, but nothing is working --- api/src/index.ts | 2 +- ui/src/Nodes.svelte | 5 +++-- ui/src/stores/nodes.ts | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/api/src/index.ts b/api/src/index.ts index ddf82fb..c673d2f 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -118,7 +118,7 @@ createRoutes((app) => { }) app.get('/api/nodes/:nodeNum/history', (req, res) => { - const nodeNum = parseInt(req.params.nodeNum, 10); + const nodeNum = Number(req.params.nodeNum); // Ensure this is a number if (isNaN(nodeNum)) { return res.status(400).json({ error: 'Invalid nodeNum' }); } diff --git a/ui/src/Nodes.svelte b/ui/src/Nodes.svelte index aabdf85..98e8fd1 100644 --- a/ui/src/Nodes.svelte +++ b/ui/src/Nodes.svelte @@ -289,7 +289,8 @@ title="Traceroute {node.hopsAway == 0 ? 'Direct' : ''}{node?.trace ? [$myNodeNum, ...node?.trace?.route, node?.num].map((id) => getNodeNameById(id)).join(' -> ') : ''}" on:click={() => axios.post('/traceRoute', { destination: node.num })}>↯ - + + {:else if $hasAccess} {/each} {/if} - +
+ + +
{/if}
diff --git a/ui/src/app.css b/ui/src/app.css index 94e5ee5..b96d2af 100644 --- a/ui/src/app.css +++ b/ui/src/app.css @@ -98,3 +98,11 @@ button:active { top: 73px; left: .5em; } + +.node-history { + @apply absolute top-12 left-4 p-2 bg-black/80 rounded-lg max-h-60 overflow-auto flex flex-col gap-1 text-sm; +} + +.timestamp-item { + @apply w-full text-left px-2 py-0.5 rounded bg-slate-700/50 hover:bg-slate-700; +} From d9cbb4d702cb55e4a6535bc98c8ed04a935a802d Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Wed, 4 Jun 2025 15:50:42 -0400 Subject: [PATCH 29/32] Fixed coodinate conversions --- ui/src/Map.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index 9e77ce4..d9f94cc 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -91,10 +91,10 @@ pendingTrail = true; requestAnimationFrame(() => { - const coordsTransformed = trailArray.map(p => fromLonLat(p.coords)); + const coords = trailArray.map(p => p.coords); console.log('→ scheduleTrailUpdate called, trailArray length =', trailArray.length); - console.log(' transformed coords:', coordsTransformed); - ol?.plotTrail(coordsTransformed); + console.log(' coords:', coords); + ol?.plotTrail(coords); pendingTrail = false; }); } From ce886ea983883f878f77747e8816dcb67ee5abee Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Wed, 4 Jun 2025 16:07:07 -0400 Subject: [PATCH 30/32] Something is working --- ui/src/Map.svelte | 3 +++ ui/src/lib/OpenLayersMap.svelte | 41 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/ui/src/Map.svelte b/ui/src/Map.svelte index d9f94cc..859b624 100644 --- a/ui/src/Map.svelte +++ b/ui/src/Map.svelte @@ -95,12 +95,15 @@ console.log('→ scheduleTrailUpdate called, trailArray length =', trailArray.length); console.log(' coords:', coords); ol?.plotTrail(coords); + // also drop timestamped markers along the trail + ol?.plotTrailMarkers(trailArray); pendingTrail = false; }); } $: if (ol) { ol.plotTrail([]) + ol.plotTrailMarkers([]) // ...existing plotData() or other init calls... } diff --git a/ui/src/lib/OpenLayersMap.svelte b/ui/src/lib/OpenLayersMap.svelte index 7b8efd4..ae3ca8f 100644 --- a/ui/src/lib/OpenLayersMap.svelte +++ b/ui/src/lib/OpenLayersMap.svelte @@ -308,6 +308,47 @@ layers['trail'] = trailLayer; map.addLayer(trailLayer); } + + export function plotTrailMarkers(points: { coords: [number, number]; ts: number }[]) { + if (layers['trailMarkers']) { + map.removeLayer(layers['trailMarkers']); + delete layers['trailMarkers']; + } + + if (points.length === 0) return; + + const features = points.map(p => { + const feature = new Feature({ + geometry: new Point(p.coords) + }); + feature.setStyle( + new Style({ + image: new Circle({ + radius: 5, + fill: new Fill({ color: '#f00' }), + stroke: new Stroke({ color: '#fff', width: 1 }) + }), + text: new Text({ + font: '12px sans-serif', + offsetY: -12, + fill: new Fill({ color: !darkMode ? '#000' : '#fff' }), + stroke: new Stroke({ color: !darkMode ? '#fff' : '#000', width: 3 }), + text: new Date(p.ts).toLocaleTimeString() + }) + }) + ); + return feature; + }); + + const layer = new VectorLayer({ + source: new VectorSource({ features }), + updateWhileAnimating: true, + updateWhileInteracting: true + }); + + layers['trailMarkers'] = layer; + map.addLayer(layer); + }
From a6bb2ccaf8ef6cef212793d1cfb62c373d94f928 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Wed, 4 Jun 2025 16:43:30 -0400 Subject: [PATCH 31/32] Updated trail ui elements --- ui/src/lib/OpenLayersMap.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/lib/OpenLayersMap.svelte b/ui/src/lib/OpenLayersMap.svelte index ae3ca8f..247a926 100644 --- a/ui/src/lib/OpenLayersMap.svelte +++ b/ui/src/lib/OpenLayersMap.svelte @@ -298,7 +298,8 @@ style: new Style({ stroke: new Stroke({ color: '#FF0000', - width: 4 + width: 2 + lineDash: [6, 10] }) }), updateWhileAnimating: true, From 306ae52dbad18ab6b55204aa65891246e6532081 Mon Sep 17 00:00:00 2001 From: TheWISPrer Date: Wed, 4 Jun 2025 16:47:19 -0400 Subject: [PATCH 32/32] Forgot a comma --- ui/src/lib/OpenLayersMap.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/lib/OpenLayersMap.svelte b/ui/src/lib/OpenLayersMap.svelte index 247a926..6a8c7d3 100644 --- a/ui/src/lib/OpenLayersMap.svelte +++ b/ui/src/lib/OpenLayersMap.svelte @@ -298,7 +298,7 @@ style: new Style({ stroke: new Stroke({ color: '#FF0000', - width: 2 + width: 2, lineDash: [6, 10] }) }),