diff --git a/ai/tasks/done/TASK-259-analyze-weapon-icon-canvas-and-normalization-strategy.md b/ai/tasks/done/TASK-259-analyze-weapon-icon-canvas-and-normalization-strategy.md new file mode 100644 index 0000000..2a288c2 --- /dev/null +++ b/ai/tasks/done/TASK-259-analyze-weapon-icon-canvas-and-normalization-strategy.md @@ -0,0 +1,279 @@ +--- +id: TASK-259 +title: Analyze weapon icon canvas and normalization strategy +status: review +type: research +team: Frontend Senior +supporting_teams: + - Experto en interfaz +roadmap_item: frontend-current-match +priority: medium +--- + +# TASK-259 - Analyze weapon icon canvas and normalization strategy + +## Goal + +Analyze why weapon icons in the current match combat feed still look visually uneven after TASK-258, without editing weapon assets or changing runtime behavior. This task is kept in review because the analysis should be accepted before TASK-260 implementation starts. + +## Context + +TASK-258 made the combat feed weapon slot more stable in CSS, but several weapons still have different visual weight. The likely cause is not only the CSS frame: the source weapon files use different canvas ratios, viewBox sizes and internal whitespace. + +## Files Read First + +- `ai/architecture-index.md` +- `ai/repo-context.md` +- `ai/task-template.md` +- `ai/orchestrator/frontend-senior.md` +- `frontend/assets/js/current-match-weapon-icons.js` +- `frontend/assets/js/partida-actual.js` +- `frontend/assets/css/historico.css` +- `frontend/assets/img/weapons/` + +## Analysis + +### Resolver And Render Path + +The current match weapon resolver is `frontend/assets/js/current-match-weapon-icons.js`. + +- Runtime base path: `./assets/img/weapons/black/` +- Runtime lookup: `globalThis.HLL_VIETNAM_CURRENT_MATCH_WEAPON_ICONS.lookup` +- Unknown fallback: `precision_strike_black.svg` + +Resolved files for the required sample, using the real `HLL_VIETNAM_CURRENT_MATCH_WEAPON_ICONS.lookup` normalization: + +| Requested weapon | Resolved label | Resolved file | Resolved URL | +| --- | --- | --- | --- | +| M1 GARAND | M1 GARAND | `m1_garand_black.svg` | `./assets/img/weapons/black/m1_garand_black.svg` | +| GEWEHR 43 | GEWEHR 43 | `gewehr_black.svg` | `./assets/img/weapons/black/gewehr_black.svg` | +| KARABINER 98K | KARABINER 98K | `kar98k_black.svg` | `./assets/img/weapons/black/kar98k_black.svg` | +| MG42 | MG42 | `mg42_black.svg` | `./assets/img/weapons/black/mg42_black.svg` | +| BROWNING M1919 | BROWNING M1919 | `browning_m1919_black.svg` | `./assets/img/weapons/black/browning_m1919_black.svg` | +| M2 AP MINE | M2 AP MINE | `m2_ap_mine_black.svg` | `./assets/img/weapons/black/m2_ap_mine_black.svg` | +| MP40 | MP40 | `mp40_black.svg` | `./assets/img/weapons/black/mp40_black.svg` | +| PANZERSCHRECK | PANZERSCHRECK | `panzerschreck_black.svg` | `./assets/img/weapons/black/panzerschreck_black.svg` | +| 75MM CANNON / cannon equivalent | 75MM CANNON [PAK 40] | `pak_40_75mm_black.svg` | `./assets/img/weapons/black/pak_40_75mm_black.svg` | + +The combat feed renderer is `frontend/assets/js/partida-actual.js`. + +- `renderKillFeedRow()` resolves `event.weapon` through `resolveKillFeedWeapon()`. +- `renderKillFeedWeaponIcon()` emits: + - `.current-match-killfeed__weapon-icon-frame` + - `.current-match-killfeed__weapon-icon` + - fallback `?` span on image error +- The generated `img` keeps fixed HTML attributes `width="88"` and `height="32"`, but TASK-258 CSS overrides the rendered sizing with `width: auto`, `height: auto`, `max-width` and `max-height`. + +### Asset Types + +Repository asset inventory under `frontend/assets/img/weapons/`: + +| Area | Type | Count | Notes | +| --- | ---: | ---: | --- | +| `frontend/assets/img/weapons/` | PNG | 38 | Legacy/root weapon assets, not used by current match black resolver. | +| `frontend/assets/img/weapons/black/` | SVG | 123 | Used by current match combat feed resolver. | +| `frontend/assets/img/weapons/red/` | SVG | 38 | Not used by current match black resolver. | +| `frontend/assets/img/weapons/white/` | SVG | 38 | Not used by current match black resolver. | +| `frontend/assets/img/weapons/black - copia/` | SVG | 122 | Untracked copy present in working tree, not used by resolver. | + +For the combat feed path analyzed here, the resolved assets are SVG. No WebP weapon assets were found in the resolver path. + +### CSS Application Check + +TASK-258 CSS applies to the DOM generated by `partida-actual.js`. + +- JS emits `.current-match-killfeed__row`, `.current-match-killfeed__weapon`, `.current-match-killfeed__weapon-icon-frame` and `.current-match-killfeed__weapon-icon`. +- `historico.css` defines those selectors directly. +- A static selector search found no later conflicting definitions outside the expected media queries in `historico.css`. +- Browser computed-style measurement was run against `frontend/partida-actual.html` loaded through `file://`, with test rows injected into the real `#current-match-feed-list`. The backend fetches fail in this diagnostic mode because `file://` cannot call `/api/...`, but the DOM, CSS and local SVG loading path are the same static frontend path under inspection. + +This means the remaining visual mismatch is not explained by a missing selector. CSS can constrain the frame, but it cannot remove transparent/internal whitespace inside each SVG viewBox. + +Computed values observed at a 1366px viewport: + +| DOM element | Selector | Computed values | Overwrite finding | +| --- | --- | --- | --- | +| Weapon wrapper | `.current-match-killfeed__weapon` | `display: grid`, `width: 112px`, `min-height: 48px`, actual row sample height `58.2031px` | Matches TASK-258 CSS. No conflicting computed value observed. | +| Icon frame | `.current-match-killfeed__weapon-icon-frame` | `width: 96px`, `height: 36px`, `padding: 3px 7px`, `overflow: hidden` | Matches TASK-258 CSS. No conflicting computed value observed. | +| Image | `.current-match-killfeed__weapon-icon` | `max-width: 82px`, `max-height: 26px`, `object-fit: contain`, `object-position: 50% 50%` | Matches TASK-258 CSS. No conflicting computed value observed. | + +Computed image render sizes from the same DOM sample: + +| Weapon | Natural image size from browser | Computed image size | Why it still differs visually | +| --- | ---: | ---: | --- | +| M1 GARAND | `1448x1086` | `34.6562x26px` | SVG ratio 1.33 and large vertical whitespace force browser to cap by height; visible rifle becomes very small. | +| GEWEHR 43 | `2172x724` | `78x26px` | SVG ratio 3.00 uses the fixed slot more effectively. | +| KARABINER 98K | `1448x1086` | `34.6562x26px` | Same 1.33 canvas family as M1 Garand; rendered width is less than half of 3.00 assets. | +| MG42 | `1448x1086` | `34.6562x26px` | Same computed box as rifles, but silhouette has more vertical mass inside its canvas. | +| BROWNING M1919 | `1254x1254` | `26x26px` | Square canvas makes the rendered box even narrower. | +| M2 AP MINE | `1254x1254` | `26x26px` | Square canvas and compact object need semantic tuning even after crop. | +| MP40 | `2172x724` | `78x26px` | Wide 3.00 canvas fills much more slot width. | +| PANZERSCHRECK | `2172x724` | `78x26px` | Wide 3.00 canvas fills much more slot width and has heavy silhouette. | +| 75MM CANNON [PAK 40] | `2172x724` | `78x26px` | Wide canvas plus large silhouette dominates compared with rifles. | + +## Asset Measurements + +The sample was measured with a one-off inline Node script. For SVGs, the script parsed `width`, `height`, `viewBox`, aspect ratio and approximate path bounds to estimate occupied canvas area and margins. No script file was created. + +Distribution of `viewBox` aspect ratios in `frontend/assets/img/weapons/black/`: + +| Ratio | Count | +| ---: | ---: | +| 1.00 | 29 | +| 1.33 | 39 | +| 1.50 | 1 | +| 1.78 | 3 | +| 3.00 | 51 | + +Representative asset sample: + +| Weapon | Resolved file | Type | Width / height / viewBox | Ratio | Approx. silhouette occupancy | Canvas / margin observation | Suspicion | +| --- | --- | --- | --- | ---: | --- | --- | +| M1 GARAND | `m1_garand_black.svg` | SVG | `width 1448`, `height 1086`, `viewBox 0 0 1448 1086` | 1.33 | approx. `90.7% x 22.7%` | Long rifle has large top/bottom margins, roughly `417px` top and `423px` bottom. | margen interno excesivo | +| GEWEHR 43 | `gewehr_black.svg` | SVG | `width 2172`, `height 724`, `viewBox 0 0 2172 724` | 3.00 | approx. `88.4% x 41.7%` | Wide canvas fits rifle shape better, with moderate vertical margin. | OK / mild tuning | +| KARABINER 98K | `kar98k_black.svg` | SVG | `width 1448`, `height 1086`, `viewBox 0 0 1448 1086` | 1.33 | approx. `94.5% x 21.9%` | Long rifle has large top/bottom margins, roughly `417px` top and `431px` bottom. | margen interno excesivo | +| MG42 | `mg42_black.svg` | SVG | `width 1448`, `height 1086`, `viewBox 0 0 1448 1086` | 1.33 | approx. `96.0% x 39.4%` | Same canvas family as rifles, but silhouette is almost twice as tall. | necesita normalizacion | +| BROWNING M1919 | `browning_m1919_black.svg` | SVG | `width 1254`, `height 1254`, `viewBox 0 0 1254 1254` | 1.00 | approx. `90.5% x 36.2%` | Square canvas forces the browser to render at `26x26px` under TASK-258 max-height. | necesita normalizacion | +| M2 AP MINE | `m2_ap_mine_black.svg` | SVG | `width 1254`, `height 1254`, `viewBox 0 0 1254 1254` | 1.00 | approx. `92.3% x 45.9%` | Square canvas and compact subject are not comparable with long weapons. | necesita tuning por arma | +| MP40 | `mp40_black.svg` | SVG | `width 2172`, `height 724`, `viewBox 0 0 2172 724` | 3.00 | approx. `85.0% x 76.1%` | Wide canvas fills slot width, but silhouette is much taller than rifles. | necesita normalizacion | +| PANZERSCHRECK | `panzerschreck_black.svg` | SVG | `width 2172`, `height 724`, `viewBox 0 0 2172 724` | 3.00 | approx. `90.9% x 56.2%` | Wide launcher fills slot width and has heavy silhouette. | necesita normalizacion | +| 75MM CANNON [PAK 40] | `pak_40_75mm_black.svg` | SVG | `width 2172`, `height 724`, `viewBox 0 0 2172 724` | 3.00 | approx. `94.7% x 69.2%` | Cannon occupies much of the canvas and dominates the same CSS frame. | necesita tuning por arma | + +## Diagnosis + +The issue is asset canvas/viewBox normalization, not a selector failure. + +- Selector CSS problem: not observed. Rendered classes and CSS selectors match. +- CSS overwritten problem: not observed in static scan. Only expected media query variants were found. +- Asset canvas/viewBox/internal whitespace problem: confirmed. The same CSS frame receives SVGs with ratios from 1.00 to 3.00 and very different occupied-height percentages. + +CSS from TASK-258 is useful and should remain because it stabilizes the combat feed frame, prevents layout jumps and gives a consistent maximum slot. It is not sufficient to equalize perceived visual mass across assets with very different canvas geometry. The browser computed data shows the practical failure mode: a 1.33-ratio SVG renders at about `34.6562x26px`, a 1.00-ratio SVG renders at `26x26px`, and a 3.00-ratio SVG renders at `78x26px` under the same TASK-258 image constraints. + +## Technical Options + +### A. Create normalized derived assets in a new folder + +Create derived files such as `frontend/assets/img/weapons/normalized-black/` from the existing black SVGs, preserving originals. Each derived asset should crop or normalize the viewBox to the visible silhouette plus controlled padding, then be tested in the existing frame. + +Pros: + +- Does not mutate original assets. +- Keeps runtime CSS simple. +- Produces stable visual results across long rifles, mines, launchers and cannons. + +Cons: + +- Requires a generation/QA pass. +- Needs resolver switch in a later task. + +### B. Adjust viewBox of existing SVGs + +Edit current SVG viewBoxes in `frontend/assets/img/weapons/black/`. + +Pros: + +- Directly fixes source geometry. + +Cons: + +- Mutates original assets. +- Higher review risk. +- Harder to revert if originals are needed for other contexts. + +### C. Create a manifest of tuning by weapon + +Add a tuning manifest with per-icon classes or variables, for example scale factors for rifles, mines and heavy weapons. + +Pros: + +- Avoids asset generation. +- Can handle semantic outliers such as mines or vehicle guns. + +Cons: + +- Becomes manual and brittle. +- Still cannot remove transparent canvas; it only counter-scales it. + +### D. Keep CSS and accept differences + +Do nothing beyond TASK-258. + +Pros: + +- No new files or resolver changes. + +Cons: + +- Does not address the reported visual inconsistency. +- Long thin rifles and square/heavy assets will keep reading as uneven. + +## Recommendation + +Preferred path: A. Create normalized derived assets in a new folder, then switch the current match resolver to that folder only after visual QA. + +Do not touch originals in `frontend/assets/img/weapons/black/` during the implementation task. Generate derived normalized black SVGs from the existing resolver set, with a consistent target canvas strategy: + +- Preserve original source files untouched. +- Crop each SVG viewBox to visible path bounds plus controlled padding. +- Keep a consistent output viewport family for horizontal weapons. +- Define explicit exceptions or per-asset scale targets for mines, artillery, vehicles and support actions. +- Add a small manifest only for unavoidable semantic outliers after normalization, not as the primary fix. + +## Proposed TASK-260 + +Create `TASK-260-create-derived-normalized-current-match-weapon-icons.md` with this scope: + +1. Generate derived normalized SVGs in a new folder, for example `frontend/assets/img/weapons/current-match-normalized-black/`. +2. Do not modify files under `frontend/assets/img/weapons/black/`. +3. Start with the nine measured files in this task and normalize them to comparable visible silhouette occupancy before expanding. +4. Crop each SVG viewBox to visible path bounds plus controlled padding, then apply a target output strategy that prevents rifles from shrinking to `34.6562x26px` while wide assets render at `78x26px`. +5. Define explicit handling for semantic outliers: mines, cannons, tanks, support effects and vehicle icons should not be forced to the same perceived mass as long rifles. +6. Include a repeatable local one-off or support script only if it is intended to be committed as platform/tooling; otherwise run it outside commit. +7. Expand to all files referenced by `CURRENT_MATCH_BLACK_WEAPON_ICON_FILES`. +8. Switch `CURRENT_MATCH_BLACK_WEAPON_ICON_PATH` only after derived files exist and spot checks pass. +9. Keep TASK-258 CSS unless browser QA proves a minor frame adjustment is needed. + +Suggested validation for TASK-260: + +- `node --check frontend/assets/js/current-match-weapon-icons.js` +- `node --check frontend/assets/js/partida-actual.js` +- Verify all resolver files exist in the new normalized folder. +- Browser spot check of combat feed icons at desktop and mobile widths. +- `git status --short --untracked-files=all` +- Confirm original `frontend/assets/img/weapons/black/` files are unchanged. + +## Validation + +- Read required repository context and frontend role guidance. +- Inspected the resolver in `frontend/assets/js/current-match-weapon-icons.js`. +- Inspected combat feed rendering in `frontend/assets/js/partida-actual.js`. +- Inspected TASK-258 CSS selectors in `frontend/assets/css/historico.css`. +- Ran a one-off inline Node measurement script for representative SVG dimensions, viewBox ratios and approximate path occupancy. +- Resolved the required sample through the actual runtime lookup in `current-match-weapon-icons.js`. +- Ran browser computed-style diagnostics with a temporary Playwright install outside the repo, loading `frontend/partida-actual.html` through `file://` and injecting combat-feed test rows into the real DOM. +- Ran a static selector search for combat feed weapon classes. +- Ran `git status --short --untracked-files=all`. +- Did not run `node --check frontend/assets/js/partida-actual.js` because no JS file was modified. +- No integration tests were relevant for this documentation-only analysis. + +## Outcome + +TASK-259 is ready for review as an analysis and decision task. It should not be treated as closed until the analysis is accepted. The remaining uneven icon sizing is caused by asset geometry differences: mixed SVG canvas ratios, large internal whitespace and different visible silhouette occupancy. CSS from TASK-258 applies and should remain, but it is not sufficient as the final normalization mechanism. + +Modified file for this task: + +- `ai/tasks/done/TASK-259-analyze-weapon-icon-canvas-and-normalization-strategy.md` + +Explicitly not modified: + +- `frontend/assets/js/partida-actual.js` +- `frontend/assets/js/current-match-weapon-icons.js` +- `frontend/assets/css/historico.css` +- `frontend/assets/img/weapons/` +- backend, scheduler, RCON, server configuration, `ai/system-metrics.md`, `tmp/`, TASK-204 and unrelated prior changes. + +## Change Budget + +- Files modified by this task: 1 +- Product code modified by this task: 0 +- Asset files modified by this task: 0