From a5972d646fed84dfacc064c232f419d671bc089d Mon Sep 17 00:00:00 2001 From: christian Date: Thu, 8 Oct 2026 09:52:21 -0400 Subject: [PATCH] Hold the page number key for one second to jump to the status page. A tap still goes to the start page, now on release so a hold does not flash through page 1 first. Hold time is nav_hold_ms in config.json. Co-Authored-By: Claude Opus 5.5 --- README.md | 5 ++++- config.json | 1 + make_page.py | 20 +++++++++++++++++--- status-page.companionconfig | Bin 5099 -> 5125 bytes tests/test_status_panel.py | 7 ++++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 757604d..bb91855 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,10 @@ Row 0 holds the group buttons. Each one shows the first problem in its group, pl ## Page keys on every page -The **page number key** on every page follows the ALL button. It's green when everything is fine, flashes red when something is wrong, and turns purple if the watchdog stops. UP and DOWN stay normal. Which keys follow ALL is `"nav_keys"` in `config.json` (`["pagenum"]`; add `"pageup"` / `"pagedown"` to colour those too). The coloured key is Companion's own key converted to a regular button (`nav_templates.json`), so a press works exactly as before. +The **page number key** on every page follows the ALL button. It's green when everything is fine, flashes red when something is wrong, and turns purple if the watchdog stops. UP and DOWN stay normal. Which keys follow ALL is `"nav_keys"` in `config.json` (`["pagenum"]`; add `"pageup"` / `"pagedown"` to colour those too). The coloured key is Companion's own key converted to a regular button (`nav_templates.json`). + +- **Tap the page number:** go to the start page (page 1), as before. It now acts when you let go, not when you press. +- **Hold the page number for 1 second:** jump to the status page (page 19). The hold time is `"nav_hold_ms"` in `config.json`. - `python3 nav_keys.py` applies them to every page. Run it again after adding pages. - `python3 nav_keys.py --revert` puts Companion's built-in nav keys back. diff --git a/config.json b/config.json index bdd5a8e..98fbddd 100644 --- a/config.json +++ b/config.json @@ -3,6 +3,7 @@ "page": 19, "page_name": "19\\nStatus", "nav_keys": ["pagenum"], + "nav_hold_ms": 1000, "variables": { "state": "sp_state", "heartbeat": "sp_hb", "command": "sp_cmd" }, "service_windows": [ diff --git a/make_page.py b/make_page.py index b458607..afb1a3b 100644 --- a/make_page.py +++ b/make_page.py @@ -136,8 +136,13 @@ NAV_LOCATIONS = {"pageup": ("0", "0"), "pagenum": ("1", "0"), "pagedown": ("2", NAV_MARKER = "STATUS nav key" -def nav_key(kind, names): - """Companion's own UP / page number / DOWN key, coloured like the ALL button.""" +def nav_key(kind, names, status_page=None, hold_ms=1000): + """Companion's own UP / page number / DOWN key, coloured like the ALL button. + + For the page number key, status_page adds: tap = Companion's usual "go to the + start page" (moved to release, so a hold does not flash through it first), + hold for hold_ms = go to the status page. + """ templates = json.load(open(os.path.join(HERE, "nav_templates.json"))) key = json.loads(json.dumps(templates[kind])) stale = "$(internal:time_unix) - $(custom:%s) > %d" % (names["heartbeat"], STALE_SECONDS) @@ -155,6 +160,14 @@ def nav_key(kind, names): layer["borderColor"] = prop(fg, True) paint(layer.get("children", [])) paint(key["style"]["layers"]) + if kind == "pagenum" and status_page: + step = key["steps"]["0"] + tap = step["action_sets"]["down"] + hold = [{"type": "action", "id": stable_id("nav", "hold"), "definitionId": "set_page", + "connectionId": "internal", + "options": {"surfaceId": prop("self"), "page": prop(status_page)}, "children": {}}] + step["action_sets"] = {"down": [], "up": tap, str(hold_ms): hold} + step["options"]["runWhileHeld"] = [hold_ms] key["options"]["notes"] = ("%s (%s): colour follows the ALL button on the status page. " "Undo on every page with: python3 nav_keys.py --revert" % (NAV_MARKER, kind)) return key @@ -164,7 +177,8 @@ def build(cfg): controls = {} coloured = cfg.get("nav_keys", list(NAV_LOCATIONS)) # which nav keys follow the ALL button for kind, (r, c) in NAV_LOCATIONS.items(): - controls.setdefault(r, {})[c] = nav_key(kind, cfg["variables"]) if kind in coloured else {"type": kind} + controls.setdefault(r, {})[c] = (nav_key(kind, cfg["variables"], cfg["page"], cfg.get("nav_hold_ms", 1000)) + if kind in coloured else {"type": kind}) for tile in cfg["tiles"]: if "pos" not in tile: continue diff --git a/status-page.companionconfig b/status-page.companionconfig index 09bc048c22ea8a41329fbee85123ab21ab044cfa..fd30f6d680884605a162086e0e6d8ff259b95bec 100644 GIT binary patch literal 5125 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 G+W!Dx92gw{ literal 5099 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 diff --git a/tests/test_status_panel.py b/tests/test_status_panel.py index 62c7952..0aecce4 100644 --- a/tests/test_status_panel.py +++ b/tests/test_status_panel.py @@ -561,7 +561,12 @@ class TestPage(unittest.TestCase): key = self.controls[r]["0"] self.assertEqual(key["type"], "button-layered") self.assertTrue(key["options"]["notes"].startswith("STATUS nav key (%s)" % kind)) - self.assertEqual(key["steps"]["0"]["action_sets"]["down"][0]["definitionId"], action) + step = key["steps"]["0"] + self.assertEqual(step["action_sets"]["down"], []) + tap, hold = step["action_sets"]["up"][0], step["action_sets"]["1000"][0] + self.assertEqual((tap["definitionId"], tap["options"]["page"]["value"]), (action, "startup")) + self.assertEqual((hold["definitionId"], hold["options"]["page"]["value"]), ("set_page", 19)) + self.assertEqual(step["options"]["runWhileHeld"], [1000]) box = [l for l in key["style"]["layers"] if l["type"] == "box"][0] self.assertTrue(box["color"]["isExpression"]) self.assertIn("'$.all.b'", box["color"]["value"]) # follows the ALL button