Skip to content

Fix recording playback UI bugs#210

Merged
fank merged 14 commits intomainfrom
fix/bugfixes
Feb 14, 2026
Merged

Fix recording playback UI bugs#210
fank merged 14 commits intomainfrom
fix/bugfixes

Conversation

@fank
Copy link
Member

@fank fank commented Feb 14, 2026

Summary

  • Fix clockwise rotation for ELLIPSE and RECTANGLE briefing markers (was counter-clockwise)
  • Filter briefing markers by the active side selected in the units panel
  • Show vehicle crew count and player member names in vehicle popups
  • Hide entity popups when zoomed out below threshold (zoom <=4 legacy, <=14 MapLibre)
  • Add popup text to ICON briefing markers (e.g. "WEST Kevin 80,4")
  • Assign briefing markers to correct layer groups (systemMarkers, projectileMarkers, briefingMarkers)
  • Use leaflet-popup-vehicle CSS class for vehicle popups (left-aligned text, min-width 200px)
  • Only list player crew members in vehicle popup (AI crew excluded from name list)
  • Vehicle isPlayer derives from crew composition for "players" display mode visibility
  • Apply per-entity state (isPlayer, isInVehicle) in refreshPopupVisibility
  • Open ICON briefing marker popup after adding to map (was no-op before DOM existed)
  • Expand RLE vehicle positions in JSON decoder (vehicles appeared at wrong positions during playback)
  • Remove briefing markers from the correct layer group (smoke grenades and projectile markers never disappeared)

Test plan

  • All 14 fixes have dedicated test coverage (713 tests total, all passing)
  • Manual testing: load a mission with briefing markers and verify rotation, side filtering, and popup text
  • Manual testing: verify vehicle popups show crew count with player names only
  • Manual testing: zoom in/out to verify popup hide threshold works
  • Manual testing: verify smoke grenade markers appear and disappear at correct frames
  • Manual testing: verify vehicle entities move at correct times (not prematurely)
  • Manual testing: toggle nameDisplayMode between "all", "players", "none"

fank added 9 commits February 13, 2026 22:26
…rkers

Arma directions are clockwise from north, but the rotation matrix used
counter-clockwise convention. Negate the angle to match Arma's convention.
Added test coverage for rotation direction.
Only show markers belonging to the currently selected side (+ GLOBAL).
Lifted activeSide signal to shared shortcuts module so MarkerManager
can react to side changes via the render bridge.
Vehicles now display crew info matching the old frontend format:
"VehicleName (N)\nCrew1\nCrew2...". Added HTML escaping and tests.
Matches old frontend behavior: popups are hidden at zoom <=4 (legacy)
or <=14 (MapLibre). Also refreshes popup visibility on nameDisplayMode
change.
Builds popup labels for ICON markers matching old frontend logic:
player-owned markers show "SIDE PlayerName Text", system markers
show just the text, projectiles show "PlayerName Text".
…le/briefing)

ICON markers are now classified into systemMarkers (player=-1),
projectileMarkers (magIcons/Minefield/mil_triangle on GLOBAL),
or briefingMarkers (player-owned) matching the old frontend logic.
Vehicle popups now use text-align: left (leaflet-popup-vehicle) matching
the old frontend, while infantry popups keep text-align: center
(leaflet-popup-unit).
Matches the old frontend's getCrewString() which filtered crew names to
isPlayer=true units only. AI crew members are counted in the total but
not listed by name.
Vehicles now derive isPlayer from crew composition: true if any crew
member is a player, false otherwise. This matches the old frontend's
hideMarkerPopup logic which checked vehicle crew for player presence.
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @fank, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refines the user interface and display logic within the recording playback feature. It addresses several visual inconsistencies and enhances the clarity of map elements. Key improvements include accurate rotation for briefing markers, dynamic filtering of markers based on selected sides, and more informative vehicle popups that detail player crew. Additionally, it optimizes map performance and readability by managing popup visibility according to zoom levels and correctly categorizing markers into distinct rendering layers.

