Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/feedthejoe / src/app/main.js |
| Date | Not specified in source |
| Repository | Janady13/feedthejoe |
| Revision | 0e608a2b515a959bf35d2783428766624400ca3c |
| Branch | main |
| Path or archive member | src/app/main.js |
| Original SHA-256 | 0f5f4101b0682ad19ab2ab05d49d2cd0fd9049a607e028a8cc77ce452d5918ea |
| Git blob SHA | c55228db43a22a7148256604b51ea92b6c71fbb4 |
| Reading-copy SHA-256 | 0f5f4101b0682ad19ab2ab05d49d2cd0fd9049a607e028a8cc77ce452d5918ea |
Recovered source transcript
Download complete source text ↓184 lines · 6.6 KB
/**
* Composition root.
*
* The only module permitted to import from every layer, and the only place
* where the pure core is joined to the impure edge. Everything above this file
* is testable without a browser; everything below it is a browser detail.
*
* Read this file to understand the dataflow. It is deliberately linear:
*
* load corpus -> restore state -> render -> observe -> re-rank -> render
* ^ |
* +------------------------------------+
*/
import { loadCorpus } from '../adapters/corpus.js';
import { createStatePort } from '../adapters/storage.js';
import { createObserver } from '../adapters/observer.js';
import { rank, applyEvent, recordImpressions, initialState, DEFAULT_CONFIG } from '../core/rank/rank.js';
import { applyOrder, bindWhy } from '../ui/feed.js';
import { createTerrain } from '../ui/terrain.js';
/** Re-rank at most this often. Re-ordering under the reader's cursor is hostile. */
const RERANK_IDLE_MS = 2600;
/** Yield to the main thread, with a fallback for engines without the Scheduler API. */
async function yieldToMain() {
if ('scheduler' in globalThis && 'yield' in globalThis.scheduler) {
await globalThis.scheduler.yield();
} else {
await new Promise((r) => setTimeout(r, 0));
}
}
async function boot() {
const feedEl = document.querySelector('[data-feed]');
if (!feedEl) return;
const { items, dim } = await loadCorpus(
feedEl.dataset.corpus ?? '/content/corpus.json',
feedEl.dataset.embeddings ?? '/content/embeddings.bin',
);
const index = new Map(items.map((i) => [i.id, i]));
const indexObj = Object.fromEntries(index);
const topics = [...new Set(items.map((i) => i.topic))].sort();
const store = createStatePort(dim);
let state = store.load() ?? initialState(dim);
// Session seed: stable for this tab so a re-render is not a re-shuffle, but
// different between visitors so the site is not identical for everyone.
const seed = (() => {
try {
const k = 'ftj.seed';
const found = sessionStorage.getItem(k);
if (found) return Number(found);
const s = (Date.now() ^ (Math.random() * 0xffffffff)) >>> 0;
sessionStorage.setItem(k, String(s));
return s;
} catch { return 20260907; }
})();
let round = 0;
let dirty = false;
let timer = 0;
// The terrain needs the projected manifold — the 2D shape of the corpus.
// It is a separate fetch because the feed is fully usable without it: if this
// request fails the page loses its drawing, not its content.
const canvas = document.querySelector('[data-instrument]');
let terrain = null;
let manifold = null;
if (canvas) {
try {
const res = await fetch(feedEl.dataset.manifold ?? '/content/manifold.json', { credentials: 'omit' });
if (res.ok) {
manifold = await res.json();
terrain = createTerrain(canvas, manifold);
}
} catch {
/* No drawing. The document stands on its own. */
}
}
/** itemId -> its position on the manifold. */
const place = new Map((manifold?.points ?? []).map((p) => [p.id, p]));
/** Posterior means, normalised — "how much does the model like each topic". */
function topicMix() {
const means = topics.map((t) => {
const p = state.topics[t] ?? { alpha: DEFAULT_CONFIG.priorAlpha, beta: DEFAULT_CONFIG.priorBeta };
return p.alpha / (p.alpha + p.beta);
});
const sum = means.reduce((s, m) => s + m, 0) || 1;
return means.map((m) => m / sum);
}
/**
* Where on the manifold should the light fall?
*
* Not a projection of the interest vector — projecting a 384-dimensional
* vector into a 2D layout that was fitted to *distances* would put the light
* somewhere arbitrary. Instead, take the terrain positions of the entries the
* model currently ranks highest and use their weighted centroid. The light
* lands where the work it is recommending actually sits, which is both
* correct and legible: you can see it move to the region it is drawn to.
*
* @param {Array<{item: object}>} ranked
*/
function focusOf(ranked) {
let sx = 0; let sy = 0; let sw = 0;
for (let i = 0; i < Math.min(6, ranked.length); i += 1) {
const p = place.get(ranked[i].item.id);
if (!p) continue;
const w = Math.exp(-i / 2.5); // the top entry dominates
sx += p.x * w; sy += p.y * w; sw += w;
}
if (sw === 0) return null;
return { x: sx / sw, y: sy / sw };
}
async function rerank({ animate = true } = {}) {
const ranked = rank(items, state, { nowMs: Date.now(), seed, round, limit: items.length });
round += 1;
await yieldToMain();
applyOrder(feedEl, ranked, { animate });
state = recordImpressions(state, ranked.slice(0, 6).map((r) => r.item.id));
store.save(state);
const focus = focusOf(ranked);
if (focus) terrain?.update({ focus });
renderState();
}
const stateEl = document.querySelector('[data-state-topics]');
function renderState() {
if (!stateEl) return;
const mix = topicMix();
stateEl.innerHTML = topics.map((t, i) => {
const p = state.topics[t] ?? { alpha: 1, beta: 1 };
const evidence = p.alpha + p.beta - 2;
return `<div class="belief__t">
<span class="belief__n"><span>${t}</span><b>${(mix[i] * 100).toFixed(0)}%</b></span>
<span class="bar"><i style="--v:${mix[i].toFixed(3)}"></i></span>
<span class="belief__n" style="color:var(--ink-faint)"><span>${evidence > 0 ? `${evidence.toFixed(1)} observations` : 'no evidence yet'}</span></span>
</div>`;
}).join('');
}
function scheduleRerank() {
dirty = true;
clearTimeout(timer);
timer = setTimeout(() => { if (dirty) { dirty = false; rerank(); } }, RERANK_IDLE_MS);
}
const observer = createObserver({
root: feedEl,
index,
onSignal(ev) {
state = applyEvent(state, ev, indexObj);
scheduleRerank();
},
});
for (const el of feedEl.querySelectorAll('[data-item-id]')) observer.observe(el);
bindWhy(feedEl);
document.documentElement.classList.add('is-live');
// First pass is not animated: the reader has not asked for anything yet, and
// a page that rearranges itself on arrival is disorienting rather than clever.
await rerank({ animate: false });
const reset = document.querySelector('[data-reset]');
reset?.addEventListener('click', () => {
store.clear();
state = initialState(dim);
round = 0;
rerank();
});
}
boot().catch((err) => {
// A failure here must leave a readable document behind, not a blank page.
console.error('[feedthejoe] runtime failed; static document remains intact', err);
});