<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Louis Liu</title>
    <description>The latest articles on Forem by Louis Liu (@louis7).</description>
    <link>https://forem.com/louis7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F929271%2Fd8eb2758-2c0a-4cc2-aec3-c3ed531cae08.png</url>
      <title>Forem: Louis Liu</title>
      <link>https://forem.com/louis7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/louis7"/>
    <language>en</language>
    <item>
      <title>Building a Mini Basketball Game as a VS Code Extension</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Sat, 28 Feb 2026 08:58:54 +0000</pubDate>
      <link>https://forem.com/louis7/building-a-mini-basketball-game-as-a-vs-code-extension-5dmb</link>
      <guid>https://forem.com/louis7/building-a-mini-basketball-game-as-a-vs-code-extension-5dmb</guid>
      <description>&lt;p&gt;I've always wanted to build my own game. Recently, I started learning game development. My first experimental project is a VS Code extension called &lt;strong&gt;Swipe Hoop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It's a basketball mini-game you can play inside VS Code. I wanted to call it "Swipe Basketball" but due to VS Code Marketplace review policies, I couldn't use words like "basketball" or "game" in the extension's name.&lt;/p&gt;

&lt;p&gt;It’s a small experiment, but it was surprisingly fun to build.&lt;/p&gt;

&lt;p&gt;You can install the extension through &lt;a href="https://marketplace.visualstudio.com/items?itemName=louis-liu.swipe-hoops-vscode" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt; or just search Swipe Hoop in VS Code. Hope you enjoy the game!&lt;/p&gt;

&lt;h2&gt;
  
  
  Gameplay
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hpiwckubk6h235uki69.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hpiwckubk6h235uki69.png" alt="gameplay1" width="494" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxydrttc9zf8b27wy108.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxydrttc9zf8b27wy108.gif" alt="demo" width="720" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stop Copy-Pasting to ChatGPT — Meet Snap Mind</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Sat, 14 Feb 2026 08:34:51 +0000</pubDate>
      <link>https://forem.com/louis7/stop-copy-pasting-to-chatgpt-meet-snap-mind-me</link>
      <guid>https://forem.com/louis7/stop-copy-pasting-to-chatgpt-meet-snap-mind-me</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-01-21"&gt;GitHub Copilot CLI Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;Snap Mind&lt;/strong&gt;, an AI-powered desktop assistant that brings large language models directly into your daily workflow — without context switching.&lt;/p&gt;

&lt;p&gt;The core idea is simple but powerful:&lt;br&gt;&lt;br&gt;
👉 Select any text&lt;br&gt;&lt;br&gt;
👉 Press a hotkey&lt;br&gt;&lt;br&gt;
👉 Instantly get AI help&lt;/p&gt;

&lt;p&gt;Instead of opening ChatGPT or switching tabs, Snap Mind lets you translate, rewrite, summarize, or explain text right where you work. The goal is to make AI feel native to the operating system — fast, invisible, and always within reach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Blazing Fast&lt;/strong&gt; — trigger AI with a single keystroke
&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Works Anywhere&lt;/strong&gt; — usable inside any application
&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Custom Prompts&lt;/strong&gt; — tailor AI actions to your workflow
&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Multi-purpose&lt;/strong&gt; — translation, summarization, rewriting, explanations, and more
&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Zero context switching&lt;/strong&gt; — stay in your flow state
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why I Built This
&lt;/h3&gt;

&lt;p&gt;While LLMs are powerful, the friction of opening web apps and copy-pasting text breaks focus. I wanted AI to feel like a native superpower — something that responds instantly to intent.&lt;/p&gt;

&lt;p&gt;Snap Mind is my attempt to make AI interaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;faster
&lt;/li&gt;
&lt;li&gt;more contextual
&lt;/li&gt;
&lt;li&gt;and more ergonomic for everyday work&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🔗 GitHub Repo: &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;https://github.com/Snap-Mind/snap-mind&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 Website: &lt;a href="https://snap-mind.github.io/" rel="noopener noreferrer"&gt;https://snap-mind.github.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Translate selected text instantly
&lt;/li&gt;
&lt;li&gt;Rewrite emails to sound more professional
&lt;/li&gt;
&lt;li&gt;Summarize long articles
&lt;/li&gt;
&lt;li&gt;Explain code snippets in place
&lt;/li&gt;
&lt;li&gt;Generate quick replies without leaving your editor
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot CLI significantly accelerated my development workflow while building Snap Mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I Used It
&lt;/h3&gt;

&lt;p&gt;I used Copilot CLI to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scaffold boilerplate code
&lt;/li&gt;
&lt;li&gt;generate shell commands
&lt;/li&gt;
&lt;li&gt;speed up debugging workflows
&lt;/li&gt;
&lt;li&gt;explore unfamiliar APIs quickly
&lt;/li&gt;
&lt;li&gt;iterate on prompt templates
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One particularly helpful workflow was using Copilot CLI to quickly generate and refine command-line interactions during early prototyping. It reduced the cognitive overhead of context switching and let me stay focused on product design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Impact on My Development Experience
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What worked well:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🚀 Faster iteration loops
&lt;/li&gt;
&lt;li&gt;🧩 Reduced time spent on repetitive tasks
&lt;/li&gt;
&lt;li&gt;🔍 Helpful for exploring unfamiliar code patterns
&lt;/li&gt;
&lt;li&gt;💡 Great for quick command suggestions
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copilot CLI shines most when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;give clear, focused prompts
&lt;/li&gt;
&lt;li&gt;use it interactively during development
&lt;/li&gt;
&lt;li&gt;treat it as a collaborator, not an autopilot
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, it helped me move from idea → prototype → usable tool much faster.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>My New Year Portfolio Challenge!</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Fri, 23 Jan 2026 10:52:20 +0000</pubDate>
      <link>https://forem.com/louis7/my-new-year-portfolio-challenge-g18</link>
      <guid>https://forem.com/louis7/my-new-year-portfolio-challenge-g18</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/new-year-new-you-google-ai-2025-12-31"&gt;New Year, New You Portfolio Challenge Presented by Google AI&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About Me
&lt;/h2&gt;

&lt;p&gt;I'm Louis, a full-stack web developer with a strong focus on web technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Portfolio
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__cloud-run"&gt;
  &lt;iframe height="600px" src="https://dev-valley-315140115302.us-west1.run.app/"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;




&lt;p&gt;Or access it in your browser: &lt;a href="https://dev-valley-315140115302.us-west1.run.app/" rel="noopener noreferrer"&gt;https://dev-valley-315140115302.us-west1.run.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I have always wanted to create a personal homepage that reflects my unique style. As a big fan of video games, I want to have gaming elements in my page. Based on this idea, I used Google AI Studio to bring this personal homepage to life.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Most Proud Of
&lt;/h2&gt;

&lt;p&gt;98% of the code was written through vibe coding. I'm very pleased that I successfully implemented map switching and interactions between characters and objects. Furthermore, I added a mini-game (here's a hint: it's a fishing game). Although the art style isn't perfect and there is a lack of instructions, I think I've done a good enough job.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>googleaichallenge</category>
      <category>portfolio</category>
      <category>gemini</category>
    </item>
    <item>
      <title>What Really Happens When an LLM Chooses the Next Token🤯</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Mon, 12 Jan 2026 03:16:54 +0000</pubDate>
      <link>https://forem.com/louis7/what-really-happens-when-an-llm-chooses-the-next-token-19n7</link>
      <guid>https://forem.com/louis7/what-really-happens-when-an-llm-chooses-the-next-token-19n7</guid>
      <description>&lt;p&gt;LLM outputs sometimes feel stable. Sometimes they suddenly become random.&lt;/p&gt;

&lt;p&gt;Often, the only thing that changed is a parameter.&lt;/p&gt;

&lt;p&gt;So what actually happens at the moment an LLM decides &lt;strong&gt;which token comes next&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;The short answer is simple:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;it samples one token from a probability distribution.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s break that moment down with visuals.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;Given a prompt, the model predicts a probability distribution over possible next tokens.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Twinkle twinkle little&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, the model assigns a probability to each candidate token.&lt;br&gt;&lt;br&gt;
You can imagine them laid out on a 0–100 scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher probability → larger segment
&lt;/li&gt;
&lt;li&gt;Lower probability → smaller segment
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbo52f112hw1pktlypu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxbo52f112hw1pktlypu1.png" alt="Probability Distribution Chart" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Sampling: What Actually Happens
&lt;/h2&gt;

&lt;p&gt;Next comes &lt;strong&gt;sampling&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A practical way to think about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Generate a random number&lt;br&gt;&lt;br&gt;
See which segment it falls into&lt;br&gt;&lt;br&gt;
Output the corresponding token&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since &lt;strong&gt;“star”&lt;/strong&gt; has the largest segment, it’s the most likely result:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Twinkle twinkle little star&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Temperature, Top-p, and Top-k&lt;br&gt;&lt;br&gt;
&lt;strong&gt;only affect this sampling step.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From here on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temperature = 1
&lt;/li&gt;
&lt;li&gt;Top-p = 1
&lt;/li&gt;
&lt;li&gt;Top-k = 10
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll change one parameter at a time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Temperature
&lt;/h2&gt;

&lt;p&gt;Temperature does one thing:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;it stretches or flattens probability differences.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lower temperature → strong preferences → stable output
&lt;/li&gt;
&lt;li&gt;Higher temperature → flatter distribution → more randomness
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The gap between &lt;strong&gt;“star”&lt;/strong&gt; and &lt;strong&gt;“car”&lt;/strong&gt; is &lt;strong&gt;19.6&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;With Temperature = 0.5, the gap grows to &lt;strong&gt;36.1&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zfj5gtce245juisa0xb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zfj5gtce245juisa0xb.gif" alt="Temperature demo 1" width="720" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Temperature = 1.68, lower-probability tokens become more competitive:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwg10iel57lyrl2ftwfe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwg10iel57lyrl2ftwfe.gif" alt="Temperature demo 2" width="720" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key point:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Temperature doesn’t remove tokens.&lt;br&gt;&lt;br&gt;
It only changes how strongly the model prefers one over another.&lt;/p&gt;




&lt;h2&gt;
  
  
  Top-p (Nucleus Sampling)
&lt;/h2&gt;

&lt;p&gt;Top-p controls &lt;strong&gt;how much probability mass is kept&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The process is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start from the highest-probability token
&lt;/li&gt;
&lt;li&gt;Keep adding tokens until cumulative probability ≥ Top-p
&lt;/li&gt;
&lt;li&gt;Drop the rest
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Top-p = 0.6, only tokens covering 60% of total probability remain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jfblcmxd13z41a3zw9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jfblcmxd13z41a3zw9s.png" alt="Top-p demo 1" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The remaining tokens are then renormalized:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7y2fhuoazxqdjh7vjqt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7y2fhuoazxqdjh7vjqt.gif" alt="Top-p demo 2" width="720" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of tokens is dynamic
&lt;/li&gt;
&lt;li&gt;More peaked distributions keep fewer tokens
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Top-k
&lt;/h2&gt;

&lt;p&gt;Top-k is simpler.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep only the top K tokens.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Top-k = 1 → always pick the most likely token
&lt;/li&gt;
&lt;li&gt;Top-k = 5 → sample from the top 5
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else is ignored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5a3f6ss914hmlbn8zsv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5a3f6ss914hmlbn8zsv.gif" alt="Top-k demo 1" width="600" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In one line:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Top-k limits quantity
&lt;/li&gt;
&lt;li&gt;Top-p limits probability mass
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;All visuals in this article come from:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://louis-7.github.io/llm-sampling-visualizer/" rel="noopener noreferrer"&gt;LLM Sampling Visualizer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If sampling parameters feel abstract,&lt;br&gt;&lt;br&gt;
five minutes with this tool builds intuition faster than reading more text.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=geEDnyFY_dw" rel="noopener noreferrer"&gt;Temperature &amp;amp; Top-p: controlling creativity in LLMs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andreban.github.io/temperature-topk-visualizer/" rel="noopener noreferrer"&gt;Temperature, Top-K and Top-P visualization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Git Deep Dive: Mastering rerere</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Wed, 12 Nov 2025 18:00:00 +0000</pubDate>
      <link>https://forem.com/louis7/git-deep-dive-mastering-rerere-12jm</link>
      <guid>https://forem.com/louis7/git-deep-dive-mastering-rerere-12jm</guid>
      <description>&lt;p&gt;No, It's Not a Typo: Let's Talk About &lt;code&gt;git rerere&lt;/code&gt; (Reuse Recorded Resolution)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git rerere&lt;/code&gt; helps Git remember how you resolved a conflict previously, and automatically reuses that resolution when the same conflict occurs again. This can be a huge time-saver with recurring merge conflicts.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to enabled it
&lt;/h2&gt;

&lt;p&gt;Globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; rerere.enabled &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Per project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config rerere.enabled &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--get&lt;/span&gt; rerere.enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;I'm updating &lt;code&gt;hello.txt&lt;/code&gt; on &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;feature/earth&lt;/code&gt; branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;main&lt;/code&gt; branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello World!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;feature/earth&lt;/code&gt; branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello Earth!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if we try to merge &lt;code&gt;feature/earth&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;, we’ll get a conflict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout main
git merge feature/earth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conflict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
&lt;/span&gt;&lt;span class="p"&gt;Hello World!
&lt;/span&gt;&lt;span class="gh"&gt;=======
&lt;/span&gt;&lt;span class="p"&gt;Hello Earth!
&lt;/span&gt;&lt;span class="gi"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature/earth
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We edit the file to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello Universe!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then stage and commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add hello.txt
git commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will record the resolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Recorded resolution for 'hello.txt'.
[main 7fd7eda] Merge branch 'feature/earth'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next time Git encounters the same conflict, it will automatically reuse the previous resolution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge feature/earth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Resolved 'hello.txt' using previous resolution.
Automatic merge failed; fix conflicts and then commit the result.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Useful commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Manually trigger rerere&lt;/span&gt;
git rerere
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Forget a recored resolution&lt;/span&gt;
git rerere forget &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check the rerere cache&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; .git/rr-cache/
&lt;span class="nb"&gt;cat&lt;/span&gt; .git/rr-cache/&amp;lt;conflict-id&amp;gt;/preimage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t want Git to auto-resolve using &lt;code&gt;rerere&lt;/code&gt;, you can reset the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;--conflict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;merge &amp;lt;file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Advanced
&lt;/h2&gt;

&lt;p&gt;When Git detects a conflict during a merge or rebase, it generates a hash key based on the conflicting content — this becomes the &lt;code&gt;conflict-id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Git then stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;preimage&lt;/strong&gt; (conflicted content)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;postimage&lt;/strong&gt; (your resolved version)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are saved under &lt;code&gt;.git/rr-cache/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  File Structure:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.git/
└── rr-cache/
    ├── &amp;lt;conflict-id&amp;gt;/
    │   ├── preimage      # The file before conflict resolution
    │   ├── postimage     # The file after conflict resolution
    │   └── thisimage     # Temporary file used during auto-resolution
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Important Notes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;conflict-id&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; a Git object, even though it looks like one.&lt;/li&gt;
&lt;li&gt;It’s stored &lt;strong&gt;locally only&lt;/strong&gt; — it won’t be pushed to the remote.&lt;/li&gt;
&lt;li&gt;If you delete &lt;code&gt;.git/rr-cache&lt;/code&gt;, the resolution is &lt;strong&gt;gone forever&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git rerere&lt;/code&gt; is a powerful but often overlooked tools. It can save you a lot of time when dealing with recurring merge conflicts.&lt;/p&gt;

</description>
      <category>git</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>🧠 Bringing AI to One Keystroke</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Tue, 21 Oct 2025 00:47:43 +0000</pubDate>
      <link>https://forem.com/louis7/bringing-ai-to-one-keystroke-5kn</link>
      <guid>https://forem.com/louis7/bringing-ai-to-one-keystroke-5kn</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest2025"&gt;2025 Hacktoberfest Writing Challenge&lt;/a&gt;: Maintainer Spotlight&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AI is powerful — but using it still feels… awkward.&lt;br&gt;
Every time I want AI to help me rewrite a sentence, summarize a paragraph, or translate a message, I have to switch apps, paste text, and then jump back into my workflow. It breaks focus.&lt;/p&gt;

&lt;p&gt;I live inside Slack, Notion, VS Code, and a dozen other tools every day. I wanted AI to feel invisible — something that’s just there, one keystroke away.&lt;/p&gt;

&lt;p&gt;That’s how SnapMind was born.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 What Is SnapMind?
&lt;/h2&gt;

&lt;p&gt;SnapMind is a desktop AI assistant that brings LLMs directly into your daily workflow.&lt;/p&gt;

&lt;p&gt;Select any text, hit a hotkey, and instantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;✍️ Rewrite or polish your content&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🌍 Translate text on the fly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;📑 Summarize long paragraphs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;💡 Explain code or concepts — without leaving your current app&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s like having ChatGPT, DeepL, and Grammarly in one lightweight popup — always ready, never intrusive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wez6vsrjc7hbfcseyd5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wez6vsrjc7hbfcseyd5.gif" alt="SnapMind Demo" width="600" height="340"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡️ What makes SnapMind Special
&lt;/h2&gt;

&lt;p&gt;SnapMind isn’t just another AI app — it’s a workflow enhancer.&lt;br&gt;
It’s built around three core principles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Speed: AI should be accessible in a single keystroke.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Seamlessness: Stay in flow — no context switching.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Customization: Save and trigger your favorite prompts instantly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ❤️ Why I Love Maintaining SnapMind
&lt;/h2&gt;

&lt;p&gt;I really appreciate the convenience that AI brings, but most AI applications haven't truly integrated into my work. I still need to frequently switch between different products to achieve my goals. I hope there can be software that truly integrates into my daily work, acting like an invisible partner to help me complete tasks when I need it. This is also the reason SnapMind was created.&lt;/p&gt;

&lt;p&gt;I’m deeply grateful to everyone who’s starred the repo. I'm hoping there are more people who will open issues or share feedback. Together, we’re building the kind of AI tool we actually want to use.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌍 Join Me
&lt;/h2&gt;

&lt;p&gt;If you believe AI should be simple, fast, and seamlessly integrated into your workflow — join me!&lt;/p&gt;

&lt;p&gt;⭐️ Star SnapMind on GitHub&lt;br&gt;
💬 Open an issue or PR&lt;br&gt;
🌟 Share your favorite use cases&lt;/p&gt;

&lt;p&gt;Let’s make AI one keystroke away.&lt;/p&gt;

&lt;p&gt;GitHub SnapMind 👉 &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;https://github.com/Snap-Mind/snap-mind&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a Custom Key Binding Recorder in React</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Thu, 02 Oct 2025 12:31:26 +0000</pubDate>
      <link>https://forem.com/louis7/building-a-custom-key-binding-recorder-in-react-3478</link>
      <guid>https://forem.com/louis7/building-a-custom-key-binding-recorder-in-react-3478</guid>
      <description>&lt;p&gt;I’ve been working on a desktop AI assistant — &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;SnapMind&lt;/a&gt; — that lets you instantly interact with LLMs from anywhere on your system. One of the core features is triggering prompts with customizable hotkeys.&lt;/p&gt;

&lt;p&gt;By default, the app comes with built-in hotkeys, but I wanted users to be able to define their own key bindings.&lt;/p&gt;

&lt;p&gt;At first, I considered using a third-party library for this. But I soon realized it wasn’t too hard to build my own lightweight recorder, so I decided to implement it from scratch. In this post, I’ll share the key points of how I designed this component.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rules
&lt;/h2&gt;

&lt;p&gt;The component should allow users to provide up to 2 modifier keys and one main key. Once a valid key binding is captured, the recording should stop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start listening when the input is focused
&lt;/li&gt;
&lt;li&gt;The final key binding = up to 2 modifiers + 1 main key
&lt;/li&gt;
&lt;li&gt;Automatically commit once a valid combination is captured (≥1 modifier + 1 main key)
&lt;/li&gt;
&lt;li&gt;Provide a reset button to clear user inputs
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  States
&lt;/h2&gt;

&lt;p&gt;Here’s how I structured the component’s state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;keybindings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setKeybindings&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// The final result&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;modKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setModKeys&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// Active modifiers captured (max 2)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mainKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMainKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Main (non-modifier) key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recording&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRecording&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Indicate recording state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Input reference&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Listen to key down
&lt;/h2&gt;

&lt;p&gt;Initially, I wanted the component to display key presses in real time — for example, showing &lt;code&gt;Ctrl + Shift + C&lt;/code&gt; when held, then &lt;code&gt;Ctrl + C&lt;/code&gt; once &lt;code&gt;Shift&lt;/code&gt; is released. That would have been cool, but I realized a simpler approach was enough: just capture the combination on key down and persist it until reset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onKeyDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KeyboardEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;recording&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;keybindings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextMods&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modKeys&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Use Set here so it won't record duplicated keys&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;nextMain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isModifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle keys like Ctrl and Shift.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// We're using Set, so I don't care about capture duplicate modifiers&lt;/span&gt;
      &lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nextMain&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isAllowedMainKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// I don't want to capture keys like F1~F12 and other special keys&lt;/span&gt;
    &lt;span class="nx"&gt;nextMain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Update state if changed&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMain&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;mainKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setMainKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;modKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;modKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setModKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Attempt to finalize immediately once we have both parts&lt;/span&gt;
  &lt;span class="nf"&gt;attemptCommit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextMods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextMain&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Display recorded keys