Highlights

  • Briefing Marker Rotation Fix: Corrected the rotation logic for ELLIPSE and RECTANGLE briefing markers to ensure they rotate clockwise, matching Arma's convention.
  • Side-based Briefing Marker Filtering: Implemented filtering for briefing markers based on the active side selected in the units panel, allowing users to view markers relevant to a specific faction.
  • Enhanced Vehicle Popups: Updated vehicle popups to display crew count and list player member names, excluding AI crew, and assigned a specific CSS class for styling.
  • Zoom-based Popup Visibility: Introduced a mechanism to hide entity popups automatically when the map is zoomed out below a predefined threshold, improving map readability at higher zoom levels.
  • ICON Briefing Marker Popup Text: Added functionality to display descriptive popup text for ICON briefing markers, including side, player name, and custom text.
  • Briefing Marker Layer Assignment: Assigned briefing markers to appropriate layer groups (systemMarkers, projectileMarkers, briefingMarkers) for better organization and rendering control.
  • Vehicle Player Status Derivation: Modified the isPlayer property for vehicles to reflect whether any of their crew members are players, influencing visibility in 'players' display mode.
Changelog
  • ui/src/pages/recording-playback/tests/useRenderBridge.test.tsx
    • Added new tests to verify vehicle marker display, including crew count, player names, AI exclusion, and HTML escaping for names.
    • Included tests for dynamic updates of vehicle crew information when crew composition changes over time.
    • Added tests to confirm that a vehicle's 'isPlayer' status correctly reflects the presence of any player crew members.
  • ui/src/pages/recording-playback/components/UnitsTab.tsx
    • Removed local state for activeSide to utilize a global signal for consistent side filtering across the application.
  • ui/src/pages/recording-playback/load-operation.ts
    • Modified loadMarkers to accept an optional getEntityName callback, enabling dynamic lookup of entity names for marker popup text.
  • ui/src/pages/recording-playback/shortcuts.ts
    • Introduced activeSide as a new signal to manage the currently selected side, facilitating briefing marker filtering.
  • ui/src/pages/recording-playback/useRenderBridge.ts
    • Implemented escapeHtml utility function to prevent XSS in displayed names.
    • Added vehicleDisplayName function to construct detailed vehicle names with crew counts and player names.
    • Introduced vehicleHasPlayerCrew function to determine if a vehicle has any player crew members for visibility logic.
    • Updated entity marker creation to use the new vehicleDisplayName and vehicleHasPlayerCrew functions.
    • Added a createEffect to update briefing markers based on changes to the activeSide filter.
  • ui/src/playback/tests/marker-manager.test.ts
    • Added comprehensive tests for setSideFilter to ensure correct filtering, removal, and retention of markers based on side selection.
    • Included tests for marker layer classification, verifying that system, projectile, and briefing markers are assigned to their correct layers.
    • Added tests for ICON marker popup text generation, covering various scenarios like system markers, player-owned markers, global markers, and projectile markers.
  • ui/src/playback/marker-manager.ts
    • Introduced setSideFilter method to control the visibility of briefing markers based on their side.
    • Added classifyMarkerLayer function to determine the appropriate rendering layer for each briefing marker.
    • Implemented buildMarkerPopupText function to generate dynamic popup text for ICON markers based on their properties and associated entities.
    • Updated loadMarkers to use buildMarkerPopupText and classifyMarkerLayer when processing marker definitions.
    • Modified updateFrame to apply side filtering and assign markers to their designated layers.
  • ui/src/renderers/leaflet/tests/zoom-suppression.test.ts
    • Added tests to confirm that RECTANGLE and ELLIPSE briefing markers rotate clockwise as expected.
    • Included tests for entity popup visibility, ensuring they hide below a certain zoom threshold and reappear above it.
    • Added tests to verify that leaflet-popup-unit and leaflet-popup-vehicle CSS classes are correctly applied to entity popups.
  • ui/src/renderers/leaflet/leaflet-renderer.ts
    • Introduced hideMarkerPopups state to control global popup visibility based on zoom level.
    • Updated zoomend event listener to re-evaluate hideMarkerPopups and refresh popup visibility.
    • Modified createEntityMarker to dynamically assign leaflet-popup-unit or leaflet-popup-vehicle CSS classes based on icon type.
    • Adjusted updateEntityMarker to incorporate hideMarkerPopups into the popup display logic.
    • Implemented refreshPopupVisibility method to re-evaluate and update the display style of all entity popups.
    • Corrected the rotation calculation for ELLIPSE and RECTANGLE shapes by negating the angle to achieve clockwise rotation.
    • Updated createBriefingMarker to assign markers to specific layers based on the layer property in BriefingMarkerDef and added popup support for ICON markers.
  • ui/src/renderers/renderer.types.ts
    • Added an optional layer property to the BriefingMarkerDef interface to specify the rendering layer for briefing markers.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Feb 14, 2026

