PageSourceSearch

https://www.yafei.dev/about/

html yafei.dev collected 2026-09-24 14:59:46 UTC 51,639 bytes, 219 lines download raw bytes

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&#39;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&#39;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="{&quot;lang&quot;:[0,&quot;en&quot;]}" ssr client="only" opts="{&quot;name&quot;:&quot;Search&quot;,&quot;value&quot;:&quot;svelte&quot;}"></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="{&quot;name&quot;:&quot;LanguageSwitch&quot;,&quot;value&quot;:&quot;svelte&quot;}"></astro-island><astro-island uid="m9r06" component-url="/_astro/LightDarkSwitch.gMIqSQMN.js" component-export="default" renderer-url="/_astro/client.svelte.Ccz8e0GQ.js" props="{&quot;lang&quot;:[0,&quot;en&quot;]}" ssr client="only" opts="{&quot;name&quot;:&quot;LightDarkSwitch&quot;,&quot;value&quot;:&quot;svelte&quot;}"></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="{&quot;name&quot;:&quot;DisplaySettings&quot;,&quot;value&quot;:&quot;svelte&quot;}"></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&amp;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(&#45;&#45;primary)] mx-16 border-dashed py-8 max-w-[var(&#45;&#45;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(&#45;&#45;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">&copy; <span id="copyright-year">2025&ndash;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(&#45;&#45;primary)] mx-16 border-dashed py-8 max-w-[var(&#45;&#45;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(&#45;&#45;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">&copy; <span id="copyright-year">2025&ndash;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.