Skip to content

feat(ui): image map live refresh, status footer, hover thumbnails - #41

Open
lstein wants to merge 1 commit into
feat/image-map-07-marker-zoomfrom
feat/image-map-08-live-theme
Open

feat(ui): image map live refresh, status footer, hover thumbnails#41
lstein wants to merge 1 commit into
feat/image-map-07-marker-zoomfrom
feat/image-map-08-live-theme

Conversation

@lstein

@lstein lstein commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

PR 8 of the Image Map stack:

  • Socket-driven refresh replaces polling: image_map_projection_ready (routed by the backend to the owning user) or a socket reconnect refetches the points — but only once the widget has loaded the map, so users who never open it pay nothing. A refresh requested mid-flight is queued and runs when the current fetch settles (the projection-ready-during-initial-GET window is exactly when the event matters most). Typed contracts for both new events are added to the socket event map.
  • Footer: point count, an "updating…" hint while the served projection is stale, embedding-index progress (admins are the only recipients of those events, and the text hides itself when the backfill drains), and a manual refresh button.
  • Hover previews: dwelling 150ms on a point shows its thumbnail beside the cursor (PhotoMapAI behavior). A monotonic hover session guards against early/stale-coordinate previews from slow fetches; thumbnail URLs are cached per session (they derive from the immutable image name, unlike full DTOs).
  • No theming work needed: the plot is transparent with hidden axes, so it inherits every workbench theme.

An adversarial review covering this diff was run; its findings (swallowed mid-flight refresh, hover race) are fixed above. Accepted trade-offs it confirmed: after first load, projection-ready events refetch for the session even with the widget closed (host runtimes are always mounted; admin scope makes any user's activity relevant); the thumbnail-URL cache grows by name→string entries only.

Testing

Runtime attach/detach with mocked socketHub (gating on load state, reconnect refresh, index-count storage), store chaining semantics. Full suite green at the stack top.

🤖 Generated with Claude Code

@lstein
lstein requested a review from blessedcoolant as a code owner August 2, 2026 21:49
@github-actions github-actions Bot added the python label Aug 2, 2026
@lstein
lstein marked this pull request as draft August 2, 2026 22:24
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from aa21512 to 32045e9 Compare August 2, 2026 22:38
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch 2 times, most recently from 3a5662d to 84be196 Compare August 3, 2026 01:38
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from 32045e9 to bf26409 Compare August 3, 2026 01:38
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from 84be196 to 7960b81 Compare August 3, 2026 02:26
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from bf26409 to 0015963 Compare August 3, 2026 02:26
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from 7960b81 to f15c9c5 Compare August 3, 2026 18:19
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from 0015963 to 32b12f4 Compare August 3, 2026 18:20
@lstein
lstein marked this pull request as ready for review August 3, 2026 18:28
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from f15c9c5 to e55f4db Compare August 4, 2026 01:43
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from 32b12f4 to 2d68eb8 Compare August 4, 2026 01:43
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from e55f4db to 3545b1f Compare August 4, 2026 05:37
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch 2 times, most recently from 312156e to 8a2e9a2 Compare August 4, 2026 14:58
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch 2 times, most recently from 343998e to cc30d46 Compare August 4, 2026 22:32
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch 2 times, most recently from c47f5ae to e239eca Compare August 5, 2026 00:31
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from cc30d46 to 16273bb Compare August 5, 2026 00:31
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from e239eca to 78b62a5 Compare August 5, 2026 00:38
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from 16273bb to aae9670 Compare August 5, 2026 00:38
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from 78b62a5 to a43f464 Compare August 5, 2026 01:18
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from aae9670 to 640ce5a Compare August 5, 2026 01:18
- Socket-driven refresh replaces polling: an `image_map_projection_ready`
  event (routed by the backend to the owning user) or a socket reconnect
  refetches the point set — but only once the widget has loaded the map,
  so users who never open it pay nothing. Contracts for the two new
  backend events are added to the typed socket event map.
- New widget footer: point count, an "updating…" hint while the served
  projection is stale, embedding-index progress (admins are the only
  recipients of those events), and a manual refresh button.
- Hover previews: dwelling on a point for 150ms shows its thumbnail beside
  the cursor (PhotoMapAI behavior). Thumbnail URLs are cached per session —
  unlike full DTOs they derive from the immutable image name.

The plot needs no explicit theming work: its background is transparent and
axes are hidden, so it inherits every workbench theme for free.

Amended during the rebase onto the re-architected webv2:

- The socket event contracts moved out of the deleted shared event map
  (`workbench/backend/events.ts`): the two image-map events are domain
  contracts, so they now live in `workbench/image-map/events.ts` as plain
  interfaces (the queue feature's `data/events.ts` is not a public queue
  module, and `socketHub.on` is stringly-typed, so no shared map is
  needed). The hub itself is imported from `@platform/transport/socketHub`
  (same on/onConnectionChange surface as the old workbench hub).
- The data runtime moved to main's host slot: `hasHost: true` on the
  eager manifest with `host: ImageMapDataRuntime` returned from the
  deferred implementation (queue/workflow pattern); the component wraps
  `attachImageMapDataRuntime` in `useMountEffect`. The footer likewise
  ships through the implementation's `footer` slot instead of the old
  single-manifest module.
- The thumbnail cache resolves names through the gallery feature's public
  interface (`galleryImages.resolveMany`) instead of the deleted
  `workbench/gallery/api`; the footer's IconButton/Tooltip come from
  `@platform/ui`, and `WidgetViewProps` from `@workbench/widgetContracts`.
- The runtime test mocks `@platform/transport/socketHub` at its new path;
  the store and its tests were otherwise untouched by the move.
- Performance baselines recaptured (build + browser, browser verified
  passing twice): mounting the singleton host at editor boot fetches the
  image-map implementation chunk on every editor route (+2 script
  requests, editor owned bytes +34 for the eager `hasHost` flag). Plotly
  itself stays behind the lazy plot import and no map data is fetched at
  boot — the runtime only refreshes a map the user has already opened.

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

Review fix: the thumbnail cache and index-count state are account-owned
resources — cleared on account invalidation, with scope-guarded caching.
@lstein
lstein force-pushed the feat/image-map-07-marker-zoom branch from 640ce5a to d36dbbb Compare August 5, 2026 01:21
@lstein
lstein force-pushed the feat/image-map-08-live-theme branch from a43f464 to 311ecaa Compare August 5, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant