<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Random writings on Định Nguyễn</title>
    <link>https://dinhnn.com/posts/</link>
    <description>Recent content in Random writings on Định Nguyễn</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 14 Apr 2026 14:00:00 +0700</lastBuildDate>
    <atom:link href="https://dinhnn.com/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>ClauBlink — A Physical LED That Blinks When Claude Is Thinking</title>
      <link>https://dinhnn.com/posts/claublink-led-indicator/</link>
      <pubDate>Tue, 14 Apr 2026 14:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/claublink-led-indicator/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I built a physical LED indicator that shows what Claude Code is doing in real-time. Fast blink means it&amp;rsquo;s streaming a response. Slow blink means it&amp;rsquo;s thinking. Solid green means it&amp;rsquo;s done. Works over USB serial and WiFi, supports two boards (ESP32-C3 and RP2040), and hooks into Claude Code&amp;rsquo;s event system with a 56-line shell script.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/dinhnguyen/ClauBlink&#34;&gt;github.com/dinhnguyen/ClauBlink&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;why&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#why&#34;&gt;Why&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I run Claude Code sessions that sometimes take a while. At home, I don&amp;rsquo;t want to sit there watching the terminal. I want to step away, play with my kid, do something else. But checking my phone for notifications isn&amp;rsquo;t great either, because if I&amp;rsquo;m playing with my son, I&amp;rsquo;m playing with my son, not staring at a screen.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Roll the Dice — When Your Kid Needs a Dice and You Have a Terminal</title>
      <link>https://dinhnn.com/posts/roll-the-dice/</link>
      <pubDate>Sun, 12 Apr 2026 17:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/roll-the-dice/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;My son wanted to play a board game. We only had one physical dice. Instead of going to find more, I opened Claude Code and built a web dice roller with 3D animations, 10 random dramatic soundtracks, and a competitive scoring mode. The whole thing took about 30 minutes.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href=&#34;https://dice.dinhnn.com&#34;&gt;dice.dinhnn.com&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Fun fact: while writing this post I learned that the correct singular form of &amp;ldquo;dice&amp;rdquo; is actually &amp;ldquo;die.&amp;rdquo; But this is a fun post about rolling dice with my kid, not a grammar exam. So &amp;ldquo;one dice&amp;rdquo; it is.&lt;/em&gt; 🎲&lt;/p&gt;</description>
    </item>
    <item>
      <title>Jira Report — A Tiny Tool for People Who Don&#39;t Want to Learn JQL</title>
      <link>https://dinhnn.com/posts/building-jira-report/</link>
      <pubDate>Sat, 11 Apr 2026 12:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/building-jira-report/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I built a small Jira report tool that answers one question: &lt;strong&gt;what did the team work on in a given period?&lt;/strong&gt; You pick boards, pick dates, get a table. Nothing more.&lt;/p&gt;&#xA;&lt;p&gt;JQL can already do this. But JQL has a learning curve, and most people on a team don&amp;rsquo;t have the time or interest to write queries. So I built a form.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href=&#34;https://jira-report.dinhnn.com/&#34;&gt;jira-report.dinhnn.com&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;the-problem&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#the-problem&#34;&gt;The Problem&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Every sprint review, every weekly sync, every 1:1 — someone asks the same question.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Building py-player — A Media Player Nobody Asked For</title>
      <link>https://dinhnn.com/posts/building-py-player/</link>
      <pubDate>Fri, 10 Apr 2026 00:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/building-py-player/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I built a dependency-free Web Component media player with 7 layout presets, 7 themes, audio visualization, and i18n. Nobody asked for it. I&amp;rsquo;m not sure anyone needs it. But I built it anyway, with Claude Code doing most of the heavy lifting.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Docs &amp;amp; Playground:&lt;/strong&gt; &lt;a href=&#34;https://py.dinhnn.com&#34;&gt;py.dinhnn.com&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href=&#34;https://github.com/dinhnguyen/py-player&#34;&gt;github.com/dinhnguyen/py-player&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;npm:&lt;/strong&gt; &lt;code&gt;npm install py-player&lt;/code&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;an-idea-that-sat-around-for-years&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#an-idea-that-sat-around-for-years&#34;&gt;An Idea That Sat Around for Years&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve wanted to build a custom media player for a long time. Not because the world needs another one — HTML5 &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; have gotten really good. The native controls work fine for most people. The ecosystem of existing players is mature. There&amp;rsquo;s howler.js, plyr, vidstack, and dozens more.&lt;/p&gt;</description>
    </item>
    <item>
      <title>When a Bad Custom Module Breaks Odoo &#39;s Attendance — And You Have to Fix It in Yours</title>
      <link>https://dinhnn.com/posts/odoo-attendance-overtime-problem/</link>
      <pubDate>Thu, 09 Apr 2026 00:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/odoo-attendance-overtime-problem/</guid>
      <description>&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is a personal log of what I ran into while vibe-coding an Odoo integration with Claude. Some of the approaches and assumptions here may not be correct if you have more professional Odoo experience than I do. Take it as a war story, not a best practice guide.&lt;/p&gt;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Odoo 18&amp;rsquo;s core &lt;code&gt;hr_attendance&lt;/code&gt; module works fine. The overtime computation, the unique constraint, the &lt;code&gt;_update_overtime()&lt;/code&gt; logic — all correct. The problem? A third-party custom module that overrides &lt;code&gt;create()&lt;/code&gt; and &lt;code&gt;write()&lt;/code&gt; on &lt;code&gt;hr.attendance&lt;/code&gt; &lt;strong&gt;without understanding the side effects&lt;/strong&gt;. It triggers &lt;code&gt;_update_overtime()&lt;/code&gt; multiple times for the same day, causing a duplicate key violation. Your attendance record gets rolled back. The employee&amp;rsquo;s scan vanishes.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Building QR Forest — Hiding QR Codes Inside 3D Pixel Art Forests</title>
      <link>https://dinhnn.com/posts/building-qr-forest/</link>
      <pubDate>Mon, 06 Apr 2026 00:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/building-qr-forest/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;I built a web app that generates &lt;strong&gt;scannable QR codes disguised as pixel art forests&lt;/strong&gt;. The trick: looking straight down through the tree canopy, the gaps between leaves form a valid QR pattern. Switch to 3D and it becomes a tiny voxel garden with trees, flowers, and seasonal particles. Built with Next.js 16, Three.js (React Three Fiber), and a lot of BoxGeometry.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href=&#34;https://qr-forest.dinhnn.com&#34;&gt;qr-forest.dinhnn.com&lt;/a&gt;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;the-idea&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#the-idea&#34;&gt;The Idea&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;It started with a 3D render I saw on Threads — someone had made a scene where you could switch the camera from a flat QR code view to a beautiful 3D garden. The transition was mesmerizing. I wanted to build that.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Cloudflare Workers Subrequest Limit — Post-mortem</title>
      <link>https://dinhnn.com/posts/cloudflare-workers-subrequest-limit-postmortem/</link>
      <pubDate>Fri, 03 Apr 2026 00:00:00 +0700</pubDate>
      <guid>https://dinhnn.com/posts/cloudflare-workers-subrequest-limit-postmortem/</guid>
      <description>&lt;h2 id=&#34;tldr&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#tldr&#34;&gt;TL;DR&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Deploying a Jira time-report API on Cloudflare Pages (Workers edge runtime) caused &lt;strong&gt;silent data loss&lt;/strong&gt; for 25 out of 57 issues. The root cause was Cloudflare Workers&amp;rsquo; &lt;strong&gt;50-subrequest-per-invocation limit&lt;/strong&gt; on the free tier. Fixed by embedding worklogs in the JQL search response instead of making separate API calls per issue.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;the-problem&#34; class=&#34;anchor-link&#34;&gt;&lt;a href=&#34;#the-problem&#34;&gt;The Problem&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;When selecting all 9 Jira boards on the time report page, team member &amp;ldquo;Ngọc Định Nguyễn&amp;rdquo; showed &lt;strong&gt;8h&lt;/strong&gt; instead of the correct &lt;strong&gt;30.5h&lt;/strong&gt;. Selecting only boards 36+37 showed the correct value.&lt;/p&gt;</description>
    </item>
    <item>
      <title>What?</title>
      <link>https://dinhnn.com/posts/what-is-it/</link>
      <pubDate>Fri, 13 Oct 2023 12:21:57 -0500</pubDate>
      <guid>https://dinhnn.com/posts/what-is-it/</guid>
      <description>&lt;p&gt;What :)&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
