Skip to content

Fix log streaming, drawing double-clicks, selection races, and 3D model fallback - #185

Merged
amorfinv merged 6 commits into
mainfrom
fix/log-stream-drawing-and-selection-fixes
Aug 23, 2026
Merged

Fix log streaming, drawing double-clicks, selection races, and 3D model fallback#185
amorfinv merged 6 commits into
mainfrom
fix/log-stream-drawing-and-selection-fixes

Conversation

@amorfinv

Copy link
Copy Markdown
Collaborator

A batch of bug fixes across the log stream backend and the map frontend, one commit per fix:

Backend

  • Log-stream byte offsets (WebATM/server/routes.py): output-file content is now read in binary so offsets are real byte positions. Fixes a CRLF log polled mid-line delivering its line ending twice (spurious blank lines in the stream viewer), holds back a UTF-8 character split across polls instead of rendering replacement characters, and makes lines=0 mean "no history" instead of returning the whole file. Also simplifies the health endpoint.

Frontend

  • Drawing double-clicks: all drawing modes ignore the second click of a double-click (duplicate vertices, degenerate two-click shapes, aircraft created with heading 0) and suspend MapLibre's double-click zoom while drawing. Aircraft creation also rejects a heading click on the exact spawn position.
  • Selection races: the empty-map-click unselect hit-tests synchronously instead of after a 50 ms delay, which let the select-triggered flyTo misread an aircraft click as an empty-map click and instantly unselect. Switching selection between aircraft now toggles the old aircraft's route broadcast off, and unsolicited ROUTEDATA only becomes an implicit selection while nothing is selected.
  • Speed-only route constraints: ADDWPT now emits an empty altitude slot (lat,lon,,spd) instead of dropping the speed with a warning.
  • Missing 3D models: a saved model no longer in the catalog is reset to Auto in storage/state (not just displayed as Auto), and the model loader falls back to the default model when a globally forced model fails to load.
  • 3D route layer lifecycle: a stale style-load wait can no longer re-add the route layer to a map the renderer no longer manages (zombie layer + continuous repaint loop).

🤖 Generated with Claude Code

amorfinv and others added 6 commits August 23, 2026 22:08
…hars, lines=0

Read output-file content in binary so offsets are real byte positions:
text-mode tell() cookies plus newline translation delivered a trailing \r
as \n twice when a CRLF log was polled mid-line, injecting spurious blank
lines into the stream viewer. Hold back an incomplete trailing UTF-8
sequence so a character split across polls isn't rendered as replacement
characters, and make lines=0 mean "no history, stream from the end"
instead of returning the whole file. Also simplifies the health endpoint
and hoists the re/sqlite3 imports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The second click of a double-click (originalEvent.detail > 1) is a repeat
of the first, not a deliberate placement: it dropped duplicate vertices,
instantly finished two-click shapes with degenerate geometry, and created
aircraft with a meaningless heading. All drawing modes now skip it and
disable MapLibre's double-click zoom while drawing (restored on stop).
Aircraft creation also rejects a heading click on the exact spawn position
(e.g. both clicks snapped to the same navaid), mirroring the circle tool's
zero-radius guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t handoff

The empty-map-click unselect deferred its hit test by 50 ms, letting the
select-triggered flyTo (or a data update) move the aircraft off the
clicked point and misread an aircraft click as an empty-map click,
instantly unselecting the aircraft that was just clicked. The hit test is
now synchronous with the click dispatch.

When selection switches directly between aircraft, the old aircraft's
route broadcast is now toggled off; before, it kept streaming ROUTEDATA
that got re-interpreted as an implicit selection stealing the new
selection back. Unsolicited ROUTEDATA only becomes an implicit selection
while nothing is selected. Drops the now-unused sendExplicitPos wrapper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BlueSky resolves an empty positional arg to "no constraint", so a
speed-only waypoint constraint now emits an empty altitude slot
(<lat>,<lon>,,<spd>) instead of discarding the speed with a warning.
Command building is collapsed into one arg-list path, and the private
unit-label helpers move to DataProcessor.altitudeUnitLabel alongside the
existing speedUnitLabel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A saved model no longer in the catalog is now reset to Auto in storage
and state — not just displayed as Auto — so the renderer stops forcing
the missing file (re-picking Auto in an already-Auto select fires no
change event to recover). usableModelPath gains a default-model fallback
tier for when a globally forced model fails to load: the configured
fallback path IS the forced path in that case and couldn't serve as the
fallback. Extracts isKnownModelSelection for the shared catalog check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A renderer destroyed (or re-initialized on another map) while polling for
the map style to load could still add its layer afterwards, leaving a
zombie route-3d-layer that kept rendering the last-seeded route with the
3D overlay off and forced a continuous repaint loop. The style wait now
aborts when the renderer no longer manages the map it was started on,
with a lifecycle test suite mirroring the Aircraft3DRenderer one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@amorfinv
amorfinv merged commit 92895ba into main Aug 23, 2026
2 checks passed
@amorfinv
amorfinv deleted the fix/log-stream-drawing-and-selection-fixes branch August 23, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant