at main
2 folders
31 files
feat(statistics): tabbed sections, honest gaps, smoothing overlay
Rework /statistics from one eight-chart scroll into four tabs behind a
sticky left rail that hangs in the page gutter from 2xl up. Only the
active panel mounts, and each card builds its canvas when it scrolls
into view.
Missing days are no longer drawn as straight lines. Every series is
aligned onto a shared daily grid derived from the days the crawler
actually observed: counts zero-fill (nobody listed a server that day),
prices break (there is no price to plot), and a collection outage breaks
every series at once.
Price charts gain a 7-day centered moving average over a receded raw
line, toggled from the rail. The average is null-preserving, so it never
bridges a gap. Chart.js bezier tension drops to 0 — the moving average
is the honest version of what tension was faking.
Series colours come from a fixed CVD-checked palette indexed by position
instead of Math.random() per render, which was repainting every chart on
each data update.
Fix four defects in the key metrics along the way:
- Per-unit price minima were set by fixed-price listings, whose
nvme_drives holds the configurator's *menu* rather than one machine's
drives (33 TB across eight entries, identical under two CPUs). One
such row decided the whole day. Excluding them moves the NVMe minimum
from €1.14/TB to €6.30/TB and NVMe-vs-HDD from 1.1x to 6.2x.
- The trend arrow never moved: FontAwesomeIcon does not swap its SVG
when the icon prop changes, so the tile kept the down arrow it mounted
with while showing a red "+4.08% rising".
- "Lowest price" was min(cheapest AMD, cheapest Intel), which can never
return a listing whose cpu_vendor is neither ("Intel®", "2x").
- The price index cannot exceed 1.0 by construction, so a subtitle
reading "Values > 1.0 = higher prices" described an unreachable state.
Drop the AMD-vs-Intel tile (it divided two unrelated machines) and
replace "ECC Premium" with an absolute best €/GB, since the old delta
measured RAM capacity, not error correction.
Also collapses the 37 per-datacenter queries into one grouped query per
country, and streams each result into state as it lands rather than in
three blocking batches.
feat(cloud-status): open availability history straight from the grid
The page rendered the same two axes twice: the table showed location x server
type, and a separate section below made you re-pick one of them from a dropdown
to see its history. The table is now the selector -- click a server-type row, a
location column, or a single cell, and the heatmap opens directly beneath it,
scoped accordingly. The dropdowns are gone.
A cell click needs a scope the chart could not express, so CloudAvailability
Chart gains a 'pair' viewMode that narrows on both axes and renders one row.
The chart itself is otherwise untouched: same matrix, shading, and uptime
maths.
Also in this pass:
- prev/next interval buttons, bounded by the ~30 days of retained transitions
and by the live window; the banner drops its "Last" prefix once stepped back
- tooltips state each bucket's full span, not just its start, since buckets are
sub-day at every range
- frameless layout: the map/filters/table lose the card frame and run to the
full width, scrolling horizontally only when columns genuinely do not fit
- filters fit one line at xl and up
- scope is deep-linkable via hist/range/off, clamped to retained history
feat(insights): add computed insight blocks to cloud-status and configurations
Adds a short block of statements derived from each page's own data: which
locations offer ARM at all, the widest and narrowest location by in-stock
share, the scarcest type, anything out of stock everywhere; on configurations,
the entry price, best value per core, the per-TB floor, ECC share and GPU
count.
Every sentence carries a number or name computed at request time, and a
sentence whose precondition does not hold is dropped rather than softened into
something generic -- so a quiet day yields fewer sentences, never filler. The
generators are pure functions colocated with each page and capped at four
statements, which keeps this from drifting into a keyword dump.
Both pages load server-side, so the text is in the initial HTML rather than
appearing only after hydration. /statistics is deliberately left out: it reads
from client-side DuckDB WASM, so text there would be invisible to crawlers
without a new server-side aggregate.
feat(statistics): tabbed sections, honest gaps, smoothing overlay
Rework /statistics from one eight-chart scroll into four tabs behind a
sticky left rail that hangs in the page gutter from 2xl up. Only the
active panel mounts, and each card builds its canvas when it scrolls
into view.
Missing days are no longer drawn as straight lines. Every series is
aligned onto a shared daily grid derived from the days the crawler
actually observed: counts zero-fill (nobody listed a server that day),
prices break (there is no price to plot), and a collection outage breaks
every series at once.
Price charts gain a 7-day centered moving average over a receded raw
line, toggled from the rail. The average is null-preserving, so it never
bridges a gap. Chart.js bezier tension drops to 0 — the moving average
is the honest version of what tension was faking.
Series colours come from a fixed CVD-checked palette indexed by position
instead of Math.random() per render, which was repainting every chart on
each data update.
Fix four defects in the key metrics along the way:
- Per-unit price minima were set by fixed-price listings, whose
nvme_drives holds the configurator's *menu* rather than one machine's
drives (33 TB across eight entries, identical under two CPUs). One
such row decided the whole day. Excluding them moves the NVMe minimum
from €1.14/TB to €6.30/TB and NVMe-vs-HDD from 1.1x to 6.2x.
- The trend arrow never moved: FontAwesomeIcon does not swap its SVG
when the icon prop changes, so the tile kept the down arrow it mounted
with while showing a red "+4.08% rising".
- "Lowest price" was min(cheapest AMD, cheapest Intel), which can never
return a listing whose cpu_vendor is neither ("Intel®", "2x").
- The price index cannot exceed 1.0 by construction, so a subtitle
reading "Values > 1.0 = higher prices" described an unreachable state.
Drop the AMD-vs-Intel tile (it divided two unrelated machines) and
replace "ECC Premium" with an absolute best €/GB, since the old delta
measured RAM capacity, not error correction.
Also collapses the 37 per-datacenter queries into one grouped query per
country, and streams each result into state as it lands rather than in
three blocking batches.
chore: remove lint-silenced dead code and a homoglyph identifier
The underscore prefix is the eslint unused-vars escape hatch, so `_name` marks
code kept alive purely to pass lint. Removed:
- four dead aliased imports (slide as _slide x2, fade as _fade, onMount as
_onMount, enhance as _enhance) — each appeared exactly once, at the import
- statistics/+page.svelte's write-only _loading, assigned twice and never read.
Its removal emptied the finally block, which also exposed write-only queryTime
(computed, never read) and two stale 'no longer needed' comments
- two unused _result bindings in alerts.ts; the awaits are kept, only the
discarded bindings go
routes/+page.server.ts declared `dedupeBy<U+0421>pu` using CYRILLIC CAPITAL
LETTER ES instead of Latin C. Consistent between declaration and call, so it
ran correctly — but it could not be found by grepping for dedupeByCpu, and a
homoglyph in an identifier is the shape of a review-evasion trick. Renamed to
ASCII; the file now has no non-ASCII characters at all.
Swept src/, worker/src/, packages/ and scripts/ for Cyrillic and Greek Latin
lookalikes in identifier positions; this was the only one.
The underscore declarations in analyze/+page.svelte are deliberately left for
the reactivity work, where the effect holding them is being restructured.
feat(statistics): tabbed sections, honest gaps, smoothing overlay
Rework /statistics from one eight-chart scroll into four tabs behind a
sticky left rail that hangs in the page gutter from 2xl up. Only the
active panel mounts, and each card builds its canvas when it scrolls
into view.
Missing days are no longer drawn as straight lines. Every series is
aligned onto a shared daily grid derived from the days the crawler
actually observed: counts zero-fill (nobody listed a server that day),
prices break (there is no price to plot), and a collection outage breaks
every series at once.
Price charts gain a 7-day centered moving average over a receded raw
line, toggled from the rail. The average is null-preserving, so it never
bridges a gap. Chart.js bezier tension drops to 0 — the moving average
is the honest version of what tension was faking.
Series colours come from a fixed CVD-checked palette indexed by position
instead of Math.random() per render, which was repainting every chart on
each data update.
Fix four defects in the key metrics along the way:
- Per-unit price minima were set by fixed-price listings, whose
nvme_drives holds the configurator's *menu* rather than one machine's
drives (33 TB across eight entries, identical under two CPUs). One
such row decided the whole day. Excluding them moves the NVMe minimum
from €1.14/TB to €6.30/TB and NVMe-vs-HDD from 1.1x to 6.2x.
- The trend arrow never moved: FontAwesomeIcon does not swap its SVG
when the icon prop changes, so the tile kept the down arrow it mounted
with while showing a red "+4.08% rising".
- "Lowest price" was min(cheapest AMD, cheapest Intel), which can never
return a listing whose cpu_vendor is neither ("Intel®", "2x").
- The price index cannot exceed 1.0 by construction, so a subtitle
reading "Values > 1.0 = higher prices" described an unreachable state.
Drop the AMD-vs-Intel tile (it divided two unrelated machines) and
replace "ECC Premium" with an absolute best €/GB, since the old delta
measured RAM capacity, not error correction.
Also collapses the 37 per-datacenter queries into one grouped query per
country, and streams each result into state as it lands rather than in
three blocking batches.