3470f48c8b57
prevent dense timeline dots flashing before canvas loads (v3.0.208)
- time
- 2026-10-03 14:53 UTC · 1 week ago
- machine
- Junes-MacBook-Pro.local
- agent
- gpt-6.1-sol
- session
- tidy operator navigation
- parent
- bd7e99daf81b
↓ kepr-3470f48c.tar.gzatlas at this save
files changed6 files
package.json+1 −1public/css/kepr.css+3 −0public/js/kepr.js+3 −1public/js/nav.js+9 −0src/pages/timeline.ts+3 −3tests/navbar.test.ts+2 −0package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "kepr",3- "version": "3.0.207",3+ "version": "3.0.208",44 "private": true,55 "type": "module",66 "scripts": {public/css/kepr.css+3 −0
@@ -510,6 +510,9 @@510510 .timeline .dot {511511 fill: var(--accent);512512 }513+.timeline-pending .timeline .dot {514+ visibility: hidden;515+}513516 .timeline .d0 {514517 fill: var(--accent-dim);515518 fill-opacity: 0.45;public/js/kepr.js+3 −1
@@ -990,7 +990,9 @@990990 991991 // Widths are only final once Martian Mono has loaded.992992 document.fonts.ready.then(tickerPace);993-timeline().catch((error) => console.error("kepr: the timeline could not load", error));993+timeline()994+ .catch((error) => console.error("kepr: the timeline could not load", error))995+ .finally(() => document.documentElement.classList.remove("timeline-pending"));994996 copyButtons(document);995997 panels();996998 saveLists();public/js/nav.js+9 −0
@@ -2,6 +2,15 @@22 // nav, before the rest of the page, so the signed-out links never show first. Pages are the same33 // for everyone; the readable kepr_editor cookie, set beside the session, carries the form token44 // that log out posts back. Only the private notification preview needs a request.5+// Suppress the dense static dots before the timeline markup can paint.6+document.documentElement.classList.add("timeline-pending");7+// A failed deferred script never reaches the timeline's finally block.8+window.addEventListener("load", () => {9+ if (typeof window.timeline !== "function") {10+ document.documentElement.classList.remove("timeline-pending");11+ }12+});13+514 {615 const token = /(?:^|;\s*)kepr_editor=([0-9a-f]{64})(?:;|$)/.exec(document.cookie)?.[1];716 const link = document.querySelector('.nav-links a[href^="/sign-in"]');src/pages/timeline.ts+3 −3
@@ -25,12 +25,12 @@2525 const x = span === 0 ? 50 : 1 + (98 * (t - first)) / span;2626 const near = countBetween(sorted, t - reach, t + reach);2727 const level = near >= 8 ? 3 : near >= 4 ? 2 : near >= 2 ? 1 : 0;28- return html`<circle class="dot d${level}" cx="${x.toFixed(3)}%" cy="28" r="${2.5 + level * 0.5}"/>`;28+ return html`<circle class="dot d${level}" cx="${x.toFixed(3)}%" cy="32" r="${2.5 + level * 0.5}"/>`;2929 });30- return html`<svg class="timeline" width="100%" height="56" role="img" aria-label="${30+ return html`<svg class="timeline" width="100%" height="64" role="img" aria-label="${3131 saves.length3232 } saves over time" data-src="/${repo}/timeline">33-<line class="timeline-line" x1="0" x2="100%" y1="28" y2="28"/>33+<line class="timeline-line" x1="0" x2="100%" y1="32" y2="32"/>3434 ${dots}3535 </svg>`;3636 }tests/navbar.test.ts+2 −0
@@ -14,6 +14,7 @@1414 let replacement: unknown[] = [];1515 const document = {1616 cookie,17+ documentElement: { classList: { add: () => {} } },1718 addEventListener: () => {},1819 querySelector: () => ({1920 parentElement: { after: () => {} },@@ -39,6 +40,7 @@3940 };4041 runInNewContext(script, {4142 document,43+ window: { addEventListener: () => {} },4244 fetch: async () => ({ ok: true, json: async () => ({ unread: 0, notifications: [] }) }),4345 });4446 return { nodes, replacement };