<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Xin Fu</title><link>https://imfing.com/</link><description>Recent content on Xin Fu</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Wed, 11 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://imfing.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Managing windows effectively with AeroSpace</title><link>https://imfing.com/til/aerospace-tiling-window-manager/</link><pubDate>Wed, 11 Feb 2026 00:00:00 +0000</pubDate><guid>https://imfing.com/til/aerospace-tiling-window-manager/</guid><description>&lt;p&gt;Managing application windows has been a never-ending battle, especially with AI tools like Claude Code, Codex, and Cursor encouraging work across multiple projects simultaneously.
I&amp;rsquo;ve relied on &lt;a
 href="https://support.apple.com/en-gb/guide/mac-help/mh14112/mac"
 
 target="_blank" rel="noopener"
 
&gt;macOS built-in Spaces&lt;/a&gt;
, but as the number of workspaces grows, switching and finding the right window becomes harder.&lt;/p&gt;
&lt;p&gt;&lt;a
 href="https://github.com/nikitabobko/AeroSpace"
 
 target="_blank" rel="noopener"
 
&gt;AeroSpace&lt;/a&gt;
 is a tiling window manager for macOS. I tried a similar tool called &lt;a
 href="https://github.com/asmvik/yabai"
 
 target="_blank" rel="noopener"
 
&gt;yabai&lt;/a&gt;
 before but wasn&amp;rsquo;t quite impressed.&lt;/p&gt;
&lt;p&gt;AeroSpace works pretty much out of the box — windows snap into place without overlapping. It implements virtual workspaces in a faster and more predictable way than native Spaces.
For example, workspaces are mapped to shortcuts like &lt;kbd&gt;Option&lt;/kbd&gt; + &lt;kbd&gt;1&lt;/kbd&gt; or &lt;kbd&gt;Option&lt;/kbd&gt; + &lt;kbd&gt;A&lt;/kbd&gt;, making switching between them nearly instant.&lt;/p&gt;</description></item><item><title>Television: a modern fuzzy finder for the terminal</title><link>https://imfing.com/til/television-modern-fuzzy-finder-terminal/</link><pubDate>Fri, 12 Dec 2025 00:00:00 +0000</pubDate><guid>https://imfing.com/til/television-modern-fuzzy-finder-terminal/</guid><description>&lt;p&gt;I discovered &lt;a
 href="https://github.com/alexpasmantier/television"
 
 target="_blank" rel="noopener"
 
&gt;television&lt;/a&gt;
 (tv), a terminal fuzzy finder UI written in Rust, thanks to the blog post &lt;a
 href="https://zed.dev/blog/hidden-gems-part-2"
 
 target="_blank" rel="noopener"
 
&gt;Hidden Gems: Part 2&lt;/a&gt;
 by Zed.&lt;/p&gt;
&lt;p&gt;&lt;figure class="my-6"&gt;
 &lt;img
 src="https://raw.githubusercontent.com/alexpasmantier/television/main/assets/tv-transparent.png"
 alt="television-cli"
 
 data-zoomable
 class="rounded-lg mx-auto"
 loading="lazy"
 /&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s actually very similar to &lt;a
 href="https://github.com/junegunn/fzf"
 
 target="_blank" rel="noopener"
 
&gt;fzf&lt;/a&gt;
, a command line fuzzy finder which I use mostly for shell integration like &lt;code&gt;Ctrl+R&lt;/code&gt; searching for history.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;tv&lt;/code&gt; command line provides a convenient way to chain command line tools such as &lt;code&gt;fd&lt;/code&gt;, &lt;code&gt;bat&lt;/code&gt;, &lt;code&gt;gh&lt;/code&gt;, etc. into a fuzzy finder with &lt;a
 href="https://github.com/nvim-telescope/telescope.nvim"
 
 target="_blank" rel="noopener"
 
&gt;telescope&lt;/a&gt;
 style layout. It also has shell integrations.&lt;/p&gt;</description></item><item><title>Use custom LLM providers in Claude Code</title><link>https://imfing.com/til/use-custom-llm-providers-in-claude-code/</link><pubDate>Mon, 24 Nov 2025 00:00:00 +0000</pubDate><guid>https://imfing.com/til/use-custom-llm-providers-in-claude-code/</guid><description>&lt;p&gt;&lt;a
 href="https://claude.ai/code"
 
 target="_blank" rel="noopener"
 
&gt;Claude Code&lt;/a&gt;
 has been one of the most popular coding agent this year.
I&amp;rsquo;ve relied on it heavily for common development tasks.
One useful but lesser-known feature is that Claude Code allows you to override the default Anthropic API endpoint using the &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; environment variable.&lt;/p&gt;
&lt;p&gt;This is very useful in many scenarios, such as working around quota limits, connecting to custom model endpoints or third-party providers like &lt;a
 href="https://github.com/features/copilot"
 
 target="_blank" rel="noopener"
 
&gt;GitHub Copilot&lt;/a&gt;
.&lt;/p&gt;</description></item><item><title>Set Google search AI mode as browser default</title><link>https://imfing.com/til/set-google-search-ai-mode-as-browser-default/</link><pubDate>Sun, 23 Nov 2025 00:00:00 +0000</pubDate><guid>https://imfing.com/til/set-google-search-ai-mode-as-browser-default/</guid><description>&lt;p&gt;With the recent announcement of &lt;a
 href="https://blog.google/products/gemini/gemini-3/"
 
 target="_blank" rel="noopener"
 