Coverage Report for ui

Status Category Percentage Covered / Total
🔵 Lines 76.47%
⬆️ +1.71%
3053 / 3992
🔵 Statements 73.17%
⬆️ +1.42%
3765 / 5145
🔵 Functions 72.69%
⬆️ +0.86%
905 / 1245
🔵 Branches 62.07%
⬆️ +1.84%
1517 / 2444
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
ui/src/data/decoders/json-decoder.ts 89.25%
⬆️ +1.40%
67.78%
⬆️ +0.89%
100%
🟰 ±0%
90.26%
⬆️ +1.16%
121, 216, 252-298, 303
ui/src/pages/recording-playback/load-operation.ts 0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
0%
🟰 ±0%
26-61
ui/src/pages/recording-playback/shortcuts.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
ui/src/pages/recording-playback/useRenderBridge.ts 100%
🟰 ±0%
91.66%
⬆️ +1.66%
100%
🟰 ±0%
100%
🟰 ±0%
ui/src/pages/recording-playback/components/UnitsTab.tsx 87.09%
⬇️ -0.17%
75%
🟰 ±0%
85.71%
🟰 ±0%
91.89%
⬇️ -0.07%
47, 89-96, 125, 151, 201-202, 209-210
ui/src/playback/marker-manager.ts 95.83%
⬇️ -1.46%
94.39%
⬆️ +2.73%
100%
🟰 ±0%
95.37%
⬇️ -1.81%
304-306, 358-359
ui/src/renderers/renderer.types.ts 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
ui/src/renderers/leaflet/leaflet-renderer.ts 49.54%
⬆️ +12.46%
37.61%
⬆️ +10.34%
35.36%
⬆️ +11.61%
51.65%
⬆️ +13.34%
102-114, 198-199, 203, 239, 242-431, 512-518, 521-527, 530-536, 541-546, 573, 599, 606-612, 620, 629, 634, 639, 644-645, 660, 671-703, 758-759, 767-768, 775, 779, 781, 789-795, 866-873, 882, 895-898, 949-953, 974, 1052-1168, 1179, 1181, 1186, 1190, 1192, 1201-1266, 1277-1347, 1353-1364
Generated in workflow #64 for commit 30a43f0 by the Vitest Coverage Report Action

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive set of fixes and enhancements to the recording playback UI. The changes, including corrected marker rotations, improved vehicle popups with crew details, and refined marker filtering, significantly enhance the user experience. The code is well-structured and accompanied by thorough tests for each new feature and fix. I've identified a potential bug related to popup visibility logic and have also suggested a few refactorings to improve code clarity and maintainability. Overall, this is a high-quality contribution.

fank added 5 commits February 14, 2026 01:43
- Store isPlayer/isInVehicle on InternalMarkerHandle so
  refreshPopupVisibility applies the full visibility logic on zoom and
  nameDisplayMode changes, preventing brief popup flicker.
- Use Array.some() for vehicleHasPlayerCrew (more idiomatic).
- Group GLOBAL conditions in buildMarkerPopupText and use
  PROJECTILE_TYPES constant consistently.
openPopup() on a marker not yet added to the map has no effect because
the popup DOM element doesn't exist yet. Move openPopup() after addTo().
Vehicle positions in legacy JSON use run-length encoding with
[startFrame, endFrame] ranges at index 4. The decoder was treating
each RLE entry as a single frame, causing vehicles to appear at
wrong positions (e.g. a vehicle stationary for 13 minutes would
jump to its 13-minute position at ~44 seconds of playback).

Now correctly expands RLE entries into one EntityState per frame,
matching the Go backend's collectEntityPositions behavior.
removeBriefingMarker() was hardcoded to remove from briefingMarkers
layer group, but markers classified as projectileMarkers (smoke
grenades, ammo icons) or systemMarkers were added to different groups.
Leaflet's removeLayer() silently no-ops when the layer isn't in the
group, so these markers never disappeared.

Use L.Layer.remove() instead, which removes from whatever parent the
layer is currently in — matching the old frontend's removeMarker()
behavior.
@fank fank merged commit 60bb432 into main Feb 14, 2026
3 checks passed
@fank fank deleted the fix/bugfixes branch February 14, 2026 11:40
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