From 56f8b796e6e3cd502aef1c0cd5ffa48a0953a0e5 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Wed, 25 Feb 2026 16:03:47 -0800 Subject: [PATCH] Add scroll to repeater infobox on visualizer --- frontend/src/App.tsx | 14 ++++++++ frontend/src/components/SettingsModal.tsx | 39 +++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bf60bca..26123c2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -52,6 +52,7 @@ import { getStateKey } from './utils/conversationState'; import { appendRawPacketUnique } from './utils/rawPacketIdentity'; import { messageContainsMention } from './utils/messageParser'; import { mergeContactIntoList } from './utils/contactMerge'; +import { getLocalLabel, getContrastTextColor } from './utils/localLabel'; import { cn } from '@/lib/utils'; import type { Contact, Conversation, HealthStatus, Message, MessagePath, RawPacket } from './types'; @@ -66,6 +67,7 @@ export function App() { const [sidebarOpen, setSidebarOpen] = useState(false); const [showCracker, setShowCracker] = useState(false); const [crackerRunning, setCrackerRunning] = useState(false); + const [localLabel, setLocalLabel] = useState(getLocalLabel); // Defer CrackerPanel mount until first opened (lazy-loaded, but keep mounted after for state) const crackerMounted = useRef(false); @@ -467,6 +469,17 @@ export function App() { return (
+ {localLabel.text && ( +
+ {localLabel.text} +
+ )}
diff --git a/frontend/src/components/SettingsModal.tsx b/frontend/src/components/SettingsModal.tsx index e7a43ae..969de08 100644 --- a/frontend/src/components/SettingsModal.tsx +++ b/frontend/src/components/SettingsModal.tsx @@ -25,6 +25,7 @@ import { setReopenLastConversationEnabled, } from '../utils/lastViewedConversation'; import { RADIO_PRESETS } from '../utils/radioPresets'; +import { getLocalLabel, setLocalLabel, type LocalLabel } from '../utils/localLabel'; import { SETTINGS_SECTION_LABELS, type SettingsSection } from './settingsConstants'; @@ -42,6 +43,7 @@ interface SettingsModalBaseProps { onAdvertise: () => Promise; onHealthRefresh: () => Promise; onRefreshAppSettings: () => Promise; + onLocalLabelChange?: (label: LocalLabel) => void; } type SettingsModalProps = SettingsModalBaseProps & @@ -65,6 +67,7 @@ export function SettingsModal(props: SettingsModalProps) { onAdvertise, onHealthRefresh, onRefreshAppSettings, + onLocalLabelChange, } = props; const externalSidebarNav = props.externalSidebarNav === true; const desktopSection = props.externalSidebarNav ? props.desktopSection : undefined; @@ -118,6 +121,8 @@ export function SettingsModal(props: SettingsModalProps) { const [reopenLastConversation, setReopenLastConversation] = useState( getReopenLastConversationEnabled ); + const [localLabelText, setLocalLabelText] = useState(() => getLocalLabel().text); + const [localLabelColor, setLocalLabelColor] = useState(() => getLocalLabel().color); // Advertisement interval state (displayed in hours, stored as seconds in DB) const [advertIntervalHours, setAdvertIntervalHours] = useState('0'); @@ -1104,6 +1109,40 @@ export function SettingsModal(props: SettingsModalProps) {

+ + +
+ +
+ { + const text = e.target.value; + setLocalLabelText(text); + setLocalLabel(text, localLabelColor); + onLocalLabelChange?.({ text, color: localLabelColor }); + }} + placeholder="e.g. Home Base, Field Radio 2" + className="flex-1" + /> + { + const color = e.target.value; + setLocalLabelColor(color); + setLocalLabel(localLabelText, color); + onLocalLabelChange?.({ text: localLabelText, color }); + }} + className="w-10 h-9 rounded border border-input cursor-pointer bg-transparent p-0.5" + /> +
+

+ Display a colored banner at the top of the page to identify this instance. This + applies only to this device/browser. +

+
+ {getSectionError('database') && (
{getSectionError('database')}
)}