Skip to content

Live flight tracker showcase + the wire/republish bugs it surfaced#232

Draft
FarhanAliRaza wants to merge 2 commits into
mainfrom
flights-live-showcase
Draft

Live flight tracker showcase + the wire/republish bugs it surfaced#232
FarhanAliRaza wants to merge 2 commits into
mainfrom
flights-live-showcase

Conversation

@FarhanAliRaza

Copy link
Copy Markdown
Contributor

What

Adds a /flights page to the reflex-xy showcase and fixes three bugs that building it surfaced — one in the adapter's wire layer, two in the render client's in-place republish path.

The showcase (examples/reflex)

  • World mode (default): polls OpenSky /states/all — every aircraft on Earth (~14k) — and republishes the full figure each cycle: altitude-colored/speed-sized scatter with per-aircraft trails over the Natural Earth 50m coastline + borders (~80k points of xy.segments), as one multi-MB binary blob, while pan/zoom stays smooth. Click-to-follow altitude trail, box-select altitude cross-filter.
  • Region mode (XY_FLIGHTS_MODE=region): one adsb.fi 250 nm circle (~600–900 aircraft), pollable down to 1 s.
  • Offline fallback: bundled real captures; world mode ships a single 14k-aircraft frame (272 KB) and dead-reckons every plane along its recorded track at its recorded speed, so the page animates with zero network. data/regenerate.py (stdlib-only) rebuilds all assets.

Adapter fix: socket.io attachment cap

socket.io-parser's JS Decoder rejects binary packets with >10 attachments; the resulting "parse error" closes the app's entire shared websocket, which reconnects, re-subs, re-sends the oversized payload, and loops forever with no console error. The namespace now falls back from the split-buffer layout to the joined single-blob payload when the split would exceed the cap, and bounds msg replies with an err envelope. Spec section added; regression test in tests/reflex_adapter/test_socket_data_plane.py.

Client fixes: in-place republish (ChartView.updatePayload)

  • Stale hover crash → page reload → "zoom reset". The cached hover hit keeps a direct trace-group reference; a republish frees that group's buffers and the next draw threw Cannot read properties of null (reading '_fcId'). Reflex's frontend exception handler pattern-matches exactly that message class and responds with window.location.reload() — so the crash presented as selection resetting the zoom. The destroy path now drops the hover target it invalidates, and the hover draw treats null buffers as nothing-to-highlight.
  • Stale title. The title is mount-time DOM; republishes updated spec.title without touching it ("0 aircraft · idle" over a live map). Both spec-swap paths now re-sync the title element + container aria-label, forcing a re-layout when title presence changes.

Republish survival contract (viewport/selection survive; hover re-resolves; title re-syncs) documented in spec/design/reflex-integration.md.

Testing

  • tests/test_ui_issue_regressions.py::test_republish_clears_stale_hover_and_syncs_title — browser probe, red on the un-fixed bundle, green now
  • tests/reflex_adapter/test_socket_data_plane.py::test_sub_over_attachment_limit_ships_single_blob (11/11 pass)
  • scripts/render_smoke_nonumpy.py full probe list passes
  • Verified live in Chromium against a running app: 14k-aircraft world map, hover → live republish → box-select with zero console errors; title/aria tracking each poll cycle
  • pre-commit run --all-files, ruff check, ruff format --check clean

examples/reflex gains a /flights page as a throughput showcase on real
sensor data: world mode (default) polls OpenSky /states/all (~14k
aircraft globally) and republishes the full figure every cycle over a
Natural Earth 50m basemap (~80k points of segments); region mode polls
an adsb.fi 250nm circle at up-to-1s cadence. Click-to-follow trail
chart, box-select altitude cross-filter, and bundled-capture offline
fallback (world mode dead-reckons one real 14k-aircraft frame along
each track instead of shipping megabytes of frames).

