Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbc7e0cfd9 |
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -1,12 +0,0 @@
|
||||
# Generated site and pinned toolchain
|
||||
public/
|
||||
/tools/
|
||||
|
||||
# Calepin build artifacts
|
||||
.calepin/
|
||||
.calepin-entry.*
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.zed/
|
||||
.vscode/
|
||||
@@ -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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -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&§ions.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>
|
||||
@@ -1,6 +0,0 @@
|
||||
<header><h1>
|
||||
Embedded Systems
|
||||
</h1></header>
|
||||
|
||||
----------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||

|
||||
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 -->
|
||||
|
||||
|
Before Width: | Height: | Size: 88 KiB |
@@ -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"
|
||||
```
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<header><h1>
|
||||
Working environment at HES-SO
|
||||
</h1></header>
|
||||
|
||||
- [OneDrive - Linux](HES-SO/OneDrive.md)
|
||||
- [Teams](HES-SO/Teams.md)
|
||||
@@ -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>
|
||||
 <br>
|
||||

|
||||
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>
|
||||
 <br>
|
||||

|
||||
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}"
|
||||
```
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
@@ -1,4 +0,0 @@
|
||||
# Master of science in engineering in embedded system
|
||||
|
||||
## [SRE - Software Reverse Engineering](/MSE/SRE/)
|
||||
- [Tools](/MSE/SRE/tools.md)
|
||||
@@ -1,5 +0,0 @@
|
||||
<header><h1>
|
||||
SRE - Software Reverse Engineering
|
||||
</h1></header>
|
||||
|
||||
- [Tools](/MSE/SRE/tools.md)
|
||||
@@ -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
|
||||
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
* [SRE - Software Reverse Engineering](/MSE/SRE/)
|
||||
* [Tools](/MSE/SRE/tools.md)
|
||||
@@ -1,3 +0,0 @@
|
||||
<header><h1>
|
||||
Security
|
||||
</h1></header>
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
```
|
||||
|
||||
-----------
|
||||
|
||||

|
||||
|
||||
-----------
|
||||
|
||||
[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
|
||||
|
||||

|
||||
|
||||
-----------
|
||||
|
||||
## 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>
|
||||
```
|
||||
|
Before Width: | Height: | Size: 43 KiB |
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
# docsify-example-panels
|
||||
|
||||
[](https://www.npmjs.com/package/docsify-example-panels)
|
||||
[](https://github.com/docsifyjs/awesome-docsify#plugins)
|
||||
[](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)
|
||||
[](https://github.com/VagnerDomingues/docsify-example-panels/blob/master/LICENSE)
|
||||
[](https://www.jsdelivr.com/package/npm/docsify-example-panels)
|
||||
[](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
|
||||
<!-- panels:start -->
|
||||
|
||||
(...) - Awesome div wrapper
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- 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
|
||||
<!-- panels:start -->
|
||||
<!-- div:title-panel -->
|
||||
|
||||
(...) - Awesome title
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
(...) - Awesome explanation
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
|
||||
(...) - Awesome example
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- div:left-panel -->
|
||||
|
||||
3. You can add a custom class aswell.
|
||||
|
||||
perphaps you want something like a "floating-cat" class
|
||||
|
||||
<!-- div:right-panel -->
|
||||
|
||||
```markdown
|
||||
<!-- panels:start -->
|
||||
<!-- div:your-custom-class -->
|
||||
|
||||
...
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- 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
|
||||
<!-- panels:start -->
|
||||
<!-- div:floating-cat -->
|
||||
|
||||
<img src="path/to/floating-cat.png">
|
||||
|
||||
<!-- panels:end -->
|
||||
```
|
||||
|
||||
<!-- 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>
|
||||
@@ -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>
|
||||
@@ -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 - 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 pesée
|
||||
Un switch par bit.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
----------
|
||||
@@ -1,339 +0,0 @@
|
||||
<header><h1>
|
||||
Filtres
|
||||
</h1></header>
|
||||
|
||||
-----------
|
||||
|
||||
|
||||
# Diagramme de Bode
|
||||
|
||||
## H0 - Gain statique
|
||||
$$H_{0}(s)=A$$
|
||||

|
||||
|
||||
## H1 - Coupe bas, amplis haut
|
||||
$$H_{1}(s)=\frac{s}{\omega_{0}}$$
|
||||

|
||||
|
||||
## H2 - Ampli haut, coupe bas
|
||||
$$H_{2}(s)=\frac{\omega_{0}}{s}$$
|
||||

|
||||
|
||||
## H3 - Ampli haut
|
||||
$$H_{3}(s)=\frac{\omega_{0}+1}{\omega_{0}} = 1+ \frac{s}{\omega_{0}}$$
|
||||

|
||||
|
||||
## H4 - Coupe haut
|
||||
$$H_{4}(s)=\frac{\omega_{0}}{\omega_{0}+s} = \frac{1}{1+\frac{s}{\omega_{0}}}$$
|
||||

|
||||
|
||||
## Résonance
|
||||

|
||||
|
||||
----------
|
||||
|
||||
# Filtres numériques
|
||||
|
||||

|
||||
|
||||
## 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}$$
|
||||
|
||||

|
||||

|
||||
|
||||
### 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
|
||||
|
||||

|
||||
|
||||
$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>
|
||||
|
||||
|
||||
|
||||
|
||||
----------
|
||||
@@ -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)
|
||||
@@ -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
|
||||

|
||||
|
||||
<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}$$
|
||||
|
||||

|
||||
|
||||
# Capacité
|
||||

|
||||
|
||||
<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}$$
|
||||
|
||||

|
||||
|
||||
# Résistance
|
||||

|
||||
|
||||
|
||||
# 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}$
|
||||
|
||||

|
||||
|
||||
# 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
|
||||
-
|
||||

|
||||
|
||||
## 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>
|
||||
 <br>
|
||||

|
||||
|
||||
Normalized value for Butterworth order 1 to 10 <br>
|
||||

|
||||
|
||||
Normalized value for Chebyshev 0.5dB Ripple <br>
|
||||

|
||||
|
||||
Normalized value for Chebyshev 3dB Ripple <br>
|
||||

|
||||
|
||||
## 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} $$
|
||||
|
||||

|
||||
|
||||
@@ -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}}$$
|
||||

