mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-05-28 01:52:28 +02:00
fix: cross-node gate decryption, UI text scaling, aircraft zoom
- Derive gate envelope AES key from gate ID via HKDF so all nodes sharing a gate can decrypt each other's messages (was node-local) - Preserve gate_envelope/reply_to in chain payload normalization - Bump Wormhole modal text from 9-10px to 12-13px - Add aircraft icon zoom interpolation (0.8→2.0 across zoom 5-12) - Reduce Mesh Chat panel text sizes for tighter layout
This commit is contained in:
@@ -3234,7 +3234,7 @@ const MaplibreViewer = ({
|
||||
type="symbol"
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': 0.8,
|
||||
'icon-size': ['interpolate', ['linear'], ['zoom'], 5, 0.8, 8, 1.0, 12, 2.0],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
'icon-rotation-alignment': 'map',
|
||||
@@ -3249,7 +3249,7 @@ const MaplibreViewer = ({
|
||||
type="symbol"
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': 0.8,
|
||||
'icon-size': ['interpolate', ['linear'], ['zoom'], 5, 0.8, 8, 1.0, 12, 2.0],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
'icon-rotation-alignment': 'map',
|
||||
@@ -3264,7 +3264,7 @@ const MaplibreViewer = ({
|
||||
type="symbol"
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': 0.8,
|
||||
'icon-size': ['interpolate', ['linear'], ['zoom'], 5, 0.8, 8, 1.0, 12, 2.0],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
'icon-rotation-alignment': 'map',
|
||||
@@ -3279,7 +3279,7 @@ const MaplibreViewer = ({
|
||||
type="symbol"
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': 0.8,
|
||||
'icon-size': ['interpolate', ['linear'], ['zoom'], 5, 0.8, 8, 1.0, 12, 2.0],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
'icon-rotation-alignment': 'map',
|
||||
@@ -3469,7 +3469,7 @@ const MaplibreViewer = ({
|
||||
['==', ['get', 'iconId'], 'svgPotusHeli'],
|
||||
]}
|
||||
paint={{
|
||||
'circle-radius': 18,
|
||||
'circle-radius': ['interpolate', ['linear'], ['zoom'], 5, 18, 8, 22, 12, 40],
|
||||
'circle-color': 'transparent',
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': 'gold',
|
||||
@@ -3483,12 +3483,22 @@ const MaplibreViewer = ({
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': [
|
||||
'case',
|
||||
['==', ['get', 'iconId'], 'svgPotusPlane'],
|
||||
1.3,
|
||||
['==', ['get', 'iconId'], 'svgPotusHeli'],
|
||||
1.3,
|
||||
0.8,
|
||||
'interpolate', ['linear'], ['zoom'],
|
||||
5, ['case',
|
||||
['==', ['get', 'iconId'], 'svgPotusPlane'], 1.3,
|
||||
['==', ['get', 'iconId'], 'svgPotusHeli'], 1.3,
|
||||
0.8,
|
||||
],
|
||||
8, ['case',
|
||||
['==', ['get', 'iconId'], 'svgPotusPlane'], 1.6,
|
||||
['==', ['get', 'iconId'], 'svgPotusHeli'], 1.6,
|
||||
1.0,
|
||||
],
|
||||
12, ['case',
|
||||
['==', ['get', 'iconId'], 'svgPotusPlane'], 2.6,
|
||||
['==', ['get', 'iconId'], 'svgPotusHeli'], 2.6,
|
||||
2.0,
|
||||
],
|
||||
],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
@@ -3504,7 +3514,7 @@ const MaplibreViewer = ({
|
||||
type="symbol"
|
||||
layout={{
|
||||
'icon-image': ['get', 'iconId'],
|
||||
'icon-size': 0.8,
|
||||
'icon-size': ['interpolate', ['linear'], ['zoom'], 5, 0.8, 8, 1.0, 12, 2.0],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-rotate': ['get', 'rotation'],
|
||||
'icon-rotation-alignment': 'map',
|
||||
@@ -3545,7 +3555,7 @@ const MaplibreViewer = ({
|
||||
|
||||
{/* HTML labels for UAVs (orange names) */}
|
||||
{uavGeoJSON && !selectedEntity && !isMapInteracting && data?.uavs && (
|
||||
<UavLabels uavs={data.uavs} inView={inView} />
|
||||
<UavLabels uavs={data.uavs} inView={inView} zoom={zoom} />
|
||||
)}
|
||||
|
||||
{/* HTML labels for earthquakes (yellow) - only show when zoomed in (~2000 miles = zoom ~5) */}
|
||||
|
||||
@@ -3944,7 +3944,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
setActiveTab(tab.key);
|
||||
if (tab.key === 'dms') setDmView('contacts');
|
||||
}}
|
||||
className={`flex-1 flex items-center justify-center gap-1 py-1.5 text-sm font-mono tracking-wider transition-colors ${
|
||||
className={`flex-1 flex items-center justify-center gap-1 py-1.5 text-[12px] font-mono tracking-wider transition-colors ${
|
||||
activeTab === tab.key
|
||||
? 'text-cyan-300 bg-cyan-950/50 font-bold border-b border-cyan-500/50'
|
||||
: 'text-[var(--text-muted)] hover:text-cyan-600 border-b border-cyan-900/20'
|
||||
@@ -4536,7 +4536,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
value={meshRegion}
|
||||
onChange={(e) => setMeshRegion(e.target.value)}
|
||||
title="Meshtastic MQTT root"
|
||||
className="bg-[var(--bg-secondary)]/50 border border-[var(--border-primary)] text-sm font-mono text-cyan-300 px-2 py-1 outline-none focus:border-cyan-700/50"
|
||||
className="bg-[var(--bg-secondary)]/50 border border-[var(--border-primary)] text-[12px] font-mono text-cyan-300 px-2 py-1 outline-none focus:border-cyan-700/50"
|
||||
style={{ width: '132px' }}
|
||||
>
|
||||
{meshRoots.map((r) => (
|
||||
@@ -4548,7 +4548,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
<select
|
||||
value={meshChannel}
|
||||
onChange={(e) => setMeshChannel(e.target.value)}
|
||||
className="flex-1 bg-[var(--bg-secondary)]/50 border border-[var(--border-primary)] text-sm font-mono text-green-400 px-2 py-1 outline-none focus:border-cyan-700/50"
|
||||
className="flex-1 bg-[var(--bg-secondary)]/50 border border-[var(--border-primary)] text-[12px] font-mono text-green-400 px-2 py-1 outline-none focus:border-cyan-700/50"
|
||||
>
|
||||
{meshChannels.map((ch) => (
|
||||
<option key={ch} value={ch}>
|
||||
@@ -4561,7 +4561,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setMeshView('channel')}
|
||||
className={`px-2 py-0.5 text-[13px] font-mono tracking-wider border transition-colors ${
|
||||
className={`px-2 py-0.5 text-[11px] font-mono tracking-wider border transition-colors ${
|
||||
meshView === 'channel'
|
||||
? 'border-green-500/40 text-green-300 bg-green-950/30'
|
||||
: 'border-[var(--border-primary)]/40 text-[var(--text-muted)] hover:text-green-300'
|
||||
@@ -4571,7 +4571,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setMeshView('inbox')}
|
||||
className={`px-2 py-0.5 text-[13px] font-mono tracking-wider border transition-colors ${
|
||||
className={`px-2 py-0.5 text-[11px] font-mono tracking-wider border transition-colors ${
|
||||
meshView === 'inbox'
|
||||
? 'border-amber-500/40 text-amber-300 bg-amber-950/20'
|
||||
: 'border-[var(--border-primary)]/40 text-[var(--text-muted)] hover:text-amber-300'
|
||||
@@ -4580,31 +4580,31 @@ const MeshChat = React.memo(function MeshChat({
|
||||
INBOX
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-[12px] font-mono text-[var(--text-muted)] truncate">
|
||||
<div className="text-[10px] font-mono text-[var(--text-muted)] truncate">
|
||||
{publicMeshAddress ? `ADDR ${publicMeshAddress.toUpperCase()}` : 'NO PUBLIC MESH ADDRESS'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto styled-scrollbar px-3 py-1.5 border-l-2 border-cyan-800/25">
|
||||
{meshView === 'channel' && filteredMeshMessages.length === 0 && (
|
||||
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
<div className="text-[12px] font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
No messages from {meshRegion} / {meshChannel}
|
||||
</div>
|
||||
)}
|
||||
{meshView === 'inbox' && (
|
||||
<>
|
||||
{!publicMeshAddress && (
|
||||
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
<div className="text-[12px] font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
Create or load a public mesh identity to see direct Meshtastic traffic.
|
||||
</div>
|
||||
)}
|
||||
{publicMeshAddress && meshInboxMessages.length === 0 && (
|
||||
<div className="text-sm font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
<div className="text-[12px] font-mono text-[var(--text-muted)] text-center py-4 leading-[1.65]">
|
||||
No public direct messages addressed to {publicMeshAddress.toUpperCase()} yet.
|
||||
</div>
|
||||
)}
|
||||
{meshInboxMessages.map((m, i) => (
|
||||
<div key={`${m.timestamp}-${i}`} className="py-0.5 leading-[1.65]">
|
||||
<div className="flex items-start gap-1.5 text-sm font-mono">
|
||||
<div className="flex items-start gap-1.5 text-[12px] font-mono">
|
||||
<button
|
||||
onClick={(e) => handleSenderClick(m.from, e, 'meshtastic')}
|
||||
className="text-amber-300 shrink-0 hover:text-amber-200 hover:underline cursor-pointer"
|
||||
@@ -4612,14 +4612,14 @@ const MeshChat = React.memo(function MeshChat({
|
||||
{displayPublicMeshSender(m.from)}
|
||||
</button>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[12px] text-amber-200/70 mb-0.5">
|
||||
<div className="text-[10px] text-amber-200/70 mb-0.5">
|
||||
TO {publicMeshAddress.toUpperCase()}
|
||||
</div>
|
||||
<div className="break-words whitespace-pre-wrap text-amber-100/90">
|
||||
{m.text}
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-[var(--text-muted)] shrink-0 text-[13px]">
|
||||
<span className="text-[var(--text-muted)] shrink-0 text-[11px]">
|
||||
{timeAgo(
|
||||
typeof m.timestamp === 'number'
|
||||
? m.timestamp
|
||||
@@ -4634,7 +4634,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
{meshView === 'channel' &&
|
||||
filteredMeshMessages.map((m, i) => (
|
||||
<div key={`${m.timestamp}-${i}`} className="py-0.5 leading-[1.65]">
|
||||
<div className="flex gap-1.5 text-sm font-mono">
|
||||
<div className="flex gap-1.5 text-[12px] font-mono">
|
||||
<button
|
||||
onClick={(e) => handleSenderClick(m.from, e, 'meshtastic')}
|
||||
className="text-green-400 shrink-0 hover:text-green-300 hover:underline cursor-pointer"
|
||||
@@ -4646,7 +4646,7 @@ const MeshChat = React.memo(function MeshChat({
|
||||
>
|
||||
{m.text}
|
||||
</span>
|
||||
<span className="text-[var(--text-muted)] shrink-0 text-[13px]">
|
||||
<span className="text-[var(--text-muted)] shrink-0 text-[11px]">
|
||||
{timeAgo(
|
||||
typeof m.timestamp === 'number'
|
||||
? m.timestamp
|
||||
|
||||
@@ -876,13 +876,13 @@ export default function TopRightControls({
|
||||
onClick={closeTerminalLauncher}
|
||||
className="absolute inset-0 bg-black/70 backdrop-blur-[2px]"
|
||||
/>
|
||||
<div className="relative z-[1201] w-full max-w-[560px] border border-cyan-700/40 bg-[var(--bg-primary)]/96 backdrop-blur-sm shadow-[0_0_32px_rgba(0,255,255,0.12)]">
|
||||
<div className="relative z-[1201] w-full max-w-[640px] border border-cyan-700/40 bg-[var(--bg-primary)]/96 backdrop-blur-sm shadow-[0_0_32px_rgba(0,255,255,0.12)]">
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-cyan-900/30">
|
||||
<div>
|
||||
<div className="text-[10px] font-mono tracking-[0.24em] text-cyan-300">
|
||||
<div className="text-[13px] font-mono tracking-[0.24em] text-cyan-300">
|
||||
INFONET TERMINAL
|
||||
</div>
|
||||
<div className={`mt-1 text-[9px] font-mono ${terminalStatusTone}`}>
|
||||
<div className={`mt-1 text-[11px] font-mono ${terminalStatusTone}`}>
|
||||
{terminalStatusLabel} • {terminalTransportTier}
|
||||
</div>
|
||||
</div>
|
||||
@@ -892,26 +892,26 @@ export default function TopRightControls({
|
||||
className="text-[var(--text-muted)] hover:text-cyan-300 transition-colors"
|
||||
title="Close terminal launcher"
|
||||
>
|
||||
<X size={13} />
|
||||
<X size={16} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="px-5 py-5 space-y-4">
|
||||
<div className="border border-cyan-500/20 bg-cyan-950/10 px-4 py-4 text-[10px] font-mono text-cyan-100 leading-[1.8]">
|
||||
<div className="border border-cyan-500/20 bg-cyan-950/10 px-4 py-4 text-[13px] font-mono text-cyan-100 leading-[1.8]">
|
||||
{terminalPrivateReady
|
||||
? 'Enter the Wormhole-facing terminal and sync with the obfuscated Infonet commons?'
|
||||
: 'The terminal runs through Wormhole for obfuscated gates, inbox, and experimental comms.'}
|
||||
<div className="mt-2 text-[9px] text-cyan-200/70 normal-case tracking-normal">
|
||||
<div className="mt-2 text-[12px] text-cyan-200/70 normal-case tracking-normal">
|
||||
{terminalPrivateReady
|
||||
? 'Your obfuscated identity is already provisioned. Entering now keeps the obfuscated lane separate from the public node sync path.'
|
||||
: 'This turns Wormhole on and opens the obfuscated lane. If you already have a Wormhole identity, it reuses it. If you do not, it bootstraps one once and then keeps using it.'}
|
||||
</div>
|
||||
</div>
|
||||
{terminalLaunchError && (
|
||||
<div className="border border-amber-500/40 bg-amber-950/20 px-4 py-3 text-[9px] font-mono text-amber-200 leading-[1.7]">
|
||||
<div className="border border-amber-500/40 bg-amber-950/20 px-4 py-3 text-[12px] font-mono text-amber-200 leading-[1.7]">
|
||||
{terminalLaunchError}
|
||||
</div>
|
||||
)}
|
||||
<div className="border border-cyan-500/20 bg-black/30 px-4 py-4 text-[9px] font-mono text-slate-200 leading-[1.85]">
|
||||
<div className="border border-cyan-500/20 bg-black/30 px-4 py-4 text-[12px] font-mono text-slate-200 leading-[1.85]">
|
||||
<div className="text-cyan-300 tracking-[0.18em]">BEFORE YOU ENTER:</div>
|
||||
<ul className="mt-3 space-y-2 list-disc pl-5">
|
||||
<li>The terminal is for Wormhole, gates, and experimental mail.</li>
|
||||
@@ -919,7 +919,7 @@ export default function TopRightControls({
|
||||
<li>Mesh remains the public perimeter. Wormhole is the obfuscated commons.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="border border-amber-500/20 bg-amber-950/10 px-4 py-3 text-[9px] font-mono text-amber-200/80 leading-[1.85]">
|
||||
<div className="border border-amber-500/20 bg-amber-950/10 px-4 py-3 text-[12px] font-mono text-amber-200/80 leading-[1.85]">
|
||||
<div className="text-amber-300 tracking-[0.18em]">WORMHOLE CLEANUP:</div>
|
||||
<div className="mt-2">
|
||||
Closing the Infonet terminal will shut down Wormhole automatically. If you force-close
|
||||
@@ -934,7 +934,7 @@ export default function TopRightControls({
|
||||
type="button"
|
||||
onClick={() => void activateWormholeAndLaunchTerminal()}
|
||||
disabled={terminalLaunchBusy}
|
||||
className="px-4 py-3 border border-cyan-500/40 bg-cyan-950/20 hover:bg-cyan-950/35 disabled:opacity-50 text-[11px] font-mono text-cyan-300 tracking-[0.16em]"
|
||||
className="px-4 py-3 border border-cyan-500/40 bg-cyan-950/20 hover:bg-cyan-950/35 disabled:opacity-50 text-[13px] font-mono text-cyan-300 tracking-[0.16em]"
|
||||
>
|
||||
{terminalLaunchBusy
|
||||
? 'ENTERING...'
|
||||
@@ -949,7 +949,7 @@ export default function TopRightControls({
|
||||
onMeshChatNavigate?.('meshtastic');
|
||||
}}
|
||||
disabled={terminalLaunchBusy}
|
||||
className="px-4 py-3 border border-[var(--border-primary)] hover:border-cyan-500/40 disabled:opacity-50 text-[11px] font-mono text-[var(--text-muted)] tracking-[0.16em]"
|
||||
className="px-4 py-3 border border-[var(--border-primary)] hover:border-cyan-500/40 disabled:opacity-50 text-[13px] font-mono text-[var(--text-muted)] tracking-[0.16em]"
|
||||
>
|
||||
GO TO MESH
|
||||
</button>
|
||||
@@ -957,7 +957,7 @@ export default function TopRightControls({
|
||||
type="button"
|
||||
onClick={closeTerminalLauncher}
|
||||
disabled={terminalLaunchBusy}
|
||||
className="px-4 py-3 border border-[var(--border-primary)] hover:border-cyan-500/40 disabled:opacity-50 text-[11px] font-mono text-[var(--text-muted)] tracking-[0.16em]"
|
||||
className="px-4 py-3 border border-[var(--border-primary)] hover:border-cyan-500/40 disabled:opacity-50 text-[13px] font-mono text-[var(--text-muted)] tracking-[0.16em]"
|
||||
>
|
||||
CANCEL
|
||||
</button>
|
||||
|
||||
@@ -106,7 +106,7 @@ export function TrackedFlightLabels({
|
||||
style={{
|
||||
...LABEL_BASE,
|
||||
color: labelColor,
|
||||
fontSize: '10px',
|
||||
fontSize: `${Math.max(10, Math.min(16, 10 + (zoom - 5) * 1.2))}px`,
|
||||
textShadow: LABEL_SHADOW_EXTRA,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
@@ -212,9 +212,11 @@ export function TrackedYachtLabels({ ships, inView, interpShip }: TrackedYachtLa
|
||||
interface UavLabelsProps {
|
||||
uavs: UAV[];
|
||||
inView: (lat: number, lng: number) => boolean;
|
||||
zoom?: number;
|
||||
}
|
||||
|
||||
export function UavLabels({ uavs, inView }: UavLabelsProps) {
|
||||
export function UavLabels({ uavs, inView, zoom = 5 }: UavLabelsProps) {
|
||||
const labelSize = `${Math.max(10, Math.min(16, 10 + (zoom - 5) * 1.2))}px`;
|
||||
return (
|
||||
<>
|
||||
{uavs.map((uav, i) => {
|
||||
@@ -234,7 +236,7 @@ export function UavLabels({ uavs, inView }: UavLabelsProps) {
|
||||
style={{
|
||||
...LABEL_BASE,
|
||||
color: '#ff8c00',
|
||||
fontSize: '10px',
|
||||
fontSize: labelSize,
|
||||
textShadow: LABEL_SHADOW_EXTRA,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user