From c812483f60b42cc6f7fa91acd9cd1e6bab3799d0 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 1 Oct 2026 17:28:43 +0800 Subject: [PATCH] web: split the physics layer, render formulas, and fix the graph load The graph took over three seconds to draw. The force layout ran four hundred and twenty steps of repulsion between every pair of nodes, which is quadratic in the node count and was doing that work through a reactive proxy inside an effect that read the state it wrote, so it rescheduled itself on every run. The repulsion now runs on a grid over neighbouring cells, and the effect no longer tracks its own writes. The physics layer covered two unrelated subjects, so it is presented as two. Formulas are written between dollar signs and translated to MathML, which the browser sets itself, so no script has to run. The palette is now two sets of the same variables with a toggle between them, and below a thousand pixels the sidebar is a drawer over the page. --- content/concepts/chapters.json | 37 +- frontend/src/App.svelte | 59 ++- frontend/src/app.css | 250 +++++++++++- frontend/src/lib/GraphView.svelte | 102 ++++- frontend/src/lib/i18n.ts | 4 + frontend/src/lib/theme.ts | 80 ++++ frontend/src/lib/types.ts | 18 +- frontend/src/pages/Docs.svelte | 25 ++ src/api/mod.rs | 34 +- src/docs/document.rs | 167 +++++++- src/docs/markdown.rs | 194 ++++++++- src/docs/mathml.rs | 633 ++++++++++++++++++++++++++++++ src/docs/mod.rs | 1 + 13 files changed, 1538 insertions(+), 66 deletions(-) create mode 100644 frontend/src/lib/theme.ts create mode 100644 src/docs/mathml.rs diff --git a/content/concepts/chapters.json b/content/concepts/chapters.json index 1574734..0970528 100644 --- a/content/concepts/chapters.json +++ b/content/concepts/chapters.json @@ -30,15 +30,34 @@ "id": "physics", "title": "Physics", "title_zh": "物理层", - "summary": "Time scales, leap seconds, calendars, and the geometry of the Earth: its reference frames, its rotation, and its reference ellipsoid.", - "summary_zh": "时间尺度、闰秒与日历,以及地球的几何:参考系、自转与参考椭球。", - "documents": [ - "time-scales", - "leap-seconds", - "calendar-and-mjd", - "reference-frames", - "earth-rotation-angle", - "geodesy-wgs84" + "summary": "The physics layer holds two unrelated subjects: the time a measurement is stamped with, and the geometry of the Earth it is measured against.", + "summary_zh": "物理层包含两个彼此无关的主题:一次测量被标注的时刻,以及它所参照的地球几何。", + "documents": [], + "subsections": [ + { + "id": "physics-time", + "title": "Time", + "title_zh": "时间", + "summary": "Time scales, the leap seconds that keep them aligned with the Earth's rotation, and the calendar that turns an instant into a date.", + "summary_zh": "时间尺度、使其与地球自转对齐的闰秒,以及把时刻变为日期的日历。", + "documents": [ + "time-scales", + "leap-seconds", + "calendar-and-mjd" + ] + }, + { + "id": "physics-frames", + "title": "Frames and the Earth", + "title_zh": "参考系与地球", + "summary": "The frames a position is measured in, the angle that ties them together, and the ellipsoid that defines the surface.", + "summary_zh": "测量位置所用的参考系、把各参考系联系起来的角度,以及定义地表的椭球。", + "documents": [ + "reference-frames", + "earth-rotation-angle", + "geodesy-wgs84" + ] + } ] }, { diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 886b684..2d99b20 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -19,6 +19,8 @@ import { getJson, queryString } from './lib/api'; import { currentRoute, onRouteChange, navigate } from './lib/router'; import { language, pick, setLanguage, tr, type Language } from './lib/i18n'; + import { theme, toggleTheme } from './lib/theme'; + import { untrack } from 'svelte'; import Home from './pages/Home.svelte'; import Docs from './pages/Docs.svelte'; import DocumentPage from './pages/Document.svelte'; @@ -31,6 +33,15 @@ let route = $state(currentRoute()); /** The current language, read so that the shell re-renders on a switch. */ let lang = $derived($language); + let colour = $derived($theme); + + /** Whether the sidebar covers the page, on a narrow screen. */ + let menuOpen = $state(false); + + /** Switches between the dark and light palettes. */ + function onToggleTheme() { + toggleTheme(colour); + } let health = $state(null); let index = $state(null); @@ -49,6 +60,16 @@ let collapsed = $state([]); /** Folds a chapter, or unfolds it when it is already folded. */ + // The sidebar covers the page on a narrow screen, so it is closed when the + // route changes. Following a link is the usual way the route changes, and + // the links are ordinary anchors, so this is where the close belongs. + $effect(() => { + void route; + untrack(() => { + menuOpen = false; + }); + }); + function toggleChapter(id: string) { collapsed = collapsed.includes(id) ? collapsed.filter((entry) => entry !== id) @@ -165,7 +186,19 @@