Reading map: a 3D hero of everything read, traced by what I highlighted
An orbiting point cloud of all 8,424 indexed passages, with the 262 highlighted ones lit and threaded in the order they were marked. Data comes from library-rag (chunks + embeddings) projected to 3D with UMAP; the page never touches Postgres. Projection is `airy` (n_neighbors=50, min_dist=0.8) and the palette is `frost` -- both chosen by eye against the alternatives. Note that airy shows the underlying book-territory structure LESS than the tight projection the pre-flight measured, so the 0.805 k-NN purity figure describes that one, not this. Page weight: the wrapper adds 321 B gzip to the homepage's blocking load. `three` (127 KB gz) and the payload (56 KB gz) are a dynamic chunk fetched after render. It fails closed -- no WebGL, no payload, or a dead fetch leaves a plain page. The payload is a static snapshot; the 5-minute auto-build rebuilds the site, not the data. New reading reaches this map only when export.py is re-run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
---
|
||||
/**
|
||||
* The reading map — an orbiting point cloud of every passage read, traced in the
|
||||
* order it was highlighted.
|
||||
*
|
||||
* import ReadingMap from '../components/ReadingMap.astro';
|
||||
* <ReadingMap />
|
||||
*
|
||||
* Pieces:
|
||||
* src/components/ReadingMap.astro ← this file
|
||||
* src/lib/reading-map.js ← the renderer (shared with the standalone preview
|
||||
* in c0smere_devops/library-rag/umap/web)
|
||||
* src/styles/reading-map.css ← the chrome
|
||||
* public/reading_umap.json ← `uv run umap/export.py -o …`
|
||||
*
|
||||
* ⚠️ The payload is a STATIC SNAPSHOT. Nothing on cyrion regenerates it — the 5-minute
|
||||
* auto-build rebuilds the site, not the data. New reading only reaches this map when
|
||||
* someone re-runs `export.py` and commits the result. See library-rag/umap/PLAN.md §7.
|
||||
*/
|
||||
import '../styles/reading-map.css';
|
||||
|
||||
interface Props {
|
||||
/** Payload URL. Same-origin: a static file, not an API call. */
|
||||
src?: string;
|
||||
/** Caption under the canvas. Pass `null` to omit. */
|
||||
caption?: string | null;
|
||||
}
|
||||
|
||||
const {
|
||||
src = '/reading_umap.json',
|
||||
caption = 'Every passage I have read, arranged by what it is about — traced in the order I highlighted it.',
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<figure class="reading-map-figure">
|
||||
<div class="reading-map" data-src={src}></div>
|
||||
{caption && <figcaption>{caption}</figcaption>}
|
||||
</figure>
|
||||
|
||||
<script>
|
||||
// Dynamic import so `three` lands in its own chunk rather than the page bundle, and so a
|
||||
// browser that never gets here (no WebGL, blocked script) pays nothing for it.
|
||||
//
|
||||
// ⚠️ Do NOT gate this on IntersectionObserver. `.reading-map:not([data-ready])` is
|
||||
// `display: none` — that is the fail-closed rule — and a `display: none` element has no
|
||||
// box, so it never intersects and the observer would never fire. Load-on-visible here
|
||||
// deadlocks: hidden until mounted, never mounted because hidden. The renderer runs its
|
||||
// own IntersectionObserver internally to park the animation loop offscreen, which is the
|
||||
// part that actually matters for cost.
|
||||
const containers = document.querySelectorAll<HTMLElement>('.reading-map');
|
||||
if (containers.length) {
|
||||
try {
|
||||
const { mount } = await import('../lib/reading-map.js');
|
||||
for (const el of containers) {
|
||||
mount(el, {
|
||||
src: el.dataset.src!,
|
||||
// Fail closed, like the garden's other widgets: a missing payload or a dead GPU
|
||||
// leaves a plain page rather than an empty bordered box.
|
||||
onError: (err: unknown) => console.warn('reading map unavailable:', err),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
/* chunk failed to load: the figure stays hidden */
|
||||
console.warn('reading map unavailable:', err);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.reading-map-figure {
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
.reading-map-figure figcaption {
|
||||
margin-top: 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
opacity: 0.72;
|
||||
}
|
||||
/* Hide the caption too when the map itself failed to mount. */
|
||||
.reading-map-figure:not(:has([data-ready])) figcaption {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user