&lt;/h2&gt;

&lt;p&gt;Displaying the result is straightforward. I also reorder the modifiers so users won’t see something like &lt;code&gt;Shift + Ctrl + C&lt;/code&gt; (which feels unintuitive). The captured key names from the browser event are mapped to more readable labels.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;orderModifiers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mac&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rankMac&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;meta&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 class="na"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rankOther&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;control&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 class="na"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mac&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rankMac&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rankOther&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;mods&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;99&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 class="nx"&gt;rank&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;printModifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;meta&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Command&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Option&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ctrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shift&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Shift&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalizeMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;a-z&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;$/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^f&lt;/span&gt;&lt;span class="se"&gt;\d{1,2}&lt;/span&gt;&lt;span class="sr"&gt;$/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Esc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Space&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;arrowup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;arrowdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Down&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;arrowleft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;arrowright&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Right&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Enter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Tab&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;backspace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Backspace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatDisplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mac&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;other&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;orderModifiers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;printModifier&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ordered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;orderModifiers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;printModifier&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;ordered&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;normalizeMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;I added a 🔴 indicator to show whether the component is currently recording.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3j4a2vfobhy7t3n8q7q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3j4a2vfobhy7t3n8q7q.gif" alt="Demo" width="720" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/Snap-Mind/snap-mind/blob/24b9f76b0376706e5616b712bdc763fe3ecd4c30/src/components/HotkeyRecorder.tsx" rel="noopener noreferrer"&gt;source code&lt;/a&gt; to the component.&lt;/p&gt;




&lt;p&gt;A few years ago, I would have reached for a third-party library whenever I needed functionality like this. Now, with help from AI, I find it faster and cleaner to implement simple business logic myself.&lt;/p&gt;

&lt;p&gt;If this sounds interesting, feel free to give SnapMind a try — I’d love to hear your feedback!&lt;/p&gt;

&lt;p&gt;GitHub Repo: &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;https://github.com/Snap-Mind/snap-mind&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>programming</category>
    </item>
    <item>
      <title>Letting AI Build My App's Auto Updates (and Fixing the Pitfalls)</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Thu, 25 Sep 2025 15:49:55 +0000</pubDate>
      <link>https://forem.com/louis7/letting-ai-build-my-apps-auto-updates-and-fixing-the-pitfalls-4pke</link>
      <guid>https://forem.com/louis7/letting-ai-build-my-apps-auto-updates-and-fixing-the-pitfalls-4pke</guid>
      <description>&lt;p&gt;It’s been two weeks since I last shared an update about my AI assistant project — &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;SnapMind&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;The development experience was smooth with GitHub Copilot, especially when I added auto-update feature. I didn’t start by reading the Electron Updater documentation. Instead, I asked AI to generate an initial implementation for me. After I figured out how it works, I read the documentation to verify the update workflow.&lt;/p&gt;

&lt;p&gt;Of course, not everything went perfectly. On my Apple Silicon MacBook, the auto-updater mistakenly installed the x86 version of the app instead of the ARM build. Let’s just say it was a confusing few minutes before I realized what happened 😅😂 The AI doesn't know my build artifact structure, so it didn't remind me that the Electron updater could take the wrong YAML file. Context management is crucial.&lt;/p&gt;




&lt;p&gt;Let's take a look at the updates!&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto Updates
&lt;/h2&gt;

&lt;p&gt;Now you don’t need to manually download new installers from GitHub. You can check for and install updates directly inside the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1pie3oav9et8ym1naqc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1pie3oav9et8ym1naqc.png" alt=" " width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More Providers
&lt;/h2&gt;

&lt;p&gt;DeepSeek and Qwen are supported.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasusrh6m538pbno63fbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasusrh6m538pbno63fbd.png" alt="more providers" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customize Hotkeys (Beta)
&lt;/h2&gt;

&lt;p&gt;You can now set your own hotkeys to trigger prompts.&lt;br&gt;
This should’ve been there from the start, but I want the setup experience to feel great, so it’s still in beta for now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Currently, beta version can only be downloaded through &lt;a href="https://github.com/Snap-Mind/snap-mind/releases/tag/v0.2.0-beta.1" rel="noopener noreferrer"&gt;GitHub Release&lt;/a&gt; page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxijk8mo6me2y1bt08t4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxijk8mo6me2y1bt08t4.png" alt="customize hotkeys" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;There are already great desktop apps like ChatBox, DeepChat, LM Studio, and Cherry Studio. They support multiple LLMs, MCP servers, and many other advanced features.&lt;/p&gt;

&lt;p&gt;But here’s the thing: I got tired of constantly copying and pasting text or switching apps just to ask AI a question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SnapMind&lt;/strong&gt; is designed to work directly with the content you already have — no extra steps, no wasted time. &lt;/p&gt;




&lt;p&gt;🙌 I’m building SnapMind to make AI truly practical for daily workflows. What’s your use case? Share your routine in the comments — I’d love to see how SnapMind can help you. Give me a star🌟 on GitHub if you think &lt;strong&gt;SnapMind&lt;/strong&gt; could help you!&lt;/p&gt;

