1<!DOCTYPE html><html lang="en" class="bg-[var(--page-bg)] transition text-[14px] md:text-[16px]" style="--configHue: 250;--page-width: 75rem;--bannerOffset: 15vh;--banner-height-home: 68vh;--banner-height: 38vh;" data-astro-cid-ju4pidww><head><title>About - Yafei's Archive</title><meta charset="UTF-8"><link rel="canonical" href="https://www.yafei.dev/about/"><link rel="alternate" hreflang="en" href="https://www.yafei.dev/about/"><link rel="alternate" hreflang="zh-CN" href="https://www.yafei.dev/zh-CN/about/"><link rel="alternate" hreflang="x-default" href="https://www.yafei.dev/about/"><meta name="description" content="About"><meta name="author" content="Yafei Ou"><meta property="og:site_name" content="Yafei's Archive"><meta property="og:url" content="https://www.yafei.dev/about/"><meta property="og:title" content="About - Yafei's Archive"><meta property="og:description" content="About"><meta property="og:image" content="https://www.yafei.dev/_astro/the-bridge-at-argenteuil.M0xqQZlT.png"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta property="twitter:url" content="https://www.yafei.dev/about/"><meta name="twitter:title" content="About - Yafei's Archive"><meta name="twitter:description" content="About"><meta name="twitter:image" content="https://www.yafei.dev/_astro/the-bridge-at-argenteuil.M0xqQZlT.png"><meta name="viewport" content="width=device-width"><meta name="generator" content="Astro v7.2.9"><link rel="icon" href="/favicon/favicon.ico"><!-- Set the theme before the page is rendered to avoid a flash -->
1<script>(function(){const DEFAULT_THEME = "auto"; 2const LIGHT_MODE = "light"; 3const DARK_MODE = "dark"; 4const AUTO_MODE = "auto"; 5const BANNER_HEIGHT_EXTEND = 30; 6const PAGE_WIDTH = 75; 7const configHue = 250; 8const configBgHue = 90; 9const configBgFollowTheme = false; 10 11 // Load the theme from local storage 12 const theme = localStorage.getItem('theme') || DEFAULT_THEME; 13 switch (theme) { 14 case LIGHT_MODE: 15 document.documentElement.classList.remove('dark'); 16 break 17 case DARK_MODE: 18 document.documentElement.classList.add('dark'); 19 break 20 case AUTO_MODE: 21 if (window.matchMedia('(prefers-color-scheme: dark)').matches) { 22 document.documentElement.classList.add('dark'); 23 } else { 24 document.documentElement.classList.remove('dark'); 25 } 26 } 27 28 // Load the hue from local storage 29 const hue = localStorage.getItem('hue') || configHue; 30 document.documentElement.style.setProperty('--hue', hue); 31 if (configBgFollowTheme) 32 { 33 document.documentElement.style.setProperty('--bg-hue', hue); 34 } 35 else 36 { 37 document.documentElement.style.setProperty('--bg-hue', configBgHue); 38 } 39 40 // calculate the --banner-height-extend, which needs to be a multiple of 4 to avoid blurry text 41 let offset = Math.floor(window.innerHeight * (BANNER_HEIGHT_EXTEND / 100)); 42 offset = offset - offset % 4; 43 document.documentElement.style.setProperty('--banner-height-extend', `${offset}px`); 44 })();</script>
44 <!-- defines global css variables. This will be applied to <html> <body> and some other elements idk why --><link rel="alternate" type="application/rss+xml" title="Yafei Ou" href="https://www.yafei.dev/rss.xml"><link rel="stylesheet" href="/_astro/MainGridLayout.CqTCrwAc.css"><style>input.svelte-1wah7ro:focus{outline:0}.search-panel.svelte-1wah7ro{max-height:calc(100vh - 100px);overflow-y:auto} 45#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k){-webkit-appearance:none;height:1.5rem;background-image:var(--color-selection-bar);transition:background-image .15s ease-in-out}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-webkit-slider-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;background:#ffffffb3;box-shadow:none}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-webkit-slider-thumb:hover{background:#fffc}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-webkit-slider-thumb:active{background:#fff9}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-moz-range-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;border-width:0;background:#ffffffb3;box-shadow:none}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-moz-range-thumb:hover{background:#fffc}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-moz-range-thumb:active{background:#fff9}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-ms-thumb{-webkit-appearance:none;height:1rem;width:.5rem;border-radius:.125rem;background:#ffffffb3;box-shadow:none}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-ms-thumb:hover{background:#fffc}#display-setting.svelte-d7tq3k input[type=range]:where(.svelte-d7tq3k)::-ms-thumb:active{background:#fff9} 46.image-grid[data-astro-cid-vdjfdrcr]{display:grid;grid-template-columns:minmax(0,1fr);column-gap:1rem;row-gap:2rem}.image-grid[data-astro-cid-vdjfdrcr] figure{margin-top:0;margin-bottom:0}@media(width>=640px){.image-grid[data-astro-cid-vdjfdrcr]{grid-template-columns:repeat(2,minmax(0,1fr))}} 47div[data-astro-cid-figslg2k]{aspect-ratio:var(--aspectRatio)} 48</style><link rel="stylesheet" href="/_astro/Markdown.BtniRLn_.css">
48<script type="module" src="/_astro/page.C7SUca5V.js"></script>
48</head><body class=" min-h-screen transition enable-banner" style="--configHue: 250;--page-width: 75rem;--bannerOffset: 15vh;--banner-height-home: 68vh;--banner-height: 38vh;" data-astro-cid-ju4pidww><div id="config-carrier" data-hue="250"></div><div id="top-row" class="z-50 pointer-events-none relative transition-all duration-700 max-w-[var(--page-width)] px-0 md:px-4 mx-auto"><div id="navbar-wrapper" class="pointer-events-auto sticky top-0 transition-all"><div id="navbar" class="z-50 onload-animation"><div class="absolute h-8 left-0 right-0 -top-8 bg-[var(--navbar-bg)] transition"></div> <!-- used for onload animation --><div class="navbar !overflow-visible max-w-[var(--page-width)] h-[4.5rem] !rounded-t-none mx-auto flex items-center justify-between px-4"><a href="/" class="btn-plain scale-animation rounded-lg h-[3.25rem] px-5 font-bold active:scale-95"><div class="flex flex-row text-[var(--primary)] items-center text-md"><svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.75rem] mb-1 mr-2" data-icon="material-symbols:home-outline-rounded"><symbol id="ai:material-symbols:home-outline-rounded"><path fill="currentColor" d="M6 19h3v-5q0-.425.288-.712T10 13h4q.425 0 .713.288T15 14v5h3v-9l-6-4.5L6 10zm-2 0v-9q0-.475.213-.9t.587-.7l6-4.5q.525-.4 1.2-.4t1.2.4l6 4.5q.375.275.588.7T20 10v9q0 .825-.588 1.413T18 21h-4q-.425 0-.712-.288T13 20v-5h-2v5q0 .425-.288.713T10 21H6q-.825 0-1.412-.587T4 19m8-6.75"/></symbol><use href="#ai:material-symbols:home-outline-rounded"></use></svg>Yafei's Archive</div></a><div class="hidden md:flex"><a aria-label="Home" href="/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">Home</div></a><a aria-label="Publications" href="/publications/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">Publications</div></a><a aria-label="Archive" href="/archive/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">Archive</div></a><a aria-label="About" href="/about/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">About</div></a><a aria-label="CV" href="/cv/" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">CV</div></a><a aria-label="CRESSim" href="https://tbs-ualberta.github.io/CRESSim/" target="_blank" class="btn-plain scale-animation rounded-lg h-11 font-bold px-5 active:scale-95"><div class="flex items-center">CRESSim<svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[0.875rem] transition -translate-y-[1px] ml-1 text-black/[0.2] dark:text-white/[0.2]" data-icon="fa6-solid:arrow-up-right-from-square"><symbol id="ai:fa6-solid:arrow-up-right-from-square"><path fill="currentColor" d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32zM80 32C35.8 32 0 67.8 0 112v320c0 44.2 35.8 80 80 80h320c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32v112c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16h112c17.7 0 32-14.3 32-32s-14.3-32-32-32z"/></symbol><use href="#ai:fa6-solid:arrow-up-right-from-square"></use></svg></div></a></div><div class="flex"><!--<SearchPanel client:load>--><style>astro-island,astro-slot,astro-static-slot{display:contents}</style>
48<script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script>
48<script>(()=>{var g=Object.defineProperty;var w=(a,s,c)=>s in a?g(a,s,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[s]=c;var l=(a,s,c)=>w(a,typeof s!="symbol"?s+"":s,c);var E=new Set(["__proto__","constructor","prototype"]);{let a={0:t=>y(t),1:t=>c(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(c(t)),5:t=>new Set(c(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>Number.POSITIVE_INFINITY*t},s=t=>{let[p,e]=t;return p in a?a[p](e):void 0},c=t=>t.map(s),y=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([p,e])=>[p,s(e)]));class f extends HTMLElement{constructor(){super(...arguments);l(this,"Component");l(this,"hydrator");l(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let r=this.querySelectorAll("astro-slot"),n={},d=this.querySelectorAll("template[data-astro-template]");for(let o of d){let i=o.closest(this.tagName);i!=null&&i.isSameNode(this)&&(n[o.getAttribute("data-astro-template")||"default"]=o.innerHTML,o.remove())}for(let o of r){let i=o.closest(this.tagName);i!=null&&i.isSameNode(this)&&(n[o.getAttribute("name")||"default"]=o.innerHTML)}let u;try{u=this.hasAttribute("props")?y(JSON.parse(this.getAttribute("props"))):{}}catch(o){let i=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(i+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${i}`,this.getAttribute("props"),o),o}let h;await this.hydrator(this)(this.Component,u,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});l(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),r.disconnect(),this.childrenConnectedCallback()},r=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});r.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}getRetryImportUrl(e){let r=new URL(e,document.baseURI);return r.searchParams.set("astro-retry",Date.now().toString()),r.toString()}async importWithRetry(e){try{return await import(e)}catch(r){return await new Promise(n=>setTimeout(n,1e3)),import(this.getRetryImportUrl(e))}}handleHydrationError(e){let r=this.getAttribute("component-url"),n=new CustomEvent("astro:hydration-error",{cancelable:!0,bubbles:!0,composed:!0,detail:{error:e,componentUrl:r}});this.dispatchEvent(n)&&console.error(`[astro-island] Error hydrating ${r}`,e)}async start(){let e=JSON.parse(this.getAttribute("opts")),r=this.getAttribute("client");if(Astro[r]===void 0){window.addEventListener(`astro:${r}`,()=>this.start(),{once:!0});return}try{await Astro[r](async()=>{let n=this.getAttribute("renderer-url");try{let[d,{default:u}]=await Promise.all([this.importWithRetry(this.getAttribute("component-url")),n?this.importWithRetry(n):Promise.resolve({default:()=>()=>{}})]),h=this.getAttribute("component-export")||"default";if(h.includes(".")){this.Component=d;for(let m of h.split(".")){if(E.has(m)||!this.Component||typeof this.Component!="object"&&typeof this.Component!="function"||!Object.hasOwn(this.Component,m))throw new Error(`Invalid component export path: ${h}`);this.Component=this.Component[m]}}else{if(E.has(h))throw new Error(`Invalid component export path: ${h}`);this.Component=d[h]}return this.hydrator=u,this.hydrate}catch(d){return this.handleHydrationError(d),()=>{}}},e,this)}catch(n){this.handleHydrationError(n)}}attributeChangedCallback(){this.hydrate()}}l(f,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",f)}})();</script>
48<astro-island uid="1QJ66e" component-url="/_astro/Search.CZI_1yZc.js" component-export="default" renderer-url="/_astro/client.svelte.Ccz8e0GQ.js" props="{"lang":[0,"en"]}" ssr client="only" opts="{"name":"Search","value":"svelte"}"></astro-island><astro-island uid="18ji5" component-url="/_astro/LanguageSwitch.B_vMuEeH.js" component-export="default" renderer-url="/_astro/client.svelte.Ccz8e0GQ.js" props="{}" ssr client="only" opts="{"name":"LanguageSwitch","value":"svelte"}"></astro-island><astro-island uid="m9r06" component-url="/_astro/LightDarkSwitch.gMIqSQMN.js" component-export="default" renderer-url="/_astro/client.svelte.Ccz8e0GQ.js" props="{"lang":[0,"en"]}" ssr client="only" opts="{"name":"LightDarkSwitch","value":"svelte"}"></astro-island><button aria-label="Menu" name="Nav Menu" class="btn-plain scale-animation rounded-lg w-11 h-11 active:scale-90 md:!hidden" id="nav-menu-switch"><svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.25rem]" data-icon="material-symbols:menu-rounded"><symbol id="ai:material-symbols:menu-rounded"><path fill="currentColor" d="M4 18q-.425 0-.712-.288T3 17t.288-.712T4 16h16q.425 0 .713.288T21 17t-.288.713T20 18zm0-5q-.425 0-.712-.288T3 12t.288-.712T4 11h16q.425 0 .713.288T21 12t-.288.713T20 13zm0-5q-.425 0-.712-.288T3 7t.288-.712T4 6h16q.425 0 .713.288T21 7t-.288.713T20 8z"/></symbol><use href="#ai:material-symbols:menu-rounded"></use></svg></button></div><div id="nav-menu-panel" class="float-panel float-panel-closed absolute transition-all fixed right-4 px-2 py-2"><a href="/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 49 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 50 "><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">Home</div><svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"><symbol id="ai:material-symbols:chevron-right-rounded"><path fill="currentColor" d="M12.6 12L8.7 8.1q-.275-.275-.275-.7t.275-.7t.7-.275t.7.275l4.6 4.6q.15.15.213.325t.062.375t-.062.375t-.213.325l-4.6 4.6q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7z"/></symbol><use href="#ai:material-symbols:chevron-right-rounded"></use></svg></a><a href="/publications/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 51 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 52 "><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">Publications</div><svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded">
52<use href="#ai:material-symbols:chevron-right-rounded"></use></svg></a><a href="/archive/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 53 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 54 "><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">Archive</div><svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"><use href="#ai:material-symbols:chevron-right-rounded"></use></svg></a><a href="/about/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 55 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 56 "><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">About</div><svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded">
56<use href="#ai:material-symbols:chevron-right-rounded"></use></svg></a><a href="/cv/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 57 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 58 "><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">CV</div><svg width="1em" height="1em" viewBox="0 0 24 24" class="transition text-[1.25rem] text-[var(--primary)]" data-icon="material-symbols:chevron-right-rounded"><use href="#ai:material-symbols:chevron-right-rounded"></use></svg></a><a href="https://tbs-ualberta.github.io/CRESSim/" class="group flex justify-between items-center py-2 pl-3 pr-1 rounded-lg gap-8 59 hover:bg-[var(--btn-plain-bg-hover)] active:bg-[var(--btn-plain-bg-active)] transition 60 " target="_blank"><div class="transition text-black/75 dark:text-white/75 font-bold group-hover:text-[var(--primary)] group-active:text-[var(--primary)]">CRESSim</div><svg width="1em" height="1em" viewBox="0 0 512 512" class="transition text-[0.75rem] text-black/25 dark:text-white/25 -translate-x-1" data-icon="fa6-solid:arrow-up-right-from-square"><use href="#ai:fa6-solid:arrow-up-right-from-square"></use></svg></a></div><astro-island uid="ZKtJWp" component-url="/_astro/DisplaySettings.CSEN4yJE.js" component-export="default" renderer-url="/_astro/client.svelte.Ccz8e0GQ.js" props="{}" ssr client="only" opts="{"name":"DisplaySettings","value":"svelte"}"></astro-island></div></div>
60<script type="module">function e(){localStorage.theme===`dark`?(document.documentElement.classList.remove(`dark`),localStorage.theme=`light`):(document.documentElement.classList.add(`dark`),localStorage.theme=`dark`)}function t(){let t=document.getElementById(`scheme-switch`);t&&(t.onclick=function(){e()});let n=document.getElementById(`display-settings-switch`);n&&(n.onclick=function(){let e=document.getElementById(`display-setting`);e&&e.classList.toggle(`float-panel-closed`)});let r=document.getElementById(`nav-menu-switch`);r&&(r.onclick=function(){let e=document.getElementById(`nav-menu-panel`);e&&e.classList.toggle(`float-panel-closed`)})}t();</script>
60<script>(function(){const scriptUrl = "/pagefind/pagefind.js"; 61 62async function loadPagefind() { 63 try { 64 const response = await fetch(scriptUrl, { method: 'HEAD' }); 65 if (!response.ok) { 66 throw new Error(`Pagefind script not found: ${response.status}`); 67 } 68 69 const pagefind = await import(scriptUrl); 70 71 await pagefind.options({ 72 excerptLength: 20 73 }); 74 75 window.pagefind = pagefind; 76 77 document.dispatchEvent(new CustomEvent('pagefindready')); 78 console.log('Pagefind loaded and initialized successfully, event dispatched.'); 79 } catch (error) { 80 console.error('Failed to load Pagefind:', error); 81 window.pagefind = { 82 search: () => Promise.resolve({ results: [] }), 83 options: () => Promise.resolve(), 84 }; 85 document.dispatchEvent(new CustomEvent('pagefindloaderror')); 86 console.log('Pagefind load error, event dispatched.'); 87 } 88} 89 90if (document.readyState === 'loading') { 91 document.addEventListener('DOMContentLoaded', loadPagefind); 92} else { 93 loadPagefind(); 94} 95})();</script>
95</div></div><div id="banner-wrapper" class="absolute z-10 w-full transition duration-700 overflow-hidden" style="top: -30vh"><figure id="banner" class="object-cover h-full transition duration-700 opacity-0 scale-105"><div class="overflow-hidden relative h-full rounded-[inherit]"><div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div><img src="/_astro/the-bridge-at-argenteuil.M0xqQZlT_Z6Y1r.webp" alt="Banner image of the blog" style="object-position: center" loading="lazy" decoding="async" width="1920" height="1440" class="w-full h-full object-cover"></div></figure></div><div class="absolute w-full z-30 pointer-events-none" style="top: calc(38vh - 3.5rem)"><!-- The pointer-events-none here prevent blocking the click event of the TOC --><div class="relative max-w-[var(--page-width)] mx-auto pointer-events-auto"><div id="main-grid" class="transition duration-700 w-full left-0 right-0 grid grid-cols-[17.5rem_auto] grid-rows-[auto_1fr_auto] lg:grid-rows-[auto] 96 mx-auto gap-4 px-0 md:px-4"><!-- Banner image credit --><a href="https://www.nga.gov/artworks/61374-bridge-argenteuil" id="banner-credit" target="_blank" rel="noopener" aria-label="Visit image source" class="group onload-animation transition-all absolute flex justify-center items-center rounded-full px-3 right-4 -top-[3.25rem] bg-black/60 hover:bg-black/70 h-9 hover:pr-9 active:bg-black/80"><div class="text-white/75 text-xs">Claude Monet / The Bridge at Argenteuil (1874)</div><svg width="1em" height="1em" viewBox="0 0 512 512" class="transition absolute text-[oklch(0.75_0.14_var(--hue))] right-4 text-[0.75rem] opacity-0 group-hover:opacity-100" data-icon="fa6-solid:arrow-up-right-from-square"><use href="#ai:fa6-solid:arrow-up-right-from-square"></use></svg></a><div id="sidebar" class="mb-4 row-start-2 row-end-3 col-span-2 lg:row-start-1 lg:row-end-2 lg:col-span-1 lg:max-w-[17.5rem] onload-animation w-full"><div class="flex flex-col w-full gap-4 mb-4"><div class="card-base p-3"><a aria-label="Go to About Page" href="/about/" class="group block relative mx-auto mt-1 lg:mx-0 lg:mt-0 mb-3 97 max-w-[12rem] lg:max-w-none overflow-hidden rounded-xl active:scale-95"><div class="absolute transition pointer-events-none group-hover:bg-black/30 group-active:bg-black/50 98 w-full h-full z-50 flex items-center justify-center"><svg width="1.13em" height="1em" viewBox="0 0 576 512" class="transition opacity-0 scale-90 group-hover:scale-100 group-hover:opacity-100 text-white text-5xl" data-icon="fa6-regular:address-card"><symbol id="ai:fa6-regular:address-card"><path fill="currentColor" d="M512 80c8.8 0 16 7.2 16 16v320c0 8.8-7.2 16-16 16H64c-8.8 0-16-7.2-16-16V96c0-8.8 7.2-16 16-16zM64 32C28.7 32 0 60.7 0 96v320c0 35.3 28.7 64 64 64h448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64zm144 224a64 64 0 1 0 0-128a64 64 0 1 0 0 128m-32 32c-44.2 0-80 35.8-80 80c0 8.8 7.2 16 16 16h192c8.8 0 16-7.2 16-16c0-44.2-35.8-80-80-80zm200-144c-13.3 0-24 10.7-24 24s10.7 24 24 24h80c13.3 0 24-10.7 24-24s-10.7-24-24-24zm0 96c-13.3 0-24 10.7-24 24s10.7 24 24 24h80c13.3 0 24-10.7 24-24s-10.7-24-24-24z"/></symbol><use href="#ai:fa6-regular:address-card"></use></svg></div><figure class="mx-auto lg:w-full h-full lg:mt-0 "><div class="overflow-hidden relative h-full rounded-[inherit]"><div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div><img src="/_astro/yafei.BoDNqJ2I_ZHtGrj.webp" alt="Profile Image of the Author" style="object-position: center" loading="lazy" decoding="async" width="873" height="881" class="w-full h-full object-cover"></div></figure></a><div class="px-2"><div class="font-bold text-xl text-center mb-1 dark:text-neutral-50 transition">Yafei Ou</div><div class="h-1 w-5 bg-[var(--primary)] mx-auto rounded-full mb-2 transition"></div><div class="text-center text-neutral-400 mb-2.5 transition">Roboticist</div><div class="flex flex-wrap gap-2 justify-center mb-1"><a rel="me" aria-label="Email" href="mailto:[email protected]" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"><svg width="1em" height="1em" viewBox="0 0 24 24" class="text-[1.5rem]" data-icon="material-symbols:mail-rou
98nded"><symbol id="ai:material-symbols:mail-rounded"><path fill="currentColor" d="M4 20q-.825 0-1.412-.587T2 18V6q0-.825.588-1.412T4 4h16q.825 0 1.413.588T22 6v12q0 .825-.587 1.413T20 20zm8.263-7.212q.137-.038.262-.113L19.6 8.25q.2-.125.3-.312t.1-.413q0-.5-.425-.75T18.7 6.8L12 11L5.3 6.8q-.45-.275-.875-.012T4 7.525q0 .25.1.438t.3.287l7.075 4.425q.125.075.263.113t.262.037t.263-.037"/></symbol><use href="#ai:material-symbols:mail-rounded"></use></svg></a><a rel="me" aria-label="Google Scholar" href="https://scholar.google.com/citations?user=mWTXZ9EAAAAJ&hl" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"><svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[1.5rem]" data-icon="fa6-brands:google-scholar"><symbol id="ai:fa6-brands:google-scholar"><path fill="currentColor" d="M390.9 298.5s0 .1.1.1c9.2 19.4 14.4 41.1 14.4 64C405.3 445.1 338.5 512 256 512s-149.3-66.9-149.3-149.3c0-22.9 5.2-44.6 14.4-64c1.7-3.6 3.6-7.2 5.6-10.7q6.6-11.4 15-21.3c27.4-32.6 68.5-53.3 114.4-53.3c33.6 0 64.6 11.1 89.6 29.9c9.1 6.9 17.4 14.7 24.8 23.5c5.6 6.6 10.6 13.8 15 21.3c2 3.4 3.8 7 5.5 10.5zm26.4-18.8c-30.1-58.4-91-98.4-161.3-98.4s-131.2 40-161.3 98.4L0 202.7L256 0l256 202.7l-94.7 77.1z"/></symbol><use href="#ai:fa6-brands:google-scholar"></use></svg></a><a rel="me" aria-label="ORCID" href="https://orcid.org/0000-0002-2989-9991" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"><svg width="1em" height="1em" viewBox="0 0 512 512" class="text-[1.5rem]" data-icon="fa6-brands:orcid"><symbol id="ai:fa6-brands:orcid"><path fill="currentColor" d="M294.75 188.19h-45.92V342h47.47c67.62 0 83.12-51.34 83.12-76.91c0-41.64-26.54-76.9-84.67-76.9M256 8C119 8 8 119 8 256s111 248 248 248s248-111 248-248S393 8 256 8m-80.79 360.76h-29.84v-207.5h29.84zm-14.92-231.14a19.57 19.57 0 1 1 19.57-19.57a19.64 19.64 0 0 1-19.57 19.57M300 369h-81V161.26h80.6c76.73 0 110.44 54.83 110.44 103.85C410 318.39 368.38 369 300 369"/></symbol><use href="#ai:fa6-brands:orcid"></use></svg></a><a rel="me" aria-label="GitHub" href="https://github.com/yafei-ou" target="_blank" class="btn-regular rounded-lg h-10 w-10 active:scale-90"><svg width="0.97em" height="1em" viewBox="0 0 496 512" class="text-[1.5rem]" data-icon="fa6-brands:github"><symbol id="ai:fa6-brands:github"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6c-3.3.3-5.6-1.3-5.6-3.6c0-2 2.3-3.6 5.2-3.6c3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9c2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9c.3 2 2.9 3.3 5.9 2.6c2.9-.7 4.9-2.6 4.6-4.6c-.3-1.9-3-3.2-5.9-2.9M244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2c12.8 2.3 17.3-5.6 17.3-12.1c0-6.2-.3-40.4-.3-61.4c0 0-70 15-84.7-29.8c0 0-11.4-29.1-27.8-36.6c0 0-22.9-15.7 1.6-15.4c0 0 24.9 2 38.6 25.8c21.9 38.6 58.6 27.5 72.9 20.9c2.3-16 8.8-27.1 16-33.7c-55.9-6.2-112.3-14.3-112.3-110.5c0-27.5 7.6-41.3 23.6-58.9c-2.6-6.5-11.1-33.3 2.6-67.9c20.9-6.5 69 27 69 27c20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27c13.7 34.7 5.2 61.4 2.6 67.9c16 17.7 25.8 31.5 25.8 58.9c0 96.5-58.9 104.2-114.8 110.5c9.2 7.9 17 22.9 17 46.4c0 33.7-.3 75.4-.3 83.6c0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252C496 113.3 383.5 8 244.8 8M97.2 352.9c-1.3 1-1 3.3.7 5.2c1.6 1.6 3.9 2.3 5.2 1c1.3-1 1-3.3-.7-5.2c-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9c1.6 1 3.6.7 4.3-.7c.7-1.3-.3-2.9-2.3-3.9c-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2c2.3 2.3 5.2 2.6 6.5 1c1.3-1.3.7-4.3-1.3-6.2c-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2c-1.4-2.3-4-3.3-5.6-2"/></symbol><use href="#ai:fa6-brands:github"></use></svg></a></div></div></div></div><div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4"><widget-layout data-id="categories" data-is-collapsed="false" class="pb-4 card-base onload-animation" style="animation-delay: 150ms; --collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6="true"><div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2 99 before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)] 100 before:absolute before:left-[-16px] before:top-[5.5px]" style="--collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6>Categories</div><div id="categories" class="collapse-wrapper px-4 overflow-hidden" style="--collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6><a href="/archive/?category=development" aria-label="View all posts in the Development category"><button class=" 101 w-full 102 h-10 103 rounded-lg 104 bg-none 105 hover:bg-[var(--btn-plain-bg-hover)] 106 active:bg-[var(--btn-plain-bg-active)] 107 transition-all 108 pl-2 109 hover:pl-3 110 111 text-neutral-700 112 hover:text-[var(--primary)] 113 dark:text-neutral-300 114 dark:hover:text-[var(--primary)] 115 "><div class="flex items-center justify-between relative mr-2"><div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">Development</div><div class="transition px-2 h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold 116 text-[var(--btn-content)] dark:text-[var(--deep-text)] 117 bg-[var(--btn-regular-bg)] dark:bg-[var(--primary)] 118 flex items-center justify-center">3</div></div></button></a><a href="/archive/?category=ideas" aria-label="View all posts in the Ideas category"><button class=" 119 w-full 120 h-10 121 rounded-lg 122 bg-none 123 hover:bg-[var(--btn-plain-bg-hover)] 124 active:bg-[var(--btn-plain-bg-active)] 125 transition-all 126 pl-2 127 hover:pl-3 128 129 text-neutral-700 130 hover:text-[var(--primary)] 131 dark:text-neutral-300 132 dark:hover:text-[var(--primary)] 133 "><div class="flex items-center justify-between relative mr-2"><div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">Ideas</div><div class="transition px-2 h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold 134 text-[var(--btn-content)] dark:text-[var(--deep-text)] 135 bg-[var(--btn-regular-bg)] dark:bg-[var(--primary)] 136 flex items-center justify-center">1</div></div></button></a><a href="/archive/?category=opinions" aria-label="View all posts in the Opinions category"><button class=" 137 w-full 138 h-10 139 rounded-lg 140 bg-none 141 hover:bg-[var(--btn-plain-bg-hover)] 142 active:bg-[var(--btn-plain-bg-active)] 143 transition-all 144 pl-2 145 hover:pl-3 146 147 text-neutral-700 148 hover:text-[var(--primary)] 149 dark:text-neutral-300 150 dark:hover:text-[var(--primary)] 151 "><div class="flex items-center justify-between relative mr-2"><div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">Opinions</div><div class="transition px-2 h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold 152 text-[var(--btn-content)] dark:text-[var(--deep-text)] 153 bg-[var(--btn-regular-bg)] dark:bg-[var(--primary)] 154 flex items-center justify-center">3</div></div></button></a><a href="/archive/?category=research" aria-label="View all posts in the Research category"><button class=" 155 w-full 156 h-10 157 rounded-lg 158 bg-none 159 hover:bg-[var(--btn-plain-bg-hover)] 160 active:bg-[var(--btn-plain-bg-active)] 161 transition-all 162 pl-2 163 hover:pl-3 164 165 text-neutral-700 166 hover:text-[var(--primary)] 167 dark:text-neutral-300 168 dark:hover:text-[var(--primary)] 169 "><div class="flex items-center justify-between relative mr-2"><div class="overflow-hidden text-left whitespace-nowrap overflow-ellipsis ">Research</div><div class="transition px-2 h-7 ml-4 min-w-[2rem] rounded-lg text-sm font-bold 170 text-[var(--btn-content)] dark:text-[var(--deep-text)] 171 bg-[var(--btn-regular-bg)] dark:bg-[var(--primary)] 172 flex items-center justify-center">9</div></div></button></a></div></widget-layout>
172<script type="module">var e=class extends HTMLElement{constructor(){if(super(),this.dataset.isCollapsed!==`true`)return;let e=this.dataset.id,t=this.querySelector(`.expand-btn`),n=this.querySelector(`#${e}`);t.addEventListener(`click`,()=>{n.classList.remove(`collapsed`),t.classList.add(`hidden`)})}};customElements.get(`widget-layout`)||customElements.define(`widget-layout`,e);</script>
172<widget-layout data-id="tags" data-is-collapsed="false" class="pb-4 card-base onload-animation" style="animation-delay: 200ms; --collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6="true"><div class="font-bold transition text-lg text-neutral-900 dark:text-neutral-100 relative ml-8 mt-4 mb-2 173 before:w-1 before:h-4 before:rounded-md before:bg-[var(--primary)] 174 before:absolute before:left-[-16px] before:top-[5.5px]" style="--collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6>Tags</div><div id="tags" class="collapse-wrapper px-4 overflow-hidden" style="--collapsedHeight: 7.5rem;" data-astro-cid-pc56jia6><div class="flex gap-2 flex-wrap"><a href="/archive/?tag=3D" aria-label="View all posts with the 3D tag" class="btn-regular h-8 text-sm px-3 rounded-lg">3D</a><a href="/archive/?tag=Blender" aria-label="View all posts with the Blender tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Blender</a><a href="/archive/?tag=C%2B%2B" aria-label="View all posts with the C++ tag" class="btn-regular h-8 text-sm px-3 rounded-lg">C++</a><a href="/archive/?tag=Changelog" aria-label="View all posts with the Changelog tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Changelog</a><a href="/archive/?tag=Code" aria-label="View all posts with the Code tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Code</a><a href="/archive/?tag=CUDA" aria-label="View all posts with the CUDA tag" class="btn-regular h-8 text-sm px-3 rounded-lg">CUDA</a><a href="/archive/?tag=Film" aria-label="View all posts with the Film tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Film</a><a href="/archive/?tag=HLSL" aria-label="View all posts with the HLSL tag" class="btn-regular h-8 text-sm px-3 rounded-lg">HLSL</a><a href="/archive/?tag=Linux" aria-label="View all posts with the Linux tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Linux</a><a href="/archive/?tag=Music" aria-label="View all posts with the Music tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Music</a><a href="/archive/?tag=Python" aria-label="View all posts with the Python tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Python</a><a href="/archive/?tag=Robot%20Learning" aria-label="View all posts with the Robot Learning tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Robot Learning</a><a href="/archive/?tag=Simulation" aria-label="View all posts with the Simulation tag" class="btn-regular h-8 text-sm px-3 rounded-lg">Simulation</a></div></div></widget-layout></div></div><main id="swup-container" class="transition-swup-fade col-span-2 lg:col-span-1 overflow-hidden"><div id="content-wrapper" class="onload-animation"><!-- the overflow-hidden here prevent long text break the layout--><!-- make id different from windows.swup global property --><div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32"><div class="card-base z-10 px-9 py-6 relative w-full "><!-- title --><div class="relative onload-animation"><h1 data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title" class="transition w-full block font-bold mb-3 175 text-2xl md:text-[2.25rem]/[2.75rem] 176 text-black/90 dark:text-white/90 177 ">About</h1></div><div data-pagefind-body class="prose dark:prose-invert prose-base !max-w-none custom-md mt-2"><section><h2 id="about-me">About Me<a class="anchor" href="#about-me"><span class="anchor-icon" data-pagefind-ignore="">#</span></a></h2><p>I am currently a Postdoctoral Scholar in the Department of Electrical and Computer Engineering at the University of Alberta. I received my Ph.D. from the University of Alberta in 2025 and my B.Eng. from the University of Electronic Science and Technology of China (UESTC) in 2021. 178My research interests range widely, including medical robotics, teleoperation, virtual reality, and robot learning. Personally, I am an anime and manga fan, an occasional vinyl collector, and an amateur game developer.</p></section> 179<section><h2 id="about-the-site">About the Site<a class="anchor" href="#about-the-site"><span class="anchor-icon" data-pagefind-ignore="">
179#</span></a></h2><p>This place is where I (plan to) share insights, experiences, and the latest developments in my research. Youâll also find archives of my code and projects.</p><p>This site is built based on <a href="https://github.com/saicaca/fuwari">Fuwari</a>.</p><a id="GCcb6o3n-card" class="card-github fetch-waiting no-styling" href="https://github.com/saicaca/fuwari" target="_blank" repo="saicaca/fuwari"><div class="gc-titlebar"><div class="gc-titlebar-left"><div class="gc-owner"><div id="GCcb6o3n-avatar" class="gc-avatar"></div><div class="gc-user">saicaca</div></div><div class="gc-divider">/</div><div class="gc-repo">fuwari</div></div><div class="github-logo"></div></div><div id="GCcb6o3n-description" class="gc-description">Waiting for api.github.com...</div><div class="gc-infobar"><div id="GCcb6o3n-stars" class="gc-stars">00K</div><div id="GCcb6o3n-forks" class="gc-forks">0K</div><div id="GCcb6o3n-license" class="gc-license">0K</div><span id="GCcb6o3n-language" class="gc-language">Waiting...</span></div>
179<script id="GCcb6o3n-script" type="text/javascript" defer> 180 fetch('https://api.github.com/repos/saicaca/fuwari', { referrerPolicy: "no-referrer" }).then(response => response.json()).then(data => { 181 document.getElementById('GCcb6o3n-description').innerText = data.description?.replace(/:[a-zA-Z0-9_]+:/g, '') || "Description not set"; 182 document.getElementById('GCcb6o3n-language').innerText = data.language; 183 document.getElementById('GCcb6o3n-forks').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.forks).replaceAll("â¯", ''); 184 document.getElementById('GCcb6o3n-stars').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.stargazers_count).replaceAll("â¯", ''); 185 const avatarEl = document.getElementById('GCcb6o3n-avatar'); 186 avatarEl.style.backgroundImage = 'url(' + data.owner.avatar_url + ')'; 187 avatarEl.style.backgroundColor = 'transparent'; 188 document.getElementById('GCcb6o3n-license').innerText = data.license?.spdx_id || "no-license"; 189 document.getElementById('GCcb6o3n-card').classList.remove("fetch-waiting"); 190 console.log("[GITHUB-CARD] Loaded card for saicaca/fuwari | GCcb6o3n.") 191 }).catch(err => { 192 const c = document.getElementById('GCcb6o3n-card'); 193 c?.classList.add("fetch-error"); 194 console.warn("[GITHUB-CARD] (Error) Loading card for saicaca/fuwari | GCcb6o3n.") 195 }) 196 </script>
196</a><p>It also cherry-picks features from the <a href="https://github.com/jinhyeonseo01/fuwari-i18n">fuwari-i18n</a> fork to support multiple languages.</p><a id="GCkelq0p-card" class="card-github fetch-waiting no-styling" href="https://github.com/jinhyeonseo01/fuwari-i18n" target="_blank" repo="jinhyeonseo01/fuwari-i18n"><div class="gc-titlebar"><div class="gc-titlebar-left"><div class="gc-owner"><div id="GCkelq0p-avatar" class="gc-avatar"></div><div class="gc-user">jinhyeonseo01</div></div><div class="gc-divider">/</div><div class="gc-repo">fuwari-i18n</div></div><div class="github-logo"></div></div><div id="GCkelq0p-description" class="gc-description">Waiting for api.github.com...</div><div class="gc-infobar"><div id="GCkelq0p-stars" class="gc-stars">00K</div><div id="GCkelq0p-forks" class="gc-forks">0K</div><div id="GCkelq0p-license" class="gc-license">0K</div><span id="GCkelq0p-language" class="gc-language">Waiting...</span></div>
196<script id="GCkelq0p-script" type="text/javascript" defer> 197 fetch('https://api.github.com/repos/jinhyeonseo01/fuwari-i18n', { referrerPolicy: "no-referrer" }).then(response => response.json()).then(data => { 198 document.getElementById('GCkelq0p-description').innerText = data.description?.replace(/:[a-zA-Z0-9_]+:/g, '') || "Description not set"; 199 document.getElementById('GCkelq0p-language').innerText = data.language; 200 document.getElementById('GCkelq0p-forks').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.forks).replaceAll("â¯", ''); 201 document.getElementById('GCkelq0p-stars').innerText = Intl.NumberFormat('en-us', { notation: "compact", maximumFractionDigits: 1 }).format(data.stargazers_count).replaceAll("â¯", ''); 202 const avatarEl = document.getElementById('GCkelq0p-avatar'); 203 avatarEl.style.backgroundImage = 'url(' + data.owner.avatar_url + ')'; 204 avatarEl.style.backgroundColor = 'transparent'; 205 document.getElementById('GCkelq0p-license').innerText = data.license?.spdx_id || "no-license"; 206 document.getElementById('GCkelq0p-card').classList.remove("fetch-waiting"); 207 console.log("[GITHUB-CARD] Loaded card for jinhyeonseo01/fuwari-i18n | GCkelq0p.") 208 }).catch(err => { 209 const c = document.getElementById('GCkelq0p-card'); 210 c?.classList.add("fetch-error"); 211 console.warn("[GITHUB-CARD] (Error) Loading card for jinhyeonseo01/fuwari-i18n | GCkelq0p.") 212 }) 213 </script>
213</a></section></div>
213<script type="module">document.addEventListener(`click`,function(e){let t=e.target;if(t&&t.classList.contains(`copy-btn`)){let e=t.closest(`pre`)?.querySelector(`code`),n=Array.from(e?.querySelectorAll(`.code:not(summary *)`)??[]).map(e=>e.textContent).map(e=>e===` 214`?``:e).join(` 215`);navigator.clipboard.writeText(n);let r=t.getAttribute(`data-timeout-id`);r&&clearTimeout(parseInt(r)),t.classList.add(`success`);let i=setTimeout(()=>{t.classList.remove(`success`)},1e3);t.setAttribute(`data-timeout-id`,i.toString())}});</script>
215</div></div><div class="footer col-span-2 onload-animation hidden lg:block"><!--<div class="border-t border-[var(--primary)] mx-16 border-dashed py-8 max-w-[var(--page-width)] flex flex-col items-center justify-center px-6">--><div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-32"></div><!--<div class="transition bg-[oklch(92%_0.01_var(--hue))] dark:bg-black rounded-2xl py-8 mt-4 mb-8 flex flex-col items-center justify-center px-6">--><div class="transition border-dashed border-[oklch(85%_0.01_var(--hue))] dark:border-white/15 rounded-2xl mb-12 flex flex-col items-center justify-center px-6"><div class="transition text-50 text-sm text-center">© <span id="copyright-year">2025–2026</span> Yafei Ou. All Rights Reserved. /<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/rss.xml">RSS</a> /<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/sitemap-index.xml">Sitemap</a></div></div></div></div></main><div class="footer col-span-2 onload-animation block lg:hidden"><!--<div class="border-t border-[var(--primary)] mx-16 border-dashed py-8 max-w-[var(--page-width)] flex flex-col items-center justify-center px-6">--><div class="transition border-t border-black/10 dark:border-white/15 my-10 border-dashed mx-32"></div><!--<div class="transition bg-[oklch(92%_0.01_var(--hue))] dark:bg-black rounded-2xl py-8 mt-4 mb-8 flex flex-col items-center justify-center px-6">--><div class="transition border-dashed border-[oklch(85%_0.01_var(--hue))] dark:border-white/15 rounded-2xl mb-12 flex flex-col items-center justify-center px-6"><div class="transition text-50 text-sm text-center">© <span id="copyright-year">2025–2026</span> Yafei Ou. All Rights Reserved. /<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/rss.xml">RSS</a> /<a class="transition link text-[var(--primary)] font-medium" target="_blank" href="/sitemap-index.xml">Sitemap</a></div></div></div></div><!-- There can't be a filter on parent element, or it will break `fixed` --><div class="back-to-top-wrapper hidden lg:block" data-astro-cid-774ni4gk><div id="back-to-top-btn" class="back-to-top-btn hide flex items-center rounded-2xl overflow-hidden transition" onclick="backToTop()" data-astro-cid-774ni4gk><button aria-label="Back to Top" class="btn-card h-[3.75rem] w-[3.75rem]" data-astro-cid-774ni4gk><svg width="1em" height="1em" viewBox="0 0 24 24" class="mx-auto" data-astro-cid-774ni4gk="true" data-icon="material-symbols:keyboard-arrow-up-rounded"><symbol id="ai:material-symbols:keyboard-arrow-up-rounded"><path fill="currentColor" d="m12 10.8l-3.9 3.9q-.275.275-.7.275t-.7-.275t-.275-.7t.275-.7l4.6-4.6q.3-.3.7-.3t.7.3l4.6 4.6q.275.275.275.7t-.275.7t-.7.275t-.7-.275z"/></symbol><use href="#ai:material-symbols:keyboard-arrow-up-rounded"></use></svg></button></div></div>
215<script> 216function backToTop() { 217 window.scroll({ top: 0, behavior: 'smooth' }); 218} 219</script>
219</div></div><div class="absolute w-full z-0 hidden 2xl:block"><div class="relative max-w-[var(--page-width)] mx-auto"><!-- TOC component --><div id="toc-wrapper" class="hidden lg:block transition absolute top-0 -right-[var(--toc-width)] w-[var(--toc-width)] items-center toc-hide"><div id="toc-inner-wrapper" class="fixed top-14 w-[var(--toc-width)] h-[calc(100vh_-_20rem)] overflow-y-scroll overflow-x-hidden hide-scrollbar"><div id="toc" class="w-full h-full transition-swup-fade "><div class="h-8 w-full"></div>
219<script type="module">var e=class extends HTMLElement{tocEl=null;visibleClass=`visible`;observer;anchorNavTarget=null;headingIdxMap=new Map;headings=[];sections=[];tocEntries=[];active=[];activeIndicator=null;constructor(){super(),this.observer=new IntersectionObserver(this.markVisibleSection,{threshold:0})}markVisibleSection=e=>{e.forEach(e=>{let t=e.target.children[0]?.getAttribute(`id`),n=t?this.headingIdxMap.get(t):void 0;n!=null&&(this.active[n]=e.isIntersecting),e.isIntersecting&&this.anchorNavTarget==e.target.firstChild&&(this.anchorNavTarget=null)}),this.active.includes(!0)||this.fallback(),this.update()};toggleActiveHeading=()=>{let e=this.active.length-1,t=this.active.length-1,n=-1;for(;e>=0&&!this.active[e];)this.tocEntries[e].classList.remove(this.visibleClass),e--;for(;e>=0&&this.active[e];)this.tocEntries[e].classList.add(this.visibleClass),t=Math.min(t,e),n=Math.max(n,e),e--;for(;e>=0;)this.tocEntries[e].classList.remove(this.visibleClass),e--;if(t>n)this.activeIndicator?.setAttribute(`style`,`opacity: 0`);else{let e=this.tocEl?.getBoundingClientRect().top||0,r=this.tocEl?.scrollTop||0,i=this.tocEntries[t].getBoundingClientRect().top-e+r,a=this.tocEntries[n].getBoundingClientRect().bottom-e+r;this.activeIndicator?.setAttribute(`style`,`top: ${i}px; height: ${a-i}px`)}};scrollToActiveHeading=()=>{if(this.anchorNavTarget||!this.tocEl)return;let e=document.querySelectorAll(`#toc .${this.visibleClass}`);if(!e.length)return;let t=e[0],n=e[e.length-1],r=this.tocEl.clientHeight,i;i=n.getBoundingClientRect().bottom-t.getBoundingClientRect().top<.9*r?t.offsetTop-32:n.offsetTop-r*.8,this.tocEl.scrollTo({top:i,left:0,behavior:`smooth`})};update=()=>{requestAnimationFrame(()=>{this.toggleActiveHeading(),this.scrollToActiveHeading()})};fallback=()=>{if(this.sections.length)for(let e=0;e<this.sections.length;e++){let t=this.sections[e].getBoundingClientRect().top,n=this.sections[e].getBoundingClientRect().bottom;if(this.isInRange(t,0,window.innerHeight)||this.isInRange(n,0,window.innerHeight)||t<0&&n>window.innerHeight)this.markActiveHeading(e);else if(t>window.innerHeight)break}};markActiveHeading=e=>{this.active[e]=!0};handleAnchorClick=e=>{let t=e.composedPath().find(e=>e instanceof HTMLAnchorElement);if(t){let e=decodeURIComponent(t.hash?.substring(1)),n=this.headingIdxMap.get(e);this.anchorNavTarget=n===void 0?null:this.headings[n]}};isInRange(e,t,n){return t<e&&e<n}connectedCallback(){let e=document.querySelector(`.prose`);e?e.addEventListener(`animationend`,()=>{this.init()},{once:!0}):console.debug(`Animation element not found`)}init(){if(this.tocEl=document.getElementById(`toc-inner-wrapper`),this.tocEl&&(this.tocEl.addEventListener(`click`,this.handleAnchorClick,{capture:!0}),this.activeIndicator=document.getElementById(`active-indicator`),this.tocEntries=Array.from(document.querySelectorAll(`#toc a[href^='#']`)),this.tocEntries.length!==0)){this.sections=Array(this.tocEntries.length),this.headings=Array(this.tocEntries.length);for(let e=0;e<this.tocEntries.length;e++){let t=decodeURIComponent(this.tocEntries[e].hash?.substring(1)),n=document.getElementById(t),r=n?.parentElement;n instanceof HTMLElement&&r instanceof HTMLElement&&(this.headings[e]=n,this.sections[e]=r,this.headingIdxMap.set(t,e))}this.active=Array(this.tocEntries.length).fill(!1),this.sections.forEach(e=>this.observer.observe(e)),this.fallback(),this.update()}}disconnectedCallback(){this.sections.forEach(e=>this.observer.unobserve(e)),this.observer.disconnect(),this.tocEl?.removeEventListener(`click`,this.handleAnchorClick)}};customElements.get(`table-of-contents`)||customElements.define(`table-of-contents`,e);</script>
219<div class="h-8 w-full"></div></div></div></div><!-- #toc needs to exist for Swup to work normally --></div></div><!-- increase the page height during page transition to prevent the scrolling animation from jumping --><div id="page-height-extend" class="hidden h-[300vh]" style="--configHue: 250;--page-width: 75rem;--bannerOffset: 15vh;--banner-height-home: 68vh;--banner-height: 38vh;" data-astro-cid-ju4pidww></div></body></html>
219<script type="module" src="/_astro/Layout.astro_astro_type_script_index_0_lang.C13uKkE8.js"></script>
219<script type="module" src="/_astro/Layout.astro_astro_type_script_index_1_lang.NnOmA7wb.js"></script>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.