|
||||
$$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]$.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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}}$$
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
----------
|
||||
@@ -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}$ |
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
|
||||
# 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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
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="<mxfile host="app.diagrams.net" modified="2024-02-03T22:03:26.442Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="Da41eLOf3AT8AQXfy7Nj" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">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/</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 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="<mxfile host="app.diagrams.net" modified="2024-02-03T22:16:47.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="gL6vpL3MQfkpyT-y1ADy" version="22.1.22"><diagram name="Page-1" id="CkdF-7UHB_tb0CRjxC_V">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/</diagram></mxfile>"><defs><style type="text/css">@import url(https://fonts.googleapis.com/css2?family=Architects+Daughter:wght@400;500);
</style><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

mjx-container[jax="SVG"][display="true"][width="full"] {
 display: flex;
}

mjx-container[jax="SVG"][justify="left"] {
 text-align: left;
}

mjx-container[jax="SVG"][justify="right"] {
 text-align: right;
}

g[data-mml-node="merror"] > g {
 fill: red;
 stroke: red;
}

g[data-mml-node="merror"] > rect[data-background] {
 fill: yellow;
 stroke: none;
}

g[data-mml-node="mtable"] > line[data-line], svg[data-table] > g > line[data-line] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > rect[data-frame], svg[data-table] > g > rect[data-frame] {
 stroke-width: 70px;
 fill: none;
}

g[data-mml-node="mtable"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed {
 stroke-dasharray: 140;
}

g[data-mml-node="mtable"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted {
 stroke-linecap: round;
 stroke-dasharray: 0,140;
}

g[data-mml-node="mtable"] > g > svg {
 overflow: visible;
}

[jax="SVG"] mjx-tool {
 display: inline-block;
 position: relative;
 width: 0;
 height: 0;
}

[jax="SVG"] mjx-tool > mjx-tip {
 position: absolute;
 top: 0;
 left: 0;
}

mjx-tool > mjx-tip {
 display: inline-block;
 padding: .2em;
 border: 1px solid #888;
 font-size: 70%;
 background-color: #F8F8F8;
 color: black;
 box-shadow: 2px 2px 5px #AAAAAA;
}

g[data-mml-node="maction"][data-toggle] {
 cursor: pLine truncated
|
||||
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
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="<mxfile host="app.diagrams.net" modified="2024-02-02T17:56:41.416Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0" etag="i95P2xGId2tSeVqMwiar" version="23.1.1" scale="1" border="0">
 <diagram name="Page-1" id="ASSYr2psEjrnozlwjsYB">
 <mxGraphModel dx="1050" dy="626" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="1" shadow="0">
 <root>
 <mxCell id="0" />
 <mxCell id="1" parent="0" />
 <mxCell id="hvOSe9N11qCTzQnM01ZF-1" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=none;strokeColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="360" y="280" width="160" height="160" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-2" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="439.5" y="480" as="sourcePoint" />
 <mxPoint x="440" y="240" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-3" value="" style="endArrow=classic;html=1;rounded=0;strokeColor=#808080;" edge="1" parent="1">
 <mxGeometry width="50" height="50" relative="1" as="geometry">
 <mxPoint x="320" y="360" as="sourcePoint" />
 <mxPoint x="560" y="360" as="targetPoint" />
 </mxGeometry>
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-4" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="520" y="320" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-5" value="1" style="text;strokeColor=none;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=bottom;align=left;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="440" y="240" width="40" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-7" value="&lt;div align=&quot;center&quot;&gt;Z&lt;/div&gt;" style="text;strokeColor=#808080;fillColor=none;html=1;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;" vertex="1" parent="1">
 <mxGeometry x="530" y="240" width="30" height="40" as="geometry" />
 </mxCell>
 <mxCell id="hvOSe9N11qCTzQnM01ZF-8" value="$$\mathcal{Z} = e^{\mathbf{j}\omega}$$" style="text;strokeColor=none;fillColor=none;fontSize=20;fontStyle=1;verticalAlign=middle;align=center;fontColor=#808080;html=1;" vertex="1" parent="1">
 <mxGeometry x="280" y="240" width="120" height="40" as="geometry" />
 </mxCell>
 </root>
 </mxGraphModel>
 </diagram>
</mxfile>
"><defs><style xmlns="http://www.w3.org/1999/xhtml" id="MJX-SVG-styles">
mjx-container[jax="SVG"] {
 direction: ltr;
}

mjx-container[jax="SVG"] > svg {
 overflow: visible;
 min-height: 1px;
 min-width: 1px;
}

mjx-container[jax="SVG"] > svg a {
 fill: blue;
 stroke: blue;
}

mjx-container[jax="SVG"][display="true"] {
 display: block;
 text-align: center;
 margin: 1em 0;
}

Line truncated
|
||||
|
Before Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 55 KiB |