1 Commits
Author SHA1 Message Date
Yggdrasil CI fbc7e0cfd9 Publish site from 09f0dc42e4 2026-10-11 21:49:13 +00:00
392 changed files with 3822 additions and 17106 deletions

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
# Shell scripts and the Justfile must stay LF so they run in Git Bash on Windows.
*.sh text eol=lf
Justfile text eol=lf
-70
View File
@@ -1,70 +0,0 @@
name: Build site
on:
- push
- workflow_dispatch
jobs:
build:
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Bootstrap pinned toolchain
run: ./scripts/bootstrap.sh
- name: Build site
run: ./scripts/calepin.sh compile site public
# The official upload-artifact@v4 refuses to run on Gitea (it detects a
# non-GitHub server and aborts with GHESNotSupportedError). v3 uses the
# artifact API that Gitea implements. On Gitea >= 1.22 with a current
# act_runner, christopherhx/gitea-upload-artifact@v4 is a drop-in
# replacement if you want the v4 API.
- name: Upload site artifact
uses: actions/upload-artifact@v3
with:
name: public
path: public/
deploy:
needs: build
# Publish only from main; move the gate to publish from another branch.
if: github.ref == 'refs/heads/main'
# Write access for the automatic Actions token to push the pages branch.
permissions:
contents: write
# NOTE: must match your act_runner labels (e.g. ubuntu-latest, ubuntu-24.04).
runs-on: ubuntu-latest
steps:
- name: Download site artifact
uses: actions/download-artifact@v3
with:
name: public
path: public
- name: Check artifact layout
run: test -f public/index.html
# Publishing interfaces with prod; it never runs under local act tests
# (act sets env.ACT, the Gitea runner does not). The force-push to the
# `pages` branch is what triggers the git-pages webhook.
- name: Publish to pages branch
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
if [ -z "$GITEA_TOKEN" ]; then
echo "::error::Missing GITEA_TOKEN (the automatic Actions token)."
exit 1
fi
cd public
git init -q -b pages .
git config user.name "Yggdrasil CI"
git config user.email "ci@noreply.kb28.ch"
git add -A
git commit -q -m "Publish site from ${GITHUB_SHA}"
repo_host="${GITHUB_SERVER_URL#*://}"
repo_host="${repo_host%/}"
git push -q --force "https://${GITHUB_REPOSITORY_OWNER}:${GITEA_TOKEN}@${repo_host}/${GITHUB_REPOSITORY}.git" pages
-12
View File
@@ -1,12 +0,0 @@
# Generated site and pinned toolchain
public/
/tools/
# Calepin build artifacts
.calepin/
.calepin-entry.*
# IDE
.idea/
.zed/
.vscode/
-17
View File
@@ -1,17 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Docsify" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="start http://localhost:3000 ; docsify serve " />
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/.idea/runConfigurations/run.sh" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="powershell.exe" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Not found</title><meta content="The requested page does not exist." name=description><meta content="The requested page does not exist." property=og:description><meta content="The requested page does not exist." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Not found | Yggdrasil" property=og:title><meta content="Not found | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/404.html rel=canonical><meta content=https://yggdrasil.kb28.ch/404.html property=og:url><link href=/assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke=Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
View File
File renamed without changes.
-20
View File
@@ -1,20 +0,0 @@
# Agent Rules for Yggdrasil Migration
* **Commit Policy**: Small conventional commits. One logical change per
commit, committed only once that step is verified and working. End every
commit message with a blank line followed by an
`Assisted-by: Delta:zai-glm-5.3` footer.
* **Layout**: Sources live in `site/` (`calepin.toml` + `.typ` pages),
generated output in `public/` (gitignored), pinned binaries in `tools/`
(gitignored), helper scripts in `scripts/`, and the old Docsify
documentation stays untouched in `legacy_docs/`.
* **Toolchain**: `calepin` (0.0.61) and `typst` (0.15.1) are pinned in
`scripts/bootstrap.sh` and downloaded into `tools/` with checksum
verification. The build must never depend on global installs. CI installs
the same pinned versions by calling the same scripts.
* **Verification**: Before committing, run a fresh cycle: `rm -rf tools`,
then `just clean`, then `just build` — the build must succeed with no
calepin on `PATH`. Verify the served site (`just preview`): home 200,
category page 200, sub-page 200, missing page shows the 404 page,
`sitemap.xml` present. After every test/clean cycle `git status` must stay
clean.
View File
File renamed without changes.
-31
View File
@@ -1,31 +0,0 @@
# Show the available recipes
default:
@just --list
# Bootstrap the pinned calepin + typst binaries into tools/
tools:
./scripts/bootstrap.sh
# Compile the site into public/
build: tools
./scripts/calepin.sh compile site public
# Build, then serve the site locally
preview: build
./scripts/calepin.sh serve public
# Rebuild on change and serve the result
watch: tools
./scripts/calepin.sh watch site public --serve
# Remove generated output and build caches
clean:
./scripts/clean.sh
# Remove all EXIF metadata from a file
strip-exif file:
exiftool -all= -overwrite_original {{ quote(file) }}
# Run the CI workflow locally with act (requires act + docker)
ci:
act push -W .gitea/workflows/build.yml -j build --artifact-server-path /tmp/act-artifacts
-88
View File
@@ -1,88 +0,0 @@
# Yggdrasil
Personal knowledge base built as a static website with
[Calepin](https://vincentarelbundock.github.io/calepin/), a Typst-based static
site generator.
## Layout
- `site/` — website sources: `calepin.toml` + `.typ` pages
- `site/themes/yggdrasil/` — local theme extending the bundled `calepin` theme (scoped sidebars, academic-styled landing)
- `public/` — generated site (gitignored)
- `tools/` — pinned `calepin` and `typst` binaries (gitignored, downloaded on demand)
- `scripts/` — helper scripts used by the Justfile and CI
- `legacy_docs/` — the old Docsify documentation, kept unmodified for reference
## Quick start
`just` is the only prerequisite — the toolchain is downloaded and
checksum-verified into `tools/` on first use (no global installs, no cargo):
just # list recipes
just tools # download the pinned toolchain into tools/
just build # compile site/ into public/
just preview # build, then serve the site locally
just watch # rebuild on change and serve
just clean # remove public/ and build caches
just ci # run the CI workflow locally with act (needs act + docker)
## Adding a page
Create `site/<category>/<page>.typ`:
```typst
#set document(title: [Page title])
#metadata((title: "Page title", translation_key: "category-page")) <website-metadata>
#title()
= Page title
Content…
```
and reference it in `site/calepin.toml`:
```toml
[[sidebar.section.item]]
target = "<category>/<page>.typ"
```
Sidebar labels come from the page's `#metadata(...)` line, not from the config.
Every page also renders a printable PDF twin (`pdf = true` in
`calepin.toml`, selectable in the page's view picker). Add `pdf: false` to a
page's `#metadata(...)` to skip its PDF, as `404.typ` does.
## Adding a category
1. Create `site/<category>/` with an `index.typ`, using
`#metadata((title: "Overview", ...))` for its sidebar label.
2. Add a `[[sidebar.section]]` named after the category in `calepin.toml`,
listing the index page and its sub-pages.
3. Add a `[[menus.main]]` entry with the category's `index.typ` as `target`
and the category name as `label` (e.g. `Home`, `IT`), so it appears in the
top navigation bar. The `label` is required here because the index page's
metadata title is `Overview`.
Pages inside a category show only that category's sidebar section — the
local theme (`site/themes/yggdrasil/partials/site-nav-sidebar.html`) filters
the sidebar to the section containing the current page. Pages outside every
category (landing page, `404`, standalone pages) fall back to the full
sidebar.
## Deployment
`just build` produces a fully static site in `public/`: HTML pages with
printable PDF twins, plus `sitemap.xml`, `robots.txt`, and `llms.txt`. The
Gitea Actions workflow in
`.gitea/workflows/build.yml` builds the site on every push with the same
pinned toolchain and uploads `public/` as an artifact.
Pushes to `main` are also published: the `deploy` job force-pushes `public/`
to the repo's `pages` branch using the automatic Actions token (no secrets to
configure). A Gitea webhook on the repo then notifies the self-hosted
[git-pages](https://codeberg.org/git-pages/git-pages) server, which clones
the `pages` branch and serves the site. The push step is gated with
`if: ${{ !env.ACT }}` so local `act` runs never publish, and
`workflow_dispatch` enables manual publishes from the Gitea UI.
File renamed without changes.
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Computer Science</title><meta content="Placeholder page for the Computer Science category. The real content comes later." name=description><meta content="Placeholder page for the Computer Science category. The real content comes later." property=og:description><meta content="Placeholder page for the Computer Science category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Computer Science | Yggdrasil" property=og:title><meta content="Computer Science | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/cs/ rel=canonical><meta content=https://yggdrasil.kb28.ch/cs/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
View File
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Cooking</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Cooking | Yggdrasil" property=og:title><meta content="Cooking | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/cooking.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/cooking.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Gardening</title><meta content="Some plants description and advices for keep them alive" name=description><meta content="Some plants description and advices for keep them alive" property=og:description><meta content="Some plants description and advices for keep them alive" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Gardening | Yggdrasil" property=og:title><meta content="Gardening | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/gardening.html rel=canonical><meta content=https://yggdrasil.kb28.ch/home/gardening.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='httLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Home</title><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." property=og:description><meta content="Placeholder page for the home category: cooking, washing clothes, gardening. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Home | Yggdrasil" property=og:title><meta content="Home | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/home/ rel=canonical><meta content=https://yggdrasil.kb28.ch/home/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline pointsLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Backup</title><meta content="Placeholder page — the real content comes later." name=description><meta content="Placeholder page — the real content comes later." property=og:description><meta content="Placeholder page — the real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="Backup | Yggdrasil" property=og:title><meta content="Backup | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/backup.html rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/backup.html property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'Line truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
Binary file not shown.
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>HomeLab</title><meta content="Placeholder page for the homelab category. The real content comes later." name=description><meta content="Placeholder page for the homelab category. The real content comes later." property=og:description><meta content="Placeholder page for the homelab category. The real content comes later." name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content="HomeLab | Yggdrasil" property=og:title><meta content="HomeLab | Yggdrasil" name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/homelab/ rel=canonical><meta content=https://yggdrasil.kb28.ch/homelab/ property=og:url><link href=../assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/svLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
<!doctype html><html lang=en><head><meta charset=utf-8><meta content="width=device-width,initial-scale=1" name=viewport><title>Yggdrasil</title><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" property=og:description><meta content="Welcome to Yggdrasil, my personal knowledge base. Pick a master category from the menu" name=twitter:description><meta content=Yggdrasil property=og:site_name><meta content=Yggdrasil property=og:title><meta content=Yggdrasil name=twitter:title><meta content=article property=og:type><meta content=summary name=twitter:card><link href=https://yggdrasil.kb28.ch/ rel=canonical><meta content=https://yggdrasil.kb28.ch/ property=og:url><link href=assets/yggdrasil.ico rel=icon><meta content="light dark" name=color-scheme><script data-calepin-theme-param=theme data-calepin-theme-storage-key=calepin-website-theme>(()=>{let currentScript=document.currentScript,storageKey=currentScript?.dataset?.calepinThemeStorageKey||`calepin-website-theme`,param=currentScript?.dataset?.calepinThemeParam||`theme`,normalize=value=>value===`light`||value===`dark`?value:``,root=document.documentElement,media=window.matchMedia?window.matchMedia(`(prefers-color-scheme: dark)`):null,systemTheme=()=>media&&media.matches?`dark`:`light`,mode=``;if(param)try{mode=normalize(new URL(window.location.href).searchParams.get(param)||``)}catch{mode=``}if(!mode)try{mode=normalize(localStorage.getItem(storageKey)||``)}catch{mode=``}let theme=mode||systemTheme();root.dataset.calepinThemeMode=mode,root.dataset.theme=theme,root.style.colorScheme=theme})();</script><style>:host,:root{--pico-font-family-emoji:"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--pico-font-family-sans-serif:system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif,var(--pico-font-family-emoji);--pico-font-family-monospace:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace,var(--pico-font-family-emoji);--pico-font-family:var(--pico-font-family-sans-serif);--pico-line-height:1.5;--pico-font-weight:400;--pico-font-size:100%;--pico-text-underline-offset:.1rem;--pico-border-radius:.25rem;--pico-border-width:.0625rem;--pico-outline-width:.125rem;--pico-transition:.2s ease-in-out;--pico-spacing:1rem;--pico-typography-spacing-vertical:1rem;--pico-block-spacing-vertical:var(--pico-spacing);--pico-block-spacing-horizontal:var(--pico-spacing);--pico-grid-column-gap:var(--pico-spacing);--pico-grid-row-gap:var(--pico-spacing);--pico-form-element-spacing-vertical:.75rem;--pico-form-element-spacing-horizontal:1rem;--pico-group-box-shadow:0 0 0 #0000;--pico-group-box-shadow-focus-with-button:0 0 0 var(--pico-outline-width) var(--pico-primary-focus);--pico-group-box-shadow-focus-with-input:0 0 0 .0625rem var(--pico-form-element-border-color);--pico-modal-overlay-backdrop-filter:blur(.375rem);--pico-nav-element-spacing-vertical:1rem;--pico-nav-element-spacing-horizontal:.5rem;--pico-nav-link-spacing-vertical:.5rem;--pico-nav-link-spacing-horizontal:.5rem;--pico-nav-breadcrumb-divider:">";--pico-icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pico-icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pico-icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pico-icon-search:url("data:image/sLine truncated
`:Array.from(node.childNodes).map(codeText).join(``):``:``}document.querySelectorAll(selector).forEach(block=>{if(block.dataset.calepinCopyBound===`true`||block.querySelector(`:scope > .${buttonClass}`))return;block.dataset.calepinCopyBound=`true`;let button=document.createElement(`button`);button.type=`button`,button.className=buttonClass,button.setAttribute(`aria-label`,`Copy code`),button.setAttribute(`title`,`Copy code`),button.innerHTML=icon;let restoreTimeout=null;button.addEventListener(`click`,async()=>{restoreTimeout!==null&&(window.clearTimeout(restoreTimeout),restoreTimeout=null);let text=codeText(block.querySelector(`pre code, code, pre`));try{await copyText(text),button.classList.add(copiedClass),button.innerHTML=copiedIcon,restoreTimeout=window.setTimeout(()=>{button.classList.remove(copiedClass),button.innerHTML=icon,restoreTimeout=null},900)}catch{button.classList.remove(copiedClass),button.innerHTML=icon}}),block.prepend(button)})})();</script><script>(()=>{let pagefindModal=()=>document.querySelector(`pagefind-modal`);function modalSearchInput(modal){return modal?modal.querySelector(`pagefind-input input, input[type='search'], input`):null}function setModalSearchQuery(modal,query){if(!query)return!1;let input=modalSearchInput(modal);return input?(input.value=query,input.dispatchEvent(new Event(`input`,{bubbles:!0})),!0):!1}function focusModalSearch(modal){let input=modalSearchInput(modal);return input?(input.focus(),!0):!1}async function openPagefindSearch(query){let modal=pagefindModal();if(!modal)return!1;if(typeof modal.open!=`function`&&window.customElements?.whenDefined)try{await window.customElements.whenDefined(`pagefind-modal`)}catch{}if(typeof modal.open!=`function`)return!1;modal.open();let sync=()=>{let hasQuery=setModalSearchQuery(modal,query),hasFocus=focusModalSearch(modal);return hasQuery||hasFocus};return sync()||requestAnimationFrame(()=>{sync()||window.setTimeout(sync,50)}),!0}function isTextEntryElement(element){return element?element.isContentEditable||element.tagName===`INPUT`||element.tagName===`SELECT`||element.tagName===`TEXTAREA`:!1}let SEARCH_SUGGESTION_LIMIT=20,SEARCH_SUBRESULT_LIMIT=3,SEARCH_SUGGESTION_MIN_LENGTH=2,SEARCH_SUGGESTION_DELAY=160,pagefindModulePromise=null;function pagefindBundlePath(){let script=document.querySelector(`script[src$="pagefind-component-ui.js"], script[src$="pagefind-ui.js"]`);return script?.src?new URL(`./`,script.src).toString():new URL(`pagefind/`,window.location.href).toString()}function loadPagefindModule(){return pagefindModulePromise||=import(`${pagefindBundlePath()}pagefind.js`),pagefindModulePromise}function plainTextFromHtml(html){let element=document.createElement(`div`);return element.innerHTML=html||``,element.textContent?.trim()||``}function setSearchResultsOpen(input,results,isOpen){results.hidden=!isOpen,input.setAttribute(`aria-expanded`,String(isOpen))}function clearSearchResults(input,results){results.innerHTML=``,setSearchResultsOpen(input,results,!1)}function renderSearchMessage(input,results,message){results.innerHTML=``;let item=document.createElement(`p`);item.className=`calepin-website-search-empty`,item.textContent=message,results.appendChild(item),setSearchResultsOpen(input,results,!0)}function renderSearchResults(input,results,items){if(results.innerHTML=``,!items.length){renderSearchMessage(input,results,`No results`);return}let basePathPrefix=localCanonicalBasePathPrefix(),addSearchLink=(item,className,titleClassName,excerptClassName)=>{let href=hrefWithoutLocalBasePath(item.meta?.url||item.url||``,basePathPrefix);if(!href)return null;let link=document.createElement(`a`);link.className=className,link.href=href,link.setAttribute(`role`,`option`);let title=document.createElement(`span`);title.className=titleClassName,title.textContent=item.meta?.title||item.title||href,link.appendChild(title);let excerptText=plainTextFromHtml(item.excerpt||item.content||``);if(excerptText){let excerpt=document.createElement(`span`);excerpt.className=excerptClassName,excerpt.textContent=excerptText,link.appendChild(excerpt)}return link};for(let item of items){let link=addSearchLink(item,`calepin-website-search-result`,`calepin-website-search-result-title`,`calepin-website-search-result-excerpt`);if(!link)continue;results.appendChild(link);let subResults=Array.isArray(item.sub_results)?item.sub_results.slice(0,3):[];if(!subResults.length)continue;let nested=document.createElement(`div`);nested.className=`calepin-website-search-subresults`;for(let subResult of subResults){let subLink=addSearchLink(subResult,`calepin-website-search-subresult`,`calepin-website-search-subresult-title`,`calepin-website-search-subresult-excerpt`);subLink&&nested.appendChild(subLink)}nested.children.length&&results.appendChild(nested)}setSearchResultsOpen(input,results,!!results.children.length)}function initMobileSearchTriggers(){document.querySelectorAll(`[data-calepin-mobile-search-trigger]`).forEach(button=>{button.dataset.calepinSearchBound!==Line truncated
*
* One module-local script wires: view switcher (HTML/Source/PDF), the mobile
* sidebar drawer, sidebar section folding, inline SVGs, internal link state
* preservation, the native <dialog> video lightbox, and the Pagefind navbar
* search bridge.
*
* The Rust theme loader links this file near the end of the page body, so
* the DOM already exists when it runs and no DOMContentLoaded guard is
* needed. */
(()=>{if(!document.querySelector(`.calepin-website-topbar, .calepin-website-main, pagefind-modal, img[data-inline-svg], [data-video-dialog], [data-lightbox-dialog]`))return;let VIEW={param:`view`,rendered:`rendered`,source:`source`,pdf:`pdf`},MOBILE_QUERY=`(max-width: 56rem)`,SOURCE_DATA_ID=`calepin-website-source-data`,isMobile=()=>window.matchMedia(`(max-width: 56rem)`).matches,isPlainLeftClick=event=>event&&event.button===0&&!event.metaKey&&!event.ctrlKey&&!event.shiftKey&&!event.altKey,isInternalPageHref=href=>!!href&&!href.startsWith(`#`)&&!href.startsWith(`http://`)&&!href.startsWith(`https://`)&&!href.startsWith(`//`)&&!href.startsWith(`mailto:`)&&!href.startsWith(`tel:`);function setUrlParam(url,name,value){value?url.searchParams.set(name,value):url.searchParams.delete(name)}function readUrlParam(name,normalize,fallback=``){try{return normalize((new URL(window.location.href).searchParams.get(name)||``).toLowerCase())}catch{return fallback}}function updateCurrentUrlParam(name,value){try{let url=new URL(window.location.href);setUrlParam(url,name,value),window.history.replaceState({},``,url.toString())}catch{}}let getMain=()=>document.querySelector(`.calepin-website-main`),normalizeView=value=>value===VIEW.source||value===VIEW.pdf?value:VIEW.rendered;function currentView(selects){let select=Array.isArray(selects)?selects[0]:selects;return normalizeView(select&&select.value||VIEW.rendered)}function pageAssetCandidates(extension){let{pathname}=window.location;return pathname.endsWith(`/`)?[`${pathname}index.${extension}`]:pathname.endsWith(`.html`)?[`${pathname.slice(0,-5)}.${extension}`]:!pathname||pathname.endsWith(`.`)?[`index.${extension}`]:pathname.includes(`.`)?[`${pathname}.${extension}`]:[`${pathname}/index.${extension}`]}function replaceMainContent(node){let main=getMain();return main?(main.innerHTML=``,main.appendChild(node),!0):!1}function showMainFallback(message){let main=getMain();if(!main)return;let fallback=document.createElement(`p`);fallback.textContent=message,main.prepend(fallback)}function renderSourceText(text){let container=document.createElement(`div`);container.className=`calepin-website-source-view`;let pre=document.createElement(`pre`),code=document.createElement(`code`);code.className=`language-typ`,code.textContent=text,pre.appendChild(code),container.appendChild(pre),replaceMainContent(container)}async function renderSourceMode(){let element=document.getElementById(`calepin-website-source-data`);if(element)try{let inline=JSON.parse(element.textContent||``);if(typeof inline==`string`&&inline.length>0){renderSourceText(inline);return}}catch{}for(let sourcePath of pageAssetCandidates(`typ`))try{let response=await fetch(sourcePath);if(!response.ok)continue;renderSourceText(await response.text());return}catch{}showMainFallback(`Could not load source view.`)}async function renderPdfMode(){let isLocal=window.location.protocol===`file:`;for(let pdfPath of pageAssetCandidates(`pdf`)){if(!isLocal)try{let response=await fetch(pdfPath,{method:`HEAD`});if(!response.ok&&response.status!==405)continue}catch{}let viewer=document.createElement(`iframe`);viewer.className=`calepin-website-pdf-viewer`;try{viewer.src=new URL(pdfPath,window.location.href).toString()}catch{viewer.src=pdfPath}viewer.setAttribute(`title`,`Page PDF view`),viewer.setAttribute(`loading`,`lazy`),replaceMainContent(viewer);return}showMainFallback(`Could not load PDF view.`)}function applyView(mode,selects){let value=normalizeView(mode);(Array.isArray(selects)?selects:[selects]).filter(Boolean).forEach(select=>{select.value=value}),updateCurrentUrlParam(VIEW.param,value===VIEW.rendered?``:value),value===VIEW.source?renderSourceMode():value===VIEW.pdf&&renderPdfMode()}function initView(selects){let viewSelects=(Array.isArray(selects)?selects:[selects]).filter(Boolean);applyView(readUrlParam(VIEW.param,normalizeView,VIEW.rendered),viewSelects),viewSelects.forEach(select=>{select.dataset.calepinBound||(select.dataset.calepinBound=`true`,select.addEventListener(`change`,event=>{let value=normalizeView(event.target.value),url=new URL(window.location.href);setUrlParam(url,VIEW.param,value===VIEW.rendered?``:value),window.location.href=url.toString()}))})}
// ----------------------------------------------------------- nav drawer
function createNav(){let shell=document.querySelector(`.calepin-website-shell`),backdrop=document.querySelector(`.calepin-website-nav-backdrop`),toggle=document.querySelector(`.calepin-website-nav-toggle`);function setOpen(isOpen){!shell||!toggle||(shell.classList.toggle(`nav-open`,isOpen),document.body.classList.toggle(`calepin-website-nav-open`,isOpen),toggle.setAttribute(`aria-expanded`,String(isOpen)),backdrop&&backdrop.setAttribute(`aria-hidden`,String(!isOpen)))}let close=()=>setOpen(!1);return toggle&&toggle.addEventListener(`click`,event=>{event.preventDefault(),isMobile()&&shell&&setOpen(!shell.classList.contains(`nav-open`))}),backdrop?.addEventListener(`click`,close),document.addEventListener(`keydown`,event=>{event.key===`Escape`&&close()}),window.addEventListener(`resize`,()=>{isMobile()||close()}),{close}}
// ------------------------------------------------------ sidebar sections
// Only one section is open at a time, but "section" means siblings: opening a
// nested subsection must not fold the parent it lives in, and must only fold
// its own siblings.
function initSidebarSections(){let sections=Array.from(document.querySelectorAll(`.calepin-website-sidebar-section`));sections.length<2||sections.forEach(section=>{section.addEventListener(`toggle`,()=>{section.open&&sections.forEach(other=>{other!==section&&other.parentElement===section.parentElement&&other.removeAttribute(`open`)})})})}
// ------------------------------------------------------------- inline svg
function svgTextFromSource(src){if(!src)return Promise.resolve(``);let prefix=`data:image/svg+xml`;if(src.startsWith(prefix)){let payload=src.slice(src.indexOf(`,`)+1);try{return Promise.resolve(src.startsWith(`${prefix};base64,`)?atob(payload):decodeURIComponent(payload))}catch{return Promise.resolve(payload)}}return fetch(src).then(response=>response.text()).catch(()=>``)}function inlineSvgImage(image){return svgTextFromSource(image.getAttribute(`src`)).then(text=>{if(!text)return;let svg=new DOMParser().parseFromString(text,`image/svg+xml`).querySelector(`svg`);if(!svg)return;svg.removeAttribute(`xmlns:xlink`),svg.removeAttribute(`width`),svg.removeAttribute(`height`),image.className&&svg.setAttribute(`class`,image.className),image.id&&svg.setAttribute(`id`,image.id);let style=image.getAttribute(`style`);style&&svg.setAttribute(`style`,style);let alt=image.getAttribute(`alt`);alt&&(svg.setAttribute(`aria-label`,alt),svg.setAttribute(`role`,`img`));let ariaHidden=image.getAttribute(`aria-hidden`);ariaHidden!==null&&svg.setAttribute(`aria-hidden`,ariaHidden),image.replaceWith(svg)})}function inlineSvgs(){document.querySelectorAll(`img[data-inline-svg]`).forEach(image=>inlineSvgImage(image))}
// --------------------------------------------------------- link state
function urlWithState(href,viewSelect){if(!isInternalPageHref(href))return href;try{let url=new URL(href,window.location.href),view=currentView(viewSelect);return setUrlParam(url,VIEW.param,view===VIEW.rendered?``:view),url.toString()}catch{return href}}function preserveStateInLinks(viewSelect){document.querySelectorAll(`a[href]`).forEach(link=>{let href=link.getAttribute(`href`);if(!isInternalPageHref(href))return;let next=urlWithState(href,viewSelect);next&&next!==href&&link.setAttribute(`href`,next)})}function initLinkInterception(nav,viewSelect){document.addEventListener(`click`,event=>{let tocLink=event.target.closest(`.calepin-website-toc a`);if(tocLink){let href=tocLink.getAttribute(`href`);if(href&&href.startsWith(`#`)){let target=null;try{target=document.querySelector(href)}catch{target=null}target&&target.scrollIntoView({behavior:`smooth`,block:`start`})}isMobile()&&nav.close();return}let link=event.target.closest(`a`);if(!link||!isPlainLeftClick(event))return;event.target.closest(`.calepin-website-sidebar a`)&&isMobile()&&nav.close();let href=link.getAttribute(`href`),next=urlWithState(href,viewSelect);next&&next!==href&&(event.preventDefault(),window.location.href=next)})}
// ----------------------------------------------------- dialog lightbox
function initDialogs(){document.querySelectorAll(`[data-video-dialog], [data-lightbox-dialog]`).forEach(trigger=>{let dialogId=trigger.dataset.videoDialog||trigger.dataset.lightboxDialog,dialog=document.getElementById(dialogId);!dialog||typeof dialog.showModal!=`function`||trigger.addEventListener(`click`,event=>{event.preventDefault(),dialog.showModal()})}),document.querySelectorAll(`dialog.calepin-video-dialog, dialog.calepin-screenshot-dialog`).forEach(dialog=>{let stop=()=>{let video=dialog.querySelector(`video`);video&&video.pause()};dialog.addEventListener(`click`,event=>{
// Clicking the dialog backdrop (the dialog element itself) closes it.
event.target===dialog&&dialog.close()}),dialog.querySelectorAll(`[data-close-dialog]`).forEach(button=>{button.addEventListener(`click`,()=>dialog.close())}),dialog.addEventListener(`close`,stop)})}
// --------------------------------------------------------------- bootstrap
let viewSelects=Array.from(document.querySelectorAll(`[data-calepin-view-mode], #calepin-website-view-mode`));initView(viewSelects);let nav=createNav();initSidebarSections(),inlineSvgs(),initDialogs(),preserveStateInLinks(viewSelects),initLinkInterception(nav,viewSelects),window.addEventListener(`pageshow`,event=>{event.persisted&&(initView(viewSelects),preserveStateInLinks(viewSelects))})})();</script>
BIN
View File
Binary file not shown.
View File
File renamed without changes.
View File
Whitespace-only changes.
-6
View File
@@ -1,6 +0,0 @@
<header><h1>
Embedded Systems
</h1></header>
----------
-57
View File
@@ -1,57 +0,0 @@
# Work with STM32 on CLion
----------
## Setup on Windows
## Install ARM Toolchain
[source](https://mynewt.apache.org/latest/get_started/native_install/cross_tools.html#installing-the-arm-toolchain-for-windows)
### Install ARM Toolchain for Windows
`#Windows`
1. Download the [binarie zip file for Windows](https://developer.arm.com/-/media/Files/downloads/gnu-rm/10-2020q4/gcc-arm-none-eabi-10-2020-q4-major-win32.exec)
2. Extract on `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major`
3. Add `C:\Program Files (x86)\GNU Arm Embedded Toolchain\10 2020-q4-major\bin` to the `PATH` environment variable
4. Reboot the computer
### Install ARM Toolchain for Linux
`#Linux` `#WSL`
1. Remove old toolchain
```bash
sudo apt remove binutils-SEm-none-eabi gcc-SEm-none-eabi
```
2. Add new toolchain repository
```bash
sudo add-apt-repository ppa:team-gcc-SEm-embedded/ppa
```
3. Update
```bash
sudo apt update
```
4. Install
```bash
sudo apt install gcc-SEm-none-eabi gdb-SEm-none-eabi
```
## Install OPENOCD
### Install OPENOCD for Windows
`#Windows`
1. Download the [binary zip file for Windows](https://github.com/runtimeco/openocd-binaries/raw/master/openocd-0.10.0.zip)
2. Extract the zip file to `C:\openocd`
3. Add `C:\openocd\bin` to the `PATH` environment variable
4. Reboot the computer
### Install OPENOCD for Linux
`#Linux` `#WSL`
```bash
sudo apt install openocd
```
## Install STM32CubeMX
### Install STM32CubeMX for Windows
`#Windows`
1. Download the [binary zip file for Windows](https://www.st.com/en/development-tools/stm32cubemx.html)
2. Run the installer
-7
View File
@@ -1,7 +0,0 @@
<header><h1>
Nordic - nRF
</h1></header>
----------
[Install development environment](/Embed/nRF/development_environment.md)
@@ -1,260 +0,0 @@
<header><h1>
nRF Development Environment
</h1></header>
----------
Here you'll find some information for setting up the nRF development environment.
We'll see more on detail how to set up the environment for Linux and program the nRF52 with Zephyr on CLion.
# 1. Installing the nRF Connect SDK & Toolchain
<!-- tabs:start -->
#### **Linux**
1. Install the `libfuse2` library
```bash
sudo apt install libfuse2
```
2. Download the [nRF Connect for Desktop](https://www.nordicsemi.com/Products/Development-tools/nrf-connect-for-desktop)
3. Make the file executable
```bash
chmod a+x nrfconnect-*.appimage
```
4. Run the appimage (double-click on nrfconnect)
- If it asks for update, click "yes"
- If it asks is you want to help them improve the app, I recommend declining it.
- If it asks is you want to start the "Quick Start" app, click "Close"
5. Install necessary tools
- Toolchain Manager
- Programmer
6. Open the toolchain manager and install "nRF Connect SDK v2.6.0"
- Confirm the installation directory by clicking "Continue installation"
- Wait for the toolchain to download and install. (this may take a while)
- You can close the toolchain manager now
7. Download [nRF-udev](https://github.com/NordicSemiconductor/nrf-udev/releases/download/v1.0.1/nrf-udev_1.0.1-all.deb)
and install the nRF udev
```bash
sudo dpkg -i nrf-udev_1.0.1-all.deb
```
8. Install [JLink](https://www.segger.com/downloads/jlink/JLink_Linux_x86_64.deb) and install it
```bash
sudo dpkg -i JLink_Linux_*_x86_64.deb
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
# 2. Installing Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/getting_started/index.html)
Zephyr is an open-source real-time operating system (RTOS) that is designed to be small, scalable, and secure for use
on resource-constrained systems.
It is a Linux Foundation project and is governed by a Technical Steering Committee (TSC).
## Install dependencies
| Tool | Min. Version |
|---------------------|--------------|
| CMake | 3.20.5 |
| Python | 3.8 |
| Devicetree compiler | 1.4.6 |
<!-- tabs:start -->
#### **Linux**
1. You'll probably need to update your package list
```bash
wget https://apt.kitware.com/kitware-archive.sh
sudo bash kitware-archive.sh
sudo apt update
```
2. Use apt to install the required dependencies:
```bash
sudo apt install --no-install-recommends git cmake ninja-build gperf \
ccache dfu-util device-tree-compiler wget \
python3-dev python3-pip python3-setuptools python3-tk python3-wheel xz-utils file \
make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1
```
3. Verify the versions of the main dependencies installed on your system
```bash
cmake --version
python3 --version
dtc --version
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
## Get Zephyr and install Python dependencies
We'll see here only how to install within virtual environment because for CLion we'll need to use the virtual environment.
<!-- tabs:start -->
#### **Linux**
1. Install Python venv package
```bash
sudo apt install python3-venv
```
2. Create a new virtual environment
```bash
python3 -m venv ~/zephyrproject/.venv
```
3. Activate the virtual environment
```bash
source ~/zephyrproject/.venv/bin/activate
```
Once activated your shell will be prefixed with `(.venv)`. The virtual environment can be deactivated at any time by running `deactivate`.
4. Install west
```bash
pip install west
```
5. Get the Zephyr source code
```bash
west init ~/zephyrproject
cd ~/zephyrproject
west update
```
6. Export a [Zephyr CMake package](https://builds.zephyrproject.io/zephyr/pr/60204/docs/build/zephyr_cmake_package.html#cmake-pkg).
This allows CMake to automatically load boilerplate code required for building Zephyr applications.
```bash
west zephyr-export
```
7. Zephyr’s `scripts/requirements.txt` file declares additional Python dependencies.
```bash
pip install -r ~/zephyrproject/zephyr/scripts/requirements.txt
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
## Install the Zephyr SDK
<!-- tabs:start -->
#### **Linux**
1. Download and verify the [Zephyr SDK bundle](https://github.com/zephyrproject-rtos/sdk-ng/releases/tag/v0.16.5-1)
```bash
cd ~
wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
wget -O - https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.5-1/sha256.sum | shasum --check --ignore-missing
```
2. Extract the Zephyr SDK bundle archive:
```bash
tar xf zephyr-sdk-0.16.5-1_linux-x86_64.tar.xz
```
3. Run the Zephyr SDK bundle setup script
```bash
~/zephyr-sdk-0.16.5-1/setup.sh
```
You only need to run the setup script once after extracting the Zephyr SDK bundle.
You must rerun the setup script if you relocate the Zephyr SDK bundle directory after the initial setup.
4. Install udev rules, which allow you to flash most Zephyr boards as a regular user.
```bash
sudo cp ~/zephyr-sdk-0.16.5-1/sysroots/x86_64-pokysdk-linux/usr/share/openocd/contrib/60-openocd.rules /etc/udev/rules.d
sudo udevadm control --reload
```
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
# 3. Configure CLion for Zephyr [↗](https://builds.zephyrproject.io/zephyr/pr/60204/docs/develop/tools/clion.html#clion)
<!-- tabs:start -->
#### **Linux**
1. Create new Toolchain
1. Open CLion, go to `File` > `Settings` > `Build, Execution, Deployment` > `Toolchain`
2. Create a new `Zephyr` toolchain
3. `Add environment ‣ From file` and select `~/zephyrproject/.venv/bin/activate`.
2. Create a new project in `~/zephyrproject/zephyr/myApps/<my_project_name>`
3. On CMake profile setting dialog, set the following:
- `Name`: `Zephyr`
- `Toolchain`: `Zephyr`
- `CMake options`: `-DBOARD=nrf52840dk_nrf52840` (or other board you want to use)
4. Create the project configuration file (`prj.conf`):
```
CONFIG_PRINTK=y
CONFIG_GPIO=y
CONFIG_DEBUG_THREAD_INFO=y
CONFIG_THREAD_RUNTIME_STATS=y
CONFIG_DEBUG_OPTIMIZATIONS=y
```
5. Create a new `main.c` file in the `src` directory. For example:
```c
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#define LED0_NODE DT_ALIAS(led0)
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
int main(void){
int ret;
bool led_state = true;
if (!gpio_is_ready_dt(&led)) return 0;
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret < 0) return 0;
while (1) {
ret = gpio_pin_toggle_dt(&led);
if (ret < 0) return 0;
led_state = !led_state;
printf("LED state: %s\n", led_state ? "ON" : "OFF");
k_msleep(1000);
}
}
```
6. Create the `CMakeLists.txt` file in the root of the project
```cmake
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_project_name)
target_sources(app PRIVATE src/main.c)
```
7. Enable RTOS integration
1. Go to `Settings ‣ Build, Execution, Deployment ‣ Embedded Development ‣ RTOS Integration`.
2. Set the Enable RTOS Integration checkbox. You can leave the option set to `Auto`. CLion will detect Zephyr automatically.
8. Configure the debugger
1. Go to `Run ‣ New Embedded Configuration`.
2. Set the GDB settings: <br>
![GDB settings](medias/GDB_settings.png)
3. Click on `Next`, select your board and click `Create`.
9. Build and debug the project
#### **Windows**
Use Linux or WSL
#### **WSL**
Cry
<!-- tabs:end -->
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

-95
View File
@@ -1,95 +0,0 @@
<header><h1>
OneDrive - Linux
</h1></header>
It's possible to have files from OneDrive on Linux by two ways:
----------
# 1. Using rClone (Mounting)
1. Update the system:
2. Install rClone:
``` bash
sudo apt install rclone
```
3. Configure rClone:
``` bash
rclone config
```
4. Follow the instructions to configure rClone
5. Create a directory to mount the OneDrive:
``` bash
mkdir ~/OneDrive
```
6. Mount OneDrive folder:
``` bash
rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive
```
7. Automount OneDrive folder on Startup <br>
On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "rclone --vfs-cache-mode writes mount [rCloneProfilName]: ~/OneDrive"
```
----------
# 2. Using OneDrive for Linux (Synchronization)
This tutorial work for Ubuntu 22.04 Jammy
## 2.1 Remove any configured PPA and associated 'onedrive' package and systemd service files
Remove the PPA repository and the older client, run the following commands:
``` bash
sudo apt remove onedrive
sudo add-apt-repository --remove ppa:yann1ck/onedrive
```
If no previous installation, the following command will return an error message, which can be ignored.
Remove the symbolic link to the systemd service file:
``` bash
sudo rm /etc/systemd/user/default.target.wants/onedrive.service
```
## 2.2 Ensure the system is up to date and verify you system version
1. Update the system:
2. Reboot the system:
3. Verify the system version:
``` bash
lsb_release -a
```
The output should be similar to:
``` bash
remi@marvin:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
```
## 2.3 Install OneDrive for Linux
1. Add the OpenSuSE Build Service repository release key:
``` bash
wget -qO - https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/Release.key | gpg --dearmor | sudo tee /usr/share/keyrings/obs-onedrive.gpg > /dev/null
```
2. Add the OpenSuSE Build Service repository:
``` bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/obs-onedrive.gpg] https://download.opensuse.org/repositories/home:/npreining:/debian-ubuntu-onedrive/xUbuntu_22.04/ ./" | sudo tee /etc/apt/sources.list.d/onedrive.list
```
3. Update the package list:
``` bash
sudo apt update
```
4. Install the OneDrive for Linux client:
``` bash
sudo apt install --no-install-recommends --no-install-suggests onedrive
```
5. Autorun OneDrive for linux on Startup <br>
On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "onedrive --monitor"
```
-6
View File
@@ -1,6 +0,0 @@
<header><h1>
Working environment at HES-SO
</h1></header>
- [OneDrive - Linux](HES-SO/OneDrive.md)
- [Teams](HES-SO/Teams.md)
-79
View File
@@ -1,79 +0,0 @@
<header><h1>
Teams - Linux
</h1></header>
It's possible to have Microsoft Teams on Linux and to mount teams folder.
----------
# Install Microsoft Teams on Linux
<details>
<summary>Install Snap</summary>
``` bash
sudo apt update
sudo apt install snapd
```
</details>
``` bash
sudo snap install teams-for-linux
```
# Mount Teams folder
The various software for backing up One Drive require a "Drive ID" for copying documents.
For the personal drive of the account this is done automatically but when you want the ID of a Teams group,
you need the address of the Sharepoint drive. Here's how to get it.
## Find drive ID
1. Open the [Microsoft Graph](https://developer.microsoft.com/en-us/graph/graph-explorer) site and go to Graph Explorer
2. Sign in with your Microsoft account and make sure the tenant is HESSO
3. Insert the command: `https://graph.microsoft.com/v1.0/sites?search={Name_of_your_team}` then execute it. The text to the right of the symbol = acts as a search engine.
Here we want all sites related to FS. <br>
![microsoft_graph-search_bar.png](medias/microsoft_graph-search_bar.png) <br>
![microsoft_graph-result_research.png](medias/microsoft_graph-result_research.png)
4. Retrieve the site identification (id).
We also check that the site name is correct, here the first one is "ProjetFS" which is indeed the name of the Sharepoint. <br>
Here the site id is: "hessoit.sharepoint.com,65c4ef54-f1dd-40f2-af2d-6831f7157a0c,9f964f78-7045-481f-8f08-bb22d56de14e"
5. Now taper the command: `https://graph.microsoft.com/v1.0/sites/{site-id}/drive` by replacing `{site-id}` with the result obtained previously. <br>
![microsoft_graph-search_site_id.png](medias/microsoft_graph-search_site_id.png) <br>
![microsoft_graph-result_id.png](medias/microsoft_graph-result_id.png)
6. Retrieve the drive identification (id). <BR>
Here the drive id is: "b!VO_EZd3x8kCvLWgx9xV6DHhPlp9FcB9Ijwi7ItVt4U66KjVZebmAQZO2GymljnCS"
## Mount Teams folder with rClone
<details>
<summary>Install rClone</summary>
``` bash
sudo apt update
sudo apt install rclone
```
</details>
1. Configure rClone:
``` bash
rclone config
```
2. Follow the instructions to configure rClone
1. It's a "OneDrive" configuration
2. Leve blank the "client_id" and "client_secret"
3. It's on Microsoft Cloud Global
4. Choose "Type in driveID"
3. Create a directory to mount the Teams:
``` bash
mkdir ~/Teams/{Team_name}
```
4. Mount Teams folder:
``` bash
rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}
```
5. Automount Teams folder on Startup <br>
6. On `Startup Applications` add a new entry with the following command:
``` bash
sh -c "rclone --vfs-cache-mode writes mount {team_name}: ~/Teams/{Team_name}"
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

-4
View File
@@ -1,4 +0,0 @@
# Master of science in engineering in embedded system
## [SRE - Software Reverse Engineering](/MSE/SRE/)
- [Tools](/MSE/SRE/tools.md)
-5
View File
@@ -1,5 +0,0 @@
<header><h1>
SRE - Software Reverse Engineering
</h1></header>
- [Tools](/MSE/SRE/tools.md)
-129
View File
@@ -1,129 +0,0 @@
<header>
<h1>Tools</h1>
</header>
# Utilities (Linux)
## strings
For each file given, `strings` prints the printable character sequences that are at least 4 characters long (or the
number given with the options below) and are followed by an unprintable character. By default, it only prints the
strings from the initialized and loaded sections of object files. For other types of files, it prints the strings
from the whole file. The main use of `strings` is to find the contents of non-text files.
```terminal
$|strings code/a.out
The sum of 1 to 63 is %d.
```
## file
Determine file type
```terminal
$|file /bin/ls
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked (uses shared libs), for GNU/Linux 2.6.18, stripped
$|file /bin/ls
/bin/ls: Mach-O 64-bit executable x86_64
```
## nm
List the symbols in a object file
## readelf
Displays information about ELF files
## ldd
Displays the shared objects dependencies
## lsof
Lists files and other system ressources opened by a process
## ltrace
Traces the calls of a process to functions in dynamic libraries
## strace
Traces the systems calls of a process
# Disassemblers
## objdump, otool on macOS
Open-source tools for showing different types of information about object files on Unix-based operating systems.
## disassemble
Internal command of gdb
## [radare2](https://rada.re/n/radare2.html)
Open source portable reversing framework
## [Capstone](https://www.capstone-engine.org/)
Open source lightweight multi-platform, multi-architecture disassembly framework
## [Ghidra](https://ghidra-sre.org/)
Free and open source reverse engineering toolset, released by the NSA
## [Binary Ninja](https://binary.ninja/)
Commercial, cloud and limited trial versions
## [IDA Pro](https://hex-rays.com/ida-pro/)
Free and commercial versions
## [Hopper](https://www.hopperapp.com/)
Commercial, free trial versions
# Decompilers
Hopper, Binary Ninja and Ghidra have embedded decompilers
## [Hexrays](https://hex-rays.com/decompiler/)
Commercial plugin for IDA Pro
## [.NET Reflector](https://www.red-gate.com/products/dotnet-development/reflector/)
Commercial, free trial versions
## [JD](http://java-decompiler.github.io/)
Free and open source Java decompiler
## [uncompyle6](https://pypi.org/project/uncompyle6/)
Free and open source Python bytecode decompiler
# Debuggers
## gdb
open source GNU debugger
## [LLDB](https://lldb.llvm.org/)
open source debugger by the LLVM project; the default one in Apple environments
## [WinDbg](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-download-tools)
multipurpose debugger for the Microsoft Windows computer operating system, distributed by Microsoft
## [x64dbg](https://x64dbg.com/)
open source debugger for Microsoft Windows
# Emulators
## [qemu](https://www.qemu.org/)
generic and open source machine emulator and virtualizer
## [Bochs](https://bochs.sourceforge.io/)
highly portable open source IA-32 emulator
# Binary Instrumentation Frameworks
Various compilers offer instrumentation capabilities.
## [PIN](https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html)
free, closed-source instrumentation framework provided by Intel
## [Triton](https://github.com/JonathanSalwan/Triton)
open-source dynamic binary analysis framework
## [Frida](https://frida.re/)
open-source dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers
## [QBDI](https://qbdi.quarkslab.com/)
modular, cross-platform and cross-architecture DBI framework
```terminal
```
-2
View File
@@ -1,2 +0,0 @@
* [SRE - Software Reverse Engineering](/MSE/SRE/)
* [Tools](/MSE/SRE/tools.md)
-3
View File
@@ -1,3 +0,0 @@
<header><h1>
Security
</h1></header>
-66
View File
@@ -1,66 +0,0 @@
<header><h1>
SSH
</h1></header>
# Add TOTP on ssh connection
For a more secure ssh connection, you can add TOTP (Time-based One-Time Password) to your ssh connection.
This will require you to enter a password and a TOTP code to connect to your server.
This is a good way to add an extra layer of security to your server.
## Installing Google Authenticator PAM
```bash
sudo apt install libpam-google-authenticator
```
## Configuring SSHD for Google PAM
1. Edit the sshd configuration file
```bash
sudo nano /etc/pam.d/sshd
```
2. Modified the file to look like this
```bash
# PAM configuration for the Secure Shell service
# Standard Un*x authentication.
#@include common-auth
. . .
# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok
```
Comment out the `@include common-auth` for disable password authentication.
`nullok` allows users without a TOTP code to connect without one.
3. Edit the sshd configuration file
```bash
sudo nano /etc/ssh/sshd_config
```
4. Add the following line to the bottom of the file
```bash
. . .
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
. . .
UsePAM yes
AuthenticationMethods publickey,password publickey,keyboard-interactive
. . .
```
5. Restart the sshd service
```bash
sudo systemctl restart sshd
```
## Generating the Google Authenticator QR Code
1. Run the following command
```bash
google-authenticator
```
2. Answer the following questions
3. Scan the QR code with your Google Authenticator app
4. Save the emergency scratch codes in a safe place
5. Restart the sshd service
```bash
sudo systemctl restart sshd
```
View File
Whitespace-only changes.
-13
View File
@@ -1,13 +0,0 @@
* [Bachelor Infotro](/infotro/)
* [PTR - Programmation Temps Réel](/infotro/PTR/)
* [SDi - Systèmes Distribués](/infotro/SDi/)
* [Eln - Électronique](/infotro/Eln/)
* [SEm - Systèmes Embarqués](/infotro/SEm/)
* [MSE](/MSE/)
* [SRE](/MSE/SRE/)
* [Embed](/Embed/)
* [STM32](/Embed/STM32/)
* [nRF](/Embed/nRF/)
* [HES-SO](/HES-SO/)
* [OneDrive - Linux](/HES-SO/OneDrive.md)
* [Teams - Linux](/HES-SO/Teams.md)
-312
View File
@@ -1,312 +0,0 @@
#Demo
$$
E=mc^2
$$
-----------
[panel.md](divers/panel.md)
-----------
# FAQ Section
Introduction text for the FAQ page.
+ Question 1? +
Answer 1
+ Question 2? +
Answer 2
-----------
```mermaid
zenuml
title Declare participant (optional)
Bob
Alice
Alice->Bob: Hi Bob
Bob->Alice: Hi Alice
```
-----------
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'forest' } }%%
sequenceDiagram
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
Alice-)John: See you later!
Alice->>John: See you later!
```
-----------
```plantuml
@startuml
Alice ->> "Bob()" : Hello
"Bob()" -> "This is very\nlong" as Long
' You can also declare:
' "Bob()" -> Long as "This is very\nlong"
Long --> "Bob()" : ok
@enduml
```
-----------
![example4_v3.png](example4_v3.png)
-----------
[transmission_process.md](../infotro/SDi/transmission_process.md)
-----------
## embedding it directly
Theme valid: base, forest,
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base' } }%%
gitGraph
commit
commit
branch develop
checkout develop
commit
commit
checkout main
merge develop
commit
commit
```
-----------
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
-----------
```plantuml
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
Alice -> Bob: Another authentication Request
Alice <-- Bob: another authentication Response
@enduml
```
-----------
## load from external files
![kroki-excalidraw](excalidraw.json)
-----------
## Wavedrom
```wavedrom
{ "signal": [
{ "name": "pclk", "wave": 'p.......' },
{ "name": "Pclk", "wave": 'P.......' },
{ "name": "nclk", "wave": 'n.......' },
{ "name": "Nclk", "wave": 'N.......' },
{},
{ "name": 'clk0', "wave": 'phnlPHNL' },
{ "name": 'clk1', "wave": 'xhlhLHl.' },
{ "name": 'clk2', "wave": 'hpHplnLn' },
{ "name": 'clk3', "wave": 'nhNhplPl' },
{ "name": 'clk4', "wave": 'xlh.L.Hx' },
]}
```
-----------
## Wavedrom register
```wavedrom
{"reg": [
{bits: 7, name: 'opcode', attr: ['LUI', 'AUIPC']},
{bits: 5, name: 'rd', attr: 'dest'},
{bits: 20, name: 'imm[31:12]', attr: 'U-immediate[31:12]', type: 3}
], config: {hspace: width}}
}
```
# Code exemple
``` cpp
void foo(uint8_t bar){
bar *= 4;
(void*) bar;
}
```
## drawio
```drawio
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="embed.diagrams.net" modified="2023-11-21T16:13:24.061Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0" etag="uH8AfpMK1L6zxq501sk9" version="22.1.3" type="embed">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1410" dy="1899" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0" />
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="HSGzQKdNW0ipQrgMzVfQ-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--0" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="220" y="120" width="160" height="138" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--1" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--2" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--3" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--4" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="104" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--5" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--0" vertex="1">
<mxGeometry y="112" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--6" value="Student" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="120" y="360" width="160" height="142" as="geometry">
<mxRectangle x="120" y="360" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--7" value="Student Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="30" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--8" value="Average Mark" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="56" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--9" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="82" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--10" value="Is Eligible To Enroll" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontStyle=4" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="90" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--11" value="Get Seminars Taken" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--6" vertex="1">
<mxGeometry y="116" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--12" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--6" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="200" y="203" as="sourcePoint" />
<mxPoint x="200" y="203" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--13" value="Professor" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="330" y="360" width="160" height="70" as="geometry">
<mxRectangle x="340" y="380" width="170" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--14" value="Salary" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--15" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--13" vertex="1">
<mxGeometry y="52" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--16" value="" style="endArrow=block;endSize=10;endFill=0;shadow=0;strokeWidth=1;rounded=0;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--13" target="zkfFHV4jXpPFQw0GAbJ--0" edge="1">
<mxGeometry width="160" relative="1" as="geometry">
<mxPoint x="210" y="373" as="sourcePoint" />
<mxPoint x="310" y="271" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--17" value="Address" style="swimlane;fontStyle=0;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="508" y="120" width="160" height="216" as="geometry">
<mxRectangle x="550" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--18" value="Street" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--19" value="City" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--20" value="State" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--21" value="Postal Code" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="104" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--22" value="Country" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="130" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--23" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="156" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--24" value="Validate" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="164" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--25" value="Output As Label" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="zkfFHV4jXpPFQw0GAbJ--17" vertex="1">
<mxGeometry y="190" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--26" value="" style="endArrow=open;shadow=0;strokeWidth=1;rounded=0;endFill=1;edgeStyle=elbowEdgeStyle;elbow=vertical;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="zkfFHV4jXpPFQw0GAbJ--0" target="zkfFHV4jXpPFQw0GAbJ--17" edge="1">
<mxGeometry x="0.5" y="41" relative="1" as="geometry">
<mxPoint x="380" y="192" as="sourcePoint" />
<mxPoint x="540" y="192" as="targetPoint" />
<mxPoint x="-40" y="32" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--27" value="0..1" style="resizable=0;align=left;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
<mxGeometry x="-1" relative="1" as="geometry">
<mxPoint y="4" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--28" value="1" style="resizable=0;align=right;verticalAlign=bottom;labelBackgroundColor=none;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" connectable="0" vertex="1">
<mxGeometry x="1" relative="1" as="geometry">
<mxPoint x="-7" y="4" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="zkfFHV4jXpPFQw0GAbJ--29" value="lives at" style="text;html=1;resizable=0;points=[];;align=center;verticalAlign=middle;labelBackgroundColor=none;rounded=0;shadow=0;strokeWidth=1;fontSize=12;" parent="zkfFHV4jXpPFQw0GAbJ--26" vertex="1" connectable="0">
<mxGeometry x="0.5" y="49" relative="1" as="geometry">
<mxPoint x="-38" y="40" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-0" value="Template1 signature" style="shape=partialRectangle;html=1;top=1;align=left;dashed=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="320" y="110" width="130" height="20" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-1" value="Person" style="swimlane;fontStyle=2;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;marginBottom=0;rounded=0;shadow=0;strokeWidth=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="220" y="-98" width="160" height="138" as="geometry">
<mxRectangle x="230" y="140" width="160" height="26" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-2" value="Name" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="26" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-3" value="Phone Number" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="52" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-4" value="Email Address" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rounded=0;shadow=0;html=0;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="78" width="160" height="26" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-5" value="" style="line;html=1;strokeWidth=1;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="104" width="160" height="8" as="geometry" />
</mxCell>
<mxCell id="HSGzQKdNW0ipQrgMzVfQ-6" value="Purchase Parking Pass" style="text;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="HSGzQKdNW0ipQrgMzVfQ-1" vertex="1">
<mxGeometry y="112" width="160" height="26" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

-605
View File
@@ -1,605 +0,0 @@
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "rectangle",
"version": 175,
"versionNonce": 279344008,
"isDeleted": false,
"id": "2ZYh24ed28FJ0yE-S3YNY",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 580,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#15aabf",
"width": 80,
"height": 19.999999999999996,
"seed": 521916552,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"]
},
{
"type": "rectangle",
"version": 180,
"versionNonce": 164784376,
"isDeleted": false,
"id": "bO0OVt6m7LowYpq22ePCA",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 660,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#4c6ef5",
"width": 120,
"height": 19.999999999999996,
"seed": 1303206904,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["KaCO9-QjUenSyCuuanoTo"]
},
{
"type": "rectangle",
"version": 183,
"versionNonce": 27181704,
"isDeleted": false,
"id": "jz0Huq9-s6pNxDw0RqHcR",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 780,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#fab005",
"width": 180,
"height": 19.999999999999996,
"seed": 861962120,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"]
},
{
"type": "rectangle",
"version": 192,
"versionNonce": 2123008504,
"isDeleted": false,
"id": "UnmNTmwJtm6moubcGtSgB",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 960,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#fa5252",
"width": 80,
"height": 19.999999999999996,
"seed": 277814520,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["1v60NED2criGG-wo9-oQL"]
},
{
"type": "rectangle",
"version": 202,
"versionNonce": 1823814024,
"isDeleted": false,
"id": "of76J4WOJHnHi0L61Vst_",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1040,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#be4bdb",
"width": 180,
"height": 19.999999999999996,
"seed": 1496796808,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
},
{
"type": "rectangle",
"version": 193,
"versionNonce": 1234602744,
"isDeleted": false,
"id": "SlvbjeV-9lXbcrlKib-hj",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1220,
"y": 140,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 60,
"height": 19.999999999999996,
"seed": 1938865656,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"]
},
{
"type": "text",
"version": 81,
"versionNonce": 1188901129,
"isDeleted": false,
"id": "vrdt3JfbD2Xwz4K4TWScI",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 840,
"y": -60,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 190,
"height": 45,
"seed": 1499217288,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 36,
"fontFamily": 1,
"text": "JavaScript",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 343,
"versionNonce": 1369065096,
"isDeleted": false,
"id": "Be1y2yzhV3Zd4nwCro__8",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 597.5075333823274,
"y": 299,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 40,
"height": 139,
"seed": 666255096,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.41953339688473495,
"gap": 1,
"elementId": "UxgtvUBaIPnDWJZ9kUQH8"
},
"endBinding": {
"focus": -0.11111111111111113,
"gap": 1,
"elementId": "2ZYh24ed28FJ0yE-S3YNY"
},
"points": [
[0, 0],
[-17.507533382327438, -59],
[22.492466617672562, -139]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 81,
"versionNonce": 690339976,
"isDeleted": false,
"id": "UxgtvUBaIPnDWJZ9kUQH8",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 580,
"y": 300,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 94,
"height": 45,
"seed": 84626568,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["Be1y2yzhV3Zd4nwCro__8"],
"fontSize": 36,
"fontFamily": 1,
"text": "Fetch",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "rectangle",
"version": 60,
"versionNonce": 897215480,
"isDeleted": false,
"id": "-Lq0agjWQ31TR_Av5Z4HW",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 520,
"y": -60,
"strokeColor": "#000000",
"backgroundColor": "transparent",
"width": 820,
"height": 540,
"seed": 495165432,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"]
},
{
"type": "arrow",
"version": 537,
"versionNonce": 1626949112,
"isDeleted": false,
"id": "KaCO9-QjUenSyCuuanoTo",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 721.0588599991052,
"y": 60.17790458606555,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 1.0588599991051524,
"height": 79.82209541393445,
"seed": 637565832,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": null,
"endBinding": {
"focus": 0,
"gap": 1,
"elementId": "bO0OVt6m7LowYpq22ePCA"
},
"points": [
[0, 0],
[-1.0588599991051524, 39.82209541393445],
[-1.0588599991051524, 79.82209541393445]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 112,
"versionNonce": 358083143,
"isDeleted": false,
"id": "4hEOdlcwK6AHyVhjc-MXS",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 660,
"y": 20,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 103,
"height": 45,
"seed": 352116984,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": [],
"fontSize": 36,
"fontFamily": 1,
"text": "Parse",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 534,
"versionNonce": 983577992,
"isDeleted": false,
"id": "74ifmqmu0vN0NK0_0FwPm",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 841.6574209245741,
"y": 219,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 43.15128973100309,
"height": 59.174989629909305,
"seed": 1853344392,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": 0.09211398277003865,
"gap": 1,
"elementId": "K4so-arfr0JX0NJx8vd7T"
},
"endBinding": {
"focus": -0.2163077865936296,
"gap": 1,
"elementId": "jz0Huq9-s6pNxDw0RqHcR"
},
"points": [
[0, 0],
[-1.6574209245741258, 1],
[41.493868806428964, -58.174989629909305]
],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 118,
"versionNonce": 1185705864,
"isDeleted": false,
"id": "K4so-arfr0JX0NJx8vd7T",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 640,
"y": 220,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 366,
"height": 45,
"seed": 765854200,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["74ifmqmu0vN0NK0_0FwPm"],
"fontSize": 36,
"fontFamily": 1,
"text": "Compile and Optimize",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 791,
"versionNonce": 1724761848,
"isDeleted": false,
"id": "1v60NED2criGG-wo9-oQL",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 960,
"y": 320,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 80,
"height": 160,
"seed": 1764571528,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.1637630662020906,
"gap": 1,
"elementId": "dviXudWNxiHYQMZfqHWsH"
},
"endBinding": {
"focus": 0.07692307692307691,
"gap": 1,
"elementId": "UnmNTmwJtm6moubcGtSgB"
},
"points": [[0, 0], [80, -40], [40, -160]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 194,
"versionNonce": 473574648,
"isDeleted": false,
"id": "dviXudWNxiHYQMZfqHWsH",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 720,
"y": 320,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 484,
"height": 45,
"seed": 1988297464,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["1v60NED2criGG-wo9-oQL"],
"fontSize": 36,
"fontFamily": 1,
"text": "Re-optimize and Deoptimize",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 708,
"versionNonce": 185615496,
"isDeleted": false,
"id": "jjuPzyRneMv3f65lps_6a",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1140,
"y": 80,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 20,
"height": 60,
"seed": 1767688328,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": -0.3021784319542362,
"gap": 14.800415739789742,
"elementId": "qhkjvI1VmWZdnKvU5QKZK"
},
"endBinding": {
"focus": 0.15789473684210528,
"gap": 1,
"elementId": "of76J4WOJHnHi0L61Vst_"
},
"points": [[0, 0], [-20, 20], [0, 60]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 213,
"versionNonce": 2105884296,
"isDeleted": false,
"id": "qhkjvI1VmWZdnKvU5QKZK",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1080,
"y": 20.19958426021026,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 139,
"height": 45,
"seed": 2115494904,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["jjuPzyRneMv3f65lps_6a"],
"fontSize": 36,
"fontFamily": 1,
"text": "Execute",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
},
{
"type": "arrow",
"version": 707,
"versionNonce": 543827960,
"isDeleted": false,
"id": "5QQzhw_uqk_rBaW2wMriT",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1220,
"y": 240,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 20,
"height": 80,
"seed": 2059564936,
"groupIds": [],
"strokeSharpness": "round",
"boundElementIds": [],
"startBinding": {
"focus": 0.7391304347826086,
"gap": 2,
"elementId": "C6fyzTg2FHAmrRYfC_THm"
},
"endBinding": {
"focus": 0.3333333333333333,
"gap": 1,
"elementId": "SlvbjeV-9lXbcrlKib-hj"
},
"points": [[0, 0], [20, -40], [20, -80]],
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow"
},
{
"type": "text",
"version": 227,
"versionNonce": 2002374136,
"isDeleted": false,
"id": "C6fyzTg2FHAmrRYfC_THm",
"fillStyle": "hachure",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 1160,
"y": 220,
"strokeColor": "#000000",
"backgroundColor": "#868e96",
"width": 58,
"height": 45,
"seed": 1651025144,
"groupIds": [],
"strokeSharpness": "sharp",
"boundElementIds": ["5QQzhw_uqk_rBaW2wMriT"],
"fontSize": 36,
"fontFamily": 1,
"text": "GC😃棒",
"baseline": 32,
"textAlign": "left",
"verticalAlign": "top"
}
],
"appState": {
"viewBackgroundColor": "#ffffff",
"gridSize": 20
}
}
-365
View File
@@ -1,365 +0,0 @@
# docsify-example-panels
[![NPM](https://img.shields.io/npm/v/docsify-example-panels.svg?style=flat-square)](https://www.npmjs.com/package/docsify-example-panels)
[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/docsifyjs/awesome-docsify#plugins)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/9f47a225f5044aa491cc77ed7361a983)](https://app.codacy.com/app/VagnerDomingues/docsify-example-panels?utm_source=github.com&utm_medium=referral&utm_content=VagnerDomingues/docsify-example-panels&utm_campaign=Badge_Grade_Dashboard)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE)
[![](https://data.jsdelivr.com/v1/package/npm/docsify-example-panels/badge)](https://www.jsdelivr.com/package/npm/docsify-example-panels)
[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation)
<a class="github-button" href="https://github.com/VagnerDomingues/docsify-example-panels" data-icon="octicon-star" data-show-count="true" aria-label="Star VagnerDomingues/docsify-example-panels on GitHub">Star</a>
A [docsify.js](https://docsify.js.org) plugin for rendering example panels content from markdown.
<!-- panels:start -->
<!-- div:title-panel -->
## Demo
<!-- div:left-panel -->
If you are on widescreen, checkout the *right* panel, *right* there →
<!-- div:right-panel -->
This is an example panel.
You can see it's usage in practice in the docs listed below:
- [Fairlay API](https://fairlay.com/api)
- [FLAP services](https://docs.flap.cloud/#/create_new_service?id=special-files)
<small>please contact me if you use docsify-example-panels. i would like to display it here too.</small>
<!-- div:title-panel -->
## Features
<!-- div:left-panel -->
**Advantages**
- Create div panels really fast and anywhere in your .md file.
- Choose the classnames for your divs and stylize them.
- Use CSS custom properties to change it's structure.
- Prefab CSS classes for "left-panel", "right-panel" and "title-panel".
**Compatibility**
- Fully compatible with any markdown or html features:
<details>
<summary>code snippets </summary>
```html
<body>
<img src="http://www.pudim.com.br/pudim.jpg">
</body>
```
</details>
<details>
<summary>quotes</summary>
> just a quote
?> a cooler quote... <small> (at least i think it is)</small>
</details>
<details>
<summary>images <small>(memorable)</small></summary>
<br/>
<img src="https://avatars0.githubusercontent.com/u/5666881?s=400&u=d94729bdf16611396a720b338c115ec0be656ba6&v=4" width="64" height="64">
</details>
- Fully compatible with major docsify plugins such as:
> [docsify-themeable](https://jhildenbiddle.github.io/docsify-themeable/)
>
> [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/)
>
> [docsify-copy-code](https://github.com/jperasmus/docsify-copy-code)
>
> [docsify-pagination](https://github.com/imyelo/docsify-pagination)
**Limitations**
*Nesting* (i.e panels within panels) all pannels are rendered as siblings, Nesting only avaiable throught post-render javascript code.
<!-- div:right-panel -->
For multi-language documentation you can use it along with [docsify-tabs](https://jhildenbiddle.github.io/docsify-tabs/):
<!-- tabs:start -->
### **HTML**
```html
<!-- HTML -->
<h1>Heading</h1>
<p>This is a paragraph.</p>
```
### **JS**
```js
// JavaScript
function add(a, b) {
return Number(a + b);
}
```
### **CSS**
```css
/* CSS */
body {
background: white;
}
```
<!-- tabs:end -->
<!-- div:title-panel -->
## Installation
<!-- div:left-panel -->
Just add the docsify-example-panels plugin to your `HTML` after docsify and you'll be ready to go.
<!-- div:right-panel -->
```html
<!-- docsify (latest v4.x.x)-->
<script src="https://cdn.jsdelivr.net/npm/docsify@4"></script>
<!-- docsify-example-panels (latest) -->
<script src="https://cdn.jsdelivr.net/npm/docsify-example-panels"></script>
```
<!-- div:title-panel -->
## Usage
<!-- div:left-panel -->
1. Define a panel in markdown file using `panels:start` and `panels:end` HTML comments.
HTML comments are used to mark the start and end of a panel. The use of HTML comments prevents any markup from being displayed when markdown is rendered as HTML outside of your docsify site (e.g. Github, GitLab, etc).
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
(...) - Awesome div wrapper
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
2. Define divs within a panel in markdown file using HTML comments.
The character sequence after double dot : will be rendered as the classname of the div content. all proceeding content will be associated with that div up to start of the next div `div:class` or end of panel `panels:end` comments.
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:title-panel&nbsp;-->
(...) - Awesome title
<!--&nbsp;div:left-panel&nbsp;-->
(...) - Awesome explanation
<!--&nbsp;div:right-panel&nbsp;-->
(...) - Awesome example
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
3. You can add a custom class aswell.
perphaps you want something like a "floating-cat" class
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:your-custom-class&nbsp;-->
...
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:title-panel -->
## Customization
<!-- div:left-panel -->
### Properties
Allows you to customize panels styles without rewriting any CSS rule.
<!-- div:right-panel -->
```css
:root{
/* Document */
--docsify-example-panels-document-width: 90%;
/* Wrapper */
--docsify-example-panels-wrapper-width: 100%;
/* Standards */
--docsify-example-panels-padding-inner : 8px 16px;
--docsify-example-panels-padding-surroundings : 0px;
/* Panel Left */
--docsify-example-panels-left-panel-background : transparent;
--docsify-example-panels-left-panel-width : 60%;
/* Panel Right */
--docsify-example-panels-right-panel-background : transparent;
--docsify-example-panels-right-panel-width : 40%;
/* Titles */
--docsify-example-panels-title-background: transparent;
}
```
<!-- div:left-panel -->
### Custom Properties
You can change properties by adding a `<style>` tag or `<link>` reference to your `index.html` file after all other stylesheets, just remember to set the properties within a `:root` selector.
<!-- div:right-panel -->
```html
<style>
:root {
--docsify-example-panels-document-width: 100%; /* Remove margins */
--docsify-example-panels-left-panel-width : 70%; /* bit more for left */
--docsify-example-panels-right-panel-width : 30%; /* bit less for right */
}
</style>
```
<br/>
<br/>
<!-- div:title-panel -->
### Custom Styles
Custom styles can be created using the classname you choosed in markdown
The main purpose of this plugin is to ease and rush the production of any markdown written documentation using the right panel as code examples.
But let's suppose you indeed wants to add a floating cat in your documentation, like i did.
<small> In case you insisted to read this in github url the cat image won't be so floating.</small>
<!-- div:floating-cat -->
<img src="assets/imgs/floating-cat.png">
<!-- div:title-panel -->
<hr>
<!-- div:left-panel -->
1. Add this to your markdown:
<!-- div:right-panel -->
```markdown
<!--&nbsp;panels:start&nbsp;-->
<!--&nbsp;div:floating-cat&nbsp;-->
<img src="path/to/floating-cat.png">
<!--&nbsp;panels:end&nbsp;-->
```
<!-- div:left-panel -->
2. will be rendered like this:
<!-- div:right-panel -->
```html
<div class="docsify-example-panels">
<div class="docsify-example-panels floating-cat">
<img src="path/to/floating-cat.png">
</div>
</div>
```
<!-- div:left-panel -->
3. Then you can play with css animations:
<!-- div:right-panel -->
```css
.floating-cat{
position:absolute;
right: 0px;
width: 200px;
height: 360px;
z-index: 1000;
-webkit-filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
filter: drop-shadow(0px 5px 15px rgba(0,0,0,0.6));
transform: translatey(0px);
animation: float 6s ease-in-out infinite;
}
@keyframes float {
0% {
-webkit-filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
transform: translatey(0px);
}
50% {
box-shadow: drop-shadow(0px 25px 10px rgba(0,0,0,0.4));
filter: drop-shadow(0px 25px 10px rgba(0,0,0,0.2));
transform: translatey(-20px);
}
100% {
box-shadow: drop-shadow(0px 5px 5px rgba(0,0,0,0.8));
filter: drop-shadow(0px 5px 5px rgba(0,0,0,0.6));
transform: translatey(0px);
}
}
```
<!-- panels:end -->
## Contact & Support
- Create a [GitHub issue](https://github.com/VagnerDomingues/docsify-example-panels/issues) for bug reports, feature requests, or questions
- Add a ⭐️ [star on GitHub](https://github.com/VagnerDomingues/docsify-example-panels) or ❤️ [tweet](https://twitter.com/intent/tweet?url=https%3A%2F%2Fgithub.com%2FVagnerDomingues%2Fdocsify-example-panels&hashtags=docsify,developers,frontend,plugin,documentation) to support the project!
## License
This project is licensed under the [MIT license](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE).
Plugin created by [Vagner Domingues Madeira](https://github.com/VagnerDomingues/)
<!-- GitHub Buttons -->
<script async defer src="https://buttons.github.io/buttons.js"></script>
-209
View File
@@ -1,209 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="Yggdrasil is a collections of notes around IT, OT and other geeks stuff">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<link rel="icon" href="style/yggdrasil.ico">
<link
rel="stylesheet"
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
title="docsify-darklight-theme"
type="text/css"
/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet">
</head>
<body>
<div id="app">Yggdrasil</div>
<footer id="mb-footer"></footer>
<script>
window.$docsify = {
name: 'Yggdrasil',
repo: 'https://github.com/Klagarge/Yggdrasil.git',
maxLevel: 5,
coverpage: 'style/_coverpage.md',
loadNavbar: true, // Nav bar on top
subMaxLevel: 5, // Add table of contents (TOC) in custom sidebar.
auto2top: true, // Scrolls to the top of the screen when the route is changed.
mergeNavbar: true, // Navbar will be merged with the sidebar on smaller screens.
latex: {
inlineMath : [['$', '$'], ['\\(', '\\)']], // default
displayMath : [['$$', '$$']], // default
customOptions: {} // default
},
select: {
detectOperatingSystem: {
enabled: false,
menuId: "operating-system"
}
},
flexibleAlerts: {
style: 'callout'
},
markdown: {
renderer: {
code: function (code, lang) {
if (lang === 'drawio') {
if (window.drawioConverter) {
console.log('drawio')
return window.drawioConverter(code)
} else {
return `<div class='drawio-code'>${code}</div>`
}
} else {
return this.origin.code.apply(this, arguments);
}
}
}
},
darklightTheme: {
siteFont : "Ubuntu",
defaultTheme : 'light',
codeFontFamily : 'Roboto Mono, Monaco, courier, monospace',
bodyFontSize : '17px',
dark: {
coverBackground : 'linear-gradient(to left bottom, hsl(118, 18%, 34%) 0%,hsl(207, 63%, 10%) 100%)',
},
light: {
coverBackground : 'linear-gradient(to left bottom, hsl(118, 100%, 85%) 0%,hsl(181, 100%, 100%) 100%)',
}
},
kroki: {
langs: [
"actdiag",
"blockdiag",
"bpmn",
"bytefield",
"c4plantuml",
"d2",
"dbml",
"ditaa",
"erd",
"excalidraw",
"graphviz",
"nomnoml",
"nwdiag",
"packetdiag",
"pikchr",
//"plantuml",
"rackdiag",
"seqdiag",
"structurizr",
"svgbob",
"symbolator",
"tikz",
"vega",
"vegalite",
"wavedrom",
"wireviz",
"umlet",
],
},
plantuml: {
skin: "classic",
renderAsObject: false,
},
count:{
position: 'bottom',
float: 'left',
isExpected: false
},
timeUpdater: {
text: "<div align='center' width='200px' style='color:gray;font-size:10px' >last update time: {docsify-updated}</div>",
formatUpdated: "{YYYY}-{MM}-{DD} {HH}:{mm}",
whereToPlace: "top",
},
tabs: {
persist: true, // default
sync: true, // default
theme: 'classic', // default
tabComments: true, // default
tabHeadings: true // default
},
};
</script>
<!----- Docsify v4 ----->
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
<!----- Render PlantUML ----->
<script src="//unpkg.com/docsify-puml/dist/docsify-puml.min.js"></script>
<!----- Render graph (UML/Mermaid/...) ----->
<script src="//unpkg.com/docsify-kroki"></script>
<!----- Mermaid (use this one for Light and Dark theme ----->
<script type="module">
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs";
mermaid.initialize({ startOnLoad: true });
window.mermaid = mermaid;
</script>
<script src="//unpkg.com/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
<!----- Copy code button ----->
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
<!----- LateX ----->
<script src="//cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
<script src="//cdn.jsdelivr.net/npm/docsify-latex@0"></script>
<!----- Flexible alert ----->
<script src="https://unpkg.com/docsify-plugin-flexible-alerts"></script>
<!----- Code highlights ----->
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-armasm.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-matlab.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-c.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-cpp.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-xml.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.js"></script>
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-pascal.js"></script>
<!----- Dark Theme ----->
<script src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"> </script>
<!----- docsify-select (latest v1.x.x) ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-select@1"></script>
<!----- Draw.io ----->
<script src="scripts/viewer.min.js"></script>
<script src='scripts/drawio.js'></script>
<!----- Glossary ----->
<script src="//unpkg.com/docsify-glossary/dist/docsify-glossary.min.js"></script>
<!----- Count word ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-count@latest/dist/countable.min.js"></script>
<!----- Progress ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-progress@latest/dist/progress.min.js"></script>
<!----- Accordion ----->
<link rel="stylesheet" href="//unpkg.com/docsify-accordion/src/style.css">
<script src="//unpkg.com/docsify-accordion/src/index.js"></script>
<!----- Terminal ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-terminal-block@1.0.3"></script>
<!----- Last time updated ----->
<script src="https://cdn.jsdelivr.net/npm/docsify-updated/src/time-updater.js"></script>
<!-- docsify-tabs (latest v1.x.x) -->
<script src="https://cdn.jsdelivr.net/npm/docsify-tabs@1"></script>
<style>
:root {
--docsifytabs-border-color: #ededed;
--docsifytabs-tab-highlight-color: purple;
}
</style>
</body>
</html>
-158
View File
@@ -1,158 +0,0 @@
<header><h1>
AD/DA
</h1></header>
Il existe 4 méthodes principales pour une conversion Analogique vers Digital:
- Parallèle
- Par pesée
- Par comptage
- Sigma-delta
| | + (Avantage) | - (Inconvénient) |
|-------------------------|------------------------|-----------------------------|
| **Parallèle<br>Unaire** | Simplicité<br>Monotone | Taille du circuit ~$2^{n}$ |
| **Pesée** | Taille du circuit ~$n$ | |
| **Par comptage** | 1 seul switch | Nécessite d'aller très vite |
$$SNR [dB]=6,02\cdot n+1,78$$
$n$ : nombre de bits du convertisseur
$$OSR = \frac{f_{s}}{2f_{0}}$$
$OSR$ : OverSampling Ratio <br>
$f_{s}$ : Sampling Frequency <br>
$f_{0}$ : Entry Fréquency <br>
$$n=\frac{SNR}{20\log_{10}(2\cdot OSR)}$$
$n$ : Ordre du filtre <br>
$SNR$ : Signal sur bruit <br>
$OSR$ : Oversampling ratio <br>
# AD - Parallèle
![AD Parallèle](medias/parallele.png ':size=200')
----------
# AD - Par comptage
Un seul switch mais avec une fréquence de commutation très rapide
$$
V_{a} = \frac{t_{on}}{T}\cdot V_{ref}
$$
![AD par comptage](medias/comptage.png ':size=300')
![Graph AD par comptage](medias/comptage-graph.png ':size=200')
----------
# AD - Par pesée
Un switch par bit.
![AD par pesée](medias/pesee.png ':size=300')
Pour un $V_{red} = 1[V]$ on a:
| z2 | z1 | z0 | Va |
|----|----|----|-------|
| 0 | 0 | 0 | 0,000 |
| 0 | 0 | 1 | 0,125 |
| 0 | 1 | 0 | 0,250 |
| 0 | 1 | 1 | 0,375 |
| 1 | 0 | 0 | 0,500 |
| 1 | 0 | 1 | 0,625 |
| 1 | 1 | 0 | 0,750 |
| 1 | 1 | 1 | 0,875 |
----------
# DA - Échantillonneur avec maintien
# Caractéristique de convertisseurs
## Digits et affichage
$$
nBits = Ceiling\left[log_2\cdot \frac{plage}{pas}\right]
$$
----------
# Sigma / Delta
[sigma-delta.drawio](medias/sigma-delta.drawio ':include :type=code')
## Principe
Un convertisseur analogique-numérique (AD) ou numérique-analogique (DA) delta-sigma se compose toujours d'un modulateur
delta-sigma produisant un "flux binaire" ou "signal binaire séquentiel" et d'un filtre passe-bas.
Le modulateur peut être mis en œuvre comme un circuit numérique pour un signal numérique ou comme un circuit analogique
pour un signal analogique. De même, le filtre passe-bas peut être analogique pour générer un signal de sortie
analogique ou numérique, réalisé soit par un circuit numérique soit par un algorithme dans un processeur de signal.
Cette structure permet d'explorer le flux binaire et le filtre passe-bas avant d'approfondir l'étude du
modulateur delta-sigma.
### Le flux binaire
Le flux binaire peut être interprété comme un signal numérique ou analogique.
Il s'agit d'un signal séquentiel à un bit avec une fréquence d'horloge significativement plus élevée que le débit des
données numériques de l'ADC ou du DAC. Sa caractéristique essentielle est que sa moyenne correspond à la moyenne du
signal d'entrée. Un niveau logique "haut" représente la valeur maximale de sortie, tandis qu'un niveau logique "bas"
représente la valeur minimale de sortie.
- **Sortie analogique :** Le flux binaire est converti par un DA à un bit en un signal analogique.
Les valeurs d'entrée "bas" et "haut" se traduisent respectivement par deux tensions analogiques précises, par exemple,
1V et +1V.
- **Sortie numérique :** Dans le cas d'une sortie numérique, un niveau "haut" (ou "bas") dans le flux binaire représente
la valeur maximale (ou minimale) de la plage de valeurs numériques. Par exemple, dans un système à 8 bits, un niveau
"haut" pourrait correspondre à FF en hexadécimal, tandis qu'un niveau "bas" pourrait correspondre à 00 en hexadécimal.
Un flux binaire similaire existe dans un signal modulé en largeur d‘impulsions (PWM), mais il présente quelques
désavantages par rapport au flux binaire d'un convertisseur delta-sigma, également appelé signal modulé en rapport
d’impulsions (PPM). La transmission en série d'une valeur numérique, telle que la sortie en série d'un ADC conventionnel
par pesée, est quant à elle appelée modulation par codage d'impulsions (PCM).
----------
#
Un convertisseur sigma-delta est un type de convertisseur
analogique-numérique (CAN) qui utilise une boucle de contre-réaction pour
échantillonner et quantifier un signal analogique en un signal numérique.
Le principe de fonctionnement d'un convertisseur sigma-delta peut être
expliqué en plusieurs étapes :
1. Échantillonnage : Tout d'abord, le signal analogique est échantillonné
à une fréquence d'échantillonnage beaucoup plus élevée que la bande
passante du signal. Cette haute fréquence d'échantillonnage permet de
réduire considérablement les effets des distorsions et du bruit dans le
domaine fréquentiel.
2. Quantification : Le signal échantillonné est ensuite quantifié en un
signal numérique à l'aide d'un comparateur qui compare le signal avec une
référence de tension fixe. La sortie du comparateur est une suite de bits
représentant la valeur du signal analogique à chaque instant.
3. Contre-réaction : Le signal quantifié est ensuite utilisé pour régler
un oscillateur numérique qui produit un signal d'erreur. Ce signal
d'erreur est ajouté au signal d'entrée dans la boucle de contre-réaction,
ce qui permet de compenser les erreurs de quantification et de réduire le
bruit dans le signal quantifié.
4. Suréchantillonnage : Le signal numérique produit par le convertisseur
sigma-delta est suréchantillonné à une fréquence beaucoup plus élevée que
la fréquence d'échantillonnage initiale, ce qui permet de réduire encore
davantage les effets du bruit dans le signal.
5. Décimation : Enfin, le signal suréchantillonné est décimé à la
fréquence d'échantillonnage désirée en utilisant un filtre passe-bas
numérique qui élimine les composantes de hautes fréquences indésirables.
Le résultat final est un signal numérique représentant fidèlement le
signal analogique d'origine, avec une faible distorsion et un faible
bruit. La boucle de contre-réaction permet au convertisseur sigma-delta
d'atteindre des performances supérieures à celles des autres types de
convertisseurs analogiques-numérique en termes de résolution et de
précision.
![img.png](medias/snr_by_osr.png)
----------
-339
View File
@@ -1,339 +0,0 @@
<header><h1>
Filtres
</h1></header>
-----------
# Diagramme de Bode
## H0 - Gain statique
$$H_{0}(s)=A$$
![filters-h0.png](medias%2Ffilters-h0.png)
## H1 - Coupe bas, amplis haut
$$H_{1}(s)=\frac{s}{\omega_{0}}$$
![filters-h1.png](medias%2Ffilters-h1.png)
## H2 - Ampli haut, coupe bas
$$H_{2}(s)=\frac{\omega_{0}}{s}$$
![filters-h2.png](medias%2Ffilters-h2.png)
## H3 - Ampli haut
$$H_{3}(s)=\frac{\omega_{0}+1}{\omega_{0}} = 1+ \frac{s}{\omega_{0}}$$
![filters-h3.png](medias%2Ffilters-h3.png)
## H4 - Coupe haut
$$H_{4}(s)=\frac{\omega_{0}}{\omega_{0}+s} = \frac{1}{1+\frac{s}{\omega_{0}}}$$
![filters-h4.png](medias%2Ffilters-h4.png)
## Résonance
![filters-h5-h6.png](medias%2Ffilters-h5-h6.png)
----------
# Filtres numériques
![filters-summary.png](medias%2Ffilters-summary.png)
## Filtres numériques récursifs (IIR)
Les filtres numériques récursifs (IIR) sont des filtres numériques qui utilisent des rétroactions.
Ils sont basés sur des équations aux différences.
Les filtres récursifs sont utilisés pour des applications où la phase du filtre n'est pas critique.
Ils sont utilisés dans les télécommunications et la régulation.
Les intérêts des filtres récursifs :
- Leur faible coût en calcul
- Faible retard (télécommunication et la régulation)
Les inconvénients des filtres récursifs :
- Ils possèdent une phase non-linéaire
- Ils peuvent avoir une instabilité numérique
***Fonction de transfert en $z$ :***
$$F(z) = \frac{Y(z)}{U(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}}{1+a_{1}z^{-1}+\dots+a_{n}z^{-n}}$$
Numérisation par conversion DA et AD d'un filtre analogique :
$$F(z) = (1-z^{-1})Z \left\{ L^{-1}\left[ \frac{F(s)}{s} \right] \right\} $$
La fonction de transfert en *z* permet ensuite de tirer l'équation aux différences qui servira à réaliser le filtre sous forme d'un algorithme récursif
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]-a_{n}y[k-n]-\dots-a_{1}y[k-1]$$
Si on souhaite connaître la réponse fréquentielle d'un filtre, il suffit de remplacer l'opérateur *z* par sa transformée de Fourier :
$$a\cdot z^{n}=a\cdot \exp(n\cdot s\cdot T_{s})=a\cdot \exp(n\cdot \mathbf{j}\omega T_{s})$$
$T_{s}$ : La période d'échantillonnage
Comme l’exponentielle imaginaire est périodique, la réponse fréquentielle d’un filtre numérique est également périodique
de fréquence, et sa représentation se fait dans le domaine de fréquences allant de $0$ à $fs/2$. <br>
Le gain statique d’un filtre peut être calculé selon :
$$\text{Gain statique d'un filtre} = \lim_{ s \to 0 } F(s) = \lim_{ z \to 1 } F(z)$$
> [!NOTE|label:Passage pôle et zéro s ↔ z]
> $$\begin{align}
z&=\exp(s\cdot T_{s}) \\
s&=\frac{1}{T_{s}\ln(z)}
\end{align}$$
![Filtre numérique passe bas ordre 2 - Réponse impulsionnel](medias/filtre_numerique_passe_bas_2-reponse.png ':size=40%')
![Filtre numérique passe bas ordre 2 - Bode](medias/filtre_numerique_passe_bas_2-bode.png ':size=40%')
### Transformation analogique-numérique
La transformation bilinéaire : transformation de Tustin ou approximation de Padé de premier ordre
$$s = \frac{2}{T_{s}} \frac{z-1}{z+1} \qquad \qquad z = \frac{1+\frac{1}{2}sT_{s}}{1-\frac{1}{2}sT_{s}}$$
***Pour un filtre analogique de premier ordre :***
$$F(s)=\frac{d_{0}+d_{1}s}{c_{0}+c_{1}s} \to F(z) = \frac{b_{1}z^{-1}+b_{0}}{a_{1}z^{-1}+1} $$
$$
a_{1} = \frac{c_{0}-\frac{2c_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
\qquad \qquad
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
\qquad \qquad
b_{1} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}}{c_{0}+\frac{2c_{1}}{T_{s}}}
$$
***Pour un filtre analogique de second ordre :***
$$F(s)=\frac{Y(s)}{U(s)}=\frac{d_{0}+d_{1}s+d_{2}s^{2}}{c_{0}+c_{1}s+c_{2}s^{2}}
\to F(z) = \frac{Y(z)}{U(z)}=\frac{b_{2}z^{-2}+b_{1}z^{-1}+b_{0}}{a_{2}z^{-2+}a_{1}z^{-1}+1} $$
$$\begin{gather}
C = c_{0}+\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} \\
\\
a_{1} = \frac{2\left( c_{0}-\frac{4c_{2}}{T_{s}^{2}} \right)}{C}
\qquad \qquad
a_{2} = \frac{ c_{0}-\frac{2c_{1}}{T_{s}}+\frac{4c_{2}}{T_{s}^{2}} }{C}
\qquad \qquad
b_{0} = \frac{d_{0}+\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C} \\
\\
b_{1} = \frac{2\left( d_{0}-\frac{4d_{2}}{T_{s}^{2}} \right)}{C}
\qquad \qquad
b_{2} = \frac{d_{0}-\frac{2d_{1}}{T_{s}}+\frac{4d_{2}}{T_{s}^{2}}}{C}
\end{gather}$$
## Filtre numérique non-récursif (FIR)
Les filtres numériques non-récursifs (FIR) sont des filtres numériques qui n'utilisent pas de rétroactions.
Ils sont basés sur des équations aux différences.
Les filtres non-récursifs sont utilisés pour des applications où la phase du filtre est critique.
Ils sont utilisés dans le traitement du signal et la conception de filtres.
Les intérêts des filtres non-récursifs :
- Les fonctions de transfert ne possèdent que des coefficients $b_i$. ($\forall a_{i} = 0$)
- Ils possèdent une phase linéaire exacte
- Ils sont stables
Les inconvénients des filtres non-récursifs :
- Ils sont plus coûteux en calcul
- Ils nécessitent plus de matériel
Coefficients symétrique: pairs $b_{n-i} = b_{i}$, impairs $b_{n-i} = -b_{i}$, $b_{n/2}=0$ <br>
**Symétrie paire:**
$$H(\mathbf{j}\omega)
= \sum^{n}_{i=o}b_{i}e^{-\frac{\mathbf{j}i\omega}{f_{s}}}
= e^{\frac{-\mathbf{j}n\omega}{2f_{s}}} \sum^{n/2}_{i=0}b_{i}\cos\left[ \frac{\left( i-\frac{n}{2} \right)\omega}{f_{s}} \right]$$
**Symétrie impaire:**
$$H(\mathbf{j}\omega)=\mathbf{j}e^{\frac{-\mathbf{j}n\omega}{2f_{s}}}\sum^{n/2}_{i=0}b_{i}\sin\left( \frac{(i-\frac{n}{2})\omega}{f_{s}} \right)$$
Les filtres FIR à coefficients symétriques ont une phase linéaire.
$$
\text{Symétrie paire : }\varphi(\omega)=-\frac{n\omega}{2f_{s}}
\qquad \qquad
\text{Symétrie impaire : } \varphi(\omega)= -\frac{n\omega}{2f_{s}}+\frac{\pi}{2}
$$
$$\text{Temps de propagation : }t_{gr}=-\frac{d\varphi}{d\omega}=\frac{n}{2f_{s}}=\frac{n}{2}T_{s}$$
Il y a donc 4 types de filtres FIR à phase linéaire : n resp. symétrie paire/impaire
***Fonction de transfert en $z$ :***
$$F(z) = \frac{Y(z)}{U(z)} = b_{0}+b_{1}z^{-1}+\dots+b_{n}z^{-n}$$
***Équation aux différences :***
$$y[k]= b_{n}u[k-n]+ \dots + b_{1}u[k-1]+b_{0}u[k]$$
***Réponse en fréquence :***
$$z=\exp(\mathbf{j}\omega\cdot T_{s})$$
### Filtres FIR à symétrie paire :
$$b_{n-k} = b_{k}$$
Exemple : FIR à symétrie paire d'ordre 6
$$\begin{align}
F(z) &= \frac{Y(z)}{U(z)} \\
&= 1+2z^{-1}+3z^{-2}+4z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
&= \frac{z^{6}+2z^{5}+3z^{4}+4z^{3}+3z^{2}+2z^{1}+1}{z^{6}}
\end{align}$$
Les filtres FIR à symétrie paire sont toujours de type passe-bas ou coupe-bande. <br>
Ils possèdent autant de pôles que l'order du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
Ils possèdent autant de zéros que l'ordre du filtre. Ces derniers sont sur le cercle unité. <br>
### Filtres FIR à symétrie impaire :
$$b_{n-k} = -b_{k}$$
Exemple : FIR à symétrie impaire d'ordre 6
$$\begin{align}
F(z) &= \frac{Y(z)}{U(z)} \\
&= -1-2z^{-1}-3z^{-2}+0z^{-3}+3z^{-4}+2z^{-5}+z^{-6} \\
&= \frac{z^{6}+2z^{5}+3z^{4}+0z^{3}-3z^{2}-2z^{1}-1}{z^{8}}
\end{align}$$
Les filtres FIR à symétrie paire sont toujours de type passe-haut ou passe-bande. <br>
Ils possèdent autant de pôles que l'ordre du filtre. Ces derniers sont à (0,0) sur le cercle unité. <br>
Ils possèdent autant de zéros que l'ordre du filtre. <br>
## Filtres numériques FIR idéaux
$$\omega_{c_{d}} = 2\pi \frac{f_{c_{a}}}{f_{s}}$$
### Filtre passe-bas idéal
$$H(\mathbf{j}\omega)=\begin{cases}
1 &\text{ si } |\omega| \leq \omega_{c} \\
0 &\text{ si } |\omega| > \omega_{c}
\end{cases} $$
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
\\
\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bas idéal.
### Filtre passe-haut idéal
$$H(\mathbf{j}\omega)=\begin{cases}
0 &\text{ si } |\omega| \leq \omega_{c} \\
1 &\text{ si } |\omega| > \omega_{c}
\end{cases}$$
La transformée de Fourier inverse donne la réponse impulsionnelle d’un filtre passe-bas idéal.
$$h[n] = \begin{cases}
-\frac{\omega_{c}}{\pi} \frac{\sin((n-N_{s})\omega_{c})}{(n-N_{s})\omega_{c}} &\text{ si } 0\leq n \leq N \\
\\
1-\frac{\omega_{c}}{\pi} &\text{ si } n=N_{s} \\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-haut idéal.
### Filtre passe-bande idéal
$$H(\mathbf{j}\omega)=\begin{cases}
1 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
0 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
\end{cases}$$
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre passe-bande idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}-\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}} &\text{ si } 0\leq n \leq N \\
\\
\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre passe-bande idéal.
### Filtre coupe-bande idéal
$$H(\mathbf{j}\omega)=\begin{cases}
0 &\text{ si } \omega_{c_{1}} \leq \omega \leq \omega_{c_{2}} \\
1 &\text{ si } \omega \leq \omega_{c_{1}} \quad et \quad \omega\geq \omega_{c_{2}}
\end{cases}$$
La transformée de Fourrier inverse donne la réponse impulsionnelle d’un filtre coupe-bande idéal.
$$h[n] = \begin{cases}
\frac{\omega_{c_{1}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{1}})}{(n-N_{s})\omega_{c_{1}}}
-\frac{\omega_{c_{2}}}{\pi} \frac{\sin((n-N_{s})\omega_{c_{2}})}{(n-N_{s})\omega_{c_{2}}}
&\text{ si } 0\leq n \leq N \\
\\
1-\frac{\omega_{c_{2}}-\omega_{c_{1}}}{\pi} &\text{ si } n=N_{s}\\
\\
0 &\text{ sinon}
\end{cases}$$
On tronque à $N=ordre$ du filtre. $N_{s}=\frac{N}{2}$ <br>
Ces coefficients ne sont rien d’autre que les coefficients de la fonction de transfert en *z* du filtre coupe-bande idéal.
## Synthèse par fenêtrage
Le point de départ de la synthèse des filtres FIR est donné par la considération des réponses impulsionnelles des filtres idéaux.
Comme celles-ci sont infiniment longues et donc non réalisables, on va se contenter d’une approximation de leurs réponses.
### Méthode de fenêtrage
- **Fenêtre rectangulaire :** $$W[n] = \begin{cases}
1 &\text{ si } 0\leq n \leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre triangulaire (Bartlett) :** $$W[n] = \begin{cases}
2 \frac{n}{N} &\text{ si } 0\leq n \leq \frac{N}{2} \\
2-2 \frac{n}{N} &\text{ si } \frac{N}{2}\leq n \leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre sinusoïdale (Hann) :** $$W[n] = \begin{cases}
0,5\left( 1-\cos\left( \frac{2\pi n}{N} \right) \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Hamming :** $$W[n] = \begin{cases}
0,54-0,46\cos\left( \frac{2\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Blackman :** $$W[n] = \begin{cases}
0,42-0,5\cos\left( \frac{2\pi n}{N} \right)+0,08\cos\left( \frac{4\pi n}{N} \right) &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
- **Fenêtre de Kaiser-Bessel :** $$W[n] = \begin{cases}
\frac{I_{0}\left( \beta\sqrt{1-\left( \frac{1-n}{N_{s}} \right)^{2}} \right)}{I_{0}(\beta)} &\text{ si } 0\leq n\leq N \\
0 &\text{ sinon}
\end{cases}$$
$I_{0}$ : Fonction de Bessel modifiée d’ordre 0 <br>
$\beta$ : Paramètre de forme
***Caractéristiques spectrales de quelques fenêtres***
| Fenêtres | Largeur du 1er lobe | Atténuation du 1er lobe [dB] | Décroissance des lobes suivants | Ondulation Rp [dB] | Atténuation max [dB] | Bande de transition<br>[rad/sec]<br>$\Delta\omega$ |
|---------------------|---------------------|------------------------------|---------------------------------|--------------------|----------------------|----------------------------------------------------|
| Rectangulaire | $4\pi/N$ | $-13$ | $20 dB/dec$ | $0,74$ | $21$ | $1,8\pi/N$ |
| Triangulaire | $8\pi/N$ | $-27$ | $40 dB/dec$ | $0$ | $25$ | $6,1\pi/N$ |
| Cosinus | $8\pi/N$ | $-32$ | $60 dB/dec$ | $0,055$ | $44$ | $6,2\pi/N$ |
| Hamming | $8\pi/N$ | $-43$ | $20 dB/dec$ | $0,014$ | $53$ | $6,6\pi/N$ |
| Blackman | $12\pi/N$ | $-58$ | $60 dB/dec$ | $0,0017$ | $74$ | $11\pi/N$ |
| Kaiser $\beta=4,54$ | $7,2\pi/N$ | $-30$ | $20 dB/dec$ | $0,025$ | $50$ | $5,8\pi/N$ |
| Kaiser $\beta=5,66$ | $8,4\pi/N$ | $-42$ | $20 dB/dec$ | $0,009$ | $60$ | $7,2\pi/N$ |
### Comment calculer son filtre
![specification_de_filtre.svg](medias%2Fspecification_de_filtre.svg ':size=50%')
$H(\mathbf{j}\omega)$ : Gain <br>
$\delta_{1}$ : L'erreur de gain tolérable <br>
$\delta_{2}$ : L'erreur d'atténuation tolérable <br>
$\omega_{p}$ : Fréquence de pulsation de passage <br>
$\omega_{a}$ : Fréquence de pulsation d'arrêt <br>
1. En fonction du gabarit, déterminer le type de filtre désiré (`passe-bas`, `passe-haut`, `passe-bande`, `coupe-bande`)
2. Calculer $f_{c} \to \omega_{c}$ et $\Delta f \to \Delta\omega$
$$\begin{gather}
f_{c} = \frac{f_{a}+f_{p}}{2} \qquad & \qquad \Delta f = f_{a}-f_{p} \\
\omega_{c} = 2\pi \frac{f_{c}}{f_{s}} \quad & \quad \Delta\omega = \frac{2\pi\Delta f}{f_{s}}
\end{gather}$$
3. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation et du reste du cahier des charges.(voir tableau ci-dessus)
4. Calculer l'ordre du filtre à l'aide de la bande de transition du filtre choisit. Arrondir au nombre pair supérieur.
5. Calculer la réponse impulsionnelle $h_{f}[n]$ du filtre avec $h[n]*W[n]$
6. Calculer les coefficients $b_{n}$ du filtre avec $h_{f}[n]$
<details>
<summary>Démarche selon les slides</summary>
1. Connaissant le gabarit du filtre désiré, choisir le filtre idéal correspondant
2. Calculer les pulsations caractéristiques se situant au centre des bandes de transitions
3. Rechercher la réponse impulsionnelle $h[n]$ du filtre idéal, si celle-ci n’est pas connue, on peut la calculer par transformation de Fourier inverse
4. Choisir une fenêtre $W[n]$ satisfaisante du point de vue de l’atténuation
5. Connaissant la largeur de la bande de transition , calculer l’ordre du filtre $N$
6. Calculer les coefficients du filtre en multipliant la réponse impulsionnelle par la fenêtre choisie
</details>
----------
-9
View File
@@ -1,9 +0,0 @@
<header>
<h1>Électronique</h1>
</header>
# Convertisseurs
- [AD/DA](/infotro/Eln/ADDA)
- [Signaux et échantillonnage](/infotro/Eln/SignauxEtEchantillonnage)
- [Filtres](/infotro/Eln/Filtres)
- [RF](/infotro/Eln/RF)
-145
View File
@@ -1,145 +0,0 @@
<header><h1>
Radio Fréquence
</h1></header>
----------
# Skin effect
Le courant électrique circule principalement à la surface des conducteurs.
Pour un fil de cuivre rond, la résistance en continue est donnée par la formule suivante:
$$R_{DC} = \rho\frac{l}{\pi r²}$$
avec:
- $R_{DC}$: résistance en continue
- $\rho$: résistivité du cuivre
- $l$: longueur du fil
- $r$: rayon du fil
La résistance en courant alternatif est donnée par la formule suivante:
$$R_{AC} = R_{DC} \frac{r}{2\delta}$$
avec:
- $R_{AC}$: résistance en courant alternatif
- $R_{DC}$: résistance en continue
- $r$: rayon du fil
- $\delta$: profondeur de pénétration du courant
La profondeur de pénétration du courant est donnée par la formule suivante:
$$\delta = \sqrt{\frac{\rho}{\pi f \mu_0}}$$
avec:
- $\delta$: profondeur de pénétration du courant
- $\rho$: résistivité du cuivre
- $f$: fréquence
- $\mu_0$: perméabilité magnétique du vide $= 4\pi 10^{-7} H/m$
On peut en déduire la résistance en courant alternatif:
$$R_{AC} = \frac{l\sqrt{\rho\pi\mu_0}}{2\pi r} \sqrt{f} \qquad \qquad R_{AC(f_{2})} = R_{AC(f_{1})} \cdot \sqrt{ \frac{f_{2}}{f_{1}} }$$
Pour calculer l'inductance et la capacité d'un fil, on peut utiliser les formules suivantes :
$$\begin{align}
L&=\frac{\mu_{0}l}{\pi}acosh\left( \frac{d}{2r} \right) \\
C&=\frac{\pi\epsilon_{0}l}{acosh\left( \frac{d}{2r} \right)}
\end{align}$$
avec :
- $\mu_{0}$: perméabilité magnétique du vide $=4\pi\cdot 10^{-7}\left[ \frac{H}{m} \right]$
- $\epsilon_{0}$: permittivité du vide $=8.854\cdot 10^{-12}\left[ \frac{F}{m} \right]$
# Model à haute fréquence
## Inductance
![rf-inductor.png](medias%2Frf-inductor.png ':size=50%')
<u>Facteur de qualité :</u>
$$Q(f) = \frac{X_{L}(f)}{R(f)} \qquad \qquad R_{AC}(f)= \frac{l\sqrt{ \rho \pi \mu_{0} }}{2\pi r}\sqrt{ f}$$
<u>Réactance & Impédance:</u>
$$X_{L}=\omega L=2\pi fL \qquad \qquad Z_{L}=jX_{L}=j\omega L$$
<u>Résonance :</u> $\omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$ <br>
SRF: Self Resonant Frequency = $\frac{\omega_{r}}{2\pi}$
$$Z_{L,tot} = (j\omega L+R) \mid \mid \frac{1}{j\omega C} =\frac{sL+R}{1+sRC+s^{2}LC}$$
![rf-inductor.png](medias/rf-inductor-graph.png)
# Capacité
![rf-capacitor.png](medias%2Frf-capacitor.png ':size=50%')
<u>Facteur de qualité :</u>
$$Q(f) = \frac{X_{C}(f)}{R} \qquad \qquad \omega_{r} ≃\omega_{0}=\frac{1}{\sqrt{ LC }}$$
<u>Réactance & Impédance:</u>
$$X_{C}=\frac{1}{\omega C} \qquad \qquad Z_{C}=\frac{1}{j\omega C}$$
![rf-capacitor-graph.png](medias/rf-capacitor-graph.png)
# Résistance
![rf-resistor.png](medias%2Frf-resistor.png ':size=35%')
# Transmission line
$$ k = \frac{2\pi}{\lambda} \qquad \qquad \lambda = \frac{c_{mat}}{f} $$
$\Gamma$ (Gamma) : coefficient de réflexion
# Smith Chart
## Ajouter une ligne de réflexion
1. Placer $Z1$
1. Normaliser ($\hat{Z_{1}}=\frac{Z_{1}}{Z_{0}}$)
2. Placer le point au croisement de l'axe réel et imaginaire (Impédance = bleu)
2. Chercher $\Gamma$ (indice de reflexion)
1. Tracer l'angle ("Angle of reflexion coefficient in degrees")
2. Tracer le module (cercle au compas + rapport sur les échelles du bas)
3. Ajouter la ligne selon le sens
- $\circlearrowright$ : toward generator
- $\circlearrowleft$ : toward load
4. Lire les nouvelles valeures
1. Tracer le nouvel angle $\to \angle(\Gamma_{2})$
2. Tracer et lire $\hat{Z_{2}}$ puis $Z_{2}$
![add_line_on_smith_chart.png](medias/add_line_on_smith_chart.png)
# Impedance Matching
$Z_{LD} \to Z_S^*$, sans résistance
- Capacité + inductance
- ligne de transmission + capacité / inductance + ligne de transmission
- ligne de transmission + stub + ligne de transmission
-
![impedance_matching.png](medias%2Fimpedance_matching.png)
## Stub
Pour trouver la longueur du stub :
1. Pour un short stub, placer une inductance parallèle et pour un open stub un condensateur parallèle
2. Trouver la valeur normalisée de cette admittance.
3. Pour un short stub, placer le delta dans les négatifs (parti du dessus) et pour un open stub dans les positifs (partie du dessous).
4. Regarder la longueur d'onde. Partir du côté open circuit pour un open stub et du côté short circuit pour un short stub.
# Filtres
Filter components <br>
![Filters prototype](medias/rf-filters_prototype.png) <br>
![Filters components](medias/rf-filter_components.png)
Normalized value for Butterworth order 1 to 10 <br>
![Normalized value for Butterworth order 1 to 10](medias/rf-normalized_value-butterworth_1_10.png)
Normalized value for Chebyshev 0.5dB Ripple <br>
![Normalize value for Chebyshev 0.5 Ripple](medias/rf-normalized_value_chebyshev_ripple_0_5.png)
Normalized value for Chebyshev 3dB Ripple <br>
![Normalize value for Chebyshev 3 Ripple](medias/rf-normalized_value_chebyshev_ripple_3.png)
## Filtres passe-bas en microstrip
$l$ : longueur physique en mm <br>
$\theta$ : longueur électrique / degré <br>
$$ l_l = g_k * \frac{\lambda_{gl} * Z_0}{2\pi *Z_{0l}} $$
$$ l_c = g_k * \frac{\lambda_{gc} * Z_{0c}}{2\pi *Z_0} $$
$$ \theta_l = g_k * \frac{360 * Z_0}{2\pi *Z_{0l}} $$
$$ \theta_c = g_k * \frac{360* Z_{0c}}{2\pi *Z_0} $$
![rf-kuroda_identities.png](medias%2Frf-kuroda_identities.png)
@@ -1,256 +0,0 @@
<header><h1>
Signaux et échantillonnage
</h1></header>
-----------
# Fourier
Pour comprendre les signaux, il est important de comprendre la transformée de Fourier.
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
## Transformée de Fourier
La transformée de Fourier est une technique de traitement du signal utilisée pour analyser et manipuler les signaux continus.
Elle permet d'exprimer un signal dans le temps en domaine fréquentiel.
La transformée de Fourier est définie par la formule suivante :
Soit $f(x)$ une fonction non périodique définie sur $\mathbb{R}$.
$$\begin{align}
\text{Transformée de Fourier de }f(x) &: \qquad \widehat{f}(\omega) = \int^{\infty}_{-\infty}f(x)e^{-\mathbf{i}\omega x}dx \qquad (\omega\in \mathbb{R})\\
\text{Intégrale de Fourier de } f(x) &: \qquad \frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega) e^{\mathbf{i}x\omega}\, d\omega \qquad(x \in\mathbb{R}).
\end{align}$$
**Théorème** : Si $\int_{-\infty}^{\infty} |f(x) \, dx < \infty$ et si $f$ est une fonction de variation bornée sur tout $\mathbb{R}$, alors
$$
\frac{1}{2\pi}\int_{-\infty}^{\infty} \widehat{f}(\omega)e^{\mathbf{i}x\omega} \, d\omega = \begin{cases}
f(x) &\text{si }f \text{ est continue au point }x \\
\text{milieu du saut de } f(x) &\text{si }f \text{ a un saut en }x
\end{cases}
$$
## Transformée de Fourier discrète
La transformée de Fourier discrète (DFT - Discret Fourier Transform) est une transformation discrète utilisée pour analyser le spectre fréquentiel
d'un signal discret.
Elle est l'équivalent discret de la transformée de Fourier, qui est utilisée pour analyser les signaux continus.
On pose: $\Omega := \omega \cdot T_{s}$
La transformation de Fourier de $x[k]$ est donnée par la formule suivante :
$$\mathcal{F}\{x[k]\} = X(\Omega) \quad := \quad\sum^{+\infty}_{k=-\infty}x[k]\cdot e^{-jk\Omega}= \mathcal{F}\{x_{s}(t)\}$$
$$\mathcal{F\{x[k]\} = \mathcal{Z}\{x[k]\}}|_{z=e^{j\Omega}}$$
![fourier_cerlce_unitaire.svg](medias/fourier_cerlce_unitaire.svg)
$$x[k]=\frac{1}{2\pi} \int^{+\pi}_{-\pi}X(\Omega)e^{jk\Omega} d\Omega$$
La DFT est utilisée dans de nombreux domaines, notamment en traitement du signal, en télécommunications, en traitement d'images
et en compression de données.
> [!NOTE|label:FFT]
> La transformée de Fourier rapide (FFT) est un algorithme efficace pour calculer la DFT. Elle est largement utilisée dans
> de nombreuses applications, notamment en traitement du signal et en compression de données.
----------
# Réponse impulsionnelle
La réponse impulsionnelle $h[k]$ d'un système discret LIT est sa réponse à l'impulsion unitaire à l'origine $δ[k]$.
![Réponse impulsionelle - Continu/discret](medias/reponse_impulsionelle-continue_discret.png)
Avant l'impulsion, la réponse impulsionnelle $h$ est nulle.
Après l'impulsion, la réponse impulsionnelle $h$ est la réponse du système à l'impulsion unitaire à l'origine.
*Causal* = le futur n'a pas d'influence sur le passé.
$$ h[k] = \mathcal{H} \{ δ[k] \} $$
$\mathcal{H}$ : Heaviside (peut aussi se noter $\varepsilon$ pour un heaviside en discret) <br>
$y[k]$ : réponse du système <br>
$x[k]$ : signal d'entrée <br>
$h[k]$ : réponse impulsionnelle <br>
$$
\forall x \in \mathbb{R}, \mathcal{H}(x)=\begin{cases}
0 &\text{ si } x<0 \\
1 &\text{ si } x \geq 0
\end{cases}
$$
Pour un système causal
$$\begin{align}
y[k] &= \sum^{k}_{n=-\infty}(x[n]\cdot h[k-n]) \\
\\
y[k] &= \sum^{+\infty}_{n=0}(x[k-n]\cdot h[n])
\end{align}$$
La réponse impulsionnelle caractérise <u>ENTIÈREMENT</u> un système LIT.
## Système LIT stable
Un système LIT (Linéaire et Invariant dans le Temps) est stable si et seulement si sa réponse impulsionnelle est sommable.
> [!NOTE]
> $\sum^{+\infty}_{k=-\infty}|h[k]|<+\infty$
## Système LIT causal à réponse impulsionnelle finie
Un système LIT (Linéaire et Invariant dans le Temps) est causal à réponse impulsionnelle finie si et seulement si sa
réponse impulsionnelle est nulle pour $k<0$ et sommable pour $k\geq0$.
> [!NOTE]
> fini: $h(k) |_{k<\infty} = 0$
$$\begin{align}
y[k] &= \sum^{N}_{n=0}x[k-n]\cdot h[n] \\
&= b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]
\end{align}$$
$N$ : Limite de notre réponse impulsionnelle.
![schema_bloc_fir.png](medias/schema_bloc_fir.svg ':size=75%')
En faisant la somme, la réponse impulsionnelle se transforme en facteurs $(b_0, b_1, b_2, ..., b_n)$.
On peut retrouver la réponse impulsionnelle à partir de ces facteurs.
La somme décomposée est appelée *équation aux différences*.
Pour obtenir la réponse indicielle depuis la réponse impulsionnelle, on utilise la somme cumulée de la réponse impulsionnelle.
On peut aussi mathématiquement obtenir la réponse impulsionnelle en multipliant la réponse indicielle par Heaviside ($F(z)\cdot \varepsilon(z)$).
----------
# Transformée en Z
La transformée en Z, également connue sous le nom de transformée en fréquence Z, est une technique de traitement du
signal utilisée pour analyser et manipuler les signaux numériques.
Elle permet de décomposer un signal en fonction de la fréquence et de fournir des informations sur la nature du signal.
La principale différence entre la transformée en Z et la transformation de Laplace (ou S) est que la transformée en Z
est adaptée aux signaux discrets, c'est-à-dire aux valeurs qui ne varient pas continuellement dans le temps.
En revanche, la transformation de Laplace est utilisée pour les signaux continus et permet d'étudier leur réponse en
fréquence et en phase.
En résumé, la transformée en Z est conçue pour les signaux discrets et les systèmes numériques, tandis que la
transformation de Laplace s'applique aux signaux continus et aux systèmes analogiques.
[Tableau des transformées en Z](/infotro/Eln/Ztransform)
> [!NOTE|label:S]
> Domaine fréquentiel continue avec Laplace.
> [!NOTE|label:Z]
> Domaine fréquentiel discret.
Avec $z:=e^{sT_{s}}$ on a
$$\mathcal{Z}\{x[k]\} = X(z) \quad := \quad \sum^{+\infty}_{k=-\infty}(x[k]\cdot z^{-k})=\mathcal{L\{x_{s}(t)\}}$$
qui est la transformée en Z de $x[k]$.<br>
$T_{s}$ : Période d'échantillonnage. <br>
$x[k]$ : Fonction du [Tableau des transformées en Z](/infotro/Eln/Ztransform) ou valeur du graphe des réponses impulsionnelle<br>
On a aussi la propriété suivante :
$$\mathcal{Z}\{x[k-n]\}=z^{-n}\cdot X(z)$$
## Marche à suivre - Transformée en *Z*
1. Avoir une équation aux différences.
2. Pour chaque terme de l'équation, appliquer la transformée en Z.
Avec :
- $f[k] => F(z)$
- $f[k-n] => z^{-n}\cdot F(z)$
3. Isoler $Y(z)$.
4. $H(z) = \frac{Y(z)}{X(z)}$ est la fonction de transfert.
> [!NOTE|label:Example]
> L'équation aux différences est donnée par $$\tag{1} y[k] = \frac{1}{2}(x[k]+y[k-1])$$
> devient
> $$\begin{align}
\tag{2} Y(z) &= \frac{1}{2}(X(z)+z^{-1}\cdot Y(z)) \\
Y(z)-\frac{1}{2}\cdot z^{-1}\cdot Y(z) &= \frac{1}{2}\cdot X(z) \\
\tag{3} Y(z) &= \frac{1}{2-z^{-1}}X(z) \\
\tag{4} H(z) &= \frac{Y(z)}{X(z)} = \frac{1}{2-z^{-1}}
\end{align}$$
> [!TIP]
> $h[k]\stackrel{\text{Z tabel}}{\longleftrightarrow}H(z)$
>
> Exemple: $h[k] = 2^{-(k+1)}$ devient
> $$\begin{align}
2^{-(k+1)} &= 2^{-k}\cdot 2^{-1} = \frac{1}{2}\cdot 2^{-k} = \frac{1}{2}\cdot (2^{-1})^k \\
&\to \frac{1}{2}\cdot \frac{z}{z-\frac{1}{2}} =\frac{z}{2z-1} = \frac{1}{2-\frac{1}{z}} = \frac{1}{2-z^{-1}}
\end{align}$$
> [!WARNING]
> Les propriétés de la transformée en Z se trouvent dans le [Tableau des transformées en Z](/infotro/Eln/Ztransform)
L'équation aux différences
$$y[k] + a_{1}y[k-1]+\dots+a_{M}y[k-M] \quad = \quad b_{0}x[k]+b_{1}x[k-1]+\dots+b_{N}x[k-N]$$
devient par la transformation en z
$$H(z) = \frac{Y(z)}{X(z)} = \frac{b_{0}+b_{1}z^{-1}+\dots+b_{N}z^{-1}}{1+a_{1}z^{-1}+\dots+a_{M}z^{-M}} \quad a_{m}, b_{n} \space \text{réels}$$
Les zéros et les pôles de la fonction de transfert donnent donc :
$$H(z) = K \frac{(z-o_{1})\cdot (z-o_{2})\cdot \space \dots \space \cdot (z-o_{N})}{(z-p_{1})\cdot (z-p_{2})\cdot \space \dots \space \cdot (z-p_{M}) }\quad o_{n}, p_{n} \space \text{complexes}$$
## Marche à suivre - Transformée en *Z* inverse
1. Avoir une fonction de transfert : $H(z) = \frac{Y(z)}{X(z)}$.
2. Effectuer le numérateur et le dénominateur.
3. Mettre les $z$ en puissance négative : $\cdot \frac{z^{-1}}{z^{-1}}$
4. Effectuer le produit en croix.
5. Pour chaque terme, appliquer la transformée en Z inverse.
- $F(z) \implies f[k]$
- $F(z)\cdot z^{-n} \implies f[k-n]$
6. Isoler $y[k]$. Et on a notre équation aux différences.
----------
# Approximation bilinéaire
L'approximation bilinéaire est une technique utilisée pour convertir une fonction de transfert continue en une fonction
de transfert discrète.
Elle est utilisée dans le domaine du traitement du signal pour convertir un système continu en un système discret.
Développement en série de la fonction $ln$ autour de 1 ($x=0$) :
$$\ln\left( \frac{1+x}{1-x} \right) = 2\left(x + \frac{x^{3}}{3} + \frac{x^{5}}{5} + \dots \right)$$
donne l'approximation
$\ln\left( \frac{1+x}{1-x} \right) \approx 2x$
## Approximation bilinéaire - Pôles et zéros
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
- $k_a$, $p_{ai}$ and $z_{ai}$ sont le gain, les pôles et les zéros de notre filtre analogique
Attention, $k_a$ n'est pas le gain DC
- $k_d$, $p_{di}$ and $z_{di}$ sont le gain, des pôles et les zéros de notre filtre numérique.
Pour un filtre passe bas sans zéros, le gain numérique est:
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
On peut appliquer la transformation bilinéaire aux pôles et zéros du filtre analogique pour obtenir les pôles et zéros du filtre numérique.
$$
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
\qquad \qquad
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
$$
Pour un filtre passe bas, le gain "bilinear" est :
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
Pour passer de $\omega_o'$ à $\omega_o$ et vice-versa, on utilise la relation suivante :
$$\begin{align}
\omega_{0} &= 2f_{s}\tan\left( \frac{\omega_{0}'}{2f_{s}} \right) \\
\\
\omega_{0}'&=2f_{S}\cdot \arctan\left( \frac{\omega_{0}}{2fs} \right)
\end{align}$$
## Autres méthodes d'approximation d'un système continu LIT par un syst. discret
On détermine la réponse impulsionnelle du système discret LIT à partir de celle du système continu en appliquant la relation :
$$h[k] = T_{s}\cdot(kT_{s})$$
$h(kT_s)$ : Réponse impulsionnelle en continu <br>
On peut du coup approximer l'équation différentielle par une équation aux différences.
Par exemple pour la dérivée première de $x(t)$, on a
$$x'[k]= \frac{x[k]-x[k-1]}{T_{s}}$$
----------
-30
View File
@@ -1,30 +0,0 @@
<header><h1>
Tableau des transformées en Z
</h1></header>
-----------
$$f[k] =\varepsilon[x]=\begin{cases}
1^{x} &\text{ si } x\geq 0 \\
0 &\text{ si } x<0
\end{cases} \qquad \forall x=k,k-n
$$
$\varepsilon$ : fonction de Heaviside en discret
----------
| Nr. | $f[k]$ | $F(z)$ | $f(t)$ | $F(s)$ |
|------|-------------------|----------------------------------------|-------------|--------------------|
| 1 | $\delta_{k}, 0^k$ | 1 | $\delta(t)$ | 1 |
| 2 | $1^k$ | $\frac{z}{z-1}$ | $1$ | $\frac{1}{s}$ |
| 3 | $kT$ | $\frac{Tz}{z-1}^{2}$ | $t$ | $\frac{1}{s^{2}}$ |
| 4 | $(kT)^{2}$ | $\frac{T^{2}z(z+1)}{(z-1)^{3}}$ | $t^{2}$ | $\frac{2!}{s^{3}}$ |
| 5 | $(kT)^{3}$ | $\frac{T^{3}z(z^{2}+4z+1)}{(z-1)^{4}}$ | $t^{3}$ | $\frac{3!}{s^{4}}$ |
| 6 | $e^{akT}$ | $\frac{z}{z-e^{aT}}$ | $e^{at}$ | $\frac{1}{s-a}$ |
![ztabelle01.png](ztabelle01.png)
![ztabelle02.png](ztabelle02.png)
![ztabelle03.png](ztabelle03.png)
Dans la théorie Eln la partie transformée en Z commence à la slide 23 de "Eln2_LTI_0910_f.pdf".
@@ -1,133 +0,0 @@
<header><h1>
Lab 06 - Filters
</h1></header>
----------
In this laboratory on digital filters, the main objective is to understand how digital filters work.
We will use MATLAB to simulate and implement these filters on a digital signal processor (DSP).
# Analog filter design
We must design Butterworth low-pass filter that cuts at 1kHz as shown with the following equation :
$$F(s) = \frac{1}{\frac{s^{2}}{\omega_c^{2}} + s\cdot\frac{1.414}{\omega_c} + 1} $$
In according to the sample theory, we have to choose at least 2 times the maximum frequency of the signal.
In this case, the maximum frequency is 1 kHz, so we have to choose a sample frequency at least at 2 kHz.
We take 10 time $f_c$ for the sample frequency, so we choose 20 kHz.
## Transfer function
For calculate the transfer function, we have to calculate the poles and zeros of the filter.
$$p_{di} = e^{p_{ai} \cdot T} \qquad z_{di} = e^{z_{ai} \cdot T} $$
- $k_a$, $p_{ai}$ and $z_{ai}$ are the gain, poles and zeros of the analog filter that the representation *zpk*.
Be careful, $k_a$ is not the DC gain.
- $k_d$, $p_{di}$ and $z_{di}$ are the gain, poles and zeros of the digital filter.
For a low pass filter without zeros, the "digital" gain is :
$$k_{d} = k_{a}\cdot \prod_{i} \frac{1-e^{P_{ai}\cdot T}}{P_{ai}}$$
We can apply the bilinear transformation to the poles and zeros of the analog filter to obtain the poles and zeros of the digital filter.
$$
p_{di} = \frac{1+p_{ai}\cdot \frac{T}{2}}{1-p_{ai}\cdot \frac{T}{2}}
\qquad \qquad
z_{di} = \frac{1+z_{ai}\cdot \frac{T}{2}}{1-z_{ai}\cdot \frac{T}{2}}
$$
And for the low pass filter, the "bilinear" gain is :
$$k_{d} = k_{a}\cdot \frac{1}{\prod_{i}\left( \frac{2}{T}-p_{ai} \right)}$$
On the following diagram, we can see the bode diagram of the analog, digital filter and the filter with the bilinear transformation.
![bode_design_filter.svg](bode_design_filter.svg)
# IIR filter design
We must design an IIR filter with the following specifications :
- $H_0 = 0 [dB]$
- $H_1 = -3 [dB]$
- $H_2 = -40 [dB]$
- $f_{pass} = 7 [kHz]$
- $f_{stop} = 9 [kHz]$
- $f_s = 48 [kHz]$
We first calculate the analog filter for search the order with the function *ellipord*.
Then, we calculate the poles and zeros with the function *ellip*.
We apply the bilinear transformation to the poles and zeros to obtain the poles and zeros of the digital filter.
Finally, we can plot the bode diagram of the digital filter and the filter with the bilinear transformation.
![iir_filter.svg](iir_filter.svg)
# DSP implementation
We will implement the previously designed digital filter using a C program that will run in real-time on a fixed-point DSP.
We will employ a cascade structure, consisting of 2 second-order stages.
We will use the TMS320C6211 DSK from Texas Instruments.
The following equation represent an universal transfer function of a second-order filter :
$$H(z) = \frac{b_{0}+b_{1}\cdot z^{-1}+b_{2}\cdot z^{-2}}{a_{0}+a_{1}\cdot z^{-1}+a_{2}\cdot z^{-2}}$$
The following figure depicts the block diagram of this implementation in the case where a0 = 1
This structure is commonly referred to as "Direct Form I." It requires the storage of past values of both the input and output.
![direct_form_1.png](direct_form_1.svg)
So we have the difference equation :
$$y(n) = b_{0}\cdot x(n)+b_{1}\cdot x(n-1)+b_{2}\cdot x(n-2)-a_{1}\cdot y(n-1)-a_{2}\cdot y(n-2)$$
It exists a second structure called "Direct Form II" which is more efficient in terms of computation and memory usage.
![direct_form_2.png](direct_form_2.svg)
The difference equation is :
$$\begin{align}
y(n) &= tmp\cdot b_{0}+w[0]\cdot b_{1}+w[1]\cdot b_{2} \\
\\
tmp &= x(n)-a_{1}\cdot w[0]-a_{2}\cdot w[1] \\
\\
w[0] &= tmp_{prev} \\
\\
w[1] &= w[0]_{prev}
\end{align}$$
The variables can be translated into code by shifting the coefficients 14 positions to the left to enhance precision
after the decimal point, as the DSP only works with integers.
Structures for the coefficients and the state variables are defined as follows :
```c
typedef struct {
short b0;
short b1;
short b2;
short a0;
short a1;
short a2;
} coeff_sec_order;
short w3[2];
short w4[2];
coeff_sec_order stage1 = { 469, 349, 469, 16384, -23699, 10692};
coeff_sec_order stage2 = {16384, -11187, 16384, 16384, -19657, 14890};
```
The following code is the implementation of the Direct Form II function :
```c
void iir_sec_order_dir_s(coeff_sec_order Coeff, short*w, short* Input, short* Output) {
register short tmp;
tmp = (long)* Input - (( (Long)Coeff.a1 * (long)w[0]) >> SHIFT) - (( (long)Coeff.a2 * (long)w[1]) >> SHIFT);
*Output = (( (long)Coeff.b0 * (long)tmp >> SHIFT) + (( (long)Coeff.b1 * (long)w[0]) >> SHIFT) + (( (long)Coeff.b2 * (long)w[1]) >> SHIFT);
w[1] = w[0];
w[0] = tmp;
}
```
For a better effect of the filter, the function can be called several times.
File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 78 KiB

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="840px" height="286px" viewBox="-0.5 -0.5 840 286" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-03T22:03:26.442Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;Da41eLOf3AT8AQXfy7Nj&quot; version=&quot;22.1.22&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;CkdF-7UHB_tb0CRjxC_V&quot;&gt;7V1bc6M2FP41nmkfkkHcbB7XTtLtTDrTaWbb5mkHxzKmxcjFcmLvr68wkgGJq0EGOyQPQQddz/k4+nQkyEibrfe/BPZm9RtaQG+kKov9SHsYqSrQNZX8CSWHSDI2lEjgBO6CZooFL+4PSIUs285dwG0qI0bIw+4mLXxDvg/fcEpmBwH6SGdbIi/d6sZ2oCB4ebM9UfqXu8ArKjUVJb7xFbrOijZtsRtr+5Q5EmxX9gJ9RKJjHu1xpM0ChHB0td7PoBcqj+klqugp5+6pYwH0cZUCT8D2/dmvz1N4mKu+M3/5tvzzzqB9wwc2YLgg46dJFOAVcpBve4+xdBqgnb+AYa0KScV5nhHaECEgwn8gxgdqTHuHERGt8Nqjd7c4QP/CGfJQcGxTmyjh7+kOUzRR0TTqX9ip3GEz/aJd8AYLxkqRiO3Agbggn34yDkE1RGuIgwMpF0DPxu57uh82hZdzyhdbgFxQI9QwCO3ku+3taEsj1fRId6dLRAZMEGzTQZr/7ULoTL/Ndz7exWly5dC/x3LzWEBGpv8YGY+j8fQOjMYPkYRlJP2d84WJLGqXiTm0YLjHhRZdup7H5D7yQ/zYnuv4JPlGLAiJfPoOA+ySR+4LvbF2F4sj0j5WLoYvm2jEH8TBCOhLoCrsZzVMhTkpOIHB0nRMCk0/2WvXC30WVS8be9hXuC/GoogdWmDCnAN1hhp9ZJWP2LXoVLRKOBUmax1t+oC2z4M2Xe8YbVYG2kJIzL8rDBu8wQPX9p1c6yTtIVo+HyYCJnKtSdpzfYdakqb+oKoCRTNWI8tZatpyqtmx5RgbE0x3Z38H12i744PnuZuvjJJEt5/hUq5lwclGbAqYdG1aIFiuZxQQ+osvIZOP0UEkT244zGPzRPXB4e9k4jVM3Bss+bBP3nw40FR1allKGa2GlJEW/R25x1mXOXDeDUw4EESUl5ZKUn++IpOrSOMqigYoVETUbh8S2TZhhm1Bh/XsDuf2qzg/uYh6EKP7pNwGgFf7D3gG6BjDrykISwe0KgXQ47YAPba4iixJgM7psFyAai2y8nBq3v/k/yyQ7/pEmzrfgWMn5nNz0jemlrWki0j2VRK1y5Hs7plY74NxXTMx6QxLs6z7yXlTkgAnsSpJLCtuqSrPEkpImsjMXF+kDr6oyBcZSte+aJxhOs5WDvE9m9zB0w0Ze86yK3WVApS+eegsPPMe2/PczbYCfu3tJtqvWrr70H/XA/SZKCywtWiGrrRcAXriRJTQbHpKbHmz6VwNGhkaZLKGkxj/lJy5puKqAVw1OZNXW3PFZDB6LaNraWupZxodFFcj2ehgIhr5czBeuHdxIsJDUq+0d+F1XChM1A/vlG6FN93irsamDWDdj83zgCkwooy6JAV5jPRBCknc2Oo/9JtBGySAHcNcOrRZAKQ0xGlKeQasSQ5nrR2z5zy8blzWN7OH4hoAmg6Tg04B2hXuzPZ8r7DwquF8W4MfKCeEF1+Adh7dZUq4sRVonh06U3PWDky/VyMVVDgsQUusnrV1Mlg93+o3sQZVzVvnOek1ptISzykl4mZFPhQ9Za0TIqDwmx48ga6MT8Af2700FR9fD0T7s1aUTrHVcYsUO6sy2bCqEHOVTrFB385DqlmnlG+AYk9KmcJF1cy4wzWRrXIVDhS7xOoVVvWD1W+NYms3H+tW2uUvhdN1eQRckUJ/7njXYd2bphL/qMZ52NTrVSsbqxUibBcnRZ3HHbUqAbHrI0VaztKzMzVfXwSqggoHUlRidWOw+ickRVcUd+xPUKdCPDGaqyTEE9sM/4Dzwz91j7eIbZW8lVdWQM6BGC3r2Cd9xVi9xtPivXnFuPPD5NoVHPNruMUixdWVrvS0nCOojV0dd4ip0WESwPlN/cITLYP1DaNPwgZf+6ACRriyT0Hh7MNxmZUpF8ZV7z+c0KKn0dWK7Etvyr6afc8oK3ozfNAowYmu+WVroHFTSfffz2rzVf0Bb33HW+df0NLFNVDP5pybe0UcqPyJfiumHrV5i1ahsrbW/Rp/bCluK79/pWXkrP712/n2AagHbLlvgpVHyyQtIXmg62dTfb3CYrS1R4Zv6xKvgun9jwR3+0qXrIgu79nP/hyV6NYrxjgaA/QioVm99+dPu2MHeQZp7ueEmtrCkD4W9x6skr6VFZEEvKwTqk2+hHYYvoQmbbEk4FfeoRWSjL+bH4Et/u8D2uP/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&#xa;</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"][width="full"] {&#xa; display: flex;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="left"] {&#xa; text-align: left;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="right"] {&#xa; text-align: right;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; g {&#xa; fill: red;&#xa; stroke: red;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; rect[data-background] {&#xa; fill: yellow;&#xa; stroke: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; line[data-line], svg[data-table] &gt; g &gt; line[data-line] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; rect[data-frame], svg[data-table] &gt; g &gt; rect[data-frame] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dashed, svg[data-table] &gt; g &gt; .mjx-dashed {&#xa; stroke-dasharray: 140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dotted, svg[data-table] &gt; g &gt; .mjx-dotted {&#xa; stroke-linecap: round;&#xa; stroke-dasharray: 0,140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; g &gt; svg {&#xa; overflow: visible;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool {&#xa; display: inline-block;&#xa; position: relative;&#xa; width: 0;&#xa; height: 0;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool &gt; mjx-tip {&#xa; position: absolute;&#xa; top: 0;&#xa; left: 0;&#xa;}&#xa;&#xa;mjx-tool &gt; mjx-tip {&#xa; display: inline-block;&#xa; padding: .2em;&#xa; border: 1px solid #888;&#xa; font-size: 70%;&#xa; background-color: #F8F8F8;&#xa; color: black;&#xa; box-shadow: 2px 2px 5px #AAAAAA;&#xa;}&#xa;&#xa;g[data-mml-node="maction"][data-toggle] {&#xa; cursor: pLine truncated

Before

Width:  |  Height:  |  Size: 82 KiB

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="520px" height="366px" viewBox="-0.5 -0.5 520 366" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-03T22:16:47.416Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;gL6vpL3MQfkpyT-y1ADy&quot; version=&quot;22.1.22&quot;&gt;&lt;diagram name=&quot;Page-1&quot; id=&quot;CkdF-7UHB_tb0CRjxC_V&quot;&gt;7Vxtj6M2EP41SO2HXfFmEj5esru9Slup6up6t59OJPESWoJTQjbJ/fqa2A7BQDAsDpBDd7rDg23MPI/H4/EQxZiu9r+Fznr5B1pAX9HVxV4xHhRdt8dj/G8sOBAB0EZE4Ibegoi0RPDi/YBUqFLp1lvATapihJAfeeu0cI6CAM6jlMwJQ7RLV3tDfvqpa8eFGcHL3PGz0q/eIlpSqaWqyY3P0HOX9NE2u7FyTpWJYLN0FmhHRMc6xqNiTEOEInK12k+hH+uO6YV09FRw9zSwEAaRSIMnzQmC6e/PE3iY6YE7e/ny9vedTnp5d/wtfWFFt3zc3+QN4W6xwpw5uWH9t41HOvky2wbRNinjK5f+f2w3SwQm/vtDAY/KaHKnKaMHImEV8UhnfGMsI89l4qPiogNDI4L7WL6JQvQvnCIfhUe5MVbjP/GoPd9n8gAFuNnE8T03wMU51hPE8sk7DCMPI/yJ3lh5i0X8gMlu6UXwZU3eeIfpjGUh2gYLGKsw7n4ZrXx8qVH95I2AjI1xRac1KbE1wMr0nVRafnJWnh9PEape9u7xWOG+EHHtxCM8/yBawSg84CqsgUopodLJZ7DyLqGySUXLMxIzmUMnj3vqOuEXvqAUq0A3c6DbT0Q302qZbnYO3WJOzL6rjBw84qHnBG4hPOeAZKEv5kmGFIVw4ud5gUuhpKW/qKq0fMAbgW6chk5v21Kw9T+D3Z3zXesjeMep53vrz3QI9PYzfJMLrW1xa8C4bWS1DHBwgX0uWkRhtEQuChz/MZFydjGp84zQmir0HxhFB2r4nG2E0ogXw5vVOgwWn2LXMSEHljx58WseH481Hx6+nRde48I9YMWH/fnNhwMtkfeOX/YyltjFdUIXRmWWLYt5CH0n8t7T/echSJv+ibzjsnsyAyZnBngWbNA2nEPa7NzZzPRkpHvSbK4n8oqZnrDincNZtXVcYXNpyFb+kItHdrkBviBjSBh+UvAHSK93n/T1SA33XvSNPi++PmuFS0mjuFB9IhC+XVKsIThjdF3KlBnpDc2YMe+rWx+dMCkr3QSHjQa993gB3/8S/Jpx0qs75NRGD7742ao/6pw/l7f1I754L925K/ri7XtsViF2+oDdReyA2TZ2oxzsOLBcbOnWhW9PI6vOjFVXK2tF69oeJI/QvHvm483aRoDAzmZNAs9v3j5eLaoxuiYNL2CdhaEtLQtQL7vVOtNsegGursOLDmZdDYIcDTLZB31J3gOs6Uly3WhifmRTXuJ4AL0S6Nw+Wa8Juna5G8mgs9duc5UZd83lZTq4sVWmCIbW1Jy3Le22xRFQ4bDMlKCet58cUL/xdcYQMKnXXmda38wYIgawf+sMwbo764zRP4sjoMJhnSlBHQyo/4TrTF7ogmYf6H0MeXYl+6D1eKjZ/ewDeqCaHKK+KmdnqLkHqsnhbXJe+0qf3UxGQulBrNHyQaxm8H4Zf4AqbHr4nkyejZKtj5k1Ll3jqKQMmZaTCZjZL+UwORFrnsM6z2Hbvreshmic11ljWThc4hCwyrJwLjeQk4VjWt2fWPUM+WnR6PjEIauEhImjcv7iuCnjbwi6nlWnjK22MQFGnZ8ANVYA+fmYpRMA2IITwBxLmgCAmwC1MzeNsp5aT0Qz884YP5KIdhgS0aR9FKLz6bZtH8uZ9m3bwLTjkFg96TaQRcbKd4ByTKDKraiGWjv2xKfBj667AWQWuPsUTQcptM5TVHSVlsRQLjhqNsZQk4+uy2ZoThS8Ywy9zRAFi4uXhyjkcHjMx/dBTQpn0nX4jhoLTYD8SVc8ty43kLMzA9lD3J0CJqoCHgbns346NmcmRc/q+T1/Y84nyB4ixzBrA8xNwix6yiQP5uypcbRaDwg3hrDoLlIewv0JJZ9ix9UcnJZCycIOjqRQsj3imMZ/uCzq4PAdGdfeRHY/1lvLRRc+CqmwNW2Qv6YgfYGsPSaXOQN42onvMe2SnqQdHoKqh4fgGh66wJctV/+SrvVoKjv4uLHkU1BwTNPaB4uqgJq7lYYooMIh+bQEdYHPqgbUby351OpBbLWfsVNR30xSdpfGR6PqumY2f45wbbtk9Ieh9fKo2mKo1TJDDd6xtu9ZtkVlI6qNyjuTTdQeZNL20pSKpjsBST9Up/E/u1U3SpPdUQn+UF1jFAWdp2g/P0gQNaWSKGpzTqRRO47Ie6ONxRFxMfn5cVI9+Q134/F/&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);&#xa;</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"][width="full"] {&#xa; display: flex;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="left"] {&#xa; text-align: left;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][justify="right"] {&#xa; text-align: right;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; g {&#xa; fill: red;&#xa; stroke: red;&#xa;}&#xa;&#xa;g[data-mml-node="merror"] &gt; rect[data-background] {&#xa; fill: yellow;&#xa; stroke: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; line[data-line], svg[data-table] &gt; g &gt; line[data-line] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; rect[data-frame], svg[data-table] &gt; g &gt; rect[data-frame] {&#xa; stroke-width: 70px;&#xa; fill: none;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dashed, svg[data-table] &gt; g &gt; .mjx-dashed {&#xa; stroke-dasharray: 140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; .mjx-dotted, svg[data-table] &gt; g &gt; .mjx-dotted {&#xa; stroke-linecap: round;&#xa; stroke-dasharray: 0,140;&#xa;}&#xa;&#xa;g[data-mml-node="mtable"] &gt; g &gt; svg {&#xa; overflow: visible;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool {&#xa; display: inline-block;&#xa; position: relative;&#xa; width: 0;&#xa; height: 0;&#xa;}&#xa;&#xa;[jax="SVG"] mjx-tool &gt; mjx-tip {&#xa; position: absolute;&#xa; top: 0;&#xa; left: 0;&#xa;}&#xa;&#xa;mjx-tool &gt; mjx-tip {&#xa; display: inline-block;&#xa; padding: .2em;&#xa; border: 1px solid #888;&#xa; font-size: 70%;&#xa; background-color: #F8F8F8;&#xa; color: black;&#xa; box-shadow: 2px 2px 5px #AAAAAA;&#xa;}&#xa;&#xa;g[data-mml-node="maction"][data-toggle] {&#xa; cursor: pLine truncated

Before

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because it is too large. Load diff

Before

Width:  |  Height:  |  Size: 72 KiB

Whitespace-only changes.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Do not edit this file with editors other than draw.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="289px" height="262px" viewBox="-0.5 -0.5 289 262" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2024-02-02T17:56:41.416Z&quot; agent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0&quot; etag=&quot;i95P2xGId2tSeVqMwiar&quot; version=&quot;23.1.1&quot; scale=&quot;1&quot; border=&quot;0&quot;&gt;&#xA; &lt;diagram name=&quot;Page-1&quot; id=&quot;ASSYr2psEjrnozlwjsYB&quot;&gt;&#xA; &lt;mxGraphModel dx=&quot;1050&quot; dy=&quot;626&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;827&quot; pageHeight=&quot;1169&quot; math=&quot;1&quot; shadow=&quot;0&quot;&gt;&#xA; &lt;root&gt;&#xA; &lt;mxCell id=&quot;0&quot; /&gt;&#xA; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot; /&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-1&quot; value=&quot;&quot; style=&quot;ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;360&quot; y=&quot;280&quot; width=&quot;160&quot; height=&quot;160&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-2&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#xA; &lt;mxPoint x=&quot;439.5&quot; y=&quot;480&quot; as=&quot;sourcePoint&quot; /&gt;&#xA; &lt;mxPoint x=&quot;440&quot; y=&quot;240&quot; as=&quot;targetPoint&quot; /&gt;&#xA; &lt;/mxGeometry&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-3&quot; value=&quot;&quot; style=&quot;endArrow=classic;html=1;rounded=0;strokeColor=#808080;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry width=&quot;50&quot; height=&quot;50&quot; relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#xA; &lt;mxPoint x=&quot;320&quot; y=&quot;360&quot; as=&quot;sourcePoint&quot; /&gt;&#xA; &lt;mxPoint x=&quot;560&quot; y=&quot;360&quot; as=&quot;targetPoint&quot; /&gt;&#xA; &lt;/mxGeometry&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-4&quot; value=&quot;1&quot; style=&quot;text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;520&quot; y=&quot;320&quot; width=&quot;40&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-5&quot; value=&quot;1&quot; style=&quot;text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;440&quot; y=&quot;240&quot; width=&quot;40&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-7&quot; value=&quot;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;Z&amp;lt;/div&amp;gt;&quot; style=&quot;text;strokeColor=#808080;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;530&quot; y=&quot;240&quot; width=&quot;30&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;mxCell id=&quot;hvOSe9N11qCTzQnM01ZF-8&quot; value=&quot;$$\mathcal{Z} = e^{\mathbf{j}\omega}$$&quot; style=&quot;text;strokeColor=none;fillColor=none;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;html=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#xA; &lt;mxGeometry x=&quot;280&quot; y=&quot;240&quot; width=&quot;120&quot; height=&quot;40&quot; as=&quot;geometry&quot; /&gt;&#xA; &lt;/mxCell&gt;&#xA; &lt;/root&gt;&#xA; &lt;/mxGraphModel&gt;&#xA; &lt;/diagram&gt;&#xA;&lt;/mxfile&gt;&#xA;"><defs><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">&#xa;mjx-container[jax="SVG"] {&#xa; direction: ltr;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg {&#xa; overflow: visible;&#xa; min-height: 1px;&#xa; min-width: 1px;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"] &gt; svg a {&#xa; fill: blue;&#xa; stroke: blue;&#xa;}&#xa;&#xa;mjx-container[jax="SVG"][display="true"] {&#xa; display: block;&#xa; text-align: center;&#xa; margin: 1em 0;&#xa;}&#xa;&#xa;Line truncated

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Loaded 100 of 392 files, more files were not shown because too many files have changed in this diff. Show more