Building it surfaced an adapter bug: socket.io-parser's JS Decoder
rejects binary packets with more than 10 attachments, and the resulting
"parse error" closes the app's entire shared websocket, which then
reconnects and re-subs in a silent infinite loop. The namespace now
falls back from the split-buffer layout to the joined single-blob
payload whenever the split would exceed the cap, and bounds msg replies
with an err envelope instead of emitting an unparseable packet.
A republish through ChartView.updatePayload destroys the outgoing trace
groups, but the cached hover hit kept a direct reference to one — the
next draw dereferenced its freed (nulled) xBuf and threw. Worse than a
broken highlight: Reflex's frontend exception handler pattern-matches
"Cannot read properties of null" as a hydration error and responds with
window.location.reload(), so every crash became a full page reload that
reset the viewport (the "selecting resets zoom" symptom on the flights
page). _destroyTraceResources now clears the hover target it is about
to invalidate, and _drawHoverState treats null buffers as nothing to
highlight.

The title had the mirror-image bug: it is mount-time DOM, so an
in-place swap updated spec.title without ever touching the visible
element ("0 aircraft - idle" over a live map). Both spec-swap paths
(updatePayload and streaming append) now re-sync the title element and
the container's aria-label; a title appearing or disappearing forces a
re-layout since it owns top-margin space.
@FarhanAliRaza
FarhanAliRaza marked this pull request as draft July 23, 2026 20:07
@codspeed-hq

codspeed-hq Bot commented Jul 23, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 102 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing flights-live-showcase (7cb3756) with main (7123396)

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a live flight tracker showcase and fixes republish issues it exposed. The main changes are:

  • New /flights Reflex page with live OpenSky/adsb.fi polling and bundled offline replay data.
  • Asset regeneration script and documentation for flight basemaps and replay captures.
  • Socket data-plane fallback for socket.io packets that would exceed the binary attachment cap.
  • Client republish updates that clear stale hover state and resync chart title/aria chrome.
  • Browser and websocket tests for the republish and attachment-cap fixes.

Confidence Score: 4/5

Safe to merge after addressing the non-blocking UI helper style issue.

Core socket, republish, and showcase changes match the intended contracts and include targeted tests.

examples/reflex/xy_reflex_demo/ui.py

T-Rex T-Rex Logs

What T-Rex did

  • The Flights UI server was started and its runtime logs were captured for review.
  • A Playwright probe script and the browser trace were saved to enable verification of the UI interaction flow.
  • Before- and after-interaction visuals were captured and a video of the interaction was recorded to document the UI flow.
  • The server and browser logs are available via provided URLs for reviewer access.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
examples/reflex/xy_reflex_demo/flights.py Adds the live flight tracker Reflex page with background polling, offline replay, trails, following, and selection filtering.
examples/reflex/xy_reflex_demo/ui.py Extracts shared showcase UI helpers; one non-blocking link component style issue was noted.
js/src/50_chartview.ts Adds title synchronization and stale-hover cleanup/guards during in-place payload republish.
python/reflex-xy/reflex_xy/namespace.py Adds socket.io attachment-cap handling for payloads and bounded error handling for oversized message replies.
tests/reflex_adapter/test_socket_data_plane.py Adds websocket test coverage for over-cap payload fallback while preserving existing data-plane contracts.
tests/test_ui_issue_regressions.py Adds browser test coverage for stale hover cleanup and title/aria synchronization on republish.

Reviews (1): Last reviewed commit: "Drop stale hover and re-sync title on in..." | Re-trigger Greptile

*[
rx.badge(name, variant="solid")
if name == current
else rx.link(rx.badge(name, variant="soft"), href=href)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Avoid pre-styled links
nav() wraps badge navigation in rx.link, which brings Reflex link styling into a badge-only control. The project rule asks for rx.el.a when underline styling should be avoided, and these badges are intended to look like pills rather than text links.

Suggested change
else rx.link(rx.badge(name, variant="soft"), href=href)
else rx.el.a(rx.badge(name, variant="soft"), href=href)

Rule Used: Use rx.el.a (unstyled) instead of rx.link (pre... (source)

Learned From
reflex-dev/reflex-web#1649

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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