* feat(viz): switch a chart between views
Adds `views` to a chart spec: a segmented control above the chart that
switches which measure it draws. Modelled on the charts in the author's own
blog, which use the same control for the same job.
Every view is rendered into the same committed SVG, one <g> each, and all but
the first are inline-hidden. Switching shows one and hides the rest, so nothing
re-renders and nothing is fetched on click. With JavaScript off a reader still
gets a complete chart rather than dead buttons.
A view is a patch over the base spec, so it can change the column plotted, the
axis, the title and the y-max.
Applies it to oversampling/recall, whose CSV already carried ndcg_at_10 while
the chart only drew recall_at_10 -- the second measure was in the table but
invisible in the picture. The default view is byte-identical to the chart
published today, so the page looks the same until a reader picks the other tab.
The Markdown output is unchanged and carries every view's columns, since it
prints the whole CSV.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(viz): the caption has to follow the view
Caught this in a screenshot: the oversampling caption cites recall going 0.605
to 0.988, and it stayed put when the reader switched to the nDCG view, where
every bar sits between 0.2786 and 0.3238. A caption is a claim about the
numbers on screen, so leaving it fixed makes the figure lie.
Each view after the first takes caption2=, caption3= and so on, alongside the
existing caption= at the call site, where the rest of the prose lives. The
build fails if a view has no caption rather than letting it inherit one.
The figcaption is also the figure's accessible name, so it changes for screen
readers too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* style(viz): white label on the active toggle, and fewer comments
The active segment took its colour from --qi-surface, which flips to near-black
in dark mode, so the label sat dark-on-red. Neither surface clears AA on the
accent anyway (4.30 light, 3.73 dark); white gives 4.77. Adds --qi-accent-ink
for text sitting on the accent.
Also cuts the comments this branch added from 21 lines to 9.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(viz): Markdown output carries every view's claim
The table is the whole CSV, so for a chart with views it holds every view's
columns at once. The title and caption were still view 0's, so the .md read
"Recall@10 by quantization" over a table that also carried nDCG.
Now emits one caption per view, labelled, and the oversampling title no longer
names a single measure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* Trigger CI
* refactor(viz): call the view picker a toggle, not a switch
`.qi-switch` in islands.scss is already a binary on/off control with a
track and a knob. The chart's control picks one of N views, so it was
borrowing a name that meant something else.
viz-switch -> viz-toggle, data-viz-view-btn -> data-viz-toggle-btn,
wireSwitch -> wireToggle.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* refactor(viz): resolve a chart's arguments in one place
chart.html and chart.markdown.md both derived the id, the caption, the
spec and the per-view captions, from eleven identical lines. The rules had
already drifted: the HTML output refused a view missing its caption2=,
while the Markdown output printed an empty claim for it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(viz): refuse a per-chart width rather than stretch the chart
Charts deliberately share one width: the SVG scales to the column, so a
wider viewBox renders identical font sizes smaller. The manifest spread a
spec over that default, so a spec setting "width" drew at its own size
inside the 980-wide viewBox the shortcode builds, and came out stretched.
No chart sets one today; this makes the next one a build error.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(viz): check each chart's CSV against the table in its post
The same numbers live twice, in the CSV a chart is drawn from and in the
markdown table above it. Nothing compared them, so correcting one and not
the other publishes a chart that disagrees with its own table.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Abdon Pijpelink <abdon.pijpelink@qdrant.com>
* Replace the text processing diagram with an interactive island
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Add diagram to the 'BM25 Text Processing' section too
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
7 charts across 5 posts, in 3 kinds: two-panel columns, grouped columns and faceted lines. Replaces 1,024 KB of raster screenshots with 71 KB of inline SVG, hover and keyboard focus included.
Charts are generated from CSVs and committed, so a changed number shows up as a diff in review.
Pages also build as Markdown, where the shortcode emits the source data as a table instead of the raw SVG.
Joint article with Matin Mahmood of hyper3labs on hyperbolic embeddings for hierarchical data, and what it takes to serve them from Qdrant.
Covers the Poincare ball, a controlled comparison on the Google Product Taxonomy, the d+2 lift failing inside HNSW, and the prefetch plus Formula Query rescore that fixes it. Two figures are live against the collection: the taxonomy viewer and the CLIP comparison.