summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--about/index.html39
-rw-r--r--blog/8seg/index.html49
-rw-r--r--blog/css-only-code-blocks/index.html259
-rw-r--r--blog/hsm-basics/index.html42
-rw-r--r--blog/ihsm-worlds-first-diy-hsm/index.html39
-rw-r--r--blog/index.html150
-rw-r--r--blog/index.xml52
-rw-r--r--blog/jupyterlab-notebook-file-oneliner/index.html85
-rw-r--r--blog/kicad-mesh-plugin/index.html71
-rw-r--r--blog/led-characterization/index.html65
-rw-r--r--blog/multichannel-led-driver/index.html67
-rw-r--r--blog/private-contact-discovery/index.html43
-rw-r--r--blog/serial-protocols/index.html39
-rw-r--r--blog/telekom-gpon-sfp/index.html49
-rw-r--r--blog/thors-hammer/index.html45
-rw-r--r--blog/wifi-led-driver/index.html45
-rw-r--r--blog/wsdiff-static-html-diffs/index.html169
-rw-r--r--categories/index.html37
-rw-r--r--categories/index.xml2
-rw-r--r--imprint/index.html39
-rw-r--r--index.html171
-rw-r--r--index.xml105
-rw-r--r--pagefind/fragment/unknown_13245ab.pf_fragmentbin0 -> 1038 bytes
-rw-r--r--pagefind/fragment/unknown_1eea6ce.pf_fragmentbin0 -> 531 bytes
-rw-r--r--pagefind/fragment/unknown_2322ae6.pf_fragmentbin0 -> 1779 bytes
-rw-r--r--pagefind/fragment/unknown_3584c24.pf_fragmentbin0 -> 304 bytes
-rw-r--r--pagefind/fragment/unknown_3c5b478.pf_fragmentbin0 -> 4109 bytes
-rw-r--r--pagefind/fragment/unknown_53eea2c.pf_fragmentbin0 -> 6682 bytes
-rw-r--r--pagefind/fragment/unknown_57eeca7.pf_fragmentbin0 -> 11912 bytes
-rw-r--r--pagefind/fragment/unknown_62bb76a.pf_fragmentbin0 -> 2899 bytes
-rw-r--r--pagefind/fragment/unknown_70d175b.pf_fragmentbin0 -> 8364 bytes
-rw-r--r--pagefind/fragment/unknown_74968c4.pf_fragmentbin0 -> 2157 bytes
-rw-r--r--pagefind/fragment/unknown_75d2fd6.pf_fragmentbin0 -> 1367 bytes
-rw-r--r--pagefind/fragment/unknown_89b567d.pf_fragmentbin0 -> 848 bytes
-rw-r--r--pagefind/fragment/unknown_91e77ca.pf_fragmentbin0 -> 443 bytes
-rw-r--r--pagefind/fragment/unknown_a052396.pf_fragmentbin0 -> 7726 bytes
-rw-r--r--pagefind/fragment/unknown_b311a8e.pf_fragmentbin0 -> 5047 bytes
-rw-r--r--pagefind/fragment/unknown_bb53633.pf_fragmentbin0 -> 6539 bytes
-rw-r--r--pagefind/fragment/unknown_c96659e.pf_fragmentbin0 -> 2431 bytes
-rw-r--r--pagefind/fragment/unknown_ca2490a.pf_fragmentbin0 -> 4982 bytes
-rw-r--r--pagefind/fragment/unknown_dca33a5.pf_fragmentbin0 -> 1692 bytes
-rw-r--r--pagefind/fragment/unknown_e377283.pf_fragmentbin0 -> 3163 bytes
-rw-r--r--pagefind/fragment/unknown_e47a975.pf_fragmentbin0 -> 888 bytes
-rw-r--r--pagefind/fragment/unknown_f18dc78.pf_fragmentbin0 -> 6502 bytes
-rw-r--r--pagefind/fragment/unknown_f1fb7e7.pf_fragmentbin0 -> 1564 bytes
-rw-r--r--pagefind/index/unknown_517251c.pf_indexbin0 -> 42032 bytes
-rw-r--r--pagefind/index/unknown_b9ec868.pf_indexbin0 -> 36987 bytes
-rw-r--r--pagefind/index/unknown_d860aa8.pf_indexbin0 -> 42062 bytes
-rw-r--r--pagefind/pagefind-entry.json1
-rw-r--r--pagefind/pagefind-highlight.js1069
-rw-r--r--pagefind/pagefind-modular-ui.css214
-rw-r--r--pagefind/pagefind-modular-ui.js8
-rw-r--r--pagefind/pagefind-ui.css1
-rw-r--r--pagefind/pagefind-ui.js2
-rw-r--r--pagefind/pagefind.js9
-rw-r--r--pagefind/pagefind.unknown_64739f45e64e1bc.pf_metabin0 -> 320 bytes
-rw-r--r--pagefind/wasm.unknown.pagefindbin0 -> 55137 bytes
-rw-r--r--posts/index.html37
-rw-r--r--posts/index.xml2
-rw-r--r--projects/8seg/index.html39
-rw-r--r--projects/gerbolyze/index.html41
-rw-r--r--projects/gerbonara/index.html41
-rw-r--r--projects/index.html37
-rw-r--r--projects/index.xml50
-rw-r--r--projects/kimesh/index.html39
-rw-r--r--projects/lolcat-c/index.html39
-rw-r--r--projects/python-mpv/index.html103
-rw-r--r--projects/svg-flatten/index.html39
-rw-r--r--projects/wsdiff/index.html39
-rw-r--r--sitemap.xml17
-rw-r--r--style.css196
-rw-r--r--wsdiff-example.html889
72 files changed, 4014 insertions, 560 deletions
diff --git a/about/index.html b/about/index.html
index ada859e..072aa77 100644
--- a/about/index.html
+++ b/about/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About" class="active">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -31,7 +39,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
@@ -82,17 +90,30 @@ that's used in the background of this site is by <a class="reference external" h
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/8seg/index.html b/blog/8seg/index.html
index 05f6fd0..793b338 100644
--- a/blog/8seg/index.html
+++ b/blog/8seg/index.html
@@ -1,12 +1,17 @@
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
- <title>8seg | Home</title>
+ <title>8seg Technical Overview | Home</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -25,16 +33,16 @@
</nav>
<header>
- <h1>8seg</h1>
+ <h1>8seg Technical Overview</h1>
<ul class="breadcrumbs">
<li><a href="/">jaseg.de</a></li>
- <li><a href="/blog/">Blog</a></li><li><a href="/blog/8seg/">8seg</a></li>
+ <li><a href="/blog/">Blog</a></li><li><a href="/blog/8seg/">8seg Technical Overview</a></li>
</ul>
<strong>2023-12-26</strong>
</header>
- <main>
- <div class="document" id="seg-technical-overview">
-<h1 class="title">8seg Technical Overview</h1>
+ <main data-pagefind-body>
+ <div class="document">
+
<div class="section" id="prologue">
<h2>Prologue</h2>
@@ -204,17 +212,30 @@ set of pre-programmed waveform transitions.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/css-only-code-blocks/index.html b/blog/css-only-code-blocks/index.html
new file mode 100644
index 0000000..5b15937
--- /dev/null
+++ b/blog/css-only-code-blocks/index.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html>
+<html><head>
+ <meta charset="utf-8">
+ <title>Code listings with nice line wrapping and line numbers from plain CSS | Home</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="color-scheme" content="dark light">
+ <link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
+</head>
+<body><nav>
+ <div class="internal">
+
+ <a href="/" title="Home">Home</a>
+ <a href="/blog/" title="Blog">Blog</a>
+ <a href="/projects/" title="Projects">Projects</a>
+ <a href="/about/" title="About">About</a>
+ </div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
+ <div class="external">
+ <a href="https://git.jaseg.de/" title="cgit">cgit</a>
+ <a href="https://github.com/jaseg" title="Github">Github</a>
+ <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a>
+ <a href="https://chaos.social/@jaseg" title="Mastodon">Mastodon</a>
+ </span>
+</nav>
+
+ <header>
+ <h1>Code listings with nice line wrapping and line numbers from plain CSS</h1>
+<ul class="breadcrumbs">
+ <li><a href="/">jaseg.de</a></li>
+ <li><a href="/blog/">Blog</a></li><li><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></li>
+</ul>
+ <strong>2025-07-23</strong>
+ </header>
+ <main data-pagefind-body>
+ <div class="document">
+
+
+<p>Code listings in web pages are often a bit of a pain to use. Often, they don't wrap on small screens. Also, copy-pasting
+code from a code listing often copies the line numbers along with the code. Finally, many implementations use
+heavyweight HTML and/or javascript, making them slow to render (looking at you, gitlab).</p>
+<p>For this blog, I wrote an implementation that renders HTML code listings entirely without JavaScript, renders line
+numbers using plain CSS such that they don't get selected with the code, and that works with the browser to wrap in a
+natural way while still supporting the little line continuation arrows that are used to show that a line was soft
+wrapped in text editors.</p>
+<p>This blog is rendered as a static site using <a class="reference external" href="https://gohugo.io/">Hugo</a> from a pile of <a class="reference external" href="https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html">RestructuredText</a> documents. RestructuredText renders
+code listings using <a class="reference external" href="https://pygments.org/">Pygments</a> by default. Pygments hard-bakes the line numbers into the generated HTML, so I am using a
+<a class="reference external" href="https://en.wikipedia.org/wiki/Monkey_patch">monkey-patched</a> hook that changes the line number rendering to just a bunch of empty <tt class="docutils literal">&lt;span&gt;</tt> elements. The resulting
+HTML for a code block then looks like this:</p>
+<pre class="code html literal-block">
+<span class="lineno"></span><span class="line"><span class="p">&lt;</span><span class="nt">pre</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;code [language] literal-block&quot;</span><span class="p">&gt;</span></span>
+<span class="lineno"></span><span class="line"> <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;lineno&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">span</span><span class="p">&gt;</span></span>
+<span class="lineno"></span><span class="line"> <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;line&quot;</span><span class="p">&gt;</span></span>
+<span class="lineno"></span><span class="line"> <span class="p">&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;[syntax highlight token]&quot;</span><span class="p">&gt;</span>The <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;&lt;</span><span class="nt">span</span> <span class="na">class</span><span class="o">=</span><span class="s">&quot;[other syntax highlight token]&quot;</span><span class="p">&gt;</span>code!<span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span></span>
+<span class="lineno"></span><span class="line"> <span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span></span>
+<span class="lineno"></span><span class="line"> <span class="cm">&lt;!-- ... repeat once for each source line. --&gt;</span></span>
+<span class="lineno"></span><span class="line"><span class="p">&lt;/</span><span class="nt">pre</span><span class="p">&gt;</span>
+</span></pre>
+<p>You can find the (rather short) source of the <tt class="docutils literal">rst2html</tt> wrapper <a class="reference external" href="#rst2html-wrapper">below</a>.</p>
+<div class="section" id="the-css">
+<h2>The CSS</h2>
+<p>This modified HTML structure of the code listing gets accompanied by some CSS to make it flow nicely. Here is a listing
+of the complete CSS controlling the listing. The only bit that isn't included here is the actual syntax styling rules
+for the pygments tokens.</p>
+<pre class="code css literal-block">
+<span class="lineno"></span><span class="line"><span class="c">/*****************************************************/</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Code block formatting / syntax highlighting rules */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/*****************************************************/</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">font-family</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;Fira Code&quot;</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">font-size</span><span class="p">:</span><span class="w"> </span><span class="mi">13</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">left</span><span class="p">;</span><span class="w"> </span><span class="c">/* Override default content &quot;justify&quot; alignment */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre-wrap</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">word-wrap</span><span class="p">:</span><span class="w"> </span><span class="kc">break-word</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow-x</span><span class="p">:</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">display</span><span class="p">:</span><span class="w"> </span><span class="k">grid</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">align-items</span><span class="p">:</span><span class="w"> </span><span class="kc">start</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">grid-template-columns</span><span class="p">:</span><span class="w"> </span><span class="n">min-content</span><span class="w"> </span><span class="mi">1</span><span class="n">fr</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">line</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-left</span><span class="p">:</span><span class="w"> </span><span class="nb">calc</span><span class="p">(</span><span class="mi">2</span><span class="kt">em</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">);</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-indent</span><span class="p">:</span><span class="w"> </span><span class="mi">-2</span><span class="kt">em</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-top</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">min-width</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">em</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Make individual syntax tokens wrap anywhere */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">line</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="nt">span</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow-wrap</span><span class="p">:</span><span class="w"> </span><span class="n">anywhere</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre-wrap</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* We render line numbers in CSS! */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">counter-increment</span><span class="p">:</span><span class="w"> </span><span class="n">lineno</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">word-break</span><span class="p">:</span><span class="w"> </span><span class="n">keep-all</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-left</span><span class="p">:</span><span class="w"> </span><span class="mi">15</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">padding-right</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">overflow</span><span class="p">:</span><span class="w"> </span><span class="kc">clip</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">relative</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">right</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text-muted</span><span class="p">);</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">border-right</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-fg-highlight</span><span class="p">);</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">align-self</span><span class="p">:</span><span class="w"> </span><span class="kc">stretch</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* We also handle line continuation markers in CSS. */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="p">::</span><span class="nd">after</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">position</span><span class="p">:</span><span class="w"> </span><span class="kc">absolute</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">right</span><span class="p">:</span><span class="w"> </span><span class="mi">5</span><span class="kt">px</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="s2">&quot;\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳&quot;</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">white-space</span><span class="p">:</span><span class="w"> </span><span class="kc">pre</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text-muted</span><span class="p">);</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Insert the actual line number */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="p">.</span><span class="nc">lineno</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">content</span><span class="p">:</span><span class="w"> </span><span class="nb">counter</span><span class="p">(</span><span class="n">lineno</span><span class="p">);</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="p">::</span><span class="nd">before</span><span class="w"> </span><span class="p">{</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"> </span><span class="k">counter-reset</span><span class="p">:</span><span class="w"> </span><span class="n">lineno</span><span class="p">;</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">.</span><span class="nc">hll</span><span class="w"> </span><span class="p">{}</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c">/* Following are about 50 lines that define the styling of each kind of pygments syntax highlight token. These lines</span></span>
+<span class="lineno"></span><span class="line"><span class="c"> all look like the following: */</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="p">.</span><span class="nc">code</span><span class="w"> </span><span class="p">.</span><span class="nc">c</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="nf">var</span><span class="p">(</span><span class="nv">--c-text</span><span class="p">);</span><span class="w"> </span><span class="k">font-weight</span><span class="p">:</span><span class="w"> </span><span class="mi">400</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="c">/* Comment */</span>
+</span></pre>
+<p>This CSS does a few things:</p>
+<blockquote>
+<ol class="arabic simple">
+<li>It renders the <tt class="docutils literal">&lt;pre&gt;</tt> code listing element using a two-column CSS <tt class="docutils literal">display: grid</tt> layout. The left column is
+used for the line numbers, and the right column is used for the code lines.</li>
+<li>It numbers the lines using a <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/counter">CSS Counter</a>. CSS counters are meant for things like numbering headings and such, but
+they are a perfect fit for our purpose.</li>
+<li>It inserts the counter value as the line number into the <tt class="docutils literal">&lt;span <span class="pre">class=&quot;lineno&quot;&gt;</span></tt> element's <tt class="docutils literal">::before</tt>
+pseudo-element. A side effect of using the <tt class="docutils literal">::before</tt> pseudo-element is that without doing anything extra, the
+line numbers will remain outside of the normal text selection so they will neither be highlighted when selecting
+listing content, nor will they be copied when copy/pasting the listing content.</li>
+<li>It inserts a string of <tt class="docutils literal"><span class="pre">&quot;\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳&quot;</span></tt> into the line number span's
+<tt class="docutils literal">::after</tt> pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks, and
+starting with an empty line. The <tt class="docutils literal">::after</tt> pseudo-element is positioned using <tt class="docutils literal">position: absolute</tt>, and the
+parent <tt class="docutils literal">&lt;span <span class="pre">class=&quot;lineno&quot;&gt;</span></tt> has <tt class="docutils literal">position: relative</tt> set. This way, the arrow pseudo-element gets placed on
+top of the lineno span without affecting the layout at all. By setting <tt class="docutils literal">overflow: clip</tt> on the parent <tt class="docutils literal">&lt;span
+<span class="pre">class=&quot;lineno&quot;&gt;</span></tt>, the arrow pseudo-element gets cut off vertically wherever the parent lineno element naturally
+ends.</li>
+</ol>
+</blockquote>
+<p>The line number span is inserted into the parent <tt class="docutils literal">&lt;pre&gt;</tt> element's CSS grid using <tt class="docutils literal"><span class="pre">align-self:</span> stretch</tt>, which
+causes it to vertically stretch to fill the available space. Since the line number span only contains the line number,
+its minimum height is a single line. As a result, it will stretch higher only when the corresponding code line in the
+right grid column stretches vertically because of line wrapping. When that happens, part of the arrow pseudo-element
+starts showing through from behind the <tt class="docutils literal">overflow: clip</tt> of the line number span, and one arrow gets rendered for each
+wrapped listing line.</p>
+<p>When the page is too narrow, we don't want the code listing's lines to wrapp into a column of single characters. To
+prevent that, we simply set a <tt class="docutils literal"><span class="pre">min-width</span></tt> on the <tt class="docutils literal">&lt;span <span class="pre">class=&quot;line&quot;&gt;</span></tt> in the right column, and set <tt class="docutils literal"><span class="pre">overflow-x:</span>
+auto</tt> on the listing <tt class="docutils literal">&lt;pre&gt;</tt>. This results in a horizontal scroll bar appearing whenever the listing gets too narrow.</p>
+<p>You can try out the line wrapping by resizing this page!</p>
+</div>
+<div class="section" id="rst2html-wrapper">
+<h2>rst2html wrapper</h2>
+<p>Here is the python <tt class="docutils literal">rst2html</tt> wrapper that monkey-patches code rendering. I made hugo invoke this while building the
+page by simply overriding the <tt class="docutils literal">PATH</tt> environment variable.</p>
+<pre class="code python literal-block">
+<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Based on https://gist.github.com/mastbaum/2655700 for the basic plugin scaffolding</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">re</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">docutils.core</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.transforms</span><span class="w"> </span><span class="kn">import</span> <span class="n">Transform</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.nodes</span><span class="w"> </span><span class="kn">import</span> <span class="n">TextElement</span><span class="p">,</span> <span class="n">Inline</span><span class="p">,</span> <span class="n">Text</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.parsers.rst</span><span class="w"> </span><span class="kn">import</span> <span class="n">Directive</span><span class="p">,</span> <span class="n">directives</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">docutils.writers.html4css1</span><span class="w"> </span><span class="kn">import</span> <span class="n">Writer</span><span class="p">,</span> <span class="n">HTMLTranslator</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span><span class="w"> </span><span class="nc">UnfuckedHTMLTranslator</span><span class="p">(</span><span class="n">HTMLTranslator</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">False</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">visit_literal_block</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Insert an empty &quot;lineno&quot; span before each line. We insert the line numbers using pure CSS in a ::before</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># pseudo-element. This has the added advantage that the line numbers don't get included in text selection.</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># These line number spans are also used to show line continuation markers when a line is wrapped.</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">True</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">starttag</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="s1">'pre'</span><span class="p">,</span> <span class="n">CLASS</span><span class="o">=</span><span class="s1">'literal-block'</span><span class="p">))</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'&lt;span class=&quot;lineno&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;'</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">depart_literal_block</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span> <span class="o">=</span> <span class="kc">False</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1">&lt;/span&gt;&lt;/pre&gt;</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">visit_Text</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">in_literal_block</span><span class="p">:</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="s1">'([^</span><span class="se">\n</span><span class="s1">]*)(</span><span class="se">\n</span><span class="s1">|$)'</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">astext</span><span class="p">()):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">text</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">groups</span><span class="p">()</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="n">text</span><span class="p">:</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">visit_Text</span><span class="p">(</span><span class="n">Text</span><span class="p">(</span><span class="n">text</span><span class="p">))</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">:</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">Inline</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">depart_inline</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">body</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s1">'&lt;/span&gt;</span><span class="se">\n</span><span class="s1">&lt;span class=&quot;lineno&quot;&gt;&lt;/span&gt;&lt;span class=&quot;line&quot;&gt;'</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">,</span> <span class="n">Inline</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">visit_inline</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">else</span><span class="p">:</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">visit_Text</span><span class="p">(</span><span class="n">node</span><span class="p">)</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">html_writer</span> <span class="o">=</span> <span class="n">Writer</span><span class="p">()</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">html_writer</span><span class="o">.</span><span class="n">translator_class</span> <span class="o">=</span> <span class="n">UnfuckedHTMLTranslator</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">docutils</span><span class="o">.</span><span class="n">core</span><span class="o">.</span><span class="n">publish_cmdline</span><span class="p">(</span><span class="n">writer</span><span class="o">=</span><span class="n">html_writer</span><span class="p">)</span>
+</span></pre>
+</div>
+</div>
+ </main><footer>
+ Copyright © 2025 Jan Sebastian Götte
+ / <a href="/about/">About</a>
+ / <a href="/imprint/">Imprint</a>
+</footer>
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
+ </script>
+ </body>
+</html>
diff --git a/blog/hsm-basics/index.html b/blog/hsm-basics/index.html
index 0c9a062..cfa72b9 100644
--- a/blog/hsm-basics/index.html
+++ b/blog/hsm-basics/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,12 +40,10 @@
</ul>
<strong>2019-05-17</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
-<div class="section" id="hardware-security-modules-and-security-research-and-cryptography">
-<h2>Hardware Security Modules and Security Research and Cryptography</h2>
<p>On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of
Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level
hardware security people and people working in the financial industry HSMs are not thought about that often. In
@@ -45,7 +51,6 @@ particular most network or systems security people would not consider them an op
really interesting to think about what could be done with an HSM in conjunction with modern cryptography (instead of
just plain old RSA-OAEP and AES-CBC).</p>
<p><a class="reference external" href="mori_semi_hsm_talk_web.pdf">Click here to download a PDF with the slides for this talk.</a></p>
-</div>
<div class="section" id="ideas-for-research-in-hsms">
<h2>Ideas for research in HSMs</h2>
<p>Preparing for this talk brought me back to some research ideas I've been working on for a while now. Since I'm not sure
@@ -224,17 +229,30 @@ while not providing better sensitivity.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/ihsm-worlds-first-diy-hsm/index.html b/blog/ihsm-worlds-first-diy-hsm/index.html
index 67a3732..b393484 100644
--- a/blog/ihsm-worlds-first-diy-hsm/index.html
+++ b/blog/ihsm-worlds-first-diy-hsm/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2021-11-23</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document" id="world-s-first-diy-hsm">
<h1 class="title">World's First DIY HSM</h1>
@@ -65,17 +73,30 @@ could go out and build. We are planning to release this sort of documentation at
focusing our effort on the next iteration of the design instead. Stay tuned for updates ;)</p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/index.html b/blog/index.html
index d8947b2..f50aa1b 100644
--- a/blog/index.html
+++ b/blog/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,17 +40,50 @@
</header>
<main class="cards">
- <div class="intro">
- <div class="document">
+ <div class="card"><h3><a href="/blog/wsdiff-static-html-diffs/">wsdiff: Responsive diffs in plain HTML</a></h3><strong>2025-07-25</strong>
+
+ <div class="summary">
+ <div class="document">
+
+
+<p>There's many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed this by publishing wsdiff, a diffing tool written in Python that produces diffs as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch between unified and split diffs based on screen size using only CSS.</p>
+</div>
+ <a href="http://jaseg.de/blog/wsdiff-static-html-diffs/">Read more</a>
+ </div>
+</div>
+
+ <div class="card"><h3><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></h3><strong>2025-07-23</strong>
+
+ <div class="summary">
+ <div class="document">
+
+
+<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p>
+</div>
+ <a href="http://jaseg.de/blog/css-only-code-blocks/">Read more</a>
+ </div>
+</div>
+ <div class="card"><h3><a href="/blog/jupyterlab-notebook-file-oneliner/">Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</a></h3><strong>2025-06-29</strong>
+ <div class="summary">
+ <div class="document">
+
+<p>If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.</p>
+</div>
+ <a href="http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/">Read more</a>
+ </div>
</div>
- </div>
- <div class="card"><h3><a href="/blog/8seg/">8seg</a></h3><strong>2023-12-26</strong>
+
+ <div class="card"><h3><a href="/blog/8seg/">8seg Technical Overview</a></h3><strong>2023-12-26</strong>
<div class="summary">
- 8seg Technical Overview Prologue German hacker culture has this intense love for things that light up in colorful ways. Like for many others in this community, I have always been fascinated by LEDs. One of the first things on my pile of unfinished projects was to build my own LED matrix and use it to display text. When I started that project, I was still new to electronics. Back then, commercial LED matrices were limited to red or green color only, and were very expensive, so there was an incentive to build your own.
+ <div class="document">
+
+
+<p>8seg is a large-scale LED light art installation that displays text on a 1.5 meter high, 30 meter wide 8-segment display made from cheap LED tape.</p>
+</div>
<a href="http://jaseg.de/blog/8seg/">Read more</a>
</div>
</div>
@@ -50,7 +91,11 @@
<div class="card"><h3><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></h3><strong>2022-02-21</strong>
<div class="summary">
- Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.
+ <div class="document">
+
+
+<p>Short tutorial on getting a Deutsche Telekom GPON internet connection running using a SFP ONU unit in an Ubiquiti EdgeRouter.</p>
+</div>
<a href="http://jaseg.de/blog/telekom-gpon-sfp/">Read more</a>
</div>
</div>
@@ -58,7 +103,11 @@
<div class="card"><h3><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></h3><strong>2021-11-23</strong>
<div class="summary">
- World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.
+ <div class="document">
+
+
+<p>Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any single-board computer using a moving tamper-sensing mesh made from cheap PCBs.</p>
+</div>
<a href="http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/">Read more</a>
</div>
</div>
@@ -66,7 +115,11 @@
<div class="card"><h3><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></h3><strong>2020-08-18</strong>
<div class="summary">
- Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.
+ <div class="document">
+
+
+<p>I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can route around existing footprints and traces on the PCB.</p>
+</div>
<a href="http://jaseg.de/blog/kicad-mesh-plugin/">Read more</a>
</div>
</div>
@@ -74,7 +127,11 @@
<div class="card"><h3><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></h3><strong>2019-06-22</strong>
<div class="summary">
- Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users.
+ <div class="document">
+
+
+<p>I gave a short introduction into Private Contact Discovery protocols at our university workgroup.</p>
+</div>
<a href="http://jaseg.de/blog/private-contact-discovery/">Read more</a>
</div>
</div>
@@ -82,7 +139,11 @@
<div class="card"><h3><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></h3><strong>2019-05-17</strong>
<div class="summary">
- Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.
+ <div class="document">
+
+
+<p>I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on interesting research directions.</p>
+</div>
<a href="http://jaseg.de/blog/hsm-basics/">Read more</a>
</div>
</div>
@@ -90,9 +151,18 @@
<div class="card"><h3><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></h3><strong>2018-05-19</strong>
<div class="summary">
- Scroll to the end for the TL;DR.
-In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions.
-If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.
+ <div class="document">
+
+
+<p>Scroll to the end for the <a class="reference internal" href="#conclusion">TL;DR</a>.</p>
+<p>In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will
+summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error
+conditions.</p>
+<p>If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to
+another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and
+quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into
+your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to
+set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.</p></div>
<a href="http://jaseg.de/blog/serial-protocols/">Read more</a>
</div>
</div>
@@ -100,8 +170,11 @@ If you have done low-level microcontroller firmware you will regularly have had
<div class="card"><h3><a href="/blog/thors-hammer/">Thor&#39;s Hammer</a></h3><strong>2018-05-03</strong>
<div class="summary">
- In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards.
-Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press.
+ <div class="document">
+
+
+<p>In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards.</p>
+</div>
<a href="http://jaseg.de/blog/thors-hammer/">Read more</a>
</div>
</div>
@@ -109,7 +182,11 @@ Your browser does not support the HTML5 video tag. A demonstration of the comple
<div class="card"><h3><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></h3><strong>2018-05-02</strong>
<div class="summary">
- Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.
+ <div class="document">
+
+
+<p>Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on all 32 channels using a cheap microcontroller by using Binary Code Modulation.</p>
+</div>
<a href="http://jaseg.de/blog/multichannel-led-driver/">Read more</a>
</div>
</div>
@@ -117,7 +194,11 @@ Your browser does not support the HTML5 video tag. A demonstration of the comple
<div class="card"><h3><a href="/blog/wifi-led-driver/">Wifi Led Driver</a></h3><strong>2018-05-02</strong>
<div class="summary">
- Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.
+ <div class="document">
+
+
+<p>After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I just want to pop a piece of LED tape or two somewhere, but I don't need a full 32 channels of control. I ended up thinking that a smaller version of the 32-channel driver that didn't require a separate control computer would be handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266 module instead of the RS485 transceiver for WiFi connectivity.</p>
+</div>
<a href="http://jaseg.de/blog/wifi-led-driver/">Read more</a>
</div>
</div>
@@ -125,23 +206,40 @@ Your browser does not support the HTML5 video tag. A demonstration of the comple
<div class="card"><h3><a href="/blog/led-characterization/">LED Characterization</a></h3><strong>2018-05-02</strong>
<div class="summary">
- Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong!
+ <div class="document">
+
+
+<p>Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look <em>all wrong</em>! This observation led me down a rabbit hole of color perception and LED peculiarities.</p>
+</div>
<a href="http://jaseg.de/blog/led-characterization/">Read more</a>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/index.xml b/blog/index.xml
index 5054ab1..718b0d0 100644
--- a/blog/index.xml
+++ b/blog/index.xml
@@ -4,90 +4,108 @@
<title>Blog on Home</title>
<link>http://jaseg.de/blog/</link>
<description>Recent content in Blog on Home</description>
- <generator>Hugo -- gohugo.io</generator>
+ <generator>Hugo</generator>
<language>en-us</language>
<copyright>Jan Sebastian Götte</copyright>
- <lastBuildDate>Tue, 26 Dec 2023 15:26:00 +0100</lastBuildDate>
+ <lastBuildDate>Fri, 25 Jul 2025 23:42:00 +0100</lastBuildDate>
<atom:link href="http://jaseg.de/blog/index.xml" rel="self" type="application/rss+xml" />
<item>
- <title>8seg</title>
+ <title>wsdiff: Responsive diffs in plain HTML</title>
+ <link>http://jaseg.de/blog/wsdiff-static-html-diffs/</link>
+ <pubDate>Fri, 25 Jul 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/wsdiff-static-html-diffs/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;There&#39;s many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed this by publishing wsdiff, a diffing tool written in Python that produces diffs as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch between unified and split diffs based on screen size using only CSS.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>Code listings with nice line wrapping and line numbers from plain CSS</title>
+ <link>http://jaseg.de/blog/css-only-code-blocks/</link>
+ <pubDate>Wed, 23 Jul 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/css-only-code-blocks/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Code listings in web pages are often a bit of a pain to use. Usually, they don&#39;t wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</title>
+ <link>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</link>
+ <pubDate>Sun, 29 Jun 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>8seg Technical Overview</title>
<link>http://jaseg.de/blog/8seg/</link>
<pubDate>Tue, 26 Dec 2023 15:26:00 +0100</pubDate>
<guid>http://jaseg.de/blog/8seg/</guid>
- <description>8seg Technical Overview Prologue German hacker culture has this intense love for things that light up in colorful ways. Like for many others in this community, I have always been fascinated by LEDs. One of the first things on my pile of unfinished projects was to build my own LED matrix and use it to display text. When I started that project, I was still new to electronics. Back then, commercial LED matrices were limited to red or green color only, and were very expensive, so there was an incentive to build your own.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;8seg is a large-scale LED light art installation that displays text on a 1.5 meter high, 30 meter wide 8-segment display made from cheap LED tape.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</title>
<link>http://jaseg.de/blog/telekom-gpon-sfp/</link>
<pubDate>Mon, 21 Feb 2022 20:00:00 +0100</pubDate>
<guid>http://jaseg.de/blog/telekom-gpon-sfp/</guid>
- <description>Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Short tutorial on getting a Deutsche Telekom GPON internet connection running using a SFP ONU unit in an Ubiquiti EdgeRouter.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>New Paper on Inertial Hardware Security Modules</title>
<link>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</link>
<pubDate>Tue, 23 Nov 2021 23:42:20 +0100</pubDate>
<guid>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</guid>
- <description>World&#39;s First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any single-board computer using a moving tamper-sensing mesh made from cheap PCBs.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Kicad Mesh Plugin</title>
<link>http://jaseg.de/blog/kicad-mesh-plugin/</link>
<pubDate>Tue, 18 Aug 2020 13:15:39 +0200</pubDate>
<guid>http://jaseg.de/blog/kicad-mesh-plugin/</guid>
- <description>Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can route around existing footprints and traces on the PCB.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Private Contact Discovery</title>
<link>http://jaseg.de/blog/private-contact-discovery/</link>
<pubDate>Sat, 22 Jun 2019 10:30:00 +0800</pubDate>
<guid>http://jaseg.de/blog/private-contact-discovery/</guid>
- <description>Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user&#39;s address book, find out which of their contacts also use the same messenger without the messenger&#39;s servers learning anything about the user&#39;s address book. The widespread non-private way to do this is to simply upload the user&#39;s address book to the app&#39;s operator&#39;s servers and do an SQL JOIN keyed on the phone number field against the database of registered users.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I gave a short introduction into Private Contact Discovery protocols at our university workgroup.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Hardware Security Module Basics</title>
<link>http://jaseg.de/blog/hsm-basics/</link>
<pubDate>Fri, 17 May 2019 15:29:20 +0800</pubDate>
<guid>http://jaseg.de/blog/hsm-basics/</guid>
- <description>Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori&#39;s security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on interesting research directions.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>How to talk to your microcontroller over serial</title>
<link>http://jaseg.de/blog/serial-protocols/</link>
<pubDate>Sat, 19 May 2018 08:09:46 +0200</pubDate>
<guid>http://jaseg.de/blog/serial-protocols/</guid>
- <description>Scroll to the end for the TL;DR.
-In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions.
-If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Scroll to the end for the &lt;a class=&#34;reference internal&#34; href=&#34;#conclusion&#34;&gt;TL;DR&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will&#xA;summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error&#xA;conditions.&lt;/p&gt;&#xA;&lt;p&gt;If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to&#xA;another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and&#xA;quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into&#xA;your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to&#xA;set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.&lt;/p&gt;&lt;/div&gt;</description>
</item>
<item>
<title>Thor&#39;s Hammer</title>
<link>http://jaseg.de/blog/thors-hammer/</link>
<pubDate>Thu, 03 May 2018 11:59:37 +0200</pubDate>
<guid>http://jaseg.de/blog/thors-hammer/</guid>
- <description>In case you were having an inferiority complex because your friends&#39; IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here&#39;s the solution: Thor&#39;s Hammer, a simple typing cadence enhancer for PS/2 keyboards.
-Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard&#39;s PS/2 clock line and briefly actuates a large solenoid on each key press.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;In case you were having an inferiority complex because your friends&#39; IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here&#39;s the solution: Thor&#39;s Hammer, a simple typing cadence enhancer for PS/2 keyboards.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>32-Channel LED tape driver</title>
<link>http://jaseg.de/blog/multichannel-led-driver/</link>
<pubDate>Wed, 02 May 2018 11:31:14 +0200</pubDate>
<guid>http://jaseg.de/blog/multichannel-led-driver/</guid>
- <description>Theoretical basics Together, a friend and I outfitted the small staircase at Berlin&#39;s Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Together, a friend and I outfitted the small staircase at Berlin&#39;s Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on all 32 channels using a cheap microcontroller by using Binary Code Modulation.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Wifi Led Driver</title>
<link>http://jaseg.de/blog/wifi-led-driver/</link>
<pubDate>Wed, 02 May 2018 11:31:03 +0200</pubDate>
<guid>http://jaseg.de/blog/wifi-led-driver/</guid>
- <description>Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I just want to pop a piece of LED tape or two somewhere, but I don&#39;t need a full 32 channels of control. I ended up thinking that a smaller version of the 32-channel driver that didn&#39;t require a separate control computer would be handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266 module instead of the RS485 transceiver for WiFi connectivity.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>LED Characterization</title>
<link>http://jaseg.de/blog/led-characterization/</link>
<pubDate>Wed, 02 May 2018 11:18:38 +0200</pubDate>
<guid>http://jaseg.de/blog/led-characterization/</guid>
- <description>Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong!</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look &lt;em&gt;all wrong&lt;/em&gt;! This observation led me down a rabbit hole of color perception and LED peculiarities.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
</channel>
</rss>
diff --git a/blog/jupyterlab-notebook-file-oneliner/index.html b/blog/jupyterlab-notebook-file-oneliner/index.html
new file mode 100644
index 0000000..9eb93a2
--- /dev/null
+++ b/blog/jupyterlab-notebook-file-oneliner/index.html
@@ -0,0 +1,85 @@
+<!DOCTYPE html>
+<html><head>
+ <meta charset="utf-8">
+ <title>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook | Home</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="color-scheme" content="dark light">
+ <link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
+</head>
+<body><nav>
+ <div class="internal">
+
+ <a href="/" title="Home">Home</a>
+ <a href="/blog/" title="Blog">Blog</a>
+ <a href="/projects/" title="Projects">Projects</a>
+ <a href="/about/" title="About">About</a>
+ </div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
+ <div class="external">
+ <a href="https://git.jaseg.de/" title="cgit">cgit</a>
+ <a href="https://github.com/jaseg" title="Github">Github</a>
+ <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a>
+ <a href="https://chaos.social/@jaseg" title="Mastodon">Mastodon</a>
+ </span>
+</nav>
+
+ <header>
+ <h1>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</h1>
+<ul class="breadcrumbs">
+ <li><a href="/">jaseg.de</a></li>
+ <li><a href="/blog/">Blog</a></li><li><a href="/blog/jupyterlab-notebook-file-oneliner/">Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</a></li>
+</ul>
+ <strong>2025-06-29</strong>
+ </header>
+ <main data-pagefind-body>
+ <div class="document">
+
+
+<p>If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems
+chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.</p>
+<pre class="code python literal-block">
+<span class="lineno"></span><span class="line"><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">Path</span><span class="p">(</span><span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">Path</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span><span class="o">.</span><span class="n">read_bytes</span><span class="p">())[</span><span class="s1">'jupyter_session'</span><span class="p">])</span>
+</span></pre>
+<p>The way this works is that for each notebook, jupyter starts a python &quot;kernel&quot; process that actually runs the notebook's
+code. That kernel gets a json file with info on the notebook's location on the disk passed through its command line.
+Since we're running code in that exact python process, we can just grab that json file from sys.argv, and read it
+ourselves.</p>
+</div>
+ </main><footer>
+ Copyright © 2025 Jan Sebastian Götte
+ / <a href="/about/">About</a>
+ / <a href="/imprint/">Imprint</a>
+</footer>
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
+ </script>
+ </body>
+</html>
diff --git a/blog/kicad-mesh-plugin/index.html b/blog/kicad-mesh-plugin/index.html
index 4c0315d..2119817 100644
--- a/blog/kicad-mesh-plugin/index.html
+++ b/blog/kicad-mesh-plugin/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,12 +40,12 @@
</ul>
<strong>2020-08-18</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
-<figure>
-<img src="images/anim.webp" style="max-width: 20em">
+<figure data-pagefind-ignore>
+ <img src="images/anim.webp" style="max-width: 20em">
</figure><div class="section" id="tamper-detection-meshes">
<h2>Tamper Detection Meshes</h2>
<p>Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and
@@ -61,7 +69,7 @@ One of the core cryptographic components in financial applications are smartcard
most countries nowadays. These smartcards contain a small, specialized cryptographic microcontroller that is designed to
be hard to tamper with. Though one of the design goals of the system is to reduce the amount of sensitive information
stored on the card, things such as copying of a card can only be hindered by making the chip hard to read out.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/modern_art.svg" style="max-width: 20em">
</figure><p>With smartcards being the means of choice on one side of the counter in electronic payments, on the other side of the
counter a different technology prevails. Attacks on payment terminals are bound to have much more dire consequences than
@@ -105,14 +113,14 @@ into grid cells that are fully inside the set boundaries. All cells outside or g
this step.</p>
<p>I decided to implement this auto-router in a KiCAD plugin. Though KiCADs plugin API is not the best, it was just about
usable for this task.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/kicad-mesh-outline.png" alt="KiCAD showing an irregular board shape with rounded corners and
indents. In the middle of the board there is a footprint for a 4-pin surface-mount pin header.">
<figcaption>The process starts out with the mesh shape being defined inside KiCAD. The mesh's outline is drawn
onto one of the graphical "Eco" layers. A footprint is placed to serve as a placeholder for the mesh's
connections to the outside world. This footprint is later used as the starting point for the mesh generation
algorithm.</figcaption>
-</figure><figure>
+</figure><figure data-pagefind-ignore>
<img src="images/grid-vis-plain.svg" alt="A vizualization of the grid fitting process. Over the mesh's irregular
outline a grid is drawn. In this picture, all grid cells that are fully inside the grid are shown. Grid cells
that overlap the mesh border are highlighted. Grid cells outside of the mesh border are not drawn.">
@@ -128,7 +136,7 @@ algorithm it yields more or less organized-looking results. Below are five examp
levels of randomness with the cells colored according to their distance from the tree root. 0% randomness means that the
algorithm is going to try cells in forward direction first on every step, and only then try out left and right. 100%
means that on every step, the algorithm is choosing a new direction at random.</p>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/cells-0.svg" alt="a completely organized looking grid with spiral patterns all over.">
<figcaption>0%</figcaption>
@@ -155,34 +163,34 @@ it. The core observation here is that there is only 16 possible ways a cell can
each of which it can either be connected to or not, which results in 2^4 options. If you consider rotations and
mirroring, this works out to rotations or mirrored versions of only six base tiles: The empty tile, a tile with all four
sides connected, a straight through, a 90 degree bend, and a &quot;T&quot;-junction—see the illustration below.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/maze_tiles_plain.svg" style="max-width: 20em">
<figcaption>
There are six possible tile types in our connectivity graph inside its square tiling. This graphic illustrates
all sixteen rotations of these with how they would look in a two-conductor mesh.
</figcaption>
</figure><p>After tiling the grid according to the key above, we get the result below.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/tiles-25-small.svg">
<figcaption>
An auto-routed mesh with traces colored according to tile types.
</figcaption>
-</figure><figure>
+</figure><figure data-pagefind-ignore>
<img src="images/traces-25-small.svg">
<figcaption>
The same mesh, but with traces all black.
</figcaption>
</figure><p>Putting it all together got me the KiCAD plugin you can see in the screenshot below.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/kicad-mesh-settings2.png">
<figcaption>
The plugin settings window open.
</figcaption>
-</figure><figure>
-<img src="images/kicad-mesh-result-large.png">
-<figcaption>
- After runing the plugin, the generated mesh looks like this in pcbnew.
-</figcaption>
+</figure><figure data-pagefind-ignore>
+ <img src="images/kicad-mesh-result-large.png">
+ <figcaption>
+ After runing the plugin, the generated mesh looks like this in pcbnew.
+ </figcaption>
</figure><p>I am fairly happy with the result, but getting there was a medium pain. Especially KiCAD's plugin API is still very
unfinieshed. It is hard to use, most parts are completely undocumented and if you use anything but its most basic parts
things tend to break. One particular pain point for me was that after generating the mesh, the traces have been added to
@@ -194,24 +202,37 @@ making a copy of the board file first and treating mesh generation as a non-reve
.. raw:: html
- <figure>
+ <figure data-pagefind-ignore>
<img src="images/grid-vis-plain.svg" alt="">
<figcaption></figcaption>
</figure> -->
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/led-characterization/index.html b/blog/led-characterization/index.html
index 294b090..96fe4ea 100644
--- a/blog/led-characterization/index.html
+++ b/blog/led-characterization/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2018-05-02</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
@@ -47,7 +55,7 @@ perception and LED peculiarities.</p>
interesting, with up to two RGB or RGBW (red-green-blue-white) LED tapes. For ambient lighting high color resolution was
really important so you could dim it down a lot without flickering. I ended up using the same driver stage I used in the
<a class="reference external" href="http://jaseg.de/blog/multichannel-led-driver/">multichannel LED driver</a> project for its great color resolution and low hardware requirements.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/rgb_cube.svg" alt="An illustration of the RGB color cube.">
<figcaption>An illustration of the RGB color cube.
<a href="https://commons.wikimedia.org/wiki/File:RGB_color_cube.svg">Picture</a> by
@@ -68,7 +76,7 @@ color. <a class="reference external" href="https://en.wikipedia.org/wiki/HSL_and
<em>perceptual</em> color spaces such as <a class="reference external" href="https://en.wikipedia.org/wiki/CIE_1931_color_space">XYZ (CIE 1931)</a> and <a class="reference external" href="https://en.wikipedia.org/wiki/Lab_color_space">CIE Lab/LCh</a> were born, further improving this alignment. In
this mathematical model, mapping a color from one color space into another color space is just a coordinate
transformation.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/hsv_cylinder.png" alt="An illustration of the HSV color space as a cylinder.">
<figcaption>An illustration of the HSV color space as a cylinder.
<a href="https://commons.wikimedia.org/wiki/File:HSV_color_solid_cylinder.png">Picture</a> by
@@ -83,7 +91,7 @@ XYZ. The fat white curve is a projection of the <em>monochromatic spectral locus
XYZ for pure visible wavelengths.</p>
<p>As you can see, sRGB is <em>much</em> smaller than XYZ or even the part within the monochromatic locus that we can perceive. In
particular in the blues and greens we loose <em>a lot</em> of colors to sRGB.</p>
-<figure>
+<figure data-pagefind-ignore>
<video controls loop>
<source src="video/sRGB.mkv" type="video/h264">
<source src="video/sRGB.webm" type="video/webm">
@@ -108,7 +116,7 @@ In practice, the blue channel of my RGB tape to me <em>looks</em> much brighter
may be of a slightly different hue compared to the reference red used in <a class="reference external" href="https://en.wikipedia.org/wiki/SRGB">sRGB</a> which would also skew the RGB color
space.</li>
</ul>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about
100% overshoot during about 70% of the cycle time.">
@@ -160,7 +168,7 @@ specific to the semiconductor used and is quite precise. White LEDs are in fact
and re-emits a broader spectrum of more yellow-ish wavelengths instead. The final LED spectrum is a superposition of
both spectra, with some of the original blue light leaking through the phosphor mixing with the broadband yellow
spectrum of the phosphor.</p>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/spectrograph_step1_parts.jpg">
<figcaption>The ingredients. The cup of coffee and Madoka Magica DVD set are essential to the eventual
@@ -188,7 +196,7 @@ spectrum of the phosphor.</p>
flaw: I wanted to acquire quantitative measurements of brightness across the spectrum. Since I don't have a precise
technical datasheet specifying the spectral response of any of my cameras I can't compare the absolute brightness of
different colors on their pictures. Some other sensor was needed.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/daylight_spectrum_dvd.jpg">
<figcaption>The daylight spectrum as seen using a DVD as a grating.
<a href="https://commons.wikimedia.org/wiki/File:SpectresSolaires-DVD.jpg">Picture</a> by
@@ -219,7 +227,7 @@ rejection and a regular non-inverting amplifier using another op-amp from the sa
transimpedance amplifier output. I put all the passives setting amplifier response (the gain-setting resistors and the
filter resistor and capacitors) on a small removable adapter so I could easily change them if necessary. I put a small
trimpot on the virtual ground both amplifers use as a reference so I could trim that if necessary.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/preamp_schematic.jpg" alt="A drawing of the photodiode preamplifier's schematic">
<figcaption>The photodiode preamplifier schematic. Schematic drawn with an unlicensed copy of
DaveCAD.</figcaption>
@@ -232,7 +240,7 @@ SMD-to-DIP adapter.</p>
<p>Flying-wire construction is just fine for this low-frequency circuit. In a high-speed photodiode preamp, the
transimpedance amplifier circuit would be highly sensitive to stray capacitance, but we're not aiming at high speed
here.</p>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/preamp_front.jpg">
<figcaption>The front side of the preamplifier board.</figcaption>
@@ -258,7 +266,7 @@ this wire does not put too much strain on it.</p>
the linear stage in front of the spectrometer viewing window. A line on the screen paper points to the photodiode die in
parallel to the linear stage allowing precise alignment.</p>
<p>The whole unit with photodiode preamplifier, linear stage, photodiode and stepper motor driver finally looks like this:</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/electronics_whole.jpg" alt="The complete electronics setup of the spectrograph. In the back
there is the DVD drive stepper stage. In front of it, mounted on a piece of wood are a small USB-to-12V
switching-regulator module to power the stepper motor in the top left, below on the bottom left is the
@@ -295,7 +303,7 @@ notebook is capable of live-updating a graph with the in-progress spectrum's dat
check for when I made some mistake easy to spot in the resulting data.</p>
<p>After one color channel is captured, the LED tape has to be manually set to the next color and the next measurement can
begin.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/raw_plot_cheap_rgb.svg" alt="A plot with three wide peaks, two large peaks on both sides and
one smaller one in the middle. The middle one overlaps the two on the sides. The large ones are about 2.5V in
amplitude. Overall, the plot is about 300 stepper steps wide with each peak being around 130 steps wide.">
@@ -324,7 +332,7 @@ estimate of the three colors' peaks' locations and widths.</p>
<p>The photodiode's response is strongly wavelength-dependent. In particular in the blue band, the photodiode's sensitivity
gets very poor down to about 20% at the edge to ultraviolet. This effect is strong enough to move the apparent location
of the blue peak towards red.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/photodiode_sensitivity.svg" alt="A plot of photodiode sensitivity against wavelength relative
to peak sensitivity at 820nm. The sensitivity rises from 20% at 380nm approximately linearly to 80% at 620nm,
then the rise rolls off.">
@@ -338,7 +346,7 @@ using this coarse measurement. Then all three channel peaks are measured in the
estimate is produced by a least-squares fit of a linear function. This fine estimate is then used for a second
sensitivity correction of all original measurements and the scale is changed from stepper motor step count to
wavelength in nanometers.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/processed_plot_cheap_rgb.svg" alt="A plot with three wide peaks, all three of different
heights. The leftmost peak is highest at 6nA, the middle peak lowest at 1.6nA and the rightmost peak in between
at 4nA. The middle one overlaps the two on the sides. Overall, the plot spans about 300nm on its x axis with
@@ -365,7 +373,7 @@ space (colorful) as well as sRGB (white) for comparison plotted within CIE 1931
both so for this illustration the LED color space has been scaled to fit. These figures were made with blender and a few
lines of python. The blender project file including all settings and the python script to generate the color space
models can be found in the <a class="reference external" href="https://github.com/jaseg/led_drv">project repo</a>.</p>
-<figure>
+<figure data-pagefind-ignore>
<video controls loop>
<source src="video/led_within_srgb_scale=1.0.mkv" type="video/h264">
<source src="video/led_within_srgb_scale=1.0.webm" type="video/webm">
@@ -415,17 +423,30 @@ can view the Jupyter notebook most of the analysis above <a class="reference ext
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/multichannel-led-driver/index.html b/blog/multichannel-led-driver/index.html
index 22fa408..0077335 100644
--- a/blog/multichannel-led-driver/index.html
+++ b/blog/multichannel-led-driver/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2018-05-02</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
@@ -64,7 +72,7 @@ and it looks like it's off to your eye. If you turn it off right at the end, it'
super bright to your eye. Now, if you turn it off halfway into the cycle, it's on half the time and it will look to your
eye as half as bright as before. This means that you can control the LED's brightness with only a digital signal and
good timing.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/pwm_schema.jpg" alt="A visualization of PWM at different duty cycles.">
<figcaption>Waveforms of two PWM cycles at different duty cycles.</figcaption>
</figure><p>PWM works great if you have a dedicated PWM output on your microcontroller. It's extremely simple in both hardware and
@@ -104,7 +112,7 @@ channel's duty cycle into chunks the size of these bit periods. The amazingly el
can guess from the name these bit periods are weighted in powers of two. Say the shortest bit period lasts 1
microsecond. Then the second-shortest bit period is 2 microseconds and the third is 4, the fifth 8, the sixth 16 and so
on.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/bcm_schema.jpg" alt="A visualization of BCM at different duty cycles.">
<figcaption>Waveforms of a single 4-bit BCM cycle at different duty cycles. This BCM can produce 16 different
levels.</figcaption>
@@ -139,7 +147,7 @@ drive a couple of amps into the LED tape from the weak outputs of the shift regi
<em>reset</em> inputs. We set the timer to PWM mode so we can generate pulses with precise timing. At the beginning of each
bit period, a pulse will strobe the data for this bit period that we shifted in previously. At the end of the bit
period, one pulse will reset the shift register and one will strobe the freshly-reset zeros into the outputs.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/olsndot_output_schematic.jpg" alt="From left to right, we see the STM32, one of the shift
registers, and the LEDs and MOSFETs. The LED tape is driven to ground by the MOSFETs, which are in turn directly
driven from the shift register outputs. The shift register is wired up to the STM32 with its clock and data
@@ -176,7 +184,7 @@ values. It turned out that our extremely simple LED driving circuit consisting o
driving a MOSFET, which in turn directly drives the LED tape was maybe a little bit too simple. After some measurements
it turned out that we were looking at about 6Vpp of ringing on the driver's output voltage. The picture below is the
voltrage we saw on our oscilloscope on the LED tape.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/driver_ringing_strong.jpg" alt="Strong ringing on the LED voltage waveform edge at about
100% overshoot during about 70% of the cycle time.">
<figcaption>Bad ringing on the LED output voltage caused by wiring inductance. Note that the effect on the
@@ -185,7 +193,7 @@ voltrage we saw on our oscilloscope on the LED tape.</p>
<h4>Dynamic switching behavior: Cause and Effect</h4>
<p>A bit of <a class="reference external" href="http://www.analog.com/en/design-center/design-tools-and-calculators/ltspice-simulator.html">LTSpice</a> action later we found that the inductance of the few metres of cable leading to the LED tape is the
likely culprit. The figure below is the schematic used for the simulations.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/driver_output_ltspice_schematic.jpg" alt="The LTSpice schematic of one output of the driver,
taking into account the shift register's output ESR and the wiring ESL.">
<figcaption>The schematic of the simulation in LTSpice</figcaption>
@@ -193,7 +201,7 @@ likely culprit. The figure below is the schematic used for the simulations.</p>
in full. Combined with the cable inductance, this works out to a considerable lag of the rising edge of the LED
current, and bad ringing on its falling edge. Below is the voltage on the LED output from an LTSpice simulation of our
driver.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/overshoot_sim_r0.svg" alt="The result of the LTSpice simulation of our driver output. The LED
current shows similar ringing to what we measured using the oscilloscope. Interestingly, the gate voltage shows
strong ringing, too.">
@@ -206,14 +214,14 @@ capacitance. The result of this is that the LED current passing the wire's ESL r
series inductance gets excited slightly less, and the overshoot decreases. Below is a picture of the waveform with the
damping resistor in place and a picture of our measurement for comparison. The resistor values don't agree perfectly
since the estimated ESL and stray capacitance of the wiring is probably way off.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/driver_ringing_weak.jpg" alt="Weak ringing on the LED voltage waveform edge at about 30%
overshoot during about 20% of the cycle time.">
<figcaption>Adding a resistor in front of the MOSFET gate to slow the transition damped the ringing somewhat,
but ultimately it cannot be eliminated entirely. Note how you can actually see the miller plateau on the
trailing edge of this signal.
</figcaption>
-</figure><figure>
+</figure><figure data-pagefind-ignore>
<img src="images/overshoot_sim_r100.svg" alt="The result of the LTSpice simulation of our driver output with an
extra 100 Ohms between shift register output and MOSFET gate. Similar to the oscilloscope measurement the
ringing is much reduced in its amplitude.">
@@ -224,13 +232,13 @@ shift register's output at very small duty cycles (1µs or less). This is caused
plateau</a>. For illustration, below is a graph of both the excitation waveform (the boxy line) and the resulting LED
current (the other ones) both without damping (top) and with 220Ω damping (bottom). As you can see the effective duty
cycle of the LED current is not at all equal to the 50% duty cycle of the excitation square wave.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/asymmetric_iled.svg" alt="The result of an LTSpice simulation of the LED duty cycle without and
with damping. Dampening widens the LED current waveform from 50% duty cycle with sharp edges to about 80% duty
cycle with soft edges.">
<figcaption>Simulated LED duty cycle with and without damping. The damping resistance used in this simulation
was 220Ω.</figcaption>
-</figure><figure>
+</figure><figure data-pagefind-ignore>
<img src="images/asymmetric_vgate.svg" alt="The gate voltages in the spice simulation above. The undamped
response shows sharp edges with the miller plateau being a barely noticeable step, but with strong ringing on
the trailing edge. The damped response shows RC-like slow-edges, but has wide miller plateaus on both edges
@@ -261,7 +269,7 @@ calibration, the LED driver is set to enable each single BCM period in turn, i.e
shielding against both stray light and electromagnetic interference and a photodiode looking at the LED tape. We used
the venerable <a class="reference external" href="http://www.vishay.com/docs/81521/bpw34.pdf">BPW34</a> photodiode in our setup as I had a bunch leftover from another project and because they are quite
sensitive owing to their physically large die area.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/linearization_setup.jpg" alt="The led measurement setup consists of several PCBs and a
breadboard linked with a bunch of wires and a big tin can to shield the LEDs and the photodiode. A large sub-D
connector is put into the top of the tin can as a feed-through for the LED tape's control signals and the
@@ -283,7 +291,7 @@ ADC chips I had in my parts bin.</p>
measurement at each step. Later on, these measurements can be plotted to visualize the resulting slope's linearity, and
we can even do a simulation of the resulting brightness for all possible control values by just adding the measured
photocurrents for a certain BCM setpoint just as our retinas would do.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/driver_linearity_raw.svg" alt="">
<figcaption>
A plot of the measured brightness of our LED tape for each BCM period. The brightness values are normalized
@@ -298,7 +306,7 @@ photocurrents for a certain BCM setpoint just as our retinas would do.</p>
</figure><p>While it would be possible to fully automate the optimization of BCM driver lookup tables, we needed only one and in the
end I just sat down and manually tweaked the ideal values we initially calculated until I liked the result. You can see
the resulting brightness curve below.</p>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/uncorrected_brightness_sim.svg" alt="">
<figcaption>
@@ -344,7 +352,7 @@ and control their outputs.</p>
</div>
<div class="section" id="conclusion">
<h3>Conclusion</h3>
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<a href="images/olsndot_schematic.png">
<img src="images/olsndot_schematic.png" alt="A picture of the LED driver schematic">
@@ -368,17 +376,30 @@ analyze the brightness measurement data <a class="reference external" href="http
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/private-contact-discovery/index.html b/blog/private-contact-discovery/index.html
index cbb3814..0ad3659 100644
--- a/blog/private-contact-discovery/index.html
+++ b/blog/private-contact-discovery/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,9 +40,9 @@
</ul>
<strong>2019-06-22</strong>
</header>
- <main>
- <div class="document" id="private-contact-discovery">
-<h1 class="title">Private Contact Discovery</h1>
+ <main data-pagefind-body>
+ <div class="document">
+
<p>Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on
installation: Given a user's address book, find out which of their contacts also use the same messenger without the
@@ -64,17 +72,30 @@ users benefit from improved privacy, but your company might be able to avoid a b
accountability issues by simply not producing as much sensitive data in the first place.</p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/serial-protocols/index.html b/blog/serial-protocols/index.html
index c5e2fd0..562b451 100644
--- a/blog/serial-protocols/index.html
+++ b/blog/serial-protocols/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2018-05-19</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
@@ -235,17 +243,30 @@ want to set a large framebuffer in pieces, do it in a <a class="reference extern
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/telekom-gpon-sfp/index.html b/blog/telekom-gpon-sfp/index.html
index e077a44..1351707 100644
--- a/blog/telekom-gpon-sfp/index.html
+++ b/blog/telekom-gpon-sfp/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2022-02-21</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
@@ -116,7 +124,7 @@ this, configure the eth5 interface (which is the SFP port) to use the static IP
<figure style="width: 20em">
<a href="images/edgerouter_sfp_config.png">
<img src="images/edgerouter_sfp_config.png" alt="The EdgeRouter's graphical configuration interface showing IP
- address 10.10.1.2/24 being configured for interface eth5, which is the SFP interface.">
+ address 10.10.1.2/24 being configured for interface eth5, which is the SFP interface." data-pagefind-ignore>
</a>
<figcaption>SFP interface configuration to access the SFP ONU from a laptop connected to the EdgeRouter's LAN
port</figcaption>
@@ -129,7 +137,7 @@ protocol, with destination address <tt class="docutils literal">10.10.1.0/24</tt
<a href="images/edgerouter_snat_config.png">
<img src="images/edgerouter_snat_config.png" alt="The EdgeRouter's graphical configuration interface showing a
source NAT being configured for interface eth5 for TCP protocol connections to destination address 10.10.1.1
- using masquerading.">
+ using masquerading." data-pagefind-ignore>
</a>
<figcaption>Source NAT configuration to access the SFP ONU from LAN. eth5, masquerading on, TCP, destination
10.10.1.1 (the SFP ONU's IP).</figcaption>
@@ -146,7 +154,7 @@ default login credentials for the device are admin/1234.</p>
<a href="images/sfp_onu_web_if.png">
<img src="images/sfp_onu_web_if.png" alt="The SFP ONU configuration web interface is a basic-looking website with
a big Zyxel logo on it. It has menu options named status, setup and management. It shows a system overview
- page that lists the device's uptime and software version.">
+ page that lists the device's uptime and software version." data-pagefind-ignore>
</a>
<figcaption>The SFP ONU's web interface.</figcaption>
</figure></div>
@@ -161,7 +169,7 @@ on this page.</p>
<a href="images/sfp_onu_ploam_pw_config.png">
<img src="images/sfp_onu_ploam_pw_config.png" alt="The SFP ONU configuration web interface shows its SLID
configuration page. A text field labelled SLID asks the user to enter a value of at most ten characters. As
- an example, abcdefg123 is listed.">
+ an example, abcdefg123 is listed." data-pagefind-ignore>
</a>
<figcaption>The SFP ONU's config interface to set SLID/PLOAM PW/ONT-Installationskennung.</figcaption>
</figure><p>Press &quot;Save Config&quot; on the top right of the web page, then select &quot;Reset ONU&quot; and click &quot;Apply&quot; under the &quot;Reset ONU&quot;
@@ -170,7 +178,7 @@ link on the left. Make sure to not select the factory reset option instead.</p>
<a href="images/sfp_onu_reset.png">
<img src="images/sfp_onu_reset.png" alt="The SFP ONU configuration web interface shows its reset ONU page. There
are two options labelled Reset ONU and Reset to factory default settings. The reset ONU option is
- selected.">
+ selected." data-pagefind-ignore>
</a>
<figcaption>Rebooting the SFP ONU.</figcaption>
</figure><p>With the ONU configured, after the reset the &quot;GPON Information&quot; page from the left menu under &quot;Status&quot; from the top menu
@@ -210,17 +218,30 @@ collected <a class="reference external" href="https://github.com/xvzf/zyxel-gpon
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/thors-hammer/index.html b/blog/thors-hammer/index.html
index d012aad..366194d 100644
--- a/blog/thors-hammer/index.html
+++ b/blog/thors-hammer/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,14 +40,14 @@
</ul>
<strong>2018-05-03</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
<p>In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the
shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer
for <a class="reference external" href="https://en.wikipedia.org/wiki/PS/2_port">PS/2</a> keyboards.</p>
-<figure>
+<figure data-pagefind-ignore>
<video controls loop>
<source src="video/thors_hammer.mov" type="video/h264">
<source src="video/thors_hammer.webm" type="video/webm">
@@ -56,13 +64,13 @@ want to send data. In case of a keyboard that's the case when a key is pressed o
LED state, otherwise the clock line is silent. We ignore the LED activity for now as it's generally coupled to key
presses. By just triggering an NE555 configured as astable flipflop we can stretch each train of clock pulses to a
pulse a few tens of milliseconds long that is enough to actuate the solenoid.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/thors_hammer_schematic.jpg" alt="The schematic of the PS2 driver">
<figcaption>The schematic of the driver stretching the PS/2 clock pulses to drive the solenoid.</figcaption>
</figure><p>Since PS/2 sends each key press and key release separately this circuit will pulse twice per keystroke. It would be
possible to ignore one of them but I figure the added noise just adds to the experience.</p>
<p>Built on a breadboard, the circuit looks like this.</p>
-<figure>
+<figure data-pagefind-ignore>
<img src="images/thors_hammer_breadboard.jpg" alt="The circuit built on a breadboard">
<figcaption>The completed circuit built up on a breadboard and attached to a keyboard.</figcaption>
</figure><p>Since my solenoid did not have a tensioning spring I used a rubber band and some vinyl tape to make an adjustable
@@ -72,17 +80,30 @@ excitation duration using the potentiometer. My particular solenoid was a bit sl
board as shims between the plunger and the case to limit the plunger's travel inside the solenoid core.</p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/wifi-led-driver/index.html b/blog/wifi-led-driver/index.html
index 7df1f28..88a4b62 100644
--- a/blog/wifi-led-driver/index.html
+++ b/blog/wifi-led-driver/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,14 +40,14 @@
</ul>
<strong>2018-05-02</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="document">
<div class="section" id="project-motivation">
<h2>Project motivation</h2>
<!-- FIXME finished project picture with LED tape -->
-<figure>
+<figure data-pagefind-ignore>
<img src="images/board_in_case.small.jpg">
<figcaption>The completed driver board installed in the 3D-printed case. This device can now be connected to
12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin
@@ -67,7 +75,7 @@ a small <a class="reference external" href="https://en.wikipedia.org/wiki/Surfac
currents their <a class="reference external" href="http://m.littelfuse.com/~/media/electronics/datasheets/resettable_ptcs/littelfuse_ptc_16r_datasheet.pdf.pdf">trip times get long enough that they become unlikely to trip in time to save anything</a>, so plain old non-resettable fuses would be the way to go there.</p>
<!-- FIXME finished board photos -->
<!-- FIXME board with test tape picture -->
-<div class="subfigure">
+<div class="subfigure" data-pagefind-ignore>
<figure>
<img src="images/schematic.png">
<figcaption>
@@ -87,7 +95,7 @@ currents their <a class="reference external" href="http://m.littelfuse.com/~/med
<a href="resource/schematic_and_pcb.pdf">Download PDF</a>
</figcaption>
</figure>
-</div><figure>
+</div><figure data-pagefind-ignore>
<img src="images/boards.small.jpg">
<figcaption>The completed PCBs of this project (front) and the `multichannel LED driver`_ project the driver
circuitry was derived from (back).
@@ -130,17 +138,30 @@ violence.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/blog/wsdiff-static-html-diffs/index.html b/blog/wsdiff-static-html-diffs/index.html
new file mode 100644
index 0000000..5bccd82
--- /dev/null
+++ b/blog/wsdiff-static-html-diffs/index.html
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<html><head>
+ <meta charset="utf-8">
+ <title>wsdiff: Responsive diffs in plain HTML | Home</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="color-scheme" content="dark light">
+ <link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
+</head>
+<body><nav>
+ <div class="internal">
+
+ <a href="/" title="Home">Home</a>
+ <a href="/blog/" title="Blog">Blog</a>
+ <a href="/projects/" title="Projects">Projects</a>
+ <a href="/about/" title="About">About</a>
+ </div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
+ <div class="external">
+ <a href="https://git.jaseg.de/" title="cgit">cgit</a>
+ <a href="https://github.com/jaseg" title="Github">Github</a>
+ <a href="https://gitlab.com/neinseg" title="Gitlab">Gitlab</a>
+ <a href="https://chaos.social/@jaseg" title="Mastodon">Mastodon</a>
+ </span>
+</nav>
+
+ <header>
+ <h1>wsdiff: Responsive diffs in plain HTML</h1>
+<ul class="breadcrumbs">
+ <li><a href="/">jaseg.de</a></li>
+ <li><a href="/blog/">Blog</a></li><li><a href="/blog/wsdiff-static-html-diffs/">wsdiff: Responsive diffs in plain HTML</a></li>
+</ul>
+ <strong>2025-07-25</strong>
+ </header>
+ <main data-pagefind-body>
+ <div class="document">
+
+
+<div class="section" id="demo">
+<h2>Demo</h2>
+<p>First off, have a demo. Because of the width of this page, the output will show an unified diff. To try out the split
+diff layout, make sure your browser window is wide enough and open the demo in a separate tab using <a class="reference external" href="/wsdiff-example.html">this link</a>.</p>
+<p>wsdiff supports dark mode, try it out by toggling dark mode in your operating system!</p>
+<iframe src="/wsdiff-example.html" style="width: 100%; height: 30em; border: 1px #d0d0d0 solid" id="wsdiff example diff"></iframe></div>
+<div class="section" id="core-features">
+<h2>Core Features</h2>
+<p>There's many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed
+this by publishing <a class="reference external" href="https://pypi.org/project/wsdiff/">wsdiff</a>, a diffing tool written in Python that produces diffs
+as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch
+between unified and split diffs based on screen size using only CSS.</p>
+<div class="section" id="responsive-line-wrapping">
+<h3>Responsive Line Wrapping</h3>
+<p>The first challenge I solved was wrapping source code lines to match the available screen space. Other tools often just
+show horizontal scroll bars, which is an okay workaround when you're mostly working with hard-wrapped source code on a
+laptop or desktop screen, but which results in catastrophic UX on any phone.</p>
+<p>I solved line breaking with a combination of CSS-controlled, web-standard word breaking rules: <tt class="docutils literal"><span class="pre">overflow-wrap:</span>
+anywhere</tt> for source code (<a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-wrap">MDN link</a>) and
+<tt class="docutils literal"><span class="pre">white-space:</span> <span class="pre">pre-wrap</span></tt> to preserve whitespace accurately (<a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/white-space">MDN link</a>). To make both sides of the split diff align, and to
+align line numbers with wrapped source code lines, the diff is laid out using a <a class="reference external" href="https://css-tricks.com/snippets/css/complete-guide-grid/">CSS grid layout</a>. In side-by-side
+view, the layout has four columns: two for line numbers and two for the content. In unified view, the left (&quot;old&quot;)
+content column is dropped, and the deleted or modified lines that are highlighted in it in side-by-side view are slotted
+into the remaining right column.</p>
+<p>When soft-wrapping source code, text editors will often display a little curved arrow marker to indicate that a line was
+soft-wrapped, and that there is not actually a newline character in the file at that location. wsdiff solves this
+using the same technique I used for the soft-wrapping code blocks in this blog, described <a class="reference external" href="http://jaseg.de/blog/css-only-code-blocks/">here</a>. It inserts a string of <tt class="docutils literal"><span class="pre">&quot;\a↳\a↳\a↳\a↳\a↳...&quot;</span></tt> into the line number
+element's <tt class="docutils literal">::after</tt> pseudo-element. This string evaluates to a sequence of unicode arrows separated by line breaks,
+and starting with an empty line. The <tt class="docutils literal">::after</tt> pseudo-element is positioned using <tt class="docutils literal">position: absolute</tt>, and the
+parent <tt class="docutils literal">&lt;span <span class="pre">class=&quot;lineno&quot;&gt;</span></tt> has <tt class="docutils literal">position: relative</tt> set. This way, the arrow pseudo-element gets placed on top
+of the lineno span without affecting the layout at all. By setting <tt class="docutils literal">overflow: clip</tt> on the parent <tt class="docutils literal">&lt;span
+<span class="pre">class=&quot;lineno&quot;&gt;</span></tt>, the arrow pseudo-element gets cut off vertically wherever the parent line number element naturally
+ends. Since both the line and the line number element share a grid row, the line number element always matches the
+height of the soft-wrapped line.</p>
+</div>
+<div class="section" id="responsive-split-unified-layout-selection">
+<h3>Responsive Split/Unified Layout Selection</h3>
+<p>To dynamically change between unified and side-by-side views, wsdiff uses a web-standard <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries">Media Query</a>. By default, the
+page is laid out for side-by-side view. In the HTML source, the diff is listed as it is displayed in side-by-side view,
+with the old and new lines along with their line numbers interleaved.</p>
+<p>The magic happens when the media query gets triggered by a narrow screen width. The media query re-adjusts the layout in
+four core steps:</p>
+<blockquote>
+<ol class="arabic simple">
+<li>All unchanged lines in the left (old) column are hidden.</li>
+<li>The left content column of the grid layout is hidden, so that now there are three columns: old line number, new line
+number, and unified content.</li>
+<li>All deleted or changed lines from the left (old) column are re-located to the right column. They naturally slot in
+in the right spot because they already appear in the right order in the HTML source.</li>
+<li>By slotting in the old lines in the right column, we have created gaps in the line number columns. Every deleted
+line has an empty cell in the new line number column, and every inserted line has one in the old line number column.
+The CSS adjusts the layout of these empty cells such that the border lines align nicely, and it overrides the
+newline markers so that they only show in the right (new) line number column, not both.</li>
+</ol>
+</blockquote>
+<p>Since this is all CSS, it happens automatically and near-instantly. Since it is using only web standard features, it
+works across browsers and platforms.</p>
+</div>
+<div class="section" id="unchanged-line-folding-in-css">
+<h3>Unchanged Line Folding in CSS</h3>
+<p>When showing the diff of a large file, it is usually best to hide large runs of unchanged lines. wsdiff does this
+similar to code folding in text editors. When a long run of unchanged lines is detected, a marker is placed spanning the
+diff. This marker contains a checkbox that can be toggled to hide the unchanged lines. This feature is done completely
+in CSS using a <tt class="docutils literal"><span class="pre">:has(input[type=&quot;checkbox&quot;]:checked)</span></tt> selector.</p>
+<p>The actual mechanics are quite simple. To cleanly hide the lines, they must be placed in a container <tt class="docutils literal">&lt;div&gt;</tt>. That div
+has a CSS subgrid layout using <tt class="docutils literal">display: grid; <span class="pre">grid-template-columns:</span> subgrid;</tt>, meaning that its contents align to
+the surrounding diff grid.</p>
+</div>
+<div class="section" id="dark-mode">
+<h3>Dark Mode</h3>
+<p>Integrating a website with the OS-level dark mode is surprisingly easy. All you need is a <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries">Media Query</a> that selects
+for <tt class="docutils literal">&#64;media <span class="pre">(prefers-color-scheme:</span> dark)</tt> and you're good. wsdiff uses named colors using <a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties">CSS Custom Properties</a>, so
+the actual dark mode media query only needs to override these color properties, and the rest of the CSS will be
+re-computed automatically.</p>
+</div>
+<div class="section" id="limitations-text-selection">
+<h3>Limitations: Text selection</h3>
+<p>A limitation in having a combined, single HTML source for both side-by-side and unified diffs is that text selection
+only works naturally in either mode. You can't make text selection work in both simultaneously without re-sorting the
+lines in the HTML source, since there is no way to override the text selection order from pure CSS. In wsdiff, I worked
+around this issue by just disabling text selection on the unchanged lines in the left (old) column, so selecting text in
+the right column copies the unified diff as one would expect.</p>
+</div>
+</div>
+<div class="section" id="try-it-yourself">
+<h2>Try it yourself!</h2>
+<p>You can find the demo from above at <a class="reference external" href="/wsdiff-example.html">this link</a>.</p>
+<p>You can install wsdiff yourself <a class="reference external" href="https://pypi.org/project/wsdiff/">from PyPI</a>:</p>
+<pre class="code sh literal-block">
+<span class="lineno"></span><span class="line">$<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>-U<span class="w"> </span>wsdiff<span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span>Successfully<span class="w"> </span>installed<span class="w"> </span>wsdiff-0.3.1<span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span>$<span class="w"> </span>wsdiff<span class="w"> </span>old.py<span class="w"> </span>new.py<span class="w"> </span>-o<span class="w"> </span>diff.html
+</span></pre>
+</div>
+</div>
+ </main><footer>
+ Copyright © 2025 Jan Sebastian Götte
+ / <a href="/about/">About</a>
+ / <a href="/imprint/">Imprint</a>
+</footer>
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
+ </script>
+ </body>
+</html>
diff --git a/categories/index.html b/categories/index.html
index c49ae1e..cf9cfdd 100644
--- a/categories/index.html
+++ b/categories/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -33,17 +41,30 @@
</header>
<main class="cards">
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/categories/index.xml b/categories/index.xml
index 23e2595..02ee00a 100644
--- a/categories/index.xml
+++ b/categories/index.xml
@@ -4,7 +4,7 @@
<title>Categories on Home</title>
<link>http://jaseg.de/categories/</link>
<description>Recent content in Categories on Home</description>
- <generator>Hugo -- gohugo.io</generator>
+ <generator>Hugo</generator>
<language>en-us</language>
<copyright>Jan Sebastian Götte</copyright>
<atom:link href="http://jaseg.de/categories/index.xml" rel="self" type="application/rss+xml" />
diff --git a/imprint/index.html b/imprint/index.html
index 6361881..2eba271 100644
--- a/imprint/index.html
+++ b/imprint/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -55,7 +63,7 @@ Dieses Werk ist lizenziert unter einer
</a>.<br/>
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">
<img alt="Creative Commons Lizenzvertrag" style="border-width:0"
- src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" />
+ src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" data-pagefind-ignore/>
</a></div>
<div class="section" id="haftungsbeschrankung-fur-inhalte-der-website">
<h2>Haftungsbeschränkung für Inhalte der Website</h2>
@@ -114,17 +122,30 @@ Schritten verfolgt.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/index.html b/index.html
index 92b8e58..385a208 100644
--- a/index.html
+++ b/index.html
@@ -1,6 +1,6 @@
<!DOCTYPE html>
<html><head>
- <meta name="generator" content="Hugo 0.120.1">
+ <meta name="generator" content="Hugo 0.147.8">
<meta charset="utf-8">
<title>Home</title>
<meta name="description" content="">
@@ -8,6 +8,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -17,6 +22,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -39,7 +47,10 @@ projects on the projects page. On the top right of this page, there are links to
pages. If you want to learn more about me, head over to the about page.</p>
</div>
</div>
+
+
<h2>Recently updated projects</h2>
+
<div class="card"><h3><a href="/projects/kimesh/">KiMesh</a></h3>
<div class="summary">
@@ -57,6 +68,7 @@ pages. If you want to learn more about me, head over to the about page.</p>
</div>
</div>
+
<div class="card"><h3><a href="/projects/8seg/">8seg</a></h3>
<div class="summary">
@@ -74,110 +86,185 @@ pages. If you want to learn more about me, head over to the about page.</p>
</div>
</div>
+
<div class="pagination-links">
+
<a href="/projects/">See more<span class="arrow-right"></span></a>
+
</div>
+
<h2>Blog</h2>
- <div class="card"><h3><a href="/blog/8seg/">8seg</a></h3><strong>2023-12-26</strong>
+
+ <div class="card"><h3><a href="/blog/wsdiff-static-html-diffs/">wsdiff: Responsive diffs in plain HTML</a></h3><strong>2025-07-25</strong>
<div class="summary">
- 8seg Technical Overview Prologue German hacker culture has this intense love for things that light up in colorful ways. Like for many others in this community, I have always been fascinated by LEDs. One of the first things on my pile of unfinished projects was to build my own LED matrix and use it to display text. When I started that project, I was still new to electronics. Back then, commercial LED matrices were limited to red or green color only, and were very expensive, so there was an incentive to build your own.
- <a href="http://jaseg.de/blog/8seg/">Read more</a>
+ <div class="document">
+
+
+<p>There's many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed this by publishing wsdiff, a diffing tool written in Python that produces diffs as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch between unified and split diffs based on screen size using only CSS.</p>
+</div>
+ <a href="http://jaseg.de/blog/wsdiff-static-html-diffs/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></h3><strong>2022-02-21</strong>
+
+ <div class="card"><h3><a href="/blog/css-only-code-blocks/">Code listings with nice line wrapping and line numbers from plain CSS</a></h3><strong>2025-07-23</strong>
<div class="summary">
- Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.
- <a href="http://jaseg.de/blog/telekom-gpon-sfp/">Read more</a>
+ <div class="document">
+
+
+<p>Code listings in web pages are often a bit of a pain to use. Usually, they don't wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.</p>
+</div>
+ <a href="http://jaseg.de/blog/css-only-code-blocks/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></h3><strong>2021-11-23</strong>
+
+ <div class="card"><h3><a href="/blog/jupyterlab-notebook-file-oneliner/">Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</a></h3><strong>2025-06-29</strong>
<div class="summary">
- World's First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.
- <a href="http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/">Read more</a>
+ <div class="document">
+
+
+<p>If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.</p>
+</div>
+ <a href="http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></h3><strong>2020-08-18</strong>
+
+ <div class="card"><h3><a href="/blog/8seg/">8seg Technical Overview</a></h3><strong>2023-12-26</strong>
<div class="summary">
- Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.
- <a href="http://jaseg.de/blog/kicad-mesh-plugin/">Read more</a>
+ <div class="document">
+
+
+<p>8seg is a large-scale LED light art installation that displays text on a 1.5 meter high, 30 meter wide 8-segment display made from cheap LED tape.</p>
+</div>
+ <a href="http://jaseg.de/blog/8seg/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></h3><strong>2019-06-22</strong>
+
+ <div class="card"><h3><a href="/blog/telekom-gpon-sfp/">Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</a></h3><strong>2022-02-21</strong>
<div class="summary">
- Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user's address book, find out which of their contacts also use the same messenger without the messenger's servers learning anything about the user's address book. The widespread non-private way to do this is to simply upload the user's address book to the app's operator's servers and do an SQL JOIN keyed on the phone number field against the database of registered users.
- <a href="http://jaseg.de/blog/private-contact-discovery/">Read more</a>
+ <div class="document">
+
+
+<p>Short tutorial on getting a Deutsche Telekom GPON internet connection running using a SFP ONU unit in an Ubiquiti EdgeRouter.</p>
+</div>
+ <a href="http://jaseg.de/blog/telekom-gpon-sfp/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></h3><strong>2019-05-17</strong>
+
+ <div class="card"><h3><a href="/blog/ihsm-worlds-first-diy-hsm/">New Paper on Inertial Hardware Security Modules</a></h3><strong>2021-11-23</strong>
<div class="summary">
- Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori's security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.
- <a href="http://jaseg.de/blog/hsm-basics/">Read more</a>
+ <div class="document">
+
+
+<p>Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any single-board computer using a moving tamper-sensing mesh made from cheap PCBs.</p>
+</div>
+ <a href="http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></h3><strong>2018-05-19</strong>
+
+ <div class="card"><h3><a href="/blog/kicad-mesh-plugin/">Kicad Mesh Plugin</a></h3><strong>2020-08-18</strong>
<div class="summary">
- Scroll to the end for the TL;DR.
-In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions.
-If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.
- <a href="http://jaseg.de/blog/serial-protocols/">Read more</a>
+ <div class="document">
+
+
+<p>I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can route around existing footprints and traces on the PCB.</p>
+</div>
+ <a href="http://jaseg.de/blog/kicad-mesh-plugin/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/thors-hammer/">Thor&#39;s Hammer</a></h3><strong>2018-05-03</strong>
+
+ <div class="card"><h3><a href="/blog/private-contact-discovery/">Private Contact Discovery</a></h3><strong>2019-06-22</strong>
<div class="summary">
- In case you were having an inferiority complex because your friends' IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here's the solution: Thor's Hammer, a simple typing cadence enhancer for PS/2 keyboards.
-Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard's PS/2 clock line and briefly actuates a large solenoid on each key press.
- <a href="http://jaseg.de/blog/thors-hammer/">Read more</a>
+ <div class="document">
+
+
+<p>I gave a short introduction into Private Contact Discovery protocols at our university workgroup.</p>
+</div>
+ <a href="http://jaseg.de/blog/private-contact-discovery/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/multichannel-led-driver/">32-Channel LED tape driver</a></h3><strong>2018-05-02</strong>
+
+ <div class="card"><h3><a href="/blog/hsm-basics/">Hardware Security Module Basics</a></h3><strong>2019-05-17</strong>
<div class="summary">
- Theoretical basics Together, a friend and I outfitted the small staircase at Berlin's Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.
- <a href="http://jaseg.de/blog/multichannel-led-driver/">Read more</a>
+ <div class="document">
+
+
+<p>I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on interesting research directions.</p>
+</div>
+ <a href="http://jaseg.de/blog/hsm-basics/">Read more</a>
</div>
</div>
- <div class="card"><h3><a href="/blog/wifi-led-driver/">Wifi Led Driver</a></h3><strong>2018-05-02</strong>
+
+ <div class="card"><h3><a href="/blog/serial-protocols/">How to talk to your microcontroller over serial</a></h3><strong>2018-05-19</strong>
<div class="summary">
- Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.
- <a href="http://jaseg.de/blog/wifi-led-driver/">Read more</a>
+ <div class="document">
+
+
+<p>Scroll to the end for the <a class="reference internal" href="#conclusion">TL;DR</a>.</p>
+<p>In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will
+summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error
+conditions.</p>
+<p>If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to
+another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and
+quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into
+your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to
+set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.</p></div>
+ <a href="http://jaseg.de/blog/serial-protocols/">Read more</a>
</div>
</div>
+
<div class="pagination-links">
+
<a href="/blog/">See more<span class="arrow-right"></span></a>
+
</div>
+
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/index.xml b/index.xml
index 8c898c7..72cf757 100644
--- a/index.xml
+++ b/index.xml
@@ -4,193 +4,178 @@
<title>jaseg.de on Home</title>
<link>http://jaseg.de/</link>
<description>Recent content in jaseg.de on Home</description>
- <generator>Hugo -- gohugo.io</generator>
+ <generator>Hugo</generator>
<language>en-us</language>
<copyright>Jan Sebastian Götte</copyright>
- <lastBuildDate>Tue, 26 Dec 2023 15:26:00 +0100</lastBuildDate>
+ <lastBuildDate>Fri, 25 Jul 2025 23:42:00 +0100</lastBuildDate>
<atom:link href="http://jaseg.de/index.xml" rel="self" type="application/rss+xml" />
<item>
- <title>8seg</title>
+ <title>wsdiff: Responsive diffs in plain HTML</title>
+ <link>http://jaseg.de/blog/wsdiff-static-html-diffs/</link>
+ <pubDate>Fri, 25 Jul 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/wsdiff-static-html-diffs/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;There&#39;s many tools that render diffs on the web, but almost none that work well on small screens such as phones. I fixed this by publishing wsdiff, a diffing tool written in Python that produces diffs as beautiful, responsive, static, self-contained HTML pages. wsdiffs wrap text to fit the window, and dynamically switch between unified and split diffs based on screen size using only CSS.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>Code listings with nice line wrapping and line numbers from plain CSS</title>
+ <link>http://jaseg.de/blog/css-only-code-blocks/</link>
+ <pubDate>Wed, 23 Jul 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/css-only-code-blocks/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Code listings in web pages are often a bit of a pain to use. Usually, they don&#39;t wrap on small screens. Also, copy-pasting code from a code listing often copies the line numbers along with the code. Finally, many implementations use heavyweight HTML and/or javascript, making them slow to render. For this blog, I wrote a little CSS hack that renders nice, wrapping code blocks with line continuation markers in plain CSS without any JS.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>Getting the .ipynb Notebook File Location From a Running Jupyter Lab Notebook</title>
+ <link>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</link>
+ <pubDate>Sun, 29 Jun 2025 23:42:00 +0100</pubDate>
+ <guid>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</guid>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;If you need to get the path of the ipynb file in a running #Jupyter notebook, this one-liner will do the trick. It seems chatgpt is confused, and a bunch of other approaches on the web look fragile and/or unnecessarily complex to me.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
+ </item>
+ <item>
+ <title>8seg Technical Overview</title>
<link>http://jaseg.de/blog/8seg/</link>
<pubDate>Tue, 26 Dec 2023 15:26:00 +0100</pubDate>
<guid>http://jaseg.de/blog/8seg/</guid>
- <description>8seg Technical Overview Prologue German hacker culture has this intense love for things that light up in colorful ways. Like for many others in this community, I have always been fascinated by LEDs. One of the first things on my pile of unfinished projects was to build my own LED matrix and use it to display text. When I started that project, I was still new to electronics. Back then, commercial LED matrices were limited to red or green color only, and were very expensive, so there was an incentive to build your own.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;8seg is a large-scale LED light art installation that displays text on a 1.5 meter high, 30 meter wide 8-segment display made from cheap LED tape.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>KiMesh</title>
<link>http://jaseg.de/projects/kimesh/</link>
<pubDate>Wed, 04 Oct 2023 23:42:00 +0200</pubDate>
<guid>http://jaseg.de/projects/kimesh/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Ubiquiti EdgeRouter on Deutsche Telekom GPON Fiber</title>
<link>http://jaseg.de/blog/telekom-gpon-sfp/</link>
<pubDate>Mon, 21 Feb 2022 20:00:00 +0100</pubDate>
<guid>http://jaseg.de/blog/telekom-gpon-sfp/</guid>
- <description>Disclaimer I provide this guide as a reference for other knowledgeable users without any warranty. Please feel free to use this as a resource but do not hold me responsible if this does not work for you. There is a significant chance that due to an error on my side or due to Telekom changing their setup this guide will not work for you, and you may end up having to pay for an unsuccessful Telekom technician visit.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Short tutorial on getting a Deutsche Telekom GPON internet connection running using a SFP ONU unit in an Ubiquiti EdgeRouter.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>New Paper on Inertial Hardware Security Modules</title>
<link>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</link>
<pubDate>Tue, 23 Nov 2021 23:42:20 +0100</pubDate>
<guid>http://jaseg.de/blog/ihsm-worlds-first-diy-hsm/</guid>
- <description>World&#39;s First DIY HSM Last week, Prof. Dr. Björn Scheuermann and I have published our first joint paper on Hardware Security Modules. In our paper, we introduce Inertial Hardware Security Modules (IHSMs), a new way of building high-security HSMs from basic components. I think the technology we demonstrate in our paper might allow some neat applications where some civil organization deploys a service that no one, not even they themselves, can snoop on.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Paper announcement: We have published a paper on how you can DIY a tamper-sensing hardware security module from any single-board computer using a moving tamper-sensing mesh made from cheap PCBs.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Kicad Mesh Plugin</title>
<link>http://jaseg.de/blog/kicad-mesh-plugin/</link>
<pubDate>Tue, 18 Aug 2020 13:15:39 +0200</pubDate>
<guid>http://jaseg.de/blog/kicad-mesh-plugin/</guid>
- <description>Tamper Detection Meshes Cryptography is at the foundation of our modern, networked world. From email to card payment infrastructure in brick and mortar stores, cryptographic keys secure almost every part of our digital lives againts cybercriminals or curious surveillance capitalists. Without cryptography, many of the things we routinely do in our lives such as paying for groceries with a credit card, messaging a friend on Signal or unlocking a car with its keyfob would not be possible.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I wrote a little KiCad plugin that you can use to create security meshes, heaters and other things where you need one or more traces cover the entire surface of a PCB. The plugin supports arbitrary PCB shapes, cutouts, and can route around existing footprints and traces on the PCB.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Private Contact Discovery</title>
<link>http://jaseg.de/blog/private-contact-discovery/</link>
<pubDate>Sat, 22 Jun 2019 10:30:00 +0800</pubDate>
<guid>http://jaseg.de/blog/private-contact-discovery/</guid>
- <description>Private Contact Discovery Private Contact Discovery (PCD) is the formal name for the problem modern smartphone messenger applications have on installation: Given a user&#39;s address book, find out which of their contacts also use the same messenger without the messenger&#39;s servers learning anything about the user&#39;s address book. The widespread non-private way to do this is to simply upload the user&#39;s address book to the app&#39;s operator&#39;s servers and do an SQL JOIN keyed on the phone number field against the database of registered users.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I gave a short introduction into Private Contact Discovery protocols at our university workgroup.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Hardware Security Module Basics</title>
<link>http://jaseg.de/blog/hsm-basics/</link>
<pubDate>Fri, 17 May 2019 15:29:20 +0800</pubDate>
<guid>http://jaseg.de/blog/hsm-basics/</guid>
- <description>Hardware Security Modules and Security Research and Cryptography On May 17 2019 I gave a short presentation on the fundamentals of hardware security modules at the weekly seminar of Prof. Mori&#39;s security research working group at Waseda University. The motivation for this was that outside of low-level hardware security people and people working in the financial industry HSMs are not thought about that often. In particular most network or systems security people would not consider them an option.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;I gave a short introduction into Hardware Security Modules at our university workgroup, including an overview on interesting research directions.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>How to talk to your microcontroller over serial</title>
<link>http://jaseg.de/blog/serial-protocols/</link>
<pubDate>Sat, 19 May 2018 08:09:46 +0200</pubDate>
<guid>http://jaseg.de/blog/serial-protocols/</guid>
- <description>Scroll to the end for the TL;DR.
-In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error conditions.
-If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to another microcontroller or to a computer.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Scroll to the end for the &lt;a class=&#34;reference internal&#34; href=&#34;#conclusion&#34;&gt;TL;DR&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;In this article I will give an overview on the protocols spoken on serial ports, highlighting common pitfalls. I will&#xA;summarize some points on how to design a serial protocol that is simple to implement and works reliably even under error&#xA;conditions.&lt;/p&gt;&#xA;&lt;p&gt;If you have done low-level microcontroller firmware you will regularly have had to stuff some data up a serial port to&#xA;another microcontroller or to a computer. In the age of USB, an old-school serial port is still the simplest and&#xA;quickest way to get communication to a control computer up and running. Integrating a ten thousand-line USB stack into&#xA;your firmware and writing the necessary low-level drivers on the host side might take days. Poking a few registers to&#xA;set up your UART to talk to an external hardware USB to serial converter is a matter of minutes.&lt;/p&gt;&lt;/div&gt;</description>
</item>
<item>
<title>Thor&#39;s Hammer</title>
<link>http://jaseg.de/blog/thors-hammer/</link>
<pubDate>Thu, 03 May 2018 11:59:37 +0200</pubDate>
<guid>http://jaseg.de/blog/thors-hammer/</guid>
- <description>In case you were having an inferiority complex because your friends&#39; IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here&#39;s the solution: Thor&#39;s Hammer, a simple typing cadence enhancer for PS/2 keyboards.
-Your browser does not support the HTML5 video tag. A demonstration of the completed project. h264 download / webm download The connects to the keyboard&#39;s PS/2 clock line and briefly actuates a large solenoid on each key press.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;In case you were having an inferiority complex because your friends&#39; IBM Model M keyboards are so much louder than the shitty rubber dome freebie you got with your pc... Here&#39;s the solution: Thor&#39;s Hammer, a simple typing cadence enhancer for PS/2 keyboards.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>32-Channel LED tape driver</title>
<link>http://jaseg.de/blog/multichannel-led-driver/</link>
<pubDate>Wed, 02 May 2018 11:31:14 +0200</pubDate>
<guid>http://jaseg.de/blog/multichannel-led-driver/</guid>
- <description>Theoretical basics Together, a friend and I outfitted the small staircase at Berlin&#39;s Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. This tape is like regular RGB tape but with an additional warm white channel, which makes for much more natural pastels and whites. There are several variants of RGBW tape. Cheap ones have separate RGB and white LEDs, which is fine for indirect lighting but does not work for direct lighting.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Together, a friend and I outfitted the small staircase at Berlin&#39;s Chaos Computer Club with nice, shiny RGB-WW LED tape for ambient lighting. For this installation, I made a 32-channel LED driver that achieves high dynamic range on all 32 channels using a cheap microcontroller by using Binary Code Modulation.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Wifi Led Driver</title>
<link>http://jaseg.de/blog/wifi-led-driver/</link>
<pubDate>Wed, 02 May 2018 11:31:03 +0200</pubDate>
<guid>http://jaseg.de/blog/wifi-led-driver/</guid>
- <description>Project motivation The completed driver board installed in the 3D-printed case. This device can now be connected to 12V and two segments of LED tape that can then be controlled trough Wifi. The ESP8266 module goes on the pin header on the left and was removed for this picture. After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;After the multichannel LED driver was completed, I was just getting used to controlling LEDs at 14-bit resolution. I liked the board we designed in this project, but at 32 channels it was a bit large for most use cases. Sometimes I just want to pop a piece of LED tape or two somewhere, but I don&#39;t need a full 32 channels of control. I ended up thinking that a smaller version of the 32-channel driver that didn&#39;t require a separate control computer would be handy. So I sat down and designed a variant of the design with only 8 channels instead of 32 and an on-board ESP8266 module instead of the RS485 transceiver for WiFi connectivity.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>LED Characterization</title>
<link>http://jaseg.de/blog/led-characterization/</link>
<pubDate>Wed, 02 May 2018 11:18:38 +0200</pubDate>
<guid>http://jaseg.de/blog/led-characterization/</guid>
- <description>Preface Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look all wrong!</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Recently, I have been working on a small driver for ambient lighting using 12V LED strips like you can get inexpensively from China. I wanted to be able to just throw one of these somewhere, stick down some LED tape, hook it up to a small transformer and be able to control it through Wifi. When I was writing the firmware, I noticed that when fading between different colors, the colors look &lt;em&gt;all wrong&lt;/em&gt;! This observation led me down a rabbit hole of color perception and LED peculiarities.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>8seg</title>
<link>http://jaseg.de/projects/8seg/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/8seg/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a variety of spaces, conforming to the space&#39;s size and shape through bending and folding.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a variety of spaces, conforming to the space&#39;s size and shape through bending and folding.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>About jaseg</title>
<link>http://jaseg.de/about/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/about/</guid>
- <description>About Hej, I&#39;m Jan, or jaseg. At the moment I&#39;m doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on Hardware Security. This is my personal website where I publish things that I find interesting.
-I self-host my code at git.jaseg.de, but I am also on github and on gitlab. I use github for issue tracking for some of my projects such as gerbolyze and python-mpv. I maintain the python-mpv and gerbolyze python packages on PyPI.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;section&#34; id=&#34;about&#34;&gt;&#xA;&lt;h2&gt;About&lt;/h2&gt;&#xA;&lt;p&gt;Hej, I&#39;m Jan, or jaseg. At the moment I&#39;m doing a PhD (Dr.-Ing.) at TU Darmstadt in Computer Science, specializing on&#xA;Hardware Security. This is my personal website where I publish things that I find interesting.&lt;/p&gt;&#xA;&lt;p&gt;I self-host my code at &lt;a class=&#34;reference external&#34; href=&#34;https://git.jaseg.de/&#34;&gt;git.jaseg.de&lt;/a&gt;, but I am also on &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/jaseg&#34;&gt;github&lt;/a&gt;&#xA;and on &lt;a class=&#34;reference external&#34; href=&#34;https://gitlab.com/neinseg&#34;&gt;gitlab&lt;/a&gt;. I use github for issue tracking for some of my projects such as&#xA;&lt;a class=&#34;reference external&#34; href=&#34;https://github.com/jaseg/gerbolyze&#34;&gt;gerbolyze&lt;/a&gt; and &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/jaseg/python-mpv&#34;&gt;python-mpv&lt;/a&gt;. I maintain&#xA;the &lt;a class=&#34;reference external&#34; href=&#34;https://pypi.org/project/python-mpv/&#34;&gt;python-mpv&lt;/a&gt; and &lt;a class=&#34;reference external&#34; href=&#34;https://pypi.org/project/gerbolyze/&#34;&gt;gerbolyze&lt;/a&gt; python&#xA;packages on PyPI. Release tags on these two repositories are signed with the release signing key found &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/jaseg.gpg&#34;&gt;on github&lt;/a&gt; and below.&lt;/p&gt;&lt;/div&gt;</description>
</item>
<item>
<title>Gerbolyze</title>
<link>http://jaseg.de/projects/gerbolyze/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/gerbolyze/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Gerbonara</title>
<link>http://jaseg.de/projects/gerbonara/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/gerbonara/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Impressum</title>
<link>http://jaseg.de/imprint/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/imprint/</guid>
- <description>Impressum Sebastian Götte c/o Praxis Dr. Götte Krankenhausstr. 1a 54634 Bitburg imprint@jaseg.net Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.) Lizenz dieser Seite Dieses Werk ist lizenziert unter einer Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International Lizenz (CC-BY-NC-SA) . Haftungsbeschränkung für Inhalte der Website Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung bereithält, nach den allgemeinen Gesetzen verantwortlich.</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;div class=&#34;section&#34; id=&#34;impressum&#34;&gt;&#xA;&lt;h2&gt;Impressum&lt;/h2&gt;&#xA;&lt;p&gt;&#xA;Sebastian Götte&lt;br/&gt;&#xA;c/o Praxis Dr. Götte&lt;br/&gt;&#xA;Krankenhausstr. 1a&lt;br/&gt;&#xA;54634 Bitburg&lt;br/&gt;&#xA;imprint@jaseg.net&#xA;&lt;/p&gt;&#xA;&#xA;Inhaltlich Verantwortlicher gem. § 55 II RStV: Sebastian Götte (Anschrift s.o.)&lt;/div&gt;&#xA;&lt;div class=&#34;section&#34; id=&#34;lizenz-dieser-seite&#34;&gt;&#xA;&lt;h2&gt;Lizenz dieser Seite&lt;/h2&gt;&#xA;Dieses Werk ist lizenziert unter einer&#xA;&lt;a rel=&#34;license&#34; href=&#34;http://creativecommons.org/licenses/by-nc-sa/4.0/&#34;&gt;&#xA; Creative Commons Namensnennung - Nicht-kommerziell - Weitergabe unter gleichen Bedingungen 4.0 International&#xA; Lizenz (CC-BY-NC-SA)&#xA;&lt;/a&gt;.&lt;br/&gt;&#xA;&lt;a rel=&#34;license&#34; href=&#34;http://creativecommons.org/licenses/by-nc-sa/4.0/&#34;&gt;&#xA; &lt;img alt=&#34;Creative Commons Lizenzvertrag&#34; style=&#34;border-width:0&#34;&#xA; src=&#34;https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png&#34; data-pagefind-ignore/&gt;&#xA;&lt;/a&gt;&lt;/div&gt;&#xA;&lt;div class=&#34;section&#34; id=&#34;haftungsbeschrankung-fur-inhalte-der-website&#34;&gt;&#xA;&lt;h2&gt;Haftungsbeschränkung für Inhalte der Website&lt;/h2&gt;&#xA;&lt;p&gt;Gemäß § 7 Abs. 1 TMG ist der Verantwortliche der Website i. S. v. § 5 TMG für eigene Informationen, die er zur Nutzung&#xA;bereithält, nach den allgemeinen Gesetzen verantwortlich.&lt;/p&gt;&lt;/div&gt;</description>
</item>
<item>
<title>lolcat-c</title>
<link>http://jaseg.de/projects/lolcat-c/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/lolcat-c/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;lolcat-c is a small, high-performance re-implementation of the &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/busyloop/lolcat&#34;&gt;lolcat&lt;/a&gt; rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;lolcat-c is a small, high-performance re-implementation of the &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/busyloop/lolcat&#34;&gt;lolcat&lt;/a&gt; rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>python-mpv</title>
<link>http://jaseg.de/projects/python-mpv/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/python-mpv/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>svg-flatten</title>
<link>http://jaseg.de/projects/svg-flatten/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/svg-flatten/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>wsdiff</title>
<link>http://jaseg.de/projects/wsdiff/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/wsdiff/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
</channel>
</rss>
diff --git a/pagefind/fragment/unknown_13245ab.pf_fragment b/pagefind/fragment/unknown_13245ab.pf_fragment
new file mode 100644
index 0000000..477d52b
--- /dev/null
+++ b/pagefind/fragment/unknown_13245ab.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_1eea6ce.pf_fragment b/pagefind/fragment/unknown_1eea6ce.pf_fragment
new file mode 100644
index 0000000..3244a68
--- /dev/null
+++ b/pagefind/fragment/unknown_1eea6ce.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_2322ae6.pf_fragment b/pagefind/fragment/unknown_2322ae6.pf_fragment
new file mode 100644
index 0000000..e76d186
--- /dev/null
+++ b/pagefind/fragment/unknown_2322ae6.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_3584c24.pf_fragment b/pagefind/fragment/unknown_3584c24.pf_fragment
new file mode 100644
index 0000000..0cbb0f3
--- /dev/null
+++ b/pagefind/fragment/unknown_3584c24.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_3c5b478.pf_fragment b/pagefind/fragment/unknown_3c5b478.pf_fragment
new file mode 100644
index 0000000..2e5889b
--- /dev/null
+++ b/pagefind/fragment/unknown_3c5b478.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_53eea2c.pf_fragment b/pagefind/fragment/unknown_53eea2c.pf_fragment
new file mode 100644
index 0000000..438027a
--- /dev/null
+++ b/pagefind/fragment/unknown_53eea2c.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_57eeca7.pf_fragment b/pagefind/fragment/unknown_57eeca7.pf_fragment
new file mode 100644
index 0000000..30ea7a1
--- /dev/null
+++ b/pagefind/fragment/unknown_57eeca7.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_62bb76a.pf_fragment b/pagefind/fragment/unknown_62bb76a.pf_fragment
new file mode 100644
index 0000000..62d9e7d
--- /dev/null
+++ b/pagefind/fragment/unknown_62bb76a.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_70d175b.pf_fragment b/pagefind/fragment/unknown_70d175b.pf_fragment
new file mode 100644
index 0000000..cb0d3ef
--- /dev/null
+++ b/pagefind/fragment/unknown_70d175b.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_74968c4.pf_fragment b/pagefind/fragment/unknown_74968c4.pf_fragment
new file mode 100644
index 0000000..dcfdee6
--- /dev/null
+++ b/pagefind/fragment/unknown_74968c4.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_75d2fd6.pf_fragment b/pagefind/fragment/unknown_75d2fd6.pf_fragment
new file mode 100644
index 0000000..dfa5d55
--- /dev/null
+++ b/pagefind/fragment/unknown_75d2fd6.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_89b567d.pf_fragment b/pagefind/fragment/unknown_89b567d.pf_fragment
new file mode 100644
index 0000000..4b7cdfc
--- /dev/null
+++ b/pagefind/fragment/unknown_89b567d.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_91e77ca.pf_fragment b/pagefind/fragment/unknown_91e77ca.pf_fragment
new file mode 100644
index 0000000..5ca36a4
--- /dev/null
+++ b/pagefind/fragment/unknown_91e77ca.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_a052396.pf_fragment b/pagefind/fragment/unknown_a052396.pf_fragment
new file mode 100644
index 0000000..9ff3788
--- /dev/null
+++ b/pagefind/fragment/unknown_a052396.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_b311a8e.pf_fragment b/pagefind/fragment/unknown_b311a8e.pf_fragment
new file mode 100644
index 0000000..fdd6804
--- /dev/null
+++ b/pagefind/fragment/unknown_b311a8e.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_bb53633.pf_fragment b/pagefind/fragment/unknown_bb53633.pf_fragment
new file mode 100644
index 0000000..d9e58cf
--- /dev/null
+++ b/pagefind/fragment/unknown_bb53633.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_c96659e.pf_fragment b/pagefind/fragment/unknown_c96659e.pf_fragment
new file mode 100644
index 0000000..7467c16
--- /dev/null
+++ b/pagefind/fragment/unknown_c96659e.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_ca2490a.pf_fragment b/pagefind/fragment/unknown_ca2490a.pf_fragment
new file mode 100644
index 0000000..966be85
--- /dev/null
+++ b/pagefind/fragment/unknown_ca2490a.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_dca33a5.pf_fragment b/pagefind/fragment/unknown_dca33a5.pf_fragment
new file mode 100644
index 0000000..299a3a0
--- /dev/null
+++ b/pagefind/fragment/unknown_dca33a5.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_e377283.pf_fragment b/pagefind/fragment/unknown_e377283.pf_fragment
new file mode 100644
index 0000000..f8892b9
--- /dev/null
+++ b/pagefind/fragment/unknown_e377283.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_e47a975.pf_fragment b/pagefind/fragment/unknown_e47a975.pf_fragment
new file mode 100644
index 0000000..8f9f797
--- /dev/null
+++ b/pagefind/fragment/unknown_e47a975.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_f18dc78.pf_fragment b/pagefind/fragment/unknown_f18dc78.pf_fragment
new file mode 100644
index 0000000..2303975
--- /dev/null
+++ b/pagefind/fragment/unknown_f18dc78.pf_fragment
Binary files differ
diff --git a/pagefind/fragment/unknown_f1fb7e7.pf_fragment b/pagefind/fragment/unknown_f1fb7e7.pf_fragment
new file mode 100644
index 0000000..a32d2c7
--- /dev/null
+++ b/pagefind/fragment/unknown_f1fb7e7.pf_fragment
Binary files differ
diff --git a/pagefind/index/unknown_517251c.pf_index b/pagefind/index/unknown_517251c.pf_index
new file mode 100644
index 0000000..f2b32e3
--- /dev/null
+++ b/pagefind/index/unknown_517251c.pf_index
Binary files differ
diff --git a/pagefind/index/unknown_b9ec868.pf_index b/pagefind/index/unknown_b9ec868.pf_index
new file mode 100644
index 0000000..9bfdb6c
--- /dev/null
+++ b/pagefind/index/unknown_b9ec868.pf_index
Binary files differ
diff --git a/pagefind/index/unknown_d860aa8.pf_index b/pagefind/index/unknown_d860aa8.pf_index
new file mode 100644
index 0000000..c9803de
--- /dev/null
+++ b/pagefind/index/unknown_d860aa8.pf_index
Binary files differ
diff --git a/pagefind/pagefind-entry.json b/pagefind/pagefind-entry.json
new file mode 100644
index 0000000..f8f976c
--- /dev/null
+++ b/pagefind/pagefind-entry.json
@@ -0,0 +1 @@
+{"version":"1.0.4","languages":{"unknown":{"hash":"unknown_64739f45e64e1bc","wasm":null,"page_count":23}}} \ No newline at end of file
diff --git a/pagefind/pagefind-highlight.js b/pagefind/pagefind-highlight.js
new file mode 100644
index 0000000..c823fbf
--- /dev/null
+++ b/pagefind/pagefind-highlight.js
@@ -0,0 +1,1069 @@
+var __create = Object.create;
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __getProtoOf = Object.getPrototypeOf;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+var __commonJS = (cb, mod) => function __require() {
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
+};
+var __copyProps = (to, from, except, desc) => {
+ if (from && typeof from === "object" || typeof from === "function") {
+ for (let key of __getOwnPropNames(from))
+ if (!__hasOwnProp.call(to, key) && key !== except)
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+ }
+ return to;
+};
+var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
+ // If the importer is in node compatibility mode or this is not an ESM
+ // file that has been converted to a CommonJS file using a Babel-
+ // compatible transform (i.e. "__esModule" has not been set), then set
+ // "default" to the CommonJS "module.exports" for node compatibility.
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
+ mod
+));
+
+// node_modules/mark.js/dist/mark.js
+var require_mark = __commonJS({
+ "node_modules/mark.js/dist/mark.js"(exports, module) {
+ (function(global, factory) {
+ typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global.Mark = factory();
+ })(exports, function() {
+ "use strict";
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
+ return typeof obj;
+ } : function(obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+ var classCallCheck = function(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ };
+ var createClass = function() {
+ function defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor)
+ descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+ return function(Constructor, protoProps, staticProps) {
+ if (protoProps)
+ defineProperties(Constructor.prototype, protoProps);
+ if (staticProps)
+ defineProperties(Constructor, staticProps);
+ return Constructor;
+ };
+ }();
+ var _extends = Object.assign || function(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+ return target;
+ };
+ var DOMIterator = function() {
+ function DOMIterator2(ctx) {
+ var iframes = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;
+ var exclude = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : [];
+ var iframesTimeout = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 5e3;
+ classCallCheck(this, DOMIterator2);
+ this.ctx = ctx;
+ this.iframes = iframes;
+ this.exclude = exclude;
+ this.iframesTimeout = iframesTimeout;
+ }
+ createClass(DOMIterator2, [{
+ key: "getContexts",
+ value: function getContexts() {
+ var ctx = void 0, filteredCtx = [];
+ if (typeof this.ctx === "undefined" || !this.ctx) {
+ ctx = [];
+ } else if (NodeList.prototype.isPrototypeOf(this.ctx)) {
+ ctx = Array.prototype.slice.call(this.ctx);
+ } else if (Array.isArray(this.ctx)) {
+ ctx = this.ctx;
+ } else if (typeof this.ctx === "string") {
+ ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx));
+ } else {
+ ctx = [this.ctx];
+ }
+ ctx.forEach(function(ctx2) {
+ var isDescendant = filteredCtx.filter(function(contexts) {
+ return contexts.contains(ctx2);
+ }).length > 0;
+ if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) {
+ filteredCtx.push(ctx2);
+ }
+ });
+ return filteredCtx;
+ }
+ }, {
+ key: "getIframeContents",
+ value: function getIframeContents(ifr, successFn) {
+ var errorFn = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : function() {
+ };
+ var doc = void 0;
+ try {
+ var ifrWin = ifr.contentWindow;
+ doc = ifrWin.document;
+ if (!ifrWin || !doc) {
+ throw new Error("iframe inaccessible");
+ }
+ } catch (e) {
+ errorFn();
+ }
+ if (doc) {
+ successFn(doc);
+ }
+ }
+ }, {
+ key: "isIframeBlank",
+ value: function isIframeBlank(ifr) {
+ var bl = "about:blank", src = ifr.getAttribute("src").trim(), href = ifr.contentWindow.location.href;
+ return href === bl && src !== bl && src;
+ }
+ }, {
+ key: "observeIframeLoad",
+ value: function observeIframeLoad(ifr, successFn, errorFn) {
+ var _this = this;
+ var called = false, tout = null;
+ var listener = function listener2() {
+ if (called) {
+ return;
+ }
+ called = true;
+ clearTimeout(tout);
+ try {
+ if (!_this.isIframeBlank(ifr)) {
+ ifr.removeEventListener("load", listener2);
+ _this.getIframeContents(ifr, successFn, errorFn);
+ }
+ } catch (e) {
+ errorFn();
+ }
+ };
+ ifr.addEventListener("load", listener);
+ tout = setTimeout(listener, this.iframesTimeout);
+ }
+ }, {
+ key: "onIframeReady",
+ value: function onIframeReady(ifr, successFn, errorFn) {
+ try {
+ if (ifr.contentWindow.document.readyState === "complete") {
+ if (this.isIframeBlank(ifr)) {
+ this.observeIframeLoad(ifr, successFn, errorFn);
+ } else {
+ this.getIframeContents(ifr, successFn, errorFn);
+ }
+ } else {
+ this.observeIframeLoad(ifr, successFn, errorFn);
+ }
+ } catch (e) {
+ errorFn();
+ }
+ }
+ }, {
+ key: "waitForIframes",
+ value: function waitForIframes(ctx, done) {
+ var _this2 = this;
+ var eachCalled = 0;
+ this.forEachIframe(ctx, function() {
+ return true;
+ }, function(ifr) {
+ eachCalled++;
+ _this2.waitForIframes(ifr.querySelector("html"), function() {
+ if (!--eachCalled) {
+ done();
+ }
+ });
+ }, function(handled) {
+ if (!handled) {
+ done();
+ }
+ });
+ }
+ }, {
+ key: "forEachIframe",
+ value: function forEachIframe(ctx, filter, each) {
+ var _this3 = this;
+ var end = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() {
+ };
+ var ifr = ctx.querySelectorAll("iframe"), open = ifr.length, handled = 0;
+ ifr = Array.prototype.slice.call(ifr);
+ var checkEnd = function checkEnd2() {
+ if (--open <= 0) {
+ end(handled);
+ }
+ };
+ if (!open) {
+ checkEnd();
+ }
+ ifr.forEach(function(ifr2) {
+ if (DOMIterator2.matches(ifr2, _this3.exclude)) {
+ checkEnd();
+ } else {
+ _this3.onIframeReady(ifr2, function(con) {
+ if (filter(ifr2)) {
+ handled++;
+ each(con);
+ }
+ checkEnd();
+ }, checkEnd);
+ }
+ });
+ }
+ }, {
+ key: "createIterator",
+ value: function createIterator(ctx, whatToShow, filter) {
+ return document.createNodeIterator(ctx, whatToShow, filter, false);
+ }
+ }, {
+ key: "createInstanceOnIframe",
+ value: function createInstanceOnIframe(contents) {
+ return new DOMIterator2(contents.querySelector("html"), this.iframes);
+ }
+ }, {
+ key: "compareNodeIframe",
+ value: function compareNodeIframe(node, prevNode, ifr) {
+ var compCurr = node.compareDocumentPosition(ifr), prev = Node.DOCUMENT_POSITION_PRECEDING;
+ if (compCurr & prev) {
+ if (prevNode !== null) {
+ var compPrev = prevNode.compareDocumentPosition(ifr), after = Node.DOCUMENT_POSITION_FOLLOWING;
+ if (compPrev & after) {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+ }, {
+ key: "getIteratorNode",
+ value: function getIteratorNode(itr) {
+ var prevNode = itr.previousNode();
+ var node = void 0;
+ if (prevNode === null) {
+ node = itr.nextNode();
+ } else {
+ node = itr.nextNode() && itr.nextNode();
+ }
+ return {
+ prevNode,
+ node
+ };
+ }
+ }, {
+ key: "checkIframeFilter",
+ value: function checkIframeFilter(node, prevNode, currIfr, ifr) {
+ var key = false, handled = false;
+ ifr.forEach(function(ifrDict, i) {
+ if (ifrDict.val === currIfr) {
+ key = i;
+ handled = ifrDict.handled;
+ }
+ });
+ if (this.compareNodeIframe(node, prevNode, currIfr)) {
+ if (key === false && !handled) {
+ ifr.push({
+ val: currIfr,
+ handled: true
+ });
+ } else if (key !== false && !handled) {
+ ifr[key].handled = true;
+ }
+ return true;
+ }
+ if (key === false) {
+ ifr.push({
+ val: currIfr,
+ handled: false
+ });
+ }
+ return false;
+ }
+ }, {
+ key: "handleOpenIframes",
+ value: function handleOpenIframes(ifr, whatToShow, eCb, fCb) {
+ var _this4 = this;
+ ifr.forEach(function(ifrDict) {
+ if (!ifrDict.handled) {
+ _this4.getIframeContents(ifrDict.val, function(con) {
+ _this4.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb);
+ });
+ }
+ });
+ }
+ }, {
+ key: "iterateThroughNodes",
+ value: function iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) {
+ var _this5 = this;
+ var itr = this.createIterator(ctx, whatToShow, filterCb);
+ var ifr = [], elements = [], node = void 0, prevNode = void 0, retrieveNodes = function retrieveNodes2() {
+ var _getIteratorNode = _this5.getIteratorNode(itr);
+ prevNode = _getIteratorNode.prevNode;
+ node = _getIteratorNode.node;
+ return node;
+ };
+ while (retrieveNodes()) {
+ if (this.iframes) {
+ this.forEachIframe(ctx, function(currIfr) {
+ return _this5.checkIframeFilter(node, prevNode, currIfr, ifr);
+ }, function(con) {
+ _this5.createInstanceOnIframe(con).forEachNode(whatToShow, function(ifrNode) {
+ return elements.push(ifrNode);
+ }, filterCb);
+ });
+ }
+ elements.push(node);
+ }
+ elements.forEach(function(node2) {
+ eachCb(node2);
+ });
+ if (this.iframes) {
+ this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb);
+ }
+ doneCb();
+ }
+ }, {
+ key: "forEachNode",
+ value: function forEachNode(whatToShow, each, filter) {
+ var _this6 = this;
+ var done = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() {
+ };
+ var contexts = this.getContexts();
+ var open = contexts.length;
+ if (!open) {
+ done();
+ }
+ contexts.forEach(function(ctx) {
+ var ready = function ready2() {
+ _this6.iterateThroughNodes(whatToShow, ctx, each, filter, function() {
+ if (--open <= 0) {
+ done();
+ }
+ });
+ };
+ if (_this6.iframes) {
+ _this6.waitForIframes(ctx, ready);
+ } else {
+ ready();
+ }
+ });
+ }
+ }], [{
+ key: "matches",
+ value: function matches(element, selector) {
+ var selectors = typeof selector === "string" ? [selector] : selector, fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector;
+ if (fn) {
+ var match = false;
+ selectors.every(function(sel) {
+ if (fn.call(element, sel)) {
+ match = true;
+ return false;
+ }
+ return true;
+ });
+ return match;
+ } else {
+ return false;
+ }
+ }
+ }]);
+ return DOMIterator2;
+ }();
+ var Mark$1 = function() {
+ function Mark3(ctx) {
+ classCallCheck(this, Mark3);
+ this.ctx = ctx;
+ this.ie = false;
+ var ua = window.navigator.userAgent;
+ if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) {
+ this.ie = true;
+ }
+ }
+ createClass(Mark3, [{
+ key: "log",
+ value: function log(msg) {
+ var level = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "debug";
+ var log2 = this.opt.log;
+ if (!this.opt.debug) {
+ return;
+ }
+ if ((typeof log2 === "undefined" ? "undefined" : _typeof(log2)) === "object" && typeof log2[level] === "function") {
+ log2[level]("mark.js: " + msg);
+ }
+ }
+ }, {
+ key: "escapeStr",
+ value: function escapeStr(str) {
+ return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ }
+ }, {
+ key: "createRegExp",
+ value: function createRegExp(str) {
+ if (this.opt.wildcards !== "disabled") {
+ str = this.setupWildcardsRegExp(str);
+ }
+ str = this.escapeStr(str);
+ if (Object.keys(this.opt.synonyms).length) {
+ str = this.createSynonymsRegExp(str);
+ }
+ if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
+ str = this.setupIgnoreJoinersRegExp(str);
+ }
+ if (this.opt.diacritics) {
+ str = this.createDiacriticsRegExp(str);
+ }
+ str = this.createMergedBlanksRegExp(str);
+ if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
+ str = this.createJoinersRegExp(str);
+ }
+ if (this.opt.wildcards !== "disabled") {
+ str = this.createWildcardsRegExp(str);
+ }
+ str = this.createAccuracyRegExp(str);
+ return str;
+ }
+ }, {
+ key: "createSynonymsRegExp",
+ value: function createSynonymsRegExp(str) {
+ var syn = this.opt.synonyms, sens = this.opt.caseSensitive ? "" : "i", joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : "";
+ for (var index in syn) {
+ if (syn.hasOwnProperty(index)) {
+ var value = syn[index], k1 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(index) : this.escapeStr(index), k2 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(value) : this.escapeStr(value);
+ if (k1 !== "" && k2 !== "") {
+ str = str.replace(new RegExp("(" + this.escapeStr(k1) + "|" + this.escapeStr(k2) + ")", "gm" + sens), joinerPlaceholder + ("(" + this.processSynomyms(k1) + "|") + (this.processSynomyms(k2) + ")") + joinerPlaceholder);
+ }
+ }
+ }
+ return str;
+ }
+ }, {
+ key: "processSynomyms",
+ value: function processSynomyms(str) {
+ if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {
+ str = this.setupIgnoreJoinersRegExp(str);
+ }
+ return str;
+ }
+ }, {
+ key: "setupWildcardsRegExp",
+ value: function setupWildcardsRegExp(str) {
+ str = str.replace(/(?:\\)*\?/g, function(val) {
+ return val.charAt(0) === "\\" ? "?" : "";
+ });
+ return str.replace(/(?:\\)*\*/g, function(val) {
+ return val.charAt(0) === "\\" ? "*" : "";
+ });
+ }
+ }, {
+ key: "createWildcardsRegExp",
+ value: function createWildcardsRegExp(str) {
+ var spaces = this.opt.wildcards === "withSpaces";
+ return str.replace(/\u0001/g, spaces ? "[\\S\\s]?" : "\\S?").replace(/\u0002/g, spaces ? "[\\S\\s]*?" : "\\S*");
+ }
+ }, {
+ key: "setupIgnoreJoinersRegExp",
+ value: function setupIgnoreJoinersRegExp(str) {
+ return str.replace(/[^(|)\\]/g, function(val, indx, original) {
+ var nextChar = original.charAt(indx + 1);
+ if (/[(|)\\]/.test(nextChar) || nextChar === "") {
+ return val;
+ } else {
+ return val + "\0";
+ }
+ });
+ }
+ }, {
+ key: "createJoinersRegExp",
+ value: function createJoinersRegExp(str) {
+ var joiner = [];
+ var ignorePunctuation = this.opt.ignorePunctuation;
+ if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) {
+ joiner.push(this.escapeStr(ignorePunctuation.join("")));
+ }
+ if (this.opt.ignoreJoiners) {
+ joiner.push("\\u00ad\\u200b\\u200c\\u200d");
+ }
+ return joiner.length ? str.split(/\u0000+/).join("[" + joiner.join("") + "]*") : str;
+ }
+ }, {
+ key: "createDiacriticsRegExp",
+ value: function createDiacriticsRegExp(str) {
+ var sens = this.opt.caseSensitive ? "" : "i", dct = this.opt.caseSensitive ? ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105", "A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010D", "C\xC7\u0106\u010C", "d\u0111\u010F", "D\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119", "E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012B", "I\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142", "L\u0141", "n\xF1\u0148\u0144", "N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014D", "O\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159", "R\u0158", "s\u0161\u015B\u0219\u015F", "S\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163", "T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016B", "U\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFF", "Y\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017A", "Z\u017D\u017B\u0179"] : ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010DC\xC7\u0106\u010C", "d\u0111\u010FD\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012BI\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142L\u0141", "n\xF1\u0148\u0144N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014DO\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159R\u0158", "s\u0161\u015B\u0219\u015FS\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016BU\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFFY\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017AZ\u017D\u017B\u0179"];
+ var handled = [];
+ str.split("").forEach(function(ch) {
+ dct.every(function(dct2) {
+ if (dct2.indexOf(ch) !== -1) {
+ if (handled.indexOf(dct2) > -1) {
+ return false;
+ }
+ str = str.replace(new RegExp("[" + dct2 + "]", "gm" + sens), "[" + dct2 + "]");
+ handled.push(dct2);
+ }
+ return true;
+ });
+ });
+ return str;
+ }
+ }, {
+ key: "createMergedBlanksRegExp",
+ value: function createMergedBlanksRegExp(str) {
+ return str.replace(/[\s]+/gmi, "[\\s]+");
+ }
+ }, {
+ key: "createAccuracyRegExp",
+ value: function createAccuracyRegExp(str) {
+ var _this = this;
+ var chars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\xA1\xBF";
+ var acc = this.opt.accuracy, val = typeof acc === "string" ? acc : acc.value, ls = typeof acc === "string" ? [] : acc.limiters, lsJoin = "";
+ ls.forEach(function(limiter) {
+ lsJoin += "|" + _this.escapeStr(limiter);
+ });
+ switch (val) {
+ case "partially":
+ default:
+ return "()(" + str + ")";
+ case "complementary":
+ lsJoin = "\\s" + (lsJoin ? lsJoin : this.escapeStr(chars));
+ return "()([^" + lsJoin + "]*" + str + "[^" + lsJoin + "]*)";
+ case "exactly":
+ return "(^|\\s" + lsJoin + ")(" + str + ")(?=$|\\s" + lsJoin + ")";
+ }
+ }
+ }, {
+ key: "getSeparatedKeywords",
+ value: function getSeparatedKeywords(sv) {
+ var _this2 = this;
+ var stack = [];
+ sv.forEach(function(kw) {
+ if (!_this2.opt.separateWordSearch) {
+ if (kw.trim() && stack.indexOf(kw) === -1) {
+ stack.push(kw);
+ }
+ } else {
+ kw.split(" ").forEach(function(kwSplitted) {
+ if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) {
+ stack.push(kwSplitted);
+ }
+ });
+ }
+ });
+ return {
+ "keywords": stack.sort(function(a, b) {
+ return b.length - a.length;
+ }),
+ "length": stack.length
+ };
+ }
+ }, {
+ key: "isNumeric",
+ value: function isNumeric(value) {
+ return Number(parseFloat(value)) == value;
+ }
+ }, {
+ key: "checkRanges",
+ value: function checkRanges(array) {
+ var _this3 = this;
+ if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== "[object Object]") {
+ this.log("markRanges() will only accept an array of objects");
+ this.opt.noMatch(array);
+ return [];
+ }
+ var stack = [];
+ var last = 0;
+ array.sort(function(a, b) {
+ return a.start - b.start;
+ }).forEach(function(item) {
+ var _callNoMatchOnInvalid = _this3.callNoMatchOnInvalidRanges(item, last), start = _callNoMatchOnInvalid.start, end = _callNoMatchOnInvalid.end, valid = _callNoMatchOnInvalid.valid;
+ if (valid) {
+ item.start = start;
+ item.length = end - start;
+ stack.push(item);
+ last = end;
+ }
+ });
+ return stack;
+ }
+ }, {
+ key: "callNoMatchOnInvalidRanges",
+ value: function callNoMatchOnInvalidRanges(range, last) {
+ var start = void 0, end = void 0, valid = false;
+ if (range && typeof range.start !== "undefined") {
+ start = parseInt(range.start, 10);
+ end = start + parseInt(range.length, 10);
+ if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) {
+ valid = true;
+ } else {
+ this.log("Ignoring invalid or overlapping range: " + ("" + JSON.stringify(range)));
+ this.opt.noMatch(range);
+ }
+ } else {
+ this.log("Ignoring invalid range: " + JSON.stringify(range));
+ this.opt.noMatch(range);
+ }
+ return {
+ start,
+ end,
+ valid
+ };
+ }
+ }, {
+ key: "checkWhitespaceRanges",
+ value: function checkWhitespaceRanges(range, originalLength, string) {
+ var end = void 0, valid = true, max = string.length, offset = originalLength - max, start = parseInt(range.start, 10) - offset;
+ start = start > max ? max : start;
+ end = start + parseInt(range.length, 10);
+ if (end > max) {
+ end = max;
+ this.log("End range automatically set to the max value of " + max);
+ }
+ if (start < 0 || end - start < 0 || start > max || end > max) {
+ valid = false;
+ this.log("Invalid range: " + JSON.stringify(range));
+ this.opt.noMatch(range);
+ } else if (string.substring(start, end).replace(/\s+/g, "") === "") {
+ valid = false;
+ this.log("Skipping whitespace only range: " + JSON.stringify(range));
+ this.opt.noMatch(range);
+ }
+ return {
+ start,
+ end,
+ valid
+ };
+ }
+ }, {
+ key: "getTextNodes",
+ value: function getTextNodes(cb) {
+ var _this4 = this;
+ var val = "", nodes = [];
+ this.iterator.forEachNode(NodeFilter.SHOW_TEXT, function(node) {
+ nodes.push({
+ start: val.length,
+ end: (val += node.textContent).length,
+ node
+ });
+ }, function(node) {
+ if (_this4.matchesExclude(node.parentNode)) {
+ return NodeFilter.FILTER_REJECT;
+ } else {
+ return NodeFilter.FILTER_ACCEPT;
+ }
+ }, function() {
+ cb({
+ value: val,
+ nodes
+ });
+ });
+ }
+ }, {
+ key: "matchesExclude",
+ value: function matchesExclude(el) {
+ return DOMIterator.matches(el, this.opt.exclude.concat(["script", "style", "title", "head", "html"]));
+ }
+ }, {
+ key: "wrapRangeInTextNode",
+ value: function wrapRangeInTextNode(node, start, end) {
+ var hEl = !this.opt.element ? "mark" : this.opt.element, startNode = node.splitText(start), ret = startNode.splitText(end - start);
+ var repl = document.createElement(hEl);
+ repl.setAttribute("data-markjs", "true");
+ if (this.opt.className) {
+ repl.setAttribute("class", this.opt.className);
+ }
+ repl.textContent = startNode.textContent;
+ startNode.parentNode.replaceChild(repl, startNode);
+ return ret;
+ }
+ }, {
+ key: "wrapRangeInMappedTextNode",
+ value: function wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) {
+ var _this5 = this;
+ dict.nodes.every(function(n, i) {
+ var sibl = dict.nodes[i + 1];
+ if (typeof sibl === "undefined" || sibl.start > start) {
+ if (!filterCb(n.node)) {
+ return false;
+ }
+ var s = start - n.start, e = (end > n.end ? n.end : end) - n.start, startStr = dict.value.substr(0, n.start), endStr = dict.value.substr(e + n.start);
+ n.node = _this5.wrapRangeInTextNode(n.node, s, e);
+ dict.value = startStr + endStr;
+ dict.nodes.forEach(function(k, j) {
+ if (j >= i) {
+ if (dict.nodes[j].start > 0 && j !== i) {
+ dict.nodes[j].start -= e;
+ }
+ dict.nodes[j].end -= e;
+ }
+ });
+ end -= e;
+ eachCb(n.node.previousSibling, n.start);
+ if (end > n.end) {
+ start = n.end;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+ }, {
+ key: "wrapMatches",
+ value: function wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) {
+ var _this6 = this;
+ var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
+ this.getTextNodes(function(dict) {
+ dict.nodes.forEach(function(node) {
+ node = node.node;
+ var match = void 0;
+ while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== "") {
+ if (!filterCb(match[matchIdx], node)) {
+ continue;
+ }
+ var pos = match.index;
+ if (matchIdx !== 0) {
+ for (var i = 1; i < matchIdx; i++) {
+ pos += match[i].length;
+ }
+ }
+ node = _this6.wrapRangeInTextNode(node, pos, pos + match[matchIdx].length);
+ eachCb(node.previousSibling);
+ regex.lastIndex = 0;
+ }
+ });
+ endCb();
+ });
+ }
+ }, {
+ key: "wrapMatchesAcrossElements",
+ value: function wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) {
+ var _this7 = this;
+ var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;
+ this.getTextNodes(function(dict) {
+ var match = void 0;
+ while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== "") {
+ var start = match.index;
+ if (matchIdx !== 0) {
+ for (var i = 1; i < matchIdx; i++) {
+ start += match[i].length;
+ }
+ }
+ var end = start + match[matchIdx].length;
+ _this7.wrapRangeInMappedTextNode(dict, start, end, function(node) {
+ return filterCb(match[matchIdx], node);
+ }, function(node, lastIndex) {
+ regex.lastIndex = lastIndex;
+ eachCb(node);
+ });
+ }
+ endCb();
+ });
+ }
+ }, {
+ key: "wrapRangeFromIndex",
+ value: function wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) {
+ var _this8 = this;
+ this.getTextNodes(function(dict) {
+ var originalLength = dict.value.length;
+ ranges.forEach(function(range, counter) {
+ var _checkWhitespaceRange = _this8.checkWhitespaceRanges(range, originalLength, dict.value), start = _checkWhitespaceRange.start, end = _checkWhitespaceRange.end, valid = _checkWhitespaceRange.valid;
+ if (valid) {
+ _this8.wrapRangeInMappedTextNode(dict, start, end, function(node) {
+ return filterCb(node, range, dict.value.substring(start, end), counter);
+ }, function(node) {
+ eachCb(node, range);
+ });
+ }
+ });
+ endCb();
+ });
+ }
+ }, {
+ key: "unwrapMatches",
+ value: function unwrapMatches(node) {
+ var parent = node.parentNode;
+ var docFrag = document.createDocumentFragment();
+ while (node.firstChild) {
+ docFrag.appendChild(node.removeChild(node.firstChild));
+ }
+ parent.replaceChild(docFrag, node);
+ if (!this.ie) {
+ parent.normalize();
+ } else {
+ this.normalizeTextNode(parent);
+ }
+ }
+ }, {
+ key: "normalizeTextNode",
+ value: function normalizeTextNode(node) {
+ if (!node) {
+ return;
+ }
+ if (node.nodeType === 3) {
+ while (node.nextSibling && node.nextSibling.nodeType === 3) {
+ node.nodeValue += node.nextSibling.nodeValue;
+ node.parentNode.removeChild(node.nextSibling);
+ }
+ } else {
+ this.normalizeTextNode(node.firstChild);
+ }
+ this.normalizeTextNode(node.nextSibling);
+ }
+ }, {
+ key: "markRegExp",
+ value: function markRegExp(regexp, opt) {
+ var _this9 = this;
+ this.opt = opt;
+ this.log('Searching with expression "' + regexp + '"');
+ var totalMatches = 0, fn = "wrapMatches";
+ var eachCb = function eachCb2(element) {
+ totalMatches++;
+ _this9.opt.each(element);
+ };
+ if (this.opt.acrossElements) {
+ fn = "wrapMatchesAcrossElements";
+ }
+ this[fn](regexp, this.opt.ignoreGroups, function(match, node) {
+ return _this9.opt.filter(node, match, totalMatches);
+ }, eachCb, function() {
+ if (totalMatches === 0) {
+ _this9.opt.noMatch(regexp);
+ }
+ _this9.opt.done(totalMatches);
+ });
+ }
+ }, {
+ key: "mark",
+ value: function mark(sv, opt) {
+ var _this10 = this;
+ this.opt = opt;
+ var totalMatches = 0, fn = "wrapMatches";
+ var _getSeparatedKeywords = this.getSeparatedKeywords(typeof sv === "string" ? [sv] : sv), kwArr = _getSeparatedKeywords.keywords, kwArrLen = _getSeparatedKeywords.length, sens = this.opt.caseSensitive ? "" : "i", handler = function handler2(kw) {
+ var regex = new RegExp(_this10.createRegExp(kw), "gm" + sens), matches = 0;
+ _this10.log('Searching with expression "' + regex + '"');
+ _this10[fn](regex, 1, function(term, node) {
+ return _this10.opt.filter(node, kw, totalMatches, matches);
+ }, function(element) {
+ matches++;
+ totalMatches++;
+ _this10.opt.each(element);
+ }, function() {
+ if (matches === 0) {
+ _this10.opt.noMatch(kw);
+ }
+ if (kwArr[kwArrLen - 1] === kw) {
+ _this10.opt.done(totalMatches);
+ } else {
+ handler2(kwArr[kwArr.indexOf(kw) + 1]);
+ }
+ });
+ };
+ if (this.opt.acrossElements) {
+ fn = "wrapMatchesAcrossElements";
+ }
+ if (kwArrLen === 0) {
+ this.opt.done(totalMatches);
+ } else {
+ handler(kwArr[0]);
+ }
+ }
+ }, {
+ key: "markRanges",
+ value: function markRanges(rawRanges, opt) {
+ var _this11 = this;
+ this.opt = opt;
+ var totalMatches = 0, ranges = this.checkRanges(rawRanges);
+ if (ranges && ranges.length) {
+ this.log("Starting to mark with the following ranges: " + JSON.stringify(ranges));
+ this.wrapRangeFromIndex(ranges, function(node, range, match, counter) {
+ return _this11.opt.filter(node, range, match, counter);
+ }, function(element, range) {
+ totalMatches++;
+ _this11.opt.each(element, range);
+ }, function() {
+ _this11.opt.done(totalMatches);
+ });
+ } else {
+ this.opt.done(totalMatches);
+ }
+ }
+ }, {
+ key: "unmark",
+ value: function unmark(opt) {
+ var _this12 = this;
+ this.opt = opt;
+ var sel = this.opt.element ? this.opt.element : "*";
+ sel += "[data-markjs]";
+ if (this.opt.className) {
+ sel += "." + this.opt.className;
+ }
+ this.log('Removal selector "' + sel + '"');
+ this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, function(node) {
+ _this12.unwrapMatches(node);
+ }, function(node) {
+ var matchesSel = DOMIterator.matches(node, sel), matchesExclude = _this12.matchesExclude(node);
+ if (!matchesSel || matchesExclude) {
+ return NodeFilter.FILTER_REJECT;
+ } else {
+ return NodeFilter.FILTER_ACCEPT;
+ }
+ }, this.opt.done);
+ }
+ }, {
+ key: "opt",
+ set: function set$$1(val) {
+ this._opt = _extends({}, {
+ "element": "",
+ "className": "",
+ "exclude": [],
+ "iframes": false,
+ "iframesTimeout": 5e3,
+ "separateWordSearch": true,
+ "diacritics": true,
+ "synonyms": {},
+ "accuracy": "partially",
+ "acrossElements": false,
+ "caseSensitive": false,
+ "ignoreJoiners": false,
+ "ignoreGroups": 0,
+ "ignorePunctuation": [],
+ "wildcards": "disabled",
+ "each": function each() {
+ },
+ "noMatch": function noMatch() {
+ },
+ "filter": function filter() {
+ return true;
+ },
+ "done": function done() {
+ },
+ "debug": false,
+ "log": window.console
+ }, val);
+ },
+ get: function get$$1() {
+ return this._opt;
+ }
+ }, {
+ key: "iterator",
+ get: function get$$1() {
+ return new DOMIterator(this.ctx, this.opt.iframes, this.opt.exclude, this.opt.iframesTimeout);
+ }
+ }]);
+ return Mark3;
+ }();
+ function Mark2(ctx) {
+ var _this = this;
+ var instance = new Mark$1(ctx);
+ this.mark = function(sv, opt) {
+ instance.mark(sv, opt);
+ return _this;
+ };
+ this.markRegExp = function(sv, opt) {
+ instance.markRegExp(sv, opt);
+ return _this;
+ };
+ this.markRanges = function(sv, opt) {
+ instance.markRanges(sv, opt);
+ return _this;
+ };
+ this.unmark = function(opt) {
+ instance.unmark(opt);
+ return _this;
+ };
+ return this;
+ }
+ return Mark2;
+ });
+ }
+});
+
+// lib/highlight.ts
+var import_mark = __toESM(require_mark(), 1);
+var PagefindHighlight = class {
+ constructor(options = {
+ markContext: null,
+ highlightParam: "pagefind-highlight",
+ markOptions: {
+ className: "pagefind-highlight",
+ exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"]
+ },
+ addStyles: true
+ }) {
+ var _a, _b;
+ const { highlightParam, markContext, markOptions, addStyles } = options;
+ this.highlightParam = highlightParam ?? "pagefind-highlight";
+ this.addStyles = addStyles ?? true;
+ this.markContext = markContext !== void 0 ? markContext : null;
+ this.markOptions = markOptions !== void 0 ? markOptions : {
+ className: "pagefind-highlight",
+ exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"]
+ };
+ (_a = this.markOptions).className ?? (_a.className = "pagefind__highlight");
+ (_b = this.markOptions).exclude ?? (_b.exclude = [
+ "[data-pagefind-ignore]",
+ "[data-pagefind-ignore] *"
+ ]);
+ this.markOptions.separateWordSearch = false;
+ this.highlight();
+ }
+ getHighlightParams(paramName) {
+ const urlParams = new URLSearchParams(window.location.search);
+ return urlParams.getAll(paramName);
+ }
+ // Inline styles might be too hard to override
+ addHighlightStyles(className) {
+ if (!className)
+ return;
+ const styleElement = document.createElement("style");
+ styleElement.innerText = `:where(.${className}) { background-color: yellow; color: black; }`;
+ document.head.appendChild(styleElement);
+ }
+ createMarkInstance() {
+ if (this.markContext) {
+ return new import_mark.default(this.markContext);
+ }
+ const pagefindBody = document.querySelectorAll("[data-pagefind-body]");
+ if (pagefindBody.length !== 0) {
+ return new import_mark.default(pagefindBody);
+ } else {
+ return new import_mark.default(document.body);
+ }
+ }
+ markText(instance, text) {
+ instance.mark(text, this.markOptions);
+ }
+ highlight() {
+ const params = this.getHighlightParams(this.highlightParam);
+ if (!params || params.length === 0)
+ return;
+ this.addStyles && this.addHighlightStyles(this.markOptions.className);
+ const markInstance = this.createMarkInstance();
+ this.markText(markInstance, params);
+ }
+};
+window.PagefindHighlight = PagefindHighlight;
+export {
+ PagefindHighlight as default
+};
+/*! Bundled license information:
+
+mark.js/dist/mark.js:
+ (*!***************************************************
+ * mark.js v8.11.1
+ * https://markjs.io/
+ * Copyright (c) 2014–2018, Julian Kühnel
+ * Released under the MIT license https://git.io/vwTVl
+ *****************************************************)
+*/
diff --git a/pagefind/pagefind-modular-ui.css b/pagefind/pagefind-modular-ui.css
new file mode 100644
index 0000000..9c6793e
--- /dev/null
+++ b/pagefind/pagefind-modular-ui.css
@@ -0,0 +1,214 @@
+:root {
+ --pagefind-ui-scale: 0.8;
+ --pagefind-ui-primary: #034AD8;
+ --pagefind-ui-fade: #707070;
+ --pagefind-ui-text: #393939;
+ --pagefind-ui-background: #ffffff;
+ --pagefind-ui-border: #eeeeee;
+ --pagefind-ui-tag: #eeeeee;
+ --pagefind-ui-border-width: 2px;
+ --pagefind-ui-border-radius: 8px;
+ --pagefind-ui-image-border-radius: 8px;
+ --pagefind-ui-image-box-ratio: 3 / 2;
+ --pagefind-ui-font: system, -apple-system, ".SFNSText-Regular",
+ "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue",
+ "Lucida Grande", sans-serif;
+}
+
+[data-pfmod-hidden] {
+ display: none !important;
+}
+
+[data-pfmod-suppressed] {
+ opacity: 0 !important;
+ pointer-events: none !important;
+}
+
+[data-pfmod-sr-hidden] {
+ -webkit-clip: rect(0 0 0 0) !important;
+ clip: rect(0 0 0 0) !important;
+ -webkit-clip-path: inset(100%) !important;
+ clip-path: inset(100%) !important;
+ height: 1px !important;
+ overflow: hidden !important;
+ overflow: clip !important;
+ position: absolute !important;
+ white-space: nowrap !important;
+ width: 1px !important;
+}
+
+[data-pfmod-loading] {
+ color: var(--pagefind-ui-text);
+ background-color: var(--pagefind-ui-text);
+ border-radius: var(--pagefind-ui-border-radius);
+ opacity: 0.1;
+ pointer-events: none;
+}
+
+/* Input */
+
+.pagefind-modular-input-wrapper {
+ position: relative;
+}
+
+.pagefind-modular-input-wrapper::before {
+ background-color: var(--pagefind-ui-text);
+ width: calc(18px * var(--pagefind-ui-scale));
+ height: calc(18px * var(--pagefind-ui-scale));
+ top: calc(23px * var(--pagefind-ui-scale));
+ left: calc(20px * var(--pagefind-ui-scale));
+ content: "";
+ position: absolute;
+ display: block;
+ opacity: 0.7;
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
+ mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");
+ -webkit-mask-size: 100%;
+ mask-size: 100%;
+ z-index: 9;
+ pointer-events: none;
+}
+
+.pagefind-modular-input {
+ height: calc(64px * var(--pagefind-ui-scale));
+ padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));
+ background-color: var(--pagefind-ui-background);
+ border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);
+ border-radius: var(--pagefind-ui-border-radius);
+ font-size: calc(21px * var(--pagefind-ui-scale));
+ position: relative;
+ appearance: none;
+ -webkit-appearance: none;
+ display: flex;
+ width: 100%;
+ box-sizing: border-box;
+ font-weight: 700;
+}
+
+.pagefind-modular-input::placeholder {
+ opacity: 0.2;
+}
+
+.pagefind-modular-input-clear {
+ position: absolute;
+ top: calc(2px * var(--pagefind-ui-scale));
+ right: calc(2px * var(--pagefind-ui-scale));
+ height: calc(60px * var(--pagefind-ui-scale));
+ border-radius: var(--pagefind-ui-border-radius);
+ padding: 0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));
+ color: var(--pagefind-ui-text);
+ font-size: calc(14px * var(--pagefind-ui-scale));
+ cursor: pointer;
+ background-color: var(--pagefind-ui-background);
+ border: none;
+ appearance: none;
+}
+
+/* ResultList */
+
+.pagefind-modular-list-result {
+ list-style-type: none;
+ display: flex;
+ align-items: flex-start;
+ gap: min(calc(40px * var(--pagefind-ui-scale)), 3%);
+ padding: calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));
+ border-top: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border);
+}
+
+.pagefind-modular-list-result:last-of-type {
+ border-bottom: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border);
+}
+
+.pagefind-modular-list-thumb {
+ width: min(30%,
+ calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));
+ max-width: calc(120px * var(--pagefind-ui-scale));
+ margin-top: calc(10px * var(--pagefind-ui-scale));
+ aspect-ratio: var(--pagefind-ui-image-box-ratio);
+ position: relative;
+}
+
+.pagefind-modular-list-image {
+ display: block;
+ position: absolute;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 0;
+ width: auto;
+ height: auto;
+ max-width: 100%;
+ max-height: 100%;
+ border-radius: var(--pagefind-ui-image-border-radius);
+}
+
+.pagefind-modular-list-inner {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ margin-top: calc(10px * var(--pagefind-ui-scale));
+}
+
+.pagefind-modular-list-title {
+ display: inline-block;
+ font-weight: 700;
+ font-size: calc(21px * var(--pagefind-ui-scale));
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.pagefind-modular-list-link {
+ color: var(--pagefind-ui-text);
+ text-decoration: none;
+}
+
+.pagefind-modular-list-link:hover {
+ text-decoration: underline;
+}
+
+.pagefind-modular-list-excerpt {
+ display: inline-block;
+ font-weight: 400;
+ font-size: calc(16px * var(--pagefind-ui-scale));
+ margin-top: calc(4px * var(--pagefind-ui-scale));
+ margin-bottom: 0;
+ min-width: calc(250px * var(--pagefind-ui-scale));
+}
+
+/* FilterPills */
+
+.pagefind-modular-filter-pills-wrapper {
+ overflow-x: scroll;
+ padding: 15px 0;
+}
+
+.pagefind-modular-filter-pills {
+ display: flex;
+ gap: 6px;
+}
+
+.pagefind-modular-filter-pill {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border: none;
+ appearance: none;
+ padding: 0 calc(24px * var(--pagefind-ui-scale));
+ background-color: var(--pagefind-ui-background);
+ color: var(--pagefind-ui-fade);
+ border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);
+ border-radius: calc(25px * var(--pagefind-ui-scale));
+ font-size: calc(18px * var(--pagefind-ui-scale));
+ height: calc(50px * var(--pagefind-ui-scale));
+ cursor: pointer;
+ white-space: nowrap;
+}
+
+.pagefind-modular-filter-pill:hover {
+ border-color: var(--pagefind-ui-primary);
+}
+
+.pagefind-modular-filter-pill[aria-pressed="true"] {
+ border-color: var(--pagefind-ui-primary);
+ color: var(--pagefind-ui-primary);
+} \ No newline at end of file
diff --git a/pagefind/pagefind-modular-ui.js b/pagefind/pagefind-modular-ui.js
new file mode 100644
index 0000000..9301909
--- /dev/null
+++ b/pagefind/pagefind-modular-ui.js
@@ -0,0 +1,8 @@
+(()=>{var b=Object.defineProperty;var w=(i,e)=>{for(var t in e)b(i,t,{get:e[t],enumerable:!0})};var f={};w(f,{FilterPills:()=>h,Input:()=>l,Instance:()=>p,ResultList:()=>a,Summary:()=>o});var r=class i{constructor(e){this.element=document.createElement(e)}id(e){return this.element.id=e,this}class(e){return this.element.classList.add(e),this}attrs(e){for(let[t,s]of Object.entries(e))this.element.setAttribute(t,s);return this}text(e){return this.element.innerText=e,this}html(e){return this.element.innerHTML=e,this}handle(e,t){return this.element.addEventListener(e,t),this}addTo(e){return e instanceof i?e.element.appendChild(this.element):e.appendChild(this.element),this.element}};var T=async(i=100)=>new Promise(e=>setTimeout(e,i)),l=class{constructor(e={}){if(this.inputEl=null,this.clearEl=null,this.instance=null,this.searchID=0,this.debounceTimeoutMs=e.debounceTimeoutMs??300,e.inputElement){if(e.containerElement){console.warn("[Pagefind Input component]: inputElement and containerElement both supplied. Ignoring the container option.");return}this.initExisting(e.inputElement)}else if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Input component]: No selector supplied for containerElement or inputElement");return}this.inputEl.addEventListener("input",async t=>{if(this.instance&&typeof t?.target?.value=="string"){this.updateState(t.target.value);let s=++this.searchID;if(await T(this.debounceTimeoutMs),s!==this.searchID)return null;this.instance?.triggerSearch(t.target.value)}}),this.inputEl.addEventListener("keydown",t=>{t.key==="Escape"&&(++this.searchID,this.inputEl.value="",this.instance?.triggerSearch(""),this.updateState("")),t.key==="Enter"&&t.preventDefault()}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad()})}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No container found for ${e} selector`);return}if(t.tagName==="INPUT")console.warn(`[Pagefind Input component]: Encountered input element for ${e} when a container was expected`),console.warn("[Pagefind Input component]: Treating containerElement option as inputElement and proceeding"),this.initExisting(e);else{t.innerHTML="";let s=0;for(;document.querySelector(`#pfmod-input-${s}`);)s+=1;let n=new r("form").class("pagefind-modular-input-wrapper").attrs({role:"search","aria-label":"Search this site",action:"javascript:void(0);"});new r("label").attrs({for:`pfmod-input-${s}`,"data-pfmod-sr-hidden":"true"}).text("Search this site").addTo(n),this.inputEl=new r("input").id(`pfmod-input-${s}`).class("pagefind-modular-input").attrs({autocapitalize:"none",enterkeyhint:"search"}).addTo(n),this.clearEl=new r("button").class("pagefind-modular-input-clear").attrs({"data-pfmod-suppressed":"true"}).text("Clear").handle("click",()=>{this.inputEl.value="",this.instance.triggerSearch(""),this.updateState("")}).addTo(n),n.addTo(t)}}initExisting(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No input element found for ${e} selector`);return}if(t.tagName!=="INPUT"){console.error(`[Pagefind Input component]: Expected ${e} to be an <input> element`);return}this.inputEl=t}updateState(e){this.clearEl&&(e&&e?.length?this.clearEl.removeAttribute("data-pfmod-suppressed"):this.clearEl.setAttribute("data-pfmod-suppressed","true"))}register(e){this.instance=e,this.instance.on("search",(t,s)=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.updateState(t))})}focus(){this.inputEl&&this.inputEl.focus()}};var g=i=>{if(i instanceof Element)return[i];if(Array.isArray(i)&&i.every(e=>e instanceof Element))return i;if(typeof i=="string"||i instanceof String){let e=document.createElement("div");return e.innerHTML=i,[...e.childNodes]}else return console.error(`[Pagefind ResultList component]: Expected template function to return an HTML element or string, got ${typeof i}`),[]},v=()=>{let i=(e=30)=>". ".repeat(Math.floor(10+Math.random()*e));return`<li class="pagefind-modular-list-result">
+ <div class="pagefind-modular-list-thumb" data-pfmod-loading></div>
+ <div class="pagefind-modular-list-inner">
+ <p class="pagefind-modular-list-title" data-pfmod-loading>${i(30)}</p>
+ <p class="pagefind-modular-list-excerpt" data-pfmod-loading>${i(40)}</p>
+ </div>
+</li>`},y=i=>{let e=new r("li").class("pagefind-modular-list-result"),t=new r("div").class("pagefind-modular-list-thumb").addTo(e);i?.meta?.image&&new r("img").class("pagefind-modular-list-image").attrs({src:i.meta.image,alt:i.meta.image_alt||i.meta.title}).addTo(t);let s=new r("div").class("pagefind-modular-list-inner").addTo(e),n=new r("p").class("pagefind-modular-list-title").addTo(s);return new r("a").class("pagefind-modular-list-link").text(i.meta?.title).attrs({href:i.meta?.url||i.url}).addTo(n),new r("p").class("pagefind-modular-list-excerpt").html(i.excerpt).addTo(s),e.element},E=i=>{if(!(i instanceof HTMLElement))return null;let e=window.getComputedStyle(i).overflowY;return e!=="visible"&&e!=="hidden"?i:E(i.parentNode)},d=class{constructor(e={}){this.rawResult=e.result,this.placeholderNodes=e.placeholderNodes,this.resultFn=e.resultFn,this.intersectionEl=e.intersectionEl,this.result=null,this.waitForIntersection()}waitForIntersection(){if(!this.placeholderNodes?.length)return;let e={root:this.intersectionEl,rootMargin:"0px",threshold:.01};new IntersectionObserver((s,n)=>{this.result===null&&s?.[0]?.isIntersecting&&(this.load(),n.disconnect())},e).observe(this.placeholderNodes[0])}async load(){if(!this.placeholderNodes?.length)return;this.result=await this.rawResult.data();let e=this.resultFn(this.result),t=g(e);for(;this.placeholderNodes.length>1;)this.placeholderNodes.pop().remove();this.placeholderNodes[0].replaceWith(...t)}},a=class{constructor(e){if(this.intersectionEl=document.body,this.containerEl=null,this.results=[],this.placeholderTemplate=e.placeholderTemplate??v,this.resultTemplate=e.resultTemplate??y,e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind ResultList component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind ResultList component]: No container found for ${e} selector`);return}this.containerEl=t}append(e){for(let t of e)this.containerEl.appendChild(t)}register(e){e.on("results",t=>{this.containerEl&&(this.containerEl.innerHTML="",this.intersectionEl=E(this.containerEl),this.results=t.results.map(s=>{let n=g(this.placeholderTemplate());return this.append(n),new d({result:s,placeholderNodes:n,resultFn:this.resultTemplate,intersectionEl:this.intersectionEl})}))}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerHTML="")})}};var o=class{constructor(e={}){if(this.containerEl=null,this.defaultMessage=e.defaultMessage??"",this.term="",e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Summary component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Summary component]: No container found for ${e} selector`);return}this.containerEl=t,this.containerEl.innerText=this.defaultMessage}register(e){e.on("search",(t,s)=>{this.term=t}),e.on("results",t=>{if(!this.containerEl||!t)return;if(!this.term){this.containerEl.innerText=this.defaultMessage;return}let s=t?.results?.length??0;this.containerEl.innerText=`${s} result${s===1?"":"s"} for ${this.term}`}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerText=`Searching for ${this.term}...`)})}};var h=class{constructor(e={}){if(this.instance=null,this.wrapper=null,this.pillContainer=null,this.available={},this.selected=["All"],this.total=0,this.filterMemo="",this.filter=e.filter,this.ordering=e.ordering??null,this.alwaysShow=e.alwaysShow??!1,this.selectMultiple=e.selectMultiple??!1,!this.filter?.length){console.error("[Pagefind FilterPills component]: No filter option supplied, nothing to display");return}if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind FilterPills component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind FilterPills component]: No container found for ${e} selector`);return}t.innerHTML="";let s=`pagefind_modular_filter_pills_${this.filter}`,n=new r("div").class("pagefind-modular-filter-pills-wrapper").attrs({role:"group","aria-labelledby":s});this.alwaysShow||n.attrs({"data-pfmod-hidden":!0}),new r("div").id(s).class("pagefind-modular-filter-pills-label").attrs({"data-pfmod-sr-hidden":!0}).text(`Filter results by ${this.filter}`).addTo(n),this.pillContainer=new r("div").class("pagefind-modular-filter-pills").addTo(n),this.wrapper=n.addTo(t)}update(){let e=this.available.map(t=>t[0]).join("~");e==this.filterMemo?this.updateExisting():(this.renderNew(),this.filterMemo=e)}pushFilters(){let e=this.selected.filter(t=>t!=="All");this.instance.triggerFilter(this.filter,e)}pillInner(e,t){return this.total?`<span aria-label="${e}">${e} (${t})</span>`:`<span aria-label="${e}">${e}</span>`}renderNew(){this.available.forEach(([e,t])=>{new r("button").class("pagefind-modular-filter-pill").html(this.pillInner(e,t)).attrs({"aria-pressed":this.selected.includes(e),type:"button"}).handle("click",()=>{e==="All"?this.selected=["All"]:this.selected.includes(e)?this.selected=this.selected.filter(s=>s!==e):this.selectMultiple?this.selected.push(e):this.selected=[e],this.selected?.length?this.selected?.length>1&&(this.selected=this.selected.filter(s=>s!=="All")):this.selected=["All"],this.update(),this.pushFilters()}).addTo(this.pillContainer)})}updateExisting(){let e=[...this.pillContainer.childNodes];this.available.forEach(([t,s],n)=>{e[n].innerHTML=this.pillInner(t,s),e[n].setAttribute("aria-pressed",this.selected.includes(t))})}register(e){this.instance=e,this.instance.on("filters",t=>{if(!this.pillContainer)return;this.selectMultiple?t=t.available:t=t.total;let s=t[this.filter];if(!s){console.warn(`[Pagefind FilterPills component]: No possible values found for the ${this.filter} filter`);return}this.available=Object.entries(s),Array.isArray(this.ordering)?this.available.sort((n,c)=>{let m=this.ordering.indexOf(n[0]),_=this.ordering.indexOf(c[0]);return(m===-1?1/0:m)-(_===-1?1/0:_)}):this.available.sort((n,c)=>n[0].localeCompare(c[0])),this.available.unshift(["All",this.total]),this.update()}),e.on("results",t=>{this.pillContainer&&(this.total=t?.unfilteredResultCount||0,this.available?.[0]?.[0]==="All"&&(this.available[0][1]=this.total),this.total||this.alwaysShow?this.wrapper.removeAttribute("data-pfmod-hidden"):this.wrapper.setAttribute("data-pfmod-hidden","true"),this.update())})}};var F=async(i=50)=>await new Promise(e=>setTimeout(e,i)),u;try{u=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?modular-ui.js.*$/)[1]}catch{u="/pagefind/"}var p=class{constructor(e={}){this.__pagefind__=null,this.__initializing__=null,this.__searchID__=0,this.__hooks__={search:[],filters:[],loading:[],results:[]},this.components=[],this.searchTerm="",this.searchFilters={},this.searchResult={},this.availableFilters=null,this.totalFilters=null,this.options={bundlePath:e.bundlePath??u,mergeIndex:e.mergeIndex??[]},delete e.bundlePath,delete e.resetStyles,delete e.processResult,delete e.processTerm,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations,this.pagefindOptions=e}add(e){e?.register?.(this),this.components.push(e)}on(e,t){if(!this.__hooks__[e]){let s=Object.keys(this.__hooks__).join(", ");console.error(`[Pagefind Composable]: Unknown event type ${e}. Supported events: [${s}]`);return}if(typeof t!="function"){console.error(`[Pagefind Composable]: Expected callback to be a function, received ${typeof t}`);return}this.__hooks__[e].push(t)}triggerLoad(){this.__load__()}triggerSearch(e){this.searchTerm=e,this.__dispatch__("search",e,this.searchFilters),this.__search__(e,this.searchFilters)}triggerSearchWithFilters(e,t){this.searchTerm=e,this.searchFilters=t,this.__dispatch__("search",e,t),this.__search__(e,t)}triggerFilters(e){this.searchFilters=e,this.__dispatch__("search",this.searchTerm,e),this.__search__(this.searchTerm,e)}triggerFilter(e,t){this.searchFilters=this.searchFilters||{},this.searchFilters[e]=t,this.__dispatch__("search",this.searchTerm,this.searchFilters),this.__search__(this.searchTerm,this.searchFilters)}__dispatch__(e,...t){this.__hooks__[e]?.forEach(s=>s?.(...t))}async __clear__(){this.__dispatch__("results",{results:[],unfilteredTotalCount:0}),this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}async __search__(e,t){this.__dispatch__("loading"),await this.__load__();let s=++this.__searchID__;if(!e||!e.length)return this.__clear__();let n=await this.__pagefind__.search(e,{filters:t});n&&this.__searchID__===s&&(n.filters&&Object.keys(n.filters)?.length&&(this.availableFilters=n.filters,this.totalFilters=n.totalFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})),this.searchResult=n,this.__dispatch__("results",this.searchResult))}async __load__(){if(this.__initializing__){for(;!this.__pagefind__;)await F(50);return}if(this.__initializing__=!0,!this.__pagefind__){let e;try{e=await import(`${this.options.bundlePath}pagefind.js`)}catch(t){console.error(t),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindComposable Instance",`[DEBUG: Loaded from ${document?.currentScript?.src??"no known script location"}]`].join(`
+`))}await e.options(this.pagefindOptions||{});for(let t of this.options.mergeIndex){if(!t.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let s=t.bundlePath;delete t.bundlePath,await e.mergeIndex(s,t)}this.__pagefind__=e}this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}};window.PagefindModularUI=f;})();
diff --git a/pagefind/pagefind-ui.css b/pagefind/pagefind-ui.css
new file mode 100644
index 0000000..d7984a9
--- /dev/null
+++ b/pagefind/pagefind-ui.css
@@ -0,0 +1 @@
+.pagefind-ui__result.svelte-j9e30.svelte-j9e30{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-j9e30.svelte-j9e30:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-thumb.svelte-j9e30.svelte-j9e30{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-j9e30.svelte-j9e30{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-j9e30.svelte-j9e30{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-j9e30.svelte-j9e30{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-j9e30.svelte-j9e30{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-j9e30.svelte-j9e30{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf{display:flex;flex-direction:column;padding-left:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf:first-of-type{padding-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{font-size:.9em;position:relative}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:before{content:"\2937 ";position:absolute;top:0;right:calc(100% + .1em)}.pagefind-ui__result-thumb.svelte-4xnkmf.svelte-4xnkmf{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-4xnkmf.svelte-4xnkmf{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-4xnkmf.svelte-4xnkmf{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-4xnkmf.svelte-4xnkmf{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-4xnkmf.svelte-4xnkmf{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}legend.svelte-1v2r7ls.svelte-1v2r7ls{position:absolute;clip:rect(0 0 0 0)}.pagefind-ui__filter-panel.svelte-1v2r7ls.svelte-1v2r7ls{min-width:min(calc(260px * var(--pagefind-ui-scale)),100%);flex:1;display:flex;flex-direction:column;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{border:0;padding:0}.pagefind-ui__filter-block.svelte-1v2r7ls.svelte-1v2r7ls{padding:0;display:block;border-bottom:solid calc(2px * var(--pagefind-ui-scale)) var(--pagefind-ui-border);padding:calc(20px * var(--pagefind-ui-scale)) 0}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls{font-size:calc(16px * var(--pagefind-ui-scale));position:relative;display:flex;align-items:center;list-style:none;font-weight:700;cursor:pointer;height:calc(24px * var(--pagefind-ui-scale))}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls::-webkit-details-marker{display:none}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls:after{position:absolute;content:"";right:calc(6px * var(--pagefind-ui-scale));top:50%;width:calc(8px * var(--pagefind-ui-scale));height:calc(8px * var(--pagefind-ui-scale));border:solid calc(2px * var(--pagefind-ui-scale)) currentColor;border-right:0;border-top:0;transform:translateY(-70%) rotate(-45deg)}.pagefind-ui__filter-block[open].svelte-1v2r7ls .pagefind-ui__filter-name.svelte-1v2r7ls:after{transform:translateY(-70%) rotate(-225deg)}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{display:flex;flex-direction:column;gap:calc(20px * var(--pagefind-ui-scale));padding-top:calc(30px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls{position:relative;display:flex;align-items:center;gap:calc(8px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls:before{position:absolute;content:"";top:50%;left:calc(8px * var(--pagefind-ui-scale));width:0px;height:0px;border:solid 1px #fff;opacity:0;transform:translate(calc(4.5px * var(--pagefind-ui-scale) * -1),calc(.8px * var(--pagefind-ui-scale))) skew(-5deg) rotate(-45deg);transform-origin:top left;border-top:0;border-right:0;pointer-events:none}.pagefind-ui__filter-value.pagefind-ui__filter-value--checked.svelte-1v2r7ls.svelte-1v2r7ls:before{opacity:1;width:calc(9px * var(--pagefind-ui-scale));height:calc(4px * var(--pagefind-ui-scale));transition:width .1s ease-out .1s,height .1s ease-in}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls{margin:0;width:calc(16px * var(--pagefind-ui-scale));height:calc(16px * var(--pagefind-ui-scale));border:solid 1px var(--pagefind-ui-border);appearance:none;-webkit-appearance:none;border-radius:calc(var(--pagefind-ui-border-radius) / 2);background-color:var(--pagefind-ui-background);cursor:pointer}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls:checked{background-color:var(--pagefind-ui-primary);border:solid 1px var(--pagefind-ui-primary)}.pagefind-ui__filter-label.svelte-1v2r7ls.svelte-1v2r7ls{cursor:pointer;font-size:calc(16px * var(--pagefind-ui-scale));font-weight:400}.pagefind-ui--reset *:where(:not(html,iframe,canvas,img,svg,video):not(svg *,symbol *)){all:unset;display:revert;outline:revert}.pagefind-ui--reset *,.pagefind-ui--reset *:before,.pagefind-ui--reset *:after{box-sizing:border-box}.pagefind-ui--reset a,.pagefind-ui--reset button{cursor:revert}.pagefind-ui--reset ol,.pagefind-ui--reset ul,.pagefind-ui--reset menu{list-style:none}.pagefind-ui--reset img{max-width:100%}.pagefind-ui--reset table{border-collapse:collapse}.pagefind-ui--reset input,.pagefind-ui--reset textarea{-webkit-user-select:auto}.pagefind-ui--reset textarea{white-space:revert}.pagefind-ui--reset meter{-webkit-appearance:revert;appearance:revert}.pagefind-ui--reset ::placeholder{color:unset}.pagefind-ui--reset :where([hidden]){display:none}.pagefind-ui--reset :where([contenteditable]:not([contenteditable="false"])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}.pagefind-ui--reset :where([draggable="true"]){-webkit-user-drag:element}.pagefind-ui--reset mark{all:revert}:root{--pagefind-ui-scale:.8;--pagefind-ui-primary:#393939;--pagefind-ui-text:#393939;--pagefind-ui-background:#ffffff;--pagefind-ui-border:#eeeeee;--pagefind-ui-tag:#eeeeee;--pagefind-ui-border-width:2px;--pagefind-ui-border-radius:8px;--pagefind-ui-image-border-radius:8px;--pagefind-ui-image-box-ratio:3 / 2;--pagefind-ui-font:system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif}.pagefind-ui.svelte-e9gkc3{width:100%;color:var(--pagefind-ui-text);font-family:var(--pagefind-ui-font)}.pagefind-ui__hidden.svelte-e9gkc3{display:none!important}.pagefind-ui__suppressed.svelte-e9gkc3{opacity:0;pointer-events:none}.pagefind-ui__form.svelte-e9gkc3{position:relative}.pagefind-ui__form.svelte-e9gkc3:before{background-color:var(--pagefind-ui-text);width:calc(18px * var(--pagefind-ui-scale));height:calc(18px * var(--pagefind-ui-scale));top:calc(23px * var(--pagefind-ui-scale));left:calc(20px * var(--pagefind-ui-scale));content:"";position:absolute;display:block;opacity:.7;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");-webkit-mask-size:100%;mask-size:100%;z-index:9;pointer-events:none}.pagefind-ui__search-input.svelte-e9gkc3{height:calc(64px * var(--pagefind-ui-scale));padding:0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));background-color:var(--pagefind-ui-background);border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);font-size:calc(21px * var(--pagefind-ui-scale));position:relative;appearance:none;-webkit-appearance:none;display:flex;width:100%;box-sizing:border-box;font-weight:700}.pagefind-ui__search-input.svelte-e9gkc3::placeholder{opacity:.2}.pagefind-ui__search-clear.svelte-e9gkc3{position:absolute;top:calc(3px * var(--pagefind-ui-scale));right:calc(3px * var(--pagefind-ui-scale));height:calc(58px * var(--pagefind-ui-scale));padding:0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));color:var(--pagefind-ui-text);font-size:calc(14px * var(--pagefind-ui-scale));cursor:pointer;background-color:var(--pagefind-ui-background);border-radius:var(--pagefind-ui-border-radius)}.pagefind-ui__drawer.svelte-e9gkc3{gap:calc(60px * var(--pagefind-ui-scale));display:flex;flex-direction:row;flex-wrap:wrap}.pagefind-ui__results-area.svelte-e9gkc3{min-width:min(calc(400px * var(--pagefind-ui-scale)),100%);flex:1000;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__results.svelte-e9gkc3{padding:0}.pagefind-ui__message.svelte-e9gkc3{box-sizing:content-box;font-size:calc(16px * var(--pagefind-ui-scale));height:calc(24px * var(--pagefind-ui-scale));padding:calc(20px * var(--pagefind-ui-scale)) 0;display:flex;align-items:center;font-weight:700;margin-top:0}.pagefind-ui__button.svelte-e9gkc3{margin-top:calc(40px * var(--pagefind-ui-scale));border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);height:calc(48px * var(--pagefind-ui-scale));padding:0 calc(12px * var(--pagefind-ui-scale));font-size:calc(16px * var(--pagefind-ui-scale));color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background);width:100%;text-align:center;font-weight:700;cursor:pointer}.pagefind-ui__button.svelte-e9gkc3:hover{border-color:var(--pagefind-ui-primary);color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background)}
diff --git a/pagefind/pagefind-ui.js b/pagefind/pagefind-ui.js
new file mode 100644
index 0000000..e964eca
--- /dev/null
+++ b/pagefind/pagefind-ui.js
@@ -0,0 +1,2 @@
+(()=>{var is=Object.defineProperty;var v=(n,e)=>{for(var t in e)is(n,t,{get:e[t],enumerable:!0})};function j(){}function lt(n){return n()}function Qt(){return Object.create(null)}function V(n){n.forEach(lt)}function Ye(n){return typeof n=="function"}function G(n,e){return n!=n?e==e:n!==e||n&&typeof n=="object"||typeof n=="function"}var Ke;function le(n,e){return Ke||(Ke=document.createElement("a")),Ke.href=e,n===Ke.href}function xt(n){return Object.keys(n).length===0}var $t=typeof window<"u"?window:typeof globalThis<"u"?globalThis:global,fe=class{constructor(e){this.options=e,this._listeners="WeakMap"in $t?new WeakMap:void 0}observe(e,t){return this._listeners.set(e,t),this._getObserver().observe(e,this.options),()=>{this._listeners.delete(e),this._observer.unobserve(e)}}_getObserver(){var e;return(e=this._observer)!==null&&e!==void 0?e:this._observer=new ResizeObserver(t=>{var s;for(let r of t)fe.entries.set(r.target,r),(s=this._listeners.get(r.target))===null||s===void 0||s(r)})}};fe.entries="WeakMap"in $t?new WeakMap:void 0;var en=!1;function as(){en=!0}function os(){en=!1}function b(n,e){n.appendChild(e)}function y(n,e,t){n.insertBefore(e,t||null)}function C(n){n.parentNode&&n.parentNode.removeChild(n)}function Q(n,e){for(let t=0;t<n.length;t+=1)n[t]&&n[t].d(e)}function k(n){return document.createElement(n)}function us(n){return document.createElementNS("http://www.w3.org/2000/svg",n)}function A(n){return document.createTextNode(n)}function M(){return A(" ")}function x(){return A("")}function K(n,e,t,s){return n.addEventListener(e,t,s),()=>n.removeEventListener(e,t,s)}function p(n,e,t){t==null?n.removeAttribute(e):n.getAttribute(e)!==t&&n.setAttribute(e,t)}function cs(n){return Array.from(n.childNodes)}function N(n,e){e=""+e,n.data!==e&&(n.data=e)}function it(n,e){n.value=e??""}function W(n,e,t){n.classList[t?"add":"remove"](e)}var Xe=class{constructor(e=!1){this.is_svg=!1,this.is_svg=e,this.e=this.n=null}c(e){this.h(e)}m(e,t,s=null){this.e||(this.is_svg?this.e=us(t.nodeName):this.e=k(t.nodeType===11?"TEMPLATE":t.nodeName),this.t=t.tagName!=="TEMPLATE"?t:t.content,this.c(e)),this.i(s)}h(e){this.e.innerHTML=e,this.n=Array.from(this.e.nodeName==="TEMPLATE"?this.e.content.childNodes:this.e.childNodes)}i(e){for(let t=0;t<this.n.length;t+=1)y(this.t,this.n[t],e)}p(e){this.d(),this.h(e),this.i(this.a)}d(){this.n.forEach(C)}};var de;function _e(n){de=n}function tn(){if(!de)throw new Error("Function called outside component initialization");return de}function at(n){tn().$$.on_mount.push(n)}function ot(n){tn().$$.on_destroy.push(n)}var ne=[];var re=[],se=[],nt=[],_s=Promise.resolve(),st=!1;function fs(){st||(st=!0,_s.then(sn))}function rt(n){se.push(n)}function nn(n){nt.push(n)}var tt=new Set,te=0;function sn(){if(te!==0)return;let n=de;do{try{for(;te<ne.length;){let e=ne[te];te++,_e(e),ds(e.$$)}}catch(e){throw ne.length=0,te=0,e}for(_e(null),ne.length=0,te=0;re.length;)re.pop()();for(let e=0;e<se.length;e+=1){let t=se[e];tt.has(t)||(tt.add(t),t())}se.length=0}while(ne.length);for(;nt.length;)nt.pop()();st=!1,tt.clear(),_e(n)}function ds(n){if(n.fragment!==null){n.update(),V(n.before_update);let e=n.dirty;n.dirty=[-1],n.fragment&&n.fragment.p(n.ctx,e),n.after_update.forEach(rt)}}function hs(n){let e=[],t=[];se.forEach(s=>n.indexOf(s)===-1?e.push(s):t.push(s)),t.forEach(s=>s()),se=e}var Je=new Set,ee;function ie(){ee={r:0,c:[],p:ee}}function ae(){ee.r||V(ee.c),ee=ee.p}function z(n,e){n&&n.i&&(Je.delete(n),n.i(e))}function I(n,e,t,s){if(n&&n.o){if(Je.has(n))return;Je.add(n),ee.c.push(()=>{Je.delete(n),s&&(t&&n.d(1),s())}),n.o(e)}else s&&s()}function rn(n,e){I(n,1,1,()=>{e.delete(n.key)})}function ln(n,e,t,s,r,l,i,a,o,h,_,f){let c=n.length,E=l.length,u=c,m={};for(;u--;)m[n[u].key]=u;let d=[],R=new Map,T=new Map,S=[];for(u=E;u--;){let F=f(r,l,u),U=t(F),P=i.get(U);P?s&&S.push(()=>P.p(F,e)):(P=h(U,F),P.c()),R.set(U,d[u]=P),U in m&&T.set(U,Math.abs(u-m[U]))}let w=new Set,B=new Set;function X(F){z(F,1),F.m(a,_),i.set(F.key,F),_=F.first,E--}for(;c&&E;){let F=d[E-1],U=n[c-1],P=F.key,Z=U.key;F===U?(_=F.first,c--,E--):R.has(Z)?!i.has(P)||w.has(P)?X(F):B.has(Z)?c--:T.get(P)>T.get(Z)?(B.add(P),X(F)):(w.add(Z),c--):(o(U,i),c--)}for(;c--;){let F=n[c];R.has(F.key)||o(F,i)}for(;E;)X(d[E-1]);return V(S),d}var ms=["allowfullscreen","allowpaymentrequest","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","inert","ismap","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"],Fi=new Set([...ms]);function an(n,e,t){let s=n.$$.props[e];s!==void 0&&(n.$$.bound[s]=t,t(n.$$.ctx[s]))}function Ze(n){n&&n.c()}function he(n,e,t,s){let{fragment:r,after_update:l}=n.$$;r&&r.m(e,t),s||rt(()=>{let i=n.$$.on_mount.map(lt).filter(Ye);n.$$.on_destroy?n.$$.on_destroy.push(...i):V(i),n.$$.on_mount=[]}),l.forEach(rt)}function oe(n,e){let t=n.$$;t.fragment!==null&&(hs(t.after_update),V(t.on_destroy),t.fragment&&t.fragment.d(e),t.on_destroy=t.fragment=null,t.ctx=[])}function ps(n,e){n.$$.dirty[0]===-1&&(ne.push(n),fs(),n.$$.dirty.fill(0)),n.$$.dirty[e/31|0]|=1<<e%31}function J(n,e,t,s,r,l,i,a=[-1]){let o=de;_e(n);let h=n.$$={fragment:null,ctx:[],props:l,update:j,not_equal:r,bound:Qt(),on_mount:[],on_destroy:[],on_disconnect:[],before_update:[],after_update:[],context:new Map(e.context||(o?o.$$.context:[])),callbacks:Qt(),dirty:a,skip_bound:!1,root:e.target||o.$$.root};i&&i(h.root);let _=!1;if(h.ctx=t?t(n,e.props||{},(f,c,...E)=>{let u=E.length?E[0]:c;return h.ctx&&r(h.ctx[f],h.ctx[f]=u)&&(!h.skip_bound&&h.bound[f]&&h.bound[f](u),_&&ps(n,f)),c}):[],h.update(),_=!0,V(h.before_update),h.fragment=s?s(h.ctx):!1,e.target){if(e.hydrate){as();let f=cs(e.target);h.fragment&&h.fragment.l(f),f.forEach(C)}else h.fragment&&h.fragment.c();e.intro&&z(n.$$.fragment),he(n,e.target,e.anchor,e.customElement),os(),sn()}_e(o)}var gs;typeof HTMLElement=="function"&&(gs=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){let{on_mount:n}=this.$$;this.$$.on_disconnect=n.map(lt).filter(Ye);for(let e in this.$$.slotted)this.appendChild(this.$$.slotted[e])}attributeChangedCallback(n,e,t){this[n]=t}disconnectedCallback(){V(this.$$.on_disconnect)}$destroy(){oe(this,1),this.$destroy=j}$on(n,e){if(!Ye(e))return j;let t=this.$$.callbacks[n]||(this.$$.callbacks[n]=[]);return t.push(e),()=>{let s=t.indexOf(e);s!==-1&&t.splice(s,1)}}$set(n){this.$$set&&!xt(n)&&(this.$$.skip_bound=!0,this.$$set(n),this.$$.skip_bound=!1)}});var q=class{$destroy(){oe(this,1),this.$destroy=j}$on(e,t){if(!Ye(t))return j;let s=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return s.push(t),()=>{let r=s.indexOf(t);r!==-1&&s.splice(r,1)}}$set(e){this.$$set&&!xt(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}};function D(n){let e=typeof n=="string"?n.charCodeAt(0):n;return e>=97&&e<=122||e>=65&&e<=90}function $(n){let e=typeof n=="string"?n.charCodeAt(0):n;return e>=48&&e<=57}function Y(n){return D(n)||$(n)}var on=["art-lojban","cel-gaulish","no-bok","no-nyn","zh-guoyu","zh-hakka","zh-min","zh-min-nan","zh-xiang"];var ut={"en-gb-oed":"en-GB-oxendict","i-ami":"ami","i-bnn":"bnn","i-default":null,"i-enochian":null,"i-hak":"hak","i-klingon":"tlh","i-lux":"lb","i-mingo":null,"i-navajo":"nv","i-pwn":"pwn","i-tao":"tao","i-tay":"tay","i-tsu":"tsu","sgn-be-fr":"sfb","sgn-be-nl":"vgt","sgn-ch-de":"sgg","art-lojban":"jbo","cel-gaulish":null,"no-bok":"nb","no-nyn":"nn","zh-guoyu":"cmn","zh-hakka":"hak","zh-min":null,"zh-min-nan":"nan","zh-xiang":"hsn"};var Es={}.hasOwnProperty;function Qe(n,e={}){let t=un(),s=String(n),r=s.toLowerCase(),l=0;if(n==null)throw new Error("Expected string, got `"+n+"`");if(Es.call(ut,r)){let a=ut[r];return(e.normalize===void 0||e.normalize===null||e.normalize)&&typeof a=="string"?Qe(a):(t[on.includes(r)?"regular":"irregular"]=s,t)}for(;D(r.charCodeAt(l))&&l<9;)l++;if(l>1&&l<9){if(t.language=s.slice(0,l),l<4){let a=0;for(;r.charCodeAt(l)===45&&D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&D(r.charCodeAt(l+3))&&!D(r.charCodeAt(l+4));){if(a>2)return i(l,3,"Too many extended language subtags, expected at most 3 subtags");t.extendedLanguageSubtags.push(s.slice(l+1,l+4)),l+=4,a++}}for(r.charCodeAt(l)===45&&D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&D(r.charCodeAt(l+3))&&D(r.charCodeAt(l+4))&&!D(r.charCodeAt(l+5))&&(t.script=s.slice(l+1,l+5),l+=5),r.charCodeAt(l)===45&&(D(r.charCodeAt(l+1))&&D(r.charCodeAt(l+2))&&!D(r.charCodeAt(l+3))?(t.region=s.slice(l+1,l+3),l+=3):$(r.charCodeAt(l+1))&&$(r.charCodeAt(l+2))&&$(r.charCodeAt(l+3))&&!$(r.charCodeAt(l+4))&&(t.region=s.slice(l+1,l+4),l+=4));r.charCodeAt(l)===45;){let a=l+1,o=a;for(;Y(r.charCodeAt(o));){if(o-a>7)return i(o,1,"Too long variant, expected at most 8 characters");o++}if(o-a>4||o-a>3&&$(r.charCodeAt(a)))t.variants.push(s.slice(a,o)),l=o;else break}for(;r.charCodeAt(l)===45&&!(r.charCodeAt(l+1)===120||!Y(r.charCodeAt(l+1))||r.charCodeAt(l+2)!==45||!Y(r.charCodeAt(l+3)));){let a=l+2,o=0;for(;r.charCodeAt(a)===45&&Y(r.charCodeAt(a+1))&&Y(r.charCodeAt(a+2));){let h=a+1;for(a=h+2,o++;Y(r.charCodeAt(a));){if(a-h>7)return i(a,2,"Too long extension, expected at most 8 characters");a++}}if(!o)return i(a,4,"Empty extension, extensions must have at least 2 characters of content");t.extensions.push({singleton:s.charAt(l+1),extensions:s.slice(l+3,a).split("-")}),l=a}}else l=0;if(l===0&&r.charCodeAt(l)===120||r.charCodeAt(l)===45&&r.charCodeAt(l+1)===120){l=l?l+2:1;let a=l;for(;r.charCodeAt(a)===45&&Y(r.charCodeAt(a+1));){let o=l+1;for(a=o;Y(r.charCodeAt(a));){if(a-o>7)return i(a,5,"Too long private-use area, expected at most 8 characters");a++}t.privateuse.push(s.slice(l+1,a)),l=a}}if(l!==s.length)return i(l,6,"Found superfluous content after tag");return t;function i(a,o,h){return e.warning&&e.warning(h,o,a),e.forgiving?t:un()}}function un(){return{language:null,extendedLanguageSubtags:[],script:null,region:null,variants:[],extensions:[],privateuse:[],irregular:null,regular:null}}function cn(n,e,t){let s=n.slice();return s[8]=e[t][0],s[9]=e[t][1],s}function bs(n){let e,t,s,r,l,i=n[0]&&_n(n);return{c(){i&&i.c(),e=M(),t=k("div"),s=k("p"),s.textContent=`${n[3](30)}`,r=M(),l=k("p"),l.textContent=`${n[3](40)}`,p(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-j9e30"),p(l,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-j9e30"),p(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(a,o){i&&i.m(a,o),y(a,e,o),y(a,t,o),b(t,s),b(t,r),b(t,l)},p(a,o){a[0]?i||(i=_n(a),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(a){i&&i.d(a),a&&C(e),a&&C(t)}}}function Rs(n){let e,t,s,r,l=n[1].meta?.title+"",i,a,o,h,_=n[1].excerpt+"",f,c=n[0]&&fn(n),E=n[2].length&&hn(n);return{c(){c&&c.c(),e=M(),t=k("div"),s=k("p"),r=k("a"),i=A(l),o=M(),h=k("p"),f=M(),E&&E.c(),p(r,"class","pagefind-ui__result-link svelte-j9e30"),p(r,"href",a=n[1].meta?.url||n[1].url),p(s,"class","pagefind-ui__result-title svelte-j9e30"),p(h,"class","pagefind-ui__result-excerpt svelte-j9e30"),p(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(u,m){c&&c.m(u,m),y(u,e,m),y(u,t,m),b(t,s),b(s,r),b(r,i),b(t,o),b(t,h),h.innerHTML=_,b(t,f),E&&E.m(t,null)},p(u,m){u[0]?c?c.p(u,m):(c=fn(u),c.c(),c.m(e.parentNode,e)):c&&(c.d(1),c=null),m&2&&l!==(l=u[1].meta?.title+"")&&N(i,l),m&2&&a!==(a=u[1].meta?.url||u[1].url)&&p(r,"href",a),m&2&&_!==(_=u[1].excerpt+"")&&(h.innerHTML=_),u[2].length?E?E.p(u,m):(E=hn(u),E.c(),E.m(t,null)):E&&(E.d(1),E=null)},d(u){c&&c.d(u),u&&C(e),u&&C(t),E&&E.d()}}}function _n(n){let e;return{c(){e=k("div"),p(e,"class","pagefind-ui__result-thumb pagefind-ui__loading svelte-j9e30")},m(t,s){y(t,e,s)},d(t){t&&C(e)}}}function fn(n){let e,t=n[1].meta.image&&dn(n);return{c(){e=k("div"),t&&t.c(),p(e,"class","pagefind-ui__result-thumb svelte-j9e30")},m(s,r){y(s,e,r),t&&t.m(e,null)},p(s,r){s[1].meta.image?t?t.p(s,r):(t=dn(s),t.c(),t.m(e,null)):t&&(t.d(1),t=null)},d(s){s&&C(e),t&&t.d()}}}function dn(n){let e,t,s;return{c(){e=k("img"),p(e,"class","pagefind-ui__result-image svelte-j9e30"),le(e.src,t=n[1].meta?.image)||p(e,"src",t),p(e,"alt",s=n[1].meta?.image_alt||n[1].meta?.title)},m(r,l){y(r,e,l)},p(r,l){l&2&&!le(e.src,t=r[1].meta?.image)&&p(e,"src",t),l&2&&s!==(s=r[1].meta?.image_alt||r[1].meta?.title)&&p(e,"alt",s)},d(r){r&&C(e)}}}function hn(n){let e,t=n[2],s=[];for(let r=0;r<t.length;r+=1)s[r]=mn(cn(n,t,r));return{c(){e=k("ul");for(let r=0;r<s.length;r+=1)s[r].c();p(e,"class","pagefind-ui__result-tags svelte-j9e30")},m(r,l){y(r,e,l);for(let i=0;i<s.length;i+=1)s[i]&&s[i].m(e,null)},p(r,l){if(l&4){t=r[2];let i;for(i=0;i<t.length;i+=1){let a=cn(r,t,i);s[i]?s[i].p(a,l):(s[i]=mn(a),s[i].c(),s[i].m(e,null))}for(;i<s.length;i+=1)s[i].d(1);s.length=t.length}},d(r){r&&C(e),Q(s,r)}}}function mn(n){let e,t=n[8].replace(/^(\w)/,pn)+"",s,r,l=n[9]+"",i,a;return{c(){e=k("li"),s=A(t),r=A(": "),i=A(l),a=M(),p(e,"class","pagefind-ui__result-tag svelte-j9e30")},m(o,h){y(o,e,h),b(e,s),b(e,r),b(e,i),b(e,a)},p(o,h){h&4&&t!==(t=o[8].replace(/^(\w)/,pn)+"")&&N(s,t),h&4&&l!==(l=o[9]+"")&&N(i,l)},d(o){o&&C(e)}}}function Ts(n){let e;function t(l,i){return l[1]?Rs:bs}let s=t(n,-1),r=s(n);return{c(){e=k("li"),r.c(),p(e,"class","pagefind-ui__result svelte-j9e30")},m(l,i){y(l,e,i),r.m(e,null)},p(l,[i]){s===(s=t(l,i))&&r?r.p(l,i):(r.d(1),r=s(l),r&&(r.c(),r.m(e,null)))},i:j,o:j,d(l){l&&C(e),r.d()}}}var pn=n=>n.toLocaleUpperCase();function ks(n,e,t){let{show_images:s=!0}=e,{process_result:r=null}=e,{result:l={data:async()=>{}}}=e,i=["title","image","image_alt","url"],a,o=[],h=async f=>{t(1,a=await f.data()),t(1,a=r?.(a)??a),t(2,o=Object.entries(a.meta).filter(([c])=>!i.includes(c)))},_=(f=30)=>". ".repeat(Math.floor(10+Math.random()*f));return n.$$set=f=>{"show_images"in f&&t(0,s=f.show_images),"process_result"in f&&t(4,r=f.process_result),"result"in f&&t(5,l=f.result)},n.$$.update=()=>{if(n.$$.dirty&32)e:h(l)},[s,a,o,_,r,l]}var ct=class extends q{constructor(e){super(),J(this,e,ks,Ts,G,{show_images:0,process_result:4,result:5})}},gn=ct;function En(n,e,t){let s=n.slice();return s[11]=e[t][0],s[12]=e[t][1],s}function bn(n,e,t){let s=n.slice();return s[15]=e[t],s}function Cs(n){let e,t,s,r,l,i=n[0]&&Rn(n);return{c(){i&&i.c(),e=M(),t=k("div"),s=k("p"),s.textContent=`${n[5](30)}`,r=M(),l=k("p"),l.textContent=`${n[5](40)}`,p(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-4xnkmf"),p(l,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-4xnkmf"),p(t,"class","pagefind-ui__result-inner svelte-4xnkmf")},m(a,o){i&&i.m(a,o),y(a,e,o),y(a,t,o),b(t,s),b(t,r),b(t,l)},p(a,o){a[0]?i||(i=Rn(a),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(a){i&&i.d(a),a&&C(e),a&&C(t)}}}function ys(n){let e,t,s,r,l=n[1].meta?.title+"",i,a,o,h,_,f=n[0]&&Tn(n),c=n[4]&&Cn(n),E=n[3],u=[];for(let d=0;d<E.length;d+=1)u[d]=yn(bn(n,E,d));let m=n[2].length&&Sn(n);return{c(){f&&f.c(),e=M(),t=k("div"),s=k("p"),r=k("a"),i=A(l),o=M(),c&&c.c(),h=M();for(let d=0;d<u.length;d+=1)u[d].c();_=M(),m&&m.c(),p(r,"class","pagefind-ui__result-link svelte-4xnkmf"),p(r,"href",a=n[1].meta?.url||n[1].url),p(s,"class","pagefind-ui__result-title svelte-4xnkmf"),p(t,"class","pagefind-ui__result-inner svelte-4xnkmf")},m(d,R){f&&f.m(d,R),y(d,e,R),y(d,t,R),b(t,s),b(s,r),b(r,i),b(t,o),c&&c.m(t,null),b(t,h);for(let T=0;T<u.length;T+=1)u[T]&&u[T].m(t,null);b(t,_),m&&m.m(t,null)},p(d,R){if(d[0]?f?f.p(d,R):(f=Tn(d),f.c(),f.m(e.parentNode,e)):f&&(f.d(1),f=null),R&2&&l!==(l=d[1].meta?.title+"")&&N(i,l),R&2&&a!==(a=d[1].meta?.url||d[1].url)&&p(r,"href",a),d[4]?c?c.p(d,R):(c=Cn(d),c.c(),c.m(t,h)):c&&(c.d(1),c=null),R&8){E=d[3];let T;for(T=0;T<E.length;T+=1){let S=bn(d,E,T);u[T]?u[T].p(S,R):(u[T]=yn(S),u[T].c(),u[T].m(t,_))}for(;T<u.length;T+=1)u[T].d(1);u.length=E.length}d[2].length?m?m.p(d,R):(m=Sn(d),m.c(),m.m(t,null)):m&&(m.d(1),m=null)},d(d){f&&f.d(d),d&&C(e),d&&C(t),c&&c.d(),Q(u,d),m&&m.d()}}}function Rn(n){let e;return{c(){e=k("div"),p(e,"class","pagefind-ui__result-thumb pagefind-ui__loading svelte-4xnkmf")},m(t,s){y(t,e,s)},d(t){t&&C(e)}}}function Tn(n){let e,t=n[1].meta.image&&kn(n);return{c(){e=k("div"),t&&t.c(),p(e,"class","pagefind-ui__result-thumb svelte-4xnkmf")},m(s,r){y(s,e,r),t&&t.m(e,null)},p(s,r){s[1].meta.image?t?t.p(s,r):(t=kn(s),t.c(),t.m(e,null)):t&&(t.d(1),t=null)},d(s){s&&C(e),t&&t.d()}}}function kn(n){let e,t,s;return{c(){e=k("img"),p(e,"class","pagefind-ui__result-image svelte-4xnkmf"),le(e.src,t=n[1].meta?.image)||p(e,"src",t),p(e,"alt",s=n[1].meta?.image_alt||n[1].meta?.title)},m(r,l){y(r,e,l)},p(r,l){l&2&&!le(e.src,t=r[1].meta?.image)&&p(e,"src",t),l&2&&s!==(s=r[1].meta?.image_alt||r[1].meta?.title)&&p(e,"alt",s)},d(r){r&&C(e)}}}function Cn(n){let e,t=n[1].excerpt+"";return{c(){e=k("p"),p(e,"class","pagefind-ui__result-excerpt svelte-4xnkmf")},m(s,r){y(s,e,r),e.innerHTML=t},p(s,r){r&2&&t!==(t=s[1].excerpt+"")&&(e.innerHTML=t)},d(s){s&&C(e)}}}function yn(n){let e,t,s,r=n[15].title+"",l,i,a,o,h=n[15].excerpt+"";return{c(){e=k("div"),t=k("p"),s=k("a"),l=A(r),a=M(),o=k("p"),p(s,"class","pagefind-ui__result-link svelte-4xnkmf"),p(s,"href",i=n[15].url),p(t,"class","pagefind-ui__result-title svelte-4xnkmf"),p(o,"class","pagefind-ui__result-excerpt svelte-4xnkmf"),p(e,"class","pagefind-ui__result-nested svelte-4xnkmf")},m(_,f){y(_,e,f),b(e,t),b(t,s),b(s,l),b(e,a),b(e,o),o.innerHTML=h},p(_,f){f&8&&r!==(r=_[15].title+"")&&N(l,r),f&8&&i!==(i=_[15].url)&&p(s,"href",i),f&8&&h!==(h=_[15].excerpt+"")&&(o.innerHTML=h)},d(_){_&&C(e)}}}function Sn(n){let e,t=n[2],s=[];for(let r=0;r<t.length;r+=1)s[r]=vn(En(n,t,r));return{c(){e=k("ul");for(let r=0;r<s.length;r+=1)s[r].c();p(e,"class","pagefind-ui__result-tags svelte-4xnkmf")},m(r,l){y(r,e,l);for(let i=0;i<s.length;i+=1)s[i]&&s[i].m(e,null)},p(r,l){if(l&4){t=r[2];let i;for(i=0;i<t.length;i+=1){let a=En(r,t,i);s[i]?s[i].p(a,l):(s[i]=vn(a),s[i].c(),s[i].m(e,null))}for(;i<s.length;i+=1)s[i].d(1);s.length=t.length}},d(r){r&&C(e),Q(s,r)}}}function vn(n){let e,t=n[11].replace(/^(\w)/,Mn)+"",s,r,l=n[12]+"",i,a;return{c(){e=k("li"),s=A(t),r=A(": "),i=A(l),a=M(),p(e,"class","pagefind-ui__result-tag svelte-4xnkmf")},m(o,h){y(o,e,h),b(e,s),b(e,r),b(e,i),b(e,a)},p(o,h){h&4&&t!==(t=o[11].replace(/^(\w)/,Mn)+"")&&N(s,t),h&4&&l!==(l=o[12]+"")&&N(i,l)},d(o){o&&C(e)}}}function Ss(n){let e;function t(l,i){return l[1]?ys:Cs}let s=t(n,-1),r=s(n);return{c(){e=k("li"),r.c(),p(e,"class","pagefind-ui__result svelte-4xnkmf")},m(l,i){y(l,e,i),r.m(e,null)},p(l,[i]){s===(s=t(l,i))&&r?r.p(l,i):(r.d(1),r=s(l),r&&(r.c(),r.m(e,null)))},i:j,o:j,d(l){l&&C(e),r.d()}}}var Mn=n=>n.toLocaleUpperCase();function vs(n,e,t){let{show_images:s=!0}=e,{process_result:r=null}=e,{result:l={data:async()=>{}}}=e,i=["title","image","image_alt","url"],a,o=[],h=[],_=!1,f=(u,m)=>{if(u.length<=m)return u;let d=[...u].sort((R,T)=>T.locations.length-R.locations.length).slice(0,3).map(R=>R.url);return u.filter(R=>d.includes(R.url))},c=async u=>{t(1,a=await u.data()),t(1,a=r?.(a)??a),t(2,o=Object.entries(a.meta).filter(([m])=>!i.includes(m))),Array.isArray(a.sub_results)&&(t(4,_=a.sub_results?.[0]?.url===(a.meta?.url||a.url)),_?t(3,h=f(a.sub_results.slice(1),3)):t(3,h=f([...a.sub_results],3)))},E=(u=30)=>". ".repeat(Math.floor(10+Math.random()*u));return n.$$set=u=>{"show_images"in u&&t(0,s=u.show_images),"process_result"in u&&t(6,r=u.process_result),"result"in u&&t(7,l=u.result)},n.$$.update=()=>{if(n.$$.dirty&128)e:c(l)},[s,a,o,h,_,E,r,l]}var _t=class extends q{constructor(e){super(),J(this,e,vs,Ss,G,{show_images:0,process_result:6,result:7})}},An=_t;function wn(n,e,t){let s=n.slice();return s[9]=e[t][0],s[10]=e[t][1],s[11]=e,s[12]=t,s}function Fn(n,e,t){let s=n.slice();return s[13]=e[t][0],s[14]=e[t][1],s[15]=e,s[16]=t,s}function Hn(n){let e,t,s=n[3]("filters_label",n[4],n[5])+"",r,l,i=Object.entries(n[1]),a=[];for(let o=0;o<i.length;o+=1)a[o]=jn(wn(n,i,o));return{c(){e=k("fieldset"),t=k("legend"),r=A(s),l=M();for(let o=0;o<a.length;o+=1)a[o].c();p(t,"class","pagefind-ui__filter-panel-label svelte-1v2r7ls"),p(e,"class","pagefind-ui__filter-panel svelte-1v2r7ls")},m(o,h){y(o,e,h),b(e,t),b(t,r),b(e,l);for(let _=0;_<a.length;_+=1)a[_]&&a[_].m(e,null)},p(o,h){if(h&56&&s!==(s=o[3]("filters_label",o[4],o[5])+"")&&N(r,s),h&71){i=Object.entries(o[1]);let _;for(_=0;_<i.length;_+=1){let f=wn(o,i,_);a[_]?a[_].p(f,h):(a[_]=jn(f),a[_].c(),a[_].m(e,null))}for(;_<a.length;_+=1)a[_].d(1);a.length=i.length}},d(o){o&&C(e),Q(a,o)}}}function Nn(n){let e,t,s,r,l,i,a,o,h=n[13]+"",_,f=n[14]+"",c,E,u,m,d,R;function T(){n[8].call(t,n[9],n[13])}return{c(){e=k("div"),t=k("input"),i=M(),a=k("label"),o=new Xe(!1),_=A(" ("),c=A(f),E=A(")"),m=M(),p(t,"class","pagefind-ui__filter-checkbox svelte-1v2r7ls"),p(t,"type","checkbox"),p(t,"id",s=n[9]+"-"+n[13]),p(t,"name",r=n[9]),t.__value=l=n[13],t.value=t.__value,o.a=_,p(a,"class","pagefind-ui__filter-label svelte-1v2r7ls"),p(a,"for",u=n[9]+"-"+n[13]),p(e,"class","pagefind-ui__filter-value svelte-1v2r7ls"),W(e,"pagefind-ui__filter-value--checked",n[0][`${n[9]}:${n[13]}`])},m(S,w){y(S,e,w),b(e,t),t.checked=n[0][`${n[9]}:${n[13]}`],b(e,i),b(e,a),o.m(h,a),b(a,_),b(a,c),b(a,E),b(e,m),d||(R=K(t,"change",T),d=!0)},p(S,w){n=S,w&2&&s!==(s=n[9]+"-"+n[13])&&p(t,"id",s),w&2&&r!==(r=n[9])&&p(t,"name",r),w&2&&l!==(l=n[13])&&(t.__value=l,t.value=t.__value),w&3&&(t.checked=n[0][`${n[9]}:${n[13]}`]),w&2&&h!==(h=n[13]+"")&&o.p(h),w&2&&f!==(f=n[14]+"")&&N(c,f),w&2&&u!==(u=n[9]+"-"+n[13])&&p(a,"for",u),w&3&&W(e,"pagefind-ui__filter-value--checked",n[0][`${n[9]}:${n[13]}`])},d(S){S&&C(e),d=!1,R()}}}function On(n){let e,t=(n[2]||n[14]||n[0][`${n[9]}:${n[13]}`])&&Nn(n);return{c(){t&&t.c(),e=x()},m(s,r){t&&t.m(s,r),y(s,e,r)},p(s,r){s[2]||s[14]||s[0][`${s[9]}:${s[13]}`]?t?t.p(s,r):(t=Nn(s),t.c(),t.m(e.parentNode,e)):t&&(t.d(1),t=null)},d(s){t&&t.d(s),s&&C(e)}}}function jn(n){let e,t,s=n[9].replace(/^(\w)/,zn)+"",r,l,i,a=n[9]+"",o,h,_=Object.entries(n[10]||{}),f=[];for(let c=0;c<_.length;c+=1)f[c]=On(Fn(n,_,c));return{c(){e=k("details"),t=k("summary"),r=M(),l=k("fieldset"),i=k("legend"),o=M();for(let c=0;c<f.length;c+=1)f[c].c();h=M(),p(t,"class","pagefind-ui__filter-name svelte-1v2r7ls"),p(i,"class","pagefind-ui__filter-group-label svelte-1v2r7ls"),p(l,"class","pagefind-ui__filter-group svelte-1v2r7ls"),p(e,"class","pagefind-ui__filter-block svelte-1v2r7ls"),e.open=n[6]},m(c,E){y(c,e,E),b(e,t),t.innerHTML=s,b(e,r),b(e,l),b(l,i),i.innerHTML=a,b(l,o);for(let u=0;u<f.length;u+=1)f[u]&&f[u].m(l,null);b(e,h)},p(c,E){if(E&2&&s!==(s=c[9].replace(/^(\w)/,zn)+"")&&(t.innerHTML=s),E&2&&a!==(a=c[9]+"")&&(i.innerHTML=a),E&7){_=Object.entries(c[10]||{});let u;for(u=0;u<_.length;u+=1){let m=Fn(c,_,u);f[u]?f[u].p(m,E):(f[u]=On(m),f[u].c(),f[u].m(l,null))}for(;u<f.length;u+=1)f[u].d(1);f.length=_.length}E&64&&(e.open=c[6])},d(c){c&&C(e),Q(f,c)}}}function Ms(n){let e=n[1]&&Object.entries(n[1]).length,t,s=e&&Hn(n);return{c(){s&&s.c(),t=x()},m(r,l){s&&s.m(r,l),y(r,t,l)},p(r,[l]){l&2&&(e=r[1]&&Object.entries(r[1]).length),e?s?s.p(r,l):(s=Hn(r),s.c(),s.m(t.parentNode,t)):s&&(s.d(1),s=null)},i:j,o:j,d(r){s&&s.d(r),r&&C(t)}}}var zn=n=>n.toLocaleUpperCase();function As(n,e,t){let{available_filters:s=null}=e,{show_empty_filters:r=!0}=e,{translate:l=()=>""}=e,{automatic_translations:i={}}=e,{translations:a={}}=e,o={},h=!1,_=!1;function f(c,E){o[`${c}:${E}`]=this.checked,t(0,o)}return n.$$set=c=>{"available_filters"in c&&t(1,s=c.available_filters),"show_empty_filters"in c&&t(2,r=c.show_empty_filters),"translate"in c&&t(3,l=c.translate),"automatic_translations"in c&&t(4,i=c.automatic_translations),"translations"in c&&t(5,a=c.translations)},n.$$.update=()=>{if(n.$$.dirty&130){e:if(s&&!h){t(7,h=!0);let c=Object.entries(s||{});c.length===1&&Object.entries(c[0][1])?.length<=6&&t(6,_=!0)}}},[o,s,r,l,i,a,_,h,f]}var ft=class extends q{constructor(e){super(),J(this,e,As,Ms,G,{available_filters:1,show_empty_filters:2,translate:3,automatic_translations:4,translations:5,selected_filters:0})}get selected_filters(){return this.$$.ctx[0]}},Dn=ft;var dt={};v(dt,{comments:()=>Fs,default:()=>Os,direction:()=>Hs,strings:()=>Ns,thanks_to:()=>ws});var ws="Jan Claasen <jan@cloudcannon.com>",Fs="",Hs="ltr",Ns={placeholder:"Soek",clear_search:"Opruim",load_more:"Laai nog resultate",search_label:"Soek hierdie webwerf",filters_label:"Filters",zero_results:"Geen resultate vir [SEARCH_TERM]",many_results:"[COUNT] resultate vir [SEARCH_TERM]",one_result:"[COUNT] resultate vir [SEARCH_TERM]",alt_search:"Geen resultate vir [SEARCH_TERM]. Toon resultate vir [DIFFERENT_TERM] in plaas daarvan",search_suggestion:"Geen resultate vir [SEARCH_TERM]. Probeer eerder een van die volgende terme:",searching:"Soek vir [SEARCH_TERM]"},Os={thanks_to:ws,comments:Fs,direction:Hs,strings:Ns};var ht={};v(ht,{comments:()=>zs,default:()=>Is,direction:()=>Ds,strings:()=>Us,thanks_to:()=>js});var js="Maruf Alom <mail@marufalom.com>",zs="",Ds="ltr",Us={placeholder:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",clear_search:"\u09AE\u09C1\u099B\u09C7 \u09AB\u09C7\u09B2\u09C1\u09A8",load_more:"\u0986\u09B0\u09CB \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09C1\u09A8",search_label:"\u098F\u0987 \u0993\u09DF\u09C7\u09AC\u09B8\u09BE\u0987\u099F\u09C7 \u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",filters_label:"\u09AB\u09BF\u09B2\u09CD\u099F\u09BE\u09B0",zero_results:"[SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF",many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09DF\u09BE \u0997\u09BF\u09DF\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09DF\u09BE \u0997\u09BF\u09DF\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",alt_search:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF. \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09C7 [DIFFERENT_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u09A6\u09C7\u0996\u09BE\u09A8\u09CB \u09B9\u099A\u09CD\u099B\u09C7",search_suggestion:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09DF\u09BE \u09AF\u09BE\u09DF\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u09AC\u09BF\u09B7\u09DF\u09C7. \u09A8\u09BF\u09A8\u09CD\u09AE\u09C7\u09B0 \u09AC\u09BF\u09B7\u09DF\u09AC\u09B8\u09CD\u09A4\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09A6\u09C7\u0996\u09C1\u09A8:",searching:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u099A\u09B2\u099B\u09C7 [SEARCH_TERM]..."},Is={thanks_to:js,comments:zs,direction:Ds,strings:Us};var mt={};v(mt,{comments:()=>Ls,default:()=>Ws,direction:()=>qs,strings:()=>Bs,thanks_to:()=>Ps});var Ps="Pablo Villaverde <https://github.com/pvillaverde>",Ls="",qs="ltr",Bs={placeholder:"Cerca",clear_search:"Netejar",load_more:"Veure m\xE9es resultats",search_label:"Cerca en aquest lloc",filters_label:"Filtres",zero_results:"No es van trobar resultats per [SEARCH_TERM]",many_results:"[COUNT] resultats trobats per [SEARCH_TERM]",one_result:"[COUNT] resultat trobat per [SEARCH_TERM]",alt_search:"No es van trobar resultats per [SEARCH_TERM]. Mostrant al seu lloc resultats per [DIFFERENT_TERM]",search_suggestion:"No es van trobar resultats per [SEARCH_TERM]. Proveu una de les cerques seg\xFCents:",searching:"Cercant [SEARCH_TERM]..."},Ws={thanks_to:Ps,comments:Ls,direction:qs,strings:Bs};var pt={};v(pt,{comments:()=>Gs,default:()=>Ys,direction:()=>Ks,strings:()=>Js,thanks_to:()=>Vs});var Vs="Jonas Smedegaard <dr@jones.dk>",Gs="",Ks="ltr",Js={placeholder:"S\xF8g",clear_search:"Nulstil",load_more:"Indl\xE6s flere resultater",search_label:"S\xF8g p\xE5 dette website",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v et af disse s\xF8geord i stedet:",searching:"S\xF8ger efter [SEARCH_TERM]..."},Ys={thanks_to:Vs,comments:Gs,direction:Ks,strings:Js};var gt={};v(gt,{comments:()=>Zs,default:()=>$s,direction:()=>Qs,strings:()=>xs,thanks_to:()=>Xs});var Xs="Jan Claasen <jan@cloudcannon.com>",Zs="",Qs="ltr",xs={placeholder:"Suche",clear_search:"L\xF6schen",load_more:"Mehr Ergebnisse laden",search_label:"Suche diese Seite",filters_label:"Filter",zero_results:"Keine Ergebnisse f\xFCr [SEARCH_TERM]",many_results:"[COUNT] Ergebnisse f\xFCr [SEARCH_TERM]",one_result:"[COUNT] Ergebnis f\xFCr [SEARCH_TERM]",alt_search:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Stattdessen werden Ergebnisse f\xFCr [DIFFERENT_TERM] angezeigt",search_suggestion:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Versuchen Sie eine der folgenden Suchen:",searching:"Suche f\xFCr [SEARCH_TERM]"},$s={thanks_to:Xs,comments:Zs,direction:Qs,strings:xs};var Et={};v(Et,{comments:()=>tr,default:()=>rr,direction:()=>nr,strings:()=>sr,thanks_to:()=>er});var er="Liam Bigelow <liam@cloudcannon.com>",tr="",nr="ltr",sr={placeholder:"Search",clear_search:"Clear",load_more:"Load more results",search_label:"Search this site",filters_label:"Filters",zero_results:"No results for [SEARCH_TERM]",many_results:"[COUNT] results for [SEARCH_TERM]",one_result:"[COUNT] result for [SEARCH_TERM]",alt_search:"No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead",search_suggestion:"No results for [SEARCH_TERM]. Try one of the following searches:",searching:"Searching for [SEARCH_TERM]..."},rr={thanks_to:er,comments:tr,direction:nr,strings:sr};var bt={};v(bt,{comments:()=>ir,default:()=>ur,direction:()=>ar,strings:()=>or,thanks_to:()=>lr});var lr="Pablo Villaverde <https://github.com/pvillaverde>",ir="",ar="ltr",or={placeholder:"Buscar",clear_search:"Limpiar",load_more:"Ver m\xE1s resultados",search_label:"Buscar en este sitio",filters_label:"Filtros",zero_results:"No se encontraron resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",alt_search:"No se encontraron resultados para [SEARCH_TERM]. Mostrando en su lugar resultados para [DIFFERENT_TERM]",search_suggestion:"No se encontraron resultados para [SEARCH_TERM]. Prueba una de las siguientes b\xFAsquedas:",searching:"Buscando [SEARCH_TERM]..."},ur={thanks_to:lr,comments:ir,direction:ar,strings:or};var Rt={};v(Rt,{comments:()=>_r,default:()=>hr,direction:()=>fr,strings:()=>dr,thanks_to:()=>cr});var cr="Valtteri Laitinen <dev@valtlai.fi>",_r="",fr="ltr",dr={placeholder:"Haku",clear_search:"Tyhjenn\xE4",load_more:"Lataa lis\xE4\xE4 tuloksia",search_label:"Hae t\xE4lt\xE4 sivustolta",filters_label:"Suodattimet",zero_results:"Ei tuloksia haulle [SEARCH_TERM]",many_results:"[COUNT] tulosta haulle [SEARCH_TERM]",one_result:"[COUNT] tulos haulle [SEARCH_TERM]",alt_search:"Ei tuloksia haulle [SEARCH_TERM]. N\xE4ytet\xE4\xE4n tulokset sen sijaan haulle [DIFFERENT_TERM]",search_suggestion:"Ei tuloksia haulle [SEARCH_TERM]. Kokeile jotain seuraavista:",searching:"Haetaan [SEARCH_TERM]..."},hr={thanks_to:cr,comments:_r,direction:fr,strings:dr};var Tt={};v(Tt,{comments:()=>pr,default:()=>br,direction:()=>gr,strings:()=>Er,thanks_to:()=>mr});var mr="Nicolas Friedli <nicolas@theologique.ch>",pr="",gr="ltr",Er={placeholder:"Rechercher",clear_search:"Nettoyer",load_more:"Charger plus de r\xE9sultats",search_label:"Recherche sur ce site",filters_label:"Filtres",zero_results:"Pas de r\xE9sultat pour [SEARCH_TERM]",many_results:"[COUNT] r\xE9sultats pour [SEARCH_TERM]",one_result:"[COUNT] r\xE9sultat pour [SEARCH_TERM]",alt_search:"Pas de r\xE9sultat pour [SEARCH_TERM]. Montre les r\xE9sultats pour [DIFFERENT_TERM] \xE0 la place",search_suggestion:"Pas de r\xE9sultat pour [SEARCH_TERM]. Essayer une des recherches suivantes:",searching:"Recherche [SEARCH_TERM]..."},br={thanks_to:mr,comments:pr,direction:gr,strings:Er};var kt={};v(kt,{comments:()=>Tr,default:()=>yr,direction:()=>kr,strings:()=>Cr,thanks_to:()=>Rr});var Rr="Pablo Villaverde <https://github.com/pvillaverde>",Tr="",kr="ltr",Cr={placeholder:"Buscar",clear_search:"Limpar",load_more:"Ver m\xE1is resultados",search_label:"Buscar neste sitio",filters_label:"Filtros",zero_results:"Non se atoparon resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados atopados para [SEARCH_TERM]",one_result:"[COUNT] resultado atopado para [SEARCH_TERM]",alt_search:"Non se atoparon resultados para [SEARCH_TERM]. Amosando no seu lugar resultados para [DIFFERENT_TERM]",search_suggestion:"Non se atoparon resultados para [SEARCH_TERM]. Probe unha das seguintes pesquisas:",searching:"Buscando [SEARCH_TERM]..."},yr={thanks_to:Rr,comments:Tr,direction:kr,strings:Cr};var Ct={};v(Ct,{comments:()=>vr,default:()=>wr,direction:()=>Mr,strings:()=>Ar,thanks_to:()=>Sr});var Sr="Amit Yadav <amit@thetechbasket.com>",vr="",Mr="ltr",Ar={placeholder:"\u0916\u094B\u091C\u0947\u0902",clear_search:"\u0938\u093E\u092B \u0915\u0930\u0947\u0902",load_more:"\u0914\u0930 \u0905\u0927\u093F\u0915 \u092A\u0930\u093F\u0923\u093E\u092E \u0932\u094B\u0921 \u0915\u0930\u0947\u0902",search_label:"\u0907\u0938 \u0938\u093E\u0907\u091F \u092E\u0947\u0902 \u0916\u094B\u091C\u0947\u0902",filters_label:"\u092B\u093C\u093F\u0932\u094D\u091F\u0930",zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E",many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u0947",one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u093E",alt_search:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0907\u0938\u0915\u0947 \u092C\u091C\u093E\u092F [DIFFERENT_TERM] \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E \u0930\u0939\u093E \u0939\u0948",search_suggestion:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0928\u093F\u092E\u094D\u0928\u0932\u093F\u0916\u093F\u0924 \u0916\u094B\u091C\u094B\u0902 \u092E\u0947\u0902 \u0938\u0947 \u0915\u094B\u0908 \u090F\u0915 \u0906\u091C\u093C\u092E\u093E\u090F\u0902:",searching:"[SEARCH_TERM] \u0915\u0940 \u0916\u094B\u091C \u0915\u0940 \u091C\u093E \u0930\u0939\u0940 \u0939\u0948..."},wr={thanks_to:Sr,comments:vr,direction:Mr,strings:Ar};var yt={};v(yt,{comments:()=>Hr,default:()=>jr,direction:()=>Nr,strings:()=>Or,thanks_to:()=>Fr});var Fr="Diomed <https://github.com/diomed>",Hr="",Nr="ltr",Or={placeholder:"Tra\u017Ei",clear_search:"O\u010Disti",load_more:"U\u010Ditaj vi\u0161e rezultata",search_label:"Pretra\u017Ei ovu stranicu",filters_label:"Filteri",zero_results:"Nema rezultata za [SEARCH_TERM]",many_results:"[COUNT] rezultata za [SEARCH_TERM]",one_result:"[COUNT] rezultat za [SEARCH_TERM]",alt_search:"Nema rezultata za [SEARCH_TERM]. Prikazujem rezultate za [DIFFERENT_TERM]",search_suggestion:"Nema rezultata za [SEARCH_TERM]. Poku\u0161aj s jednom od ovih pretraga:",searching:"Pretra\u017Eujem [SEARCH_TERM]..."},jr={thanks_to:Fr,comments:Hr,direction:Nr,strings:Or};var St={};v(St,{comments:()=>Dr,default:()=>Pr,direction:()=>Ur,strings:()=>Ir,thanks_to:()=>zr});var zr="Adam Laki <info@adamlaki.com>",Dr="",Ur="ltr",Ir={placeholder:"Keres\xE9s",clear_search:"T\xF6rl\xE9s",load_more:"Tov\xE1bbi tal\xE1latok bet\xF6lt\xE9se",search_label:"Keres\xE9s az oldalon",filters_label:"Sz\u0171r\xE9s",zero_results:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",many_results:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",one_result:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",alt_search:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Tal\xE1latok mutat\xE1sa ink\xE1bb a(z) [DIFFERENT_TERM] kifejez\xE9sre",search_suggestion:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Pr\xF3b\xE1ld meg a k\xF6vetkez\u0151 keres\xE9sek egyik\xE9t:",searching:"Keres\xE9s a(z) [SEARCH_TERM] kifejez\xE9sre..."},Pr={thanks_to:zr,comments:Dr,direction:Ur,strings:Ir};var vt={};v(vt,{comments:()=>qr,default:()=>Vr,direction:()=>Br,strings:()=>Wr,thanks_to:()=>Lr});var Lr="Nixentric",qr="",Br="ltr",Wr={placeholder:"Cari",clear_search:"Bersihkan",load_more:"Muat lebih banyak hasil",search_label:"Telusuri situs ini",filters_label:"Filter",zero_results:"[SEARCH_TERM] tidak ditemukan",many_results:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",one_result:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",alt_search:"[SEARCH_TERM] tidak ditemukan. Menampilkan hasil [DIFFERENT_TERM] sebagai gantinya",search_suggestion:"[SEARCH_TERM] tidak ditemukan. Coba salah satu pencarian berikut ini:",searching:"Mencari [SEARCH_TERM]..."},Vr={thanks_to:Lr,comments:qr,direction:Br,strings:Wr};var Mt={};v(Mt,{comments:()=>Kr,default:()=>Xr,direction:()=>Jr,strings:()=>Yr,thanks_to:()=>Gr});var Gr="Cosette Bruhns Alonso, Andrew Janco <apjanco@upenn.edu>",Kr="",Jr="ltr",Yr={placeholder:"Cerca",clear_search:"Cancella la cronologia",load_more:"Mostra pi\xF9 risultati",search_label:"Cerca nel sito",filters_label:"Filtri di ricerca",zero_results:"Nessun risultato per [SEARCH_TERM]",many_results:"[COUNT] risultati per [SEARCH_TERM]",one_result:"[COUNT] risultato per [SEARCH_TERM]",alt_search:"Nessun risultato per [SEARCH_TERM]. Mostrando risultati per [DIFFERENT_TERM] come alternativa.",search_suggestion:"Nessun risultato per [SEARCH_TERM]. Prova una delle seguenti ricerche:",searching:"Cercando [SEARCH_TERM]..."},Xr={thanks_to:Gr,comments:Kr,direction:Jr,strings:Yr};var At={};v(At,{comments:()=>Qr,default:()=>el,direction:()=>xr,strings:()=>$r,thanks_to:()=>Zr});var Zr="Tate",Qr="",xr="ltr",$r={placeholder:"\u691C\u7D22",clear_search:"\u6D88\u3059",load_more:"\u3082\u3063\u3068\u8AAD\u307F\u8FBC\u3080",search_label:"\u3053\u306E\u30B5\u30A4\u30C8\u3092\u691C\u7D22",filters_label:"\u30D5\u30A3\u30EB\u30BF",zero_results:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",many_results:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",one_result:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",alt_search:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002[DIFFERENT_TERM]\u306E\u691C\u7D22\u7D50\u679C\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059",search_suggestion:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u4EF6\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u6B21\u306E\u3044\u305A\u308C\u304B\u306E\u691C\u7D22\u3092\u8A66\u3057\u3066\u304F\u3060\u3055\u3044",searching:"[SEARCH_TERM]\u3092\u691C\u7D22\u3057\u3066\u3044\u307E\u3059"},el={thanks_to:Zr,comments:Qr,direction:xr,strings:$r};var wt={};v(wt,{comments:()=>nl,default:()=>ll,direction:()=>sl,strings:()=>rl,thanks_to:()=>tl});var tl="",nl="",sl="ltr",rl={placeholder:"Rapu",clear_search:"Whakakore",load_more:"Whakauta \u0113tahi otinga k\u0113",search_label:"Rapu",filters_label:"T\u0101tari",zero_results:"Otinga kore ki [SEARCH_TERM]",many_results:"[COUNT] otinga ki [SEARCH_TERM]",one_result:"[COUNT] otinga ki [SEARCH_TERM]",alt_search:"Otinga kore ki [SEARCH_TERM]. Otinga k\u0113 ki [DIFFERENT_TERM]",search_suggestion:"Otinga kore ki [SEARCH_TERM]. whakam\u0101tau ki ng\u0101 mea atu:",searching:"Rapu ki [SEARCH_TERM]..."},ll={thanks_to:tl,comments:nl,direction:sl,strings:rl};var Ft={};v(Ft,{comments:()=>al,default:()=>cl,direction:()=>ol,strings:()=>ul,thanks_to:()=>il});var il="Paul van Brouwershaven",al="",ol="ltr",ul={placeholder:"Zoeken",clear_search:"Reset",load_more:"Meer resultaten laden",search_label:"Doorzoek deze site",filters_label:"Filters",zero_results:"Geen resultaten voor [SEARCH_TERM]",many_results:"[COUNT] resultaten voor [SEARCH_TERM]",one_result:"[COUNT] resultaat voor [SEARCH_TERM]",alt_search:"Geen resultaten voor [SEARCH_TERM]. In plaats daarvan worden resultaten voor [DIFFERENT_TERM] weergegeven",search_suggestion:"Geen resultaten voor [SEARCH_TERM]. Probeer een van de volgende zoekopdrachten:",searching:"Zoeken naar [SEARCH_TERM]..."},cl={thanks_to:il,comments:al,direction:ol,strings:ul};var Ht={};v(Ht,{comments:()=>fl,default:()=>ml,direction:()=>dl,strings:()=>hl,thanks_to:()=>_l});var _l="Christopher Wingate",fl="",dl="ltr",hl={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]"},ml={thanks_to:_l,comments:fl,direction:dl,strings:hl};var Nt={};v(Nt,{comments:()=>gl,default:()=>Rl,direction:()=>El,strings:()=>bl,thanks_to:()=>pl});var pl="",gl="",El="ltr",bl={placeholder:"Szukaj",clear_search:"Wyczy\u015B\u0107",load_more:"Za\u0142aduj wi\u0119cej",search_label:"Przeszukaj t\u0119 stron\u0119",filters_label:"Filtry",zero_results:"Brak wynik\xF3w dla [SEARCH_TERM]",many_results:"[COUNT] wynik\xF3w dla [SEARCH_TERM]",one_result:"[COUNT] wynik dla [SEARCH_TERM]",alt_search:"Brak wynik\xF3w dla [SEARCH_TERM]. Wy\u015Bwietlam wyniki dla [DIFFERENT_TERM]",search_suggestion:"Brak wynik\xF3w dla [SEARCH_TERM]. Pokrewne wyniki wyszukiwania:",searching:"Szukam [SEARCH_TERM]..."},Rl={thanks_to:pl,comments:gl,direction:El,strings:bl};var Ot={};v(Ot,{comments:()=>kl,default:()=>Sl,direction:()=>Cl,strings:()=>yl,thanks_to:()=>Tl});var Tl="Jonatah",kl="",Cl="ltr",yl={placeholder:"Pesquisar",clear_search:"Limpar",load_more:"Ver mais resultados",search_label:"Pesquisar",filters_label:"Filtros",zero_results:"Nenhum resultado encontrado para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",alt_search:"Nenhum resultado encontrado para [SEARCH_TERM]. Exibindo resultados para [DIFFERENT_TERM]",search_suggestion:"Nenhum resultado encontrado para [SEARCH_TERM]. Tente uma das seguintes pesquisas:",searching:"Pesquisando por [SEARCH_TERM]..."},Sl={thanks_to:Tl,comments:kl,direction:Cl,strings:yl};var jt={};v(jt,{comments:()=>Ml,default:()=>Fl,direction:()=>Al,strings:()=>wl,thanks_to:()=>vl});var vl="Aleksandr Gordeev",Ml="",Al="ltr",wl={placeholder:"\u041F\u043E\u0438\u0441\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0435",search_label:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0438\u043B\u044C\u0442\u0440\u044B",zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",alt_search:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u044B \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0438\u0437 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432",searching:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]"},Fl={thanks_to:vl,comments:Ml,direction:Al,strings:wl};var zt={};v(zt,{comments:()=>Nl,default:()=>zl,direction:()=>Ol,strings:()=>jl,thanks_to:()=>Hl});var Hl="Andrija Sagicc",Nl="",Ol="ltr",jl={placeholder:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",clear_search:"\u0411\u0440\u0438\u0441\u0430\u045A\u0435",load_more:"\u041F\u0440\u0438\u043A\u0430\u0437 \u0432\u0438\u0448\u0435 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",search_label:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0441\u0430\u0458\u0442\u0430",filters_label:"\u0424\u0438\u043B\u0442\u0435\u0440\u0438",zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",alt_search:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u0440\u0438\u043A\u0430\u0437 \u0434\u043E\u0434\u0430\u0442\u043D\u0438\u043A \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [DIFFERENT_TERM]",search_suggestion:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u043E\u043A\u0443\u0448\u0430\u0458\u0442\u0435 \u0441\u0430 \u043D\u0435\u043A\u043E\u043C \u043E\u0434 \u0441\u043B\u0435\u0434\u0435\u045B\u0438\u0445 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430:",searching:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0442\u0435\u0440\u043C\u0438\u043D\u0430 [SEARCH_TERM]..."},zl={thanks_to:Hl,comments:Nl,direction:Ol,strings:jl};var Dt={};v(Dt,{comments:()=>Ul,default:()=>Ll,direction:()=>Il,strings:()=>Pl,thanks_to:()=>Dl});var Dl="Montazar Al-Jaber <montazar@nanawee.tech>",Ul="",Il="ltr",Pl={placeholder:"S\xF6k",clear_search:"Rensa",load_more:"Visa fler tr\xE4ffar",search_label:"S\xF6k p\xE5 denna sida",filters_label:"Filter",zero_results:"[SEARCH_TERM] gav inga tr\xE4ffar",many_results:"[SEARCH_TERM] gav [COUNT] tr\xE4ffar",one_result:"[SEARCH_TERM] gav [COUNT] tr\xE4ff",alt_search:"[SEARCH_TERM] gav inga tr\xE4ffar. Visar resultat f\xF6r [DIFFERENT_TERM] ist\xE4llet",search_suggestion:"[SEARCH_TERM] gav inga tr\xE4ffar. F\xF6rs\xF6k igen med en av f\xF6ljande s\xF6kord:",searching:"S\xF6ker efter [SEARCH_TERM]..."},Ll={thanks_to:Dl,comments:Ul,direction:Il,strings:Pl};var Ut={};v(Ut,{comments:()=>Bl,default:()=>Gl,direction:()=>Wl,strings:()=>Vl,thanks_to:()=>ql});var ql="",Bl="",Wl="ltr",Vl={placeholder:"\u0BA4\u0BC7\u0B9F\u0BC1\u0B95",clear_search:"\u0B85\u0BB4\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0B95",load_more:"\u0BAE\u0BC7\u0BB2\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BC8\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1\u0B95",search_label:"\u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BB3\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0B9F\u0BC1\u0B95",filters_label:"\u0BB5\u0B9F\u0BBF\u0B95\u0B9F\u0BCD\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BCD",zero_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",many_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 [COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",one_result:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",alt_search:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD\u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8, \u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B92\u0BA4\u0BCD\u0BA4 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD [DIFFERENT_TERM]",search_suggestion:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD \u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8.\u0B87\u0BA4\u0BB1\u0BCD\u0B95\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BC0\u0B9F\u0BBE\u0BA9 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0BA4\u0BC7\u0B9F\u0BC1\u0B95:",searching:"[SEARCH_TERM] \u0BA4\u0BC7\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BA9\u0BCD\u0BB1\u0BA4\u0BC1"},Gl={thanks_to:ql,comments:Bl,direction:Wl,strings:Vl};var It={};v(It,{comments:()=>Jl,default:()=>Zl,direction:()=>Yl,strings:()=>Xl,thanks_to:()=>Kl});var Kl="Taylan \xD6zg\xFCr Bildik",Jl="",Yl="ltr",Xl={placeholder:"Ara\u015Ft\u0131r",clear_search:"Temizle",load_more:"Daha fazla sonu\xE7",search_label:"Site genelinde arama",filters_label:"Filtreler",zero_results:"[SEARCH_TERM] i\xE7in sonu\xE7 yok",many_results:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",one_result:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",alt_search:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Bunun yerine [DIFFERENT_TERM] i\xE7in sonu\xE7lar g\xF6steriliyor",search_suggestion:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Alternatif olarak a\u015Fa\u011F\u0131daki kelimelerden birini deneyebilirsiniz:",searching:"[SEARCH_TERM] ara\u015Ft\u0131r\u0131l\u0131yor..."},Zl={thanks_to:Kl,comments:Jl,direction:Yl,strings:Xl};var Pt={};v(Pt,{comments:()=>xl,default:()=>ti,direction:()=>$l,strings:()=>ei,thanks_to:()=>Ql});var Ql="Long Nhat Nguyen",xl="",$l="ltr",ei={placeholder:"T\xECm ki\u1EBFm",clear_search:"X\xF3a",load_more:"Nhi\u1EC1u k\u1EBFt qu\u1EA3 h\u01A1n",search_label:"T\xECm ki\u1EBFm trong trang n\xE0y",filters_label:"B\u1ED9 l\u1ECDc",zero_results:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",many_results:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",one_result:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",alt_search:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Ki\u1EC3m th\u1ECB k\u1EBFt qu\u1EA3 thay th\u1EBF v\u1EDBi [DIFFERENT_TERM]",search_suggestion:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Th\u1EED m\u1ED9t trong c\xE1c t\xECm ki\u1EBFm:",searching:"\u0110ang t\xECm ki\u1EBFm cho [SEARCH_TERM]..."},ti={thanks_to:Ql,comments:xl,direction:$l,strings:ei};var Lt={};v(Lt,{comments:()=>si,default:()=>ii,direction:()=>ri,strings:()=>li,thanks_to:()=>ni});var ni="Amber Song",si="",ri="ltr",li={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},ii={thanks_to:ni,comments:si,direction:ri,strings:li};var qt={};v(qt,{comments:()=>oi,default:()=>_i,direction:()=>ui,strings:()=>ci,thanks_to:()=>ai});var ai="Amber Song",oi="",ui="ltr",ci={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F09\u66F4\u591A\u7D50\u679C",search_label:"\u7AD9\u5167\u641C\u7D22",filters_label:"\u7BE9\u9078",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",many_results:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",one_result:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u6539\u70BA\u986F\u793A [DIFFERENT_TERM] \u7684\u76F8\u95DC\u7D50\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u8ACB\u5617\u8A66\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},_i={thanks_to:ai,comments:oi,direction:ui,strings:ci};var Bt={};v(Bt,{comments:()=>di,default:()=>pi,direction:()=>hi,strings:()=>mi,thanks_to:()=>fi});var fi="Amber Song",di="",hi="ltr",mi={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]..."},pi={thanks_to:fi,comments:di,direction:hi,strings:mi};var gi=[dt,ht,mt,pt,gt,Et,bt,Rt,Tt,kt,Ct,yt,St,vt,Mt,At,wt,Ft,Ht,Nt,Ot,jt,zt,Dt,Ut,It,Pt,Lt,qt,Bt],Un=gi,In=["../../translations/af.json","../../translations/bn.json","../../translations/ca.json","../../translations/da.json","../../translations/de.json","../../translations/en.json","../../translations/es.json","../../translations/fi.json","../../translations/fr.json","../../translations/gl.json","../../translations/hi.json","../../translations/hr.json","../../translations/hu.json","../../translations/id.json","../../translations/it.json","../../translations/ja.json","../../translations/mi.json","../../translations/nl.json","../../translations/no.json","../../translations/pl.json","../../translations/pt.json","../../translations/ru.json","../../translations/sr.json","../../translations/sv.json","../../translations/ta.json","../../translations/tr.json","../../translations/vi.json","../../translations/zh-cn.json","../../translations/zh-tw.json","../../translations/zh.json"];function Pn(n,e,t){let s=n.slice();return s[48]=e[t],s}function Ln(n){let e,t,s;function r(i){n[34](i)}let l={show_empty_filters:n[4],available_filters:n[16],translate:n[18],automatic_translations:n[17],translations:n[5]};return n[7]!==void 0&&(l.selected_filters=n[7]),e=new Dn({props:l}),re.push(()=>an(e,"selected_filters",r)),{c(){Ze(e.$$.fragment)},m(i,a){he(e,i,a),s=!0},p(i,a){let o={};a[0]&16&&(o.show_empty_filters=i[4]),a[0]&65536&&(o.available_filters=i[16]),a[0]&131072&&(o.automatic_translations=i[17]),a[0]&32&&(o.translations=i[5]),!t&&a[0]&128&&(t=!0,o.selected_filters=i[7],nn(()=>t=!1)),e.$set(o)},i(i){s||(z(e.$$.fragment,i),s=!0)},o(i){I(e.$$.fragment,i),s=!1},d(i){oe(e,i)}}}function qn(n){let e,t,s,r,l=[Ri,bi],i=[];function a(o,h){return o[12]?0:1}return t=a(n,[-1,-1]),s=i[t]=l[t](n),{c(){e=k("div"),s.c(),p(e,"class","pagefind-ui__results-area svelte-e9gkc3")},m(o,h){y(o,e,h),i[t].m(e,null),r=!0},p(o,h){let _=t;t=a(o,h),t===_?i[t].p(o,h):(ie(),I(i[_],1,1,()=>{i[_]=null}),ae(),s=i[t],s?s.p(o,h):(s=i[t]=l[t](o),s.c()),z(s,1),s.m(e,null))},i(o){r||(z(s),r=!0)},o(o){I(s),r=!1},d(o){o&&C(e),i[t].d()}}}function bi(n){let e,t,s,r=[],l=new Map,i,a,o;function h(m,d){return m[11].results.length===0?Ci:m[11].results.length===1?ki:Ti}let _=h(n,[-1,-1]),f=_(n),c=n[11].results.slice(0,n[15]),E=m=>m[48].id;for(let m=0;m<c.length;m+=1){let d=Pn(n,c,m),R=E(d);l.set(R,r[m]=Bn(R,d))}let u=n[11].results.length>n[15]&&Wn(n);return{c(){e=k("p"),f.c(),t=M(),s=k("ol");for(let m=0;m<r.length;m+=1)r[m].c();i=M(),u&&u.c(),a=x(),p(e,"class","pagefind-ui__message svelte-e9gkc3"),p(s,"class","pagefind-ui__results svelte-e9gkc3")},m(m,d){y(m,e,d),f.m(e,null),y(m,t,d),y(m,s,d);for(let R=0;R<r.length;R+=1)r[R]&&r[R].m(s,null);y(m,i,d),u&&u.m(m,d),y(m,a,d),o=!0},p(m,d){_===(_=h(m,d))&&f?f.p(m,d):(f.d(1),f=_(m),f&&(f.c(),f.m(e,null))),d[0]&34830&&(c=m[11].results.slice(0,m[15]),ie(),r=ln(r,d,E,1,m,c,l,s,rn,Bn,null,Pn),ae()),m[11].results.length>m[15]?u?u.p(m,d):(u=Wn(m),u.c(),u.m(a.parentNode,a)):u&&(u.d(1),u=null)},i(m){if(!o){for(let d=0;d<c.length;d+=1)z(r[d]);o=!0}},o(m){for(let d=0;d<r.length;d+=1)I(r[d]);o=!1},d(m){m&&C(e),f.d(),m&&C(t),m&&C(s);for(let d=0;d<r.length;d+=1)r[d].d();m&&C(i),u&&u.d(m),m&&C(a)}}}function Ri(n){let e,t=n[14]&&Vn(n);return{c(){t&&t.c(),e=x()},m(s,r){t&&t.m(s,r),y(s,e,r)},p(s,r){s[14]?t?t.p(s,r):(t=Vn(s),t.c(),t.m(e.parentNode,e)):t&&(t.d(1),t=null)},i:j,o:j,d(s){t&&t.d(s),s&&C(e)}}}function Ti(n){let e=n[18]("many_results",n[17],n[5]).replace(/\[SEARCH_TERM\]/,n[14]).replace(/\[COUNT\]/,new Intl.NumberFormat(n[5].language).format(n[11].results.length))+"",t;return{c(){t=A(e)},m(s,r){y(s,t,r)},p(s,r){r[0]&149536&&e!==(e=s[18]("many_results",s[17],s[5]).replace(/\[SEARCH_TERM\]/,s[14]).replace(/\[COUNT\]/,new Intl.NumberFormat(s[5].language).format(s[11].results.length))+"")&&N(t,e)},d(s){s&&C(t)}}}function ki(n){let e=n[18]("one_result",n[17],n[5]).replace(/\[SEARCH_TERM\]/,n[14]).replace(/\[COUNT\]/,new Intl.NumberFormat(n[5].language).format(1))+"",t;return{c(){t=A(e)},m(s,r){y(s,t,r)},p(s,r){r[0]&147488&&e!==(e=s[18]("one_result",s[17],s[5]).replace(/\[SEARCH_TERM\]/,s[14]).replace(/\[COUNT\]/,new Intl.NumberFormat(s[5].language).format(1))+"")&&N(t,e)},d(s){s&&C(t)}}}function Ci(n){let e=n[18]("zero_results",n[17],n[5]).replace(/\[SEARCH_TERM\]/,n[14])+"",t;return{c(){t=A(e)},m(s,r){y(s,t,r)},p(s,r){r[0]&147488&&e!==(e=s[18]("zero_results",s[17],s[5]).replace(/\[SEARCH_TERM\]/,s[14])+"")&&N(t,e)},d(s){s&&C(t)}}}function yi(n){let e,t;return e=new gn({props:{show_images:n[1],process_result:n[3],result:n[48]}}),{c(){Ze(e.$$.fragment)},m(s,r){he(e,s,r),t=!0},p(s,r){let l={};r[0]&2&&(l.show_images=s[1]),r[0]&8&&(l.process_result=s[3]),r[0]&34816&&(l.result=s[48]),e.$set(l)},i(s){t||(z(e.$$.fragment,s),t=!0)},o(s){I(e.$$.fragment,s),t=!1},d(s){oe(e,s)}}}function Si(n){let e,t;return e=new An({props:{show_images:n[1],process_result:n[3],result:n[48]}}),{c(){Ze(e.$$.fragment)},m(s,r){he(e,s,r),t=!0},p(s,r){let l={};r[0]&2&&(l.show_images=s[1]),r[0]&8&&(l.process_result=s[3]),r[0]&34816&&(l.result=s[48]),e.$set(l)},i(s){t||(z(e.$$.fragment,s),t=!0)},o(s){I(e.$$.fragment,s),t=!1},d(s){oe(e,s)}}}function Bn(n,e){let t,s,r,l,i,a=[Si,yi],o=[];function h(_,f){return _[2]?0:1}return s=h(e,[-1,-1]),r=o[s]=a[s](e),{key:n,first:null,c(){t=x(),r.c(),l=x(),this.first=t},m(_,f){y(_,t,f),o[s].m(_,f),y(_,l,f),i=!0},p(_,f){e=_;let c=s;s=h(e,f),s===c?o[s].p(e,f):(ie(),I(o[c],1,1,()=>{o[c]=null}),ae(),r=o[s],r?r.p(e,f):(r=o[s]=a[s](e),r.c()),z(r,1),r.m(l.parentNode,l))},i(_){i||(z(r),i=!0)},o(_){I(r),i=!1},d(_){_&&C(t),o[s].d(_),_&&C(l)}}}function Wn(n){let e,t=n[18]("load_more",n[17],n[5])+"",s,r,l;return{c(){e=k("button"),s=A(t),p(e,"type","button"),p(e,"class","pagefind-ui__button svelte-e9gkc3")},m(i,a){y(i,e,a),b(e,s),r||(l=K(e,"click",n[20]),r=!0)},p(i,a){a[0]&131104&&t!==(t=i[18]("load_more",i[17],i[5])+"")&&N(s,t)},d(i){i&&C(e),r=!1,l()}}}function Vn(n){let e,t=n[18]("searching",n[17],n[5]).replace(/\[SEARCH_TERM\]/,n[14])+"",s;return{c(){e=k("p"),s=A(t),p(e,"class","pagefind-ui__message svelte-e9gkc3")},m(r,l){y(r,e,l),b(e,s)},p(r,l){l[0]&147488&&t!==(t=r[18]("searching",r[17],r[5]).replace(/\[SEARCH_TERM\]/,r[14])+"")&&N(s,t)},d(r){r&&C(e)}}}function vi(n){let e,t,s,r,l,i,a=n[18]("clear_search",n[17],n[5])+"",o,h,_,f,c,E,u,m,d=n[10]&&Ln(n),R=n[13]&&qn(n);return{c(){e=k("div"),t=k("form"),s=k("input"),l=M(),i=k("button"),o=A(a),h=M(),_=k("div"),d&&d.c(),f=M(),R&&R.c(),p(s,"class","pagefind-ui__search-input svelte-e9gkc3"),p(s,"type","text"),p(s,"placeholder",r=n[18]("placeholder",n[17],n[5])),p(s,"autocapitalize","none"),p(s,"enterkeyhint","search"),p(i,"class","pagefind-ui__search-clear svelte-e9gkc3"),W(i,"pagefind-ui__suppressed",!n[6]),p(_,"class","pagefind-ui__drawer svelte-e9gkc3"),W(_,"pagefind-ui__hidden",!n[13]),p(t,"class","pagefind-ui__form svelte-e9gkc3"),p(t,"role","search"),p(t,"aria-label",c=n[18]("search_label",n[17],n[5])),p(t,"action","javascript:void(0);"),p(e,"class","pagefind-ui svelte-e9gkc3"),W(e,"pagefind-ui--reset",n[0])},m(T,S){y(T,e,S),b(e,t),b(t,s),it(s,n[6]),n[31](s),b(t,l),b(t,i),b(i,o),n[32](i),b(t,h),b(t,_),d&&d.m(_,null),b(_,f),R&&R.m(_,null),E=!0,u||(m=[K(s,"focus",n[19]),K(s,"keydown",n[29]),K(s,"input",n[30]),K(i,"click",n[33]),K(t,"submit",Mi)],u=!0)},p(T,S){(!E||S[0]&131104&&r!==(r=T[18]("placeholder",T[17],T[5])))&&p(s,"placeholder",r),S[0]&64&&s.value!==T[6]&&it(s,T[6]),(!E||S[0]&131104)&&a!==(a=T[18]("clear_search",T[17],T[5])+"")&&N(o,a),(!E||S[0]&64)&&W(i,"pagefind-ui__suppressed",!T[6]),T[10]?d?(d.p(T,S),S[0]&1024&&z(d,1)):(d=Ln(T),d.c(),z(d,1),d.m(_,f)):d&&(ie(),I(d,1,1,()=>{d=null}),ae()),T[13]?R?(R.p(T,S),S[0]&8192&&z(R,1)):(R=qn(T),R.c(),z(R,1),R.m(_,null)):R&&(ie(),I(R,1,1,()=>{R=null}),ae()),(!E||S[0]&8192)&&W(_,"pagefind-ui__hidden",!T[13]),(!E||S[0]&131104&&c!==(c=T[18]("search_label",T[17],T[5])))&&p(t,"aria-label",c),(!E||S[0]&1)&&W(e,"pagefind-ui--reset",T[0])},i(T){E||(z(d),z(R),E=!0)},o(T){I(d),I(R),E=!1},d(T){T&&C(e),n[31](null),n[32](null),d&&d.d(),R&&R.d(),u=!1,V(m)}}}var Mi=n=>n.preventDefault();function Ai(n,e,t){let s={},r=In.map(g=>g.match(/([^\/]+)\.json$/)[1]);for(let g=0;g<r.length;g++)s[r[g]]={language:r[g],...Un[g].strings};let{base_path:l="/pagefind/"}=e,{page_size:i=5}=e,{reset_styles:a=!0}=e,{show_images:o=!0}=e,{show_sub_results:h=!1}=e,{excerpt_length:_}=e,{process_result:f=null}=e,{process_term:c=null}=e,{show_empty_filters:E=!0}=e,{debounce_timeout_ms:u=300}=e,{pagefind_options:m={}}=e,{merge_index:d=[]}=e,{trigger_search_term:R=""}=e,{translations:T={}}=e,S="",w,B,X,F=40,U=!1,P=[],Z=!1,$e=!1,Gt=0,Kt="",et=i,Jt=null,ue=null,Ge={},Yt=s.en,Kn=(g,H,O)=>O[g]??H[g]??"";at(()=>{let g=document?.querySelector?.("html")?.getAttribute?.("lang")||"en",H=Qe(g.toLocaleLowerCase());t(17,Yt=s[`${H.language}-${H.script}-${H.region}`]||s[`${H.language}-${H.region}`]||s[`${H.language}`]||s.en)}),ot(()=>{w?.destroy?.(),w=null});let Xt=async()=>{if(!U&&(t(10,U=!0),!w)){let g;try{g=await import(`${l}pagefind.js`)}catch(O){console.error(O),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindUI",`[DEBUG: Loaded from ${document?.currentScript?.src??"no known script location"}]`].join(`
+`))}_||t(22,_=h?12:30);let H={...m||{},excerptLength:_};await g.options(H);for(let O of d){if(!O.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let L=O.bundlePath;delete O.bundlePath,await g.mergeIndex(L,O)}w=g,Jn()}},Jn=async()=>{w&&(Jt=await w.filters(),(!ue||!Object.keys(ue).length)&&t(16,ue=Jt))},Yn=g=>{let H={};return Object.entries(g).filter(([,O])=>O).forEach(([O])=>{let[L,ls]=O.split(/:(.*)$/);H[L]=H[L]||[],H[L].push(ls)}),H},ce,Xn=async(g,H)=>{if(!g){t(13,$e=!1),ce&&clearTimeout(ce);return}let O=Yn(H),L=()=>Zn(g,O);u>0&&g?(ce&&clearTimeout(ce),ce=setTimeout(L,u),await Zt(),w.preload(g,{filters:O})):L(),Qn()},Zt=async()=>{for(;!w;)Xt(),await new Promise(g=>setTimeout(g,50))},Zn=async(g,H)=>{t(14,Kt=g||""),typeof c=="function"&&(g=c(g)),t(12,Z=!0),t(13,$e=!0),await Zt();let O=++Gt,L=await w.search(g,{filters:H});Gt===O&&(L.filters&&Object.keys(L.filters)?.length&&t(16,ue=L.filters),t(11,P=L),t(12,Z=!1),t(15,et=i))},Qn=()=>{let g=X.offsetWidth;g!=F&&t(8,B.style.paddingRight=`${g+2}px`,B)},xn=g=>{g?.preventDefault(),t(15,et+=i)},$n=g=>{g.key==="Escape"&&(t(6,S=""),B.blur()),g.key==="Enter"&&g.preventDefault()};function es(){S=this.value,t(6,S),t(21,R)}function ts(g){re[g?"unshift":"push"](()=>{B=g,t(8,B)})}function ns(g){re[g?"unshift":"push"](()=>{X=g,t(9,X)})}let ss=()=>{t(6,S=""),B.blur()};function rs(g){Ge=g,t(7,Ge)}return n.$$set=g=>{"base_path"in g&&t(23,l=g.base_path),"page_size"in g&&t(24,i=g.page_size),"reset_styles"in g&&t(0,a=g.reset_styles),"show_images"in g&&t(1,o=g.show_images),"show_sub_results"in g&&t(2,h=g.show_sub_results),"excerpt_length"in g&&t(22,_=g.excerpt_length),"process_result"in g&&t(3,f=g.process_result),"process_term"in g&&t(25,c=g.process_term),"show_empty_filters"in g&&t(4,E=g.show_empty_filters),"debounce_timeout_ms"in g&&t(26,u=g.debounce_timeout_ms),"pagefind_options"in g&&t(27,m=g.pagefind_options),"merge_index"in g&&t(28,d=g.merge_index),"trigger_search_term"in g&&t(21,R=g.trigger_search_term),"translations"in g&&t(5,T=g.translations)},n.$$.update=()=>{if(n.$$.dirty[0]&2097152)e:R&&(t(6,S=R),t(21,R=""));if(n.$$.dirty[0]&192)e:Xn(S,Ge)},[a,o,h,f,E,T,S,Ge,B,X,U,P,Z,$e,Kt,et,ue,Yt,Kn,Xt,xn,R,_,l,i,c,u,m,d,$n,es,ts,ns,ss,rs]}var Wt=class extends q{constructor(e){super(),J(this,e,Ai,vi,G,{base_path:23,page_size:24,reset_styles:0,show_images:1,show_sub_results:2,excerpt_length:22,process_result:3,process_term:25,show_empty_filters:4,debounce_timeout_ms:26,pagefind_options:27,merge_index:28,trigger_search_term:21,translations:5},null,[-1,-1])}},Gn=Wt;var Vt;try{Vt=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?ui.js.*$/)[1]}catch{Vt="/pagefind/"}var xe=class{constructor(e){this._pfs=null;let t=e.element??"[data-pagefind-ui]",s=e.bundlePath??Vt,r=e.pageSize??5,l=e.resetStyles??!0,i=e.showImages??!0,a=e.showSubResults??!1,o=e.excerptLength??0,h=e.processResult??null,_=e.processTerm??null,f=e.showEmptyFilters??!0,c=e.debounceTimeoutMs??300,E=e.mergeIndex??[],u=e.translations??[];delete e.element,delete e.bundlePath,delete e.pageSize,delete e.resetStyles,delete e.showImages,delete e.showSubResults,delete e.excerptLength,delete e.processResult,delete e.processTerm,delete e.showEmptyFilters,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations;let m=t instanceof HTMLElement?t:document.querySelector(t);m?this._pfs=new Gn({target:m,props:{base_path:s,page_size:r,reset_styles:l,show_images:i,show_sub_results:a,excerpt_length:o,process_result:h,process_term:_,show_empty_filters:f,debounce_timeout_ms:c,merge_index:E,translations:u,pagefind_options:e}}):console.error(`Pagefind UI couldn't find the selector ${t}`)}triggerSearch(e){this._pfs.$$set({trigger_search_term:e})}destroy(){this._pfs.$destroy()}};window.PagefindUI=xe;})();
diff --git a/pagefind/pagefind.js b/pagefind/pagefind.js
new file mode 100644
index 0000000..94ab923
--- /dev/null
+++ b/pagefind/pagefind.js
@@ -0,0 +1,9 @@
+const pagefind_version="1.0.4";let wasm_bindgen;(function(){const __exports={};let script_src;if(typeof document==='undefined'){script_src=location.href}else{script_src=new URL("UNHANDLED",location.href).toString()}let wasm;let cachedUint8Memory0=null;function getUint8Memory0(){if(cachedUint8Memory0===null||cachedUint8Memory0.byteLength===0){cachedUint8Memory0=new Uint8Array(wasm.memory.buffer)}return cachedUint8Memory0}let WASM_VECTOR_LEN=0;function passArray8ToWasm0(arg,malloc){const ptr=malloc(arg.length*1);getUint8Memory0().set(arg,ptr/1);WASM_VECTOR_LEN=arg.length;return ptr}__exports.init_pagefind=function(metadata_bytes){const ptr0=passArray8ToWasm0(metadata_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.init_pagefind(ptr0,len0);return ret};__exports.load_index_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_index_chunk(ptr,ptr0,len0);return ret};__exports.load_filter_chunk=function(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_filter_chunk(ptr,ptr0,len0);return ret};const cachedTextEncoder=new TextEncoder('utf-8');const encodeString=(typeof cachedTextEncoder.encodeInto==='function'?function(arg,view){return cachedTextEncoder.encodeInto(arg,view)}:function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length}});function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length);getUint8Memory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr}let len=arg.length;let ptr=malloc(len);const mem=getUint8Memory0();let offset=0;for(;offset<len;offset++){const code=arg.charCodeAt(offset);if(code>0x7F)break;mem[ptr+offset]=code}if(offset!==len){if(offset!==0){arg=arg.slice(offset)}ptr=realloc(ptr,len,len=offset+arg.length*3);const view=getUint8Memory0().subarray(ptr+offset,ptr+len);const ret=encodeString(arg,view);offset+=ret.written}WASM_VECTOR_LEN=offset;return ptr}__exports.add_synthetic_filter=function(ptr,filter){const ptr0=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.add_synthetic_filter(ptr,ptr0,len0);return ret};let cachedInt32Memory0=null;function getInt32Memory0(){if(cachedInt32Memory0===null||cachedInt32Memory0.byteLength===0){cachedInt32Memory0=new Int32Array(wasm.memory.buffer)}return cachedInt32Memory0}const cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function getStringFromWasm0(ptr,len){return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr,ptr+len))}__exports.request_indexes=function(ptr,query){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_filter_indexes=function(ptr,filters){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(filters,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.request_filter_indexes(retptr,ptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.request_all_filter_indexes=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.request_all_filter_indexes(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.filters=function(ptr){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);wasm.filters(retptr,ptr);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};__exports.search=function(ptr,query,filter,sort,exact){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ptr1=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len1=WASM_VECTOR_LEN;const ptr2=passStringToWasm0(sort,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len2=WASM_VECTOR_LEN;wasm.search(retptr,ptr,ptr0,len0,ptr1,len1,ptr2,len2,exact);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1)}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1)}};async function load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports)}catch(e){if(module.headers.get('Content-Type')!='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e)}else{throw e}}}const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports)}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module}}else{return instance}}}function getImports(){const imports={};imports.wbg={};return imports}function initMemory(imports,maybe_memory){}function finalizeInit(instance,module){wasm=instance.exports;init.__wbindgen_wasm_module=module;cachedInt32Memory0=null;cachedUint8Memory0=null;return wasm}function initSync(module){const imports=getImports();initMemory(imports);if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module)}const instance=new WebAssembly.Instance(module,imports);return finalizeInit(instance,module)}async function init(input){if(typeof input==='undefined'){input=script_src.replace(/\.js$/,'_bg.wasm')}const imports=getImports();if(typeof input==='string'||(typeof Request==='function'&&input instanceof Request)||(typeof URL==='function'&&input instanceof URL)){input=fetch(input)}initMemory(imports);const{instance,module}=await load(await input,imports);return finalizeInit(instance,module)}wasm_bindgen=Object.assign(init,{initSync},__exports)})();var u8=Uint8Array;var u16=Uint16Array;var u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i2=0;i2<31;++i2){b[i2]=start+=1<<eb[i2-1]}var r=new u32(b[30]);for(var i2=1;i2<30;++i2){for(var j=b[i2];j<b[i2+1];++j){r[j]=j-b[i2]<<5|i2}}return[b,r]};var _a=freb(fleb,2);var fl=_a[0];var revfl=_a[1];fl[28]=258,revfl[258]=28;var _b=freb(fdeb,0);var fd=_b[0];var revfd=_b[1];var rev=new u16(32768);for(i=0;i<32768;++i){x=(i&43690)>>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1}var x;var i;var hMap=function(cd,mb,r){var s=cd.length;var i2=0;var l=new u16(mb);for(;i2<s;++i2){if(cd[i2])++l[cd[i2]-1]}var le=new u16(mb);for(i2=0;i2<mb;++i2){le[i2]=le[i2-1]+l[i2-1]<<1}var co;if(r){co=new u16(1<<mb);var rvb=15-mb;for(i2=0;i2<s;++i2){if(cd[i2]){var sv=i2<<4|cd[i2];var r_1=mb-cd[i2];var v=le[cd[i2]-1]++<<r_1;for(var m=v|(1<<r_1)-1;v<=m;++v){co[rev[v]>>>rvb]=sv}}}}else{co=new u16(s);for(i2=0;i2<s;++i2){if(cd[i2]){co[i2]=rev[le[cd[i2]-1]++]>>>15-cd[i2]}}}return co};var flt=new u8(288);for(i=0;i<144;++i)flt[i]=8;var i;for(i=144;i<256;++i)flt[i]=9;var i;for(i=256;i<280;++i)flt[i]=7;var i;for(i=280;i<288;++i)flt[i]=8;var i;var fdt=new u8(32);for(i=0;i<32;++i)fdt[i]=5;var i;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i2=1;i2<a.length;++i2){if(a[i2]>m)m=a[i2]}return m};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7)};var shft=function(p){return(p+7)/8|0};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v.BYTES_PER_ELEMENT==2?u16:v.BYTES_PER_ELEMENT==4?u32:u8)(e-s);n.set(v.subarray(s,e));return n};var ec=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"];var err=function(ind,msg,nt){var e=new Error(msg||ec[ind]);e.code=ind;if(Error.captureStackTrace)Error.captureStackTrace(e,err);if(!nt)throw e;return e};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&st.f&&!st.l)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l2){var bl=buf.length;if(l2>bl){var nbuf=new u8(Math.max(bl*2,l2));nbuf.set(buf);buf=nbuf}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)err(0);break}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8,st.f=final;continue}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i2=0;i2<hcLen;++i2){clt[clim[i2]]=bits(dat,pos+i2*3,7)}pos+=hcLen*3;var clb=max(clt),clbmsk=(1<<clb)-1;var clm=hMap(clt,clb,1);for(var i2=0;i2<tl;){var r=clm[bits(dat,pos,clbmsk)];pos+=r&15;var s=r>>>4;if(s<16){ldt[i2++]=s}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i2-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i2++]=c}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1)}else err(1);if(pos>tbts){if(noSt)err(0);break}}if(noBuf)cbuf(bt+131072);var lms=(1<<lbt)-1,dms=(1<<dbt)-1;var lpos=pos;for(;;lpos=pos){var c=lm[bits16(dat,pos)&lms],sym=c>>>4;pos+=c&15;if(pos>tbts){if(noSt)err(0);break}if(!c)err(2);if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break}else{var add=sym-254;if(sym>264){var i2=sym-257,b=fleb[i2];add=bits(dat,pos,(1<<b)-1)+fl[i2];pos+=b}var d=dm[bits16(dat,pos)&dms],dsym=d>>>4;if(!d)err(3);pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<<b)-1,pos+=b}if(pos>tbts){if(noSt)err(0);break}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt<end;bt+=4){buf[bt]=buf[bt-dt];buf[bt+1]=buf[bt+1-dt];buf[bt+2]=buf[bt+2-dt];buf[bt+3]=buf[bt+3-dt]}bt=end}}st.l=lm,st.p=lpos,st.b=bt,st.f=final;if(lm)final=1,st.m=lbt,st.d=dm,st.n=dbt}while(!final);return bt==buf.length?buf:slc(buf,0,bt)};var et=new u8(0);var gzs=function(d){if(d[0]!=31||d[1]!=139||d[2]!=8)err(6,"invalid gzip data");var flg=d[3];var st=10;if(flg&4)st+=d[10]|(d[11]<<8)+2;for(var zs=(flg>>3&1)+(flg>>4&1);zs>0;zs-=!d[st++]);return st+(flg&2)};var gzl=function(d){var l=d.length;return(d[l-4]|d[l-3]<<8|d[l-2]<<16|d[l-1]<<24)>>>0};function gunzipSync(data,out){return inflt(data.subarray(gzs(data),-8),out||new u8(gzl(data)))}var td=typeof TextDecoder!="undefined"&&new TextDecoder();var tds=0;try{td.decode(et,{stream:true});tds=1}catch(e){}var gz_default=gunzipSync;var calculate_excerpt_region=(word_positions,excerpt_length)=>{if(word_positions.length===0){return 0}let words=[];for(const word of word_positions){words[word.location]=words[word.location]||0;words[word.location]+=word.balanced_score}if(words.length<=excerpt_length){return 0}let densest=words.slice(0,excerpt_length).reduce((partialSum,a)=>partialSum+a,0);let working_sum=densest;let densest_at=[0];for(let i2=0;i2<words.length;i2++){const boundary=i2+excerpt_length;working_sum+=(words[boundary]??0)-(words[i2]??0);if(working_sum>densest){densest=working_sum;densest_at=[i2]}else if(working_sum===densest&&densest_at[densest_at.length-1]===i2-1){densest_at.push(i2)}}let midpoint=densest_at[Math.floor(densest_at.length/2)];return midpoint};var build_excerpt=(content,start,length,locations,not_before,not_from)=>{let is_zws_delimited=content.includes("\u200B");let fragment_words=[];if(is_zws_delimited){fragment_words=content.split("\u200B")}else{fragment_words=content.split(/[\r\n\s]+/g)}for(let word of locations){if(fragment_words[word]?.startsWith(`<mark>`)){continue}fragment_words[word]=`<mark>${fragment_words[word]}</mark>`}let endcap=not_from??fragment_words.length;let startcap=not_before??0;if(endcap-startcap<length){length=endcap-startcap}if(start+length>endcap){start=endcap-length}if(start<startcap){start=startcap}return fragment_words.slice(start,start+length).join(is_zws_delimited?"":" ").trim()};var calculate_sub_results=(fragment,desired_excerpt_length)=>{const anchors=fragment.anchors.filter((a)=>/h\d/i.test(a.element)&&a.text?.length&&/\S/.test(a.text)).sort((a,b)=>a.location-b.location);const results=[];let current_anchor_position=0;let current_anchor={title:fragment.meta["title"],url:fragment.url,weighted_locations:[],locations:[],excerpt:""};const add_result=(end_range)=>{if(current_anchor.locations.length){const relative_weighted_locations=current_anchor.weighted_locations.map((l)=>{return{weight:l.weight,balanced_score:l.balanced_score,location:l.location-current_anchor_position}});const excerpt_start=calculate_excerpt_region(relative_weighted_locations,desired_excerpt_length)+current_anchor_position;const excerpt_length=end_range?Math.min(end_range-excerpt_start,desired_excerpt_length):desired_excerpt_length;current_anchor.excerpt=build_excerpt(fragment.raw_content??"",excerpt_start,excerpt_length,current_anchor.locations,current_anchor_position,end_range);results.push(current_anchor)}};for(let word of fragment.weighted_locations){if(!anchors.length||word.location<anchors[0].location){current_anchor.weighted_locations.push(word);current_anchor.locations.push(word.location)}else{let next_anchor=anchors.shift();add_result(next_anchor.location);while(anchors.length&&word.location>=anchors[0].location){next_anchor=anchors.shift()}let anchored_url=fragment.url;try{const url_is_fq=/^((https?:)?\/\/)/.test(anchored_url);if(url_is_fq){let fq_url=new URL(anchored_url);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString()}else{if(!/^\//.test(anchored_url)){anchored_url=`/${anchored_url}`}let fq_url=new URL(`https://example.com${anchored_url}`);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString().replace(/^https:\/\/example.com/,"")}}catch(e){console.error(`Pagefind: Couldn't process ${anchored_url} for a search result`)}current_anchor_position=next_anchor.location;current_anchor={title:next_anchor.text,url:anchored_url,anchor:next_anchor,weighted_locations:[word],locations:[word.location],excerpt:""}}}add_result(anchors[0]?.location);return results};var asyncSleep=async(ms=100)=>{return new Promise((r)=>setTimeout(r,ms))};var PagefindInstance=class{constructor(opts={}){this.version=pagefind_version;this.backend=wasm_bindgen;this.decoder=new TextDecoder("utf-8");this.wasm=null;this.basePath=opts.basePath||"/pagefind/";this.primary=opts.primary||false;if(this.primary&&!opts.basePath){this.initPrimary()}if(/[^\/]$/.test(this.basePath)){this.basePath=`${this.basePath}/`}if(window?.location?.origin&&this.basePath.startsWith(window.location.origin)){this.basePath=this.basePath.replace(window.location.origin,"")}this.baseUrl=opts.baseUrl||this.defaultBaseUrl();if(!/^(\/|https?:\/\/)/.test(this.baseUrl)){this.baseUrl=`/${this.baseUrl}`}this.indexWeight=opts.indexWeight??1;this.excerptLength=opts.excerptLength??30;this.mergeFilter=opts.mergeFilter??{};this.highlightParam=opts.highlightParam??null;this.loaded_chunks={};this.loaded_filters={};this.loaded_fragments={};this.raw_ptr=null;this.searchMeta=null;this.languages=null}initPrimary(){let derivedBasePath=import.meta.url.match(/^(.*\/)pagefind.js.*$/)?.[1];if(derivedBasePath){this.basePath=derivedBasePath}else{console.warn(["Pagefind couldn't determine the base of the bundle from the import path. Falling back to the default.","Set a basePath option when initialising Pagefind to ignore this message."].join("\n"))}}defaultBaseUrl(){let default_base=this.basePath.match(/^(.*\/)_?pagefind/)?.[1];return default_base||"/"}async options(options2){const opts=["basePath","baseUrl","indexWeight","excerptLength","mergeFilter","highlightParam"];for(const[k,v]of Object.entries(options2)){if(k==="mergeFilter"){let filters2=this.stringifyFilters(v);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}else if(opts.includes(k)){if(k==="basePath"&&typeof v==="string")this.basePath=v;if(k==="baseUrl"&&typeof v==="string")this.baseUrl=v;if(k==="indexWeight"&&typeof v==="number")this.indexWeight=v;if(k==="excerptLength"&&typeof v==="number")this.excerptLength=v;if(k==="mergeFilter"&&typeof v==="object")this.mergeFilter=v;if(k==="highlightParam"&&typeof v==="string")this.highlightParam=v}else{console.warn(`Unknown Pagefind option ${k}. Allowed options: [${opts.join(", ")}]`)}}}decompress(data,file="unknown file"){if(this.decoder.decode(data.slice(0,12))==="pagefind_dcd"){return data.slice(12)}data=gz_default(data);if(this.decoder.decode(data.slice(0,12))!=="pagefind_dcd"){console.error(`Decompressing ${file} appears to have failed: Missing signature`);return data}return data.slice(12)}async init(language,opts){await this.loadEntry();let index=this.findIndex(language);let lang_wasm=index.wasm?index.wasm:"unknown";let resources=[this.loadMeta(index.hash)];if(opts.load_wasm===true){resources.push(this.loadWasm(lang_wasm))}await Promise.all(resources);this.raw_ptr=this.backend.init_pagefind(new Uint8Array(this.searchMeta));if(Object.keys(this.mergeFilter)?.length){let filters2=this.stringifyFilters(this.mergeFilter);let ptr=await this.getPtr();this.raw_ptr=this.backend.add_synthetic_filter(ptr,filters2)}}async loadEntry(){try{let entry_response=await fetch(`${this.basePath}pagefind-entry.json?ts=${Date.now()}`);let entry_json=await entry_response.json();this.languages=entry_json.languages;if(entry_json.version!==this.version){if(this.primary){console.warn(["Pagefind JS version doesn't match the version in your search index.",`Pagefind JS: ${this.version}. Pagefind index: ${entry_json.version}`,"If you upgraded Pagefind recently, you likely have a cached pagefind.js file.","If you encounter any search errors, try clearing your cache."].join("\n"))}else{console.warn(["Merging a Pagefind index from a different version than the main Pagefind instance.",`Main Pagefind JS: ${this.version}. Merged index (${this.basePath}): ${entry_json.version}`,"If you encounter any search errors, make sure that both sites are running the same version of Pagefind."].join("\n"))}}}catch(e){console.error(`Failed to load Pagefind metadata:
+${e?.toString()}`);throw new Error("Failed to load Pagefind metadata")}}findIndex(language){if(this.languages){let index=this.languages[language];if(index)return index;index=this.languages[language.split("-")[0]];if(index)return index;let topLang=Object.values(this.languages).sort((a,b)=>b.page_count-a.page_count);if(topLang[0])return topLang[0]}throw new Error("Pagefind Error: No language indexes found.")}async loadMeta(index){try{let compressed_resp=await fetch(`${this.basePath}pagefind.${index}.pf_meta`);let compressed_meta=await compressed_resp.arrayBuffer();this.searchMeta=this.decompress(new Uint8Array(compressed_meta),"Pagefind metadata")}catch(e){console.error(`Failed to load the meta index:
+${e?.toString()}`)}}async loadWasm(language){try{const wasm_url=`${this.basePath}wasm.${language}.pagefind`;let compressed_resp=await fetch(wasm_url);let compressed_wasm=await compressed_resp.arrayBuffer();const final_wasm=this.decompress(new Uint8Array(compressed_wasm),"Pagefind WebAssembly");if(!final_wasm){throw new Error("No WASM after decompression")}this.wasm=await this.backend(final_wasm)}catch(e){console.error(`Failed to load the Pagefind WASM:
+${e?.toString()}`);throw new Error(`Failed to load the Pagefind WASM:
+${e?.toString()}`)}}async _loadGenericChunk(url,method){try{let compressed_resp=await fetch(url);let compressed_chunk=await compressed_resp.arrayBuffer();let chunk=this.decompress(new Uint8Array(compressed_chunk),url);let ptr=await this.getPtr();this.raw_ptr=this.backend[method](ptr,chunk)}catch(e){console.error(`Failed to load the index chunk ${url}:
+${e?.toString()}`)}}async loadChunk(hash){if(!this.loaded_chunks[hash]){const url=`${this.basePath}index/${hash}.pf_index`;this.loaded_chunks[hash]=this._loadGenericChunk(url,"load_index_chunk")}return await this.loaded_chunks[hash]}async loadFilterChunk(hash){if(!this.loaded_filters[hash]){const url=`${this.basePath}filter/${hash}.pf_filter`;this.loaded_filters[hash]=this._loadGenericChunk(url,"load_filter_chunk")}return await this.loaded_filters[hash]}async _loadFragment(hash){let compressed_resp=await fetch(`${this.basePath}fragment/${hash}.pf_fragment`);let compressed_fragment=await compressed_resp.arrayBuffer();let fragment=this.decompress(new Uint8Array(compressed_fragment),`Fragment ${hash}`);return JSON.parse(new TextDecoder().decode(fragment))}async loadFragment(hash,weighted_locations=[],search_term){if(!this.loaded_fragments[hash]){this.loaded_fragments[hash]=this._loadFragment(hash)}let fragment=await this.loaded_fragments[hash];fragment.weighted_locations=weighted_locations;fragment.locations=weighted_locations.map((l)=>l.location);if(!fragment.raw_content){fragment.raw_content=fragment.content.replace(/</g,"&lt;").replace(/>/g,"&gt;");fragment.content=fragment.content.replace(/\u200B/g,"")}if(!fragment.raw_url){fragment.raw_url=fragment.url}fragment.url=this.processedUrl(fragment.raw_url,search_term);const excerpt_start=calculate_excerpt_region(weighted_locations,this.excerptLength);fragment.excerpt=build_excerpt(fragment.raw_content,excerpt_start,this.excerptLength,fragment.locations);fragment.sub_results=calculate_sub_results(fragment,this.excerptLength);return fragment}fullUrl(raw){if(/^(https?:)?\/\//.test(raw)){return raw}return`${this.baseUrl}/${raw}`.replace(/\/+/g,"/").replace(/^(https?:\/)/,"$1/")}processedUrl(url,search_term){const normalized=this.fullUrl(url);if(this.highlightParam===null){return normalized}let individual_terms=search_term.split(/\s+/);try{let processed=new URL(normalized);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term)}return processed.toString()}catch(e){try{let processed=new URL(`https://example.com${normalized}`);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term)}return processed.toString().replace(/^https:\/\/example\.com/,"")}catch(e2){return normalized}}}async getPtr(){while(this.raw_ptr===null){await asyncSleep(50)}if(!this.raw_ptr){console.error("Pagefind: WASM Error (No pointer)");throw new Error("Pagefind: WASM Error (No pointer)")}return this.raw_ptr}parseFilters(str){let output={};if(!str)return output;for(const block of str.split("__PF_FILTER_DELIM__")){let[filter,values]=block.split(/:(.*)$/);output[filter]={};if(values){for(const valueBlock of values.split("__PF_VALUE_DELIM__")){if(valueBlock){let extract=valueBlock.match(/^(.*):(\d+)$/);if(extract){let[,value,count]=extract;output[filter][value]=parseInt(count)??count}}}}}return output}stringifyFilters(obj={}){return JSON.stringify(obj)}stringifySorts(obj={}){let sorts=Object.entries(obj);for(let[sort,direction]of sorts){if(sorts.length>1){console.warn(`Pagefind was provided multiple sort options in this search, but can only operate on one. Using the ${sort} sort.`)}if(direction!=="asc"&&direction!=="desc"){console.warn(`Pagefind was provided a sort with unknown direction ${direction}. Supported: [asc, desc]`)}return`${sort}:${direction}`}return``}async filters(){let ptr=await this.getPtr();let filters2=this.backend.request_all_filter_indexes(ptr);let filter_chunks=filters2.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...filter_chunks]);ptr=await this.getPtr();let results=this.backend.filters(ptr);return this.parseFilters(results)}async preload(term,options2={}){await this.search(term,{...options2,preload:true})}async search(term,options2={}){options2={verbose:false,filters:{},sort:{},...options2};const log=(str)=>{if(options2.verbose)console.log(str)};log(`Starting search on ${this.basePath}`);let start=Date.now();let ptr=await this.getPtr();let filter_only=term===null;term=term??"";let exact_search=/^\s*".+"\s*$/.test(term);if(exact_search){log(`Running an exact search`)}term=term.toLowerCase().trim().replace(/[\.`~!@#\$%\^&\*\(\)\{\}\[\]\\\|:;'",<>\/\?\-]/g,"").replace(/\s{2,}/g," ").trim();log(`Normalized search term to ${term}`);if(!term?.length&&!filter_only){return{results:[],unfilteredResultCount:0,filters:{},totalFilters:{},timings:{preload:Date.now()-start,search:Date.now()-start,total:Date.now()-start}}}let sort_list=this.stringifySorts(options2.sort);log(`Stringified sort to ${sort_list}`);const filter_list=this.stringifyFilters(options2.filters);log(`Stringified filters to ${filter_list}`);let index_resp=this.backend.request_indexes(ptr,term);let filter_resp=this.backend.request_filter_indexes(ptr,filter_list);let chunks=index_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadChunk(chunk));let filter_chunks=filter_resp.split(" ").filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...chunks,...filter_chunks]);log(`Loaded necessary chunks to run search`);if(options2.preload){log(`Preload \u2014 bailing out of search operation now.`);return null}ptr=await this.getPtr();let searchStart=Date.now();let result=this.backend.search(ptr,term,filter_list,sort_list,exact_search);log(`Got the raw search result: ${result}`);let[unfilteredResultCount,all_results,filters2,totalFilters]=result.split(/:([^:]*):(.*)__PF_UNFILTERED_DELIM__(.*)$/);let filterObj=this.parseFilters(filters2);let totalFilterObj=this.parseFilters(totalFilters);log(`Remaining filters: ${JSON.stringify(result)}`);let results=all_results.length?all_results.split(" "):[];let resultsInterface=results.map((result2)=>{let[hash,score,all_locations]=result2.split("@");log(`Processing result:
+ hash:${hash}
+ score:${score}
+ locations:${all_locations}`);let weighted_locations=all_locations.length?all_locations.split(",").map((l)=>{let[weight,balanced_score,location]=l.split(">");return{weight:parseInt(weight)/24,balanced_score:parseFloat(balanced_score),location:parseInt(location)}}):[];let locations=weighted_locations.map((l)=>l.location);return{id:hash,score:parseFloat(score)*this.indexWeight,words:locations,data:async()=>await this.loadFragment(hash,weighted_locations,term)}});const searchTime=Date.now()-searchStart;const realTime=Date.now()-start;log(`Found ${results.length} result${results.length == 1 ? "" : "s"} for "${term}" in ${Date.now() - searchStart}ms (${Date.now() - start}ms realtime)`);return{results:resultsInterface,unfilteredResultCount:parseInt(unfilteredResultCount),filters:filterObj,totalFilters:totalFilterObj,timings:{preload:realTime-searchTime,search:searchTime,total:realTime}}}};var Pagefind=class{constructor(options2={}){this.backend=wasm_bindgen;this.primaryLanguage="unknown";this.searchID=0;this.primary=new PagefindInstance({...options2,primary:true});this.instances=[this.primary];this.init(options2?.language)}async options(options2){await this.primary.options(options2)}async init(overrideLanguage){if(document?.querySelector){const langCode=document.querySelector("html")?.getAttribute("lang")||"unknown";this.primaryLanguage=langCode.toLocaleLowerCase()}await this.primary.init(overrideLanguage?overrideLanguage:this.primaryLanguage,{load_wasm:true})}async mergeIndex(indexPath,options2={}){if(this.primary.basePath.startsWith(indexPath)){console.warn(`Skipping mergeIndex ${indexPath} that appears to be the same as the primary index (${this.primary.basePath})`);return}let newInstance=new PagefindInstance({primary:false,basePath:indexPath});this.instances.push(newInstance);while(this.primary.wasm===null){await asyncSleep(50)}await newInstance.init(options2.language||this.primaryLanguage,{load_wasm:false});delete options2["language"];await newInstance.options(options2)}mergeFilters(filters2){const merged={};for(const searchFilter of filters2){for(const[filterKey,values]of Object.entries(searchFilter)){if(!merged[filterKey]){merged[filterKey]=values;continue}else{const filter=merged[filterKey];for(const[valueKey,count]of Object.entries(values)){filter[valueKey]=(filter[valueKey]||0)+count}}}}return merged}async filters(){let filters2=await Promise.all(this.instances.map((i2)=>i2.filters()));return this.mergeFilters(filters2)}async preload(term,options2={}){await Promise.all(this.instances.map((i2)=>i2.preload(term,options2)))}async debouncedSearch(term,options2,debounceTimeoutMs){const thisSearchID=++this.searchID;this.preload(term,options2);await asyncSleep(debounceTimeoutMs);if(thisSearchID!==this.searchID){return null}const searchResult=await this.search(term,options2);if(thisSearchID!==this.searchID){return null}return searchResult}async search(term,options2={}){let search2=await Promise.all(this.instances.map((i2)=>i2.search(term,options2)));const filters2=this.mergeFilters(search2.map((s)=>s.filters));const totalFilters=this.mergeFilters(search2.map((s)=>s.totalFilters));const results=search2.map((s)=>s.results).flat().sort((a,b)=>b.score-a.score);const timings=search2.map((s)=>s.timings);const unfilteredResultCount=search2.reduce((sum,s)=>sum+s.unfilteredResultCount,0);return{results,unfilteredResultCount,filters:filters2,totalFilters,timings}}};var pagefind=void 0;var initial_options=void 0;var init_pagefind=()=>{if(!pagefind){pagefind=new Pagefind(initial_options??{})}};var options=async(new_options)=>{if(pagefind){await pagefind.options(new_options)}else{initial_options=new_options}};var init=async()=>{init_pagefind()};var destroy=async()=>{pagefind=void 0;initial_options=void 0};var mergeIndex=async(indexPath,options2)=>{init_pagefind();return await pagefind.mergeIndex(indexPath,options2)};var search=async(term,options2)=>{init_pagefind();return await pagefind.search(term,options2)};var debouncedSearch=async(term,options2,debounceTimeoutMs=300)=>{init_pagefind();return await pagefind.debouncedSearch(term,options2,debounceTimeoutMs)};var preload=async(term,options2)=>{init_pagefind();return await pagefind.preload(term,options2)};var filters=async()=>{init_pagefind();return await pagefind.filters()};export{debouncedSearch,destroy,filters,init,mergeIndex,options,preload,search} \ No newline at end of file
diff --git a/pagefind/pagefind.unknown_64739f45e64e1bc.pf_meta b/pagefind/pagefind.unknown_64739f45e64e1bc.pf_meta
new file mode 100644
index 0000000..4cdd169
--- /dev/null
+++ b/pagefind/pagefind.unknown_64739f45e64e1bc.pf_meta
Binary files differ
diff --git a/pagefind/wasm.unknown.pagefind b/pagefind/wasm.unknown.pagefind
new file mode 100644
index 0000000..4f30a2b
--- /dev/null
+++ b/pagefind/wasm.unknown.pagefind
Binary files differ
diff --git a/posts/index.html b/posts/index.html
index 43aabb4..9c21ce9 100644
--- a/posts/index.html
+++ b/posts/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -33,17 +41,30 @@
</header>
<main class="cards">
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/posts/index.xml b/posts/index.xml
index 54069ad..972fa31 100644
--- a/posts/index.xml
+++ b/posts/index.xml
@@ -4,7 +4,7 @@
<title>Posts on Home</title>
<link>http://jaseg.de/posts/</link>
<description>Recent content in Posts on Home</description>
- <generator>Hugo -- gohugo.io</generator>
+ <generator>Hugo</generator>
<language>en-us</language>
<copyright>Jan Sebastian Götte</copyright>
<atom:link href="http://jaseg.de/posts/index.xml" rel="self" type="application/rss+xml" />
diff --git a/projects/8seg/index.html b/projects/8seg/index.html
index 829fe70..0887902 100644
--- a/projects/8seg/index.html
+++ b/projects/8seg/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/8seg.git">Sources and hardware design files</a>
<a href="https://www.printables.com/model/695260-parametric-flexible-aluminium-profile-case">Enclosure 3D models</a>
@@ -47,17 +55,30 @@ variety of spaces, conforming to the space's size and shape through bending and
<p>By popular request, you can find 3D models for the printable parts of the power supply enclosures <a class="reference external" href="https://www.printables.com/model/695260-parametric-flexible-aluminium-profile-case">here</a></p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/gerbolyze/index.html b/projects/gerbolyze/index.html
index 575a54c..a4b13c1 100644
--- a/projects/gerbolyze/index.html
+++ b/projects/gerbolyze/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/gerbolyze.git">Sources</a>
<a href="https://github.com/jaseg/gerbolyze/issues">Issues</a>
@@ -66,7 +74,7 @@ anime silkscreen.</p>
<p>Gerbolyze is based on <a class="reference external" href="https://gitlab.com/gerbolyze/gerbonara">gerbonara</a>.</p>
<img alt="pics/process-overview.png" src="pics/process-overview.png" style="width: 800px;" />
<div class="contents topic" id="contents">
-<p class="topic-title">Contents</p>
+<p class="topic-title"><a class="reference internal" href="#top">Contents</a></p>
<ul class="simple">
<li><a class="reference internal" href="#tl-dr-produce-high-quality-artistic-pcbs-in-three-easy-steps" id="toc-entry-1">Tl;dr: Produce high-quality artistic PCBs in three easy steps!</a></li>
<li><a class="reference internal" href="#quick-start-installation-any-platform" id="toc-entry-2">Quick Start Installation (Any Platform)</a></li>
@@ -634,17 +642,30 @@ avoid that so the default license is still AGPL.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/gerbonara/index.html b/projects/gerbonara/index.html
index 70d2513..2326da9 100644
--- a/projects/gerbonara/index.html
+++ b/projects/gerbonara/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/gerbonara.git">Sources</a>
<a href="https://gitlab.com/gerbolyze/gerbonara/issues">Issues</a>
@@ -90,7 +98,7 @@ existing Gerber files exported from a normal PCB tool for artistic purposes.</p>
</span></pre>
<p>Then, you are ready to read and write gerber files:</p>
<pre class="code python literal-block">
-<span class="lineno"></span><span class="line"><span class="kn">from</span> <span class="nn">gerbonara</span> <span class="kn">import</span> <span class="n">LayerStack</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="kn">from</span><span class="w"> </span><span class="nn">gerbonara</span><span class="w"> </span><span class="kn">import</span> <span class="n">LayerStack</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">stack</span> <span class="o">=</span> <span class="n">LayerStack</span><span class="o">.</span><span class="n">from_directory</span><span class="p">(</span><span class="s1">'output/gerber'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">w</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="n">stack</span><span class="o">.</span><span class="n">outline</span><span class="o">.</span><span class="n">size</span><span class="p">(</span><span class="s1">'mm'</span><span class="p">)</span><span class="w"></span></span>
@@ -149,17 +157,30 @@ some non-standard naming convention.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/index.html b/projects/index.html
index 092f552..e4a6f1f 100644
--- a/projects/index.html
+++ b/projects/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects" class="active">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -181,17 +189,30 @@ open an issue on the project's issue tracker.</p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/index.xml b/projects/index.xml
index 2742a44..5a88e71 100644
--- a/projects/index.xml
+++ b/projects/index.xml
@@ -4,7 +4,7 @@
<title>Projects on Home</title>
<link>http://jaseg.de/projects/</link>
<description>Recent content in Projects on Home</description>
- <generator>Hugo -- gohugo.io</generator>
+ <generator>Hugo</generator>
<language>en-us</language>
<copyright>Jan Sebastian Götte</copyright>
<lastBuildDate>Wed, 04 Oct 2023 23:42:00 +0200</lastBuildDate>
@@ -14,88 +14,56 @@
<link>http://jaseg.de/projects/kimesh/</link>
<pubDate>Wed, 04 Oct 2023 23:42:00 +0200</pubDate>
<guid>http://jaseg.de/projects/kimesh/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;KiMesh is a KiCad plugin that automatically creates security meshes with two or traces covering an arbitrarily-shaped outline on the board.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>8seg</title>
<link>http://jaseg.de/projects/8seg/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/8seg/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a variety of spaces, conforming to the space&#39;s size and shape through bending and folding.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;8seg is an experimental textual display. It is made from a 45m by 1.5m large lacework banner that can be put up in a variety of spaces, conforming to the space&#39;s size and shape through bending and folding.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Gerbolyze</title>
<link>http://jaseg.de/projects/gerbolyze/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/gerbolyze/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Gerbolyze is a tool that allows the modification of Gerber PCB artwork with a vector graphics editor like Inkscape. Gerbolyze directly converts between SVG and Gerber, and accurately reproduces details that other tools can not.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>Gerbonara</title>
<link>http://jaseg.de/projects/gerbonara/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/gerbonara/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;Gerbonara is a user-friendly, powerful tool for reading, writing, modification and rendering of Gerber PCB artwork from the command line or from Python code. Gerbonara supports the Gerber dialects of all industry-standard EDA tools.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>lolcat-c</title>
<link>http://jaseg.de/projects/lolcat-c/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/lolcat-c/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;lolcat-c is a small, high-performance re-implementation of the &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/busyloop/lolcat&#34;&gt;lolcat&lt;/a&gt; rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;lolcat-c is a small, high-performance re-implementation of the &lt;a class=&#34;reference external&#34; href=&#34;https://github.com/busyloop/lolcat&#34;&gt;lolcat&lt;/a&gt; rainbow cat utility. lolcat-c is meant as a lolcat that you can actually use in production. It is fast, not slowing down whatever you pipe through it, and it robustly handles real-world terminal output including escape sequences.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>python-mpv</title>
<link>http://jaseg.de/projects/python-mpv/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/python-mpv/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;python-mpv is a small, ctypes-based Python library wrapping the libmpv media player library. Despite its small size and simple API, python-mpv allows advanced control over libmpv and beyond simple remote control of mpv can be used to embed mpv in OpenGL, Qt, and GTK-based Python applications.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>svg-flatten</title>
<link>http://jaseg.de/projects/svg-flatten/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/svg-flatten/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;svg-flatten is a command-line utility that performs vector occlusion and clipping on SVG files, producing a flattened SVG file without overlapping elements, without changing what the file looks like. svg-flatten is used as a part of gerbolyze.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
<item>
<title>wsdiff</title>
<link>http://jaseg.de/projects/wsdiff/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://jaseg.de/projects/wsdiff/</guid>
- <description>&lt;div class=&#34;document&#34;&gt;
-
-
-&lt;p&gt;wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.&lt;/p&gt;
-&lt;/div&gt;</description>
+ <description>&lt;div class=&#34;document&#34;&gt;&#xA;&#xA;&#xA;&lt;p&gt;wsdiff is a command-line utility that produces self-contained, syntax-highlighted, HTML-formatted diffs that support both unified and side-by-side diffs from a single source file using nothing but CSS magic.&lt;/p&gt;&#xA;&lt;/div&gt;</description>
</item>
</channel>
</rss>
diff --git a/projects/kimesh/index.html b/projects/kimesh/index.html
index c009284..9c8e318 100644
--- a/projects/kimesh/index.html
+++ b/projects/kimesh/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
<strong>2023-10-04</strong>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/kimesh.git">Sources</a>
<a href="https://github.com/jaseg/kimesh/issues">Issues</a>
@@ -88,17 +96,30 @@ higher-numbered pins are.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/lolcat-c/index.html b/projects/lolcat-c/index.html
index 0e96a0d..7397c2f 100644
--- a/projects/lolcat-c/index.html
+++ b/projects/lolcat-c/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/lolcat.git">Sources</a>
<a href="https://github.com/jaseg/lolcat">Github</a>
@@ -116,17 +124,30 @@
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/python-mpv/index.html b/projects/python-mpv/index.html
index 037e1d8..a692d13 100644
--- a/projects/python-mpv/index.html
+++ b/projects/python-mpv/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/python-mpv.git">Sources</a>
<a href="https://github.com/jaseg/python-mpv/issues">Issues</a>
@@ -82,7 +90,7 @@ since right now there is not many OSX users.</p>
<div class="section" id="usage">
<h2>Usage</h2>
<pre class="code python literal-block">
-<span class="lineno"></span><span class="line"><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'https://youtu.be/DOmdB7D-pUU'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">wait_for_playback</span><span class="p">()</span>
@@ -105,16 +113,16 @@ that can be passed to an event loop to tell it to wake up the python-mpv event h
<h4>Logging, Properties, Python Key Bindings, Screenshots and youtube-dl</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_log</span><span class="p">(</span><span class="n">loglevel</span><span class="p">,</span> <span class="n">component</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">my_log</span><span class="p">(</span><span class="n">loglevel</span><span class="p">,</span> <span class="n">component</span><span class="p">,</span> <span class="n">message</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'[</span><span class="si">{}</span><span class="s1">] </span><span class="si">{}</span><span class="s1">: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">loglevel</span><span class="p">,</span> <span class="n">component</span><span class="p">,</span> <span class="n">message</span><span class="p">))</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">log_handler</span><span class="o">=</span><span class="n">my_log</span><span class="p">,</span> <span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># Property access, these can be changed at runtime</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">&#64;player</span><span class="o">.</span><span class="n">property_observer</span><span class="p">(</span><span class="s1">'time-pos'</span><span class="p">)</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">time_observer</span><span class="p">(</span><span class="n">_name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">time_observer</span><span class="p">(</span><span class="n">_name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Here, _value is either None if nothing is playing or a float containing</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># fractional seconds since the beginning of the file.</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'Now playing at </span><span class="si">{:.2f}</span><span class="s1">s'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">value</span><span class="p">))</span><span class="w"></span></span>
@@ -125,11 +133,11 @@ that can be passed to an event loop to tell it to wake up the python-mpv event h
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="p">[</span><span class="s1">'vo'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'gpu'</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">&#64;player</span><span class="o">.</span><span class="n">on_key_press</span><span class="p">(</span><span class="s1">'q'</span><span class="p">)</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_q_binding</span><span class="p">():</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">my_q_binding</span><span class="p">():</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">print</span><span class="p">(</span><span class="s1">'THERE IS NO ESCAPE'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">&#64;player</span><span class="o">.</span><span class="n">on_key_press</span><span class="p">(</span><span class="s1">'s'</span><span class="p">)</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">my_s_binding</span><span class="p">():</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">my_s_binding</span><span class="p">():</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">pillow_img</span> <span class="o">=</span> <span class="n">player</span><span class="o">.</span><span class="n">screenshot_raw</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">pillow_img</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'screenshot.png'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
@@ -145,17 +153,17 @@ that can be passed to an event loop to tell it to wake up the python-mpv event h
the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on github (#202) for the original code!</p>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">sys</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">p</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">p</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">skip_silence</span><span class="p">():</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">skip_silence</span><span class="p">():</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">set_loglevel</span><span class="p">(</span><span class="s1">'debug'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">af</span> <span class="o">=</span> <span class="s1">'lavfi=[silencedetect=n=-20dB:d=1]'</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">p</span><span class="o">.</span><span class="n">speed</span> <span class="o">=</span> <span class="mi">100</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">evt</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">check</span><span class="p">(</span><span class="n">evt</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">toks</span> <span class="o">=</span> <span class="n">evt</span><span class="p">[</span><span class="s1">'event'</span><span class="p">][</span><span class="s1">'text'</span><span class="p">]</span><span class="o">.</span><span class="n">split</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">if</span> <span class="s1">'silence_end:'</span> <span class="ow">in</span> <span class="n">toks</span><span class="p">:</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">toks</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span><span class="w"></span></span>
@@ -171,9 +179,9 @@ the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on g
<h4>Video overlays</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">time</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PIL</span> <span class="kn">import</span> <span class="n">Image</span><span class="p">,</span> <span class="n">ImageDraw</span><span class="p">,</span> <span class="n">ImageFont</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">time</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">PIL</span><span class="w"> </span><span class="kn">import</span> <span class="n">Image</span><span class="p">,</span> <span class="n">ImageDraw</span><span class="p">,</span> <span class="n">ImageFont</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
@@ -208,7 +216,7 @@ the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on g
<h4>Playlist handling</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">ytdl</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_default_bindings</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">input_vo_keyboard</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
@@ -228,11 +236,11 @@ the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on g
<h4>Directly feeding mpv data from python</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="nd">&#64;player</span><span class="o">.</span><span class="n">python_stream</span><span class="p">(</span><span class="s1">'foo'</span><span class="p">)</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span> <span class="nf">reader</span><span class="p">():</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">def</span><span class="w"> </span><span class="nf">reader</span><span class="p">():</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">while</span> <span class="kc">True</span><span class="p">:</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">yield</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span><span class="p">)</span><span class="w"></span></span>
@@ -246,7 +254,7 @@ the filter, then parsing its output from mpv's log. Thanks to Sean DeNigris on g
<p>The easiest way to load custom subtitles from a file is to pass the <tt class="docutils literal"><span class="pre">--sub-file</span></tt> option to the <tt class="docutils literal">loadfile</tt> call:</p>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">loadfile</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">,</span> <span class="n">sub_file</span><span class="o">=</span><span class="s1">'test.srt'</span><span class="p">)</span><span class="w"></span></span>
@@ -258,7 +266,7 @@ called once the player is done loading the file and starts playing. An easy way
<tt class="docutils literal"><span class="pre">core-idle</span></tt> property.</p>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">player</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s1">'test.webm'</span><span class="p">)</span><span class="w"></span></span>
@@ -288,14 +296,14 @@ the MPV instance. See <a class="reference external" href="https://github.com/jas
<h4>PyQT embedding</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">sys</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">sys</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PyQt5.QtWidgets</span> <span class="kn">import</span> <span class="o">*</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">PyQt5.QtCore</span> <span class="kn">import</span> <span class="o">*</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">PyQt5.QtWidgets</span><span class="w"> </span><span class="kn">import</span> <span class="o">*</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">PyQt5.QtCore</span><span class="w"> </span><span class="kn">import</span> <span class="o">*</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span> <span class="nc">Test</span><span class="p">(</span><span class="n">QMainWindow</span><span class="p">):</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span><span class="w"> </span><span class="nc">Test</span><span class="p">(</span><span class="n">QMainWindow</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">container</span> <span class="o">=</span> <span class="n">QWidget</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">setCentralWidget</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">container</span><span class="p">)</span><span class="w"></span></span>
@@ -311,7 +319,7 @@ the MPV instance. See <a class="reference external" href="https://github.com/jas
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># This is necessary since PyQT stomps over the locale settings needed by libmpv.</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="c1"># This needs to happen after importing PyQT before creating the first mpv.MPV instance.</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">locale</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">locale</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">locale</span><span class="o">.</span><span class="n">setlocale</span><span class="p">(</span><span class="n">locale</span><span class="o">.</span><span class="n">LC_NUMERIC</span><span class="p">,</span> <span class="s1">'C'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">win</span> <span class="o">=</span> <span class="n">Test</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">win</span><span class="o">.</span><span class="n">show</span><span class="p">()</span><span class="w"></span></span>
@@ -322,17 +330,17 @@ the MPV instance. See <a class="reference external" href="https://github.com/jas
<h4>PyGObject embedding</h4>
<pre class="code python literal-block">
<span class="lineno"></span><span class="line"><span class="ch">#!/usr/bin/env python3</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">gi</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">gi</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span> <span class="nn">mpv</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">mpv</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span><span class="n">gi</span><span class="o">.</span><span class="n">require_version</span><span class="p">(</span><span class="s1">'Gtk'</span><span class="p">,</span> <span class="s1">'3.0'</span><span class="p">)</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span> <span class="nn">gi.repository</span> <span class="kn">import</span> <span class="n">Gtk</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="kn">from</span><span class="w"> </span><span class="nn">gi.repository</span><span class="w"> </span><span class="kn">import</span> <span class="n">Gtk</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span> <span class="nc">MainClass</span><span class="p">(</span><span class="n">Gtk</span><span class="o">.</span><span class="n">Window</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">class</span><span class="w"> </span><span class="nc">MainClass</span><span class="p">(</span><span class="n">Gtk</span><span class="o">.</span><span class="n">Window</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="nb">super</span><span class="p">(</span><span class="n">MainClass</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">set_default_size</span><span class="p">(</span><span class="mi">600</span><span class="p">,</span> <span class="mi">400</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="s2">&quot;destroy&quot;</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">on_destroy</span><span class="p">)</span><span class="w"></span></span>
@@ -345,7 +353,7 @@ the MPV instance. See <a class="reference external" href="https://github.com/jas
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span> <span class="o">=</span> <span class="n">mpv</span><span class="o">.</span><span class="n">MPV</span><span class="p">(</span><span class="n">wid</span><span class="o">=</span><span class="nb">str</span><span class="p">(</span><span class="n">widget</span><span class="o">.</span><span class="n">get_property</span><span class="p">(</span><span class="s2">&quot;window&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">get_xid</span><span class="p">()))</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span><span class="o">.</span><span class="n">play</span><span class="p">(</span><span class="s2">&quot;test.webm&quot;</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span> <span class="nf">on_destroy</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="k">def</span><span class="w"> </span><span class="nf">on_destroy</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">widget</span><span class="p">,</span> <span class="n">data</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="bp">self</span><span class="o">.</span><span class="n">mpv</span><span class="o">.</span><span class="n">terminate</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">Gtk</span><span class="o">.</span><span class="n">main_quit</span><span class="p">()</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
@@ -353,7 +361,7 @@ the MPV instance. See <a class="reference external" href="https://github.com/jas
<span class="lineno"></span><span class="line"><span class="w"></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># This is necessary since like Qt, Gtk stomps over the locale settings needed by libmpv.</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="c1"># Like with Qt, this needs to happen after importing Gtk but before creating the first mpv.MPV instance.</span><span class="w"></span></span>
-<span class="lineno"></span><span class="line"><span class="w"></span> <span class="kn">import</span> <span class="nn">locale</span><span class="w"></span></span>
+<span class="lineno"></span><span class="line"><span class="w"></span> <span class="kn">import</span><span class="w"> </span><span class="nn">locale</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">locale</span><span class="o">.</span><span class="n">setlocale</span><span class="p">(</span><span class="n">locale</span><span class="o">.</span><span class="n">LC_NUMERIC</span><span class="p">,</span> <span class="s1">'C'</span><span class="p">)</span><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span></span>
<span class="lineno"></span><span class="line"><span class="w"></span> <span class="n">application</span> <span class="o">=</span> <span class="n">MainClass</span><span class="p">()</span><span class="w"></span></span>
@@ -392,17 +400,30 @@ For details, see <a class="reference external" href="https://github.com/mpv-play
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/svg-flatten/index.html b/projects/svg-flatten/index.html
index cc80426..76a21d2 100644
--- a/projects/svg-flatten/index.html
+++ b/projects/svg-flatten/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/gerbolyze.git/tree/svg-flatten?h=main">Sources</a>
<a href="https://github.com/jaseg/gerbolyze/issues">Issues</a>
@@ -47,17 +55,30 @@ gerbolyze.</p>
<p>I developed svg-flatten as part of <a class="reference external" href="http://jaseg.de/projects/gerbolyze/">gerbolyze</a>, but it can be used independently.</p>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/projects/wsdiff/index.html b/projects/wsdiff/index.html
index 53c3c5a..ad0467b 100644
--- a/projects/wsdiff/index.html
+++ b/projects/wsdiff/index.html
@@ -7,6 +7,11 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="dark light">
<link rel="stylesheet" href="/style.css">
+
+ <link rel="preload" href="/fonts/roboto_slab/RobotoSlab-VariableFont_wght.ttf" as="font" type="font/ttf" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Regular.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-Bold.woff2" as="font" type="font/woff2" crossorigin />
+ <link rel="preload" href="/fonts/nyght-serif-main/fonts/WEB/NyghtSerif-BoldItalic.woff2" as="font" type="font/woff2" crossorigin />
</head>
<body><nav>
<div class="internal">
@@ -16,6 +21,9 @@
<a href="/projects/" title="Projects">Projects</a>
<a href="/about/" title="About">About</a>
</div>
+ <div class="search">
+ <div id="search"></div>
+ </div>
<div class="external">
<a href="https://git.jaseg.de/" title="cgit">cgit</a>
<a href="https://github.com/jaseg" title="Github">Github</a>
@@ -32,7 +40,7 @@
</ul>
</header>
- <main>
+ <main data-pagefind-body>
<div class="links">
<a href="https://git.jaseg.de/wsdiff.git">Sources</a>
<a href="https://github.com/jaseg/wsdiff/issues">Issues</a>
@@ -89,17 +97,30 @@ on available screen space.</p>
</div>
</div>
</main><footer>
- Copyright © 2023 Jan Sebastian Götte
+ Copyright © 2025 Jan Sebastian Götte
/ <a href="/about/">About</a>
/ <a href="/imprint/">Imprint</a>
</footer>
-<script>
- if(navigator.getEnvironmentIntegrity!==undefined)document.querySelector('body').innerHTML=`<h1>Your browser
- contains Google DRM</h1>"Web Environment Integrity" is a Google euphemism for a DRM that is designed to
- prevent ad-blocking, and which Google has forced into their browsers against widespread public opposition.
- In support of an open web, this website does not function with this DRM. Please install a browser such
- as <a href="https://www.mozilla.org/en-US/firefox/new/">Firefox</a> that respects your freedom and supports
- ad blockers.`;
+<script type="text/javascript" src="/pagefind/pagefind-ui.js" defer></script>
+ <script>
+ window.addEventListener('DOMContentLoaded', (event) => {
+ new PagefindUI({element: "#search", showSubResults: true});
+ });
+ </script>
+ <script type="speculationrules">
+ {
+ "prerender": [
+ {
+ "source": "document",
+ "where": {
+ "and": [
+ {"href_matches": "/*"}
+ ]
+ },
+ "eagerness": "moderate"
+ }
+ ]
+ }
</script>
</body>
</html>
diff --git a/sitemap.xml b/sitemap.xml
index 38e5fe2..a517566 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,13 +2,22 @@
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<url>
- <loc>http://jaseg.de/blog/8seg/</loc>
- <lastmod>2023-12-26T15:26:00+01:00</lastmod>
- </url><url>
<loc>http://jaseg.de/blog/</loc>
- <lastmod>2023-12-26T15:26:00+01:00</lastmod>
+ <lastmod>2025-07-25T23:42:00+01:00</lastmod>
</url><url>
<loc>http://jaseg.de/</loc>
+ <lastmod>2025-07-25T23:42:00+01:00</lastmod>
+ </url><url>
+ <loc>http://jaseg.de/blog/wsdiff-static-html-diffs/</loc>
+ <lastmod>2025-07-25T23:42:00+01:00</lastmod>
+ </url><url>
+ <loc>http://jaseg.de/blog/css-only-code-blocks/</loc>
+ <lastmod>2025-07-23T23:42:00+01:00</lastmod>
+ </url><url>
+ <loc>http://jaseg.de/blog/jupyterlab-notebook-file-oneliner/</loc>
+ <lastmod>2025-06-29T23:42:00+01:00</lastmod>
+ </url><url>
+ <loc>http://jaseg.de/blog/8seg/</loc>
<lastmod>2023-12-26T15:26:00+01:00</lastmod>
</url><url>
<loc>http://jaseg.de/projects/kimesh/</loc>
diff --git a/style.css b/style.css
index f137490..db4c318 100644
--- a/style.css
+++ b/style.css
@@ -267,6 +267,14 @@ h3 {
font-weight: 700;
}
+/* Prevent long literals from breaking the page layout's width */
+span.pre {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ overflow-x: auto;
+ overflow-wrap: anywhere;
+}
+
body > header {
z-index: 1;
margin-top: 100px;
@@ -471,9 +479,14 @@ img:hover {
filter: none;
}
+/*****************************************************/
+/* Code block formatting / syntax highlighting rules */
+/*****************************************************/
+
.code {
font-family: "Fira Code";
font-size: 13px;
+ text-align: left; /* Override default content "justify" alignment */
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: auto;
@@ -495,6 +508,7 @@ img:hover {
white-space: pre-wrap;
}
+/* We render line numbers in CSS! */
.code > .lineno {
counter-increment: lineno;
word-break: keep-all;
@@ -509,6 +523,7 @@ img:hover {
align-self: stretch;
}
+/* We also handle line continuation markers in CSS. */
.code > .lineno::after {
position: absolute;
right: 5px;
@@ -517,6 +532,7 @@ img:hover {
color: var(--c-text-muted);
}
+/* Insert the actual line number */
.code > .lineno::before {
content: counter(lineno);
}
@@ -525,6 +541,64 @@ img:hover {
counter-reset: lineno;
}
+/* Token styling rules for syntax highlighting */
+.code .hll {}
+.code .c { color: var(--c-text); font-weight: 400 } /* Comment */
+.code .n { color: var(--c-text); font-weight: 400 } /* Name */
+.code .o { color: var(--c-text); font-weight: 400 } /* Operator */
+.code .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */
+.code .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */
+.code .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */
+.code .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */
+.code .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */
+.code .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */
+.code .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */
+.code .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */
+.code .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */
+.code .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */
+.code .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */
+.code .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */
+.code .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */
+.code .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */
+.code .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */
+.code .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */
+.code .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */
+.code .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */
+.code .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */
+.code .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */
+.code .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */
+.code .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */
+.code .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */
+.code .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */
+.code .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */
+.code .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */
+.code .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */
+.code .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */
+.code .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */
+.code .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */
+.code .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */
+.code .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */
+.code .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */
+.code .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */
+.code .s { color: var(--c-text); font-weight: 600 } /* Literal.String */
+.code .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */
+.code .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */
+.code .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */
+.code .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */
+.code .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */
+.code .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */
+.code .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */
+.code .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */
+.code .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */
+.code .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */
+.code .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */
+.code .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */
+.code .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */
+.code .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */
+.code .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */
+.code .ss { color: var(--c-text); font-weight: 600 } /* Literal.String.Symbol */
+.code .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.Long */
+
footer {
margin-top: 30px;
align-self: center;
@@ -533,63 +607,6 @@ footer {
padding: 5px 25px 5px 25px;
}
-body .hll {}
-body .c { color: var(--c-text); font-weight: 400 } /* Comment */
-body .n { color: var(--c-text); font-weight: 400 } /* Name */
-body .o { color: var(--c-text); font-weight: 400 } /* Operator */
-body .cm { color: var(--c-text); font-weight: 400 } /* Comment.Multiline */
-body .cp { color: var(--c-text); font-weight: 400 } /* Comment.Preproc */
-body .c1 { color: var(--c-text); font-weight: 400 } /* Comment.Single */
-body .cs { color: var(--c-text); font-weight: 400 } /* Comment.Special */
-body .nd { color: var(--c-text); font-weight: 400 } /* Name.Decorator */
-body .nn { color: var(--c-text); font-weight: 400 } /* Name.Namespace */
-body .vc { color: var(--c-text); font-weight: 400 } /* Name.Variable.Class */
-body .vg { color: var(--c-text); font-weight: 400 } /* Name.Variable.Global */
-body .vi { color: var(--c-text); font-weight: 400 } /* Name.Variable.Instance */
-body .err { color: var(--c-text-highlight); font-weight: 500 } /* Error */
-body .k { color: var(--c-text-highlight); font-weight: 500 } /* Keyword */
-body .l { color: var(--c-text-highlight); font-weight: 500 } /* Literal */
-body .kc { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Constant */
-body .kd { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Declaration */
-body .kn { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Namespace */
-body .kp { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Pseudo */
-body .kr { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Reserved */
-body .kt { color: var(--c-text-highlight); font-weight: 500 } /* Keyword.Type */
-body .na { color: var(--c-text-highlight); font-weight: 500 } /* Name.Attribute */
-body .nb { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin */
-body .nc { color: var(--c-text-highlight); font-weight: 500 } /* Name.Class */
-body .no { color: var(--c-text-highlight); font-weight: 500 } /* Name.Constant */
-body .ni { color: var(--c-text-highlight); font-weight: 500 } /* Name.Entity */
-body .ne { color: var(--c-text-highlight); font-weight: 500 } /* Name.Exception */
-body .nf { color: var(--c-text-highlight); font-weight: 500 } /* Name.Function */
-body .nl { color: var(--c-text-highlight); font-weight: 500 } /* Name.Label */
-body .nx { color: var(--c-text-highlight); font-weight: 500 } /* Name.Other */
-body .py { color: var(--c-text-highlight); font-weight: 500 } /* Name.Property */
-body .nt { color: var(--c-text-highlight); font-weight: 500 } /* Name.Tag */
-body .nv { color: var(--c-text-highlight); font-weight: 500 } /* Name.Variable */
-body .ow { color: var(--c-text-highlight); font-weight: 500 } /* Operator.Word */
-body .bp { color: var(--c-text-highlight); font-weight: 500 } /* Name.Builtin.Pseudo */
-body .ld { color: var(--c-text); font-weight: 600 } /* Literal.Date */
-body .m { color: var(--c-text); font-weight: 600 } /* Literal.Number */
-body .s { color: var(--c-text); font-weight: 600 } /* Literal.String */
-body .mb { color: var(--c-text); font-weight: 600 } /* Literal.Number.Bin */
-body .mf { color: var(--c-text); font-weight: 600 } /* Literal.Number.Float */
-body .mh { color: var(--c-text); font-weight: 600 } /* Literal.Number.Hex */
-body .mi { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer */
-body .mo { color: var(--c-text); font-weight: 600 } /* Literal.Number.Oct */
-body .sb { color: var(--c-text); font-weight: 600 } /* Literal.String.Backtick */
-body .sc { color: var(--c-text); font-weight: 600 } /* Literal.String.Char */
-body .sd { color: var(--c-text); font-weight: 600 } /* Literal.String.Doc */
-body .s2 { color: var(--c-text); font-weight: 600 } /* Literal.String.Double */
-body .se { color: var(--c-text); font-weight: 600 } /* Literal.String.Escape */
-body .sh { color: var(--c-text); font-weight: 600 } /* Literal.String.Heredoc */
-body .si { color: var(--c-text); font-weight: 600 } /* Literal.String.Interpol */
-body .sx { color: var(--c-text); font-weight: 600 } /* Literal.String.Other */
-body .sr { color: var(--c-text); font-weight: 600 } /* Literal.String.Regex */
-body .s1 { color: var(--c-text); font-weight: 600 } /* Literal.String.Single */
-body .ss { color: var(--c-text); font-weight: 600 } /* Literal.String.Symbol */
-body .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.Long */
-
@media (max-width: 40em) {
nav > div {
flex-grow: 1;
@@ -601,6 +618,71 @@ body .il { color: var(--c-text); font-weight: 600 } /* Literal.Number.Integer.L
}
}
+.search {
+ align-self: center;
+ max-width: calc(100vw - 20px);
+ flex-grow: 1;
+ margin-left: 10px;
+ margin-right: 10px;
+}
+
+.pagefind-ui__form {
+ max-width: 100%;
+}
+
+.search input {
+ height: 1.5lh;
+ max-width: 100%;
+}
+
+#search, .pagefind-ui, #search form, #search input {
+ width: 100%;
+}
+
+.search button {
+ display: none;
+}
+
+.pagefind-ui__drawer {
+ position: fixed;
+ margin-top: 10px;
+ background: var(--c-bg-highlight);
+ max-width: min(100%, min(100vw, 40em));
+ max-height: 100%;
+ overflow-x: clip;
+ overflow-y: scroll;
+ text-overflow: clip;
+ z-index: 100;
+}
+
+.pagefind-ui__message {
+ font-family: "Nyght Serif";
+ font-weight: 700;
+ font-size: 16pt;
+}
+
+.pagefind-ui__results-area {
+ padding: 10px;
+}
+
+.pagefind-ui__results-area img {
+ max-width: min(50vw, 200px);
+}
+
+.pagefind-ui__results-area a {
+ max-width: min(50vw, 200px);
+ color: var(--c-fg-secondary) !important;
+ text-decoration: none;
+ padding: 10px 10px 10px 0 !important;
+ font-family: "Nyght Serif";
+ font-weight: 700;
+ font-size: 12pt;
+}
+
+.pagefind-ui__results-area figure, .pagefind-ui__results-area div.subfigure, .pagefind-ui__results-area img {
+ display: none;
+}
+
@media print, (prefers-color-scheme: light) {
html {
--c-bg-backdrop: hsl(220, 10%, 90%);
diff --git a/wsdiff-example.html b/wsdiff-example.html
new file mode 100644
index 0000000..59f6ceb
--- /dev/null
+++ b/wsdiff-example.html
@@ -0,0 +1,889 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>diff: example_old.py / example.py</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
+ <style id="wsd-main-style">
+ html, body {
+ margin: 0;
+ padding: 0;
+ font-family: sans-serif;
+ }
+
+
+
+@media (prefers-color-scheme: light) {
+ html {
+ --c-bg-primary: #ffffff;
+ --c-fg-primary: #000000;
+ --c-bg-auxiliary: #f8f8f8;
+ --c-fg-auxiliary: #a0a0a0;
+ --c-border-line: #e0e0e0;
+ --c-bg-insert: #ecfdf0;
+ --c-bg-delete: #fbe9eb;
+ --c-bg-delete-lineno: #f9d7dc;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #fac5cd;
+ --c-fg-delete-word: #400000;
+ --c-fg-insert-word: #004000;
+ --c-bg-insert-word: #c7f0d2;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #ddfbe6;
+ --c-bg-empty: #f0f0f0;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-delete: #e0c8c8; /* pick a darker border color inside the light red gutter */
+ }
+}
+
+@media (prefers-color-scheme: dark) {
+ html {
+ --c-bg-primary: #010409;
+ --c-fg-primary: #a0a0a0;
+ --c-bg-auxiliary: #0d1117;
+ --c-fg-auxiliary: #f0f6fc;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-line: #3d444d;
+ --c-bg-insert: #223738;
+ --c-bg-delete: #280d1f;
+ --c-bg-delete-lineno: #421632;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #421632;
+ --c-fg-delete-word: #fac5cd;
+ --c-fg-insert-word: #c7f0d2;
+ --c-bg-insert-word: #325148;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #325148;
+ --c-bg-empty: #080b0f;
+ --c-border-delete: #e0c8c8;
+ }
+}
+
+@media print {
+ html {
+ /* Copy of the light theme, but we clip all light gray backgrounds to white. */
+ --c-bg-primary: #ffffff;
+ --c-fg-primary: #000000;
+ --c-bg-auxiliary: #ffffff;
+ --c-fg-auxiliary: #a0a0a0;
+ --c-border-line: #e0e0e0;
+ --c-bg-insert: #ecfdf0;
+ --c-bg-delete: #fbe9eb;
+ --c-bg-delete-lineno: #f9d7dc;
+ --c-fg-delete-lineno: #ae969a;
+ --c-bg-delete-word: #fac5cd;
+ --c-fg-delete-word: #400000;
+ --c-fg-insert-word: #004000;
+ --c-bg-insert-word: #c7f0d2;
+ --c-fg-insert-lineno: #9bb0a1;
+ --c-bg-insert-lineno: #ddfbe6;
+ --c-bg-empty: #ffffff;
+ --c-fg-foldline: #bbbbbb;
+ --c-border-delete: #e0c8c8;
+ }
+}
+
+@layer wsd-base-style {
+ html {
+ background-color: var(--c-bg-primary);
+ height: 100%;
+ width: 100%;
+ }
+
+ #wsd-js-controls {
+ display: none;
+ color: var(--c-fg-primary);
+ background-color: var(--c-bg-auxiliary);
+ padding: 5px 20px;
+ font-size: 10pt;
+ font-weight: bold;
+ border: 1px solid var(--c-border-line);
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ flex-direction: row-reverse;
+ }
+
+ @media screen and (max-width: 40em) {
+ #wsd-js-controls {
+ position: initial;
+ }
+
+ .wsd-diff {
+ border-top: none;
+ }
+
+ .wsd-file-title {
+ background-color: var(--c-bg-auxiliary);
+ border-bottom: solid 1px var(--c-border-line);
+ }
+ }
+
+ #wsd-js-controls input[type="checkbox"] {
+ width: 20px;
+ height: 20px;
+ }
+
+ #wsd-js-controls input, #wsd-js-controls label, #wsd-js-controls .control-label {
+ vertical-align: middle;
+ }
+
+ .wsd-field-group {
+ display: inline-block;
+ }
+
+ .wsd-field {
+ white-space: nowrap;
+ display: inline-block;
+ }
+
+ #wsd-js-controls label {
+ font-weight: normal;
+ margin-right: .5em;
+ margin-left: 5px;
+ }
+
+ .wsd-control-label {
+ margin-right: .5em;
+ margin-left: 5px;
+ padding-bottom: 3px;
+ }
+
+ .wsd-file-container {
+ font-family: monospace;
+ font-size: 9pt;
+ background-color: var(--c-bg-auxiliary);
+ border: solid 1px var(--c-border-line);
+ margin: 15px;
+ }
+
+ .wsd-file-title {
+ padding: 10px 20px;
+ font-size: 10pt;
+ font-weight: bold;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ display: flex;
+ }
+
+ .wsd-filename {
+ max-width: 30em;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ direction: rtl;
+ }
+
+ .wsd-diff-files {
+ color: var(--c-fg-primary);
+ }
+
+ .wsd-diff {
+ background-color: var(--c-bg-primary);
+ overflow-x: auto;
+ display: grid;
+ align-items: start;
+ border-top: 1px solid var(--c-border-line);
+ }
+
+ .wsd-line {
+ padding-left: calc(4em + 5px);
+ text-indent: -4em;
+ padding-top: 2px;
+ align-self: stretch; /* Make sure empty lines don't collapse */
+ }
+
+ /* Make individual syntax tokens wrap anywhere */
+ .wsd-line > span {
+ overflow-wrap: anywhere;
+ white-space: pre-wrap;
+ }
+
+ .wsd-line {
+ min-width: 15em;
+ }
+
+ .wsd-line.wsd-left.wsd-change, .wsd-line.wsd-left.wsd-insert {
+ background-color: var(--c-bg-delete);
+ }
+
+ .wsd-line.wsd-right.wsd-change, .wsd-line.wsd-right.wsd-insert {
+ background-color: var(--c-bg-insert);
+ }
+
+ .wsd-lineno.wsd-left.wsd-change, .wsd-lineno.wsd-left.wsd-insert {
+ background-color: var(--c-bg-delete-lineno);
+ color: var(--c-fg-delete-lineno);
+ }
+
+ .wsd-lineno.wsd-right.wsd-change, .wsd-lineno.wsd-right.wsd-insert {
+ background-color: var(--c-bg-insert-lineno);
+ color: var(--c-fg-insert-lineno);
+ }
+
+ .wsd-right > .wsd-word-change {
+ background-color: var(--c-bg-insert-word);
+ color: var(--c-fg-insert-word);
+ }
+
+ .wsd-left > .wsd-word-change {
+ background-color: var(--c-bg-delete-word);
+ color: var(--c-fg-delete-word);
+ }
+
+ .wsd-lineno {
+ word-break: keep-all;
+ margin: 0;
+ padding-left: 30px;
+ padding-right: 5px;
+ overflow: clip;
+ position: relative;
+ text-align: right;
+ color: var(--c-fg-auxiliary);
+ background-color: var(--c-bg-auxiliary);
+ border-right: 1px solid var(--c-border-line);
+ align-self: stretch;
+ }
+
+ .wsd-lineno::after {
+ position: absolute;
+ right: 0;
+ content: "\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳\a↳";
+ white-space: pre;
+ color: var(--c-fg-auxiliary);
+ }
+
+ /* Default rules for split diff for wide screens (laptops) */
+ .wsd-diff {
+ grid-template-columns: min-content 1fr min-content 1fr;
+ }
+
+ .wsd-empty {
+ background-color: var(--c-bg-empty);
+ align-self: stretch;
+ }
+
+ /* line continuation arrows only in non-empty lines */
+ .wsd-lineno.wsd-empty::after {
+ content: "";
+ }
+
+ .wsd-lineno, .wsd-left {
+ user-select: none;
+ }
+
+ /* Collapsing runs of unchanged lines */
+ .wsd-collapse {
+ grid-column: 1 / span 4;
+ display: grid;
+ grid-template-columns: subgrid;
+ }
+
+ .wsd-collapse-controls {
+ grid-column: 1 / span 4;
+ display: flex;
+ justify-content: center;
+ color: var(--c-fg-auxiliary);
+
+ background-image: radial-gradient(var(--c-fg-foldline) 1px, transparent 0);
+ background-size: 10px 10px;
+ background-position: center;
+ background-repeat: repeat-x;
+ background-color: var(--c-bg-auxiliary)
+ }
+
+ .wsd-collapse-controls > label {
+ background-color: var(--c-bg-auxiliary);
+ }
+
+ .wsd-collapse:has(input[type="checkbox"]:checked) > span {
+ display: none;
+ }
+}
+
+@layer wsd-automatic-media-rule {
+ /* Unified diff for narrow screens (phones) */
+ @media screen and (max-width: 70em) {
+ .wsd-diff {
+ grid-auto-flow: dense;
+ grid-template-columns: min-content min-content 1fr;
+ }
+
+ .wsd-collapse, .wsd-collapse-controls {
+ grid-column: 1 / span 3;
+ }
+
+ .wsd-lineno {
+ padding-left: 1em;
+ }
+
+ .wsd-lineno.wsd-left {
+ grid-column: 1;
+ }
+
+ .wsd-lineno.wsd-left.wsd-change, .wsd-lineno.wsd-right.wsd-change {
+ grid-column: 1 / span 2;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ padding-left: 0;
+ padding-right: 0;
+ grid-auto-flow: dense;
+ /* To make alignment of left line number work, since we loose margin and padding control using ::before. */
+ column-gap: 10px;
+ }
+
+ .wsd-lineno.wsd-right.wsd-change::before {
+ content: "";
+ align-self: stretch;
+ grid-column: 1;
+ border-right: 1px solid var(--c-border-line);
+ margin-right: -6px; /* move border into column gap, and 1px over to align with other borders */
+ }
+
+ .wsd-lineno.wsd-left.wsd-change::before {
+ content: "";
+ align-self: stretch;
+ grid-column: 2;
+ border-left: 1px solid var(--c-border-delete);
+ margin-left: -5px;
+ }
+
+ .wsd-lineno.wsd-left.wsd-insert {
+ border-right: 1px solid var(--c-border-delete);
+ }
+
+ .wsd-lineno.wsd-right.wsd-change::after {
+ grid-column: 2;
+ }
+
+ .wsd-lineno.wsd-left.wsd-insert {
+ grid-column: 1 / span 2;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-auto-flow: dense;
+ column-gap: 10px;
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .wsd-lineno.wsd-right {
+ grid-column: 2;
+ }
+
+ .wsd-lineno.wsd-right.wsd-insert {
+ grid-column: 2;
+ }
+
+ .wsd-line.wsd-left, .wsd-line.wsd-right.wsd-empty {
+ display: none;
+ }
+
+ .wsd-line {
+ grid-column: 3;
+ }
+
+ .wsd-line.wsd-left.wsd-insert {
+ display: block;
+ }
+
+ .wsd-line.wsd-left.wsd-change {
+ display: block;
+ }
+
+ .wsd-lineno.wsd-right.wsd-empty {
+ display: none;
+ }
+
+ .wsd-lineno.wsd-left.wsd-empty {
+ background-color: var(--c-bg-insert-lineno);
+ }
+
+ /* line continuation arrows only in right line number column */
+ .wsd-lineno.wsd-left.wsd-insert::after {
+ }
+
+ .wsd-lineno.wsd-left.wsd-insert::before {
+ content: "";
+ grid-column: 2;
+ border-left: 1px solid var(--c-border-delete); /* pick a darker border color inside the light red gutter */
+ margin-left: -5px;
+ }
+ }
+}
+
+ </style>
+ <style>
+ @media print, (prefers-color-scheme: light) {
+ pre { line-height: 125%; }
+td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+.hll { background-color: #ffffcc }
+.wsd-c { color: #177500 } /* Comment */
+.wsd-err { color: #000 } /* Error */
+.wsd-k { color: #A90D91 } /* Keyword */
+.wsd-l { color: #1C01CE } /* Literal */
+.wsd-n { color: #000 } /* Name */
+.wsd-o { color: #000 } /* Operator */
+.wsd-ch { color: #177500 } /* Comment.Hashbang */
+.wsd-cm { color: #177500 } /* Comment.Multiline */
+.wsd-cp { color: #633820 } /* Comment.Preproc */
+.wsd-cpf { color: #177500 } /* Comment.PreprocFile */
+.wsd-c1 { color: #177500 } /* Comment.Single */
+.wsd-cs { color: #177500 } /* Comment.Special */
+.wsd-kc { color: #A90D91 } /* Keyword.Constant */
+.wsd-kd { color: #A90D91 } /* Keyword.Declaration */
+.wsd-kn { color: #A90D91 } /* Keyword.Namespace */
+.wsd-kp { color: #A90D91 } /* Keyword.Pseudo */
+.wsd-kr { color: #A90D91 } /* Keyword.Reserved */
+.wsd-kt { color: #A90D91 } /* Keyword.Type */
+.wsd-ld { color: #1C01CE } /* Literal.Date */
+.wsd-m { color: #1C01CE } /* Literal.Number */
+.wsd-s { color: #C41A16 } /* Literal.String */
+.wsd-na { color: #836C28 } /* Name.Attribute */
+.wsd-nb { color: #A90D91 } /* Name.Builtin */
+.wsd-nc { color: #3F6E75 } /* Name.Class */
+.wsd-no { color: #000 } /* Name.Constant */
+.wsd-nd { color: #000 } /* Name.Decorator */
+.wsd-ni { color: #000 } /* Name.Entity */
+.wsd-ne { color: #000 } /* Name.Exception */
+.wsd-nf { color: #000 } /* Name.Function */
+.wsd-nl { color: #000 } /* Name.Label */
+.wsd-nn { color: #000 } /* Name.Namespace */
+.wsd-nx { color: #000 } /* Name.Other */
+.wsd-py { color: #000 } /* Name.Property */
+.wsd-nt { color: #000 } /* Name.Tag */
+.wsd-nv { color: #000 } /* Name.Variable */
+.wsd-ow { color: #000 } /* Operator.Word */
+.wsd-mb { color: #1C01CE } /* Literal.Number.Bin */
+.wsd-mf { color: #1C01CE } /* Literal.Number.Float */
+.wsd-mh { color: #1C01CE } /* Literal.Number.Hex */
+.wsd-mi { color: #1C01CE } /* Literal.Number.Integer */
+.wsd-mo { color: #1C01CE } /* Literal.Number.Oct */
+.wsd-sa { color: #C41A16 } /* Literal.String.Affix */
+.wsd-sb { color: #C41A16 } /* Literal.String.Backtick */
+.wsd-sc { color: #2300CE } /* Literal.String.Char */
+.wsd-dl { color: #C41A16 } /* Literal.String.Delimiter */
+.wsd-sd { color: #C41A16 } /* Literal.String.Doc */
+.wsd-s2 { color: #C41A16 } /* Literal.String.Double */
+.wsd-se { color: #C41A16 } /* Literal.String.Escape */
+.wsd-sh { color: #C41A16 } /* Literal.String.Heredoc */
+.wsd-si { color: #C41A16 } /* Literal.String.Interpol */
+.wsd-sx { color: #C41A16 } /* Literal.String.Other */
+.wsd-sr { color: #C41A16 } /* Literal.String.Regex */
+.wsd-s1 { color: #C41A16 } /* Literal.String.Single */
+.wsd-ss { color: #C41A16 } /* Literal.String.Symbol */
+.wsd-bp { color: #5B269A } /* Name.Builtin.Pseudo */
+.wsd-fm { color: #000 } /* Name.Function.Magic */
+.wsd-vc { color: #000 } /* Name.Variable.Class */
+.wsd-vg { color: #000 } /* Name.Variable.Global */
+.wsd-vi { color: #000 } /* Name.Variable.Instance */
+.wsd-vm { color: #000 } /* Name.Variable.Magic */
+.wsd-il { color: #1C01CE } /* Literal.Number.Integer.Long */
+ }
+
+ @media (prefers-color-scheme: dark) {
+ pre { line-height: 125%; }
+td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
+td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
+.hll { background-color: #716799 }
+.wsd-c { color: #B0BEC5 } /* Comment */
+.wsd-err { color: #960050; background-color: #1E0010 } /* Error */
+.wsd-k { color: #C2FFDF } /* Keyword */
+.wsd-l { color: #AE81FF } /* Literal */
+.wsd-n { color: #F8F8F2 } /* Name */
+.wsd-o { color: #FFB8D1 } /* Operator */
+.wsd-p { color: #F8F8F2 } /* Punctuation */
+.wsd-ch { color: #B0BEC5 } /* Comment.Hashbang */
+.wsd-cm { color: #B0BEC5 } /* Comment.Multiline */
+.wsd-cp { color: #B0BEC5 } /* Comment.Preproc */
+.wsd-cpf { color: #B0BEC5 } /* Comment.PreprocFile */
+.wsd-c1 { color: #B0BEC5 } /* Comment.Single */
+.wsd-cs { color: #B0BEC5 } /* Comment.Special */
+.wsd-gd { color: #F92672 } /* Generic.Deleted */
+.wsd-ge { font-style: italic } /* Generic.Emph */
+.wsd-gi { color: #A6E22E } /* Generic.Inserted */
+.wsd-gs { font-weight: bold } /* Generic.Strong */
+.wsd-gu { color: #75715E } /* Generic.Subheading */
+.wsd-kc { color: #C2FFDF } /* Keyword.Constant */
+.wsd-kd { color: #C2FFDF } /* Keyword.Declaration */
+.wsd-kn { color: #FFB8D1 } /* Keyword.Namespace */
+.wsd-kp { color: #C2FFDF } /* Keyword.Pseudo */
+.wsd-kr { color: #C2FFDF } /* Keyword.Reserved */
+.wsd-kt { color: #C2FFDF } /* Keyword.Type */
+.wsd-ld { color: #E6DB74 } /* Literal.Date */
+.wsd-m { color: #C5A3FF } /* Literal.Number */
+.wsd-s { color: #1BC5E0 } /* Literal.String */
+.wsd-na { color: #CEB1FF } /* Name.Attribute */
+.wsd-nb { color: #F8F8F2 } /* Name.Builtin */
+.wsd-nc { color: #CEB1FF } /* Name.Class */
+.wsd-no { color: #C5A3FF } /* Name.Constant */
+.wsd-nd { color: #CEB1FF } /* Name.Decorator */
+.wsd-ni { color: #F8F8F2 } /* Name.Entity */
+.wsd-ne { color: #CEB1FF } /* Name.Exception */
+.wsd-nf { color: #CEB1FF } /* Name.Function */
+.wsd-nl { color: #F8F8F2 } /* Name.Label */
+.wsd-nn { color: #F8F8F2 } /* Name.Namespace */
+.wsd-nx { color: #F8F8F2 } /* Name.Other */
+.wsd-py { color: #F8F8F2 } /* Name.Property */
+.wsd-nt { color: #FFB8D1 } /* Name.Tag */
+.wsd-nv { color: #F8F8F2 } /* Name.Variable */
+.wsd-ow { color: #FFB8D1 } /* Operator.Word */
+.wsd-pm { color: #F8F8F2 } /* Punctuation.Marker */
+.wsd-w { color: #A8757B } /* Text.Whitespace */
+.wsd-mb { color: #C5A3FF } /* Literal.Number.Bin */
+.wsd-mf { color: #C5A3FF } /* Literal.Number.Float */
+.wsd-mh { color: #C5A3FF } /* Literal.Number.Hex */
+.wsd-mi { color: #C5A3FF } /* Literal.Number.Integer */
+.wsd-mo { color: #C5A3FF } /* Literal.Number.Oct */
+.wsd-sa { color: #1BC5E0 } /* Literal.String.Affix */
+.wsd-sb { color: #1BC5E0 } /* Literal.String.Backtick */
+.wsd-sc { color: #1BC5E0 } /* Literal.String.Char */
+.wsd-dl { color: #1BC5E0 } /* Literal.String.Delimiter */
+.wsd-sd { color: #1BC5E0 } /* Literal.String.Doc */
+.wsd-s2 { color: #1BC5E0 } /* Literal.String.Double */
+.wsd-se { color: #1BC5E0 } /* Literal.String.Escape */
+.wsd-sh { color: #1BC5E0 } /* Literal.String.Heredoc */
+.wsd-si { color: #1BC5E0 } /* Literal.String.Interpol */
+.wsd-sx { color: #1BC5E0 } /* Literal.String.Other */
+.wsd-sr { color: #1BC5E0 } /* Literal.String.Regex */
+.wsd-s1 { color: #1BC5E0 } /* Literal.String.Single */
+.wsd-ss { color: #1BC5E0 } /* Literal.String.Symbol */
+.wsd-bp { color: #80CBC4 } /* Name.Builtin.Pseudo */
+.wsd-fm { color: #CEB1FF } /* Name.Function.Magic */
+.wsd-vc { color: #F8F8F2 } /* Name.Variable.Class */
+.wsd-vg { color: #F8F8F2 } /* Name.Variable.Global */
+.wsd-vi { color: #F8F8F2 } /* Name.Variable.Instance */
+.wsd-vm { color: #F8F8F2 } /* Name.Variable.Magic */
+.wsd-il { color: #C5A3FF } /* Literal.Number.Integer.Long */
+ }
+ </style>
+ </head>
+ <body>
+
+ <div id="wsd-js-controls">
+ <div class="wsd-single-control">
+ <span class="wsd-control-label">Split view</span>
+ <span class="wsd-three-way-toggle">
+ <div class="wsd-field-group">
+ <div class="wsd-field"><input type="checkbox" id="wsd-toggle-split-auto" checked></input><label for="wsd-toggle-split-auto">Auto</label></div>
+ <div class="wsd-field"><input type="checkbox" id="wsd-toggle-split-force" disabled></input><label for="wsd-toggle-split-force">Split view</label></div>
+ </div>
+ </span>
+ </div>
+ </div>
+
+ <script>
+
+ const findStylesheet = (id => Array.from(document.styleSheets).find(element => element.ownerNode && element.ownerNode.id == id));
+ const findRule = ((stylesheet, name) => Array.from(stylesheet.cssRules).find(
+ element => (element instanceof CSSLayerBlockRule && element.name == name)).cssRules[0]);
+
+ const automaticMediaElement = findRule(findStylesheet('wsd-main-style'), 'wsd-automatic-media-rule');
+ const automaticMediaRule = automaticMediaElement.media[0];
+ const impossibleMediaRule = "screen and (max-width: 0px)";
+ const tautologicalMediaRule = "screen and (min-width: 0px)";
+
+ const toggleAuto = document.getElementById("wsd-toggle-split-auto");
+ const toggleForce = document.getElementById("wsd-toggle-split-force");
+ toggleAuto.checked = true;
+ toggleForce.disabled = true;
+
+ toggleAuto.addEventListener('change', (event) => {
+ const automatic = toggleAuto.checked;
+ toggleForce.disabled = automatic;
+ if (automatic) {
+ automaticMediaElement.media.deleteMedium(automaticMediaElement.media[0]);
+ automaticMediaElement.media.appendMedium(automaticMediaRule);
+ } else {
+ automaticMediaElement.media.deleteMedium(automaticMediaRule);
+ if (toggleForce.checked) {
+ automaticMediaElement.media.appendMedium(impossibleMediaRule);
+ } else {
+ automaticMediaElement.media.appendMedium(tautologicalMediaRule);
+ }
+ }
+ });
+
+ toggleForce.addEventListener('change', (event) => {
+ const automatic = toggleAuto.checked;
+ if (!automatic) {
+ automaticMediaElement.media.deleteMedium(automaticMediaElement.media[0]);
+ if (toggleForce.checked) {
+ automaticMediaElement.media.appendMedium(impossibleMediaRule);
+ } else {
+ automaticMediaElement.media.appendMedium(tautologicalMediaRule);
+ }
+ }
+ });
+
+ const mediaMatch = window.matchMedia(automaticMediaRule);
+ mediaMatch.addEventListener('change', (event) => {
+ const automatic = toggleAuto.checked;
+ if (automatic) {
+ toggleForce.checked = !event.matches;
+ }
+ });
+ toggleForce.checked = !mediaMatch.matches;
+
+ document.getElementById('wsd-js-controls').style = 'display: flex';
+
+ </script>
+ <div class="wsd-diff-files">
+ <div class="wsd-file-container">
+ <div class="wsd-file-title"><div class="wsd-filename">&#x202D;example.py</div></div>
+ <div class="wsd-diff">
+ <span class="wsd-lineno wsd-left">1</span><span class="wsd-line wsd-left"><span class="wsd-ch">#!/usr/bin/env python3</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">1</span><span class="wsd-line wsd-right"><span class="wsd-ch">#!/usr/bin/env python3</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">2</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">2</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">3</span><span class="wsd-line wsd-left"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">math</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">3</span><span class="wsd-line wsd-right"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">math</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">4</span><span class="wsd-line wsd-left"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">itertools</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">4</span><span class="wsd-line wsd-right"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">itertools</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">5</span><span class="wsd-line wsd-left"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">textwrap</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">5</span><span class="wsd-line wsd-right"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">textwrap</span><span class="wsd-w"></span></span></span>
+<div class="wsd-collapse"><div class="wsd-collapse-controls"><label><input type="checkbox" checked> Collapse 24 unchanged lines</label></div>
+<span class="wsd-lineno wsd-left">6</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">6</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">7</span><span class="wsd-line wsd-left"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">click</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">7</span><span class="wsd-line wsd-right"><span class="wsd-kn">import</span><span class="wsd-w"> </span><span class="wsd-nn">click</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">8</span><span class="wsd-line wsd-left"><span class="wsd-kn">from</span><span class="wsd-w"> </span><span class="wsd-nn">reedmuller</span><span class="wsd-w"> </span><span class="wsd-kn">import</span><span class="n"> </span><span class="wsd-n">reedmuller</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">8</span><span class="wsd-line wsd-right"><span class="wsd-kn">from</span><span class="wsd-w"> </span><span class="wsd-nn">reedmuller</span><span class="wsd-w"> </span><span class="wsd-kn">import</span><span class="n"> </span><span class="wsd-n">reedmuller</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">9</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">9</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">10</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">10</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">11</span><span class="wsd-line wsd-left"><span class="wsd-k">class</span><span class="wsd-w"> </span><span class="wsd-nc">Tag</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">11</span><span class="wsd-line wsd-right"><span class="wsd-k">class</span><span class="wsd-w"> </span><span class="wsd-nc">Tag</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">12</span><span class="wsd-line wsd-left"><span class="wsd-w"> </span><span class="wsd-sd">&quot;&quot;&quot; Helper class to ease creation of SVG. All API functions that create SVG allow you to substitute this with your</span></span></span>
+<span class="wsd-lineno wsd-right">12</span><span class="wsd-line wsd-right"><span class="wsd-w"> </span><span class="wsd-sd">&quot;&quot;&quot; Helper class to ease creation of SVG. All API functions that create SVG allow you to substitute this with your</span></span></span>
+<span class="wsd-lineno wsd-left">13</span><span class="wsd-line wsd-left"><span class="wsd-sd"> own implementation by passing a ``tag`` parameter. &quot;&quot;&quot;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">13</span><span class="wsd-line wsd-right"><span class="wsd-sd"> own implementation by passing a ``tag`` parameter. &quot;&quot;&quot;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">14</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">14</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">15</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-fm">__init__</span><span class="wsd-p">(</span><span class="wsd-bp">self</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">children</span><span class="wsd-o">=</span><span class="wsd-kc">None</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-o">=</span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-o">*</span><span class="wsd-n">attrs</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">15</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-fm">__init__</span><span class="wsd-p">(</span><span class="wsd-bp">self</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">children</span><span class="wsd-o">=</span><span class="wsd-kc">None</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-o">=</span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-o">*</span><span class="wsd-n">attrs</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">16</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">fill</span><span class="n"> </span><span class="wsd-o">:=</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">get</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-ow">and</span><span class="n"> </span><span class="wsd-nb">isinstance</span><span class="wsd-p">(</span><span class="wsd-n">fill</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">tuple</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">16</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">fill</span><span class="n"> </span><span class="wsd-o">:=</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">get</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-ow">and</span><span class="n"> </span><span class="wsd-nb">isinstance</span><span class="wsd-p">(</span><span class="wsd-n">fill</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">tuple</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">17</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill-opacity</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">fill</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">17</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">fill-opacity</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">fill</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">18</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">stroke</span><span class="n"> </span><span class="wsd-o">:=</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">get</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-ow">and</span><span class="n"> </span><span class="wsd-nb">isinstance</span><span class="wsd-p">(</span><span class="wsd-n">stroke</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">tuple</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">18</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">stroke</span><span class="n"> </span><span class="wsd-o">:=</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">get</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-ow">and</span><span class="n"> </span><span class="wsd-nb">isinstance</span><span class="wsd-p">(</span><span class="wsd-n">stroke</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">tuple</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">19</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke-opacity</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">stroke</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">19</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-p">[</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">stroke-opacity</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">stroke</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">20</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">attrs</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">20</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">attrs</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">name</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">attrs</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">21</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-ow">or</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">21</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-ow">or</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">22</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">root</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">22</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">root</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">23</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">23</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">24</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-fm">__str__</span><span class="wsd-p">(</span><span class="wsd-bp">self</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">24</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-fm">__str__</span><span class="wsd-p">(</span><span class="wsd-bp">self</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">25</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">prefix</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">&lt;?xml version=</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">1.0</span><span class="wsd-s1">&quot;</span><span class="wsd-s1"> encoding=</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">utf-8</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">?&gt;</span><span class="wsd-se">\n</span><span class="wsd-s1">&#x27;</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">root</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">25</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">prefix</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">&lt;?xml version=</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">1.0</span><span class="wsd-s1">&quot;</span><span class="wsd-s1"> encoding=</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">utf-8</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">?&gt;</span><span class="wsd-se">\n</span><span class="wsd-s1">&#x27;</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">root</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">26</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">opening</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-o">.</span><span class="wsd-n">join</span><span class="wsd-p">(</span><span class="wsd-p">[</span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">key</span><span class="wsd-o">.</span><span class="wsd-n">replace</span><span class="wsd-p">(</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">__</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"> </span><span class="wsd-s2">&quot;</span><span class="wsd-s2">:</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-o">.</span><span class="wsd-n">replace</span><span class="wsd-p">(</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">_</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"> </span><span class="wsd-s2">&quot;</span><span class="wsd-s2">-</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-si">}</span><span class="wsd-s1">=</span><span class="wsd-s1">&quot;</span><span class="wsd-si">{</span><span class="wsd-n">value</span><span class="wsd-si">}</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">&#x27;</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">value</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">items</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">26</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">opening</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-o">.</span><span class="wsd-n">join</span><span class="wsd-p">(</span><span class="wsd-p">[</span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-p">]</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">key</span><span class="wsd-o">.</span><span class="wsd-n">replace</span><span class="wsd-p">(</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">__</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"> </span><span class="wsd-s2">&quot;</span><span class="wsd-s2">:</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-o">.</span><span class="wsd-n">replace</span><span class="wsd-p">(</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">_</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"> </span><span class="wsd-s2">&quot;</span><span class="wsd-s2">-</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-si">}</span><span class="wsd-s1">=</span><span class="wsd-s1">&quot;</span><span class="wsd-si">{</span><span class="wsd-n">value</span><span class="wsd-si">}</span><span class="wsd-s1">&quot;</span><span class="wsd-s1">&#x27;</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">value</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">attrs</span><span class="wsd-o">.</span><span class="wsd-n">items</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">27</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">27</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">28</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-se">\n</span><span class="wsd-s1">&#x27;</span><span class="wsd-o">.</span><span class="wsd-n">join</span><span class="wsd-p">(</span><span class="wsd-n">textwrap</span><span class="wsd-o">.</span><span class="wsd-n">indent</span><span class="wsd-p">(</span><span class="wsd-nb">str</span><span class="wsd-p">(</span><span class="wsd-n">c</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">c</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">28</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">children</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-se">\n</span><span class="wsd-s1">&#x27;</span><span class="wsd-o">.</span><span class="wsd-n">join</span><span class="wsd-p">(</span><span class="wsd-n">textwrap</span><span class="wsd-o">.</span><span class="wsd-n">indent</span><span class="wsd-p">(</span><span class="wsd-nb">str</span><span class="wsd-p">(</span><span class="wsd-n">c</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">c</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">children</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">29</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">prefix</span><span class="wsd-si">}</span><span class="wsd-s1">&lt;</span><span class="wsd-si">{</span><span class="wsd-n">opening</span><span class="wsd-si">}</span><span class="wsd-s1">&gt;</span><span class="wsd-se">\n</span><span class="wsd-si">{</span><span class="wsd-n">children</span><span class="wsd-si">}</span><span class="wsd-se">\n</span><span class="wsd-s1">&lt;/</span><span class="wsd-si">{</span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-si">}</span><span class="wsd-s1">&gt;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">29</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">prefix</span><span class="wsd-si">}</span><span class="wsd-s1">&lt;</span><span class="wsd-si">{</span><span class="wsd-n">opening</span><span class="wsd-si">}</span><span class="wsd-s1">&gt;</span><span class="wsd-se">\n</span><span class="wsd-si">{</span><span class="wsd-n">children</span><span class="wsd-si">}</span><span class="wsd-se">\n</span><span class="wsd-s1">&lt;/</span><span class="wsd-si">{</span><span class="wsd-bp">self</span><span class="wsd-o">.</span><span class="wsd-n">name</span><span class="wsd-si">}</span><span class="wsd-s1">&gt;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+</div>
+<span class="wsd-lineno wsd-left">30</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">else</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">30</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">else</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">31</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">prefix</span><span class="wsd-si">}</span><span class="wsd-s1">&lt;</span><span class="wsd-si">{</span><span class="wsd-n">opening</span><span class="wsd-si">}</span><span class="wsd-s1">/&gt;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">31</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">prefix</span><span class="wsd-si">}</span><span class="wsd-s1">&lt;</span><span class="wsd-si">{</span><span class="wsd-n">opening</span><span class="wsd-si">}</span><span class="wsd-s1">/&gt;</span><span class="wsd-s1">&#x27;</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">32</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">32</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">33</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">33</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">34</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-nd">@classmethod</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">34</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-nd">@classmethod</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-change">35</span><span class="wsd-line wsd-left wsd-change"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-nf">setup_svg</span><span class="wsd-p">(</span><span class="wsd-n">kls</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-p">,</span><span class="wsd-n"></span><span class="n wsd-word-change"> </span><span class="wsd-n wsd-word-change">margin</span><span class="wsd-o wsd-word-change">=</span><span class="wsd-mi wsd-word-change">0</span><span class="wsd-p wsd-word-change">,</span><span class="wsd-n wsd-word-change"></span><span class="n"> </span><span class="wsd-n">unit</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">pagecolor</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">white</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-change">35</span><span class="wsd-line wsd-right wsd-change"><span class="n"> </span><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-nf">setup_svg</span><span class="wsd-p">(</span><span class="wsd-n">kls</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">unit</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">pagecolor</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">white</span><span class="wsd-s1">&#x27;</span><span class="wsd-wsd-p"></span><span class="wsd-p wsd-word-change">,</span><span class="n wsd-word-change"> </span><span class="wsd-n wsd-word-change">inkscape</span><span class="wsd-o wsd-word-change">=</span><span class="wsd-kc wsd-word-change">False</span><span class="wsd-wsd-p wsd-word-change"></span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">36</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">min_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">min_y</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">max_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">max_y</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">36</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">min_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">min_y</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">max_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">max_y</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-insert">37</span><span class="wsd-line wsd-left wsd-insert"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left wsd-insert">38</span><span class="wsd-line wsd-left wsd-insert"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left wsd-insert">39</span><span class="wsd-line wsd-left wsd-insert"><span class="n"> </span><span class="wsd-n">min_x</span><span class="n"> </span><span class="wsd-o">-</span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left wsd-insert">40</span><span class="wsd-line wsd-left wsd-insert"><span class="n"> </span><span class="wsd-n">min_y</span><span class="n"> </span><span class="wsd-o">-</span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left wsd-insert">41</span><span class="wsd-line wsd-left wsd-insert"><span class="n"> </span><span class="wsd-n">max_x</span><span class="n"> </span><span class="wsd-o">+</span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left wsd-insert">42</span><span class="wsd-line wsd-left wsd-insert"><span class="n"> </span><span class="wsd-n">max_y</span><span class="n"> </span><span class="wsd-o">+</span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-empty"></span><span class="wsd-line wsd-right wsd-empty"></span>
+<span class="wsd-lineno wsd-left">43</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">37</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">44</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">h</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">max_x</span><span class="n"> </span><span class="wsd-o">-</span><span class="n"> </span><span class="wsd-n">min_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">max_y</span><span class="n"> </span><span class="wsd-o">-</span><span class="n"> </span><span class="wsd-n">min_y</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">38</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">h</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">max_x</span><span class="n"> </span><span class="wsd-o">-</span><span class="n"> </span><span class="wsd-n">min_x</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">max_y</span><span class="n"> </span><span class="wsd-o">-</span><span class="n"> </span><span class="wsd-n">min_y</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">45</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">w</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mf">1.0</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">math</span><span class="wsd-o">.</span><span class="wsd-n">isclose</span><span class="wsd-p">(</span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mf">0.0</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-n">w</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">39</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">w</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mf">1.0</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">math</span><span class="wsd-o">.</span><span class="wsd-n">isclose</span><span class="wsd-p">(</span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mf">0.0</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-n">w</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">46</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">h</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mf">1.0</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">math</span><span class="wsd-o">.</span><span class="wsd-n">isclose</span><span class="wsd-p">(</span><span class="wsd-n">h</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mf">0.0</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-n">h</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">40</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">h</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mf">1.0</span><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">math</span><span class="wsd-o">.</span><span class="wsd-n">isclose</span><span class="wsd-p">(</span><span class="wsd-n">h</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mf">0.0</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">else</span><span class="n"> </span><span class="wsd-n">h</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">47</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">41</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">42</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">inkscape</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">43</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-o">.</span><span class="wsd-n">insert</span><span class="wsd-p">(</span><span class="wsd-mi">0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">kls</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">sodipodi:namedview</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">id</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">namedview1</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">pagecolor</span><span class="wsd-o">=</span><span class="wsd-n">pagecolor</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">44</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">inkscape__document_units</span><span class="wsd-o">=</span><span class="wsd-n">unit</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-change">48</span><span class="wsd-line wsd-left wsd-change"><span class="n"> </span><span class="wsd-n">namespaces</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">dict</span><span class="wsd-p">(</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-change">45</span><span class="wsd-line wsd-right wsd-change"><span class="wsd-n"></span><span class="wsd-n wsd-word-change"> </span><span class="n"> </span><span class="wsd-n">namespaces</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">dict</span><span class="wsd-p">(</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-change">49</span><span class="wsd-line wsd-left wsd-change"><span class="n"> </span><span class="wsd-n">xmlns</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/2000/svg</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-change">46</span><span class="wsd-line wsd-right wsd-change"><span class="wsd-n"></span><span class="wsd-n wsd-word-change"> </span><span class="n"> </span><span class="wsd-n">xmlns</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/2000/svg</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">47</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">xmlns__xlink</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/1999/xlink</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">48</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">xmlns__sodipodi</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">49</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">xmlns__inkscape</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">http://www.inkscape.org/namespaces/inkscape</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">50</span><span class="wsd-line wsd-right wsd-insert"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">51</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-k">else</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">52</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">namespaces</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">dict</span><span class="wsd-p">(</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-empty"></span><span class="wsd-line wsd-left wsd-empty"></span>
+<span class="wsd-lineno wsd-right wsd-insert">53</span><span class="wsd-line wsd-right wsd-insert"><span class="n"> </span><span class="wsd-n">xmlns</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/2000/svg</span><span class="wsd-s2">&quot;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left wsd-change">50</span><span class="wsd-line wsd-left wsd-change"><span class="n"> </span><span class="wsd-n">xmlns__xlink</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/1999/xlink</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right wsd-change">54</span><span class="wsd-line wsd-right wsd-change"><span class="wsd-n"></span><span class="wsd-n wsd-word-change"> </span><span class="n"> </span><span class="wsd-n">xmlns__xlink</span><span class="wsd-o">=</span><span class="wsd-s2">&quot;</span><span class="wsd-s2">http://www.w3.org/1999/xlink</span><span class="wsd-s2">&quot;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">51</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">55</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">52</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-n">kls</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">svg</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">56</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">return</span><span class="n"> </span><span class="wsd-n">kls</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">svg</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">53</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">width</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">w</span><span class="wsd-si">}</span><span class="wsd-si">{</span><span class="wsd-n">unit</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">height</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">h</span><span class="wsd-si">}</span><span class="wsd-si">{</span><span class="wsd-n">unit</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">57</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">width</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">w</span><span class="wsd-si">}</span><span class="wsd-si">{</span><span class="wsd-n">unit</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">height</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">h</span><span class="wsd-si">}</span><span class="wsd-si">{</span><span class="wsd-n">unit</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">54</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">viewBox</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">min_x</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">min_y</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">w</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">h</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">58</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">viewBox</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">min_x</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">min_y</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">w</span><span class="wsd-si">}</span><span class="wsd-s1"> </span><span class="wsd-si">{</span><span class="wsd-n">h</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">55</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">style</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">background-color:</span><span class="wsd-si">{</span><span class="wsd-n">pagecolor</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">59</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">style</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">background-color:</span><span class="wsd-si">{</span><span class="wsd-n">pagecolor</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<div class="wsd-collapse"><div class="wsd-collapse-controls"><label><input type="checkbox" checked> Collapse 49 unchanged lines</label></div>
+<span class="wsd-lineno wsd-left">56</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-o">*</span><span class="wsd-n">namespaces</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">60</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-o">*</span><span class="wsd-n">namespaces</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">57</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-o">=</span><span class="wsd-kc">True</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">61</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">root</span><span class="wsd-o">=</span><span class="wsd-kc">True</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">58</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">62</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">59</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">63</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">60</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">command</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">64</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">command</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">61</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-h</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--height</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">20</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Bar height in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">65</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-h</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--height</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">20</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Bar height in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">62</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-t/-n</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--text/--no-text</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Whether to add text containing the data under the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">66</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-t/-n</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--text/--no-text</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Whether to add text containing the data under the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">63</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-f</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--font</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">sans-serif</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Font for the text underneath the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">67</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-f</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--font</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">sans-serif</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Font for the text underneath the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">64</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-s</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--font-size</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">12</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Font size for the text underneath the bar code in points (pt)</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">68</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-s</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--font-size</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">12</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Font size for the text underneath the bar code in points (pt)</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">65</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-b</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--bar-width</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mf">1.0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Bar width in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">69</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-b</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--bar-width</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mf">1.0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Bar width in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">66</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-m</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--margin</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mf">3.0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Margin around bar code in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">70</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-m</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--margin</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mf">3.0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">Margin around bar code in mm</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">67</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-c</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--color</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">black</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">SVG color for the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">71</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-c</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--color</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">black</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">SVG color for the bar code</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">68</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--text-color</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-kc">None</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">SVG color for the text (defaults to the bar code</span><span class="wsd-se">\&#x27;</span><span class="wsd-s1">s color)</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">72</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--text-color</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-kc">None</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">SVG color for the text (defaults to the bar code</span><span class="wsd-se">\&#x27;</span><span class="wsd-s1">s color)</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">69</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--dpi</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">96</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">DPI value to assume for internal SVG unit conversions</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">73</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">option</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">--dpi</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-nb">float</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-mi">96</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">help</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">DPI value to assume for internal SVG unit conversions</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">70</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">argument</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">data</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">74</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">argument</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">data</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">71</span><span class="wsd-line wsd-left"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">argument</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">outfile</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-n">click</span><span class="wsd-o">.</span><span class="wsd-n">File</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">w</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">75</span><span class="wsd-line wsd-right"><span class="wsd-nd">@click</span><span class="wsd-o">.</span><span class="wsd-n">argument</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">outfile</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-nb">type</span><span class="wsd-o">=</span><span class="wsd-n">click</span><span class="wsd-o">.</span><span class="wsd-n">File</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">w</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">default</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">-</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">72</span><span class="wsd-line wsd-left"><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-nf">cli</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">outfile</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">height</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">text</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bar_width</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">text_color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">dpi</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">76</span><span class="wsd-line wsd-right"><span class="wsd-k">def</span><span class="wsd-w"> </span><span class="wsd-nf">cli</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">outfile</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">height</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">text</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bar_width</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">text_color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">dpi</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">73</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">data</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">int</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mi">16</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">77</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">data</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">int</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mi">16</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">74</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">text_color</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">text_color</span><span class="n"> </span><span class="wsd-ow">or</span><span class="n"> </span><span class="wsd-n">color</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">78</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">text_color</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">text_color</span><span class="n"> </span><span class="wsd-ow">or</span><span class="n"> </span><span class="wsd-n">color</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">75</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">79</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">76</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">NUM_BITS</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mi">26</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">80</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">NUM_BITS</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-mi">26</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">77</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">81</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">78</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">data_bits</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-nb">bool</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="n"> </span><span class="wsd-o">&amp;</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-mi">1</span><span class="wsd-o">&lt;&lt;</span><span class="wsd-n">i</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">i</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-nb">range</span><span class="wsd-p">(</span><span class="wsd-n">NUM_BITS</span><span class="wsd-p">)</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">82</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">data_bits</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-nb">bool</span><span class="wsd-p">(</span><span class="wsd-n">data</span><span class="n"> </span><span class="wsd-o">&amp;</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-mi">1</span><span class="wsd-o">&lt;&lt;</span><span class="wsd-n">i</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">i</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-nb">range</span><span class="wsd-p">(</span><span class="wsd-n">NUM_BITS</span><span class="wsd-p">)</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">79</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">data_encoded</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">itertools</span><span class="wsd-o">.</span><span class="wsd-n">chain</span><span class="wsd-p">(</span><span class="wsd-o">*</span><span class="wsd-p">[</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">83</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">data_encoded</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">itertools</span><span class="wsd-o">.</span><span class="wsd-n">chain</span><span class="wsd-p">(</span><span class="wsd-o">*</span><span class="wsd-p">[</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">80</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">a</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-ow">not</span><span class="n"> </span><span class="wsd-n">a</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">a</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-n">data_bits</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">84</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">a</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-ow">not</span><span class="n"> </span><span class="wsd-n">a</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">a</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-n">data_bits</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">81</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">85</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">82</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">data_encoded</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-n">data_encoded</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">86</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">data_encoded</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-o">*</span><span class="wsd-n">data_encoded</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">False</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-kc">True</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">83</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">87</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">84</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">width</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">len</span><span class="wsd-p">(</span><span class="wsd-n">data_encoded</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-o">*</span><span class="n"> </span><span class="wsd-n">bar_width</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">88</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">width</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">len</span><span class="wsd-p">(</span><span class="wsd-n">data_encoded</span><span class="wsd-p">)</span><span class="n"> </span><span class="wsd-o">*</span><span class="n"> </span><span class="wsd-n">bar_width</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">85</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-c1"># 1 px = 0.75 pt</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">89</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-c1"># 1 px = 0.75 pt</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">86</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">pt_to_mm</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-k">lambda</span><span class="n"> </span><span class="wsd-n">pt</span><span class="wsd-p">:</span><span class="n"> </span><span class="wsd-n">pt</span><span class="n"> </span><span class="wsd-o">/</span><span class="n"> </span><span class="wsd-mf">0.75</span><span class="n"> </span><span class="wsd-o">/</span><span class="wsd-n">dpi</span><span class="n"> </span><span class="wsd-o">*</span><span class="n"> </span><span class="wsd-mf">25.4</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">90</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">pt_to_mm</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-k">lambda</span><span class="n"> </span><span class="wsd-n">pt</span><span class="wsd-p">:</span><span class="n"> </span><span class="wsd-n">pt</span><span class="n"> </span><span class="wsd-o">/</span><span class="n"> </span><span class="wsd-mf">0.75</span><span class="n"> </span><span class="wsd-o">/</span><span class="wsd-n">dpi</span><span class="n"> </span><span class="wsd-o">*</span><span class="n"> </span><span class="wsd-mf">25.4</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">87</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">font_size</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">pt_to_mm</span><span class="wsd-p">(</span><span class="wsd-n">font_size</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">91</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">font_size</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">pt_to_mm</span><span class="wsd-p">(</span><span class="wsd-n">font_size</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">88</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">total_height</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">height</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-o">*</span><span class="wsd-mi">2</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">92</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">total_height</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">height</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-o">*</span><span class="wsd-mi">2</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">89</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">93</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">90</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">tags</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">94</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">tags</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-p">[</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">91</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">group</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-n">itertools</span><span class="wsd-o">.</span><span class="wsd-n">groupby</span><span class="wsd-p">(</span><span class="wsd-nb">enumerate</span><span class="wsd-p">(</span><span class="wsd-n">data_encoded</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-o">=</span><span class="wsd-k">lambda</span><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-p">:</span><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-p">[</span><span class="wsd-mi">1</span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">95</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">for</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">group</span><span class="n"> </span><span class="wsd-ow">in</span><span class="n"> </span><span class="wsd-n">itertools</span><span class="wsd-o">.</span><span class="wsd-n">groupby</span><span class="wsd-p">(</span><span class="wsd-nb">enumerate</span><span class="wsd-p">(</span><span class="wsd-n">data_encoded</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-o">=</span><span class="wsd-k">lambda</span><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-p">:</span><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-p">[</span><span class="wsd-mi">1</span><span class="wsd-p">]</span><span class="wsd-p">)</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">92</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">96</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">key</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">93</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">group</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">list</span><span class="wsd-p">(</span><span class="wsd-n">group</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">97</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">group</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">list</span><span class="wsd-p">(</span><span class="wsd-n">group</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">94</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">x0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">_key</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">group</span><span class="wsd-p">[</span><span class="wsd-mi">0</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">98</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">x0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">_key</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-n">group</span><span class="wsd-p">[</span><span class="wsd-mi">0</span><span class="wsd-p">]</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">95</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">w</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">len</span><span class="wsd-p">(</span><span class="wsd-n">group</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">99</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">w</span><span class="n"> </span><span class="wsd-o">=</span><span class="n"> </span><span class="wsd-nb">len</span><span class="wsd-p">(</span><span class="wsd-n">group</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">96</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-o">.</span><span class="wsd-n">append</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">path</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">stroke</span><span class="wsd-o">=</span><span class="wsd-n">color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">stroke_width</span><span class="wsd-o">=</span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">d</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">M </span><span class="wsd-si">{</span><span class="wsd-p">(</span><span class="wsd-n">x0</span><span class="wsd-w"> </span><span class="wsd-o">+</span><span class="wsd-w"> </span><span class="wsd-n">w</span><span class="wsd-o">/</span><span class="wsd-mi">2</span><span class="wsd-p">)</span><span class="wsd-o">*</span><span class="wsd-n">bar_width</span><span class="wsd-si">}</span><span class="wsd-s1"> 0 l 0 </span><span class="wsd-si">{</span><span class="wsd-n">height</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">100</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-o">.</span><span class="wsd-n">append</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">path</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">stroke</span><span class="wsd-o">=</span><span class="wsd-n">color</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">stroke_width</span><span class="wsd-o">=</span><span class="wsd-n">w</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">d</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">M </span><span class="wsd-si">{</span><span class="wsd-p">(</span><span class="wsd-n">x0</span><span class="wsd-w"> </span><span class="wsd-o">+</span><span class="wsd-w"> </span><span class="wsd-n">w</span><span class="wsd-o">/</span><span class="wsd-mi">2</span><span class="wsd-p">)</span><span class="wsd-o">*</span><span class="wsd-n">bar_width</span><span class="wsd-si">}</span><span class="wsd-s1"> 0 l 0 </span><span class="wsd-si">{</span><span class="wsd-n">height</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">97</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">101</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">98</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">text</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">102</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-n">text</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">99</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-o">.</span><span class="wsd-n">append</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">text</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">children</span><span class="wsd-o">=</span><span class="wsd-p">[</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">data</span><span class="wsd-si">:</span><span class="wsd-s1">07x</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">103</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">tags</span><span class="wsd-o">.</span><span class="wsd-n">append</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-p">(</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">text</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">children</span><span class="wsd-o">=</span><span class="wsd-p">[</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">data</span><span class="wsd-si">:</span><span class="wsd-s1">07x</span><span class="wsd-si">}</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">]</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">100</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-o">=</span><span class="wsd-n">width</span><span class="wsd-o">/</span><span class="wsd-mi">2</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">y</span><span class="wsd-o">=</span><span class="wsd-n">height</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-mf">0.5</span><span class="wsd-o">*</span><span class="wsd-n">font_size</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">104</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">x</span><span class="wsd-o">=</span><span class="wsd-n">width</span><span class="wsd-o">/</span><span class="wsd-mi">2</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">y</span><span class="wsd-o">=</span><span class="wsd-n">height</span><span class="n"> </span><span class="wsd-o">+</span><span class="n"> </span><span class="wsd-mf">0.5</span><span class="wsd-o">*</span><span class="wsd-n">font_size</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">101</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">font_family</span><span class="wsd-o">=</span><span class="wsd-n">font</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">font_size</span><span class="wsd-si">:</span><span class="wsd-s1">.3f</span><span class="wsd-si">}</span><span class="wsd-s1">px</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">105</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">font_family</span><span class="wsd-o">=</span><span class="wsd-n">font</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">font_size</span><span class="wsd-o">=</span><span class="wsd-sa">f</span><span class="wsd-s1">&#x27;</span><span class="wsd-si">{</span><span class="wsd-n">font_size</span><span class="wsd-si">:</span><span class="wsd-s1">.3f</span><span class="wsd-si">}</span><span class="wsd-s1">px</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">102</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">text_anchor</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">middle</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">dominant_baseline</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">hanging</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">106</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">text_anchor</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">middle</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">dominant_baseline</span><span class="wsd-o">=</span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">hanging</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">,</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">103</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">fill</span><span class="wsd-o">=</span><span class="wsd-n">text_color</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">107</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">fill</span><span class="wsd-o">=</span><span class="wsd-n">text_color</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">104</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">108</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+</div>
+<span class="wsd-lineno wsd-left">105</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">outfile</span><span class="wsd-o">.</span><span class="wsd-n">write</span><span class="wsd-p">(</span><span class="wsd-nb">str</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-o">.</span><span class="wsd-n">setup_svg</span><span class="wsd-p">(</span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-o">=</span><span class="wsd-p">(</span><span class="wsd-p">(</span><span class="wsd-mi">0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mi">0</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">width</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">total_height</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-o">=</span><span class="wsd-n">margin</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">109</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">outfile</span><span class="wsd-o">.</span><span class="wsd-n">write</span><span class="wsd-p">(</span><span class="wsd-nb">str</span><span class="wsd-p">(</span><span class="wsd-n">Tag</span><span class="wsd-o">.</span><span class="wsd-n">setup_svg</span><span class="wsd-p">(</span><span class="wsd-n">tags</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">bounds</span><span class="wsd-o">=</span><span class="wsd-p">(</span><span class="wsd-p">(</span><span class="wsd-mi">0</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-mi">0</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-p">(</span><span class="wsd-n">width</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">total_height</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-p">,</span><span class="n"> </span><span class="wsd-n">margin</span><span class="wsd-o">=</span><span class="wsd-n">margin</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">106</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">110</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">107</span><span class="wsd-line wsd-left"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">111</span><span class="wsd-line wsd-right"><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">108</span><span class="wsd-line wsd-left"><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-vm">__name__</span><span class="n"> </span><span class="wsd-o">==</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">__main__</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">112</span><span class="wsd-line wsd-right"><span class="wsd-k">if</span><span class="n"> </span><span class="wsd-vm">__name__</span><span class="n"> </span><span class="wsd-o">==</span><span class="n"> </span><span class="wsd-s1">&#x27;</span><span class="wsd-s1">__main__</span><span class="wsd-s1">&#x27;</span><span class="wsd-p">:</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-left">109</span><span class="wsd-line wsd-left"><span class="n"> </span><span class="wsd-n">cli</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+<span class="wsd-lineno wsd-right">113</span><span class="wsd-line wsd-right"><span class="n"> </span><span class="wsd-n">cli</span><span class="wsd-p">(</span><span class="wsd-p">)</span><span class="wsd-w"></span></span></span>
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
+