Calc2
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 29s

This commit is contained in:
2026-10-02 02:14:29 +02:00
parent 325394150d
commit e36fef7290
21 changed files with 1745 additions and 46 deletions

View File

@@ -13,15 +13,70 @@ const props = defineProps<{
* keep two articles' "Proof" headings from sharing one id.
*/
namespace?: string
/**
* The article's own language: callout boxes (```theorem, …) label
* themselves in it, whatever language the interface happens to speak.
*/
articleLang?: string
}>()
const rendered = computed(() => renderMarkdown(props.content, props.namespace))
const rendered = computed(() => renderMarkdown(props.content, props.namespace, props.articleLang))
const router = useRouter()
/* Callout boxes fold away under their label. The browser's <details> carries
the state, but a state flip alone has no motion: the toggle click is caught
here and the fold — height and opacity of the box's body — runs through the
Web Animations API. Opening sets `open` right away (the body needs to be
laid out before it can be measured); closing flips it only once the body
has folded away. The box's intended state rides along as data-open, so
re-clicking mid-fold picks the animation up where it stands instead of
fighting it. */
const folds = new WeakMap<HTMLDetailsElement, Animation>()
function foldBox(details: HTMLDetailsElement, event: MouseEvent) {
const body = details.querySelector<HTMLElement>('.md-box-body')
const isOpen = details.dataset.open ?? String(details.open)
const wantOpen = isOpen !== 'true'
details.dataset.open = String(wantOpen)
// nothing to shrink, or the reader asks for no motion: let <details> answer
if (!body || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return
event.preventDefault()
const running = folds.get(details)
// a fold in flight shows its height — the new animation starts there
const from = running ? getComputedStyle(body).height : wantOpen ? '0px' : `${body.scrollHeight}px`
running?.cancel()
if (wantOpen) details.open = true
body.style.overflow = 'hidden'
const anim = body.animate(
[
{ height: from, opacity: wantOpen ? 0 : 1 },
{ height: wantOpen ? `${body.scrollHeight}px` : '0px', opacity: wantOpen ? 1 : 0 },
],
{ duration: 260, easing: 'cubic-bezier(0.33, 1, 0.68, 1)' },
)
folds.set(details, anim)
anim.onfinish = () => {
details.open = wantOpen
body.style.overflow = ''
folds.delete(details)
}
}
function onClick(e: MouseEvent) {
if (!props.interceptLinks) return
const target = e.target as HTMLElement
const summary = target.closest?.('summary')
if (summary) {
const details = summary.closest('details')
if (details) {
foldBox(details, e)
return
}
}
if (!props.interceptLinks) return
const anchor = target.closest?.('a')
if (!anchor) return
const href = anchor.getAttribute('href') || ''