&gt;Gemini 3&lt;/a&gt;
, I was thinking about giving &lt;a
 href="https://search.google/intl/en-GB/ways-to-search/ai-mode/"
 
 target="_blank" rel="noopener"
 
&gt;Google search AI mode&lt;/a&gt;
 another shot.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using &lt;a
 href="https://www.perplexity.ai/"
 
 target="_blank" rel="noopener"
 
&gt;Perlexitity&lt;/a&gt;
 for a while and found it quite useful for many of my searches, but lately it has felt slower and the quality of results has declined.&lt;/p&gt;
&lt;p&gt;After trying out the latest Google search AI mode, I&amp;rsquo;m quite impressed by the quality, speed and overall user experience. I&amp;rsquo;ve now set it as my default search option in Chrome.&lt;/p&gt;</description></item><item><title>Run Llama 2 locally on MacBook</title><link>https://imfing.com/til/run-llama-2-locally-on-macbook/</link><pubDate>Sun, 23 Jul 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/run-llama-2-locally-on-macbook/</guid><description>&lt;p&gt;Last week, Meta released &lt;a
 href="https://about.fb.com/news/2023/07/llama-2/"
 
 target="_blank" rel="noopener"
 
&gt;Llama 2&lt;/a&gt;
, an &amp;ldquo;open source&amp;rdquo; large language model that is free for research and commercial use. Within a few hours, the community has ported Llama 2 to &lt;a
 href="https://github.com/ggerganov/llama.cpp"
 
 target="_blank" rel="noopener"
 
&gt;llama.cpp&lt;/a&gt;
 which makes it eaiser and more efficient to run Llama 2 locally.&lt;/p&gt;
&lt;h2 id="download-and-compile-llamacpp"&gt;
 &lt;a class="anchor" href="#download-and-compile-llamacpp"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Download and compile llama.cpp
&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; llama.cpp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;LLAMA_METAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that &lt;code&gt;LLAMA_METAL&lt;/code&gt; is set to &lt;code&gt;1&lt;/code&gt; to enable using GPU on Apple Silicone. On my M1 Pro MacBook Pro, the compliation took about a few seconds.&lt;/p&gt;</description></item><item><title>Create interactive utility app with Streamlit</title><link>https://imfing.com/til/create-interactive-utility-app-with-streamlit/</link><pubDate>Mon, 10 Jul 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/create-interactive-utility-app-with-streamlit/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&lt;a
 href="https://streamlit.io/"
 
 target="_blank" rel="noopener"
 
&gt;Streamlit&lt;/a&gt;
 is an open-source Python library that makes it easy to create and share beautiful, custom web apps for machine learning and data science.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Besides data science and machine learning, I found Streamlit can also be used for creating very simple utility apps.&lt;/p&gt;
&lt;h2 id="postboy---a-simple-postman-like-app"&gt;
 &lt;a class="anchor" href="#postboy---a-simple-postman-like-app"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Postboy - a simple Postman-like app
&lt;/h2&gt;
&lt;p&gt;For example, we can create a simple &lt;a
 href="https://www.postman.com/"
 
 target="_blank" rel="noopener"
 
&gt;Postman&lt;/a&gt;
-like app for testing REST APIs with Streamlit in just a few lines of code.&lt;/p&gt;</description></item><item><title>Handling JWT in Python</title><link>https://imfing.com/til/handling-jwt-in-python/</link><pubDate>Sun, 09 Jul 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/handling-jwt-in-python/</guid><description>&lt;p&gt;&lt;a
 href="https://en.wikipedia.org/wiki/JSON_Web_Token"
 
 target="_blank" rel="noopener"
 
&gt;JSON Web Tokens (JWT)&lt;/a&gt;
 is an open standard (&lt;a
 href="https://tools.ietf.org/html/rfc7519"
 
 target="_blank" rel="noopener"
 
&gt;RFC 7519&lt;/a&gt;
) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.&lt;/p&gt;
&lt;h2 id="jwt-structure"&gt;
 &lt;a class="anchor" href="#jwt-structure"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 JWT Structure
&lt;/h2&gt;
&lt;p&gt;JWTs consist of three parts separated by dots:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Header&lt;/li&gt;
&lt;li&gt;Payload&lt;/li&gt;
&lt;li&gt;Signature&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="header"&gt;
 &lt;a class="anchor" href="#header"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Header
&lt;/h3&gt;
&lt;p&gt;The header contains the algorithm used to sign the token and the type of token. The header is base64 encoded and looks like this:&lt;/p&gt;</description></item><item><title>Question answering over documents with LLM</title><link>https://imfing.com/til/question-answering-over-documents-with-llm/</link><pubDate>Sat, 01 Jul 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/question-answering-over-documents-with-llm/</guid><description>&lt;p&gt;One of the most popular applications for &lt;a
 href="https://en.wikipedia.org/wiki/Large_language_model"
 
 target="_blank" rel="noopener"
 
&gt;large language model (LLM)&lt;/a&gt;
 is question answering over various types of documents, such a plain text, web pages, and PDFs. Usually, we want to make the model answer the question which it hasn&amp;rsquo;t been trained on.&lt;/p&gt;
