Skip to content

[Backport release/0.0.14] feat(cockpit): map click-to-goal, path overlay and cancel button - #4189

Open
github-actions[bot] wants to merge 1 commit into
release/0.0.14from
backport/4183-to-release/0.0.14
Open

github-actions[bot] wants to merge 1 commit into
release/0.0.14from
backport/4183-to-release/0.0.14

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Description

Backport of #4183 to release/0.0.14.

@greptile-apps

greptile-apps Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 3/5

Not safe to merge until map-margin clicks are rejected and click goals retain the displayed costmap coordinate frame; the publish-error ordering issue is non-blocking but should be corrected for reliable operator feedback.

Findings

  1. P1 Reject margin clicks
  2. P1 Keep costmap frame
  3. P2 Keep latest publish error

Summary

This PR adds interactive cockpit map navigation controls, path rendering, goal publication, cancellation, replay/pacing behavior, and typed map codecs. Two reproduced navigation-goal correctness problems must be fixed before merging: clicks in letterboxed map margins publish out-of-bounds goals, and click goals discard the displayed costmap frame. A separate non-blocking UI issue can hide the latest publish failure after overlapping actions.

Reviews (1) · Last reviewed commit: "feat(cockpit): map click-to-goal, path o..."

Comment on lines +160 to +166
const onCanvasClick = (e: MouseEvent): void => {
const rect = canvas.getBoundingClientRect();
if (place === null || rect.width === 0 || rect.height === 0) return;
const t = fitTransform(place, canvas.width, canvas.height);
const px = (e.clientX - rect.left) * canvas.width / rect.width;
const py = (e.clientY - rect.top) * canvas.height / rect.height;
onClick?.(...canvasToWorld(t, px, py));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Reject margin clicks

When a fitted map is letterboxed, this handler accepts clicks in the blank canvas margins and publishes coordinates outside the displayed costmap. With a 2m×2m map centered in a 400×200 canvas, clicking (20,100) in the left margin published {x:-0.8,y:1} although the map bounds are x=[0,2]. A normal margin click can therefore replace the active goal with an unplannable out-of-bounds target. Reject clicks outside the fitted grid before converting and publishing them.

Knowledge Base Used:

Artifacts

Evidence from the check

  • The executable browser script imports the candidate MapPanel source through Vite, renders the letterboxed map, and records both click conditions; it directly exercises the candidate handler.

Command output from the check

  • The executed command log records exit code 0, the valid control goal `{x:1,y:1}`, and the margin-click goal `{x:-0.8,y:1}`; the margin click publishes outside the costmap.

▶ Recording of the check

  • Chromium recording of a click at the center of the visible map that publishes the valid in-bounds goal `{x:1,y:1}`; it establishes the same rendered canvas and normal click behavior.

Control click result inside the centered costmap

  • Poster frame from the control recording showing the rendered letterboxed map and its in-bounds click result; the map-area control produces a valid goal.

Evidence from the check

  • Captured data from the executed in-map control click records canvas `(200,100)` and emitted `{x:1,y:1}`; the control goal is inside bounds.

▶ Recording of the check

  • Chromium recording of a click in the left black letterbox margin rather than the map that publishes `{x:-0.8,y:1}`; the candidate accepts and publishes an out-of-bounds goal.

Margin click result showing an out-of-bounds x coordinate

  • Poster frame from the margin-click recording showing the letterboxed map and published negative x goal; the left margin produces an invalid costmap coordinate.

Evidence from the check

  • Captured data from the executed left-margin click records canvas `(20,100)` and emitted `{x:-0.8,y:1}`; the emitted x coordinate is outside bounds.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +91 to +97
@web_decoder("point.json.v1")
def decode_point(msg: dict[str, Any]) -> PointStamped:
if not isinstance(msg, dict):
raise ValueError(f"point.json.v1 wants an object, got {type(msg).__name__}")
return PointStamped(
finite_number(msg.get("x"), "x"), finite_number(msg.get("y"), "y"), frame_id="world"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Keep costmap frame

A click on a costmap whose frame is not world is decoded as PointStamped(frame_id="world"). The map wire data drops the original OccupancyGrid.frame_id, so coordinates calculated in an odom, map, or other costmap frame are silently interpreted as world coordinates by frame-aware navigation consumers. Propagate the displayed costmap frame through the Map2D click contract, or reject non-world maps rather than publishing mislabeled goals.

Knowledge Base Used:

Artifacts

Evidence from the check

  • Executable Python contract creates a costmap in an arbitrary frame and exercises the registered click decoder, showing whether the frame survives.

Evidence from the check

  • Executable shell command checks out the parent revision temporarily and saves side-by-side runtime captures for the baseline and candidate.

Command output from the check

  • Captured command output from the parent revision shows `point.json.v1` was unregistered before the change, establishing the comparable baseline.

Command output from the check

  • Captured command output from the candidate encodes an `arbitrary_costmap_frame` map and decodes a click as a `world` point, confirming frame loss.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines +196 to +205
function send(
session: Session,
ch: string,
value: JsonValue,
onError: (message: string | null) => void,
): void {
session.publish(ch, value).then(
() => onError(null),
(err: unknown) => onError(`send failed: ${err instanceof Error ? err.message : String(err)}`),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Keep latest publish error

An older map-click success can clear the error from a later cancel request because both operations update the same error state without request correlation. The rendered cancel failure disappeared after the earlier click completed successfully. This is non-blocking, but it hides the latest failed action and makes operators believe the cancel was sent. Track the newest request or keep click and cancel error state separate.

Knowledge Base Used:

Artifacts

▶ Recording of the check

  • Chromium executes the overlap harness, rejects the second cancel publish while the first click remains pending, and shows the cancel error alert; this establishes the state before the stale completion.

Poster frame showing send failed newer cancel failed on the map panel

  • Poster frame from the before recording shows the rendered newer cancel failure alert; the newer failure is correctly visible before the stale click completion.

▶ Recording of the check

  • Chromium first renders the newer cancel failure and then resolves the older click publish; the alert disappears, proving the stale completion overwrites newer error state.

Poster frame showing the map panel with no alert after the stale success

  • Poster frame from the after recording shows no error alert after the earlier click succeeds; the newer cancel failure has been incorrectly cleared.

Command output from the check

  • The recorded command, working directory, Playwright output, and exit code show the targeted Chromium test ran successfully with both assertions passing; the behavior is confirmed.

Evidence from the check

  • Playwright's machine-readable observed output lists both browser test cases, their video attachments, and passed status; the forced settle ordering was exercised.

Evidence from the check

  • The executed Playwright source mounts the actual MapPanel, leaves click and cancel publishes pending, rejects cancel first, then resolves click; it directly tests the reported race.

Evidence from the check

  • The executed harness renders the actual MapPanel with an active path and deferred Session.publish promises; it provides deterministic overlapping click and cancel operations.

Evidence from the check

  • The executed configuration starts Vite and runs the targeted test in Chromium with video capture; it documents the browser test command environment.

Evidence from the check

  • The executed Vite configuration resolves the cockpit workspace aliases while serving the test harness; it enables the actual component to run in Chromium.

View artifacts

T-Rex Ran code and verified through T-Rex

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