Skip to content

Commit ebc14a1

Browse files
DAEKYU YOONDAEKYU YOON
authored andcommitted
Update docs, AREPO guide, and site layout
1 parent 82b3f7e commit ebc14a1

21 files changed

Lines changed: 2371 additions & 103 deletions

astro.config.mjs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@ export default defineConfig({
1515
starlight({
1616
plugins: [pagePlugin()],
1717
title: 'CosmoSim Primer',
18+
logo: {
19+
src: './src/assets/galaxy.svg',
20+
},
1821
description: 'Beginner-first guide to Arepo, Enzo, Gadget, and Gizmo.',
22+
pagination: false,
23+
components: {
24+
Header: '/src/components/PageHeader.astro',
25+
TableOfContents: '/src/components/InstantTableOfContents.astro',
26+
Footer: '/src/components/EmptyFooter.astro',
27+
LanguageSelect: '/src/components/LanguageSelect.astro'
28+
},
1929
defaultLocale: 'en',
2030
locales: {
2131
en: {
@@ -27,13 +37,7 @@ export default defineConfig({
2737
lang: 'ko'
2838
}
2939
},
30-
customCss: ['./src/styles/custom.css'],
31-
sidebar: [
32-
{
33-
label: 'Foundations',
34-
autogenerate: { directory: '.' }
35-
}
36-
]
40+
customCss: ['./src/styles/custom.css']
3741
})
3842
]
3943
});

public/galaxy.svg

Lines changed: 30 additions & 0 deletions
Loading

src/assets/galaxy.svg

Lines changed: 30 additions & 0 deletions
Loading

src/components/EmptyFooter.astro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
// Intentionally empty to remove in-content Previous/Next footer area.
3+
---
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
---
2+
import InstantTableOfContentsList from './InstantTableOfContentsList.astro';
3+
4+
const { toc } = Astro.locals.starlightRoute;
5+
const items = toc ? toc.items.filter((item) => item.slug !== '_top') : [];
6+
const minHeadingLevel = toc?.minHeadingLevel ?? 2;
7+
const maxHeadingLevel = toc?.maxHeadingLevel ?? 3;
8+
---
9+
10+
{
11+
toc &&
12+
items.length > 0 && (
13+
<instant-starlight-toc data-min-h={minHeadingLevel} data-max-h={maxHeadingLevel}>
14+
<nav aria-labelledby="starlight__on-this-page">
15+
<h2 id="starlight__on-this-page">{Astro.locals.t('tableOfContents.onThisPage')}</h2>
16+
<InstantTableOfContentsList toc={items} />
17+
</nav>
18+
</instant-starlight-toc>
19+
)
20+
}
21+
22+
<script>
23+
class InstantStarlightTOC extends HTMLElement {
24+
constructor() {
25+
super();
26+
this.currentLink = this.querySelector('a[aria-current="true"]');
27+
this.minH = parseInt(this.dataset.minH || '2', 10);
28+
this.maxH = parseInt(this.dataset.maxH || '3', 10);
29+
this.cleanup = null;
30+
const levels = Array.from(
31+
{ length: 1 + this.maxH - this.minH },
32+
(_, index) => `h${this.minH + index}`
33+
).join(',');
34+
this.tocHeadingSelector = `h1#_top,:where(${levels})[id]`;
35+
this.onIdle = (cb) =>
36+
(window.requestIdleCallback || ((idleCb) => window.setTimeout(idleCb, 1)))(cb);
37+
}
38+
39+
connectedCallback() {
40+
if (this.cleanup) this.cleanup();
41+
this.onIdle(() => this.init());
42+
}
43+
44+
disconnectedCallback() {
45+
if (this.cleanup) this.cleanup();
46+
}
47+
48+
setCurrent(link) {
49+
if (!link || link === this.currentLink) return;
50+
if (this.currentLink) this.currentLink.removeAttribute('aria-current');
51+
link.setAttribute('aria-current', 'true');
52+
this.currentLink = link;
53+
}
54+
55+
getRootMargin() {
56+
const navBarHeight = document.querySelector('header')?.getBoundingClientRect().height || 0;
57+
const mobileTocHeight = this.querySelector('summary')?.getBoundingClientRect().height || 0;
58+
59+
// Use the visual center of the readable area (below sticky header/mobile TOC)
60+
// as the activation line for the right-side TOC.
61+
const height = document.documentElement.clientHeight;
62+
const contentTop = navBarHeight + mobileTocHeight + 32;
63+
const visibleHeight = Math.max(1, height - contentTop);
64+
const center = contentTop + visibleHeight / 2;
65+
const bandHalf = 1;
66+
const top = Math.max(0, Math.round(center - bandHalf));
67+
const bottom = Math.round(center + bandHalf);
68+
return `-${top}px 0% ${bottom - height}px`;
69+
}
70+
71+
init() {
72+
const links = Array.from(this.querySelectorAll('a[href^="#"]'));
73+
if (!links.length) return;
74+
75+
const isHeading = (el) => el.matches(this.tocHeadingSelector);
76+
77+
const getElementHeading = (el) => {
78+
if (!el) return null;
79+
const origin = el;
80+
81+
while (el) {
82+
if (el.matches('.sl-markdown-content, main > *')) {
83+
return document.getElementById('_top');
84+
}
85+
if (isHeading(el)) return el;
86+
87+
const childHeading = el.querySelector(this.tocHeadingSelector);
88+
if (childHeading) return childHeading;
89+
90+
el = el.previousElementSibling;
91+
while (el?.lastElementChild) el = el.lastElementChild;
92+
const siblingHeading = getElementHeading(el);
93+
if (siblingHeading) return siblingHeading;
94+
}
95+
96+
return getElementHeading(origin.parentElement);
97+
};
98+
99+
const setCurrent = (entries) => {
100+
for (const { isIntersecting, target } of entries) {
101+
if (!isIntersecting) continue;
102+
const heading = getElementHeading(target);
103+
if (!heading) continue;
104+
const link = links.find((candidate) => {
105+
return candidate.hash === `#${encodeURIComponent(heading.id)}`;
106+
});
107+
if (link) {
108+
this.setCurrent(link);
109+
break;
110+
}
111+
}
112+
};
113+
114+
const toObserve = document.querySelectorAll(
115+
[
116+
`main :where(${this.tocHeadingSelector})`,
117+
`main :where(${this.tocHeadingSelector}, .sl-heading-wrapper) ~ *:not(:has(${this.tocHeadingSelector}))`,
118+
`main .sl-markdown-content > *:not(:has(${this.tocHeadingSelector}))`,
119+
`main > *:not(:has(${this.tocHeadingSelector}))`,
120+
].join(',')
121+
);
122+
123+
let observer;
124+
const observe = () => {
125+
if (observer) return;
126+
observer = new IntersectionObserver(setCurrent, { rootMargin: this.getRootMargin() });
127+
toObserve.forEach((el) => observer.observe(el));
128+
};
129+
observe();
130+
131+
let timeout;
132+
const handleResize = () => {
133+
if (observer) {
134+
observer.disconnect();
135+
observer = undefined;
136+
}
137+
window.clearTimeout(timeout);
138+
timeout = window.setTimeout(() => this.onIdle(observe), 200);
139+
};
140+
141+
window.addEventListener('resize', handleResize);
142+
this.cleanup = () => {
143+
window.removeEventListener('resize', handleResize);
144+
window.clearTimeout(timeout);
145+
if (observer) observer.disconnect();
146+
observer = undefined;
147+
};
148+
}
149+
}
150+
151+
if (!customElements.get('instant-starlight-toc')) {
152+
customElements.define('instant-starlight-toc', InstantStarlightTOC);
153+
}
154+
</script>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
interface TocItem {
3+
slug: string;
4+
text: string;
5+
children: TocItem[];
6+
}
7+
8+
interface Props {
9+
toc: TocItem[];
10+
depth?: number;
11+
}
12+
13+
const { toc, depth = 0 } = Astro.props;
14+
---
15+
16+
<ul>
17+
{
18+
toc.map((heading) => (
19+
<li>
20+
<a href={'#' + heading.slug}>
21+
<span>{heading.text}</span>
22+
</a>
23+
{heading.children.length > 0 && <Astro.self toc={heading.children} depth={depth + 1} />}
24+
</li>
25+
))
26+
}
27+
</ul>
28+
29+
<style define:vars={{ depth }}>
30+
@layer starlight.core {
31+
ul {
32+
padding: 0;
33+
list-style: none;
34+
}
35+
36+
a {
37+
--pad-inline: 0.5rem;
38+
display: block;
39+
border-radius: 0.25rem;
40+
padding-block: 0.25rem;
41+
padding-inline: calc(1rem * var(--depth) + var(--pad-inline)) var(--pad-inline);
42+
line-height: 1.25;
43+
}
44+
45+
a[aria-current='true'] {
46+
color: var(--sl-color-text-accent);
47+
}
48+
}
49+
</style>

0 commit comments

Comments
 (0)