&lt;h2 id="overview"&gt;
 &lt;a class="anchor" href="#overview"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Overview
&lt;/h2&gt;
&lt;p&gt;There are mainly two steps involved:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Data ingestion: load source documents and convert them into vector embeddings which will be stored in a vector database&lt;/li&gt;
&lt;li&gt;Question answering: when given input question, convert to vector embedding first, then perform similarity search within the vector database, and top k results will be used as context for the LLM to generate answer to the question.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Diagrams:&lt;/p&gt;</description></item><item><title>Deploy Ghost instance to Fly.io</title><link>https://imfing.com/til/deploy-ghost-instance-to-fly-io/</link><pubDate>Wed, 28 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/deploy-ghost-instance-to-fly-io/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Not affiliated with Ghost or Fly.io, this is only for evaluation purpose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a
 href="https://fly.io/"
 
 target="_blank" rel="noopener"
 
&gt;Fly.io&lt;/a&gt;
 comes with generous free tier allowance, which can be used to run some random side projects for free.
&lt;a
 href="https://ghost.org/"
 
 target="_blank" rel="noopener"
 
&gt;Ghost&lt;/a&gt;
 is a WordPress like publishing platform. Unfortunately, there&amp;rsquo;s no free tier provided by their company. Similar to WordPress, it provides an &lt;a
 href="https://github.com/TryGhost/Ghost"
 
 target="_blank" rel="noopener"
 
&gt;open-source version&lt;/a&gt;
 that can be self-hosted.&lt;/p&gt;
&lt;h2 id="get-started"&gt;
 &lt;a class="anchor" href="#get-started"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Get Started
&lt;/h2&gt;
&lt;p&gt;To get started, first create an account on &lt;a
 href="https://fly.io/"
 
 target="_blank" rel="noopener"
 
&gt;Fly.io&lt;/a&gt;
, and install &lt;a
 href="https://fly.io/docs/hands-on/install-flyctl/"
 
 target="_blank" rel="noopener"
 
&gt;&lt;code&gt;flyctl&lt;/code&gt;&lt;/a&gt;
:&lt;/p&gt;</description></item><item><title>Custom middleware for FastAPI application</title><link>https://imfing.com/til/custom-middleware-for-fastapi-application/</link><pubDate>Sun, 25 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/custom-middleware-for-fastapi-application/</guid><description>&lt;p&gt;&lt;a
 href="https://github.com/tiangolo/fastapi"
 
 target="_blank" rel="noopener"
 
&gt;FastAPI&lt;/a&gt;
 is a Python web framework for building APIs.
By using a middleware, we are able to process request and response before/after they get handled by the application.&lt;/p&gt;
&lt;h2 id="implement-custom-middleware"&gt;
 &lt;a class="anchor" href="#implement-custom-middleware"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Implement custom middleware
&lt;/h2&gt;
&lt;p&gt;&lt;a
 href="https://github.com/encode/starlette"
 
 target="_blank" rel="noopener"
 
&gt;Starlette&lt;/a&gt;
 is a lightweight ASGI framework/toolkit on which FastAPI is based.
It provides &lt;a
 href="https://www.starlette.io/middleware/#basehttpmiddleware"
 
 target="_blank" rel="noopener"
 
&gt;&lt;code&gt;BaseHTTPMiddleware&lt;/code&gt;&lt;/a&gt;
 class for us to implement custom middleware.
It&amp;rsquo;s required to override the &lt;code&gt;async def dispatch(request, call_next)&lt;/code&gt; method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;starlette.middleware.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseHTTPMiddleware&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomHeaderMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseHTTPMiddleware&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Example&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;header_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;call_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Custom&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;header_value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The example middleware above simply adds a &lt;code&gt;Custom&lt;/code&gt; header to the response.&lt;/p&gt;</description></item><item><title>Notes on "Langchain for LLM Application Development"</title><link>https://imfing.com/til/notes-on-langchain-for-llm-application-development/</link><pubDate>Sun, 25 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/notes-on-langchain-for-llm-application-development/</guid><description>&lt;p&gt;Course Link: &lt;a
 href="https://www.deeplearning.ai/short-courses/langchain-for-llm-application-development/"
 
 target="_blank" rel="noopener"
 
&gt;LangChain for LLM Application Development - DeepLearning.AI&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;Previous notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a
 href="./notes-on-building-systems-with-the-chatgpt-api"
 
&gt;Notes on &amp;ldquo;Building Systems with the ChatGPT API&amp;rdquo;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="introduction"&gt;
 &lt;a class="anchor" href="#introduction"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Introduction
&lt;/h2&gt;
&lt;p&gt;What is &lt;a
 href="https://github.com/hwchase17/langchain"
 
 target="_blank" rel="noopener"
 
&gt;Langchain&lt;/a&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open-source development framework for LLM applications&lt;/li&gt;
&lt;li&gt;Provide both Python and JavaScript/Typescript packages&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Modular components which can be combined to build end-to-end applications.&lt;/p&gt;
&lt;h2 id="models-prompts-and-output-parsers"&gt;
 &lt;a class="anchor" href="#models-prompts-and-output-parsers"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Models, Prompts and Output Parsers
