feat: redraw the result image in the modern design - #833
Conversation
PR Summary by QodoRedesign shared result PNG to match modern dark UI
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
formatSpeedtestDataForImage() assigned the timestamp to itself, so the value reached the image exactly as the database returned it. Every other field on that list is passed through format(); this one reads like a placeholder that was never filled in, and static analysis flags it as a self-assignment. How much precision the column carries is decided by the backend, and the three schemas shipped here disagree: MySQL's `timestamp` keeps none, PostgreSQL's `timestamp without time zone DEFAULT now()` keeps microseconds and MSSQL's `datetime` keeps milliseconds. A PostgreSQL deployment therefore drew "2026-08-10 02:04:13.957789" where a MySQL one drew "2026-08-10 02:04:13".
46b1144 to
80f50ff
Compare
Code Review by Qodo
1. Polygon call incompatible
|
| imagefilledpolygon($im, [ | ||
| (int) $cx, (int) ($tipY + $dir * $head * 0.5), | ||
| (int) ($cx - $head), (int) ($tipY - $dir * $head * 0.55), | ||
| (int) ($cx + $head), (int) ($tipY - $dir * $head * 0.55), | ||
| ], $color); |
There was a problem hiding this comment.
1. Polygon call incompatible 🐞 Bug ☼ Reliability
directionBadge() calls imagefilledpolygon() with the 3-argument form, which can break deployments on older PHP versions that this repo’s docs still claim to support. On such systems, generating /results/?id=… can error out before producing an image.
Agent Prompt
### Issue description
`directionBadge()` uses `imagefilledpolygon($im, $points, $color)`. Some PHP/GD versions require the legacy signature `imagefilledpolygon($im, $points, $num_points, $color)`. Since project docs still state PHP 5.4+ support, this can make result image generation fail on those deployments.
### Issue Context
- The code path is executed for every rendered image (download/upload badges).
- Docs still describe supporting older PHP versions.
### Fix Focus Areas
- results/index.php[300-321]
### Suggested fix
Use a version-safe call, e.g.:
- Build `$points = [...]` once
- If `PHP_VERSION_ID >= 80000` call the 3-arg variant; else call the 4-arg variant with `$numPoints = (int)(count($points)/2)`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
| while ('' !== $text && textWidth($text.'…', $font, $size) > $maxWidth) { | ||
| $text = substr($text, 0, -1); | ||
| } | ||
|
|
There was a problem hiding this comment.
2. Costly byte truncation 🐞 Bug ➹ Performance
fitText() shrinks strings by repeatedly removing a byte with substr() and re-measuring via imageftbbox(), which is inefficient for long inputs and can split multibyte UTF-8 characters. This matters because the rendered client string comes directly from the stored HTTP_USER_AGENT and can be arbitrarily long.
Agent Prompt
### Issue description
`fitText()` truncates with `substr($text, 0, -1)` in a loop and calls `imageftbbox()` each iteration. This is inefficient for long strings and byte-based truncation can corrupt UTF-8 sequences.
### Issue Context
- `drawImage()` applies `fitText()` to the stored user-agent (`$data['ua']`).
- Telemetry stores `ua` directly from `$_SERVER['HTTP_USER_AGENT']`.
### Fix Focus Areas
- results/index.php[360-378]
- results/index.php[522-533]
- results/telemetry.php[7-12]
### Suggested fix
- Impose a hard maximum length before measuring (e.g., cap UA to N characters).
- Truncate by Unicode characters (e.g., use `preg_split('//u', ...)` and pop chars) rather than `substr` bytes.
- Reduce measurement calls (e.g., binary search for the longest fitting prefix instead of per-character iteration).
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Replace the outdated classic result image with the modern dark styling used by the frontend. Reuse the existing color palette and display the client, address family, and timestamp already available in the result data.
80f50ff to
c9606ae
Compare
|
I like the design. |
The shared result image still draws black on white in the classic style, while
index-modern.htmlandfrontend/styling/describe a dark interface. Sharing aresult posts an image that no longer looks like the site it came from.
Colours are taken from
frontend/styling/colors.cssrather than invented, sothe two stay in step.
Three things the image already carried but never showed are now drawn, all from
columns the query selects today: the client, the address family, and a
timestamp split into a date and a time.
Rendered at 3x and scaled down, because GD antialiases nothing it fills and
circles and rounded corners would otherwise come out stepped. No new
dependency, and the same two fonts already in
results/.Edge cases, rendered: a browser user agent, which is shortened to fit, a long
provider name, and
redact_ip_addresseson, where the family is dropped ratherthan reporting the stored placeholder as IPv4.
The panels carry a gradient rather than a sparkline on purpose: a curve inside a
result reads as measured data, and no per-second samples are kept for a shared
result, so it would be invented.
Stacked on #832. Happy to split the client and address family out onto the
classic card instead if the redesign is not wanted.