1
0
Fork 0

Adding upstream version 0.46.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-05 18:42:15 +01:00
parent eb42e29864
commit 5ea73fcab5
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
125 changed files with 8421 additions and 71 deletions

127
docs/theme/Autodoc.jinja vendored Normal file
View file

@ -0,0 +1,127 @@
{#def
obj: dict | None = None,
name: str = "",
level: int = 2,
members: bool = True,
#}
{% set obj = obj or autodoc(name) %}
<h{{ level }}>
<code class="doc-symbol doc-symbol-{{ obj.symbol }}">{{ obj.symbol }}</code>
<span class="doc-oname doc-oname-{{ obj.symbol }}">{{ name or obj.name }}</span>
{% if obj.label -%}
<small class="doc-olabel doc-olabel-{{ obj.label }}">
<code>{{ obj.label }}</code>
</small>
{%- endif %}
</h{{ level }}>
{%- if obj.short_description -%}
<div class="doc-short-description">
{{ obj.short_description | markdown | utils.widont }}
</div>
{% endif -%}
{%- if obj.signature -%}
<div class="doc-signature">
{% filter markdown -%}
```python
{{ obj.signature }}
```
{%- endfilter %}
</div>
{%- endif %}
{% if obj.bases -%}
<div class="doc-bases">
<p>Bases:
{%- for base in obj.bases %} <code>{{ base }}</code>{% if not loop.last %}, {% endif %}
{%- endfor %}
</p>
</div>
{%- endif %}
{% if obj.params -%}
<table class="doc-arguments">
<thead><tr><th>Argument</th><th>Description</th></tr>
</thead>
<tbody>
{%- for param in obj.params %}
<tr>
<td><code>{{ param.name }}</code></td>
<td>{{ param.description | markdown | utils.widont }}</td>
</tr>
{%- endfor %}
</tbody>
</table>
{%- endif %}
{%- if obj.description -%}
<div class="doc-long-description">
{{ obj.description | markdown | utils.widont }}
</div>
{% endif -%}
{% if obj.examples -%}
<div class="doc-examples">
<p><strong>Example:</strong></p>
{% for ex in obj.examples -%}
<div>
{% if ex.description %}{{ ex.description | markdown | utils.widont }}{% endif %}
{% if ex.snippet %}{{ ex.snippet }}{% endif %}
<div>
{% endfor -%}
</div>
{%- endif %}
{% if obj.returns -%}
<li class="doc-returns">
<p><strong>Returns:</strong></p>
{% if ex.returns -%}
<p>{{ obj.returns }}</p>
{%- endif %}
{% if ex.many_returns -%}
<ul>
{% for return in ex.many_returns %}
<li>{{ return }}</li>
{%- endfor %}
</ul>
{%- endif %}
</div>
{%- endif %}
{% if obj.raises -%}
<div class="doc-raises"></div>
<p><strong>Raises:</strong></p>
<ul>
{% for raises in obj.raises -%}
<li>{{ raises.description | markdown | utils.widont }}<ul>
{% endfor -%}
</ul>
</li>
{%- endif %}
{% if members -%}
{% if obj.attrs or obj.properties-%}
<div class="doc-attrs">
{% for attr in obj.attrs -%}
<Autodoc obj={{ attr }} level={{ level + 1 }} />
{% endfor %}
{% for attr in obj.properties %}
<Autodoc obj={{ attr }} level={{ level + 1 }} />
{%- endfor %}
</div>
{%- endif %}
{% if obj.methods -%}
<div class="doc-methods">
{% for method in obj.methods %}
<Autodoc obj={{ method }} level={{ level + 1 }} />
{%- endfor %}
</div>
{%- endif %}
{%- endif %}

45
docs/theme/Callout.jinja vendored Normal file
View file

@ -0,0 +1,45 @@
{#def title="", type="info", icon="", open=True #}
{% set icons = {
"note": "sticky_note",
"info": "info",
"tip": "check_circle",
"alert": "release_alert",
"warning": "warning",
"danger": "release_alert",
"error": "release_alert",
"internal": "rocket_launch",
"todo": "checklist",
} %}
{% if icon != False %}
{% set icon = icon or icons.get(type) %}
{% endif %}
{% do attrs.set(class="type-" + type or "none") %}
{% if title -%}
<details {{ attrs.render(open=open, class="cd-callout", data_component="Callout") }}>
<summary>
{% if icon -%}
<i class="icon">{{ icon }}</i>
{% endif -%}
{{ title }}
<icon class="icon arrow">keyboard_arrow_down</icon>
</summary>
<div class="content">{{content}}</div>
</details>
{%- else -%}
<aside {{ attrs.render(class="cd-callout", data_component="Callout") }}>
{% if icon -%}
<div class="icon-wrapper">
<i class="icon">{{ icon }}</i>
</div>
{%- endif %}
<div class="content">{{content}}</div>
</aside>
{%- endif %}

27
docs/theme/ExampleTabs.jinja vendored Normal file
View file

@ -0,0 +1,27 @@
{#def prefix, panels={} #}
<div {{ attrs.render(
class="cd-example-tabs not-prose",
data_component="ExampleTabs",
) }}>
<TabGroup class="example-tabgroup">
<TabList class="example-tablist" data-md-skip>
{%- for text in panels.keys() %}
<Tab
class="example-tab"
target={{ "ex-%s-%s" % (prefix, loop.index) }}
selected={{ loop.index == 1 }}
>{{ text }}</Tab>
{%- endfor %}
</TabList>
{%- for name in panels.values() %}
<TabPanel
class="example-tabpanel"
id={{ "ex-%s-%s" % (prefix, loop.index) }}
hidden={{ loop.index != 1 }}
>
{{ catalog.irender(name) }}
</TabPanel>
{%- endfor %}
</TabGroup>
</div>

10
docs/theme/Footer.jinja vendored Normal file
View file

@ -0,0 +1,10 @@
<footer {{ attrs.render(class="cd-footer", data_component="Footer") }}>
<div class="wrapper">
<Copyright />
<div class="built-with">
This site was
<a title="Coming soon!">built using Claydocs</a>
</div>
<ThemeSwitch class="themeswitch" />
</div>
</footer>

17
docs/theme/Header.jinja vendored Normal file
View file

@ -0,0 +1,17 @@
{#def title="", section="" #}
{% set section = section or page.section if section != false else None %}
{% set title = title or page.title %}
<header {{ attrs.render(
class="cd-header",
data_component="Header",
) }}>
<div>
{% if section -%}
<div>{{ section }}</div>
{%- endif %}
<h1>{{ title | utils.widont }}</h1>
</div>
<p class="description">{{ content | utils.widont }}</p>
</header>

25
docs/theme/Layout.jinja vendored Normal file
View file

@ -0,0 +1,25 @@
{#def title="", description="" #}
<!DOCTYPE html>
<html lang="{{ page.lang }}" class="light">
<head>
<meta charset="utf-8">
<MetaTags page={{ page }} />
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<link rel="apple-touch-icon" sizes="120x120" href="/static/img/apple-touch-icon.png">
<link rel="stylesheet" href="/static/theme.css?v={{ utils.timestamp }}">
<link rel="stylesheet" href="/static/prose.css?v={{ utils.timestamp }}">
<link rel="stylesheet" href="/static/docs.css?v={{ utils.timestamp }}">
{{ catalog.render_assets() }}
{% if page.prev_page and page.prev_page.url -%}
<link href="{{ page.prev_page.url }}" rel="prev">
{% endif -%}
{% if page.next_page and page.next_page.url -%}
<link href="{{ page.next_page.url }}" rel="next">
{% endif -%}
<meta property="generator" content="claydocs" />
</head>
<body {{ attrs.render() }}>
{{ content }}
</body>
</html>

22
docs/theme/MetaTags.jinja vendored Normal file
View file

@ -0,0 +1,22 @@
{#def page #}
{% set DEFAULT_TITLE = "JinjaX Documentation" %}
{% set title = (page.title + " | " + DEFAULT_TITLE) if page.title else DEFAULT_TITLE %}
{% set description = page.description %}
<title>{{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ nav.domain }}{{ page.url }}">
<meta property="og:title" content="{{ title }}">
<meta property="twitter:title" content="{{ title }}">
{% if description -%}
<meta property="og:description" content="{{ description }}">
<meta property="twitter:description" content="{{ description }}">
{%- endif %}
<meta property="og:image" content="{{ nav.domain }}{{ page.url }}/og-card.png">
<meta property="og:image:type" content="image/png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:image" content="{{ nav.domain }}{{ page.url }}/og-card.png">

3
docs/theme/NavBar.jinja vendored Normal file
View file

@ -0,0 +1,3 @@
<nav {{ attrs.render(class="cd-navbar", data_component="NavBar") }}>
{{ content }}
</nav>

3
docs/theme/NavGlobal.jinja vendored Normal file
View file

@ -0,0 +1,3 @@
<div {{ attrs.render(class="cd-nav-global scrollbar-thin", data_component="NavGlobal") }}>
<Toc toc={{ nav.toc }} page={{ page}} />
</div>

5
docs/theme/NavLocal.jinja vendored Normal file
View file

@ -0,0 +1,5 @@
<div {{ attrs.render(class="cd-nav-local", data_component="NavLocal") }}>
<div class="wrapper scrollbar-thin">
<TocPage :page_toc="page.toc" />
</div>
</div>

18
docs/theme/NavMobile.jinja vendored Normal file
View file

@ -0,0 +1,18 @@
{% do attrs.set(id="navMobile", class="cd-nav-mobile", data_component="NavMobile") %}
<Popover _attrs={{ attrs }}>
<header>
<Logo base_url={{ page.base_url }} class="logo" />
<nav class="cd-navbar nav-links">
<ThemeSwitch class="themeswitch" text={{ false }} />
</nav>
<PopButton
target="navMobile"
action="close"
title="Close menu"
class="cd-toggle-sidebar cd-text-button"
>Close</PopButton>
</header>
<Toc class="toc" toc={{ nav.toc }} page={{ page }} />
</Popover>

17
docs/theme/NavTop.jinja vendored Normal file
View file

@ -0,0 +1,17 @@
<section class="cd-nav-top", data_component="NavTop">
<div class="wrapper">
<Logo class="logo" base_url={{ page.base_url }} />
<nav class="cd-navbar nav-links">
<NavLinks />
<ThemeSwitch class="themeswitch" text={{ false }} />
</nav>
<nav class="nav-extra">
<NavExtra />
</nav>
<PopButton
target="navMobile"
title="Show menu"
class="cd-toggle-sidebar cd-text-button"
>Menu</PopButton>
</div>
</section>

19
docs/theme/Page.jinja vendored Normal file
View file

@ -0,0 +1,19 @@
<Layout
title={{ page.title }}
description={{ page.description }}
class={{ meta.get('class', 'cd-page') }}
>
<NavTop></NavTop>
<div class="page-wrapper">
<NavGlobal></NavGlobal>
<main id="main" class="page prose">{{ content }}</main>
<NavLocal></NavLocal>
</div>
<PrevNext
curr={{ page }}
prev={{ page.prev_page }}
next={{ page.next_page }}
/>
<NavMobile></NavMobile>
<Footer></Footer>
</Layout>

10
docs/theme/PageSingle.jinja vendored Normal file
View file

@ -0,0 +1,10 @@
<Layout
title={{ page.title }}
description={{ page.description }}
class={{ meta.get('class', 'cd-page-single') }}
>
<NavTop />
<main>{{ content }}</main>
<NavMobile />
<Footer />
</Layout>

26
docs/theme/PrevNext.jinja vendored Normal file
View file

@ -0,0 +1,26 @@
{#def curr, prev, next #}
<nav {{ attrs.render(class="cd-prevnext", data_component="PrevNext") }}>
{% if prev.url -%}
<a href="{{ prev.url }}" class="prev">
<i>&larr;</i>
<div>
<div class="section">
{{ prev.section or "Previous" if prev.section != curr.section else "Previous" }}
</div>
<div class="title">{{ prev.title }}</div>
</div>
</a>
{%- endif %}
{% if next.url -%}
<a href="{{ next.url }}" class="next">
<div>
<div class="section">
{{ next.section or "Next" if next.section != curr.section else "Next" }}
</div>
<div class="title">{{ next.title }}</div>
</div>
<i>&rarr;</i>
</a>
{%- endif %}
</nav>

61
docs/theme/SocialCard.jinja vendored Normal file
View file

@ -0,0 +1,61 @@
{#def page #}
<Layout
title={{ page.title }}
description={{page.description }}
>
<style>
body {
background-color: black;
}
.sc1 {
width: 1200px;
height: 630px;
margin: 0;
font-family: SF Pro Display, system-ui, sans-serif;
color: white;
background-image: linear-gradient(to bottom, #404faa, #0068b2);
}
.sc1__wrapper {
position: absolute;
inset: 60px 90px 90px 60px;
}
.sc1__wrapper > * {
position: absolute;
left: 0;
max-width: 1060px;
}
.sc1__logo {
top: 0;
height: 50px;
}
.sc1__section {
top: 150px;
line-height: 1;
font-weight: bold;
text-transform: uppercase;
font-size: 22px;
width: 600px;
}
.sc1__title {
top: 180px;
line-height: 1;
font-weight: bold;
font-size: 68px;
}
.sc1__description {
bottom: 0;
line-height: 1.4;
font-weight: normal;
font-size: 22px;
}
</style>
<article class="sc1">
<div class="sc1__wrapper">
<img class="sc1__logo" src="/static/img/jinjax-logo-w.png" />
<h2 class="sc1__section">{{ page.section }}</h2>
<h1 class="sc1__title">{{ page.title | utils.widont }}</h1>
<div class="sc1__description">{{ page.description | utils.widont }}</div>
</div>
</article>
</Layout>

24
docs/theme/Source.jinja vendored Normal file
View file

@ -0,0 +1,24 @@
{# def url: str, label: str = "" #}
{% do attrs.set(href=url, target="_blank") %}
{% set icon = url.replace("http://", "").replace("https://", "").split(".")[0] %}
<a {{ attrs.render(class="cd-source", data_component="Source") }}>
<div class="cd-source__icon">
{% if icon == "github" -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
{% elif icon == "gitlab" -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M503.5 204.6L502.8 202.8L433.1 21.02C431.7 17.45 429.2 14.43 425.9 12.38C423.5 10.83 420.8 9.865 417.9 9.57C415 9.275 412.2 9.653 409.5 10.68C406.8 11.7 404.4 13.34 402.4 15.46C400.5 17.58 399.1 20.13 398.3 22.9L351.3 166.9H160.8L113.7 22.9C112.9 20.13 111.5 17.59 109.6 15.47C107.6 13.35 105.2 11.72 102.5 10.7C99.86 9.675 96.98 9.295 94.12 9.587C91.26 9.878 88.51 10.83 86.08 12.38C82.84 14.43 80.33 17.45 78.92 21.02L9.267 202.8L8.543 204.6C-1.484 230.8-2.72 259.6 5.023 286.6C12.77 313.5 29.07 337.3 51.47 354.2L51.74 354.4L52.33 354.8L158.3 434.3L210.9 474L242.9 498.2C246.6 500.1 251.2 502.5 255.9 502.5C260.6 502.5 265.2 500.1 268.9 498.2L300.9 474L353.5 434.3L460.2 354.4L460.5 354.1C482.9 337.2 499.2 313.5 506.1 286.6C514.7 259.6 513.5 230.8 503.5 204.6z"/></svg>
{%- else -%}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
{%- endif %}
</div>
<div class="cd-source__repo">
<span class="cd-source__label">{{ label or url.split("/", 3)[-1] }}</span>
<ul class="cd-source__facts">
<li data-fact="version" hidden></li>
<li data-fact="stars" hidden></li>
<li data-fact="forks" hidden></li>
<li data-fact="numrepos" hidden></li>
</ul>
</div>
</a>

123
docs/theme/Source.js vendored Normal file
View file

@ -0,0 +1,123 @@
const ATTR_FACT = "data-fact";
const CLASS_FACTS = "cd-source__facts";
const CLASS_FACTS_VISIBLE = `${CLASS_FACTS}--visible`;
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll('[data-component="Source"]').forEach(showFacts);
});
function showFacts(node) {
function renderFacts(facts) {
Array.from(node.querySelectorAll(`[${ATTR_FACT}]`))
.forEach(function(node) {
const name = node.getAttribute(ATTR_FACT);
if (facts[name]) {
node.removeAttribute("hidden");
node.innerText = facts[name];
}
});
node.querySelector(`.${CLASS_FACTS}`).classList.add(CLASS_FACTS_VISIBLE);
}
getSourceFacts(node.href, renderFacts);
}
function getSourceFacts(url, callback) {
const key = `Source:${url}`;
let facts = sessionStorage.getItem(key);
if (facts) {
callback(JSON.parse(facts));
return;
}
fetchSourceFacts(url)
.then((facts) => {
if (facts && Object.keys(facts).length) {
sessionStorage.setItem(key, JSON.stringify(facts));
callback(facts);
}
});
}
function fetchJSON(url) {
return fetch(url)
.then(response => response.json())
.catch((error) => {
console.log(error);
});
}
function fetchSourceFacts(url) {
/* Try to match GitHub repository */
let match = url.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);
if (match) {
const [, user, repo] = match;
return fetchSourceFactsFromGitHub(user, repo);
}
/* Try to match GitLab repository */
match = url.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i);
if (match) {
const [, base, slug] = match;
return fetchSourceFactsFromGitLab(base, slug);
}
/* Fallback */
return null;
}
function fetchSourceFactsFromGitLab(base, project) {
const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}`
fetchJSON(url)
.then(function({ star_count, forks_count }) {
return {
stars: star_count,
forks: forks_count,
};
});
}
function fetchSourceFactsFromGitHub (user, repo) {
if (typeof repo === "undefined") {
return fetchSourceFactsFromGitHubOrg(user);
} else {
return fetchSourceFactsFromGitHubRepo(user, repo);
}
}
function fetchSourceFactsFromGitHubOrg(user) {
const url = `https://api.github.com/users/${user}`
fetchJSON(url)
.then(function(data) {
return {
numrepos: data.public_repos,
};
});
}
function fetchSourceFactsFromGitHubRepo(user, repo) {
const url = `https://api.github.com/repos/${user}/${repo}`
const release = fetchJSON(`${url}/releases/latest`)
.then((data) => {
return {
version: data.tag_name,
};
});
const info = fetchJSON(url)
.then((data) => {
return {
stars: data.stargazers_count,
forks: data.forks_count,
};
});
return Promise.all([release, info])
.then(([release, info]) => {
return { ...release, ...info };
});
}

6
docs/theme/Test.jinja vendored Normal file
View file

@ -0,0 +1,6 @@
<!DOCTYPE html><html>
<head><MetaTags page={{ page }} /></head><body class="prose">
{{ content }}
</body></html>

36
docs/theme/ThemeSwitch.jinja vendored Normal file
View file

@ -0,0 +1,36 @@
{#def text=true #}
<button {{ attrs.render(
class="cd-theme-switch",
data_component="ThemeSwitch",
title="Toggle light & dark mode",
aria_label="auto",
aria_live="polite",
) }}>
<svg
class="sun-and-moon"
aria-hidden="true"
stroke-linecap="round"
viewBox="0 0 24 24"
>
<circle class="sun" cx="12" cy="12" r="6" mask="url(#moon-mask)"></circle>
<g class="sun-beams">
<line x1="12" y1="1" x2="12" y2="3" />
<line x1="12" y1="21" x2="12" y2="23" />
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
<line x1="1" y1="12" x2="3" y2="12" />
<line x1="21" y1="12" x2="23" y2="12" />
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
</g>
<mask class="moon" id="moon-mask">
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<circle cx="24" cy="10" r="6"></circle>
</mask>
</svg>
{% if text -%}
<span class="light-text">Light</span>
<span class="dark-text">Dark</span>
{%- endif %}
</button>

41
docs/theme/ThemeSwitch.js vendored Normal file
View file

@ -0,0 +1,41 @@
import { on } from "./jxui.js";
const SEL_TARGET = ".cd-theme-switch";
const STORAGE_KEY = "theme";
const DARK = "dark";
const LIGHT = "light";
const theme = {value: getColorPreference()};
reflectPreference();
on("click", SEL_TARGET, onClick);
// sync with system changes
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", ({matches:isDark}) => {
theme.value = isDark ? DARK : LIGHT
setPreference()
});
function onClick (event, target) {
if (target.matches("[disabled]")) return;
theme.value = theme.value === LIGHT ? DARK : LIGHT;
setPreference();
}
function setPreference () {
localStorage.setItem(STORAGE_KEY, theme.value);
reflectPreference();
}
function reflectPreference () {
const value = getColorPreference ();
if (value === DARK) {
document.documentElement.classList.add(DARK);
document.documentElement.classList.remove(LIGHT);
} else {
document.documentElement.classList.add(LIGHT);
document.documentElement.classList.remove(DARK);
}
}
function getColorPreference () {
return localStorage.getItem(STORAGE_KEY);
}

45
docs/theme/Toc.jinja vendored Normal file
View file

@ -0,0 +1,45 @@
{# def toc, page #}
{% macro render_page(url, title) %}
{% if url != "/" -%}
<div class="page {{ 'active' if page.url == url else '' }}">
<a href="{{ url }}#">{{ title }}</a>
</div>
{%- endif %}
{% endmacro %}
{% macro render_collapsable(title, children) %}
<details open>
{% if title %}<summary>{{ title }}</summary>{% endif %}
{{ render_children(children) }}
</details>
{% endmacro %}
{% macro render_section(title, children) %}
<section open>
{% if title %}<h2>{{ title }}</h2>{% endif %}
{{ render_children(children) }}
</section>
{% endmacro %}
{% macro render_children(children, collapsable=True) %}
{%- for url, title, sub_children in children %}
{% if sub_children -%}
{% if collapsable -%}
{{ render_collapsable(title, sub_children) }}
{%- else -%}
{{ render_section(title, sub_children) }}
{%- endif %}
{%- else -%}
{{ render_page(url, title) }}
{%- endif %}
{%- endfor %}
{% endmacro %}
<div {{ attrs.render(class="cd-toc", data_component="Toc") }}>
{{ render_children(toc, collapsable=False) }}
</div>

21
docs/theme/TocPage.jinja vendored Normal file
View file

@ -0,0 +1,21 @@
{#def page_toc, max_depth=3 #}
{% macro render_sub_items(pages) %}
{%- for section in pages %}
<li class="indent-{{ section.level }}">
<a href="#{{ section.id }}"><span>{{ section.name }}</span></a>
</li>
{% if section.level <= max_depth -%}
{{ render_sub_items(section.children) }}
{%- endif %}
{%- endfor %}
{% endmacro %}
<ul {{ attrs.render(class="cd-toc-page", data_component="TocPage") }}>
{%- for section in page_toc %}
<li class="indent-{{ section.level }}">
<a href="#{{ section.id }}"><span>{{ section.name }}</span></a>
</li>
{{ render_sub_items(section.children) }}
{%- endfor %}
</ul>

97
docs/theme/TocPage.js vendored Normal file
View file

@ -0,0 +1,97 @@
import { on } from "./jxui.js";
const ACTIVE = "active";
const SEL_BACKTOTOP = ".cd-back-to-top"
const SEL_PAGETOC = ".cd-toc-page"
const SEL_TARGET = `${SEL_PAGETOC} a`;
const SEL_ACTIVE = `${SEL_TARGET}.${ACTIVE}`;
const SEL_PAGE = "#main.page";
const SEL_SECTIONS = `${SEL_PAGE} section[id]`;
const DESKTOP_THRESHOLD = 1024;
on("click", SEL_TARGET, handleClick);
on("click", SEL_BACKTOTOP, backToTop);
function handleClick(event, target) {
removeHighlight();
setTimeout(function () { updateHighlight(target) }, 10);
}
function updateHighlight (elem) {
if (window.innerWidth > DESKTOP_THRESHOLD && !elem?.classList.contains(ACTIVE)) {
removeHighlight();
if (!elem) return;
elem.classList.add(ACTIVE);
}
}
function removeHighlight () {
document.querySelectorAll(SEL_ACTIVE).forEach(function (node) {
node.classList.remove(ACTIVE);
});
}
function resetNavPosition () {
var pagetoc = document.querySelector(SEL_TOC);
pagetoc?.scroll({ top: 0 });
}
export function backToTop () {
window.scrollTo({ top: 0, behavior: "smooth" });
resetNavPosition();
}
export function scrollSpy() {
const sections = Array.from(document.querySelectorAll(SEL_SECTIONS));
function matchingNavLink(elem) {
if (!elem) return;
var index = sections.indexOf(elem);
var match;
while (index >= 0 && !match) {
var sectionId = sections[index].getAttribute("id");
if (sectionId) {
match = document.querySelector(`${SEL_PAGETOC} [href="#${sectionId}"]`);
}
index--;
}
return match;
}
function belowBottomHalf(i) {
return i.boundingClientRect.bottom > (i.rootBounds.bottom + i.rootBounds.top) / 2;
}
function prevElem(elem) {
var index = sections.indexOf(elem);
if (index <= 0) {
return null;
}
return sections[index - 1];
}
const PAGE_LOAD_BUFFER = 1000;
function navHighlight(entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
updateHighlight(matchingNavLink(entry.target));
} else if (entry.time >= PAGE_LOAD_BUFFER && belowBottomHalf(entry)) {
updateHighlight(matchingNavLink(prevElem(entry.target)));
}
});
}
const observer = new IntersectionObserver(navHighlight, {
threshold: 0,
rootMargin: "0% 0px -95% 0px"
});
sections.forEach(function (elem) {
observer.observe(elem);
})
observer.observe(document.querySelector(SEL_PAGE));
}
document.addEventListener("DOMContentLoaded", scrollSpy);