&lt;/h2&gt;
&lt;h3 id="openai-api"&gt;
 &lt;a class="anchor" href="#openai-api"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 OpenAI API
&lt;/h3&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;openai&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;gpt-3.5-turbo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;role&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="langchain"&gt;
 &lt;a class="anchor" href="#langchain"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 LangChain
&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;langchain.chat_models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;chat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h5 id="prompt-template"&gt;
 &lt;a class="anchor" href="#prompt-template"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Prompt template
&lt;/h5&gt;
&lt;p&gt;A &lt;a
 href="https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates"
 
 target="_blank" rel="noopener"
 
&gt;prompt template&lt;/a&gt;
 refers to a reproducible way to generate a prompt.&lt;/p&gt;</description></item><item><title>Set up Hugo with Tailwind CSS in 2023</title><link>https://imfing.com/til/set-up-hugo-with-tailwind-css-in-2023/</link><pubDate>Sat, 24 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/set-up-hugo-with-tailwind-css-in-2023/</guid><description>&lt;p&gt;With the release of &lt;a
 href="https://github.com/gohugoio/hugo/releases/tag/v0.112.0"
 
 target="_blank" rel="noopener"
 
&gt;v0.112.0&lt;/a&gt;
, Hugo added the native support for &lt;a
 href="https://tailwindcss.com/blog/tailwindcss-v3"
 
 target="_blank" rel="noopener"
 
&gt;TailwindCSS v3.x&lt;/a&gt;
. The author of Hugo provided an example repository setting up TailwindCSS v3: &lt;a
 href="https://github.com/bep/hugo-starter-tailwind-basic"
 
 target="_blank" rel="noopener"
 
&gt;bep/hugo-starter-tailwind-basic&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Note that it uses PostCSS so make sure to use Hugo extended version greater than v0.112.0.&lt;/p&gt;
&lt;h2 id="how-does-it-work"&gt;
 &lt;a class="anchor" href="#how-does-it-work"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 How does it work?
&lt;/h2&gt;
&lt;p&gt;According to the release note:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The basic concept is to add &lt;code&gt;hugo_stats.json&lt;/code&gt; to the server watcher list in Hugo and trigger a new TailwindCSS build only whenever either this file or the main CSS file changes.&lt;/p&gt;</description></item><item><title>Write pytest tests for argparse</title><link>https://imfing.com/til/write-pytest-tests-for-argparse/</link><pubDate>Thu, 22 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/write-pytest-tests-for-argparse/</guid><description>&lt;p&gt;Writing unit tests for Python code that uses &lt;a
 href="https://docs.python.org/3/library/argparse.html"
 
 target="_blank" rel="noopener"
 
&gt;argparse&lt;/a&gt;
 can be non-trivial.&lt;/p&gt;
&lt;h2 id="call-main-with-optional-arguments"&gt;
 &lt;a class="anchor" href="#call-main-with-optional-arguments"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Call &lt;code&gt;main&lt;/code&gt; with optional arguments
&lt;/h2&gt;
&lt;p&gt;One way suggested by &lt;a
 href="https://til.simonwillison.net/pytest/pytest-argparse"
 
 target="_blank" rel="noopener"
 
&gt;Simon Willison&lt;/a&gt;
 was to make &lt;code&gt;main()&lt;/code&gt; function take optional arguments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parsed_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes it easy to just test &lt;code&gt;main()&lt;/code&gt; function by calling it with different arguments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;option&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-h&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--help&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capsys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="patch-sysargv"&gt;
 &lt;a class="anchor" href="#patch-sysargv"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Patch &lt;code&gt;sys.argv&lt;/code&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s also possible to patch the &lt;code&gt;sys.argv&lt;/code&gt; with the mock arguments for testing:&lt;/p&gt;</description></item><item><title>Notes on "Building Systems with the ChatGPT API"</title><link>https://imfing.com/til/notes-on-building-systems-with-the-chatgpt-api/</link><pubDate>Sun, 11 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/notes-on-building-systems-with-the-chatgpt-api/</guid><description>&lt;p&gt;Course Link: &lt;a
 href="https://www.deeplearning.ai/short-courses/building-systems-with-chatgpt/"
 
 target="_blank" rel="noopener"
 
&gt;Building Systems with the ChatGPT API - DeepLearning.AI&lt;/a&gt;
&lt;/p&gt;
&lt;h2 id="introduction"&gt;
 &lt;a class="anchor" href="#introduction"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Introduction
&lt;/h2&gt;
&lt;p&gt;Process of building an application&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;supervised learning: usually takes long time
&lt;ul&gt;
&lt;li&gt;get labeled data&lt;/li&gt;
&lt;li&gt;train model on data&lt;/li&gt;
&lt;li&gt;deploy &amp;amp; call model&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;prompt-based AI: takes short time
&lt;ul&gt;
&lt;li&gt;specify prompt and call model&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="language-models"&gt;
 &lt;a class="anchor" href="#language-models"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Language Models
&lt;/h2&gt;
&lt;p&gt;How is works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A language model is built by using supervised learning to repeatedly predict the next word.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two types of LLMs&lt;/p&gt;</description></item><item><title>Full content RSS in Hugo</title><link>https://imfing.com/til/full-content-rss-in-hugo/</link><pubDate>Sun, 04 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/full-content-rss-in-hugo/</guid><description>&lt;p&gt;By default, &lt;a
 href="https://github.com/gohugoio/hugo"
 
 target="_blank" rel="noopener"
 