&lt;p&gt;Project link: &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;https://github.com/Snap-Mind/snap-mind&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Is AI Pushing Software Engineers to the Next Efficiency Test?</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Thu, 18 Sep 2025 12:07:10 +0000</pubDate>
      <link>https://forem.com/louis7/is-ai-pushing-software-engineers-to-the-next-efficiency-test-52de</link>
      <guid>https://forem.com/louis7/is-ai-pushing-software-engineers-to-the-next-efficiency-test-52de</guid>
      <description>&lt;p&gt;The momentum of AI development remains strong, and it has become an unavoidable topic in our daily lives. Since AI entered our work routines, discussions about whether it might replace humans have been ongoing. In software development, this question is particularly relevant: is AI a productivity booster, or does it present new challenges for software engineers?  &lt;/p&gt;




&lt;h2&gt;
  
  
  AI in Real-World Development Scenarios
&lt;/h2&gt;

&lt;p&gt;Earlier this year, my organization purchased GitHub Copilot licenses for our team. Naturally, any investment comes with an expectation of return, so the question of “how to leverage AI to improve work efficiency” quickly became a topic on the table.  &lt;/p&gt;

&lt;p&gt;Efficiency is often measured directly: a feature that used to take a full day to develop—can it now be completed in half a day? Many imagine AI as a “magic tool” that can instantly turn any developer into an experienced engineer. Online stories often emphasize &lt;strong&gt;speed&lt;/strong&gt;—people with little programming experience have used AI to quickly build a small product and even launch it.  &lt;/p&gt;

&lt;p&gt;Before seeing these cases, my view of AI was mostly positive. I enjoyed using AI to help write code because it reduces repetitive work and frees me from thinking about trivial details. But when efficiency becomes the main focus, I began to hesitate: &lt;strong&gt;can AI really help me deliver more within the same amount of time?&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  The Cost of Speed
&lt;/h2&gt;

&lt;p&gt;When discussing AI-assisted coding, the first thing people mention is &lt;strong&gt;speed&lt;/strong&gt;: features launch faster, iterations progress quicker, and delivery cycles shorten. But in software engineering, is speed the only metric that matters?  &lt;/p&gt;

&lt;p&gt;In a business context, software engineers typically work as part of a team, which is very different from an individual building a product from scratch. Can AI truly integrate into an existing codebase? While enjoying the benefits AI brings, we also need to consider potential risks.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Different developers may use prompts differently, resulting in AI-generated code with inconsistent styles. Time saved in the short term may lead to long-term maintenance costs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency&lt;/strong&gt;: Over-reliance on AI, even for simple tasks, may actually reduce overall efficiency.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality&lt;/strong&gt;: AI can generate large volumes of code quickly, but these outputs still require human review. If reviews fall behind, potential issues may go unnoticed.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accuracy&lt;/strong&gt;: When generated code doesn’t match expectations, humans still need to refactor or regenerate it.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability&lt;/strong&gt;: Seemingly completed features may later demand extra time for maintenance due to messy structure or poor readability.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Speed has its value, but if the focus is solely on being fast, the result may ultimately become unmanageable.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Value Beyond Speed
&lt;/h2&gt;

&lt;p&gt;Many articles discussing AI-assisted development focus on &lt;strong&gt;rapid iteration&lt;/strong&gt; and &lt;strong&gt;delivery speed&lt;/strong&gt;. I believe it’s more important to recognize &lt;strong&gt;AI’s ability to help developers focus on complex problems and improve accuracy&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;As noted by IBM in their &lt;a href="https://www.ibm.com/cn-zh/think/topics/ai-in-software-development" rel="noopener noreferrer"&gt;AI in Software Development&lt;/a&gt; article:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Gen AI assists in code generation and automates repetitive coding tasks. Gen AI-powered tools help developers focus on complex problems, while AI-driven autocompletion and real-time suggestions improve speed and accuracy. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, AI is not simply about “writing code faster.” Its true value lies in freeing developers from repetitive work and allowing them to focus on truly complex and creative tasks.  &lt;/p&gt;




&lt;h2&gt;
  
  
  A Broader Perspective
&lt;/h2&gt;

&lt;p&gt;Software development involves more than just the engineering team. Requirements gathering, analysis, UI/UX design, maintenance support, and product documentation can all leverage AI to improve efficiency. These areas are often overlooked, yet they represent some of the most untapped potential for AI to add value.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Defining Efficiency in Practice
&lt;/h2&gt;

&lt;p&gt;So, can AI truly improve software development efficiency? This is a question that can only be answered through practice.  &lt;/p&gt;

&lt;p&gt;If we start with the assumption that “AI saves time” and work backward from there, teams may face risks. Efficiency is not just speed; it is &lt;strong&gt;stable delivery, consistent style, reliable quality, and maintainability over time&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Coding is not only about being “fast”; it also requires coordination and rhythm within the team. The value of AI may not lie in compressing a week’s work into three days, but in allowing developers to focus their limited energy where it matters most.  &lt;/p&gt;

&lt;p&gt;This is not the first time a method or tool has challenged team efficiency. The previous wave was agile development. Many companies, in adopting agile, focus on speed, but often simply increase pressure on the team without fully understanding agile principles. AI is similar: on the surface, it promises efficiency, but its true value lies in understanding the underlying principles and applying them thoughtfully.  &lt;/p&gt;

