Merge branch 'main' into gsd/phase-13-aiui-functional-conversational-node-control-and-content-surf

# Conflicts:
#	.planning/config.json
This commit is contained in:
archipelago
2026-08-06 16:02:47 -04:00
26 changed files with 1438 additions and 67 deletions
@@ -256,8 +256,11 @@
<p v-if="effectiveKind === 'meshcore' && rfPreset" class="text-[11px] text-sky-300/80 mt-1">
MeshCore RF params for {{ selectedRegion?.code }} are applied to the radio automatically on connect.
</p>
<p v-if="effectiveKind === 'reticulum'" class="text-[11px] text-sky-300/80 mt-1">
RNode radio parameters (frequency / bandwidth / SF / CR) are managed by the Reticulum daemon's interface config on this node.
<p v-if="effectiveKind === 'reticulum' && rnodePlan" class="text-[11px] text-sky-300/80 mt-1">
RNode plan for {{ form.region }}: {{ (rnodePlan.frequency / 1e6).toFixed(4) }} MHz, {{ rnodePlan.bandwidth / 1000 }} kHz, SF{{ rnodePlan.spreading_factor }}, CR4/{{ rnodePlan.coding_rate }}, {{ rnodePlan.txpower }} dBm applied on connect, and the radio confirms it.
</p>
<p v-else-if="effectiveKind === 'reticulum'" class="text-[11px] text-sky-300/80 mt-1">
Pick a region to apply its recommended RNode RF plan on connect editable any time in Mesh Device settings.
</p>
</div>
@@ -302,7 +305,7 @@ import { useRouter } from 'vue-router'
import BaseModal from '@/components/BaseModal.vue'
import { useMeshStore, type MeshDeviceProbe, type MeshConfigureParams, type FlashFirmwareFamily, type FlashBoard, type FlashJobStatus } from '@/stores/mesh'
import { useAppStore } from '@/stores/app'
import { LORA_REGIONS, regionByCode, suggestRegionFromLatLon, meshcorePlanFor } from '@/utils/loraRegions'
import { LORA_REGIONS, regionByCode, suggestRegionFromLatLon, meshcorePlanFor, RNODE_REGION_PLANS } from '@/utils/loraRegions'
import { resolveMeshDeviceImage } from '@/utils/meshDeviceImages'
const mesh = useMeshStore()
@@ -373,6 +376,10 @@ const form = ref({
})
const selectedRegion = computed(() => regionByCode(form.value.region))
/** The chosen region's recommended RNode RF plan (undefined = none chosen). */
const rnodePlan = computed(() =>
form.value.region ? RNODE_REGION_PLANS[form.value.region] : undefined,
)
// The firmware whose options we surface: the probe result wins, else the
// last connected type, else meshtastic-style (where presets apply).
const effectiveKind = computed(() => {
@@ -501,6 +508,17 @@ async function applySetup() {
}
}
await mesh.configure(params)
// RNode radios: the region's recommended RF plan is applied through the
// Reticulum daemon's persisted settings (mesh.rnode-config-apply), the
// same round-trip the Device panel uses — the radio confirms the values
// itself after the daemon restarts with them. Best-effort here: a
// failure must not abort the connect the user just asked for.
if (effectiveKind.value === 'reticulum' && rnodePlan.value) {
mesh.suppressDeviceDetect()
void mesh
.applyRnodeConfig({ enabled: true, port: null, ...rnodePlan.value })
.catch(() => {})
}
mesh.dismissDetectedDevice(path)
void router.push('/dashboard/mesh')
} catch (e) {