diff --git a/internal/core/repeaters.go b/internal/core/repeaters.go
index b2e5ee7..4dc25eb 100644
--- a/internal/core/repeaters.go
+++ b/internal/core/repeaters.go
@@ -6,6 +6,7 @@ import (
"fmt"
"html/template"
"image/color"
+ "math"
"net/http"
"net/url"
"strconv"
@@ -175,15 +176,17 @@ func (s *Handlers) handleAddRepeater(w http.ResponseWriter, r *http.Request) {
}
// parseRadioForm reads and validates the radio fields from a repeater form.
+// Frequency and bandwidth are entered in MHz/kHz (matching the region presets)
+// and stored as Hz.
func parseRadioForm(r *http.Request) (freq, bw int64, sf, cr int16, ok bool) {
- f, e1 := strconv.ParseInt(r.FormValue("radio_freq_hz"), 10, 64)
- b, e2 := strconv.ParseInt(r.FormValue("radio_bw_hz"), 10, 64)
+ mhz, e1 := strconv.ParseFloat(r.FormValue("radio_freq_mhz"), 64)
+ khz, e2 := strconv.ParseFloat(r.FormValue("radio_bw_khz"), 64)
s, e3 := strconv.ParseInt(r.FormValue("radio_sf"), 10, 16)
c, e4 := strconv.ParseInt(r.FormValue("radio_cr"), 10, 16)
- if e1 != nil || e2 != nil || e3 != nil || e4 != nil || f <= 0 || b <= 0 {
+ if e1 != nil || e2 != nil || e3 != nil || e4 != nil || mhz <= 0 || khz <= 0 {
return 0, 0, 0, 0, false
}
- return f, b, int16(s), int16(c), true
+ return int64(math.Round(mhz * 1e6)), int64(math.Round(khz * 1e3)), int16(s), int16(c), true
}
// pageEditRepeater shows the edit form for an owned repeater.
diff --git a/internal/core/templates/add_repeater.html b/internal/core/templates/add_repeater.html
index dd09f02..7155be8 100644
--- a/internal/core/templates/add_repeater.html
+++ b/internal/core/templates/add_repeater.html
@@ -93,8 +93,8 @@
@@ -112,23 +112,45 @@
{{end}}
diff --git a/internal/core/templates/confirm.html b/internal/core/templates/confirm.html
index 94af626..4a27bc1 100644
--- a/internal/core/templates/confirm.html
+++ b/internal/core/templates/confirm.html
@@ -16,7 +16,7 @@
to verify it has access. This is optional — the repeater works either way.
{{.Repeater.PublicKeyHex}}
- {{.Repeater.RadioFreqHz}} Hz · BW {{.Repeater.RadioBwHz}} · SF{{.Repeater.RadioSF}} · CR{{.Repeater.RadioCR}}
+ {{mhz .Repeater.RadioFreqHz}} MHz · BW {{khz .Repeater.RadioBwHz}} · SF{{.Repeater.RadioSF}} · CR{{.Repeater.RadioCR}}
This browser does not support WebSerial. Use Chrome, Edge, or another Chromium-based browser over HTTPS or on localhost.
diff --git a/internal/core/templates/edit_repeater.html b/internal/core/templates/edit_repeater.html
index 475b663..4300cef 100644
--- a/internal/core/templates/edit_repeater.html
+++ b/internal/core/templates/edit_repeater.html
@@ -29,8 +29,8 @@
@@ -55,22 +55,44 @@
{{end}}
diff --git a/internal/web/env.go b/internal/web/env.go
index 3d2bbe2..bb3473d 100644
--- a/internal/web/env.go
+++ b/internal/web/env.go
@@ -12,6 +12,7 @@ import (
"net"
"net/http"
"net/url"
+ "strconv"
"strings"
"github.com/go-chi/chi/v5"
@@ -117,8 +118,16 @@ func (e *Env) SetDefaultLayout(name string) { e.Renderer.defaultLayout = name }
// NewRenderer parses the shared base layout (base.html + icons.html) and composes
// each of the surface's own *.html pages onto it. Each page redefines the
// content/title/header blocks, so every page gets its own cloned template set.
+// templateFuncs are helpers available to every page template. mhz/khz present
+// the Hz-canonical radio values in the human-readable units the region presets
+// use (MHz for frequency, kHz for bandwidth), formatted without trailing zeros.
+var templateFuncs = template.FuncMap{
+ "mhz": func(hz int64) string { return strconv.FormatFloat(float64(hz)/1e6, 'f', -1, 64) },
+ "khz": func(hz int64) string { return strconv.FormatFloat(float64(hz)/1e3, 'f', -1, 64) },
+}
+
func NewRenderer(cfg *config.Config, surfaceTemplates fs.FS) (*Renderer, error) {
- base, err := template.New("").ParseFS(sharedTemplatesFS, "templates/base.html", "templates/icons.html", "templates/org_tabs.html", "templates/repeater_tabs.html")
+ base, err := template.New("").Funcs(templateFuncs).ParseFS(sharedTemplatesFS, "templates/base.html", "templates/icons.html", "templates/org_tabs.html", "templates/repeater_tabs.html")
if err != nil {
return nil, err
}