&lt;p&gt;AI is not a simple accelerator—it is an opportunity for us to redefine what efficiency truly means.  &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.ibm.com/cn-zh/think/topics/ai-in-software-development" rel="noopener noreferrer"&gt;IBM: AI in Software Development&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>vibecoding</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Bring AI Into Any App With One Keystroke</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Wed, 10 Sep 2025 05:21:40 +0000</pubDate>
      <link>https://forem.com/louis7/bring-ai-into-any-app-with-one-keystroke-4ono</link>
      <guid>https://forem.com/louis7/bring-ai-into-any-app-with-one-keystroke-4ono</guid>
      <description>&lt;p&gt;AI is powerful, but the way we interact with it still feels clunky. Most of the time, I have to open a browser or launch a desktop app, type in my question, and then copy the result back into my workflow.&lt;/p&gt;

&lt;p&gt;I reply to numerous messages on Slack every day. If I want AI to rewrite something for me, I need to copy the text, paste it into ChatGPT, adjust the response, and then copy it back. It works—but it’s disruptive.&lt;/p&gt;

&lt;p&gt;What if AI could be just one keystroke away, seamlessly integrated into whatever I’m doing? That’s the idea behind &lt;strong&gt;SnapMind&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s SnapMind?
&lt;/h2&gt;

&lt;p&gt;SnapMind is a desktop AI assistant that brings LLMs anywhere in your system. Just &lt;strong&gt;select text + hit a hotkey&lt;/strong&gt;, and you can instantly:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Translate
&lt;/li&gt;
&lt;li&gt;Rewrite
&lt;/li&gt;
&lt;li&gt;Summarize
&lt;/li&gt;
&lt;li&gt;Explain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;— all without leaving the app you’re working in.&lt;/p&gt;

&lt;p&gt;In short: it’s a &lt;strong&gt;quick prompt launcher&lt;/strong&gt; for your daily workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Bind your favorite prompts to hotkeys, and trigger them instantly on selected text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08g9ee7qkdvgu31cbz2a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F08g9ee7qkdvgu31cbz2a.gif" alt="SnapMind Demo" width="560" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Blazing Fast&lt;/strong&gt; — AI in a single keystroke
&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Seamless Experience&lt;/strong&gt; — Works in any app without breaking your flow
&lt;/li&gt;
&lt;li&gt;🛠 &lt;strong&gt;Highly Customizable&lt;/strong&gt; — Save prompts for the tasks you do most&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌍 &lt;strong&gt;Instant Translation&lt;/strong&gt; — Translate any text on the fly
&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Polish &amp;amp; Rewrite&lt;/strong&gt; — Make content clearer, shorter, or more professional
&lt;/li&gt;
&lt;li&gt;📑 &lt;strong&gt;Summarize in Seconds&lt;/strong&gt; — Get key takeaways from long reports
&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Quick Explanations&lt;/strong&gt; — Highlight concepts, code, or terms for clarity
&lt;/li&gt;
&lt;li&gt;📬 &lt;strong&gt;Smart Drafting&lt;/strong&gt; — Generate emails, replies, or snippets in seconds
&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Learning Companion&lt;/strong&gt; — Check grammar, simplify content, aid understanding
&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;On-the-fly Brainstorming&lt;/strong&gt; — Turn raw notes into structured thoughts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;Here’s what’s coming to SnapMind:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 Support for more AI providers (OpenAI, Azure OpenAI, Anthropic, Google AI — with more on the way)
&lt;/li&gt;
&lt;li&gt;🖼️ Image input/output: select an image and send it to AI or ask AI created an image for you.&lt;/li&gt;
&lt;li&gt;📝 Better markdown rendering for easy copy &amp;amp; paste
&lt;/li&gt;
&lt;li&gt;🔄 Auto-updates via Electron Builder&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’d love to hear from you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What use cases do you want SnapMind to support?
&lt;/li&gt;
&lt;li&gt;Do you have cool workflows or scenarios where SnapMind could made you more productive?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Try it now at &lt;a href="https://github.com/Snap-Mind/snap-mind" rel="noopener noreferrer"&gt;Github - SnapMind&lt;/a&gt;. ⭐️ It will very be helpful if you can give the repo a star! ⭐️&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>3-Minute Guide: Make GitHub Copilot Generate High-Quality Code</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Thu, 04 Sep 2025 03:18:36 +0000</pubDate>
      <link>https://forem.com/louis7/3-minute-guide-make-github-copilot-generate-high-quality-code-2cph</link>
      <guid>https://forem.com/louis7/3-minute-guide-make-github-copilot-generate-high-quality-code-2cph</guid>
      <description>&lt;p&gt;GitHub Copilot allows you to &lt;strong&gt;customize its behavior&lt;/strong&gt; by adding Markdown files to your &lt;code&gt;.github&lt;/code&gt; folder in a Git repository. There are three types of files you can add:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Instructions&lt;/strong&gt; – guide Copilot on your coding preferences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompts&lt;/strong&gt; – reusable prompt templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat Modes&lt;/strong&gt; – customized modes for different tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpcyn0a1un2na94uyoyo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzpcyn0a1un2na94uyoyo.png" alt="Complete folder structure" width="578" height="366"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Instructions
&lt;/h2&gt;

&lt;p&gt;Instruction files let you provide Copilot with guidance and preferences.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;copilot-instructions.md&lt;/code&gt; file in the &lt;code&gt;.github&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Copilot Instructions&lt;/span&gt;

