Skip to content

fix(core): replace numbered point callout with badge-neutral centered marker - #3096

Open
NianZheChao wants to merge 1 commit into
web-infra-dev:mainfrom
NianZheChao:fix/point-marker-reticle
Open

fix(core): replace numbered point callout with badge-neutral centered marker#3096
NianZheChao wants to merge 1 commit into
web-infra-dev:mainfrom
NianZheChao:fix/point-marker-reticle

Conversation

@NianZheChao

Copy link
Copy Markdown

Summary


In the recorder's tap-to-describe flow, the model receives a screenshot with a temporary annotation marking the tapped point and is asked to describe the real UI element at that point. The old annotation was a red filled bubble with a white "1" digit connected by a leader line — visually near-identical to an Android notification badge. Vision-language models frequently described the annotation itself as if it were part of the page (e.g. "a back arrow with a red badge showing 1"), polluting the recorded element descriptions.

This PR replaces the numbered callout with a badge-neutral centered marker: a hollow ring with four crosshair ticks, no digit, no leader line, no offset. The prompt wording is aligned with the new marker.


How I found it



  1. I was using Midscene Studio (Xiaomi device, adb + scrcpy, 1080×2400 screenshots) to record taps in a finance app.
  2. After every tap, the recorder auto-generates an element description via the point-description path (describeElementAtPointservice.describecompositePointMarkerImg). The generated descriptions repeatedly mentioned a red badge that did not exist in the app. Examples from ai-call.log (back-translated):
    • "the back arrow icon in the top-left corner with a red badge showing the number 1"
    • "the fund group item … with a red unread-count badge (1) in the top-right corner"
  3. This reproduced consistently with both models I tested — qwen3-vl-plus and qwen3.7-plus — so it did not look like a model-specific quirk.
  4. I inspected the request payloads in the logs, then regenerated the exact image sent to the model by running the production compositePointMarkerImg code on a real 1080×2400 recording screenshot. The marker turned out to be a red filled circle with a bold white "1", drawn offset from the tap point with a leader line — structurally the same shape as an Android notification badge.
    

Root cause


drawPointMarker in packages/shared rendered the point marker as a numbered callout bubble (filled red circle + white digit + leader line, offset from the point). Even though the system prompt states the annotation is temporary, the badge-like appearance was strong enough that both models interpreted it as a genuine notification indicator and included it in the element description.


Before / after


Same real recording screenshot, same tap point — generated with the production code path (marker code from main on the left, this PR on the right):

midscene-marker-before-after

The old marker is a red filled bubble with a white "1" connected by a leader line; the new marker is a hollow centered ring with crosshair ticks. The white halo ring and white ticks stay visible on dark/red backgrounds, while the red ring stays visible on light backgrounds, and the center is left untouched so the tapped element remains readable.


Changes


packages/shared/src/img/box-select.ts

  • Rewrote drawPointMarker to draw a hollow marker centered exactly on the tap point: white halo ring + colored ring + four white crosshair ticks — no digit, no leader line, no callout offset. The center pixels are never painted. Red/blue marker variants (indexId) are preserved.
  • Removed the now-unused drawCallout / drawEllipse helpers (dead code, no remaining callers).
    
    packages/core/src/ai-model/prompt/describe.ts
  • System prompt now refers to "the temporary target marker (a ring with a crosshair)" and "marker center" instead of "callout" / "callout endpoint/center".
  • The overlay ignore list explicitly covers ring, crosshair, line, color, marker, border, dot, or selection box.
    
    packages/core/src/service/index.ts
  • User-facing image messages updated from "temporary callout" to "temporary target marker" wording (both the single-image path and the deep-describe multi-image path).
    
    Tests
  • packages/shared/tests/unit-test/image/index.test.ts: pixel assertions updated for the centered ring (ring pixels at the expected offsets are colored; point-center pixels stay untouched; pixels outside the marker are untouched).
  • packages/core/tests/unit-test/prompt/describe.test.ts (+ snapshot) and service-describe.test.ts: assertions updated to the new marker wording.
    

Validation



  • npx rstest run tests/unit-test/image in packages/shared76/76 passed
  • npx rstest run tests/unit-test/prompt/describe.test.ts tests/unit-test/service-describe.test.ts in packages/core23/23 passed
  • npx nx build shared and npx nx build core — success
  • Biome lint on all changed files — no findings
  • Regenerated the exact image payload the model receives from a real Android recording screenshot via production code, confirming the new marker contains no badge-like or digit glyph (see before/after image above)
    
    No public API changes. The point marker is only used by the describe-for-point path; report rendering, planning, and element-index boxed annotations are untouched.

The point-description marker was a red filled bubble with a white "1"
digit and a leader line, visually similar to an Android notification
badge, so vision-language models often described the annotation itself
(e.g. "a red badge with number 1") instead of the real target element.

- draw a hollow centered reticle (white halo, red ring, four crosshair
  ticks) with no digit and no callout offset in drawPointMarker
- update describer prompts from "callout" to "target marker (ring with
  crosshair)" wording so the model ignores the overlay
- update image marker unit tests and the describer prompt snapshot
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