Repository navigation
fix(app,web): hang a Discover-heard repeater's star from its own position - #724
Conversation
c0fe8a3 to
af00c86
Compare
|
Review before merge: one point to change, one question. #725, #726 and #729 sit on top of this one. Point 1's call sites checked at af00c86; the effect is read from the code, not run. 1. Dot hue and selection dimming still key a Discover hearing by its prefix (change requested)
For a
Proposed: one 2. Question: the app pairs over two different node sets |
…tion A Discover reply carries a prefix of the responder's key. The node layer paired it with the registry node whose key it alone starts (efiten#661); the reach stars did not, and looked the prefix up as a whole key, which it never is. So the repeater drew its ▲ and, beside it, a star hung from its RSSI estimate with the prefix as its name. The pairing is now one function, registryMatcher in nodelayer.js, which groupSenderPointsForNodes and coverageStars both use; AGENTS.md §7 names both. On the data of 8 Aug to 7 Sep around Nijmegen, 63 of 71 stars now hang from their ▲, where 5 of 66 did. Closes efiten#723 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
af00c86 to
82588a0
Compare
|
Pushed 82588a0 for the review, rebased on master. Per point:
App: 1637 tests, eslint, build. Web: 763 tests, eslint, Playwright 297 passed. |
Closes #666 **Builds on #724:** the first commit is that PR (a Discover-heard repeater's star under its own ▲). Review the second; it rebases to one commit once #724 is in. ## What An **Export** button in #bar, next to Start mapping, opens a sheet with the first export of #665: **Repeaters heard**, the map as it stands (filters, window, hunters) as a 1200×1200 PNG, saved as `mesh-hunter-repeaters-heard-YYYY-MM-DD.png`. Below 640px the button moves into the menu with Start mapping and Log in (`barnarrow.js`, #561). The picture shows the stars the reach layer would draw for the view, from the same registry slice, window receptions and attribution, whether the layer is on or not: - every repeater in its own hue (`assignHues`), each ray's strength as a gradient within it: strong full and thick, weak lighter and thinner, with the map's width and opacity (`rayStyle`); - a dot per hearing, the ▲ or ● the star hangs from, and the names that fit (most-heard first, dropped on overlap, never the ▲); - the mapped cells (the resolution closest to 360 Mercator units, res 8 today) and the route in grey; a star whose nearest hearing is more than 30 km off is left out and named; - a band with the mark, the title, the window as dates, up to three hunters, three numbers (repeaters, receptions, farthest) and a legend; - always the light theme. Decided with Kasper on mockups rendered from a real `/api/points` export; what was tried and not chosen (signal-tier colours, #720's hex fill over every repeater) is in `docs/2026-09-27-export-repeaters-heard.md`. ## How - `exportheard.js`: the rules (which stars, the numbers, the route, the cells, label placement, the texts), unit-tested. - `exportrender.js`: a second MapLibre instance offscreen at 1200×1000 with `preserveDrawingBuffer` (the live map is untouched), then a canvas for the glyphs, names, scale bar and band. The bare background stands in when the hosted style does not load. - `exportsheet.js`: the sheet, on the `.lc-modal` shell with the settings sheet's focus rules. - Members only, like the node positions it is made of; anyone else sees the item disabled with the reason. - `hexgrid.js` is copied whole from the app and added to the parity list, which #720 needs too. ## Verification - `exportheard.test.js` (18): what is drawn, the 30 km rule, the numbers, the route breaks, the cells, label placement, tint, window text, hunters, file name, scale bar. Five rules mutated, each caught. - `e2e/export.spec.js`: a member downloads a PNG of 1200×1200 with the right name; a guest sees the reason and a disabled button; Escape closes and hands focus back. Red with the sheet unwired. - Rendered in Chromium with the real export of 8 Aug to 7 Sep around Nijmegen: 87 repeaters, 4501 receptions. - web: 789 tests green, eslint clean, full e2e 301 passed 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: efiten <erwin.fiten@gmail.com>
🤖 I have created a release *beep* *boop* --- <details><summary>app: 1.30.0</summary> ## [1.30.0](app-v1.29.1...app-v1.30.0) (2026-09-30) ### Features * **app,web:** ease what the map shows by zoom, on one hex system ([#734](#734)) ([4c2a57b](4c2a57b)) * **app,web:** name a relay on the map the way the app does ([#733](#733)) ([a9aa01a](a9aa01a)) * **app:** send one broker to several collectors under one switch ([#722](#722)) ([8c6da30](8c6da30)) * **web:** export the repeaters heard in view as a 1200×1200 PNG ([#725](#725)) ([d6186bc](d6186bc)) ### Bug Fixes * **app,web:** hang a Discover-heard repeater's star from its own position ([#724](#724)) ([15a6ada](15a6ada)) * **app,web:** put the caret in the target picker's search when it opens ([#721](#721)) ([6bd899c](6bd899c)) * **app:** drop the float window's previous and next buttons ([#717](#717)) ([c957ff3](c957ff3)) * **app:** give the sound the playback buffer instead of the smallest ([#712](#712)) ([dee5484](dee5484)) * **app:** hide Chrome's pause on the fullscreen readout ([#715](#715)) ([836d91e](836d91e)) * **app:** keep a space under the HUD on Android ([#710](#710)) ([2754716](2754716)) * **app:** let the fullscreen readout turn with the phone ([#718](#718)) ([fae3749](fae3749)) * **app:** put the transmit pop under the receptions it sat over ([#738](#738)) ([2bfc5fa](2bfc5fa)) * **server:** keep the node registry in memory on a loop of its own ([#736](#736)) ([4f3e988](4f3e988)) * **web:** keep the map bar on one row between 641 and 767 px ([#732](#732)) ([c44dc1d](c44dc1d)) </details> <details><summary>server: 1.10.1</summary> ## [1.10.1](server-v1.10.0...server-v1.10.1) (2026-09-30) ### Bug Fixes * **server:** keep the node registry in memory on a loop of its own ([#736](#736)) ([4f3e988](4f3e988)) * **server:** read a refused frame again when the message-id decoder changes ([#737](#737)) ([8586c71](8586c71)) </details> <details><summary>web: 1.26.0</summary> ## [1.26.0](web-v1.25.1...web-v1.26.0) (2026-09-30) ### Features * **app,web:** ease what the map shows by zoom, on one hex system ([#734](#734)) ([4c2a57b](4c2a57b)) * **app,web:** name a relay on the map the way the app does ([#733](#733)) ([a9aa01a](a9aa01a)) * **app:** send one broker to several collectors under one switch ([#722](#722)) ([8c6da30](8c6da30)) * **web:** export the repeaters heard in view as a 1200×1200 PNG ([#725](#725)) ([d6186bc](d6186bc)) ### Bug Fixes * **app,web:** hang a Discover-heard repeater's star from its own position ([#724](#724)) ([15a6ada](15a6ada)) * **app,web:** put the caret in the target picker's search when it opens ([#721](#721)) ([6bd899c](6bd899c)) * **app:** drop the float window's previous and next buttons ([#717](#717)) ([c957ff3](c957ff3)) * **app:** give the sound the playback buffer instead of the smallest ([#712](#712)) ([dee5484](dee5484)) * **app:** hide Chrome's pause on the fullscreen readout ([#715](#715)) ([836d91e](836d91e)) * **app:** keep a space under the HUD on Android ([#710](#710)) ([2754716](2754716)) * **app:** let the fullscreen readout turn with the phone ([#718](#718)) ([fae3749](fae3749)) * **app:** put the transmit pop under the receptions it sat over ([#738](#738)) ([2bfc5fa](2bfc5fa)) * **server:** keep the node registry in memory on a loop of its own ([#736](#736)) ([4f3e988](4f3e988)) * **web:** keep the map bar on one row between 641 and 767 px ([#732](#732)) ([c44dc1d](c44dc1d)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Closes #723
What
A Discover reply carries a prefix of the responder's key. The node layer paired it with the registry node whose key it alone starts (#661). The reach stars did not:
starKeygave the prefix a star of its own, andpositionOflooked it up as a whole key, which a prefix never is. So a repeater heard by Discover drew its ▲ and, beside it, a star hung from its RSSI estimate with the prefix as its name.The pairing is now one function,
registryMatcher(nodes)innodelayer.js:groupSenderPointsForNodesuses it where it had the same loop inline;coverageStarstakes it asregistryNodeOf: a hearing whose own id names one registry node joins that node's star, keyed by its pubkey, under its ▲;Both copies of
nodelayer.jschange the same way;coverage.jsis copied whole.web/parity.test.jsaddsregistryMatcherto the shared core and checks both surfaces name the same node. Amended indocs/2026-09-08-coverage-overview.md(point 8).Effect on real data
On the
/api/pointsexport of 8 Aug to 7 Sep around Nijmegen: before, 5 of 66 stars hung from their ▲ and 61 from an estimate; after, 63 of 71 hang from their ▲ and 8 from an estimate. The total grows because a prefix with too few hearings for an estimate had no origin at all, and now hangs from its node.Found while building the #666 export, which draws the stars as the map has them.
Verification
coverage.test.js: a prefix's hearings join the node's star with its advert; a prefix that starts two keys keeps its own; a relay hash is left to the attribution. The first is red with the pairing removed.nodelayer.test.js:registryMatchernames the node for a discover prefix and an advert key, none for an ambiguous or unknown prefix, none for a relay hash.npm run buildclean🤖 Generated with Claude Code