&gt;Hugo&lt;/a&gt;
 only displays a summary of the post.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;description&amp;gt;{{ .Summary | html }}&amp;lt;/description&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To enable a full text RSS feed for a section of my site, I added a &lt;code&gt;layouts/section/section.rss.xml&lt;/code&gt; template file according to &lt;a
 href="https://gohugo.io/templates/lookup-order/#examples-layout-lookup-for-section-pages"
 
 target="_blank" rel="noopener"
 
&gt;Hugo&amp;rsquo;s layout lookup order&lt;/a&gt;
. Fill it with the &lt;a
 href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml"
 
 target="_blank" rel="noopener"
 
&gt;default RSS layout template&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Modified the description part and add &lt;a
 href="https://www.w3.org/wiki/RssContent"
 
 target="_blank" rel="noopener"
 
&gt;RSS content&lt;/a&gt;
:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;&amp;lt;description&amp;gt;{{ with .Description | html }}{{ . }}{{ else }}{{ .Summary | html }}{{ end -}}&amp;lt;/description&amp;gt;
&amp;lt;content:encoded&amp;gt;{{ (printf &amp;#34;&amp;lt;![CDATA[%s]]&amp;gt;&amp;#34; .Content) | safeHTML }}&amp;lt;/content:encoded&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Verify the change by going to &lt;code&gt;https://localhost:1313/&amp;lt;section&amp;gt;/index.xml&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Build LLVM with CMake</title><link>https://imfing.com/til/build-llvm-with-cmake/</link><pubDate>Sat, 03 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/build-llvm-with-cmake/</guid><description>&lt;p&gt;The &lt;a
 href="https://github.com/llvm/llvm-project"
 
 target="_blank" rel="noopener"
 
&gt;LLVM&lt;/a&gt;
 Project is a collection of modular and reusable compiler and toolchain technologies.&lt;/p&gt;
&lt;h2 id="using-the-pre-built-distribution"&gt;
 &lt;a class="anchor" href="#using-the-pre-built-distribution"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Using the pre-built distribution
&lt;/h2&gt;
&lt;p&gt;In most cases, we can just directly download and use the pre-compiled version of LLVM and Clang from &lt;a
 href="https://github.com/llvm/llvm-project/releases"
 
 target="_blank" rel="noopener"
 
&gt;llvm-project/releases&lt;/a&gt;
. For example, &lt;a
 href="https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.0/clang&amp;#43;llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
 
 target="_blank" rel="noopener"
 
&gt;clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz&lt;/a&gt;
 is a pre-built distribution for Ubuntu 18.04 and for the x86-64 platforms.&lt;/p&gt;
&lt;h2 id="build-llvm-from-source"&gt;
 &lt;a class="anchor" href="#build-llvm-from-source"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Build LLVM from source
&lt;/h2&gt;
&lt;p&gt;In cases where there&amp;rsquo;s no pre-built version that suffice our needs, we can download and build LLVM from scratch.
For example, &lt;a
 href="https://github.com/openai/triton/tree/main/python"
 
 target="_blank" rel="noopener"
 
&gt;openai/triton&lt;/a&gt;
 Python library requires a LLVM compiler to build the wheel.&lt;/p&gt;</description></item><item><title>Customize GitHub Codespace using Dev Container</title><link>https://imfing.com/til/customize-github-codespace-using-devcontainer/</link><pubDate>Thu, 01 Jun 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/customize-github-codespace-using-devcontainer/</guid><description>&lt;p&gt;A &lt;a
 href="https://containers.dev/"
 
 target="_blank" rel="noopener"
 
&gt;Development Container&lt;/a&gt;
 (or Dev Container for short) allows you to use a container as a full-featured development environment.&lt;/p&gt;
&lt;p&gt;&lt;a
 href="https://github.com/features/codespaces"
 
 target="_blank" rel="noopener"
 
&gt;GitHub Codespace&lt;/a&gt;
 is a development environment that&amp;rsquo;s hosted in the cloud, which allows you to use VS Code directly from the browser to edit, commit and run projects. Whenever you work in a codespace, you are using a dev container on a virtual machine.&lt;/p&gt;
&lt;p&gt;The dev container configuration is located under &lt;code&gt;.devcontainer/devcontainer.json&lt;/code&gt;. There are a number of pre-built dev container images published under &lt;code&gt;mcr.microsoft.com/devcontainers&lt;/code&gt; on &lt;a
 href="https://github.com/devcontainers/images"
 
 target="_blank" rel="noopener"
 
&gt;devcontainers/images&lt;/a&gt;
.&lt;/p&gt;</description></item><item><title>Build multi-arch Docker images in GitHub Actions</title><link>https://imfing.com/til/build-multi-arch-docker-images-in-github-actions/</link><pubDate>Wed, 10 May 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/build-multi-arch-docker-images-in-github-actions/</guid><description>&lt;h2 id="publishing-images-to-github-packages"&gt;
 &lt;a class="anchor" href="#publishing-images-to-github-packages"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Publishing images to GitHub Packages
&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s say we have a repository that includes a &lt;code&gt;Dockerfile&lt;/code&gt;. We can utilize a GitHub workflow to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check out the repository&lt;/li&gt;
&lt;li&gt;Log in to the GitHub Container Registry (ghcr.io)&lt;/li&gt;
&lt;li&gt;Extract metadata&lt;/li&gt;
&lt;li&gt;Build and push the Docker image to our specified registry&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Create and publish a Docker image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;v*&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;REGISTRY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ghcr.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ github.repository }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build-and-push-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Log in to the Container registry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ env.REGISTRY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ github.actor }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Extract metadata (tags, labels) for Docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;meta&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Build and push Docker image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="build-and-publish-multi-arch-images"&gt;
 &lt;a class="anchor" href="#build-and-publish-multi-arch-images"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Build and publish multi-arch images
&lt;/h2&gt;
&lt;p&gt;Depends on the type of the host machines, images can be built for multiple platforms, e.g. &lt;code&gt;linux/amd64&lt;/code&gt;, &lt;code&gt;linux/arm64/v8&lt;/code&gt;, etc.&lt;/p&gt;</description></item><item><title>Caddy log filter</title><link>https://imfing.com/til/caddy-log-filter/</link><pubDate>Tue, 07 Feb 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/caddy-log-filter/</guid><description>&lt;p&gt;&lt;a
 href="https://github.com/caddyserver/caddy"
 
 target="_blank" rel="noopener"
 
&gt;Caddy&lt;/a&gt;
 is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go.&lt;/p&gt;
&lt;p&gt;It can be used as a reverse proxy server, for example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;:10001 {
 respond /healthz &amp;#34;OK&amp;#34; 200
 reverse_proxy /* http://localhost:8000 {
 header_up x-account &amp;#34;user&amp;#34;
 header_up x-secret-key &amp;#34;secret&amp;#34;
 }
 log
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above &lt;code&gt;Caddyfile&lt;/code&gt; will launch a http server listening on port &lt;code&gt;10001&lt;/code&gt; and will redirect requests to &lt;code&gt;localhost:8000&lt;/code&gt; and add two headers which contain sensitive information.
When the &lt;code&gt;debug&lt;/code&gt; mode is turned on, caddy&amp;rsquo;s internal logger will log down the requests from the reverse proxy to the localhost server, which will expose the sensitive info.&lt;/p&gt;</description></item><item><title>Shortcut for creating daily note</title><link>https://imfing.com/til/shortcut-for-creating-daily-note/</link><pubDate>Sat, 28 Jan 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/shortcut-for-creating-daily-note/</guid><description>&lt;p&gt;I like to jog down things on my Apple Note as it&amp;rsquo;s easier and fast to access whenever my iPhone is around. With iOS built-in Shortcuts app, creating a daily note entry can be just one click.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the shortcut I made:&lt;/p&gt;
&lt;p&gt;&lt;figure class="my-6"&gt;
 &lt;img
 src="https://user-images.githubusercontent.com/5097752/215294187-745c583f-977a-4e44-b31f-8896b1f36e1a.png"
 alt="Shortcut for creating daily note"
 
 data-zoomable
 class="rounded-lg mx-auto"
 loading="lazy"
 /&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Format current date to be &lt;code&gt;yyyy-MM-dd&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Find in all notes where the name contains such date string&lt;/li&gt;
&lt;li&gt;If exists, open the note&lt;/li&gt;
&lt;li&gt;If not, create a note with the formatted date as title&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Credit:&lt;/p&gt;</description></item><item><title>Decode Flask session cookie</title><link>https://imfing.com/til/decode-flask-session-cookie/</link><pubDate>Wed, 04 Jan 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/decode-flask-session-cookie/</guid><description>&lt;p&gt;Snippet to decode &lt;a
 href="https://flask.palletsprojects.com/en/2.2.x/"
 
 target="_blank" rel="noopener"
 
&gt;Flask&lt;/a&gt;
 session cookie.&lt;/p&gt;
&lt;p&gt;To find the cookie in Chrome, open Inspect -&amp;gt; Application -&amp;gt; Cookies, then find the &lt;code&gt;session&lt;/code&gt; cookie.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a short Python snippet that decodes the session cookie using &lt;a
 href="https://docs.python.org/3/library/zlib.html"
 
 target="_blank" rel="noopener"
 
&gt;zlib&lt;/a&gt;
 and &lt;code&gt;base64.urlsafe_b64decode&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;zlib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;base64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Decode a Flask cookie.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;compressed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;compressed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlsafe_b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;compressed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decompress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;utf-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[Decoding error: are you sure this was a Flask session cookie? &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s2"&gt;]&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reference:&lt;/p&gt;</description></item><item><title>Shortcut to copy url as markdown</title><link>https://imfing.com/til/shortcut-to-copy-url-as-markdown/</link><pubDate>Tue, 03 Jan 2023 00:00:00 +0000</pubDate><guid>https://imfing.com/til/shortcut-to-copy-url-as-markdown/</guid><description>&lt;p&gt;&lt;figure class="my-6"&gt;
 &lt;img
 src="https://user-images.githubusercontent.com/5097752/210312153-219cc8e1-560f-4c35-9f68-b46a45cc89c4.jpeg"
 alt="image"
 
 data-zoomable
 class="rounded-lg mx-auto"
 loading="lazy"
 /&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;This shortcut can copy a link from Safari share sheet and copy as Markdown format &lt;code&gt;[title](url]&lt;/code&gt; to the clipboard.&lt;/p&gt;
&lt;p&gt;&lt;figure class="my-6"&gt;
 &lt;img
 src="https://user-images.githubusercontent.com/5097752/210312483-57b414c7-7e74-43cb-bb82-7544d8ee52ab.jpeg"
 alt="Safari share sheet"
 
 data-zoomable
 class="rounded-lg mx-auto"
 loading="lazy"
 /&gt;&lt;/figure&gt;
&lt;/p&gt;</description></item><item><title>Markdown reference-style link</title><link>https://imfing.com/til/markdown-reference-style-link/</link><pubDate>Fri, 30 Dec 2022 00:00:00 +0000</pubDate><guid>https://imfing.com/til/markdown-reference-style-link/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Reference-style links are a special kind of link that make URLs easier to display and read in Markdown.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Instead of inline link: &lt;code&gt;[text](link)&lt;/code&gt;, the reference-style:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[text][label]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[label]: https://google.com &amp;#34;Google&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First part of the link is formatted with two sets of brackets.&lt;/p&gt;
&lt;p&gt;The second part of a reference-style link is formatted with the following attributes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The label, in brackets, followed immediately by a colon and at least one space (e.g., &lt;code&gt;[label]: &lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The URL for the link, which you can optionally enclose in angle brackets.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;optional&lt;/strong&gt; title for the link, which you can enclose in double quotes, single quotes, or parentheses.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See it in action:&lt;/p&gt;</description></item><item><title>Fn Option Delete</title><link>https://imfing.com/til/fn-option-delete/</link><pubDate>Fri, 16 Dec 2022 00:00:00 +0000</pubDate><guid>https://imfing.com/til/fn-option-delete/</guid><description>&lt;p&gt;As long time Mac user, I wasn&amp;rsquo;t aware that &amp;ldquo;forward delete&amp;rdquo; and &amp;ldquo;backward delete&amp;rdquo; exist 😂&lt;/p&gt;
&lt;p&gt;Backward delete &lt;kbd&gt;option + delete&lt;/kbd&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello world▐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello▐
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Forward delete &lt;kbd&gt;fn + option + delete&lt;/kbd&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello ▐world
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hello▐
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alternatively, I was using vim editing mode for quick cursor navigation and editing.&lt;/p&gt;</description></item><item><title>Lightroom backup workflow</title><link>https://imfing.com/til/lightroom-backup-workflow/</link><pubDate>Tue, 13 Dec 2022 00:00:00 +0000</pubDate><guid>https://imfing.com/til/lightroom-backup-workflow/</guid><description>&lt;p&gt;Midway through 2022, I began using &lt;a
 href="https://www.adobe.com/products/photoshop-lightroom.html"
 
 target="_blank" rel="noopener"
 
&gt;Lightroom Classic&lt;/a&gt;
 to manage and edit camera-captured images.
Now that the end of the year is approaching, I&amp;rsquo;ve decided to organize and back up the photos from this year on my &lt;a
 href="https://lacie.com/"
 
 target="_blank" rel="noopener"
 
&gt;Lacie&lt;/a&gt;
 portable hard drive so I can free up space on my MacBook Pro.&lt;/p&gt;
&lt;h2 id="setup"&gt;
 &lt;a class="anchor" href="#setup"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Setup
&lt;/h2&gt;
&lt;p&gt;All of my photos are stored in three distinct locations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MacBook Pro (500GB): ad-hoc editing&lt;/li&gt;
&lt;li&gt;Sandisk Extreme Portable SSD (1TB): bulk editing&lt;/li&gt;
&lt;li&gt;LaCie Portable Hard Drive (4TB): backup&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I usually bring my laptop with me when I travel. After each day I would import my photos directly into my laptop and edit them right away. Sometimes, I import the photos to the SSD drive. LaCie hard drive is mostly used for making backups.&lt;/p&gt;</description></item><item><title>Merge changes from GitHub template repository</title><link>https://imfing.com/til/merge-changes-from-github-template-repository/</link><pubDate>Tue, 06 Dec 2022 00:00:00 +0000</pubDate><guid>https://imfing.com/til/merge-changes-from-github-template-repository/</guid><description>&lt;p&gt;It&amp;rsquo;s very convenient to use &lt;a
 href="https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository"
 
 target="_blank" rel="noopener"
 
&gt;GitHub template repository&lt;/a&gt;
 feature to bootstrap a repository. I thought it would also have functionality like &lt;a
 href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork"
 
 target="_blank" rel="noopener"
 
&gt;Syncing a fork&lt;/a&gt;
, but unfortunately it doesn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;So to sync changes from upstream template repository, we need to use &lt;code&gt;git&lt;/code&gt; command line:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add remote&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git remote add template &amp;lt;repo&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch template changes&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git fetch --all
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use &lt;code&gt;--allow-unrelated-histories&lt;/code&gt; to merge, we may also need to manually resolve all the merge conflicts.&lt;/p&gt;</description></item><item><title>Manage GitHub project in Linear</title><link>https://imfing.com/til/manage-github-project-in-linear/</link><pubDate>Sun, 04 Dec 2022 00:00:00 +0000</pubDate><guid>https://imfing.com/til/manage-github-project-in-linear/</guid><description>&lt;p&gt;While I was working on my simple personal project: &lt;a
 href="https://github.com/imfing/issues-blog"
 
 target="_blank" rel="noopener"
 
&gt;issues-blog&lt;/a&gt;
, I experimented with &lt;a
 href="https://linear.app"
 
 target="_blank" rel="noopener"
 
&gt;Linear&lt;/a&gt;
 to manage this small project.&lt;/p&gt;
&lt;p&gt;Linear surprised me by being a powerful and elegant tool for managing issues and projects. It has nice and modern UI/UX, and more responsive than JIRA.&lt;/p&gt;
&lt;p&gt;&lt;figure class="my-6"&gt;
 &lt;img
 src="https://user-images.githubusercontent.com/5097752/205522813-2d5c2e1c-eb61-4709-a6a2-748399ff5439.png"
 alt="Screenshot of Linear app"
 
 data-zoomable
 class="rounded-lg mx-auto"
 loading="lazy"
 /&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="connect-linear-with-github"&gt;
 &lt;a class="anchor" href="#connect-linear-with-github"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Connect Linear with GitHub
&lt;/h2&gt;
&lt;p&gt;Linear comes with integration with GitHub of course: &lt;a
 href="https://linear.app/docs/github"
 
 target="_blank" rel="noopener"
 
&gt;GitHub - Linear Guide&lt;/a&gt;
. To enable it, go to &lt;code&gt;Settings&lt;/code&gt;, and under &lt;code&gt;Integrations -&amp;gt; GitHub&lt;/code&gt; we can connect Linear with GitHub pull requests.&lt;/p&gt;</description></item><item><title>College</title><link>https://imfing.com/about/college/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://imfing.com/about/college/</guid><description>&lt;p&gt;Brief summary of my experiences and projects during college 👨‍🎓&lt;/p&gt;
&lt;h2 id="internships"&gt;
 &lt;a class="anchor" href="#internships"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Internships
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Uber ATG&lt;/strong&gt;, Machine Learning Platform, 2020 Summer&lt;/p&gt;
&lt;p&gt;Pipeline and parallelize CI/CD for inference benchmarking and evaluation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Microsoft Research Asia&lt;/strong&gt;, Software Analytics Group, 2019 Spring&lt;/p&gt;
&lt;p&gt;Research on analyzing and assessing data visualizations with deep learning models.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Xiaomi Technology&lt;/strong&gt;, AI &amp;amp; Cloud Platform, 2018 Fall&lt;/p&gt;
&lt;p&gt;Model inference acceleration for production using NVIDIA TensorRT.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;University of British Columbia&lt;/strong&gt;, 2018 Summer&lt;/p&gt;</description></item><item><title>Uses</title><link>https://imfing.com/uses/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://imfing.com/uses/</guid><description>&lt;p&gt;👨🏻‍💻 Here&amp;rsquo;s a list of tech gears, hardware, and apps I use on a regular basis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Last updated: May 2026&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="hardware"&gt;
 &lt;a class="anchor" href="#hardware"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Hardware
&lt;/h3&gt;
&lt;h4 id="computer"&gt;
 &lt;a class="anchor" href="#computer"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Computer
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;14&amp;quot; MacBook Pro (M1 Pro 32GB + 512GB, 2021)&lt;/li&gt;
&lt;li&gt;IQUNIX MQ80&lt;/li&gt;
&lt;li&gt;HHKB Professional HYBRID Type-S&lt;/li&gt;
&lt;li&gt;Magic Trackpad&lt;/li&gt;
&lt;li&gt;BenQ RD280UG Monitor&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="audio"&gt;
 &lt;a class="anchor" href="#audio"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Audio
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Apple AirPods Pro 2&lt;/li&gt;
&lt;li&gt;Bose Noise Cancelling Headphones 700 (Soapstone)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="gaming--accessories"&gt;
 &lt;a class="anchor" href="#gaming--accessories"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Gaming / Accessories
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Nintendo Switch 2&lt;/li&gt;
&lt;li&gt;PlayStation 5&lt;/li&gt;
&lt;li&gt;Kindle Paperwhite 4 (2018)&lt;/li&gt;
&lt;li&gt;WHOOP 5.0&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="software"&gt;
 &lt;a class="anchor" href="#software"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Software
&lt;/h3&gt;
&lt;h4 id="coding"&gt;
 &lt;a class="anchor" href="#coding"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Coding
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Codex&lt;/li&gt;
&lt;li&gt;Claude Code&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="services"&gt;
 &lt;a class="anchor" href="#services"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Services
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;1Password&lt;/li&gt;
&lt;li&gt;Spotify&lt;/li&gt;
&lt;li&gt;Raindrop.io&lt;/li&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;Claude&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="tools"&gt;
 &lt;a class="anchor" href="#tools"&gt;
 &lt;span class="icon icon-link"&gt;&lt;/span&gt;
 &lt;/a&gt;
 Tools
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian&lt;/li&gt;
&lt;li&gt;Ghostty&lt;/li&gt;
&lt;li&gt;Things&lt;/li&gt;
&lt;li&gt;Anki&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>