Skip to content

Commit c35664b

Browse files
authored
Merge pull request #438 from SimmerV/feature-addition
Map overhaul: terrain-aware RF tools (Coverage / LOS / Scan), WASM ITM, donut clusters, 3D terrain
2 parents 1a1c3c2 + a288b63 commit c35664b

57 files changed

Lines changed: 12972 additions & 1807 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

frontend/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
66
<base href="/" />
77
<title>MeshInfo</title>
88
</head>
@@ -15,6 +15,7 @@
1515
font-size: 12px;
1616
margin: 0;
1717
padding: 0;
18+
padding: env(safe-area-inset-top, 0) env(safe-area-inset-right, 0) env(safe-area-inset-bottom, 0) env(safe-area-inset-left, 0);
1819
}
1920
a {
2021
text-decoration: none;

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
1111
"preview": "vite preview",
1212
"test": "vitest run",
13-
"test:watch": "vitest"
13+
"test:watch": "vitest",
14+
"build:wasm": "docker build -t meshinfo-itm-build wasm/itm && docker run --rm -v \"$(pwd)/src/generated/itm\":/out meshinfo-itm-build"
1415
},
1516
"dependencies": {
1617
"@reduxjs/toolkit": "^2.11.2",

frontend/src/components/Layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ const isFullBleed = isMap || isChat || isLog || isTraceroutes || isTelemetry ||
6868

6969
return (
7070
<div className={isFullBleed ? "h-dvh overflow-hidden" : ""}>
71-
<Menu isDark={isDark} onDarkChange={(dark) => setIsDark(dark)} />
71+
<Menu isDark={isDark} onDarkChange={(dark) => setIsDark(dark)} overlayMode={isMap} />
7272

7373
<div
74-
className={`lg:pl-60 dark:bg-gray-950 dark:text-gray-100 lg:pt-0
74+
className={`${isMap ? "" : "lg:pl-60"} dark:bg-gray-950 dark:text-gray-100 lg:pt-0
7575
${isFullBleed ? "pt-0 h-full overflow-hidden" : "pt-14"}`}
7676
>
7777
<main className={isFullBleed ? "h-full" : "py-1"}>

frontend/src/components/Menu.tsx

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ const defaultTools = [
1414
export const Menu = ({
1515
isDark,
1616
onDarkChange,
17+
overlayMode = false,
1718
}: {
1819
isDark: boolean;
1920
onDarkChange: (dark: boolean) => void;
21+
overlayMode?: boolean;
2022
}) => {
2123
const { data: config } = useGetConfigQuery();
2224
const [showMenu, setShowMenu] = useState(false);
@@ -39,13 +41,15 @@ export const Menu = ({
3941

4042
return (
4143
<>
42-
{/* Mobile Hamburger Button */}
44+
{/* Hamburger Button — always visible in overlay mode, mobile-only otherwise */}
4345
<button
4446
type="button"
45-
className={`lg:hidden fixed z-50 top-4 right-4 left-auto p-2 rounded-lg shadow-lg backdrop-blur-xs border transition-all duration-200 ${
46-
showMenu
47-
? "bg-gray-800 dark:bg-gray-200 border-gray-600 dark:border-gray-400"
48-
: "bg-white/90 dark:bg-gray-800/90 border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700"
47+
className={`${overlayMode ? "" : "lg:hidden"} fixed z-50 top-4 ${overlayMode ? "left-4" : "right-4 left-auto"} p-2 rounded-lg shadow-lg backdrop-blur-xs border transition-all duration-200 ${
48+
showMenu
49+
? "bg-gray-800 dark:bg-gray-200 border-gray-600 dark:border-gray-400"
50+
: overlayMode
51+
? "bg-gray-900/80 dark:bg-gray-900/80 backdrop-blur-xl border-white/10 hover:bg-gray-900/90"
52+
: "bg-white/90 dark:bg-gray-800/90 border-gray-200 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700"
4953
}`}
5054
onClick={() => setShowMenu(!showMenu)}
5155
aria-label="Toggle Menu"
@@ -74,22 +78,22 @@ export const Menu = ({
7478
</div>
7579
</button>
7680

77-
{/* Mobile Backdrop */}
78-
<div
79-
className={`lg:hidden fixed inset-0 bg-black/20 backdrop-blur-xs z-40 transition-opacity duration-200 ${
81+
{/* Backdrop — always available in overlay mode, mobile-only otherwise */}
82+
<div
83+
className={`${overlayMode ? "" : "lg:hidden"} fixed inset-0 bg-black/20 backdrop-blur-xs z-40 transition-opacity duration-200 ${
8084
showMenu ? "opacity-100" : "opacity-0 pointer-events-none"
8185
}`}
8286
onClick={() => setShowMenu(false)}
8387
/>
8488

8589
{/* Navigation Panel */}
8690
<div
87-
className={`w-full lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-60 lg:flex-col ${
91+
className={`w-full ${overlayMode ? "" : "lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-60 lg:flex-col"} ${
8892
showMenu ? "" : "hidden"
89-
} dark:text-gray-100 z-0 lg:z-50`}
93+
} dark:text-gray-100 z-0 ${overlayMode ? "" : "lg:z-50"}`}
9094
>
91-
{/* Mobile Drawer */}
92-
<div className="lg:hidden fixed inset-y-0 left-0 z-50 w-80 max-w-[80vw] bg-white dark:bg-gray-900 shadow-xl border-r border-gray-200 dark:border-gray-700">
95+
{/* Drawer (mobile always, desktop when overlayMode) */}
96+
<div className={`${overlayMode ? "" : "lg:hidden"} fixed inset-y-0 left-0 z-50 w-80 max-w-[80vw] bg-white dark:bg-gray-900 shadow-xl border-r border-gray-200 dark:border-gray-700`}>
9397
<div className="flex flex-col h-full overflow-hidden">
9498
{/* Mobile Header */}
9599
<div className="p-4 border-b border-gray-200 dark:border-gray-700">
@@ -114,9 +118,9 @@ export const Menu = ({
114118
</div>
115119

116120
{/* Mobile Navigation */}
117-
<div className="flex-1 overflow-y-auto px-2 py-3 space-y-1">
118-
<div className="space-y-1">
119-
<h3 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider px-3 py-2">Mesh</h3>
121+
<div className="flex-1 overflow-y-auto px-2 py-3 space-y-4">
122+
<div className="space-y-0.5">
123+
<h3 className="text-[10px] font-bold text-cyan-600 dark:text-cyan-500 uppercase tracking-widest px-3 pt-2 pb-1">Mesh</h3>
120124
<Link to="/chat" onClick={() => setShowMenu(false)} className="block">
121125
<div className="flex items-center gap-3 p-3 rounded-lg transition-colors hover:bg-gray-100 dark:hover:bg-gray-700/50">
122126
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">Chat</span>
@@ -165,8 +169,8 @@ export const Menu = ({
165169
</Link>
166170
</div>
167171

168-
<div className="space-y-1">
169-
<h3 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider px-3 py-2">Tools</h3>
172+
<div className="space-y-0.5">
173+
<h3 className="text-[10px] font-bold text-cyan-600 dark:text-cyan-500 uppercase tracking-widest px-3 pt-2 pb-1">Tools</h3>
170174
{(config?.mesh?.tools ?? defaultTools).map((tool, index) => (
171175
<a
172176
key={`mobile-tools-${index}`}
@@ -183,8 +187,8 @@ export const Menu = ({
183187
))}
184188
</div>
185189

186-
<div className="space-y-1">
187-
<h3 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider px-3 py-2">Meshtastic Addons</h3>
190+
<div className="space-y-0.5">
191+
<h3 className="text-[10px] font-bold text-cyan-600 dark:text-cyan-500 uppercase tracking-widest px-3 pt-2 pb-1">Meshtastic Addons</h3>
188192
<a
189193
href="https://github.com/armooo/meshtastic_dopewars"
190194
target="_blank"
@@ -223,8 +227,8 @@ export const Menu = ({
223227
</div>
224228
</div>
225229

226-
{/* Desktop Sidebar */}
227-
<div className="hidden lg:flex lg:flex-col px-6 pb-4 overflow-y-auto bg-gray-300 dark:bg-gray-800 border-r-2 grow gap-y-5 border-r-cyan-600">
230+
{/* Desktop Sidebar — hidden in overlay mode */}
231+
<div className={`${overlayMode ? "hidden" : "hidden lg:flex lg:flex-col"} px-6 pb-4 overflow-y-auto bg-gray-300 dark:bg-gray-800 border-r-2 grow gap-y-5 border-r-cyan-600`}>
228232
<div className="flex items-center h-24 mt-4 shrink-0">
229233
<div className="text-2xl">
230234
{config?.mesh?.name?.split(" ").map((word, index) => (
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# The real artifacts produced by `yarn build:wasm` overwrite the
2+
# placeholder files in this directory. We want to keep the placeholders
3+
# themselves checked in (so `tsc`/Vite resolve imports in fresh clones)
4+
# but ignore anything else that lands here from the build.
5+
*
6+
!.gitignore
7+
!itm.js
8+
!itm.d.ts
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Emscripten module produced by build.sh from NTIA/itm v1.4.
3+
* Auto-generated glue; hand-written `.d.ts` lives here so TS consumers
4+
* see a typed default export even when the .js is freshly rebuilt.
5+
*/
6+
export interface ItmModule {
7+
_malloc: (size: number) => number;
8+
_free: (ptr: number) => void;
9+
_ITM_P2P_TLS: (...args: number[]) => number;
10+
_ITM_P2P_TLS_Ex: (...args: number[]) => number;
11+
_ITM_P2P_CR: (...args: number[]) => number;
12+
_ITM_P2P_CR_Ex: (...args: number[]) => number;
13+
_ITM_AREA_TLS: (...args: number[]) => number;
14+
_ITM_AREA_TLS_Ex: (...args: number[]) => number;
15+
_ITM_AREA_CR: (...args: number[]) => number;
16+
_ITM_AREA_CR_Ex: (...args: number[]) => number;
17+
HEAP8: Int8Array;
18+
HEAPU8: Uint8Array;
19+
HEAP16: Int16Array;
20+
HEAPU16: Uint16Array;
21+
HEAP32: Int32Array;
22+
HEAPU32: Uint32Array;
23+
HEAPF32: Float32Array;
24+
HEAPF64: Float64Array;
25+
getValue(ptr: number, type: string): number;
26+
setValue(ptr: number, value: number, type: string): void;
27+
}
28+
29+
declare const createItm: (overrides?: Partial<ItmModule>) => Promise<ItmModule>;
30+
export default createItm;

frontend/src/generated/itm/itm.js

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/src/index.css

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
--color-indigo-500: #6366f1;
1313
--color-indigo-600: #4f46e5;
1414
--color-indigo-700: #4338ca;
15+
/* Z-index scale for the map overlay stack (panels > settings chips > popovers). */
16+
--z-index-1050: 1050;
17+
--z-index-1060: 1060;
18+
--z-index-1100: 1100;
1519
}
1620

1721
/*
@@ -46,3 +50,66 @@
4650
.dark a:hover {
4751
color: #6366f1;
4852
}
53+
54+
/* Hide scrollbar but keep scroll functionality */
55+
.no-scrollbar::-webkit-scrollbar { display: none; }
56+
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
57+
58+
/* Bottom sheet expanded state — dvh accounts for mobile browser chrome */
59+
.bottom-sheet-expanded {
60+
height: 100dvh !important;
61+
max-height: 100dvh !important;
62+
transition: height 200ms ease-out, max-height 200ms ease-out;
63+
}
64+
.bottom-sheet-collapsing {
65+
transition: height 200ms ease-out, max-height 200ms ease-out;
66+
}
67+
68+
/* Mapbox hover tooltip styling */
69+
.map-hover-tooltip .mapboxgl-popup-content {
70+
background: rgba(0, 0, 0, 0.85);
71+
color: white;
72+
padding: 6px 10px;
73+
border-radius: 6px;
74+
font-size: 12px;
75+
line-height: 1.4;
76+
pointer-events: none;
77+
box-shadow: none;
78+
}
79+
.map-hover-tooltip .mapboxgl-popup-tip {
80+
border-top-color: rgba(0, 0, 0, 0.85);
81+
}
82+
83+
/* OL zoom controls: move to top-right to match Mapbox layout */
84+
.ol-zoom {
85+
left: auto !important;
86+
right: 8px;
87+
top: 8px;
88+
}
89+
90+
/* Mapbox controls: logo + attribution side by side */
91+
.mapboxgl-ctrl-top-right {
92+
display: flex;
93+
flex-direction: row;
94+
align-items: flex-start;
95+
gap: 4px;
96+
}
97+
.mapboxgl-ctrl-top-right .mapboxgl-ctrl {
98+
margin: 0 !important;
99+
}
100+
101+
/* Map details panel slide-in animations */
102+
@keyframes slideInRight {
103+
from { transform: translateX(100%); }
104+
to { transform: translateX(0); }
105+
}
106+
107+
@keyframes slideInUp {
108+
from { transform: translateY(100%); }
109+
to { transform: translateY(0); }
110+
}
111+
112+
@keyframes slideInLeft {
113+
from { transform: translate(-100%, -50%); opacity: 0; }
114+
to { transform: translate(0, -50%); opacity: 1; }
115+
}

0 commit comments

Comments
 (0)