Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
959a7f5
fix(sdk): keep config fields a form doesn't render when staging a sec…
danditomaso Oct 17, 2026
151fadd
feat(web): restructure Settings navigation to match the Android app
danditomaso Oct 17, 2026
49ce480
feat(web): Android-style Channels page
danditomaso Oct 17, 2026
88ba0e4
feat(web): align radio config pages with Android's groups and fields
danditomaso Oct 17, 2026
632802a
feat(web): align module config pages with Android's groups and fields
danditomaso Oct 17, 2026
5524211
feat(web): add Administration, Backup & Restore, Advanced, App and Ab…
danditomaso Oct 17, 2026
b2d12e0
fix(web): stage the fixed position once and show staged edits as unsaved
danditomaso Oct 17, 2026
33818ca
fix(web): keep every theme control on the same preference
danditomaso Oct 17, 2026
316b1dc
chore(web): remove the Status Message module form
danditomaso Oct 17, 2026
7ae584d
fix(web): don't drop a staged fixed position when the Position tab re…
danditomaso Oct 17, 2026
46fc42c
fix(web): keep the sidebar focus ring inside the button
danditomaso Oct 17, 2026
2be3126
feat(web): move theme and language out of the sidebar panel
danditomaso Oct 17, 2026
f5548b6
feat(web): list every Settings page and tab in the command menu
danditomaso Oct 17, 2026
9144c51
Merge upstream/main into feat/settings-android-parity
danditomaso Oct 18, 2026
98e189b
fix(web): keep a channel's precision and avoid empty profile exports
danditomaso Oct 18, 2026
587581c
fix(web): only require a six-digit Bluetooth PIN in fixed PIN mode
danditomaso Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 74 additions & 26 deletions apps/web/public/i18n/locales/en/channels.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,49 +7,97 @@
"import": "Import",
"export": "Export"
},
"validation": {
"pskInvalid": "Please enter a valid {{bits}} bit PSK."
"header": {
"frequency": "Freq: {{frequency}} MHz",
"slot": "Slot: {{slot}}",
"slotAuto": "Slot: Auto"
},
"settings": {
"label": "Channel Settings",
"description": "Crypto, MQTT & misc settings"
"list": {
"add": "Add channel",
"edit": "Edit {{name}}",
"delete": "Delete {{name}}"
},
"role": {
"label": "Role",
"description": "Device telemetry is sent over PRIMARY. Only one PRIMARY allowed",
"options": {
"primary": "PRIMARY",
"disabled": "DISABLED",
"secondary": "SECONDARY"
"legend": {
"primary": "Primary",
"secondary": "Secondary",
"dialog": {
"title": "Channel Features",
"description": "What the primary and secondary channels do and what the channel icons mean.",
"primaryFeature": "Periodic position and telemetry broadcast",
"secondaryNoTelemetry": "No periodic telemetry broadcast",
"secondaryPositionFeature": "Disabling position on the primary channel allows periodic position broadcasts on the first secondary channel with the position enabled, otherwise manual position request required.",
"manualPositionRequest": "Manual position request required",
"iconMeanings": "Icon Meanings"
}
},
"icons": {
"location": {
"label": "Location Sharing",
"description": "Periodic position broadcast"
},
"uplink": {
"label": "MQTT Uplink Enabled",
"description": "Messages from the mesh will be sent to the public internet through any node's configured gateway."
},
"downlink": {
"label": "MQTT Downlink Enabled",
"description": "Messages from a public internet gateway are forwarded to the local mesh. Due to the zero-hop policy, traffic from the default MQTT server will not propagate further than this device."
}
},
"security": {
"label": "Security Status",
"secure": {
"label": "Secure",
"description": "A green lock means the channel is securely encrypted with either a 128 or 256 bit AES key."
},
"insecureNoPrecise": {
"label": "Insecure Channel, Not Precise",
"description": "A yellow open lock means the channel is not securely encrypted, is not used for precise location data, and uses either no key at all or a 1 byte known key."
},
"insecurePreciseOnly": {
"label": "Insecure Channel, Precise Location",
"description": "A red open lock means the channel is not securely encrypted, is used for precise location data, and uses either no key at all or a 1 byte known key."
},
"insecurePreciseMqtt": {
"label": "Warning: Insecure, Precise Location & MQTT Uplink",
"description": "A red open lock with a warning means the channel is not securely encrypted, is used for precise location data which is being uplinked to the internet via MQTT, and uses either no key at all or a 1 byte known key."
}
},
"editDialog": {
"addTitle": "Add channel",
"editTitle": "Edit channel {{index}}",
"description": "Changes are sent to the radio when you save the settings."
},
"validation": {
"pskInvalid": "Please enter a valid {{bits}} bit PSK."
},
"name": {
"label": "Channel Name",
"description": "Up to 11 bytes. Leave blank to use the default name."
},
"psk": {
"label": "Pre-Shared Key",
"label": "PSK",
"description": "Supported PSK lengths: 256-bit, 128-bit, 8-bit, Empty (0-bit)",
"generate": "Generate"
},
"name": {
"label": "Name",
"description": "A unique name for the channel <12 bytes, leave blank for default"
},
"uplinkEnabled": {
"label": "Uplink Enabled",
"label": "MQTT Uplink Enabled",
"description": "Send messages from the local mesh to MQTT"
},
"downlinkEnabled": {
"label": "Downlink Enabled",
"label": "MQTT Downlink Enabled",
"description": "Send messages from MQTT to the local mesh"
},
"useAead": {
"label": "Authenticated Encryption (Experimental)",
"description": "Encrypt with AES-CCM, which stops forged and tampered messages. Every node on this channel must turn this on: the channel then rejects standard AES-CTR packets, so nodes without it can no longer talk here."
"positionEnabled": {
"label": "Position enabled",
"description": "Share this node's position on this channel."
},
"preciseLocation": {
"label": "Precise location"
},
"positionPrecision": {
"label": "Location",
"description": "The precision of the location to share with the channel. Choose Position sharing off to share none.",
"label": "Location precision",
"options": {
"none": "Position sharing off",
"precise": "Precise Location",
"metric_km23": "Within 23 kilometers",
"metric_km12": "Within 12 kilometers",
"metric_km5_8": "Within 5.8 kilometers",
Expand Down
3 changes: 3 additions & 0 deletions apps/web/public/i18n/locales/en/commandPalette.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
"nodes": "Nodes"
}
},
"settings": {
"label": "Settings"
},
"manage": {
"label": "Manage",
"command": {
Expand Down
111 changes: 66 additions & 45 deletions apps/web/public/i18n/locales/en/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,28 @@
"tabSecurity": "Security"
},
"sidebar": {
"label": "Configuration"
"label": "Configuration",
"configuration": "Configuration",
"backupRestore": "Backup & Restore",
"advanced": "Advanced",
"app": "App",
"information": "Information"
},
"navigation": {
"user": "User",
"lora": "LoRa",
"channels": "Channels",
"security": "Security",
"deviceConfiguration": "Device configuration",
"moduleConfiguration": "Module configuration",
"administration": "Administration",
"deviceProfile": "Import / export configuration",
"firmwareUpdate": "Firmware update",
"cleanNodeDb": "Clean node database",
"debugPanel": "Debug panel",
"appSettings": "App settings",
"about": "About",
"help": "Help & documentation"
},
"device": {
"title": "Device",
Expand All @@ -26,17 +47,17 @@
"description": "Buzzer pin override",
"label": "Buzzer GPIO"
},
"disableTripleClick": {
"description": "Disable triple click",
"label": "Disable Triple Click"
"tripleClickAdHocPing": {
"label": "Triple Click Ad Hoc Ping",
"description": "Send a position on the primary channel when the user button is triple clicked."
},
"doubleTapAsButtonPress": {
"description": "Treat double tap as button press",
"label": "Double Tap as Button Press"
"label": "Double Tap as Button"
},
"ledHeartbeatDisabled": {
"description": "Disable default blinking LED",
"label": "LED Heartbeat Disabled"
"ledHeartbeat": {
"label": "LED Heartbeat",
"description": "Controls the blinking LED on the device. For most devices this controls one of up to 4 LEDs; the charger and GPS LEDs are not controllable."
},
"nodeInfoBroadcastInterval": {
"description": "How often to broadcast node info",
Expand Down Expand Up @@ -136,15 +157,11 @@
},
"twelveHourClock": {
"description": "Display time in 12h format",
"label": "Use 12h clock format"
"label": "12 Hour Clock"
},
"wakeOnTapOrMotion": {
"description": "Wake the device on tap or motion",
"label": "Wake on tap or motion"
},
"enableMessageBubbles": {
"label": "Message Bubbles",
"description": "Display incoming messages as bubbles on the device screen"
"label": "Wake screen on tap or motion"
},
"deviceDisplay": {
"label": "Device Display",
Expand Down Expand Up @@ -174,17 +191,20 @@
"description": "The denominator of the coding rate",
"label": "Coding Rate"
},
"frequencyOffset": {
"description": "Frequency offset to correct for crystal calibration errors",
"label": "Frequency Offset"
"codingRateOverride": {
"label": "Coding Rate Override",
"description": "Adds error correction on top of the preset. A higher coding rate makes every packet longer on air and uses more of the duty cycle and channel utilization budget.",
"maxDescription": "This preset already uses the highest coding rate.",
"presetDefault": "Preset default ({{rate}})",
"fraction": "4/{{rate}}"
},
"frequencySlot": {
"description": "Your node's operating frequency is calculated based on the region, modem preset, and this field. When 0, the slot is automatically calculated based on the primary channel name and will change from the default public slot.",
"label": "Frequency Slot"
},
"hopLimit": {
"description": "Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. 0 hop broadcast messages will not get ACKs.",
"label": "Number of Hops"
"label": "Hop Limit"
},
"ignoreMqtt": {
"description": "Don't forward MQTT messages over the mesh",
Expand All @@ -208,6 +228,10 @@
"description": "Override frequency",
"label": "Frequency Override"
},
"paFanDisabled": {
"label": "PA Fan Disabled",
"description": "Turn off the power amplifier's cooling fan"
},
"region": {
"description": "The region where you will be using your radios.",
"licensedOnlySuffix": "(licensed operators only)",
Expand Down Expand Up @@ -241,14 +265,6 @@
"label": "Radio Settings",
"description": "Settings for the LoRa radio"
},
"femLnaMode": {
"label": "FEM/LNA Mode",
"description": "Control an external RF front-end module / low-noise amplifier (only on boards that have one)"
},
"serialHalOnly": {
"label": "Serial HAL Only",
"description": "Communicate with the radio exclusively over the serial hardware abstraction layer"
},
"optionsCard": {
"label": "Options",
"description": "LoRa core options"
Expand All @@ -264,7 +280,7 @@
"note": "Note: Some devices (ESP32) cannot use both Bluetooth and WiFi at the same time.",
"addressMode": {
"description": "Address assignment selection",
"label": "IPv4 mode"
"label": "Address mode"
},
"dns": {
"description": "DNS Server",
Expand Down Expand Up @@ -298,9 +314,9 @@
"description": "Enable or disable the WiFi radio",
"label": "WiFi enabled"
},
"meshViaUdp": {
"label": "Enabled",
"description": "Forward mesh packets over UDP"
"udpBroadcast": {
"label": "UDP broadcasting",
"description": "Enable broadcasting packets via UDP over the local network."
},
"ntpServer": {
"label": "NTP server"
Expand Down Expand Up @@ -370,11 +386,11 @@
},
"gpsMode": {
"description": "Configure whether device GPS is Enabled, Disabled, or Not Present",
"label": "GPS Mode (Physical Hardware)"
"label": "GPS Mode"
},
"gpsUpdateInterval": {
"description": "How often should we try to get a GPS position (<10sec keeps GPS on).",
"label": "GPS Polling Interval"
"label": "Update Interval"
},
"positionFlags": {
"description": "Optional fields to include when assembling position messages. The more fields are included, the larger the message will be — leading to longer airtime and a higher risk of packet loss.",
Expand All @@ -390,11 +406,11 @@
},
"smartPositionMinDistance": {
"description": "The minimum distance change in meters to be considered for a smart position broadcast.",
"label": "Smart Distance"
"label": "Minimum Distance"
},
"smartPositionMinInterval": {
"description": "The fastest that position updates will be sent if the minimum distance has been satisfied.",
"label": "Smart Interval"
"label": "Minimum Interval"
},
"transmitPin": {
"description": "GPS transmit GPIO pin override",
Expand Down Expand Up @@ -439,7 +455,11 @@
"power": {
"adcMultiplierOverride": {
"description": "Used for tweaking battery voltage reading",
"label": "ADC multiplier override ratio"
"label": "ADC Override"
},
"adcOverrideEnabled": {
"label": "ADC multiplier override",
"description": "Override the battery voltage reading's ADC multiplier"
},
"ina219Address": {
"description": "Address of the INA219 battery monitor",
Expand All @@ -459,7 +479,7 @@
},
"powerSavingEnabled": {
"description": "Select if powered from a low-current source (i.e. solar), to minimize power consumption as much as possible.",
"label": "Enable power saving mode"
"label": "Power saving"
},
"shutdownOnBatteryDelay": {
"description": "Automatically shutdown node after this long when on battery, 0 for indefinite",
Expand All @@ -485,10 +505,6 @@
"security": {
"description": "Settings for the Security configuration",
"title": "Security",
"adminChannelEnabled": {
"description": "Allow incoming device control over the insecure legacy admin channel",
"label": "Legacy Admin channel"
},
"enableDebugLogApi": {
"description": "Output live debug logging over serial, view and export position-redacted device logs over Bluetooth",
"label": "Debug log API enabled"
Expand Down Expand Up @@ -543,21 +559,22 @@
},
"administration": {
"label": "Administration",
"description": "Managed-mode and legacy admin channel"
"description": "Managed mode and lockdown"
},
"packetSignaturesCard": {
"label": "Packet Signatures",
"label": "Packet authenticity",
"description": "How the device treats signed and unsigned packets it receives (firmware 2.8+)"
},
"packetSignaturePolicy": {
"label": "Packet Signature Policy",
"label": "Protection level",
"description": "Compatible accepts unsigned packets but drops any with a wrong signature. Balanced also rejects unsigned broadcasts from nodes that have signed before. Strict accepts only packets with a verified signature or successful PKI decryption, so packets from older nodes may be ignored.",
"compatible": "Compatible - Accept Unsigned",
"balanced": "Balanced - Prefer Authenticated",
"strict": "Strict - Require Authentication"
"strict": "Strict - Require Authentication",
"unsupported": "This device does not support packet signature verification."
},
"lockdown": {
"label": "Lockdown",
"label": "Lockdown mode",
"description": "Encrypts the radio's stored settings and withholds them from clients until they unlock it with a passphrase.",
"status": "Status",
"state": {
Expand Down Expand Up @@ -613,6 +630,10 @@
"hardwareModel": {
"label": "Hardware model",
"description": "Read-only — reported by the device"
},
"statusMessage": {
"label": "Status Message",
"description": "A public status message that is broadcast to the mesh on change and every 12 hours. Up to 80 bytes."
}
}
}
Loading
Loading