&lt;span class="gu"&gt;## General Guidelines&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Follow the instructions in the instructions folder whenever possible.
&lt;span class="p"&gt;-&lt;/span&gt; File Changes: Only edit files when you have full context. Prefer reading large sections of code before making changes.
&lt;span class="p"&gt;-&lt;/span&gt; ...

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;

...

&lt;span class="gu"&gt;## Coding Standard&lt;/span&gt;

...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file will automatically bring the instructions into your chat context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih7xnng6bq1o8w2bsh8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fih7xnng6bq1o8w2bsh8l.png" alt="Instruction File Example" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also create &lt;strong&gt;path-specific instruction files&lt;/strong&gt; (&lt;code&gt;xxx.instructions.md&lt;/code&gt;) inside &lt;code&gt;.github/instructions&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;applyTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.ts,**/*.tsx"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures instructions only apply to the files you specify.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Prompts
&lt;/h2&gt;

&lt;p&gt;Prompt files allow you to create &lt;strong&gt;reusable prompts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;xxx.prompt.md&lt;/code&gt; file in &lt;code&gt;.github/prompts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;agent'&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GPT-4o&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;githubRepo'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;codebase'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;new&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;React&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;form&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;component'&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;Your goal is to generate a new React form component based on the templates in&lt;/span&gt; &lt;span class="c1"&gt;#githubRepo contoso/react-templates.&lt;/span&gt;

&lt;span class="s"&gt;Ask for the form name and fields if not provided.&lt;/span&gt;

&lt;span class="na"&gt;Requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;* Use form design system components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;design-system/Form.md&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;&lt;span class="s"&gt;(../docs/design-system/Form.md)&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Use `react-hook-form` for form state management&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Always define TypeScript types for your form data&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Prefer *uncontrolled* components using `register`&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Use `defaultValues` to prevent unnecessary rerenders&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Use `yup` for validation&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Create reusable validation schemas in separate files&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Use TypeScript types to ensure type safety&lt;/span&gt;
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="s"&gt;Customize UX-friendly validation rules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use it, type &lt;code&gt;/&lt;/code&gt; followed by the prompt file name in the chat window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frblgcmnjhmyevfu2bg8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frblgcmnjhmyevfu2bg8b.png" alt="Prompt File Example" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Chat Modes
&lt;/h2&gt;

&lt;p&gt;Create an &lt;code&gt;xxx.chatmode.md&lt;/code&gt; file in &lt;code&gt;.github/chatmodes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate an implementation plan for new features or refactoring existing code.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;codebase'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fetch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;findTestFiles'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;githubRepo'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;search'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;usages'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Claude Sonnet &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# Planning mode instructions&lt;/span&gt;
&lt;span class="s"&gt;You are in planning mode. Your task is to generate an implementation plan for a new feature or for refactoring existing code.&lt;/span&gt;
&lt;span class="s"&gt;Do not make any code edits; just generate a plan.&lt;/span&gt;

&lt;span class="na"&gt;The plan should include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Overview&lt;/span&gt;&lt;span class="err"&gt;**&lt;/span&gt; &lt;span class="s"&gt;– brief description of the feature or task&lt;/span&gt;  
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Requirements&lt;/span&gt;&lt;span class="err"&gt;**&lt;/span&gt; &lt;span class="s"&gt;– list of requirements&lt;/span&gt;  
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Implementation&lt;/span&gt; &lt;span class="s"&gt;Steps** – detailed step-by-step plan&lt;/span&gt;  
&lt;span class="err"&gt;*&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;*Testing&lt;/span&gt;&lt;span class="err"&gt;**&lt;/span&gt; &lt;span class="s"&gt;– tests to verify the implementation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can select your custom chat mode in the chat window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2efiusptqiwlvvgqw7ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2efiusptqiwlvvgqw7ot.png" alt="Chat Mode Example" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You don’t need to create these files from scratch. &lt;a href="https://github.com/github/awesome-copilot/tree/main" rel="noopener noreferrer"&gt;Awesome Copilot&lt;/a&gt; provides many templates—you just need to copy them into your project as needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/customization/custom-instructions?originUrl=%2Fdocs%2Fcopilot%2Fcustomization%2Fcustom-chat-modes#_use-a-githubcopilotinstructionsmd-file" rel="noopener noreferrer"&gt;Use custom instructions in VS Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/customization/prompt-files?originUrl=%2Fdocs%2Fcopilot%2Fcustomization%2Fcustom-instructions#_create-a-prompt-file" rel="noopener noreferrer"&gt;Use prompt files in VS Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/customization/custom-chat-modes?originUrl=%2Fdocs%2Fcopilot%2Fcustomization%2Fprompt-files#_custom-chat-modes" rel="noopener noreferrer"&gt;Use chat modes in VS Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vibecoding</category>
      <category>githubcopilot</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Hello Monday</title>
      <dc:creator>Louis Liu</dc:creator>
      <pubDate>Mon, 28 Jul 2025 01:59:55 +0000</pubDate>
      <link>https://forem.com/louis7/hello-monday-88l</link>
      <guid>https://forem.com/louis7/hello-monday-88l</guid>
      <description>&lt;p&gt;Another tough week, but seeing the high-five Octocat at the bottom of the pull request gave me a bit of relief.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5musdxrl3zvno7tkihgb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5musdxrl3zvno7tkihgb.gif" width="256" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope y’all have a smooth week—no bugs, no blockers!&lt;/p&gt;

</description>
      <category>water</category>
      <category>discuss</category>
      <category>github</category>
    </item>
  </channel>
</rss>
